summaryrefslogtreecommitdiff
path: root/gas/config
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/aout_gnu.h450
-rw-r--r--gas/config/atof-ieee.c812
-rw-r--r--gas/config/atof-vax.c450
-rw-r--r--gas/config/bfin-aux.h68
-rw-r--r--gas/config/bfin-defs.h397
-rw-r--r--gas/config/bfin-lex-wrapper.c25
-rw-r--r--gas/config/bfin-lex.l556
-rw-r--r--gas/config/bfin-parse.y4671
-rw-r--r--gas/config/e-crisaout.c38
-rw-r--r--gas/config/e-criself.c38
-rw-r--r--gas/config/e-i386aout.c38
-rw-r--r--gas/config/e-i386coff.c38
-rw-r--r--gas/config/e-i386elf.c38
-rw-r--r--gas/config/e-mipself.c56
-rw-r--r--gas/config/itbl-mips.h46
-rw-r--r--gas/config/m68k-parse.h358
-rw-r--r--gas/config/m68k-parse.y1118
-rw-r--r--gas/config/obj-aout.c342
-rw-r--r--gas/config/obj-aout.h70
-rw-r--r--gas/config/obj-coff-seh.c1024
-rw-r--r--gas/config/obj-coff-seh.h206
-rw-r--r--gas/config/obj-coff.c1955
-rw-r--r--gas/config/obj-coff.h408
-rw-r--r--gas/config/obj-ecoff.c319
-rw-r--r--gas/config/obj-ecoff.h77
-rw-r--r--gas/config/obj-elf.c2683
-rw-r--r--gas/config/obj-elf.h252
-rw-r--r--gas/config/obj-evax.c524
-rw-r--r--gas/config/obj-evax.h111
-rw-r--r--gas/config/obj-fdpicelf.c20
-rw-r--r--gas/config/obj-fdpicelf.h21
-rw-r--r--gas/config/obj-macho.c1994
-rw-r--r--gas/config/obj-macho.h119
-rw-r--r--gas/config/obj-multi.c23
-rw-r--r--gas/config/obj-multi.h173
-rw-r--r--gas/config/obj-som.c327
-rw-r--r--gas/config/obj-som.h74
-rw-r--r--gas/config/rl78-defs.h52
-rw-r--r--gas/config/rl78-parse.y1598
-rw-r--r--gas/config/rx-defs.h69
-rw-r--r--gas/config/rx-parse.y1645
-rw-r--r--gas/config/tc-aarch64.c7631
-rw-r--r--gas/config/tc-aarch64.h235
-rw-r--r--gas/config/tc-alpha.c6369
-rw-r--r--gas/config/tc-alpha.h186
-rw-r--r--gas/config/tc-arc.c1897
-rw-r--r--gas/config/tc-arc.h74
-rw-r--r--gas/config/tc-arm.c25602
-rw-r--r--gas/config/tc-arm.h379
-rw-r--r--gas/config/tc-avr.c1850
-rw-r--r--gas/config/tc-avr.h215
-rw-r--r--gas/config/tc-bfin.c2733
-rw-r--r--gas/config/tc-bfin.h84
-rw-r--r--gas/config/tc-cr16.c2569
-rw-r--r--gas/config/tc-cr16.h76
-rw-r--r--gas/config/tc-cris.c4427
-rw-r--r--gas/config/tc-cris.h170
-rw-r--r--gas/config/tc-crx.c2010
-rw-r--r--gas/config/tc-crx.h79
-rw-r--r--gas/config/tc-d10v.c1819
-rw-r--r--gas/config/tc-d10v.h63
-rw-r--r--gas/config/tc-d30v.c2126
-rw-r--r--gas/config/tc-d30v.h64
-rw-r--r--gas/config/tc-dlx.c1235
-rw-r--r--gas/config/tc-dlx.h66
-rw-r--r--gas/config/tc-epiphany.c1111
-rw-r--r--gas/config/tc-epiphany.h102
-rw-r--r--gas/config/tc-fr30.c419
-rw-r--r--gas/config/tc-fr30.h65
-rw-r--r--gas/config/tc-frv.c1833
-rw-r--r--gas/config/tc-frv.h125
-rw-r--r--gas/config/tc-generic.c22
-rw-r--r--gas/config/tc-generic.h35
-rw-r--r--gas/config/tc-h8300.c2258
-rw-r--r--gas/config/tc-h8300.h90
-rw-r--r--gas/config/tc-hppa.c8798
-rw-r--r--gas/config/tc-hppa.h240
-rw-r--r--gas/config/tc-i370.c2666
-rw-r--r--gas/config/tc-i370.h63
-rw-r--r--gas/config/tc-i386-intel.c1002
-rw-r--r--gas/config/tc-i386.c10676
-rw-r--r--gas/config/tc-i386.h341
-rw-r--r--gas/config/tc-i860.c1491
-rw-r--r--gas/config/tc-i860.h95
-rw-r--r--gas/config/tc-i960.c2666
-rw-r--r--gas/config/tc-i960.h186
-rw-r--r--gas/config/tc-ia64.c12070
-rw-r--r--gas/config/tc-ia64.h331
-rw-r--r--gas/config/tc-ip2k.c426
-rw-r--r--gas/config/tc-ip2k.h65
-rw-r--r--gas/config/tc-iq2000.c987
-rw-r--r--gas/config/tc-iq2000.h65
-rw-r--r--gas/config/tc-lm32.c421
-rw-r--r--gas/config/tc-lm32.h50
-rw-r--r--gas/config/tc-m32c.c1294
-rw-r--r--gas/config/tc-m32c.h87
-rw-r--r--gas/config/tc-m32r.c2409
-rw-r--r--gas/config/tc-m32r.h125
-rw-r--r--gas/config/tc-m68851.h276
-rw-r--r--gas/config/tc-m68hc11.c4499
-rw-r--r--gas/config/tc-m68hc11.h108
-rw-r--r--gas/config/tc-m68k.c8144
-rw-r--r--gas/config/tc-m68k.h194
-rw-r--r--gas/config/tc-mcore.c2235
-rw-r--r--gas/config/tc-mcore.h95
-rw-r--r--gas/config/tc-mep.c2202
-rw-r--r--gas/config/tc-mep.h119
-rw-r--r--gas/config/tc-metag.c7141
-rw-r--r--gas/config/tc-metag.h72
-rw-r--r--gas/config/tc-microblaze.c2524
-rw-r--r--gas/config/tc-microblaze.h120
-rw-r--r--gas/config/tc-mips.c19112
-rw-r--r--gas/config/tc-mips.h200
-rw-r--r--gas/config/tc-mmix.c4332
-rw-r--r--gas/config/tc-mmix.h240
-rw-r--r--gas/config/tc-mn10200.c1340
-rw-r--r--gas/config/tc-mn10200.h46
-rw-r--r--gas/config/tc-mn10300.c2639
-rw-r--r--gas/config/tc-mn10300.h126
-rw-r--r--gas/config/tc-moxie.c844
-rw-r--r--gas/config/tc-moxie.h47
-rw-r--r--gas/config/tc-msp430.c3936
-rw-r--r--gas/config/tc-msp430.h172
-rw-r--r--gas/config/tc-mt.c483
-rw-r--r--gas/config/tc-mt.h70
-rw-r--r--gas/config/tc-nds32.c6600
-rw-r--r--gas/config/tc-nds32.h281
-rw-r--r--gas/config/tc-nios2.c3105
-rw-r--r--gas/config/tc-nios2.h121
-rw-r--r--gas/config/tc-ns32k.c2253
-rw-r--r--gas/config/tc-ns32k.h123
-rw-r--r--gas/config/tc-or1k.c362
-rw-r--r--gas/config/tc-or1k.h79
-rw-r--r--gas/config/tc-pdp11.c1453
-rw-r--r--gas/config/tc-pdp11.h33
-rw-r--r--gas/config/tc-pj.c495
-rw-r--r--gas/config/tc-pj.h61
-rw-r--r--gas/config/tc-ppc.c7193
-rw-r--r--gas/config/tc-ppc.h290
-rw-r--r--gas/config/tc-rl78.c1411
-rw-r--r--gas/config/tc-rl78.h84
-rw-r--r--gas/config/tc-rx.c2688
-rw-r--r--gas/config/tc-rx.h105
-rw-r--r--gas/config/tc-s390.c2518
-rw-r--r--gas/config/tc-s390.h100
-rw-r--r--gas/config/tc-score.c7836
-rw-r--r--gas/config/tc-score.h78
-rw-r--r--gas/config/tc-score7.c6972
-rw-r--r--gas/config/tc-sh.c4641
-rw-r--r--gas/config/tc-sh.h259
-rw-r--r--gas/config/tc-sh64.c3527
-rw-r--r--gas/config/tc-sh64.h227
-rw-r--r--gas/config/tc-sparc.c4871
-rw-r--r--gas/config/tc-sparc.h204
-rw-r--r--gas/config/tc-spu.c1100
-rw-r--r--gas/config/tc-spu.h109
-rw-r--r--gas/config/tc-tic30.c2002
-rw-r--r--gas/config/tc-tic30.h51
-rw-r--r--gas/config/tc-tic4x.c3037
-rw-r--r--gas/config/tc-tic4x.h93
-rw-r--r--gas/config/tc-tic54x.c5408
-rw-r--r--gas/config/tc-tic54x.h128
-rw-r--r--gas/config/tc-tic6x.c5377
-rw-r--r--gas/config/tc-tic6x.h227
-rw-r--r--gas/config/tc-tilegx.c1895
-rw-r--r--gas/config/tc-tilegx.h93
-rw-r--r--gas/config/tc-tilepro.c1677
-rw-r--r--gas/config/tc-tilepro.h93
-rw-r--r--gas/config/tc-v850.c3718
-rw-r--r--gas/config/tc-v850.h86
-rw-r--r--gas/config/tc-vax.c3410
-rw-r--r--gas/config/tc-vax.h79
-rw-r--r--gas/config/tc-xc16x.c349
-rw-r--r--gas/config/tc-xc16x.h60
-rw-r--r--gas/config/tc-xgate.c1352
-rw-r--r--gas/config/tc-xgate.h115
-rw-r--r--gas/config/tc-xstormy16.c600
-rw-r--r--gas/config/tc-xstormy16.h69
-rw-r--r--gas/config/tc-xtensa.c12603
-rw-r--r--gas/config/tc-xtensa.h465
-rw-r--r--gas/config/tc-z80.c2064
-rw-r--r--gas/config/tc-z80.h109
-rw-r--r--gas/config/tc-z8k.c1565
-rw-r--r--gas/config/tc-z8k.h39
-rw-r--r--gas/config/te-386bsd.h32
-rw-r--r--gas/config/te-aix5.h22
-rw-r--r--gas/config/te-armeabi.h27
-rw-r--r--gas/config/te-armfbsdeabi.h22
-rw-r--r--gas/config/te-armfbsdvfp.h22
-rw-r--r--gas/config/te-armlinuxeabi.h24
-rw-r--r--gas/config/te-dragonfly.h30
-rw-r--r--gas/config/te-dynix.h26
-rw-r--r--gas/config/te-epoc-pe.h27
-rw-r--r--gas/config/te-freebsd.h30
-rw-r--r--gas/config/te-generic.h38
-rw-r--r--gas/config/te-gnu.h23
-rw-r--r--gas/config/te-go32.h31
-rw-r--r--gas/config/te-hppa.h28
-rw-r--r--gas/config/te-hppa64.h25
-rw-r--r--gas/config/te-hppalinux64.h24
-rw-r--r--gas/config/te-hpux.h23
-rw-r--r--gas/config/te-i386aix.h38
-rw-r--r--gas/config/te-ia64aix.h23
-rw-r--r--gas/config/te-interix.h35
-rw-r--r--gas/config/te-irix.h31
-rw-r--r--gas/config/te-linux.h23
-rw-r--r--gas/config/te-lynx.h26
-rw-r--r--gas/config/te-mach.h21
-rw-r--r--gas/config/te-macos.h28
-rw-r--r--gas/config/te-nacl.h30
-rw-r--r--gas/config/te-nbsd.h23
-rw-r--r--gas/config/te-nbsd532.h32
-rw-r--r--gas/config/te-netware.h28
-rw-r--r--gas/config/te-pc532mach.h32
-rw-r--r--gas/config/te-pe.h26
-rw-r--r--gas/config/te-pep.h29
-rw-r--r--gas/config/te-psos.h35
-rw-r--r--gas/config/te-riscix.h25
-rw-r--r--gas/config/te-solaris.h40
-rw-r--r--gas/config/te-sparcaout.h22
-rw-r--r--gas/config/te-sun3.h48
-rw-r--r--gas/config/te-svr4.h23
-rw-r--r--gas/config/te-symbian.h22
-rw-r--r--gas/config/te-tmips.h40
-rw-r--r--gas/config/te-uclinux.h22
-rw-r--r--gas/config/te-vms.c347
-rw-r--r--gas/config/te-vms.h41
-rw-r--r--gas/config/te-vxworks.h30
-rw-r--r--gas/config/te-wince-pe.h21
-rw-r--r--gas/config/vax-inst.h79
-rw-r--r--gas/config/xtensa-istack.h105
-rw-r--r--gas/config/xtensa-relax.c1924
-rw-r--r--gas/config/xtensa-relax.h189
233 files changed, 309007 insertions, 0 deletions
diff --git a/gas/config/aout_gnu.h b/gas/config/aout_gnu.h
new file mode 100644
index 0000000..35ab91b
--- /dev/null
+++ b/gas/config/aout_gnu.h
@@ -0,0 +1,450 @@
+/* This file is aout_gnu.h
+
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef __A_OUT_GNU_H__
+#define __A_OUT_GNU_H__
+
+/* There are two main flavours of a.out, one which uses the standard
+ relocations, and one which uses extended relocations.
+
+ Today, the extended reloc uses are
+ TC_SPARC
+
+ each must define the enum reloc_type
+
+*/
+
+#define USE_EXTENDED_RELOC defined(TC_SPARC)
+
+#if defined(TC_SPARC)
+enum reloc_type
+ {
+ RELOC_8, RELOC_16, RELOC_32,/* simple relocations */
+ RELOC_DISP8, RELOC_DISP16, RELOC_DISP32, /* pc-rel displacement */
+ RELOC_WDISP30, RELOC_WDISP22,
+ RELOC_HI22, RELOC_22,
+ RELOC_13, RELOC_LO10,
+ RELOC_SFA_BASE, RELOC_SFA_OFF13,
+ RELOC_BASE10, RELOC_BASE13, RELOC_BASE22, /* P.I.C. (base-relative) */
+ RELOC_PC10, RELOC_PC22, /* for some sort of pc-rel P.I.C. (?) */
+ RELOC_JMP_TBL, /* P.I.C. jump table */
+ RELOC_SEGOFF16, /* reputedly for shared libraries somehow */
+ RELOC_GLOB_DAT, RELOC_JMP_SLOT, RELOC_RELATIVE,
+ RELOC_10, RELOC_11,
+ RELOC_WDISP2_14,
+ RELOC_WDISP19,
+ RELOC_HHI22,
+ RELOC_HLO10,
+
+ /* 29K relocation types */
+ RELOC_JUMPTARG, RELOC_CONST, RELOC_CONSTH,
+
+ RELOC_WDISP14, RELOC_WDISP21,
+
+ NO_RELOC
+ };
+
+#endif /* TC_SPARC */
+
+#define __GNU_EXEC_MACROS__
+
+#ifndef __STRUCT_EXEC_OVERRIDE__
+
+/* This is the layout on disk of a Unix V7, Berkeley, SunOS, Vax Ultrix
+ "struct exec". Don't assume that on this machine, the "struct exec"
+ will lay out the same sizes or alignments. */
+
+struct exec_bytes
+ {
+ unsigned char a_info[4];
+ unsigned char a_text[4];
+ unsigned char a_data[4];
+ unsigned char a_bss[4];
+ unsigned char a_syms[4];
+ unsigned char a_entry[4];
+ unsigned char a_trsize[4];
+ unsigned char a_drsize[4];
+ };
+
+/* How big the "struct exec" is on disk */
+#define EXEC_BYTES_SIZE (8 * 4)
+
+/* This is the layout in memory of a "struct exec" while we process it. */
+
+struct exec
+{
+ unsigned long a_info; /* Use macros N_MAGIC, etc for access */
+ unsigned a_text; /* length of text, in bytes */
+ unsigned a_data; /* length of data, in bytes */
+ unsigned a_bss; /* length of uninitialized data area for file, in bytes */
+ unsigned a_syms; /* length of symbol table data in file, in bytes */
+ unsigned a_entry; /* start address */
+ unsigned a_trsize; /* length of relocation info for text, in bytes */
+ unsigned a_drsize; /* length of relocation info for data, in bytes */
+};
+
+#endif /* __STRUCT_EXEC_OVERRIDE__ */
+
+/* these go in the N_MACHTYPE field */
+/* These symbols could be defined by code from Suns...punt 'em */
+#undef M_UNKNOWN
+#undef M_68010
+#undef M_68020
+#undef M_SPARC
+enum machine_type
+ {
+ M_UNKNOWN = 0,
+ M_68010 = 1,
+ M_68020 = 2,
+ M_SPARC = 3,
+ /* skip a bunch so we don't run into any of sun's numbers */
+ M_386 = 100,
+ M_29K = 101,
+ M_RS6000 = 102, /* IBM RS/6000 */
+ M_VAX4K_NETBSD = 150,
+ /* HP/BSD formats */
+ M_HP200 = 200, /* hp200 (68010) BSD binary */
+ M_HP300 = 300, /* hp300 (68020+68881) BSD binary */
+ M_HPUX23 = 0x020C /* hp200/300 HPUX binary */
+ };
+
+#define N_MAGIC(exec) ((exec).a_info & 0xffff)
+#define N_MACHTYPE(exec) ((enum machine_type)(((exec).a_info >> 16) & 0xff))
+#define N_FLAGS(exec) (((exec).a_info >> 24) & 0xff)
+#define N_SET_INFO(exec, magic, type, flags) \
+ ((exec).a_info = ((magic) & 0xffff) \
+ | (((int)(type) & 0xff) << 16) \
+ | (((flags) & 0xff) << 24))
+#define N_SET_MAGIC(exec, magic) \
+ ((exec).a_info = (((exec).a_info & 0xffff0000) | ((magic) & 0xffff)))
+
+#define N_SET_MACHTYPE(exec, machtype) \
+ ((exec).a_info = \
+ ((exec).a_info&0xff00ffff) | ((((int)(machtype))&0xff) << 16))
+
+#define N_SET_FLAGS(exec, flags) \
+ ((exec).a_info = \
+ ((exec).a_info&0x00ffffff) | (((flags) & 0xff) << 24))
+
+/* Code indicating object file or impure executable. */
+#ifndef OMAGIC
+#define OMAGIC 0407
+#endif
+/* Code indicating pure executable. */
+#define NMAGIC 0410
+/* Code indicating demand-paged executable. */
+#define ZMAGIC 0413
+
+/* Virtual Address of text segment from the a.out file. For OMAGIC,
+ (almost always "unlinked .o's" these days), should be zero.
+ For linked files, should reflect reality if we know it. */
+
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) (N_MAGIC(x)==OMAGIC? 0 : TEXT_START_ADDR)
+#endif
+
+#ifndef N_BADMAG
+#define N_BADMAG(x) (N_MAGIC(x) != OMAGIC \
+ && N_MAGIC(x) != NMAGIC \
+ && N_MAGIC(x) != ZMAGIC)
+#endif
+
+/* By default, segment size is constant. But on some machines, it can
+ be a function of the a.out header (e.g. machine type). */
+#ifndef N_SEGSIZE
+#define N_SEGSIZE(x) SEGMENT_SIZE
+#endif
+
+/* This complexity is for encapsulated COFF support */
+#ifndef _N_HDROFF
+#define _N_HDROFF(x) (N_SEGSIZE(x) - sizeof (struct exec))
+#endif
+
+#ifndef N_TXTOFF
+#define N_TXTOFF(x) (N_MAGIC(x) == ZMAGIC ? \
+ _N_HDROFF((x)) + sizeof (struct exec) : \
+ sizeof (struct exec))
+#endif
+
+#ifndef N_DATOFF
+#define N_DATOFF(x) ( N_TXTOFF(x) + (x).a_text )
+#endif
+
+#ifndef N_TRELOFF
+#define N_TRELOFF(x) ( N_DATOFF(x) + (x).a_data )
+#endif
+
+#ifndef N_DRELOFF
+#define N_DRELOFF(x) ( N_TRELOFF(x) + (x).a_trsize )
+#endif
+
+#ifndef N_SYMOFF
+#define N_SYMOFF(x) ( N_DRELOFF(x) + (x).a_drsize )
+#endif
+
+#ifndef N_STROFF
+#define N_STROFF(x) ( N_SYMOFF(x) + (x).a_syms )
+#endif
+
+/* Address of text segment in memory after it is loaded. */
+#ifndef N_TXTADDR
+#define N_TXTADDR(x) 0
+#endif
+
+#ifndef N_DATADDR
+#define N_DATADDR(x) \
+ (N_MAGIC(x)==OMAGIC? (N_TXTADDR(x)+(x).a_text) \
+ : (N_SEGSIZE(x) + ((N_TXTADDR(x)+(x).a_text-1) & ~(N_SEGSIZE(x)-1))))
+#endif
+
+/* Address of bss segment in memory after it is loaded. */
+#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data)
+
+struct nlist
+ {
+ union
+ {
+ char *n_name;
+ struct nlist *n_next;
+ long n_strx;
+ }
+ n_un;
+ unsigned char n_type;
+ char n_other;
+ short n_desc;
+ unsigned long n_value;
+ };
+
+#define N_UNDF 0
+#define N_ABS 2
+#define N_TEXT 4
+#define N_DATA 6
+#define N_BSS 8
+#define N_COMM 0x12 /* common (visible in shared lib commons) */
+#define N_FN 0x1F /* File name of a .o file */
+
+/* Note: N_EXT can only usefully be OR-ed with N_UNDF, N_ABS, N_TEXT,
+ N_DATA, or N_BSS. When the low-order bit of other types is set,
+ (e.g. N_WARNING versus N_FN), they are two different types. */
+#define N_EXT 1
+#define N_TYPE 036
+#define N_STAB 0340
+
+/* The following type indicates the definition of a symbol as being
+ an indirect reference to another symbol. The other symbol
+ appears as an undefined reference, immediately following this symbol.
+
+ Indirection is asymmetrical. The other symbol's value will be used
+ to satisfy requests for the indirect symbol, but not vice versa.
+ If the other symbol does not have a definition, libraries will
+ be searched to find a definition. */
+
+#define N_INDR 0xa
+
+/* The following symbols refer to set elements.
+ All the N_SET[ATDB] symbols with the same name form one set.
+ Space is allocated for the set in the text section, and each set
+ element's value is stored into one word of the space.
+ The first word of the space is the length of the set (number of elements).
+
+ The address of the set is made into an N_SETV symbol
+ whose name is the same as the name of the set.
+ This symbol acts like a N_DATA global symbol
+ in that it can satisfy undefined external references. */
+
+/* These appear as input to LD, in a .o file. */
+#define N_SETA 0x14 /* Absolute set element symbol */
+#define N_SETT 0x16 /* Text set element symbol */
+#define N_SETD 0x18 /* Data set element symbol */
+#define N_SETB 0x1A /* Bss set element symbol */
+
+/* This is output from LD. */
+#define N_SETV 0x1C /* Pointer to set vector in data area. */
+
+/* Warning symbol. The text gives a warning message, the next symbol
+ in the table will be undefined. When the symbol is referenced, the
+ message is printed. */
+
+#define N_WARNING 0x1e
+
+/* Weak symbols. These are a GNU extension to the a.out format. The
+ semantics are those of ELF weak symbols. Weak symbols are always
+ externally visible. The N_WEAK? values are squeezed into the
+ available slots. The value of a N_WEAKU symbol is 0. The values
+ of the other types are the definitions. */
+#define N_WEAKU 0x0d /* Weak undefined symbol. */
+#define N_WEAKA 0x0e /* Weak absolute symbol. */
+#define N_WEAKT 0x0f /* Weak text symbol. */
+#define N_WEAKD 0x10 /* Weak data symbol. */
+#define N_WEAKB 0x11 /* Weak bss symbol. */
+
+/* This structure describes a single relocation to be performed.
+ The text-relocation section of the file is a vector of these structures,
+ all of which apply to the text section.
+ Likewise, the data-relocation section applies to the data section. */
+
+/* The following enum and struct were borrowed from SunOS's
+ /usr/include/sun4/a.out.h and extended to handle
+ other machines. It is currently used on SPARC and AMD 29000.
+
+ reloc_ext_bytes is how it looks on disk. reloc_info_extended is
+ how we might process it on a native host. */
+#if USE_EXTENDED_RELOC
+
+struct reloc_ext_bytes
+ {
+ unsigned char r_address[4];
+ unsigned char r_index[3];
+ unsigned char r_bits[1];
+ unsigned char r_addend[4];
+ };
+
+#define RELOC_EXT_BITS_EXTERN_BIG 0x80
+#define RELOC_EXT_BITS_EXTERN_LITTLE 0x01
+
+#define RELOC_EXT_BITS_TYPE_BIG 0x1F
+#define RELOC_EXT_BITS_TYPE_SH_BIG 0
+#define RELOC_EXT_BITS_TYPE_LITTLE 0xF8
+#define RELOC_EXT_BITS_TYPE_SH_LITTLE 3
+
+#define RELOC_EXT_SIZE 12 /* Bytes per relocation entry */
+
+struct reloc_info_extended
+{
+ unsigned long r_address;
+ unsigned int r_index:24;
+# define r_symbolnum r_index
+ unsigned r_extern:1;
+ unsigned:2;
+ /* RS/6000 compiler does not support enum bitfield
+ enum reloc_type r_type:5; */
+ enum reloc_type r_type;
+ long int r_addend;
+};
+
+#else
+
+/* The standard, old-fashioned, Berkeley compatible relocation struct */
+
+#ifdef TC_I860
+/* NOTE: three bits max, see struct reloc_info_i860.r_type */
+enum i860_reloc_type
+ {
+ NO_RELOC = 0, BRADDR, LOW0, LOW1, LOW2, LOW3, LOW4, SPLIT0, SPLIT1, SPLIT2, RELOC_32,
+ };
+
+typedef enum i860_reloc_type reloc_type;
+
+/* NOTE: two bits max, see reloc_info_i860.r_type */
+enum highlow_type
+ {
+ NO_SPEC = 0, PAIR, HIGH, HIGHADJ,
+ };
+
+struct reloc_info_i860
+{
+ unsigned long r_address;
+ /*
+ * Using bit fields here is a bad idea because the order is not portable. :-(
+ */
+ unsigned int r_symbolnum:24;
+ unsigned int r_pcrel:1;
+ unsigned int r_extern:1;
+ /* combining the two field simplifies the argument passing in "new_fix()" */
+ /* and is compatible with the existing Sparc #ifdef's */
+ /* r_type: highlow_type - bits 5,4; reloc_type - bits 3-0 */
+ unsigned int r_type:6;
+ long r_addend;
+};
+
+#endif /* TC_I860 */
+
+struct reloc_std_bytes
+ {
+ unsigned char r_address[4];
+ unsigned char r_index[3];
+ unsigned char r_bits[1];
+ };
+
+#define RELOC_STD_BITS_PCREL_BIG 0x80
+#define RELOC_STD_BITS_PCREL_LITTLE 0x01
+
+#define RELOC_STD_BITS_LENGTH_BIG 0x60
+#define RELOC_STD_BITS_LENGTH_SH_BIG 5 /* To shift to units place */
+#define RELOC_STD_BITS_LENGTH_LITTLE 0x06
+#define RELOC_STD_BITS_LENGTH_SH_LITTLE 1
+
+#define RELOC_STD_BITS_EXTERN_BIG 0x10
+#define RELOC_STD_BITS_EXTERN_LITTLE 0x08
+
+#define RELOC_STD_BITS_BASEREL_BIG 0x08
+#define RELOC_STD_BITS_BASEREL_LITTLE 0x08
+
+#define RELOC_STD_BITS_JMPTABLE_BIG 0x04
+#define RELOC_STD_BITS_JMPTABLE_LITTLE 0x04
+
+#define RELOC_STD_BITS_RELATIVE_BIG 0x02
+#define RELOC_STD_BITS_RELATIVE_LITTLE 0x02
+
+#define RELOC_STD_SIZE 8 /* Bytes per relocation entry */
+
+#endif /* USE_EXTENDED_RELOC */
+
+#ifndef CUSTOM_RELOC_FORMAT
+struct relocation_info
+{
+ /* Address (within segment) to be relocated. */
+ int r_address;
+ /* The meaning of r_symbolnum depends on r_extern. */
+ unsigned int r_symbolnum:24;
+ /* Nonzero means value is a pc-relative offset
+ and it should be relocated for changes in its own address
+ as well as for changes in the symbol or section specified. */
+ unsigned int r_pcrel:1;
+ /* Length (as exponent of 2) of the field to be relocated.
+ Thus, a value of 2 indicates 1<<2 bytes. */
+ unsigned int r_length:2;
+ /* 1 => relocate with value of symbol.
+ r_symbolnum is the index of the symbol
+ in file's the symbol table.
+ 0 => relocate with the address of a segment.
+ r_symbolnum is N_TEXT, N_DATA, N_BSS or N_ABS
+ (the N_EXT bit may be set also, but signifies nothing). */
+ unsigned int r_extern:1;
+ /* The next three bits are for SunOS shared libraries, and seem to
+ be undocumented. */
+#ifdef TC_NS32K
+ unsigned int r_bsr:1;
+ unsigned int r_disp:2;
+#else
+ unsigned int r_baserel:1; /* Linkage table relative */
+ unsigned int r_jmptable:1; /* pc-relative to jump table */
+ unsigned int r_relative:1; /* "relative relocation" */
+#endif /* TC_NS32K */
+ /* unused */
+ unsigned int r_pad:1; /* Padding -- set to zero */
+};
+
+#endif /* CUSTOM_RELOC_FORMAT */
+
+#endif /* __A_OUT_GNU_H__ */
+
+/* end of aout_gnu.h */
diff --git a/gas/config/atof-ieee.c b/gas/config/atof-ieee.c
new file mode 100644
index 0000000..d23405c
--- /dev/null
+++ b/gas/config/atof-ieee.c
@@ -0,0 +1,812 @@
+/* atof_ieee.c - turn a Flonum into an IEEE floating point number
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+
+/* Flonums returned here. */
+extern FLONUM_TYPE generic_floating_point_number;
+
+extern const char EXP_CHARS[];
+/* Precision in LittleNums. */
+/* Don't count the gap in the m68k extended precision format. */
+#define MAX_PRECISION 5
+#define F_PRECISION 2
+#define D_PRECISION 4
+#define X_PRECISION 5
+#define P_PRECISION 5
+
+/* Length in LittleNums of guard bits. */
+#define GUARD 2
+
+#ifndef TC_LARGEST_EXPONENT_IS_NORMAL
+#define TC_LARGEST_EXPONENT_IS_NORMAL(PRECISION) 0
+#endif
+
+static const unsigned long mask[] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff,
+};
+
+static int bits_left_in_littlenum;
+static int littlenums_left;
+static LITTLENUM_TYPE *littlenum_pointer;
+
+static int
+next_bits (int number_of_bits)
+{
+ int return_value;
+
+ if (!littlenums_left)
+ return 0;
+
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+
+ if (--littlenums_left)
+ {
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ --littlenum_pointer;
+ return_value |=
+ (*littlenum_pointer >> bits_left_in_littlenum)
+ & mask[number_of_bits];
+ }
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value =
+ mask[number_of_bits] & (*littlenum_pointer >> bits_left_in_littlenum);
+ }
+ return return_value;
+}
+
+/* Num had better be less than LITTLENUM_NUMBER_OF_BITS. */
+
+static void
+unget_bits (int num)
+{
+ if (!littlenums_left)
+ {
+ ++littlenum_pointer;
+ ++littlenums_left;
+ bits_left_in_littlenum = num;
+ }
+ else if (bits_left_in_littlenum + num > LITTLENUM_NUMBER_OF_BITS)
+ {
+ bits_left_in_littlenum =
+ num - (LITTLENUM_NUMBER_OF_BITS - bits_left_in_littlenum);
+ ++littlenum_pointer;
+ ++littlenums_left;
+ }
+ else
+ bits_left_in_littlenum += num;
+}
+
+static void
+make_invalid_floating_point_number (LITTLENUM_TYPE *words)
+{
+ as_bad (_("cannot create floating-point number"));
+ /* Zero the leftmost bit. */
+ words[0] = (LITTLENUM_TYPE) ((unsigned) -1) >> 1;
+ words[1] = (LITTLENUM_TYPE) -1;
+ words[2] = (LITTLENUM_TYPE) -1;
+ words[3] = (LITTLENUM_TYPE) -1;
+ words[4] = (LITTLENUM_TYPE) -1;
+ words[5] = (LITTLENUM_TYPE) -1;
+}
+
+/* Warning: This returns 16-bit LITTLENUMs. It is up to the caller to
+ figure out any alignment problems and to conspire for the
+ bytes/word to be emitted in the right order. Bigendians beware! */
+
+/* Note that atof-ieee always has X and P precisions enabled. it is up
+ to md_atof to filter them out if the target machine does not support
+ them. */
+
+/* Returns pointer past text consumed. */
+
+char *
+atof_ieee (char *str, /* Text to convert to binary. */
+ int what_kind, /* 'd', 'f', 'x', 'p'. */
+ LITTLENUM_TYPE *words) /* Build the binary here. */
+{
+ /* Extra bits for zeroed low-order bits.
+ The 1st MAX_PRECISION are zeroed, the last contain flonum bits. */
+ static LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ char *return_value;
+ /* Number of 16-bit words in the format. */
+ int precision;
+ long exponent_bits;
+ FLONUM_TYPE save_gen_flonum;
+
+ /* We have to save the generic_floating_point_number because it
+ contains storage allocation about the array of LITTLENUMs where
+ the value is actually stored. We will allocate our own array of
+ littlenums below, but have to restore the global one on exit. */
+ save_gen_flonum = generic_floating_point_number;
+
+ return_value = str;
+ generic_floating_point_number.low = bits + MAX_PRECISION;
+ generic_floating_point_number.high = NULL;
+ generic_floating_point_number.leader = NULL;
+ generic_floating_point_number.exponent = 0;
+ generic_floating_point_number.sign = '\0';
+
+ /* Use more LittleNums than seems necessary: the highest flonum may
+ have 15 leading 0 bits, so could be useless. */
+
+ memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION);
+
+ switch (what_kind)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ precision = F_PRECISION;
+ exponent_bits = 8;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ precision = D_PRECISION;
+ exponent_bits = 11;
+ break;
+
+ case 'x':
+ case 'X':
+ case 'e':
+ case 'E':
+ precision = X_PRECISION;
+ exponent_bits = 15;
+ break;
+
+ case 'p':
+ case 'P':
+ precision = P_PRECISION;
+ exponent_bits = -1;
+ break;
+
+ default:
+ make_invalid_floating_point_number (words);
+ return (NULL);
+ }
+
+ generic_floating_point_number.high
+ = generic_floating_point_number.low + precision - 1 + GUARD;
+
+ if (atof_generic (&return_value, ".", EXP_CHARS,
+ &generic_floating_point_number))
+ {
+ make_invalid_floating_point_number (words);
+ return NULL;
+ }
+ gen_to_words (words, precision, exponent_bits);
+
+ /* Restore the generic_floating_point_number's storage alloc (and
+ everything else). */
+ generic_floating_point_number = save_gen_flonum;
+
+ return return_value;
+}
+
+/* Turn generic_floating_point_number into a real float/double/extended. */
+
+int
+gen_to_words (LITTLENUM_TYPE *words, int precision, long exponent_bits)
+{
+ int return_value = 0;
+
+ long exponent_1;
+ long exponent_2;
+ long exponent_3;
+ long exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+ LITTLENUM_TYPE *lp;
+ LITTLENUM_TYPE *words_end;
+
+ words_end = words + precision;
+#ifdef TC_M68K
+ if (precision == X_PRECISION)
+ /* On the m68k the extended precision format has a gap of 16 bits
+ between the exponent and the mantissa. */
+ words_end++;
+#endif
+
+ if (generic_floating_point_number.low > generic_floating_point_number.leader)
+ {
+ /* 0.0e0 seen. */
+ if (generic_floating_point_number.sign == '+')
+ words[0] = 0x0000;
+ else
+ words[0] = 0x8000;
+ memset (&words[1], '\0',
+ (words_end - words - 1) * sizeof (LITTLENUM_TYPE));
+ return return_value;
+ }
+
+ /* NaN: Do the right thing. */
+ if (generic_floating_point_number.sign == 0)
+ {
+ if (TC_LARGEST_EXPONENT_IS_NORMAL (precision))
+ as_warn (_("NaNs are not supported by this target\n"));
+ if (precision == F_PRECISION)
+ {
+ words[0] = 0x7fff;
+ words[1] = 0xffff;
+ }
+ else if (precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ words[0] = 0x7fff;
+ words[1] = 0;
+ words[2] = 0xffff;
+ words[3] = 0xffff;
+ words[4] = 0xffff;
+ words[5] = 0xffff;
+#else /* ! TC_M68K */
+#ifdef TC_I386
+ words[0] = 0xffff;
+ words[1] = 0xc000;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+#else /* ! TC_I386 */
+ abort ();
+#endif /* ! TC_I386 */
+#endif /* ! TC_M68K */
+ }
+ else
+ {
+ words[0] = 0x7fff;
+ words[1] = 0xffff;
+ words[2] = 0xffff;
+ words[3] = 0xffff;
+ }
+ return return_value;
+ }
+ else if (generic_floating_point_number.sign == 'P')
+ {
+ if (TC_LARGEST_EXPONENT_IS_NORMAL (precision))
+ as_warn (_("Infinities are not supported by this target\n"));
+
+ /* +INF: Do the right thing. */
+ if (precision == F_PRECISION)
+ {
+ words[0] = 0x7f80;
+ words[1] = 0;
+ }
+ else if (precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ words[0] = 0x7fff;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+ words[5] = 0;
+#else /* ! TC_M68K */
+#ifdef TC_I386
+ words[0] = 0x7fff;
+ words[1] = 0x8000;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+#else /* ! TC_I386 */
+ abort ();
+#endif /* ! TC_I386 */
+#endif /* ! TC_M68K */
+ }
+ else
+ {
+ words[0] = 0x7ff0;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ }
+ return return_value;
+ }
+ else if (generic_floating_point_number.sign == 'N')
+ {
+ if (TC_LARGEST_EXPONENT_IS_NORMAL (precision))
+ as_warn (_("Infinities are not supported by this target\n"));
+
+ /* Negative INF. */
+ if (precision == F_PRECISION)
+ {
+ words[0] = 0xff80;
+ words[1] = 0x0;
+ }
+ else if (precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ words[0] = 0xffff;
+ words[1] = 0;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+ words[5] = 0;
+#else /* ! TC_M68K */
+#ifdef TC_I386
+ words[0] = 0xffff;
+ words[1] = 0x8000;
+ words[2] = 0;
+ words[3] = 0;
+ words[4] = 0;
+#else /* ! TC_I386 */
+ abort ();
+#endif /* ! TC_I386 */
+#endif /* ! TC_M68K */
+ }
+ else
+ {
+ words[0] = 0xfff0;
+ words[1] = 0x0;
+ words[2] = 0x0;
+ words[3] = 0x0;
+ }
+ return return_value;
+ }
+
+ /* The floating point formats we support have:
+ Bit 15 is sign bit.
+ Bits 14:n are excess-whatever exponent.
+ Bits n-1:0 (if any) are most significant bits of fraction.
+ Bits 15:0 of the next word(s) are the next most significant bits.
+
+ So we need: number of bits of exponent, number of bits of
+ mantissa. */
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = generic_floating_point_number.leader;
+ littlenums_left = (1
+ + generic_floating_point_number.leader
+ - generic_floating_point_number.low);
+
+ /* Seek (and forget) 1st significant bit. */
+ for (exponent_skippage = 0; !next_bits (1); ++exponent_skippage);
+ exponent_1 = (generic_floating_point_number.exponent
+ + generic_floating_point_number.leader
+ + 1
+ - generic_floating_point_number.low);
+
+ /* Radix LITTLENUM_RADIX, point just higher than
+ generic_floating_point_number.leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2);
+
+ /* Offset exponent. */
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits. */
+ word1 = ((generic_floating_point_number.sign == '+')
+ ? 0
+ : (1 << (LITTLENUM_NUMBER_OF_BITS - 1)));
+
+ /* Assume 2's complement integers. */
+ if (exponent_4 <= 0)
+ {
+ int prec_bits;
+ int num_bits;
+
+ unget_bits (1);
+ num_bits = -exponent_4;
+ prec_bits =
+ LITTLENUM_NUMBER_OF_BITS * precision - (exponent_bits + 1 + num_bits);
+#ifdef TC_I386
+ if (precision == X_PRECISION && exponent_bits == 15)
+ {
+ /* On the i386 a denormalized extended precision float is
+ shifted down by one, effectively decreasing the exponent
+ bias by one. */
+ prec_bits -= 1;
+ num_bits += 1;
+ }
+#endif
+
+ if (num_bits >= LITTLENUM_NUMBER_OF_BITS - exponent_bits)
+ {
+ /* Bigger than one littlenum. */
+ num_bits -= (LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits;
+ *lp++ = word1;
+ if (num_bits + exponent_bits + 1
+ > precision * LITTLENUM_NUMBER_OF_BITS)
+ {
+ /* Exponent overflow. */
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+#ifdef TC_M68K
+ if (precision == X_PRECISION && exponent_bits == 15)
+ *lp++ = 0;
+#endif
+ while (num_bits >= LITTLENUM_NUMBER_OF_BITS)
+ {
+ num_bits -= LITTLENUM_NUMBER_OF_BITS;
+ *lp++ = 0;
+ }
+ if (num_bits)
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS - (num_bits));
+ }
+ else
+ {
+ if (precision == X_PRECISION && exponent_bits == 15)
+ {
+ *lp++ = word1;
+#ifdef TC_M68K
+ *lp++ = 0;
+#endif
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS - num_bits);
+ }
+ else
+ {
+ word1 |= next_bits ((LITTLENUM_NUMBER_OF_BITS - 1)
+ - (exponent_bits + num_bits));
+ *lp++ = word1;
+ }
+ }
+ while (lp < words_end)
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ /* Round the mantissa up, but don't change the number. */
+ if (next_bits (1))
+ {
+ --lp;
+ if (prec_bits >= LITTLENUM_NUMBER_OF_BITS)
+ {
+ int n = 0;
+ int tmp_bits;
+
+ n = 0;
+ tmp_bits = prec_bits;
+ while (tmp_bits > LITTLENUM_NUMBER_OF_BITS)
+ {
+ if (lp[n] != (LITTLENUM_TYPE) - 1)
+ break;
+ --n;
+ tmp_bits -= LITTLENUM_NUMBER_OF_BITS;
+ }
+ if (tmp_bits > LITTLENUM_NUMBER_OF_BITS
+ || (lp[n] & mask[tmp_bits]) != mask[tmp_bits]
+ || (prec_bits != (precision * LITTLENUM_NUMBER_OF_BITS
+ - exponent_bits - 1)
+#ifdef TC_I386
+ /* An extended precision float with only the integer
+ bit set would be invalid. That must be converted
+ to the smallest normalized number. */
+ && !(precision == X_PRECISION
+ && prec_bits == (precision * LITTLENUM_NUMBER_OF_BITS
+ - exponent_bits - 2))
+#endif
+ ))
+ {
+ unsigned long carry;
+
+ for (carry = 1; carry && (lp >= words); lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+ }
+ else
+ {
+ /* This is an overflow of the denormal numbers. We
+ need to forget what we have produced, and instead
+ generate the smallest normalized number. */
+ lp = words;
+ word1 = ((generic_floating_point_number.sign == '+')
+ ? 0
+ : (1 << (LITTLENUM_NUMBER_OF_BITS - 1)));
+ word1 |= (1
+ << ((LITTLENUM_NUMBER_OF_BITS - 1)
+ - exponent_bits));
+ *lp++ = word1;
+#ifdef TC_I386
+ /* Set the integer bit in the extended precision format.
+ This cannot happen on the m68k where the mantissa
+ just overflows into the integer bit above. */
+ if (precision == X_PRECISION)
+ *lp++ = 1 << (LITTLENUM_NUMBER_OF_BITS - 1);
+#endif
+ while (lp < words_end)
+ *lp++ = 0;
+ }
+ }
+ else
+ *lp += 1;
+ }
+
+ return return_value;
+ }
+ else if ((unsigned long) exponent_4 > mask[exponent_bits]
+ || (! TC_LARGEST_EXPONENT_IS_NORMAL (precision)
+ && (unsigned long) exponent_4 == mask[exponent_bits]))
+ {
+ /* Exponent overflow. Lose immediately. */
+
+ /* We leave return_value alone: admit we read the
+ number, but return a floating exception
+ because we can't encode the number. */
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+ else
+ {
+ word1 |= (exponent_4 << ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits))
+ | next_bits ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits);
+ }
+
+ *lp++ = word1;
+
+ /* X_PRECISION is special: on the 68k, it has 16 bits of zero in the
+ middle. Either way, it is then followed by a 1 bit. */
+ if (exponent_bits == 15 && precision == X_PRECISION)
+ {
+#ifdef TC_M68K
+ *lp++ = 0;
+#endif
+ *lp++ = (1 << (LITTLENUM_NUMBER_OF_BITS - 1)
+ | next_bits (LITTLENUM_NUMBER_OF_BITS - 1));
+ }
+
+ /* The rest of the words are just mantissa bits. */
+ while (lp < words_end)
+ *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ if (next_bits (1))
+ {
+ unsigned long carry;
+ /* Since the NEXT bit is a 1, round UP the mantissa.
+ The cunning design of these hidden-1 floats permits
+ us to let the mantissa overflow into the exponent, and
+ it 'does the right thing'. However, we lose if the
+ highest-order bit of the lowest-order word flips.
+ Is that clear? */
+
+ /* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif */
+ for (carry = 1, lp--; carry; lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ if (lp == words)
+ break;
+ }
+ if (precision == X_PRECISION && exponent_bits == 15)
+ {
+ /* Extended precision numbers have an explicit integer bit
+ that we may have to restore. */
+ if (lp == words)
+ {
+#ifdef TC_M68K
+ /* On the m68k there is a gap of 16 bits. We must
+ explicitly propagate the carry into the exponent. */
+ words[0] += words[1];
+ words[1] = 0;
+ lp++;
+#endif
+ /* Put back the integer bit. */
+ lp[1] |= 1 << (LITTLENUM_NUMBER_OF_BITS - 1);
+ }
+ }
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)))
+ {
+ /* We leave return_value alone: admit we read the number,
+ but return a floating exception because we can't encode
+ the number. */
+ *words &= ~(1 << (LITTLENUM_NUMBER_OF_BITS - 1));
+ }
+ }
+ return return_value;
+}
+
+#ifdef TEST
+char *
+print_gen (gen)
+ FLONUM_TYPE *gen;
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE arr[10];
+ double dv;
+ float fv;
+ static char sbuf[40];
+
+ if (gen)
+ {
+ f = generic_floating_point_number;
+ generic_floating_point_number = *gen;
+ }
+ gen_to_words (&arr[0], 4, 11);
+ memcpy (&dv, &arr[0], sizeof (double));
+ sprintf (sbuf, "%x %x %x %x %.14G ", arr[0], arr[1], arr[2], arr[3], dv);
+ gen_to_words (&arr[0], 2, 8);
+ memcpy (&fv, &arr[0], sizeof (float));
+ sprintf (sbuf + strlen (sbuf), "%x %x %.12g\n", arr[0], arr[1], fv);
+
+ if (gen)
+ generic_floating_point_number = f;
+
+ return (sbuf);
+}
+#endif
+
+extern const char FLT_CHARS[];
+#define MAX_LITTLENUMS 6
+
+/* This is a utility function called from various tc-*.c files. It
+ is here in order to reduce code duplication.
+
+ Turn a string at input_line_pointer into a floating point constant
+ of type TYPE (a character found in the FLT_CHARS macro), and store
+ it as LITTLENUMS in the bytes buffer LITP. The number of chars
+ emitted is stored in *SIZEP. BIG_WORDIAN is TRUE if the littlenums
+ should be emitted most significant littlenum first.
+
+ An error message is returned, or a NULL pointer if everything went OK. */
+
+char *
+ieee_md_atof (int type,
+ char *litP,
+ int *sizeP,
+ bfd_boolean big_wordian)
+{
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+ int prec = 0;
+
+ if (strchr (FLT_CHARS, type) != NULL)
+ {
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = F_PRECISION;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = D_PRECISION;
+ break;
+
+ case 't':
+ case 'T':
+ prec = X_PRECISION;
+ type = 'x'; /* This is what atof_ieee() understands. */
+ break;
+
+ case 'x':
+ case 'X':
+ case 'p':
+ case 'P':
+#ifdef TC_M68K
+ /* Note: on the m68k there is a gap of 16 bits (one littlenum)
+ between the exponent and mantissa. Hence the precision is
+ 6 and not 5. */
+ prec = P_PRECISION + 1;
+#else
+ prec = P_PRECISION;
+#endif
+ break;
+
+ default:
+ break;
+ }
+ }
+ /* The 'f' and 'd' types are always recognised, even if the target has
+ not put them into the FLT_CHARS macro. This is because the 'f' type
+ can come from the .dc.s, .dcb.s, .float or .single pseudo-ops and the
+ 'd' type from the .dc.d, .dbc.d or .double pseudo-ops.
+
+ The 'x' type is not implicitly recongised however, even though it can
+ be generated by the .dc.x and .dbc.x pseudo-ops because not all targets
+ can support floating point values that big. ie the target has to
+ explicitly allow them by putting them into FLT_CHARS. */
+ else if (type == 'f')
+ prec = F_PRECISION;
+ else if (type == 'd')
+ prec = D_PRECISION;
+
+ if (prec == 0)
+ {
+ *sizeP = 0;
+ return _("Unrecognized or unsupported floating point constant");
+ }
+
+ gas_assert (prec <= MAX_LITTLENUMS);
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ if (big_wordian)
+ {
+ for (wordP = words; prec --;)
+ {
+ md_number_to_chars (litP, (valueT) (* wordP ++), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ {
+ for (wordP = words + prec; prec --;)
+ {
+ md_number_to_chars (litP, (valueT) (* -- wordP), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+
+ return NULL;
+}
diff --git a/gas/config/atof-vax.c b/gas/config/atof-vax.c
new file mode 100644
index 0000000..9075b97
--- /dev/null
+++ b/gas/config/atof-vax.c
@@ -0,0 +1,450 @@
+/* atof_vax.c - turn a Flonum into a VAX floating point number
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+
+/* Precision in LittleNums. */
+#define MAX_PRECISION 8
+#define H_PRECISION 8
+#define G_PRECISION 4
+#define D_PRECISION 4
+#define F_PRECISION 2
+
+/* Length in LittleNums of guard bits. */
+#define GUARD 2
+
+int flonum_gen2vax (int, FLONUM_TYPE *, LITTLENUM_TYPE *);
+
+/* Number of chars in flonum type 'letter'. */
+
+static unsigned int
+atof_vax_sizeof (int letter)
+{
+ int return_value;
+
+ /* Permitting uppercase letters is probably a bad idea.
+ Please use only lower-cased letters in case the upper-cased
+ ones become unsupported! */
+ switch (letter)
+ {
+ case 'f':
+ case 'F':
+ return_value = 4;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'g':
+ case 'G':
+ return_value = 8;
+ break;
+
+ case 'h':
+ case 'H':
+ return_value = 16;
+ break;
+
+ default:
+ return_value = 0;
+ break;
+ }
+
+ return return_value;
+}
+
+static const long mask[] =
+{
+ 0x00000000,
+ 0x00000001,
+ 0x00000003,
+ 0x00000007,
+ 0x0000000f,
+ 0x0000001f,
+ 0x0000003f,
+ 0x0000007f,
+ 0x000000ff,
+ 0x000001ff,
+ 0x000003ff,
+ 0x000007ff,
+ 0x00000fff,
+ 0x00001fff,
+ 0x00003fff,
+ 0x00007fff,
+ 0x0000ffff,
+ 0x0001ffff,
+ 0x0003ffff,
+ 0x0007ffff,
+ 0x000fffff,
+ 0x001fffff,
+ 0x003fffff,
+ 0x007fffff,
+ 0x00ffffff,
+ 0x01ffffff,
+ 0x03ffffff,
+ 0x07ffffff,
+ 0x0fffffff,
+ 0x1fffffff,
+ 0x3fffffff,
+ 0x7fffffff,
+ 0xffffffff
+};
+
+
+/* Shared between flonum_gen2vax and next_bits. */
+static int bits_left_in_littlenum;
+static LITTLENUM_TYPE *littlenum_pointer;
+static LITTLENUM_TYPE *littlenum_end;
+
+static int
+next_bits (int number_of_bits)
+{
+ int return_value;
+
+ if (littlenum_pointer < littlenum_end)
+ return 0;
+ if (number_of_bits >= bits_left_in_littlenum)
+ {
+ return_value = mask[bits_left_in_littlenum] & *littlenum_pointer;
+ number_of_bits -= bits_left_in_littlenum;
+ return_value <<= number_of_bits;
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits;
+ littlenum_pointer--;
+ if (littlenum_pointer >= littlenum_end)
+ return_value |= ((*littlenum_pointer) >> (bits_left_in_littlenum)) & mask[number_of_bits];
+ }
+ else
+ {
+ bits_left_in_littlenum -= number_of_bits;
+ return_value = mask[number_of_bits] & ((*littlenum_pointer) >> bits_left_in_littlenum);
+ }
+ return return_value;
+}
+
+static void
+make_invalid_floating_point_number (LITTLENUM_TYPE *words)
+{
+ *words = 0x8000; /* Floating Reserved Operand Code. */
+}
+
+
+static int /* 0 means letter is OK. */
+what_kind_of_float (int letter, /* In: lowercase please. What kind of float? */
+ int *precisionP, /* Number of 16-bit words in the float. */
+ long *exponent_bitsP) /* Number of exponent bits. */
+{
+ int retval;
+
+ retval = 0;
+ switch (letter)
+ {
+ case 'f':
+ *precisionP = F_PRECISION;
+ *exponent_bitsP = 8;
+ break;
+
+ case 'd':
+ *precisionP = D_PRECISION;
+ *exponent_bitsP = 8;
+ break;
+
+ case 'g':
+ *precisionP = G_PRECISION;
+ *exponent_bitsP = 11;
+ break;
+
+ case 'h':
+ *precisionP = H_PRECISION;
+ *exponent_bitsP = 15;
+ break;
+
+ default:
+ retval = 69;
+ break;
+ }
+ return retval;
+}
+
+/* Warning: this returns 16-bit LITTLENUMs, because that is
+ what the VAX thinks in. It is up to the caller to figure
+ out any alignment problems and to conspire for the bytes/word
+ to be emitted in the right order. Bigendians beware! */
+
+static char *
+atof_vax (char *str, /* Text to convert to binary. */
+ int what_kind, /* 'd', 'f', 'g', 'h' */
+ LITTLENUM_TYPE *words) /* Build the binary here. */
+{
+ FLONUM_TYPE f;
+ LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ /* Extra bits for zeroed low-order bits.
+ The 1st MAX_PRECISION are zeroed,
+ the last contain flonum bits. */
+ char *return_value;
+ int precision; /* Number of 16-bit words in the format. */
+ long exponent_bits;
+
+ return_value = str;
+ f.low = bits + MAX_PRECISION;
+ f.high = NULL;
+ f.leader = NULL;
+ f.exponent = 0;
+ f.sign = '\0';
+
+ if (what_kind_of_float (what_kind, &precision, &exponent_bits))
+ {
+ return_value = NULL;
+ make_invalid_floating_point_number (words);
+ }
+
+ if (return_value)
+ {
+ memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION);
+
+ /* Use more LittleNums than seems
+ necessary: the highest flonum may have
+ 15 leading 0 bits, so could be useless. */
+ f.high = f.low + precision - 1 + GUARD;
+
+ if (atof_generic (&return_value, ".", "eE", &f))
+ {
+ make_invalid_floating_point_number (words);
+ return_value = NULL;
+ }
+ else if (flonum_gen2vax (what_kind, &f, words))
+ return_value = NULL;
+ }
+
+ return return_value;
+}
+
+/* In: a flonum, a vax floating point format.
+ Out: a vax floating-point bit pattern. */
+
+int
+flonum_gen2vax (int format_letter, /* One of 'd' 'f' 'g' 'h'. */
+ FLONUM_TYPE *f,
+ LITTLENUM_TYPE *words) /* Deliver answer here. */
+{
+ LITTLENUM_TYPE *lp;
+ int precision;
+ long exponent_bits;
+ int return_value; /* 0 == OK. */
+
+ return_value = what_kind_of_float (format_letter, &precision, &exponent_bits);
+
+ if (return_value != 0)
+ make_invalid_floating_point_number (words);
+
+ else
+ {
+ if (f->low > f->leader)
+ /* 0.0e0 seen. */
+ memset (words, '\0', sizeof (LITTLENUM_TYPE) * precision);
+
+ else
+ {
+ long exponent_1;
+ long exponent_2;
+ long exponent_3;
+ long exponent_4;
+ int exponent_skippage;
+ LITTLENUM_TYPE word1;
+
+ /* JF: Deal with new Nan, +Inf and -Inf codes. */
+ if (f->sign != '-' && f->sign != '+')
+ {
+ make_invalid_floating_point_number (words);
+ return return_value;
+ }
+
+ /* All vaxen floating_point formats (so far) have:
+ Bit 15 is sign bit.
+ Bits 14:n are excess-whatever exponent.
+ Bits n-1:0 (if any) are most significant bits of fraction.
+ Bits 15:0 of the next word are the next most significant bits.
+ And so on for each other word.
+
+ All this to be compatible with a KF11?? (Which is still faster
+ than lots of vaxen I can think of, but it also has higher
+ maintenance costs ... sigh).
+
+ So we need: number of bits of exponent, number of bits of
+ mantissa. */
+
+ bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS;
+ littlenum_pointer = f->leader;
+ littlenum_end = f->low;
+ /* Seek (and forget) 1st significant bit. */
+ for (exponent_skippage = 0;
+ !next_bits (1);
+ exponent_skippage++);
+
+ exponent_1 = f->exponent + f->leader + 1 - f->low;
+ /* Radix LITTLENUM_RADIX, point just higher than f->leader. */
+ exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS;
+ /* Radix 2. */
+ exponent_3 = exponent_2 - exponent_skippage;
+ /* Forget leading zeros, forget 1st bit. */
+ exponent_4 = exponent_3 + (1 << (exponent_bits - 1));
+ /* Offset exponent. */
+
+ if (exponent_4 & ~mask[exponent_bits])
+ {
+ /* Exponent overflow. Lose immediately. */
+ make_invalid_floating_point_number (words);
+
+ /* We leave return_value alone: admit we read the
+ number, but return a floating exception
+ because we can't encode the number. */
+ }
+ else
+ {
+ lp = words;
+
+ /* Word 1. Sign, exponent and perhaps high bits.
+ Assume 2's complement integers. */
+ word1 = (((exponent_4 & mask[exponent_bits]) << (15 - exponent_bits))
+ | ((f->sign == '+') ? 0 : 0x8000)
+ | next_bits (15 - exponent_bits));
+ *lp++ = word1;
+
+ /* The rest of the words are just mantissa bits. */
+ for (; lp < words + precision; lp++)
+ *lp = next_bits (LITTLENUM_NUMBER_OF_BITS);
+
+ if (next_bits (1))
+ {
+ /* Since the NEXT bit is a 1, round UP the mantissa.
+ The cunning design of these hidden-1 floats permits
+ us to let the mantissa overflow into the exponent, and
+ it 'does the right thing'. However, we lose if the
+ highest-order bit of the lowest-order word flips.
+ Is that clear? */
+ unsigned long carry;
+
+ /*
+ #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2)
+ Please allow at least 1 more bit in carry than is in a LITTLENUM.
+ We need that extra bit to hold a carry during a LITTLENUM carry
+ propagation. Another extra bit (kept 0) will assure us that we
+ don't get a sticky sign bit after shifting right, and that
+ permits us to propagate the carry without any masking of bits.
+ #endif */
+ for (carry = 1, lp--;
+ carry && (lp >= words);
+ lp--)
+ {
+ carry = *lp + carry;
+ *lp = carry;
+ carry >>= LITTLENUM_NUMBER_OF_BITS;
+ }
+
+ if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1)))
+ {
+ make_invalid_floating_point_number (words);
+ /* We leave return_value alone: admit we read the
+ number, but return a floating exception
+ because we can't encode the number. */
+ }
+ }
+ }
+ }
+ }
+ return return_value;
+}
+
+/* JF this used to be in vax.c but this looks like a better place for it. */
+
+/* In: input_line_pointer->the 1st character of a floating-point
+ number.
+ 1 letter denoting the type of statement that wants a
+ binary floating point number returned.
+ Address of where to build floating point literal.
+ Assumed to be 'big enough'.
+ Address of where to return size of literal (in chars).
+
+ Out: Input_line_pointer->of next char after floating number.
+ Error message, or 0.
+ Floating point literal.
+ Number of chars we used for the literal. */
+
+#define MAXIMUM_NUMBER_OF_LITTLENUMS 8 /* For .hfloats. */
+
+char *
+vax_md_atof (int what_statement_type,
+ char *literalP,
+ int *sizeP)
+{
+ LITTLENUM_TYPE words[MAXIMUM_NUMBER_OF_LITTLENUMS];
+ char kind_of_float;
+ unsigned int number_of_chars;
+ LITTLENUM_TYPE *littlenumP;
+
+ switch (what_statement_type)
+ {
+ case 'F':
+ case 'f':
+ kind_of_float = 'f';
+ break;
+
+ case 'D':
+ case 'd':
+ kind_of_float = 'd';
+ break;
+
+ case 'g':
+ kind_of_float = 'g';
+ break;
+
+ case 'h':
+ kind_of_float = 'h';
+ break;
+
+ default:
+ kind_of_float = 0;
+ break;
+ };
+
+ if (kind_of_float)
+ {
+ LITTLENUM_TYPE *limit;
+
+ input_line_pointer = atof_vax (input_line_pointer,
+ kind_of_float,
+ words);
+ /* The atof_vax() builds up 16-bit numbers.
+ Since the assembler may not be running on
+ a little-endian machine, be very careful about
+ converting words to chars. */
+ number_of_chars = atof_vax_sizeof (kind_of_float);
+ know (number_of_chars <= MAXIMUM_NUMBER_OF_LITTLENUMS * sizeof (LITTLENUM_TYPE));
+ limit = words + (number_of_chars / sizeof (LITTLENUM_TYPE));
+ for (littlenumP = words; littlenumP < limit; littlenumP++)
+ {
+ md_number_to_chars (literalP, *littlenumP, sizeof (LITTLENUM_TYPE));
+ literalP += sizeof (LITTLENUM_TYPE);
+ };
+ }
+ else
+ number_of_chars = 0;
+
+ *sizeP = number_of_chars;
+ return kind_of_float ? NULL : _("Unrecognized or unsupported floating point constant");
+}
diff --git a/gas/config/bfin-aux.h b/gas/config/bfin-aux.h
new file mode 100644
index 0000000..44665b9
--- /dev/null
+++ b/gas/config/bfin-aux.h
@@ -0,0 +1,68 @@
+/* bfin-aux.h ADI Blackfin Header file for gas
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "bfin-defs.h"
+
+#define REG_T Register *
+
+INSTR_T bfin_gen_dsp32mac (int, int, int, int, int, int, int, int, int, int,
+ REG_T, REG_T, REG_T, int);
+INSTR_T bfin_gen_dsp32mult (int, int, int, int, int, int, int, int, int, int,
+ REG_T, REG_T, REG_T, int);
+INSTR_T bfin_gen_dsp32alu (int, int, int, int, int, REG_T, REG_T, REG_T, REG_T);
+INSTR_T bfin_gen_dsp32shift (int, REG_T, REG_T, REG_T, int, int);
+INSTR_T bfin_gen_dsp32shiftimm (int, REG_T, int, REG_T, int, int);
+INSTR_T bfin_gen_ldimmhalf (REG_T, int, int, int, Expr_Node *, int);
+INSTR_T bfin_gen_ldstidxi (REG_T, REG_T, int, int, int, Expr_Node *);
+INSTR_T bfin_gen_ldst (REG_T, REG_T, int, int, int, int);
+INSTR_T bfin_gen_ldstii (REG_T, REG_T, Expr_Node *, int, int);
+INSTR_T bfin_gen_ldstiifp (REG_T, Expr_Node *, int);
+INSTR_T bfin_gen_ldstpmod (REG_T, REG_T, int, int, REG_T);
+INSTR_T bfin_gen_dspldst (REG_T, REG_T, int, int, int);
+INSTR_T bfin_gen_alu2op (REG_T, REG_T, int);
+INSTR_T bfin_gen_compi2opd (REG_T, int, int);
+INSTR_T bfin_gen_compi2opp (REG_T, int, int);
+INSTR_T bfin_gen_dagmodik (REG_T, int);
+INSTR_T bfin_gen_dagmodim (REG_T, REG_T, int, int);
+INSTR_T bfin_gen_ptr2op (REG_T, REG_T, int);
+INSTR_T bfin_gen_logi2op (int, int, int);
+INSTR_T bfin_gen_comp3op (REG_T, REG_T, REG_T, int);
+INSTR_T bfin_gen_ccmv (REG_T, REG_T, int);
+INSTR_T bfin_gen_ccflag (REG_T, int, int, int, int);
+INSTR_T bfin_gen_cc2stat (int, int, int);
+INSTR_T bfin_gen_regmv (REG_T, REG_T);
+INSTR_T bfin_gen_cc2dreg (int, REG_T);
+INSTR_T bfin_gen_brcc (int, int, Expr_Node *);
+INSTR_T bfin_gen_ujump (Expr_Node *);
+INSTR_T bfin_gen_cactrl (REG_T, int, int);
+INSTR_T bfin_gen_progctrl (int, int);
+INSTR_T bfin_gen_loopsetup (Expr_Node *, REG_T, int, Expr_Node *, REG_T);
+INSTR_T bfin_gen_loop (Expr_Node *, REG_T, int, REG_T);
+void bfin_loop_attempt_create_label (Expr_Node *, int);
+void bfin_loop_beginend (Expr_Node *, int);
+INSTR_T bfin_gen_pushpopmultiple (int, int, int, int, int);
+INSTR_T bfin_gen_pushpopreg (REG_T, int);
+INSTR_T bfin_gen_calla (Expr_Node *, int);
+INSTR_T bfin_gen_linkage (int, int);
+INSTR_T bfin_gen_pseudodbg (int, int, int);
+INSTR_T bfin_gen_pseudodbg_assert (int, REG_T, int);
+INSTR_T bfin_gen_pseudochr (int);
+bfd_boolean bfin_resource_conflict (INSTR_T, INSTR_T, INSTR_T);
+INSTR_T bfin_gen_multi_instr (INSTR_T, INSTR_T, INSTR_T);
diff --git a/gas/config/bfin-defs.h b/gas/config/bfin-defs.h
new file mode 100644
index 0000000..0f494c8
--- /dev/null
+++ b/gas/config/bfin-defs.h
@@ -0,0 +1,397 @@
+/* bfin-defs.h ADI Blackfin gas header file
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef BFIN_PARSE_H
+#define BFIN_PARSE_H
+
+#include "opcode/bfin.h"
+
+#define PCREL 1
+#define CODE_FRAG_SIZE 4096 /* 1 page. */
+
+
+/* Definition for all status bits. */
+typedef enum
+{
+ c_0,
+ c_1,
+ c_4,
+ c_2,
+ c_uimm2,
+ c_uimm3,
+ c_imm3,
+ c_pcrel4,
+ c_imm4,
+ c_uimm4s4,
+ c_uimm4,
+ c_uimm4s2,
+ c_negimm5s4,
+ c_imm5,
+ c_uimm5,
+ c_imm6,
+ c_imm7,
+ c_imm8,
+ c_uimm8,
+ c_pcrel8,
+ c_uimm8s4,
+ c_pcrel8s4,
+ c_lppcrel10,
+ c_pcrel10,
+ c_pcrel12,
+ c_imm16s4,
+ c_luimm16,
+ c_imm16,
+ c_huimm16,
+ c_rimm16,
+ c_imm16s2,
+ c_uimm16s4,
+ c_uimm16,
+ c_pcrel24
+} const_forms_t;
+
+
+/* High-Nibble: group code, low nibble: register code. */
+
+
+#define T_REG_R 0x00
+#define T_REG_P 0x10
+#define T_REG_I 0x20
+#define T_REG_B 0x30
+#define T_REG_L 0x34
+#define T_REG_M 0x24
+#define T_REG_A 0x40
+
+/* All registers above this value don't
+ belong to a usuable register group. */
+#define T_NOGROUP 0xa0
+
+/* Flags. */
+#define F_REG_NONE 0
+#define F_REG_HIGH 1
+#define F_REG_LOW 2
+
+enum machine_registers
+{
+ REG_R0 = T_REG_R, REG_R1, REG_R2, REG_R3, REG_R4, REG_R5, REG_R6, REG_R7,
+ REG_P0 = T_REG_P, REG_P1, REG_P2, REG_P3, REG_P4, REG_P5, REG_SP, REG_FP,
+ REG_I0 = T_REG_I, REG_I1, REG_I2, REG_I3,
+ REG_M0 = T_REG_M, REG_M1, REG_M2, REG_M3,
+ REG_B0 = T_REG_B, REG_B1, REG_B2, REG_B3,
+ REG_L0 = T_REG_L, REG_L1, REG_L2, REG_L3,
+ REG_A0x = T_REG_A, REG_A0w, REG_A1x, REG_A1w,
+ REG_ASTAT = 0x46,
+ REG_RETS = 0x47,
+ REG_LC0 = 0x60, REG_LT0, REG_LB0, REG_LC1, REG_LT1, REG_LB1,
+ REG_CYCLES, REG_CYCLES2,
+ REG_USP = 0x70, REG_SEQSTAT, REG_SYSCFG,
+ REG_RETI, REG_RETX, REG_RETN, REG_RETE, REG_EMUDAT,
+
+/* These don't have groups. */
+ REG_sftreset = T_NOGROUP, REG_omode, REG_excause, REG_emucause,
+ REG_idle_req, REG_hwerrcause,
+ REG_A0 = 0xc0, REG_A1, REG_CC,
+/* Pseudo registers, used only for distinction from symbols. */
+ REG_RL0, REG_RL1, REG_RL2, REG_RL3,
+ REG_RL4, REG_RL5, REG_RL6, REG_RL7,
+ REG_RH0, REG_RH1, REG_RH2, REG_RH3,
+ REG_RH4, REG_RH5, REG_RH6, REG_RH7,
+ REG_LASTREG
+};
+
+/* Status register flags. */
+
+enum statusflags
+{
+ S_AZ = 0,
+ S_AN,
+ S_AC0_COPY,
+ S_V_COPY,
+ S_AQ = 6,
+ S_RND_MOD = 8,
+ S_AC0 = 12,
+ S_AC1,
+ S_AV0 = 16,
+ S_AV0S,
+ S_AV1,
+ S_AV1S,
+ S_V = 24,
+ S_VS = 25
+};
+
+
+enum reg_class
+{
+ rc_dregs_lo,
+ rc_dregs_hi,
+ rc_dregs,
+ rc_dregs_pair,
+ rc_pregs,
+ rc_spfp,
+ rc_dregs_hilo,
+ rc_accum_ext,
+ rc_accum_word,
+ rc_accum,
+ rc_iregs,
+ rc_mregs,
+ rc_bregs,
+ rc_lregs,
+ rc_dpregs,
+ rc_gregs,
+ rc_regs,
+ rc_statbits,
+ rc_ignore_bits,
+ rc_ccstat,
+ rc_counters,
+ rc_dregs2_sysregs1,
+ rc_open,
+ rc_sysregs2,
+ rc_sysregs3,
+ rc_allregs,
+ LIM_REG_CLASSES
+};
+
+/* Register type checking macros. */
+
+#define CODE_MASK 0x07
+#define CLASS_MASK 0xf0
+
+#define REG_SAME(a, b) ((a).regno == (b).regno)
+#define REG_EQUAL(a, b) (((a).regno & CODE_MASK) == ((b).regno & CODE_MASK))
+#define REG_CLASS(a) ((a).regno & 0xf0)
+#define IS_A1(a) ((a).regno == REG_A1)
+#define IS_H(a) ((a).flags & F_REG_HIGH ? 1: 0)
+#define IS_EVEN(r) ((r).regno % 2 == 0)
+#define IS_HCOMPL(a, b) (REG_EQUAL(a, b) && \
+ ((a).flags & F_REG_HIGH) != ((b).flags & F_REG_HIGH))
+
+/* register type checking. */
+#define _TYPECHECK(r, x) (((r).regno & CLASS_MASK) == T_REG_##x)
+
+#define IS_DREG(r) _TYPECHECK(r, R)
+#define IS_DREG_H(r) (_TYPECHECK(r, R) && IS_H(r))
+#define IS_DREG_L(r) (_TYPECHECK(r, R) && !IS_H(r))
+#define IS_PREG(r) _TYPECHECK(r, P)
+#define IS_IREG(r) (((r).regno & 0xf4) == T_REG_I)
+#define IS_MREG(r) (((r).regno & 0xf4) == T_REG_M)
+#define IS_BREG(r) (((r).regno & 0xf4) == T_REG_B)
+#define IS_LREG(r) (((r).regno & 0xf4) == T_REG_L)
+#define IS_CREG(r) ((r).regno == REG_LC0 || (r).regno == REG_LC1)
+#define IS_EMUDAT(r) ((r).regno == REG_EMUDAT)
+#define IS_ALLREG(r) ((r).regno < T_NOGROUP)
+
+#define IS_GENREG(r) \
+ (IS_DREG (r) || IS_PREG (r) \
+ || (r).regno == REG_A0x || (r).regno == REG_A0w \
+ || (r).regno == REG_A1x || (r).regno == REG_A1w)
+
+#define IS_DAGREG(r) \
+ (IS_IREG (r) || IS_MREG (r) || IS_BREG (r) || IS_LREG (r))
+
+#define IS_SYSREG(r) \
+ ((r).regno == REG_ASTAT || (r).regno == REG_SEQSTAT \
+ || (r).regno == REG_SYSCFG || (r).regno == REG_RETI \
+ || (r).regno == REG_RETX || (r).regno == REG_RETN \
+ || (r).regno == REG_RETE || (r).regno == REG_RETS \
+ || (r).regno == REG_LC0 || (r).regno == REG_LC1 \
+ || (r).regno == REG_LT0 || (r).regno == REG_LT1 \
+ || (r).regno == REG_LB0 || (r).regno == REG_LB1 \
+ || (r).regno == REG_CYCLES || (r).regno == REG_CYCLES2 \
+ || (r).regno == REG_EMUDAT)
+
+/* Expression value macros. */
+
+typedef enum
+{
+ ones_compl,
+ twos_compl,
+ mult,
+ divide,
+ mod,
+ add,
+ sub,
+ lsh,
+ rsh,
+ logand,
+ logior,
+ logxor
+} expr_opcodes_t;
+
+struct expressionS;
+
+#define SYMBOL_T symbolS*
+
+struct expression_cell
+{
+ int value;
+ SYMBOL_T symbol;
+};
+
+/* User Type Definitions. */
+struct bfin_insn
+{
+ unsigned long value;
+ struct bfin_insn *next;
+ struct expression_cell *exp;
+ int pcrel;
+ int reloc;
+};
+
+#define INSTR_T struct bfin_insn*
+#define EXPR_T struct expression_cell*
+
+typedef struct expr_node_struct Expr_Node;
+
+extern INSTR_T gencode (unsigned long x);
+extern INSTR_T conscode (INSTR_T head, INSTR_T tail);
+extern INSTR_T conctcode (INSTR_T head, INSTR_T tail);
+extern INSTR_T note_reloc
+ (INSTR_T code, Expr_Node *, int reloc,int pcrel);
+extern INSTR_T note_reloc1
+ (INSTR_T code, const char * sym, int reloc, int pcrel);
+extern INSTR_T note_reloc2
+ (INSTR_T code, const char *symbol, int reloc, int value, int pcrel);
+
+/* Types of expressions. */
+typedef enum
+{
+ Expr_Node_Binop, /* Binary operator. */
+ Expr_Node_Unop, /* Unary operator. */
+ Expr_Node_Reloc, /* Symbol to be relocated. */
+ Expr_Node_GOT_Reloc, /* Symbol to be relocated using the GOT. */
+ Expr_Node_Constant /* Constant. */
+} Expr_Node_Type;
+
+/* Types of operators. */
+typedef enum
+{
+ Expr_Op_Type_Add,
+ Expr_Op_Type_Sub,
+ Expr_Op_Type_Mult,
+ Expr_Op_Type_Div,
+ Expr_Op_Type_Mod,
+ Expr_Op_Type_Lshift,
+ Expr_Op_Type_Rshift,
+ Expr_Op_Type_BAND, /* Bitwise AND. */
+ Expr_Op_Type_BOR, /* Bitwise OR. */
+ Expr_Op_Type_BXOR, /* Bitwise exclusive OR. */
+ Expr_Op_Type_LAND, /* Logical AND. */
+ Expr_Op_Type_LOR, /* Logical OR. */
+ Expr_Op_Type_NEG,
+ Expr_Op_Type_COMP /* Complement. */
+} Expr_Op_Type;
+
+/* The value that can be stored ... depends on type. */
+typedef union
+{
+ const char *s_value; /* if relocation symbol, the text. */
+ long long i_value; /* if constant, the value. */
+ Expr_Op_Type op_value; /* if operator, the value. */
+} Expr_Node_Value;
+
+/* The expression node. */
+struct expr_node_struct
+{
+ Expr_Node_Type type;
+ Expr_Node_Value value;
+ Expr_Node *Left_Child;
+ Expr_Node *Right_Child;
+};
+
+
+/* Operations on the expression node. */
+Expr_Node *Expr_Node_Create (Expr_Node_Type type,
+ Expr_Node_Value value,
+ Expr_Node *Left_Child,
+ Expr_Node *Right_Child);
+
+/* Generate the reloc structure as a series of instructions. */
+INSTR_T Expr_Node_Gen_Reloc (Expr_Node *head, int parent_reloc);
+
+#define MKREF(x) mkexpr (0,x)
+#define ALLOCATE(x) malloc (x)
+
+#define NULL_CODE ((INSTR_T) 0)
+
+#ifndef EXPR_VALUE
+#define EXPR_VALUE(x) (((x)->type == Expr_Node_Constant) ? ((x)->value.i_value) : 0)
+#endif
+#ifndef EXPR_SYMBOL
+#define EXPR_SYMBOL(x) ((x)->symbol)
+#endif
+
+
+typedef long reg_t;
+
+
+typedef struct _register
+{
+ reg_t regno; /* Register ID as defined in machine_registers. */
+ int flags;
+} Register;
+
+
+typedef struct _macfunc
+{
+ char n;
+ char op;
+ char w;
+ char P;
+ Register dst;
+ Register s0;
+ Register s1;
+} Macfunc;
+
+typedef struct _opt_mode
+{
+ int MM;
+ int mod;
+} Opt_mode;
+
+typedef enum
+{
+ SEMANTIC_ERROR,
+ NO_INSN_GENERATED,
+ INSN_GENERATED
+} parse_state;
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int debug_codeselection;
+
+void error (char *format, ...);
+void warn (char *format, ...);
+int semantic_error (char *syntax);
+void semantic_error_2 (char *syntax);
+
+EXPR_T mkexpr (int, SYMBOL_T);
+
+/* Defined in bfin-lex.l. */
+void set_start_state (void);
+
+extern int insn_regmask (int, int);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BFIN_PARSE_H */
+
diff --git a/gas/config/bfin-lex-wrapper.c b/gas/config/bfin-lex-wrapper.c
new file mode 100644
index 0000000..d836c30
--- /dev/null
+++ b/gas/config/bfin-lex-wrapper.c
@@ -0,0 +1,25 @@
+/* Copyright (C) 2012-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* The C source file generated by flex includes stdio.h before any of
+ the C code in bfin-lex.l. Make sure we include sysdep.h first, so
+ that config.h can set the correct values for various things. */
+
+#include "sysdep.h"
+#include "bfin-lex.c"
diff --git a/gas/config/bfin-lex.l b/gas/config/bfin-lex.l
new file mode 100644
index 0000000..c8462c5
--- /dev/null
+++ b/gas/config/bfin-lex.l
@@ -0,0 +1,556 @@
+/* bfin-lex.l ADI Blackfin lexer
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+%{
+
+#include "as.h"
+#include "bfin-defs.h"
+#include "bfin-parse.h"
+
+static long parse_int (char **end);
+static int parse_halfreg (Register *r, int cl, char *hr);
+static int parse_reg (Register *r, int type, char *rt);
+int yylex (void);
+
+#define _REG yylval.reg
+
+
+%}
+
+/* Define Start States ... Actually we will use exclusion.
+ If no start state is specified it should match any state
+ and <INITIAL> would match some keyword rules only with
+ initial. */
+%s KEYWORD
+%s FLAGS
+
+%%
+[sS][fF][tT][rR][eE][sS][eE][tT] _REG.regno = REG_sftreset; return REG;
+[oO][mM][oO][dD][eE] _REG.regno = REG_omode; return REG;
+[iI][dD][lL][eE]_[rR][eE][qQ] _REG.regno = REG_idle_req; return REG;
+[hH][wW][eE][rR][rR][cC][aA][uU][sS][eE] _REG.regno = REG_hwerrcause; return REG;
+[eE][xX][cC][aA][uU][sS][eE] _REG.regno = REG_excause; return REG;
+[eE][mM][uU][cC][aA][uU][sS][eE] _REG.regno = REG_emucause; return REG;
+<FLAGS>[zZ] return Z;
+<FLAGS>[xX] return X;
+[wW]32 yylval.value = M_W32; return MMOD;
+[wW] return W;
+[vV][iI][tT]_[mM][aA][xX] return VIT_MAX;
+[vV] return V; /* Special: V is a statflag and a modifier. */
+[uU][sS][pP] _REG.regno = REG_USP; return REG;
+[tT][lL] return TL;
+[tT][hH] return TH;
+[tT][fF][uU] yylval.value = M_TFU; return MMOD;
+[tT][eE][sS][tT][sS][eE][tT] return TESTSET;
+<FLAGS>[tT] yylval.value = M_T; return MMOD;
+<FLAGS>[sS] return S;
+[sS][yY][sS][cC][fF][gG] _REG.regno = REG_SYSCFG; return REG;
+[sS][tT][iI] return STI;
+[sS][sS][yY][nN][cC] return SSYNC;
+[sS][pP]"."[lL] _REG.regno = REG_SP; _REG.flags = F_REG_LOW; return HALF_REG;
+[sS][pP]"."[hH] _REG.regno = REG_SP; _REG.flags = F_REG_HIGH; return HALF_REG;
+[sS][pP] _REG.regno = REG_SP; return REG;
+[sS][iI][gG][nN][bB][iI][tT][sS] return SIGNBITS;
+[sS][iI][gG][nN] return SIGN;
+[sS][eE][qQ][sS][tT][aA][tT] _REG.regno = REG_SEQSTAT; return REG;
+[sS][eE][aA][rR][cC][hH] return SEARCH;
+[sS][hH][iI][fF][tT] return SHIFT;
+[sS][cC][oO] return SCO;
+
+[sS][aA][aA] return SAA;
+[sS]2[rR][nN][dD] yylval.value = M_S2RND; return MMOD;
+[rR][tT][xX] return RTX;
+[rR][tT][sS] return RTS;
+[rR][tT][nN] return RTN;
+[rR][tT][iI] return RTI;
+[rR][tT][eE] return RTE;
+[rR][oO][tT] return ROT;
+[rR][nN][dD]20 return RND20;
+[rR][nN][dD]12 return RND12;
+[rR][nN][dD][lL] return RNDL;
+[rR][nN][dD][hH] return RNDH;
+[rR][nN][dD] return RND;
+
+[rR][0-7]"."[lLhHbB] return parse_halfreg(&yylval.reg, T_REG_R, yytext);
+
+[rR][eE][tT][sS] _REG.regno = REG_RETS; return REG;
+[rR][eE][tT][iI] _REG.regno = REG_RETI; return REG;
+[rR][eE][tT][xX] _REG.regno = REG_RETX; return REG;
+[rR][eE][tT][nN] _REG.regno = REG_RETN; return REG;
+[rR][eE][tT][eE] _REG.regno = REG_RETE; return REG;
+[eE][mM][uU][dD][aA][tT] _REG.regno = REG_EMUDAT; return REG;
+[rR][aA][iI][sS][eE] return RAISE;
+
+[rR][0-7] return parse_reg (&yylval.reg, T_REG_R, yytext);
+
+[rR] return R;
+[pP][rR][nN][tT] return PRNT;
+[pP][cC] return PC;
+[pP][aA][cC][kK] return PACK;
+
+[pP][0-5]"."[lLhH] return parse_halfreg (&yylval.reg, T_REG_P, yytext);
+[pP][0-5] return parse_reg (&yylval.reg, T_REG_P, yytext);
+
+[oO][uU][tT][cC] return OUTC;
+[oO][nN][eE][sS] return ONES;
+
+[nN][oO][tT] return NOT;
+[nN][oO][pP] return NOP;
+[mM][nN][oO][pP] return MNOP;
+[nN][sS] return NS;
+
+
+[mM][iI][nN] return MIN;
+[mM][aA][xX] return MAX;
+
+[mM][0-3]"."[lLhH] return parse_halfreg (&yylval.reg, T_REG_M, yytext);
+[mM][0-3] return parse_reg (&yylval.reg, T_REG_M, yytext);
+
+<FLAGS>[mM] return M;
+[lL][tT] return LT;
+[lL][sS][hH][iI][fF][tT] return LSHIFT;
+[lL][sS][eE][tT][uU][pP] return LSETUP;
+[lL][oO][oO][pP] return LOOP;
+[lL][oO][oO][pP]_[bB][eE][gG][iI][nN] return LOOP_BEGIN;
+[lL][oO][oO][pP]_[eE][nN][dD] return LOOP_END;
+
+[lL][eE] return LE;
+[lL][cC]0 _REG.regno = REG_LC0; return REG;
+[lL][tT]0 _REG.regno = REG_LT0; return REG;
+[lL][bB]0 _REG.regno = REG_LB0; return REG;
+[lL][cC]1 _REG.regno = REG_LC1; return REG;
+[lL][tT]1 _REG.regno = REG_LT1; return REG;
+[lL][bB]1 _REG.regno = REG_LB1; return REG;
+
+[lL][0-3]"."[lLhH] return parse_halfreg (&yylval.reg, T_REG_L, yytext);
+[lL][0-3] return parse_reg (&yylval.reg, T_REG_L, yytext);
+[lL][oO] return LO;
+[jJ][uU][mM][pP]"."[sS] { BEGIN 0; return JUMP_DOT_S;}
+[jJ][uU][mM][pP]"."[lL] { BEGIN 0; return JUMP_DOT_L;}
+[jJ][uU][mM][pP] { BEGIN 0; return JUMP;}
+[jJ][uU][mM][pP]"."[xX] { BEGIN 0; return JUMP_DOT_L; }
+[iI][uU] yylval.value = M_IU; return MMOD;
+[iI][sS][sS]2 yylval.value = M_ISS2; return MMOD;
+[iI][sS] yylval.value = M_IS; return MMOD;
+[iI][hH] yylval.value = M_IH; return MMOD;
+[iI][fF] return IF;
+[iI][0-3]"."[lLhH] return parse_halfreg (&yylval.reg, T_REG_I, yytext);
+[iI][0-3] return parse_reg (&yylval.reg, T_REG_I, yytext);
+[hH][lL][tT] return HLT;
+[hH][iI] return HI;
+[gG][tT] return GT;
+[gG][eE] return GE;
+[fF][uU] yylval.value = M_FU; return MMOD;
+[fF][pP] _REG.regno = REG_FP; return REG;
+[fF][pP]"."[lL] _REG.regno = REG_FP; _REG.flags = F_REG_LOW; return HALF_REG;
+[fF][pP]"."[hH] _REG.regno = REG_FP; _REG.flags = F_REG_HIGH; return HALF_REG;
+
+[eE][xX][tT][rR][aA][cC][tT] return EXTRACT;
+[eE][xX][pP][aA][dD][jJ] return EXPADJ;
+[eE][xX][cC][pP][tT] return EXCPT;
+[eE][mM][uU][eE][xX][cC][pP][tT] return EMUEXCPT;
+[dD][iI][vV][sS] return DIVS;
+[dD][iI][vV][qQ] return DIVQ;
+[dD][iI][sS][aA][lL][gG][nN][eE][xX][cC][pP][tT] return DISALGNEXCPT;
+[dD][eE][pP][oO][sS][iI][tT] return DEPOSIT;
+[dD][bB][gG][hH][aA][lL][tT] return DBGHALT;
+[dD][bB][gG][cC][mM][pP][lL][xX] return DBGCMPLX;
+[dD][bB][gG][aA][lL] return DBGAL;
+[dD][bB][gG][aA][hH] return DBGAH;
+[dD][bB][gG][aA] return DBGA;
+[dD][bB][gG] return DBG;
+[cC][yY][cC][lL][eE][sS]2 { _REG.regno = REG_CYCLES2; return REG; }
+[cC][yY][cC][lL][eE][sS] { _REG.regno = REG_CYCLES; return REG; }
+[cC][sS][yY][nN][cC] return CSYNC;
+[cC][oO] return CO;
+[cC][lL][iI] return CLI;
+
+[cC][cC] _REG.regno = REG_CC; return CCREG;
+[cC][aA][lL][lL]"."[xX] { BEGIN 0; return CALL;}
+[cC][aA][lL][lL] { BEGIN 0; return CALL;}
+[bB][yY][tT][eE][uU][nN][pP][aA][cC][kK] return BYTEUNPACK;
+[bB][yY][tT][eE][pP][aA][cC][kK] return BYTEPACK;
+[bB][yY][tT][eE][oO][pP]16[mM] return BYTEOP16M;
+[bB][yY][tT][eE][oO][pP]16[pP] return BYTEOP16P;
+[bB][yY][tT][eE][oO][pP]3[pP] return BYTEOP3P;
+[bB][yY][tT][eE][oO][pP]2[pP] return BYTEOP2P;
+[bB][yY][tT][eE][oO][pP]1[pP] return BYTEOP1P;
+[bB][yY] return BY;
+[bB][xX][oO][rR][sS][hH][iI][fF][tT] return BXORSHIFT;
+[bB][xX][oO][rR] return BXOR;
+
+[bB][rR][eE][vV] return BREV;
+[bB][pP] return BP;
+[bB][iI][tT][tT][sS][tT] return BITTST;
+[bB][iI][tT][tT][gG][lL] return BITTGL;
+[bB][iI][tT][sS][eE][tT] return BITSET;
+[bB][iI][tT][mM][uU][xX] return BITMUX;
+[bB][iI][tT][cC][lL][rR] return BITCLR;
+[bB][0-3]"."[lLhH] return parse_halfreg (&yylval.reg, T_REG_B, yytext);
+[bB][0-3] return parse_reg (&yylval.reg, T_REG_B, yytext);
+[bB] return B;
+[aA][zZ] _REG.regno = S_AZ; return STATUS_REG;
+[aA][nN] _REG.regno = S_AN; return STATUS_REG;
+[aA][cC]0_[cC][oO][pP][yY] _REG.regno = S_AC0_COPY; return STATUS_REG;
+[vV]_[cC][oO][pP][yY] _REG.regno = S_V_COPY; return STATUS_REG;
+[aA][qQ] _REG.regno = S_AQ; return STATUS_REG;
+[aA][cC]0 _REG.regno = S_AC0; return STATUS_REG;
+[aA][cC]1 _REG.regno = S_AC1; return STATUS_REG;
+[aA][vV]0 _REG.regno = S_AV0; return STATUS_REG;
+[aA][vV]0[sS] _REG.regno = S_AV0S; return STATUS_REG;
+[aA][vV]1 _REG.regno = S_AV1; return STATUS_REG;
+[aA][vV]1[sS] _REG.regno = S_AV1S; return STATUS_REG;
+[vV][sS] _REG.regno = S_VS; return STATUS_REG;
+[rR][nN][dD]_[mM][oO][dD] _REG.regno = S_RND_MOD; return STATUS_REG;
+
+
+[aA][sS][tT][aA][tT] _REG.regno = REG_ASTAT; return REG;
+[aA][sS][hH][iI][fF][tT] return ASHIFT;
+[aA][sS][lL] return ASL;
+[aA][sS][rR] return ASR;
+[aA][lL][iI][gG][nN]8 return ALIGN8;
+[aA][lL][iI][gG][nN]16 return ALIGN16;
+[aA][lL][iI][gG][nN]24 return ALIGN24;
+[aA]1"."[lL] return A_ONE_DOT_L;
+[aA]0"."[lL] return A_ZERO_DOT_L;
+[aA]1"."[hH] return A_ONE_DOT_H;
+[aA]0"."[hH] return A_ZERO_DOT_H;
+[aA][bB][sS] return ABS;
+[aA][bB][oO][rR][tT] return ABORT;
+[aA]1"."[xX] _REG.regno = REG_A1x; return REG;
+[aA]1"."[wW] _REG.regno = REG_A1w; return REG;
+[aA]1 _REG.regno = REG_A1; return REG_A_DOUBLE_ONE;
+[aA]0"."[xX] _REG.regno = REG_A0x; return REG;
+[aA]0"."[wW] _REG.regno = REG_A0w; return REG;
+[aA]0 _REG.regno = REG_A0; return REG_A_DOUBLE_ZERO;
+[Gg][Oo][Tt] return GOT;
+[Gg][Oo][Tt]"17"[Mm]"4" return GOT17M4;
+[Ff][Uu][Nn][Cc][Dd][Ee][Ss][Cc]"_"[Gg][Oo][Tt]"17"[Mm]"4" return FUNCDESC_GOT17M4;
+[Pp][Ll][Tt][Pp][Cc] return PLTPC;
+
+
+"~" return TILDA;
+"|=" return _BAR_ASSIGN;
+"|" return BAR;
+"^=" return _CARET_ASSIGN;
+"^" return CARET;
+"]" return RBRACK;
+"[" return LBRACK;
+">>>=" return _GREATER_GREATER_GREATER_THAN_ASSIGN;
+">>=" return _GREATER_GREATER_ASSIGN;
+">>>" return _GREATER_GREATER_GREATER;
+">>" return GREATER_GREATER;
+"==" return _ASSIGN_ASSIGN;
+"=" return ASSIGN;
+"<=" return _LESS_THAN_ASSIGN;
+"<<=" return _LESS_LESS_ASSIGN;
+"<<" return LESS_LESS;
+"<" return LESS_THAN;
+"(" BEGIN(FLAGS); return LPAREN;
+")" BEGIN(INITIAL); return RPAREN;
+":" return COLON;
+"/" return SLASH;
+"-=" return _MINUS_ASSIGN;
+"+|+" return _PLUS_BAR_PLUS;
+"-|+" return _MINUS_BAR_PLUS;
+"+|-" return _PLUS_BAR_MINUS;
+"-|-" return _MINUS_BAR_MINUS;
+"--" return _MINUS_MINUS;
+"-" return MINUS;
+"," return COMMA;
+"+=" return _PLUS_ASSIGN;
+"++" return _PLUS_PLUS;
+"+" return PLUS;
+"*=" return _STAR_ASSIGN;
+"*" return STAR;
+"&=" return _AMPERSAND_ASSIGN;
+"&" return AMPERSAND;
+"%" return PERCENT;
+"!" return BANG;
+";" return SEMICOLON;
+"=!" return _ASSIGN_BANG;
+"||" return DOUBLE_BAR;
+"@" return AT;
+<KEYWORD>[pP][rR][eE][fF][eE][tT][cC][hH] return PREFETCH;
+<KEYWORD>[uU][nN][lL][iI][nN][kK] return UNLINK;
+<KEYWORD>[lL][iI][nN][kK] return LINK;
+<KEYWORD>[iI][dD][lL][eE] return IDLE;
+<KEYWORD>[iI][fF][lL][uU][sS][hH] return IFLUSH;
+<KEYWORD>[fF][lL][uU][sS][hH][iI][nN][vV] return FLUSHINV;
+<KEYWORD>[fF][lL][uU][sS][hH] return FLUSH;
+([0-9]+)|(0[xX][0-9a-fA-F]+)|([bhfodBHOFD]#[0-9a-fA-F]+)|(0"."[0-9]+) {
+ yylval.value = parse_int (&yytext);
+ return NUMBER;
+ }
+[[:alpha:]\x80-\xff_$.][[:alnum:]\x80-\xff_$.]* {
+ yylval.symbol = symbol_find_or_make (yytext);
+ symbol_mark_used (yylval.symbol);
+ return SYMBOL;
+ }
+[0-9][bfBF] {
+ char *name;
+ char *ref = strdup (yytext);
+ if (ref[1] == 'b' || ref[1] == 'B')
+ {
+ name = fb_label_name ((int) (ref[0] - '0'), 0);
+ yylval.symbol = symbol_find (name);
+
+ if ((yylval.symbol != NULL)
+ && (S_IS_DEFINED (yylval.symbol)))
+ return SYMBOL;
+ as_bad ("backward reference to unknown label %d:",
+ (int) (ref[0] - '0'));
+ }
+ else if (ref[1] == 'f' || ref[1] == 'F')
+ {
+ /* Forward reference. Expect symbol to be undefined or
+ unknown. undefined: seen it before. unknown: never seen
+ it before.
+
+ Construct a local label name, then an undefined symbol.
+ Just return it as never seen before. */
+
+ name = fb_label_name ((int) (ref[0] - '0'), 1);
+ yylval.symbol = symbol_find_or_make (name);
+ /* We have no need to check symbol properties. */
+ return SYMBOL;
+ }
+ }
+[ \t\n] ;
+"/*".*"*/" ;
+. return yytext[0];
+%%
+static long parse_int (char **end)
+{
+ char fmt = '\0';
+ int not_done = 1;
+ int shiftvalue = 0;
+ char * char_bag;
+ long value = 0;
+ char *arg = *end;
+
+ while (*arg && *arg == ' ')
+ arg++;
+
+ switch (*arg)
+ {
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ fmt = 'd';
+ break;
+
+ case '0': /* Accept different formated integers hex octal and binary. */
+ {
+ char c = *++arg;
+ arg++;
+ if (c == 'x' || c == 'X') /* Hex input. */
+ fmt = 'h';
+ else if (c == 'b' || c == 'B')
+ fmt = 'b';
+ else if (c == '.')
+ fmt = 'f';
+ else
+ { /* Octal. */
+ arg--;
+ fmt = 'o';
+ }
+ break;
+ }
+
+ case 'd':
+ case 'D':
+ case 'h':
+ case 'H':
+ case 'o':
+ case 'O':
+ case 'b':
+ case 'B':
+ case 'f':
+ case 'F':
+ {
+ fmt = *arg++;
+ if (*arg == '#')
+ arg++;
+ }
+ }
+
+ switch (fmt)
+ {
+ case 'h':
+ case 'H':
+ shiftvalue = 4;
+ char_bag = "0123456789ABCDEFabcdef";
+ break;
+
+ case 'o':
+ case 'O':
+ shiftvalue = 3;
+ char_bag = "01234567";
+ break;
+
+ case 'b':
+ case 'B':
+ shiftvalue = 1;
+ char_bag = "01";
+ break;
+
+/* The assembler allows for fractional constants to be created
+ by either the 0.xxxx or the f#xxxx format
+
+ i.e. 0.5 would result in 0x4000
+
+ note .5 would result in the identifier .5.
+
+ The assembler converts to fractional format 1.15 by the simple rule:
+
+ value = (short) (finput * (1 << 15)). */
+
+ case 'f':
+ case 'F':
+ {
+ float fval = 0.0;
+ float pos = 10.0;
+ while (1)
+ {
+ int c;
+ c = *arg++;
+
+ if (c >= '0' && c <= '9')
+ {
+ float digit = (c - '0') / pos;
+ fval = fval + digit;
+ pos = pos * 10.0;
+ }
+ else
+ {
+ *--arg = c;
+ value = (short) (fval * (1 << 15));
+ break;
+ }
+ }
+ *end = arg+1;
+ return value;
+ }
+
+ case 'd':
+ case 'D':
+ default:
+ {
+ while (1)
+ {
+ char c;
+ c = *arg++;
+ if (c >= '0' && c <= '9')
+ value = (value * 10) + (c - '0');
+ else
+ {
+ /* Constants that are suffixed with k|K are multiplied by 1024
+ This suffix is only allowed on decimal constants. */
+ if (c == 'k' || c == 'K')
+ value *= 1024;
+ else
+ *--arg = c;
+ break;
+ }
+ }
+ *end = arg+1;
+ return value;
+ }
+ }
+
+ while (not_done)
+ {
+ char c;
+ c = *arg++;
+ if (c == 0 || !strchr (char_bag, c))
+ {
+ not_done = 0;
+ *--arg = c;
+ }
+ else
+ {
+ if (c >= 'a' && c <= 'z')
+ c = c - ('a' - '9') + 1;
+ else if (c >= 'A' && c <= 'Z')
+ c = c - ('A' - '9') + 1;
+
+ c -= '0';
+ value = (value << shiftvalue) + c;
+ }
+ }
+ *end = arg+1;
+ return value;
+}
+
+
+static int parse_reg (Register *r, int cl, char *rt)
+{
+ r->regno = cl | (rt[1] - '0');
+ r->flags = F_REG_NONE;
+ return REG;
+}
+
+static int parse_halfreg (Register *r, int cl, char *rt)
+{
+ r->regno = cl | (rt[1] - '0');
+
+ switch (rt[3])
+ {
+ case 'b':
+ case 'B':
+ return BYTE_DREG;
+
+ case 'l':
+ case 'L':
+ r->flags = F_REG_LOW;
+ break;
+
+ case 'h':
+ case 'H':
+ r->flags = F_REG_HIGH;
+ break;
+ }
+
+ return HALF_REG;
+}
+
+/* Our start state is KEYWORD as we have
+ command keywords such as PREFETCH. */
+
+void
+set_start_state (void)
+{
+ BEGIN KEYWORD;
+}
+
+
+#ifndef yywrap
+int
+yywrap ()
+{
+ return 1;
+}
+#endif
diff --git a/gas/config/bfin-parse.y b/gas/config/bfin-parse.y
new file mode 100644
index 0000000..435beea
--- /dev/null
+++ b/gas/config/bfin-parse.y
@@ -0,0 +1,4671 @@
+/* bfin-parse.y ADI Blackfin parser
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+%{
+
+#include "as.h"
+
+#include "bfin-aux.h" /* Opcode generating auxiliaries. */
+#include "libbfd.h"
+#include "elf/common.h"
+#include "elf/bfin.h"
+
+#define DSP32ALU(aopcde, HL, dst1, dst0, src0, src1, s, x, aop) \
+ bfin_gen_dsp32alu (HL, aopcde, aop, s, x, dst0, dst1, src0, src1)
+
+#define DSP32MAC(op1, MM, mmod, w1, P, h01, h11, h00, h10, dst, op0, src0, src1, w0) \
+ bfin_gen_dsp32mac (op1, MM, mmod, w1, P, h01, h11, h00, h10, op0, \
+ dst, src0, src1, w0)
+
+#define DSP32MULT(op1, MM, mmod, w1, P, h01, h11, h00, h10, dst, op0, src0, src1, w0) \
+ bfin_gen_dsp32mult (op1, MM, mmod, w1, P, h01, h11, h00, h10, op0, \
+ dst, src0, src1, w0)
+
+#define DSP32SHIFT(sopcde, dst0, src0, src1, sop, hls) \
+ bfin_gen_dsp32shift (sopcde, dst0, src0, src1, sop, hls)
+
+#define DSP32SHIFTIMM(sopcde, dst0, immag, src1, sop, hls) \
+ bfin_gen_dsp32shiftimm (sopcde, dst0, immag, src1, sop, hls)
+
+#define LDIMMHALF_R(reg, h, s, z, hword) \
+ bfin_gen_ldimmhalf (reg, h, s, z, hword, 1)
+
+#define LDIMMHALF_R5(reg, h, s, z, hword) \
+ bfin_gen_ldimmhalf (reg, h, s, z, hword, 2)
+
+#define LDSTIDXI(ptr, reg, w, sz, z, offset) \
+ bfin_gen_ldstidxi (ptr, reg, w, sz, z, offset)
+
+#define LDST(ptr, reg, aop, sz, z, w) \
+ bfin_gen_ldst (ptr, reg, aop, sz, z, w)
+
+#define LDSTII(ptr, reg, offset, w, op) \
+ bfin_gen_ldstii (ptr, reg, offset, w, op)
+
+#define DSPLDST(i, m, reg, aop, w) \
+ bfin_gen_dspldst (i, reg, aop, w, m)
+
+#define LDSTPMOD(ptr, reg, idx, aop, w) \
+ bfin_gen_ldstpmod (ptr, reg, aop, w, idx)
+
+#define LDSTIIFP(offset, reg, w) \
+ bfin_gen_ldstiifp (reg, offset, w)
+
+#define LOGI2OP(dst, src, opc) \
+ bfin_gen_logi2op (opc, src, dst.regno & CODE_MASK)
+
+#define ALU2OP(dst, src, opc) \
+ bfin_gen_alu2op (dst, src, opc)
+
+#define BRCC(t, b, offset) \
+ bfin_gen_brcc (t, b, offset)
+
+#define UJUMP(offset) \
+ bfin_gen_ujump (offset)
+
+#define PROGCTRL(prgfunc, poprnd) \
+ bfin_gen_progctrl (prgfunc, poprnd)
+
+#define PUSHPOPMULTIPLE(dr, pr, d, p, w) \
+ bfin_gen_pushpopmultiple (dr, pr, d, p, w)
+
+#define PUSHPOPREG(reg, w) \
+ bfin_gen_pushpopreg (reg, w)
+
+#define CALLA(addr, s) \
+ bfin_gen_calla (addr, s)
+
+#define LINKAGE(r, framesize) \
+ bfin_gen_linkage (r, framesize)
+
+#define COMPI2OPD(dst, src, op) \
+ bfin_gen_compi2opd (dst, src, op)
+
+#define COMPI2OPP(dst, src, op) \
+ bfin_gen_compi2opp (dst, src, op)
+
+#define DAGMODIK(i, op) \
+ bfin_gen_dagmodik (i, op)
+
+#define DAGMODIM(i, m, op, br) \
+ bfin_gen_dagmodim (i, m, op, br)
+
+#define COMP3OP(dst, src0, src1, opc) \
+ bfin_gen_comp3op (src0, src1, dst, opc)
+
+#define PTR2OP(dst, src, opc) \
+ bfin_gen_ptr2op (dst, src, opc)
+
+#define CCFLAG(x, y, opc, i, g) \
+ bfin_gen_ccflag (x, y, opc, i, g)
+
+#define CCMV(src, dst, t) \
+ bfin_gen_ccmv (src, dst, t)
+
+#define CACTRL(reg, a, op) \
+ bfin_gen_cactrl (reg, a, op)
+
+#define LOOPSETUP(soffset, c, rop, eoffset, reg) \
+ bfin_gen_loopsetup (soffset, c, rop, eoffset, reg)
+
+#define HL2(r1, r0) (IS_H (r1) << 1 | IS_H (r0))
+#define IS_RANGE(bits, expr, sign, mul) \
+ value_match(expr, bits, sign, mul, 1)
+#define IS_URANGE(bits, expr, sign, mul) \
+ value_match(expr, bits, sign, mul, 0)
+#define IS_CONST(expr) (expr->type == Expr_Node_Constant)
+#define IS_RELOC(expr) (expr->type != Expr_Node_Constant)
+#define IS_IMM(expr, bits) value_match (expr, bits, 0, 1, 1)
+#define IS_UIMM(expr, bits) value_match (expr, bits, 0, 1, 0)
+
+#define IS_PCREL4(expr) \
+ (value_match (expr, 4, 0, 2, 0))
+
+#define IS_LPPCREL10(expr) \
+ (value_match (expr, 10, 0, 2, 0))
+
+#define IS_PCREL10(expr) \
+ (value_match (expr, 10, 0, 2, 1))
+
+#define IS_PCREL12(expr) \
+ (value_match (expr, 12, 0, 2, 1))
+
+#define IS_PCREL24(expr) \
+ (value_match (expr, 24, 0, 2, 1))
+
+
+static int value_match (Expr_Node *, int, int, int, int);
+
+extern FILE *errorf;
+extern INSTR_T insn;
+
+static Expr_Node *binary (Expr_Op_Type, Expr_Node *, Expr_Node *);
+static Expr_Node *unary (Expr_Op_Type, Expr_Node *);
+
+static void notethat (char *, ...);
+
+char *current_inputline;
+extern char *yytext;
+int yyerror (char *);
+
+/* Used to set SRCx fields to all 1s as described in the PRM. */
+static Register reg7 = {REG_R7, 0};
+
+void error (char *format, ...)
+{
+ va_list ap;
+ static char buffer[2000];
+
+ va_start (ap, format);
+ vsprintf (buffer, format, ap);
+ va_end (ap);
+
+ as_bad ("%s", buffer);
+}
+
+int
+yyerror (char *msg)
+{
+ if (msg[0] == '\0')
+ error ("%s", msg);
+
+ else if (yytext[0] != ';')
+ error ("%s. Input text was %s.", msg, yytext);
+ else
+ error ("%s.", msg);
+
+ return -1;
+}
+
+static int
+in_range_p (Expr_Node *exp, int from, int to, unsigned int mask)
+{
+ int val = EXPR_VALUE (exp);
+ if (exp->type != Expr_Node_Constant)
+ return 0;
+ if (val < from || val > to)
+ return 0;
+ return (val & mask) == 0;
+}
+
+extern int yylex (void);
+
+#define imm3(x) EXPR_VALUE (x)
+#define imm4(x) EXPR_VALUE (x)
+#define uimm4(x) EXPR_VALUE (x)
+#define imm5(x) EXPR_VALUE (x)
+#define uimm5(x) EXPR_VALUE (x)
+#define imm6(x) EXPR_VALUE (x)
+#define imm7(x) EXPR_VALUE (x)
+#define uimm8(x) EXPR_VALUE (x)
+#define imm16(x) EXPR_VALUE (x)
+#define uimm16s4(x) ((EXPR_VALUE (x)) >> 2)
+#define uimm16(x) EXPR_VALUE (x)
+
+/* Return true if a value is inside a range. */
+#define IN_RANGE(x, low, high) \
+ (((EXPR_VALUE(x)) >= (low)) && (EXPR_VALUE(x)) <= ((high)))
+
+/* Auxiliary functions. */
+
+static int
+valid_dreg_pair (Register *reg1, Expr_Node *reg2)
+{
+ if (!IS_DREG (*reg1))
+ {
+ yyerror ("Dregs expected");
+ return 0;
+ }
+
+ if (reg1->regno != 1 && reg1->regno != 3)
+ {
+ yyerror ("Bad register pair");
+ return 0;
+ }
+
+ if (imm7 (reg2) != reg1->regno - 1)
+ {
+ yyerror ("Bad register pair");
+ return 0;
+ }
+
+ reg1->regno--;
+ return 1;
+}
+
+static int
+check_multiply_halfregs (Macfunc *aa, Macfunc *ab)
+{
+ if ((!REG_EQUAL (aa->s0, ab->s0) && !REG_EQUAL (aa->s0, ab->s1))
+ || (!REG_EQUAL (aa->s1, ab->s1) && !REG_EQUAL (aa->s1, ab->s0)))
+ return yyerror ("Source multiplication register mismatch");
+
+ return 0;
+}
+
+
+/* Check mac option. */
+
+static int
+check_macfunc_option (Macfunc *a, Opt_mode *opt)
+{
+ /* Default option is always valid. */
+ if (opt->mod == 0)
+ return 0;
+
+ if ((a->w == 1 && a->P == 1
+ && opt->mod != M_FU && opt->mod != M_IS && opt->mod != M_IU
+ && opt->mod != M_S2RND && opt->mod != M_ISS2)
+ || (a->w == 1 && a->P == 0
+ && opt->mod != M_FU && opt->mod != M_IS && opt->mod != M_IU
+ && opt->mod != M_T && opt->mod != M_TFU && opt->mod != M_S2RND
+ && opt->mod != M_ISS2 && opt->mod != M_IH)
+ || (a->w == 0 && a->P == 0
+ && opt->mod != M_FU && opt->mod != M_IS && opt->mod != M_W32))
+ return -1;
+
+ return 0;
+}
+
+/* Check (vector) mac funcs and ops. */
+
+static int
+check_macfuncs (Macfunc *aa, Opt_mode *opa,
+ Macfunc *ab, Opt_mode *opb)
+{
+ /* Variables for swapping. */
+ Macfunc mtmp;
+ Opt_mode otmp;
+
+ /* The option mode should be put at the end of the second instruction
+ of the vector except M, which should follow MAC1 instruction. */
+ if (opa->mod != 0)
+ return yyerror ("Bad opt mode");
+
+ /* If a0macfunc comes before a1macfunc, swap them. */
+
+ if (aa->n == 0)
+ {
+ /* (M) is not allowed here. */
+ if (opa->MM != 0)
+ return yyerror ("(M) not allowed with A0MAC");
+ if (ab->n != 1)
+ return yyerror ("Vector AxMACs can't be same");
+
+ mtmp = *aa; *aa = *ab; *ab = mtmp;
+ otmp = *opa; *opa = *opb; *opb = otmp;
+ }
+ else
+ {
+ if (opb->MM != 0)
+ return yyerror ("(M) not allowed with A0MAC");
+ if (ab->n != 0)
+ return yyerror ("Vector AxMACs can't be same");
+ }
+
+ /* If both ops are one of 0, 1, or 2, we have multiply_halfregs in both
+ assignment_or_macfuncs. */
+ if ((aa->op == 0 || aa->op == 1 || aa->op == 2)
+ && (ab->op == 0 || ab->op == 1 || ab->op == 2))
+ {
+ if (check_multiply_halfregs (aa, ab) < 0)
+ return -1;
+ }
+ else
+ {
+ /* Only one of the assign_macfuncs has a half reg multiply
+ Evil trick: Just 'OR' their source register codes:
+ We can do that, because we know they were initialized to 0
+ in the rules that don't use multiply_halfregs. */
+ aa->s0.regno |= (ab->s0.regno & CODE_MASK);
+ aa->s1.regno |= (ab->s1.regno & CODE_MASK);
+ }
+
+ if (aa->w == ab->w && aa->P != ab->P)
+ return yyerror ("Destination Dreg sizes (full or half) must match");
+
+ if (aa->w && ab->w)
+ {
+ if (aa->P && (aa->dst.regno - ab->dst.regno) != 1)
+ return yyerror ("Destination Dregs (full) must differ by one");
+ if (!aa->P && aa->dst.regno != ab->dst.regno)
+ return yyerror ("Destination Dregs (half) must match");
+ }
+
+ /* Make sure mod flags get ORed, too. */
+ opb->mod |= opa->mod;
+
+ /* Check option. */
+ if (check_macfunc_option (aa, opb) < 0
+ && check_macfunc_option (ab, opb) < 0)
+ return yyerror ("bad option");
+
+ /* Make sure first macfunc has got both P flags ORed. */
+ aa->P |= ab->P;
+
+ return 0;
+}
+
+
+static int
+is_group1 (INSTR_T x)
+{
+ /* Group1 is dpsLDST, LDSTpmod, LDST, LDSTiiFP, LDSTii. */
+ if ((x->value & 0xc000) == 0x8000 || (x->value == 0x0000))
+ return 1;
+
+ return 0;
+}
+
+static int
+is_group2 (INSTR_T x)
+{
+ if ((((x->value & 0xfc00) == 0x9c00) /* dspLDST. */
+ && !((x->value & 0xfde0) == 0x9c60) /* dagMODim. */
+ && !((x->value & 0xfde0) == 0x9ce0) /* dagMODim with bit rev. */
+ && !((x->value & 0xfde0) == 0x9d60)) /* pick dagMODik. */
+ || (x->value == 0x0000))
+ return 1;
+ return 0;
+}
+
+static int
+is_store (INSTR_T x)
+{
+ if (!x)
+ return 0;
+
+ if ((x->value & 0xf000) == 0x8000)
+ {
+ int aop = ((x->value >> 9) & 0x3);
+ int w = ((x->value >> 11) & 0x1);
+ if (!w || aop == 3)
+ return 0;
+ return 1;
+ }
+
+ if (((x->value & 0xFF60) == 0x9E60) || /* dagMODim_0 */
+ ((x->value & 0xFFF0) == 0x9F60)) /* dagMODik_0 */
+ return 0;
+
+ /* decode_dspLDST_0 */
+ if ((x->value & 0xFC00) == 0x9C00)
+ {
+ int w = ((x->value >> 9) & 0x1);
+ if (w)
+ return 1;
+ }
+
+ return 0;
+}
+
+static INSTR_T
+gen_multi_instr_1 (INSTR_T dsp32, INSTR_T dsp16_grp1, INSTR_T dsp16_grp2)
+{
+ int mask1 = dsp32 ? insn_regmask (dsp32->value, dsp32->next->value) : 0;
+ int mask2 = dsp16_grp1 ? insn_regmask (dsp16_grp1->value, 0) : 0;
+ int mask3 = dsp16_grp2 ? insn_regmask (dsp16_grp2->value, 0) : 0;
+
+ if ((mask1 & mask2) || (mask1 & mask3) || (mask2 & mask3))
+ yyerror ("resource conflict in multi-issue instruction");
+
+ /* Anomaly 05000074 */
+ if (ENABLE_AC_05000074
+ && dsp32 != NULL && dsp16_grp1 != NULL
+ && (dsp32->value & 0xf780) == 0xc680
+ && ((dsp16_grp1->value & 0xfe40) == 0x9240
+ || (dsp16_grp1->value & 0xfe08) == 0xba08
+ || (dsp16_grp1->value & 0xfc00) == 0xbc00))
+ yyerror ("anomaly 05000074 - Multi-Issue Instruction with \
+dsp32shiftimm in slot1 and P-reg Store in slot2 Not Supported");
+
+ if (is_store (dsp16_grp1) && is_store (dsp16_grp2))
+ yyerror ("Only one instruction in multi-issue instruction can be a store");
+
+ return bfin_gen_multi_instr (dsp32, dsp16_grp1, dsp16_grp2);
+}
+
+%}
+
+%union {
+ INSTR_T instr;
+ Expr_Node *expr;
+ SYMBOL_T symbol;
+ long value;
+ Register reg;
+ Macfunc macfunc;
+ struct { int r0; int s0; int x0; int aop; } modcodes;
+ struct { int r0; } r0;
+ Opt_mode mod;
+}
+
+
+/* Tokens. */
+
+/* Vector Specific. */
+%token BYTEOP16P BYTEOP16M
+%token BYTEOP1P BYTEOP2P BYTEOP3P
+%token BYTEUNPACK BYTEPACK
+%token PACK
+%token SAA
+%token ALIGN8 ALIGN16 ALIGN24
+%token VIT_MAX
+%token EXTRACT DEPOSIT EXPADJ SEARCH
+%token ONES SIGN SIGNBITS
+
+/* Stack. */
+%token LINK UNLINK
+
+/* Registers. */
+%token REG
+%token PC
+%token CCREG BYTE_DREG
+%token REG_A_DOUBLE_ZERO REG_A_DOUBLE_ONE
+%token A_ZERO_DOT_L A_ZERO_DOT_H A_ONE_DOT_L A_ONE_DOT_H
+%token HALF_REG
+
+/* Progctrl. */
+%token NOP
+%token RTI RTS RTX RTN RTE
+%token HLT IDLE
+%token STI CLI
+%token CSYNC SSYNC
+%token EMUEXCPT
+%token RAISE EXCPT
+%token LSETUP
+%token LOOP
+%token LOOP_BEGIN
+%token LOOP_END
+%token DISALGNEXCPT
+%token JUMP JUMP_DOT_S JUMP_DOT_L
+%token CALL
+
+/* Emulator only. */
+%token ABORT
+
+/* Operators. */
+%token NOT TILDA BANG
+%token AMPERSAND BAR
+%token PERCENT
+%token CARET
+%token BXOR
+
+%token MINUS PLUS STAR SLASH
+%token NEG
+%token MIN MAX ABS
+%token DOUBLE_BAR
+%token _PLUS_BAR_PLUS _PLUS_BAR_MINUS _MINUS_BAR_PLUS _MINUS_BAR_MINUS
+%token _MINUS_MINUS _PLUS_PLUS
+
+/* Shift/rotate ops. */
+%token SHIFT LSHIFT ASHIFT BXORSHIFT
+%token _GREATER_GREATER_GREATER_THAN_ASSIGN
+%token ROT
+%token LESS_LESS GREATER_GREATER
+%token _GREATER_GREATER_GREATER
+%token _LESS_LESS_ASSIGN _GREATER_GREATER_ASSIGN
+%token DIVS DIVQ
+
+/* In place operators. */
+%token ASSIGN _STAR_ASSIGN
+%token _BAR_ASSIGN _CARET_ASSIGN _AMPERSAND_ASSIGN
+%token _MINUS_ASSIGN _PLUS_ASSIGN
+
+/* Assignments, comparisons. */
+%token _ASSIGN_BANG _LESS_THAN_ASSIGN _ASSIGN_ASSIGN
+%token GE LT LE GT
+%token LESS_THAN
+
+/* Cache. */
+%token FLUSHINV FLUSH
+%token IFLUSH PREFETCH
+
+/* Misc. */
+%token PRNT
+%token OUTC
+%token WHATREG
+%token TESTSET
+
+/* Modifiers. */
+%token ASL ASR
+%token B W
+%token NS S CO SCO
+%token TH TL
+%token BP
+%token BREV
+%token X Z
+%token M MMOD
+%token R RND RNDL RNDH RND12 RND20
+%token V
+%token LO HI
+
+/* Bit ops. */
+%token BITTGL BITCLR BITSET BITTST BITMUX
+
+/* Debug. */
+%token DBGAL DBGAH DBGHALT DBG DBGA DBGCMPLX
+
+/* Semantic auxiliaries. */
+
+%token IF COMMA BY
+%token COLON SEMICOLON
+%token RPAREN LPAREN LBRACK RBRACK
+%token STATUS_REG
+%token MNOP
+%token SYMBOL NUMBER
+%token GOT GOT17M4 FUNCDESC_GOT17M4
+%token AT PLTPC
+
+/* Types. */
+%type <instr> asm
+%type <value> MMOD
+%type <mod> opt_mode
+
+%type <value> NUMBER
+%type <r0> aligndir
+%type <modcodes> byteop_mod
+%type <reg> a_assign
+%type <reg> a_plusassign
+%type <reg> a_minusassign
+%type <macfunc> multiply_halfregs
+%type <macfunc> assign_macfunc
+%type <macfunc> a_macfunc
+%type <expr> expr_1
+%type <instr> asm_1
+%type <r0> vmod
+%type <modcodes> vsmod
+%type <modcodes> ccstat
+%type <r0> cc_op
+%type <reg> CCREG
+%type <reg> reg_with_postinc
+%type <reg> reg_with_predec
+
+%type <r0> searchmod
+%type <expr> symbol
+%type <symbol> SYMBOL
+%type <expr> eterm
+%type <reg> REG
+%type <reg> BYTE_DREG
+%type <reg> REG_A_DOUBLE_ZERO
+%type <reg> REG_A_DOUBLE_ONE
+%type <reg> REG_A
+%type <reg> STATUS_REG
+%type <expr> expr
+%type <r0> xpmod
+%type <r0> xpmod1
+%type <modcodes> smod
+%type <modcodes> b3_op
+%type <modcodes> rnd_op
+%type <modcodes> post_op
+%type <reg> HALF_REG
+%type <r0> iu_or_nothing
+%type <r0> plus_minus
+%type <r0> asr_asl
+%type <r0> asr_asl_0
+%type <modcodes> sco
+%type <modcodes> amod0
+%type <modcodes> amod1
+%type <modcodes> amod2
+%type <r0> op_bar_op
+%type <r0> w32_or_nothing
+%type <r0> c_align
+%type <r0> min_max
+%type <expr> got
+%type <expr> got_or_expr
+%type <expr> pltpc
+%type <value> any_gotrel GOT GOT17M4 FUNCDESC_GOT17M4
+
+/* Precedence rules. */
+%left BAR
+%left CARET
+%left AMPERSAND
+%left LESS_LESS GREATER_GREATER
+%left PLUS MINUS
+%left STAR SLASH PERCENT
+
+%right ASSIGN
+
+%right TILDA BANG
+%start statement
+%%
+statement:
+ | asm
+ {
+ insn = $1;
+ if (insn == (INSTR_T) 0)
+ return NO_INSN_GENERATED;
+ else if (insn == (INSTR_T) - 1)
+ return SEMANTIC_ERROR;
+ else
+ return INSN_GENERATED;
+ }
+ ;
+
+asm: asm_1 SEMICOLON
+ /* Parallel instructions. */
+ | asm_1 DOUBLE_BAR asm_1 DOUBLE_BAR asm_1 SEMICOLON
+ {
+ if (($1->value & 0xf800) == 0xc000)
+ {
+ if (is_group1 ($3) && is_group2 ($5))
+ $$ = gen_multi_instr_1 ($1, $3, $5);
+ else if (is_group2 ($3) && is_group1 ($5))
+ $$ = gen_multi_instr_1 ($1, $5, $3);
+ else
+ return yyerror ("Wrong 16 bit instructions groups, slot 2 and slot 3 must be 16-bit instrution group");
+ }
+ else if (($3->value & 0xf800) == 0xc000)
+ {
+ if (is_group1 ($1) && is_group2 ($5))
+ $$ = gen_multi_instr_1 ($3, $1, $5);
+ else if (is_group2 ($1) && is_group1 ($5))
+ $$ = gen_multi_instr_1 ($3, $5, $1);
+ else
+ return yyerror ("Wrong 16 bit instructions groups, slot 1 and slot 3 must be 16-bit instrution group");
+ }
+ else if (($5->value & 0xf800) == 0xc000)
+ {
+ if (is_group1 ($1) && is_group2 ($3))
+ $$ = gen_multi_instr_1 ($5, $1, $3);
+ else if (is_group2 ($1) && is_group1 ($3))
+ $$ = gen_multi_instr_1 ($5, $3, $1);
+ else
+ return yyerror ("Wrong 16 bit instructions groups, slot 1 and slot 2 must be 16-bit instrution group");
+ }
+ else
+ error ("\nIllegal Multi Issue Construct, at least any one of the slot must be DSP32 instruction group\n");
+ }
+
+ | asm_1 DOUBLE_BAR asm_1 SEMICOLON
+ {
+ if (($1->value & 0xf800) == 0xc000)
+ {
+ if (is_group1 ($3))
+ $$ = gen_multi_instr_1 ($1, $3, 0);
+ else if (is_group2 ($3))
+ $$ = gen_multi_instr_1 ($1, 0, $3);
+ else
+ return yyerror ("Wrong 16 bit instructions groups, slot 2 must be the 16-bit instruction group");
+ }
+ else if (($3->value & 0xf800) == 0xc000)
+ {
+ if (is_group1 ($1))
+ $$ = gen_multi_instr_1 ($3, $1, 0);
+ else if (is_group2 ($1))
+ $$ = gen_multi_instr_1 ($3, 0, $1);
+ else
+ return yyerror ("Wrong 16 bit instructions groups, slot 1 must be the 16-bit instruction group");
+ }
+ else if (is_group1 ($1) && is_group2 ($3))
+ $$ = gen_multi_instr_1 (0, $1, $3);
+ else if (is_group2 ($1) && is_group1 ($3))
+ $$ = gen_multi_instr_1 (0, $3, $1);
+ else
+ return yyerror ("Wrong 16 bit instructions groups, slot 1 and slot 2 must be the 16-bit instruction group");
+ }
+ | error
+ {
+ $$ = 0;
+ yyerror ("");
+ yyerrok;
+ }
+ ;
+
+/* DSPMAC. */
+
+asm_1:
+ MNOP
+ {
+ $$ = DSP32MAC (3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0);
+ }
+ | assign_macfunc opt_mode
+ {
+ int op0, op1;
+ int w0 = 0, w1 = 0;
+ int h00, h10, h01, h11;
+
+ if (check_macfunc_option (&$1, &$2) < 0)
+ return yyerror ("bad option");
+
+ if ($1.n == 0)
+ {
+ if ($2.MM)
+ return yyerror ("(m) not allowed with a0 unit");
+ op1 = 3;
+ op0 = $1.op;
+ w1 = 0;
+ w0 = $1.w;
+ h00 = IS_H ($1.s0);
+ h10 = IS_H ($1.s1);
+ h01 = h11 = 0;
+ }
+ else
+ {
+ op1 = $1.op;
+ op0 = 3;
+ w1 = $1.w;
+ w0 = 0;
+ h00 = h10 = 0;
+ h01 = IS_H ($1.s0);
+ h11 = IS_H ($1.s1);
+ }
+ $$ = DSP32MAC (op1, $2.MM, $2.mod, w1, $1.P, h01, h11, h00, h10,
+ &$1.dst, op0, &$1.s0, &$1.s1, w0);
+ }
+
+
+/* VECTOR MACs. */
+
+ | assign_macfunc opt_mode COMMA assign_macfunc opt_mode
+ {
+ Register *dst;
+
+ if (check_macfuncs (&$1, &$2, &$4, &$5) < 0)
+ return -1;
+ notethat ("assign_macfunc (.), assign_macfunc (.)\n");
+
+ if ($1.w)
+ dst = &$1.dst;
+ else
+ dst = &$4.dst;
+
+ $$ = DSP32MAC ($1.op, $2.MM, $5.mod, $1.w, $1.P,
+ IS_H ($1.s0), IS_H ($1.s1), IS_H ($4.s0), IS_H ($4.s1),
+ dst, $4.op, &$1.s0, &$1.s1, $4.w);
+ }
+
+/* DSPALU. */
+
+ | DISALGNEXCPT
+ {
+ notethat ("dsp32alu: DISALGNEXCPT\n");
+ $$ = DSP32ALU (18, 0, 0, 0, 0, 0, 0, 0, 3);
+ }
+ | REG ASSIGN LPAREN a_plusassign REG_A RPAREN
+ {
+ if (IS_DREG ($1) && !IS_A1 ($4) && IS_A1 ($5))
+ {
+ notethat ("dsp32alu: dregs = ( A0 += A1 )\n");
+ $$ = DSP32ALU (11, 0, 0, &$1, &reg7, &reg7, 0, 0, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ | HALF_REG ASSIGN LPAREN a_plusassign REG_A RPAREN
+ {
+ if (!IS_A1 ($4) && IS_A1 ($5))
+ {
+ notethat ("dsp32alu: dregs_half = ( A0 += A1 )\n");
+ $$ = DSP32ALU (11, IS_H ($1), 0, &$1, &reg7, &reg7, 0, 0, 1);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ | A_ZERO_DOT_H ASSIGN HALF_REG
+ {
+ notethat ("dsp32alu: A_ZERO_DOT_H = dregs_hi\n");
+ $$ = DSP32ALU (9, IS_H ($3), 0, 0, &$3, 0, 0, 0, 0);
+ }
+ | A_ONE_DOT_H ASSIGN HALF_REG
+ {
+ notethat ("dsp32alu: A_ZERO_DOT_H = dregs_hi\n");
+ $$ = DSP32ALU (9, IS_H ($3), 0, 0, &$3, 0, 0, 0, 2);
+ }
+ | LPAREN REG COMMA REG RPAREN ASSIGN BYTEOP16P LPAREN REG
+ COLON expr COMMA REG COLON expr RPAREN aligndir
+ {
+ if (!IS_DREG ($2) || !IS_DREG ($4))
+ return yyerror ("Dregs expected");
+ else if (REG_SAME ($2, $4))
+ return yyerror ("Illegal dest register combination");
+ else if (!valid_dreg_pair (&$9, $11))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$13, $15))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: (dregs , dregs ) = BYTEOP16P (dregs_pair , dregs_pair ) (aligndir)\n");
+ $$ = DSP32ALU (21, 0, &$2, &$4, &$9, &$13, $17.r0, 0, 0);
+ }
+ }
+
+ | LPAREN REG COMMA REG RPAREN ASSIGN BYTEOP16M LPAREN REG COLON expr COMMA
+ REG COLON expr RPAREN aligndir
+ {
+ if (!IS_DREG ($2) || !IS_DREG ($4))
+ return yyerror ("Dregs expected");
+ else if (REG_SAME ($2, $4))
+ return yyerror ("Illegal dest register combination");
+ else if (!valid_dreg_pair (&$9, $11))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$13, $15))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: (dregs , dregs ) = BYTEOP16M (dregs_pair , dregs_pair ) (aligndir)\n");
+ $$ = DSP32ALU (21, 0, &$2, &$4, &$9, &$13, $17.r0, 0, 1);
+ }
+ }
+
+ | LPAREN REG COMMA REG RPAREN ASSIGN BYTEUNPACK REG COLON expr aligndir
+ {
+ if (!IS_DREG ($2) || !IS_DREG ($4))
+ return yyerror ("Dregs expected");
+ else if (REG_SAME ($2, $4))
+ return yyerror ("Illegal dest register combination");
+ else if (!valid_dreg_pair (&$8, $10))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: (dregs , dregs ) = BYTEUNPACK dregs_pair (aligndir)\n");
+ $$ = DSP32ALU (24, 0, &$2, &$4, &$8, 0, $11.r0, 0, 1);
+ }
+ }
+ | LPAREN REG COMMA REG RPAREN ASSIGN SEARCH REG LPAREN searchmod RPAREN
+ {
+ if (REG_SAME ($2, $4))
+ return yyerror ("Illegal dest register combination");
+
+ if (IS_DREG ($2) && IS_DREG ($4) && IS_DREG ($8))
+ {
+ notethat ("dsp32alu: (dregs , dregs ) = SEARCH dregs (searchmod)\n");
+ $$ = DSP32ALU (13, 0, &$2, &$4, &$8, 0, 0, 0, $10.r0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ | REG ASSIGN A_ONE_DOT_L PLUS A_ONE_DOT_H COMMA
+ REG ASSIGN A_ZERO_DOT_L PLUS A_ZERO_DOT_H
+ {
+ if (REG_SAME ($1, $7))
+ return yyerror ("Illegal dest register combination");
+
+ if (IS_DREG ($1) && IS_DREG ($7))
+ {
+ notethat ("dsp32alu: dregs = A1.l + A1.h, dregs = A0.l + A0.h \n");
+ $$ = DSP32ALU (12, 0, &$1, &$7, &reg7, &reg7, 0, 0, 1);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+
+ | REG ASSIGN REG_A PLUS REG_A COMMA REG ASSIGN REG_A MINUS REG_A amod1
+ {
+ if (REG_SAME ($1, $7))
+ return yyerror ("Resource conflict in dest reg");
+
+ if (IS_DREG ($1) && IS_DREG ($7) && !REG_SAME ($3, $5)
+ && IS_A1 ($9) && !IS_A1 ($11))
+ {
+ notethat ("dsp32alu: dregs = A1 + A0 , dregs = A1 - A0 (amod1)\n");
+ $$ = DSP32ALU (17, 0, &$1, &$7, &reg7, &reg7, $12.s0, $12.x0, 0);
+
+ }
+ else if (IS_DREG ($1) && IS_DREG ($7) && !REG_SAME ($3, $5)
+ && !IS_A1 ($9) && IS_A1 ($11))
+ {
+ notethat ("dsp32alu: dregs = A0 + A1 , dregs = A0 - A1 (amod1)\n");
+ $$ = DSP32ALU (17, 0, &$1, &$7, &reg7, &reg7, $12.s0, $12.x0, 1);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN REG plus_minus REG COMMA REG ASSIGN REG plus_minus REG amod1
+ {
+ if ($4.r0 == $10.r0)
+ return yyerror ("Operators must differ");
+
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_DREG ($5)
+ && REG_SAME ($3, $9) && REG_SAME ($5, $11))
+ {
+ notethat ("dsp32alu: dregs = dregs + dregs,"
+ "dregs = dregs - dregs (amod1)\n");
+ $$ = DSP32ALU (4, 0, &$1, &$7, &$3, &$5, $12.s0, $12.x0, 2);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+/* Bar Operations. */
+
+ | REG ASSIGN REG op_bar_op REG COMMA REG ASSIGN REG op_bar_op REG amod2
+ {
+ if (!REG_SAME ($3, $9) || !REG_SAME ($5, $11))
+ return yyerror ("Differing source registers");
+
+ if (!IS_DREG ($1) || !IS_DREG ($3) || !IS_DREG ($5) || !IS_DREG ($7))
+ return yyerror ("Dregs expected");
+
+ if (REG_SAME ($1, $7))
+ return yyerror ("Resource conflict in dest reg");
+
+ if ($4.r0 == 1 && $10.r0 == 2)
+ {
+ notethat ("dsp32alu: dregs = dregs .|. dregs , dregs = dregs .|. dregs (amod2)\n");
+ $$ = DSP32ALU (1, 1, &$1, &$7, &$3, &$5, $12.s0, $12.x0, $12.r0);
+ }
+ else if ($4.r0 == 0 && $10.r0 == 3)
+ {
+ notethat ("dsp32alu: dregs = dregs .|. dregs , dregs = dregs .|. dregs (amod2)\n");
+ $$ = DSP32ALU (1, 0, &$1, &$7, &$3, &$5, $12.s0, $12.x0, $12.r0);
+ }
+ else
+ return yyerror ("Bar operand mismatch");
+ }
+
+ | REG ASSIGN ABS REG vmod
+ {
+ int op;
+
+ if (IS_DREG ($1) && IS_DREG ($4))
+ {
+ if ($5.r0)
+ {
+ notethat ("dsp32alu: dregs = ABS dregs (v)\n");
+ op = 6;
+ }
+ else
+ {
+ /* Vector version of ABS. */
+ notethat ("dsp32alu: dregs = ABS dregs\n");
+ op = 7;
+ }
+ $$ = DSP32ALU (op, 0, 0, &$1, &$4, 0, 0, 0, 2);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ | a_assign ABS REG_A
+ {
+ notethat ("dsp32alu: Ax = ABS Ax\n");
+ $$ = DSP32ALU (16, IS_A1 ($1), 0, 0, &reg7, &reg7, 0, 0, IS_A1 ($3));
+ }
+ | A_ZERO_DOT_L ASSIGN HALF_REG
+ {
+ if (IS_DREG_L ($3))
+ {
+ notethat ("dsp32alu: A0.l = reg_half\n");
+ $$ = DSP32ALU (9, IS_H ($3), 0, 0, &$3, 0, 0, 0, 0);
+ }
+ else
+ return yyerror ("A0.l = Rx.l expected");
+ }
+ | A_ONE_DOT_L ASSIGN HALF_REG
+ {
+ if (IS_DREG_L ($3))
+ {
+ notethat ("dsp32alu: A1.l = reg_half\n");
+ $$ = DSP32ALU (9, IS_H ($3), 0, 0, &$3, 0, 0, 0, 2);
+ }
+ else
+ return yyerror ("A1.l = Rx.l expected");
+ }
+
+ | REG ASSIGN c_align LPAREN REG COMMA REG RPAREN
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ notethat ("dsp32shift: dregs = ALIGN8 (dregs , dregs )\n");
+ $$ = DSP32SHIFT (13, &$1, &$7, &$5, $3.r0, 0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG ASSIGN BYTEOP1P LPAREN REG COLON expr COMMA REG COLON expr RPAREN byteop_mod
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dregs expected");
+ else if (!valid_dreg_pair (&$5, $7))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$9, $11))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: dregs = BYTEOP1P (dregs_pair , dregs_pair ) (T)\n");
+ $$ = DSP32ALU (20, 0, 0, &$1, &$5, &$9, $13.s0, 0, $13.r0);
+ }
+ }
+ | REG ASSIGN BYTEOP1P LPAREN REG COLON expr COMMA REG COLON expr RPAREN
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dregs expected");
+ else if (!valid_dreg_pair (&$5, $7))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$9, $11))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: dregs = BYTEOP1P (dregs_pair , dregs_pair ) (T)\n");
+ $$ = DSP32ALU (20, 0, 0, &$1, &$5, &$9, 0, 0, 0);
+ }
+ }
+
+ | REG ASSIGN BYTEOP2P LPAREN REG COLON expr COMMA REG COLON expr RPAREN
+ rnd_op
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dregs expected");
+ else if (!valid_dreg_pair (&$5, $7))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$9, $11))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: dregs = BYTEOP2P (dregs_pair , dregs_pair ) (rnd_op)\n");
+ $$ = DSP32ALU (22, $13.r0, 0, &$1, &$5, &$9, $13.s0, $13.x0, $13.aop);
+ }
+ }
+
+ | REG ASSIGN BYTEOP3P LPAREN REG COLON expr COMMA REG COLON expr RPAREN
+ b3_op
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dregs expected");
+ else if (!valid_dreg_pair (&$5, $7))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$9, $11))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: dregs = BYTEOP3P (dregs_pair , dregs_pair ) (b3_op)\n");
+ $$ = DSP32ALU (23, $13.x0, 0, &$1, &$5, &$9, $13.s0, 0, 0);
+ }
+ }
+
+ | REG ASSIGN BYTEPACK LPAREN REG COMMA REG RPAREN
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ notethat ("dsp32alu: dregs = BYTEPACK (dregs , dregs )\n");
+ $$ = DSP32ALU (24, 0, 0, &$1, &$5, &$7, 0, 0, 0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | HALF_REG ASSIGN HALF_REG ASSIGN SIGN LPAREN HALF_REG RPAREN STAR
+ HALF_REG PLUS SIGN LPAREN HALF_REG RPAREN STAR HALF_REG
+ {
+ if (IS_HCOMPL ($1, $3) && IS_HCOMPL ($7, $14) && IS_HCOMPL ($10, $17))
+ {
+ notethat ("dsp32alu: dregs_hi = dregs_lo ="
+ "SIGN (dregs_hi) * dregs_hi + "
+ "SIGN (dregs_lo) * dregs_lo \n");
+
+ $$ = DSP32ALU (12, 0, 0, &$1, &$7, &$10, 0, 0, 0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ | REG ASSIGN REG plus_minus REG amod1
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_DREG ($5))
+ {
+ if ($6.aop == 0)
+ {
+ /* No saturation flag specified, generate the 16 bit variant. */
+ notethat ("COMP3op: dregs = dregs +- dregs\n");
+ $$ = COMP3OP (&$1, &$3, &$5, $4.r0);
+ }
+ else
+ {
+ /* Saturation flag specified, generate the 32 bit variant. */
+ notethat ("dsp32alu: dregs = dregs +- dregs (amod1)\n");
+ $$ = DSP32ALU (4, 0, 0, &$1, &$3, &$5, $6.s0, $6.x0, $4.r0);
+ }
+ }
+ else
+ if (IS_PREG ($1) && IS_PREG ($3) && IS_PREG ($5) && $4.r0 == 0)
+ {
+ notethat ("COMP3op: pregs = pregs + pregs\n");
+ $$ = COMP3OP (&$1, &$3, &$5, 5);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ | REG ASSIGN min_max LPAREN REG COMMA REG RPAREN vmod
+ {
+ int op;
+
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ if ($9.r0)
+ op = 6;
+ else
+ op = 7;
+
+ notethat ("dsp32alu: dregs = {MIN|MAX} (dregs, dregs)\n");
+ $$ = DSP32ALU (op, 0, 0, &$1, &$5, &$7, 0, 0, $3.r0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | a_assign MINUS REG_A
+ {
+ notethat ("dsp32alu: Ax = - Ax\n");
+ $$ = DSP32ALU (14, IS_A1 ($1), 0, 0, &reg7, &reg7, 0, 0, IS_A1 ($3));
+ }
+ | HALF_REG ASSIGN HALF_REG plus_minus HALF_REG amod1
+ {
+ notethat ("dsp32alu: dregs_lo = dregs_lo +- dregs_lo (amod1)\n");
+ $$ = DSP32ALU (2 | $4.r0, IS_H ($1), 0, &$1, &$3, &$5,
+ $6.s0, $6.x0, HL2 ($3, $5));
+ }
+ | a_assign a_assign expr
+ {
+ if (EXPR_VALUE ($3) == 0 && !REG_SAME ($1, $2))
+ {
+ notethat ("dsp32alu: A1 = A0 = 0\n");
+ $$ = DSP32ALU (8, 0, 0, 0, &reg7, &reg7, 0, 0, 2);
+ }
+ else
+ return yyerror ("Bad value, 0 expected");
+ }
+
+ /* Saturating. */
+ | a_assign REG_A LPAREN S RPAREN
+ {
+ if (REG_SAME ($1, $2))
+ {
+ notethat ("dsp32alu: Ax = Ax (S)\n");
+ $$ = DSP32ALU (8, 0, 0, 0, &reg7, &reg7, 1, 0, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Registers must be equal");
+ }
+
+ | HALF_REG ASSIGN REG LPAREN RND RPAREN
+ {
+ if (IS_DREG ($3))
+ {
+ notethat ("dsp32alu: dregs_half = dregs (RND)\n");
+ $$ = DSP32ALU (12, IS_H ($1), 0, &$1, &$3, 0, 0, 0, 3);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | HALF_REG ASSIGN REG plus_minus REG LPAREN RND12 RPAREN
+ {
+ if (IS_DREG ($3) && IS_DREG ($5))
+ {
+ notethat ("dsp32alu: dregs_half = dregs (+-) dregs (RND12)\n");
+ $$ = DSP32ALU (5, IS_H ($1), 0, &$1, &$3, &$5, 0, 0, $4.r0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | HALF_REG ASSIGN REG plus_minus REG LPAREN RND20 RPAREN
+ {
+ if (IS_DREG ($3) && IS_DREG ($5))
+ {
+ notethat ("dsp32alu: dregs_half = dregs -+ dregs (RND20)\n");
+ $$ = DSP32ALU (5, IS_H ($1), 0, &$1, &$3, &$5, 0, 1, $4.r0 | 2);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | a_assign REG_A
+ {
+ if (!REG_SAME ($1, $2))
+ {
+ notethat ("dsp32alu: An = Am\n");
+ $$ = DSP32ALU (8, 0, 0, 0, &reg7, &reg7, IS_A1 ($1), 0, 3);
+ }
+ else
+ return yyerror ("Accu reg arguments must differ");
+ }
+
+ | a_assign REG
+ {
+ if (IS_DREG ($2))
+ {
+ notethat ("dsp32alu: An = dregs\n");
+ $$ = DSP32ALU (9, 0, 0, 0, &$2, 0, 1, 0, IS_A1 ($1) << 1);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG ASSIGN HALF_REG xpmod
+ {
+ if (!IS_H ($3))
+ {
+ if ($1.regno == REG_A0x && IS_DREG ($3))
+ {
+ notethat ("dsp32alu: A0.x = dregs_lo\n");
+ $$ = DSP32ALU (9, 0, 0, 0, &$3, 0, 0, 0, 1);
+ }
+ else if ($1.regno == REG_A1x && IS_DREG ($3))
+ {
+ notethat ("dsp32alu: A1.x = dregs_lo\n");
+ $$ = DSP32ALU (9, 0, 0, 0, &$3, 0, 0, 0, 3);
+ }
+ else if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ notethat ("ALU2op: dregs = dregs_lo\n");
+ $$ = ALU2OP (&$1, &$3, 10 | ($4.r0 ? 0: 1));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ else
+ return yyerror ("Low reg expected");
+ }
+
+ | HALF_REG ASSIGN expr
+ {
+ notethat ("LDIMMhalf: pregs_half = imm16\n");
+
+ if (!IS_DREG ($1) && !IS_PREG ($1) && !IS_IREG ($1)
+ && !IS_MREG ($1) && !IS_BREG ($1) && !IS_LREG ($1))
+ return yyerror ("Wrong register for load immediate");
+
+ if (!IS_IMM ($3, 16) && !IS_UIMM ($3, 16))
+ return yyerror ("Constant out of range");
+
+ $$ = LDIMMHALF_R (&$1, IS_H ($1), 0, 0, $3);
+ }
+
+ | a_assign expr
+ {
+ notethat ("dsp32alu: An = 0\n");
+
+ if (imm7 ($2) != 0)
+ return yyerror ("0 expected");
+
+ $$ = DSP32ALU (8, 0, 0, 0, 0, 0, 0, 0, IS_A1 ($1));
+ }
+
+ | REG ASSIGN expr xpmod1
+ {
+ if (!IS_DREG ($1) && !IS_PREG ($1) && !IS_IREG ($1)
+ && !IS_MREG ($1) && !IS_BREG ($1) && !IS_LREG ($1))
+ return yyerror ("Wrong register for load immediate");
+
+ if ($4.r0 == 0)
+ {
+ /* 7 bit immediate value if possible.
+ We will check for that constant value for efficiency
+ If it goes to reloc, it will be 16 bit. */
+ if (IS_CONST ($3) && IS_IMM ($3, 7) && IS_DREG ($1))
+ {
+ notethat ("COMPI2opD: dregs = imm7 (x) \n");
+ $$ = COMPI2OPD (&$1, imm7 ($3), 0);
+ }
+ else if (IS_CONST ($3) && IS_IMM ($3, 7) && IS_PREG ($1))
+ {
+ notethat ("COMPI2opP: pregs = imm7 (x)\n");
+ $$ = COMPI2OPP (&$1, imm7 ($3), 0);
+ }
+ else
+ {
+ if (IS_CONST ($3) && !IS_IMM ($3, 16))
+ return yyerror ("Immediate value out of range");
+
+ notethat ("LDIMMhalf: regs = luimm16 (x)\n");
+ /* reg, H, S, Z. */
+ $$ = LDIMMHALF_R5 (&$1, 0, 1, 0, $3);
+ }
+ }
+ else
+ {
+ /* (z) There is no 7 bit zero extended instruction.
+ If the expr is a relocation, generate it. */
+
+ if (IS_CONST ($3) && !IS_UIMM ($3, 16))
+ return yyerror ("Immediate value out of range");
+
+ notethat ("LDIMMhalf: regs = luimm16 (x)\n");
+ /* reg, H, S, Z. */
+ $$ = LDIMMHALF_R5 (&$1, 0, 0, 1, $3);
+ }
+ }
+
+ | HALF_REG ASSIGN REG
+ {
+ if (IS_H ($1))
+ return yyerror ("Low reg expected");
+
+ if (IS_DREG ($1) && $3.regno == REG_A0x)
+ {
+ notethat ("dsp32alu: dregs_lo = A0.x\n");
+ $$ = DSP32ALU (10, 0, 0, &$1, &reg7, &reg7, 0, 0, 0);
+ }
+ else if (IS_DREG ($1) && $3.regno == REG_A1x)
+ {
+ notethat ("dsp32alu: dregs_lo = A1.x\n");
+ $$ = DSP32ALU (10, 0, 0, &$1, &reg7, &reg7, 0, 0, 1);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN REG op_bar_op REG amod0
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_DREG ($5))
+ {
+ notethat ("dsp32alu: dregs = dregs .|. dregs (amod0)\n");
+ $$ = DSP32ALU (0, 0, 0, &$1, &$3, &$5, $6.s0, $6.x0, $4.r0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN BYTE_DREG xpmod
+ {
+ if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ notethat ("ALU2op: dregs = dregs_byte\n");
+ $$ = ALU2OP (&$1, &$3, 12 | ($4.r0 ? 0: 1));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign ABS REG_A COMMA a_assign ABS REG_A
+ {
+ if (REG_SAME ($1, $3) && REG_SAME ($5, $7) && !REG_SAME ($1, $5))
+ {
+ notethat ("dsp32alu: A1 = ABS A1 , A0 = ABS A0\n");
+ $$ = DSP32ALU (16, 0, 0, 0, &reg7, &reg7, 0, 0, 3);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign MINUS REG_A COMMA a_assign MINUS REG_A
+ {
+ if (REG_SAME ($1, $3) && REG_SAME ($5, $7) && !REG_SAME ($1, $5))
+ {
+ notethat ("dsp32alu: A1 = - A1 , A0 = - A0\n");
+ $$ = DSP32ALU (14, 0, 0, 0, &reg7, &reg7, 0, 0, 3);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_minusassign REG_A w32_or_nothing
+ {
+ if (!IS_A1 ($1) && IS_A1 ($2))
+ {
+ notethat ("dsp32alu: A0 -= A1\n");
+ $$ = DSP32ALU (11, 0, 0, 0, &reg7, &reg7, $3.r0, 0, 3);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG _MINUS_ASSIGN expr
+ {
+ if (IS_IREG ($1) && EXPR_VALUE ($3) == 4)
+ {
+ notethat ("dagMODik: iregs -= 4\n");
+ $$ = DAGMODIK (&$1, 3);
+ }
+ else if (IS_IREG ($1) && EXPR_VALUE ($3) == 2)
+ {
+ notethat ("dagMODik: iregs -= 2\n");
+ $$ = DAGMODIK (&$1, 1);
+ }
+ else
+ return yyerror ("Register or value mismatch");
+ }
+
+ | REG _PLUS_ASSIGN REG LPAREN BREV RPAREN
+ {
+ if (IS_IREG ($1) && IS_MREG ($3))
+ {
+ notethat ("dagMODim: iregs += mregs (opt_brev)\n");
+ /* i, m, op, br. */
+ $$ = DAGMODIM (&$1, &$3, 0, 1);
+ }
+ else if (IS_PREG ($1) && IS_PREG ($3))
+ {
+ notethat ("PTR2op: pregs += pregs (BREV )\n");
+ $$ = PTR2OP (&$1, &$3, 5);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG _MINUS_ASSIGN REG
+ {
+ if (IS_IREG ($1) && IS_MREG ($3))
+ {
+ notethat ("dagMODim: iregs -= mregs\n");
+ $$ = DAGMODIM (&$1, &$3, 1, 0);
+ }
+ else if (IS_PREG ($1) && IS_PREG ($3))
+ {
+ notethat ("PTR2op: pregs -= pregs\n");
+ $$ = PTR2OP (&$1, &$3, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG_A _PLUS_ASSIGN REG_A w32_or_nothing
+ {
+ if (!IS_A1 ($1) && IS_A1 ($3))
+ {
+ notethat ("dsp32alu: A0 += A1 (W32)\n");
+ $$ = DSP32ALU (11, 0, 0, 0, &reg7, &reg7, $4.r0, 0, 2);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG _PLUS_ASSIGN REG
+ {
+ if (IS_IREG ($1) && IS_MREG ($3))
+ {
+ notethat ("dagMODim: iregs += mregs\n");
+ $$ = DAGMODIM (&$1, &$3, 0, 0);
+ }
+ else
+ return yyerror ("iregs += mregs expected");
+ }
+
+ | REG _PLUS_ASSIGN expr
+ {
+ if (IS_IREG ($1))
+ {
+ if (EXPR_VALUE ($3) == 4)
+ {
+ notethat ("dagMODik: iregs += 4\n");
+ $$ = DAGMODIK (&$1, 2);
+ }
+ else if (EXPR_VALUE ($3) == 2)
+ {
+ notethat ("dagMODik: iregs += 2\n");
+ $$ = DAGMODIK (&$1, 0);
+ }
+ else
+ return yyerror ("iregs += [ 2 | 4 ");
+ }
+ else if (IS_PREG ($1) && IS_IMM ($3, 7))
+ {
+ notethat ("COMPI2opP: pregs += imm7\n");
+ $$ = COMPI2OPP (&$1, imm7 ($3), 1);
+ }
+ else if (IS_DREG ($1) && IS_IMM ($3, 7))
+ {
+ notethat ("COMPI2opD: dregs += imm7\n");
+ $$ = COMPI2OPD (&$1, imm7 ($3), 1);
+ }
+ else if ((IS_DREG ($1) || IS_PREG ($1)) && IS_CONST ($3))
+ return yyerror ("Immediate value out of range");
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG _STAR_ASSIGN REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ notethat ("ALU2op: dregs *= dregs\n");
+ $$ = ALU2OP (&$1, &$3, 3);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | SAA LPAREN REG COLON expr COMMA REG COLON expr RPAREN aligndir
+ {
+ if (!valid_dreg_pair (&$3, $5))
+ return yyerror ("Bad dreg pair");
+ else if (!valid_dreg_pair (&$7, $9))
+ return yyerror ("Bad dreg pair");
+ else
+ {
+ notethat ("dsp32alu: SAA (dregs_pair , dregs_pair ) (aligndir)\n");
+ $$ = DSP32ALU (18, 0, 0, 0, &$3, &$7, $11.r0, 0, 0);
+ }
+ }
+
+ | a_assign REG_A LPAREN S RPAREN COMMA a_assign REG_A LPAREN S RPAREN
+ {
+ if (REG_SAME ($1, $2) && REG_SAME ($7, $8) && !REG_SAME ($1, $7))
+ {
+ notethat ("dsp32alu: A1 = A1 (S) , A0 = A0 (S)\n");
+ $$ = DSP32ALU (8, 0, 0, 0, &reg7, &reg7, 1, 0, 2);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN LPAREN REG PLUS REG RPAREN LESS_LESS expr
+ {
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_DREG ($6)
+ && REG_SAME ($1, $4))
+ {
+ if (EXPR_VALUE ($9) == 1)
+ {
+ notethat ("ALU2op: dregs = (dregs + dregs) << 1\n");
+ $$ = ALU2OP (&$1, &$6, 4);
+ }
+ else if (EXPR_VALUE ($9) == 2)
+ {
+ notethat ("ALU2op: dregs = (dregs + dregs) << 2\n");
+ $$ = ALU2OP (&$1, &$6, 5);
+ }
+ else
+ return yyerror ("Bad shift value");
+ }
+ else if (IS_PREG ($1) && IS_PREG ($4) && IS_PREG ($6)
+ && REG_SAME ($1, $4))
+ {
+ if (EXPR_VALUE ($9) == 1)
+ {
+ notethat ("PTR2op: pregs = (pregs + pregs) << 1\n");
+ $$ = PTR2OP (&$1, &$6, 6);
+ }
+ else if (EXPR_VALUE ($9) == 2)
+ {
+ notethat ("PTR2op: pregs = (pregs + pregs) << 2\n");
+ $$ = PTR2OP (&$1, &$6, 7);
+ }
+ else
+ return yyerror ("Bad shift value");
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+/* COMP3 CCFLAG. */
+ | REG ASSIGN REG BAR REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_DREG ($5))
+ {
+ notethat ("COMP3op: dregs = dregs | dregs\n");
+ $$ = COMP3OP (&$1, &$3, &$5, 3);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ | REG ASSIGN REG CARET REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_DREG ($5))
+ {
+ notethat ("COMP3op: dregs = dregs ^ dregs\n");
+ $$ = COMP3OP (&$1, &$3, &$5, 4);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ | REG ASSIGN REG PLUS LPAREN REG LESS_LESS expr RPAREN
+ {
+ if (IS_PREG ($1) && IS_PREG ($3) && IS_PREG ($6))
+ {
+ if (EXPR_VALUE ($8) == 1)
+ {
+ notethat ("COMP3op: pregs = pregs + (pregs << 1)\n");
+ $$ = COMP3OP (&$1, &$3, &$6, 6);
+ }
+ else if (EXPR_VALUE ($8) == 2)
+ {
+ notethat ("COMP3op: pregs = pregs + (pregs << 2)\n");
+ $$ = COMP3OP (&$1, &$3, &$6, 7);
+ }
+ else
+ return yyerror ("Bad shift value");
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ | CCREG ASSIGN REG_A _ASSIGN_ASSIGN REG_A
+ {
+ if ($3.regno == REG_A0 && $5.regno == REG_A1)
+ {
+ notethat ("CCflag: CC = A0 == A1\n");
+ $$ = CCFLAG (0, 0, 5, 0, 0);
+ }
+ else
+ return yyerror ("AREGs are in bad order or same");
+ }
+ | CCREG ASSIGN REG_A LESS_THAN REG_A
+ {
+ if ($3.regno == REG_A0 && $5.regno == REG_A1)
+ {
+ notethat ("CCflag: CC = A0 < A1\n");
+ $$ = CCFLAG (0, 0, 6, 0, 0);
+ }
+ else
+ return yyerror ("AREGs are in bad order or same");
+ }
+ | CCREG ASSIGN REG LESS_THAN REG iu_or_nothing
+ {
+ if ((IS_DREG ($3) && IS_DREG ($5))
+ || (IS_PREG ($3) && IS_PREG ($5)))
+ {
+ notethat ("CCflag: CC = dpregs < dpregs\n");
+ $$ = CCFLAG (&$3, $5.regno & CODE_MASK, $6.r0, 0, IS_PREG ($3) ? 1 : 0);
+ }
+ else
+ return yyerror ("Bad register in comparison");
+ }
+ | CCREG ASSIGN REG LESS_THAN expr iu_or_nothing
+ {
+ if (!IS_DREG ($3) && !IS_PREG ($3))
+ return yyerror ("Bad register in comparison");
+
+ if (($6.r0 == 1 && IS_IMM ($5, 3))
+ || ($6.r0 == 3 && IS_UIMM ($5, 3)))
+ {
+ notethat ("CCflag: CC = dpregs < (u)imm3\n");
+ $$ = CCFLAG (&$3, imm3 ($5), $6.r0, 1, IS_PREG ($3) ? 1 : 0);
+ }
+ else
+ return yyerror ("Bad constant value");
+ }
+ | CCREG ASSIGN REG _ASSIGN_ASSIGN REG
+ {
+ if ((IS_DREG ($3) && IS_DREG ($5))
+ || (IS_PREG ($3) && IS_PREG ($5)))
+ {
+ notethat ("CCflag: CC = dpregs == dpregs\n");
+ $$ = CCFLAG (&$3, $5.regno & CODE_MASK, 0, 0, IS_PREG ($3) ? 1 : 0);
+ }
+ else
+ return yyerror ("Bad register in comparison");
+ }
+ | CCREG ASSIGN REG _ASSIGN_ASSIGN expr
+ {
+ if (!IS_DREG ($3) && !IS_PREG ($3))
+ return yyerror ("Bad register in comparison");
+
+ if (IS_IMM ($5, 3))
+ {
+ notethat ("CCflag: CC = dpregs == imm3\n");
+ $$ = CCFLAG (&$3, imm3 ($5), 0, 1, IS_PREG ($3) ? 1 : 0);
+ }
+ else
+ return yyerror ("Bad constant range");
+ }
+ | CCREG ASSIGN REG_A _LESS_THAN_ASSIGN REG_A
+ {
+ if ($3.regno == REG_A0 && $5.regno == REG_A1)
+ {
+ notethat ("CCflag: CC = A0 <= A1\n");
+ $$ = CCFLAG (0, 0, 7, 0, 0);
+ }
+ else
+ return yyerror ("AREGs are in bad order or same");
+ }
+ | CCREG ASSIGN REG _LESS_THAN_ASSIGN REG iu_or_nothing
+ {
+ if ((IS_DREG ($3) && IS_DREG ($5))
+ || (IS_PREG ($3) && IS_PREG ($5)))
+ {
+ notethat ("CCflag: CC = dpregs <= dpregs (..)\n");
+ $$ = CCFLAG (&$3, $5.regno & CODE_MASK,
+ 1 + $6.r0, 0, IS_PREG ($3) ? 1 : 0);
+ }
+ else
+ return yyerror ("Bad register in comparison");
+ }
+ | CCREG ASSIGN REG _LESS_THAN_ASSIGN expr iu_or_nothing
+ {
+ if (!IS_DREG ($3) && !IS_PREG ($3))
+ return yyerror ("Bad register in comparison");
+
+ if (($6.r0 == 1 && IS_IMM ($5, 3))
+ || ($6.r0 == 3 && IS_UIMM ($5, 3)))
+ {
+ notethat ("CCflag: CC = dpregs <= (u)imm3\n");
+ $$ = CCFLAG (&$3, imm3 ($5), 1 + $6.r0, 1, IS_PREG ($3) ? 1 : 0);
+ }
+ else
+ return yyerror ("Bad constant value");
+ }
+
+ | REG ASSIGN REG AMPERSAND REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_DREG ($5))
+ {
+ notethat ("COMP3op: dregs = dregs & dregs\n");
+ $$ = COMP3OP (&$1, &$3, &$5, 2);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | ccstat
+ {
+ notethat ("CC2stat operation\n");
+ $$ = bfin_gen_cc2stat ($1.r0, $1.x0, $1.s0);
+ }
+
+ | REG ASSIGN REG
+ {
+ if ((IS_GENREG ($1) && IS_GENREG ($3))
+ || (IS_GENREG ($1) && IS_DAGREG ($3))
+ || (IS_DAGREG ($1) && IS_GENREG ($3))
+ || (IS_DAGREG ($1) && IS_DAGREG ($3))
+ || (IS_GENREG ($1) && $3.regno == REG_USP)
+ || ($1.regno == REG_USP && IS_GENREG ($3))
+ || ($1.regno == REG_USP && $3.regno == REG_USP)
+ || (IS_DREG ($1) && IS_SYSREG ($3))
+ || (IS_PREG ($1) && IS_SYSREG ($3))
+ || (IS_SYSREG ($1) && IS_GENREG ($3))
+ || (IS_ALLREG ($1) && IS_EMUDAT ($3))
+ || (IS_EMUDAT ($1) && IS_ALLREG ($3))
+ || (IS_SYSREG ($1) && $3.regno == REG_USP))
+ {
+ $$ = bfin_gen_regmv (&$3, &$1);
+ }
+ else
+ return yyerror ("Unsupported register move");
+ }
+
+ | CCREG ASSIGN REG
+ {
+ if (IS_DREG ($3))
+ {
+ notethat ("CC2dreg: CC = dregs\n");
+ $$ = bfin_gen_cc2dreg (1, &$3);
+ }
+ else
+ return yyerror ("Only 'CC = Dreg' supported");
+ }
+
+ | REG ASSIGN CCREG
+ {
+ if (IS_DREG ($1))
+ {
+ notethat ("CC2dreg: dregs = CC\n");
+ $$ = bfin_gen_cc2dreg (0, &$1);
+ }
+ else
+ return yyerror ("Only 'Dreg = CC' supported");
+ }
+
+ | CCREG _ASSIGN_BANG CCREG
+ {
+ notethat ("CC2dreg: CC =! CC\n");
+ $$ = bfin_gen_cc2dreg (3, 0);
+ }
+
+/* DSPMULT. */
+
+ | HALF_REG ASSIGN multiply_halfregs opt_mode
+ {
+ notethat ("dsp32mult: dregs_half = multiply_halfregs (opt_mode)\n");
+
+ if (!IS_H ($1) && $4.MM)
+ return yyerror ("(M) not allowed with MAC0");
+
+ if ($4.mod != 0 && $4.mod != M_FU && $4.mod != M_IS
+ && $4.mod != M_IU && $4.mod != M_T && $4.mod != M_TFU
+ && $4.mod != M_S2RND && $4.mod != M_ISS2 && $4.mod != M_IH)
+ return yyerror ("bad option.");
+
+ if (IS_H ($1))
+ {
+ $$ = DSP32MULT (0, $4.MM, $4.mod, 1, 0,
+ IS_H ($3.s0), IS_H ($3.s1), 0, 0,
+ &$1, 0, &$3.s0, &$3.s1, 0);
+ }
+ else
+ {
+ $$ = DSP32MULT (0, 0, $4.mod, 0, 0,
+ 0, 0, IS_H ($3.s0), IS_H ($3.s1),
+ &$1, 0, &$3.s0, &$3.s1, 1);
+ }
+ }
+
+ | REG ASSIGN multiply_halfregs opt_mode
+ {
+ /* Odd registers can use (M). */
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected");
+
+ if (IS_EVEN ($1) && $4.MM)
+ return yyerror ("(M) not allowed with MAC0");
+
+ if ($4.mod != 0 && $4.mod != M_FU && $4.mod != M_IS
+ && $4.mod != M_S2RND && $4.mod != M_ISS2)
+ return yyerror ("bad option");
+
+ if (!IS_EVEN ($1))
+ {
+ notethat ("dsp32mult: dregs = multiply_halfregs (opt_mode)\n");
+
+ $$ = DSP32MULT (0, $4.MM, $4.mod, 1, 1,
+ IS_H ($3.s0), IS_H ($3.s1), 0, 0,
+ &$1, 0, &$3.s0, &$3.s1, 0);
+ }
+ else
+ {
+ notethat ("dsp32mult: dregs = multiply_halfregs opt_mode\n");
+ $$ = DSP32MULT (0, 0, $4.mod, 0, 1,
+ 0, 0, IS_H ($3.s0), IS_H ($3.s1),
+ &$1, 0, &$3.s0, &$3.s1, 1);
+ }
+ }
+
+ | HALF_REG ASSIGN multiply_halfregs opt_mode COMMA
+ HALF_REG ASSIGN multiply_halfregs opt_mode
+ {
+ if (!IS_DREG ($1) || !IS_DREG ($6))
+ return yyerror ("Dregs expected");
+
+ if (!IS_HCOMPL($1, $6))
+ return yyerror ("Dest registers mismatch");
+
+ if (check_multiply_halfregs (&$3, &$8) < 0)
+ return -1;
+
+ if ((!IS_H ($1) && $4.MM)
+ || (!IS_H ($6) && $9.MM))
+ return yyerror ("(M) not allowed with MAC0");
+
+ notethat ("dsp32mult: dregs_hi = multiply_halfregs mxd_mod, "
+ "dregs_lo = multiply_halfregs opt_mode\n");
+
+ if (IS_H ($1))
+ $$ = DSP32MULT (0, $4.MM, $9.mod, 1, 0,
+ IS_H ($3.s0), IS_H ($3.s1), IS_H ($8.s0), IS_H ($8.s1),
+ &$1, 0, &$3.s0, &$3.s1, 1);
+ else
+ $$ = DSP32MULT (0, $9.MM, $9.mod, 1, 0,
+ IS_H ($8.s0), IS_H ($8.s1), IS_H ($3.s0), IS_H ($3.s1),
+ &$1, 0, &$3.s0, &$3.s1, 1);
+ }
+
+ | REG ASSIGN multiply_halfregs opt_mode COMMA REG ASSIGN multiply_halfregs opt_mode
+ {
+ if (!IS_DREG ($1) || !IS_DREG ($6))
+ return yyerror ("Dregs expected");
+
+ if ((IS_EVEN ($1) && $6.regno - $1.regno != 1)
+ || (IS_EVEN ($6) && $1.regno - $6.regno != 1))
+ return yyerror ("Dest registers mismatch");
+
+ if (check_multiply_halfregs (&$3, &$8) < 0)
+ return -1;
+
+ if ((IS_EVEN ($1) && $4.MM)
+ || (IS_EVEN ($6) && $9.MM))
+ return yyerror ("(M) not allowed with MAC0");
+
+ notethat ("dsp32mult: dregs = multiply_halfregs mxd_mod, "
+ "dregs = multiply_halfregs opt_mode\n");
+
+ if (IS_EVEN ($1))
+ $$ = DSP32MULT (0, $9.MM, $9.mod, 1, 1,
+ IS_H ($8.s0), IS_H ($8.s1), IS_H ($3.s0), IS_H ($3.s1),
+ &$1, 0, &$3.s0, &$3.s1, 1);
+ else
+ $$ = DSP32MULT (0, $4.MM, $9.mod, 1, 1,
+ IS_H ($3.s0), IS_H ($3.s1), IS_H ($8.s0), IS_H ($8.s1),
+ &$1, 0, &$3.s0, &$3.s1, 1);
+ }
+
+
+/* SHIFTs. */
+ | a_assign ASHIFT REG_A BY HALF_REG
+ {
+ if (!REG_SAME ($1, $3))
+ return yyerror ("Aregs must be same");
+
+ if (IS_DREG ($5) && !IS_H ($5))
+ {
+ notethat ("dsp32shift: A0 = ASHIFT A0 BY dregs_lo\n");
+ $$ = DSP32SHIFT (3, 0, &$5, 0, 0, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | HALF_REG ASSIGN ASHIFT HALF_REG BY HALF_REG smod
+ {
+ if (IS_DREG ($6) && !IS_H ($6))
+ {
+ notethat ("dsp32shift: dregs_half = ASHIFT dregs_half BY dregs_lo\n");
+ $$ = DSP32SHIFT (0, &$1, &$6, &$4, $7.s0, HL2 ($1, $4));
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | a_assign REG_A LESS_LESS expr
+ {
+ if (!REG_SAME ($1, $2))
+ return yyerror ("Aregs must be same");
+
+ if (IS_UIMM ($4, 5))
+ {
+ notethat ("dsp32shiftimm: A0 = A0 << uimm5\n");
+ $$ = DSP32SHIFTIMM (3, 0, imm5 ($4), 0, 0, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Bad shift value");
+ }
+
+ | REG ASSIGN REG LESS_LESS expr vsmod
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ if ($6.r0)
+ {
+ /* Vector? */
+ notethat ("dsp32shiftimm: dregs = dregs << expr (V, .)\n");
+ $$ = DSP32SHIFTIMM (1, &$1, imm4 ($5), &$3, $6.s0 ? 1 : 2, 0);
+ }
+ else
+ {
+ notethat ("dsp32shiftimm: dregs = dregs << uimm5 (.)\n");
+ $$ = DSP32SHIFTIMM (2, &$1, imm6 ($5), &$3, $6.s0 ? 1 : 2, 0);
+ }
+ }
+ else if ($6.s0 == 0 && IS_PREG ($1) && IS_PREG ($3))
+ {
+ if (EXPR_VALUE ($5) == 2)
+ {
+ notethat ("PTR2op: pregs = pregs << 2\n");
+ $$ = PTR2OP (&$1, &$3, 1);
+ }
+ else if (EXPR_VALUE ($5) == 1)
+ {
+ notethat ("COMP3op: pregs = pregs << 1\n");
+ $$ = COMP3OP (&$1, &$3, &$3, 5);
+ }
+ else
+ return yyerror ("Bad shift value");
+ }
+ else
+ return yyerror ("Bad shift value or register");
+ }
+ | HALF_REG ASSIGN HALF_REG LESS_LESS expr smod
+ {
+ if (IS_UIMM ($5, 4))
+ {
+ if ($6.s0)
+ {
+ notethat ("dsp32shiftimm: dregs_half = dregs_half << uimm4 (S)\n");
+ $$ = DSP32SHIFTIMM (0x0, &$1, imm5 ($5), &$3, $6.s0, HL2 ($1, $3));
+ }
+ else
+ {
+ notethat ("dsp32shiftimm: dregs_half = dregs_half << uimm4\n");
+ $$ = DSP32SHIFTIMM (0x0, &$1, imm5 ($5), &$3, 2, HL2 ($1, $3));
+ }
+ }
+ else
+ return yyerror ("Bad shift value");
+ }
+ | REG ASSIGN ASHIFT REG BY HALF_REG vsmod
+ {
+ int op;
+
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_DREG ($6) && !IS_H ($6))
+ {
+ if ($7.r0)
+ {
+ op = 1;
+ notethat ("dsp32shift: dregs = ASHIFT dregs BY "
+ "dregs_lo (V, .)\n");
+ }
+ else
+ {
+
+ op = 2;
+ notethat ("dsp32shift: dregs = ASHIFT dregs BY dregs_lo (.)\n");
+ }
+ $$ = DSP32SHIFT (op, &$1, &$6, &$4, $7.s0, 0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+/* EXPADJ. */
+ | HALF_REG ASSIGN EXPADJ LPAREN REG COMMA HALF_REG RPAREN vmod
+ {
+ if (IS_DREG_L ($1) && IS_DREG_L ($5) && IS_DREG_L ($7))
+ {
+ notethat ("dsp32shift: dregs_lo = EXPADJ (dregs , dregs_lo )\n");
+ $$ = DSP32SHIFT (7, &$1, &$7, &$5, $9.r0, 0);
+ }
+ else
+ return yyerror ("Bad shift value or register");
+ }
+
+
+ | HALF_REG ASSIGN EXPADJ LPAREN HALF_REG COMMA HALF_REG RPAREN
+ {
+ if (IS_DREG_L ($1) && IS_DREG_L ($5) && IS_DREG_L ($7))
+ {
+ notethat ("dsp32shift: dregs_lo = EXPADJ (dregs_lo, dregs_lo)\n");
+ $$ = DSP32SHIFT (7, &$1, &$7, &$5, 2, 0);
+ }
+ else if (IS_DREG_L ($1) && IS_DREG_H ($5) && IS_DREG_L ($7))
+ {
+ notethat ("dsp32shift: dregs_lo = EXPADJ (dregs_hi, dregs_lo)\n");
+ $$ = DSP32SHIFT (7, &$1, &$7, &$5, 3, 0);
+ }
+ else
+ return yyerror ("Bad shift value or register");
+ }
+
+/* DEPOSIT. */
+
+ | REG ASSIGN DEPOSIT LPAREN REG COMMA REG RPAREN
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ notethat ("dsp32shift: dregs = DEPOSIT (dregs , dregs )\n");
+ $$ = DSP32SHIFT (10, &$1, &$7, &$5, 2, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN DEPOSIT LPAREN REG COMMA REG RPAREN LPAREN X RPAREN
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ notethat ("dsp32shift: dregs = DEPOSIT (dregs , dregs ) (X)\n");
+ $$ = DSP32SHIFT (10, &$1, &$7, &$5, 3, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN EXTRACT LPAREN REG COMMA HALF_REG RPAREN xpmod
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG_L ($7))
+ {
+ notethat ("dsp32shift: dregs = EXTRACT (dregs, dregs_lo ) (.)\n");
+ $$ = DSP32SHIFT (10, &$1, &$7, &$5, $9.r0, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign REG_A _GREATER_GREATER_GREATER expr
+ {
+ if (!REG_SAME ($1, $2))
+ return yyerror ("Aregs must be same");
+
+ if (IS_UIMM ($4, 5))
+ {
+ notethat ("dsp32shiftimm: Ax = Ax >>> uimm5\n");
+ $$ = DSP32SHIFTIMM (3, 0, -imm6 ($4), 0, 0, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Shift value range error");
+ }
+ | a_assign LSHIFT REG_A BY HALF_REG
+ {
+ if (REG_SAME ($1, $3) && IS_DREG_L ($5))
+ {
+ notethat ("dsp32shift: Ax = LSHIFT Ax BY dregs_lo\n");
+ $$ = DSP32SHIFT (3, 0, &$5, 0, 1, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN LSHIFT HALF_REG BY HALF_REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_DREG_L ($6))
+ {
+ notethat ("dsp32shift: dregs_lo = LSHIFT dregs_hi BY dregs_lo\n");
+ $$ = DSP32SHIFT (0, &$1, &$6, &$4, 2, HL2 ($1, $4));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN LSHIFT REG BY HALF_REG vmod
+ {
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_DREG_L ($6))
+ {
+ notethat ("dsp32shift: dregs = LSHIFT dregs BY dregs_lo (V )\n");
+ $$ = DSP32SHIFT ($7.r0 ? 1: 2, &$1, &$6, &$4, 2, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN SHIFT REG BY HALF_REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_DREG_L ($6))
+ {
+ notethat ("dsp32shift: dregs = SHIFT dregs BY dregs_lo\n");
+ $$ = DSP32SHIFT (2, &$1, &$6, &$4, 2, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign REG_A GREATER_GREATER expr
+ {
+ if (REG_SAME ($1, $2) && IS_IMM ($4, 6) >= 0)
+ {
+ notethat ("dsp32shiftimm: Ax = Ax >> imm6\n");
+ $$ = DSP32SHIFTIMM (3, 0, -imm6 ($4), 0, 1, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Accu register expected");
+ }
+
+ | REG ASSIGN REG GREATER_GREATER expr vmod
+ {
+ if ($6.r0 == 1)
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ notethat ("dsp32shiftimm: dregs = dregs >> uimm5 (V)\n");
+ $$ = DSP32SHIFTIMM (1, &$1, -uimm5 ($5), &$3, 2, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ else
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ notethat ("dsp32shiftimm: dregs = dregs >> uimm5\n");
+ $$ = DSP32SHIFTIMM (2, &$1, -imm6 ($5), &$3, 2, 0);
+ }
+ else if (IS_PREG ($1) && IS_PREG ($3) && EXPR_VALUE ($5) == 2)
+ {
+ notethat ("PTR2op: pregs = pregs >> 2\n");
+ $$ = PTR2OP (&$1, &$3, 3);
+ }
+ else if (IS_PREG ($1) && IS_PREG ($3) && EXPR_VALUE ($5) == 1)
+ {
+ notethat ("PTR2op: pregs = pregs >> 1\n");
+ $$ = PTR2OP (&$1, &$3, 4);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ }
+ | HALF_REG ASSIGN HALF_REG GREATER_GREATER expr
+ {
+ if (IS_UIMM ($5, 5))
+ {
+ notethat ("dsp32shiftimm: dregs_half = dregs_half >> uimm5\n");
+ $$ = DSP32SHIFTIMM (0, &$1, -uimm5 ($5), &$3, 2, HL2 ($1, $3));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+ | HALF_REG ASSIGN HALF_REG _GREATER_GREATER_GREATER expr smod
+ {
+ if (IS_UIMM ($5, 5))
+ {
+ notethat ("dsp32shiftimm: dregs_half = dregs_half >>> uimm5\n");
+ $$ = DSP32SHIFTIMM (0, &$1, -uimm5 ($5), &$3,
+ $6.s0, HL2 ($1, $3));
+ }
+ else
+ return yyerror ("Register or modifier mismatch");
+ }
+
+
+ | REG ASSIGN REG _GREATER_GREATER_GREATER expr vsmod
+ {
+ if (IS_DREG ($1) && IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ if ($6.r0)
+ {
+ /* Vector? */
+ notethat ("dsp32shiftimm: dregs = dregs >>> uimm5 (V, .)\n");
+ $$ = DSP32SHIFTIMM (1, &$1, -uimm5 ($5), &$3, $6.s0, 0);
+ }
+ else
+ {
+ notethat ("dsp32shiftimm: dregs = dregs >>> uimm5 (.)\n");
+ $$ = DSP32SHIFTIMM (2, &$1, -uimm5 ($5), &$3, $6.s0, 0);
+ }
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN ONES REG
+ {
+ if (IS_DREG_L ($1) && IS_DREG ($4))
+ {
+ notethat ("dsp32shift: dregs_lo = ONES dregs\n");
+ $$ = DSP32SHIFT (6, &$1, 0, &$4, 3, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN PACK LPAREN HALF_REG COMMA HALF_REG RPAREN
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ notethat ("dsp32shift: dregs = PACK (dregs_hi , dregs_hi )\n");
+ $$ = DSP32SHIFT (4, &$1, &$7, &$5, HL2 ($5, $7), 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN CCREG ASSIGN BXORSHIFT LPAREN REG_A COMMA REG RPAREN
+ {
+ if (IS_DREG ($1)
+ && $7.regno == REG_A0
+ && IS_DREG ($9) && !IS_H ($1) && !IS_A1 ($7))
+ {
+ notethat ("dsp32shift: dregs_lo = CC = BXORSHIFT (A0 , dregs )\n");
+ $$ = DSP32SHIFT (11, &$1, &$9, 0, 0, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN CCREG ASSIGN BXOR LPAREN REG_A COMMA REG RPAREN
+ {
+ if (IS_DREG ($1)
+ && $7.regno == REG_A0
+ && IS_DREG ($9) && !IS_H ($1) && !IS_A1 ($7))
+ {
+ notethat ("dsp32shift: dregs_lo = CC = BXOR (A0 , dregs)\n");
+ $$ = DSP32SHIFT (11, &$1, &$9, 0, 1, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN CCREG ASSIGN BXOR LPAREN REG_A COMMA REG_A COMMA CCREG RPAREN
+ {
+ if (IS_DREG ($1) && !IS_H ($1) && !REG_SAME ($7, $9))
+ {
+ notethat ("dsp32shift: dregs_lo = CC = BXOR (A0 , A1 , CC)\n");
+ $$ = DSP32SHIFT (12, &$1, 0, 0, 1, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign ROT REG_A BY HALF_REG
+ {
+ if (REG_SAME ($1, $3) && IS_DREG_L ($5))
+ {
+ notethat ("dsp32shift: Ax = ROT Ax BY dregs_lo\n");
+ $$ = DSP32SHIFT (3, 0, &$5, 0, 2, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN ROT REG BY HALF_REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_DREG_L ($6))
+ {
+ notethat ("dsp32shift: dregs = ROT dregs BY dregs_lo\n");
+ $$ = DSP32SHIFT (2, &$1, &$6, &$4, 3, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign ROT REG_A BY expr
+ {
+ if (IS_IMM ($5, 6))
+ {
+ notethat ("dsp32shiftimm: An = ROT An BY imm6\n");
+ $$ = DSP32SHIFTIMM (3, 0, imm6 ($5), 0, 2, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN ROT REG BY expr
+ {
+ if (IS_DREG ($1) && IS_DREG ($4) && IS_IMM ($6, 6))
+ {
+ $$ = DSP32SHIFTIMM (2, &$1, imm6 ($6), &$4, 3, IS_A1 ($1));
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN SIGNBITS REG_A
+ {
+ if (IS_DREG_L ($1))
+ {
+ notethat ("dsp32shift: dregs_lo = SIGNBITS An\n");
+ $$ = DSP32SHIFT (6, &$1, 0, 0, IS_A1 ($4), 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN SIGNBITS REG
+ {
+ if (IS_DREG_L ($1) && IS_DREG ($4))
+ {
+ notethat ("dsp32shift: dregs_lo = SIGNBITS dregs\n");
+ $$ = DSP32SHIFT (5, &$1, 0, &$4, 0, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | HALF_REG ASSIGN SIGNBITS HALF_REG
+ {
+ if (IS_DREG_L ($1))
+ {
+ notethat ("dsp32shift: dregs_lo = SIGNBITS dregs_lo\n");
+ $$ = DSP32SHIFT (5, &$1, 0, &$4, 1 + IS_H ($4), 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ /* The ASR bit is just inverted here. */
+ | HALF_REG ASSIGN VIT_MAX LPAREN REG RPAREN asr_asl
+ {
+ if (IS_DREG_L ($1) && IS_DREG ($5))
+ {
+ notethat ("dsp32shift: dregs_lo = VIT_MAX (dregs) (..)\n");
+ $$ = DSP32SHIFT (9, &$1, 0, &$5, ($7.r0 ? 0 : 1), 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | REG ASSIGN VIT_MAX LPAREN REG COMMA REG RPAREN asr_asl
+ {
+ if (IS_DREG ($1) && IS_DREG ($5) && IS_DREG ($7))
+ {
+ notethat ("dsp32shift: dregs = VIT_MAX (dregs, dregs) (ASR)\n");
+ $$ = DSP32SHIFT (9, &$1, &$7, &$5, 2 | ($9.r0 ? 0 : 1), 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | BITMUX LPAREN REG COMMA REG COMMA REG_A RPAREN asr_asl
+ {
+ if (REG_SAME ($3, $5))
+ return yyerror ("Illegal source register combination");
+
+ if (IS_DREG ($3) && IS_DREG ($5) && !IS_A1 ($7))
+ {
+ notethat ("dsp32shift: BITMUX (dregs , dregs , A0) (ASR)\n");
+ $$ = DSP32SHIFT (8, 0, &$3, &$5, $9.r0, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | a_assign BXORSHIFT LPAREN REG_A COMMA REG_A COMMA CCREG RPAREN
+ {
+ if (!IS_A1 ($1) && !IS_A1 ($4) && IS_A1 ($6))
+ {
+ notethat ("dsp32shift: A0 = BXORSHIFT (A0 , A1 , CC )\n");
+ $$ = DSP32SHIFT (12, 0, 0, 0, 0, 0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+
+/* LOGI2op: BITCLR (dregs, uimm5). */
+ | BITCLR LPAREN REG COMMA expr RPAREN
+ {
+ if (IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ notethat ("LOGI2op: BITCLR (dregs , uimm5 )\n");
+ $$ = LOGI2OP ($3, uimm5 ($5), 4);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+/* LOGI2op: BITSET (dregs, uimm5). */
+ | BITSET LPAREN REG COMMA expr RPAREN
+ {
+ if (IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ notethat ("LOGI2op: BITCLR (dregs , uimm5 )\n");
+ $$ = LOGI2OP ($3, uimm5 ($5), 2);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+/* LOGI2op: BITTGL (dregs, uimm5). */
+ | BITTGL LPAREN REG COMMA expr RPAREN
+ {
+ if (IS_DREG ($3) && IS_UIMM ($5, 5))
+ {
+ notethat ("LOGI2op: BITCLR (dregs , uimm5 )\n");
+ $$ = LOGI2OP ($3, uimm5 ($5), 3);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | CCREG _ASSIGN_BANG BITTST LPAREN REG COMMA expr RPAREN
+ {
+ if (IS_DREG ($5) && IS_UIMM ($7, 5))
+ {
+ notethat ("LOGI2op: CC =! BITTST (dregs , uimm5 )\n");
+ $$ = LOGI2OP ($5, uimm5 ($7), 0);
+ }
+ else
+ return yyerror ("Register mismatch or value error");
+ }
+
+ | CCREG ASSIGN BITTST LPAREN REG COMMA expr RPAREN
+ {
+ if (IS_DREG ($5) && IS_UIMM ($7, 5))
+ {
+ notethat ("LOGI2op: CC = BITTST (dregs , uimm5 )\n");
+ $$ = LOGI2OP ($5, uimm5 ($7), 1);
+ }
+ else
+ return yyerror ("Register mismatch or value error");
+ }
+
+ | IF BANG CCREG REG ASSIGN REG
+ {
+ if ((IS_DREG ($4) || IS_PREG ($4))
+ && (IS_DREG ($6) || IS_PREG ($6)))
+ {
+ notethat ("ccMV: IF ! CC gregs = gregs\n");
+ $$ = CCMV (&$6, &$4, 0);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | IF CCREG REG ASSIGN REG
+ {
+ if ((IS_DREG ($5) || IS_PREG ($5))
+ && (IS_DREG ($3) || IS_PREG ($3)))
+ {
+ notethat ("ccMV: IF CC gregs = gregs\n");
+ $$ = CCMV (&$5, &$3, 1);
+ }
+ else
+ return yyerror ("Register mismatch");
+ }
+
+ | IF BANG CCREG JUMP expr
+ {
+ if (IS_PCREL10 ($5))
+ {
+ notethat ("BRCC: IF !CC JUMP pcrel11m2\n");
+ $$ = BRCC (0, 0, $5);
+ }
+ else
+ return yyerror ("Bad jump offset");
+ }
+
+ | IF BANG CCREG JUMP expr LPAREN BP RPAREN
+ {
+ if (IS_PCREL10 ($5))
+ {
+ notethat ("BRCC: IF !CC JUMP pcrel11m2\n");
+ $$ = BRCC (0, 1, $5);
+ }
+ else
+ return yyerror ("Bad jump offset");
+ }
+
+ | IF CCREG JUMP expr
+ {
+ if (IS_PCREL10 ($4))
+ {
+ notethat ("BRCC: IF CC JUMP pcrel11m2\n");
+ $$ = BRCC (1, 0, $4);
+ }
+ else
+ return yyerror ("Bad jump offset");
+ }
+
+ | IF CCREG JUMP expr LPAREN BP RPAREN
+ {
+ if (IS_PCREL10 ($4))
+ {
+ notethat ("BRCC: IF !CC JUMP pcrel11m2\n");
+ $$ = BRCC (1, 1, $4);
+ }
+ else
+ return yyerror ("Bad jump offset");
+ }
+ | NOP
+ {
+ notethat ("ProgCtrl: NOP\n");
+ $$ = PROGCTRL (0, 0);
+ }
+
+ | RTS
+ {
+ notethat ("ProgCtrl: RTS\n");
+ $$ = PROGCTRL (1, 0);
+ }
+
+ | RTI
+ {
+ notethat ("ProgCtrl: RTI\n");
+ $$ = PROGCTRL (1, 1);
+ }
+
+ | RTX
+ {
+ notethat ("ProgCtrl: RTX\n");
+ $$ = PROGCTRL (1, 2);
+ }
+
+ | RTN
+ {
+ notethat ("ProgCtrl: RTN\n");
+ $$ = PROGCTRL (1, 3);
+ }
+
+ | RTE
+ {
+ notethat ("ProgCtrl: RTE\n");
+ $$ = PROGCTRL (1, 4);
+ }
+
+ | IDLE
+ {
+ notethat ("ProgCtrl: IDLE\n");
+ $$ = PROGCTRL (2, 0);
+ }
+
+ | CSYNC
+ {
+ notethat ("ProgCtrl: CSYNC\n");
+ $$ = PROGCTRL (2, 3);
+ }
+
+ | SSYNC
+ {
+ notethat ("ProgCtrl: SSYNC\n");
+ $$ = PROGCTRL (2, 4);
+ }
+
+ | EMUEXCPT
+ {
+ notethat ("ProgCtrl: EMUEXCPT\n");
+ $$ = PROGCTRL (2, 5);
+ }
+
+ | CLI REG
+ {
+ if (IS_DREG ($2))
+ {
+ notethat ("ProgCtrl: CLI dregs\n");
+ $$ = PROGCTRL (3, $2.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Dreg expected for CLI");
+ }
+
+ | STI REG
+ {
+ if (IS_DREG ($2))
+ {
+ notethat ("ProgCtrl: STI dregs\n");
+ $$ = PROGCTRL (4, $2.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Dreg expected for STI");
+ }
+
+ | JUMP LPAREN REG RPAREN
+ {
+ if (IS_PREG ($3))
+ {
+ notethat ("ProgCtrl: JUMP (pregs )\n");
+ $$ = PROGCTRL (5, $3.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Bad register for indirect jump");
+ }
+
+ | CALL LPAREN REG RPAREN
+ {
+ if (IS_PREG ($3))
+ {
+ notethat ("ProgCtrl: CALL (pregs )\n");
+ $$ = PROGCTRL (6, $3.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Bad register for indirect call");
+ }
+
+ | CALL LPAREN PC PLUS REG RPAREN
+ {
+ if (IS_PREG ($5))
+ {
+ notethat ("ProgCtrl: CALL (PC + pregs )\n");
+ $$ = PROGCTRL (7, $5.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Bad register for indirect call");
+ }
+
+ | JUMP LPAREN PC PLUS REG RPAREN
+ {
+ if (IS_PREG ($5))
+ {
+ notethat ("ProgCtrl: JUMP (PC + pregs )\n");
+ $$ = PROGCTRL (8, $5.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Bad register for indirect jump");
+ }
+
+ | RAISE expr
+ {
+ if (IS_UIMM ($2, 4))
+ {
+ notethat ("ProgCtrl: RAISE uimm4\n");
+ $$ = PROGCTRL (9, uimm4 ($2));
+ }
+ else
+ return yyerror ("Bad value for RAISE");
+ }
+
+ | EXCPT expr
+ {
+ notethat ("ProgCtrl: EMUEXCPT\n");
+ $$ = PROGCTRL (10, uimm4 ($2));
+ }
+
+ | TESTSET LPAREN REG RPAREN
+ {
+ if (IS_PREG ($3))
+ {
+ if ($3.regno == REG_SP || $3.regno == REG_FP)
+ return yyerror ("Bad register for TESTSET");
+
+ notethat ("ProgCtrl: TESTSET (pregs )\n");
+ $$ = PROGCTRL (11, $3.regno & CODE_MASK);
+ }
+ else
+ return yyerror ("Preg expected");
+ }
+
+ | JUMP expr
+ {
+ if (IS_PCREL12 ($2))
+ {
+ notethat ("UJUMP: JUMP pcrel12\n");
+ $$ = UJUMP ($2);
+ }
+ else
+ return yyerror ("Bad value for relative jump");
+ }
+
+ | JUMP_DOT_S expr
+ {
+ if (IS_PCREL12 ($2))
+ {
+ notethat ("UJUMP: JUMP_DOT_S pcrel12\n");
+ $$ = UJUMP($2);
+ }
+ else
+ return yyerror ("Bad value for relative jump");
+ }
+
+ | JUMP_DOT_L expr
+ {
+ if (IS_PCREL24 ($2))
+ {
+ notethat ("CALLa: jump.l pcrel24\n");
+ $$ = CALLA ($2, 0);
+ }
+ else
+ return yyerror ("Bad value for long jump");
+ }
+
+ | JUMP_DOT_L pltpc
+ {
+ if (IS_PCREL24 ($2))
+ {
+ notethat ("CALLa: jump.l pcrel24\n");
+ $$ = CALLA ($2, 2);
+ }
+ else
+ return yyerror ("Bad value for long jump");
+ }
+
+ | CALL expr
+ {
+ if (IS_PCREL24 ($2))
+ {
+ notethat ("CALLa: CALL pcrel25m2\n");
+ $$ = CALLA ($2, 1);
+ }
+ else
+ return yyerror ("Bad call address");
+ }
+ | CALL pltpc
+ {
+ if (IS_PCREL24 ($2))
+ {
+ notethat ("CALLa: CALL pcrel25m2\n");
+ $$ = CALLA ($2, 2);
+ }
+ else
+ return yyerror ("Bad call address");
+ }
+
+/* ALU2ops. */
+/* ALU2op: DIVQ (dregs, dregs). */
+ | DIVQ LPAREN REG COMMA REG RPAREN
+ {
+ if (IS_DREG ($3) && IS_DREG ($5))
+ $$ = ALU2OP (&$3, &$5, 8);
+ else
+ return yyerror ("Bad registers for DIVQ");
+ }
+
+ | DIVS LPAREN REG COMMA REG RPAREN
+ {
+ if (IS_DREG ($3) && IS_DREG ($5))
+ $$ = ALU2OP (&$3, &$5, 9);
+ else
+ return yyerror ("Bad registers for DIVS");
+ }
+
+ | REG ASSIGN MINUS REG vsmod
+ {
+ if (IS_DREG ($1) && IS_DREG ($4))
+ {
+ if ($5.r0 == 0 && $5.s0 == 0 && $5.aop == 0)
+ {
+ notethat ("ALU2op: dregs = - dregs\n");
+ $$ = ALU2OP (&$1, &$4, 14);
+ }
+ else if ($5.r0 == 1 && $5.s0 == 0 && $5.aop == 3)
+ {
+ notethat ("dsp32alu: dregs = - dregs (.)\n");
+ $$ = DSP32ALU (15, 0, 0, &$1, &$4, 0, $5.s0, 0, 3);
+ }
+ else
+ {
+ notethat ("dsp32alu: dregs = - dregs (.)\n");
+ $$ = DSP32ALU (7, 0, 0, &$1, &$4, 0, $5.s0, 0, 3);
+ }
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG ASSIGN TILDA REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($4))
+ {
+ notethat ("ALU2op: dregs = ~dregs\n");
+ $$ = ALU2OP (&$1, &$4, 15);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG _GREATER_GREATER_ASSIGN REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ notethat ("ALU2op: dregs >>= dregs\n");
+ $$ = ALU2OP (&$1, &$3, 1);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG _GREATER_GREATER_ASSIGN expr
+ {
+ if (IS_DREG ($1) && IS_UIMM ($3, 5))
+ {
+ notethat ("LOGI2op: dregs >>= uimm5\n");
+ $$ = LOGI2OP ($1, uimm5 ($3), 6);
+ }
+ else
+ return yyerror ("Dregs expected or value error");
+ }
+
+ | REG _GREATER_GREATER_GREATER_THAN_ASSIGN REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ notethat ("ALU2op: dregs >>>= dregs\n");
+ $$ = ALU2OP (&$1, &$3, 0);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG _LESS_LESS_ASSIGN REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ notethat ("ALU2op: dregs <<= dregs\n");
+ $$ = ALU2OP (&$1, &$3, 2);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+ | REG _LESS_LESS_ASSIGN expr
+ {
+ if (IS_DREG ($1) && IS_UIMM ($3, 5))
+ {
+ notethat ("LOGI2op: dregs <<= uimm5\n");
+ $$ = LOGI2OP ($1, uimm5 ($3), 7);
+ }
+ else
+ return yyerror ("Dregs expected or const value error");
+ }
+
+
+ | REG _GREATER_GREATER_GREATER_THAN_ASSIGN expr
+ {
+ if (IS_DREG ($1) && IS_UIMM ($3, 5))
+ {
+ notethat ("LOGI2op: dregs >>>= uimm5\n");
+ $$ = LOGI2OP ($1, uimm5 ($3), 5);
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+
+/* Cache Control. */
+
+ | FLUSH LBRACK REG RBRACK
+ {
+ notethat ("CaCTRL: FLUSH [ pregs ]\n");
+ if (IS_PREG ($3))
+ $$ = CACTRL (&$3, 0, 2);
+ else
+ return yyerror ("Bad register(s) for FLUSH");
+ }
+
+ | FLUSH reg_with_postinc
+ {
+ if (IS_PREG ($2))
+ {
+ notethat ("CaCTRL: FLUSH [ pregs ++ ]\n");
+ $$ = CACTRL (&$2, 1, 2);
+ }
+ else
+ return yyerror ("Bad register(s) for FLUSH");
+ }
+
+ | FLUSHINV LBRACK REG RBRACK
+ {
+ if (IS_PREG ($3))
+ {
+ notethat ("CaCTRL: FLUSHINV [ pregs ]\n");
+ $$ = CACTRL (&$3, 0, 1);
+ }
+ else
+ return yyerror ("Bad register(s) for FLUSH");
+ }
+
+ | FLUSHINV reg_with_postinc
+ {
+ if (IS_PREG ($2))
+ {
+ notethat ("CaCTRL: FLUSHINV [ pregs ++ ]\n");
+ $$ = CACTRL (&$2, 1, 1);
+ }
+ else
+ return yyerror ("Bad register(s) for FLUSH");
+ }
+
+/* CaCTRL: IFLUSH [pregs]. */
+ | IFLUSH LBRACK REG RBRACK
+ {
+ if (IS_PREG ($3))
+ {
+ notethat ("CaCTRL: IFLUSH [ pregs ]\n");
+ $$ = CACTRL (&$3, 0, 3);
+ }
+ else
+ return yyerror ("Bad register(s) for FLUSH");
+ }
+
+ | IFLUSH reg_with_postinc
+ {
+ if (IS_PREG ($2))
+ {
+ notethat ("CaCTRL: IFLUSH [ pregs ++ ]\n");
+ $$ = CACTRL (&$2, 1, 3);
+ }
+ else
+ return yyerror ("Bad register(s) for FLUSH");
+ }
+
+ | PREFETCH LBRACK REG RBRACK
+ {
+ if (IS_PREG ($3))
+ {
+ notethat ("CaCTRL: PREFETCH [ pregs ]\n");
+ $$ = CACTRL (&$3, 0, 0);
+ }
+ else
+ return yyerror ("Bad register(s) for PREFETCH");
+ }
+
+ | PREFETCH reg_with_postinc
+ {
+ if (IS_PREG ($2))
+ {
+ notethat ("CaCTRL: PREFETCH [ pregs ++ ]\n");
+ $$ = CACTRL (&$2, 1, 0);
+ }
+ else
+ return yyerror ("Bad register(s) for PREFETCH");
+ }
+
+/* LOAD/STORE. */
+/* LDST: B [ pregs <post_op> ] = dregs. */
+
+ | B LBRACK REG post_op RBRACK ASSIGN REG
+ {
+ if (!IS_DREG ($7))
+ return yyerror ("Dreg expected for source operand");
+ if (!IS_PREG ($3))
+ return yyerror ("Preg expected in address");
+
+ notethat ("LDST: B [ pregs <post_op> ] = dregs\n");
+ $$ = LDST (&$3, &$7, $4.x0, 2, 0, 1);
+ }
+
+/* LDSTidxI: B [ pregs + imm16 ] = dregs. */
+ | B LBRACK REG plus_minus expr RBRACK ASSIGN REG
+ {
+ Expr_Node *tmp = $5;
+
+ if (!IS_DREG ($8))
+ return yyerror ("Dreg expected for source operand");
+ if (!IS_PREG ($3))
+ return yyerror ("Preg expected in address");
+
+ if (IS_RELOC ($5))
+ return yyerror ("Plain symbol used as offset");
+
+ if ($4.r0)
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+
+ if (in_range_p (tmp, -32768, 32767, 0))
+ {
+ notethat ("LDST: B [ pregs + imm16 ] = dregs\n");
+ $$ = LDSTIDXI (&$3, &$8, 1, 2, 0, $5);
+ }
+ else
+ return yyerror ("Displacement out of range");
+ }
+
+
+/* LDSTii: W [ pregs + uimm4s2 ] = dregs. */
+ | W LBRACK REG plus_minus expr RBRACK ASSIGN REG
+ {
+ Expr_Node *tmp = $5;
+
+ if (!IS_DREG ($8))
+ return yyerror ("Dreg expected for source operand");
+ if (!IS_PREG ($3))
+ return yyerror ("Preg expected in address");
+
+ if ($4.r0)
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+
+ if (IS_RELOC ($5))
+ return yyerror ("Plain symbol used as offset");
+
+ if (in_range_p (tmp, 0, 30, 1))
+ {
+ notethat ("LDSTii: W [ pregs +- uimm5m2 ] = dregs\n");
+ $$ = LDSTII (&$3, &$8, tmp, 1, 1);
+ }
+ else if (in_range_p (tmp, -65536, 65535, 1))
+ {
+ notethat ("LDSTidxI: W [ pregs + imm17m2 ] = dregs\n");
+ $$ = LDSTIDXI (&$3, &$8, 1, 1, 0, tmp);
+ }
+ else
+ return yyerror ("Displacement out of range");
+ }
+
+/* LDST: W [ pregs <post_op> ] = dregs. */
+ | W LBRACK REG post_op RBRACK ASSIGN REG
+ {
+ if (!IS_DREG ($7))
+ return yyerror ("Dreg expected for source operand");
+ if (!IS_PREG ($3))
+ return yyerror ("Preg expected in address");
+
+ notethat ("LDST: W [ pregs <post_op> ] = dregs\n");
+ $$ = LDST (&$3, &$7, $4.x0, 1, 0, 1);
+ }
+
+ | W LBRACK REG post_op RBRACK ASSIGN HALF_REG
+ {
+ if (!IS_DREG ($7))
+ return yyerror ("Dreg expected for source operand");
+ if ($4.x0 == 2)
+ {
+ if (!IS_IREG ($3) && !IS_PREG ($3))
+ return yyerror ("Ireg or Preg expected in address");
+ }
+ else if (!IS_IREG ($3))
+ return yyerror ("Ireg expected in address");
+
+ if (IS_IREG ($3))
+ {
+ notethat ("dspLDST: W [ iregs <post_op> ] = dregs_half\n");
+ $$ = DSPLDST (&$3, 1 + IS_H ($7), &$7, $4.x0, 1);
+ }
+ else
+ {
+ notethat ("LDSTpmod: W [ pregs ] = dregs_half\n");
+ $$ = LDSTPMOD (&$3, &$7, &$3, 1 + IS_H ($7), 1);
+ }
+ }
+
+/* LDSTiiFP: [ FP - const ] = dpregs. */
+ | LBRACK REG plus_minus expr RBRACK ASSIGN REG
+ {
+ Expr_Node *tmp = $4;
+ int ispreg = IS_PREG ($7);
+
+ if (!IS_PREG ($2))
+ return yyerror ("Preg expected in address");
+
+ if (!IS_DREG ($7) && !ispreg)
+ return yyerror ("Preg expected for source operand");
+
+ if ($3.r0)
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+
+ if (IS_RELOC ($4))
+ return yyerror ("Plain symbol used as offset");
+
+ if (in_range_p (tmp, 0, 63, 3))
+ {
+ notethat ("LDSTii: dpregs = [ pregs + uimm6m4 ]\n");
+ $$ = LDSTII (&$2, &$7, tmp, 1, ispreg ? 3 : 0);
+ }
+ else if ($2.regno == REG_FP && in_range_p (tmp, -128, 0, 3))
+ {
+ notethat ("LDSTiiFP: dpregs = [ FP - uimm7m4 ]\n");
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+ $$ = LDSTIIFP (tmp, &$7, 1);
+ }
+ else if (in_range_p (tmp, -131072, 131071, 3))
+ {
+ notethat ("LDSTidxI: [ pregs + imm18m4 ] = dpregs\n");
+ $$ = LDSTIDXI (&$2, &$7, 1, 0, ispreg ? 1 : 0, tmp);
+ }
+ else
+ return yyerror ("Displacement out of range");
+ }
+
+ | REG ASSIGN W LBRACK REG plus_minus expr RBRACK xpmod
+ {
+ Expr_Node *tmp = $7;
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+ if (!IS_PREG ($5))
+ return yyerror ("Preg expected in address");
+
+ if ($6.r0)
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+
+ if (IS_RELOC ($7))
+ return yyerror ("Plain symbol used as offset");
+
+ if (in_range_p (tmp, 0, 30, 1))
+ {
+ notethat ("LDSTii: dregs = W [ pregs + uimm5m2 ] (.)\n");
+ $$ = LDSTII (&$5, &$1, tmp, 0, 1 << $9.r0);
+ }
+ else if (in_range_p (tmp, -65536, 65535, 1))
+ {
+ notethat ("LDSTidxI: dregs = W [ pregs + imm17m2 ] (.)\n");
+ $$ = LDSTIDXI (&$5, &$1, 0, 1, $9.r0, tmp);
+ }
+ else
+ return yyerror ("Displacement out of range");
+ }
+
+ | HALF_REG ASSIGN W LBRACK REG post_op RBRACK
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for source operand");
+ if ($6.x0 == 2)
+ {
+ if (!IS_IREG ($5) && !IS_PREG ($5))
+ return yyerror ("Ireg or Preg expected in address");
+ }
+ else if (!IS_IREG ($5))
+ return yyerror ("Ireg expected in address");
+
+ if (IS_IREG ($5))
+ {
+ notethat ("dspLDST: dregs_half = W [ iregs <post_op> ]\n");
+ $$ = DSPLDST(&$5, 1 + IS_H ($1), &$1, $6.x0, 0);
+ }
+ else
+ {
+ notethat ("LDSTpmod: dregs_half = W [ pregs <post_op> ]\n");
+ $$ = LDSTPMOD (&$5, &$1, &$5, 1 + IS_H ($1), 0);
+ }
+ }
+
+
+ | REG ASSIGN W LBRACK REG post_op RBRACK xpmod
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+ if (!IS_PREG ($5))
+ return yyerror ("Preg expected in address");
+
+ notethat ("LDST: dregs = W [ pregs <post_op> ] (.)\n");
+ $$ = LDST (&$5, &$1, $6.x0, 1, $8.r0, 0);
+ }
+
+ | REG ASSIGN W LBRACK REG _PLUS_PLUS REG RBRACK xpmod
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+ if (!IS_PREG ($5) || !IS_PREG ($7))
+ return yyerror ("Preg expected in address");
+
+ notethat ("LDSTpmod: dregs = W [ pregs ++ pregs ] (.)\n");
+ $$ = LDSTPMOD (&$5, &$1, &$7, 3, $9.r0);
+ }
+
+ | HALF_REG ASSIGN W LBRACK REG _PLUS_PLUS REG RBRACK
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+ if (!IS_PREG ($5) || !IS_PREG ($7))
+ return yyerror ("Preg expected in address");
+
+ notethat ("LDSTpmod: dregs_half = W [ pregs ++ pregs ]\n");
+ $$ = LDSTPMOD (&$5, &$1, &$7, 1 + IS_H ($1), 0);
+ }
+
+ | LBRACK REG post_op RBRACK ASSIGN REG
+ {
+ if (!IS_IREG ($2) && !IS_PREG ($2))
+ return yyerror ("Ireg or Preg expected in address");
+ else if (IS_IREG ($2) && !IS_DREG ($6))
+ return yyerror ("Dreg expected for source operand");
+ else if (IS_PREG ($2) && !IS_DREG ($6) && !IS_PREG ($6))
+ return yyerror ("Dreg or Preg expected for source operand");
+
+ if (IS_IREG ($2))
+ {
+ notethat ("dspLDST: [ iregs <post_op> ] = dregs\n");
+ $$ = DSPLDST(&$2, 0, &$6, $3.x0, 1);
+ }
+ else if (IS_DREG ($6))
+ {
+ notethat ("LDST: [ pregs <post_op> ] = dregs\n");
+ $$ = LDST (&$2, &$6, $3.x0, 0, 0, 1);
+ }
+ else
+ {
+ notethat ("LDST: [ pregs <post_op> ] = pregs\n");
+ $$ = LDST (&$2, &$6, $3.x0, 0, 1, 1);
+ }
+ }
+
+ | LBRACK REG _PLUS_PLUS REG RBRACK ASSIGN REG
+ {
+ if (!IS_DREG ($7))
+ return yyerror ("Dreg expected for source operand");
+
+ if (IS_IREG ($2) && IS_MREG ($4))
+ {
+ notethat ("dspLDST: [ iregs ++ mregs ] = dregs\n");
+ $$ = DSPLDST(&$2, $4.regno & CODE_MASK, &$7, 3, 1);
+ }
+ else if (IS_PREG ($2) && IS_PREG ($4))
+ {
+ notethat ("LDSTpmod: [ pregs ++ pregs ] = dregs\n");
+ $$ = LDSTPMOD (&$2, &$7, &$4, 0, 1);
+ }
+ else
+ return yyerror ("Preg ++ Preg or Ireg ++ Mreg expected in address");
+ }
+
+ | W LBRACK REG _PLUS_PLUS REG RBRACK ASSIGN HALF_REG
+ {
+ if (!IS_DREG ($8))
+ return yyerror ("Dreg expected for source operand");
+
+ if (IS_PREG ($3) && IS_PREG ($5))
+ {
+ notethat ("LDSTpmod: W [ pregs ++ pregs ] = dregs_half\n");
+ $$ = LDSTPMOD (&$3, &$8, &$5, 1 + IS_H ($8), 1);
+ }
+ else
+ return yyerror ("Preg ++ Preg expected in address");
+ }
+
+ | REG ASSIGN B LBRACK REG plus_minus expr RBRACK xpmod
+ {
+ Expr_Node *tmp = $7;
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+ if (!IS_PREG ($5))
+ return yyerror ("Preg expected in address");
+
+ if ($6.r0)
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+
+ if (IS_RELOC ($7))
+ return yyerror ("Plain symbol used as offset");
+
+ if (in_range_p (tmp, -32768, 32767, 0))
+ {
+ notethat ("LDSTidxI: dregs = B [ pregs + imm16 ] (%c)\n",
+ $9.r0 ? 'X' : 'Z');
+ $$ = LDSTIDXI (&$5, &$1, 0, 2, $9.r0, tmp);
+ }
+ else
+ return yyerror ("Displacement out of range");
+ }
+
+ | REG ASSIGN B LBRACK REG post_op RBRACK xpmod
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+ if (!IS_PREG ($5))
+ return yyerror ("Preg expected in address");
+
+ notethat ("LDST: dregs = B [ pregs <post_op> ] (%c)\n",
+ $8.r0 ? 'X' : 'Z');
+ $$ = LDST (&$5, &$1, $6.x0, 2, $8.r0, 0);
+ }
+
+ | REG ASSIGN LBRACK REG _PLUS_PLUS REG RBRACK
+ {
+ if (!IS_DREG ($1))
+ return yyerror ("Dreg expected for destination operand");
+
+ if (IS_IREG ($4) && IS_MREG ($6))
+ {
+ notethat ("dspLDST: dregs = [ iregs ++ mregs ]\n");
+ $$ = DSPLDST(&$4, $6.regno & CODE_MASK, &$1, 3, 0);
+ }
+ else if (IS_PREG ($4) && IS_PREG ($6))
+ {
+ notethat ("LDSTpmod: dregs = [ pregs ++ pregs ]\n");
+ $$ = LDSTPMOD (&$4, &$1, &$6, 0, 0);
+ }
+ else
+ return yyerror ("Preg ++ Preg or Ireg ++ Mreg expected in address");
+ }
+
+ | REG ASSIGN LBRACK REG plus_minus got_or_expr RBRACK
+ {
+ Expr_Node *tmp = $6;
+ int ispreg = IS_PREG ($1);
+ int isgot = IS_RELOC($6);
+
+ if (!IS_PREG ($4))
+ return yyerror ("Preg expected in address");
+
+ if (!IS_DREG ($1) && !ispreg)
+ return yyerror ("Dreg or Preg expected for destination operand");
+
+ if (tmp->type == Expr_Node_Reloc
+ && strcmp (tmp->value.s_value,
+ "_current_shared_library_p5_offset_") != 0)
+ return yyerror ("Plain symbol used as offset");
+
+ if ($5.r0)
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+
+ if (isgot)
+ {
+ notethat ("LDSTidxI: dpregs = [ pregs + sym@got ]\n");
+ $$ = LDSTIDXI (&$4, &$1, 0, 0, ispreg ? 1 : 0, tmp);
+ }
+ else if (in_range_p (tmp, 0, 63, 3))
+ {
+ notethat ("LDSTii: dpregs = [ pregs + uimm7m4 ]\n");
+ $$ = LDSTII (&$4, &$1, tmp, 0, ispreg ? 3 : 0);
+ }
+ else if ($4.regno == REG_FP && in_range_p (tmp, -128, 0, 3))
+ {
+ notethat ("LDSTiiFP: dpregs = [ FP - uimm7m4 ]\n");
+ tmp = unary (Expr_Op_Type_NEG, tmp);
+ $$ = LDSTIIFP (tmp, &$1, 0);
+ }
+ else if (in_range_p (tmp, -131072, 131071, 3))
+ {
+ notethat ("LDSTidxI: dpregs = [ pregs + imm18m4 ]\n");
+ $$ = LDSTIDXI (&$4, &$1, 0, 0, ispreg ? 1 : 0, tmp);
+
+ }
+ else
+ return yyerror ("Displacement out of range");
+ }
+
+ | REG ASSIGN LBRACK REG post_op RBRACK
+ {
+ if (!IS_IREG ($4) && !IS_PREG ($4))
+ return yyerror ("Ireg or Preg expected in address");
+ else if (IS_IREG ($4) && !IS_DREG ($1))
+ return yyerror ("Dreg expected in destination operand");
+ else if (IS_PREG ($4) && !IS_DREG ($1) && !IS_PREG ($1)
+ && ($4.regno != REG_SP || !IS_ALLREG ($1) || $5.x0 != 0))
+ return yyerror ("Dreg or Preg expected in destination operand");
+
+ if (IS_IREG ($4))
+ {
+ notethat ("dspLDST: dregs = [ iregs <post_op> ]\n");
+ $$ = DSPLDST (&$4, 0, &$1, $5.x0, 0);
+ }
+ else if (IS_DREG ($1))
+ {
+ notethat ("LDST: dregs = [ pregs <post_op> ]\n");
+ $$ = LDST (&$4, &$1, $5.x0, 0, 0, 0);
+ }
+ else if (IS_PREG ($1))
+ {
+ if (REG_SAME ($1, $4) && $5.x0 != 2)
+ return yyerror ("Pregs can't be same");
+
+ notethat ("LDST: pregs = [ pregs <post_op> ]\n");
+ $$ = LDST (&$4, &$1, $5.x0, 0, 1, 0);
+ }
+ else
+ {
+ notethat ("PushPopReg: allregs = [ SP ++ ]\n");
+ $$ = PUSHPOPREG (&$1, 0);
+ }
+ }
+
+
+/* PushPopMultiple. */
+ | reg_with_predec ASSIGN LPAREN REG COLON expr COMMA REG COLON expr RPAREN
+ {
+ if ($1.regno != REG_SP)
+ yyerror ("Stack Pointer expected");
+ if ($4.regno == REG_R7
+ && IN_RANGE ($6, 0, 7)
+ && $8.regno == REG_P5
+ && IN_RANGE ($10, 0, 5))
+ {
+ notethat ("PushPopMultiple: [ -- SP ] = (R7 : reglim , P5 : reglim )\n");
+ $$ = PUSHPOPMULTIPLE (imm5 ($6), imm5 ($10), 1, 1, 1);
+ }
+ else
+ return yyerror ("Bad register for PushPopMultiple");
+ }
+
+ | reg_with_predec ASSIGN LPAREN REG COLON expr RPAREN
+ {
+ if ($1.regno != REG_SP)
+ yyerror ("Stack Pointer expected");
+
+ if ($4.regno == REG_R7 && IN_RANGE ($6, 0, 7))
+ {
+ notethat ("PushPopMultiple: [ -- SP ] = (R7 : reglim )\n");
+ $$ = PUSHPOPMULTIPLE (imm5 ($6), 0, 1, 0, 1);
+ }
+ else if ($4.regno == REG_P5 && IN_RANGE ($6, 0, 6))
+ {
+ notethat ("PushPopMultiple: [ -- SP ] = (P5 : reglim )\n");
+ $$ = PUSHPOPMULTIPLE (0, imm5 ($6), 0, 1, 1);
+ }
+ else
+ return yyerror ("Bad register for PushPopMultiple");
+ }
+
+ | LPAREN REG COLON expr COMMA REG COLON expr RPAREN ASSIGN reg_with_postinc
+ {
+ if ($11.regno != REG_SP)
+ yyerror ("Stack Pointer expected");
+ if ($2.regno == REG_R7 && (IN_RANGE ($4, 0, 7))
+ && $6.regno == REG_P5 && (IN_RANGE ($8, 0, 6)))
+ {
+ notethat ("PushPopMultiple: (R7 : reglim , P5 : reglim ) = [ SP ++ ]\n");
+ $$ = PUSHPOPMULTIPLE (imm5 ($4), imm5 ($8), 1, 1, 0);
+ }
+ else
+ return yyerror ("Bad register range for PushPopMultiple");
+ }
+
+ | LPAREN REG COLON expr RPAREN ASSIGN reg_with_postinc
+ {
+ if ($7.regno != REG_SP)
+ yyerror ("Stack Pointer expected");
+
+ if ($2.regno == REG_R7 && IN_RANGE ($4, 0, 7))
+ {
+ notethat ("PushPopMultiple: (R7 : reglim ) = [ SP ++ ]\n");
+ $$ = PUSHPOPMULTIPLE (imm5 ($4), 0, 1, 0, 0);
+ }
+ else if ($2.regno == REG_P5 && IN_RANGE ($4, 0, 6))
+ {
+ notethat ("PushPopMultiple: (P5 : reglim ) = [ SP ++ ]\n");
+ $$ = PUSHPOPMULTIPLE (0, imm5 ($4), 0, 1, 0);
+ }
+ else
+ return yyerror ("Bad register range for PushPopMultiple");
+ }
+
+ | reg_with_predec ASSIGN REG
+ {
+ if ($1.regno != REG_SP)
+ yyerror ("Stack Pointer expected");
+
+ if (IS_ALLREG ($3))
+ {
+ notethat ("PushPopReg: [ -- SP ] = allregs\n");
+ $$ = PUSHPOPREG (&$3, 1);
+ }
+ else
+ return yyerror ("Bad register for PushPopReg");
+ }
+
+/* Linkage. */
+
+ | LINK expr
+ {
+ if (IS_URANGE (16, $2, 0, 4))
+ $$ = LINKAGE (0, uimm16s4 ($2));
+ else
+ return yyerror ("Bad constant for LINK");
+ }
+
+ | UNLINK
+ {
+ notethat ("linkage: UNLINK\n");
+ $$ = LINKAGE (1, 0);
+ }
+
+
+/* LSETUP. */
+
+ | LSETUP LPAREN expr COMMA expr RPAREN REG
+ {
+ if (IS_PCREL4 ($3) && IS_LPPCREL10 ($5) && IS_CREG ($7))
+ {
+ notethat ("LoopSetup: LSETUP (pcrel4 , lppcrel10 ) counters\n");
+ $$ = LOOPSETUP ($3, &$7, 0, $5, 0);
+ }
+ else
+ return yyerror ("Bad register or values for LSETUP");
+
+ }
+ | LSETUP LPAREN expr COMMA expr RPAREN REG ASSIGN REG
+ {
+ if (IS_PCREL4 ($3) && IS_LPPCREL10 ($5)
+ && IS_PREG ($9) && IS_CREG ($7))
+ {
+ notethat ("LoopSetup: LSETUP (pcrel4 , lppcrel10 ) counters = pregs\n");
+ $$ = LOOPSETUP ($3, &$7, 1, $5, &$9);
+ }
+ else
+ return yyerror ("Bad register or values for LSETUP");
+ }
+
+ | LSETUP LPAREN expr COMMA expr RPAREN REG ASSIGN REG GREATER_GREATER expr
+ {
+ if (IS_PCREL4 ($3) && IS_LPPCREL10 ($5)
+ && IS_PREG ($9) && IS_CREG ($7)
+ && EXPR_VALUE ($11) == 1)
+ {
+ notethat ("LoopSetup: LSETUP (pcrel4 , lppcrel10 ) counters = pregs >> 1\n");
+ $$ = LOOPSETUP ($3, &$7, 3, $5, &$9);
+ }
+ else
+ return yyerror ("Bad register or values for LSETUP");
+ }
+
+/* LOOP. */
+ | LOOP expr REG
+ {
+ if (!IS_RELOC ($2))
+ return yyerror ("Invalid expression in loop statement");
+ if (!IS_CREG ($3))
+ return yyerror ("Invalid loop counter register");
+ $$ = bfin_gen_loop ($2, &$3, 0, 0);
+ }
+ | LOOP expr REG ASSIGN REG
+ {
+ if (IS_RELOC ($2) && IS_PREG ($5) && IS_CREG ($3))
+ {
+ notethat ("Loop: LOOP expr counters = pregs\n");
+ $$ = bfin_gen_loop ($2, &$3, 1, &$5);
+ }
+ else
+ return yyerror ("Bad register or values for LOOP");
+ }
+ | LOOP expr REG ASSIGN REG GREATER_GREATER expr
+ {
+ if (IS_RELOC ($2) && IS_PREG ($5) && IS_CREG ($3) && EXPR_VALUE ($7) == 1)
+ {
+ notethat ("Loop: LOOP expr counters = pregs >> 1\n");
+ $$ = bfin_gen_loop ($2, &$3, 3, &$5);
+ }
+ else
+ return yyerror ("Bad register or values for LOOP");
+ }
+
+/* LOOP_BEGIN. */
+ | LOOP_BEGIN NUMBER
+ {
+ Expr_Node_Value val;
+ val.i_value = $2;
+ Expr_Node *tmp = Expr_Node_Create (Expr_Node_Constant, val, NULL, NULL);
+ bfin_loop_attempt_create_label (tmp, 1);
+ if (!IS_RELOC (tmp))
+ return yyerror ("Invalid expression in LOOP_BEGIN statement");
+ bfin_loop_beginend (tmp, 1);
+ $$ = 0;
+ }
+ | LOOP_BEGIN expr
+ {
+ if (!IS_RELOC ($2))
+ return yyerror ("Invalid expression in LOOP_BEGIN statement");
+
+ bfin_loop_beginend ($2, 1);
+ $$ = 0;
+ }
+
+/* LOOP_END. */
+ | LOOP_END NUMBER
+ {
+ Expr_Node_Value val;
+ val.i_value = $2;
+ Expr_Node *tmp = Expr_Node_Create (Expr_Node_Constant, val, NULL, NULL);
+ bfin_loop_attempt_create_label (tmp, 1);
+ if (!IS_RELOC (tmp))
+ return yyerror ("Invalid expression in LOOP_END statement");
+ bfin_loop_beginend (tmp, 0);
+ $$ = 0;
+ }
+ | LOOP_END expr
+ {
+ if (!IS_RELOC ($2))
+ return yyerror ("Invalid expression in LOOP_END statement");
+
+ bfin_loop_beginend ($2, 0);
+ $$ = 0;
+ }
+
+/* pseudoDEBUG. */
+
+ | ABORT
+ {
+ notethat ("psedoDEBUG: ABORT\n");
+ $$ = bfin_gen_pseudodbg (3, 3, 0);
+ }
+
+ | DBG
+ {
+ notethat ("pseudoDEBUG: DBG\n");
+ $$ = bfin_gen_pseudodbg (3, 7, 0);
+ }
+ | DBG REG_A
+ {
+ notethat ("pseudoDEBUG: DBG REG_A\n");
+ $$ = bfin_gen_pseudodbg (3, IS_A1 ($2), 0);
+ }
+ | DBG REG
+ {
+ notethat ("pseudoDEBUG: DBG allregs\n");
+ $$ = bfin_gen_pseudodbg (0, $2.regno & CODE_MASK, ($2.regno & CLASS_MASK) >> 4);
+ }
+
+ | DBGCMPLX LPAREN REG RPAREN
+ {
+ if (!IS_DREG ($3))
+ return yyerror ("Dregs expected");
+ notethat ("pseudoDEBUG: DBGCMPLX (dregs )\n");
+ $$ = bfin_gen_pseudodbg (3, 6, ($3.regno & CODE_MASK) >> 4);
+ }
+
+ | DBGHALT
+ {
+ notethat ("psedoDEBUG: DBGHALT\n");
+ $$ = bfin_gen_pseudodbg (3, 5, 0);
+ }
+
+ | HLT
+ {
+ notethat ("psedoDEBUG: HLT\n");
+ $$ = bfin_gen_pseudodbg (3, 4, 0);
+ }
+
+ | DBGA LPAREN HALF_REG COMMA expr RPAREN
+ {
+ notethat ("pseudodbg_assert: DBGA (regs_lo/hi , uimm16 )\n");
+ $$ = bfin_gen_pseudodbg_assert (IS_H ($3), &$3, uimm16 ($5));
+ }
+
+ | DBGAH LPAREN REG COMMA expr RPAREN
+ {
+ notethat ("pseudodbg_assert: DBGAH (regs , uimm16 )\n");
+ $$ = bfin_gen_pseudodbg_assert (3, &$3, uimm16 ($5));
+ }
+
+ | DBGAL LPAREN REG COMMA expr RPAREN
+ {
+ notethat ("psedodbg_assert: DBGAL (regs , uimm16 )\n");
+ $$ = bfin_gen_pseudodbg_assert (2, &$3, uimm16 ($5));
+ }
+
+ | OUTC expr
+ {
+ if (!IS_UIMM ($2, 8))
+ return yyerror ("Constant out of range");
+ notethat ("psedodbg_assert: OUTC uimm8\n");
+ $$ = bfin_gen_pseudochr (uimm8 ($2));
+ }
+
+ | OUTC REG
+ {
+ if (!IS_DREG ($2))
+ return yyerror ("Dregs expected");
+ notethat ("psedodbg_assert: OUTC dreg\n");
+ $$ = bfin_gen_pseudodbg (2, $2.regno & CODE_MASK, 0);
+ }
+
+;
+
+/* AUX RULES. */
+
+/* Register rules. */
+
+REG_A: REG_A_DOUBLE_ZERO
+ {
+ $$ = $1;
+ }
+ | REG_A_DOUBLE_ONE
+ {
+ $$ = $1;
+ }
+ ;
+
+
+/* Modifiers. */
+
+opt_mode:
+ {
+ $$.MM = 0;
+ $$.mod = 0;
+ }
+ | LPAREN M COMMA MMOD RPAREN
+ {
+ $$.MM = 1;
+ $$.mod = $4;
+ }
+ | LPAREN MMOD COMMA M RPAREN
+ {
+ $$.MM = 1;
+ $$.mod = $2;
+ }
+ | LPAREN MMOD RPAREN
+ {
+ $$.MM = 0;
+ $$.mod = $2;
+ }
+ | LPAREN M RPAREN
+ {
+ $$.MM = 1;
+ $$.mod = 0;
+ }
+ ;
+
+asr_asl: LPAREN ASL RPAREN
+ {
+ $$.r0 = 1;
+ }
+ | LPAREN ASR RPAREN
+ {
+ $$.r0 = 0;
+ }
+ ;
+
+sco:
+ {
+ $$.s0 = 0;
+ $$.x0 = 0;
+ }
+ | S
+ {
+ $$.s0 = 1;
+ $$.x0 = 0;
+ }
+ | CO
+ {
+ $$.s0 = 0;
+ $$.x0 = 1;
+ }
+ | SCO
+ {
+ $$.s0 = 1;
+ $$.x0 = 1;
+ }
+ ;
+
+asr_asl_0:
+ ASL
+ {
+ $$.r0 = 1;
+ }
+ | ASR
+ {
+ $$.r0 = 0;
+ }
+ ;
+
+amod0:
+ {
+ $$.s0 = 0;
+ $$.x0 = 0;
+ }
+ | LPAREN sco RPAREN
+ {
+ $$.s0 = $2.s0;
+ $$.x0 = $2.x0;
+ }
+ ;
+
+amod1:
+ {
+ $$.s0 = 0;
+ $$.x0 = 0;
+ $$.aop = 0;
+ }
+ | LPAREN NS RPAREN
+ {
+ $$.s0 = 0;
+ $$.x0 = 0;
+ $$.aop = 1;
+ }
+ | LPAREN S RPAREN
+ {
+ $$.s0 = 1;
+ $$.x0 = 0;
+ $$.aop = 1;
+ }
+ ;
+
+amod2:
+ {
+ $$.r0 = 0;
+ $$.s0 = 0;
+ $$.x0 = 0;
+ }
+ | LPAREN asr_asl_0 RPAREN
+ {
+ $$.r0 = 2 + $2.r0;
+ $$.s0 = 0;
+ $$.x0 = 0;
+ }
+ | LPAREN sco RPAREN
+ {
+ $$.r0 = 0;
+ $$.s0 = $2.s0;
+ $$.x0 = $2.x0;
+ }
+ | LPAREN asr_asl_0 COMMA sco RPAREN
+ {
+ $$.r0 = 2 + $2.r0;
+ $$.s0 = $4.s0;
+ $$.x0 = $4.x0;
+ }
+ | LPAREN sco COMMA asr_asl_0 RPAREN
+ {
+ $$.r0 = 2 + $4.r0;
+ $$.s0 = $2.s0;
+ $$.x0 = $2.x0;
+ }
+ ;
+
+xpmod:
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN Z RPAREN
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN X RPAREN
+ {
+ $$.r0 = 1;
+ }
+ ;
+
+xpmod1:
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN X RPAREN
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN Z RPAREN
+ {
+ $$.r0 = 1;
+ }
+ ;
+
+vsmod:
+ {
+ $$.r0 = 0;
+ $$.s0 = 0;
+ $$.aop = 0;
+ }
+ | LPAREN NS RPAREN
+ {
+ $$.r0 = 0;
+ $$.s0 = 0;
+ $$.aop = 3;
+ }
+ | LPAREN S RPAREN
+ {
+ $$.r0 = 0;
+ $$.s0 = 1;
+ $$.aop = 3;
+ }
+ | LPAREN V RPAREN
+ {
+ $$.r0 = 1;
+ $$.s0 = 0;
+ $$.aop = 3;
+ }
+ | LPAREN V COMMA S RPAREN
+ {
+ $$.r0 = 1;
+ $$.s0 = 1;
+ }
+ | LPAREN S COMMA V RPAREN
+ {
+ $$.r0 = 1;
+ $$.s0 = 1;
+ }
+ ;
+
+vmod:
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN V RPAREN
+ {
+ $$.r0 = 1;
+ }
+ ;
+
+smod:
+ {
+ $$.s0 = 0;
+ }
+ | LPAREN S RPAREN
+ {
+ $$.s0 = 1;
+ }
+ ;
+
+searchmod:
+ GE
+ {
+ $$.r0 = 1;
+ }
+ | GT
+ {
+ $$.r0 = 0;
+ }
+ | LE
+ {
+ $$.r0 = 3;
+ }
+ | LT
+ {
+ $$.r0 = 2;
+ }
+ ;
+
+aligndir:
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN R RPAREN
+ {
+ $$.r0 = 1;
+ }
+ ;
+
+byteop_mod:
+ LPAREN R RPAREN
+ {
+ $$.r0 = 0;
+ $$.s0 = 1;
+ }
+ | LPAREN MMOD RPAREN
+ {
+ if ($2 != M_T)
+ return yyerror ("Bad modifier");
+ $$.r0 = 1;
+ $$.s0 = 0;
+ }
+ | LPAREN MMOD COMMA R RPAREN
+ {
+ if ($2 != M_T)
+ return yyerror ("Bad modifier");
+ $$.r0 = 1;
+ $$.s0 = 1;
+ }
+ | LPAREN R COMMA MMOD RPAREN
+ {
+ if ($4 != M_T)
+ return yyerror ("Bad modifier");
+ $$.r0 = 1;
+ $$.s0 = 1;
+ }
+ ;
+
+
+
+c_align:
+ ALIGN8
+ {
+ $$.r0 = 0;
+ }
+ | ALIGN16
+ {
+ $$.r0 = 1;
+ }
+ | ALIGN24
+ {
+ $$.r0 = 2;
+ }
+ ;
+
+w32_or_nothing:
+ {
+ $$.r0 = 0;
+ }
+ | LPAREN MMOD RPAREN
+ {
+ if ($2 == M_W32)
+ $$.r0 = 1;
+ else
+ return yyerror ("Only (W32) allowed");
+ }
+ ;
+
+iu_or_nothing:
+ {
+ $$.r0 = 1;
+ }
+ | LPAREN MMOD RPAREN
+ {
+ if ($2 == M_IU)
+ $$.r0 = 3;
+ else
+ return yyerror ("(IU) expected");
+ }
+ ;
+
+reg_with_predec: LBRACK _MINUS_MINUS REG RBRACK
+ {
+ $$ = $3;
+ }
+ ;
+
+reg_with_postinc: LBRACK REG _PLUS_PLUS RBRACK
+ {
+ $$ = $2;
+ }
+ ;
+
+/* Operators. */
+
+min_max:
+ MIN
+ {
+ $$.r0 = 1;
+ }
+ | MAX
+ {
+ $$.r0 = 0;
+ }
+ ;
+
+op_bar_op:
+ _PLUS_BAR_PLUS
+ {
+ $$.r0 = 0;
+ }
+ | _PLUS_BAR_MINUS
+ {
+ $$.r0 = 1;
+ }
+ | _MINUS_BAR_PLUS
+ {
+ $$.r0 = 2;
+ }
+ | _MINUS_BAR_MINUS
+ {
+ $$.r0 = 3;
+ }
+ ;
+
+plus_minus:
+ PLUS
+ {
+ $$.r0 = 0;
+ }
+ | MINUS
+ {
+ $$.r0 = 1;
+ }
+ ;
+
+rnd_op:
+ LPAREN RNDH RPAREN
+ {
+ $$.r0 = 1; /* HL. */
+ $$.s0 = 0; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 0; /* aop. */
+ }
+
+ | LPAREN TH RPAREN
+ {
+ $$.r0 = 1; /* HL. */
+ $$.s0 = 0; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 1; /* aop. */
+ }
+
+ | LPAREN RNDL RPAREN
+ {
+ $$.r0 = 0; /* HL. */
+ $$.s0 = 0; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 0; /* aop. */
+ }
+
+ | LPAREN TL RPAREN
+ {
+ $$.r0 = 0; /* HL. */
+ $$.s0 = 0; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 1;
+ }
+
+ | LPAREN RNDH COMMA R RPAREN
+ {
+ $$.r0 = 1; /* HL. */
+ $$.s0 = 1; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 0; /* aop. */
+ }
+ | LPAREN TH COMMA R RPAREN
+ {
+ $$.r0 = 1; /* HL. */
+ $$.s0 = 1; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 1; /* aop. */
+ }
+ | LPAREN RNDL COMMA R RPAREN
+ {
+ $$.r0 = 0; /* HL. */
+ $$.s0 = 1; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 0; /* aop. */
+ }
+
+ | LPAREN TL COMMA R RPAREN
+ {
+ $$.r0 = 0; /* HL. */
+ $$.s0 = 1; /* s. */
+ $$.x0 = 0; /* x. */
+ $$.aop = 1; /* aop. */
+ }
+ ;
+
+b3_op:
+ LPAREN LO RPAREN
+ {
+ $$.s0 = 0; /* s. */
+ $$.x0 = 0; /* HL. */
+ }
+ | LPAREN HI RPAREN
+ {
+ $$.s0 = 0; /* s. */
+ $$.x0 = 1; /* HL. */
+ }
+ | LPAREN LO COMMA R RPAREN
+ {
+ $$.s0 = 1; /* s. */
+ $$.x0 = 0; /* HL. */
+ }
+ | LPAREN HI COMMA R RPAREN
+ {
+ $$.s0 = 1; /* s. */
+ $$.x0 = 1; /* HL. */
+ }
+ ;
+
+post_op:
+ {
+ $$.x0 = 2;
+ }
+ | _PLUS_PLUS
+ {
+ $$.x0 = 0;
+ }
+ | _MINUS_MINUS
+ {
+ $$.x0 = 1;
+ }
+ ;
+
+/* Assignments, Macfuncs. */
+
+a_assign:
+ REG_A ASSIGN
+ {
+ $$ = $1;
+ }
+ ;
+
+a_minusassign:
+ REG_A _MINUS_ASSIGN
+ {
+ $$ = $1;
+ }
+ ;
+
+a_plusassign:
+ REG_A _PLUS_ASSIGN
+ {
+ $$ = $1;
+ }
+ ;
+
+assign_macfunc:
+ REG ASSIGN REG_A
+ {
+ if (IS_A1 ($3) && IS_EVEN ($1))
+ return yyerror ("Cannot move A1 to even register");
+ else if (!IS_A1 ($3) && !IS_EVEN ($1))
+ return yyerror ("Cannot move A0 to odd register");
+
+ $$.w = 1;
+ $$.P = 1;
+ $$.n = IS_A1 ($3);
+ $$.op = 3;
+ $$.dst = $1;
+ $$.s0.regno = 0;
+ $$.s1.regno = 0;
+ }
+ | a_macfunc
+ {
+ $$ = $1;
+ $$.w = 0; $$.P = 0;
+ $$.dst.regno = 0;
+ }
+ | REG ASSIGN LPAREN a_macfunc RPAREN
+ {
+ if ($4.n && IS_EVEN ($1))
+ return yyerror ("Cannot move A1 to even register");
+ else if (!$4.n && !IS_EVEN ($1))
+ return yyerror ("Cannot move A0 to odd register");
+
+ $$ = $4;
+ $$.w = 1;
+ $$.P = 1;
+ $$.dst = $1;
+ }
+
+ | HALF_REG ASSIGN LPAREN a_macfunc RPAREN
+ {
+ if ($4.n && !IS_H ($1))
+ return yyerror ("Cannot move A1 to low half of register");
+ else if (!$4.n && IS_H ($1))
+ return yyerror ("Cannot move A0 to high half of register");
+
+ $$ = $4;
+ $$.w = 1;
+ $$.P = 0;
+ $$.dst = $1;
+ }
+
+ | HALF_REG ASSIGN REG_A
+ {
+ if (IS_A1 ($3) && !IS_H ($1))
+ return yyerror ("Cannot move A1 to low half of register");
+ else if (!IS_A1 ($3) && IS_H ($1))
+ return yyerror ("Cannot move A0 to high half of register");
+
+ $$.w = 1;
+ $$.P = 0;
+ $$.n = IS_A1 ($3);
+ $$.op = 3;
+ $$.dst = $1;
+ $$.s0.regno = 0;
+ $$.s1.regno = 0;
+ }
+ ;
+
+a_macfunc:
+ a_assign multiply_halfregs
+ {
+ $$.n = IS_A1 ($1);
+ $$.op = 0;
+ $$.s0 = $2.s0;
+ $$.s1 = $2.s1;
+ }
+ | a_plusassign multiply_halfregs
+ {
+ $$.n = IS_A1 ($1);
+ $$.op = 1;
+ $$.s0 = $2.s0;
+ $$.s1 = $2.s1;
+ }
+ | a_minusassign multiply_halfregs
+ {
+ $$.n = IS_A1 ($1);
+ $$.op = 2;
+ $$.s0 = $2.s0;
+ $$.s1 = $2.s1;
+ }
+ ;
+
+multiply_halfregs:
+ HALF_REG STAR HALF_REG
+ {
+ if (IS_DREG ($1) && IS_DREG ($3))
+ {
+ $$.s0 = $1;
+ $$.s1 = $3;
+ }
+ else
+ return yyerror ("Dregs expected");
+ }
+ ;
+
+cc_op:
+ ASSIGN
+ {
+ $$.r0 = 0;
+ }
+ | _BAR_ASSIGN
+ {
+ $$.r0 = 1;
+ }
+ | _AMPERSAND_ASSIGN
+ {
+ $$.r0 = 2;
+ }
+ | _CARET_ASSIGN
+ {
+ $$.r0 = 3;
+ }
+ ;
+
+ccstat:
+ CCREG cc_op STATUS_REG
+ {
+ $$.r0 = $3.regno;
+ $$.x0 = $2.r0;
+ $$.s0 = 0;
+ }
+ | CCREG cc_op V
+ {
+ $$.r0 = 0x18;
+ $$.x0 = $2.r0;
+ $$.s0 = 0;
+ }
+ | STATUS_REG cc_op CCREG
+ {
+ $$.r0 = $1.regno;
+ $$.x0 = $2.r0;
+ $$.s0 = 1;
+ }
+ | V cc_op CCREG
+ {
+ $$.r0 = 0x18;
+ $$.x0 = $2.r0;
+ $$.s0 = 1;
+ }
+ ;
+
+/* Expressions and Symbols. */
+
+symbol: SYMBOL
+ {
+ Expr_Node_Value val;
+ val.s_value = S_GET_NAME($1);
+ $$ = Expr_Node_Create (Expr_Node_Reloc, val, NULL, NULL);
+ }
+ ;
+
+any_gotrel:
+ GOT
+ { $$ = BFD_RELOC_BFIN_GOT; }
+ | GOT17M4
+ { $$ = BFD_RELOC_BFIN_GOT17M4; }
+ | FUNCDESC_GOT17M4
+ { $$ = BFD_RELOC_BFIN_FUNCDESC_GOT17M4; }
+ ;
+
+got: symbol AT any_gotrel
+ {
+ Expr_Node_Value val;
+ val.i_value = $3;
+ $$ = Expr_Node_Create (Expr_Node_GOT_Reloc, val, $1, NULL);
+ }
+ ;
+
+got_or_expr: got
+ {
+ $$ = $1;
+ }
+ | expr
+ {
+ $$ = $1;
+ }
+ ;
+
+pltpc :
+ symbol AT PLTPC
+ {
+ $$ = $1;
+ }
+ ;
+
+eterm: NUMBER
+ {
+ Expr_Node_Value val;
+ val.i_value = $1;
+ $$ = Expr_Node_Create (Expr_Node_Constant, val, NULL, NULL);
+ }
+ | symbol
+ {
+ $$ = $1;
+ }
+ | LPAREN expr_1 RPAREN
+ {
+ $$ = $2;
+ }
+ | TILDA expr_1
+ {
+ $$ = unary (Expr_Op_Type_COMP, $2);
+ }
+ | MINUS expr_1 %prec TILDA
+ {
+ $$ = unary (Expr_Op_Type_NEG, $2);
+ }
+ ;
+
+expr: expr_1
+ {
+ $$ = $1;
+ }
+ ;
+
+expr_1: expr_1 STAR expr_1
+ {
+ $$ = binary (Expr_Op_Type_Mult, $1, $3);
+ }
+ | expr_1 SLASH expr_1
+ {
+ $$ = binary (Expr_Op_Type_Div, $1, $3);
+ }
+ | expr_1 PERCENT expr_1
+ {
+ $$ = binary (Expr_Op_Type_Mod, $1, $3);
+ }
+ | expr_1 PLUS expr_1
+ {
+ $$ = binary (Expr_Op_Type_Add, $1, $3);
+ }
+ | expr_1 MINUS expr_1
+ {
+ $$ = binary (Expr_Op_Type_Sub, $1, $3);
+ }
+ | expr_1 LESS_LESS expr_1
+ {
+ $$ = binary (Expr_Op_Type_Lshift, $1, $3);
+ }
+ | expr_1 GREATER_GREATER expr_1
+ {
+ $$ = binary (Expr_Op_Type_Rshift, $1, $3);
+ }
+ | expr_1 AMPERSAND expr_1
+ {
+ $$ = binary (Expr_Op_Type_BAND, $1, $3);
+ }
+ | expr_1 CARET expr_1
+ {
+ $$ = binary (Expr_Op_Type_LOR, $1, $3);
+ }
+ | expr_1 BAR expr_1
+ {
+ $$ = binary (Expr_Op_Type_BOR, $1, $3);
+ }
+ | eterm
+ {
+ $$ = $1;
+ }
+ ;
+
+
+%%
+
+EXPR_T
+mkexpr (int x, SYMBOL_T s)
+{
+ EXPR_T e = (EXPR_T) ALLOCATE (sizeof (struct expression_cell));
+ e->value = x;
+ EXPR_SYMBOL(e) = s;
+ return e;
+}
+
+static int
+value_match (Expr_Node *exp, int sz, int sign, int mul, int issigned)
+{
+ int umax = (1 << sz) - 1;
+ int min = -1 << (sz - 1);
+ int max = (1 << (sz - 1)) - 1;
+
+ int v = (EXPR_VALUE (exp)) & 0xffffffff;
+
+ if ((v % mul) != 0)
+ {
+ error ("%s:%d: Value Error -- Must align to %d\n", __FILE__, __LINE__, mul);
+ return 0;
+ }
+
+ v /= mul;
+
+ if (sign)
+ v = -v;
+
+ if (issigned)
+ {
+ if (v >= min && v <= max) return 1;
+
+#ifdef DEBUG
+ fprintf(stderr, "signed value %lx out of range\n", v * mul);
+#endif
+ return 0;
+ }
+ if (v <= umax && v >= 0)
+ return 1;
+#ifdef DEBUG
+ fprintf(stderr, "unsigned value %lx out of range\n", v * mul);
+#endif
+ return 0;
+}
+
+/* Return the expression structure that allows symbol operations.
+ If the left and right children are constants, do the operation. */
+static Expr_Node *
+binary (Expr_Op_Type op, Expr_Node *x, Expr_Node *y)
+{
+ Expr_Node_Value val;
+
+ if (x->type == Expr_Node_Constant && y->type == Expr_Node_Constant)
+ {
+ switch (op)
+ {
+ case Expr_Op_Type_Add:
+ x->value.i_value += y->value.i_value;
+ break;
+ case Expr_Op_Type_Sub:
+ x->value.i_value -= y->value.i_value;
+ break;
+ case Expr_Op_Type_Mult:
+ x->value.i_value *= y->value.i_value;
+ break;
+ case Expr_Op_Type_Div:
+ if (y->value.i_value == 0)
+ error ("Illegal Expression: Division by zero.");
+ else
+ x->value.i_value /= y->value.i_value;
+ break;
+ case Expr_Op_Type_Mod:
+ x->value.i_value %= y->value.i_value;
+ break;
+ case Expr_Op_Type_Lshift:
+ x->value.i_value <<= y->value.i_value;
+ break;
+ case Expr_Op_Type_Rshift:
+ x->value.i_value >>= y->value.i_value;
+ break;
+ case Expr_Op_Type_BAND:
+ x->value.i_value &= y->value.i_value;
+ break;
+ case Expr_Op_Type_BOR:
+ x->value.i_value |= y->value.i_value;
+ break;
+ case Expr_Op_Type_BXOR:
+ x->value.i_value ^= y->value.i_value;
+ break;
+ case Expr_Op_Type_LAND:
+ x->value.i_value = x->value.i_value && y->value.i_value;
+ break;
+ case Expr_Op_Type_LOR:
+ x->value.i_value = x->value.i_value || y->value.i_value;
+ break;
+
+ default:
+ error ("%s:%d: Internal assembler error\n", __FILE__, __LINE__);
+ }
+ return x;
+ }
+ /* Canonicalize order to EXPR OP CONSTANT. */
+ if (x->type == Expr_Node_Constant)
+ {
+ Expr_Node *t = x;
+ x = y;
+ y = t;
+ }
+ /* Canonicalize subtraction of const to addition of negated const. */
+ if (op == Expr_Op_Type_Sub && y->type == Expr_Node_Constant)
+ {
+ op = Expr_Op_Type_Add;
+ y->value.i_value = -y->value.i_value;
+ }
+ if (y->type == Expr_Node_Constant && x->type == Expr_Node_Binop
+ && x->Right_Child->type == Expr_Node_Constant)
+ {
+ if (op == x->value.op_value && x->value.op_value == Expr_Op_Type_Add)
+ {
+ x->Right_Child->value.i_value += y->value.i_value;
+ return x;
+ }
+ }
+
+ /* Create a new expression structure. */
+ val.op_value = op;
+ return Expr_Node_Create (Expr_Node_Binop, val, x, y);
+}
+
+static Expr_Node *
+unary (Expr_Op_Type op, Expr_Node *x)
+{
+ if (x->type == Expr_Node_Constant)
+ {
+ switch (op)
+ {
+ case Expr_Op_Type_NEG:
+ x->value.i_value = -x->value.i_value;
+ break;
+ case Expr_Op_Type_COMP:
+ x->value.i_value = ~x->value.i_value;
+ break;
+ default:
+ error ("%s:%d: Internal assembler error\n", __FILE__, __LINE__);
+ }
+ return x;
+ }
+ else
+ {
+ /* Create a new expression structure. */
+ Expr_Node_Value val;
+ val.op_value = op;
+ return Expr_Node_Create (Expr_Node_Unop, val, x, NULL);
+ }
+}
+
+int debug_codeselection = 0;
+static void
+notethat (char *format, ...)
+{
+ va_list ap;
+ va_start (ap, format);
+ if (debug_codeselection)
+ {
+ vfprintf (errorf, format, ap);
+ }
+ va_end (ap);
+}
+
+#ifdef TEST
+main (int argc, char **argv)
+{
+ yyparse();
+}
+#endif
+
diff --git a/gas/config/e-crisaout.c b/gas/config/e-crisaout.c
new file mode 100644
index 0000000..c1fa544
--- /dev/null
+++ b/gas/config/e-crisaout.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "emul.h"
+
+static const char *crisaout_bfd_name (void);
+
+static const char *
+crisaout_bfd_name (void)
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name crisaout_bfd_name
+#define emul_format &aout_format_ops
+
+#define emul_name "crisaout"
+#define emul_struct_name crisaout
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-criself.c b/gas/config/e-criself.c
new file mode 100644
index 0000000..db86924
--- /dev/null
+++ b/gas/config/e-criself.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "emul.h"
+
+static const char *criself_bfd_name (void);
+
+static const char *
+criself_bfd_name (void)
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name criself_bfd_name
+#define emul_format &elf_format_ops
+
+#define emul_name "criself"
+#define emul_struct_name criself
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-i386aout.c b/gas/config/e-i386aout.c
new file mode 100644
index 0000000..7fd7c31
--- /dev/null
+++ b/gas/config/e-i386aout.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "emul.h"
+
+static const char *i386aout_bfd_name (void);
+
+static const char *
+i386aout_bfd_name (void)
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name i386aout_bfd_name
+#define emul_format &aout_format_ops
+
+#define emul_name "i386aout"
+#define emul_struct_name i386aout
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-i386coff.c b/gas/config/e-i386coff.c
new file mode 100644
index 0000000..272a167
--- /dev/null
+++ b/gas/config/e-i386coff.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "emul.h"
+
+static const char *i386coff_bfd_name (void);
+
+static const char *
+i386coff_bfd_name (void)
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name i386coff_bfd_name
+#define emul_format &coff_format_ops
+
+#define emul_name "i386coff"
+#define emul_struct_name i386coff
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-i386elf.c b/gas/config/e-i386elf.c
new file mode 100644
index 0000000..a5b2713
--- /dev/null
+++ b/gas/config/e-i386elf.c
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "emul.h"
+
+static const char *i386elf_bfd_name (void);
+
+static const char *
+i386elf_bfd_name (void)
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name i386elf_bfd_name
+#define emul_format &elf_format_ops
+
+#define emul_name "i386elf"
+#define emul_struct_name i386elf
+#define emul_default_endian 0
+#include "emul-target.h"
diff --git a/gas/config/e-mipself.c b/gas/config/e-mipself.c
new file mode 100644
index 0000000..bc7f09f
--- /dev/null
+++ b/gas/config/e-mipself.c
@@ -0,0 +1,56 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "emul.h"
+
+static const char *mipself_bfd_name (void);
+
+static const char *
+mipself_bfd_name (void)
+{
+ abort ();
+ return NULL;
+}
+
+#define emul_bfd_name mipself_bfd_name
+#define emul_format &elf_format_ops
+
+#define emul_name "mipsbelf"
+#define emul_struct_name mipsbelf
+#define emul_default_endian 1
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipslelf"
+#define emul_struct_name mipslelf
+#define emul_default_endian 0
+#include "emul-target.h"
+
+#undef emul_name
+#undef emul_struct_name
+#undef emul_default_endian
+
+#define emul_name "mipself"
+#define emul_struct_name mipself
+#define emul_default_endian 2
+#include "emul-target.h"
diff --git a/gas/config/itbl-mips.h b/gas/config/itbl-mips.h
new file mode 100644
index 0000000..69f1dc4
--- /dev/null
+++ b/gas/config/itbl-mips.h
@@ -0,0 +1,46 @@
+/* itbl-mips.h
+
+ Copyright (C) 1997-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Defines for Mips itbl cop support. */
+
+#include "opcode/mips.h"
+
+/* Values for processors will be from 0 to NUMBER_OF_PROCESSORS-1 */
+#define NUMBER_OF_PROCESSORS 4
+#define MAX_BITPOS 31
+
+/* Mips specifics */
+#define MIPS_OPCODE_COP0 (0x21) /* COPz+CO, bits 31-25: 0100zz1 */
+#define MIPS_ENCODE_COP_NUM(z) ((MIPS_OPCODE_COP0|z<<1)<<25)
+#define MIPS_IS_COP_INSN(insn) ((MIPS_OPCODE_COP0&(insn>>25)) \
+ == MIPS_OPCODE_COP0)
+#define MIPS_DECODE_COP_NUM(insn) ((~MIPS_OPCODE_COP0&(insn>>25))>>1)
+#define MIPS_DECODE_COP_COFUN(insn) ((~MIPS_ENCODE_COP_NUM(3))&(insn))
+
+/* definitions required by generic code */
+#define ITBL_IS_INSN(insn) MIPS_IS_COP_INSN(insn)
+#define ITBL_DECODE_PNUM(insn) MIPS_DECODE_COP_NUM(insn)
+#define ITBL_ENCODE_PNUM(pnum) MIPS_ENCODE_COP_NUM(pnum)
+
+#define ITBL_OPCODE_STRUCT mips_opcode
+#define ITBL_OPCODES mips_opcodes
+#define ITBL_NUM_OPCODES NUMOPCODES
+#define ITBL_NUM_MACROS M_NUM_MACROS
diff --git a/gas/config/m68k-parse.h b/gas/config/m68k-parse.h
new file mode 100644
index 0000000..b4c0b5b
--- /dev/null
+++ b/gas/config/m68k-parse.h
@@ -0,0 +1,358 @@
+/* m68k-parse.h -- header file for m68k assembler
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef M68K_PARSE_H
+#define M68K_PARSE_H
+
+/* This header file defines things which are shared between the
+ operand parser in m68k.y and the m68k assembler proper in
+ tc-m68k.c. */
+
+/* The various m68k registers. */
+
+/* DATA and ADDR have to be contiguous, so that reg-DATA gives
+ 0-7==data reg, 8-15==addr reg for operands that take both types.
+
+ We don't use forms like "ADDR0 = ADDR" here because this file is
+ likely to be used on an Apollo, and the broken Apollo compiler
+ gives an `undefined variable' error if we do that, according to
+ troy@cbme.unsw.edu.au. */
+
+#define DATA DATA0
+#define ADDR ADDR0
+#define SP ADDR7
+#define BAD BAD0
+#define BAC BAC0
+
+enum m68k_register
+{
+ DATA0 = 1, /* 1- 8 == data registers 0-7 */
+ DATA1,
+ DATA2,
+ DATA3,
+ DATA4,
+ DATA5,
+ DATA6,
+ DATA7,
+
+ ADDR0,
+ ADDR1,
+ ADDR2,
+ ADDR3,
+ ADDR4,
+ ADDR5,
+ ADDR6,
+ ADDR7,
+
+ FP0, /* Eight FP registers */
+ FP1,
+ FP2,
+ FP3,
+ FP4,
+ FP5,
+ FP6,
+ FP7,
+
+ COP0, /* Co-processor #0-#7 */
+ COP1,
+ COP2,
+ COP3,
+ COP4,
+ COP5,
+ COP6,
+ COP7,
+
+ PC, /* Program counter */
+ ZPC, /* Hack for Program space, but 0 addressing */
+ SR, /* Status Reg */
+ CCR, /* Condition code Reg */
+ ACC, /* Accumulator Reg0 (EMAC or ACC on MAC). */
+ ACC1, /* Accumulator Reg 1 (EMAC). */
+ ACC2, /* Accumulator Reg 2 (EMAC). */
+ ACC3, /* Accumulator Reg 3 (EMAC). */
+ ACCEXT01, /* Accumulator extension 0&1 (EMAC). */
+ ACCEXT23, /* Accumulator extension 2&3 (EMAC). */
+ MACSR, /* MAC Status Reg */
+ MASK, /* Modulus Reg */
+
+ /* These have to be grouped together for the movec instruction to work. */
+ USP, /* User Stack Pointer */
+ ISP, /* Interrupt stack pointer */
+ SFC,
+ DFC,
+ CACR,
+ VBR,
+ CAAR,
+ CPUCR,
+ MSP,
+ ITT0,
+ ITT1,
+ DTT0,
+ DTT1,
+ MMUSR,
+ TC,
+ SRP,
+ URP,
+ BUSCR, /* 68060 added these. */
+ PCR,
+ ROMBAR, /* mcf5200 added these. */
+ RAMBAR_ALT, /* Some CF chips have RAMBAR using
+ RAMBAR0's number */
+ RAMBAR0,
+ RAMBAR1,
+ MMUBAR, /* mcfv4e added these. */
+ ROMBAR0, /* mcfv4e added these. */
+ ROMBAR1, /* mcfv4e added these. */
+ MPCR, EDRAMBAR, SECMBAR, /* mcfv4e added these. */
+ PCR1U0, PCR1L0, PCR1U1, PCR1L1,/* mcfv4e added these. */
+ PCR2U0, PCR2L0, PCR2U1, PCR2L1,/* mcfv4e added these. */
+ PCR3U0, PCR3L0, PCR3U1, PCR3L1,/* mcfv4e added these. */
+ MBAR0, MBAR1, /* mcfv4e added these. */
+ ACR0, ACR1, ACR2, ACR3, /* mcf5200 added these. */
+ ACR4, ACR5, ACR6, ACR7, /* mcf54418 added these. */
+ FLASHBAR, RAMBAR, /* mcf528x added these. */
+ MBAR2, /* mcf5249 added this. */
+ MBAR,
+ RGPIOBAR, /* mcf54418 added this. */
+ ASID, /* m5475. */
+ CAC, /* fido added this. */
+ MBO,
+#define last_movec_reg MBO
+ /* End of movec ordering constraints. */
+
+ FPI,
+ FPS,
+ FPC,
+
+ DRP, /* 68851 or 68030 MMU regs */
+ CRP,
+ CAL,
+ VAL,
+ SCC,
+ AC,
+ BAD0,
+ BAD1,
+ BAD2,
+ BAD3,
+ BAD4,
+ BAD5,
+ BAD6,
+ BAD7,
+ BAC0,
+ BAC1,
+ BAC2,
+ BAC3,
+ BAC4,
+ BAC5,
+ BAC6,
+ BAC7,
+ PSR, /* aka MMUSR on 68030 (but not MMUSR on 68040)
+ and ACUSR on 68ec030 */
+ PCSR,
+
+ IC, /* instruction cache token */
+ DC, /* data cache token */
+ NC, /* no cache token */
+ BC, /* both caches token */
+
+ TT0, /* 68030 access control unit regs */
+ TT1,
+
+ ZDATA0, /* suppressed data registers. */
+ ZDATA1,
+ ZDATA2,
+ ZDATA3,
+ ZDATA4,
+ ZDATA5,
+ ZDATA6,
+ ZDATA7,
+
+ ZADDR0, /* suppressed address registers. */
+ ZADDR1,
+ ZADDR2,
+ ZADDR3,
+ ZADDR4,
+ ZADDR5,
+ ZADDR6,
+ ZADDR7,
+
+ /* Upper and lower half of data and address registers. Order *must*
+ be DATAxL, ADDRxL, DATAxU, ADDRxU. */
+ DATA0L, /* lower half of data registers */
+ DATA1L,
+ DATA2L,
+ DATA3L,
+ DATA4L,
+ DATA5L,
+ DATA6L,
+ DATA7L,
+
+ ADDR0L, /* lower half of address registers */
+ ADDR1L,
+ ADDR2L,
+ ADDR3L,
+ ADDR4L,
+ ADDR5L,
+ ADDR6L,
+ ADDR7L,
+
+ DATA0U, /* upper half of data registers */
+ DATA1U,
+ DATA2U,
+ DATA3U,
+ DATA4U,
+ DATA5U,
+ DATA6U,
+ DATA7U,
+
+ ADDR0U, /* upper half of address registers */
+ ADDR1U,
+ ADDR2U,
+ ADDR3U,
+ ADDR4U,
+ ADDR5U,
+ ADDR6U,
+ ADDR7U,
+};
+
+/* Size information. */
+
+enum m68k_size
+{
+ /* Unspecified. */
+ SIZE_UNSPEC,
+
+ /* Byte. */
+ SIZE_BYTE,
+
+ /* Word (2 bytes). */
+ SIZE_WORD,
+
+ /* Longword (4 bytes). */
+ SIZE_LONG
+};
+
+/* The structure used to hold information about an index register. */
+
+struct m68k_indexreg
+{
+ /* The index register itself. */
+ enum m68k_register reg;
+
+ /* The size to use. */
+ enum m68k_size size;
+
+ /* The value to scale by. */
+ int scale;
+};
+
+#ifdef OBJ_ELF
+/* The type of a PIC expression. */
+
+enum pic_relocation
+{
+ pic_none, /* not pic */
+ pic_plt_pcrel, /* @PLTPC */
+ pic_got_pcrel, /* @GOTPC */
+ pic_plt_off, /* @PLT */
+ pic_got_off, /* @GOT */
+ pic_tls_gd, /* @TLSGD */
+ pic_tls_ldm, /* @TLSLDM */
+ pic_tls_ldo, /* @TLSLDO */
+ pic_tls_ie, /* @TLSIE */
+ pic_tls_le /* @TLSLE */
+};
+#endif
+
+/* The structure used to hold information about an expression. */
+
+struct m68k_exp
+{
+ /* The size to use. */
+ enum m68k_size size;
+
+#ifdef OBJ_ELF
+ /* The type of pic relocation if any. */
+ enum pic_relocation pic_reloc;
+#endif
+
+ /* The expression itself. */
+ expressionS exp;
+};
+
+/* The operand modes. */
+
+enum m68k_operand_type
+{
+ IMMED = 1,
+ ABSL,
+ DREG,
+ AREG,
+ FPREG,
+ CONTROL,
+ AINDR,
+ AINC,
+ ADEC,
+ DISP,
+ BASE,
+ POST,
+ PRE,
+ LSH, /* MAC/EMAC scalefactor '<<'. */
+ RSH, /* MAC/EMAC scalefactor '>>'. */
+ REGLST
+};
+
+/* The structure used to hold a parsed operand. */
+
+struct m68k_op
+{
+ /* The type of operand. */
+ enum m68k_operand_type mode;
+
+ /* The main register. */
+ enum m68k_register reg;
+
+ /* The register mask for mode REGLST. */
+ unsigned long mask;
+
+ /* An error message. */
+ const char *error;
+
+ /* The index register. */
+ struct m68k_indexreg index;
+
+ /* The displacement. */
+ struct m68k_exp disp;
+
+ /* The outer displacement. */
+ struct m68k_exp odisp;
+
+ /* Is a trailing '&' added to an <ea>? (for MAC/EMAC mask addressing). */
+ int trailing_ampersand;
+};
+
+#endif /* ! defined (M68K_PARSE_H) */
+
+/* The parsing function. */
+
+extern int m68k_ip_op (char *, struct m68k_op *);
+
+/* Whether register prefixes are optional. */
+extern int flag_reg_prefix_optional;
diff --git a/gas/config/m68k-parse.y b/gas/config/m68k-parse.y
new file mode 100644
index 0000000..d5c59a1
--- /dev/null
+++ b/gas/config/m68k-parse.y
@@ -0,0 +1,1118 @@
+/* m68k.y -- bison grammar for m68k operand parsing
+ Copyright (C) 1995-2014 Free Software Foundation, Inc.
+ Written by Ken Raeburn and Ian Lance Taylor, Cygnus Support
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file holds a bison grammar to parse m68k operands. The m68k
+ has a complicated operand syntax, and gas supports two main
+ variations of it. Using a grammar is probably overkill, but at
+ least it makes clear exactly what we do support. */
+
+%{
+
+#include "as.h"
+#include "tc-m68k.h"
+#include "m68k-parse.h"
+#include "safe-ctype.h"
+
+/* Remap normal yacc parser interface names (yyparse, yylex, yyerror,
+ etc), as well as gratuitously global symbol names If other parser
+ generators (bison, byacc, etc) produce additional global names that
+ conflict at link time, then those parser generators need to be
+ fixed instead of adding those names to this list. */
+
+#define yymaxdepth m68k_maxdepth
+#define yyparse m68k_parse
+#define yylex m68k_lex
+#define yyerror m68k_error
+#define yylval m68k_lval
+#define yychar m68k_char
+#define yydebug m68k_debug
+#define yypact m68k_pact
+#define yyr1 m68k_r1
+#define yyr2 m68k_r2
+#define yydef m68k_def
+#define yychk m68k_chk
+#define yypgo m68k_pgo
+#define yyact m68k_act
+#define yyexca m68k_exca
+#define yyerrflag m68k_errflag
+#define yynerrs m68k_nerrs
+#define yyps m68k_ps
+#define yypv m68k_pv
+#define yys m68k_s
+#define yy_yys m68k_yys
+#define yystate m68k_state
+#define yytmp m68k_tmp
+#define yyv m68k_v
+#define yy_yyv m68k_yyv
+#define yyval m68k_val
+#define yylloc m68k_lloc
+#define yyreds m68k_reds /* With YYDEBUG defined */
+#define yytoks m68k_toks /* With YYDEBUG defined */
+#define yylhs m68k_yylhs
+#define yylen m68k_yylen
+#define yydefred m68k_yydefred
+#define yydgoto m68k_yydgoto
+#define yysindex m68k_yysindex
+#define yyrindex m68k_yyrindex
+#define yygindex m68k_yygindex
+#define yytable m68k_yytable
+#define yycheck m68k_yycheck
+
+#ifndef YYDEBUG
+#define YYDEBUG 1
+#endif
+
+/* Internal functions. */
+
+static enum m68k_register m68k_reg_parse (char **);
+static int yylex (void);
+static void yyerror (const char *);
+
+/* The parser sets fields pointed to by this global variable. */
+static struct m68k_op *op;
+
+%}
+
+%union
+{
+ struct m68k_indexreg indexreg;
+ enum m68k_register reg;
+ struct m68k_exp exp;
+ unsigned long mask;
+ int onereg;
+ int trailing_ampersand;
+}
+
+%token <reg> DR AR FPR FPCR LPC ZAR ZDR LZPC CREG
+%token <indexreg> INDEXREG
+%token <exp> EXPR
+
+%type <indexreg> zireg zdireg
+%type <reg> zadr zdr apc zapc zpc optzapc optczapc
+%type <exp> optcexpr optexprc
+%type <mask> reglist ireglist reglistpair
+%type <onereg> reglistreg
+%type <trailing_ampersand> optional_ampersand
+
+%%
+
+/* An operand. */
+
+operand:
+ generic_operand
+ | motorola_operand optional_ampersand
+ {
+ op->trailing_ampersand = $2;
+ }
+ | mit_operand optional_ampersand
+ {
+ op->trailing_ampersand = $2;
+ }
+ ;
+
+/* A trailing ampersand(for MAC/EMAC mask addressing). */
+optional_ampersand:
+ /* empty */
+ { $$ = 0; }
+ | '&'
+ { $$ = 1; }
+ ;
+
+/* A generic operand. */
+
+generic_operand:
+ '<' '<'
+ {
+ op->mode = LSH;
+ }
+
+ | '>' '>'
+ {
+ op->mode = RSH;
+ }
+
+ | DR
+ {
+ op->mode = DREG;
+ op->reg = $1;
+ }
+ | AR
+ {
+ op->mode = AREG;
+ op->reg = $1;
+ }
+ | FPR
+ {
+ op->mode = FPREG;
+ op->reg = $1;
+ }
+ | FPCR
+ {
+ op->mode = CONTROL;
+ op->reg = $1;
+ }
+ | CREG
+ {
+ op->mode = CONTROL;
+ op->reg = $1;
+ }
+ | EXPR
+ {
+ op->mode = ABSL;
+ op->disp = $1;
+ }
+ | '#' EXPR
+ {
+ op->mode = IMMED;
+ op->disp = $2;
+ }
+ | '&' EXPR
+ {
+ op->mode = IMMED;
+ op->disp = $2;
+ }
+ | reglist
+ {
+ op->mode = REGLST;
+ op->mask = $1;
+ }
+ ;
+
+/* An operand in Motorola syntax. This includes MRI syntax as well,
+ which may or may not be different in that it permits commutativity
+ of index and base registers, and permits an offset expression to
+ appear inside or outside of the parentheses. */
+
+motorola_operand:
+ '(' AR ')'
+ {
+ op->mode = AINDR;
+ op->reg = $2;
+ }
+ | '(' AR ')' '+'
+ {
+ op->mode = AINC;
+ op->reg = $2;
+ }
+ | '-' '(' AR ')'
+ {
+ op->mode = ADEC;
+ op->reg = $3;
+ }
+ | '(' EXPR ',' zapc ')'
+ {
+ op->reg = $4;
+ op->disp = $2;
+ if (($4 >= ZADDR0 && $4 <= ZADDR7)
+ || $4 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | '(' zapc ',' EXPR ')'
+ {
+ op->reg = $2;
+ op->disp = $4;
+ if (($2 >= ZADDR0 && $2 <= ZADDR7)
+ || $2 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | EXPR '(' zapc ')'
+ {
+ op->reg = $3;
+ op->disp = $1;
+ if (($3 >= ZADDR0 && $3 <= ZADDR7)
+ || $3 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | '(' LPC ')'
+ {
+ op->mode = DISP;
+ op->reg = $2;
+ }
+ | '(' ZAR ')'
+ {
+ op->mode = BASE;
+ op->reg = $2;
+ }
+ | '(' LZPC ')'
+ {
+ op->mode = BASE;
+ op->reg = $2;
+ }
+ | '(' EXPR ',' zapc ',' zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $4;
+ op->disp = $2;
+ op->index = $6;
+ }
+ | '(' EXPR ',' zapc ',' zpc ')'
+ {
+ if ($4 == PC || $4 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = BASE;
+ op->reg = $6;
+ op->disp = $2;
+ op->index.reg = $4;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ }
+ | '(' EXPR ',' zdireg optczapc ')'
+ {
+ op->mode = BASE;
+ op->reg = $5;
+ op->disp = $2;
+ op->index = $4;
+ }
+ | '(' zdireg ',' EXPR ')'
+ {
+ op->mode = BASE;
+ op->disp = $4;
+ op->index = $2;
+ }
+ | EXPR '(' zapc ',' zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $3;
+ op->disp = $1;
+ op->index = $5;
+ }
+ | '(' zapc ',' zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $2;
+ op->index = $4;
+ }
+ | EXPR '(' zapc ',' zpc ')'
+ {
+ if ($3 == PC || $3 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = BASE;
+ op->reg = $5;
+ op->disp = $1;
+ op->index.reg = $3;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ }
+ | '(' zapc ',' zpc ')'
+ {
+ if ($2 == PC || $2 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = BASE;
+ op->reg = $4;
+ op->index.reg = $2;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ }
+ | EXPR '(' zdireg optczapc ')'
+ {
+ op->mode = BASE;
+ op->reg = $4;
+ op->disp = $1;
+ op->index = $3;
+ }
+ | '(' zdireg optczapc ')'
+ {
+ op->mode = BASE;
+ op->reg = $3;
+ op->index = $2;
+ }
+ | '(' '[' EXPR optczapc ']' ',' zireg optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $4;
+ op->disp = $3;
+ op->index = $7;
+ op->odisp = $8;
+ }
+ | '(' '[' EXPR optczapc ']' optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $4;
+ op->disp = $3;
+ op->odisp = $6;
+ }
+ | '(' '[' zapc ']' ',' zireg optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $3;
+ op->index = $6;
+ op->odisp = $7;
+ }
+ | '(' '[' zapc ']' optcexpr ')'
+ {
+ op->mode = POST;
+ op->reg = $3;
+ op->odisp = $5;
+ }
+ | '(' '[' EXPR ',' zapc ',' zireg ']' optcexpr ')'
+ {
+ op->mode = PRE;
+ op->reg = $5;
+ op->disp = $3;
+ op->index = $7;
+ op->odisp = $9;
+ }
+ | '(' '[' zapc ',' zireg ']' optcexpr ')'
+ {
+ op->mode = PRE;
+ op->reg = $3;
+ op->index = $5;
+ op->odisp = $7;
+ }
+ | '(' '[' EXPR ',' zapc ',' zpc ']' optcexpr ')'
+ {
+ if ($5 == PC || $5 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = PRE;
+ op->reg = $7;
+ op->disp = $3;
+ op->index.reg = $5;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ op->odisp = $9;
+ }
+ | '(' '[' zapc ',' zpc ']' optcexpr ')'
+ {
+ if ($3 == PC || $3 == ZPC)
+ yyerror (_("syntax error"));
+ op->mode = PRE;
+ op->reg = $5;
+ op->index.reg = $3;
+ op->index.size = SIZE_UNSPEC;
+ op->index.scale = 1;
+ op->odisp = $7;
+ }
+ | '(' '[' optexprc zdireg optczapc ']' optcexpr ')'
+ {
+ op->mode = PRE;
+ op->reg = $5;
+ op->disp = $3;
+ op->index = $4;
+ op->odisp = $7;
+ }
+ ;
+
+/* An operand in MIT syntax. */
+
+mit_operand:
+ optzapc '@'
+ {
+ /* We use optzapc to avoid a shift/reduce conflict. */
+ if ($1 < ADDR0 || $1 > ADDR7)
+ yyerror (_("syntax error"));
+ op->mode = AINDR;
+ op->reg = $1;
+ }
+ | optzapc '@' '+'
+ {
+ /* We use optzapc to avoid a shift/reduce conflict. */
+ if ($1 < ADDR0 || $1 > ADDR7)
+ yyerror (_("syntax error"));
+ op->mode = AINC;
+ op->reg = $1;
+ }
+ | optzapc '@' '-'
+ {
+ /* We use optzapc to avoid a shift/reduce conflict. */
+ if ($1 < ADDR0 || $1 > ADDR7)
+ yyerror (_("syntax error"));
+ op->mode = ADEC;
+ op->reg = $1;
+ }
+ | optzapc '@' '(' EXPR ')'
+ {
+ op->reg = $1;
+ op->disp = $4;
+ if (($1 >= ZADDR0 && $1 <= ZADDR7)
+ || $1 == ZPC)
+ op->mode = BASE;
+ else
+ op->mode = DISP;
+ }
+ | optzapc '@' '(' optexprc zireg ')'
+ {
+ op->mode = BASE;
+ op->reg = $1;
+ op->disp = $4;
+ op->index = $5;
+ }
+ | optzapc '@' '(' EXPR ')' '@' '(' optexprc zireg ')'
+ {
+ op->mode = POST;
+ op->reg = $1;
+ op->disp = $4;
+ op->index = $9;
+ op->odisp = $8;
+ }
+ | optzapc '@' '(' EXPR ')' '@' '(' EXPR ')'
+ {
+ op->mode = POST;
+ op->reg = $1;
+ op->disp = $4;
+ op->odisp = $8;
+ }
+ | optzapc '@' '(' optexprc zireg ')' '@' '(' EXPR ')'
+ {
+ op->mode = PRE;
+ op->reg = $1;
+ op->disp = $4;
+ op->index = $5;
+ op->odisp = $9;
+ }
+ ;
+
+/* An index register, possibly suppressed, which need not have a size
+ or scale. */
+
+zireg:
+ INDEXREG
+ | zadr
+ {
+ $$.reg = $1;
+ $$.size = SIZE_UNSPEC;
+ $$.scale = 1;
+ }
+ ;
+
+/* A register which may be an index register, but which may not be an
+ address register. This nonterminal is used to avoid ambiguity when
+ trying to parse something like (0,d5,a6) as compared to (0,a6,d5). */
+
+zdireg:
+ INDEXREG
+ | zdr
+ {
+ $$.reg = $1;
+ $$.size = SIZE_UNSPEC;
+ $$.scale = 1;
+ }
+ ;
+
+/* An address or data register, or a suppressed address or data
+ register. */
+
+zadr:
+ zdr
+ | AR
+ | ZAR
+ ;
+
+/* A data register which may be suppressed. */
+
+zdr:
+ DR
+ | ZDR
+ ;
+
+/* Either an address register or the PC. */
+
+apc:
+ AR
+ | LPC
+ ;
+
+/* Either an address register, or the PC, or a suppressed address
+ register, or a suppressed PC. */
+
+zapc:
+ apc
+ | LZPC
+ | ZAR
+ ;
+
+/* An optional zapc. */
+
+optzapc:
+ /* empty */
+ {
+ $$ = ZADDR0;
+ }
+ | zapc
+ ;
+
+/* The PC, optionally suppressed. */
+
+zpc:
+ LPC
+ | LZPC
+ ;
+
+/* ',' zapc when it may be omitted. */
+
+optczapc:
+ /* empty */
+ {
+ $$ = ZADDR0;
+ }
+ | ',' zapc
+ {
+ $$ = $2;
+ }
+ ;
+
+/* ',' EXPR when it may be omitted. */
+
+optcexpr:
+ /* empty */
+ {
+ $$.exp.X_op = O_absent;
+ $$.size = SIZE_UNSPEC;
+ }
+ | ',' EXPR
+ {
+ $$ = $2;
+ }
+ ;
+
+/* EXPR ',' when it may be omitted. */
+
+optexprc:
+ /* empty */
+ {
+ $$.exp.X_op = O_absent;
+ $$.size = SIZE_UNSPEC;
+ }
+ | EXPR ','
+ {
+ $$ = $1;
+ }
+ ;
+
+/* A register list for the movem instruction. */
+
+reglist:
+ reglistpair
+ | reglistpair '/' ireglist
+ {
+ $$ = $1 | $3;
+ }
+ | reglistreg '/' ireglist
+ {
+ $$ = (1 << $1) | $3;
+ }
+ ;
+
+/* We use ireglist when we know we are looking at a reglist, and we
+ can safely reduce a simple register to reglistreg. If we permitted
+ reglist to reduce to reglistreg, it would be ambiguous whether a
+ plain register were a DREG/AREG/FPREG or a REGLST. */
+
+ireglist:
+ reglistreg
+ {
+ $$ = 1 << $1;
+ }
+ | reglistpair
+ | reglistpair '/' ireglist
+ {
+ $$ = $1 | $3;
+ }
+ | reglistreg '/' ireglist
+ {
+ $$ = (1 << $1) | $3;
+ }
+ ;
+
+reglistpair:
+ reglistreg '-' reglistreg
+ {
+ if ($1 <= $3)
+ $$ = (1 << ($3 + 1)) - 1 - ((1 << $1) - 1);
+ else
+ $$ = (1 << ($1 + 1)) - 1 - ((1 << $3) - 1);
+ }
+ ;
+
+reglistreg:
+ DR
+ {
+ $$ = $1 - DATA0;
+ }
+ | AR
+ {
+ $$ = $1 - ADDR0 + 8;
+ }
+ | FPR
+ {
+ $$ = $1 - FP0 + 16;
+ }
+ | FPCR
+ {
+ if ($1 == FPI)
+ $$ = 24;
+ else if ($1 == FPS)
+ $$ = 25;
+ else
+ $$ = 26;
+ }
+ ;
+
+%%
+
+/* The string to parse is stored here, and modified by yylex. */
+
+static char *str;
+
+/* The original string pointer. */
+
+static char *strorig;
+
+/* If *CCP could be a register, return the register number and advance
+ *CCP. Otherwise don't change *CCP, and return 0. */
+
+static enum m68k_register
+m68k_reg_parse (ccp)
+ register char **ccp;
+{
+ char *start = *ccp;
+ char c;
+ char *p;
+ symbolS *symbolp;
+
+ if (flag_reg_prefix_optional)
+ {
+ if (*start == REGISTER_PREFIX)
+ start++;
+ p = start;
+ }
+ else
+ {
+ if (*start != REGISTER_PREFIX)
+ return 0;
+ p = start + 1;
+ }
+
+ if (! is_name_beginner (*p))
+ return 0;
+
+ p++;
+ while (is_part_of_name (*p) && *p != '.' && *p != ':' && *p != '*')
+ p++;
+
+ c = *p;
+ *p = 0;
+ symbolp = symbol_find (start);
+ *p = c;
+
+ if (symbolp != NULL && S_GET_SEGMENT (symbolp) == reg_section)
+ {
+ *ccp = p;
+ return S_GET_VALUE (symbolp);
+ }
+
+ /* In MRI mode, something like foo.bar can be equated to a register
+ name. */
+ while (flag_mri && c == '.')
+ {
+ ++p;
+ while (is_part_of_name (*p) && *p != '.' && *p != ':' && *p != '*')
+ p++;
+ c = *p;
+ *p = '\0';
+ symbolp = symbol_find (start);
+ *p = c;
+ if (symbolp != NULL && S_GET_SEGMENT (symbolp) == reg_section)
+ {
+ *ccp = p;
+ return S_GET_VALUE (symbolp);
+ }
+ }
+
+ return 0;
+}
+
+/* The lexer. */
+
+static int
+yylex ()
+{
+ enum m68k_register reg;
+ char *s;
+ int parens;
+ int c = 0;
+ int tail = 0;
+ char *hold;
+
+ if (*str == ' ')
+ ++str;
+
+ if (*str == '\0')
+ return 0;
+
+ /* Various special characters are just returned directly. */
+ switch (*str)
+ {
+ case '@':
+ /* In MRI mode, this can be the start of an octal number. */
+ if (flag_mri)
+ {
+ if (ISDIGIT (str[1])
+ || ((str[1] == '+' || str[1] == '-')
+ && ISDIGIT (str[2])))
+ break;
+ }
+ /* Fall through. */
+ case '#':
+ case '&':
+ case ',':
+ case ')':
+ case '/':
+ case '[':
+ case ']':
+ case '<':
+ case '>':
+ return *str++;
+ case '+':
+ /* It so happens that a '+' can only appear at the end of an
+ operand, or if it is trailed by an '&'(see mac load insn).
+ If it appears anywhere else, it must be a unary. */
+ if (str[1] == '\0' || (str[1] == '&' && str[2] == '\0'))
+ return *str++;
+ break;
+ case '-':
+ /* A '-' can only appear in -(ar), rn-rn, or ar@-. If it
+ appears anywhere else, it must be a unary minus on an
+ expression, unless it it trailed by a '&'(see mac load insn). */
+ if (str[1] == '\0' || (str[1] == '&' && str[2] == '\0'))
+ return *str++;
+ s = str + 1;
+ if (*s == '(')
+ ++s;
+ if (m68k_reg_parse (&s) != 0)
+ return *str++;
+ break;
+ case '(':
+ /* A '(' can only appear in `(reg)', `(expr,...', `([', `@(', or
+ `)('. If it appears anywhere else, it must be starting an
+ expression. */
+ if (str[1] == '['
+ || (str > strorig
+ && (str[-1] == '@'
+ || str[-1] == ')')))
+ return *str++;
+ s = str + 1;
+ if (m68k_reg_parse (&s) != 0)
+ return *str++;
+ /* Check for the case of '(expr,...' by scanning ahead. If we
+ find a comma outside of balanced parentheses, we return '('.
+ If we find an unbalanced right parenthesis, then presumably
+ the '(' really starts an expression. */
+ parens = 0;
+ for (s = str + 1; *s != '\0'; s++)
+ {
+ if (*s == '(')
+ ++parens;
+ else if (*s == ')')
+ {
+ if (parens == 0)
+ break;
+ --parens;
+ }
+ else if (*s == ',' && parens == 0)
+ {
+ /* A comma can not normally appear in an expression, so
+ this is a case of '(expr,...'. */
+ return *str++;
+ }
+ }
+ }
+
+ /* See if it's a register. */
+
+ reg = m68k_reg_parse (&str);
+ if (reg != 0)
+ {
+ int ret;
+
+ yylval.reg = reg;
+
+ if (reg >= DATA0 && reg <= DATA7)
+ ret = DR;
+ else if (reg >= ADDR0 && reg <= ADDR7)
+ ret = AR;
+ else if (reg >= FP0 && reg <= FP7)
+ return FPR;
+ else if (reg == FPI
+ || reg == FPS
+ || reg == FPC)
+ return FPCR;
+ else if (reg == PC)
+ return LPC;
+ else if (reg >= ZDATA0 && reg <= ZDATA7)
+ ret = ZDR;
+ else if (reg >= ZADDR0 && reg <= ZADDR7)
+ ret = ZAR;
+ else if (reg == ZPC)
+ return LZPC;
+ else
+ return CREG;
+
+ /* If we get here, we have a data or address register. We
+ must check for a size or scale; if we find one, we must
+ return INDEXREG. */
+
+ s = str;
+
+ if (*s != '.' && *s != ':' && *s != '*')
+ return ret;
+
+ yylval.indexreg.reg = reg;
+
+ if (*s != '.' && *s != ':')
+ yylval.indexreg.size = SIZE_UNSPEC;
+ else
+ {
+ ++s;
+ switch (*s)
+ {
+ case 'w':
+ case 'W':
+ yylval.indexreg.size = SIZE_WORD;
+ ++s;
+ break;
+ case 'l':
+ case 'L':
+ yylval.indexreg.size = SIZE_LONG;
+ ++s;
+ break;
+ default:
+ yyerror (_("illegal size specification"));
+ yylval.indexreg.size = SIZE_UNSPEC;
+ break;
+ }
+ }
+
+ yylval.indexreg.scale = 1;
+
+ if (*s == '*' || *s == ':')
+ {
+ expressionS scale;
+
+ ++s;
+
+ hold = input_line_pointer;
+ input_line_pointer = s;
+ expression (&scale);
+ s = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (scale.X_op != O_constant)
+ yyerror (_("scale specification must resolve to a number"));
+ else
+ {
+ switch (scale.X_add_number)
+ {
+ case 1:
+ case 2:
+ case 4:
+ case 8:
+ yylval.indexreg.scale = scale.X_add_number;
+ break;
+ default:
+ yyerror (_("invalid scale value"));
+ break;
+ }
+ }
+ }
+
+ str = s;
+
+ return INDEXREG;
+ }
+
+ /* It must be an expression. Before we call expression, we need to
+ look ahead to see if there is a size specification. We must do
+ that first, because otherwise foo.l will be treated as the symbol
+ foo.l, rather than as the symbol foo with a long size
+ specification. The grammar requires that all expressions end at
+ the end of the operand, or with ',', '(', ']', ')'. */
+
+ parens = 0;
+ for (s = str; *s != '\0'; s++)
+ {
+ if (*s == '(')
+ {
+ if (parens == 0
+ && s > str
+ && (s[-1] == ')' || ISALNUM (s[-1])))
+ break;
+ ++parens;
+ }
+ else if (*s == ')')
+ {
+ if (parens == 0)
+ break;
+ --parens;
+ }
+ else if (parens == 0
+ && (*s == ',' || *s == ']'))
+ break;
+ }
+
+ yylval.exp.size = SIZE_UNSPEC;
+ if (s <= str + 2
+ || (s[-2] != '.' && s[-2] != ':'))
+ tail = 0;
+ else
+ {
+ switch (s[-1])
+ {
+ case 's':
+ case 'S':
+ case 'b':
+ case 'B':
+ yylval.exp.size = SIZE_BYTE;
+ break;
+ case 'w':
+ case 'W':
+ yylval.exp.size = SIZE_WORD;
+ break;
+ case 'l':
+ case 'L':
+ yylval.exp.size = SIZE_LONG;
+ break;
+ default:
+ break;
+ }
+ if (yylval.exp.size != SIZE_UNSPEC)
+ tail = 2;
+ }
+
+#ifdef OBJ_ELF
+ {
+ /* Look for @PLTPC, etc. */
+ char *cp;
+
+ yylval.exp.pic_reloc = pic_none;
+ cp = s - tail;
+ if (cp - 7 > str && cp[-7] == '@')
+ {
+ if (strncmp (cp - 7, "@TLSLDM", 7) == 0)
+ {
+ yylval.exp.pic_reloc = pic_tls_ldm;
+ tail += 7;
+ }
+ else if (strncmp (cp - 7, "@TLSLDO", 7) == 0)
+ {
+ yylval.exp.pic_reloc = pic_tls_ldo;
+ tail += 7;
+ }
+ }
+ else if (cp - 6 > str && cp[-6] == '@')
+ {
+ if (strncmp (cp - 6, "@PLTPC", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_plt_pcrel;
+ tail += 6;
+ }
+ else if (strncmp (cp - 6, "@GOTPC", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_got_pcrel;
+ tail += 6;
+ }
+ else if (strncmp (cp - 6, "@TLSGD", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_tls_gd;
+ tail += 6;
+ }
+ else if (strncmp (cp - 6, "@TLSIE", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_tls_ie;
+ tail += 6;
+ }
+ else if (strncmp (cp - 6, "@TLSLE", 6) == 0)
+ {
+ yylval.exp.pic_reloc = pic_tls_le;
+ tail += 6;
+ }
+ }
+ else if (cp - 4 > str && cp[-4] == '@')
+ {
+ if (strncmp (cp - 4, "@PLT", 4) == 0)
+ {
+ yylval.exp.pic_reloc = pic_plt_off;
+ tail += 4;
+ }
+ else if (strncmp (cp - 4, "@GOT", 4) == 0)
+ {
+ yylval.exp.pic_reloc = pic_got_off;
+ tail += 4;
+ }
+ }
+ }
+#endif
+
+ if (tail != 0)
+ {
+ c = s[-tail];
+ s[-tail] = 0;
+ }
+
+ hold = input_line_pointer;
+ input_line_pointer = str;
+ expression (&yylval.exp.exp);
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (tail != 0)
+ {
+ s[-tail] = c;
+ str = s;
+ }
+
+ return EXPR;
+}
+
+/* Parse an m68k operand. This is the only function which is called
+ from outside this file. */
+
+int
+m68k_ip_op (s, oparg)
+ char *s;
+ struct m68k_op *oparg;
+{
+ memset (oparg, 0, sizeof *oparg);
+ oparg->error = NULL;
+ oparg->index.reg = ZDATA0;
+ oparg->index.scale = 1;
+ oparg->disp.exp.X_op = O_absent;
+ oparg->odisp.exp.X_op = O_absent;
+
+ str = strorig = s;
+ op = oparg;
+
+ return yyparse ();
+}
+
+/* The error handler. */
+
+static void
+yyerror (s)
+ const char *s;
+{
+ op->error = s;
+}
diff --git a/gas/config/obj-aout.c b/gas/config/obj-aout.c
new file mode 100644
index 0000000..93ea904
--- /dev/null
+++ b/gas/config/obj-aout.c
@@ -0,0 +1,342 @@
+/* a.out object file format
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define OBJ_HEADER "obj-aout.h"
+
+#include "as.h"
+#undef NO_RELOC
+#include "aout/aout64.h"
+
+void
+obj_aout_frob_symbol (symbolS *sym, int *punt ATTRIBUTE_UNUSED)
+{
+ flagword flags;
+ asection *sec;
+ int type;
+
+ flags = symbol_get_bfdsym (sym)->flags;
+ type = aout_symbol (symbol_get_bfdsym (sym))->type;
+ sec = S_GET_SEGMENT (sym);
+
+ /* Only frob simple symbols this way right now. */
+ if (! (type & ~ (N_TYPE | N_EXT)))
+ {
+ if (type == (N_UNDF | N_EXT)
+ && sec == bfd_abs_section_ptr)
+ {
+ sec = bfd_und_section_ptr;
+ S_SET_SEGMENT (sym, sec);
+ }
+
+ if ((type & N_TYPE) != N_INDR
+ && (type & N_TYPE) != N_SETA
+ && (type & N_TYPE) != N_SETT
+ && (type & N_TYPE) != N_SETD
+ && (type & N_TYPE) != N_SETB
+ && type != N_WARNING
+ && (sec == bfd_abs_section_ptr
+ || sec == bfd_und_section_ptr))
+ return;
+ if (flags & BSF_EXPORT)
+ type |= N_EXT;
+
+ switch (type & N_TYPE)
+ {
+ case N_SETA:
+ case N_SETT:
+ case N_SETD:
+ case N_SETB:
+ /* Set the debugging flag for constructor symbols so that
+ BFD leaves them alone. */
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+
+ /* You can't put a common symbol in a set. The way a set
+ element works is that the symbol has a definition and a
+ name, and the linker adds the definition to the set of
+ that name. That does not work for a common symbol,
+ because the linker can't tell which common symbol the
+ user means. FIXME: Using as_bad here may be
+ inappropriate, since the user may want to force a
+ particular type without regard to the semantics of sets;
+ on the other hand, we certainly don't want anybody to be
+ mislead into thinking that their code will work. */
+ if (S_IS_COMMON (sym))
+ as_bad (_("Attempt to put a common symbol into set %s"),
+ S_GET_NAME (sym));
+ /* Similarly, you can't put an undefined symbol in a set. */
+ else if (! S_IS_DEFINED (sym))
+ as_bad (_("Attempt to put an undefined symbol into set %s"),
+ S_GET_NAME (sym));
+
+ break;
+ case N_INDR:
+ /* Put indirect symbols in the indirect section. */
+ S_SET_SEGMENT (sym, bfd_ind_section_ptr);
+ symbol_get_bfdsym (sym)->flags |= BSF_INDIRECT;
+ if (type & N_EXT)
+ {
+ symbol_get_bfdsym (sym)->flags |= BSF_EXPORT;
+ symbol_get_bfdsym (sym)->flags &=~ BSF_LOCAL;
+ }
+ break;
+ case N_WARNING:
+ /* Mark warning symbols. */
+ symbol_get_bfdsym (sym)->flags |= BSF_WARNING;
+ break;
+ }
+ }
+ else
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+
+ aout_symbol (symbol_get_bfdsym (sym))->type = type;
+
+ /* Double check weak symbols. */
+ if (S_IS_WEAK (sym) && S_IS_COMMON (sym))
+ as_bad (_("Symbol `%s' can not be both weak and common"),
+ S_GET_NAME (sym));
+}
+
+void
+obj_aout_frob_file_before_fix (void)
+{
+ /* Relocation processing may require knowing the VMAs of the sections.
+ Since writing to a section will cause the BFD back end to compute the
+ VMAs, fake it out here.... */
+ bfd_byte b = 0;
+ bfd_boolean x = TRUE;
+ if (bfd_section_size (stdoutput, text_section) != 0)
+ x = bfd_set_section_contents (stdoutput, text_section, &b, (file_ptr) 0,
+ (bfd_size_type) 1);
+ else if (bfd_section_size (stdoutput, data_section) != 0)
+ x = bfd_set_section_contents (stdoutput, data_section, &b, (file_ptr) 0,
+ (bfd_size_type) 1);
+
+ gas_assert (x);
+}
+
+static void
+obj_aout_line (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Assume delimiter is part of expression.
+ BSD4.2 as fails with delightful bug, so we
+ are not being incompatible here. */
+ new_logical_line ((char *) NULL, (int) (get_absolute_expression ()));
+ demand_empty_rest_of_line ();
+}
+
+/* Handle .weak. This is a GNU extension. */
+
+static void
+obj_aout_weak (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+/* Handle .type. On {Net,Open}BSD, this is used to set the n_other field,
+ which is then apparently used when doing dynamic linking. Older
+ versions of gas ignored the .type pseudo-op, so we also ignore it if
+ we can't parse it. */
+
+static void
+obj_aout_type (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '@')
+ {
+ ++input_line_pointer;
+ if (strncmp (input_line_pointer, "object", 6) == 0)
+ S_SET_OTHER (sym, 1);
+ else if (strncmp (input_line_pointer, "function", 8) == 0)
+ S_SET_OTHER (sym, 2);
+ }
+ }
+
+ /* Ignore everything else on the line. */
+ s_ignore (0);
+}
+
+/* Support for an AOUT emulation. */
+
+static void
+aout_pop_insert (void)
+{
+ pop_insert (aout_pseudo_table);
+}
+
+static int
+obj_aout_s_get_other (symbolS *sym)
+{
+ return aout_symbol (symbol_get_bfdsym (sym))->other;
+}
+
+static void
+obj_aout_s_set_other (symbolS *sym, int o)
+{
+ aout_symbol (symbol_get_bfdsym (sym))->other = o;
+}
+
+static int
+obj_aout_sec_sym_ok_for_reloc (asection *sec ATTRIBUTE_UNUSED)
+{
+ return obj_sec_sym_ok_for_reloc (sec);
+}
+
+static void
+obj_aout_process_stab (segT seg ATTRIBUTE_UNUSED,
+ int w,
+ const char *s,
+ int t,
+ int o,
+ int d)
+{
+ aout_process_stab (w, s, t, o, d);
+}
+
+static int
+obj_aout_s_get_desc (symbolS *sym)
+{
+ return aout_symbol (symbol_get_bfdsym (sym))->desc;
+}
+
+static void
+obj_aout_s_set_desc (symbolS *sym, int d)
+{
+ aout_symbol (symbol_get_bfdsym (sym))->desc = d;
+}
+
+static int
+obj_aout_s_get_type (symbolS *sym)
+{
+ return aout_symbol (symbol_get_bfdsym (sym))->type;
+}
+
+static void
+obj_aout_s_set_type (symbolS *sym, int t)
+{
+ aout_symbol (symbol_get_bfdsym (sym))->type = t;
+}
+
+static int
+obj_aout_separate_stab_sections (void)
+{
+ return 0;
+}
+
+/* When changed, make sure these table entries match the single-format
+ definitions in obj-aout.h. */
+
+const struct format_ops aout_format_ops =
+{
+ bfd_target_aout_flavour,
+ 1, /* dfl_leading_underscore. */
+ 0, /* emit_section_symbols. */
+ 0, /* begin. */
+ 0, /* app_file. */
+ obj_aout_frob_symbol,
+ 0, /* frob_file. */
+ 0, /* frob_file_before_adjust. */
+ obj_aout_frob_file_before_fix,
+ 0, /* frob_file_after_relocs. */
+ 0, /* s_get_size. */
+ 0, /* s_set_size. */
+ 0, /* s_get_align. */
+ 0, /* s_set_align. */
+ obj_aout_s_get_other,
+ obj_aout_s_set_other,
+ obj_aout_s_get_desc,
+ obj_aout_s_set_desc,
+ obj_aout_s_get_type,
+ obj_aout_s_set_type,
+ 0, /* copy_symbol_attributes. */
+ 0, /* generate_asm_lineno. */
+ obj_aout_process_stab,
+ obj_aout_separate_stab_sections,
+ 0, /* init_stab_section. */
+ obj_aout_sec_sym_ok_for_reloc,
+ aout_pop_insert,
+ 0, /* ecoff_set_ext. */
+ 0, /* read_begin_hook. */
+ 0, /* symbol_new_hook. */
+ 0, /* symbol_clone_hook. */
+ 0 /* adjust_symtab. */
+};
+
+const pseudo_typeS aout_pseudo_table[] =
+{
+ {"line", obj_aout_line, 0}, /* Source code line number. */
+ {"ln", obj_aout_line, 0}, /* COFF line number that we use anyway. */
+
+ {"weak", obj_aout_weak, 0}, /* Mark symbol as weak. */
+
+ {"type", obj_aout_type, 0},
+
+ /* coff debug pseudos (ignored) */
+ {"def", s_ignore, 0},
+ {"dim", s_ignore, 0},
+ {"endef", s_ignore, 0},
+ {"ident", s_ignore, 0},
+ {"line", s_ignore, 0},
+ {"ln", s_ignore, 0},
+ {"scl", s_ignore, 0},
+ {"size", s_ignore, 0},
+ {"tag", s_ignore, 0},
+ {"val", s_ignore, 0},
+ {"version", s_ignore, 0},
+
+ {"optim", s_ignore, 0}, /* For sun386i cc (?). */
+
+ /* other stuff */
+ {"ABORT", s_abort, 0},
+
+ {NULL, NULL, 0}
+};
diff --git a/gas/config/obj-aout.h b/gas/config/obj-aout.h
new file mode 100644
index 0000000..784dae7
--- /dev/null
+++ b/gas/config/obj-aout.h
@@ -0,0 +1,70 @@
+/* obj-aout.h, a.out object file format for gas, the assembler.
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Tag to validate a.out object file format processing */
+#define OBJ_AOUT 1
+
+#include "targ-cpu.h"
+
+#include "bfd/libaout.h"
+
+#define OUTPUT_FLAVOR bfd_target_aout_flavour
+
+extern const pseudo_typeS aout_pseudo_table[];
+
+#ifndef obj_pop_insert
+#define obj_pop_insert() pop_insert (aout_pseudo_table)
+#endif
+
+/* Symbol table entry data type. */
+
+typedef struct nlist obj_symbol_type; /* Symbol table entry. */
+
+/* Symbol table macros and constants */
+
+#define S_SET_OTHER(S,V) \
+ (aout_symbol (symbol_get_bfdsym (S))->other = (V))
+#define S_SET_TYPE(S,T) \
+ (aout_symbol (symbol_get_bfdsym (S))->type = (T))
+#define S_SET_DESC(S,D) \
+ (aout_symbol (symbol_get_bfdsym (S))->desc = (D))
+#define S_GET_OTHER(S) \
+ (aout_symbol (symbol_get_bfdsym (S))->other)
+#define S_GET_TYPE(S) \
+ (aout_symbol (symbol_get_bfdsym (S))->type)
+#define S_GET_DESC(S) \
+ (aout_symbol (symbol_get_bfdsym (S))->desc)
+
+extern asection *text_section, *data_section, *bss_section;
+
+#define obj_frob_symbol(S,PUNT) obj_aout_frob_symbol (S, &PUNT)
+#define obj_frob_file_before_fix() obj_aout_frob_file_before_fix ()
+
+extern void obj_aout_frob_symbol (symbolS *, int *);
+extern void obj_aout_frob_file_before_fix (void);
+
+#define obj_sec_sym_ok_for_reloc(SEC) 1
+
+#define obj_read_begin_hook() {;}
+#define obj_symbol_new_hook(s) {;}
+
+#define EMIT_SECTION_SYMBOLS 0
+
+#define AOUT_STABS
diff --git a/gas/config/obj-coff-seh.c b/gas/config/obj-coff-seh.c
new file mode 100644
index 0000000..ad3fc87
--- /dev/null
+++ b/gas/config/obj-coff-seh.c
@@ -0,0 +1,1024 @@
+/* seh pdata/xdata coff object file format
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "obj-coff-seh.h"
+
+
+/* Private segment collection list. */
+struct seh_seg_list {
+ segT seg;
+ int subseg;
+ char *seg_name;
+};
+
+/* Local data. */
+static seh_context *seh_ctx_cur = NULL;
+
+static struct hash_control *seh_hash;
+
+static struct seh_seg_list *x_segcur = NULL;
+static struct seh_seg_list *p_segcur = NULL;
+
+static void write_function_xdata (seh_context *);
+static void write_function_pdata (seh_context *);
+
+
+/* Build based on segment the derived .pdata/.xdata
+ segment name containing origin segment's postfix name part. */
+static char *
+get_pxdata_name (segT seg, const char *base_name)
+{
+ const char *name,*dollar, *dot;
+ char *sname;
+
+ name = bfd_get_section_name (stdoutput, seg);
+
+ dollar = strchr (name, '$');
+ dot = strchr (name + 1, '.');
+
+ if (!dollar && !dot)
+ name = "";
+ else if (!dollar)
+ name = dot;
+ else if (!dot)
+ name = dollar;
+ else if (dot < dollar)
+ name = dot;
+ else
+ name = dollar;
+
+ sname = concat (base_name, name, NULL);
+
+ return sname;
+}
+
+/* Allocate a seh_seg_list structure. */
+static struct seh_seg_list *
+alloc_pxdata_item (segT seg, int subseg, char *name)
+{
+ struct seh_seg_list *r;
+
+ r = (struct seh_seg_list *)
+ xmalloc (sizeof (struct seh_seg_list) + strlen (name));
+ r->seg = seg;
+ r->subseg = subseg;
+ r->seg_name = name;
+ return r;
+}
+
+/* Generate pdata/xdata segment with same linkonce properties
+ of based segment. */
+static segT
+make_pxdata_seg (segT cseg, char *name)
+{
+ segT save_seg = now_seg;
+ int save_subseg = now_subseg;
+ segT r;
+ flagword flags;
+
+ r = subseg_new (name, 0);
+ /* Check if code segment is marked as linked once. */
+ flags = bfd_get_section_flags (stdoutput, cseg)
+ & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
+ | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE
+ | SEC_LINK_DUPLICATES_SAME_CONTENTS);
+
+ /* Add standard section flags. */
+ flags |= SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA;
+
+ /* Apply possibly linked once flags to new generated segment, too. */
+ if (!bfd_set_section_flags (stdoutput, r, flags))
+ as_bad (_("bfd_set_section_flags: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ /* Restore to previous segment. */
+ subseg_set (save_seg, save_subseg);
+ return r;
+}
+
+static void
+seh_hash_insert (const char *name, struct seh_seg_list *item)
+{
+ const char *error_string;
+
+ if ((error_string = hash_jam (seh_hash, name, (char *) item)))
+ as_fatal (_("Inserting \"%s\" into structure table failed: %s"),
+ name, error_string);
+}
+
+static struct seh_seg_list *
+seh_hash_find (char *name)
+{
+ return (struct seh_seg_list *) hash_find (seh_hash, name);
+}
+
+static struct seh_seg_list *
+seh_hash_find_or_make (segT cseg, const char *base_name)
+{
+ struct seh_seg_list *item;
+ char *name;
+
+ /* Initialize seh_hash once. */
+ if (!seh_hash)
+ seh_hash = hash_new ();
+
+ name = get_pxdata_name (cseg, base_name);
+
+ item = seh_hash_find (name);
+ if (!item)
+ {
+ item = alloc_pxdata_item (make_pxdata_seg (cseg, name), 0, name);
+
+ seh_hash_insert (item->seg_name, item);
+ }
+ else
+ free (name);
+
+ return item;
+}
+
+/* Check if current segment has same name. */
+static int
+seh_validate_seg (const char *directive)
+{
+ const char *cseg_name, *nseg_name;
+ if (seh_ctx_cur->code_seg == now_seg)
+ return 1;
+ cseg_name = bfd_get_section_name (stdoutput, seh_ctx_cur->code_seg);
+ nseg_name = bfd_get_section_name (stdoutput, now_seg);
+ as_bad (_("%s used in segment '%s' instead of expected '%s'"),
+ directive, nseg_name, cseg_name);
+ ignore_rest_of_line ();
+ return 0;
+}
+
+/* Switch back to the code section, whatever that may be. */
+static void
+obj_coff_seh_code (int ignored ATTRIBUTE_UNUSED)
+{
+ subseg_set (seh_ctx_cur->code_seg, 0);
+}
+
+static void
+switch_xdata (int subseg, segT code_seg)
+{
+ x_segcur = seh_hash_find_or_make (code_seg, ".xdata");
+
+ subseg_set (x_segcur->seg, subseg);
+}
+
+static void
+switch_pdata (segT code_seg)
+{
+ p_segcur = seh_hash_find_or_make (code_seg, ".pdata");
+
+ subseg_set (p_segcur->seg, p_segcur->subseg);
+}
+
+/* Parsing routines. */
+
+/* Return the style of SEH unwind info to generate. */
+
+static seh_kind
+seh_get_target_kind (void)
+{
+ if (!stdoutput)
+ return seh_kind_unknown;
+ switch (bfd_get_arch (stdoutput))
+ {
+ case bfd_arch_arm:
+ case bfd_arch_powerpc:
+ case bfd_arch_sh:
+ return seh_kind_arm;
+ case bfd_arch_i386:
+ switch (bfd_get_mach (stdoutput))
+ {
+ case bfd_mach_x86_64:
+ case bfd_mach_x86_64_intel_syntax:
+ return seh_kind_x64;
+ default:
+ break;
+ }
+ /* FALL THROUGH. */
+ case bfd_arch_mips:
+ return seh_kind_mips;
+ case bfd_arch_ia64:
+ /* Should return seh_kind_x64. But not implemented yet. */
+ return seh_kind_unknown;
+ default:
+ break;
+ }
+ return seh_kind_unknown;
+}
+
+/* Verify that we're in the context of a seh_proc. */
+
+static int
+verify_context (const char *directive)
+{
+ if (seh_ctx_cur == NULL)
+ {
+ as_bad (_("%s used outside of .seh_proc block"), directive);
+ ignore_rest_of_line ();
+ return 0;
+ }
+ return 1;
+}
+
+/* Similar, except we also verify the appropriate target. */
+
+static int
+verify_context_and_target (const char *directive, seh_kind target)
+{
+ if (seh_get_target_kind () != target)
+ {
+ as_warn (_("%s ignored for this target"), directive);
+ ignore_rest_of_line ();
+ return 0;
+ }
+ return verify_context (directive);
+}
+
+/* Skip whitespace and a comma. Error if the comma is not seen. */
+
+static int
+skip_whitespace_and_comma (int required)
+{
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ return 1;
+ }
+ else if (required)
+ {
+ as_bad (_("missing separator"));
+ ignore_rest_of_line ();
+ }
+ else
+ demand_empty_rest_of_line ();
+ return 0;
+}
+
+/* Mark current context to use 32-bit instruction (arm). */
+
+static void
+obj_coff_seh_32 (int what)
+{
+ if (!verify_context_and_target ((what ? ".seh_32" : ".seh_no32"),
+ seh_kind_arm))
+ return;
+
+ seh_ctx_cur->use_instruction_32 = (what ? 1 : 0);
+ demand_empty_rest_of_line ();
+}
+
+/* Set for current context the handler and optional data (arm). */
+
+static void
+obj_coff_seh_eh (int what ATTRIBUTE_UNUSED)
+{
+ if (!verify_context_and_target (".seh_eh", seh_kind_arm))
+ return;
+
+ /* Write block to .text if exception handler is set. */
+ seh_ctx_cur->handler_written = 1;
+ emit_expr (&seh_ctx_cur->handler, 4);
+ emit_expr (&seh_ctx_cur->handler_data, 4);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Set for current context the default handler (x64). */
+
+static void
+obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
+{
+ char *symbol_name;
+ char name_end;
+
+ if (!verify_context (".seh_handler"))
+ return;
+
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_bad (_(".seh_handler requires a handler"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '@')
+ {
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ seh_ctx_cur->handler.X_op = O_constant;
+ seh_ctx_cur->handler.X_add_number = 0;
+
+ if (strcasecmp (symbol_name, "@0") == 0
+ || strcasecmp (symbol_name, "@null") == 0)
+ ;
+ else if (strcasecmp (symbol_name, "@1") == 0)
+ seh_ctx_cur->handler.X_add_number = 1;
+ else
+ as_bad (_("unknown constant value '%s' for handler"), symbol_name);
+
+ *input_line_pointer = name_end;
+ }
+ else
+ expression (&seh_ctx_cur->handler);
+
+ seh_ctx_cur->handler_data.X_op = O_constant;
+ seh_ctx_cur->handler_data.X_add_number = 0;
+ seh_ctx_cur->handler_flags = 0;
+
+ if (!skip_whitespace_and_comma (0))
+ return;
+
+ if (seh_get_target_kind () == seh_kind_x64)
+ {
+ do
+ {
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (strcasecmp (symbol_name, "@unwind") == 0)
+ seh_ctx_cur->handler_flags |= UNW_FLAG_UHANDLER;
+ else if (strcasecmp (symbol_name, "@except") == 0)
+ seh_ctx_cur->handler_flags |= UNW_FLAG_EHANDLER;
+ else
+ as_bad (_(".seh_handler constant '%s' unknown"), symbol_name);
+
+ *input_line_pointer = name_end;
+ }
+ while (skip_whitespace_and_comma (0));
+ }
+ else
+ {
+ expression (&seh_ctx_cur->handler_data);
+ demand_empty_rest_of_line ();
+
+ if (seh_ctx_cur->handler_written)
+ as_warn (_(".seh_handler after .seh_eh is ignored"));
+ }
+}
+
+/* Switch to subsection for handler data for exception region (x64). */
+
+static void
+obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED)
+{
+ if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64))
+ return;
+ demand_empty_rest_of_line ();
+
+ switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg);
+}
+
+/* Mark end of current context. */
+
+static void
+do_seh_endproc (void)
+{
+ seh_ctx_cur->end_addr = symbol_temp_new_now ();
+
+ write_function_xdata (seh_ctx_cur);
+ write_function_pdata (seh_ctx_cur);
+ seh_ctx_cur = NULL;
+}
+
+static void
+obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (seh_ctx_cur == NULL)
+ {
+ as_bad (_(".seh_endproc used without .seh_proc"));
+ return;
+ }
+ seh_validate_seg (".seh_endproc");
+ do_seh_endproc ();
+}
+
+/* Mark begin of new context. */
+
+static void
+obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
+{
+ char *symbol_name;
+ char name_end;
+
+ if (seh_ctx_cur != NULL)
+ {
+ as_bad (_("previous SEH entry not closed (missing .seh_endproc)"));
+ do_seh_endproc ();
+ }
+
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_bad (_(".seh_proc requires function label name"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ seh_ctx_cur = XCNEW (seh_context);
+
+ seh_ctx_cur->code_seg = now_seg;
+
+ if (seh_get_target_kind () == seh_kind_x64)
+ {
+ x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata");
+ seh_ctx_cur->subsection = x_segcur->subseg;
+ x_segcur->subseg += 2;
+ }
+
+ SKIP_WHITESPACE ();
+
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ seh_ctx_cur->func_name = xstrdup (symbol_name);
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+
+ seh_ctx_cur->start_addr = symbol_temp_new_now ();
+}
+
+/* Mark end of prologue for current context. */
+
+static void
+obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED)
+{
+ if (!verify_context (".seh_endprologue")
+ || !seh_validate_seg (".seh_endprologue"))
+ return;
+ demand_empty_rest_of_line ();
+
+ if (seh_ctx_cur->endprologue_addr != NULL)
+ as_warn (_("duplicate .seh_endprologue in .seh_proc block"));
+ else
+ seh_ctx_cur->endprologue_addr = symbol_temp_new_now ();
+}
+
+/* End-of-file hook. */
+
+void
+obj_coff_seh_do_final (void)
+{
+ if (seh_ctx_cur != NULL)
+ {
+ as_bad (_("open SEH entry at end of file (missing .cfi_endproc)"));
+ do_seh_endproc ();
+ }
+}
+
+/* Enter a prologue element into current context (x64). */
+
+static void
+seh_x64_make_prologue_element (int code, int info, offsetT off)
+{
+ seh_prologue_element *n;
+
+ if (seh_ctx_cur == NULL)
+ return;
+ if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max)
+ {
+ seh_ctx_cur->elems_max += 8;
+ seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element,
+ seh_ctx_cur->elems,
+ seh_ctx_cur->elems_max);
+ }
+
+ n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++];
+ n->code = code;
+ n->info = info;
+ n->off = off;
+ n->pc_addr = symbol_temp_new_now ();
+}
+
+/* Helper to read a register name from input stream (x64). */
+
+static int
+seh_x64_read_reg (const char *directive, int kind)
+{
+ static const char * const int_regs[16] =
+ { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi",
+ "r8","r9","r10","r11","r12","r13","r14","r15" };
+ static const char * const xmm_regs[16] =
+ { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+ "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" };
+
+ const char * const *regs = NULL;
+ char name_end;
+ char *symbol_name = NULL;
+ int i;
+
+ switch (kind)
+ {
+ case 0:
+ case 1:
+ regs = int_regs;
+ break;
+ case 2:
+ regs = xmm_regs;
+ break;
+ default:
+ abort ();
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '%')
+ ++input_line_pointer;
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ for (i = 0; i < 16; i++)
+ if (! strcasecmp (regs[i], symbol_name))
+ break;
+
+ *input_line_pointer = name_end;
+
+ /* Error if register not found, or EAX used as a frame pointer. */
+ if (i == 16 || (kind == 0 && i == 0))
+ {
+ as_bad (_("invalid register for %s"), directive);
+ return -1;
+ }
+
+ return i;
+}
+
+/* Add a register push-unwind token to the current context. */
+
+static void
+obj_coff_seh_pushreg (int what ATTRIBUTE_UNUSED)
+{
+ int reg;
+
+ if (!verify_context_and_target (".seh_pushreg", seh_kind_x64)
+ || !seh_validate_seg (".seh_pushreg"))
+ return;
+
+ reg = seh_x64_read_reg (".seh_pushreg", 1);
+ demand_empty_rest_of_line ();
+
+ if (reg < 0)
+ return;
+
+ seh_x64_make_prologue_element (UWOP_PUSH_NONVOL, reg, 0);
+}
+
+/* Add a register frame-unwind token to the current context. */
+
+static void
+obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED)
+{
+ if (!verify_context_and_target (".seh_pushframe", seh_kind_x64)
+ || !seh_validate_seg (".seh_pushframe"))
+ return;
+ demand_empty_rest_of_line ();
+
+ seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, 0, 0);
+}
+
+/* Add a register save-unwind token to current context. */
+
+static void
+obj_coff_seh_save (int what)
+{
+ const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm");
+ int code, reg, scale;
+ offsetT off;
+
+ if (!verify_context_and_target (directive, seh_kind_x64)
+ || !seh_validate_seg (directive))
+ return;
+
+ reg = seh_x64_read_reg (directive, what);
+
+ if (!skip_whitespace_and_comma (1))
+ return;
+
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (reg < 0)
+ return;
+ if (off < 0)
+ {
+ as_bad (_("%s offset is negative"), directive);
+ return;
+ }
+
+ scale = (what == 1 ? 8 : 16);
+
+ if ((off & (scale - 1)) == 0 && off <= (offsetT) (0xffff * scale))
+ {
+ code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128);
+ off /= scale;
+ }
+ else if (off < (offsetT) 0xffffffff)
+ code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR);
+ else
+ {
+ as_bad (_("%s offset out of range"), directive);
+ return;
+ }
+
+ seh_x64_make_prologue_element (code, reg, off);
+}
+
+/* Add a stack-allocation token to current context. */
+
+static void
+obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED)
+{
+ offsetT off;
+ int code, info;
+
+ if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64)
+ || !seh_validate_seg (".seh_stackalloc"))
+ return;
+
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (off == 0)
+ return;
+ if (off < 0)
+ {
+ as_bad (_(".seh_stackalloc offset is negative"));
+ return;
+ }
+
+ if ((off & 7) == 0 && off <= 128)
+ code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0;
+ else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8))
+ code = UWOP_ALLOC_LARGE, info = 0, off >>= 3;
+ else if (off <= (offsetT) 0xffffffff)
+ code = UWOP_ALLOC_LARGE, info = 1;
+ else
+ {
+ as_bad (_(".seh_stackalloc offset out of range"));
+ return;
+ }
+
+ seh_x64_make_prologue_element (code, info, off);
+}
+
+/* Add a frame-pointer token to current context. */
+
+static void
+obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED)
+{
+ offsetT off;
+ int reg;
+
+ if (!verify_context_and_target (".seh_setframe", seh_kind_x64)
+ || !seh_validate_seg (".seh_setframe"))
+ return;
+
+ reg = seh_x64_read_reg (".seh_setframe", 0);
+
+ if (!skip_whitespace_and_comma (1))
+ return;
+
+ off = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (reg < 0)
+ return;
+ if (off < 0)
+ as_bad (_(".seh_setframe offset is negative"));
+ else if (off > 240)
+ as_bad (_(".seh_setframe offset out of range"));
+ else if (off & 15)
+ as_bad (_(".seh_setframe offset not a multiple of 16"));
+ else if (seh_ctx_cur->framereg != 0)
+ as_bad (_("duplicate .seh_setframe in current .seh_proc"));
+ else
+ {
+ seh_ctx_cur->framereg = reg;
+ seh_ctx_cur->frameoff = off;
+ seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0);
+ }
+}
+
+/* Data writing routines. */
+
+/* Output raw integers in 1, 2, or 4 bytes. */
+
+static inline void
+out_one (int byte)
+{
+ FRAG_APPEND_1_CHAR (byte);
+}
+
+static inline void
+out_two (int data)
+{
+ md_number_to_chars (frag_more (2), data, 2);
+}
+
+static inline void
+out_four (int data)
+{
+ md_number_to_chars (frag_more (4), data, 4);
+}
+
+/* Write out prologue data for x64. */
+
+static void
+seh_x64_write_prologue_data (const seh_context *c)
+{
+ int i;
+
+ /* We have to store in reverse order. */
+ for (i = c->elems_count - 1; i >= 0; --i)
+ {
+ const seh_prologue_element *e = c->elems + i;
+ expressionS exp;
+
+ /* First comes byte offset in code. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = e->pc_addr;
+ exp.X_op_symbol = c->start_addr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 1);
+
+ /* Second comes code+info packed into a byte. */
+ out_one ((e->info << 4) | e->code);
+
+ switch (e->code)
+ {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_ALLOC_SMALL:
+ case UWOP_SET_FPREG:
+ case UWOP_PUSH_MACHFRAME:
+ /* These have no extra data. */
+ break;
+
+ case UWOP_ALLOC_LARGE:
+ if (e->info)
+ {
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR:
+ /* An unscaled 4 byte offset. */
+ out_four (e->off);
+ break;
+ }
+ /* FALLTHRU */
+
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128:
+ /* A scaled 2 byte offset. */
+ out_two (e->off);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+}
+
+static int
+seh_x64_size_prologue_data (const seh_context *c)
+{
+ int i, ret = 0;
+
+ for (i = c->elems_count - 1; i >= 0; --i)
+ switch (c->elems[i].code)
+ {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_ALLOC_SMALL:
+ case UWOP_SET_FPREG:
+ case UWOP_PUSH_MACHFRAME:
+ ret += 1;
+ break;
+
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM128:
+ ret += 2;
+ break;
+
+ case UWOP_SAVE_NONVOL_FAR:
+ case UWOP_SAVE_XMM128_FAR:
+ ret += 3;
+ break;
+
+ case UWOP_ALLOC_LARGE:
+ ret += (c->elems[i].info ? 3 : 2);
+ break;
+
+ default:
+ abort ();
+ }
+
+ return ret;
+}
+
+/* Write out the xdata information for one function (x64). */
+
+static void
+seh_x64_write_function_xdata (seh_context *c)
+{
+ int flags, count_unwind_codes;
+ expressionS exp;
+
+ /* Set 4-byte alignment. */
+ frag_align (2, 0, 0);
+
+ c->xdata_addr = symbol_temp_new_now ();
+ flags = c->handler_flags;
+ count_unwind_codes = seh_x64_size_prologue_data (c);
+
+ /* ubyte:3 version, ubyte:5 flags. */
+ out_one ((flags << 3) | 1);
+
+ /* Size of prologue. */
+ if (c->endprologue_addr)
+ {
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = c->endprologue_addr;
+ exp.X_op_symbol = c->start_addr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 1);
+ }
+ else
+ out_one (0);
+
+ /* Number of slots (i.e. shorts) in the unwind codes array. */
+ if (count_unwind_codes > 255)
+ as_fatal (_("too much unwind data in this .seh_proc"));
+ out_one (count_unwind_codes);
+
+ /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */
+ /* Note that frameoff is already a multiple of 16, and therefore
+ the offset is already both scaled and shifted into place. */
+ out_one (c->frameoff | c->framereg);
+
+ seh_x64_write_prologue_data (c);
+
+ /* We need to align prologue data. */
+ if (count_unwind_codes & 1)
+ out_two (0);
+
+ if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER))
+ {
+ /* Force the use of segment-relative relocations instead of absolute
+ valued expressions. Don't adjust for constants (e.g. NULL). */
+ if (c->handler.X_op == O_symbol)
+ c->handler.X_op = O_symbol_rva;
+ emit_expr (&c->handler, 4);
+ }
+
+ /* Handler data will be tacked in here by subsections. */
+}
+
+/* Write out xdata for one function. */
+
+static void
+write_function_xdata (seh_context *c)
+{
+ segT save_seg = now_seg;
+ int save_subseg = now_subseg;
+
+ /* MIPS, SH, ARM don't have xdata. */
+ if (seh_get_target_kind () != seh_kind_x64)
+ return;
+
+ switch_xdata (c->subsection, c->code_seg);
+
+ seh_x64_write_function_xdata (c);
+
+ subseg_set (save_seg, save_subseg);
+}
+
+/* Write pdata section data for one function (arm). */
+
+static void
+seh_arm_write_function_pdata (seh_context *c)
+{
+ expressionS exp;
+ unsigned int prol_len = 0, func_len = 0;
+ unsigned int val;
+
+ /* Start address of the function. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = c->start_addr;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 4);
+
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = c->end_addr;
+ exp.X_op_symbol = c->start_addr;
+ exp.X_add_number = 0;
+ if (resolve_expression (&exp) && exp.X_op == O_constant)
+ func_len = exp.X_add_number;
+ else
+ as_bad (_(".seh_endproc in a different section from .seh_proc"));
+
+ if (c->endprologue_addr)
+ {
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = c->endprologue_addr;
+ exp.X_op_symbol = c->start_addr;
+ exp.X_add_number = 0;
+
+ if (resolve_expression (&exp) && exp.X_op == O_constant)
+ prol_len = exp.X_add_number;
+ else
+ as_bad (_(".seh_endprologue in a different section from .seh_proc"));
+ }
+
+ /* Both function and prologue are in units of instructions. */
+ func_len >>= (c->use_instruction_32 ? 2 : 1);
+ prol_len >>= (c->use_instruction_32 ? 2 : 1);
+
+ /* Assemble the second word of the pdata. */
+ val = prol_len & 0xff;
+ val |= (func_len & 0x3fffff) << 8;
+ if (c->use_instruction_32)
+ val |= 0x40000000U;
+ if (c->handler_written)
+ val |= 0x80000000U;
+ out_four (val);
+}
+
+/* Write out pdata for one function. */
+
+static void
+write_function_pdata (seh_context *c)
+{
+ expressionS exp;
+ segT save_seg = now_seg;
+ int save_subseg = now_subseg;
+ memset (&exp, 0, sizeof (expressionS));
+ switch_pdata (c->code_seg);
+
+ switch (seh_get_target_kind ())
+ {
+ case seh_kind_x64:
+ exp.X_op = O_symbol_rva;
+ exp.X_add_number = 0;
+
+ exp.X_add_symbol = c->start_addr;
+ emit_expr (&exp, 4);
+ exp.X_op = O_symbol_rva;
+ exp.X_add_number = 0;
+ exp.X_add_symbol = c->end_addr;
+ emit_expr (&exp, 4);
+ exp.X_op = O_symbol_rva;
+ exp.X_add_number = 0;
+ exp.X_add_symbol = c->xdata_addr;
+ emit_expr (&exp, 4);
+ break;
+
+ case seh_kind_mips:
+ exp.X_op = O_symbol;
+ exp.X_add_number = 0;
+
+ exp.X_add_symbol = c->start_addr;
+ emit_expr (&exp, 4);
+ exp.X_add_symbol = c->end_addr;
+ emit_expr (&exp, 4);
+
+ emit_expr (&c->handler, 4);
+ emit_expr (&c->handler_data, 4);
+
+ exp.X_add_symbol = (c->endprologue_addr
+ ? c->endprologue_addr
+ : c->start_addr);
+ emit_expr (&exp, 4);
+ break;
+
+ case seh_kind_arm:
+ seh_arm_write_function_pdata (c);
+ break;
+
+ default:
+ abort ();
+ }
+
+ subseg_set (save_seg, save_subseg);
+}
diff --git a/gas/config/obj-coff-seh.h b/gas/config/obj-coff-seh.h
new file mode 100644
index 0000000..cf49485
--- /dev/null
+++ b/gas/config/obj-coff-seh.h
@@ -0,0 +1,206 @@
+/* seh pdata/xdata coff object file format
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Short overview:
+ There are at the moment three different function entry formats preset.
+ The first is the MIPS one. The second version
+ is for ARM, PPC, SH3, and SH4 mainly for Windows CE.
+ The third is the IA64 and x64 version. Note, the IA64 isn't implemented yet,
+ but to find information about it, please see specification about IA64 on
+ http://download.intel.com/design/Itanium/Downloads/245358.pdf file.
+
+ The first version has just entries in the pdata section: BeginAddress,
+ EndAddress, ExceptionHandler, HandlerData, and PrologueEndAddress. Each
+ value is a pointer to the corresponding data and has size of 4 bytes.
+
+ The second variant has the following entries in the pdata section.
+ BeginAddress, PrologueLength (8 bits), EndAddress (22 bits),
+ Use-32-bit-instruction (1 bit), and Exception-Handler-Exists (1 bit).
+ If the FunctionLength is zero, or the Exception-Handler-Exists bit
+ is true, a PDATA_EH block is placed directly before function entry.
+
+ The third version has a function entry block of BeginAddress (RVA),
+ EndAddress (RVA), and UnwindData (RVA). The description of the
+ prologue, excepetion-handler, and additional SEH data is stored
+ within the UNWIND_DATA field in the xdata section.
+
+ The pseudos:
+ .seh_proc <fct_name>
+ .seh_endprologue
+ .seh_handler <handler>[,@unwind][,@except] (x64)
+ .seh_handler <handler>[,<handler_data>] (others)
+ .seh_handlerdata
+ .seh_eh
+ .seh_32/.seh_no32
+ .seh_endproc
+ .seh_setframe <reg>,<offset>
+ .seh_stackalloc
+ .seh_pushreg
+ .seh_savereg
+ .seh_savexmm
+ .seh_pushframe
+ .seh_code
+*/
+
+/* architecture specific pdata/xdata handling. */
+#define SEH_CMDS \
+ {"seh_proc", obj_coff_seh_proc, 0}, \
+ {"seh_endproc", obj_coff_seh_endproc, 0}, \
+ {"seh_pushreg", obj_coff_seh_pushreg, 0}, \
+ {"seh_savereg", obj_coff_seh_save, 1}, \
+ {"seh_savexmm", obj_coff_seh_save, 2}, \
+ {"seh_pushframe", obj_coff_seh_pushframe, 0}, \
+ {"seh_endprologue", obj_coff_seh_endprologue, 0}, \
+ {"seh_setframe", obj_coff_seh_setframe, 0}, \
+ {"seh_stackalloc", obj_coff_seh_stackalloc, 0}, \
+ {"seh_eh", obj_coff_seh_eh, 0}, \
+ {"seh_32", obj_coff_seh_32, 1}, \
+ {"seh_no32", obj_coff_seh_32, 0}, \
+ {"seh_handler", obj_coff_seh_handler, 0}, \
+ {"seh_code", obj_coff_seh_code, 0}, \
+ {"seh_handlerdata", obj_coff_seh_handlerdata, 0},
+
+/* Type definitions. */
+
+typedef struct seh_prologue_element
+{
+ int code;
+ int info;
+ offsetT off;
+ symbolS *pc_addr;
+} seh_prologue_element;
+
+typedef struct seh_context
+{
+ struct seh_context *next;
+
+ /* Initial code-segment. */
+ segT code_seg;
+ /* Function name. */
+ char *func_name;
+ /* BeginAddress. */
+ symbolS *start_addr;
+ /* EndAddress. */
+ symbolS *end_addr;
+ /* Unwind data. */
+ symbolS *xdata_addr;
+ /* PrologueEnd. */
+ symbolS *endprologue_addr;
+ /* ExceptionHandler. */
+ expressionS handler;
+ /* ExceptionHandlerData. (arm, mips) */
+ expressionS handler_data;
+
+ /* ARM .seh_eh directive seen. */
+ int handler_written;
+
+ /* WinCE specific data. */
+ int use_instruction_32;
+ /* Was record already processed. */
+ int done;
+
+ /* x64 flags for the xdata header. */
+ int handler_flags;
+ int subsection;
+
+ /* x64 framereg and frame offset information. */
+ int framereg;
+ int frameoff;
+
+ /* Information about x64 specific unwind data fields. */
+ int elems_count;
+ int elems_max;
+ seh_prologue_element *elems;
+} seh_context;
+
+typedef enum seh_kind {
+ seh_kind_unknown = 0,
+ seh_kind_mips = 1, /* Used for MIPS and x86 pdata generation. */
+ seh_kind_arm = 2, /* Used for ARM, PPC, SH3, and SH4 pdata (PDATA_EH) generation. */
+ seh_kind_x64 = 3 /* Used for IA64 and x64 pdata/xdata generation. */
+} seh_kind;
+
+/* Forward declarations. */
+static void obj_coff_seh_stackalloc (int);
+static void obj_coff_seh_setframe (int);
+static void obj_coff_seh_endprologue (int);
+static void obj_coff_seh_save (int);
+static void obj_coff_seh_pushreg (int);
+static void obj_coff_seh_pushframe (int);
+static void obj_coff_seh_endproc (int);
+static void obj_coff_seh_eh (int);
+static void obj_coff_seh_32 (int);
+static void obj_coff_seh_proc (int);
+static void obj_coff_seh_handler (int);
+static void obj_coff_seh_handlerdata (int);
+static void obj_coff_seh_code (int);
+
+#define UNDSEC bfd_und_section_ptr
+
+/* Check if x64 UNW_... macros are already defined. */
+#ifndef PEX64_FLAG_NHANDLER
+/* We can't include here coff/pe.h header. So we have to copy macros
+ from coff/pe.h here. */
+#define PEX64_UNWCODE_CODE(VAL) ((VAL) & 0xf)
+#define PEX64_UNWCODE_INFO(VAL) (((VAL) >> 4) & 0xf)
+
+/* The unwind info. */
+#define UNW_FLAG_NHANDLER 0
+#define UNW_FLAG_EHANDLER 1
+#define UNW_FLAG_UHANDLER 2
+#define UNW_FLAG_FHANDLER 3
+#define UNW_FLAG_CHAININFO 4
+
+#define UNW_FLAG_MASK 0x1f
+
+/* The unwind codes. */
+#define UWOP_PUSH_NONVOL 0
+#define UWOP_ALLOC_LARGE 1
+#define UWOP_ALLOC_SMALL 2
+#define UWOP_SET_FPREG 3
+#define UWOP_SAVE_NONVOL 4
+#define UWOP_SAVE_NONVOL_FAR 5
+#define UWOP_SAVE_XMM 6
+#define UWOP_SAVE_XMM_FAR 7
+#define UWOP_SAVE_XMM128 8
+#define UWOP_SAVE_XMM128_FAR 9
+#define UWOP_PUSH_MACHFRAME 10
+
+#define PEX64_UWI_VERSION(VAL) ((VAL) & 7)
+#define PEX64_UWI_FLAGS(VAL) (((VAL) >> 3) & 0x1f)
+#define PEX64_UWI_FRAMEREG(VAL) ((VAL) & 0xf)
+#define PEX64_UWI_FRAMEOFF(VAL) (((VAL) >> 4) & 0xf)
+#define PEX64_UWI_SIZEOF_UWCODE_ARRAY(VAL) \
+ ((((VAL) + 1) & ~1) * 2)
+
+#define PEX64_OFFSET_TO_UNWIND_CODE 0x4
+
+#define PEX64_OFFSET_TO_HANDLER_RVA (COUNTOFUNWINDCODES) \
+ (PEX64_OFFSET_TO_UNWIND_CODE + \
+ PEX64_UWI_SIZEOF_UWCODE_ARRAY(COUNTOFUNWINDCODES))
+
+#define PEX64_OFFSET_TO_SCOPE_COUNT(COUNTOFUNWINDCODES) \
+ (PEX64_OFFSET_TO_HANDLER_RVA(COUNTOFUNWINDCODES) + 4)
+
+#define PEX64_SCOPE_ENTRY(COUNTOFUNWINDCODES, IDX) \
+ (PEX64_OFFSET_TO_SCOPE_COUNT(COUNTOFUNWINDCODES) + \
+ PEX64_SCOPE_ENTRY_SIZE * (IDX))
+
+#endif
diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c
new file mode 100644
index 0000000..9f5a903
--- /dev/null
+++ b/gas/config/obj-coff.c
@@ -0,0 +1,1955 @@
+/* coff object file format
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define OBJ_HEADER "obj-coff.h"
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "struc-symbol.h"
+
+#ifdef TE_PE
+#include "coff/pe.h"
+#endif
+
+#ifdef OBJ_XCOFF
+#include "coff/xcoff.h"
+#endif
+
+#define streq(a,b) (strcmp ((a), (b)) == 0)
+#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0)
+
+/* I think this is probably always correct. */
+#ifndef KEEP_RELOC_INFO
+#define KEEP_RELOC_INFO
+#endif
+
+/* obj_coff_section will use this macro to set a new section's
+ attributes when a directive has no valid flags or the "w" flag is
+ used. This default should be appropriate for most. */
+#ifndef TC_COFF_SECTION_DEFAULT_ATTRIBUTES
+#define TC_COFF_SECTION_DEFAULT_ATTRIBUTES (SEC_LOAD | SEC_DATA)
+#endif
+
+/* This is used to hold the symbol built by a sequence of pseudo-ops
+ from .def and .endef. */
+static symbolS *def_symbol_in_progress;
+#ifdef TE_PE
+/* PE weak alternate symbols begin with this string. */
+static const char weak_altprefix[] = ".weak.";
+#endif /* TE_PE */
+
+#include "obj-coff-seh.c"
+
+typedef struct
+ {
+ unsigned long chunk_size;
+ unsigned long element_size;
+ unsigned long size;
+ char *data;
+ unsigned long pointer;
+ }
+stack;
+
+
+/* Stack stuff. */
+
+static stack *
+stack_init (unsigned long chunk_size,
+ unsigned long element_size)
+{
+ stack *st;
+
+ st = malloc (sizeof (* st));
+ if (!st)
+ return NULL;
+ st->data = malloc (chunk_size);
+ if (!st->data)
+ {
+ free (st);
+ return NULL;
+ }
+ st->pointer = 0;
+ st->size = chunk_size;
+ st->chunk_size = chunk_size;
+ st->element_size = element_size;
+ return st;
+}
+
+static char *
+stack_push (stack *st, char *element)
+{
+ if (st->pointer + st->element_size >= st->size)
+ {
+ st->size += st->chunk_size;
+ if ((st->data = xrealloc (st->data, st->size)) == NULL)
+ return NULL;
+ }
+ memcpy (st->data + st->pointer, element, st->element_size);
+ st->pointer += st->element_size;
+ return st->data + st->pointer;
+}
+
+static char *
+stack_pop (stack *st)
+{
+ if (st->pointer < st->element_size)
+ {
+ st->pointer = 0;
+ return NULL;
+ }
+ st->pointer -= st->element_size;
+ return st->data + st->pointer;
+}
+
+/* Maintain a list of the tagnames of the structures. */
+
+static struct hash_control *tag_hash;
+
+static void
+tag_init (void)
+{
+ tag_hash = hash_new ();
+}
+
+static void
+tag_insert (const char *name, symbolS *symbolP)
+{
+ const char *error_string;
+
+ if ((error_string = hash_jam (tag_hash, name, (char *) symbolP)))
+ as_fatal (_("Inserting \"%s\" into structure table failed: %s"),
+ name, error_string);
+}
+
+static symbolS *
+tag_find (char *name)
+{
+ return (symbolS *) hash_find (tag_hash, name);
+}
+
+static symbolS *
+tag_find_or_make (char *name)
+{
+ symbolS *symbolP;
+
+ if ((symbolP = tag_find (name)) == NULL)
+ {
+ symbolP = symbol_new (name, undefined_section,
+ 0, &zero_address_frag);
+
+ tag_insert (S_GET_NAME (symbolP), symbolP);
+ symbol_table_insert (symbolP);
+ }
+
+ return symbolP;
+}
+
+/* We accept the .bss directive to set the section for backward
+ compatibility with earlier versions of gas. */
+
+static void
+obj_coff_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ if (*input_line_pointer == '\n')
+ subseg_new (".bss", get_absolute_expression ());
+ else
+ s_lcomm (0);
+}
+
+#ifdef TE_PE
+/* Called from read.c:s_comm after we've parsed .comm symbol, size.
+ Parse a possible alignment value. */
+
+static symbolS *
+obj_coff_common_parse (int ignore ATTRIBUTE_UNUSED, symbolS *symbolP, addressT size)
+{
+ addressT align = 0;
+
+ if (*input_line_pointer == ',')
+ {
+ align = parse_align (0);
+ if (align == (addressT) -1)
+ return NULL;
+ }
+
+ S_SET_VALUE (symbolP, size);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+
+ /* There is no S_SET_ALIGN (symbolP, align) in COFF/PE.
+ Instead we must add a note to the .drectve section. */
+ if (align)
+ {
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ flagword oldflags;
+ asection *sec;
+ size_t pfxlen, numlen;
+ char *frag;
+ char numbuff[20];
+
+ sec = subseg_new (".drectve", 0);
+ oldflags = bfd_get_section_flags (stdoutput, sec);
+ if (oldflags == SEC_NO_FLAGS)
+ {
+ if (!bfd_set_section_flags (stdoutput, sec,
+ TC_COFF_SECTION_DEFAULT_ATTRIBUTES))
+ as_warn (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+ }
+
+ /* Emit a string. Note no NUL-termination. */
+ pfxlen = strlen (" -aligncomm:") + 2 + strlen (S_GET_NAME (symbolP)) + 1;
+ numlen = snprintf (numbuff, sizeof (numbuff), "%d", (int) align);
+ frag = frag_more (pfxlen + numlen);
+ (void) sprintf (frag, " -aligncomm:\"%s\",", S_GET_NAME (symbolP));
+ memcpy (frag + pfxlen, numbuff, numlen);
+ /* Restore original subseg. */
+ subseg_set (current_seg, current_subseg);
+ }
+
+ return symbolP;
+}
+
+static void
+obj_coff_comm (int ignore ATTRIBUTE_UNUSED)
+{
+ s_comm_internal (ignore, obj_coff_common_parse);
+}
+#endif /* TE_PE */
+
+#define GET_FILENAME_STRING(X) \
+ ((char *) (&((X)->sy_symbol.ost_auxent->x_file.x_n.x_offset))[1])
+
+/* @@ Ick. */
+static segT
+fetch_coff_debug_section (void)
+{
+ static segT debug_section;
+
+ if (!debug_section)
+ {
+ const asymbol *s;
+
+ s = bfd_make_debug_symbol (stdoutput, NULL, 0);
+ gas_assert (s != 0);
+ debug_section = s->section;
+ }
+ return debug_section;
+}
+
+void
+SA_SET_SYM_ENDNDX (symbolS *sym, symbolS *val)
+{
+ combined_entry_type *entry, *p;
+
+ entry = &coffsymbol (symbol_get_bfdsym (sym))->native[1];
+ p = coffsymbol (symbol_get_bfdsym (val))->native;
+ entry->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p = p;
+ entry->fix_end = 1;
+}
+
+static void
+SA_SET_SYM_TAGNDX (symbolS *sym, symbolS *val)
+{
+ combined_entry_type *entry, *p;
+
+ entry = &coffsymbol (symbol_get_bfdsym (sym))->native[1];
+ p = coffsymbol (symbol_get_bfdsym (val))->native;
+ entry->u.auxent.x_sym.x_tagndx.p = p;
+ entry->fix_tag = 1;
+}
+
+static int
+S_GET_DATA_TYPE (symbolS *sym)
+{
+ return coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_type;
+}
+
+int
+S_SET_DATA_TYPE (symbolS *sym, int val)
+{
+ coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_type = val;
+ return val;
+}
+
+int
+S_GET_STORAGE_CLASS (symbolS *sym)
+{
+ return coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_sclass;
+}
+
+int
+S_SET_STORAGE_CLASS (symbolS *sym, int val)
+{
+ coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_sclass = val;
+ return val;
+}
+
+/* Merge a debug symbol containing debug information into a normal symbol. */
+
+static void
+c_symbol_merge (symbolS *debug, symbolS *normal)
+{
+ S_SET_DATA_TYPE (normal, S_GET_DATA_TYPE (debug));
+ S_SET_STORAGE_CLASS (normal, S_GET_STORAGE_CLASS (debug));
+
+ if (S_GET_NUMBER_AUXILIARY (debug) > S_GET_NUMBER_AUXILIARY (normal))
+ /* Take the most we have. */
+ S_SET_NUMBER_AUXILIARY (normal, S_GET_NUMBER_AUXILIARY (debug));
+
+ if (S_GET_NUMBER_AUXILIARY (debug) > 0)
+ /* Move all the auxiliary information. */
+ memcpy (SYM_AUXINFO (normal), SYM_AUXINFO (debug),
+ (S_GET_NUMBER_AUXILIARY (debug)
+ * sizeof (*SYM_AUXINFO (debug))));
+
+ /* Move the debug flags. */
+ SF_SET_DEBUG_FIELD (normal, SF_GET_DEBUG_FIELD (debug));
+}
+
+void
+c_dot_file_symbol (const char *filename, int appfile ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP;
+
+ /* BFD converts filename to a .file symbol with an aux entry. It
+ also handles chaining. */
+ symbolP = symbol_new (filename, bfd_abs_section_ptr, 0, &zero_address_frag);
+
+ S_SET_STORAGE_CLASS (symbolP, C_FILE);
+ S_SET_NUMBER_AUXILIARY (symbolP, 1);
+
+ symbol_get_bfdsym (symbolP)->flags = BSF_DEBUGGING;
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing)
+ listing_source_file (filename);
+ }
+#endif
+
+ /* Make sure that the symbol is first on the symbol chain. */
+ if (symbol_rootP != symbolP)
+ {
+ symbol_remove (symbolP, &symbol_rootP, &symbol_lastP);
+ symbol_insert (symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP);
+ }
+}
+
+/* Line number handling. */
+
+struct line_no
+{
+ struct line_no *next;
+ fragS *frag;
+ alent l;
+};
+
+int coff_line_base;
+
+/* Symbol of last function, which we should hang line#s off of. */
+static symbolS *line_fsym;
+
+#define in_function() (line_fsym != 0)
+#define clear_function() (line_fsym = 0)
+#define set_function(F) (line_fsym = (F), coff_add_linesym (F))
+
+
+void
+coff_obj_symbol_new_hook (symbolS *symbolP)
+{
+ long sz = (OBJ_COFF_MAX_AUXENTRIES + 1) * sizeof (combined_entry_type);
+ char * s = xmalloc (sz);
+
+ memset (s, 0, sz);
+ coffsymbol (symbol_get_bfdsym (symbolP))->native = (combined_entry_type *) s;
+ coffsymbol (symbol_get_bfdsym (symbolP))->native->is_sym = TRUE;
+
+ S_SET_DATA_TYPE (symbolP, T_NULL);
+ S_SET_STORAGE_CLASS (symbolP, 0);
+ S_SET_NUMBER_AUXILIARY (symbolP, 0);
+
+ if (S_IS_STRING (symbolP))
+ SF_SET_STRING (symbolP);
+
+ if (S_IS_LOCAL (symbolP))
+ SF_SET_LOCAL (symbolP);
+}
+
+void
+coff_obj_symbol_clone_hook (symbolS *newsymP, symbolS *orgsymP)
+{
+ long sz = (OBJ_COFF_MAX_AUXENTRIES + 1) * sizeof (combined_entry_type);
+ combined_entry_type * s = xmalloc (sz);
+
+ memcpy (s, coffsymbol (symbol_get_bfdsym (orgsymP))->native, sz);
+ coffsymbol (symbol_get_bfdsym (newsymP))->native = s;
+
+ SF_SET (newsymP, SF_GET (orgsymP));
+}
+
+
+/* Handle .ln directives. */
+
+static symbolS *current_lineno_sym;
+static struct line_no *line_nos;
+/* FIXME: Blindly assume all .ln directives will be in the .text section. */
+int coff_n_line_nos;
+
+static void
+add_lineno (fragS * frag, addressT offset, int num)
+{
+ struct line_no * new_line = xmalloc (sizeof (* new_line));
+
+ if (!current_lineno_sym)
+ abort ();
+
+#ifndef OBJ_XCOFF
+ /* The native aix assembler accepts negative line number. */
+
+ if (num <= 0)
+ {
+ /* Zero is used as an end marker in the file. */
+ as_warn (_("Line numbers must be positive integers\n"));
+ num = 1;
+ }
+#endif /* OBJ_XCOFF */
+ new_line->next = line_nos;
+ new_line->frag = frag;
+ new_line->l.line_number = num;
+ new_line->l.u.offset = offset;
+ line_nos = new_line;
+ coff_n_line_nos++;
+}
+
+void
+coff_add_linesym (symbolS *sym)
+{
+ if (line_nos)
+ {
+ coffsymbol (symbol_get_bfdsym (current_lineno_sym))->lineno =
+ (alent *) line_nos;
+ coff_n_line_nos++;
+ line_nos = 0;
+ }
+ current_lineno_sym = sym;
+}
+
+static void
+obj_coff_ln (int appline)
+{
+ int l;
+
+ if (! appline && def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".ln pseudo-op inside .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ l = get_absolute_expression ();
+
+ /* If there is no lineno symbol, treat a .ln
+ directive as if it were a .appline directive. */
+ if (appline || current_lineno_sym == NULL)
+ new_logical_line ((char *) NULL, l - 1);
+ else
+ add_lineno (frag_now, frag_now_fix (), l);
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing)
+ {
+ if (! appline)
+ l += coff_line_base - 1;
+ listing_source_line (l);
+ }
+ }
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/* .loc is essentially the same as .ln; parse it for assembler
+ compatibility. */
+
+static void
+obj_coff_loc (int ignore ATTRIBUTE_UNUSED)
+{
+ int lineno;
+
+ /* FIXME: Why do we need this check? We need it for ECOFF, but why
+ do we need it for COFF? */
+ if (now_seg != text_section)
+ {
+ as_warn (_(".loc outside of .text"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".loc pseudo-op inside .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ /* Skip the file number. */
+ SKIP_WHITESPACE ();
+ get_absolute_expression ();
+ SKIP_WHITESPACE ();
+
+ lineno = get_absolute_expression ();
+
+#ifndef NO_LISTING
+ {
+ extern int listing;
+
+ if (listing)
+ {
+ lineno += coff_line_base - 1;
+ listing_source_line (lineno);
+ }
+ }
+#endif
+
+ demand_empty_rest_of_line ();
+
+ add_lineno (frag_now, frag_now_fix (), lineno);
+}
+
+/* Handle the .ident pseudo-op. */
+
+static void
+obj_coff_ident (int ignore ATTRIBUTE_UNUSED)
+{
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+#ifdef TE_PE
+ {
+ segT sec;
+
+ /* We could put it in .comment, but that creates an extra section
+ that shouldn't be loaded into memory, which requires linker
+ changes... For now, until proven otherwise, use .rdata. */
+ sec = subseg_new (".rdata$zzz", 0);
+ bfd_set_section_flags (stdoutput, sec,
+ ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA)
+ & bfd_applicable_section_flags (stdoutput)));
+ }
+#else
+ subseg_new (".comment", 0);
+#endif
+
+ stringer (8 + 1);
+ subseg_set (current_seg, current_subseg);
+}
+
+/* Handle .def directives.
+
+ One might ask : why can't we symbol_new if the symbol does not
+ already exist and fill it with debug information. Because of
+ the C_EFCN special symbol. It would clobber the value of the
+ function symbol before we have a chance to notice that it is
+ a C_EFCN. And a second reason is that the code is more clear this
+ way. (at least I think it is :-). */
+
+#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';')
+#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \
+ *input_line_pointer == '\t') \
+ input_line_pointer++;
+
+static void
+obj_coff_def (int what ATTRIBUTE_UNUSED)
+{
+ char name_end; /* Char after the end of name. */
+ char *symbol_name; /* Name of the debug symbol. */
+ char *symbol_name_copy; /* Temporary copy of the name. */
+ unsigned int symbol_name_length;
+
+ if (def_symbol_in_progress != NULL)
+ {
+ as_warn (_(".def pseudo-op used inside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACES ();
+
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ symbol_name_length = strlen (symbol_name);
+ symbol_name_copy = xmalloc (symbol_name_length + 1);
+ strcpy (symbol_name_copy, symbol_name);
+#ifdef tc_canonicalize_symbol_name
+ symbol_name_copy = tc_canonicalize_symbol_name (symbol_name_copy);
+#endif
+
+ /* Initialize the new symbol. */
+ def_symbol_in_progress = symbol_make (symbol_name_copy);
+ symbol_set_frag (def_symbol_in_progress, &zero_address_frag);
+ S_SET_VALUE (def_symbol_in_progress, 0);
+
+ if (S_IS_STRING (def_symbol_in_progress))
+ SF_SET_STRING (def_symbol_in_progress);
+
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_endef (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP = NULL;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".endef pseudo-op used outside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ /* Set the section number according to storage class. */
+ switch (S_GET_STORAGE_CLASS (def_symbol_in_progress))
+ {
+ case C_STRTAG:
+ case C_ENTAG:
+ case C_UNTAG:
+ SF_SET_TAG (def_symbol_in_progress);
+ /* Fall through. */
+ case C_FILE:
+ case C_TPDEF:
+ SF_SET_DEBUG (def_symbol_in_progress);
+ S_SET_SEGMENT (def_symbol_in_progress, fetch_coff_debug_section ());
+ break;
+
+ case C_EFCN:
+ SF_SET_LOCAL (def_symbol_in_progress); /* Do not emit this symbol. */
+ /* Fall through. */
+ case C_BLOCK:
+ SF_SET_PROCESS (def_symbol_in_progress); /* Will need processing before writing. */
+ /* Fall through. */
+ case C_FCN:
+ {
+ const char *name;
+
+ S_SET_SEGMENT (def_symbol_in_progress, text_section);
+
+ name = S_GET_NAME (def_symbol_in_progress);
+ if (name[0] == '.' && name[2] == 'f' && name[3] == '\0')
+ {
+ switch (name[1])
+ {
+ case 'b':
+ /* .bf */
+ if (! in_function ())
+ as_warn (_("`%s' symbol without preceding function"), name);
+ /* Will need relocating. */
+ SF_SET_PROCESS (def_symbol_in_progress);
+ clear_function ();
+ break;
+#ifdef TE_PE
+ case 'e':
+ /* .ef */
+ /* The MS compilers output the actual endline, not the
+ function-relative one... we want to match without
+ changing the assembler input. */
+ SA_SET_SYM_LNNO (def_symbol_in_progress,
+ (SA_GET_SYM_LNNO (def_symbol_in_progress)
+ + coff_line_base));
+ break;
+#endif
+ }
+ }
+ }
+ break;
+
+#ifdef C_AUTOARG
+ case C_AUTOARG:
+#endif /* C_AUTOARG */
+ case C_AUTO:
+ case C_REG:
+ case C_ARG:
+ case C_REGPARM:
+ case C_FIELD:
+
+ /* According to the COFF documentation:
+
+ http://osr5doc.sco.com:1996/topics/COFF_SectNumFld.html
+
+ A special section number (-2) marks symbolic debugging symbols,
+ including structure/union/enumeration tag names, typedefs, and
+ the name of the file. A section number of -1 indicates that the
+ symbol has a value but is not relocatable. Examples of
+ absolute-valued symbols include automatic and register variables,
+ function arguments, and .eos symbols.
+
+ But from Ian Lance Taylor:
+
+ http://sources.redhat.com/ml/binutils/2000-08/msg00202.html
+
+ the actual tools all marked them as section -1. So the GNU COFF
+ assembler follows historical COFF assemblers.
+
+ However, it causes problems for djgpp
+
+ http://sources.redhat.com/ml/binutils/2000-08/msg00210.html
+
+ By defining STRICTCOFF, a COFF port can make the assembler to
+ follow the documented behavior. */
+#ifdef STRICTCOFF
+ case C_MOS:
+ case C_MOE:
+ case C_MOU:
+ case C_EOS:
+#endif
+ SF_SET_DEBUG (def_symbol_in_progress);
+ S_SET_SEGMENT (def_symbol_in_progress, absolute_section);
+ break;
+
+#ifndef STRICTCOFF
+ case C_MOS:
+ case C_MOE:
+ case C_MOU:
+ case C_EOS:
+ S_SET_SEGMENT (def_symbol_in_progress, absolute_section);
+ break;
+#endif
+
+ case C_EXT:
+ case C_WEAKEXT:
+#ifdef TE_PE
+ case C_NT_WEAK:
+#endif
+ case C_STAT:
+ case C_LABEL:
+ /* Valid but set somewhere else (s_comm, s_lcomm, colon). */
+ break;
+
+ default:
+ case C_USTATIC:
+ case C_EXTDEF:
+ case C_ULABEL:
+ as_warn (_("unexpected storage class %d"),
+ S_GET_STORAGE_CLASS (def_symbol_in_progress));
+ break;
+ }
+
+ /* Now that we have built a debug symbol, try to find if we should
+ merge with an existing symbol or not. If a symbol is C_EFCN or
+ absolute_section or untagged SEG_DEBUG it never merges. We also
+ don't merge labels, which are in a different namespace, nor
+ symbols which have not yet been defined since they are typically
+ unique, nor do we merge tags with non-tags. */
+
+ /* Two cases for functions. Either debug followed by definition or
+ definition followed by debug. For definition first, we will
+ merge the debug symbol into the definition. For debug first, the
+ lineno entry MUST point to the definition function or else it
+ will point off into space when obj_crawl_symbol_chain() merges
+ the debug symbol into the real symbol. Therefor, let's presume
+ the debug symbol is a real function reference. */
+
+ /* FIXME-SOON If for some reason the definition label/symbol is
+ never seen, this will probably leave an undefined symbol at link
+ time. */
+
+ if (S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_EFCN
+ || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_LABEL
+ || (streq (bfd_get_section_name (stdoutput,
+ S_GET_SEGMENT (def_symbol_in_progress)),
+ "*DEBUG*")
+ && !SF_GET_TAG (def_symbol_in_progress))
+ || S_GET_SEGMENT (def_symbol_in_progress) == absolute_section
+ || ! symbol_constant_p (def_symbol_in_progress)
+ || (symbolP = symbol_find (S_GET_NAME (def_symbol_in_progress))) == NULL
+ || SF_GET_TAG (def_symbol_in_progress) != SF_GET_TAG (symbolP))
+ {
+ /* If it already is at the end of the symbol list, do nothing */
+ if (def_symbol_in_progress != symbol_lastP)
+ {
+ symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+ symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP,
+ &symbol_lastP);
+ }
+ }
+ else
+ {
+ /* This symbol already exists, merge the newly created symbol
+ into the old one. This is not mandatory. The linker can
+ handle duplicate symbols correctly. But I guess that it save
+ a *lot* of space if the assembly file defines a lot of
+ symbols. [loic] */
+
+ /* The debug entry (def_symbol_in_progress) is merged into the
+ previous definition. */
+
+ c_symbol_merge (def_symbol_in_progress, symbolP);
+ symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+
+ def_symbol_in_progress = symbolP;
+
+ if (SF_GET_FUNCTION (def_symbol_in_progress)
+ || SF_GET_TAG (def_symbol_in_progress)
+ || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_STAT)
+ {
+ /* For functions, and tags, and static symbols, the symbol
+ *must* be where the debug symbol appears. Move the
+ existing symbol to the current place. */
+ /* If it already is at the end of the symbol list, do nothing. */
+ if (def_symbol_in_progress != symbol_lastP)
+ {
+ symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP);
+ symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP);
+ }
+ }
+ }
+
+ if (SF_GET_TAG (def_symbol_in_progress))
+ {
+ symbolS *oldtag;
+
+ oldtag = symbol_find (S_GET_NAME (def_symbol_in_progress));
+ if (oldtag == NULL || ! SF_GET_TAG (oldtag))
+ tag_insert (S_GET_NAME (def_symbol_in_progress),
+ def_symbol_in_progress);
+ }
+
+ if (SF_GET_FUNCTION (def_symbol_in_progress))
+ {
+ set_function (def_symbol_in_progress);
+ SF_SET_PROCESS (def_symbol_in_progress);
+
+ if (symbolP == NULL)
+ /* That is, if this is the first time we've seen the
+ function. */
+ symbol_table_insert (def_symbol_in_progress);
+
+ }
+
+ def_symbol_in_progress = NULL;
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_dim (int ignore ATTRIBUTE_UNUSED)
+{
+ int d_index;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".dim pseudo-op used outside of .def/.endef: ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+
+ for (d_index = 0; d_index < DIMNUM; d_index++)
+ {
+ SKIP_WHITESPACES ();
+ SA_SET_SYM_DIMEN (def_symbol_in_progress, d_index,
+ get_absolute_expression ());
+
+ switch (*input_line_pointer)
+ {
+ case ',':
+ input_line_pointer++;
+ break;
+
+ default:
+ as_warn (_("badly formed .dim directive ignored"));
+ /* Fall through. */
+ case '\n':
+ case ';':
+ d_index = DIMNUM;
+ break;
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_line (int ignore ATTRIBUTE_UNUSED)
+{
+ int this_base;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ /* Probably stabs-style line? */
+ obj_coff_ln (0);
+ return;
+ }
+
+ this_base = get_absolute_expression ();
+ if (streq (".bf", S_GET_NAME (def_symbol_in_progress)))
+ coff_line_base = this_base;
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ SA_SET_SYM_LNNO (def_symbol_in_progress, this_base);
+
+ demand_empty_rest_of_line ();
+
+#ifndef NO_LISTING
+ if (streq (".bf", S_GET_NAME (def_symbol_in_progress)))
+ {
+ extern int listing;
+
+ if (listing)
+ listing_source_line ((unsigned int) this_base);
+ }
+#endif
+}
+
+static void
+obj_coff_size (int ignore ATTRIBUTE_UNUSED)
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".size pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ SA_SET_SYM_SIZE (def_symbol_in_progress, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_scl (int ignore ATTRIBUTE_UNUSED)
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".scl pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_STORAGE_CLASS (def_symbol_in_progress, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_tag (int ignore ATTRIBUTE_UNUSED)
+{
+ char *symbol_name;
+ char name_end;
+
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".tag pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1);
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+#ifdef tc_canonicalize_symbol_name
+ symbol_name = tc_canonicalize_symbol_name (symbol_name);
+#endif
+
+ /* Assume that the symbol referred to by .tag is always defined.
+ This was a bad assumption. I've added find_or_make. xoxorich. */
+ SA_SET_SYM_TAGNDX (def_symbol_in_progress,
+ tag_find_or_make (symbol_name));
+ if (SA_GET_SYM_TAGNDX (def_symbol_in_progress) == 0L)
+ as_warn (_("tag not found for .tag %s"), symbol_name);
+
+ SF_SET_TAGGED (def_symbol_in_progress);
+ *input_line_pointer = name_end;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_type (int ignore ATTRIBUTE_UNUSED)
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".type pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ S_SET_DATA_TYPE (def_symbol_in_progress, get_absolute_expression ());
+
+ if (ISFCN (S_GET_DATA_TYPE (def_symbol_in_progress)) &&
+ S_GET_STORAGE_CLASS (def_symbol_in_progress) != C_TPDEF)
+ SF_SET_FUNCTION (def_symbol_in_progress);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_val (int ignore ATTRIBUTE_UNUSED)
+{
+ if (def_symbol_in_progress == NULL)
+ {
+ as_warn (_(".val pseudo-op used outside of .def/.endef ignored."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (is_name_beginner (*input_line_pointer))
+ {
+ char *symbol_name = input_line_pointer;
+ char name_end = get_symbol_end ();
+
+#ifdef tc_canonicalize_symbol_name
+ symbol_name = tc_canonicalize_symbol_name (symbol_name);
+#endif
+ if (streq (symbol_name, "."))
+ {
+ /* If the .val is != from the .def (e.g. statics). */
+ symbol_set_frag (def_symbol_in_progress, frag_now);
+ S_SET_VALUE (def_symbol_in_progress, (valueT) frag_now_fix ());
+ }
+ else if (! streq (S_GET_NAME (def_symbol_in_progress), symbol_name))
+ {
+ expressionS exp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = symbol_find_or_make (symbol_name);
+ exp.X_op_symbol = NULL;
+ exp.X_add_number = 0;
+ symbol_set_value_expression (def_symbol_in_progress, &exp);
+
+ /* If the segment is undefined when the forward reference is
+ resolved, then copy the segment id from the forward
+ symbol. */
+ SF_SET_GET_SEGMENT (def_symbol_in_progress);
+
+ /* FIXME: gcc can generate address expressions here in
+ unusual cases (search for "obscure" in sdbout.c). We
+ just ignore the offset here, thus generating incorrect
+ debugging information. We ignore the rest of the line
+ just below. */
+ }
+ /* Otherwise, it is the name of a non debug symbol and its value
+ will be calculated later. */
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ S_SET_VALUE (def_symbol_in_progress, get_absolute_expression ());
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+#ifdef TE_PE
+
+/* Return nonzero if name begins with weak alternate symbol prefix. */
+
+static int
+weak_is_altname (const char * name)
+{
+ return strneq (name, weak_altprefix, sizeof (weak_altprefix) - 1);
+}
+
+/* Return the name of the alternate symbol
+ name corresponding to a weak symbol's name. */
+
+static const char *
+weak_name2altname (const char * name)
+{
+ char *alt_name;
+
+ alt_name = xmalloc (sizeof (weak_altprefix) + strlen (name));
+ strcpy (alt_name, weak_altprefix);
+ return strcat (alt_name, name);
+}
+
+/* Return the name of the weak symbol corresponding to an
+ alternate symbol. */
+
+static const char *
+weak_altname2name (const char * name)
+{
+ gas_assert (weak_is_altname (name));
+ return xstrdup (name + 6);
+}
+
+/* Make a weak symbol name unique by
+ appending the name of an external symbol. */
+
+static const char *
+weak_uniquify (const char * name)
+{
+ char *ret;
+ const char * unique = "";
+
+#ifdef TE_PE
+ if (an_external_name != NULL)
+ unique = an_external_name;
+#endif
+ gas_assert (weak_is_altname (name));
+
+ ret = xmalloc (strlen (name) + strlen (unique) + 2);
+ strcpy (ret, name);
+ strcat (ret, ".");
+ strcat (ret, unique);
+ return ret;
+}
+
+void
+pecoff_obj_set_weak_hook (symbolS *symbolP)
+{
+ symbolS *alternateP;
+
+ /* See _Microsoft Portable Executable and Common Object
+ File Format Specification_, section 5.5.3.
+ Create a symbol representing the alternate value.
+ coff_frob_symbol will set the value of this symbol from
+ the value of the weak symbol itself. */
+ S_SET_STORAGE_CLASS (symbolP, C_NT_WEAK);
+ S_SET_NUMBER_AUXILIARY (symbolP, 1);
+ SA_SET_SYM_FSIZE (symbolP, IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY);
+
+ alternateP = symbol_find_or_make (weak_name2altname (S_GET_NAME (symbolP)));
+ S_SET_EXTERNAL (alternateP);
+ S_SET_STORAGE_CLASS (alternateP, C_NT_WEAK);
+
+ SA_SET_SYM_TAGNDX (symbolP, alternateP);
+}
+
+void
+pecoff_obj_clear_weak_hook (symbolS *symbolP)
+{
+ symbolS *alternateP;
+
+ S_SET_STORAGE_CLASS (symbolP, 0);
+ SA_SET_SYM_FSIZE (symbolP, 0);
+
+ alternateP = symbol_find (weak_name2altname (S_GET_NAME (symbolP)));
+ S_CLEAR_EXTERNAL (alternateP);
+}
+
+#endif /* TE_PE */
+
+/* Handle .weak. This is a GNU extension in formats other than PE. */
+
+static void
+obj_coff_weak (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if (*name == 0)
+ {
+ as_warn (_("badly formed .weak directive ignored"));
+ ignore_rest_of_line ();
+ return;
+ }
+ c = 0;
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+void
+coff_obj_read_begin_hook (void)
+{
+ /* These had better be the same. Usually 18 bytes. */
+ know (sizeof (SYMENT) == sizeof (AUXENT));
+ know (SYMESZ == AUXESZ);
+ tag_init ();
+}
+
+symbolS *coff_last_function;
+#ifndef OBJ_XCOFF
+static symbolS *coff_last_bf;
+#endif
+
+void
+coff_frob_symbol (symbolS *symp, int *punt)
+{
+ static symbolS *last_tagP;
+ static stack *block_stack;
+ static symbolS *set_end;
+ symbolS *next_set_end = NULL;
+
+ if (symp == &abs_symbol)
+ {
+ *punt = 1;
+ return;
+ }
+
+ if (current_lineno_sym)
+ coff_add_linesym (NULL);
+
+ if (!block_stack)
+ block_stack = stack_init (512, sizeof (symbolS*));
+
+#ifdef TE_PE
+ if (S_GET_STORAGE_CLASS (symp) == C_NT_WEAK
+ && ! S_IS_WEAK (symp)
+ && weak_is_altname (S_GET_NAME (symp)))
+ {
+ /* This is a weak alternate symbol. All processing of
+ PECOFFweak symbols is done here, through the alternate. */
+ symbolS *weakp = symbol_find_noref (weak_altname2name
+ (S_GET_NAME (symp)), 1);
+
+ gas_assert (weakp);
+ gas_assert (S_GET_NUMBER_AUXILIARY (weakp) == 1);
+
+ if (! S_IS_WEAK (weakp))
+ {
+ /* The symbol was turned from weak to strong. Discard altname. */
+ *punt = 1;
+ return;
+ }
+ else if (symbol_equated_p (weakp))
+ {
+ /* The weak symbol has an alternate specified; symp is unneeded. */
+ S_SET_STORAGE_CLASS (weakp, C_NT_WEAK);
+ SA_SET_SYM_TAGNDX (weakp,
+ symbol_get_value_expression (weakp)->X_add_symbol);
+
+ S_CLEAR_EXTERNAL (symp);
+ *punt = 1;
+ return;
+ }
+ else
+ {
+ /* The weak symbol has been assigned an alternate value.
+ Copy this value to symp, and set symp as weakp's alternate. */
+ if (S_GET_STORAGE_CLASS (weakp) != C_NT_WEAK)
+ {
+ S_SET_STORAGE_CLASS (symp, S_GET_STORAGE_CLASS (weakp));
+ S_SET_STORAGE_CLASS (weakp, C_NT_WEAK);
+ }
+
+ if (S_IS_DEFINED (weakp))
+ {
+ /* This is a defined weak symbol. Copy value information
+ from the weak symbol itself to the alternate symbol. */
+ symbol_set_value_expression (symp,
+ symbol_get_value_expression (weakp));
+ symbol_set_frag (symp, symbol_get_frag (weakp));
+ S_SET_SEGMENT (symp, S_GET_SEGMENT (weakp));
+ }
+ else
+ {
+ /* This is an undefined weak symbol.
+ Define the alternate symbol to zero. */
+ S_SET_VALUE (symp, 0);
+ S_SET_SEGMENT (symp, absolute_section);
+ }
+
+ S_SET_NAME (symp, weak_uniquify (S_GET_NAME (symp)));
+ S_SET_STORAGE_CLASS (symp, C_EXT);
+
+ S_SET_VALUE (weakp, 0);
+ S_SET_SEGMENT (weakp, undefined_section);
+ }
+ }
+#else /* TE_PE */
+ if (S_IS_WEAK (symp))
+ S_SET_STORAGE_CLASS (symp, C_WEAKEXT);
+#endif /* TE_PE */
+
+ if (!S_IS_DEFINED (symp)
+ && !S_IS_WEAK (symp)
+ && S_GET_STORAGE_CLASS (symp) != C_STAT)
+ S_SET_STORAGE_CLASS (symp, C_EXT);
+
+ if (!SF_GET_DEBUG (symp))
+ {
+ symbolS * real;
+
+ if (!SF_GET_LOCAL (symp)
+ && !SF_GET_STATICS (symp)
+ && S_GET_STORAGE_CLASS (symp) != C_LABEL
+ && symbol_constant_p (symp)
+ && (real = symbol_find_noref (S_GET_NAME (symp), 1))
+ && S_GET_STORAGE_CLASS (real) == C_NULL
+ && real != symp)
+ {
+ c_symbol_merge (symp, real);
+ *punt = 1;
+ return;
+ }
+
+ if (!S_IS_DEFINED (symp) && !SF_GET_LOCAL (symp))
+ {
+ gas_assert (S_GET_VALUE (symp) == 0);
+ if (S_IS_WEAKREFD (symp))
+ *punt = 1;
+ else
+ S_SET_EXTERNAL (symp);
+ }
+ else if (S_GET_STORAGE_CLASS (symp) == C_NULL)
+ {
+ if (S_GET_SEGMENT (symp) == text_section
+ && symp != seg_info (text_section)->sym)
+ S_SET_STORAGE_CLASS (symp, C_LABEL);
+ else
+ S_SET_STORAGE_CLASS (symp, C_STAT);
+ }
+
+ if (SF_GET_PROCESS (symp))
+ {
+ if (S_GET_STORAGE_CLASS (symp) == C_BLOCK)
+ {
+ if (streq (S_GET_NAME (symp), ".bb"))
+ stack_push (block_stack, (char *) &symp);
+ else
+ {
+ symbolS *begin;
+
+ begin = *(symbolS **) stack_pop (block_stack);
+ if (begin == 0)
+ as_warn (_("mismatched .eb"));
+ else
+ next_set_end = begin;
+ }
+ }
+
+ if (coff_last_function == 0 && SF_GET_FUNCTION (symp)
+ && S_IS_DEFINED (symp))
+ {
+ union internal_auxent *auxp;
+
+ coff_last_function = symp;
+ if (S_GET_NUMBER_AUXILIARY (symp) < 1)
+ S_SET_NUMBER_AUXILIARY (symp, 1);
+ auxp = SYM_AUXENT (symp);
+ memset (auxp->x_sym.x_fcnary.x_ary.x_dimen, 0,
+ sizeof (auxp->x_sym.x_fcnary.x_ary.x_dimen));
+ }
+
+ if (S_GET_STORAGE_CLASS (symp) == C_EFCN
+ && S_IS_DEFINED (symp))
+ {
+ if (coff_last_function == 0)
+ as_fatal (_("C_EFCN symbol for %s out of scope"),
+ S_GET_NAME (symp));
+ SA_SET_SYM_FSIZE (coff_last_function,
+ (long) (S_GET_VALUE (symp)
+ - S_GET_VALUE (coff_last_function)));
+ next_set_end = coff_last_function;
+ coff_last_function = 0;
+ }
+ }
+
+ if (S_IS_EXTERNAL (symp))
+ S_SET_STORAGE_CLASS (symp, C_EXT);
+ else if (SF_GET_LOCAL (symp))
+ *punt = 1;
+
+ if (SF_GET_FUNCTION (symp))
+ symbol_get_bfdsym (symp)->flags |= BSF_FUNCTION;
+ }
+
+ /* Double check weak symbols. */
+ if (S_IS_WEAK (symp) && S_IS_COMMON (symp))
+ as_bad (_("Symbol `%s' can not be both weak and common"),
+ S_GET_NAME (symp));
+
+ if (SF_GET_TAG (symp))
+ last_tagP = symp;
+ else if (S_GET_STORAGE_CLASS (symp) == C_EOS)
+ next_set_end = last_tagP;
+
+#ifdef OBJ_XCOFF
+ /* This is pretty horrible, but we have to set *punt correctly in
+ order to call SA_SET_SYM_ENDNDX correctly. */
+ if (! symbol_used_in_reloc_p (symp)
+ && ((symbol_get_bfdsym (symp)->flags & BSF_SECTION_SYM) != 0
+ || (! (S_IS_EXTERNAL (symp) || S_IS_WEAK (symp))
+ && ! symbol_get_tc (symp)->output
+ && S_GET_STORAGE_CLASS (symp) != C_FILE)))
+ *punt = 1;
+#endif
+
+ if (set_end != (symbolS *) NULL
+ && ! *punt
+ && ((symbol_get_bfdsym (symp)->flags & BSF_NOT_AT_END) != 0
+ || (S_IS_DEFINED (symp)
+ && ! S_IS_COMMON (symp)
+ && (! S_IS_EXTERNAL (symp) || SF_GET_FUNCTION (symp)))))
+ {
+ SA_SET_SYM_ENDNDX (set_end, symp);
+ set_end = NULL;
+ }
+
+ if (next_set_end != NULL)
+ {
+ if (set_end != NULL)
+ as_warn (_("Warning: internal error: forgetting to set endndx of %s"),
+ S_GET_NAME (set_end));
+ set_end = next_set_end;
+ }
+
+#ifndef OBJ_XCOFF
+ if (! *punt
+ && S_GET_STORAGE_CLASS (symp) == C_FCN
+ && streq (S_GET_NAME (symp), ".bf"))
+ {
+ if (coff_last_bf != NULL)
+ SA_SET_SYM_ENDNDX (coff_last_bf, symp);
+ coff_last_bf = symp;
+ }
+#endif
+ if (coffsymbol (symbol_get_bfdsym (symp))->lineno)
+ {
+ int i;
+ struct line_no *lptr;
+ alent *l;
+
+ lptr = (struct line_no *) coffsymbol (symbol_get_bfdsym (symp))->lineno;
+ for (i = 0; lptr; lptr = lptr->next)
+ i++;
+ lptr = (struct line_no *) coffsymbol (symbol_get_bfdsym (symp))->lineno;
+
+ /* We need i entries for line numbers, plus 1 for the first
+ entry which BFD will override, plus 1 for the last zero
+ entry (a marker for BFD). */
+ l = xmalloc ((i + 2) * sizeof (* l));
+ coffsymbol (symbol_get_bfdsym (symp))->lineno = l;
+ l[i + 1].line_number = 0;
+ l[i + 1].u.sym = NULL;
+ for (; i > 0; i--)
+ {
+ if (lptr->frag)
+ lptr->l.u.offset += lptr->frag->fr_address / OCTETS_PER_BYTE;
+ l[i] = lptr->l;
+ lptr = lptr->next;
+ }
+ }
+}
+
+void
+coff_adjust_section_syms (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void * x ATTRIBUTE_UNUSED)
+{
+ symbolS *secsym;
+ segment_info_type *seginfo = seg_info (sec);
+ int nlnno, nrelocs = 0;
+
+ /* RS/6000 gas creates a .debug section manually in ppc_frob_file in
+ tc-ppc.c. Do not get confused by it. */
+ if (seginfo == NULL)
+ return;
+
+ if (streq (sec->name, ".text"))
+ nlnno = coff_n_line_nos;
+ else
+ nlnno = 0;
+ {
+ /* @@ Hope that none of the fixups expand to more than one reloc
+ entry... */
+ fixS *fixp = seginfo->fix_root;
+ while (fixp)
+ {
+ if (! fixp->fx_done)
+ nrelocs++;
+ fixp = fixp->fx_next;
+ }
+ }
+ if (bfd_get_section_size (sec) == 0
+ && nrelocs == 0
+ && nlnno == 0
+ && sec != text_section
+ && sec != data_section
+ && sec != bss_section)
+ return;
+
+ secsym = section_symbol (sec);
+ /* This is an estimate; we'll plug in the real value using
+ SET_SECTION_RELOCS later */
+ SA_SET_SCN_NRELOC (secsym, nrelocs);
+ SA_SET_SCN_NLINNO (secsym, nlnno);
+}
+
+void
+coff_frob_file_after_relocs (void)
+{
+ bfd_map_over_sections (stdoutput, coff_adjust_section_syms, NULL);
+}
+
+/* Implement the .section pseudo op:
+ .section name {, "flags"}
+ ^ ^
+ | +--- optional flags: 'b' for bss
+ | 'i' for info
+ +-- section name 'l' for lib
+ 'n' for noload
+ 'o' for over
+ 'w' for data
+ 'd' (apparently m88k for data)
+ 'e' for exclude
+ 'x' for text
+ 'r' for read-only data
+ 's' for shared data (PE)
+ 'y' for noread
+ '0' - '9' for power-of-two alignment (GNU extension).
+ But if the argument is not a quoted string, treat it as a
+ subsegment number.
+
+ Note the 'a' flag is silently ignored. This allows the same
+ .section directive to be parsed in both ELF and COFF formats. */
+
+void
+obj_coff_section (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Strip out the section name. */
+ char *section_name;
+ char c;
+ int alignment = -1;
+ char *name;
+ unsigned int exp;
+ flagword flags, oldflags;
+ asection *sec;
+
+ if (flag_mri)
+ {
+ char type;
+
+ s_mri_sect (&type);
+ return;
+ }
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ exp = 0;
+ flags = SEC_NO_FLAGS;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ exp = get_absolute_expression ();
+ else
+ {
+ unsigned char attr;
+ int readonly_removed = 0;
+ int load_removed = 0;
+
+ while (attr = *++input_line_pointer,
+ attr != '"'
+ && ! is_end_of_line[attr])
+ {
+ if (ISDIGIT (attr))
+ {
+ alignment = attr - '0';
+ continue;
+ }
+ switch (attr)
+ {
+ case 'e':
+ /* Exclude section from linking. */
+ flags |= SEC_EXCLUDE;
+ break;
+
+ case 'b':
+ /* Uninitialised data section. */
+ flags |= SEC_ALLOC;
+ flags &=~ SEC_LOAD;
+ break;
+
+ case 'n':
+ /* Section not loaded. */
+ flags &=~ SEC_LOAD;
+ flags |= SEC_NEVER_LOAD;
+ load_removed = 1;
+ break;
+
+ case 's':
+ /* Shared section. */
+ flags |= SEC_COFF_SHARED;
+ /* Fall through. */
+ case 'd':
+ /* Data section. */
+ flags |= SEC_DATA;
+ if (! load_removed)
+ flags |= SEC_LOAD;
+ flags &=~ SEC_READONLY;
+ break;
+
+ case 'w':
+ /* Writable section. */
+ flags &=~ SEC_READONLY;
+ readonly_removed = 1;
+ break;
+
+ case 'a':
+ /* Ignore. Here for compatibility with ELF. */
+ break;
+
+ case 'r': /* Read-only section. Implies a data section. */
+ readonly_removed = 0;
+ /* Fall through. */
+ case 'x': /* Executable section. */
+ /* If we are setting the 'x' attribute or if the 'r'
+ attribute is being used to restore the readonly status
+ of a code section (eg "wxr") then set the SEC_CODE flag,
+ otherwise set the SEC_DATA flag. */
+ flags |= (attr == 'x' || (flags & SEC_CODE) ? SEC_CODE : SEC_DATA);
+ if (! load_removed)
+ flags |= SEC_LOAD;
+ /* Note - the READONLY flag is set here, even for the 'x'
+ attribute in order to be compatible with the MSVC
+ linker. */
+ if (! readonly_removed)
+ flags |= SEC_READONLY;
+ break;
+
+ case 'y':
+ flags |= SEC_COFF_NOREAD | SEC_READONLY;
+ break;
+
+ case 'i': /* STYP_INFO */
+ case 'l': /* STYP_LIB */
+ case 'o': /* STYP_OVER */
+ as_warn (_("unsupported section attribute '%c'"), attr);
+ break;
+
+ default:
+ as_warn (_("unknown section attribute '%c'"), attr);
+ break;
+ }
+ }
+ if (attr == '"')
+ ++input_line_pointer;
+ }
+ }
+
+ sec = subseg_new (name, (subsegT) exp);
+
+ if (alignment >= 0)
+ sec->alignment_power = alignment;
+
+ oldflags = bfd_get_section_flags (stdoutput, sec);
+ if (oldflags == SEC_NO_FLAGS)
+ {
+ /* Set section flags for a new section just created by subseg_new.
+ Provide a default if no flags were parsed. */
+ if (flags == SEC_NO_FLAGS)
+ flags = TC_COFF_SECTION_DEFAULT_ATTRIBUTES;
+
+#ifdef COFF_LONG_SECTION_NAMES
+ /* Add SEC_LINK_ONCE and SEC_LINK_DUPLICATES_DISCARD to .gnu.linkonce
+ sections so adjust_reloc_syms in write.c will correctly handle
+ relocs which refer to non-local symbols in these sections. */
+ if (strneq (name, ".gnu.linkonce", sizeof (".gnu.linkonce") - 1))
+ flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
+#endif
+
+ if (! bfd_set_section_flags (stdoutput, sec, flags))
+ as_warn (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+ }
+ else if (flags != SEC_NO_FLAGS)
+ {
+ /* This section's attributes have already been set. Warn if the
+ attributes don't match. */
+ flagword matchflags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+ | SEC_DATA | SEC_COFF_SHARED | SEC_NEVER_LOAD
+ | SEC_COFF_NOREAD);
+ if ((flags ^ oldflags) & matchflags)
+ as_warn (_("Ignoring changed section attributes for %s"), name);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+void
+coff_adjust_symtab (void)
+{
+ if (symbol_rootP == NULL
+ || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
+ c_dot_file_symbol ("fake", 0);
+}
+
+void
+coff_frob_section (segT sec)
+{
+ segT strsec;
+ char *p;
+ fragS *fragp;
+ bfd_vma n_entries;
+
+ /* The COFF back end in BFD requires that all section sizes be
+ rounded up to multiples of the corresponding section alignments,
+ supposedly because standard COFF has no other way of encoding alignment
+ for sections. If your COFF flavor has a different way of encoding
+ section alignment, then skip this step, as TICOFF does. */
+ bfd_vma size = bfd_get_section_size (sec);
+#if !defined(TICOFF)
+ bfd_vma align_power = (bfd_vma) sec->alignment_power + OCTETS_PER_BYTE_POWER;
+ bfd_vma mask = ((bfd_vma) 1 << align_power) - 1;
+
+ if (size & mask)
+ {
+ bfd_vma new_size;
+ fragS *last;
+
+ new_size = (size + mask) & ~mask;
+ bfd_set_section_size (stdoutput, sec, new_size);
+
+ /* If the size had to be rounded up, add some padding in
+ the last non-empty frag. */
+ fragp = seg_info (sec)->frchainP->frch_root;
+ last = seg_info (sec)->frchainP->frch_last;
+ while (fragp->fr_next != last)
+ fragp = fragp->fr_next;
+ last->fr_address = size;
+ fragp->fr_offset += new_size - size;
+ }
+#endif
+
+ /* If the section size is non-zero, the section symbol needs an aux
+ entry associated with it, indicating the size. We don't know
+ all the values yet; coff_frob_symbol will fill them in later. */
+#ifndef TICOFF
+ if (size != 0
+ || sec == text_section
+ || sec == data_section
+ || sec == bss_section)
+#endif
+ {
+ symbolS *secsym = section_symbol (sec);
+ unsigned char sclass = C_STAT;
+
+#ifdef OBJ_XCOFF
+ if (bfd_get_section_flags (stdoutput, sec) & SEC_DEBUGGING)
+ sclass = C_DWARF;
+#endif
+ S_SET_STORAGE_CLASS (secsym, sclass);
+ S_SET_NUMBER_AUXILIARY (secsym, 1);
+ SF_SET_STATICS (secsym);
+ SA_SET_SCN_SCNLEN (secsym, size);
+ }
+ /* FIXME: These should be in a "stabs.h" file, or maybe as.h. */
+#ifndef STAB_SECTION_NAME
+#define STAB_SECTION_NAME ".stab"
+#endif
+#ifndef STAB_STRING_SECTION_NAME
+#define STAB_STRING_SECTION_NAME ".stabstr"
+#endif
+ if (! streq (STAB_STRING_SECTION_NAME, sec->name))
+ return;
+
+ strsec = sec;
+ sec = subseg_get (STAB_SECTION_NAME, 0);
+ /* size is already rounded up, since other section will be listed first */
+ size = bfd_get_section_size (strsec);
+
+ n_entries = bfd_get_section_size (sec) / 12 - 1;
+
+ /* Find first non-empty frag. It should be large enough. */
+ fragp = seg_info (sec)->frchainP->frch_root;
+ while (fragp && fragp->fr_fix == 0)
+ fragp = fragp->fr_next;
+ gas_assert (fragp != 0 && fragp->fr_fix >= 12);
+
+ /* Store the values. */
+ p = fragp->fr_literal;
+ bfd_h_put_16 (stdoutput, n_entries, (bfd_byte *) p + 6);
+ bfd_h_put_32 (stdoutput, size, (bfd_byte *) p + 8);
+}
+
+void
+obj_coff_init_stab_section (segT seg)
+{
+ char *file;
+ char *p;
+ char *stabstr_name;
+ unsigned int stroff;
+
+ /* Make space for this first symbol. */
+ p = frag_more (12);
+ /* Zero it out. */
+ memset (p, 0, 12);
+ as_where (&file, (unsigned int *) NULL);
+ stabstr_name = xmalloc (strlen (seg->name) + 4);
+ strcpy (stabstr_name, seg->name);
+ strcat (stabstr_name, "str");
+ stroff = get_stab_string_offset (file, stabstr_name);
+ know (stroff == 1);
+ md_number_to_chars (p, stroff, 4);
+}
+
+#ifdef DEBUG
+const char * s_get_name (symbolS *);
+
+const char *
+s_get_name (symbolS *s)
+{
+ return ((s == NULL) ? "(NULL)" : S_GET_NAME (s));
+}
+
+void symbol_dump (void);
+
+void
+symbol_dump (void)
+{
+ symbolS *symbolP;
+
+ for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP))
+ printf (_("0x%lx: \"%s\" type = %ld, class = %d, segment = %d\n"),
+ (unsigned long) symbolP,
+ S_GET_NAME (symbolP),
+ (long) S_GET_DATA_TYPE (symbolP),
+ S_GET_STORAGE_CLASS (symbolP),
+ (int) S_GET_SEGMENT (symbolP));
+}
+
+#endif /* DEBUG */
+
+const pseudo_typeS coff_pseudo_table[] =
+{
+ {"ABORT", s_abort, 0},
+ {"appline", obj_coff_ln, 1},
+ /* We accept the .bss directive for backward compatibility with
+ earlier versions of gas. */
+ {"bss", obj_coff_bss, 0},
+#ifdef TE_PE
+ /* PE provides an enhanced version of .comm with alignment. */
+ {"comm", obj_coff_comm, 0},
+#endif /* TE_PE */
+ {"def", obj_coff_def, 0},
+ {"dim", obj_coff_dim, 0},
+ {"endef", obj_coff_endef, 0},
+ {"ident", obj_coff_ident, 0},
+ {"line", obj_coff_line, 0},
+ {"ln", obj_coff_ln, 0},
+ {"scl", obj_coff_scl, 0},
+ {"sect", obj_coff_section, 0},
+ {"sect.s", obj_coff_section, 0},
+ {"section", obj_coff_section, 0},
+ {"section.s", obj_coff_section, 0},
+ /* FIXME: We ignore the MRI short attribute. */
+ {"size", obj_coff_size, 0},
+ {"tag", obj_coff_tag, 0},
+ {"type", obj_coff_type, 0},
+ {"val", obj_coff_val, 0},
+ {"version", s_ignore, 0},
+ {"loc", obj_coff_loc, 0},
+ {"optim", s_ignore, 0}, /* For sun386i cc (?) */
+ {"weak", obj_coff_weak, 0},
+#if defined TC_TIC4X
+ /* The tic4x uses sdef instead of def. */
+ {"sdef", obj_coff_def, 0},
+#endif
+#if defined(SEH_CMDS)
+ SEH_CMDS
+#endif
+ {NULL, NULL, 0}
+};
+
+
+/* Support for a COFF emulation. */
+
+static void
+coff_pop_insert (void)
+{
+ pop_insert (coff_pseudo_table);
+}
+
+static int
+coff_separate_stab_sections (void)
+{
+ return 1;
+}
+
+const struct format_ops coff_format_ops =
+{
+ bfd_target_coff_flavour,
+ 0, /* dfl_leading_underscore */
+ 1, /* emit_section_symbols */
+ 0, /* begin */
+ c_dot_file_symbol,
+ coff_frob_symbol,
+ 0, /* frob_file */
+ 0, /* frob_file_before_adjust */
+ 0, /* frob_file_before_fix */
+ coff_frob_file_after_relocs,
+ 0, /* s_get_size */
+ 0, /* s_set_size */
+ 0, /* s_get_align */
+ 0, /* s_set_align */
+ 0, /* s_get_other */
+ 0, /* s_set_other */
+ 0, /* s_get_desc */
+ 0, /* s_set_desc */
+ 0, /* s_get_type */
+ 0, /* s_set_type */
+ 0, /* copy_symbol_attributes */
+ 0, /* generate_asm_lineno */
+ 0, /* process_stab */
+ coff_separate_stab_sections,
+ obj_coff_init_stab_section,
+ 0, /* sec_sym_ok_for_reloc */
+ coff_pop_insert,
+ 0, /* ecoff_set_ext */
+ coff_obj_read_begin_hook,
+ coff_obj_symbol_new_hook,
+ coff_obj_symbol_clone_hook,
+ coff_adjust_symtab
+};
diff --git a/gas/config/obj-coff.h b/gas/config/obj-coff.h
new file mode 100644
index 0000000..dba6b63
--- /dev/null
+++ b/gas/config/obj-coff.h
@@ -0,0 +1,408 @@
+/* coff object file format
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef OBJ_FORMAT_H
+#define OBJ_FORMAT_H
+
+#define OBJ_COFF 1
+
+#include "targ-cpu.h"
+
+/* This internal_lineno crap is to stop namespace pollution from the
+ bfd internal coff headerfile. */
+#define internal_lineno bfd_internal_lineno
+#include "coff/internal.h"
+#undef internal_lineno
+
+/* CPU-specific setup: */
+
+#ifdef TC_ARM
+#include "coff/arm.h"
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "coff-arm"
+#endif
+#endif
+
+#ifdef TC_PPC
+#ifdef TE_PE
+#include "coff/powerpc.h"
+#else
+#include "coff/rs6000.h"
+#endif
+#endif
+
+#ifdef TC_SPARC
+#include "coff/sparc.h"
+#endif
+
+#ifdef TC_I386
+#ifdef TE_PEP
+#include "coff/x86_64.h"
+#else
+#include "coff/i386.h"
+#endif
+
+#ifndef TARGET_FORMAT
+#ifdef TE_PEP
+#define TARGET_FORMAT "coff-x86-64"
+#else
+#define TARGET_FORMAT "coff-i386"
+#endif
+#endif
+#endif
+
+#ifdef TC_M68K
+#include "coff/m68k.h"
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "coff-m68k"
+#endif
+#endif
+
+#ifdef TC_I960
+#include "coff/i960.h"
+#define TARGET_FORMAT "coff-Intel-little"
+#endif
+
+#ifdef TC_Z80
+#include "coff/z80.h"
+#define TARGET_FORMAT "coff-z80"
+#endif
+
+#ifdef TC_Z8K
+#include "coff/z8k.h"
+#define TARGET_FORMAT "coff-z8k"
+#endif
+
+#ifdef TC_H8300
+#include "coff/h8300.h"
+#define TARGET_FORMAT "coff-h8300"
+#endif
+
+#ifdef TC_H8500
+#include "coff/h8500.h"
+#define TARGET_FORMAT "coff-h8500"
+#endif
+
+#ifdef TC_SH
+
+#ifdef TE_PE
+#define COFF_WITH_PE
+#endif
+
+#include "coff/sh.h"
+
+#ifdef TE_PE
+#define TARGET_FORMAT "pe-shl"
+#else
+
+#define TARGET_FORMAT \
+ (!target_big_endian \
+ ? (sh_small ? "coff-shl-small" : "coff-shl") \
+ : (sh_small ? "coff-sh-small" : "coff-sh"))
+
+#endif
+#endif
+
+#ifdef TC_MIPS
+#define COFF_WITH_PE
+#include "coff/mipspe.h"
+#undef TARGET_FORMAT
+#define TARGET_FORMAT "pe-mips"
+#endif
+
+#ifdef TC_TIC30
+#include "coff/tic30.h"
+#define TARGET_FORMAT "coff-tic30"
+#endif
+
+#ifdef TC_TIC4X
+#include "coff/tic4x.h"
+#define TARGET_FORMAT "coff2-tic4x"
+#endif
+
+#ifdef TC_TIC54X
+#include "coff/tic54x.h"
+#define TARGET_FORMAT "coff1-c54x"
+#endif
+
+#ifdef TC_MCORE
+#include "coff/mcore.h"
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "pe-mcore"
+#endif
+#endif
+
+#ifdef TE_PE
+#define obj_set_weak_hook pecoff_obj_set_weak_hook
+#define obj_clear_weak_hook pecoff_obj_clear_weak_hook
+#endif
+
+#ifndef OBJ_COFF_MAX_AUXENTRIES
+#define OBJ_COFF_MAX_AUXENTRIES 1
+#endif
+
+#define obj_symbol_new_hook coff_obj_symbol_new_hook
+#define obj_symbol_clone_hook coff_obj_symbol_clone_hook
+#define obj_read_begin_hook coff_obj_read_begin_hook
+
+#include "bfd/libcoff.h"
+
+#define OUTPUT_FLAVOR bfd_target_coff_flavour
+
+/* Alter the field names, for now, until we've fixed up the other
+ references to use the new name. */
+#ifdef TC_I960
+#define TC_SYMFIELD_TYPE symbolS *
+#define sy_tc bal
+#endif
+
+#define OBJ_SYMFIELD_TYPE unsigned long
+#define sy_obj sy_obj_flags
+
+/* We can't use the predefined section symbols in bfd/section.c, as
+ COFF symbols have extra fields. See bfd/libcoff.h:coff_symbol_type. */
+#ifndef obj_sec_sym_ok_for_reloc
+#define obj_sec_sym_ok_for_reloc(SEC) ((SEC)->owner != 0)
+#endif
+
+#define SYM_AUXENT(S) \
+ (&coffsymbol (symbol_get_bfdsym (S))->native[1].u.auxent)
+#define SYM_AUXINFO(S) \
+ (&coffsymbol (symbol_get_bfdsym (S))->native[1])
+
+/* The number of auxiliary entries. */
+#define S_GET_NUMBER_AUXILIARY(s) \
+ (coffsymbol (symbol_get_bfdsym (s))->native->u.syment.n_numaux)
+/* The number of auxiliary entries. */
+#define S_SET_NUMBER_AUXILIARY(s, v) (S_GET_NUMBER_AUXILIARY (s) = (v))
+
+/* True if a symbol name is in the string table, i.e. its length is > 8. */
+#define S_IS_STRING(s) (strlen (S_GET_NAME (s)) > 8 ? 1 : 0)
+
+/* Auxiliary entry macros. SA_ stands for symbol auxiliary. */
+/* Omit the tv related fields. */
+/* Accessors. */
+
+#define SA_GET_SYM_TAGNDX(s) (SYM_AUXENT (s)->x_sym.x_tagndx.l)
+#define SA_GET_SYM_LNNO(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno)
+#define SA_GET_SYM_SIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size)
+#define SA_GET_SYM_FSIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize)
+#define SA_GET_SYM_LNNOPTR(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr)
+#define SA_GET_SYM_ENDNDX(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_endndx)
+#define SA_GET_SYM_DIMEN(s,i) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)])
+#define SA_GET_FILE_FNAME(s) (SYM_AUXENT (s)->x_file.x_fname)
+#define SA_GET_SCN_SCNLEN(s) (SYM_AUXENT (s)->x_scn.x_scnlen)
+#define SA_GET_SCN_NRELOC(s) (SYM_AUXENT (s)->x_scn.x_nreloc)
+#define SA_GET_SCN_NLINNO(s) (SYM_AUXENT (s)->x_scn.x_nlinno)
+
+#define SA_SET_SYM_LNNO(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno = (v))
+#define SA_SET_SYM_SIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size = (v))
+#define SA_SET_SYM_FSIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize = (v))
+#define SA_SET_SYM_LNNOPTR(s,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr = (v))
+#define SA_SET_SYM_DIMEN(s,i,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)] = (v))
+#define SA_SET_FILE_FNAME(s,v) strncpy (SYM_AUXENT (s)->x_file.x_fname, (v), FILNMLEN)
+#define SA_SET_SCN_SCNLEN(s,v) (SYM_AUXENT (s)->x_scn.x_scnlen = (v))
+#define SA_SET_SCN_NRELOC(s,v) (SYM_AUXENT (s)->x_scn.x_nreloc = (v))
+#define SA_SET_SCN_NLINNO(s,v) (SYM_AUXENT (s)->x_scn.x_nlinno = (v))
+
+/* Internal use only definitions. SF_ stands for symbol flags.
+
+ These values can be assigned to sy_symbol.ost_flags field of a symbolS.
+
+ You'll break i960 if you shift the SYSPROC bits anywhere else. for
+ more on the balname/callname hack, see tc-i960.h. b.out is done
+ differently. */
+
+#define SF_I960_MASK 0x000001ff /* Bits 0-8 are used by the i960 port. */
+#define SF_SYSPROC 0x0000003f /* bits 0-5 are used to store the sysproc number. */
+#define SF_IS_SYSPROC 0x00000040 /* bit 6 marks symbols that are sysprocs. */
+#define SF_BALNAME 0x00000080 /* bit 7 marks BALNAME symbols. */
+#define SF_CALLNAME 0x00000100 /* bit 8 marks CALLNAME symbols. */
+
+#define SF_NORMAL_MASK 0x0000ffff /* bits 12-15 are general purpose. */
+
+#define SF_STATICS 0x00001000 /* Mark the .text & all symbols. */
+#define SF_DEFINED 0x00002000 /* Symbol is defined in this file. */
+#define SF_STRING 0x00004000 /* Symbol name length > 8. */
+#define SF_LOCAL 0x00008000 /* Symbol must not be emitted. */
+
+#define SF_DEBUG_MASK 0xffff0000 /* bits 16-31 are debug info. */
+
+#define SF_FUNCTION 0x00010000 /* The symbol is a function. */
+#define SF_PROCESS 0x00020000 /* Process symbol before write. */
+#define SF_TAGGED 0x00040000 /* Is associated with a tag. */
+#define SF_TAG 0x00080000 /* Is a tag. */
+#define SF_DEBUG 0x00100000 /* Is in debug or abs section. */
+#define SF_GET_SEGMENT 0x00200000 /* Get the section of the forward symbol. */
+/* All other bits are unused. */
+
+/* Accessors. */
+#define SF_GET(s) (* symbol_get_obj (s))
+#define SF_GET_DEBUG(s) (symbol_get_bfdsym (s)->flags & BSF_DEBUGGING)
+#define SF_SET_DEBUG(s) (symbol_get_bfdsym (s)->flags |= BSF_DEBUGGING)
+#define SF_GET_NORMAL_FIELD(s) (SF_GET (s) & SF_NORMAL_MASK)
+#define SF_GET_DEBUG_FIELD(s) (SF_GET (s) & SF_DEBUG_MASK)
+#define SF_GET_FILE(s) (SF_GET (s) & SF_FILE)
+#define SF_GET_STATICS(s) (SF_GET (s) & SF_STATICS)
+#define SF_GET_DEFINED(s) (SF_GET (s) & SF_DEFINED)
+#define SF_GET_STRING(s) (SF_GET (s) & SF_STRING)
+#define SF_GET_LOCAL(s) (SF_GET (s) & SF_LOCAL)
+#define SF_GET_FUNCTION(s) (SF_GET (s) & SF_FUNCTION)
+#define SF_GET_PROCESS(s) (SF_GET (s) & SF_PROCESS)
+#define SF_GET_TAGGED(s) (SF_GET (s) & SF_TAGGED)
+#define SF_GET_TAG(s) (SF_GET (s) & SF_TAG)
+#define SF_GET_GET_SEGMENT(s) (SF_GET (s) & SF_GET_SEGMENT)
+#define SF_GET_I960(s) (SF_GET (s) & SF_I960_MASK) /* Used by i960. */
+#define SF_GET_BALNAME(s) (SF_GET (s) & SF_BALNAME) /* Used by i960. */
+#define SF_GET_CALLNAME(s) (SF_GET (s) & SF_CALLNAME) /* Used by i960. */
+#define SF_GET_IS_SYSPROC(s) (SF_GET (s) & SF_IS_SYSPROC) /* Used by i960. */
+#define SF_GET_SYSPROC(s) (SF_GET (s) & SF_SYSPROC) /* Used by i960. */
+
+/* Modifiers. */
+#define SF_SET(s,v) (SF_GET (s) = (v))
+#define SF_SET_NORMAL_FIELD(s,v)(SF_GET (s) |= ((v) & SF_NORMAL_MASK))
+#define SF_SET_DEBUG_FIELD(s,v) (SF_GET (s) |= ((v) & SF_DEBUG_MASK))
+#define SF_SET_FILE(s) (SF_GET (s) |= SF_FILE)
+#define SF_SET_STATICS(s) (SF_GET (s) |= SF_STATICS)
+#define SF_SET_DEFINED(s) (SF_GET (s) |= SF_DEFINED)
+#define SF_SET_STRING(s) (SF_GET (s) |= SF_STRING)
+#define SF_SET_LOCAL(s) (SF_GET (s) |= SF_LOCAL)
+#define SF_CLEAR_LOCAL(s) (SF_GET (s) &= ~SF_LOCAL)
+#define SF_SET_FUNCTION(s) (SF_GET (s) |= SF_FUNCTION)
+#define SF_SET_PROCESS(s) (SF_GET (s) |= SF_PROCESS)
+#define SF_SET_TAGGED(s) (SF_GET (s) |= SF_TAGGED)
+#define SF_SET_TAG(s) (SF_GET (s) |= SF_TAG)
+#define SF_SET_GET_SEGMENT(s) (SF_GET (s) |= SF_GET_SEGMENT)
+#define SF_SET_I960(s,v) (SF_GET (s) |= ((v) & SF_I960_MASK)) /* Used by i960. */
+#define SF_SET_BALNAME(s) (SF_GET (s) |= SF_BALNAME) /* Used by i960. */
+#define SF_SET_CALLNAME(s) (SF_GET (s) |= SF_CALLNAME) /* Used by i960. */
+#define SF_SET_IS_SYSPROC(s) (SF_GET (s) |= SF_IS_SYSPROC) /* Used by i960. */
+#define SF_SET_SYSPROC(s,v) (SF_GET (s) |= ((v) & SF_SYSPROC)) /* Used by i960. */
+
+
+/* Line number handling. */
+extern int text_lineno_number;
+extern int coff_line_base;
+extern int coff_n_line_nos;
+extern symbolS *coff_last_function;
+
+#define obj_emit_lineno(WHERE, LINE, FILE_START) abort ()
+#define obj_app_file(name, app) c_dot_file_symbol (name, app)
+#define obj_frob_symbol(S,P) coff_frob_symbol (S, & P)
+#define obj_frob_section(S) coff_frob_section (S)
+#define obj_frob_file_after_relocs() coff_frob_file_after_relocs ()
+#ifndef obj_adjust_symtab
+#define obj_adjust_symtab() coff_adjust_symtab ()
+#endif
+
+/* Forward the segment of a forwarded symbol, handle assignments that
+ just copy symbol values, etc. */
+#ifndef OBJ_COPY_SYMBOL_ATTRIBUTES
+#ifndef TE_I386AIX
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest, src) \
+ (SF_GET_GET_SEGMENT (dest) \
+ ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \
+ : 0)
+#else
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest, src) \
+ (SF_GET_GET_SEGMENT (dest) && S_GET_SEGMENT (dest) == SEG_UNKNOWN \
+ ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \
+ : 0)
+#endif
+#endif
+
+/* Sanity check. */
+
+#ifdef TC_I960
+#ifndef C_LEAFSTAT
+hey ! Where is the C_LEAFSTAT definition ? i960 - coff support is depending on it.
+#endif /* no C_LEAFSTAT */
+#endif /* TC_I960 */
+
+extern const pseudo_typeS coff_pseudo_table[];
+
+#ifndef obj_pop_insert
+#define obj_pop_insert() pop_insert (coff_pseudo_table)
+#endif
+
+/* In COFF, if a symbol is defined using .def/.val SYM/.endef, it's OK
+ to redefine the symbol later on. This can happen if C symbols use
+ a prefix, and a symbol is defined both with and without the prefix,
+ as in start/_start/__start in gcc/libgcc1-test.c. */
+#define RESOLVE_SYMBOL_REDEFINITION(sym) \
+(SF_GET_GET_SEGMENT (sym) \
+ ? (sym->sy_frag = frag_now, \
+ S_SET_VALUE (sym, frag_now_fix ()), \
+ S_SET_SEGMENT (sym, now_seg), \
+ 0) \
+ : 0)
+
+/* Stabs in a coff file go into their own section. */
+#define SEPARATE_STAB_SECTIONS 1
+
+/* We need 12 bytes at the start of the section to hold some initial
+ information. */
+#define INIT_STAB_SECTION(seg) obj_coff_init_stab_section (seg)
+
+/* Store the number of relocations in the section aux entry. */
+#define SET_SECTION_RELOCS(sec, relocs, n) \
+ SA_SET_SCN_NRELOC (section_symbol (sec), n)
+
+#define obj_app_file(name, app) c_dot_file_symbol (name, app)
+
+extern int S_SET_DATA_TYPE (symbolS *, int);
+extern int S_SET_STORAGE_CLASS (symbolS *, int);
+extern int S_GET_STORAGE_CLASS (symbolS *);
+extern void SA_SET_SYM_ENDNDX (symbolS *, symbolS *);
+extern void coff_add_linesym (symbolS *);
+extern void c_dot_file_symbol (const char *, int);
+extern void coff_frob_symbol (symbolS *, int *);
+extern void coff_adjust_symtab (void);
+extern void coff_frob_section (segT);
+extern void coff_adjust_section_syms (bfd *, asection *, void *);
+extern void coff_frob_file_after_relocs (void);
+extern void coff_obj_symbol_new_hook (symbolS *);
+extern void coff_obj_symbol_clone_hook (symbolS *, symbolS *);
+extern void coff_obj_read_begin_hook (void);
+#ifdef TE_PE
+extern void pecoff_obj_set_weak_hook (symbolS *);
+extern void pecoff_obj_clear_weak_hook (symbolS *);
+#endif
+extern void obj_coff_section (int);
+extern segT obj_coff_add_segment (const char *);
+extern void obj_coff_section (int);
+extern void c_dot_file_symbol (const char *, int);
+extern segT s_get_segment (symbolS *);
+#ifndef tc_coff_symbol_emit_hook
+extern void tc_coff_symbol_emit_hook (symbolS *);
+#endif
+extern void obj_coff_pe_handle_link_once (void);
+extern void obj_coff_init_stab_section (segT);
+extern void c_section_header (struct internal_scnhdr *,
+ char *, long, long, long, long,
+ long, long, long, long);
+extern void obj_coff_seh_do_final (void);
+
+#ifndef obj_coff_generate_pdata
+#define obj_coff_generate_pdata obj_coff_seh_do_final
+#endif
+
+
+#endif /* OBJ_FORMAT_H */
diff --git a/gas/config/obj-ecoff.c b/gas/config/obj-ecoff.c
new file mode 100644
index 0000000..3c1df47
--- /dev/null
+++ b/gas/config/obj-ecoff.c
@@ -0,0 +1,319 @@
+/* ECOFF object file format.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+ This file was put together by Ian Lance Taylor <ian@cygnus.com>.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define OBJ_HEADER "obj-ecoff.h"
+#include "as.h"
+#include "coff/internal.h"
+#include "bfd/libcoff.h"
+#include "bfd/libecoff.h"
+
+/* Almost all of the ECOFF support is actually in ecoff.c in the main
+ gas directory. This file mostly just arranges to call that one at
+ the right times. */
+
+/* Set section VMAs and GP values before reloc processing. */
+
+void
+ecoff_frob_file_before_fix (void)
+{
+ bfd_vma addr;
+ asection *sec;
+
+ /* Set the section VMA values. We force the .sdata and .sbss
+ sections to the end to ensure that their VMA addresses are close
+ together so that the GP register can address both of them. We
+ put the .bss section after the .sbss section.
+
+ Also, for the Alpha, we must sort the sections, to make sure they
+ appear in the output file in the correct order. (Actually, maybe
+ this is a job for BFD. But the VMAs computed would be out of
+ whack if we computed them given our initial, random ordering.
+ It's possible that that wouldn't break things; I could do some
+ experimenting sometime and find out.
+
+ This output ordering of sections is magic, on the Alpha, at
+ least. The .lita section must come before .lit8 and .lit4,
+ otherwise the OSF/1 linker may silently trash the .lit{4,8}
+ section contents. Also, .text must preceed .rdata. These differ
+ from the order described in some parts of the DEC OSF/1 Assembly
+ Language Programmer's Guide, but that order doesn't seem to work
+ with their linker.
+
+ I don't know if section ordering on the MIPS is important. */
+
+ static const char *const names[] =
+ {
+ /* text segment */
+ ".text", ".rdata", ".init", ".fini",
+ /* data segment */
+ ".data", ".lita", ".lit8", ".lit4", ".sdata", ".got",
+ /* bss segment */
+ ".sbss", ".bss",
+ };
+#define n_names ((int) (sizeof (names) / sizeof (names[0])))
+
+ /* Sections that match names, order to be straightened out later. */
+ asection *secs[n_names];
+ int i;
+
+ addr = 0;
+ for (i = 0; i < n_names; i++)
+ secs[i] = NULL;
+
+ for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
+ {
+ for (i = 0; i < n_names; i++)
+ if (!strcmp (sec->name, names[i]))
+ {
+ secs[i] = sec;
+ bfd_section_list_remove (stdoutput, sec);
+ break;
+ }
+ if (i == n_names)
+ {
+ bfd_set_section_vma (stdoutput, sec, addr);
+ addr += bfd_section_size (stdoutput, sec);
+ }
+ }
+ for (i = 0; i < n_names; i++)
+ if (secs[i])
+ {
+ bfd_set_section_vma (stdoutput, secs[i], addr);
+ addr += bfd_section_size (stdoutput, secs[i]);
+ }
+ for (i = n_names - 1; i >= 0; i--)
+ if (secs[i])
+ bfd_section_list_prepend (stdoutput, secs[i]);
+
+ /* Fill in the register masks. */
+ {
+ unsigned long gprmask = 0;
+ unsigned long fprmask = 0;
+ unsigned long *cprmask = NULL;
+
+#ifdef TC_MIPS
+ /* Fill in the MIPS register masks. It's probably not worth
+ setting up a generic interface for this. */
+ gprmask = mips_gprmask;
+ cprmask = mips_cprmask;
+#endif
+
+#ifdef TC_ALPHA
+ alpha_frob_ecoff_data ();
+
+ if (! bfd_ecoff_set_gp_value (stdoutput, alpha_gp_value))
+ as_fatal (_("Can't set GP value"));
+
+ gprmask = alpha_gprmask;
+ fprmask = alpha_fprmask;
+#endif
+
+ if (! bfd_ecoff_set_regmasks (stdoutput, gprmask, fprmask, cprmask))
+ as_fatal (_("Can't set register masks"));
+ }
+}
+
+/* Swap out the symbols and debugging information for BFD. */
+
+void
+ecoff_frob_file (void)
+{
+ const struct ecoff_debug_swap * const debug_swap
+ = &ecoff_backend (stdoutput)->debug_swap;
+ bfd_vma addr ATTRIBUTE_UNUSED;
+ HDRR *hdr;
+ char *buf;
+ char *set;
+
+ /* Build the ECOFF debugging information. */
+ gas_assert (ecoff_data (stdoutput) != 0);
+ hdr = &ecoff_data (stdoutput)->debug_info.symbolic_header;
+ ecoff_build_debug (hdr, &buf, debug_swap);
+
+ /* Finish up the ecoff_tdata structure. */
+ set = buf;
+#define SET(ptr, count, type, size) \
+ if (hdr->count == 0) \
+ ecoff_data (stdoutput)->debug_info.ptr = NULL; \
+ else \
+ { \
+ ecoff_data (stdoutput)->debug_info.ptr = (type) set; \
+ set += hdr->count * size; \
+ }
+
+ SET (line, cbLine, unsigned char *, sizeof (unsigned char));
+ SET (external_dnr, idnMax, void *, debug_swap->external_dnr_size);
+ SET (external_pdr, ipdMax, void *, debug_swap->external_pdr_size);
+ SET (external_sym, isymMax, void *, debug_swap->external_sym_size);
+ SET (external_opt, ioptMax, void *, debug_swap->external_opt_size);
+ SET (external_aux, iauxMax, union aux_ext *, sizeof (union aux_ext));
+ SET (ss, issMax, char *, sizeof (char));
+ SET (ssext, issExtMax, char *, sizeof (char));
+ SET (external_rfd, crfd, void *, debug_swap->external_rfd_size);
+ SET (external_fdr, ifdMax, void *, debug_swap->external_fdr_size);
+ SET (external_ext, iextMax, void *, debug_swap->external_ext_size);
+#undef SET
+}
+
+/* This is called by the ECOFF code to set the external information
+ for a symbol. We just pass it on to BFD, which expects the swapped
+ information to be stored in the native field of the symbol. */
+
+void
+obj_ecoff_set_ext (symbolS *sym, EXTR *ext)
+{
+ const struct ecoff_debug_swap * const debug_swap
+ = &ecoff_backend (stdoutput)->debug_swap;
+ ecoff_symbol_type *esym;
+
+ know (bfd_asymbol_flavour (symbol_get_bfdsym (sym))
+ == bfd_target_ecoff_flavour);
+ esym = ecoffsymbol (symbol_get_bfdsym (sym));
+ esym->local = FALSE;
+ esym->native = xmalloc (debug_swap->external_ext_size);
+ (*debug_swap->swap_ext_out) (stdoutput, ext, esym->native);
+}
+
+static int
+ecoff_sec_sym_ok_for_reloc (asection *sec ATTRIBUTE_UNUSED)
+{
+ return 1;
+}
+
+static void
+obj_ecoff_frob_symbol (symbolS *sym, int *puntp ATTRIBUTE_UNUSED)
+{
+ ecoff_frob_symbol (sym);
+}
+
+static void
+ecoff_pop_insert (void)
+{
+ pop_insert (obj_pseudo_table);
+}
+
+static int
+ecoff_separate_stab_sections (void)
+{
+ return 0;
+}
+
+/* These are the pseudo-ops we support in this file. Only those
+ relating to debugging information are supported here.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .aent,
+ .bgnb, .endb, .verstamp, .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ MIPS CPU specific, and should be defined by tc-mips.c: .alias,
+ .extern, .galive, .gjaldef, .gjrlive, .livereg, .noalias, .option,
+ .rdata, .sdata, .set.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not ECOFF specific. I have
+ only listed the ones which are not already in read.c. It's not
+ completely clear where these should be defined, but tc-mips.c is
+ probably the most reasonable place: .asciiz, .asm0, .endr, .err,
+ .half, .lab, .repeat, .struct, .weakext. */
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ /* COFF style debugging information. .ln is not used; .loc is used
+ instead. */
+ { "def", ecoff_directive_def, 0 },
+ { "dim", ecoff_directive_dim, 0 },
+ { "endef", ecoff_directive_endef, 0 },
+ { "file", ecoff_directive_file, 0 },
+ { "scl", ecoff_directive_scl, 0 },
+ { "size", ecoff_directive_size, 0 },
+ { "esize", ecoff_directive_size, 0 },
+ { "tag", ecoff_directive_tag, 0 },
+ { "type", ecoff_directive_type, 0 },
+ { "etype", ecoff_directive_type, 0 },
+ { "val", ecoff_directive_val, 0 },
+
+ /* ECOFF specific debugging information. */
+ { "begin", ecoff_directive_begin, 0 },
+ { "bend", ecoff_directive_bend, 0 },
+ { "end", ecoff_directive_end, 0 },
+ { "ent", ecoff_directive_ent, 0 },
+ { "fmask", ecoff_directive_fmask, 0 },
+ { "frame", ecoff_directive_frame, 0 },
+ { "loc", ecoff_directive_loc, 0 },
+ { "mask", ecoff_directive_mask, 0 },
+
+ /* Other ECOFF directives. */
+ { "extern", ecoff_directive_extern, 0 },
+
+#ifndef TC_MIPS
+ /* For TC_MIPS, tc-mips.c adds this. */
+ { "weakext", ecoff_directive_weakext, 0 },
+#endif
+
+ /* These are used on Irix. I don't know how to implement them. */
+ { "bgnb", s_ignore, 0 },
+ { "endb", s_ignore, 0 },
+ { "verstamp", s_ignore, 0 },
+
+ /* Sentinel. */
+ { NULL, s_ignore, 0 }
+};
+
+const struct format_ops ecoff_format_ops =
+{
+ bfd_target_ecoff_flavour,
+ 0, /* dfl_leading_underscore. */
+
+ /* FIXME: A comment why emit_section_symbols is different here (1) from
+ the single-format definition (0) would be in order. */
+ 1, /* emit_section_symbols. */
+ 0, /* begin. */
+ ecoff_new_file,
+ obj_ecoff_frob_symbol,
+ ecoff_frob_file,
+ 0, /* frob_file_before_adjust. */
+ ecoff_frob_file_before_fix,
+ 0, /* frob_file_after_relocs. */
+ 0, /* s_get_size. */
+ 0, /* s_set_size. */
+ 0, /* s_get_align. */
+ 0, /* s_set_align. */
+ 0, /* s_get_other. */
+ 0, /* s_set_other. */
+ 0, /* s_get_desc. */
+ 0, /* s_set_desc. */
+ 0, /* s_get_type. */
+ 0, /* s_set_type. */
+ 0, /* copy_symbol_attributes. */
+ ecoff_generate_asm_lineno,
+ ecoff_stab,
+ ecoff_separate_stab_sections,
+ 0, /* init_stab_section. */
+ ecoff_sec_sym_ok_for_reloc,
+ ecoff_pop_insert,
+ ecoff_set_ext,
+ ecoff_read_begin_hook,
+ ecoff_symbol_new_hook,
+ ecoff_symbol_clone_hook,
+ 0 /* adjust_symtab. */
+};
diff --git a/gas/config/obj-ecoff.h b/gas/config/obj-ecoff.h
new file mode 100644
index 0000000..70cafdf
--- /dev/null
+++ b/gas/config/obj-ecoff.h
@@ -0,0 +1,77 @@
+/* ECOFF object file format header file.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+ Contributed by Cygnus Support.
+ Written by Ian Lance Taylor <ian@cygnus.com>.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define OBJ_ECOFF 1
+
+/* Use the generic ECOFF debugging code. */
+#define ECOFF_DEBUGGING 1
+
+#define OUTPUT_FLAVOR bfd_target_ecoff_flavour
+
+#include "targ-cpu.h"
+
+#include "ecoff.h"
+
+/* For each gas symbol we keep track of which file it came from, of
+ whether we have generated an ECOFF symbol for it, and whether the
+ symbols is undefined (this last is needed to distinguish a .extern
+ symbols from a .comm symbol). */
+
+struct ecoff_sy_obj
+{
+ struct efdr *ecoff_file;
+ struct localsym *ecoff_symbol;
+ valueT ecoff_extern_size;
+};
+
+#define OBJ_SYMFIELD_TYPE struct ecoff_sy_obj
+
+/* Modify the ECOFF symbol. */
+#define obj_frob_symbol(symp, punt) ecoff_frob_symbol (symp)
+
+/* Set section VMAs and GP. */
+#define obj_frob_file_before_fix() ecoff_frob_file_before_fix ()
+
+/* This is used to write the symbolic data in the format that BFD
+ expects it. */
+#define obj_frob_file() ecoff_frob_file ()
+
+/* We use the ECOFF functions as our hooks. */
+#define obj_read_begin_hook ecoff_read_begin_hook
+#define obj_symbol_new_hook ecoff_symbol_new_hook
+#define obj_symbol_clone_hook ecoff_symbol_clone_hook
+
+/* Record file switches in the ECOFF symbol table. */
+#define obj_app_file(name, app) ecoff_new_file (name, app)
+
+/* At the moment we don't want to do any stabs processing in read.c. */
+#define OBJ_PROCESS_STAB(seg, what, string, type, other, desc) \
+ ecoff_stab ((seg), (what), (string), (type), (other), (desc))
+
+#define EMIT_SECTION_SYMBOLS 0
+#define obj_sec_sym_ok_for_reloc(SEC) 1
+
+#define obj_ecoff_set_ext ecoff_set_ext
+
+extern void ecoff_frob_file_before_fix (void);
+extern void ecoff_frob_file (void);
+extern void obj_ecoff_set_ext (symbolS *, EXTR *);
diff --git a/gas/config/obj-elf.c b/gas/config/obj-elf.c
new file mode 100644
index 0000000..e2ef99e
--- /dev/null
+++ b/gas/config/obj-elf.c
@@ -0,0 +1,2683 @@
+/* ELF object file format
+ Copyright (C) 1992-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define OBJ_HEADER "obj-elf.h"
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "obstack.h"
+#include "struc-symbol.h"
+#include "dwarf2dbg.h"
+
+#ifndef ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#else
+#define NEED_ECOFF_DEBUG
+#endif
+
+#ifdef NEED_ECOFF_DEBUG
+#include "ecoff.h"
+#endif
+
+#ifdef TC_ALPHA
+#include "elf/alpha.h"
+#endif
+
+#ifdef TC_MIPS
+#include "elf/mips.h"
+#endif
+
+#ifdef TC_PPC
+#include "elf/ppc.h"
+#endif
+
+#ifdef TC_I370
+#include "elf/i370.h"
+#endif
+
+#ifdef TC_I386
+#include "elf/x86-64.h"
+#endif
+
+#ifdef TC_MEP
+#include "elf/mep.h"
+#endif
+
+#ifdef TC_NIOS2
+#include "elf/nios2.h"
+#endif
+
+static void obj_elf_line (int);
+static void obj_elf_size (int);
+static void obj_elf_type (int);
+static void obj_elf_ident (int);
+static void obj_elf_weak (int);
+static void obj_elf_local (int);
+static void obj_elf_visibility (int);
+static void obj_elf_symver (int);
+static void obj_elf_subsection (int);
+static void obj_elf_popsection (int);
+static void obj_elf_gnu_attribute (int);
+static void obj_elf_tls_common (int);
+static void obj_elf_lcomm (int);
+static void obj_elf_struct (int);
+
+static const pseudo_typeS elf_pseudo_table[] =
+{
+ {"comm", obj_elf_common, 0},
+ {"common", obj_elf_common, 1},
+ {"ident", obj_elf_ident, 0},
+ {"lcomm", obj_elf_lcomm, 0},
+ {"local", obj_elf_local, 0},
+ {"previous", obj_elf_previous, 0},
+ {"section", obj_elf_section, 0},
+ {"section.s", obj_elf_section, 0},
+ {"sect", obj_elf_section, 0},
+ {"sect.s", obj_elf_section, 0},
+ {"pushsection", obj_elf_section, 1},
+ {"popsection", obj_elf_popsection, 0},
+ {"size", obj_elf_size, 0},
+ {"type", obj_elf_type, 0},
+ {"version", obj_elf_version, 0},
+ {"weak", obj_elf_weak, 0},
+
+ /* These define symbol visibility. */
+ {"internal", obj_elf_visibility, STV_INTERNAL},
+ {"hidden", obj_elf_visibility, STV_HIDDEN},
+ {"protected", obj_elf_visibility, STV_PROTECTED},
+
+ /* These are used for stabs-in-elf configurations. */
+ {"line", obj_elf_line, 0},
+
+ /* This is a GNU extension to handle symbol versions. */
+ {"symver", obj_elf_symver, 0},
+
+ /* A GNU extension to change subsection only. */
+ {"subsection", obj_elf_subsection, 0},
+
+ /* These are GNU extensions to aid in garbage collecting C++ vtables. */
+ {"vtable_inherit", (void (*) (int)) &obj_elf_vtable_inherit, 0},
+ {"vtable_entry", (void (*) (int)) &obj_elf_vtable_entry, 0},
+
+ /* A GNU extension for object attributes. */
+ {"gnu_attribute", obj_elf_gnu_attribute, 0},
+
+ /* These are used for dwarf. */
+ {"2byte", cons, 2},
+ {"4byte", cons, 4},
+ {"8byte", cons, 8},
+ /* These are used for dwarf2. */
+ { "file", (void (*) (int)) dwarf2_directive_file, 0 },
+ { "loc", dwarf2_directive_loc, 0 },
+ { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
+
+ /* We need to trap the section changing calls to handle .previous. */
+ {"data", obj_elf_data, 0},
+ {"offset", obj_elf_struct, 0},
+ {"struct", obj_elf_struct, 0},
+ {"text", obj_elf_text, 0},
+
+ {"tls_common", obj_elf_tls_common, 0},
+
+ /* End sentinel. */
+ {NULL, NULL, 0},
+};
+
+static const pseudo_typeS ecoff_debug_pseudo_table[] =
+{
+#ifdef NEED_ECOFF_DEBUG
+ /* COFF style debugging information for ECOFF. .ln is not used; .loc
+ is used instead. */
+ { "def", ecoff_directive_def, 0 },
+ { "dim", ecoff_directive_dim, 0 },
+ { "endef", ecoff_directive_endef, 0 },
+ { "file", ecoff_directive_file, 0 },
+ { "scl", ecoff_directive_scl, 0 },
+ { "tag", ecoff_directive_tag, 0 },
+ { "val", ecoff_directive_val, 0 },
+
+ /* COFF debugging requires pseudo-ops .size and .type, but ELF
+ already has meanings for those. We use .esize and .etype
+ instead. These are only generated by gcc anyhow. */
+ { "esize", ecoff_directive_size, 0 },
+ { "etype", ecoff_directive_type, 0 },
+
+ /* ECOFF specific debugging information. */
+ { "begin", ecoff_directive_begin, 0 },
+ { "bend", ecoff_directive_bend, 0 },
+ { "end", ecoff_directive_end, 0 },
+ { "ent", ecoff_directive_ent, 0 },
+ { "fmask", ecoff_directive_fmask, 0 },
+ { "frame", ecoff_directive_frame, 0 },
+ { "loc", ecoff_directive_loc, 0 },
+ { "mask", ecoff_directive_mask, 0 },
+
+ /* Other ECOFF directives. */
+ { "extern", ecoff_directive_extern, 0 },
+
+ /* These are used on Irix. I don't know how to implement them. */
+ { "alias", s_ignore, 0 },
+ { "bgnb", s_ignore, 0 },
+ { "endb", s_ignore, 0 },
+ { "lab", s_ignore, 0 },
+ { "noalias", s_ignore, 0 },
+ { "verstamp", s_ignore, 0 },
+ { "vreg", s_ignore, 0 },
+#endif
+
+ {NULL, NULL, 0} /* end sentinel */
+};
+
+#undef NO_RELOC
+#include "aout/aout64.h"
+
+/* This is called when the assembler starts. */
+
+asection *elf_com_section_ptr;
+
+void
+elf_begin (void)
+{
+ asection *s;
+
+ /* Add symbols for the known sections to the symbol table. */
+ s = bfd_get_section_by_name (stdoutput, TEXT_SECTION_NAME);
+ symbol_table_insert (section_symbol (s));
+ s = bfd_get_section_by_name (stdoutput, DATA_SECTION_NAME);
+ symbol_table_insert (section_symbol (s));
+ s = bfd_get_section_by_name (stdoutput, BSS_SECTION_NAME);
+ symbol_table_insert (section_symbol (s));
+ elf_com_section_ptr = bfd_com_section_ptr;
+}
+
+void
+elf_pop_insert (void)
+{
+ pop_insert (elf_pseudo_table);
+ if (ECOFF_DEBUGGING)
+ pop_insert (ecoff_debug_pseudo_table);
+}
+
+static bfd_vma
+elf_s_get_size (symbolS *sym)
+{
+ return S_GET_SIZE (sym);
+}
+
+static void
+elf_s_set_size (symbolS *sym, bfd_vma sz)
+{
+ S_SET_SIZE (sym, sz);
+}
+
+static bfd_vma
+elf_s_get_align (symbolS *sym)
+{
+ return S_GET_ALIGN (sym);
+}
+
+static void
+elf_s_set_align (symbolS *sym, bfd_vma align)
+{
+ S_SET_ALIGN (sym, align);
+}
+
+int
+elf_s_get_other (symbolS *sym)
+{
+ return elf_symbol (symbol_get_bfdsym (sym))->internal_elf_sym.st_other;
+}
+
+static void
+elf_s_set_other (symbolS *sym, int other)
+{
+ S_SET_OTHER (sym, other);
+}
+
+static int
+elf_sec_sym_ok_for_reloc (asection *sec)
+{
+ return obj_sec_sym_ok_for_reloc (sec);
+}
+
+void
+elf_file_symbol (const char *s, int appfile)
+{
+ if (!appfile
+ || symbol_rootP == NULL
+ || symbol_rootP->bsym == NULL
+ || (symbol_rootP->bsym->flags & BSF_FILE) == 0)
+ {
+ symbolS *sym;
+ size_t name_length;
+
+ sym = symbol_new (s, absolute_section, 0, NULL);
+ symbol_set_frag (sym, &zero_address_frag);
+
+ name_length = strlen (s);
+ if (name_length > strlen (S_GET_NAME (sym)))
+ {
+ obstack_grow (&notes, s, name_length + 1);
+ S_SET_NAME (sym, (const char *) obstack_finish (&notes));
+ }
+ else
+ strcpy ((char *) S_GET_NAME (sym), s);
+
+ symbol_get_bfdsym (sym)->flags |= BSF_FILE;
+
+ if (symbol_rootP != sym)
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (sym, symbol_rootP, &symbol_rootP, &symbol_lastP);
+#ifdef DEBUG
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif
+ }
+ }
+
+#ifdef NEED_ECOFF_DEBUG
+ ecoff_new_file (s, appfile);
+#endif
+}
+
+/* Called from read.c:s_comm after we've parsed .comm symbol, size.
+ Parse a possible alignment value. */
+
+symbolS *
+elf_common_parse (int ignore ATTRIBUTE_UNUSED, symbolS *symbolP, addressT size)
+{
+ addressT align = 0;
+ int is_local = symbol_get_obj (symbolP)->local;
+
+ if (*input_line_pointer == ',')
+ {
+ char *save = input_line_pointer;
+
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ /* For sparc. Accept .common symbol, length, "bss" */
+ input_line_pointer++;
+ /* Some use the dot, some don't. */
+ if (*input_line_pointer == '.')
+ input_line_pointer++;
+ /* Some say data, some say bss. */
+ if (strncmp (input_line_pointer, "bss\"", 4) == 0)
+ input_line_pointer += 4;
+ else if (strncmp (input_line_pointer, "data\"", 5) == 0)
+ input_line_pointer += 5;
+ else
+ {
+ char *p = input_line_pointer;
+ char c;
+
+ while (*--p != '"')
+ ;
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ if (*input_line_pointer++ == '"')
+ break;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+ as_bad (_("bad .common segment %s"), p);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return NULL;
+ }
+ /* ??? Don't ask me why these are always global. */
+ is_local = 0;
+ }
+ else
+ {
+ input_line_pointer = save;
+ align = parse_align (is_local);
+ if (align == (addressT) -1)
+ return NULL;
+ }
+ }
+
+ if (is_local)
+ {
+ bss_alloc (symbolP, size, align);
+ S_CLEAR_EXTERNAL (symbolP);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, size);
+ S_SET_ALIGN (symbolP, align);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, elf_com_section_ptr);
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+
+ return symbolP;
+}
+
+void
+obj_elf_common (int is_common)
+{
+ if (flag_mri && is_common)
+ s_mri_common (0);
+ else
+ s_comm_internal (0, elf_common_parse);
+}
+
+static void
+obj_elf_tls_common (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP = s_comm_internal (0, elf_common_parse);
+
+ if (symbolP)
+ symbol_get_bfdsym (symbolP)->flags |= BSF_THREAD_LOCAL;
+}
+
+static void
+obj_elf_lcomm (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP = s_comm_internal (0, s_lcomm_internal);
+
+ if (symbolP)
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+}
+
+static symbolS *
+get_sym_from_input_line_and_check (void)
+{
+ char *name;
+ char c;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ sym = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+
+ /* There is no symbol name if input_line_pointer has not moved. */
+ if (name == input_line_pointer)
+ as_bad (_("Missing symbol name in directive"));
+ return sym;
+}
+
+static void
+obj_elf_local (int ignore ATTRIBUTE_UNUSED)
+{
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ symbolP = get_sym_from_input_line_and_check ();
+ c = *input_line_pointer;
+ S_CLEAR_EXTERNAL (symbolP);
+ symbol_get_obj (symbolP)->local = 1;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_weak (int ignore ATTRIBUTE_UNUSED)
+{
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ symbolP = get_sym_from_input_line_and_check ();
+ c = *input_line_pointer;
+ S_SET_WEAK (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_visibility (int visibility)
+{
+ int c;
+ symbolS *symbolP;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ do
+ {
+ symbolP = get_sym_from_input_line_and_check ();
+
+ bfdsym = symbol_get_bfdsym (symbolP);
+ elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+
+ gas_assert (elfsym);
+
+ elfsym->internal_elf_sym.st_other &= ~3;
+ elfsym->internal_elf_sym.st_other |= visibility;
+
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer ++;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+static segT previous_section;
+static int previous_subsection;
+
+struct section_stack
+{
+ struct section_stack *next;
+ segT seg, prev_seg;
+ int subseg, prev_subseg;
+};
+
+static struct section_stack *section_stack;
+
+static bfd_boolean
+get_section (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+{
+ const char *gname = (const char *) inf;
+ const char *group_name = elf_group_name (sec);
+
+ return (group_name == gname
+ || (group_name != NULL
+ && gname != NULL
+ && strcmp (group_name, gname) == 0));
+}
+
+/* Handle the .section pseudo-op. This code supports two different
+ syntaxes.
+
+ The first is found on Solaris, and looks like
+ .section ".sec1",#alloc,#execinstr,#write
+ Here the names after '#' are the SHF_* flags to turn on for the
+ section. I'm not sure how it determines the SHT_* type (BFD
+ doesn't really give us control over the type, anyhow).
+
+ The second format is found on UnixWare, and probably most SVR4
+ machines, and looks like
+ .section .sec1,"a",@progbits
+ The quoted string may contain any combination of a, w, x, and
+ represents the SHF_* flags to turn on for the section. The string
+ beginning with '@' can be progbits or nobits. There should be
+ other possibilities, but I don't know what they are. In any case,
+ BFD doesn't really let us set the section type. */
+
+void
+obj_elf_change_section (const char *name,
+ int type,
+ bfd_vma attr,
+ int entsize,
+ const char *group_name,
+ int linkonce,
+ int push)
+{
+ asection *old_sec;
+ segT sec;
+ flagword flags;
+ const struct elf_backend_data *bed;
+ const struct bfd_elf_special_section *ssect;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ /* Switch to the section, creating it if necessary. */
+ if (push)
+ {
+ struct section_stack *elt;
+ elt = (struct section_stack *) xmalloc (sizeof (struct section_stack));
+ elt->next = section_stack;
+ elt->seg = now_seg;
+ elt->prev_seg = previous_section;
+ elt->subseg = now_subseg;
+ elt->prev_subseg = previous_subsection;
+ section_stack = elt;
+ }
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+
+ old_sec = bfd_get_section_by_name_if (stdoutput, name, get_section,
+ (void *) group_name);
+ if (old_sec)
+ {
+ sec = old_sec;
+ subseg_set (sec, 0);
+ }
+ else
+ sec = subseg_force_new (name, 0);
+
+ bed = get_elf_backend_data (stdoutput);
+ ssect = (*bed->get_sec_type_attr) (stdoutput, sec);
+
+ if (ssect != NULL)
+ {
+ bfd_boolean override = FALSE;
+
+ if (type == SHT_NULL)
+ type = ssect->type;
+ else if (type != ssect->type)
+ {
+ if (old_sec == NULL
+ /* Some older versions of gcc will emit
+
+ .section .init_array,"aw",@progbits
+
+ for __attribute__ ((section (".init_array"))).
+ "@progbits" is incorrect. Also for x86-64 large bss
+ sections, some older versions of gcc will emit
+
+ .section .lbss,"aw",@progbits
+
+ "@progbits" is incorrect. */
+#ifdef TC_I386
+ && (bed->s->arch_size != 64
+ || !(ssect->attr & SHF_X86_64_LARGE))
+#endif
+ && ssect->type != SHT_INIT_ARRAY
+ && ssect->type != SHT_FINI_ARRAY
+ && ssect->type != SHT_PREINIT_ARRAY)
+ {
+ /* We allow to specify any type for a .note section. */
+ if (ssect->type != SHT_NOTE)
+ as_warn (_("setting incorrect section type for %s"),
+ name);
+ }
+ else
+ {
+ as_warn (_("ignoring incorrect section type for %s"),
+ name);
+ type = ssect->type;
+ }
+ }
+
+ if (old_sec == NULL && (attr & ~ssect->attr) != 0)
+ {
+ /* As a GNU extension, we permit a .note section to be
+ allocatable. If the linker sees an allocatable .note
+ section, it will create a PT_NOTE segment in the output
+ file. We also allow "x" for .note.GNU-stack. */
+ if (ssect->type == SHT_NOTE
+ && (attr == SHF_ALLOC || attr == SHF_EXECINSTR))
+ ;
+ /* Allow different SHF_MERGE and SHF_STRINGS if we have
+ something like .rodata.str. */
+ else if (ssect->suffix_length == -2
+ && name[ssect->prefix_length] == '.'
+ && (attr
+ & ~ssect->attr
+ & ~SHF_MERGE
+ & ~SHF_STRINGS) == 0)
+ ;
+ /* .interp, .strtab and .symtab can have SHF_ALLOC. */
+ else if (attr == SHF_ALLOC
+ && (strcmp (name, ".interp") == 0
+ || strcmp (name, ".strtab") == 0
+ || strcmp (name, ".symtab") == 0))
+ override = TRUE;
+ /* .note.GNU-stack can have SHF_EXECINSTR. */
+ else if (attr == SHF_EXECINSTR
+ && strcmp (name, ".note.GNU-stack") == 0)
+ override = TRUE;
+#ifdef TC_ALPHA
+ /* A section on Alpha may have SHF_ALPHA_GPREL. */
+ else if ((attr & ~ssect->attr) == SHF_ALPHA_GPREL)
+ override = TRUE;
+#endif
+#ifdef TC_RX
+ else if (attr == (SHF_EXECINSTR | SHF_WRITE | SHF_ALLOC)
+ && (ssect->type == SHT_INIT_ARRAY
+ || ssect->type == SHT_FINI_ARRAY
+ || ssect->type == SHT_PREINIT_ARRAY))
+ /* RX init/fini arrays can and should have the "awx" attributes set. */
+ ;
+#endif
+ else
+ {
+ if (group_name == NULL)
+ as_warn (_("setting incorrect section attributes for %s"),
+ name);
+ override = TRUE;
+ }
+ }
+ if (!override && old_sec == NULL)
+ attr |= ssect->attr;
+ }
+
+ /* Convert ELF type and flags to BFD flags. */
+ flags = (SEC_RELOC
+ | ((attr & SHF_WRITE) ? 0 : SEC_READONLY)
+ | ((attr & SHF_ALLOC) ? SEC_ALLOC : 0)
+ | (((attr & SHF_ALLOC) && type != SHT_NOBITS) ? SEC_LOAD : 0)
+ | ((attr & SHF_EXECINSTR) ? SEC_CODE : 0)
+ | ((attr & SHF_MERGE) ? SEC_MERGE : 0)
+ | ((attr & SHF_STRINGS) ? SEC_STRINGS : 0)
+ | ((attr & SHF_EXCLUDE) ? SEC_EXCLUDE: 0)
+ | ((attr & SHF_TLS) ? SEC_THREAD_LOCAL : 0));
+#ifdef md_elf_section_flags
+ flags = md_elf_section_flags (flags, attr, type);
+#endif
+
+ if (linkonce)
+ flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
+
+ if (old_sec == NULL)
+ {
+ symbolS *secsym;
+
+ if (type == SHT_NULL)
+ type = bfd_elf_get_default_section_type (flags);
+ elf_section_type (sec) = type;
+ elf_section_flags (sec) = attr;
+
+ /* Prevent SEC_HAS_CONTENTS from being inadvertently set. */
+ if (type == SHT_NOBITS)
+ seg_info (sec)->bss = 1;
+
+ bfd_set_section_flags (stdoutput, sec, flags);
+ if (flags & SEC_MERGE)
+ sec->entsize = entsize;
+ elf_group_name (sec) = group_name;
+
+ /* Add a symbol for this section to the symbol table. */
+ secsym = symbol_find (name);
+ if (secsym != NULL)
+ symbol_set_bfdsym (secsym, sec->symbol);
+ else
+ symbol_table_insert (section_symbol (sec));
+ }
+ else
+ {
+ if (type != SHT_NULL
+ && (unsigned) type != elf_section_type (old_sec))
+ as_warn (_("ignoring changed section type for %s"), name);
+
+ if (attr != 0)
+ {
+ /* If section attributes are specified the second time we see a
+ particular section, then check that they are the same as we
+ saw the first time. */
+ if (((old_sec->flags ^ flags)
+ & (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE
+ | SEC_EXCLUDE | SEC_SORT_ENTRIES | SEC_MERGE | SEC_STRINGS
+ | SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD
+ | SEC_THREAD_LOCAL)))
+ as_warn (_("ignoring changed section attributes for %s"), name);
+ if ((flags & SEC_MERGE) && old_sec->entsize != (unsigned) entsize)
+ as_warn (_("ignoring changed section entity size for %s"), name);
+ }
+ }
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static bfd_vma
+obj_elf_parse_section_letters (char *str, size_t len, bfd_boolean *is_clone)
+{
+ bfd_vma attr = 0;
+ *is_clone = FALSE;
+
+ while (len > 0)
+ {
+ switch (*str)
+ {
+ case 'a':
+ attr |= SHF_ALLOC;
+ break;
+ case 'e':
+ attr |= SHF_EXCLUDE;
+ break;
+ case 'w':
+ attr |= SHF_WRITE;
+ break;
+ case 'x':
+ attr |= SHF_EXECINSTR;
+ break;
+ case 'M':
+ attr |= SHF_MERGE;
+ break;
+ case 'S':
+ attr |= SHF_STRINGS;
+ break;
+ case 'G':
+ attr |= SHF_GROUP;
+ break;
+ case 'T':
+ attr |= SHF_TLS;
+ break;
+ case '?':
+ *is_clone = TRUE;
+ break;
+ /* Compatibility. */
+ case 'm':
+ if (*(str - 1) == 'a')
+ {
+ attr |= SHF_MERGE;
+ if (len > 1 && str[1] == 's')
+ {
+ attr |= SHF_STRINGS;
+ str++, len--;
+ }
+ break;
+ }
+ default:
+ {
+ char *bad_msg = _("unrecognized .section attribute: want a,e,w,x,M,S,G,T");
+#ifdef md_elf_section_letter
+ bfd_vma md_attr = md_elf_section_letter (*str, &bad_msg);
+ if (md_attr != (bfd_vma) -1)
+ attr |= md_attr;
+ else
+#endif
+ as_fatal ("%s", bad_msg);
+ }
+ break;
+ }
+ str++, len--;
+ }
+
+ return attr;
+}
+
+static int
+obj_elf_section_type (char *str, size_t len, bfd_boolean warn)
+{
+ if (len == 8 && strncmp (str, "progbits", 8) == 0)
+ return SHT_PROGBITS;
+ if (len == 6 && strncmp (str, "nobits", 6) == 0)
+ return SHT_NOBITS;
+ if (len == 4 && strncmp (str, "note", 4) == 0)
+ return SHT_NOTE;
+ if (len == 10 && strncmp (str, "init_array", 10) == 0)
+ return SHT_INIT_ARRAY;
+ if (len == 10 && strncmp (str, "fini_array", 10) == 0)
+ return SHT_FINI_ARRAY;
+ if (len == 13 && strncmp (str, "preinit_array", 13) == 0)
+ return SHT_PREINIT_ARRAY;
+
+#ifdef md_elf_section_type
+ {
+ int md_type = md_elf_section_type (str, len);
+ if (md_type >= 0)
+ return md_type;
+ }
+#endif
+
+ if (warn)
+ as_warn (_("unrecognized section type"));
+ return 0;
+}
+
+static bfd_vma
+obj_elf_section_word (char *str, size_t len, int *type)
+{
+ int ret;
+
+ if (len == 5 && strncmp (str, "write", 5) == 0)
+ return SHF_WRITE;
+ if (len == 5 && strncmp (str, "alloc", 5) == 0)
+ return SHF_ALLOC;
+ if (len == 9 && strncmp (str, "execinstr", 9) == 0)
+ return SHF_EXECINSTR;
+ if (len == 7 && strncmp (str, "exclude", 7) == 0)
+ return SHF_EXCLUDE;
+ if (len == 3 && strncmp (str, "tls", 3) == 0)
+ return SHF_TLS;
+
+#ifdef md_elf_section_word
+ {
+ bfd_vma md_attr = md_elf_section_word (str, len);
+ if (md_attr > 0)
+ return md_attr;
+ }
+#endif
+
+ ret = obj_elf_section_type (str, len, FALSE);
+ if (ret != 0)
+ *type = ret;
+ else
+ as_warn (_("unrecognized section attribute"));
+
+ return 0;
+}
+
+/* Get name of section. */
+char *
+obj_elf_section_name (void)
+{
+ char *name;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ {
+ int dummy;
+
+ name = demand_copy_C_string (&dummy);
+ if (name == NULL)
+ {
+ ignore_rest_of_line ();
+ return NULL;
+ }
+ }
+ else
+ {
+ char *end = input_line_pointer;
+
+ while (0 == strchr ("\n\t,; ", *end))
+ end++;
+ if (end == input_line_pointer)
+ {
+ as_bad (_("missing name"));
+ ignore_rest_of_line ();
+ return NULL;
+ }
+
+ name = (char *) xmalloc (end - input_line_pointer + 1);
+ memcpy (name, input_line_pointer, end - input_line_pointer);
+ name[end - input_line_pointer] = '\0';
+#ifdef tc_canonicalize_section_name
+ name = tc_canonicalize_section_name (name);
+#endif
+ input_line_pointer = end;
+ }
+ SKIP_WHITESPACE ();
+ return name;
+}
+
+void
+obj_elf_section (int push)
+{
+ char *name, *group_name, *beg;
+ int type, dummy;
+ bfd_vma attr;
+ int entsize;
+ int linkonce;
+ subsegT new_subsection = -1;
+
+#ifndef TC_I370
+ if (flag_mri)
+ {
+ char mri_type;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+
+ s_mri_sect (&mri_type);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+
+ return;
+ }
+#endif /* ! defined (TC_I370) */
+
+ name = obj_elf_section_name ();
+ if (name == NULL)
+ return;
+ type = SHT_NULL;
+ attr = 0;
+ group_name = NULL;
+ entsize = 0;
+ linkonce = 0;
+
+ if (*input_line_pointer == ',')
+ {
+ /* Skip the comma. */
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (push && ISDIGIT (*input_line_pointer))
+ {
+ /* .pushsection has an optional subsection. */
+ new_subsection = (subsegT) get_absolute_expression ();
+
+ SKIP_WHITESPACE ();
+
+ /* Stop if we don't see a comma. */
+ if (*input_line_pointer != ',')
+ goto done;
+
+ /* Skip the comma. */
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ if (*input_line_pointer == '"')
+ {
+ bfd_boolean is_clone;
+
+ beg = demand_copy_C_string (&dummy);
+ if (beg == NULL)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+ attr |= obj_elf_parse_section_letters (beg, strlen (beg), &is_clone);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ char c;
+ char *save = input_line_pointer;
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (c == '"')
+ {
+ beg = demand_copy_C_string (&dummy);
+ if (beg == NULL)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+ type = obj_elf_section_type (beg, strlen (beg), TRUE);
+ }
+ else if (c == '@' || c == '%')
+ {
+ beg = ++input_line_pointer;
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+ type = obj_elf_section_type (beg, input_line_pointer - beg, TRUE);
+ }
+ else
+ input_line_pointer = save;
+ }
+
+ SKIP_WHITESPACE ();
+ if ((attr & SHF_MERGE) != 0 && *input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ entsize = get_absolute_expression ();
+ SKIP_WHITESPACE ();
+ if (entsize < 0)
+ {
+ as_warn (_("invalid merge entity size"));
+ attr &= ~SHF_MERGE;
+ entsize = 0;
+ }
+ }
+ else if ((attr & SHF_MERGE) != 0)
+ {
+ as_warn (_("entity size for SHF_MERGE not specified"));
+ attr &= ~SHF_MERGE;
+ }
+
+ if ((attr & SHF_GROUP) != 0 && is_clone)
+ {
+ as_warn (_("? section flag ignored with G present"));
+ is_clone = FALSE;
+ }
+ if ((attr & SHF_GROUP) != 0 && *input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ group_name = obj_elf_section_name ();
+ if (group_name == NULL)
+ attr &= ~SHF_GROUP;
+ else if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (strncmp (input_line_pointer, "comdat", 6) == 0)
+ {
+ input_line_pointer += 6;
+ linkonce = 1;
+ }
+ }
+ else if (strncmp (name, ".gnu.linkonce", 13) == 0)
+ linkonce = 1;
+ }
+ else if ((attr & SHF_GROUP) != 0)
+ {
+ as_warn (_("group name for SHF_GROUP not specified"));
+ attr &= ~SHF_GROUP;
+ }
+
+ if (is_clone)
+ {
+ const char *now_group = elf_group_name (now_seg);
+ if (now_group != NULL)
+ {
+ group_name = xstrdup (now_group);
+ linkonce = (now_seg->flags & SEC_LINK_ONCE) != 0;
+ }
+ }
+ }
+ else
+ {
+ do
+ {
+ char c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '#')
+ {
+ as_bad (_("character following name is not '#'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ beg = ++input_line_pointer;
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+
+ attr |= obj_elf_section_word (beg, input_line_pointer - beg, & type);
+
+ SKIP_WHITESPACE ();
+ }
+ while (*input_line_pointer++ == ',');
+ --input_line_pointer;
+ }
+ }
+
+done:
+ demand_empty_rest_of_line ();
+
+ obj_elf_change_section (name, type, attr, entsize, group_name, linkonce, push);
+
+ if (push && new_subsection != -1)
+ subseg_set (now_seg, new_subsection);
+}
+
+/* Change to the .data section. */
+
+void
+obj_elf_data (int i)
+{
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+ s_data (i);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+/* Change to the .text section. */
+
+void
+obj_elf_text (int i)
+{
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+ s_text (i);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+/* Change to the *ABS* section. */
+
+void
+obj_elf_struct (int i)
+{
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+ s_struct (i);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static void
+obj_elf_subsection (int ignore ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+
+ temp = get_absolute_expression ();
+ subseg_set (now_seg, (subsegT) temp);
+ demand_empty_rest_of_line ();
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+/* This can be called from the processor backends if they change
+ sections. */
+
+void
+obj_elf_section_change_hook (void)
+{
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+}
+
+void
+obj_elf_previous (int ignore ATTRIBUTE_UNUSED)
+{
+ segT new_section;
+ int new_subsection;
+
+ if (previous_section == 0)
+ {
+ as_warn (_(".previous without corresponding .section; ignored"));
+ return;
+ }
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ new_section = previous_section;
+ new_subsection = previous_subsection;
+ previous_section = now_seg;
+ previous_subsection = now_subseg;
+ subseg_set (new_section, new_subsection);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static void
+obj_elf_popsection (int xxx ATTRIBUTE_UNUSED)
+{
+ struct section_stack *top = section_stack;
+
+ if (top == NULL)
+ {
+ as_warn (_(".popsection without corresponding .pushsection; ignored"));
+ return;
+ }
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ section_stack = top->next;
+ previous_section = top->prev_seg;
+ previous_subsection = top->prev_subseg;
+ subseg_set (top->seg, top->subseg);
+ free (top);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static void
+obj_elf_line (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Assume delimiter is part of expression. BSD4.2 as fails with
+ delightful bug, so we are not being incompatible here. */
+ new_logical_line (NULL, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+/* This handles the .symver pseudo-op, which is used to specify a
+ symbol version. The syntax is ``.symver NAME,SYMVERNAME''.
+ SYMVERNAME may contain ELF_VER_CHR ('@') characters. This
+ pseudo-op causes the assembler to emit a symbol named SYMVERNAME
+ with the same value as the symbol NAME. */
+
+static void
+obj_elf_symver (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char old_lexat;
+ symbolS *sym;
+
+ sym = get_sym_from_input_line_and_check ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after name in .symver"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+
+ /* Temporarily include '@' in symbol names. */
+ old_lexat = lex_type[(unsigned char) '@'];
+ lex_type[(unsigned char) '@'] |= LEX_NAME;
+ c = get_symbol_end ();
+ lex_type[(unsigned char) '@'] = old_lexat;
+
+ if (symbol_get_obj (sym)->versioned_name == NULL)
+ {
+ symbol_get_obj (sym)->versioned_name = xstrdup (name);
+
+ *input_line_pointer = c;
+
+ if (strchr (symbol_get_obj (sym)->versioned_name,
+ ELF_VER_CHR) == NULL)
+ {
+ as_bad (_("missing version name in `%s' for symbol `%s'"),
+ symbol_get_obj (sym)->versioned_name,
+ S_GET_NAME (sym));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ {
+ if (strcmp (symbol_get_obj (sym)->versioned_name, name))
+ {
+ as_bad (_("multiple versions [`%s'|`%s'] for symbol `%s'"),
+ name, symbol_get_obj (sym)->versioned_name,
+ S_GET_NAME (sym));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = c;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* This handles the .vtable_inherit pseudo-op, which is used to indicate
+ to the linker the hierarchy in which a particular table resides. The
+ syntax is ".vtable_inherit CHILDNAME, PARENTNAME". */
+
+struct fix *
+obj_elf_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+{
+ char *cname, *pname;
+ symbolS *csym, *psym;
+ char c, bad = 0;
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ cname = input_line_pointer;
+ c = get_symbol_end ();
+ csym = symbol_find (cname);
+
+ /* GCFIXME: should check that we don't have two .vtable_inherits for
+ the same child symbol. Also, we can currently only do this if the
+ child symbol is already exists and is placed in a fragment. */
+
+ if (csym == NULL || symbol_get_frag (csym) == NULL)
+ {
+ as_bad (_("expected `%s' to have already been set for .vtable_inherit"),
+ cname);
+ bad = 1;
+ }
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after name in .vtable_inherit"));
+ ignore_rest_of_line ();
+ return NULL;
+ }
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ if (input_line_pointer[0] == '0'
+ && (input_line_pointer[1] == '\0'
+ || ISSPACE (input_line_pointer[1])))
+ {
+ psym = section_symbol (absolute_section);
+ ++input_line_pointer;
+ }
+ else
+ {
+ pname = input_line_pointer;
+ c = get_symbol_end ();
+ psym = symbol_find_or_make (pname);
+ *input_line_pointer = c;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (bad)
+ return NULL;
+
+ gas_assert (symbol_get_value_expression (csym)->X_op == O_constant);
+ return fix_new (symbol_get_frag (csym),
+ symbol_get_value_expression (csym)->X_add_number,
+ 0, psym, 0, 0, BFD_RELOC_VTABLE_INHERIT);
+}
+
+/* This handles the .vtable_entry pseudo-op, which is used to indicate
+ to the linker that a vtable slot was used. The syntax is
+ ".vtable_entry tablename, offset". */
+
+struct fix *
+obj_elf_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+ offsetT offset;
+
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ sym = get_sym_from_input_line_and_check ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after name in .vtable_entry"));
+ ignore_rest_of_line ();
+ return NULL;
+ }
+
+ ++input_line_pointer;
+ if (*input_line_pointer == '#')
+ ++input_line_pointer;
+
+ offset = get_absolute_expression ();
+
+ demand_empty_rest_of_line ();
+
+ return fix_new (frag_now, frag_now_fix (), 0, sym, offset, 0,
+ BFD_RELOC_VTABLE_ENTRY);
+}
+
+#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
+
+static inline int
+skip_past_char (char ** str, char c)
+{
+ if (**str == c)
+ {
+ (*str)++;
+ return 0;
+ }
+ else
+ return -1;
+}
+#define skip_past_comma(str) skip_past_char (str, ',')
+
+/* A list of attributes that have been explicitly set by the assembly code.
+ VENDOR is the vendor id, BASE is the tag shifted right by the number
+ of bits in MASK, and bit N of MASK is set if tag BASE+N has been set. */
+struct recorded_attribute_info {
+ struct recorded_attribute_info *next;
+ int vendor;
+ unsigned int base;
+ unsigned long mask;
+};
+static struct recorded_attribute_info *recorded_attributes;
+
+/* Record that we have seen an explicit specification of attribute TAG
+ for vendor VENDOR. */
+
+static void
+record_attribute (int vendor, unsigned int tag)
+{
+ unsigned int base;
+ unsigned long mask;
+ struct recorded_attribute_info *rai;
+
+ base = tag / (8 * sizeof (rai->mask));
+ mask = 1UL << (tag % (8 * sizeof (rai->mask)));
+ for (rai = recorded_attributes; rai; rai = rai->next)
+ if (rai->vendor == vendor && rai->base == base)
+ {
+ rai->mask |= mask;
+ return;
+ }
+
+ rai = XNEW (struct recorded_attribute_info);
+ rai->next = recorded_attributes;
+ rai->vendor = vendor;
+ rai->base = base;
+ rai->mask = mask;
+ recorded_attributes = rai;
+}
+
+/* Return true if we have seen an explicit specification of attribute TAG
+ for vendor VENDOR. */
+
+bfd_boolean
+obj_elf_seen_attribute (int vendor, unsigned int tag)
+{
+ unsigned int base;
+ unsigned long mask;
+ struct recorded_attribute_info *rai;
+
+ base = tag / (8 * sizeof (rai->mask));
+ mask = 1UL << (tag % (8 * sizeof (rai->mask)));
+ for (rai = recorded_attributes; rai; rai = rai->next)
+ if (rai->vendor == vendor && rai->base == base)
+ return (rai->mask & mask) != 0;
+ return FALSE;
+}
+
+/* Parse an attribute directive for VENDOR.
+ Returns the attribute number read, or zero on error. */
+
+int
+obj_elf_vendor_attribute (int vendor)
+{
+ expressionS exp;
+ int type;
+ int tag;
+ unsigned int i = 0;
+ char *s = NULL;
+
+ /* Read the first number or name. */
+ skip_whitespace (input_line_pointer);
+ s = input_line_pointer;
+ if (ISDIGIT (*input_line_pointer))
+ {
+ expression (& exp);
+ if (exp.X_op != O_constant)
+ goto bad;
+ tag = exp.X_add_number;
+ }
+ else
+ {
+ char *name;
+
+ /* A name may contain '_', but no other punctuation. */
+ for (; ISALNUM (*input_line_pointer) || *input_line_pointer == '_';
+ ++input_line_pointer)
+ i++;
+ if (i == 0)
+ goto bad;
+
+ name = (char *) alloca (i + 1);
+ memcpy (name, s, i);
+ name[i] = '\0';
+
+#ifndef CONVERT_SYMBOLIC_ATTRIBUTE
+#define CONVERT_SYMBOLIC_ATTRIBUTE(a) -1
+#endif
+
+ tag = CONVERT_SYMBOLIC_ATTRIBUTE (name);
+ if (tag == -1)
+ {
+ as_bad (_("Attribute name not recognised: %s"), name);
+ ignore_rest_of_line ();
+ return 0;
+ }
+ }
+
+ type = _bfd_elf_obj_attrs_arg_type (stdoutput, vendor, tag);
+
+ if (skip_past_comma (&input_line_pointer) == -1)
+ goto bad;
+ if (type & 1)
+ {
+ expression (& exp);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected numeric constant"));
+ ignore_rest_of_line ();
+ return 0;
+ }
+ i = exp.X_add_number;
+ }
+ if ((type & 3) == 3
+ && skip_past_comma (&input_line_pointer) == -1)
+ {
+ as_bad (_("expected comma"));
+ ignore_rest_of_line ();
+ return 0;
+ }
+ if (type & 2)
+ {
+ int len;
+
+ skip_whitespace (input_line_pointer);
+ if (*input_line_pointer != '"')
+ goto bad_string;
+ s = demand_copy_C_string (&len);
+ }
+
+ record_attribute (vendor, tag);
+ switch (type & 3)
+ {
+ case 3:
+ bfd_elf_add_obj_attr_int_string (stdoutput, vendor, tag, i, s);
+ break;
+ case 2:
+ bfd_elf_add_obj_attr_string (stdoutput, vendor, tag, s);
+ break;
+ case 1:
+ bfd_elf_add_obj_attr_int (stdoutput, vendor, tag, i);
+ break;
+ default:
+ abort ();
+ }
+
+ demand_empty_rest_of_line ();
+ return tag;
+bad_string:
+ as_bad (_("bad string constant"));
+ ignore_rest_of_line ();
+ return 0;
+bad:
+ as_bad (_("expected <tag> , <value>"));
+ ignore_rest_of_line ();
+ return 0;
+}
+
+/* Parse a .gnu_attribute directive. */
+
+static void
+obj_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ obj_elf_vendor_attribute (OBJ_ATTR_GNU);
+}
+
+void
+elf_obj_read_begin_hook (void)
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_read_begin_hook ();
+#endif
+}
+
+void
+elf_obj_symbol_new_hook (symbolS *symbolP)
+{
+ struct elf_obj_sy *sy_obj;
+
+ sy_obj = symbol_get_obj (symbolP);
+ sy_obj->size = NULL;
+ sy_obj->versioned_name = NULL;
+
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_symbol_new_hook (symbolP);
+#endif
+}
+
+/* When setting one symbol equal to another, by default we probably
+ want them to have the same "size", whatever it means in the current
+ context. */
+
+void
+elf_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ struct elf_obj_sy *srcelf = symbol_get_obj (src);
+ struct elf_obj_sy *destelf = symbol_get_obj (dest);
+ if (srcelf->size)
+ {
+ if (destelf->size == NULL)
+ destelf->size = (expressionS *) xmalloc (sizeof (expressionS));
+ *destelf->size = *srcelf->size;
+ }
+ else
+ {
+ if (destelf->size != NULL)
+ free (destelf->size);
+ destelf->size = NULL;
+ }
+ S_SET_SIZE (dest, S_GET_SIZE (src));
+ /* Don't copy visibility. */
+ S_SET_OTHER (dest, (ELF_ST_VISIBILITY (S_GET_OTHER (dest))
+ | (S_GET_OTHER (src) & ~ELF_ST_VISIBILITY (-1))));
+}
+
+void
+obj_elf_version (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ unsigned int c;
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ Elf_Internal_Note i_note;
+ Elf_External_Note e_note;
+ asection *note_secp = NULL;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ unsigned int len;
+
+ ++input_line_pointer; /* -> 1st char of string. */
+ name = input_line_pointer;
+
+ while (is_a_char (c = next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+ *(input_line_pointer - 1) = '\0';
+ *input_line_pointer = c;
+
+ /* Create the .note section. */
+ note_secp = subseg_new (".note", 0);
+ bfd_set_section_flags (stdoutput,
+ note_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* Process the version string. */
+ len = strlen (name) + 1;
+
+ /* PR 3456: Although the name field is padded out to an 4-byte
+ boundary, the namesz field should not be adjusted. */
+ i_note.namesz = len;
+ i_note.descsz = 0; /* No description. */
+ i_note.type = NT_VERSION;
+ p = frag_more (sizeof (e_note.namesz));
+ md_number_to_chars (p, i_note.namesz, sizeof (e_note.namesz));
+ p = frag_more (sizeof (e_note.descsz));
+ md_number_to_chars (p, i_note.descsz, sizeof (e_note.descsz));
+ p = frag_more (sizeof (e_note.type));
+ md_number_to_chars (p, i_note.type, sizeof (e_note.type));
+ p = frag_more (len);
+ memcpy (p, name, len);
+
+ frag_align (2, 0, 0);
+
+ subseg_set (seg, subseg);
+ }
+ else
+ as_bad (_("expected quoted string"));
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_size (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer;
+ char c = get_symbol_end ();
+ char *p;
+ expressionS exp;
+ symbolS *sym;
+
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_bad (_("expected comma after name `%s' in .size directive"), name);
+ *p = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ expression (&exp);
+ if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing expression in .size directive"));
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ }
+ *p = 0;
+ sym = symbol_find_or_make (name);
+ *p = c;
+ if (exp.X_op == O_constant)
+ {
+ S_SET_SIZE (sym, exp.X_add_number);
+ if (symbol_get_obj (sym)->size)
+ {
+ xfree (symbol_get_obj (sym)->size);
+ symbol_get_obj (sym)->size = NULL;
+ }
+ }
+ else
+ {
+ symbol_get_obj (sym)->size =
+ (expressionS *) xmalloc (sizeof (expressionS));
+ *symbol_get_obj (sym)->size = exp;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the ELF .type pseudo-op. This sets the type of a symbol.
+ There are six syntaxes:
+
+ The first (used on Solaris) is
+ .type SYM,#function
+ The second (used on UnixWare) is
+ .type SYM,@function
+ The third (reportedly to be used on Irix 6.0) is
+ .type SYM STT_FUNC
+ The fourth (used on NetBSD/Arm and Linux/ARM) is
+ .type SYM,%function
+ The fifth (used on SVR4/860) is
+ .type SYM,"function"
+ The sixth (emitted by recent SunPRO under Solaris) is
+ .type SYM,[0-9]
+ where the integer is the STT_* value.
+ */
+
+static char *
+obj_elf_type_name (char *cp)
+{
+ char *p;
+
+ p = input_line_pointer;
+ if (*input_line_pointer >= '0'
+ && *input_line_pointer <= '9')
+ {
+ while (*input_line_pointer >= '0'
+ && *input_line_pointer <= '9')
+ ++input_line_pointer;
+ *cp = *input_line_pointer;
+ *input_line_pointer = '\0';
+ }
+ else
+ *cp = get_symbol_end ();
+
+ return p;
+}
+
+static void
+obj_elf_type (int ignore ATTRIBUTE_UNUSED)
+{
+ char c;
+ int type;
+ const char *type_name;
+ symbolS *sym;
+ elf_symbol_type *elfsym;
+
+ sym = get_sym_from_input_line_and_check ();
+ c = *input_line_pointer;
+ elfsym = (elf_symbol_type *) symbol_get_bfdsym (sym);
+
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE ();
+ if ( *input_line_pointer == '#'
+ || *input_line_pointer == '@'
+ || *input_line_pointer == '"'
+ || *input_line_pointer == '%')
+ ++input_line_pointer;
+
+ type_name = obj_elf_type_name (& c);
+
+ type = 0;
+ if (strcmp (type_name, "function") == 0
+ || strcmp (type_name, "2") == 0
+ || strcmp (type_name, "STT_FUNC") == 0)
+ type = BSF_FUNCTION;
+ else if (strcmp (type_name, "object") == 0
+ || strcmp (type_name, "1") == 0
+ || strcmp (type_name, "STT_OBJECT") == 0)
+ type = BSF_OBJECT;
+ else if (strcmp (type_name, "tls_object") == 0
+ || strcmp (type_name, "6") == 0
+ || strcmp (type_name, "STT_TLS") == 0)
+ type = BSF_OBJECT | BSF_THREAD_LOCAL;
+ else if (strcmp (type_name, "notype") == 0
+ || strcmp (type_name, "0") == 0
+ || strcmp (type_name, "STT_NOTYPE") == 0)
+ ;
+ else if (strcmp (type_name, "common") == 0
+ || strcmp (type_name, "5") == 0
+ || strcmp (type_name, "STT_COMMON") == 0)
+ {
+ type = BSF_OBJECT;
+
+ if (! S_IS_COMMON (sym))
+ {
+ if (S_IS_VOLATILE (sym))
+ {
+ sym = symbol_clone (sym, 1);
+ S_SET_SEGMENT (sym, bfd_com_section_ptr);
+ S_SET_VALUE (sym, 0);
+ S_SET_EXTERNAL (sym);
+ symbol_set_frag (sym, &zero_address_frag);
+ S_CLEAR_VOLATILE (sym);
+ }
+ else if (S_IS_DEFINED (sym) || symbol_equated_p (sym))
+ as_bad (_("symbol '%s' is already defined"), S_GET_NAME (sym));
+ else
+ {
+ /* FIXME: Is it safe to just change the section ? */
+ S_SET_SEGMENT (sym, bfd_com_section_ptr);
+ S_SET_VALUE (sym, 0);
+ S_SET_EXTERNAL (sym);
+ }
+ }
+ }
+ else if (strcmp (type_name, "gnu_indirect_function") == 0
+ || strcmp (type_name, "10") == 0
+ || strcmp (type_name, "STT_GNU_IFUNC") == 0)
+ {
+ const struct elf_backend_data *bed;
+
+ bed = get_elf_backend_data (stdoutput);
+ if (!(bed->elf_osabi == ELFOSABI_GNU
+ || bed->elf_osabi == ELFOSABI_FREEBSD
+ /* GNU is still using the default value 0. */
+ || bed->elf_osabi == ELFOSABI_NONE))
+ as_bad (_("symbol type \"%s\" is supported only by GNU and FreeBSD targets"),
+ type_name);
+ type = BSF_FUNCTION | BSF_GNU_INDIRECT_FUNCTION;
+ }
+ else if (strcmp (type_name, "gnu_unique_object") == 0)
+ {
+ struct elf_backend_data *bed;
+
+ bed = (struct elf_backend_data *) get_elf_backend_data (stdoutput);
+ if (!(bed->elf_osabi == ELFOSABI_GNU
+ /* GNU is still using the default value 0. */
+ || bed->elf_osabi == ELFOSABI_NONE))
+ as_bad (_("symbol type \"%s\" is supported only by GNU targets"),
+ type_name);
+ type = BSF_OBJECT | BSF_GNU_UNIQUE;
+ /* PR 10549: Always set OSABI field to GNU for objects containing unique symbols. */
+ bed->elf_osabi = ELFOSABI_GNU;
+ }
+#ifdef md_elf_symbol_type
+ else if ((type = md_elf_symbol_type (type_name, sym, elfsym)) != -1)
+ ;
+#endif
+ else
+ as_bad (_("unrecognized symbol type \"%s\""), type_name);
+
+ *input_line_pointer = c;
+
+ if (*input_line_pointer == '"')
+ ++input_line_pointer;
+
+ elfsym->symbol.flags |= type;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_elf_ident (int ignore ATTRIBUTE_UNUSED)
+{
+ static segT comment_section;
+ segT old_section = now_seg;
+ int old_subsection = now_subseg;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (!comment_section)
+ {
+ char *p;
+ comment_section = subseg_new (".comment", 0);
+ bfd_set_section_flags (stdoutput, comment_section,
+ SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_MERGE | SEC_STRINGS);
+ comment_section->entsize = 1;
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+ p = frag_more (1);
+ *p = 0;
+ }
+ else
+ subseg_set (comment_section, 0);
+ stringer (8 + 1);
+ subseg_set (old_section, old_subsection);
+}
+
+#ifdef INIT_STAB_SECTION
+
+/* The first entry in a .stabs section is special. */
+
+void
+obj_elf_init_stab_section (segT seg)
+{
+ char *file;
+ char *p;
+ char *stabstr_name;
+ unsigned int stroff;
+
+ /* Force the section to align to a longword boundary. Without this,
+ UnixWare ar crashes. */
+ bfd_set_section_alignment (stdoutput, seg, 2);
+
+ /* Make space for this first symbol. */
+ p = frag_more (12);
+ /* Zero it out. */
+ memset (p, 0, 12);
+ as_where (&file, NULL);
+ stabstr_name = (char *) xmalloc (strlen (segment_name (seg)) + 4);
+ strcpy (stabstr_name, segment_name (seg));
+ strcat (stabstr_name, "str");
+ stroff = get_stab_string_offset (file, stabstr_name);
+ know (stroff == 1 || (stroff == 0 && file[0] == '\0'));
+ md_number_to_chars (p, stroff, 4);
+ seg_info (seg)->stabu.p = p;
+}
+
+#endif
+
+/* Fill in the counts in the first entry in a .stabs section. */
+
+static void
+adjust_stab_sections (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+ char *name;
+ asection *strsec;
+ char *p;
+ int strsz, nsyms;
+
+ if (strncmp (".stab", sec->name, 5))
+ return;
+ if (!strcmp ("str", sec->name + strlen (sec->name) - 3))
+ return;
+
+ name = (char *) alloca (strlen (sec->name) + 4);
+ strcpy (name, sec->name);
+ strcat (name, "str");
+ strsec = bfd_get_section_by_name (abfd, name);
+ if (strsec)
+ strsz = bfd_section_size (abfd, strsec);
+ else
+ strsz = 0;
+ nsyms = bfd_section_size (abfd, sec) / 12 - 1;
+
+ p = seg_info (sec)->stabu.p;
+ gas_assert (p != 0);
+
+ bfd_h_put_16 (abfd, nsyms, p + 6);
+ bfd_h_put_32 (abfd, strsz, p + 8);
+}
+
+#ifdef NEED_ECOFF_DEBUG
+
+/* This function is called by the ECOFF code. It is supposed to
+ record the external symbol information so that the backend can
+ write it out correctly. The ELF backend doesn't actually handle
+ this at the moment, so we do it ourselves. We save the information
+ in the symbol. */
+
+#ifdef OBJ_MAYBE_ELF
+static
+#endif
+void
+elf_ecoff_set_ext (symbolS *sym, struct ecoff_extr *ext)
+{
+ symbol_get_bfdsym (sym)->udata.p = ext;
+}
+
+/* This function is called by bfd_ecoff_debug_externals. It is
+ supposed to *EXT to the external symbol information, and return
+ whether the symbol should be used at all. */
+
+static bfd_boolean
+elf_get_extr (asymbol *sym, EXTR *ext)
+{
+ if (sym->udata.p == NULL)
+ return FALSE;
+ *ext = *(EXTR *) sym->udata.p;
+ return TRUE;
+}
+
+/* This function is called by bfd_ecoff_debug_externals. It has
+ nothing to do for ELF. */
+
+static void
+elf_set_index (asymbol *sym ATTRIBUTE_UNUSED,
+ bfd_size_type indx ATTRIBUTE_UNUSED)
+{
+}
+
+#endif /* NEED_ECOFF_DEBUG */
+
+void
+elf_frob_symbol (symbolS *symp, int *puntp)
+{
+ struct elf_obj_sy *sy_obj;
+ expressionS *size;
+
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_frob_symbol (symp);
+#endif
+
+ sy_obj = symbol_get_obj (symp);
+
+ size = sy_obj->size;
+ if (size != NULL)
+ {
+ if (resolve_expression (size)
+ && size->X_op == O_constant)
+ S_SET_SIZE (symp, size->X_add_number);
+ else
+ {
+ if (flag_size_check == size_check_error)
+ as_bad (_(".size expression for %s "
+ "does not evaluate to a constant"), S_GET_NAME (symp));
+ else
+ as_warn (_(".size expression for %s "
+ "does not evaluate to a constant"), S_GET_NAME (symp));
+ }
+ free (sy_obj->size);
+ sy_obj->size = NULL;
+ }
+
+ if (sy_obj->versioned_name != NULL)
+ {
+ char *p;
+
+ p = strchr (sy_obj->versioned_name, ELF_VER_CHR);
+ if (p == NULL)
+ /* We will have already reported an error about a missing version. */
+ *puntp = TRUE;
+
+ /* This symbol was given a new name with the .symver directive.
+
+ If this is an external reference, just rename the symbol to
+ include the version string. This will make the relocs be
+ against the correct versioned symbol.
+
+ If this is a definition, add an alias. FIXME: Using an alias
+ will permit the debugging information to refer to the right
+ symbol. However, it's not clear whether it is the best
+ approach. */
+
+ else if (! S_IS_DEFINED (symp))
+ {
+ /* Verify that the name isn't using the @@ syntax--this is
+ reserved for definitions of the default version to link
+ against. */
+ if (p[1] == ELF_VER_CHR)
+ {
+ as_bad (_("invalid attempt to declare external version name"
+ " as default in symbol `%s'"),
+ sy_obj->versioned_name);
+ *puntp = TRUE;
+ }
+ S_SET_NAME (symp, sy_obj->versioned_name);
+ }
+ else
+ {
+ if (p[1] == ELF_VER_CHR && p[2] == ELF_VER_CHR)
+ {
+ size_t l;
+
+ /* The @@@ syntax is a special case. It renames the
+ symbol name to versioned_name with one `@' removed. */
+ l = strlen (&p[3]) + 1;
+ memmove (&p[2], &p[3], l);
+ S_SET_NAME (symp, sy_obj->versioned_name);
+ }
+ else
+ {
+ symbolS *symp2;
+
+ /* FIXME: Creating a new symbol here is risky. We're
+ in the final loop over the symbol table. We can
+ get away with it only because the symbol goes to
+ the end of the list, where the loop will still see
+ it. It would probably be better to do this in
+ obj_frob_file_before_adjust. */
+
+ symp2 = symbol_find_or_make (sy_obj->versioned_name);
+
+ /* Now we act as though we saw symp2 = sym. */
+
+ S_SET_SEGMENT (symp2, S_GET_SEGMENT (symp));
+
+ /* Subtracting out the frag address here is a hack
+ because we are in the middle of the final loop. */
+ S_SET_VALUE (symp2,
+ (S_GET_VALUE (symp)
+ - symbol_get_frag (symp)->fr_address));
+
+ symbol_set_frag (symp2, symbol_get_frag (symp));
+
+ /* This will copy over the size information. */
+ copy_symbol_attributes (symp2, symp);
+
+ S_SET_OTHER (symp2, S_GET_OTHER (symp));
+
+ if (S_IS_WEAK (symp))
+ S_SET_WEAK (symp2);
+
+ if (S_IS_EXTERNAL (symp))
+ S_SET_EXTERNAL (symp2);
+ }
+ }
+ }
+
+ /* Double check weak symbols. */
+ if (S_IS_WEAK (symp))
+ {
+ if (S_IS_COMMON (symp))
+ as_bad (_("symbol `%s' can not be both weak and common"),
+ S_GET_NAME (symp));
+ }
+
+#ifdef TC_MIPS
+ /* The Irix 5 and 6 assemblers set the type of any common symbol and
+ any undefined non-function symbol to STT_OBJECT. We try to be
+ compatible, since newer Irix 5 and 6 linkers care. However, we
+ only set undefined symbols to be STT_OBJECT if we are on Irix,
+ because that is the only time gcc will generate the necessary
+ .global directives to mark functions. */
+
+ if (S_IS_COMMON (symp))
+ symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
+
+ if (strstr (TARGET_OS, "irix") != NULL
+ && ! S_IS_DEFINED (symp)
+ && (symbol_get_bfdsym (symp)->flags & BSF_FUNCTION) == 0)
+ symbol_get_bfdsym (symp)->flags |= BSF_OBJECT;
+#endif
+}
+
+struct group_list
+{
+ asection **head; /* Section lists. */
+ unsigned int *elt_count; /* Number of sections in each list. */
+ unsigned int num_group; /* Number of lists. */
+ struct hash_control *indexes; /* Maps group name to index in head array. */
+};
+
+/* Called via bfd_map_over_sections. If SEC is a member of a group,
+ add it to a list of sections belonging to the group. INF is a
+ pointer to a struct group_list, which is where we store the head of
+ each list. */
+
+static void
+build_group_lists (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+{
+ struct group_list *list = (struct group_list *) inf;
+ const char *group_name = elf_group_name (sec);
+ unsigned int i;
+ unsigned int *elem_idx;
+ unsigned int *idx_ptr;
+
+ if (group_name == NULL)
+ return;
+
+ /* If this group already has a list, add the section to the head of
+ the list. */
+ elem_idx = (unsigned int *) hash_find (list->indexes, group_name);
+ if (elem_idx != NULL)
+ {
+ elf_next_in_group (sec) = list->head[*elem_idx];
+ list->head[*elem_idx] = sec;
+ list->elt_count[*elem_idx] += 1;
+ return;
+ }
+
+ /* New group. Make the arrays bigger in chunks to minimize calls to
+ realloc. */
+ i = list->num_group;
+ if ((i & 127) == 0)
+ {
+ unsigned int newsize = i + 128;
+ list->head = (asection **) xrealloc (list->head,
+ newsize * sizeof (*list->head));
+ list->elt_count = (unsigned int *)
+ xrealloc (list->elt_count, newsize * sizeof (*list->elt_count));
+ }
+ list->head[i] = sec;
+ list->elt_count[i] = 1;
+ list->num_group += 1;
+
+ /* Add index to hash. */
+ idx_ptr = (unsigned int *) xmalloc (sizeof (unsigned int));
+ *idx_ptr = i;
+ hash_insert (list->indexes, group_name, idx_ptr);
+}
+
+static void free_section_idx (const char *key ATTRIBUTE_UNUSED, void *val)
+{
+ free ((unsigned int *) val);
+}
+
+void
+elf_adjust_symtab (void)
+{
+ struct group_list list;
+ unsigned int i;
+
+ /* Go find section groups. */
+ list.num_group = 0;
+ list.head = NULL;
+ list.elt_count = NULL;
+ list.indexes = hash_new ();
+ bfd_map_over_sections (stdoutput, build_group_lists, &list);
+
+ /* Make the SHT_GROUP sections that describe each section group. We
+ can't set up the section contents here yet, because elf section
+ indices have yet to be calculated. elf.c:set_group_contents does
+ the rest of the work. */
+ for (i = 0; i < list.num_group; i++)
+ {
+ const char *group_name = elf_group_name (list.head[i]);
+ const char *sec_name;
+ asection *s;
+ flagword flags;
+ struct symbol *sy;
+ bfd_size_type size;
+
+ flags = SEC_READONLY | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_GROUP;
+ for (s = list.head[i]; s != NULL; s = elf_next_in_group (s))
+ if ((s->flags ^ flags) & SEC_LINK_ONCE)
+ {
+ flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
+ if (s != list.head[i])
+ {
+ as_warn (_("assuming all members of group `%s' are COMDAT"),
+ group_name);
+ break;
+ }
+ }
+
+ sec_name = ".group";
+ s = subseg_force_new (sec_name, 0);
+ if (s == NULL
+ || !bfd_set_section_flags (stdoutput, s, flags)
+ || !bfd_set_section_alignment (stdoutput, s, 2))
+ {
+ as_fatal (_("can't create group: %s"),
+ bfd_errmsg (bfd_get_error ()));
+ }
+ elf_section_type (s) = SHT_GROUP;
+
+ /* Pass a pointer to the first section in this group. */
+ elf_next_in_group (s) = list.head[i];
+ /* Make sure that the signature symbol for the group has the
+ name of the group. */
+ sy = symbol_find_exact (group_name);
+ if (!sy
+ || (sy != symbol_lastP
+ && (sy->sy_next == NULL
+ || sy->sy_next->sy_previous != sy)))
+ {
+ /* Create the symbol now. */
+ sy = symbol_new (group_name, now_seg, (valueT) 0, frag_now);
+#ifdef TE_SOLARIS
+ /* Before Solaris 11 build 154, Sun ld rejects local group
+ signature symbols, so make them weak hidden instead. */
+ symbol_get_bfdsym (sy)->flags |= BSF_WEAK;
+ S_SET_OTHER (sy, STV_HIDDEN);
+#else
+ symbol_get_obj (sy)->local = 1;
+#endif
+ symbol_table_insert (sy);
+ }
+ elf_group_id (s) = symbol_get_bfdsym (sy);
+
+ size = 4 * (list.elt_count[i] + 1);
+ bfd_set_section_size (stdoutput, s, size);
+ s->contents = (unsigned char *) frag_more (size);
+ frag_now->fr_fix = frag_now_fix_octets ();
+ frag_wane (frag_now);
+ }
+
+ /* Cleanup hash. */
+ hash_traverse (list.indexes, free_section_idx);
+ hash_die (list.indexes);
+}
+
+void
+elf_frob_file (void)
+{
+ bfd_map_over_sections (stdoutput, adjust_stab_sections, NULL);
+
+#ifdef elf_tc_final_processing
+ elf_tc_final_processing ();
+#endif
+}
+
+/* It removes any unneeded versioned symbols from the symbol table. */
+
+void
+elf_frob_file_before_adjust (void)
+{
+ if (symbol_rootP)
+ {
+ symbolS *symp;
+
+ for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+ if (!S_IS_DEFINED (symp))
+ {
+ if (symbol_get_obj (symp)->versioned_name)
+ {
+ char *p;
+
+ /* The @@@ syntax is a special case. If the symbol is
+ not defined, 2 `@'s will be removed from the
+ versioned_name. */
+
+ p = strchr (symbol_get_obj (symp)->versioned_name,
+ ELF_VER_CHR);
+ if (p != NULL && p[1] == ELF_VER_CHR && p[2] == ELF_VER_CHR)
+ {
+ size_t l = strlen (&p[3]) + 1;
+ memmove (&p[1], &p[3], l);
+ }
+ if (symbol_used_p (symp) == 0
+ && symbol_used_in_reloc_p (symp) == 0)
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
+
+ /* If there was .weak foo, but foo was neither defined nor
+ used anywhere, remove it. */
+
+ else if (S_IS_WEAK (symp)
+ && symbol_used_p (symp) == 0
+ && symbol_used_in_reloc_p (symp) == 0)
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
+ }
+}
+
+/* It is required that we let write_relocs have the opportunity to
+ optimize away fixups before output has begun, since it is possible
+ to eliminate all fixups for a section and thus we never should
+ have generated the relocation section. */
+
+void
+elf_frob_file_after_relocs (void)
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ /* Generate the ECOFF debugging information. */
+ {
+ const struct ecoff_debug_swap *debug_swap;
+ struct ecoff_debug_info debug;
+ char *buf;
+ asection *sec;
+
+ debug_swap
+ = get_elf_backend_data (stdoutput)->elf_backend_ecoff_debug_swap;
+ know (debug_swap != NULL);
+ ecoff_build_debug (&debug.symbolic_header, &buf, debug_swap);
+
+ /* Set up the pointers in debug. */
+#define SET(ptr, offset, type) \
+ debug.ptr = (type) (buf + debug.symbolic_header.offset)
+
+ SET (line, cbLineOffset, unsigned char *);
+ SET (external_dnr, cbDnOffset, void *);
+ SET (external_pdr, cbPdOffset, void *);
+ SET (external_sym, cbSymOffset, void *);
+ SET (external_opt, cbOptOffset, void *);
+ SET (external_aux, cbAuxOffset, union aux_ext *);
+ SET (ss, cbSsOffset, char *);
+ SET (external_fdr, cbFdOffset, void *);
+ SET (external_rfd, cbRfdOffset, void *);
+ /* ssext and external_ext are set up just below. */
+
+#undef SET
+
+ /* Set up the external symbols. */
+ debug.ssext = debug.ssext_end = NULL;
+ debug.external_ext = debug.external_ext_end = NULL;
+ if (! bfd_ecoff_debug_externals (stdoutput, &debug, debug_swap, TRUE,
+ elf_get_extr, elf_set_index))
+ as_fatal (_("failed to set up debugging information: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ sec = bfd_get_section_by_name (stdoutput, ".mdebug");
+ gas_assert (sec != NULL);
+
+ know (!stdoutput->output_has_begun);
+
+ /* We set the size of the section, call bfd_set_section_contents
+ to force the ELF backend to allocate a file position, and then
+ write out the data. FIXME: Is this really the best way to do
+ this? */
+ bfd_set_section_size
+ (stdoutput, sec, bfd_ecoff_debug_size (stdoutput, &debug, debug_swap));
+
+ /* Pass BUF to bfd_set_section_contents because this will
+ eventually become a call to fwrite, and ISO C prohibits
+ passing a NULL pointer to a stdio function even if the
+ pointer will not be used. */
+ if (! bfd_set_section_contents (stdoutput, sec, buf, 0, 0))
+ as_fatal (_("can't start writing .mdebug section: %s"),
+ bfd_errmsg (bfd_get_error ()));
+
+ know (stdoutput->output_has_begun);
+ know (sec->filepos != 0);
+
+ if (! bfd_ecoff_write_debug (stdoutput, &debug, debug_swap,
+ sec->filepos))
+ as_fatal (_("could not write .mdebug section: %s"),
+ bfd_errmsg (bfd_get_error ()));
+ }
+#endif /* NEED_ECOFF_DEBUG */
+}
+
+#ifdef SCO_ELF
+
+/* Heavily plagiarized from obj_elf_version. The idea is to emit the
+ SCO specific identifier in the .notes section to satisfy the SCO
+ linker.
+
+ This looks more complicated than it really is. As opposed to the
+ "obvious" solution, this should handle the cross dev cases
+ correctly. (i.e, hosting on a 64 bit big endian processor, but
+ generating SCO Elf code) Efficiency isn't a concern, as there
+ should be exactly one of these sections per object module.
+
+ SCO OpenServer 5 identifies it's ELF modules with a standard ELF
+ .note section.
+
+ int_32 namesz = 4 ; Name size
+ int_32 descsz = 12 ; Descriptive information
+ int_32 type = 1 ;
+ char name[4] = "SCO" ; Originator name ALWAYS SCO + NULL
+ int_32 version = (major ver # << 16) | version of tools ;
+ int_32 source = (tool_id << 16 ) | 1 ;
+ int_32 info = 0 ; These are set by the SCO tools, but we
+ don't know enough about the source
+ environment to set them. SCO ld currently
+ ignores them, and recommends we set them
+ to zero. */
+
+#define SCO_MAJOR_VERSION 0x1
+#define SCO_MINOR_VERSION 0x1
+
+void
+sco_id (void)
+{
+
+ char *name;
+ unsigned int c;
+ char ch;
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ Elf_Internal_Note i_note;
+ Elf_External_Note e_note;
+ asection *note_secp = NULL;
+ int i, len;
+
+ /* create the .note section */
+
+ note_secp = subseg_new (".note", 0);
+ bfd_set_section_flags (stdoutput,
+ note_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* process the version string */
+
+ i_note.namesz = 4;
+ i_note.descsz = 12; /* 12 descriptive bytes */
+ i_note.type = NT_VERSION; /* Contains a version string */
+
+ p = frag_more (sizeof (i_note.namesz));
+ md_number_to_chars (p, i_note.namesz, 4);
+
+ p = frag_more (sizeof (i_note.descsz));
+ md_number_to_chars (p, i_note.descsz, 4);
+
+ p = frag_more (sizeof (i_note.type));
+ md_number_to_chars (p, i_note.type, 4);
+
+ p = frag_more (4);
+ strcpy (p, "SCO");
+
+ /* Note: this is the version number of the ELF we're representing */
+ p = frag_more (4);
+ md_number_to_chars (p, (SCO_MAJOR_VERSION << 16) | (SCO_MINOR_VERSION), 4);
+
+ /* Here, we pick a magic number for ourselves (yes, I "registered"
+ it with SCO. The bottom bit shows that we are compat with the
+ SCO ABI. */
+ p = frag_more (4);
+ md_number_to_chars (p, 0x4c520000 | 0x0001, 4);
+
+ /* If we knew (or cared) what the source language options were, we'd
+ fill them in here. SCO has given us permission to ignore these
+ and just set them to zero. */
+ p = frag_more (4);
+ md_number_to_chars (p, 0x0000, 4);
+
+ frag_align (2, 0, 0);
+
+ /* We probably can't restore the current segment, for there likely
+ isn't one yet... */
+ if (seg && subseg)
+ subseg_set (seg, subseg);
+
+}
+
+#endif /* SCO_ELF */
+
+static void
+elf_generate_asm_lineno (void)
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_generate_asm_lineno ();
+#endif
+}
+
+static void
+elf_process_stab (segT sec ATTRIBUTE_UNUSED,
+ int what ATTRIBUTE_UNUSED,
+ const char *string ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED,
+ int other ATTRIBUTE_UNUSED,
+ int desc ATTRIBUTE_UNUSED)
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (ECOFF_DEBUGGING)
+ ecoff_stab (sec, what, string, type, other, desc);
+#endif
+}
+
+static int
+elf_separate_stab_sections (void)
+{
+#ifdef NEED_ECOFF_DEBUG
+ return (!ECOFF_DEBUGGING);
+#else
+ return 1;
+#endif
+}
+
+static void
+elf_init_stab_section (segT seg)
+{
+#ifdef NEED_ECOFF_DEBUG
+ if (!ECOFF_DEBUGGING)
+#endif
+ obj_elf_init_stab_section (seg);
+}
+
+const struct format_ops elf_format_ops =
+{
+ bfd_target_elf_flavour,
+ 0, /* dfl_leading_underscore */
+ 1, /* emit_section_symbols */
+ elf_begin,
+ elf_file_symbol,
+ elf_frob_symbol,
+ elf_frob_file,
+ elf_frob_file_before_adjust,
+ 0, /* obj_frob_file_before_fix */
+ elf_frob_file_after_relocs,
+ elf_s_get_size, elf_s_set_size,
+ elf_s_get_align, elf_s_set_align,
+ elf_s_get_other,
+ elf_s_set_other,
+ 0, /* s_get_desc */
+ 0, /* s_set_desc */
+ 0, /* s_get_type */
+ 0, /* s_set_type */
+ elf_copy_symbol_attributes,
+ elf_generate_asm_lineno,
+ elf_process_stab,
+ elf_separate_stab_sections,
+ elf_init_stab_section,
+ elf_sec_sym_ok_for_reloc,
+ elf_pop_insert,
+#ifdef NEED_ECOFF_DEBUG
+ elf_ecoff_set_ext,
+#else
+ 0, /* ecoff_set_ext */
+#endif
+ elf_obj_read_begin_hook,
+ elf_obj_symbol_new_hook,
+ 0,
+ elf_adjust_symtab
+};
diff --git a/gas/config/obj-elf.h b/gas/config/obj-elf.h
new file mode 100644
index 0000000..3f8f8f4
--- /dev/null
+++ b/gas/config/obj-elf.h
@@ -0,0 +1,252 @@
+/* ELF object file format.
+ Copyright (C) 1992-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* HP PA-RISC support was contributed by the Center for Software Science
+ at the University of Utah. */
+
+#ifndef _OBJ_ELF_H
+#define _OBJ_ELF_H
+
+#define OBJ_ELF 1
+
+/* Note that all macros in this file should be wrapped in #ifndef, for
+ sake of obj-multi.h which includes this file. */
+
+#ifndef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR bfd_target_elf_flavour
+#endif
+
+#define BYTES_IN_WORD 4 /* for now */
+#include "bfd/elf-bfd.h"
+
+#include "targ-cpu.h"
+
+#ifdef TC_ALPHA
+#define ECOFF_DEBUGGING (alpha_flag_mdebug > 0)
+extern int alpha_flag_mdebug;
+#endif
+
+/* For now, always set ECOFF_DEBUGGING for a MIPS target. */
+#ifdef TC_MIPS
+#define ECOFF_DEBUGGING mips_flag_mdebug
+extern int mips_flag_mdebug;
+#endif /* TC_MIPS */
+
+#ifdef OBJ_MAYBE_ECOFF
+#ifndef ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 1
+#endif
+#endif
+
+/* Additional information we keep for each symbol. */
+struct elf_obj_sy
+{
+ /* Whether the symbol has been marked as local. */
+ int local;
+
+ /* Use this to keep track of .size expressions that involve
+ differences that we can't compute yet. */
+ expressionS *size;
+
+ /* The name specified by the .symver directive. */
+ char *versioned_name;
+
+#ifdef ECOFF_DEBUGGING
+ /* If we are generating ECOFF debugging information, we need some
+ additional fields for each symbol. */
+ struct efdr *ecoff_file;
+ struct localsym *ecoff_symbol;
+ valueT ecoff_extern_size;
+#endif
+};
+
+#define OBJ_SYMFIELD_TYPE struct elf_obj_sy
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+#ifndef obj_begin
+#define obj_begin() elf_begin ()
+#endif
+extern void elf_begin (void);
+
+#ifndef LOCAL_LABEL_PREFIX
+#define LOCAL_LABEL_PREFIX '.'
+#endif
+
+/* should be conditional on address size! */
+#define elf_symbol(asymbol) ((elf_symbol_type *) (&(asymbol)->the_bfd))
+
+#ifndef S_GET_SIZE
+#define S_GET_SIZE(S) \
+ (elf_symbol (symbol_get_bfdsym (S))->internal_elf_sym.st_size)
+#endif
+#ifndef S_SET_SIZE
+#define S_SET_SIZE(S,V) \
+ (elf_symbol (symbol_get_bfdsym (S))->internal_elf_sym.st_size = (V))
+#endif
+
+#ifndef S_GET_ALIGN
+#define S_GET_ALIGN(S) \
+ (elf_symbol (symbol_get_bfdsym (S))->internal_elf_sym.st_value)
+#endif
+#ifndef S_SET_ALIGN
+#define S_SET_ALIGN(S,V) \
+ (elf_symbol (symbol_get_bfdsym (S))->internal_elf_sym.st_value = (V))
+#endif
+
+int elf_s_get_other (symbolS *);
+#ifndef S_GET_OTHER
+#define S_GET_OTHER(S) (elf_s_get_other (S))
+#endif
+#ifndef S_SET_OTHER
+#define S_SET_OTHER(S,V) \
+ (elf_symbol (symbol_get_bfdsym (S))->internal_elf_sym.st_other = (V))
+#endif
+
+extern asection *gdb_section;
+
+#ifndef obj_frob_file
+#define obj_frob_file elf_frob_file
+#endif
+extern void elf_frob_file (void);
+
+#ifndef obj_frob_file_before_adjust
+#define obj_frob_file_before_adjust elf_frob_file_before_adjust
+#endif
+extern void elf_frob_file_before_adjust (void);
+
+#ifndef obj_frob_file_after_relocs
+#define obj_frob_file_after_relocs elf_frob_file_after_relocs
+#endif
+extern void elf_frob_file_after_relocs (void);
+
+/* If the target doesn't have special processing for labels, take care of
+ dwarf2 output at the object file level. */
+#ifndef tc_frob_label
+#include "dwarf2dbg.h"
+#define obj_frob_label dwarf2_emit_label
+#endif
+
+#ifndef obj_app_file
+#define obj_app_file elf_file_symbol
+#endif
+extern void elf_file_symbol (const char *, int);
+
+extern void obj_elf_section_change_hook (void);
+
+extern void obj_elf_section (int);
+extern char * obj_elf_section_name (void);
+extern void obj_elf_previous (int);
+extern void obj_elf_version (int);
+extern void obj_elf_common (int);
+extern void obj_elf_data (int);
+extern void obj_elf_text (int);
+extern void obj_elf_change_section
+ (const char *, int, bfd_vma, int, const char *, int, int);
+extern struct fix *obj_elf_vtable_inherit (int);
+extern struct fix *obj_elf_vtable_entry (int);
+extern bfd_boolean obj_elf_seen_attribute
+ (int, unsigned int);
+extern int obj_elf_vendor_attribute (int);
+
+/* BFD wants to write the udata field, which is a no-no for the
+ predefined section symbols in bfd/section.c. They are read-only. */
+#ifndef obj_sec_sym_ok_for_reloc
+#define obj_sec_sym_ok_for_reloc(SEC) ((SEC)->owner != 0)
+#endif
+
+void elf_obj_read_begin_hook (void);
+#ifndef obj_read_begin_hook
+#define obj_read_begin_hook elf_obj_read_begin_hook
+#endif
+
+void elf_obj_symbol_new_hook (symbolS *);
+#ifndef obj_symbol_new_hook
+#define obj_symbol_new_hook elf_obj_symbol_new_hook
+#endif
+
+void elf_copy_symbol_attributes (symbolS *, symbolS *);
+#ifndef OBJ_COPY_SYMBOL_ATTRIBUTES
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(DEST, SRC) \
+ (elf_copy_symbol_attributes (DEST, SRC))
+#endif
+
+void elf_adjust_symtab (void);
+#ifndef obj_adjust_symtab
+#define obj_adjust_symtab elf_adjust_symtab
+#endif
+
+#ifndef SEPARATE_STAB_SECTIONS
+/* Avoid ifndef each separate macro setting by wrapping the whole of the
+ stab group on the assumption that whoever sets SEPARATE_STAB_SECTIONS
+ caters to ECOFF_DEBUGGING and the right setting of INIT_STAB_SECTIONS
+ and OBJ_PROCESS_STAB too, without needing the tweaks below. */
+
+/* Stabs go in a separate section. */
+#define SEPARATE_STAB_SECTIONS 1
+
+/* We need 12 bytes at the start of the section to hold some initial
+ information. */
+extern void obj_elf_init_stab_section (segT);
+#define INIT_STAB_SECTION(seg) obj_elf_init_stab_section (seg)
+
+#ifdef ECOFF_DEBUGGING
+/* We smuggle stabs in ECOFF rather than using a separate section.
+ The Irix linker can not handle a separate stabs section. */
+
+#undef SEPARATE_STAB_SECTIONS
+#define SEPARATE_STAB_SECTIONS (!ECOFF_DEBUGGING)
+
+#undef INIT_STAB_SECTION
+#define INIT_STAB_SECTION(seg) \
+ ((void) (ECOFF_DEBUGGING ? 0 : (obj_elf_init_stab_section (seg), 0)))
+
+#undef OBJ_PROCESS_STAB
+#define OBJ_PROCESS_STAB(seg, what, string, type, other, desc) \
+ if (ECOFF_DEBUGGING) \
+ ecoff_stab ((seg), (what), (string), (type), (other), (desc))
+#endif /* ECOFF_DEBUGGING */
+
+#endif /* SEPARATE_STAB_SECTIONS not defined. */
+
+extern void elf_frob_symbol (symbolS *, int *);
+#ifndef obj_frob_symbol
+#define obj_frob_symbol(symp, punt) elf_frob_symbol (symp, &punt)
+#endif
+
+extern void elf_pop_insert (void);
+#ifndef obj_pop_insert
+#define obj_pop_insert() elf_pop_insert()
+#endif
+
+#ifndef OBJ_MAYBE_ELF
+/* If OBJ_MAYBE_ELF then obj-multi.h will define obj_ecoff_set_ext. */
+#define obj_ecoff_set_ext elf_ecoff_set_ext
+struct ecoff_extr;
+extern void elf_ecoff_set_ext (symbolS *, struct ecoff_extr *);
+#endif
+extern asection *elf_com_section_ptr;
+extern symbolS * elf_common_parse (int ignore ATTRIBUTE_UNUSED, symbolS *symbolP,
+ addressT size);
+
+#endif /* _OBJ_ELF_H */
diff --git a/gas/config/obj-evax.c b/gas/config/obj-evax.c
new file mode 100644
index 0000000..a38269c
--- /dev/null
+++ b/gas/config/obj-evax.c
@@ -0,0 +1,524 @@
+/* obj-evax.c - EVAX (openVMS/Alpha) object file format.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+ Contributed by Klaus Kämpf (kkaempf@progis.de) of
+ proGIS Software, Aachen, Germany.
+ Extensively enhanced by Douglas Rupp of AdaCore.
+
+ This file is part of GAS, the GNU Assembler
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#define OBJ_HEADER "obj-evax.h"
+
+#include "as.h"
+#include "bfd.h"
+#include "vms.h"
+#include "subsegs.h"
+#include "struc-symbol.h"
+#include "safe-ctype.h"
+
+static void s_evax_weak (int);
+static unsigned int crc32 (unsigned char *, int);
+static char *encode_32 (unsigned int);
+static char *encode_16 (unsigned int);
+static int decode_16 (const char *);
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ { "weak", s_evax_weak, 0},
+ {0, 0, 0},
+}; /* obj_pseudo_table */
+
+void obj_read_begin_hook () {}
+
+/* Handle the weak specific pseudo-op. */
+
+static void
+s_evax_weak (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ char *stop = NULL;
+ char stopc;
+
+ if (flag_mri)
+ stop = mri_comment_field (&stopc);
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ if (flag_mri)
+ mri_comment_end (stop, stopc);
+
+ demand_empty_rest_of_line ();
+}
+
+void
+evax_symbol_new_hook (symbolS *sym)
+{
+ struct evax_private_udata_struct *udata;
+
+ udata = (struct evax_private_udata_struct *)
+ xmalloc (sizeof (struct evax_private_udata_struct));
+
+ udata->bsym = symbol_get_bfdsym (sym);
+ udata->enbsym = NULL;
+ udata->origname = xstrdup (S_GET_NAME (sym));
+ udata->lkindex = 0;
+ symbol_get_bfdsym(sym)->udata.p = (PTR) udata;
+}
+
+void
+evax_frob_symbol (symbolS *sym, int *punt)
+{
+ const char *symname = S_GET_NAME (sym);
+ int symlen = strlen (symname);
+ asymbol *symbol = symbol_get_bfdsym (sym);
+
+ if (symlen > 4
+ && strcmp (symname + symlen - 4, "..en") == 0
+ && S_GET_SEGMENT (sym) == undefined_section)
+ {
+ symbol_clear_used_in_reloc (sym);
+ *punt = 1;
+ }
+
+ else if ((symbol->flags & BSF_GLOBAL) && (symbol->flags & BSF_FUNCTION))
+ {
+ struct evax_private_udata_struct *udata
+ = (struct evax_private_udata_struct *)symbol->udata.p;
+
+ /* Fix up equates of function definitions. */
+ while (udata->enbsym == NULL)
+ {
+ /* ??? Equates have been resolved at this point so their
+ expression is O_constant; but they previously were
+ O_symbol and we hope the equated symbol is still there. */
+ sym = symbol_get_value_expression (sym)->X_add_symbol;
+ if (sym == NULL)
+ {
+ as_bad (_("no entry symbol for global function '%s'"), symname);
+ return;
+ }
+ symbol = symbol_get_bfdsym (sym);
+ udata->enbsym
+ = ((struct evax_private_udata_struct *)symbol->udata.p)->enbsym;
+ }
+ }
+}
+
+void
+evax_frob_file_before_adjust (void)
+{
+ struct alpha_linkage_fixups *l;
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ segment_info_type *seginfo;
+ int linkage_index = 1;
+
+ subseg_set (alpha_link_section, 0);
+ seginfo = seg_info (alpha_link_section);
+
+ /* Handle .linkage fixups. */
+ for (l = alpha_linkage_fixup_root; l != NULL; l = l->next)
+ {
+ if (S_GET_SEGMENT (l->fixp->fx_addsy) == alpha_link_section)
+ {
+ /* The symbol is defined in the file. The linkage entry decays to
+ two relocs. */
+ symbolS *entry_sym;
+ fixS *fixpentry, *fixppdesc, *fixtail;
+
+ fixtail = seginfo->fix_tail;
+
+ /* Replace the linkage with the local symbols */
+ entry_sym = symbol_find
+ (((struct evax_private_udata_struct *)symbol_get_bfdsym (l->fixp->fx_addsy)->udata.p)->enbsym->name);
+ if (!entry_sym)
+ abort ();
+ fixpentry = fix_new (l->fixp->fx_frag, l->fixp->fx_where, 8,
+ entry_sym, l->fixp->fx_offset, 0,
+ BFD_RELOC_64);
+ fixppdesc = fix_new (l->fixp->fx_frag, l->fixp->fx_where + 8, 8,
+ l->fixp->fx_addsy, l->fixp->fx_offset, 0,
+ BFD_RELOC_64);
+ l->fixp->fx_size = 0;
+ l->fixp->fx_done = 1;
+
+ /* If not already at the tail, splice the new fixups into
+ the chain right after the one we are nulling out */
+ if (fixtail != l->fixp)
+ {
+ fixppdesc->fx_next = l->fixp->fx_next;
+ l->fixp->fx_next = fixpentry;
+ fixtail->fx_next = 0;
+ seginfo->fix_tail = fixtail;
+ }
+ }
+ else
+ {
+ /* Assign a linkage index. */
+ ((struct evax_private_udata_struct *)
+ symbol_get_bfdsym (l->label)->udata.p)->lkindex = linkage_index;
+
+ l->fixp->fx_addnumber = linkage_index;
+
+ linkage_index += 2;
+ }
+ }
+
+ subseg_set (current_section, current_subsec);
+}
+
+void
+evax_frob_file_before_fix (void)
+{
+ /* Now that the fixups are done earlier, we need to transfer the values
+ into the BFD symbols before calling fix_segment (ideally should not
+ be done also later). */
+ if (symbol_rootP)
+ {
+ symbolS *symp;
+
+ /* Set the value into the BFD symbol. Up til now the value
+ has only been kept in the gas symbolS struct. */
+ for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+ symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp);
+ }
+}
+
+/* The length is computed from the maximum allowable length of 64 less the
+ 4 character ..xx extension that must be preserved (removed before
+ krunching and appended back on afterwards). The $<nnn>.. prefix is
+ also removed and prepened back on, but doesn't enter into the length
+ computation because symbols with that prefix are always resolved
+ by the assembler and will never appear in the symbol table. At least
+ I hope that's true, TBD. */
+#define MAX_LABEL_LENGTH 60
+
+static char *shorten_identifier (char *);
+static int is_truncated_identifier (char *);
+
+char *
+evax_shorten_name (char *id)
+{
+ int prefix_dotdot = 0;
+ char prefix [64];
+ int len = strlen (id);
+ int suffix_dotdot = len;
+ char suffix [64];
+ char *base_id;
+
+ /* This test may be too conservative. */
+ if (len <= MAX_LABEL_LENGTH)
+ return id;
+
+ suffix [0] = 0;
+ prefix [0] = 0;
+
+ /* Check for ..xx suffix and save it. */
+ if (strncmp (&id[len-4], "..", 2) == 0)
+ {
+ suffix_dotdot = len - 4;
+ strncpy (suffix, &id[len-4], 4);
+ suffix [4] = 0;
+ }
+
+ /* Check for $<nnn>.. prefix and save it. */
+ if ((id[0] == '$') && ISDIGIT (id[1]))
+ {
+ int i;
+
+ for (i=2; i < len; i++)
+ {
+ if (!ISDIGIT (id[i]))
+ {
+ if (id[i] == '.' && id [i+1] == '.')
+ {
+ prefix_dotdot = i+2;
+ strncpy (prefix, id, prefix_dotdot);
+ prefix [prefix_dotdot] = 0;
+ }
+ break;
+ }
+ }
+ }
+
+ /* We only need worry about krunching the base symbol. */
+ base_id = xmalloc (suffix_dotdot - prefix_dotdot + 1);
+ strncpy (base_id, &id[prefix_dotdot], suffix_dotdot - prefix_dotdot);
+ base_id [suffix_dotdot - prefix_dotdot] = 0;
+
+ if (strlen (base_id) > MAX_LABEL_LENGTH)
+ {
+ char new_id [4096];
+ char *return_id;
+
+ strcpy (new_id, base_id);
+
+ /* Shorten it. */
+ strcpy (new_id, shorten_identifier (new_id));
+
+ /* Prepend back the prefix if there was one. */
+ if (prefix_dotdot)
+ {
+ memmove (&new_id [prefix_dotdot], new_id, strlen (new_id) + 1);
+ strncpy (new_id, prefix, prefix_dotdot);
+ }
+
+ /* Append back the suffix if there was one. */
+ if (strlen (suffix))
+ strcat (new_id, suffix);
+
+ /* Save it on the heap and return. */
+ return_id = xmalloc (strlen (new_id) + 1);
+ strcpy (return_id, new_id);
+
+ return return_id;
+ }
+ else
+ return id;
+}
+
+/* The code below implements a mechanism for truncating long
+ identifiers to an arbitrary length (set by MAX_LABEL_LENGTH).
+
+ It attempts to make each truncated identifier unique by replacing
+ part of the identifier with an encoded 32-bit CRC and an associated
+ checksum (the checksum is used as a way to determine that the name
+ was truncated).
+
+ Note that both a portion of the start and of the end of the
+ identifier may be kept. The macro ID_SUFFIX_LENGTH will return the
+ number of characters in the suffix of the identifier that should be
+ kept.
+
+ The portion of the identifier that is going to be removed is
+ checksummed. The checksum is then encoded as a 5-character string,
+ the characters of which are then summed. This sum is then encoded
+ as a 3-character string. Finally, the original length of the
+ identifier is encoded as a 3-character string.
+
+ These three strings are then concatenated together (along with an _h
+ which further designates that the name was truncated):
+
+ "original_identifier"_haaaaabbbccc
+
+ aaaaa = 32-bit CRC
+ bbb = length of original identifier
+ ccc = sum of 32-bit CRC characters
+
+ The resulting identifier will be MAX_LABEL_LENGTH characters long.
+
+ */
+
+
+/* Table used to convert an integer into a string. */
+
+static const char codings[] = {
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_'};
+
+/* The number of codings in the above table. */
+static const int number_of_codings = sizeof (codings) / sizeof (char);
+
+/* Table used by decode_16 () to convert an encoded string back into
+ an integer. */
+static char decodings[256];
+
+/* Table used by the crc32 function to calcuate the checksum. */
+static unsigned int crc32_table[256] = {0, 0};
+
+/* Given a string in BUF, calculate a 32-bit CRC for it.
+
+ This is used as a reasonably unique hash for the given string. */
+
+static unsigned int
+crc32 (unsigned char *buf, int len)
+{
+ unsigned int crc = 0xffffffff;
+
+ if (! crc32_table[1])
+ {
+ /* Initialize the CRC table and the decoding table. */
+ int i, j;
+ unsigned int c;
+
+ for (i = 0; i < 256; i++)
+ {
+ for (c = i << 24, j = 8; j > 0; --j)
+ c = c & 0x80000000 ? (c << 1) ^ 0x04c11db7 : (c << 1);
+ crc32_table[i] = c;
+ decodings[i] = 0;
+ }
+ for (i = 0; i < number_of_codings; i++)
+ decodings[codings[i] & 255] = i;
+ }
+
+ while (len--)
+ {
+ crc = (crc << 8) ^ crc32_table[(crc >> 24) ^ *buf];
+ buf++;
+ }
+ return crc;
+}
+
+/* Encode the lower 32 bits of VALUE as a 5-character string. */
+
+static char *
+encode_32 (unsigned int value)
+{
+ static char res[6];
+ int x;
+
+ res[5] = 0;
+ for(x = 0; x < 5; x++)
+ {
+ res[x] = codings[value % number_of_codings];
+ value = value / number_of_codings;
+ }
+ return res;
+}
+
+/* Encode the lower 16 bits of VALUE as a 3-character string. */
+
+static char *
+encode_16 (unsigned int value)
+{
+ static char res[4];
+ int x;
+
+ res[3] = 0;
+ for(x = 0; x < 3; x++)
+ {
+ res[x] = codings[value % number_of_codings];
+ value = value / number_of_codings;
+ }
+ return res;
+}
+
+/* Convert the encoded string obtained from encode_16 () back into a
+ 16-bit integer. */
+
+static int
+decode_16 (const char *string)
+{
+ return decodings[(int) string[2]] * number_of_codings * number_of_codings
+ + decodings[(int) string[1]] * number_of_codings
+ + decodings[(int) string[0]];
+}
+
+/* ID_SUFFIX_LENGTH is used to determine how many characters in the
+ suffix of the identifier are to be preserved, if any. */
+
+#ifndef ID_SUFFIX_LENGTH
+#define ID_SUFFIX_LENGTH(ID) (0)
+#endif
+
+/* Return a reasonably-unique version of NAME that is less than or
+ equal to MAX_LABEL_LENGTH characters long. The string returned from
+ this function may be a copy of NAME; the function will never
+ actually modify the contents of NAME. */
+
+static char newname[MAX_LABEL_LENGTH + 1];
+
+static char *
+shorten_identifier (char *name)
+{
+ int crc, len, sum, x, final_len;
+ char *crc_chars;
+ int suffix_length = ID_SUFFIX_LENGTH (name);
+
+ if ((len = strlen (name)) <= MAX_LABEL_LENGTH)
+ return name;
+
+ final_len = MAX_LABEL_LENGTH - 2 - 5 - 3 - 3 - suffix_length;
+ crc = crc32 ((unsigned char *)name + final_len,
+ len - final_len - suffix_length);
+ crc_chars = encode_32 (crc);
+ sum = 0;
+ for (x = 0; x < 5; x++)
+ sum += crc_chars [x];
+ strncpy (newname, name, final_len);
+ newname [MAX_LABEL_LENGTH] = 0;
+ /* Now append the suffix of the original identifier, if any. */
+ if (suffix_length)
+ strncpy (newname + MAX_LABEL_LENGTH - suffix_length,
+ name + len - suffix_length,
+ suffix_length);
+ strncpy (newname + final_len, "_h", 2);
+ strncpy (newname + final_len + 2 , crc_chars, 5);
+ strncpy (newname + final_len + 2 + 5, encode_16 (len), 3);
+ strncpy (newname + final_len + 2 + 5 + 3, encode_16 (sum), 3);
+ if (!is_truncated_identifier (newname))
+ abort ();
+ return newname;
+}
+
+/* Determine whether or not ID is a truncated identifier, and return a
+ non-zero value if it is. */
+
+static int
+is_truncated_identifier (char *id)
+{
+ char *ptr;
+ int len = strlen (id);
+ /* If it's not exactly MAX_LABEL_LENGTH characters long, it can't be
+ a truncated identifier. */
+ if (len != MAX_LABEL_LENGTH)
+ return 0;
+
+ /* Start scanning backwards for a _h. */
+ len = len - 3 - 3 - 5 - 2;
+ ptr = id + len;
+ while (ptr >= id)
+ {
+ if (ptr[0] == '_' && ptr[1] == 'h')
+ {
+ /* Now see if the sum encoded in the identifer matches. */
+ int x, sum;
+ sum = 0;
+ for (x = 0; x < 5; x++)
+ sum += ptr[x + 2];
+ /* If it matches, this is probably a truncated identifier. */
+ if (sum == decode_16 (ptr + 5 + 2 + 3))
+ return 1;
+ }
+ ptr--;
+ }
+ return 0;
+}
+
+/* end of obj-evax.c */
diff --git a/gas/config/obj-evax.h b/gas/config/obj-evax.h
new file mode 100644
index 0000000..c1fc9be
--- /dev/null
+++ b/gas/config/obj-evax.h
@@ -0,0 +1,111 @@
+/* This file is obj-evax.h
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+ Contributed by Klaus Kämpf (kkaempf@progis.de) of
+ proGIS Software, Aachen, Germany.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/*
+ * This file is obj-evax.h and is intended to be a template for
+ * object format specific header files.
+ */
+
+#include "as.h"
+
+/* define an obj specific macro off which target cpu back ends may key. */
+#define OBJ_EVAX 1
+
+/* include whatever target cpu is appropriate. */
+#include "targ-cpu.h"
+
+#define OUTPUT_FLAVOR bfd_target_evax_flavour
+
+struct fix;
+
+/* Simply linked list of .linkage. */
+struct alpha_linkage_fixups
+{
+ /* Next entry. */
+ struct alpha_linkage_fixups *next;
+
+ /* Corresponding fixup. */
+ struct fix *fixp;
+
+ /* Label that designates this entry.
+ Note that a linkage entry can only be designated by one label.
+ Also, s_alpha_linkage force the creation of a label. */
+ symbolS *label;
+};
+
+/*
+ * SYMBOLS
+ */
+
+/*
+ * If your object format needs to reorder symbols, define this. When
+ * defined, symbols are kept on a doubly linked list and functions are
+ * made available for push, insert, append, and delete. If not defined,
+ * symbols are kept on a singly linked list, only the append and clear
+ * facilities are available, and they are macros.
+ */
+
+/* #define SYMBOLS_NEED_PACKPOINTERS */
+
+#define OBJ_EMIT_LINENO(a,b,c) /* must be *something*. This no-op's it out. */
+
+#define obj_symbol_new_hook(s) evax_symbol_new_hook (s)
+#define obj_frob_symbol(s,p) evax_frob_symbol (s, &p)
+#define obj_frob_file_before_adjust evax_frob_file_before_adjust
+#define obj_frob_file_before_fix evax_frob_file_before_fix
+
+#define S_GET_OTHER(S) 0
+#define S_GET_TYPE(S) 0
+#define S_GET_DESC(S) 0
+
+#define PDSC_S_K_KIND_FP_STACK 9
+#define PDSC_S_K_KIND_FP_REGISTER 10
+#define PDSC_S_K_KIND_NULL 8
+
+#define PDSC_S_K_MIN_STACK_SIZE 32
+#define PDSC_S_K_MIN_REGISTER_SIZE 24
+#define PDSC_S_K_NULL_SIZE 16
+
+#define PDSC_S_M_HANDLER_VALID 0x10 /* low byte */
+#define PDSC_S_M_HANDLER_DATA_VALID 0x40 /* low byte */
+#define PDSC_S_M_BASE_REG_IS_FP 0x80 /* low byte */
+#define PDSC_S_M_NATIVE 0x10 /* high byte */
+#define PDSC_S_M_NO_JACKET 0x20 /* high byte */
+
+#define LKP_S_K_SIZE 16
+
+extern segT alpha_link_section;
+extern struct alpha_linkage_fixups *alpha_linkage_fixup_root;
+
+extern void evax_section (int);
+extern void evax_symbol_new_hook (symbolS *);
+extern void evax_frob_symbol (symbolS *, int *);
+extern void evax_frob_file_before_adjust (void);
+extern void evax_frob_file_before_fix (void);
+extern char *evax_shorten_name (char *);
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
diff --git a/gas/config/obj-fdpicelf.c b/gas/config/obj-fdpicelf.c
new file mode 100644
index 0000000..13d945d
--- /dev/null
+++ b/gas/config/obj-fdpicelf.c
@@ -0,0 +1,20 @@
+/* Copyright (C) 2012-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "obj-elf.c"
diff --git a/gas/config/obj-fdpicelf.h b/gas/config/obj-fdpicelf.h
new file mode 100644
index 0000000..6da1ef2
--- /dev/null
+++ b/gas/config/obj-fdpicelf.h
@@ -0,0 +1,21 @@
+/* Copyright (C) 2012-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define OBJ_FDPIC_ELF 1
+#include "obj-elf.h"
diff --git a/gas/config/obj-macho.c b/gas/config/obj-macho.c
new file mode 100644
index 0000000..fcf1729
--- /dev/null
+++ b/gas/config/obj-macho.c
@@ -0,0 +1,1994 @@
+/* Mach-O object file format
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Here we handle the mach-o directives that are common to all architectures.
+
+ Most significant are mach-o named sections and a variety of symbol type
+ decorations. */
+
+/* Mach-O supports multiple, named segments each of which may contain
+ multiple named sections. Thus the concept of subsectioning is
+ handled by (say) having a __TEXT segment with appropriate flags from
+ which subsections are generated like __text, __const etc.
+
+ The well-known as short-hand section switch directives like .text, .data
+ etc. are mapped onto predefined segment/section pairs using facilites
+ supplied by the mach-o port of bfd.
+
+ A number of additional mach-o short-hand section switch directives are
+ also defined. */
+
+#define OBJ_HEADER "obj-macho.h"
+
+#include "as.h"
+#include "subsegs.h"
+#include "symbols.h"
+#include "write.h"
+#include "mach-o.h"
+#include "mach-o/loader.h"
+#include "obj-macho.h"
+
+#include <string.h>
+
+/* Forward decls. */
+static segT obj_mach_o_segT_from_bfd_name (const char *, int);
+
+/* TODO: Implement "-dynamic"/"-static" command line options. */
+
+static int obj_mach_o_is_static;
+
+/* TODO: Implement the "-n" command line option to suppress the initial
+ switch to the text segment. */
+
+static int obj_mach_o_start_with_text_section = 1;
+
+/* Allow for special re-ordering on output. */
+
+static int obj_mach_o_seen_objc_section;
+
+/* Start-up: At present, just create the sections we want. */
+void
+mach_o_begin (void)
+{
+ /* Mach-O only defines the .text section by default, and even this can
+ be suppressed by a flag. In the latter event, the first code MUST
+ be a section definition. */
+ if (obj_mach_o_start_with_text_section)
+ {
+ text_section = obj_mach_o_segT_from_bfd_name (TEXT_SECTION_NAME, 1);
+ subseg_set (text_section, 0);
+ if (obj_mach_o_is_static)
+ {
+ bfd_mach_o_section *mo_sec
+ = bfd_mach_o_get_mach_o_section (text_section);
+ mo_sec->flags &= ~BFD_MACH_O_S_ATTR_PURE_INSTRUCTIONS;
+ }
+ }
+}
+
+/* Remember the subsections_by_symbols state in case we need to reset
+ the file flags. */
+
+static int obj_mach_o_subsections_by_symbols;
+
+/* This will put at most 16 characters (terminated by a ',' or newline) from
+ the input stream into dest. If there are more than 16 chars before the
+ delimiter, a warning is given and the string is truncated. On completion of
+ this function, input_line_pointer will point to the char after the ',' or
+ to the newline.
+
+ It trims leading and trailing space. */
+
+static int
+collect_16char_name (char *dest, const char *msg, int require_comma)
+{
+ char c, *namstart;
+
+ SKIP_WHITESPACE ();
+ namstart = input_line_pointer;
+
+ while ( (c = *input_line_pointer) != ','
+ && !is_end_of_line[(unsigned char) c])
+ input_line_pointer++;
+
+ {
+ int len = input_line_pointer - namstart; /* could be zero. */
+ /* lose any trailing space. */
+ while (len > 0 && namstart[len-1] == ' ')
+ len--;
+ if (len > 16)
+ {
+ *input_line_pointer = '\0'; /* make a temp string. */
+ as_bad (_("the %s name '%s' is too long (maximum 16 characters)"),
+ msg, namstart);
+ *input_line_pointer = c; /* restore for printing. */
+ len = 16;
+ }
+ if (len > 0)
+ memcpy (dest, namstart, len);
+ }
+
+ if (c != ',' && require_comma)
+ {
+ as_bad (_("expected a %s name followed by a `,'"), msg);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+obj_mach_o_get_section_names (char *seg, char *sec,
+ unsigned segl, unsigned secl)
+{
+ /* Zero-length segment and section names are allowed. */
+ /* Parse segment name. */
+ memset (seg, 0, segl);
+ if (collect_16char_name (seg, "segment", 1))
+ {
+ ignore_rest_of_line ();
+ return 0;
+ }
+ input_line_pointer++; /* Skip the terminating ',' */
+
+ /* Parse section name, which can be empty. */
+ memset (sec, 0, secl);
+ collect_16char_name (sec, "section", 0);
+ return 1;
+}
+
+/* Build (or get) a section from the mach-o description - which includes
+ optional definitions for type, attributes, alignment and stub size.
+
+ BFD supplies default values for sections which have a canonical name. */
+
+#define SECT_TYPE_SPECIFIED 0x0001
+#define SECT_ATTR_SPECIFIED 0x0002
+#define SECT_ALGN_SPECIFIED 0x0004
+#define SECT_STUB_SPECIFIED 0x0008
+
+static segT
+obj_mach_o_make_or_get_sect (char * segname, char * sectname,
+ unsigned int specified_mask,
+ unsigned int usectype, unsigned int usecattr,
+ unsigned int ualign, offsetT stub_size)
+{
+ unsigned int sectype, secattr, secalign;
+ flagword oldflags, flags;
+ const char *name;
+ segT sec;
+ bfd_mach_o_section *msect;
+ const mach_o_section_name_xlat *xlat;
+
+ /* This provides default bfd flags and default mach-o section type and
+ attributes along with the canonical name. */
+ xlat = bfd_mach_o_section_data_for_mach_sect (stdoutput, segname, sectname);
+
+ /* TODO: more checking of whether overides are acually allowed. */
+
+ if (xlat != NULL)
+ {
+ name = xstrdup (xlat->bfd_name);
+ sectype = xlat->macho_sectype;
+ if (specified_mask & SECT_TYPE_SPECIFIED)
+ {
+ if ((sectype == BFD_MACH_O_S_ZEROFILL
+ || sectype == BFD_MACH_O_S_GB_ZEROFILL)
+ && sectype != usectype)
+ as_bad (_("cannot overide zerofill section type for `%s,%s'"),
+ segname, sectname);
+ else
+ sectype = usectype;
+ }
+ secattr = xlat->macho_secattr;
+ secalign = xlat->sectalign;
+ flags = xlat->bfd_flags;
+ }
+ else
+ {
+ /* There is no normal BFD section name for this section. Create one.
+ The name created doesn't really matter as it will never be written
+ on disk. */
+ size_t seglen = strlen (segname);
+ size_t sectlen = strlen (sectname);
+ char *n;
+
+ n = xmalloc (seglen + 1 + sectlen + 1);
+ memcpy (n, segname, seglen);
+ n[seglen] = '.';
+ memcpy (n + seglen + 1, sectname, sectlen);
+ n[seglen + 1 + sectlen] = 0;
+ name = n;
+ if (specified_mask & SECT_TYPE_SPECIFIED)
+ sectype = usectype;
+ else
+ sectype = BFD_MACH_O_S_REGULAR;
+ secattr = BFD_MACH_O_S_ATTR_NONE;
+ secalign = 0;
+ flags = SEC_NO_FLAGS;
+ }
+
+ /* For now, just use what the user provided. */
+
+ if (specified_mask & SECT_ATTR_SPECIFIED)
+ secattr = usecattr;
+
+ if (specified_mask & SECT_ALGN_SPECIFIED)
+ secalign = ualign;
+
+ /* Sub-segments don't exists as is on Mach-O. */
+ sec = subseg_new (name, 0);
+
+ oldflags = bfd_get_section_flags (stdoutput, sec);
+ msect = bfd_mach_o_get_mach_o_section (sec);
+
+ if (oldflags == SEC_NO_FLAGS)
+ {
+ /* In the absence of canonical information, try to determine CODE and
+ DEBUG section flags from the mach-o section data. */
+ if (flags == SEC_NO_FLAGS
+ && (specified_mask & SECT_ATTR_SPECIFIED)
+ && (secattr & BFD_MACH_O_S_ATTR_PURE_INSTRUCTIONS))
+ flags |= SEC_CODE;
+
+ if (flags == SEC_NO_FLAGS
+ && (specified_mask & SECT_ATTR_SPECIFIED)
+ && (secattr & BFD_MACH_O_S_ATTR_DEBUG))
+ flags |= SEC_DEBUGGING;
+
+ /* New, so just use the defaults or what's specified. */
+ if (! bfd_set_section_flags (stdoutput, sec, flags))
+ as_warn (_("failed to set flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+
+ strncpy (msect->segname, segname, sizeof (msect->segname));
+ strncpy (msect->sectname, sectname, sizeof (msect->sectname));
+
+ msect->align = secalign;
+ msect->flags = sectype | secattr;
+
+ if (sectype == BFD_MACH_O_S_ZEROFILL
+ || sectype == BFD_MACH_O_S_GB_ZEROFILL)
+ seg_info (sec)->bss = 1;
+ }
+ else if (flags != SEC_NO_FLAGS)
+ {
+ if (flags != oldflags
+ || msect->flags != (secattr | sectype))
+ as_warn (_("Ignoring changed section attributes for %s"), name);
+ }
+
+ if (specified_mask & SECT_STUB_SPECIFIED)
+ /* At present, the stub size is not supplied from the BFD tables. */
+ msect->reserved2 = stub_size;
+
+ return sec;
+}
+
+/* .section
+
+ The '.section' specification syntax looks like:
+ .section <segment> , <section> [, type [, attribs [, size]]]
+
+ White space is allowed everywhere between elements.
+
+ <segment> and <section> may be from 0 to 16 chars in length - they may
+ contain spaces but leading and trailing space will be trimmed. It is
+ mandatory that they be present (or that zero-length names are indicated
+ by ",,").
+
+ There is only a single section type for any entry.
+
+ There may be multiple attributes, they are delimited by `+'.
+
+ Not all section types and attributes are accepted by the Darwin system
+ assemblers as user-specifiable - although, at present, we do here. */
+
+static void
+obj_mach_o_section (int ignore ATTRIBUTE_UNUSED)
+{
+ unsigned int sectype = BFD_MACH_O_S_REGULAR;
+ unsigned int specified_mask = 0;
+ unsigned int secattr = 0;
+ offsetT sizeof_stub = 0;
+ segT new_seg;
+ char segname[17];
+ char sectname[17];
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ /* Get the User's segment annd section names. */
+ if (! obj_mach_o_get_section_names (segname, sectname, 17, 17))
+ return;
+
+ /* Parse section type, if present. */
+ if (*input_line_pointer == ',')
+ {
+ char *p;
+ char c;
+ char tmpc;
+ int len;
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ p = input_line_pointer;
+ while ((c = *input_line_pointer) != ','
+ && !is_end_of_line[(unsigned char) c])
+ input_line_pointer++;
+
+ len = input_line_pointer - p;
+ /* strip trailing spaces. */
+ while (len > 0 && p[len-1] == ' ')
+ len--;
+ tmpc = p[len];
+
+ /* Temporarily make a string from the token. */
+ p[len] = 0;
+ sectype = bfd_mach_o_get_section_type_from_name (stdoutput, p);
+ if (sectype > 255) /* Max Section ID == 255. */
+ {
+ as_bad (_("unknown or invalid section type '%s'"), p);
+ p[len] = tmpc;
+ ignore_rest_of_line ();
+ return;
+ }
+ else
+ specified_mask |= SECT_TYPE_SPECIFIED;
+ /* Restore. */
+ p[len] = tmpc;
+
+ /* Parse attributes.
+ TODO: check validity of attributes for section type. */
+ if ((specified_mask & SECT_TYPE_SPECIFIED)
+ && c == ',')
+ {
+ do
+ {
+ int attr;
+
+ /* Skip initial `,' and subsequent `+'. */
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ p = input_line_pointer;
+ while ((c = *input_line_pointer) != '+'
+ && c != ','
+ && !is_end_of_line[(unsigned char) c])
+ input_line_pointer++;
+
+ len = input_line_pointer - p;
+ /* strip trailing spaces. */
+ while (len > 0 && p[len-1] == ' ')
+ len--;
+ tmpc = p[len];
+
+ /* Temporarily make a string from the token. */
+ p[len] ='\0';
+ attr = bfd_mach_o_get_section_attribute_from_name (p);
+ if (attr == -1)
+ {
+ as_bad (_("unknown or invalid section attribute '%s'"), p);
+ p[len] = tmpc;
+ ignore_rest_of_line ();
+ return;
+ }
+ else
+ {
+ specified_mask |= SECT_ATTR_SPECIFIED;
+ secattr |= attr;
+ }
+ /* Restore. */
+ p[len] = tmpc;
+ }
+ while (*input_line_pointer == '+');
+
+ /* Parse sizeof_stub. */
+ if ((specified_mask & SECT_ATTR_SPECIFIED)
+ && *input_line_pointer == ',')
+ {
+ if (sectype != BFD_MACH_O_S_SYMBOL_STUBS)
+ {
+ as_bad (_("unexpected section size information"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++;
+ sizeof_stub = get_absolute_expression ();
+ specified_mask |= SECT_STUB_SPECIFIED;
+ }
+ else if ((specified_mask & SECT_ATTR_SPECIFIED)
+ && sectype == BFD_MACH_O_S_SYMBOL_STUBS)
+ {
+ as_bad (_("missing sizeof_stub expression"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ }
+
+ new_seg = obj_mach_o_make_or_get_sect (segname, sectname, specified_mask,
+ sectype, secattr, 0 /*align */,
+ sizeof_stub);
+ if (new_seg != NULL)
+ {
+ subseg_set (new_seg, 0);
+ demand_empty_rest_of_line ();
+ }
+}
+
+/* .zerofill segname, sectname [, symbolname, size [, align]]
+
+ Zerofill switches, temporarily, to a sect of type 'zerofill'.
+
+ If a variable name is given, it defines that in the section.
+ Otherwise it just creates the section if it doesn't exist. */
+
+static void
+obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
+{
+ char segname[17];
+ char sectname[17];
+ segT old_seg = now_seg;
+ segT new_seg;
+ symbolS *sym = NULL;
+ unsigned int align = 0;
+ unsigned int specified_mask = 0;
+ offsetT size = 0;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ /* Get the User's segment annd section names. */
+ if (! obj_mach_o_get_section_names (segname, sectname, 17, 17))
+ return;
+
+ /* Parse variable definition, if present. */
+ if (*input_line_pointer == ',')
+ {
+ /* Parse symbol, size [.align]
+ We follow the method of s_common_internal, with the difference
+ that the symbol cannot be a duplicate-common. */
+ char *name;
+ char c;
+ char *p;
+ expressionS exp;
+
+ input_line_pointer++; /* Skip ',' */
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+
+ if (name == p)
+ {
+ as_bad (_("expected symbol name"));
+ ignore_rest_of_line ();
+ goto done;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+
+ expression_and_evaluate (&exp);
+ if (exp.X_op != O_constant
+ && exp.X_op != O_absent)
+ {
+ as_bad (_("bad or irreducible absolute expression"));
+ ignore_rest_of_line ();
+ goto done;
+ }
+ else if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing size expression"));
+ ignore_rest_of_line ();
+ goto done;
+ }
+
+ size = exp.X_add_number;
+ size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1;
+ if (exp.X_add_number != size || !exp.X_unsigned)
+ {
+ as_warn (_("size (%ld) out of range, ignored"),
+ (long) exp.X_add_number);
+ ignore_rest_of_line ();
+ goto done;
+ }
+
+ *p = 0; /* Make the name into a c string for err messages. */
+ sym = symbol_find_or_make (name);
+ if (S_IS_DEFINED (sym) || symbol_equated_p (sym))
+ {
+ as_bad (_("symbol `%s' is already defined"), name);
+ *p = c;
+ ignore_rest_of_line ();
+ goto done;
+ }
+
+ size = S_GET_VALUE (sym);
+ if (size == 0)
+ size = exp.X_add_number;
+ else if (size != exp.X_add_number)
+ as_warn (_("size of \"%s\" is already %ld; not changing to %ld"),
+ name, (long) size, (long) exp.X_add_number);
+
+ *p = c; /* Restore the termination char. */
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ align = (unsigned int) parse_align (0);
+ if (align == (unsigned int) -1)
+ {
+ as_warn (_("align value not recognized, using size"));
+ align = size;
+ }
+ if (align > 15)
+ {
+ as_warn (_("Alignment (%lu) too large: 15 assumed."),
+ (unsigned long)align);
+ align = 15;
+ }
+ specified_mask |= SECT_ALGN_SPECIFIED;
+ }
+ }
+ /* else just a section definition. */
+
+ specified_mask |= SECT_TYPE_SPECIFIED;
+ new_seg = obj_mach_o_make_or_get_sect (segname, sectname, specified_mask,
+ BFD_MACH_O_S_ZEROFILL,
+ BFD_MACH_O_S_ATTR_NONE,
+ align, (offsetT) 0 /*stub size*/);
+ if (new_seg == NULL)
+ return;
+
+ /* In case the user specifies the bss section by mach-o name.
+ Create it on demand */
+ if (strcmp (new_seg->name, BSS_SECTION_NAME) == 0
+ && bss_section == NULL)
+ bss_section = new_seg;
+
+ subseg_set (new_seg, 0);
+
+ if (sym != NULL)
+ {
+ char *pfrag;
+
+ if (align)
+ {
+ record_alignment (new_seg, align);
+ frag_align (align, 0, 0);
+ }
+
+ /* Detach from old frag. */
+ if (S_GET_SEGMENT (sym) == new_seg)
+ symbol_get_frag (sym)->fr_symbol = NULL;
+
+ symbol_set_frag (sym, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, 0, sym, size, NULL);
+ *pfrag = 0;
+
+ S_SET_SEGMENT (sym, new_seg);
+ if (new_seg == bss_section)
+ S_CLEAR_EXTERNAL (sym);
+ }
+
+done:
+ /* switch back to the section that was current before the .zerofill. */
+ subseg_set (old_seg, 0);
+}
+
+static segT
+obj_mach_o_segT_from_bfd_name (const char *nam, int must_succeed)
+{
+ const mach_o_section_name_xlat *xlat;
+ const char *segn;
+ segT sec;
+
+ /* BFD has tables of flags and default attributes for all the sections that
+ have a 'canonical' name. */
+ xlat = bfd_mach_o_section_data_for_bfd_name (stdoutput, nam, &segn);
+ if (xlat == NULL)
+ {
+ if (must_succeed)
+ as_fatal (_("BFD is out of sync with GAS, "
+ "unhandled well-known section type `%s'"), nam);
+ return NULL;
+ }
+
+ sec = bfd_get_section_by_name (stdoutput, nam);
+ if (sec == NULL)
+ {
+ bfd_mach_o_section *msect;
+
+ sec = subseg_force_new (xlat->bfd_name, 0);
+
+ /* Set default type, attributes and alignment. */
+ msect = bfd_mach_o_get_mach_o_section (sec);
+ msect->flags = xlat->macho_sectype | xlat->macho_secattr;
+ msect->align = xlat->sectalign;
+
+ if ((msect->flags & BFD_MACH_O_SECTION_TYPE_MASK)
+ == BFD_MACH_O_S_ZEROFILL)
+ seg_info (sec)->bss = 1;
+ }
+
+ return sec;
+}
+
+static const char * const known_sections[] =
+{
+ /* 0 */ NULL,
+ /* __TEXT */
+ /* 1 */ ".const",
+ /* 2 */ ".static_const",
+ /* 3 */ ".cstring",
+ /* 4 */ ".literal4",
+ /* 5 */ ".literal8",
+ /* 6 */ ".literal16",
+ /* 7 */ ".constructor",
+ /* 8 */ ".destructor",
+ /* 9 */ ".eh_frame",
+ /* __DATA */
+ /* 10 */ ".const_data",
+ /* 11 */ ".static_data",
+ /* 12 */ ".mod_init_func",
+ /* 13 */ ".mod_term_func",
+ /* 14 */ ".dyld",
+ /* 15 */ ".cfstring"
+};
+
+/* Interface for a known non-optional section directive. */
+
+static void
+obj_mach_o_known_section (int sect_index)
+{
+ segT section;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ section = obj_mach_o_segT_from_bfd_name (known_sections[sect_index], 1);
+ if (section != NULL)
+ subseg_set (section, 0);
+
+ /* else, we leave the section as it was; there was a fatal error anyway. */
+}
+
+static const char * const objc_sections[] =
+{
+ /* 0 */ NULL,
+ /* 1 */ ".objc_class",
+ /* 2 */ ".objc_meta_class",
+ /* 3 */ ".objc_cat_cls_meth",
+ /* 4 */ ".objc_cat_inst_meth",
+ /* 5 */ ".objc_protocol",
+ /* 6 */ ".objc_string_object",
+ /* 7 */ ".objc_cls_meth",
+ /* 8 */ ".objc_inst_meth",
+ /* 9 */ ".objc_cls_refs",
+ /* 10 */ ".objc_message_refs",
+ /* 11 */ ".objc_symbols",
+ /* 12 */ ".objc_category",
+ /* 13 */ ".objc_class_vars",
+ /* 14 */ ".objc_instance_vars",
+ /* 15 */ ".objc_module_info",
+ /* 16 */ ".cstring", /* objc_class_names Alias for .cstring */
+ /* 17 */ ".cstring", /* Alias objc_meth_var_types for .cstring */
+ /* 18 */ ".cstring", /* objc_meth_var_names Alias for .cstring */
+ /* 19 */ ".objc_selector_strs",
+ /* 20 */ ".objc_image_info", /* extension. */
+ /* 21 */ ".objc_selector_fixup", /* extension. */
+ /* 22 */ ".objc1_class_ext", /* ObjC-1 extension. */
+ /* 23 */ ".objc1_property_list", /* ObjC-1 extension. */
+ /* 24 */ ".objc1_protocol_ext" /* ObjC-1 extension. */
+};
+
+/* This currently does the same as known_sections, but kept separate for
+ ease of maintenance. */
+
+static void
+obj_mach_o_objc_section (int sect_index)
+{
+ segT section;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ section = obj_mach_o_segT_from_bfd_name (objc_sections[sect_index], 1);
+ if (section != NULL)
+ {
+ obj_mach_o_seen_objc_section = 1; /* We need to ensure that certain
+ sections are present and in the
+ right order. */
+ subseg_set (section, 0);
+ }
+
+ /* else, we leave the section as it was; there was a fatal error anyway. */
+}
+
+/* Debug section directives. */
+
+static const char * const debug_sections[] =
+{
+ /* 0 */ NULL,
+ /* __DWARF */
+ /* 1 */ ".debug_frame",
+ /* 2 */ ".debug_info",
+ /* 3 */ ".debug_abbrev",
+ /* 4 */ ".debug_aranges",
+ /* 5 */ ".debug_macinfo",
+ /* 6 */ ".debug_line",
+ /* 7 */ ".debug_loc",
+ /* 8 */ ".debug_pubnames",
+ /* 9 */ ".debug_pubtypes",
+ /* 10 */ ".debug_str",
+ /* 11 */ ".debug_ranges",
+ /* 12 */ ".debug_macro"
+};
+
+/* ??? Maybe these should be conditional on gdwarf-*.
+ It`s also likely that we will need to be able to set them from the cfi
+ code. */
+
+static void
+obj_mach_o_debug_section (int sect_index)
+{
+ segT section;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ section = obj_mach_o_segT_from_bfd_name (debug_sections[sect_index], 1);
+ if (section != NULL)
+ subseg_set (section, 0);
+
+ /* else, we leave the section as it was; there was a fatal error anyway. */
+}
+
+/* This could be moved to the tc-xx files, but there is so little dependency
+ there, that the code might as well be shared. */
+
+struct opt_tgt_sect
+{
+ const char *name;
+ unsigned x86_val;
+ unsigned ppc_val;
+};
+
+/* The extensions here are for specific sections that are generated by GCC
+ and Darwin system tools, but don't have directives in the `system as'. */
+
+static const struct opt_tgt_sect tgt_sections[] =
+{
+ /* 0 */ { NULL, 0, 0},
+ /* 1 */ { ".lazy_symbol_pointer", 0, 0},
+ /* 2 */ { ".lazy_symbol_pointer2", 0, 0}, /* X86 - extension */
+ /* 3 */ { ".lazy_symbol_pointer3", 0, 0}, /* X86 - extension */
+ /* 4 */ { ".non_lazy_symbol_pointer", 0, 0},
+ /* 5 */ { ".non_lazy_symbol_pointer_x86", 0, 0}, /* X86 - extension */
+ /* 6 */ { ".symbol_stub", 16, 20},
+ /* 7 */ { ".symbol_stub1", 0, 16}, /* PPC - extension */
+ /* 8 */ { ".picsymbol_stub", 26, 36},
+ /* 9 */ { ".picsymbol_stub1", 0, 32}, /* PPC - extension */
+ /* 10 */ { ".picsymbol_stub2", 25, 0}, /* X86 - extension */
+ /* 11 */ { ".picsymbol_stub3", 5, 0}, /* X86 - extension */
+};
+
+/* Interface for an optional section directive. */
+
+static void
+obj_mach_o_opt_tgt_section (int sect_index)
+{
+ const struct opt_tgt_sect *tgtsct = &tgt_sections[sect_index];
+ segT section;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ section = obj_mach_o_segT_from_bfd_name (tgtsct->name, 0);
+ if (section == NULL)
+ {
+ as_bad (_("%s is not used for the selected target"), tgtsct->name);
+ /* Leave the section as it is. */
+ }
+ else
+ {
+ bfd_mach_o_section *mo_sec = bfd_mach_o_get_mach_o_section (section);
+ subseg_set (section, 0);
+#if defined (TC_I386)
+ mo_sec->reserved2 = tgtsct->x86_val;
+#elif defined (TC_PPC)
+ mo_sec->reserved2 = tgtsct->ppc_val;
+#else
+ mo_sec->reserved2 = 0;
+#endif
+ }
+}
+
+/* We don't necessarily have the three 'base' sections on mach-o.
+ Normally, we would start up with only the 'text' section defined.
+ However, even that can be suppressed with (TODO) c/l option "-n".
+ Thus, we have to be able to create all three sections on-demand. */
+
+static void
+obj_mach_o_base_section (int sect_index)
+{
+ segT section;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ /* We don't support numeric (or any other) qualifications on the
+ well-known section shorthands. */
+ demand_empty_rest_of_line ();
+
+ switch (sect_index)
+ {
+ /* Handle the three sections that are globally known within GAS.
+ For Mach-O, these are created on demand rather than at startup. */
+ case 1:
+ if (text_section == NULL)
+ text_section = obj_mach_o_segT_from_bfd_name (TEXT_SECTION_NAME, 1);
+ if (obj_mach_o_is_static)
+ {
+ bfd_mach_o_section *mo_sec
+ = bfd_mach_o_get_mach_o_section (text_section);
+ mo_sec->flags &= ~BFD_MACH_O_S_ATTR_PURE_INSTRUCTIONS;
+ }
+ section = text_section;
+ break;
+ case 2:
+ if (data_section == NULL)
+ data_section = obj_mach_o_segT_from_bfd_name (DATA_SECTION_NAME, 1);
+ section = data_section;
+ break;
+ case 3:
+ /* ??? maybe this achieves very little, as an addition. */
+ if (bss_section == NULL)
+ {
+ bss_section = obj_mach_o_segT_from_bfd_name (BSS_SECTION_NAME, 1);
+ seg_info (bss_section)->bss = 1;
+ }
+ section = bss_section;
+ break;
+ default:
+ as_fatal (_("internal error: base section index out of range"));
+ return;
+ break;
+ }
+ subseg_set (section, 0);
+}
+
+/* This finishes off parsing a .comm or .lcomm statement, which both can have
+ an (optional) alignment field. It also allows us to create the bss section
+ on demand. */
+
+static symbolS *
+obj_mach_o_common_parse (int is_local, symbolS *symbolP,
+ addressT size)
+{
+ addressT align = 0;
+ bfd_mach_o_asymbol *s;
+
+ SKIP_WHITESPACE ();
+
+ /* Both comm and lcomm take an optional alignment, as a power
+ of two between 1 and 15. */
+ if (*input_line_pointer == ',')
+ {
+ /* We expect a power of 2. */
+ align = parse_align (0);
+ if (align == (addressT) -1)
+ return NULL;
+ if (align > 15)
+ {
+ as_warn (_("Alignment (%lu) too large: 15 assumed."),
+ (unsigned long)align);
+ align = 15;
+ }
+ }
+
+ s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (symbolP);
+ if (is_local)
+ {
+ /* Create the BSS section on demand. */
+ if (bss_section == NULL)
+ {
+ bss_section = obj_mach_o_segT_from_bfd_name (BSS_SECTION_NAME, 1);
+ seg_info (bss_section)->bss = 1;
+ }
+ bss_alloc (symbolP, size, align);
+ s->n_type = BFD_MACH_O_N_SECT;
+ S_CLEAR_EXTERNAL (symbolP);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, size);
+ S_SET_ALIGN (symbolP, align);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ s->n_type = BFD_MACH_O_N_UNDF | BFD_MACH_O_N_EXT;
+ }
+
+ /* This is a data object (whatever we choose that to mean). */
+ s->symbol.flags |= BSF_OBJECT;
+
+ /* We've set symbol qualifiers, so validate if you can. */
+ s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
+
+ return symbolP;
+}
+
+static void
+obj_mach_o_comm (int is_local)
+{
+ s_comm_internal (is_local, obj_mach_o_common_parse);
+}
+
+/* Set properties that apply to the whole file. At present, the only
+ one defined, is subsections_via_symbols. */
+
+typedef enum obj_mach_o_file_properties {
+ OBJ_MACH_O_FILE_PROP_NONE = 0,
+ OBJ_MACH_O_FILE_PROP_SUBSECTS_VIA_SYMS,
+ OBJ_MACH_O_FILE_PROP_MAX
+} obj_mach_o_file_properties;
+
+static void
+obj_mach_o_fileprop (int prop)
+{
+ if (prop < 0 || prop >= OBJ_MACH_O_FILE_PROP_MAX)
+ as_fatal (_("internal error: bad file property ID %d"), prop);
+
+ switch ((obj_mach_o_file_properties) prop)
+ {
+ case OBJ_MACH_O_FILE_PROP_SUBSECTS_VIA_SYMS:
+ obj_mach_o_subsections_by_symbols = 1;
+ if (!bfd_set_private_flags (stdoutput,
+ BFD_MACH_O_MH_SUBSECTIONS_VIA_SYMBOLS))
+ as_bad (_("failed to set subsections by symbols"));
+ demand_empty_rest_of_line ();
+ break;
+ default:
+ break;
+ }
+}
+
+/* Temporary markers for symbol reference data.
+ Lazy will remain in place. */
+#define LAZY 0x01
+#define REFE 0x02
+
+/* We have a bunch of qualifiers that may be applied to symbols.
+ .globl is handled here so that we might make sure that conflicting qualifiers
+ are caught where possible. */
+
+typedef enum obj_mach_o_symbol_type {
+ OBJ_MACH_O_SYM_UNK = 0,
+ OBJ_MACH_O_SYM_LOCAL = 1,
+ OBJ_MACH_O_SYM_GLOBL = 2,
+ OBJ_MACH_O_SYM_REFERENCE = 3,
+ OBJ_MACH_O_SYM_WEAK_REF = 4,
+ OBJ_MACH_O_SYM_LAZY_REF = 5,
+ OBJ_MACH_O_SYM_WEAK_DEF = 6,
+ OBJ_MACH_O_SYM_PRIV_EXT = 7,
+ OBJ_MACH_O_SYM_NO_DEAD_STRIP = 8,
+ OBJ_MACH_O_SYM_WEAK = 9
+} obj_mach_o_symbol_type;
+
+/* Set Mach-O-specific symbol qualifiers. */
+
+static int
+obj_mach_o_set_symbol_qualifier (symbolS *sym, int type)
+{
+ int is_defined;
+ bfd_mach_o_asymbol *s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sym);
+ bfd_mach_o_section *sec;
+ int sectype = -1;
+
+ /* If the symbol is defined, then we can do more rigorous checking on
+ the validity of the qualifiers. Otherwise, we are stuck with waiting
+ until it's defined - or until write the file.
+
+ In certain cases (e.g. when a symbol qualifier is intended to introduce
+ an undefined symbol in a stubs section) we should check that the current
+ section is appropriate to the qualifier. */
+
+ is_defined = s->symbol.section != bfd_und_section_ptr;
+ if (is_defined)
+ sec = bfd_mach_o_get_mach_o_section (s->symbol.section) ;
+ else
+ sec = bfd_mach_o_get_mach_o_section (now_seg) ;
+
+ if (sec != NULL)
+ sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
+
+ switch ((obj_mach_o_symbol_type) type)
+ {
+ case OBJ_MACH_O_SYM_LOCAL:
+ /* This is an extension over the system tools. */
+ if (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT))
+ {
+ as_bad (_("'%s' previously declared as '%s'."), s->symbol.name,
+ (s->n_type & BFD_MACH_O_N_PEXT) ? "private extern"
+ : "global" );
+ s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+ return 1;
+ }
+ else
+ {
+ s->n_type &= ~BFD_MACH_O_N_EXT;
+ S_CLEAR_EXTERNAL (sym);
+ }
+ break;
+
+ case OBJ_MACH_O_SYM_PRIV_EXT:
+ s->n_type |= BFD_MACH_O_N_PEXT ;
+ s->n_desc &= ~LAZY; /* The native tool switches this off too. */
+ /* We follow the system tools in marking PEXT as also global. */
+ /* Fall through. */
+
+ case OBJ_MACH_O_SYM_GLOBL:
+ /* It's not an error to define a symbol and then make it global. */
+ s->n_type |= BFD_MACH_O_N_EXT;
+ S_SET_EXTERNAL (sym);
+ break;
+
+ case OBJ_MACH_O_SYM_REFERENCE:
+ if (is_defined)
+ s->n_desc |= BFD_MACH_O_N_NO_DEAD_STRIP;
+ else
+ s->n_desc |= (REFE | BFD_MACH_O_N_NO_DEAD_STRIP);
+ break;
+
+ case OBJ_MACH_O_SYM_LAZY_REF:
+ if (is_defined)
+ s->n_desc |= BFD_MACH_O_N_NO_DEAD_STRIP;
+ else
+ s->n_desc |= (REFE | LAZY | BFD_MACH_O_N_NO_DEAD_STRIP);
+ break;
+
+ /* Force ld to retain the symbol - even if it appears unused. */
+ case OBJ_MACH_O_SYM_NO_DEAD_STRIP:
+ s->n_desc |= BFD_MACH_O_N_NO_DEAD_STRIP ;
+ break;
+
+ /* Mach-O's idea of weak ... */
+ case OBJ_MACH_O_SYM_WEAK_REF:
+ s->n_desc |= BFD_MACH_O_N_WEAK_REF ;
+ break;
+
+ case OBJ_MACH_O_SYM_WEAK_DEF:
+ if (is_defined && sectype != BFD_MACH_O_S_COALESCED)
+ {
+ as_bad (_("'%s' can't be a weak_definition (currently only"
+ " supported in sections of type coalesced)"),
+ s->symbol.name);
+ s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+ return 1;
+ }
+ else
+ s->n_desc |= BFD_MACH_O_N_WEAK_DEF;
+ break;
+
+ case OBJ_MACH_O_SYM_WEAK:
+ /* A generic 'weak' - we try to figure out what it means at
+ symbol frob time. */
+ S_SET_WEAK (sym);
+ break;
+
+ default:
+ break;
+ }
+
+ /* We've seen some kind of qualifier - check validity if or when the entity
+ is defined. */
+ s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
+ return 0;
+}
+
+/* Respond to symbol qualifiers.
+ All of the form:
+ .<qualifier> symbol [, symbol]*
+ a list of symbols is an extension over the Darwin system as. */
+
+static void
+obj_mach_o_sym_qual (int ntype)
+{
+ char *name;
+ char c;
+ symbolS *symbolP;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ obj_mach_o_set_symbol_qualifier (symbolP, ntype);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+typedef struct obj_mach_o_indirect_sym
+{
+ symbolS *sym;
+ segT sect;
+ struct obj_mach_o_indirect_sym *next;
+} obj_mach_o_indirect_sym;
+
+/* We store in order an maintain a pointer to the last one - to save reversing
+ later. */
+obj_mach_o_indirect_sym *indirect_syms;
+obj_mach_o_indirect_sym *indirect_syms_tail;
+
+static void
+obj_mach_o_indirect_symbol (int arg ATTRIBUTE_UNUSED)
+{
+ bfd_mach_o_section *sec = bfd_mach_o_get_mach_o_section (now_seg);
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (obj_mach_o_is_static)
+ as_bad (_("use of .indirect_symbols requires `-dynamic'"));
+
+ switch (sec->flags & BFD_MACH_O_SECTION_TYPE_MASK)
+ {
+ case BFD_MACH_O_S_SYMBOL_STUBS:
+ case BFD_MACH_O_S_LAZY_SYMBOL_POINTERS:
+ case BFD_MACH_O_S_NON_LAZY_SYMBOL_POINTERS:
+ {
+ obj_mach_o_indirect_sym *isym;
+ char *name = input_line_pointer;
+ char c = get_symbol_end ();
+ symbolS *sym = symbol_find_or_make (name);
+ unsigned int elsize =
+ bfd_mach_o_section_get_entry_size (stdoutput, sec);
+
+ if (elsize == 0)
+ {
+ as_bad (_("attempt to add an indirect_symbol to a stub or"
+ " reference section with a zero-sized element at %s"),
+ name);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ /* The indirect symbols are validated after the symbol table is
+ frozen, we must make sure that if a local symbol is used as an
+ indirect, it is promoted to a 'real' one. Fetching the bfd sym
+ achieves this. */
+ symbol_get_bfdsym (sym);
+ isym = (obj_mach_o_indirect_sym *)
+ xmalloc (sizeof (obj_mach_o_indirect_sym));
+
+ /* Just record the data for now, we will validate it when we
+ compute the output in obj_mach_o_set_indirect_symbols. */
+ isym->sym = sym;
+ isym->sect = now_seg;
+ isym->next = NULL;
+ if (indirect_syms == NULL)
+ indirect_syms = isym;
+ else
+ indirect_syms_tail->next = isym;
+ indirect_syms_tail = isym;
+ }
+ break;
+
+ default:
+ as_bad (_("an .indirect_symbol must be in a symbol pointer"
+ " or stub section."));
+ ignore_rest_of_line ();
+ return;
+ }
+ demand_empty_rest_of_line ();
+}
+
+const pseudo_typeS mach_o_pseudo_table[] =
+{
+ /* Section directives. */
+ { "comm", obj_mach_o_comm, 0 },
+ { "lcomm", obj_mach_o_comm, 1 },
+
+ { "text", obj_mach_o_base_section, 1},
+ { "data", obj_mach_o_base_section, 2},
+ { "bss", obj_mach_o_base_section, 3}, /* extension */
+
+ { "const", obj_mach_o_known_section, 1},
+ { "static_const", obj_mach_o_known_section, 2},
+ { "cstring", obj_mach_o_known_section, 3},
+ { "literal4", obj_mach_o_known_section, 4},
+ { "literal8", obj_mach_o_known_section, 5},
+ { "literal16", obj_mach_o_known_section, 6},
+ { "constructor", obj_mach_o_known_section, 7},
+ { "destructor", obj_mach_o_known_section, 8},
+ { "eh_frame", obj_mach_o_known_section, 9},
+
+ { "const_data", obj_mach_o_known_section, 10},
+ { "static_data", obj_mach_o_known_section, 11},
+ { "mod_init_func", obj_mach_o_known_section, 12},
+ { "mod_term_func", obj_mach_o_known_section, 13},
+ { "dyld", obj_mach_o_known_section, 14},
+ { "cfstring", obj_mach_o_known_section, 15},
+
+ { "objc_class", obj_mach_o_objc_section, 1},
+ { "objc_meta_class", obj_mach_o_objc_section, 2},
+ { "objc_cat_cls_meth", obj_mach_o_objc_section, 3},
+ { "objc_cat_inst_meth", obj_mach_o_objc_section, 4},
+ { "objc_protocol", obj_mach_o_objc_section, 5},
+ { "objc_string_object", obj_mach_o_objc_section, 6},
+ { "objc_cls_meth", obj_mach_o_objc_section, 7},
+ { "objc_inst_meth", obj_mach_o_objc_section, 8},
+ { "objc_cls_refs", obj_mach_o_objc_section, 9},
+ { "objc_message_refs", obj_mach_o_objc_section, 10},
+ { "objc_symbols", obj_mach_o_objc_section, 11},
+ { "objc_category", obj_mach_o_objc_section, 12},
+ { "objc_class_vars", obj_mach_o_objc_section, 13},
+ { "objc_instance_vars", obj_mach_o_objc_section, 14},
+ { "objc_module_info", obj_mach_o_objc_section, 15},
+ { "objc_class_names", obj_mach_o_objc_section, 16}, /* Alias for .cstring */
+ { "objc_meth_var_types", obj_mach_o_objc_section, 17}, /* Alias for .cstring */
+ { "objc_meth_var_names", obj_mach_o_objc_section, 18}, /* Alias for .cstring */
+ { "objc_selector_strs", obj_mach_o_objc_section, 19},
+ { "objc_image_info", obj_mach_o_objc_section, 20}, /* extension. */
+ { "objc_selector_fixup", obj_mach_o_objc_section, 21}, /* extension. */
+ { "objc1_class_ext", obj_mach_o_objc_section, 22}, /* ObjC-1 extension. */
+ { "objc1_property_list", obj_mach_o_objc_section, 23}, /* ObjC-1 extension. */
+ { "objc1_protocol_ext", obj_mach_o_objc_section, 24}, /* ObjC-1 extension. */
+
+ { "debug_frame", obj_mach_o_debug_section, 1}, /* extension. */
+ { "debug_info", obj_mach_o_debug_section, 2}, /* extension. */
+ { "debug_abbrev", obj_mach_o_debug_section, 3}, /* extension. */
+ { "debug_aranges", obj_mach_o_debug_section, 4}, /* extension. */
+ { "debug_macinfo", obj_mach_o_debug_section, 5}, /* extension. */
+ { "debug_line", obj_mach_o_debug_section, 6}, /* extension. */
+ { "debug_loc", obj_mach_o_debug_section, 7}, /* extension. */
+ { "debug_pubnames", obj_mach_o_debug_section, 8}, /* extension. */
+ { "debug_pubtypes", obj_mach_o_debug_section, 9}, /* extension. */
+ { "debug_str", obj_mach_o_debug_section, 10}, /* extension. */
+ { "debug_ranges", obj_mach_o_debug_section, 11}, /* extension. */
+ { "debug_macro", obj_mach_o_debug_section, 12}, /* extension. */
+
+ { "lazy_symbol_pointer", obj_mach_o_opt_tgt_section, 1},
+ { "lazy_symbol_pointer2", obj_mach_o_opt_tgt_section, 2}, /* extension. */
+ { "lazy_symbol_pointer3", obj_mach_o_opt_tgt_section, 3}, /* extension. */
+ { "non_lazy_symbol_pointer", obj_mach_o_opt_tgt_section, 4},
+ { "non_lazy_symbol_pointer_x86", obj_mach_o_opt_tgt_section, 5}, /* extension. */
+ { "symbol_stub", obj_mach_o_opt_tgt_section, 6},
+ { "symbol_stub1", obj_mach_o_opt_tgt_section, 7}, /* extension. */
+ { "picsymbol_stub", obj_mach_o_opt_tgt_section, 8}, /* extension. */
+ { "picsymbol_stub1", obj_mach_o_opt_tgt_section, 9}, /* extension. */
+ { "picsymbol_stub2", obj_mach_o_opt_tgt_section, 4}, /* extension. */
+ { "picsymbol_stub3", obj_mach_o_opt_tgt_section, 4}, /* extension. */
+
+ { "section", obj_mach_o_section, 0},
+ { "zerofill", obj_mach_o_zerofill, 0},
+
+ /* Symbol qualifiers. */
+ {"local", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_LOCAL},
+ {"globl", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_GLOBL},
+ {"reference", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_REFERENCE},
+ {"weak_reference", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_WEAK_REF},
+ {"lazy_reference", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_LAZY_REF},
+ {"weak_definition", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_WEAK_DEF},
+ {"private_extern", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_PRIV_EXT},
+ {"no_dead_strip", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_NO_DEAD_STRIP},
+ {"weak", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_WEAK}, /* ext */
+
+ { "indirect_symbol", obj_mach_o_indirect_symbol, 0},
+
+ /* File flags. */
+ { "subsections_via_symbols", obj_mach_o_fileprop,
+ OBJ_MACH_O_FILE_PROP_SUBSECTS_VIA_SYMS},
+
+ {NULL, NULL, 0}
+};
+
+/* Determine the default n_type value for a symbol from its section. */
+
+static unsigned
+obj_mach_o_type_for_symbol (bfd_mach_o_asymbol *s)
+{
+ if (s->symbol.section == bfd_abs_section_ptr)
+ return BFD_MACH_O_N_ABS;
+ else if (s->symbol.section == bfd_com_section_ptr
+ || s->symbol.section == bfd_und_section_ptr)
+ return BFD_MACH_O_N_UNDF;
+ else
+ return BFD_MACH_O_N_SECT;
+}
+
+void
+obj_mach_o_frob_colon (const char *name)
+{
+ if (!bfd_is_local_label_name (stdoutput, name))
+ {
+ /* A non-local label will create a new subsection, so start a new
+ frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+}
+
+/* We need to check the correspondence between some kinds of symbols and their
+ sections. Common and BSS vars will seen via the obj_macho_comm() function.
+
+ The earlier we can pick up a problem, the better the diagnostics will be.
+
+ However, when symbol type information is attached, the symbol section will
+ quite possibly be unknown. So we are stuck with checking (most of the)
+ validity at the time the file is written (unfortunately, then one doesn't
+ get line number information in the diagnostic). */
+
+/* Here we pick up the case where symbol qualifiers have been applied that
+ are possibly incompatible with the section etc. that the symbol is defined
+ in. */
+
+void obj_mach_o_frob_label (struct symbol *sp)
+{
+ bfd_mach_o_asymbol *s;
+ unsigned base_type;
+ bfd_mach_o_section *sec;
+ int sectype = -1;
+
+ if (!bfd_is_local_label_name (stdoutput, S_GET_NAME (sp)))
+ {
+ /* If this is a non-local label, it should have started a new sub-
+ section. */
+ gas_assert (frag_now->obj_frag_data.subsection == NULL);
+ frag_now->obj_frag_data.subsection = sp;
+ }
+
+ /* Leave local symbols alone. */
+
+ if (S_IS_LOCAL (sp))
+ return;
+
+ s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sp);
+ /* Leave debug symbols alone. */
+ if ((s->n_type & BFD_MACH_O_N_STAB) != 0)
+ return;
+
+ /* This is the base symbol type, that we mask in. */
+ base_type = obj_mach_o_type_for_symbol (s);
+
+ sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
+ if (sec != NULL)
+ sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
+
+ /* If there is a pre-existing qualifier, we can make some checks about
+ validity now. */
+
+ if(s->symbol.udata.i == SYM_MACHO_FIELDS_NOT_VALIDATED)
+ {
+ if ((s->n_desc & BFD_MACH_O_N_WEAK_DEF)
+ && sectype != BFD_MACH_O_S_COALESCED)
+ {
+ as_bad (_("'%s' can't be a weak_definition (currently only supported"
+ " in sections of type coalesced)"), s->symbol.name);
+ /* Don't cascade errors. */
+ s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+ }
+
+ /* Have we changed from an undefined to defined ref? */
+ s->n_desc &= ~(REFE | LAZY);
+ }
+
+ s->n_type &= ~BFD_MACH_O_N_TYPE;
+ s->n_type |= base_type;
+}
+
+/* This is the fall-back, we come here when we get to the end of the file and
+ the symbol is not defined - or there are combinations of qualifiers required
+ (e.g. global + weak_def). */
+
+int
+obj_mach_o_frob_symbol (struct symbol *sp)
+{
+ bfd_mach_o_asymbol *s;
+ unsigned base_type;
+ bfd_mach_o_section *sec;
+ int sectype = -1;
+
+ /* Leave local symbols alone. */
+ if (S_IS_LOCAL (sp))
+ return 0;
+
+ s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sp);
+ /* Leave debug symbols alone. */
+ if ((s->n_type & BFD_MACH_O_N_STAB) != 0)
+ return 0;
+
+ base_type = obj_mach_o_type_for_symbol (s);
+ sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
+ if (sec != NULL)
+ sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
+
+ if (s->symbol.section == bfd_und_section_ptr)
+ {
+ /* ??? Do we really gain much from implementing this as well as the
+ mach-o specific ones? */
+ if (s->symbol.flags & BSF_WEAK)
+ s->n_desc |= BFD_MACH_O_N_WEAK_REF;
+
+ /* Undefined syms, become extern. */
+ s->n_type |= BFD_MACH_O_N_EXT;
+ S_SET_EXTERNAL (sp);
+ }
+ else if (s->symbol.section == bfd_com_section_ptr)
+ {
+ /* ... so do comm. */
+ s->n_type |= BFD_MACH_O_N_EXT;
+ S_SET_EXTERNAL (sp);
+ }
+ else
+ {
+ if ((s->symbol.flags & BSF_WEAK)
+ && (sectype == BFD_MACH_O_S_COALESCED)
+ && (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT)))
+ s->n_desc |= BFD_MACH_O_N_WEAK_DEF;
+/* ??? we should do this - but then that reveals that the semantics of weak
+ are different from what's supported in mach-o object files.
+ else
+ as_bad (_("'%s' can't be a weak_definition."),
+ s->symbol.name); */
+ }
+
+ if (s->symbol.udata.i == SYM_MACHO_FIELDS_UNSET)
+ {
+ /* Anything here that should be added that is non-standard. */
+ s->n_desc &= ~BFD_MACH_O_REFERENCE_MASK;
+ }
+ else if (s->symbol.udata.i == SYM_MACHO_FIELDS_NOT_VALIDATED)
+ {
+ /* Try to validate any combinations. */
+ if (s->n_desc & BFD_MACH_O_N_WEAK_DEF)
+ {
+ if (s->symbol.section == bfd_und_section_ptr)
+ as_bad (_("'%s' can't be a weak_definition (since it is"
+ " undefined)"), s->symbol.name);
+ else if (sectype != BFD_MACH_O_S_COALESCED)
+ as_bad (_("'%s' can't be a weak_definition (currently only supported"
+ " in sections of type coalesced)"), s->symbol.name);
+ else if (! (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT)))
+ as_bad (_("Non-global symbol: '%s' can't be a weak_definition."),
+ s->symbol.name);
+ }
+
+ }
+ else
+ as_bad (_("internal error: [%s] unexpected code [%lx] in frob symbol"),
+ s->symbol.name, (unsigned long)s->symbol.udata.i);
+
+ s->n_type &= ~BFD_MACH_O_N_TYPE;
+ s->n_type |= base_type;
+
+ if (s->symbol.flags & BSF_GLOBAL)
+ s->n_type |= BFD_MACH_O_N_EXT;
+
+ /* This cuts both ways - we promote some things to external above. */
+ if (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT))
+ S_SET_EXTERNAL (sp);
+
+ return 0;
+}
+
+/* Support stabs for mach-o. */
+
+void
+obj_mach_o_process_stab (int what, const char *string,
+ int type, int other, int desc)
+{
+ symbolS *symbolP;
+ bfd_mach_o_asymbol *s;
+
+ switch (what)
+ {
+ case 'd':
+ symbolP = symbol_new ("", now_seg, frag_now_fix (), frag_now);
+ /* Special stabd NULL name indicator. */
+ S_SET_NAME (symbolP, NULL);
+ break;
+
+ case 'n':
+ case 's':
+ symbolP = symbol_new (string, undefined_section, (valueT) 0,
+ &zero_address_frag);
+ pseudo_set (symbolP);
+ break;
+
+ default:
+ as_bad(_("unrecognized stab type '%c'"), (char)what);
+ abort ();
+ break;
+ }
+
+ s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (symbolP);
+ s->n_type = type;
+ s->n_desc = desc;
+ /* For stabd, this will eventually get overwritten by the section number. */
+ s->n_sect = other;
+
+ /* It's a debug symbol. */
+ s->symbol.flags |= BSF_DEBUGGING;
+
+ /* We've set it - so check it, if you can, but don't try to create the
+ flags. */
+ s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
+}
+
+/* This is a place to check for any errors that we can't detect until we know
+ what remains undefined at the end of assembly. */
+
+static void
+obj_mach_o_check_before_writing (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ fixS *fixP;
+ struct frchain *frchp;
+ segment_info_type *seginfo = seg_info (sec);
+
+ if (seginfo == NULL)
+ return;
+
+ /* We are not allowed subtractions where either of the operands is
+ undefined. So look through the frags for any fixes to check. */
+ for (frchp = seginfo->frchainP; frchp != NULL; frchp = frchp->frch_next)
+ for (fixP = frchp->fix_root; fixP != NULL; fixP = fixP->fx_next)
+ {
+ if (fixP->fx_addsy != NULL
+ && fixP->fx_subsy != NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || ! S_IS_DEFINED (fixP->fx_subsy)))
+ {
+ segT add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy);
+ segT sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy);
+
+ if (! S_IS_DEFINED (fixP->fx_addsy)
+ && S_IS_DEFINED (fixP->fx_subsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("`%s' can't be undefined in `%s' - `%s' {%s section}"),
+ S_GET_NAME (fixP->fx_addsy), S_GET_NAME (fixP->fx_addsy),
+ S_GET_NAME (fixP->fx_subsy), segment_name (sub_symbol_segment));
+ }
+ else if (! S_IS_DEFINED (fixP->fx_subsy)
+ && S_IS_DEFINED (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("`%s' can't be undefined in `%s' {%s section} - `%s'"),
+ S_GET_NAME (fixP->fx_subsy), S_GET_NAME (fixP->fx_addsy),
+ segment_name (add_symbol_segment), S_GET_NAME (fixP->fx_subsy));
+ }
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("`%s' and `%s' can't be undefined in `%s' - `%s'"),
+ S_GET_NAME (fixP->fx_addsy), S_GET_NAME (fixP->fx_subsy),
+ S_GET_NAME (fixP->fx_addsy), S_GET_NAME (fixP->fx_subsy));
+ }
+ }
+ }
+}
+
+/* Do any checks that we can't complete without knowing what's undefined. */
+void
+obj_mach_o_pre_output_hook (void)
+{
+ bfd_map_over_sections (stdoutput, obj_mach_o_check_before_writing, (char *) 0);
+}
+
+/* Here we count up frags in each subsection (where a sub-section is defined
+ as starting with a non-local symbol).
+ Note that, if there are no non-local symbols in a section, all the frags will
+ be attached as one anonymous subsection. */
+
+static void
+obj_mach_o_set_subsections (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ symbolS *cur_subsection = NULL;
+ struct obj_mach_o_symbol_data *cur_subsection_data = NULL;
+ fragS *frag;
+ frchainS *chain;
+
+ /* Protect against sections not created by gas. */
+ if (seginfo == NULL)
+ return;
+
+ /* Attach every frag to a subsection. */
+ for (chain = seginfo->frchainP; chain != NULL; chain = chain->frch_next)
+ for (frag = chain->frch_root; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->obj_frag_data.subsection == NULL)
+ frag->obj_frag_data.subsection = cur_subsection;
+ else
+ {
+ cur_subsection = frag->obj_frag_data.subsection;
+ cur_subsection_data = symbol_get_obj (cur_subsection);
+ cur_subsection_data->subsection_size = 0;
+ }
+ if (cur_subsection_data != NULL)
+ {
+ /* Update subsection size. */
+ cur_subsection_data->subsection_size += frag->fr_fix;
+ }
+ }
+}
+
+/* Handle mach-o subsections-via-symbols counting up frags belonging to each
+ sub-section. */
+
+void
+obj_mach_o_pre_relax_hook (void)
+{
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_subsections, (char *) 0);
+}
+
+/* Zerofill and GB Zerofill sections must be sorted to follow all other
+ sections in their segments.
+
+ The native 'as' leaves the sections physically in the order they appear in
+ the source, and adjusts the section VMAs to meet the constraint.
+
+ We follow this for now - if nothing else, it makes comparison easier.
+
+ An alternative implementation would be to sort the sections as ld requires.
+ It might be advantageous to implement such a scheme in the future (or even
+ to make the style of section ordering user-selectable). */
+
+typedef struct obj_mach_o_set_vma_data
+{
+ bfd_vma vma;
+ unsigned vma_pass;
+ unsigned zerofill_seen;
+ unsigned gb_zerofill_seen;
+} obj_mach_o_set_vma_data;
+
+/* We do (possibly) three passes through to set the vma, so that:
+
+ zerofill sections get VMAs after all others in their segment
+ GB zerofill get VMAs last.
+
+ As we go, we notice if we see any Zerofill or GB Zerofill sections, so that
+ we can skip the additional passes if there's nothing to do. */
+
+static void
+obj_mach_o_set_section_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *v_p)
+{
+ bfd_mach_o_section *ms = bfd_mach_o_get_mach_o_section (sec);
+ unsigned bfd_align = bfd_get_section_alignment (abfd, sec);
+ obj_mach_o_set_vma_data *p = (struct obj_mach_o_set_vma_data *)v_p;
+ unsigned sectype = (ms->flags & BFD_MACH_O_SECTION_TYPE_MASK);
+ unsigned zf;
+
+ zf = 0;
+ if (sectype == BFD_MACH_O_S_ZEROFILL)
+ {
+ zf = 1;
+ p->zerofill_seen = zf;
+ }
+ else if (sectype == BFD_MACH_O_S_GB_ZEROFILL)
+ {
+ zf = 2;
+ p->gb_zerofill_seen = zf;
+ }
+
+ if (p->vma_pass != zf)
+ return;
+
+ /* We know the section size now - so make a vma for the section just
+ based on order. */
+ ms->size = bfd_get_section_size (sec);
+
+ /* Make sure that the align agrees, and set to the largest value chosen. */
+ ms->align = ms->align > bfd_align ? ms->align : bfd_align;
+ bfd_set_section_alignment (abfd, sec, ms->align);
+
+ p->vma += (1 << ms->align) - 1;
+ p->vma &= ~((1 << ms->align) - 1);
+ ms->addr = p->vma;
+ bfd_set_section_vma (abfd, sec, p->vma);
+ p->vma += ms->size;
+}
+
+/* (potentially) three passes over the sections, setting VMA. We skip the
+ {gb}zerofill passes if we didn't see any of the relevant sections. */
+
+void obj_mach_o_post_relax_hook (void)
+{
+ obj_mach_o_set_vma_data d;
+
+ memset (&d, 0, sizeof (d));
+
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_section_vma, (char *) &d);
+ if ((d.vma_pass = d.zerofill_seen) != 0)
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_section_vma, (char *) &d);
+ if ((d.vma_pass = d.gb_zerofill_seen) != 0)
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_section_vma, (char *) &d);
+}
+
+static void
+obj_mach_o_set_indirect_symbols (bfd *abfd, asection *sec,
+ void *xxx ATTRIBUTE_UNUSED)
+{
+ bfd_vma sect_size = bfd_section_size (abfd, sec);
+ bfd_mach_o_section *ms = bfd_mach_o_get_mach_o_section (sec);
+ unsigned lazy = 0;
+
+ /* See if we have any indirect syms to consider. */
+ if (indirect_syms == NULL)
+ return;
+
+ /* Process indirect symbols.
+ Check for errors, if OK attach them as a flat array to the section
+ for which they are defined. */
+
+ switch (ms->flags & BFD_MACH_O_SECTION_TYPE_MASK)
+ {
+ case BFD_MACH_O_S_SYMBOL_STUBS:
+ case BFD_MACH_O_S_LAZY_SYMBOL_POINTERS:
+ lazy = LAZY;
+ /* Fall through. */
+ case BFD_MACH_O_S_NON_LAZY_SYMBOL_POINTERS:
+ {
+ unsigned int nactual = 0;
+ unsigned int ncalc;
+ obj_mach_o_indirect_sym *isym;
+ obj_mach_o_indirect_sym *list = NULL;
+ obj_mach_o_indirect_sym *list_tail = NULL;
+ unsigned long eltsiz =
+ bfd_mach_o_section_get_entry_size (abfd, ms);
+
+ for (isym = indirect_syms; isym != NULL; isym = isym->next)
+ {
+ if (isym->sect == sec)
+ {
+ nactual++;
+ if (list == NULL)
+ list = isym;
+ else
+ list_tail->next = isym;
+ list_tail = isym;
+ }
+ }
+
+ /* If none are in this section, stop here. */
+ if (nactual == 0)
+ break;
+
+ /* If we somehow added indirect symbols to a section with a zero
+ entry size, we're dead ... */
+ gas_assert (eltsiz != 0);
+
+ ncalc = (unsigned int) (sect_size / eltsiz);
+ if (nactual != ncalc)
+ as_bad (_("the number of .indirect_symbols defined in section %s"
+ " does not match the number expected (%d defined, %d"
+ " expected)"), sec->name, nactual, ncalc);
+ else
+ {
+ unsigned n;
+ bfd_mach_o_asymbol *sym;
+ ms->indirect_syms =
+ bfd_zalloc (abfd,
+ nactual * sizeof (bfd_mach_o_asymbol *));
+
+ if (ms->indirect_syms == NULL)
+ {
+ as_fatal (_("internal error: failed to allocate %d indirect"
+ "symbol pointers"), nactual);
+ }
+
+ for (isym = list, n = 0; isym != NULL; isym = isym->next, n++)
+ {
+ sym = (bfd_mach_o_asymbol *)symbol_get_bfdsym (isym->sym);
+ /* Array is init to NULL & NULL signals a local symbol
+ If the section is lazy-bound, we need to keep the
+ reference to the symbol, since dyld can override.
+
+ Absolute symbols are handled specially. */
+ if (sym->symbol.section == bfd_abs_section_ptr)
+ ms->indirect_syms[n] = sym;
+ else if (S_IS_LOCAL (isym->sym) && ! lazy)
+ ;
+ else
+ {
+ if (sym == NULL)
+ ;
+ /* If the symbols is external ... */
+ else if (S_IS_EXTERNAL (isym->sym)
+ || (sym->n_type & BFD_MACH_O_N_EXT)
+ || ! S_IS_DEFINED (isym->sym)
+ || lazy)
+ {
+ sym->n_desc &= ~LAZY;
+ /* ... it can be lazy, if not defined or hidden. */
+ if ((sym->n_type & BFD_MACH_O_N_TYPE)
+ == BFD_MACH_O_N_UNDF
+ && ! (sym->n_type & BFD_MACH_O_N_PEXT)
+ && (sym->n_type & BFD_MACH_O_N_EXT))
+ sym->n_desc |= lazy;
+ ms->indirect_syms[n] = sym;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* The process of relocation could alter what's externally visible, thus we
+ leave setting the indirect symbols until last. */
+
+void
+obj_mach_o_frob_file_after_relocs (void)
+{
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_indirect_symbols, (char *) 0);
+}
+
+/* Reverse relocations order to make ld happy. */
+
+void
+obj_mach_o_reorder_section_relocs (asection *sec, arelent **rels, unsigned int n)
+{
+ unsigned int i;
+ unsigned int max = n / 2;
+
+ for (i = 0; i < max; i++)
+ {
+ arelent *r = rels[i];
+ rels[i] = rels[n - i - 1];
+ rels[n - i - 1] = r;
+ }
+ bfd_set_reloc (stdoutput, sec, rels, n);
+}
+
+/* Relocation rules are different in frame sections. */
+
+static int
+obj_mach_o_is_frame_section (segT sec)
+{
+ int l;
+ l = strlen (segment_name (sec));
+ if ((l == 9 && strncmp (".eh_frame", segment_name (sec), 9) == 0)
+ || (l == 12 && strncmp (".debug_frame", segment_name (sec), 12) == 0))
+ return 1;
+ return 0;
+}
+
+/* Unless we're in a frame section, we need to force relocs to be generated for
+ local subtractions. We might eliminate them later (if they are within the
+ same sub-section) but we don't know that at the point that this decision is
+ being made. */
+
+int
+obj_mach_o_allow_local_subtract (expressionS * left ATTRIBUTE_UNUSED,
+ expressionS * right ATTRIBUTE_UNUSED,
+ segT seg)
+{
+ /* Don't interfere if it's one of the GAS internal sections. */
+ if (! SEG_NORMAL (seg))
+ return 1;
+
+ /* Allow in frame sections, otherwise emit a reloc. */
+ return obj_mach_o_is_frame_section (seg);
+}
+
+int
+obj_mach_o_in_different_subsection (symbolS *a, symbolS *b)
+{
+ fragS *fa;
+ fragS *fb;
+
+ if (S_GET_SEGMENT (a) != S_GET_SEGMENT (b)
+ || !S_IS_DEFINED (a)
+ || !S_IS_DEFINED (b))
+ {
+ /* Not in the same segment, or undefined symbol. */
+ return 1;
+ }
+
+ fa = symbol_get_frag (a);
+ fb = symbol_get_frag (b);
+ if (fa == NULL || fb == NULL)
+ {
+ /* One of the symbols is not in a subsection. */
+ return 1;
+ }
+
+ return fa->obj_frag_data.subsection != fb->obj_frag_data.subsection;
+}
+
+int
+obj_mach_o_force_reloc_sub_same (fixS *fix, segT seg)
+{
+ if (! SEG_NORMAL (seg))
+ return 1;
+ return obj_mach_o_in_different_subsection (fix->fx_addsy, fix->fx_subsy);
+}
+
+int
+obj_mach_o_force_reloc_sub_local (fixS *fix, segT seg ATTRIBUTE_UNUSED)
+{
+ return obj_mach_o_in_different_subsection (fix->fx_addsy, fix->fx_subsy);
+}
+
+int
+obj_mach_o_force_reloc (fixS *fix)
+{
+ if (generic_force_reloc (fix))
+ return 1;
+
+ /* Force a reloc if the target is not in the same subsection.
+ FIXME: handle (a - b) where a and b belongs to the same subsection ? */
+ if (fix->fx_addsy != NULL)
+ {
+ symbolS *subsec = fix->fx_frag->obj_frag_data.subsection;
+ symbolS *targ = fix->fx_addsy;
+
+ /* There might be no subsections at all. */
+ if (subsec == NULL)
+ return 0;
+
+ if (S_GET_SEGMENT (targ) == absolute_section)
+ return 0;
+
+ return obj_mach_o_in_different_subsection (targ, subsec);
+ }
+ return 0;
+}
diff --git a/gas/config/obj-macho.h b/gas/config/obj-macho.h
new file mode 100644
index 0000000..632fb8d
--- /dev/null
+++ b/gas/config/obj-macho.h
@@ -0,0 +1,119 @@
+/* Mach-O object file format for gas, the assembler.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef _OBJ_MACH_O_H
+#define _OBJ_MACH_O_H
+
+/* Tag to validate Mach-O object file format processing */
+#define OBJ_MACH_O 1
+
+#include "bfd/mach-o.h"
+
+#include "targ-cpu.h"
+
+#define OUTPUT_FLAVOR bfd_target_mach_o_flavour
+
+/* We want to control how the sections are pre-defined on startup. */
+#define obj_begin() mach_o_begin ()
+extern void mach_o_begin (void);
+
+/* All our align expressions are power of two. */
+#define USE_ALIGN_PTWO 1
+
+/* Common symbols can carry alignment information. */
+#ifndef S_SET_ALIGN
+#define S_SET_ALIGN(S,V) do {\
+ bfd_mach_o_asymbol *___s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (S);\
+ ___s->n_desc = (___s->n_desc & 0xf0ff) | (((V) & 0x0f) << 8);\
+} while (0)
+#endif
+
+extern const pseudo_typeS mach_o_pseudo_table[];
+
+#ifndef obj_pop_insert
+#define obj_pop_insert() pop_insert (mach_o_pseudo_table)
+#endif
+
+#define obj_sec_sym_ok_for_reloc(SEC) 1
+
+#define obj_read_begin_hook() {;}
+#define obj_symbol_new_hook(s) {;}
+
+#define EMIT_SECTION_SYMBOLS 0
+
+struct obj_mach_o_symbol_data
+{
+ /* If the symbol represents a subsection, this is the size of the subsection.
+ This is used to check whether a local symbol belongs to a subsection. */
+ valueT subsection_size;
+};
+#define OBJ_SYMFIELD_TYPE struct obj_mach_o_symbol_data
+
+#define obj_frob_colon obj_mach_o_frob_colon
+extern void obj_mach_o_frob_colon (const char *);
+
+/* Called when a label is defined. Mach-O uses this to create subsections. */
+#define obj_frob_label obj_mach_o_frob_label
+extern void obj_mach_o_frob_label (symbolS *);
+
+#define obj_frob_symbol(s, punt) punt = obj_mach_o_frob_symbol(s)
+extern int obj_mach_o_frob_symbol (struct symbol *);
+
+#define OBJ_PROCESS_STAB(SEG,W,S,T,O,D) obj_mach_o_process_stab(W,S,T,O,D)
+extern void obj_mach_o_process_stab (int, const char *,int, int, int);
+
+struct obj_mach_o_frag_data
+{
+ /* Symbol that corresponds to the subsection. */
+ symbolS *subsection;
+};
+
+#define OBJ_FRAG_TYPE struct obj_mach_o_frag_data
+
+#define md_pre_output_hook obj_mach_o_pre_output_hook()
+extern void obj_mach_o_pre_output_hook(void);
+
+#define md_pre_relax_hook obj_mach_o_pre_relax_hook()
+extern void obj_mach_o_pre_relax_hook (void);
+
+#define md_post_relax_hook obj_mach_o_post_relax_hook()
+extern void obj_mach_o_post_relax_hook (void);
+
+#define obj_frob_file_after_relocs obj_mach_o_frob_file_after_relocs
+extern void obj_mach_o_frob_file_after_relocs (void);
+
+#define SET_SECTION_RELOCS(sec, relocs, n) \
+ obj_mach_o_reorder_section_relocs (sec, relocs, n)
+extern void obj_mach_o_reorder_section_relocs (asection *, arelent **,
+ unsigned int);
+
+/* Emit relocs for local subtracts, to cater for subsections-via-symbols. */
+#define md_allow_local_subtract(LEFT, RIGHT, SECTION) \
+ obj_mach_o_allow_local_subtract (LEFT, RIGHT, SECTION)
+extern int obj_mach_o_allow_local_subtract (expressionS *, expressionS *,
+ segT);
+
+struct fix;
+extern int obj_mach_o_in_different_subsection (symbolS *a, symbolS *b);
+extern int obj_mach_o_force_reloc (struct fix *fix);
+extern int obj_mach_o_force_reloc_sub_same (struct fix *fix, segT seg);
+extern int obj_mach_o_force_reloc_sub_local (struct fix *fix, segT seg);
+
+#endif /* _OBJ_MACH_O_H */
diff --git a/gas/config/obj-multi.c b/gas/config/obj-multi.c
new file mode 100644
index 0000000..8c78d62
--- /dev/null
+++ b/gas/config/obj-multi.c
@@ -0,0 +1,23 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/* foo */
+
+#include "as.h"
+
diff --git a/gas/config/obj-multi.h b/gas/config/obj-multi.h
new file mode 100644
index 0000000..e656e2c
--- /dev/null
+++ b/gas/config/obj-multi.h
@@ -0,0 +1,173 @@
+/* Multiple object format emulation.
+ Copyright (C) 1995-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef _OBJ_MULTI_H
+#define _OBJ_MULTI_H
+
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+
+#include "emul.h"
+#include "targ-cpu.h"
+
+#define OUTPUT_FLAVOR \
+ (this_format->flavor)
+
+#define obj_begin() \
+ (this_format->begin \
+ ? (*this_format->begin) () \
+ : (void) 0)
+
+#define obj_app_file(NAME, APPFILE) \
+ (this_format->app_file \
+ ? (*this_format->app_file) (NAME, APPFILE) \
+ : (void) 0)
+
+#define obj_frob_symbol(S,P) \
+ (*this_format->frob_symbol) (S, &(P))
+
+#define obj_frob_file() \
+ (this_format->frob_file \
+ ? (*this_format->frob_file) () \
+ : (void) 0)
+
+#define obj_frob_file_before_adjust() \
+ (this_format->frob_file_before_adjust \
+ ? (*this_format->frob_file_before_adjust) () \
+ : (void) 0)
+
+#define obj_frob_file_before_fix() \
+ (this_format->frob_file_before_fix \
+ ? (*this_format->frob_file_before_fix) () \
+ : (void) 0)
+
+#define obj_frob_file_after_relocs() \
+ (this_format->frob_file_after_relocs \
+ ? (*this_format->frob_file_after_relocs) () \
+ : (void) 0)
+
+#define obj_ecoff_set_ext \
+ (*this_format->ecoff_set_ext)
+
+#define obj_pop_insert \
+ (*this_format->pop_insert)
+
+#define obj_read_begin_hook() \
+ (this_format->read_begin_hook \
+ ? (*this_format->read_begin_hook) () \
+ : (void) 0)
+
+#define obj_symbol_new_hook(S) \
+ (this_format->symbol_new_hook \
+ ? (*this_format->symbol_new_hook) (S) \
+ : (void) 0)
+
+#define obj_symbol_clone_hook(N, O) \
+ (this_format->symbol_clone_hook \
+ ? (*this_format->symbol_clone_hook) (N, O) \
+ : (void) 0)
+
+#define obj_sec_sym_ok_for_reloc(A) \
+ (this_format->sec_sym_ok_for_reloc \
+ ? (*this_format->sec_sym_ok_for_reloc) (A) \
+ : 0)
+
+#define obj_adjust_symtab() \
+ (this_format->adjust_symtab \
+ ? (*this_format->adjust_symtab) () \
+ : (void) 0)
+
+#define S_GET_SIZE \
+ (*this_format->s_get_size)
+
+#define S_SET_SIZE(S, N) \
+ (this_format->s_set_size \
+ ? (*this_format->s_set_size) (S, N) \
+ : (void) 0)
+
+#define S_GET_ALIGN \
+ (*this_format->s_get_align)
+
+#define S_SET_ALIGN(S, N) \
+ (this_format->s_set_align \
+ ? (*this_format->s_set_align) (S, N) \
+ : (void) 0)
+
+#define S_GET_OTHER \
+ (*this_format->s_get_other)
+
+#define S_SET_OTHER(S, O) \
+ (this_format->s_set_other \
+ ? (*this_format->s_set_other) (S, O) \
+ : (void) 0)
+
+#define S_GET_DESC \
+ (*this_format->s_get_desc)
+
+#define S_SET_DESC(S, D) \
+ (this_format->s_set_desc \
+ ? (*this_format->s_set_desc) (S, D) \
+ : (void) 0)
+
+#define S_GET_TYPE \
+ (*this_format->s_get_desc)
+
+#define S_SET_TYPE(S, T) \
+ (this_format->s_set_type \
+ ? (*this_format->s_set_type) (S, T) \
+ : (void) 0)
+
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(d,s) \
+ (this_format->copy_symbol_attributes \
+ ? (*this_format->copy_symbol_attributes) (d, s) \
+ : (void) 0)
+
+#define OBJ_PROCESS_STAB(SEG,W,S,T,O,D) \
+ (this_format->process_stab \
+ ? (*this_format->process_stab) (SEG,W,S,T,O,D) \
+ : (void) 0)
+
+#define SEPARATE_STAB_SECTIONS \
+ ((*this_format->separate_stab_sections) ())
+
+#define INIT_STAB_SECTION(S) \
+ (this_format->init_stab_section \
+ ? (*this_format->init_stab_section) (S) \
+ : (void) 0)
+
+#define EMIT_SECTION_SYMBOLS (this_format->emit_section_symbols)
+
+#define FAKE_LABEL_NAME (this_emulation->fake_label_name)
+
+#ifdef OBJ_MAYBE_ELF
+/* We need OBJ_SYMFIELD_TYPE so that symbol_get_obj is defined in symbol.c
+ We also need various STAB defines for stab.c */
+#include "obj-elf.h"
+#endif
+
+#ifdef OBJ_MAYBE_AOUT
+/* We want aout_process_stab in stabs.c for the aout table. Defining this
+ macro will have no other effect. */
+#define AOUT_STABS
+#endif
+
+#endif /* !OBJ_HEADER */
+#endif /* _OBJ_MULTI_H */
diff --git a/gas/config/obj-som.c b/gas/config/obj-som.c
new file mode 100644
index 0000000..55a9b6a
--- /dev/null
+++ b/gas/config/obj-som.c
@@ -0,0 +1,327 @@
+/* SOM object file format.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Written by the Center for Software Science at the University of Utah
+ and by Cygnus Support. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "aout/stab_gnu.h"
+
+static int version_seen = 0;
+static int copyright_seen = 0;
+static int compiler_seen = 0;
+
+/* Unused by SOM. */
+
+void
+obj_read_begin_hook (void)
+{
+}
+
+/* Handle a .compiler directive. This is intended to create the
+ compilation unit auxiliary header for MPE such that the linkeditor
+ can handle SOM extraction from archives. The format of the quoted
+ string is "sourcefile language version" and is delimited by blanks. */
+
+void
+obj_som_compiler (int unused ATTRIBUTE_UNUSED)
+{
+ char *buf;
+ char c;
+ char *filename;
+ char *language_name;
+ char *p;
+ char *version_id;
+
+ if (compiler_seen)
+ {
+ as_bad (_("Only one .compiler pseudo-op per file!"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ buf = input_line_pointer;
+ ++input_line_pointer;
+ while (is_a_char (next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\000';
+ }
+ else
+ {
+ as_bad (_("Expected quoted string"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Parse the quoted string into its component parts. Skip the
+ quote. */
+ filename = buf + 1;
+ p = filename;
+ while (*p != ' ' && *p != '\000')
+ p++;
+ if (*p == '\000')
+ {
+ as_bad (_(".compiler directive missing language and version"));
+ return;
+ }
+ *p = '\000';
+
+ language_name = ++p;
+ while (*p != ' ' && *p != '\000')
+ p++;
+ if (*p == '\000')
+ {
+ as_bad (_(".compiler directive missing version"));
+ return;
+ }
+ *p = '\000';
+
+ version_id = ++p;
+ while (*p != '\000')
+ p++;
+ /* Remove the trailing quote. */
+ *(--p) = '\000';
+
+ compiler_seen = 1;
+ if (! bfd_som_attach_compilation_unit (stdoutput, filename, language_name,
+ "GNU Tools", version_id))
+ {
+ bfd_perror (stdoutput->filename);
+ as_fatal (_("FATAL: Attaching compiler header %s"), stdoutput->filename);
+ }
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .version directive. */
+
+void
+obj_som_version (int unused ATTRIBUTE_UNUSED)
+{
+ char *version, c;
+
+ if (version_seen)
+ {
+ as_bad (_("Only one .version pseudo-op per file!"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ version = input_line_pointer;
+ ++input_line_pointer;
+ while (is_a_char (next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\000';
+ }
+ else
+ {
+ as_bad (_("Expected quoted string"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ version_seen = 1;
+ if (!bfd_som_attach_aux_hdr (stdoutput, VERSION_AUX_ID, version))
+ as_fatal (_("attaching version header %s: %s"),
+ stdoutput->filename, bfd_errmsg (bfd_get_error ()));
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .copyright directive. This probably isn't complete, but
+ it's of dubious value anyway and (IMHO) not worth the time to finish.
+ If you care about copyright strings that much, you fix it. */
+
+void
+obj_som_copyright (int unused ATTRIBUTE_UNUSED)
+{
+ char *copyright, c;
+
+ if (copyright_seen)
+ {
+ as_bad (_("Only one .copyright pseudo-op per file!"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\"')
+ {
+ copyright = input_line_pointer;
+ ++input_line_pointer;
+ while (is_a_char (next_char_of_string ()))
+ ;
+ c = *input_line_pointer;
+ *input_line_pointer = '\000';
+ }
+ else
+ {
+ as_bad (_("Expected quoted string"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ copyright_seen = 1;
+ if (!bfd_som_attach_aux_hdr (stdoutput, COPYRIGHT_AUX_ID, copyright))
+ as_fatal (_("attaching copyright header %s: %s"),
+ stdoutput->filename, bfd_errmsg (bfd_get_error ()));
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Perform any initialization necessary for stabs support.
+
+ For SOM we need to create the space which will contain the
+ two stabs subspaces. Additionally we need to set up the
+ space/subspace relationships and set space/subspace attributes
+ which BFD does not understand. */
+
+void
+obj_som_init_stab_section (segT seg)
+{
+ segT saved_seg = now_seg;
+ segT space;
+ subsegT saved_subseg = now_subseg;
+ char *p, *file;
+ unsigned int stroff;
+
+ /* Make the space which will contain the debug subspaces. */
+ space = bfd_make_section_old_way (stdoutput, "$GDB_DEBUG$");
+
+ /* Set SOM specific attributes for the space. In particular we set
+ the space "defined", "private", "sort_key", and "spnum" values.
+
+ Due to a bug in pxdb (called by hpux linker), the sort keys
+ of the various stabs spaces/subspaces need to be "small". We
+ reserve range 72/73 which appear to work well. */
+ obj_set_section_attributes (space, 1, 1, 72, 2);
+ bfd_set_section_alignment (stdoutput, space, 2);
+
+ /* Set the containing space for both stab sections to be $GDB_DEBUG$
+ (just created above). Also set some attributes which BFD does
+ not understand. In particular, access bits, sort keys, and load
+ quadrant. */
+ obj_set_subsection_attributes (seg, space, 0x1f, 73, 0, 0, 0, 0);
+ bfd_set_section_alignment (stdoutput, seg, 2);
+
+ /* Make some space for the first special stab entry and zero the memory.
+ It contains information about the length of this file's
+ stab string and the like. Using it avoids the need to
+ relocate the stab strings.
+
+ The $GDB_STRINGS$ space will be created as a side effect of
+ the call to get_stab_string_offset. */
+ p = frag_more (12);
+ memset (p, 0, 12);
+ as_where (&file, (unsigned int *) NULL);
+ stroff = get_stab_string_offset (file, "$GDB_STRINGS$");
+ know (stroff == 1);
+ md_number_to_chars (p, stroff, 4);
+ seg_info (seg)->stabu.p = p;
+
+ /* Set the containing space for both stab sections to be $GDB_DEBUG$
+ (just created above). Also set some attributes which BFD does
+ not understand. In particular, access bits, sort keys, and load
+ quadrant. */
+ seg = bfd_get_section_by_name (stdoutput, "$GDB_STRINGS$");
+ obj_set_subsection_attributes (seg, space, 0x1f, 72, 0, 0, 0, 0);
+ bfd_set_section_alignment (stdoutput, seg, 2);
+
+ subseg_set (saved_seg, saved_subseg);
+}
+
+/* Fill in the counts in the first entry in a .stabs section. */
+
+static void
+adjust_stab_sections (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+ asection *strsec;
+ char *p;
+ int strsz, nsyms;
+
+ if (strcmp ("$GDB_SYMBOLS$", sec->name))
+ return;
+
+ strsec = bfd_get_section_by_name (abfd, "$GDB_STRINGS$");
+ if (strsec)
+ strsz = bfd_section_size (abfd, strsec);
+ else
+ strsz = 0;
+ nsyms = bfd_section_size (abfd, sec) / 12 - 1;
+
+ p = seg_info (sec)->stabu.p;
+ gas_assert (p != 0);
+
+ bfd_h_put_16 (abfd, (bfd_vma) nsyms, (bfd_byte *) p + 6);
+ bfd_h_put_32 (abfd, (bfd_vma) strsz, (bfd_byte *) p + 8);
+}
+
+/* Called late in the assembly phase to adjust the special
+ stab entry and to set the starting address for each code subspace. */
+
+void
+som_frob_file (void)
+{
+ bfd_map_over_sections (stdoutput, adjust_stab_sections, (void *) 0);
+}
+
+static void
+obj_som_weak (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_WEAK (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+ demand_empty_rest_of_line ();
+}
+
+const pseudo_typeS obj_pseudo_table[] =
+{
+ {"weak", obj_som_weak, 0},
+ {NULL, NULL, 0}
+};
diff --git a/gas/config/obj-som.h b/gas/config/obj-som.h
new file mode 100644
index 0000000..bb2aead
--- /dev/null
+++ b/gas/config/obj-som.h
@@ -0,0 +1,74 @@
+/* SOM object file format.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Written by the Center for Software Science at the University of Utah
+ and by Cygnus Support. */
+
+#ifndef _OBJ_SOM_H
+#define _OBJ_SOM_H
+
+#define OBJ_SOM 1
+
+#include "bfd/som.h"
+#include "som/reloc.h"
+#include "targ-cpu.h"
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE !FALSE
+#endif
+
+/* should be conditional on address size! */
+#define som_symbol(asymbol) ((som_symbol_type *) (&(asymbol)->the_bfd))
+
+extern void som_file_symbol (char *);
+extern void som_frob_file (void);
+extern void obj_som_version (int);
+extern void obj_som_init_stab_section (segT);
+extern void obj_som_copyright (int);
+extern void obj_som_compiler (int);
+
+#define obj_symbol_new_hook(s) {;}
+
+/* SOM has several attributes for spaces/subspaces which can not
+ be easily expressed in BFD. We use these macros to trigger calls
+ into the SOM BFD backend to set these attributes. */
+#define obj_set_section_attributes bfd_som_set_section_attributes
+#define obj_set_subsection_attributes bfd_som_set_subsection_attributes
+
+/* Likewise for symbol types. */
+#define obj_set_symbol_type bfd_som_set_symbol_type
+
+/* Stabs go in a separate sections. GDB expects to find them in sections
+ with the names $GDB_SYMBOLS$ and $GDB_STRINGS$ rather than .stab and
+ .stabstr. */
+#define SEPARATE_STAB_SECTIONS 1
+#define STAB_SECTION_NAME "$GDB_SYMBOLS$"
+#define STAB_STRING_SECTION_NAME "$GDB_STRINGS$"
+
+/* We use INIT_STAB_SECTION to record the space/subspace relationships
+ for the various debugging sections. */
+#define INIT_STAB_SECTION(seg) obj_som_init_stab_section (seg)
+
+/* We'll be updating the magic 1st stab entry once the entire assembly
+ fail has been processed. */
+#define obj_frob_file() som_frob_file()
+
+#endif /* _OBJ_SOM_H */
diff --git a/gas/config/rl78-defs.h b/gas/config/rl78-defs.h
new file mode 100644
index 0000000..0af8874
--- /dev/null
+++ b/gas/config/rl78-defs.h
@@ -0,0 +1,52 @@
+/* rl78-defs.h Renesas RL78 internal definitions
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef RL78_DEFS_H
+#define RL78_DEFS_H
+
+/* Third operand to rl78_op. */
+#define RL78REL_DATA 0
+#define RL78REL_PCREL 1
+
+#define RL78_RELAX_NONE 0
+#define RL78_RELAX_BRANCH 1
+
+extern int rl78_error (const char *);
+extern void rl78_lex_init (char *, char *);
+extern void rl78_prefix (int);
+extern int rl78_has_prefix (void);
+extern void rl78_base1 (int);
+extern void rl78_base2 (int, int);
+extern void rl78_base3 (int, int, int);
+extern void rl78_base4 (int, int, int, int);
+extern void rl78_field (int, int, int);
+extern void rl78_op (expressionS, int, int);
+extern void rl78_disp3 (expressionS, int);
+extern void rl78_field5s (expressionS);
+extern void rl78_field5s2 (expressionS);
+extern void rl78_relax (int, int);
+extern void rl78_linkrelax_addr16 (void);
+extern void rl78_linkrelax_branch (void);
+extern int rl78_parse (void);
+extern int rl78_wrap (void);
+
+extern char * rl78_lex_start;
+extern char * rl78_lex_end;
+#endif
diff --git a/gas/config/rl78-parse.y b/gas/config/rl78-parse.y
new file mode 100644
index 0000000..e358a27
--- /dev/null
+++ b/gas/config/rl78-parse.y
@@ -0,0 +1,1598 @@
+/* rl78-parse.y Renesas RL78 parser
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+%{
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "rl78-defs.h"
+
+static int rl78_lex (void);
+
+/* Ok, here are the rules for using these macros...
+
+ B*() is used to specify the base opcode bytes. Fields to be filled
+ in later, leave zero. Call this first.
+
+ F() and FE() are used to fill in fields within the base opcode bytes. You MUST
+ call B*() before any F() or FE().
+
+ [UN]*O*(), PC*() appends operands to the end of the opcode. You
+ must call P() and B*() before any of these, so that the fixups
+ have the right byte location.
+ O = signed, UO = unsigned, NO = negated, PC = pcrel
+
+ IMM() adds an immediate and fills in the field for it.
+ NIMM() same, but negates the immediate.
+ NBIMM() same, but negates the immediate, for sbb.
+ DSP() adds a displacement, and fills in the field for it.
+
+ Note that order is significant for the O, IMM, and DSP macros, as
+ they append their data to the operand buffer in the order that you
+ call them.
+
+ Use "disp" for displacements whenever possible; this handles the
+ "0" case properly. */
+
+#define B1(b1) rl78_base1 (b1)
+#define B2(b1, b2) rl78_base2 (b1, b2)
+#define B3(b1, b2, b3) rl78_base3 (b1, b2, b3)
+#define B4(b1, b2, b3, b4) rl78_base4 (b1, b2, b3, b4)
+
+/* POS is bits from the MSB of the first byte to the LSB of the last byte. */
+#define F(val,pos,sz) rl78_field (val, pos, sz)
+#define FE(exp,pos,sz) rl78_field (exp_val (exp), pos, sz);
+
+#define O1(v) rl78_op (v, 1, RL78REL_DATA)
+#define O2(v) rl78_op (v, 2, RL78REL_DATA)
+#define O3(v) rl78_op (v, 3, RL78REL_DATA)
+#define O4(v) rl78_op (v, 4, RL78REL_DATA)
+
+#define PC1(v) rl78_op (v, 1, RL78REL_PCREL)
+#define PC2(v) rl78_op (v, 2, RL78REL_PCREL)
+#define PC3(v) rl78_op (v, 3, RL78REL_PCREL)
+
+#define IMM(v,pos) F (immediate (v, RL78REL_SIGNED, pos), pos, 2); \
+ if (v.X_op != O_constant && v.X_op != O_big) rl78_linkrelax_imm (pos)
+#define NIMM(v,pos) F (immediate (v, RL78REL_NEGATIVE, pos), pos, 2)
+#define NBIMM(v,pos) F (immediate (v, RL78REL_NEGATIVE_BORROW, pos), pos, 2)
+#define DSP(v,pos,msz) if (!v.X_md) rl78_relax (RL78_RELAX_DISP, pos); \
+ else rl78_linkrelax_dsp (pos); \
+ F (displacement (v, msz), pos, 2)
+
+#define id24(a,b2,b3) B3 (0xfb+a, b2, b3)
+
+static int expr_is_sfr (expressionS);
+static int expr_is_saddr (expressionS);
+static int expr_is_word_aligned (expressionS);
+static int exp_val (expressionS exp);
+
+static int need_flag = 0;
+static int rl78_in_brackets = 0;
+static int rl78_last_token = 0;
+static char * rl78_init_start;
+static char * rl78_last_exp_start = 0;
+static int rl78_bit_insn = 0;
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+
+#define NOT_SADDR rl78_error ("Expression not 0xFFE20 to 0xFFF1F")
+#define SA(e) if (!expr_is_saddr (e)) NOT_SADDR;
+
+#define NOT_SFR rl78_error ("Expression not 0xFFF00 to 0xFFFFF")
+#define SFR(e) if (!expr_is_sfr (e)) NOT_SFR;
+
+#define NOT_SFR_OR_SADDR rl78_error ("Expression not 0xFFE20 to 0xFFFFF")
+
+#define NOT_ES if (rl78_has_prefix()) rl78_error ("ES: prefix not allowed here");
+
+#define WA(x) if (!expr_is_word_aligned (x)) rl78_error ("Expression not word-aligned");
+
+static void check_expr_is_bit_index (expressionS);
+#define Bit(e) check_expr_is_bit_index (e);
+
+/* Returns TRUE (non-zero) if the expression is a constant in the
+ given range. */
+static int check_expr_is_const (expressionS, int vmin, int vmax);
+
+/* Convert a "regb" value to a "reg_xbc" value. Error if other
+ registers are passed. Needed to avoid reduce-reduce conflicts. */
+static int
+reg_xbc (int reg)
+{
+ switch (reg)
+ {
+ case 0: /* X */
+ return 0x10;
+ case 3: /* B */
+ return 0x20;
+ case 2: /* C */
+ return 0x30;
+ default:
+ rl78_error ("Only X, B, or C allowed here");
+ return 0;
+ }
+}
+
+%}
+
+%name-prefix="rl78_"
+
+%union {
+ int regno;
+ expressionS exp;
+}
+
+%type <regno> regb regb_na regw regw_na FLAG sfr
+%type <regno> A X B C D E H L AX BC DE HL
+%type <exp> EXPR
+
+%type <regno> addsub addsubw andor1 bt_bf setclr1 oneclrb oneclrw
+%type <regno> incdec incdecw
+
+%token A X B C D E H L AX BC DE HL
+%token SPL SPH PSW CS ES PMC MEM
+%token FLAG SP CY
+%token RB0 RB1 RB2 RB3
+
+%token EXPR UNKNOWN_OPCODE IS_OPCODE
+
+%token DOT_S DOT_B DOT_W DOT_L DOT_A DOT_UB DOT_UW
+
+%token ADD ADDC ADDW AND_ AND1
+/* BC is also a register pair */
+%token BF BH BNC BNH BNZ BR BRK BRK1 BT BTCLR BZ
+%token CALL CALLT CLR1 CLRB CLRW CMP CMP0 CMPS CMPW
+%token DEC DECW DI DIVHU DIVWU
+%token EI
+%token HALT
+%token INC INCW
+%token MACH MACHU MOV MOV1 MOVS MOVW MULH MULHU MULU
+%token NOP NOT1
+%token ONEB ONEW OR OR1
+%token POP PUSH
+%token RET RETI RETB ROL ROLC ROLWC ROR RORC
+%token SAR SARW SEL SET1 SHL SHLW SHR SHRW
+%token SKC SKH SKNC SKNH SKNZ SKZ STOP SUB SUBC SUBW
+%token XCH XCHW XOR XOR1
+
+%%
+/* ====================================================================== */
+
+statement :
+
+ UNKNOWN_OPCODE
+ { as_bad (_("Unknown opcode: %s"), rl78_init_start); }
+
+/* The opcodes are listed in approximately alphabetical order. */
+
+/* For reference:
+
+ sfr = special function register - symbol, 0xFFF00 to 0xFFFFF
+ sfrp = special function register - symbol, 0xFFF00 to 0xFFFFE, even only
+ saddr = 0xFFE20 to 0xFFF1F
+ saddrp = 0xFFE20 to 0xFFF1E, even only
+
+ addr20 = 0x00000 to 0xFFFFF
+ addr16 = 0x00000 to 0x0FFFF, even only for 16-bit ops
+ addr5 = 0x00000 to 0x000BE, even only
+*/
+
+/* ---------------------------------------------------------------------- */
+
+/* addsub is ADD, ADDC, SUB, SUBC, AND, OR, XOR, and parts of CMP. */
+
+ | addsub A ',' '#' EXPR
+ { B1 (0x0c|$1); O1 ($5); }
+
+ | addsub EXPR {SA($2)} ',' '#' EXPR
+ { B1 (0x0a|$1); O1 ($2); O1 ($6); }
+
+ | addsub A ',' A
+ { B2 (0x61, 0x01|$1); }
+
+ | addsub A ',' regb_na
+ { B2 (0x61, 0x08|$1); F ($4, 13, 3); }
+
+ | addsub regb_na ',' A
+ { B2 (0x61, 0x00|$1); F ($2, 13, 3); }
+
+ | addsub A ',' EXPR {SA($4)}
+ { B1 (0x0b|$1); O1 ($4); }
+
+ | addsub A ',' opt_es '!' EXPR
+ { B1 (0x0f|$1); O2 ($6); rl78_linkrelax_addr16 (); }
+
+ | addsub A ',' opt_es '[' HL ']'
+ { B1 (0x0d|$1); }
+
+ | addsub A ',' opt_es '[' HL '+' EXPR ']'
+ { B1 (0x0e|$1); O1 ($8); }
+
+ | addsub A ',' opt_es '[' HL '+' B ']'
+ { B2 (0x61, 0x80|$1); }
+
+ | addsub A ',' opt_es '[' HL '+' C ']'
+ { B2 (0x61, 0x82|$1); }
+
+
+
+ | addsub opt_es '!' EXPR ',' '#' EXPR
+ { if ($1 != 0x40)
+ { rl78_error ("Only CMP takes these operands"); }
+ else
+ { B1 (0x00|$1); O2 ($4); O1 ($7); rl78_linkrelax_addr16 (); }
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | addsubw AX ',' '#' EXPR
+ { B1 (0x04|$1); O2 ($5); }
+
+ | addsubw AX ',' regw
+ { B1 (0x01|$1); F ($4, 5, 2); }
+
+ | addsubw AX ',' EXPR {SA($4)}
+ { B1 (0x06|$1); O1 ($4); }
+
+ | addsubw AX ',' opt_es '!' EXPR
+ { B1 (0x02|$1); O2 ($6); rl78_linkrelax_addr16 (); }
+
+ | addsubw AX ',' opt_es '[' HL '+' EXPR ']'
+ { B2 (0x61, 0x09|$1); O1 ($8); }
+
+ | addsubw AX ',' opt_es '[' HL ']'
+ { B4 (0x61, 0x09|$1, 0, 0); }
+
+ | addsubw SP ',' '#' EXPR
+ { B1 ($1 ? 0x20 : 0x10); O1 ($5);
+ if ($1 == 0x40)
+ rl78_error ("CMPW SP,#imm not allowed");
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | andor1 CY ',' sfr '.' EXPR {Bit($6)}
+ { B3 (0x71, 0x08|$1, $4); FE ($6, 9, 3); }
+
+ | andor1 CY ',' EXPR '.' EXPR {Bit($6)}
+ { if (expr_is_sfr ($4))
+ { B2 (0x71, 0x08|$1); FE ($6, 9, 3); O1 ($4); }
+ else if (expr_is_saddr ($4))
+ { B2 (0x71, 0x00|$1); FE ($6, 9, 3); O1 ($4); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | andor1 CY ',' A '.' EXPR {Bit($6)}
+ { B2 (0x71, 0x88|$1); FE ($6, 9, 3); }
+
+ | andor1 CY ',' opt_es '[' HL ']' '.' EXPR {Bit($9)}
+ { B2 (0x71, 0x80|$1); FE ($9, 9, 3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BC '$' EXPR
+ { B1 (0xdc); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+
+ | BNC '$' EXPR
+ { B1 (0xde); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+
+ | BZ '$' EXPR
+ { B1 (0xdd); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+
+ | BNZ '$' EXPR
+ { B1 (0xdf); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+
+ | BH '$' EXPR
+ { B2 (0x61, 0xc3); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+
+ | BNH '$' EXPR
+ { B2 (0x61, 0xd3); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+
+/* ---------------------------------------------------------------------- */
+
+ | bt_bf sfr '.' EXPR ',' '$' EXPR
+ { B3 (0x31, 0x80|$1, $2); FE ($4, 9, 3); PC1 ($7); }
+
+ | bt_bf EXPR '.' EXPR ',' '$' EXPR
+ { if (expr_is_sfr ($2))
+ { B2 (0x31, 0x80|$1); FE ($4, 9, 3); O1 ($2); PC1 ($7); }
+ else if (expr_is_saddr ($2))
+ { B2 (0x31, 0x00|$1); FE ($4, 9, 3); O1 ($2); PC1 ($7); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | bt_bf A '.' EXPR ',' '$' EXPR
+ { B2 (0x31, 0x01|$1); FE ($4, 9, 3); PC1 ($7); }
+
+ | bt_bf opt_es '[' HL ']' '.' EXPR ',' '$' EXPR
+ { B2 (0x31, 0x81|$1); FE ($7, 9, 3); PC1 ($10); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BR AX
+ { B2 (0x61, 0xcb); }
+
+ | BR '$' EXPR
+ { B1 (0xef); PC1 ($3); }
+
+ | BR '$' '!' EXPR
+ { B1 (0xee); PC2 ($4); rl78_linkrelax_branch (); }
+
+ | BR '!' EXPR
+ { B1 (0xed); O2 ($3); rl78_linkrelax_branch (); }
+
+ | BR '!' '!' EXPR
+ { B1 (0xec); O3 ($4); rl78_linkrelax_branch (); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BRK
+ { B2 (0x61, 0xcc); }
+
+ | BRK1
+ { B1 (0xff); }
+
+/* ---------------------------------------------------------------------- */
+
+ | CALL regw
+ { B2 (0x61, 0xca); F ($2, 10, 2); }
+
+ | CALL '$' '!' EXPR
+ { B1 (0xfe); PC2 ($4); }
+
+ | CALL '!' EXPR
+ { B1 (0xfd); O2 ($3); }
+
+ | CALL '!' '!' EXPR
+ { B1 (0xfc); O3 ($4); rl78_linkrelax_branch (); }
+
+ | CALLT '[' EXPR ']'
+ { if ($3.X_op != O_constant)
+ rl78_error ("CALLT requires a numeric address");
+ else
+ {
+ int i = $3.X_add_number;
+ if (i < 0x80 || i > 0xbe)
+ rl78_error ("CALLT address not 0x80..0xbe");
+ else if (i & 1)
+ rl78_error ("CALLT address not even");
+ else
+ {
+ B2 (0x61, 0x84);
+ F ((i >> 1) & 7, 9, 3);
+ F ((i >> 4) & 7, 14, 2);
+ }
+ }
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | setclr1 CY
+ { B2 (0x71, $1 ? 0x88 : 0x80); }
+
+ | setclr1 sfr '.' EXPR
+ { B3 (0x71, 0x0a|$1, $2); FE ($4, 9, 3); }
+
+ | setclr1 EXPR '.' EXPR
+ { if (expr_is_sfr ($2))
+ { B2 (0x71, 0x0a|$1); FE ($4, 9, 3); O1 ($2); }
+ else if (expr_is_saddr ($2))
+ { B2 (0x71, 0x02|$1); FE ($4, 9, 3); O1 ($2); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | setclr1 A '.' EXPR
+ { B2 (0x71, 0x8a|$1); FE ($4, 9, 3); }
+
+ | setclr1 opt_es '!' EXPR '.' EXPR
+ { B2 (0x71, 0x00+$1*0x08); FE ($6, 9, 3); O2 ($4); rl78_linkrelax_addr16 (); }
+
+ | setclr1 opt_es '[' HL ']' '.' EXPR
+ { B2 (0x71, 0x82|$1); FE ($7, 9, 3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | oneclrb A
+ { B1 (0xe1|$1); }
+ | oneclrb X
+ { B1 (0xe0|$1); }
+ | oneclrb B
+ { B1 (0xe3|$1); }
+ | oneclrb C
+ { B1 (0xe2|$1); }
+
+ | oneclrb EXPR {SA($2)}
+ { B1 (0xe4|$1); O1 ($2); }
+
+ | oneclrb opt_es '!' EXPR
+ { B1 (0xe5|$1); O2 ($4); rl78_linkrelax_addr16 (); }
+
+/* ---------------------------------------------------------------------- */
+
+ | oneclrw AX
+ { B1 (0xe6|$1); }
+ | oneclrw BC
+ { B1 (0xe7|$1); }
+
+/* ---------------------------------------------------------------------- */
+
+ | CMP0 A
+ { B1 (0xd1); }
+
+ | CMP0 X
+ { B1 (0xd0); }
+
+ | CMP0 B
+ { B1 (0xd3); }
+
+ | CMP0 C
+ { B1 (0xd2); }
+
+ | CMP0 EXPR {SA($2)}
+ { B1 (0xd4); O1 ($2); }
+
+ | CMP0 opt_es '!' EXPR
+ { B1 (0xd5); O2 ($4); rl78_linkrelax_addr16 (); }
+
+/* ---------------------------------------------------------------------- */
+
+ | CMPS X ',' opt_es '[' HL '+' EXPR ']'
+ { B2 (0x61, 0xde); O1 ($8); }
+
+/* ---------------------------------------------------------------------- */
+
+ | incdec regb
+ { B1 (0x80|$1); F ($2, 5, 3); }
+
+ | incdec EXPR {SA($2)}
+ { B1 (0xa4|$1); O1 ($2); }
+ | incdec '!' EXPR
+ { B1 (0xa0|$1); O2 ($3); rl78_linkrelax_addr16 (); }
+ | incdec ES ':' '!' EXPR
+ { B2 (0x11, 0xa0|$1); O2 ($5); }
+ | incdec '[' HL '+' EXPR ']'
+ { B2 (0x61, 0x59+$1); O1 ($5); }
+ | incdec ES ':' '[' HL '+' EXPR ']'
+ { B3 (0x11, 0x61, 0x59+$1); O1 ($7); }
+
+/* ---------------------------------------------------------------------- */
+
+ | incdecw regw
+ { B1 (0xa1|$1); F ($2, 5, 2); }
+
+ | incdecw EXPR {SA($2)}
+ { B1 (0xa6|$1); O1 ($2); }
+
+ | incdecw opt_es '!' EXPR
+ { B1 (0xa2|$1); O2 ($4); rl78_linkrelax_addr16 (); }
+
+ | incdecw opt_es '[' HL '+' EXPR ']'
+ { B2 (0x61, 0x79+$1); O1 ($6); }
+
+/* ---------------------------------------------------------------------- */
+
+ | DI
+ { B3 (0x71, 0x7b, 0xfa); }
+
+ | EI
+ { B3 (0x71, 0x7a, 0xfa); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MULHU
+ { B3 (0xce, 0xfb, 0x01); }
+
+ | MULH
+ { B3 (0xce, 0xfb, 0x02); }
+
+ | MULU X
+ { B1 (0xd6); }
+
+ | DIVHU
+ { B3 (0xce, 0xfb, 0x03); }
+
+/* Note that the DIVWU encoding was changed from [0xce,0xfb,0x04] to
+ [0xce,0xfb,0x0b]. Different versions of the Software Manual exist
+ with the same version number, but varying encodings. The version
+ here matches the hardware. */
+
+ | DIVWU
+ { B3 (0xce, 0xfb, 0x0b); }
+
+ | MACHU
+ { B3 (0xce, 0xfb, 0x05); }
+
+ | MACH
+ { B3 (0xce, 0xfb, 0x06); }
+
+/* ---------------------------------------------------------------------- */
+
+ | HALT
+ { B2 (0x61, 0xed); }
+
+/* ---------------------------------------------------------------------- */
+/* Note that opt_es is included even when it's not an option, to avoid
+ shift/reduce conflicts. The NOT_ES macro produces an error if ES:
+ is given by the user. */
+
+ | MOV A ',' '#' EXPR
+ { B1 (0x51); O1 ($5); }
+ | MOV regb_na ',' '#' EXPR
+ { B1 (0x50); F($2, 5, 3); O1 ($5); }
+
+ | MOV sfr ',' '#' EXPR
+ { if ($2 != 0xfd)
+ { B2 (0xce, $2); O1 ($5); }
+ else
+ { B1 (0x41); O1 ($5); }
+ }
+
+ | MOV opt_es EXPR ',' '#' EXPR {NOT_ES}
+ { if (expr_is_sfr ($3))
+ { B1 (0xce); O1 ($3); O1 ($6); }
+ else if (expr_is_saddr ($3))
+ { B1 (0xcd); O1 ($3); O1 ($6); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | MOV '!' EXPR ',' '#' EXPR
+ { B1 (0xcf); O2 ($3); O1 ($6); rl78_linkrelax_addr16 (); }
+
+ | MOV ES ':' '!' EXPR ',' '#' EXPR
+ { B2 (0x11, 0xcf); O2 ($5); O1 ($8); }
+
+ | MOV regb_na ',' A
+ { B1 (0x70); F ($2, 5, 3); }
+
+ | MOV A ',' regb_na
+ { B1 (0x60); F ($4, 5, 3); }
+
+ | MOV opt_es EXPR ',' A {NOT_ES}
+ { if (expr_is_sfr ($3))
+ { B1 (0x9e); O1 ($3); }
+ else if (expr_is_saddr ($3))
+ { B1 (0x9d); O1 ($3); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | MOV A ',' opt_es '!' EXPR
+ { B1 (0x8f); O2 ($6); rl78_linkrelax_addr16 (); }
+
+ | MOV '!' EXPR ',' A
+ { B1 (0x9f); O2 ($3); rl78_linkrelax_addr16 (); }
+
+ | MOV ES ':' '!' EXPR ',' A
+ { B2 (0x11, 0x9f); O2 ($5); }
+
+ | MOV regb_na ',' opt_es '!' EXPR
+ { B1 (0xc9|reg_xbc($2)); O2 ($6); rl78_linkrelax_addr16 (); }
+
+ | MOV A ',' opt_es EXPR {NOT_ES}
+ { if (expr_is_saddr ($5))
+ { B1 (0x8d); O1 ($5); }
+ else if (expr_is_sfr ($5))
+ { B1 (0x8e); O1 ($5); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | MOV regb_na ',' opt_es EXPR {SA($5)} {NOT_ES}
+ { B1 (0xc8|reg_xbc($2)); O1 ($5); }
+
+ | MOV A ',' sfr
+ { B2 (0x8e, $4); }
+
+ | MOV sfr ',' regb
+ { if ($4 != 1)
+ rl78_error ("Only A allowed here");
+ else
+ { B2 (0x9e, $2); }
+ }
+
+ | MOV sfr ',' opt_es EXPR {SA($5)} {NOT_ES}
+ { if ($2 != 0xfd)
+ rl78_error ("Only ES allowed here");
+ else
+ { B2 (0x61, 0xb8); O1 ($5); }
+ }
+
+ | MOV A ',' opt_es '[' DE ']'
+ { B1 (0x89); }
+
+ | MOV opt_es '[' DE ']' ',' A
+ { B1 (0x99); }
+
+ | MOV opt_es '[' DE '+' EXPR ']' ',' '#' EXPR
+ { B1 (0xca); O1 ($6); O1 ($10); }
+
+ | MOV A ',' opt_es '[' DE '+' EXPR ']'
+ { B1 (0x8a); O1 ($8); }
+
+ | MOV opt_es '[' DE '+' EXPR ']' ',' A
+ { B1 (0x9a); O1 ($6); }
+
+ | MOV A ',' opt_es '[' HL ']'
+ { B1 (0x8b); }
+
+ | MOV opt_es '[' HL ']' ',' A
+ { B1 (0x9b); }
+
+ | MOV opt_es '[' HL '+' EXPR ']' ',' '#' EXPR
+ { B1 (0xcc); O1 ($6); O1 ($10); }
+
+ | MOV A ',' opt_es '[' HL '+' EXPR ']'
+ { B1 (0x8c); O1 ($8); }
+
+ | MOV opt_es '[' HL '+' EXPR ']' ',' A
+ { B1 (0x9c); O1 ($6); }
+
+ | MOV A ',' opt_es '[' HL '+' B ']'
+ { B2 (0x61, 0xc9); }
+
+ | MOV opt_es '[' HL '+' B ']' ',' A
+ { B2 (0x61, 0xd9); }
+
+ | MOV A ',' opt_es '[' HL '+' C ']'
+ { B2 (0x61, 0xe9); }
+
+ | MOV opt_es '[' HL '+' C ']' ',' A
+ { B2 (0x61, 0xf9); }
+
+ | MOV opt_es EXPR '[' B ']' ',' '#' EXPR
+ { B1 (0x19); O2 ($3); O1 ($9); }
+
+ | MOV A ',' opt_es EXPR '[' B ']'
+ { B1 (0x09); O2 ($5); }
+
+ | MOV opt_es EXPR '[' B ']' ',' A
+ { B1 (0x18); O2 ($3); }
+
+ | MOV opt_es EXPR '[' C ']' ',' '#' EXPR
+ { B1 (0x38); O2 ($3); O1 ($9); }
+
+ | MOV A ',' opt_es EXPR '[' C ']'
+ { B1 (0x29); O2 ($5); }
+
+ | MOV opt_es EXPR '[' C ']' ',' A
+ { B1 (0x28); O2 ($3); }
+
+ | MOV opt_es EXPR '[' BC ']' ',' '#' EXPR
+ { B1 (0x39); O2 ($3); O1 ($9); }
+
+ | MOV opt_es '[' BC ']' ',' '#' EXPR
+ { B3 (0x39, 0, 0); O1 ($8); }
+
+ | MOV A ',' opt_es EXPR '[' BC ']'
+ { B1 (0x49); O2 ($5); }
+
+ | MOV A ',' opt_es '[' BC ']'
+ { B3 (0x49, 0, 0); }
+
+ | MOV opt_es EXPR '[' BC ']' ',' A
+ { B1 (0x48); O2 ($3); }
+
+ | MOV opt_es '[' BC ']' ',' A
+ { B3 (0x48, 0, 0); }
+
+ | MOV opt_es '[' SP '+' EXPR ']' ',' '#' EXPR {NOT_ES}
+ { B1 (0xc8); O1 ($6); O1 ($10); }
+
+ | MOV opt_es '[' SP ']' ',' '#' EXPR {NOT_ES}
+ { B2 (0xc8, 0); O1 ($8); }
+
+ | MOV A ',' opt_es '[' SP '+' EXPR ']' {NOT_ES}
+ { B1 (0x88); O1 ($8); }
+
+ | MOV A ',' opt_es '[' SP ']' {NOT_ES}
+ { B2 (0x88, 0); }
+
+ | MOV opt_es '[' SP '+' EXPR ']' ',' A {NOT_ES}
+ { B1 (0x98); O1 ($6); }
+
+ | MOV opt_es '[' SP ']' ',' A {NOT_ES}
+ { B2 (0x98, 0); }
+
+/* ---------------------------------------------------------------------- */
+
+ | mov1 CY ',' EXPR '.' EXPR
+ { if (expr_is_saddr ($4))
+ { B2 (0x71, 0x04); FE ($6, 9, 3); O1 ($4); }
+ else if (expr_is_sfr ($4))
+ { B2 (0x71, 0x0c); FE ($6, 9, 3); O1 ($4); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | mov1 CY ',' A '.' EXPR
+ { B2 (0x71, 0x8c); FE ($6, 9, 3); }
+
+ | mov1 CY ',' sfr '.' EXPR
+ { B3 (0x71, 0x0c, $4); FE ($6, 9, 3); }
+
+ | mov1 CY ',' opt_es '[' HL ']' '.' EXPR
+ { B2 (0x71, 0x84); FE ($9, 9, 3); }
+
+ | mov1 EXPR '.' EXPR ',' CY
+ { if (expr_is_saddr ($2))
+ { B2 (0x71, 0x01); FE ($4, 9, 3); O1 ($2); }
+ else if (expr_is_sfr ($2))
+ { B2 (0x71, 0x09); FE ($4, 9, 3); O1 ($2); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | mov1 A '.' EXPR ',' CY
+ { B2 (0x71, 0x89); FE ($4, 9, 3); }
+
+ | mov1 sfr '.' EXPR ',' CY
+ { B3 (0x71, 0x09, $2); FE ($4, 9, 3); }
+
+ | mov1 opt_es '[' HL ']' '.' EXPR ',' CY
+ { B2 (0x71, 0x81); FE ($7, 9, 3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOVS opt_es '[' HL '+' EXPR ']' ',' X
+ { B2 (0x61, 0xce); O1 ($6); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOVW AX ',' '#' EXPR
+ { B1 (0x30); O2 ($5); }
+
+ | MOVW regw_na ',' '#' EXPR
+ { B1 (0x30); F ($2, 5, 2); O2 ($5); }
+
+ | MOVW opt_es EXPR ',' '#' EXPR {NOT_ES}
+ { if (expr_is_saddr ($3))
+ { B1 (0xc9); O1 ($3); O2 ($6); }
+ else if (expr_is_sfr ($3))
+ { B1 (0xcb); O1 ($3); O2 ($6); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | MOVW AX ',' opt_es EXPR {NOT_ES}
+ { if (expr_is_saddr ($5))
+ { B1 (0xad); O1 ($5); WA($5); }
+ else if (expr_is_sfr ($5))
+ { B1 (0xae); O1 ($5); WA($5); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | MOVW opt_es EXPR ',' AX {NOT_ES}
+ { if (expr_is_saddr ($3))
+ { B1 (0xbd); O1 ($3); WA($3); }
+ else if (expr_is_sfr ($3))
+ { B1 (0xbe); O1 ($3); WA($3); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+ | MOVW AX ',' regw_na
+ { B1 (0x11); F ($4, 5, 2); }
+
+ | MOVW regw_na ',' AX
+ { B1 (0x10); F ($2, 5, 2); }
+
+ | MOVW AX ',' opt_es '!' EXPR
+ { B1 (0xaf); O2 ($6); WA($6); rl78_linkrelax_addr16 (); }
+
+ | MOVW opt_es '!' EXPR ',' AX
+ { B1 (0xbf); O2 ($4); WA($4); rl78_linkrelax_addr16 (); }
+
+ | MOVW AX ',' opt_es '[' DE ']'
+ { B1 (0xa9); }
+
+ | MOVW opt_es '[' DE ']' ',' AX
+ { B1 (0xb9); }
+
+ | MOVW AX ',' opt_es '[' DE '+' EXPR ']'
+ { B1 (0xaa); O1 ($8); }
+
+ | MOVW opt_es '[' DE '+' EXPR ']' ',' AX
+ { B1 (0xba); O1 ($6); }
+
+ | MOVW AX ',' opt_es '[' HL ']'
+ { B1 (0xab); }
+
+ | MOVW opt_es '[' HL ']' ',' AX
+ { B1 (0xbb); }
+
+ | MOVW AX ',' opt_es '[' HL '+' EXPR ']'
+ { B1 (0xac); O1 ($8); }
+
+ | MOVW opt_es '[' HL '+' EXPR ']' ',' AX
+ { B1 (0xbc); O1 ($6); }
+
+ | MOVW AX ',' opt_es EXPR '[' B ']'
+ { B1 (0x59); O2 ($5); }
+
+ | MOVW opt_es EXPR '[' B ']' ',' AX
+ { B1 (0x58); O2 ($3); }
+
+ | MOVW AX ',' opt_es EXPR '[' C ']'
+ { B1 (0x69); O2 ($5); }
+
+ | MOVW opt_es EXPR '[' C ']' ',' AX
+ { B1 (0x68); O2 ($3); }
+
+ | MOVW AX ',' opt_es EXPR '[' BC ']'
+ { B1 (0x79); O2 ($5); }
+
+ | MOVW AX ',' opt_es '[' BC ']'
+ { B3 (0x79, 0, 0); }
+
+ | MOVW opt_es EXPR '[' BC ']' ',' AX
+ { B1 (0x78); O2 ($3); }
+
+ | MOVW opt_es '[' BC ']' ',' AX
+ { B3 (0x78, 0, 0); }
+
+ | MOVW AX ',' opt_es '[' SP '+' EXPR ']' {NOT_ES}
+ { B1 (0xa8); O1 ($8); WA($8);}
+
+ | MOVW AX ',' opt_es '[' SP ']' {NOT_ES}
+ { B2 (0xa8, 0); }
+
+ | MOVW opt_es '[' SP '+' EXPR ']' ',' AX {NOT_ES}
+ { B1 (0xb8); O1 ($6); WA($6); }
+
+ | MOVW opt_es '[' SP ']' ',' AX {NOT_ES}
+ { B2 (0xb8, 0); }
+
+ | MOVW regw_na ',' EXPR {SA($4)}
+ { B1 (0xca); F ($2, 2, 2); O1 ($4); WA($4); }
+
+ | MOVW regw_na ',' opt_es '!' EXPR
+ { B1 (0xcb); F ($2, 2, 2); O2 ($6); WA($6); rl78_linkrelax_addr16 (); }
+
+ | MOVW SP ',' '#' EXPR
+ { B2 (0xcb, 0xf8); O2 ($5); }
+
+ | MOVW SP ',' AX
+ { B2 (0xbe, 0xf8); }
+
+ | MOVW AX ',' SP
+ { B2 (0xae, 0xf8); }
+
+ | MOVW regw_na ',' SP
+ { B3 (0xcb, 0xf8, 0xff); F ($2, 2, 2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | NOP
+ { B1 (0x00); }
+
+/* ---------------------------------------------------------------------- */
+
+ | NOT1 CY
+ { B2 (0x71, 0xc0); }
+
+/* ---------------------------------------------------------------------- */
+
+ | POP regw
+ { B1 (0xc0); F ($2, 5, 2); }
+
+ | POP PSW
+ { B2 (0x61, 0xcd); };
+
+ | PUSH regw
+ { B1 (0xc1); F ($2, 5, 2); }
+
+ | PUSH PSW
+ { B2 (0x61, 0xdd); };
+
+/* ---------------------------------------------------------------------- */
+
+ | RET
+ { B1 (0xd7); }
+
+ | RETI
+ { B2 (0x61, 0xfc); }
+
+ | RETB
+ { B2 (0x61, 0xec); }
+
+/* ---------------------------------------------------------------------- */
+
+ | ROL A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 1))
+ { B2 (0x61, 0xeb); }
+ }
+
+ | ROLC A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 1))
+ { B2 (0x61, 0xdc); }
+ }
+
+ | ROLWC AX ',' EXPR
+ { if (check_expr_is_const ($4, 1, 1))
+ { B2 (0x61, 0xee); }
+ }
+
+ | ROLWC BC ',' EXPR
+ { if (check_expr_is_const ($4, 1, 1))
+ { B2 (0x61, 0xfe); }
+ }
+
+ | ROR A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 1))
+ { B2 (0x61, 0xdb); }
+ }
+
+ | RORC A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 1))
+ { B2 (0x61, 0xfb);}
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | SAR A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 7))
+ { B2 (0x31, 0x0b); FE ($4, 9, 3); }
+ }
+
+ | SARW AX ',' EXPR
+ { if (check_expr_is_const ($4, 1, 15))
+ { B2 (0x31, 0x0f); FE ($4, 8, 4); }
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | SEL RB0
+ { B2 (0x61, 0xcf); }
+
+ | SEL RB1
+ { B2 (0x61, 0xdf); }
+
+ | SEL RB2
+ { B2 (0x61, 0xef); }
+
+ | SEL RB3
+ { B2 (0x61, 0xff); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SHL A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 7))
+ { B2 (0x31, 0x09); FE ($4, 9, 3); }
+ }
+
+ | SHL B ',' EXPR
+ { if (check_expr_is_const ($4, 1, 7))
+ { B2 (0x31, 0x08); FE ($4, 9, 3); }
+ }
+
+ | SHL C ',' EXPR
+ { if (check_expr_is_const ($4, 1, 7))
+ { B2 (0x31, 0x07); FE ($4, 9, 3); }
+ }
+
+ | SHLW AX ',' EXPR
+ { if (check_expr_is_const ($4, 1, 15))
+ { B2 (0x31, 0x0d); FE ($4, 8, 4); }
+ }
+
+ | SHLW BC ',' EXPR
+ { if (check_expr_is_const ($4, 1, 15))
+ { B2 (0x31, 0x0c); FE ($4, 8, 4); }
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | SHR A ',' EXPR
+ { if (check_expr_is_const ($4, 1, 7))
+ { B2 (0x31, 0x0a); FE ($4, 9, 3); }
+ }
+
+ | SHRW AX ',' EXPR
+ { if (check_expr_is_const ($4, 1, 15))
+ { B2 (0x31, 0x0e); FE ($4, 8, 4); }
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | SKC
+ { B2 (0x61, 0xc8); rl78_linkrelax_branch (); }
+
+ | SKH
+ { B2 (0x61, 0xe3); rl78_linkrelax_branch (); }
+
+ | SKNC
+ { B2 (0x61, 0xd8); rl78_linkrelax_branch (); }
+
+ | SKNH
+ { B2 (0x61, 0xf3); rl78_linkrelax_branch (); }
+
+ | SKNZ
+ { B2 (0x61, 0xf8); rl78_linkrelax_branch (); }
+
+ | SKZ
+ { B2 (0x61, 0xe8); rl78_linkrelax_branch (); }
+
+/* ---------------------------------------------------------------------- */
+
+ | STOP
+ { B2 (0x61, 0xfd); }
+
+/* ---------------------------------------------------------------------- */
+
+ | XCH A ',' regb_na
+ { if ($4 == 0) /* X */
+ { B1 (0x08); }
+ else
+ { B2 (0x61, 0x88); F ($4, 13, 3); }
+ }
+
+ | XCH A ',' opt_es '!' EXPR
+ { B2 (0x61, 0xaa); O2 ($6); rl78_linkrelax_addr16 (); }
+
+ | XCH A ',' opt_es '[' DE ']'
+ { B2 (0x61, 0xae); }
+
+ | XCH A ',' opt_es '[' DE '+' EXPR ']'
+ { B2 (0x61, 0xaf); O1 ($8); }
+
+ | XCH A ',' opt_es '[' HL ']'
+ { B2 (0x61, 0xac); }
+
+ | XCH A ',' opt_es '[' HL '+' EXPR ']'
+ { B2 (0x61, 0xad); O1 ($8); }
+
+ | XCH A ',' opt_es '[' HL '+' B ']'
+ { B2 (0x61, 0xb9); }
+
+ | XCH A ',' opt_es '[' HL '+' C ']'
+ { B2 (0x61, 0xa9); }
+
+ | XCH A ',' EXPR
+ { if (expr_is_sfr ($4))
+ { B2 (0x61, 0xab); O1 ($4); }
+ else if (expr_is_saddr ($4))
+ { B2 (0x61, 0xa8); O1 ($4); }
+ else
+ NOT_SFR_OR_SADDR;
+ }
+
+/* ---------------------------------------------------------------------- */
+
+ | XCHW AX ',' regw_na
+ { B1 (0x31); F ($4, 5, 2); }
+
+/* ---------------------------------------------------------------------- */
+
+ ; /* end of statement */
+
+/* ---------------------------------------------------------------------- */
+
+opt_es : /* nothing */
+ | ES ':'
+ { rl78_prefix (0x11); }
+ ;
+
+regb : X { $$ = 0; }
+ | A { $$ = 1; }
+ | C { $$ = 2; }
+ | B { $$ = 3; }
+ | E { $$ = 4; }
+ | D { $$ = 5; }
+ | L { $$ = 6; }
+ | H { $$ = 7; }
+ ;
+
+regb_na : X { $$ = 0; }
+ | C { $$ = 2; }
+ | B { $$ = 3; }
+ | E { $$ = 4; }
+ | D { $$ = 5; }
+ | L { $$ = 6; }
+ | H { $$ = 7; }
+ ;
+
+regw : AX { $$ = 0; }
+ | BC { $$ = 1; }
+ | DE { $$ = 2; }
+ | HL { $$ = 3; }
+ ;
+
+regw_na : BC { $$ = 1; }
+ | DE { $$ = 2; }
+ | HL { $$ = 3; }
+ ;
+
+sfr : SPL { $$ = 0xf8; }
+ | SPH { $$ = 0xf9; }
+ | PSW { $$ = 0xfa; }
+ | CS { $$ = 0xfc; }
+ | ES { $$ = 0xfd; }
+ | PMC { $$ = 0xfe; }
+ | MEM { $$ = 0xff; }
+ ;
+
+/* ---------------------------------------------------------------------- */
+/* Shortcuts for groups of opcodes with common encodings. */
+
+addsub : ADD { $$ = 0x00; }
+ | ADDC { $$ = 0x10; }
+ | SUB { $$ = 0x20; }
+ | SUBC { $$ = 0x30; }
+ | CMP { $$ = 0x40; }
+ | AND_ { $$ = 0x50; }
+ | OR { $$ = 0x60; }
+ | XOR { $$ = 0x70; }
+ ;
+
+addsubw : ADDW { $$ = 0x00; }
+ | SUBW { $$ = 0x20; }
+ | CMPW { $$ = 0x40; }
+ ;
+
+andor1 : AND1 { $$ = 0x05; rl78_bit_insn = 1; }
+ | OR1 { $$ = 0x06; rl78_bit_insn = 1; }
+ | XOR1 { $$ = 0x07; rl78_bit_insn = 1; }
+ ;
+
+bt_bf : BT { $$ = 0x02; rl78_bit_insn = 1; rl78_relax (RL78_RELAX_BRANCH, 0); }
+ | BF { $$ = 0x04; rl78_bit_insn = 1; rl78_relax (RL78_RELAX_BRANCH, 0); }
+ | BTCLR { $$ = 0x00; rl78_bit_insn = 1; }
+ ;
+
+setclr1 : SET1 { $$ = 0; rl78_bit_insn = 1; }
+ | CLR1 { $$ = 1; rl78_bit_insn = 1; }
+ ;
+
+oneclrb : ONEB { $$ = 0x00; }
+ | CLRB { $$ = 0x10; }
+ ;
+
+oneclrw : ONEW { $$ = 0x00; }
+ | CLRW { $$ = 0x10; }
+ ;
+
+incdec : INC { $$ = 0x00; }
+ | DEC { $$ = 0x10; }
+ ;
+
+incdecw : INCW { $$ = 0x00; }
+ | DECW { $$ = 0x10; }
+ ;
+
+mov1 : MOV1 { rl78_bit_insn = 1; }
+ ;
+
+%%
+/* ====================================================================== */
+
+static struct
+{
+ const char * string;
+ int token;
+ int val;
+}
+token_table[] =
+{
+ { "r0", X, 0 },
+ { "r1", A, 1 },
+ { "r2", C, 2 },
+ { "r3", B, 3 },
+ { "r4", E, 4 },
+ { "r5", D, 5 },
+ { "r6", L, 6 },
+ { "r7", H, 7 },
+ { "x", X, 0 },
+ { "a", A, 1 },
+ { "c", C, 2 },
+ { "b", B, 3 },
+ { "e", E, 4 },
+ { "d", D, 5 },
+ { "l", L, 6 },
+ { "h", H, 7 },
+
+ { "rp0", AX, 0 },
+ { "rp1", BC, 1 },
+ { "rp2", DE, 2 },
+ { "rp3", HL, 3 },
+ { "ax", AX, 0 },
+ { "bc", BC, 1 },
+ { "de", DE, 2 },
+ { "hl", HL, 3 },
+
+ { "RB0", RB0, 0 },
+ { "RB1", RB1, 1 },
+ { "RB2", RB2, 2 },
+ { "RB3", RB3, 3 },
+
+ { "sp", SP, 0 },
+ { "cy", CY, 0 },
+
+ { "spl", SPL, 0xf8 },
+ { "sph", SPH, 0xf9 },
+ { "psw", PSW, 0xfa },
+ { "cs", CS, 0xfc },
+ { "es", ES, 0xfd },
+ { "pmc", PMC, 0xfe },
+ { "mem", MEM, 0xff },
+
+ { ".s", DOT_S, 0 },
+ { ".b", DOT_B, 0 },
+ { ".w", DOT_W, 0 },
+ { ".l", DOT_L, 0 },
+ { ".a", DOT_A , 0},
+ { ".ub", DOT_UB, 0 },
+ { ".uw", DOT_UW , 0},
+
+ { "c", FLAG, 0 },
+ { "z", FLAG, 1 },
+ { "s", FLAG, 2 },
+ { "o", FLAG, 3 },
+ { "i", FLAG, 8 },
+ { "u", FLAG, 9 },
+
+#define OPC(x) { #x, x, IS_OPCODE }
+
+ OPC(ADD),
+ OPC(ADDC),
+ OPC(ADDW),
+ { "and", AND_, IS_OPCODE },
+ OPC(AND1),
+ OPC(BC),
+ OPC(BF),
+ OPC(BH),
+ OPC(BNC),
+ OPC(BNH),
+ OPC(BNZ),
+ OPC(BR),
+ OPC(BRK),
+ OPC(BRK1),
+ OPC(BT),
+ OPC(BTCLR),
+ OPC(BZ),
+ OPC(CALL),
+ OPC(CALLT),
+ OPC(CLR1),
+ OPC(CLRB),
+ OPC(CLRW),
+ OPC(CMP),
+ OPC(CMP0),
+ OPC(CMPS),
+ OPC(CMPW),
+ OPC(DEC),
+ OPC(DECW),
+ OPC(DI),
+ OPC(DIVHU),
+ OPC(DIVWU),
+ OPC(EI),
+ OPC(HALT),
+ OPC(INC),
+ OPC(INCW),
+ OPC(MACH),
+ OPC(MACHU),
+ OPC(MOV),
+ OPC(MOV1),
+ OPC(MOVS),
+ OPC(MOVW),
+ OPC(MULH),
+ OPC(MULHU),
+ OPC(MULU),
+ OPC(NOP),
+ OPC(NOT1),
+ OPC(ONEB),
+ OPC(ONEW),
+ OPC(OR),
+ OPC(OR1),
+ OPC(POP),
+ OPC(PUSH),
+ OPC(RET),
+ OPC(RETI),
+ OPC(RETB),
+ OPC(ROL),
+ OPC(ROLC),
+ OPC(ROLWC),
+ OPC(ROR),
+ OPC(RORC),
+ OPC(SAR),
+ OPC(SARW),
+ OPC(SEL),
+ OPC(SET1),
+ OPC(SHL),
+ OPC(SHLW),
+ OPC(SHR),
+ OPC(SHRW),
+ OPC(SKC),
+ OPC(SKH),
+ OPC(SKNC),
+ OPC(SKNH),
+ OPC(SKNZ),
+ OPC(SKZ),
+ OPC(STOP),
+ OPC(SUB),
+ OPC(SUBC),
+ OPC(SUBW),
+ OPC(XCH),
+ OPC(XCHW),
+ OPC(XOR),
+ OPC(XOR1),
+};
+
+#define NUM_TOKENS (sizeof (token_table) / sizeof (token_table[0]))
+
+void
+rl78_lex_init (char * beginning, char * ending)
+{
+ rl78_init_start = beginning;
+ rl78_lex_start = beginning;
+ rl78_lex_end = ending;
+ rl78_in_brackets = 0;
+ rl78_last_token = 0;
+
+ rl78_bit_insn = 0;
+
+ setbuf (stdout, 0);
+}
+
+/* Return a pointer to the '.' in a bit index expression (like
+ foo.5), or NULL if none is found. */
+static char *
+find_bit_index (char *tok)
+{
+ char *last_dot = NULL;
+ char *last_digit = NULL;
+ while (*tok && *tok != ',')
+ {
+ if (*tok == '.')
+ {
+ last_dot = tok;
+ last_digit = NULL;
+ }
+ else if (*tok >= '0' && *tok <= '7'
+ && last_dot != NULL
+ && last_digit == NULL)
+ {
+ last_digit = tok;
+ }
+ else if (ISSPACE (*tok))
+ {
+ /* skip */
+ }
+ else
+ {
+ last_dot = NULL;
+ last_digit = NULL;
+ }
+ tok ++;
+ }
+ if (last_dot != NULL
+ && last_digit != NULL)
+ return last_dot;
+ return NULL;
+}
+
+static int
+rl78_lex (void)
+{
+ /*unsigned int ci;*/
+ char * save_input_pointer;
+ char * bit = NULL;
+
+ while (ISSPACE (*rl78_lex_start)
+ && rl78_lex_start != rl78_lex_end)
+ rl78_lex_start ++;
+
+ rl78_last_exp_start = rl78_lex_start;
+
+ if (rl78_lex_start == rl78_lex_end)
+ return 0;
+
+ if (ISALPHA (*rl78_lex_start)
+ || (*rl78_lex_start == '.' && ISALPHA (rl78_lex_start[1])))
+ {
+ unsigned int i;
+ char * e;
+ char save;
+
+ for (e = rl78_lex_start + 1;
+ e < rl78_lex_end && ISALNUM (*e);
+ e ++)
+ ;
+ save = *e;
+ *e = 0;
+
+ for (i = 0; i < NUM_TOKENS; i++)
+ if (strcasecmp (rl78_lex_start, token_table[i].string) == 0
+ && !(token_table[i].val == IS_OPCODE && rl78_last_token != 0)
+ && !(token_table[i].token == FLAG && !need_flag))
+ {
+ rl78_lval.regno = token_table[i].val;
+ *e = save;
+ rl78_lex_start = e;
+ rl78_last_token = token_table[i].token;
+ return token_table[i].token;
+ }
+ *e = save;
+ }
+
+ if (rl78_last_token == 0)
+ {
+ rl78_last_token = UNKNOWN_OPCODE;
+ return UNKNOWN_OPCODE;
+ }
+
+ if (rl78_last_token == UNKNOWN_OPCODE)
+ return 0;
+
+ if (*rl78_lex_start == '[')
+ rl78_in_brackets = 1;
+ if (*rl78_lex_start == ']')
+ rl78_in_brackets = 0;
+
+ /* '.' is funny - the syntax includes it for bitfields, but only for
+ bitfields. We check for it specially so we can allow labels
+ with '.' in them. */
+
+ if (rl78_bit_insn
+ && *rl78_lex_start == '.'
+ && find_bit_index (rl78_lex_start) == rl78_lex_start)
+ {
+ rl78_last_token = *rl78_lex_start;
+ return *rl78_lex_start ++;
+ }
+
+ if ((rl78_in_brackets && *rl78_lex_start == '+')
+ || strchr ("[],#!$:", *rl78_lex_start))
+ {
+ rl78_last_token = *rl78_lex_start;
+ return *rl78_lex_start ++;
+ }
+
+ /* Again, '.' is funny. Look for '.<digit>' at the end of the line
+ or before a comma, which is a bitfield, not an expression. */
+
+ if (rl78_bit_insn)
+ {
+ bit = find_bit_index (rl78_lex_start);
+ if (bit)
+ *bit = 0;
+ else
+ bit = NULL;
+ }
+
+ save_input_pointer = input_line_pointer;
+ input_line_pointer = rl78_lex_start;
+ rl78_lval.exp.X_md = 0;
+ expression (&rl78_lval.exp);
+
+ if (bit)
+ *bit = '.';
+
+ rl78_lex_start = input_line_pointer;
+ input_line_pointer = save_input_pointer;
+ rl78_last_token = EXPR;
+ return EXPR;
+}
+
+int
+rl78_error (const char * str)
+{
+ int len;
+
+ len = rl78_last_exp_start - rl78_init_start;
+
+ as_bad ("%s", rl78_init_start);
+ as_bad ("%*s^ %s", len, "", str);
+ return 0;
+}
+
+static int
+expr_is_sfr (expressionS exp)
+{
+ unsigned long v;
+
+ if (exp.X_op != O_constant)
+ return 0;
+
+ v = exp.X_add_number;
+ if (0xFFF00 <= v && v <= 0xFFFFF)
+ return 1;
+ return 0;
+}
+
+static int
+expr_is_saddr (expressionS exp)
+{
+ unsigned long v;
+
+ if (exp.X_op != O_constant)
+ return 0;
+
+ v = exp.X_add_number;
+ if (0xFFE20 <= v && v <= 0xFFF1F)
+ return 1;
+ return 0;
+}
+
+static int
+expr_is_word_aligned (expressionS exp)
+{
+ unsigned long v;
+
+ if (exp.X_op != O_constant)
+ return 1;
+
+ v = exp.X_add_number;
+ if (v & 1)
+ return 0;
+ return 1;
+
+}
+
+static void
+check_expr_is_bit_index (expressionS exp)
+{
+ int val;
+
+ if (exp.X_op != O_constant)
+ {
+ rl78_error (_("bit index must be a constant"));
+ return;
+ }
+ val = exp.X_add_number;
+
+ if (val < 0 || val > 7)
+ rl78_error (_("rtsd size must be 0..7"));
+}
+
+static int
+exp_val (expressionS exp)
+{
+ if (exp.X_op != O_constant)
+ {
+ rl78_error (_("constant expected"));
+ return 0;
+ }
+ return exp.X_add_number;
+}
+
+static int
+check_expr_is_const (expressionS e, int vmin, int vmax)
+{
+ static char buf[100];
+ if (e.X_op != O_constant
+ || e.X_add_number < vmin
+ || e.X_add_number > vmax)
+ {
+ if (vmin == vmax)
+ sprintf (buf, "%d expected here", vmin);
+ else
+ sprintf (buf, "%d..%d expected here", vmin, vmax);
+ rl78_error(buf);
+ return 0;
+ }
+ return 1;
+}
+
+
diff --git a/gas/config/rx-defs.h b/gas/config/rx-defs.h
new file mode 100644
index 0000000..93c4fa9
--- /dev/null
+++ b/gas/config/rx-defs.h
@@ -0,0 +1,69 @@
+/* rx-defs.h Renesas RX internal definitions
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef RX_DEFS_H
+#define RX_DEFS_H
+
+/* Third operand to rx_op. */
+#define RXREL_SIGNED 0
+#define RXREL_UNSIGNED 1
+#define RXREL_NEGATIVE 2
+#define RXREL_PCREL 3
+#define RXREL_NEGATIVE_BORROW 4
+
+#define RX_RELAX_NONE 0
+#define RX_RELAX_BRANCH 1
+#define RX_RELAX_IMM 2
+#define RX_RELAX_DISP 3
+
+enum rx_cpu_types
+{
+ RX600,
+ RX610,
+ RX200,
+ RX100
+};
+
+extern int rx_pid_register;
+extern int rx_gp_register;
+extern enum rx_cpu_types rx_cpu;
+
+extern int rx_error (const char *);
+extern void rx_lex_init (char *, char *);
+extern void rx_base1 (int);
+extern void rx_base2 (int, int);
+extern void rx_base3 (int, int, int);
+extern void rx_base4 (int, int, int, int);
+extern void rx_field (int, int, int);
+extern void rx_op (expressionS, int, int);
+extern void rx_disp3 (expressionS, int);
+extern void rx_field5s (expressionS);
+extern void rx_field5s2 (expressionS);
+extern void rx_relax (int, int);
+extern void rx_linkrelax_dsp (int);
+extern void rx_linkrelax_imm (int);
+extern void rx_linkrelax_branch (void);
+extern int rx_parse (void);
+extern int rx_wrap (void);
+
+extern char * rx_lex_start;
+extern char * rx_lex_end;
+
+#endif /* RX_DEFS_H */
diff --git a/gas/config/rx-parse.y b/gas/config/rx-parse.y
new file mode 100644
index 0000000..d93b4f6
--- /dev/null
+++ b/gas/config/rx-parse.y
@@ -0,0 +1,1645 @@
+/* rx-parse.y Renesas RX parser
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+%{
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "rx-defs.h"
+
+static int rx_lex (void);
+
+#define COND_EQ 0
+#define COND_NE 1
+
+#define MEMEX 0x06
+
+#define BSIZE 0
+#define WSIZE 1
+#define LSIZE 2
+
+/* .sb .sw .l .uw */
+static int sizemap[] = { BSIZE, WSIZE, LSIZE, WSIZE };
+
+/* Ok, here are the rules for using these macros...
+
+ B*() is used to specify the base opcode bytes. Fields to be filled
+ in later, leave zero. Call this first.
+
+ F() and FE() are used to fill in fields within the base opcode bytes. You MUST
+ call B*() before any F() or FE().
+
+ [UN]*O*(), PC*() appends operands to the end of the opcode. You
+ must call P() and B*() before any of these, so that the fixups
+ have the right byte location.
+ O = signed, UO = unsigned, NO = negated, PC = pcrel
+
+ IMM() adds an immediate and fills in the field for it.
+ NIMM() same, but negates the immediate.
+ NBIMM() same, but negates the immediate, for sbb.
+ DSP() adds a displacement, and fills in the field for it.
+
+ Note that order is significant for the O, IMM, and DSP macros, as
+ they append their data to the operand buffer in the order that you
+ call them.
+
+ Use "disp" for displacements whenever possible; this handles the
+ "0" case properly. */
+
+#define B1(b1) rx_base1 (b1)
+#define B2(b1, b2) rx_base2 (b1, b2)
+#define B3(b1, b2, b3) rx_base3 (b1, b2, b3)
+#define B4(b1, b2, b3, b4) rx_base4 (b1, b2, b3, b4)
+
+/* POS is bits from the MSB of the first byte to the LSB of the last byte. */
+#define F(val,pos,sz) rx_field (val, pos, sz)
+#define FE(exp,pos,sz) rx_field (exp_val (exp), pos, sz);
+
+#define O1(v) rx_op (v, 1, RXREL_SIGNED); rx_range (v, -128, 255)
+#define O2(v) rx_op (v, 2, RXREL_SIGNED); rx_range (v, -32768, 65536)
+#define O3(v) rx_op (v, 3, RXREL_SIGNED); rx_range (v, -8388608, 16777216)
+#define O4(v) rx_op (v, 4, RXREL_SIGNED)
+
+#define UO1(v) rx_op (v, 1, RXREL_UNSIGNED); rx_range (v, 0, 255)
+#define UO2(v) rx_op (v, 2, RXREL_UNSIGNED); rx_range (v, 0, 65536)
+#define UO3(v) rx_op (v, 3, RXREL_UNSIGNED); rx_range (v, 0, 16777216)
+#define UO4(v) rx_op (v, 4, RXREL_UNSIGNED)
+
+#define NO1(v) rx_op (v, 1, RXREL_NEGATIVE)
+#define NO2(v) rx_op (v, 2, RXREL_NEGATIVE)
+#define NO3(v) rx_op (v, 3, RXREL_NEGATIVE)
+#define NO4(v) rx_op (v, 4, RXREL_NEGATIVE)
+
+#define PC1(v) rx_op (v, 1, RXREL_PCREL)
+#define PC2(v) rx_op (v, 2, RXREL_PCREL)
+#define PC3(v) rx_op (v, 3, RXREL_PCREL)
+
+#define IMM_(v,pos,size) F (immediate (v, RXREL_SIGNED, pos, size), pos, 2); \
+ if (v.X_op != O_constant && v.X_op != O_big) rx_linkrelax_imm (pos)
+#define IMM(v,pos) IMM_ (v, pos, 32)
+#define IMMW(v,pos) IMM_ (v, pos, 16); rx_range (v, -32768, 65536)
+#define IMMB(v,pos) IMM_ (v, pos, 8); rx_range (v, -128, 255)
+#define NIMM(v,pos) F (immediate (v, RXREL_NEGATIVE, pos, 32), pos, 2)
+#define NBIMM(v,pos) F (immediate (v, RXREL_NEGATIVE_BORROW, pos, 32), pos, 2)
+#define DSP(v,pos,msz) if (!v.X_md) rx_relax (RX_RELAX_DISP, pos); \
+ else rx_linkrelax_dsp (pos); \
+ F (displacement (v, msz), pos, 2)
+
+#define id24(a,b2,b3) B3 (0xfb+a, b2, b3)
+
+static void rx_check_float_support (void);
+static int rx_intop (expressionS, int, int);
+static int rx_uintop (expressionS, int);
+static int rx_disp3op (expressionS);
+static int rx_disp5op (expressionS *, int);
+static int rx_disp5op0 (expressionS *, int);
+static int exp_val (expressionS exp);
+static expressionS zero_expr (void);
+static int immediate (expressionS, int, int, int);
+static int displacement (expressionS, int);
+static void rtsd_immediate (expressionS);
+static void rx_range (expressionS, int, int);
+
+static int need_flag = 0;
+static int rx_in_brackets = 0;
+static int rx_last_token = 0;
+static char * rx_init_start;
+static char * rx_last_exp_start = 0;
+static int sub_op;
+static int sub_op2;
+
+#define YYDEBUG 1
+#define YYERROR_VERBOSE 1
+
+%}
+
+%name-prefix="rx_"
+
+%union {
+ int regno;
+ expressionS exp;
+}
+
+%type <regno> REG FLAG CREG BCND BMCND SCCND
+%type <regno> flag bwl bw memex
+%type <exp> EXPR disp
+
+%token REG FLAG CREG
+
+%token EXPR UNKNOWN_OPCODE IS_OPCODE
+
+%token DOT_S DOT_B DOT_W DOT_L DOT_A DOT_UB DOT_UW
+
+%token ABS ADC ADD AND_
+%token BCLR BCND BMCND BNOT BRA BRK BSET BSR BTST
+%token CLRPSW CMP
+%token DBT DIV DIVU
+%token EDIV EDIVU EMUL EMULU
+%token FADD FCMP FDIV FMUL FREIT FSUB FTOI
+%token INT ITOF
+%token JMP JSR
+%token MACHI MACLO MAX MIN MOV MOVU MUL MULHI MULLO MULU MVFACHI MVFACMI MVFACLO
+%token MVFC MVTACHI MVTACLO MVTC MVTIPL
+%token NEG NOP NOT
+%token OR
+%token POP POPC POPM PUSH PUSHA PUSHC PUSHM
+%token RACW REIT REVL REVW RMPA ROLC RORC ROTL ROTR ROUND RTE RTFI RTS RTSD
+%token SAT SATR SBB SCCND SCMPU SETPSW SHAR SHLL SHLR SMOVB SMOVF
+%token SMOVU SSTR STNZ STOP STZ SUB SUNTIL SWHILE
+%token TST
+%token WAIT
+%token XCHG XOR
+
+%%
+/* ====================================================================== */
+
+statement :
+
+ UNKNOWN_OPCODE
+ { as_bad (_("Unknown opcode: %s"), rx_init_start); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BRK
+ { B1 (0x00); }
+
+ | DBT
+ { B1 (0x01); }
+
+ | RTS
+ { B1 (0x02); }
+
+ | NOP
+ { B1 (0x03); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BRA EXPR
+ { if (rx_disp3op ($2))
+ { B1 (0x08); rx_disp3 ($2, 5); }
+ else if (rx_intop ($2, 8, 8))
+ { B1 (0x2e); PC1 ($2); }
+ else if (rx_intop ($2, 16, 16))
+ { B1 (0x38); PC2 ($2); }
+ else if (rx_intop ($2, 24, 24))
+ { B1 (0x04); PC3 ($2); }
+ else
+ { rx_relax (RX_RELAX_BRANCH, 0);
+ rx_linkrelax_branch ();
+ /* We'll convert this to a longer one later if needed. */
+ B1 (0x08); rx_disp3 ($2, 5); } }
+
+ | BRA DOT_A EXPR
+ { B1 (0x04); PC3 ($3); }
+
+ | BRA DOT_S EXPR
+ { B1 (0x08); rx_disp3 ($3, 5); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BSR EXPR
+ { if (rx_intop ($2, 16, 16))
+ { B1 (0x39); PC2 ($2); }
+ else if (rx_intop ($2, 24, 24))
+ { B1 (0x05); PC3 ($2); }
+ else
+ { rx_relax (RX_RELAX_BRANCH, 0);
+ rx_linkrelax_branch ();
+ B1 (0x39); PC2 ($2); } }
+ | BSR DOT_A EXPR
+ { B1 (0x05), PC3 ($3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BCND DOT_S EXPR
+ { if ($1 == COND_EQ || $1 == COND_NE)
+ { B1 ($1 == COND_EQ ? 0x10 : 0x18); rx_disp3 ($3, 5); }
+ else
+ as_bad (_("Only BEQ and BNE may have .S")); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BCND DOT_B EXPR
+ { B1 (0x20); F ($1, 4, 4); PC1 ($3); }
+
+ | BRA DOT_B EXPR
+ { B1 (0x2e), PC1 ($3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BRA DOT_W EXPR
+ { B1 (0x38), PC2 ($3); }
+ | BSR DOT_W EXPR
+ { B1 (0x39), PC2 ($3); }
+ | BCND DOT_W EXPR
+ { if ($1 == COND_EQ || $1 == COND_NE)
+ { B1 ($1 == COND_EQ ? 0x3a : 0x3b); PC2 ($3); }
+ else
+ as_bad (_("Only BEQ and BNE may have .W")); }
+ | BCND EXPR
+ { if ($1 == COND_EQ || $1 == COND_NE)
+ {
+ rx_relax (RX_RELAX_BRANCH, 0);
+ rx_linkrelax_branch ();
+ B1 ($1 == COND_EQ ? 0x10 : 0x18); rx_disp3 ($2, 5);
+ }
+ else
+ {
+ rx_relax (RX_RELAX_BRANCH, 0);
+ /* This is because we might turn it into a
+ jump-over-jump long branch. */
+ rx_linkrelax_branch ();
+ B1 (0x20); F ($1, 4, 4); PC1 ($2);
+ } }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV DOT_B '#' EXPR ',' disp '[' REG ']'
+ /* rx_disp5op changes the value if it succeeds, so keep it last. */
+ { if ($8 <= 7 && rx_uintop ($4, 8) && rx_disp5op0 (&$6, BSIZE))
+ { B2 (0x3c, 0); rx_field5s2 ($6); F ($8, 9, 3); O1 ($4); }
+ else
+ { B2 (0xf8, 0x04); F ($8, 8, 4); DSP ($6, 6, BSIZE); O1 ($4);
+ if ($4.X_op != O_constant && $4.X_op != O_big) rx_linkrelax_imm (12); } }
+
+ | MOV DOT_W '#' EXPR ',' disp '[' REG ']'
+ { if ($8 <= 7 && rx_uintop ($4, 8) && rx_disp5op0 (&$6, WSIZE))
+ { B2 (0x3d, 0); rx_field5s2 ($6); F ($8, 9, 3); O1 ($4); }
+ else
+ { B2 (0xf8, 0x01); F ($8, 8, 4); DSP ($6, 6, WSIZE); IMMW ($4, 12); } }
+
+ | MOV DOT_L '#' EXPR ',' disp '[' REG ']'
+ { if ($8 <= 7 && rx_uintop ($4, 8) && rx_disp5op0 (&$6, LSIZE))
+ { B2 (0x3e, 0); rx_field5s2 ($6); F ($8, 9, 3); O1 ($4); }
+ else
+ { B2 (0xf8, 0x02); F ($8, 8, 4); DSP ($6, 6, LSIZE); IMM ($4, 12); } }
+
+/* ---------------------------------------------------------------------- */
+
+ | RTSD '#' EXPR ',' REG '-' REG
+ { B2 (0x3f, 0); F ($5, 8, 4); F ($7, 12, 4); rtsd_immediate ($3);
+ if ($5 == 0)
+ rx_error (_("RTSD cannot pop R0"));
+ if ($5 > $7)
+ rx_error (_("RTSD first reg must be <= second reg")); }
+
+/* ---------------------------------------------------------------------- */
+
+ | CMP REG ',' REG
+ { B2 (0x47, 0); F ($2, 8, 4); F ($4, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | CMP disp '[' REG ']' DOT_UB ',' REG
+ { B2 (0x44, 0); F ($4, 8, 4); F ($8, 12, 4); DSP ($2, 6, BSIZE); }
+
+ | CMP disp '[' REG ']' memex ',' REG
+ { B3 (MEMEX, 0x04, 0); F ($6, 8, 2); F ($4, 16, 4); F ($8, 20, 4); DSP ($2, 14, sizemap[$6]); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOVU bw REG ',' REG
+ { B2 (0x5b, 0x00); F ($2, 5, 1); F ($3, 8, 4); F ($5, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOVU bw '[' REG ']' ',' REG
+ { B2 (0x58, 0x00); F ($2, 5, 1); F ($4, 8, 4); F ($7, 12, 4); }
+
+ | MOVU bw EXPR '[' REG ']' ',' REG
+ { if ($5 <= 7 && $8 <= 7 && rx_disp5op (&$3, $2))
+ { B2 (0xb0, 0); F ($2, 4, 1); F ($5, 9, 3); F ($8, 13, 3); rx_field5s ($3); }
+ else
+ { B2 (0x58, 0x00); F ($2, 5, 1); F ($5, 8, 4); F ($8, 12, 4); DSP ($3, 6, $2); } }
+
+/* ---------------------------------------------------------------------- */
+
+ | SUB '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x60, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else
+ /* This is really an add, but we negate the immediate. */
+ { B2 (0x70, 0); F ($5, 8, 4); F ($5, 12, 4); NIMM ($3, 6); } }
+
+ | CMP '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x61, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else if (rx_uintop ($3, 8))
+ { B2 (0x75, 0x50); F ($5, 12, 4); UO1 ($3); }
+ else
+ { B2 (0x74, 0x00); F ($5, 12, 4); IMM ($3, 6); } }
+
+ | ADD '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x62, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else
+ { B2 (0x70, 0); F ($5, 8, 4); F ($5, 12, 4); IMM ($3, 6); } }
+
+ | MUL '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x63, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else
+ { B2 (0x74, 0x10); F ($5, 12, 4); IMM ($3, 6); } }
+
+ | AND_ '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x64, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else
+ { B2 (0x74, 0x20); F ($5, 12, 4); IMM ($3, 6); } }
+
+ | OR '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x65, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else
+ { B2 (0x74, 0x30); F ($5, 12, 4); IMM ($3, 6); } }
+
+ | MOV DOT_L '#' EXPR ',' REG
+ { if (rx_uintop ($4, 4))
+ { B2 (0x66, 0); FE ($4, 8, 4); F ($6, 12, 4); }
+ else if (rx_uintop ($4, 8))
+ { B2 (0x75, 0x40); F ($6, 12, 4); UO1 ($4); }
+ else
+ { B2 (0xfb, 0x02); F ($6, 8, 4); IMM ($4, 12); } }
+
+ | MOV '#' EXPR ',' REG
+ { if (rx_uintop ($3, 4))
+ { B2 (0x66, 0); FE ($3, 8, 4); F ($5, 12, 4); }
+ else if (rx_uintop ($3, 8))
+ { B2 (0x75, 0x40); F ($5, 12, 4); UO1 ($3); }
+ else
+ { B2 (0xfb, 0x02); F ($5, 8, 4); IMM ($3, 12); } }
+
+/* ---------------------------------------------------------------------- */
+
+ | RTSD '#' EXPR
+ { B1 (0x67); rtsd_immediate ($3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SHLR { sub_op = 0; } op_shift
+ | SHAR { sub_op = 1; } op_shift
+ | SHLL { sub_op = 2; } op_shift
+
+/* ---------------------------------------------------------------------- */
+
+ | PUSHM REG '-' REG
+ {
+ if ($2 == $4)
+ { B2 (0x7e, 0x80); F (LSIZE, 10, 2); F ($2, 12, 4); }
+ else
+ { B2 (0x6e, 0); F ($2, 8, 4); F ($4, 12, 4); }
+ if ($2 == 0)
+ rx_error (_("PUSHM cannot push R0"));
+ if ($2 > $4)
+ rx_error (_("PUSHM first reg must be <= second reg")); }
+
+/* ---------------------------------------------------------------------- */
+
+ | POPM REG '-' REG
+ {
+ if ($2 == $4)
+ { B2 (0x7e, 0xb0); F ($2, 12, 4); }
+ else
+ { B2 (0x6f, 0); F ($2, 8, 4); F ($4, 12, 4); }
+ if ($2 == 0)
+ rx_error (_("POPM cannot pop R0"));
+ if ($2 > $4)
+ rx_error (_("POPM first reg must be <= second reg")); }
+
+/* ---------------------------------------------------------------------- */
+
+ | ADD '#' EXPR ',' REG ',' REG
+ { B2 (0x70, 0x00); F ($5, 8, 4); F ($7, 12, 4); IMM ($3, 6); }
+
+/* ---------------------------------------------------------------------- */
+
+ | INT '#' EXPR
+ { B2(0x75, 0x60), UO1 ($3); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BSET '#' EXPR ',' REG
+ { B2 (0x78, 0); FE ($3, 7, 5); F ($5, 12, 4); }
+ | BCLR '#' EXPR ',' REG
+ { B2 (0x7a, 0); FE ($3, 7, 5); F ($5, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BTST '#' EXPR ',' REG
+ { B2 (0x7c, 0x00); FE ($3, 7, 5); F ($5, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SAT REG
+ { B2 (0x7e, 0x30); F ($2, 12, 4); }
+ | RORC REG
+ { B2 (0x7e, 0x40); F ($2, 12, 4); }
+ | ROLC REG
+ { B2 (0x7e, 0x50); F ($2, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | PUSH bwl REG
+ { B2 (0x7e, 0x80); F ($2, 10, 2); F ($3, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | POP REG
+ { B2 (0x7e, 0xb0); F ($2, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | PUSHC CREG
+ { if ($2 < 16)
+ { B2 (0x7e, 0xc0); F ($2, 12, 4); }
+ else
+ as_bad (_("PUSHC can only push the first 16 control registers")); }
+
+/* ---------------------------------------------------------------------- */
+
+ | POPC CREG
+ { if ($2 < 16)
+ { B2 (0x7e, 0xe0); F ($2, 12, 4); }
+ else
+ as_bad (_("POPC can only pop the first 16 control registers")); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SETPSW flag
+ { B2 (0x7f, 0xa0); F ($2, 12, 4); }
+ | CLRPSW flag
+ { B2 (0x7f, 0xb0); F ($2, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | JMP REG
+ { B2 (0x7f, 0x00); F ($2, 12, 4); }
+ | JSR REG
+ { B2 (0x7f, 0x10); F ($2, 12, 4); }
+ | BRA opt_l REG
+ { B2 (0x7f, 0x40); F ($3, 12, 4); }
+ | BSR opt_l REG
+ { B2 (0x7f, 0x50); F ($3, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SCMPU
+ { B2 (0x7f, 0x83); }
+ | SMOVU
+ { B2 (0x7f, 0x87); }
+ | SMOVB
+ { B2 (0x7f, 0x8b); }
+ | SMOVF
+ { B2 (0x7f, 0x8f); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SUNTIL bwl
+ { B2 (0x7f, 0x80); F ($2, 14, 2); }
+ | SWHILE bwl
+ { B2 (0x7f, 0x84); F ($2, 14, 2); }
+ | SSTR bwl
+ { B2 (0x7f, 0x88); F ($2, 14, 2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | RMPA bwl
+ { B2 (0x7f, 0x8c); F ($2, 14, 2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | RTFI
+ { B2 (0x7f, 0x94); }
+ | RTE
+ { B2 (0x7f, 0x95); }
+ | WAIT
+ { B2 (0x7f, 0x96); }
+ | SATR
+ { B2 (0x7f, 0x93); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MVTIPL '#' EXPR
+ { B3 (0x75, 0x70, 0x00); FE ($3, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ /* rx_disp5op changes the value if it succeeds, so keep it last. */
+ | MOV bwl REG ',' EXPR '[' REG ']'
+ { if ($3 <= 7 && $7 <= 7 && rx_disp5op (&$5, $2))
+ { B2 (0x80, 0); F ($2, 2, 2); F ($7, 9, 3); F ($3, 13, 3); rx_field5s ($5); }
+ else
+ { B2 (0xc3, 0x00); F ($2, 2, 2); F ($7, 8, 4); F ($3, 12, 4); DSP ($5, 4, $2); }}
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl EXPR '[' REG ']' ',' REG
+ { if ($5 <= 7 && $8 <= 7 && rx_disp5op (&$3, $2))
+ { B2 (0x88, 0); F ($2, 2, 2); F ($5, 9, 3); F ($8, 13, 3); rx_field5s ($3); }
+ else
+ { B2 (0xcc, 0x00); F ($2, 2, 2); F ($5, 8, 4); F ($8, 12, 4); DSP ($3, 6, $2); } }
+
+/* ---------------------------------------------------------------------- */
+
+ /* MOV a,b - if a is a reg and b is mem, src and dest are
+ swapped. */
+
+ /* We don't use "disp" here because it causes a shift/reduce
+ conflict with the other displacement-less patterns. */
+
+ | MOV bwl REG ',' '[' REG ']'
+ { B2 (0xc3, 0x00); F ($2, 2, 2); F ($6, 8, 4); F ($3, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl '[' REG ']' ',' disp '[' REG ']'
+ { B2 (0xc0, 0); F ($2, 2, 2); F ($4, 8, 4); F ($9, 12, 4); DSP ($7, 4, $2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl EXPR '[' REG ']' ',' disp '[' REG ']'
+ { B2 (0xc0, 0x00); F ($2, 2, 2); F ($5, 8, 4); F ($10, 12, 4); DSP ($3, 6, $2); DSP ($8, 4, $2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl REG ',' REG
+ { B2 (0xcf, 0x00); F ($2, 2, 2); F ($3, 8, 4); F ($5, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl '[' REG ']' ',' REG
+ { B2 (0xcc, 0x00); F ($2, 2, 2); F ($4, 8, 4); F ($7, 12, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BSET '#' EXPR ',' disp '[' REG ']' DOT_B
+ { B2 (0xf0, 0x00); F ($7, 8, 4); FE ($3, 13, 3); DSP ($5, 6, BSIZE); }
+ | BCLR '#' EXPR ',' disp '[' REG ']' DOT_B
+ { B2 (0xf0, 0x08); F ($7, 8, 4); FE ($3, 13, 3); DSP ($5, 6, BSIZE); }
+ | BTST '#' EXPR ',' disp '[' REG ']' DOT_B
+ { B2 (0xf4, 0x00); F ($7, 8, 4); FE ($3, 13, 3); DSP ($5, 6, BSIZE); }
+
+/* ---------------------------------------------------------------------- */
+
+ | PUSH bwl disp '[' REG ']'
+ { B2 (0xf4, 0x08); F ($2, 14, 2); F ($5, 8, 4); DSP ($3, 6, $2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SBB { sub_op = 0; } op_dp20_rm_l
+ | NEG { sub_op = 1; sub_op2 = 1; } op_dp20_rr
+ | ADC { sub_op = 2; } op_dp20_rim_l
+ | ABS { sub_op = 3; sub_op2 = 2; } op_dp20_rr
+ | MAX { sub_op = 4; } op_dp20_rim
+ | MIN { sub_op = 5; } op_dp20_rim
+ | EMUL { sub_op = 6; } op_dp20_i
+ | EMULU { sub_op = 7; } op_dp20_i
+ | DIV { sub_op = 8; } op_dp20_rim
+ | DIVU { sub_op = 9; } op_dp20_rim
+ | TST { sub_op = 12; } op_dp20_rim
+ | XOR { sub_op = 13; } op_dp20_rim
+ | NOT { sub_op = 14; sub_op2 = 0; } op_dp20_rr
+ | STZ { sub_op = 14; } op_dp20_i
+ | STNZ { sub_op = 15; } op_dp20_i
+
+/* ---------------------------------------------------------------------- */
+
+ | EMUL { sub_op = 6; } op_xchg
+ | EMULU { sub_op = 7; } op_xchg
+ | XCHG { sub_op = 16; } op_xchg
+ | ITOF { sub_op = 17; } op_xchg
+
+/* ---------------------------------------------------------------------- */
+
+ | BSET REG ',' REG
+ { id24 (1, 0x63, 0x00); F ($4, 16, 4); F ($2, 20, 4); }
+ | BCLR REG ',' REG
+ { id24 (1, 0x67, 0x00); F ($4, 16, 4); F ($2, 20, 4); }
+ | BTST REG ',' REG
+ { id24 (1, 0x6b, 0x00); F ($4, 16, 4); F ($2, 20, 4); }
+ | BNOT REG ',' REG
+ { id24 (1, 0x6f, 0x00); F ($4, 16, 4); F ($2, 20, 4); }
+
+ | BSET REG ',' disp '[' REG ']' opt_b
+ { id24 (1, 0x60, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); }
+ | BCLR REG ',' disp '[' REG ']' opt_b
+ { id24 (1, 0x64, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); }
+ | BTST REG ',' disp '[' REG ']' opt_b
+ { id24 (1, 0x68, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); }
+ | BNOT REG ',' disp '[' REG ']' opt_b
+ { id24 (1, 0x6c, 0x00); F ($6, 16, 4); F ($2, 20, 4); DSP ($4, 14, BSIZE); }
+
+/* ---------------------------------------------------------------------- */
+
+ | FSUB { sub_op = 0; } float2_op
+ | FCMP { sub_op = 1; } float2_op
+ | FADD { sub_op = 2; } float2_op
+ | FMUL { sub_op = 3; } float2_op
+ | FDIV { sub_op = 4; } float2_op
+ | FTOI { sub_op = 5; } float2_op_ni
+ | ROUND { sub_op = 6; } float2_op_ni
+
+/* ---------------------------------------------------------------------- */
+
+ | SCCND DOT_L REG
+ { id24 (1, 0xdb, 0x00); F ($1, 20, 4); F ($3, 16, 4); }
+ | SCCND bwl disp '[' REG ']'
+ { id24 (1, 0xd0, 0x00); F ($1, 20, 4); F ($2, 12, 2); F ($5, 16, 4); DSP ($3, 14, $2); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BMCND '#' EXPR ',' disp '[' REG ']' opt_b
+ { id24 (1, 0xe0, 0x00); F ($1, 20, 4); FE ($3, 11, 3);
+ F ($7, 16, 4); DSP ($5, 14, BSIZE); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BNOT '#' EXPR ',' disp '[' REG ']' opt_b
+ { id24 (1, 0xe0, 0x0f); FE ($3, 11, 3); F ($7, 16, 4);
+ DSP ($5, 14, BSIZE); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MULHI REG ',' REG
+ { id24 (2, 0x00, 0x00); F ($2, 16, 4); F ($4, 20, 4); }
+ | MULLO REG ',' REG
+ { id24 (2, 0x01, 0x00); F ($2, 16, 4); F ($4, 20, 4); }
+ | MACHI REG ',' REG
+ { id24 (2, 0x04, 0x00); F ($2, 16, 4); F ($4, 20, 4); }
+ | MACLO REG ',' REG
+ { id24 (2, 0x05, 0x00); F ($2, 16, 4); F ($4, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ /* We don't have syntax for these yet. */
+ | MVTACHI REG
+ { id24 (2, 0x17, 0x00); F ($2, 20, 4); }
+ | MVTACLO REG
+ { id24 (2, 0x17, 0x10); F ($2, 20, 4); }
+ | MVFACHI REG
+ { id24 (2, 0x1f, 0x00); F ($2, 20, 4); }
+ | MVFACMI REG
+ { id24 (2, 0x1f, 0x20); F ($2, 20, 4); }
+ | MVFACLO REG
+ { id24 (2, 0x1f, 0x10); F ($2, 20, 4); }
+
+ | RACW '#' EXPR
+ { id24 (2, 0x18, 0x00);
+ if (rx_uintop ($3, 4) && $3.X_add_number == 1)
+ ;
+ else if (rx_uintop ($3, 4) && $3.X_add_number == 2)
+ F (1, 19, 1);
+ else
+ as_bad (_("RACW expects #1 or #2"));}
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl REG ',' '[' REG '+' ']'
+ { id24 (2, 0x20, 0); F ($2, 14, 2); F ($6, 16, 4); F ($3, 20, 4); }
+ | MOV bwl REG ',' '[' '-' REG ']'
+ { id24 (2, 0x24, 0); F ($2, 14, 2); F ($7, 16, 4); F ($3, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl '[' REG '+' ']' ',' REG
+ { id24 (2, 0x28, 0); F ($2, 14, 2); F ($4, 16, 4); F ($8, 20, 4); }
+ | MOV bwl '[' '-' REG ']' ',' REG
+ { id24 (2, 0x2c, 0); F ($2, 14, 2); F ($5, 16, 4); F ($8, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOVU bw '[' REG '+' ']' ',' REG
+ { id24 (2, 0x38, 0); F ($2, 15, 1); F ($4, 16, 4); F ($8, 20, 4); }
+ | MOVU bw '[' '-' REG ']' ',' REG
+ { id24 (2, 0x3c, 0); F ($2, 15, 1); F ($5, 16, 4); F ($8, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | ROTL { sub_op = 6; } op_shift_rot
+ | ROTR { sub_op = 4; } op_shift_rot
+ | REVW { sub_op = 5; } op_shift_rot
+ | REVL { sub_op = 7; } op_shift_rot
+
+/* ---------------------------------------------------------------------- */
+
+ | MVTC REG ',' CREG
+ { id24 (2, 0x68, 0x00); F ($4 % 16, 20, 4); F ($4 / 16, 15, 1);
+ F ($2, 16, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MVFC CREG ',' REG
+ { id24 (2, 0x6a, 0); F ($2, 15, 5); F ($4, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | ROTL '#' EXPR ',' REG
+ { id24 (2, 0x6e, 0); FE ($3, 15, 5); F ($5, 20, 4); }
+ | ROTR '#' EXPR ',' REG
+ { id24 (2, 0x6c, 0); FE ($3, 15, 5); F ($5, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MVTC '#' EXPR ',' CREG
+ { id24 (2, 0x73, 0x00); F ($5, 19, 5); IMM ($3, 12); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BMCND '#' EXPR ',' REG
+ { id24 (2, 0xe0, 0x00); F ($1, 16, 4); FE ($3, 11, 5);
+ F ($5, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | BNOT '#' EXPR ',' REG
+ { id24 (2, 0xe0, 0xf0); FE ($3, 11, 5); F ($5, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | MOV bwl REG ',' '[' REG ',' REG ']'
+ { id24 (3, 0x00, 0); F ($2, 10, 2); F ($6, 12, 4); F ($8, 16, 4); F ($3, 20, 4); }
+
+ | MOV bwl '[' REG ',' REG ']' ',' REG
+ { id24 (3, 0x40, 0); F ($2, 10, 2); F ($4, 12, 4); F ($6, 16, 4); F ($9, 20, 4); }
+
+ | MOVU bw '[' REG ',' REG ']' ',' REG
+ { id24 (3, 0xc0, 0); F ($2, 10, 2); F ($4, 12, 4); F ($6, 16, 4); F ($9, 20, 4); }
+
+/* ---------------------------------------------------------------------- */
+
+ | SUB { sub_op = 0; } op_subadd
+ | ADD { sub_op = 2; } op_subadd
+ | MUL { sub_op = 3; } op_subadd
+ | AND_ { sub_op = 4; } op_subadd
+ | OR { sub_op = 5; } op_subadd
+
+/* ---------------------------------------------------------------------- */
+/* There is no SBB #imm so we fake it with ADC. */
+
+ | SBB '#' EXPR ',' REG
+ { id24 (2, 0x70, 0x20); F ($5, 20, 4); NBIMM ($3, 12); }
+
+/* ---------------------------------------------------------------------- */
+
+ ;
+
+/* ====================================================================== */
+
+op_subadd
+ : REG ',' REG
+ { B2 (0x43 + (sub_op<<2), 0); F ($1, 8, 4); F ($3, 12, 4); }
+ | disp '[' REG ']' DOT_UB ',' REG
+ { B2 (0x40 + (sub_op<<2), 0); F ($3, 8, 4); F ($7, 12, 4); DSP ($1, 6, BSIZE); }
+ | disp '[' REG ']' memex ',' REG
+ { B3 (MEMEX, sub_op<<2, 0); F ($5, 8, 2); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, sizemap[$5]); }
+ | REG ',' REG ',' REG
+ { id24 (4, sub_op<<4, 0), F ($5, 12, 4), F ($1, 16, 4), F ($3, 20, 4); }
+ ;
+
+/* sbb, neg, adc, abs, max, min, div, divu, tst, not, xor, stz, stnz, emul, emulu */
+
+op_dp20_rm_l
+ : REG ',' REG
+ { id24 (1, 0x03 + (sub_op<<2), 0x00); F ($1, 16, 4); F ($3, 20, 4); }
+ | disp '[' REG ']' opt_l ',' REG
+ { B4 (MEMEX, 0xa0, 0x00 + sub_op, 0x00);
+ F ($3, 24, 4); F ($7, 28, 4); DSP ($1, 14, LSIZE); }
+ ;
+
+/* neg, adc, abs, max, min, div, divu, tst, not, xor, stz, stnz, emul, emulu */
+
+op_dp20_rm
+ : REG ',' REG
+ { id24 (1, 0x03 + (sub_op<<2), 0x00); F ($1, 16, 4); F ($3, 20, 4); }
+ | disp '[' REG ']' DOT_UB ',' REG
+ { id24 (1, 0x00 + (sub_op<<2), 0x00); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, BSIZE); }
+ | disp '[' REG ']' memex ',' REG
+ { B4 (MEMEX, 0x20 + ($5 << 6), 0x00 + sub_op, 0x00);
+ F ($3, 24, 4); F ($7, 28, 4); DSP ($1, 14, sizemap[$5]); }
+ ;
+
+op_dp20_i
+ : '#' EXPR ',' REG
+ { id24 (2, 0x70, sub_op<<4); F ($4, 20, 4); IMM ($2, 12); }
+ ;
+
+op_dp20_rim
+ : op_dp20_rm
+ | op_dp20_i
+ ;
+
+op_dp20_rim_l
+ : op_dp20_rm_l
+ | op_dp20_i
+ ;
+
+op_dp20_rr
+ : REG ',' REG
+ { id24 (1, 0x03 + (sub_op<<2), 0x00); F ($1, 16, 4); F ($3, 20, 4); }
+ | REG
+ { B2 (0x7e, sub_op2 << 4); F ($1, 12, 4); }
+ ;
+
+/* xchg, itof, emul, emulu */
+op_xchg
+ : REG ',' REG
+ { id24 (1, 0x03 + (sub_op<<2), 0); F ($1, 16, 4); F ($3, 20, 4); }
+ | disp '[' REG ']' DOT_UB ',' REG
+ { id24 (1, 0x00 + (sub_op<<2), 0); F ($3, 16, 4); F ($7, 20, 4); DSP ($1, 14, BSIZE); }
+ | disp '[' REG ']' memex ',' REG
+ { B4 (MEMEX, 0x20, 0x00 + sub_op, 0); F ($5, 8, 2); F ($3, 24, 4); F ($7, 28, 4);
+ DSP ($1, 14, sizemap[$5]); }
+ ;
+
+/* 000:SHLR, 001:SHAR, 010:SHLL, 011:-, 100:ROTR, 101:REVW, 110:ROTL, 111:REVL */
+op_shift_rot
+ : REG ',' REG
+ { id24 (2, 0x60 + sub_op, 0); F ($1, 16, 4); F ($3, 20, 4); }
+ ;
+op_shift
+ : '#' EXPR ',' REG
+ { B2 (0x68 + (sub_op<<1), 0); FE ($2, 7, 5); F ($4, 12, 4); }
+ | '#' EXPR ',' REG ',' REG
+ { id24 (2, 0x80 + (sub_op << 5), 0); FE ($2, 11, 5); F ($4, 16, 4); F ($6, 20, 4); }
+ | op_shift_rot
+ ;
+
+
+float2_op
+ : { rx_check_float_support (); }
+ '#' EXPR ',' REG
+ { id24 (2, 0x72, sub_op << 4); F ($5, 20, 4); O4 ($3); }
+ | float2_op_ni
+ ;
+
+float2_op_ni
+ : { rx_check_float_support (); }
+ REG ',' REG
+ { id24 (1, 0x83 + (sub_op << 2), 0); F ($2, 16, 4); F ($4, 20, 4); }
+ | { rx_check_float_support (); }
+ disp '[' REG ']' opt_l ',' REG
+ { id24 (1, 0x80 + (sub_op << 2), 0); F ($4, 16, 4); F ($8, 20, 4); DSP ($2, 14, LSIZE); }
+ ;
+
+/* ====================================================================== */
+
+disp : { $$ = zero_expr (); }
+ | EXPR { $$ = $1; }
+ ;
+
+flag : { need_flag = 1; } FLAG { need_flag = 0; $$ = $2; }
+ ;
+
+/* DOT_UB is not listed here, it's handled with a separate pattern. */
+/* Use sizemap[$n] to get LSIZE etc. */
+memex : DOT_B { $$ = 0; }
+ | DOT_W { $$ = 1; }
+ | { $$ = 2; }
+ | DOT_L { $$ = 2; }
+ | DOT_UW { $$ = 3; }
+ ;
+
+bwl : { $$ = LSIZE; }
+ | DOT_B { $$ = BSIZE; }
+ | DOT_W { $$ = WSIZE; }
+ | DOT_L { $$ = LSIZE; }
+ ;
+
+bw : { $$ = 1; }
+ | DOT_B { $$ = 0; }
+ | DOT_W { $$ = 1; }
+ ;
+
+opt_l : {}
+ | DOT_L {}
+ ;
+
+opt_b : {}
+ | DOT_B {}
+ ;
+
+%%
+/* ====================================================================== */
+
+static struct
+{
+ const char * string;
+ int token;
+ int val;
+}
+token_table[] =
+{
+ { "r0", REG, 0 },
+ { "r1", REG, 1 },
+ { "r2", REG, 2 },
+ { "r3", REG, 3 },
+ { "r4", REG, 4 },
+ { "r5", REG, 5 },
+ { "r6", REG, 6 },
+ { "r7", REG, 7 },
+ { "r8", REG, 8 },
+ { "r9", REG, 9 },
+ { "r10", REG, 10 },
+ { "r11", REG, 11 },
+ { "r12", REG, 12 },
+ { "r13", REG, 13 },
+ { "r14", REG, 14 },
+ { "r15", REG, 15 },
+
+ { "psw", CREG, 0 },
+ { "pc", CREG, 1 },
+ { "usp", CREG, 2 },
+ { "fpsw", CREG, 3 },
+ /* reserved */
+ /* reserved */
+ /* reserved */
+ { "wr", CREG, 7 },
+
+ { "bpsw", CREG, 8 },
+ { "bpc", CREG, 9 },
+ { "isp", CREG, 10 },
+ { "fintv", CREG, 11 },
+ { "intb", CREG, 12 },
+
+ { "pbp", CREG, 16 },
+ { "pben", CREG, 17 },
+
+ { "bbpsw", CREG, 24 },
+ { "bbpc", CREG, 25 },
+
+ { ".s", DOT_S, 0 },
+ { ".b", DOT_B, 0 },
+ { ".w", DOT_W, 0 },
+ { ".l", DOT_L, 0 },
+ { ".a", DOT_A , 0},
+ { ".ub", DOT_UB, 0 },
+ { ".uw", DOT_UW , 0},
+
+ { "c", FLAG, 0 },
+ { "z", FLAG, 1 },
+ { "s", FLAG, 2 },
+ { "o", FLAG, 3 },
+ { "i", FLAG, 8 },
+ { "u", FLAG, 9 },
+
+#define OPC(x) { #x, x, IS_OPCODE }
+ OPC(ABS),
+ OPC(ADC),
+ OPC(ADD),
+ { "and", AND_, IS_OPCODE },
+ OPC(BCLR),
+ OPC(BCND),
+ OPC(BMCND),
+ OPC(BNOT),
+ OPC(BRA),
+ OPC(BRK),
+ OPC(BSET),
+ OPC(BSR),
+ OPC(BTST),
+ OPC(CLRPSW),
+ OPC(CMP),
+ OPC(DBT),
+ OPC(DIV),
+ OPC(DIVU),
+ OPC(EDIV),
+ OPC(EDIVU),
+ OPC(EMUL),
+ OPC(EMULU),
+ OPC(FADD),
+ OPC(FCMP),
+ OPC(FDIV),
+ OPC(FMUL),
+ OPC(FREIT),
+ OPC(FSUB),
+ OPC(FTOI),
+ OPC(INT),
+ OPC(ITOF),
+ OPC(JMP),
+ OPC(JSR),
+ OPC(MVFACHI),
+ OPC(MVFACMI),
+ OPC(MVFACLO),
+ OPC(MVFC),
+ OPC(MVTACHI),
+ OPC(MVTACLO),
+ OPC(MVTC),
+ OPC(MVTIPL),
+ OPC(MACHI),
+ OPC(MACLO),
+ OPC(MAX),
+ OPC(MIN),
+ OPC(MOV),
+ OPC(MOVU),
+ OPC(MUL),
+ OPC(MULHI),
+ OPC(MULLO),
+ OPC(MULU),
+ OPC(NEG),
+ OPC(NOP),
+ OPC(NOT),
+ OPC(OR),
+ OPC(POP),
+ OPC(POPC),
+ OPC(POPM),
+ OPC(PUSH),
+ OPC(PUSHA),
+ OPC(PUSHC),
+ OPC(PUSHM),
+ OPC(RACW),
+ OPC(REIT),
+ OPC(REVL),
+ OPC(REVW),
+ OPC(RMPA),
+ OPC(ROLC),
+ OPC(RORC),
+ OPC(ROTL),
+ OPC(ROTR),
+ OPC(ROUND),
+ OPC(RTE),
+ OPC(RTFI),
+ OPC(RTS),
+ OPC(RTSD),
+ OPC(SAT),
+ OPC(SATR),
+ OPC(SBB),
+ OPC(SCCND),
+ OPC(SCMPU),
+ OPC(SETPSW),
+ OPC(SHAR),
+ OPC(SHLL),
+ OPC(SHLR),
+ OPC(SMOVB),
+ OPC(SMOVF),
+ OPC(SMOVU),
+ OPC(SSTR),
+ OPC(STNZ),
+ OPC(STOP),
+ OPC(STZ),
+ OPC(SUB),
+ OPC(SUNTIL),
+ OPC(SWHILE),
+ OPC(TST),
+ OPC(WAIT),
+ OPC(XCHG),
+ OPC(XOR),
+};
+
+#define NUM_TOKENS (sizeof (token_table) / sizeof (token_table[0]))
+
+static struct
+{
+ char * string;
+ int token;
+}
+condition_opcode_table[] =
+{
+ { "b", BCND },
+ { "bm", BMCND },
+ { "sc", SCCND },
+};
+
+#define NUM_CONDITION_OPCODES (sizeof (condition_opcode_table) / sizeof (condition_opcode_table[0]))
+
+static struct
+{
+ char * string;
+ int val;
+}
+condition_table[] =
+{
+ { "z", 0 },
+ { "eq", 0 },
+ { "geu", 2 },
+ { "c", 2 },
+ { "gtu", 4 },
+ { "pz", 6 },
+ { "ge", 8 },
+ { "gt", 10 },
+ { "o", 12},
+ /* always = 14 */
+ { "nz", 1 },
+ { "ne", 1 },
+ { "ltu", 3 },
+ { "nc", 3 },
+ { "leu", 5 },
+ { "n", 7 },
+ { "lt", 9 },
+ { "le", 11 },
+ { "no", 13 }
+ /* never = 15 */
+};
+
+#define NUM_CONDITIONS (sizeof (condition_table) / sizeof (condition_table[0]))
+
+void
+rx_lex_init (char * beginning, char * ending)
+{
+ rx_init_start = beginning;
+ rx_lex_start = beginning;
+ rx_lex_end = ending;
+ rx_in_brackets = 0;
+ rx_last_token = 0;
+
+ setbuf (stdout, 0);
+}
+
+static int
+check_condition (char * base)
+{
+ char * cp;
+ unsigned int i;
+
+ if ((unsigned) (rx_lex_end - rx_lex_start) < strlen (base) + 1)
+ return 0;
+ if (memcmp (rx_lex_start, base, strlen (base)))
+ return 0;
+ cp = rx_lex_start + strlen (base);
+ for (i = 0; i < NUM_CONDITIONS; i ++)
+ {
+ if (strcasecmp (cp, condition_table[i].string) == 0)
+ {
+ rx_lval.regno = condition_table[i].val;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+rx_lex (void)
+{
+ unsigned int ci;
+ char * save_input_pointer;
+
+ while (ISSPACE (*rx_lex_start)
+ && rx_lex_start != rx_lex_end)
+ rx_lex_start ++;
+
+ rx_last_exp_start = rx_lex_start;
+
+ if (rx_lex_start == rx_lex_end)
+ return 0;
+
+ if (ISALPHA (*rx_lex_start)
+ || (rx_pid_register != -1 && memcmp (rx_lex_start, "%pidreg", 7) == 0)
+ || (rx_gp_register != -1 && memcmp (rx_lex_start, "%gpreg", 6) == 0)
+ || (*rx_lex_start == '.' && ISALPHA (rx_lex_start[1])))
+ {
+ unsigned int i;
+ char * e;
+ char save;
+
+ for (e = rx_lex_start + 1;
+ e < rx_lex_end && ISALNUM (*e);
+ e ++)
+ ;
+ save = *e;
+ *e = 0;
+
+ if (strcmp (rx_lex_start, "%pidreg") == 0)
+ {
+ {
+ rx_lval.regno = rx_pid_register;
+ *e = save;
+ rx_lex_start = e;
+ rx_last_token = REG;
+ return REG;
+ }
+ }
+
+ if (strcmp (rx_lex_start, "%gpreg") == 0)
+ {
+ {
+ rx_lval.regno = rx_gp_register;
+ *e = save;
+ rx_lex_start = e;
+ rx_last_token = REG;
+ return REG;
+ }
+ }
+
+ if (rx_last_token == 0)
+ for (ci = 0; ci < NUM_CONDITION_OPCODES; ci ++)
+ if (check_condition (condition_opcode_table[ci].string))
+ {
+ *e = save;
+ rx_lex_start = e;
+ rx_last_token = condition_opcode_table[ci].token;
+ return condition_opcode_table[ci].token;
+ }
+
+ for (i = 0; i < NUM_TOKENS; i++)
+ if (strcasecmp (rx_lex_start, token_table[i].string) == 0
+ && !(token_table[i].val == IS_OPCODE && rx_last_token != 0)
+ && !(token_table[i].token == FLAG && !need_flag))
+ {
+ rx_lval.regno = token_table[i].val;
+ *e = save;
+ rx_lex_start = e;
+ rx_last_token = token_table[i].token;
+ return token_table[i].token;
+ }
+ *e = save;
+ }
+
+ if (rx_last_token == 0)
+ {
+ rx_last_token = UNKNOWN_OPCODE;
+ return UNKNOWN_OPCODE;
+ }
+
+ if (rx_last_token == UNKNOWN_OPCODE)
+ return 0;
+
+ if (*rx_lex_start == '[')
+ rx_in_brackets = 1;
+ if (*rx_lex_start == ']')
+ rx_in_brackets = 0;
+
+ if (rx_in_brackets
+ || rx_last_token == REG
+ || strchr ("[],#", *rx_lex_start))
+ {
+ rx_last_token = *rx_lex_start;
+ return *rx_lex_start ++;
+ }
+
+ save_input_pointer = input_line_pointer;
+ input_line_pointer = rx_lex_start;
+ rx_lval.exp.X_md = 0;
+ expression (&rx_lval.exp);
+
+ /* We parse but ignore any :<size> modifier on expressions. */
+ if (*input_line_pointer == ':')
+ {
+ char *cp;
+
+ for (cp = input_line_pointer + 1; *cp && cp < rx_lex_end; cp++)
+ if (!ISDIGIT (*cp))
+ break;
+ if (cp > input_line_pointer+1)
+ input_line_pointer = cp;
+ }
+
+ rx_lex_start = input_line_pointer;
+ input_line_pointer = save_input_pointer;
+ rx_last_token = EXPR;
+ return EXPR;
+}
+
+int
+rx_error (const char * str)
+{
+ int len;
+
+ len = rx_last_exp_start - rx_init_start;
+
+ as_bad ("%s", rx_init_start);
+ as_bad ("%*s^ %s", len, "", str);
+ return 0;
+}
+
+static int
+rx_intop (expressionS exp, int nbits, int opbits)
+{
+ long v;
+ long mask, msb;
+
+ if (exp.X_op == O_big && nbits == 32)
+ return 1;
+ if (exp.X_op != O_constant)
+ return 0;
+ v = exp.X_add_number;
+
+ msb = 1UL << (opbits - 1);
+ mask = (1UL << opbits) - 1;
+
+ if ((v & msb) && ! (v & ~mask))
+ v -= 1UL << opbits;
+
+ switch (nbits)
+ {
+ case 4:
+ return -0x8 <= v && v <= 0x7;
+ case 5:
+ return -0x10 <= v && v <= 0x17;
+ case 8:
+ return -0x80 <= v && v <= 0x7f;
+ case 16:
+ return -0x8000 <= v && v <= 0x7fff;
+ case 24:
+ return -0x800000 <= v && v <= 0x7fffff;
+ case 32:
+ return 1;
+ default:
+ printf ("rx_intop passed %d\n", nbits);
+ abort ();
+ }
+ return 1;
+}
+
+static int
+rx_uintop (expressionS exp, int nbits)
+{
+ unsigned long v;
+
+ if (exp.X_op != O_constant)
+ return 0;
+ v = exp.X_add_number;
+
+ switch (nbits)
+ {
+ case 4:
+ return v <= 0xf;
+ case 8:
+ return v <= 0xff;
+ case 16:
+ return v <= 0xffff;
+ case 24:
+ return v <= 0xffffff;
+ default:
+ printf ("rx_uintop passed %d\n", nbits);
+ abort ();
+ }
+ return 1;
+}
+
+static int
+rx_disp3op (expressionS exp)
+{
+ unsigned long v;
+
+ if (exp.X_op != O_constant)
+ return 0;
+ v = exp.X_add_number;
+ if (v < 3 || v > 10)
+ return 0;
+ return 1;
+}
+
+static int
+rx_disp5op (expressionS * exp, int msize)
+{
+ long v;
+
+ if (exp->X_op != O_constant)
+ return 0;
+ v = exp->X_add_number;
+
+ switch (msize)
+ {
+ case BSIZE:
+ if (0 < v && v <= 31)
+ return 1;
+ break;
+ case WSIZE:
+ if (v & 1)
+ return 0;
+ if (0 < v && v <= 63)
+ {
+ exp->X_add_number >>= 1;
+ return 1;
+ }
+ break;
+ case LSIZE:
+ if (v & 3)
+ return 0;
+ if (0 < v && v <= 127)
+ {
+ exp->X_add_number >>= 2;
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+/* Just like the above, but allows a zero displacement. */
+
+static int
+rx_disp5op0 (expressionS * exp, int msize)
+{
+ if (exp->X_op != O_constant)
+ return 0;
+ if (exp->X_add_number == 0)
+ return 1;
+ return rx_disp5op (exp, msize);
+}
+
+static int
+exp_val (expressionS exp)
+{
+ if (exp.X_op != O_constant)
+ {
+ rx_error (_("constant expected"));
+ return 0;
+ }
+ return exp.X_add_number;
+}
+
+static expressionS
+zero_expr (void)
+{
+ /* Static, so program load sets it to all zeros, which is what we want. */
+ static expressionS zero;
+ zero.X_op = O_constant;
+ return zero;
+}
+
+static int
+immediate (expressionS exp, int type, int pos, int bits)
+{
+ /* We will emit constants ourself here, so negate them. */
+ if (type == RXREL_NEGATIVE && exp.X_op == O_constant)
+ exp.X_add_number = - exp.X_add_number;
+ if (type == RXREL_NEGATIVE_BORROW)
+ {
+ if (exp.X_op == O_constant)
+ exp.X_add_number = - exp.X_add_number - 1;
+ else
+ rx_error (_("sbb cannot use symbolic immediates"));
+ }
+
+ if (rx_intop (exp, 8, bits))
+ {
+ rx_op (exp, 1, type);
+ return 1;
+ }
+ else if (rx_intop (exp, 16, bits))
+ {
+ rx_op (exp, 2, type);
+ return 2;
+ }
+ else if (rx_uintop (exp, 16) && bits == 16)
+ {
+ rx_op (exp, 2, type);
+ return 2;
+ }
+ else if (rx_intop (exp, 24, bits))
+ {
+ rx_op (exp, 3, type);
+ return 3;
+ }
+ else if (rx_intop (exp, 32, bits))
+ {
+ rx_op (exp, 4, type);
+ return 0;
+ }
+ else if (type == RXREL_SIGNED)
+ {
+ /* This is a symbolic immediate, we will relax it later. */
+ rx_relax (RX_RELAX_IMM, pos);
+ rx_op (exp, linkrelax ? 4 : 1, type);
+ return 1;
+ }
+ else
+ {
+ /* Let the linker deal with it. */
+ rx_op (exp, 4, type);
+ return 0;
+ }
+}
+
+static int
+displacement (expressionS exp, int msize)
+{
+ int val;
+ int vshift = 0;
+
+ if (exp.X_op == O_symbol
+ && exp.X_md)
+ {
+ switch (exp.X_md)
+ {
+ case BFD_RELOC_GPREL16:
+ switch (msize)
+ {
+ case BSIZE:
+ exp.X_md = BFD_RELOC_RX_GPRELB;
+ break;
+ case WSIZE:
+ exp.X_md = BFD_RELOC_RX_GPRELW;
+ break;
+ case LSIZE:
+ exp.X_md = BFD_RELOC_RX_GPRELL;
+ break;
+ }
+ O2 (exp);
+ return 2;
+ }
+ }
+
+ if (exp.X_op == O_subtract)
+ {
+ exp.X_md = BFD_RELOC_RX_DIFF;
+ O2 (exp);
+ return 2;
+ }
+
+ if (exp.X_op != O_constant)
+ {
+ rx_error (_("displacements must be constants"));
+ return -1;
+ }
+ val = exp.X_add_number;
+
+ if (val == 0)
+ return 0;
+
+ switch (msize)
+ {
+ case BSIZE:
+ break;
+ case WSIZE:
+ if (val & 1)
+ rx_error (_("word displacement not word-aligned"));
+ vshift = 1;
+ break;
+ case LSIZE:
+ if (val & 3)
+ rx_error (_("long displacement not long-aligned"));
+ vshift = 2;
+ break;
+ default:
+ as_bad (_("displacement with unknown size (internal bug?)\n"));
+ break;
+ }
+
+ val >>= vshift;
+ exp.X_add_number = val;
+
+ if (0 <= val && val <= 255 )
+ {
+ O1 (exp);
+ return 1;
+ }
+
+ if (0 <= val && val <= 65535)
+ {
+ O2 (exp);
+ return 2;
+ }
+ if (val < 0)
+ rx_error (_("negative displacements not allowed"));
+ else
+ rx_error (_("displacement too large"));
+ return -1;
+}
+
+static void
+rtsd_immediate (expressionS exp)
+{
+ int val;
+
+ if (exp.X_op != O_constant)
+ {
+ rx_error (_("rtsd size must be constant"));
+ return;
+ }
+ val = exp.X_add_number;
+ if (val & 3)
+ rx_error (_("rtsd size must be multiple of 4"));
+
+ if (val < 0 || val > 1020)
+ rx_error (_("rtsd size must be 0..1020"));
+
+ val >>= 2;
+ exp.X_add_number = val;
+ O1 (exp);
+}
+
+static void
+rx_range (expressionS exp, int minv, int maxv)
+{
+ int val;
+
+ if (exp.X_op != O_constant)
+ return;
+
+ val = exp.X_add_number;
+ if (val < minv || val > maxv)
+ as_warn (_("Value %d out of range %d..%d"), val, minv, maxv);
+}
+
+static void
+rx_check_float_support (void)
+{
+ if (rx_cpu == RX100 || rx_cpu == RX200)
+ rx_error (_("target CPU type does not support floating point instructions"));
+}
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
new file mode 100644
index 0000000..0e58764
--- /dev/null
+++ b/gas/config/tc-aarch64.c
@@ -0,0 +1,7631 @@
+/* tc-aarch64.c -- Assemble for the AArch64 ISA
+
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the license, 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 this program; see the file COPYING3. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+#include "as.h"
+#include <limits.h>
+#include <stdarg.h>
+#include "bfd_stdint.h"
+#define NO_RELOC 0
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "obstack.h"
+
+#ifdef OBJ_ELF
+#include "elf/aarch64.h"
+#include "dw2gencfi.h"
+#endif
+
+#include "dwarf2dbg.h"
+
+/* Types of processor to assemble for. */
+#ifndef CPU_DEFAULT
+#define CPU_DEFAULT AARCH64_ARCH_V8
+#endif
+
+#define streq(a, b) (strcmp (a, b) == 0)
+
+#define END_OF_INSN '\0'
+
+static aarch64_feature_set cpu_variant;
+
+/* Variables that we set while parsing command-line options. Once all
+ options have been read we re-process these values to set the real
+ assembly flags. */
+static const aarch64_feature_set *mcpu_cpu_opt = NULL;
+static const aarch64_feature_set *march_cpu_opt = NULL;
+
+/* Constants for known architecture features. */
+static const aarch64_feature_set cpu_default = CPU_DEFAULT;
+
+static const aarch64_feature_set aarch64_arch_any = AARCH64_ANY;
+static const aarch64_feature_set aarch64_arch_none = AARCH64_ARCH_NONE;
+
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+static symbolS *GOT_symbol;
+
+/* Which ABI to use. */
+enum aarch64_abi_type
+{
+ AARCH64_ABI_LP64 = 0,
+ AARCH64_ABI_ILP32 = 1
+};
+
+/* AArch64 ABI for the output file. */
+static enum aarch64_abi_type aarch64_abi = AARCH64_ABI_LP64;
+
+/* When non-zero, program to a 32-bit model, in which the C data types
+ int, long and all pointer types are 32-bit objects (ILP32); or to a
+ 64-bit model, in which the C int type is 32-bits but the C long type
+ and all pointer types are 64-bit objects (LP64). */
+#define ilp32_p (aarch64_abi == AARCH64_ABI_ILP32)
+#endif
+
+enum neon_el_type
+{
+ NT_invtype = -1,
+ NT_b,
+ NT_h,
+ NT_s,
+ NT_d,
+ NT_q
+};
+
+/* Bits for DEFINED field in neon_type_el. */
+#define NTA_HASTYPE 1
+#define NTA_HASINDEX 2
+
+struct neon_type_el
+{
+ enum neon_el_type type;
+ unsigned char defined;
+ unsigned width;
+ int64_t index;
+};
+
+#define FIXUP_F_HAS_EXPLICIT_SHIFT 0x00000001
+
+struct reloc
+{
+ bfd_reloc_code_real_type type;
+ expressionS exp;
+ int pc_rel;
+ enum aarch64_opnd opnd;
+ uint32_t flags;
+ unsigned need_libopcodes_p : 1;
+};
+
+struct aarch64_instruction
+{
+ /* libopcodes structure for instruction intermediate representation. */
+ aarch64_inst base;
+ /* Record assembly errors found during the parsing. */
+ struct
+ {
+ enum aarch64_operand_error_kind kind;
+ const char *error;
+ } parsing_error;
+ /* The condition that appears in the assembly line. */
+ int cond;
+ /* Relocation information (including the GAS internal fixup). */
+ struct reloc reloc;
+ /* Need to generate an immediate in the literal pool. */
+ unsigned gen_lit_pool : 1;
+};
+
+typedef struct aarch64_instruction aarch64_instruction;
+
+static aarch64_instruction inst;
+
+static bfd_boolean parse_operands (char *, const aarch64_opcode *);
+static bfd_boolean programmer_friendly_fixup (aarch64_instruction *);
+
+/* Diagnostics inline function utilites.
+
+ These are lightweight utlities which should only be called by parse_operands
+ and other parsers. GAS processes each assembly line by parsing it against
+ instruction template(s), in the case of multiple templates (for the same
+ mnemonic name), those templates are tried one by one until one succeeds or
+ all fail. An assembly line may fail a few templates before being
+ successfully parsed; an error saved here in most cases is not a user error
+ but an error indicating the current template is not the right template.
+ Therefore it is very important that errors can be saved at a low cost during
+ the parsing; we don't want to slow down the whole parsing by recording
+ non-user errors in detail.
+
+ Remember that the objective is to help GAS pick up the most approapriate
+ error message in the case of multiple templates, e.g. FMOV which has 8
+ templates. */
+
+static inline void
+clear_error (void)
+{
+ inst.parsing_error.kind = AARCH64_OPDE_NIL;
+ inst.parsing_error.error = NULL;
+}
+
+static inline bfd_boolean
+error_p (void)
+{
+ return inst.parsing_error.kind != AARCH64_OPDE_NIL;
+}
+
+static inline const char *
+get_error_message (void)
+{
+ return inst.parsing_error.error;
+}
+
+static inline void
+set_error_message (const char *error)
+{
+ inst.parsing_error.error = error;
+}
+
+static inline enum aarch64_operand_error_kind
+get_error_kind (void)
+{
+ return inst.parsing_error.kind;
+}
+
+static inline void
+set_error_kind (enum aarch64_operand_error_kind kind)
+{
+ inst.parsing_error.kind = kind;
+}
+
+static inline void
+set_error (enum aarch64_operand_error_kind kind, const char *error)
+{
+ inst.parsing_error.kind = kind;
+ inst.parsing_error.error = error;
+}
+
+static inline void
+set_recoverable_error (const char *error)
+{
+ set_error (AARCH64_OPDE_RECOVERABLE, error);
+}
+
+/* Use the DESC field of the corresponding aarch64_operand entry to compose
+ the error message. */
+static inline void
+set_default_error (void)
+{
+ set_error (AARCH64_OPDE_SYNTAX_ERROR, NULL);
+}
+
+static inline void
+set_syntax_error (const char *error)
+{
+ set_error (AARCH64_OPDE_SYNTAX_ERROR, error);
+}
+
+static inline void
+set_first_syntax_error (const char *error)
+{
+ if (! error_p ())
+ set_error (AARCH64_OPDE_SYNTAX_ERROR, error);
+}
+
+static inline void
+set_fatal_syntax_error (const char *error)
+{
+ set_error (AARCH64_OPDE_FATAL_SYNTAX_ERROR, error);
+}
+
+/* Number of littlenums required to hold an extended precision number. */
+#define MAX_LITTLENUMS 6
+
+/* Return value for certain parsers when the parsing fails; those parsers
+ return the information of the parsed result, e.g. register number, on
+ success. */
+#define PARSE_FAIL -1
+
+/* This is an invalid condition code that means no conditional field is
+ present. */
+#define COND_ALWAYS 0x10
+
+typedef struct
+{
+ const char *template;
+ unsigned long value;
+} asm_barrier_opt;
+
+typedef struct
+{
+ const char *template;
+ uint32_t value;
+} asm_nzcv;
+
+struct reloc_entry
+{
+ char *name;
+ bfd_reloc_code_real_type reloc;
+};
+
+/* Structure for a hash table entry for a register. */
+typedef struct
+{
+ const char *name;
+ unsigned char number;
+ unsigned char type;
+ unsigned char builtin;
+} reg_entry;
+
+/* Macros to define the register types and masks for the purpose
+ of parsing. */
+
+#undef AARCH64_REG_TYPES
+#define AARCH64_REG_TYPES \
+ BASIC_REG_TYPE(R_32) /* w[0-30] */ \
+ BASIC_REG_TYPE(R_64) /* x[0-30] */ \
+ BASIC_REG_TYPE(SP_32) /* wsp */ \
+ BASIC_REG_TYPE(SP_64) /* sp */ \
+ BASIC_REG_TYPE(Z_32) /* wzr */ \
+ BASIC_REG_TYPE(Z_64) /* xzr */ \
+ BASIC_REG_TYPE(FP_B) /* b[0-31] *//* NOTE: keep FP_[BHSDQ] consecutive! */\
+ BASIC_REG_TYPE(FP_H) /* h[0-31] */ \
+ BASIC_REG_TYPE(FP_S) /* s[0-31] */ \
+ BASIC_REG_TYPE(FP_D) /* d[0-31] */ \
+ BASIC_REG_TYPE(FP_Q) /* q[0-31] */ \
+ BASIC_REG_TYPE(CN) /* c[0-7] */ \
+ BASIC_REG_TYPE(VN) /* v[0-31] */ \
+ /* Typecheck: any 64-bit int reg (inc SP exc XZR) */ \
+ MULTI_REG_TYPE(R64_SP, REG_TYPE(R_64) | REG_TYPE(SP_64)) \
+ /* Typecheck: any int (inc {W}SP inc [WX]ZR) */ \
+ MULTI_REG_TYPE(R_Z_SP, REG_TYPE(R_32) | REG_TYPE(R_64) \
+ | REG_TYPE(SP_32) | REG_TYPE(SP_64) \
+ | REG_TYPE(Z_32) | REG_TYPE(Z_64)) \
+ /* Typecheck: any [BHSDQ]P FP. */ \
+ MULTI_REG_TYPE(BHSDQ, REG_TYPE(FP_B) | REG_TYPE(FP_H) \
+ | REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q)) \
+ /* Typecheck: any int or [BHSDQ]P FP or V reg (exc SP inc [WX]ZR) */ \
+ MULTI_REG_TYPE(R_Z_BHSDQ_V, REG_TYPE(R_32) | REG_TYPE(R_64) \
+ | REG_TYPE(Z_32) | REG_TYPE(Z_64) | REG_TYPE(VN) \
+ | REG_TYPE(FP_B) | REG_TYPE(FP_H) \
+ | REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q)) \
+ /* Any integer register; used for error messages only. */ \
+ MULTI_REG_TYPE(R_N, REG_TYPE(R_32) | REG_TYPE(R_64) \
+ | REG_TYPE(SP_32) | REG_TYPE(SP_64) \
+ | REG_TYPE(Z_32) | REG_TYPE(Z_64)) \
+ /* Pseudo type to mark the end of the enumerator sequence. */ \
+ BASIC_REG_TYPE(MAX)
+
+#undef BASIC_REG_TYPE
+#define BASIC_REG_TYPE(T) REG_TYPE_##T,
+#undef MULTI_REG_TYPE
+#define MULTI_REG_TYPE(T,V) BASIC_REG_TYPE(T)
+
+/* Register type enumerators. */
+typedef enum
+{
+ /* A list of REG_TYPE_*. */
+ AARCH64_REG_TYPES
+} aarch64_reg_type;
+
+#undef BASIC_REG_TYPE
+#define BASIC_REG_TYPE(T) 1 << REG_TYPE_##T,
+#undef REG_TYPE
+#define REG_TYPE(T) (1 << REG_TYPE_##T)
+#undef MULTI_REG_TYPE
+#define MULTI_REG_TYPE(T,V) V,
+
+/* Values indexed by aarch64_reg_type to assist the type checking. */
+static const unsigned reg_type_masks[] =
+{
+ AARCH64_REG_TYPES
+};
+
+#undef BASIC_REG_TYPE
+#undef REG_TYPE
+#undef MULTI_REG_TYPE
+#undef AARCH64_REG_TYPES
+
+/* Diagnostics used when we don't get a register of the expected type.
+ Note: this has to synchronized with aarch64_reg_type definitions
+ above. */
+static const char *
+get_reg_expected_msg (aarch64_reg_type reg_type)
+{
+ const char *msg;
+
+ switch (reg_type)
+ {
+ case REG_TYPE_R_32:
+ msg = N_("integer 32-bit register expected");
+ break;
+ case REG_TYPE_R_64:
+ msg = N_("integer 64-bit register expected");
+ break;
+ case REG_TYPE_R_N:
+ msg = N_("integer register expected");
+ break;
+ case REG_TYPE_R_Z_SP:
+ msg = N_("integer, zero or SP register expected");
+ break;
+ case REG_TYPE_FP_B:
+ msg = N_("8-bit SIMD scalar register expected");
+ break;
+ case REG_TYPE_FP_H:
+ msg = N_("16-bit SIMD scalar or floating-point half precision "
+ "register expected");
+ break;
+ case REG_TYPE_FP_S:
+ msg = N_("32-bit SIMD scalar or floating-point single precision "
+ "register expected");
+ break;
+ case REG_TYPE_FP_D:
+ msg = N_("64-bit SIMD scalar or floating-point double precision "
+ "register expected");
+ break;
+ case REG_TYPE_FP_Q:
+ msg = N_("128-bit SIMD scalar or floating-point quad precision "
+ "register expected");
+ break;
+ case REG_TYPE_CN:
+ msg = N_("C0 - C15 expected");
+ break;
+ case REG_TYPE_R_Z_BHSDQ_V:
+ msg = N_("register expected");
+ break;
+ case REG_TYPE_BHSDQ: /* any [BHSDQ]P FP */
+ msg = N_("SIMD scalar or floating-point register expected");
+ break;
+ case REG_TYPE_VN: /* any V reg */
+ msg = N_("vector register expected");
+ break;
+ default:
+ as_fatal (_("invalid register type %d"), reg_type);
+ }
+ return msg;
+}
+
+/* Some well known registers that we refer to directly elsewhere. */
+#define REG_SP 31
+
+/* Instructions take 4 bytes in the object file. */
+#define INSN_SIZE 4
+
+/* Define some common error messages. */
+#define BAD_SP _("SP not allowed here")
+
+static struct hash_control *aarch64_ops_hsh;
+static struct hash_control *aarch64_cond_hsh;
+static struct hash_control *aarch64_shift_hsh;
+static struct hash_control *aarch64_sys_regs_hsh;
+static struct hash_control *aarch64_pstatefield_hsh;
+static struct hash_control *aarch64_sys_regs_ic_hsh;
+static struct hash_control *aarch64_sys_regs_dc_hsh;
+static struct hash_control *aarch64_sys_regs_at_hsh;
+static struct hash_control *aarch64_sys_regs_tlbi_hsh;
+static struct hash_control *aarch64_reg_hsh;
+static struct hash_control *aarch64_barrier_opt_hsh;
+static struct hash_control *aarch64_nzcv_hsh;
+static struct hash_control *aarch64_pldop_hsh;
+
+/* Stuff needed to resolve the label ambiguity
+ As:
+ ...
+ label: <insn>
+ may differ from:
+ ...
+ label:
+ <insn> */
+
+static symbolS *last_label_seen;
+
+/* Literal pool structure. Held on a per-section
+ and per-sub-section basis. */
+
+#define MAX_LITERAL_POOL_SIZE 1024
+typedef struct literal_expression
+{
+ expressionS exp;
+ /* If exp.op == O_big then this bignum holds a copy of the global bignum value. */
+ LITTLENUM_TYPE * bignum;
+} literal_expression;
+
+typedef struct literal_pool
+{
+ literal_expression literals[MAX_LITERAL_POOL_SIZE];
+ unsigned int next_free_entry;
+ unsigned int id;
+ symbolS *symbol;
+ segT section;
+ subsegT sub_section;
+ int size;
+ struct literal_pool *next;
+} literal_pool;
+
+/* Pointer to a linked list of literal pools. */
+static literal_pool *list_of_pools = NULL;
+
+/* Pure syntax. */
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant
+ from exp in floating point numbers. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant. */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+
+const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+/* Prefix character that indicates the start of an immediate value. */
+#define is_immediate_prefix(C) ((C) == '#')
+
+/* Separator character handling. */
+
+#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
+
+static inline bfd_boolean
+skip_past_char (char **str, char c)
+{
+ if (**str == c)
+ {
+ (*str)++;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+#define skip_past_comma(str) skip_past_char (str, ',')
+
+/* Arithmetic expressions (possibly involving symbols). */
+
+static bfd_boolean in_my_get_expression_p = FALSE;
+
+/* Third argument to my_get_expression. */
+#define GE_NO_PREFIX 0
+#define GE_OPT_PREFIX 1
+
+/* Return TRUE if the string pointed by *STR is successfully parsed
+ as an valid expression; *EP will be filled with the information of
+ such an expression. Otherwise return FALSE. */
+
+static bfd_boolean
+my_get_expression (expressionS * ep, char **str, int prefix_mode,
+ int reject_absent)
+{
+ char *save_in;
+ segT seg;
+ int prefix_present_p = 0;
+
+ switch (prefix_mode)
+ {
+ case GE_NO_PREFIX:
+ break;
+ case GE_OPT_PREFIX:
+ if (is_immediate_prefix (**str))
+ {
+ (*str)++;
+ prefix_present_p = 1;
+ }
+ break;
+ default:
+ abort ();
+ }
+
+ memset (ep, 0, sizeof (expressionS));
+
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ in_my_get_expression_p = TRUE;
+ seg = expression (ep);
+ in_my_get_expression_p = FALSE;
+
+ if (ep->X_op == O_illegal || (reject_absent && ep->X_op == O_absent))
+ {
+ /* We found a bad expression in md_operand(). */
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ if (prefix_present_p && ! error_p ())
+ set_fatal_syntax_error (_("bad expression"));
+ else
+ set_first_syntax_error (_("bad expression"));
+ return FALSE;
+ }
+
+#ifdef OBJ_AOUT
+ if (seg != absolute_section
+ && seg != text_section
+ && seg != data_section
+ && seg != bss_section && seg != undefined_section)
+ {
+ set_syntax_error (_("bad segment"));
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return FALSE;
+ }
+#else
+ (void) seg;
+#endif
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return TRUE;
+}
+
+/* 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. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* We handle all bad expressions here, so that we can report the faulty
+ instruction in the error message. */
+void
+md_operand (expressionS * exp)
+{
+ if (in_my_get_expression_p)
+ exp->X_op = O_illegal;
+}
+
+/* Immediate values. */
+
+/* Errors may be set multiple times during parsing or bit encoding
+ (particularly in the Neon bits), but usually the earliest error which is set
+ will be the most meaningful. Avoid overwriting it with later (cascading)
+ errors by calling this function. */
+
+static void
+first_error (const char *error)
+{
+ if (! error_p ())
+ set_syntax_error (error);
+}
+
+/* Similiar to first_error, but this function accepts formatted error
+ message. */
+static void
+first_error_fmt (const char *format, ...)
+{
+ va_list args;
+ enum
+ { size = 100 };
+ /* N.B. this single buffer will not cause error messages for different
+ instructions to pollute each other; this is because at the end of
+ processing of each assembly line, error message if any will be
+ collected by as_bad. */
+ static char buffer[size];
+
+ if (! error_p ())
+ {
+ int ret ATTRIBUTE_UNUSED;
+ va_start (args, format);
+ ret = vsnprintf (buffer, size, format, args);
+ know (ret <= size - 1 && ret >= 0);
+ va_end (args);
+ set_syntax_error (buffer);
+ }
+}
+
+/* Register parsing. */
+
+/* Generic register parser which is called by other specialized
+ register parsers.
+ CCP points to what should be the beginning of a register name.
+ If it is indeed a valid register name, advance CCP over it and
+ return the reg_entry structure; otherwise return NULL.
+ It does not issue diagnostics. */
+
+static reg_entry *
+parse_reg (char **ccp)
+{
+ char *start = *ccp;
+ char *p;
+ reg_entry *reg;
+
+#ifdef REGISTER_PREFIX
+ if (*start != REGISTER_PREFIX)
+ return NULL;
+ start++;
+#endif
+
+ p = start;
+ if (!ISALPHA (*p) || !is_name_beginner (*p))
+ return NULL;
+
+ do
+ p++;
+ while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
+
+ reg = (reg_entry *) hash_find_n (aarch64_reg_hsh, start, p - start);
+
+ if (!reg)
+ return NULL;
+
+ *ccp = p;
+ return reg;
+}
+
+/* Return TRUE if REG->TYPE is a valid type of TYPE; otherwise
+ return FALSE. */
+static bfd_boolean
+aarch64_check_reg_type (const reg_entry *reg, aarch64_reg_type type)
+{
+ if (reg->type == type)
+ return TRUE;
+
+ switch (type)
+ {
+ case REG_TYPE_R64_SP: /* 64-bit integer reg (inc SP exc XZR). */
+ case REG_TYPE_R_Z_SP: /* Integer reg (inc {X}SP inc [WX]ZR). */
+ case REG_TYPE_R_Z_BHSDQ_V: /* Any register apart from Cn. */
+ case REG_TYPE_BHSDQ: /* Any [BHSDQ]P FP or SIMD scalar register. */
+ case REG_TYPE_VN: /* Vector register. */
+ gas_assert (reg->type < REG_TYPE_MAX && type < REG_TYPE_MAX);
+ return ((reg_type_masks[reg->type] & reg_type_masks[type])
+ == reg_type_masks[reg->type]);
+ default:
+ as_fatal ("unhandled type %d", type);
+ abort ();
+ }
+}
+
+/* Parse a register and return PARSE_FAIL if the register is not of type R_Z_SP.
+ Return the register number otherwise. *ISREG32 is set to one if the
+ register is 32-bit wide; *ISREGZERO is set to one if the register is
+ of type Z_32 or Z_64.
+ Note that this function does not issue any diagnostics. */
+
+static int
+aarch64_reg_parse_32_64 (char **ccp, int reject_sp, int reject_rz,
+ int *isreg32, int *isregzero)
+{
+ char *str = *ccp;
+ const reg_entry *reg = parse_reg (&str);
+
+ if (reg == NULL)
+ return PARSE_FAIL;
+
+ if (! aarch64_check_reg_type (reg, REG_TYPE_R_Z_SP))
+ return PARSE_FAIL;
+
+ switch (reg->type)
+ {
+ case REG_TYPE_SP_32:
+ case REG_TYPE_SP_64:
+ if (reject_sp)
+ return PARSE_FAIL;
+ *isreg32 = reg->type == REG_TYPE_SP_32;
+ *isregzero = 0;
+ break;
+ case REG_TYPE_R_32:
+ case REG_TYPE_R_64:
+ *isreg32 = reg->type == REG_TYPE_R_32;
+ *isregzero = 0;
+ break;
+ case REG_TYPE_Z_32:
+ case REG_TYPE_Z_64:
+ if (reject_rz)
+ return PARSE_FAIL;
+ *isreg32 = reg->type == REG_TYPE_Z_32;
+ *isregzero = 1;
+ break;
+ default:
+ return PARSE_FAIL;
+ }
+
+ *ccp = str;
+
+ return reg->number;
+}
+
+/* Parse the qualifier of a SIMD vector register or a SIMD vector element.
+ Fill in *PARSED_TYPE and return TRUE if the parsing succeeds;
+ otherwise return FALSE.
+
+ Accept only one occurrence of:
+ 8b 16b 4h 8h 2s 4s 1d 2d
+ b h s d q */
+static bfd_boolean
+parse_neon_type_for_operand (struct neon_type_el *parsed_type, char **str)
+{
+ char *ptr = *str;
+ unsigned width;
+ unsigned element_size;
+ enum neon_el_type type;
+
+ /* skip '.' */
+ ptr++;
+
+ if (!ISDIGIT (*ptr))
+ {
+ width = 0;
+ goto elt_size;
+ }
+ width = strtoul (ptr, &ptr, 10);
+ if (width != 1 && width != 2 && width != 4 && width != 8 && width != 16)
+ {
+ first_error_fmt (_("bad size %d in vector width specifier"), width);
+ return FALSE;
+ }
+
+elt_size:
+ switch (TOLOWER (*ptr))
+ {
+ case 'b':
+ type = NT_b;
+ element_size = 8;
+ break;
+ case 'h':
+ type = NT_h;
+ element_size = 16;
+ break;
+ case 's':
+ type = NT_s;
+ element_size = 32;
+ break;
+ case 'd':
+ type = NT_d;
+ element_size = 64;
+ break;
+ case 'q':
+ if (width == 1)
+ {
+ type = NT_q;
+ element_size = 128;
+ break;
+ }
+ /* fall through. */
+ default:
+ if (*ptr != '\0')
+ first_error_fmt (_("unexpected character `%c' in element size"), *ptr);
+ else
+ first_error (_("missing element size"));
+ return FALSE;
+ }
+ if (width != 0 && width * element_size != 64 && width * element_size != 128)
+ {
+ first_error_fmt (_
+ ("invalid element size %d and vector size combination %c"),
+ width, *ptr);
+ return FALSE;
+ }
+ ptr++;
+
+ parsed_type->type = type;
+ parsed_type->width = width;
+
+ *str = ptr;
+
+ return TRUE;
+}
+
+/* Parse a single type, e.g. ".8b", leading period included.
+ Only applicable to Vn registers.
+
+ Return TRUE on success; otherwise return FALSE. */
+static bfd_boolean
+parse_neon_operand_type (struct neon_type_el *vectype, char **ccp)
+{
+ char *str = *ccp;
+
+ if (*str == '.')
+ {
+ if (! parse_neon_type_for_operand (vectype, &str))
+ {
+ first_error (_("vector type expected"));
+ return FALSE;
+ }
+ }
+ else
+ return FALSE;
+
+ *ccp = str;
+
+ return TRUE;
+}
+
+/* Parse a register of the type TYPE.
+
+ Return PARSE_FAIL if the string pointed by *CCP is not a valid register
+ name or the parsed register is not of TYPE.
+
+ Otherwise return the register number, and optionally fill in the actual
+ type of the register in *RTYPE when multiple alternatives were given, and
+ return the register shape and element index information in *TYPEINFO.
+
+ IN_REG_LIST should be set with TRUE if the caller is parsing a register
+ list. */
+
+static int
+parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype,
+ struct neon_type_el *typeinfo, bfd_boolean in_reg_list)
+{
+ char *str = *ccp;
+ const reg_entry *reg = parse_reg (&str);
+ struct neon_type_el atype;
+ struct neon_type_el parsetype;
+ bfd_boolean is_typed_vecreg = FALSE;
+
+ atype.defined = 0;
+ atype.type = NT_invtype;
+ atype.width = -1;
+ atype.index = 0;
+
+ if (reg == NULL)
+ {
+ if (typeinfo)
+ *typeinfo = atype;
+ set_default_error ();
+ return PARSE_FAIL;
+ }
+
+ if (! aarch64_check_reg_type (reg, type))
+ {
+ DEBUG_TRACE ("reg type check failed");
+ set_default_error ();
+ return PARSE_FAIL;
+ }
+ type = reg->type;
+
+ if (type == REG_TYPE_VN
+ && parse_neon_operand_type (&parsetype, &str))
+ {
+ /* Register if of the form Vn.[bhsdq]. */
+ is_typed_vecreg = TRUE;
+
+ if (parsetype.width == 0)
+ /* Expect index. In the new scheme we cannot have
+ Vn.[bhsdq] represent a scalar. Therefore any
+ Vn.[bhsdq] should have an index following it.
+ Except in reglists ofcourse. */
+ atype.defined |= NTA_HASINDEX;
+ else
+ atype.defined |= NTA_HASTYPE;
+
+ atype.type = parsetype.type;
+ atype.width = parsetype.width;
+ }
+
+ if (skip_past_char (&str, '['))
+ {
+ expressionS exp;
+
+ /* Reject Sn[index] syntax. */
+ if (!is_typed_vecreg)
+ {
+ first_error (_("this type of register can't be indexed"));
+ return PARSE_FAIL;
+ }
+
+ if (in_reg_list == TRUE)
+ {
+ first_error (_("index not allowed inside register list"));
+ return PARSE_FAIL;
+ }
+
+ atype.defined |= NTA_HASINDEX;
+
+ my_get_expression (&exp, &str, GE_NO_PREFIX, 1);
+
+ if (exp.X_op != O_constant)
+ {
+ first_error (_("constant expression required"));
+ return PARSE_FAIL;
+ }
+
+ if (! skip_past_char (&str, ']'))
+ return PARSE_FAIL;
+
+ atype.index = exp.X_add_number;
+ }
+ else if (!in_reg_list && (atype.defined & NTA_HASINDEX) != 0)
+ {
+ /* Indexed vector register expected. */
+ first_error (_("indexed vector register expected"));
+ return PARSE_FAIL;
+ }
+
+ /* A vector reg Vn should be typed or indexed. */
+ if (type == REG_TYPE_VN && atype.defined == 0)
+ {
+ first_error (_("invalid use of vector register"));
+ }
+
+ if (typeinfo)
+ *typeinfo = atype;
+
+ if (rtype)
+ *rtype = type;
+
+ *ccp = str;
+
+ return reg->number;
+}
+
+/* Parse register.
+
+ Return the register number on success; return PARSE_FAIL otherwise.
+
+ If RTYPE is not NULL, return in *RTYPE the (possibly restricted) type of
+ the register (e.g. NEON double or quad reg when either has been requested).
+
+ If this is a NEON vector register with additional type information, fill
+ in the struct pointed to by VECTYPE (if non-NULL).
+
+ This parser does not handle register list. */
+
+static int
+aarch64_reg_parse (char **ccp, aarch64_reg_type type,
+ aarch64_reg_type *rtype, struct neon_type_el *vectype)
+{
+ struct neon_type_el atype;
+ char *str = *ccp;
+ int reg = parse_typed_reg (&str, type, rtype, &atype,
+ /*in_reg_list= */ FALSE);
+
+ if (reg == PARSE_FAIL)
+ return PARSE_FAIL;
+
+ if (vectype)
+ *vectype = atype;
+
+ *ccp = str;
+
+ return reg;
+}
+
+static inline bfd_boolean
+eq_neon_type_el (struct neon_type_el e1, struct neon_type_el e2)
+{
+ return
+ e1.type == e2.type
+ && e1.defined == e2.defined
+ && e1.width == e2.width && e1.index == e2.index;
+}
+
+/* This function parses the NEON register list. On success, it returns
+ the parsed register list information in the following encoded format:
+
+ bit 18-22 | 13-17 | 7-11 | 2-6 | 0-1
+ 4th regno | 3rd regno | 2nd regno | 1st regno | num_of_reg
+
+ The information of the register shape and/or index is returned in
+ *VECTYPE.
+
+ It returns PARSE_FAIL if the register list is invalid.
+
+ The list contains one to four registers.
+ Each register can be one of:
+ <Vt>.<T>[<index>]
+ <Vt>.<T>
+ All <T> should be identical.
+ All <index> should be identical.
+ There are restrictions on <Vt> numbers which are checked later
+ (by reg_list_valid_p). */
+
+static int
+parse_neon_reg_list (char **ccp, struct neon_type_el *vectype)
+{
+ char *str = *ccp;
+ int nb_regs;
+ struct neon_type_el typeinfo, typeinfo_first;
+ int val, val_range;
+ int in_range;
+ int ret_val;
+ int i;
+ bfd_boolean error = FALSE;
+ bfd_boolean expect_index = FALSE;
+
+ if (*str != '{')
+ {
+ set_syntax_error (_("expecting {"));
+ return PARSE_FAIL;
+ }
+ str++;
+
+ nb_regs = 0;
+ typeinfo_first.defined = 0;
+ typeinfo_first.type = NT_invtype;
+ typeinfo_first.width = -1;
+ typeinfo_first.index = 0;
+ ret_val = 0;
+ val = -1;
+ val_range = -1;
+ in_range = 0;
+ do
+ {
+ if (in_range)
+ {
+ str++; /* skip over '-' */
+ val_range = val;
+ }
+ val = parse_typed_reg (&str, REG_TYPE_VN, NULL, &typeinfo,
+ /*in_reg_list= */ TRUE);
+ if (val == PARSE_FAIL)
+ {
+ set_first_syntax_error (_("invalid vector register in list"));
+ error = TRUE;
+ continue;
+ }
+ /* reject [bhsd]n */
+ if (typeinfo.defined == 0)
+ {
+ set_first_syntax_error (_("invalid scalar register in list"));
+ error = TRUE;
+ continue;
+ }
+
+ if (typeinfo.defined & NTA_HASINDEX)
+ expect_index = TRUE;
+
+ if (in_range)
+ {
+ if (val < val_range)
+ {
+ set_first_syntax_error
+ (_("invalid range in vector register list"));
+ error = TRUE;
+ }
+ val_range++;
+ }
+ else
+ {
+ val_range = val;
+ if (nb_regs == 0)
+ typeinfo_first = typeinfo;
+ else if (! eq_neon_type_el (typeinfo_first, typeinfo))
+ {
+ set_first_syntax_error
+ (_("type mismatch in vector register list"));
+ error = TRUE;
+ }
+ }
+ if (! error)
+ for (i = val_range; i <= val; i++)
+ {
+ ret_val |= i << (5 * nb_regs);
+ nb_regs++;
+ }
+ in_range = 0;
+ }
+ while (skip_past_comma (&str) || (in_range = 1, *str == '-'));
+
+ skip_whitespace (str);
+ if (*str != '}')
+ {
+ set_first_syntax_error (_("end of vector register list not found"));
+ error = TRUE;
+ }
+ str++;
+
+ skip_whitespace (str);
+
+ if (expect_index)
+ {
+ if (skip_past_char (&str, '['))
+ {
+ expressionS exp;
+
+ my_get_expression (&exp, &str, GE_NO_PREFIX, 1);
+ if (exp.X_op != O_constant)
+ {
+ set_first_syntax_error (_("constant expression required."));
+ error = TRUE;
+ }
+ if (! skip_past_char (&str, ']'))
+ error = TRUE;
+ else
+ typeinfo_first.index = exp.X_add_number;
+ }
+ else
+ {
+ set_first_syntax_error (_("expected index"));
+ error = TRUE;
+ }
+ }
+
+ if (nb_regs > 4)
+ {
+ set_first_syntax_error (_("too many registers in vector register list"));
+ error = TRUE;
+ }
+ else if (nb_regs == 0)
+ {
+ set_first_syntax_error (_("empty vector register list"));
+ error = TRUE;
+ }
+
+ *ccp = str;
+ if (! error)
+ *vectype = typeinfo_first;
+
+ return error ? PARSE_FAIL : (ret_val << 2) | (nb_regs - 1);
+}
+
+/* Directives: register aliases. */
+
+static reg_entry *
+insert_reg_alias (char *str, int number, aarch64_reg_type type)
+{
+ reg_entry *new;
+ const char *name;
+
+ if ((new = hash_find (aarch64_reg_hsh, str)) != 0)
+ {
+ if (new->builtin)
+ as_warn (_("ignoring attempt to redefine built-in register '%s'"),
+ str);
+
+ /* Only warn about a redefinition if it's not defined as the
+ same register. */
+ else if (new->number != number || new->type != type)
+ as_warn (_("ignoring redefinition of register alias '%s'"), str);
+
+ return NULL;
+ }
+
+ name = xstrdup (str);
+ new = xmalloc (sizeof (reg_entry));
+
+ new->name = name;
+ new->number = number;
+ new->type = type;
+ new->builtin = FALSE;
+
+ if (hash_insert (aarch64_reg_hsh, name, (void *) new))
+ abort ();
+
+ return new;
+}
+
+/* Look for the .req directive. This is of the form:
+
+ new_register_name .req existing_register_name
+
+ If we find one, or if it looks sufficiently like one that we want to
+ handle any error here, return TRUE. Otherwise return FALSE. */
+
+static bfd_boolean
+create_register_alias (char *newname, char *p)
+{
+ const reg_entry *old;
+ char *oldname, *nbuf;
+ size_t nlen;
+
+ /* The input scrubber ensures that whitespace after the mnemonic is
+ collapsed to single spaces. */
+ oldname = p;
+ if (strncmp (oldname, " .req ", 6) != 0)
+ return FALSE;
+
+ oldname += 6;
+ if (*oldname == '\0')
+ return FALSE;
+
+ old = hash_find (aarch64_reg_hsh, oldname);
+ if (!old)
+ {
+ as_warn (_("unknown register '%s' -- .req ignored"), oldname);
+ return TRUE;
+ }
+
+ /* If TC_CASE_SENSITIVE is defined, then newname already points to
+ the desired alias name, and p points to its end. If not, then
+ the desired alias name is in the global original_case_string. */
+#ifdef TC_CASE_SENSITIVE
+ nlen = p - newname;
+#else
+ newname = original_case_string;
+ nlen = strlen (newname);
+#endif
+
+ nbuf = alloca (nlen + 1);
+ memcpy (nbuf, newname, nlen);
+ nbuf[nlen] = '\0';
+
+ /* Create aliases under the new name as stated; an all-lowercase
+ version of the new name; and an all-uppercase version of the new
+ name. */
+ if (insert_reg_alias (nbuf, old->number, old->type) != NULL)
+ {
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
+
+ if (strncmp (nbuf, newname, nlen))
+ {
+ /* If this attempt to create an additional alias fails, do not bother
+ trying to create the all-lower case alias. We will fail and issue
+ a second, duplicate error message. This situation arises when the
+ programmer does something like:
+ foo .req r0
+ Foo .req r1
+ The second .req creates the "Foo" alias but then fails to create
+ the artificial FOO alias because it has already been created by the
+ first .req. */
+ if (insert_reg_alias (nbuf, old->number, old->type) == NULL)
+ return TRUE;
+ }
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+
+ if (strncmp (nbuf, newname, nlen))
+ insert_reg_alias (nbuf, old->number, old->type);
+ }
+
+ return TRUE;
+}
+
+/* Should never be called, as .req goes between the alias and the
+ register name, not at the beginning of the line. */
+static void
+s_req (int a ATTRIBUTE_UNUSED)
+{
+ as_bad (_("invalid syntax for .req directive"));
+}
+
+/* The .unreq directive deletes an alias which was previously defined
+ by .req. For example:
+
+ my_alias .req r11
+ .unreq my_alias */
+
+static void
+s_unreq (int a ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char saved_char;
+
+ name = input_line_pointer;
+
+ while (*input_line_pointer != 0
+ && *input_line_pointer != ' ' && *input_line_pointer != '\n')
+ ++input_line_pointer;
+
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ if (!*name)
+ as_bad (_("invalid syntax for .unreq directive"));
+ else
+ {
+ reg_entry *reg = hash_find (aarch64_reg_hsh, name);
+
+ if (!reg)
+ as_bad (_("unknown register alias '%s'"), name);
+ else if (reg->builtin)
+ as_warn (_("ignoring attempt to undefine built-in register '%s'"),
+ name);
+ else
+ {
+ char *p;
+ char *nbuf;
+
+ hash_delete (aarch64_reg_hsh, name, FALSE);
+ free ((char *) reg->name);
+ free (reg);
+
+ /* Also locate the all upper case and all lower case versions.
+ Do not complain if we cannot find one or the other as it
+ was probably deleted above. */
+
+ nbuf = strdup (name);
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
+ reg = hash_find (aarch64_reg_hsh, nbuf);
+ if (reg)
+ {
+ hash_delete (aarch64_reg_hsh, nbuf, FALSE);
+ free ((char *) reg->name);
+ free (reg);
+ }
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+ reg = hash_find (aarch64_reg_hsh, nbuf);
+ if (reg)
+ {
+ hash_delete (aarch64_reg_hsh, nbuf, FALSE);
+ free ((char *) reg->name);
+ free (reg);
+ }
+
+ free (nbuf);
+ }
+ }
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+}
+
+/* Directives: Instruction set selection. */
+
+#ifdef OBJ_ELF
+/* This code is to handle mapping symbols as defined in the ARM AArch64 ELF
+ spec. (See "Mapping symbols", section 4.5.4, ARM AAELF64 version 0.05).
+ Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
+ and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped. */
+
+/* Create a new mapping symbol for the transition to STATE. */
+
+static void
+make_mapping_symbol (enum mstate state, valueT value, fragS * frag)
+{
+ symbolS *symbolP;
+ const char *symname;
+ int type;
+
+ switch (state)
+ {
+ case MAP_DATA:
+ symname = "$d";
+ type = BSF_NO_FLAGS;
+ break;
+ case MAP_INSN:
+ symname = "$x";
+ type = BSF_NO_FLAGS;
+ break;
+ default:
+ abort ();
+ }
+
+ symbolP = symbol_new (symname, now_seg, value, frag);
+ symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
+
+ /* Save the mapping symbols for future reference. Also check that
+ we do not place two mapping symbols at the same offset within a
+ frag. We'll handle overlap between frags in
+ check_mapping_symbols.
+
+ If .fill or other data filling directive generates zero sized data,
+ the mapping symbol for the following code will have the same value
+ as the one generated for the data filling directive. In this case,
+ we replace the old symbol with the new one at the same address. */
+ if (value == 0)
+ {
+ if (frag->tc_frag_data.first_map != NULL)
+ {
+ know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0);
+ symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP,
+ &symbol_lastP);
+ }
+ frag->tc_frag_data.first_map = symbolP;
+ }
+ if (frag->tc_frag_data.last_map != NULL)
+ {
+ know (S_GET_VALUE (frag->tc_frag_data.last_map) <=
+ S_GET_VALUE (symbolP));
+ if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP))
+ symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP,
+ &symbol_lastP);
+ }
+ frag->tc_frag_data.last_map = symbolP;
+}
+
+/* We must sometimes convert a region marked as code to data during
+ code alignment, if an odd number of bytes have to be padded. The
+ code mapping symbol is pushed to an aligned address. */
+
+static void
+insert_data_mapping_symbol (enum mstate state,
+ valueT value, fragS * frag, offsetT bytes)
+{
+ /* If there was already a mapping symbol, remove it. */
+ if (frag->tc_frag_data.last_map != NULL
+ && S_GET_VALUE (frag->tc_frag_data.last_map) ==
+ frag->fr_address + value)
+ {
+ symbolS *symp = frag->tc_frag_data.last_map;
+
+ if (value == 0)
+ {
+ know (frag->tc_frag_data.first_map == symp);
+ frag->tc_frag_data.first_map = NULL;
+ }
+ frag->tc_frag_data.last_map = NULL;
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
+
+ make_mapping_symbol (MAP_DATA, value, frag);
+ make_mapping_symbol (state, value + bytes, frag);
+}
+
+static void mapping_state_2 (enum mstate state, int max_chars);
+
+/* Set the mapping state to STATE. Only call this when about to
+ emit some STATE bytes to the file. */
+
+void
+mapping_state (enum mstate state)
+{
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+
+#define TRANSITION(from, to) (mapstate == (from) && state == (to))
+
+ if (mapstate == state)
+ /* The mapping symbol has already been emitted.
+ There is nothing else to do. */
+ return;
+ else if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
+ /* This case will be evaluated later in the next else. */
+ return;
+ else if (TRANSITION (MAP_UNDEFINED, MAP_INSN))
+ {
+ /* Only add the symbol if the offset is > 0:
+ if we're at the first frag, check it's size > 0;
+ if we're not at the first frag, then for sure
+ the offset is > 0. */
+ struct frag *const frag_first = seg_info (now_seg)->frchainP->frch_root;
+ const int add_symbol = (frag_now != frag_first)
+ || (frag_now_fix () > 0);
+
+ if (add_symbol)
+ make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
+ }
+
+ mapping_state_2 (state, 0);
+#undef TRANSITION
+}
+
+/* Same as mapping_state, but MAX_CHARS bytes have already been
+ allocated. Put the mapping symbol that far back. */
+
+static void
+mapping_state_2 (enum mstate state, int max_chars)
+{
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+
+ if (!SEG_NORMAL (now_seg))
+ return;
+
+ if (mapstate == state)
+ /* The mapping symbol has already been emitted.
+ There is nothing else to do. */
+ return;
+
+ seg_info (now_seg)->tc_segment_info_data.mapstate = state;
+ make_mapping_symbol (state, (valueT) frag_now_fix () - max_chars, frag_now);
+}
+#else
+#define mapping_state(x) /* nothing */
+#define mapping_state_2(x, y) /* nothing */
+#endif
+
+/* Directives: sectioning and alignment. */
+
+static void
+s_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ /* We don't support putting frags in the BSS segment, we fake it by
+ marking in_bss, then looking at s_skip for clues. */
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+ mapping_state (MAP_DATA);
+}
+
+static void
+s_even (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Never make frag if expect extra pass. */
+ if (!need_pass_2)
+ frag_align (1, 0, 0);
+
+ record_alignment (now_seg, 1);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Directives: Literal pools. */
+
+static literal_pool *
+find_literal_pool (int size)
+{
+ literal_pool *pool;
+
+ for (pool = list_of_pools; pool != NULL; pool = pool->next)
+ {
+ if (pool->section == now_seg
+ && pool->sub_section == now_subseg && pool->size == size)
+ break;
+ }
+
+ return pool;
+}
+
+static literal_pool *
+find_or_make_literal_pool (int size)
+{
+ /* Next literal pool ID number. */
+ static unsigned int latest_pool_num = 1;
+ literal_pool *pool;
+
+ pool = find_literal_pool (size);
+
+ if (pool == NULL)
+ {
+ /* Create a new pool. */
+ pool = xmalloc (sizeof (*pool));
+ if (!pool)
+ return NULL;
+
+ /* Currently we always put the literal pool in the current text
+ section. If we were generating "small" model code where we
+ knew that all code and initialised data was within 1MB then
+ we could output literals to mergeable, read-only data
+ sections. */
+
+ pool->next_free_entry = 0;
+ pool->section = now_seg;
+ pool->sub_section = now_subseg;
+ pool->size = size;
+ pool->next = list_of_pools;
+ pool->symbol = NULL;
+
+ /* Add it to the list. */
+ list_of_pools = pool;
+ }
+
+ /* New pools, and emptied pools, will have a NULL symbol. */
+ if (pool->symbol == NULL)
+ {
+ pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ pool->id = latest_pool_num++;
+ }
+
+ /* Done. */
+ return pool;
+}
+
+/* Add the literal of size SIZE in *EXP to the relevant literal pool.
+ Return TRUE on success, otherwise return FALSE. */
+static bfd_boolean
+add_to_lit_pool (expressionS *exp, int size)
+{
+ literal_pool *pool;
+ unsigned int entry;
+
+ pool = find_or_make_literal_pool (size);
+
+ /* Check if this literal value is already in the pool. */
+ for (entry = 0; entry < pool->next_free_entry; entry++)
+ {
+ expressionS * litexp = & pool->literals[entry].exp;
+
+ if ((litexp->X_op == exp->X_op)
+ && (exp->X_op == O_constant)
+ && (litexp->X_add_number == exp->X_add_number)
+ && (litexp->X_unsigned == exp->X_unsigned))
+ break;
+
+ if ((litexp->X_op == exp->X_op)
+ && (exp->X_op == O_symbol)
+ && (litexp->X_add_number == exp->X_add_number)
+ && (litexp->X_add_symbol == exp->X_add_symbol)
+ && (litexp->X_op_symbol == exp->X_op_symbol))
+ break;
+ }
+
+ /* Do we need to create a new entry? */
+ if (entry == pool->next_free_entry)
+ {
+ if (entry >= MAX_LITERAL_POOL_SIZE)
+ {
+ set_syntax_error (_("literal pool overflow"));
+ return FALSE;
+ }
+
+ pool->literals[entry].exp = *exp;
+ pool->next_free_entry += 1;
+ if (exp->X_op == O_big)
+ {
+ /* PR 16688: Bignums are held in a single global array. We must
+ copy and preserve that value now, before it is overwritten. */
+ pool->literals[entry].bignum = xmalloc (CHARS_PER_LITTLENUM * exp->X_add_number);
+ memcpy (pool->literals[entry].bignum, generic_bignum,
+ CHARS_PER_LITTLENUM * exp->X_add_number);
+ }
+ else
+ pool->literals[entry].bignum = NULL;
+ }
+
+ exp->X_op = O_symbol;
+ exp->X_add_number = ((int) entry) * size;
+ exp->X_add_symbol = pool->symbol;
+
+ return TRUE;
+}
+
+/* Can't use symbol_new here, so have to create a symbol and then at
+ a later date assign it a value. Thats what these functions do. */
+
+static void
+symbol_locate (symbolS * symbolP,
+ const char *name,/* It is copied, the caller can modify. */
+ segT segment, /* Segment identifier (SEG_<something>). */
+ valueT valu, /* Symbol value. */
+ fragS * frag) /* Associated fragment. */
+{
+ size_t name_length;
+ char *preserved_copy_of_name;
+
+ name_length = strlen (name) + 1; /* +1 for \0. */
+ obstack_grow (&notes, name, name_length);
+ preserved_copy_of_name = obstack_finish (&notes);
+
+#ifdef tc_canonicalize_symbol_name
+ preserved_copy_of_name =
+ tc_canonicalize_symbol_name (preserved_copy_of_name);
+#endif
+
+ S_SET_NAME (symbolP, preserved_copy_of_name);
+
+ S_SET_SEGMENT (symbolP, segment);
+ S_SET_VALUE (symbolP, valu);
+ symbol_clear_list_pointers (symbolP);
+
+ symbol_set_frag (symbolP, frag);
+
+ /* Link to end of symbol chain. */
+ {
+ extern int symbol_table_frozen;
+
+ if (symbol_table_frozen)
+ abort ();
+ }
+
+ symbol_append (symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ obj_symbol_new_hook (symbolP);
+
+#ifdef tc_symbol_new_hook
+ tc_symbol_new_hook (symbolP);
+#endif
+
+#ifdef DEBUG_SYMS
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS */
+}
+
+
+static void
+s_ltorg (int ignored ATTRIBUTE_UNUSED)
+{
+ unsigned int entry;
+ literal_pool *pool;
+ char sym_name[20];
+ int align;
+
+ for (align = 2; align <= 4; align++)
+ {
+ int size = 1 << align;
+
+ pool = find_literal_pool (size);
+ if (pool == NULL || pool->symbol == NULL || pool->next_free_entry == 0)
+ continue;
+
+ mapping_state (MAP_DATA);
+
+ /* Align pool as you have word accesses.
+ Only make a frag if we have to. */
+ if (!need_pass_2)
+ frag_align (align, 0, 0);
+
+ record_alignment (now_seg, align);
+
+ sprintf (sym_name, "$$lit_\002%x", pool->id);
+
+ symbol_locate (pool->symbol, sym_name, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ symbol_table_insert (pool->symbol);
+
+ for (entry = 0; entry < pool->next_free_entry; entry++)
+ {
+ expressionS * exp = & pool->literals[entry].exp;
+
+ if (exp->X_op == O_big)
+ {
+ /* PR 16688: Restore the global bignum value. */
+ gas_assert (pool->literals[entry].bignum != NULL);
+ memcpy (generic_bignum, pool->literals[entry].bignum,
+ CHARS_PER_LITTLENUM * exp->X_add_number);
+ }
+
+ /* First output the expression in the instruction to the pool. */
+ emit_expr (exp, size); /* .word|.xword */
+
+ if (exp->X_op == O_big)
+ {
+ free (pool->literals[entry].bignum);
+ pool->literals[entry].bignum = NULL;
+ }
+ }
+
+ /* Mark the pool as empty. */
+ pool->next_free_entry = 0;
+ pool->symbol = NULL;
+ }
+}
+
+#ifdef OBJ_ELF
+/* Forward declarations for functions below, in the MD interface
+ section. */
+static fixS *fix_new_aarch64 (fragS *, int, short, expressionS *, int, int);
+static struct reloc_table_entry * find_reloc_table_entry (char **);
+
+/* Directives: Data. */
+/* N.B. the support for relocation suffix in this directive needs to be
+ implemented properly. */
+
+static void
+s_aarch64_elf_cons (int nbytes)
+{
+ expressionS exp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ mapping_state (MAP_DATA);
+ do
+ {
+ struct reloc_table_entry *reloc;
+
+ expression (&exp);
+
+ if (exp.X_op != O_symbol)
+ emit_expr (&exp, (unsigned int) nbytes);
+ else
+ {
+ skip_past_char (&input_line_pointer, '#');
+ if (skip_past_char (&input_line_pointer, ':'))
+ {
+ reloc = find_reloc_table_entry (&input_line_pointer);
+ if (reloc == NULL)
+ as_bad (_("unrecognized relocation suffix"));
+ else
+ as_bad (_("unimplemented relocation suffix"));
+ ignore_rest_of_line ();
+ return;
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Put terminator back into stream. */
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+
+#endif /* OBJ_ELF */
+
+/* Output a 32-bit word, but mark as an instruction. */
+
+static void
+s_aarch64_inst (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (!need_pass_2)
+ frag_align_code (2, 0);
+#ifdef OBJ_ELF
+ mapping_state (MAP_INSN);
+#endif
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("constant expression required"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (target_big_endian)
+ {
+ unsigned int val = exp.X_add_number;
+ exp.X_add_number = SWAP_32 (val);
+ }
+ emit_expr (&exp, 4);
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Put terminator back into stream. */
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+
+#ifdef OBJ_ELF
+/* Emit BFD_RELOC_AARCH64_TLSDESC_CALL on the next BLR instruction. */
+
+static void
+s_tlsdesccall (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ /* Since we're just labelling the code, there's no need to define a
+ mapping symbol. */
+ expression (&exp);
+ /* Make sure there is enough room in this frag for the following
+ blr. This trick only works if the blr follows immediately after
+ the .tlsdesc directive. */
+ frag_grow (4);
+ fix_new_aarch64 (frag_now, frag_more (0) - frag_now->fr_literal, 4, &exp, 0,
+ BFD_RELOC_AARCH64_TLSDESC_CALL);
+
+ demand_empty_rest_of_line ();
+}
+#endif /* OBJ_ELF */
+
+static void s_aarch64_arch (int);
+static void s_aarch64_cpu (int);
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+
+const pseudo_typeS md_pseudo_table[] = {
+ /* Never called because '.req' does not start a line. */
+ {"req", s_req, 0},
+ {"unreq", s_unreq, 0},
+ {"bss", s_bss, 0},
+ {"even", s_even, 0},
+ {"ltorg", s_ltorg, 0},
+ {"pool", s_ltorg, 0},
+ {"cpu", s_aarch64_cpu, 0},
+ {"arch", s_aarch64_arch, 0},
+ {"inst", s_aarch64_inst, 0},
+#ifdef OBJ_ELF
+ {"tlsdesccall", s_tlsdesccall, 0},
+ {"word", s_aarch64_elf_cons, 4},
+ {"long", s_aarch64_elf_cons, 4},
+ {"xword", s_aarch64_elf_cons, 8},
+ {"dword", s_aarch64_elf_cons, 8},
+#endif
+ {0, 0, 0}
+};
+
+
+/* Check whether STR points to a register name followed by a comma or the
+ end of line; REG_TYPE indicates which register types are checked
+ against. Return TRUE if STR is such a register name; otherwise return
+ FALSE. The function does not intend to produce any diagnostics, but since
+ the register parser aarch64_reg_parse, which is called by this function,
+ does produce diagnostics, we call clear_error to clear any diagnostics
+ that may be generated by aarch64_reg_parse.
+ Also, the function returns FALSE directly if there is any user error
+ present at the function entry. This prevents the existing diagnostics
+ state from being spoiled.
+ The function currently serves parse_constant_immediate and
+ parse_big_immediate only. */
+static bfd_boolean
+reg_name_p (char *str, aarch64_reg_type reg_type)
+{
+ int reg;
+
+ /* Prevent the diagnostics state from being spoiled. */
+ if (error_p ())
+ return FALSE;
+
+ reg = aarch64_reg_parse (&str, reg_type, NULL, NULL);
+
+ /* Clear the parsing error that may be set by the reg parser. */
+ clear_error ();
+
+ if (reg == PARSE_FAIL)
+ return FALSE;
+
+ skip_whitespace (str);
+ if (*str == ',' || is_end_of_line[(unsigned int) *str])
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Parser functions used exclusively in instruction operands. */
+
+/* Parse an immediate expression which may not be constant.
+
+ To prevent the expression parser from pushing a register name
+ into the symbol table as an undefined symbol, firstly a check is
+ done to find out whether STR is a valid register name followed
+ by a comma or the end of line. Return FALSE if STR is such a
+ string. */
+
+static bfd_boolean
+parse_immediate_expression (char **str, expressionS *exp)
+{
+ if (reg_name_p (*str, REG_TYPE_R_Z_BHSDQ_V))
+ {
+ set_recoverable_error (_("immediate operand required"));
+ return FALSE;
+ }
+
+ my_get_expression (exp, str, GE_OPT_PREFIX, 1);
+
+ if (exp->X_op == O_absent)
+ {
+ set_fatal_syntax_error (_("missing immediate expression"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Constant immediate-value read function for use in insn parsing.
+ STR points to the beginning of the immediate (with the optional
+ leading #); *VAL receives the value.
+
+ Return TRUE on success; otherwise return FALSE. */
+
+static bfd_boolean
+parse_constant_immediate (char **str, int64_t * val)
+{
+ expressionS exp;
+
+ if (! parse_immediate_expression (str, &exp))
+ return FALSE;
+
+ if (exp.X_op != O_constant)
+ {
+ set_syntax_error (_("constant expression required"));
+ return FALSE;
+ }
+
+ *val = exp.X_add_number;
+ return TRUE;
+}
+
+static uint32_t
+encode_imm_float_bits (uint32_t imm)
+{
+ return ((imm >> 19) & 0x7f) /* b[25:19] -> b[6:0] */
+ | ((imm >> (31 - 7)) & 0x80); /* b[31] -> b[7] */
+}
+
+/* Return TRUE if the single-precision floating-point value encoded in IMM
+ can be expressed in the AArch64 8-bit signed floating-point format with
+ 3-bit exponent and normalized 4 bits of precision; in other words, the
+ floating-point value must be expressable as
+ (+/-) n / 16 * power (2, r)
+ where n and r are integers such that 16 <= n <=31 and -3 <= r <= 4. */
+
+static bfd_boolean
+aarch64_imm_float_p (uint32_t imm)
+{
+ /* If a single-precision floating-point value has the following bit
+ pattern, it can be expressed in the AArch64 8-bit floating-point
+ format:
+
+ 3 32222222 2221111111111
+ 1 09876543 21098765432109876543210
+ n Eeeeeexx xxxx0000000000000000000
+
+ where n, e and each x are either 0 or 1 independently, with
+ E == ~ e. */
+
+ uint32_t pattern;
+
+ /* Prepare the pattern for 'Eeeeee'. */
+ if (((imm >> 30) & 0x1) == 0)
+ pattern = 0x3e000000;
+ else
+ pattern = 0x40000000;
+
+ return (imm & 0x7ffff) == 0 /* lower 19 bits are 0. */
+ && ((imm & 0x7e000000) == pattern); /* bits 25 - 29 == ~ bit 30. */
+}
+
+/* Like aarch64_imm_float_p but for a double-precision floating-point value.
+
+ Return TRUE if the value encoded in IMM can be expressed in the AArch64
+ 8-bit signed floating-point format with 3-bit exponent and normalized 4
+ bits of precision (i.e. can be used in an FMOV instruction); return the
+ equivalent single-precision encoding in *FPWORD.
+
+ Otherwise return FALSE. */
+
+static bfd_boolean
+aarch64_double_precision_fmovable (uint64_t imm, uint32_t *fpword)
+{
+ /* If a double-precision floating-point value has the following bit
+ pattern, it can be expressed in the AArch64 8-bit floating-point
+ format:
+
+ 6 66655555555 554444444...21111111111
+ 3 21098765432 109876543...098765432109876543210
+ n Eeeeeeeeexx xxxx00000...000000000000000000000
+
+ where n, e and each x are either 0 or 1 independently, with
+ E == ~ e. */
+
+ uint32_t pattern;
+ uint32_t high32 = imm >> 32;
+
+ /* Lower 32 bits need to be 0s. */
+ if ((imm & 0xffffffff) != 0)
+ return FALSE;
+
+ /* Prepare the pattern for 'Eeeeeeeee'. */
+ if (((high32 >> 30) & 0x1) == 0)
+ pattern = 0x3fc00000;
+ else
+ pattern = 0x40000000;
+
+ if ((high32 & 0xffff) == 0 /* bits 32 - 47 are 0. */
+ && (high32 & 0x7fc00000) == pattern) /* bits 54 - 61 == ~ bit 62. */
+ {
+ /* Convert to the single-precision encoding.
+ i.e. convert
+ n Eeeeeeeeexx xxxx00000...000000000000000000000
+ to
+ n Eeeeeexx xxxx0000000000000000000. */
+ *fpword = ((high32 & 0xfe000000) /* nEeeeee. */
+ | (((high32 >> 16) & 0x3f) << 19)); /* xxxxxx. */
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/* Parse a floating-point immediate. Return TRUE on success and return the
+ value in *IMMED in the format of IEEE754 single-precision encoding.
+ *CCP points to the start of the string; DP_P is TRUE when the immediate
+ is expected to be in double-precision (N.B. this only matters when
+ hexadecimal representation is involved).
+
+ N.B. 0.0 is accepted by this function. */
+
+static bfd_boolean
+parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p)
+{
+ char *str = *ccp;
+ char *fpnum;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ int found_fpchar = 0;
+ int64_t val = 0;
+ unsigned fpword = 0;
+ bfd_boolean hex_p = FALSE;
+
+ skip_past_char (&str, '#');
+
+ fpnum = str;
+ skip_whitespace (fpnum);
+
+ if (strncmp (fpnum, "0x", 2) == 0)
+ {
+ /* Support the hexadecimal representation of the IEEE754 encoding.
+ Double-precision is expected when DP_P is TRUE, otherwise the
+ representation should be in single-precision. */
+ if (! parse_constant_immediate (&str, &val))
+ goto invalid_fp;
+
+ if (dp_p)
+ {
+ if (! aarch64_double_precision_fmovable (val, &fpword))
+ goto invalid_fp;
+ }
+ else if ((uint64_t) val > 0xffffffff)
+ goto invalid_fp;
+ else
+ fpword = val;
+
+ hex_p = TRUE;
+ }
+ else
+ {
+ /* We must not accidentally parse an integer as a floating-point number.
+ Make sure that the value we parse is not an integer by checking for
+ special characters '.' or 'e'. */
+ for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++)
+ if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
+ {
+ found_fpchar = 1;
+ break;
+ }
+
+ if (!found_fpchar)
+ return FALSE;
+ }
+
+ if (! hex_p)
+ {
+ int i;
+
+ if ((str = atof_ieee (str, 's', words)) == NULL)
+ goto invalid_fp;
+
+ /* Our FP word must be 32 bits (single-precision FP). */
+ for (i = 0; i < 32 / LITTLENUM_NUMBER_OF_BITS; i++)
+ {
+ fpword <<= LITTLENUM_NUMBER_OF_BITS;
+ fpword |= words[i];
+ }
+ }
+
+ if (aarch64_imm_float_p (fpword) || (fpword & 0x7fffffff) == 0)
+ {
+ *immed = fpword;
+ *ccp = str;
+ return TRUE;
+ }
+
+invalid_fp:
+ set_fatal_syntax_error (_("invalid floating-point constant"));
+ return FALSE;
+}
+
+/* Less-generic immediate-value read function with the possibility of loading
+ a big (64-bit) immediate, as required by AdvSIMD Modified immediate
+ instructions.
+
+ To prevent the expression parser from pushing a register name into the
+ symbol table as an undefined symbol, a check is firstly done to find
+ out whether STR is a valid register name followed by a comma or the end
+ of line. Return FALSE if STR is such a register. */
+
+static bfd_boolean
+parse_big_immediate (char **str, int64_t *imm)
+{
+ char *ptr = *str;
+
+ if (reg_name_p (ptr, REG_TYPE_R_Z_BHSDQ_V))
+ {
+ set_syntax_error (_("immediate operand required"));
+ return FALSE;
+ }
+
+ my_get_expression (&inst.reloc.exp, &ptr, GE_OPT_PREFIX, 1);
+
+ if (inst.reloc.exp.X_op == O_constant)
+ *imm = inst.reloc.exp.X_add_number;
+
+ *str = ptr;
+
+ return TRUE;
+}
+
+/* Set operand IDX of the *INSTR that needs a GAS internal fixup.
+ if NEED_LIBOPCODES is non-zero, the fixup will need
+ assistance from the libopcodes. */
+
+static inline void
+aarch64_set_gas_internal_fixup (struct reloc *reloc,
+ const aarch64_opnd_info *operand,
+ int need_libopcodes_p)
+{
+ reloc->type = BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP;
+ reloc->opnd = operand->type;
+ if (need_libopcodes_p)
+ reloc->need_libopcodes_p = 1;
+};
+
+/* Return TRUE if the instruction needs to be fixed up later internally by
+ the GAS; otherwise return FALSE. */
+
+static inline bfd_boolean
+aarch64_gas_internal_fixup_p (void)
+{
+ return inst.reloc.type == BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP;
+}
+
+/* Assign the immediate value to the relavant field in *OPERAND if
+ RELOC->EXP is a constant expression; otherwise, flag that *OPERAND
+ needs an internal fixup in a later stage.
+ ADDR_OFF_P determines whether it is the field ADDR.OFFSET.IMM or
+ IMM.VALUE that may get assigned with the constant. */
+static inline void
+assign_imm_if_const_or_fixup_later (struct reloc *reloc,
+ aarch64_opnd_info *operand,
+ int addr_off_p,
+ int need_libopcodes_p,
+ int skip_p)
+{
+ if (reloc->exp.X_op == O_constant)
+ {
+ if (addr_off_p)
+ operand->addr.offset.imm = reloc->exp.X_add_number;
+ else
+ operand->imm.value = reloc->exp.X_add_number;
+ reloc->type = BFD_RELOC_UNUSED;
+ }
+ else
+ {
+ aarch64_set_gas_internal_fixup (reloc, operand, need_libopcodes_p);
+ /* Tell libopcodes to ignore this operand or not. This is helpful
+ when one of the operands needs to be fixed up later but we need
+ libopcodes to check the other operands. */
+ operand->skip = skip_p;
+ }
+}
+
+/* Relocation modifiers. Each entry in the table contains the textual
+ name for the relocation which may be placed before a symbol used as
+ a load/store offset, or add immediate. It must be surrounded by a
+ leading and trailing colon, for example:
+
+ ldr x0, [x1, #:rello:varsym]
+ add x0, x1, #:rello:varsym */
+
+struct reloc_table_entry
+{
+ const char *name;
+ int pc_rel;
+ bfd_reloc_code_real_type adrp_type;
+ bfd_reloc_code_real_type movw_type;
+ bfd_reloc_code_real_type add_type;
+ bfd_reloc_code_real_type ldst_type;
+};
+
+static struct reloc_table_entry reloc_table[] = {
+ /* Low 12 bits of absolute address: ADD/i and LDR/STR */
+ {"lo12", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_ADD_LO12,
+ BFD_RELOC_AARCH64_LDST_LO12},
+
+ /* Higher 21 bits of pc-relative page offset: ADRP */
+ {"pg_hi21", 1,
+ BFD_RELOC_AARCH64_ADR_HI21_PCREL,
+ 0,
+ 0,
+ 0},
+
+ /* Higher 21 bits of pc-relative page offset: ADRP, no check */
+ {"pg_hi21_nc", 1,
+ BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL,
+ 0,
+ 0,
+ 0},
+
+ /* Most significant bits 0-15 of unsigned address/value: MOVZ */
+ {"abs_g0", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G0,
+ 0,
+ 0},
+
+ /* Most significant bits 0-15 of signed address/value: MOVN/Z */
+ {"abs_g0_s", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G0_S,
+ 0,
+ 0},
+
+ /* Less significant bits 0-15 of address/value: MOVK, no check */
+ {"abs_g0_nc", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G0_NC,
+ 0,
+ 0},
+
+ /* Most significant bits 16-31 of unsigned address/value: MOVZ */
+ {"abs_g1", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G1,
+ 0,
+ 0},
+
+ /* Most significant bits 16-31 of signed address/value: MOVN/Z */
+ {"abs_g1_s", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G1_S,
+ 0,
+ 0},
+
+ /* Less significant bits 16-31 of address/value: MOVK, no check */
+ {"abs_g1_nc", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G1_NC,
+ 0,
+ 0},
+
+ /* Most significant bits 32-47 of unsigned address/value: MOVZ */
+ {"abs_g2", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G2,
+ 0,
+ 0},
+
+ /* Most significant bits 32-47 of signed address/value: MOVN/Z */
+ {"abs_g2_s", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G2_S,
+ 0,
+ 0},
+
+ /* Less significant bits 32-47 of address/value: MOVK, no check */
+ {"abs_g2_nc", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G2_NC,
+ 0,
+ 0},
+
+ /* Most significant bits 48-63 of signed/unsigned address/value: MOVZ */
+ {"abs_g3", 0,
+ 0,
+ BFD_RELOC_AARCH64_MOVW_G3,
+ 0,
+ 0},
+
+ /* Get to the page containing GOT entry for a symbol. */
+ {"got", 1,
+ BFD_RELOC_AARCH64_ADR_GOT_PAGE,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_GOT_LD_PREL19},
+
+ /* 12 bit offset into the page containing GOT entry for that symbol. */
+ {"got_lo12", 0,
+ 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_LD_GOT_LO12_NC},
+
+ /* Get to the page containing GOT TLS entry for a symbol */
+ {"tlsgd", 0,
+ BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21,
+ 0,
+ 0,
+ 0},
+
+ /* 12 bit offset into the page containing GOT TLS entry for a symbol */
+ {"tlsgd_lo12", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC,
+ 0},
+
+ /* Get to the page containing GOT TLS entry for a symbol */
+ {"tlsdesc", 0,
+ BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21,
+ 0,
+ 0,
+ 0},
+
+ /* 12 bit offset into the page containing GOT TLS entry for a symbol */
+ {"tlsdesc_lo12", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC,
+ BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC},
+
+ /* Get to the page containing GOT TLS entry for a symbol */
+ {"gottprel", 0,
+ BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21,
+ 0,
+ 0,
+ 0},
+
+ /* 12 bit offset into the page containing GOT TLS entry for a symbol */
+ {"gottprel_lo12", 0,
+ 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC},
+
+ /* Get tp offset for a symbol. */
+ {"tprel", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+ 0},
+
+ /* Get tp offset for a symbol. */
+ {"tprel_lo12", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+ 0},
+
+ /* Get tp offset for a symbol. */
+ {"tprel_hi12", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12,
+ 0},
+
+ /* Get tp offset for a symbol. */
+ {"tprel_lo12_nc", 0,
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
+ 0},
+
+ /* Most significant bits 32-47 of address/value: MOVZ. */
+ {"tprel_g2", 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2,
+ 0,
+ 0},
+
+ /* Most significant bits 16-31 of address/value: MOVZ. */
+ {"tprel_g1", 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1,
+ 0,
+ 0},
+
+ /* Most significant bits 16-31 of address/value: MOVZ, no check. */
+ {"tprel_g1_nc", 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC,
+ 0,
+ 0},
+
+ /* Most significant bits 0-15 of address/value: MOVZ. */
+ {"tprel_g0", 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0,
+ 0,
+ 0},
+
+ /* Most significant bits 0-15 of address/value: MOVZ, no check. */
+ {"tprel_g0_nc", 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC,
+ 0,
+ 0},
+};
+
+/* Given the address of a pointer pointing to the textual name of a
+ relocation as may appear in assembler source, attempt to find its
+ details in reloc_table. The pointer will be updated to the character
+ after the trailing colon. On failure, NULL will be returned;
+ otherwise return the reloc_table_entry. */
+
+static struct reloc_table_entry *
+find_reloc_table_entry (char **str)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE (reloc_table); i++)
+ {
+ int length = strlen (reloc_table[i].name);
+
+ if (strncasecmp (reloc_table[i].name, *str, length) == 0
+ && (*str)[length] == ':')
+ {
+ *str += (length + 1);
+ return &reloc_table[i];
+ }
+ }
+
+ return NULL;
+}
+
+/* Mode argument to parse_shift and parser_shifter_operand. */
+enum parse_shift_mode
+{
+ SHIFTED_ARITH_IMM, /* "rn{,lsl|lsr|asl|asr|uxt|sxt #n}" or
+ "#imm{,lsl #n}" */
+ SHIFTED_LOGIC_IMM, /* "rn{,lsl|lsr|asl|asr|ror #n}" or
+ "#imm" */
+ SHIFTED_LSL, /* bare "lsl #n" */
+ SHIFTED_LSL_MSL, /* "lsl|msl #n" */
+ SHIFTED_REG_OFFSET /* [su]xtw|sxtx {#n} or lsl #n */
+};
+
+/* Parse a <shift> operator on an AArch64 data processing instruction.
+ Return TRUE on success; otherwise return FALSE. */
+static bfd_boolean
+parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode)
+{
+ const struct aarch64_name_value_pair *shift_op;
+ enum aarch64_modifier_kind kind;
+ expressionS exp;
+ int exp_has_prefix;
+ char *s = *str;
+ char *p = s;
+
+ for (p = *str; ISALPHA (*p); p++)
+ ;
+
+ if (p == *str)
+ {
+ set_syntax_error (_("shift expression expected"));
+ return FALSE;
+ }
+
+ shift_op = hash_find_n (aarch64_shift_hsh, *str, p - *str);
+
+ if (shift_op == NULL)
+ {
+ set_syntax_error (_("shift operator expected"));
+ return FALSE;
+ }
+
+ kind = aarch64_get_operand_modifier (shift_op);
+
+ if (kind == AARCH64_MOD_MSL && mode != SHIFTED_LSL_MSL)
+ {
+ set_syntax_error (_("invalid use of 'MSL'"));
+ return FALSE;
+ }
+
+ switch (mode)
+ {
+ case SHIFTED_LOGIC_IMM:
+ if (aarch64_extend_operator_p (kind) == TRUE)
+ {
+ set_syntax_error (_("extending shift is not permitted"));
+ return FALSE;
+ }
+ break;
+
+ case SHIFTED_ARITH_IMM:
+ if (kind == AARCH64_MOD_ROR)
+ {
+ set_syntax_error (_("'ROR' shift is not permitted"));
+ return FALSE;
+ }
+ break;
+
+ case SHIFTED_LSL:
+ if (kind != AARCH64_MOD_LSL)
+ {
+ set_syntax_error (_("only 'LSL' shift is permitted"));
+ return FALSE;
+ }
+ break;
+
+ case SHIFTED_REG_OFFSET:
+ if (kind != AARCH64_MOD_UXTW && kind != AARCH64_MOD_LSL
+ && kind != AARCH64_MOD_SXTW && kind != AARCH64_MOD_SXTX)
+ {
+ set_fatal_syntax_error
+ (_("invalid shift for the register offset addressing mode"));
+ return FALSE;
+ }
+ break;
+
+ case SHIFTED_LSL_MSL:
+ if (kind != AARCH64_MOD_LSL && kind != AARCH64_MOD_MSL)
+ {
+ set_syntax_error (_("invalid shift operator"));
+ return FALSE;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Whitespace can appear here if the next thing is a bare digit. */
+ skip_whitespace (p);
+
+ /* Parse shift amount. */
+ exp_has_prefix = 0;
+ if (mode == SHIFTED_REG_OFFSET && *p == ']')
+ exp.X_op = O_absent;
+ else
+ {
+ if (is_immediate_prefix (*p))
+ {
+ p++;
+ exp_has_prefix = 1;
+ }
+ my_get_expression (&exp, &p, GE_NO_PREFIX, 0);
+ }
+ if (exp.X_op == O_absent)
+ {
+ if (aarch64_extend_operator_p (kind) == FALSE || exp_has_prefix)
+ {
+ set_syntax_error (_("missing shift amount"));
+ return FALSE;
+ }
+ operand->shifter.amount = 0;
+ }
+ else if (exp.X_op != O_constant)
+ {
+ set_syntax_error (_("constant shift amount required"));
+ return FALSE;
+ }
+ else if (exp.X_add_number < 0 || exp.X_add_number > 63)
+ {
+ set_fatal_syntax_error (_("shift amount out of range 0 to 63"));
+ return FALSE;
+ }
+ else
+ {
+ operand->shifter.amount = exp.X_add_number;
+ operand->shifter.amount_present = 1;
+ }
+
+ operand->shifter.operator_present = 1;
+ operand->shifter.kind = kind;
+
+ *str = p;
+ return TRUE;
+}
+
+/* Parse a <shifter_operand> for a data processing instruction:
+
+ #<immediate>
+ #<immediate>, LSL #imm
+
+ Validation of immediate operands is deferred to md_apply_fix.
+
+ Return TRUE on success; otherwise return FALSE. */
+
+static bfd_boolean
+parse_shifter_operand_imm (char **str, aarch64_opnd_info *operand,
+ enum parse_shift_mode mode)
+{
+ char *p;
+
+ if (mode != SHIFTED_ARITH_IMM && mode != SHIFTED_LOGIC_IMM)
+ return FALSE;
+
+ p = *str;
+
+ /* Accept an immediate expression. */
+ if (! my_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX, 1))
+ return FALSE;
+
+ /* Accept optional LSL for arithmetic immediate values. */
+ if (mode == SHIFTED_ARITH_IMM && skip_past_comma (&p))
+ if (! parse_shift (&p, operand, SHIFTED_LSL))
+ return FALSE;
+
+ /* Not accept any shifter for logical immediate values. */
+ if (mode == SHIFTED_LOGIC_IMM && skip_past_comma (&p)
+ && parse_shift (&p, operand, mode))
+ {
+ set_syntax_error (_("unexpected shift operator"));
+ return FALSE;
+ }
+
+ *str = p;
+ return TRUE;
+}
+
+/* Parse a <shifter_operand> for a data processing instruction:
+
+ <Rm>
+ <Rm>, <shift>
+ #<immediate>
+ #<immediate>, LSL #imm
+
+ where <shift> is handled by parse_shift above, and the last two
+ cases are handled by the function above.
+
+ Validation of immediate operands is deferred to md_apply_fix.
+
+ Return TRUE on success; otherwise return FALSE. */
+
+static bfd_boolean
+parse_shifter_operand (char **str, aarch64_opnd_info *operand,
+ enum parse_shift_mode mode)
+{
+ int reg;
+ int isreg32, isregzero;
+ enum aarch64_operand_class opd_class
+ = aarch64_get_operand_class (operand->type);
+
+ if ((reg =
+ aarch64_reg_parse_32_64 (str, 0, 0, &isreg32, &isregzero)) != PARSE_FAIL)
+ {
+ if (opd_class == AARCH64_OPND_CLASS_IMMEDIATE)
+ {
+ set_syntax_error (_("unexpected register in the immediate operand"));
+ return FALSE;
+ }
+
+ if (!isregzero && reg == REG_SP)
+ {
+ set_syntax_error (BAD_SP);
+ return FALSE;
+ }
+
+ operand->reg.regno = reg;
+ operand->qualifier = isreg32 ? AARCH64_OPND_QLF_W : AARCH64_OPND_QLF_X;
+
+ /* Accept optional shift operation on register. */
+ if (! skip_past_comma (str))
+ return TRUE;
+
+ if (! parse_shift (str, operand, mode))
+ return FALSE;
+
+ return TRUE;
+ }
+ else if (opd_class == AARCH64_OPND_CLASS_MODIFIED_REG)
+ {
+ set_syntax_error
+ (_("integer register expected in the extended/shifted operand "
+ "register"));
+ return FALSE;
+ }
+
+ /* We have a shifted immediate variable. */
+ return parse_shifter_operand_imm (str, operand, mode);
+}
+
+/* Return TRUE on success; return FALSE otherwise. */
+
+static bfd_boolean
+parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand,
+ enum parse_shift_mode mode)
+{
+ char *p = *str;
+
+ /* Determine if we have the sequence of characters #: or just :
+ coming next. If we do, then we check for a :rello: relocation
+ modifier. If we don't, punt the whole lot to
+ parse_shifter_operand. */
+
+ if ((p[0] == '#' && p[1] == ':') || p[0] == ':')
+ {
+ struct reloc_table_entry *entry;
+
+ if (p[0] == '#')
+ p += 2;
+ else
+ p++;
+ *str = p;
+
+ /* Try to parse a relocation. Anything else is an error. */
+ if (!(entry = find_reloc_table_entry (str)))
+ {
+ set_syntax_error (_("unknown relocation modifier"));
+ return FALSE;
+ }
+
+ if (entry->add_type == 0)
+ {
+ set_syntax_error
+ (_("this relocation modifier is not allowed on this instruction"));
+ return FALSE;
+ }
+
+ /* Save str before we decompose it. */
+ p = *str;
+
+ /* Next, we parse the expression. */
+ if (! my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX, 1))
+ return FALSE;
+
+ /* Record the relocation type (use the ADD variant here). */
+ inst.reloc.type = entry->add_type;
+ inst.reloc.pc_rel = entry->pc_rel;
+
+ /* If str is empty, we've reached the end, stop here. */
+ if (**str == '\0')
+ return TRUE;
+
+ /* Otherwise, we have a shifted reloc modifier, so rewind to
+ recover the variable name and continue parsing for the shifter. */
+ *str = p;
+ return parse_shifter_operand_imm (str, operand, mode);
+ }
+
+ return parse_shifter_operand (str, operand, mode);
+}
+
+/* Parse all forms of an address expression. Information is written
+ to *OPERAND and/or inst.reloc.
+
+ The A64 instruction set has the following addressing modes:
+
+ Offset
+ [base] // in SIMD ld/st structure
+ [base{,#0}] // in ld/st exclusive
+ [base{,#imm}]
+ [base,Xm{,LSL #imm}]
+ [base,Xm,SXTX {#imm}]
+ [base,Wm,(S|U)XTW {#imm}]
+ Pre-indexed
+ [base,#imm]!
+ Post-indexed
+ [base],#imm
+ [base],Xm // in SIMD ld/st structure
+ PC-relative (literal)
+ label
+ =immediate
+
+ (As a convenience, the notation "=immediate" is permitted in conjunction
+ with the pc-relative literal load instructions to automatically place an
+ immediate value or symbolic address in a nearby literal pool and generate
+ a hidden label which references it.)
+
+ Upon a successful parsing, the address structure in *OPERAND will be
+ filled in the following way:
+
+ .base_regno = <base>
+ .offset.is_reg // 1 if the offset is a register
+ .offset.imm = <imm>
+ .offset.regno = <Rm>
+
+ For different addressing modes defined in the A64 ISA:
+
+ Offset
+ .pcrel=0; .preind=1; .postind=0; .writeback=0
+ Pre-indexed
+ .pcrel=0; .preind=1; .postind=0; .writeback=1
+ Post-indexed
+ .pcrel=0; .preind=0; .postind=1; .writeback=1
+ PC-relative (literal)
+ .pcrel=1; .preind=1; .postind=0; .writeback=0
+
+ The shift/extension information, if any, will be stored in .shifter.
+
+ It is the caller's responsibility to check for addressing modes not
+ supported by the instruction, and to set inst.reloc.type. */
+
+static bfd_boolean
+parse_address_main (char **str, aarch64_opnd_info *operand, int reloc,
+ int accept_reg_post_index)
+{
+ char *p = *str;
+ int reg;
+ int isreg32, isregzero;
+ expressionS *exp = &inst.reloc.exp;
+
+ if (! skip_past_char (&p, '['))
+ {
+ /* =immediate or label. */
+ operand->addr.pcrel = 1;
+ operand->addr.preind = 1;
+
+ /* #:<reloc_op>:<symbol> */
+ skip_past_char (&p, '#');
+ if (reloc && skip_past_char (&p, ':'))
+ {
+ struct reloc_table_entry *entry;
+
+ /* Try to parse a relocation modifier. Anything else is
+ an error. */
+ entry = find_reloc_table_entry (&p);
+ if (! entry)
+ {
+ set_syntax_error (_("unknown relocation modifier"));
+ return FALSE;
+ }
+
+ if (entry->ldst_type == 0)
+ {
+ set_syntax_error
+ (_("this relocation modifier is not allowed on this "
+ "instruction"));
+ return FALSE;
+ }
+
+ /* #:<reloc_op>: */
+ if (! my_get_expression (exp, &p, GE_NO_PREFIX, 1))
+ {
+ set_syntax_error (_("invalid relocation expression"));
+ return FALSE;
+ }
+
+ /* #:<reloc_op>:<expr> */
+ /* Record the load/store relocation type. */
+ inst.reloc.type = entry->ldst_type;
+ inst.reloc.pc_rel = entry->pc_rel;
+ }
+ else
+ {
+
+ if (skip_past_char (&p, '='))
+ /* =immediate; need to generate the literal in the literal pool. */
+ inst.gen_lit_pool = 1;
+
+ if (!my_get_expression (exp, &p, GE_NO_PREFIX, 1))
+ {
+ set_syntax_error (_("invalid address"));
+ return FALSE;
+ }
+ }
+
+ *str = p;
+ return TRUE;
+ }
+
+ /* [ */
+
+ /* Accept SP and reject ZR */
+ reg = aarch64_reg_parse_32_64 (&p, 0, 1, &isreg32, &isregzero);
+ if (reg == PARSE_FAIL || isreg32)
+ {
+ set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64)));
+ return FALSE;
+ }
+ operand->addr.base_regno = reg;
+
+ /* [Xn */
+ if (skip_past_comma (&p))
+ {
+ /* [Xn, */
+ operand->addr.preind = 1;
+
+ /* Reject SP and accept ZR */
+ reg = aarch64_reg_parse_32_64 (&p, 1, 0, &isreg32, &isregzero);
+ if (reg != PARSE_FAIL)
+ {
+ /* [Xn,Rm */
+ operand->addr.offset.regno = reg;
+ operand->addr.offset.is_reg = 1;
+ /* Shifted index. */
+ if (skip_past_comma (&p))
+ {
+ /* [Xn,Rm, */
+ if (! parse_shift (&p, operand, SHIFTED_REG_OFFSET))
+ /* Use the diagnostics set in parse_shift, so not set new
+ error message here. */
+ return FALSE;
+ }
+ /* We only accept:
+ [base,Xm{,LSL #imm}]
+ [base,Xm,SXTX {#imm}]
+ [base,Wm,(S|U)XTW {#imm}] */
+ if (operand->shifter.kind == AARCH64_MOD_NONE
+ || operand->shifter.kind == AARCH64_MOD_LSL
+ || operand->shifter.kind == AARCH64_MOD_SXTX)
+ {
+ if (isreg32)
+ {
+ set_syntax_error (_("invalid use of 32-bit register offset"));
+ return FALSE;
+ }
+ }
+ else if (!isreg32)
+ {
+ set_syntax_error (_("invalid use of 64-bit register offset"));
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* [Xn,#:<reloc_op>:<symbol> */
+ skip_past_char (&p, '#');
+ if (reloc && skip_past_char (&p, ':'))
+ {
+ struct reloc_table_entry *entry;
+
+ /* Try to parse a relocation modifier. Anything else is
+ an error. */
+ if (!(entry = find_reloc_table_entry (&p)))
+ {
+ set_syntax_error (_("unknown relocation modifier"));
+ return FALSE;
+ }
+
+ if (entry->ldst_type == 0)
+ {
+ set_syntax_error
+ (_("this relocation modifier is not allowed on this "
+ "instruction"));
+ return FALSE;
+ }
+
+ /* [Xn,#:<reloc_op>: */
+ /* We now have the group relocation table entry corresponding to
+ the name in the assembler source. Next, we parse the
+ expression. */
+ if (! my_get_expression (exp, &p, GE_NO_PREFIX, 1))
+ {
+ set_syntax_error (_("invalid relocation expression"));
+ return FALSE;
+ }
+
+ /* [Xn,#:<reloc_op>:<expr> */
+ /* Record the load/store relocation type. */
+ inst.reloc.type = entry->ldst_type;
+ inst.reloc.pc_rel = entry->pc_rel;
+ }
+ else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1))
+ {
+ set_syntax_error (_("invalid expression in the address"));
+ return FALSE;
+ }
+ /* [Xn,<expr> */
+ }
+ }
+
+ if (! skip_past_char (&p, ']'))
+ {
+ set_syntax_error (_("']' expected"));
+ return FALSE;
+ }
+
+ if (skip_past_char (&p, '!'))
+ {
+ if (operand->addr.preind && operand->addr.offset.is_reg)
+ {
+ set_syntax_error (_("register offset not allowed in pre-indexed "
+ "addressing mode"));
+ return FALSE;
+ }
+ /* [Xn]! */
+ operand->addr.writeback = 1;
+ }
+ else if (skip_past_comma (&p))
+ {
+ /* [Xn], */
+ operand->addr.postind = 1;
+ operand->addr.writeback = 1;
+
+ if (operand->addr.preind)
+ {
+ set_syntax_error (_("cannot combine pre- and post-indexing"));
+ return FALSE;
+ }
+
+ if (accept_reg_post_index
+ && (reg = aarch64_reg_parse_32_64 (&p, 1, 1, &isreg32,
+ &isregzero)) != PARSE_FAIL)
+ {
+ /* [Xn],Xm */
+ if (isreg32)
+ {
+ set_syntax_error (_("invalid 32-bit register offset"));
+ return FALSE;
+ }
+ operand->addr.offset.regno = reg;
+ operand->addr.offset.is_reg = 1;
+ }
+ else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1))
+ {
+ /* [Xn],#expr */
+ set_syntax_error (_("invalid expression in the address"));
+ return FALSE;
+ }
+ }
+
+ /* If at this point neither .preind nor .postind is set, we have a
+ bare [Rn]{!}; reject [Rn]! but accept [Rn] as a shorthand for [Rn,#0]. */
+ if (operand->addr.preind == 0 && operand->addr.postind == 0)
+ {
+ if (operand->addr.writeback)
+ {
+ /* Reject [Rn]! */
+ set_syntax_error (_("missing offset in the pre-indexed address"));
+ return FALSE;
+ }
+ operand->addr.preind = 1;
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
+ }
+
+ *str = p;
+ return TRUE;
+}
+
+/* Return TRUE on success; otherwise return FALSE. */
+static bfd_boolean
+parse_address (char **str, aarch64_opnd_info *operand,
+ int accept_reg_post_index)
+{
+ return parse_address_main (str, operand, 0, accept_reg_post_index);
+}
+
+/* Return TRUE on success; otherwise return FALSE. */
+static bfd_boolean
+parse_address_reloc (char **str, aarch64_opnd_info *operand)
+{
+ return parse_address_main (str, operand, 1, 0);
+}
+
+/* Parse an operand for a MOVZ, MOVN or MOVK instruction.
+ Return TRUE on success; otherwise return FALSE. */
+static bfd_boolean
+parse_half (char **str, int *internal_fixup_p)
+{
+ char *p, *saved;
+ int dummy;
+
+ p = *str;
+ skip_past_char (&p, '#');
+
+ gas_assert (internal_fixup_p);
+ *internal_fixup_p = 0;
+
+ if (*p == ':')
+ {
+ struct reloc_table_entry *entry;
+
+ /* Try to parse a relocation. Anything else is an error. */
+ ++p;
+ if (!(entry = find_reloc_table_entry (&p)))
+ {
+ set_syntax_error (_("unknown relocation modifier"));
+ return FALSE;
+ }
+
+ if (entry->movw_type == 0)
+ {
+ set_syntax_error
+ (_("this relocation modifier is not allowed on this instruction"));
+ return FALSE;
+ }
+
+ inst.reloc.type = entry->movw_type;
+ }
+ else
+ *internal_fixup_p = 1;
+
+ /* Avoid parsing a register as a general symbol. */
+ saved = p;
+ if (aarch64_reg_parse_32_64 (&p, 0, 0, &dummy, &dummy) != PARSE_FAIL)
+ return FALSE;
+ p = saved;
+
+ if (! my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, 1))
+ return FALSE;
+
+ *str = p;
+ return TRUE;
+}
+
+/* Parse an operand for an ADRP instruction:
+ ADRP <Xd>, <label>
+ Return TRUE on success; otherwise return FALSE. */
+
+static bfd_boolean
+parse_adrp (char **str)
+{
+ char *p;
+
+ p = *str;
+ if (*p == ':')
+ {
+ struct reloc_table_entry *entry;
+
+ /* Try to parse a relocation. Anything else is an error. */
+ ++p;
+ if (!(entry = find_reloc_table_entry (&p)))
+ {
+ set_syntax_error (_("unknown relocation modifier"));
+ return FALSE;
+ }
+
+ if (entry->adrp_type == 0)
+ {
+ set_syntax_error
+ (_("this relocation modifier is not allowed on this instruction"));
+ return FALSE;
+ }
+
+ inst.reloc.type = entry->adrp_type;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_AARCH64_ADR_HI21_PCREL;
+
+ inst.reloc.pc_rel = 1;
+
+ if (! my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, 1))
+ return FALSE;
+
+ *str = p;
+ return TRUE;
+}
+
+/* Miscellaneous. */
+
+/* Parse an option for a preload instruction. Returns the encoding for the
+ option, or PARSE_FAIL. */
+
+static int
+parse_pldop (char **str)
+{
+ char *p, *q;
+ const struct aarch64_name_value_pair *o;
+
+ p = q = *str;
+ while (ISALNUM (*q))
+ q++;
+
+ o = hash_find_n (aarch64_pldop_hsh, p, q - p);
+ if (!o)
+ return PARSE_FAIL;
+
+ *str = q;
+ return o->value;
+}
+
+/* Parse an option for a barrier instruction. Returns the encoding for the
+ option, or PARSE_FAIL. */
+
+static int
+parse_barrier (char **str)
+{
+ char *p, *q;
+ const asm_barrier_opt *o;
+
+ p = q = *str;
+ while (ISALPHA (*q))
+ q++;
+
+ o = hash_find_n (aarch64_barrier_opt_hsh, p, q - p);
+ if (!o)
+ return PARSE_FAIL;
+
+ *str = q;
+ return o->value;
+}
+
+/* Parse a system register or a PSTATE field name for an MSR/MRS instruction.
+ Returns the encoding for the option, or PARSE_FAIL.
+
+ If IMPLE_DEFINED_P is non-zero, the function will also try to parse the
+ implementation defined system register name S<op0>_<op1>_<Cn>_<Cm>_<op2>. */
+
+static int
+parse_sys_reg (char **str, struct hash_control *sys_regs, int imple_defined_p)
+{
+ char *p, *q;
+ char buf[32];
+ const aarch64_sys_reg *o;
+ int value;
+
+ p = buf;
+ for (q = *str; ISALNUM (*q) || *q == '_'; q++)
+ if (p < buf + 31)
+ *p++ = TOLOWER (*q);
+ *p = '\0';
+ /* Assert that BUF be large enough. */
+ gas_assert (p - buf == q - *str);
+
+ o = hash_find (sys_regs, buf);
+ if (!o)
+ {
+ if (!imple_defined_p)
+ return PARSE_FAIL;
+ else
+ {
+ /* Parse S<op0>_<op1>_<Cn>_<Cm>_<op2>. */
+ unsigned int op0, op1, cn, cm, op2;
+
+ if (sscanf (buf, "s%u_%u_c%u_c%u_%u", &op0, &op1, &cn, &cm, &op2)
+ != 5)
+ return PARSE_FAIL;
+ if (op0 > 3 || op1 > 7 || cn > 15 || cm > 15 || op2 > 7)
+ return PARSE_FAIL;
+ value = (op0 << 14) | (op1 << 11) | (cn << 7) | (cm << 3) | op2;
+ }
+ }
+ else
+ {
+ if (aarch64_sys_reg_deprecated_p (o))
+ as_warn (_("system register name '%s' is deprecated and may be "
+"removed in a future release"), buf);
+ value = o->value;
+ }
+
+ *str = q;
+ return value;
+}
+
+/* Parse a system reg for ic/dc/at/tlbi instructions. Returns the table entry
+ for the option, or NULL. */
+
+static const aarch64_sys_ins_reg *
+parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs)
+{
+ char *p, *q;
+ char buf[32];
+ const aarch64_sys_ins_reg *o;
+
+ p = buf;
+ for (q = *str; ISALNUM (*q) || *q == '_'; q++)
+ if (p < buf + 31)
+ *p++ = TOLOWER (*q);
+ *p = '\0';
+
+ o = hash_find (sys_ins_regs, buf);
+ if (!o)
+ return NULL;
+
+ *str = q;
+ return o;
+}
+
+#define po_char_or_fail(chr) do { \
+ if (! skip_past_char (&str, chr)) \
+ goto failure; \
+} while (0)
+
+#define po_reg_or_fail(regtype) do { \
+ val = aarch64_reg_parse (&str, regtype, &rtype, NULL); \
+ if (val == PARSE_FAIL) \
+ { \
+ set_default_error (); \
+ goto failure; \
+ } \
+ } while (0)
+
+#define po_int_reg_or_fail(reject_sp, reject_rz) do { \
+ val = aarch64_reg_parse_32_64 (&str, reject_sp, reject_rz, \
+ &isreg32, &isregzero); \
+ if (val == PARSE_FAIL) \
+ { \
+ set_default_error (); \
+ goto failure; \
+ } \
+ info->reg.regno = val; \
+ if (isreg32) \
+ info->qualifier = AARCH64_OPND_QLF_W; \
+ else \
+ info->qualifier = AARCH64_OPND_QLF_X; \
+ } while (0)
+
+#define po_imm_nc_or_fail() do { \
+ if (! parse_constant_immediate (&str, &val)) \
+ goto failure; \
+ } while (0)
+
+#define po_imm_or_fail(min, max) do { \
+ if (! parse_constant_immediate (&str, &val)) \
+ goto failure; \
+ if (val < min || val > max) \
+ { \
+ set_fatal_syntax_error (_("immediate value out of range "\
+#min " to "#max)); \
+ goto failure; \
+ } \
+ } while (0)
+
+#define po_misc_or_fail(expr) do { \
+ if (!expr) \
+ goto failure; \
+ } while (0)
+
+/* encode the 12-bit imm field of Add/sub immediate */
+static inline uint32_t
+encode_addsub_imm (uint32_t imm)
+{
+ return imm << 10;
+}
+
+/* encode the shift amount field of Add/sub immediate */
+static inline uint32_t
+encode_addsub_imm_shift_amount (uint32_t cnt)
+{
+ return cnt << 22;
+}
+
+
+/* encode the imm field of Adr instruction */
+static inline uint32_t
+encode_adr_imm (uint32_t imm)
+{
+ return (((imm & 0x3) << 29) /* [1:0] -> [30:29] */
+ | ((imm & (0x7ffff << 2)) << 3)); /* [20:2] -> [23:5] */
+}
+
+/* encode the immediate field of Move wide immediate */
+static inline uint32_t
+encode_movw_imm (uint32_t imm)
+{
+ return imm << 5;
+}
+
+/* encode the 26-bit offset of unconditional branch */
+static inline uint32_t
+encode_branch_ofs_26 (uint32_t ofs)
+{
+ return ofs & ((1 << 26) - 1);
+}
+
+/* encode the 19-bit offset of conditional branch and compare & branch */
+static inline uint32_t
+encode_cond_branch_ofs_19 (uint32_t ofs)
+{
+ return (ofs & ((1 << 19) - 1)) << 5;
+}
+
+/* encode the 19-bit offset of ld literal */
+static inline uint32_t
+encode_ld_lit_ofs_19 (uint32_t ofs)
+{
+ return (ofs & ((1 << 19) - 1)) << 5;
+}
+
+/* Encode the 14-bit offset of test & branch. */
+static inline uint32_t
+encode_tst_branch_ofs_14 (uint32_t ofs)
+{
+ return (ofs & ((1 << 14) - 1)) << 5;
+}
+
+/* Encode the 16-bit imm field of svc/hvc/smc. */
+static inline uint32_t
+encode_svc_imm (uint32_t imm)
+{
+ return imm << 5;
+}
+
+/* Reencode add(s) to sub(s), or sub(s) to add(s). */
+static inline uint32_t
+reencode_addsub_switch_add_sub (uint32_t opcode)
+{
+ return opcode ^ (1 << 30);
+}
+
+static inline uint32_t
+reencode_movzn_to_movz (uint32_t opcode)
+{
+ return opcode | (1 << 30);
+}
+
+static inline uint32_t
+reencode_movzn_to_movn (uint32_t opcode)
+{
+ return opcode & ~(1 << 30);
+}
+
+/* Overall per-instruction processing. */
+
+/* We need to be able to fix up arbitrary expressions in some statements.
+ This is so that we can handle symbols that are an arbitrary distance from
+ the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+ which returns part of an address in a form which will be valid for
+ a data instruction. We do this by pushing the expression into a symbol
+ in the expr_section, and creating a fix for that. */
+
+static fixS *
+fix_new_aarch64 (fragS * frag,
+ int where,
+ short int size, expressionS * exp, int pc_rel, int reloc)
+{
+ fixS *new_fix;
+
+ switch (exp->X_op)
+ {
+ case O_constant:
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+ break;
+
+ default:
+ new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
+ pc_rel, reloc);
+ break;
+ }
+ return new_fix;
+}
+
+/* Diagnostics on operands errors. */
+
+/* By default, output verbose error message.
+ Disable the verbose error message by -mno-verbose-error. */
+static int verbose_error_p = 1;
+
+#ifdef DEBUG_AARCH64
+/* N.B. this is only for the purpose of debugging. */
+const char* operand_mismatch_kind_names[] =
+{
+ "AARCH64_OPDE_NIL",
+ "AARCH64_OPDE_RECOVERABLE",
+ "AARCH64_OPDE_SYNTAX_ERROR",
+ "AARCH64_OPDE_FATAL_SYNTAX_ERROR",
+ "AARCH64_OPDE_INVALID_VARIANT",
+ "AARCH64_OPDE_OUT_OF_RANGE",
+ "AARCH64_OPDE_UNALIGNED",
+ "AARCH64_OPDE_REG_LIST",
+ "AARCH64_OPDE_OTHER_ERROR",
+};
+#endif /* DEBUG_AARCH64 */
+
+/* Return TRUE if LHS is of higher severity than RHS, otherwise return FALSE.
+
+ When multiple errors of different kinds are found in the same assembly
+ line, only the error of the highest severity will be picked up for
+ issuing the diagnostics. */
+
+static inline bfd_boolean
+operand_error_higher_severity_p (enum aarch64_operand_error_kind lhs,
+ enum aarch64_operand_error_kind rhs)
+{
+ gas_assert (AARCH64_OPDE_RECOVERABLE > AARCH64_OPDE_NIL);
+ gas_assert (AARCH64_OPDE_SYNTAX_ERROR > AARCH64_OPDE_RECOVERABLE);
+ gas_assert (AARCH64_OPDE_FATAL_SYNTAX_ERROR > AARCH64_OPDE_SYNTAX_ERROR);
+ gas_assert (AARCH64_OPDE_INVALID_VARIANT > AARCH64_OPDE_FATAL_SYNTAX_ERROR);
+ gas_assert (AARCH64_OPDE_OUT_OF_RANGE > AARCH64_OPDE_INVALID_VARIANT);
+ gas_assert (AARCH64_OPDE_UNALIGNED > AARCH64_OPDE_OUT_OF_RANGE);
+ gas_assert (AARCH64_OPDE_REG_LIST > AARCH64_OPDE_UNALIGNED);
+ gas_assert (AARCH64_OPDE_OTHER_ERROR > AARCH64_OPDE_REG_LIST);
+ return lhs > rhs;
+}
+
+/* Helper routine to get the mnemonic name from the assembly instruction
+ line; should only be called for the diagnosis purpose, as there is
+ string copy operation involved, which may affect the runtime
+ performance if used in elsewhere. */
+
+static const char*
+get_mnemonic_name (const char *str)
+{
+ static char mnemonic[32];
+ char *ptr;
+
+ /* Get the first 15 bytes and assume that the full name is included. */
+ strncpy (mnemonic, str, 31);
+ mnemonic[31] = '\0';
+
+ /* Scan up to the end of the mnemonic, which must end in white space,
+ '.', or end of string. */
+ for (ptr = mnemonic; is_part_of_name(*ptr); ++ptr)
+ ;
+
+ *ptr = '\0';
+
+ /* Append '...' to the truncated long name. */
+ if (ptr - mnemonic == 31)
+ mnemonic[28] = mnemonic[29] = mnemonic[30] = '.';
+
+ return mnemonic;
+}
+
+static void
+reset_aarch64_instruction (aarch64_instruction *instruction)
+{
+ memset (instruction, '\0', sizeof (aarch64_instruction));
+ instruction->reloc.type = BFD_RELOC_UNUSED;
+}
+
+/* Data strutures storing one user error in the assembly code related to
+ operands. */
+
+struct operand_error_record
+{
+ const aarch64_opcode *opcode;
+ aarch64_operand_error detail;
+ struct operand_error_record *next;
+};
+
+typedef struct operand_error_record operand_error_record;
+
+struct operand_errors
+{
+ operand_error_record *head;
+ operand_error_record *tail;
+};
+
+typedef struct operand_errors operand_errors;
+
+/* Top-level data structure reporting user errors for the current line of
+ the assembly code.
+ The way md_assemble works is that all opcodes sharing the same mnemonic
+ name are iterated to find a match to the assembly line. In this data
+ structure, each of the such opcodes will have one operand_error_record
+ allocated and inserted. In other words, excessive errors related with
+ a single opcode are disregarded. */
+operand_errors operand_error_report;
+
+/* Free record nodes. */
+static operand_error_record *free_opnd_error_record_nodes = NULL;
+
+/* Initialize the data structure that stores the operand mismatch
+ information on assembling one line of the assembly code. */
+static void
+init_operand_error_report (void)
+{
+ if (operand_error_report.head != NULL)
+ {
+ gas_assert (operand_error_report.tail != NULL);
+ operand_error_report.tail->next = free_opnd_error_record_nodes;
+ free_opnd_error_record_nodes = operand_error_report.head;
+ operand_error_report.head = NULL;
+ operand_error_report.tail = NULL;
+ return;
+ }
+ gas_assert (operand_error_report.tail == NULL);
+}
+
+/* Return TRUE if some operand error has been recorded during the
+ parsing of the current assembly line using the opcode *OPCODE;
+ otherwise return FALSE. */
+static inline bfd_boolean
+opcode_has_operand_error_p (const aarch64_opcode *opcode)
+{
+ operand_error_record *record = operand_error_report.head;
+ return record && record->opcode == opcode;
+}
+
+/* Add the error record *NEW_RECORD to operand_error_report. The record's
+ OPCODE field is initialized with OPCODE.
+ N.B. only one record for each opcode, i.e. the maximum of one error is
+ recorded for each instruction template. */
+
+static void
+add_operand_error_record (const operand_error_record* new_record)
+{
+ const aarch64_opcode *opcode = new_record->opcode;
+ operand_error_record* record = operand_error_report.head;
+
+ /* The record may have been created for this opcode. If not, we need
+ to prepare one. */
+ if (! opcode_has_operand_error_p (opcode))
+ {
+ /* Get one empty record. */
+ if (free_opnd_error_record_nodes == NULL)
+ {
+ record = xmalloc (sizeof (operand_error_record));
+ if (record == NULL)
+ abort ();
+ }
+ else
+ {
+ record = free_opnd_error_record_nodes;
+ free_opnd_error_record_nodes = record->next;
+ }
+ record->opcode = opcode;
+ /* Insert at the head. */
+ record->next = operand_error_report.head;
+ operand_error_report.head = record;
+ if (operand_error_report.tail == NULL)
+ operand_error_report.tail = record;
+ }
+ else if (record->detail.kind != AARCH64_OPDE_NIL
+ && record->detail.index <= new_record->detail.index
+ && operand_error_higher_severity_p (record->detail.kind,
+ new_record->detail.kind))
+ {
+ /* In the case of multiple errors found on operands related with a
+ single opcode, only record the error of the leftmost operand and
+ only if the error is of higher severity. */
+ DEBUG_TRACE ("error %s on operand %d not added to the report due to"
+ " the existing error %s on operand %d",
+ operand_mismatch_kind_names[new_record->detail.kind],
+ new_record->detail.index,
+ operand_mismatch_kind_names[record->detail.kind],
+ record->detail.index);
+ return;
+ }
+
+ record->detail = new_record->detail;
+}
+
+static inline void
+record_operand_error_info (const aarch64_opcode *opcode,
+ aarch64_operand_error *error_info)
+{
+ operand_error_record record;
+ record.opcode = opcode;
+ record.detail = *error_info;
+ add_operand_error_record (&record);
+}
+
+/* Record an error of kind KIND and, if ERROR is not NULL, of the detailed
+ error message *ERROR, for operand IDX (count from 0). */
+
+static void
+record_operand_error (const aarch64_opcode *opcode, int idx,
+ enum aarch64_operand_error_kind kind,
+ const char* error)
+{
+ aarch64_operand_error info;
+ memset(&info, 0, sizeof (info));
+ info.index = idx;
+ info.kind = kind;
+ info.error = error;
+ record_operand_error_info (opcode, &info);
+}
+
+static void
+record_operand_error_with_data (const aarch64_opcode *opcode, int idx,
+ enum aarch64_operand_error_kind kind,
+ const char* error, const int *extra_data)
+{
+ aarch64_operand_error info;
+ info.index = idx;
+ info.kind = kind;
+ info.error = error;
+ info.data[0] = extra_data[0];
+ info.data[1] = extra_data[1];
+ info.data[2] = extra_data[2];
+ record_operand_error_info (opcode, &info);
+}
+
+static void
+record_operand_out_of_range_error (const aarch64_opcode *opcode, int idx,
+ const char* error, int lower_bound,
+ int upper_bound)
+{
+ int data[3] = {lower_bound, upper_bound, 0};
+ record_operand_error_with_data (opcode, idx, AARCH64_OPDE_OUT_OF_RANGE,
+ error, data);
+}
+
+/* Remove the operand error record for *OPCODE. */
+static void ATTRIBUTE_UNUSED
+remove_operand_error_record (const aarch64_opcode *opcode)
+{
+ if (opcode_has_operand_error_p (opcode))
+ {
+ operand_error_record* record = operand_error_report.head;
+ gas_assert (record != NULL && operand_error_report.tail != NULL);
+ operand_error_report.head = record->next;
+ record->next = free_opnd_error_record_nodes;
+ free_opnd_error_record_nodes = record;
+ if (operand_error_report.head == NULL)
+ {
+ gas_assert (operand_error_report.tail == record);
+ operand_error_report.tail = NULL;
+ }
+ }
+}
+
+/* Given the instruction in *INSTR, return the index of the best matched
+ qualifier sequence in the list (an array) headed by QUALIFIERS_LIST.
+
+ Return -1 if there is no qualifier sequence; return the first match
+ if there is multiple matches found. */
+
+static int
+find_best_match (const aarch64_inst *instr,
+ const aarch64_opnd_qualifier_seq_t *qualifiers_list)
+{
+ int i, num_opnds, max_num_matched, idx;
+
+ num_opnds = aarch64_num_of_operands (instr->opcode);
+ if (num_opnds == 0)
+ {
+ DEBUG_TRACE ("no operand");
+ return -1;
+ }
+
+ max_num_matched = 0;
+ idx = -1;
+
+ /* For each pattern. */
+ for (i = 0; i < AARCH64_MAX_QLF_SEQ_NUM; ++i, ++qualifiers_list)
+ {
+ int j, num_matched;
+ const aarch64_opnd_qualifier_t *qualifiers = *qualifiers_list;
+
+ /* Most opcodes has much fewer patterns in the list. */
+ if (empty_qualifier_sequence_p (qualifiers) == TRUE)
+ {
+ DEBUG_TRACE_IF (i == 0, "empty list of qualifier sequence");
+ if (i != 0 && idx == -1)
+ /* If nothing has been matched, return the 1st sequence. */
+ idx = 0;
+ break;
+ }
+
+ for (j = 0, num_matched = 0; j < num_opnds; ++j, ++qualifiers)
+ if (*qualifiers == instr->operands[j].qualifier)
+ ++num_matched;
+
+ if (num_matched > max_num_matched)
+ {
+ max_num_matched = num_matched;
+ idx = i;
+ }
+ }
+
+ DEBUG_TRACE ("return with %d", idx);
+ return idx;
+}
+
+/* Assign qualifiers in the qualifier seqence (headed by QUALIFIERS) to the
+ corresponding operands in *INSTR. */
+
+static inline void
+assign_qualifier_sequence (aarch64_inst *instr,
+ const aarch64_opnd_qualifier_t *qualifiers)
+{
+ int i = 0;
+ int num_opnds = aarch64_num_of_operands (instr->opcode);
+ gas_assert (num_opnds);
+ for (i = 0; i < num_opnds; ++i, ++qualifiers)
+ instr->operands[i].qualifier = *qualifiers;
+}
+
+/* Print operands for the diagnosis purpose. */
+
+static void
+print_operands (char *buf, const aarch64_opcode *opcode,
+ const aarch64_opnd_info *opnds)
+{
+ int i;
+
+ for (i = 0; i < AARCH64_MAX_OPND_NUM; ++i)
+ {
+ const size_t size = 128;
+ char str[size];
+
+ /* We regard the opcode operand info more, however we also look into
+ the inst->operands to support the disassembling of the optional
+ operand.
+ The two operand code should be the same in all cases, apart from
+ when the operand can be optional. */
+ if (opcode->operands[i] == AARCH64_OPND_NIL
+ || opnds[i].type == AARCH64_OPND_NIL)
+ break;
+
+ /* Generate the operand string in STR. */
+ aarch64_print_operand (str, size, 0, opcode, opnds, i, NULL, NULL);
+
+ /* Delimiter. */
+ if (str[0] != '\0')
+ strcat (buf, i == 0 ? " " : ",");
+
+ /* Append the operand string. */
+ strcat (buf, str);
+ }
+}
+
+/* Send to stderr a string as information. */
+
+static void
+output_info (const char *format, ...)
+{
+ char *file;
+ unsigned int line;
+ va_list args;
+
+ as_where (&file, &line);
+ if (file)
+ {
+ if (line != 0)
+ fprintf (stderr, "%s:%u: ", file, line);
+ else
+ fprintf (stderr, "%s: ", file);
+ }
+ fprintf (stderr, _("Info: "));
+ va_start (args, format);
+ vfprintf (stderr, format, args);
+ va_end (args);
+ (void) putc ('\n', stderr);
+}
+
+/* Output one operand error record. */
+
+static void
+output_operand_error_record (const operand_error_record *record, char *str)
+{
+ int idx = record->detail.index;
+ const aarch64_opcode *opcode = record->opcode;
+ enum aarch64_opnd opd_code = (idx != -1 ? opcode->operands[idx]
+ : AARCH64_OPND_NIL);
+ const aarch64_operand_error *detail = &record->detail;
+
+ switch (detail->kind)
+ {
+ case AARCH64_OPDE_NIL:
+ gas_assert (0);
+ break;
+
+ case AARCH64_OPDE_SYNTAX_ERROR:
+ case AARCH64_OPDE_RECOVERABLE:
+ case AARCH64_OPDE_FATAL_SYNTAX_ERROR:
+ case AARCH64_OPDE_OTHER_ERROR:
+ gas_assert (idx >= 0);
+ /* Use the prepared error message if there is, otherwise use the
+ operand description string to describe the error. */
+ if (detail->error != NULL)
+ {
+ if (detail->index == -1)
+ as_bad (_("%s -- `%s'"), detail->error, str);
+ else
+ as_bad (_("%s at operand %d -- `%s'"),
+ detail->error, detail->index + 1, str);
+ }
+ else
+ as_bad (_("operand %d should be %s -- `%s'"), idx + 1,
+ aarch64_get_operand_desc (opd_code), str);
+ break;
+
+ case AARCH64_OPDE_INVALID_VARIANT:
+ as_bad (_("operand mismatch -- `%s'"), str);
+ if (verbose_error_p)
+ {
+ /* We will try to correct the erroneous instruction and also provide
+ more information e.g. all other valid variants.
+
+ The string representation of the corrected instruction and other
+ valid variants are generated by
+
+ 1) obtaining the intermediate representation of the erroneous
+ instruction;
+ 2) manipulating the IR, e.g. replacing the operand qualifier;
+ 3) printing out the instruction by calling the printer functions
+ shared with the disassembler.
+
+ The limitation of this method is that the exact input assembly
+ line cannot be accurately reproduced in some cases, for example an
+ optional operand present in the actual assembly line will be
+ omitted in the output; likewise for the optional syntax rules,
+ e.g. the # before the immediate. Another limitation is that the
+ assembly symbols and relocation operations in the assembly line
+ currently cannot be printed out in the error report. Last but not
+ least, when there is other error(s) co-exist with this error, the
+ 'corrected' instruction may be still incorrect, e.g. given
+ 'ldnp h0,h1,[x0,#6]!'
+ this diagnosis will provide the version:
+ 'ldnp s0,s1,[x0,#6]!'
+ which is still not right. */
+ size_t len = strlen (get_mnemonic_name (str));
+ int i, qlf_idx;
+ bfd_boolean result;
+ const size_t size = 2048;
+ char buf[size];
+ aarch64_inst *inst_base = &inst.base;
+ const aarch64_opnd_qualifier_seq_t *qualifiers_list;
+
+ /* Init inst. */
+ reset_aarch64_instruction (&inst);
+ inst_base->opcode = opcode;
+
+ /* Reset the error report so that there is no side effect on the
+ following operand parsing. */
+ init_operand_error_report ();
+
+ /* Fill inst. */
+ result = parse_operands (str + len, opcode)
+ && programmer_friendly_fixup (&inst);
+ gas_assert (result);
+ result = aarch64_opcode_encode (opcode, inst_base, &inst_base->value,
+ NULL, NULL);
+ gas_assert (!result);
+
+ /* Find the most matched qualifier sequence. */
+ qlf_idx = find_best_match (inst_base, opcode->qualifiers_list);
+ gas_assert (qlf_idx > -1);
+
+ /* Assign the qualifiers. */
+ assign_qualifier_sequence (inst_base,
+ opcode->qualifiers_list[qlf_idx]);
+
+ /* Print the hint. */
+ output_info (_(" did you mean this?"));
+ snprintf (buf, size, "\t%s", get_mnemonic_name (str));
+ print_operands (buf, opcode, inst_base->operands);
+ output_info (_(" %s"), buf);
+
+ /* Print out other variant(s) if there is any. */
+ if (qlf_idx != 0 ||
+ !empty_qualifier_sequence_p (opcode->qualifiers_list[1]))
+ output_info (_(" other valid variant(s):"));
+
+ /* For each pattern. */
+ qualifiers_list = opcode->qualifiers_list;
+ for (i = 0; i < AARCH64_MAX_QLF_SEQ_NUM; ++i, ++qualifiers_list)
+ {
+ /* Most opcodes has much fewer patterns in the list.
+ First NIL qualifier indicates the end in the list. */
+ if (empty_qualifier_sequence_p (*qualifiers_list) == TRUE)
+ break;
+
+ if (i != qlf_idx)
+ {
+ /* Mnemonics name. */
+ snprintf (buf, size, "\t%s", get_mnemonic_name (str));
+
+ /* Assign the qualifiers. */
+ assign_qualifier_sequence (inst_base, *qualifiers_list);
+
+ /* Print instruction. */
+ print_operands (buf, opcode, inst_base->operands);
+
+ output_info (_(" %s"), buf);
+ }
+ }
+ }
+ break;
+
+ case AARCH64_OPDE_OUT_OF_RANGE:
+ if (detail->data[0] != detail->data[1])
+ as_bad (_("%s out of range %d to %d at operand %d -- `%s'"),
+ detail->error ? detail->error : _("immediate value"),
+ detail->data[0], detail->data[1], detail->index + 1, str);
+ else
+ as_bad (_("%s expected to be %d at operand %d -- `%s'"),
+ detail->error ? detail->error : _("immediate value"),
+ detail->data[0], detail->index + 1, str);
+ break;
+
+ case AARCH64_OPDE_REG_LIST:
+ if (detail->data[0] == 1)
+ as_bad (_("invalid number of registers in the list; "
+ "only 1 register is expected at operand %d -- `%s'"),
+ detail->index + 1, str);
+ else
+ as_bad (_("invalid number of registers in the list; "
+ "%d registers are expected at operand %d -- `%s'"),
+ detail->data[0], detail->index + 1, str);
+ break;
+
+ case AARCH64_OPDE_UNALIGNED:
+ as_bad (_("immediate value should be a multiple of "
+ "%d at operand %d -- `%s'"),
+ detail->data[0], detail->index + 1, str);
+ break;
+
+ default:
+ gas_assert (0);
+ break;
+ }
+}
+
+/* Process and output the error message about the operand mismatching.
+
+ When this function is called, the operand error information had
+ been collected for an assembly line and there will be multiple
+ errors in the case of mulitple instruction templates; output the
+ error message that most closely describes the problem. */
+
+static void
+output_operand_error_report (char *str)
+{
+ int largest_error_pos;
+ const char *msg = NULL;
+ enum aarch64_operand_error_kind kind;
+ operand_error_record *curr;
+ operand_error_record *head = operand_error_report.head;
+ operand_error_record *record = NULL;
+
+ /* No error to report. */
+ if (head == NULL)
+ return;
+
+ gas_assert (head != NULL && operand_error_report.tail != NULL);
+
+ /* Only one error. */
+ if (head == operand_error_report.tail)
+ {
+ DEBUG_TRACE ("single opcode entry with error kind: %s",
+ operand_mismatch_kind_names[head->detail.kind]);
+ output_operand_error_record (head, str);
+ return;
+ }
+
+ /* Find the error kind of the highest severity. */
+ DEBUG_TRACE ("multiple opcode entres with error kind");
+ kind = AARCH64_OPDE_NIL;
+ for (curr = head; curr != NULL; curr = curr->next)
+ {
+ gas_assert (curr->detail.kind != AARCH64_OPDE_NIL);
+ DEBUG_TRACE ("\t%s", operand_mismatch_kind_names[curr->detail.kind]);
+ if (operand_error_higher_severity_p (curr->detail.kind, kind))
+ kind = curr->detail.kind;
+ }
+ gas_assert (kind != AARCH64_OPDE_NIL);
+
+ /* Pick up one of errors of KIND to report. */
+ largest_error_pos = -2; /* Index can be -1 which means unknown index. */
+ for (curr = head; curr != NULL; curr = curr->next)
+ {
+ if (curr->detail.kind != kind)
+ continue;
+ /* If there are multiple errors, pick up the one with the highest
+ mismatching operand index. In the case of multiple errors with
+ the equally highest operand index, pick up the first one or the
+ first one with non-NULL error message. */
+ if (curr->detail.index > largest_error_pos
+ || (curr->detail.index == largest_error_pos && msg == NULL
+ && curr->detail.error != NULL))
+ {
+ largest_error_pos = curr->detail.index;
+ record = curr;
+ msg = record->detail.error;
+ }
+ }
+
+ gas_assert (largest_error_pos != -2 && record != NULL);
+ DEBUG_TRACE ("Pick up error kind %s to report",
+ operand_mismatch_kind_names[record->detail.kind]);
+
+ /* Output. */
+ output_operand_error_record (record, str);
+}
+
+/* Write an AARCH64 instruction to buf - always little-endian. */
+static void
+put_aarch64_insn (char *buf, uint32_t insn)
+{
+ unsigned char *where = (unsigned char *) buf;
+ where[0] = insn;
+ where[1] = insn >> 8;
+ where[2] = insn >> 16;
+ where[3] = insn >> 24;
+}
+
+static uint32_t
+get_aarch64_insn (char *buf)
+{
+ unsigned char *where = (unsigned char *) buf;
+ uint32_t result;
+ result = (where[0] | (where[1] << 8) | (where[2] << 16) | (where[3] << 24));
+ return result;
+}
+
+static void
+output_inst (struct aarch64_inst *new_inst)
+{
+ char *to = NULL;
+
+ to = frag_more (INSN_SIZE);
+
+ frag_now->tc_frag_data.recorded = 1;
+
+ put_aarch64_insn (to, inst.base.value);
+
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
+ {
+ fixS *fixp = fix_new_aarch64 (frag_now, to - frag_now->fr_literal,
+ INSN_SIZE, &inst.reloc.exp,
+ inst.reloc.pc_rel,
+ inst.reloc.type);
+ DEBUG_TRACE ("Prepared relocation fix up");
+ /* Don't check the addend value against the instruction size,
+ that's the job of our code in md_apply_fix(). */
+ fixp->fx_no_overflow = 1;
+ if (new_inst != NULL)
+ fixp->tc_fix_data.inst = new_inst;
+ if (aarch64_gas_internal_fixup_p ())
+ {
+ gas_assert (inst.reloc.opnd != AARCH64_OPND_NIL);
+ fixp->tc_fix_data.opnd = inst.reloc.opnd;
+ fixp->fx_addnumber = inst.reloc.flags;
+ }
+ }
+
+ dwarf2_emit_insn (INSN_SIZE);
+}
+
+/* Link together opcodes of the same name. */
+
+struct templates
+{
+ aarch64_opcode *opcode;
+ struct templates *next;
+};
+
+typedef struct templates templates;
+
+static templates *
+lookup_mnemonic (const char *start, int len)
+{
+ templates *templ = NULL;
+
+ templ = hash_find_n (aarch64_ops_hsh, start, len);
+ return templ;
+}
+
+/* Subroutine of md_assemble, responsible for looking up the primary
+ opcode from the mnemonic the user wrote. STR points to the
+ beginning of the mnemonic. */
+
+static templates *
+opcode_lookup (char **str)
+{
+ char *end, *base;
+ const aarch64_cond *cond;
+ char condname[16];
+ int len;
+
+ /* Scan up to the end of the mnemonic, which must end in white space,
+ '.', or end of string. */
+ for (base = end = *str; is_part_of_name(*end); end++)
+ if (*end == '.')
+ break;
+
+ if (end == base)
+ return 0;
+
+ inst.cond = COND_ALWAYS;
+
+ /* Handle a possible condition. */
+ if (end[0] == '.')
+ {
+ cond = hash_find_n (aarch64_cond_hsh, end + 1, 2);
+ if (cond)
+ {
+ inst.cond = cond->value;
+ *str = end + 3;
+ }
+ else
+ {
+ *str = end;
+ return 0;
+ }
+ }
+ else
+ *str = end;
+
+ len = end - base;
+
+ if (inst.cond == COND_ALWAYS)
+ {
+ /* Look for unaffixed mnemonic. */
+ return lookup_mnemonic (base, len);
+ }
+ else if (len <= 13)
+ {
+ /* append ".c" to mnemonic if conditional */
+ memcpy (condname, base, len);
+ memcpy (condname + len, ".c", 2);
+ base = condname;
+ len += 2;
+ return lookup_mnemonic (base, len);
+ }
+
+ return NULL;
+}
+
+/* Internal helper routine converting a vector neon_type_el structure
+ *VECTYPE to a corresponding operand qualifier. */
+
+static inline aarch64_opnd_qualifier_t
+vectype_to_qualifier (const struct neon_type_el *vectype)
+{
+ /* Element size in bytes indexed by neon_el_type. */
+ const unsigned char ele_size[5]
+ = {1, 2, 4, 8, 16};
+
+ if (!vectype->defined || vectype->type == NT_invtype)
+ goto vectype_conversion_fail;
+
+ gas_assert (vectype->type >= NT_b && vectype->type <= NT_q);
+
+ if (vectype->defined & NTA_HASINDEX)
+ /* Vector element register. */
+ return AARCH64_OPND_QLF_S_B + vectype->type;
+ else
+ {
+ /* Vector register. */
+ int reg_size = ele_size[vectype->type] * vectype->width;
+ unsigned offset;
+ if (reg_size != 16 && reg_size != 8)
+ goto vectype_conversion_fail;
+ /* The conversion is calculated based on the relation of the order of
+ qualifiers to the vector element size and vector register size. */
+ offset = (vectype->type == NT_q)
+ ? 8 : (vectype->type << 1) + (reg_size >> 4);
+ gas_assert (offset <= 8);
+ return AARCH64_OPND_QLF_V_8B + offset;
+ }
+
+vectype_conversion_fail:
+ first_error (_("bad vector arrangement type"));
+ return AARCH64_OPND_QLF_NIL;
+}
+
+/* Process an optional operand that is found omitted from the assembly line.
+ Fill *OPERAND for such an operand of type TYPE. OPCODE points to the
+ instruction's opcode entry while IDX is the index of this omitted operand.
+ */
+
+static void
+process_omitted_operand (enum aarch64_opnd type, const aarch64_opcode *opcode,
+ int idx, aarch64_opnd_info *operand)
+{
+ aarch64_insn default_value = get_optional_operand_default_value (opcode);
+ gas_assert (optional_operand_p (opcode, idx));
+ gas_assert (!operand->present);
+
+ switch (type)
+ {
+ case AARCH64_OPND_Rd:
+ case AARCH64_OPND_Rn:
+ case AARCH64_OPND_Rm:
+ case AARCH64_OPND_Rt:
+ case AARCH64_OPND_Rt2:
+ case AARCH64_OPND_Rs:
+ case AARCH64_OPND_Ra:
+ case AARCH64_OPND_Rt_SYS:
+ case AARCH64_OPND_Rd_SP:
+ case AARCH64_OPND_Rn_SP:
+ case AARCH64_OPND_Fd:
+ case AARCH64_OPND_Fn:
+ case AARCH64_OPND_Fm:
+ case AARCH64_OPND_Fa:
+ case AARCH64_OPND_Ft:
+ case AARCH64_OPND_Ft2:
+ case AARCH64_OPND_Sd:
+ case AARCH64_OPND_Sn:
+ case AARCH64_OPND_Sm:
+ case AARCH64_OPND_Vd:
+ case AARCH64_OPND_Vn:
+ case AARCH64_OPND_Vm:
+ case AARCH64_OPND_VdD1:
+ case AARCH64_OPND_VnD1:
+ operand->reg.regno = default_value;
+ break;
+
+ case AARCH64_OPND_Ed:
+ case AARCH64_OPND_En:
+ case AARCH64_OPND_Em:
+ operand->reglane.regno = default_value;
+ break;
+
+ case AARCH64_OPND_IDX:
+ case AARCH64_OPND_BIT_NUM:
+ case AARCH64_OPND_IMMR:
+ case AARCH64_OPND_IMMS:
+ case AARCH64_OPND_SHLL_IMM:
+ case AARCH64_OPND_IMM_VLSL:
+ case AARCH64_OPND_IMM_VLSR:
+ case AARCH64_OPND_CCMP_IMM:
+ case AARCH64_OPND_FBITS:
+ case AARCH64_OPND_UIMM4:
+ case AARCH64_OPND_UIMM3_OP1:
+ case AARCH64_OPND_UIMM3_OP2:
+ case AARCH64_OPND_IMM:
+ case AARCH64_OPND_WIDTH:
+ case AARCH64_OPND_UIMM7:
+ case AARCH64_OPND_NZCV:
+ operand->imm.value = default_value;
+ break;
+
+ case AARCH64_OPND_EXCEPTION:
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ break;
+
+ case AARCH64_OPND_BARRIER_ISB:
+ operand->barrier = aarch64_barrier_options + default_value;
+
+ default:
+ break;
+ }
+}
+
+/* Process the relocation type for move wide instructions.
+ Return TRUE on success; otherwise return FALSE. */
+
+static bfd_boolean
+process_movw_reloc_info (void)
+{
+ int is32;
+ unsigned shift;
+
+ is32 = inst.base.operands[0].qualifier == AARCH64_OPND_QLF_W ? 1 : 0;
+
+ if (inst.base.opcode->op == OP_MOVK)
+ switch (inst.reloc.type)
+ {
+ case BFD_RELOC_AARCH64_MOVW_G0_S:
+ case BFD_RELOC_AARCH64_MOVW_G1_S:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
+ set_syntax_error
+ (_("the specified relocation type is not allowed for MOVK"));
+ return FALSE;
+ default:
+ break;
+ }
+
+ switch (inst.reloc.type)
+ {
+ case BFD_RELOC_AARCH64_MOVW_G0:
+ case BFD_RELOC_AARCH64_MOVW_G0_S:
+ case BFD_RELOC_AARCH64_MOVW_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
+ shift = 0;
+ break;
+ case BFD_RELOC_AARCH64_MOVW_G1:
+ case BFD_RELOC_AARCH64_MOVW_G1_S:
+ case BFD_RELOC_AARCH64_MOVW_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ shift = 16;
+ break;
+ case BFD_RELOC_AARCH64_MOVW_G2:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
+ case BFD_RELOC_AARCH64_MOVW_G2_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
+ if (is32)
+ {
+ set_fatal_syntax_error
+ (_("the specified relocation type is not allowed for 32-bit "
+ "register"));
+ return FALSE;
+ }
+ shift = 32;
+ break;
+ case BFD_RELOC_AARCH64_MOVW_G3:
+ if (is32)
+ {
+ set_fatal_syntax_error
+ (_("the specified relocation type is not allowed for 32-bit "
+ "register"));
+ return FALSE;
+ }
+ shift = 48;
+ break;
+ default:
+ /* More cases should be added when more MOVW-related relocation types
+ are supported in GAS. */
+ gas_assert (aarch64_gas_internal_fixup_p ());
+ /* The shift amount should have already been set by the parser. */
+ return TRUE;
+ }
+ inst.base.operands[1].shifter.amount = shift;
+ return TRUE;
+}
+
+/* A primitive log caculator. */
+
+static inline unsigned int
+get_logsz (unsigned int size)
+{
+ const unsigned char ls[16] =
+ {0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4};
+ if (size > 16)
+ {
+ gas_assert (0);
+ return -1;
+ }
+ gas_assert (ls[size - 1] != (unsigned char)-1);
+ return ls[size - 1];
+}
+
+/* Determine and return the real reloc type code for an instruction
+ with the pseudo reloc type code BFD_RELOC_AARCH64_LDST_LO12. */
+
+static inline bfd_reloc_code_real_type
+ldst_lo12_determine_real_reloc_type (void)
+{
+ int logsz;
+ enum aarch64_opnd_qualifier opd0_qlf = inst.base.operands[0].qualifier;
+ enum aarch64_opnd_qualifier opd1_qlf = inst.base.operands[1].qualifier;
+
+ const bfd_reloc_code_real_type reloc_ldst_lo12[5] = {
+ BFD_RELOC_AARCH64_LDST8_LO12, BFD_RELOC_AARCH64_LDST16_LO12,
+ BFD_RELOC_AARCH64_LDST32_LO12, BFD_RELOC_AARCH64_LDST64_LO12,
+ BFD_RELOC_AARCH64_LDST128_LO12
+ };
+
+ gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12);
+ gas_assert (inst.base.opcode->operands[1] == AARCH64_OPND_ADDR_UIMM12);
+
+ if (opd1_qlf == AARCH64_OPND_QLF_NIL)
+ opd1_qlf =
+ aarch64_get_expected_qualifier (inst.base.opcode->qualifiers_list,
+ 1, opd0_qlf, 0);
+ gas_assert (opd1_qlf != AARCH64_OPND_QLF_NIL);
+
+ logsz = get_logsz (aarch64_get_qualifier_esize (opd1_qlf));
+ gas_assert (logsz >= 0 && logsz <= 4);
+
+ return reloc_ldst_lo12[logsz];
+}
+
+/* Check whether a register list REGINFO is valid. The registers must be
+ numbered in increasing order (modulo 32), in increments of one or two.
+
+ If ACCEPT_ALTERNATE is non-zero, the register numbers should be in
+ increments of two.
+
+ Return FALSE if such a register list is invalid, otherwise return TRUE. */
+
+static bfd_boolean
+reg_list_valid_p (uint32_t reginfo, int accept_alternate)
+{
+ uint32_t i, nb_regs, prev_regno, incr;
+
+ nb_regs = 1 + (reginfo & 0x3);
+ reginfo >>= 2;
+ prev_regno = reginfo & 0x1f;
+ incr = accept_alternate ? 2 : 1;
+
+ for (i = 1; i < nb_regs; ++i)
+ {
+ uint32_t curr_regno;
+ reginfo >>= 5;
+ curr_regno = reginfo & 0x1f;
+ if (curr_regno != ((prev_regno + incr) & 0x1f))
+ return FALSE;
+ prev_regno = curr_regno;
+ }
+
+ return TRUE;
+}
+
+/* Generic instruction operand parser. This does no encoding and no
+ semantic validation; it merely squirrels values away in the inst
+ structure. Returns TRUE or FALSE depending on whether the
+ specified grammar matched. */
+
+static bfd_boolean
+parse_operands (char *str, const aarch64_opcode *opcode)
+{
+ int i;
+ char *backtrack_pos = 0;
+ const enum aarch64_opnd *operands = opcode->operands;
+
+ clear_error ();
+ skip_whitespace (str);
+
+ for (i = 0; operands[i] != AARCH64_OPND_NIL; i++)
+ {
+ int64_t val;
+ int isreg32, isregzero;
+ int comma_skipped_p = 0;
+ aarch64_reg_type rtype;
+ struct neon_type_el vectype;
+ aarch64_opnd_info *info = &inst.base.operands[i];
+
+ DEBUG_TRACE ("parse operand %d", i);
+
+ /* Assign the operand code. */
+ info->type = operands[i];
+
+ if (optional_operand_p (opcode, i))
+ {
+ /* Remember where we are in case we need to backtrack. */
+ gas_assert (!backtrack_pos);
+ backtrack_pos = str;
+ }
+
+ /* Expect comma between operands; the backtrack mechanizm will take
+ care of cases of omitted optional operand. */
+ if (i > 0 && ! skip_past_char (&str, ','))
+ {
+ set_syntax_error (_("comma expected between operands"));
+ goto failure;
+ }
+ else
+ comma_skipped_p = 1;
+
+ switch (operands[i])
+ {
+ case AARCH64_OPND_Rd:
+ case AARCH64_OPND_Rn:
+ case AARCH64_OPND_Rm:
+ case AARCH64_OPND_Rt:
+ case AARCH64_OPND_Rt2:
+ case AARCH64_OPND_Rs:
+ case AARCH64_OPND_Ra:
+ case AARCH64_OPND_Rt_SYS:
+ case AARCH64_OPND_PAIRREG:
+ po_int_reg_or_fail (1, 0);
+ break;
+
+ case AARCH64_OPND_Rd_SP:
+ case AARCH64_OPND_Rn_SP:
+ po_int_reg_or_fail (0, 1);
+ break;
+
+ case AARCH64_OPND_Rm_EXT:
+ case AARCH64_OPND_Rm_SFT:
+ po_misc_or_fail (parse_shifter_operand
+ (&str, info, (operands[i] == AARCH64_OPND_Rm_EXT
+ ? SHIFTED_ARITH_IMM
+ : SHIFTED_LOGIC_IMM)));
+ if (!info->shifter.operator_present)
+ {
+ /* Default to LSL if not present. Libopcodes prefers shifter
+ kind to be explicit. */
+ gas_assert (info->shifter.kind == AARCH64_MOD_NONE);
+ info->shifter.kind = AARCH64_MOD_LSL;
+ /* For Rm_EXT, libopcodes will carry out further check on whether
+ or not stack pointer is used in the instruction (Recall that
+ "the extend operator is not optional unless at least one of
+ "Rd" or "Rn" is '11111' (i.e. WSP)"). */
+ }
+ break;
+
+ case AARCH64_OPND_Fd:
+ case AARCH64_OPND_Fn:
+ case AARCH64_OPND_Fm:
+ case AARCH64_OPND_Fa:
+ case AARCH64_OPND_Ft:
+ case AARCH64_OPND_Ft2:
+ case AARCH64_OPND_Sd:
+ case AARCH64_OPND_Sn:
+ case AARCH64_OPND_Sm:
+ val = aarch64_reg_parse (&str, REG_TYPE_BHSDQ, &rtype, NULL);
+ if (val == PARSE_FAIL)
+ {
+ first_error (_(get_reg_expected_msg (REG_TYPE_BHSDQ)));
+ goto failure;
+ }
+ gas_assert (rtype >= REG_TYPE_FP_B && rtype <= REG_TYPE_FP_Q);
+
+ info->reg.regno = val;
+ info->qualifier = AARCH64_OPND_QLF_S_B + (rtype - REG_TYPE_FP_B);
+ break;
+
+ case AARCH64_OPND_Vd:
+ case AARCH64_OPND_Vn:
+ case AARCH64_OPND_Vm:
+ val = aarch64_reg_parse (&str, REG_TYPE_VN, NULL, &vectype);
+ if (val == PARSE_FAIL)
+ {
+ first_error (_(get_reg_expected_msg (REG_TYPE_VN)));
+ goto failure;
+ }
+ if (vectype.defined & NTA_HASINDEX)
+ goto failure;
+
+ info->reg.regno = val;
+ info->qualifier = vectype_to_qualifier (&vectype);
+ if (info->qualifier == AARCH64_OPND_QLF_NIL)
+ goto failure;
+ break;
+
+ case AARCH64_OPND_VdD1:
+ case AARCH64_OPND_VnD1:
+ val = aarch64_reg_parse (&str, REG_TYPE_VN, NULL, &vectype);
+ if (val == PARSE_FAIL)
+ {
+ set_first_syntax_error (_(get_reg_expected_msg (REG_TYPE_VN)));
+ goto failure;
+ }
+ if (vectype.type != NT_d || vectype.index != 1)
+ {
+ set_fatal_syntax_error
+ (_("the top half of a 128-bit FP/SIMD register is expected"));
+ goto failure;
+ }
+ info->reg.regno = val;
+ /* N.B: VdD1 and VnD1 are treated as an fp or advsimd scalar register
+ here; it is correct for the purpose of encoding/decoding since
+ only the register number is explicitly encoded in the related
+ instructions, although this appears a bit hacky. */
+ info->qualifier = AARCH64_OPND_QLF_S_D;
+ break;
+
+ case AARCH64_OPND_Ed:
+ case AARCH64_OPND_En:
+ case AARCH64_OPND_Em:
+ val = aarch64_reg_parse (&str, REG_TYPE_VN, NULL, &vectype);
+ if (val == PARSE_FAIL)
+ {
+ first_error (_(get_reg_expected_msg (REG_TYPE_VN)));
+ goto failure;
+ }
+ if (vectype.type == NT_invtype || !(vectype.defined & NTA_HASINDEX))
+ goto failure;
+
+ info->reglane.regno = val;
+ info->reglane.index = vectype.index;
+ info->qualifier = vectype_to_qualifier (&vectype);
+ if (info->qualifier == AARCH64_OPND_QLF_NIL)
+ goto failure;
+ break;
+
+ case AARCH64_OPND_LVn:
+ case AARCH64_OPND_LVt:
+ case AARCH64_OPND_LVt_AL:
+ case AARCH64_OPND_LEt:
+ if ((val = parse_neon_reg_list (&str, &vectype)) == PARSE_FAIL)
+ goto failure;
+ if (! reg_list_valid_p (val, /* accept_alternate */ 0))
+ {
+ set_fatal_syntax_error (_("invalid register list"));
+ goto failure;
+ }
+ info->reglist.first_regno = (val >> 2) & 0x1f;
+ info->reglist.num_regs = (val & 0x3) + 1;
+ if (operands[i] == AARCH64_OPND_LEt)
+ {
+ if (!(vectype.defined & NTA_HASINDEX))
+ goto failure;
+ info->reglist.has_index = 1;
+ info->reglist.index = vectype.index;
+ }
+ else if (!(vectype.defined & NTA_HASTYPE))
+ goto failure;
+ info->qualifier = vectype_to_qualifier (&vectype);
+ if (info->qualifier == AARCH64_OPND_QLF_NIL)
+ goto failure;
+ break;
+
+ case AARCH64_OPND_Cn:
+ case AARCH64_OPND_Cm:
+ po_reg_or_fail (REG_TYPE_CN);
+ if (val > 15)
+ {
+ set_fatal_syntax_error (_(get_reg_expected_msg (REG_TYPE_CN)));
+ goto failure;
+ }
+ inst.base.operands[i].reg.regno = val;
+ break;
+
+ case AARCH64_OPND_SHLL_IMM:
+ case AARCH64_OPND_IMM_VLSR:
+ po_imm_or_fail (1, 64);
+ info->imm.value = val;
+ break;
+
+ case AARCH64_OPND_CCMP_IMM:
+ case AARCH64_OPND_FBITS:
+ case AARCH64_OPND_UIMM4:
+ case AARCH64_OPND_UIMM3_OP1:
+ case AARCH64_OPND_UIMM3_OP2:
+ case AARCH64_OPND_IMM_VLSL:
+ case AARCH64_OPND_IMM:
+ case AARCH64_OPND_WIDTH:
+ po_imm_nc_or_fail ();
+ info->imm.value = val;
+ break;
+
+ case AARCH64_OPND_UIMM7:
+ po_imm_or_fail (0, 127);
+ info->imm.value = val;
+ break;
+
+ case AARCH64_OPND_IDX:
+ case AARCH64_OPND_BIT_NUM:
+ case AARCH64_OPND_IMMR:
+ case AARCH64_OPND_IMMS:
+ po_imm_or_fail (0, 63);
+ info->imm.value = val;
+ break;
+
+ case AARCH64_OPND_IMM0:
+ po_imm_nc_or_fail ();
+ if (val != 0)
+ {
+ set_fatal_syntax_error (_("immediate zero expected"));
+ goto failure;
+ }
+ info->imm.value = 0;
+ break;
+
+ case AARCH64_OPND_FPIMM0:
+ {
+ int qfloat;
+ bfd_boolean res1 = FALSE, res2 = FALSE;
+ /* N.B. -0.0 will be rejected; although -0.0 shouldn't be rejected,
+ it is probably not worth the effort to support it. */
+ if (!(res1 = parse_aarch64_imm_float (&str, &qfloat, FALSE))
+ && !(res2 = parse_constant_immediate (&str, &val)))
+ goto failure;
+ if ((res1 && qfloat == 0) || (res2 && val == 0))
+ {
+ info->imm.value = 0;
+ info->imm.is_fp = 1;
+ break;
+ }
+ set_fatal_syntax_error (_("immediate zero expected"));
+ goto failure;
+ }
+
+ case AARCH64_OPND_IMM_MOV:
+ {
+ char *saved = str;
+ if (reg_name_p (str, REG_TYPE_R_Z_SP) ||
+ reg_name_p (str, REG_TYPE_VN))
+ goto failure;
+ str = saved;
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_OPT_PREFIX, 1));
+ /* The MOV immediate alias will be fixed up by fix_mov_imm_insn
+ later. fix_mov_imm_insn will try to determine a machine
+ instruction (MOVZ, MOVN or ORR) for it and will issue an error
+ message if the immediate cannot be moved by a single
+ instruction. */
+ aarch64_set_gas_internal_fixup (&inst.reloc, info, 1);
+ inst.base.operands[i].skip = 1;
+ }
+ break;
+
+ case AARCH64_OPND_SIMD_IMM:
+ case AARCH64_OPND_SIMD_IMM_SFT:
+ if (! parse_big_immediate (&str, &val))
+ goto failure;
+ assign_imm_if_const_or_fixup_later (&inst.reloc, info,
+ /* addr_off_p */ 0,
+ /* need_libopcodes_p */ 1,
+ /* skip_p */ 1);
+ /* Parse shift.
+ N.B. although AARCH64_OPND_SIMD_IMM doesn't permit any
+ shift, we don't check it here; we leave the checking to
+ the libopcodes (operand_general_constraint_met_p). By
+ doing this, we achieve better diagnostics. */
+ if (skip_past_comma (&str)
+ && ! parse_shift (&str, info, SHIFTED_LSL_MSL))
+ goto failure;
+ if (!info->shifter.operator_present
+ && info->type == AARCH64_OPND_SIMD_IMM_SFT)
+ {
+ /* Default to LSL if not present. Libopcodes prefers shifter
+ kind to be explicit. */
+ gas_assert (info->shifter.kind == AARCH64_MOD_NONE);
+ info->shifter.kind = AARCH64_MOD_LSL;
+ }
+ break;
+
+ case AARCH64_OPND_FPIMM:
+ case AARCH64_OPND_SIMD_FPIMM:
+ {
+ int qfloat;
+ bfd_boolean dp_p
+ = (aarch64_get_qualifier_esize (inst.base.operands[0].qualifier)
+ == 8);
+ if (! parse_aarch64_imm_float (&str, &qfloat, dp_p))
+ goto failure;
+ if (qfloat == 0)
+ {
+ set_fatal_syntax_error (_("invalid floating-point constant"));
+ goto failure;
+ }
+ inst.base.operands[i].imm.value = encode_imm_float_bits (qfloat);
+ inst.base.operands[i].imm.is_fp = 1;
+ }
+ break;
+
+ case AARCH64_OPND_LIMM:
+ po_misc_or_fail (parse_shifter_operand (&str, info,
+ SHIFTED_LOGIC_IMM));
+ if (info->shifter.operator_present)
+ {
+ set_fatal_syntax_error
+ (_("shift not allowed for bitmask immediate"));
+ goto failure;
+ }
+ assign_imm_if_const_or_fixup_later (&inst.reloc, info,
+ /* addr_off_p */ 0,
+ /* need_libopcodes_p */ 1,
+ /* skip_p */ 1);
+ break;
+
+ case AARCH64_OPND_AIMM:
+ if (opcode->op == OP_ADD)
+ /* ADD may have relocation types. */
+ po_misc_or_fail (parse_shifter_operand_reloc (&str, info,
+ SHIFTED_ARITH_IMM));
+ else
+ po_misc_or_fail (parse_shifter_operand (&str, info,
+ SHIFTED_ARITH_IMM));
+ switch (inst.reloc.type)
+ {
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
+ info->shifter.amount = 12;
+ break;
+ case BFD_RELOC_UNUSED:
+ aarch64_set_gas_internal_fixup (&inst.reloc, info, 0);
+ if (info->shifter.kind != AARCH64_MOD_NONE)
+ inst.reloc.flags = FIXUP_F_HAS_EXPLICIT_SHIFT;
+ inst.reloc.pc_rel = 0;
+ break;
+ default:
+ break;
+ }
+ info->imm.value = 0;
+ if (!info->shifter.operator_present)
+ {
+ /* Default to LSL if not present. Libopcodes prefers shifter
+ kind to be explicit. */
+ gas_assert (info->shifter.kind == AARCH64_MOD_NONE);
+ info->shifter.kind = AARCH64_MOD_LSL;
+ }
+ break;
+
+ case AARCH64_OPND_HALF:
+ {
+ /* #<imm16> or relocation. */
+ int internal_fixup_p;
+ po_misc_or_fail (parse_half (&str, &internal_fixup_p));
+ if (internal_fixup_p)
+ aarch64_set_gas_internal_fixup (&inst.reloc, info, 0);
+ skip_whitespace (str);
+ if (skip_past_comma (&str))
+ {
+ /* {, LSL #<shift>} */
+ if (! aarch64_gas_internal_fixup_p ())
+ {
+ set_fatal_syntax_error (_("can't mix relocation modifier "
+ "with explicit shift"));
+ goto failure;
+ }
+ po_misc_or_fail (parse_shift (&str, info, SHIFTED_LSL));
+ }
+ else
+ inst.base.operands[i].shifter.amount = 0;
+ inst.base.operands[i].shifter.kind = AARCH64_MOD_LSL;
+ inst.base.operands[i].imm.value = 0;
+ if (! process_movw_reloc_info ())
+ goto failure;
+ }
+ break;
+
+ case AARCH64_OPND_EXCEPTION:
+ po_misc_or_fail (parse_immediate_expression (&str, &inst.reloc.exp));
+ assign_imm_if_const_or_fixup_later (&inst.reloc, info,
+ /* addr_off_p */ 0,
+ /* need_libopcodes_p */ 0,
+ /* skip_p */ 1);
+ break;
+
+ case AARCH64_OPND_NZCV:
+ {
+ const asm_nzcv *nzcv = hash_find_n (aarch64_nzcv_hsh, str, 4);
+ if (nzcv != NULL)
+ {
+ str += 4;
+ info->imm.value = nzcv->value;
+ break;
+ }
+ po_imm_or_fail (0, 15);
+ info->imm.value = val;
+ }
+ break;
+
+ case AARCH64_OPND_COND:
+ case AARCH64_OPND_COND1:
+ info->cond = hash_find_n (aarch64_cond_hsh, str, 2);
+ str += 2;
+ if (info->cond == NULL)
+ {
+ set_syntax_error (_("invalid condition"));
+ goto failure;
+ }
+ else if (operands[i] == AARCH64_OPND_COND1
+ && (info->cond->value & 0xe) == 0xe)
+ {
+ /* Not allow AL or NV. */
+ set_default_error ();
+ goto failure;
+ }
+ break;
+
+ case AARCH64_OPND_ADDR_ADRP:
+ po_misc_or_fail (parse_adrp (&str));
+ /* Clear the value as operand needs to be relocated. */
+ info->imm.value = 0;
+ break;
+
+ case AARCH64_OPND_ADDR_PCREL14:
+ case AARCH64_OPND_ADDR_PCREL19:
+ case AARCH64_OPND_ADDR_PCREL21:
+ case AARCH64_OPND_ADDR_PCREL26:
+ po_misc_or_fail (parse_address_reloc (&str, info));
+ if (!info->addr.pcrel)
+ {
+ set_syntax_error (_("invalid pc-relative address"));
+ goto failure;
+ }
+ if (inst.gen_lit_pool
+ && (opcode->iclass != loadlit || opcode->op == OP_PRFM_LIT))
+ {
+ /* Only permit "=value" in the literal load instructions.
+ The literal will be generated by programmer_friendly_fixup. */
+ set_syntax_error (_("invalid use of \"=immediate\""));
+ goto failure;
+ }
+ if (inst.reloc.exp.X_op == O_symbol && find_reloc_table_entry (&str))
+ {
+ set_syntax_error (_("unrecognized relocation suffix"));
+ goto failure;
+ }
+ if (inst.reloc.exp.X_op == O_constant && !inst.gen_lit_pool)
+ {
+ info->imm.value = inst.reloc.exp.X_add_number;
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
+ else
+ {
+ info->imm.value = 0;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ switch (opcode->iclass)
+ {
+ case compbranch:
+ case condbranch:
+ /* e.g. CBZ or B.COND */
+ gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL19);
+ inst.reloc.type = BFD_RELOC_AARCH64_BRANCH19;
+ break;
+ case testbranch:
+ /* e.g. TBZ */
+ gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL14);
+ inst.reloc.type = BFD_RELOC_AARCH64_TSTBR14;
+ break;
+ case branch_imm:
+ /* e.g. B or BL */
+ gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL26);
+ inst.reloc.type =
+ (opcode->op == OP_BL) ? BFD_RELOC_AARCH64_CALL26
+ : BFD_RELOC_AARCH64_JUMP26;
+ break;
+ case loadlit:
+ gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL19);
+ inst.reloc.type = BFD_RELOC_AARCH64_LD_LO19_PCREL;
+ break;
+ case pcreladdr:
+ gas_assert (operands[i] == AARCH64_OPND_ADDR_PCREL21);
+ inst.reloc.type = BFD_RELOC_AARCH64_ADR_LO21_PCREL;
+ break;
+ default:
+ gas_assert (0);
+ abort ();
+ }
+ inst.reloc.pc_rel = 1;
+ }
+ break;
+
+ case AARCH64_OPND_ADDR_SIMPLE:
+ case AARCH64_OPND_SIMD_ADDR_SIMPLE:
+ /* [<Xn|SP>{, #<simm>}] */
+ po_char_or_fail ('[');
+ po_reg_or_fail (REG_TYPE_R64_SP);
+ /* Accept optional ", #0". */
+ if (operands[i] == AARCH64_OPND_ADDR_SIMPLE
+ && skip_past_char (&str, ','))
+ {
+ skip_past_char (&str, '#');
+ if (! skip_past_char (&str, '0'))
+ {
+ set_fatal_syntax_error
+ (_("the optional immediate offset can only be 0"));
+ goto failure;
+ }
+ }
+ po_char_or_fail (']');
+ info->addr.base_regno = val;
+ break;
+
+ case AARCH64_OPND_ADDR_REGOFF:
+ /* [<Xn|SP>, <R><m>{, <extend> {<amount>}}] */
+ po_misc_or_fail (parse_address (&str, info, 0));
+ if (info->addr.pcrel || !info->addr.offset.is_reg
+ || !info->addr.preind || info->addr.postind
+ || info->addr.writeback)
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ if (!info->shifter.operator_present)
+ {
+ /* Default to LSL if not present. Libopcodes prefers shifter
+ kind to be explicit. */
+ gas_assert (info->shifter.kind == AARCH64_MOD_NONE);
+ info->shifter.kind = AARCH64_MOD_LSL;
+ }
+ /* Qualifier to be deduced by libopcodes. */
+ break;
+
+ case AARCH64_OPND_ADDR_SIMM7:
+ po_misc_or_fail (parse_address (&str, info, 0));
+ if (info->addr.pcrel || info->addr.offset.is_reg
+ || (!info->addr.preind && !info->addr.postind))
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ assign_imm_if_const_or_fixup_later (&inst.reloc, info,
+ /* addr_off_p */ 1,
+ /* need_libopcodes_p */ 1,
+ /* skip_p */ 0);
+ break;
+
+ case AARCH64_OPND_ADDR_SIMM9:
+ case AARCH64_OPND_ADDR_SIMM9_2:
+ po_misc_or_fail (parse_address_reloc (&str, info));
+ if (info->addr.pcrel || info->addr.offset.is_reg
+ || (!info->addr.preind && !info->addr.postind)
+ || (operands[i] == AARCH64_OPND_ADDR_SIMM9_2
+ && info->addr.writeback))
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
+ {
+ set_syntax_error (_("relocation not allowed"));
+ goto failure;
+ }
+ assign_imm_if_const_or_fixup_later (&inst.reloc, info,
+ /* addr_off_p */ 1,
+ /* need_libopcodes_p */ 1,
+ /* skip_p */ 0);
+ break;
+
+ case AARCH64_OPND_ADDR_UIMM12:
+ po_misc_or_fail (parse_address_reloc (&str, info));
+ if (info->addr.pcrel || info->addr.offset.is_reg
+ || !info->addr.preind || info->addr.writeback)
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ aarch64_set_gas_internal_fixup (&inst.reloc, info, 1);
+ else if (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12)
+ inst.reloc.type = ldst_lo12_determine_real_reloc_type ();
+ /* Leave qualifier to be determined by libopcodes. */
+ break;
+
+ case AARCH64_OPND_SIMD_ADDR_POST:
+ /* [<Xn|SP>], <Xm|#<amount>> */
+ po_misc_or_fail (parse_address (&str, info, 1));
+ if (!info->addr.postind || !info->addr.writeback)
+ {
+ set_syntax_error (_("invalid addressing mode"));
+ goto failure;
+ }
+ if (!info->addr.offset.is_reg)
+ {
+ if (inst.reloc.exp.X_op == O_constant)
+ info->addr.offset.imm = inst.reloc.exp.X_add_number;
+ else
+ {
+ set_fatal_syntax_error
+ (_("writeback value should be an immediate constant"));
+ goto failure;
+ }
+ }
+ /* No qualifier. */
+ break;
+
+ case AARCH64_OPND_SYSREG:
+ if ((val = parse_sys_reg (&str, aarch64_sys_regs_hsh, 1))
+ == PARSE_FAIL)
+ {
+ set_syntax_error (_("unknown or missing system register name"));
+ goto failure;
+ }
+ inst.base.operands[i].sysreg = val;
+ break;
+
+ case AARCH64_OPND_PSTATEFIELD:
+ if ((val = parse_sys_reg (&str, aarch64_pstatefield_hsh, 0))
+ == PARSE_FAIL)
+ {
+ set_syntax_error (_("unknown or missing PSTATE field name"));
+ goto failure;
+ }
+ inst.base.operands[i].pstatefield = val;
+ break;
+
+ case AARCH64_OPND_SYSREG_IC:
+ inst.base.operands[i].sysins_op =
+ parse_sys_ins_reg (&str, aarch64_sys_regs_ic_hsh);
+ goto sys_reg_ins;
+ case AARCH64_OPND_SYSREG_DC:
+ inst.base.operands[i].sysins_op =
+ parse_sys_ins_reg (&str, aarch64_sys_regs_dc_hsh);
+ goto sys_reg_ins;
+ case AARCH64_OPND_SYSREG_AT:
+ inst.base.operands[i].sysins_op =
+ parse_sys_ins_reg (&str, aarch64_sys_regs_at_hsh);
+ goto sys_reg_ins;
+ case AARCH64_OPND_SYSREG_TLBI:
+ inst.base.operands[i].sysins_op =
+ parse_sys_ins_reg (&str, aarch64_sys_regs_tlbi_hsh);
+sys_reg_ins:
+ if (inst.base.operands[i].sysins_op == NULL)
+ {
+ set_fatal_syntax_error ( _("unknown or missing operation name"));
+ goto failure;
+ }
+ break;
+
+ case AARCH64_OPND_BARRIER:
+ case AARCH64_OPND_BARRIER_ISB:
+ val = parse_barrier (&str);
+ if (val != PARSE_FAIL
+ && operands[i] == AARCH64_OPND_BARRIER_ISB && val != 0xf)
+ {
+ /* ISB only accepts options name 'sy'. */
+ set_syntax_error
+ (_("the specified option is not accepted in ISB"));
+ /* Turn off backtrack as this optional operand is present. */
+ backtrack_pos = 0;
+ goto failure;
+ }
+ /* This is an extension to accept a 0..15 immediate. */
+ if (val == PARSE_FAIL)
+ po_imm_or_fail (0, 15);
+ info->barrier = aarch64_barrier_options + val;
+ break;
+
+ case AARCH64_OPND_PRFOP:
+ val = parse_pldop (&str);
+ /* This is an extension to accept a 0..31 immediate. */
+ if (val == PARSE_FAIL)
+ po_imm_or_fail (0, 31);
+ inst.base.operands[i].prfop = aarch64_prfops + val;
+ break;
+
+ default:
+ as_fatal (_("unhandled operand code %d"), operands[i]);
+ }
+
+ /* If we get here, this operand was successfully parsed. */
+ inst.base.operands[i].present = 1;
+ continue;
+
+failure:
+ /* The parse routine should already have set the error, but in case
+ not, set a default one here. */
+ if (! error_p ())
+ set_default_error ();
+
+ if (! backtrack_pos)
+ goto parse_operands_return;
+
+ {
+ /* We reach here because this operand is marked as optional, and
+ either no operand was supplied or the operand was supplied but it
+ was syntactically incorrect. In the latter case we report an
+ error. In the former case we perform a few more checks before
+ dropping through to the code to insert the default operand. */
+
+ char *tmp = backtrack_pos;
+ char endchar = END_OF_INSN;
+
+ if (i != (aarch64_num_of_operands (opcode) - 1))
+ endchar = ',';
+ skip_past_char (&tmp, ',');
+
+ if (*tmp != endchar)
+ /* The user has supplied an operand in the wrong format. */
+ goto parse_operands_return;
+
+ /* Make sure there is not a comma before the optional operand.
+ For example the fifth operand of 'sys' is optional:
+
+ sys #0,c0,c0,#0, <--- wrong
+ sys #0,c0,c0,#0 <--- correct. */
+ if (comma_skipped_p && i && endchar == END_OF_INSN)
+ {
+ set_fatal_syntax_error
+ (_("unexpected comma before the omitted optional operand"));
+ goto parse_operands_return;
+ }
+ }
+
+ /* Reaching here means we are dealing with an optional operand that is
+ omitted from the assembly line. */
+ gas_assert (optional_operand_p (opcode, i));
+ info->present = 0;
+ process_omitted_operand (operands[i], opcode, i, info);
+
+ /* Try again, skipping the optional operand at backtrack_pos. */
+ str = backtrack_pos;
+ backtrack_pos = 0;
+
+ /* Clear any error record after the omitted optional operand has been
+ successfully handled. */
+ clear_error ();
+ }
+
+ /* Check if we have parsed all the operands. */
+ if (*str != '\0' && ! error_p ())
+ {
+ /* Set I to the index of the last present operand; this is
+ for the purpose of diagnostics. */
+ for (i -= 1; i >= 0 && !inst.base.operands[i].present; --i)
+ ;
+ set_fatal_syntax_error
+ (_("unexpected characters following instruction"));
+ }
+
+parse_operands_return:
+
+ if (error_p ())
+ {
+ DEBUG_TRACE ("parsing FAIL: %s - %s",
+ operand_mismatch_kind_names[get_error_kind ()],
+ get_error_message ());
+ /* Record the operand error properly; this is useful when there
+ are multiple instruction templates for a mnemonic name, so that
+ later on, we can select the error that most closely describes
+ the problem. */
+ record_operand_error (opcode, i, get_error_kind (),
+ get_error_message ());
+ return FALSE;
+ }
+ else
+ {
+ DEBUG_TRACE ("parsing SUCCESS");
+ return TRUE;
+ }
+}
+
+/* It does some fix-up to provide some programmer friendly feature while
+ keeping the libopcodes happy, i.e. libopcodes only accepts
+ the preferred architectural syntax.
+ Return FALSE if there is any failure; otherwise return TRUE. */
+
+static bfd_boolean
+programmer_friendly_fixup (aarch64_instruction *instr)
+{
+ aarch64_inst *base = &instr->base;
+ const aarch64_opcode *opcode = base->opcode;
+ enum aarch64_op op = opcode->op;
+ aarch64_opnd_info *operands = base->operands;
+
+ DEBUG_TRACE ("enter");
+
+ switch (opcode->iclass)
+ {
+ case testbranch:
+ /* TBNZ Xn|Wn, #uimm6, label
+ Test and Branch Not Zero: conditionally jumps to label if bit number
+ uimm6 in register Xn is not zero. The bit number implies the width of
+ the register, which may be written and should be disassembled as Wn if
+ uimm is less than 32. */
+ if (operands[0].qualifier == AARCH64_OPND_QLF_W)
+ {
+ if (operands[1].imm.value >= 32)
+ {
+ record_operand_out_of_range_error (opcode, 1, _("immediate value"),
+ 0, 31);
+ return FALSE;
+ }
+ operands[0].qualifier = AARCH64_OPND_QLF_X;
+ }
+ break;
+ case loadlit:
+ /* LDR Wt, label | =value
+ As a convenience assemblers will typically permit the notation
+ "=value" in conjunction with the pc-relative literal load instructions
+ to automatically place an immediate value or symbolic address in a
+ nearby literal pool and generate a hidden label which references it.
+ ISREG has been set to 0 in the case of =value. */
+ if (instr->gen_lit_pool
+ && (op == OP_LDR_LIT || op == OP_LDRV_LIT || op == OP_LDRSW_LIT))
+ {
+ int size = aarch64_get_qualifier_esize (operands[0].qualifier);
+ if (op == OP_LDRSW_LIT)
+ size = 4;
+ if (instr->reloc.exp.X_op != O_constant
+ && instr->reloc.exp.X_op != O_big
+ && instr->reloc.exp.X_op != O_symbol)
+ {
+ record_operand_error (opcode, 1,
+ AARCH64_OPDE_FATAL_SYNTAX_ERROR,
+ _("constant expression expected"));
+ return FALSE;
+ }
+ if (! add_to_lit_pool (&instr->reloc.exp, size))
+ {
+ record_operand_error (opcode, 1,
+ AARCH64_OPDE_OTHER_ERROR,
+ _("literal pool insertion failed"));
+ return FALSE;
+ }
+ }
+ break;
+ case log_shift:
+ case bitfield:
+ /* UXT[BHW] Wd, Wn
+ Unsigned Extend Byte|Halfword|Word: UXT[BH] is architectural alias
+ for UBFM Wd,Wn,#0,#7|15, while UXTW is pseudo instruction which is
+ encoded using ORR Wd, WZR, Wn (MOV Wd,Wn).
+ A programmer-friendly assembler should accept a destination Xd in
+ place of Wd, however that is not the preferred form for disassembly.
+ */
+ if ((op == OP_UXTB || op == OP_UXTH || op == OP_UXTW)
+ && operands[1].qualifier == AARCH64_OPND_QLF_W
+ && operands[0].qualifier == AARCH64_OPND_QLF_X)
+ operands[0].qualifier = AARCH64_OPND_QLF_W;
+ break;
+
+ case addsub_ext:
+ {
+ /* In the 64-bit form, the final register operand is written as Wm
+ for all but the (possibly omitted) UXTX/LSL and SXTX
+ operators.
+ As a programmer-friendly assembler, we accept e.g.
+ ADDS <Xd>, <Xn|SP>, <Xm>{, UXTB {#<amount>}} and change it to
+ ADDS <Xd>, <Xn|SP>, <Wm>{, UXTB {#<amount>}}. */
+ int idx = aarch64_operand_index (opcode->operands,
+ AARCH64_OPND_Rm_EXT);
+ gas_assert (idx == 1 || idx == 2);
+ if (operands[0].qualifier == AARCH64_OPND_QLF_X
+ && operands[idx].qualifier == AARCH64_OPND_QLF_X
+ && operands[idx].shifter.kind != AARCH64_MOD_LSL
+ && operands[idx].shifter.kind != AARCH64_MOD_UXTX
+ && operands[idx].shifter.kind != AARCH64_MOD_SXTX)
+ operands[idx].qualifier = AARCH64_OPND_QLF_W;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ DEBUG_TRACE ("exit with SUCCESS");
+ return TRUE;
+}
+
+/* A wrapper function to interface with libopcodes on encoding and
+ record the error message if there is any.
+
+ Return TRUE on success; otherwise return FALSE. */
+
+static bfd_boolean
+do_encode (const aarch64_opcode *opcode, aarch64_inst *instr,
+ aarch64_insn *code)
+{
+ aarch64_operand_error error_info;
+ error_info.kind = AARCH64_OPDE_NIL;
+ if (aarch64_opcode_encode (opcode, instr, code, NULL, &error_info))
+ return TRUE;
+ else
+ {
+ gas_assert (error_info.kind != AARCH64_OPDE_NIL);
+ record_operand_error_info (opcode, &error_info);
+ return FALSE;
+ }
+}
+
+#ifdef DEBUG_AARCH64
+static inline void
+dump_opcode_operands (const aarch64_opcode *opcode)
+{
+ int i = 0;
+ while (opcode->operands[i] != AARCH64_OPND_NIL)
+ {
+ aarch64_verbose ("\t\t opnd%d: %s", i,
+ aarch64_get_operand_name (opcode->operands[i])[0] != '\0'
+ ? aarch64_get_operand_name (opcode->operands[i])
+ : aarch64_get_operand_desc (opcode->operands[i]));
+ ++i;
+ }
+}
+#endif /* DEBUG_AARCH64 */
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *str)
+{
+ char *p = str;
+ templates *template;
+ aarch64_opcode *opcode;
+ aarch64_inst *inst_base;
+ unsigned saved_cond;
+
+ /* Align the previous label if needed. */
+ if (last_label_seen != NULL)
+ {
+ symbol_set_frag (last_label_seen, frag_now);
+ S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
+ S_SET_SEGMENT (last_label_seen, now_seg);
+ }
+
+ inst.reloc.type = BFD_RELOC_UNUSED;
+
+ DEBUG_TRACE ("\n\n");
+ DEBUG_TRACE ("==============================");
+ DEBUG_TRACE ("Enter md_assemble with %s", str);
+
+ template = opcode_lookup (&p);
+ if (!template)
+ {
+ /* It wasn't an instruction, but it might be a register alias of
+ the form alias .req reg directive. */
+ if (!create_register_alias (str, p))
+ as_bad (_("unknown mnemonic `%s' -- `%s'"), get_mnemonic_name (str),
+ str);
+ return;
+ }
+
+ skip_whitespace (p);
+ if (*p == ',')
+ {
+ as_bad (_("unexpected comma after the mnemonic name `%s' -- `%s'"),
+ get_mnemonic_name (str), str);
+ return;
+ }
+
+ init_operand_error_report ();
+
+ saved_cond = inst.cond;
+ reset_aarch64_instruction (&inst);
+ inst.cond = saved_cond;
+
+ /* Iterate through all opcode entries with the same mnemonic name. */
+ do
+ {
+ opcode = template->opcode;
+
+ DEBUG_TRACE ("opcode %s found", opcode->name);
+#ifdef DEBUG_AARCH64
+ if (debug_dump)
+ dump_opcode_operands (opcode);
+#endif /* DEBUG_AARCH64 */
+
+ mapping_state (MAP_INSN);
+
+ inst_base = &inst.base;
+ inst_base->opcode = opcode;
+
+ /* Truly conditionally executed instructions, e.g. b.cond. */
+ if (opcode->flags & F_COND)
+ {
+ gas_assert (inst.cond != COND_ALWAYS);
+ inst_base->cond = get_cond_from_value (inst.cond);
+ DEBUG_TRACE ("condition found %s", inst_base->cond->names[0]);
+ }
+ else if (inst.cond != COND_ALWAYS)
+ {
+ /* It shouldn't arrive here, where the assembly looks like a
+ conditional instruction but the found opcode is unconditional. */
+ gas_assert (0);
+ continue;
+ }
+
+ if (parse_operands (p, opcode)
+ && programmer_friendly_fixup (&inst)
+ && do_encode (inst_base->opcode, &inst.base, &inst_base->value))
+ {
+ /* Check that this instruction is supported for this CPU. */
+ if (!opcode->avariant
+ || !AARCH64_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant))
+ {
+ as_bad (_("selected processor does not support `%s'"), str);
+ return;
+ }
+
+ if (inst.reloc.type == BFD_RELOC_UNUSED
+ || !inst.reloc.need_libopcodes_p)
+ output_inst (NULL);
+ else
+ {
+ /* If there is relocation generated for the instruction,
+ store the instruction information for the future fix-up. */
+ struct aarch64_inst *copy;
+ gas_assert (inst.reloc.type != BFD_RELOC_UNUSED);
+ if ((copy = xmalloc (sizeof (struct aarch64_inst))) == NULL)
+ abort ();
+ memcpy (copy, &inst.base, sizeof (struct aarch64_inst));
+ output_inst (copy);
+ }
+ return;
+ }
+
+ template = template->next;
+ if (template != NULL)
+ {
+ reset_aarch64_instruction (&inst);
+ inst.cond = saved_cond;
+ }
+ }
+ while (template != NULL);
+
+ /* Issue the error messages if any. */
+ output_operand_error_report (str);
+}
+
+/* Various frobbings of labels and their addresses. */
+
+void
+aarch64_start_line_hook (void)
+{
+ last_label_seen = NULL;
+}
+
+void
+aarch64_frob_label (symbolS * sym)
+{
+ last_label_seen = sym;
+
+ dwarf2_emit_label (sym);
+}
+
+int
+aarch64_data_in_code (void)
+{
+ if (!strncmp (input_line_pointer + 1, "data:", 5))
+ {
+ *input_line_pointer = '/';
+ input_line_pointer += 5;
+ *input_line_pointer = 0;
+ return 1;
+ }
+
+ return 0;
+}
+
+char *
+aarch64_canonicalize_symbol_name (char *name)
+{
+ int len;
+
+ if ((len = strlen (name)) > 5 && streq (name + len - 5, "/data"))
+ *(name + len - 5) = 0;
+
+ return name;
+}
+
+/* Table of all register names defined by default. The user can
+ define additional names with .req. Note that all register names
+ should appear in both upper and lowercase variants. Some registers
+ also have mixed-case names. */
+
+#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE }
+#define REGNUM(p,n,t) REGDEF(p##n, n, t)
+#define REGSET31(p,t) \
+ REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \
+ REGNUM(p, 4,t), REGNUM(p, 5,t), REGNUM(p, 6,t), REGNUM(p, 7,t), \
+ REGNUM(p, 8,t), REGNUM(p, 9,t), REGNUM(p,10,t), REGNUM(p,11,t), \
+ REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t), \
+ REGNUM(p,16,t), REGNUM(p,17,t), REGNUM(p,18,t), REGNUM(p,19,t), \
+ REGNUM(p,20,t), REGNUM(p,21,t), REGNUM(p,22,t), REGNUM(p,23,t), \
+ REGNUM(p,24,t), REGNUM(p,25,t), REGNUM(p,26,t), REGNUM(p,27,t), \
+ REGNUM(p,28,t), REGNUM(p,29,t), REGNUM(p,30,t)
+#define REGSET(p,t) \
+ REGSET31(p,t), REGNUM(p,31,t)
+
+/* These go into aarch64_reg_hsh hash-table. */
+static const reg_entry reg_names[] = {
+ /* Integer registers. */
+ REGSET31 (x, R_64), REGSET31 (X, R_64),
+ REGSET31 (w, R_32), REGSET31 (W, R_32),
+
+ REGDEF (wsp, 31, SP_32), REGDEF (WSP, 31, SP_32),
+ REGDEF (sp, 31, SP_64), REGDEF (SP, 31, SP_64),
+
+ REGDEF (wzr, 31, Z_32), REGDEF (WZR, 31, Z_32),
+ REGDEF (xzr, 31, Z_64), REGDEF (XZR, 31, Z_64),
+
+ /* Coprocessor register numbers. */
+ REGSET (c, CN), REGSET (C, CN),
+
+ /* Floating-point single precision registers. */
+ REGSET (s, FP_S), REGSET (S, FP_S),
+
+ /* Floating-point double precision registers. */
+ REGSET (d, FP_D), REGSET (D, FP_D),
+
+ /* Floating-point half precision registers. */
+ REGSET (h, FP_H), REGSET (H, FP_H),
+
+ /* Floating-point byte precision registers. */
+ REGSET (b, FP_B), REGSET (B, FP_B),
+
+ /* Floating-point quad precision registers. */
+ REGSET (q, FP_Q), REGSET (Q, FP_Q),
+
+ /* FP/SIMD registers. */
+ REGSET (v, VN), REGSET (V, VN),
+};
+
+#undef REGDEF
+#undef REGNUM
+#undef REGSET
+
+#define N 1
+#define n 0
+#define Z 1
+#define z 0
+#define C 1
+#define c 0
+#define V 1
+#define v 0
+#define B(a,b,c,d) (((a) << 3) | ((b) << 2) | ((c) << 1) | (d))
+static const asm_nzcv nzcv_names[] = {
+ {"nzcv", B (n, z, c, v)},
+ {"nzcV", B (n, z, c, V)},
+ {"nzCv", B (n, z, C, v)},
+ {"nzCV", B (n, z, C, V)},
+ {"nZcv", B (n, Z, c, v)},
+ {"nZcV", B (n, Z, c, V)},
+ {"nZCv", B (n, Z, C, v)},
+ {"nZCV", B (n, Z, C, V)},
+ {"Nzcv", B (N, z, c, v)},
+ {"NzcV", B (N, z, c, V)},
+ {"NzCv", B (N, z, C, v)},
+ {"NzCV", B (N, z, C, V)},
+ {"NZcv", B (N, Z, c, v)},
+ {"NZcV", B (N, Z, c, V)},
+ {"NZCv", B (N, Z, C, v)},
+ {"NZCV", B (N, Z, C, V)}
+};
+
+#undef N
+#undef n
+#undef Z
+#undef z
+#undef C
+#undef c
+#undef V
+#undef v
+#undef B
+
+/* MD interface: bits in the object file. */
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least). */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* MD interface: Sections. */
+
+/* Estimate the size of a frag before relaxing. Assume everything fits in
+ 4 bytes. */
+
+int
+md_estimate_size_before_relax (fragS * fragp, segT segtype ATTRIBUTE_UNUSED)
+{
+ fragp->fr_var = 4;
+ return 4;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ return size;
+}
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment.
+
+ Here we fill the frag with the appropriate info for padding the
+ output stream. The resulting frag will consist of a fixed (fr_fix)
+ and of a repeating (fr_var) part.
+
+ The fixed content is always emitted before the repeating content and
+ these two parts are used as follows in constructing the output:
+ - the fixed part will be used to align to a valid instruction word
+ boundary, in case that we start at a misaligned address; as no
+ executable instruction can live at the misaligned location, we
+ simply fill with zeros;
+ - the variable part will be used to cover the remaining padding and
+ we fill using the AArch64 NOP instruction.
+
+ Note that the size of a RS_ALIGN_CODE fragment is always 7 to provide
+ enough storage space for up to 3 bytes for padding the back to a valid
+ instruction alignment and exactly 4 bytes to store the NOP pattern. */
+
+void
+aarch64_handle_align (fragS * fragP)
+{
+ /* NOP = d503201f */
+ /* AArch64 instructions are always little-endian. */
+ static char const aarch64_noop[4] = { 0x1f, 0x20, 0x03, 0xd5 };
+
+ int bytes, fix, noop_size;
+ char *p;
+
+ if (fragP->fr_type != rs_align_code)
+ return;
+
+ bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ p = fragP->fr_literal + fragP->fr_fix;
+
+#ifdef OBJ_ELF
+ gas_assert (fragP->tc_frag_data.recorded);
+#endif
+
+ noop_size = sizeof (aarch64_noop);
+
+ fix = bytes & (noop_size - 1);
+ if (fix)
+ {
+#ifdef OBJ_ELF
+ insert_data_mapping_symbol (MAP_INSN, fragP->fr_fix, fragP, fix);
+#endif
+ memset (p, 0, fix);
+ p += fix;
+ fragP->fr_fix += fix;
+ }
+
+ if (noop_size)
+ memcpy (p, aarch64_noop, noop_size);
+ fragP->fr_var = noop_size;
+}
+
+/* Perform target specific initialisation of a frag.
+ Note - despite the name this initialisation is not done when the frag
+ is created, but only when its type is assigned. A frag can be created
+ and used a long time before its type is set, so beware of assuming that
+ this initialisationis performed first. */
+
+#ifndef OBJ_ELF
+void
+aarch64_init_frag (fragS * fragP ATTRIBUTE_UNUSED,
+ int max_chars ATTRIBUTE_UNUSED)
+{
+}
+
+#else /* OBJ_ELF is defined. */
+void
+aarch64_init_frag (fragS * fragP, int max_chars)
+{
+ /* Record a mapping symbol for alignment frags. We will delete this
+ later if the alignment ends up empty. */
+ if (!fragP->tc_frag_data.recorded)
+ {
+ fragP->tc_frag_data.recorded = 1;
+ switch (fragP->fr_type)
+ {
+ case rs_align:
+ case rs_align_test:
+ case rs_fill:
+ mapping_state_2 (MAP_DATA, max_chars);
+ break;
+ case rs_align_code:
+ mapping_state_2 (MAP_INSN, max_chars);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure. */
+
+void
+tc_aarch64_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (REG_SP, 0);
+}
+#endif /* OBJ_ELF */
+
+/* Convert REGNAME to a DWARF-2 register number. */
+
+int
+tc_aarch64_regname_to_dw2regnum (char *regname)
+{
+ const reg_entry *reg = parse_reg (&regname);
+ if (reg == NULL)
+ return -1;
+
+ switch (reg->type)
+ {
+ case REG_TYPE_SP_32:
+ case REG_TYPE_SP_64:
+ case REG_TYPE_R_32:
+ case REG_TYPE_R_64:
+ return reg->number;
+
+ case REG_TYPE_FP_B:
+ case REG_TYPE_FP_H:
+ case REG_TYPE_FP_S:
+ case REG_TYPE_FP_D:
+ case REG_TYPE_FP_Q:
+ return reg->number + 64;
+
+ default:
+ break;
+ }
+ return -1;
+}
+
+/* Implement DWARF2_ADDR_SIZE. */
+
+int
+aarch64_dwarf2_addr_size (void)
+{
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+ if (ilp32_p)
+ return 4;
+#endif
+ return bfd_arch_bits_per_address (stdoutput) / 8;
+}
+
+/* MD interface: Symbol and relocation handling. */
+
+/* Return the address within the segment that a PC-relative fixup is
+ relative to. For AArch64 PC-relative fixups applied to instructions
+ are generally relative to the location plus AARCH64_PCREL_OFFSET bytes. */
+
+long
+md_pcrel_from_section (fixS * fixP, segT seg)
+{
+ offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ /* If this is pc-relative and we are going to emit a relocation
+ then we just want to put out any pipeline compensation that the linker
+ will need. Otherwise we want to use the calculated base. */
+ if (fixP->fx_pcrel
+ && ((fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ || aarch64_force_relocation (fixP)))
+ base = 0;
+
+ /* AArch64 should be consistent for all pc-relative relocations. */
+ return base + AARCH64_PCREL_OFFSET;
+}
+
+/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
+ Otherwise we have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ if (name[0] == '_' && name[1] == 'G'
+ && streq (name, GLOBAL_OFFSET_TABLE_NAME))
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in the symbol table"));
+
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ }
+
+ return GOT_symbol;
+ }
+#endif
+
+ return 0;
+}
+
+/* Return non-zero if the indicated VALUE has overflowed the maximum
+ range expressible by a unsigned number with the indicated number of
+ BITS. */
+
+static bfd_boolean
+unsigned_overflow (valueT value, unsigned bits)
+{
+ valueT lim;
+ if (bits >= sizeof (valueT) * 8)
+ return FALSE;
+ lim = (valueT) 1 << bits;
+ return (value >= lim);
+}
+
+
+/* Return non-zero if the indicated VALUE has overflowed the maximum
+ range expressible by an signed number with the indicated number of
+ BITS. */
+
+static bfd_boolean
+signed_overflow (offsetT value, unsigned bits)
+{
+ offsetT lim;
+ if (bits >= sizeof (offsetT) * 8)
+ return FALSE;
+ lim = (offsetT) 1 << (bits - 1);
+ return (value < -lim || value >= lim);
+}
+
+/* Given an instruction in *INST, which is expected to be a scaled, 12-bit,
+ unsigned immediate offset load/store instruction, try to encode it as
+ an unscaled, 9-bit, signed immediate offset load/store instruction.
+ Return TRUE if it is successful; otherwise return FALSE.
+
+ As a programmer-friendly assembler, LDUR/STUR instructions can be generated
+ in response to the standard LDR/STR mnemonics when the immediate offset is
+ unambiguous, i.e. when it is negative or unaligned. */
+
+static bfd_boolean
+try_to_encode_as_unscaled_ldst (aarch64_inst *instr)
+{
+ int idx;
+ enum aarch64_op new_op;
+ const aarch64_opcode *new_opcode;
+
+ gas_assert (instr->opcode->iclass == ldst_pos);
+
+ switch (instr->opcode->op)
+ {
+ case OP_LDRB_POS:new_op = OP_LDURB; break;
+ case OP_STRB_POS: new_op = OP_STURB; break;
+ case OP_LDRSB_POS: new_op = OP_LDURSB; break;
+ case OP_LDRH_POS: new_op = OP_LDURH; break;
+ case OP_STRH_POS: new_op = OP_STURH; break;
+ case OP_LDRSH_POS: new_op = OP_LDURSH; break;
+ case OP_LDR_POS: new_op = OP_LDUR; break;
+ case OP_STR_POS: new_op = OP_STUR; break;
+ case OP_LDRF_POS: new_op = OP_LDURV; break;
+ case OP_STRF_POS: new_op = OP_STURV; break;
+ case OP_LDRSW_POS: new_op = OP_LDURSW; break;
+ case OP_PRFM_POS: new_op = OP_PRFUM; break;
+ default: new_op = OP_NIL; break;
+ }
+
+ if (new_op == OP_NIL)
+ return FALSE;
+
+ new_opcode = aarch64_get_opcode (new_op);
+ gas_assert (new_opcode != NULL);
+
+ DEBUG_TRACE ("Check programmer-friendly STURB/LDURB -> STRB/LDRB: %d == %d",
+ instr->opcode->op, new_opcode->op);
+
+ aarch64_replace_opcode (instr, new_opcode);
+
+ /* Clear up the ADDR_SIMM9's qualifier; otherwise the
+ qualifier matching may fail because the out-of-date qualifier will
+ prevent the operand being updated with a new and correct qualifier. */
+ idx = aarch64_operand_index (instr->opcode->operands,
+ AARCH64_OPND_ADDR_SIMM9);
+ gas_assert (idx == 1);
+ instr->operands[idx].qualifier = AARCH64_OPND_QLF_NIL;
+
+ DEBUG_TRACE ("Found LDURB entry to encode programmer-friendly LDRB");
+
+ if (!aarch64_opcode_encode (instr->opcode, instr, &instr->value, NULL, NULL))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Called by fix_insn to fix a MOV immediate alias instruction.
+
+ Operand for a generic move immediate instruction, which is an alias
+ instruction that generates a single MOVZ, MOVN or ORR instruction to loads
+ a 32-bit/64-bit immediate value into general register. An assembler error
+ shall result if the immediate cannot be created by a single one of these
+ instructions. If there is a choice, then to ensure reversability an
+ assembler must prefer a MOVZ to MOVN, and MOVZ or MOVN to ORR. */
+
+static void
+fix_mov_imm_insn (fixS *fixP, char *buf, aarch64_inst *instr, offsetT value)
+{
+ const aarch64_opcode *opcode;
+
+ /* Need to check if the destination is SP/ZR. The check has to be done
+ before any aarch64_replace_opcode. */
+ int try_mov_wide_p = !aarch64_stack_pointer_p (&instr->operands[0]);
+ int try_mov_bitmask_p = !aarch64_zero_register_p (&instr->operands[0]);
+
+ instr->operands[1].imm.value = value;
+ instr->operands[1].skip = 0;
+
+ if (try_mov_wide_p)
+ {
+ /* Try the MOVZ alias. */
+ opcode = aarch64_get_opcode (OP_MOV_IMM_WIDE);
+ aarch64_replace_opcode (instr, opcode);
+ if (aarch64_opcode_encode (instr->opcode, instr,
+ &instr->value, NULL, NULL))
+ {
+ put_aarch64_insn (buf, instr->value);
+ return;
+ }
+ /* Try the MOVK alias. */
+ opcode = aarch64_get_opcode (OP_MOV_IMM_WIDEN);
+ aarch64_replace_opcode (instr, opcode);
+ if (aarch64_opcode_encode (instr->opcode, instr,
+ &instr->value, NULL, NULL))
+ {
+ put_aarch64_insn (buf, instr->value);
+ return;
+ }
+ }
+
+ if (try_mov_bitmask_p)
+ {
+ /* Try the ORR alias. */
+ opcode = aarch64_get_opcode (OP_MOV_IMM_LOG);
+ aarch64_replace_opcode (instr, opcode);
+ if (aarch64_opcode_encode (instr->opcode, instr,
+ &instr->value, NULL, NULL))
+ {
+ put_aarch64_insn (buf, instr->value);
+ return;
+ }
+ }
+
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate cannot be moved by a single instruction"));
+}
+
+/* An instruction operand which is immediate related may have symbol used
+ in the assembly, e.g.
+
+ mov w0, u32
+ .set u32, 0x00ffff00
+
+ At the time when the assembly instruction is parsed, a referenced symbol,
+ like 'u32' in the above example may not have been seen; a fixS is created
+ in such a case and is handled here after symbols have been resolved.
+ Instruction is fixed up with VALUE using the information in *FIXP plus
+ extra information in FLAGS.
+
+ This function is called by md_apply_fix to fix up instructions that need
+ a fix-up described above but does not involve any linker-time relocation. */
+
+static void
+fix_insn (fixS *fixP, uint32_t flags, offsetT value)
+{
+ int idx;
+ uint32_t insn;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ enum aarch64_opnd opnd = fixP->tc_fix_data.opnd;
+ aarch64_inst *new_inst = fixP->tc_fix_data.inst;
+
+ if (new_inst)
+ {
+ /* Now the instruction is about to be fixed-up, so the operand that
+ was previously marked as 'ignored' needs to be unmarked in order
+ to get the encoding done properly. */
+ idx = aarch64_operand_index (new_inst->opcode->operands, opnd);
+ new_inst->operands[idx].skip = 0;
+ }
+
+ gas_assert (opnd != AARCH64_OPND_NIL);
+
+ switch (opnd)
+ {
+ case AARCH64_OPND_EXCEPTION:
+ if (unsigned_overflow (value, 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_svc_imm (value);
+ put_aarch64_insn (buf, insn);
+ break;
+
+ case AARCH64_OPND_AIMM:
+ /* ADD or SUB with immediate.
+ NOTE this assumes we come here with a add/sub shifted reg encoding
+ 3 322|2222|2 2 2 21111 111111
+ 1 098|7654|3 2 1 09876 543210 98765 43210
+ 0b000000 sf 000|1011|shift 0 Rm imm6 Rn Rd ADD
+ 2b000000 sf 010|1011|shift 0 Rm imm6 Rn Rd ADDS
+ 4b000000 sf 100|1011|shift 0 Rm imm6 Rn Rd SUB
+ 6b000000 sf 110|1011|shift 0 Rm imm6 Rn Rd SUBS
+ ->
+ 3 322|2222|2 2 221111111111
+ 1 098|7654|3 2 109876543210 98765 43210
+ 11000000 sf 001|0001|shift imm12 Rn Rd ADD
+ 31000000 sf 011|0001|shift imm12 Rn Rd ADDS
+ 51000000 sf 101|0001|shift imm12 Rn Rd SUB
+ 71000000 sf 111|0001|shift imm12 Rn Rd SUBS
+ Fields sf Rn Rd are already set. */
+ insn = get_aarch64_insn (buf);
+ if (value < 0)
+ {
+ /* Add <-> sub. */
+ insn = reencode_addsub_switch_add_sub (insn);
+ value = -value;
+ }
+
+ if ((flags & FIXUP_F_HAS_EXPLICIT_SHIFT) == 0
+ && unsigned_overflow (value, 12))
+ {
+ /* Try to shift the value by 12 to make it fit. */
+ if (((value >> 12) << 12) == value
+ && ! unsigned_overflow (value, 12 + 12))
+ {
+ value >>= 12;
+ insn |= encode_addsub_imm_shift_amount (1);
+ }
+ }
+
+ if (unsigned_overflow (value, 12))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate out of range"));
+
+ insn |= encode_addsub_imm (value);
+
+ put_aarch64_insn (buf, insn);
+ break;
+
+ case AARCH64_OPND_SIMD_IMM:
+ case AARCH64_OPND_SIMD_IMM_SFT:
+ case AARCH64_OPND_LIMM:
+ /* Bit mask immediate. */
+ gas_assert (new_inst != NULL);
+ idx = aarch64_operand_index (new_inst->opcode->operands, opnd);
+ new_inst->operands[idx].imm.value = value;
+ if (aarch64_opcode_encode (new_inst->opcode, new_inst,
+ &new_inst->value, NULL, NULL))
+ put_aarch64_insn (buf, new_inst->value);
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid immediate"));
+ break;
+
+ case AARCH64_OPND_HALF:
+ /* 16-bit unsigned immediate. */
+ if (unsigned_overflow (value, 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_movw_imm (value & 0xffff);
+ put_aarch64_insn (buf, insn);
+ break;
+
+ case AARCH64_OPND_IMM_MOV:
+ /* Operand for a generic move immediate instruction, which is
+ an alias instruction that generates a single MOVZ, MOVN or ORR
+ instruction to loads a 32-bit/64-bit immediate value into general
+ register. An assembler error shall result if the immediate cannot be
+ created by a single one of these instructions. If there is a choice,
+ then to ensure reversability an assembler must prefer a MOVZ to MOVN,
+ and MOVZ or MOVN to ORR. */
+ gas_assert (new_inst != NULL);
+ fix_mov_imm_insn (fixP, buf, new_inst, value);
+ break;
+
+ case AARCH64_OPND_ADDR_SIMM7:
+ case AARCH64_OPND_ADDR_SIMM9:
+ case AARCH64_OPND_ADDR_SIMM9_2:
+ case AARCH64_OPND_ADDR_UIMM12:
+ /* Immediate offset in an address. */
+ insn = get_aarch64_insn (buf);
+
+ gas_assert (new_inst != NULL && new_inst->value == insn);
+ gas_assert (new_inst->opcode->operands[1] == opnd
+ || new_inst->opcode->operands[2] == opnd);
+
+ /* Get the index of the address operand. */
+ if (new_inst->opcode->operands[1] == opnd)
+ /* e.g. STR <Xt>, [<Xn|SP>, <R><m>{, <extend> {<amount>}}]. */
+ idx = 1;
+ else
+ /* e.g. LDP <Qt1>, <Qt2>, [<Xn|SP>{, #<imm>}]. */
+ idx = 2;
+
+ /* Update the resolved offset value. */
+ new_inst->operands[idx].addr.offset.imm = value;
+
+ /* Encode/fix-up. */
+ if (aarch64_opcode_encode (new_inst->opcode, new_inst,
+ &new_inst->value, NULL, NULL))
+ {
+ put_aarch64_insn (buf, new_inst->value);
+ break;
+ }
+ else if (new_inst->opcode->iclass == ldst_pos
+ && try_to_encode_as_unscaled_ldst (new_inst))
+ {
+ put_aarch64_insn (buf, new_inst->value);
+ break;
+ }
+
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset out of range"));
+ break;
+
+ default:
+ gas_assert (0);
+ as_fatal (_("unhandled operand code %d"), opnd);
+ }
+}
+
+/* Apply a fixup (fixP) to segment data, once it has been determined
+ by our caller that we have all the info we need to fix it up.
+
+ Parameter valP is the pointer to the value of the bits. */
+
+void
+md_apply_fix (fixS * fixP, valueT * valP, segT seg)
+{
+ offsetT value = *valP;
+ uint32_t insn;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int scale;
+ unsigned flags = fixP->fx_addnumber;
+
+ DEBUG_TRACE ("\n\n");
+ DEBUG_TRACE ("~~~~~~~~~~~~~~~~~~~~~~~~~");
+ DEBUG_TRACE ("Enter md_apply_fix");
+
+ gas_assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
+
+ /* Note whether this will delete the relocation. */
+
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ fixP->fx_done = 1;
+
+ /* Process the relocations. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NONE:
+ /* This will need to go in the object file. */
+ fixP->fx_done = 0;
+ break;
+
+ case BFD_RELOC_8:
+ case BFD_RELOC_8_PCREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 1);
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 2);
+ break;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 4);
+ break;
+
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 8);
+ break;
+
+ case BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP:
+ /* We claim that these fixups have been processed here, even if
+ in fact we generate an error because we do not have a reloc
+ for them, so tc_gen_reloc() will reject them. */
+ fixP->fx_done = 1;
+ if (fixP->fx_addsy && !S_IS_DEFINED (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("undefined symbol %s used as an immediate value"),
+ S_GET_NAME (fixP->fx_addsy));
+ goto apply_fix_return;
+ }
+ fix_insn (fixP, flags, value);
+ break;
+
+ case BFD_RELOC_AARCH64_LD_LO19_PCREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pc-relative load offset not word aligned"));
+ if (signed_overflow (value, 21))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pc-relative load offset out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_ld_lit_ofs_19 (value >> 2);
+ put_aarch64_insn (buf, insn);
+ }
+ break;
+
+ case BFD_RELOC_AARCH64_ADR_LO21_PCREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (signed_overflow (value, 21))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pc-relative address offset out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_adr_imm (value);
+ put_aarch64_insn (buf, insn);
+ }
+ break;
+
+ case BFD_RELOC_AARCH64_BRANCH19:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch target not word aligned"));
+ if (signed_overflow (value, 21))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_cond_branch_ofs_19 (value >> 2);
+ put_aarch64_insn (buf, insn);
+ }
+ break;
+
+ case BFD_RELOC_AARCH64_TSTBR14:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch target not word aligned"));
+ if (signed_overflow (value, 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_tst_branch_ofs_14 (value >> 2);
+ put_aarch64_insn (buf, insn);
+ }
+ break;
+
+ case BFD_RELOC_AARCH64_JUMP26:
+ case BFD_RELOC_AARCH64_CALL26:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch target not word aligned"));
+ if (signed_overflow (value, 28))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
+ insn = get_aarch64_insn (buf);
+ insn |= encode_branch_ofs_26 (value >> 2);
+ put_aarch64_insn (buf, insn);
+ }
+ break;
+
+ case BFD_RELOC_AARCH64_MOVW_G0:
+ case BFD_RELOC_AARCH64_MOVW_G0_S:
+ case BFD_RELOC_AARCH64_MOVW_G0_NC:
+ scale = 0;
+ goto movw_common;
+ case BFD_RELOC_AARCH64_MOVW_G1:
+ case BFD_RELOC_AARCH64_MOVW_G1_S:
+ case BFD_RELOC_AARCH64_MOVW_G1_NC:
+ scale = 16;
+ goto movw_common;
+ case BFD_RELOC_AARCH64_MOVW_G2:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
+ case BFD_RELOC_AARCH64_MOVW_G2_NC:
+ scale = 32;
+ goto movw_common;
+ case BFD_RELOC_AARCH64_MOVW_G3:
+ scale = 48;
+ movw_common:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ insn = get_aarch64_insn (buf);
+
+ if (!fixP->fx_done)
+ {
+ /* REL signed addend must fit in 16 bits */
+ if (signed_overflow (value, 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ }
+ else
+ {
+ /* Check for overflow and scale. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_AARCH64_MOVW_G0:
+ case BFD_RELOC_AARCH64_MOVW_G1:
+ case BFD_RELOC_AARCH64_MOVW_G2:
+ case BFD_RELOC_AARCH64_MOVW_G3:
+ if (unsigned_overflow (value, scale + 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsigned value out of range"));
+ break;
+ case BFD_RELOC_AARCH64_MOVW_G0_S:
+ case BFD_RELOC_AARCH64_MOVW_G1_S:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
+ /* NOTE: We can only come here with movz or movn. */
+ if (signed_overflow (value, scale + 16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("signed value out of range"));
+ if (value < 0)
+ {
+ /* Force use of MOVN. */
+ value = ~value;
+ insn = reencode_movzn_to_movn (insn);
+ }
+ else
+ {
+ /* Force use of MOVZ. */
+ insn = reencode_movzn_to_movz (insn);
+ }
+ break;
+ default:
+ /* Unchecked relocations. */
+ break;
+ }
+ value >>= scale;
+ }
+
+ /* Insert value into MOVN/MOVZ/MOVK instruction. */
+ insn |= encode_movw_imm (value & 0xffff);
+
+ put_aarch64_insn (buf, insn);
+ }
+ break;
+
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC:
+ fixP->fx_r_type = (ilp32_p
+ ? BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC
+ : BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC);
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC:
+ fixP->fx_r_type = (ilp32_p
+ ? BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC
+ : BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC);
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_LD_GOT_LO12_NC:
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ fixP->fx_r_type = (ilp32_p
+ ? BFD_RELOC_AARCH64_LD32_GOT_LO12_NC
+ : BFD_RELOC_AARCH64_LD64_GOT_LO12_NC);
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+ case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+ case BFD_RELOC_AARCH64_ADD_LO12:
+ case BFD_RELOC_AARCH64_LDST8_LO12:
+ case BFD_RELOC_AARCH64_LDST16_LO12:
+ case BFD_RELOC_AARCH64_LDST32_LO12:
+ case BFD_RELOC_AARCH64_LDST64_LO12:
+ case BFD_RELOC_AARCH64_LDST128_LO12:
+ case BFD_RELOC_AARCH64_GOT_LD_PREL19:
+ case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+ case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ /* Should always be exported to object file, see
+ aarch64_force_relocation(). */
+ gas_assert (!fixP->fx_done);
+ gas_assert (seg->use_rela_p);
+ break;
+
+ case BFD_RELOC_AARCH64_TLSDESC_ADD:
+ case BFD_RELOC_AARCH64_TLSDESC_LDR:
+ case BFD_RELOC_AARCH64_TLSDESC_CALL:
+ break;
+
+ case BFD_RELOC_UNUSED:
+ /* An error will already have been reported. */
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unexpected %s fixup"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
+ }
+
+apply_fix_return:
+ /* Free the allocated the struct aarch64_inst.
+ N.B. currently there are very limited number of fix-up types actually use
+ this field, so the impact on the performance should be minimal . */
+ if (fixP->tc_fix_data.inst != NULL)
+ free (fixP->tc_fix_data.inst);
+
+ return;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection * section, fixS * fixp)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_pcrel)
+ {
+ if (section->use_rela_p)
+ fixp->fx_offset -= md_pcrel_from_section (fixp, section);
+ else
+ fixp->fx_offset = reloc->address;
+ }
+ reloc->addend = fixp->fx_offset;
+
+ code = fixp->fx_r_type;
+ switch (code)
+ {
+ case BFD_RELOC_16:
+ if (fixp->fx_pcrel)
+ code = BFD_RELOC_16_PCREL;
+ break;
+
+ case BFD_RELOC_32:
+ if (fixp->fx_pcrel)
+ code = BFD_RELOC_32_PCREL;
+ break;
+
+ case BFD_RELOC_64:
+ if (fixp->fx_pcrel)
+ code = BFD_RELOC_64_PCREL;
+ break;
+
+ default:
+ break;
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _
+ ("cannot represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ return reloc;
+}
+
+/* This fix_new is called by cons via TC_CONS_FIX_NEW. */
+
+void
+cons_fix_new_aarch64 (fragS * frag, int where, int size, expressionS * exp)
+{
+ bfd_reloc_code_real_type type;
+ int pcrel = 0;
+
+ /* Pick a reloc.
+ FIXME: @@ Should look at CPU word size. */
+ switch (size)
+ {
+ case 1:
+ type = BFD_RELOC_8;
+ break;
+ case 2:
+ type = BFD_RELOC_16;
+ break;
+ case 4:
+ type = BFD_RELOC_32;
+ break;
+ case 8:
+ type = BFD_RELOC_64;
+ break;
+ default:
+ as_bad (_("cannot do %u-byte relocation"), size);
+ type = BFD_RELOC_UNUSED;
+ break;
+ }
+
+ fix_new_exp (frag, where, (int) size, exp, pcrel, type);
+}
+
+int
+aarch64_force_relocation (struct fix *fixp)
+{
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP:
+ /* Perform these "immediate" internal relocations
+ even if the symbol is extern or weak. */
+ return 0;
+
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC:
+ case BFD_RELOC_AARCH64_LD_GOT_LO12_NC:
+ /* Pseudo relocs that need to be fixed up according to
+ ilp32_p. */
+ return 0;
+
+ case BFD_RELOC_AARCH64_ADD_LO12:
+ case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+ case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+ case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+ case BFD_RELOC_AARCH64_GOT_LD_PREL19:
+ case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LDST128_LO12:
+ case BFD_RELOC_AARCH64_LDST16_LO12:
+ case BFD_RELOC_AARCH64_LDST32_LO12:
+ case BFD_RELOC_AARCH64_LDST64_LO12:
+ case BFD_RELOC_AARCH64_LDST8_LO12:
+ case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
+ case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
+ case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
+ /* Always leave these relocations for the linker. */
+ return 1;
+
+ default:
+ break;
+ }
+
+ return generic_force_reloc (fixp);
+}
+
+#ifdef OBJ_ELF
+
+const char *
+elf64_aarch64_target_format (void)
+{
+ if (target_big_endian)
+ return ilp32_p ? "elf32-bigaarch64" : "elf64-bigaarch64";
+ else
+ return ilp32_p ? "elf32-littleaarch64" : "elf64-littleaarch64";
+}
+
+void
+aarch64elf_frob_symbol (symbolS * symp, int *puntp)
+{
+ elf_frob_symbol (symp, puntp);
+}
+#endif
+
+/* MD interface: Finalization. */
+
+/* A good place to do this, although this was probably not intended
+ for this kind of use. We need to dump the literal pool before
+ references are made to a null symbol pointer. */
+
+void
+aarch64_cleanup (void)
+{
+ literal_pool *pool;
+
+ for (pool = list_of_pools; pool; pool = pool->next)
+ {
+ /* Put it at the end of the relevant section. */
+ subseg_set (pool->section, pool->sub_section);
+ s_ltorg (0);
+ }
+}
+
+#ifdef OBJ_ELF
+/* Remove any excess mapping symbols generated for alignment frags in
+ SEC. We may have created a mapping symbol before a zero byte
+ alignment; remove it if there's a mapping symbol after the
+ alignment. */
+static void
+check_mapping_symbols (bfd * abfd ATTRIBUTE_UNUSED, asection * sec,
+ void *dummy ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragp;
+
+ if (seginfo == NULL || seginfo->frchainP == NULL)
+ return;
+
+ for (fragp = seginfo->frchainP->frch_root;
+ fragp != NULL; fragp = fragp->fr_next)
+ {
+ symbolS *sym = fragp->tc_frag_data.last_map;
+ fragS *next = fragp->fr_next;
+
+ /* Variable-sized frags have been converted to fixed size by
+ this point. But if this was variable-sized to start with,
+ there will be a fixed-size frag after it. So don't handle
+ next == NULL. */
+ if (sym == NULL || next == NULL)
+ continue;
+
+ if (S_GET_VALUE (sym) < next->fr_address)
+ /* Not at the end of this frag. */
+ continue;
+ know (S_GET_VALUE (sym) == next->fr_address);
+
+ do
+ {
+ if (next->tc_frag_data.first_map != NULL)
+ {
+ /* Next frag starts with a mapping symbol. Discard this
+ one. */
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ if (next->fr_next == NULL)
+ {
+ /* This mapping symbol is at the end of the section. Discard
+ it. */
+ know (next->fr_fix == 0 && next->fr_var == 0);
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ /* As long as we have empty frags without any mapping symbols,
+ keep looking. */
+ /* If the next frag is non-empty and does not start with a
+ mapping symbol, then this mapping symbol is required. */
+ if (next->fr_address != next->fr_next->fr_address)
+ break;
+
+ next = next->fr_next;
+ }
+ while (next != NULL);
+ }
+}
+#endif
+
+/* Adjust the symbol table. */
+
+void
+aarch64_adjust_symtab (void)
+{
+#ifdef OBJ_ELF
+ /* Remove any overlapping mapping symbols generated by alignment frags. */
+ bfd_map_over_sections (stdoutput, check_mapping_symbols, (char *) 0);
+ /* Now do generic ELF adjustments. */
+ elf_adjust_symtab ();
+#endif
+}
+
+static void
+checked_hash_insert (struct hash_control *table, const char *key, void *value)
+{
+ const char *hash_err;
+
+ hash_err = hash_insert (table, key, value);
+ if (hash_err)
+ printf ("Internal Error: Can't hash %s\n", key);
+}
+
+static void
+fill_instruction_hash_table (void)
+{
+ aarch64_opcode *opcode = aarch64_opcode_table;
+
+ while (opcode->name != NULL)
+ {
+ templates *templ, *new_templ;
+ templ = hash_find (aarch64_ops_hsh, opcode->name);
+
+ new_templ = (templates *) xmalloc (sizeof (templates));
+ new_templ->opcode = opcode;
+ new_templ->next = NULL;
+
+ if (!templ)
+ checked_hash_insert (aarch64_ops_hsh, opcode->name, (void *) new_templ);
+ else
+ {
+ new_templ->next = templ->next;
+ templ->next = new_templ;
+ }
+ ++opcode;
+ }
+}
+
+static inline void
+convert_to_upper (char *dst, const char *src, size_t num)
+{
+ unsigned int i;
+ for (i = 0; i < num && *src != '\0'; ++i, ++dst, ++src)
+ *dst = TOUPPER (*src);
+ *dst = '\0';
+}
+
+/* Assume STR point to a lower-case string, allocate, convert and return
+ the corresponding upper-case string. */
+static inline const char*
+get_upper_str (const char *str)
+{
+ char *ret;
+ size_t len = strlen (str);
+ if ((ret = xmalloc (len + 1)) == NULL)
+ abort ();
+ convert_to_upper (ret, str, len);
+ return ret;
+}
+
+/* MD interface: Initialization. */
+
+void
+md_begin (void)
+{
+ unsigned mach;
+ unsigned int i;
+
+ if ((aarch64_ops_hsh = hash_new ()) == NULL
+ || (aarch64_cond_hsh = hash_new ()) == NULL
+ || (aarch64_shift_hsh = hash_new ()) == NULL
+ || (aarch64_sys_regs_hsh = hash_new ()) == NULL
+ || (aarch64_pstatefield_hsh = hash_new ()) == NULL
+ || (aarch64_sys_regs_ic_hsh = hash_new ()) == NULL
+ || (aarch64_sys_regs_dc_hsh = hash_new ()) == NULL
+ || (aarch64_sys_regs_at_hsh = hash_new ()) == NULL
+ || (aarch64_sys_regs_tlbi_hsh = hash_new ()) == NULL
+ || (aarch64_reg_hsh = hash_new ()) == NULL
+ || (aarch64_barrier_opt_hsh = hash_new ()) == NULL
+ || (aarch64_nzcv_hsh = hash_new ()) == NULL
+ || (aarch64_pldop_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ fill_instruction_hash_table ();
+
+ for (i = 0; aarch64_sys_regs[i].name != NULL; ++i)
+ checked_hash_insert (aarch64_sys_regs_hsh, aarch64_sys_regs[i].name,
+ (void *) (aarch64_sys_regs + i));
+
+ for (i = 0; aarch64_pstatefields[i].name != NULL; ++i)
+ checked_hash_insert (aarch64_pstatefield_hsh,
+ aarch64_pstatefields[i].name,
+ (void *) (aarch64_pstatefields + i));
+
+ for (i = 0; aarch64_sys_regs_ic[i].template != NULL; i++)
+ checked_hash_insert (aarch64_sys_regs_ic_hsh,
+ aarch64_sys_regs_ic[i].template,
+ (void *) (aarch64_sys_regs_ic + i));
+
+ for (i = 0; aarch64_sys_regs_dc[i].template != NULL; i++)
+ checked_hash_insert (aarch64_sys_regs_dc_hsh,
+ aarch64_sys_regs_dc[i].template,
+ (void *) (aarch64_sys_regs_dc + i));
+
+ for (i = 0; aarch64_sys_regs_at[i].template != NULL; i++)
+ checked_hash_insert (aarch64_sys_regs_at_hsh,
+ aarch64_sys_regs_at[i].template,
+ (void *) (aarch64_sys_regs_at + i));
+
+ for (i = 0; aarch64_sys_regs_tlbi[i].template != NULL; i++)
+ checked_hash_insert (aarch64_sys_regs_tlbi_hsh,
+ aarch64_sys_regs_tlbi[i].template,
+ (void *) (aarch64_sys_regs_tlbi + i));
+
+ for (i = 0; i < ARRAY_SIZE (reg_names); i++)
+ checked_hash_insert (aarch64_reg_hsh, reg_names[i].name,
+ (void *) (reg_names + i));
+
+ for (i = 0; i < ARRAY_SIZE (nzcv_names); i++)
+ checked_hash_insert (aarch64_nzcv_hsh, nzcv_names[i].template,
+ (void *) (nzcv_names + i));
+
+ for (i = 0; aarch64_operand_modifiers[i].name != NULL; i++)
+ {
+ const char *name = aarch64_operand_modifiers[i].name;
+ checked_hash_insert (aarch64_shift_hsh, name,
+ (void *) (aarch64_operand_modifiers + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_shift_hsh, get_upper_str (name),
+ (void *) (aarch64_operand_modifiers + i));
+ }
+
+ for (i = 0; i < ARRAY_SIZE (aarch64_conds); i++)
+ {
+ unsigned int j;
+ /* A condition code may have alias(es), e.g. "cc", "lo" and "ul" are
+ the same condition code. */
+ for (j = 0; j < ARRAY_SIZE (aarch64_conds[i].names); ++j)
+ {
+ const char *name = aarch64_conds[i].names[j];
+ if (name == NULL)
+ break;
+ checked_hash_insert (aarch64_cond_hsh, name,
+ (void *) (aarch64_conds + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_cond_hsh, get_upper_str (name),
+ (void *) (aarch64_conds + i));
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE (aarch64_barrier_options); i++)
+ {
+ const char *name = aarch64_barrier_options[i].name;
+ /* Skip xx00 - the unallocated values of option. */
+ if ((i & 0x3) == 0)
+ continue;
+ checked_hash_insert (aarch64_barrier_opt_hsh, name,
+ (void *) (aarch64_barrier_options + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_barrier_opt_hsh, get_upper_str (name),
+ (void *) (aarch64_barrier_options + i));
+ }
+
+ for (i = 0; i < ARRAY_SIZE (aarch64_prfops); i++)
+ {
+ const char* name = aarch64_prfops[i].name;
+ /* Skip the unallocated hint encodings. */
+ if (name == NULL)
+ continue;
+ checked_hash_insert (aarch64_pldop_hsh, name,
+ (void *) (aarch64_prfops + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_pldop_hsh, get_upper_str (name),
+ (void *) (aarch64_prfops + i));
+ }
+
+ /* Set the cpu variant based on the command-line options. */
+ if (!mcpu_cpu_opt)
+ mcpu_cpu_opt = march_cpu_opt;
+
+ if (!mcpu_cpu_opt)
+ mcpu_cpu_opt = &cpu_default;
+
+ cpu_variant = *mcpu_cpu_opt;
+
+ /* Record the CPU type. */
+ mach = ilp32_p ? bfd_mach_aarch64_ilp32 : bfd_mach_aarch64;
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+}
+
+/* Command line processing. */
+
+const char *md_shortopts = "m:";
+
+#ifdef AARCH64_BI_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#else
+#if TARGET_BYTES_BIG_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#else
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#endif
+#endif
+
+struct option md_longopts[] = {
+#ifdef OPTION_EB
+ {"EB", no_argument, NULL, OPTION_EB},
+#endif
+#ifdef OPTION_EL
+ {"EL", no_argument, NULL, OPTION_EL},
+#endif
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+struct aarch64_option_table
+{
+ char *option; /* Option name to match. */
+ char *help; /* Help information. */
+ int *var; /* Variable to change. */
+ int value; /* What to change it to. */
+ char *deprecated; /* If non-null, print this message. */
+};
+
+static struct aarch64_option_table aarch64_opts[] = {
+ {"mbig-endian", N_("assemble for big-endian"), &target_big_endian, 1, NULL},
+ {"mlittle-endian", N_("assemble for little-endian"), &target_big_endian, 0,
+ NULL},
+#ifdef DEBUG_AARCH64
+ {"mdebug-dump", N_("temporary switch for dumping"), &debug_dump, 1, NULL},
+#endif /* DEBUG_AARCH64 */
+ {"mverbose-error", N_("output verbose error messages"), &verbose_error_p, 1,
+ NULL},
+ {"mno-verbose-error", N_("do not output verbose error messages"),
+ &verbose_error_p, 0, NULL},
+ {NULL, NULL, NULL, 0, NULL}
+};
+
+struct aarch64_cpu_option_table
+{
+ char *name;
+ const aarch64_feature_set value;
+ /* The canonical name of the CPU, or NULL to use NAME converted to upper
+ case. */
+ const char *canonical_name;
+};
+
+/* This list should, at a minimum, contain all the cpu names
+ recognized by GCC. */
+static const struct aarch64_cpu_option_table aarch64_cpus[] = {
+ {"all", AARCH64_ANY, NULL},
+ {"cortex-a53", AARCH64_FEATURE(AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A53"},
+ {"cortex-a57", AARCH64_FEATURE(AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "Cortex-A57"},
+ /* The 'xgene-1' name is an older name for 'xgene1', which was used
+ in earlier releases and is superseded by 'xgene1' in all
+ tools. */
+ {"xgene-1", AARCH64_ARCH_V8, "APM X-Gene 1"},
+ {"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"},
+ {"xgene2", AARCH64_FEATURE(AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "APM X-Gene 2"},
+ {"generic", AARCH64_ARCH_V8, NULL},
+
+ /* These two are example CPUs supported in GCC, once we have real
+ CPUs they will be removed. */
+ {"example-1", AARCH64_ARCH_V8, NULL},
+ {"example-2", AARCH64_ARCH_V8, NULL},
+
+ {NULL, AARCH64_ARCH_NONE, NULL}
+};
+
+struct aarch64_arch_option_table
+{
+ char *name;
+ const aarch64_feature_set value;
+};
+
+/* This list should, at a minimum, contain all the architecture names
+ recognized by GCC. */
+static const struct aarch64_arch_option_table aarch64_archs[] = {
+ {"all", AARCH64_ANY},
+ {"armv8-a", AARCH64_ARCH_V8},
+ {NULL, AARCH64_ARCH_NONE}
+};
+
+/* ISA extensions. */
+struct aarch64_option_cpu_value_table
+{
+ char *name;
+ const aarch64_feature_set value;
+};
+
+static const struct aarch64_option_cpu_value_table aarch64_features[] = {
+ {"crc", AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0)},
+ {"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0)},
+ {"fp", AARCH64_FEATURE (AARCH64_FEATURE_FP, 0)},
+ {"lse", AARCH64_FEATURE (AARCH64_FEATURE_LSE, 0)},
+ {"simd", AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)},
+ {NULL, AARCH64_ARCH_NONE}
+};
+
+struct aarch64_long_option_table
+{
+ char *option; /* Substring to match. */
+ char *help; /* Help information. */
+ int (*func) (char *subopt); /* Function to decode sub-option. */
+ char *deprecated; /* If non-null, print this message. */
+};
+
+static int
+aarch64_parse_features (char *str, const aarch64_feature_set **opt_p)
+{
+ /* We insist on extensions being added before being removed. We achieve
+ this by using the ADDING_VALUE variable to indicate whether we are
+ adding an extension (1) or removing it (0) and only allowing it to
+ change in the order -1 -> 1 -> 0. */
+ int adding_value = -1;
+ aarch64_feature_set *ext_set = xmalloc (sizeof (aarch64_feature_set));
+
+ /* Copy the feature set, so that we can modify it. */
+ *ext_set = **opt_p;
+ *opt_p = ext_set;
+
+ while (str != NULL && *str != 0)
+ {
+ const struct aarch64_option_cpu_value_table *opt;
+ char *ext;
+ int optlen;
+
+ if (*str != '+')
+ {
+ as_bad (_("invalid architectural extension"));
+ return 0;
+ }
+
+ str++;
+ ext = strchr (str, '+');
+
+ if (ext != NULL)
+ optlen = ext - str;
+ else
+ optlen = strlen (str);
+
+ if (optlen >= 2 && strncmp (str, "no", 2) == 0)
+ {
+ if (adding_value != 0)
+ adding_value = 0;
+ optlen -= 2;
+ str += 2;
+ }
+ else if (optlen > 0)
+ {
+ if (adding_value == -1)
+ adding_value = 1;
+ else if (adding_value != 1)
+ {
+ as_bad (_("must specify extensions to add before specifying "
+ "those to remove"));
+ return FALSE;
+ }
+ }
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing architectural extension"));
+ return 0;
+ }
+
+ gas_assert (adding_value != -1);
+
+ for (opt = aarch64_features; opt->name != NULL; opt++)
+ if (strncmp (opt->name, str, optlen) == 0)
+ {
+ /* Add or remove the extension. */
+ if (adding_value)
+ AARCH64_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value);
+ else
+ AARCH64_CLEAR_FEATURE (*ext_set, *ext_set, opt->value);
+ break;
+ }
+
+ if (opt->name == NULL)
+ {
+ as_bad (_("unknown architectural extension `%s'"), str);
+ return 0;
+ }
+
+ str = ext;
+ };
+
+ return 1;
+}
+
+static int
+aarch64_parse_cpu (char *str)
+{
+ const struct aarch64_cpu_option_table *opt;
+ char *ext = strchr (str, '+');
+ size_t optlen;
+
+ if (ext != NULL)
+ optlen = ext - str;
+ else
+ optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing cpu name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = aarch64_cpus; opt->name != NULL; opt++)
+ if (strlen (opt->name) == optlen && strncmp (str, opt->name, optlen) == 0)
+ {
+ mcpu_cpu_opt = &opt->value;
+ if (ext != NULL)
+ return aarch64_parse_features (ext, &mcpu_cpu_opt);
+
+ return 1;
+ }
+
+ as_bad (_("unknown cpu `%s'"), str);
+ return 0;
+}
+
+static int
+aarch64_parse_arch (char *str)
+{
+ const struct aarch64_arch_option_table *opt;
+ char *ext = strchr (str, '+');
+ size_t optlen;
+
+ if (ext != NULL)
+ optlen = ext - str;
+ else
+ optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing architecture name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = aarch64_archs; opt->name != NULL; opt++)
+ if (strlen (opt->name) == optlen && strncmp (str, opt->name, optlen) == 0)
+ {
+ march_cpu_opt = &opt->value;
+ if (ext != NULL)
+ return aarch64_parse_features (ext, &march_cpu_opt);
+
+ return 1;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), str);
+ return 0;
+}
+
+/* ABIs. */
+struct aarch64_option_abi_value_table
+{
+ char *name;
+ enum aarch64_abi_type value;
+};
+
+static const struct aarch64_option_abi_value_table aarch64_abis[] = {
+ {"ilp32", AARCH64_ABI_ILP32},
+ {"lp64", AARCH64_ABI_LP64},
+ {NULL, 0}
+};
+
+static int
+aarch64_parse_abi (char *str)
+{
+ const struct aarch64_option_abi_value_table *opt;
+ size_t optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing abi name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = aarch64_abis; opt->name != NULL; opt++)
+ if (strlen (opt->name) == optlen && strncmp (str, opt->name, optlen) == 0)
+ {
+ aarch64_abi = opt->value;
+ return 1;
+ }
+
+ as_bad (_("unknown abi `%s'\n"), str);
+ return 0;
+}
+
+static struct aarch64_long_option_table aarch64_long_opts[] = {
+#ifdef OBJ_ELF
+ {"mabi=", N_("<abi name>\t specify for ABI <abi name>"),
+ aarch64_parse_abi, NULL},
+#endif /* OBJ_ELF */
+ {"mcpu=", N_("<cpu name>\t assemble for CPU <cpu name>"),
+ aarch64_parse_cpu, NULL},
+ {"march=", N_("<arch name>\t assemble for architecture <arch name>"),
+ aarch64_parse_arch, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
+int
+md_parse_option (int c, char *arg)
+{
+ struct aarch64_option_table *opt;
+ struct aarch64_long_option_table *lopt;
+
+ switch (c)
+ {
+#ifdef OPTION_EB
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+#endif
+
+#ifdef OPTION_EL
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+#endif
+
+ case 'a':
+ /* Listing option. Just ignore these, we don't support additional
+ ones. */
+ return 0;
+
+ default:
+ for (opt = aarch64_opts; opt->option != NULL; opt++)
+ {
+ if (c == opt->option[0]
+ && ((arg == NULL && opt->option[1] == 0)
+ || streq (arg, opt->option + 1)))
+ {
+ /* If the option is deprecated, tell the user. */
+ if (opt->deprecated != NULL)
+ as_tsktsk (_("option `-%c%s' is deprecated: %s"), c,
+ arg ? arg : "", _(opt->deprecated));
+
+ if (opt->var != NULL)
+ *opt->var = opt->value;
+
+ return 1;
+ }
+ }
+
+ for (lopt = aarch64_long_opts; lopt->option != NULL; lopt++)
+ {
+ /* These options are expected to have an argument. */
+ if (c == lopt->option[0]
+ && arg != NULL
+ && strncmp (arg, lopt->option + 1,
+ strlen (lopt->option + 1)) == 0)
+ {
+ /* If the option is deprecated, tell the user. */
+ if (lopt->deprecated != NULL)
+ as_tsktsk (_("option `-%c%s' is deprecated: %s"), c, arg,
+ _(lopt->deprecated));
+
+ /* Call the sup-option parser. */
+ return lopt->func (arg + strlen (lopt->option) - 1);
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * fp)
+{
+ struct aarch64_option_table *opt;
+ struct aarch64_long_option_table *lopt;
+
+ fprintf (fp, _(" AArch64-specific assembler options:\n"));
+
+ for (opt = aarch64_opts; opt->option != NULL; opt++)
+ if (opt->help != NULL)
+ fprintf (fp, " -%-23s%s\n", opt->option, _(opt->help));
+
+ for (lopt = aarch64_long_opts; lopt->option != NULL; lopt++)
+ if (lopt->help != NULL)
+ fprintf (fp, " -%s%s\n", lopt->option, _(lopt->help));
+
+#ifdef OPTION_EB
+ fprintf (fp, _("\
+ -EB assemble code for a big-endian cpu\n"));
+#endif
+
+#ifdef OPTION_EL
+ fprintf (fp, _("\
+ -EL assemble code for a little-endian cpu\n"));
+#endif
+}
+
+/* Parse a .cpu directive. */
+
+static void
+s_aarch64_cpu (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct aarch64_cpu_option_table *opt;
+ char saved_char;
+ char *name;
+ char *ext;
+ size_t optlen;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ ext = strchr (name, '+');
+
+ if (ext != NULL)
+ optlen = ext - name;
+ else
+ optlen = strlen (name);
+
+ /* Skip the first "all" entry. */
+ for (opt = aarch64_cpus + 1; opt->name != NULL; opt++)
+ if (strlen (opt->name) == optlen
+ && strncmp (name, opt->name, optlen) == 0)
+ {
+ mcpu_cpu_opt = &opt->value;
+ if (ext != NULL)
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt))
+ return;
+
+ cpu_variant = *mcpu_cpu_opt;
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ as_bad (_("unknown cpu `%s'"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+
+/* Parse a .arch directive. */
+
+static void
+s_aarch64_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct aarch64_arch_option_table *opt;
+ char saved_char;
+ char *name;
+ char *ext;
+ size_t optlen;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ ext = strchr (name, '+');
+
+ if (ext != NULL)
+ optlen = ext - name;
+ else
+ optlen = strlen (name);
+
+ /* Skip the first "all" entry. */
+ for (opt = aarch64_archs + 1; opt->name != NULL; opt++)
+ if (strlen (opt->name) == optlen
+ && strncmp (name, opt->name, optlen) == 0)
+ {
+ mcpu_cpu_opt = &opt->value;
+ if (ext != NULL)
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt))
+ return;
+
+ cpu_variant = *mcpu_cpu_opt;
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+/* Copy symbol information. */
+
+void
+aarch64_copy_symbol_attributes (symbolS * dest, symbolS * src)
+{
+ AARCH64_GET_FLAG (dest) = AARCH64_GET_FLAG (src);
+}
diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
new file mode 100644
index 0000000..1fad6ce
--- /dev/null
+++ b/gas/config/tc-aarch64.h
@@ -0,0 +1,235 @@
+/* tc-aarch64.h -- Header file for tc-aarch64.c.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Contributed by ARM Ltd.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the license, 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 this program; see the file COPYING3. If not,
+ see <http://www.gnu.org/licenses/>. */
+
+#ifndef TC_AARCH64
+#define TC_AARCH64 1
+
+#include "opcode/aarch64.h"
+
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 0
+#endif
+
+#define WORKING_DOT_WORD
+
+#define TARGET_ARCH bfd_arch_aarch64
+
+#define DIFF_EXPR_OK
+
+/* Permit // comments. */
+#define DOUBLESLASH_LINE_COMMENTS
+
+#ifdef LITTLE_ENDIAN
+#undef LITTLE_ENDIAN
+#endif
+
+#ifdef BIG_ENDIAN
+#undef BIG_ENDIAN
+#endif
+
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+
+#define SWAP_32(n) \
+ ((((n) & 0xff) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) \
+ | (((n) >> 24) & 0xff))
+
+struct fix;
+
+struct aarch64_fix
+{
+ struct aarch64_inst *inst;
+ enum aarch64_opnd opnd;
+};
+
+#if defined OBJ_ELF
+# define AARCH64_BI_ENDIAN
+# define TARGET_FORMAT elf64_aarch64_target_format ()
+#endif
+
+#define TC_FORCE_RELOCATION(FIX) aarch64_force_relocation (FIX)
+
+/* Currently there is no machine specific frags generated. */
+#define md_convert_frag(b,s,f) as_fatal ("aarch64 convert_frag called\n")
+
+#define md_cleanup() aarch64_cleanup ()
+
+#define md_start_line_hook() aarch64_start_line_hook ()
+
+#define tc_frob_label(S) aarch64_frob_label (S)
+
+/* We also need to mark assembler created symbols: */
+#define tc_frob_fake_label(S) aarch64_frob_label (S)
+
+#define TC_FIX_TYPE struct aarch64_fix
+#define TC_INIT_FIX_DATA(FIX) { (FIX)->tc_fix_data.inst = NULL; \
+ (FIX)->tc_fix_data.opnd = AARCH64_OPND_NIL; }
+
+#define TC_SYMFIELD_TYPE unsigned int
+#define AARCH64_GET_FLAG(s) (*symbol_get_tc (s))
+
+void aarch64_copy_symbol_attributes (symbolS *, symbolS *);
+#ifndef TC_COPY_SYMBOL_ATTRIBUTES
+#define TC_COPY_SYMBOL_ATTRIBUTES(DEST, SRC) \
+ (aarch64_copy_symbol_attributes (DEST, SRC))
+#endif
+
+#define TC_START_LABEL(C,S,STR) ((C) == ':' \
+ || ((C) == '/' && aarch64_data_in_code ()))
+#define tc_canonicalize_symbol_name(str) aarch64_canonicalize_symbol_name (str);
+#define obj_adjust_symtab() aarch64_adjust_symtab ()
+
+#define LISTING_HEADER "AARCH64 GAS "
+
+#define LOCAL_LABEL(name) (name[0] == '.' && name[1] == 'L')
+#define LOCAL_LABELS_FB 1
+
+/* This expression evaluates to true if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ False if we are willing to perform this relocation while building
+ the .o file. GOTOFF does not need to be checked here because it is
+ not pcrel. I am not sure if some of the others are ever used with
+ pcrel, but it is easier to be safe than sorry. */
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_64 \
+ || (FIX)->fx_r_type == BFD_RELOC_32 \
+ || TC_FORCE_RELOCATION (FIX))
+
+#define TC_CONS_FIX_NEW(f,w,s,e,r) cons_fix_new_aarch64 ((f), (w), (s), (e))
+
+/* Max space for a rs_align_code fragment is 3 unaligned bytes
+ (fr_fix) plus 4 bytes to contain the repeating NOP (fr_var). */
+#define MAX_MEM_FOR_RS_ALIGN_CODE 7
+
+/* For frags in code sections we need to record whether they contain
+ code or data. */
+struct aarch64_frag_type
+{
+ int recorded;
+#ifdef OBJ_ELF
+ /* If there is a mapping symbol at offset 0 in this frag,
+ it will be saved in FIRST_MAP. If there are any mapping
+ symbols in this frag, the last one will be saved in
+ LAST_MAP. */
+ symbolS *first_map, *last_map;
+#endif
+};
+
+#define TC_FRAG_TYPE struct aarch64_frag_type
+/* NOTE: max_chars is a local variable from frag_var / frag_variant. */
+#define TC_FRAG_INIT(fragp) aarch64_init_frag (fragp, max_chars)
+#define HANDLE_ALIGN(fragp) aarch64_handle_align (fragp)
+
+#define md_do_align(N, FILL, LEN, MAX, LABEL) \
+ if (FILL == NULL && (N) != 0 && ! need_pass_2 && subseg_text_p (now_seg)) \
+ { \
+ frag_align_code (N, MAX); \
+ goto LABEL; \
+ }
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+
+/* The lr register is r30. */
+#define DWARF2_DEFAULT_RETURN_COLUMN 30
+
+/* Registers are generally saved at negative offsets to the CFA. */
+#define DWARF2_CIE_DATA_ALIGNMENT (-8)
+
+extern int aarch64_dwarf2_addr_size (void);
+#define DWARF2_ADDR_SIZE(bfd) aarch64_dwarf2_addr_size ()
+
+#ifdef OBJ_ELF
+# define obj_frob_symbol(sym, punt) aarch64elf_frob_symbol ((sym), & (punt))
+
+# define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+# define TC_SEGMENT_INFO_TYPE struct aarch64_segment_info_type
+
+/* This is not really an alignment operation, but it's something we
+ need to do at the same time: whenever we are figuring out the
+ alignment for data, we should check whether a $d symbol is
+ necessary. */
+# define md_cons_align(nbytes) mapping_state (MAP_DATA)
+
+enum mstate
+{
+ MAP_UNDEFINED = 0, /* Must be zero, for seginfo in new sections. */
+ MAP_DATA,
+ MAP_INSN,
+};
+
+void mapping_state (enum mstate);
+
+struct aarch64_segment_info_type
+{
+ enum mstate mapstate;
+ unsigned int marked_pr_dependency;
+};
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+/* CFI hooks. */
+#define tc_regname_to_dw2regnum tc_aarch64_regname_to_dw2regnum
+#define tc_cfi_frame_initial_instructions tc_aarch64_frame_initial_instructions
+
+#else /* Not OBJ_ELF. */
+#define GLOBAL_OFFSET_TABLE_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+
+#if defined OBJ_ELF || defined OBJ_COFF
+
+# define EXTERN_FORCE_RELOC 1
+# define tc_fix_adjustable(FIX) 1
+/* Values passed to md_apply_fix don't include the symbol value. */
+# define MD_APPLY_SYM_VALUE(FIX) 0
+
+#endif
+
+#define MD_PCREL_FROM_SECTION(F,S) md_pcrel_from_section(F,S)
+
+extern long md_pcrel_from_section (struct fix *, segT);
+extern void aarch64_frag_align_code (int, int);
+extern const char * elf64_aarch64_target_format (void);
+extern int aarch64_force_relocation (struct fix *);
+extern void aarch64_cleanup (void);
+extern void aarch64_start_line_hook (void);
+extern void aarch64_frob_label (symbolS *);
+extern int aarch64_data_in_code (void);
+extern char * aarch64_canonicalize_symbol_name (char *);
+extern void aarch64_adjust_symtab (void);
+extern void aarch64elf_frob_symbol (symbolS *, int *);
+extern void cons_fix_new_aarch64 (fragS *, int, int, expressionS *);
+extern void aarch64_init_frag (struct frag *, int);
+extern void aarch64_handle_align (struct frag *);
+extern int tc_aarch64_regname_to_dw2regnum (char *regname);
+extern void tc_aarch64_frame_initial_instructions (void);
+
+#ifdef TE_PE
+
+#define O_secrel O_md1
+
+#define TC_DWARF2_EMIT_OFFSET tc_pe_dwarf2_emit_offset
+void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int);
+
+#endif /* TE_PE */
+
+#endif /* TC_AARCH64 */
diff --git a/gas/config/tc-alpha.c b/gas/config/tc-alpha.c
new file mode 100644
index 0000000..ea6aa19
--- /dev/null
+++ b/gas/config/tc-alpha.c
@@ -0,0 +1,6369 @@
+/* tc-alpha.c - Processor-specific code for the DEC Alpha AXP CPU.
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+ Contributed by Carnegie Mellon University, 1993.
+ Written by Alessandro Forin, based on earlier gas-1.38 target CPU files.
+ Modified by Ken Raeburn for gas-2.x and ECOFF support.
+ Modified by Richard Henderson for ELF support.
+ Modified by Klaus K"ampf for EVAX (OpenVMS/Alpha) support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Mach Operating System
+ Copyright (c) 1993 Carnegie Mellon University
+ All Rights Reserved.
+
+ Permission to use, copy, modify and distribute this software and its
+ documentation is hereby granted, provided that both the copyright
+ notice and this permission notice appear in all copies of the
+ software, derivative works or modified versions, and any portions
+ thereof, and that both notices appear in supporting documentation.
+
+ CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+ CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+
+ Carnegie Mellon requests users of this software to return to
+
+ Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ School of Computer Science
+ Carnegie Mellon University
+ Pittsburgh PA 15213-3890
+
+ any improvements or extensions that they make and grant Carnegie the
+ rights to redistribute these changes. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "struc-symbol.h"
+#include "ecoff.h"
+
+#include "opcode/alpha.h"
+
+#ifdef OBJ_ELF
+#include "elf/alpha.h"
+#endif
+
+#ifdef OBJ_EVAX
+#include "vms.h"
+#include "vms/egps.h"
+#endif
+
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "safe-ctype.h"
+
+/* Local types. */
+
+#define TOKENIZE_ERROR -1
+#define TOKENIZE_ERROR_REPORT -2
+#define MAX_INSN_FIXUPS 2
+#define MAX_INSN_ARGS 5
+
+/* Used since new relocation types are introduced in this
+ file (DUMMY_RELOC_LITUSE_*) */
+typedef int extended_bfd_reloc_code_real_type;
+
+struct alpha_fixup
+{
+ expressionS exp;
+ /* bfd_reloc_code_real_type reloc; */
+ extended_bfd_reloc_code_real_type reloc;
+#ifdef OBJ_EVAX
+ /* The symbol of the item in the linkage section. */
+ symbolS *xtrasym;
+
+ /* The symbol of the procedure descriptor. */
+ symbolS *procsym;
+#endif
+};
+
+struct alpha_insn
+{
+ unsigned insn;
+ int nfixups;
+ struct alpha_fixup fixups[MAX_INSN_FIXUPS];
+ long sequence;
+};
+
+enum alpha_macro_arg
+ {
+ MACRO_EOA = 1,
+ MACRO_IR,
+ MACRO_PIR,
+ MACRO_OPIR,
+ MACRO_CPIR,
+ MACRO_FPR,
+ MACRO_EXP
+ };
+
+struct alpha_macro
+{
+ const char *name;
+ void (*emit) (const expressionS *, int, const void *);
+ const void * arg;
+ enum alpha_macro_arg argsets[16];
+};
+
+/* Extra expression types. */
+
+#define O_pregister O_md1 /* O_register, in parentheses. */
+#define O_cpregister O_md2 /* + a leading comma. */
+
+/* The alpha_reloc_op table below depends on the ordering of these. */
+#define O_literal O_md3 /* !literal relocation. */
+#define O_lituse_addr O_md4 /* !lituse_addr relocation. */
+#define O_lituse_base O_md5 /* !lituse_base relocation. */
+#define O_lituse_bytoff O_md6 /* !lituse_bytoff relocation. */
+#define O_lituse_jsr O_md7 /* !lituse_jsr relocation. */
+#define O_lituse_tlsgd O_md8 /* !lituse_tlsgd relocation. */
+#define O_lituse_tlsldm O_md9 /* !lituse_tlsldm relocation. */
+#define O_lituse_jsrdirect O_md10 /* !lituse_jsrdirect relocation. */
+#define O_gpdisp O_md11 /* !gpdisp relocation. */
+#define O_gprelhigh O_md12 /* !gprelhigh relocation. */
+#define O_gprellow O_md13 /* !gprellow relocation. */
+#define O_gprel O_md14 /* !gprel relocation. */
+#define O_samegp O_md15 /* !samegp relocation. */
+#define O_tlsgd O_md16 /* !tlsgd relocation. */
+#define O_tlsldm O_md17 /* !tlsldm relocation. */
+#define O_gotdtprel O_md18 /* !gotdtprel relocation. */
+#define O_dtprelhi O_md19 /* !dtprelhi relocation. */
+#define O_dtprello O_md20 /* !dtprello relocation. */
+#define O_dtprel O_md21 /* !dtprel relocation. */
+#define O_gottprel O_md22 /* !gottprel relocation. */
+#define O_tprelhi O_md23 /* !tprelhi relocation. */
+#define O_tprello O_md24 /* !tprello relocation. */
+#define O_tprel O_md25 /* !tprel relocation. */
+
+#define DUMMY_RELOC_LITUSE_ADDR (BFD_RELOC_UNUSED + 1)
+#define DUMMY_RELOC_LITUSE_BASE (BFD_RELOC_UNUSED + 2)
+#define DUMMY_RELOC_LITUSE_BYTOFF (BFD_RELOC_UNUSED + 3)
+#define DUMMY_RELOC_LITUSE_JSR (BFD_RELOC_UNUSED + 4)
+#define DUMMY_RELOC_LITUSE_TLSGD (BFD_RELOC_UNUSED + 5)
+#define DUMMY_RELOC_LITUSE_TLSLDM (BFD_RELOC_UNUSED + 6)
+#define DUMMY_RELOC_LITUSE_JSRDIRECT (BFD_RELOC_UNUSED + 7)
+
+#define USER_RELOC_P(R) ((R) >= O_literal && (R) <= O_tprel)
+
+/* Macros for extracting the type and number of encoded register tokens. */
+
+#define is_ir_num(x) (((x) & 32) == 0)
+#define is_fpr_num(x) (((x) & 32) != 0)
+#define regno(x) ((x) & 31)
+
+/* Something odd inherited from the old assembler. */
+
+#define note_gpreg(R) (alpha_gprmask |= (1 << (R)))
+#define note_fpreg(R) (alpha_fprmask |= (1 << (R)))
+
+/* Predicates for 16- and 32-bit ranges */
+/* XXX: The non-shift version appears to trigger a compiler bug when
+ cross-assembling from x86 w/ gcc 2.7.2. */
+
+#if 1
+#define range_signed_16(x) \
+ (((offsetT) (x) >> 15) == 0 || ((offsetT) (x) >> 15) == -1)
+#define range_signed_32(x) \
+ (((offsetT) (x) >> 31) == 0 || ((offsetT) (x) >> 31) == -1)
+#else
+#define range_signed_16(x) ((offsetT) (x) >= -(offsetT) 0x8000 && \
+ (offsetT) (x) <= (offsetT) 0x7FFF)
+#define range_signed_32(x) ((offsetT) (x) >= -(offsetT) 0x80000000 && \
+ (offsetT) (x) <= (offsetT) 0x7FFFFFFF)
+#endif
+
+/* Macros for sign extending from 16- and 32-bits. */
+/* XXX: The cast macros will work on all the systems that I care about,
+ but really a predicate should be found to use the non-cast forms. */
+
+#if 1
+#define sign_extend_16(x) ((short) (x))
+#define sign_extend_32(x) ((int) (x))
+#else
+#define sign_extend_16(x) ((offsetT) (((x) & 0xFFFF) ^ 0x8000) - 0x8000)
+#define sign_extend_32(x) ((offsetT) (((x) & 0xFFFFFFFF) \
+ ^ 0x80000000) - 0x80000000)
+#endif
+
+/* Macros to build tokens. */
+
+#define set_tok_reg(t, r) (memset (&(t), 0, sizeof (t)), \
+ (t).X_op = O_register, \
+ (t).X_add_number = (r))
+#define set_tok_preg(t, r) (memset (&(t), 0, sizeof (t)), \
+ (t).X_op = O_pregister, \
+ (t).X_add_number = (r))
+#define set_tok_cpreg(t, r) (memset (&(t), 0, sizeof (t)), \
+ (t).X_op = O_cpregister, \
+ (t).X_add_number = (r))
+#define set_tok_freg(t, r) (memset (&(t), 0, sizeof (t)), \
+ (t).X_op = O_register, \
+ (t).X_add_number = (r) + 32)
+#define set_tok_sym(t, s, a) (memset (&(t), 0, sizeof (t)), \
+ (t).X_op = O_symbol, \
+ (t).X_add_symbol = (s), \
+ (t).X_add_number = (a))
+#define set_tok_const(t, n) (memset (&(t), 0, sizeof (t)), \
+ (t).X_op = O_constant, \
+ (t).X_add_number = (n))
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+/* XXX: Do all of these really get used on the alpha?? */
+char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+#ifdef OBJ_EVAX
+const char *md_shortopts = "Fm:g+1h:HG:";
+#else
+const char *md_shortopts = "Fm:gG:";
+#endif
+
+struct option md_longopts[] =
+ {
+#define OPTION_32ADDR (OPTION_MD_BASE)
+ { "32addr", no_argument, NULL, OPTION_32ADDR },
+#define OPTION_RELAX (OPTION_32ADDR + 1)
+ { "relax", no_argument, NULL, OPTION_RELAX },
+#ifdef OBJ_ELF
+#define OPTION_MDEBUG (OPTION_RELAX + 1)
+#define OPTION_NO_MDEBUG (OPTION_MDEBUG + 1)
+ { "mdebug", no_argument, NULL, OPTION_MDEBUG },
+ { "no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG },
+#endif
+#ifdef OBJ_EVAX
+#define OPTION_REPLACE (OPTION_RELAX + 1)
+#define OPTION_NOREPLACE (OPTION_REPLACE+1)
+ { "replace", no_argument, NULL, OPTION_REPLACE },
+ { "noreplace", no_argument, NULL, OPTION_NOREPLACE },
+#endif
+ { NULL, no_argument, NULL, 0 }
+ };
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+#ifdef OBJ_EVAX
+#define AXP_REG_R0 0
+#define AXP_REG_R16 16
+#define AXP_REG_R17 17
+#undef AXP_REG_T9
+#define AXP_REG_T9 22
+#undef AXP_REG_T10
+#define AXP_REG_T10 23
+#undef AXP_REG_T11
+#define AXP_REG_T11 24
+#undef AXP_REG_T12
+#define AXP_REG_T12 25
+#define AXP_REG_AI 25
+#undef AXP_REG_FP
+#define AXP_REG_FP 29
+
+#undef AXP_REG_GP
+#define AXP_REG_GP AXP_REG_PV
+
+#endif /* OBJ_EVAX */
+
+/* The cpu for which we are generating code. */
+static unsigned alpha_target = AXP_OPCODE_BASE;
+static const char *alpha_target_name = "<all>";
+
+/* The hash table of instruction opcodes. */
+static struct hash_control *alpha_opcode_hash;
+
+/* The hash table of macro opcodes. */
+static struct hash_control *alpha_macro_hash;
+
+#ifdef OBJ_ECOFF
+/* The $gp relocation symbol. */
+static symbolS *alpha_gp_symbol;
+
+/* XXX: what is this, and why is it exported? */
+valueT alpha_gp_value;
+#endif
+
+/* The current $gp register. */
+static int alpha_gp_register = AXP_REG_GP;
+
+/* A table of the register symbols. */
+static symbolS *alpha_register_table[64];
+
+/* Constant sections, or sections of constants. */
+#ifdef OBJ_ECOFF
+static segT alpha_lita_section;
+#endif
+#ifdef OBJ_EVAX
+segT alpha_link_section;
+#endif
+#ifndef OBJ_EVAX
+static segT alpha_lit8_section;
+#endif
+
+/* Symbols referring to said sections. */
+#ifdef OBJ_ECOFF
+static symbolS *alpha_lita_symbol;
+#endif
+#ifdef OBJ_EVAX
+static symbolS *alpha_link_symbol;
+#endif
+#ifndef OBJ_EVAX
+static symbolS *alpha_lit8_symbol;
+#endif
+
+/* Literal for .litX+0x8000 within .lita. */
+#ifdef OBJ_ECOFF
+static offsetT alpha_lit8_literal;
+#endif
+
+/* Is the assembler not allowed to use $at? */
+static int alpha_noat_on = 0;
+
+/* Are macros enabled? */
+static int alpha_macros_on = 1;
+
+/* Are floats disabled? */
+static int alpha_nofloats_on = 0;
+
+/* Are addresses 32 bit? */
+static int alpha_addr32_on = 0;
+
+/* Symbol labelling the current insn. When the Alpha gas sees
+ foo:
+ .quad 0
+ and the section happens to not be on an eight byte boundary, it
+ will align both the symbol and the .quad to an eight byte boundary. */
+static symbolS *alpha_insn_label;
+#if defined(OBJ_ELF) || defined (OBJ_EVAX)
+static symbolS *alpha_prologue_label;
+#endif
+
+#ifdef OBJ_EVAX
+/* Symbol associate with the current jsr instruction. */
+static symbolS *alpha_linkage_symbol;
+#endif
+
+/* Whether we should automatically align data generation pseudo-ops.
+ .align 0 will turn this off. */
+static int alpha_auto_align_on = 1;
+
+/* The known current alignment of the current section. */
+static int alpha_current_align;
+
+/* These are exported to ECOFF code. */
+unsigned long alpha_gprmask, alpha_fprmask;
+
+/* Whether the debugging option was seen. */
+static int alpha_debug;
+
+#ifdef OBJ_ELF
+/* Whether we are emitting an mdebug section. */
+int alpha_flag_mdebug = -1;
+#endif
+
+#ifdef OBJ_EVAX
+/* Whether to perform the VMS procedure call optimization. */
+int alpha_flag_replace = 1;
+#endif
+
+/* Don't fully resolve relocations, allowing code movement in the linker. */
+static int alpha_flag_relax;
+
+/* What value to give to bfd_set_gp_size. */
+static int g_switch_value = 8;
+
+#ifdef OBJ_EVAX
+/* Collect information about current procedure here. */
+struct alpha_evax_procs
+{
+ symbolS *symbol; /* Proc pdesc symbol. */
+ int pdsckind;
+ int framereg; /* Register for frame pointer. */
+ int framesize; /* Size of frame. */
+ int rsa_offset;
+ int ra_save;
+ int fp_save;
+ long imask;
+ long fmask;
+ int type;
+ int prologue;
+ symbolS *handler;
+ int handler_data;
+};
+
+/* Linked list of .linkage fixups. */
+struct alpha_linkage_fixups *alpha_linkage_fixup_root;
+static struct alpha_linkage_fixups *alpha_linkage_fixup_tail;
+
+/* Current procedure descriptor. */
+static struct alpha_evax_procs *alpha_evax_proc;
+static struct alpha_evax_procs alpha_evax_proc_data;
+
+static int alpha_flag_hash_long_names = 0; /* -+ */
+static int alpha_flag_show_after_trunc = 0; /* -H */
+
+/* If the -+ switch is given, then a hash is appended to any name that is
+ longer than 64 characters, else longer symbol names are truncated. */
+
+#endif
+
+#ifdef RELOC_OP_P
+/* A table to map the spelling of a relocation operand into an appropriate
+ bfd_reloc_code_real_type type. The table is assumed to be ordered such
+ that op-O_literal indexes into it. */
+
+#define ALPHA_RELOC_TABLE(op) \
+(&alpha_reloc_op[ ((!USER_RELOC_P (op)) \
+ ? (abort (), 0) \
+ : (int) (op) - (int) O_literal) ])
+
+#define DEF(NAME, RELOC, REQ, ALLOW) \
+ { #NAME, sizeof(#NAME)-1, O_##NAME, RELOC, REQ, ALLOW}
+
+static const struct alpha_reloc_op_tag
+{
+ const char *name; /* String to lookup. */
+ size_t length; /* Size of the string. */
+ operatorT op; /* Which operator to use. */
+ extended_bfd_reloc_code_real_type reloc;
+ unsigned int require_seq : 1; /* Require a sequence number. */
+ unsigned int allow_seq : 1; /* Allow a sequence number. */
+}
+alpha_reloc_op[] =
+{
+ DEF (literal, BFD_RELOC_ALPHA_ELF_LITERAL, 0, 1),
+ DEF (lituse_addr, DUMMY_RELOC_LITUSE_ADDR, 1, 1),
+ DEF (lituse_base, DUMMY_RELOC_LITUSE_BASE, 1, 1),
+ DEF (lituse_bytoff, DUMMY_RELOC_LITUSE_BYTOFF, 1, 1),
+ DEF (lituse_jsr, DUMMY_RELOC_LITUSE_JSR, 1, 1),
+ DEF (lituse_tlsgd, DUMMY_RELOC_LITUSE_TLSGD, 1, 1),
+ DEF (lituse_tlsldm, DUMMY_RELOC_LITUSE_TLSLDM, 1, 1),
+ DEF (lituse_jsrdirect, DUMMY_RELOC_LITUSE_JSRDIRECT, 1, 1),
+ DEF (gpdisp, BFD_RELOC_ALPHA_GPDISP, 1, 1),
+ DEF (gprelhigh, BFD_RELOC_ALPHA_GPREL_HI16, 0, 0),
+ DEF (gprellow, BFD_RELOC_ALPHA_GPREL_LO16, 0, 0),
+ DEF (gprel, BFD_RELOC_GPREL16, 0, 0),
+ DEF (samegp, BFD_RELOC_ALPHA_BRSGP, 0, 0),
+ DEF (tlsgd, BFD_RELOC_ALPHA_TLSGD, 0, 1),
+ DEF (tlsldm, BFD_RELOC_ALPHA_TLSLDM, 0, 1),
+ DEF (gotdtprel, BFD_RELOC_ALPHA_GOTDTPREL16, 0, 0),
+ DEF (dtprelhi, BFD_RELOC_ALPHA_DTPREL_HI16, 0, 0),
+ DEF (dtprello, BFD_RELOC_ALPHA_DTPREL_LO16, 0, 0),
+ DEF (dtprel, BFD_RELOC_ALPHA_DTPREL16, 0, 0),
+ DEF (gottprel, BFD_RELOC_ALPHA_GOTTPREL16, 0, 0),
+ DEF (tprelhi, BFD_RELOC_ALPHA_TPREL_HI16, 0, 0),
+ DEF (tprello, BFD_RELOC_ALPHA_TPREL_LO16, 0, 0),
+ DEF (tprel, BFD_RELOC_ALPHA_TPREL16, 0, 0),
+};
+
+#undef DEF
+
+static const int alpha_num_reloc_op
+ = sizeof (alpha_reloc_op) / sizeof (*alpha_reloc_op);
+#endif /* RELOC_OP_P */
+
+/* Maximum # digits needed to hold the largest sequence #. */
+#define ALPHA_RELOC_DIGITS 25
+
+/* Structure to hold explicit sequence information. */
+struct alpha_reloc_tag
+{
+ fixS *master; /* The literal reloc. */
+#ifdef OBJ_EVAX
+ struct symbol *sym; /* Linkage section item symbol. */
+ struct symbol *psym; /* Pdesc symbol. */
+#endif
+ fixS *slaves; /* Head of linked list of lituses. */
+ segT segment; /* Segment relocs are in or undefined_section. */
+ long sequence; /* Sequence #. */
+ unsigned n_master; /* # of literals. */
+ unsigned n_slaves; /* # of lituses. */
+ unsigned saw_tlsgd : 1; /* True if ... */
+ unsigned saw_tlsldm : 1;
+ unsigned saw_lu_tlsgd : 1;
+ unsigned saw_lu_tlsldm : 1;
+ unsigned multi_section_p : 1; /* True if more than one section was used. */
+ char string[1]; /* Printable form of sequence to hash with. */
+};
+
+/* Hash table to link up literals with the appropriate lituse. */
+static struct hash_control *alpha_literal_hash;
+
+/* Sequence numbers for internal use by macros. */
+static long next_sequence_num = -1;
+
+/* A table of CPU names and opcode sets. */
+
+static const struct cpu_type
+{
+ const char *name;
+ unsigned flags;
+}
+cpu_types[] =
+{
+ /* Ad hoc convention: cpu number gets palcode, process code doesn't.
+ This supports usage under DU 4.0b that does ".arch ev4", and
+ usage in MILO that does -m21064. Probably something more
+ specific like -m21064-pal should be used, but oh well. */
+
+ { "21064", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21064a", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21066", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21068", AXP_OPCODE_BASE|AXP_OPCODE_EV4 },
+ { "21164", AXP_OPCODE_BASE|AXP_OPCODE_EV5 },
+ { "21164a", AXP_OPCODE_BASE|AXP_OPCODE_EV5|AXP_OPCODE_BWX },
+ { "21164pc", (AXP_OPCODE_BASE|AXP_OPCODE_EV5|AXP_OPCODE_BWX
+ |AXP_OPCODE_MAX) },
+ { "21264", (AXP_OPCODE_BASE|AXP_OPCODE_EV6|AXP_OPCODE_BWX
+ |AXP_OPCODE_MAX|AXP_OPCODE_CIX) },
+ { "21264a", (AXP_OPCODE_BASE|AXP_OPCODE_EV6|AXP_OPCODE_BWX
+ |AXP_OPCODE_MAX|AXP_OPCODE_CIX) },
+ { "21264b", (AXP_OPCODE_BASE|AXP_OPCODE_EV6|AXP_OPCODE_BWX
+ |AXP_OPCODE_MAX|AXP_OPCODE_CIX) },
+
+ { "ev4", AXP_OPCODE_BASE },
+ { "ev45", AXP_OPCODE_BASE },
+ { "lca45", AXP_OPCODE_BASE },
+ { "ev5", AXP_OPCODE_BASE },
+ { "ev56", AXP_OPCODE_BASE|AXP_OPCODE_BWX },
+ { "pca56", AXP_OPCODE_BASE|AXP_OPCODE_BWX|AXP_OPCODE_MAX },
+ { "ev6", AXP_OPCODE_BASE|AXP_OPCODE_BWX|AXP_OPCODE_MAX|AXP_OPCODE_CIX },
+ { "ev67", AXP_OPCODE_BASE|AXP_OPCODE_BWX|AXP_OPCODE_MAX|AXP_OPCODE_CIX },
+ { "ev68", AXP_OPCODE_BASE|AXP_OPCODE_BWX|AXP_OPCODE_MAX|AXP_OPCODE_CIX },
+
+ { "all", AXP_OPCODE_BASE },
+ { 0, 0 }
+};
+
+/* Some instruction sets indexed by lg(size). */
+static const char * const sextX_op[] = { "sextb", "sextw", "sextl", NULL };
+static const char * const insXl_op[] = { "insbl", "inswl", "insll", "insql" };
+static const char * const insXh_op[] = { NULL, "inswh", "inslh", "insqh" };
+static const char * const extXl_op[] = { "extbl", "extwl", "extll", "extql" };
+static const char * const extXh_op[] = { NULL, "extwh", "extlh", "extqh" };
+static const char * const mskXl_op[] = { "mskbl", "mskwl", "mskll", "mskql" };
+static const char * const mskXh_op[] = { NULL, "mskwh", "msklh", "mskqh" };
+static const char * const stX_op[] = { "stb", "stw", "stl", "stq" };
+static const char * const ldXu_op[] = { "ldbu", "ldwu", NULL, NULL };
+
+static void assemble_insn (const struct alpha_opcode *, const expressionS *, int, struct alpha_insn *, extended_bfd_reloc_code_real_type);
+static void emit_insn (struct alpha_insn *);
+static void assemble_tokens (const char *, const expressionS *, int, int);
+#ifdef OBJ_EVAX
+static char *s_alpha_section_name (void);
+static symbolS *add_to_link_pool (symbolS *, offsetT);
+#endif
+
+static struct alpha_reloc_tag *
+get_alpha_reloc_tag (long sequence)
+{
+ char buffer[ALPHA_RELOC_DIGITS];
+ struct alpha_reloc_tag *info;
+
+ sprintf (buffer, "!%ld", sequence);
+
+ info = (struct alpha_reloc_tag *) hash_find (alpha_literal_hash, buffer);
+ if (! info)
+ {
+ size_t len = strlen (buffer);
+ const char *errmsg;
+
+ info = (struct alpha_reloc_tag *)
+ xcalloc (sizeof (struct alpha_reloc_tag) + len, 1);
+
+ info->segment = now_seg;
+ info->sequence = sequence;
+ strcpy (info->string, buffer);
+ errmsg = hash_insert (alpha_literal_hash, info->string, (void *) info);
+ if (errmsg)
+ as_fatal ("%s", errmsg);
+#ifdef OBJ_EVAX
+ info->sym = 0;
+ info->psym = 0;
+#endif
+ }
+
+ return info;
+}
+
+#ifndef OBJ_EVAX
+
+static void
+alpha_adjust_relocs (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void * ptr ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fixS **prevP;
+ fixS *fixp;
+ fixS *next;
+ fixS *slave;
+
+ /* If seginfo is NULL, we did not create this section; don't do
+ anything with it. By using a pointer to a pointer, we can update
+ the links in place. */
+ if (seginfo == NULL)
+ return;
+
+ /* If there are no relocations, skip the section. */
+ if (! seginfo->fix_root)
+ return;
+
+ /* First rebuild the fixup chain without the explicit lituse and
+ gpdisp_lo16 relocs. */
+ prevP = &seginfo->fix_root;
+ for (fixp = seginfo->fix_root; fixp; fixp = next)
+ {
+ next = fixp->fx_next;
+ fixp->fx_next = (fixS *) 0;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_LITUSE:
+ if (fixp->tc_fix_data.info->n_master == 0)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("No !literal!%ld was found"),
+ fixp->tc_fix_data.info->sequence);
+#ifdef RELOC_OP_P
+ if (fixp->fx_offset == LITUSE_ALPHA_TLSGD)
+ {
+ if (! fixp->tc_fix_data.info->saw_tlsgd)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("No !tlsgd!%ld was found"),
+ fixp->tc_fix_data.info->sequence);
+ }
+ else if (fixp->fx_offset == LITUSE_ALPHA_TLSLDM)
+ {
+ if (! fixp->tc_fix_data.info->saw_tlsldm)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("No !tlsldm!%ld was found"),
+ fixp->tc_fix_data.info->sequence);
+ }
+#endif
+ break;
+
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ if (fixp->tc_fix_data.info->n_master == 0)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("No ldah !gpdisp!%ld was found"),
+ fixp->tc_fix_data.info->sequence);
+ break;
+
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ if (fixp->tc_fix_data.info
+ && (fixp->tc_fix_data.info->saw_tlsgd
+ || fixp->tc_fix_data.info->saw_tlsldm))
+ break;
+ /* FALLTHRU */
+
+ default:
+ *prevP = fixp;
+ prevP = &fixp->fx_next;
+ break;
+ }
+ }
+
+ /* Go back and re-chain dependent relocations. They are currently
+ linked through the next_reloc field in reverse order, so as we
+ go through the next_reloc chain, we effectively reverse the chain
+ once again.
+
+ Except if there is more than one !literal for a given sequence
+ number. In that case, the programmer and/or compiler is not sure
+ how control flows from literal to lituse, and we can't be sure to
+ get the relaxation correct.
+
+ ??? Well, actually we could, if there are enough lituses such that
+ we can make each literal have at least one of each lituse type
+ present. Not implemented.
+
+ Also suppress the optimization if the !literals/!lituses are spread
+ in different segments. This can happen with "intersting" uses of
+ inline assembly; examples are present in the Linux kernel semaphores. */
+
+ for (fixp = seginfo->fix_root; fixp; fixp = next)
+ {
+ next = fixp->fx_next;
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_TLSGD:
+ case BFD_RELOC_ALPHA_TLSLDM:
+ if (!fixp->tc_fix_data.info)
+ break;
+ if (fixp->tc_fix_data.info->n_master == 0)
+ break;
+ else if (fixp->tc_fix_data.info->n_master > 1)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("too many !literal!%ld for %s"),
+ fixp->tc_fix_data.info->sequence,
+ (fixp->fx_r_type == BFD_RELOC_ALPHA_TLSGD
+ ? "!tlsgd" : "!tlsldm"));
+ break;
+ }
+
+ fixp->tc_fix_data.info->master->fx_next = fixp->fx_next;
+ fixp->fx_next = fixp->tc_fix_data.info->master;
+ fixp = fixp->fx_next;
+ /* Fall through. */
+
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ if (fixp->tc_fix_data.info
+ && fixp->tc_fix_data.info->n_master == 1
+ && ! fixp->tc_fix_data.info->multi_section_p)
+ {
+ for (slave = fixp->tc_fix_data.info->slaves;
+ slave != (fixS *) 0;
+ slave = slave->tc_fix_data.next_reloc)
+ {
+ slave->fx_next = fixp->fx_next;
+ fixp->fx_next = slave;
+ }
+ }
+ break;
+
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ if (fixp->tc_fix_data.info->n_slaves == 0)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("No lda !gpdisp!%ld was found"),
+ fixp->tc_fix_data.info->sequence);
+ else
+ {
+ slave = fixp->tc_fix_data.info->slaves;
+ slave->fx_next = next;
+ fixp->fx_next = slave;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/* Before the relocations are written, reorder them, so that user
+ supplied !lituse relocations follow the appropriate !literal
+ relocations, and similarly for !gpdisp relocations. */
+
+void
+alpha_before_fix (void)
+{
+ if (alpha_literal_hash)
+ bfd_map_over_sections (stdoutput, alpha_adjust_relocs, NULL);
+}
+
+#endif
+
+#ifdef DEBUG_ALPHA
+static void
+debug_exp (expressionS tok[], int ntok)
+{
+ int i;
+
+ fprintf (stderr, "debug_exp: %d tokens", ntok);
+ for (i = 0; i < ntok; i++)
+ {
+ expressionS *t = &tok[i];
+ const char *name;
+
+ switch (t->X_op)
+ {
+ default: name = "unknown"; break;
+ case O_illegal: name = "O_illegal"; break;
+ case O_absent: name = "O_absent"; break;
+ case O_constant: name = "O_constant"; break;
+ case O_symbol: name = "O_symbol"; break;
+ case O_symbol_rva: name = "O_symbol_rva"; break;
+ case O_register: name = "O_register"; break;
+ case O_big: name = "O_big"; break;
+ case O_uminus: name = "O_uminus"; break;
+ case O_bit_not: name = "O_bit_not"; break;
+ case O_logical_not: name = "O_logical_not"; break;
+ case O_multiply: name = "O_multiply"; break;
+ case O_divide: name = "O_divide"; break;
+ case O_modulus: name = "O_modulus"; break;
+ case O_left_shift: name = "O_left_shift"; break;
+ case O_right_shift: name = "O_right_shift"; break;
+ case O_bit_inclusive_or: name = "O_bit_inclusive_or"; break;
+ case O_bit_or_not: name = "O_bit_or_not"; break;
+ case O_bit_exclusive_or: name = "O_bit_exclusive_or"; break;
+ case O_bit_and: name = "O_bit_and"; break;
+ case O_add: name = "O_add"; break;
+ case O_subtract: name = "O_subtract"; break;
+ case O_eq: name = "O_eq"; break;
+ case O_ne: name = "O_ne"; break;
+ case O_lt: name = "O_lt"; break;
+ case O_le: name = "O_le"; break;
+ case O_ge: name = "O_ge"; break;
+ case O_gt: name = "O_gt"; break;
+ case O_logical_and: name = "O_logical_and"; break;
+ case O_logical_or: name = "O_logical_or"; break;
+ case O_index: name = "O_index"; break;
+ case O_pregister: name = "O_pregister"; break;
+ case O_cpregister: name = "O_cpregister"; break;
+ case O_literal: name = "O_literal"; break;
+ case O_lituse_addr: name = "O_lituse_addr"; break;
+ case O_lituse_base: name = "O_lituse_base"; break;
+ case O_lituse_bytoff: name = "O_lituse_bytoff"; break;
+ case O_lituse_jsr: name = "O_lituse_jsr"; break;
+ case O_lituse_tlsgd: name = "O_lituse_tlsgd"; break;
+ case O_lituse_tlsldm: name = "O_lituse_tlsldm"; break;
+ case O_lituse_jsrdirect: name = "O_lituse_jsrdirect"; break;
+ case O_gpdisp: name = "O_gpdisp"; break;
+ case O_gprelhigh: name = "O_gprelhigh"; break;
+ case O_gprellow: name = "O_gprellow"; break;
+ case O_gprel: name = "O_gprel"; break;
+ case O_samegp: name = "O_samegp"; break;
+ case O_tlsgd: name = "O_tlsgd"; break;
+ case O_tlsldm: name = "O_tlsldm"; break;
+ case O_gotdtprel: name = "O_gotdtprel"; break;
+ case O_dtprelhi: name = "O_dtprelhi"; break;
+ case O_dtprello: name = "O_dtprello"; break;
+ case O_dtprel: name = "O_dtprel"; break;
+ case O_gottprel: name = "O_gottprel"; break;
+ case O_tprelhi: name = "O_tprelhi"; break;
+ case O_tprello: name = "O_tprello"; break;
+ case O_tprel: name = "O_tprel"; break;
+ }
+
+ fprintf (stderr, ", %s(%s, %s, %d)", name,
+ (t->X_add_symbol) ? S_GET_NAME (t->X_add_symbol) : "--",
+ (t->X_op_symbol) ? S_GET_NAME (t->X_op_symbol) : "--",
+ (int) t->X_add_number);
+ }
+ fprintf (stderr, "\n");
+ fflush (stderr);
+}
+#endif
+
+/* Parse the arguments to an opcode. */
+
+static int
+tokenize_arguments (char *str,
+ expressionS tok[],
+ int ntok)
+{
+ expressionS *end_tok = tok + ntok;
+ char *old_input_line_pointer;
+ int saw_comma = 0, saw_arg = 0;
+#ifdef DEBUG_ALPHA
+ expressionS *orig_tok = tok;
+#endif
+#ifdef RELOC_OP_P
+ char *p;
+ const struct alpha_reloc_op_tag *r;
+ int c, i;
+ size_t len;
+ int reloc_found_p = 0;
+#endif
+
+ memset (tok, 0, sizeof (*tok) * ntok);
+
+ /* Save and restore input_line_pointer around this function. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+
+#ifdef RELOC_OP_P
+ /* ??? Wrest control of ! away from the regular expression parser. */
+ is_end_of_line[(unsigned char) '!'] = 1;
+#endif
+
+ while (tok < end_tok && *input_line_pointer)
+ {
+ SKIP_WHITESPACE ();
+ switch (*input_line_pointer)
+ {
+ case '\0':
+ goto fini;
+
+#ifdef RELOC_OP_P
+ case '!':
+ /* A relocation operand can be placed after the normal operand on an
+ assembly language statement, and has the following form:
+ !relocation_type!sequence_number. */
+ if (reloc_found_p)
+ {
+ /* Only support one relocation op per insn. */
+ as_bad (_("More than one relocation op per insn"));
+ goto err_report;
+ }
+
+ if (!saw_arg)
+ goto err;
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ p = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Parse !relocation_type. */
+ len = input_line_pointer - p;
+ if (len == 0)
+ {
+ as_bad (_("No relocation operand"));
+ goto err_report;
+ }
+
+ r = &alpha_reloc_op[0];
+ for (i = alpha_num_reloc_op - 1; i >= 0; i--, r++)
+ if (len == r->length && memcmp (p, r->name, len) == 0)
+ break;
+ if (i < 0)
+ {
+ as_bad (_("Unknown relocation operand: !%s"), p);
+ goto err_report;
+ }
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '!')
+ {
+ if (r->require_seq)
+ {
+ as_bad (_("no sequence number after !%s"), p);
+ goto err_report;
+ }
+
+ tok->X_add_number = 0;
+ }
+ else
+ {
+ if (! r->allow_seq)
+ {
+ as_bad (_("!%s does not use a sequence number"), p);
+ goto err_report;
+ }
+
+ input_line_pointer++;
+
+ /* Parse !sequence_number. */
+ expression (tok);
+ if (tok->X_op != O_constant || tok->X_add_number <= 0)
+ {
+ as_bad (_("Bad sequence number: !%s!%s"),
+ r->name, input_line_pointer);
+ goto err_report;
+ }
+ }
+
+ tok->X_op = r->op;
+ reloc_found_p = 1;
+ ++tok;
+ break;
+#endif /* RELOC_OP_P */
+
+ case ',':
+ ++input_line_pointer;
+ if (saw_comma || !saw_arg)
+ goto err;
+ saw_comma = 1;
+ break;
+
+ case '(':
+ {
+ char *hold = input_line_pointer++;
+
+ /* First try for parenthesized register ... */
+ expression (tok);
+ if (*input_line_pointer == ')' && tok->X_op == O_register)
+ {
+ tok->X_op = (saw_comma ? O_cpregister : O_pregister);
+ saw_comma = 0;
+ saw_arg = 1;
+ ++input_line_pointer;
+ ++tok;
+ break;
+ }
+
+ /* ... then fall through to plain expression. */
+ input_line_pointer = hold;
+ }
+
+ default:
+ if (saw_arg && !saw_comma)
+ goto err;
+
+ expression (tok);
+ if (tok->X_op == O_illegal || tok->X_op == O_absent)
+ goto err;
+
+ saw_comma = 0;
+ saw_arg = 1;
+ ++tok;
+ break;
+ }
+ }
+
+fini:
+ if (saw_comma)
+ goto err;
+ input_line_pointer = old_input_line_pointer;
+
+#ifdef DEBUG_ALPHA
+ debug_exp (orig_tok, ntok - (end_tok - tok));
+#endif
+#ifdef RELOC_OP_P
+ is_end_of_line[(unsigned char) '!'] = 0;
+#endif
+
+ return ntok - (end_tok - tok);
+
+err:
+#ifdef RELOC_OP_P
+ is_end_of_line[(unsigned char) '!'] = 0;
+#endif
+ input_line_pointer = old_input_line_pointer;
+ return TOKENIZE_ERROR;
+
+#ifdef RELOC_OP_P
+err_report:
+ is_end_of_line[(unsigned char) '!'] = 0;
+#endif
+ input_line_pointer = old_input_line_pointer;
+ return TOKENIZE_ERROR_REPORT;
+}
+
+/* Search forward through all variants of an opcode looking for a
+ syntax match. */
+
+static const struct alpha_opcode *
+find_opcode_match (const struct alpha_opcode *first_opcode,
+ const expressionS *tok,
+ int *pntok,
+ int *pcpumatch)
+{
+ const struct alpha_opcode *opcode = first_opcode;
+ int ntok = *pntok;
+ int got_cpu_match = 0;
+
+ do
+ {
+ const unsigned char *opidx;
+ int tokidx = 0;
+
+ /* Don't match opcodes that don't exist on this architecture. */
+ if (!(opcode->flags & alpha_target))
+ goto match_failed;
+
+ got_cpu_match = 1;
+
+ for (opidx = opcode->operands; *opidx; ++opidx)
+ {
+ const struct alpha_operand *operand = &alpha_operands[*opidx];
+
+ /* Only take input from real operands. */
+ if (operand->flags & AXP_OPERAND_FAKE)
+ continue;
+
+ /* When we expect input, make sure we have it. */
+ if (tokidx >= ntok)
+ {
+ if ((operand->flags & AXP_OPERAND_OPTIONAL_MASK) == 0)
+ goto match_failed;
+ continue;
+ }
+
+ /* Match operand type with expression type. */
+ switch (operand->flags & AXP_OPERAND_TYPECHECK_MASK)
+ {
+ case AXP_OPERAND_IR:
+ if (tok[tokidx].X_op != O_register
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+ case AXP_OPERAND_FPR:
+ if (tok[tokidx].X_op != O_register
+ || !is_fpr_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+ case AXP_OPERAND_IR | AXP_OPERAND_PARENS:
+ if (tok[tokidx].X_op != O_pregister
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+ case AXP_OPERAND_IR | AXP_OPERAND_PARENS | AXP_OPERAND_COMMA:
+ if (tok[tokidx].X_op != O_cpregister
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ break;
+
+ case AXP_OPERAND_RELATIVE:
+ case AXP_OPERAND_SIGNED:
+ case AXP_OPERAND_UNSIGNED:
+ switch (tok[tokidx].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ case O_pregister:
+ case O_cpregister:
+ goto match_failed;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ /* Everything else should have been fake. */
+ abort ();
+ }
+ ++tokidx;
+ }
+
+ /* Possible match -- did we use all of our input? */
+ if (tokidx == ntok)
+ {
+ *pntok = ntok;
+ return opcode;
+ }
+
+ match_failed:;
+ }
+ while (++opcode - alpha_opcodes < (int) alpha_num_opcodes
+ && !strcmp (opcode->name, first_opcode->name));
+
+ if (*pcpumatch)
+ *pcpumatch = got_cpu_match;
+
+ return NULL;
+}
+
+/* Given an opcode name and a pre-tokenized set of arguments, assemble
+ the insn, but do not emit it.
+
+ Note that this implies no macros allowed, since we can't store more
+ than one insn in an insn structure. */
+
+static void
+assemble_tokens_to_insn (const char *opname,
+ const expressionS *tok,
+ int ntok,
+ struct alpha_insn *insn)
+{
+ const struct alpha_opcode *opcode;
+
+ /* Search opcodes. */
+ opcode = (const struct alpha_opcode *) hash_find (alpha_opcode_hash, opname);
+ if (opcode)
+ {
+ int cpumatch;
+ opcode = find_opcode_match (opcode, tok, &ntok, &cpumatch);
+ if (opcode)
+ {
+ assemble_insn (opcode, tok, ntok, insn, BFD_RELOC_UNUSED);
+ return;
+ }
+ else if (cpumatch)
+ as_bad (_("inappropriate arguments for opcode `%s'"), opname);
+ else
+ as_bad (_("opcode `%s' not supported for target %s"), opname,
+ alpha_target_name);
+ }
+ else
+ as_bad (_("unknown opcode `%s'"), opname);
+}
+
+/* Build a BFD section with its flags set appropriately for the .lita,
+ .lit8, or .lit4 sections. */
+
+static void
+create_literal_section (const char *name,
+ segT *secp,
+ symbolS **symp)
+{
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ segT new_sec;
+
+ *secp = new_sec = subseg_new (name, 0);
+ subseg_set (current_section, current_subsec);
+ bfd_set_section_alignment (stdoutput, new_sec, 4);
+ bfd_set_section_flags (stdoutput, new_sec,
+ SEC_RELOC | SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_DATA);
+
+ S_CLEAR_EXTERNAL (*symp = section_symbol (new_sec));
+}
+
+/* Load a (partial) expression into a target register.
+
+ If poffset is not null, after the call it will either contain
+ O_constant 0, or a 16-bit offset appropriate for any MEM format
+ instruction. In addition, pbasereg will be modified to point to
+ the base register to use in that MEM format instruction.
+
+ In any case, *pbasereg should contain a base register to add to the
+ expression. This will normally be either AXP_REG_ZERO or
+ alpha_gp_register. Symbol addresses will always be loaded via $gp,
+ so "foo($0)" is interpreted as adding the address of foo to $0;
+ i.e. "ldq $targ, LIT($gp); addq $targ, $0, $targ". Odd, perhaps,
+ but this is what OSF/1 does.
+
+ If explicit relocations of the form !literal!<number> are allowed,
+ and used, then explicit_reloc with be an expression pointer.
+
+ Finally, the return value is nonzero if the calling macro may emit
+ a LITUSE reloc if otherwise appropriate; the return value is the
+ sequence number to use. */
+
+static long
+load_expression (int targreg,
+ const expressionS *exp,
+ int *pbasereg,
+ expressionS *poffset,
+ const char *opname)
+{
+ long emit_lituse = 0;
+ offsetT addend = exp->X_add_number;
+ int basereg = *pbasereg;
+ struct alpha_insn insn;
+ expressionS newtok[3];
+
+ switch (exp->X_op)
+ {
+ case O_symbol:
+ {
+#ifdef OBJ_ECOFF
+ offsetT lit;
+
+ /* Attempt to reduce .lit load by splitting the offset from
+ its symbol when possible, but don't create a situation in
+ which we'd fail. */
+ if (!range_signed_32 (addend) &&
+ (alpha_noat_on || targreg == AXP_REG_AT))
+ {
+ lit = add_to_literal_pool (exp->X_add_symbol, addend,
+ alpha_lita_section, 8);
+ addend = 0;
+ }
+ else
+ lit = add_to_literal_pool (exp->X_add_symbol, 0,
+ alpha_lita_section, 8);
+
+ if (lit >= 0x8000)
+ as_fatal (_("overflow in literal (.lita) table"));
+
+ /* Emit "ldq r, lit(gp)". */
+
+ if (basereg != alpha_gp_register && targreg == basereg)
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+ if (targreg == AXP_REG_AT)
+ as_bad (_("macro requires $at while $at in use"));
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ }
+ else
+ set_tok_reg (newtok[0], targreg);
+
+ set_tok_sym (newtok[1], alpha_lita_symbol, lit);
+ set_tok_preg (newtok[2], alpha_gp_register);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ gas_assert (insn.nfixups == 1);
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITERAL;
+ insn.sequence = emit_lituse = next_sequence_num--;
+#endif /* OBJ_ECOFF */
+#ifdef OBJ_ELF
+ /* Emit "ldq r, gotoff(gp)". */
+
+ if (basereg != alpha_gp_register && targreg == basereg)
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+ if (targreg == AXP_REG_AT)
+ as_bad (_("macro requires $at while $at in use"));
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ }
+ else
+ set_tok_reg (newtok[0], targreg);
+
+ /* XXX: Disable this .got minimizing optimization so that we can get
+ better instruction offset knowledge in the compiler. This happens
+ very infrequently anyway. */
+ if (1
+ || (!range_signed_32 (addend)
+ && (alpha_noat_on || targreg == AXP_REG_AT)))
+ {
+ newtok[1] = *exp;
+ addend = 0;
+ }
+ else
+ set_tok_sym (newtok[1], exp->X_add_symbol, 0);
+
+ set_tok_preg (newtok[2], alpha_gp_register);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ gas_assert (insn.nfixups == 1);
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_ELF_LITERAL;
+ insn.sequence = emit_lituse = next_sequence_num--;
+#endif /* OBJ_ELF */
+#ifdef OBJ_EVAX
+ /* Find symbol or symbol pointer in link section. */
+
+ if (exp->X_add_symbol == alpha_evax_proc->symbol)
+ {
+ /* Linkage-relative expression. */
+ set_tok_reg (newtok[0], targreg);
+
+ if (range_signed_16 (addend))
+ {
+ set_tok_const (newtok[1], addend);
+ addend = 0;
+ }
+ else
+ {
+ set_tok_const (newtok[1], 0);
+ }
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("lda", newtok, 3, &insn);
+ }
+ else
+ {
+ const char *symname = S_GET_NAME (exp->X_add_symbol);
+ const char *ptr1, *ptr2;
+ int symlen = strlen (symname);
+
+ if ((symlen > 4 &&
+ strcmp (ptr2 = &symname [symlen - 4], "..lk") == 0))
+ {
+ /* Access to an item whose address is stored in the linkage
+ section. Just read the address. */
+ set_tok_reg (newtok[0], targreg);
+
+ newtok[1] = *exp;
+ newtok[1].X_op = O_subtract;
+ newtok[1].X_op_symbol = alpha_evax_proc->symbol;
+
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+ alpha_linkage_symbol = exp->X_add_symbol;
+
+ if (poffset)
+ set_tok_const (*poffset, 0);
+
+ if (alpha_flag_replace && targreg == 26)
+ {
+ /* Add a NOP fixup for 'ldX $26,YYY..NAME..lk'. */
+ char *ensymname;
+ symbolS *ensym;
+
+ /* Build the entry name as 'NAME..en'. */
+ ptr1 = strstr (symname, "..") + 2;
+ if (ptr1 > ptr2)
+ ptr1 = symname;
+ ensymname = (char *) alloca (ptr2 - ptr1 + 5);
+ memcpy (ensymname, ptr1, ptr2 - ptr1);
+ memcpy (ensymname + (ptr2 - ptr1), "..en", 5);
+
+ gas_assert (insn.nfixups + 1 <= MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = BFD_RELOC_ALPHA_NOP;
+ ensym = symbol_find_or_make (ensymname);
+ symbol_mark_used (ensym);
+ /* The fixup must be the same as the BFD_RELOC_ALPHA_BOH
+ case in emit_jsrjmp. See B.4.5.2 of the OpenVMS Linker
+ Utility Manual. */
+ insn.fixups[insn.nfixups].exp.X_op = O_symbol;
+ insn.fixups[insn.nfixups].exp.X_add_symbol = ensym;
+ insn.fixups[insn.nfixups].exp.X_add_number = 0;
+ insn.fixups[insn.nfixups].xtrasym = alpha_linkage_symbol;
+ insn.fixups[insn.nfixups].procsym = alpha_evax_proc->symbol;
+ insn.nfixups++;
+
+ /* ??? Force bsym to be instantiated now, as it will be
+ too late to do so in tc_gen_reloc. */
+ symbol_get_bfdsym (exp->X_add_symbol);
+ }
+ else if (alpha_flag_replace && targreg == 27)
+ {
+ /* Add a lda fixup for 'ldX $27,YYY.NAME..lk+8'. */
+ char *psymname;
+ symbolS *psym;
+
+ /* Extract NAME. */
+ ptr1 = strstr (symname, "..") + 2;
+ if (ptr1 > ptr2)
+ ptr1 = symname;
+ psymname = (char *) alloca (ptr2 - ptr1 + 1);
+ memcpy (psymname, ptr1, ptr2 - ptr1);
+ psymname [ptr2 - ptr1] = 0;
+
+ gas_assert (insn.nfixups + 1 <= MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = BFD_RELOC_ALPHA_LDA;
+ psym = symbol_find_or_make (psymname);
+ symbol_mark_used (psym);
+ insn.fixups[insn.nfixups].exp.X_op = O_subtract;
+ insn.fixups[insn.nfixups].exp.X_add_symbol = psym;
+ insn.fixups[insn.nfixups].exp.X_op_symbol = alpha_evax_proc->symbol;
+ insn.fixups[insn.nfixups].exp.X_add_number = 0;
+ insn.fixups[insn.nfixups].xtrasym = alpha_linkage_symbol;
+ insn.fixups[insn.nfixups].procsym = alpha_evax_proc->symbol;
+ insn.nfixups++;
+ }
+
+ emit_insn (&insn);
+ return 0;
+ }
+ else
+ {
+ /* Not in the linkage section. Put the value into the linkage
+ section. */
+ symbolS *linkexp;
+
+ if (!range_signed_32 (addend))
+ addend = sign_extend_32 (addend);
+ linkexp = add_to_link_pool (exp->X_add_symbol, 0);
+ set_tok_reg (newtok[0], targreg);
+ set_tok_sym (newtok[1], linkexp, 0);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+ }
+ }
+#endif /* OBJ_EVAX */
+
+ emit_insn (&insn);
+
+#ifndef OBJ_EVAX
+ if (basereg != alpha_gp_register && basereg != AXP_REG_ZERO)
+ {
+ /* Emit "addq r, base, r". */
+
+ set_tok_reg (newtok[1], basereg);
+ set_tok_reg (newtok[2], targreg);
+ assemble_tokens ("addq", newtok, 3, 0);
+ }
+#endif
+ basereg = targreg;
+ }
+ break;
+
+ case O_constant:
+ break;
+
+ case O_subtract:
+ /* Assume that this difference expression will be resolved to an
+ absolute value and that that value will fit in 16 bits. */
+
+ set_tok_reg (newtok[0], targreg);
+ newtok[1] = *exp;
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens (opname, newtok, 3, 0);
+
+ if (poffset)
+ set_tok_const (*poffset, 0);
+ return 0;
+
+ case O_big:
+ if (exp->X_add_number > 0)
+ as_bad (_("bignum invalid; zero assumed"));
+ else
+ as_bad (_("floating point number invalid; zero assumed"));
+ addend = 0;
+ break;
+
+ default:
+ as_bad (_("can't handle expression"));
+ addend = 0;
+ break;
+ }
+
+ if (!range_signed_32 (addend))
+ {
+#ifdef OBJ_EVAX
+ symbolS *litexp;
+#else
+ offsetT lit;
+ long seq_num = next_sequence_num--;
+#endif
+
+ /* For 64-bit addends, just put it in the literal pool. */
+#ifdef OBJ_EVAX
+ /* Emit "ldq targreg, lit(basereg)". */
+ litexp = add_to_link_pool (section_symbol (absolute_section), addend);
+ set_tok_reg (newtok[0], targreg);
+ set_tok_sym (newtok[1], litexp, 0);
+ set_tok_preg (newtok[2], alpha_gp_register);
+ assemble_tokens ("ldq", newtok, 3, 0);
+#else
+
+ if (alpha_lit8_section == NULL)
+ {
+ create_literal_section (".lit8",
+ &alpha_lit8_section,
+ &alpha_lit8_symbol);
+
+#ifdef OBJ_ECOFF
+ alpha_lit8_literal = add_to_literal_pool (alpha_lit8_symbol, 0x8000,
+ alpha_lita_section, 8);
+ if (alpha_lit8_literal >= 0x8000)
+ as_fatal (_("overflow in literal (.lita) table"));
+#endif
+ }
+
+ lit = add_to_literal_pool (NULL, addend, alpha_lit8_section, 8) - 0x8000;
+ if (lit >= 0x8000)
+ as_fatal (_("overflow in literal (.lit8) table"));
+
+ /* Emit "lda litreg, .lit8+0x8000". */
+
+ if (targreg == basereg)
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+ if (targreg == AXP_REG_AT)
+ as_bad (_("macro requires $at while $at in use"));
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ }
+ else
+ set_tok_reg (newtok[0], targreg);
+#ifdef OBJ_ECOFF
+ set_tok_sym (newtok[1], alpha_lita_symbol, alpha_lit8_literal);
+#endif
+#ifdef OBJ_ELF
+ set_tok_sym (newtok[1], alpha_lit8_symbol, 0x8000);
+#endif
+ set_tok_preg (newtok[2], alpha_gp_register);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ gas_assert (insn.nfixups == 1);
+#ifdef OBJ_ECOFF
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_LITERAL;
+#endif
+#ifdef OBJ_ELF
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_ELF_LITERAL;
+#endif
+ insn.sequence = seq_num;
+
+ emit_insn (&insn);
+
+ /* Emit "ldq litreg, lit(litreg)". */
+
+ set_tok_const (newtok[1], lit);
+ set_tok_preg (newtok[2], newtok[0].X_add_number);
+
+ assemble_tokens_to_insn ("ldq", newtok, 3, &insn);
+
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = seq_num;
+ emit_lituse = 0;
+
+ emit_insn (&insn);
+
+ /* Emit "addq litreg, base, target". */
+
+ if (basereg != AXP_REG_ZERO)
+ {
+ set_tok_reg (newtok[1], basereg);
+ set_tok_reg (newtok[2], targreg);
+ assemble_tokens ("addq", newtok, 3, 0);
+ }
+#endif /* !OBJ_EVAX */
+
+ if (poffset)
+ set_tok_const (*poffset, 0);
+ *pbasereg = targreg;
+ }
+ else
+ {
+ offsetT low, high, extra, tmp;
+
+ /* For 32-bit operands, break up the addend. */
+
+ low = sign_extend_16 (addend);
+ tmp = addend - low;
+ high = sign_extend_16 (tmp >> 16);
+
+ if (tmp - (high << 16))
+ {
+ extra = 0x4000;
+ tmp -= 0x40000000;
+ high = sign_extend_16 (tmp >> 16);
+ }
+ else
+ extra = 0;
+
+ set_tok_reg (newtok[0], targreg);
+ set_tok_preg (newtok[2], basereg);
+
+ if (extra)
+ {
+ /* Emit "ldah r, extra(r). */
+ set_tok_const (newtok[1], extra);
+ assemble_tokens ("ldah", newtok, 3, 0);
+ set_tok_preg (newtok[2], basereg = targreg);
+ }
+
+ if (high)
+ {
+ /* Emit "ldah r, high(r). */
+ set_tok_const (newtok[1], high);
+ assemble_tokens ("ldah", newtok, 3, 0);
+ basereg = targreg;
+ set_tok_preg (newtok[2], basereg);
+ }
+
+ if ((low && !poffset) || (!poffset && basereg != targreg))
+ {
+ /* Emit "lda r, low(base)". */
+ set_tok_const (newtok[1], low);
+ assemble_tokens ("lda", newtok, 3, 0);
+ basereg = targreg;
+ low = 0;
+ }
+
+ if (poffset)
+ set_tok_const (*poffset, low);
+ *pbasereg = basereg;
+ }
+
+ return emit_lituse;
+}
+
+/* The lda macro differs from the lda instruction in that it handles
+ most simple expressions, particularly symbol address loads and
+ large constants. */
+
+static void
+emit_lda (const expressionS *tok,
+ int ntok,
+ const void * unused ATTRIBUTE_UNUSED)
+{
+ int basereg;
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ (void) load_expression (tok[0].X_add_number, &tok[1], &basereg, NULL, "lda");
+}
+
+/* The ldah macro differs from the ldah instruction in that it has $31
+ as an implied base register. */
+
+static void
+emit_ldah (const expressionS *tok,
+ int ntok ATTRIBUTE_UNUSED,
+ const void * unused ATTRIBUTE_UNUSED)
+{
+ expressionS newtok[3];
+
+ newtok[0] = tok[0];
+ newtok[1] = tok[1];
+ set_tok_preg (newtok[2], AXP_REG_ZERO);
+
+ assemble_tokens ("ldah", newtok, 3, 0);
+}
+
+/* Called internally to handle all alignment needs. This takes care
+ of eliding calls to frag_align if'n the cached current alignment
+ says we've already got it, as well as taking care of the auto-align
+ feature wrt labels. */
+
+static void
+alpha_align (int n,
+ char *pfill,
+ symbolS *label,
+ int force ATTRIBUTE_UNUSED)
+{
+ if (alpha_current_align >= n)
+ return;
+
+ if (pfill == NULL)
+ {
+ if (subseg_text_p (now_seg))
+ frag_align_code (n, 0);
+ else
+ frag_align (n, 0, 0);
+ }
+ else
+ frag_align (n, *pfill, 0);
+
+ alpha_current_align = n;
+
+ if (label != NULL && S_GET_SEGMENT (label) == now_seg)
+ {
+ symbol_set_frag (label, frag_now);
+ S_SET_VALUE (label, (valueT) frag_now_fix ());
+ }
+
+ record_alignment (now_seg, n);
+
+ /* ??? If alpha_flag_relax && force && elf, record the requested alignment
+ in a reloc for the linker to see. */
+}
+
+/* Actually output an instruction with its fixup. */
+
+static void
+emit_insn (struct alpha_insn *insn)
+{
+ char *f;
+ int i;
+
+ /* Take care of alignment duties. */
+ if (alpha_auto_align_on && alpha_current_align < 2)
+ alpha_align (2, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > 2)
+ alpha_current_align = 2;
+ alpha_insn_label = NULL;
+
+ /* Write out the instruction. */
+ f = frag_more (4);
+ md_number_to_chars (f, insn->insn, 4);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (4);
+#endif
+
+ /* Apply the fixups in order. */
+ for (i = 0; i < insn->nfixups; ++i)
+ {
+ const struct alpha_operand *operand = (const struct alpha_operand *) 0;
+ struct alpha_fixup *fixup = &insn->fixups[i];
+ struct alpha_reloc_tag *info = NULL;
+ int size, pcrel;
+ fixS *fixP;
+
+ /* Some fixups are only used internally and so have no howto. */
+ if ((int) fixup->reloc < 0)
+ {
+ operand = &alpha_operands[-(int) fixup->reloc];
+ size = 4;
+ pcrel = ((operand->flags & AXP_OPERAND_RELATIVE) != 0);
+ }
+ else if (fixup->reloc > BFD_RELOC_UNUSED
+ || fixup->reloc == BFD_RELOC_ALPHA_GPDISP_HI16
+ || fixup->reloc == BFD_RELOC_ALPHA_GPDISP_LO16)
+ {
+ size = 2;
+ pcrel = 0;
+ }
+ else
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) fixup->reloc);
+ gas_assert (reloc_howto);
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ switch (fixup->reloc)
+ {
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_NOP:
+ case BFD_RELOC_ALPHA_BSR:
+ case BFD_RELOC_ALPHA_LDA:
+ case BFD_RELOC_ALPHA_BOH:
+ break;
+#endif
+ default:
+ gas_assert (size >= 1 && size <= 4);
+ }
+
+ pcrel = reloc_howto->pc_relative;
+ }
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal, size,
+ &fixup->exp, pcrel, (bfd_reloc_code_real_type) fixup->reloc);
+
+ /* Turn off complaints that the addend is too large for some fixups,
+ and copy in the sequence number for the explicit relocations. */
+ switch (fixup->reloc)
+ {
+ case BFD_RELOC_ALPHA_HINT:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_ALPHA_GPREL_HI16:
+ case BFD_RELOC_ALPHA_GPREL_LO16:
+ case BFD_RELOC_ALPHA_GOTDTPREL16:
+ case BFD_RELOC_ALPHA_DTPREL_HI16:
+ case BFD_RELOC_ALPHA_DTPREL_LO16:
+ case BFD_RELOC_ALPHA_DTPREL16:
+ case BFD_RELOC_ALPHA_GOTTPREL16:
+ case BFD_RELOC_ALPHA_TPREL_HI16:
+ case BFD_RELOC_ALPHA_TPREL_LO16:
+ case BFD_RELOC_ALPHA_TPREL16:
+ fixP->fx_no_overflow = 1;
+ break;
+
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ fixP->fx_no_overflow = 1;
+ fixP->fx_addsy = section_symbol (now_seg);
+ fixP->fx_offset = 0;
+
+ info = get_alpha_reloc_tag (insn->sequence);
+ if (++info->n_master > 1)
+ as_bad (_("too many ldah insns for !gpdisp!%ld"), insn->sequence);
+ if (info->segment != now_seg)
+ as_bad (_("both insns for !gpdisp!%ld must be in the same section"),
+ insn->sequence);
+ fixP->tc_fix_data.info = info;
+ break;
+
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ fixP->fx_no_overflow = 1;
+
+ info = get_alpha_reloc_tag (insn->sequence);
+ if (++info->n_slaves > 1)
+ as_bad (_("too many lda insns for !gpdisp!%ld"), insn->sequence);
+ if (info->segment != now_seg)
+ as_bad (_("both insns for !gpdisp!%ld must be in the same section"),
+ insn->sequence);
+ fixP->tc_fix_data.info = info;
+ info->slaves = fixP;
+ break;
+
+ case BFD_RELOC_ALPHA_LITERAL:
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ fixP->fx_no_overflow = 1;
+
+ if (insn->sequence == 0)
+ break;
+ info = get_alpha_reloc_tag (insn->sequence);
+ info->master = fixP;
+ info->n_master++;
+ if (info->segment != now_seg)
+ info->multi_section_p = 1;
+ fixP->tc_fix_data.info = info;
+ break;
+
+#ifdef RELOC_OP_P
+ case DUMMY_RELOC_LITUSE_ADDR:
+ fixP->fx_offset = LITUSE_ALPHA_ADDR;
+ goto do_lituse;
+ case DUMMY_RELOC_LITUSE_BASE:
+ fixP->fx_offset = LITUSE_ALPHA_BASE;
+ goto do_lituse;
+ case DUMMY_RELOC_LITUSE_BYTOFF:
+ fixP->fx_offset = LITUSE_ALPHA_BYTOFF;
+ goto do_lituse;
+ case DUMMY_RELOC_LITUSE_JSR:
+ fixP->fx_offset = LITUSE_ALPHA_JSR;
+ goto do_lituse;
+ case DUMMY_RELOC_LITUSE_TLSGD:
+ fixP->fx_offset = LITUSE_ALPHA_TLSGD;
+ goto do_lituse;
+ case DUMMY_RELOC_LITUSE_TLSLDM:
+ fixP->fx_offset = LITUSE_ALPHA_TLSLDM;
+ goto do_lituse;
+ case DUMMY_RELOC_LITUSE_JSRDIRECT:
+ fixP->fx_offset = LITUSE_ALPHA_JSRDIRECT;
+ goto do_lituse;
+ do_lituse:
+ fixP->fx_addsy = section_symbol (now_seg);
+ fixP->fx_r_type = BFD_RELOC_ALPHA_LITUSE;
+
+ info = get_alpha_reloc_tag (insn->sequence);
+ if (fixup->reloc == DUMMY_RELOC_LITUSE_TLSGD)
+ info->saw_lu_tlsgd = 1;
+ else if (fixup->reloc == DUMMY_RELOC_LITUSE_TLSLDM)
+ info->saw_lu_tlsldm = 1;
+ if (++info->n_slaves > 1)
+ {
+ if (info->saw_lu_tlsgd)
+ as_bad (_("too many lituse insns for !lituse_tlsgd!%ld"),
+ insn->sequence);
+ else if (info->saw_lu_tlsldm)
+ as_bad (_("too many lituse insns for !lituse_tlsldm!%ld"),
+ insn->sequence);
+ }
+ fixP->tc_fix_data.info = info;
+ fixP->tc_fix_data.next_reloc = info->slaves;
+ info->slaves = fixP;
+ if (info->segment != now_seg)
+ info->multi_section_p = 1;
+ break;
+
+ case BFD_RELOC_ALPHA_TLSGD:
+ fixP->fx_no_overflow = 1;
+
+ if (insn->sequence == 0)
+ break;
+ info = get_alpha_reloc_tag (insn->sequence);
+ if (info->saw_tlsgd)
+ as_bad (_("duplicate !tlsgd!%ld"), insn->sequence);
+ else if (info->saw_tlsldm)
+ as_bad (_("sequence number in use for !tlsldm!%ld"),
+ insn->sequence);
+ else
+ info->saw_tlsgd = 1;
+ fixP->tc_fix_data.info = info;
+ break;
+
+ case BFD_RELOC_ALPHA_TLSLDM:
+ fixP->fx_no_overflow = 1;
+
+ if (insn->sequence == 0)
+ break;
+ info = get_alpha_reloc_tag (insn->sequence);
+ if (info->saw_tlsldm)
+ as_bad (_("duplicate !tlsldm!%ld"), insn->sequence);
+ else if (info->saw_tlsgd)
+ as_bad (_("sequence number in use for !tlsgd!%ld"),
+ insn->sequence);
+ else
+ info->saw_tlsldm = 1;
+ fixP->tc_fix_data.info = info;
+ break;
+#endif
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_NOP:
+ case BFD_RELOC_ALPHA_LDA:
+ case BFD_RELOC_ALPHA_BSR:
+ case BFD_RELOC_ALPHA_BOH:
+ info = get_alpha_reloc_tag (next_sequence_num--);
+ fixP->tc_fix_data.info = info;
+ fixP->tc_fix_data.info->sym = fixup->xtrasym;
+ fixP->tc_fix_data.info->psym = fixup->procsym;
+ break;
+#endif
+
+ default:
+ if ((int) fixup->reloc < 0)
+ {
+ if (operand->flags & AXP_OPERAND_NOOVERFLOW)
+ fixP->fx_no_overflow = 1;
+ }
+ break;
+ }
+ }
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned
+insert_operand (unsigned insn,
+ const struct alpha_operand *operand,
+ offsetT val,
+ char *file,
+ unsigned line)
+{
+ if (operand->bits != 32 && !(operand->flags & AXP_OPERAND_NOOVERFLOW))
+ {
+ offsetT min, max;
+
+ if (operand->flags & AXP_OPERAND_SIGNED)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = -(1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if (val < min || val > max)
+ as_bad_value_out_of_range (_("operand"), val, min, max, file, line);
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+
+ insn = (*operand->insert) (insn, val, &errmsg);
+ if (errmsg)
+ as_warn ("%s", errmsg);
+ }
+ else
+ insn |= ((val & ((1 << operand->bits) - 1)) << operand->shift);
+
+ return insn;
+}
+
+/* Turn an opcode description and a set of arguments into
+ an instruction and a fixup. */
+
+static void
+assemble_insn (const struct alpha_opcode *opcode,
+ const expressionS *tok,
+ int ntok,
+ struct alpha_insn *insn,
+ extended_bfd_reloc_code_real_type reloc)
+{
+ const struct alpha_operand *reloc_operand = NULL;
+ const expressionS *reloc_exp = NULL;
+ const unsigned char *argidx;
+ unsigned image;
+ int tokidx = 0;
+
+ memset (insn, 0, sizeof (*insn));
+ image = opcode->opcode;
+
+ for (argidx = opcode->operands; *argidx; ++argidx)
+ {
+ const struct alpha_operand *operand = &alpha_operands[*argidx];
+ const expressionS *t = (const expressionS *) 0;
+
+ if (operand->flags & AXP_OPERAND_FAKE)
+ {
+ /* Fake operands take no value and generate no fixup. */
+ image = insert_operand (image, operand, 0, NULL, 0);
+ continue;
+ }
+
+ if (tokidx >= ntok)
+ {
+ switch (operand->flags & AXP_OPERAND_OPTIONAL_MASK)
+ {
+ case AXP_OPERAND_DEFAULT_FIRST:
+ t = &tok[0];
+ break;
+ case AXP_OPERAND_DEFAULT_SECOND:
+ t = &tok[1];
+ break;
+ case AXP_OPERAND_DEFAULT_ZERO:
+ {
+ static expressionS zero_exp;
+ t = &zero_exp;
+ zero_exp.X_op = O_constant;
+ zero_exp.X_unsigned = 1;
+ }
+ break;
+ default:
+ abort ();
+ }
+ }
+ else
+ t = &tok[tokidx++];
+
+ switch (t->X_op)
+ {
+ case O_register:
+ case O_pregister:
+ case O_cpregister:
+ image = insert_operand (image, operand, regno (t->X_add_number),
+ NULL, 0);
+ break;
+
+ case O_constant:
+ image = insert_operand (image, operand, t->X_add_number, NULL, 0);
+ gas_assert (reloc_operand == NULL);
+ reloc_operand = operand;
+ reloc_exp = t;
+ break;
+
+ default:
+ /* This is only 0 for fields that should contain registers,
+ which means this pattern shouldn't have matched. */
+ if (operand->default_reloc == 0)
+ abort ();
+
+ /* There is one special case for which an insn receives two
+ relocations, and thus the user-supplied reloc does not
+ override the operand reloc. */
+ if (operand->default_reloc == BFD_RELOC_ALPHA_HINT)
+ {
+ struct alpha_fixup *fixup;
+
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixup = &insn->fixups[insn->nfixups++];
+ fixup->exp = *t;
+ fixup->reloc = BFD_RELOC_ALPHA_HINT;
+ }
+ else
+ {
+ if (reloc == BFD_RELOC_UNUSED)
+ reloc = operand->default_reloc;
+
+ gas_assert (reloc_operand == NULL);
+ reloc_operand = operand;
+ reloc_exp = t;
+ }
+ break;
+ }
+ }
+
+ if (reloc != BFD_RELOC_UNUSED)
+ {
+ struct alpha_fixup *fixup;
+
+ if (insn->nfixups >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ /* ??? My but this is hacky. But the OSF/1 assembler uses the same
+ relocation tag for both ldah and lda with gpdisp. Choose the
+ correct internal relocation based on the opcode. */
+ if (reloc == BFD_RELOC_ALPHA_GPDISP)
+ {
+ if (strcmp (opcode->name, "ldah") == 0)
+ reloc = BFD_RELOC_ALPHA_GPDISP_HI16;
+ else if (strcmp (opcode->name, "lda") == 0)
+ reloc = BFD_RELOC_ALPHA_GPDISP_LO16;
+ else
+ as_bad (_("invalid relocation for instruction"));
+ }
+
+ /* If this is a real relocation (as opposed to a lituse hint), then
+ the relocation width should match the operand width.
+ Take care of -MDISP in operand table. */
+ else if (reloc < BFD_RELOC_UNUSED && reloc > 0)
+ {
+ reloc_howto_type *reloc_howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) reloc);
+ if (reloc_operand == NULL
+ || reloc_howto->bitsize != reloc_operand->bits)
+ {
+ as_bad (_("invalid relocation for field"));
+ return;
+ }
+ }
+
+ fixup = &insn->fixups[insn->nfixups++];
+ if (reloc_exp)
+ fixup->exp = *reloc_exp;
+ else
+ fixup->exp.X_op = O_absent;
+ fixup->reloc = reloc;
+ }
+
+ insn->insn = image;
+}
+
+/* Handle all "simple" integer register loads -- ldq, ldq_l, ldq_u,
+ etc. They differ from the real instructions in that they do simple
+ expressions like the lda macro. */
+
+static void
+emit_ir_load (const expressionS *tok,
+ int ntok,
+ const void * opname)
+{
+ int basereg;
+ long lituse;
+ expressionS newtok[3];
+ struct alpha_insn insn;
+ const char *symname
+ = tok[1].X_add_symbol ? S_GET_NAME (tok[1].X_add_symbol): "";
+ int symlen = strlen (symname);
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ lituse = load_expression (tok[0].X_add_number, &tok[1],
+ &basereg, &newtok[1], (const char *) opname);
+
+ if (basereg == alpha_gp_register &&
+ (symlen > 4 && strcmp (&symname [symlen - 4], "..lk") == 0))
+ return;
+
+ newtok[0] = tok[0];
+ set_tok_preg (newtok[2], basereg);
+
+ assemble_tokens_to_insn ((const char *) opname, newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+}
+
+/* Handle fp register loads, and both integer and fp register stores.
+ Again, we handle simple expressions. */
+
+static void
+emit_loadstore (const expressionS *tok,
+ int ntok,
+ const void * opname)
+{
+ int basereg;
+ long lituse;
+ expressionS newtok[3];
+ struct alpha_insn insn;
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ if (tok[1].X_op != O_constant || !range_signed_16 (tok[1].X_add_number))
+ {
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ lituse = load_expression (AXP_REG_AT, &tok[1],
+ &basereg, &newtok[1], (const char *) opname);
+ }
+ else
+ {
+ newtok[1] = tok[1];
+ lituse = 0;
+ }
+
+ newtok[0] = tok[0];
+ set_tok_preg (newtok[2], basereg);
+
+ assemble_tokens_to_insn ((const char *) opname, newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+}
+
+/* Load a half-word or byte as an unsigned value. */
+
+static void
+emit_ldXu (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ if (alpha_target & AXP_OPCODE_BWX)
+ emit_ir_load (tok, ntok, ldXu_op[(long) vlgsize]);
+ else
+ {
+ expressionS newtok[3];
+ struct alpha_insn insn;
+ int basereg;
+ long lituse;
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant
+ ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ /* Emit "lda $at, exp". */
+ lituse = load_expression (AXP_REG_AT, &tok[1], &basereg, NULL, "lda");
+
+ /* Emit "ldq_u targ, 0($at)". */
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("ldq_u", newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "extXl targ, $at, targ". */
+ set_tok_reg (newtok[1], basereg);
+ newtok[2] = newtok[0];
+ assemble_tokens_to_insn (extXl_op[(long) vlgsize], newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BYTOFF;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+ }
+}
+
+/* Load a half-word or byte as a signed value. */
+
+static void
+emit_ldX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ emit_ldXu (tok, ntok, vlgsize);
+ assemble_tokens (sextX_op[(long) vlgsize], tok, 1, 1);
+}
+
+/* Load an integral value from an unaligned address as an unsigned
+ value. */
+
+static void
+emit_uldXu (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ long lgsize = (long) vlgsize;
+ expressionS newtok[3];
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ /* Emit "lda $at, exp". */
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* Emit "ldq_u $t9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "ldq_u $t10, size-1($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize) - 1);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "extXl $t9, $at, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T9);
+ assemble_tokens (extXl_op[lgsize], newtok, 3, 1);
+
+ /* Emit "extXh $t10, $at, $t10". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[2], AXP_REG_T10);
+ assemble_tokens (extXh_op[lgsize], newtok, 3, 1);
+
+ /* Emit "or $t9, $t10, targ". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ newtok[2] = tok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+}
+
+/* Load an integral value from an unaligned address as a signed value.
+ Note that quads should get funneled to the unsigned load since we
+ don't have to do the sign extension. */
+
+static void
+emit_uldX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ emit_uldXu (tok, ntok, vlgsize);
+ assemble_tokens (sextX_op[(long) vlgsize], tok, 1, 1);
+}
+
+/* Implement the ldil macro. */
+
+static void
+emit_ldil (const expressionS *tok,
+ int ntok,
+ const void * unused ATTRIBUTE_UNUSED)
+{
+ expressionS newtok[2];
+
+ memcpy (newtok, tok, sizeof (newtok));
+ newtok[1].X_add_number = sign_extend_32 (tok[1].X_add_number);
+
+ assemble_tokens ("lda", newtok, ntok, 1);
+}
+
+/* Store a half-word or byte. */
+
+static void
+emit_stX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ int lgsize = (int) (long) vlgsize;
+
+ if (alpha_target & AXP_OPCODE_BWX)
+ emit_loadstore (tok, ntok, stX_op[lgsize]);
+ else
+ {
+ expressionS newtok[3];
+ struct alpha_insn insn;
+ int basereg;
+ long lituse;
+
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ if (ntok == 2)
+ basereg = (tok[1].X_op == O_constant
+ ? AXP_REG_ZERO : alpha_gp_register);
+ else
+ basereg = tok[2].X_add_number;
+
+ /* Emit "lda $at, exp". */
+ lituse = load_expression (AXP_REG_AT, &tok[1], &basereg, NULL, "lda");
+
+ /* Emit "ldq_u $t9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], basereg);
+ assemble_tokens_to_insn ("ldq_u", newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "insXl src, $at, $t10". */
+ newtok[0] = tok[0];
+ set_tok_reg (newtok[1], basereg);
+ set_tok_reg (newtok[2], AXP_REG_T10);
+ assemble_tokens_to_insn (insXl_op[lgsize], newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BYTOFF;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "mskXl $t9, $at, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ newtok[2] = newtok[0];
+ assemble_tokens_to_insn (mskXl_op[lgsize], newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BYTOFF;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+
+ /* Emit "or $t9, $t10, $t9". */
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* Emit "stq_u $t9, 0($at). */
+ set_tok_const(newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens_to_insn ("stq_u", newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_BASE;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+ emit_insn (&insn);
+ }
+}
+
+/* Store an integer to an unaligned address. */
+
+static void
+emit_ustX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ int lgsize = (int) (long) vlgsize;
+ expressionS newtok[3];
+
+ /* Emit "lda $at, exp". */
+ memcpy (newtok, tok, sizeof (expressionS) * ntok);
+ newtok[0].X_add_number = AXP_REG_AT;
+ assemble_tokens ("lda", newtok, ntok, 1);
+
+ /* Emit "ldq_u $9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "ldq_u $10, size-1($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize) - 1);
+ assemble_tokens ("ldq_u", newtok, 3, 1);
+
+ /* Emit "insXl src, $at, $t11". */
+ newtok[0] = tok[0];
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ set_tok_reg (newtok[2], AXP_REG_T11);
+ assemble_tokens (insXl_op[lgsize], newtok, 3, 1);
+
+ /* Emit "insXh src, $at, $t12". */
+ set_tok_reg (newtok[2], AXP_REG_T12);
+ assemble_tokens (insXh_op[lgsize], newtok, 3, 1);
+
+ /* Emit "mskXl $t9, $at, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXl_op[lgsize], newtok, 3, 1);
+
+ /* Emit "mskXh $t10, $at, $t10". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ newtok[2] = newtok[0];
+ assemble_tokens (mskXh_op[lgsize], newtok, 3, 1);
+
+ /* Emit "or $t9, $t11, $t9". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ newtok[2] = newtok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* Emit "or $t10, $t12, $t10". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_T12);
+ newtok[2] = newtok[0];
+ assemble_tokens ("or", newtok, 3, 1);
+
+ /* Emit "stq_u $t10, size-1($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_const (newtok[1], (1 << lgsize) - 1);
+ set_tok_preg (newtok[2], AXP_REG_AT);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+
+ /* Emit "stq_u $t9, 0($at)". */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_const (newtok[1], 0);
+ assemble_tokens ("stq_u", newtok, 3, 1);
+}
+
+/* Sign extend a half-word or byte. The 32-bit sign extend is
+ implemented as "addl $31, $r, $t" in the opcode table. */
+
+static void
+emit_sextX (const expressionS *tok,
+ int ntok,
+ const void * vlgsize)
+{
+ long lgsize = (long) vlgsize;
+
+ if (alpha_target & AXP_OPCODE_BWX)
+ assemble_tokens (sextX_op[lgsize], tok, ntok, 0);
+ else
+ {
+ int bitshift = 64 - 8 * (1 << lgsize);
+ expressionS newtok[3];
+
+ /* Emit "sll src,bits,dst". */
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], bitshift);
+ newtok[2] = tok[ntok - 1];
+ assemble_tokens ("sll", newtok, 3, 1);
+
+ /* Emit "sra dst,bits,dst". */
+ newtok[0] = newtok[2];
+ assemble_tokens ("sra", newtok, 3, 1);
+ }
+}
+
+/* Implement the division and modulus macros. */
+
+#ifdef OBJ_EVAX
+
+/* Make register usage like in normal procedure call.
+ Don't clobber PV and RA. */
+
+static void
+emit_division (const expressionS *tok,
+ int ntok,
+ const void * symname)
+{
+ /* DIVISION and MODULUS. Yech.
+
+ Convert
+ OP x,y,result
+ to
+ mov x,R16 # if x != R16
+ mov y,R17 # if y != R17
+ lda AT,__OP
+ jsr AT,(AT),0
+ mov R0,result
+
+ with appropriate optimizations if R0,R16,R17 are the registers
+ specified by the compiler. */
+
+ int xr, yr, rr;
+ symbolS *sym;
+ expressionS newtok[3];
+
+ xr = regno (tok[0].X_add_number);
+ yr = regno (tok[1].X_add_number);
+
+ if (ntok < 3)
+ rr = xr;
+ else
+ rr = regno (tok[2].X_add_number);
+
+ /* Move the operands into the right place. */
+ if (yr == AXP_REG_R16 && xr == AXP_REG_R17)
+ {
+ /* They are in exactly the wrong order -- swap through AT. */
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ set_tok_reg (newtok[0], AXP_REG_R16);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_R17);
+ set_tok_reg (newtok[1], AXP_REG_R16);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ else
+ {
+ if (yr == AXP_REG_R16)
+ {
+ set_tok_reg (newtok[0], AXP_REG_R16);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (xr != AXP_REG_R16)
+ {
+ set_tok_reg (newtok[0], xr);
+ set_tok_reg (newtok[1], AXP_REG_R16);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (yr != AXP_REG_R16 && yr != AXP_REG_R17)
+ {
+ set_tok_reg (newtok[0], yr);
+ set_tok_reg (newtok[1], AXP_REG_R17);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ }
+
+ sym = symbol_find_or_make ((const char *) symname);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_sym (newtok[1], sym, 0);
+ assemble_tokens ("lda", newtok, 2, 1);
+
+ /* Call the division routine. */
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_cpreg (newtok[1], AXP_REG_AT);
+ set_tok_const (newtok[2], 0);
+ assemble_tokens ("jsr", newtok, 3, 1);
+
+ /* Move the result to the right place. */
+ if (rr != AXP_REG_R0)
+ {
+ set_tok_reg (newtok[0], AXP_REG_R0);
+ set_tok_reg (newtok[1], rr);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+}
+
+#else /* !OBJ_EVAX */
+
+static void
+emit_division (const expressionS *tok,
+ int ntok,
+ const void * symname)
+{
+ /* DIVISION and MODULUS. Yech.
+ Convert
+ OP x,y,result
+ to
+ lda pv,__OP
+ mov x,t10
+ mov y,t11
+ jsr t9,(pv),__OP
+ mov t12,result
+
+ with appropriate optimizations if t10,t11,t12 are the registers
+ specified by the compiler. */
+
+ int xr, yr, rr;
+ symbolS *sym;
+ expressionS newtok[3];
+
+ xr = regno (tok[0].X_add_number);
+ yr = regno (tok[1].X_add_number);
+
+ if (ntok < 3)
+ rr = xr;
+ else
+ rr = regno (tok[2].X_add_number);
+
+ sym = symbol_find_or_make ((const char *) symname);
+
+ /* Move the operands into the right place. */
+ if (yr == AXP_REG_T10 && xr == AXP_REG_T11)
+ {
+ /* They are in exactly the wrong order -- swap through AT. */
+ if (alpha_noat_on)
+ as_bad (_("macro requires $at register while noat in effect"));
+
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_AT);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_T11);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("mov", newtok, 2, 1);
+
+ set_tok_reg (newtok[0], AXP_REG_AT);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ else
+ {
+ if (yr == AXP_REG_T10)
+ {
+ set_tok_reg (newtok[0], AXP_REG_T10);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (xr != AXP_REG_T10)
+ {
+ set_tok_reg (newtok[0], xr);
+ set_tok_reg (newtok[1], AXP_REG_T10);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+
+ if (yr != AXP_REG_T10 && yr != AXP_REG_T11)
+ {
+ set_tok_reg (newtok[0], yr);
+ set_tok_reg (newtok[1], AXP_REG_T11);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+ }
+
+ /* Call the division routine. */
+ set_tok_reg (newtok[0], AXP_REG_T9);
+ set_tok_sym (newtok[1], sym, 0);
+ assemble_tokens ("jsr", newtok, 2, 1);
+
+ /* Reload the GP register. */
+#ifdef OBJ_AOUT
+FIXME
+#endif
+#if defined(OBJ_ECOFF) || defined(OBJ_ELF)
+ set_tok_reg (newtok[0], alpha_gp_register);
+ set_tok_const (newtok[1], 0);
+ set_tok_preg (newtok[2], AXP_REG_T9);
+ assemble_tokens ("ldgp", newtok, 3, 1);
+#endif
+
+ /* Move the result to the right place. */
+ if (rr != AXP_REG_T12)
+ {
+ set_tok_reg (newtok[0], AXP_REG_T12);
+ set_tok_reg (newtok[1], rr);
+ assemble_tokens ("mov", newtok, 2, 1);
+ }
+}
+
+#endif /* !OBJ_EVAX */
+
+/* The jsr and jmp macros differ from their instruction counterparts
+ in that they can load the target address and default most
+ everything. */
+
+static void
+emit_jsrjmp (const expressionS *tok,
+ int ntok,
+ const void * vopname)
+{
+ const char *opname = (const char *) vopname;
+ struct alpha_insn insn;
+ expressionS newtok[3];
+ int r, tokidx = 0;
+ long lituse = 0;
+
+ if (tokidx < ntok && tok[tokidx].X_op == O_register)
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = strcmp (opname, "jmp") == 0 ? AXP_REG_ZERO : AXP_REG_RA;
+
+ set_tok_reg (newtok[0], r);
+
+ if (tokidx < ntok &&
+ (tok[tokidx].X_op == O_pregister || tok[tokidx].X_op == O_cpregister))
+ r = regno (tok[tokidx++].X_add_number);
+#ifdef OBJ_EVAX
+ /* Keep register if jsr $n.<sym>. */
+#else
+ else
+ {
+ int basereg = alpha_gp_register;
+ lituse = load_expression (r = AXP_REG_PV, &tok[tokidx],
+ &basereg, NULL, opname);
+ }
+#endif
+
+ set_tok_cpreg (newtok[1], r);
+
+#ifndef OBJ_EVAX
+ if (tokidx < ntok)
+ newtok[2] = tok[tokidx];
+ else
+#endif
+ set_tok_const (newtok[2], 0);
+
+ assemble_tokens_to_insn (opname, newtok, 3, &insn);
+
+ if (lituse)
+ {
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ insn.fixups[insn.nfixups].reloc = DUMMY_RELOC_LITUSE_JSR;
+ insn.fixups[insn.nfixups].exp.X_op = O_absent;
+ insn.nfixups++;
+ insn.sequence = lituse;
+ }
+
+#ifdef OBJ_EVAX
+ if (alpha_flag_replace
+ && r == AXP_REG_RA
+ && tok[tokidx].X_add_symbol
+ && alpha_linkage_symbol)
+ {
+ /* Create a BOH reloc for 'jsr $27,NAME'. */
+ const char *symname = S_GET_NAME (tok[tokidx].X_add_symbol);
+ int symlen = strlen (symname);
+ char *ensymname;
+
+ /* Build the entry name as 'NAME..en'. */
+ ensymname = (char *) alloca (symlen + 5);
+ memcpy (ensymname, symname, symlen);
+ memcpy (ensymname + symlen, "..en", 5);
+
+ gas_assert (insn.nfixups < MAX_INSN_FIXUPS);
+ if (insn.nfixups > 0)
+ {
+ memmove (&insn.fixups[1], &insn.fixups[0],
+ sizeof(struct alpha_fixup) * insn.nfixups);
+ }
+
+ /* The fixup must be the same as the BFD_RELOC_ALPHA_NOP
+ case in load_expression. See B.4.5.2 of the OpenVMS
+ Linker Utility Manual. */
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_BOH;
+ insn.fixups[0].exp.X_op = O_symbol;
+ insn.fixups[0].exp.X_add_symbol = symbol_find_or_make (ensymname);
+ insn.fixups[0].exp.X_add_number = 0;
+ insn.fixups[0].xtrasym = alpha_linkage_symbol;
+ insn.fixups[0].procsym = alpha_evax_proc->symbol;
+ insn.nfixups++;
+ alpha_linkage_symbol = 0;
+ }
+#endif
+
+ emit_insn (&insn);
+}
+
+/* The ret and jcr instructions differ from their instruction
+ counterparts in that everything can be defaulted. */
+
+static void
+emit_retjcr (const expressionS *tok,
+ int ntok,
+ const void * vopname)
+{
+ const char *opname = (const char *) vopname;
+ expressionS newtok[3];
+ int r, tokidx = 0;
+
+ if (tokidx < ntok && tok[tokidx].X_op == O_register)
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = AXP_REG_ZERO;
+
+ set_tok_reg (newtok[0], r);
+
+ if (tokidx < ntok &&
+ (tok[tokidx].X_op == O_pregister || tok[tokidx].X_op == O_cpregister))
+ r = regno (tok[tokidx++].X_add_number);
+ else
+ r = AXP_REG_RA;
+
+ set_tok_cpreg (newtok[1], r);
+
+ if (tokidx < ntok)
+ newtok[2] = tok[tokidx];
+ else
+ set_tok_const (newtok[2], strcmp (opname, "ret") == 0);
+
+ assemble_tokens (opname, newtok, 3, 0);
+}
+
+/* Implement the ldgp macro. */
+
+static void
+emit_ldgp (const expressionS *tok ATTRIBUTE_UNUSED,
+ int ntok ATTRIBUTE_UNUSED,
+ const void * unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_AOUT
+FIXME
+#endif
+#if defined(OBJ_ECOFF) || defined(OBJ_ELF)
+ /* from "ldgp r1,n(r2)", generate "ldah r1,X(R2); lda r1,Y(r1)"
+ with appropriate constants and relocations. */
+ struct alpha_insn insn;
+ expressionS newtok[3];
+ expressionS addend;
+
+#ifdef OBJ_ECOFF
+ if (regno (tok[2].X_add_number) == AXP_REG_PV)
+ ecoff_set_gp_prolog_size (0);
+#endif
+
+ newtok[0] = tok[0];
+ set_tok_const (newtok[1], 0);
+ newtok[2] = tok[2];
+
+ assemble_tokens_to_insn ("ldah", newtok, 3, &insn);
+
+ addend = tok[1];
+
+#ifdef OBJ_ECOFF
+ if (addend.X_op != O_constant)
+ as_bad (_("can not resolve expression"));
+ addend.X_op = O_symbol;
+ addend.X_add_symbol = alpha_gp_symbol;
+#endif
+
+ insn.nfixups = 1;
+ insn.fixups[0].exp = addend;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_GPDISP_HI16;
+ insn.sequence = next_sequence_num;
+
+ emit_insn (&insn);
+
+ set_tok_preg (newtok[2], tok[0].X_add_number);
+
+ assemble_tokens_to_insn ("lda", newtok, 3, &insn);
+
+#ifdef OBJ_ECOFF
+ addend.X_add_number += 4;
+#endif
+
+ insn.nfixups = 1;
+ insn.fixups[0].exp = addend;
+ insn.fixups[0].reloc = BFD_RELOC_ALPHA_GPDISP_LO16;
+ insn.sequence = next_sequence_num--;
+
+ emit_insn (&insn);
+#endif /* OBJ_ECOFF || OBJ_ELF */
+}
+
+/* The macro table. */
+
+static const struct alpha_macro alpha_macros[] =
+{
+/* Load/Store macros. */
+ { "lda", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldah", emit_ldah, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "ldl", emit_ir_load, "ldl",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldl_l", emit_ir_load, "ldl_l",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldq", emit_ir_load, "ldq",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldq_l", emit_ir_load, "ldq_l",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldq_u", emit_ir_load, "ldq_u",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldf", emit_loadstore, "ldf",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldg", emit_loadstore, "ldg",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "lds", emit_loadstore, "lds",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldt", emit_loadstore, "ldt",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "ldb", emit_ldX, (void *) 0,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldbu", emit_ldXu, (void *) 0,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldw", emit_ldX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ldwu", emit_ldXu, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "uldw", emit_uldX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldwu", emit_uldXu, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldl", emit_uldX, (void *) 2,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldlu", emit_uldXu, (void *) 2,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "uldq", emit_uldXu, (void *) 3,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "ldgp", emit_ldgp, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_PIR, MACRO_EOA } },
+
+ { "ldi", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldil", emit_ldil, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+ { "ldiq", emit_lda, NULL,
+ { MACRO_IR, MACRO_EXP, MACRO_EOA } },
+
+ { "stl", emit_loadstore, "stl",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stl_c", emit_loadstore, "stl_c",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stq", emit_loadstore, "stq",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stq_c", emit_loadstore, "stq_c",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stq_u", emit_loadstore, "stq_u",
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stf", emit_loadstore, "stf",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stg", emit_loadstore, "stg",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "sts", emit_loadstore, "sts",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stt", emit_loadstore, "stt",
+ { MACRO_FPR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+ { "stb", emit_stX, (void *) 0,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "stw", emit_stX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ustw", emit_ustX, (void *) 1,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ustl", emit_ustX, (void *) 2,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+ { "ustq", emit_ustX, (void *) 3,
+ { MACRO_IR, MACRO_EXP, MACRO_OPIR, MACRO_EOA } },
+
+/* Arithmetic macros. */
+
+ { "sextb", emit_sextX, (void *) 0,
+ { MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ /* MACRO_EXP, MACRO_IR, MACRO_EOA */ } },
+ { "sextw", emit_sextX, (void *) 1,
+ { MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ /* MACRO_EXP, MACRO_IR, MACRO_EOA */ } },
+
+ { "divl", emit_division, "__divl",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divlu", emit_division, "__divlu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divq", emit_division, "__divq",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "divqu", emit_division, "__divqu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "reml", emit_division, "__reml",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remlu", emit_division, "__remlu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remq", emit_division, "__remq",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+ { "remqu", emit_division, "__remqu",
+ { MACRO_IR, MACRO_IR, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_IR, MACRO_EOA,
+ /* MACRO_IR, MACRO_EXP, MACRO_IR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA */ } },
+
+ { "jsr", emit_jsrjmp, "jsr",
+ { MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA } },
+ { "jmp", emit_jsrjmp, "jmp",
+ { MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA } },
+ { "ret", emit_retjcr, "ret",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+ { "jcr", emit_retjcr, "jcr",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+ { "jsr_coroutine", emit_retjcr, "jcr",
+ { MACRO_IR, MACRO_EXP, MACRO_EOA,
+ MACRO_IR, MACRO_EOA,
+ MACRO_PIR, MACRO_EXP, MACRO_EOA,
+ MACRO_PIR, MACRO_EOA,
+ MACRO_EXP, MACRO_EOA,
+ MACRO_EOA } },
+};
+
+static const unsigned int alpha_num_macros
+ = sizeof (alpha_macros) / sizeof (*alpha_macros);
+
+/* Search forward through all variants of a macro looking for a syntax
+ match. */
+
+static const struct alpha_macro *
+find_macro_match (const struct alpha_macro *first_macro,
+ const expressionS *tok,
+ int *pntok)
+
+{
+ const struct alpha_macro *macro = first_macro;
+ int ntok = *pntok;
+
+ do
+ {
+ const enum alpha_macro_arg *arg = macro->argsets;
+ int tokidx = 0;
+
+ while (*arg)
+ {
+ switch (*arg)
+ {
+ case MACRO_EOA:
+ if (tokidx == ntok)
+ return macro;
+ else
+ tokidx = 0;
+ break;
+
+ /* Index register. */
+ case MACRO_IR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_register
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Parenthesized index register. */
+ case MACRO_PIR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_pregister
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Optional parenthesized index register. */
+ case MACRO_OPIR:
+ if (tokidx < ntok && tok[tokidx].X_op == O_pregister
+ && is_ir_num (tok[tokidx].X_add_number))
+ ++tokidx;
+ break;
+
+ /* Leading comma with a parenthesized index register. */
+ case MACRO_CPIR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_cpregister
+ || !is_ir_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Floating point register. */
+ case MACRO_FPR:
+ if (tokidx >= ntok || tok[tokidx].X_op != O_register
+ || !is_fpr_num (tok[tokidx].X_add_number))
+ goto match_failed;
+ ++tokidx;
+ break;
+
+ /* Normal expression. */
+ case MACRO_EXP:
+ if (tokidx >= ntok)
+ goto match_failed;
+ switch (tok[tokidx].X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ case O_pregister:
+ case O_cpregister:
+ case O_literal:
+ case O_lituse_base:
+ case O_lituse_bytoff:
+ case O_lituse_jsr:
+ case O_gpdisp:
+ case O_gprelhigh:
+ case O_gprellow:
+ case O_gprel:
+ case O_samegp:
+ goto match_failed;
+
+ default:
+ break;
+ }
+ ++tokidx;
+ break;
+
+ match_failed:
+ while (*arg != MACRO_EOA)
+ ++arg;
+ tokidx = 0;
+ break;
+ }
+ ++arg;
+ }
+ }
+ while (++macro - alpha_macros < (int) alpha_num_macros
+ && !strcmp (macro->name, first_macro->name));
+
+ return NULL;
+}
+
+/* Given an opcode name and a pre-tokenized set of arguments, take the
+ opcode all the way through emission. */
+
+static void
+assemble_tokens (const char *opname,
+ const expressionS *tok,
+ int ntok,
+ int local_macros_on)
+{
+ int found_something = 0;
+ const struct alpha_opcode *opcode;
+ const struct alpha_macro *macro;
+ int cpumatch = 1;
+ extended_bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
+
+#ifdef RELOC_OP_P
+ /* If a user-specified relocation is present, this is not a macro. */
+ if (ntok && USER_RELOC_P (tok[ntok - 1].X_op))
+ {
+ reloc = ALPHA_RELOC_TABLE (tok[ntok - 1].X_op)->reloc;
+ ntok--;
+ }
+ else
+#endif
+ if (local_macros_on)
+ {
+ macro = ((const struct alpha_macro *)
+ hash_find (alpha_macro_hash, opname));
+ if (macro)
+ {
+ found_something = 1;
+ macro = find_macro_match (macro, tok, &ntok);
+ if (macro)
+ {
+ (*macro->emit) (tok, ntok, macro->arg);
+ return;
+ }
+ }
+ }
+
+ /* Search opcodes. */
+ opcode = (const struct alpha_opcode *) hash_find (alpha_opcode_hash, opname);
+ if (opcode)
+ {
+ found_something = 1;
+ opcode = find_opcode_match (opcode, tok, &ntok, &cpumatch);
+ if (opcode)
+ {
+ struct alpha_insn insn;
+ assemble_insn (opcode, tok, ntok, &insn, reloc);
+
+ /* Copy the sequence number for the reloc from the reloc token. */
+ if (reloc != BFD_RELOC_UNUSED)
+ insn.sequence = tok[ntok].X_add_number;
+
+ emit_insn (&insn);
+ return;
+ }
+ }
+
+ if (found_something)
+ {
+ if (cpumatch)
+ as_bad (_("inappropriate arguments for opcode `%s'"), opname);
+ else
+ as_bad (_("opcode `%s' not supported for target %s"), opname,
+ alpha_target_name);
+ }
+ else
+ as_bad (_("unknown opcode `%s'"), opname);
+}
+
+#ifdef OBJ_EVAX
+
+/* Add sym+addend to link pool.
+ Return offset from curent procedure value (pv) to entry in link pool.
+
+ Add new fixup only if offset isn't 16bit. */
+
+static symbolS *
+add_to_link_pool (symbolS *sym, offsetT addend)
+{
+ symbolS *basesym;
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ char *p;
+ segment_info_type *seginfo = seg_info (alpha_link_section);
+ fixS *fixp;
+ symbolS *linksym, *expsym;
+ expressionS e;
+
+ basesym = alpha_evax_proc->symbol;
+
+ /* @@ This assumes all entries in a given section will be of the same
+ size... Probably correct, but unwise to rely on. */
+ /* This must always be called with the same subsegment. */
+
+ if (seginfo->frchainP)
+ for (fixp = seginfo->frchainP->fix_root;
+ fixp != (fixS *) NULL;
+ fixp = fixp->fx_next)
+ {
+ if (fixp->fx_addsy == sym
+ && fixp->fx_offset == (valueT)addend
+ && fixp->tc_fix_data.info
+ && fixp->tc_fix_data.info->sym
+ && fixp->tc_fix_data.info->sym->sy_value.X_op_symbol == basesym)
+ return fixp->tc_fix_data.info->sym;
+ }
+
+ /* Not found, add a new entry. */
+ subseg_set (alpha_link_section, 0);
+ linksym = symbol_new
+ (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (), frag_now);
+ p = frag_more (8);
+ memset (p, 0, 8);
+
+ /* Create a symbol for 'basesym - linksym' (offset of the added entry). */
+ e.X_op = O_subtract;
+ e.X_add_symbol = linksym;
+ e.X_op_symbol = basesym;
+ e.X_add_number = 0;
+ expsym = make_expr_symbol (&e);
+
+ /* Create a fixup for the entry. */
+ fixp = fix_new
+ (frag_now, p - frag_now->fr_literal, 8, sym, addend, 0, BFD_RELOC_64);
+ fixp->tc_fix_data.info = get_alpha_reloc_tag (next_sequence_num--);
+ fixp->tc_fix_data.info->sym = expsym;
+
+ subseg_set (current_section, current_subsec);
+
+ /* Return the symbol. */
+ return expsym;
+}
+#endif /* OBJ_EVAX */
+
+/* Assembler directives. */
+
+/* Handle the .text pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_text (int i)
+{
+#ifdef OBJ_ELF
+ obj_elf_text (i);
+#else
+ s_text (i);
+#endif
+#ifdef OBJ_EVAX
+ {
+ symbolS * symbolP;
+
+ symbolP = symbol_find (".text");
+ if (symbolP == NULL)
+ {
+ symbolP = symbol_make (".text");
+ S_SET_SEGMENT (symbolP, text_section);
+ symbol_table_insert (symbolP);
+ }
+ }
+#endif
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+/* Handle the .data pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_data (int i)
+{
+#ifdef OBJ_ELF
+ obj_elf_data (i);
+#else
+ s_data (i);
+#endif
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+#if defined (OBJ_ECOFF) || defined (OBJ_EVAX)
+
+/* Handle the OSF/1 and openVMS .comm pseudo quirks. */
+
+static void
+s_alpha_comm (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+#ifdef OBJ_EVAX
+ offsetT temp;
+ int log_align = 0;
+#endif
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+
+ SKIP_WHITESPACE ();
+
+ /* Alpha OSF/1 compiler doesn't provide the comma, gcc does. */
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+#ifdef OBJ_EVAX
+ if (*input_line_pointer != ',')
+ temp = 8; /* Default alignment. */
+ else
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ temp = get_absolute_expression ();
+ }
+
+ /* ??? Unlike on OSF/1, the alignment factor is not in log units. */
+ while ((temp >>= 1) != 0)
+ ++log_align;
+
+ if (*input_line_pointer == ',')
+ {
+ /* Extended form of the directive
+
+ .comm symbol, size, alignment, section
+
+ where the "common" semantics is transferred to the section.
+ The symbol is effectively an alias for the section name. */
+
+ segT sec;
+ char *sec_name;
+ symbolS *sec_symbol;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ int cur_size;
+
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ sec_name = s_alpha_section_name ();
+ sec_symbol = symbol_find_or_make (sec_name);
+ sec = subseg_new (sec_name, 0);
+ S_SET_SEGMENT (sec_symbol, sec);
+ symbol_get_bfdsym (sec_symbol)->flags |= BSF_SECTION_SYM;
+ bfd_vms_set_section_flags (stdoutput, sec, 0,
+ EGPS__V_OVR | EGPS__V_GBL | EGPS__V_NOMOD);
+ record_alignment (sec, log_align);
+
+ /* Reuse stab_string_size to store the size of the section. */
+ cur_size = seg_info (sec)->stabu.stab_string_size;
+ if ((int) size > cur_size)
+ {
+ char *pfrag
+ = frag_var (rs_fill, 1, 1, (relax_substateT)0, NULL,
+ (valueT)size - (valueT)cur_size, NULL);
+ *pfrag = 0;
+ seg_info (sec)->stabu.stab_string_size = (int)size;
+ }
+
+ S_SET_SEGMENT (symbolP, sec);
+
+ subseg_set (current_seg, current_subseg);
+ }
+ else
+ {
+ /* Regular form of the directive
+
+ .comm symbol, size, alignment
+
+ where the "common" semantics in on the symbol.
+ These symbols are assembled in the .bss section. */
+
+ char *pfrag;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ subseg_set (bss_section, 1);
+ frag_align (log_align, 0, 0);
+ record_alignment (bss_section, log_align);
+
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, symbolP,
+ size, NULL);
+ *pfrag = 0;
+
+ S_SET_SEGMENT (symbolP, bss_section);
+
+ subseg_set (current_seg, current_subseg);
+ }
+#endif
+
+ if (S_GET_VALUE (symbolP))
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) size)
+ as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+ }
+ else
+ {
+#ifndef OBJ_EVAX
+ S_SET_VALUE (symbolP, (valueT) size);
+#endif
+ S_SET_EXTERNAL (symbolP);
+ }
+
+#ifndef OBJ_EVAX
+ know (symbolP->sy_frag == &zero_address_frag);
+#endif
+ demand_empty_rest_of_line ();
+}
+
+#endif /* ! OBJ_ELF */
+
+#ifdef OBJ_ECOFF
+
+/* Handle the .rdata pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_rdata (int ignore ATTRIBUTE_UNUSED)
+{
+ get_absolute_expression ();
+ subseg_new (".rdata", 0);
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+#endif
+
+#ifdef OBJ_ECOFF
+
+/* Handle the .sdata pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_sdata (int ignore ATTRIBUTE_UNUSED)
+{
+ get_absolute_expression ();
+ subseg_new (".sdata", 0);
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+#endif
+
+#ifdef OBJ_ELF
+struct alpha_elf_frame_data
+{
+ symbolS *func_sym;
+ symbolS *func_end_sym;
+ symbolS *prologue_sym;
+ unsigned int mask;
+ unsigned int fmask;
+ int fp_regno;
+ int ra_regno;
+ offsetT frame_size;
+ offsetT mask_offset;
+ offsetT fmask_offset;
+
+ struct alpha_elf_frame_data *next;
+};
+
+static struct alpha_elf_frame_data *all_frame_data;
+static struct alpha_elf_frame_data **plast_frame_data = &all_frame_data;
+static struct alpha_elf_frame_data *cur_frame_data;
+
+/* Handle the .section pseudo-op. This is like the usual one, but it
+ clears alpha_insn_label and restores auto alignment. */
+
+static void
+s_alpha_section (int ignore ATTRIBUTE_UNUSED)
+{
+ obj_elf_section (ignore);
+
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+static void
+s_alpha_ent (int dummy ATTRIBUTE_UNUSED)
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_ent (0);
+ else
+ {
+ char *name, name_end;
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (! is_name_beginner (*name))
+ {
+ as_warn (_(".ent directive has no name"));
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ symbolS *sym;
+
+ if (cur_frame_data)
+ as_warn (_("nested .ent directives"));
+
+ sym = symbol_find_or_make (name);
+ symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
+
+ cur_frame_data = (struct alpha_elf_frame_data *)
+ calloc (1, sizeof (*cur_frame_data));
+ cur_frame_data->func_sym = sym;
+
+ /* Provide sensible defaults. */
+ cur_frame_data->fp_regno = 30; /* sp */
+ cur_frame_data->ra_regno = 26; /* ra */
+
+ *plast_frame_data = cur_frame_data;
+ plast_frame_data = &cur_frame_data->next;
+
+ /* The .ent directive is sometimes followed by a number. Not sure
+ what it really means, but ignore it. */
+ *input_line_pointer = name_end;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-')
+ (void) get_absolute_expression ();
+ }
+ demand_empty_rest_of_line ();
+ }
+}
+
+static void
+s_alpha_end (int dummy ATTRIBUTE_UNUSED)
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_end (0);
+ else
+ {
+ char *name, name_end;
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (! is_name_beginner (*name))
+ {
+ as_warn (_(".end directive has no name"));
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ symbolS *sym;
+
+ sym = symbol_find (name);
+ if (!cur_frame_data)
+ as_warn (_(".end directive without matching .ent"));
+ else if (sym != cur_frame_data->func_sym)
+ as_warn (_(".end directive names different symbol than .ent"));
+
+ /* Create an expression to calculate the size of the function. */
+ if (sym && cur_frame_data)
+ {
+ OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (sym);
+ expressionS *exp = (expressionS *) xmalloc (sizeof (expressionS));
+
+ obj->size = exp;
+ exp->X_op = O_subtract;
+ exp->X_add_symbol = symbol_temp_new_now ();
+ exp->X_op_symbol = sym;
+ exp->X_add_number = 0;
+
+ cur_frame_data->func_end_sym = exp->X_add_symbol;
+ }
+
+ cur_frame_data = NULL;
+
+ *input_line_pointer = name_end;
+ }
+ demand_empty_rest_of_line ();
+ }
+}
+
+static void
+s_alpha_mask (int fp)
+{
+ if (ECOFF_DEBUGGING)
+ {
+ if (fp)
+ ecoff_directive_fmask (0);
+ else
+ ecoff_directive_mask (0);
+ }
+ else
+ {
+ long val;
+ offsetT offset;
+
+ if (!cur_frame_data)
+ {
+ if (fp)
+ as_warn (_(".fmask outside of .ent"));
+ else
+ as_warn (_(".mask outside of .ent"));
+ discard_rest_of_line ();
+ return;
+ }
+
+ if (get_absolute_expression_and_terminator (&val) != ',')
+ {
+ if (fp)
+ as_warn (_("bad .fmask directive"));
+ else
+ as_warn (_("bad .mask directive"));
+ --input_line_pointer;
+ discard_rest_of_line ();
+ return;
+ }
+
+ offset = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+
+ if (fp)
+ {
+ cur_frame_data->fmask = val;
+ cur_frame_data->fmask_offset = offset;
+ }
+ else
+ {
+ cur_frame_data->mask = val;
+ cur_frame_data->mask_offset = offset;
+ }
+ }
+}
+
+static void
+s_alpha_frame (int dummy ATTRIBUTE_UNUSED)
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_frame (0);
+ else
+ {
+ long val;
+
+ if (!cur_frame_data)
+ {
+ as_warn (_(".frame outside of .ent"));
+ discard_rest_of_line ();
+ return;
+ }
+
+ cur_frame_data->fp_regno = tc_get_register (1);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ','
+ || get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("bad .frame directive"));
+ --input_line_pointer;
+ discard_rest_of_line ();
+ return;
+ }
+ cur_frame_data->frame_size = val;
+
+ cur_frame_data->ra_regno = tc_get_register (0);
+
+ /* Next comes the "offset of saved $a0 from $sp". In gcc terms
+ this is current_function_pretend_args_size. There's no place
+ to put this value, so ignore it. */
+ s_ignore (42);
+ }
+}
+
+static void
+s_alpha_prologue (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+ int arg;
+
+ arg = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+ alpha_prologue_label = symbol_new
+ (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (), frag_now);
+
+ if (ECOFF_DEBUGGING)
+ sym = ecoff_get_cur_proc_sym ();
+ else
+ sym = cur_frame_data ? cur_frame_data->func_sym : NULL;
+
+ if (sym == NULL)
+ {
+ as_bad (_(".prologue directive without a preceding .ent directive"));
+ return;
+ }
+
+ switch (arg)
+ {
+ case 0: /* No PV required. */
+ S_SET_OTHER (sym, STO_ALPHA_NOPV
+ | (S_GET_OTHER (sym) & ~STO_ALPHA_STD_GPLOAD));
+ break;
+ case 1: /* Std GP load. */
+ S_SET_OTHER (sym, STO_ALPHA_STD_GPLOAD
+ | (S_GET_OTHER (sym) & ~STO_ALPHA_STD_GPLOAD));
+ break;
+ case 2: /* Non-std use of PV. */
+ break;
+
+ default:
+ as_bad (_("Invalid argument %d to .prologue."), arg);
+ break;
+ }
+
+ if (cur_frame_data)
+ cur_frame_data->prologue_sym = symbol_temp_new_now ();
+}
+
+static char *first_file_directive;
+
+static void
+s_alpha_file (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Save the first .file directive we see, so that we can change our
+ minds about whether ecoff debugging should or shouldn't be enabled. */
+ if (alpha_flag_mdebug < 0 && ! first_file_directive)
+ {
+ char *start = input_line_pointer;
+ size_t len;
+
+ discard_rest_of_line ();
+
+ len = input_line_pointer - start;
+ first_file_directive = (char *) xmalloc (len + 1);
+ memcpy (first_file_directive, start, len);
+ first_file_directive[len] = '\0';
+
+ input_line_pointer = start;
+ }
+
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_file (0);
+ else
+ dwarf2_directive_file (0);
+}
+
+static void
+s_alpha_loc (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ECOFF_DEBUGGING)
+ ecoff_directive_loc (0);
+ else
+ dwarf2_directive_loc (0);
+}
+
+static void
+s_alpha_stab (int n)
+{
+ /* If we've been undecided about mdebug, make up our minds in favour. */
+ if (alpha_flag_mdebug < 0)
+ {
+ segT sec = subseg_new (".mdebug", 0);
+ bfd_set_section_flags (stdoutput, sec, SEC_HAS_CONTENTS | SEC_READONLY);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+
+ ecoff_read_begin_hook ();
+
+ if (first_file_directive)
+ {
+ char *save_ilp = input_line_pointer;
+ input_line_pointer = first_file_directive;
+ ecoff_directive_file (0);
+ input_line_pointer = save_ilp;
+ free (first_file_directive);
+ }
+
+ alpha_flag_mdebug = 1;
+ }
+ s_stab (n);
+}
+
+static void
+s_alpha_coff_wrapper (int which)
+{
+ static void (* const fns[]) (int) = {
+ ecoff_directive_begin,
+ ecoff_directive_bend,
+ ecoff_directive_def,
+ ecoff_directive_dim,
+ ecoff_directive_endef,
+ ecoff_directive_scl,
+ ecoff_directive_tag,
+ ecoff_directive_val,
+ };
+
+ gas_assert (which >= 0 && which < (int) (sizeof (fns)/sizeof (*fns)));
+
+ if (ECOFF_DEBUGGING)
+ (*fns[which]) (0);
+ else
+ {
+ as_bad (_("ECOFF debugging is disabled."));
+ ignore_rest_of_line ();
+ }
+}
+
+/* Called at the end of assembly. Here we emit unwind info for frames
+ unless the compiler has done it for us. */
+
+void
+alpha_elf_md_end (void)
+{
+ struct alpha_elf_frame_data *p;
+
+ if (cur_frame_data)
+ as_warn (_(".ent directive without matching .end"));
+
+ /* If someone has generated the unwind info themselves, great. */
+ if (bfd_get_section_by_name (stdoutput, ".eh_frame") != NULL)
+ return;
+
+ /* ??? In theory we could look for functions for which we have
+ generated unwind info via CFI directives, and those we have not.
+ Those we have not could still get their unwind info from here.
+ For now, do nothing if we've seen any CFI directives. Note that
+ the above test will not trigger, as we've not emitted data yet. */
+ if (all_fde_data != NULL)
+ return;
+
+ /* Generate .eh_frame data for the unwind directives specified. */
+ for (p = all_frame_data; p ; p = p->next)
+ if (p->prologue_sym)
+ {
+ /* Create a temporary symbol at the same location as our
+ function symbol. This prevents problems with globals. */
+ cfi_new_fde (symbol_temp_new (S_GET_SEGMENT (p->func_sym),
+ S_GET_VALUE (p->func_sym),
+ symbol_get_frag (p->func_sym)));
+
+ cfi_set_return_column (p->ra_regno);
+ cfi_add_CFA_def_cfa_register (30);
+ if (p->fp_regno != 30 || p->mask || p->fmask || p->frame_size)
+ {
+ unsigned int mask;
+ offsetT offset;
+
+ cfi_add_advance_loc (p->prologue_sym);
+
+ if (p->fp_regno != 30)
+ if (p->frame_size != 0)
+ cfi_add_CFA_def_cfa (p->fp_regno, p->frame_size);
+ else
+ cfi_add_CFA_def_cfa_register (p->fp_regno);
+ else if (p->frame_size != 0)
+ cfi_add_CFA_def_cfa_offset (p->frame_size);
+
+ mask = p->mask;
+ offset = p->mask_offset;
+
+ /* Recall that $26 is special-cased and stored first. */
+ if ((mask >> 26) & 1)
+ {
+ cfi_add_CFA_offset (26, offset);
+ offset += 8;
+ mask &= ~(1 << 26);
+ }
+ while (mask)
+ {
+ unsigned int i;
+ i = mask & -mask;
+ mask ^= i;
+ i = ffs (i) - 1;
+
+ cfi_add_CFA_offset (i, offset);
+ offset += 8;
+ }
+
+ mask = p->fmask;
+ offset = p->fmask_offset;
+ while (mask)
+ {
+ unsigned int i;
+ i = mask & -mask;
+ mask ^= i;
+ i = ffs (i) - 1;
+
+ cfi_add_CFA_offset (i + 32, offset);
+ offset += 8;
+ }
+ }
+
+ cfi_end_fde (p->func_end_sym);
+ }
+}
+
+static void
+s_alpha_usepv (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, name_end;
+ char *which, which_end;
+ symbolS *sym;
+ int other;
+
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (! is_name_beginner (*name))
+ {
+ as_bad (_(".usepv directive has no name"));
+ *input_line_pointer = name_end;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ sym = symbol_find_or_make (name);
+ *input_line_pointer++ = name_end;
+
+ if (name_end != ',')
+ {
+ as_bad (_(".usepv directive has no type"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ which = input_line_pointer;
+ which_end = get_symbol_end ();
+
+ if (strcmp (which, "no") == 0)
+ other = STO_ALPHA_NOPV;
+ else if (strcmp (which, "std") == 0)
+ other = STO_ALPHA_STD_GPLOAD;
+ else
+ {
+ as_bad (_("unknown argument for .usepv"));
+ other = 0;
+ }
+
+ *input_line_pointer = which_end;
+ demand_empty_rest_of_line ();
+
+ S_SET_OTHER (sym, other | (S_GET_OTHER (sym) & ~STO_ALPHA_STD_GPLOAD));
+}
+#endif /* OBJ_ELF */
+
+/* Standard calling conventions leaves the CFA at $30 on entry. */
+
+void
+alpha_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (30);
+}
+
+#ifdef OBJ_EVAX
+
+/* Get name of section. */
+static char *
+s_alpha_section_name (void)
+{
+ char *name;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ {
+ int dummy;
+
+ name = demand_copy_C_string (&dummy);
+ if (name == NULL)
+ {
+ ignore_rest_of_line ();
+ return NULL;
+ }
+ }
+ else
+ {
+ char *end = input_line_pointer;
+
+ while (0 == strchr ("\n\t,; ", *end))
+ end++;
+ if (end == input_line_pointer)
+ {
+ as_warn (_("missing name"));
+ ignore_rest_of_line ();
+ return NULL;
+ }
+
+ name = xmalloc (end - input_line_pointer + 1);
+ memcpy (name, input_line_pointer, end - input_line_pointer);
+ name[end - input_line_pointer] = '\0';
+ input_line_pointer = end;
+ }
+ SKIP_WHITESPACE ();
+ return name;
+}
+
+/* Put clear/set flags in one flagword. The LSBs are flags to be set,
+ the MSBs are the flags to be cleared. */
+
+#define EGPS__V_NO_SHIFT 16
+#define EGPS__V_MASK 0xffff
+
+/* Parse one VMS section flag. */
+
+static flagword
+s_alpha_section_word (char *str, size_t len)
+{
+ int no = 0;
+ flagword flag = 0;
+
+ if (len == 5 && strncmp (str, "NO", 2) == 0)
+ {
+ no = 1;
+ str += 2;
+ len -= 2;
+ }
+
+ if (len == 3)
+ {
+ if (strncmp (str, "PIC", 3) == 0)
+ flag = EGPS__V_PIC;
+ else if (strncmp (str, "LIB", 3) == 0)
+ flag = EGPS__V_LIB;
+ else if (strncmp (str, "OVR", 3) == 0)
+ flag = EGPS__V_OVR;
+ else if (strncmp (str, "REL", 3) == 0)
+ flag = EGPS__V_REL;
+ else if (strncmp (str, "GBL", 3) == 0)
+ flag = EGPS__V_GBL;
+ else if (strncmp (str, "SHR", 3) == 0)
+ flag = EGPS__V_SHR;
+ else if (strncmp (str, "EXE", 3) == 0)
+ flag = EGPS__V_EXE;
+ else if (strncmp (str, "WRT", 3) == 0)
+ flag = EGPS__V_WRT;
+ else if (strncmp (str, "VEC", 3) == 0)
+ flag = EGPS__V_VEC;
+ else if (strncmp (str, "MOD", 3) == 0)
+ {
+ flag = no ? EGPS__V_NOMOD : EGPS__V_NOMOD << EGPS__V_NO_SHIFT;
+ no = 0;
+ }
+ else if (strncmp (str, "COM", 3) == 0)
+ flag = EGPS__V_COM;
+ }
+
+ if (flag == 0)
+ {
+ char c = str[len];
+ str[len] = 0;
+ as_warn (_("unknown section attribute %s"), str);
+ str[len] = c;
+ return 0;
+ }
+
+ if (no)
+ return flag << EGPS__V_NO_SHIFT;
+ else
+ return flag;
+}
+
+/* Handle the section specific pseudo-op. */
+
+#define EVAX_SECTION_COUNT 5
+
+static char *section_name[EVAX_SECTION_COUNT + 1] =
+ { "NULL", ".rdata", ".comm", ".link", ".ctors", ".dtors" };
+
+static void
+s_alpha_section (int secid)
+{
+ char *name, *beg;
+ segT sec;
+ flagword vms_flags = 0;
+ symbolS *symbol;
+
+ if (secid == 0)
+ {
+ name = s_alpha_section_name ();
+ if (name == NULL)
+ return;
+ sec = subseg_new (name, 0);
+ if (*input_line_pointer == ',')
+ {
+ /* Skip the comma. */
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ do
+ {
+ char c;
+
+ SKIP_WHITESPACE ();
+ beg = input_line_pointer;
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+
+ vms_flags |= s_alpha_section_word (beg, input_line_pointer - beg);
+
+ SKIP_WHITESPACE ();
+ }
+ while (*input_line_pointer++ == ',');
+ --input_line_pointer;
+ }
+
+ symbol = symbol_find_or_make (name);
+ S_SET_SEGMENT (symbol, sec);
+ symbol_get_bfdsym (symbol)->flags |= BSF_SECTION_SYM;
+ bfd_vms_set_section_flags
+ (stdoutput, sec,
+ (vms_flags >> EGPS__V_NO_SHIFT) & EGPS__V_MASK,
+ vms_flags & EGPS__V_MASK);
+ }
+ else
+ {
+ get_absolute_expression ();
+ subseg_new (section_name[secid], 0);
+ }
+
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+static void
+s_alpha_literals (int ignore ATTRIBUTE_UNUSED)
+{
+ subseg_new (".literals", 0);
+ demand_empty_rest_of_line ();
+ alpha_insn_label = NULL;
+ alpha_auto_align_on = 1;
+ alpha_current_align = 0;
+}
+
+/* Parse .ent directives. */
+
+static void
+s_alpha_ent (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbol;
+ expressionS symexpr;
+
+ if (alpha_evax_proc != NULL)
+ as_bad (_("previous .ent not closed by a .end"));
+
+ alpha_evax_proc = &alpha_evax_proc_data;
+
+ alpha_evax_proc->pdsckind = 0;
+ alpha_evax_proc->framereg = -1;
+ alpha_evax_proc->framesize = 0;
+ alpha_evax_proc->rsa_offset = 0;
+ alpha_evax_proc->ra_save = AXP_REG_RA;
+ alpha_evax_proc->fp_save = -1;
+ alpha_evax_proc->imask = 0;
+ alpha_evax_proc->fmask = 0;
+ alpha_evax_proc->prologue = 0;
+ alpha_evax_proc->type = 0;
+ alpha_evax_proc->handler = 0;
+ alpha_evax_proc->handler_data = 0;
+
+ expression (&symexpr);
+
+ if (symexpr.X_op != O_symbol)
+ {
+ as_fatal (_(".ent directive has no symbol"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ symbol = make_expr_symbol (&symexpr);
+ symbol_get_bfdsym (symbol)->flags |= BSF_FUNCTION;
+ alpha_evax_proc->symbol = symbol;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_handler (int is_data)
+{
+ if (is_data)
+ alpha_evax_proc->handler_data = get_absolute_expression ();
+ else
+ {
+ char *name, name_end;
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (! is_name_beginner (*name))
+ {
+ as_warn (_(".handler directive has no name"));
+ *input_line_pointer = name_end;
+ }
+ else
+ {
+ symbolS *sym;
+
+ sym = symbol_find_or_make (name);
+ symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
+ alpha_evax_proc->handler = sym;
+ *input_line_pointer = name_end;
+ }
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Parse .frame <framreg>,<framesize>,RA,<rsa_offset> directives. */
+
+static void
+s_alpha_frame (int ignore ATTRIBUTE_UNUSED)
+{
+ long val;
+ int ra;
+
+ alpha_evax_proc->framereg = tc_get_register (1);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ','
+ || get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .frame directive 1./2. param"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ alpha_evax_proc->framesize = val;
+
+ ra = tc_get_register (1);
+ if (ra != AXP_REG_RA)
+ as_warn (_("Bad RA (%d) register for .frame"), ra);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ',')
+ {
+ as_warn (_("Bad .frame directive 3./4. param"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ alpha_evax_proc->rsa_offset = get_absolute_expression ();
+}
+
+/* Parse .prologue. */
+
+static void
+s_alpha_prologue (int ignore ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ alpha_prologue_label = symbol_new
+ (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (), frag_now);
+}
+
+/* Parse .pdesc <entry_name>,{null|stack|reg}
+ Insert a procedure descriptor. */
+
+static void
+s_alpha_pdesc (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char name_end;
+ register char *p;
+ expressionS exp;
+ symbolS *entry_sym;
+ const char *entry_sym_name;
+ const char *pdesc_sym_name;
+ fixS *fixp;
+ size_t len;
+
+ if (now_seg != alpha_link_section)
+ {
+ as_bad (_(".pdesc directive not in link (.link) section"));
+ return;
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_bad (_(".pdesc directive has no entry symbol"));
+ return;
+ }
+
+ entry_sym = make_expr_symbol (&exp);
+ entry_sym_name = S_GET_NAME (entry_sym);
+
+ /* Strip "..en". */
+ len = strlen (entry_sym_name);
+ if (len < 4 || strcmp (entry_sym_name + len - 4, "..en") != 0)
+ {
+ as_bad (_(".pdesc has a bad entry symbol"));
+ return;
+ }
+ len -= 4;
+ pdesc_sym_name = S_GET_NAME (alpha_evax_proc->symbol);
+
+ if (!alpha_evax_proc
+ || !S_IS_DEFINED (alpha_evax_proc->symbol)
+ || strlen (pdesc_sym_name) != len
+ || memcmp (entry_sym_name, pdesc_sym_name, len) != 0)
+ {
+ as_fatal (_(".pdesc doesn't match with last .ent"));
+ return;
+ }
+
+ /* Define pdesc symbol. */
+ symbol_set_value_now (alpha_evax_proc->symbol);
+
+ /* Save bfd symbol of proc entry in function symbol. */
+ ((struct evax_private_udata_struct *)
+ symbol_get_bfdsym (alpha_evax_proc->symbol)->udata.p)->enbsym
+ = symbol_get_bfdsym (entry_sym);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ',')
+ {
+ as_warn (_("No comma after .pdesc <entryname>"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (strncmp (name, "stack", 5) == 0)
+ alpha_evax_proc->pdsckind = PDSC_S_K_KIND_FP_STACK;
+
+ else if (strncmp (name, "reg", 3) == 0)
+ alpha_evax_proc->pdsckind = PDSC_S_K_KIND_FP_REGISTER;
+
+ else if (strncmp (name, "null", 4) == 0)
+ alpha_evax_proc->pdsckind = PDSC_S_K_KIND_NULL;
+
+ else
+ {
+ as_fatal (_("unknown procedure kind"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = name_end;
+ demand_empty_rest_of_line ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ frag_align (3, 0, 0);
+ p = frag_more (16);
+ fixp = fix_new (frag_now, p - frag_now->fr_literal, 8, 0, 0, 0, 0);
+ fixp->fx_done = 1;
+
+ *p = alpha_evax_proc->pdsckind
+ | ((alpha_evax_proc->framereg == 29) ? PDSC_S_M_BASE_REG_IS_FP : 0)
+ | ((alpha_evax_proc->handler) ? PDSC_S_M_HANDLER_VALID : 0)
+ | ((alpha_evax_proc->handler_data) ? PDSC_S_M_HANDLER_DATA_VALID : 0);
+ *(p + 1) = PDSC_S_M_NATIVE | PDSC_S_M_NO_JACKET;
+
+ switch (alpha_evax_proc->pdsckind)
+ {
+ case PDSC_S_K_KIND_NULL:
+ *(p + 2) = 0;
+ *(p + 3) = 0;
+ break;
+ case PDSC_S_K_KIND_FP_REGISTER:
+ *(p + 2) = alpha_evax_proc->fp_save;
+ *(p + 3) = alpha_evax_proc->ra_save;
+ break;
+ case PDSC_S_K_KIND_FP_STACK:
+ md_number_to_chars (p + 2, (valueT) alpha_evax_proc->rsa_offset, 2);
+ break;
+ default: /* impossible */
+ break;
+ }
+
+ *(p + 4) = 0;
+ *(p + 5) = alpha_evax_proc->type & 0x0f;
+
+ /* Signature offset. */
+ md_number_to_chars (p + 6, (valueT) 0, 2);
+
+ fix_new_exp (frag_now, p - frag_now->fr_literal + 8,
+ 8, &exp, 0, BFD_RELOC_64);
+
+ if (alpha_evax_proc->pdsckind == PDSC_S_K_KIND_NULL)
+ return;
+
+ /* pdesc+16: Size. */
+ p = frag_more (6);
+ md_number_to_chars (p, (valueT) alpha_evax_proc->framesize, 4);
+ md_number_to_chars (p + 4, (valueT) 0, 2);
+
+ /* Entry length. */
+ exp.X_op = O_subtract;
+ exp.X_add_symbol = alpha_prologue_label;
+ exp.X_op_symbol = entry_sym;
+ emit_expr (&exp, 2);
+
+ if (alpha_evax_proc->pdsckind == PDSC_S_K_KIND_FP_REGISTER)
+ return;
+
+ /* pdesc+24: register masks. */
+ p = frag_more (8);
+ md_number_to_chars (p, alpha_evax_proc->imask, 4);
+ md_number_to_chars (p + 4, alpha_evax_proc->fmask, 4);
+
+ if (alpha_evax_proc->handler)
+ {
+ p = frag_more (8);
+ fixp = fix_new (frag_now, p - frag_now->fr_literal, 8,
+ alpha_evax_proc->handler, 0, 0, BFD_RELOC_64);
+ }
+
+ if (alpha_evax_proc->handler_data)
+ {
+ p = frag_more (8);
+ md_number_to_chars (p, alpha_evax_proc->handler_data, 8);
+ }
+}
+
+/* Support for crash debug on vms. */
+
+static void
+s_alpha_name (int ignore ATTRIBUTE_UNUSED)
+{
+ char *p;
+ expressionS exp;
+
+ if (now_seg != alpha_link_section)
+ {
+ as_bad (_(".name directive not in link (.link) section"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_warn (_(".name directive has no symbol"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ frag_align (3, 0, 0);
+ p = frag_more (8);
+
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 8, &exp, 0, BFD_RELOC_64);
+}
+
+/* Parse .linkage <symbol>.
+ Create a linkage pair relocation. */
+
+static void
+s_alpha_linkage (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ char *p;
+ fixS *fixp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_fatal (_("No symbol after .linkage"));
+ }
+ else
+ {
+ struct alpha_linkage_fixups *linkage_fixup;
+
+ p = frag_more (LKP_S_K_SIZE);
+ memset (p, 0, LKP_S_K_SIZE);
+ fixp = fix_new_exp
+ (frag_now, p - frag_now->fr_literal, LKP_S_K_SIZE, &exp, 0,
+ BFD_RELOC_ALPHA_LINKAGE);
+
+ if (alpha_insn_label == NULL)
+ alpha_insn_label = symbol_new
+ (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (), frag_now);
+
+ /* Create a linkage element. */
+ linkage_fixup = (struct alpha_linkage_fixups *)
+ xmalloc (sizeof (struct alpha_linkage_fixups));
+ linkage_fixup->fixp = fixp;
+ linkage_fixup->next = NULL;
+ linkage_fixup->label = alpha_insn_label;
+
+ /* Append it to the list. */
+ if (alpha_linkage_fixup_root == NULL)
+ alpha_linkage_fixup_root = linkage_fixup;
+ else
+ alpha_linkage_fixup_tail->next = linkage_fixup;
+ alpha_linkage_fixup_tail = linkage_fixup;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Parse .code_address <symbol>.
+ Create a code address relocation. */
+
+static void
+s_alpha_code_address (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ char *p;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ as_fatal (_("No symbol after .code_address"));
+ else
+ {
+ p = frag_more (8);
+ memset (p, 0, 8);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 8, &exp, 0,\
+ BFD_RELOC_ALPHA_CODEADDR);
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_fp_save (int ignore ATTRIBUTE_UNUSED)
+{
+ alpha_evax_proc->fp_save = tc_get_register (1);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_mask (int ignore ATTRIBUTE_UNUSED)
+{
+ long val;
+
+ if (get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .mask directive"));
+ --input_line_pointer;
+ }
+ else
+ {
+ alpha_evax_proc->imask = val;
+ (void) get_absolute_expression ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_fmask (int ignore ATTRIBUTE_UNUSED)
+{
+ long val;
+
+ if (get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("Bad .fmask directive"));
+ --input_line_pointer;
+ }
+ else
+ {
+ alpha_evax_proc->fmask = val;
+ (void) get_absolute_expression ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_alpha_end (int ignore ATTRIBUTE_UNUSED)
+{
+ char c;
+
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+ alpha_evax_proc = NULL;
+}
+
+static void
+s_alpha_file (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *s;
+ int length;
+ static char case_hack[32];
+
+ sprintf (case_hack, "<CASE:%01d%01d>",
+ alpha_flag_hash_long_names, alpha_flag_show_after_trunc);
+
+ s = symbol_find_or_make (case_hack);
+ symbol_get_bfdsym (s)->flags |= BSF_FILE;
+
+ get_absolute_expression ();
+ s = symbol_find_or_make (demand_copy_string (&length));
+ symbol_get_bfdsym (s)->flags |= BSF_FILE;
+ demand_empty_rest_of_line ();
+}
+#endif /* OBJ_EVAX */
+
+/* Handle the .gprel32 pseudo op. */
+
+static void
+s_alpha_gprel32 (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ char *p;
+
+ SKIP_WHITESPACE ();
+ expression (&e);
+
+#ifdef OBJ_ELF
+ switch (e.X_op)
+ {
+ case O_constant:
+ e.X_add_symbol = section_symbol (absolute_section);
+ e.X_op = O_symbol;
+ /* FALLTHRU */
+ case O_symbol:
+ break;
+ default:
+ abort ();
+ }
+#else
+#ifdef OBJ_ECOFF
+ switch (e.X_op)
+ {
+ case O_constant:
+ e.X_add_symbol = section_symbol (absolute_section);
+ /* fall through */
+ case O_symbol:
+ e.X_op = O_subtract;
+ e.X_op_symbol = alpha_gp_symbol;
+ break;
+ default:
+ abort ();
+ }
+#endif
+#endif
+
+ if (alpha_auto_align_on && alpha_current_align < 2)
+ alpha_align (2, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > 2)
+ alpha_current_align = 2;
+ alpha_insn_label = NULL;
+
+ p = frag_more (4);
+ memset (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4,
+ &e, 0, BFD_RELOC_GPREL32);
+}
+
+/* Handle floating point allocation pseudo-ops. This is like the
+ generic vresion, but it makes sure the current label, if any, is
+ correctly aligned. */
+
+static void
+s_alpha_float_cons (int type)
+{
+ int log_size;
+
+ switch (type)
+ {
+ default:
+ case 'f':
+ case 'F':
+ log_size = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'G':
+ log_size = 3;
+ break;
+
+ case 'x':
+ case 'X':
+ case 'p':
+ case 'P':
+ log_size = 4;
+ break;
+ }
+
+ if (alpha_auto_align_on && alpha_current_align < log_size)
+ alpha_align (log_size, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > log_size)
+ alpha_current_align = log_size;
+ alpha_insn_label = NULL;
+
+ float_cons (type);
+}
+
+/* Handle the .proc pseudo op. We don't really do much with it except
+ parse it. */
+
+static void
+s_alpha_proc (int is_static ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ symbolS *symbolP;
+ int temp;
+
+ /* Takes ".proc name,nargs". */
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_warn (_("Expected comma after name \"%s\""), name);
+ *p = c;
+ temp = 0;
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ }
+ /* *symbol_get_obj (symbolP) = (signed char) temp; */
+ (void) symbolP;
+ as_warn (_("unhandled: .proc %s,%d"), name, temp);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .set pseudo op. This is used to turn on and off most of
+ the assembler features. */
+
+static void
+s_alpha_set (int x ATTRIBUTE_UNUSED)
+{
+ char *name, ch, *s;
+ int yesno = 1;
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ ch = get_symbol_end ();
+
+ s = name;
+ if (s[0] == 'n' && s[1] == 'o')
+ {
+ yesno = 0;
+ s += 2;
+ }
+ if (!strcmp ("reorder", s))
+ /* ignore */ ;
+ else if (!strcmp ("at", s))
+ alpha_noat_on = !yesno;
+ else if (!strcmp ("macro", s))
+ alpha_macros_on = yesno;
+ else if (!strcmp ("move", s))
+ /* ignore */ ;
+ else if (!strcmp ("volatile", s))
+ /* ignore */ ;
+ else
+ as_warn (_("Tried to .set unrecognized mode `%s'"), name);
+
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .base pseudo op. This changes the assembler's notion of
+ the $gp register. */
+
+static void
+s_alpha_base (int ignore ATTRIBUTE_UNUSED)
+{
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '$')
+ {
+ /* $rNN form. */
+ input_line_pointer++;
+ if (*input_line_pointer == 'r')
+ input_line_pointer++;
+ }
+
+ alpha_gp_register = get_absolute_expression ();
+ if (alpha_gp_register < 0 || alpha_gp_register > 31)
+ {
+ alpha_gp_register = AXP_REG_GP;
+ as_warn (_("Bad base register, using $%d."), alpha_gp_register);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .align pseudo-op. This aligns to a power of two. It
+ also adjusts any current instruction label. We treat this the same
+ way the MIPS port does: .align 0 turns off auto alignment. */
+
+static void
+s_alpha_align (int ignore ATTRIBUTE_UNUSED)
+{
+ int align;
+ char fill, *pfill;
+ long max_alignment = 16;
+
+ align = get_absolute_expression ();
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_bad (_("Alignment too large: %d. assumed"), align);
+ }
+ else if (align < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed"));
+ align = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ fill = get_absolute_expression ();
+ pfill = &fill;
+ }
+ else
+ pfill = NULL;
+
+ if (align != 0)
+ {
+ alpha_auto_align_on = 1;
+ alpha_align (align, pfill, NULL, 1);
+ }
+ else
+ {
+ alpha_auto_align_on = 0;
+ }
+ alpha_insn_label = NULL;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Hook the normal string processor to reset known alignment. */
+
+static void
+s_alpha_stringer (int terminate)
+{
+ alpha_current_align = 0;
+ alpha_insn_label = NULL;
+ stringer (8 + terminate);
+}
+
+/* Hook the normal space processing to reset known alignment. */
+
+static void
+s_alpha_space (int ignore)
+{
+ alpha_current_align = 0;
+ alpha_insn_label = NULL;
+ s_space (ignore);
+}
+
+/* Hook into cons for auto-alignment. */
+
+void
+alpha_cons_align (int size)
+{
+ int log_size;
+
+ log_size = 0;
+ while ((size >>= 1) != 0)
+ ++log_size;
+
+ if (alpha_auto_align_on && alpha_current_align < log_size)
+ alpha_align (log_size, (char *) NULL, alpha_insn_label, 0);
+ if (alpha_current_align > log_size)
+ alpha_current_align = log_size;
+ alpha_insn_label = NULL;
+}
+
+/* Here come the .uword, .ulong, and .uquad explicitly unaligned
+ pseudos. We just turn off auto-alignment and call down to cons. */
+
+static void
+s_alpha_ucons (int bytes)
+{
+ int hold = alpha_auto_align_on;
+ alpha_auto_align_on = 0;
+ cons (bytes);
+ alpha_auto_align_on = hold;
+}
+
+/* Switch the working cpu type. */
+
+static void
+s_alpha_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name, ch;
+ const struct cpu_type *p;
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ ch = get_symbol_end ();
+
+ for (p = cpu_types; p->name; ++p)
+ if (strcmp (name, p->name) == 0)
+ {
+ alpha_target_name = p->name, alpha_target = p->flags;
+ goto found;
+ }
+ as_warn (_("Unknown CPU identifier `%s'"), name);
+
+found:
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+#ifdef DEBUG1
+/* print token expression with alpha specific extension. */
+
+static void
+alpha_print_token (FILE *f, const expressionS *exp)
+{
+ switch (exp->X_op)
+ {
+ case O_cpregister:
+ putc (',', f);
+ /* FALLTHRU */
+ case O_pregister:
+ putc ('(', f);
+ {
+ expressionS nexp = *exp;
+ nexp.X_op = O_register;
+ print_expr_1 (f, &nexp);
+ }
+ putc (')', f);
+ break;
+ default:
+ print_expr_1 (f, exp);
+ break;
+ }
+}
+#endif
+
+/* The target specific pseudo-ops which we support. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+#ifdef OBJ_ECOFF
+ {"comm", s_alpha_comm, 0}, /* OSF1 compiler does this. */
+ {"rdata", s_alpha_rdata, 0},
+#endif
+ {"text", s_alpha_text, 0},
+ {"data", s_alpha_data, 0},
+#ifdef OBJ_ECOFF
+ {"sdata", s_alpha_sdata, 0},
+#endif
+#ifdef OBJ_ELF
+ {"section", s_alpha_section, 0},
+ {"section.s", s_alpha_section, 0},
+ {"sect", s_alpha_section, 0},
+ {"sect.s", s_alpha_section, 0},
+#endif
+#ifdef OBJ_EVAX
+ {"section", s_alpha_section, 0},
+ {"literals", s_alpha_literals, 0},
+ {"pdesc", s_alpha_pdesc, 0},
+ {"name", s_alpha_name, 0},
+ {"linkage", s_alpha_linkage, 0},
+ {"code_address", s_alpha_code_address, 0},
+ {"ent", s_alpha_ent, 0},
+ {"frame", s_alpha_frame, 0},
+ {"fp_save", s_alpha_fp_save, 0},
+ {"mask", s_alpha_mask, 0},
+ {"fmask", s_alpha_fmask, 0},
+ {"end", s_alpha_end, 0},
+ {"file", s_alpha_file, 0},
+ {"rdata", s_alpha_section, 1},
+ {"comm", s_alpha_comm, 0},
+ {"link", s_alpha_section, 3},
+ {"ctors", s_alpha_section, 4},
+ {"dtors", s_alpha_section, 5},
+ {"handler", s_alpha_handler, 0},
+ {"handler_data", s_alpha_handler, 1},
+#endif
+#ifdef OBJ_ELF
+ /* Frame related pseudos. */
+ {"ent", s_alpha_ent, 0},
+ {"end", s_alpha_end, 0},
+ {"mask", s_alpha_mask, 0},
+ {"fmask", s_alpha_mask, 1},
+ {"frame", s_alpha_frame, 0},
+ {"prologue", s_alpha_prologue, 0},
+ {"file", s_alpha_file, 5},
+ {"loc", s_alpha_loc, 9},
+ {"stabs", s_alpha_stab, 's'},
+ {"stabn", s_alpha_stab, 'n'},
+ {"usepv", s_alpha_usepv, 0},
+ /* COFF debugging related pseudos. */
+ {"begin", s_alpha_coff_wrapper, 0},
+ {"bend", s_alpha_coff_wrapper, 1},
+ {"def", s_alpha_coff_wrapper, 2},
+ {"dim", s_alpha_coff_wrapper, 3},
+ {"endef", s_alpha_coff_wrapper, 4},
+ {"scl", s_alpha_coff_wrapper, 5},
+ {"tag", s_alpha_coff_wrapper, 6},
+ {"val", s_alpha_coff_wrapper, 7},
+#else
+#ifdef OBJ_EVAX
+ {"prologue", s_alpha_prologue, 0},
+#else
+ {"prologue", s_ignore, 0},
+#endif
+#endif
+ {"gprel32", s_alpha_gprel32, 0},
+ {"t_floating", s_alpha_float_cons, 'd'},
+ {"s_floating", s_alpha_float_cons, 'f'},
+ {"f_floating", s_alpha_float_cons, 'F'},
+ {"g_floating", s_alpha_float_cons, 'G'},
+ {"d_floating", s_alpha_float_cons, 'D'},
+
+ {"proc", s_alpha_proc, 0},
+ {"aproc", s_alpha_proc, 1},
+ {"set", s_alpha_set, 0},
+ {"reguse", s_ignore, 0},
+ {"livereg", s_ignore, 0},
+ {"base", s_alpha_base, 0}, /*??*/
+ {"option", s_ignore, 0},
+ {"aent", s_ignore, 0},
+ {"ugen", s_ignore, 0},
+ {"eflag", s_ignore, 0},
+
+ {"align", s_alpha_align, 0},
+ {"double", s_alpha_float_cons, 'd'},
+ {"float", s_alpha_float_cons, 'f'},
+ {"single", s_alpha_float_cons, 'f'},
+ {"ascii", s_alpha_stringer, 0},
+ {"asciz", s_alpha_stringer, 1},
+ {"string", s_alpha_stringer, 1},
+ {"space", s_alpha_space, 0},
+ {"skip", s_alpha_space, 0},
+ {"zero", s_alpha_space, 0},
+
+/* Unaligned data pseudos. */
+ {"uword", s_alpha_ucons, 2},
+ {"ulong", s_alpha_ucons, 4},
+ {"uquad", s_alpha_ucons, 8},
+
+#ifdef OBJ_ELF
+/* Dwarf wants these versions of unaligned. */
+ {"2byte", s_alpha_ucons, 2},
+ {"4byte", s_alpha_ucons, 4},
+ {"8byte", s_alpha_ucons, 8},
+#endif
+
+/* We don't do any optimizing, so we can safely ignore these. */
+ {"noalias", s_ignore, 0},
+ {"alias", s_ignore, 0},
+
+ {"arch", s_alpha_arch, 0},
+
+ {NULL, 0, 0},
+};
+
+#ifdef OBJ_ECOFF
+
+/* @@@ GP selection voodoo. All of this seems overly complicated and
+ unnecessary; which is the primary reason it's for ECOFF only. */
+
+static inline void
+maybe_set_gp (asection *sec)
+{
+ bfd_vma vma;
+
+ if (!sec)
+ return;
+ vma = bfd_get_section_vma (sec->owner, sec);
+ if (vma && vma < alpha_gp_value)
+ alpha_gp_value = vma;
+}
+
+static void
+select_gp_value (void)
+{
+ gas_assert (alpha_gp_value == 0);
+
+ /* Get minus-one in whatever width... */
+ alpha_gp_value = 0;
+ alpha_gp_value--;
+
+ /* Select the smallest VMA of these existing sections. */
+ maybe_set_gp (alpha_lita_section);
+
+/* @@ Will a simple 0x8000 work here? If not, why not? */
+#define GP_ADJUSTMENT (0x8000 - 0x10)
+
+ alpha_gp_value += GP_ADJUSTMENT;
+
+ S_SET_VALUE (alpha_gp_symbol, alpha_gp_value);
+
+#ifdef DEBUG1
+ printf (_("Chose GP value of %lx\n"), alpha_gp_value);
+#endif
+}
+#endif /* OBJ_ECOFF */
+
+#ifdef OBJ_ELF
+/* Map 's' to SHF_ALPHA_GPREL. */
+
+bfd_vma
+alpha_elf_section_letter (int letter, char **ptr_msg)
+{
+ if (letter == 's')
+ return SHF_ALPHA_GPREL;
+
+ *ptr_msg = _("bad .section directive: want a,s,w,x,M,S,G,T in string");
+ return -1;
+}
+
+/* Map SHF_ALPHA_GPREL to SEC_SMALL_DATA. */
+
+flagword
+alpha_elf_section_flags (flagword flags, bfd_vma attr, int type ATTRIBUTE_UNUSED)
+{
+ if (attr & SHF_ALPHA_GPREL)
+ flags |= SEC_SMALL_DATA;
+ return flags;
+}
+#endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+alpha_handle_align (fragS *fragp)
+{
+ static char const unop[4] = { 0x00, 0x00, 0xfe, 0x2f };
+ static char const nopunop[8] =
+ {
+ 0x1f, 0x04, 0xff, 0x47,
+ 0x00, 0x00, 0xfe, 0x2f
+ };
+
+ int bytes, fix;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ if (bytes & 3)
+ {
+ fix = bytes & 3;
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ if (bytes & 4)
+ {
+ memcpy (p, unop, 4);
+ p += 4;
+ bytes -= 4;
+ fix += 4;
+ }
+
+ memcpy (p, nopunop, 8);
+
+ fragp->fr_fix += fix;
+ fragp->fr_var = 8;
+}
+
+/* Public interface functions. */
+
+/* This function is called once, at assembler startup time. It sets
+ up all the tables, etc. that the MD part of the assembler will
+ need, that can be determined before arguments are parsed. */
+
+void
+md_begin (void)
+{
+ unsigned int i;
+
+ /* Verify that X_op field is wide enough. */
+ {
+ expressionS e;
+
+ e.X_op = O_max;
+ gas_assert (e.X_op == O_max);
+ }
+
+ /* Create the opcode hash table. */
+ alpha_opcode_hash = hash_new ();
+
+ for (i = 0; i < alpha_num_opcodes;)
+ {
+ const char *name, *retval, *slash;
+
+ name = alpha_opcodes[i].name;
+ retval = hash_insert (alpha_opcode_hash, name, (void *) &alpha_opcodes[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash opcode `%s': %s"),
+ name, retval);
+
+ /* Some opcodes include modifiers of various sorts with a "/mod"
+ syntax, like the architecture manual suggests. However, for
+ use with gcc at least, we also need access to those same opcodes
+ without the "/". */
+
+ if ((slash = strchr (name, '/')) != NULL)
+ {
+ char *p = (char *) xmalloc (strlen (name));
+
+ memcpy (p, name, slash - name);
+ strcpy (p + (slash - name), slash + 1);
+
+ (void) hash_insert (alpha_opcode_hash, p, (void *) &alpha_opcodes[i]);
+ /* Ignore failures -- the opcode table does duplicate some
+ variants in different forms, like "hw_stq" and "hw_st/q". */
+ }
+
+ while (++i < alpha_num_opcodes
+ && (alpha_opcodes[i].name == name
+ || !strcmp (alpha_opcodes[i].name, name)))
+ continue;
+ }
+
+ /* Create the macro hash table. */
+ alpha_macro_hash = hash_new ();
+
+ for (i = 0; i < alpha_num_macros;)
+ {
+ const char *name, *retval;
+
+ name = alpha_macros[i].name;
+ retval = hash_insert (alpha_macro_hash, name, (void *) &alpha_macros[i]);
+ if (retval)
+ as_fatal (_("internal error: can't hash macro `%s': %s"),
+ name, retval);
+
+ while (++i < alpha_num_macros
+ && (alpha_macros[i].name == name
+ || !strcmp (alpha_macros[i].name, name)))
+ continue;
+ }
+
+ /* Construct symbols for each of the registers. */
+ for (i = 0; i < 32; ++i)
+ {
+ char name[4];
+
+ sprintf (name, "$%d", i);
+ alpha_register_table[i] = symbol_create (name, reg_section, i,
+ &zero_address_frag);
+ }
+
+ for (; i < 64; ++i)
+ {
+ char name[5];
+
+ sprintf (name, "$f%d", i - 32);
+ alpha_register_table[i] = symbol_create (name, reg_section, i,
+ &zero_address_frag);
+ }
+
+ /* Create the special symbols and sections we'll be using. */
+
+ /* So .sbss will get used for tiny objects. */
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+#ifdef OBJ_ECOFF
+ create_literal_section (".lita", &alpha_lita_section, &alpha_lita_symbol);
+
+ /* For handling the GP, create a symbol that won't be output in the
+ symbol table. We'll edit it out of relocs later. */
+ alpha_gp_symbol = symbol_create ("<GP value>", alpha_lita_section, 0x8000,
+ &zero_address_frag);
+#endif
+
+#ifdef OBJ_EVAX
+ create_literal_section (".link", &alpha_link_section, &alpha_link_symbol);
+#endif
+
+#ifdef OBJ_ELF
+ if (ECOFF_DEBUGGING)
+ {
+ segT sec = subseg_new (".mdebug", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec, SEC_HAS_CONTENTS | SEC_READONLY);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+ }
+#endif
+
+ /* Create literal lookup hash table. */
+ alpha_literal_hash = hash_new ();
+
+ subseg_set (text_section, 0);
+}
+
+/* The public interface to the instruction assembler. */
+
+void
+md_assemble (char *str)
+{
+ /* Current maximum is 13. */
+ char opname[32];
+ expressionS tok[MAX_INSN_ARGS];
+ int ntok, trunclen;
+ size_t opnamelen;
+
+ /* Split off the opcode. */
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/46819");
+ trunclen = (opnamelen < sizeof (opname) - 1
+ ? opnamelen
+ : sizeof (opname) - 1);
+ memcpy (opname, str, trunclen);
+ opname[trunclen] = '\0';
+
+ /* Tokenize the rest of the line. */
+ if ((ntok = tokenize_arguments (str + opnamelen, tok, MAX_INSN_ARGS)) < 0)
+ {
+ if (ntok != TOKENIZE_ERROR_REPORT)
+ as_bad (_("syntax error"));
+
+ return;
+ }
+
+ /* Finish it off. */
+ assemble_tokens (opname, tok, ntok, alpha_macros_on);
+}
+
+/* Round up a section's size to the appropriate boundary. */
+
+valueT
+md_section_align (segT seg, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ valueT mask = ((valueT) 1 << align) - 1;
+
+ return (size + mask) & ~mask;
+}
+
+/* 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. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ extern char *vax_md_atof (int, char *, int *);
+
+ switch (type)
+ {
+ /* VAX floats. */
+ case 'G':
+ /* vax_md_atof() doesn't like "G" for some reason. */
+ type = 'g';
+ case 'F':
+ case 'D':
+ return vax_md_atof (type, litP, sizeP);
+
+ default:
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+ }
+}
+
+/* Take care of the target-specific command-line options. */
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'F':
+ alpha_nofloats_on = 1;
+ break;
+
+ case OPTION_32ADDR:
+ alpha_addr32_on = 1;
+ break;
+
+ case 'g':
+ alpha_debug = 1;
+ break;
+
+ case 'G':
+ g_switch_value = atoi (arg);
+ break;
+
+ case 'm':
+ {
+ const struct cpu_type *p;
+
+ for (p = cpu_types; p->name; ++p)
+ if (strcmp (arg, p->name) == 0)
+ {
+ alpha_target_name = p->name, alpha_target = p->flags;
+ goto found;
+ }
+ as_warn (_("Unknown CPU identifier `%s'"), arg);
+ found:;
+ }
+ break;
+
+#ifdef OBJ_EVAX
+ case '+': /* For g++. Hash any name > 63 chars long. */
+ alpha_flag_hash_long_names = 1;
+ break;
+
+ case 'H': /* Show new symbol after hash truncation. */
+ alpha_flag_show_after_trunc = 1;
+ break;
+
+ case 'h': /* For gnu-c/vax compatibility. */
+ break;
+
+ case OPTION_REPLACE:
+ alpha_flag_replace = 1;
+ break;
+
+ case OPTION_NOREPLACE:
+ alpha_flag_replace = 0;
+ break;
+#endif
+
+ case OPTION_RELAX:
+ alpha_flag_relax = 1;
+ break;
+
+#ifdef OBJ_ELF
+ case OPTION_MDEBUG:
+ alpha_flag_mdebug = 1;
+ break;
+ case OPTION_NO_MDEBUG:
+ alpha_flag_mdebug = 0;
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Print a description of the command-line options that we accept. */
+
+void
+md_show_usage (FILE *stream)
+{
+ fputs (_("\
+Alpha options:\n\
+-32addr treat addresses as 32-bit values\n\
+-F lack floating point instructions support\n\
+-mev4 | -mev45 | -mev5 | -mev56 | -mpca56 | -mev6 | -mev67 | -mev68 | -mall\n\
+ specify variant of Alpha architecture\n\
+-m21064 | -m21066 | -m21164 | -m21164a | -m21164pc | -m21264 | -m21264a | -m21264b\n\
+ these variants include PALcode opcodes\n"),
+ stream);
+#ifdef OBJ_EVAX
+ fputs (_("\
+VMS options:\n\
+-+ encode (don't truncate) names longer than 64 characters\n\
+-H show new symbol after hash truncation\n\
+-replace/-noreplace enable or disable the optimization of procedure calls\n"),
+ stream);
+#endif
+}
+
+/* Decide from what point a pc-relative relocation is relative to,
+ relative to the pc-relative fixup. Er, relatively speaking. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_23_PCREL_S2:
+ case BFD_RELOC_ALPHA_HINT:
+ case BFD_RELOC_ALPHA_BRSGP:
+ return addr + 4;
+ default:
+ return addr;
+ }
+}
+
+/* Attempt to simplify or even eliminate a fixup. The return value is
+ ignored; perhaps it was once meaningful, but now it is historical.
+ To indicate that a fixup has been eliminated, set fixP->fx_done.
+
+ For ELF, here it is that we transform the GPDISP_HI16 reloc we used
+ internally into the GPDISP reloc used externally. We had to do
+ this so that we'd have the GPDISP_LO16 reloc as a tag to compute
+ the distance to the "lda" instruction for setting the addend to
+ GPDISP. */
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg)
+{
+ char * const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = * valP;
+ unsigned image, size;
+
+ switch (fixP->fx_r_type)
+ {
+ /* The GPDISP relocations are processed internally with a symbol
+ referring to the current function's section; we need to drop
+ in a value which, when added to the address of the start of
+ the function, gives the desired GP. */
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ {
+ fixS *next = fixP->fx_next;
+
+ /* With user-specified !gpdisp relocations, we can be missing
+ the matching LO16 reloc. We will have already issued an
+ error message. */
+ if (next)
+ fixP->fx_offset = (next->fx_frag->fr_address + next->fx_where
+ - fixP->fx_frag->fr_address - fixP->fx_where);
+
+ value = (value - sign_extend_16 (value)) >> 16;
+ }
+#ifdef OBJ_ELF
+ fixP->fx_r_type = BFD_RELOC_ALPHA_GPDISP;
+#endif
+ goto do_reloc_gp;
+
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ value = sign_extend_16 (value);
+ fixP->fx_offset = 0;
+#ifdef OBJ_ELF
+ fixP->fx_done = 1;
+#endif
+
+ do_reloc_gp:
+ fixP->fx_addsy = section_symbol (seg);
+ md_number_to_chars (fixpos, value, 2);
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ size = 2;
+ goto do_reloc_xx;
+
+ case BFD_RELOC_32:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ size = 4;
+ goto do_reloc_xx;
+
+ case BFD_RELOC_64:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ size = 8;
+
+ do_reloc_xx:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ md_number_to_chars (fixpos, value, size);
+ goto done;
+ }
+ return;
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_GPREL32:
+ gas_assert (fixP->fx_subsy == alpha_gp_symbol);
+ fixP->fx_subsy = 0;
+ /* FIXME: inherited this obliviousness of `value' -- why? */
+ md_number_to_chars (fixpos, -alpha_gp_value, 4);
+ break;
+#else
+ case BFD_RELOC_GPREL32:
+#endif
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_ALPHA_GPREL_HI16:
+ case BFD_RELOC_ALPHA_GPREL_LO16:
+ return;
+
+ case BFD_RELOC_23_PCREL_S2:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ image = bfd_getl32 (fixpos);
+ image = (image & ~0x1FFFFF) | ((value >> 2) & 0x1FFFFF);
+ goto write_done;
+ }
+ return;
+
+ case BFD_RELOC_ALPHA_HINT:
+ if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
+ {
+ image = bfd_getl32 (fixpos);
+ image = (image & ~0x3FFF) | ((value >> 2) & 0x3FFF);
+ goto write_done;
+ }
+ return;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_BRSGP:
+ return;
+
+ case BFD_RELOC_ALPHA_TLSGD:
+ case BFD_RELOC_ALPHA_TLSLDM:
+ case BFD_RELOC_ALPHA_GOTDTPREL16:
+ case BFD_RELOC_ALPHA_DTPREL_HI16:
+ case BFD_RELOC_ALPHA_DTPREL_LO16:
+ case BFD_RELOC_ALPHA_DTPREL16:
+ case BFD_RELOC_ALPHA_GOTTPREL16:
+ case BFD_RELOC_ALPHA_TPREL_HI16:
+ case BFD_RELOC_ALPHA_TPREL_LO16:
+ case BFD_RELOC_ALPHA_TPREL16:
+ if (fixP->fx_addsy)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ return;
+#endif
+
+#ifdef OBJ_ECOFF
+ case BFD_RELOC_ALPHA_LITERAL:
+ md_number_to_chars (fixpos, value, 2);
+ return;
+#endif
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+ return;
+
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_NOP:
+ value -= (8 + 4); /* PC-relative, base is jsr+4. */
+
+ /* From B.4.5.2 of the OpenVMS Linker Utility Manual:
+ "Finally, the ETIR$C_STC_BSR command passes the same address
+ as ETIR$C_STC_NOP (so that they will fail or succeed together),
+ and the same test is done again." */
+ if (S_GET_SEGMENT (fixP->fx_addsy) == undefined_section)
+ {
+ fixP->fx_addnumber = -value;
+ return;
+ }
+
+ if ((abs (value) >> 2) & ~0xfffff)
+ goto done;
+ else
+ {
+ /* Change to a nop. */
+ image = 0x47FF041F;
+ goto write_done;
+ }
+
+ case BFD_RELOC_ALPHA_LDA:
+ /* fixup_segment sets fixP->fx_addsy to NULL when it can pre-compute
+ the value for an O_subtract. */
+ if (fixP->fx_addsy
+ && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section)
+ {
+ fixP->fx_addnumber = symbol_get_bfdsym (fixP->fx_subsy)->value;
+ return;
+ }
+
+ if ((abs (value)) & ~0x7fff)
+ goto done;
+ else
+ {
+ /* Change to an lda. */
+ image = 0x237B0000 | (value & 0xFFFF);
+ goto write_done;
+ }
+
+ case BFD_RELOC_ALPHA_BSR:
+ case BFD_RELOC_ALPHA_BOH:
+ value -= 4; /* PC-relative, base is jsr+4. */
+
+ /* See comment in the BFD_RELOC_ALPHA_NOP case above. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) == undefined_section)
+ {
+ fixP->fx_addnumber = -value;
+ return;
+ }
+
+ if ((abs (value) >> 2) & ~0xfffff)
+ {
+ /* Out of range. */
+ if (fixP->fx_r_type == BFD_RELOC_ALPHA_BOH)
+ {
+ /* Add a hint. */
+ image = bfd_getl32(fixpos);
+ image = (image & ~0x3FFF) | ((value >> 2) & 0x3FFF);
+ goto write_done;
+ }
+ goto done;
+ }
+ else
+ {
+ /* Change to a branch. */
+ image = 0xD3400000 | ((value >> 2) & 0x1FFFFF);
+ goto write_done;
+ }
+#endif
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ return;
+
+ default:
+ {
+ const struct alpha_operand *operand;
+
+ if ((int) fixP->fx_r_type >= 0)
+ as_fatal (_("unhandled relocation type %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+
+ gas_assert (-(int) fixP->fx_r_type < (int) alpha_num_operands);
+ operand = &alpha_operands[-(int) fixP->fx_r_type];
+
+ /* The rest of these fixups only exist internally during symbol
+ resolution and have no representation in the object file.
+ Therefore they must be completely resolved as constants. */
+
+ if (fixP->fx_addsy != 0
+ && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("non-absolute expression in constant field"));
+
+ image = bfd_getl32 (fixpos);
+ image = insert_operand (image, operand, (offsetT) value,
+ fixP->fx_file, fixP->fx_line);
+ }
+ goto write_done;
+ }
+
+ if (fixP->fx_addsy != 0 || fixP->fx_pcrel != 0)
+ return;
+ else
+ {
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("type %d reloc done?\n"), (int) fixP->fx_r_type);
+ goto done;
+ }
+
+write_done:
+ md_number_to_chars (fixpos, image, 4);
+
+done:
+ fixP->fx_done = 1;
+}
+
+/* Look for a register name in the given symbol. */
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ if (*name == '$')
+ {
+ int is_float = 0, num;
+
+ switch (*++name)
+ {
+ case 'f':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[AXP_REG_FP];
+ is_float = 32;
+ /* Fall through. */
+
+ case 'r':
+ if (!ISDIGIT (*++name))
+ break;
+ /* Fall through. */
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (name[1] == '\0')
+ num = name[0] - '0';
+ else if (name[0] != '0' && ISDIGIT (name[1]) && name[2] == '\0')
+ {
+ num = (name[0] - '0') * 10 + name[1] - '0';
+ if (num >= 32)
+ break;
+ }
+ else
+ break;
+
+ if (!alpha_noat_on && (num + is_float) == AXP_REG_AT)
+ as_warn (_("Used $at without \".set noat\""));
+ return alpha_register_table[num + is_float];
+
+ case 'a':
+ if (name[1] == 't' && name[2] == '\0')
+ {
+ if (!alpha_noat_on)
+ as_warn (_("Used $at without \".set noat\""));
+ return alpha_register_table[AXP_REG_AT];
+ }
+ break;
+
+ case 'g':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[alpha_gp_register];
+ break;
+
+ case 's':
+ if (name[1] == 'p' && name[2] == '\0')
+ return alpha_register_table[AXP_REG_SP];
+ break;
+ }
+ }
+ return NULL;
+}
+
+#ifdef OBJ_ECOFF
+/* @@@ Magic ECOFF bits. */
+
+void
+alpha_frob_ecoff_data (void)
+{
+ select_gp_value ();
+ /* $zero and $f31 are read-only. */
+ alpha_gprmask &= ~1;
+ alpha_fprmask &= ~1;
+}
+#endif
+
+/* Hook to remember a recently defined label so that the auto-align
+ code can adjust the symbol after we know what alignment will be
+ required. */
+
+void
+alpha_define_label (symbolS *sym)
+{
+ alpha_insn_label = sym;
+#ifdef OBJ_ELF
+ dwarf2_emit_label (sym);
+#endif
+}
+
+/* Return true if we must always emit a reloc for a type and false if
+ there is some hope of resolving it at assembly time. */
+
+int
+alpha_force_relocation (fixS *f)
+{
+ if (alpha_flag_relax)
+ return 1;
+
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ case BFD_RELOC_ALPHA_GPDISP:
+ case BFD_RELOC_ALPHA_LITERAL:
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_ALPHA_GPREL_HI16:
+ case BFD_RELOC_ALPHA_GPREL_LO16:
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+ case BFD_RELOC_ALPHA_BRSGP:
+ case BFD_RELOC_ALPHA_TLSGD:
+ case BFD_RELOC_ALPHA_TLSLDM:
+ case BFD_RELOC_ALPHA_GOTDTPREL16:
+ case BFD_RELOC_ALPHA_DTPREL_HI16:
+ case BFD_RELOC_ALPHA_DTPREL_LO16:
+ case BFD_RELOC_ALPHA_DTPREL16:
+ case BFD_RELOC_ALPHA_GOTTPREL16:
+ case BFD_RELOC_ALPHA_TPREL_HI16:
+ case BFD_RELOC_ALPHA_TPREL_LO16:
+ case BFD_RELOC_ALPHA_TPREL16:
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_NOP:
+ case BFD_RELOC_ALPHA_BSR:
+ case BFD_RELOC_ALPHA_LDA:
+ case BFD_RELOC_ALPHA_BOH:
+#endif
+ return 1;
+
+ default:
+ break;
+ }
+
+ return generic_force_reloc (f);
+}
+
+/* Return true if we can partially resolve a relocation now. */
+
+int
+alpha_fix_adjustable (fixS *f)
+{
+ /* Are there any relocation types for which we must generate a
+ reloc but we can adjust the values contained within it? */
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_ALPHA_GPDISP_HI16:
+ case BFD_RELOC_ALPHA_GPDISP_LO16:
+ case BFD_RELOC_ALPHA_GPDISP:
+ return 0;
+
+ case BFD_RELOC_ALPHA_LITERAL:
+ case BFD_RELOC_ALPHA_ELF_LITERAL:
+ case BFD_RELOC_ALPHA_LITUSE:
+ case BFD_RELOC_ALPHA_LINKAGE:
+ case BFD_RELOC_ALPHA_CODEADDR:
+ return 1;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ return 0;
+
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_ALPHA_GPREL_HI16:
+ case BFD_RELOC_ALPHA_GPREL_LO16:
+ case BFD_RELOC_23_PCREL_S2:
+ case BFD_RELOC_16:
+ case BFD_RELOC_32:
+ case BFD_RELOC_64:
+ case BFD_RELOC_ALPHA_HINT:
+ return 1;
+
+ case BFD_RELOC_ALPHA_TLSGD:
+ case BFD_RELOC_ALPHA_TLSLDM:
+ case BFD_RELOC_ALPHA_GOTDTPREL16:
+ case BFD_RELOC_ALPHA_DTPREL_HI16:
+ case BFD_RELOC_ALPHA_DTPREL_LO16:
+ case BFD_RELOC_ALPHA_DTPREL16:
+ case BFD_RELOC_ALPHA_GOTTPREL16:
+ case BFD_RELOC_ALPHA_TPREL_HI16:
+ case BFD_RELOC_ALPHA_TPREL_LO16:
+ case BFD_RELOC_ALPHA_TPREL16:
+ /* ??? No idea why we can't return a reference to .tbss+10, but
+ we're preventing this in the other assemblers. Follow for now. */
+ return 0;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ALPHA_BRSGP:
+ /* If we have a BRSGP reloc to a local symbol, adjust it to BRADDR and
+ let it get resolved at assembly time. */
+ {
+ symbolS *sym = f->fx_addsy;
+ const char *name;
+ int offset = 0;
+
+ if (generic_force_reloc (f))
+ return 0;
+
+ switch (S_GET_OTHER (sym) & STO_ALPHA_STD_GPLOAD)
+ {
+ case STO_ALPHA_NOPV:
+ break;
+ case STO_ALPHA_STD_GPLOAD:
+ offset = 8;
+ break;
+ default:
+ if (S_IS_LOCAL (sym))
+ name = "<local>";
+ else
+ name = S_GET_NAME (sym);
+ as_bad_where (f->fx_file, f->fx_line,
+ _("!samegp reloc against symbol without .prologue: %s"),
+ name);
+ break;
+ }
+ f->fx_r_type = BFD_RELOC_23_PCREL_S2;
+ f->fx_offset += offset;
+ return 1;
+ }
+#endif
+#ifdef OBJ_EVAX
+ case BFD_RELOC_ALPHA_NOP:
+ case BFD_RELOC_ALPHA_BSR:
+ case BFD_RELOC_ALPHA_LDA:
+ case BFD_RELOC_ALPHA_BOH:
+ return 1;
+#endif
+
+ default:
+ return 1;
+ }
+}
+
+/* Generate the BFD reloc to be stuck in the object file from the
+ fixup used internally in the assembler. */
+
+arelent *
+tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED,
+ fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (* reloc));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ gas_assert ((int) fixp->fx_r_type > 0);
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+
+ gas_assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->addend = fixp->fx_offset;
+
+#ifdef OBJ_ECOFF
+ /* Fake out bfd_perform_relocation. sigh. */
+ /* ??? Better would be to use the special_function hook. */
+ if (fixp->fx_r_type == BFD_RELOC_ALPHA_LITERAL)
+ reloc->addend = -alpha_gp_value;
+#endif
+
+#ifdef OBJ_EVAX
+ switch (fixp->fx_r_type)
+ {
+ struct evax_private_udata_struct *udata;
+ const char *pname;
+ int pname_len;
+
+ case BFD_RELOC_ALPHA_LINKAGE:
+ /* Copy the linkage index. */
+ reloc->addend = fixp->fx_addnumber;
+ break;
+
+ case BFD_RELOC_ALPHA_NOP:
+ case BFD_RELOC_ALPHA_BSR:
+ case BFD_RELOC_ALPHA_LDA:
+ case BFD_RELOC_ALPHA_BOH:
+ pname = symbol_get_bfdsym (fixp->fx_addsy)->name;
+
+ /* We need the non-suffixed name of the procedure. Beware that
+ the main symbol might be equated so look it up and take its name. */
+ pname_len = strlen (pname);
+ if (pname_len > 4 && strcmp (pname + pname_len - 4, "..en") == 0)
+ {
+ symbolS *sym;
+ char *my_pname = (char *) alloca (pname_len - 4 + 1);
+
+ memcpy (my_pname, pname, pname_len - 4);
+ my_pname [pname_len - 4] = 0;
+ sym = symbol_find (my_pname);
+ if (sym == NULL)
+ abort ();
+
+ while (symbol_equated_reloc_p (sym))
+ {
+ symbolS *n = symbol_get_value_expression (sym)->X_add_symbol;
+
+ /* We must avoid looping, as that can occur with a badly
+ written program. */
+ if (n == sym)
+ break;
+ sym = n;
+ }
+ pname = symbol_get_bfdsym (sym)->name;
+ }
+
+ udata = (struct evax_private_udata_struct *)
+ xmalloc (sizeof (struct evax_private_udata_struct));
+ udata->enbsym = symbol_get_bfdsym (fixp->fx_addsy);
+ udata->bsym = symbol_get_bfdsym (fixp->tc_fix_data.info->psym);
+ udata->origname = (char *)pname;
+ udata->lkindex = ((struct evax_private_udata_struct *)
+ symbol_get_bfdsym (fixp->tc_fix_data.info->sym)->udata.p)->lkindex;
+ reloc->sym_ptr_ptr = (void *)udata;
+ reloc->addend = fixp->fx_addnumber;
+
+ default:
+ break;
+ }
+#endif
+
+ return reloc;
+}
+
+/* Parse a register name off of the input_line and return a register
+ number. Gets md_undefined_symbol above to do the register name
+ matching for us.
+
+ Only called as a part of processing the ECOFF .frame directive. */
+
+int
+tc_get_register (int frame ATTRIBUTE_UNUSED)
+{
+ int framereg = AXP_REG_SP;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ {
+ char *s = input_line_pointer;
+ char c = get_symbol_end ();
+ symbolS *sym = md_undefined_symbol (s);
+
+ *strchr (s, '\0') = c;
+ if (sym && (framereg = S_GET_VALUE (sym)) <= 31)
+ goto found;
+ }
+ as_warn (_("frame reg expected, using $%d."), framereg);
+
+found:
+ note_gpreg (framereg);
+ return framereg;
+}
+
+/* This is called before the symbol table is processed. In order to
+ work with gcc when using mips-tfile, we must keep all local labels.
+ However, in other cases, we want to discard them. If we were
+ called with -g, but we didn't see any debugging information, it may
+ mean that gcc is smuggling debugging information through to
+ mips-tfile, in which case we must generate all local labels. */
+
+#ifdef OBJ_ECOFF
+
+void
+alpha_frob_file_before_adjust (void)
+{
+ if (alpha_debug != 0
+ && ! ecoff_debugging_seen)
+ flag_keep_locals = 1;
+}
+
+#endif /* OBJ_ECOFF */
+
+/* The Alpha has support for some VAX floating point types, as well as for
+ IEEE floating point. We consider IEEE to be the primary floating point
+ format, and sneak in the VAX floating point support here. */
+#include "config/atof-vax.c"
diff --git a/gas/config/tc-alpha.h b/gas/config/tc-alpha.h
new file mode 100644
index 0000000..98f81f5
--- /dev/null
+++ b/gas/config/tc-alpha.h
@@ -0,0 +1,186 @@
+/* This file is tc-alpha.h
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Written by Ken Raeburn <raeburn@cygnus.com>.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_ALPHA
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define WORKING_DOT_WORD
+
+#define TARGET_ARCH bfd_arch_alpha
+
+#ifdef TE_FreeBSD
+#define ELF_TARGET_FORMAT "elf64-alpha-freebsd"
+#endif
+#ifndef ELF_TARGET_FORMAT
+#define ELF_TARGET_FORMAT "elf64-alpha"
+#endif
+
+#define TARGET_FORMAT (OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ ? "ecoff-littlealpha" \
+ : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ ? ELF_TARGET_FORMAT \
+ : OUTPUT_FLAVOR == bfd_target_evax_flavour \
+ ? "vms-alpha" \
+ : "unknown-format")
+
+#define NEED_LITERAL_POOL
+#define REPEAT_CONS_EXPRESSIONS
+
+struct fix;
+struct alpha_reloc_tag;
+
+extern int alpha_force_relocation (struct fix *);
+extern int alpha_fix_adjustable (struct fix *);
+
+extern unsigned long alpha_gprmask, alpha_fprmask;
+extern valueT alpha_gp_value;
+
+#define TC_FORCE_RELOCATION(FIX) alpha_force_relocation (FIX)
+#define tc_fix_adjustable(FIX) alpha_fix_adjustable (FIX)
+#define RELOC_REQUIRES_SYMBOL
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_convert_frag(b,s,f) as_fatal ("alpha convert_frag\n")
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal ("estimate_size_before_relax called"),1)
+#define md_operand(x)
+
+#ifdef OBJ_EVAX
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1
+
+#define tc_canonicalize_symbol_name evax_shorten_name
+
+#define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP,RELOC) \
+ (void) RELOC, \
+ fix_new_exp (FRAG, OFF, (int)LEN, EXP, 0, \
+ LEN == 2 ? BFD_RELOC_16 \
+ : LEN == 4 ? BFD_RELOC_32 \
+ : LEN == 8 ? BFD_RELOC_64 \
+ : BFD_RELOC_ALPHA_LINKAGE);
+#endif
+
+#ifdef OBJ_EVAX
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 3
+#else
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(size, align) \
+ do \
+ { \
+ align = 0; \
+ if (size > 1) \
+ { \
+ addressT temp = 1; \
+ while ((size & temp) == 0) \
+ ++align, temp <<= 1; \
+ } \
+ } \
+ while (0)
+#endif
+
+#define md_number_to_chars number_to_chars_littleendian
+
+extern int tc_get_register (int);
+extern void alpha_frob_ecoff_data (void);
+
+#define tc_frob_label(sym) alpha_define_label (sym)
+extern void alpha_define_label (symbolS *);
+
+#define md_cons_align(nbytes) alpha_cons_align (nbytes)
+extern void alpha_cons_align (int);
+
+#define HANDLE_ALIGN(fragp) alpha_handle_align (fragp)
+extern void alpha_handle_align (struct frag *);
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4 + 8)
+
+#ifdef OBJ_ECOFF
+#define tc_frob_file_before_adjust() alpha_frob_file_before_adjust ()
+extern void alpha_frob_file_before_adjust (void);
+#endif
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs. */
+
+#ifdef OBJ_ELF
+#define md_elf_section_letter alpha_elf_section_letter
+extern bfd_vma alpha_elf_section_letter (int, char **);
+#define md_elf_section_flags alpha_elf_section_flags
+extern flagword alpha_elf_section_flags (flagword, bfd_vma, int);
+#endif
+
+/* Whether to add support for explicit !relocation_op!sequence_number. At the
+ moment, only do this for ELF, though ECOFF could use it as well. */
+
+#ifdef OBJ_ELF
+#define RELOC_OP_P
+#endif
+
+#ifndef OBJ_EVAX
+/* Before the relocations are written, reorder them, so that user
+ supplied !lituse relocations follow the appropriate !literal
+ relocations. Also convert the gas-internal relocations to the
+ appropriate linker relocations. */
+#define tc_frob_file_before_fix() alpha_before_fix ()
+extern void alpha_before_fix (void);
+#endif
+
+#ifdef OBJ_ELF
+#define md_end alpha_elf_md_end
+extern void alpha_elf_md_end (void);
+#endif
+
+/* New fields for supporting explicit relocations (such as !literal to mark
+ where a pointer is loaded from the global table, and !lituse_base to track
+ all of the normal uses of that pointer). */
+
+#define TC_FIX_TYPE struct alpha_fix_tag
+
+struct alpha_fix_tag
+{
+ struct fix *next_reloc; /* Next !lituse or !gpdisp. */
+ struct alpha_reloc_tag *info; /* Other members with same sequence. */
+};
+
+/* Initialize the TC_FIX_TYPE field. */
+#define TC_INIT_FIX_DATA(FIX) \
+do { \
+ FIX->tc_fix_data.next_reloc = NULL; \
+ FIX->tc_fix_data.info = NULL; \
+} while (0)
+
+/* Work with DEBUG5 to print fields in tc_fix_type. */
+#define TC_FIX_DATA_PRINT(STREAM, FIX) \
+do { \
+ if (FIX->tc_fix_data.info) \
+ fprintf (STREAM, "\tinfo = 0x%lx, next_reloc = 0x%lx\n", \
+ (long) FIX->tc_fix_data.info, \
+ (long) FIX->tc_fix_data.next_reloc); \
+} while (0)
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions alpha_cfi_frame_initial_instructions
+extern void alpha_cfi_frame_initial_instructions (void);
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+#define DWARF2_DEFAULT_RETURN_COLUMN 26
+#define DWARF2_CIE_DATA_ALIGNMENT (-8)
diff --git a/gas/config/tc-arc.c b/gas/config/tc-arc.c
new file mode 100644
index 0000000..5499d88
--- /dev/null
+++ b/gas/config/tc-arc.c
@@ -0,0 +1,1897 @@
+/* tc-arc.c -- Assembler for the ARC
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Contributed by Doug Evans (dje@cygnus.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "struc-symbol.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/arc.h"
+#include "../opcodes/arc-ext.h"
+#include "elf/arc.h"
+#include "dwarf2dbg.h"
+
+const struct suffix_classes
+{
+ char *name;
+ int len;
+} suffixclass[] =
+{
+ { "SUFFIX_COND|SUFFIX_FLAG",23 },
+ { "SUFFIX_FLAG", 11 },
+ { "SUFFIX_COND", 11 },
+ { "SUFFIX_NONE", 11 }
+};
+
+#define MAXSUFFIXCLASS (sizeof (suffixclass) / sizeof (struct suffix_classes))
+
+const struct syntax_classes
+{
+ char *name;
+ int len;
+ int s_class;
+} syntaxclass[] =
+{
+ { "SYNTAX_3OP|OP1_MUST_BE_IMM", 26, SYNTAX_3OP|OP1_MUST_BE_IMM|SYNTAX_VALID },
+ { "OP1_MUST_BE_IMM|SYNTAX_3OP", 26, OP1_MUST_BE_IMM|SYNTAX_3OP|SYNTAX_VALID },
+ { "SYNTAX_2OP|OP1_IMM_IMPLIED", 26, SYNTAX_2OP|OP1_IMM_IMPLIED|SYNTAX_VALID },
+ { "OP1_IMM_IMPLIED|SYNTAX_2OP", 26, OP1_IMM_IMPLIED|SYNTAX_2OP|SYNTAX_VALID },
+ { "SYNTAX_3OP", 10, SYNTAX_3OP|SYNTAX_VALID },
+ { "SYNTAX_2OP", 10, SYNTAX_2OP|SYNTAX_VALID }
+};
+
+#define MAXSYNTAXCLASS (sizeof (syntaxclass) / sizeof (struct syntax_classes))
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "#;";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always
+ work if '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = "";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ As in 0f12.456 or 0d1.2345e12. */
+const char FLT_CHARS[] = "rRsSfFdD";
+
+/* Byte order. */
+extern int target_big_endian;
+const char *arc_target_format = DEFAULT_TARGET_FORMAT;
+static int byte_order = DEFAULT_BYTE_ORDER;
+
+static segT arcext_section;
+
+/* One of bfd_mach_arc_n. */
+static int arc_mach_type = bfd_mach_arc_6;
+
+/* Non-zero if the cpu type has been explicitly specified. */
+static int mach_type_specified_p = 0;
+
+/* Non-zero if opcode tables have been initialized.
+ A .option command must appear before any instructions. */
+static int cpu_tables_init_p = 0;
+
+static struct hash_control *arc_suffix_hash = NULL;
+
+const char *md_shortopts = "";
+
+enum options
+{
+ OPTION_EB = OPTION_MD_BASE,
+ OPTION_EL,
+ OPTION_ARC5,
+ OPTION_ARC6,
+ OPTION_ARC7,
+ OPTION_ARC8,
+ OPTION_ARC
+};
+
+struct option md_longopts[] =
+{
+ { "EB", no_argument, NULL, OPTION_EB },
+ { "EL", no_argument, NULL, OPTION_EL },
+ { "marc5", no_argument, NULL, OPTION_ARC5 },
+ { "pre-v6", no_argument, NULL, OPTION_ARC5 },
+ { "marc6", no_argument, NULL, OPTION_ARC6 },
+ { "marc7", no_argument, NULL, OPTION_ARC7 },
+ { "marc8", no_argument, NULL, OPTION_ARC8 },
+ { "marc", no_argument, NULL, OPTION_ARC },
+ { NULL, no_argument, NULL, 0 }
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+#define IS_SYMBOL_OPERAND(o) \
+ ((o) == 'b' || (o) == 'c' || (o) == 's' || (o) == 'o' || (o) == 'O')
+
+struct arc_operand_value *get_ext_suffix (char *s);
+
+/* Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option. */
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_ARC5:
+ arc_mach_type = bfd_mach_arc_5;
+ break;
+ case OPTION_ARC:
+ case OPTION_ARC6:
+ arc_mach_type = bfd_mach_arc_6;
+ break;
+ case OPTION_ARC7:
+ arc_mach_type = bfd_mach_arc_7;
+ break;
+ case OPTION_ARC8:
+ arc_mach_type = bfd_mach_arc_8;
+ break;
+ case OPTION_EB:
+ byte_order = BIG_ENDIAN;
+ arc_target_format = "elf32-bigarc";
+ break;
+ case OPTION_EL:
+ byte_order = LITTLE_ENDIAN;
+ arc_target_format = "elf32-littlearc";
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, "\
+ARC Options:\n\
+ -marc[5|6|7|8] select processor variant (default arc%d)\n\
+ -EB assemble code for a big endian cpu\n\
+ -EL assemble code for a little endian cpu\n", arc_mach_type + 5);
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need.
+ Opcode selection is deferred until later because we might see a .option
+ command. */
+
+void
+md_begin (void)
+{
+ /* The endianness can be chosen "at the factory". */
+ target_big_endian = byte_order == BIG_ENDIAN;
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, arc_mach_type))
+ as_warn (_("could not set architecture and machine"));
+
+ /* This call is necessary because we need to initialize `arc_operand_map'
+ which may be needed before we see the first insn. */
+ arc_opcode_init_tables (arc_get_opcode_mach (arc_mach_type,
+ target_big_endian));
+}
+
+/* Initialize the various opcode and operand tables.
+ MACH is one of bfd_mach_arc_xxx. */
+
+static void
+init_opcode_tables (int mach)
+{
+ int i;
+ char *last;
+
+ if ((arc_suffix_hash = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+ as_warn (_("could not set architecture and machine"));
+
+ /* This initializes a few things in arc-opc.c that we need.
+ This must be called before the various arc_xxx_supported fns. */
+ arc_opcode_init_tables (arc_get_opcode_mach (mach, target_big_endian));
+
+ /* Only put the first entry of each equivalently named suffix in the
+ table. */
+ last = "";
+ for (i = 0; i < arc_suffixes_count; i++)
+ {
+ if (strcmp (arc_suffixes[i].name, last) != 0)
+ hash_insert (arc_suffix_hash, arc_suffixes[i].name, (void *) (arc_suffixes + i));
+ last = arc_suffixes[i].name;
+ }
+
+ /* Since registers don't have a prefix, we put them in the symbol table so
+ they can't be used as symbols. This also simplifies argument parsing as
+ we can let gas parse registers for us. The recorded register number is
+ the address of the register's entry in arc_reg_names.
+
+ If the register name is already in the table, then the existing
+ definition is assumed to be from an .ExtCoreRegister pseudo-op. */
+
+ for (i = 0; i < arc_reg_names_count; i++)
+ {
+ if (symbol_find (arc_reg_names[i].name))
+ continue;
+ /* Use symbol_create here instead of symbol_new so we don't try to
+ output registers into the object file's symbol table. */
+ symbol_table_insert (symbol_create (arc_reg_names[i].name,
+ reg_section,
+ (valueT) &arc_reg_names[i],
+ &zero_address_frag));
+ }
+
+ /* Tell `.option' it's too late. */
+ cpu_tables_init_p = 1;
+}
+
+/* Insert an operand value into an instruction.
+ If REG is non-NULL, it is a register number and ignore VAL. */
+
+static arc_insn
+arc_insert_operand (arc_insn insn,
+ const struct arc_operand *operand,
+ int mods,
+ const struct arc_operand_value *reg,
+ offsetT val,
+ char *file,
+ unsigned int line)
+{
+ if (operand->bits != 32)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & ARC_OPERAND_SIGNED) != 0)
+ {
+ if ((operand->flags & ARC_OPERAND_SIGNOPT) != 0)
+ max = (1 << operand->bits) - 1;
+ else
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ if ((operand->flags & ARC_OPERAND_NEGATIVE) != 0)
+ test = - val;
+ else
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg;
+
+ errmsg = NULL;
+ insn = (*operand->insert) (insn, operand, mods, reg, (long) val, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_warn ("%s", errmsg);
+ }
+ else
+ insn |= (((long) val & ((1 << operand->bits) - 1))
+ << operand->shift);
+
+ return insn;
+}
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct arc_fixup
+{
+ /* index into `arc_operands' */
+ int opindex;
+ expressionS exp;
+};
+
+#define MAX_FIXUPS 5
+
+#define MAX_SUFFIXES 5
+
+/* Compute the reloc type of an expression.
+ The possibly modified expression is stored in EXPNEW.
+
+ This is used to convert the expressions generated by the %-op's into
+ the appropriate operand type. It is called for both data in instructions
+ (operands) and data outside instructions (variables, debugging info, etc.).
+
+ Currently supported %-ops:
+
+ %st(symbol): represented as "symbol >> 2"
+ "st" is short for STatus as in the status register (pc)
+
+ DEFAULT_TYPE is the type to use if no special processing is required.
+
+ DATA_P is non-zero for data or limm values, zero for insn operands.
+ Remember that the opcode "insertion fns" cannot be used on data, they're
+ only for inserting operands into insns. They also can't be used for limm
+ values as the insertion routines don't handle limm values. When called for
+ insns we return fudged reloc types (real_value - BFD_RELOC_UNUSED). When
+ called for data or limm values we use real reloc types. */
+
+static int
+get_arc_exp_reloc_type (int data_p,
+ int default_type,
+ expressionS *exp,
+ expressionS *expnew)
+{
+ /* If the expression is "symbol >> 2" we must change it to just "symbol",
+ as fix_new_exp can't handle it. Similarly for (symbol - symbol) >> 2.
+ That's ok though. What's really going on here is that we're using
+ ">> 2" as a special syntax for specifying BFD_RELOC_ARC_B26. */
+
+ if (exp->X_op == O_right_shift
+ && exp->X_op_symbol != NULL
+ && exp->X_op_symbol->sy_value.X_op == O_constant
+ && exp->X_op_symbol->sy_value.X_add_number == 2
+ && exp->X_add_number == 0)
+ {
+ if (exp->X_add_symbol != NULL
+ && (exp->X_add_symbol->sy_value.X_op == O_constant
+ || exp->X_add_symbol->sy_value.X_op == O_symbol))
+ {
+ *expnew = *exp;
+ expnew->X_op = O_symbol;
+ expnew->X_op_symbol = NULL;
+ return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
+ }
+ else if (exp->X_add_symbol != NULL
+ && exp->X_add_symbol->sy_value.X_op == O_subtract)
+ {
+ *expnew = exp->X_add_symbol->sy_value;
+ return data_p ? BFD_RELOC_ARC_B26 : arc_operand_map['J'];
+ }
+ }
+
+ *expnew = *exp;
+ return default_type;
+}
+
+static int
+arc_set_ext_seg (void)
+{
+ if (!arcext_section)
+ {
+ arcext_section = subseg_new (".arcextmap", 0);
+ bfd_set_section_flags (stdoutput, arcext_section,
+ SEC_READONLY | SEC_HAS_CONTENTS);
+ }
+ else
+ subseg_set (arcext_section, 0);
+ return 1;
+}
+
+static void
+arc_extoper (int opertype)
+{
+ char *name;
+ char *mode;
+ char c;
+ char *p;
+ int imode = 0;
+ int number;
+ struct arc_ext_operand_value *ext_oper;
+ symbolS *symbolP;
+
+ segT old_sec;
+ int old_subsec;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ name = xstrdup (name);
+
+ p = name;
+ while (*p)
+ {
+ *p = TOLOWER (*p);
+ p++;
+ }
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after operand name"));
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ number = get_absolute_expression ();
+
+ if (number < 0)
+ {
+ as_bad (_("negative operand number %d"), number);
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+
+ if (opertype)
+ {
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after register-number"));
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ mode = input_line_pointer;
+
+ if (!strncmp (mode, "r|w", 3))
+ {
+ imode = 0;
+ input_line_pointer += 3;
+ }
+ else
+ {
+ if (!strncmp (mode, "r", 1))
+ {
+ imode = ARC_REGISTER_READONLY;
+ input_line_pointer += 1;
+ }
+ else
+ {
+ if (strncmp (mode, "w", 1))
+ {
+ as_bad (_("invalid mode"));
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+ else
+ {
+ imode = ARC_REGISTER_WRITEONLY;
+ input_line_pointer += 1;
+ }
+ }
+ }
+ SKIP_WHITESPACE ();
+ if (1 == opertype)
+ {
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after register-mode"));
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+
+ if (!strncmp (input_line_pointer, "cannot_shortcut", 15))
+ {
+ imode |= arc_get_noshortcut_flag ();
+ input_line_pointer += 15;
+ }
+ else
+ {
+ if (strncmp (input_line_pointer, "can_shortcut", 12))
+ {
+ as_bad (_("shortcut designator invalid"));
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+ else
+ {
+ input_line_pointer += 12;
+ }
+ }
+ }
+ }
+
+ if ((opertype == 1) && number > 60)
+ {
+ as_bad (_("core register value (%d) too large"), number);
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+
+ if ((opertype == 0) && number > 31)
+ {
+ as_bad (_("condition code value (%d) too large"), number);
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ }
+
+ ext_oper = (struct arc_ext_operand_value *)
+ xmalloc (sizeof (struct arc_ext_operand_value));
+
+ if (opertype)
+ {
+ /* If the symbol already exists, point it at the new definition. */
+ if ((symbolP = symbol_find (name)))
+ {
+ if (S_GET_SEGMENT (symbolP) == reg_section)
+ S_SET_VALUE (symbolP, (valueT) &ext_oper->operand);
+ else
+ {
+ as_bad (_("attempt to override symbol: %s"), name);
+ ignore_rest_of_line ();
+ free (name);
+ free (ext_oper);
+ return;
+ }
+ }
+ else
+ {
+ /* If its not there, add it. */
+ symbol_table_insert (symbol_create (name, reg_section,
+ (valueT) &ext_oper->operand,
+ &zero_address_frag));
+ }
+ }
+
+ ext_oper->operand.name = name;
+ ext_oper->operand.value = number;
+ ext_oper->operand.type = arc_operand_type (opertype);
+ ext_oper->operand.flags = imode;
+
+ ext_oper->next = arc_ext_operands;
+ arc_ext_operands = ext_oper;
+
+ /* OK, now that we know what this operand is, put a description in
+ the arc extension section of the output file. */
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ arc_set_ext_seg ();
+
+ switch (opertype)
+ {
+ case 0:
+ p = frag_more (1);
+ *p = 3 + strlen (name) + 1;
+ p = frag_more (1);
+ *p = EXT_COND_CODE;
+ p = frag_more (1);
+ *p = number;
+ p = frag_more (strlen (name) + 1);
+ strcpy (p, name);
+ break;
+ case 1:
+ p = frag_more (1);
+ *p = 3 + strlen (name) + 1;
+ p = frag_more (1);
+ *p = EXT_CORE_REGISTER;
+ p = frag_more (1);
+ *p = number;
+ p = frag_more (strlen (name) + 1);
+ strcpy (p, name);
+ break;
+ case 2:
+ p = frag_more (1);
+ *p = 6 + strlen (name) + 1;
+ p = frag_more (1);
+ *p = EXT_AUX_REGISTER;
+ p = frag_more (1);
+ *p = number >> 24 & 0xff;
+ p = frag_more (1);
+ *p = number >> 16 & 0xff;
+ p = frag_more (1);
+ *p = number >> 8 & 0xff;
+ p = frag_more (1);
+ *p = number & 0xff;
+ p = frag_more (strlen (name) + 1);
+ strcpy (p, name);
+ break;
+ default:
+ as_bad (_("invalid opertype"));
+ ignore_rest_of_line ();
+ free (name);
+ return;
+ break;
+ }
+
+ subseg_set (old_sec, old_subsec);
+
+ /* Enter all registers into the symbol table. */
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+arc_extinst (int ignore ATTRIBUTE_UNUSED)
+{
+ char syntax[129];
+ char *name;
+ char *p;
+ char c;
+ int suffixcode = -1;
+ int opcode, subopcode;
+ int i;
+ int s_class = 0;
+ int name_len;
+ struct arc_opcode *ext_op;
+
+ segT old_sec;
+ int old_subsec;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ name = xstrdup (name);
+ strcpy (syntax, name);
+ name_len = strlen (name);
+
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after operand name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ opcode = get_absolute_expression ();
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after opcode"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ subopcode = get_absolute_expression ();
+
+ if (subopcode < 0)
+ {
+ as_bad (_("negative subopcode %d"), subopcode);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (subopcode)
+ {
+ if (3 != opcode)
+ {
+ as_bad (_("subcode value found when opcode not equal 0x03"));
+ ignore_rest_of_line ();
+ return;
+ }
+ else
+ {
+ if (subopcode < 0x09 || subopcode == 0x3f)
+ {
+ as_bad (_("invalid subopcode %d"), subopcode);
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after subopcode"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+
+ for (i = 0; i < (int) MAXSUFFIXCLASS; i++)
+ {
+ if (!strncmp (suffixclass[i].name,input_line_pointer, suffixclass[i].len))
+ {
+ suffixcode = i;
+ input_line_pointer += suffixclass[i].len;
+ break;
+ }
+ }
+
+ if (-1 == suffixcode)
+ {
+ as_bad (_("invalid suffix class"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after suffix class"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+
+ for (i = 0; i < (int) MAXSYNTAXCLASS; i++)
+ {
+ if (!strncmp (syntaxclass[i].name,input_line_pointer, syntaxclass[i].len))
+ {
+ s_class = syntaxclass[i].s_class;
+ input_line_pointer += syntaxclass[i].len;
+ break;
+ }
+ }
+
+ if (0 == (SYNTAX_VALID & s_class))
+ {
+ as_bad (_("invalid syntax class"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if ((0x3 == opcode) & (s_class & SYNTAX_3OP))
+ {
+ as_bad (_("opcode 0x3 and SYNTAX_3OP invalid"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ switch (suffixcode)
+ {
+ case 0:
+ strcat (syntax, "%.q%.f ");
+ break;
+ case 1:
+ strcat (syntax, "%.f ");
+ break;
+ case 2:
+ strcat (syntax, "%.q ");
+ break;
+ case 3:
+ strcat (syntax, " ");
+ break;
+ default:
+ as_bad (_("unknown suffix class"));
+ ignore_rest_of_line ();
+ return;
+ break;
+ };
+
+ strcat (syntax, ((opcode == 0x3) ? "%a,%b" : ((s_class & SYNTAX_3OP) ? "%a,%b,%c" : "%b,%c")));
+ if (suffixcode < 2)
+ strcat (syntax, "%F");
+ strcat (syntax, "%S%L");
+
+ ext_op = (struct arc_opcode *) xmalloc (sizeof (struct arc_opcode));
+ ext_op->syntax = xstrdup (syntax);
+
+ ext_op->mask = I (-1) | ((0x3 == opcode) ? C (-1) : 0);
+ ext_op->value = I (opcode) | ((0x3 == opcode) ? C (subopcode) : 0);
+ ext_op->flags = s_class;
+ ext_op->next_asm = arc_ext_opcodes;
+ ext_op->next_dis = arc_ext_opcodes;
+ arc_ext_opcodes = ext_op;
+
+ /* OK, now that we know what this inst is, put a description in the
+ arc extension section of the output file. */
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ arc_set_ext_seg ();
+
+ p = frag_more (1);
+ *p = 5 + name_len + 1;
+ p = frag_more (1);
+ *p = EXT_INSTRUCTION;
+ p = frag_more (1);
+ *p = opcode;
+ p = frag_more (1);
+ *p = subopcode;
+ p = frag_more (1);
+ *p = (s_class & (OP1_MUST_BE_IMM | OP1_IMM_IMPLIED) ? IGNORE_FIRST_OPD : 0);
+ p = frag_more (name_len);
+ strncpy (p, syntax, name_len);
+ p = frag_more (1);
+ *p = '\0';
+
+ subseg_set (old_sec, old_subsec);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+arc_common (int localScope)
+{
+ char *name;
+ char c;
+ char *p;
+ int align, size;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* just after name is now '\0' */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ size = get_absolute_expression ();
+
+ if (size < 0)
+ {
+ as_bad (_("negative symbol length"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (((int) S_GET_VALUE (symbolP) != 0) \
+ && ((int) S_GET_VALUE (symbolP) != size))
+ {
+ as_warn (_("length of symbol \"%s\" already %ld, ignoring %d"),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+ }
+ gas_assert (symbolP->sy_frag == &zero_address_frag);
+
+ /* Now parse the alignment field. This field is optional for
+ local and global symbols. Default alignment is zero. */
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ align = get_absolute_expression ();
+ if (align < 0)
+ {
+ align = 0;
+ as_warn (_("assuming symbol alignment of zero"));
+ }
+ }
+ else
+ align = 0;
+
+ if (localScope != 0)
+ {
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ record_alignment (bss_section, align);
+ subseg_set (bss_section, 0); /* ??? subseg_set (bss_section, 1); ??? */
+
+ if (align)
+ /* Do alignment. */
+ frag_align (align, 0, 0);
+
+ /* Detach from old frag. */
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = NULL;
+
+ symbolP->sy_frag = frag_now;
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *pfrag = 0;
+
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, bss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ symbol_get_obj (symbolP)->local = 1;
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, align);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+
+ symbolP->bsym->flags |= BSF_OBJECT;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Select the cpu we're assembling for. */
+
+static void
+arc_option (int ignore ATTRIBUTE_UNUSED)
+{
+ extern int arc_get_mach (char *);
+ int mach;
+ char c;
+ char *cpu;
+
+ cpu = input_line_pointer;
+ c = get_symbol_end ();
+ mach = arc_get_mach (cpu);
+ *input_line_pointer = c;
+
+ /* If an instruction has already been seen, it's too late. */
+ if (cpu_tables_init_p)
+ {
+ as_bad (_("\".option\" directive must appear before any instructions"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (mach == -1)
+ goto bad_cpu;
+
+ if (mach_type_specified_p && mach != arc_mach_type)
+ {
+ as_bad (_("\".option\" directive conflicts with initial definition"));
+ ignore_rest_of_line ();
+ return;
+ }
+ else
+ {
+ /* The cpu may have been selected on the command line. */
+ if (mach != arc_mach_type)
+ as_warn (_("\".option\" directive overrides command-line (default) value"));
+ arc_mach_type = mach;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_arc, mach))
+ as_fatal (_("could not set architecture and machine"));
+ mach_type_specified_p = 1;
+ }
+ demand_empty_rest_of_line ();
+ return;
+
+ bad_cpu:
+ as_bad (_("invalid identifier for \".option\""));
+ ignore_rest_of_line ();
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("relaxation not supported\n"));
+ return 1;
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+static void
+arc_code_symbol (expressionS *expressionP)
+{
+ if (expressionP->X_op == O_symbol && expressionP->X_add_number == 0)
+ {
+ expressionS two;
+
+ expressionP->X_op = O_right_shift;
+ expressionP->X_add_symbol->sy_value.X_op = O_constant;
+ two.X_op = O_constant;
+ two.X_add_symbol = two.X_op_symbol = NULL;
+ two.X_add_number = 2;
+ expressionP->X_op_symbol = make_expr_symbol (&two);
+ }
+ /* Allow %st(sym1-sym2) */
+ else if (expressionP->X_op == O_subtract
+ && expressionP->X_add_symbol != NULL
+ && expressionP->X_op_symbol != NULL
+ && expressionP->X_add_number == 0)
+ {
+ expressionS two;
+
+ expressionP->X_add_symbol = make_expr_symbol (expressionP);
+ expressionP->X_op = O_right_shift;
+ two.X_op = O_constant;
+ two.X_add_symbol = two.X_op_symbol = NULL;
+ two.X_add_number = 2;
+ expressionP->X_op_symbol = make_expr_symbol (&two);
+ }
+ else
+ as_bad (_("expression too complex code symbol"));
+}
+
+/* Parse an operand that is machine-specific.
+
+ The ARC has a special %-op to adjust addresses so they're usable in
+ branches. The "st" is short for the STatus register.
+ ??? Later expand this to take a flags value too.
+
+ ??? We can't create new expression types so we map the %-op's onto the
+ existing syntax. This means that the user could use the chosen syntax
+ to achieve the same effect. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ char *p = input_line_pointer;
+
+ if (*p != '%')
+ return;
+
+ if (strncmp (p, "%st(", 4) == 0)
+ {
+ input_line_pointer += 4;
+ expression (expressionP);
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("missing ')' in %%-op"));
+ return;
+ }
+ ++input_line_pointer;
+ arc_code_symbol (expressionP);
+ }
+ else
+ {
+ /* It could be a register. */
+ int i, l;
+ struct arc_ext_operand_value *ext_oper = arc_ext_operands;
+ p++;
+
+ while (ext_oper)
+ {
+ l = strlen (ext_oper->operand.name);
+ if (!strncmp (p, ext_oper->operand.name, l) && !ISALNUM (*(p + l)))
+ {
+ input_line_pointer += l + 1;
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = (offsetT) &ext_oper->operand;
+ return;
+ }
+ ext_oper = ext_oper->next;
+ }
+ for (i = 0; i < arc_reg_names_count; i++)
+ {
+ l = strlen (arc_reg_names[i].name);
+ if (!strncmp (p, arc_reg_names[i].name, l) && !ISALNUM (*(p + l)))
+ {
+ input_line_pointer += l + 1;
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = (offsetT) &arc_reg_names[i];
+ break;
+ }
+ }
+ }
+}
+
+/* We have no need to default values of symbols.
+ We could catch register names here, but that is handled by inserting
+ them all in the symbol table to begin with. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Functions concerning expressions. */
+
+/* Parse a .byte, .word, etc. expression.
+
+ Values for the status register are specified with %st(label).
+ `label' will be right shifted by 2. */
+
+bfd_reloc_code_real_type
+arc_parse_cons_expression (expressionS *exp,
+ unsigned int nbytes ATTRIBUTE_UNUSED)
+{
+ char *p = input_line_pointer;
+ int code_symbol_fix = 0;
+
+ for (; ! is_end_of_line[(unsigned char) *p]; p++)
+ if (*p == '@' && !strncmp (p, "@h30", 4))
+ {
+ code_symbol_fix = 1;
+ strcpy (p, "; ");
+ }
+ expression_and_evaluate (exp);
+ if (code_symbol_fix)
+ {
+ arc_code_symbol (exp);
+ input_line_pointer = p;
+ }
+ return BFD_RELOC_NONE;
+}
+
+/* Record a fixup for a cons expression. */
+
+void
+arc_cons_fix_new (fragS *frag,
+ int where,
+ int nbytes,
+ expressionS *exp,
+ bfd_reloc_code_real_type r ATTRIBUTE_UNUSED)
+{
+ if (nbytes == 4)
+ {
+ int reloc_type;
+ expressionS exptmp;
+
+ /* This may be a special ARC reloc (eg: %st()). */
+ reloc_type = get_arc_exp_reloc_type (1, BFD_RELOC_32, exp, &exptmp);
+ fix_new_exp (frag, where, nbytes, &exptmp, 0,
+ (enum bfd_reloc_code_real) reloc_type);
+ }
+ else
+ {
+ fix_new_exp (frag, where, nbytes, exp, 0,
+ nbytes == 2 ? BFD_RELOC_16
+ : nbytes == 8 ? BFD_RELOC_64
+ : BFD_RELOC_32);
+ }
+}
+
+/* Functions concerning relocs. */
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ /* Return the address of the delay slot. */
+ return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the call to fix_new_exp, above. In the call
+ above we used a reloc code which was the largest legal reloc code
+ plus the operand index. Here we undo that to recover the operand
+ index. At this point all symbol values should be fully resolved,
+ and we attempt to completely resolve the reloc. If we can not do
+ that, we determine the correct reloc code and put it back in the fixup. */
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg)
+{
+ valueT value = * valP;
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ else if (fixP->fx_pcrel)
+ {
+ /* Hack around bfd_install_relocation brain damage. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ value += md_pcrel_from (fixP);
+ }
+
+ /* We can't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex;
+ const struct arc_operand *operand;
+ char *where;
+ arc_insn insn;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+
+ operand = &arc_operands[opindex];
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ insn = bfd_getb32 ((unsigned char *) where);
+ else
+ insn = bfd_getl32 ((unsigned char *) where);
+ insn = arc_insert_operand (insn, operand, -1, NULL, (offsetT) value,
+ fixP->fx_file, fixP->fx_line);
+ if (target_big_endian)
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+
+ if (fixP->fx_done)
+ /* Nothing else to do here. */
+ return;
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into relocs.
+ !!! Note that we can't handle limm values here. Since we're using
+ implicit addends the addend must be inserted into the instruction,
+ however, the opcode insertion routines currently do nothing with
+ limm values. */
+ if (operand->fmt == 'B')
+ {
+ gas_assert ((operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0
+ && operand->bits == 20
+ && operand->shift == 7);
+ fixP->fx_r_type = BFD_RELOC_ARC_B22_PCREL;
+ }
+ else if (operand->fmt == 'J')
+ {
+ gas_assert ((operand->flags & ARC_OPERAND_ABSOLUTE_BRANCH) != 0
+ && operand->bits == 24
+ && operand->shift == 32);
+ fixP->fx_r_type = BFD_RELOC_ARC_B26;
+ }
+ else if (operand->fmt == 'L')
+ {
+ gas_assert ((operand->flags & ARC_OPERAND_LIMM) != 0
+ && operand->bits == 32
+ && operand->shift == 32);
+ fixP->fx_r_type = BFD_RELOC_32;
+ }
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ fixP->fx_done = 1;
+ return;
+ }
+ }
+ else
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 1);
+ break;
+ case BFD_RELOC_16:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 2);
+ break;
+ case BFD_RELOC_32:
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 4);
+ break;
+ case BFD_RELOC_ARC_B26:
+ /* If !fixP->fx_done then `value' is an implicit addend.
+ We must shift it right by 2 in this case as well because the
+ linker performs the relocation and then adds this in (as opposed
+ to adding this in and then shifting right by 2). */
+ value >>= 2;
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 4);
+ break;
+ default:
+ abort ();
+ }
+ }
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixP)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ /* Set addend to account for PC being advanced one insn before the
+ target address is computed. */
+
+ reloc->addend = (fixP->fx_pcrel ? -4 : 0);
+
+ return reloc;
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0). */
+ { "comm", arc_common, 0 },
+ { "common", arc_common, 0 },
+ { "lcomm", arc_common, 1 },
+ { "lcommon", arc_common, 1 },
+ { "2byte", cons, 2 },
+ { "half", cons, 2 },
+ { "short", cons, 2 },
+ { "3byte", cons, 3 },
+ { "4byte", cons, 4 },
+ { "word", cons, 4 },
+ { "option", arc_option, 0 },
+ { "cpu", arc_option, 0 },
+ { "block", s_space, 0 },
+ { "extcondcode", arc_extoper, 0 },
+ { "extcoreregister", arc_extoper, 1 },
+ { "extauxregister", arc_extoper, 2 },
+ { "extinstruction", arc_extinst, 0 },
+ { NULL, 0, 0 },
+};
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (char *str)
+{
+ const struct arc_opcode *opcode;
+ const struct arc_opcode *std_opcode;
+ struct arc_opcode *ext_opcode;
+ char *start;
+ const char *last_errmsg = 0;
+ arc_insn insn;
+ static int init_tables_p = 0;
+
+ /* Opcode table initialization is deferred until here because we have to
+ wait for a possible .option command. */
+ if (!init_tables_p)
+ {
+ init_opcode_tables (arc_mach_type);
+ init_tables_p = 1;
+ }
+
+ /* Skip leading white space. */
+ while (ISSPACE (*str))
+ str++;
+
+ /* The instructions are stored in lists hashed by the first letter (though
+ we needn't care how they're hashed). Get the first in the list. */
+
+ ext_opcode = arc_ext_opcodes;
+ std_opcode = arc_opcode_lookup_asm (str);
+
+ /* Keep looking until we find a match. */
+ start = str;
+ for (opcode = (ext_opcode ? ext_opcode : std_opcode);
+ opcode != NULL;
+ opcode = (ARC_OPCODE_NEXT_ASM (opcode)
+ ? ARC_OPCODE_NEXT_ASM (opcode)
+ : (ext_opcode ? ext_opcode = NULL, std_opcode : NULL)))
+ {
+ int past_opcode_p, fc, num_suffixes;
+ int fix_up_at = 0;
+ char *syn;
+ struct arc_fixup fixups[MAX_FIXUPS];
+ /* Used as a sanity check. If we need a limm reloc, make sure we ask
+ for an extra 4 bytes from frag_more. */
+ int limm_reloc_p;
+ int ext_suffix_p;
+ const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
+
+ /* Is this opcode supported by the selected cpu? */
+ if (! arc_opcode_supported (opcode))
+ continue;
+
+ /* Scan the syntax string. If it doesn't match, try the next one. */
+ arc_opcode_init_insert ();
+ insn = opcode->value;
+ fc = 0;
+ past_opcode_p = 0;
+ num_suffixes = 0;
+ limm_reloc_p = 0;
+ ext_suffix_p = 0;
+
+ /* We don't check for (*str != '\0') here because we want to parse
+ any trailing fake arguments in the syntax string. */
+ for (str = start, syn = opcode->syntax; *syn != '\0';)
+ {
+ int mods;
+ const struct arc_operand *operand;
+
+ /* Non operand chars must match exactly. */
+ if (*syn != '%' || *++syn == '%')
+ {
+ if (*str == *syn)
+ {
+ if (*syn == ' ')
+ past_opcode_p = 1;
+ ++syn;
+ ++str;
+ }
+ else
+ break;
+ continue;
+ }
+
+ /* We have an operand. Pick out any modifiers. */
+ mods = 0;
+ while (ARC_MOD_P (arc_operands[arc_operand_map[(int) *syn]].flags))
+ {
+ mods |= arc_operands[arc_operand_map[(int) *syn]].flags & ARC_MOD_BITS;
+ ++syn;
+ }
+ operand = arc_operands + arc_operand_map[(int) *syn];
+ if (operand->fmt == 0)
+ as_fatal (_("unknown syntax format character `%c'"), *syn);
+
+ if (operand->flags & ARC_OPERAND_FAKE)
+ {
+ const char *errmsg = NULL;
+ if (operand->insert)
+ {
+ insn = (*operand->insert) (insn, operand, mods, NULL, 0, &errmsg);
+ if (errmsg != (const char *) NULL)
+ {
+ last_errmsg = errmsg;
+ if (operand->flags & ARC_OPERAND_ERROR)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+ else if (operand->flags & ARC_OPERAND_WARN)
+ as_warn ("%s", errmsg);
+ break;
+ }
+ if (limm_reloc_p
+ && (operand->flags && operand->flags & ARC_OPERAND_LIMM)
+ && (operand->flags &
+ (ARC_OPERAND_ABSOLUTE_BRANCH | ARC_OPERAND_ADDRESS)))
+ {
+ fixups[fix_up_at].opindex = arc_operand_map[operand->fmt];
+ }
+ }
+ ++syn;
+ }
+ /* Are we finished with suffixes? */
+ else if (!past_opcode_p)
+ {
+ int found;
+ char c;
+ char *s, *t;
+ const struct arc_operand_value *suf, *suffix_end;
+ const struct arc_operand_value *suffix = NULL;
+
+ if (!(operand->flags & ARC_OPERAND_SUFFIX))
+ abort ();
+
+ /* If we're at a space in the input string, we want to skip the
+ remaining suffixes. There may be some fake ones though, so
+ just go on to try the next one. */
+ if (*str == ' ')
+ {
+ ++syn;
+ continue;
+ }
+
+ s = str;
+ if (mods & ARC_MOD_DOT)
+ {
+ if (*s != '.')
+ break;
+ ++s;
+ }
+ else
+ {
+ /* This can happen in "b.nd foo" and we're currently looking
+ for "%q" (ie: a condition code suffix). */
+ if (*s == '.')
+ {
+ ++syn;
+ continue;
+ }
+ }
+
+ /* Pick the suffix out and look it up via the hash table. */
+ for (t = s; *t && ISALNUM (*t); ++t)
+ continue;
+ c = *t;
+ *t = '\0';
+ if ((suf = get_ext_suffix (s)))
+ ext_suffix_p = 1;
+ else
+ suf = (const struct arc_operand_value *)
+ hash_find (arc_suffix_hash, s);
+ if (!suf)
+ {
+ /* This can happen in "blle foo" and we're currently using
+ the template "b%q%.n %j". The "bl" insn occurs later in
+ the table so "lle" isn't an illegal suffix. */
+ *t = c;
+ break;
+ }
+
+ /* Is it the right type? Note that the same character is used
+ several times, so we have to examine all of them. This is
+ relatively efficient as equivalent entries are kept
+ together. If it's not the right type, don't increment `str'
+ so we try the next one in the series. */
+ found = 0;
+ if (ext_suffix_p && arc_operands[suf->type].fmt == *syn)
+ {
+ /* Insert the suffix's value into the insn. */
+ *t = c;
+ if (operand->insert)
+ insn = (*operand->insert) (insn, operand,
+ mods, NULL, suf->value,
+ NULL);
+ else
+ insn |= suf->value << operand->shift;
+ suffix = suf;
+ str = t;
+ found = 1;
+ }
+ else
+ {
+ *t = c;
+ suffix_end = arc_suffixes + arc_suffixes_count;
+ for (suffix = suf;
+ suffix < suffix_end && strcmp (suffix->name, suf->name) == 0;
+ ++suffix)
+ {
+ if (arc_operands[suffix->type].fmt == *syn)
+ {
+ /* Insert the suffix's value into the insn. */
+ if (operand->insert)
+ insn = (*operand->insert) (insn, operand,
+ mods, NULL, suffix->value,
+ NULL);
+ else
+ insn |= suffix->value << operand->shift;
+
+ str = t;
+ found = 1;
+ break;
+ }
+ }
+ }
+ ++syn;
+ if (!found)
+ /* Wrong type. Just go on to try next insn entry. */
+ ;
+ else
+ {
+ if (num_suffixes == MAX_SUFFIXES)
+ as_bad (_("too many suffixes"));
+ else
+ insn_suffixes[num_suffixes++] = suffix;
+ }
+ }
+ else
+ /* This is either a register or an expression of some kind. */
+ {
+ char *hold;
+ const struct arc_operand_value *reg = NULL;
+ long value = 0;
+ expressionS exp;
+
+ if (operand->flags & ARC_OPERAND_SUFFIX)
+ abort ();
+
+ /* Is there anything left to parse?
+ We don't check for this at the top because we want to parse
+ any trailing fake arguments in the syntax string. */
+ if (is_end_of_line[(unsigned char) *str])
+ break;
+
+ /* Parse the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+ expression (&exp);
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (exp.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (exp.X_op == O_absent)
+ as_bad (_("missing operand"));
+ else if (exp.X_op == O_constant)
+ value = exp.X_add_number;
+ else if (exp.X_op == O_register)
+ reg = (struct arc_operand_value *) exp.X_add_number;
+#define IS_REG_DEST_OPERAND(o) ((o) == 'a')
+ else if (IS_REG_DEST_OPERAND (*syn))
+ as_bad (_("symbol as destination register"));
+ else
+ {
+ if (!strncmp (str, "@h30", 4))
+ {
+ arc_code_symbol (&exp);
+ str += 4;
+ }
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = exp;
+ /* We don't support shimm relocs. break here to force
+ the assembler to output a limm. */
+#define IS_REG_SHIMM_OFFSET(o) ((o) == 'd')
+ if (IS_REG_SHIMM_OFFSET (*syn))
+ break;
+ /* If this is a register constant (IE: one whose
+ register value gets stored as 61-63) then this
+ must be a limm. */
+ /* ??? This bit could use some cleaning up.
+ Referencing the format chars like this goes
+ against style. */
+ if (IS_SYMBOL_OPERAND (*syn))
+ {
+ const char *junk;
+ limm_reloc_p = 1;
+ /* Save this, we don't yet know what reloc to use. */
+ fix_up_at = fc;
+ /* Tell insert_reg we need a limm. This is
+ needed because the value at this point is
+ zero, a shimm. */
+ /* ??? We need a cleaner interface than this. */
+ (*arc_operands[arc_operand_map['Q']].insert)
+ (insn, operand, mods, reg, 0L, &junk);
+ }
+ else
+ fixups[fc].opindex = arc_operand_map[(int) *syn];
+ ++fc;
+ value = 0;
+ }
+
+ /* Insert the register or expression into the instruction. */
+ if (operand->insert)
+ {
+ const char *errmsg = NULL;
+ insn = (*operand->insert) (insn, operand, mods,
+ reg, (long) value, &errmsg);
+ if (errmsg != (const char *) NULL)
+ {
+ last_errmsg = errmsg;
+ if (operand->flags & ARC_OPERAND_ERROR)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+ else if (operand->flags & ARC_OPERAND_WARN)
+ as_warn ("%s", errmsg);
+ break;
+ }
+ }
+ else
+ insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
+
+ ++syn;
+ }
+ }
+
+ /* If we're at the end of the syntax string, we're done. */
+ /* FIXME: try to move this to a separate function. */
+ if (*syn == '\0')
+ {
+ int i;
+ char *f;
+ long limm, limm_p;
+
+ /* For the moment we assume a valid `str' can only contain blanks
+ now. IE: We needn't try again with a longer version of the
+ insn and it is assumed that longer versions of insns appear
+ before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (!is_end_of_line[(unsigned char) *str])
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ /* Is there a limm value? */
+ limm_p = arc_opcode_limm_p (&limm);
+
+ /* Perform various error and warning tests. */
+
+ {
+ static int in_delay_slot_p = 0;
+ static int prev_insn_needs_cc_nop_p = 0;
+ /* delay slot type seen */
+ int delay_slot_type = ARC_DELAY_NONE;
+ /* conditional execution flag seen */
+ int conditional = 0;
+ /* 1 if condition codes are being set */
+ int cc_set_p = 0;
+ /* 1 if conditional branch, including `b' "branch always" */
+ int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
+
+ for (i = 0; i < num_suffixes; ++i)
+ {
+ switch (arc_operands[insn_suffixes[i]->type].fmt)
+ {
+ case 'n':
+ delay_slot_type = insn_suffixes[i]->value;
+ break;
+ case 'q':
+ conditional = insn_suffixes[i]->value;
+ break;
+ case 'f':
+ cc_set_p = 1;
+ break;
+ }
+ }
+
+ /* Putting an insn with a limm value in a delay slot is supposed to
+ be legal, but let's warn the user anyway. Ditto for 8 byte
+ jumps with delay slots. */
+ if (in_delay_slot_p && limm_p)
+ as_warn (_("8 byte instruction in delay slot"));
+ if (delay_slot_type != ARC_DELAY_NONE
+ && limm_p && arc_insn_not_jl (insn)) /* except for jl addr */
+ as_warn (_("8 byte jump instruction with delay slot"));
+ in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
+
+ /* Warn when a conditional branch immediately follows a set of
+ the condition codes. Note that this needn't be done if the
+ insn that sets the condition codes uses a limm. */
+ if (cond_branch_p && conditional != 0 /* 0 = "always" */
+ && prev_insn_needs_cc_nop_p && arc_mach_type == bfd_mach_arc_5)
+ as_warn (_("conditional branch follows set of flags"));
+ prev_insn_needs_cc_nop_p =
+ /* FIXME: ??? not required:
+ (delay_slot_type != ARC_DELAY_NONE) && */
+ cc_set_p && !limm_p;
+ }
+
+ /* Write out the instruction.
+ It is important to fetch enough space in one call to `frag_more'.
+ We use (f - frag_now->fr_literal) to compute where we are and we
+ don't want frag_now to change between calls. */
+ if (limm_p)
+ {
+ f = frag_more (8);
+ md_number_to_chars (f, insn, 4);
+ md_number_to_chars (f + 4, limm, 4);
+ dwarf2_emit_insn (8);
+ }
+ else if (limm_reloc_p)
+ /* We need a limm reloc, but the tables think we don't. */
+ abort ();
+ else
+ {
+ f = frag_more (4);
+ md_number_to_chars (f, insn, 4);
+ dwarf2_emit_insn (4);
+ }
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; ++i)
+ {
+ int op_type, reloc_type;
+ expressionS exptmp;
+ const struct arc_operand *operand;
+
+ /* Create a fixup for this operand.
+ At this point we do not use a bfd_reloc_code_real_type for
+ operands residing in the insn, but instead just use the
+ operand index. This lets us easily handle fixups for any
+ operand type, although that is admittedly not a very exciting
+ feature. We pick a BFD reloc type in md_apply_fix.
+
+ Limm values (4 byte immediate "constants") must be treated
+ normally because they're not part of the actual insn word
+ and thus the insertion routines don't handle them. */
+
+ if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
+ {
+ /* Modify the fixup addend as required by the cpu. */
+ fixups[i].exp.X_add_number += arc_limm_fixup_adjust (insn);
+ op_type = fixups[i].opindex;
+ /* FIXME: can we add this data to the operand table? */
+ if (op_type == arc_operand_map['L']
+ || op_type == arc_operand_map['s']
+ || op_type == arc_operand_map['o']
+ || op_type == arc_operand_map['O'])
+ reloc_type = BFD_RELOC_32;
+ else if (op_type == arc_operand_map['J'])
+ reloc_type = BFD_RELOC_ARC_B26;
+ else
+ abort ();
+ reloc_type = get_arc_exp_reloc_type (1, reloc_type,
+ &fixups[i].exp,
+ &exptmp);
+ }
+ else
+ {
+ op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
+ &fixups[i].exp, &exptmp);
+ reloc_type = op_type + (int) BFD_RELOC_UNUSED;
+ }
+ operand = &arc_operands[op_type];
+ fix_new_exp (frag_now,
+ ((f - frag_now->fr_literal)
+ + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)), 4,
+ &exptmp,
+ (operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
+ (bfd_reloc_code_real_type) reloc_type);
+ }
+ return;
+ }
+ }
+
+ if (NULL == last_errmsg)
+ as_bad (_("bad instruction `%s'"), start);
+ else
+ as_bad ("%s", last_errmsg);
+}
diff --git a/gas/config/tc-arc.h b/gas/config/tc-arc.h
new file mode 100644
index 0000000..a2789e6
--- /dev/null
+++ b/gas/config/tc-arc.h
@@ -0,0 +1,74 @@
+/* tc-arc.h - Macros and type defines for the ARC.
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Contributed by Doug Evans (dje@cygnus.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_ARC 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define LOCAL_LABELS_FB 1
+
+#define TARGET_ARCH bfd_arch_arc
+
+#define DIFF_EXPR_OK
+#define REGISTER_PREFIX '%'
+
+#ifdef LITTLE_ENDIAN
+#undef LITTLE_ENDIAN
+#endif
+
+#ifdef BIG_ENDIAN
+#undef BIG_ENDIAN
+#endif
+
+#define LITTLE_ENDIAN 1234
+
+#define BIG_ENDIAN 4321
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+extern const char * arc_target_format;
+
+#define DEFAULT_TARGET_FORMAT "elf32-littlearc"
+#define TARGET_FORMAT arc_target_format
+#define DEFAULT_BYTE_ORDER LITTLE_ENDIAN
+#define WORKING_DOT_WORD
+#define LISTING_HEADER "ARC GAS "
+
+/* The ARC needs to parse reloc specifiers in .word. */
+
+extern bfd_reloc_code_real_type arc_parse_cons_expression (struct expressionS *,
+ unsigned);
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+ arc_parse_cons_expression (EXP, NBYTES)
+
+extern void arc_cons_fix_new (struct frag *, int, int, struct expressionS *,
+ bfd_reloc_code_real_type);
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \
+ arc_cons_fix_new (FRAG, WHERE, NBYTES, EXP, RELOC)
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
new file mode 100644
index 0000000..5077f87
--- /dev/null
+++ b/gas/config/tc-arm.c
@@ -0,0 +1,25602 @@
+/* tc-arm.c -- Assemble for the ARM
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modified by David Taylor (dtaylor@armltd.co.uk)
+ Cirrus coprocessor mods by Aldy Hernandez (aldyh@redhat.com)
+ Cirrus coprocessor fixes by Petko Manolov (petkan@nucleusys.com)
+ Cirrus coprocessor fixes by Vladimir Ivanov (vladitx@nucleusys.com)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include <limits.h>
+#include <stdarg.h>
+#define NO_RELOC 0
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "obstack.h"
+#include "libiberty.h"
+#include "opcode/arm.h"
+
+#ifdef OBJ_ELF
+#include "elf/arm.h"
+#include "dw2gencfi.h"
+#endif
+
+#include "dwarf2dbg.h"
+
+#ifdef OBJ_ELF
+/* Must be at least the size of the largest unwind opcode (currently two). */
+#define ARM_OPCODE_CHUNK_SIZE 8
+
+/* This structure holds the unwinding state. */
+
+static struct
+{
+ symbolS * proc_start;
+ symbolS * table_entry;
+ symbolS * personality_routine;
+ int personality_index;
+ /* The segment containing the function. */
+ segT saved_seg;
+ subsegT saved_subseg;
+ /* Opcodes generated from this function. */
+ unsigned char * opcodes;
+ int opcode_count;
+ int opcode_alloc;
+ /* The number of bytes pushed to the stack. */
+ offsetT frame_size;
+ /* We don't add stack adjustment opcodes immediately so that we can merge
+ multiple adjustments. We can also omit the final adjustment
+ when using a frame pointer. */
+ offsetT pending_offset;
+ /* These two fields are set by both unwind_movsp and unwind_setfp. They
+ hold the reg+offset to use when restoring sp from a frame pointer. */
+ offsetT fp_offset;
+ int fp_reg;
+ /* Nonzero if an unwind_setfp directive has been seen. */
+ unsigned fp_used:1;
+ /* Nonzero if the last opcode restores sp from fp_reg. */
+ unsigned sp_restored:1;
+} unwind;
+
+#endif /* OBJ_ELF */
+
+/* Results from operand parsing worker functions. */
+
+typedef enum
+{
+ PARSE_OPERAND_SUCCESS,
+ PARSE_OPERAND_FAIL,
+ PARSE_OPERAND_FAIL_NO_BACKTRACK
+} parse_operand_result;
+
+enum arm_float_abi
+{
+ ARM_FLOAT_ABI_HARD,
+ ARM_FLOAT_ABI_SOFTFP,
+ ARM_FLOAT_ABI_SOFT
+};
+
+/* Types of processor to assemble for. */
+#ifndef CPU_DEFAULT
+/* The code that was here used to select a default CPU depending on compiler
+ pre-defines which were only present when doing native builds, thus
+ changing gas' default behaviour depending upon the build host.
+
+ If you have a target that requires a default CPU option then the you
+ should define CPU_DEFAULT here. */
+#endif
+
+#ifndef FPU_DEFAULT
+# ifdef TE_LINUX
+# define FPU_DEFAULT FPU_ARCH_FPA
+# elif defined (TE_NetBSD)
+# ifdef OBJ_ELF
+# define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, but VFP order. */
+# else
+ /* Legacy a.out format. */
+# define FPU_DEFAULT FPU_ARCH_FPA /* Soft-float, but FPA order. */
+# endif
+# elif defined (TE_VXWORKS)
+# define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, VFP order. */
+# else
+ /* For backwards compatibility, default to FPA. */
+# define FPU_DEFAULT FPU_ARCH_FPA
+# endif
+#endif /* ifndef FPU_DEFAULT */
+
+#define streq(a, b) (strcmp (a, b) == 0)
+
+static arm_feature_set cpu_variant;
+static arm_feature_set arm_arch_used;
+static arm_feature_set thumb_arch_used;
+
+/* Flags stored in private area of BFD structure. */
+static int uses_apcs_26 = FALSE;
+static int atpcs = FALSE;
+static int support_interwork = FALSE;
+static int uses_apcs_float = FALSE;
+static int pic_code = FALSE;
+static int fix_v4bx = FALSE;
+/* Warn on using deprecated features. */
+static int warn_on_deprecated = TRUE;
+
+/* Understand CodeComposer Studio assembly syntax. */
+bfd_boolean codecomposer_syntax = FALSE;
+
+/* Variables that we set while parsing command-line options. Once all
+ options have been read we re-process these values to set the real
+ assembly flags. */
+static const arm_feature_set *legacy_cpu = NULL;
+static const arm_feature_set *legacy_fpu = NULL;
+
+static const arm_feature_set *mcpu_cpu_opt = NULL;
+static const arm_feature_set *mcpu_fpu_opt = NULL;
+static const arm_feature_set *march_cpu_opt = NULL;
+static const arm_feature_set *march_fpu_opt = NULL;
+static const arm_feature_set *mfpu_opt = NULL;
+static const arm_feature_set *object_arch = NULL;
+
+/* Constants for known architecture features. */
+static const arm_feature_set fpu_default = FPU_DEFAULT;
+static const arm_feature_set fpu_arch_vfp_v1 = FPU_ARCH_VFP_V1;
+static const arm_feature_set fpu_arch_vfp_v2 = FPU_ARCH_VFP_V2;
+static const arm_feature_set fpu_arch_vfp_v3 = FPU_ARCH_VFP_V3;
+static const arm_feature_set fpu_arch_neon_v1 = FPU_ARCH_NEON_V1;
+static const arm_feature_set fpu_arch_fpa = FPU_ARCH_FPA;
+static const arm_feature_set fpu_any_hard = FPU_ANY_HARD;
+static const arm_feature_set fpu_arch_maverick = FPU_ARCH_MAVERICK;
+static const arm_feature_set fpu_endian_pure = FPU_ARCH_ENDIAN_PURE;
+
+#ifdef CPU_DEFAULT
+static const arm_feature_set cpu_default = CPU_DEFAULT;
+#endif
+
+static const arm_feature_set arm_ext_v1 = ARM_FEATURE (ARM_EXT_V1, 0);
+static const arm_feature_set arm_ext_v2 = ARM_FEATURE (ARM_EXT_V1, 0);
+static const arm_feature_set arm_ext_v2s = ARM_FEATURE (ARM_EXT_V2S, 0);
+static const arm_feature_set arm_ext_v3 = ARM_FEATURE (ARM_EXT_V3, 0);
+static const arm_feature_set arm_ext_v3m = ARM_FEATURE (ARM_EXT_V3M, 0);
+static const arm_feature_set arm_ext_v4 = ARM_FEATURE (ARM_EXT_V4, 0);
+static const arm_feature_set arm_ext_v4t = ARM_FEATURE (ARM_EXT_V4T, 0);
+static const arm_feature_set arm_ext_v5 = ARM_FEATURE (ARM_EXT_V5, 0);
+static const arm_feature_set arm_ext_v4t_5 =
+ ARM_FEATURE (ARM_EXT_V4T | ARM_EXT_V5, 0);
+static const arm_feature_set arm_ext_v5t = ARM_FEATURE (ARM_EXT_V5T, 0);
+static const arm_feature_set arm_ext_v5e = ARM_FEATURE (ARM_EXT_V5E, 0);
+static const arm_feature_set arm_ext_v5exp = ARM_FEATURE (ARM_EXT_V5ExP, 0);
+static const arm_feature_set arm_ext_v5j = ARM_FEATURE (ARM_EXT_V5J, 0);
+static const arm_feature_set arm_ext_v6 = ARM_FEATURE (ARM_EXT_V6, 0);
+static const arm_feature_set arm_ext_v6k = ARM_FEATURE (ARM_EXT_V6K, 0);
+static const arm_feature_set arm_ext_v6t2 = ARM_FEATURE (ARM_EXT_V6T2, 0);
+static const arm_feature_set arm_ext_v6m = ARM_FEATURE (ARM_EXT_V6M, 0);
+static const arm_feature_set arm_ext_v6_notm = ARM_FEATURE (ARM_EXT_V6_NOTM, 0);
+static const arm_feature_set arm_ext_v6_dsp = ARM_FEATURE (ARM_EXT_V6_DSP, 0);
+static const arm_feature_set arm_ext_barrier = ARM_FEATURE (ARM_EXT_BARRIER, 0);
+static const arm_feature_set arm_ext_msr = ARM_FEATURE (ARM_EXT_THUMB_MSR, 0);
+static const arm_feature_set arm_ext_div = ARM_FEATURE (ARM_EXT_DIV, 0);
+static const arm_feature_set arm_ext_v7 = ARM_FEATURE (ARM_EXT_V7, 0);
+static const arm_feature_set arm_ext_v7a = ARM_FEATURE (ARM_EXT_V7A, 0);
+static const arm_feature_set arm_ext_v7r = ARM_FEATURE (ARM_EXT_V7R, 0);
+static const arm_feature_set arm_ext_v7m = ARM_FEATURE (ARM_EXT_V7M, 0);
+static const arm_feature_set arm_ext_v8 = ARM_FEATURE (ARM_EXT_V8, 0);
+static const arm_feature_set arm_ext_m =
+ ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_OS | ARM_EXT_V7M, 0);
+static const arm_feature_set arm_ext_mp = ARM_FEATURE (ARM_EXT_MP, 0);
+static const arm_feature_set arm_ext_sec = ARM_FEATURE (ARM_EXT_SEC, 0);
+static const arm_feature_set arm_ext_os = ARM_FEATURE (ARM_EXT_OS, 0);
+static const arm_feature_set arm_ext_adiv = ARM_FEATURE (ARM_EXT_ADIV, 0);
+static const arm_feature_set arm_ext_virt = ARM_FEATURE (ARM_EXT_VIRT, 0);
+
+static const arm_feature_set arm_arch_any = ARM_ANY;
+static const arm_feature_set arm_arch_full = ARM_FEATURE (-1, -1);
+static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2;
+static const arm_feature_set arm_arch_none = ARM_ARCH_NONE;
+static const arm_feature_set arm_arch_v6m_only = ARM_ARCH_V6M_ONLY;
+
+static const arm_feature_set arm_cext_iwmmxt2 =
+ ARM_FEATURE (0, ARM_CEXT_IWMMXT2);
+static const arm_feature_set arm_cext_iwmmxt =
+ ARM_FEATURE (0, ARM_CEXT_IWMMXT);
+static const arm_feature_set arm_cext_xscale =
+ ARM_FEATURE (0, ARM_CEXT_XSCALE);
+static const arm_feature_set arm_cext_maverick =
+ ARM_FEATURE (0, ARM_CEXT_MAVERICK);
+static const arm_feature_set fpu_fpa_ext_v1 = ARM_FEATURE (0, FPU_FPA_EXT_V1);
+static const arm_feature_set fpu_fpa_ext_v2 = ARM_FEATURE (0, FPU_FPA_EXT_V2);
+static const arm_feature_set fpu_vfp_ext_v1xd =
+ ARM_FEATURE (0, FPU_VFP_EXT_V1xD);
+static const arm_feature_set fpu_vfp_ext_v1 = ARM_FEATURE (0, FPU_VFP_EXT_V1);
+static const arm_feature_set fpu_vfp_ext_v2 = ARM_FEATURE (0, FPU_VFP_EXT_V2);
+static const arm_feature_set fpu_vfp_ext_v3xd = ARM_FEATURE (0, FPU_VFP_EXT_V3xD);
+static const arm_feature_set fpu_vfp_ext_v3 = ARM_FEATURE (0, FPU_VFP_EXT_V3);
+static const arm_feature_set fpu_vfp_ext_d32 =
+ ARM_FEATURE (0, FPU_VFP_EXT_D32);
+static const arm_feature_set fpu_neon_ext_v1 = ARM_FEATURE (0, FPU_NEON_EXT_V1);
+static const arm_feature_set fpu_vfp_v3_or_neon_ext =
+ ARM_FEATURE (0, FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3);
+static const arm_feature_set fpu_vfp_fp16 = ARM_FEATURE (0, FPU_VFP_EXT_FP16);
+static const arm_feature_set fpu_neon_ext_fma = ARM_FEATURE (0, FPU_NEON_EXT_FMA);
+static const arm_feature_set fpu_vfp_ext_fma = ARM_FEATURE (0, FPU_VFP_EXT_FMA);
+static const arm_feature_set fpu_vfp_ext_armv8 =
+ ARM_FEATURE (0, FPU_VFP_EXT_ARMV8);
+static const arm_feature_set fpu_neon_ext_armv8 =
+ ARM_FEATURE (0, FPU_NEON_EXT_ARMV8);
+static const arm_feature_set fpu_crypto_ext_armv8 =
+ ARM_FEATURE (0, FPU_CRYPTO_EXT_ARMV8);
+static const arm_feature_set crc_ext_armv8 =
+ ARM_FEATURE (0, CRC_EXT_ARMV8);
+
+static int mfloat_abi_opt = -1;
+/* Record user cpu selection for object attributes. */
+static arm_feature_set selected_cpu = ARM_ARCH_NONE;
+/* Must be long enough to hold any of the names in arm_cpus. */
+static char selected_cpu_name[16];
+
+extern FLONUM_TYPE generic_floating_point_number;
+
+/* Return if no cpu was selected on command-line. */
+static bfd_boolean
+no_cpu_selected (void)
+{
+ return selected_cpu.core == arm_arch_none.core
+ && selected_cpu.coproc == arm_arch_none.coproc;
+}
+
+#ifdef OBJ_ELF
+# ifdef EABI_DEFAULT
+static int meabi_flags = EABI_DEFAULT;
+# else
+static int meabi_flags = EF_ARM_EABI_UNKNOWN;
+# endif
+
+static int attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES];
+
+bfd_boolean
+arm_is_eabi (void)
+{
+ return (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4);
+}
+#endif
+
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+symbolS * GOT_symbol;
+#endif
+
+/* 0: assemble for ARM,
+ 1: assemble for Thumb,
+ 2: assemble for Thumb even though target CPU does not support thumb
+ instructions. */
+static int thumb_mode = 0;
+/* A value distinct from the possible values for thumb_mode that we
+ can use to record whether thumb_mode has been copied into the
+ tc_frag_data field of a frag. */
+#define MODE_RECORDED (1 << 4)
+
+/* Specifies the intrinsic IT insn behavior mode. */
+enum implicit_it_mode
+{
+ IMPLICIT_IT_MODE_NEVER = 0x00,
+ IMPLICIT_IT_MODE_ARM = 0x01,
+ IMPLICIT_IT_MODE_THUMB = 0x02,
+ IMPLICIT_IT_MODE_ALWAYS = (IMPLICIT_IT_MODE_ARM | IMPLICIT_IT_MODE_THUMB)
+};
+static int implicit_it_mode = IMPLICIT_IT_MODE_ARM;
+
+/* If unified_syntax is true, we are processing the new unified
+ ARM/Thumb syntax. Important differences from the old ARM mode:
+
+ - Immediate operands do not require a # prefix.
+ - Conditional affixes always appear at the end of the
+ instruction. (For backward compatibility, those instructions
+ that formerly had them in the middle, continue to accept them
+ there.)
+ - The IT instruction may appear, and if it does is validated
+ against subsequent conditional affixes. It does not generate
+ machine code.
+
+ Important differences from the old Thumb mode:
+
+ - Immediate operands do not require a # prefix.
+ - Most of the V6T2 instructions are only available in unified mode.
+ - The .N and .W suffixes are recognized and honored (it is an error
+ if they cannot be honored).
+ - All instructions set the flags if and only if they have an 's' affix.
+ - Conditional affixes may be used. They are validated against
+ preceding IT instructions. Unlike ARM mode, you cannot use a
+ conditional affix except in the scope of an IT instruction. */
+
+static bfd_boolean unified_syntax = FALSE;
+
+/* An immediate operand can start with #, and ld*, st*, pld operands
+ can contain [ and ]. We need to tell APP not to elide whitespace
+ before a [, which can appear as the first operand for pld.
+ Likewise, a { can appear as the first operand for push, pop, vld*, etc. */
+const char arm_symbol_chars[] = "#[]{}";
+
+enum neon_el_type
+{
+ NT_invtype,
+ NT_untyped,
+ NT_integer,
+ NT_float,
+ NT_poly,
+ NT_signed,
+ NT_unsigned
+};
+
+struct neon_type_el
+{
+ enum neon_el_type type;
+ unsigned size;
+};
+
+#define NEON_MAX_TYPE_ELS 4
+
+struct neon_type
+{
+ struct neon_type_el el[NEON_MAX_TYPE_ELS];
+ unsigned elems;
+};
+
+enum it_instruction_type
+{
+ OUTSIDE_IT_INSN,
+ INSIDE_IT_INSN,
+ INSIDE_IT_LAST_INSN,
+ IF_INSIDE_IT_LAST_INSN, /* Either outside or inside;
+ if inside, should be the last one. */
+ NEUTRAL_IT_INSN, /* This could be either inside or outside,
+ i.e. BKPT and NOP. */
+ IT_INSN /* The IT insn has been parsed. */
+};
+
+/* The maximum number of operands we need. */
+#define ARM_IT_MAX_OPERANDS 6
+
+struct arm_it
+{
+ const char * error;
+ unsigned long instruction;
+ int size;
+ int size_req;
+ int cond;
+ /* "uncond_value" is set to the value in place of the conditional field in
+ unconditional versions of the instruction, or -1 if nothing is
+ appropriate. */
+ int uncond_value;
+ struct neon_type vectype;
+ /* This does not indicate an actual NEON instruction, only that
+ the mnemonic accepts neon-style type suffixes. */
+ int is_neon;
+ /* Set to the opcode if the instruction needs relaxation.
+ Zero if the instruction is not relaxed. */
+ unsigned long relax;
+ struct
+ {
+ bfd_reloc_code_real_type type;
+ expressionS exp;
+ int pc_rel;
+ } reloc;
+
+ enum it_instruction_type it_insn_type;
+
+ struct
+ {
+ unsigned reg;
+ signed int imm;
+ struct neon_type_el vectype;
+ unsigned present : 1; /* Operand present. */
+ unsigned isreg : 1; /* Operand was a register. */
+ unsigned immisreg : 1; /* .imm field is a second register. */
+ unsigned isscalar : 1; /* Operand is a (Neon) scalar. */
+ unsigned immisalign : 1; /* Immediate is an alignment specifier. */
+ unsigned immisfloat : 1; /* Immediate was parsed as a float. */
+ /* Note: we abuse "regisimm" to mean "is Neon register" in VMOV
+ instructions. This allows us to disambiguate ARM <-> vector insns. */
+ unsigned regisimm : 1; /* 64-bit immediate, reg forms high 32 bits. */
+ unsigned isvec : 1; /* Is a single, double or quad VFP/Neon reg. */
+ unsigned isquad : 1; /* Operand is Neon quad-precision register. */
+ unsigned issingle : 1; /* Operand is VFP single-precision register. */
+ unsigned hasreloc : 1; /* Operand has relocation suffix. */
+ unsigned writeback : 1; /* Operand has trailing ! */
+ unsigned preind : 1; /* Preindexed address. */
+ unsigned postind : 1; /* Postindexed address. */
+ unsigned negative : 1; /* Index register was negated. */
+ unsigned shifted : 1; /* Shift applied to operation. */
+ unsigned shift_kind : 3; /* Shift operation (enum shift_kind). */
+ } operands[ARM_IT_MAX_OPERANDS];
+};
+
+static struct arm_it inst;
+
+#define NUM_FLOAT_VALS 8
+
+const char * fp_const[] =
+{
+ "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
+};
+
+/* Number of littlenums required to hold an extended precision number. */
+#define MAX_LITTLENUMS 6
+
+LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
+
+#define FAIL (-1)
+#define SUCCESS (0)
+
+#define SUFF_S 1
+#define SUFF_D 2
+#define SUFF_E 3
+#define SUFF_P 4
+
+#define CP_T_X 0x00008000
+#define CP_T_Y 0x00400000
+
+#define CONDS_BIT 0x00100000
+#define LOAD_BIT 0x00100000
+
+#define DOUBLE_LOAD_FLAG 0x00000001
+
+struct asm_cond
+{
+ const char * template_name;
+ unsigned long value;
+};
+
+#define COND_ALWAYS 0xE
+
+struct asm_psr
+{
+ const char * template_name;
+ unsigned long field;
+};
+
+struct asm_barrier_opt
+{
+ const char * template_name;
+ unsigned long value;
+ const arm_feature_set arch;
+};
+
+/* The bit that distinguishes CPSR and SPSR. */
+#define SPSR_BIT (1 << 22)
+
+/* The individual PSR flag bits. */
+#define PSR_c (1 << 16)
+#define PSR_x (1 << 17)
+#define PSR_s (1 << 18)
+#define PSR_f (1 << 19)
+
+struct reloc_entry
+{
+ char * name;
+ bfd_reloc_code_real_type reloc;
+};
+
+enum vfp_reg_pos
+{
+ VFP_REG_Sd, VFP_REG_Sm, VFP_REG_Sn,
+ VFP_REG_Dd, VFP_REG_Dm, VFP_REG_Dn
+};
+
+enum vfp_ldstm_type
+{
+ VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
+};
+
+/* Bits for DEFINED field in neon_typed_alias. */
+#define NTA_HASTYPE 1
+#define NTA_HASINDEX 2
+
+struct neon_typed_alias
+{
+ unsigned char defined;
+ unsigned char index;
+ struct neon_type_el eltype;
+};
+
+/* ARM register categories. This includes coprocessor numbers and various
+ architecture extensions' registers. */
+enum arm_reg_type
+{
+ REG_TYPE_RN,
+ REG_TYPE_CP,
+ REG_TYPE_CN,
+ REG_TYPE_FN,
+ REG_TYPE_VFS,
+ REG_TYPE_VFD,
+ REG_TYPE_NQ,
+ REG_TYPE_VFSD,
+ REG_TYPE_NDQ,
+ REG_TYPE_NSDQ,
+ REG_TYPE_VFC,
+ REG_TYPE_MVF,
+ REG_TYPE_MVD,
+ REG_TYPE_MVFX,
+ REG_TYPE_MVDX,
+ REG_TYPE_MVAX,
+ REG_TYPE_DSPSC,
+ REG_TYPE_MMXWR,
+ REG_TYPE_MMXWC,
+ REG_TYPE_MMXWCG,
+ REG_TYPE_XSCALE,
+ REG_TYPE_RNB
+};
+
+/* Structure for a hash table entry for a register.
+ If TYPE is REG_TYPE_VFD or REG_TYPE_NQ, the NEON field can point to extra
+ information which states whether a vector type or index is specified (for a
+ register alias created with .dn or .qn). Otherwise NEON should be NULL. */
+struct reg_entry
+{
+ const char * name;
+ unsigned int number;
+ unsigned char type;
+ unsigned char builtin;
+ struct neon_typed_alias * neon;
+};
+
+/* Diagnostics used when we don't get a register of the expected type. */
+const char * const reg_expected_msgs[] =
+{
+ N_("ARM register expected"),
+ N_("bad or missing co-processor number"),
+ N_("co-processor register expected"),
+ N_("FPA register expected"),
+ N_("VFP single precision register expected"),
+ N_("VFP/Neon double precision register expected"),
+ N_("Neon quad precision register expected"),
+ N_("VFP single or double precision register expected"),
+ N_("Neon double or quad precision register expected"),
+ N_("VFP single, double or Neon quad precision register expected"),
+ N_("VFP system register expected"),
+ N_("Maverick MVF register expected"),
+ N_("Maverick MVD register expected"),
+ N_("Maverick MVFX register expected"),
+ N_("Maverick MVDX register expected"),
+ N_("Maverick MVAX register expected"),
+ N_("Maverick DSPSC register expected"),
+ N_("iWMMXt data register expected"),
+ N_("iWMMXt control register expected"),
+ N_("iWMMXt scalar register expected"),
+ N_("XScale accumulator register expected"),
+};
+
+/* Some well known registers that we refer to directly elsewhere. */
+#define REG_R12 12
+#define REG_SP 13
+#define REG_LR 14
+#define REG_PC 15
+
+/* ARM instructions take 4bytes in the object file, Thumb instructions
+ take 2: */
+#define INSN_SIZE 4
+
+struct asm_opcode
+{
+ /* Basic string to match. */
+ const char * template_name;
+
+ /* Parameters to instruction. */
+ unsigned int operands[8];
+
+ /* Conditional tag - see opcode_lookup. */
+ unsigned int tag : 4;
+
+ /* Basic instruction code. */
+ unsigned int avalue : 28;
+
+ /* Thumb-format instruction code. */
+ unsigned int tvalue;
+
+ /* Which architecture variant provides this instruction. */
+ const arm_feature_set * avariant;
+ const arm_feature_set * tvariant;
+
+ /* Function to call to encode instruction in ARM format. */
+ void (* aencode) (void);
+
+ /* Function to call to encode instruction in Thumb format. */
+ void (* tencode) (void);
+};
+
+/* Defines for various bits that we will want to toggle. */
+#define INST_IMMEDIATE 0x02000000
+#define OFFSET_REG 0x02000000
+#define HWOFFSET_IMM 0x00400000
+#define SHIFT_BY_REG 0x00000010
+#define PRE_INDEX 0x01000000
+#define INDEX_UP 0x00800000
+#define WRITE_BACK 0x00200000
+#define LDM_TYPE_2_OR_3 0x00400000
+#define CPSI_MMOD 0x00020000
+
+#define LITERAL_MASK 0xf000f000
+#define OPCODE_MASK 0xfe1fffff
+#define V4_STR_BIT 0x00000020
+#define VLDR_VMOV_SAME 0x0040f000
+
+#define T2_SUBS_PC_LR 0xf3de8f00
+
+#define DATA_OP_SHIFT 21
+
+#define T2_OPCODE_MASK 0xfe1fffff
+#define T2_DATA_OP_SHIFT 21
+
+#define A_COND_MASK 0xf0000000
+#define A_PUSH_POP_OP_MASK 0x0fff0000
+
+/* Opcodes for pushing/poping registers to/from the stack. */
+#define A1_OPCODE_PUSH 0x092d0000
+#define A2_OPCODE_PUSH 0x052d0004
+#define A2_OPCODE_POP 0x049d0004
+
+/* Codes to distinguish the arithmetic instructions. */
+#define OPCODE_AND 0
+#define OPCODE_EOR 1
+#define OPCODE_SUB 2
+#define OPCODE_RSB 3
+#define OPCODE_ADD 4
+#define OPCODE_ADC 5
+#define OPCODE_SBC 6
+#define OPCODE_RSC 7
+#define OPCODE_TST 8
+#define OPCODE_TEQ 9
+#define OPCODE_CMP 10
+#define OPCODE_CMN 11
+#define OPCODE_ORR 12
+#define OPCODE_MOV 13
+#define OPCODE_BIC 14
+#define OPCODE_MVN 15
+
+#define T2_OPCODE_AND 0
+#define T2_OPCODE_BIC 1
+#define T2_OPCODE_ORR 2
+#define T2_OPCODE_ORN 3
+#define T2_OPCODE_EOR 4
+#define T2_OPCODE_ADD 8
+#define T2_OPCODE_ADC 10
+#define T2_OPCODE_SBC 11
+#define T2_OPCODE_SUB 13
+#define T2_OPCODE_RSB 14
+
+#define T_OPCODE_MUL 0x4340
+#define T_OPCODE_TST 0x4200
+#define T_OPCODE_CMN 0x42c0
+#define T_OPCODE_NEG 0x4240
+#define T_OPCODE_MVN 0x43c0
+
+#define T_OPCODE_ADD_R3 0x1800
+#define T_OPCODE_SUB_R3 0x1a00
+#define T_OPCODE_ADD_HI 0x4400
+#define T_OPCODE_ADD_ST 0xb000
+#define T_OPCODE_SUB_ST 0xb080
+#define T_OPCODE_ADD_SP 0xa800
+#define T_OPCODE_ADD_PC 0xa000
+#define T_OPCODE_ADD_I8 0x3000
+#define T_OPCODE_SUB_I8 0x3800
+#define T_OPCODE_ADD_I3 0x1c00
+#define T_OPCODE_SUB_I3 0x1e00
+
+#define T_OPCODE_ASR_R 0x4100
+#define T_OPCODE_LSL_R 0x4080
+#define T_OPCODE_LSR_R 0x40c0
+#define T_OPCODE_ROR_R 0x41c0
+#define T_OPCODE_ASR_I 0x1000
+#define T_OPCODE_LSL_I 0x0000
+#define T_OPCODE_LSR_I 0x0800
+
+#define T_OPCODE_MOV_I8 0x2000
+#define T_OPCODE_CMP_I8 0x2800
+#define T_OPCODE_CMP_LR 0x4280
+#define T_OPCODE_MOV_HR 0x4600
+#define T_OPCODE_CMP_HR 0x4500
+
+#define T_OPCODE_LDR_PC 0x4800
+#define T_OPCODE_LDR_SP 0x9800
+#define T_OPCODE_STR_SP 0x9000
+#define T_OPCODE_LDR_IW 0x6800
+#define T_OPCODE_STR_IW 0x6000
+#define T_OPCODE_LDR_IH 0x8800
+#define T_OPCODE_STR_IH 0x8000
+#define T_OPCODE_LDR_IB 0x7800
+#define T_OPCODE_STR_IB 0x7000
+#define T_OPCODE_LDR_RW 0x5800
+#define T_OPCODE_STR_RW 0x5000
+#define T_OPCODE_LDR_RH 0x5a00
+#define T_OPCODE_STR_RH 0x5200
+#define T_OPCODE_LDR_RB 0x5c00
+#define T_OPCODE_STR_RB 0x5400
+
+#define T_OPCODE_PUSH 0xb400
+#define T_OPCODE_POP 0xbc00
+
+#define T_OPCODE_BRANCH 0xe000
+
+#define THUMB_SIZE 2 /* Size of thumb instruction. */
+#define THUMB_PP_PC_LR 0x0100
+#define THUMB_LOAD_BIT 0x0800
+#define THUMB2_LOAD_BIT 0x00100000
+
+#define BAD_ARGS _("bad arguments to instruction")
+#define BAD_SP _("r13 not allowed here")
+#define BAD_PC _("r15 not allowed here")
+#define BAD_COND _("instruction cannot be conditional")
+#define BAD_OVERLAP _("registers may not be the same")
+#define BAD_HIREG _("lo register required")
+#define BAD_THUMB32 _("instruction not supported in Thumb16 mode")
+#define BAD_ADDR_MODE _("instruction does not accept this addressing mode");
+#define BAD_BRANCH _("branch must be last instruction in IT block")
+#define BAD_NOT_IT _("instruction not allowed in IT block")
+#define BAD_FPU _("selected FPU does not support instruction")
+#define BAD_OUT_IT _("thumb conditional instruction should be in IT block")
+#define BAD_IT_COND _("incorrect condition in IT block")
+#define BAD_IT_IT _("IT falling in the range of a previous IT block")
+#define MISSING_FNSTART _("missing .fnstart before unwinding directive")
+#define BAD_PC_ADDRESSING \
+ _("cannot use register index with PC-relative addressing")
+#define BAD_PC_WRITEBACK \
+ _("cannot use writeback with PC-relative addressing")
+#define BAD_RANGE _("branch out of range")
+#define UNPRED_REG(R) _("using " R " results in unpredictable behaviour")
+
+static struct hash_control * arm_ops_hsh;
+static struct hash_control * arm_cond_hsh;
+static struct hash_control * arm_shift_hsh;
+static struct hash_control * arm_psr_hsh;
+static struct hash_control * arm_v7m_psr_hsh;
+static struct hash_control * arm_reg_hsh;
+static struct hash_control * arm_reloc_hsh;
+static struct hash_control * arm_barrier_opt_hsh;
+
+/* Stuff needed to resolve the label ambiguity
+ As:
+ ...
+ label: <insn>
+ may differ from:
+ ...
+ label:
+ <insn> */
+
+symbolS * last_label_seen;
+static int label_is_thumb_function_name = FALSE;
+
+/* Literal pool structure. Held on a per-section
+ and per-sub-section basis. */
+
+#define MAX_LITERAL_POOL_SIZE 1024
+typedef struct literal_pool
+{
+ expressionS literals [MAX_LITERAL_POOL_SIZE];
+ unsigned int next_free_entry;
+ unsigned int id;
+ symbolS * symbol;
+ segT section;
+ subsegT sub_section;
+#ifdef OBJ_ELF
+ struct dwarf2_line_info locs [MAX_LITERAL_POOL_SIZE];
+#endif
+ struct literal_pool * next;
+ unsigned int alignment;
+} literal_pool;
+
+/* Pointer to a linked list of literal pools. */
+literal_pool * list_of_pools = NULL;
+
+typedef enum asmfunc_states
+{
+ OUTSIDE_ASMFUNC,
+ WAITING_ASMFUNC_NAME,
+ WAITING_ENDASMFUNC
+} asmfunc_states;
+
+static asmfunc_states asmfunc_state = OUTSIDE_ASMFUNC;
+
+#ifdef OBJ_ELF
+# define now_it seg_info (now_seg)->tc_segment_info_data.current_it
+#else
+static struct current_it now_it;
+#endif
+
+static inline int
+now_it_compatible (int cond)
+{
+ return (cond & ~1) == (now_it.cc & ~1);
+}
+
+static inline int
+conditional_insn (void)
+{
+ return inst.cond != COND_ALWAYS;
+}
+
+static int in_it_block (void);
+
+static int handle_it_state (void);
+
+static void force_automatic_it_block_close (void);
+
+static void it_fsm_post_encode (void);
+
+#define set_it_insn_type(type) \
+ do \
+ { \
+ inst.it_insn_type = type; \
+ if (handle_it_state () == FAIL) \
+ return; \
+ } \
+ while (0)
+
+#define set_it_insn_type_nonvoid(type, failret) \
+ do \
+ { \
+ inst.it_insn_type = type; \
+ if (handle_it_state () == FAIL) \
+ return failret; \
+ } \
+ while(0)
+
+#define set_it_insn_type_last() \
+ do \
+ { \
+ if (inst.cond == COND_ALWAYS) \
+ set_it_insn_type (IF_INSIDE_IT_LAST_INSN); \
+ else \
+ set_it_insn_type (INSIDE_IT_LAST_INSN); \
+ } \
+ while (0)
+
+/* Pure syntax. */
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+char arm_comment_chars[] = "@";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#";
+
+char arm_line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant
+ from exp in floating point numbers. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant. */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+
+const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+/* Prefix characters that indicate the start of an immediate
+ value. */
+#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
+
+/* Separator character handling. */
+
+#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
+
+static inline int
+skip_past_char (char ** str, char c)
+{
+ /* PR gas/14987: Allow for whitespace before the expected character. */
+ skip_whitespace (*str);
+
+ if (**str == c)
+ {
+ (*str)++;
+ return SUCCESS;
+ }
+ else
+ return FAIL;
+}
+
+#define skip_past_comma(str) skip_past_char (str, ',')
+
+/* Arithmetic expressions (possibly involving symbols). */
+
+/* Return TRUE if anything in the expression is a bignum. */
+
+static int
+walk_no_bignums (symbolS * sp)
+{
+ if (symbol_get_value_expression (sp)->X_op == O_big)
+ return 1;
+
+ if (symbol_get_value_expression (sp)->X_add_symbol)
+ {
+ return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
+ || (symbol_get_value_expression (sp)->X_op_symbol
+ && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
+ }
+
+ return 0;
+}
+
+static int in_my_get_expression = 0;
+
+/* Third argument to my_get_expression. */
+#define GE_NO_PREFIX 0
+#define GE_IMM_PREFIX 1
+#define GE_OPT_PREFIX 2
+/* This is a bit of a hack. Use an optional prefix, and also allow big (64-bit)
+ immediates, as can be used in Neon VMVN and VMOV immediate instructions. */
+#define GE_OPT_PREFIX_BIG 3
+
+static int
+my_get_expression (expressionS * ep, char ** str, int prefix_mode)
+{
+ char * save_in;
+ segT seg;
+
+ /* In unified syntax, all prefixes are optional. */
+ if (unified_syntax)
+ prefix_mode = (prefix_mode == GE_OPT_PREFIX_BIG) ? prefix_mode
+ : GE_OPT_PREFIX;
+
+ switch (prefix_mode)
+ {
+ case GE_NO_PREFIX: break;
+ case GE_IMM_PREFIX:
+ if (!is_immediate_prefix (**str))
+ {
+ inst.error = _("immediate expression requires a # prefix");
+ return FAIL;
+ }
+ (*str)++;
+ break;
+ case GE_OPT_PREFIX:
+ case GE_OPT_PREFIX_BIG:
+ if (is_immediate_prefix (**str))
+ (*str)++;
+ break;
+ default: abort ();
+ }
+
+ memset (ep, 0, sizeof (expressionS));
+
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ in_my_get_expression = 1;
+ seg = expression (ep);
+ in_my_get_expression = 0;
+
+ if (ep->X_op == O_illegal || ep->X_op == O_absent)
+ {
+ /* We found a bad or missing expression in md_operand(). */
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ if (inst.error == NULL)
+ inst.error = (ep->X_op == O_absent
+ ? _("missing expression") :_("bad expression"));
+ return 1;
+ }
+
+#ifdef OBJ_AOUT
+ if (seg != absolute_section
+ && seg != text_section
+ && seg != data_section
+ && seg != bss_section
+ && seg != undefined_section)
+ {
+ inst.error = _("bad segment");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+#else
+ (void) seg;
+#endif
+
+ /* Get rid of any bignums now, so that we don't generate an error for which
+ we can't establish a line number later on. Big numbers are never valid
+ in instructions, which is where this routine is always called. */
+ if (prefix_mode != GE_OPT_PREFIX_BIG
+ && (ep->X_op == O_big
+ || (ep->X_add_symbol
+ && (walk_no_bignums (ep->X_add_symbol)
+ || (ep->X_op_symbol
+ && walk_no_bignums (ep->X_op_symbol))))))
+ {
+ inst.error = _("invalid constant");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+/* 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.
+
+ Note that fp constants aren't represent in the normal way on the ARM.
+ In big endian mode, things are as expected. However, in little endian
+ mode fp constants are big-endian word-wise, and little-endian byte-wise
+ within the words. For example, (double) 1.1 in big endian mode is
+ the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
+ the byte sequence 99 99 f1 3f 9a 99 99 99.
+
+ ??? The format of 12 byte floats is uncertain according to gcc's arm.h. */
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 5;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 5;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Unrecognized or unsupported floating point constant");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ {
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_endian_pure))
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ else
+ /* For a 4 byte float the order of elements in `words' is 1 0.
+ For an 8 byte float the order is 1 0 3 2. */
+ for (i = 0; i < prec; i += 2)
+ {
+ md_number_to_chars (litP, (valueT) words[i + 1],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_chars (litP + sizeof (LITTLENUM_TYPE),
+ (valueT) words[i], sizeof (LITTLENUM_TYPE));
+ litP += 2 * sizeof (LITTLENUM_TYPE);
+ }
+ }
+
+ return NULL;
+}
+
+/* We handle all bad expressions here, so that we can report the faulty
+ instruction in the error message. */
+void
+md_operand (expressionS * exp)
+{
+ if (in_my_get_expression)
+ exp->X_op = O_illegal;
+}
+
+/* Immediate values. */
+
+/* Generic immediate-value read function for use in directives.
+ Accepts anything that 'expression' can fold to a constant.
+ *val receives the number. */
+#ifdef OBJ_ELF
+static int
+immediate_for_directive (int *val)
+{
+ expressionS exp;
+ exp.X_op = O_illegal;
+
+ if (is_immediate_prefix (*input_line_pointer))
+ {
+ input_line_pointer++;
+ expression (&exp);
+ }
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected #constant"));
+ ignore_rest_of_line ();
+ return FAIL;
+ }
+ *val = exp.X_add_number;
+ return SUCCESS;
+}
+#endif
+
+/* Register parsing. */
+
+/* Generic register parser. CCP points to what should be the
+ beginning of a register name. If it is indeed a valid register
+ name, advance CCP over it and return the reg_entry structure;
+ otherwise return NULL. Does not issue diagnostics. */
+
+static struct reg_entry *
+arm_reg_parse_multi (char **ccp)
+{
+ char *start = *ccp;
+ char *p;
+ struct reg_entry *reg;
+
+ skip_whitespace (start);
+
+#ifdef REGISTER_PREFIX
+ if (*start != REGISTER_PREFIX)
+ return NULL;
+ start++;
+#endif
+#ifdef OPTIONAL_REGISTER_PREFIX
+ if (*start == OPTIONAL_REGISTER_PREFIX)
+ start++;
+#endif
+
+ p = start;
+ if (!ISALPHA (*p) || !is_name_beginner (*p))
+ return NULL;
+
+ do
+ p++;
+ while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
+
+ reg = (struct reg_entry *) hash_find_n (arm_reg_hsh, start, p - start);
+
+ if (!reg)
+ return NULL;
+
+ *ccp = p;
+ return reg;
+}
+
+static int
+arm_reg_alt_syntax (char **ccp, char *start, struct reg_entry *reg,
+ enum arm_reg_type type)
+{
+ /* Alternative syntaxes are accepted for a few register classes. */
+ switch (type)
+ {
+ case REG_TYPE_MVF:
+ case REG_TYPE_MVD:
+ case REG_TYPE_MVFX:
+ case REG_TYPE_MVDX:
+ /* Generic coprocessor register names are allowed for these. */
+ if (reg && reg->type == REG_TYPE_CN)
+ return reg->number;
+ break;
+
+ case REG_TYPE_CP:
+ /* For backward compatibility, a bare number is valid here. */
+ {
+ unsigned long processor = strtoul (start, ccp, 10);
+ if (*ccp != start && processor <= 15)
+ return processor;
+ }
+
+ case REG_TYPE_MMXWC:
+ /* WC includes WCG. ??? I'm not sure this is true for all
+ instructions that take WC registers. */
+ if (reg && reg->type == REG_TYPE_MMXWCG)
+ return reg->number;
+ break;
+
+ default:
+ break;
+ }
+
+ return FAIL;
+}
+
+/* As arm_reg_parse_multi, but the register must be of type TYPE, and the
+ return value is the register number or FAIL. */
+
+static int
+arm_reg_parse (char **ccp, enum arm_reg_type type)
+{
+ char *start = *ccp;
+ struct reg_entry *reg = arm_reg_parse_multi (ccp);
+ int ret;
+
+ /* Do not allow a scalar (reg+index) to parse as a register. */
+ if (reg && reg->neon && (reg->neon->defined & NTA_HASINDEX))
+ return FAIL;
+
+ if (reg && reg->type == type)
+ return reg->number;
+
+ if ((ret = arm_reg_alt_syntax (ccp, start, reg, type)) != FAIL)
+ return ret;
+
+ *ccp = start;
+ return FAIL;
+}
+
+/* Parse a Neon type specifier. *STR should point at the leading '.'
+ character. Does no verification at this stage that the type fits the opcode
+ properly. E.g.,
+
+ .i32.i32.s16
+ .s32.f32
+ .u16
+
+ Can all be legally parsed by this function.
+
+ Fills in neon_type struct pointer with parsed information, and updates STR
+ to point after the parsed type specifier. Returns SUCCESS if this was a legal
+ type, FAIL if not. */
+
+static int
+parse_neon_type (struct neon_type *type, char **str)
+{
+ char *ptr = *str;
+
+ if (type)
+ type->elems = 0;
+
+ while (type->elems < NEON_MAX_TYPE_ELS)
+ {
+ enum neon_el_type thistype = NT_untyped;
+ unsigned thissize = -1u;
+
+ if (*ptr != '.')
+ break;
+
+ ptr++;
+
+ /* Just a size without an explicit type. */
+ if (ISDIGIT (*ptr))
+ goto parsesize;
+
+ switch (TOLOWER (*ptr))
+ {
+ case 'i': thistype = NT_integer; break;
+ case 'f': thistype = NT_float; break;
+ case 'p': thistype = NT_poly; break;
+ case 's': thistype = NT_signed; break;
+ case 'u': thistype = NT_unsigned; break;
+ case 'd':
+ thistype = NT_float;
+ thissize = 64;
+ ptr++;
+ goto done;
+ default:
+ as_bad (_("unexpected character `%c' in type specifier"), *ptr);
+ return FAIL;
+ }
+
+ ptr++;
+
+ /* .f is an abbreviation for .f32. */
+ if (thistype == NT_float && !ISDIGIT (*ptr))
+ thissize = 32;
+ else
+ {
+ parsesize:
+ thissize = strtoul (ptr, &ptr, 10);
+
+ if (thissize != 8 && thissize != 16 && thissize != 32
+ && thissize != 64)
+ {
+ as_bad (_("bad size %d in type specifier"), thissize);
+ return FAIL;
+ }
+ }
+
+ done:
+ if (type)
+ {
+ type->el[type->elems].type = thistype;
+ type->el[type->elems].size = thissize;
+ type->elems++;
+ }
+ }
+
+ /* Empty/missing type is not a successful parse. */
+ if (type->elems == 0)
+ return FAIL;
+
+ *str = ptr;
+
+ return SUCCESS;
+}
+
+/* Errors may be set multiple times during parsing or bit encoding
+ (particularly in the Neon bits), but usually the earliest error which is set
+ will be the most meaningful. Avoid overwriting it with later (cascading)
+ errors by calling this function. */
+
+static void
+first_error (const char *err)
+{
+ if (!inst.error)
+ inst.error = err;
+}
+
+/* Parse a single type, e.g. ".s32", leading period included. */
+static int
+parse_neon_operand_type (struct neon_type_el *vectype, char **ccp)
+{
+ char *str = *ccp;
+ struct neon_type optype;
+
+ if (*str == '.')
+ {
+ if (parse_neon_type (&optype, &str) == SUCCESS)
+ {
+ if (optype.elems == 1)
+ *vectype = optype.el[0];
+ else
+ {
+ first_error (_("only one type should be specified for operand"));
+ return FAIL;
+ }
+ }
+ else
+ {
+ first_error (_("vector type expected"));
+ return FAIL;
+ }
+ }
+ else
+ return FAIL;
+
+ *ccp = str;
+
+ return SUCCESS;
+}
+
+/* Special meanings for indices (which have a range of 0-7), which will fit into
+ a 4-bit integer. */
+
+#define NEON_ALL_LANES 15
+#define NEON_INTERLEAVE_LANES 14
+
+/* Parse either a register or a scalar, with an optional type. Return the
+ register number, and optionally fill in the actual type of the register
+ when multiple alternatives were given (NEON_TYPE_NDQ) in *RTYPE, and
+ type/index information in *TYPEINFO. */
+
+static int
+parse_typed_reg_or_scalar (char **ccp, enum arm_reg_type type,
+ enum arm_reg_type *rtype,
+ struct neon_typed_alias *typeinfo)
+{
+ char *str = *ccp;
+ struct reg_entry *reg = arm_reg_parse_multi (&str);
+ struct neon_typed_alias atype;
+ struct neon_type_el parsetype;
+
+ atype.defined = 0;
+ atype.index = -1;
+ atype.eltype.type = NT_invtype;
+ atype.eltype.size = -1;
+
+ /* Try alternate syntax for some types of register. Note these are mutually
+ exclusive with the Neon syntax extensions. */
+ if (reg == NULL)
+ {
+ int altreg = arm_reg_alt_syntax (&str, *ccp, reg, type);
+ if (altreg != FAIL)
+ *ccp = str;
+ if (typeinfo)
+ *typeinfo = atype;
+ return altreg;
+ }
+
+ /* Undo polymorphism when a set of register types may be accepted. */
+ if ((type == REG_TYPE_NDQ
+ && (reg->type == REG_TYPE_NQ || reg->type == REG_TYPE_VFD))
+ || (type == REG_TYPE_VFSD
+ && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD))
+ || (type == REG_TYPE_NSDQ
+ && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD
+ || reg->type == REG_TYPE_NQ))
+ || (type == REG_TYPE_MMXWC
+ && (reg->type == REG_TYPE_MMXWCG)))
+ type = (enum arm_reg_type) reg->type;
+
+ if (type != reg->type)
+ return FAIL;
+
+ if (reg->neon)
+ atype = *reg->neon;
+
+ if (parse_neon_operand_type (&parsetype, &str) == SUCCESS)
+ {
+ if ((atype.defined & NTA_HASTYPE) != 0)
+ {
+ first_error (_("can't redefine type for operand"));
+ return FAIL;
+ }
+ atype.defined |= NTA_HASTYPE;
+ atype.eltype = parsetype;
+ }
+
+ if (skip_past_char (&str, '[') == SUCCESS)
+ {
+ if (type != REG_TYPE_VFD)
+ {
+ first_error (_("only D registers may be indexed"));
+ return FAIL;
+ }
+
+ if ((atype.defined & NTA_HASINDEX) != 0)
+ {
+ first_error (_("can't change index for operand"));
+ return FAIL;
+ }
+
+ atype.defined |= NTA_HASINDEX;
+
+ if (skip_past_char (&str, ']') == SUCCESS)
+ atype.index = NEON_ALL_LANES;
+ else
+ {
+ expressionS exp;
+
+ my_get_expression (&exp, &str, GE_NO_PREFIX);
+
+ if (exp.X_op != O_constant)
+ {
+ first_error (_("constant expression required"));
+ return FAIL;
+ }
+
+ if (skip_past_char (&str, ']') == FAIL)
+ return FAIL;
+
+ atype.index = exp.X_add_number;
+ }
+ }
+
+ if (typeinfo)
+ *typeinfo = atype;
+
+ if (rtype)
+ *rtype = type;
+
+ *ccp = str;
+
+ return reg->number;
+}
+
+/* Like arm_reg_parse, but allow allow the following extra features:
+ - If RTYPE is non-zero, return the (possibly restricted) type of the
+ register (e.g. Neon double or quad reg when either has been requested).
+ - If this is a Neon vector type with additional type information, fill
+ in the struct pointed to by VECTYPE (if non-NULL).
+ This function will fault on encountering a scalar. */
+
+static int
+arm_typed_reg_parse (char **ccp, enum arm_reg_type type,
+ enum arm_reg_type *rtype, struct neon_type_el *vectype)
+{
+ struct neon_typed_alias atype;
+ char *str = *ccp;
+ int reg = parse_typed_reg_or_scalar (&str, type, rtype, &atype);
+
+ if (reg == FAIL)
+ return FAIL;
+
+ /* Do not allow regname(... to parse as a register. */
+ if (*str == '(')
+ return FAIL;
+
+ /* Do not allow a scalar (reg+index) to parse as a register. */
+ if ((atype.defined & NTA_HASINDEX) != 0)
+ {
+ first_error (_("register operand expected, but got scalar"));
+ return FAIL;
+ }
+
+ if (vectype)
+ *vectype = atype.eltype;
+
+ *ccp = str;
+
+ return reg;
+}
+
+#define NEON_SCALAR_REG(X) ((X) >> 4)
+#define NEON_SCALAR_INDEX(X) ((X) & 15)
+
+/* Parse a Neon scalar. Most of the time when we're parsing a scalar, we don't
+ have enough information to be able to do a good job bounds-checking. So, we
+ just do easy checks here, and do further checks later. */
+
+static int
+parse_scalar (char **ccp, int elsize, struct neon_type_el *type)
+{
+ int reg;
+ char *str = *ccp;
+ struct neon_typed_alias atype;
+
+ reg = parse_typed_reg_or_scalar (&str, REG_TYPE_VFD, NULL, &atype);
+
+ if (reg == FAIL || (atype.defined & NTA_HASINDEX) == 0)
+ return FAIL;
+
+ if (atype.index == NEON_ALL_LANES)
+ {
+ first_error (_("scalar must have an index"));
+ return FAIL;
+ }
+ else if (atype.index >= 64 / elsize)
+ {
+ first_error (_("scalar index out of range"));
+ return FAIL;
+ }
+
+ if (type)
+ *type = atype.eltype;
+
+ *ccp = str;
+
+ return reg * 16 + atype.index;
+}
+
+/* Parse an ARM register list. Returns the bitmask, or FAIL. */
+
+static long
+parse_reg_list (char ** strp)
+{
+ char * str = * strp;
+ long range = 0;
+ int another_range;
+
+ /* We come back here if we get ranges concatenated by '+' or '|'. */
+ do
+ {
+ skip_whitespace (str);
+
+ another_range = 0;
+
+ if (*str == '{')
+ {
+ int in_range = 0;
+ int cur_reg = -1;
+
+ str++;
+ do
+ {
+ int reg;
+
+ if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
+ {
+ first_error (_(reg_expected_msgs[REG_TYPE_RN]));
+ return FAIL;
+ }
+
+ if (in_range)
+ {
+ int i;
+
+ if (reg <= cur_reg)
+ {
+ first_error (_("bad range in register list"));
+ return FAIL;
+ }
+
+ for (i = cur_reg + 1; i < reg; i++)
+ {
+ if (range & (1 << i))
+ as_tsktsk
+ (_("Warning: duplicated register (r%d) in register list"),
+ i);
+ else
+ range |= 1 << i;
+ }
+ in_range = 0;
+ }
+
+ if (range & (1 << reg))
+ as_tsktsk (_("Warning: duplicated register (r%d) in register list"),
+ reg);
+ else if (reg <= cur_reg)
+ as_tsktsk (_("Warning: register range not in ascending order"));
+
+ range |= 1 << reg;
+ cur_reg = reg;
+ }
+ while (skip_past_comma (&str) != FAIL
+ || (in_range = 1, *str++ == '-'));
+ str--;
+
+ if (skip_past_char (&str, '}') == FAIL)
+ {
+ first_error (_("missing `}'"));
+ return FAIL;
+ }
+ }
+ else
+ {
+ expressionS exp;
+
+ if (my_get_expression (&exp, &str, GE_NO_PREFIX))
+ return FAIL;
+
+ if (exp.X_op == O_constant)
+ {
+ if (exp.X_add_number
+ != (exp.X_add_number & 0x0000ffff))
+ {
+ inst.error = _("invalid register mask");
+ return FAIL;
+ }
+
+ if ((range & exp.X_add_number) != 0)
+ {
+ int regno = range & exp.X_add_number;
+
+ regno &= -regno;
+ regno = (1 << regno) - 1;
+ as_tsktsk
+ (_("Warning: duplicated register (r%d) in register list"),
+ regno);
+ }
+
+ range |= exp.X_add_number;
+ }
+ else
+ {
+ if (inst.reloc.type != 0)
+ {
+ inst.error = _("expression too complex");
+ return FAIL;
+ }
+
+ memcpy (&inst.reloc.exp, &exp, sizeof (expressionS));
+ inst.reloc.type = BFD_RELOC_ARM_MULTI;
+ inst.reloc.pc_rel = 0;
+ }
+ }
+
+ if (*str == '|' || *str == '+')
+ {
+ str++;
+ another_range = 1;
+ }
+ }
+ while (another_range);
+
+ *strp = str;
+ return range;
+}
+
+/* Types of registers in a list. */
+
+enum reg_list_els
+{
+ REGLIST_VFP_S,
+ REGLIST_VFP_D,
+ REGLIST_NEON_D
+};
+
+/* Parse a VFP register list. If the string is invalid return FAIL.
+ Otherwise return the number of registers, and set PBASE to the first
+ register. Parses registers of type ETYPE.
+ If REGLIST_NEON_D is used, several syntax enhancements are enabled:
+ - Q registers can be used to specify pairs of D registers
+ - { } can be omitted from around a singleton register list
+ FIXME: This is not implemented, as it would require backtracking in
+ some cases, e.g.:
+ vtbl.8 d3,d4,d5
+ This could be done (the meaning isn't really ambiguous), but doesn't
+ fit in well with the current parsing framework.
+ - 32 D registers may be used (also true for VFPv3).
+ FIXME: Types are ignored in these register lists, which is probably a
+ bug. */
+
+static int
+parse_vfp_reg_list (char **ccp, unsigned int *pbase, enum reg_list_els etype)
+{
+ char *str = *ccp;
+ int base_reg;
+ int new_base;
+ enum arm_reg_type regtype = (enum arm_reg_type) 0;
+ int max_regs = 0;
+ int count = 0;
+ int warned = 0;
+ unsigned long mask = 0;
+ int i;
+
+ if (skip_past_char (&str, '{') == FAIL)
+ {
+ inst.error = _("expecting {");
+ return FAIL;
+ }
+
+ switch (etype)
+ {
+ case REGLIST_VFP_S:
+ regtype = REG_TYPE_VFS;
+ max_regs = 32;
+ break;
+
+ case REGLIST_VFP_D:
+ regtype = REG_TYPE_VFD;
+ break;
+
+ case REGLIST_NEON_D:
+ regtype = REG_TYPE_NDQ;
+ break;
+ }
+
+ if (etype != REGLIST_VFP_S)
+ {
+ /* VFPv3 allows 32 D registers, except for the VFPv3-D16 variant. */
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_d32))
+ {
+ max_regs = 32;
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ fpu_vfp_ext_d32);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
+ fpu_vfp_ext_d32);
+ }
+ else
+ max_regs = 16;
+ }
+
+ base_reg = max_regs;
+
+ do
+ {
+ int setmask = 1, addregs = 1;
+
+ new_base = arm_typed_reg_parse (&str, regtype, &regtype, NULL);
+
+ if (new_base == FAIL)
+ {
+ first_error (_(reg_expected_msgs[regtype]));
+ return FAIL;
+ }
+
+ if (new_base >= max_regs)
+ {
+ first_error (_("register out of range in list"));
+ return FAIL;
+ }
+
+ /* Note: a value of 2 * n is returned for the register Q<n>. */
+ if (regtype == REG_TYPE_NQ)
+ {
+ setmask = 3;
+ addregs = 2;
+ }
+
+ if (new_base < base_reg)
+ base_reg = new_base;
+
+ if (mask & (setmask << new_base))
+ {
+ first_error (_("invalid register list"));
+ return FAIL;
+ }
+
+ if ((mask >> new_base) != 0 && ! warned)
+ {
+ as_tsktsk (_("register list not in ascending order"));
+ warned = 1;
+ }
+
+ mask |= setmask << new_base;
+ count += addregs;
+
+ if (*str == '-') /* We have the start of a range expression */
+ {
+ int high_range;
+
+ str++;
+
+ if ((high_range = arm_typed_reg_parse (&str, regtype, NULL, NULL))
+ == FAIL)
+ {
+ inst.error = gettext (reg_expected_msgs[regtype]);
+ return FAIL;
+ }
+
+ if (high_range >= max_regs)
+ {
+ first_error (_("register out of range in list"));
+ return FAIL;
+ }
+
+ if (regtype == REG_TYPE_NQ)
+ high_range = high_range + 1;
+
+ if (high_range <= new_base)
+ {
+ inst.error = _("register range not in ascending order");
+ return FAIL;
+ }
+
+ for (new_base += addregs; new_base <= high_range; new_base += addregs)
+ {
+ if (mask & (setmask << new_base))
+ {
+ inst.error = _("invalid register list");
+ return FAIL;
+ }
+
+ mask |= setmask << new_base;
+ count += addregs;
+ }
+ }
+ }
+ while (skip_past_comma (&str) != FAIL);
+
+ str++;
+
+ /* Sanity check -- should have raised a parse error above. */
+ if (count == 0 || count > max_regs)
+ abort ();
+
+ *pbase = base_reg;
+
+ /* Final test -- the registers must be consecutive. */
+ mask >>= base_reg;
+ for (i = 0; i < count; i++)
+ {
+ if ((mask & (1u << i)) == 0)
+ {
+ inst.error = _("non-contiguous register range");
+ return FAIL;
+ }
+ }
+
+ *ccp = str;
+
+ return count;
+}
+
+/* True if two alias types are the same. */
+
+static bfd_boolean
+neon_alias_types_same (struct neon_typed_alias *a, struct neon_typed_alias *b)
+{
+ if (!a && !b)
+ return TRUE;
+
+ if (!a || !b)
+ return FALSE;
+
+ if (a->defined != b->defined)
+ return FALSE;
+
+ if ((a->defined & NTA_HASTYPE) != 0
+ && (a->eltype.type != b->eltype.type
+ || a->eltype.size != b->eltype.size))
+ return FALSE;
+
+ if ((a->defined & NTA_HASINDEX) != 0
+ && (a->index != b->index))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Parse element/structure lists for Neon VLD<n> and VST<n> instructions.
+ The base register is put in *PBASE.
+ The lane (or one of the NEON_*_LANES constants) is placed in bits [3:0] of
+ the return value.
+ The register stride (minus one) is put in bit 4 of the return value.
+ Bits [6:5] encode the list length (minus one).
+ The type of the list elements is put in *ELTYPE, if non-NULL. */
+
+#define NEON_LANE(X) ((X) & 0xf)
+#define NEON_REG_STRIDE(X) ((((X) >> 4) & 1) + 1)
+#define NEON_REGLIST_LENGTH(X) ((((X) >> 5) & 3) + 1)
+
+static int
+parse_neon_el_struct_list (char **str, unsigned *pbase,
+ struct neon_type_el *eltype)
+{
+ char *ptr = *str;
+ int base_reg = -1;
+ int reg_incr = -1;
+ int count = 0;
+ int lane = -1;
+ int leading_brace = 0;
+ enum arm_reg_type rtype = REG_TYPE_NDQ;
+ const char *const incr_error = _("register stride must be 1 or 2");
+ const char *const type_error = _("mismatched element/structure types in list");
+ struct neon_typed_alias firsttype;
+
+ if (skip_past_char (&ptr, '{') == SUCCESS)
+ leading_brace = 1;
+
+ do
+ {
+ struct neon_typed_alias atype;
+ int getreg = parse_typed_reg_or_scalar (&ptr, rtype, &rtype, &atype);
+
+ if (getreg == FAIL)
+ {
+ first_error (_(reg_expected_msgs[rtype]));
+ return FAIL;
+ }
+
+ if (base_reg == -1)
+ {
+ base_reg = getreg;
+ if (rtype == REG_TYPE_NQ)
+ {
+ reg_incr = 1;
+ }
+ firsttype = atype;
+ }
+ else if (reg_incr == -1)
+ {
+ reg_incr = getreg - base_reg;
+ if (reg_incr < 1 || reg_incr > 2)
+ {
+ first_error (_(incr_error));
+ return FAIL;
+ }
+ }
+ else if (getreg != base_reg + reg_incr * count)
+ {
+ first_error (_(incr_error));
+ return FAIL;
+ }
+
+ if (! neon_alias_types_same (&atype, &firsttype))
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+
+ /* Handle Dn-Dm or Qn-Qm syntax. Can only be used with non-indexed list
+ modes. */
+ if (ptr[0] == '-')
+ {
+ struct neon_typed_alias htype;
+ int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1;
+ if (lane == -1)
+ lane = NEON_INTERLEAVE_LANES;
+ else if (lane != NEON_INTERLEAVE_LANES)
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ if (reg_incr == -1)
+ reg_incr = 1;
+ else if (reg_incr != 1)
+ {
+ first_error (_("don't use Rn-Rm syntax with non-unit stride"));
+ return FAIL;
+ }
+ ptr++;
+ hireg = parse_typed_reg_or_scalar (&ptr, rtype, NULL, &htype);
+ if (hireg == FAIL)
+ {
+ first_error (_(reg_expected_msgs[rtype]));
+ return FAIL;
+ }
+ if (! neon_alias_types_same (&htype, &firsttype))
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ count += hireg + dregs - getreg;
+ continue;
+ }
+
+ /* If we're using Q registers, we can't use [] or [n] syntax. */
+ if (rtype == REG_TYPE_NQ)
+ {
+ count += 2;
+ continue;
+ }
+
+ if ((atype.defined & NTA_HASINDEX) != 0)
+ {
+ if (lane == -1)
+ lane = atype.index;
+ else if (lane != atype.index)
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ }
+ else if (lane == -1)
+ lane = NEON_INTERLEAVE_LANES;
+ else if (lane != NEON_INTERLEAVE_LANES)
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ count++;
+ }
+ while ((count != 1 || leading_brace) && skip_past_comma (&ptr) != FAIL);
+
+ /* No lane set by [x]. We must be interleaving structures. */
+ if (lane == -1)
+ lane = NEON_INTERLEAVE_LANES;
+
+ /* Sanity check. */
+ if (lane == -1 || base_reg == -1 || count < 1 || count > 4
+ || (count > 1 && reg_incr == -1))
+ {
+ first_error (_("error parsing element/structure list"));
+ return FAIL;
+ }
+
+ if ((count > 1 || leading_brace) && skip_past_char (&ptr, '}') == FAIL)
+ {
+ first_error (_("expected }"));
+ return FAIL;
+ }
+
+ if (reg_incr == -1)
+ reg_incr = 1;
+
+ if (eltype)
+ *eltype = firsttype.eltype;
+
+ *pbase = base_reg;
+ *str = ptr;
+
+ return lane | ((reg_incr - 1) << 4) | ((count - 1) << 5);
+}
+
+/* Parse an explicit relocation suffix on an expression. This is
+ either nothing, or a word in parentheses. Note that if !OBJ_ELF,
+ arm_reloc_hsh contains no entries, so this function can only
+ succeed if there is no () after the word. Returns -1 on error,
+ BFD_RELOC_UNUSED if there wasn't any suffix. */
+
+static int
+parse_reloc (char **str)
+{
+ struct reloc_entry *r;
+ char *p, *q;
+
+ if (**str != '(')
+ return BFD_RELOC_UNUSED;
+
+ p = *str + 1;
+ q = p;
+
+ while (*q && *q != ')' && *q != ',')
+ q++;
+ if (*q != ')')
+ return -1;
+
+ if ((r = (struct reloc_entry *)
+ hash_find_n (arm_reloc_hsh, p, q - p)) == NULL)
+ return -1;
+
+ *str = q + 1;
+ return r->reloc;
+}
+
+/* Directives: register aliases. */
+
+static struct reg_entry *
+insert_reg_alias (char *str, unsigned number, int type)
+{
+ struct reg_entry *new_reg;
+ const char *name;
+
+ if ((new_reg = (struct reg_entry *) hash_find (arm_reg_hsh, str)) != 0)
+ {
+ if (new_reg->builtin)
+ as_warn (_("ignoring attempt to redefine built-in register '%s'"), str);
+
+ /* Only warn about a redefinition if it's not defined as the
+ same register. */
+ else if (new_reg->number != number || new_reg->type != type)
+ as_warn (_("ignoring redefinition of register alias '%s'"), str);
+
+ return NULL;
+ }
+
+ name = xstrdup (str);
+ new_reg = (struct reg_entry *) xmalloc (sizeof (struct reg_entry));
+
+ new_reg->name = name;
+ new_reg->number = number;
+ new_reg->type = type;
+ new_reg->builtin = FALSE;
+ new_reg->neon = NULL;
+
+ if (hash_insert (arm_reg_hsh, name, (void *) new_reg))
+ abort ();
+
+ return new_reg;
+}
+
+static void
+insert_neon_reg_alias (char *str, int number, int type,
+ struct neon_typed_alias *atype)
+{
+ struct reg_entry *reg = insert_reg_alias (str, number, type);
+
+ if (!reg)
+ {
+ first_error (_("attempt to redefine typed alias"));
+ return;
+ }
+
+ if (atype)
+ {
+ reg->neon = (struct neon_typed_alias *)
+ xmalloc (sizeof (struct neon_typed_alias));
+ *reg->neon = *atype;
+ }
+}
+
+/* Look for the .req directive. This is of the form:
+
+ new_register_name .req existing_register_name
+
+ If we find one, or if it looks sufficiently like one that we want to
+ handle any error here, return TRUE. Otherwise return FALSE. */
+
+static bfd_boolean
+create_register_alias (char * newname, char *p)
+{
+ struct reg_entry *old;
+ char *oldname, *nbuf;
+ size_t nlen;
+
+ /* The input scrubber ensures that whitespace after the mnemonic is
+ collapsed to single spaces. */
+ oldname = p;
+ if (strncmp (oldname, " .req ", 6) != 0)
+ return FALSE;
+
+ oldname += 6;
+ if (*oldname == '\0')
+ return FALSE;
+
+ old = (struct reg_entry *) hash_find (arm_reg_hsh, oldname);
+ if (!old)
+ {
+ as_warn (_("unknown register '%s' -- .req ignored"), oldname);
+ return TRUE;
+ }
+
+ /* If TC_CASE_SENSITIVE is defined, then newname already points to
+ the desired alias name, and p points to its end. If not, then
+ the desired alias name is in the global original_case_string. */
+#ifdef TC_CASE_SENSITIVE
+ nlen = p - newname;
+#else
+ newname = original_case_string;
+ nlen = strlen (newname);
+#endif
+
+ nbuf = (char *) alloca (nlen + 1);
+ memcpy (nbuf, newname, nlen);
+ nbuf[nlen] = '\0';
+
+ /* Create aliases under the new name as stated; an all-lowercase
+ version of the new name; and an all-uppercase version of the new
+ name. */
+ if (insert_reg_alias (nbuf, old->number, old->type) != NULL)
+ {
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
+
+ if (strncmp (nbuf, newname, nlen))
+ {
+ /* If this attempt to create an additional alias fails, do not bother
+ trying to create the all-lower case alias. We will fail and issue
+ a second, duplicate error message. This situation arises when the
+ programmer does something like:
+ foo .req r0
+ Foo .req r1
+ The second .req creates the "Foo" alias but then fails to create
+ the artificial FOO alias because it has already been created by the
+ first .req. */
+ if (insert_reg_alias (nbuf, old->number, old->type) == NULL)
+ return TRUE;
+ }
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+
+ if (strncmp (nbuf, newname, nlen))
+ insert_reg_alias (nbuf, old->number, old->type);
+ }
+
+ return TRUE;
+}
+
+/* Create a Neon typed/indexed register alias using directives, e.g.:
+ X .dn d5.s32[1]
+ Y .qn 6.s16
+ Z .dn d7
+ T .dn Z[0]
+ These typed registers can be used instead of the types specified after the
+ Neon mnemonic, so long as all operands given have types. Types can also be
+ specified directly, e.g.:
+ vadd d0.s32, d1.s32, d2.s32 */
+
+static bfd_boolean
+create_neon_reg_alias (char *newname, char *p)
+{
+ enum arm_reg_type basetype;
+ struct reg_entry *basereg;
+ struct reg_entry mybasereg;
+ struct neon_type ntype;
+ struct neon_typed_alias typeinfo;
+ char *namebuf, *nameend ATTRIBUTE_UNUSED;
+ int namelen;
+
+ typeinfo.defined = 0;
+ typeinfo.eltype.type = NT_invtype;
+ typeinfo.eltype.size = -1;
+ typeinfo.index = -1;
+
+ nameend = p;
+
+ if (strncmp (p, " .dn ", 5) == 0)
+ basetype = REG_TYPE_VFD;
+ else if (strncmp (p, " .qn ", 5) == 0)
+ basetype = REG_TYPE_NQ;
+ else
+ return FALSE;
+
+ p += 5;
+
+ if (*p == '\0')
+ return FALSE;
+
+ basereg = arm_reg_parse_multi (&p);
+
+ if (basereg && basereg->type != basetype)
+ {
+ as_bad (_("bad type for register"));
+ return FALSE;
+ }
+
+ if (basereg == NULL)
+ {
+ expressionS exp;
+ /* Try parsing as an integer. */
+ my_get_expression (&exp, &p, GE_NO_PREFIX);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expression must be constant"));
+ return FALSE;
+ }
+ basereg = &mybasereg;
+ basereg->number = (basetype == REG_TYPE_NQ) ? exp.X_add_number * 2
+ : exp.X_add_number;
+ basereg->neon = 0;
+ }
+
+ if (basereg->neon)
+ typeinfo = *basereg->neon;
+
+ if (parse_neon_type (&ntype, &p) == SUCCESS)
+ {
+ /* We got a type. */
+ if (typeinfo.defined & NTA_HASTYPE)
+ {
+ as_bad (_("can't redefine the type of a register alias"));
+ return FALSE;
+ }
+
+ typeinfo.defined |= NTA_HASTYPE;
+ if (ntype.elems != 1)
+ {
+ as_bad (_("you must specify a single type only"));
+ return FALSE;
+ }
+ typeinfo.eltype = ntype.el[0];
+ }
+
+ if (skip_past_char (&p, '[') == SUCCESS)
+ {
+ expressionS exp;
+ /* We got a scalar index. */
+
+ if (typeinfo.defined & NTA_HASINDEX)
+ {
+ as_bad (_("can't redefine the index of a scalar alias"));
+ return FALSE;
+ }
+
+ my_get_expression (&exp, &p, GE_NO_PREFIX);
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("scalar index must be constant"));
+ return FALSE;
+ }
+
+ typeinfo.defined |= NTA_HASINDEX;
+ typeinfo.index = exp.X_add_number;
+
+ if (skip_past_char (&p, ']') == FAIL)
+ {
+ as_bad (_("expecting ]"));
+ return FALSE;
+ }
+ }
+
+ /* If TC_CASE_SENSITIVE is defined, then newname already points to
+ the desired alias name, and p points to its end. If not, then
+ the desired alias name is in the global original_case_string. */
+#ifdef TC_CASE_SENSITIVE
+ namelen = nameend - newname;
+#else
+ newname = original_case_string;
+ namelen = strlen (newname);
+#endif
+
+ namebuf = (char *) alloca (namelen + 1);
+ strncpy (namebuf, newname, namelen);
+ namebuf[namelen] = '\0';
+
+ insert_neon_reg_alias (namebuf, basereg->number, basetype,
+ typeinfo.defined != 0 ? &typeinfo : NULL);
+
+ /* Insert name in all uppercase. */
+ for (p = namebuf; *p; p++)
+ *p = TOUPPER (*p);
+
+ if (strncmp (namebuf, newname, namelen))
+ insert_neon_reg_alias (namebuf, basereg->number, basetype,
+ typeinfo.defined != 0 ? &typeinfo : NULL);
+
+ /* Insert name in all lowercase. */
+ for (p = namebuf; *p; p++)
+ *p = TOLOWER (*p);
+
+ if (strncmp (namebuf, newname, namelen))
+ insert_neon_reg_alias (namebuf, basereg->number, basetype,
+ typeinfo.defined != 0 ? &typeinfo : NULL);
+
+ return TRUE;
+}
+
+/* Should never be called, as .req goes between the alias and the
+ register name, not at the beginning of the line. */
+
+static void
+s_req (int a ATTRIBUTE_UNUSED)
+{
+ as_bad (_("invalid syntax for .req directive"));
+}
+
+static void
+s_dn (int a ATTRIBUTE_UNUSED)
+{
+ as_bad (_("invalid syntax for .dn directive"));
+}
+
+static void
+s_qn (int a ATTRIBUTE_UNUSED)
+{
+ as_bad (_("invalid syntax for .qn directive"));
+}
+
+/* The .unreq directive deletes an alias which was previously defined
+ by .req. For example:
+
+ my_alias .req r11
+ .unreq my_alias */
+
+static void
+s_unreq (int a ATTRIBUTE_UNUSED)
+{
+ char * name;
+ char saved_char;
+
+ name = input_line_pointer;
+
+ while (*input_line_pointer != 0
+ && *input_line_pointer != ' '
+ && *input_line_pointer != '\n')
+ ++input_line_pointer;
+
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ if (!*name)
+ as_bad (_("invalid syntax for .unreq directive"));
+ else
+ {
+ struct reg_entry *reg = (struct reg_entry *) hash_find (arm_reg_hsh,
+ name);
+
+ if (!reg)
+ as_bad (_("unknown register alias '%s'"), name);
+ else if (reg->builtin)
+ as_warn (_("ignoring attempt to use .unreq on fixed register name: '%s'"),
+ name);
+ else
+ {
+ char * p;
+ char * nbuf;
+
+ hash_delete (arm_reg_hsh, name, FALSE);
+ free ((char *) reg->name);
+ if (reg->neon)
+ free (reg->neon);
+ free (reg);
+
+ /* Also locate the all upper case and all lower case versions.
+ Do not complain if we cannot find one or the other as it
+ was probably deleted above. */
+
+ nbuf = strdup (name);
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
+ reg = (struct reg_entry *) hash_find (arm_reg_hsh, nbuf);
+ if (reg)
+ {
+ hash_delete (arm_reg_hsh, nbuf, FALSE);
+ free ((char *) reg->name);
+ if (reg->neon)
+ free (reg->neon);
+ free (reg);
+ }
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+ reg = (struct reg_entry *) hash_find (arm_reg_hsh, nbuf);
+ if (reg)
+ {
+ hash_delete (arm_reg_hsh, nbuf, FALSE);
+ free ((char *) reg->name);
+ if (reg->neon)
+ free (reg->neon);
+ free (reg);
+ }
+
+ free (nbuf);
+ }
+ }
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+}
+
+/* Directives: Instruction set selection. */
+
+#ifdef OBJ_ELF
+/* This code is to handle mapping symbols as defined in the ARM ELF spec.
+ (See "Mapping symbols", section 4.5.5, ARM AAELF version 1.0).
+ Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
+ and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped. */
+
+/* Create a new mapping symbol for the transition to STATE. */
+
+static void
+make_mapping_symbol (enum mstate state, valueT value, fragS *frag)
+{
+ symbolS * symbolP;
+ const char * symname;
+ int type;
+
+ switch (state)
+ {
+ case MAP_DATA:
+ symname = "$d";
+ type = BSF_NO_FLAGS;
+ break;
+ case MAP_ARM:
+ symname = "$a";
+ type = BSF_NO_FLAGS;
+ break;
+ case MAP_THUMB:
+ symname = "$t";
+ type = BSF_NO_FLAGS;
+ break;
+ default:
+ abort ();
+ }
+
+ symbolP = symbol_new (symname, now_seg, value, frag);
+ symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
+
+ switch (state)
+ {
+ case MAP_ARM:
+ THUMB_SET_FUNC (symbolP, 0);
+ ARM_SET_THUMB (symbolP, 0);
+ ARM_SET_INTERWORK (symbolP, support_interwork);
+ break;
+
+ case MAP_THUMB:
+ THUMB_SET_FUNC (symbolP, 1);
+ ARM_SET_THUMB (symbolP, 1);
+ ARM_SET_INTERWORK (symbolP, support_interwork);
+ break;
+
+ case MAP_DATA:
+ default:
+ break;
+ }
+
+ /* Save the mapping symbols for future reference. Also check that
+ we do not place two mapping symbols at the same offset within a
+ frag. We'll handle overlap between frags in
+ check_mapping_symbols.
+
+ If .fill or other data filling directive generates zero sized data,
+ the mapping symbol for the following code will have the same value
+ as the one generated for the data filling directive. In this case,
+ we replace the old symbol with the new one at the same address. */
+ if (value == 0)
+ {
+ if (frag->tc_frag_data.first_map != NULL)
+ {
+ know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0);
+ symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP, &symbol_lastP);
+ }
+ frag->tc_frag_data.first_map = symbolP;
+ }
+ if (frag->tc_frag_data.last_map != NULL)
+ {
+ know (S_GET_VALUE (frag->tc_frag_data.last_map) <= S_GET_VALUE (symbolP));
+ if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP))
+ symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP, &symbol_lastP);
+ }
+ frag->tc_frag_data.last_map = symbolP;
+}
+
+/* We must sometimes convert a region marked as code to data during
+ code alignment, if an odd number of bytes have to be padded. The
+ code mapping symbol is pushed to an aligned address. */
+
+static void
+insert_data_mapping_symbol (enum mstate state,
+ valueT value, fragS *frag, offsetT bytes)
+{
+ /* If there was already a mapping symbol, remove it. */
+ if (frag->tc_frag_data.last_map != NULL
+ && S_GET_VALUE (frag->tc_frag_data.last_map) == frag->fr_address + value)
+ {
+ symbolS *symp = frag->tc_frag_data.last_map;
+
+ if (value == 0)
+ {
+ know (frag->tc_frag_data.first_map == symp);
+ frag->tc_frag_data.first_map = NULL;
+ }
+ frag->tc_frag_data.last_map = NULL;
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
+
+ make_mapping_symbol (MAP_DATA, value, frag);
+ make_mapping_symbol (state, value + bytes, frag);
+}
+
+static void mapping_state_2 (enum mstate state, int max_chars);
+
+/* Set the mapping state to STATE. Only call this when about to
+ emit some STATE bytes to the file. */
+
+void
+mapping_state (enum mstate state)
+{
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+
+#define TRANSITION(from, to) (mapstate == (from) && state == (to))
+
+ if (mapstate == state)
+ /* The mapping symbol has already been emitted.
+ There is nothing else to do. */
+ return;
+
+ if (state == MAP_ARM || state == MAP_THUMB)
+ /* PR gas/12931
+ All ARM instructions require 4-byte alignment.
+ (Almost) all Thumb instructions require 2-byte alignment.
+
+ When emitting instructions into any section, mark the section
+ appropriately.
+
+ Some Thumb instructions are alignment-sensitive modulo 4 bytes,
+ but themselves require 2-byte alignment; this applies to some
+ PC- relative forms. However, these cases will invovle implicit
+ literal pool generation or an explicit .align >=2, both of
+ which will cause the section to me marked with sufficient
+ alignment. Thus, we don't handle those cases here. */
+ record_alignment (now_seg, state == MAP_ARM ? 2 : 1);
+
+ if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
+ /* This case will be evaluated later in the next else. */
+ return;
+ else if (TRANSITION (MAP_UNDEFINED, MAP_ARM)
+ || TRANSITION (MAP_UNDEFINED, MAP_THUMB))
+ {
+ /* Only add the symbol if the offset is > 0:
+ if we're at the first frag, check it's size > 0;
+ if we're not at the first frag, then for sure
+ the offset is > 0. */
+ struct frag * const frag_first = seg_info (now_seg)->frchainP->frch_root;
+ const int add_symbol = (frag_now != frag_first) || (frag_now_fix () > 0);
+
+ if (add_symbol)
+ make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
+ }
+
+ mapping_state_2 (state, 0);
+#undef TRANSITION
+}
+
+/* Same as mapping_state, but MAX_CHARS bytes have already been
+ allocated. Put the mapping symbol that far back. */
+
+static void
+mapping_state_2 (enum mstate state, int max_chars)
+{
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+
+ if (!SEG_NORMAL (now_seg))
+ return;
+
+ if (mapstate == state)
+ /* The mapping symbol has already been emitted.
+ There is nothing else to do. */
+ return;
+
+ seg_info (now_seg)->tc_segment_info_data.mapstate = state;
+ make_mapping_symbol (state, (valueT) frag_now_fix () - max_chars, frag_now);
+}
+#else
+#define mapping_state(x) ((void)0)
+#define mapping_state_2(x, y) ((void)0)
+#endif
+
+/* Find the real, Thumb encoded start of a Thumb function. */
+
+#ifdef OBJ_COFF
+static symbolS *
+find_real_start (symbolS * symbolP)
+{
+ char * real_start;
+ const char * name = S_GET_NAME (symbolP);
+ symbolS * new_target;
+
+ /* This definition must agree with the one in gcc/config/arm/thumb.c. */
+#define STUB_NAME ".real_start_of"
+
+ if (name == NULL)
+ abort ();
+
+ /* The compiler may generate BL instructions to local labels because
+ it needs to perform a branch to a far away location. These labels
+ do not have a corresponding ".real_start_of" label. We check
+ both for S_IS_LOCAL and for a leading dot, to give a way to bypass
+ the ".real_start_of" convention for nonlocal branches. */
+ if (S_IS_LOCAL (symbolP) || name[0] == '.')
+ return symbolP;
+
+ real_start = ACONCAT ((STUB_NAME, name, NULL));
+ new_target = symbol_find (real_start);
+
+ if (new_target == NULL)
+ {
+ as_warn (_("Failed to find real start of function: %s\n"), name);
+ new_target = symbolP;
+ }
+
+ return new_target;
+}
+#endif
+
+static void
+opcode_select (int width)
+{
+ switch (width)
+ {
+ case 16:
+ if (! thumb_mode)
+ {
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v4t))
+ as_bad (_("selected processor does not support THUMB opcodes"));
+
+ thumb_mode = 1;
+ /* No need to force the alignment, since we will have been
+ coming from ARM mode, which is word-aligned. */
+ record_alignment (now_seg, 1);
+ }
+ break;
+
+ case 32:
+ if (thumb_mode)
+ {
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1))
+ as_bad (_("selected processor does not support ARM opcodes"));
+
+ thumb_mode = 0;
+
+ if (!need_pass_2)
+ frag_align (2, 0, 0);
+
+ record_alignment (now_seg, 1);
+ }
+ break;
+
+ default:
+ as_bad (_("invalid instruction size selected (%d)"), width);
+ }
+}
+
+static void
+s_arm (int ignore ATTRIBUTE_UNUSED)
+{
+ opcode_select (32);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_thumb (int ignore ATTRIBUTE_UNUSED)
+{
+ opcode_select (16);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_code (int unused ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+ temp = get_absolute_expression ();
+ switch (temp)
+ {
+ case 16:
+ case 32:
+ opcode_select (temp);
+ break;
+
+ default:
+ as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
+ }
+}
+
+static void
+s_force_thumb (int ignore ATTRIBUTE_UNUSED)
+{
+ /* If we are not already in thumb mode go into it, EVEN if
+ the target processor does not support thumb instructions.
+ This is used by gcc/config/arm/lib1funcs.asm for example
+ to compile interworking support functions even if the
+ target processor should not support interworking. */
+ if (! thumb_mode)
+ {
+ thumb_mode = 2;
+ record_alignment (now_seg, 1);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_thumb_func (int ignore ATTRIBUTE_UNUSED)
+{
+ s_thumb (0);
+
+ /* The following label is the name/address of the start of a Thumb function.
+ We need to know this for the interworking support. */
+ label_is_thumb_function_name = TRUE;
+}
+
+/* Perform a .set directive, but also mark the alias as
+ being a thumb function. */
+
+static void
+s_thumb_set (int equiv)
+{
+ /* XXX the following is a duplicate of the code for s_set() in read.c
+ We cannot just call that code as we need to get at the symbol that
+ is created. */
+ char * name;
+ char delim;
+ char * end_name;
+ symbolS * symbolP;
+
+ /* Especial apologies for the random logic:
+ This just grew, and could be parsed much more simply!
+ Dean - in haste. */
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = delim;
+
+ if (*input_line_pointer != ',')
+ {
+ *end_name = 0;
+ as_bad (_("expected comma after name \"%s\""), name);
+ *end_name = delim;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++;
+ *end_name = 0;
+
+ if (name[0] == '.' && name[1] == '\0')
+ {
+ /* XXX - this should not happen to .thumb_set. */
+ abort ();
+ }
+
+ if ((symbolP = symbol_find (name)) == NULL
+ && (symbolP = md_undefined_symbol (name)) == NULL)
+ {
+#ifndef NO_LISTING
+ /* When doing symbol listings, play games with dummy fragments living
+ outside the normal fragment chain to record the file and line info
+ for this symbol. */
+ if (listing & LISTING_SYMBOLS)
+ {
+ extern struct list_info_struct * listing_tail;
+ fragS * dummy_frag = (fragS * ) xmalloc (sizeof (fragS));
+
+ memset (dummy_frag, 0, sizeof (fragS));
+ dummy_frag->fr_type = rs_fill;
+ dummy_frag->line = listing_tail;
+ symbolP = symbol_new (name, undefined_section, 0, dummy_frag);
+ dummy_frag->fr_symbol = symbolP;
+ }
+ else
+#endif
+ symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
+
+#ifdef OBJ_COFF
+ /* "set" symbols are local unless otherwise specified. */
+ SF_SET_LOCAL (symbolP);
+#endif /* OBJ_COFF */
+ } /* Make a new symbol. */
+
+ symbol_table_insert (symbolP);
+
+ * end_name = delim;
+
+ if (equiv
+ && S_IS_DEFINED (symbolP)
+ && S_GET_SEGMENT (symbolP) != reg_section)
+ as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
+
+ pseudo_set (symbolP);
+
+ demand_empty_rest_of_line ();
+
+ /* XXX Now we come to the Thumb specific bit of code. */
+
+ THUMB_SET_FUNC (symbolP, 1);
+ ARM_SET_THUMB (symbolP, 1);
+#if defined OBJ_ELF || defined OBJ_COFF
+ ARM_SET_INTERWORK (symbolP, support_interwork);
+#endif
+}
+
+/* Directives: Mode selection. */
+
+/* .syntax [unified|divided] - choose the new unified syntax
+ (same for Arm and Thumb encoding, modulo slight differences in what
+ can be represented) or the old divergent syntax for each mode. */
+static void
+s_syntax (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, delim;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+
+ if (!strcasecmp (name, "unified"))
+ unified_syntax = TRUE;
+ else if (!strcasecmp (name, "divided"))
+ unified_syntax = FALSE;
+ else
+ {
+ as_bad (_("unrecognized syntax mode \"%s\""), name);
+ return;
+ }
+ *input_line_pointer = delim;
+ demand_empty_rest_of_line ();
+}
+
+/* Directives: sectioning and alignment. */
+
+/* Same as s_align_ptwo but align 0 => align 2. */
+
+static void
+s_align (int unused ATTRIBUTE_UNUSED)
+{
+ int temp;
+ bfd_boolean fill_p;
+ long temp_fill;
+ long max_alignment = 15;
+
+ temp = get_absolute_expression ();
+ if (temp > max_alignment)
+ as_bad (_("alignment too large: %d assumed"), temp = max_alignment);
+ else if (temp < 0)
+ {
+ as_bad (_("alignment negative. 0 assumed."));
+ temp = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ temp_fill = get_absolute_expression ();
+ fill_p = TRUE;
+ }
+ else
+ {
+ fill_p = FALSE;
+ temp_fill = 0;
+ }
+
+ if (!temp)
+ temp = 2;
+
+ /* Only make a frag if we HAVE to. */
+ if (temp && !need_pass_2)
+ {
+ if (!fill_p && subseg_text_p (now_seg))
+ frag_align_code (temp, 0);
+ else
+ frag_align (temp, (int) temp_fill, 0);
+ }
+ demand_empty_rest_of_line ();
+
+ record_alignment (now_seg, temp);
+}
+
+static void
+s_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ /* We don't support putting frags in the BSS segment, we fake it by
+ marking in_bss, then looking at s_skip for clues. */
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+static void
+s_even (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Never make frag if expect extra pass. */
+ if (!need_pass_2)
+ frag_align (1, 0, 0);
+
+ record_alignment (now_seg, 1);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Directives: CodeComposer Studio. */
+
+/* .ref (for CodeComposer Studio syntax only). */
+static void
+s_ccs_ref (int unused ATTRIBUTE_UNUSED)
+{
+ if (codecomposer_syntax)
+ ignore_rest_of_line ();
+ else
+ as_bad (_(".ref pseudo-op only available with -mccs flag."));
+}
+
+/* If name is not NULL, then it is used for marking the beginning of a
+ function, wherease if it is NULL then it means the function end. */
+static void
+asmfunc_debug (const char * name)
+{
+ static const char * last_name = NULL;
+
+ if (name != NULL)
+ {
+ gas_assert (last_name == NULL);
+ last_name = name;
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_func (name, name);
+ }
+ else
+ {
+ gas_assert (last_name != NULL);
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_endfunc (last_name, last_name);
+
+ last_name = NULL;
+ }
+}
+
+static void
+s_ccs_asmfunc (int unused ATTRIBUTE_UNUSED)
+{
+ if (codecomposer_syntax)
+ {
+ switch (asmfunc_state)
+ {
+ case OUTSIDE_ASMFUNC:
+ asmfunc_state = WAITING_ASMFUNC_NAME;
+ break;
+
+ case WAITING_ASMFUNC_NAME:
+ as_bad (_(".asmfunc repeated."));
+ break;
+
+ case WAITING_ENDASMFUNC:
+ as_bad (_(".asmfunc without function."));
+ break;
+ }
+ demand_empty_rest_of_line ();
+ }
+ else
+ as_bad (_(".asmfunc pseudo-op only available with -mccs flag."));
+}
+
+static void
+s_ccs_endasmfunc (int unused ATTRIBUTE_UNUSED)
+{
+ if (codecomposer_syntax)
+ {
+ switch (asmfunc_state)
+ {
+ case OUTSIDE_ASMFUNC:
+ as_bad (_(".endasmfunc without a .asmfunc."));
+ break;
+
+ case WAITING_ASMFUNC_NAME:
+ as_bad (_(".endasmfunc without function."));
+ break;
+
+ case WAITING_ENDASMFUNC:
+ asmfunc_state = OUTSIDE_ASMFUNC;
+ asmfunc_debug (NULL);
+ break;
+ }
+ demand_empty_rest_of_line ();
+ }
+ else
+ as_bad (_(".endasmfunc pseudo-op only available with -mccs flag."));
+}
+
+static void
+s_ccs_def (int name)
+{
+ if (codecomposer_syntax)
+ s_globl (name);
+ else
+ as_bad (_(".def pseudo-op only available with -mccs flag."));
+}
+
+/* Directives: Literal pools. */
+
+static literal_pool *
+find_literal_pool (void)
+{
+ literal_pool * pool;
+
+ for (pool = list_of_pools; pool != NULL; pool = pool->next)
+ {
+ if (pool->section == now_seg
+ && pool->sub_section == now_subseg)
+ break;
+ }
+
+ return pool;
+}
+
+static literal_pool *
+find_or_make_literal_pool (void)
+{
+ /* Next literal pool ID number. */
+ static unsigned int latest_pool_num = 1;
+ literal_pool * pool;
+
+ pool = find_literal_pool ();
+
+ if (pool == NULL)
+ {
+ /* Create a new pool. */
+ pool = (literal_pool *) xmalloc (sizeof (* pool));
+ if (! pool)
+ return NULL;
+
+ pool->next_free_entry = 0;
+ pool->section = now_seg;
+ pool->sub_section = now_subseg;
+ pool->next = list_of_pools;
+ pool->symbol = NULL;
+ pool->alignment = 2;
+
+ /* Add it to the list. */
+ list_of_pools = pool;
+ }
+
+ /* New pools, and emptied pools, will have a NULL symbol. */
+ if (pool->symbol == NULL)
+ {
+ pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ pool->id = latest_pool_num ++;
+ }
+
+ /* Done. */
+ return pool;
+}
+
+/* Add the literal in the global 'inst'
+ structure to the relevant literal pool. */
+
+static int
+add_to_lit_pool (unsigned int nbytes)
+{
+#define PADDING_SLOT 0x1
+#define LIT_ENTRY_SIZE_MASK 0xFF
+ literal_pool * pool;
+ unsigned int entry, pool_size = 0;
+ bfd_boolean padding_slot_p = FALSE;
+ unsigned imm1 = 0;
+ unsigned imm2 = 0;
+
+ if (nbytes == 8)
+ {
+ imm1 = inst.operands[1].imm;
+ imm2 = (inst.operands[1].regisimm ? inst.operands[1].reg
+ : inst.reloc.exp.X_unsigned ? 0
+ : ((bfd_int64_t) inst.operands[1].imm) >> 32);
+ if (target_big_endian)
+ {
+ imm1 = imm2;
+ imm2 = inst.operands[1].imm;
+ }
+ }
+
+ pool = find_or_make_literal_pool ();
+
+ /* Check if this literal value is already in the pool. */
+ for (entry = 0; entry < pool->next_free_entry; entry ++)
+ {
+ if (nbytes == 4)
+ {
+ if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+ && (inst.reloc.exp.X_op == O_constant)
+ && (pool->literals[entry].X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (pool->literals[entry].X_md == nbytes)
+ && (pool->literals[entry].X_unsigned
+ == inst.reloc.exp.X_unsigned))
+ break;
+
+ if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+ && (inst.reloc.exp.X_op == O_symbol)
+ && (pool->literals[entry].X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (pool->literals[entry].X_add_symbol
+ == inst.reloc.exp.X_add_symbol)
+ && (pool->literals[entry].X_op_symbol
+ == inst.reloc.exp.X_op_symbol)
+ && (pool->literals[entry].X_md == nbytes))
+ break;
+ }
+ else if ((nbytes == 8)
+ && !(pool_size & 0x7)
+ && ((entry + 1) != pool->next_free_entry)
+ && (pool->literals[entry].X_op == O_constant)
+ && (pool->literals[entry].X_add_number == (offsetT) imm1)
+ && (pool->literals[entry].X_unsigned
+ == inst.reloc.exp.X_unsigned)
+ && (pool->literals[entry + 1].X_op == O_constant)
+ && (pool->literals[entry + 1].X_add_number == (offsetT) imm2)
+ && (pool->literals[entry + 1].X_unsigned
+ == inst.reloc.exp.X_unsigned))
+ break;
+
+ padding_slot_p = ((pool->literals[entry].X_md >> 8) == PADDING_SLOT);
+ if (padding_slot_p && (nbytes == 4))
+ break;
+
+ pool_size += 4;
+ }
+
+ /* Do we need to create a new entry? */
+ if (entry == pool->next_free_entry)
+ {
+ if (entry >= MAX_LITERAL_POOL_SIZE)
+ {
+ inst.error = _("literal pool overflow");
+ return FAIL;
+ }
+
+ if (nbytes == 8)
+ {
+ /* For 8-byte entries, we align to an 8-byte boundary,
+ and split it into two 4-byte entries, because on 32-bit
+ host, 8-byte constants are treated as big num, thus
+ saved in "generic_bignum" which will be overwritten
+ by later assignments.
+
+ We also need to make sure there is enough space for
+ the split.
+
+ We also check to make sure the literal operand is a
+ constant number. */
+ if (!(inst.reloc.exp.X_op == O_constant
+ || inst.reloc.exp.X_op == O_big))
+ {
+ inst.error = _("invalid type for literal pool");
+ return FAIL;
+ }
+ else if (pool_size & 0x7)
+ {
+ if ((entry + 2) >= MAX_LITERAL_POOL_SIZE)
+ {
+ inst.error = _("literal pool overflow");
+ return FAIL;
+ }
+
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_add_number = 0;
+ pool->literals[entry++].X_md = (PADDING_SLOT << 8) | 4;
+ pool->next_free_entry += 1;
+ pool_size += 4;
+ }
+ else if ((entry + 1) >= MAX_LITERAL_POOL_SIZE)
+ {
+ inst.error = _("literal pool overflow");
+ return FAIL;
+ }
+
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_op = O_constant;
+ pool->literals[entry].X_add_number = imm1;
+ pool->literals[entry].X_unsigned = inst.reloc.exp.X_unsigned;
+ pool->literals[entry++].X_md = 4;
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_op = O_constant;
+ pool->literals[entry].X_add_number = imm2;
+ pool->literals[entry].X_unsigned = inst.reloc.exp.X_unsigned;
+ pool->literals[entry].X_md = 4;
+ pool->alignment = 3;
+ pool->next_free_entry += 1;
+ }
+ else
+ {
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_md = 4;
+ }
+
+#ifdef OBJ_ELF
+ /* PR ld/12974: Record the location of the first source line to reference
+ this entry in the literal pool. If it turns out during linking that the
+ symbol does not exist we will be able to give an accurate line number for
+ the (first use of the) missing reference. */
+ if (debug_type == DEBUG_DWARF2)
+ dwarf2_where (pool->locs + entry);
+#endif
+ pool->next_free_entry += 1;
+ }
+ else if (padding_slot_p)
+ {
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_md = nbytes;
+ }
+
+ inst.reloc.exp.X_op = O_symbol;
+ inst.reloc.exp.X_add_number = pool_size;
+ inst.reloc.exp.X_add_symbol = pool->symbol;
+
+ return SUCCESS;
+}
+
+bfd_boolean
+tc_start_label_without_colon (char unused1 ATTRIBUTE_UNUSED, const char * rest)
+{
+ bfd_boolean ret = TRUE;
+
+ if (codecomposer_syntax && asmfunc_state == WAITING_ASMFUNC_NAME)
+ {
+ const char *label = rest;
+
+ while (!is_end_of_line[(int) label[-1]])
+ --label;
+
+ if (*label == '.')
+ {
+ as_bad (_("Invalid label '%s'"), label);
+ ret = FALSE;
+ }
+
+ asmfunc_debug (label);
+
+ asmfunc_state = WAITING_ENDASMFUNC;
+ }
+
+ return ret;
+}
+
+/* Can't use symbol_new here, so have to create a symbol and then at
+ a later date assign it a value. Thats what these functions do. */
+
+static void
+symbol_locate (symbolS * symbolP,
+ const char * name, /* It is copied, the caller can modify. */
+ segT segment, /* Segment identifier (SEG_<something>). */
+ valueT valu, /* Symbol value. */
+ fragS * frag) /* Associated fragment. */
+{
+ size_t name_length;
+ char * preserved_copy_of_name;
+
+ name_length = strlen (name) + 1; /* +1 for \0. */
+ obstack_grow (&notes, name, name_length);
+ preserved_copy_of_name = (char *) obstack_finish (&notes);
+
+#ifdef tc_canonicalize_symbol_name
+ preserved_copy_of_name =
+ tc_canonicalize_symbol_name (preserved_copy_of_name);
+#endif
+
+ S_SET_NAME (symbolP, preserved_copy_of_name);
+
+ S_SET_SEGMENT (symbolP, segment);
+ S_SET_VALUE (symbolP, valu);
+ symbol_clear_list_pointers (symbolP);
+
+ symbol_set_frag (symbolP, frag);
+
+ /* Link to end of symbol chain. */
+ {
+ extern int symbol_table_frozen;
+
+ if (symbol_table_frozen)
+ abort ();
+ }
+
+ symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
+
+ obj_symbol_new_hook (symbolP);
+
+#ifdef tc_symbol_new_hook
+ tc_symbol_new_hook (symbolP);
+#endif
+
+#ifdef DEBUG_SYMS
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS */
+}
+
+static void
+s_ltorg (int ignored ATTRIBUTE_UNUSED)
+{
+ unsigned int entry;
+ literal_pool * pool;
+ char sym_name[20];
+
+ pool = find_literal_pool ();
+ if (pool == NULL
+ || pool->symbol == NULL
+ || pool->next_free_entry == 0)
+ return;
+
+ /* Align pool as you have word accesses.
+ Only make a frag if we have to. */
+ if (!need_pass_2)
+ frag_align (pool->alignment, 0, 0);
+
+ record_alignment (now_seg, 2);
+
+#ifdef OBJ_ELF
+ seg_info (now_seg)->tc_segment_info_data.mapstate = MAP_DATA;
+ make_mapping_symbol (MAP_DATA, (valueT) frag_now_fix (), frag_now);
+#endif
+ sprintf (sym_name, "$$lit_\002%x", pool->id);
+
+ symbol_locate (pool->symbol, sym_name, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ symbol_table_insert (pool->symbol);
+
+ ARM_SET_THUMB (pool->symbol, thumb_mode);
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ ARM_SET_INTERWORK (pool->symbol, support_interwork);
+#endif
+
+ for (entry = 0; entry < pool->next_free_entry; entry ++)
+ {
+#ifdef OBJ_ELF
+ if (debug_type == DEBUG_DWARF2)
+ dwarf2_gen_line_info (frag_now_fix (), pool->locs + entry);
+#endif
+ /* First output the expression in the instruction to the pool. */
+ emit_expr (&(pool->literals[entry]),
+ pool->literals[entry].X_md & LIT_ENTRY_SIZE_MASK);
+ }
+
+ /* Mark the pool as empty. */
+ pool->next_free_entry = 0;
+ pool->symbol = NULL;
+}
+
+#ifdef OBJ_ELF
+/* Forward declarations for functions below, in the MD interface
+ section. */
+static void fix_new_arm (fragS *, int, short, expressionS *, int, int);
+static valueT create_unwind_entry (int);
+static void start_unwind_section (const segT, int);
+static void add_unwind_opcode (valueT, int);
+static void flush_pending_unwind (void);
+
+/* Directives: Data. */
+
+static void
+s_arm_elf_cons (int nbytes)
+{
+ expressionS exp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ mapping_state (MAP_DATA);
+ do
+ {
+ int reloc;
+ char *base = input_line_pointer;
+
+ expression (& exp);
+
+ if (exp.X_op != O_symbol)
+ emit_expr (&exp, (unsigned int) nbytes);
+ else
+ {
+ char *before_reloc = input_line_pointer;
+ reloc = parse_reloc (&input_line_pointer);
+ if (reloc == -1)
+ {
+ as_bad (_("unrecognized relocation suffix"));
+ ignore_rest_of_line ();
+ return;
+ }
+ else if (reloc == BFD_RELOC_UNUSED)
+ emit_expr (&exp, (unsigned int) nbytes);
+ else
+ {
+ reloc_howto_type *howto = (reloc_howto_type *)
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) reloc);
+ int size = bfd_get_reloc_size (howto);
+
+ if (reloc == BFD_RELOC_ARM_PLT32)
+ {
+ as_bad (_("(plt) is only valid on branch targets"));
+ reloc = BFD_RELOC_UNUSED;
+ size = 0;
+ }
+
+ if (size > nbytes)
+ as_bad (_("%s relocations do not fit in %d bytes"),
+ howto->name, nbytes);
+ else
+ {
+ /* We've parsed an expression stopping at O_symbol.
+ But there may be more expression left now that we
+ have parsed the relocation marker. Parse it again.
+ XXX Surely there is a cleaner way to do this. */
+ char *p = input_line_pointer;
+ int offset;
+ char *save_buf = (char *) alloca (input_line_pointer - base);
+ memcpy (save_buf, base, input_line_pointer - base);
+ memmove (base + (input_line_pointer - before_reloc),
+ base, before_reloc - base);
+
+ input_line_pointer = base + (input_line_pointer-before_reloc);
+ expression (&exp);
+ memcpy (base, save_buf, p - base);
+
+ offset = nbytes - size;
+ p = frag_more (nbytes);
+ memset (p, 0, nbytes);
+ fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
+ size, &exp, 0, (enum bfd_reloc_code_real) reloc);
+ }
+ }
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Put terminator back into stream. */
+ input_line_pointer --;
+ demand_empty_rest_of_line ();
+}
+
+/* Emit an expression containing a 32-bit thumb instruction.
+ Implementation based on put_thumb32_insn. */
+
+static void
+emit_thumb32_expr (expressionS * exp)
+{
+ expressionS exp_high = *exp;
+
+ exp_high.X_add_number = (unsigned long)exp_high.X_add_number >> 16;
+ emit_expr (& exp_high, (unsigned int) THUMB_SIZE);
+ exp->X_add_number &= 0xffff;
+ emit_expr (exp, (unsigned int) THUMB_SIZE);
+}
+
+/* Guess the instruction size based on the opcode. */
+
+static int
+thumb_insn_size (int opcode)
+{
+ if ((unsigned int) opcode < 0xe800u)
+ return 2;
+ else if ((unsigned int) opcode >= 0xe8000000u)
+ return 4;
+ else
+ return 0;
+}
+
+static bfd_boolean
+emit_insn (expressionS *exp, int nbytes)
+{
+ int size = 0;
+
+ if (exp->X_op == O_constant)
+ {
+ size = nbytes;
+
+ if (size == 0)
+ size = thumb_insn_size (exp->X_add_number);
+
+ if (size != 0)
+ {
+ if (size == 2 && (unsigned int)exp->X_add_number > 0xffffu)
+ {
+ as_bad (_(".inst.n operand too big. "\
+ "Use .inst.w instead"));
+ size = 0;
+ }
+ else
+ {
+ if (now_it.state == AUTOMATIC_IT_BLOCK)
+ set_it_insn_type_nonvoid (OUTSIDE_IT_INSN, 0);
+ else
+ set_it_insn_type_nonvoid (NEUTRAL_IT_INSN, 0);
+
+ if (thumb_mode && (size > THUMB_SIZE) && !target_big_endian)
+ emit_thumb32_expr (exp);
+ else
+ emit_expr (exp, (unsigned int) size);
+
+ it_fsm_post_encode ();
+ }
+ }
+ else
+ as_bad (_("cannot determine Thumb instruction size. " \
+ "Use .inst.n/.inst.w instead"));
+ }
+ else
+ as_bad (_("constant expression required"));
+
+ return (size != 0);
+}
+
+/* Like s_arm_elf_cons but do not use md_cons_align and
+ set the mapping state to MAP_ARM/MAP_THUMB. */
+
+static void
+s_arm_elf_inst (int nbytes)
+{
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ /* Calling mapping_state () here will not change ARM/THUMB,
+ but will ensure not to be in DATA state. */
+
+ if (thumb_mode)
+ mapping_state (MAP_THUMB);
+ else
+ {
+ if (nbytes != 0)
+ {
+ as_bad (_("width suffixes are invalid in ARM mode"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ nbytes = 4;
+
+ mapping_state (MAP_ARM);
+ }
+
+ do
+ {
+ expressionS exp;
+
+ expression (& exp);
+
+ if (! emit_insn (& exp, nbytes))
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Put terminator back into stream. */
+ input_line_pointer --;
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a .rel31 directive. */
+
+static void
+s_arm_rel31 (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ char *p;
+ valueT highbit;
+
+ highbit = 0;
+ if (*input_line_pointer == '1')
+ highbit = 0x80000000;
+ else if (*input_line_pointer != '0')
+ as_bad (_("expected 0 or 1"));
+
+ input_line_pointer++;
+ if (*input_line_pointer != ',')
+ as_bad (_("missing comma"));
+ input_line_pointer++;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+ md_cons_align (4);
+#endif
+
+ mapping_state (MAP_DATA);
+
+ expression (&exp);
+
+ p = frag_more (4);
+ md_number_to_chars (p, highbit, 4);
+ fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Directives: AEABI stack-unwind tables. */
+
+/* Parse an unwind_fnstart directive. Simply records the current location. */
+
+static void
+s_arm_unwind_fnstart (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (unwind.proc_start)
+ {
+ as_bad (_("duplicate .fnstart directive"));
+ return;
+ }
+
+ /* Mark the start of the function. */
+ unwind.proc_start = expr_build_dot ();
+
+ /* Reset the rest of the unwind info. */
+ unwind.opcode_count = 0;
+ unwind.table_entry = NULL;
+ unwind.personality_routine = NULL;
+ unwind.personality_index = -1;
+ unwind.frame_size = 0;
+ unwind.fp_offset = 0;
+ unwind.fp_reg = REG_SP;
+ unwind.fp_used = 0;
+ unwind.sp_restored = 0;
+}
+
+
+/* Parse a handlerdata directive. Creates the exception handling table entry
+ for the function. */
+
+static void
+s_arm_unwind_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ if (unwind.table_entry)
+ as_bad (_("duplicate .handlerdata directive"));
+
+ create_unwind_entry (1);
+}
+
+/* Parse an unwind_fnend directive. Generates the index table entry. */
+
+static void
+s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED)
+{
+ long where;
+ char *ptr;
+ valueT val;
+ unsigned int marked_pr_dependency;
+
+ demand_empty_rest_of_line ();
+
+ if (!unwind.proc_start)
+ {
+ as_bad (_(".fnend directive without .fnstart"));
+ return;
+ }
+
+ /* Add eh table entry. */
+ if (unwind.table_entry == NULL)
+ val = create_unwind_entry (0);
+ else
+ val = 0;
+
+ /* Add index table entry. This is two words. */
+ start_unwind_section (unwind.saved_seg, 1);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+ ptr = frag_more (8);
+ memset (ptr, 0, 8);
+ where = frag_now_fix () - 8;
+
+ /* Self relative offset of the function start. */
+ fix_new (frag_now, where, 4, unwind.proc_start, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ /* Indicate dependency on EHABI-defined personality routines to the
+ linker, if it hasn't been done already. */
+ marked_pr_dependency
+ = seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency;
+ if (unwind.personality_index >= 0 && unwind.personality_index < 3
+ && !(marked_pr_dependency & (1 << unwind.personality_index)))
+ {
+ static const char *const name[] =
+ {
+ "__aeabi_unwind_cpp_pr0",
+ "__aeabi_unwind_cpp_pr1",
+ "__aeabi_unwind_cpp_pr2"
+ };
+ symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
+ fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+ seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+ |= 1 << unwind.personality_index;
+ }
+
+ if (val)
+ /* Inline exception table entry. */
+ md_number_to_chars (ptr + 4, val, 4);
+ else
+ /* Self relative offset of the table entry. */
+ fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ /* Restore the original section. */
+ subseg_set (unwind.saved_seg, unwind.saved_subseg);
+
+ unwind.proc_start = NULL;
+}
+
+
+/* Parse an unwind_cantunwind directive. */
+
+static void
+s_arm_unwind_cantunwind (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("personality routine specified for cantunwind frame"));
+
+ unwind.personality_index = -2;
+}
+
+
+/* Parse a personalityindex directive. */
+
+static void
+s_arm_unwind_personalityindex (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("duplicate .personalityindex directive"));
+
+ expression (&exp);
+
+ if (exp.X_op != O_constant
+ || exp.X_add_number < 0 || exp.X_add_number > 15)
+ {
+ as_bad (_("bad personality routine number"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ unwind.personality_index = exp.X_add_number;
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse a personality directive. */
+
+static void
+s_arm_unwind_personality (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name, *p, c;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("duplicate .personality directive"));
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ unwind.personality_routine = symbol_find_or_make (name);
+ *p = c;
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse a directive saving core registers. */
+
+static void
+s_arm_unwind_save_core (void)
+{
+ valueT op;
+ long range;
+ int n;
+
+ range = parse_reg_list (&input_line_pointer);
+ if (range == FAIL)
+ {
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ /* Turn .unwind_movsp ip followed by .unwind_save {..., ip, ...}
+ into .unwind_save {..., sp...}. We aren't bothered about the value of
+ ip because it is clobbered by calls. */
+ if (unwind.sp_restored && unwind.fp_reg == 12
+ && (range & 0x3000) == 0x1000)
+ {
+ unwind.opcode_count--;
+ unwind.sp_restored = 0;
+ range = (range | 0x2000) & ~0x1000;
+ unwind.pending_offset = 0;
+ }
+
+ /* Pop r4-r15. */
+ if (range & 0xfff0)
+ {
+ /* See if we can use the short opcodes. These pop a block of up to 8
+ registers starting with r4, plus maybe r14. */
+ for (n = 0; n < 8; n++)
+ {
+ /* Break at the first non-saved register. */
+ if ((range & (1 << (n + 4))) == 0)
+ break;
+ }
+ /* See if there are any other bits set. */
+ if (n == 0 || (range & (0xfff0 << n) & 0xbff0) != 0)
+ {
+ /* Use the long form. */
+ op = 0x8000 | ((range >> 4) & 0xfff);
+ add_unwind_opcode (op, 2);
+ }
+ else
+ {
+ /* Use the short form. */
+ if (range & 0x4000)
+ op = 0xa8; /* Pop r14. */
+ else
+ op = 0xa0; /* Do not pop r14. */
+ op |= (n - 1);
+ add_unwind_opcode (op, 1);
+ }
+ }
+
+ /* Pop r0-r3. */
+ if (range & 0xf)
+ {
+ op = 0xb100 | (range & 0xf);
+ add_unwind_opcode (op, 2);
+ }
+
+ /* Record the number of bytes pushed. */
+ for (n = 0; n < 16; n++)
+ {
+ if (range & (1 << n))
+ unwind.frame_size += 4;
+ }
+}
+
+
+/* Parse a directive saving FPA registers. */
+
+static void
+s_arm_unwind_save_fpa (int reg)
+{
+ expressionS exp;
+ int num_regs;
+ valueT op;
+
+ /* Get Number of registers to transfer. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ expression (&exp);
+ else
+ exp.X_op = O_illegal;
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected , <constant>"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ num_regs = exp.X_add_number;
+
+ if (num_regs < 1 || num_regs > 4)
+ {
+ as_bad (_("number of registers must be in the range [1:4]"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (reg == 4)
+ {
+ /* Short form. */
+ op = 0xb4 | (num_regs - 1);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xc800 | (reg << 4) | (num_regs - 1);
+ add_unwind_opcode (op, 2);
+ }
+ unwind.frame_size += num_regs * 12;
+}
+
+
+/* Parse a directive saving VFP registers for ARMv6 and above. */
+
+static void
+s_arm_unwind_save_vfp_armv6 (void)
+{
+ int count;
+ unsigned int start;
+ valueT op;
+ int num_vfpv3_regs = 0;
+ int num_regs_below_16;
+
+ count = parse_vfp_reg_list (&input_line_pointer, &start, REGLIST_VFP_D);
+ if (count == FAIL)
+ {
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ /* We always generate FSTMD/FLDMD-style unwinding opcodes (rather
+ than FSTMX/FLDMX-style ones). */
+
+ /* Generate opcode for (VFPv3) registers numbered in the range 16 .. 31. */
+ if (start >= 16)
+ num_vfpv3_regs = count;
+ else if (start + count > 16)
+ num_vfpv3_regs = start + count - 16;
+
+ if (num_vfpv3_regs > 0)
+ {
+ int start_offset = start > 16 ? start - 16 : 0;
+ op = 0xc800 | (start_offset << 4) | (num_vfpv3_regs - 1);
+ add_unwind_opcode (op, 2);
+ }
+
+ /* Generate opcode for registers numbered in the range 0 .. 15. */
+ num_regs_below_16 = num_vfpv3_regs > 0 ? 16 - (int) start : count;
+ gas_assert (num_regs_below_16 + num_vfpv3_regs == count);
+ if (num_regs_below_16 > 0)
+ {
+ op = 0xc900 | (start << 4) | (num_regs_below_16 - 1);
+ add_unwind_opcode (op, 2);
+ }
+
+ unwind.frame_size += count * 8;
+}
+
+
+/* Parse a directive saving VFP registers for pre-ARMv6. */
+
+static void
+s_arm_unwind_save_vfp (void)
+{
+ int count;
+ unsigned int reg;
+ valueT op;
+
+ count = parse_vfp_reg_list (&input_line_pointer, &reg, REGLIST_VFP_D);
+ if (count == FAIL)
+ {
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (reg == 8)
+ {
+ /* Short form. */
+ op = 0xb8 | (count - 1);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xb300 | (reg << 4) | (count - 1);
+ add_unwind_opcode (op, 2);
+ }
+ unwind.frame_size += count * 8 + 4;
+}
+
+
+/* Parse a directive saving iWMMXt data registers. */
+
+static void
+s_arm_unwind_save_mmxwr (void)
+{
+ int reg;
+ int hi_reg;
+ int i;
+ unsigned mask = 0;
+ valueT op;
+
+ if (*input_line_pointer == '{')
+ input_line_pointer++;
+
+ do
+ {
+ reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
+
+ if (reg == FAIL)
+ {
+ as_bad ("%s", _(reg_expected_msgs[REG_TYPE_MMXWR]));
+ goto error;
+ }
+
+ if (mask >> reg)
+ as_tsktsk (_("register list not in ascending order"));
+ mask |= 1 << reg;
+
+ if (*input_line_pointer == '-')
+ {
+ input_line_pointer++;
+ hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
+ if (hi_reg == FAIL)
+ {
+ as_bad ("%s", _(reg_expected_msgs[REG_TYPE_MMXWR]));
+ goto error;
+ }
+ else if (reg >= hi_reg)
+ {
+ as_bad (_("bad register range"));
+ goto error;
+ }
+ for (; reg < hi_reg; reg++)
+ mask |= 1 << reg;
+ }
+ }
+ while (skip_past_comma (&input_line_pointer) != FAIL);
+
+ skip_past_char (&input_line_pointer, '}');
+
+ demand_empty_rest_of_line ();
+
+ /* Generate any deferred opcodes because we're going to be looking at
+ the list. */
+ flush_pending_unwind ();
+
+ for (i = 0; i < 16; i++)
+ {
+ if (mask & (1 << i))
+ unwind.frame_size += 8;
+ }
+
+ /* Attempt to combine with a previous opcode. We do this because gcc
+ likes to output separate unwind directives for a single block of
+ registers. */
+ if (unwind.opcode_count > 0)
+ {
+ i = unwind.opcodes[unwind.opcode_count - 1];
+ if ((i & 0xf8) == 0xc0)
+ {
+ i &= 7;
+ /* Only merge if the blocks are contiguous. */
+ if (i < 6)
+ {
+ if ((mask & 0xfe00) == (1 << 9))
+ {
+ mask |= ((1 << (i + 11)) - 1) & 0xfc00;
+ unwind.opcode_count--;
+ }
+ }
+ else if (i == 6 && unwind.opcode_count >= 2)
+ {
+ i = unwind.opcodes[unwind.opcode_count - 2];
+ reg = i >> 4;
+ i &= 0xf;
+
+ op = 0xffff << (reg - 1);
+ if (reg > 0
+ && ((mask & op) == (1u << (reg - 1))))
+ {
+ op = (1 << (reg + i + 1)) - 1;
+ op &= ~((1 << reg) - 1);
+ mask |= op;
+ unwind.opcode_count -= 2;
+ }
+ }
+ }
+ }
+
+ hi_reg = 15;
+ /* We want to generate opcodes in the order the registers have been
+ saved, ie. descending order. */
+ for (reg = 15; reg >= -1; reg--)
+ {
+ /* Save registers in blocks. */
+ if (reg < 0
+ || !(mask & (1 << reg)))
+ {
+ /* We found an unsaved reg. Generate opcodes to save the
+ preceding block. */
+ if (reg != hi_reg)
+ {
+ if (reg == 9)
+ {
+ /* Short form. */
+ op = 0xc0 | (hi_reg - 10);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xc600 | ((reg + 1) << 4) | ((hi_reg - reg) - 1);
+ add_unwind_opcode (op, 2);
+ }
+ }
+ hi_reg = reg - 1;
+ }
+ }
+
+ return;
+error:
+ ignore_rest_of_line ();
+}
+
+static void
+s_arm_unwind_save_mmxwcg (void)
+{
+ int reg;
+ int hi_reg;
+ unsigned mask = 0;
+ valueT op;
+
+ if (*input_line_pointer == '{')
+ input_line_pointer++;
+
+ skip_whitespace (input_line_pointer);
+
+ do
+ {
+ reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
+
+ if (reg == FAIL)
+ {
+ as_bad ("%s", _(reg_expected_msgs[REG_TYPE_MMXWCG]));
+ goto error;
+ }
+
+ reg -= 8;
+ if (mask >> reg)
+ as_tsktsk (_("register list not in ascending order"));
+ mask |= 1 << reg;
+
+ if (*input_line_pointer == '-')
+ {
+ input_line_pointer++;
+ hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
+ if (hi_reg == FAIL)
+ {
+ as_bad ("%s", _(reg_expected_msgs[REG_TYPE_MMXWCG]));
+ goto error;
+ }
+ else if (reg >= hi_reg)
+ {
+ as_bad (_("bad register range"));
+ goto error;
+ }
+ for (; reg < hi_reg; reg++)
+ mask |= 1 << reg;
+ }
+ }
+ while (skip_past_comma (&input_line_pointer) != FAIL);
+
+ skip_past_char (&input_line_pointer, '}');
+
+ demand_empty_rest_of_line ();
+
+ /* Generate any deferred opcodes because we're going to be looking at
+ the list. */
+ flush_pending_unwind ();
+
+ for (reg = 0; reg < 16; reg++)
+ {
+ if (mask & (1 << reg))
+ unwind.frame_size += 4;
+ }
+ op = 0xc700 | mask;
+ add_unwind_opcode (op, 2);
+ return;
+error:
+ ignore_rest_of_line ();
+}
+
+
+/* Parse an unwind_save directive.
+ If the argument is non-zero, this is a .vsave directive. */
+
+static void
+s_arm_unwind_save (int arch_v6)
+{
+ char *peek;
+ struct reg_entry *reg;
+ bfd_boolean had_brace = FALSE;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ /* Figure out what sort of save we have. */
+ peek = input_line_pointer;
+
+ if (*peek == '{')
+ {
+ had_brace = TRUE;
+ peek++;
+ }
+
+ reg = arm_reg_parse_multi (&peek);
+
+ if (!reg)
+ {
+ as_bad (_("register expected"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ switch (reg->type)
+ {
+ case REG_TYPE_FN:
+ if (had_brace)
+ {
+ as_bad (_("FPA .unwind_save does not take a register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer = peek;
+ s_arm_unwind_save_fpa (reg->number);
+ return;
+
+ case REG_TYPE_RN:
+ s_arm_unwind_save_core ();
+ return;
+
+ case REG_TYPE_VFD:
+ if (arch_v6)
+ s_arm_unwind_save_vfp_armv6 ();
+ else
+ s_arm_unwind_save_vfp ();
+ return;
+
+ case REG_TYPE_MMXWR:
+ s_arm_unwind_save_mmxwr ();
+ return;
+
+ case REG_TYPE_MMXWCG:
+ s_arm_unwind_save_mmxwcg ();
+ return;
+
+ default:
+ as_bad (_(".unwind_save does not support this kind of register"));
+ ignore_rest_of_line ();
+ }
+}
+
+
+/* Parse an unwind_movsp directive. */
+
+static void
+s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
+{
+ int reg;
+ valueT op;
+ int offset;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+ if (reg == FAIL)
+ {
+ as_bad ("%s", _(reg_expected_msgs[REG_TYPE_RN]));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Optional constant. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ {
+ if (immediate_for_directive (&offset) == FAIL)
+ return;
+ }
+ else
+ offset = 0;
+
+ demand_empty_rest_of_line ();
+
+ if (reg == REG_SP || reg == REG_PC)
+ {
+ as_bad (_("SP and PC not permitted in .unwind_movsp directive"));
+ return;
+ }
+
+ if (unwind.fp_reg != REG_SP)
+ as_bad (_("unexpected .unwind_movsp directive"));
+
+ /* Generate opcode to restore the value. */
+ op = 0x90 | reg;
+ add_unwind_opcode (op, 1);
+
+ /* Record the information for later. */
+ unwind.fp_reg = reg;
+ unwind.fp_offset = unwind.frame_size - offset;
+ unwind.sp_restored = 1;
+}
+
+/* Parse an unwind_pad directive. */
+
+static void
+s_arm_unwind_pad (int ignored ATTRIBUTE_UNUSED)
+{
+ int offset;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ if (immediate_for_directive (&offset) == FAIL)
+ return;
+
+ if (offset & 3)
+ {
+ as_bad (_("stack increment must be multiple of 4"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Don't generate any opcodes, just record the details for later. */
+ unwind.frame_size += offset;
+ unwind.pending_offset += offset;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse an unwind_setfp directive. */
+
+static void
+s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
+{
+ int sp_reg;
+ int fp_reg;
+ int offset;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ fp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ sp_reg = FAIL;
+ else
+ sp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+
+ if (fp_reg == FAIL || sp_reg == FAIL)
+ {
+ as_bad (_("expected <reg>, <reg>"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Optional constant. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ {
+ if (immediate_for_directive (&offset) == FAIL)
+ return;
+ }
+ else
+ offset = 0;
+
+ demand_empty_rest_of_line ();
+
+ if (sp_reg != REG_SP && sp_reg != unwind.fp_reg)
+ {
+ as_bad (_("register must be either sp or set by a previous"
+ "unwind_movsp directive"));
+ return;
+ }
+
+ /* Don't generate any opcodes, just record the information for later. */
+ unwind.fp_reg = fp_reg;
+ unwind.fp_used = 1;
+ if (sp_reg == REG_SP)
+ unwind.fp_offset = unwind.frame_size - offset;
+ else
+ unwind.fp_offset -= offset;
+}
+
+/* Parse an unwind_raw directive. */
+
+static void
+s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ /* This is an arbitrary limit. */
+ unsigned char op[16];
+ int count;
+
+ if (!unwind.proc_start)
+ as_bad (MISSING_FNSTART);
+
+ expression (&exp);
+ if (exp.X_op == O_constant
+ && skip_past_comma (&input_line_pointer) != FAIL)
+ {
+ unwind.frame_size += exp.X_add_number;
+ expression (&exp);
+ }
+ else
+ exp.X_op = O_illegal;
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected <offset>, <opcode>"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ count = 0;
+
+ /* Parse the opcode. */
+ for (;;)
+ {
+ if (count >= 16)
+ {
+ as_bad (_("unwind opcode too long"));
+ ignore_rest_of_line ();
+ }
+ if (exp.X_op != O_constant || exp.X_add_number & ~0xff)
+ {
+ as_bad (_("invalid unwind opcode"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op[count++] = exp.X_add_number;
+
+ /* Parse the next byte. */
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ break;
+
+ expression (&exp);
+ }
+
+ /* Add the opcode bytes in reverse order. */
+ while (count--)
+ add_unwind_opcode (op[count], 1);
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse a .eabi_attribute directive. */
+
+static void
+s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+
+ if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
+ attributes_set_explicitly[tag] = 1;
+}
+
+/* Emit a tls fix for the symbol. */
+
+static void
+s_arm_tls_descseq (int ignored ATTRIBUTE_UNUSED)
+{
+ char *p;
+ expressionS exp;
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+ md_cons_align (4);
+#endif
+
+ /* Since we're just labelling the code, there's no need to define a
+ mapping symbol. */
+ expression (&exp);
+ p = obstack_next_free (&frchain_now->frch_obstack);
+ fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 0,
+ thumb_mode ? BFD_RELOC_ARM_THM_TLS_DESCSEQ
+ : BFD_RELOC_ARM_TLS_DESCSEQ);
+}
+#endif /* OBJ_ELF */
+
+static void s_arm_arch (int);
+static void s_arm_object_arch (int);
+static void s_arm_cpu (int);
+static void s_arm_fpu (int);
+static void s_arm_arch_extension (int);
+
+#ifdef TE_PE
+
+static void
+pe_directive_secrel (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol)
+ exp.X_op = O_secrel;
+
+ emit_expr (&exp, 4);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+#endif /* TE_PE */
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Never called because '.req' does not start a line. */
+ { "req", s_req, 0 },
+ /* Following two are likewise never called. */
+ { "dn", s_dn, 0 },
+ { "qn", s_qn, 0 },
+ { "unreq", s_unreq, 0 },
+ { "bss", s_bss, 0 },
+ { "align", s_align, 0 },
+ { "arm", s_arm, 0 },
+ { "thumb", s_thumb, 0 },
+ { "code", s_code, 0 },
+ { "force_thumb", s_force_thumb, 0 },
+ { "thumb_func", s_thumb_func, 0 },
+ { "thumb_set", s_thumb_set, 0 },
+ { "even", s_even, 0 },
+ { "ltorg", s_ltorg, 0 },
+ { "pool", s_ltorg, 0 },
+ { "syntax", s_syntax, 0 },
+ { "cpu", s_arm_cpu, 0 },
+ { "arch", s_arm_arch, 0 },
+ { "object_arch", s_arm_object_arch, 0 },
+ { "fpu", s_arm_fpu, 0 },
+ { "arch_extension", s_arm_arch_extension, 0 },
+#ifdef OBJ_ELF
+ { "word", s_arm_elf_cons, 4 },
+ { "long", s_arm_elf_cons, 4 },
+ { "inst.n", s_arm_elf_inst, 2 },
+ { "inst.w", s_arm_elf_inst, 4 },
+ { "inst", s_arm_elf_inst, 0 },
+ { "rel31", s_arm_rel31, 0 },
+ { "fnstart", s_arm_unwind_fnstart, 0 },
+ { "fnend", s_arm_unwind_fnend, 0 },
+ { "cantunwind", s_arm_unwind_cantunwind, 0 },
+ { "personality", s_arm_unwind_personality, 0 },
+ { "personalityindex", s_arm_unwind_personalityindex, 0 },
+ { "handlerdata", s_arm_unwind_handlerdata, 0 },
+ { "save", s_arm_unwind_save, 0 },
+ { "vsave", s_arm_unwind_save, 1 },
+ { "movsp", s_arm_unwind_movsp, 0 },
+ { "pad", s_arm_unwind_pad, 0 },
+ { "setfp", s_arm_unwind_setfp, 0 },
+ { "unwind_raw", s_arm_unwind_raw, 0 },
+ { "eabi_attribute", s_arm_eabi_attribute, 0 },
+ { "tlsdescseq", s_arm_tls_descseq, 0 },
+#else
+ { "word", cons, 4},
+
+ /* These are used for dwarf. */
+ {"2byte", cons, 2},
+ {"4byte", cons, 4},
+ {"8byte", cons, 8},
+ /* These are used for dwarf2. */
+ { "file", (void (*) (int)) dwarf2_directive_file, 0 },
+ { "loc", dwarf2_directive_loc, 0 },
+ { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
+#endif
+ { "extend", float_cons, 'x' },
+ { "ldouble", float_cons, 'x' },
+ { "packed", float_cons, 'p' },
+#ifdef TE_PE
+ {"secrel32", pe_directive_secrel, 0},
+#endif
+
+ /* These are for compatibility with CodeComposer Studio. */
+ {"ref", s_ccs_ref, 0},
+ {"def", s_ccs_def, 0},
+ {"asmfunc", s_ccs_asmfunc, 0},
+ {"endasmfunc", s_ccs_endasmfunc, 0},
+
+ { 0, 0, 0 }
+};
+
+/* Parser functions used exclusively in instruction operands. */
+
+/* Generic immediate-value read function for use in insn parsing.
+ STR points to the beginning of the immediate (the leading #);
+ VAL receives the value; if the value is outside [MIN, MAX]
+ issue an error. PREFIX_OPT is true if the immediate prefix is
+ optional. */
+
+static int
+parse_immediate (char **str, int *val, int min, int max,
+ bfd_boolean prefix_opt)
+{
+ expressionS exp;
+ my_get_expression (&exp, str, prefix_opt ? GE_OPT_PREFIX : GE_IMM_PREFIX);
+ if (exp.X_op != O_constant)
+ {
+ inst.error = _("constant expression required");
+ return FAIL;
+ }
+
+ if (exp.X_add_number < min || exp.X_add_number > max)
+ {
+ inst.error = _("immediate value out of range");
+ return FAIL;
+ }
+
+ *val = exp.X_add_number;
+ return SUCCESS;
+}
+
+/* Less-generic immediate-value read function with the possibility of loading a
+ big (64-bit) immediate, as required by Neon VMOV, VMVN and logic immediate
+ instructions. Puts the result directly in inst.operands[i]. */
+
+static int
+parse_big_immediate (char **str, int i, expressionS *in_exp,
+ bfd_boolean allow_symbol_p)
+{
+ expressionS exp;
+ expressionS *exp_p = in_exp ? in_exp : &exp;
+ char *ptr = *str;
+
+ my_get_expression (exp_p, &ptr, GE_OPT_PREFIX_BIG);
+
+ if (exp_p->X_op == O_constant)
+ {
+ inst.operands[i].imm = exp_p->X_add_number & 0xffffffff;
+ /* If we're on a 64-bit host, then a 64-bit number can be returned using
+ O_constant. We have to be careful not to break compilation for
+ 32-bit X_add_number, though. */
+ if ((exp_p->X_add_number & ~(offsetT)(0xffffffffU)) != 0)
+ {
+ /* X >> 32 is illegal if sizeof (exp_p->X_add_number) == 4. */
+ inst.operands[i].reg = (((exp_p->X_add_number >> 16) >> 16)
+ & 0xffffffff);
+ inst.operands[i].regisimm = 1;
+ }
+ }
+ else if (exp_p->X_op == O_big
+ && LITTLENUM_NUMBER_OF_BITS * exp_p->X_add_number > 32)
+ {
+ unsigned parts = 32 / LITTLENUM_NUMBER_OF_BITS, j, idx = 0;
+
+ /* Bignums have their least significant bits in
+ generic_bignum[0]. Make sure we put 32 bits in imm and
+ 32 bits in reg, in a (hopefully) portable way. */
+ gas_assert (parts != 0);
+
+ /* Make sure that the number is not too big.
+ PR 11972: Bignums can now be sign-extended to the
+ size of a .octa so check that the out of range bits
+ are all zero or all one. */
+ if (LITTLENUM_NUMBER_OF_BITS * exp_p->X_add_number > 64)
+ {
+ LITTLENUM_TYPE m = -1;
+
+ if (generic_bignum[parts * 2] != 0
+ && generic_bignum[parts * 2] != m)
+ return FAIL;
+
+ for (j = parts * 2 + 1; j < (unsigned) exp_p->X_add_number; j++)
+ if (generic_bignum[j] != generic_bignum[j-1])
+ return FAIL;
+ }
+
+ inst.operands[i].imm = 0;
+ for (j = 0; j < parts; j++, idx++)
+ inst.operands[i].imm |= generic_bignum[idx]
+ << (LITTLENUM_NUMBER_OF_BITS * j);
+ inst.operands[i].reg = 0;
+ for (j = 0; j < parts; j++, idx++)
+ inst.operands[i].reg |= generic_bignum[idx]
+ << (LITTLENUM_NUMBER_OF_BITS * j);
+ inst.operands[i].regisimm = 1;
+ }
+ else if (!(exp_p->X_op == O_symbol && allow_symbol_p))
+ return FAIL;
+
+ *str = ptr;
+
+ return SUCCESS;
+}
+
+/* Returns the pseudo-register number of an FPA immediate constant,
+ or FAIL if there isn't a valid constant here. */
+
+static int
+parse_fpa_immediate (char ** str)
+{
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char * save_in;
+ expressionS exp;
+ int i;
+ int j;
+
+ /* First try and match exact strings, this is to guarantee
+ that some formats will work even for cross assembly. */
+
+ for (i = 0; fp_const[i]; i++)
+ {
+ if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
+ {
+ char *start = *str;
+
+ *str += strlen (fp_const[i]);
+ if (is_end_of_line[(unsigned char) **str])
+ return i + 8;
+ *str = start;
+ }
+ }
+
+ /* Just because we didn't get a match doesn't mean that the constant
+ isn't valid, just that it is in a format that we don't
+ automatically recognize. Try parsing it with the standard
+ expression routines. */
+
+ memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
+
+ /* Look for a raw floating point number. */
+ if ((save_in = atof_ieee (*str, 'x', words)) != NULL
+ && is_end_of_line[(unsigned char) *save_in])
+ {
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ {
+ for (j = 0; j < MAX_LITTLENUMS; j++)
+ {
+ if (words[j] != fp_values[i][j])
+ break;
+ }
+
+ if (j == MAX_LITTLENUMS)
+ {
+ *str = save_in;
+ return i + 8;
+ }
+ }
+ }
+
+ /* Try and parse a more complex expression, this will probably fail
+ unless the code uses a floating point prefix (eg "0f"). */
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ if (expression (&exp) == absolute_section
+ && exp.X_op == O_big
+ && exp.X_add_number < 0)
+ {
+ /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
+ Ditto for 15. */
+ if (gen_to_words (words, 5, (long) 15) == 0)
+ {
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ {
+ for (j = 0; j < MAX_LITTLENUMS; j++)
+ {
+ if (words[j] != fp_values[i][j])
+ break;
+ }
+
+ if (j == MAX_LITTLENUMS)
+ {
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return i + 8;
+ }
+ }
+ }
+ }
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ inst.error = _("invalid FPA immediate expression");
+ return FAIL;
+}
+
+/* Returns 1 if a number has "quarter-precision" float format
+ 0baBbbbbbc defgh000 00000000 00000000. */
+
+static int
+is_quarter_float (unsigned imm)
+{
+ int bs = (imm & 0x20000000) ? 0x3e000000 : 0x40000000;
+ return (imm & 0x7ffff) == 0 && ((imm & 0x7e000000) ^ bs) == 0;
+}
+
+
+/* Detect the presence of a floating point or integer zero constant,
+ i.e. #0.0 or #0. */
+
+static bfd_boolean
+parse_ifimm_zero (char **in)
+{
+ int error_code;
+
+ if (!is_immediate_prefix (**in))
+ return FALSE;
+
+ ++*in;
+ error_code = atof_generic (in, ".", EXP_CHARS,
+ &generic_floating_point_number);
+
+ if (!error_code
+ && generic_floating_point_number.sign == '+'
+ && (generic_floating_point_number.low
+ > generic_floating_point_number.leader))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Parse an 8-bit "quarter-precision" floating point number of the form:
+ 0baBbbbbbc defgh000 00000000 00000000.
+ The zero and minus-zero cases need special handling, since they can't be
+ encoded in the "quarter-precision" float format, but can nonetheless be
+ loaded as integer constants. */
+
+static unsigned
+parse_qfloat_immediate (char **ccp, int *immed)
+{
+ char *str = *ccp;
+ char *fpnum;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ int found_fpchar = 0;
+
+ skip_past_char (&str, '#');
+
+ /* We must not accidentally parse an integer as a floating-point number. Make
+ sure that the value we parse is not an integer by checking for special
+ characters '.' or 'e'.
+ FIXME: This is a horrible hack, but doing better is tricky because type
+ information isn't in a very usable state at parse time. */
+ fpnum = str;
+ skip_whitespace (fpnum);
+
+ if (strncmp (fpnum, "0x", 2) == 0)
+ return FAIL;
+ else
+ {
+ for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++)
+ if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
+ {
+ found_fpchar = 1;
+ break;
+ }
+
+ if (!found_fpchar)
+ return FAIL;
+ }
+
+ if ((str = atof_ieee (str, 's', words)) != NULL)
+ {
+ unsigned fpword = 0;
+ int i;
+
+ /* Our FP word must be 32 bits (single-precision FP). */
+ for (i = 0; i < 32 / LITTLENUM_NUMBER_OF_BITS; i++)
+ {
+ fpword <<= LITTLENUM_NUMBER_OF_BITS;
+ fpword |= words[i];
+ }
+
+ if (is_quarter_float (fpword) || (fpword & 0x7fffffff) == 0)
+ *immed = fpword;
+ else
+ return FAIL;
+
+ *ccp = str;
+
+ return SUCCESS;
+ }
+
+ return FAIL;
+}
+
+/* Shift operands. */
+enum shift_kind
+{
+ SHIFT_LSL, SHIFT_LSR, SHIFT_ASR, SHIFT_ROR, SHIFT_RRX
+};
+
+struct asm_shift_name
+{
+ const char *name;
+ enum shift_kind kind;
+};
+
+/* Third argument to parse_shift. */
+enum parse_shift_mode
+{
+ NO_SHIFT_RESTRICT, /* Any kind of shift is accepted. */
+ SHIFT_IMMEDIATE, /* Shift operand must be an immediate. */
+ SHIFT_LSL_OR_ASR_IMMEDIATE, /* Shift must be LSL or ASR immediate. */
+ SHIFT_ASR_IMMEDIATE, /* Shift must be ASR immediate. */
+ SHIFT_LSL_IMMEDIATE, /* Shift must be LSL immediate. */
+};
+
+/* Parse a <shift> specifier on an ARM data processing instruction.
+ This has three forms:
+
+ (LSL|LSR|ASL|ASR|ROR) Rs
+ (LSL|LSR|ASL|ASR|ROR) #imm
+ RRX
+
+ Note that ASL is assimilated to LSL in the instruction encoding, and
+ RRX to ROR #0 (which cannot be written as such). */
+
+static int
+parse_shift (char **str, int i, enum parse_shift_mode mode)
+{
+ const struct asm_shift_name *shift_name;
+ enum shift_kind shift;
+ char *s = *str;
+ char *p = s;
+ int reg;
+
+ for (p = *str; ISALPHA (*p); p++)
+ ;
+
+ if (p == *str)
+ {
+ inst.error = _("shift expression expected");
+ return FAIL;
+ }
+
+ shift_name = (const struct asm_shift_name *) hash_find_n (arm_shift_hsh, *str,
+ p - *str);
+
+ if (shift_name == NULL)
+ {
+ inst.error = _("shift expression expected");
+ return FAIL;
+ }
+
+ shift = shift_name->kind;
+
+ switch (mode)
+ {
+ case NO_SHIFT_RESTRICT:
+ case SHIFT_IMMEDIATE: break;
+
+ case SHIFT_LSL_OR_ASR_IMMEDIATE:
+ if (shift != SHIFT_LSL && shift != SHIFT_ASR)
+ {
+ inst.error = _("'LSL' or 'ASR' required");
+ return FAIL;
+ }
+ break;
+
+ case SHIFT_LSL_IMMEDIATE:
+ if (shift != SHIFT_LSL)
+ {
+ inst.error = _("'LSL' required");
+ return FAIL;
+ }
+ break;
+
+ case SHIFT_ASR_IMMEDIATE:
+ if (shift != SHIFT_ASR)
+ {
+ inst.error = _("'ASR' required");
+ return FAIL;
+ }
+ break;
+
+ default: abort ();
+ }
+
+ if (shift != SHIFT_RRX)
+ {
+ /* Whitespace can appear here if the next thing is a bare digit. */
+ skip_whitespace (p);
+
+ if (mode == NO_SHIFT_RESTRICT
+ && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
+ }
+ else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
+ }
+ inst.operands[i].shift_kind = shift;
+ inst.operands[i].shifted = 1;
+ *str = p;
+ return SUCCESS;
+}
+
+/* Parse a <shifter_operand> for an ARM data processing instruction:
+
+ #<immediate>
+ #<immediate>, <rotate>
+ <Rm>
+ <Rm>, <shift>
+
+ where <shift> is defined by parse_shift above, and <rotate> is a
+ multiple of 2 between 0 and 30. Validation of immediate operands
+ is deferred to md_apply_fix. */
+
+static int
+parse_shifter_operand (char **str, int i)
+{
+ int value;
+ expressionS exp;
+
+ if ((value = arm_reg_parse (str, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].reg = value;
+ inst.operands[i].isreg = 1;
+
+ /* parse_shift will override this if appropriate */
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
+
+ if (skip_past_comma (str) == FAIL)
+ return SUCCESS;
+
+ /* Shift operation on register. */
+ return parse_shift (str, i, NO_SHIFT_RESTRICT);
+ }
+
+ if (my_get_expression (&inst.reloc.exp, str, GE_IMM_PREFIX))
+ return FAIL;
+
+ if (skip_past_comma (str) == SUCCESS)
+ {
+ /* #x, y -- ie explicit rotation by Y. */
+ if (my_get_expression (&exp, str, GE_NO_PREFIX))
+ return FAIL;
+
+ if (exp.X_op != O_constant || inst.reloc.exp.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return FAIL;
+ }
+
+ value = exp.X_add_number;
+ if (value < 0 || value > 30 || value % 2 != 0)
+ {
+ inst.error = _("invalid rotation");
+ return FAIL;
+ }
+ if (inst.reloc.exp.X_add_number < 0 || inst.reloc.exp.X_add_number > 255)
+ {
+ inst.error = _("invalid constant");
+ return FAIL;
+ }
+
+ /* Encode as specified. */
+ inst.operands[i].imm = inst.reloc.exp.X_add_number | value << 7;
+ return SUCCESS;
+ }
+
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
+ return SUCCESS;
+}
+
+/* Group relocation information. Each entry in the table contains the
+ textual name of the relocation as may appear in assembler source
+ and must end with a colon.
+ Along with this textual name are the relocation codes to be used if
+ the corresponding instruction is an ALU instruction (ADD or SUB only),
+ an LDR, an LDRS, or an LDC. */
+
+struct group_reloc_table_entry
+{
+ const char *name;
+ int alu_code;
+ int ldr_code;
+ int ldrs_code;
+ int ldc_code;
+};
+
+typedef enum
+{
+ /* Varieties of non-ALU group relocation. */
+
+ GROUP_LDR,
+ GROUP_LDRS,
+ GROUP_LDC
+} group_reloc_type;
+
+static struct group_reloc_table_entry group_reloc_table[] =
+ { /* Program counter relative: */
+ { "pc_g0_nc",
+ BFD_RELOC_ARM_ALU_PC_G0_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "pc_g0",
+ BFD_RELOC_ARM_ALU_PC_G0, /* ALU */
+ BFD_RELOC_ARM_LDR_PC_G0, /* LDR */
+ BFD_RELOC_ARM_LDRS_PC_G0, /* LDRS */
+ BFD_RELOC_ARM_LDC_PC_G0 }, /* LDC */
+ { "pc_g1_nc",
+ BFD_RELOC_ARM_ALU_PC_G1_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "pc_g1",
+ BFD_RELOC_ARM_ALU_PC_G1, /* ALU */
+ BFD_RELOC_ARM_LDR_PC_G1, /* LDR */
+ BFD_RELOC_ARM_LDRS_PC_G1, /* LDRS */
+ BFD_RELOC_ARM_LDC_PC_G1 }, /* LDC */
+ { "pc_g2",
+ BFD_RELOC_ARM_ALU_PC_G2, /* ALU */
+ BFD_RELOC_ARM_LDR_PC_G2, /* LDR */
+ BFD_RELOC_ARM_LDRS_PC_G2, /* LDRS */
+ BFD_RELOC_ARM_LDC_PC_G2 }, /* LDC */
+ /* Section base relative */
+ { "sb_g0_nc",
+ BFD_RELOC_ARM_ALU_SB_G0_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "sb_g0",
+ BFD_RELOC_ARM_ALU_SB_G0, /* ALU */
+ BFD_RELOC_ARM_LDR_SB_G0, /* LDR */
+ BFD_RELOC_ARM_LDRS_SB_G0, /* LDRS */
+ BFD_RELOC_ARM_LDC_SB_G0 }, /* LDC */
+ { "sb_g1_nc",
+ BFD_RELOC_ARM_ALU_SB_G1_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "sb_g1",
+ BFD_RELOC_ARM_ALU_SB_G1, /* ALU */
+ BFD_RELOC_ARM_LDR_SB_G1, /* LDR */
+ BFD_RELOC_ARM_LDRS_SB_G1, /* LDRS */
+ BFD_RELOC_ARM_LDC_SB_G1 }, /* LDC */
+ { "sb_g2",
+ BFD_RELOC_ARM_ALU_SB_G2, /* ALU */
+ BFD_RELOC_ARM_LDR_SB_G2, /* LDR */
+ BFD_RELOC_ARM_LDRS_SB_G2, /* LDRS */
+ BFD_RELOC_ARM_LDC_SB_G2 } }; /* LDC */
+
+/* Given the address of a pointer pointing to the textual name of a group
+ relocation as may appear in assembler source, attempt to find its details
+ in group_reloc_table. The pointer will be updated to the character after
+ the trailing colon. On failure, FAIL will be returned; SUCCESS
+ otherwise. On success, *entry will be updated to point at the relevant
+ group_reloc_table entry. */
+
+static int
+find_group_reloc_table_entry (char **str, struct group_reloc_table_entry **out)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE (group_reloc_table); i++)
+ {
+ int length = strlen (group_reloc_table[i].name);
+
+ if (strncasecmp (group_reloc_table[i].name, *str, length) == 0
+ && (*str)[length] == ':')
+ {
+ *out = &group_reloc_table[i];
+ *str += (length + 1);
+ return SUCCESS;
+ }
+ }
+
+ return FAIL;
+}
+
+/* Parse a <shifter_operand> for an ARM data processing instruction
+ (as for parse_shifter_operand) where group relocations are allowed:
+
+ #<immediate>
+ #<immediate>, <rotate>
+ #:<group_reloc>:<expression>
+ <Rm>
+ <Rm>, <shift>
+
+ where <group_reloc> is one of the strings defined in group_reloc_table.
+ The hashes are optional.
+
+ Everything else is as for parse_shifter_operand. */
+
+static parse_operand_result
+parse_shifter_operand_group_reloc (char **str, int i)
+{
+ /* Determine if we have the sequence of characters #: or just :
+ coming next. If we do, then we check for a group relocation.
+ If we don't, punt the whole lot to parse_shifter_operand. */
+
+ if (((*str)[0] == '#' && (*str)[1] == ':')
+ || (*str)[0] == ':')
+ {
+ struct group_reloc_table_entry *entry;
+
+ if ((*str)[0] == '#')
+ (*str) += 2;
+ else
+ (*str)++;
+
+ /* Try to parse a group relocation. Anything else is an error. */
+ if (find_group_reloc_table_entry (str, &entry) == FAIL)
+ {
+ inst.error = _("unknown group relocation");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
+
+ /* We now have the group relocation table entry corresponding to
+ the name in the assembler source. Next, we parse the expression. */
+ if (my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX))
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+
+ /* Record the relocation type (always the ALU variant here). */
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->alu_code;
+ gas_assert (inst.reloc.type != 0);
+
+ return PARSE_OPERAND_SUCCESS;
+ }
+ else
+ return parse_shifter_operand (str, i) == SUCCESS
+ ? PARSE_OPERAND_SUCCESS : PARSE_OPERAND_FAIL;
+
+ /* Never reached. */
+}
+
+/* Parse a Neon alignment expression. Information is written to
+ inst.operands[i]. We assume the initial ':' has been skipped.
+
+ align .imm = align << 8, .immisalign=1, .preind=0 */
+static parse_operand_result
+parse_neon_alignment (char **str, int i)
+{
+ char *p = *str;
+ expressionS exp;
+
+ my_get_expression (&exp, &p, GE_NO_PREFIX);
+
+ if (exp.X_op != O_constant)
+ {
+ inst.error = _("alignment must be constant");
+ return PARSE_OPERAND_FAIL;
+ }
+
+ inst.operands[i].imm = exp.X_add_number << 8;
+ inst.operands[i].immisalign = 1;
+ /* Alignments are not pre-indexes. */
+ inst.operands[i].preind = 0;
+
+ *str = p;
+ return PARSE_OPERAND_SUCCESS;
+}
+
+/* Parse all forms of an ARM address expression. Information is written
+ to inst.operands[i] and/or inst.reloc.
+
+ Preindexed addressing (.preind=1):
+
+ [Rn, #offset] .reg=Rn .reloc.exp=offset
+ [Rn, +/-Rm] .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ [Rn, +/-Rm, shift] .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ .shift_kind=shift .reloc.exp=shift_imm
+
+ These three may have a trailing ! which causes .writeback to be set also.
+
+ Postindexed addressing (.postind=1, .writeback=1):
+
+ [Rn], #offset .reg=Rn .reloc.exp=offset
+ [Rn], +/-Rm .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ [Rn], +/-Rm, shift .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ .shift_kind=shift .reloc.exp=shift_imm
+
+ Unindexed addressing (.preind=0, .postind=0):
+
+ [Rn], {option} .reg=Rn .imm=option .immisreg=0
+
+ Other:
+
+ [Rn]{!} shorthand for [Rn,#0]{!}
+ =immediate .isreg=0 .reloc.exp=immediate
+ label .reg=PC .reloc.pc_rel=1 .reloc.exp=label
+
+ It is the caller's responsibility to check for addressing modes not
+ supported by the instruction, and to set inst.reloc.type. */
+
+static parse_operand_result
+parse_address_main (char **str, int i, int group_relocations,
+ group_reloc_type group_type)
+{
+ char *p = *str;
+ int reg;
+
+ if (skip_past_char (&p, '[') == FAIL)
+ {
+ if (skip_past_char (&p, '=') == FAIL)
+ {
+ /* Bare address - translate to PC-relative offset. */
+ inst.reloc.pc_rel = 1;
+ inst.operands[i].reg = REG_PC;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].preind = 1;
+
+ if (my_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX_BIG))
+ return PARSE_OPERAND_FAIL;
+ }
+ else if (parse_big_immediate (&p, i, &inst.reloc.exp,
+ /*allow_symbol_p=*/TRUE))
+ return PARSE_OPERAND_FAIL;
+
+ *str = p;
+ return PARSE_OPERAND_SUCCESS;
+ }
+
+ /* PR gas/14887: Allow for whitespace after the opening bracket. */
+ skip_whitespace (p);
+
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+ {
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return PARSE_OPERAND_FAIL;
+ }
+ inst.operands[i].reg = reg;
+ inst.operands[i].isreg = 1;
+
+ if (skip_past_comma (&p) == SUCCESS)
+ {
+ inst.operands[i].preind = 1;
+
+ if (*p == '+') p++;
+ else if (*p == '-') p++, inst.operands[i].negative = 1;
+
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
+
+ if (skip_past_comma (&p) == SUCCESS)
+ if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+ return PARSE_OPERAND_FAIL;
+ }
+ else if (skip_past_char (&p, ':') == SUCCESS)
+ {
+ /* FIXME: '@' should be used here, but it's filtered out by generic
+ code before we get to see it here. This may be subject to
+ change. */
+ parse_operand_result result = parse_neon_alignment (&p, i);
+
+ if (result != PARSE_OPERAND_SUCCESS)
+ return result;
+ }
+ else
+ {
+ if (inst.operands[i].negative)
+ {
+ inst.operands[i].negative = 0;
+ p--;
+ }
+
+ if (group_relocations
+ && ((*p == '#' && *(p + 1) == ':') || *p == ':'))
+ {
+ struct group_reloc_table_entry *entry;
+
+ /* Skip over the #: or : sequence. */
+ if (*p == '#')
+ p += 2;
+ else
+ p++;
+
+ /* Try to parse a group relocation. Anything else is an
+ error. */
+ if (find_group_reloc_table_entry (&p, &entry) == FAIL)
+ {
+ inst.error = _("unknown group relocation");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
+
+ /* We now have the group relocation table entry corresponding to
+ the name in the assembler source. Next, we parse the
+ expression. */
+ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+
+ /* Record the relocation type. */
+ switch (group_type)
+ {
+ case GROUP_LDR:
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->ldr_code;
+ break;
+
+ case GROUP_LDRS:
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->ldrs_code;
+ break;
+
+ case GROUP_LDC:
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->ldc_code;
+ break;
+
+ default:
+ gas_assert (0);
+ }
+
+ if (inst.reloc.type == 0)
+ {
+ inst.error = _("this group relocation is not allowed on this instruction");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
+ }
+ else
+ {
+ char *q = p;
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return PARSE_OPERAND_FAIL;
+ /* If the offset is 0, find out if it's a +0 or -0. */
+ if (inst.reloc.exp.X_op == O_constant
+ && inst.reloc.exp.X_add_number == 0)
+ {
+ skip_whitespace (q);
+ if (*q == '#')
+ {
+ q++;
+ skip_whitespace (q);
+ }
+ if (*q == '-')
+ inst.operands[i].negative = 1;
+ }
+ }
+ }
+ }
+ else if (skip_past_char (&p, ':') == SUCCESS)
+ {
+ /* FIXME: '@' should be used here, but it's filtered out by generic code
+ before we get to see it here. This may be subject to change. */
+ parse_operand_result result = parse_neon_alignment (&p, i);
+
+ if (result != PARSE_OPERAND_SUCCESS)
+ return result;
+ }
+
+ if (skip_past_char (&p, ']') == FAIL)
+ {
+ inst.error = _("']' expected");
+ return PARSE_OPERAND_FAIL;
+ }
+
+ if (skip_past_char (&p, '!') == SUCCESS)
+ inst.operands[i].writeback = 1;
+
+ else if (skip_past_comma (&p) == SUCCESS)
+ {
+ if (skip_past_char (&p, '{') == SUCCESS)
+ {
+ /* [Rn], {expr} - unindexed, with option */
+ if (parse_immediate (&p, &inst.operands[i].imm,
+ 0, 255, TRUE) == FAIL)
+ return PARSE_OPERAND_FAIL;
+
+ if (skip_past_char (&p, '}') == FAIL)
+ {
+ inst.error = _("'}' expected at end of 'option' field");
+ return PARSE_OPERAND_FAIL;
+ }
+ if (inst.operands[i].preind)
+ {
+ inst.error = _("cannot combine index with option");
+ return PARSE_OPERAND_FAIL;
+ }
+ *str = p;
+ return PARSE_OPERAND_SUCCESS;
+ }
+ else
+ {
+ inst.operands[i].postind = 1;
+ inst.operands[i].writeback = 1;
+
+ if (inst.operands[i].preind)
+ {
+ inst.error = _("cannot combine pre- and post-indexing");
+ return PARSE_OPERAND_FAIL;
+ }
+
+ if (*p == '+') p++;
+ else if (*p == '-') p++, inst.operands[i].negative = 1;
+
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ /* We might be using the immediate for alignment already. If we
+ are, OR the register number into the low-order bits. */
+ if (inst.operands[i].immisalign)
+ inst.operands[i].imm |= reg;
+ else
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
+
+ if (skip_past_comma (&p) == SUCCESS)
+ if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+ return PARSE_OPERAND_FAIL;
+ }
+ else
+ {
+ char *q = p;
+ if (inst.operands[i].negative)
+ {
+ inst.operands[i].negative = 0;
+ p--;
+ }
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return PARSE_OPERAND_FAIL;
+ /* If the offset is 0, find out if it's a +0 or -0. */
+ if (inst.reloc.exp.X_op == O_constant
+ && inst.reloc.exp.X_add_number == 0)
+ {
+ skip_whitespace (q);
+ if (*q == '#')
+ {
+ q++;
+ skip_whitespace (q);
+ }
+ if (*q == '-')
+ inst.operands[i].negative = 1;
+ }
+ }
+ }
+ }
+
+ /* If at this point neither .preind nor .postind is set, we have a
+ bare [Rn]{!}, which is shorthand for [Rn,#0]{!}. */
+ if (inst.operands[i].preind == 0 && inst.operands[i].postind == 0)
+ {
+ inst.operands[i].preind = 1;
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
+ }
+ *str = p;
+ return PARSE_OPERAND_SUCCESS;
+}
+
+static int
+parse_address (char **str, int i)
+{
+ return parse_address_main (str, i, 0, GROUP_LDR) == PARSE_OPERAND_SUCCESS
+ ? SUCCESS : FAIL;
+}
+
+static parse_operand_result
+parse_address_group_reloc (char **str, int i, group_reloc_type type)
+{
+ return parse_address_main (str, i, 1, type);
+}
+
+/* Parse an operand for a MOVW or MOVT instruction. */
+static int
+parse_half (char **str)
+{
+ char * p;
+
+ p = *str;
+ skip_past_char (&p, '#');
+ if (strncasecmp (p, ":lower16:", 9) == 0)
+ inst.reloc.type = BFD_RELOC_ARM_MOVW;
+ else if (strncasecmp (p, ":upper16:", 9) == 0)
+ inst.reloc.type = BFD_RELOC_ARM_MOVT;
+
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
+ {
+ p += 9;
+ skip_whitespace (p);
+ }
+
+ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+ return FAIL;
+
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ if (inst.reloc.exp.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return FAIL;
+ }
+ if (inst.reloc.exp.X_add_number < 0
+ || inst.reloc.exp.X_add_number > 0xffff)
+ {
+ inst.error = _("immediate value out of range");
+ return FAIL;
+ }
+ }
+ *str = p;
+ return SUCCESS;
+}
+
+/* Miscellaneous. */
+
+/* Parse a PSR flag operand. The value returned is FAIL on syntax error,
+ or a bitmask suitable to be or-ed into the ARM msr instruction. */
+static int
+parse_psr (char **str, bfd_boolean lhs)
+{
+ char *p;
+ unsigned long psr_field;
+ const struct asm_psr *psr;
+ char *start;
+ bfd_boolean is_apsr = FALSE;
+ bfd_boolean m_profile = ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m);
+
+ /* PR gas/12698: If the user has specified -march=all then m_profile will
+ be TRUE, but we want to ignore it in this case as we are building for any
+ CPU type, including non-m variants. */
+ if (selected_cpu.core == arm_arch_any.core)
+ m_profile = FALSE;
+
+ /* CPSR's and SPSR's can now be lowercase. This is just a convenience
+ feature for ease of use and backwards compatibility. */
+ p = *str;
+ if (strncasecmp (p, "SPSR", 4) == 0)
+ {
+ if (m_profile)
+ goto unsupported_psr;
+
+ psr_field = SPSR_BIT;
+ }
+ else if (strncasecmp (p, "CPSR", 4) == 0)
+ {
+ if (m_profile)
+ goto unsupported_psr;
+
+ psr_field = 0;
+ }
+ else if (strncasecmp (p, "APSR", 4) == 0)
+ {
+ /* APSR[_<bits>] can be used as a synonym for CPSR[_<flags>] on ARMv7-A
+ and ARMv7-R architecture CPUs. */
+ is_apsr = TRUE;
+ psr_field = 0;
+ }
+ else if (m_profile)
+ {
+ start = p;
+ do
+ p++;
+ while (ISALNUM (*p) || *p == '_');
+
+ if (strncasecmp (start, "iapsr", 5) == 0
+ || strncasecmp (start, "eapsr", 5) == 0
+ || strncasecmp (start, "xpsr", 4) == 0
+ || strncasecmp (start, "psr", 3) == 0)
+ p = start + strcspn (start, "rR") + 1;
+
+ psr = (const struct asm_psr *) hash_find_n (arm_v7m_psr_hsh, start,
+ p - start);
+
+ if (!psr)
+ return FAIL;
+
+ /* If APSR is being written, a bitfield may be specified. Note that
+ APSR itself is handled above. */
+ if (psr->field <= 3)
+ {
+ psr_field = psr->field;
+ is_apsr = TRUE;
+ goto check_suffix;
+ }
+
+ *str = p;
+ /* M-profile MSR instructions have the mask field set to "10", except
+ *PSR variants which modify APSR, which may use a different mask (and
+ have been handled already). Do that by setting the PSR_f field
+ here. */
+ return psr->field | (lhs ? PSR_f : 0);
+ }
+ else
+ goto unsupported_psr;
+
+ p += 4;
+check_suffix:
+ if (*p == '_')
+ {
+ /* A suffix follows. */
+ p++;
+ start = p;
+
+ do
+ p++;
+ while (ISALNUM (*p) || *p == '_');
+
+ if (is_apsr)
+ {
+ /* APSR uses a notation for bits, rather than fields. */
+ unsigned int nzcvq_bits = 0;
+ unsigned int g_bit = 0;
+ char *bit;
+
+ for (bit = start; bit != p; bit++)
+ {
+ switch (TOLOWER (*bit))
+ {
+ case 'n':
+ nzcvq_bits |= (nzcvq_bits & 0x01) ? 0x20 : 0x01;
+ break;
+
+ case 'z':
+ nzcvq_bits |= (nzcvq_bits & 0x02) ? 0x20 : 0x02;
+ break;
+
+ case 'c':
+ nzcvq_bits |= (nzcvq_bits & 0x04) ? 0x20 : 0x04;
+ break;
+
+ case 'v':
+ nzcvq_bits |= (nzcvq_bits & 0x08) ? 0x20 : 0x08;
+ break;
+
+ case 'q':
+ nzcvq_bits |= (nzcvq_bits & 0x10) ? 0x20 : 0x10;
+ break;
+
+ case 'g':
+ g_bit |= (g_bit & 0x1) ? 0x2 : 0x1;
+ break;
+
+ default:
+ inst.error = _("unexpected bit specified after APSR");
+ return FAIL;
+ }
+ }
+
+ if (nzcvq_bits == 0x1f)
+ psr_field |= PSR_f;
+
+ if (g_bit == 0x1)
+ {
+ if (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp))
+ {
+ inst.error = _("selected processor does not "
+ "support DSP extension");
+ return FAIL;
+ }
+
+ psr_field |= PSR_s;
+ }
+
+ if ((nzcvq_bits & 0x20) != 0
+ || (nzcvq_bits != 0x1f && nzcvq_bits != 0)
+ || (g_bit & 0x2) != 0)
+ {
+ inst.error = _("bad bitmask specified after APSR");
+ return FAIL;
+ }
+ }
+ else
+ {
+ psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start,
+ p - start);
+ if (!psr)
+ goto error;
+
+ psr_field |= psr->field;
+ }
+ }
+ else
+ {
+ if (ISALNUM (*p))
+ goto error; /* Garbage after "[CS]PSR". */
+
+ /* Unadorned APSR is equivalent to APSR_nzcvq/CPSR_f (for writes). This
+ is deprecated, but allow it anyway. */
+ if (is_apsr && lhs)
+ {
+ psr_field |= PSR_f;
+ as_tsktsk (_("writing to APSR without specifying a bitmask is "
+ "deprecated"));
+ }
+ else if (!m_profile)
+ /* These bits are never right for M-profile devices: don't set them
+ (only code paths which read/write APSR reach here). */
+ psr_field |= (PSR_c | PSR_f);
+ }
+ *str = p;
+ return psr_field;
+
+ unsupported_psr:
+ inst.error = _("selected processor does not support requested special "
+ "purpose register");
+ return FAIL;
+
+ error:
+ inst.error = _("flag for {c}psr instruction expected");
+ return FAIL;
+}
+
+/* Parse the flags argument to CPSI[ED]. Returns FAIL on error, or a
+ value suitable for splatting into the AIF field of the instruction. */
+
+static int
+parse_cps_flags (char **str)
+{
+ int val = 0;
+ int saw_a_flag = 0;
+ char *s = *str;
+
+ for (;;)
+ switch (*s++)
+ {
+ case '\0': case ',':
+ goto done;
+
+ case 'a': case 'A': saw_a_flag = 1; val |= 0x4; break;
+ case 'i': case 'I': saw_a_flag = 1; val |= 0x2; break;
+ case 'f': case 'F': saw_a_flag = 1; val |= 0x1; break;
+
+ default:
+ inst.error = _("unrecognized CPS flag");
+ return FAIL;
+ }
+
+ done:
+ if (saw_a_flag == 0)
+ {
+ inst.error = _("missing CPS flags");
+ return FAIL;
+ }
+
+ *str = s - 1;
+ return val;
+}
+
+/* Parse an endian specifier ("BE" or "LE", case insensitive);
+ returns 0 for big-endian, 1 for little-endian, FAIL for an error. */
+
+static int
+parse_endian_specifier (char **str)
+{
+ int little_endian;
+ char *s = *str;
+
+ if (strncasecmp (s, "BE", 2))
+ little_endian = 0;
+ else if (strncasecmp (s, "LE", 2))
+ little_endian = 1;
+ else
+ {
+ inst.error = _("valid endian specifiers are be or le");
+ return FAIL;
+ }
+
+ if (ISALNUM (s[2]) || s[2] == '_')
+ {
+ inst.error = _("valid endian specifiers are be or le");
+ return FAIL;
+ }
+
+ *str = s + 2;
+ return little_endian;
+}
+
+/* Parse a rotation specifier: ROR #0, #8, #16, #24. *val receives a
+ value suitable for poking into the rotate field of an sxt or sxta
+ instruction, or FAIL on error. */
+
+static int
+parse_ror (char **str)
+{
+ int rot;
+ char *s = *str;
+
+ if (strncasecmp (s, "ROR", 3) == 0)
+ s += 3;
+ else
+ {
+ inst.error = _("missing rotation field after comma");
+ return FAIL;
+ }
+
+ if (parse_immediate (&s, &rot, 0, 24, FALSE) == FAIL)
+ return FAIL;
+
+ switch (rot)
+ {
+ case 0: *str = s; return 0x0;
+ case 8: *str = s; return 0x1;
+ case 16: *str = s; return 0x2;
+ case 24: *str = s; return 0x3;
+
+ default:
+ inst.error = _("rotation can only be 0, 8, 16, or 24");
+ return FAIL;
+ }
+}
+
+/* Parse a conditional code (from conds[] below). The value returned is in the
+ range 0 .. 14, or FAIL. */
+static int
+parse_cond (char **str)
+{
+ char *q;
+ const struct asm_cond *c;
+ int n;
+ /* Condition codes are always 2 characters, so matching up to
+ 3 characters is sufficient. */
+ char cond[3];
+
+ q = *str;
+ n = 0;
+ while (ISALPHA (*q) && n < 3)
+ {
+ cond[n] = TOLOWER (*q);
+ q++;
+ n++;
+ }
+
+ c = (const struct asm_cond *) hash_find_n (arm_cond_hsh, cond, n);
+ if (!c)
+ {
+ inst.error = _("condition required");
+ return FAIL;
+ }
+
+ *str = q;
+ return c->value;
+}
+
+/* If the given feature available in the selected CPU, mark it as used.
+ Returns TRUE iff feature is available. */
+static bfd_boolean
+mark_feature_used (const arm_feature_set *feature)
+{
+ /* Ensure the option is valid on the current architecture. */
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
+ return FALSE;
+
+ /* Add the appropriate architecture feature for the barrier option used.
+ */
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, *feature);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *feature);
+
+ return TRUE;
+}
+
+/* Parse an option for a barrier instruction. Returns the encoding for the
+ option, or FAIL. */
+static int
+parse_barrier (char **str)
+{
+ char *p, *q;
+ const struct asm_barrier_opt *o;
+
+ p = q = *str;
+ while (ISALPHA (*q))
+ q++;
+
+ o = (const struct asm_barrier_opt *) hash_find_n (arm_barrier_opt_hsh, p,
+ q - p);
+ if (!o)
+ return FAIL;
+
+ if (!mark_feature_used (&o->arch))
+ return FAIL;
+
+ *str = q;
+ return o->value;
+}
+
+/* Parse the operands of a table branch instruction. Similar to a memory
+ operand. */
+static int
+parse_tb (char **str)
+{
+ char * p = *str;
+ int reg;
+
+ if (skip_past_char (&p, '[') == FAIL)
+ {
+ inst.error = _("'[' expected");
+ return FAIL;
+ }
+
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+ {
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return FAIL;
+ }
+ inst.operands[0].reg = reg;
+
+ if (skip_past_comma (&p) == FAIL)
+ {
+ inst.error = _("',' expected");
+ return FAIL;
+ }
+
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+ {
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return FAIL;
+ }
+ inst.operands[0].imm = reg;
+
+ if (skip_past_comma (&p) == SUCCESS)
+ {
+ if (parse_shift (&p, 0, SHIFT_LSL_IMMEDIATE) == FAIL)
+ return FAIL;
+ if (inst.reloc.exp.X_add_number != 1)
+ {
+ inst.error = _("invalid shift");
+ return FAIL;
+ }
+ inst.operands[0].shifted = 1;
+ }
+
+ if (skip_past_char (&p, ']') == FAIL)
+ {
+ inst.error = _("']' expected");
+ return FAIL;
+ }
+ *str = p;
+ return SUCCESS;
+}
+
+/* Parse the operands of a Neon VMOV instruction. See do_neon_mov for more
+ information on the types the operands can take and how they are encoded.
+ Up to four operands may be read; this function handles setting the
+ ".present" field for each read operand itself.
+ Updates STR and WHICH_OPERAND if parsing is successful and returns SUCCESS,
+ else returns FAIL. */
+
+static int
+parse_neon_mov (char **str, int *which_operand)
+{
+ int i = *which_operand, val;
+ enum arm_reg_type rtype;
+ char *ptr = *str;
+ struct neon_type_el optype;
+
+ if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL)
+ {
+ /* Case 4: VMOV<c><q>.<size> <Dn[x]>, <Rd>. */
+ inst.operands[i].reg = val;
+ inst.operands[i].isscalar = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i++].present = 1;
+
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+ }
+ else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NSDQ, &rtype, &optype))
+ != FAIL)
+ {
+ /* Cases 0, 1, 2, 3, 5 (D only). */
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
+ inst.operands[i].isvec = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i++].present = 1;
+
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
+ {
+ /* Case 5: VMOV<c><q> <Dm>, <Rd>, <Rn>.
+ Case 13: VMOV <Sd>, <Rm> */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+
+ if (rtype == REG_TYPE_NQ)
+ {
+ first_error (_("can't use Neon quad register here"));
+ return FAIL;
+ }
+ else if (rtype != REG_TYPE_VFS)
+ {
+ i++;
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+ }
+ }
+ else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NSDQ, &rtype,
+ &optype)) != FAIL)
+ {
+ /* Case 0: VMOV<c><q> <Qd>, <Qm>
+ Case 1: VMOV<c><q> <Dd>, <Dm>
+ Case 8: VMOV.F32 <Sd>, <Sm>
+ Case 15: VMOV <Sd>, <Se>, <Rn>, <Rm> */
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
+ inst.operands[i].isvec = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+
+ if (skip_past_comma (&ptr) == SUCCESS)
+ {
+ /* Case 15. */
+ i++;
+
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i++].present = 1;
+
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+ }
+ }
+ else if (parse_qfloat_immediate (&ptr, &inst.operands[i].imm) == SUCCESS)
+ /* Case 2: VMOV<c><q>.<dt> <Qd>, #<float-imm>
+ Case 3: VMOV<c><q>.<dt> <Dd>, #<float-imm>
+ Case 10: VMOV.F32 <Sd>, #<imm>
+ Case 11: VMOV.F64 <Dd>, #<imm> */
+ inst.operands[i].immisfloat = 1;
+ else if (parse_big_immediate (&ptr, i, NULL, /*allow_symbol_p=*/FALSE)
+ == SUCCESS)
+ /* Case 2: VMOV<c><q>.<dt> <Qd>, #<imm>
+ Case 3: VMOV<c><q>.<dt> <Dd>, #<imm> */
+ ;
+ else
+ {
+ first_error (_("expected <Rm> or <Dm> or <Qm> operand"));
+ return FAIL;
+ }
+ }
+ else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
+ {
+ /* Cases 6, 7. */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i++].present = 1;
+
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL)
+ {
+ /* Case 6: VMOV<c><q>.<dt> <Rd>, <Dn[x]> */
+ inst.operands[i].reg = val;
+ inst.operands[i].isscalar = 1;
+ inst.operands[i].present = 1;
+ inst.operands[i].vectype = optype;
+ }
+ else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
+ {
+ /* Case 7: VMOV<c><q> <Rd>, <Rn>, <Dm> */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i++].present = 1;
+
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFSD, &rtype, &optype))
+ == FAIL)
+ {
+ first_error (_(reg_expected_msgs[REG_TYPE_VFSD]));
+ return FAIL;
+ }
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isvec = 1;
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+
+ if (rtype == REG_TYPE_VFS)
+ {
+ /* Case 14. */
+ i++;
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+ if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL,
+ &optype)) == FAIL)
+ {
+ first_error (_(reg_expected_msgs[REG_TYPE_VFS]));
+ return FAIL;
+ }
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isvec = 1;
+ inst.operands[i].issingle = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+ }
+ }
+ else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL, &optype))
+ != FAIL)
+ {
+ /* Case 13. */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isvec = 1;
+ inst.operands[i].issingle = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+ }
+ }
+ else
+ {
+ first_error (_("parse error"));
+ return FAIL;
+ }
+
+ /* Successfully parsed the operands. Update args. */
+ *which_operand = i;
+ *str = ptr;
+ return SUCCESS;
+
+ wanted_comma:
+ first_error (_("expected comma"));
+ return FAIL;
+
+ wanted_arm:
+ first_error (_(reg_expected_msgs[REG_TYPE_RN]));
+ return FAIL;
+}
+
+/* Use this macro when the operand constraints are different
+ for ARM and THUMB (e.g. ldrd). */
+#define MIX_ARM_THUMB_OPERANDS(arm_operand, thumb_operand) \
+ ((arm_operand) | ((thumb_operand) << 16))
+
+/* Matcher codes for parse_operands. */
+enum operand_parse_code
+{
+ OP_stop, /* end of line */
+
+ OP_RR, /* ARM register */
+ OP_RRnpc, /* ARM register, not r15 */
+ OP_RRnpcsp, /* ARM register, neither r15 nor r13 (a.k.a. 'BadReg') */
+ OP_RRnpcb, /* ARM register, not r15, in square brackets */
+ OP_RRnpctw, /* ARM register, not r15 in Thumb-state or with writeback,
+ optional trailing ! */
+ OP_RRw, /* ARM register, not r15, optional trailing ! */
+ OP_RCP, /* Coprocessor number */
+ OP_RCN, /* Coprocessor register */
+ OP_RF, /* FPA register */
+ OP_RVS, /* VFP single precision register */
+ OP_RVD, /* VFP double precision register (0..15) */
+ OP_RND, /* Neon double precision register (0..31) */
+ OP_RNQ, /* Neon quad precision register */
+ OP_RVSD, /* VFP single or double precision register */
+ OP_RNDQ, /* Neon double or quad precision register */
+ OP_RNSDQ, /* Neon single, double or quad precision register */
+ OP_RNSC, /* Neon scalar D[X] */
+ OP_RVC, /* VFP control register */
+ OP_RMF, /* Maverick F register */
+ OP_RMD, /* Maverick D register */
+ OP_RMFX, /* Maverick FX register */
+ OP_RMDX, /* Maverick DX register */
+ OP_RMAX, /* Maverick AX register */
+ OP_RMDS, /* Maverick DSPSC register */
+ OP_RIWR, /* iWMMXt wR register */
+ OP_RIWC, /* iWMMXt wC register */
+ OP_RIWG, /* iWMMXt wCG register */
+ OP_RXA, /* XScale accumulator register */
+
+ OP_REGLST, /* ARM register list */
+ OP_VRSLST, /* VFP single-precision register list */
+ OP_VRDLST, /* VFP double-precision register list */
+ OP_VRSDLST, /* VFP single or double-precision register list (& quad) */
+ OP_NRDLST, /* Neon double-precision register list (d0-d31, qN aliases) */
+ OP_NSTRLST, /* Neon element/structure list */
+
+ OP_RNDQ_I0, /* Neon D or Q reg, or immediate zero. */
+ OP_RVSD_I0, /* VFP S or D reg, or immediate zero. */
+ OP_RSVD_FI0, /* VFP S or D reg, or floating point immediate zero. */
+ OP_RR_RNSC, /* ARM reg or Neon scalar. */
+ OP_RNSDQ_RNSC, /* Vector S, D or Q reg, or Neon scalar. */
+ OP_RNDQ_RNSC, /* Neon D or Q reg, or Neon scalar. */
+ OP_RND_RNSC, /* Neon D reg, or Neon scalar. */
+ OP_VMOV, /* Neon VMOV operands. */
+ OP_RNDQ_Ibig, /* Neon D or Q reg, or big immediate for logic and VMVN. */
+ OP_RNDQ_I63b, /* Neon D or Q reg, or immediate for shift. */
+ OP_RIWR_I32z, /* iWMMXt wR register, or immediate 0 .. 32 for iWMMXt2. */
+
+ OP_I0, /* immediate zero */
+ OP_I7, /* immediate value 0 .. 7 */
+ OP_I15, /* 0 .. 15 */
+ OP_I16, /* 1 .. 16 */
+ OP_I16z, /* 0 .. 16 */
+ OP_I31, /* 0 .. 31 */
+ OP_I31w, /* 0 .. 31, optional trailing ! */
+ OP_I32, /* 1 .. 32 */
+ OP_I32z, /* 0 .. 32 */
+ OP_I63, /* 0 .. 63 */
+ OP_I63s, /* -64 .. 63 */
+ OP_I64, /* 1 .. 64 */
+ OP_I64z, /* 0 .. 64 */
+ OP_I255, /* 0 .. 255 */
+
+ OP_I4b, /* immediate, prefix optional, 1 .. 4 */
+ OP_I7b, /* 0 .. 7 */
+ OP_I15b, /* 0 .. 15 */
+ OP_I31b, /* 0 .. 31 */
+
+ OP_SH, /* shifter operand */
+ OP_SHG, /* shifter operand with possible group relocation */
+ OP_ADDR, /* Memory address expression (any mode) */
+ OP_ADDRGLDR, /* Mem addr expr (any mode) with possible LDR group reloc */
+ OP_ADDRGLDRS, /* Mem addr expr (any mode) with possible LDRS group reloc */
+ OP_ADDRGLDC, /* Mem addr expr (any mode) with possible LDC group reloc */
+ OP_EXP, /* arbitrary expression */
+ OP_EXPi, /* same, with optional immediate prefix */
+ OP_EXPr, /* same, with optional relocation suffix */
+ OP_HALF, /* 0 .. 65535 or low/high reloc. */
+
+ OP_CPSF, /* CPS flags */
+ OP_ENDI, /* Endianness specifier */
+ OP_wPSR, /* CPSR/SPSR/APSR mask for msr (writing). */
+ OP_rPSR, /* CPSR/SPSR/APSR mask for msr (reading). */
+ OP_COND, /* conditional code */
+ OP_TB, /* Table branch. */
+
+ OP_APSR_RR, /* ARM register or "APSR_nzcv". */
+
+ OP_RRnpc_I0, /* ARM register or literal 0 */
+ OP_RR_EXr, /* ARM register or expression with opt. reloc suff. */
+ OP_RR_EXi, /* ARM register or expression with imm prefix */
+ OP_RF_IF, /* FPA register or immediate */
+ OP_RIWR_RIWC, /* iWMMXt R or C reg */
+ OP_RIWC_RIWG, /* iWMMXt wC or wCG reg */
+
+ /* Optional operands. */
+ OP_oI7b, /* immediate, prefix optional, 0 .. 7 */
+ OP_oI31b, /* 0 .. 31 */
+ OP_oI32b, /* 1 .. 32 */
+ OP_oI32z, /* 0 .. 32 */
+ OP_oIffffb, /* 0 .. 65535 */
+ OP_oI255c, /* curly-brace enclosed, 0 .. 255 */
+
+ OP_oRR, /* ARM register */
+ OP_oRRnpc, /* ARM register, not the PC */
+ OP_oRRnpcsp, /* ARM register, neither the PC nor the SP (a.k.a. BadReg) */
+ OP_oRRw, /* ARM register, not r15, optional trailing ! */
+ OP_oRND, /* Optional Neon double precision register */
+ OP_oRNQ, /* Optional Neon quad precision register */
+ OP_oRNDQ, /* Optional Neon double or quad precision register */
+ OP_oRNSDQ, /* Optional single, double or quad precision vector register */
+ OP_oSHll, /* LSL immediate */
+ OP_oSHar, /* ASR immediate */
+ OP_oSHllar, /* LSL or ASR immediate */
+ OP_oROR, /* ROR 0/8/16/24 */
+ OP_oBARRIER_I15, /* Option argument for a barrier instruction. */
+
+ /* Some pre-defined mixed (ARM/THUMB) operands. */
+ OP_RR_npcsp = MIX_ARM_THUMB_OPERANDS (OP_RR, OP_RRnpcsp),
+ OP_RRnpc_npcsp = MIX_ARM_THUMB_OPERANDS (OP_RRnpc, OP_RRnpcsp),
+ OP_oRRnpc_npcsp = MIX_ARM_THUMB_OPERANDS (OP_oRRnpc, OP_oRRnpcsp),
+
+ OP_FIRST_OPTIONAL = OP_oI7b
+};
+
+/* Generic instruction operand parser. This does no encoding and no
+ semantic validation; it merely squirrels values away in the inst
+ structure. Returns SUCCESS or FAIL depending on whether the
+ specified grammar matched. */
+static int
+parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb)
+{
+ unsigned const int *upat = pattern;
+ char *backtrack_pos = 0;
+ const char *backtrack_error = 0;
+ int i, val = 0, backtrack_index = 0;
+ enum arm_reg_type rtype;
+ parse_operand_result result;
+ unsigned int op_parse_code;
+
+#define po_char_or_fail(chr) \
+ do \
+ { \
+ if (skip_past_char (&str, chr) == FAIL) \
+ goto bad_args; \
+ } \
+ while (0)
+
+#define po_reg_or_fail(regtype) \
+ do \
+ { \
+ val = arm_typed_reg_parse (& str, regtype, & rtype, \
+ & inst.operands[i].vectype); \
+ if (val == FAIL) \
+ { \
+ first_error (_(reg_expected_msgs[regtype])); \
+ goto failure; \
+ } \
+ inst.operands[i].reg = val; \
+ inst.operands[i].isreg = 1; \
+ inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \
+ inst.operands[i].isvec = (rtype == REG_TYPE_VFS \
+ || rtype == REG_TYPE_VFD \
+ || rtype == REG_TYPE_NQ); \
+ } \
+ while (0)
+
+#define po_reg_or_goto(regtype, label) \
+ do \
+ { \
+ val = arm_typed_reg_parse (& str, regtype, & rtype, \
+ & inst.operands[i].vectype); \
+ if (val == FAIL) \
+ goto label; \
+ \
+ inst.operands[i].reg = val; \
+ inst.operands[i].isreg = 1; \
+ inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \
+ inst.operands[i].isvec = (rtype == REG_TYPE_VFS \
+ || rtype == REG_TYPE_VFD \
+ || rtype == REG_TYPE_NQ); \
+ } \
+ while (0)
+
+#define po_imm_or_fail(min, max, popt) \
+ do \
+ { \
+ if (parse_immediate (&str, &val, min, max, popt) == FAIL) \
+ goto failure; \
+ inst.operands[i].imm = val; \
+ } \
+ while (0)
+
+#define po_scalar_or_goto(elsz, label) \
+ do \
+ { \
+ val = parse_scalar (& str, elsz, & inst.operands[i].vectype); \
+ if (val == FAIL) \
+ goto label; \
+ inst.operands[i].reg = val; \
+ inst.operands[i].isscalar = 1; \
+ } \
+ while (0)
+
+#define po_misc_or_fail(expr) \
+ do \
+ { \
+ if (expr) \
+ goto failure; \
+ } \
+ while (0)
+
+#define po_misc_or_fail_no_backtrack(expr) \
+ do \
+ { \
+ result = expr; \
+ if (result == PARSE_OPERAND_FAIL_NO_BACKTRACK) \
+ backtrack_pos = 0; \
+ if (result != PARSE_OPERAND_SUCCESS) \
+ goto failure; \
+ } \
+ while (0)
+
+#define po_barrier_or_imm(str) \
+ do \
+ { \
+ val = parse_barrier (&str); \
+ if (val == FAIL && ! ISALPHA (*str)) \
+ goto immediate; \
+ if (val == FAIL \
+ /* ISB can only take SY as an option. */ \
+ || ((inst.instruction & 0xf0) == 0x60 \
+ && val != 0xf)) \
+ { \
+ inst.error = _("invalid barrier type"); \
+ backtrack_pos = 0; \
+ goto failure; \
+ } \
+ } \
+ while (0)
+
+ skip_whitespace (str);
+
+ for (i = 0; upat[i] != OP_stop; i++)
+ {
+ op_parse_code = upat[i];
+ if (op_parse_code >= 1<<16)
+ op_parse_code = thumb ? (op_parse_code >> 16)
+ : (op_parse_code & ((1<<16)-1));
+
+ if (op_parse_code >= OP_FIRST_OPTIONAL)
+ {
+ /* Remember where we are in case we need to backtrack. */
+ gas_assert (!backtrack_pos);
+ backtrack_pos = str;
+ backtrack_error = inst.error;
+ backtrack_index = i;
+ }
+
+ if (i > 0 && (i > 1 || inst.operands[0].present))
+ po_char_or_fail (',');
+
+ switch (op_parse_code)
+ {
+ /* Registers */
+ case OP_oRRnpc:
+ case OP_oRRnpcsp:
+ case OP_RRnpc:
+ case OP_RRnpcsp:
+ case OP_oRR:
+ case OP_RR: po_reg_or_fail (REG_TYPE_RN); break;
+ case OP_RCP: po_reg_or_fail (REG_TYPE_CP); break;
+ case OP_RCN: po_reg_or_fail (REG_TYPE_CN); break;
+ case OP_RF: po_reg_or_fail (REG_TYPE_FN); break;
+ case OP_RVS: po_reg_or_fail (REG_TYPE_VFS); break;
+ case OP_RVD: po_reg_or_fail (REG_TYPE_VFD); break;
+ case OP_oRND:
+ case OP_RND: po_reg_or_fail (REG_TYPE_VFD); break;
+ case OP_RVC:
+ po_reg_or_goto (REG_TYPE_VFC, coproc_reg);
+ break;
+ /* Also accept generic coprocessor regs for unknown registers. */
+ coproc_reg:
+ po_reg_or_fail (REG_TYPE_CN);
+ break;
+ case OP_RMF: po_reg_or_fail (REG_TYPE_MVF); break;
+ case OP_RMD: po_reg_or_fail (REG_TYPE_MVD); break;
+ case OP_RMFX: po_reg_or_fail (REG_TYPE_MVFX); break;
+ case OP_RMDX: po_reg_or_fail (REG_TYPE_MVDX); break;
+ case OP_RMAX: po_reg_or_fail (REG_TYPE_MVAX); break;
+ case OP_RMDS: po_reg_or_fail (REG_TYPE_DSPSC); break;
+ case OP_RIWR: po_reg_or_fail (REG_TYPE_MMXWR); break;
+ case OP_RIWC: po_reg_or_fail (REG_TYPE_MMXWC); break;
+ case OP_RIWG: po_reg_or_fail (REG_TYPE_MMXWCG); break;
+ case OP_RXA: po_reg_or_fail (REG_TYPE_XSCALE); break;
+ case OP_oRNQ:
+ case OP_RNQ: po_reg_or_fail (REG_TYPE_NQ); break;
+ case OP_oRNDQ:
+ case OP_RNDQ: po_reg_or_fail (REG_TYPE_NDQ); break;
+ case OP_RVSD: po_reg_or_fail (REG_TYPE_VFSD); break;
+ case OP_oRNSDQ:
+ case OP_RNSDQ: po_reg_or_fail (REG_TYPE_NSDQ); break;
+
+ /* Neon scalar. Using an element size of 8 means that some invalid
+ scalars are accepted here, so deal with those in later code. */
+ case OP_RNSC: po_scalar_or_goto (8, failure); break;
+
+ case OP_RNDQ_I0:
+ {
+ po_reg_or_goto (REG_TYPE_NDQ, try_imm0);
+ break;
+ try_imm0:
+ po_imm_or_fail (0, 0, TRUE);
+ }
+ break;
+
+ case OP_RVSD_I0:
+ po_reg_or_goto (REG_TYPE_VFSD, try_imm0);
+ break;
+
+ case OP_RSVD_FI0:
+ {
+ po_reg_or_goto (REG_TYPE_VFSD, try_ifimm0);
+ break;
+ try_ifimm0:
+ if (parse_ifimm_zero (&str))
+ inst.operands[i].imm = 0;
+ else
+ {
+ inst.error
+ = _("only floating point zero is allowed as immediate value");
+ goto failure;
+ }
+ }
+ break;
+
+ case OP_RR_RNSC:
+ {
+ po_scalar_or_goto (8, try_rr);
+ break;
+ try_rr:
+ po_reg_or_fail (REG_TYPE_RN);
+ }
+ break;
+
+ case OP_RNSDQ_RNSC:
+ {
+ po_scalar_or_goto (8, try_nsdq);
+ break;
+ try_nsdq:
+ po_reg_or_fail (REG_TYPE_NSDQ);
+ }
+ break;
+
+ case OP_RNDQ_RNSC:
+ {
+ po_scalar_or_goto (8, try_ndq);
+ break;
+ try_ndq:
+ po_reg_or_fail (REG_TYPE_NDQ);
+ }
+ break;
+
+ case OP_RND_RNSC:
+ {
+ po_scalar_or_goto (8, try_vfd);
+ break;
+ try_vfd:
+ po_reg_or_fail (REG_TYPE_VFD);
+ }
+ break;
+
+ case OP_VMOV:
+ /* WARNING: parse_neon_mov can move the operand counter, i. If we're
+ not careful then bad things might happen. */
+ po_misc_or_fail (parse_neon_mov (&str, &i) == FAIL);
+ break;
+
+ case OP_RNDQ_Ibig:
+ {
+ po_reg_or_goto (REG_TYPE_NDQ, try_immbig);
+ break;
+ try_immbig:
+ /* There's a possibility of getting a 64-bit immediate here, so
+ we need special handling. */
+ if (parse_big_immediate (&str, i, NULL, /*allow_symbol_p=*/FALSE)
+ == FAIL)
+ {
+ inst.error = _("immediate value is out of range");
+ goto failure;
+ }
+ }
+ break;
+
+ case OP_RNDQ_I63b:
+ {
+ po_reg_or_goto (REG_TYPE_NDQ, try_shimm);
+ break;
+ try_shimm:
+ po_imm_or_fail (0, 63, TRUE);
+ }
+ break;
+
+ case OP_RRnpcb:
+ po_char_or_fail ('[');
+ po_reg_or_fail (REG_TYPE_RN);
+ po_char_or_fail (']');
+ break;
+
+ case OP_RRnpctw:
+ case OP_RRw:
+ case OP_oRRw:
+ po_reg_or_fail (REG_TYPE_RN);
+ if (skip_past_char (&str, '!') == SUCCESS)
+ inst.operands[i].writeback = 1;
+ break;
+
+ /* Immediates */
+ case OP_I7: po_imm_or_fail ( 0, 7, FALSE); break;
+ case OP_I15: po_imm_or_fail ( 0, 15, FALSE); break;
+ case OP_I16: po_imm_or_fail ( 1, 16, FALSE); break;
+ case OP_I16z: po_imm_or_fail ( 0, 16, FALSE); break;
+ case OP_I31: po_imm_or_fail ( 0, 31, FALSE); break;
+ case OP_I32: po_imm_or_fail ( 1, 32, FALSE); break;
+ case OP_I32z: po_imm_or_fail ( 0, 32, FALSE); break;
+ case OP_I63s: po_imm_or_fail (-64, 63, FALSE); break;
+ case OP_I63: po_imm_or_fail ( 0, 63, FALSE); break;
+ case OP_I64: po_imm_or_fail ( 1, 64, FALSE); break;
+ case OP_I64z: po_imm_or_fail ( 0, 64, FALSE); break;
+ case OP_I255: po_imm_or_fail ( 0, 255, FALSE); break;
+
+ case OP_I4b: po_imm_or_fail ( 1, 4, TRUE); break;
+ case OP_oI7b:
+ case OP_I7b: po_imm_or_fail ( 0, 7, TRUE); break;
+ case OP_I15b: po_imm_or_fail ( 0, 15, TRUE); break;
+ case OP_oI31b:
+ case OP_I31b: po_imm_or_fail ( 0, 31, TRUE); break;
+ case OP_oI32b: po_imm_or_fail ( 1, 32, TRUE); break;
+ case OP_oI32z: po_imm_or_fail ( 0, 32, TRUE); break;
+ case OP_oIffffb: po_imm_or_fail ( 0, 0xffff, TRUE); break;
+
+ /* Immediate variants */
+ case OP_oI255c:
+ po_char_or_fail ('{');
+ po_imm_or_fail (0, 255, TRUE);
+ po_char_or_fail ('}');
+ break;
+
+ case OP_I31w:
+ /* The expression parser chokes on a trailing !, so we have
+ to find it first and zap it. */
+ {
+ char *s = str;
+ while (*s && *s != ',')
+ s++;
+ if (s[-1] == '!')
+ {
+ s[-1] = '\0';
+ inst.operands[i].writeback = 1;
+ }
+ po_imm_or_fail (0, 31, TRUE);
+ if (str == s - 1)
+ str = s;
+ }
+ break;
+
+ /* Expressions */
+ case OP_EXPi: EXPi:
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_OPT_PREFIX));
+ break;
+
+ case OP_EXP:
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_NO_PREFIX));
+ break;
+
+ case OP_EXPr: EXPr:
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_NO_PREFIX));
+ if (inst.reloc.exp.X_op == O_symbol)
+ {
+ val = parse_reloc (&str);
+ if (val == -1)
+ {
+ inst.error = _("unrecognized relocation suffix");
+ goto failure;
+ }
+ else if (val != BFD_RELOC_UNUSED)
+ {
+ inst.operands[i].imm = val;
+ inst.operands[i].hasreloc = 1;
+ }
+ }
+ break;
+
+ /* Operand for MOVW or MOVT. */
+ case OP_HALF:
+ po_misc_or_fail (parse_half (&str));
+ break;
+
+ /* Register or expression. */
+ case OP_RR_EXr: po_reg_or_goto (REG_TYPE_RN, EXPr); break;
+ case OP_RR_EXi: po_reg_or_goto (REG_TYPE_RN, EXPi); break;
+
+ /* Register or immediate. */
+ case OP_RRnpc_I0: po_reg_or_goto (REG_TYPE_RN, I0); break;
+ I0: po_imm_or_fail (0, 0, FALSE); break;
+
+ case OP_RF_IF: po_reg_or_goto (REG_TYPE_FN, IF); break;
+ IF:
+ if (!is_immediate_prefix (*str))
+ goto bad_args;
+ str++;
+ val = parse_fpa_immediate (&str);
+ if (val == FAIL)
+ goto failure;
+ /* FPA immediates are encoded as registers 8-15.
+ parse_fpa_immediate has already applied the offset. */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ break;
+
+ case OP_RIWR_I32z: po_reg_or_goto (REG_TYPE_MMXWR, I32z); break;
+ I32z: po_imm_or_fail (0, 32, FALSE); break;
+
+ /* Two kinds of register. */
+ case OP_RIWR_RIWC:
+ {
+ struct reg_entry *rege = arm_reg_parse_multi (&str);
+ if (!rege
+ || (rege->type != REG_TYPE_MMXWR
+ && rege->type != REG_TYPE_MMXWC
+ && rege->type != REG_TYPE_MMXWCG))
+ {
+ inst.error = _("iWMMXt data or control register expected");
+ goto failure;
+ }
+ inst.operands[i].reg = rege->number;
+ inst.operands[i].isreg = (rege->type == REG_TYPE_MMXWR);
+ }
+ break;
+
+ case OP_RIWC_RIWG:
+ {
+ struct reg_entry *rege = arm_reg_parse_multi (&str);
+ if (!rege
+ || (rege->type != REG_TYPE_MMXWC
+ && rege->type != REG_TYPE_MMXWCG))
+ {
+ inst.error = _("iWMMXt control register expected");
+ goto failure;
+ }
+ inst.operands[i].reg = rege->number;
+ inst.operands[i].isreg = 1;
+ }
+ break;
+
+ /* Misc */
+ case OP_CPSF: val = parse_cps_flags (&str); break;
+ case OP_ENDI: val = parse_endian_specifier (&str); break;
+ case OP_oROR: val = parse_ror (&str); break;
+ case OP_COND: val = parse_cond (&str); break;
+ case OP_oBARRIER_I15:
+ po_barrier_or_imm (str); break;
+ immediate:
+ if (parse_immediate (&str, &val, 0, 15, TRUE) == FAIL)
+ goto failure;
+ break;
+
+ case OP_wPSR:
+ case OP_rPSR:
+ po_reg_or_goto (REG_TYPE_RNB, try_psr);
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_virt))
+ {
+ inst.error = _("Banked registers are not available with this "
+ "architecture.");
+ goto failure;
+ }
+ break;
+ try_psr:
+ val = parse_psr (&str, op_parse_code == OP_wPSR);
+ break;
+
+ case OP_APSR_RR:
+ po_reg_or_goto (REG_TYPE_RN, try_apsr);
+ break;
+ try_apsr:
+ /* Parse "APSR_nvzc" operand (for FMSTAT-equivalent MRS
+ instruction). */
+ if (strncasecmp (str, "APSR_", 5) == 0)
+ {
+ unsigned found = 0;
+ str += 5;
+ while (found < 15)
+ switch (*str++)
+ {
+ case 'c': found = (found & 1) ? 16 : found | 1; break;
+ case 'n': found = (found & 2) ? 16 : found | 2; break;
+ case 'z': found = (found & 4) ? 16 : found | 4; break;
+ case 'v': found = (found & 8) ? 16 : found | 8; break;
+ default: found = 16;
+ }
+ if (found != 15)
+ goto failure;
+ inst.operands[i].isvec = 1;
+ /* APSR_nzcv is encoded in instructions as if it were the REG_PC. */
+ inst.operands[i].reg = REG_PC;
+ }
+ else
+ goto failure;
+ break;
+
+ case OP_TB:
+ po_misc_or_fail (parse_tb (&str));
+ break;
+
+ /* Register lists. */
+ case OP_REGLST:
+ val = parse_reg_list (&str);
+ if (*str == '^')
+ {
+ inst.operands[1].writeback = 1;
+ str++;
+ }
+ break;
+
+ case OP_VRSLST:
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg, REGLIST_VFP_S);
+ break;
+
+ case OP_VRDLST:
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg, REGLIST_VFP_D);
+ break;
+
+ case OP_VRSDLST:
+ /* Allow Q registers too. */
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
+ REGLIST_NEON_D);
+ if (val == FAIL)
+ {
+ inst.error = NULL;
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
+ REGLIST_VFP_S);
+ inst.operands[i].issingle = 1;
+ }
+ break;
+
+ case OP_NRDLST:
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
+ REGLIST_NEON_D);
+ break;
+
+ case OP_NSTRLST:
+ val = parse_neon_el_struct_list (&str, &inst.operands[i].reg,
+ &inst.operands[i].vectype);
+ break;
+
+ /* Addressing modes */
+ case OP_ADDR:
+ po_misc_or_fail (parse_address (&str, i));
+ break;
+
+ case OP_ADDRGLDR:
+ po_misc_or_fail_no_backtrack (
+ parse_address_group_reloc (&str, i, GROUP_LDR));
+ break;
+
+ case OP_ADDRGLDRS:
+ po_misc_or_fail_no_backtrack (
+ parse_address_group_reloc (&str, i, GROUP_LDRS));
+ break;
+
+ case OP_ADDRGLDC:
+ po_misc_or_fail_no_backtrack (
+ parse_address_group_reloc (&str, i, GROUP_LDC));
+ break;
+
+ case OP_SH:
+ po_misc_or_fail (parse_shifter_operand (&str, i));
+ break;
+
+ case OP_SHG:
+ po_misc_or_fail_no_backtrack (
+ parse_shifter_operand_group_reloc (&str, i));
+ break;
+
+ case OP_oSHll:
+ po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE));
+ break;
+
+ case OP_oSHar:
+ po_misc_or_fail (parse_shift (&str, i, SHIFT_ASR_IMMEDIATE));
+ break;
+
+ case OP_oSHllar:
+ po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_OR_ASR_IMMEDIATE));
+ break;
+
+ default:
+ as_fatal (_("unhandled operand code %d"), op_parse_code);
+ }
+
+ /* Various value-based sanity checks and shared operations. We
+ do not signal immediate failures for the register constraints;
+ this allows a syntax error to take precedence. */
+ switch (op_parse_code)
+ {
+ case OP_oRRnpc:
+ case OP_RRnpc:
+ case OP_RRnpcb:
+ case OP_RRw:
+ case OP_oRRw:
+ case OP_RRnpc_I0:
+ if (inst.operands[i].isreg && inst.operands[i].reg == REG_PC)
+ inst.error = BAD_PC;
+ break;
+
+ case OP_oRRnpcsp:
+ case OP_RRnpcsp:
+ if (inst.operands[i].isreg)
+ {
+ if (inst.operands[i].reg == REG_PC)
+ inst.error = BAD_PC;
+ else if (inst.operands[i].reg == REG_SP)
+ inst.error = BAD_SP;
+ }
+ break;
+
+ case OP_RRnpctw:
+ if (inst.operands[i].isreg
+ && inst.operands[i].reg == REG_PC
+ && (inst.operands[i].writeback || thumb))
+ inst.error = BAD_PC;
+ break;
+
+ case OP_CPSF:
+ case OP_ENDI:
+ case OP_oROR:
+ case OP_wPSR:
+ case OP_rPSR:
+ case OP_COND:
+ case OP_oBARRIER_I15:
+ case OP_REGLST:
+ case OP_VRSLST:
+ case OP_VRDLST:
+ case OP_VRSDLST:
+ case OP_NRDLST:
+ case OP_NSTRLST:
+ if (val == FAIL)
+ goto failure;
+ inst.operands[i].imm = val;
+ break;
+
+ default:
+ break;
+ }
+
+ /* If we get here, this operand was successfully parsed. */
+ inst.operands[i].present = 1;
+ continue;
+
+ bad_args:
+ inst.error = BAD_ARGS;
+
+ failure:
+ if (!backtrack_pos)
+ {
+ /* The parse routine should already have set inst.error, but set a
+ default here just in case. */
+ if (!inst.error)
+ inst.error = _("syntax error");
+ return FAIL;
+ }
+
+ /* Do not backtrack over a trailing optional argument that
+ absorbed some text. We will only fail again, with the
+ 'garbage following instruction' error message, which is
+ probably less helpful than the current one. */
+ if (backtrack_index == i && backtrack_pos != str
+ && upat[i+1] == OP_stop)
+ {
+ if (!inst.error)
+ inst.error = _("syntax error");
+ return FAIL;
+ }
+
+ /* Try again, skipping the optional argument at backtrack_pos. */
+ str = backtrack_pos;
+ inst.error = backtrack_error;
+ inst.operands[backtrack_index].present = 0;
+ i = backtrack_index;
+ backtrack_pos = 0;
+ }
+
+ /* Check that we have parsed all the arguments. */
+ if (*str != '\0' && !inst.error)
+ inst.error = _("garbage following instruction");
+
+ return inst.error ? FAIL : SUCCESS;
+}
+
+#undef po_char_or_fail
+#undef po_reg_or_fail
+#undef po_reg_or_goto
+#undef po_imm_or_fail
+#undef po_scalar_or_fail
+#undef po_barrier_or_imm
+
+/* Shorthand macro for instruction encoding functions issuing errors. */
+#define constraint(expr, err) \
+ do \
+ { \
+ if (expr) \
+ { \
+ inst.error = err; \
+ return; \
+ } \
+ } \
+ while (0)
+
+/* Reject "bad registers" for Thumb-2 instructions. Many Thumb-2
+ instructions are unpredictable if these registers are used. This
+ is the BadReg predicate in ARM's Thumb-2 documentation. */
+#define reject_bad_reg(reg) \
+ do \
+ if (reg == REG_SP || reg == REG_PC) \
+ { \
+ inst.error = (reg == REG_SP) ? BAD_SP : BAD_PC; \
+ return; \
+ } \
+ while (0)
+
+/* If REG is R13 (the stack pointer), warn that its use is
+ deprecated. */
+#define warn_deprecated_sp(reg) \
+ do \
+ if (warn_on_deprecated && reg == REG_SP) \
+ as_warn (_("use of r13 is deprecated")); \
+ while (0)
+
+/* Functions for operand encoding. ARM, then Thumb. */
+
+#define rotate_left(v, n) (v << n | v >> (32 - n))
+
+/* If VAL can be encoded in the immediate field of an ARM instruction,
+ return the encoded form. Otherwise, return FAIL. */
+
+static unsigned int
+encode_arm_immediate (unsigned int val)
+{
+ unsigned int a, i;
+
+ for (i = 0; i < 32; i += 2)
+ if ((a = rotate_left (val, i)) <= 0xff)
+ return a | (i << 7); /* 12-bit pack: [shift-cnt,const]. */
+
+ return FAIL;
+}
+
+/* If VAL can be encoded in the immediate field of a Thumb32 instruction,
+ return the encoded form. Otherwise, return FAIL. */
+static unsigned int
+encode_thumb32_immediate (unsigned int val)
+{
+ unsigned int a, i;
+
+ if (val <= 0xff)
+ return val;
+
+ for (i = 1; i <= 24; i++)
+ {
+ a = val >> i;
+ if ((val & ~(0xff << i)) == 0)
+ return ((val >> i) & 0x7f) | ((32 - i) << 7);
+ }
+
+ a = val & 0xff;
+ if (val == ((a << 16) | a))
+ return 0x100 | a;
+ if (val == ((a << 24) | (a << 16) | (a << 8) | a))
+ return 0x300 | a;
+
+ a = val & 0xff00;
+ if (val == ((a << 16) | a))
+ return 0x200 | (a >> 8);
+
+ return FAIL;
+}
+/* Encode a VFP SP or DP register number into inst.instruction. */
+
+static void
+encode_arm_vfp_reg (int reg, enum vfp_reg_pos pos)
+{
+ if ((pos == VFP_REG_Dd || pos == VFP_REG_Dn || pos == VFP_REG_Dm)
+ && reg > 15)
+ {
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_d32))
+ {
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ fpu_vfp_ext_d32);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
+ fpu_vfp_ext_d32);
+ }
+ else
+ {
+ first_error (_("D register out of range for selected VFP version"));
+ return;
+ }
+ }
+
+ switch (pos)
+ {
+ case VFP_REG_Sd:
+ inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
+ break;
+
+ case VFP_REG_Sn:
+ inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
+ break;
+
+ case VFP_REG_Sm:
+ inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
+ break;
+
+ case VFP_REG_Dd:
+ inst.instruction |= ((reg & 15) << 12) | ((reg >> 4) << 22);
+ break;
+
+ case VFP_REG_Dn:
+ inst.instruction |= ((reg & 15) << 16) | ((reg >> 4) << 7);
+ break;
+
+ case VFP_REG_Dm:
+ inst.instruction |= (reg & 15) | ((reg >> 4) << 5);
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Encode a <shift> in an ARM-format instruction. The immediate,
+ if any, is handled by md_apply_fix. */
+static void
+encode_arm_shift (int i)
+{
+ if (inst.operands[i].shift_kind == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 5;
+ else
+ {
+ inst.instruction |= inst.operands[i].shift_kind << 5;
+ if (inst.operands[i].immisreg)
+ {
+ inst.instruction |= SHIFT_BY_REG;
+ inst.instruction |= inst.operands[i].imm << 8;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+ }
+}
+
+static void
+encode_arm_shifter_operand (int i)
+{
+ if (inst.operands[i].isreg)
+ {
+ inst.instruction |= inst.operands[i].reg;
+ encode_arm_shift (i);
+ }
+ else
+ {
+ inst.instruction |= INST_IMMEDIATE;
+ if (inst.reloc.type != BFD_RELOC_ARM_IMMEDIATE)
+ inst.instruction |= inst.operands[i].imm;
+ }
+}
+
+/* Subroutine of encode_arm_addr_mode_2 and encode_arm_addr_mode_3. */
+static void
+encode_arm_addr_mode_common (int i, bfd_boolean is_t)
+{
+ /* PR 14260:
+ Generate an error if the operand is not a register. */
+ constraint (!inst.operands[i].isreg,
+ _("Instruction does not support =N addresses"));
+
+ inst.instruction |= inst.operands[i].reg << 16;
+
+ if (inst.operands[i].preind)
+ {
+ if (is_t)
+ {
+ inst.error = _("instruction does not accept preindexed addressing");
+ return;
+ }
+ inst.instruction |= PRE_INDEX;
+ if (inst.operands[i].writeback)
+ inst.instruction |= WRITE_BACK;
+
+ }
+ else if (inst.operands[i].postind)
+ {
+ gas_assert (inst.operands[i].writeback);
+ if (is_t)
+ inst.instruction |= WRITE_BACK;
+ }
+ else /* unindexed - only for coprocessor */
+ {
+ inst.error = _("instruction does not accept unindexed addressing");
+ return;
+ }
+
+ if (((inst.instruction & WRITE_BACK) || !(inst.instruction & PRE_INDEX))
+ && (((inst.instruction & 0x000f0000) >> 16)
+ == ((inst.instruction & 0x0000f000) >> 12)))
+ as_warn ((inst.instruction & LOAD_BIT)
+ ? _("destination register same as write-back base")
+ : _("source register same as write-back base"));
+}
+
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format mode 2 load or store instruction. If is_t is true,
+ reject forms that cannot be used with a T instruction (i.e. not
+ post-indexed). */
+static void
+encode_arm_addr_mode_2 (int i, bfd_boolean is_t)
+{
+ const bfd_boolean is_pc = (inst.operands[i].reg == REG_PC);
+
+ encode_arm_addr_mode_common (i, is_t);
+
+ if (inst.operands[i].immisreg)
+ {
+ constraint ((inst.operands[i].imm == REG_PC
+ || (is_pc && inst.operands[i].writeback)),
+ BAD_PC_ADDRESSING);
+ inst.instruction |= INST_IMMEDIATE; /* yes, this is backwards */
+ inst.instruction |= inst.operands[i].imm;
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ if (inst.operands[i].shifted)
+ {
+ if (inst.operands[i].shift_kind == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 5;
+ else
+ {
+ inst.instruction |= inst.operands[i].shift_kind << 5;
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+ }
+ }
+ }
+ else /* immediate offset in inst.reloc */
+ {
+ if (is_pc && !inst.reloc.pc_rel)
+ {
+ const bfd_boolean is_load = ((inst.instruction & LOAD_BIT) != 0);
+
+ /* If is_t is TRUE, it's called from do_ldstt. ldrt/strt
+ cannot use PC in addressing.
+ PC cannot be used in writeback addressing, either. */
+ constraint ((is_t || inst.operands[i].writeback),
+ BAD_PC_ADDRESSING);
+
+ /* Use of PC in str is deprecated for ARMv7. */
+ if (warn_on_deprecated
+ && !is_load
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7))
+ as_warn (_("use of PC in this instruction is deprecated"));
+ }
+
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ }
+ }
+}
+
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format mode 3 load or store instruction. Reject forms that
+ cannot be used with such instructions. If is_t is true, reject
+ forms that cannot be used with a T instruction (i.e. not
+ post-indexed). */
+static void
+encode_arm_addr_mode_3 (int i, bfd_boolean is_t)
+{
+ if (inst.operands[i].immisreg && inst.operands[i].shifted)
+ {
+ inst.error = _("instruction does not accept scaled register index");
+ return;
+ }
+
+ encode_arm_addr_mode_common (i, is_t);
+
+ if (inst.operands[i].immisreg)
+ {
+ constraint ((inst.operands[i].imm == REG_PC
+ || (is_t && inst.operands[i].reg == REG_PC)),
+ BAD_PC_ADDRESSING);
+ constraint (inst.operands[i].reg == REG_PC && inst.operands[i].writeback,
+ BAD_PC_WRITEBACK);
+ inst.instruction |= inst.operands[i].imm;
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ }
+ else /* immediate offset in inst.reloc */
+ {
+ constraint ((inst.operands[i].reg == REG_PC && !inst.reloc.pc_rel
+ && inst.operands[i].writeback),
+ BAD_PC_WRITEBACK);
+ inst.instruction |= HWOFFSET_IMM;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ }
+ }
+}
+
+/* Write immediate bits [7:0] to the following locations:
+
+ |28/24|23 19|18 16|15 4|3 0|
+ | a |x x x x x|b c d|x x x x x x x x x x x x|e f g h|
+
+ This function is used by VMOV/VMVN/VORR/VBIC. */
+
+static void
+neon_write_immbits (unsigned immbits)
+{
+ inst.instruction |= immbits & 0xf;
+ inst.instruction |= ((immbits >> 4) & 0x7) << 16;
+ inst.instruction |= ((immbits >> 7) & 0x1) << (thumb_mode ? 28 : 24);
+}
+
+/* Invert low-order SIZE bits of XHI:XLO. */
+
+static void
+neon_invert_size (unsigned *xlo, unsigned *xhi, int size)
+{
+ unsigned immlo = xlo ? *xlo : 0;
+ unsigned immhi = xhi ? *xhi : 0;
+
+ switch (size)
+ {
+ case 8:
+ immlo = (~immlo) & 0xff;
+ break;
+
+ case 16:
+ immlo = (~immlo) & 0xffff;
+ break;
+
+ case 64:
+ immhi = (~immhi) & 0xffffffff;
+ /* fall through. */
+
+ case 32:
+ immlo = (~immlo) & 0xffffffff;
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (xlo)
+ *xlo = immlo;
+
+ if (xhi)
+ *xhi = immhi;
+}
+
+/* True if IMM has form 0bAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD for bits
+ A, B, C, D. */
+
+static int
+neon_bits_same_in_bytes (unsigned imm)
+{
+ return ((imm & 0x000000ff) == 0 || (imm & 0x000000ff) == 0x000000ff)
+ && ((imm & 0x0000ff00) == 0 || (imm & 0x0000ff00) == 0x0000ff00)
+ && ((imm & 0x00ff0000) == 0 || (imm & 0x00ff0000) == 0x00ff0000)
+ && ((imm & 0xff000000) == 0 || (imm & 0xff000000) == 0xff000000);
+}
+
+/* For immediate of above form, return 0bABCD. */
+
+static unsigned
+neon_squash_bits (unsigned imm)
+{
+ return (imm & 0x01) | ((imm & 0x0100) >> 7) | ((imm & 0x010000) >> 14)
+ | ((imm & 0x01000000) >> 21);
+}
+
+/* Compress quarter-float representation to 0b...000 abcdefgh. */
+
+static unsigned
+neon_qfloat_bits (unsigned imm)
+{
+ return ((imm >> 19) & 0x7f) | ((imm >> 24) & 0x80);
+}
+
+/* Returns CMODE. IMMBITS [7:0] is set to bits suitable for inserting into
+ the instruction. *OP is passed as the initial value of the op field, and
+ may be set to a different value depending on the constant (i.e.
+ "MOV I64, 0bAAAAAAAABBBB..." which uses OP = 1 despite being MOV not
+ MVN). If the immediate looks like a repeated pattern then also
+ try smaller element sizes. */
+
+static int
+neon_cmode_for_move_imm (unsigned immlo, unsigned immhi, int float_p,
+ unsigned *immbits, int *op, int size,
+ enum neon_el_type type)
+{
+ /* Only permit float immediates (including 0.0/-0.0) if the operand type is
+ float. */
+ if (type == NT_float && !float_p)
+ return FAIL;
+
+ if (type == NT_float && is_quarter_float (immlo) && immhi == 0)
+ {
+ if (size != 32 || *op == 1)
+ return FAIL;
+ *immbits = neon_qfloat_bits (immlo);
+ return 0xf;
+ }
+
+ if (size == 64)
+ {
+ if (neon_bits_same_in_bytes (immhi)
+ && neon_bits_same_in_bytes (immlo))
+ {
+ if (*op == 1)
+ return FAIL;
+ *immbits = (neon_squash_bits (immhi) << 4)
+ | neon_squash_bits (immlo);
+ *op = 1;
+ return 0xe;
+ }
+
+ if (immhi != immlo)
+ return FAIL;
+ }
+
+ if (size >= 32)
+ {
+ if (immlo == (immlo & 0x000000ff))
+ {
+ *immbits = immlo;
+ return 0x0;
+ }
+ else if (immlo == (immlo & 0x0000ff00))
+ {
+ *immbits = immlo >> 8;
+ return 0x2;
+ }
+ else if (immlo == (immlo & 0x00ff0000))
+ {
+ *immbits = immlo >> 16;
+ return 0x4;
+ }
+ else if (immlo == (immlo & 0xff000000))
+ {
+ *immbits = immlo >> 24;
+ return 0x6;
+ }
+ else if (immlo == ((immlo & 0x0000ff00) | 0x000000ff))
+ {
+ *immbits = (immlo >> 8) & 0xff;
+ return 0xc;
+ }
+ else if (immlo == ((immlo & 0x00ff0000) | 0x0000ffff))
+ {
+ *immbits = (immlo >> 16) & 0xff;
+ return 0xd;
+ }
+
+ if ((immlo & 0xffff) != (immlo >> 16))
+ return FAIL;
+ immlo &= 0xffff;
+ }
+
+ if (size >= 16)
+ {
+ if (immlo == (immlo & 0x000000ff))
+ {
+ *immbits = immlo;
+ return 0x8;
+ }
+ else if (immlo == (immlo & 0x0000ff00))
+ {
+ *immbits = immlo >> 8;
+ return 0xa;
+ }
+
+ if ((immlo & 0xff) != (immlo >> 8))
+ return FAIL;
+ immlo &= 0xff;
+ }
+
+ if (immlo == (immlo & 0x000000ff))
+ {
+ /* Don't allow MVN with 8-bit immediate. */
+ if (*op == 1)
+ return FAIL;
+ *immbits = immlo;
+ return 0xe;
+ }
+
+ return FAIL;
+}
+
+enum lit_type
+{
+ CONST_THUMB,
+ CONST_ARM,
+ CONST_VEC
+};
+
+/* inst.reloc.exp describes an "=expr" load pseudo-operation.
+ Determine whether it can be performed with a move instruction; if
+ it can, convert inst.instruction to that move instruction and
+ return TRUE; if it can't, convert inst.instruction to a literal-pool
+ load and return FALSE. If this is not a valid thing to do in the
+ current context, set inst.error and return TRUE.
+
+ inst.operands[i] describes the destination register. */
+
+static bfd_boolean
+move_or_literal_pool (int i, enum lit_type t, bfd_boolean mode_3)
+{
+ unsigned long tbit;
+ bfd_boolean thumb_p = (t == CONST_THUMB);
+ bfd_boolean arm_p = (t == CONST_ARM);
+ bfd_boolean vec64_p = (t == CONST_VEC) && !inst.operands[i].issingle;
+
+ if (thumb_p)
+ tbit = (inst.instruction > 0xffff) ? THUMB2_LOAD_BIT : THUMB_LOAD_BIT;
+ else
+ tbit = LOAD_BIT;
+
+ if ((inst.instruction & tbit) == 0)
+ {
+ inst.error = _("invalid pseudo operation");
+ return TRUE;
+ }
+ if (inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_symbol
+ && inst.reloc.exp.X_op != O_big)
+ {
+ inst.error = _("constant expression expected");
+ return TRUE;
+ }
+ if ((inst.reloc.exp.X_op == O_constant
+ || inst.reloc.exp.X_op == O_big)
+ && !inst.operands[i].issingle)
+ {
+ if (thumb_p && inst.reloc.exp.X_op == O_constant)
+ {
+ if (!unified_syntax && (inst.reloc.exp.X_add_number & ~0xFF) == 0)
+ {
+ /* This can be done with a mov(1) instruction. */
+ inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
+ inst.instruction |= inst.reloc.exp.X_add_number;
+ return TRUE;
+ }
+ }
+ else if (arm_p && inst.reloc.exp.X_op == O_constant)
+ {
+ int value = encode_arm_immediate (inst.reloc.exp.X_add_number);
+ if (value != FAIL)
+ {
+ /* This can be done with a mov instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return TRUE;
+ }
+
+ value = encode_arm_immediate (~inst.reloc.exp.X_add_number);
+ if (value != FAIL)
+ {
+ /* This can be done with a mvn instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return TRUE;
+ }
+ }
+ else if (vec64_p)
+ {
+ int op = 0;
+ unsigned immbits = 0;
+ unsigned immlo = inst.operands[1].imm;
+ unsigned immhi = inst.operands[1].regisimm
+ ? inst.operands[1].reg
+ : inst.reloc.exp.X_unsigned
+ ? 0
+ : ((bfd_int64_t)((int) immlo)) >> 32;
+ int cmode = neon_cmode_for_move_imm (immlo, immhi, FALSE, &immbits,
+ &op, 64, NT_invtype);
+
+ if (cmode == FAIL)
+ {
+ neon_invert_size (&immlo, &immhi, 64);
+ op = !op;
+ cmode = neon_cmode_for_move_imm (immlo, immhi, FALSE, &immbits,
+ &op, 64, NT_invtype);
+ }
+ if (cmode != FAIL)
+ {
+ inst.instruction = (inst.instruction & VLDR_VMOV_SAME)
+ | (1 << 23)
+ | (cmode << 8)
+ | (op << 5)
+ | (1 << 4);
+ /* Fill other bits in vmov encoding for both thumb and arm. */
+ if (thumb_mode)
+ inst.instruction |= (0x7 << 29) | (0xF << 24);
+ else
+ inst.instruction |= (0xF << 28) | (0x1 << 25);
+ neon_write_immbits (immbits);
+ return TRUE;
+ }
+ }
+ }
+
+ if (add_to_lit_pool ((!inst.operands[i].isvec
+ || inst.operands[i].issingle) ? 4 : 8) == FAIL)
+ return TRUE;
+
+ inst.operands[1].reg = REG_PC;
+ inst.operands[1].isreg = 1;
+ inst.operands[1].preind = 1;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = (thumb_p
+ ? BFD_RELOC_ARM_THUMB_OFFSET
+ : (mode_3
+ ? BFD_RELOC_ARM_HWLITERAL
+ : BFD_RELOC_ARM_LITERAL));
+ return FALSE;
+}
+
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format instruction. Reject all forms which cannot be encoded
+ into a coprocessor load/store instruction. If wb_ok is false,
+ reject use of writeback; if unind_ok is false, reject use of
+ unindexed addressing. If reloc_override is not 0, use it instead
+ of BFD_ARM_CP_OFF_IMM, unless the initial relocation is a group one
+ (in which case it is preserved). */
+
+static int
+encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
+{
+ if (!inst.operands[i].isreg)
+ {
+ gas_assert (inst.operands[0].isvec);
+ if (move_or_literal_pool (0, CONST_VEC, /*mode_3=*/FALSE))
+ return SUCCESS;
+ }
+
+ inst.instruction |= inst.operands[i].reg << 16;
+
+ gas_assert (!(inst.operands[i].preind && inst.operands[i].postind));
+
+ if (!inst.operands[i].preind && !inst.operands[i].postind) /* unindexed */
+ {
+ gas_assert (!inst.operands[i].writeback);
+ if (!unind_ok)
+ {
+ inst.error = _("instruction does not support unindexed addressing");
+ return FAIL;
+ }
+ inst.instruction |= inst.operands[i].imm;
+ inst.instruction |= INDEX_UP;
+ return SUCCESS;
+ }
+
+ if (inst.operands[i].preind)
+ inst.instruction |= PRE_INDEX;
+
+ if (inst.operands[i].writeback)
+ {
+ if (inst.operands[i].reg == REG_PC)
+ {
+ inst.error = _("pc may not be used with write-back");
+ return FAIL;
+ }
+ if (!wb_ok)
+ {
+ inst.error = _("instruction does not support writeback");
+ return FAIL;
+ }
+ inst.instruction |= WRITE_BACK;
+ }
+
+ if (reloc_override)
+ inst.reloc.type = (bfd_reloc_code_real_type) reloc_override;
+ else if ((inst.reloc.type < BFD_RELOC_ARM_ALU_PC_G0_NC
+ || inst.reloc.type > BFD_RELOC_ARM_LDC_SB_G2)
+ && inst.reloc.type != BFD_RELOC_ARM_LDR_PC_G0)
+ {
+ if (thumb_mode)
+ inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ }
+
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+
+ return SUCCESS;
+}
+
+/* Functions for instruction encoding, sorted by sub-architecture.
+ First some generics; their names are taken from the conventional
+ bit positions for register arguments in ARM format instructions. */
+
+static void
+do_noargs (void)
+{
+}
+
+static void
+do_rd (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+}
+
+static void
+do_rd_rm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+}
+
+static void
+do_rm_rn (void)
+{
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 16;
+}
+
+static void
+do_rd_rn (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+}
+
+static void
+do_rn_rd (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 12;
+}
+
+static bfd_boolean
+check_obsolete (const arm_feature_set *feature, const char *msg)
+{
+ if (ARM_CPU_IS_ANY (cpu_variant))
+ {
+ as_warn ("%s", msg);
+ return TRUE;
+ }
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
+ {
+ as_bad ("%s", msg);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static void
+do_rd_rm_rn (void)
+{
+ unsigned Rn = inst.operands[2].reg;
+ /* Enforce restrictions on SWP instruction. */
+ if ((inst.instruction & 0x0fbfffff) == 0x01000090)
+ {
+ constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg,
+ _("Rn must not overlap other operands"));
+
+ /* SWP{b} is obsolete for ARMv8-A, and deprecated for ARMv6* and ARMv7.
+ */
+ if (!check_obsolete (&arm_ext_v8,
+ _("swp{b} use is obsoleted for ARMv8 and later"))
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6))
+ as_warn (_("swp{b} use is deprecated for ARMv6 and ARMv7"));
+ }
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= Rn << 16;
+}
+
+static void
+do_rd_rn_rm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+}
+
+static void
+do_rm_rd_rn (void)
+{
+ constraint ((inst.operands[2].reg == REG_PC), BAD_PC);
+ constraint (((inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_illegal)
+ || inst.reloc.exp.X_add_number != 0),
+ BAD_ADDR_MODE);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+static void
+do_imm0 (void)
+{
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
+do_rd_cpaddr (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, TRUE, 0);
+}
+
+/* ARM instructions, in alphabetical order by function name (except
+ that wrapper functions appear immediately after the function they
+ wrap). */
+
+/* This is a pseudo-op of the form "adr rd, label" to be converted
+ into a relative address of the form "add rd, pc, #label-.-8". */
+
+static void
+do_adr (void)
+{
+ inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
+
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.exp.X_add_number -= 8;
+}
+
+/* This is a pseudo-op of the form "adrl rd, label" to be converted
+ into a relative address of the form:
+ add rd, pc, #low(label-.-8)"
+ add rd, rd, #high(label-.-8)" */
+
+static void
+do_adrl (void)
+{
+ inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
+
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+ inst.reloc.pc_rel = 1;
+ inst.size = INSN_SIZE * 2;
+ inst.reloc.exp.X_add_number -= 8;
+}
+
+static void
+do_arit (void)
+{
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ encode_arm_shifter_operand (2);
+}
+
+static void
+do_barrier (void)
+{
+ if (inst.operands[0].present)
+ inst.instruction |= inst.operands[0].imm;
+ else
+ inst.instruction |= 0xf;
+}
+
+static void
+do_bfc (void)
+{
+ unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 7;
+ inst.instruction |= (msb - 1) << 16;
+}
+
+static void
+do_bfi (void)
+{
+ unsigned int msb;
+
+ /* #0 in second position is alternative syntax for bfc, which is
+ the same instruction but with REG_PC in the Rm field. */
+ if (!inst.operands[1].isreg)
+ inst.operands[1].reg = REG_PC;
+
+ msb = inst.operands[2].imm + inst.operands[3].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 7;
+ inst.instruction |= (msb - 1) << 16;
+}
+
+static void
+do_bfx (void)
+{
+ constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+ _("bit-field extends past end of register"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 7;
+ inst.instruction |= (inst.operands[3].imm - 1) << 16;
+}
+
+/* ARM V5 breakpoint instruction (argument parse)
+ BKPT <16 bit unsigned immediate>
+ Instruction is not conditional.
+ The bit pattern given in insns[] has the COND_ALWAYS condition,
+ and it is an error if the caller tried to override that. */
+
+static void
+do_bkpt (void)
+{
+ /* Top 12 of 16 bits to bits 19:8. */
+ inst.instruction |= (inst.operands[0].imm & 0xfff0) << 4;
+
+ /* Bottom 4 of 16 bits to bits 3:0. */
+ inst.instruction |= inst.operands[0].imm & 0xf;
+}
+
+static void
+encode_branch (int default_reloc)
+{
+ if (inst.operands[0].hasreloc)
+ {
+ constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32
+ && inst.operands[0].imm != BFD_RELOC_ARM_TLS_CALL,
+ _("the only valid suffixes here are '(plt)' and '(tlscall)'"));
+ inst.reloc.type = inst.operands[0].imm == BFD_RELOC_ARM_PLT32
+ ? BFD_RELOC_ARM_PLT32
+ : thumb_mode ? BFD_RELOC_ARM_THM_TLS_CALL : BFD_RELOC_ARM_TLS_CALL;
+ }
+ else
+ inst.reloc.type = (bfd_reloc_code_real_type) default_reloc;
+ inst.reloc.pc_rel = 1;
+}
+
+static void
+do_branch (void)
+{
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
+ encode_branch (BFD_RELOC_ARM_PCREL_JUMP);
+ else
+#endif
+ encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
+}
+
+static void
+do_bl (void)
+{
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
+ {
+ if (inst.cond == COND_ALWAYS)
+ encode_branch (BFD_RELOC_ARM_PCREL_CALL);
+ else
+ encode_branch (BFD_RELOC_ARM_PCREL_JUMP);
+ }
+ else
+#endif
+ encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
+}
+
+/* ARM V5 branch-link-exchange instruction (argument parse)
+ BLX <target_addr> ie BLX(1)
+ BLX{<condition>} <Rm> ie BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
+ Also, the <target_addr> can be 25 bits, hence has its own reloc. */
+
+static void
+do_blx (void)
+{
+ if (inst.operands[0].isreg)
+ {
+ /* Arg is a register; the opcode provided by insns[] is correct.
+ It is not illegal to do "blx pc", just useless. */
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in blx in ARM mode is not really useful"));
+
+ inst.instruction |= inst.operands[0].reg;
+ }
+ else
+ {
+ /* Arg is an address; this instruction cannot be executed
+ conditionally, and the opcode must be adjusted.
+ We retain the BFD_RELOC_ARM_PCREL_BLX till the very end
+ where we generate out a BFD_RELOC_ARM_PCREL_CALL instead. */
+ constraint (inst.cond != COND_ALWAYS, BAD_COND);
+ inst.instruction = 0xfa000000;
+ encode_branch (BFD_RELOC_ARM_PCREL_BLX);
+ }
+}
+
+static void
+do_bx (void)
+{
+ bfd_boolean want_reloc;
+
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
+
+ inst.instruction |= inst.operands[0].reg;
+ /* Output R_ARM_V4BX relocations if is an EABI object that looks like
+ it is for ARMv4t or earlier. */
+ want_reloc = !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5);
+ if (object_arch && !ARM_CPU_HAS_FEATURE (*object_arch, arm_ext_v5))
+ want_reloc = TRUE;
+
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
+#endif
+ want_reloc = FALSE;
+
+ if (want_reloc)
+ inst.reloc.type = BFD_RELOC_ARM_V4BX;
+}
+
+
+/* ARM v5TEJ. Jump to Jazelle code. */
+
+static void
+do_bxj (void)
+{
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bxj is not really useful"));
+
+ inst.instruction |= inst.operands[0].reg;
+}
+
+/* Co-processor data operation:
+ CDP{cond} <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>}
+ CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>} */
+static void
+do_cdp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 20;
+ inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
+ inst.instruction |= inst.operands[5].imm << 5;
+}
+
+static void
+do_cmp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_shifter_operand (1);
+}
+
+/* Transfer between coprocessor and ARM registers.
+ MRC{cond} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
+ MRC2
+ MCR{cond}
+ MCR2
+
+ No special properties. */
+
+struct deprecated_coproc_regs_s
+{
+ unsigned cp;
+ int opc1;
+ unsigned crn;
+ unsigned crm;
+ int opc2;
+ arm_feature_set deprecated;
+ arm_feature_set obsoleted;
+ const char *dep_msg;
+ const char *obs_msg;
+};
+
+#define DEPR_ACCESS_V8 \
+ N_("This coprocessor register access is deprecated in ARMv8")
+
+/* Table of all deprecated coprocessor registers. */
+static struct deprecated_coproc_regs_s deprecated_coproc_regs[] =
+{
+ {15, 0, 7, 10, 5, /* CP15DMB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 10, 4, /* CP15DSB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 5, 4, /* CP15ISB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 1, 0, 0, /* TEEHBR. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 0, 0, 0, /* TEECR. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+};
+
+#undef DEPR_ACCESS_V8
+
+static const size_t deprecated_coproc_reg_count =
+ sizeof (deprecated_coproc_regs) / sizeof (deprecated_coproc_regs[0]);
+
+static void
+do_co_reg (void)
+{
+ unsigned Rd;
+ size_t i;
+
+ Rd = inst.operands[2].reg;
+ if (thumb_mode)
+ {
+ if (inst.instruction == 0xee000010
+ || inst.instruction == 0xfe000010)
+ /* MCR, MCR2 */
+ reject_bad_reg (Rd);
+ else
+ /* MRC, MRC2 */
+ constraint (Rd == REG_SP, BAD_SP);
+ }
+ else
+ {
+ /* MCR */
+ if (inst.instruction == 0xe000010)
+ constraint (Rd == REG_PC, BAD_PC);
+ }
+
+ for (i = 0; i < deprecated_coproc_reg_count; ++i)
+ {
+ const struct deprecated_coproc_regs_s *r =
+ deprecated_coproc_regs + i;
+
+ if (inst.operands[0].reg == r->cp
+ && inst.operands[1].imm == r->opc1
+ && inst.operands[3].reg == r->crn
+ && inst.operands[4].reg == r->crm
+ && inst.operands[5].imm == r->opc2)
+ {
+ if (! ARM_CPU_IS_ANY (cpu_variant)
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, r->deprecated))
+ as_warn ("%s", r->dep_msg);
+ }
+ }
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 21;
+ inst.instruction |= Rd << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
+ inst.instruction |= inst.operands[5].imm << 5;
+}
+
+/* Transfer between coprocessor register and pair of ARM registers.
+ MCRR{cond} <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+ MCRR2
+ MRRC{cond}
+ MRRC2
+
+ Two XScale instructions are special cases of these:
+
+ MAR{cond} acc0, <RdLo>, <RdHi> == MCRR{cond} p0, #0, <RdLo>, <RdHi>, c0
+ MRA{cond} acc0, <RdLo>, <RdHi> == MRRC{cond} p0, #0, <RdLo>, <RdHi>, c0
+
+ Result unpredictable if Rd or Rn is R15. */
+
+static void
+do_co_reg2c (void)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[2].reg;
+ Rn = inst.operands[3].reg;
+
+ if (thumb_mode)
+ {
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ }
+ else
+ {
+ constraint (Rd == REG_PC, BAD_PC);
+ constraint (Rn == REG_PC, BAD_PC);
+ }
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 4;
+ inst.instruction |= Rd << 12;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= inst.operands[4].reg;
+}
+
+static void
+do_cpsi (void)
+{
+ inst.instruction |= inst.operands[0].imm << 6;
+ if (inst.operands[1].present)
+ {
+ inst.instruction |= CPSI_MMOD;
+ inst.instruction |= inst.operands[1].imm;
+ }
+}
+
+static void
+do_dbg (void)
+{
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
+do_div (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = (inst.operands[1].present
+ ? inst.operands[1].reg : Rd);
+ Rm = inst.operands[2].reg;
+
+ constraint ((Rd == REG_PC), BAD_PC);
+ constraint ((Rn == REG_PC), BAD_PC);
+ constraint ((Rm == REG_PC), BAD_PC);
+
+ inst.instruction |= Rd << 16;
+ inst.instruction |= Rn << 0;
+ inst.instruction |= Rm << 8;
+}
+
+static void
+do_it (void)
+{
+ /* There is no IT instruction in ARM mode. We
+ process it to do the validation as if in
+ thumb mode, just in case the code gets
+ assembled for thumb using the unified syntax. */
+
+ inst.size = 0;
+ if (unified_syntax)
+ {
+ set_it_insn_type (IT_INSN);
+ now_it.mask = (inst.instruction & 0xf) | 0x10;
+ now_it.cc = inst.operands[0].imm;
+ }
+}
+
+/* If there is only one register in the register list,
+ then return its register number. Otherwise return -1. */
+static int
+only_one_reg_in_list (int range)
+{
+ int i = ffs (range) - 1;
+ return (i > 15 || range != (1 << i)) ? -1 : i;
+}
+
+static void
+encode_ldmstm(int from_push_pop_mnem)
+{
+ int base_reg = inst.operands[0].reg;
+ int range = inst.operands[1].imm;
+ int one_reg;
+
+ inst.instruction |= base_reg << 16;
+ inst.instruction |= range;
+
+ if (inst.operands[1].writeback)
+ inst.instruction |= LDM_TYPE_2_OR_3;
+
+ if (inst.operands[0].writeback)
+ {
+ inst.instruction |= WRITE_BACK;
+ /* Check for unpredictable uses of writeback. */
+ if (inst.instruction & LOAD_BIT)
+ {
+ /* Not allowed in LDM type 2. */
+ if ((inst.instruction & LDM_TYPE_2_OR_3)
+ && ((range & (1 << REG_PC)) == 0))
+ as_warn (_("writeback of base register is UNPREDICTABLE"));
+ /* Only allowed if base reg not in list for other types. */
+ else if (range & (1 << base_reg))
+ as_warn (_("writeback of base register when in register list is UNPREDICTABLE"));
+ }
+ else /* STM. */
+ {
+ /* Not allowed for type 2. */
+ if (inst.instruction & LDM_TYPE_2_OR_3)
+ as_warn (_("writeback of base register is UNPREDICTABLE"));
+ /* Only allowed if base reg not in list, or first in list. */
+ else if ((range & (1 << base_reg))
+ && (range & ((1 << base_reg) - 1)))
+ as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
+ }
+ }
+
+ /* If PUSH/POP has only one register, then use the A2 encoding. */
+ one_reg = only_one_reg_in_list (range);
+ if (from_push_pop_mnem && one_reg >= 0)
+ {
+ int is_push = (inst.instruction & A_PUSH_POP_OP_MASK) == A1_OPCODE_PUSH;
+
+ inst.instruction &= A_COND_MASK;
+ inst.instruction |= is_push ? A2_OPCODE_PUSH : A2_OPCODE_POP;
+ inst.instruction |= one_reg << 12;
+ }
+}
+
+static void
+do_ldmstm (void)
+{
+ encode_ldmstm (/*from_push_pop_mnem=*/FALSE);
+}
+
+/* ARMv5TE load-consecutive (argument parse)
+ Mode is like LDRH.
+
+ LDRccD R, mode
+ STRccD R, mode. */
+
+static void
+do_ldrd (void)
+{
+ constraint (inst.operands[0].reg % 2 != 0,
+ _("first transfer register must be even"));
+ constraint (inst.operands[1].present
+ && inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("can only transfer two consecutive registers"));
+ constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
+ constraint (!inst.operands[2].isreg, _("'[' expected"));
+
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg + 1;
+
+ /* encode_arm_addr_mode_3 will diagnose overlap between the base
+ register and the first register written; we have to diagnose
+ overlap between the base and the second register written here. */
+
+ if (inst.operands[2].reg == inst.operands[1].reg
+ && (inst.operands[2].writeback || inst.operands[2].postind))
+ as_warn (_("base register written back, and overlaps "
+ "second transfer register"));
+
+ if (!(inst.instruction & V4_STR_BIT))
+ {
+ /* For an index-register load, the index register must not overlap the
+ destination (even if not write-back). */
+ if (inst.operands[2].immisreg
+ && ((unsigned) inst.operands[2].imm == inst.operands[0].reg
+ || (unsigned) inst.operands[2].imm == inst.operands[1].reg))
+ as_warn (_("index register overlaps transfer register"));
+ }
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
+}
+
+static void
+do_ldrex (void)
+{
+ constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+ || inst.operands[1].postind || inst.operands[1].writeback
+ || inst.operands[1].immisreg || inst.operands[1].shifted
+ || inst.operands[1].negative
+ /* This can arise if the programmer has written
+ strex rN, rM, foo
+ or if they have mistakenly used a register name as the last
+ operand, eg:
+ strex rN, rM, rX
+ It is very difficult to distinguish between these two cases
+ because "rX" might actually be a label. ie the register
+ name has been occluded by a symbol of the same name. So we
+ just generate a general 'bad addressing mode' type error
+ message and leave it up to the programmer to discover the
+ true cause and fix their mistake. */
+ || (inst.operands[1].reg == REG_PC),
+ BAD_ADDR_MODE);
+
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("offset must be zero in ARM encoding"));
+
+ constraint ((inst.operands[1].reg == REG_PC), BAD_PC);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_UNUSED;
+}
+
+static void
+do_ldrexd (void)
+{
+ constraint (inst.operands[0].reg % 2 != 0,
+ _("even register required"));
+ constraint (inst.operands[1].present
+ && inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("can only load two consecutive registers"));
+ /* If op 1 were present and equal to PC, this function wouldn't
+ have been called in the first place. */
+ constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+/* In both ARM and thumb state 'ldr pc, #imm' with an immediate
+ which is not a multiple of four is UNPREDICTABLE. */
+static void
+check_ldr_r15_aligned (void)
+{
+ constraint (!(inst.operands[1].immisreg)
+ && (inst.operands[0].reg == REG_PC
+ && inst.operands[1].reg == REG_PC
+ && (inst.reloc.exp.X_add_number & 0x3)),
+ _("ldr to register 15 must be 4-byte alligned"));
+}
+
+static void
+do_ldst (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, CONST_ARM, /*mode_3=*/FALSE))
+ return;
+ encode_arm_addr_mode_2 (1, /*is_t=*/FALSE);
+ check_ldr_r15_aligned ();
+}
+
+static void
+do_ldstt (void)
+{
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
+ {
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction requires a post-indexed address"));
+
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
+ }
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_2 (1, /*is_t=*/TRUE);
+}
+
+/* Halfword and signed-byte load/store operations. */
+
+static void
+do_ldstv4 (void)
+{
+ constraint (inst.operands[0].reg == REG_PC, BAD_PC);
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, CONST_ARM, /*mode_3=*/TRUE))
+ return;
+ encode_arm_addr_mode_3 (1, /*is_t=*/FALSE);
+}
+
+static void
+do_ldsttv4 (void)
+{
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
+ {
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction requires a post-indexed address"));
+
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
+ }
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_3 (1, /*is_t=*/TRUE);
+}
+
+/* Co-processor register load/store.
+ Format: <LDC|STC>{cond}[L] CP#,CRd,<address> */
+static void
+do_lstc (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 12;
+ encode_arm_cp_address (2, TRUE, TRUE, 0);
+}
+
+static void
+do_mlas (void)
+{
+ /* This restriction does not apply to mls (nor to mla in v6 or later). */
+ if (inst.operands[0].reg == inst.operands[1].reg
+ && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6)
+ && !(inst.instruction & 0x00400000))
+ as_tsktsk (_("Rd and Rm should be different in mla"));
+
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 12;
+}
+
+static void
+do_mov (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_shifter_operand (1);
+}
+
+/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>. */
+static void
+do_mov16 (void)
+{
+ bfd_vma imm;
+ bfd_boolean top;
+
+ top = (inst.instruction & 0x00400000) != 0;
+ constraint (top && inst.reloc.type == BFD_RELOC_ARM_MOVW,
+ _(":lower16: not allowed this instruction"));
+ constraint (!top && inst.reloc.type == BFD_RELOC_ARM_MOVT,
+ _(":upper16: not allowed instruction"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ imm = inst.reloc.exp.X_add_number;
+ /* The value is in two pieces: 0:11, 16:19. */
+ inst.instruction |= (imm & 0x00000fff);
+ inst.instruction |= (imm & 0x0000f000) << 4;
+ }
+}
+
+static void do_vfp_nsyn_opcode (const char *);
+
+static int
+do_vfp_nsyn_mrs (void)
+{
+ if (inst.operands[0].isvec)
+ {
+ if (inst.operands[1].reg != 1)
+ first_error (_("operand 1 must be FPSCR"));
+ memset (&inst.operands[0], '\0', sizeof (inst.operands[0]));
+ memset (&inst.operands[1], '\0', sizeof (inst.operands[1]));
+ do_vfp_nsyn_opcode ("fmstat");
+ }
+ else if (inst.operands[1].isvec)
+ do_vfp_nsyn_opcode ("fmrx");
+ else
+ return FAIL;
+
+ return SUCCESS;
+}
+
+static int
+do_vfp_nsyn_msr (void)
+{
+ if (inst.operands[0].isvec)
+ do_vfp_nsyn_opcode ("fmxr");
+ else
+ return FAIL;
+
+ return SUCCESS;
+}
+
+static void
+do_vmrs (void)
+{
+ unsigned Rt = inst.operands[0].reg;
+
+ if (thumb_mode && Rt == REG_SP)
+ {
+ inst.error = BAD_SP;
+ return;
+ }
+
+ /* APSR_ sets isvec. All other refs to PC are illegal. */
+ if (!inst.operands[0].isvec && Rt == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* If we get through parsing the register name, we just insert the number
+ generated into the instruction without further validation. */
+ inst.instruction |= (inst.operands[1].reg << 16);
+ inst.instruction |= (Rt << 12);
+}
+
+static void
+do_vmsr (void)
+{
+ unsigned Rt = inst.operands[1].reg;
+
+ if (thumb_mode)
+ reject_bad_reg (Rt);
+ else if (Rt == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* If we get through parsing the register name, we just insert the number
+ generated into the instruction without further validation. */
+ inst.instruction |= (inst.operands[0].reg << 16);
+ inst.instruction |= (Rt << 12);
+}
+
+static void
+do_mrs (void)
+{
+ unsigned br;
+
+ if (do_vfp_nsyn_mrs () == SUCCESS)
+ return;
+
+ constraint (inst.operands[0].reg == REG_PC, BAD_PC);
+ inst.instruction |= inst.operands[0].reg << 12;
+
+ if (inst.operands[1].isreg)
+ {
+ br = inst.operands[1].reg;
+ if (((br & 0x200) == 0) && ((br & 0xf0000) != 0xf000))
+ as_bad (_("bad register for mrs"));
+ }
+ else
+ {
+ /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
+ constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
+ != (PSR_c|PSR_f),
+ _("'APSR', 'CPSR' or 'SPSR' expected"));
+ br = (15<<16) | (inst.operands[1].imm & SPSR_BIT);
+ }
+
+ inst.instruction |= br;
+}
+
+/* Two possible forms:
+ "{C|S}PSR_<field>, Rm",
+ "{C|S}PSR_f, #expression". */
+
+static void
+do_msr (void)
+{
+ if (do_vfp_nsyn_msr () == SUCCESS)
+ return;
+
+ inst.instruction |= inst.operands[0].imm;
+ if (inst.operands[1].isreg)
+ inst.instruction |= inst.operands[1].reg;
+ else
+ {
+ inst.instruction |= INST_IMMEDIATE;
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
+ }
+}
+
+static void
+do_mul (void)
+{
+ constraint (inst.operands[2].reg == REG_PC, BAD_PC);
+
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+
+ if (inst.operands[0].reg == inst.operands[1].reg
+ && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
+ as_tsktsk (_("Rd and Rm should be different in mul"));
+}
+
+/* Long Multiply Parser
+ UMULL RdLo, RdHi, Rm, Rs
+ SMULL RdLo, RdHi, Rm, Rs
+ UMLAL RdLo, RdHi, Rm, Rs
+ SMLAL RdLo, RdHi, Rm, Rs. */
+
+static void
+do_mull (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].reg << 8;
+
+ /* rdhi and rdlo must be different. */
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+
+ /* rdhi, rdlo and rm must all be different before armv6. */
+ if ((inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[1].reg == inst.operands[2].reg)
+ && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
+ as_tsktsk (_("rdhi, rdlo and rm must all be different"));
+}
+
+static void
+do_nop (void)
+{
+ if (inst.operands[0].present
+ || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6k))
+ {
+ /* Architectural NOP hints are CPSR sets with no bits selected. */
+ inst.instruction &= 0xf0000000;
+ inst.instruction |= 0x0320f000;
+ if (inst.operands[0].present)
+ inst.instruction |= inst.operands[0].imm;
+ }
+}
+
+/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
+ PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
+ Condition defaults to COND_ALWAYS.
+ Error if Rd, Rn or Rm are R15. */
+
+static void
+do_pkhbt (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
+}
+
+/* ARM V6 PKHTB (Argument Parse). */
+
+static void
+do_pkhtb (void)
+{
+ if (!inst.operands[3].present)
+ {
+ /* If the shift specifier is omitted, turn the instruction
+ into pkhbt rd, rm, rn. */
+ inst.instruction &= 0xfff00010;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
+ }
+ else
+ {
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ encode_arm_shift (3);
+ }
+}
+
+/* ARMv5TE: Preload-Cache
+ MP Extensions: Preload for write
+
+ PLD(W) <addr_mode>
+
+ Syntactically, like LDR with B=1, W=0, L=1. */
+
+static void
+do_pld (void)
+{
+ constraint (!inst.operands[0].isreg,
+ _("'[' expected after PLD mnemonic"));
+ constraint (inst.operands[0].postind,
+ _("post-indexed expression used in preload instruction"));
+ constraint (inst.operands[0].writeback,
+ _("writeback used in preload instruction"));
+ constraint (!inst.operands[0].preind,
+ _("unindexed addressing used in preload instruction"));
+ encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
+}
+
+/* ARMv7: PLI <addr_mode> */
+static void
+do_pli (void)
+{
+ constraint (!inst.operands[0].isreg,
+ _("'[' expected after PLI mnemonic"));
+ constraint (inst.operands[0].postind,
+ _("post-indexed expression used in preload instruction"));
+ constraint (inst.operands[0].writeback,
+ _("writeback used in preload instruction"));
+ constraint (!inst.operands[0].preind,
+ _("unindexed addressing used in preload instruction"));
+ encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
+ inst.instruction &= ~PRE_INDEX;
+}
+
+static void
+do_push_pop (void)
+{
+ inst.operands[1] = inst.operands[0];
+ memset (&inst.operands[0], 0, sizeof inst.operands[0]);
+ inst.operands[0].isreg = 1;
+ inst.operands[0].writeback = 1;
+ inst.operands[0].reg = REG_SP;
+ encode_ldmstm (/*from_push_pop_mnem=*/TRUE);
+}
+
+/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
+ word at the specified address and the following word
+ respectively.
+ Unconditionally executed.
+ Error if Rn is R15. */
+
+static void
+do_rfe (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+}
+
+/* ARM V6 ssat (argument parse). */
+
+static void
+do_ssat (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= (inst.operands[1].imm - 1) << 16;
+ inst.instruction |= inst.operands[2].reg;
+
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
+}
+
+/* ARM V6 usat (argument parse). */
+
+static void
+do_usat (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 16;
+ inst.instruction |= inst.operands[2].reg;
+
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
+}
+
+/* ARM V6 ssat16 (argument parse). */
+
+static void
+do_ssat16 (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= ((inst.operands[1].imm - 1) << 16);
+ inst.instruction |= inst.operands[2].reg;
+}
+
+static void
+do_usat16 (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 16;
+ inst.instruction |= inst.operands[2].reg;
+}
+
+/* ARM V6 SETEND (argument parse). Sets the E bit in the CPSR while
+ preserving the other bits.
+
+ setend <endian_specifier>, where <endian_specifier> is either
+ BE or LE. */
+
+static void
+do_setend (void)
+{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_warn (_("setend use is deprecated for ARMv8"));
+
+ if (inst.operands[0].imm)
+ inst.instruction |= 0x200;
+}
+
+static void
+do_shift (void)
+{
+ unsigned int Rm = (inst.operands[1].present
+ ? inst.operands[1].reg
+ : inst.operands[0].reg);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= Rm;
+ if (inst.operands[2].isreg) /* Rd, {Rm,} Rs */
+ {
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= SHIFT_BY_REG;
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+}
+
+static void
+do_smc (void)
+{
+ inst.reloc.type = BFD_RELOC_ARM_SMC;
+ inst.reloc.pc_rel = 0;
+}
+
+static void
+do_hvc (void)
+{
+ inst.reloc.type = BFD_RELOC_ARM_HVC;
+ inst.reloc.pc_rel = 0;
+}
+
+static void
+do_swi (void)
+{
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
+ inst.reloc.pc_rel = 0;
+}
+
+/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
+ SMLAxy{cond} Rd,Rm,Rs,Rn
+ SMLAWy{cond} Rd,Rm,Rs,Rn
+ Error if any register is R15. */
+
+static void
+do_smla (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 12;
+}
+
+/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
+ SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
+ Error if any register is R15.
+ Warning if Rdlo == Rdhi. */
+
+static void
+do_smlal (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].reg << 8;
+
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+}
+
+/* ARM V5E (El Segundo) signed-multiply (argument parse)
+ SMULxy{cond} Rd,Rm,Rs
+ Error if any register is R15. */
+
+static void
+do_smul (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+}
+
+/* ARM V6 srs (argument parse). The variable fields in the encoding are
+ the same for both ARM and Thumb-2. */
+
+static void
+do_srs (void)
+{
+ int reg;
+
+ if (inst.operands[0].present)
+ {
+ reg = inst.operands[0].reg;
+ constraint (reg != REG_SP, _("SRS base register must be r13"));
+ }
+ else
+ reg = REG_SP;
+
+ inst.instruction |= reg << 16;
+ inst.instruction |= inst.operands[1].imm;
+ if (inst.operands[0].writeback || inst.operands[1].writeback)
+ inst.instruction |= WRITE_BACK;
+}
+
+/* ARM V6 strex (argument parse). */
+
+static void
+do_strex (void)
+{
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative
+ /* See comment in do_ldrex(). */
+ || (inst.operands[2].reg == REG_PC),
+ BAD_ADDR_MODE);
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("offset must be zero in ARM encoding"));
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.reloc.type = BFD_RELOC_UNUSED;
+}
+
+static void
+do_t_strexbh (void)
+{
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative,
+ BAD_ADDR_MODE);
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rm_rd_rn ();
+}
+
+static void
+do_strexd (void)
+{
+ constraint (inst.operands[1].reg % 2 != 0,
+ _("even register required"));
+ constraint (inst.operands[2].present
+ && inst.operands[2].reg != inst.operands[1].reg + 1,
+ _("can only store two consecutive registers"));
+ /* If op 2 were present and equal to PC, this function wouldn't
+ have been called in the first place. */
+ constraint (inst.operands[1].reg == REG_LR, _("r14 not allowed here"));
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[1].reg + 1
+ || inst.operands[0].reg == inst.operands[3].reg,
+ BAD_OVERLAP);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[3].reg << 16;
+}
+
+/* ARM V8 STRL. */
+static void
+do_stlex (void)
+{
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rd_rm_rn ();
+}
+
+static void
+do_t_stlex (void)
+{
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rm_rd_rn ();
+}
+
+/* ARM V6 SXTAH extracts a 16-bit value from a register, sign
+ extends it to 32-bits, and adds the result to a value in another
+ register. You can specify a rotation by 0, 8, 16, or 24 bits
+ before extracting the 16-bit value.
+ SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
+
+static void
+do_sxtah (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 10;
+}
+
+/* ARM V6 SXTH.
+
+ SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
+
+static void
+do_sxth (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 10;
+}
+
+/* VFP instructions. In a logical order: SP variant first, monad
+ before dyad, arithmetic then move then load/store. */
+
+static void
+do_vfp_sp_monadic (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
+
+static void
+do_vfp_sp_dyadic (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn);
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm);
+}
+
+static void
+do_vfp_sp_compare_z (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+}
+
+static void
+do_vfp_dp_sp_cvt (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
+
+static void
+do_vfp_sp_dp_cvt (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm);
+}
+
+static void
+do_vfp_reg_from_sp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn);
+}
+
+static void
+do_vfp_reg2_from_sp2 (void)
+{
+ constraint (inst.operands[2].imm != 2,
+ _("only two consecutive VFP SP registers allowed here"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm);
+}
+
+static void
+do_vfp_sp_from_reg (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sn);
+ inst.instruction |= inst.operands[1].reg << 12;
+}
+
+static void
+do_vfp_sp2_from_reg2 (void)
+{
+ constraint (inst.operands[0].imm != 2,
+ _("only two consecutive VFP SP registers allowed here"));
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sm);
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+static void
+do_vfp_sp_ldst (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
+
+static void
+do_vfp_dp_ldst (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
+
+
+static void
+vfp_sp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+ else
+ constraint (ldstm_type != VFP_LDSTMIA,
+ _("this addressing mode requires base-register writeback"));
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sd);
+ inst.instruction |= inst.operands[1].imm;
+}
+
+static void
+vfp_dp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+ int count;
+
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+ else
+ constraint (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX,
+ _("this addressing mode requires base-register writeback"));
+
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
+
+ count = inst.operands[1].imm << 1;
+ if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
+ count += 1;
+
+ inst.instruction |= count;
+}
+
+static void
+do_vfp_sp_ldstmia (void)
+{
+ vfp_sp_ldstm (VFP_LDSTMIA);
+}
+
+static void
+do_vfp_sp_ldstmdb (void)
+{
+ vfp_sp_ldstm (VFP_LDSTMDB);
+}
+
+static void
+do_vfp_dp_ldstmia (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMIA);
+}
+
+static void
+do_vfp_dp_ldstmdb (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMDB);
+}
+
+static void
+do_vfp_xp_ldstmia (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMIAX);
+}
+
+static void
+do_vfp_xp_ldstmdb (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMDBX);
+}
+
+static void
+do_vfp_dp_rd_rm (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm);
+}
+
+static void
+do_vfp_dp_rn_rd (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dn);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
+}
+
+static void
+do_vfp_dp_rd_rn (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn);
+}
+
+static void
+do_vfp_dp_rd_rn_rm (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn);
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dm);
+}
+
+static void
+do_vfp_dp_rd (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+}
+
+static void
+do_vfp_dp_rm_rd_rn (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dm);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dn);
+}
+
+/* VFPv3 instructions. */
+static void
+do_vfp_sp_const (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
+ inst.instruction |= (inst.operands[1].imm & 0x0f);
+}
+
+static void
+do_vfp_dp_const (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
+ inst.instruction |= (inst.operands[1].imm & 0x0f);
+}
+
+static void
+vfp_conv (int srcsize)
+{
+ int immbits = srcsize - inst.operands[1].imm;
+
+ if (srcsize == 16 && !(immbits >= 0 && immbits <= srcsize))
+ {
+ /* If srcsize is 16, inst.operands[1].imm must be in the range 0-16.
+ i.e. immbits must be in range 0 - 16. */
+ inst.error = _("immediate value out of range, expected range [0, 16]");
+ return;
+ }
+ else if (srcsize == 32 && !(immbits >= 0 && immbits < srcsize))
+ {
+ /* If srcsize is 32, inst.operands[1].imm must be in the range 1-32.
+ i.e. immbits must be in range 0 - 31. */
+ inst.error = _("immediate value out of range, expected range [1, 32]");
+ return;
+ }
+
+ inst.instruction |= (immbits & 1) << 5;
+ inst.instruction |= (immbits >> 1);
+}
+
+static void
+do_vfp_sp_conv_16 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ vfp_conv (16);
+}
+
+static void
+do_vfp_dp_conv_16 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ vfp_conv (16);
+}
+
+static void
+do_vfp_sp_conv_32 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ vfp_conv (32);
+}
+
+static void
+do_vfp_dp_conv_32 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ vfp_conv (32);
+}
+
+/* FPA instructions. Also in a logical order. */
+
+static void
+do_fpa_cmp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+}
+
+static void
+do_fpa_ldmstm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ switch (inst.operands[1].imm)
+ {
+ case 1: inst.instruction |= CP_T_X; break;
+ case 2: inst.instruction |= CP_T_Y; break;
+ case 3: inst.instruction |= CP_T_Y | CP_T_X; break;
+ case 4: break;
+ default: abort ();
+ }
+
+ if (inst.instruction & (PRE_INDEX | INDEX_UP))
+ {
+ /* The instruction specified "ea" or "fd", so we can only accept
+ [Rn]{!}. The instruction does not really support stacking or
+ unstacking, so we have to emulate these by setting appropriate
+ bits and offsets. */
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction does not support indexing"));
+
+ if ((inst.instruction & PRE_INDEX) || inst.operands[2].writeback)
+ inst.reloc.exp.X_add_number = 12 * inst.operands[1].imm;
+
+ if (!(inst.instruction & INDEX_UP))
+ inst.reloc.exp.X_add_number = -inst.reloc.exp.X_add_number;
+
+ if (!(inst.instruction & PRE_INDEX) && inst.operands[2].writeback)
+ {
+ inst.operands[2].preind = 0;
+ inst.operands[2].postind = 1;
+ }
+ }
+
+ encode_arm_cp_address (2, TRUE, TRUE, 0);
+}
+
+/* iWMMXt instructions: strictly in alphabetical order. */
+
+static void
+do_iwmmxt_tandorc (void)
+{
+ constraint (inst.operands[0].reg != REG_PC, _("only r15 allowed here"));
+}
+
+static void
+do_iwmmxt_textrc (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm;
+}
+
+static void
+do_iwmmxt_textrm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].imm;
+}
+
+static void
+do_iwmmxt_tinsr (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].imm;
+}
+
+static void
+do_iwmmxt_tmia (void)
+{
+ inst.instruction |= inst.operands[0].reg << 5;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
+
+static void
+do_iwmmxt_waligni (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 20;
+}
+
+static void
+do_iwmmxt_wmerge (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 21;
+}
+
+static void
+do_iwmmxt_wmov (void)
+{
+ /* WMOV rD, rN is an alias for WOR rD, rN, rN. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+}
+
+static void
+do_iwmmxt_wldstbh (void)
+{
+ int reloc;
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (thumb_mode)
+ reloc = BFD_RELOC_ARM_T32_CP_OFF_IMM_S2;
+ else
+ reloc = BFD_RELOC_ARM_CP_OFF_IMM_S2;
+ encode_arm_cp_address (1, TRUE, FALSE, reloc);
+}
+
+static void
+do_iwmmxt_wldstw (void)
+{
+ /* RIWR_RIWC clears .isreg for a control register. */
+ if (!inst.operands[0].isreg)
+ {
+ constraint (inst.cond != COND_ALWAYS, BAD_COND);
+ inst.instruction |= 0xf0000000;
+ }
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, TRUE, 0);
+}
+
+static void
+do_iwmmxt_wldstd (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2)
+ && inst.operands[1].immisreg)
+ {
+ inst.instruction &= ~0x1a000ff;
+ inst.instruction |= (0xf << 28);
+ if (inst.operands[1].preind)
+ inst.instruction |= PRE_INDEX;
+ if (!inst.operands[1].negative)
+ inst.instruction |= INDEX_UP;
+ if (inst.operands[1].writeback)
+ inst.instruction |= WRITE_BACK;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.reloc.exp.X_add_number << 4;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ else
+ encode_arm_cp_address (1, TRUE, FALSE, 0);
+}
+
+static void
+do_iwmmxt_wshufh (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= ((inst.operands[2].imm & 0xf0) << 16);
+ inst.instruction |= (inst.operands[2].imm & 0x0f);
+}
+
+static void
+do_iwmmxt_wzero (void)
+{
+ /* WZERO reg is an alias for WANDN reg, reg, reg. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[0].reg << 16;
+}
+
+static void
+do_iwmmxt_wrwrwr_or_imm5 (void)
+{
+ if (inst.operands[2].isreg)
+ do_rd_rn_rm ();
+ else {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2),
+ _("immediate operand requires iWMMXt2"));
+ do_rd_rn ();
+ if (inst.operands[2].imm == 0)
+ {
+ switch ((inst.instruction >> 20) & 0xf)
+ {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* w...h wrd, wrn, #0 -> wrorh wrd, wrn, #16. */
+ inst.operands[2].imm = 16;
+ inst.instruction = (inst.instruction & 0xff0fffff) | (0x7 << 20);
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ /* w...w wrd, wrn, #0 -> wrorw wrd, wrn, #32. */
+ inst.operands[2].imm = 32;
+ inst.instruction = (inst.instruction & 0xff0fffff) | (0xb << 20);
+ break;
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ {
+ /* w...d wrd, wrn, #0 -> wor wrd, wrn, wrn. */
+ unsigned long wrn;
+ wrn = (inst.instruction >> 16) & 0xf;
+ inst.instruction &= 0xff0fff0f;
+ inst.instruction |= wrn;
+ /* Bail out here; the instruction is now assembled. */
+ return;
+ }
+ }
+ }
+ /* Map 32 -> 0, etc. */
+ inst.operands[2].imm &= 0x1f;
+ inst.instruction |= (0xf << 28) | ((inst.operands[2].imm & 0x10) << 4) | (inst.operands[2].imm & 0xf);
+ }
+}
+
+/* Cirrus Maverick instructions. Simple 2-, 3-, and 4-register
+ operations first, then control, shift, and load/store. */
+
+/* Insns like "foo X,Y,Z". */
+
+static void
+do_mav_triple (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
+
+/* Insns like "foo W,X,Y,Z".
+ where W=MVAX[0:3] and X,Y,Z=MVFX[0:15]. */
+
+static void
+do_mav_quad (void)
+{
+ inst.instruction |= inst.operands[0].reg << 5;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.instruction |= inst.operands[3].reg;
+}
+
+/* cfmvsc32<cond> DSPSC,MVDX[15:0]. */
+static void
+do_mav_dspsc (void)
+{
+ inst.instruction |= inst.operands[1].reg << 12;
+}
+
+/* Maverick shift immediate instructions.
+ cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
+ cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0]. */
+
+static void
+do_mav_shift (void)
+{
+ int imm = inst.operands[2].imm;
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+
+ /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
+ Bits 5-7 of the insn should have bits 4-6 of the immediate.
+ Bit 4 should be 0. */
+ imm = (imm & 0xf) | ((imm & 0x70) << 1);
+
+ inst.instruction |= imm;
+}
+
+/* XScale instructions. Also sorted arithmetic before move. */
+
+/* Xscale multiply-accumulate (argument parse)
+ MIAcc acc0,Rm,Rs
+ MIAPHcc acc0,Rm,Rs
+ MIAxycc acc0,Rm,Rs. */
+
+static void
+do_xsc_mia (void)
+{
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
+
+/* Xscale move-accumulator-register (argument parse)
+
+ MARcc acc0,RdLo,RdHi. */
+
+static void
+do_xsc_mar (void)
+{
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+/* Xscale move-register-accumulator (argument parse)
+
+ MRAcc RdLo,RdHi,acc0. */
+
+static void
+do_xsc_mra (void)
+{
+ constraint (inst.operands[0].reg == inst.operands[1].reg, BAD_OVERLAP);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+}
+
+/* Encoding functions relevant only to Thumb. */
+
+/* inst.operands[i] is a shifted-register operand; encode
+ it into inst.instruction in the format used by Thumb32. */
+
+static void
+encode_thumb32_shifted_operand (int i)
+{
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[i].shift_kind;
+
+ constraint (inst.operands[i].immisreg,
+ _("shift by register not allowed in thumb mode"));
+ inst.instruction |= inst.operands[i].reg;
+ if (shift == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 4;
+ else
+ {
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+
+ constraint (value > 32
+ || (value == 32 && (shift == SHIFT_LSL
+ || shift == SHIFT_ROR)),
+ _("shift expression is too large"));
+
+ if (value == 0)
+ shift = SHIFT_LSL;
+ else if (value == 32)
+ value = 0;
+
+ inst.instruction |= shift << 4;
+ inst.instruction |= (value & 0x1c) << 10;
+ inst.instruction |= (value & 0x03) << 6;
+ }
+}
+
+
+/* inst.operands[i] was set up by parse_address. Encode it into a
+ Thumb32 format load or store instruction. Reject forms that cannot
+ be used with such instructions. If is_t is true, reject forms that
+ cannot be used with a T instruction; if is_d is true, reject forms
+ that cannot be used with a D instruction. If it is a store insn,
+ reject PC in Rn. */
+
+static void
+encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
+{
+ const bfd_boolean is_pc = (inst.operands[i].reg == REG_PC);
+
+ constraint (!inst.operands[i].isreg,
+ _("Instruction does not support =N addresses"));
+
+ inst.instruction |= inst.operands[i].reg << 16;
+ if (inst.operands[i].immisreg)
+ {
+ constraint (is_pc, BAD_PC_ADDRESSING);
+ constraint (is_t || is_d, _("cannot use register index with this instruction"));
+ constraint (inst.operands[i].negative,
+ _("Thumb does not support negative register indexing"));
+ constraint (inst.operands[i].postind,
+ _("Thumb does not support register post-indexing"));
+ constraint (inst.operands[i].writeback,
+ _("Thumb does not support register indexing with writeback"));
+ constraint (inst.operands[i].shifted && inst.operands[i].shift_kind != SHIFT_LSL,
+ _("Thumb supports only LSL in shifted register indexing"));
+
+ inst.instruction |= inst.operands[i].imm;
+ if (inst.operands[i].shifted)
+ {
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ constraint (inst.reloc.exp.X_add_number < 0
+ || inst.reloc.exp.X_add_number > 3,
+ _("shift out of range"));
+ inst.instruction |= inst.reloc.exp.X_add_number << 4;
+ }
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
+ else if (inst.operands[i].preind)
+ {
+ constraint (is_pc && inst.operands[i].writeback, BAD_PC_WRITEBACK);
+ constraint (is_t && inst.operands[i].writeback,
+ _("cannot use writeback with this instruction"));
+ constraint (is_pc && ((inst.instruction & THUMB2_LOAD_BIT) == 0),
+ BAD_PC_ADDRESSING);
+
+ if (is_d)
+ {
+ inst.instruction |= 0x01000000;
+ if (inst.operands[i].writeback)
+ inst.instruction |= 0x00200000;
+ }
+ else
+ {
+ inst.instruction |= 0x00000c00;
+ if (inst.operands[i].writeback)
+ inst.instruction |= 0x00000100;
+ }
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+ }
+ else if (inst.operands[i].postind)
+ {
+ gas_assert (inst.operands[i].writeback);
+ constraint (is_pc, _("cannot use post-indexing with PC-relative addressing"));
+ constraint (is_t, _("cannot use post-indexing with this instruction"));
+
+ if (is_d)
+ inst.instruction |= 0x00200000;
+ else
+ inst.instruction |= 0x00000900;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+ }
+ else /* unindexed - only for coprocessor */
+ inst.error = _("instruction does not accept unindexed addressing");
+}
+
+/* Table of Thumb instructions which exist in both 16- and 32-bit
+ encodings (the latter only in post-V6T2 cores). The index is the
+ value used in the insns table below. When there is more than one
+ possible 16-bit encoding for the instruction, this table always
+ holds variant (1).
+ Also contains several pseudo-instructions used during relaxation. */
+#define T16_32_TAB \
+ X(_adc, 4140, eb400000), \
+ X(_adcs, 4140, eb500000), \
+ X(_add, 1c00, eb000000), \
+ X(_adds, 1c00, eb100000), \
+ X(_addi, 0000, f1000000), \
+ X(_addis, 0000, f1100000), \
+ X(_add_pc,000f, f20f0000), \
+ X(_add_sp,000d, f10d0000), \
+ X(_adr, 000f, f20f0000), \
+ X(_and, 4000, ea000000), \
+ X(_ands, 4000, ea100000), \
+ X(_asr, 1000, fa40f000), \
+ X(_asrs, 1000, fa50f000), \
+ X(_b, e000, f000b000), \
+ X(_bcond, d000, f0008000), \
+ X(_bic, 4380, ea200000), \
+ X(_bics, 4380, ea300000), \
+ X(_cmn, 42c0, eb100f00), \
+ X(_cmp, 2800, ebb00f00), \
+ X(_cpsie, b660, f3af8400), \
+ X(_cpsid, b670, f3af8600), \
+ X(_cpy, 4600, ea4f0000), \
+ X(_dec_sp,80dd, f1ad0d00), \
+ X(_eor, 4040, ea800000), \
+ X(_eors, 4040, ea900000), \
+ X(_inc_sp,00dd, f10d0d00), \
+ X(_ldmia, c800, e8900000), \
+ X(_ldr, 6800, f8500000), \
+ X(_ldrb, 7800, f8100000), \
+ X(_ldrh, 8800, f8300000), \
+ X(_ldrsb, 5600, f9100000), \
+ X(_ldrsh, 5e00, f9300000), \
+ X(_ldr_pc,4800, f85f0000), \
+ X(_ldr_pc2,4800, f85f0000), \
+ X(_ldr_sp,9800, f85d0000), \
+ X(_lsl, 0000, fa00f000), \
+ X(_lsls, 0000, fa10f000), \
+ X(_lsr, 0800, fa20f000), \
+ X(_lsrs, 0800, fa30f000), \
+ X(_mov, 2000, ea4f0000), \
+ X(_movs, 2000, ea5f0000), \
+ X(_mul, 4340, fb00f000), \
+ X(_muls, 4340, ffffffff), /* no 32b muls */ \
+ X(_mvn, 43c0, ea6f0000), \
+ X(_mvns, 43c0, ea7f0000), \
+ X(_neg, 4240, f1c00000), /* rsb #0 */ \
+ X(_negs, 4240, f1d00000), /* rsbs #0 */ \
+ X(_orr, 4300, ea400000), \
+ X(_orrs, 4300, ea500000), \
+ X(_pop, bc00, e8bd0000), /* ldmia sp!,... */ \
+ X(_push, b400, e92d0000), /* stmdb sp!,... */ \
+ X(_rev, ba00, fa90f080), \
+ X(_rev16, ba40, fa90f090), \
+ X(_revsh, bac0, fa90f0b0), \
+ X(_ror, 41c0, fa60f000), \
+ X(_rors, 41c0, fa70f000), \
+ X(_sbc, 4180, eb600000), \
+ X(_sbcs, 4180, eb700000), \
+ X(_stmia, c000, e8800000), \
+ X(_str, 6000, f8400000), \
+ X(_strb, 7000, f8000000), \
+ X(_strh, 8000, f8200000), \
+ X(_str_sp,9000, f84d0000), \
+ X(_sub, 1e00, eba00000), \
+ X(_subs, 1e00, ebb00000), \
+ X(_subi, 8000, f1a00000), \
+ X(_subis, 8000, f1b00000), \
+ X(_sxtb, b240, fa4ff080), \
+ X(_sxth, b200, fa0ff080), \
+ X(_tst, 4200, ea100f00), \
+ X(_uxtb, b2c0, fa5ff080), \
+ X(_uxth, b280, fa1ff080), \
+ X(_nop, bf00, f3af8000), \
+ X(_yield, bf10, f3af8001), \
+ X(_wfe, bf20, f3af8002), \
+ X(_wfi, bf30, f3af8003), \
+ X(_sev, bf40, f3af8004), \
+ X(_sevl, bf50, f3af8005), \
+ X(_udf, de00, f7f0a000)
+
+/* To catch errors in encoding functions, the codes are all offset by
+ 0xF800, putting them in one of the 32-bit prefix ranges, ergo undefined
+ as 16-bit instructions. */
+#define X(a,b,c) T_MNEM##a
+enum t16_32_codes { T16_32_OFFSET = 0xF7FF, T16_32_TAB };
+#undef X
+
+#define X(a,b,c) 0x##b
+static const unsigned short thumb_op16[] = { T16_32_TAB };
+#define THUMB_OP16(n) (thumb_op16[(n) - (T16_32_OFFSET + 1)])
+#undef X
+
+#define X(a,b,c) 0x##c
+static const unsigned int thumb_op32[] = { T16_32_TAB };
+#define THUMB_OP32(n) (thumb_op32[(n) - (T16_32_OFFSET + 1)])
+#define THUMB_SETS_FLAGS(n) (THUMB_OP32 (n) & 0x00100000)
+#undef X
+#undef T16_32_TAB
+
+/* Thumb instruction encoders, in alphabetical order. */
+
+/* ADDW or SUBW. */
+
+static void
+do_t_add_sub_w (void)
+{
+ int Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+
+ /* If Rn is REG_PC, this is ADR; if Rn is REG_SP, then this
+ is the SP-{plus,minus}-immediate form of the instruction. */
+ if (Rn == REG_SP)
+ constraint (Rd == REG_PC, BAD_PC);
+ else
+ reject_bad_reg (Rd);
+
+ inst.instruction |= (Rn << 16) | (Rd << 8);
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
+}
+
+/* Parse an add or subtract instruction. We get here with inst.instruction
+ equalling any of THUMB_OPCODE_add, adds, sub, or subs. */
+
+static void
+do_t_add_sub (void)
+{
+ int Rd, Rs, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+
+ if (Rd == REG_PC)
+ set_it_insn_type_last ();
+
+ if (unified_syntax)
+ {
+ bfd_boolean flags;
+ bfd_boolean narrow;
+ int opcode;
+
+ flags = (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs);
+ if (flags)
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ if (!inst.operands[2].isreg)
+ {
+ int add;
+
+ constraint (Rd == REG_SP && Rs != REG_SP, BAD_SP);
+
+ add = (inst.instruction == T_MNEM_add
+ || inst.instruction == T_MNEM_adds);
+ opcode = 0;
+ if (inst.size_req != 4)
+ {
+ /* Attempt to use a narrow opcode, with relaxation if
+ appropriate. */
+ if (Rd == REG_SP && Rs == REG_SP && !flags)
+ opcode = add ? T_MNEM_inc_sp : T_MNEM_dec_sp;
+ else if (Rd <= 7 && Rs == REG_SP && add && !flags)
+ opcode = T_MNEM_add_sp;
+ else if (Rd <= 7 && Rs == REG_PC && add && !flags)
+ opcode = T_MNEM_add_pc;
+ else if (Rd <= 7 && Rs <= 7 && narrow)
+ {
+ if (flags)
+ opcode = add ? T_MNEM_addis : T_MNEM_subis;
+ else
+ opcode = add ? T_MNEM_addi : T_MNEM_subi;
+ }
+ if (opcode)
+ {
+ inst.instruction = THUMB_OP16(opcode);
+ inst.instruction |= (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ if (inst.size_req != 2)
+ inst.relax = opcode;
+ }
+ else
+ constraint (inst.size_req == 2, BAD_HIREG);
+ }
+ if (inst.size_req == 4
+ || (inst.size_req != 2 && !opcode))
+ {
+ if (Rd == REG_PC)
+ {
+ constraint (add, BAD_PC);
+ constraint (Rs != REG_LR || inst.instruction != T_MNEM_subs,
+ _("only SUBS PC, LR, #const allowed"));
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ constraint (inst.reloc.exp.X_add_number < 0
+ || inst.reloc.exp.X_add_number > 0xff,
+ _("immediate value out of range"));
+ inst.instruction = T2_SUBS_PC_LR
+ | inst.reloc.exp.X_add_number;
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ return;
+ }
+ else if (Rs == REG_PC)
+ {
+ /* Always use addw/subw. */
+ inst.instruction = add ? 0xf20f0000 : 0xf2af0000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff)
+ | 0x10000000;
+ if (flags)
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_T32_ADD_IMM;
+ }
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ }
+ }
+ else
+ {
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[2].shift_kind;
+
+ Rn = inst.operands[2].reg;
+ /* See if we can do this with a 16-bit instruction. */
+ if (!inst.operands[2].shifted && inst.size_req != 4)
+ {
+ if (Rd > 7 || Rs > 7 || Rn > 7)
+ narrow = FALSE;
+
+ if (narrow)
+ {
+ inst.instruction = ((inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_add)
+ ? T_OPCODE_ADD_R3
+ : T_OPCODE_SUB_R3);
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ return;
+ }
+
+ if (inst.instruction == T_MNEM_add && (Rd == Rs || Rd == Rn))
+ {
+ /* Thumb-1 cores (except v6-M) require at least one high
+ register in a narrow non flag setting add. */
+ if (Rd > 7 || Rn > 7
+ || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2)
+ || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_msr))
+ {
+ if (Rd == Rn)
+ {
+ Rn = Rs;
+ Rs = Rd;
+ }
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ inst.instruction |= Rn << 3;
+ return;
+ }
+ }
+ }
+
+ constraint (Rd == REG_PC, BAD_PC);
+ constraint (Rd == REG_SP && Rs != REG_SP, BAD_SP);
+ constraint (Rs == REG_PC, BAD_PC);
+ reject_bad_reg (Rn);
+
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ constraint (Rd == REG_SP && Rs == REG_SP && value > 3,
+ _("shift value over 3 not allowed in thumb mode"));
+ constraint (Rd == REG_SP && Rs == REG_SP && shift != SHIFT_LSL,
+ _("only LSL shift allowed in thumb mode"));
+ encode_thumb32_shifted_operand (2);
+ }
+ }
+ else
+ {
+ constraint (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs,
+ BAD_THUMB32);
+
+ if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
+ {
+ constraint ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
+ || (Rs > 7 && Rs != REG_SP && Rs != REG_PC),
+ BAD_HIREG);
+
+ inst.instruction = (inst.instruction == T_MNEM_add
+ ? 0x0000 : 0x8000);
+ inst.instruction |= (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ return;
+ }
+
+ Rn = inst.operands[2].reg;
+ constraint (inst.operands[2].shifted, _("unshifted register required"));
+
+ /* We now have Rd, Rs, and Rn set to registers. */
+ if (Rd > 7 || Rs > 7 || Rn > 7)
+ {
+ /* Can't do this for SUB. */
+ constraint (inst.instruction == T_MNEM_sub, BAD_HIREG);
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ if (Rs == Rd)
+ inst.instruction |= Rn << 3;
+ else if (Rn == Rd)
+ inst.instruction |= Rs << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
+ }
+ else
+ {
+ inst.instruction = (inst.instruction == T_MNEM_add
+ ? T_OPCODE_ADD_R3 : T_OPCODE_SUB_R3);
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ }
+ }
+}
+
+static void
+do_t_adr (void)
+{
+ unsigned Rd;
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+
+ if (unified_syntax && inst.size_req == 0 && Rd <= 7)
+ {
+ /* Defer to section relaxation. */
+ inst.relax = inst.instruction;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd << 4;
+ }
+ else if (unified_syntax && inst.size_req != 2)
+ {
+ /* Generate a 32-bit opcode. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
+ inst.reloc.pc_rel = 1;
+ }
+ else
+ {
+ /* Generate a 16-bit opcode. */
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
+ inst.reloc.pc_rel = 1;
+
+ inst.instruction |= Rd << 4;
+ }
+}
+
+/* Arithmetic instructions for which there is just one 16-bit
+ instruction encoding, and it allows only two low registers.
+ For maximal compatibility with ARM syntax, we allow three register
+ operands even when Thumb-32 instructions are not available, as long
+ as the first two are identical. For instance, both "sbc r0,r1" and
+ "sbc r0,r0,r1" are allowed. */
+static void
+do_t_arit3 (void)
+{
+ int Rd, Rs, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rs);
+ if (inst.operands[2].isreg)
+ reject_bad_reg (Rn);
+
+ if (unified_syntax)
+ {
+ if (!inst.operands[2].isreg)
+ {
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ bfd_boolean narrow;
+
+ /* See if we can do this with a 16-bit instruction. */
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+
+ if (Rd > 7 || Rn > 7 || Rs > 7)
+ narrow = FALSE;
+ if (inst.operands[2].shifted)
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (narrow
+ && Rd == Rs)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ return;
+ }
+
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
+ }
+ }
+ else
+ {
+ /* On its face this is a lie - the instruction does set the
+ flags. However, the only supported mnemonic in this mode
+ says it doesn't. */
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+
+ constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+ _("unshifted register required"));
+ constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
+ constraint (Rd != Rs,
+ _("dest and source1 must be the same register"));
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ }
+}
+
+/* Similarly, but for instructions where the arithmetic operation is
+ commutative, so we can allow either of them to be different from
+ the destination operand in a 16-bit instruction. For instance, all
+ three of "adc r0,r1", "adc r0,r0,r1", and "adc r0,r1,r0" are
+ accepted. */
+static void
+do_t_arit3c (void)
+{
+ int Rd, Rs, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rs);
+ if (inst.operands[2].isreg)
+ reject_bad_reg (Rn);
+
+ if (unified_syntax)
+ {
+ if (!inst.operands[2].isreg)
+ {
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ bfd_boolean narrow;
+
+ /* See if we can do this with a 16-bit instruction. */
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+
+ if (Rd > 7 || Rn > 7 || Rs > 7)
+ narrow = FALSE;
+ if (inst.operands[2].shifted)
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (narrow)
+ {
+ if (Rd == Rs)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ return;
+ }
+ if (Rd == Rn)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rs << 3;
+ return;
+ }
+ }
+
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
+ }
+ }
+ else
+ {
+ /* On its face this is a lie - the instruction does set the
+ flags. However, the only supported mnemonic in this mode
+ says it doesn't. */
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+
+ constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+ _("unshifted register required"));
+ constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+
+ if (Rd == Rs)
+ inst.instruction |= Rn << 3;
+ else if (Rd == Rn)
+ inst.instruction |= Rs << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
+ }
+}
+
+static void
+do_t_bfc (void)
+{
+ unsigned Rd;
+ unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= (inst.operands[1].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[1].imm & 0x03) << 6;
+ inst.instruction |= msb - 1;
+}
+
+static void
+do_t_bfi (void)
+{
+ int Rd, Rn;
+ unsigned int msb;
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+
+ /* #0 in second position is alternative syntax for bfc, which is
+ the same instruction but with REG_PC in the Rm field. */
+ if (!inst.operands[1].isreg)
+ Rn = REG_PC;
+ else
+ {
+ Rn = inst.operands[1].reg;
+ reject_bad_reg (Rn);
+ }
+
+ msb = inst.operands[2].imm + inst.operands[3].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+ inst.instruction |= msb - 1;
+}
+
+static void
+do_t_bfx (void)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+
+ constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+ _("bit-field extends past end of register"));
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+ inst.instruction |= inst.operands[3].imm - 1;
+}
+
+/* ARM V5 Thumb BLX (argument parse)
+ BLX <target_addr> which is BLX(1)
+ BLX <Rm> which is BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
+
+ ??? How to take advantage of the additional two bits of displacement
+ available in Thumb32 mode? Need new relocation? */
+
+static void
+do_t_blx (void)
+{
+ set_it_insn_type_last ();
+
+ if (inst.operands[0].isreg)
+ {
+ constraint (inst.operands[0].reg == REG_PC, BAD_PC);
+ /* We have a register, so this is BLX(2). */
+ inst.instruction |= inst.operands[0].reg << 3;
+ }
+ else
+ {
+ /* No register. This must be BLX(1). */
+ inst.instruction = 0xf000e800;
+ encode_branch (BFD_RELOC_THUMB_PCREL_BLX);
+ }
+}
+
+static void
+do_t_branch (void)
+{
+ int opcode;
+ int cond;
+ int reloc;
+
+ cond = inst.cond;
+ set_it_insn_type (IF_INSIDE_IT_LAST_INSN);
+
+ if (in_it_block ())
+ {
+ /* Conditional branches inside IT blocks are encoded as unconditional
+ branches. */
+ cond = COND_ALWAYS;
+ }
+ else
+ cond = inst.cond;
+
+ if (cond != COND_ALWAYS)
+ opcode = T_MNEM_bcond;
+ else
+ opcode = inst.instruction;
+
+ if (unified_syntax
+ && (inst.size_req == 4
+ || (inst.size_req != 2
+ && (inst.operands[0].hasreloc
+ || inst.reloc.exp.X_op == O_constant))))
+ {
+ inst.instruction = THUMB_OP32(opcode);
+ if (cond == COND_ALWAYS)
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH25;
+ else
+ {
+ gas_assert (cond != 0xF);
+ inst.instruction |= cond << 22;
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH20;
+ }
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16(opcode);
+ if (cond == COND_ALWAYS)
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ else
+ {
+ inst.instruction |= cond << 8;
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ }
+ /* Allow section relaxation. */
+ if (unified_syntax && inst.size_req != 2)
+ inst.relax = opcode;
+ }
+ inst.reloc.type = reloc;
+ inst.reloc.pc_rel = 1;
+}
+
+/* Actually do the work for Thumb state bkpt and hlt. The only difference
+ between the two is the maximum immediate allowed - which is passed in
+ RANGE. */
+static void
+do_t_bkpt_hlt1 (int range)
+{
+ constraint (inst.cond != COND_ALWAYS,
+ _("instruction is always unconditional"));
+ if (inst.operands[0].present)
+ {
+ constraint (inst.operands[0].imm > range,
+ _("immediate value out of range"));
+ inst.instruction |= inst.operands[0].imm;
+ }
+
+ set_it_insn_type (NEUTRAL_IT_INSN);
+}
+
+static void
+do_t_hlt (void)
+{
+ do_t_bkpt_hlt1 (63);
+}
+
+static void
+do_t_bkpt (void)
+{
+ do_t_bkpt_hlt1 (255);
+}
+
+static void
+do_t_branch23 (void)
+{
+ set_it_insn_type_last ();
+ encode_branch (BFD_RELOC_THUMB_PCREL_BRANCH23);
+
+ /* md_apply_fix blows up with 'bl foo(PLT)' where foo is defined in
+ this file. We used to simply ignore the PLT reloc type here --
+ the branch encoding is now needed to deal with TLSCALL relocs.
+ So if we see a PLT reloc now, put it back to how it used to be to
+ keep the preexisting behaviour. */
+ if (inst.reloc.type == BFD_RELOC_ARM_PLT32)
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+
+#if defined(OBJ_COFF)
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if ( inst.reloc.exp.X_op == O_symbol
+ && inst.reloc.exp.X_add_symbol != NULL
+ && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
+ && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
+ inst.reloc.exp.X_add_symbol =
+ find_real_start (inst.reloc.exp.X_add_symbol);
+#endif
+}
+
+static void
+do_t_bx (void)
+{
+ set_it_insn_type_last ();
+ inst.instruction |= inst.operands[0].reg << 3;
+ /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
+ should cause the alignment to be checked once it is known. This is
+ because BX PC only works if the instruction is word aligned. */
+}
+
+static void
+do_t_bxj (void)
+{
+ int Rm;
+
+ set_it_insn_type_last ();
+ Rm = inst.operands[0].reg;
+ reject_bad_reg (Rm);
+ inst.instruction |= Rm << 16;
+}
+
+static void
+do_t_clz (void)
+{
+ unsigned Rd;
+ unsigned Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_cps (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
+do_t_cpsi (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ if (unified_syntax
+ && (inst.operands[1].present || inst.size_req == 4)
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm))
+ {
+ unsigned int imod = (inst.instruction & 0x0030) >> 4;
+ inst.instruction = 0xf3af8000;
+ inst.instruction |= imod << 9;
+ inst.instruction |= inst.operands[0].imm << 5;
+ if (inst.operands[1].present)
+ inst.instruction |= 0x100 | inst.operands[1].imm;
+ }
+ else
+ {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1)
+ && (inst.operands[0].imm & 4),
+ _("selected processor does not support 'A' form "
+ "of this instruction"));
+ constraint (inst.operands[1].present || inst.size_req == 4,
+ _("Thumb does not support the 2-argument "
+ "form of this instruction"));
+ inst.instruction |= inst.operands[0].imm;
+ }
+}
+
+/* THUMB CPY instruction (argument parse). */
+
+static void
+do_t_cpy (void)
+{
+ if (inst.size_req == 4)
+ {
+ inst.instruction = THUMB_OP32 (T_MNEM_mov);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg;
+ }
+ else
+ {
+ inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+ inst.instruction |= (inst.operands[0].reg & 0x7);
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+}
+
+static void
+do_t_cbz (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ constraint (inst.operands[0].reg > 7, BAD_HIREG);
+ inst.instruction |= inst.operands[0].reg;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH7;
+}
+
+static void
+do_t_dbg (void)
+{
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
+do_t_div (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = (inst.operands[1].present
+ ? inst.operands[1].reg : Rd);
+ Rm = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_hint (void)
+{
+ if (unified_syntax && inst.size_req == 4)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ else
+ inst.instruction = THUMB_OP16 (inst.instruction);
+}
+
+static void
+do_t_it (void)
+{
+ unsigned int cond = inst.operands[0].imm;
+
+ set_it_insn_type (IT_INSN);
+ now_it.mask = (inst.instruction & 0xf) | 0x10;
+ now_it.cc = cond;
+ now_it.warn_deprecated = FALSE;
+
+ /* If the condition is a negative condition, invert the mask. */
+ if ((cond & 0x1) == 0x0)
+ {
+ unsigned int mask = inst.instruction & 0x000f;
+
+ if ((mask & 0x7) == 0)
+ {
+ /* No conversion needed. */
+ now_it.block_length = 1;
+ }
+ else if ((mask & 0x3) == 0)
+ {
+ mask ^= 0x8;
+ now_it.block_length = 2;
+ }
+ else if ((mask & 0x1) == 0)
+ {
+ mask ^= 0xC;
+ now_it.block_length = 3;
+ }
+ else
+ {
+ mask ^= 0xE;
+ now_it.block_length = 4;
+ }
+
+ inst.instruction &= 0xfff0;
+ inst.instruction |= mask;
+ }
+
+ inst.instruction |= cond << 4;
+}
+
+/* Helper function used for both push/pop and ldm/stm. */
+static void
+encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback)
+{
+ bfd_boolean load;
+
+ load = (inst.instruction & (1 << 20)) != 0;
+
+ if (mask & (1 << 13))
+ inst.error = _("SP not allowed in register list");
+
+ if ((mask & (1 << base)) != 0
+ && writeback)
+ inst.error = _("having the base register in the register list when "
+ "using write back is UNPREDICTABLE");
+
+ if (load)
+ {
+ if (mask & (1 << 15))
+ {
+ if (mask & (1 << 14))
+ inst.error = _("LR and PC should not both be in register list");
+ else
+ set_it_insn_type_last ();
+ }
+ }
+ else
+ {
+ if (mask & (1 << 15))
+ inst.error = _("PC not allowed in register list");
+ }
+
+ if ((mask & (mask - 1)) == 0)
+ {
+ /* Single register transfers implemented as str/ldr. */
+ if (writeback)
+ {
+ if (inst.instruction & (1 << 23))
+ inst.instruction = 0x00000b04; /* ia! -> [base], #4 */
+ else
+ inst.instruction = 0x00000d04; /* db! -> [base, #-4]! */
+ }
+ else
+ {
+ if (inst.instruction & (1 << 23))
+ inst.instruction = 0x00800000; /* ia -> [base] */
+ else
+ inst.instruction = 0x00000c04; /* db -> [base, #-4] */
+ }
+
+ inst.instruction |= 0xf8400000;
+ if (load)
+ inst.instruction |= 0x00100000;
+
+ mask = ffs (mask) - 1;
+ mask <<= 12;
+ }
+ else if (writeback)
+ inst.instruction |= WRITE_BACK;
+
+ inst.instruction |= mask;
+ inst.instruction |= base << 16;
+}
+
+static void
+do_t_ldmstm (void)
+{
+ /* This really doesn't seem worth it. */
+ constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+ _("expression too complex"));
+ constraint (inst.operands[1].writeback,
+ _("Thumb load/store multiple does not support {reglist}^"));
+
+ if (unified_syntax)
+ {
+ bfd_boolean narrow;
+ unsigned mask;
+
+ narrow = FALSE;
+ /* See if we can use a 16-bit instruction. */
+ if (inst.instruction < 0xffff /* not ldmdb/stmdb */
+ && inst.size_req != 4
+ && !(inst.operands[1].imm & ~0xff))
+ {
+ mask = 1 << inst.operands[0].reg;
+
+ if (inst.operands[0].reg <= 7)
+ {
+ if (inst.instruction == T_MNEM_stmia
+ ? inst.operands[0].writeback
+ : (inst.operands[0].writeback
+ == !(inst.operands[1].imm & mask)))
+ {
+ if (inst.instruction == T_MNEM_stmia
+ && (inst.operands[1].imm & mask)
+ && (inst.operands[1].imm & (mask - 1)))
+ as_warn (_("value stored for r%d is UNKNOWN"),
+ inst.operands[0].reg);
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
+ }
+ else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
+ {
+ /* This means 1 register in reg list one of 3 situations:
+ 1. Instruction is stmia, but without writeback.
+ 2. lmdia without writeback, but with Rn not in
+ reglist.
+ 3. ldmia with writeback, but with Rn in reglist.
+ Case 3 is UNPREDICTABLE behaviour, so we handle
+ case 1 and 2 which can be converted into a 16-bit
+ str or ldr. The SP cases are handled below. */
+ unsigned long opcode;
+ /* First, record an error for Case 3. */
+ if (inst.operands[1].imm & mask
+ && inst.operands[0].writeback)
+ inst.error =
+ _("having the base register in the register list when "
+ "using write back is UNPREDICTABLE");
+
+ opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str
+ : T_MNEM_ldr);
+ inst.instruction = THUMB_OP16 (opcode);
+ inst.instruction |= inst.operands[0].reg << 3;
+ inst.instruction |= (ffs (inst.operands[1].imm)-1);
+ narrow = TRUE;
+ }
+ }
+ else if (inst.operands[0] .reg == REG_SP)
+ {
+ if (inst.operands[0].writeback)
+ {
+ inst.instruction =
+ THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_push : T_MNEM_pop);
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
+ }
+ else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
+ {
+ inst.instruction =
+ THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_str_sp : T_MNEM_ldr_sp);
+ inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8);
+ narrow = TRUE;
+ }
+ }
+ }
+
+ if (!narrow)
+ {
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+
+ encode_thumb2_ldmstm (inst.operands[0].reg, inst.operands[1].imm,
+ inst.operands[0].writeback);
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7
+ || (inst.operands[1].imm & ~0xff), BAD_HIREG);
+ constraint (inst.instruction != T_MNEM_ldmia
+ && inst.instruction != T_MNEM_stmia,
+ _("Thumb-2 instruction only valid in unified syntax"));
+ if (inst.instruction == T_MNEM_stmia)
+ {
+ if (!inst.operands[0].writeback)
+ as_warn (_("this instruction will write back the base register"));
+ if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
+ && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
+ as_warn (_("value stored for r%d is UNKNOWN"),
+ inst.operands[0].reg);
+ }
+ else
+ {
+ if (!inst.operands[0].writeback
+ && !(inst.operands[1].imm & (1 << inst.operands[0].reg)))
+ as_warn (_("this instruction will write back the base register"));
+ else if (inst.operands[0].writeback
+ && (inst.operands[1].imm & (1 << inst.operands[0].reg)))
+ as_warn (_("this instruction will not write back the base register"));
+ }
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ }
+}
+
+static void
+do_t_ldrex (void)
+{
+ constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+ || inst.operands[1].postind || inst.operands[1].writeback
+ || inst.operands[1].immisreg || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ BAD_ADDR_MODE);
+
+ constraint ((inst.operands[1].reg == REG_PC), BAD_PC);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
+}
+
+static void
+do_t_ldrexd (void)
+{
+ if (!inst.operands[1].present)
+ {
+ constraint (inst.operands[0].reg == REG_LR,
+ _("r14 not allowed as first register "
+ "when second register is omitted"));
+ inst.operands[1].reg = inst.operands[0].reg + 1;
+ }
+ constraint (inst.operands[0].reg == inst.operands[1].reg,
+ BAD_OVERLAP);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+static void
+do_t_ldst (void)
+{
+ unsigned long opcode;
+ int Rn;
+
+ if (inst.operands[0].isreg
+ && !inst.operands[0].preind
+ && inst.operands[0].reg == REG_PC)
+ set_it_insn_type_last ();
+
+ opcode = inst.instruction;
+ if (unified_syntax)
+ {
+ if (!inst.operands[1].isreg)
+ {
+ if (opcode <= 0xffff)
+ inst.instruction = THUMB_OP32 (opcode);
+ if (move_or_literal_pool (0, CONST_THUMB, /*mode_3=*/FALSE))
+ return;
+ }
+ if (inst.operands[1].isreg
+ && !inst.operands[1].writeback
+ && !inst.operands[1].shifted && !inst.operands[1].postind
+ && !inst.operands[1].negative && inst.operands[0].reg <= 7
+ && opcode <= 0xffff
+ && inst.size_req != 4)
+ {
+ /* Insn may have a 16-bit form. */
+ Rn = inst.operands[1].reg;
+ if (inst.operands[1].immisreg)
+ {
+ inst.instruction = THUMB_OP16 (opcode);
+ /* [Rn, Rik] */
+ if (Rn <= 7 && inst.operands[1].imm <= 7)
+ goto op16;
+ else if (opcode != T_MNEM_ldr && opcode != T_MNEM_str)
+ reject_bad_reg (inst.operands[1].imm);
+ }
+ else if ((Rn <= 7 && opcode != T_MNEM_ldrsh
+ && opcode != T_MNEM_ldrsb)
+ || ((Rn == REG_PC || Rn == REG_SP) && opcode == T_MNEM_ldr)
+ || (Rn == REG_SP && opcode == T_MNEM_str))
+ {
+ /* [Rn, #const] */
+ if (Rn > 7)
+ {
+ if (Rn == REG_PC)
+ {
+ if (inst.reloc.pc_rel)
+ opcode = T_MNEM_ldr_pc2;
+ else
+ opcode = T_MNEM_ldr_pc;
+ }
+ else
+ {
+ if (opcode == T_MNEM_ldr)
+ opcode = T_MNEM_ldr_sp;
+ else
+ opcode = T_MNEM_str_sp;
+ }
+ inst.instruction = inst.operands[0].reg << 8;
+ }
+ else
+ {
+ inst.instruction = inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ inst.instruction |= THUMB_OP16 (opcode);
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ else
+ inst.relax = opcode;
+ return;
+ }
+ }
+ /* Definitely a 32-bit variant. */
+
+ /* Warning for Erratum 752419. */
+ if (opcode == T_MNEM_ldr
+ && inst.operands[0].reg == REG_SP
+ && inst.operands[1].writeback == 1
+ && !inst.operands[1].immisreg)
+ {
+ if (no_cpu_selected ()
+ || (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7)
+ && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a)
+ && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7r)))
+ as_warn (_("This instruction may be unpredictable "
+ "if executed on M-profile cores "
+ "with interrupts enabled."));
+ }
+
+ /* Do some validations regarding addressing modes. */
+ if (inst.operands[1].immisreg)
+ reject_bad_reg (inst.operands[1].imm);
+
+ constraint (inst.operands[1].writeback == 1
+ && inst.operands[0].reg == inst.operands[1].reg,
+ BAD_OVERLAP);
+
+ inst.instruction = THUMB_OP32 (opcode);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
+ check_ldr_r15_aligned ();
+ return;
+ }
+
+ constraint (inst.operands[0].reg > 7, BAD_HIREG);
+
+ if (inst.instruction == T_MNEM_ldrsh || inst.instruction == T_MNEM_ldrsb)
+ {
+ /* Only [Rn,Rm] is acceptable. */
+ constraint (inst.operands[1].reg > 7 || inst.operands[1].imm > 7, BAD_HIREG);
+ constraint (!inst.operands[1].isreg || !inst.operands[1].immisreg
+ || inst.operands[1].postind || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ _("Thumb does not support this addressing mode"));
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ goto op16;
+ }
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, CONST_THUMB, /*mode_3=*/FALSE))
+ return;
+
+ constraint (!inst.operands[1].preind
+ || inst.operands[1].shifted
+ || inst.operands[1].writeback,
+ _("Thumb does not support this addressing mode"));
+ if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
+ {
+ constraint (inst.instruction & 0x0600,
+ _("byte or halfword not valid for base register"));
+ constraint (inst.operands[1].reg == REG_PC
+ && !(inst.instruction & THUMB_LOAD_BIT),
+ _("r15 based store not allowed"));
+ constraint (inst.operands[1].immisreg,
+ _("invalid base register for register offset"));
+
+ if (inst.operands[1].reg == REG_PC)
+ inst.instruction = T_OPCODE_LDR_PC;
+ else if (inst.instruction & THUMB_LOAD_BIT)
+ inst.instruction = T_OPCODE_LDR_SP;
+ else
+ inst.instruction = T_OPCODE_STR_SP;
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ return;
+ }
+
+ constraint (inst.operands[1].reg > 7, BAD_HIREG);
+ if (!inst.operands[1].immisreg)
+ {
+ /* Immediate offset. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ return;
+ }
+
+ /* Register offset. */
+ constraint (inst.operands[1].imm > 7, BAD_HIREG);
+ constraint (inst.operands[1].negative,
+ _("Thumb does not support this addressing mode"));
+
+ op16:
+ switch (inst.instruction)
+ {
+ case T_OPCODE_STR_IW: inst.instruction = T_OPCODE_STR_RW; break;
+ case T_OPCODE_STR_IH: inst.instruction = T_OPCODE_STR_RH; break;
+ case T_OPCODE_STR_IB: inst.instruction = T_OPCODE_STR_RB; break;
+ case T_OPCODE_LDR_IW: inst.instruction = T_OPCODE_LDR_RW; break;
+ case T_OPCODE_LDR_IH: inst.instruction = T_OPCODE_LDR_RH; break;
+ case T_OPCODE_LDR_IB: inst.instruction = T_OPCODE_LDR_RB; break;
+ case 0x5600 /* ldrsb */:
+ case 0x5e00 /* ldrsh */: break;
+ default: abort ();
+ }
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.instruction |= inst.operands[1].imm << 6;
+}
+
+static void
+do_t_ldstd (void)
+{
+ if (!inst.operands[1].present)
+ {
+ inst.operands[1].reg = inst.operands[0].reg + 1;
+ constraint (inst.operands[0].reg == REG_LR,
+ _("r14 not allowed here"));
+ constraint (inst.operands[0].reg == REG_R12,
+ _("r12 not allowed here"));
+ }
+
+ if (inst.operands[2].writeback
+ && (inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[1].reg == inst.operands[2].reg))
+ as_warn (_("base register written back, and overlaps "
+ "one of transfer registers"));
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ encode_thumb32_addr_mode (2, /*is_t=*/FALSE, /*is_d=*/TRUE);
+}
+
+static void
+do_t_ldstt (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_thumb32_addr_mode (1, /*is_t=*/TRUE, /*is_d=*/FALSE);
+}
+
+static void
+do_t_mla (void)
+{
+ unsigned Rd, Rn, Rm, Ra;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+ Ra = inst.operands[3].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+ reject_bad_reg (Ra);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+ inst.instruction |= Ra << 12;
+}
+
+static void
+do_t_mlal (void)
+{
+ unsigned RdLo, RdHi, Rn, Rm;
+
+ RdLo = inst.operands[0].reg;
+ RdHi = inst.operands[1].reg;
+ Rn = inst.operands[2].reg;
+ Rm = inst.operands[3].reg;
+
+ reject_bad_reg (RdLo);
+ reject_bad_reg (RdHi);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= RdLo << 12;
+ inst.instruction |= RdHi << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_mov_cmp (void)
+{
+ unsigned Rn, Rm;
+
+ Rn = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ if (Rn == REG_PC)
+ set_it_insn_type_last ();
+
+ if (unified_syntax)
+ {
+ int r0off = (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs) ? 8 : 16;
+ unsigned long opcode;
+ bfd_boolean narrow;
+ bfd_boolean low_regs;
+
+ low_regs = (Rn <= 7 && Rm <= 7);
+ opcode = inst.instruction;
+ if (in_it_block ())
+ narrow = opcode != T_MNEM_movs;
+ else
+ narrow = opcode != T_MNEM_movs || low_regs;
+ if (inst.size_req == 4
+ || inst.operands[1].shifted)
+ narrow = FALSE;
+
+ /* MOVS PC, LR is encoded as SUBS PC, LR, #0. */
+ if (opcode == T_MNEM_movs && inst.operands[1].isreg
+ && !inst.operands[1].shifted
+ && Rn == REG_PC
+ && Rm == REG_LR)
+ {
+ inst.instruction = T2_SUBS_PC_LR;
+ return;
+ }
+
+ if (opcode == T_MNEM_cmp)
+ {
+ constraint (Rn == REG_PC, BAD_PC);
+ if (narrow)
+ {
+ /* In the Thumb-2 ISA, use of R13 as Rm is deprecated,
+ but valid. */
+ warn_deprecated_sp (Rm);
+ /* R15 was documented as a valid choice for Rm in ARMv6,
+ but as UNPREDICTABLE in ARMv7. ARM's proprietary
+ tools reject R15, so we do too. */
+ constraint (Rm == REG_PC, BAD_PC);
+ }
+ else
+ reject_bad_reg (Rm);
+ }
+ else if (opcode == T_MNEM_mov
+ || opcode == T_MNEM_movs)
+ {
+ if (inst.operands[1].isreg)
+ {
+ if (opcode == T_MNEM_movs)
+ {
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+ }
+ else if (narrow)
+ {
+ /* This is mov.n. */
+ if ((Rn == REG_SP || Rn == REG_PC)
+ && (Rm == REG_SP || Rm == REG_PC))
+ {
+ as_warn (_("Use of r%u as a source register is "
+ "deprecated when r%u is the destination "
+ "register."), Rm, Rn);
+ }
+ }
+ else
+ {
+ /* This is mov.w. */
+ constraint (Rn == REG_PC, BAD_PC);
+ constraint (Rm == REG_PC, BAD_PC);
+ constraint (Rn == REG_SP && Rm == REG_SP, BAD_SP);
+ }
+ }
+ else
+ reject_bad_reg (Rn);
+ }
+
+ if (!inst.operands[1].isreg)
+ {
+ /* Immediate operand. */
+ if (!in_it_block () && opcode == T_MNEM_mov)
+ narrow = 0;
+ if (low_regs && narrow)
+ {
+ inst.instruction = THUMB_OP16 (opcode);
+ inst.instruction |= Rn << 8;
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ else
+ inst.relax = opcode;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rn << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ }
+ else if (inst.operands[1].shifted && inst.operands[1].immisreg
+ && (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs))
+ {
+ /* Register shifts are encoded as separate shift instructions. */
+ bfd_boolean flags = (inst.instruction == T_MNEM_movs);
+
+ if (in_it_block ())
+ narrow = !flags;
+ else
+ narrow = flags;
+
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (!low_regs || inst.operands[1].imm > 7)
+ narrow = FALSE;
+
+ if (Rn != Rm)
+ narrow = FALSE;
+
+ switch (inst.operands[1].shift_kind)
+ {
+ case SHIFT_LSL:
+ opcode = narrow ? T_OPCODE_LSL_R : THUMB_OP32 (T_MNEM_lsl);
+ break;
+ case SHIFT_ASR:
+ opcode = narrow ? T_OPCODE_ASR_R : THUMB_OP32 (T_MNEM_asr);
+ break;
+ case SHIFT_LSR:
+ opcode = narrow ? T_OPCODE_LSR_R : THUMB_OP32 (T_MNEM_lsr);
+ break;
+ case SHIFT_ROR:
+ opcode = narrow ? T_OPCODE_ROR_R : THUMB_OP32 (T_MNEM_ror);
+ break;
+ default:
+ abort ();
+ }
+
+ inst.instruction = opcode;
+ if (narrow)
+ {
+ inst.instruction |= Rn;
+ inst.instruction |= inst.operands[1].imm << 3;
+ }
+ else
+ {
+ if (flags)
+ inst.instruction |= CONDS_BIT;
+
+ inst.instruction |= Rn << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ }
+ else if (!narrow)
+ {
+ /* Some mov with immediate shift have narrow variants.
+ Register shifts are handled above. */
+ if (low_regs && inst.operands[1].shifted
+ && (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs))
+ {
+ if (in_it_block ())
+ narrow = (inst.instruction == T_MNEM_mov);
+ else
+ narrow = (inst.instruction == T_MNEM_movs);
+ }
+
+ if (narrow)
+ {
+ switch (inst.operands[1].shift_kind)
+ {
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
+ default: narrow = FALSE; break;
+ }
+ }
+
+ if (narrow)
+ {
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rn << r0off;
+ encode_thumb32_shifted_operand (1);
+ }
+ }
+ else
+ switch (inst.instruction)
+ {
+ case T_MNEM_mov:
+ /* In v4t or v5t a move of two lowregs produces unpredictable
+ results. Don't allow this. */
+ if (low_regs)
+ {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6),
+ "MOV Rd, Rs with two low registers is not "
+ "permitted on this architecture");
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ arm_ext_v6);
+ }
+
+ inst.instruction = T_OPCODE_MOV_HR;
+ inst.instruction |= (Rn & 0x8) << 4;
+ inst.instruction |= (Rn & 0x7);
+ inst.instruction |= Rm << 3;
+ break;
+
+ case T_MNEM_movs:
+ /* We know we have low registers at this point.
+ Generate LSLS Rd, Rs, #0. */
+ inst.instruction = T_OPCODE_LSL_I;
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ break;
+
+ case T_MNEM_cmp:
+ if (low_regs)
+ {
+ inst.instruction = T_OPCODE_CMP_LR;
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+ else
+ {
+ inst.instruction = T_OPCODE_CMP_HR;
+ inst.instruction |= (Rn & 0x8) << 4;
+ inst.instruction |= (Rn & 0x7);
+ inst.instruction |= Rm << 3;
+ }
+ break;
+ }
+ return;
+ }
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+
+ /* PR 10443: Do not silently ignore shifted operands. */
+ constraint (inst.operands[1].shifted,
+ _("shifts in CMP/MOV instructions are only supported in unified syntax"));
+
+ if (inst.operands[1].isreg)
+ {
+ if (Rn < 8 && Rm < 8)
+ {
+ /* A move of two lowregs is encoded as ADD Rd, Rs, #0
+ since a MOV instruction produces unpredictable results. */
+ if (inst.instruction == T_OPCODE_MOV_I8)
+ inst.instruction = T_OPCODE_ADD_I3;
+ else
+ inst.instruction = T_OPCODE_CMP_LR;
+
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+ else
+ {
+ if (inst.instruction == T_OPCODE_MOV_I8)
+ inst.instruction = T_OPCODE_MOV_HR;
+ else
+ inst.instruction = T_OPCODE_CMP_HR;
+ do_t_cpy ();
+ }
+ }
+ else
+ {
+ constraint (Rn > 7,
+ _("only lo regs allowed with immediate"));
+ inst.instruction |= Rn << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ }
+}
+
+static void
+do_t_mov16 (void)
+{
+ unsigned Rd;
+ bfd_vma imm;
+ bfd_boolean top;
+
+ top = (inst.instruction & 0x00800000) != 0;
+ if (inst.reloc.type == BFD_RELOC_ARM_MOVW)
+ {
+ constraint (top, _(":lower16: not allowed this instruction"));
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVW;
+ }
+ else if (inst.reloc.type == BFD_RELOC_ARM_MOVT)
+ {
+ constraint (!top, _(":upper16: not allowed this instruction"));
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVT;
+ }
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+
+ inst.instruction |= Rd << 8;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ imm = inst.reloc.exp.X_add_number;
+ inst.instruction |= (imm & 0xf000) << 4;
+ inst.instruction |= (imm & 0x0800) << 15;
+ inst.instruction |= (imm & 0x0700) << 4;
+ inst.instruction |= (imm & 0x00ff);
+ }
+}
+
+static void
+do_t_mvn_tst (void)
+{
+ unsigned Rn, Rm;
+
+ Rn = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ if (inst.instruction == T_MNEM_cmp
+ || inst.instruction == T_MNEM_cmn)
+ constraint (Rn == REG_PC, BAD_PC);
+ else
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ if (unified_syntax)
+ {
+ int r0off = (inst.instruction == T_MNEM_mvn
+ || inst.instruction == T_MNEM_mvns) ? 8 : 16;
+ bfd_boolean narrow;
+
+ if (inst.size_req == 4
+ || inst.instruction > 0xffff
+ || inst.operands[1].shifted
+ || Rn > 7 || Rm > 7)
+ narrow = FALSE;
+ else if (inst.instruction == T_MNEM_cmn
+ || inst.instruction == T_MNEM_tst)
+ narrow = TRUE;
+ else if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+
+ if (!inst.operands[1].isreg)
+ {
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rn << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ /* See if we can do this with a 16-bit instruction. */
+ if (narrow)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+ else
+ {
+ constraint (inst.operands[1].shifted
+ && inst.operands[1].immisreg,
+ _("shift must be constant"));
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rn << r0off;
+ encode_thumb32_shifted_operand (1);
+ }
+ }
+ }
+ else
+ {
+ constraint (inst.instruction > 0xffff
+ || inst.instruction == T_MNEM_mvns, BAD_THUMB32);
+ constraint (!inst.operands[1].isreg || inst.operands[1].shifted,
+ _("unshifted register required"));
+ constraint (Rn > 7 || Rm > 7,
+ BAD_HIREG);
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+}
+
+static void
+do_t_mrs (void)
+{
+ unsigned Rd;
+
+ if (do_vfp_nsyn_mrs () == SUCCESS)
+ return;
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+ inst.instruction |= Rd << 8;
+
+ if (inst.operands[1].isreg)
+ {
+ unsigned br = inst.operands[1].reg;
+ if (((br & 0x200) == 0) && ((br & 0xf000) != 0xf000))
+ as_bad (_("bad register for mrs"));
+
+ inst.instruction |= br & (0xf << 16);
+ inst.instruction |= (br & 0x300) >> 4;
+ inst.instruction |= (br & SPSR_BIT) >> 2;
+ }
+ else
+ {
+ int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
+
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
+ {
+ /* PR gas/12698: The constraint is only applied for m_profile.
+ If the user has specified -march=all, we want to ignore it as
+ we are building for any CPU type, including non-m variants. */
+ bfd_boolean m_profile = selected_cpu.core != arm_arch_any.core;
+ constraint ((flags != 0) && m_profile, _("selected processor does "
+ "not support requested special purpose register"));
+ }
+ else
+ /* mrs only accepts APSR/CPSR/SPSR/CPSR_all/SPSR_all (for non-M profile
+ devices). */
+ constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f),
+ _("'APSR', 'CPSR' or 'SPSR' expected"));
+
+ inst.instruction |= (flags & SPSR_BIT) >> 2;
+ inst.instruction |= inst.operands[1].imm & 0xff;
+ inst.instruction |= 0xf0000;
+ }
+}
+
+static void
+do_t_msr (void)
+{
+ int flags;
+ unsigned Rn;
+
+ if (do_vfp_nsyn_msr () == SUCCESS)
+ return;
+
+ constraint (!inst.operands[1].isreg,
+ _("Thumb encoding does not support an immediate here"));
+
+ if (inst.operands[0].isreg)
+ flags = (int)(inst.operands[0].reg);
+ else
+ flags = inst.operands[0].imm;
+
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
+ {
+ int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
+
+ /* PR gas/12698: The constraint is only applied for m_profile.
+ If the user has specified -march=all, we want to ignore it as
+ we are building for any CPU type, including non-m variants. */
+ bfd_boolean m_profile = selected_cpu.core != arm_arch_any.core;
+ constraint (((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && (bits & ~(PSR_s | PSR_f)) != 0)
+ || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && bits != PSR_f)) && m_profile,
+ _("selected processor does not support requested special "
+ "purpose register"));
+ }
+ else
+ constraint ((flags & 0xff) != 0, _("selected processor does not support "
+ "requested special purpose register"));
+
+ Rn = inst.operands[1].reg;
+ reject_bad_reg (Rn);
+
+ inst.instruction |= (flags & SPSR_BIT) >> 2;
+ inst.instruction |= (flags & 0xf0000) >> 8;
+ inst.instruction |= (flags & 0x300) >> 4;
+ inst.instruction |= (flags & 0xff);
+ inst.instruction |= Rn << 16;
+}
+
+static void
+do_t_mul (void)
+{
+ bfd_boolean narrow;
+ unsigned Rd, Rn, Rm;
+
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[0].reg;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
+ if (unified_syntax)
+ {
+ if (inst.size_req == 4
+ || (Rd != Rn
+ && Rd != Rm)
+ || Rn > 7
+ || Rm > 7)
+ narrow = FALSE;
+ else if (inst.instruction == T_MNEM_muls)
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ }
+ else
+ {
+ constraint (inst.instruction == T_MNEM_muls, BAD_THUMB32);
+ constraint (Rn > 7 || Rm > 7,
+ BAD_HIREG);
+ narrow = TRUE;
+ }
+
+ if (narrow)
+ {
+ /* 16-bit MULS/Conditional MUL. */
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+
+ if (Rd == Rn)
+ inst.instruction |= Rm << 3;
+ else if (Rd == Rm)
+ inst.instruction |= Rn << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
+ }
+ else
+ {
+ constraint (inst.instruction != T_MNEM_mul,
+ _("Thumb-2 MUL must not set flags"));
+ /* 32-bit MUL. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm << 0;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+ }
+}
+
+static void
+do_t_mull (void)
+{
+ unsigned RdLo, RdHi, Rn, Rm;
+
+ RdLo = inst.operands[0].reg;
+ RdHi = inst.operands[1].reg;
+ Rn = inst.operands[2].reg;
+ Rm = inst.operands[3].reg;
+
+ reject_bad_reg (RdLo);
+ reject_bad_reg (RdHi);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= RdLo << 12;
+ inst.instruction |= RdHi << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+
+ if (RdLo == RdHi)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+}
+
+static void
+do_t_nop (void)
+{
+ set_it_insn_type (NEUTRAL_IT_INSN);
+
+ if (unified_syntax)
+ {
+ if (inst.size_req == 4 || inst.operands[0].imm > 15)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm;
+ }
+ else
+ {
+ /* PR9722: Check for Thumb2 availability before
+ generating a thumb2 nop instruction. */
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2))
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm << 4;
+ }
+ else
+ inst.instruction = 0x46c0;
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].present,
+ _("Thumb does not support NOP with hints"));
+ inst.instruction = 0x46c0;
+ }
+}
+
+static void
+do_t_neg (void)
+{
+ if (unified_syntax)
+ {
+ bfd_boolean narrow;
+
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (!narrow)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+ BAD_HIREG);
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+}
+
+static void
+do_t_orn (void)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].present ? inst.operands[1].reg : Rd;
+
+ reject_bad_reg (Rd);
+ /* Rn == REG_SP is unpredictable; Rn == REG_PC is MVN. */
+ reject_bad_reg (Rn);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+
+ if (!inst.operands[2].isreg)
+ {
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ unsigned Rm;
+
+ Rm = inst.operands[2].reg;
+ reject_bad_reg (Rm);
+
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ encode_thumb32_shifted_operand (2);
+ }
+}
+
+static void
+do_t_pkhbt (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+ if (inst.operands[3].present)
+ {
+ unsigned int val = inst.reloc.exp.X_add_number;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ inst.instruction |= (val & 0x1c) << 10;
+ inst.instruction |= (val & 0x03) << 6;
+ }
+}
+
+static void
+do_t_pkhtb (void)
+{
+ if (!inst.operands[3].present)
+ {
+ unsigned Rtmp;
+
+ inst.instruction &= ~0x00000020;
+
+ /* PR 10168. Swap the Rm and Rn registers. */
+ Rtmp = inst.operands[1].reg;
+ inst.operands[1].reg = inst.operands[2].reg;
+ inst.operands[2].reg = Rtmp;
+ }
+ do_t_pkhbt ();
+}
+
+static void
+do_t_pld (void)
+{
+ if (inst.operands[0].immisreg)
+ reject_bad_reg (inst.operands[0].imm);
+
+ encode_thumb32_addr_mode (0, /*is_t=*/FALSE, /*is_d=*/FALSE);
+}
+
+static void
+do_t_push_pop (void)
+{
+ unsigned mask;
+
+ constraint (inst.operands[0].writeback,
+ _("push/pop do not support {reglist}^"));
+ constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+ _("expression too complex"));
+
+ mask = inst.operands[0].imm;
+ if (inst.size_req != 4 && (mask & ~0xff) == 0)
+ inst.instruction = THUMB_OP16 (inst.instruction) | mask;
+ else if (inst.size_req != 4
+ && (mask & ~0xff) == (1 << (inst.instruction == T_MNEM_push
+ ? REG_LR : REG_PC)))
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= THUMB_PP_PC_LR;
+ inst.instruction |= mask & 0xff;
+ }
+ else if (unified_syntax)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ encode_thumb2_ldmstm (13, mask, TRUE);
+ }
+ else
+ {
+ inst.error = _("invalid register list to push/pop instruction");
+ return;
+ }
+}
+
+static void
+do_t_rbit (void)
+{
+ unsigned Rd, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_rev (void)
+{
+ unsigned Rd, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ if (Rd <= 7 && Rm <= 7
+ && inst.size_req != 4)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rm << 3;
+ }
+ else if (unified_syntax)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= Rm;
+ }
+ else
+ inst.error = BAD_HIREG;
+}
+
+static void
+do_t_rrx (void)
+{
+ unsigned Rd, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_rsb (void)
+{
+ unsigned Rd, Rs;
+
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rs);
+ if (inst.operands[2].isreg)
+ reject_bad_reg (inst.operands[2].reg);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ if (!inst.operands[2].isreg)
+ {
+ bfd_boolean narrow;
+
+ if ((inst.instruction & 0x00100000) != 0)
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+
+ if (Rd > 7 || Rs > 7)
+ narrow = FALSE;
+
+ if (inst.size_req == 4 || !unified_syntax)
+ narrow = FALSE;
+
+ if (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0)
+ narrow = FALSE;
+
+ /* Turn rsb #0 into 16-bit neg. We should probably do this via
+ relaxation, but it doesn't seem worth the hassle. */
+ if (narrow)
+ {
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ inst.instruction = THUMB_OP16 (T_MNEM_negs);
+ inst.instruction |= Rs << 3;
+ inst.instruction |= Rd;
+ }
+ else
+ {
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ }
+ else
+ encode_thumb32_shifted_operand (2);
+}
+
+static void
+do_t_setend (void)
+{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_warn (_("setend use is deprecated for ARMv8"));
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ if (inst.operands[0].imm)
+ inst.instruction |= 0x8;
+}
+
+static void
+do_t_shift (void)
+{
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg;
+
+ if (unified_syntax)
+ {
+ bfd_boolean narrow;
+ int shift_kind;
+
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr:
+ case T_MNEM_asrs: shift_kind = SHIFT_ASR; break;
+ case T_MNEM_lsl:
+ case T_MNEM_lsls: shift_kind = SHIFT_LSL; break;
+ case T_MNEM_lsr:
+ case T_MNEM_lsrs: shift_kind = SHIFT_LSR; break;
+ case T_MNEM_ror:
+ case T_MNEM_rors: shift_kind = SHIFT_ROR; break;
+ default: abort ();
+ }
+
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)
+ narrow = FALSE;
+ if (!inst.operands[2].isreg && shift_kind == SHIFT_ROR)
+ narrow = FALSE;
+ if (inst.operands[2].isreg
+ && (inst.operands[1].reg != inst.operands[0].reg
+ || inst.operands[2].reg > 7))
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ reject_bad_reg (inst.operands[0].reg);
+ reject_bad_reg (inst.operands[1].reg);
+
+ if (!narrow)
+ {
+ if (inst.operands[2].isreg)
+ {
+ reject_bad_reg (inst.operands[2].reg);
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
+ }
+ else
+ {
+ inst.operands[1].shifted = 1;
+ inst.operands[1].shift_kind = shift_kind;
+ inst.instruction = THUMB_OP32 (THUMB_SETS_FLAGS (inst.instruction)
+ ? T_MNEM_movs : T_MNEM_mov);
+ inst.instruction |= inst.operands[0].reg << 8;
+ encode_thumb32_shifted_operand (1);
+ /* Prevent the incorrect generation of an ARM_IMMEDIATE fixup. */
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
+ }
+ else
+ {
+ if (inst.operands[2].isreg)
+ {
+ switch (shift_kind)
+ {
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_R; break;
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_R; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_R; break;
+ case SHIFT_ROR: inst.instruction = T_OPCODE_ROR_R; break;
+ default: abort ();
+ }
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[2].reg << 3;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
+ }
+ else
+ {
+ switch (shift_kind)
+ {
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
+ default: abort ();
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7
+ || inst.operands[1].reg > 7, BAD_HIREG);
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+
+ if (inst.operands[2].isreg) /* Rd, {Rs,} Rn */
+ {
+ constraint (inst.operands[2].reg > 7, BAD_HIREG);
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("source1 and dest must be same register"));
+
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_R; break;
+ case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_R; break;
+ case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_R; break;
+ case T_MNEM_ror: inst.instruction = T_OPCODE_ROR_R; break;
+ default: abort ();
+ }
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[2].reg << 3;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
+ }
+ else
+ {
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_I; break;
+ case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_I; break;
+ case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_I; break;
+ case T_MNEM_ror: inst.error = _("ror #imm not supported"); return;
+ default: abort ();
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ }
+}
+
+static void
+do_t_simd (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_simd2 (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_smc (void)
+{
+ unsigned int value = inst.reloc.exp.X_add_number;
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a),
+ _("SMC is not permitted on this architecture"));
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ inst.instruction |= (value & 0xf000) >> 12;
+ inst.instruction |= (value & 0x0ff0);
+ inst.instruction |= (value & 0x000f) << 16;
+ /* PR gas/15623: SMC instructions must be last in an IT block. */
+ set_it_insn_type_last ();
+}
+
+static void
+do_t_hvc (void)
+{
+ unsigned int value = inst.reloc.exp.X_add_number;
+
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ inst.instruction |= (value & 0x0fff);
+ inst.instruction |= (value & 0xf000) << 4;
+}
+
+static void
+do_t_ssat_usat (int bias)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= inst.operands[1].imm - bias;
+ inst.instruction |= Rn << 16;
+
+ if (inst.operands[3].present)
+ {
+ offsetT shift_amount = inst.reloc.exp.X_add_number;
+
+ inst.reloc.type = BFD_RELOC_UNUSED;
+
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+
+ if (shift_amount != 0)
+ {
+ constraint (shift_amount > 31,
+ _("shift expression is too large"));
+
+ if (inst.operands[3].shift_kind == SHIFT_ASR)
+ inst.instruction |= 0x00200000; /* sh bit. */
+
+ inst.instruction |= (shift_amount & 0x1c) << 10;
+ inst.instruction |= (shift_amount & 0x03) << 6;
+ }
+ }
+}
+
+static void
+do_t_ssat (void)
+{
+ do_t_ssat_usat (1);
+}
+
+static void
+do_t_ssat16 (void)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= inst.operands[1].imm - 1;
+ inst.instruction |= Rn << 16;
+}
+
+static void
+do_t_strex (void)
+{
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative,
+ BAD_ADDR_MODE);
+
+ constraint (inst.operands[2].reg == REG_PC, BAD_PC);
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
+}
+
+static void
+do_t_strexd (void)
+{
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[1].reg + 1;
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[0].reg == inst.operands[3].reg,
+ BAD_OVERLAP);
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 16;
+}
+
+static void
+do_t_sxtah (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+ inst.instruction |= inst.operands[3].imm << 4;
+}
+
+static void
+do_t_sxth (void)
+{
+ unsigned Rd, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ if (inst.instruction <= 0xffff
+ && inst.size_req != 4
+ && Rd <= 7 && Rm <= 7
+ && (!inst.operands[2].present || inst.operands[2].imm == 0))
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rm << 3;
+ }
+ else if (unified_syntax)
+ {
+ if (inst.instruction <= 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm;
+ inst.instruction |= inst.operands[2].imm << 4;
+ }
+ else
+ {
+ constraint (inst.operands[2].present && inst.operands[2].imm != 0,
+ _("Thumb encoding does not support rotation"));
+ constraint (1, BAD_HIREG);
+ }
+}
+
+static void
+do_t_swi (void)
+{
+ /* We have to do the following check manually as ARM_EXT_OS only applies
+ to ARM_EXT_V6M. */
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6m))
+ {
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_os)
+ /* This only applies to the v6m howver, not later architectures. */
+ && ! ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7))
+ as_bad (_("SVC is not permitted on this architecture"));
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_os);
+ }
+
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
+}
+
+static void
+do_t_tb (void)
+{
+ unsigned Rn, Rm;
+ int half;
+
+ half = (inst.instruction & 0x10) != 0;
+ set_it_insn_type_last ();
+ constraint (inst.operands[0].immisreg,
+ _("instruction requires register index"));
+
+ Rn = inst.operands[0].reg;
+ Rm = inst.operands[0].imm;
+
+ constraint (Rn == REG_SP, BAD_SP);
+ reject_bad_reg (Rm);
+
+ constraint (!half && inst.operands[0].shifted,
+ _("instruction does not allow shifted index"));
+ inst.instruction |= (Rn << 16) | Rm;
+}
+
+static void
+do_t_udf (void)
+{
+ if (!inst.operands[0].present)
+ inst.operands[0].imm = 0;
+
+ if ((unsigned int) inst.operands[0].imm > 255 || inst.size_req == 4)
+ {
+ constraint (inst.size_req == 2,
+ _("immediate value out of range"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= (inst.operands[0].imm & 0xf000u) << 4;
+ inst.instruction |= (inst.operands[0].imm & 0x0fffu) << 0;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm;
+ }
+
+ set_it_insn_type (NEUTRAL_IT_INSN);
+}
+
+
+static void
+do_t_usat (void)
+{
+ do_t_ssat_usat (0);
+}
+
+static void
+do_t_usat16 (void)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= inst.operands[1].imm;
+ inst.instruction |= Rn << 16;
+}
+
+/* Neon instruction encoder helpers. */
+
+/* Encodings for the different types for various Neon opcodes. */
+
+/* An "invalid" code for the following tables. */
+#define N_INV -1u
+
+struct neon_tab_entry
+{
+ unsigned integer;
+ unsigned float_or_poly;
+ unsigned scalar_or_imm;
+};
+
+/* Map overloaded Neon opcodes to their respective encodings. */
+#define NEON_ENC_TAB \
+ X(vabd, 0x0000700, 0x1200d00, N_INV), \
+ X(vmax, 0x0000600, 0x0000f00, N_INV), \
+ X(vmin, 0x0000610, 0x0200f00, N_INV), \
+ X(vpadd, 0x0000b10, 0x1000d00, N_INV), \
+ X(vpmax, 0x0000a00, 0x1000f00, N_INV), \
+ X(vpmin, 0x0000a10, 0x1200f00, N_INV), \
+ X(vadd, 0x0000800, 0x0000d00, N_INV), \
+ X(vsub, 0x1000800, 0x0200d00, N_INV), \
+ X(vceq, 0x1000810, 0x0000e00, 0x1b10100), \
+ X(vcge, 0x0000310, 0x1000e00, 0x1b10080), \
+ X(vcgt, 0x0000300, 0x1200e00, 0x1b10000), \
+ /* Register variants of the following two instructions are encoded as
+ vcge / vcgt with the operands reversed. */ \
+ X(vclt, 0x0000300, 0x1200e00, 0x1b10200), \
+ X(vcle, 0x0000310, 0x1000e00, 0x1b10180), \
+ X(vfma, N_INV, 0x0000c10, N_INV), \
+ X(vfms, N_INV, 0x0200c10, N_INV), \
+ X(vmla, 0x0000900, 0x0000d10, 0x0800040), \
+ X(vmls, 0x1000900, 0x0200d10, 0x0800440), \
+ X(vmul, 0x0000910, 0x1000d10, 0x0800840), \
+ X(vmull, 0x0800c00, 0x0800e00, 0x0800a40), /* polynomial not float. */ \
+ X(vmlal, 0x0800800, N_INV, 0x0800240), \
+ X(vmlsl, 0x0800a00, N_INV, 0x0800640), \
+ X(vqdmlal, 0x0800900, N_INV, 0x0800340), \
+ X(vqdmlsl, 0x0800b00, N_INV, 0x0800740), \
+ X(vqdmull, 0x0800d00, N_INV, 0x0800b40), \
+ X(vqdmulh, 0x0000b00, N_INV, 0x0800c40), \
+ X(vqrdmulh, 0x1000b00, N_INV, 0x0800d40), \
+ X(vshl, 0x0000400, N_INV, 0x0800510), \
+ X(vqshl, 0x0000410, N_INV, 0x0800710), \
+ X(vand, 0x0000110, N_INV, 0x0800030), \
+ X(vbic, 0x0100110, N_INV, 0x0800030), \
+ X(veor, 0x1000110, N_INV, N_INV), \
+ X(vorn, 0x0300110, N_INV, 0x0800010), \
+ X(vorr, 0x0200110, N_INV, 0x0800010), \
+ X(vmvn, 0x1b00580, N_INV, 0x0800030), \
+ X(vshll, 0x1b20300, N_INV, 0x0800a10), /* max shift, immediate. */ \
+ X(vcvt, 0x1b30600, N_INV, 0x0800e10), /* integer, fixed-point. */ \
+ X(vdup, 0xe800b10, N_INV, 0x1b00c00), /* arm, scalar. */ \
+ X(vld1, 0x0200000, 0x0a00000, 0x0a00c00), /* interlv, lane, dup. */ \
+ X(vst1, 0x0000000, 0x0800000, N_INV), \
+ X(vld2, 0x0200100, 0x0a00100, 0x0a00d00), \
+ X(vst2, 0x0000100, 0x0800100, N_INV), \
+ X(vld3, 0x0200200, 0x0a00200, 0x0a00e00), \
+ X(vst3, 0x0000200, 0x0800200, N_INV), \
+ X(vld4, 0x0200300, 0x0a00300, 0x0a00f00), \
+ X(vst4, 0x0000300, 0x0800300, N_INV), \
+ X(vmovn, 0x1b20200, N_INV, N_INV), \
+ X(vtrn, 0x1b20080, N_INV, N_INV), \
+ X(vqmovn, 0x1b20200, N_INV, N_INV), \
+ X(vqmovun, 0x1b20240, N_INV, N_INV), \
+ X(vnmul, 0xe200a40, 0xe200b40, N_INV), \
+ X(vnmla, 0xe100a40, 0xe100b40, N_INV), \
+ X(vnmls, 0xe100a00, 0xe100b00, N_INV), \
+ X(vfnma, 0xe900a40, 0xe900b40, N_INV), \
+ X(vfnms, 0xe900a00, 0xe900b00, N_INV), \
+ X(vcmp, 0xeb40a40, 0xeb40b40, N_INV), \
+ X(vcmpz, 0xeb50a40, 0xeb50b40, N_INV), \
+ X(vcmpe, 0xeb40ac0, 0xeb40bc0, N_INV), \
+ X(vcmpez, 0xeb50ac0, 0xeb50bc0, N_INV), \
+ X(vseleq, 0xe000a00, N_INV, N_INV), \
+ X(vselvs, 0xe100a00, N_INV, N_INV), \
+ X(vselge, 0xe200a00, N_INV, N_INV), \
+ X(vselgt, 0xe300a00, N_INV, N_INV), \
+ X(vmaxnm, 0xe800a00, 0x3000f10, N_INV), \
+ X(vminnm, 0xe800a40, 0x3200f10, N_INV), \
+ X(vcvta, 0xebc0a40, 0x3bb0000, N_INV), \
+ X(vrintr, 0xeb60a40, 0x3ba0400, N_INV), \
+ X(vrinta, 0xeb80a40, 0x3ba0400, N_INV), \
+ X(aes, 0x3b00300, N_INV, N_INV), \
+ X(sha3op, 0x2000c00, N_INV, N_INV), \
+ X(sha1h, 0x3b902c0, N_INV, N_INV), \
+ X(sha2op, 0x3ba0380, N_INV, N_INV)
+
+enum neon_opc
+{
+#define X(OPC,I,F,S) N_MNEM_##OPC
+NEON_ENC_TAB
+#undef X
+};
+
+static const struct neon_tab_entry neon_enc_tab[] =
+{
+#define X(OPC,I,F,S) { (I), (F), (S) }
+NEON_ENC_TAB
+#undef X
+};
+
+/* Do not use these macros; instead, use NEON_ENCODE defined below. */
+#define NEON_ENC_INTEGER_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
+#define NEON_ENC_ARMREG_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
+#define NEON_ENC_POLY_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
+#define NEON_ENC_FLOAT_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
+#define NEON_ENC_SCALAR_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
+#define NEON_ENC_IMMED_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
+#define NEON_ENC_INTERLV_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
+#define NEON_ENC_LANE_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
+#define NEON_ENC_DUP_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
+#define NEON_ENC_SINGLE_(X) \
+ ((neon_enc_tab[(X) & 0x0fffffff].integer) | ((X) & 0xf0000000))
+#define NEON_ENC_DOUBLE_(X) \
+ ((neon_enc_tab[(X) & 0x0fffffff].float_or_poly) | ((X) & 0xf0000000))
+#define NEON_ENC_FPV8_(X) \
+ ((neon_enc_tab[(X) & 0x0fffffff].integer) | ((X) & 0xf000000))
+
+#define NEON_ENCODE(type, inst) \
+ do \
+ { \
+ inst.instruction = NEON_ENC_##type##_ (inst.instruction); \
+ inst.is_neon = 1; \
+ } \
+ while (0)
+
+#define check_neon_suffixes \
+ do \
+ { \
+ if (!inst.error && inst.vectype.elems > 0 && !inst.is_neon) \
+ { \
+ as_bad (_("invalid neon suffix for non neon instruction")); \
+ return; \
+ } \
+ } \
+ while (0)
+
+/* Define shapes for instruction operands. The following mnemonic characters
+ are used in this table:
+
+ F - VFP S<n> register
+ D - Neon D<n> register
+ Q - Neon Q<n> register
+ I - Immediate
+ S - Scalar
+ R - ARM register
+ L - D<n> register list
+
+ This table is used to generate various data:
+ - enumerations of the form NS_DDR to be used as arguments to
+ neon_select_shape.
+ - a table classifying shapes into single, double, quad, mixed.
+ - a table used to drive neon_select_shape. */
+
+#define NEON_SHAPE_DEF \
+ X(3, (D, D, D), DOUBLE), \
+ X(3, (Q, Q, Q), QUAD), \
+ X(3, (D, D, I), DOUBLE), \
+ X(3, (Q, Q, I), QUAD), \
+ X(3, (D, D, S), DOUBLE), \
+ X(3, (Q, Q, S), QUAD), \
+ X(2, (D, D), DOUBLE), \
+ X(2, (Q, Q), QUAD), \
+ X(2, (D, S), DOUBLE), \
+ X(2, (Q, S), QUAD), \
+ X(2, (D, R), DOUBLE), \
+ X(2, (Q, R), QUAD), \
+ X(2, (D, I), DOUBLE), \
+ X(2, (Q, I), QUAD), \
+ X(3, (D, L, D), DOUBLE), \
+ X(2, (D, Q), MIXED), \
+ X(2, (Q, D), MIXED), \
+ X(3, (D, Q, I), MIXED), \
+ X(3, (Q, D, I), MIXED), \
+ X(3, (Q, D, D), MIXED), \
+ X(3, (D, Q, Q), MIXED), \
+ X(3, (Q, Q, D), MIXED), \
+ X(3, (Q, D, S), MIXED), \
+ X(3, (D, Q, S), MIXED), \
+ X(4, (D, D, D, I), DOUBLE), \
+ X(4, (Q, Q, Q, I), QUAD), \
+ X(2, (F, F), SINGLE), \
+ X(3, (F, F, F), SINGLE), \
+ X(2, (F, I), SINGLE), \
+ X(2, (F, D), MIXED), \
+ X(2, (D, F), MIXED), \
+ X(3, (F, F, I), MIXED), \
+ X(4, (R, R, F, F), SINGLE), \
+ X(4, (F, F, R, R), SINGLE), \
+ X(3, (D, R, R), DOUBLE), \
+ X(3, (R, R, D), DOUBLE), \
+ X(2, (S, R), SINGLE), \
+ X(2, (R, S), SINGLE), \
+ X(2, (F, R), SINGLE), \
+ X(2, (R, F), SINGLE)
+
+#define S2(A,B) NS_##A##B
+#define S3(A,B,C) NS_##A##B##C
+#define S4(A,B,C,D) NS_##A##B##C##D
+
+#define X(N, L, C) S##N L
+
+enum neon_shape
+{
+ NEON_SHAPE_DEF,
+ NS_NULL
+};
+
+#undef X
+#undef S2
+#undef S3
+#undef S4
+
+enum neon_shape_class
+{
+ SC_SINGLE,
+ SC_DOUBLE,
+ SC_QUAD,
+ SC_MIXED
+};
+
+#define X(N, L, C) SC_##C
+
+static enum neon_shape_class neon_shape_class[] =
+{
+ NEON_SHAPE_DEF
+};
+
+#undef X
+
+enum neon_shape_el
+{
+ SE_F,
+ SE_D,
+ SE_Q,
+ SE_I,
+ SE_S,
+ SE_R,
+ SE_L
+};
+
+/* Register widths of above. */
+static unsigned neon_shape_el_size[] =
+{
+ 32,
+ 64,
+ 128,
+ 0,
+ 32,
+ 32,
+ 0
+};
+
+struct neon_shape_info
+{
+ unsigned els;
+ enum neon_shape_el el[NEON_MAX_TYPE_ELS];
+};
+
+#define S2(A,B) { SE_##A, SE_##B }
+#define S3(A,B,C) { SE_##A, SE_##B, SE_##C }
+#define S4(A,B,C,D) { SE_##A, SE_##B, SE_##C, SE_##D }
+
+#define X(N, L, C) { N, S##N L }
+
+static struct neon_shape_info neon_shape_tab[] =
+{
+ NEON_SHAPE_DEF
+};
+
+#undef X
+#undef S2
+#undef S3
+#undef S4
+
+/* Bit masks used in type checking given instructions.
+ 'N_EQK' means the type must be the same as (or based on in some way) the key
+ type, which itself is marked with the 'N_KEY' bit. If the 'N_EQK' bit is
+ set, various other bits can be set as well in order to modify the meaning of
+ the type constraint. */
+
+enum neon_type_mask
+{
+ N_S8 = 0x0000001,
+ N_S16 = 0x0000002,
+ N_S32 = 0x0000004,
+ N_S64 = 0x0000008,
+ N_U8 = 0x0000010,
+ N_U16 = 0x0000020,
+ N_U32 = 0x0000040,
+ N_U64 = 0x0000080,
+ N_I8 = 0x0000100,
+ N_I16 = 0x0000200,
+ N_I32 = 0x0000400,
+ N_I64 = 0x0000800,
+ N_8 = 0x0001000,
+ N_16 = 0x0002000,
+ N_32 = 0x0004000,
+ N_64 = 0x0008000,
+ N_P8 = 0x0010000,
+ N_P16 = 0x0020000,
+ N_F16 = 0x0040000,
+ N_F32 = 0x0080000,
+ N_F64 = 0x0100000,
+ N_P64 = 0x0200000,
+ N_KEY = 0x1000000, /* Key element (main type specifier). */
+ N_EQK = 0x2000000, /* Given operand has the same type & size as the key. */
+ N_VFP = 0x4000000, /* VFP mode: operand size must match register width. */
+ N_UNT = 0x8000000, /* Must be explicitly untyped. */
+ N_DBL = 0x0000001, /* If N_EQK, this operand is twice the size. */
+ N_HLF = 0x0000002, /* If N_EQK, this operand is half the size. */
+ N_SGN = 0x0000004, /* If N_EQK, this operand is forced to be signed. */
+ N_UNS = 0x0000008, /* If N_EQK, this operand is forced to be unsigned. */
+ N_INT = 0x0000010, /* If N_EQK, this operand is forced to be integer. */
+ N_FLT = 0x0000020, /* If N_EQK, this operand is forced to be float. */
+ N_SIZ = 0x0000040, /* If N_EQK, this operand is forced to be size-only. */
+ N_UTYP = 0,
+ N_MAX_NONSPECIAL = N_P64
+};
+
+#define N_ALLMODS (N_DBL | N_HLF | N_SGN | N_UNS | N_INT | N_FLT | N_SIZ)
+
+#define N_SU_ALL (N_S8 | N_S16 | N_S32 | N_S64 | N_U8 | N_U16 | N_U32 | N_U64)
+#define N_SU_32 (N_S8 | N_S16 | N_S32 | N_U8 | N_U16 | N_U32)
+#define N_SU_16_64 (N_S16 | N_S32 | N_S64 | N_U16 | N_U32 | N_U64)
+#define N_SUF_32 (N_SU_32 | N_F32)
+#define N_I_ALL (N_I8 | N_I16 | N_I32 | N_I64)
+#define N_IF_32 (N_I8 | N_I16 | N_I32 | N_F32)
+
+/* Pass this as the first type argument to neon_check_type to ignore types
+ altogether. */
+#define N_IGNORE_TYPE (N_KEY | N_EQK)
+
+/* Select a "shape" for the current instruction (describing register types or
+ sizes) from a list of alternatives. Return NS_NULL if the current instruction
+ doesn't fit. For non-polymorphic shapes, checking is usually done as a
+ function of operand parsing, so this function doesn't need to be called.
+ Shapes should be listed in order of decreasing length. */
+
+static enum neon_shape
+neon_select_shape (enum neon_shape shape, ...)
+{
+ va_list ap;
+ enum neon_shape first_shape = shape;
+
+ /* Fix missing optional operands. FIXME: we don't know at this point how
+ many arguments we should have, so this makes the assumption that we have
+ > 1. This is true of all current Neon opcodes, I think, but may not be
+ true in the future. */
+ if (!inst.operands[1].present)
+ inst.operands[1] = inst.operands[0];
+
+ va_start (ap, shape);
+
+ for (; shape != NS_NULL; shape = (enum neon_shape) va_arg (ap, int))
+ {
+ unsigned j;
+ int matches = 1;
+
+ for (j = 0; j < neon_shape_tab[shape].els; j++)
+ {
+ if (!inst.operands[j].present)
+ {
+ matches = 0;
+ break;
+ }
+
+ switch (neon_shape_tab[shape].el[j])
+ {
+ case SE_F:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].issingle
+ && !inst.operands[j].isquad))
+ matches = 0;
+ break;
+
+ case SE_D:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && !inst.operands[j].isquad
+ && !inst.operands[j].issingle))
+ matches = 0;
+ break;
+
+ case SE_R:
+ if (!(inst.operands[j].isreg
+ && !inst.operands[j].isvec))
+ matches = 0;
+ break;
+
+ case SE_Q:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].isquad
+ && !inst.operands[j].issingle))
+ matches = 0;
+ break;
+
+ case SE_I:
+ if (!(!inst.operands[j].isreg
+ && !inst.operands[j].isscalar))
+ matches = 0;
+ break;
+
+ case SE_S:
+ if (!(!inst.operands[j].isreg
+ && inst.operands[j].isscalar))
+ matches = 0;
+ break;
+
+ case SE_L:
+ break;
+ }
+ if (!matches)
+ break;
+ }
+ if (matches && (j >= ARM_IT_MAX_OPERANDS || !inst.operands[j].present))
+ /* We've matched all the entries in the shape table, and we don't
+ have any left over operands which have not been matched. */
+ break;
+ }
+
+ va_end (ap);
+
+ if (shape == NS_NULL && first_shape != NS_NULL)
+ first_error (_("invalid instruction shape"));
+
+ return shape;
+}
+
+/* True if SHAPE is predominantly a quadword operation (most of the time, this
+ means the Q bit should be set). */
+
+static int
+neon_quad (enum neon_shape shape)
+{
+ return neon_shape_class[shape] == SC_QUAD;
+}
+
+static void
+neon_modify_type_size (unsigned typebits, enum neon_el_type *g_type,
+ unsigned *g_size)
+{
+ /* Allow modification to be made to types which are constrained to be
+ based on the key element, based on bits set alongside N_EQK. */
+ if ((typebits & N_EQK) != 0)
+ {
+ if ((typebits & N_HLF) != 0)
+ *g_size /= 2;
+ else if ((typebits & N_DBL) != 0)
+ *g_size *= 2;
+ if ((typebits & N_SGN) != 0)
+ *g_type = NT_signed;
+ else if ((typebits & N_UNS) != 0)
+ *g_type = NT_unsigned;
+ else if ((typebits & N_INT) != 0)
+ *g_type = NT_integer;
+ else if ((typebits & N_FLT) != 0)
+ *g_type = NT_float;
+ else if ((typebits & N_SIZ) != 0)
+ *g_type = NT_untyped;
+ }
+}
+
+/* Return operand OPNO promoted by bits set in THISARG. KEY should be the "key"
+ operand type, i.e. the single type specified in a Neon instruction when it
+ is the only one given. */
+
+static struct neon_type_el
+neon_type_promote (struct neon_type_el *key, unsigned thisarg)
+{
+ struct neon_type_el dest = *key;
+
+ gas_assert ((thisarg & N_EQK) != 0);
+
+ neon_modify_type_size (thisarg, &dest.type, &dest.size);
+
+ return dest;
+}
+
+/* Convert Neon type and size into compact bitmask representation. */
+
+static enum neon_type_mask
+type_chk_of_el_type (enum neon_el_type type, unsigned size)
+{
+ switch (type)
+ {
+ case NT_untyped:
+ switch (size)
+ {
+ case 8: return N_8;
+ case 16: return N_16;
+ case 32: return N_32;
+ case 64: return N_64;
+ default: ;
+ }
+ break;
+
+ case NT_integer:
+ switch (size)
+ {
+ case 8: return N_I8;
+ case 16: return N_I16;
+ case 32: return N_I32;
+ case 64: return N_I64;
+ default: ;
+ }
+ break;
+
+ case NT_float:
+ switch (size)
+ {
+ case 16: return N_F16;
+ case 32: return N_F32;
+ case 64: return N_F64;
+ default: ;
+ }
+ break;
+
+ case NT_poly:
+ switch (size)
+ {
+ case 8: return N_P8;
+ case 16: return N_P16;
+ case 64: return N_P64;
+ default: ;
+ }
+ break;
+
+ case NT_signed:
+ switch (size)
+ {
+ case 8: return N_S8;
+ case 16: return N_S16;
+ case 32: return N_S32;
+ case 64: return N_S64;
+ default: ;
+ }
+ break;
+
+ case NT_unsigned:
+ switch (size)
+ {
+ case 8: return N_U8;
+ case 16: return N_U16;
+ case 32: return N_U32;
+ case 64: return N_U64;
+ default: ;
+ }
+ break;
+
+ default: ;
+ }
+
+ return N_UTYP;
+}
+
+/* Convert compact Neon bitmask type representation to a type and size. Only
+ handles the case where a single bit is set in the mask. */
+
+static int
+el_type_of_type_chk (enum neon_el_type *type, unsigned *size,
+ enum neon_type_mask mask)
+{
+ if ((mask & N_EQK) != 0)
+ return FAIL;
+
+ if ((mask & (N_S8 | N_U8 | N_I8 | N_8 | N_P8)) != 0)
+ *size = 8;
+ else if ((mask & (N_S16 | N_U16 | N_I16 | N_16 | N_F16 | N_P16)) != 0)
+ *size = 16;
+ else if ((mask & (N_S32 | N_U32 | N_I32 | N_32 | N_F32)) != 0)
+ *size = 32;
+ else if ((mask & (N_S64 | N_U64 | N_I64 | N_64 | N_F64 | N_P64)) != 0)
+ *size = 64;
+ else
+ return FAIL;
+
+ if ((mask & (N_S8 | N_S16 | N_S32 | N_S64)) != 0)
+ *type = NT_signed;
+ else if ((mask & (N_U8 | N_U16 | N_U32 | N_U64)) != 0)
+ *type = NT_unsigned;
+ else if ((mask & (N_I8 | N_I16 | N_I32 | N_I64)) != 0)
+ *type = NT_integer;
+ else if ((mask & (N_8 | N_16 | N_32 | N_64)) != 0)
+ *type = NT_untyped;
+ else if ((mask & (N_P8 | N_P16 | N_P64)) != 0)
+ *type = NT_poly;
+ else if ((mask & (N_F16 | N_F32 | N_F64)) != 0)
+ *type = NT_float;
+ else
+ return FAIL;
+
+ return SUCCESS;
+}
+
+/* Modify a bitmask of allowed types. This is only needed for type
+ relaxation. */
+
+static unsigned
+modify_types_allowed (unsigned allowed, unsigned mods)
+{
+ unsigned size;
+ enum neon_el_type type;
+ unsigned destmask;
+ int i;
+
+ destmask = 0;
+
+ for (i = 1; i <= N_MAX_NONSPECIAL; i <<= 1)
+ {
+ if (el_type_of_type_chk (&type, &size,
+ (enum neon_type_mask) (allowed & i)) == SUCCESS)
+ {
+ neon_modify_type_size (mods, &type, &size);
+ destmask |= type_chk_of_el_type (type, size);
+ }
+ }
+
+ return destmask;
+}
+
+/* Check type and return type classification.
+ The manual states (paraphrase): If one datatype is given, it indicates the
+ type given in:
+ - the second operand, if there is one
+ - the operand, if there is no second operand
+ - the result, if there are no operands.
+ This isn't quite good enough though, so we use a concept of a "key" datatype
+ which is set on a per-instruction basis, which is the one which matters when
+ only one data type is written.
+ Note: this function has side-effects (e.g. filling in missing operands). All
+ Neon instructions should call it before performing bit encoding. */
+
+static struct neon_type_el
+neon_check_type (unsigned els, enum neon_shape ns, ...)
+{
+ va_list ap;
+ unsigned i, pass, key_el = 0;
+ unsigned types[NEON_MAX_TYPE_ELS];
+ enum neon_el_type k_type = NT_invtype;
+ unsigned k_size = -1u;
+ struct neon_type_el badtype = {NT_invtype, -1};
+ unsigned key_allowed = 0;
+
+ /* Optional registers in Neon instructions are always (not) in operand 1.
+ Fill in the missing operand here, if it was omitted. */
+ if (els > 1 && !inst.operands[1].present)
+ inst.operands[1] = inst.operands[0];
+
+ /* Suck up all the varargs. */
+ va_start (ap, ns);
+ for (i = 0; i < els; i++)
+ {
+ unsigned thisarg = va_arg (ap, unsigned);
+ if (thisarg == N_IGNORE_TYPE)
+ {
+ va_end (ap);
+ return badtype;
+ }
+ types[i] = thisarg;
+ if ((thisarg & N_KEY) != 0)
+ key_el = i;
+ }
+ va_end (ap);
+
+ if (inst.vectype.elems > 0)
+ for (i = 0; i < els; i++)
+ if (inst.operands[i].vectype.type != NT_invtype)
+ {
+ first_error (_("types specified in both the mnemonic and operands"));
+ return badtype;
+ }
+
+ /* Duplicate inst.vectype elements here as necessary.
+ FIXME: No idea if this is exactly the same as the ARM assembler,
+ particularly when an insn takes one register and one non-register
+ operand. */
+ if (inst.vectype.elems == 1 && els > 1)
+ {
+ unsigned j;
+ inst.vectype.elems = els;
+ inst.vectype.el[key_el] = inst.vectype.el[0];
+ for (j = 0; j < els; j++)
+ if (j != key_el)
+ inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
+ types[j]);
+ }
+ else if (inst.vectype.elems == 0 && els > 0)
+ {
+ unsigned j;
+ /* No types were given after the mnemonic, so look for types specified
+ after each operand. We allow some flexibility here; as long as the
+ "key" operand has a type, we can infer the others. */
+ for (j = 0; j < els; j++)
+ if (inst.operands[j].vectype.type != NT_invtype)
+ inst.vectype.el[j] = inst.operands[j].vectype;
+
+ if (inst.operands[key_el].vectype.type != NT_invtype)
+ {
+ for (j = 0; j < els; j++)
+ if (inst.operands[j].vectype.type == NT_invtype)
+ inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
+ types[j]);
+ }
+ else
+ {
+ first_error (_("operand types can't be inferred"));
+ return badtype;
+ }
+ }
+ else if (inst.vectype.elems != els)
+ {
+ first_error (_("type specifier has the wrong number of parts"));
+ return badtype;
+ }
+
+ for (pass = 0; pass < 2; pass++)
+ {
+ for (i = 0; i < els; i++)
+ {
+ unsigned thisarg = types[i];
+ unsigned types_allowed = ((thisarg & N_EQK) != 0 && pass != 0)
+ ? modify_types_allowed (key_allowed, thisarg) : thisarg;
+ enum neon_el_type g_type = inst.vectype.el[i].type;
+ unsigned g_size = inst.vectype.el[i].size;
+
+ /* Decay more-specific signed & unsigned types to sign-insensitive
+ integer types if sign-specific variants are unavailable. */
+ if ((g_type == NT_signed || g_type == NT_unsigned)
+ && (types_allowed & N_SU_ALL) == 0)
+ g_type = NT_integer;
+
+ /* If only untyped args are allowed, decay any more specific types to
+ them. Some instructions only care about signs for some element
+ sizes, so handle that properly. */
+ if (((types_allowed & N_UNT) == 0)
+ && ((g_size == 8 && (types_allowed & N_8) != 0)
+ || (g_size == 16 && (types_allowed & N_16) != 0)
+ || (g_size == 32 && (types_allowed & N_32) != 0)
+ || (g_size == 64 && (types_allowed & N_64) != 0)))
+ g_type = NT_untyped;
+
+ if (pass == 0)
+ {
+ if ((thisarg & N_KEY) != 0)
+ {
+ k_type = g_type;
+ k_size = g_size;
+ key_allowed = thisarg & ~N_KEY;
+ }
+ }
+ else
+ {
+ if ((thisarg & N_VFP) != 0)
+ {
+ enum neon_shape_el regshape;
+ unsigned regwidth, match;
+
+ /* PR 11136: Catch the case where we are passed a shape of NS_NULL. */
+ if (ns == NS_NULL)
+ {
+ first_error (_("invalid instruction shape"));
+ return badtype;
+ }
+ regshape = neon_shape_tab[ns].el[i];
+ regwidth = neon_shape_el_size[regshape];
+
+ /* In VFP mode, operands must match register widths. If we
+ have a key operand, use its width, else use the width of
+ the current operand. */
+ if (k_size != -1u)
+ match = k_size;
+ else
+ match = g_size;
+
+ if (regwidth != match)
+ {
+ first_error (_("operand size must match register width"));
+ return badtype;
+ }
+ }
+
+ if ((thisarg & N_EQK) == 0)
+ {
+ unsigned given_type = type_chk_of_el_type (g_type, g_size);
+
+ if ((given_type & types_allowed) == 0)
+ {
+ first_error (_("bad type in Neon instruction"));
+ return badtype;
+ }
+ }
+ else
+ {
+ enum neon_el_type mod_k_type = k_type;
+ unsigned mod_k_size = k_size;
+ neon_modify_type_size (thisarg, &mod_k_type, &mod_k_size);
+ if (g_type != mod_k_type || g_size != mod_k_size)
+ {
+ first_error (_("inconsistent types in Neon instruction"));
+ return badtype;
+ }
+ }
+ }
+ }
+ }
+
+ return inst.vectype.el[key_el];
+}
+
+/* Neon-style VFP instruction forwarding. */
+
+/* Thumb VFP instructions have 0xE in the condition field. */
+
+static void
+do_vfp_cond_or_thumb (void)
+{
+ inst.is_neon = 1;
+
+ if (thumb_mode)
+ inst.instruction |= 0xe0000000;
+ else
+ inst.instruction |= inst.cond << 28;
+}
+
+/* Look up and encode a simple mnemonic, for use as a helper function for the
+ Neon-style VFP syntax. This avoids duplication of bits of the insns table,
+ etc. It is assumed that operand parsing has already been done, and that the
+ operands are in the form expected by the given opcode (this isn't necessarily
+ the same as the form in which they were parsed, hence some massaging must
+ take place before this function is called).
+ Checks current arch version against that in the looked-up opcode. */
+
+static void
+do_vfp_nsyn_opcode (const char *opname)
+{
+ const struct asm_opcode *opcode;
+
+ opcode = (const struct asm_opcode *) hash_find (arm_ops_hsh, opname);
+
+ if (!opcode)
+ abort ();
+
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant,
+ thumb_mode ? *opcode->tvariant : *opcode->avariant),
+ _(BAD_FPU));
+
+ inst.is_neon = 1;
+
+ if (thumb_mode)
+ {
+ inst.instruction = opcode->tvalue;
+ opcode->tencode ();
+ }
+ else
+ {
+ inst.instruction = (inst.cond << 28) | opcode->avalue;
+ opcode->aencode ();
+ }
+}
+
+static void
+do_vfp_nsyn_add_sub (enum neon_shape rs)
+{
+ int is_add = (inst.instruction & 0x0fffffff) == N_MNEM_vadd;
+
+ if (rs == NS_FFF)
+ {
+ if (is_add)
+ do_vfp_nsyn_opcode ("fadds");
+ else
+ do_vfp_nsyn_opcode ("fsubs");
+ }
+ else
+ {
+ if (is_add)
+ do_vfp_nsyn_opcode ("faddd");
+ else
+ do_vfp_nsyn_opcode ("fsubd");
+ }
+}
+
+/* Check operand types to see if this is a VFP instruction, and if so call
+ PFN (). */
+
+static int
+try_vfp_nsyn (int args, void (*pfn) (enum neon_shape))
+{
+ enum neon_shape rs;
+ struct neon_type_el et;
+
+ switch (args)
+ {
+ case 2:
+ rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
+ et = neon_check_type (2, rs,
+ N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ break;
+
+ case 3:
+ rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
+ et = neon_check_type (3, rs,
+ N_EQK | N_VFP, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (et.type != NT_invtype)
+ {
+ pfn (rs);
+ return SUCCESS;
+ }
+
+ inst.error = NULL;
+ return FAIL;
+}
+
+static void
+do_vfp_nsyn_mla_mls (enum neon_shape rs)
+{
+ int is_mla = (inst.instruction & 0x0fffffff) == N_MNEM_vmla;
+
+ if (rs == NS_FFF)
+ {
+ if (is_mla)
+ do_vfp_nsyn_opcode ("fmacs");
+ else
+ do_vfp_nsyn_opcode ("fnmacs");
+ }
+ else
+ {
+ if (is_mla)
+ do_vfp_nsyn_opcode ("fmacd");
+ else
+ do_vfp_nsyn_opcode ("fnmacd");
+ }
+}
+
+static void
+do_vfp_nsyn_fma_fms (enum neon_shape rs)
+{
+ int is_fma = (inst.instruction & 0x0fffffff) == N_MNEM_vfma;
+
+ if (rs == NS_FFF)
+ {
+ if (is_fma)
+ do_vfp_nsyn_opcode ("ffmas");
+ else
+ do_vfp_nsyn_opcode ("ffnmas");
+ }
+ else
+ {
+ if (is_fma)
+ do_vfp_nsyn_opcode ("ffmad");
+ else
+ do_vfp_nsyn_opcode ("ffnmad");
+ }
+}
+
+static void
+do_vfp_nsyn_mul (enum neon_shape rs)
+{
+ if (rs == NS_FFF)
+ do_vfp_nsyn_opcode ("fmuls");
+ else
+ do_vfp_nsyn_opcode ("fmuld");
+}
+
+static void
+do_vfp_nsyn_abs_neg (enum neon_shape rs)
+{
+ int is_neg = (inst.instruction & 0x80) != 0;
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_VFP | N_KEY);
+
+ if (rs == NS_FF)
+ {
+ if (is_neg)
+ do_vfp_nsyn_opcode ("fnegs");
+ else
+ do_vfp_nsyn_opcode ("fabss");
+ }
+ else
+ {
+ if (is_neg)
+ do_vfp_nsyn_opcode ("fnegd");
+ else
+ do_vfp_nsyn_opcode ("fabsd");
+ }
+}
+
+/* Encode single-precision (only!) VFP fldm/fstm instructions. Double precision
+ insns belong to Neon, and are handled elsewhere. */
+
+static void
+do_vfp_nsyn_ldm_stm (int is_dbmode)
+{
+ int is_ldm = (inst.instruction & (1 << 20)) != 0;
+ if (is_ldm)
+ {
+ if (is_dbmode)
+ do_vfp_nsyn_opcode ("fldmdbs");
+ else
+ do_vfp_nsyn_opcode ("fldmias");
+ }
+ else
+ {
+ if (is_dbmode)
+ do_vfp_nsyn_opcode ("fstmdbs");
+ else
+ do_vfp_nsyn_opcode ("fstmias");
+ }
+}
+
+static void
+do_vfp_nsyn_sqrt (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+
+ if (rs == NS_FF)
+ do_vfp_nsyn_opcode ("fsqrts");
+ else
+ do_vfp_nsyn_opcode ("fsqrtd");
+}
+
+static void
+do_vfp_nsyn_div (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
+ neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
+ N_F32 | N_F64 | N_KEY | N_VFP);
+
+ if (rs == NS_FFF)
+ do_vfp_nsyn_opcode ("fdivs");
+ else
+ do_vfp_nsyn_opcode ("fdivd");
+}
+
+static void
+do_vfp_nsyn_nmul (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
+ neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
+ N_F32 | N_F64 | N_KEY | N_VFP);
+
+ if (rs == NS_FFF)
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_dyadic ();
+ }
+ else
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd_rn_rm ();
+ }
+ do_vfp_cond_or_thumb ();
+}
+
+static void
+do_vfp_nsyn_cmp (void)
+{
+ if (inst.operands[1].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+
+ if (rs == NS_FF)
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_monadic ();
+ }
+ else
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd_rm ();
+ }
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_FI, NS_DI, NS_NULL);
+ neon_check_type (2, rs, N_F32 | N_F64 | N_KEY | N_VFP, N_EQK);
+
+ switch (inst.instruction & 0x0fffffff)
+ {
+ case N_MNEM_vcmp:
+ inst.instruction += N_MNEM_vcmpz - N_MNEM_vcmp;
+ break;
+ case N_MNEM_vcmpe:
+ inst.instruction += N_MNEM_vcmpez - N_MNEM_vcmpe;
+ break;
+ default:
+ abort ();
+ }
+
+ if (rs == NS_FI)
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_compare_z ();
+ }
+ else
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd ();
+ }
+ }
+ do_vfp_cond_or_thumb ();
+}
+
+static void
+nsyn_insert_sp (void)
+{
+ inst.operands[1] = inst.operands[0];
+ memset (&inst.operands[0], '\0', sizeof (inst.operands[0]));
+ inst.operands[0].reg = REG_SP;
+ inst.operands[0].isreg = 1;
+ inst.operands[0].writeback = 1;
+ inst.operands[0].present = 1;
+}
+
+static void
+do_vfp_nsyn_push (void)
+{
+ nsyn_insert_sp ();
+ if (inst.operands[1].issingle)
+ do_vfp_nsyn_opcode ("fstmdbs");
+ else
+ do_vfp_nsyn_opcode ("fstmdbd");
+}
+
+static void
+do_vfp_nsyn_pop (void)
+{
+ nsyn_insert_sp ();
+ if (inst.operands[1].issingle)
+ do_vfp_nsyn_opcode ("fldmias");
+ else
+ do_vfp_nsyn_opcode ("fldmiad");
+}
+
+/* Fix up Neon data-processing instructions, ORing in the correct bits for
+ ARM mode or Thumb mode and moving the encoded bit 24 to bit 28. */
+
+static void
+neon_dp_fixup (struct arm_it* insn)
+{
+ unsigned int i = insn->instruction;
+ insn->is_neon = 1;
+
+ if (thumb_mode)
+ {
+ /* The U bit is at bit 24 by default. Move to bit 28 in Thumb mode. */
+ if (i & (1 << 24))
+ i |= 1 << 28;
+
+ i &= ~(1 << 24);
+
+ i |= 0xef000000;
+ }
+ else
+ i |= 0xf2000000;
+
+ insn->instruction = i;
+}
+
+/* Turn a size (8, 16, 32, 64) into the respective bit number minus 3
+ (0, 1, 2, 3). */
+
+static unsigned
+neon_logbits (unsigned x)
+{
+ return ffs (x) - 4;
+}
+
+#define LOW4(R) ((R) & 0xf)
+#define HI1(R) (((R) >> 4) & 1)
+
+/* Encode insns with bit pattern:
+
+ |28/24|23|22 |21 20|19 16|15 12|11 8|7|6|5|4|3 0|
+ | U |x |D |size | Rn | Rd |x x x x|N|Q|M|x| Rm |
+
+ SIZE is passed in bits. -1 means size field isn't changed, in case it has a
+ different meaning for some instruction. */
+
+static void
+neon_three_same (int isquad, int ubit, int size)
+{
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= (isquad != 0) << 6;
+ inst.instruction |= (ubit != 0) << 24;
+ if (size != -1)
+ inst.instruction |= neon_logbits (size) << 20;
+
+ neon_dp_fixup (&inst);
+}
+
+/* Encode instructions of the form:
+
+ |28/24|23|22|21 20|19 18|17 16|15 12|11 7|6|5|4|3 0|
+ | U |x |D |x x |size |x x | Rd |x x x x x|Q|M|x| Rm |
+
+ Don't write size if SIZE == -1. */
+
+static void
+neon_two_same (int qbit, int ubit, int size)
+{
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= (qbit != 0) << 6;
+ inst.instruction |= (ubit != 0) << 24;
+
+ if (size != -1)
+ inst.instruction |= neon_logbits (size) << 18;
+
+ neon_dp_fixup (&inst);
+}
+
+/* Neon instruction encoders, in approximate order of appearance. */
+
+static void
+do_neon_dyadic_i_su (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_SU_32 | N_KEY);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+}
+
+static void
+do_neon_dyadic_i64_su (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_SU_ALL | N_KEY);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+}
+
+static void
+neon_imm_shift (int write_ubit, int uval, int isquad, struct neon_type_el et,
+ unsigned immbits)
+{
+ unsigned size = et.size >> 3;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= (isquad != 0) << 6;
+ inst.instruction |= immbits << 16;
+ inst.instruction |= (size >> 3) << 7;
+ inst.instruction |= (size & 0x7) << 19;
+ if (write_ubit)
+ inst.instruction |= (uval != 0) << 24;
+
+ neon_dp_fixup (&inst);
+}
+
+static void
+do_neon_shl_imm (void)
+{
+ if (!inst.operands[2].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_KEY | N_I_ALL);
+ NEON_ENCODE (IMMED, inst);
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, inst.operands[2].imm);
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
+ unsigned int tmp;
+
+ /* VSHL/VQSHL 3-register variants have syntax such as:
+ vshl.xx Dd, Dm, Dn
+ whereas other 3-register operations encoded by neon_three_same have
+ syntax like:
+ vadd.xx Dd, Dn, Dm
+ (i.e. with Dn & Dm reversed). Swap operands[1].reg and operands[2].reg
+ here. */
+ tmp = inst.operands[2].reg;
+ inst.operands[2].reg = inst.operands[1].reg;
+ inst.operands[1].reg = tmp;
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+ }
+}
+
+static void
+do_neon_qshl_imm (void)
+{
+ if (!inst.operands[2].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_ALL | N_KEY);
+
+ NEON_ENCODE (IMMED, inst);
+ neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
+ inst.operands[2].imm);
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
+ unsigned int tmp;
+
+ /* See note in do_neon_shl_imm. */
+ tmp = inst.operands[2].reg;
+ inst.operands[2].reg = inst.operands[1].reg;
+ inst.operands[1].reg = tmp;
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+ }
+}
+
+static void
+do_neon_rshl (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_SU_ALL | N_KEY);
+ unsigned int tmp;
+
+ tmp = inst.operands[2].reg;
+ inst.operands[2].reg = inst.operands[1].reg;
+ inst.operands[1].reg = tmp;
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+}
+
+static int
+neon_cmode_for_logic_imm (unsigned immediate, unsigned *immbits, int size)
+{
+ /* Handle .I8 pseudo-instructions. */
+ if (size == 8)
+ {
+ /* Unfortunately, this will make everything apart from zero out-of-range.
+ FIXME is this the intended semantics? There doesn't seem much point in
+ accepting .I8 if so. */
+ immediate |= immediate << 8;
+ size = 16;
+ }
+
+ if (size >= 32)
+ {
+ if (immediate == (immediate & 0x000000ff))
+ {
+ *immbits = immediate;
+ return 0x1;
+ }
+ else if (immediate == (immediate & 0x0000ff00))
+ {
+ *immbits = immediate >> 8;
+ return 0x3;
+ }
+ else if (immediate == (immediate & 0x00ff0000))
+ {
+ *immbits = immediate >> 16;
+ return 0x5;
+ }
+ else if (immediate == (immediate & 0xff000000))
+ {
+ *immbits = immediate >> 24;
+ return 0x7;
+ }
+ if ((immediate & 0xffff) != (immediate >> 16))
+ goto bad_immediate;
+ immediate &= 0xffff;
+ }
+
+ if (immediate == (immediate & 0x000000ff))
+ {
+ *immbits = immediate;
+ return 0x9;
+ }
+ else if (immediate == (immediate & 0x0000ff00))
+ {
+ *immbits = immediate >> 8;
+ return 0xb;
+ }
+
+ bad_immediate:
+ first_error (_("immediate value out of range"));
+ return FAIL;
+}
+
+static void
+do_neon_logic (void)
+{
+ if (inst.operands[2].present && inst.operands[2].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ neon_check_type (3, rs, N_IGNORE_TYPE);
+ /* U bit and size field were set as part of the bitmask. */
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), 0, -1);
+ }
+ else
+ {
+ const int three_ops_form = (inst.operands[2].present
+ && !inst.operands[2].isreg);
+ const int immoperand = (three_ops_form ? 2 : 1);
+ enum neon_shape rs = (three_ops_form
+ ? neon_select_shape (NS_DDI, NS_QQI, NS_NULL)
+ : neon_select_shape (NS_DI, NS_QI, NS_NULL));
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK);
+ enum neon_opc opcode = (enum neon_opc) inst.instruction & 0x0fffffff;
+ unsigned immbits;
+ int cmode;
+
+ if (et.type == NT_invtype)
+ return;
+
+ if (three_ops_form)
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("first and second operands shall be the same register"));
+
+ NEON_ENCODE (IMMED, inst);
+
+ immbits = inst.operands[immoperand].imm;
+ if (et.size == 64)
+ {
+ /* .i64 is a pseudo-op, so the immediate must be a repeating
+ pattern. */
+ if (immbits != (inst.operands[immoperand].regisimm ?
+ inst.operands[immoperand].reg : 0))
+ {
+ /* Set immbits to an invalid constant. */
+ immbits = 0xdeadbeef;
+ }
+ }
+
+ switch (opcode)
+ {
+ case N_MNEM_vbic:
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ case N_MNEM_vorr:
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ case N_MNEM_vand:
+ /* Pseudo-instruction for VBIC. */
+ neon_invert_size (&immbits, 0, et.size);
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ case N_MNEM_vorn:
+ /* Pseudo-instruction for VORR. */
+ neon_invert_size (&immbits, 0, et.size);
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (cmode == FAIL)
+ return;
+
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= cmode << 8;
+ neon_write_immbits (immbits);
+
+ neon_dp_fixup (&inst);
+ }
+}
+
+static void
+do_neon_bitfield (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ neon_check_type (3, rs, N_IGNORE_TYPE);
+ neon_three_same (neon_quad (rs), 0, -1);
+}
+
+static void
+neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types,
+ unsigned destbits)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs, N_EQK | destbits, N_EQK,
+ types | N_KEY);
+ if (et.type == NT_float)
+ {
+ NEON_ENCODE (FLOAT, inst);
+ neon_three_same (neon_quad (rs), 0, -1);
+ }
+ else
+ {
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), et.type == ubit_meaning, et.size);
+ }
+}
+
+static void
+do_neon_dyadic_if_su (void)
+{
+ neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
+}
+
+static void
+do_neon_dyadic_if_su_d (void)
+{
+ /* This version only allow D registers, but that constraint is enforced during
+ operand parsing so we don't need to do anything extra here. */
+ neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
+}
+
+static void
+do_neon_dyadic_if_i_d (void)
+{
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
+}
+
+enum vfp_or_neon_is_neon_bits
+{
+ NEON_CHECK_CC = 1,
+ NEON_CHECK_ARCH = 2,
+ NEON_CHECK_ARCH8 = 4
+};
+
+/* Call this function if an instruction which may have belonged to the VFP or
+ Neon instruction sets, but turned out to be a Neon instruction (due to the
+ operand types involved, etc.). We have to check and/or fix-up a couple of
+ things:
+
+ - Make sure the user hasn't attempted to make a Neon instruction
+ conditional.
+ - Alter the value in the condition code field if necessary.
+ - Make sure that the arch supports Neon instructions.
+
+ Which of these operations take place depends on bits from enum
+ vfp_or_neon_is_neon_bits.
+
+ WARNING: This function has side effects! If NEON_CHECK_CC is used and the
+ current instruction's condition is COND_ALWAYS, the condition field is
+ changed to inst.uncond_value. This is necessary because instructions shared
+ between VFP and Neon may be conditional for the VFP variants only, and the
+ unconditional Neon version must have, e.g., 0xF in the condition field. */
+
+static int
+vfp_or_neon_is_neon (unsigned check)
+{
+ /* Conditions are always legal in Thumb mode (IT blocks). */
+ if (!thumb_mode && (check & NEON_CHECK_CC))
+ {
+ if (inst.cond != COND_ALWAYS)
+ {
+ first_error (_(BAD_COND));
+ return FAIL;
+ }
+ if (inst.uncond_value != -1)
+ inst.instruction |= inst.uncond_value << 28;
+ }
+
+ if ((check & NEON_CHECK_ARCH)
+ && !mark_feature_used (&fpu_neon_ext_v1))
+ {
+ first_error (_(BAD_FPU));
+ return FAIL;
+ }
+
+ if ((check & NEON_CHECK_ARCH8)
+ && !mark_feature_used (&fpu_neon_ext_armv8))
+ {
+ first_error (_(BAD_FPU));
+ return FAIL;
+ }
+
+ return SUCCESS;
+}
+
+static void
+do_neon_addsub_if_i (void)
+{
+ if (try_vfp_nsyn (3, do_vfp_nsyn_add_sub) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32 | N_I64, 0);
+}
+
+/* Swaps operands 1 and 2. If operand 1 (optional arg) was omitted, we want the
+ result to be:
+ V<op> A,B (A is operand 0, B is operand 2)
+ to mean:
+ V<op> A,B,A
+ not:
+ V<op> A,B,B
+ so handle that case specially. */
+
+static void
+neon_exchange_operands (void)
+{
+ void *scratch = alloca (sizeof (inst.operands[0]));
+ if (inst.operands[1].present)
+ {
+ /* Swap operands[1] and operands[2]. */
+ memcpy (scratch, &inst.operands[1], sizeof (inst.operands[0]));
+ inst.operands[1] = inst.operands[2];
+ memcpy (&inst.operands[2], scratch, sizeof (inst.operands[0]));
+ }
+ else
+ {
+ inst.operands[1] = inst.operands[2];
+ inst.operands[2] = inst.operands[0];
+ }
+}
+
+static void
+neon_compare (unsigned regtypes, unsigned immtypes, int invert)
+{
+ if (inst.operands[2].isreg)
+ {
+ if (invert)
+ neon_exchange_operands ();
+ neon_dyadic_misc (NT_unsigned, regtypes, N_SIZ);
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK | N_SIZ, immtypes | N_KEY);
+
+ NEON_ENCODE (IMMED, inst);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= (et.type == NT_float) << 10;
+ inst.instruction |= neon_logbits (et.size) << 18;
+
+ neon_dp_fixup (&inst);
+ }
+}
+
+static void
+do_neon_cmp (void)
+{
+ neon_compare (N_SUF_32, N_S8 | N_S16 | N_S32 | N_F32, FALSE);
+}
+
+static void
+do_neon_cmp_inv (void)
+{
+ neon_compare (N_SUF_32, N_S8 | N_S16 | N_S32 | N_F32, TRUE);
+}
+
+static void
+do_neon_ceq (void)
+{
+ neon_compare (N_IF_32, N_IF_32, FALSE);
+}
+
+/* For multiply instructions, we have the possibility of 16-bit or 32-bit
+ scalars, which are encoded in 5 bits, M : Rm.
+ For 16-bit scalars, the register is encoded in Rm[2:0] and the index in
+ M:Rm[3], and for 32-bit scalars, the register is encoded in Rm[3:0] and the
+ index in M. */
+
+static unsigned
+neon_scalar_for_mul (unsigned scalar, unsigned elsize)
+{
+ unsigned regno = NEON_SCALAR_REG (scalar);
+ unsigned elno = NEON_SCALAR_INDEX (scalar);
+
+ switch (elsize)
+ {
+ case 16:
+ if (regno > 7 || elno > 3)
+ goto bad_scalar;
+ return regno | (elno << 3);
+
+ case 32:
+ if (regno > 15 || elno > 1)
+ goto bad_scalar;
+ return regno | (elno << 4);
+
+ default:
+ bad_scalar:
+ first_error (_("scalar out of range for multiply instruction"));
+ }
+
+ return 0;
+}
+
+/* Encode multiply / multiply-accumulate scalar instructions. */
+
+static void
+neon_mul_mac (struct neon_type_el et, int ubit)
+{
+ unsigned scalar;
+
+ /* Give a more helpful error message if we have an invalid type. */
+ if (et.type == NT_invtype)
+ return;
+
+ scalar = neon_scalar_for_mul (inst.operands[2].reg, et.size);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (scalar);
+ inst.instruction |= HI1 (scalar) << 5;
+ inst.instruction |= (et.type == NT_float) << 8;
+ inst.instruction |= neon_logbits (et.size) << 20;
+ inst.instruction |= (ubit != 0) << 24;
+
+ neon_dp_fixup (&inst);
+}
+
+static void
+do_neon_mac_maybe_scalar (void)
+{
+ if (try_vfp_nsyn (3, do_vfp_nsyn_mla_mls) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ if (inst.operands[2].isscalar)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_I16 | N_I32 | N_F32 | N_KEY);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, neon_quad (rs));
+ }
+ else
+ {
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
+ }
+}
+
+static void
+do_neon_fmac (void)
+{
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fma_fms) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
+}
+
+static void
+do_neon_tst (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ neon_three_same (neon_quad (rs), 0, et.size);
+}
+
+/* VMUL with 3 registers allows the P8 type. The scalar version supports the
+ same types as the MAC equivalents. The polynomial type for this instruction
+ is encoded the same as the integer type. */
+
+static void
+do_neon_mul (void)
+{
+ if (try_vfp_nsyn (3, do_vfp_nsyn_mul) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ if (inst.operands[2].isscalar)
+ do_neon_mac_maybe_scalar ();
+ else
+ neon_dyadic_misc (NT_poly, N_I8 | N_I16 | N_I32 | N_F32 | N_P8, 0);
+}
+
+static void
+do_neon_qdmulh (void)
+{
+ if (inst.operands[2].isscalar)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, neon_quad (rs));
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ /* The U bit (rounding) comes from bit mask. */
+ neon_three_same (neon_quad (rs), 0, et.size);
+ }
+}
+
+static void
+do_neon_fcmp_absolute (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
+ /* Size field comes from bit mask. */
+ neon_three_same (neon_quad (rs), 1, -1);
+}
+
+static void
+do_neon_fcmp_absolute_inv (void)
+{
+ neon_exchange_operands ();
+ do_neon_fcmp_absolute ();
+}
+
+static void
+do_neon_step (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
+ neon_three_same (neon_quad (rs), 0, -1);
+}
+
+static void
+do_neon_abs_neg (void)
+{
+ enum neon_shape rs;
+ struct neon_type_el et;
+
+ if (try_vfp_nsyn (2, do_vfp_nsyn_abs_neg) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ et = neon_check_type (2, rs, N_EQK, N_S8 | N_S16 | N_S32 | N_F32 | N_KEY);
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= (et.type == NT_float) << 10;
+ inst.instruction |= neon_logbits (et.size) << 18;
+
+ neon_dp_fixup (&inst);
+}
+
+static void
+do_neon_sli (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ constraint (imm < 0 || (unsigned)imm >= et.size,
+ _("immediate out of range for insert"));
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
+}
+
+static void
+do_neon_sri (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range for insert"));
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, et.size - imm);
+}
+
+static void
+do_neon_qshlu_imm (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK | N_UNS, N_S8 | N_S16 | N_S32 | N_S64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ constraint (imm < 0 || (unsigned)imm >= et.size,
+ _("immediate out of range for shift"));
+ /* Only encodes the 'U present' variant of the instruction.
+ In this case, signed types have OP (bit 8) set to 0.
+ Unsigned types have OP set to 1. */
+ inst.instruction |= (et.type == NT_unsigned) << 8;
+ /* The rest of the bits are the same as other immediate shifts. */
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
+}
+
+static void
+do_neon_qmovn (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_DQ,
+ N_EQK | N_HLF, N_SU_16_64 | N_KEY);
+ /* Saturating move where operands can be signed or unsigned, and the
+ destination has the same signedness. */
+ NEON_ENCODE (INTEGER, inst);
+ if (et.type == NT_unsigned)
+ inst.instruction |= 0xc0;
+ else
+ inst.instruction |= 0x80;
+ neon_two_same (0, 1, et.size / 2);
+}
+
+static void
+do_neon_qmovun (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_DQ,
+ N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY);
+ /* Saturating move with unsigned results. Operands must be signed. */
+ NEON_ENCODE (INTEGER, inst);
+ neon_two_same (0, 1, et.size / 2);
+}
+
+static void
+do_neon_rshift_sat_narrow (void)
+{
+ /* FIXME: Types for narrowing. If operands are signed, results can be signed
+ or unsigned. If operands are unsigned, results must also be unsigned. */
+ struct neon_type_el et = neon_check_type (2, NS_DQI,
+ N_EQK | N_HLF, N_SU_16_64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ /* This gets the bounds check, size encoding and immediate bits calculation
+ right. */
+ et.size /= 2;
+
+ /* VQ{R}SHRN.I<size> <Dd>, <Qm>, #0 is a synonym for
+ VQMOVN.I<size> <Dd>, <Qm>. */
+ if (imm == 0)
+ {
+ inst.operands[2].present = 0;
+ inst.instruction = N_MNEM_vqmovn;
+ do_neon_qmovn ();
+ return;
+ }
+
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range"));
+ neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, et.size - imm);
+}
+
+static void
+do_neon_rshift_sat_narrow_u (void)
+{
+ /* FIXME: Types for narrowing. If operands are signed, results can be signed
+ or unsigned. If operands are unsigned, results must also be unsigned. */
+ struct neon_type_el et = neon_check_type (2, NS_DQI,
+ N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ /* This gets the bounds check, size encoding and immediate bits calculation
+ right. */
+ et.size /= 2;
+
+ /* VQSHRUN.I<size> <Dd>, <Qm>, #0 is a synonym for
+ VQMOVUN.I<size> <Dd>, <Qm>. */
+ if (imm == 0)
+ {
+ inst.operands[2].present = 0;
+ inst.instruction = N_MNEM_vqmovun;
+ do_neon_qmovun ();
+ return;
+ }
+
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range"));
+ /* FIXME: The manual is kind of unclear about what value U should have in
+ VQ{R}SHRUN instructions, but U=0, op=0 definitely encodes VRSHR, so it
+ must be 1. */
+ neon_imm_shift (TRUE, 1, 0, et, et.size - imm);
+}
+
+static void
+do_neon_movn (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_DQ,
+ N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ neon_two_same (0, 1, et.size / 2);
+}
+
+static void
+do_neon_rshift_narrow (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_DQI,
+ N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ /* This gets the bounds check, size encoding and immediate bits calculation
+ right. */
+ et.size /= 2;
+
+ /* If immediate is zero then we are a pseudo-instruction for
+ VMOVN.I<size> <Dd>, <Qm> */
+ if (imm == 0)
+ {
+ inst.operands[2].present = 0;
+ inst.instruction = N_MNEM_vmovn;
+ do_neon_movn ();
+ return;
+ }
+
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range for narrowing operation"));
+ neon_imm_shift (FALSE, 0, 0, et, et.size - imm);
+}
+
+static void
+do_neon_shll (void)
+{
+ /* FIXME: Type checking when lengthening. */
+ struct neon_type_el et = neon_check_type (2, NS_QDI,
+ N_EQK | N_DBL, N_I8 | N_I16 | N_I32 | N_KEY);
+ unsigned imm = inst.operands[2].imm;
+
+ if (imm == et.size)
+ {
+ /* Maximum shift variant. */
+ NEON_ENCODE (INTEGER, inst);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_logbits (et.size) << 18;
+
+ neon_dp_fixup (&inst);
+ }
+ else
+ {
+ /* A more-specific type check for non-max versions. */
+ et = neon_check_type (2, NS_QDI,
+ N_EQK | N_DBL, N_SU_32 | N_KEY);
+ NEON_ENCODE (IMMED, inst);
+ neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, imm);
+ }
+}
+
+/* Check the various types for the VCVT instruction, and return which version
+ the current instruction is. */
+
+#define CVT_FLAVOUR_VAR \
+ CVT_VAR (s32_f32, N_S32, N_F32, whole_reg, "ftosls", "ftosis", "ftosizs") \
+ CVT_VAR (u32_f32, N_U32, N_F32, whole_reg, "ftouls", "ftouis", "ftouizs") \
+ CVT_VAR (f32_s32, N_F32, N_S32, whole_reg, "fsltos", "fsitos", NULL) \
+ CVT_VAR (f32_u32, N_F32, N_U32, whole_reg, "fultos", "fuitos", NULL) \
+ /* Half-precision conversions. */ \
+ CVT_VAR (f32_f16, N_F32, N_F16, whole_reg, NULL, NULL, NULL) \
+ CVT_VAR (f16_f32, N_F16, N_F32, whole_reg, NULL, NULL, NULL) \
+ /* VFP instructions. */ \
+ CVT_VAR (f32_f64, N_F32, N_F64, N_VFP, NULL, "fcvtsd", NULL) \
+ CVT_VAR (f64_f32, N_F64, N_F32, N_VFP, NULL, "fcvtds", NULL) \
+ CVT_VAR (s32_f64, N_S32, N_F64 | key, N_VFP, "ftosld", "ftosid", "ftosizd") \
+ CVT_VAR (u32_f64, N_U32, N_F64 | key, N_VFP, "ftould", "ftouid", "ftouizd") \
+ CVT_VAR (f64_s32, N_F64 | key, N_S32, N_VFP, "fsltod", "fsitod", NULL) \
+ CVT_VAR (f64_u32, N_F64 | key, N_U32, N_VFP, "fultod", "fuitod", NULL) \
+ /* VFP instructions with bitshift. */ \
+ CVT_VAR (f32_s16, N_F32 | key, N_S16, N_VFP, "fshtos", NULL, NULL) \
+ CVT_VAR (f32_u16, N_F32 | key, N_U16, N_VFP, "fuhtos", NULL, NULL) \
+ CVT_VAR (f64_s16, N_F64 | key, N_S16, N_VFP, "fshtod", NULL, NULL) \
+ CVT_VAR (f64_u16, N_F64 | key, N_U16, N_VFP, "fuhtod", NULL, NULL) \
+ CVT_VAR (s16_f32, N_S16, N_F32 | key, N_VFP, "ftoshs", NULL, NULL) \
+ CVT_VAR (u16_f32, N_U16, N_F32 | key, N_VFP, "ftouhs", NULL, NULL) \
+ CVT_VAR (s16_f64, N_S16, N_F64 | key, N_VFP, "ftoshd", NULL, NULL) \
+ CVT_VAR (u16_f64, N_U16, N_F64 | key, N_VFP, "ftouhd", NULL, NULL)
+
+#define CVT_VAR(C, X, Y, R, BSN, CN, ZN) \
+ neon_cvt_flavour_##C,
+
+/* The different types of conversions we can do. */
+enum neon_cvt_flavour
+{
+ CVT_FLAVOUR_VAR
+ neon_cvt_flavour_invalid,
+ neon_cvt_flavour_first_fp = neon_cvt_flavour_f32_f64
+};
+
+#undef CVT_VAR
+
+static enum neon_cvt_flavour
+get_neon_cvt_flavour (enum neon_shape rs)
+{
+#define CVT_VAR(C,X,Y,R,BSN,CN,ZN) \
+ et = neon_check_type (2, rs, (R) | (X), (R) | (Y)); \
+ if (et.type != NT_invtype) \
+ { \
+ inst.error = NULL; \
+ return (neon_cvt_flavour_##C); \
+ }
+
+ struct neon_type_el et;
+ unsigned whole_reg = (rs == NS_FFI || rs == NS_FD || rs == NS_DF
+ || rs == NS_FF) ? N_VFP : 0;
+ /* The instruction versions which take an immediate take one register
+ argument, which is extended to the width of the full register. Thus the
+ "source" and "destination" registers must have the same width. Hack that
+ here by making the size equal to the key (wider, in this case) operand. */
+ unsigned key = (rs == NS_QQI || rs == NS_DDI || rs == NS_FFI) ? N_KEY : 0;
+
+ CVT_FLAVOUR_VAR;
+
+ return neon_cvt_flavour_invalid;
+#undef CVT_VAR
+}
+
+enum neon_cvt_mode
+{
+ neon_cvt_mode_a,
+ neon_cvt_mode_n,
+ neon_cvt_mode_p,
+ neon_cvt_mode_m,
+ neon_cvt_mode_z,
+ neon_cvt_mode_x,
+ neon_cvt_mode_r
+};
+
+/* Neon-syntax VFP conversions. */
+
+static void
+do_vfp_nsyn_cvt (enum neon_shape rs, enum neon_cvt_flavour flavour)
+{
+ const char *opname = 0;
+
+ if (rs == NS_DDI || rs == NS_QQI || rs == NS_FFI)
+ {
+ /* Conversions with immediate bitshift. */
+ const char *enc[] =
+ {
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) BSN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
+ };
+
+ if (flavour < (int) ARRAY_SIZE (enc))
+ {
+ opname = enc[flavour];
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("operands 0 and 1 must be the same register"));
+ inst.operands[1] = inst.operands[2];
+ memset (&inst.operands[2], '\0', sizeof (inst.operands[2]));
+ }
+ }
+ else
+ {
+ /* Conversions without bitshift. */
+ const char *enc[] =
+ {
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) CN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
+ };
+
+ if (flavour < (int) ARRAY_SIZE (enc))
+ opname = enc[flavour];
+ }
+
+ if (opname)
+ do_vfp_nsyn_opcode (opname);
+}
+
+static void
+do_vfp_nsyn_cvtz (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_NULL);
+ enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
+ const char *enc[] =
+ {
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) ZN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
+ };
+
+ if (flavour < (int) ARRAY_SIZE (enc) && enc[flavour])
+ do_vfp_nsyn_opcode (enc[flavour]);
+}
+
+static void
+do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour flavour,
+ enum neon_cvt_mode mode)
+{
+ int sz, op;
+ int rm;
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ switch (flavour)
+ {
+ case neon_cvt_flavour_s32_f64:
+ sz = 1;
+ op = 1;
+ break;
+ case neon_cvt_flavour_s32_f32:
+ sz = 0;
+ op = 1;
+ break;
+ case neon_cvt_flavour_u32_f64:
+ sz = 1;
+ op = 0;
+ break;
+ case neon_cvt_flavour_u32_f32:
+ sz = 0;
+ op = 0;
+ break;
+ default:
+ first_error (_("invalid instruction shape"));
+ return;
+ }
+
+ switch (mode)
+ {
+ case neon_cvt_mode_a: rm = 0; break;
+ case neon_cvt_mode_n: rm = 1; break;
+ case neon_cvt_mode_p: rm = 2; break;
+ case neon_cvt_mode_m: rm = 3; break;
+ default: first_error (_("invalid rounding mode")); return;
+ }
+
+ NEON_ENCODE (FPV8, inst);
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, sz == 1 ? VFP_REG_Dm : VFP_REG_Sm);
+ inst.instruction |= sz << 8;
+ inst.instruction |= op << 7;
+ inst.instruction |= rm << 16;
+ inst.instruction |= 0xf0000000;
+ inst.is_neon = TRUE;
+}
+
+static void
+do_neon_cvt_1 (enum neon_cvt_mode mode)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_FFI, NS_DD, NS_QQ,
+ NS_FD, NS_DF, NS_FF, NS_QD, NS_DQ, NS_NULL);
+ enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
+
+ /* PR11109: Handle round-to-zero for VCVT conversions. */
+ if (mode == neon_cvt_mode_z
+ && ARM_CPU_HAS_FEATURE (cpu_variant, fpu_arch_vfp_v2)
+ && (flavour == neon_cvt_flavour_s32_f32
+ || flavour == neon_cvt_flavour_u32_f32
+ || flavour == neon_cvt_flavour_s32_f64
+ || flavour == neon_cvt_flavour_u32_f64)
+ && (rs == NS_FD || rs == NS_FF))
+ {
+ do_vfp_nsyn_cvtz ();
+ return;
+ }
+
+ /* VFP rather than Neon conversions. */
+ if (flavour >= neon_cvt_flavour_first_fp)
+ {
+ if (mode == neon_cvt_mode_x || mode == neon_cvt_mode_z)
+ do_vfp_nsyn_cvt (rs, flavour);
+ else
+ do_vfp_nsyn_cvt_fpv8 (flavour, mode);
+
+ return;
+ }
+
+ switch (rs)
+ {
+ case NS_DDI:
+ case NS_QQI:
+ {
+ unsigned immbits;
+ unsigned enctab[] = { 0x0000100, 0x1000100, 0x0, 0x1000000 };
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ /* Fixed-point conversion with #0 immediate is encoded as an
+ integer conversion. */
+ if (inst.operands[2].present && inst.operands[2].imm == 0)
+ goto int_encode;
+ immbits = 32 - inst.operands[2].imm;
+ NEON_ENCODE (IMMED, inst);
+ if (flavour != neon_cvt_flavour_invalid)
+ inst.instruction |= enctab[flavour];
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= 1 << 21;
+ inst.instruction |= immbits << 16;
+
+ neon_dp_fixup (&inst);
+ }
+ break;
+
+ case NS_DD:
+ case NS_QQ:
+ if (mode != neon_cvt_mode_x && mode != neon_cvt_mode_z)
+ {
+ NEON_ENCODE (FLOAT, inst);
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= (flavour == neon_cvt_flavour_u32_f32) << 7;
+ inst.instruction |= mode << 8;
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+ }
+ else
+ {
+ int_encode:
+ {
+ unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080 };
+
+ NEON_ENCODE (INTEGER, inst);
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ if (flavour != neon_cvt_flavour_invalid)
+ inst.instruction |= enctab[flavour];
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= 2 << 18;
+
+ neon_dp_fixup (&inst);
+ }
+ }
+ break;
+
+ /* Half-precision conversions for Advanced SIMD -- neon. */
+ case NS_QD:
+ case NS_DQ:
+
+ if ((rs == NS_DQ)
+ && (inst.vectype.el[0].size != 16 || inst.vectype.el[1].size != 32))
+ {
+ as_bad (_("operand size must match register width"));
+ break;
+ }
+
+ if ((rs == NS_QD)
+ && ((inst.vectype.el[0].size != 32 || inst.vectype.el[1].size != 16)))
+ {
+ as_bad (_("operand size must match register width"));
+ break;
+ }
+
+ if (rs == NS_DQ)
+ inst.instruction = 0x3b60600;
+ else
+ inst.instruction = 0x3b60700;
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ neon_dp_fixup (&inst);
+ break;
+
+ default:
+ /* Some VFP conversions go here (s32 <-> f32, u32 <-> f32). */
+ if (mode == neon_cvt_mode_x || mode == neon_cvt_mode_z)
+ do_vfp_nsyn_cvt (rs, flavour);
+ else
+ do_vfp_nsyn_cvt_fpv8 (flavour, mode);
+ }
+}
+
+static void
+do_neon_cvtr (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_x);
+}
+
+static void
+do_neon_cvt (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_z);
+}
+
+static void
+do_neon_cvta (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_a);
+}
+
+static void
+do_neon_cvtn (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_n);
+}
+
+static void
+do_neon_cvtp (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_p);
+}
+
+static void
+do_neon_cvtm (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_m);
+}
+
+static void
+do_neon_cvttb_2 (bfd_boolean t, bfd_boolean to, bfd_boolean is_double)
+{
+ if (is_double)
+ mark_feature_used (&fpu_vfp_ext_armv8);
+
+ encode_arm_vfp_reg (inst.operands[0].reg,
+ (is_double && !to) ? VFP_REG_Dd : VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg,
+ (is_double && to) ? VFP_REG_Dm : VFP_REG_Sm);
+ inst.instruction |= to ? 0x10000 : 0;
+ inst.instruction |= t ? 0x80 : 0;
+ inst.instruction |= is_double ? 0x100 : 0;
+ do_vfp_cond_or_thumb ();
+}
+
+static void
+do_neon_cvttb_1 (bfd_boolean t)
+{
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_DF, NS_NULL);
+
+ if (rs == NS_NULL)
+ return;
+ else if (neon_check_type (2, rs, N_F16, N_F32 | N_VFP).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/TRUE, /*is_double=*/FALSE);
+ }
+ else if (neon_check_type (2, rs, N_F32 | N_VFP, N_F16).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/FALSE, /*is_double=*/FALSE);
+ }
+ else if (neon_check_type (2, rs, N_F16, N_F64 | N_VFP).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/TRUE, /*is_double=*/TRUE);
+ }
+ else if (neon_check_type (2, rs, N_F64 | N_VFP, N_F16).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/FALSE, /*is_double=*/TRUE);
+ }
+ else
+ return;
+}
+
+static void
+do_neon_cvtb (void)
+{
+ do_neon_cvttb_1 (FALSE);
+}
+
+
+static void
+do_neon_cvtt (void)
+{
+ do_neon_cvttb_1 (TRUE);
+}
+
+static void
+neon_move_immediate (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DI, NS_QI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK);
+ unsigned immlo, immhi = 0, immbits;
+ int op, cmode, float_p;
+
+ constraint (et.type == NT_invtype,
+ _("operand size must be specified for immediate VMOV"));
+
+ /* We start out as an MVN instruction if OP = 1, MOV otherwise. */
+ op = (inst.instruction & (1 << 5)) != 0;
+
+ immlo = inst.operands[1].imm;
+ if (inst.operands[1].regisimm)
+ immhi = inst.operands[1].reg;
+
+ constraint (et.size < 32 && (immlo & ~((1 << et.size) - 1)) != 0,
+ _("immediate has bits set outside the operand size"));
+
+ float_p = inst.operands[1].immisfloat;
+
+ if ((cmode = neon_cmode_for_move_imm (immlo, immhi, float_p, &immbits, &op,
+ et.size, et.type)) == FAIL)
+ {
+ /* Invert relevant bits only. */
+ neon_invert_size (&immlo, &immhi, et.size);
+ /* Flip from VMOV/VMVN to VMVN/VMOV. Some immediate types are unavailable
+ with one or the other; those cases are caught by
+ neon_cmode_for_move_imm. */
+ op = !op;
+ if ((cmode = neon_cmode_for_move_imm (immlo, immhi, float_p, &immbits,
+ &op, et.size, et.type)) == FAIL)
+ {
+ first_error (_("immediate out of range"));
+ return;
+ }
+ }
+
+ inst.instruction &= ~(1 << 5);
+ inst.instruction |= op << 5;
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= cmode << 8;
+
+ neon_write_immbits (immbits);
+}
+
+static void
+do_neon_mvn (void)
+{
+ if (inst.operands[1].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+
+ NEON_ENCODE (INTEGER, inst);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ }
+ else
+ {
+ NEON_ENCODE (IMMED, inst);
+ neon_move_immediate ();
+ }
+
+ neon_dp_fixup (&inst);
+}
+
+/* Encode instructions of form:
+
+ |28/24|23|22|21 20|19 16|15 12|11 8|7|6|5|4|3 0|
+ | U |x |D |size | Rn | Rd |x x x x|N|x|M|x| Rm | */
+
+static void
+neon_mixed_length (struct neon_type_el et, unsigned size)
+{
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= (et.type == NT_unsigned) << 24;
+ inst.instruction |= neon_logbits (size) << 20;
+
+ neon_dp_fixup (&inst);
+}
+
+static void
+do_neon_dyadic_long (void)
+{
+ /* FIXME: Type checking for lengthening op. */
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, N_SU_32 | N_KEY);
+ neon_mixed_length (et, et.size);
+}
+
+static void
+do_neon_abal (void)
+{
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_INT | N_DBL, N_EQK, N_SU_32 | N_KEY);
+ neon_mixed_length (et, et.size);
+}
+
+static void
+neon_mac_reg_scalar_long (unsigned regtypes, unsigned scalartypes)
+{
+ if (inst.operands[2].isscalar)
+ {
+ struct neon_type_el et = neon_check_type (3, NS_QDS,
+ N_EQK | N_DBL, N_EQK, regtypes | N_KEY);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, et.type == NT_unsigned);
+ }
+ else
+ {
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, scalartypes | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ neon_mixed_length (et, et.size);
+ }
+}
+
+static void
+do_neon_mac_maybe_scalar_long (void)
+{
+ neon_mac_reg_scalar_long (N_S16 | N_S32 | N_U16 | N_U32, N_SU_32);
+}
+
+static void
+do_neon_dyadic_wide (void)
+{
+ struct neon_type_el et = neon_check_type (3, NS_QQD,
+ N_EQK | N_DBL, N_EQK | N_DBL, N_SU_32 | N_KEY);
+ neon_mixed_length (et, et.size);
+}
+
+static void
+do_neon_dyadic_narrow (void)
+{
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, N_I16 | N_I32 | N_I64 | N_KEY);
+ /* Operand sign is unimportant, and the U bit is part of the opcode,
+ so force the operand type to integer. */
+ et.type = NT_integer;
+ neon_mixed_length (et, et.size / 2);
+}
+
+static void
+do_neon_mul_sat_scalar_long (void)
+{
+ neon_mac_reg_scalar_long (N_S16 | N_S32, N_S16 | N_S32);
+}
+
+static void
+do_neon_vmull (void)
+{
+ if (inst.operands[2].isscalar)
+ do_neon_mac_maybe_scalar_long ();
+ else
+ {
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, N_SU_32 | N_P8 | N_P64 | N_KEY);
+
+ if (et.type == NT_poly)
+ NEON_ENCODE (POLY, inst);
+ else
+ NEON_ENCODE (INTEGER, inst);
+
+ /* For polynomial encoding the U bit must be zero, and the size must
+ be 8 (encoded as 0b00) or, on ARMv8 or later 64 (encoded, non
+ obviously, as 0b10). */
+ if (et.size == 64)
+ {
+ /* Check we're on the correct architecture. */
+ if (!mark_feature_used (&fpu_crypto_ext_armv8))
+ inst.error =
+ _("Instruction form not available on this architecture.");
+
+ et.size = 32;
+ }
+
+ neon_mixed_length (et, et.size);
+ }
+}
+
+static void
+do_neon_ext (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDDI, NS_QQQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
+ unsigned imm = (inst.operands[3].imm * et.size) / 8;
+
+ constraint (imm >= (unsigned) (neon_quad (rs) ? 16 : 8),
+ _("shift out of range"));
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= imm << 8;
+
+ neon_dp_fixup (&inst);
+}
+
+static void
+do_neon_rev (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ unsigned op = (inst.instruction >> 7) & 3;
+ /* N (width of reversed regions) is encoded as part of the bitmask. We
+ extract it here to check the elements to be reversed are smaller.
+ Otherwise we'd get a reserved instruction. */
+ unsigned elsize = (op == 2) ? 16 : (op == 1) ? 32 : (op == 0) ? 64 : 0;
+ gas_assert (elsize != 0);
+ constraint (et.size >= elsize,
+ _("elements must be smaller than reversal region"));
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_dup (void)
+{
+ if (inst.operands[1].isscalar)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DS, NS_QS, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ unsigned sizebits = et.size >> 3;
+ unsigned dm = NEON_SCALAR_REG (inst.operands[1].reg);
+ int logsize = neon_logbits (et.size);
+ unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg) << logsize;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC) == FAIL)
+ return;
+
+ NEON_ENCODE (SCALAR, inst);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (dm);
+ inst.instruction |= HI1 (dm) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= x << 17;
+ inst.instruction |= sizebits << 16;
+
+ neon_dp_fixup (&inst);
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_DR, NS_QR, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_8 | N_16 | N_32 | N_KEY, N_EQK);
+ /* Duplicate ARM register to lanes of vector. */
+ NEON_ENCODE (ARMREG, inst);
+ switch (et.size)
+ {
+ case 8: inst.instruction |= 0x400000; break;
+ case 16: inst.instruction |= 0x000020; break;
+ case 32: inst.instruction |= 0x000000; break;
+ default: break;
+ }
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 12;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 7;
+ inst.instruction |= neon_quad (rs) << 21;
+ /* The encoding for this instruction is identical for the ARM and Thumb
+ variants, except for the condition field. */
+ do_vfp_cond_or_thumb ();
+ }
+}
+
+/* VMOV has particularly many variations. It can be one of:
+ 0. VMOV<c><q> <Qd>, <Qm>
+ 1. VMOV<c><q> <Dd>, <Dm>
+ (Register operations, which are VORR with Rm = Rn.)
+ 2. VMOV<c><q>.<dt> <Qd>, #<imm>
+ 3. VMOV<c><q>.<dt> <Dd>, #<imm>
+ (Immediate loads.)
+ 4. VMOV<c><q>.<size> <Dn[x]>, <Rd>
+ (ARM register to scalar.)
+ 5. VMOV<c><q> <Dm>, <Rd>, <Rn>
+ (Two ARM registers to vector.)
+ 6. VMOV<c><q>.<dt> <Rd>, <Dn[x]>
+ (Scalar to ARM register.)
+ 7. VMOV<c><q> <Rd>, <Rn>, <Dm>
+ (Vector to two ARM registers.)
+ 8. VMOV.F32 <Sd>, <Sm>
+ 9. VMOV.F64 <Dd>, <Dm>
+ (VFP register moves.)
+ 10. VMOV.F32 <Sd>, #imm
+ 11. VMOV.F64 <Dd>, #imm
+ (VFP float immediate load.)
+ 12. VMOV <Rd>, <Sm>
+ (VFP single to ARM reg.)
+ 13. VMOV <Sd>, <Rm>
+ (ARM reg to VFP single.)
+ 14. VMOV <Rd>, <Re>, <Sn>, <Sm>
+ (Two ARM regs to two VFP singles.)
+ 15. VMOV <Sd>, <Se>, <Rn>, <Rm>
+ (Two VFP singles to two ARM regs.)
+
+ These cases can be disambiguated using neon_select_shape, except cases 1/9
+ and 3/11 which depend on the operand type too.
+
+ All the encoded bits are hardcoded by this function.
+
+ Cases 4, 6 may be used with VFPv1 and above (only 32-bit transfers!).
+ Cases 5, 7 may be used with VFPv2 and above.
+
+ FIXME: Some of the checking may be a bit sloppy (in a couple of cases you
+ can specify a type where it doesn't make sense to, and is ignored). */
+
+static void
+do_neon_mov (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_RRFF, NS_FFRR, NS_DRR, NS_RRD,
+ NS_QQ, NS_DD, NS_QI, NS_DI, NS_SR, NS_RS, NS_FF, NS_FI, NS_RF, NS_FR,
+ NS_NULL);
+ struct neon_type_el et;
+ const char *ldconst = 0;
+
+ switch (rs)
+ {
+ case NS_DD: /* case 1/9. */
+ et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY);
+ /* It is not an error here if no type is given. */
+ inst.error = NULL;
+ if (et.type == NT_float && et.size == 64)
+ {
+ do_vfp_nsyn_opcode ("fcpyd");
+ break;
+ }
+ /* fall through. */
+
+ case NS_QQ: /* case 0/1. */
+ {
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+ /* The architecture manual I have doesn't explicitly state which
+ value the U bit should have for register->register moves, but
+ the equivalent VORR instruction has U = 0, so do that. */
+ inst.instruction = 0x0200110;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= neon_quad (rs) << 6;
+
+ neon_dp_fixup (&inst);
+ }
+ break;
+
+ case NS_DI: /* case 3/11. */
+ et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY);
+ inst.error = NULL;
+ if (et.type == NT_float && et.size == 64)
+ {
+ /* case 11 (fconstd). */
+ ldconst = "fconstd";
+ goto encode_fconstd;
+ }
+ /* fall through. */
+
+ case NS_QI: /* case 2/3. */
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+ inst.instruction = 0x0800010;
+ neon_move_immediate ();
+ neon_dp_fixup (&inst);
+ break;
+
+ case NS_SR: /* case 4. */
+ {
+ unsigned bcdebits = 0;
+ int logsize;
+ unsigned dn = NEON_SCALAR_REG (inst.operands[0].reg);
+ unsigned x = NEON_SCALAR_INDEX (inst.operands[0].reg);
+
+ /* .<size> is optional here, defaulting to .32. */
+ if (inst.vectype.elems == 0
+ && inst.operands[0].vectype.type == NT_invtype
+ && inst.operands[1].vectype.type == NT_invtype)
+ {
+ inst.vectype.el[0].type = NT_untyped;
+ inst.vectype.el[0].size = 32;
+ inst.vectype.elems = 1;
+ }
+
+ et = neon_check_type (2, NS_NULL, N_8 | N_16 | N_32 | N_KEY, N_EQK);
+ logsize = neon_logbits (et.size);
+
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1),
+ _(BAD_FPU));
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)
+ && et.size != 32, _(BAD_FPU));
+ constraint (et.type == NT_invtype, _("bad type for scalar"));
+ constraint (x >= 64 / et.size, _("scalar index out of range"));
+
+ switch (et.size)
+ {
+ case 8: bcdebits = 0x8; break;
+ case 16: bcdebits = 0x1; break;
+ case 32: bcdebits = 0x0; break;
+ default: ;
+ }
+
+ bcdebits |= x << logsize;
+
+ inst.instruction = 0xe000b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= LOW4 (dn) << 16;
+ inst.instruction |= HI1 (dn) << 7;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= (bcdebits & 3) << 5;
+ inst.instruction |= (bcdebits >> 2) << 21;
+ }
+ break;
+
+ case NS_DRR: /* case 5 (fmdrr). */
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
+ _(BAD_FPU));
+
+ inst.instruction = 0xc400b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= LOW4 (inst.operands[0].reg);
+ inst.instruction |= HI1 (inst.operands[0].reg) << 5;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ break;
+
+ case NS_RS: /* case 6. */
+ {
+ unsigned logsize;
+ unsigned dn = NEON_SCALAR_REG (inst.operands[1].reg);
+ unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg);
+ unsigned abcdebits = 0;
+
+ /* .<dt> is optional here, defaulting to .32. */
+ if (inst.vectype.elems == 0
+ && inst.operands[0].vectype.type == NT_invtype
+ && inst.operands[1].vectype.type == NT_invtype)
+ {
+ inst.vectype.el[0].type = NT_untyped;
+ inst.vectype.el[0].size = 32;
+ inst.vectype.elems = 1;
+ }
+
+ et = neon_check_type (2, NS_NULL,
+ N_EQK, N_S8 | N_S16 | N_U8 | N_U16 | N_32 | N_KEY);
+ logsize = neon_logbits (et.size);
+
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1),
+ _(BAD_FPU));
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)
+ && et.size != 32, _(BAD_FPU));
+ constraint (et.type == NT_invtype, _("bad type for scalar"));
+ constraint (x >= 64 / et.size, _("scalar index out of range"));
+
+ switch (et.size)
+ {
+ case 8: abcdebits = (et.type == NT_signed) ? 0x08 : 0x18; break;
+ case 16: abcdebits = (et.type == NT_signed) ? 0x01 : 0x11; break;
+ case 32: abcdebits = 0x00; break;
+ default: ;
+ }
+
+ abcdebits |= x << logsize;
+ inst.instruction = 0xe100b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= LOW4 (dn) << 16;
+ inst.instruction |= HI1 (dn) << 7;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= (abcdebits & 3) << 5;
+ inst.instruction |= (abcdebits >> 2) << 21;
+ }
+ break;
+
+ case NS_RRD: /* case 7 (fmrrd). */
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
+ _(BAD_FPU));
+
+ inst.instruction = 0xc500b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ break;
+
+ case NS_FF: /* case 8 (fcpys). */
+ do_vfp_nsyn_opcode ("fcpys");
+ break;
+
+ case NS_FI: /* case 10 (fconsts). */
+ ldconst = "fconsts";
+ encode_fconstd:
+ if (is_quarter_float (inst.operands[1].imm))
+ {
+ inst.operands[1].imm = neon_qfloat_bits (inst.operands[1].imm);
+ do_vfp_nsyn_opcode (ldconst);
+ }
+ else
+ first_error (_("immediate out of range"));
+ break;
+
+ case NS_RF: /* case 12 (fmrs). */
+ do_vfp_nsyn_opcode ("fmrs");
+ break;
+
+ case NS_FR: /* case 13 (fmsr). */
+ do_vfp_nsyn_opcode ("fmsr");
+ break;
+
+ /* The encoders for the fmrrs and fmsrr instructions expect three operands
+ (one of which is a list), but we have parsed four. Do some fiddling to
+ make the operands what do_vfp_reg2_from_sp2 and do_vfp_sp2_from_reg2
+ expect. */
+ case NS_RRFF: /* case 14 (fmrrs). */
+ constraint (inst.operands[3].reg != inst.operands[2].reg + 1,
+ _("VFP registers must be adjacent"));
+ inst.operands[2].imm = 2;
+ memset (&inst.operands[3], '\0', sizeof (inst.operands[3]));
+ do_vfp_nsyn_opcode ("fmrrs");
+ break;
+
+ case NS_FFRR: /* case 15 (fmsrr). */
+ constraint (inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("VFP registers must be adjacent"));
+ inst.operands[1] = inst.operands[2];
+ inst.operands[2] = inst.operands[3];
+ inst.operands[0].imm = 2;
+ memset (&inst.operands[3], '\0', sizeof (inst.operands[3]));
+ do_vfp_nsyn_opcode ("fmsrr");
+ break;
+
+ case NS_NULL:
+ /* neon_select_shape has determined that the instruction
+ shape is wrong and has already set the error message. */
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+static void
+do_neon_rshift_round_imm (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_ALL | N_KEY);
+ int imm = inst.operands[2].imm;
+
+ /* imm == 0 case is encoded as VMOV for V{R}SHR. */
+ if (imm == 0)
+ {
+ inst.operands[2].present = 0;
+ do_neon_mov ();
+ return;
+ }
+
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range for shift"));
+ neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
+ et.size - imm);
+}
+
+static void
+do_neon_movl (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_QD,
+ N_EQK | N_DBL, N_SU_32 | N_KEY);
+ unsigned sizebits = et.size >> 3;
+ inst.instruction |= sizebits << 19;
+ neon_two_same (0, et.type == NT_unsigned, -1);
+}
+
+static void
+do_neon_trn (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_zip_uzp (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ if (rs == NS_DD && et.size == 32)
+ {
+ /* Special case: encode as VTRN.32 <Dd>, <Dm>. */
+ inst.instruction = N_MNEM_vtrn;
+ do_neon_trn ();
+ return;
+ }
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_sat_abs_neg (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_S8 | N_S16 | N_S32 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_pair_long (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_32 | N_KEY);
+ /* Unsigned is encoded in OP field (bit 7) for these instruction. */
+ inst.instruction |= (et.type == NT_unsigned) << 7;
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_recip_est (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK | N_FLT, N_F32 | N_U32 | N_KEY);
+ inst.instruction |= (et.type == NT_float) << 8;
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_cls (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_S8 | N_S16 | N_S32 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_clz (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_I8 | N_I16 | N_I32 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_cnt (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK | N_INT, N_8 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
+
+static void
+do_neon_swp (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ neon_two_same (neon_quad (rs), 1, -1);
+}
+
+static void
+do_neon_tbl_tbx (void)
+{
+ unsigned listlenbits;
+ neon_check_type (3, NS_DLD, N_EQK, N_EQK, N_8 | N_KEY);
+
+ if (inst.operands[1].imm < 1 || inst.operands[1].imm > 4)
+ {
+ first_error (_("bad list length for table lookup"));
+ return;
+ }
+
+ listlenbits = inst.operands[1].imm - 1;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= listlenbits << 8;
+
+ neon_dp_fixup (&inst);
+}
+
+static void
+do_neon_ldm_stm (void)
+{
+ /* P, U and L bits are part of bitmask. */
+ int is_dbmode = (inst.instruction & (1 << 24)) != 0;
+ unsigned offsetbits = inst.operands[1].imm * 2;
+
+ if (inst.operands[1].issingle)
+ {
+ do_vfp_nsyn_ldm_stm (is_dbmode);
+ return;
+ }
+
+ constraint (is_dbmode && !inst.operands[0].writeback,
+ _("writeback (!) must be used for VLDMDB and VSTMDB"));
+
+ constraint (inst.operands[1].imm < 1 || inst.operands[1].imm > 16,
+ _("register list must contain at least 1 and at most 16 "
+ "registers"));
+
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[0].writeback << 21;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 22;
+
+ inst.instruction |= offsetbits;
+
+ do_vfp_cond_or_thumb ();
+}
+
+static void
+do_neon_ldr_str (void)
+{
+ int is_ldr = (inst.instruction & (1 << 20)) != 0;
+
+ /* Use of PC in vstr in ARM mode is deprecated in ARMv7.
+ And is UNPREDICTABLE in thumb mode. */
+ if (!is_ldr
+ && inst.operands[1].reg == REG_PC
+ && (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7) || thumb_mode))
+ {
+ if (thumb_mode)
+ inst.error = _("Use of PC here is UNPREDICTABLE");
+ else if (warn_on_deprecated)
+ as_warn (_("Use of PC here is deprecated"));
+ }
+
+ if (inst.operands[0].issingle)
+ {
+ if (is_ldr)
+ do_vfp_nsyn_opcode ("flds");
+ else
+ do_vfp_nsyn_opcode ("fsts");
+ }
+ else
+ {
+ if (is_ldr)
+ do_vfp_nsyn_opcode ("fldd");
+ else
+ do_vfp_nsyn_opcode ("fstd");
+ }
+}
+
+/* "interleave" version also handles non-interleaving register VLD1/VST1
+ instructions. */
+
+static void
+do_neon_ld_st_interleave (void)
+{
+ struct neon_type_el et = neon_check_type (1, NS_NULL,
+ N_8 | N_16 | N_32 | N_64);
+ unsigned alignbits = 0;
+ unsigned idx;
+ /* The bits in this table go:
+ 0: register stride of one (0) or two (1)
+ 1,2: register list length, minus one (1, 2, 3, 4).
+ 3,4: <n> in instruction type, minus one (VLD<n> / VST<n>).
+ We use -1 for invalid entries. */
+ const int typetable[] =
+ {
+ 0x7, -1, 0xa, -1, 0x6, -1, 0x2, -1, /* VLD1 / VST1. */
+ -1, -1, 0x8, 0x9, -1, -1, 0x3, -1, /* VLD2 / VST2. */
+ -1, -1, -1, -1, 0x4, 0x5, -1, -1, /* VLD3 / VST3. */
+ -1, -1, -1, -1, -1, -1, 0x0, 0x1 /* VLD4 / VST4. */
+ };
+ int typebits;
+
+ if (et.type == NT_invtype)
+ return;
+
+ if (inst.operands[1].immisalign)
+ switch (inst.operands[1].imm >> 8)
+ {
+ case 64: alignbits = 1; break;
+ case 128:
+ if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2
+ && NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
+ goto bad_alignment;
+ alignbits = 2;
+ break;
+ case 256:
+ if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
+ goto bad_alignment;
+ alignbits = 3;
+ break;
+ default:
+ bad_alignment:
+ first_error (_("bad alignment"));
+ return;
+ }
+
+ inst.instruction |= alignbits << 4;
+ inst.instruction |= neon_logbits (et.size) << 6;
+
+ /* Bits [4:6] of the immediate in a list specifier encode register stride
+ (minus 1) in bit 4, and list length in bits [5:6]. We put the <n> of
+ VLD<n>/VST<n> in bits [9:8] of the initial bitmask. Suck it out here, look
+ up the right value for "type" in a table based on this value and the given
+ list style, then stick it back. */
+ idx = ((inst.operands[0].imm >> 4) & 7)
+ | (((inst.instruction >> 8) & 3) << 3);
+
+ typebits = typetable[idx];
+
+ constraint (typebits == -1, _("bad list type for instruction"));
+ constraint (((inst.instruction >> 8) & 3) && et.size == 64,
+ _("bad element type for instruction"));
+
+ inst.instruction &= ~0xf00;
+ inst.instruction |= typebits << 8;
+}
+
+/* Check alignment is valid for do_neon_ld_st_lane and do_neon_ld_dup.
+ *DO_ALIGN is set to 1 if the relevant alignment bit should be set, 0
+ otherwise. The variable arguments are a list of pairs of legal (size, align)
+ values, terminated with -1. */
+
+static int
+neon_alignment_bit (int size, int align, int *do_align, ...)
+{
+ va_list ap;
+ int result = FAIL, thissize, thisalign;
+
+ if (!inst.operands[1].immisalign)
+ {
+ *do_align = 0;
+ return SUCCESS;
+ }
+
+ va_start (ap, do_align);
+
+ do
+ {
+ thissize = va_arg (ap, int);
+ if (thissize == -1)
+ break;
+ thisalign = va_arg (ap, int);
+
+ if (size == thissize && align == thisalign)
+ result = SUCCESS;
+ }
+ while (result != SUCCESS);
+
+ va_end (ap);
+
+ if (result == SUCCESS)
+ *do_align = 1;
+ else
+ first_error (_("unsupported alignment for instruction"));
+
+ return result;
+}
+
+static void
+do_neon_ld_st_lane (void)
+{
+ struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32);
+ int align_good, do_align = 0;
+ int logsize = neon_logbits (et.size);
+ int align = inst.operands[1].imm >> 8;
+ int n = (inst.instruction >> 8) & 3;
+ int max_el = 64 / et.size;
+
+ if (et.type == NT_invtype)
+ return;
+
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1,
+ _("bad list length"));
+ constraint (NEON_LANE (inst.operands[0].imm) >= max_el,
+ _("scalar index out of range"));
+ constraint (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2
+ && et.size == 8,
+ _("stride of 2 unavailable when element size is 8"));
+
+ switch (n)
+ {
+ case 0: /* VLD1 / VST1. */
+ align_good = neon_alignment_bit (et.size, align, &do_align, 16, 16,
+ 32, 32, -1);
+ if (align_good == FAIL)
+ return;
+ if (do_align)
+ {
+ unsigned alignbits = 0;
+ switch (et.size)
+ {
+ case 16: alignbits = 0x1; break;
+ case 32: alignbits = 0x3; break;
+ default: ;
+ }
+ inst.instruction |= alignbits << 4;
+ }
+ break;
+
+ case 1: /* VLD2 / VST2. */
+ align_good = neon_alignment_bit (et.size, align, &do_align, 8, 16, 16, 32,
+ 32, 64, -1);
+ if (align_good == FAIL)
+ return;
+ if (do_align)
+ inst.instruction |= 1 << 4;
+ break;
+
+ case 2: /* VLD3 / VST3. */
+ constraint (inst.operands[1].immisalign,
+ _("can't use alignment with this instruction"));
+ break;
+
+ case 3: /* VLD4 / VST4. */
+ align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32,
+ 16, 64, 32, 64, 32, 128, -1);
+ if (align_good == FAIL)
+ return;
+ if (do_align)
+ {
+ unsigned alignbits = 0;
+ switch (et.size)
+ {
+ case 8: alignbits = 0x1; break;
+ case 16: alignbits = 0x1; break;
+ case 32: alignbits = (align == 64) ? 0x1 : 0x2; break;
+ default: ;
+ }
+ inst.instruction |= alignbits << 4;
+ }
+ break;
+
+ default: ;
+ }
+
+ /* Reg stride of 2 is encoded in bit 5 when size==16, bit 6 when size==32. */
+ if (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << (4 + logsize);
+
+ inst.instruction |= NEON_LANE (inst.operands[0].imm) << (logsize + 5);
+ inst.instruction |= logsize << 10;
+}
+
+/* Encode single n-element structure to all lanes VLD<n> instructions. */
+
+static void
+do_neon_ld_dup (void)
+{
+ struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32);
+ int align_good, do_align = 0;
+
+ if (et.type == NT_invtype)
+ return;
+
+ switch ((inst.instruction >> 8) & 3)
+ {
+ case 0: /* VLD1. */
+ gas_assert (NEON_REG_STRIDE (inst.operands[0].imm) != 2);
+ align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8,
+ &do_align, 16, 16, 32, 32, -1);
+ if (align_good == FAIL)
+ return;
+ switch (NEON_REGLIST_LENGTH (inst.operands[0].imm))
+ {
+ case 1: break;
+ case 2: inst.instruction |= 1 << 5; break;
+ default: first_error (_("bad list length")); return;
+ }
+ inst.instruction |= neon_logbits (et.size) << 6;
+ break;
+
+ case 1: /* VLD2. */
+ align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8,
+ &do_align, 8, 16, 16, 32, 32, 64, -1);
+ if (align_good == FAIL)
+ return;
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2,
+ _("bad list length"));
+ if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << 5;
+ inst.instruction |= neon_logbits (et.size) << 6;
+ break;
+
+ case 2: /* VLD3. */
+ constraint (inst.operands[1].immisalign,
+ _("can't use alignment with this instruction"));
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 3,
+ _("bad list length"));
+ if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << 5;
+ inst.instruction |= neon_logbits (et.size) << 6;
+ break;
+
+ case 3: /* VLD4. */
+ {
+ int align = inst.operands[1].imm >> 8;
+ align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32,
+ 16, 64, 32, 64, 32, 128, -1);
+ if (align_good == FAIL)
+ return;
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4,
+ _("bad list length"));
+ if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << 5;
+ if (et.size == 32 && align == 128)
+ inst.instruction |= 0x3 << 6;
+ else
+ inst.instruction |= neon_logbits (et.size) << 6;
+ }
+ break;
+
+ default: ;
+ }
+
+ inst.instruction |= do_align << 4;
+}
+
+/* Disambiguate VLD<n> and VST<n> instructions, and fill in common bits (those
+ apart from bits [11:4]. */
+
+static void
+do_neon_ldx_stx (void)
+{
+ if (inst.operands[1].isreg)
+ constraint (inst.operands[1].reg == REG_PC, BAD_PC);
+
+ switch (NEON_LANE (inst.operands[0].imm))
+ {
+ case NEON_INTERLEAVE_LANES:
+ NEON_ENCODE (INTERLV, inst);
+ do_neon_ld_st_interleave ();
+ break;
+
+ case NEON_ALL_LANES:
+ NEON_ENCODE (DUP, inst);
+ if (inst.instruction == N_INV)
+ {
+ first_error ("only loads support such operands");
+ break;
+ }
+ do_neon_ld_dup ();
+ break;
+
+ default:
+ NEON_ENCODE (LANE, inst);
+ do_neon_ld_st_lane ();
+ }
+
+ /* L bit comes from bit mask. */
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= inst.operands[1].reg << 16;
+
+ if (inst.operands[1].postind)
+ {
+ int postreg = inst.operands[1].imm & 0xf;
+ constraint (!inst.operands[1].immisreg,
+ _("post-index must be a register"));
+ constraint (postreg == 0xd || postreg == 0xf,
+ _("bad register for post-index"));
+ inst.instruction |= postreg;
+ }
+ else
+ {
+ constraint (inst.operands[1].immisreg, BAD_ADDR_MODE);
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ BAD_ADDR_MODE);
+
+ if (inst.operands[1].writeback)
+ {
+ inst.instruction |= 0xd;
+ }
+ else
+ inst.instruction |= 0xf;
+ }
+
+ if (thumb_mode)
+ inst.instruction |= 0xf9000000;
+ else
+ inst.instruction |= 0xf4000000;
+}
+
+/* FP v8. */
+static void
+do_vfp_nsyn_fpv8 (enum neon_shape rs)
+{
+ NEON_ENCODE (FPV8, inst);
+
+ if (rs == NS_FFF)
+ do_vfp_sp_dyadic ();
+ else
+ do_vfp_dp_rd_rn_rm ();
+
+ if (rs == NS_DDD)
+ inst.instruction |= 0x100;
+
+ inst.instruction |= 0xf0000000;
+}
+
+static void
+do_vsel (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fpv8) != SUCCESS)
+ first_error (_("invalid instruction shape"));
+}
+
+static void
+do_vmaxnm (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fpv8) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
+
+ neon_dyadic_misc (NT_untyped, N_F32, 0);
+}
+
+static void
+do_vrint_1 (enum neon_cvt_mode mode)
+{
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et;
+
+ if (rs == NS_NULL)
+ return;
+
+ et = neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ if (et.type != NT_invtype)
+ {
+ /* VFP encodings. */
+ if (mode == neon_cvt_mode_a || mode == neon_cvt_mode_n
+ || mode == neon_cvt_mode_p || mode == neon_cvt_mode_m)
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ NEON_ENCODE (FPV8, inst);
+ if (rs == NS_FF)
+ do_vfp_sp_monadic ();
+ else
+ do_vfp_dp_rd_rm ();
+
+ switch (mode)
+ {
+ case neon_cvt_mode_r: inst.instruction |= 0x00000000; break;
+ case neon_cvt_mode_z: inst.instruction |= 0x00000080; break;
+ case neon_cvt_mode_x: inst.instruction |= 0x00010000; break;
+ case neon_cvt_mode_a: inst.instruction |= 0xf0000000; break;
+ case neon_cvt_mode_n: inst.instruction |= 0xf0010000; break;
+ case neon_cvt_mode_p: inst.instruction |= 0xf0020000; break;
+ case neon_cvt_mode_m: inst.instruction |= 0xf0030000; break;
+ default: abort ();
+ }
+
+ inst.instruction |= (rs == NS_DD) << 8;
+ do_vfp_cond_or_thumb ();
+ }
+ else
+ {
+ /* Neon encodings (or something broken...). */
+ inst.error = NULL;
+ et = neon_check_type (2, rs, N_EQK, N_F32 | N_KEY);
+
+ if (et.type == NT_invtype)
+ return;
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ NEON_ENCODE (FLOAT, inst);
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ switch (mode)
+ {
+ case neon_cvt_mode_z: inst.instruction |= 3 << 7; break;
+ case neon_cvt_mode_x: inst.instruction |= 1 << 7; break;
+ case neon_cvt_mode_a: inst.instruction |= 2 << 7; break;
+ case neon_cvt_mode_n: inst.instruction |= 0 << 7; break;
+ case neon_cvt_mode_p: inst.instruction |= 7 << 7; break;
+ case neon_cvt_mode_m: inst.instruction |= 5 << 7; break;
+ case neon_cvt_mode_r: inst.error = _("invalid rounding mode"); break;
+ default: abort ();
+ }
+
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+ }
+}
+
+static void
+do_vrintx (void)
+{
+ do_vrint_1 (neon_cvt_mode_x);
+}
+
+static void
+do_vrintz (void)
+{
+ do_vrint_1 (neon_cvt_mode_z);
+}
+
+static void
+do_vrintr (void)
+{
+ do_vrint_1 (neon_cvt_mode_r);
+}
+
+static void
+do_vrinta (void)
+{
+ do_vrint_1 (neon_cvt_mode_a);
+}
+
+static void
+do_vrintn (void)
+{
+ do_vrint_1 (neon_cvt_mode_n);
+}
+
+static void
+do_vrintp (void)
+{
+ do_vrint_1 (neon_cvt_mode_p);
+}
+
+static void
+do_vrintm (void)
+{
+ do_vrint_1 (neon_cvt_mode_m);
+}
+
+/* Crypto v1 instructions. */
+static void
+do_crypto_2op_1 (unsigned elttype, int op)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (neon_check_type (2, NS_QQ, N_EQK | N_UNT, elttype | N_UNT | N_KEY).type
+ == NT_invtype)
+ return;
+
+ inst.error = NULL;
+
+ NEON_ENCODE (INTEGER, inst);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ if (op != -1)
+ inst.instruction |= op << 6;
+
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+}
+
+static void
+do_crypto_3op_1 (int u, int op)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (neon_check_type (3, NS_QQQ, N_EQK | N_UNT, N_EQK | N_UNT,
+ N_32 | N_UNT | N_KEY).type == NT_invtype)
+ return;
+
+ inst.error = NULL;
+
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (1, u, 8 << op);
+}
+
+static void
+do_aese (void)
+{
+ do_crypto_2op_1 (N_8, 0);
+}
+
+static void
+do_aesd (void)
+{
+ do_crypto_2op_1 (N_8, 1);
+}
+
+static void
+do_aesmc (void)
+{
+ do_crypto_2op_1 (N_8, 2);
+}
+
+static void
+do_aesimc (void)
+{
+ do_crypto_2op_1 (N_8, 3);
+}
+
+static void
+do_sha1c (void)
+{
+ do_crypto_3op_1 (0, 0);
+}
+
+static void
+do_sha1p (void)
+{
+ do_crypto_3op_1 (0, 1);
+}
+
+static void
+do_sha1m (void)
+{
+ do_crypto_3op_1 (0, 2);
+}
+
+static void
+do_sha1su0 (void)
+{
+ do_crypto_3op_1 (0, 3);
+}
+
+static void
+do_sha256h (void)
+{
+ do_crypto_3op_1 (1, 0);
+}
+
+static void
+do_sha256h2 (void)
+{
+ do_crypto_3op_1 (1, 1);
+}
+
+static void
+do_sha256su1 (void)
+{
+ do_crypto_3op_1 (1, 2);
+}
+
+static void
+do_sha1h (void)
+{
+ do_crypto_2op_1 (N_32, -1);
+}
+
+static void
+do_sha1su1 (void)
+{
+ do_crypto_2op_1 (N_32, 0);
+}
+
+static void
+do_sha256su0 (void)
+{
+ do_crypto_2op_1 (N_32, 1);
+}
+
+static void
+do_crc32_1 (unsigned int poly, unsigned int sz)
+{
+ unsigned int Rd = inst.operands[0].reg;
+ unsigned int Rn = inst.operands[1].reg;
+ unsigned int Rm = inst.operands[2].reg;
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ inst.instruction |= LOW4 (Rd) << (thumb_mode ? 8 : 12);
+ inst.instruction |= LOW4 (Rn) << 16;
+ inst.instruction |= LOW4 (Rm);
+ inst.instruction |= sz << (thumb_mode ? 4 : 21);
+ inst.instruction |= poly << (thumb_mode ? 20 : 9);
+
+ if (Rd == REG_PC || Rn == REG_PC || Rm == REG_PC)
+ as_warn (UNPRED_REG ("r15"));
+ if (thumb_mode && (Rd == REG_SP || Rn == REG_SP || Rm == REG_SP))
+ as_warn (UNPRED_REG ("r13"));
+}
+
+static void
+do_crc32b (void)
+{
+ do_crc32_1 (0, 0);
+}
+
+static void
+do_crc32h (void)
+{
+ do_crc32_1 (0, 1);
+}
+
+static void
+do_crc32w (void)
+{
+ do_crc32_1 (0, 2);
+}
+
+static void
+do_crc32cb (void)
+{
+ do_crc32_1 (1, 0);
+}
+
+static void
+do_crc32ch (void)
+{
+ do_crc32_1 (1, 1);
+}
+
+static void
+do_crc32cw (void)
+{
+ do_crc32_1 (1, 2);
+}
+
+
+/* Overall per-instruction processing. */
+
+/* We need to be able to fix up arbitrary expressions in some statements.
+ This is so that we can handle symbols that are an arbitrary distance from
+ the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+ which returns part of an address in a form which will be valid for
+ a data instruction. We do this by pushing the expression into a symbol
+ in the expr_section, and creating a fix for that. */
+
+static void
+fix_new_arm (fragS * frag,
+ int where,
+ short int size,
+ expressionS * exp,
+ int pc_rel,
+ int reloc)
+{
+ fixS * new_fix;
+
+ switch (exp->X_op)
+ {
+ case O_constant:
+ if (pc_rel)
+ {
+ /* Create an absolute valued symbol, so we have something to
+ refer to in the object file. Unfortunately for us, gas's
+ generic expression parsing will already have folded out
+ any use of .set foo/.type foo %function that may have
+ been used to set type information of the target location,
+ that's being specified symbolically. We have to presume
+ the user knows what they are doing. */
+ char name[16 + 8];
+ symbolS *symbol;
+
+ sprintf (name, "*ABS*0x%lx", (unsigned long)exp->X_add_number);
+
+ symbol = symbol_find_or_make (name);
+ S_SET_SEGMENT (symbol, absolute_section);
+ symbol_set_frag (symbol, &zero_address_frag);
+ S_SET_VALUE (symbol, exp->X_add_number);
+ exp->X_op = O_symbol;
+ exp->X_add_symbol = symbol;
+ exp->X_add_number = 0;
+ }
+ /* FALLTHROUGH */
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ new_fix = fix_new_exp (frag, where, size, exp, pc_rel,
+ (enum bfd_reloc_code_real) reloc);
+ break;
+
+ default:
+ new_fix = (fixS *) fix_new (frag, where, size, make_expr_symbol (exp), 0,
+ pc_rel, (enum bfd_reloc_code_real) reloc);
+ break;
+ }
+
+ /* Mark whether the fix is to a THUMB instruction, or an ARM
+ instruction. */
+ new_fix->tc_fix_data = thumb_mode;
+}
+
+/* Create a frg for an instruction requiring relaxation. */
+static void
+output_relax_insn (void)
+{
+ char * to;
+ symbolS *sym;
+ int offset;
+
+ /* The size of the instruction is unknown, so tie the debug info to the
+ start of the instruction. */
+ dwarf2_emit_insn (0);
+
+ switch (inst.reloc.exp.X_op)
+ {
+ case O_symbol:
+ sym = inst.reloc.exp.X_add_symbol;
+ offset = inst.reloc.exp.X_add_number;
+ break;
+ case O_constant:
+ sym = NULL;
+ offset = inst.reloc.exp.X_add_number;
+ break;
+ default:
+ sym = make_expr_symbol (&inst.reloc.exp);
+ offset = 0;
+ break;
+ }
+ to = frag_var (rs_machine_dependent, INSN_SIZE, THUMB_SIZE,
+ inst.relax, sym, offset, NULL/*offset, opcode*/);
+ md_number_to_chars (to, inst.instruction, THUMB_SIZE);
+}
+
+/* Write a 32-bit thumb instruction to buf. */
+static void
+put_thumb32_insn (char * buf, unsigned long insn)
+{
+ md_number_to_chars (buf, insn >> 16, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, insn, THUMB_SIZE);
+}
+
+static void
+output_inst (const char * str)
+{
+ char * to = NULL;
+
+ if (inst.error)
+ {
+ as_bad ("%s -- `%s'", inst.error, str);
+ return;
+ }
+ if (inst.relax)
+ {
+ output_relax_insn ();
+ return;
+ }
+ if (inst.size == 0)
+ return;
+
+ to = frag_more (inst.size);
+ /* PR 9814: Record the thumb mode into the current frag so that we know
+ what type of NOP padding to use, if necessary. We override any previous
+ setting so that if the mode has changed then the NOPS that we use will
+ match the encoding of the last instruction in the frag. */
+ frag_now->tc_frag_data.thumb_mode = thumb_mode | MODE_RECORDED;
+
+ if (thumb_mode && (inst.size > THUMB_SIZE))
+ {
+ gas_assert (inst.size == (2 * THUMB_SIZE));
+ put_thumb32_insn (to, inst.instruction);
+ }
+ else if (inst.size > INSN_SIZE)
+ {
+ gas_assert (inst.size == (2 * INSN_SIZE));
+ md_number_to_chars (to, inst.instruction, INSN_SIZE);
+ md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
+ }
+ else
+ md_number_to_chars (to, inst.instruction, inst.size);
+
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
+ fix_new_arm (frag_now, to - frag_now->fr_literal,
+ inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
+ inst.reloc.type);
+
+ dwarf2_emit_insn (inst.size);
+}
+
+static char *
+output_it_inst (int cond, int mask, char * to)
+{
+ unsigned long instruction = 0xbf00;
+
+ mask &= 0xf;
+ instruction |= mask;
+ instruction |= cond << 4;
+
+ if (to == NULL)
+ {
+ to = frag_more (2);
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (2);
+#endif
+ }
+
+ md_number_to_chars (to, instruction, 2);
+
+ return to;
+}
+
+/* Tag values used in struct asm_opcode's tag field. */
+enum opcode_tag
+{
+ OT_unconditional, /* Instruction cannot be conditionalized.
+ The ARM condition field is still 0xE. */
+ OT_unconditionalF, /* Instruction cannot be conditionalized
+ and carries 0xF in its ARM condition field. */
+ OT_csuffix, /* Instruction takes a conditional suffix. */
+ OT_csuffixF, /* Some forms of the instruction take a conditional
+ suffix, others place 0xF where the condition field
+ would be. */
+ OT_cinfix3, /* Instruction takes a conditional infix,
+ beginning at character index 3. (In
+ unified mode, it becomes a suffix.) */
+ OT_cinfix3_deprecated, /* The same as OT_cinfix3. This is used for
+ tsts, cmps, cmns, and teqs. */
+ OT_cinfix3_legacy, /* Legacy instruction takes a conditional infix at
+ character index 3, even in unified mode. Used for
+ legacy instructions where suffix and infix forms
+ may be ambiguous. */
+ OT_csuf_or_in3, /* Instruction takes either a conditional
+ suffix or an infix at character index 3. */
+ OT_odd_infix_unc, /* This is the unconditional variant of an
+ instruction that takes a conditional infix
+ at an unusual position. In unified mode,
+ this variant will accept a suffix. */
+ OT_odd_infix_0 /* Values greater than or equal to OT_odd_infix_0
+ are the conditional variants of instructions that
+ take conditional infixes in unusual positions.
+ The infix appears at character index
+ (tag - OT_odd_infix_0). These are not accepted
+ in unified mode. */
+};
+
+/* Subroutine of md_assemble, responsible for looking up the primary
+ opcode from the mnemonic the user wrote. STR points to the
+ beginning of the mnemonic.
+
+ This is not simply a hash table lookup, because of conditional
+ variants. Most instructions have conditional variants, which are
+ expressed with a _conditional affix_ to the mnemonic. If we were
+ to encode each conditional variant as a literal string in the opcode
+ table, it would have approximately 20,000 entries.
+
+ Most mnemonics take this affix as a suffix, and in unified syntax,
+ 'most' is upgraded to 'all'. However, in the divided syntax, some
+ instructions take the affix as an infix, notably the s-variants of
+ the arithmetic instructions. Of those instructions, all but six
+ have the infix appear after the third character of the mnemonic.
+
+ Accordingly, the algorithm for looking up primary opcodes given
+ an identifier is:
+
+ 1. Look up the identifier in the opcode table.
+ If we find a match, go to step U.
+
+ 2. Look up the last two characters of the identifier in the
+ conditions table. If we find a match, look up the first N-2
+ characters of the identifier in the opcode table. If we
+ find a match, go to step CE.
+
+ 3. Look up the fourth and fifth characters of the identifier in
+ the conditions table. If we find a match, extract those
+ characters from the identifier, and look up the remaining
+ characters in the opcode table. If we find a match, go
+ to step CM.
+
+ 4. Fail.
+
+ U. Examine the tag field of the opcode structure, in case this is
+ one of the six instructions with its conditional infix in an
+ unusual place. If it is, the tag tells us where to find the
+ infix; look it up in the conditions table and set inst.cond
+ accordingly. Otherwise, this is an unconditional instruction.
+ Again set inst.cond accordingly. Return the opcode structure.
+
+ CE. Examine the tag field to make sure this is an instruction that
+ should receive a conditional suffix. If it is not, fail.
+ Otherwise, set inst.cond from the suffix we already looked up,
+ and return the opcode structure.
+
+ CM. Examine the tag field to make sure this is an instruction that
+ should receive a conditional infix after the third character.
+ If it is not, fail. Otherwise, undo the edits to the current
+ line of input and proceed as for case CE. */
+
+static const struct asm_opcode *
+opcode_lookup (char **str)
+{
+ char *end, *base;
+ char *affix;
+ const struct asm_opcode *opcode;
+ const struct asm_cond *cond;
+ char save[2];
+
+ /* Scan up to the end of the mnemonic, which must end in white space,
+ '.' (in unified mode, or for Neon/VFP instructions), or end of string. */
+ for (base = end = *str; *end != '\0'; end++)
+ if (*end == ' ' || *end == '.')
+ break;
+
+ if (end == base)
+ return NULL;
+
+ /* Handle a possible width suffix and/or Neon type suffix. */
+ if (end[0] == '.')
+ {
+ int offset = 2;
+
+ /* The .w and .n suffixes are only valid if the unified syntax is in
+ use. */
+ if (unified_syntax && end[1] == 'w')
+ inst.size_req = 4;
+ else if (unified_syntax && end[1] == 'n')
+ inst.size_req = 2;
+ else
+ offset = 0;
+
+ inst.vectype.elems = 0;
+
+ *str = end + offset;
+
+ if (end[offset] == '.')
+ {
+ /* See if we have a Neon type suffix (possible in either unified or
+ non-unified ARM syntax mode). */
+ if (parse_neon_type (&inst.vectype, str) == FAIL)
+ return NULL;
+ }
+ else if (end[offset] != '\0' && end[offset] != ' ')
+ return NULL;
+ }
+ else
+ *str = end;
+
+ /* Look for unaffixed or special-case affixed mnemonic. */
+ opcode = (const struct asm_opcode *) hash_find_n (arm_ops_hsh, base,
+ end - base);
+ if (opcode)
+ {
+ /* step U */
+ if (opcode->tag < OT_odd_infix_0)
+ {
+ inst.cond = COND_ALWAYS;
+ return opcode;
+ }
+
+ if (warn_on_deprecated && unified_syntax)
+ as_warn (_("conditional infixes are deprecated in unified syntax"));
+ affix = base + (opcode->tag - OT_odd_infix_0);
+ cond = (const struct asm_cond *) hash_find_n (arm_cond_hsh, affix, 2);
+ gas_assert (cond);
+
+ inst.cond = cond->value;
+ return opcode;
+ }
+
+ /* Cannot have a conditional suffix on a mnemonic of less than two
+ characters. */
+ if (end - base < 3)
+ return NULL;
+
+ /* Look for suffixed mnemonic. */
+ affix = end - 2;
+ cond = (const struct asm_cond *) hash_find_n (arm_cond_hsh, affix, 2);
+ opcode = (const struct asm_opcode *) hash_find_n (arm_ops_hsh, base,
+ affix - base);
+ if (opcode && cond)
+ {
+ /* step CE */
+ switch (opcode->tag)
+ {
+ case OT_cinfix3_legacy:
+ /* Ignore conditional suffixes matched on infix only mnemonics. */
+ break;
+
+ case OT_cinfix3:
+ case OT_cinfix3_deprecated:
+ case OT_odd_infix_unc:
+ if (!unified_syntax)
+ return 0;
+ /* else fall through */
+
+ case OT_csuffix:
+ case OT_csuffixF:
+ case OT_csuf_or_in3:
+ inst.cond = cond->value;
+ return opcode;
+
+ case OT_unconditional:
+ case OT_unconditionalF:
+ if (thumb_mode)
+ inst.cond = cond->value;
+ else
+ {
+ /* Delayed diagnostic. */
+ inst.error = BAD_COND;
+ inst.cond = COND_ALWAYS;
+ }
+ return opcode;
+
+ default:
+ return NULL;
+ }
+ }
+
+ /* Cannot have a usual-position infix on a mnemonic of less than
+ six characters (five would be a suffix). */
+ if (end - base < 6)
+ return NULL;
+
+ /* Look for infixed mnemonic in the usual position. */
+ affix = base + 3;
+ cond = (const struct asm_cond *) hash_find_n (arm_cond_hsh, affix, 2);
+ if (!cond)
+ return NULL;
+
+ memcpy (save, affix, 2);
+ memmove (affix, affix + 2, (end - affix) - 2);
+ opcode = (const struct asm_opcode *) hash_find_n (arm_ops_hsh, base,
+ (end - base) - 2);
+ memmove (affix + 2, affix, (end - affix) - 2);
+ memcpy (affix, save, 2);
+
+ if (opcode
+ && (opcode->tag == OT_cinfix3
+ || opcode->tag == OT_cinfix3_deprecated
+ || opcode->tag == OT_csuf_or_in3
+ || opcode->tag == OT_cinfix3_legacy))
+ {
+ /* Step CM. */
+ if (warn_on_deprecated && unified_syntax
+ && (opcode->tag == OT_cinfix3
+ || opcode->tag == OT_cinfix3_deprecated))
+ as_warn (_("conditional infixes are deprecated in unified syntax"));
+
+ inst.cond = cond->value;
+ return opcode;
+ }
+
+ return NULL;
+}
+
+/* This function generates an initial IT instruction, leaving its block
+ virtually open for the new instructions. Eventually,
+ the mask will be updated by now_it_add_mask () each time
+ a new instruction needs to be included in the IT block.
+ Finally, the block is closed with close_automatic_it_block ().
+ The block closure can be requested either from md_assemble (),
+ a tencode (), or due to a label hook. */
+
+static void
+new_automatic_it_block (int cond)
+{
+ now_it.state = AUTOMATIC_IT_BLOCK;
+ now_it.mask = 0x18;
+ now_it.cc = cond;
+ now_it.block_length = 1;
+ mapping_state (MAP_THUMB);
+ now_it.insn = output_it_inst (cond, now_it.mask, NULL);
+ now_it.warn_deprecated = FALSE;
+ now_it.insn_cond = TRUE;
+}
+
+/* Close an automatic IT block.
+ See comments in new_automatic_it_block (). */
+
+static void
+close_automatic_it_block (void)
+{
+ now_it.mask = 0x10;
+ now_it.block_length = 0;
+}
+
+/* Update the mask of the current automatically-generated IT
+ instruction. See comments in new_automatic_it_block (). */
+
+static void
+now_it_add_mask (int cond)
+{
+#define CLEAR_BIT(value, nbit) ((value) & ~(1 << (nbit)))
+#define SET_BIT_VALUE(value, bitvalue, nbit) (CLEAR_BIT (value, nbit) \
+ | ((bitvalue) << (nbit)))
+ const int resulting_bit = (cond & 1);
+
+ now_it.mask &= 0xf;
+ now_it.mask = SET_BIT_VALUE (now_it.mask,
+ resulting_bit,
+ (5 - now_it.block_length));
+ now_it.mask = SET_BIT_VALUE (now_it.mask,
+ 1,
+ ((5 - now_it.block_length) - 1) );
+ output_it_inst (now_it.cc, now_it.mask, now_it.insn);
+
+#undef CLEAR_BIT
+#undef SET_BIT_VALUE
+}
+
+/* The IT blocks handling machinery is accessed through the these functions:
+ it_fsm_pre_encode () from md_assemble ()
+ set_it_insn_type () optional, from the tencode functions
+ set_it_insn_type_last () ditto
+ in_it_block () ditto
+ it_fsm_post_encode () from md_assemble ()
+ force_automatic_it_block_close () from label habdling functions
+
+ Rationale:
+ 1) md_assemble () calls it_fsm_pre_encode () before calling tencode (),
+ initializing the IT insn type with a generic initial value depending
+ on the inst.condition.
+ 2) During the tencode function, two things may happen:
+ a) The tencode function overrides the IT insn type by
+ calling either set_it_insn_type (type) or set_it_insn_type_last ().
+ b) The tencode function queries the IT block state by
+ calling in_it_block () (i.e. to determine narrow/not narrow mode).
+
+ Both set_it_insn_type and in_it_block run the internal FSM state
+ handling function (handle_it_state), because: a) setting the IT insn
+ type may incur in an invalid state (exiting the function),
+ and b) querying the state requires the FSM to be updated.
+ Specifically we want to avoid creating an IT block for conditional
+ branches, so it_fsm_pre_encode is actually a guess and we can't
+ determine whether an IT block is required until the tencode () routine
+ has decided what type of instruction this actually it.
+ Because of this, if set_it_insn_type and in_it_block have to be used,
+ set_it_insn_type has to be called first.
+
+ set_it_insn_type_last () is a wrapper of set_it_insn_type (type), that
+ determines the insn IT type depending on the inst.cond code.
+ When a tencode () routine encodes an instruction that can be
+ either outside an IT block, or, in the case of being inside, has to be
+ the last one, set_it_insn_type_last () will determine the proper
+ IT instruction type based on the inst.cond code. Otherwise,
+ set_it_insn_type can be called for overriding that logic or
+ for covering other cases.
+
+ Calling handle_it_state () may not transition the IT block state to
+ OUTSIDE_IT_BLOCK immediatelly, since the (current) state could be
+ still queried. Instead, if the FSM determines that the state should
+ be transitioned to OUTSIDE_IT_BLOCK, a flag is marked to be closed
+ after the tencode () function: that's what it_fsm_post_encode () does.
+
+ Since in_it_block () calls the state handling function to get an
+ updated state, an error may occur (due to invalid insns combination).
+ In that case, inst.error is set.
+ Therefore, inst.error has to be checked after the execution of
+ the tencode () routine.
+
+ 3) Back in md_assemble(), it_fsm_post_encode () is called to commit
+ any pending state change (if any) that didn't take place in
+ handle_it_state () as explained above. */
+
+static void
+it_fsm_pre_encode (void)
+{
+ if (inst.cond != COND_ALWAYS)
+ inst.it_insn_type = INSIDE_IT_INSN;
+ else
+ inst.it_insn_type = OUTSIDE_IT_INSN;
+
+ now_it.state_handled = 0;
+}
+
+/* IT state FSM handling function. */
+
+static int
+handle_it_state (void)
+{
+ now_it.state_handled = 1;
+ now_it.insn_cond = FALSE;
+
+ switch (now_it.state)
+ {
+ case OUTSIDE_IT_BLOCK:
+ switch (inst.it_insn_type)
+ {
+ case OUTSIDE_IT_INSN:
+ break;
+
+ case INSIDE_IT_INSN:
+ case INSIDE_IT_LAST_INSN:
+ if (thumb_mode == 0)
+ {
+ if (unified_syntax
+ && !(implicit_it_mode & IMPLICIT_IT_MODE_ARM))
+ as_tsktsk (_("Warning: conditional outside an IT block"\
+ " for Thumb."));
+ }
+ else
+ {
+ if ((implicit_it_mode & IMPLICIT_IT_MODE_THUMB)
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2))
+ {
+ /* Automatically generate the IT instruction. */
+ new_automatic_it_block (inst.cond);
+ if (inst.it_insn_type == INSIDE_IT_LAST_INSN)
+ close_automatic_it_block ();
+ }
+ else
+ {
+ inst.error = BAD_OUT_IT;
+ return FAIL;
+ }
+ }
+ break;
+
+ case IF_INSIDE_IT_LAST_INSN:
+ case NEUTRAL_IT_INSN:
+ break;
+
+ case IT_INSN:
+ now_it.state = MANUAL_IT_BLOCK;
+ now_it.block_length = 0;
+ break;
+ }
+ break;
+
+ case AUTOMATIC_IT_BLOCK:
+ /* Three things may happen now:
+ a) We should increment current it block size;
+ b) We should close current it block (closing insn or 4 insns);
+ c) We should close current it block and start a new one (due
+ to incompatible conditions or
+ 4 insns-length block reached). */
+
+ switch (inst.it_insn_type)
+ {
+ case OUTSIDE_IT_INSN:
+ /* The closure of the block shall happen immediatelly,
+ so any in_it_block () call reports the block as closed. */
+ force_automatic_it_block_close ();
+ break;
+
+ case INSIDE_IT_INSN:
+ case INSIDE_IT_LAST_INSN:
+ case IF_INSIDE_IT_LAST_INSN:
+ now_it.block_length++;
+
+ if (now_it.block_length > 4
+ || !now_it_compatible (inst.cond))
+ {
+ force_automatic_it_block_close ();
+ if (inst.it_insn_type != IF_INSIDE_IT_LAST_INSN)
+ new_automatic_it_block (inst.cond);
+ }
+ else
+ {
+ now_it.insn_cond = TRUE;
+ now_it_add_mask (inst.cond);
+ }
+
+ if (now_it.state == AUTOMATIC_IT_BLOCK
+ && (inst.it_insn_type == INSIDE_IT_LAST_INSN
+ || inst.it_insn_type == IF_INSIDE_IT_LAST_INSN))
+ close_automatic_it_block ();
+ break;
+
+ case NEUTRAL_IT_INSN:
+ now_it.block_length++;
+ now_it.insn_cond = TRUE;
+
+ if (now_it.block_length > 4)
+ force_automatic_it_block_close ();
+ else
+ now_it_add_mask (now_it.cc & 1);
+ break;
+
+ case IT_INSN:
+ close_automatic_it_block ();
+ now_it.state = MANUAL_IT_BLOCK;
+ break;
+ }
+ break;
+
+ case MANUAL_IT_BLOCK:
+ {
+ /* Check conditional suffixes. */
+ const int cond = now_it.cc ^ ((now_it.mask >> 4) & 1) ^ 1;
+ int is_last;
+ now_it.mask <<= 1;
+ now_it.mask &= 0x1f;
+ is_last = (now_it.mask == 0x10);
+ now_it.insn_cond = TRUE;
+
+ switch (inst.it_insn_type)
+ {
+ case OUTSIDE_IT_INSN:
+ inst.error = BAD_NOT_IT;
+ return FAIL;
+
+ case INSIDE_IT_INSN:
+ if (cond != inst.cond)
+ {
+ inst.error = BAD_IT_COND;
+ return FAIL;
+ }
+ break;
+
+ case INSIDE_IT_LAST_INSN:
+ case IF_INSIDE_IT_LAST_INSN:
+ if (cond != inst.cond)
+ {
+ inst.error = BAD_IT_COND;
+ return FAIL;
+ }
+ if (!is_last)
+ {
+ inst.error = BAD_BRANCH;
+ return FAIL;
+ }
+ break;
+
+ case NEUTRAL_IT_INSN:
+ /* The BKPT instruction is unconditional even in an IT block. */
+ break;
+
+ case IT_INSN:
+ inst.error = BAD_IT_IT;
+ return FAIL;
+ }
+ }
+ break;
+ }
+
+ return SUCCESS;
+}
+
+struct depr_insn_mask
+{
+ unsigned long pattern;
+ unsigned long mask;
+ const char* description;
+};
+
+/* List of 16-bit instruction patterns deprecated in an IT block in
+ ARMv8. */
+static const struct depr_insn_mask depr_it_insns[] = {
+ { 0xc000, 0xc000, N_("Short branches, Undefined, SVC, LDM/STM") },
+ { 0xb000, 0xb000, N_("Miscellaneous 16-bit instructions") },
+ { 0xa000, 0xb800, N_("ADR") },
+ { 0x4800, 0xf800, N_("Literal loads") },
+ { 0x4478, 0xf478, N_("Hi-register ADD, MOV, CMP, BX, BLX using pc") },
+ { 0x4487, 0xfc87, N_("Hi-register ADD, MOV, CMP using pc") },
+ /* NOTE: 0x00dd is not the real encoding, instead, it is the 'tvalue'
+ field in asm_opcode. 'tvalue' is used at the stage this check happen. */
+ { 0x00dd, 0x7fff, N_("ADD/SUB sp, sp #imm") },
+ { 0, 0, NULL }
+};
+
+static void
+it_fsm_post_encode (void)
+{
+ int is_last;
+
+ if (!now_it.state_handled)
+ handle_it_state ();
+
+ if (now_it.insn_cond
+ && !now_it.warn_deprecated
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ {
+ if (inst.instruction >= 0x10000)
+ {
+ as_warn (_("IT blocks containing 32-bit Thumb instructions are "
+ "deprecated in ARMv8"));
+ now_it.warn_deprecated = TRUE;
+ }
+ else
+ {
+ const struct depr_insn_mask *p = depr_it_insns;
+
+ while (p->mask != 0)
+ {
+ if ((inst.instruction & p->mask) == p->pattern)
+ {
+ as_warn (_("IT blocks containing 16-bit Thumb instructions "
+ "of the following class are deprecated in ARMv8: "
+ "%s"), p->description);
+ now_it.warn_deprecated = TRUE;
+ break;
+ }
+
+ ++p;
+ }
+ }
+
+ if (now_it.block_length > 1)
+ {
+ as_warn (_("IT blocks containing more than one conditional "
+ "instruction are deprecated in ARMv8"));
+ now_it.warn_deprecated = TRUE;
+ }
+ }
+
+ is_last = (now_it.mask == 0x10);
+ if (is_last)
+ {
+ now_it.state = OUTSIDE_IT_BLOCK;
+ now_it.mask = 0;
+ }
+}
+
+static void
+force_automatic_it_block_close (void)
+{
+ if (now_it.state == AUTOMATIC_IT_BLOCK)
+ {
+ close_automatic_it_block ();
+ now_it.state = OUTSIDE_IT_BLOCK;
+ now_it.mask = 0;
+ }
+}
+
+static int
+in_it_block (void)
+{
+ if (!now_it.state_handled)
+ handle_it_state ();
+
+ return now_it.state != OUTSIDE_IT_BLOCK;
+}
+
+void
+md_assemble (char *str)
+{
+ char *p = str;
+ const struct asm_opcode * opcode;
+
+ /* Align the previous label if needed. */
+ if (last_label_seen != NULL)
+ {
+ symbol_set_frag (last_label_seen, frag_now);
+ S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
+ S_SET_SEGMENT (last_label_seen, now_seg);
+ }
+
+ memset (&inst, '\0', sizeof (inst));
+ inst.reloc.type = BFD_RELOC_UNUSED;
+
+ opcode = opcode_lookup (&p);
+ if (!opcode)
+ {
+ /* It wasn't an instruction, but it might be a register alias of
+ the form alias .req reg, or a Neon .dn/.qn directive. */
+ if (! create_register_alias (str, p)
+ && ! create_neon_reg_alias (str, p))
+ as_bad (_("bad instruction `%s'"), str);
+
+ return;
+ }
+
+ if (warn_on_deprecated && opcode->tag == OT_cinfix3_deprecated)
+ as_warn (_("s suffix on comparison instruction is deprecated"));
+
+ /* The value which unconditional instructions should have in place of the
+ condition field. */
+ inst.uncond_value = (opcode->tag == OT_csuffixF) ? 0xf : -1;
+
+ if (thumb_mode)
+ {
+ arm_feature_set variant;
+
+ variant = cpu_variant;
+ /* Only allow coprocessor instructions on Thumb-2 capable devices. */
+ if (!ARM_CPU_HAS_FEATURE (variant, arm_arch_t2))
+ ARM_CLEAR_FEATURE (variant, variant, fpu_any_hard);
+ /* Check that this instruction is supported for this CPU. */
+ if (!opcode->tvariant
+ || (thumb_mode == 1
+ && !ARM_CPU_HAS_FEATURE (variant, *opcode->tvariant)))
+ {
+ as_bad (_("selected processor does not support Thumb mode `%s'"), str);
+ return;
+ }
+ if (inst.cond != COND_ALWAYS && !unified_syntax
+ && opcode->tencode != do_t_branch)
+ {
+ as_bad (_("Thumb does not support conditional execution"));
+ return;
+ }
+
+ if (!ARM_CPU_HAS_FEATURE (variant, arm_ext_v6t2))
+ {
+ if (opcode->tencode != do_t_blx && opcode->tencode != do_t_branch23
+ && !(ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_msr)
+ || ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_barrier)))
+ {
+ /* Two things are addressed here.
+ 1) Implicit require narrow instructions on Thumb-1.
+ This avoids relaxation accidentally introducing Thumb-2
+ instructions.
+ 2) Reject wide instructions in non Thumb-2 cores. */
+ if (inst.size_req == 0)
+ inst.size_req = 2;
+ else if (inst.size_req == 4)
+ {
+ as_bad (_("selected processor does not support Thumb-2 mode `%s'"), str);
+ return;
+ }
+ }
+ }
+
+ inst.instruction = opcode->tvalue;
+
+ if (!parse_operands (p, opcode->operands, /*thumb=*/TRUE))
+ {
+ /* Prepare the it_insn_type for those encodings that don't set
+ it. */
+ it_fsm_pre_encode ();
+
+ opcode->tencode ();
+
+ it_fsm_post_encode ();
+ }
+
+ if (!(inst.error || inst.relax))
+ {
+ gas_assert (inst.instruction < 0xe800 || inst.instruction > 0xffff);
+ inst.size = (inst.instruction > 0xffff ? 4 : 2);
+ if (inst.size_req && inst.size_req != inst.size)
+ {
+ as_bad (_("cannot honor width suffix -- `%s'"), str);
+ return;
+ }
+ }
+
+ /* Something has gone badly wrong if we try to relax a fixed size
+ instruction. */
+ gas_assert (inst.size_req == 0 || !inst.relax);
+
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ *opcode->tvariant);
+ /* Many Thumb-2 instructions also have Thumb-1 variants, so explicitly
+ set those bits when Thumb-2 32-bit instructions are seen. ie.
+ anything other than bl/blx and v6-M instructions.
+ This is overly pessimistic for relaxable instructions. */
+ if (((inst.size == 4 && (inst.instruction & 0xf800e800) != 0xf000e800)
+ || inst.relax)
+ && !(ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_msr)
+ || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_barrier)))
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ arm_ext_v6t2);
+
+ check_neon_suffixes;
+
+ if (!inst.error)
+ {
+ mapping_state (MAP_THUMB);
+ }
+ }
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1))
+ {
+ bfd_boolean is_bx;
+
+ /* bx is allowed on v5 cores, and sometimes on v4 cores. */
+ is_bx = (opcode->aencode == do_bx);
+
+ /* Check that this instruction is supported for this CPU. */
+ if (!(is_bx && fix_v4bx)
+ && !(opcode->avariant &&
+ ARM_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant)))
+ {
+ as_bad (_("selected processor does not support ARM mode `%s'"), str);
+ return;
+ }
+ if (inst.size_req)
+ {
+ as_bad (_("width suffixes are invalid in ARM mode -- `%s'"), str);
+ return;
+ }
+
+ inst.instruction = opcode->avalue;
+ if (opcode->tag == OT_unconditionalF)
+ inst.instruction |= 0xF << 28;
+ else
+ inst.instruction |= inst.cond << 28;
+ inst.size = INSN_SIZE;
+ if (!parse_operands (p, opcode->operands, /*thumb=*/FALSE))
+ {
+ it_fsm_pre_encode ();
+ opcode->aencode ();
+ it_fsm_post_encode ();
+ }
+ /* Arm mode bx is marked as both v4T and v5 because it's still required
+ on a hypothetical non-thumb v5 core. */
+ if (is_bx)
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, arm_ext_v4t);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
+ *opcode->avariant);
+
+ check_neon_suffixes;
+
+ if (!inst.error)
+ {
+ mapping_state (MAP_ARM);
+ }
+ }
+ else
+ {
+ as_bad (_("attempt to use an ARM instruction on a Thumb-only processor "
+ "-- `%s'"), str);
+ return;
+ }
+ output_inst (str);
+}
+
+static void
+check_it_blocks_finished (void)
+{
+#ifdef OBJ_ELF
+ asection *sect;
+
+ for (sect = stdoutput->sections; sect != NULL; sect = sect->next)
+ if (seg_info (sect)->tc_segment_info_data.current_it.state
+ == MANUAL_IT_BLOCK)
+ {
+ as_warn (_("section '%s' finished with an open IT block."),
+ sect->name);
+ }
+#else
+ if (now_it.state == MANUAL_IT_BLOCK)
+ as_warn (_("file finished with an open IT block."));
+#endif
+}
+
+/* Various frobbings of labels and their addresses. */
+
+void
+arm_start_line_hook (void)
+{
+ last_label_seen = NULL;
+}
+
+void
+arm_frob_label (symbolS * sym)
+{
+ last_label_seen = sym;
+
+ ARM_SET_THUMB (sym, thumb_mode);
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ ARM_SET_INTERWORK (sym, support_interwork);
+#endif
+
+ force_automatic_it_block_close ();
+
+ /* Note - do not allow local symbols (.Lxxx) to be labelled
+ as Thumb functions. This is because these labels, whilst
+ they exist inside Thumb code, are not the entry points for
+ possible ARM->Thumb calls. Also, these labels can be used
+ as part of a computed goto or switch statement. eg gcc
+ can generate code that looks like this:
+
+ ldr r2, [pc, .Laaa]
+ lsl r3, r3, #2
+ ldr r2, [r3, r2]
+ mov pc, r2
+
+ .Lbbb: .word .Lxxx
+ .Lccc: .word .Lyyy
+ ..etc...
+ .Laaa: .word Lbbb
+
+ The first instruction loads the address of the jump table.
+ The second instruction converts a table index into a byte offset.
+ The third instruction gets the jump address out of the table.
+ The fourth instruction performs the jump.
+
+ If the address stored at .Laaa is that of a symbol which has the
+ Thumb_Func bit set, then the linker will arrange for this address
+ to have the bottom bit set, which in turn would mean that the
+ address computation performed by the third instruction would end
+ up with the bottom bit set. Since the ARM is capable of unaligned
+ word loads, the instruction would then load the incorrect address
+ out of the jump table, and chaos would ensue. */
+ if (label_is_thumb_function_name
+ && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ {
+ /* When the address of a Thumb function is taken the bottom
+ bit of that address should be set. This will allow
+ interworking between Arm and Thumb functions to work
+ correctly. */
+
+ THUMB_SET_FUNC (sym, 1);
+
+ label_is_thumb_function_name = FALSE;
+ }
+
+ dwarf2_emit_label (sym);
+}
+
+bfd_boolean
+arm_data_in_code (void)
+{
+ if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
+ {
+ *input_line_pointer = '/';
+ input_line_pointer += 5;
+ *input_line_pointer = 0;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+char *
+arm_canonicalize_symbol_name (char * name)
+{
+ int len;
+
+ if (thumb_mode && (len = strlen (name)) > 5
+ && streq (name + len - 5, "/data"))
+ *(name + len - 5) = 0;
+
+ return name;
+}
+
+/* Table of all register names defined by default. The user can
+ define additional names with .req. Note that all register names
+ should appear in both upper and lowercase variants. Some registers
+ also have mixed-case names. */
+
+#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE, 0 }
+#define REGNUM(p,n,t) REGDEF(p##n, n, t)
+#define REGNUM2(p,n,t) REGDEF(p##n, 2 * n, t)
+#define REGSET(p,t) \
+ REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \
+ REGNUM(p, 4,t), REGNUM(p, 5,t), REGNUM(p, 6,t), REGNUM(p, 7,t), \
+ REGNUM(p, 8,t), REGNUM(p, 9,t), REGNUM(p,10,t), REGNUM(p,11,t), \
+ REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t)
+#define REGSETH(p,t) \
+ REGNUM(p,16,t), REGNUM(p,17,t), REGNUM(p,18,t), REGNUM(p,19,t), \
+ REGNUM(p,20,t), REGNUM(p,21,t), REGNUM(p,22,t), REGNUM(p,23,t), \
+ REGNUM(p,24,t), REGNUM(p,25,t), REGNUM(p,26,t), REGNUM(p,27,t), \
+ REGNUM(p,28,t), REGNUM(p,29,t), REGNUM(p,30,t), REGNUM(p,31,t)
+#define REGSET2(p,t) \
+ REGNUM2(p, 0,t), REGNUM2(p, 1,t), REGNUM2(p, 2,t), REGNUM2(p, 3,t), \
+ REGNUM2(p, 4,t), REGNUM2(p, 5,t), REGNUM2(p, 6,t), REGNUM2(p, 7,t), \
+ REGNUM2(p, 8,t), REGNUM2(p, 9,t), REGNUM2(p,10,t), REGNUM2(p,11,t), \
+ REGNUM2(p,12,t), REGNUM2(p,13,t), REGNUM2(p,14,t), REGNUM2(p,15,t)
+#define SPLRBANK(base,bank,t) \
+ REGDEF(lr_##bank, 768|((base+0)<<16), t), \
+ REGDEF(sp_##bank, 768|((base+1)<<16), t), \
+ REGDEF(spsr_##bank, 768|(base<<16)|SPSR_BIT, t), \
+ REGDEF(LR_##bank, 768|((base+0)<<16), t), \
+ REGDEF(SP_##bank, 768|((base+1)<<16), t), \
+ REGDEF(SPSR_##bank, 768|(base<<16)|SPSR_BIT, t)
+
+static const struct reg_entry reg_names[] =
+{
+ /* ARM integer registers. */
+ REGSET(r, RN), REGSET(R, RN),
+
+ /* ATPCS synonyms. */
+ REGDEF(a1,0,RN), REGDEF(a2,1,RN), REGDEF(a3, 2,RN), REGDEF(a4, 3,RN),
+ REGDEF(v1,4,RN), REGDEF(v2,5,RN), REGDEF(v3, 6,RN), REGDEF(v4, 7,RN),
+ REGDEF(v5,8,RN), REGDEF(v6,9,RN), REGDEF(v7,10,RN), REGDEF(v8,11,RN),
+
+ REGDEF(A1,0,RN), REGDEF(A2,1,RN), REGDEF(A3, 2,RN), REGDEF(A4, 3,RN),
+ REGDEF(V1,4,RN), REGDEF(V2,5,RN), REGDEF(V3, 6,RN), REGDEF(V4, 7,RN),
+ REGDEF(V5,8,RN), REGDEF(V6,9,RN), REGDEF(V7,10,RN), REGDEF(V8,11,RN),
+
+ /* Well-known aliases. */
+ REGDEF(wr, 7,RN), REGDEF(sb, 9,RN), REGDEF(sl,10,RN), REGDEF(fp,11,RN),
+ REGDEF(ip,12,RN), REGDEF(sp,13,RN), REGDEF(lr,14,RN), REGDEF(pc,15,RN),
+
+ REGDEF(WR, 7,RN), REGDEF(SB, 9,RN), REGDEF(SL,10,RN), REGDEF(FP,11,RN),
+ REGDEF(IP,12,RN), REGDEF(SP,13,RN), REGDEF(LR,14,RN), REGDEF(PC,15,RN),
+
+ /* Coprocessor numbers. */
+ REGSET(p, CP), REGSET(P, CP),
+
+ /* Coprocessor register numbers. The "cr" variants are for backward
+ compatibility. */
+ REGSET(c, CN), REGSET(C, CN),
+ REGSET(cr, CN), REGSET(CR, CN),
+
+ /* ARM banked registers. */
+ REGDEF(R8_usr,512|(0<<16),RNB), REGDEF(r8_usr,512|(0<<16),RNB),
+ REGDEF(R9_usr,512|(1<<16),RNB), REGDEF(r9_usr,512|(1<<16),RNB),
+ REGDEF(R10_usr,512|(2<<16),RNB), REGDEF(r10_usr,512|(2<<16),RNB),
+ REGDEF(R11_usr,512|(3<<16),RNB), REGDEF(r11_usr,512|(3<<16),RNB),
+ REGDEF(R12_usr,512|(4<<16),RNB), REGDEF(r12_usr,512|(4<<16),RNB),
+ REGDEF(SP_usr,512|(5<<16),RNB), REGDEF(sp_usr,512|(5<<16),RNB),
+ REGDEF(LR_usr,512|(6<<16),RNB), REGDEF(lr_usr,512|(6<<16),RNB),
+
+ REGDEF(R8_fiq,512|(8<<16),RNB), REGDEF(r8_fiq,512|(8<<16),RNB),
+ REGDEF(R9_fiq,512|(9<<16),RNB), REGDEF(r9_fiq,512|(9<<16),RNB),
+ REGDEF(R10_fiq,512|(10<<16),RNB), REGDEF(r10_fiq,512|(10<<16),RNB),
+ REGDEF(R11_fiq,512|(11<<16),RNB), REGDEF(r11_fiq,512|(11<<16),RNB),
+ REGDEF(R12_fiq,512|(12<<16),RNB), REGDEF(r12_fiq,512|(12<<16),RNB),
+ REGDEF(SP_fiq,512|(13<<16),RNB), REGDEF(sp_fiq,512|(13<<16),RNB),
+ REGDEF(LR_fiq,512|(14<<16),RNB), REGDEF(lr_fiq,512|(14<<16),RNB),
+ REGDEF(SPSR_fiq,512|(14<<16)|SPSR_BIT,RNB), REGDEF(spsr_fiq,512|(14<<16)|SPSR_BIT,RNB),
+
+ SPLRBANK(0,IRQ,RNB), SPLRBANK(0,irq,RNB),
+ SPLRBANK(2,SVC,RNB), SPLRBANK(2,svc,RNB),
+ SPLRBANK(4,ABT,RNB), SPLRBANK(4,abt,RNB),
+ SPLRBANK(6,UND,RNB), SPLRBANK(6,und,RNB),
+ SPLRBANK(12,MON,RNB), SPLRBANK(12,mon,RNB),
+ REGDEF(elr_hyp,768|(14<<16),RNB), REGDEF(ELR_hyp,768|(14<<16),RNB),
+ REGDEF(sp_hyp,768|(15<<16),RNB), REGDEF(SP_hyp,768|(15<<16),RNB),
+ REGDEF(spsr_hyp,768|(14<<16)|SPSR_BIT,RNB),
+ REGDEF(SPSR_hyp,768|(14<<16)|SPSR_BIT,RNB),
+
+ /* FPA registers. */
+ REGNUM(f,0,FN), REGNUM(f,1,FN), REGNUM(f,2,FN), REGNUM(f,3,FN),
+ REGNUM(f,4,FN), REGNUM(f,5,FN), REGNUM(f,6,FN), REGNUM(f,7, FN),
+
+ REGNUM(F,0,FN), REGNUM(F,1,FN), REGNUM(F,2,FN), REGNUM(F,3,FN),
+ REGNUM(F,4,FN), REGNUM(F,5,FN), REGNUM(F,6,FN), REGNUM(F,7, FN),
+
+ /* VFP SP registers. */
+ REGSET(s,VFS), REGSET(S,VFS),
+ REGSETH(s,VFS), REGSETH(S,VFS),
+
+ /* VFP DP Registers. */
+ REGSET(d,VFD), REGSET(D,VFD),
+ /* Extra Neon DP registers. */
+ REGSETH(d,VFD), REGSETH(D,VFD),
+
+ /* Neon QP registers. */
+ REGSET2(q,NQ), REGSET2(Q,NQ),
+
+ /* VFP control registers. */
+ REGDEF(fpsid,0,VFC), REGDEF(fpscr,1,VFC), REGDEF(fpexc,8,VFC),
+ REGDEF(FPSID,0,VFC), REGDEF(FPSCR,1,VFC), REGDEF(FPEXC,8,VFC),
+ REGDEF(fpinst,9,VFC), REGDEF(fpinst2,10,VFC),
+ REGDEF(FPINST,9,VFC), REGDEF(FPINST2,10,VFC),
+ REGDEF(mvfr0,7,VFC), REGDEF(mvfr1,6,VFC),
+ REGDEF(MVFR0,7,VFC), REGDEF(MVFR1,6,VFC),
+
+ /* Maverick DSP coprocessor registers. */
+ REGSET(mvf,MVF), REGSET(mvd,MVD), REGSET(mvfx,MVFX), REGSET(mvdx,MVDX),
+ REGSET(MVF,MVF), REGSET(MVD,MVD), REGSET(MVFX,MVFX), REGSET(MVDX,MVDX),
+
+ REGNUM(mvax,0,MVAX), REGNUM(mvax,1,MVAX),
+ REGNUM(mvax,2,MVAX), REGNUM(mvax,3,MVAX),
+ REGDEF(dspsc,0,DSPSC),
+
+ REGNUM(MVAX,0,MVAX), REGNUM(MVAX,1,MVAX),
+ REGNUM(MVAX,2,MVAX), REGNUM(MVAX,3,MVAX),
+ REGDEF(DSPSC,0,DSPSC),
+
+ /* iWMMXt data registers - p0, c0-15. */
+ REGSET(wr,MMXWR), REGSET(wR,MMXWR), REGSET(WR, MMXWR),
+
+ /* iWMMXt control registers - p1, c0-3. */
+ REGDEF(wcid, 0,MMXWC), REGDEF(wCID, 0,MMXWC), REGDEF(WCID, 0,MMXWC),
+ REGDEF(wcon, 1,MMXWC), REGDEF(wCon, 1,MMXWC), REGDEF(WCON, 1,MMXWC),
+ REGDEF(wcssf, 2,MMXWC), REGDEF(wCSSF, 2,MMXWC), REGDEF(WCSSF, 2,MMXWC),
+ REGDEF(wcasf, 3,MMXWC), REGDEF(wCASF, 3,MMXWC), REGDEF(WCASF, 3,MMXWC),
+
+ /* iWMMXt scalar (constant/offset) registers - p1, c8-11. */
+ REGDEF(wcgr0, 8,MMXWCG), REGDEF(wCGR0, 8,MMXWCG), REGDEF(WCGR0, 8,MMXWCG),
+ REGDEF(wcgr1, 9,MMXWCG), REGDEF(wCGR1, 9,MMXWCG), REGDEF(WCGR1, 9,MMXWCG),
+ REGDEF(wcgr2,10,MMXWCG), REGDEF(wCGR2,10,MMXWCG), REGDEF(WCGR2,10,MMXWCG),
+ REGDEF(wcgr3,11,MMXWCG), REGDEF(wCGR3,11,MMXWCG), REGDEF(WCGR3,11,MMXWCG),
+
+ /* XScale accumulator registers. */
+ REGNUM(acc,0,XSCALE), REGNUM(ACC,0,XSCALE),
+};
+#undef REGDEF
+#undef REGNUM
+#undef REGSET
+
+/* Table of all PSR suffixes. Bare "CPSR" and "SPSR" are handled
+ within psr_required_here. */
+static const struct asm_psr psrs[] =
+{
+ /* Backward compatibility notation. Note that "all" is no longer
+ truly all possible PSR bits. */
+ {"all", PSR_c | PSR_f},
+ {"flg", PSR_f},
+ {"ctl", PSR_c},
+
+ /* Individual flags. */
+ {"f", PSR_f},
+ {"c", PSR_c},
+ {"x", PSR_x},
+ {"s", PSR_s},
+
+ /* Combinations of flags. */
+ {"fs", PSR_f | PSR_s},
+ {"fx", PSR_f | PSR_x},
+ {"fc", PSR_f | PSR_c},
+ {"sf", PSR_s | PSR_f},
+ {"sx", PSR_s | PSR_x},
+ {"sc", PSR_s | PSR_c},
+ {"xf", PSR_x | PSR_f},
+ {"xs", PSR_x | PSR_s},
+ {"xc", PSR_x | PSR_c},
+ {"cf", PSR_c | PSR_f},
+ {"cs", PSR_c | PSR_s},
+ {"cx", PSR_c | PSR_x},
+ {"fsx", PSR_f | PSR_s | PSR_x},
+ {"fsc", PSR_f | PSR_s | PSR_c},
+ {"fxs", PSR_f | PSR_x | PSR_s},
+ {"fxc", PSR_f | PSR_x | PSR_c},
+ {"fcs", PSR_f | PSR_c | PSR_s},
+ {"fcx", PSR_f | PSR_c | PSR_x},
+ {"sfx", PSR_s | PSR_f | PSR_x},
+ {"sfc", PSR_s | PSR_f | PSR_c},
+ {"sxf", PSR_s | PSR_x | PSR_f},
+ {"sxc", PSR_s | PSR_x | PSR_c},
+ {"scf", PSR_s | PSR_c | PSR_f},
+ {"scx", PSR_s | PSR_c | PSR_x},
+ {"xfs", PSR_x | PSR_f | PSR_s},
+ {"xfc", PSR_x | PSR_f | PSR_c},
+ {"xsf", PSR_x | PSR_s | PSR_f},
+ {"xsc", PSR_x | PSR_s | PSR_c},
+ {"xcf", PSR_x | PSR_c | PSR_f},
+ {"xcs", PSR_x | PSR_c | PSR_s},
+ {"cfs", PSR_c | PSR_f | PSR_s},
+ {"cfx", PSR_c | PSR_f | PSR_x},
+ {"csf", PSR_c | PSR_s | PSR_f},
+ {"csx", PSR_c | PSR_s | PSR_x},
+ {"cxf", PSR_c | PSR_x | PSR_f},
+ {"cxs", PSR_c | PSR_x | PSR_s},
+ {"fsxc", PSR_f | PSR_s | PSR_x | PSR_c},
+ {"fscx", PSR_f | PSR_s | PSR_c | PSR_x},
+ {"fxsc", PSR_f | PSR_x | PSR_s | PSR_c},
+ {"fxcs", PSR_f | PSR_x | PSR_c | PSR_s},
+ {"fcsx", PSR_f | PSR_c | PSR_s | PSR_x},
+ {"fcxs", PSR_f | PSR_c | PSR_x | PSR_s},
+ {"sfxc", PSR_s | PSR_f | PSR_x | PSR_c},
+ {"sfcx", PSR_s | PSR_f | PSR_c | PSR_x},
+ {"sxfc", PSR_s | PSR_x | PSR_f | PSR_c},
+ {"sxcf", PSR_s | PSR_x | PSR_c | PSR_f},
+ {"scfx", PSR_s | PSR_c | PSR_f | PSR_x},
+ {"scxf", PSR_s | PSR_c | PSR_x | PSR_f},
+ {"xfsc", PSR_x | PSR_f | PSR_s | PSR_c},
+ {"xfcs", PSR_x | PSR_f | PSR_c | PSR_s},
+ {"xsfc", PSR_x | PSR_s | PSR_f | PSR_c},
+ {"xscf", PSR_x | PSR_s | PSR_c | PSR_f},
+ {"xcfs", PSR_x | PSR_c | PSR_f | PSR_s},
+ {"xcsf", PSR_x | PSR_c | PSR_s | PSR_f},
+ {"cfsx", PSR_c | PSR_f | PSR_s | PSR_x},
+ {"cfxs", PSR_c | PSR_f | PSR_x | PSR_s},
+ {"csfx", PSR_c | PSR_s | PSR_f | PSR_x},
+ {"csxf", PSR_c | PSR_s | PSR_x | PSR_f},
+ {"cxfs", PSR_c | PSR_x | PSR_f | PSR_s},
+ {"cxsf", PSR_c | PSR_x | PSR_s | PSR_f},
+};
+
+/* Table of V7M psr names. */
+static const struct asm_psr v7m_psrs[] =
+{
+ {"apsr", 0 }, {"APSR", 0 },
+ {"iapsr", 1 }, {"IAPSR", 1 },
+ {"eapsr", 2 }, {"EAPSR", 2 },
+ {"psr", 3 }, {"PSR", 3 },
+ {"xpsr", 3 }, {"XPSR", 3 }, {"xPSR", 3 },
+ {"ipsr", 5 }, {"IPSR", 5 },
+ {"epsr", 6 }, {"EPSR", 6 },
+ {"iepsr", 7 }, {"IEPSR", 7 },
+ {"msp", 8 }, {"MSP", 8 },
+ {"psp", 9 }, {"PSP", 9 },
+ {"primask", 16}, {"PRIMASK", 16},
+ {"basepri", 17}, {"BASEPRI", 17},
+ {"basepri_max", 18}, {"BASEPRI_MAX", 18},
+ {"basepri_max", 18}, {"BASEPRI_MASK", 18}, /* Typo, preserved for backwards compatibility. */
+ {"faultmask", 19}, {"FAULTMASK", 19},
+ {"control", 20}, {"CONTROL", 20}
+};
+
+/* Table of all shift-in-operand names. */
+static const struct asm_shift_name shift_names [] =
+{
+ { "asl", SHIFT_LSL }, { "ASL", SHIFT_LSL },
+ { "lsl", SHIFT_LSL }, { "LSL", SHIFT_LSL },
+ { "lsr", SHIFT_LSR }, { "LSR", SHIFT_LSR },
+ { "asr", SHIFT_ASR }, { "ASR", SHIFT_ASR },
+ { "ror", SHIFT_ROR }, { "ROR", SHIFT_ROR },
+ { "rrx", SHIFT_RRX }, { "RRX", SHIFT_RRX }
+};
+
+/* Table of all explicit relocation names. */
+#ifdef OBJ_ELF
+static struct reloc_entry reloc_names[] =
+{
+ { "got", BFD_RELOC_ARM_GOT32 }, { "GOT", BFD_RELOC_ARM_GOT32 },
+ { "gotoff", BFD_RELOC_ARM_GOTOFF }, { "GOTOFF", BFD_RELOC_ARM_GOTOFF },
+ { "plt", BFD_RELOC_ARM_PLT32 }, { "PLT", BFD_RELOC_ARM_PLT32 },
+ { "target1", BFD_RELOC_ARM_TARGET1 }, { "TARGET1", BFD_RELOC_ARM_TARGET1 },
+ { "target2", BFD_RELOC_ARM_TARGET2 }, { "TARGET2", BFD_RELOC_ARM_TARGET2 },
+ { "sbrel", BFD_RELOC_ARM_SBREL32 }, { "SBREL", BFD_RELOC_ARM_SBREL32 },
+ { "tlsgd", BFD_RELOC_ARM_TLS_GD32}, { "TLSGD", BFD_RELOC_ARM_TLS_GD32},
+ { "tlsldm", BFD_RELOC_ARM_TLS_LDM32}, { "TLSLDM", BFD_RELOC_ARM_TLS_LDM32},
+ { "tlsldo", BFD_RELOC_ARM_TLS_LDO32}, { "TLSLDO", BFD_RELOC_ARM_TLS_LDO32},
+ { "gottpoff",BFD_RELOC_ARM_TLS_IE32}, { "GOTTPOFF",BFD_RELOC_ARM_TLS_IE32},
+ { "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32},
+ { "got_prel", BFD_RELOC_ARM_GOT_PREL}, { "GOT_PREL", BFD_RELOC_ARM_GOT_PREL},
+ { "tlsdesc", BFD_RELOC_ARM_TLS_GOTDESC},
+ { "TLSDESC", BFD_RELOC_ARM_TLS_GOTDESC},
+ { "tlscall", BFD_RELOC_ARM_TLS_CALL},
+ { "TLSCALL", BFD_RELOC_ARM_TLS_CALL},
+ { "tlsdescseq", BFD_RELOC_ARM_TLS_DESCSEQ},
+ { "TLSDESCSEQ", BFD_RELOC_ARM_TLS_DESCSEQ}
+};
+#endif
+
+/* Table of all conditional affixes. 0xF is not defined as a condition code. */
+static const struct asm_cond conds[] =
+{
+ {"eq", 0x0},
+ {"ne", 0x1},
+ {"cs", 0x2}, {"hs", 0x2},
+ {"cc", 0x3}, {"ul", 0x3}, {"lo", 0x3},
+ {"mi", 0x4},
+ {"pl", 0x5},
+ {"vs", 0x6},
+ {"vc", 0x7},
+ {"hi", 0x8},
+ {"ls", 0x9},
+ {"ge", 0xa},
+ {"lt", 0xb},
+ {"gt", 0xc},
+ {"le", 0xd},
+ {"al", 0xe}
+};
+
+#define UL_BARRIER(L,U,CODE,FEAT) \
+ { L, CODE, ARM_FEATURE (FEAT, 0) }, \
+ { U, CODE, ARM_FEATURE (FEAT, 0) }
+
+static struct asm_barrier_opt barrier_opt_names[] =
+{
+ UL_BARRIER ("sy", "SY", 0xf, ARM_EXT_BARRIER),
+ UL_BARRIER ("st", "ST", 0xe, ARM_EXT_BARRIER),
+ UL_BARRIER ("ld", "LD", 0xd, ARM_EXT_V8),
+ UL_BARRIER ("ish", "ISH", 0xb, ARM_EXT_BARRIER),
+ UL_BARRIER ("sh", "SH", 0xb, ARM_EXT_BARRIER),
+ UL_BARRIER ("ishst", "ISHST", 0xa, ARM_EXT_BARRIER),
+ UL_BARRIER ("shst", "SHST", 0xa, ARM_EXT_BARRIER),
+ UL_BARRIER ("ishld", "ISHLD", 0x9, ARM_EXT_V8),
+ UL_BARRIER ("un", "UN", 0x7, ARM_EXT_BARRIER),
+ UL_BARRIER ("nsh", "NSH", 0x7, ARM_EXT_BARRIER),
+ UL_BARRIER ("unst", "UNST", 0x6, ARM_EXT_BARRIER),
+ UL_BARRIER ("nshst", "NSHST", 0x6, ARM_EXT_BARRIER),
+ UL_BARRIER ("nshld", "NSHLD", 0x5, ARM_EXT_V8),
+ UL_BARRIER ("osh", "OSH", 0x3, ARM_EXT_BARRIER),
+ UL_BARRIER ("oshst", "OSHST", 0x2, ARM_EXT_BARRIER),
+ UL_BARRIER ("oshld", "OSHLD", 0x1, ARM_EXT_V8)
+};
+
+#undef UL_BARRIER
+
+/* Table of ARM-format instructions. */
+
+/* Macros for gluing together operand strings. N.B. In all cases
+ other than OPS0, the trailing OP_stop comes from default
+ zero-initialization of the unspecified elements of the array. */
+#define OPS0() { OP_stop, }
+#define OPS1(a) { OP_##a, }
+#define OPS2(a,b) { OP_##a,OP_##b, }
+#define OPS3(a,b,c) { OP_##a,OP_##b,OP_##c, }
+#define OPS4(a,b,c,d) { OP_##a,OP_##b,OP_##c,OP_##d, }
+#define OPS5(a,b,c,d,e) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e, }
+#define OPS6(a,b,c,d,e,f) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e,OP_##f, }
+
+/* These macros are similar to the OPSn, but do not prepend the OP_ prefix.
+ This is useful when mixing operands for ARM and THUMB, i.e. using the
+ MIX_ARM_THUMB_OPERANDS macro.
+ In order to use these macros, prefix the number of operands with _
+ e.g. _3. */
+#define OPS_1(a) { a, }
+#define OPS_2(a,b) { a,b, }
+#define OPS_3(a,b,c) { a,b,c, }
+#define OPS_4(a,b,c,d) { a,b,c,d, }
+#define OPS_5(a,b,c,d,e) { a,b,c,d,e, }
+#define OPS_6(a,b,c,d,e,f) { a,b,c,d,e,f, }
+
+/* These macros abstract out the exact format of the mnemonic table and
+ save some repeated characters. */
+
+/* The normal sort of mnemonic; has a Thumb variant; takes a conditional suffix. */
+#define TxCE(mnem, op, top, nops, ops, ae, te) \
+ { mnem, OPS##nops ops, OT_csuffix, 0x##op, top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##ae, do_##te }
+
+/* Two variants of the above - TCE for a numeric Thumb opcode, tCE for
+ a T_MNEM_xyz enumerator. */
+#define TCE(mnem, aop, top, nops, ops, ae, te) \
+ TxCE (mnem, aop, 0x##top, nops, ops, ae, te)
+#define tCE(mnem, aop, top, nops, ops, ae, te) \
+ TxCE (mnem, aop, T_MNEM##top, nops, ops, ae, te)
+
+/* Second most common sort of mnemonic: has a Thumb variant, takes a conditional
+ infix after the third character. */
+#define TxC3(mnem, op, top, nops, ops, ae, te) \
+ { mnem, OPS##nops ops, OT_cinfix3, 0x##op, top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##ae, do_##te }
+#define TxC3w(mnem, op, top, nops, ops, ae, te) \
+ { mnem, OPS##nops ops, OT_cinfix3_deprecated, 0x##op, top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##ae, do_##te }
+#define TC3(mnem, aop, top, nops, ops, ae, te) \
+ TxC3 (mnem, aop, 0x##top, nops, ops, ae, te)
+#define TC3w(mnem, aop, top, nops, ops, ae, te) \
+ TxC3w (mnem, aop, 0x##top, nops, ops, ae, te)
+#define tC3(mnem, aop, top, nops, ops, ae, te) \
+ TxC3 (mnem, aop, T_MNEM##top, nops, ops, ae, te)
+#define tC3w(mnem, aop, top, nops, ops, ae, te) \
+ TxC3w (mnem, aop, T_MNEM##top, nops, ops, ae, te)
+
+/* Mnemonic that cannot be conditionalized. The ARM condition-code
+ field is still 0xE. Many of the Thumb variants can be executed
+ conditionally, so this is checked separately. */
+#define TUE(mnem, op, top, nops, ops, ae, te) \
+ { mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##ae, do_##te }
+
+/* Same as TUE but the encoding function for ARM and Thumb modes is the same.
+ Used by mnemonics that have very minimal differences in the encoding for
+ ARM and Thumb variants and can be handled in a common function. */
+#define TUEc(mnem, op, top, nops, ops, en) \
+ { mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##en, do_##en }
+
+/* Mnemonic that cannot be conditionalized, and bears 0xF in its ARM
+ condition code field. */
+#define TUF(mnem, op, top, nops, ops, ae, te) \
+ { mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0x##top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##ae, do_##te }
+
+/* ARM-only variants of all the above. */
+#define CE(mnem, op, nops, ops, ae) \
+ { mnem, OPS##nops ops, OT_csuffix, 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define C3(mnem, op, nops, ops, ae) \
+ { #mnem, OPS##nops ops, OT_cinfix3, 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+/* Legacy mnemonics that always have conditional infix after the third
+ character. */
+#define CL(mnem, op, nops, ops, ae) \
+ { mnem, OPS##nops ops, OT_cinfix3_legacy, \
+ 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+/* Coprocessor instructions. Isomorphic between Arm and Thumb-2. */
+#define cCE(mnem, op, nops, ops, ae) \
+ { mnem, OPS##nops ops, OT_csuffix, 0x##op, 0xe##op, ARM_VARIANT, ARM_VARIANT, do_##ae, do_##ae }
+
+/* Legacy coprocessor instructions where conditional infix and conditional
+ suffix are ambiguous. For consistency this includes all FPA instructions,
+ not just the potentially ambiguous ones. */
+#define cCL(mnem, op, nops, ops, ae) \
+ { mnem, OPS##nops ops, OT_cinfix3_legacy, \
+ 0x##op, 0xe##op, ARM_VARIANT, ARM_VARIANT, do_##ae, do_##ae }
+
+/* Coprocessor, takes either a suffix or a position-3 infix
+ (for an FPA corner case). */
+#define C3E(mnem, op, nops, ops, ae) \
+ { mnem, OPS##nops ops, OT_csuf_or_in3, \
+ 0x##op, 0xe##op, ARM_VARIANT, ARM_VARIANT, do_##ae, do_##ae }
+
+#define xCM_(m1, m2, m3, op, nops, ops, ae) \
+ { m1 #m2 m3, OPS##nops ops, \
+ sizeof (#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof (m1) - 1, \
+ 0x##op, 0x0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define CM(m1, m2, op, nops, ops, ae) \
+ xCM_ (m1, , m2, op, nops, ops, ae), \
+ xCM_ (m1, eq, m2, op, nops, ops, ae), \
+ xCM_ (m1, ne, m2, op, nops, ops, ae), \
+ xCM_ (m1, cs, m2, op, nops, ops, ae), \
+ xCM_ (m1, hs, m2, op, nops, ops, ae), \
+ xCM_ (m1, cc, m2, op, nops, ops, ae), \
+ xCM_ (m1, ul, m2, op, nops, ops, ae), \
+ xCM_ (m1, lo, m2, op, nops, ops, ae), \
+ xCM_ (m1, mi, m2, op, nops, ops, ae), \
+ xCM_ (m1, pl, m2, op, nops, ops, ae), \
+ xCM_ (m1, vs, m2, op, nops, ops, ae), \
+ xCM_ (m1, vc, m2, op, nops, ops, ae), \
+ xCM_ (m1, hi, m2, op, nops, ops, ae), \
+ xCM_ (m1, ls, m2, op, nops, ops, ae), \
+ xCM_ (m1, ge, m2, op, nops, ops, ae), \
+ xCM_ (m1, lt, m2, op, nops, ops, ae), \
+ xCM_ (m1, gt, m2, op, nops, ops, ae), \
+ xCM_ (m1, le, m2, op, nops, ops, ae), \
+ xCM_ (m1, al, m2, op, nops, ops, ae)
+
+#define UE(mnem, op, nops, ops, ae) \
+ { #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0, ARM_VARIANT, 0, do_##ae, NULL }
+
+#define UF(mnem, op, nops, ops, ae) \
+ { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0, ARM_VARIANT, 0, do_##ae, NULL }
+
+/* Neon data-processing. ARM versions are unconditional with cond=0xf.
+ The Thumb and ARM variants are mostly the same (bits 0-23 and 24/28), so we
+ use the same encoding function for each. */
+#define NUF(mnem, op, nops, ops, enc) \
+ { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0x##op, \
+ ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc }
+
+/* Neon data processing, version which indirects through neon_enc_tab for
+ the various overloaded versions of opcodes. */
+#define nUF(mnem, op, nops, ops, enc) \
+ { #mnem, OPS##nops ops, OT_unconditionalF, N_MNEM##op, N_MNEM##op, \
+ ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc }
+
+/* Neon insn with conditional suffix for the ARM version, non-overloaded
+ version. */
+#define NCE_tag(mnem, op, nops, ops, enc, tag) \
+ { #mnem, OPS##nops ops, tag, 0x##op, 0x##op, ARM_VARIANT, \
+ THUMB_VARIANT, do_##enc, do_##enc }
+
+#define NCE(mnem, op, nops, ops, enc) \
+ NCE_tag (mnem, op, nops, ops, enc, OT_csuffix)
+
+#define NCEF(mnem, op, nops, ops, enc) \
+ NCE_tag (mnem, op, nops, ops, enc, OT_csuffixF)
+
+/* Neon insn with conditional suffix for the ARM version, overloaded types. */
+#define nCE_tag(mnem, op, nops, ops, enc, tag) \
+ { #mnem, OPS##nops ops, tag, N_MNEM##op, N_MNEM##op, \
+ ARM_VARIANT, THUMB_VARIANT, do_##enc, do_##enc }
+
+#define nCE(mnem, op, nops, ops, enc) \
+ nCE_tag (mnem, op, nops, ops, enc, OT_csuffix)
+
+#define nCEF(mnem, op, nops, ops, enc) \
+ nCE_tag (mnem, op, nops, ops, enc, OT_csuffixF)
+
+#define do_0 0
+
+static const struct asm_opcode insns[] =
+{
+#define ARM_VARIANT & arm_ext_v1 /* Core ARM Instructions. */
+#define THUMB_VARIANT & arm_ext_v4t
+ tCE("and", 0000000, _and, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3("ands", 0100000, _ands, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE("eor", 0200000, _eor, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3("eors", 0300000, _eors, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE("sub", 0400000, _sub, 3, (RR, oRR, SH), arit, t_add_sub),
+ tC3("subs", 0500000, _subs, 3, (RR, oRR, SH), arit, t_add_sub),
+ tCE("add", 0800000, _add, 3, (RR, oRR, SHG), arit, t_add_sub),
+ tC3("adds", 0900000, _adds, 3, (RR, oRR, SHG), arit, t_add_sub),
+ tCE("adc", 0a00000, _adc, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3("adcs", 0b00000, _adcs, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE("sbc", 0c00000, _sbc, 3, (RR, oRR, SH), arit, t_arit3),
+ tC3("sbcs", 0d00000, _sbcs, 3, (RR, oRR, SH), arit, t_arit3),
+ tCE("orr", 1800000, _orr, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3("orrs", 1900000, _orrs, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE("bic", 1c00000, _bic, 3, (RR, oRR, SH), arit, t_arit3),
+ tC3("bics", 1d00000, _bics, 3, (RR, oRR, SH), arit, t_arit3),
+
+ /* The p-variants of tst/cmp/cmn/teq (below) are the pre-V6 mechanism
+ for setting PSR flag bits. They are obsolete in V6 and do not
+ have Thumb equivalents. */
+ tCE("tst", 1100000, _tst, 2, (RR, SH), cmp, t_mvn_tst),
+ tC3w("tsts", 1100000, _tst, 2, (RR, SH), cmp, t_mvn_tst),
+ CL("tstp", 110f000, 2, (RR, SH), cmp),
+ tCE("cmp", 1500000, _cmp, 2, (RR, SH), cmp, t_mov_cmp),
+ tC3w("cmps", 1500000, _cmp, 2, (RR, SH), cmp, t_mov_cmp),
+ CL("cmpp", 150f000, 2, (RR, SH), cmp),
+ tCE("cmn", 1700000, _cmn, 2, (RR, SH), cmp, t_mvn_tst),
+ tC3w("cmns", 1700000, _cmn, 2, (RR, SH), cmp, t_mvn_tst),
+ CL("cmnp", 170f000, 2, (RR, SH), cmp),
+
+ tCE("mov", 1a00000, _mov, 2, (RR, SH), mov, t_mov_cmp),
+ tC3("movs", 1b00000, _movs, 2, (RR, SH), mov, t_mov_cmp),
+ tCE("mvn", 1e00000, _mvn, 2, (RR, SH), mov, t_mvn_tst),
+ tC3("mvns", 1f00000, _mvns, 2, (RR, SH), mov, t_mvn_tst),
+
+ tCE("ldr", 4100000, _ldr, 2, (RR, ADDRGLDR),ldst, t_ldst),
+ tC3("ldrb", 4500000, _ldrb, 2, (RRnpc_npcsp, ADDRGLDR),ldst, t_ldst),
+ tCE("str", 4000000, _str, _2, (MIX_ARM_THUMB_OPERANDS (OP_RR,
+ OP_RRnpc),
+ OP_ADDRGLDR),ldst, t_ldst),
+ tC3("strb", 4400000, _strb, 2, (RRnpc_npcsp, ADDRGLDR),ldst, t_ldst),
+
+ tCE("stm", 8800000, _stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3("stmia", 8800000, _stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3("stmea", 8800000, _stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tCE("ldm", 8900000, _ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3("ldmia", 8900000, _ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3("ldmfd", 8900000, _ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ TCE("swi", f000000, df00, 1, (EXPi), swi, t_swi),
+ TCE("svc", f000000, df00, 1, (EXPi), swi, t_swi),
+ tCE("b", a000000, _b, 1, (EXPr), branch, t_branch),
+ TCE("bl", b000000, f000f800, 1, (EXPr), bl, t_branch23),
+
+ /* Pseudo ops. */
+ tCE("adr", 28f0000, _adr, 2, (RR, EXP), adr, t_adr),
+ C3(adrl, 28f0000, 2, (RR, EXP), adrl),
+ tCE("nop", 1a00000, _nop, 1, (oI255c), nop, t_nop),
+ tCE("udf", 7f000f0, _udf, 1, (oIffffb), bkpt, t_udf),
+
+ /* Thumb-compatibility pseudo ops. */
+ tCE("lsl", 1a00000, _lsl, 3, (RR, oRR, SH), shift, t_shift),
+ tC3("lsls", 1b00000, _lsls, 3, (RR, oRR, SH), shift, t_shift),
+ tCE("lsr", 1a00020, _lsr, 3, (RR, oRR, SH), shift, t_shift),
+ tC3("lsrs", 1b00020, _lsrs, 3, (RR, oRR, SH), shift, t_shift),
+ tCE("asr", 1a00040, _asr, 3, (RR, oRR, SH), shift, t_shift),
+ tC3("asrs", 1b00040, _asrs, 3, (RR, oRR, SH), shift, t_shift),
+ tCE("ror", 1a00060, _ror, 3, (RR, oRR, SH), shift, t_shift),
+ tC3("rors", 1b00060, _rors, 3, (RR, oRR, SH), shift, t_shift),
+ tCE("neg", 2600000, _neg, 2, (RR, RR), rd_rn, t_neg),
+ tC3("negs", 2700000, _negs, 2, (RR, RR), rd_rn, t_neg),
+ tCE("push", 92d0000, _push, 1, (REGLST), push_pop, t_push_pop),
+ tCE("pop", 8bd0000, _pop, 1, (REGLST), push_pop, t_push_pop),
+
+ /* These may simplify to neg. */
+ TCE("rsb", 0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb),
+ TC3("rsbs", 0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6
+
+ TCE("cpy", 1a00000, 4600, 2, (RR, RR), rd_rm, t_cpy),
+
+ /* V1 instructions with no Thumb analogue prior to V6T2. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TCE("teq", 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst),
+ TC3w("teqs", 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst),
+ CL("teqp", 130f000, 2, (RR, SH), cmp),
+
+ TC3("ldrt", 4300000, f8500e00, 2, (RRnpc_npcsp, ADDR),ldstt, t_ldstt),
+ TC3("ldrbt", 4700000, f8100e00, 2, (RRnpc_npcsp, ADDR),ldstt, t_ldstt),
+ TC3("strt", 4200000, f8400e00, 2, (RR_npcsp, ADDR), ldstt, t_ldstt),
+ TC3("strbt", 4600000, f8000e00, 2, (RRnpc_npcsp, ADDR),ldstt, t_ldstt),
+
+ TC3("stmdb", 9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3("stmfd", 9000000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ TC3("ldmdb", 9100000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3("ldmea", 9100000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ /* V1 instructions with no Thumb analogue at all. */
+ CE("rsc", 0e00000, 3, (RR, oRR, SH), arit),
+ C3(rscs, 0f00000, 3, (RR, oRR, SH), arit),
+
+ C3(stmib, 9800000, 2, (RRw, REGLST), ldmstm),
+ C3(stmfa, 9800000, 2, (RRw, REGLST), ldmstm),
+ C3(stmda, 8000000, 2, (RRw, REGLST), ldmstm),
+ C3(stmed, 8000000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmib, 9900000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmed, 9900000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmda, 8100000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmfa, 8100000, 2, (RRw, REGLST), ldmstm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v2 /* ARM 2 - multiplies. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v4t
+
+ tCE("mul", 0000090, _mul, 3, (RRnpc, RRnpc, oRR), mul, t_mul),
+ tC3("muls", 0100090, _muls, 3, (RRnpc, RRnpc, oRR), mul, t_mul),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TCE("mla", 0200090, fb000000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
+ C3(mlas, 0300090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas),
+
+ /* Generic coprocessor instructions. */
+ TCE("cdp", e000000, ee000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp, cdp),
+ TCE("ldc", c100000, ec100000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TC3("ldcl", c500000, ec500000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TCE("stc", c000000, ec000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TC3("stcl", c400000, ec400000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TCE("mcr", e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+ TCE("mrc", e100010, ee100010, 6, (RCP, I7b, APSR_RR, RCN, RCN, oI7b), co_reg, co_reg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v2s /* ARM 3 - swp instructions. */
+
+ CE("swp", 1000090, 3, (RRnpc, RRnpc, RRnpcb), rd_rm_rn),
+ C3(swpb, 1400090, 3, (RRnpc, RRnpc, RRnpcb), rd_rm_rn),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v3 /* ARM 6 Status register instructions. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_msr
+
+ TCE("mrs", 1000000, f3e08000, 2, (RRnpc, rPSR), mrs, t_mrs),
+ TCE("msr", 120f000, f3808000, 2, (wPSR, RR_EXi), msr, t_msr),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v3m /* ARM 7M long multiplies. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TCE("smull", 0c00090, fb800000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM("smull","s", 0d00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE("umull", 0800090, fba00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM("umull","s", 0900090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE("smlal", 0e00090, fbc00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM("smlal","s", 0f00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE("umlal", 0a00090, fbe00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM("umlal","s", 0b00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v4 /* ARM Architecture 4. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v4t
+
+ tC3("ldrh", 01000b0, _ldrh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("strh", 00000b0, _strh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("ldrsh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("ldrsb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("ldsh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("ldsb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v4t_5
+
+ /* ARM Architecture 4T. */
+ /* Note: bx (and blx) are required on V5, even if the processor does
+ not support Thumb. */
+ TCE("bx", 12fff10, 4700, 1, (RR), bx, t_bx),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v5 /* ARM Architecture 5T. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v5t
+
+ /* Note: blx has 2 variants; the .value coded here is for
+ BLX(2). Only this variant has conditional execution. */
+ TCE("blx", 12fff30, 4780, 1, (RR_EXr), blx, t_blx),
+ TUE("bkpt", 1200070, be00, 1, (oIffffb), bkpt, t_bkpt),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TCE("clz", 16f0f10, fab0f080, 2, (RRnpc, RRnpc), rd_rm, t_clz),
+ TUF("ldc2", c100000, fc100000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF("ldc2l", c500000, fc500000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF("stc2", c000000, fc000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF("stc2l", c400000, fc400000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF("cdp2", e000000, fe000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp, cdp),
+ TUF("mcr2", e000010, fe000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+ TUF("mrc2", e100010, fe100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v5exp /* ARM Architecture 5TExP. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v5exp
+
+ TCE("smlabb", 1000080, fb100000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE("smlatb", 10000a0, fb100020, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE("smlabt", 10000c0, fb100010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE("smlatt", 10000e0, fb100030, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+
+ TCE("smlawb", 1200080, fb300000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE("smlawt", 12000c0, fb300010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+
+ TCE("smlalbb", 1400080, fbc00080, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+ TCE("smlaltb", 14000a0, fbc000a0, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+ TCE("smlalbt", 14000c0, fbc00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+ TCE("smlaltt", 14000e0, fbc000b0, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+
+ TCE("smulbb", 1600080, fb10f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smultb", 16000a0, fb10f020, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smulbt", 16000c0, fb10f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smultt", 16000e0, fb10f030, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+
+ TCE("smulwb", 12000a0, fb30f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smulwt", 12000e0, fb30f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+
+ TCE("qadd", 1000050, fa80f080, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2),
+ TCE("qdadd", 1400050, fa80f090, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2),
+ TCE("qsub", 1200050, fa80f0a0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2),
+ TCE("qdsub", 1600050, fa80f0b0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v5e /* ARM Architecture 5TE. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TUF("pld", 450f000, f810f000, 1, (ADDR), pld, t_pld),
+ TC3("ldrd", 00000d0, e8500000, 3, (RRnpc_npcsp, oRRnpc_npcsp, ADDRGLDRS),
+ ldrd, t_ldstd),
+ TC3("strd", 00000f0, e8400000, 3, (RRnpc_npcsp, oRRnpc_npcsp,
+ ADDRGLDRS), ldrd, t_ldstd),
+
+ TCE("mcrr", c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TCE("mrrc", c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v5j /* ARM Architecture 5TEJ. */
+
+ TCE("bxj", 12fff20, f3c08f00, 1, (RR), bxj, t_bxj),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v6 /* ARM V6. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6
+
+ TUF("cpsie", 1080000, b660, 2, (CPSF, oI31b), cpsi, t_cpsi),
+ TUF("cpsid", 10c0000, b670, 2, (CPSF, oI31b), cpsi, t_cpsi),
+ tCE("rev", 6bf0f30, _rev, 2, (RRnpc, RRnpc), rd_rm, t_rev),
+ tCE("rev16", 6bf0fb0, _rev16, 2, (RRnpc, RRnpc), rd_rm, t_rev),
+ tCE("revsh", 6ff0fb0, _revsh, 2, (RRnpc, RRnpc), rd_rm, t_rev),
+ tCE("sxth", 6bf0070, _sxth, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ tCE("uxth", 6ff0070, _uxth, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ tCE("sxtb", 6af0070, _sxtb, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ tCE("uxtb", 6ef0070, _uxtb, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ TUF("setend", 1010000, b650, 1, (ENDI), setend, t_setend),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TCE("ldrex", 1900f9f, e8500f00, 2, (RRnpc_npcsp, ADDR), ldrex, t_ldrex),
+ TCE("strex", 1800f90, e8400000, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
+ strex, t_strex),
+ TUF("mcrr2", c400000, fc400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TUF("mrrc2", c500000, fc500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+
+ TCE("ssat", 6a00010, f3000000, 4, (RRnpc, I32, RRnpc, oSHllar),ssat, t_ssat),
+ TCE("usat", 6e00010, f3800000, 4, (RRnpc, I31, RRnpc, oSHllar),usat, t_usat),
+
+/* ARM V6 not included in V7M. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6_notm
+ TUF("rfeia", 8900a00, e990c000, 1, (RRw), rfe, rfe),
+ TUF("rfe", 8900a00, e990c000, 1, (RRw), rfe, rfe),
+ UF(rfeib, 9900a00, 1, (RRw), rfe),
+ UF(rfeda, 8100a00, 1, (RRw), rfe),
+ TUF("rfedb", 9100a00, e810c000, 1, (RRw), rfe, rfe),
+ TUF("rfefd", 8900a00, e990c000, 1, (RRw), rfe, rfe),
+ UF(rfefa, 8100a00, 1, (RRw), rfe),
+ TUF("rfeea", 9100a00, e810c000, 1, (RRw), rfe, rfe),
+ UF(rfeed, 9900a00, 1, (RRw), rfe),
+ TUF("srsia", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
+ TUF("srs", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
+ TUF("srsea", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
+ UF(srsib, 9c00500, 2, (oRRw, I31w), srs),
+ UF(srsfa, 9c00500, 2, (oRRw, I31w), srs),
+ UF(srsda, 8400500, 2, (oRRw, I31w), srs),
+ UF(srsed, 8400500, 2, (oRRw, I31w), srs),
+ TUF("srsdb", 9400500, e800c000, 2, (oRRw, I31w), srs, srs),
+ TUF("srsfd", 9400500, e800c000, 2, (oRRw, I31w), srs, srs),
+
+/* ARM V6 not included in V7M (eg. integer SIMD). */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6_dsp
+ TUF("cps", 1020000, f3af8100, 1, (I31b), imm0, t_cps),
+ TCE("pkhbt", 6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll), pkhbt, t_pkhbt),
+ TCE("pkhtb", 6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar), pkhtb, t_pkhtb),
+ TCE("qadd16", 6200f10, fa90f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qadd8", 6200f90, fa80f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qasx", 6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for QASX. */
+ TCE("qaddsubx",6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qsax", 6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for QSAX. */
+ TCE("qsubaddx",6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qsub16", 6200f70, fad0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qsub8", 6200ff0, fac0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("sadd16", 6100f10, fa90f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("sadd8", 6100f90, fa80f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("sasx", 6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for SASX. */
+ TCE("saddsubx",6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shadd16", 6300f10, fa90f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shadd8", 6300f90, fa80f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shasx", 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for SHASX. */
+ TCE("shaddsubx", 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shsax", 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for SHSAX. */
+ TCE("shsubaddx", 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shsub16", 6300f70, fad0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shsub8", 6300ff0, fac0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("ssax", 6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for SSAX. */
+ TCE("ssubaddx",6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("ssub16", 6100f70, fad0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("ssub8", 6100ff0, fac0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uadd16", 6500f10, fa90f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uadd8", 6500f90, fa80f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uasx", 6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for UASX. */
+ TCE("uaddsubx",6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhadd16", 6700f10, fa90f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhadd8", 6700f90, fa80f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhasx", 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for UHASX. */
+ TCE("uhaddsubx", 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhsax", 6700f50, fae0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for UHSAX. */
+ TCE("uhsubaddx", 6700f50, fae0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhsub16", 6700f70, fad0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhsub8", 6700ff0, fac0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqadd16", 6600f10, fa90f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqadd8", 6600f90, fa80f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqasx", 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for UQASX. */
+ TCE("uqaddsubx", 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqsax", 6600f50, fae0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for UQSAX. */
+ TCE("uqsubaddx", 6600f50, fae0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqsub16", 6600f70, fad0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqsub8", 6600ff0, fac0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("usub16", 6500f70, fad0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("usax", 6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ /* Old name for USAX. */
+ TCE("usubaddx",6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("usub8", 6500ff0, fac0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("sxtah", 6b00070, fa00f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE("sxtab16", 6800070, fa20f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE("sxtab", 6a00070, fa40f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE("sxtb16", 68f0070, fa2ff080, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ TCE("uxtah", 6f00070, fa10f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE("uxtab16", 6c00070, fa30f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE("uxtab", 6e00070, fa50f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE("uxtb16", 6cf0070, fa3ff080, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ TCE("sel", 6800fb0, faa0f080, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("smlad", 7000010, fb200000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smladx", 7000030, fb200010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smlald", 7400010, fbc000c0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE("smlaldx", 7400030, fbc000d0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE("smlsd", 7000050, fb400000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smlsdx", 7000070, fb400010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smlsld", 7400050, fbd000c0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE("smlsldx", 7400070, fbd000d0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE("smmla", 7500010, fb500000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smmlar", 7500030, fb500010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smmls", 75000d0, fb600000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smmlsr", 75000f0, fb600010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("smmul", 750f010, fb50f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smmulr", 750f030, fb50f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smuad", 700f010, fb20f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smuadx", 700f030, fb20f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smusd", 700f050, fb40f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("smusdx", 700f070, fb40f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("ssat16", 6a00f30, f3200000, 3, (RRnpc, I16, RRnpc), ssat16, t_ssat16),
+ TCE("umaal", 0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal, t_mlal),
+ TCE("usad8", 780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE("usada8", 7800010, fb700000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE("usat16", 6e00f30, f3a00000, 3, (RRnpc, I15, RRnpc), usat16, t_usat16),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v6k
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6k
+
+ tCE("yield", 320f001, _yield, 0, (), noargs, t_hint),
+ tCE("wfe", 320f002, _wfe, 0, (), noargs, t_hint),
+ tCE("wfi", 320f003, _wfi, 0, (), noargs, t_hint),
+ tCE("sev", 320f004, _sev, 0, (), noargs, t_hint),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6_notm
+ TCE("ldrexd", 1b00f9f, e8d0007f, 3, (RRnpc_npcsp, oRRnpc_npcsp, RRnpcb),
+ ldrexd, t_ldrexd),
+ TCE("strexd", 1a00f90, e8c00070, 4, (RRnpc_npcsp, RRnpc_npcsp, oRRnpc_npcsp,
+ RRnpcb), strexd, t_strexd),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+ TCE("ldrexb", 1d00f9f, e8d00f4f, 2, (RRnpc_npcsp,RRnpcb),
+ rd_rn, rd_rn),
+ TCE("ldrexh", 1f00f9f, e8d00f5f, 2, (RRnpc_npcsp, RRnpcb),
+ rd_rn, rd_rn),
+ TCE("strexb", 1c00f90, e8c00f40, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
+ strex, t_strexbh),
+ TCE("strexh", 1e00f90, e8c00f50, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
+ strex, t_strexbh),
+ TUF("clrex", 57ff01f, f3bf8f2f, 0, (), noargs, noargs),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_sec
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_sec
+
+ TCE("smc", 1600070, f7f08000, 1, (EXPi), smc, t_smc),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_virt
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_virt
+
+ TCE("hvc", 1400070, f7e08000, 1, (EXPi), hvc, t_hvc),
+ TCE("eret", 160006e, f3de8f00, 0, (), noargs, noargs),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v6t2
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
+ TCE("bfc", 7c0001f, f36f0000, 3, (RRnpc, I31, I32), bfc, t_bfc),
+ TCE("bfi", 7c00010, f3600000, 4, (RRnpc, RRnpc_I0, I31, I32), bfi, t_bfi),
+ TCE("sbfx", 7a00050, f3400000, 4, (RR, RR, I31, I32), bfx, t_bfx),
+ TCE("ubfx", 7e00050, f3c00000, 4, (RR, RR, I31, I32), bfx, t_bfx),
+
+ TCE("mls", 0600090, fb000010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
+ TCE("movw", 3000000, f2400000, 2, (RRnpc, HALF), mov16, t_mov16),
+ TCE("movt", 3400000, f2c00000, 2, (RRnpc, HALF), mov16, t_mov16),
+ TCE("rbit", 6ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit),
+
+ TC3("ldrht", 03000b0, f8300e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
+ TC3("ldrsht", 03000f0, f9300e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
+ TC3("ldrsbt", 03000d0, f9100e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
+ TC3("strht", 02000b0, f8200e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
+
+ /* Thumb-only instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT NULL
+ TUE("cbnz", 0, b900, 2, (RR, EXP), 0, t_cbz),
+ TUE("cbz", 0, b100, 2, (RR, EXP), 0, t_cbz),
+
+ /* ARM does not really have an IT instruction, so always allow it.
+ The opcode is copied from Thumb in order to allow warnings in
+ -mimplicit-it=[never | arm] modes. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v1
+
+ TUE("it", bf08, bf08, 1, (COND), it, t_it),
+ TUE("itt", bf0c, bf0c, 1, (COND), it, t_it),
+ TUE("ite", bf04, bf04, 1, (COND), it, t_it),
+ TUE("ittt", bf0e, bf0e, 1, (COND), it, t_it),
+ TUE("itet", bf06, bf06, 1, (COND), it, t_it),
+ TUE("itte", bf0a, bf0a, 1, (COND), it, t_it),
+ TUE("itee", bf02, bf02, 1, (COND), it, t_it),
+ TUE("itttt", bf0f, bf0f, 1, (COND), it, t_it),
+ TUE("itett", bf07, bf07, 1, (COND), it, t_it),
+ TUE("ittet", bf0b, bf0b, 1, (COND), it, t_it),
+ TUE("iteet", bf03, bf03, 1, (COND), it, t_it),
+ TUE("ittte", bf0d, bf0d, 1, (COND), it, t_it),
+ TUE("itete", bf05, bf05, 1, (COND), it, t_it),
+ TUE("ittee", bf09, bf09, 1, (COND), it, t_it),
+ TUE("iteee", bf01, bf01, 1, (COND), it, t_it),
+ /* ARM/Thumb-2 instructions with no Thumb-1 equivalent. */
+ TC3("rrx", 01a00060, ea4f0030, 2, (RR, RR), rd_rm, t_rrx),
+ TC3("rrxs", 01b00060, ea5f0030, 2, (RR, RR), rd_rm, t_rrx),
+
+ /* Thumb2 only instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT NULL
+
+ TCE("addw", 0, f2000000, 3, (RR, RR, EXPi), 0, t_add_sub_w),
+ TCE("subw", 0, f2a00000, 3, (RR, RR, EXPi), 0, t_add_sub_w),
+ TCE("orn", 0, ea600000, 3, (RR, oRR, SH), 0, t_orn),
+ TCE("orns", 0, ea700000, 3, (RR, oRR, SH), 0, t_orn),
+ TCE("tbb", 0, e8d0f000, 1, (TB), 0, t_tb),
+ TCE("tbh", 0, e8d0f010, 1, (TB), 0, t_tb),
+
+ /* Hardware division instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_adiv
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_div
+
+ TCE("sdiv", 710f010, fb90f0f0, 3, (RR, oRR, RR), div, t_div),
+ TCE("udiv", 730f010, fbb0f0f0, 3, (RR, oRR, RR), div, t_div),
+
+ /* ARM V6M/V7 instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_barrier
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_barrier
+
+ TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER_I15), barrier, barrier),
+ TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER_I15), barrier, barrier),
+ TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER_I15), barrier, barrier),
+
+ /* ARM V7 instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v7
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v7
+
+ TUF("pli", 450f000, f910f000, 1, (ADDR), pli, t_pld),
+ TCE("dbg", 320f0f0, f3af80f0, 1, (I15), dbg, t_dbg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_mp
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_mp
+
+ TUF("pldw", 410f000, f830f000, 1, (ADDR), pld, t_pld),
+
+ /* AArchv8 instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v8
+
+ tCE("sevl", 320f005, _sevl, 0, (), noargs, t_hint),
+ TUE("hlt", 1000070, ba80, 1, (oIffffb), bkpt, t_hlt),
+ TCE("ldaex", 1900e9f, e8d00fef, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("ldaexd", 1b00e9f, e8d000ff, 3, (RRnpc, oRRnpc, RRnpcb),
+ ldrexd, t_ldrexd),
+ TCE("ldaexb", 1d00e9f, e8d00fcf, 2, (RRnpc,RRnpcb), rd_rn, rd_rn),
+ TCE("ldaexh", 1f00e9f, e8d00fdf, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("stlex", 1800e90, e8c00fe0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("stlexd", 1a00e90, e8c000f0, 4, (RRnpc, RRnpc, oRRnpc, RRnpcb),
+ strexd, t_strexd),
+ TCE("stlexb", 1c00e90, e8c00fc0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("stlexh", 1e00e90, e8c00fd0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("lda", 1900c9f, e8d00faf, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("ldab", 1d00c9f, e8d00f8f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("ldah", 1f00c9f, e8d00f9f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("stl", 180fc90, e8c00faf, 2, (RRnpc, RRnpcb), rm_rn, rd_rn),
+ TCE("stlb", 1c0fc90, e8c00f8f, 2, (RRnpc, RRnpcb), rm_rn, rd_rn),
+ TCE("stlh", 1e0fc90, e8c00f9f, 2, (RRnpc, RRnpcb), rm_rn, rd_rn),
+
+ /* ARMv8 T32 only. */
+#undef ARM_VARIANT
+#define ARM_VARIANT NULL
+ TUF("dcps1", 0, f78f8001, 0, (), noargs, noargs),
+ TUF("dcps2", 0, f78f8002, 0, (), noargs, noargs),
+ TUF("dcps3", 0, f78f8003, 0, (), noargs, noargs),
+
+ /* FP for ARMv8. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_armv8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_armv8
+
+ nUF(vseleq, _vseleq, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vselvs, _vselvs, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vselge, _vselge, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vselgt, _vselgt, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vmaxnm, _vmaxnm, 3, (RNSDQ, oRNSDQ, RNSDQ), vmaxnm),
+ nUF(vminnm, _vminnm, 3, (RNSDQ, oRNSDQ, RNSDQ), vmaxnm),
+ nUF(vcvta, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvta),
+ nUF(vcvtn, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvtn),
+ nUF(vcvtp, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvtp),
+ nUF(vcvtm, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvtm),
+ nCE(vrintr, _vrintr, 2, (RNSDQ, oRNSDQ), vrintr),
+ nCE(vrintz, _vrintr, 2, (RNSDQ, oRNSDQ), vrintz),
+ nCE(vrintx, _vrintr, 2, (RNSDQ, oRNSDQ), vrintx),
+ nUF(vrinta, _vrinta, 2, (RNSDQ, oRNSDQ), vrinta),
+ nUF(vrintn, _vrinta, 2, (RNSDQ, oRNSDQ), vrintn),
+ nUF(vrintp, _vrinta, 2, (RNSDQ, oRNSDQ), vrintp),
+ nUF(vrintm, _vrinta, 2, (RNSDQ, oRNSDQ), vrintm),
+
+ /* Crypto v1 extensions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_crypto_ext_armv8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_crypto_ext_armv8
+
+ nUF(aese, _aes, 2, (RNQ, RNQ), aese),
+ nUF(aesd, _aes, 2, (RNQ, RNQ), aesd),
+ nUF(aesmc, _aes, 2, (RNQ, RNQ), aesmc),
+ nUF(aesimc, _aes, 2, (RNQ, RNQ), aesimc),
+ nUF(sha1c, _sha3op, 3, (RNQ, RNQ, RNQ), sha1c),
+ nUF(sha1p, _sha3op, 3, (RNQ, RNQ, RNQ), sha1p),
+ nUF(sha1m, _sha3op, 3, (RNQ, RNQ, RNQ), sha1m),
+ nUF(sha1su0, _sha3op, 3, (RNQ, RNQ, RNQ), sha1su0),
+ nUF(sha256h, _sha3op, 3, (RNQ, RNQ, RNQ), sha256h),
+ nUF(sha256h2, _sha3op, 3, (RNQ, RNQ, RNQ), sha256h2),
+ nUF(sha256su1, _sha3op, 3, (RNQ, RNQ, RNQ), sha256su1),
+ nUF(sha1h, _sha1h, 2, (RNQ, RNQ), sha1h),
+ nUF(sha1su1, _sha2op, 2, (RNQ, RNQ), sha1su1),
+ nUF(sha256su0, _sha2op, 2, (RNQ, RNQ), sha256su0),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & crc_ext_armv8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & crc_ext_armv8
+ TUEc("crc32b", 1000040, fac0f080, 3, (RR, oRR, RR), crc32b),
+ TUEc("crc32h", 1200040, fac0f090, 3, (RR, oRR, RR), crc32h),
+ TUEc("crc32w", 1400040, fac0f0a0, 3, (RR, oRR, RR), crc32w),
+ TUEc("crc32cb",1000240, fad0f080, 3, (RR, oRR, RR), crc32cb),
+ TUEc("crc32ch",1200240, fad0f090, 3, (RR, oRR, RR), crc32ch),
+ TUEc("crc32cw",1400240, fad0f0a0, 3, (RR, oRR, RR), crc32cw),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_fpa_ext_v1 /* Core FPA instruction set (V1). */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT NULL
+
+ cCE("wfs", e200110, 1, (RR), rd),
+ cCE("rfs", e300110, 1, (RR), rd),
+ cCE("wfc", e400110, 1, (RR), rd),
+ cCE("rfc", e500110, 1, (RR), rd),
+
+ cCL("ldfs", c100100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL("ldfd", c108100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL("ldfe", c500100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL("ldfp", c508100, 2, (RF, ADDRGLDC), rd_cpaddr),
+
+ cCL("stfs", c000100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL("stfd", c008100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL("stfe", c400100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL("stfp", c408100, 2, (RF, ADDRGLDC), rd_cpaddr),
+
+ cCL("mvfs", e008100, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfsp", e008120, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfsm", e008140, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfsz", e008160, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfd", e008180, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfdp", e0081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfdm", e0081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfdz", e0081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfe", e088100, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfep", e088120, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfem", e088140, 2, (RF, RF_IF), rd_rm),
+ cCL("mvfez", e088160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("mnfs", e108100, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfsp", e108120, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfsm", e108140, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfsz", e108160, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfd", e108180, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfdp", e1081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfdm", e1081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfdz", e1081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfe", e188100, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfep", e188120, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfem", e188140, 2, (RF, RF_IF), rd_rm),
+ cCL("mnfez", e188160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("abss", e208100, 2, (RF, RF_IF), rd_rm),
+ cCL("abssp", e208120, 2, (RF, RF_IF), rd_rm),
+ cCL("abssm", e208140, 2, (RF, RF_IF), rd_rm),
+ cCL("abssz", e208160, 2, (RF, RF_IF), rd_rm),
+ cCL("absd", e208180, 2, (RF, RF_IF), rd_rm),
+ cCL("absdp", e2081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("absdm", e2081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("absdz", e2081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("abse", e288100, 2, (RF, RF_IF), rd_rm),
+ cCL("absep", e288120, 2, (RF, RF_IF), rd_rm),
+ cCL("absem", e288140, 2, (RF, RF_IF), rd_rm),
+ cCL("absez", e288160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("rnds", e308100, 2, (RF, RF_IF), rd_rm),
+ cCL("rndsp", e308120, 2, (RF, RF_IF), rd_rm),
+ cCL("rndsm", e308140, 2, (RF, RF_IF), rd_rm),
+ cCL("rndsz", e308160, 2, (RF, RF_IF), rd_rm),
+ cCL("rndd", e308180, 2, (RF, RF_IF), rd_rm),
+ cCL("rnddp", e3081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("rnddm", e3081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("rnddz", e3081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("rnde", e388100, 2, (RF, RF_IF), rd_rm),
+ cCL("rndep", e388120, 2, (RF, RF_IF), rd_rm),
+ cCL("rndem", e388140, 2, (RF, RF_IF), rd_rm),
+ cCL("rndez", e388160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("sqts", e408100, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtsp", e408120, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtsm", e408140, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtsz", e408160, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtd", e408180, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtdp", e4081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtdm", e4081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtdz", e4081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("sqte", e488100, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtep", e488120, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtem", e488140, 2, (RF, RF_IF), rd_rm),
+ cCL("sqtez", e488160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("logs", e508100, 2, (RF, RF_IF), rd_rm),
+ cCL("logsp", e508120, 2, (RF, RF_IF), rd_rm),
+ cCL("logsm", e508140, 2, (RF, RF_IF), rd_rm),
+ cCL("logsz", e508160, 2, (RF, RF_IF), rd_rm),
+ cCL("logd", e508180, 2, (RF, RF_IF), rd_rm),
+ cCL("logdp", e5081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("logdm", e5081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("logdz", e5081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("loge", e588100, 2, (RF, RF_IF), rd_rm),
+ cCL("logep", e588120, 2, (RF, RF_IF), rd_rm),
+ cCL("logem", e588140, 2, (RF, RF_IF), rd_rm),
+ cCL("logez", e588160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("lgns", e608100, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnsp", e608120, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnsm", e608140, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnsz", e608160, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnd", e608180, 2, (RF, RF_IF), rd_rm),
+ cCL("lgndp", e6081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("lgndm", e6081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("lgndz", e6081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("lgne", e688100, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnep", e688120, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnem", e688140, 2, (RF, RF_IF), rd_rm),
+ cCL("lgnez", e688160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("exps", e708100, 2, (RF, RF_IF), rd_rm),
+ cCL("expsp", e708120, 2, (RF, RF_IF), rd_rm),
+ cCL("expsm", e708140, 2, (RF, RF_IF), rd_rm),
+ cCL("expsz", e708160, 2, (RF, RF_IF), rd_rm),
+ cCL("expd", e708180, 2, (RF, RF_IF), rd_rm),
+ cCL("expdp", e7081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("expdm", e7081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("expdz", e7081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("expe", e788100, 2, (RF, RF_IF), rd_rm),
+ cCL("expep", e788120, 2, (RF, RF_IF), rd_rm),
+ cCL("expem", e788140, 2, (RF, RF_IF), rd_rm),
+ cCL("expdz", e788160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("sins", e808100, 2, (RF, RF_IF), rd_rm),
+ cCL("sinsp", e808120, 2, (RF, RF_IF), rd_rm),
+ cCL("sinsm", e808140, 2, (RF, RF_IF), rd_rm),
+ cCL("sinsz", e808160, 2, (RF, RF_IF), rd_rm),
+ cCL("sind", e808180, 2, (RF, RF_IF), rd_rm),
+ cCL("sindp", e8081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("sindm", e8081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("sindz", e8081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("sine", e888100, 2, (RF, RF_IF), rd_rm),
+ cCL("sinep", e888120, 2, (RF, RF_IF), rd_rm),
+ cCL("sinem", e888140, 2, (RF, RF_IF), rd_rm),
+ cCL("sinez", e888160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("coss", e908100, 2, (RF, RF_IF), rd_rm),
+ cCL("cossp", e908120, 2, (RF, RF_IF), rd_rm),
+ cCL("cossm", e908140, 2, (RF, RF_IF), rd_rm),
+ cCL("cossz", e908160, 2, (RF, RF_IF), rd_rm),
+ cCL("cosd", e908180, 2, (RF, RF_IF), rd_rm),
+ cCL("cosdp", e9081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("cosdm", e9081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("cosdz", e9081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("cose", e988100, 2, (RF, RF_IF), rd_rm),
+ cCL("cosep", e988120, 2, (RF, RF_IF), rd_rm),
+ cCL("cosem", e988140, 2, (RF, RF_IF), rd_rm),
+ cCL("cosez", e988160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("tans", ea08100, 2, (RF, RF_IF), rd_rm),
+ cCL("tansp", ea08120, 2, (RF, RF_IF), rd_rm),
+ cCL("tansm", ea08140, 2, (RF, RF_IF), rd_rm),
+ cCL("tansz", ea08160, 2, (RF, RF_IF), rd_rm),
+ cCL("tand", ea08180, 2, (RF, RF_IF), rd_rm),
+ cCL("tandp", ea081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("tandm", ea081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("tandz", ea081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("tane", ea88100, 2, (RF, RF_IF), rd_rm),
+ cCL("tanep", ea88120, 2, (RF, RF_IF), rd_rm),
+ cCL("tanem", ea88140, 2, (RF, RF_IF), rd_rm),
+ cCL("tanez", ea88160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("asns", eb08100, 2, (RF, RF_IF), rd_rm),
+ cCL("asnsp", eb08120, 2, (RF, RF_IF), rd_rm),
+ cCL("asnsm", eb08140, 2, (RF, RF_IF), rd_rm),
+ cCL("asnsz", eb08160, 2, (RF, RF_IF), rd_rm),
+ cCL("asnd", eb08180, 2, (RF, RF_IF), rd_rm),
+ cCL("asndp", eb081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("asndm", eb081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("asndz", eb081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("asne", eb88100, 2, (RF, RF_IF), rd_rm),
+ cCL("asnep", eb88120, 2, (RF, RF_IF), rd_rm),
+ cCL("asnem", eb88140, 2, (RF, RF_IF), rd_rm),
+ cCL("asnez", eb88160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("acss", ec08100, 2, (RF, RF_IF), rd_rm),
+ cCL("acssp", ec08120, 2, (RF, RF_IF), rd_rm),
+ cCL("acssm", ec08140, 2, (RF, RF_IF), rd_rm),
+ cCL("acssz", ec08160, 2, (RF, RF_IF), rd_rm),
+ cCL("acsd", ec08180, 2, (RF, RF_IF), rd_rm),
+ cCL("acsdp", ec081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("acsdm", ec081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("acsdz", ec081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("acse", ec88100, 2, (RF, RF_IF), rd_rm),
+ cCL("acsep", ec88120, 2, (RF, RF_IF), rd_rm),
+ cCL("acsem", ec88140, 2, (RF, RF_IF), rd_rm),
+ cCL("acsez", ec88160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("atns", ed08100, 2, (RF, RF_IF), rd_rm),
+ cCL("atnsp", ed08120, 2, (RF, RF_IF), rd_rm),
+ cCL("atnsm", ed08140, 2, (RF, RF_IF), rd_rm),
+ cCL("atnsz", ed08160, 2, (RF, RF_IF), rd_rm),
+ cCL("atnd", ed08180, 2, (RF, RF_IF), rd_rm),
+ cCL("atndp", ed081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("atndm", ed081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("atndz", ed081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("atne", ed88100, 2, (RF, RF_IF), rd_rm),
+ cCL("atnep", ed88120, 2, (RF, RF_IF), rd_rm),
+ cCL("atnem", ed88140, 2, (RF, RF_IF), rd_rm),
+ cCL("atnez", ed88160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("urds", ee08100, 2, (RF, RF_IF), rd_rm),
+ cCL("urdsp", ee08120, 2, (RF, RF_IF), rd_rm),
+ cCL("urdsm", ee08140, 2, (RF, RF_IF), rd_rm),
+ cCL("urdsz", ee08160, 2, (RF, RF_IF), rd_rm),
+ cCL("urdd", ee08180, 2, (RF, RF_IF), rd_rm),
+ cCL("urddp", ee081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("urddm", ee081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("urddz", ee081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("urde", ee88100, 2, (RF, RF_IF), rd_rm),
+ cCL("urdep", ee88120, 2, (RF, RF_IF), rd_rm),
+ cCL("urdem", ee88140, 2, (RF, RF_IF), rd_rm),
+ cCL("urdez", ee88160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("nrms", ef08100, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmsp", ef08120, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmsm", ef08140, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmsz", ef08160, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmd", ef08180, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmdp", ef081a0, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmdm", ef081c0, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmdz", ef081e0, 2, (RF, RF_IF), rd_rm),
+ cCL("nrme", ef88100, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmep", ef88120, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmem", ef88140, 2, (RF, RF_IF), rd_rm),
+ cCL("nrmez", ef88160, 2, (RF, RF_IF), rd_rm),
+
+ cCL("adfs", e000100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfsp", e000120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfsm", e000140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfsz", e000160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfd", e000180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfdp", e0001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfdm", e0001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfdz", e0001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfe", e080100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfep", e080120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfem", e080140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("adfez", e080160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("sufs", e200100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufsp", e200120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufsm", e200140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufsz", e200160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufd", e200180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufdp", e2001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufdm", e2001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufdz", e2001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufe", e280100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufep", e280120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufem", e280140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("sufez", e280160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("rsfs", e300100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfsp", e300120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfsm", e300140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfsz", e300160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfd", e300180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfdp", e3001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfdm", e3001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfdz", e3001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfe", e380100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfep", e380120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfem", e380140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rsfez", e380160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("mufs", e100100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufsp", e100120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufsm", e100140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufsz", e100160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufd", e100180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufdp", e1001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufdm", e1001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufdz", e1001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufe", e180100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufep", e180120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufem", e180140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("mufez", e180160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("dvfs", e400100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfsp", e400120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfsm", e400140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfsz", e400160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfd", e400180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfdp", e4001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfdm", e4001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfdz", e4001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfe", e480100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfep", e480120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfem", e480140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("dvfez", e480160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("rdfs", e500100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfsp", e500120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfsm", e500140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfsz", e500160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfd", e500180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfdp", e5001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfdm", e5001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfdz", e5001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfe", e580100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfep", e580120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfem", e580140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rdfez", e580160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("pows", e600100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powsp", e600120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powsm", e600140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powsz", e600160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powd", e600180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powdp", e6001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powdm", e6001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powdz", e6001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powe", e680100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powep", e680120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powem", e680140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("powez", e680160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("rpws", e700100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwsp", e700120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwsm", e700140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwsz", e700160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwd", e700180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwdp", e7001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwdm", e7001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwdz", e7001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwe", e780100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwep", e780120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwem", e780140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rpwez", e780160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("rmfs", e800100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfsp", e800120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfsm", e800140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfsz", e800160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfd", e800180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfdp", e8001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfdm", e8001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfdz", e8001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfe", e880100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfep", e880120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfem", e880140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("rmfez", e880160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("fmls", e900100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmlsp", e900120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmlsm", e900140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmlsz", e900160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmld", e900180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmldp", e9001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmldm", e9001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmldz", e9001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmle", e980100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmlep", e980120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmlem", e980140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fmlez", e980160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("fdvs", ea00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvsp", ea00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvsm", ea00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvsz", ea00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvd", ea00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvdp", ea001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvdm", ea001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvdz", ea001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdve", ea80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvep", ea80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvem", ea80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("fdvez", ea80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("frds", eb00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdsp", eb00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdsm", eb00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdsz", eb00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdd", eb00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frddp", eb001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frddm", eb001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frddz", eb001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frde", eb80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdep", eb80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdem", eb80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("frdez", eb80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCL("pols", ec00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("polsp", ec00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("polsm", ec00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("polsz", ec00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("pold", ec00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("poldp", ec001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("poldm", ec001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("poldz", ec001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("pole", ec80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("polep", ec80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("polem", ec80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ cCL("polez", ec80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ cCE("cmf", e90f110, 2, (RF, RF_IF), fpa_cmp),
+ C3E("cmfe", ed0f110, 2, (RF, RF_IF), fpa_cmp),
+ cCE("cnf", eb0f110, 2, (RF, RF_IF), fpa_cmp),
+ C3E("cnfe", ef0f110, 2, (RF, RF_IF), fpa_cmp),
+
+ cCL("flts", e000110, 2, (RF, RR), rn_rd),
+ cCL("fltsp", e000130, 2, (RF, RR), rn_rd),
+ cCL("fltsm", e000150, 2, (RF, RR), rn_rd),
+ cCL("fltsz", e000170, 2, (RF, RR), rn_rd),
+ cCL("fltd", e000190, 2, (RF, RR), rn_rd),
+ cCL("fltdp", e0001b0, 2, (RF, RR), rn_rd),
+ cCL("fltdm", e0001d0, 2, (RF, RR), rn_rd),
+ cCL("fltdz", e0001f0, 2, (RF, RR), rn_rd),
+ cCL("flte", e080110, 2, (RF, RR), rn_rd),
+ cCL("fltep", e080130, 2, (RF, RR), rn_rd),
+ cCL("fltem", e080150, 2, (RF, RR), rn_rd),
+ cCL("fltez", e080170, 2, (RF, RR), rn_rd),
+
+ /* The implementation of the FIX instruction is broken on some
+ assemblers, in that it accepts a precision specifier as well as a
+ rounding specifier, despite the fact that this is meaningless.
+ To be more compatible, we accept it as well, though of course it
+ does not set any bits. */
+ cCE("fix", e100110, 2, (RR, RF), rd_rm),
+ cCL("fixp", e100130, 2, (RR, RF), rd_rm),
+ cCL("fixm", e100150, 2, (RR, RF), rd_rm),
+ cCL("fixz", e100170, 2, (RR, RF), rd_rm),
+ cCL("fixsp", e100130, 2, (RR, RF), rd_rm),
+ cCL("fixsm", e100150, 2, (RR, RF), rd_rm),
+ cCL("fixsz", e100170, 2, (RR, RF), rd_rm),
+ cCL("fixdp", e100130, 2, (RR, RF), rd_rm),
+ cCL("fixdm", e100150, 2, (RR, RF), rd_rm),
+ cCL("fixdz", e100170, 2, (RR, RF), rd_rm),
+ cCL("fixep", e100130, 2, (RR, RF), rd_rm),
+ cCL("fixem", e100150, 2, (RR, RF), rd_rm),
+ cCL("fixez", e100170, 2, (RR, RF), rd_rm),
+
+ /* Instructions that were new with the real FPA, call them V2. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_fpa_ext_v2
+
+ cCE("lfm", c100200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ cCL("lfmfd", c900200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ cCL("lfmea", d100200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ cCE("sfm", c000200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ cCL("sfmfd", d000200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ cCL("sfmea", c800200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v1xd /* VFP V1xD (single precision). */
+
+ /* Moves and type conversions. */
+ cCE("fcpys", eb00a40, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fmrs", e100a10, 2, (RR, RVS), vfp_reg_from_sp),
+ cCE("fmsr", e000a10, 2, (RVS, RR), vfp_sp_from_reg),
+ cCE("fmstat", ef1fa10, 0, (), noargs),
+ cCE("vmrs", ef00a10, 2, (APSR_RR, RVC), vmrs),
+ cCE("vmsr", ee00a10, 2, (RVC, RR), vmsr),
+ cCE("fsitos", eb80ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fuitos", eb80a40, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("ftosis", ebd0a40, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("ftosizs", ebd0ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("ftouis", ebc0a40, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("ftouizs", ebc0ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fmrx", ef00a10, 2, (RR, RVC), rd_rn),
+ cCE("fmxr", ee00a10, 2, (RVC, RR), rn_rd),
+
+ /* Memory operations. */
+ cCE("flds", d100a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst),
+ cCE("fsts", d000a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst),
+ cCE("fldmias", c900a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia),
+ cCE("fldmfds", c900a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia),
+ cCE("fldmdbs", d300a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb),
+ cCE("fldmeas", d300a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb),
+ cCE("fldmiax", c900b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia),
+ cCE("fldmfdx", c900b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia),
+ cCE("fldmdbx", d300b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb),
+ cCE("fldmeax", d300b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb),
+ cCE("fstmias", c800a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia),
+ cCE("fstmeas", c800a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia),
+ cCE("fstmdbs", d200a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb),
+ cCE("fstmfds", d200a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb),
+ cCE("fstmiax", c800b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia),
+ cCE("fstmeax", c800b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia),
+ cCE("fstmdbx", d200b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb),
+ cCE("fstmfdx", d200b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb),
+
+ /* Monadic operations. */
+ cCE("fabss", eb00ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fnegs", eb10a40, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fsqrts", eb10ac0, 2, (RVS, RVS), vfp_sp_monadic),
+
+ /* Dyadic operations. */
+ cCE("fadds", e300a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fsubs", e300a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fmuls", e200a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fdivs", e800a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fmacs", e000a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fmscs", e100a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fnmuls", e200a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fnmacs", e000a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("fnmscs", e100a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+
+ /* Comparisons. */
+ cCE("fcmps", eb40a40, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fcmpzs", eb50a40, 1, (RVS), vfp_sp_compare_z),
+ cCE("fcmpes", eb40ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ cCE("fcmpezs", eb50ac0, 1, (RVS), vfp_sp_compare_z),
+
+ /* Double precision load/store are still present on single precision
+ implementations. */
+ cCE("fldd", d100b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst),
+ cCE("fstd", d000b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst),
+ cCE("fldmiad", c900b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia),
+ cCE("fldmfdd", c900b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia),
+ cCE("fldmdbd", d300b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb),
+ cCE("fldmead", d300b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb),
+ cCE("fstmiad", c800b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia),
+ cCE("fstmead", c800b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia),
+ cCE("fstmdbd", d200b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb),
+ cCE("fstmfdd", d200b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v1 /* VFP V1 (Double precision). */
+
+ /* Moves and type conversions. */
+ cCE("fcpyd", eb00b40, 2, (RVD, RVD), vfp_dp_rd_rm),
+ cCE("fcvtds", eb70ac0, 2, (RVD, RVS), vfp_dp_sp_cvt),
+ cCE("fcvtsd", eb70bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ cCE("fmdhr", e200b10, 2, (RVD, RR), vfp_dp_rn_rd),
+ cCE("fmdlr", e000b10, 2, (RVD, RR), vfp_dp_rn_rd),
+ cCE("fmrdh", e300b10, 2, (RR, RVD), vfp_dp_rd_rn),
+ cCE("fmrdl", e100b10, 2, (RR, RVD), vfp_dp_rd_rn),
+ cCE("fsitod", eb80bc0, 2, (RVD, RVS), vfp_dp_sp_cvt),
+ cCE("fuitod", eb80b40, 2, (RVD, RVS), vfp_dp_sp_cvt),
+ cCE("ftosid", ebd0b40, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ cCE("ftosizd", ebd0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ cCE("ftouid", ebc0b40, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ cCE("ftouizd", ebc0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
+
+ /* Monadic operations. */
+ cCE("fabsd", eb00bc0, 2, (RVD, RVD), vfp_dp_rd_rm),
+ cCE("fnegd", eb10b40, 2, (RVD, RVD), vfp_dp_rd_rm),
+ cCE("fsqrtd", eb10bc0, 2, (RVD, RVD), vfp_dp_rd_rm),
+
+ /* Dyadic operations. */
+ cCE("faddd", e300b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fsubd", e300b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fmuld", e200b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fdivd", e800b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fmacd", e000b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fmscd", e100b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fnmuld", e200b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fnmacd", e000b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("fnmscd", e100b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+
+ /* Comparisons. */
+ cCE("fcmpd", eb40b40, 2, (RVD, RVD), vfp_dp_rd_rm),
+ cCE("fcmpzd", eb50b40, 1, (RVD), vfp_dp_rd),
+ cCE("fcmped", eb40bc0, 2, (RVD, RVD), vfp_dp_rd_rm),
+ cCE("fcmpezd", eb50bc0, 1, (RVD), vfp_dp_rd),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v2
+
+ cCE("fmsrr", c400a10, 3, (VRSLST, RR, RR), vfp_sp2_from_reg2),
+ cCE("fmrrs", c500a10, 3, (RR, RR, VRSLST), vfp_reg2_from_sp2),
+ cCE("fmdrr", c400b10, 3, (RVD, RR, RR), vfp_dp_rm_rd_rn),
+ cCE("fmrrd", c500b10, 3, (RR, RR, RVD), vfp_dp_rd_rn_rm),
+
+/* Instructions which may belong to either the Neon or VFP instruction sets.
+ Individual encoder functions perform additional architecture checks. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v1xd
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_v1xd
+
+ /* These mnemonics are unique to VFP. */
+ NCE(vsqrt, 0, 2, (RVSD, RVSD), vfp_nsyn_sqrt),
+ NCE(vdiv, 0, 3, (RVSD, RVSD, RVSD), vfp_nsyn_div),
+ nCE(vnmul, _vnmul, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
+ nCE(vnmla, _vnmla, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
+ nCE(vnmls, _vnmls, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
+ nCE(vcmp, _vcmp, 2, (RVSD, RSVD_FI0), vfp_nsyn_cmp),
+ nCE(vcmpe, _vcmpe, 2, (RVSD, RSVD_FI0), vfp_nsyn_cmp),
+ NCE(vpush, 0, 1, (VRSDLST), vfp_nsyn_push),
+ NCE(vpop, 0, 1, (VRSDLST), vfp_nsyn_pop),
+ NCE(vcvtz, 0, 2, (RVSD, RVSD), vfp_nsyn_cvtz),
+
+ /* Mnemonics shared by Neon and VFP. */
+ nCEF(vmul, _vmul, 3, (RNSDQ, oRNSDQ, RNSDQ_RNSC), neon_mul),
+ nCEF(vmla, _vmla, 3, (RNSDQ, oRNSDQ, RNSDQ_RNSC), neon_mac_maybe_scalar),
+ nCEF(vmls, _vmls, 3, (RNSDQ, oRNSDQ, RNSDQ_RNSC), neon_mac_maybe_scalar),
+
+ nCEF(vadd, _vadd, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_addsub_if_i),
+ nCEF(vsub, _vsub, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_addsub_if_i),
+
+ NCEF(vabs, 1b10300, 2, (RNSDQ, RNSDQ), neon_abs_neg),
+ NCEF(vneg, 1b10380, 2, (RNSDQ, RNSDQ), neon_abs_neg),
+
+ NCE(vldm, c900b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm),
+ NCE(vldmia, c900b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm),
+ NCE(vldmdb, d100b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm),
+ NCE(vstm, c800b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm),
+ NCE(vstmia, c800b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm),
+ NCE(vstmdb, d000b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm),
+ NCE(vldr, d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
+ NCE(vstr, d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
+
+ nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32z), neon_cvt),
+ nCEF(vcvtr, _vcvt, 2, (RNSDQ, RNSDQ), neon_cvtr),
+ NCEF(vcvtb, eb20a40, 2, (RVSD, RVSD), neon_cvtb),
+ NCEF(vcvtt, eb20a40, 2, (RVSD, RVSD), neon_cvtt),
+
+
+ /* NOTE: All VMOV encoding is special-cased! */
+ NCE(vmov, 0, 1, (VMOV), neon_mov),
+ NCE(vmovq, 0, 1, (VMOV), neon_mov),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_neon_ext_v1
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_neon_ext_v1
+
+ /* Data processing with three registers of the same length. */
+ /* integer ops, valid types S8 S16 S32 U8 U16 U32. */
+ NUF(vaba, 0000710, 3, (RNDQ, RNDQ, RNDQ), neon_dyadic_i_su),
+ NUF(vabaq, 0000710, 3, (RNQ, RNQ, RNQ), neon_dyadic_i_su),
+ NUF(vhadd, 0000000, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i_su),
+ NUF(vhaddq, 0000000, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i_su),
+ NUF(vrhadd, 0000100, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i_su),
+ NUF(vrhaddq, 0000100, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i_su),
+ NUF(vhsub, 0000200, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i_su),
+ NUF(vhsubq, 0000200, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i_su),
+ /* integer ops, valid types S8 S16 S32 S64 U8 U16 U32 U64. */
+ NUF(vqadd, 0000010, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i64_su),
+ NUF(vqaddq, 0000010, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i64_su),
+ NUF(vqsub, 0000210, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_i64_su),
+ NUF(vqsubq, 0000210, 3, (RNQ, oRNQ, RNQ), neon_dyadic_i64_su),
+ NUF(vrshl, 0000500, 3, (RNDQ, oRNDQ, RNDQ), neon_rshl),
+ NUF(vrshlq, 0000500, 3, (RNQ, oRNQ, RNQ), neon_rshl),
+ NUF(vqrshl, 0000510, 3, (RNDQ, oRNDQ, RNDQ), neon_rshl),
+ NUF(vqrshlq, 0000510, 3, (RNQ, oRNQ, RNQ), neon_rshl),
+ /* If not immediate, fall back to neon_dyadic_i64_su.
+ shl_imm should accept I8 I16 I32 I64,
+ qshl_imm should accept S8 S16 S32 S64 U8 U16 U32 U64. */
+ nUF(vshl, _vshl, 3, (RNDQ, oRNDQ, RNDQ_I63b), neon_shl_imm),
+ nUF(vshlq, _vshl, 3, (RNQ, oRNQ, RNDQ_I63b), neon_shl_imm),
+ nUF(vqshl, _vqshl, 3, (RNDQ, oRNDQ, RNDQ_I63b), neon_qshl_imm),
+ nUF(vqshlq, _vqshl, 3, (RNQ, oRNQ, RNDQ_I63b), neon_qshl_imm),
+ /* Logic ops, types optional & ignored. */
+ nUF(vand, _vand, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic),
+ nUF(vandq, _vand, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic),
+ nUF(vbic, _vbic, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic),
+ nUF(vbicq, _vbic, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic),
+ nUF(vorr, _vorr, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic),
+ nUF(vorrq, _vorr, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic),
+ nUF(vorn, _vorn, 3, (RNDQ, oRNDQ, RNDQ_Ibig), neon_logic),
+ nUF(vornq, _vorn, 3, (RNQ, oRNQ, RNDQ_Ibig), neon_logic),
+ nUF(veor, _veor, 3, (RNDQ, oRNDQ, RNDQ), neon_logic),
+ nUF(veorq, _veor, 3, (RNQ, oRNQ, RNQ), neon_logic),
+ /* Bitfield ops, untyped. */
+ NUF(vbsl, 1100110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield),
+ NUF(vbslq, 1100110, 3, (RNQ, RNQ, RNQ), neon_bitfield),
+ NUF(vbit, 1200110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield),
+ NUF(vbitq, 1200110, 3, (RNQ, RNQ, RNQ), neon_bitfield),
+ NUF(vbif, 1300110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield),
+ NUF(vbifq, 1300110, 3, (RNQ, RNQ, RNQ), neon_bitfield),
+ /* Int and float variants, types S8 S16 S32 U8 U16 U32 F32. */
+ nUF(vabd, _vabd, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su),
+ nUF(vabdq, _vabd, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su),
+ nUF(vmax, _vmax, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su),
+ nUF(vmaxq, _vmax, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su),
+ nUF(vmin, _vmin, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su),
+ nUF(vminq, _vmin, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su),
+ /* Comparisons. Types S8 S16 S32 U8 U16 U32 F32. Non-immediate versions fall
+ back to neon_dyadic_if_su. */
+ nUF(vcge, _vcge, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp),
+ nUF(vcgeq, _vcge, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp),
+ nUF(vcgt, _vcgt, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp),
+ nUF(vcgtq, _vcgt, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp),
+ nUF(vclt, _vclt, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp_inv),
+ nUF(vcltq, _vclt, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp_inv),
+ nUF(vcle, _vcle, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp_inv),
+ nUF(vcleq, _vcle, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp_inv),
+ /* Comparison. Type I8 I16 I32 F32. */
+ nUF(vceq, _vceq, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_ceq),
+ nUF(vceqq, _vceq, 3, (RNQ, oRNQ, RNDQ_I0), neon_ceq),
+ /* As above, D registers only. */
+ nUF(vpmax, _vpmax, 3, (RND, oRND, RND), neon_dyadic_if_su_d),
+ nUF(vpmin, _vpmin, 3, (RND, oRND, RND), neon_dyadic_if_su_d),
+ /* Int and float variants, signedness unimportant. */
+ nUF(vmlaq, _vmla, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mac_maybe_scalar),
+ nUF(vmlsq, _vmls, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mac_maybe_scalar),
+ nUF(vpadd, _vpadd, 3, (RND, oRND, RND), neon_dyadic_if_i_d),
+ /* Add/sub take types I8 I16 I32 I64 F32. */
+ nUF(vaddq, _vadd, 3, (RNQ, oRNQ, RNQ), neon_addsub_if_i),
+ nUF(vsubq, _vsub, 3, (RNQ, oRNQ, RNQ), neon_addsub_if_i),
+ /* vtst takes sizes 8, 16, 32. */
+ NUF(vtst, 0000810, 3, (RNDQ, oRNDQ, RNDQ), neon_tst),
+ NUF(vtstq, 0000810, 3, (RNQ, oRNQ, RNQ), neon_tst),
+ /* VMUL takes I8 I16 I32 F32 P8. */
+ nUF(vmulq, _vmul, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mul),
+ /* VQD{R}MULH takes S16 S32. */
+ nUF(vqdmulh, _vqdmulh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qdmulh),
+ nUF(vqdmulhq, _vqdmulh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qdmulh),
+ nUF(vqrdmulh, _vqrdmulh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qdmulh),
+ nUF(vqrdmulhq, _vqrdmulh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qdmulh),
+ NUF(vacge, 0000e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute),
+ NUF(vacgeq, 0000e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute),
+ NUF(vacgt, 0200e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute),
+ NUF(vacgtq, 0200e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute),
+ NUF(vaclt, 0200e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute_inv),
+ NUF(vacltq, 0200e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute_inv),
+ NUF(vacle, 0000e10, 3, (RNDQ, oRNDQ, RNDQ), neon_fcmp_absolute_inv),
+ NUF(vacleq, 0000e10, 3, (RNQ, oRNQ, RNQ), neon_fcmp_absolute_inv),
+ NUF(vrecps, 0000f10, 3, (RNDQ, oRNDQ, RNDQ), neon_step),
+ NUF(vrecpsq, 0000f10, 3, (RNQ, oRNQ, RNQ), neon_step),
+ NUF(vrsqrts, 0200f10, 3, (RNDQ, oRNDQ, RNDQ), neon_step),
+ NUF(vrsqrtsq, 0200f10, 3, (RNQ, oRNQ, RNQ), neon_step),
+
+ /* Two address, int/float. Types S8 S16 S32 F32. */
+ NUF(vabsq, 1b10300, 2, (RNQ, RNQ), neon_abs_neg),
+ NUF(vnegq, 1b10380, 2, (RNQ, RNQ), neon_abs_neg),
+
+ /* Data processing with two registers and a shift amount. */
+ /* Right shifts, and variants with rounding.
+ Types accepted S8 S16 S32 S64 U8 U16 U32 U64. */
+ NUF(vshr, 0800010, 3, (RNDQ, oRNDQ, I64z), neon_rshift_round_imm),
+ NUF(vshrq, 0800010, 3, (RNQ, oRNQ, I64z), neon_rshift_round_imm),
+ NUF(vrshr, 0800210, 3, (RNDQ, oRNDQ, I64z), neon_rshift_round_imm),
+ NUF(vrshrq, 0800210, 3, (RNQ, oRNQ, I64z), neon_rshift_round_imm),
+ NUF(vsra, 0800110, 3, (RNDQ, oRNDQ, I64), neon_rshift_round_imm),
+ NUF(vsraq, 0800110, 3, (RNQ, oRNQ, I64), neon_rshift_round_imm),
+ NUF(vrsra, 0800310, 3, (RNDQ, oRNDQ, I64), neon_rshift_round_imm),
+ NUF(vrsraq, 0800310, 3, (RNQ, oRNQ, I64), neon_rshift_round_imm),
+ /* Shift and insert. Sizes accepted 8 16 32 64. */
+ NUF(vsli, 1800510, 3, (RNDQ, oRNDQ, I63), neon_sli),
+ NUF(vsliq, 1800510, 3, (RNQ, oRNQ, I63), neon_sli),
+ NUF(vsri, 1800410, 3, (RNDQ, oRNDQ, I64), neon_sri),
+ NUF(vsriq, 1800410, 3, (RNQ, oRNQ, I64), neon_sri),
+ /* QSHL{U} immediate accepts S8 S16 S32 S64 U8 U16 U32 U64. */
+ NUF(vqshlu, 1800610, 3, (RNDQ, oRNDQ, I63), neon_qshlu_imm),
+ NUF(vqshluq, 1800610, 3, (RNQ, oRNQ, I63), neon_qshlu_imm),
+ /* Right shift immediate, saturating & narrowing, with rounding variants.
+ Types accepted S16 S32 S64 U16 U32 U64. */
+ NUF(vqshrn, 0800910, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow),
+ NUF(vqrshrn, 0800950, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow),
+ /* As above, unsigned. Types accepted S16 S32 S64. */
+ NUF(vqshrun, 0800810, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow_u),
+ NUF(vqrshrun, 0800850, 3, (RND, RNQ, I32z), neon_rshift_sat_narrow_u),
+ /* Right shift narrowing. Types accepted I16 I32 I64. */
+ NUF(vshrn, 0800810, 3, (RND, RNQ, I32z), neon_rshift_narrow),
+ NUF(vrshrn, 0800850, 3, (RND, RNQ, I32z), neon_rshift_narrow),
+ /* Special case. Types S8 S16 S32 U8 U16 U32. Handles max shift variant. */
+ nUF(vshll, _vshll, 3, (RNQ, RND, I32), neon_shll),
+ /* CVT with optional immediate for fixed-point variant. */
+ nUF(vcvtq, _vcvt, 3, (RNQ, RNQ, oI32b), neon_cvt),
+
+ nUF(vmvn, _vmvn, 2, (RNDQ, RNDQ_Ibig), neon_mvn),
+ nUF(vmvnq, _vmvn, 2, (RNQ, RNDQ_Ibig), neon_mvn),
+
+ /* Data processing, three registers of different lengths. */
+ /* Dyadic, long insns. Types S8 S16 S32 U8 U16 U32. */
+ NUF(vabal, 0800500, 3, (RNQ, RND, RND), neon_abal),
+ NUF(vabdl, 0800700, 3, (RNQ, RND, RND), neon_dyadic_long),
+ NUF(vaddl, 0800000, 3, (RNQ, RND, RND), neon_dyadic_long),
+ NUF(vsubl, 0800200, 3, (RNQ, RND, RND), neon_dyadic_long),
+ /* If not scalar, fall back to neon_dyadic_long.
+ Vector types as above, scalar types S16 S32 U16 U32. */
+ nUF(vmlal, _vmlal, 3, (RNQ, RND, RND_RNSC), neon_mac_maybe_scalar_long),
+ nUF(vmlsl, _vmlsl, 3, (RNQ, RND, RND_RNSC), neon_mac_maybe_scalar_long),
+ /* Dyadic, widening insns. Types S8 S16 S32 U8 U16 U32. */
+ NUF(vaddw, 0800100, 3, (RNQ, oRNQ, RND), neon_dyadic_wide),
+ NUF(vsubw, 0800300, 3, (RNQ, oRNQ, RND), neon_dyadic_wide),
+ /* Dyadic, narrowing insns. Types I16 I32 I64. */
+ NUF(vaddhn, 0800400, 3, (RND, RNQ, RNQ), neon_dyadic_narrow),
+ NUF(vraddhn, 1800400, 3, (RND, RNQ, RNQ), neon_dyadic_narrow),
+ NUF(vsubhn, 0800600, 3, (RND, RNQ, RNQ), neon_dyadic_narrow),
+ NUF(vrsubhn, 1800600, 3, (RND, RNQ, RNQ), neon_dyadic_narrow),
+ /* Saturating doubling multiplies. Types S16 S32. */
+ nUF(vqdmlal, _vqdmlal, 3, (RNQ, RND, RND_RNSC), neon_mul_sat_scalar_long),
+ nUF(vqdmlsl, _vqdmlsl, 3, (RNQ, RND, RND_RNSC), neon_mul_sat_scalar_long),
+ nUF(vqdmull, _vqdmull, 3, (RNQ, RND, RND_RNSC), neon_mul_sat_scalar_long),
+ /* VMULL. Vector types S8 S16 S32 U8 U16 U32 P8, scalar types
+ S16 S32 U16 U32. */
+ nUF(vmull, _vmull, 3, (RNQ, RND, RND_RNSC), neon_vmull),
+
+ /* Extract. Size 8. */
+ NUF(vext, 0b00000, 4, (RNDQ, oRNDQ, RNDQ, I15), neon_ext),
+ NUF(vextq, 0b00000, 4, (RNQ, oRNQ, RNQ, I15), neon_ext),
+
+ /* Two registers, miscellaneous. */
+ /* Reverse. Sizes 8 16 32 (must be < size in opcode). */
+ NUF(vrev64, 1b00000, 2, (RNDQ, RNDQ), neon_rev),
+ NUF(vrev64q, 1b00000, 2, (RNQ, RNQ), neon_rev),
+ NUF(vrev32, 1b00080, 2, (RNDQ, RNDQ), neon_rev),
+ NUF(vrev32q, 1b00080, 2, (RNQ, RNQ), neon_rev),
+ NUF(vrev16, 1b00100, 2, (RNDQ, RNDQ), neon_rev),
+ NUF(vrev16q, 1b00100, 2, (RNQ, RNQ), neon_rev),
+ /* Vector replicate. Sizes 8 16 32. */
+ nCE(vdup, _vdup, 2, (RNDQ, RR_RNSC), neon_dup),
+ nCE(vdupq, _vdup, 2, (RNQ, RR_RNSC), neon_dup),
+ /* VMOVL. Types S8 S16 S32 U8 U16 U32. */
+ NUF(vmovl, 0800a10, 2, (RNQ, RND), neon_movl),
+ /* VMOVN. Types I16 I32 I64. */
+ nUF(vmovn, _vmovn, 2, (RND, RNQ), neon_movn),
+ /* VQMOVN. Types S16 S32 S64 U16 U32 U64. */
+ nUF(vqmovn, _vqmovn, 2, (RND, RNQ), neon_qmovn),
+ /* VQMOVUN. Types S16 S32 S64. */
+ nUF(vqmovun, _vqmovun, 2, (RND, RNQ), neon_qmovun),
+ /* VZIP / VUZP. Sizes 8 16 32. */
+ NUF(vzip, 1b20180, 2, (RNDQ, RNDQ), neon_zip_uzp),
+ NUF(vzipq, 1b20180, 2, (RNQ, RNQ), neon_zip_uzp),
+ NUF(vuzp, 1b20100, 2, (RNDQ, RNDQ), neon_zip_uzp),
+ NUF(vuzpq, 1b20100, 2, (RNQ, RNQ), neon_zip_uzp),
+ /* VQABS / VQNEG. Types S8 S16 S32. */
+ NUF(vqabs, 1b00700, 2, (RNDQ, RNDQ), neon_sat_abs_neg),
+ NUF(vqabsq, 1b00700, 2, (RNQ, RNQ), neon_sat_abs_neg),
+ NUF(vqneg, 1b00780, 2, (RNDQ, RNDQ), neon_sat_abs_neg),
+ NUF(vqnegq, 1b00780, 2, (RNQ, RNQ), neon_sat_abs_neg),
+ /* Pairwise, lengthening. Types S8 S16 S32 U8 U16 U32. */
+ NUF(vpadal, 1b00600, 2, (RNDQ, RNDQ), neon_pair_long),
+ NUF(vpadalq, 1b00600, 2, (RNQ, RNQ), neon_pair_long),
+ NUF(vpaddl, 1b00200, 2, (RNDQ, RNDQ), neon_pair_long),
+ NUF(vpaddlq, 1b00200, 2, (RNQ, RNQ), neon_pair_long),
+ /* Reciprocal estimates. Types U32 F32. */
+ NUF(vrecpe, 1b30400, 2, (RNDQ, RNDQ), neon_recip_est),
+ NUF(vrecpeq, 1b30400, 2, (RNQ, RNQ), neon_recip_est),
+ NUF(vrsqrte, 1b30480, 2, (RNDQ, RNDQ), neon_recip_est),
+ NUF(vrsqrteq, 1b30480, 2, (RNQ, RNQ), neon_recip_est),
+ /* VCLS. Types S8 S16 S32. */
+ NUF(vcls, 1b00400, 2, (RNDQ, RNDQ), neon_cls),
+ NUF(vclsq, 1b00400, 2, (RNQ, RNQ), neon_cls),
+ /* VCLZ. Types I8 I16 I32. */
+ NUF(vclz, 1b00480, 2, (RNDQ, RNDQ), neon_clz),
+ NUF(vclzq, 1b00480, 2, (RNQ, RNQ), neon_clz),
+ /* VCNT. Size 8. */
+ NUF(vcnt, 1b00500, 2, (RNDQ, RNDQ), neon_cnt),
+ NUF(vcntq, 1b00500, 2, (RNQ, RNQ), neon_cnt),
+ /* Two address, untyped. */
+ NUF(vswp, 1b20000, 2, (RNDQ, RNDQ), neon_swp),
+ NUF(vswpq, 1b20000, 2, (RNQ, RNQ), neon_swp),
+ /* VTRN. Sizes 8 16 32. */
+ nUF(vtrn, _vtrn, 2, (RNDQ, RNDQ), neon_trn),
+ nUF(vtrnq, _vtrn, 2, (RNQ, RNQ), neon_trn),
+
+ /* Table lookup. Size 8. */
+ NUF(vtbl, 1b00800, 3, (RND, NRDLST, RND), neon_tbl_tbx),
+ NUF(vtbx, 1b00840, 3, (RND, NRDLST, RND), neon_tbl_tbx),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_v3_or_neon_ext
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_v3_or_neon_ext
+
+ /* Neon element/structure load/store. */
+ nUF(vld1, _vld1, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vst1, _vst1, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vld2, _vld2, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vst2, _vst2, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vld3, _vld3, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vst3, _vst3, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vld4, _vld4, 2, (NSTRLST, ADDR), neon_ldx_stx),
+ nUF(vst4, _vst4, 2, (NSTRLST, ADDR), neon_ldx_stx),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_v3xd
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v3xd
+ cCE("fconsts", eb00a00, 2, (RVS, I255), vfp_sp_const),
+ cCE("fshtos", eba0a40, 2, (RVS, I16z), vfp_sp_conv_16),
+ cCE("fsltos", eba0ac0, 2, (RVS, I32), vfp_sp_conv_32),
+ cCE("fuhtos", ebb0a40, 2, (RVS, I16z), vfp_sp_conv_16),
+ cCE("fultos", ebb0ac0, 2, (RVS, I32), vfp_sp_conv_32),
+ cCE("ftoshs", ebe0a40, 2, (RVS, I16z), vfp_sp_conv_16),
+ cCE("ftosls", ebe0ac0, 2, (RVS, I32), vfp_sp_conv_32),
+ cCE("ftouhs", ebf0a40, 2, (RVS, I16z), vfp_sp_conv_16),
+ cCE("ftouls", ebf0ac0, 2, (RVS, I32), vfp_sp_conv_32),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_v3
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v3
+
+ cCE("fconstd", eb00b00, 2, (RVD, I255), vfp_dp_const),
+ cCE("fshtod", eba0b40, 2, (RVD, I16z), vfp_dp_conv_16),
+ cCE("fsltod", eba0bc0, 2, (RVD, I32), vfp_dp_conv_32),
+ cCE("fuhtod", ebb0b40, 2, (RVD, I16z), vfp_dp_conv_16),
+ cCE("fultod", ebb0bc0, 2, (RVD, I32), vfp_dp_conv_32),
+ cCE("ftoshd", ebe0b40, 2, (RVD, I16z), vfp_dp_conv_16),
+ cCE("ftosld", ebe0bc0, 2, (RVD, I32), vfp_dp_conv_32),
+ cCE("ftouhd", ebf0b40, 2, (RVD, I16z), vfp_dp_conv_16),
+ cCE("ftould", ebf0bc0, 2, (RVD, I32), vfp_dp_conv_32),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_fma
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_fma
+ /* Mnemonics shared by Neon and VFP. These are included in the
+ VFP FMA variant; NEON and VFP FMA always includes the NEON
+ FMA instructions. */
+ nCEF(vfma, _vfma, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_fmac),
+ nCEF(vfms, _vfms, 3, (RNSDQ, oRNSDQ, RNSDQ), neon_fmac),
+ /* ffmas/ffmad/ffmss/ffmsd are dummy mnemonics to satisfy gas;
+ the v form should always be used. */
+ cCE("ffmas", ea00a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("ffnmas", ea00a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ cCE("ffmad", ea00b00, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ cCE("ffnmad", ea00b40, 3, (RVD, RVD, RVD), vfp_dp_rd_rn_rm),
+ nCE(vfnma, _vfnma, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
+ nCE(vfnms, _vfnms, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
+
+#undef THUMB_VARIANT
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_cext_xscale /* Intel XScale extensions. */
+
+ cCE("mia", e200010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ cCE("miaph", e280010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ cCE("miabb", e2c0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ cCE("miabt", e2d0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ cCE("miatb", e2e0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ cCE("miatt", e2f0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ cCE("mar", c400000, 3, (RXA, RRnpc, RRnpc), xsc_mar),
+ cCE("mra", c500000, 3, (RRnpc, RRnpc, RXA), xsc_mra),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_cext_iwmmxt /* Intel Wireless MMX technology. */
+
+ cCE("tandcb", e13f130, 1, (RR), iwmmxt_tandorc),
+ cCE("tandch", e53f130, 1, (RR), iwmmxt_tandorc),
+ cCE("tandcw", e93f130, 1, (RR), iwmmxt_tandorc),
+ cCE("tbcstb", e400010, 2, (RIWR, RR), rn_rd),
+ cCE("tbcsth", e400050, 2, (RIWR, RR), rn_rd),
+ cCE("tbcstw", e400090, 2, (RIWR, RR), rn_rd),
+ cCE("textrcb", e130170, 2, (RR, I7), iwmmxt_textrc),
+ cCE("textrch", e530170, 2, (RR, I7), iwmmxt_textrc),
+ cCE("textrcw", e930170, 2, (RR, I7), iwmmxt_textrc),
+ cCE("textrmub",e100070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmuh",e500070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmuw",e900070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmsb",e100078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmsh",e500078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmsw",e900078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("tinsrb", e600010, 3, (RIWR, RR, I7), iwmmxt_tinsr),
+ cCE("tinsrh", e600050, 3, (RIWR, RR, I7), iwmmxt_tinsr),
+ cCE("tinsrw", e600090, 3, (RIWR, RR, I7), iwmmxt_tinsr),
+ cCE("tmcr", e000110, 2, (RIWC_RIWG, RR), rn_rd),
+ cCE("tmcrr", c400000, 3, (RIWR, RR, RR), rm_rd_rn),
+ cCE("tmia", e200010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ cCE("tmiaph", e280010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ cCE("tmiabb", e2c0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ cCE("tmiabt", e2d0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ cCE("tmiatb", e2e0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ cCE("tmiatt", e2f0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ cCE("tmovmskb",e100030, 2, (RR, RIWR), rd_rn),
+ cCE("tmovmskh",e500030, 2, (RR, RIWR), rd_rn),
+ cCE("tmovmskw",e900030, 2, (RR, RIWR), rd_rn),
+ cCE("tmrc", e100110, 2, (RR, RIWC_RIWG), rd_rn),
+ cCE("tmrrc", c500000, 3, (RR, RR, RIWR), rd_rn_rm),
+ cCE("torcb", e13f150, 1, (RR), iwmmxt_tandorc),
+ cCE("torch", e53f150, 1, (RR), iwmmxt_tandorc),
+ cCE("torcw", e93f150, 1, (RR), iwmmxt_tandorc),
+ cCE("waccb", e0001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wacch", e4001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("waccw", e8001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("waddbss", e300180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddb", e000180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddbus", e100180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddhss", e700180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddh", e400180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddhus", e500180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddwss", eb00180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddw", e800180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddwus", e900180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waligni", e000020, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_waligni),
+ cCE("walignr0",e800020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr1",e900020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr2",ea00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr3",eb00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wand", e200000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wandn", e300000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wavg2b", e800000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wavg2br", e900000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wavg2h", ec00000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wavg2hr", ed00000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpeqb", e000060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpeqh", e400060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpeqw", e800060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtub",e100060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtuh",e500060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtuw",e900060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtsb",e300060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtsh",e700060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtsw",eb00060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wldrb", c100000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ cCE("wldrh", c500000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ cCE("wldrw", c100100, 2, (RIWR_RIWC, ADDR), iwmmxt_wldstw),
+ cCE("wldrd", c500100, 2, (RIWR, ADDR), iwmmxt_wldstd),
+ cCE("wmacs", e600100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmacsz", e700100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmacu", e400100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmacuz", e500100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmadds", ea00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaddu", e800100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaxsb", e200160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaxsh", e600160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaxsw", ea00160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaxub", e000160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaxuh", e400160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaxuw", e800160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wminsb", e300160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wminsh", e700160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wminsw", eb00160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wminub", e100160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wminuh", e500160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wminuw", e900160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmov", e000000, 2, (RIWR, RIWR), iwmmxt_wmov),
+ cCE("wmulsm", e300100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulsl", e200100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulum", e100100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulul", e000100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wor", e000000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackhss",e700080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackhus",e500080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackwss",eb00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackwus",e900080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackdss",ef00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackdus",ed00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wrorh", e700040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wrorhg", e700148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wrorw", eb00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wrorwg", eb00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wrord", ef00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wrordg", ef00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsadb", e000120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsadbz", e100120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsadh", e400120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsadhz", e500120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wshufh", e0001e0, 3, (RIWR, RIWR, I255), iwmmxt_wshufh),
+ cCE("wsllh", e500040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsllhg", e500148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsllw", e900040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsllwg", e900148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wslld", ed00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wslldg", ed00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsrah", e400040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsrahg", e400148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsraw", e800040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsrawg", e800148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsrad", ec00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsradg", ec00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsrlh", e600040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsrlhg", e600148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsrlw", ea00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsrlwg", ea00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wsrld", ee00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
+ cCE("wsrldg", ee00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ cCE("wstrb", c000000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ cCE("wstrh", c400000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ cCE("wstrw", c000100, 2, (RIWR_RIWC, ADDR), iwmmxt_wldstw),
+ cCE("wstrd", c400100, 2, (RIWR, ADDR), iwmmxt_wldstd),
+ cCE("wsubbss", e3001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubb", e0001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubbus", e1001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubhss", e7001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubh", e4001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubhus", e5001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubwss", eb001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubw", e8001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubwus", e9001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wunpckehub",e0000c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckehuh",e4000c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckehuw",e8000c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckehsb",e2000c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckehsh",e6000c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckehsw",ea000c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckihb", e1000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wunpckihh", e5000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wunpckihw", e9000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wunpckelub",e0000e0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckeluh",e4000e0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckeluw",e8000e0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckelsb",e2000e0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckelsh",e6000e0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckelsw",ea000e0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wunpckilb", e1000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wunpckilh", e5000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wunpckilw", e9000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wxor", e100000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wzero", e300000, 1, (RIWR), iwmmxt_wzero),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_cext_iwmmxt2 /* Intel Wireless MMX technology, version 2. */
+
+ cCE("torvscb", e12f190, 1, (RR), iwmmxt_tandorc),
+ cCE("torvsch", e52f190, 1, (RR), iwmmxt_tandorc),
+ cCE("torvscw", e92f190, 1, (RR), iwmmxt_tandorc),
+ cCE("wabsb", e2001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wabsh", e6001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wabsw", ea001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE("wabsdiffb", e1001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wabsdiffh", e5001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wabsdiffw", e9001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddbhusl", e2001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddbhusm", e6001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddhc", e600180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddwc", ea00180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("waddsubhx", ea001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wavg4", e400000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wavg4r", e500000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaddsn", ee00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaddsx", eb00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaddun", ec00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmaddux", e900100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmerge", e000080, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_wmerge),
+ cCE("wmiabb", e0000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiabt", e1000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiatb", e2000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiatt", e3000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiabbn", e4000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiabtn", e5000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiatbn", e6000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiattn", e7000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawbb", e800120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawbt", e900120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawtb", ea00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawtt", eb00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawbbn", ec00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawbtn", ed00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawtbn", ee00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmiawttn", ef00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulsmr", ef00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulumr", ed00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulwumr", ec000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulwsmr", ee000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulwum", ed000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulwsm", ef000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wmulwl", eb000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiabb", e8000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiabt", e9000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiatb", ea000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiatt", eb000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiabbn", ec000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiabtn", ed000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiatbn", ee000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmiattn", ef000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmulm", e100080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmulmr", e300080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmulwm", ec000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wqmulwmr", ee000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wsubaddhx", ed001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_cext_maverick /* Cirrus Maverick instructions. */
+
+ cCE("cfldrs", c100400, 2, (RMF, ADDRGLDC), rd_cpaddr),
+ cCE("cfldrd", c500400, 2, (RMD, ADDRGLDC), rd_cpaddr),
+ cCE("cfldr32", c100500, 2, (RMFX, ADDRGLDC), rd_cpaddr),
+ cCE("cfldr64", c500500, 2, (RMDX, ADDRGLDC), rd_cpaddr),
+ cCE("cfstrs", c000400, 2, (RMF, ADDRGLDC), rd_cpaddr),
+ cCE("cfstrd", c400400, 2, (RMD, ADDRGLDC), rd_cpaddr),
+ cCE("cfstr32", c000500, 2, (RMFX, ADDRGLDC), rd_cpaddr),
+ cCE("cfstr64", c400500, 2, (RMDX, ADDRGLDC), rd_cpaddr),
+ cCE("cfmvsr", e000450, 2, (RMF, RR), rn_rd),
+ cCE("cfmvrs", e100450, 2, (RR, RMF), rd_rn),
+ cCE("cfmvdlr", e000410, 2, (RMD, RR), rn_rd),
+ cCE("cfmvrdl", e100410, 2, (RR, RMD), rd_rn),
+ cCE("cfmvdhr", e000430, 2, (RMD, RR), rn_rd),
+ cCE("cfmvrdh", e100430, 2, (RR, RMD), rd_rn),
+ cCE("cfmv64lr",e000510, 2, (RMDX, RR), rn_rd),
+ cCE("cfmvr64l",e100510, 2, (RR, RMDX), rd_rn),
+ cCE("cfmv64hr",e000530, 2, (RMDX, RR), rn_rd),
+ cCE("cfmvr64h",e100530, 2, (RR, RMDX), rd_rn),
+ cCE("cfmval32",e200440, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32al",e100440, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmvam32",e200460, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32am",e100460, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmvah32",e200480, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32ah",e100480, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmva32", e2004a0, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32a", e1004a0, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmva64", e2004c0, 2, (RMAX, RMDX), rd_rn),
+ cCE("cfmv64a", e1004c0, 2, (RMDX, RMAX), rd_rn),
+ cCE("cfmvsc32",e2004e0, 2, (RMDS, RMDX), mav_dspsc),
+ cCE("cfmv32sc",e1004e0, 2, (RMDX, RMDS), rd),
+ cCE("cfcpys", e000400, 2, (RMF, RMF), rd_rn),
+ cCE("cfcpyd", e000420, 2, (RMD, RMD), rd_rn),
+ cCE("cfcvtsd", e000460, 2, (RMD, RMF), rd_rn),
+ cCE("cfcvtds", e000440, 2, (RMF, RMD), rd_rn),
+ cCE("cfcvt32s",e000480, 2, (RMF, RMFX), rd_rn),
+ cCE("cfcvt32d",e0004a0, 2, (RMD, RMFX), rd_rn),
+ cCE("cfcvt64s",e0004c0, 2, (RMF, RMDX), rd_rn),
+ cCE("cfcvt64d",e0004e0, 2, (RMD, RMDX), rd_rn),
+ cCE("cfcvts32",e100580, 2, (RMFX, RMF), rd_rn),
+ cCE("cfcvtd32",e1005a0, 2, (RMFX, RMD), rd_rn),
+ cCE("cftruncs32",e1005c0, 2, (RMFX, RMF), rd_rn),
+ cCE("cftruncd32",e1005e0, 2, (RMFX, RMD), rd_rn),
+ cCE("cfrshl32",e000550, 3, (RMFX, RMFX, RR), mav_triple),
+ cCE("cfrshl64",e000570, 3, (RMDX, RMDX, RR), mav_triple),
+ cCE("cfsh32", e000500, 3, (RMFX, RMFX, I63s), mav_shift),
+ cCE("cfsh64", e200500, 3, (RMDX, RMDX, I63s), mav_shift),
+ cCE("cfcmps", e100490, 3, (RR, RMF, RMF), rd_rn_rm),
+ cCE("cfcmpd", e1004b0, 3, (RR, RMD, RMD), rd_rn_rm),
+ cCE("cfcmp32", e100590, 3, (RR, RMFX, RMFX), rd_rn_rm),
+ cCE("cfcmp64", e1005b0, 3, (RR, RMDX, RMDX), rd_rn_rm),
+ cCE("cfabss", e300400, 2, (RMF, RMF), rd_rn),
+ cCE("cfabsd", e300420, 2, (RMD, RMD), rd_rn),
+ cCE("cfnegs", e300440, 2, (RMF, RMF), rd_rn),
+ cCE("cfnegd", e300460, 2, (RMD, RMD), rd_rn),
+ cCE("cfadds", e300480, 3, (RMF, RMF, RMF), rd_rn_rm),
+ cCE("cfaddd", e3004a0, 3, (RMD, RMD, RMD), rd_rn_rm),
+ cCE("cfsubs", e3004c0, 3, (RMF, RMF, RMF), rd_rn_rm),
+ cCE("cfsubd", e3004e0, 3, (RMD, RMD, RMD), rd_rn_rm),
+ cCE("cfmuls", e100400, 3, (RMF, RMF, RMF), rd_rn_rm),
+ cCE("cfmuld", e100420, 3, (RMD, RMD, RMD), rd_rn_rm),
+ cCE("cfabs32", e300500, 2, (RMFX, RMFX), rd_rn),
+ cCE("cfabs64", e300520, 2, (RMDX, RMDX), rd_rn),
+ cCE("cfneg32", e300540, 2, (RMFX, RMFX), rd_rn),
+ cCE("cfneg64", e300560, 2, (RMDX, RMDX), rd_rn),
+ cCE("cfadd32", e300580, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ cCE("cfadd64", e3005a0, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
+ cCE("cfsub32", e3005c0, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ cCE("cfsub64", e3005e0, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
+ cCE("cfmul32", e100500, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ cCE("cfmul64", e100520, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
+ cCE("cfmac32", e100540, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ cCE("cfmsc32", e100560, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ cCE("cfmadd32",e000600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+ cCE("cfmsub32",e100600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+ cCE("cfmadda32", e200600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
+ cCE("cfmsuba32", e300600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
+};
+#undef ARM_VARIANT
+#undef THUMB_VARIANT
+#undef TCE
+#undef TUE
+#undef TUF
+#undef TCC
+#undef cCE
+#undef cCL
+#undef C3E
+#undef CE
+#undef CM
+#undef UE
+#undef UF
+#undef UT
+#undef NUF
+#undef nUF
+#undef NCE
+#undef nCE
+#undef OPS0
+#undef OPS1
+#undef OPS2
+#undef OPS3
+#undef OPS4
+#undef OPS5
+#undef OPS6
+#undef do_0
+
+/* MD interface: bits in the object file. */
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least). */
+
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+static valueT
+md_chars_to_number (char * buf, int n)
+{
+ valueT result = 0;
+ unsigned char * where = (unsigned char *) buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
+ }
+
+ return result;
+}
+
+/* MD interface: Sections. */
+
+/* Calculate the maximum variable size (i.e., excluding fr_fix)
+ that an rs_machine_dependent frag may reach. */
+
+unsigned int
+arm_frag_max_var (fragS *fragp)
+{
+ /* We only use rs_machine_dependent for variable-size Thumb instructions,
+ which are either THUMB_SIZE (2) or INSN_SIZE (4).
+
+ Note that we generate relaxable instructions even for cases that don't
+ really need it, like an immediate that's a trivial constant. So we're
+ overestimating the instruction size for some of those cases. Rather
+ than putting more intelligence here, it would probably be better to
+ avoid generating a relaxation frag in the first place when it can be
+ determined up front that a short instruction will suffice. */
+
+ gas_assert (fragp->fr_type == rs_machine_dependent);
+ return INSN_SIZE;
+}
+
+/* Estimate the size of a frag before relaxing. Assume everything fits in
+ 2 bytes. */
+
+int
+md_estimate_size_before_relax (fragS * fragp,
+ segT segtype ATTRIBUTE_UNUSED)
+{
+ fragp->fr_var = 2;
+ return 2;
+}
+
+/* Convert a machine dependent frag. */
+
+void
+md_convert_frag (bfd *abfd, segT asec ATTRIBUTE_UNUSED, fragS *fragp)
+{
+ unsigned long insn;
+ unsigned long old_op;
+ char *buf;
+ expressionS exp;
+ fixS *fixp;
+ int reloc_type;
+ int pc_rel;
+ int opcode;
+
+ buf = fragp->fr_literal + fragp->fr_fix;
+
+ old_op = bfd_get_16(abfd, buf);
+ if (fragp->fr_symbol)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ }
+ else
+ {
+ exp.X_op = O_constant;
+ }
+ exp.X_add_number = fragp->fr_offset;
+ opcode = fragp->fr_subtype;
+ switch (opcode)
+ {
+ case T_MNEM_ldr_pc:
+ case T_MNEM_ldr_pc2:
+ case T_MNEM_ldr_sp:
+ case T_MNEM_str_sp:
+ case T_MNEM_ldr:
+ case T_MNEM_ldrb:
+ case T_MNEM_ldrh:
+ case T_MNEM_str:
+ case T_MNEM_strb:
+ case T_MNEM_strh:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32 (opcode);
+ if ((old_op >> 12) == 4 || (old_op >> 12) == 9)
+ {
+ insn |= (old_op & 0x700) << 4;
+ }
+ else
+ {
+ insn |= (old_op & 7) << 12;
+ insn |= (old_op & 0x38) << 13;
+ }
+ insn |= 0x00000c00;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+ }
+ else
+ {
+ reloc_type = BFD_RELOC_ARM_THUMB_OFFSET;
+ }
+ pc_rel = (opcode == T_MNEM_ldr_pc2);
+ break;
+ case T_MNEM_adr:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32 (opcode);
+ insn |= (old_op & 0xf0) << 4;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_ADD_PC12;
+ }
+ else
+ {
+ reloc_type = BFD_RELOC_ARM_THUMB_ADD;
+ exp.X_add_number -= 4;
+ }
+ pc_rel = 1;
+ break;
+ case T_MNEM_mov:
+ case T_MNEM_movs:
+ case T_MNEM_cmp:
+ case T_MNEM_cmn:
+ if (fragp->fr_var == 4)
+ {
+ int r0off = (opcode == T_MNEM_mov
+ || opcode == T_MNEM_movs) ? 0 : 8;
+ insn = THUMB_OP32 (opcode);
+ insn = (insn & 0xe1ffffff) | 0x10000000;
+ insn |= (old_op & 0x700) << r0off;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ reloc_type = BFD_RELOC_ARM_THUMB_IMM;
+ }
+ pc_rel = 0;
+ break;
+ case T_MNEM_b:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32(opcode);
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH25;
+ }
+ else
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ pc_rel = 1;
+ break;
+ case T_MNEM_bcond:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32(opcode);
+ insn |= (old_op & 0xf00) << 14;
+ put_thumb32_insn (buf, insn);
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH20;
+ }
+ else
+ reloc_type = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ pc_rel = 1;
+ break;
+ case T_MNEM_add_sp:
+ case T_MNEM_add_pc:
+ case T_MNEM_inc_sp:
+ case T_MNEM_dec_sp:
+ if (fragp->fr_var == 4)
+ {
+ /* ??? Choose between add and addw. */
+ insn = THUMB_OP32 (opcode);
+ insn |= (old_op & 0xf0) << 4;
+ put_thumb32_insn (buf, insn);
+ if (opcode == T_MNEM_add_pc)
+ reloc_type = BFD_RELOC_ARM_T32_IMM12;
+ else
+ reloc_type = BFD_RELOC_ARM_T32_ADD_IMM;
+ }
+ else
+ reloc_type = BFD_RELOC_ARM_THUMB_ADD;
+ pc_rel = 0;
+ break;
+
+ case T_MNEM_addi:
+ case T_MNEM_addis:
+ case T_MNEM_subi:
+ case T_MNEM_subis:
+ if (fragp->fr_var == 4)
+ {
+ insn = THUMB_OP32 (opcode);
+ insn |= (old_op & 0xf0) << 4;
+ insn |= (old_op & 0xf) << 16;
+ put_thumb32_insn (buf, insn);
+ if (insn & (1 << 20))
+ reloc_type = BFD_RELOC_ARM_T32_ADD_IMM;
+ else
+ reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ reloc_type = BFD_RELOC_ARM_THUMB_ADD;
+ pc_rel = 0;
+ break;
+ default:
+ abort ();
+ }
+ fixp = fix_new_exp (fragp, fragp->fr_fix, fragp->fr_var, &exp, pc_rel,
+ (enum bfd_reloc_code_real) reloc_type);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+ fragp->fr_fix += fragp->fr_var;
+}
+
+/* Return the size of a relaxable immediate operand instruction.
+ SHIFT and SIZE specify the form of the allowable immediate. */
+static int
+relax_immediate (fragS *fragp, int size, int shift)
+{
+ offsetT offset;
+ offsetT mask;
+ offsetT low;
+
+ /* ??? Should be able to do better than this. */
+ if (fragp->fr_symbol)
+ return 4;
+
+ low = (1 << shift) - 1;
+ mask = (1 << (shift + size)) - (1 << shift);
+ offset = fragp->fr_offset;
+ /* Force misaligned offsets to 32-bit variant. */
+ if (offset & low)
+ return 4;
+ if (offset & ~mask)
+ return 4;
+ return 2;
+}
+
+/* Get the address of a symbol during relaxation. */
+static addressT
+relaxed_symbol_addr (fragS *fragp, long stretch)
+{
+ fragS *sym_frag;
+ addressT addr;
+ symbolS *sym;
+
+ sym = fragp->fr_symbol;
+ sym_frag = symbol_get_frag (sym);
+ know (S_GET_SEGMENT (sym) != absolute_section
+ || sym_frag == &zero_address_frag);
+ addr = S_GET_VALUE (sym) + fragp->fr_offset;
+
+ /* If frag has yet to be reached on this pass, assume it will
+ move by STRETCH just as we did. If this is not so, it will
+ be because some frag between grows, and that will force
+ another pass. */
+
+ if (stretch != 0
+ && sym_frag->relax_marker != fragp->relax_marker)
+ {
+ fragS *f;
+
+ /* Adjust stretch for any alignment frag. Note that if have
+ been expanding the earlier code, the symbol may be
+ defined in what appears to be an earlier frag. FIXME:
+ This doesn't handle the fr_subtype field, which specifies
+ a maximum number of bytes to skip when doing an
+ alignment. */
+ for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
+ {
+ if (f->fr_type == rs_align || f->fr_type == rs_align_code)
+ {
+ if (stretch < 0)
+ stretch = - ((- stretch)
+ & ~ ((1 << (int) f->fr_offset) - 1));
+ else
+ stretch &= ~ ((1 << (int) f->fr_offset) - 1);
+ if (stretch == 0)
+ break;
+ }
+ }
+ if (f != NULL)
+ addr += stretch;
+ }
+
+ return addr;
+}
+
+/* Return the size of a relaxable adr pseudo-instruction or PC-relative
+ load. */
+static int
+relax_adr (fragS *fragp, asection *sec, long stretch)
+{
+ addressT addr;
+ offsetT val;
+
+ /* Assume worst case for symbols not known to be in the same section. */
+ if (fragp->fr_symbol == NULL
+ || !S_IS_DEFINED (fragp->fr_symbol)
+ || sec != S_GET_SEGMENT (fragp->fr_symbol)
+ || S_IS_WEAK (fragp->fr_symbol))
+ return 4;
+
+ val = relaxed_symbol_addr (fragp, stretch);
+ addr = fragp->fr_address + fragp->fr_fix;
+ addr = (addr + 4) & ~3;
+ /* Force misaligned targets to 32-bit variant. */
+ if (val & 3)
+ return 4;
+ val -= addr;
+ if (val < 0 || val > 1020)
+ return 4;
+ return 2;
+}
+
+/* Return the size of a relaxable add/sub immediate instruction. */
+static int
+relax_addsub (fragS *fragp, asection *sec)
+{
+ char *buf;
+ int op;
+
+ buf = fragp->fr_literal + fragp->fr_fix;
+ op = bfd_get_16(sec->owner, buf);
+ if ((op & 0xf) == ((op >> 4) & 0xf))
+ return relax_immediate (fragp, 8, 0);
+ else
+ return relax_immediate (fragp, 3, 0);
+}
+
+/* Return TRUE iff the definition of symbol S could be pre-empted
+ (overridden) at link or load time. */
+static bfd_boolean
+symbol_preemptible (symbolS *s)
+{
+ /* Weak symbols can always be pre-empted. */
+ if (S_IS_WEAK (s))
+ return TRUE;
+
+ /* Non-global symbols cannot be pre-empted. */
+ if (! S_IS_EXTERNAL (s))
+ return FALSE;
+
+#ifdef OBJ_ELF
+ /* In ELF, a global symbol can be marked protected, or private. In that
+ case it can't be pre-empted (other definitions in the same link unit
+ would violate the ODR). */
+ if (ELF_ST_VISIBILITY (S_GET_OTHER (s)) > STV_DEFAULT)
+ return FALSE;
+#endif
+
+ /* Other global symbols might be pre-empted. */
+ return TRUE;
+}
+
+/* Return the size of a relaxable branch instruction. BITS is the
+ size of the offset field in the narrow instruction. */
+
+static int
+relax_branch (fragS *fragp, asection *sec, int bits, long stretch)
+{
+ addressT addr;
+ offsetT val;
+ offsetT limit;
+
+ /* Assume worst case for symbols not known to be in the same section. */
+ if (!S_IS_DEFINED (fragp->fr_symbol)
+ || sec != S_GET_SEGMENT (fragp->fr_symbol)
+ || S_IS_WEAK (fragp->fr_symbol))
+ return 4;
+
+#ifdef OBJ_ELF
+ /* A branch to a function in ARM state will require interworking. */
+ if (S_IS_DEFINED (fragp->fr_symbol)
+ && ARM_IS_FUNC (fragp->fr_symbol))
+ return 4;
+#endif
+
+ if (symbol_preemptible (fragp->fr_symbol))
+ return 4;
+
+ val = relaxed_symbol_addr (fragp, stretch);
+ addr = fragp->fr_address + fragp->fr_fix + 4;
+ val -= addr;
+
+ /* Offset is a signed value *2 */
+ limit = 1 << bits;
+ if (val >= limit || val < -limit)
+ return 4;
+ return 2;
+}
+
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+int
+arm_relax_frag (asection *sec, fragS *fragp, long stretch)
+{
+ int oldsize;
+ int newsize;
+
+ oldsize = fragp->fr_var;
+ switch (fragp->fr_subtype)
+ {
+ case T_MNEM_ldr_pc2:
+ newsize = relax_adr (fragp, sec, stretch);
+ break;
+ case T_MNEM_ldr_pc:
+ case T_MNEM_ldr_sp:
+ case T_MNEM_str_sp:
+ newsize = relax_immediate (fragp, 8, 2);
+ break;
+ case T_MNEM_ldr:
+ case T_MNEM_str:
+ newsize = relax_immediate (fragp, 5, 2);
+ break;
+ case T_MNEM_ldrh:
+ case T_MNEM_strh:
+ newsize = relax_immediate (fragp, 5, 1);
+ break;
+ case T_MNEM_ldrb:
+ case T_MNEM_strb:
+ newsize = relax_immediate (fragp, 5, 0);
+ break;
+ case T_MNEM_adr:
+ newsize = relax_adr (fragp, sec, stretch);
+ break;
+ case T_MNEM_mov:
+ case T_MNEM_movs:
+ case T_MNEM_cmp:
+ case T_MNEM_cmn:
+ newsize = relax_immediate (fragp, 8, 0);
+ break;
+ case T_MNEM_b:
+ newsize = relax_branch (fragp, sec, 11, stretch);
+ break;
+ case T_MNEM_bcond:
+ newsize = relax_branch (fragp, sec, 8, stretch);
+ break;
+ case T_MNEM_add_sp:
+ case T_MNEM_add_pc:
+ newsize = relax_immediate (fragp, 8, 2);
+ break;
+ case T_MNEM_inc_sp:
+ case T_MNEM_dec_sp:
+ newsize = relax_immediate (fragp, 7, 2);
+ break;
+ case T_MNEM_addi:
+ case T_MNEM_addis:
+ case T_MNEM_subi:
+ case T_MNEM_subis:
+ newsize = relax_addsub (fragp, sec);
+ break;
+ default:
+ abort ();
+ }
+
+ fragp->fr_var = newsize;
+ /* Freeze wide instructions that are at or before the same location as
+ in the previous pass. This avoids infinite loops.
+ Don't freeze them unconditionally because targets may be artificially
+ misaligned by the expansion of preceding frags. */
+ if (stretch <= 0 && newsize > 2)
+ {
+ md_convert_frag (sec->owner, sec, fragp);
+ frag_wane (fragp);
+ }
+
+ return newsize - oldsize;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
+{
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))
+ if (OUTPUT_FLAVOR == bfd_target_aout_flavour)
+ {
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+ }
+#endif
+
+ return size;
+}
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+arm_handle_align (fragS * fragP)
+{
+ static char const arm_noop[2][2][4] =
+ {
+ { /* ARMv1 */
+ {0x00, 0x00, 0xa0, 0xe1}, /* LE */
+ {0xe1, 0xa0, 0x00, 0x00}, /* BE */
+ },
+ { /* ARMv6k */
+ {0x00, 0xf0, 0x20, 0xe3}, /* LE */
+ {0xe3, 0x20, 0xf0, 0x00}, /* BE */
+ },
+ };
+ static char const thumb_noop[2][2][2] =
+ {
+ { /* Thumb-1 */
+ {0xc0, 0x46}, /* LE */
+ {0x46, 0xc0}, /* BE */
+ },
+ { /* Thumb-2 */
+ {0x00, 0xbf}, /* LE */
+ {0xbf, 0x00} /* BE */
+ }
+ };
+ static char const wide_thumb_noop[2][4] =
+ { /* Wide Thumb-2 */
+ {0xaf, 0xf3, 0x00, 0x80}, /* LE */
+ {0xf3, 0xaf, 0x80, 0x00}, /* BE */
+ };
+
+ unsigned bytes, fix, noop_size;
+ char * p;
+ const char * noop;
+ const char *narrow_noop = NULL;
+#ifdef OBJ_ELF
+ enum mstate state;
+#endif
+
+ if (fragP->fr_type != rs_align_code)
+ return;
+
+ bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ p = fragP->fr_literal + fragP->fr_fix;
+ fix = 0;
+
+ if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+ bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+
+ gas_assert ((fragP->tc_frag_data.thumb_mode & MODE_RECORDED) != 0);
+
+ if (fragP->tc_frag_data.thumb_mode & (~ MODE_RECORDED))
+ {
+ if (ARM_CPU_HAS_FEATURE (selected_cpu_name[0]
+ ? selected_cpu : arm_arch_none, arm_ext_v6t2))
+ {
+ narrow_noop = thumb_noop[1][target_big_endian];
+ noop = wide_thumb_noop[target_big_endian];
+ }
+ else
+ noop = thumb_noop[0][target_big_endian];
+ noop_size = 2;
+#ifdef OBJ_ELF
+ state = MAP_THUMB;
+#endif
+ }
+ else
+ {
+ noop = arm_noop[ARM_CPU_HAS_FEATURE (selected_cpu_name[0]
+ ? selected_cpu : arm_arch_none,
+ arm_ext_v6k) != 0]
+ [target_big_endian];
+ noop_size = 4;
+#ifdef OBJ_ELF
+ state = MAP_ARM;
+#endif
+ }
+
+ fragP->fr_var = noop_size;
+
+ if (bytes & (noop_size - 1))
+ {
+ fix = bytes & (noop_size - 1);
+#ifdef OBJ_ELF
+ insert_data_mapping_symbol (state, fragP->fr_fix, fragP, fix);
+#endif
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ if (narrow_noop)
+ {
+ if (bytes & noop_size)
+ {
+ /* Insert a narrow noop. */
+ memcpy (p, narrow_noop, noop_size);
+ p += noop_size;
+ bytes -= noop_size;
+ fix += noop_size;
+ }
+
+ /* Use wide noops for the remainder */
+ noop_size = 4;
+ }
+
+ while (bytes >= noop_size)
+ {
+ memcpy (p, noop, noop_size);
+ p += noop_size;
+ bytes -= noop_size;
+ fix += noop_size;
+ }
+
+ fragP->fr_fix += fix;
+}
+
+/* Called from md_do_align. Used to create an alignment
+ frag in a code section. */
+
+void
+arm_frag_align_code (int n, int max)
+{
+ char * p;
+
+ /* We assume that there will never be a requirement
+ to support alignments greater than MAX_MEM_FOR_RS_ALIGN_CODE bytes. */
+ if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+ {
+ char err_msg[128];
+
+ sprintf (err_msg,
+ _("alignments greater than %d bytes not supported in .text sections."),
+ MAX_MEM_FOR_RS_ALIGN_CODE + 1);
+ as_fatal ("%s", err_msg);
+ }
+
+ p = frag_var (rs_align_code,
+ MAX_MEM_FOR_RS_ALIGN_CODE,
+ 1,
+ (relax_substateT) max,
+ (symbolS *) NULL,
+ (offsetT) n,
+ (char *) NULL);
+ *p = 0;
+}
+
+/* Perform target specific initialisation of a frag.
+ Note - despite the name this initialisation is not done when the frag
+ is created, but only when its type is assigned. A frag can be created
+ and used a long time before its type is set, so beware of assuming that
+ this initialisationis performed first. */
+
+#ifndef OBJ_ELF
+void
+arm_init_frag (fragS * fragP, int max_chars ATTRIBUTE_UNUSED)
+{
+ /* Record whether this frag is in an ARM or a THUMB area. */
+ fragP->tc_frag_data.thumb_mode = thumb_mode | MODE_RECORDED;
+}
+
+#else /* OBJ_ELF is defined. */
+void
+arm_init_frag (fragS * fragP, int max_chars)
+{
+ /* If the current ARM vs THUMB mode has not already
+ been recorded into this frag then do so now. */
+ if ((fragP->tc_frag_data.thumb_mode & MODE_RECORDED) == 0)
+ {
+ fragP->tc_frag_data.thumb_mode = thumb_mode | MODE_RECORDED;
+
+ /* Record a mapping symbol for alignment frags. We will delete this
+ later if the alignment ends up empty. */
+ switch (fragP->fr_type)
+ {
+ case rs_align:
+ case rs_align_test:
+ case rs_fill:
+ mapping_state_2 (MAP_DATA, max_chars);
+ break;
+ case rs_align_code:
+ mapping_state_2 (thumb_mode ? MAP_THUMB : MAP_ARM, max_chars);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* When we change sections we need to issue a new mapping symbol. */
+
+void
+arm_elf_change_section (void)
+{
+ /* Link an unlinked unwind index table section to the .text section. */
+ if (elf_section_type (now_seg) == SHT_ARM_EXIDX
+ && elf_linked_to_section (now_seg) == NULL)
+ elf_linked_to_section (now_seg) = text_section;
+}
+
+int
+arm_elf_section_type (const char * str, size_t len)
+{
+ if (len == 5 && strncmp (str, "exidx", 5) == 0)
+ return SHT_ARM_EXIDX;
+
+ return -1;
+}
+
+/* Code to deal with unwinding tables. */
+
+static void add_unwind_adjustsp (offsetT);
+
+/* Generate any deferred unwind frame offset. */
+
+static void
+flush_pending_unwind (void)
+{
+ offsetT offset;
+
+ offset = unwind.pending_offset;
+ unwind.pending_offset = 0;
+ if (offset != 0)
+ add_unwind_adjustsp (offset);
+}
+
+/* Add an opcode to this list for this function. Two-byte opcodes should
+ be passed as op[0] << 8 | op[1]. The list of opcodes is built in reverse
+ order. */
+
+static void
+add_unwind_opcode (valueT op, int length)
+{
+ /* Add any deferred stack adjustment. */
+ if (unwind.pending_offset)
+ flush_pending_unwind ();
+
+ unwind.sp_restored = 0;
+
+ if (unwind.opcode_count + length > unwind.opcode_alloc)
+ {
+ unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
+ if (unwind.opcodes)
+ unwind.opcodes = (unsigned char *) xrealloc (unwind.opcodes,
+ unwind.opcode_alloc);
+ else
+ unwind.opcodes = (unsigned char *) xmalloc (unwind.opcode_alloc);
+ }
+ while (length > 0)
+ {
+ length--;
+ unwind.opcodes[unwind.opcode_count] = op & 0xff;
+ op >>= 8;
+ unwind.opcode_count++;
+ }
+}
+
+/* Add unwind opcodes to adjust the stack pointer. */
+
+static void
+add_unwind_adjustsp (offsetT offset)
+{
+ valueT op;
+
+ if (offset > 0x200)
+ {
+ /* We need at most 5 bytes to hold a 32-bit value in a uleb128. */
+ char bytes[5];
+ int n;
+ valueT o;
+
+ /* Long form: 0xb2, uleb128. */
+ /* This might not fit in a word so add the individual bytes,
+ remembering the list is built in reverse order. */
+ o = (valueT) ((offset - 0x204) >> 2);
+ if (o == 0)
+ add_unwind_opcode (0, 1);
+
+ /* Calculate the uleb128 encoding of the offset. */
+ n = 0;
+ while (o)
+ {
+ bytes[n] = o & 0x7f;
+ o >>= 7;
+ if (o)
+ bytes[n] |= 0x80;
+ n++;
+ }
+ /* Add the insn. */
+ for (; n; n--)
+ add_unwind_opcode (bytes[n - 1], 1);
+ add_unwind_opcode (0xb2, 1);
+ }
+ else if (offset > 0x100)
+ {
+ /* Two short opcodes. */
+ add_unwind_opcode (0x3f, 1);
+ op = (offset - 0x104) >> 2;
+ add_unwind_opcode (op, 1);
+ }
+ else if (offset > 0)
+ {
+ /* Short opcode. */
+ op = (offset - 4) >> 2;
+ add_unwind_opcode (op, 1);
+ }
+ else if (offset < 0)
+ {
+ offset = -offset;
+ while (offset > 0x100)
+ {
+ add_unwind_opcode (0x7f, 1);
+ offset -= 0x100;
+ }
+ op = ((offset - 4) >> 2) | 0x40;
+ add_unwind_opcode (op, 1);
+ }
+}
+
+/* Finish the list of unwind opcodes for this function. */
+static void
+finish_unwind_opcodes (void)
+{
+ valueT op;
+
+ if (unwind.fp_used)
+ {
+ /* Adjust sp as necessary. */
+ unwind.pending_offset += unwind.fp_offset - unwind.frame_size;
+ flush_pending_unwind ();
+
+ /* After restoring sp from the frame pointer. */
+ op = 0x90 | unwind.fp_reg;
+ add_unwind_opcode (op, 1);
+ }
+ else
+ flush_pending_unwind ();
+}
+
+
+/* Start an exception table entry. If idx is nonzero this is an index table
+ entry. */
+
+static void
+start_unwind_section (const segT text_seg, int idx)
+{
+ const char * text_name;
+ const char * prefix;
+ const char * prefix_once;
+ const char * group_name;
+ size_t prefix_len;
+ size_t text_len;
+ char * sec_name;
+ size_t sec_name_len;
+ int type;
+ int flags;
+ int linkonce;
+
+ if (idx)
+ {
+ prefix = ELF_STRING_ARM_unwind;
+ prefix_once = ELF_STRING_ARM_unwind_once;
+ type = SHT_ARM_EXIDX;
+ }
+ else
+ {
+ prefix = ELF_STRING_ARM_unwind_info;
+ prefix_once = ELF_STRING_ARM_unwind_info_once;
+ type = SHT_PROGBITS;
+ }
+
+ text_name = segment_name (text_seg);
+ if (streq (text_name, ".text"))
+ text_name = "";
+
+ if (strncmp (text_name, ".gnu.linkonce.t.",
+ strlen (".gnu.linkonce.t.")) == 0)
+ {
+ prefix = prefix_once;
+ text_name += strlen (".gnu.linkonce.t.");
+ }
+
+ prefix_len = strlen (prefix);
+ text_len = strlen (text_name);
+ sec_name_len = prefix_len + text_len;
+ sec_name = (char *) xmalloc (sec_name_len + 1);
+ memcpy (sec_name, prefix, prefix_len);
+ memcpy (sec_name + prefix_len, text_name, text_len);
+ sec_name[prefix_len + text_len] = '\0';
+
+ flags = SHF_ALLOC;
+ linkonce = 0;
+ group_name = 0;
+
+ /* Handle COMDAT group. */
+ if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
+ {
+ group_name = elf_group_name (text_seg);
+ if (group_name == NULL)
+ {
+ as_bad (_("Group section `%s' has no group signature"),
+ segment_name (text_seg));
+ ignore_rest_of_line ();
+ return;
+ }
+ flags |= SHF_GROUP;
+ linkonce = 1;
+ }
+
+ obj_elf_change_section (sec_name, type, flags, 0, group_name, linkonce, 0);
+
+ /* Set the section link for index tables. */
+ if (idx)
+ elf_linked_to_section (now_seg) = text_seg;
+}
+
+
+/* Start an unwind table entry. HAVE_DATA is nonzero if we have additional
+ personality routine data. Returns zero, or the index table value for
+ an inline entry. */
+
+static valueT
+create_unwind_entry (int have_data)
+{
+ int size;
+ addressT where;
+ char *ptr;
+ /* The current word of data. */
+ valueT data;
+ /* The number of bytes left in this word. */
+ int n;
+
+ finish_unwind_opcodes ();
+
+ /* Remember the current text section. */
+ unwind.saved_seg = now_seg;
+ unwind.saved_subseg = now_subseg;
+
+ start_unwind_section (now_seg, 0);
+
+ if (unwind.personality_routine == NULL)
+ {
+ if (unwind.personality_index == -2)
+ {
+ if (have_data)
+ as_bad (_("handlerdata in cantunwind frame"));
+ return 1; /* EXIDX_CANTUNWIND. */
+ }
+
+ /* Use a default personality routine if none is specified. */
+ if (unwind.personality_index == -1)
+ {
+ if (unwind.opcode_count > 3)
+ unwind.personality_index = 1;
+ else
+ unwind.personality_index = 0;
+ }
+
+ /* Space for the personality routine entry. */
+ if (unwind.personality_index == 0)
+ {
+ if (unwind.opcode_count > 3)
+ as_bad (_("too many unwind opcodes for personality routine 0"));
+
+ if (!have_data)
+ {
+ /* All the data is inline in the index table. */
+ data = 0x80;
+ n = 3;
+ while (unwind.opcode_count > 0)
+ {
+ unwind.opcode_count--;
+ data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+ n--;
+ }
+
+ /* Pad with "finish" opcodes. */
+ while (n--)
+ data = (data << 8) | 0xb0;
+
+ return data;
+ }
+ size = 0;
+ }
+ else
+ /* We get two opcodes "free" in the first word. */
+ size = unwind.opcode_count - 2;
+ }
+ else
+ {
+ /* PR 16765: Missing or misplaced unwind directives can trigger this. */
+ if (unwind.personality_index != -1)
+ {
+ as_bad (_("attempt to recreate an unwind entry"));
+ return 1;
+ }
+
+ /* An extra byte is required for the opcode count. */
+ size = unwind.opcode_count + 1;
+ }
+
+ size = (size + 3) >> 2;
+ if (size > 0xff)
+ as_bad (_("too many unwind opcodes"));
+
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+ unwind.table_entry = expr_build_dot ();
+
+ /* Allocate the table entry. */
+ ptr = frag_more ((size << 2) + 4);
+ /* PR 13449: Zero the table entries in case some of them are not used. */
+ memset (ptr, 0, (size << 2) + 4);
+ where = frag_now_fix () - ((size << 2) + 4);
+
+ switch (unwind.personality_index)
+ {
+ case -1:
+ /* ??? Should this be a PLT generating relocation? */
+ /* Custom personality routine. */
+ fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ where += 4;
+ ptr += 4;
+
+ /* Set the first byte to the number of additional words. */
+ data = size > 0 ? size - 1 : 0;
+ n = 3;
+ break;
+
+ /* ABI defined personality routines. */
+ case 0:
+ /* Three opcodes bytes are packed into the first word. */
+ data = 0x80;
+ n = 3;
+ break;
+
+ case 1:
+ case 2:
+ /* The size and first two opcode bytes go in the first word. */
+ data = ((0x80 + unwind.personality_index) << 8) | size;
+ n = 2;
+ break;
+
+ default:
+ /* Should never happen. */
+ abort ();
+ }
+
+ /* Pack the opcodes into words (MSB first), reversing the list at the same
+ time. */
+ while (unwind.opcode_count > 0)
+ {
+ if (n == 0)
+ {
+ md_number_to_chars (ptr, data, 4);
+ ptr += 4;
+ n = 4;
+ data = 0;
+ }
+ unwind.opcode_count--;
+ n--;
+ data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+ }
+
+ /* Finish off the last word. */
+ if (n < 4)
+ {
+ /* Pad with "finish" opcodes. */
+ while (n--)
+ data = (data << 8) | 0xb0;
+
+ md_number_to_chars (ptr, data, 4);
+ }
+
+ if (!have_data)
+ {
+ /* Add an empty descriptor if there is no user-specified data. */
+ ptr = frag_more (4);
+ md_number_to_chars (ptr, 0, 4);
+ }
+
+ return 0;
+}
+
+
+/* Initialize the DWARF-2 unwind information for this procedure. */
+
+void
+tc_arm_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (REG_SP, 0);
+}
+#endif /* OBJ_ELF */
+
+/* Convert REGNAME to a DWARF-2 register number. */
+
+int
+tc_arm_regname_to_dw2regnum (char *regname)
+{
+ int reg = arm_reg_parse (&regname, REG_TYPE_RN);
+ if (reg != FAIL)
+ return reg;
+
+ /* PR 16694: Allow VFP registers as well. */
+ reg = arm_reg_parse (&regname, REG_TYPE_VFS);
+ if (reg != FAIL)
+ return 64 + reg;
+
+ reg = arm_reg_parse (&regname, REG_TYPE_VFD);
+ if (reg != FAIL)
+ return reg + 256;
+
+ return -1;
+}
+
+#ifdef TE_PE
+void
+tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+ expressionS exp;
+
+ exp.X_op = O_secrel;
+ exp.X_add_symbol = symbol;
+ exp.X_add_number = 0;
+ emit_expr (&exp, size);
+}
+#endif
+
+/* MD interface: Symbol and relocation handling. */
+
+/* Return the address within the segment that a PC-relative fixup is
+ relative to. For ARM, PC-relative fixups applied to instructions
+ are generally relative to the location of the fixup plus 8 bytes.
+ Thumb branches are offset by 4, and Thumb loads relative to PC
+ require special handling. */
+
+long
+md_pcrel_from_section (fixS * fixP, segT seg)
+{
+ offsetT base = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ /* If this is pc-relative and we are going to emit a relocation
+ then we just want to put out any pipeline compensation that the linker
+ will need. Otherwise we want to use the calculated base.
+ For WinCE we skip the bias for externals as well, since this
+ is how the MS ARM-CE assembler behaves and we want to be compatible. */
+ if (fixP->fx_pcrel
+ && ((fixP->fx_addsy && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ || (arm_force_relocation (fixP)
+#ifdef TE_WINCE
+ && !S_IS_EXTERNAL (fixP->fx_addsy)
+#endif
+ )))
+ base = 0;
+
+
+ switch (fixP->fx_r_type)
+ {
+ /* PC relative addressing on the Thumb is slightly odd as the
+ bottom two bits of the PC are forced to zero for the
+ calculation. This happens *after* application of the
+ pipeline offset. However, Thumb adrl already adjusts for
+ this, so we need not do it again. */
+ case BFD_RELOC_ARM_THUMB_ADD:
+ return base & ~3;
+
+ case BFD_RELOC_ARM_THUMB_OFFSET:
+ case BFD_RELOC_ARM_T32_OFFSET_IMM:
+ case BFD_RELOC_ARM_T32_ADD_PC12:
+ case BFD_RELOC_ARM_T32_CP_OFF_IMM:
+ return (base + 4) & ~3;
+
+ /* Thumb branches are simply offset by +4. */
+ case BFD_RELOC_THUMB_PCREL_BRANCH7:
+ case BFD_RELOC_THUMB_PCREL_BRANCH9:
+ case BFD_RELOC_THUMB_PCREL_BRANCH12:
+ case BFD_RELOC_THUMB_PCREL_BRANCH20:
+ case BFD_RELOC_THUMB_PCREL_BRANCH25:
+ return base + 4;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && ARM_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
+ return base + 4;
+
+ /* BLX is like branches above, but forces the low two bits of PC to
+ zero. */
+ case BFD_RELOC_THUMB_PCREL_BLX:
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && THUMB_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
+ return (base + 4) & ~3;
+
+ /* ARM mode branches are offset by +8. However, the Windows CE
+ loader expects the relocation not to take this into account. */
+ case BFD_RELOC_ARM_PCREL_BLX:
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && ARM_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
+ return base + 8;
+
+ case BFD_RELOC_ARM_PCREL_CALL:
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && THUMB_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
+ return base + 8;
+
+ case BFD_RELOC_ARM_PCREL_BRANCH:
+ case BFD_RELOC_ARM_PCREL_JUMP:
+ case BFD_RELOC_ARM_PLT32:
+#ifdef TE_WINCE
+ /* When handling fixups immediately, because we have already
+ discovered the value of a symbol, or the address of the frag involved
+ we must account for the offset by +8, as the OS loader will never see the reloc.
+ see fixup_segment() in write.c
+ The S_IS_EXTERNAL test handles the case of global symbols.
+ Those need the calculated base, not just the pipe compensation the linker will need. */
+ if (fixP->fx_pcrel
+ && fixP->fx_addsy != NULL
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && (S_IS_EXTERNAL (fixP->fx_addsy) || !arm_force_relocation (fixP)))
+ return base + 8;
+ return base;
+#else
+ return base + 8;
+#endif
+
+
+ /* ARM mode loads relative to PC are also offset by +8. Unlike
+ branches, the Windows CE loader *does* expect the relocation
+ to take this into account. */
+ case BFD_RELOC_ARM_OFFSET_IMM:
+ case BFD_RELOC_ARM_OFFSET_IMM8:
+ case BFD_RELOC_ARM_HWLITERAL:
+ case BFD_RELOC_ARM_LITERAL:
+ case BFD_RELOC_ARM_CP_OFF_IMM:
+ return base + 8;
+
+
+ /* Other PC-relative relocations are un-offset. */
+ default:
+ return base;
+ }
+}
+
+/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
+ Otherwise we have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ if (name[0] == '_' && name[1] == 'G'
+ && streq (name, GLOBAL_OFFSET_TABLE_NAME))
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in the symbol table"));
+
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, & zero_address_frag);
+ }
+
+ return GOT_symbol;
+ }
+#endif
+
+ return NULL;
+}
+
+/* Subroutine of md_apply_fix. Check to see if an immediate can be
+ computed as two separate immediate values, added together. We
+ already know that this value cannot be computed by just one ARM
+ instruction. */
+
+static unsigned int
+validate_immediate_twopart (unsigned int val,
+ unsigned int * highpart)
+{
+ unsigned int a;
+ unsigned int i;
+
+ for (i = 0; i < 32; i += 2)
+ if (((a = rotate_left (val, i)) & 0xff) != 0)
+ {
+ if (a & 0xff00)
+ {
+ if (a & ~ 0xffff)
+ continue;
+ * highpart = (a >> 8) | ((i + 24) << 7);
+ }
+ else if (a & 0xff0000)
+ {
+ if (a & 0xff000000)
+ continue;
+ * highpart = (a >> 16) | ((i + 16) << 7);
+ }
+ else
+ {
+ gas_assert (a & 0xff000000);
+ * highpart = (a >> 24) | ((i + 8) << 7);
+ }
+
+ return (a & 0xff) | (i << 7);
+ }
+
+ return FAIL;
+}
+
+static int
+validate_offset_imm (unsigned int val, int hwse)
+{
+ if ((hwse && val > 255) || val > 4095)
+ return FAIL;
+ return val;
+}
+
+/* Subroutine of md_apply_fix. Do those data_ops which can take a
+ negative immediate constant by altering the instruction. A bit of
+ a hack really.
+ MOV <-> MVN
+ AND <-> BIC
+ ADC <-> SBC
+ by inverting the second operand, and
+ ADD <-> SUB
+ CMP <-> CMN
+ by negating the second operand. */
+
+static int
+negate_data_op (unsigned long * instruction,
+ unsigned long value)
+{
+ int op, new_inst;
+ unsigned long negated, inverted;
+
+ negated = encode_arm_immediate (-value);
+ inverted = encode_arm_immediate (~value);
+
+ op = (*instruction >> DATA_OP_SHIFT) & 0xf;
+ switch (op)
+ {
+ /* First negates. */
+ case OPCODE_SUB: /* ADD <-> SUB */
+ new_inst = OPCODE_ADD;
+ value = negated;
+ break;
+
+ case OPCODE_ADD:
+ new_inst = OPCODE_SUB;
+ value = negated;
+ break;
+
+ case OPCODE_CMP: /* CMP <-> CMN */
+ new_inst = OPCODE_CMN;
+ value = negated;
+ break;
+
+ case OPCODE_CMN:
+ new_inst = OPCODE_CMP;
+ value = negated;
+ break;
+
+ /* Now Inverted ops. */
+ case OPCODE_MOV: /* MOV <-> MVN */
+ new_inst = OPCODE_MVN;
+ value = inverted;
+ break;
+
+ case OPCODE_MVN:
+ new_inst = OPCODE_MOV;
+ value = inverted;
+ break;
+
+ case OPCODE_AND: /* AND <-> BIC */
+ new_inst = OPCODE_BIC;
+ value = inverted;
+ break;
+
+ case OPCODE_BIC:
+ new_inst = OPCODE_AND;
+ value = inverted;
+ break;
+
+ case OPCODE_ADC: /* ADC <-> SBC */
+ new_inst = OPCODE_SBC;
+ value = inverted;
+ break;
+
+ case OPCODE_SBC:
+ new_inst = OPCODE_ADC;
+ value = inverted;
+ break;
+
+ /* We cannot do anything. */
+ default:
+ return FAIL;
+ }
+
+ if (value == (unsigned) FAIL)
+ return FAIL;
+
+ *instruction &= OPCODE_MASK;
+ *instruction |= new_inst << DATA_OP_SHIFT;
+ return value;
+}
+
+/* Like negate_data_op, but for Thumb-2. */
+
+static unsigned int
+thumb32_negate_data_op (offsetT *instruction, unsigned int value)
+{
+ int op, new_inst;
+ int rd;
+ unsigned int negated, inverted;
+
+ negated = encode_thumb32_immediate (-value);
+ inverted = encode_thumb32_immediate (~value);
+
+ rd = (*instruction >> 8) & 0xf;
+ op = (*instruction >> T2_DATA_OP_SHIFT) & 0xf;
+ switch (op)
+ {
+ /* ADD <-> SUB. Includes CMP <-> CMN. */
+ case T2_OPCODE_SUB:
+ new_inst = T2_OPCODE_ADD;
+ value = negated;
+ break;
+
+ case T2_OPCODE_ADD:
+ new_inst = T2_OPCODE_SUB;
+ value = negated;
+ break;
+
+ /* ORR <-> ORN. Includes MOV <-> MVN. */
+ case T2_OPCODE_ORR:
+ new_inst = T2_OPCODE_ORN;
+ value = inverted;
+ break;
+
+ case T2_OPCODE_ORN:
+ new_inst = T2_OPCODE_ORR;
+ value = inverted;
+ break;
+
+ /* AND <-> BIC. TST has no inverted equivalent. */
+ case T2_OPCODE_AND:
+ new_inst = T2_OPCODE_BIC;
+ if (rd == 15)
+ value = FAIL;
+ else
+ value = inverted;
+ break;
+
+ case T2_OPCODE_BIC:
+ new_inst = T2_OPCODE_AND;
+ value = inverted;
+ break;
+
+ /* ADC <-> SBC */
+ case T2_OPCODE_ADC:
+ new_inst = T2_OPCODE_SBC;
+ value = inverted;
+ break;
+
+ case T2_OPCODE_SBC:
+ new_inst = T2_OPCODE_ADC;
+ value = inverted;
+ break;
+
+ /* We cannot do anything. */
+ default:
+ return FAIL;
+ }
+
+ if (value == (unsigned int)FAIL)
+ return FAIL;
+
+ *instruction &= T2_OPCODE_MASK;
+ *instruction |= new_inst << T2_DATA_OP_SHIFT;
+ return value;
+}
+
+/* Read a 32-bit thumb instruction from buf. */
+static unsigned long
+get_thumb32_insn (char * buf)
+{
+ unsigned long insn;
+ insn = md_chars_to_number (buf, THUMB_SIZE) << 16;
+ insn |= md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+
+ return insn;
+}
+
+
+/* We usually want to set the low bit on the address of thumb function
+ symbols. In particular .word foo - . should have the low bit set.
+ Generic code tries to fold the difference of two symbols to
+ a constant. Prevent this and force a relocation when the first symbols
+ is a thumb function. */
+
+bfd_boolean
+arm_optimize_expr (expressionS *l, operatorT op, expressionS *r)
+{
+ if (op == O_subtract
+ && l->X_op == O_symbol
+ && r->X_op == O_symbol
+ && THUMB_IS_FUNC (l->X_add_symbol))
+ {
+ l->X_op = O_subtract;
+ l->X_op_symbol = r->X_add_symbol;
+ l->X_add_number -= r->X_add_number;
+ return TRUE;
+ }
+
+ /* Process as normal. */
+ return FALSE;
+}
+
+/* Encode Thumb2 unconditional branches and calls. The encoding
+ for the 2 are identical for the immediate values. */
+
+static void
+encode_thumb2_b_bl_offset (char * buf, offsetT value)
+{
+#define T2I1I2MASK ((1 << 13) | (1 << 11))
+ offsetT newval;
+ offsetT newval2;
+ addressT S, I1, I2, lo, hi;
+
+ S = (value >> 24) & 0x01;
+ I1 = (value >> 23) & 0x01;
+ I2 = (value >> 22) & 0x01;
+ hi = (value >> 12) & 0x3ff;
+ lo = (value >> 1) & 0x7ff;
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+ newval |= (S << 10) | hi;
+ newval2 &= ~T2I1I2MASK;
+ newval2 |= (((I1 ^ S) << 13) | ((I2 ^ S) << 11) | lo) ^ T2I1I2MASK;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+}
+
+void
+md_apply_fix (fixS * fixP,
+ valueT * valP,
+ segT seg)
+{
+ offsetT value = * valP;
+ offsetT newval;
+ unsigned int newimm;
+ unsigned long temp;
+ int sign;
+ char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ gas_assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
+
+ /* Note whether this will delete the relocation. */
+
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ fixP->fx_done = 1;
+
+ /* On a 64-bit host, silently truncate 'value' to 32 bits for
+ consistency with the behaviour on 32-bit hosts. Remember value
+ for emit_reloc. */
+ value &= 0xffffffff;
+ value ^= 0x80000000;
+ value -= 0x80000000;
+
+ *valP = value;
+ fixP->fx_addnumber = value;
+
+ /* Same treatment for fixP->fx_offset. */
+ fixP->fx_offset &= 0xffffffff;
+ fixP->fx_offset ^= 0x80000000;
+ fixP->fx_offset -= 0x80000000;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NONE:
+ /* This will need to go in the object file. */
+ fixP->fx_done = 0;
+ break;
+
+ case BFD_RELOC_ARM_IMMEDIATE:
+ /* We claim that this fixup has been processed here,
+ even if in fact we generate an error because we do
+ not have a reloc for it, so tc_gen_reloc will reject it. */
+ fixP->fx_done = 1;
+
+ if (fixP->fx_addsy)
+ {
+ const char *msg = 0;
+
+ if (! S_IS_DEFINED (fixP->fx_addsy))
+ msg = _("undefined symbol %s used as an immediate value");
+ else if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ msg = _("symbol %s is in a different section");
+ else if (S_IS_WEAK (fixP->fx_addsy))
+ msg = _("symbol %s is weak and may be overridden later");
+
+ if (msg)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ msg, S_GET_NAME (fixP->fx_addsy));
+ break;
+ }
+ }
+
+ temp = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the offset is negative, we should use encoding A2 for ADR. */
+ if ((temp & 0xfff0000) == 0x28f0000 && value < 0)
+ newimm = negate_data_op (&temp, value);
+ else
+ {
+ newimm = encode_arm_immediate (value);
+
+ /* If the instruction will fail, see if we can fix things up by
+ changing the opcode. */
+ if (newimm == (unsigned int) FAIL)
+ newimm = negate_data_op (&temp, value);
+ }
+
+ if (newimm == (unsigned int) FAIL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid constant (%lx) after fixup"),
+ (unsigned long) value);
+ break;
+ }
+
+ newimm |= (temp & 0xfffff000);
+ md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_ADRL_IMMEDIATE:
+ {
+ unsigned int highpart = 0;
+ unsigned int newinsn = 0xe1a00000; /* nop. */
+
+ if (fixP->fx_addsy)
+ {
+ const char *msg = 0;
+
+ if (! S_IS_DEFINED (fixP->fx_addsy))
+ msg = _("undefined symbol %s used as an immediate value");
+ else if (S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ msg = _("symbol %s is in a different section");
+ else if (S_IS_WEAK (fixP->fx_addsy))
+ msg = _("symbol %s is weak and may be overridden later");
+
+ if (msg)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ msg, S_GET_NAME (fixP->fx_addsy));
+ break;
+ }
+ }
+
+ newimm = encode_arm_immediate (value);
+ temp = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the instruction will fail, see if we can fix things up by
+ changing the opcode. */
+ if (newimm == (unsigned int) FAIL
+ && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
+ {
+ /* No ? OK - try using two ADD instructions to generate
+ the value. */
+ newimm = validate_immediate_twopart (value, & highpart);
+
+ /* Yes - then make sure that the second instruction is
+ also an add. */
+ if (newimm != (unsigned int) FAIL)
+ newinsn = temp;
+ /* Still No ? Try using a negated value. */
+ else if ((newimm = validate_immediate_twopart (- value, & highpart)) != (unsigned int) FAIL)
+ temp = newinsn = (temp & OPCODE_MASK) | OPCODE_SUB << DATA_OP_SHIFT;
+ /* Otherwise - give up. */
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unable to compute ADRL instructions for PC offset of 0x%lx"),
+ (long) value);
+ break;
+ }
+
+ /* Replace the first operand in the 2nd instruction (which
+ is the PC) with the destination register. We have
+ already added in the PC in the first instruction and we
+ do not want to do it again. */
+ newinsn &= ~ 0xf0000;
+ newinsn |= ((newinsn & 0x0f000) << 4);
+ }
+
+ newimm |= (temp & 0xfffff000);
+ md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
+
+ highpart |= (newinsn & 0xfffff000);
+ md_number_to_chars (buf + INSN_SIZE, (valueT) highpart, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_OFFSET_IMM:
+ if (!fixP->fx_done && seg->use_rela_p)
+ value = 0;
+
+ case BFD_RELOC_ARM_LITERAL:
+ sign = value > 0;
+
+ if (value < 0)
+ value = - value;
+
+ if (validate_offset_imm (value, 0) == FAIL)
+ {
+ if (fixP->fx_r_type == BFD_RELOC_ARM_LITERAL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid literal constant: pool needs to be closer"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for offset (%ld)"),
+ (long) value);
+ break;
+ }
+
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ if (value == 0)
+ newval &= 0xfffff000;
+ else
+ {
+ newval &= 0xff7ff000;
+ newval |= value | (sign ? INDEX_UP : 0);
+ }
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_OFFSET_IMM8:
+ case BFD_RELOC_ARM_HWLITERAL:
+ sign = value > 0;
+
+ if (value < 0)
+ value = - value;
+
+ if (validate_offset_imm (value, 1) == FAIL)
+ {
+ if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid literal constant: pool needs to be closer"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for 8-bit offset (%ld)"),
+ (long) value);
+ break;
+ }
+
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ if (value == 0)
+ newval &= 0xfffff0f0;
+ else
+ {
+ newval &= 0xff7ff0f0;
+ newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
+ }
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_T32_OFFSET_U8:
+ if (value < 0 || value > 1020 || value % 4 != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for offset (%ld)"), (long) value);
+ value /= 4;
+
+ newval = md_chars_to_number (buf+2, THUMB_SIZE);
+ newval |= value;
+ md_number_to_chars (buf+2, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_T32_OFFSET_IMM:
+ /* This is a complicated relocation used for all varieties of Thumb32
+ load/store instruction with immediate offset:
+
+ 1110 100P u1WL NNNN XXXX YYYY iiii iiii - +/-(U) pre/post(P) 8-bit,
+ *4, optional writeback(W)
+ (doubleword load/store)
+
+ 1111 100S uTTL 1111 XXXX iiii iiii iiii - +/-(U) 12-bit PC-rel
+ 1111 100S 0TTL NNNN XXXX 1Pu1 iiii iiii - +/-(U) pre/post(P) 8-bit
+ 1111 100S 0TTL NNNN XXXX 1110 iiii iiii - positive 8-bit (T instruction)
+ 1111 100S 1TTL NNNN XXXX iiii iiii iiii - positive 12-bit
+ 1111 100S 0TTL NNNN XXXX 1100 iiii iiii - negative 8-bit
+
+ Uppercase letters indicate bits that are already encoded at
+ this point. Lowercase letters are our problem. For the
+ second block of instructions, the secondary opcode nybble
+ (bits 8..11) is present, and bit 23 is zero, even if this is
+ a PC-relative operation. */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval <<= 16;
+ newval |= md_chars_to_number (buf+THUMB_SIZE, THUMB_SIZE);
+
+ if ((newval & 0xf0000000) == 0xe0000000)
+ {
+ /* Doubleword load/store: 8-bit offset, scaled by 4. */
+ if (value >= 0)
+ newval |= (1 << 23);
+ else
+ value = -value;
+ if (value % 4 != 0)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset not a multiple of 4"));
+ break;
+ }
+ value /= 4;
+ if (value > 0xff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xff;
+ }
+ else if ((newval & 0x000f0000) == 0x000f0000)
+ {
+ /* PC-relative, 12-bit offset. */
+ if (value >= 0)
+ newval |= (1 << 23);
+ else
+ value = -value;
+ if (value > 0xfff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xfff;
+ }
+ else if ((newval & 0x00000100) == 0x00000100)
+ {
+ /* Writeback: 8-bit, +/- offset. */
+ if (value >= 0)
+ newval |= (1 << 9);
+ else
+ value = -value;
+ if (value > 0xff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xff;
+ }
+ else if ((newval & 0x00000f00) == 0x00000e00)
+ {
+ /* T-instruction: positive 8-bit offset. */
+ if (value < 0 || value > 0xff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xff;
+ newval |= value;
+ }
+ else
+ {
+ /* Positive 12-bit or negative 8-bit offset. */
+ int limit;
+ if (value >= 0)
+ {
+ newval |= (1 << 23);
+ limit = 0xfff;
+ }
+ else
+ {
+ value = -value;
+ limit = 0xff;
+ }
+ if (value > limit)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~limit;
+ }
+
+ newval |= value;
+ md_number_to_chars (buf, (newval >> 16) & 0xffff, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval & 0xffff, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_SHIFT_IMM:
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ if (((unsigned long) value) > 32
+ || (value == 32
+ && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60)))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("shift expression is too large"));
+ break;
+ }
+
+ if (value == 0)
+ /* Shifts of zero must be done as lsl. */
+ newval &= ~0x60;
+ else if (value == 32)
+ value = 0;
+ newval &= 0xfffff07f;
+ newval |= (value & 0x1f) << 7;
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_T32_IMMEDIATE:
+ case BFD_RELOC_ARM_T32_ADD_IMM:
+ case BFD_RELOC_ARM_T32_IMM12:
+ case BFD_RELOC_ARM_T32_ADD_PC12:
+ /* We claim that this fixup has been processed here,
+ even if in fact we generate an error because we do
+ not have a reloc for it, so tc_gen_reloc will reject it. */
+ fixP->fx_done = 1;
+
+ if (fixP->fx_addsy
+ && ! S_IS_DEFINED (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("undefined symbol %s used as an immediate value"),
+ S_GET_NAME (fixP->fx_addsy));
+ break;
+ }
+
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval <<= 16;
+ newval |= md_chars_to_number (buf+2, THUMB_SIZE);
+
+ newimm = FAIL;
+ if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
+ || fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM)
+ {
+ newimm = encode_thumb32_immediate (value);
+ if (newimm == (unsigned int) FAIL)
+ newimm = thumb32_negate_data_op (&newval, value);
+ }
+ if (fixP->fx_r_type != BFD_RELOC_ARM_T32_IMMEDIATE
+ && newimm == (unsigned int) FAIL)
+ {
+ /* Turn add/sum into addw/subw. */
+ if (fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM)
+ newval = (newval & 0xfeffffff) | 0x02000000;
+ /* No flat 12-bit imm encoding for addsw/subsw. */
+ if ((newval & 0x00100000) == 0)
+ {
+ /* 12 bit immediate for addw/subw. */
+ if (value < 0)
+ {
+ value = -value;
+ newval ^= 0x00a00000;
+ }
+ if (value > 0xfff)
+ newimm = (unsigned int) FAIL;
+ else
+ newimm = value;
+ }
+ }
+
+ if (newimm == (unsigned int)FAIL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid constant (%lx) after fixup"),
+ (unsigned long) value);
+ break;
+ }
+
+ newval |= (newimm & 0x800) << 15;
+ newval |= (newimm & 0x700) << 4;
+ newval |= (newimm & 0x0ff);
+
+ md_number_to_chars (buf, (valueT) ((newval >> 16) & 0xffff), THUMB_SIZE);
+ md_number_to_chars (buf+2, (valueT) (newval & 0xffff), THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_SMC:
+ if (((unsigned long) value) > 0xffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid smc expression"));
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval |= (value & 0xf) | ((value & 0xfff0) << 4);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_HVC:
+ if (((unsigned long) value) > 0xffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid hvc expression"));
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval |= (value & 0xf) | ((value & 0xfff0) << 4);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_SWI:
+ if (fixP->tc_fix_data != 0)
+ {
+ if (((unsigned long) value) > 0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid swi expression"));
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval |= value;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ else
+ {
+ if (((unsigned long) value) > 0x00ffffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid swi expression"));
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval |= value;
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_MULTI:
+ if (((unsigned long) value) > 0xffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid expression in load/store multiple"));
+ newval = value | md_chars_to_number (buf, INSN_SIZE);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_PCREL_CALL:
+
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)
+ && fixP->fx_addsy
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && THUMB_IS_FUNC (fixP->fx_addsy))
+ /* Flip the bl to blx. This is a simple flip
+ bit here because we generate PCREL_CALL for
+ unconditional bls. */
+ {
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval = newval | 0x10000000;
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ temp = 1;
+ fixP->fx_done = 1;
+ }
+ else
+ temp = 3;
+ goto arm_branch_common;
+
+ case BFD_RELOC_ARM_PCREL_JUMP:
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)
+ && fixP->fx_addsy
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && THUMB_IS_FUNC (fixP->fx_addsy))
+ {
+ /* This would map to a bl<cond>, b<cond>,
+ b<always> to a Thumb function. We
+ need to force a relocation for this particular
+ case. */
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ fixP->fx_done = 0;
+ }
+
+ case BFD_RELOC_ARM_PLT32:
+#endif
+ case BFD_RELOC_ARM_PCREL_BRANCH:
+ temp = 3;
+ goto arm_branch_common;
+
+ case BFD_RELOC_ARM_PCREL_BLX:
+
+ temp = 1;
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)
+ && fixP->fx_addsy
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && ARM_IS_FUNC (fixP->fx_addsy))
+ {
+ /* Flip the blx to a bl and warn. */
+ const char *name = S_GET_NAME (fixP->fx_addsy);
+ newval = 0xeb000000;
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("blx to '%s' an ARM ISA state function changed to bl"),
+ name);
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ temp = 3;
+ fixP->fx_done = 1;
+ }
+
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
+ fixP->fx_r_type = BFD_RELOC_ARM_PCREL_CALL;
+#endif
+
+ arm_branch_common:
+ /* We are going to store value (shifted right by two) in the
+ instruction, in a 24 bit, signed field. Bits 26 through 32 either
+ all clear or all set and bit 0 must be clear. For B/BL bit 1 must
+ also be be clear. */
+ if (value & temp)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("misaligned branch destination"));
+ if ((value & (offsetT)0xfe000000) != (offsetT)0
+ && (value & (offsetT)0xfe000000) != (offsetT)0xfe000000)
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ newval |= (value >> 2) & 0x00ffffff;
+ /* Set the H bit on BLX instructions. */
+ if (temp == 1)
+ {
+ if (value & 2)
+ newval |= 0x01000000;
+ else
+ newval &= ~0x01000000;
+ }
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CBZ */
+ /* CBZ can only branch forward. */
+
+ /* Attempts to use CBZ to branch to the next instruction
+ (which, strictly speaking, are prohibited) will be turned into
+ no-ops.
+
+ FIXME: It may be better to remove the instruction completely and
+ perform relaxation. */
+ if (value == -2)
+ {
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval = 0xbf00; /* NOP encoding T1 */
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ else
+ {
+ if (value & ~0x7e)
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval |= ((value & 0x3e) << 2) | ((value & 0x40) << 3);
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ }
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
+ if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval |= (value & 0x1ff) >> 1;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch. */
+ if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval |= (value & 0xfff) >> 1;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH20:
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && ARM_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ {
+ /* Force a relocation for a branch 20 bits wide. */
+ fixP->fx_done = 0;
+ }
+ if ((value & ~0x1fffff) && ((value & ~0x0fffff) != ~0x0fffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch out of range"));
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval2;
+ addressT S, J1, J2, lo, hi;
+
+ S = (value & 0x00100000) >> 20;
+ J2 = (value & 0x00080000) >> 19;
+ J1 = (value & 0x00040000) >> 18;
+ hi = (value & 0x0003f000) >> 12;
+ lo = (value & 0x00000ffe) >> 1;
+
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+ newval |= (S << 10) | hi;
+ newval2 |= (J1 << 13) | (J2 << 11) | lo;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BLX:
+ /* If there is a blx from a thumb state function to
+ another thumb function flip this to a bl and warn
+ about it. */
+
+ if (fixP->fx_addsy
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && THUMB_IS_FUNC (fixP->fx_addsy))
+ {
+ const char *name = S_GET_NAME (fixP->fx_addsy);
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("blx to Thumb func '%s' from Thumb ISA state changed to bl"),
+ name);
+ newval = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+ newval = newval | 0x1000;
+ md_number_to_chars (buf+THUMB_SIZE, newval, THUMB_SIZE);
+ fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+ fixP->fx_done = 1;
+ }
+
+
+ goto thumb_bl_common;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ /* A bl from Thumb state ISA to an internal ARM state function
+ is converted to a blx. */
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
+ && ARM_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ {
+ newval = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+ newval = newval & ~0x1000;
+ md_number_to_chars (buf+THUMB_SIZE, newval, THUMB_SIZE);
+ fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BLX;
+ fixP->fx_done = 1;
+ }
+
+ thumb_bl_common:
+
+ if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ /* For a BLX instruction, make sure that the relocation is rounded up
+ to a word boundary. This follows the semantics of the instruction
+ which specifies that bit 1 of the target address will come from bit
+ 1 of the base address. */
+ value = (value + 3) & ~ 3;
+
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4
+ && fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+#endif
+
+ if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
+ {
+ if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+ else if ((value & ~0x1ffffff)
+ && ((value & ~0x1ffffff) != ~0x1ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Thumb2 branch out of range"));
+ }
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ encode_thumb2_b_bl_offset (buf, value);
+
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH25:
+ if ((value & ~0x0ffffff) && ((value & ~0x0ffffff) != ~0x0ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ encode_thumb2_b_bl_offset (buf, value);
+
+ break;
+
+ case BFD_RELOC_8:
+ if (fixP->fx_done || !seg->use_rela_p)
+ *buf = value;
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 2);
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_TLS_CALL:
+ case BFD_RELOC_ARM_THM_TLS_CALL:
+ case BFD_RELOC_ARM_TLS_DESCSEQ:
+ case BFD_RELOC_ARM_THM_TLS_DESCSEQ:
+ case BFD_RELOC_ARM_TLS_GOTDESC:
+ case BFD_RELOC_ARM_TLS_GD32:
+ case BFD_RELOC_ARM_TLS_LE32:
+ case BFD_RELOC_ARM_TLS_IE32:
+ case BFD_RELOC_ARM_TLS_LDM32:
+ case BFD_RELOC_ARM_TLS_LDO32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_ARM_GOT32:
+ case BFD_RELOC_ARM_GOTOFF:
+ break;
+
+ case BFD_RELOC_ARM_GOT_PREL:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 4);
+ break;
+
+ case BFD_RELOC_ARM_TARGET2:
+ /* TARGET2 is not partial-inplace, so we need to write the
+ addend here for REL targets, because it won't be written out
+ during reloc processing later. */
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, fixP->fx_offset, 4);
+ break;
+#endif
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ case BFD_RELOC_ARM_TARGET1:
+ case BFD_RELOC_ARM_ROSEGREL32:
+ case BFD_RELOC_ARM_SBREL32:
+ case BFD_RELOC_32_PCREL:
+#ifdef TE_PE
+ case BFD_RELOC_32_SECREL:
+#endif
+ if (fixP->fx_done || !seg->use_rela_p)
+#ifdef TE_WINCE
+ /* For WinCE we only do this for pcrel fixups. */
+ if (fixP->fx_done || fixP->fx_pcrel)
+#endif
+ md_number_to_chars (buf, value, 4);
+ break;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_PREL31:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ newval = md_chars_to_number (buf, 4) & 0x80000000;
+ if ((value ^ (value >> 1)) & 0x40000000)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("rel31 relocation overflow"));
+ }
+ newval |= value & 0x7fffffff;
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+#endif
+
+ case BFD_RELOC_ARM_CP_OFF_IMM:
+ case BFD_RELOC_ARM_T32_CP_OFF_IMM:
+ if (value < -1023 || value > 1023 || (value & 3))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("co-processor offset out of range"));
+ cp_off_common:
+ sign = value > 0;
+ if (value < 0)
+ value = -value;
+ if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
+ || fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2)
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ else
+ newval = get_thumb32_insn (buf);
+ if (value == 0)
+ newval &= 0xffffff00;
+ else
+ {
+ newval &= 0xff7fff00;
+ newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ }
+ if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
+ || fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2)
+ md_number_to_chars (buf, newval, INSN_SIZE);
+ else
+ put_thumb32_insn (buf, newval);
+ break;
+
+ case BFD_RELOC_ARM_CP_OFF_IMM_S2:
+ case BFD_RELOC_ARM_T32_CP_OFF_IMM_S2:
+ if (value < -255 || value > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("co-processor offset out of range"));
+ value *= 4;
+ goto cp_off_common;
+
+ case BFD_RELOC_ARM_THUMB_OFFSET:
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ /* Exactly what ranges, and where the offset is inserted depends
+ on the type of instruction, we can establish this from the
+ top 4 bits. */
+ switch (newval >> 12)
+ {
+ case 4: /* PC load. */
+ /* Thumb PC loads are somewhat odd, bit 1 of the PC is
+ forced to zero for these loads; md_pcrel_from has already
+ compensated for this. */
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid offset, target not word aligned (0x%08lX)"),
+ (((unsigned long) fixP->fx_frag->fr_address
+ + (unsigned long) fixP->fx_where) & ~3)
+ + (unsigned long) value);
+
+ if (value & ~0x3fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid offset, value too big (0x%08lX)"),
+ (long) value);
+
+ newval |= value >> 2;
+ break;
+
+ case 9: /* SP load/store. */
+ if (value & ~0x3fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid offset, value too big (0x%08lX)"),
+ (long) value);
+ newval |= value >> 2;
+ break;
+
+ case 6: /* Word load/store. */
+ if (value & ~0x7c)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid offset, value too big (0x%08lX)"),
+ (long) value);
+ newval |= value << 4; /* 6 - 2. */
+ break;
+
+ case 7: /* Byte load/store. */
+ if (value & ~0x1f)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid offset, value too big (0x%08lX)"),
+ (long) value);
+ newval |= value << 6;
+ break;
+
+ case 8: /* Halfword load/store. */
+ if (value & ~0x3e)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid offset, value too big (0x%08lX)"),
+ (long) value);
+ newval |= value << 5; /* 6 - 1. */
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "Unable to process relocation for thumb opcode: %lx",
+ (unsigned long) newval);
+ break;
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_ADD:
+ /* This is a complicated relocation, since we use it for all of
+ the following immediate relocations:
+
+ 3bit ADD/SUB
+ 8bit ADD/SUB
+ 9bit ADD/SUB SP word-aligned
+ 10bit ADD PC/SP word-aligned
+
+ The type of instruction being processed is encoded in the
+ instruction field:
+
+ 0x8000 SUB
+ 0x00F0 Rd
+ 0x000F Rs
+ */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ {
+ int rd = (newval >> 4) & 0xf;
+ int rs = newval & 0xf;
+ int subtract = !!(newval & 0x8000);
+
+ /* Check for HI regs, only very restricted cases allowed:
+ Adjusting SP, and using PC or SP to get an address. */
+ if ((rd > 7 && (rd != REG_SP || rs != REG_SP))
+ || (rs > 7 && rs != REG_SP && rs != REG_PC))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid Hi register with immediate"));
+
+ /* If value is negative, choose the opposite instruction. */
+ if (value < 0)
+ {
+ value = -value;
+ subtract = !subtract;
+ if (value < 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value out of range"));
+ }
+
+ if (rd == REG_SP)
+ {
+ if (value & ~0x1fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid immediate for stack address calculation"));
+ newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
+ newval |= value >> 2;
+ }
+ else if (rs == REG_PC || rs == REG_SP)
+ {
+ if (subtract || value & ~0x3fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid immediate for address calculation (value = 0x%08lX)"),
+ (unsigned long) value);
+ newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP);
+ newval |= rd << 8;
+ newval |= value >> 2;
+ }
+ else if (rs == rd)
+ {
+ if (value & ~0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value out of range"));
+ newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
+ newval |= (rd << 8) | value;
+ }
+ else
+ {
+ if (value & ~0x7)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value out of range"));
+ newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
+ newval |= rd | (rs << 3) | (value << 6);
+ }
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_IMM:
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ if (value < 0 || value > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid immediate: %ld is out of range"),
+ (long) value);
+ newval |= value;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_ARM_THUMB_SHIFT:
+ /* 5bit shift value (0..32). LSL cannot take 32. */
+ newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf83f;
+ temp = newval & 0xf800;
+ if (value < 0 || value > 32 || (value == 32 && temp == T_OPCODE_LSL_I))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid shift value: %ld"), (long) value);
+ /* Shifts of zero must be encoded as LSL. */
+ if (value == 0)
+ newval = (newval & 0x003f) | T_OPCODE_LSL_I;
+ /* Shifts of 32 are encoded as zero. */
+ else if (value == 32)
+ value = 0;
+ newval |= value << 6;
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ case BFD_RELOC_ARM_MOVW:
+ case BFD_RELOC_ARM_MOVT:
+ case BFD_RELOC_ARM_THUMB_MOVW:
+ case BFD_RELOC_ARM_THUMB_MOVT:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ /* REL format relocations are limited to a 16-bit addend. */
+ if (!fixP->fx_done)
+ {
+ if (value < -0x8000 || value > 0x7fff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_ARM_MOVT
+ || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT)
+ {
+ value >>= 16;
+ }
+
+ if (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVW
+ || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT)
+ {
+ newval = get_thumb32_insn (buf);
+ newval &= 0xfbf08f00;
+ newval |= (value & 0xf000) << 4;
+ newval |= (value & 0x0800) << 15;
+ newval |= (value & 0x0700) << 4;
+ newval |= (value & 0x00ff);
+ put_thumb32_insn (buf, newval);
+ }
+ else
+ {
+ newval = md_chars_to_number (buf, 4);
+ newval &= 0xfff0f000;
+ newval |= value & 0x0fff;
+ newval |= (value & 0xf000) << 4;
+ md_number_to_chars (buf, newval, 4);
+ }
+ }
+ return;
+
+ case BFD_RELOC_ARM_ALU_PC_G0_NC:
+ case BFD_RELOC_ARM_ALU_PC_G0:
+ case BFD_RELOC_ARM_ALU_PC_G1_NC:
+ case BFD_RELOC_ARM_ALU_PC_G1:
+ case BFD_RELOC_ARM_ALU_PC_G2:
+ case BFD_RELOC_ARM_ALU_SB_G0_NC:
+ case BFD_RELOC_ARM_ALU_SB_G0:
+ case BFD_RELOC_ARM_ALU_SB_G1_NC:
+ case BFD_RELOC_ARM_ALU_SB_G1:
+ case BFD_RELOC_ARM_ALU_SB_G2:
+ gas_assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma encoded_addend;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ expressed as an 8-bit constant plus a rotation. */
+ encoded_addend = encode_arm_immediate (addend_abs);
+ if (encoded_addend == (unsigned int) FAIL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("the offset 0x%08lX is not representable"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is positive, use an ADD instruction.
+ Otherwise use a SUB. Take care not to destroy the S bit. */
+ insn &= 0xff1fffff;
+ if (value < 0)
+ insn |= 1 << 22;
+ else
+ insn |= 1 << 23;
+
+ /* Place the encoded addend into the first 12 bits of the
+ instruction. */
+ insn &= 0xfffff000;
+ insn |= encoded_addend;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_LDR_PC_G0:
+ case BFD_RELOC_ARM_LDR_PC_G1:
+ case BFD_RELOC_ARM_LDR_PC_G2:
+ case BFD_RELOC_ARM_LDR_SB_G0:
+ case BFD_RELOC_ARM_LDR_SB_G1:
+ case BFD_RELOC_ARM_LDR_SB_G2:
+ gas_assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ encoded in 12 bits. */
+ if (addend_abs >= 0x1000)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (only 12 bits available for the magnitude)"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the absolute value of the addend into the first 12 bits
+ of the instruction. */
+ insn &= 0xfffff000;
+ insn |= addend_abs;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_LDRS_PC_G0:
+ case BFD_RELOC_ARM_LDRS_PC_G1:
+ case BFD_RELOC_ARM_LDRS_PC_G2:
+ case BFD_RELOC_ARM_LDRS_SB_G0:
+ case BFD_RELOC_ARM_LDRS_SB_G1:
+ case BFD_RELOC_ARM_LDRS_SB_G2:
+ gas_assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ encoded in 8 bits. */
+ if (addend_abs >= 0x100)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (only 8 bits available for the magnitude)"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the first four bits of the absolute value of the addend
+ into the first 4 bits of the instruction, and the remaining
+ four into bits 8 .. 11. */
+ insn &= 0xfffff0f0;
+ insn |= (addend_abs & 0xf) | ((addend_abs & 0xf0) << 4);
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_LDC_PC_G0:
+ case BFD_RELOC_ARM_LDC_PC_G1:
+ case BFD_RELOC_ARM_LDC_PC_G2:
+ case BFD_RELOC_ARM_LDC_SB_G0:
+ case BFD_RELOC_ARM_LDC_SB_G1:
+ case BFD_RELOC_ARM_LDC_SB_G2:
+ gas_assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend is a multiple of
+ four and, when divided by four, fits in 8 bits. */
+ if (addend_abs & 0x3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (must be word-aligned)"),
+ (unsigned long) addend_abs);
+
+ if ((addend_abs >> 2) > 0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (must be an 8-bit number of words)"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the addend (divided by four) into the first eight
+ bits of the instruction. */
+ insn &= 0xfffffff0;
+ insn |= addend_abs >> 2;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_V4BX:
+ /* This will need to go in the object file. */
+ fixP->fx_done = 0;
+ break;
+
+ case BFD_RELOC_UNUSED:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad relocation fixup type (%d)"), fixP->fx_r_type);
+ }
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section, fixS *fixp)
+{
+ arelent * reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_pcrel)
+ {
+ if (section->use_rela_p)
+ fixp->fx_offset -= md_pcrel_from_section (fixp, section);
+ else
+ fixp->fx_offset = reloc->address;
+ }
+ reloc->addend = fixp->fx_offset;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_8_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_16:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_16_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_32:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_32_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_ARM_MOVW:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_ARM_MOVW_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_ARM_MOVT:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_ARM_MOVT_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_ARM_THUMB_MOVW:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_ARM_THUMB_MOVW_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_ARM_THUMB_MOVT:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_ARM_THUMB_MOVT_PCREL;
+ break;
+ }
+
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_ARM_PCREL_BRANCH:
+ case BFD_RELOC_ARM_PCREL_BLX:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_THUMB_PCREL_BRANCH7:
+ case BFD_RELOC_THUMB_PCREL_BRANCH9:
+ case BFD_RELOC_THUMB_PCREL_BRANCH12:
+ case BFD_RELOC_THUMB_PCREL_BRANCH20:
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ case BFD_RELOC_THUMB_PCREL_BRANCH25:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+#ifdef TE_PE
+ case BFD_RELOC_32_SECREL:
+#endif
+ code = fixp->fx_r_type;
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BLX:
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
+ code = BFD_RELOC_THUMB_PCREL_BRANCH23;
+ else
+#endif
+ code = BFD_RELOC_THUMB_PCREL_BLX;
+ break;
+
+ case BFD_RELOC_ARM_LITERAL:
+ case BFD_RELOC_ARM_HWLITERAL:
+ /* If this is called then the a literal has
+ been referenced across a section boundary. */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("literal referenced across section boundary"));
+ return NULL;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_ARM_TLS_CALL:
+ case BFD_RELOC_ARM_THM_TLS_CALL:
+ case BFD_RELOC_ARM_TLS_DESCSEQ:
+ case BFD_RELOC_ARM_THM_TLS_DESCSEQ:
+ case BFD_RELOC_ARM_GOT32:
+ case BFD_RELOC_ARM_GOTOFF:
+ case BFD_RELOC_ARM_GOT_PREL:
+ case BFD_RELOC_ARM_PLT32:
+ case BFD_RELOC_ARM_TARGET1:
+ case BFD_RELOC_ARM_ROSEGREL32:
+ case BFD_RELOC_ARM_SBREL32:
+ case BFD_RELOC_ARM_PREL31:
+ case BFD_RELOC_ARM_TARGET2:
+ case BFD_RELOC_ARM_TLS_LE32:
+ case BFD_RELOC_ARM_TLS_LDO32:
+ case BFD_RELOC_ARM_PCREL_CALL:
+ case BFD_RELOC_ARM_PCREL_JUMP:
+ case BFD_RELOC_ARM_ALU_PC_G0_NC:
+ case BFD_RELOC_ARM_ALU_PC_G0:
+ case BFD_RELOC_ARM_ALU_PC_G1_NC:
+ case BFD_RELOC_ARM_ALU_PC_G1:
+ case BFD_RELOC_ARM_ALU_PC_G2:
+ case BFD_RELOC_ARM_LDR_PC_G0:
+ case BFD_RELOC_ARM_LDR_PC_G1:
+ case BFD_RELOC_ARM_LDR_PC_G2:
+ case BFD_RELOC_ARM_LDRS_PC_G0:
+ case BFD_RELOC_ARM_LDRS_PC_G1:
+ case BFD_RELOC_ARM_LDRS_PC_G2:
+ case BFD_RELOC_ARM_LDC_PC_G0:
+ case BFD_RELOC_ARM_LDC_PC_G1:
+ case BFD_RELOC_ARM_LDC_PC_G2:
+ case BFD_RELOC_ARM_ALU_SB_G0_NC:
+ case BFD_RELOC_ARM_ALU_SB_G0:
+ case BFD_RELOC_ARM_ALU_SB_G1_NC:
+ case BFD_RELOC_ARM_ALU_SB_G1:
+ case BFD_RELOC_ARM_ALU_SB_G2:
+ case BFD_RELOC_ARM_LDR_SB_G0:
+ case BFD_RELOC_ARM_LDR_SB_G1:
+ case BFD_RELOC_ARM_LDR_SB_G2:
+ case BFD_RELOC_ARM_LDRS_SB_G0:
+ case BFD_RELOC_ARM_LDRS_SB_G1:
+ case BFD_RELOC_ARM_LDRS_SB_G2:
+ case BFD_RELOC_ARM_LDC_SB_G0:
+ case BFD_RELOC_ARM_LDC_SB_G1:
+ case BFD_RELOC_ARM_LDC_SB_G2:
+ case BFD_RELOC_ARM_V4BX:
+ code = fixp->fx_r_type;
+ break;
+
+ case BFD_RELOC_ARM_TLS_GOTDESC:
+ case BFD_RELOC_ARM_TLS_GD32:
+ case BFD_RELOC_ARM_TLS_IE32:
+ case BFD_RELOC_ARM_TLS_LDM32:
+ /* BFD will include the symbol's address in the addend.
+ But we don't want that, so subtract it out again here. */
+ if (!S_IS_COMMON (fixp->fx_addsy))
+ reloc->addend -= (*reloc->sym_ptr_ptr)->value;
+ code = fixp->fx_r_type;
+ break;
+#endif
+
+ case BFD_RELOC_ARM_IMMEDIATE:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("internal relocation (type: IMMEDIATE) not fixed up"));
+ return NULL;
+
+ case BFD_RELOC_ARM_ADRL_IMMEDIATE:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("ADRL used for a symbol not defined in the same file"));
+ return NULL;
+
+ case BFD_RELOC_ARM_OFFSET_IMM:
+ if (section->use_rela_p)
+ {
+ code = fixp->fx_r_type;
+ break;
+ }
+
+ if (fixp->fx_addsy != NULL
+ && !S_IS_DEFINED (fixp->fx_addsy)
+ && S_IS_LOCAL (fixp->fx_addsy))
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("undefined local label `%s'"),
+ S_GET_NAME (fixp->fx_addsy));
+ return NULL;
+ }
+
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("internal_relocation (type: OFFSET_IMM) not fixed up"));
+ return NULL;
+
+ default:
+ {
+ char * type;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_NONE: type = "NONE"; break;
+ case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break;
+ case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break;
+ case BFD_RELOC_ARM_SMC: type = "SMC"; break;
+ case BFD_RELOC_ARM_SWI: type = "SWI"; break;
+ case BFD_RELOC_ARM_MULTI: type = "MULTI"; break;
+ case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break;
+ case BFD_RELOC_ARM_T32_OFFSET_IMM: type = "T32_OFFSET_IMM"; break;
+ case BFD_RELOC_ARM_T32_CP_OFF_IMM: type = "T32_CP_OFF_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break;
+ case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break;
+ case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
+ default: type = _("<unknown>"); break;
+ }
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file format"),
+ type);
+ return NULL;
+ }
+ }
+
+#ifdef OBJ_ELF
+ if ((code == BFD_RELOC_32_PCREL || code == BFD_RELOC_32)
+ && GOT_symbol
+ && fixp->fx_addsy == GOT_symbol)
+ {
+ code = BFD_RELOC_ARM_GOTPC;
+ reloc->addend = fixp->fx_offset = reloc->address;
+ }
+#endif
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ /* HACK: Since arm ELF uses Rel instead of Rela, encode the
+ vtable entry to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ return reloc;
+}
+
+/* This fix_new is called by cons via TC_CONS_FIX_NEW. */
+
+void
+cons_fix_new_arm (fragS * frag,
+ int where,
+ int size,
+ expressionS * exp,
+ bfd_reloc_code_real_type reloc)
+{
+ int pcrel = 0;
+
+ /* Pick a reloc.
+ FIXME: @@ Should look at CPU word size. */
+ switch (size)
+ {
+ case 1:
+ reloc = BFD_RELOC_8;
+ break;
+ case 2:
+ reloc = BFD_RELOC_16;
+ break;
+ case 4:
+ default:
+ reloc = BFD_RELOC_32;
+ break;
+ case 8:
+ reloc = BFD_RELOC_64;
+ break;
+ }
+
+#ifdef TE_PE
+ if (exp->X_op == O_secrel)
+ {
+ exp->X_op = O_symbol;
+ reloc = BFD_RELOC_32_SECREL;
+ }
+#endif
+
+ fix_new_exp (frag, where, size, exp, pcrel, reloc);
+}
+
+#if defined (OBJ_COFF)
+void
+arm_validate_fix (fixS * fixP)
+{
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
+ && fixP->fx_addsy != NULL
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! THUMB_IS_FUNC (fixP->fx_addsy))
+ {
+ fixP->fx_addsy = find_real_start (fixP->fx_addsy);
+ }
+}
+#endif
+
+
+int
+arm_force_relocation (struct fix * fixp)
+{
+#if defined (OBJ_COFF) && defined (TE_PE)
+ if (fixp->fx_r_type == BFD_RELOC_RVA)
+ return 1;
+#endif
+
+ /* In case we have a call or a branch to a function in ARM ISA mode from
+ a thumb function or vice-versa force the relocation. These relocations
+ are cleared off for some cores that might have blx and simple transformations
+ are possible. */
+
+#ifdef OBJ_ELF
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_ARM_PCREL_JUMP:
+ case BFD_RELOC_ARM_PCREL_CALL:
+ case BFD_RELOC_THUMB_PCREL_BLX:
+ if (THUMB_IS_FUNC (fixp->fx_addsy))
+ return 1;
+ break;
+
+ case BFD_RELOC_ARM_PCREL_BLX:
+ case BFD_RELOC_THUMB_PCREL_BRANCH25:
+ case BFD_RELOC_THUMB_PCREL_BRANCH20:
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ if (ARM_IS_FUNC (fixp->fx_addsy))
+ return 1;
+ break;
+
+ default:
+ break;
+ }
+#endif
+
+ /* Resolve these relocations even if the symbol is extern or weak.
+ Technically this is probably wrong due to symbol preemption.
+ In practice these relocations do not have enough range to be useful
+ at dynamic link time, and some code (e.g. in the Linux kernel)
+ expects these references to be resolved. */
+ if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM8
+ || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2
+ || fixp->fx_r_type == BFD_RELOC_ARM_THUMB_OFFSET
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM_S2)
+ return 0;
+
+ /* Always leave these relocations for the linker. */
+ if ((fixp->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC
+ && fixp->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2)
+ || fixp->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0)
+ return 1;
+
+ /* Always generate relocations against function symbols. */
+ if (fixp->fx_r_type == BFD_RELOC_32
+ && fixp->fx_addsy
+ && (symbol_get_bfdsym (fixp->fx_addsy)->flags & BSF_FUNCTION))
+ return 1;
+
+ return generic_force_reloc (fixp);
+}
+
+#if defined (OBJ_ELF) || defined (OBJ_COFF)
+/* Relocations against function names must be left unadjusted,
+ so that the linker can use this information to generate interworking
+ stubs. The MIPS version of this function
+ also prevents relocations that are mips-16 specific, but I do not
+ know why it does this.
+
+ FIXME:
+ There is one other problem that ought to be addressed here, but
+ which currently is not: Taking the address of a label (rather
+ than a function) and then later jumping to that address. Such
+ addresses also ought to have their bottom bit set (assuming that
+ they reside in Thumb code), but at the moment they will not. */
+
+bfd_boolean
+arm_fix_adjustable (fixS * fixP)
+{
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Preserve relocations against symbols with function type. */
+ if (symbol_get_bfdsym (fixP->fx_addsy)->flags & BSF_FUNCTION)
+ return FALSE;
+
+ if (THUMB_IS_FUNC (fixP->fx_addsy)
+ && fixP->fx_subsy == NULL)
+ return FALSE;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return FALSE;
+
+ /* Don't allow symbols to be discarded on GOT related relocs. */
+ if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
+ || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
+ || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GOTDESC
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_CALL
+ || fixP->fx_r_type == BFD_RELOC_ARM_THM_TLS_CALL
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_DESCSEQ
+ || fixP->fx_r_type == BFD_RELOC_ARM_THM_TLS_DESCSEQ
+ || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
+ return FALSE;
+
+ /* Similarly for group relocations. */
+ if ((fixP->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC
+ && fixP->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2)
+ || fixP->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0)
+ return FALSE;
+
+ /* MOVW/MOVT REL relocations have limited offsets, so keep the symbols. */
+ if (fixP->fx_r_type == BFD_RELOC_ARM_MOVW
+ || fixP->fx_r_type == BFD_RELOC_ARM_MOVT
+ || fixP->fx_r_type == BFD_RELOC_ARM_MOVW_PCREL
+ || fixP->fx_r_type == BFD_RELOC_ARM_MOVT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVW
+ || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT
+ || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVW_PCREL
+ || fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT_PCREL)
+ return FALSE;
+
+ return TRUE;
+}
+#endif /* defined (OBJ_ELF) || defined (OBJ_COFF) */
+
+#ifdef OBJ_ELF
+
+const char *
+elf32_arm_target_format (void)
+{
+#ifdef TE_SYMBIAN
+ return (target_big_endian
+ ? "elf32-bigarm-symbian"
+ : "elf32-littlearm-symbian");
+#elif defined (TE_VXWORKS)
+ return (target_big_endian
+ ? "elf32-bigarm-vxworks"
+ : "elf32-littlearm-vxworks");
+#elif defined (TE_NACL)
+ return (target_big_endian
+ ? "elf32-bigarm-nacl"
+ : "elf32-littlearm-nacl");
+#else
+ if (target_big_endian)
+ return "elf32-bigarm";
+ else
+ return "elf32-littlearm";
+#endif
+}
+
+void
+armelf_frob_symbol (symbolS * symp,
+ int * puntp)
+{
+ elf_frob_symbol (symp, puntp);
+}
+#endif
+
+/* MD interface: Finalization. */
+
+void
+arm_cleanup (void)
+{
+ literal_pool * pool;
+
+ /* Ensure that all the IT blocks are properly closed. */
+ check_it_blocks_finished ();
+
+ for (pool = list_of_pools; pool; pool = pool->next)
+ {
+ /* Put it at the end of the relevant section. */
+ subseg_set (pool->section, pool->sub_section);
+#ifdef OBJ_ELF
+ arm_elf_change_section ();
+#endif
+ s_ltorg (0);
+ }
+}
+
+#ifdef OBJ_ELF
+/* Remove any excess mapping symbols generated for alignment frags in
+ SEC. We may have created a mapping symbol before a zero byte
+ alignment; remove it if there's a mapping symbol after the
+ alignment. */
+static void
+check_mapping_symbols (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
+ void *dummy ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragp;
+
+ if (seginfo == NULL || seginfo->frchainP == NULL)
+ return;
+
+ for (fragp = seginfo->frchainP->frch_root;
+ fragp != NULL;
+ fragp = fragp->fr_next)
+ {
+ symbolS *sym = fragp->tc_frag_data.last_map;
+ fragS *next = fragp->fr_next;
+
+ /* Variable-sized frags have been converted to fixed size by
+ this point. But if this was variable-sized to start with,
+ there will be a fixed-size frag after it. So don't handle
+ next == NULL. */
+ if (sym == NULL || next == NULL)
+ continue;
+
+ if (S_GET_VALUE (sym) < next->fr_address)
+ /* Not at the end of this frag. */
+ continue;
+ know (S_GET_VALUE (sym) == next->fr_address);
+
+ do
+ {
+ if (next->tc_frag_data.first_map != NULL)
+ {
+ /* Next frag starts with a mapping symbol. Discard this
+ one. */
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ if (next->fr_next == NULL)
+ {
+ /* This mapping symbol is at the end of the section. Discard
+ it. */
+ know (next->fr_fix == 0 && next->fr_var == 0);
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ break;
+ }
+
+ /* As long as we have empty frags without any mapping symbols,
+ keep looking. */
+ /* If the next frag is non-empty and does not start with a
+ mapping symbol, then this mapping symbol is required. */
+ if (next->fr_address != next->fr_next->fr_address)
+ break;
+
+ next = next->fr_next;
+ }
+ while (next != NULL);
+ }
+}
+#endif
+
+/* Adjust the symbol table. This marks Thumb symbols as distinct from
+ ARM ones. */
+
+void
+arm_adjust_symtab (void)
+{
+#ifdef OBJ_COFF
+ symbolS * sym;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ARM_IS_THUMB (sym))
+ {
+ if (THUMB_IS_FUNC (sym))
+ {
+ /* Mark the symbol as a Thumb function. */
+ if ( S_GET_STORAGE_CLASS (sym) == C_STAT
+ || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
+
+ else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
+ else
+ as_bad (_("%s: unexpected function type: %d"),
+ S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
+ }
+ else switch (S_GET_STORAGE_CLASS (sym))
+ {
+ case C_EXT:
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
+ break;
+ case C_STAT:
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
+ break;
+ case C_LABEL:
+ S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
+ }
+
+ if (ARM_IS_INTERWORK (sym))
+ coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
+ }
+#endif
+#ifdef OBJ_ELF
+ symbolS * sym;
+ char bind;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ARM_IS_THUMB (sym))
+ {
+ elf_symbol_type * elf_sym;
+
+ elf_sym = elf_symbol (symbol_get_bfdsym (sym));
+ bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info);
+
+ if (! bfd_is_arm_special_symbol_name (elf_sym->symbol.name,
+ BFD_ARM_SPECIAL_SYM_TYPE_ANY))
+ {
+ /* If it's a .thumb_func, declare it as so,
+ otherwise tag label as .code 16. */
+ if (THUMB_IS_FUNC (sym))
+ elf_sym->internal_elf_sym.st_target_internal
+ = ST_BRANCH_TO_THUMB;
+ else if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
+ elf_sym->internal_elf_sym.st_info =
+ ELF_ST_INFO (bind, STT_ARM_16BIT);
+ }
+ }
+ }
+
+ /* Remove any overlapping mapping symbols generated by alignment frags. */
+ bfd_map_over_sections (stdoutput, check_mapping_symbols, (char *) 0);
+ /* Now do generic ELF adjustments. */
+ elf_adjust_symtab ();
+#endif
+}
+
+/* MD interface: Initialization. */
+
+static void
+set_constant_flonums (void)
+{
+ int i;
+
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ if (atof_ieee ((char *) fp_const[i], 'x', fp_values[i]) == NULL)
+ abort ();
+}
+
+/* Auto-select Thumb mode if it's the only available instruction set for the
+ given architecture. */
+
+static void
+autoselect_thumb_from_cpu_variant (void)
+{
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1))
+ opcode_select (16);
+}
+
+void
+md_begin (void)
+{
+ unsigned mach;
+ unsigned int i;
+
+ if ( (arm_ops_hsh = hash_new ()) == NULL
+ || (arm_cond_hsh = hash_new ()) == NULL
+ || (arm_shift_hsh = hash_new ()) == NULL
+ || (arm_psr_hsh = hash_new ()) == NULL
+ || (arm_v7m_psr_hsh = hash_new ()) == NULL
+ || (arm_reg_hsh = hash_new ()) == NULL
+ || (arm_reloc_hsh = hash_new ()) == NULL
+ || (arm_barrier_opt_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
+ hash_insert (arm_ops_hsh, insns[i].template_name, (void *) (insns + i));
+ for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
+ hash_insert (arm_cond_hsh, conds[i].template_name, (void *) (conds + i));
+ for (i = 0; i < sizeof (shift_names) / sizeof (struct asm_shift_name); i++)
+ hash_insert (arm_shift_hsh, shift_names[i].name, (void *) (shift_names + i));
+ for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
+ hash_insert (arm_psr_hsh, psrs[i].template_name, (void *) (psrs + i));
+ for (i = 0; i < sizeof (v7m_psrs) / sizeof (struct asm_psr); i++)
+ hash_insert (arm_v7m_psr_hsh, v7m_psrs[i].template_name,
+ (void *) (v7m_psrs + i));
+ for (i = 0; i < sizeof (reg_names) / sizeof (struct reg_entry); i++)
+ hash_insert (arm_reg_hsh, reg_names[i].name, (void *) (reg_names + i));
+ for (i = 0;
+ i < sizeof (barrier_opt_names) / sizeof (struct asm_barrier_opt);
+ i++)
+ hash_insert (arm_barrier_opt_hsh, barrier_opt_names[i].template_name,
+ (void *) (barrier_opt_names + i));
+#ifdef OBJ_ELF
+ for (i = 0; i < ARRAY_SIZE (reloc_names); i++)
+ {
+ struct reloc_entry * entry = reloc_names + i;
+
+ if (arm_is_eabi() && entry->reloc == BFD_RELOC_ARM_PLT32)
+ /* This makes encode_branch() use the EABI versions of this relocation. */
+ entry->reloc = BFD_RELOC_UNUSED;
+
+ hash_insert (arm_reloc_hsh, entry->name, (void *) entry);
+ }
+#endif
+
+ set_constant_flonums ();
+
+ /* Set the cpu variant based on the command-line options. We prefer
+ -mcpu= over -march= if both are set (as for GCC); and we prefer
+ -mfpu= over any other way of setting the floating point unit.
+ Use of legacy options with new options are faulted. */
+ if (legacy_cpu)
+ {
+ if (mcpu_cpu_opt || march_cpu_opt)
+ as_bad (_("use of old and new-style options to set CPU type"));
+
+ mcpu_cpu_opt = legacy_cpu;
+ }
+ else if (!mcpu_cpu_opt)
+ mcpu_cpu_opt = march_cpu_opt;
+
+ if (legacy_fpu)
+ {
+ if (mfpu_opt)
+ as_bad (_("use of old and new-style options to set FPU type"));
+
+ mfpu_opt = legacy_fpu;
+ }
+ else if (!mfpu_opt)
+ {
+#if !(defined (EABI_DEFAULT) || defined (TE_LINUX) \
+ || defined (TE_NetBSD) || defined (TE_VXWORKS))
+ /* Some environments specify a default FPU. If they don't, infer it
+ from the processor. */
+ if (mcpu_fpu_opt)
+ mfpu_opt = mcpu_fpu_opt;
+ else
+ mfpu_opt = march_fpu_opt;
+#else
+ mfpu_opt = &fpu_default;
+#endif
+ }
+
+ if (!mfpu_opt)
+ {
+ if (mcpu_cpu_opt != NULL)
+ mfpu_opt = &fpu_default;
+ else if (mcpu_fpu_opt != NULL && ARM_CPU_HAS_FEATURE (*mcpu_fpu_opt, arm_ext_v5))
+ mfpu_opt = &fpu_arch_vfp_v2;
+ else
+ mfpu_opt = &fpu_arch_fpa;
+ }
+
+#ifdef CPU_DEFAULT
+ if (!mcpu_cpu_opt)
+ {
+ mcpu_cpu_opt = &cpu_default;
+ selected_cpu = cpu_default;
+ }
+#else
+ if (mcpu_cpu_opt)
+ selected_cpu = *mcpu_cpu_opt;
+ else
+ mcpu_cpu_opt = &arm_arch_any;
+#endif
+
+ ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+
+ autoselect_thumb_from_cpu_variant ();
+
+ arm_arch_used = thumb_arch_used = arm_arch_none;
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ {
+ unsigned int flags = 0;
+
+#if defined OBJ_ELF
+ flags = meabi_flags;
+
+ switch (meabi_flags)
+ {
+ case EF_ARM_EABI_UNKNOWN:
+#endif
+ /* Set the flags in the private structure. */
+ if (uses_apcs_26) flags |= F_APCS26;
+ if (support_interwork) flags |= F_INTERWORK;
+ if (uses_apcs_float) flags |= F_APCS_FLOAT;
+ if (pic_code) flags |= F_PIC;
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_any_hard))
+ flags |= F_SOFT_FLOAT;
+
+ switch (mfloat_abi_opt)
+ {
+ case ARM_FLOAT_ABI_SOFT:
+ case ARM_FLOAT_ABI_SOFTFP:
+ flags |= F_SOFT_FLOAT;
+ break;
+
+ case ARM_FLOAT_ABI_HARD:
+ if (flags & F_SOFT_FLOAT)
+ as_bad (_("hard-float conflicts with specified fpu"));
+ break;
+ }
+
+ /* Using pure-endian doubles (even if soft-float). */
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_endian_pure))
+ flags |= F_VFP_FLOAT;
+
+#if defined OBJ_ELF
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_arch_maverick))
+ flags |= EF_ARM_MAVERICK_FLOAT;
+ break;
+
+ case EF_ARM_EABI_VER4:
+ case EF_ARM_EABI_VER5:
+ /* No additional flags to set. */
+ break;
+
+ default:
+ abort ();
+ }
+#endif
+ bfd_set_private_flags (stdoutput, flags);
+
+ /* We have run out flags in the COFF header to encode the
+ status of ATPCS support, so instead we create a dummy,
+ empty, debug section called .arm.atpcs. */
+ if (atpcs)
+ {
+ asection * sec;
+
+ sec = bfd_make_section (stdoutput, ".arm.atpcs");
+
+ if (sec != NULL)
+ {
+ bfd_set_section_flags
+ (stdoutput, sec, SEC_READONLY | SEC_DEBUGGING /* | SEC_HAS_CONTENTS */);
+ bfd_set_section_size (stdoutput, sec, 0);
+ bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
+ }
+ }
+ }
+#endif
+
+ /* Record the CPU type as well. */
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2))
+ mach = bfd_mach_arm_iWMMXt2;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt))
+ mach = bfd_mach_arm_iWMMXt;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_xscale))
+ mach = bfd_mach_arm_XScale;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_maverick))
+ mach = bfd_mach_arm_ep9312;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v5e))
+ mach = bfd_mach_arm_5TE;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v5))
+ {
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v4t))
+ mach = bfd_mach_arm_5T;
+ else
+ mach = bfd_mach_arm_5;
+ }
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v4))
+ {
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v4t))
+ mach = bfd_mach_arm_4T;
+ else
+ mach = bfd_mach_arm_4;
+ }
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v3m))
+ mach = bfd_mach_arm_3M;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v3))
+ mach = bfd_mach_arm_3;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v2s))
+ mach = bfd_mach_arm_2a;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v2))
+ mach = bfd_mach_arm_2;
+ else
+ mach = bfd_mach_arm_unknown;
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
+}
+
+/* Command line processing. */
+
+/* md_parse_option
+ Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option.
+
+ This routine is somewhat complicated by the need for backwards
+ compatibility (since older releases of gcc can't be changed).
+ The new options try to make the interface as compatible as
+ possible with GCC.
+
+ New options (supported) are:
+
+ -mcpu=<cpu name> Assemble for selected processor
+ -march=<architecture name> Assemble for selected architecture
+ -mfpu=<fpu architecture> Assemble for selected FPU.
+ -EB/-mbig-endian Big-endian
+ -EL/-mlittle-endian Little-endian
+ -k Generate PIC code
+ -mthumb Start in Thumb mode
+ -mthumb-interwork Code supports ARM/Thumb interworking
+
+ -m[no-]warn-deprecated Warn about deprecated features
+
+ For now we will also provide support for:
+
+ -mapcs-32 32-bit Program counter
+ -mapcs-26 26-bit Program counter
+ -macps-float Floats passed in FP registers
+ -mapcs-reentrant Reentrant code
+ -matpcs
+ (sometime these will probably be replaced with -mapcs=<list of options>
+ and -matpcs=<list of options>)
+
+ The remaining options are only supported for back-wards compatibility.
+ Cpu variants, the arm part is optional:
+ -m[arm]1 Currently not supported.
+ -m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor
+ -m[arm]3 Arm 3 processor
+ -m[arm]6[xx], Arm 6 processors
+ -m[arm]7[xx][t][[d]m] Arm 7 processors
+ -m[arm]8[10] Arm 8 processors
+ -m[arm]9[20][tdmi] Arm 9 processors
+ -mstrongarm[110[0]] StrongARM processors
+ -mxscale XScale processors
+ -m[arm]v[2345[t[e]]] Arm architectures
+ -mall All (except the ARM1)
+ FP variants:
+ -mfpa10, -mfpa11 FPA10 and 11 co-processor instructions
+ -mfpe-old (No float load/store multiples)
+ -mvfpxd VFP Single precision
+ -mvfp All VFP
+ -mno-fpu Disable all floating point instructions
+
+ The following CPU names are recognized:
+ arm1, arm2, arm250, arm3, arm6, arm600, arm610, arm620,
+ arm7, arm7m, arm7d, arm7dm, arm7di, arm7dmi, arm70, arm700,
+ arm700i, arm710 arm710t, arm720, arm720t, arm740t, arm710c,
+ arm7100, arm7500, arm7500fe, arm7tdmi, arm8, arm810, arm9,
+ arm920, arm920t, arm940t, arm946, arm966, arm9tdmi, arm9e,
+ arm10t arm10e, arm1020t, arm1020e, arm10200e,
+ strongarm, strongarm110, strongarm1100, strongarm1110, xscale.
+
+ */
+
+const char * md_shortopts = "m:k";
+
+#ifdef ARM_BI_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#else
+#if TARGET_BYTES_BIG_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#else
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#endif
+#endif
+#define OPTION_FIX_V4BX (OPTION_MD_BASE + 2)
+
+struct option md_longopts[] =
+{
+#ifdef OPTION_EB
+ {"EB", no_argument, NULL, OPTION_EB},
+#endif
+#ifdef OPTION_EL
+ {"EL", no_argument, NULL, OPTION_EL},
+#endif
+ {"fix-v4bx", no_argument, NULL, OPTION_FIX_V4BX},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+struct arm_option_table
+{
+ char *option; /* Option name to match. */
+ char *help; /* Help information. */
+ int *var; /* Variable to change. */
+ int value; /* What to change it to. */
+ char *deprecated; /* If non-null, print this message. */
+};
+
+struct arm_option_table arm_opts[] =
+{
+ {"k", N_("generate PIC code"), &pic_code, 1, NULL},
+ {"mthumb", N_("assemble Thumb code"), &thumb_mode, 1, NULL},
+ {"mthumb-interwork", N_("support ARM/Thumb interworking"),
+ &support_interwork, 1, NULL},
+ {"mapcs-32", N_("code uses 32-bit program counter"), &uses_apcs_26, 0, NULL},
+ {"mapcs-26", N_("code uses 26-bit program counter"), &uses_apcs_26, 1, NULL},
+ {"mapcs-float", N_("floating point args are in fp regs"), &uses_apcs_float,
+ 1, NULL},
+ {"mapcs-reentrant", N_("re-entrant code"), &pic_code, 1, NULL},
+ {"matpcs", N_("code is ATPCS conformant"), &atpcs, 1, NULL},
+ {"mbig-endian", N_("assemble for big-endian"), &target_big_endian, 1, NULL},
+ {"mlittle-endian", N_("assemble for little-endian"), &target_big_endian, 0,
+ NULL},
+
+ /* These are recognized by the assembler, but have no affect on code. */
+ {"mapcs-frame", N_("use frame pointer"), NULL, 0, NULL},
+ {"mapcs-stack-check", N_("use stack size checking"), NULL, 0, NULL},
+
+ {"mwarn-deprecated", NULL, &warn_on_deprecated, 1, NULL},
+ {"mno-warn-deprecated", N_("do not warn on use of deprecated feature"),
+ &warn_on_deprecated, 0, NULL},
+ {NULL, NULL, NULL, 0, NULL}
+};
+
+struct arm_legacy_option_table
+{
+ char *option; /* Option name to match. */
+ const arm_feature_set **var; /* Variable to change. */
+ const arm_feature_set value; /* What to change it to. */
+ char *deprecated; /* If non-null, print this message. */
+};
+
+const struct arm_legacy_option_table arm_legacy_opts[] =
+{
+ /* DON'T add any new processors to this list -- we want the whole list
+ to go away... Add them to the processors table instead. */
+ {"marm1", &legacy_cpu, ARM_ARCH_V1, N_("use -mcpu=arm1")},
+ {"m1", &legacy_cpu, ARM_ARCH_V1, N_("use -mcpu=arm1")},
+ {"marm2", &legacy_cpu, ARM_ARCH_V2, N_("use -mcpu=arm2")},
+ {"m2", &legacy_cpu, ARM_ARCH_V2, N_("use -mcpu=arm2")},
+ {"marm250", &legacy_cpu, ARM_ARCH_V2S, N_("use -mcpu=arm250")},
+ {"m250", &legacy_cpu, ARM_ARCH_V2S, N_("use -mcpu=arm250")},
+ {"marm3", &legacy_cpu, ARM_ARCH_V2S, N_("use -mcpu=arm3")},
+ {"m3", &legacy_cpu, ARM_ARCH_V2S, N_("use -mcpu=arm3")},
+ {"marm6", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm6")},
+ {"m6", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm6")},
+ {"marm600", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm600")},
+ {"m600", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm600")},
+ {"marm610", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm610")},
+ {"m610", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm610")},
+ {"marm620", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm620")},
+ {"m620", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm620")},
+ {"marm7", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7")},
+ {"m7", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7")},
+ {"marm70", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm70")},
+ {"m70", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm70")},
+ {"marm700", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm700")},
+ {"m700", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm700")},
+ {"marm700i", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm700i")},
+ {"m700i", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm700i")},
+ {"marm710", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm710")},
+ {"m710", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm710")},
+ {"marm710c", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm710c")},
+ {"m710c", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm710c")},
+ {"marm720", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm720")},
+ {"m720", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm720")},
+ {"marm7d", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7d")},
+ {"m7d", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7d")},
+ {"marm7di", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7di")},
+ {"m7di", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7di")},
+ {"marm7m", &legacy_cpu, ARM_ARCH_V3M, N_("use -mcpu=arm7m")},
+ {"m7m", &legacy_cpu, ARM_ARCH_V3M, N_("use -mcpu=arm7m")},
+ {"marm7dm", &legacy_cpu, ARM_ARCH_V3M, N_("use -mcpu=arm7dm")},
+ {"m7dm", &legacy_cpu, ARM_ARCH_V3M, N_("use -mcpu=arm7dm")},
+ {"marm7dmi", &legacy_cpu, ARM_ARCH_V3M, N_("use -mcpu=arm7dmi")},
+ {"m7dmi", &legacy_cpu, ARM_ARCH_V3M, N_("use -mcpu=arm7dmi")},
+ {"marm7100", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7100")},
+ {"m7100", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7100")},
+ {"marm7500", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7500")},
+ {"m7500", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7500")},
+ {"marm7500fe", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7500fe")},
+ {"m7500fe", &legacy_cpu, ARM_ARCH_V3, N_("use -mcpu=arm7500fe")},
+ {"marm7t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm7tdmi")},
+ {"m7t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm7tdmi")},
+ {"marm7tdmi", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm7tdmi")},
+ {"m7tdmi", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm7tdmi")},
+ {"marm710t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm710t")},
+ {"m710t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm710t")},
+ {"marm720t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm720t")},
+ {"m720t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm720t")},
+ {"marm740t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm740t")},
+ {"m740t", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm740t")},
+ {"marm8", &legacy_cpu, ARM_ARCH_V4, N_("use -mcpu=arm8")},
+ {"m8", &legacy_cpu, ARM_ARCH_V4, N_("use -mcpu=arm8")},
+ {"marm810", &legacy_cpu, ARM_ARCH_V4, N_("use -mcpu=arm810")},
+ {"m810", &legacy_cpu, ARM_ARCH_V4, N_("use -mcpu=arm810")},
+ {"marm9", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm9")},
+ {"m9", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm9")},
+ {"marm9tdmi", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm9tdmi")},
+ {"m9tdmi", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm9tdmi")},
+ {"marm920", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm920")},
+ {"m920", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm920")},
+ {"marm940", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm940")},
+ {"m940", &legacy_cpu, ARM_ARCH_V4T, N_("use -mcpu=arm940")},
+ {"mstrongarm", &legacy_cpu, ARM_ARCH_V4, N_("use -mcpu=strongarm")},
+ {"mstrongarm110", &legacy_cpu, ARM_ARCH_V4,
+ N_("use -mcpu=strongarm110")},
+ {"mstrongarm1100", &legacy_cpu, ARM_ARCH_V4,
+ N_("use -mcpu=strongarm1100")},
+ {"mstrongarm1110", &legacy_cpu, ARM_ARCH_V4,
+ N_("use -mcpu=strongarm1110")},
+ {"mxscale", &legacy_cpu, ARM_ARCH_XSCALE, N_("use -mcpu=xscale")},
+ {"miwmmxt", &legacy_cpu, ARM_ARCH_IWMMXT, N_("use -mcpu=iwmmxt")},
+ {"mall", &legacy_cpu, ARM_ANY, N_("use -mcpu=all")},
+
+ /* Architecture variants -- don't add any more to this list either. */
+ {"mv2", &legacy_cpu, ARM_ARCH_V2, N_("use -march=armv2")},
+ {"marmv2", &legacy_cpu, ARM_ARCH_V2, N_("use -march=armv2")},
+ {"mv2a", &legacy_cpu, ARM_ARCH_V2S, N_("use -march=armv2a")},
+ {"marmv2a", &legacy_cpu, ARM_ARCH_V2S, N_("use -march=armv2a")},
+ {"mv3", &legacy_cpu, ARM_ARCH_V3, N_("use -march=armv3")},
+ {"marmv3", &legacy_cpu, ARM_ARCH_V3, N_("use -march=armv3")},
+ {"mv3m", &legacy_cpu, ARM_ARCH_V3M, N_("use -march=armv3m")},
+ {"marmv3m", &legacy_cpu, ARM_ARCH_V3M, N_("use -march=armv3m")},
+ {"mv4", &legacy_cpu, ARM_ARCH_V4, N_("use -march=armv4")},
+ {"marmv4", &legacy_cpu, ARM_ARCH_V4, N_("use -march=armv4")},
+ {"mv4t", &legacy_cpu, ARM_ARCH_V4T, N_("use -march=armv4t")},
+ {"marmv4t", &legacy_cpu, ARM_ARCH_V4T, N_("use -march=armv4t")},
+ {"mv5", &legacy_cpu, ARM_ARCH_V5, N_("use -march=armv5")},
+ {"marmv5", &legacy_cpu, ARM_ARCH_V5, N_("use -march=armv5")},
+ {"mv5t", &legacy_cpu, ARM_ARCH_V5T, N_("use -march=armv5t")},
+ {"marmv5t", &legacy_cpu, ARM_ARCH_V5T, N_("use -march=armv5t")},
+ {"mv5e", &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
+ {"marmv5e", &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
+
+ /* Floating point variants -- don't add any more to this list either. */
+ {"mfpe-old", &legacy_fpu, FPU_ARCH_FPE, N_("use -mfpu=fpe")},
+ {"mfpa10", &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa10")},
+ {"mfpa11", &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa11")},
+ {"mno-fpu", &legacy_fpu, ARM_ARCH_NONE,
+ N_("use either -mfpu=softfpa or -mfpu=softvfp")},
+
+ {NULL, NULL, ARM_ARCH_NONE, NULL}
+};
+
+struct arm_cpu_option_table
+{
+ char *name;
+ size_t name_len;
+ const arm_feature_set value;
+ /* For some CPUs we assume an FPU unless the user explicitly sets
+ -mfpu=... */
+ const arm_feature_set default_fpu;
+ /* The canonical name of the CPU, or NULL to use NAME converted to upper
+ case. */
+ const char *canonical_name;
+};
+
+/* This list should, at a minimum, contain all the cpu names
+ recognized by GCC. */
+#define ARM_CPU_OPT(N, V, DF, CN) { N, sizeof (N) - 1, V, DF, CN }
+static const struct arm_cpu_option_table arm_cpus[] =
+{
+ ARM_CPU_OPT ("all", ARM_ANY, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm1", ARM_ARCH_V1, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm2", ARM_ARCH_V2, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm250", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm3", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm6", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm60", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm600", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm610", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm620", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7d", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7di", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm70", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm700", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm700i", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm710", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm720", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm710c", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7100", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7500", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm8", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm810", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm9", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm920", ARM_ARCH_V4T, FPU_ARCH_FPA, "ARM920T"),
+ ARM_CPU_OPT ("arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("fa526", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("fa626", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ /* For V5 or later processors we default to using VFP; but the user
+ should really set the FPU type explicitly. */
+ ARM_CPU_OPT ("arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"),
+ ARM_CPU_OPT ("arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"),
+ ARM_CPU_OPT ("arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM946E-S"),
+ ARM_CPU_OPT ("arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM966E-S"),
+ ARM_CPU_OPT ("arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL),
+ ARM_CPU_OPT ("arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL),
+ ARM_CPU_OPT ("arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM1020E"),
+ ARM_CPU_OPT ("arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL),
+ ARM_CPU_OPT ("arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2,
+ "ARM1026EJ-S"),
+ ARM_CPU_OPT ("arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa606te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa616te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa626te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fmp626", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa726te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"),
+ ARM_CPU_OPT ("arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2,
+ "ARM1136JF-S"),
+ ARM_CPU_OPT ("arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, "MPCore"),
+ ARM_CPU_OPT ("mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, "MPCore"),
+ ARM_CPU_OPT ("arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("cortex-a5", ARM_ARCH_V7A_MP_SEC,
+ FPU_NONE, "Cortex-A5"),
+ ARM_CPU_OPT ("cortex-a7", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A7"),
+ ARM_CPU_OPT ("cortex-a8", ARM_ARCH_V7A_SEC,
+ ARM_FEATURE (0, FPU_VFP_V3
+ | FPU_NEON_EXT_V1),
+ "Cortex-A8"),
+ ARM_CPU_OPT ("cortex-a9", ARM_ARCH_V7A_MP_SEC,
+ ARM_FEATURE (0, FPU_VFP_V3
+ | FPU_NEON_EXT_V1),
+ "Cortex-A9"),
+ ARM_CPU_OPT ("cortex-a12", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A12"),
+ ARM_CPU_OPT ("cortex-a15", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A15"),
+ ARM_CPU_OPT ("cortex-a17", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A17"),
+ ARM_CPU_OPT ("cortex-a53", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A53"),
+ ARM_CPU_OPT ("cortex-a57", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A57"),
+ ARM_CPU_OPT ("cortex-r4", ARM_ARCH_V7R, FPU_NONE, "Cortex-R4"),
+ ARM_CPU_OPT ("cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16,
+ "Cortex-R4F"),
+ ARM_CPU_OPT ("cortex-r5", ARM_ARCH_V7R_IDIV,
+ FPU_NONE, "Cortex-R5"),
+ ARM_CPU_OPT ("cortex-r7", ARM_ARCH_V7R_IDIV,
+ FPU_ARCH_VFP_V3D16,
+ "Cortex-R7"),
+ ARM_CPU_OPT ("cortex-m4", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M4"),
+ ARM_CPU_OPT ("cortex-m3", ARM_ARCH_V7M, FPU_NONE, "Cortex-M3"),
+ ARM_CPU_OPT ("cortex-m1", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M1"),
+ ARM_CPU_OPT ("cortex-m0", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0"),
+ ARM_CPU_OPT ("cortex-m0plus", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0+"),
+ /* ??? XSCALE is really an architecture. */
+ ARM_CPU_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL),
+ /* ??? iwmmxt is not a processor. */
+ ARM_CPU_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL),
+ /* Maverick */
+ ARM_CPU_OPT ("ep9312", ARM_FEATURE (ARM_AEXT_V4T, ARM_CEXT_MAVERICK),
+ FPU_ARCH_MAVERICK, "ARM920T"),
+ /* Marvell processors. */
+ ARM_CPU_OPT ("marvell-pj4", ARM_FEATURE (ARM_AEXT_V7A | ARM_EXT_MP | ARM_EXT_SEC, 0),
+ FPU_ARCH_VFP_V3D16, NULL),
+
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL }
+};
+#undef ARM_CPU_OPT
+
+struct arm_arch_option_table
+{
+ char *name;
+ size_t name_len;
+ const arm_feature_set value;
+ const arm_feature_set default_fpu;
+};
+
+/* This list should, at a minimum, contain all the architecture names
+ recognized by GCC. */
+#define ARM_ARCH_OPT(N, V, DF) { N, sizeof (N) - 1, V, DF }
+static const struct arm_arch_option_table arm_archs[] =
+{
+ ARM_ARCH_OPT ("all", ARM_ANY, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv1", ARM_ARCH_V1, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv2", ARM_ARCH_V2, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv2a", ARM_ARCH_V2S, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv2s", ARM_ARCH_V2S, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv3", ARM_ARCH_V3, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv3m", ARM_ARCH_V3M, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4", ARM_ARCH_V4, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4xm", ARM_ARCH_V4xM, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4t", ARM_ARCH_V4T, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4txm", ARM_ARCH_V4TxM, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv5", ARM_ARCH_V5, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5t", ARM_ARCH_V5T, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5txm", ARM_ARCH_V5TxM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6", ARM_ARCH_V6, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6j", ARM_ARCH_V6, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6s-m", ARM_ARCH_V6SM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7", ARM_ARCH_V7, FPU_ARCH_VFP),
+ /* The official spelling of the ARMv7 profile variants is the dashed form.
+ Accept the non-dashed form for compatibility with old toolchains. */
+ ARM_ARCH_OPT ("armv7a", ARM_ARCH_V7A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7ve", ARM_ARCH_V7VE, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7r", ARM_ARCH_V7R, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8-a", ARM_ARCH_V8A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP),
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE }
+};
+#undef ARM_ARCH_OPT
+
+/* ISA extensions in the co-processor and main instruction set space. */
+struct arm_option_extension_value_table
+{
+ char *name;
+ size_t name_len;
+ const arm_feature_set value;
+ const arm_feature_set allowed_archs;
+};
+
+/* The following table must be in alphabetical order with a NULL last entry.
+ */
+#define ARM_EXT_OPT(N, V, AA) { N, sizeof (N) - 1, V, AA }
+static const struct arm_option_extension_value_table arm_extensions[] =
+{
+ ARM_EXT_OPT ("crc", ARCH_CRC_ARMV8, ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("crypto", FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("fp", FPU_ARCH_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0),
+ ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)),
+ ARM_EXT_OPT ("iwmmxt",ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY),
+ ARM_EXT_OPT ("iwmmxt2",
+ ARM_FEATURE (0, ARM_CEXT_IWMMXT2), ARM_ANY),
+ ARM_EXT_OPT ("maverick",
+ ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY),
+ ARM_EXT_OPT ("mp", ARM_FEATURE (ARM_EXT_MP, 0),
+ ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)),
+ ARM_EXT_OPT ("simd", FPU_ARCH_NEON_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("os", ARM_FEATURE (ARM_EXT_OS, 0),
+ ARM_FEATURE (ARM_EXT_V6M, 0)),
+ ARM_EXT_OPT ("sec", ARM_FEATURE (ARM_EXT_SEC, 0),
+ ARM_FEATURE (ARM_EXT_V6K | ARM_EXT_V7A, 0)),
+ ARM_EXT_OPT ("virt", ARM_FEATURE (ARM_EXT_VIRT | ARM_EXT_ADIV
+ | ARM_EXT_DIV, 0),
+ ARM_FEATURE (ARM_EXT_V7A, 0)),
+ ARM_EXT_OPT ("xscale",ARM_FEATURE (0, ARM_CEXT_XSCALE), ARM_ANY),
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE }
+};
+#undef ARM_EXT_OPT
+
+/* ISA floating-point and Advanced SIMD extensions. */
+struct arm_option_fpu_value_table
+{
+ char *name;
+ const arm_feature_set value;
+};
+
+/* This list should, at a minimum, contain all the fpu names
+ recognized by GCC. */
+static const struct arm_option_fpu_value_table arm_fpus[] =
+{
+ {"softfpa", FPU_NONE},
+ {"fpe", FPU_ARCH_FPE},
+ {"fpe2", FPU_ARCH_FPE},
+ {"fpe3", FPU_ARCH_FPA}, /* Third release supports LFM/SFM. */
+ {"fpa", FPU_ARCH_FPA},
+ {"fpa10", FPU_ARCH_FPA},
+ {"fpa11", FPU_ARCH_FPA},
+ {"arm7500fe", FPU_ARCH_FPA},
+ {"softvfp", FPU_ARCH_VFP},
+ {"softvfp+vfp", FPU_ARCH_VFP_V2},
+ {"vfp", FPU_ARCH_VFP_V2},
+ {"vfp9", FPU_ARCH_VFP_V2},
+ {"vfp3", FPU_ARCH_VFP_V3}, /* For backwards compatbility. */
+ {"vfp10", FPU_ARCH_VFP_V2},
+ {"vfp10-r0", FPU_ARCH_VFP_V1},
+ {"vfpxd", FPU_ARCH_VFP_V1xD},
+ {"vfpv2", FPU_ARCH_VFP_V2},
+ {"vfpv3", FPU_ARCH_VFP_V3},
+ {"vfpv3-fp16", FPU_ARCH_VFP_V3_FP16},
+ {"vfpv3-d16", FPU_ARCH_VFP_V3D16},
+ {"vfpv3-d16-fp16", FPU_ARCH_VFP_V3D16_FP16},
+ {"vfpv3xd", FPU_ARCH_VFP_V3xD},
+ {"vfpv3xd-fp16", FPU_ARCH_VFP_V3xD_FP16},
+ {"arm1020t", FPU_ARCH_VFP_V1},
+ {"arm1020e", FPU_ARCH_VFP_V2},
+ {"arm1136jfs", FPU_ARCH_VFP_V2},
+ {"arm1136jf-s", FPU_ARCH_VFP_V2},
+ {"maverick", FPU_ARCH_MAVERICK},
+ {"neon", FPU_ARCH_VFP_V3_PLUS_NEON_V1},
+ {"neon-fp16", FPU_ARCH_NEON_FP16},
+ {"vfpv4", FPU_ARCH_VFP_V4},
+ {"vfpv4-d16", FPU_ARCH_VFP_V4D16},
+ {"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16},
+ {"neon-vfpv4", FPU_ARCH_NEON_VFP_V4},
+ {"fp-armv8", FPU_ARCH_VFP_ARMV8},
+ {"neon-fp-armv8", FPU_ARCH_NEON_VFP_ARMV8},
+ {"crypto-neon-fp-armv8",
+ FPU_ARCH_CRYPTO_NEON_VFP_ARMV8},
+ {NULL, ARM_ARCH_NONE}
+};
+
+struct arm_option_value_table
+{
+ char *name;
+ long value;
+};
+
+static const struct arm_option_value_table arm_float_abis[] =
+{
+ {"hard", ARM_FLOAT_ABI_HARD},
+ {"softfp", ARM_FLOAT_ABI_SOFTFP},
+ {"soft", ARM_FLOAT_ABI_SOFT},
+ {NULL, 0}
+};
+
+#ifdef OBJ_ELF
+/* We only know how to output GNU and ver 4/5 (AAELF) formats. */
+static const struct arm_option_value_table arm_eabis[] =
+{
+ {"gnu", EF_ARM_EABI_UNKNOWN},
+ {"4", EF_ARM_EABI_VER4},
+ {"5", EF_ARM_EABI_VER5},
+ {NULL, 0}
+};
+#endif
+
+struct arm_long_option_table
+{
+ char * option; /* Substring to match. */
+ char * help; /* Help information. */
+ int (* func) (char * subopt); /* Function to decode sub-option. */
+ char * deprecated; /* If non-null, print this message. */
+};
+
+static bfd_boolean
+arm_parse_extension (char *str, const arm_feature_set **opt_p)
+{
+ arm_feature_set *ext_set = (arm_feature_set *)
+ xmalloc (sizeof (arm_feature_set));
+
+ /* We insist on extensions being specified in alphabetical order, and with
+ extensions being added before being removed. We achieve this by having
+ the global ARM_EXTENSIONS table in alphabetical order, and using the
+ ADDING_VALUE variable to indicate whether we are adding an extension (1)
+ or removing it (0) and only allowing it to change in the order
+ -1 -> 1 -> 0. */
+ const struct arm_option_extension_value_table * opt = NULL;
+ int adding_value = -1;
+
+ /* Copy the feature set, so that we can modify it. */
+ *ext_set = **opt_p;
+ *opt_p = ext_set;
+
+ while (str != NULL && *str != 0)
+ {
+ char *ext;
+ size_t len;
+
+ if (*str != '+')
+ {
+ as_bad (_("invalid architectural extension"));
+ return FALSE;
+ }
+
+ str++;
+ ext = strchr (str, '+');
+
+ if (ext != NULL)
+ len = ext - str;
+ else
+ len = strlen (str);
+
+ if (len >= 2 && strncmp (str, "no", 2) == 0)
+ {
+ if (adding_value != 0)
+ {
+ adding_value = 0;
+ opt = arm_extensions;
+ }
+
+ len -= 2;
+ str += 2;
+ }
+ else if (len > 0)
+ {
+ if (adding_value == -1)
+ {
+ adding_value = 1;
+ opt = arm_extensions;
+ }
+ else if (adding_value != 1)
+ {
+ as_bad (_("must specify extensions to add before specifying "
+ "those to remove"));
+ return FALSE;
+ }
+ }
+
+ if (len == 0)
+ {
+ as_bad (_("missing architectural extension"));
+ return FALSE;
+ }
+
+ gas_assert (adding_value != -1);
+ gas_assert (opt != NULL);
+
+ /* Scan over the options table trying to find an exact match. */
+ for (; opt->name != NULL; opt++)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
+ {
+ /* Check we can apply the extension to this architecture. */
+ if (!ARM_CPU_HAS_FEATURE (*ext_set, opt->allowed_archs))
+ {
+ as_bad (_("extension does not apply to the base architecture"));
+ return FALSE;
+ }
+
+ /* Add or remove the extension. */
+ if (adding_value)
+ ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value);
+ else
+ ARM_CLEAR_FEATURE (*ext_set, *ext_set, opt->value);
+
+ break;
+ }
+
+ if (opt->name == NULL)
+ {
+ /* Did we fail to find an extension because it wasn't specified in
+ alphabetical order, or because it does not exist? */
+
+ for (opt = arm_extensions; opt->name != NULL; opt++)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
+ break;
+
+ if (opt->name == NULL)
+ as_bad (_("unknown architectural extension `%s'"), str);
+ else
+ as_bad (_("architectural extensions must be specified in "
+ "alphabetical order"));
+
+ return FALSE;
+ }
+ else
+ {
+ /* We should skip the extension we've just matched the next time
+ round. */
+ opt++;
+ }
+
+ str = ext;
+ };
+
+ return TRUE;
+}
+
+static bfd_boolean
+arm_parse_cpu (char *str)
+{
+ const struct arm_cpu_option_table *opt;
+ char *ext = strchr (str, '+');
+ size_t len;
+
+ if (ext != NULL)
+ len = ext - str;
+ else
+ len = strlen (str);
+
+ if (len == 0)
+ {
+ as_bad (_("missing cpu name `%s'"), str);
+ return FALSE;
+ }
+
+ for (opt = arm_cpus; opt->name != NULL; opt++)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
+ {
+ mcpu_cpu_opt = &opt->value;
+ mcpu_fpu_opt = &opt->default_fpu;
+ if (opt->canonical_name)
+ strcpy (selected_cpu_name, opt->canonical_name);
+ else
+ {
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ selected_cpu_name[i] = TOUPPER (opt->name[i]);
+ selected_cpu_name[i] = 0;
+ }
+
+ if (ext != NULL)
+ return arm_parse_extension (ext, &mcpu_cpu_opt);
+
+ return TRUE;
+ }
+
+ as_bad (_("unknown cpu `%s'"), str);
+ return FALSE;
+}
+
+static bfd_boolean
+arm_parse_arch (char *str)
+{
+ const struct arm_arch_option_table *opt;
+ char *ext = strchr (str, '+');
+ size_t len;
+
+ if (ext != NULL)
+ len = ext - str;
+ else
+ len = strlen (str);
+
+ if (len == 0)
+ {
+ as_bad (_("missing architecture name `%s'"), str);
+ return FALSE;
+ }
+
+ for (opt = arm_archs; opt->name != NULL; opt++)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
+ {
+ march_cpu_opt = &opt->value;
+ march_fpu_opt = &opt->default_fpu;
+ strcpy (selected_cpu_name, opt->name);
+
+ if (ext != NULL)
+ return arm_parse_extension (ext, &march_cpu_opt);
+
+ return TRUE;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), str);
+ return FALSE;
+}
+
+static bfd_boolean
+arm_parse_fpu (char * str)
+{
+ const struct arm_option_fpu_value_table * opt;
+
+ for (opt = arm_fpus; opt->name != NULL; opt++)
+ if (streq (opt->name, str))
+ {
+ mfpu_opt = &opt->value;
+ return TRUE;
+ }
+
+ as_bad (_("unknown floating point format `%s'\n"), str);
+ return FALSE;
+}
+
+static bfd_boolean
+arm_parse_float_abi (char * str)
+{
+ const struct arm_option_value_table * opt;
+
+ for (opt = arm_float_abis; opt->name != NULL; opt++)
+ if (streq (opt->name, str))
+ {
+ mfloat_abi_opt = opt->value;
+ return TRUE;
+ }
+
+ as_bad (_("unknown floating point abi `%s'\n"), str);
+ return FALSE;
+}
+
+#ifdef OBJ_ELF
+static bfd_boolean
+arm_parse_eabi (char * str)
+{
+ const struct arm_option_value_table *opt;
+
+ for (opt = arm_eabis; opt->name != NULL; opt++)
+ if (streq (opt->name, str))
+ {
+ meabi_flags = opt->value;
+ return TRUE;
+ }
+ as_bad (_("unknown EABI `%s'\n"), str);
+ return FALSE;
+}
+#endif
+
+static bfd_boolean
+arm_parse_it_mode (char * str)
+{
+ bfd_boolean ret = TRUE;
+
+ if (streq ("arm", str))
+ implicit_it_mode = IMPLICIT_IT_MODE_ARM;
+ else if (streq ("thumb", str))
+ implicit_it_mode = IMPLICIT_IT_MODE_THUMB;
+ else if (streq ("always", str))
+ implicit_it_mode = IMPLICIT_IT_MODE_ALWAYS;
+ else if (streq ("never", str))
+ implicit_it_mode = IMPLICIT_IT_MODE_NEVER;
+ else
+ {
+ as_bad (_("unknown implicit IT mode `%s', should be "\
+ "arm, thumb, always, or never."), str);
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+static bfd_boolean
+arm_ccs_mode (char * unused ATTRIBUTE_UNUSED)
+{
+ codecomposer_syntax = TRUE;
+ arm_comment_chars[0] = ';';
+ arm_line_separator_chars[0] = 0;
+ return TRUE;
+}
+
+struct arm_long_option_table arm_long_opts[] =
+{
+ {"mcpu=", N_("<cpu name>\t assemble for CPU <cpu name>"),
+ arm_parse_cpu, NULL},
+ {"march=", N_("<arch name>\t assemble for architecture <arch name>"),
+ arm_parse_arch, NULL},
+ {"mfpu=", N_("<fpu name>\t assemble for FPU architecture <fpu name>"),
+ arm_parse_fpu, NULL},
+ {"mfloat-abi=", N_("<abi>\t assemble for floating point ABI <abi>"),
+ arm_parse_float_abi, NULL},
+#ifdef OBJ_ELF
+ {"meabi=", N_("<ver>\t\t assemble for eabi version <ver>"),
+ arm_parse_eabi, NULL},
+#endif
+ {"mimplicit-it=", N_("<mode>\t controls implicit insertion of IT instructions"),
+ arm_parse_it_mode, NULL},
+ {"mccs", N_("\t\t\t TI CodeComposer Studio syntax compatibility mode"),
+ arm_ccs_mode, NULL},
+ {NULL, NULL, 0, NULL}
+};
+
+int
+md_parse_option (int c, char * arg)
+{
+ struct arm_option_table *opt;
+ const struct arm_legacy_option_table *fopt;
+ struct arm_long_option_table *lopt;
+
+ switch (c)
+ {
+#ifdef OPTION_EB
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+#endif
+
+#ifdef OPTION_EL
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+#endif
+
+ case OPTION_FIX_V4BX:
+ fix_v4bx = TRUE;
+ break;
+
+ case 'a':
+ /* Listing option. Just ignore these, we don't support additional
+ ones. */
+ return 0;
+
+ default:
+ for (opt = arm_opts; opt->option != NULL; opt++)
+ {
+ if (c == opt->option[0]
+ && ((arg == NULL && opt->option[1] == 0)
+ || streq (arg, opt->option + 1)))
+ {
+ /* If the option is deprecated, tell the user. */
+ if (warn_on_deprecated && opt->deprecated != NULL)
+ as_tsktsk (_("option `-%c%s' is deprecated: %s"), c,
+ arg ? arg : "", _(opt->deprecated));
+
+ if (opt->var != NULL)
+ *opt->var = opt->value;
+
+ return 1;
+ }
+ }
+
+ for (fopt = arm_legacy_opts; fopt->option != NULL; fopt++)
+ {
+ if (c == fopt->option[0]
+ && ((arg == NULL && fopt->option[1] == 0)
+ || streq (arg, fopt->option + 1)))
+ {
+ /* If the option is deprecated, tell the user. */
+ if (warn_on_deprecated && fopt->deprecated != NULL)
+ as_tsktsk (_("option `-%c%s' is deprecated: %s"), c,
+ arg ? arg : "", _(fopt->deprecated));
+
+ if (fopt->var != NULL)
+ *fopt->var = &fopt->value;
+
+ return 1;
+ }
+ }
+
+ for (lopt = arm_long_opts; lopt->option != NULL; lopt++)
+ {
+ /* These options are expected to have an argument. */
+ if (c == lopt->option[0]
+ && arg != NULL
+ && strncmp (arg, lopt->option + 1,
+ strlen (lopt->option + 1)) == 0)
+ {
+ /* If the option is deprecated, tell the user. */
+ if (warn_on_deprecated && lopt->deprecated != NULL)
+ as_tsktsk (_("option `-%c%s' is deprecated: %s"), c, arg,
+ _(lopt->deprecated));
+
+ /* Call the sup-option parser. */
+ return lopt->func (arg + strlen (lopt->option) - 1);
+ }
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * fp)
+{
+ struct arm_option_table *opt;
+ struct arm_long_option_table *lopt;
+
+ fprintf (fp, _(" ARM-specific assembler options:\n"));
+
+ for (opt = arm_opts; opt->option != NULL; opt++)
+ if (opt->help != NULL)
+ fprintf (fp, " -%-23s%s\n", opt->option, _(opt->help));
+
+ for (lopt = arm_long_opts; lopt->option != NULL; lopt++)
+ if (lopt->help != NULL)
+ fprintf (fp, " -%s%s\n", lopt->option, _(lopt->help));
+
+#ifdef OPTION_EB
+ fprintf (fp, _("\
+ -EB assemble code for a big-endian cpu\n"));
+#endif
+
+#ifdef OPTION_EL
+ fprintf (fp, _("\
+ -EL assemble code for a little-endian cpu\n"));
+#endif
+
+ fprintf (fp, _("\
+ --fix-v4bx Allow BX in ARMv4 code\n"));
+}
+
+
+#ifdef OBJ_ELF
+typedef struct
+{
+ int val;
+ arm_feature_set flags;
+} cpu_arch_ver_table;
+
+/* Mapping from CPU features to EABI CPU arch values. Table must be sorted
+ least features first. */
+static const cpu_arch_ver_table cpu_arch_ver[] =
+{
+ {1, ARM_ARCH_V4},
+ {2, ARM_ARCH_V4T},
+ {3, ARM_ARCH_V5},
+ {3, ARM_ARCH_V5T},
+ {4, ARM_ARCH_V5TE},
+ {5, ARM_ARCH_V5TEJ},
+ {6, ARM_ARCH_V6},
+ {9, ARM_ARCH_V6K},
+ {7, ARM_ARCH_V6Z},
+ {11, ARM_ARCH_V6M},
+ {12, ARM_ARCH_V6SM},
+ {8, ARM_ARCH_V6T2},
+ {10, ARM_ARCH_V7VE},
+ {10, ARM_ARCH_V7R},
+ {10, ARM_ARCH_V7M},
+ {14, ARM_ARCH_V8A},
+ {0, ARM_ARCH_NONE}
+};
+
+/* Set an attribute if it has not already been set by the user. */
+static void
+aeabi_set_attribute_int (int tag, int value)
+{
+ if (tag < 1
+ || tag >= NUM_KNOWN_OBJ_ATTRIBUTES
+ || !attributes_set_explicitly[tag])
+ bfd_elf_add_proc_attr_int (stdoutput, tag, value);
+}
+
+static void
+aeabi_set_attribute_string (int tag, const char *value)
+{
+ if (tag < 1
+ || tag >= NUM_KNOWN_OBJ_ATTRIBUTES
+ || !attributes_set_explicitly[tag])
+ bfd_elf_add_proc_attr_string (stdoutput, tag, value);
+}
+
+/* Set the public EABI object attributes. */
+static void
+aeabi_set_public_attributes (void)
+{
+ int arch;
+ char profile;
+ int virt_sec = 0;
+ int fp16_optional = 0;
+ arm_feature_set flags;
+ arm_feature_set tmp;
+ const cpu_arch_ver_table *p;
+
+ /* Choose the architecture based on the capabilities of the requested cpu
+ (if any) and/or the instructions actually used. */
+ ARM_MERGE_FEATURE_SETS (flags, arm_arch_used, thumb_arch_used);
+ ARM_MERGE_FEATURE_SETS (flags, flags, *mfpu_opt);
+ ARM_MERGE_FEATURE_SETS (flags, flags, selected_cpu);
+
+ if (ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any))
+ ARM_MERGE_FEATURE_SETS (flags, flags, arm_ext_v1);
+
+ if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_any))
+ ARM_MERGE_FEATURE_SETS (flags, flags, arm_ext_v4t);
+
+ selected_cpu = flags;
+
+ /* Allow the user to override the reported architecture. */
+ if (object_arch)
+ {
+ ARM_CLEAR_FEATURE (flags, flags, arm_arch_any);
+ ARM_MERGE_FEATURE_SETS (flags, flags, *object_arch);
+ }
+
+ /* We need to make sure that the attributes do not identify us as v6S-M
+ when the only v6S-M feature in use is the Operating System Extensions. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_os))
+ if (!ARM_CPU_HAS_FEATURE (flags, arm_arch_v6m_only))
+ ARM_CLEAR_FEATURE (flags, flags, arm_ext_os);
+
+ tmp = flags;
+ arch = 0;
+ for (p = cpu_arch_ver; p->val; p++)
+ {
+ if (ARM_CPU_HAS_FEATURE (tmp, p->flags))
+ {
+ arch = p->val;
+ ARM_CLEAR_FEATURE (tmp, tmp, p->flags);
+ }
+ }
+
+ /* The table lookup above finds the last architecture to contribute
+ a new feature. Unfortunately, Tag13 is a subset of the union of
+ v6T2 and v7-M, so it is never seen as contributing a new feature.
+ We can not search for the last entry which is entirely used,
+ because if no CPU is specified we build up only those flags
+ actually used. Perhaps we should separate out the specified
+ and implicit cases. Avoid taking this path for -march=all by
+ checking for contradictory v7-A / v7-M features. */
+ if (arch == 10
+ && !ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a)
+ && ARM_CPU_HAS_FEATURE (flags, arm_ext_v7m)
+ && ARM_CPU_HAS_FEATURE (flags, arm_ext_v6_dsp))
+ arch = 13;
+
+ /* Tag_CPU_name. */
+ if (selected_cpu_name[0])
+ {
+ char *q;
+
+ q = selected_cpu_name;
+ if (strncmp (q, "armv", 4) == 0)
+ {
+ int i;
+
+ q += 4;
+ for (i = 0; q[i]; i++)
+ q[i] = TOUPPER (q[i]);
+ }
+ aeabi_set_attribute_string (Tag_CPU_name, q);
+ }
+
+ /* Tag_CPU_arch. */
+ aeabi_set_attribute_int (Tag_CPU_arch, arch);
+
+ /* Tag_CPU_arch_profile. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a))
+ profile = 'A';
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7r))
+ profile = 'R';
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_m))
+ profile = 'M';
+ else
+ profile = '\0';
+
+ if (profile != '\0')
+ aeabi_set_attribute_int (Tag_CPU_arch_profile, profile);
+
+ /* Tag_ARM_ISA_use. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v1)
+ || arch == 0)
+ aeabi_set_attribute_int (Tag_ARM_ISA_use, 1);
+
+ /* Tag_THUMB_ISA_use. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v4t)
+ || arch == 0)
+ aeabi_set_attribute_int (Tag_THUMB_ISA_use,
+ ARM_CPU_HAS_FEATURE (flags, arm_arch_t2) ? 2 : 1);
+
+ /* Tag_VFP_arch. */
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_armv8))
+ aeabi_set_attribute_int (Tag_VFP_arch, 7);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma))
+ aeabi_set_attribute_int (Tag_VFP_arch,
+ ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32)
+ ? 5 : 6);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32))
+ {
+ fp16_optional = 1;
+ aeabi_set_attribute_int (Tag_VFP_arch, 3);
+ }
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v3xd))
+ {
+ aeabi_set_attribute_int (Tag_VFP_arch, 4);
+ fp16_optional = 1;
+ }
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v2))
+ aeabi_set_attribute_int (Tag_VFP_arch, 2);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1)
+ || ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd))
+ aeabi_set_attribute_int (Tag_VFP_arch, 1);
+
+ /* Tag_ABI_HardFP_use. */
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd)
+ && !ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1))
+ aeabi_set_attribute_int (Tag_ABI_HardFP_use, 1);
+
+ /* Tag_WMMX_arch. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_cext_iwmmxt2))
+ aeabi_set_attribute_int (Tag_WMMX_arch, 2);
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_cext_iwmmxt))
+ aeabi_set_attribute_int (Tag_WMMX_arch, 1);
+
+ /* Tag_Advanced_SIMD_arch (formerly Tag_NEON_arch). */
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_armv8))
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 3);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_v1))
+ {
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma))
+ {
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 2);
+ }
+ else
+ {
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 1);
+ fp16_optional = 1;
+ }
+ }
+
+ /* Tag_VFP_HP_extension (formerly Tag_NEON_FP16_arch). */
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16) && fp16_optional)
+ aeabi_set_attribute_int (Tag_VFP_HP_extension, 1);
+
+ /* Tag_DIV_use.
+
+ We set Tag_DIV_use to two when integer divide instructions have been used
+ in ARM state, or when Thumb integer divide instructions have been used,
+ but we have no architecture profile set, nor have we any ARM instructions.
+
+ For ARMv8 we set the tag to 0 as integer divide is implied by the base
+ architecture.
+
+ For new architectures we will have to check these tests. */
+ gas_assert (arch <= TAG_CPU_ARCH_V8);
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v8))
+ aeabi_set_attribute_int (Tag_DIV_use, 0);
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv)
+ || (profile == '\0'
+ && ARM_CPU_HAS_FEATURE (flags, arm_ext_div)
+ && !ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any)))
+ aeabi_set_attribute_int (Tag_DIV_use, 2);
+
+ /* Tag_MP_extension_use. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_mp))
+ aeabi_set_attribute_int (Tag_MPextension_use, 1);
+
+ /* Tag Virtualization_use. */
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_sec))
+ virt_sec |= 1;
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_virt))
+ virt_sec |= 2;
+ if (virt_sec != 0)
+ aeabi_set_attribute_int (Tag_Virtualization_use, virt_sec);
+}
+
+/* Add the default contents for the .ARM.attributes section. */
+void
+arm_md_end (void)
+{
+ if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
+ return;
+
+ aeabi_set_public_attributes ();
+}
+#endif /* OBJ_ELF */
+
+
+/* Parse a .cpu directive. */
+
+static void
+s_arm_cpu (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct arm_cpu_option_table *opt;
+ char *name;
+ char saved_char;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ /* Skip the first "all" entry. */
+ for (opt = arm_cpus + 1; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ mcpu_cpu_opt = &opt->value;
+ selected_cpu = opt->value;
+ if (opt->canonical_name)
+ strcpy (selected_cpu_name, opt->canonical_name);
+ else
+ {
+ int i;
+ for (i = 0; opt->name[i]; i++)
+ selected_cpu_name[i] = TOUPPER (opt->name[i]);
+
+ selected_cpu_name[i] = 0;
+ }
+ ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ as_bad (_("unknown cpu `%s'"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+
+/* Parse a .arch directive. */
+
+static void
+s_arm_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct arm_arch_option_table *opt;
+ char saved_char;
+ char *name;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ /* Skip the first "all" entry. */
+ for (opt = arm_archs + 1; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ mcpu_cpu_opt = &opt->value;
+ selected_cpu = opt->value;
+ strcpy (selected_cpu_name, opt->name);
+ ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+
+/* Parse a .object_arch directive. */
+
+static void
+s_arm_object_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct arm_arch_option_table *opt;
+ char saved_char;
+ char *name;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ /* Skip the first "all" entry. */
+ for (opt = arm_archs + 1; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ object_arch = &opt->value;
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+/* Parse a .arch_extension directive. */
+
+static void
+s_arm_arch_extension (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct arm_option_extension_value_table *opt;
+ char saved_char;
+ char *name;
+ int adding_value = 1;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ if (strlen (name) >= 2
+ && strncmp (name, "no", 2) == 0)
+ {
+ adding_value = 0;
+ name += 2;
+ }
+
+ for (opt = arm_extensions; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ if (!ARM_CPU_HAS_FEATURE (*mcpu_cpu_opt, opt->allowed_archs))
+ {
+ as_bad (_("architectural extension `%s' is not allowed for the "
+ "current base architecture"), name);
+ break;
+ }
+
+ if (adding_value)
+ ARM_MERGE_FEATURE_SETS (selected_cpu, selected_cpu, opt->value);
+ else
+ ARM_CLEAR_FEATURE (selected_cpu, selected_cpu, opt->value);
+
+ mcpu_cpu_opt = &selected_cpu;
+ ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (opt->name == NULL)
+ as_bad (_("unknown architecture extension `%s'\n"), name);
+
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+/* Parse a .fpu directive. */
+
+static void
+s_arm_fpu (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct arm_option_fpu_value_table *opt;
+ char saved_char;
+ char *name;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ for (opt = arm_fpus; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ mfpu_opt = &opt->value;
+ ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown floating point format `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+/* Copy symbol information. */
+
+void
+arm_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ ARM_GET_FLAG (dest) = ARM_GET_FLAG (src);
+}
+
+#ifdef OBJ_ELF
+/* Given a symbolic attribute NAME, return the proper integer value.
+ Returns -1 if the attribute is not known. */
+
+int
+arm_convert_symbolic_attribute (const char *name)
+{
+ static const struct
+ {
+ const char * name;
+ const int tag;
+ }
+ attribute_table[] =
+ {
+ /* When you modify this table you should
+ also modify the list in doc/c-arm.texi. */
+#define T(tag) {#tag, tag}
+ T (Tag_CPU_raw_name),
+ T (Tag_CPU_name),
+ T (Tag_CPU_arch),
+ T (Tag_CPU_arch_profile),
+ T (Tag_ARM_ISA_use),
+ T (Tag_THUMB_ISA_use),
+ T (Tag_FP_arch),
+ T (Tag_VFP_arch),
+ T (Tag_WMMX_arch),
+ T (Tag_Advanced_SIMD_arch),
+ T (Tag_PCS_config),
+ T (Tag_ABI_PCS_R9_use),
+ T (Tag_ABI_PCS_RW_data),
+ T (Tag_ABI_PCS_RO_data),
+ T (Tag_ABI_PCS_GOT_use),
+ T (Tag_ABI_PCS_wchar_t),
+ T (Tag_ABI_FP_rounding),
+ T (Tag_ABI_FP_denormal),
+ T (Tag_ABI_FP_exceptions),
+ T (Tag_ABI_FP_user_exceptions),
+ T (Tag_ABI_FP_number_model),
+ T (Tag_ABI_align_needed),
+ T (Tag_ABI_align8_needed),
+ T (Tag_ABI_align_preserved),
+ T (Tag_ABI_align8_preserved),
+ T (Tag_ABI_enum_size),
+ T (Tag_ABI_HardFP_use),
+ T (Tag_ABI_VFP_args),
+ T (Tag_ABI_WMMX_args),
+ T (Tag_ABI_optimization_goals),
+ T (Tag_ABI_FP_optimization_goals),
+ T (Tag_compatibility),
+ T (Tag_CPU_unaligned_access),
+ T (Tag_FP_HP_extension),
+ T (Tag_VFP_HP_extension),
+ T (Tag_ABI_FP_16bit_format),
+ T (Tag_MPextension_use),
+ T (Tag_DIV_use),
+ T (Tag_nodefaults),
+ T (Tag_also_compatible_with),
+ T (Tag_conformance),
+ T (Tag_T2EE_use),
+ T (Tag_Virtualization_use),
+ /* We deliberately do not include Tag_MPextension_use_legacy. */
+#undef T
+ };
+ unsigned int i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+ if (streq (name, attribute_table[i].name))
+ return attribute_table[i].tag;
+
+ return -1;
+}
+
+
+/* Apply sym value for relocations only in the case that
+ they are for local symbols and you have the respective
+ architectural feature for blx and simple switches. */
+int
+arm_apply_sym_value (struct fix * fixP)
+{
+ if (fixP->fx_addsy
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)
+ && !S_FORCE_RELOC (fixP->fx_addsy, TRUE))
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_ARM_PCREL_BLX:
+ case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ if (ARM_IS_FUNC (fixP->fx_addsy))
+ return 1;
+ break;
+
+ case BFD_RELOC_ARM_PCREL_CALL:
+ case BFD_RELOC_THUMB_PCREL_BLX:
+ if (THUMB_IS_FUNC (fixP->fx_addsy))
+ return 1;
+ break;
+
+ default:
+ break;
+ }
+
+ }
+ return 0;
+}
+#endif /* OBJ_ELF */
diff --git a/gas/config/tc-arm.h b/gas/config/tc-arm.h
new file mode 100644
index 0000000..a7a0cd0
--- /dev/null
+++ b/gas/config/tc-arm.h
@@ -0,0 +1,379 @@
+/* This file is tc-arm.h
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modified by David Taylor (dtaylor@armltd.co.uk)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_ARM 1
+
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 0
+#endif
+
+#define WORKING_DOT_WORD
+
+#define COFF_MAGIC ARMMAGIC
+#define TARGET_ARCH bfd_arch_arm
+
+#define DIFF_EXPR_OK
+
+#ifdef LITTLE_ENDIAN
+#undef LITTLE_ENDIAN
+#endif
+
+#ifdef BIG_ENDIAN
+#undef BIG_ENDIAN
+#endif
+
+#define LITTLE_ENDIAN 1234
+#define BIG_ENDIAN 4321
+
+struct fix;
+
+#if defined OBJ_AOUT
+# if defined TE_RISCIX
+# define TARGET_FORMAT "a.out-riscix"
+# elif defined TE_LINUX
+# define ARM_BI_ENDIAN
+# define TARGET_FORMAT "a.out-arm-linux"
+# elif defined TE_NetBSD
+# define TARGET_FORMAT "a.out-arm-netbsd"
+# else
+# define ARM_BI_ENDIAN
+# define TARGET_FORMAT (target_big_endian ? "a.out-arm-big" : "a.out-arm-little")
+# endif
+#elif defined OBJ_AIF
+# define TARGET_FORMAT "aif"
+#elif defined OBJ_COFF
+# define ARM_BI_ENDIAN
+# if defined TE_PE
+# if defined TE_EPOC
+# define TARGET_FORMAT (target_big_endian ? "epoc-pe-arm-big" : "epoc-pe-arm-little")
+# elif defined TE_WINCE
+# define TARGET_FORMAT (target_big_endian ? "pe-arm-wince-big" : "pe-arm-wince-little")
+# else
+# define TARGET_FORMAT (target_big_endian ? "pe-arm-big" : "pe-arm-little")
+# endif
+# else
+# define TARGET_FORMAT (target_big_endian ? "coff-arm-big" : "coff-arm-little")
+# endif
+#elif defined OBJ_ELF
+# define ARM_BI_ENDIAN
+# define TARGET_FORMAT elf32_arm_target_format ()
+#endif
+
+/* We support double slash line-comments for compatibility with the ARM AArch64 Assembler. */
+#define DOUBLESLASH_LINE_COMMENTS
+
+/* We conditionally support labels without a colon. */
+#define LABELS_WITHOUT_COLONS codecomposer_syntax
+extern bfd_boolean codecomposer_syntax;
+
+#define tc_symbol_chars arm_symbol_chars
+extern const char arm_symbol_chars[];
+
+#define TC_FORCE_RELOCATION(FIX) arm_force_relocation (FIX)
+
+extern unsigned int arm_frag_max_var (struct frag *);
+#define md_frag_max_var arm_frag_max_var
+
+#define md_relax_frag(segment, fragp, stretch) \
+ arm_relax_frag (segment, fragp, stretch)
+extern int arm_relax_frag (asection *, struct frag *, long);
+
+#define md_optimize_expr(l,o,r) arm_optimize_expr (l, o, r)
+extern int arm_optimize_expr (expressionS *, operatorT, expressionS *);
+
+#define md_cleanup() arm_cleanup ()
+
+#define md_start_line_hook() arm_start_line_hook ()
+
+#define TC_START_LABEL_WITHOUT_COLON(c, l) tc_start_label_without_colon (c, l)
+extern bfd_boolean tc_start_label_without_colon (char, const char *);
+
+#define tc_frob_label(S) arm_frob_label (S)
+
+/* We also need to mark assembler created symbols: */
+#define tc_frob_fake_label(S) arm_frob_label (S)
+
+#ifdef OBJ_ELF
+#define md_end arm_md_end
+extern void arm_md_end (void);
+bfd_boolean arm_is_eabi (void);
+#endif
+
+/* NOTE: The fake label creation in stabs.c:s_stab_generic() has
+ deliberately not been updated to mark assembler created stabs
+ symbols as Thumb. */
+
+#define TC_FIX_TYPE int
+#define TC_INIT_FIX_DATA(FIX) ((FIX)->tc_fix_data = 0)
+
+/* We need to keep some local information on symbols. */
+
+#define TC_SYMFIELD_TYPE unsigned int
+#define ARM_GET_FLAG(s) (*symbol_get_tc (s))
+#define ARM_SET_FLAG(s,v) (*symbol_get_tc (s) |= (v))
+#define ARM_RESET_FLAG(s,v) (*symbol_get_tc (s) &= ~(v))
+
+#define ARM_FLAG_THUMB (1 << 0) /* The symbol is a Thumb symbol rather than an Arm symbol. */
+#define ARM_FLAG_INTERWORK (1 << 1) /* The symbol is attached to code that supports interworking. */
+#define THUMB_FLAG_FUNC (1 << 2) /* The symbol is attached to the start of a Thumb function. */
+
+#define ARM_IS_THUMB(s) (ARM_GET_FLAG (s) & ARM_FLAG_THUMB)
+#define ARM_IS_INTERWORK(s) (ARM_GET_FLAG (s) & ARM_FLAG_INTERWORK)
+
+#ifdef OBJ_ELF
+
+/* For ELF objects THUMB_IS_FUNC is inferred from
+ ARM_IS_THUMB and the function type. */
+#define THUMB_IS_FUNC(s) \
+ ((s) != NULL \
+ && ((arm_is_eabi () \
+ && (ARM_IS_THUMB (s)) \
+ && (symbol_get_bfdsym (s)->flags & BSF_FUNCTION)) \
+ || (ARM_GET_FLAG (s) & THUMB_FLAG_FUNC)))
+
+#define ARM_IS_FUNC(s) \
+ (((s) != NULL \
+ && arm_is_eabi () \
+ && !(ARM_IS_THUMB (s)) \
+ /* && !(THUMB_FLAG_FUNC & ARM_GET_FLAG (s)) \ */ \
+ && (symbol_get_bfdsym (s)->flags & BSF_FUNCTION)))
+
+
+#else
+
+#define THUMB_IS_FUNC(s) ((s) && ARM_GET_FLAG (s) & THUMB_FLAG_FUNC)
+#define ARM_IS_FUNC(s) (!THUMB_IS_FUNC (s) \
+ && (s) && (symbol_get_bfdsym (s)->flags & BSF_FUNCTION))
+#endif
+
+#define ARM_SET_THUMB(s,t) ((t) ? ARM_SET_FLAG (s, ARM_FLAG_THUMB) : ARM_RESET_FLAG (s, ARM_FLAG_THUMB))
+#define ARM_SET_INTERWORK(s,t) ((t) ? ARM_SET_FLAG (s, ARM_FLAG_INTERWORK) : ARM_RESET_FLAG (s, ARM_FLAG_INTERWORK))
+#define THUMB_SET_FUNC(s,t) ((t) ? ARM_SET_FLAG (s, THUMB_FLAG_FUNC) : ARM_RESET_FLAG (s, THUMB_FLAG_FUNC))
+
+void arm_copy_symbol_attributes (symbolS *, symbolS *);
+
+#ifndef TC_COPY_SYMBOL_ATTRIBUTES
+#define TC_COPY_SYMBOL_ATTRIBUTES(DEST, SRC) \
+ (arm_copy_symbol_attributes (DEST, SRC))
+#endif
+
+#define TC_START_LABEL(C,S,STR) (C == ':' || (C == '/' && arm_data_in_code ()))
+#define tc_canonicalize_symbol_name(str) arm_canonicalize_symbol_name (str);
+#define obj_adjust_symtab() arm_adjust_symtab ()
+
+#define LISTING_HEADER "ARM GAS "
+
+#define OPTIONAL_REGISTER_PREFIX '%'
+
+#define LOCAL_LABEL(name) (name[0] == '.' && name[1] == 'L')
+#define LOCAL_LABELS_FB 1
+
+/* This expression evaluates to true if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ False if we are willing to perform this relocation while building
+ the .o file. GOTOFF does not need to be checked here because it is
+ not pcrel. I am not sure if some of the others are ever used with
+ pcrel, but it is easier to be safe than sorry. */
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_ARM_GOT32 \
+ || (FIX)->fx_r_type == BFD_RELOC_32 \
+ || ((FIX)->fx_addsy != NULL && S_IS_WEAK ((FIX)->fx_addsy)) \
+ || TC_FORCE_RELOCATION (FIX))
+
+/* Force output of R_ARM_REL32 relocations against thumb function symbols.
+ This is needed to ensure the low bit is handled correctly. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+ (THUMB_IS_FUNC ((FIX)->fx_addsy) \
+ || !SEG_NORMAL (SEG))
+
+#define TC_FORCE_RELOCATION_ABS(FIX) \
+ (((FIX)->fx_pcrel \
+ && (FIX)->fx_r_type != BFD_RELOC_32 \
+ && (FIX)->fx_r_type != BFD_RELOC_ARM_GOT32) \
+ || TC_FORCE_RELOCATION(FIX))
+
+#define TC_CONS_FIX_NEW cons_fix_new_arm
+
+#define MAX_MEM_ALIGNMENT_BYTES 6
+#define MAX_MEM_FOR_RS_ALIGN_CODE ((1 << MAX_MEM_ALIGNMENT_BYTES) - 1)
+
+/* For frags in code sections we need to record whether they contain
+ ARM code or THUMB code. This is that if they have to be aligned,
+ they can contain the correct type of no-op instruction. */
+struct arm_frag_type
+{
+ int thumb_mode;
+#ifdef OBJ_ELF
+ /* If there is a mapping symbol at offset 0 in this frag,
+ it will be saved in FIRST_MAP. If there are any mapping
+ symbols in this frag, the last one will be saved in
+ LAST_MAP. */
+ symbolS *first_map, *last_map;
+#endif
+};
+
+#define TC_FRAG_TYPE struct arm_frag_type
+/* NOTE: max_chars is a local variable from frag_var / frag_variant. */
+#define TC_FRAG_INIT(fragp) arm_init_frag (fragp, max_chars)
+#define HANDLE_ALIGN(fragp) arm_handle_align (fragp)
+
+#define md_do_align(N, FILL, LEN, MAX, LABEL) \
+ if (FILL == NULL && (N) != 0 && ! need_pass_2 && subseg_text_p (now_seg)) \
+ { \
+ arm_frag_align_code (N, MAX); \
+ goto LABEL; \
+ }
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* The lr register is r14. */
+#define DWARF2_DEFAULT_RETURN_COLUMN 14
+
+/* Registers are generally saved at negative offsets to the CFA. */
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+/* State variables for IT block handling. */
+enum it_state
+{
+ OUTSIDE_IT_BLOCK, MANUAL_IT_BLOCK, AUTOMATIC_IT_BLOCK
+};
+struct current_it
+{
+ int mask;
+ enum it_state state;
+ int cc;
+ int block_length;
+ char *insn;
+ int state_handled;
+ int warn_deprecated;
+ int insn_cond;
+};
+
+#ifdef OBJ_ELF
+# define obj_frob_symbol(sym, punt) armelf_frob_symbol ((sym), & (punt))
+# define md_elf_section_change_hook() arm_elf_change_section ()
+# define md_elf_section_type(str, len) arm_elf_section_type (str, len)
+# define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+# define TC_SEGMENT_INFO_TYPE struct arm_segment_info_type
+
+/* This is not really an alignment operation, but it's something we
+ need to do at the same time: whenever we are figuring out the
+ alignment for data, we should check whether a $d symbol is
+ necessary. */
+# define md_cons_align(nbytes) mapping_state (MAP_DATA)
+
+enum mstate
+{
+ MAP_UNDEFINED = 0, /* Must be zero, for seginfo in new sections. */
+ MAP_DATA,
+ MAP_ARM,
+ MAP_THUMB
+};
+
+void mapping_state (enum mstate);
+
+struct arm_segment_info_type
+{
+ enum mstate mapstate;
+
+ /* Bit N indicates that an R_ARM_NONE relocation has been output for
+ __aeabi_unwind_cpp_prN already if set. This enables dependencies to be
+ emitted only once per section, to save unnecessary bloat. */
+ unsigned int marked_pr_dependency;
+
+ struct current_it current_it;
+};
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+/* CFI hooks. */
+#define tc_regname_to_dw2regnum tc_arm_regname_to_dw2regnum
+#define tc_cfi_frame_initial_instructions tc_arm_frame_initial_instructions
+
+#else /* Not OBJ_ELF. */
+#define GLOBAL_OFFSET_TABLE_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+
+#if defined OBJ_ELF || defined OBJ_COFF
+
+# define EXTERN_FORCE_RELOC 1
+# define tc_fix_adjustable(FIX) arm_fix_adjustable (FIX)
+#endif
+
+#ifdef OBJ_ELF
+/* Values passed to md_apply_fix don't include the symbol value. */
+# define MD_APPLY_SYM_VALUE(FIX) arm_apply_sym_value (FIX)
+#endif
+
+#ifdef OBJ_COFF
+# define TC_VALIDATE_FIX(FIX, SEGTYPE, LABEL) arm_validate_fix (FIX)
+/* Values passed to md_apply_fix don't include the symbol value. */
+# define MD_APPLY_SYM_VALUE(FIX) 0
+#endif
+
+#define MD_PCREL_FROM_SECTION(F,S) md_pcrel_from_section(F,S)
+
+extern long md_pcrel_from_section (struct fix *, segT);
+extern void arm_frag_align_code (int, int);
+extern void arm_validate_fix (struct fix *);
+extern const char * elf32_arm_target_format (void);
+extern void arm_elf_change_section (void);
+extern int arm_force_relocation (struct fix *);
+extern void arm_cleanup (void);
+extern void arm_start_line_hook (void);
+extern void arm_frob_label (symbolS *);
+extern int arm_data_in_code (void);
+extern char * arm_canonicalize_symbol_name (char *);
+extern void arm_adjust_symtab (void);
+extern void armelf_frob_symbol (symbolS *, int *);
+extern void cons_fix_new_arm (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+extern void arm_init_frag (struct frag *, int);
+extern void arm_handle_align (struct frag *);
+extern bfd_boolean arm_fix_adjustable (struct fix *);
+extern int arm_elf_section_type (const char *, size_t);
+extern int tc_arm_regname_to_dw2regnum (char *regname);
+extern void tc_arm_frame_initial_instructions (void);
+
+#ifdef TE_PE
+
+#define O_secrel O_md1
+
+#define TC_DWARF2_EMIT_OFFSET tc_pe_dwarf2_emit_offset
+void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int);
+
+#endif /* TE_PE */
+
+#ifdef OBJ_ELF
+#define CONVERT_SYMBOLIC_ATTRIBUTE(name) arm_convert_symbolic_attribute (name)
+extern int arm_convert_symbolic_attribute (const char *);
+extern int arm_apply_sym_value (struct fix *);
+#endif
+
+#define tc_comment_chars arm_comment_chars
+extern char arm_comment_chars[];
+
+#define tc_line_separator_chars arm_line_separator_chars
+extern char arm_line_separator_chars[];
diff --git a/gas/config/tc-avr.c b/gas/config/tc-avr.c
new file mode 100644
index 0000000..dfe66c6
--- /dev/null
+++ b/gas/config/tc-avr.c
@@ -0,0 +1,1850 @@
+/* tc-avr.c -- Assembler code for the ATMEL AVR
+
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Contributed by Denis Chertykov <denisc@overta.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 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+
+struct avr_opcodes_s
+{
+ char * name;
+ char * constraints;
+ char * opcode;
+ int insn_size; /* In words. */
+ int isa;
+ unsigned int bin_opcode;
+};
+
+#define AVR_INSN(NAME, CONSTR, OPCODE, SIZE, ISA, BIN) \
+{#NAME, CONSTR, OPCODE, SIZE, ISA, BIN},
+
+struct avr_opcodes_s avr_opcodes[] =
+{
+ #include "opcode/avr.h"
+ {NULL, NULL, NULL, 0, 0, 0}
+};
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "$";
+
+const char *md_shortopts = "m:";
+struct mcu_type_s
+{
+ char *name;
+ int isa;
+ int mach;
+};
+
+/* XXX - devices that don't seem to exist (renamed, replaced with larger
+ ones, or planned but never produced), left here for compatibility. */
+
+static struct mcu_type_s mcu_types[] =
+{
+ {"avr1", AVR_ISA_AVR1, bfd_mach_avr1},
+/* TODO: insruction set for avr2 architecture should be AVR_ISA_AVR2,
+ but set to AVR_ISA_AVR25 for some following version
+ of GCC (from 4.3) for backward compatibility. */
+ {"avr2", AVR_ISA_AVR25, bfd_mach_avr2},
+ {"avr25", AVR_ISA_AVR25, bfd_mach_avr25},
+/* TODO: insruction set for avr3 architecture should be AVR_ISA_AVR3,
+ but set to AVR_ISA_AVR3_ALL for some following version
+ of GCC (from 4.3) for backward compatibility. */
+ {"avr3", AVR_ISA_AVR3_ALL, bfd_mach_avr3},
+ {"avr31", AVR_ISA_AVR31, bfd_mach_avr31},
+ {"avr35", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"avr4", AVR_ISA_AVR4, bfd_mach_avr4},
+/* TODO: insruction set for avr5 architecture should be AVR_ISA_AVR5,
+ but set to AVR_ISA_AVR51 for some following version
+ of GCC (from 4.3) for backward compatibility. */
+ {"avr5", AVR_ISA_AVR51, bfd_mach_avr5},
+ {"avr51", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"avr6", AVR_ISA_AVR6, bfd_mach_avr6},
+ {"avrxmega1", AVR_ISA_XMEGA, bfd_mach_avrxmega1},
+ {"avrxmega2", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"avrxmega3", AVR_ISA_XMEGA, bfd_mach_avrxmega3},
+ {"avrxmega4", AVR_ISA_XMEGA, bfd_mach_avrxmega4},
+ {"avrxmega5", AVR_ISA_XMEGA, bfd_mach_avrxmega5},
+ {"avrxmega6", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"avrxmega7", AVR_ISA_XMEGA, bfd_mach_avrxmega7},
+ {"avrtiny", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {"at90s1200", AVR_ISA_1200, bfd_mach_avr1},
+ {"attiny11", AVR_ISA_AVR1, bfd_mach_avr1},
+ {"attiny12", AVR_ISA_AVR1, bfd_mach_avr1},
+ {"attiny15", AVR_ISA_AVR1, bfd_mach_avr1},
+ {"attiny28", AVR_ISA_AVR1, bfd_mach_avr1},
+ {"at90s2313", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"at90s2323", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"at90s2333", AVR_ISA_AVR2, bfd_mach_avr2}, /* XXX -> 4433 */
+ {"at90s2343", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"attiny22", AVR_ISA_AVR2, bfd_mach_avr2}, /* XXX -> 2343 */
+ {"attiny26", AVR_ISA_2xxe, bfd_mach_avr2},
+ {"at90s4414", AVR_ISA_AVR2, bfd_mach_avr2}, /* XXX -> 8515 */
+ {"at90s4433", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"at90s4434", AVR_ISA_AVR2, bfd_mach_avr2}, /* XXX -> 8535 */
+ {"at90s8515", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"at90c8534", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"at90s8535", AVR_ISA_AVR2, bfd_mach_avr2},
+ {"ata5272", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny13", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny13a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny2313", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny2313a",AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny24", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny24a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny4313", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny44", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny44a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny84", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny84a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny25", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny45", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny85", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny261", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny261a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny461", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny461a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny861", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny861a", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny87", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny43u", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny48", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny88", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"attiny828", AVR_ISA_AVR25, bfd_mach_avr25},
+ {"at86rf401", AVR_ISA_RF401, bfd_mach_avr25},
+ {"at43usb355", AVR_ISA_AVR3, bfd_mach_avr3},
+ {"at76c711", AVR_ISA_AVR3, bfd_mach_avr3},
+ {"atmega103", AVR_ISA_AVR31, bfd_mach_avr31},
+ {"at43usb320", AVR_ISA_AVR31, bfd_mach_avr31},
+ {"attiny167", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"at90usb82", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"at90usb162", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"ata5505", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"atmega8u2", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"atmega16u2", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"atmega32u2", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"attiny1634", AVR_ISA_AVR35, bfd_mach_avr35},
+ {"atmega8", AVR_ISA_M8, bfd_mach_avr4},
+ {"ata6289", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega8a", AVR_ISA_M8, bfd_mach_avr4},
+ {"ata6285", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"ata6286", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega48", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega48a", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega48pa", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega48p", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega88", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega88a", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega88p", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega88pa", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"atmega8515", AVR_ISA_M8, bfd_mach_avr4},
+ {"atmega8535", AVR_ISA_M8, bfd_mach_avr4},
+ {"atmega8hva", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm1", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm2", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm2b", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm3", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm3b", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm81", AVR_ISA_AVR4, bfd_mach_avr4},
+ {"at90pwm161", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"ata5790", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"ata5795", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega161", AVR_ISA_M161, bfd_mach_avr5},
+ {"atmega162", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega163", AVR_ISA_M161, bfd_mach_avr5},
+ {"atmega164a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega164p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega164pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega165", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega165a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega165p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega165pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega168", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega168a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega168p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega168pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega169", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega169a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega169p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega169pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega323", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega324a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega324p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega324pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega325", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega325a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega325p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega325pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3250", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3250a",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3250p",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3250pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega328", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega328p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega329", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega329a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega329p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega329pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3290", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3290a",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3290p",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega3290pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega406", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega64rfr2", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega644rfr2",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega64", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega64a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega640", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega644", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega644a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega644p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega644pa",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega645", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega645a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega645p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega649", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega649a", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega649p", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega6450", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega6450a",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega6450p",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega6490", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega6490a",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega6490p",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega64rfr2",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega644rfr2",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16hva",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16hva2",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16hvb",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16hvbrevb",AVR_ISA_AVR5,bfd_mach_avr5},
+ {"atmega32hvb",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32hvbrevb",AVR_ISA_AVR5,bfd_mach_avr5},
+ {"atmega64hve",AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90can32" , AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90can64" , AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90pwm161", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90pwm216", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90pwm316", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32c1", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega64c1", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16m1", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32m1", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega64m1", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega16u4", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32u4", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega32u6", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90usb646", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90usb647", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at90scr100", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"at94k", AVR_ISA_94K, bfd_mach_avr5},
+ {"m3000", AVR_ISA_AVR5, bfd_mach_avr5},
+ {"atmega128", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega128a", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega1280", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega1281", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega1284", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega1284p",AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega128rfa1",AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega128rfr2",AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega1284rfr2",AVR_ISA_AVR51, bfd_mach_avr51},
+ {"at90can128", AVR_ISA_AVR51, bfd_mach_avr51},
+ {"at90usb1286",AVR_ISA_AVR51, bfd_mach_avr51},
+ {"at90usb1287",AVR_ISA_AVR51, bfd_mach_avr51},
+ {"atmega2560", AVR_ISA_AVR6, bfd_mach_avr6},
+ {"atmega2561", AVR_ISA_AVR6, bfd_mach_avr6},
+ {"atmega256rfr2", AVR_ISA_AVR6, bfd_mach_avr6},
+ {"atmega2564rfr2", AVR_ISA_AVR6, bfd_mach_avr6},
+ {"atxmega16a4", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega16a4u",AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+ {"atxmega16c4", AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+ {"atxmega16d4", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega32a4", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega32a4u",AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+ {"atxmega32c4", AVR_ISA_XMEGAU, bfd_mach_avrxmega2},
+ {"atxmega32d4", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega32e5", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega16e5", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega8e5", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega32x1", AVR_ISA_XMEGA, bfd_mach_avrxmega2},
+ {"atxmega64a3", AVR_ISA_XMEGA, bfd_mach_avrxmega4},
+ {"atxmega64a3u",AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+ {"atxmega64a4u",AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+ {"atxmega64b1", AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+ {"atxmega64b3", AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+ {"atxmega64c3", AVR_ISA_XMEGAU, bfd_mach_avrxmega4},
+ {"atxmega64d3", AVR_ISA_XMEGA, bfd_mach_avrxmega4},
+ {"atxmega64d4", AVR_ISA_XMEGA, bfd_mach_avrxmega4},
+ {"atxmega64a1", AVR_ISA_XMEGA, bfd_mach_avrxmega5},
+ {"atxmega64a1u",AVR_ISA_XMEGAU, bfd_mach_avrxmega5},
+ {"atxmega128a3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega128a3u",AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega128b1", AVR_ISA_XMEGAU, bfd_mach_avrxmega6},
+ {"atxmega128b3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega128c3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega128d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega128d4", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega192a3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega192a3u",AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega192c3", AVR_ISA_XMEGAU, bfd_mach_avrxmega6},
+ {"atxmega192d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega256a3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega256a3u",AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega256a3b",AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega256a3bu",AVR_ISA_XMEGAU, bfd_mach_avrxmega6},
+ {"atxmega256c3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega256d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega384c3", AVR_ISA_XMEGAU,bfd_mach_avrxmega6},
+ {"atxmega384d3", AVR_ISA_XMEGA, bfd_mach_avrxmega6},
+ {"atxmega128a1", AVR_ISA_XMEGA, bfd_mach_avrxmega7},
+ {"atxmega128a1u", AVR_ISA_XMEGAU, bfd_mach_avrxmega7},
+ {"atxmega128a4u", AVR_ISA_XMEGAU, bfd_mach_avrxmega7},
+ {"attiny4", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {"attiny5", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {"attiny9", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {"attiny10", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {"attiny20", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {"attiny40", AVR_ISA_AVRTINY, bfd_mach_avrtiny},
+ {NULL, 0, 0}
+};
+
+
+/* Current MCU type. */
+static struct mcu_type_s default_mcu = {"avr2", AVR_ISA_AVR2, bfd_mach_avr2};
+static struct mcu_type_s specified_mcu;
+static struct mcu_type_s * avr_mcu = & default_mcu;
+
+/* AVR target-specific switches. */
+struct avr_opt_s
+{
+ int all_opcodes; /* -mall-opcodes: accept all known AVR opcodes. */
+ int no_skip_bug; /* -mno-skip-bug: no warnings for skipping 2-word insns. */
+ int no_wrap; /* -mno-wrap: reject rjmp/rcall with 8K wrap-around. */
+ int link_relax; /* -mlink-relax: generate relocations for linker
+ relaxation. */
+};
+
+static struct avr_opt_s avr_opt = { 0, 0, 0, 0 };
+
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+static void avr_set_arch (int);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"arch", avr_set_arch, 0},
+ { NULL, NULL, 0}
+};
+
+#define LDI_IMMEDIATE(x) (((x) & 0xf) | (((x) << 4) & 0xf00))
+
+#define EXP_MOD_NAME(i) exp_mod[i].name
+#define EXP_MOD_RELOC(i) exp_mod[i].reloc
+#define EXP_MOD_NEG_RELOC(i) exp_mod[i].neg_reloc
+#define HAVE_PM_P(i) exp_mod[i].have_pm
+
+struct exp_mod_s
+{
+ char * name;
+ bfd_reloc_code_real_type reloc;
+ bfd_reloc_code_real_type neg_reloc;
+ int have_pm;
+};
+
+static struct exp_mod_s exp_mod[] =
+{
+ {"hh8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 1},
+ {"pm_hh8", BFD_RELOC_AVR_HH8_LDI_PM, BFD_RELOC_AVR_HH8_LDI_PM_NEG, 0},
+ {"hi8", BFD_RELOC_AVR_HI8_LDI, BFD_RELOC_AVR_HI8_LDI_NEG, 1},
+ {"pm_hi8", BFD_RELOC_AVR_HI8_LDI_PM, BFD_RELOC_AVR_HI8_LDI_PM_NEG, 0},
+ {"lo8", BFD_RELOC_AVR_LO8_LDI, BFD_RELOC_AVR_LO8_LDI_NEG, 1},
+ {"pm_lo8", BFD_RELOC_AVR_LO8_LDI_PM, BFD_RELOC_AVR_LO8_LDI_PM_NEG, 0},
+ {"hlo8", BFD_RELOC_AVR_HH8_LDI, BFD_RELOC_AVR_HH8_LDI_NEG, 0},
+ {"hhi8", BFD_RELOC_AVR_MS8_LDI, BFD_RELOC_AVR_MS8_LDI_NEG, 0},
+};
+
+/* A union used to store indicies into the exp_mod[] array
+ in a hash table which expects void * data types. */
+typedef union
+{
+ void * ptr;
+ int index;
+} mod_index;
+
+/* Opcode hash table. */
+static struct hash_control *avr_hash;
+
+/* Reloc modifiers hash control (hh8,hi8,lo8,pm_xx). */
+static struct hash_control *avr_mod_hash;
+
+#define OPTION_MMCU 'm'
+enum options
+{
+ OPTION_ALL_OPCODES = OPTION_MD_BASE + 1,
+ OPTION_NO_SKIP_BUG,
+ OPTION_NO_WRAP,
+ OPTION_ISA_RMW,
+ OPTION_LINK_RELAX
+};
+
+struct option md_longopts[] =
+{
+ { "mmcu", required_argument, NULL, OPTION_MMCU },
+ { "mall-opcodes", no_argument, NULL, OPTION_ALL_OPCODES },
+ { "mno-skip-bug", no_argument, NULL, OPTION_NO_SKIP_BUG },
+ { "mno-wrap", no_argument, NULL, OPTION_NO_WRAP },
+ { "mrmw", no_argument, NULL, OPTION_ISA_RMW },
+ { "mlink-relax", no_argument, NULL, OPTION_LINK_RELAX },
+ { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Display nicely formatted list of known MCU names. */
+
+static void
+show_mcu_list (FILE *stream)
+{
+ int i, x;
+
+ fprintf (stream, _("Known MCU names:"));
+ x = 1000;
+
+ for (i = 0; mcu_types[i].name; i++)
+ {
+ int len = strlen (mcu_types[i].name);
+
+ x += len + 1;
+
+ if (x < 75)
+ fprintf (stream, " %s", mcu_types[i].name);
+ else
+ {
+ fprintf (stream, "\n %s", mcu_types[i].name);
+ x = len + 2;
+ }
+ }
+
+ fprintf (stream, "\n");
+}
+
+static inline char *
+skip_space (char *s)
+{
+ while (*s == ' ' || *s == '\t')
+ ++s;
+ return s;
+}
+
+/* Extract one word from FROM and copy it to TO. */
+
+static char *
+extract_word (char *from, char *to, int limit)
+{
+ char *op_end;
+ int size = 0;
+
+ /* Drop leading whitespace. */
+ from = skip_space (from);
+ *to = 0;
+
+ /* Find the op code end. */
+ for (op_end = from; *op_end != 0 && is_part_of_name (*op_end);)
+ {
+ to[size++] = *op_end++;
+ if (size + 1 >= limit)
+ break;
+ }
+
+ to[size] = 0;
+ return op_end;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream,
+ _("AVR Assembler options:\n"
+ " -mmcu=[avr-name] select microcontroller variant\n"
+ " [avr-name] can be:\n"
+ " avr1 - classic AVR core without data RAM\n"
+ " avr2 - classic AVR core with up to 8K program memory\n"
+ " avr25 - classic AVR core with up to 8K program memory\n"
+ " plus the MOVW instruction\n"
+ " avr3 - classic AVR core with up to 64K program memory\n"
+ " avr31 - classic AVR core with up to 128K program memory\n"
+ " avr35 - classic AVR core with up to 64K program memory\n"
+ " plus the MOVW instruction\n"
+ " avr4 - enhanced AVR core with up to 8K program memory\n"
+ " avr5 - enhanced AVR core with up to 64K program memory\n"
+ " avr51 - enhanced AVR core with up to 128K program memory\n"
+ " avr6 - enhanced AVR core with up to 256K program memory\n"
+ " avrxmega2 - XMEGA, > 8K, < 64K FLASH, < 64K RAM\n"
+ " avrxmega3 - XMEGA, > 8K, <= 64K FLASH, > 64K RAM\n"
+ " avrxmega4 - XMEGA, > 64K, <= 128K FLASH, <= 64K RAM\n"
+ " avrxmega5 - XMEGA, > 64K, <= 128K FLASH, > 64K RAM\n"
+ " avrxmega6 - XMEGA, > 128K, <= 256K FLASH, <= 64K RAM\n"
+ " avrxmega7 - XMEGA, > 128K, <= 256K FLASH, > 64K RAM\n"
+ " avrtiny - AVR Tiny core with 16 gp registers\n"));
+ fprintf (stream,
+ _(" -mall-opcodes accept all AVR opcodes, even if not supported by MCU\n"
+ " -mno-skip-bug disable warnings for skipping two-word instructions\n"
+ " (default for avr4, avr5)\n"
+ " -mno-wrap reject rjmp/rcall instructions with 8K wrap-around\n"
+ " (default for avr3, avr5)\n"
+ " -mrmw accept Read-Modify-Write instructions\n"
+ " -mlink-relax generate relocations for linker relaxation\n"
+ ));
+ show_mcu_list (stream);
+}
+
+static void
+avr_set_arch (int dummy ATTRIBUTE_UNUSED)
+{
+ char str[20];
+
+ input_line_pointer = extract_word (input_line_pointer, str, 20);
+ md_parse_option (OPTION_MMCU, str);
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_MMCU:
+ {
+ int i;
+ char *s = alloca (strlen (arg) + 1);
+
+ {
+ char *t = s;
+ char *arg1 = arg;
+
+ do
+ *t = TOLOWER (*arg1++);
+ while (*t++);
+ }
+
+ for (i = 0; mcu_types[i].name; ++i)
+ if (strcmp (mcu_types[i].name, s) == 0)
+ break;
+
+ if (!mcu_types[i].name)
+ {
+ show_mcu_list (stderr);
+ as_fatal (_("unknown MCU: %s\n"), arg);
+ }
+
+ /* It is OK to redefine mcu type within the same avr[1-5] bfd machine
+ type - this for allows passing -mmcu=... via gcc ASM_SPEC as well
+ as .arch ... in the asm output at the same time. */
+ if (avr_mcu == &default_mcu || avr_mcu->mach == mcu_types[i].mach)
+ {
+ specified_mcu.name = mcu_types[i].name;
+ specified_mcu.isa |= mcu_types[i].isa;
+ specified_mcu.mach = mcu_types[i].mach;
+ avr_mcu = &specified_mcu;
+ }
+ else
+ as_fatal (_("redefinition of mcu type `%s' to `%s'"),
+ avr_mcu->name, mcu_types[i].name);
+ return 1;
+ }
+ case OPTION_ALL_OPCODES:
+ avr_opt.all_opcodes = 1;
+ return 1;
+ case OPTION_NO_SKIP_BUG:
+ avr_opt.no_skip_bug = 1;
+ return 1;
+ case OPTION_NO_WRAP:
+ avr_opt.no_wrap = 1;
+ return 1;
+ case OPTION_ISA_RMW:
+ specified_mcu.isa |= AVR_ISA_RMW;
+ return 1;
+ case OPTION_LINK_RELAX:
+ avr_opt.link_relax = 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+void
+md_begin (void)
+{
+ unsigned int i;
+ struct avr_opcodes_s *opcode;
+
+ avr_hash = hash_new ();
+
+ /* Insert unique names into hash table. This hash table then provides a
+ quick index to the first opcode with a particular name in the opcode
+ table. */
+ for (opcode = avr_opcodes; opcode->name; opcode++)
+ hash_insert (avr_hash, opcode->name, (char *) opcode);
+
+ avr_mod_hash = hash_new ();
+
+ for (i = 0; i < ARRAY_SIZE (exp_mod); ++i)
+ {
+ mod_index m;
+
+ m.index = i + 10;
+ hash_insert (avr_mod_hash, EXP_MOD_NAME (i), m.ptr);
+ }
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, avr_mcu->mach);
+ linkrelax = avr_opt.link_relax;
+}
+
+/* Resolve STR as a constant expression and return the result.
+ If result greater than MAX then error. */
+
+static unsigned int
+avr_get_constant (char *str, int max)
+{
+ expressionS ex;
+
+ str = skip_space (str);
+ input_line_pointer = str;
+ expression (& ex);
+
+ if (ex.X_op != O_constant)
+ as_bad (_("constant value required"));
+
+ if (ex.X_add_number > max || ex.X_add_number < 0)
+ as_bad (_("number must be positive and less than %d"), max + 1);
+
+ return ex.X_add_number;
+}
+
+/* Parse for ldd/std offset. */
+
+static void
+avr_offset_expression (expressionS *exp)
+{
+ char *str = input_line_pointer;
+ char *tmp;
+ char op[8];
+
+ tmp = str;
+ str = extract_word (str, op, sizeof (op));
+
+ input_line_pointer = tmp;
+ expression (exp);
+
+ /* Warn about expressions that fail to use lo8 (). */
+ if (exp->X_op == O_constant)
+ {
+ int x = exp->X_add_number;
+
+ if (x < -255 || x > 255)
+ as_warn (_("constant out of 8-bit range: %d"), x);
+ }
+}
+
+/* Parse ordinary expression. */
+
+static char *
+parse_exp (char *s, expressionS *op)
+{
+ input_line_pointer = s;
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ return input_line_pointer;
+}
+
+/* Parse special expressions (needed for LDI command):
+ xx8 (address)
+ xx8 (-address)
+ pm_xx8 (address)
+ pm_xx8 (-address)
+ where xx is: hh, hi, lo. */
+
+static bfd_reloc_code_real_type
+avr_ldi_expression (expressionS *exp)
+{
+ char *str = input_line_pointer;
+ char *tmp;
+ char op[8];
+ int mod;
+ int linker_stubs_should_be_generated = 0;
+
+ tmp = str;
+
+ str = extract_word (str, op, sizeof (op));
+
+ if (op[0])
+ {
+ mod_index m;
+
+ m.ptr = hash_find (avr_mod_hash, op);
+ mod = m.index;
+
+ if (mod)
+ {
+ int closes = 0;
+
+ mod -= 10;
+ str = skip_space (str);
+
+ if (*str == '(')
+ {
+ bfd_reloc_code_real_type reloc_to_return;
+ int neg_p = 0;
+
+ ++str;
+
+ if (strncmp ("pm(", str, 3) == 0
+ || strncmp ("gs(",str,3) == 0
+ || strncmp ("-(gs(",str,5) == 0
+ || strncmp ("-(pm(", str, 5) == 0)
+ {
+ if (HAVE_PM_P (mod))
+ {
+ ++mod;
+ ++closes;
+ }
+ else
+ as_bad (_("illegal expression"));
+
+ if (str[0] == 'g' || str[2] == 'g')
+ linker_stubs_should_be_generated = 1;
+
+ if (*str == '-')
+ {
+ neg_p = 1;
+ ++closes;
+ str += 5;
+ }
+ else
+ str += 3;
+ }
+
+ if (*str == '-' && *(str + 1) == '(')
+ {
+ neg_p ^= 1;
+ ++closes;
+ str += 2;
+ }
+
+ input_line_pointer = str;
+ expression (exp);
+
+ do
+ {
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("`)' required"));
+ break;
+ }
+ input_line_pointer++;
+ }
+ while (closes--);
+
+ reloc_to_return =
+ neg_p ? EXP_MOD_NEG_RELOC (mod) : EXP_MOD_RELOC (mod);
+ if (linker_stubs_should_be_generated)
+ {
+ switch (reloc_to_return)
+ {
+ case BFD_RELOC_AVR_LO8_LDI_PM:
+ reloc_to_return = BFD_RELOC_AVR_LO8_LDI_GS;
+ break;
+ case BFD_RELOC_AVR_HI8_LDI_PM:
+ reloc_to_return = BFD_RELOC_AVR_HI8_LDI_GS;
+ break;
+
+ default:
+ /* PR 5523: Do not generate a warning here,
+ legitimate code can trigger this case. */
+ break;
+ }
+ }
+ return reloc_to_return;
+ }
+ }
+ }
+
+ input_line_pointer = tmp;
+ expression (exp);
+
+ /* Warn about expressions that fail to use lo8 (). */
+ if (exp->X_op == O_constant)
+ {
+ int x = exp->X_add_number;
+
+ if (x < -255 || x > 255)
+ as_warn (_("constant out of 8-bit range: %d"), x);
+ }
+
+ return BFD_RELOC_AVR_LDI;
+}
+
+/* Parse one instruction operand.
+ Return operand bitmask. Also fixups can be generated. */
+
+static unsigned int
+avr_operand (struct avr_opcodes_s *opcode,
+ int where,
+ char *op,
+ char **line)
+{
+ expressionS op_expr;
+ unsigned int op_mask = 0;
+ char *str = skip_space (*line);
+
+ switch (*op)
+ {
+ /* Any register operand. */
+ case 'w':
+ case 'd':
+ case 'r':
+ case 'a':
+ case 'v':
+ {
+ char * old_str = str;
+ char *lower;
+ char r_name[20];
+
+ str = extract_word (str, r_name, sizeof (r_name));
+ for (lower = r_name; *lower; ++lower)
+ {
+ if (*lower >= 'A' && *lower <= 'Z')
+ *lower += 'a' - 'A';
+ }
+
+ if (r_name[0] == 'r' && ISDIGIT (r_name[1]) && r_name[2] == 0)
+ /* Single-digit register number, ie r0-r9. */
+ op_mask = r_name[1] - '0';
+ else if (r_name[0] == 'r' && ISDIGIT (r_name[1])
+ && ISDIGIT (r_name[2]) && r_name[3] == 0)
+ /* Double-digit register number, ie r10 - r32. */
+ op_mask = (r_name[1] - '0') * 10 + r_name[2] - '0';
+ else if (r_name[0] >= 'x' && r_name[0] <= 'z'
+ && (r_name[1] == 'l' || r_name[1] == 'h') && r_name[2] == 0)
+ /* Registers r26-r31 referred to by name, ie xl, xh, yl, yh, zl, zh. */
+ op_mask = (r_name[0] - 'x') * 2 + (r_name[1] == 'h') + 26;
+ else if ((*op == 'v' || *op == 'w')
+ && r_name[0] >= 'x' && r_name[0] <= 'z' && r_name[1] == 0)
+ /* For the movw and addiw instructions, refer to registers x, y and z by name. */
+ op_mask = (r_name[0] - 'x') * 2 + 26;
+ else
+ {
+ /* Numeric or symbolic constant register number. */
+ op_mask = avr_get_constant (old_str, 31);
+ str = input_line_pointer;
+ }
+ }
+
+ if (avr_mcu->mach == bfd_mach_avrtiny)
+ {
+ if (op_mask < 16 || op_mask > 31)
+ {
+ as_bad (_("register name or number from 16 to 31 required"));
+ break;
+ }
+ }
+ else if (op_mask > 31)
+ {
+ as_bad (_("register name or number from 0 to 31 required"));
+ break;
+ }
+
+ switch (*op)
+ {
+ case 'a':
+ if (op_mask < 16 || op_mask > 23)
+ as_bad (_("register r16-r23 required"));
+ op_mask -= 16;
+ break;
+
+ case 'd':
+ if (op_mask < 16)
+ as_bad (_("register number above 15 required"));
+ op_mask -= 16;
+ break;
+
+ case 'v':
+ if (op_mask & 1)
+ as_bad (_("even register number required"));
+ op_mask >>= 1;
+ break;
+
+ case 'w':
+ if ((op_mask & 1) || op_mask < 24)
+ as_bad (_("register r24, r26, r28 or r30 required"));
+ op_mask = (op_mask - 24) >> 1;
+ break;
+ }
+ break;
+
+ case 'e':
+ {
+ char c;
+
+ if (*str == '-')
+ {
+ str = skip_space (str + 1);
+ op_mask = 0x1002;
+ }
+ c = TOLOWER (*str);
+ if (c == 'x')
+ op_mask |= 0x100c;
+ else if (c == 'y')
+ op_mask |= 0x8;
+ else if (c != 'z')
+ as_bad (_("pointer register (X, Y or Z) required"));
+
+ str = skip_space (str + 1);
+ if (*str == '+')
+ {
+ ++str;
+ if (op_mask & 2)
+ as_bad (_("cannot both predecrement and postincrement"));
+ op_mask |= 0x1001;
+ }
+
+ /* avr1 can do "ld r,Z" and "st Z,r" but no other pointer
+ registers, no predecrement, no postincrement. */
+ if (!avr_opt.all_opcodes && (op_mask & 0x100F)
+ && !(avr_mcu->isa & AVR_ISA_SRAM))
+ as_bad (_("addressing mode not supported"));
+ }
+ break;
+
+ case 'z':
+ if (*str == '-')
+ as_bad (_("can't predecrement"));
+
+ if (! (*str == 'z' || *str == 'Z'))
+ as_bad (_("pointer register Z required"));
+
+ str = skip_space (str + 1);
+
+ if (*str == '+')
+ {
+ ++str;
+ char *s;
+ for (s = opcode->opcode; *s; ++s)
+ {
+ if (*s == '+')
+ op_mask |= (1 << (15 - (s - opcode->opcode)));
+ }
+ }
+
+ /* attiny26 can do "lpm" and "lpm r,Z" but not "lpm r,Z+". */
+ if (!avr_opt.all_opcodes
+ && (op_mask & 0x0001)
+ && !(avr_mcu->isa & AVR_ISA_MOVW))
+ as_bad (_("postincrement not supported"));
+ break;
+
+ case 'b':
+ {
+ char c = TOLOWER (*str++);
+
+ if (c == 'y')
+ op_mask |= 0x8;
+ else if (c != 'z')
+ as_bad (_("pointer register (Y or Z) required"));
+ str = skip_space (str);
+ if (*str++ == '+')
+ {
+ input_line_pointer = str;
+ avr_offset_expression (& op_expr);
+ str = input_line_pointer;
+ fix_new_exp (frag_now, where, 3,
+ &op_expr, FALSE, BFD_RELOC_AVR_6);
+ }
+ }
+ break;
+
+ case 'h':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where, opcode->insn_size * 2,
+ &op_expr, FALSE, BFD_RELOC_AVR_CALL);
+ break;
+
+ case 'L':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where, opcode->insn_size * 2,
+ &op_expr, TRUE, BFD_RELOC_AVR_13_PCREL);
+ break;
+
+ case 'l':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where, opcode->insn_size * 2,
+ &op_expr, TRUE, BFD_RELOC_AVR_7_PCREL);
+ break;
+
+ case 'i':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where + 2, opcode->insn_size * 2,
+ &op_expr, FALSE, BFD_RELOC_16);
+ break;
+
+ case 'j':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where, opcode->insn_size * 2,
+ &op_expr, FALSE, BFD_RELOC_AVR_LDS_STS_16);
+ break;
+
+ case 'M':
+ {
+ bfd_reloc_code_real_type r_type;
+
+ input_line_pointer = str;
+ r_type = avr_ldi_expression (&op_expr);
+ str = input_line_pointer;
+ fix_new_exp (frag_now, where, 3,
+ &op_expr, FALSE, r_type);
+ }
+ break;
+
+ case 'n':
+ {
+ unsigned int x;
+
+ x = ~avr_get_constant (str, 255);
+ str = input_line_pointer;
+ op_mask |= (x & 0xf) | ((x << 4) & 0xf00);
+ }
+ break;
+
+ case 'K':
+ input_line_pointer = str;
+ avr_offset_expression (& op_expr);
+ str = input_line_pointer;
+ fix_new_exp (frag_now, where, 3,
+ & op_expr, FALSE, BFD_RELOC_AVR_6_ADIW);
+ break;
+
+ case 'S':
+ case 's':
+ {
+ unsigned int x;
+
+ x = avr_get_constant (str, 7);
+ str = input_line_pointer;
+ if (*op == 'S')
+ x <<= 4;
+ op_mask |= x;
+ }
+ break;
+
+ case 'P':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where, opcode->insn_size * 2,
+ &op_expr, FALSE, BFD_RELOC_AVR_PORT6);
+ break;
+
+ case 'p':
+ str = parse_exp (str, &op_expr);
+ fix_new_exp (frag_now, where, opcode->insn_size * 2,
+ &op_expr, FALSE, BFD_RELOC_AVR_PORT5);
+ break;
+
+ case 'E':
+ {
+ unsigned int x;
+
+ x = avr_get_constant (str, 15);
+ str = input_line_pointer;
+ op_mask |= (x << 4);
+ }
+ break;
+
+ case '?':
+ break;
+
+ default:
+ as_bad (_("unknown constraint `%c'"), *op);
+ }
+
+ *line = str;
+ return op_mask;
+}
+
+/* Parse instruction operands.
+ Return binary opcode. */
+
+static unsigned int
+avr_operands (struct avr_opcodes_s *opcode, char **line)
+{
+ char *op = opcode->constraints;
+ unsigned int bin = opcode->bin_opcode;
+ char *frag = frag_more (opcode->insn_size * 2);
+ char *str = *line;
+ int where = frag - frag_now->fr_literal;
+ static unsigned int prev = 0; /* Previous opcode. */
+
+ /* Opcode have operands. */
+ if (*op)
+ {
+ unsigned int reg1 = 0;
+ unsigned int reg2 = 0;
+ int reg1_present = 0;
+ int reg2_present = 0;
+
+ /* Parse first operand. */
+ if (REGISTER_P (*op))
+ reg1_present = 1;
+ reg1 = avr_operand (opcode, where, op, &str);
+ ++op;
+
+ /* Parse second operand. */
+ if (*op)
+ {
+ if (*op == ',')
+ ++op;
+
+ if (*op == '=')
+ {
+ reg2 = reg1;
+ reg2_present = 1;
+ }
+ else
+ {
+ if (REGISTER_P (*op))
+ reg2_present = 1;
+
+ str = skip_space (str);
+ if (*str++ != ',')
+ as_bad (_("`,' required"));
+ str = skip_space (str);
+
+ reg2 = avr_operand (opcode, where, op, &str);
+ }
+
+ if (reg1_present && reg2_present)
+ reg2 = (reg2 & 0xf) | ((reg2 << 5) & 0x200);
+ else if (reg2_present)
+ reg2 <<= 4;
+ }
+ if (reg1_present)
+ reg1 <<= 4;
+ bin |= reg1 | reg2;
+ }
+
+ /* Detect undefined combinations (like ld r31,Z+). */
+ if (!avr_opt.all_opcodes && AVR_UNDEF_P (bin))
+ as_warn (_("undefined combination of operands"));
+
+ if (opcode->insn_size == 2)
+ {
+ /* Warn if the previous opcode was cpse/sbic/sbis/sbrc/sbrs
+ (AVR core bug, fixed in the newer devices). */
+ if (!(avr_opt.no_skip_bug ||
+ (avr_mcu->isa & (AVR_ISA_MUL | AVR_ISA_MOVW)))
+ && AVR_SKIP_P (prev))
+ as_warn (_("skipping two-word instruction"));
+
+ bfd_putl32 ((bfd_vma) bin, frag);
+ }
+ else
+ bfd_putl16 ((bfd_vma) bin, frag);
+
+ prev = bin;
+ *line = str;
+ return bin;
+}
+
+/* GAS will call this function for each section at the end of the assembly,
+ to permit the CPU backend to adjust the alignment of a section. */
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* If you define this macro, it should return the offset between the
+ address of a PC relative fixup and the position from which the PC
+ relative adjustment should be made. On many processors, the base
+ of a PC relative instruction is the next instruction, so this
+ macro would return the length of an instruction. */
+
+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)))
+ return 0;
+
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+ return (sec->flags & SEC_DEBUGGING) == 0;
+}
+
+/* Does whatever the xtensa port does. */
+int
+avr_validate_fix_sub (fixS *fix)
+{
+ segT add_symbol_segment, sub_symbol_segment;
+
+ /* The difference of two symbols should be resolved by the assembler when
+ linkrelax is not set. If the linker may relax the section containing
+ the symbols, then an Xtensa DIFF relocation must be generated so that
+ the linker knows to adjust the difference value. */
+ if (!linkrelax || fix->fx_addsy == NULL)
+ return 0;
+
+ /* Make sure both symbols are in the same segment, and that segment is
+ "normal" and relaxable. If the segment is not "normal", then the
+ fix is not valid. If the segment is not "relaxable", then the fix
+ should have been handled earlier. */
+ add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+ if (! SEG_NORMAL (add_symbol_segment) ||
+ ! relaxable_section (add_symbol_segment))
+ return 0;
+
+ sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+ return (sub_symbol_segment == add_symbol_segment);
+}
+
+/* TC_FORCE_RELOCATION hook */
+
+/* If linkrelax is turned on, and the symbol to relocate
+ against is in a relaxable segment, don't compute the value -
+ generate a relocation instead. */
+int
+avr_force_relocation (fixS *fix)
+{
+ if (linkrelax && fix->fx_addsy
+ && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+/* GAS will call this for each fixup. It should store the correct
+ value in the object file. */
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg)
+{
+ unsigned char *where;
+ unsigned long insn;
+ long value = *valP;
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ else if (fixP->fx_pcrel)
+ {
+ segT s = S_GET_SEGMENT (fixP->fx_addsy);
+
+ if (s == seg || s == absolute_section)
+ {
+ value += S_GET_VALUE (fixP->fx_addsy);
+ fixP->fx_done = 1;
+ }
+ }
+ else if (linkrelax && fixP->fx_subsy)
+ {
+ /* For a subtraction relocation expression, generate one
+ of the DIFF relocs, with the value being the difference.
+ Note that a sym1 - sym2 expression is adjusted into a
+ section_start_sym + sym4_offset_from_section_start - sym1
+ expression. fixP->fx_addsy holds the section start symbol,
+ fixP->fx_offset holds sym2's offset, and fixP->fx_subsy
+ holds sym1. Calculate the current difference and write value,
+ but leave fx_offset as is - during relaxation,
+ fx_offset - value gives sym1's value. */
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_AVR_DIFF8;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_AVR_DIFF16;
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_AVR_DIFF32;
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ break;
+ }
+
+ value = S_GET_VALUE (fixP->fx_addsy) +
+ fixP->fx_offset - S_GET_VALUE (fixP->fx_subsy);
+
+ fixP->fx_subsy = NULL;
+ }
+ /* We don't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ /* For the DIFF relocs, write the value into the object file while still
+ keeping fx_done FALSE, as both the difference (recorded in the object file)
+ and the sym offset (part of fixP) are needed at link relax time. */
+ where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+ switch (fixP->fx_r_type)
+ {
+ default:
+ fixP->fx_no_overflow = 1;
+ break;
+ case BFD_RELOC_AVR_7_PCREL:
+ case BFD_RELOC_AVR_13_PCREL:
+ case BFD_RELOC_32:
+ case BFD_RELOC_16:
+ break;
+ case BFD_RELOC_AVR_DIFF8:
+ *where = value;
+ break;
+ case BFD_RELOC_AVR_DIFF16:
+ bfd_putl16 ((bfd_vma) value, where);
+ break;
+ case BFD_RELOC_AVR_DIFF32:
+ bfd_putl32 ((bfd_vma) value, where);
+ break;
+ case BFD_RELOC_AVR_CALL:
+ break;
+ }
+
+ if (fixP->fx_done)
+ {
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+ insn = bfd_getl16 (where);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_AVR_7_PCREL:
+ if (value & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("odd address operand: %ld"), value);
+
+ /* Instruction addresses are always right-shifted by 1. */
+ value >>= 1;
+ --value; /* Correct PC. */
+
+ if (value < -64 || value > 63)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ value = (value << 3) & 0x3f8;
+ bfd_putl16 ((bfd_vma) (value | insn), where);
+ break;
+
+ case BFD_RELOC_AVR_13_PCREL:
+ if (value & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("odd address operand: %ld"), value);
+
+ /* Instruction addresses are always right-shifted by 1. */
+ value >>= 1;
+ --value; /* Correct PC. */
+
+ if (value < -2048 || value > 2047)
+ {
+ /* No wrap for devices with >8K of program memory. */
+ if ((avr_mcu->isa & AVR_ISA_MEGA) || avr_opt.no_wrap)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ }
+
+ value &= 0xfff;
+ bfd_putl16 ((bfd_vma) (value | insn), where);
+ break;
+
+ case BFD_RELOC_32:
+ bfd_putl32 ((bfd_vma) value, where);
+ break;
+
+ case BFD_RELOC_16:
+ bfd_putl16 ((bfd_vma) value, where);
+ break;
+
+ case BFD_RELOC_8:
+ if (value > 255 || value < -128)
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ *where = value;
+ break;
+
+ case BFD_RELOC_AVR_16_PM:
+ bfd_putl16 ((bfd_vma) (value >> 1), where);
+ break;
+
+ case BFD_RELOC_AVR_LDI:
+ if (value > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
+ break;
+
+ case BFD_RELOC_AVR_LDS_STS_16:
+ if ((value < 0x40) || (value > 0xBF))
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: 0x%lx"),
+ (unsigned long)value);
+ insn |= ((value & 0xF) | ((value & 0x30) << 5) | ((value & 0x40) << 2));
+ bfd_putl16 ((bfd_vma) insn, where);
+ break;
+
+ case BFD_RELOC_AVR_6:
+ if ((value > 63) || (value < 0))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ bfd_putl16 ((bfd_vma) insn | ((value & 7) | ((value & (3 << 3)) << 7)
+ | ((value & (1 << 5)) << 8)), where);
+ break;
+
+ case BFD_RELOC_AVR_6_ADIW:
+ if ((value > 63) || (value < 0))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ bfd_putl16 ((bfd_vma) insn | (value & 0xf) | ((value & 0x30) << 2), where);
+ break;
+
+ case BFD_RELOC_AVR_LO8_LDI:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value), where);
+ break;
+
+ case BFD_RELOC_AVR_HI8_LDI:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 8), where);
+ break;
+
+ case BFD_RELOC_AVR_MS8_LDI:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 24), where);
+ break;
+
+ case BFD_RELOC_AVR_HH8_LDI:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 16), where);
+ break;
+
+ case BFD_RELOC_AVR_LO8_LDI_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value), where);
+ break;
+
+ case BFD_RELOC_AVR_HI8_LDI_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 8), where);
+ break;
+
+ case BFD_RELOC_AVR_MS8_LDI_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 24), where);
+ break;
+
+ case BFD_RELOC_AVR_HH8_LDI_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 16), where);
+ break;
+
+ case BFD_RELOC_AVR_LO8_LDI_PM:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 1), where);
+ break;
+
+ case BFD_RELOC_AVR_HI8_LDI_PM:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 9), where);
+ break;
+
+ case BFD_RELOC_AVR_HH8_LDI_PM:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (value >> 17), where);
+ break;
+
+ case BFD_RELOC_AVR_LO8_LDI_PM_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 1), where);
+ break;
+
+ case BFD_RELOC_AVR_HI8_LDI_PM_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 9), where);
+ break;
+
+ case BFD_RELOC_AVR_HH8_LDI_PM_NEG:
+ bfd_putl16 ((bfd_vma) insn | LDI_IMMEDIATE (-value >> 17), where);
+ break;
+
+ case BFD_RELOC_AVR_CALL:
+ {
+ unsigned long x;
+
+ x = bfd_getl16 (where);
+ if (value & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("odd address operand: %ld"), value);
+ value >>= 1;
+ x |= ((value & 0x10000) | ((value << 3) & 0x1f00000)) >> 16;
+ bfd_putl16 ((bfd_vma) x, where);
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
+ }
+ break;
+
+ case BFD_RELOC_AVR_8_LO:
+ *where = 0xff & value;
+ break;
+
+ case BFD_RELOC_AVR_8_HI:
+ *where = 0xff & (value >> 8);
+ break;
+
+ case BFD_RELOC_AVR_8_HLO:
+ *where = 0xff & (value >> 16);
+ break;
+
+ default:
+ as_fatal (_("line %d: unknown relocation type: 0x%x"),
+ fixP->fx_line, fixP->fx_r_type);
+ break;
+
+ case BFD_RELOC_AVR_PORT6:
+ if (value > 63)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ bfd_putl16 ((bfd_vma) insn | ((value & 0x30) << 5) | (value & 0x0f), where);
+ break;
+
+ case BFD_RELOC_AVR_PORT5:
+ if (value > 31)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range: %ld"), value);
+ bfd_putl16 ((bfd_vma) insn | ((value & 0x1f) << 3), where);
+ break;
+ }
+ }
+ else
+ {
+ switch ((int) fixP->fx_r_type)
+ {
+ case -BFD_RELOC_AVR_HI8_LDI_NEG:
+ case -BFD_RELOC_AVR_HI8_LDI:
+ case -BFD_RELOC_AVR_LO8_LDI_NEG:
+ case -BFD_RELOC_AVR_LO8_LDI:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("only constant expression allowed"));
+ fixP->fx_done = 1;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+/* 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. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED,
+ fixS *fixp)
+{
+ arelent *reloc;
+
+ if (fixp->fx_subsy != NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line, _("expression too complex"));
+ return NULL;
+ }
+
+ reloc = xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ reloc->addend = fixp->fx_offset;
+
+ return reloc;
+}
+
+void
+md_assemble (char *str)
+{
+ struct avr_opcodes_s *opcode;
+ char op[11];
+
+ str = skip_space (extract_word (str, op, sizeof (op)));
+
+ if (!op[0])
+ as_bad (_("can't find opcode "));
+
+ opcode = (struct avr_opcodes_s *) hash_find (avr_hash, op);
+
+ if (opcode && !avr_opt.all_opcodes)
+ {
+ /* Check if the instruction's ISA bit is ON in the ISA bits of the part
+ specified by the user. If not look for other instructions
+ specifications with same mnemonic who's ISA bits matches.
+
+ This requires include/opcode/avr.h to have the instructions with
+ same mnenomic to be specified in sequence. */
+
+ while ((opcode->isa & avr_mcu->isa) != opcode->isa)
+ {
+ opcode++;
+
+ if (opcode->name && strcmp(op, opcode->name))
+ {
+ as_bad (_("illegal opcode %s for mcu %s"),
+ opcode->name, avr_mcu->name);
+ return;
+ }
+ }
+ }
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode `%s'"), op);
+ return;
+ }
+
+ /* Special case for opcodes with optional operands (lpm, elpm) -
+ version with operands exists in avr_opcodes[] in the next entry. */
+
+ if (*str && *opcode->constraints == '?')
+ ++opcode;
+
+ dwarf2_emit_insn (0);
+
+ /* We used to set input_line_pointer to the result of get_operands,
+ but that is wrong. Our caller assumes we don't change it. */
+ {
+ char *t = input_line_pointer;
+
+ avr_operands (opcode, &str);
+ if (*skip_space (str))
+ as_bad (_("garbage at end of line"));
+ input_line_pointer = t;
+ }
+}
+
+const exp_mod_data_t exp_mod_data[] =
+{
+ /* Default, must be first. */
+ { "", 0, BFD_RELOC_16, "" },
+ /* Divides by 2 to get word address. Generate Stub. */
+ { "gs", 2, BFD_RELOC_AVR_16_PM, "`gs' " },
+ { "pm", 2, BFD_RELOC_AVR_16_PM, "`pm' " },
+ /* The following are used together with avr-gcc's __memx address space
+ in order to initialize a 24-bit pointer variable with a 24-bit address.
+ For address in flash, hlo8 will contain the flash segment if the
+ symbol is located in flash. If the symbol is located in RAM; hlo8
+ will contain 0x80 which matches avr-gcc's notion of how 24-bit RAM/flash
+ addresses linearize address space. */
+ { "lo8", 1, BFD_RELOC_AVR_8_LO, "`lo8' " },
+ { "hi8", 1, BFD_RELOC_AVR_8_HI, "`hi8' " },
+ { "hlo8", 1, BFD_RELOC_AVR_8_HLO, "`hlo8' " },
+ { "hh8", 1, BFD_RELOC_AVR_8_HLO, "`hh8' " },
+ /* End of list. */
+ { NULL, 0, 0, NULL }
+};
+
+/* Parse special CONS expression: pm (expression) or alternatively
+ gs (expression). These are used for addressing program memory. Moreover,
+ define lo8 (expression), hi8 (expression) and hlo8 (expression). */
+
+const exp_mod_data_t *
+avr_parse_cons_expression (expressionS *exp, int nbytes)
+{
+ const exp_mod_data_t *pexp = &exp_mod_data[0];
+ char *tmp;
+
+ tmp = input_line_pointer = skip_space (input_line_pointer);
+
+ /* The first entry of exp_mod_data[] contains an entry if no
+ expression modifier is present. Skip it. */
+
+ for (pexp++; pexp->name; pexp++)
+ {
+ int len = strlen (pexp->name);
+
+ if (nbytes == pexp->nbytes
+ && strncasecmp (input_line_pointer, pexp->name, len) == 0)
+ {
+ input_line_pointer = skip_space (input_line_pointer + len);
+
+ if (*input_line_pointer == '(')
+ {
+ input_line_pointer = skip_space (input_line_pointer + 1);
+ expression (exp);
+
+ if (*input_line_pointer == ')')
+ {
+ ++input_line_pointer;
+ return pexp;
+ }
+ else
+ {
+ as_bad (_("`)' required"));
+ return &exp_mod_data[0];
+ }
+ }
+
+ input_line_pointer = tmp;
+
+ break;
+ }
+ }
+
+ expression (exp);
+ return &exp_mod_data[0];
+}
+
+void
+avr_cons_fix_new (fragS *frag,
+ int where,
+ int nbytes,
+ expressionS *exp,
+ const exp_mod_data_t *pexp_mod_data)
+{
+ int bad = 0;
+
+ switch (pexp_mod_data->reloc)
+ {
+ default:
+ if (nbytes == 1)
+ fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_8);
+ else if (nbytes == 2)
+ fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_16);
+ else if (nbytes == 4)
+ fix_new_exp (frag, where, nbytes, exp, FALSE, BFD_RELOC_32);
+ else
+ bad = 1;
+ break;
+
+ case BFD_RELOC_AVR_16_PM:
+ case BFD_RELOC_AVR_8_LO:
+ case BFD_RELOC_AVR_8_HI:
+ case BFD_RELOC_AVR_8_HLO:
+ if (nbytes == pexp_mod_data->nbytes)
+ fix_new_exp (frag, where, nbytes, exp, FALSE, pexp_mod_data->reloc);
+ else
+ bad = 1;
+ break;
+ }
+
+ if (bad)
+ as_bad (_("illegal %srelocation size: %d"), pexp_mod_data->error, nbytes);
+}
+
+static bfd_boolean
+mcu_has_3_byte_pc (void)
+{
+ int mach = avr_mcu->mach;
+
+ return mach == bfd_mach_avr6
+ || mach == bfd_mach_avrxmega6
+ || mach == bfd_mach_avrxmega7;
+}
+
+void
+tc_cfi_frame_initial_instructions (void)
+{
+ /* AVR6 pushes 3 bytes for calls. */
+ int return_size = (mcu_has_3_byte_pc () ? 3 : 2);
+
+ /* The CFA is the caller's stack location before the call insn. */
+ /* Note that the stack pointer is dwarf register number 32. */
+ cfi_add_CFA_def_cfa (32, return_size);
+
+ /* Note that AVR consistently uses post-decrement, which means that things
+ do not line up the same way as for targers that use pre-decrement. */
+ cfi_add_CFA_offset (DWARF2_DEFAULT_RETURN_COLUMN, 1-return_size);
+}
+
+bfd_boolean
+avr_allow_local_subtract (expressionS * left,
+ expressionS * right,
+ segT section)
+{
+ /* If we are not in relaxation mode, subtraction is OK. */
+ if (!linkrelax)
+ return TRUE;
+
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ if (left->X_add_symbol == right->X_add_symbol)
+ return TRUE;
+
+ /* We have to assume that there may be instructions between the
+ two symbols and that relaxation may increase the distance between
+ them. */
+ return FALSE;
+}
diff --git a/gas/config/tc-avr.h b/gas/config/tc-avr.h
new file mode 100644
index 0000000..fb596ad
--- /dev/null
+++ b/gas/config/tc-avr.h
@@ -0,0 +1,215 @@
+/* This file is tc-avr.h
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+ Contributed by Denis Chertykov <denisc@overta.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 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* By convention, you should define this macro in the `.h' file. For
+ example, `tc-m68k.h' defines `TC_M68K'. You might have to use this
+ if it is necessary to add CPU specific code to the object format
+ file. */
+#define TC_AVR
+
+/* This macro is the BFD target name to use when creating the output
+ file. This will normally depend upon the `OBJ_FMT' macro. */
+#define TARGET_FORMAT "elf32-avr"
+
+/* This macro is the BFD architecture to pass to `bfd_set_arch_mach'. */
+#define TARGET_ARCH bfd_arch_avr
+
+/* This macro is the BFD machine number to pass to
+ `bfd_set_arch_mach'. If it is not defined, GAS will use 0. */
+#define TARGET_MACH 0
+
+/* You should define this macro to be non-zero if the target is big
+ endian, and zero if the target is little endian. */
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* If you define this macro, GAS will warn about the use of
+ nonstandard escape sequences in a string. */
+#define ONLY_STANDARD_ESCAPES
+
+/* GAS will call this function for any expression that can not be
+ recognized. When the function is called, `input_line_pointer'
+ will point to the start of the expression. */
+#define md_operand(x)
+
+typedef struct
+{
+ /* Name of the expression modifier allowed with .byte, .word, etc. */
+ const char *name;
+
+ /* Only allowed with n bytes of data. */
+ int nbytes;
+
+ /* Associated RELOC. */
+ bfd_reloc_code_real_type reloc;
+
+ /* Part of the error message. */
+ const char *error;
+} exp_mod_data_t;
+
+extern const exp_mod_data_t exp_mod_data[];
+#define TC_PARSE_CONS_RETURN_TYPE const exp_mod_data_t *
+#define TC_PARSE_CONS_RETURN_NONE exp_mod_data
+
+/* You may define this macro to parse an expression used in a data
+ allocation pseudo-op such as `.word'. You can use this to
+ recognize relocation directives that may appear in such directives. */
+#define TC_PARSE_CONS_EXPRESSION(EXPR,N) avr_parse_cons_expression (EXPR, N)
+extern const exp_mod_data_t *avr_parse_cons_expression (expressionS *, int);
+
+/* You may define this macro to generate a fixup for a data
+ allocation pseudo-op. */
+#define TC_CONS_FIX_NEW avr_cons_fix_new
+extern void avr_cons_fix_new (fragS *,int, int, expressionS *,
+ const exp_mod_data_t *);
+
+/* This should just call either `number_to_chars_bigendian' or
+ `number_to_chars_littleendian', whichever is appropriate. On
+ targets like the MIPS which support options to change the
+ endianness, which function to call is a runtime decision. On
+ other targets, `md_number_to_chars' can be a simple macro. */
+#define md_number_to_chars number_to_chars_littleendian
+
+/* `md_short_jump_size'
+ `md_long_jump_size'
+ `md_create_short_jump'
+ `md_create_long_jump'
+ If `WORKING_DOT_WORD' is defined, GAS will not do broken word
+ processing (*note Broken words::.). Otherwise, you should set
+ `md_short_jump_size' to the size of a short jump (a jump that is
+ just long enough to jump around a long jmp) and
+ `md_long_jump_size' to the size of a long jump (a jump that can go
+ anywhere in the function), You should define
+ `md_create_short_jump' to create a short jump around a long jump,
+ and define `md_create_long_jump' to create a long jump. */
+#define WORKING_DOT_WORD
+
+/* If you define this macro, it means that `tc_gen_reloc' may return
+ multiple relocation entries for a single fixup. In this case, the
+ return value of `tc_gen_reloc' is a pointer to a null terminated
+ array. */
+#undef RELOC_EXPANSION_POSSIBLE
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* If defined, this macro allows control over whether fixups for a
+ given section will be processed when the linkrelax variable is
+ set. Define it to zero and handle things in md_apply_fix instead.*/
+#define TC_LINKRELAX_FIXUP(SEG) 0
+
+/* If this macro returns non-zero, it guarantees that a relocation will be emitted
+ even when the value can be resolved locally. Do that if linkrelax is turned on */
+#define TC_FORCE_RELOCATION(fix) avr_force_relocation (fix)
+#define TC_FORCE_RELOCATION_SUB_SAME(fix, seg) \
+ (! SEG_NORMAL (seg) || avr_force_relocation (fix))
+extern int avr_force_relocation (struct fix *);
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* If you define this macro, it should return the offset between the
+ address of a PC relative fixup and the position from which the PC
+ relative adjustment should be made. On many processors, the base
+ of a PC relative instruction is the next instruction, so this
+ macro would return the length of an instruction. */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* The number of bytes to put into a word in a listing. This affects
+ the way the bytes are clumped together in the listing. For
+ example, a value of 2 might print `1234 5678' where a value of 1
+ would print `12 34 56 78'. The default value is 4. */
+#define LISTING_WORD_SIZE 2
+
+/* AVR port uses `$' as a logical line separator. */
+#define LEX_DOLLAR 0
+
+/* An `.lcomm' directive with no explicit alignment parameter will
+ use this macro to set P2VAR to the alignment that a request for
+ SIZE bytes will have. The alignment is expressed as a power of
+ two. If no alignment should take place, the macro definition
+ should do nothing. Some targets define a `.bss' directive that is
+ also affected by this macro. The default definition will set
+ P2VAR to the truncated power of two of sizes up to eight bytes. */
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 0
+
+/* We don't want gas to fixup the following program memory related relocations.
+ We will need them in case that we want to do linker relaxation.
+ We could in principle keep these fixups in gas when not relaxing.
+ However, there is no serious performance penalty when making the linker
+ make the fixup work. Check also that fx_addsy is not NULL, in order to make
+ sure that the fixup refers to some sort of label. */
+#define TC_VALIDATE_FIX(FIXP,SEG,SKIP) \
+ if ( (FIXP->fx_r_type == BFD_RELOC_AVR_7_PCREL \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_13_PCREL \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_LO8_LDI_PM \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_LO8_LDI_GS \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_HI8_LDI_PM \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_HI8_LDI_GS \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_HH8_LDI_PM \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_LO8_LDI_PM_NEG \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_HI8_LDI_PM_NEG \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_HH8_LDI_PM_NEG \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_8_LO \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_8_HI \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_8_HLO \
+ || FIXP->fx_r_type == BFD_RELOC_AVR_16_PM) \
+ && FIXP->fx_addsy != NULL \
+ && FIXP->fx_subsy == NULL) \
+ { \
+ symbol_mark_used_in_reloc (FIXP->fx_addsy); \
+ goto SKIP; \
+ }
+
+/* This macro is evaluated for any fixup with a fx_subsy that
+ fixup_segment cannot reduce to a number. If the macro returns
+ false an error will be reported. */
+#define TC_VALIDATE_FIX_SUB(fix, seg) avr_validate_fix_sub (fix)
+extern int avr_validate_fix_sub (struct fix *);
+
+/* This target is buggy, and sets fix size too large. */
+#define TC_FX_SIZE_SLACK(FIX) 2
+
+/* AVR instructions are 2 or 4 bytes long. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* 32 bits pseudo-addresses are used on AVR. */
+#define DWARF2_ADDR_SIZE(bfd) 4
+
+/* Enable cfi directives. */
+#define TARGET_USE_CFIPOP 1
+
+/* The stack grows down, and is only byte aligned. */
+#define DWARF2_CIE_DATA_ALIGNMENT -1
+
+/* Define the column that represents the PC. */
+#define DWARF2_DEFAULT_RETURN_COLUMN 36
+
+/* Define a hook to setup initial CFI state. */
+extern void tc_cfi_frame_initial_instructions (void);
+#define tc_cfi_frame_initial_instructions tc_cfi_frame_initial_instructions
+
+/* The difference between same-section symbols may be affected by linker
+ relaxation, so do not resolve such expressions in the assembler. */
+#define md_allow_local_subtract(l,r,s) avr_allow_local_subtract (l, r, s)
+extern bfd_boolean avr_allow_local_subtract (expressionS *, expressionS *, segT);
diff --git a/gas/config/tc-bfin.c b/gas/config/tc-bfin.c
new file mode 100644
index 0000000..447a477
--- /dev/null
+++ b/gas/config/tc-bfin.c
@@ -0,0 +1,2733 @@
+/* tc-bfin.c -- Assembler for the ADI Blackfin.
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "struc-symbol.h"
+#include "bfin-defs.h"
+#include "obstack.h"
+#include "safe-ctype.h"
+#ifdef OBJ_ELF
+#include "dwarf2dbg.h"
+#endif
+#include "libbfd.h"
+#include "elf/common.h"
+#include "elf/bfin.h"
+
+extern int yyparse (void);
+struct yy_buffer_state;
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+extern YY_BUFFER_STATE yy_scan_string (const char *yy_str);
+extern void yy_delete_buffer (YY_BUFFER_STATE b);
+static parse_state parse (char *line);
+
+/* Global variables. */
+struct bfin_insn *insn;
+int last_insn_size;
+
+extern struct obstack mempool;
+FILE *errorf;
+
+/* Flags to set in the elf header */
+#define DEFAULT_FLAGS 0
+
+#ifdef OBJ_FDPIC_ELF
+# define DEFAULT_FDPIC EF_BFIN_FDPIC
+#else
+# define DEFAULT_FDPIC 0
+#endif
+
+static flagword bfin_flags = DEFAULT_FLAGS | DEFAULT_FDPIC;
+static const char *bfin_pic_flag = DEFAULT_FDPIC ? "-mfdpic" : (const char *)0;
+
+/* Blackfin specific function to handle FD-PIC pointer initializations. */
+
+static void
+bfin_pic_ptr (int nbytes)
+{
+ expressionS exp;
+ char *p;
+
+ if (nbytes != 4)
+ abort ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ do
+ {
+ bfd_reloc_code_real_type reloc_type = BFD_RELOC_BFIN_FUNCDESC;
+
+ if (strncasecmp (input_line_pointer, "funcdesc(", 9) == 0)
+ {
+ input_line_pointer += 9;
+ expression (&exp);
+ if (*input_line_pointer == ')')
+ input_line_pointer++;
+ else
+ as_bad (_("missing ')'"));
+ }
+ else
+ error ("missing funcdesc in picptr");
+
+ p = frag_more (4);
+ memset (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &exp, 0,
+ reloc_type);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+static void
+bfin_s_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ register int temp;
+
+ temp = get_absolute_expression ();
+ subseg_set (bss_section, (subsegT) temp);
+ demand_empty_rest_of_line ();
+}
+
+const pseudo_typeS md_pseudo_table[] = {
+ {"align", s_align_bytes, 0},
+ {"byte2", cons, 2},
+ {"byte4", cons, 4},
+ {"picptr", bfin_pic_ptr, 4},
+ {"code", obj_elf_section, 0},
+ {"db", cons, 1},
+ {"dd", cons, 4},
+ {"dw", cons, 2},
+ {"p", s_ignore, 0},
+ {"pdata", s_ignore, 0},
+ {"var", s_ignore, 0},
+ {"bss", bfin_s_bss, 0},
+ {0, 0, 0}
+};
+
+/* Characters that are used to denote comments and line separators. */
+const char comment_chars[] = "#";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+
+/* Characters that can be used to separate the mantissa from the
+ exponent in floating point numbers. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters that mean this number is a floating point constant.
+ As in 0f12.456 or 0d1.2345e12. */
+const char FLT_CHARS[] = "fFdDxX";
+
+typedef enum bfin_cpu_type
+{
+ BFIN_CPU_UNKNOWN,
+ BFIN_CPU_BF504,
+ BFIN_CPU_BF506,
+ BFIN_CPU_BF512,
+ BFIN_CPU_BF514,
+ BFIN_CPU_BF516,
+ BFIN_CPU_BF518,
+ BFIN_CPU_BF522,
+ BFIN_CPU_BF523,
+ BFIN_CPU_BF524,
+ BFIN_CPU_BF525,
+ BFIN_CPU_BF526,
+ BFIN_CPU_BF527,
+ BFIN_CPU_BF531,
+ BFIN_CPU_BF532,
+ BFIN_CPU_BF533,
+ BFIN_CPU_BF534,
+ BFIN_CPU_BF536,
+ BFIN_CPU_BF537,
+ BFIN_CPU_BF538,
+ BFIN_CPU_BF539,
+ BFIN_CPU_BF542,
+ BFIN_CPU_BF542M,
+ BFIN_CPU_BF544,
+ BFIN_CPU_BF544M,
+ BFIN_CPU_BF547,
+ BFIN_CPU_BF547M,
+ BFIN_CPU_BF548,
+ BFIN_CPU_BF548M,
+ BFIN_CPU_BF549,
+ BFIN_CPU_BF549M,
+ BFIN_CPU_BF561,
+ BFIN_CPU_BF592,
+} bfin_cpu_t;
+
+bfin_cpu_t bfin_cpu_type = BFIN_CPU_UNKNOWN;
+/* -msi-revision support. There are three special values:
+ -1 -msi-revision=none.
+ 0xffff -msi-revision=any. */
+int bfin_si_revision;
+
+unsigned int bfin_anomaly_checks = 0;
+
+struct bfin_cpu
+{
+ const char *name;
+ bfin_cpu_t type;
+ int si_revision;
+ unsigned int anomaly_checks;
+};
+
+struct bfin_cpu bfin_cpus[] =
+{
+ {"bf504", BFIN_CPU_BF504, 0x0000, AC_05000074},
+
+ {"bf506", BFIN_CPU_BF506, 0x0000, AC_05000074},
+
+ {"bf512", BFIN_CPU_BF512, 0x0002, AC_05000074},
+ {"bf512", BFIN_CPU_BF512, 0x0001, AC_05000074},
+ {"bf512", BFIN_CPU_BF512, 0x0000, AC_05000074},
+
+ {"bf514", BFIN_CPU_BF514, 0x0002, AC_05000074},
+ {"bf514", BFIN_CPU_BF514, 0x0001, AC_05000074},
+ {"bf514", BFIN_CPU_BF514, 0x0000, AC_05000074},
+
+ {"bf516", BFIN_CPU_BF516, 0x0002, AC_05000074},
+ {"bf516", BFIN_CPU_BF516, 0x0001, AC_05000074},
+ {"bf516", BFIN_CPU_BF516, 0x0000, AC_05000074},
+
+ {"bf518", BFIN_CPU_BF518, 0x0002, AC_05000074},
+ {"bf518", BFIN_CPU_BF518, 0x0001, AC_05000074},
+ {"bf518", BFIN_CPU_BF518, 0x0000, AC_05000074},
+
+ {"bf522", BFIN_CPU_BF522, 0x0002, AC_05000074},
+ {"bf522", BFIN_CPU_BF522, 0x0001, AC_05000074},
+ {"bf522", BFIN_CPU_BF522, 0x0000, AC_05000074},
+
+ {"bf523", BFIN_CPU_BF523, 0x0002, AC_05000074},
+ {"bf523", BFIN_CPU_BF523, 0x0001, AC_05000074},
+ {"bf523", BFIN_CPU_BF523, 0x0000, AC_05000074},
+
+ {"bf524", BFIN_CPU_BF524, 0x0002, AC_05000074},
+ {"bf524", BFIN_CPU_BF524, 0x0001, AC_05000074},
+ {"bf524", BFIN_CPU_BF524, 0x0000, AC_05000074},
+
+ {"bf525", BFIN_CPU_BF525, 0x0002, AC_05000074},
+ {"bf525", BFIN_CPU_BF525, 0x0001, AC_05000074},
+ {"bf525", BFIN_CPU_BF525, 0x0000, AC_05000074},
+
+ {"bf526", BFIN_CPU_BF526, 0x0002, AC_05000074},
+ {"bf526", BFIN_CPU_BF526, 0x0001, AC_05000074},
+ {"bf526", BFIN_CPU_BF526, 0x0000, AC_05000074},
+
+ {"bf527", BFIN_CPU_BF527, 0x0002, AC_05000074},
+ {"bf527", BFIN_CPU_BF527, 0x0001, AC_05000074},
+ {"bf527", BFIN_CPU_BF527, 0x0000, AC_05000074},
+
+ {"bf531", BFIN_CPU_BF531, 0x0006, AC_05000074},
+ {"bf531", BFIN_CPU_BF531, 0x0005, AC_05000074},
+ {"bf531", BFIN_CPU_BF531, 0x0004, AC_05000074},
+ {"bf531", BFIN_CPU_BF531, 0x0003, AC_05000074},
+
+ {"bf532", BFIN_CPU_BF532, 0x0006, AC_05000074},
+ {"bf532", BFIN_CPU_BF532, 0x0005, AC_05000074},
+ {"bf532", BFIN_CPU_BF532, 0x0004, AC_05000074},
+ {"bf532", BFIN_CPU_BF532, 0x0003, AC_05000074},
+
+ {"bf533", BFIN_CPU_BF533, 0x0006, AC_05000074},
+ {"bf533", BFIN_CPU_BF533, 0x0005, AC_05000074},
+ {"bf533", BFIN_CPU_BF533, 0x0004, AC_05000074},
+ {"bf533", BFIN_CPU_BF533, 0x0003, AC_05000074},
+
+ {"bf534", BFIN_CPU_BF534, 0x0003, AC_05000074},
+ {"bf534", BFIN_CPU_BF534, 0x0002, AC_05000074},
+ {"bf534", BFIN_CPU_BF534, 0x0001, AC_05000074},
+
+ {"bf536", BFIN_CPU_BF536, 0x0003, AC_05000074},
+ {"bf536", BFIN_CPU_BF536, 0x0002, AC_05000074},
+ {"bf536", BFIN_CPU_BF536, 0x0001, AC_05000074},
+
+ {"bf537", BFIN_CPU_BF537, 0x0003, AC_05000074},
+ {"bf537", BFIN_CPU_BF537, 0x0002, AC_05000074},
+ {"bf537", BFIN_CPU_BF537, 0x0001, AC_05000074},
+
+ {"bf538", BFIN_CPU_BF538, 0x0005, AC_05000074},
+ {"bf538", BFIN_CPU_BF538, 0x0004, AC_05000074},
+ {"bf538", BFIN_CPU_BF538, 0x0003, AC_05000074},
+ {"bf538", BFIN_CPU_BF538, 0x0002, AC_05000074},
+
+ {"bf539", BFIN_CPU_BF539, 0x0005, AC_05000074},
+ {"bf539", BFIN_CPU_BF539, 0x0004, AC_05000074},
+ {"bf539", BFIN_CPU_BF539, 0x0003, AC_05000074},
+ {"bf539", BFIN_CPU_BF539, 0x0002, AC_05000074},
+
+ {"bf542m", BFIN_CPU_BF542M, 0x0003, AC_05000074},
+
+ {"bf542", BFIN_CPU_BF542, 0x0004, AC_05000074},
+ {"bf542", BFIN_CPU_BF542, 0x0002, AC_05000074},
+ {"bf542", BFIN_CPU_BF542, 0x0001, AC_05000074},
+ {"bf542", BFIN_CPU_BF542, 0x0000, AC_05000074},
+
+ {"bf544m", BFIN_CPU_BF544M, 0x0003, AC_05000074},
+
+ {"bf544", BFIN_CPU_BF544, 0x0004, AC_05000074},
+ {"bf544", BFIN_CPU_BF544, 0x0002, AC_05000074},
+ {"bf544", BFIN_CPU_BF544, 0x0001, AC_05000074},
+ {"bf544", BFIN_CPU_BF544, 0x0000, AC_05000074},
+
+ {"bf547m", BFIN_CPU_BF547M, 0x0003, AC_05000074},
+
+ {"bf547", BFIN_CPU_BF547, 0x0004, AC_05000074},
+ {"bf547", BFIN_CPU_BF547, 0x0002, AC_05000074},
+ {"bf547", BFIN_CPU_BF547, 0x0001, AC_05000074},
+ {"bf547", BFIN_CPU_BF547, 0x0000, AC_05000074},
+
+ {"bf548m", BFIN_CPU_BF548M, 0x0003, AC_05000074},
+
+ {"bf548", BFIN_CPU_BF548, 0x0004, AC_05000074},
+ {"bf548", BFIN_CPU_BF548, 0x0002, AC_05000074},
+ {"bf548", BFIN_CPU_BF548, 0x0001, AC_05000074},
+ {"bf548", BFIN_CPU_BF548, 0x0000, AC_05000074},
+
+ {"bf549m", BFIN_CPU_BF549M, 0x0003, AC_05000074},
+
+ {"bf549", BFIN_CPU_BF549, 0x0004, AC_05000074},
+ {"bf549", BFIN_CPU_BF549, 0x0002, AC_05000074},
+ {"bf549", BFIN_CPU_BF549, 0x0001, AC_05000074},
+ {"bf549", BFIN_CPU_BF549, 0x0000, AC_05000074},
+
+ {"bf561", BFIN_CPU_BF561, 0x0005, AC_05000074},
+ {"bf561", BFIN_CPU_BF561, 0x0003, AC_05000074},
+ {"bf561", BFIN_CPU_BF561, 0x0002, AC_05000074},
+
+ {"bf592", BFIN_CPU_BF592, 0x0001, AC_05000074},
+ {"bf592", BFIN_CPU_BF592, 0x0000, AC_05000074},
+
+ {NULL, 0, 0, 0}
+};
+
+/* Define bfin-specific command-line options (there are none). */
+const char *md_shortopts = "";
+
+#define OPTION_FDPIC (OPTION_MD_BASE)
+#define OPTION_NOPIC (OPTION_MD_BASE + 1)
+#define OPTION_MCPU (OPTION_MD_BASE + 2)
+
+struct option md_longopts[] =
+{
+ { "mcpu", required_argument, NULL, OPTION_MCPU },
+ { "mfdpic", no_argument, NULL, OPTION_FDPIC },
+ { "mnopic", no_argument, NULL, OPTION_NOPIC },
+ { "mno-fdpic", no_argument, NULL, OPTION_NOPIC },
+ { NULL, no_argument, NULL, 0 },
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+
+ case OPTION_MCPU:
+ {
+ const char *p, *q;
+ int i;
+
+ i = 0;
+ while ((p = bfin_cpus[i].name) != NULL)
+ {
+ if (strncmp (arg, p, strlen (p)) == 0)
+ break;
+ i++;
+ }
+
+ if (p == NULL)
+ as_fatal ("-mcpu=%s is not valid", arg);
+
+ bfin_cpu_type = bfin_cpus[i].type;
+
+ q = arg + strlen (p);
+
+ if (*q == '\0')
+ {
+ bfin_si_revision = bfin_cpus[i].si_revision;
+ bfin_anomaly_checks |= bfin_cpus[i].anomaly_checks;
+ }
+ else if (strcmp (q, "-none") == 0)
+ bfin_si_revision = -1;
+ else if (strcmp (q, "-any") == 0)
+ {
+ bfin_si_revision = 0xffff;
+ while (bfin_cpus[i].type == bfin_cpu_type)
+ {
+ bfin_anomaly_checks |= bfin_cpus[i].anomaly_checks;
+ i++;
+ }
+ }
+ else
+ {
+ unsigned int si_major, si_minor;
+ int rev_len, n;
+
+ rev_len = strlen (q);
+
+ if (sscanf (q, "-%u.%u%n", &si_major, &si_minor, &n) != 2
+ || n != rev_len
+ || si_major > 0xff || si_minor > 0xff)
+ {
+ invalid_silicon_revision:
+ as_fatal ("-mcpu=%s has invalid silicon revision", arg);
+ }
+
+ bfin_si_revision = (si_major << 8) | si_minor;
+
+ while (bfin_cpus[i].type == bfin_cpu_type
+ && bfin_cpus[i].si_revision != bfin_si_revision)
+ i++;
+
+ if (bfin_cpus[i].type != bfin_cpu_type)
+ goto invalid_silicon_revision;
+
+ bfin_anomaly_checks |= bfin_cpus[i].anomaly_checks;
+ }
+
+ break;
+ }
+
+ case OPTION_FDPIC:
+ bfin_flags |= EF_BFIN_FDPIC;
+ bfin_pic_flag = "-mfdpic";
+ break;
+
+ case OPTION_NOPIC:
+ bfin_flags &= ~(EF_BFIN_FDPIC);
+ bfin_pic_flag = 0;
+ break;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _(" Blackfin specific assembler options:\n"));
+ fprintf (stream, _(" -mcpu=<cpu[-sirevision]> specify the name of the target CPU\n"));
+ fprintf (stream, _(" -mfdpic assemble for the FDPIC ABI\n"));
+ fprintf (stream, _(" -mno-fdpic/-mnopic disable -mfdpic\n"));
+}
+
+/* Perform machine-specific initializations. */
+void
+md_begin ()
+{
+ /* Set the ELF flags if desired. */
+ if (bfin_flags)
+ bfd_set_private_flags (stdoutput, bfin_flags);
+
+ /* Set the default machine type. */
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_bfin, 0))
+ as_warn (_("Could not set architecture and machine."));
+
+ /* Ensure that lines can begin with '(', for multiple
+ register stack pops. */
+ lex_type ['('] = LEX_BEGIN_NAME;
+
+#ifdef OBJ_ELF
+ record_alignment (text_section, 2);
+ record_alignment (data_section, 2);
+ record_alignment (bss_section, 2);
+#endif
+
+ errorf = stderr;
+ obstack_init (&mempool);
+
+#ifdef DEBUG
+ extern int debug_codeselection;
+ debug_codeselection = 1;
+#endif
+
+ last_insn_size = 0;
+}
+
+/* Perform the main parsing, and assembly of the input here. Also,
+ call the required routines for alignment and fixups here.
+ This is called for every line that contains real assembly code. */
+
+void
+md_assemble (char *line)
+{
+ char *toP = 0;
+ extern char *current_inputline;
+ int size, insn_size;
+ struct bfin_insn *tmp_insn;
+ size_t len;
+ static size_t buffer_len = 0;
+ parse_state state;
+
+ len = strlen (line);
+ if (len + 2 > buffer_len)
+ {
+ if (buffer_len > 0)
+ free (current_inputline);
+ buffer_len = len + 40;
+ current_inputline = xmalloc (buffer_len);
+ }
+ memcpy (current_inputline, line, len);
+ current_inputline[len] = ';';
+ current_inputline[len + 1] = '\0';
+
+ state = parse (current_inputline);
+ if (state == NO_INSN_GENERATED)
+ return;
+
+ for (insn_size = 0, tmp_insn = insn; tmp_insn; tmp_insn = tmp_insn->next)
+ if (!tmp_insn->reloc || !tmp_insn->exp->symbol)
+ insn_size += 2;
+
+ if (insn_size)
+ toP = frag_more (insn_size);
+
+ last_insn_size = insn_size;
+
+#ifdef DEBUG
+ printf ("INS:");
+#endif
+ while (insn)
+ {
+ if (insn->reloc && insn->exp->symbol)
+ {
+ char *prev_toP = toP - 2;
+ switch (insn->reloc)
+ {
+ case BFD_RELOC_BFIN_24_PCREL_JUMP_L:
+ case BFD_RELOC_24_PCREL:
+ case BFD_RELOC_BFIN_16_LOW:
+ case BFD_RELOC_BFIN_16_HIGH:
+ size = 4;
+ break;
+ default:
+ size = 2;
+ }
+
+ /* Following if condition checks for the arithmetic relocations.
+ If the case then it doesn't required to generate the code.
+ It has been assumed that, their ID will be contiguous. */
+ if ((BFD_ARELOC_BFIN_PUSH <= insn->reloc
+ && BFD_ARELOC_BFIN_COMP >= insn->reloc)
+ || insn->reloc == BFD_RELOC_BFIN_16_IMM)
+ {
+ size = 2;
+ }
+ if (insn->reloc == BFD_ARELOC_BFIN_CONST
+ || insn->reloc == BFD_ARELOC_BFIN_PUSH)
+ size = 4;
+
+ fix_new (frag_now,
+ (prev_toP - frag_now->fr_literal),
+ size, insn->exp->symbol, insn->exp->value,
+ insn->pcrel, insn->reloc);
+ }
+ else
+ {
+ md_number_to_chars (toP, insn->value, 2);
+ toP += 2;
+ }
+
+#ifdef DEBUG
+ printf (" reloc :");
+ printf (" %02x%02x", ((unsigned char *) &insn->value)[0],
+ ((unsigned char *) &insn->value)[1]);
+ printf ("\n");
+#endif
+ insn = insn->next;
+ }
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (insn_size);
+#endif
+
+ while (*line++ != '\0')
+ if (*line == '\n')
+ bump_line_counters ();
+}
+
+/* Parse one line of instructions, and generate opcode for it.
+ To parse the line, YACC and LEX are used, because the instruction set
+ syntax doesn't confirm to the AT&T assembly syntax.
+ To call a YACC & LEX generated parser, we must provide the input via
+ a FILE stream, otherwise stdin is used by default. Below the input
+ to the function will be put into a temporary file, then the generated
+ parser uses the temporary file for parsing. */
+
+static parse_state
+parse (char *line)
+{
+ parse_state state;
+ YY_BUFFER_STATE buffstate;
+
+ buffstate = yy_scan_string (line);
+
+ /* our lex requires setting the start state to keyword
+ every line as the first word may be a keyword.
+ Fixes a bug where we could not have keywords as labels. */
+ set_start_state ();
+
+ /* Call yyparse here. */
+ state = yyparse ();
+ if (state == SEMANTIC_ERROR)
+ {
+ as_bad (_("Parse failed."));
+ insn = 0;
+ }
+
+ yy_delete_buffer (buffstate);
+ return state;
+}
+
+/* We need to handle various expressions properly.
+ Such as, [SP--] = 34, concerned by md_assemble(). */
+
+void
+md_operand (expressionS * expressionP)
+{
+ if (*input_line_pointer == '[')
+ {
+ as_tsktsk ("We found a '['!");
+ input_line_pointer++;
+ expression (expressionP);
+ }
+}
+
+/* Handle undefined symbols. */
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return (symbolS *) 0;
+}
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Convert from target byte order to host byte order. */
+
+static int
+md_chars_to_number (char *val, int n)
+{
+ int retval;
+
+ for (retval = 0; n--;)
+ {
+ retval <<= 8;
+ retval |= val[n];
+ }
+ return retval;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valueP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ long value = *valueP;
+ long newval;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_BFIN_GOT:
+ case BFD_RELOC_BFIN_GOT17M4:
+ case BFD_RELOC_BFIN_FUNCDESC_GOT17M4:
+ fixP->fx_no_overflow = 1;
+ newval = md_chars_to_number (where, 2);
+ newval |= 0x0 & 0x7f;
+ md_number_to_chars (where, newval, 2);
+ break;
+
+ case BFD_RELOC_BFIN_10_PCREL:
+ if (!value)
+ break;
+ if (value < -1024 || value > 1022)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pcrel too far BFD_RELOC_BFIN_10"));
+
+ /* 11 bit offset even numbered, so we remove right bit. */
+ value = value >> 1;
+ newval = md_chars_to_number (where, 2);
+ newval |= value & 0x03ff;
+ md_number_to_chars (where, newval, 2);
+ break;
+
+ case BFD_RELOC_BFIN_12_PCREL_JUMP:
+ case BFD_RELOC_BFIN_12_PCREL_JUMP_S:
+ case BFD_RELOC_12_PCREL:
+ if (!value)
+ break;
+
+ if (value < -4096 || value > 4094)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far BFD_RELOC_BFIN_12"));
+ /* 13 bit offset even numbered, so we remove right bit. */
+ value = value >> 1;
+ newval = md_chars_to_number (where, 2);
+ newval |= value & 0xfff;
+ md_number_to_chars (where, newval, 2);
+ break;
+
+ case BFD_RELOC_BFIN_16_LOW:
+ case BFD_RELOC_BFIN_16_HIGH:
+ fixP->fx_done = FALSE;
+ break;
+
+ case BFD_RELOC_BFIN_24_PCREL_JUMP_L:
+ case BFD_RELOC_BFIN_24_PCREL_CALL_X:
+ case BFD_RELOC_24_PCREL:
+ if (!value)
+ break;
+
+ if (value < -16777216 || value > 16777214)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far BFD_RELOC_BFIN_24"));
+
+ /* 25 bit offset even numbered, so we remove right bit. */
+ value = value >> 1;
+ value++;
+
+ md_number_to_chars (where - 2, value >> 16, 1);
+ md_number_to_chars (where, value, 1);
+ md_number_to_chars (where + 1, value >> 8, 1);
+ break;
+
+ case BFD_RELOC_BFIN_5_PCREL: /* LSETUP (a, b) : "a" */
+ if (!value)
+ break;
+ if (value < 4 || value > 30)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far BFD_RELOC_BFIN_5"));
+ value = value >> 1;
+ newval = md_chars_to_number (where, 1);
+ newval = (newval & 0xf0) | (value & 0xf);
+ md_number_to_chars (where, newval, 1);
+ break;
+
+ case BFD_RELOC_BFIN_11_PCREL: /* LSETUP (a, b) : "b" */
+ if (!value)
+ break;
+ value += 2;
+ if (value < 4 || value > 2046)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far BFD_RELOC_BFIN_11_PCREL"));
+ /* 11 bit unsigned even, so we remove right bit. */
+ value = value >> 1;
+ newval = md_chars_to_number (where, 2);
+ newval |= value & 0x03ff;
+ md_number_to_chars (where, newval, 2);
+ break;
+
+ case BFD_RELOC_8:
+ if (value < -0x80 || value >= 0x7f)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("rel too far BFD_RELOC_8"));
+ md_number_to_chars (where, value, 1);
+ break;
+
+ case BFD_RELOC_BFIN_16_IMM:
+ case BFD_RELOC_16:
+ if (value < -0x8000 || value >= 0x7fff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("rel too far BFD_RELOC_16"));
+ md_number_to_chars (where, value, 2);
+ break;
+
+ case BFD_RELOC_32:
+ md_number_to_chars (where, value, 4);
+ break;
+
+ case BFD_RELOC_BFIN_PLTPC:
+ md_number_to_chars (where, value, 2);
+ break;
+
+ case BFD_RELOC_BFIN_FUNCDESC:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = FALSE;
+ break;
+
+ default:
+ if ((BFD_ARELOC_BFIN_PUSH > fixP->fx_r_type) || (BFD_ARELOC_BFIN_COMP < fixP->fx_r_type))
+ {
+ fprintf (stderr, "Relocation %d not handled in gas." " Contact support.\n", fixP->fx_r_type);
+ return;
+ }
+ }
+
+ if (!fixP->fx_addsy)
+ fixP->fx_done = TRUE;
+
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segment, size)
+ segT segment;
+ valueT size;
+{
+ int boundary = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << boundary) - 1) & (-1 << boundary));
+}
+
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+
+/* If while processing a fixup, a reloc really needs to be created
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg ATTRIBUTE_UNUSED;
+ fixS *fixp;
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc->addend = fixp->fx_offset;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ /* xgettext:c-format. */
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+
+ xfree (reloc);
+
+ return NULL;
+ }
+
+ return reloc;
+}
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from_section (fixP, sec)
+ 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 true if the fix can be handled by GAS, false if it must
+ be passed through to the linker. */
+
+bfd_boolean
+bfin_fix_adjustable (fixS *fixP)
+{
+ switch (fixP->fx_r_type)
+ {
+ /* Adjust_reloc_syms doesn't know about the GOT. */
+ case BFD_RELOC_BFIN_GOT:
+ case BFD_RELOC_BFIN_PLTPC:
+ /* We need the symbol name for the VTABLE entries. */
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+/* Special extra functions that help bfin-parse.y perform its job. */
+
+struct obstack mempool;
+
+INSTR_T
+conscode (INSTR_T head, INSTR_T tail)
+{
+ if (!head)
+ return tail;
+ head->next = tail;
+ return head;
+}
+
+INSTR_T
+conctcode (INSTR_T head, INSTR_T tail)
+{
+ INSTR_T temp = (head);
+ if (!head)
+ return tail;
+ while (temp->next)
+ temp = temp->next;
+ temp->next = tail;
+
+ return head;
+}
+
+INSTR_T
+note_reloc (INSTR_T code, Expr_Node * symbol, int reloc, int pcrel)
+{
+ /* Assert that the symbol is not an operator. */
+ gas_assert (symbol->type == Expr_Node_Reloc);
+
+ return note_reloc1 (code, symbol->value.s_value, reloc, pcrel);
+
+}
+
+INSTR_T
+note_reloc1 (INSTR_T code, const char *symbol, int reloc, int pcrel)
+{
+ code->reloc = reloc;
+ code->exp = mkexpr (0, symbol_find_or_make (symbol));
+ code->pcrel = pcrel;
+ return code;
+}
+
+INSTR_T
+note_reloc2 (INSTR_T code, const char *symbol, int reloc, int value, int pcrel)
+{
+ code->reloc = reloc;
+ code->exp = mkexpr (value, symbol_find_or_make (symbol));
+ code->pcrel = pcrel;
+ return code;
+}
+
+INSTR_T
+gencode (unsigned long x)
+{
+ INSTR_T cell = obstack_alloc (&mempool, sizeof (struct bfin_insn));
+ memset (cell, 0, sizeof (struct bfin_insn));
+ cell->value = (x);
+ return cell;
+}
+
+int reloc;
+int ninsns;
+int count_insns;
+
+static void *
+allocate (size_t n)
+{
+ return obstack_alloc (&mempool, n);
+}
+
+Expr_Node *
+Expr_Node_Create (Expr_Node_Type type,
+ Expr_Node_Value value,
+ Expr_Node *Left_Child,
+ Expr_Node *Right_Child)
+{
+
+
+ Expr_Node *node = (Expr_Node *) allocate (sizeof (Expr_Node));
+ node->type = type;
+ node->value = value;
+ node->Left_Child = Left_Child;
+ node->Right_Child = Right_Child;
+ return node;
+}
+
+static const char *con = ".__constant";
+static const char *op = ".__operator";
+static INSTR_T Expr_Node_Gen_Reloc_R (Expr_Node * head);
+INSTR_T Expr_Node_Gen_Reloc (Expr_Node *head, int parent_reloc);
+
+INSTR_T
+Expr_Node_Gen_Reloc (Expr_Node * head, int parent_reloc)
+{
+ /* Top level reloction expression generator VDSP style.
+ If the relocation is just by itself, generate one item
+ else generate this convoluted expression. */
+
+ INSTR_T note = NULL_CODE;
+ INSTR_T note1 = NULL_CODE;
+ int pcrel = 1; /* Is the parent reloc pcrelative?
+ This calculation here and HOWTO should match. */
+
+ if (parent_reloc)
+ {
+ /* If it's 32 bit quantity then 16bit code needs to be added. */
+ int value = 0;
+
+ if (head->type == Expr_Node_Constant)
+ {
+ /* If note1 is not null code, we have to generate a right
+ aligned value for the constant. Otherwise the reloc is
+ a part of the basic command and the yacc file
+ generates this. */
+ value = head->value.i_value;
+ }
+ switch (parent_reloc)
+ {
+ /* Some relocations will need to allocate extra words. */
+ case BFD_RELOC_BFIN_16_IMM:
+ case BFD_RELOC_BFIN_16_LOW:
+ case BFD_RELOC_BFIN_16_HIGH:
+ note1 = conscode (gencode (value), NULL_CODE);
+ pcrel = 0;
+ break;
+ case BFD_RELOC_BFIN_PLTPC:
+ note1 = conscode (gencode (value), NULL_CODE);
+ pcrel = 0;
+ break;
+ case BFD_RELOC_16:
+ case BFD_RELOC_BFIN_GOT:
+ case BFD_RELOC_BFIN_GOT17M4:
+ case BFD_RELOC_BFIN_FUNCDESC_GOT17M4:
+ note1 = conscode (gencode (value), NULL_CODE);
+ pcrel = 0;
+ break;
+ case BFD_RELOC_24_PCREL:
+ case BFD_RELOC_BFIN_24_PCREL_JUMP_L:
+ case BFD_RELOC_BFIN_24_PCREL_CALL_X:
+ /* These offsets are even numbered pcrel. */
+ note1 = conscode (gencode (value >> 1), NULL_CODE);
+ break;
+ default:
+ note1 = NULL_CODE;
+ }
+ }
+ if (head->type == Expr_Node_Constant)
+ note = note1;
+ else if (head->type == Expr_Node_Reloc)
+ {
+ note = note_reloc1 (gencode (0), head->value.s_value, parent_reloc, pcrel);
+ if (note1 != NULL_CODE)
+ note = conscode (note1, note);
+ }
+ else if (head->type == Expr_Node_Binop
+ && (head->value.op_value == Expr_Op_Type_Add
+ || head->value.op_value == Expr_Op_Type_Sub)
+ && head->Left_Child->type == Expr_Node_Reloc
+ && head->Right_Child->type == Expr_Node_Constant)
+ {
+ int val = head->Right_Child->value.i_value;
+ if (head->value.op_value == Expr_Op_Type_Sub)
+ val = -val;
+ note = conscode (note_reloc2 (gencode (0), head->Left_Child->value.s_value,
+ parent_reloc, val, 0),
+ NULL_CODE);
+ if (note1 != NULL_CODE)
+ note = conscode (note1, note);
+ }
+ else
+ {
+ /* Call the recursive function. */
+ note = note_reloc1 (gencode (0), op, parent_reloc, pcrel);
+ if (note1 != NULL_CODE)
+ note = conscode (note1, note);
+ note = conctcode (Expr_Node_Gen_Reloc_R (head), note);
+ }
+ return note;
+}
+
+static INSTR_T
+Expr_Node_Gen_Reloc_R (Expr_Node * head)
+{
+
+ INSTR_T note = 0;
+ INSTR_T note1 = 0;
+
+ switch (head->type)
+ {
+ case Expr_Node_Constant:
+ note = conscode (note_reloc2 (gencode (0), con, BFD_ARELOC_BFIN_CONST, head->value.i_value, 0), NULL_CODE);
+ break;
+ case Expr_Node_Reloc:
+ note = conscode (note_reloc (gencode (0), head, BFD_ARELOC_BFIN_PUSH, 0), NULL_CODE);
+ break;
+ case Expr_Node_Binop:
+ note1 = conctcode (Expr_Node_Gen_Reloc_R (head->Left_Child), Expr_Node_Gen_Reloc_R (head->Right_Child));
+ switch (head->value.op_value)
+ {
+ case Expr_Op_Type_Add:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_ADD, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_Sub:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_SUB, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_Mult:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_MULT, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_Div:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_DIV, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_Mod:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_MOD, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_Lshift:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_LSHIFT, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_Rshift:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_RSHIFT, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_BAND:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_AND, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_BOR:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_OR, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_BXOR:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_XOR, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_LAND:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_LAND, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_LOR:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_LOR, 0), NULL_CODE));
+ break;
+ default:
+ fprintf (stderr, "%s:%d:Unknown operator found for arithmetic" " relocation", __FILE__, __LINE__);
+
+
+ }
+ break;
+ case Expr_Node_Unop:
+ note1 = conscode (Expr_Node_Gen_Reloc_R (head->Left_Child), NULL_CODE);
+ switch (head->value.op_value)
+ {
+ case Expr_Op_Type_NEG:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_NEG, 0), NULL_CODE));
+ break;
+ case Expr_Op_Type_COMP:
+ note = conctcode (note1, conscode (note_reloc1 (gencode (0), op, BFD_ARELOC_BFIN_COMP, 0), NULL_CODE));
+ break;
+ default:
+ fprintf (stderr, "%s:%d:Unknown operator found for arithmetic" " relocation", __FILE__, __LINE__);
+ }
+ break;
+ default:
+ fprintf (stderr, "%s:%d:Unknown node expression found during " "arithmetic relocation generation", __FILE__, __LINE__);
+ }
+ return note;
+}
+
+/* Blackfin opcode generation. */
+
+/* These functions are called by the generated parser
+ (from bfin-parse.y), the register type classification
+ happens in bfin-lex.l. */
+
+#include "bfin-aux.h"
+#include "opcode/bfin.h"
+
+#define INIT(t) t c_code = init_##t
+#define ASSIGN(x) c_code.opcode |= ((x & c_code.mask_##x)<<c_code.bits_##x)
+#define ASSIGNF(x,f) c_code.opcode |= ((x & c_code.mask_##f)<<c_code.bits_##f)
+#define ASSIGN_R(x) c_code.opcode |= (((x ? (x->regno & CODE_MASK) : 0) & c_code.mask_##x)<<c_code.bits_##x)
+
+#define HI(x) ((x >> 16) & 0xffff)
+#define LO(x) ((x ) & 0xffff)
+
+#define GROUP(x) ((x->regno & CLASS_MASK) >> 4)
+
+#define GEN_OPCODE32() \
+ conscode (gencode (HI (c_code.opcode)), \
+ conscode (gencode (LO (c_code.opcode)), NULL_CODE))
+
+#define GEN_OPCODE16() \
+ conscode (gencode (c_code.opcode), NULL_CODE)
+
+
+/* 32 BIT INSTRUCTIONS. */
+
+
+/* DSP32 instruction generation. */
+
+INSTR_T
+bfin_gen_dsp32mac (int op1, int MM, int mmod, int w1, int P,
+ int h01, int h11, int h00, int h10, int op0,
+ REG_T dst, REG_T src0, REG_T src1, int w0)
+{
+ INIT (DSP32Mac);
+
+ ASSIGN (op0);
+ ASSIGN (op1);
+ ASSIGN (MM);
+ ASSIGN (mmod);
+ ASSIGN (w0);
+ ASSIGN (w1);
+ ASSIGN (h01);
+ ASSIGN (h11);
+ ASSIGN (h00);
+ ASSIGN (h10);
+ ASSIGN (P);
+
+ /* If we have full reg assignments, mask out LSB to encode
+ single or simultaneous even/odd register moves. */
+ if (P)
+ {
+ dst->regno &= 0x06;
+ }
+
+ ASSIGN_R (dst);
+ ASSIGN_R (src0);
+ ASSIGN_R (src1);
+
+ return GEN_OPCODE32 ();
+}
+
+INSTR_T
+bfin_gen_dsp32mult (int op1, int MM, int mmod, int w1, int P,
+ int h01, int h11, int h00, int h10, int op0,
+ REG_T dst, REG_T src0, REG_T src1, int w0)
+{
+ INIT (DSP32Mult);
+
+ ASSIGN (op0);
+ ASSIGN (op1);
+ ASSIGN (MM);
+ ASSIGN (mmod);
+ ASSIGN (w0);
+ ASSIGN (w1);
+ ASSIGN (h01);
+ ASSIGN (h11);
+ ASSIGN (h00);
+ ASSIGN (h10);
+ ASSIGN (P);
+
+ if (P)
+ {
+ dst->regno &= 0x06;
+ }
+
+ ASSIGN_R (dst);
+ ASSIGN_R (src0);
+ ASSIGN_R (src1);
+
+ return GEN_OPCODE32 ();
+}
+
+INSTR_T
+bfin_gen_dsp32alu (int HL, int aopcde, int aop, int s, int x,
+ REG_T dst0, REG_T dst1, REG_T src0, REG_T src1)
+{
+ INIT (DSP32Alu);
+
+ ASSIGN (HL);
+ ASSIGN (aopcde);
+ ASSIGN (aop);
+ ASSIGN (s);
+ ASSIGN (x);
+ ASSIGN_R (dst0);
+ ASSIGN_R (dst1);
+ ASSIGN_R (src0);
+ ASSIGN_R (src1);
+
+ return GEN_OPCODE32 ();
+}
+
+INSTR_T
+bfin_gen_dsp32shift (int sopcde, REG_T dst0, REG_T src0,
+ REG_T src1, int sop, int HLs)
+{
+ INIT (DSP32Shift);
+
+ ASSIGN (sopcde);
+ ASSIGN (sop);
+ ASSIGN (HLs);
+
+ ASSIGN_R (dst0);
+ ASSIGN_R (src0);
+ ASSIGN_R (src1);
+
+ return GEN_OPCODE32 ();
+}
+
+INSTR_T
+bfin_gen_dsp32shiftimm (int sopcde, REG_T dst0, int immag,
+ REG_T src1, int sop, int HLs)
+{
+ INIT (DSP32ShiftImm);
+
+ ASSIGN (sopcde);
+ ASSIGN (sop);
+ ASSIGN (HLs);
+
+ ASSIGN_R (dst0);
+ ASSIGN (immag);
+ ASSIGN_R (src1);
+
+ return GEN_OPCODE32 ();
+}
+
+/* LOOP SETUP. */
+
+INSTR_T
+bfin_gen_loopsetup (Expr_Node * psoffset, REG_T c, int rop,
+ Expr_Node * peoffset, REG_T reg)
+{
+ int soffset, eoffset;
+ INIT (LoopSetup);
+
+ soffset = (EXPR_VALUE (psoffset) >> 1);
+ ASSIGN (soffset);
+ eoffset = (EXPR_VALUE (peoffset) >> 1);
+ ASSIGN (eoffset);
+ ASSIGN (rop);
+ ASSIGN_R (c);
+ ASSIGN_R (reg);
+
+ return
+ conscode (gencode (HI (c_code.opcode)),
+ conctcode (Expr_Node_Gen_Reloc (psoffset, BFD_RELOC_BFIN_5_PCREL),
+ conctcode (gencode (LO (c_code.opcode)), Expr_Node_Gen_Reloc (peoffset, BFD_RELOC_BFIN_11_PCREL))));
+
+}
+
+/* Call, Link. */
+
+INSTR_T
+bfin_gen_calla (Expr_Node * addr, int S)
+{
+ int val;
+ int high_val;
+ int rel = 0;
+ INIT (CALLa);
+
+ switch(S){
+ case 0 : rel = BFD_RELOC_BFIN_24_PCREL_JUMP_L; break;
+ case 1 : rel = BFD_RELOC_24_PCREL; break;
+ case 2 : rel = BFD_RELOC_BFIN_PLTPC; break;
+ default : break;
+ }
+
+ ASSIGN (S);
+
+ val = EXPR_VALUE (addr) >> 1;
+ high_val = val >> 16;
+
+ return conscode (gencode (HI (c_code.opcode) | (high_val & 0xff)),
+ Expr_Node_Gen_Reloc (addr, rel));
+ }
+
+INSTR_T
+bfin_gen_linkage (int R, int framesize)
+{
+ INIT (Linkage);
+
+ ASSIGN (R);
+ ASSIGN (framesize);
+
+ return GEN_OPCODE32 ();
+}
+
+
+/* Load and Store. */
+
+INSTR_T
+bfin_gen_ldimmhalf (REG_T reg, int H, int S, int Z, Expr_Node * phword, int rel)
+{
+ int grp, hword;
+ unsigned val = EXPR_VALUE (phword);
+ INIT (LDIMMhalf);
+
+ ASSIGN (H);
+ ASSIGN (S);
+ ASSIGN (Z);
+
+ ASSIGN_R (reg);
+ grp = (GROUP (reg));
+ ASSIGN (grp);
+ if (rel == 2)
+ {
+ return conscode (gencode (HI (c_code.opcode)), Expr_Node_Gen_Reloc (phword, BFD_RELOC_BFIN_16_IMM));
+ }
+ else if (rel == 1)
+ {
+ return conscode (gencode (HI (c_code.opcode)), Expr_Node_Gen_Reloc (phword, IS_H (*reg) ? BFD_RELOC_BFIN_16_HIGH : BFD_RELOC_BFIN_16_LOW));
+ }
+ else
+ {
+ hword = val;
+ ASSIGN (hword);
+ }
+ return GEN_OPCODE32 ();
+}
+
+INSTR_T
+bfin_gen_ldstidxi (REG_T ptr, REG_T reg, int W, int sz, int Z, Expr_Node * poffset)
+{
+ INIT (LDSTidxI);
+
+ if (!IS_PREG (*ptr) || (!IS_DREG (*reg) && !Z))
+ {
+ fprintf (stderr, "Warning: possible mixup of Preg/Dreg\n");
+ return 0;
+ }
+
+ ASSIGN_R (ptr);
+ ASSIGN_R (reg);
+ ASSIGN (W);
+ ASSIGN (sz);
+
+ ASSIGN (Z);
+
+ if (poffset->type != Expr_Node_Constant)
+ {
+ /* a GOT relocation such as R0 = [P5 + symbol@GOT] */
+ /* distinguish between R0 = [P5 + symbol@GOT] and
+ P5 = [P5 + _current_shared_library_p5_offset_]
+ */
+ if (poffset->type == Expr_Node_Reloc
+ && !strcmp (poffset->value.s_value,
+ "_current_shared_library_p5_offset_"))
+ {
+ return conscode (gencode (HI (c_code.opcode)),
+ Expr_Node_Gen_Reloc(poffset, BFD_RELOC_16));
+ }
+ else if (poffset->type != Expr_Node_GOT_Reloc)
+ abort ();
+
+ return conscode (gencode (HI (c_code.opcode)),
+ Expr_Node_Gen_Reloc(poffset->Left_Child,
+ poffset->value.i_value));
+ }
+ else
+ {
+ int value, offset;
+ switch (sz)
+ { /* load/store access size */
+ case 0: /* 32 bit */
+ value = EXPR_VALUE (poffset) >> 2;
+ break;
+ case 1: /* 16 bit */
+ value = EXPR_VALUE (poffset) >> 1;
+ break;
+ case 2: /* 8 bit */
+ value = EXPR_VALUE (poffset);
+ break;
+ default:
+ abort ();
+ }
+
+ offset = (value & 0xffff);
+ ASSIGN (offset);
+ return GEN_OPCODE32 ();
+ }
+}
+
+
+INSTR_T
+bfin_gen_ldst (REG_T ptr, REG_T reg, int aop, int sz, int Z, int W)
+{
+ INIT (LDST);
+
+ if (!IS_PREG (*ptr) || (!IS_DREG (*reg) && !Z))
+ {
+ fprintf (stderr, "Warning: possible mixup of Preg/Dreg\n");
+ return 0;
+ }
+
+ ASSIGN_R (ptr);
+ ASSIGN_R (reg);
+ ASSIGN (aop);
+ ASSIGN (sz);
+ ASSIGN (Z);
+ ASSIGN (W);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_ldstii (REG_T ptr, REG_T reg, Expr_Node * poffset, int W, int opc)
+{
+ int offset;
+ int value = 0;
+ INIT (LDSTii);
+
+ if (!IS_PREG (*ptr))
+ {
+ fprintf (stderr, "Warning: possible mixup of Preg/Dreg\n");
+ return 0;
+ }
+
+ switch (opc)
+ {
+ case 1:
+ case 2:
+ value = EXPR_VALUE (poffset) >> 1;
+ break;
+ case 0:
+ case 3:
+ value = EXPR_VALUE (poffset) >> 2;
+ break;
+ }
+
+ ASSIGN_R (ptr);
+ ASSIGN_R (reg);
+
+ offset = value;
+ ASSIGN (offset);
+ ASSIGN (W);
+ ASSIGNF (opc, op);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_ldstiifp (REG_T sreg, Expr_Node * poffset, int W)
+{
+ /* Set bit 4 if it's a Preg. */
+ int reg = (sreg->regno & CODE_MASK) | (IS_PREG (*sreg) ? 0x8 : 0x0);
+ int offset = ((~(EXPR_VALUE (poffset) >> 2)) & 0x1f) + 1;
+ INIT (LDSTiiFP);
+ ASSIGN (reg);
+ ASSIGN (offset);
+ ASSIGN (W);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_ldstpmod (REG_T ptr, REG_T reg, int aop, int W, REG_T idx)
+{
+ INIT (LDSTpmod);
+
+ ASSIGN_R (ptr);
+ ASSIGN_R (reg);
+ ASSIGN (aop);
+ ASSIGN (W);
+ ASSIGN_R (idx);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_dspldst (REG_T i, REG_T reg, int aop, int W, int m)
+{
+ INIT (DspLDST);
+
+ ASSIGN_R (i);
+ ASSIGN_R (reg);
+ ASSIGN (aop);
+ ASSIGN (W);
+ ASSIGN (m);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_logi2op (int opc, int src, int dst)
+{
+ INIT (LOGI2op);
+
+ ASSIGN (opc);
+ ASSIGN (src);
+ ASSIGN (dst);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_brcc (int T, int B, Expr_Node * poffset)
+{
+ int offset;
+ INIT (BRCC);
+
+ ASSIGN (T);
+ ASSIGN (B);
+ offset = ((EXPR_VALUE (poffset) >> 1));
+ ASSIGN (offset);
+ return conscode (gencode (c_code.opcode), Expr_Node_Gen_Reloc (poffset, BFD_RELOC_BFIN_10_PCREL));
+}
+
+INSTR_T
+bfin_gen_ujump (Expr_Node * poffset)
+{
+ int offset;
+ INIT (UJump);
+
+ offset = ((EXPR_VALUE (poffset) >> 1));
+ ASSIGN (offset);
+
+ return conscode (gencode (c_code.opcode),
+ Expr_Node_Gen_Reloc (
+ poffset, BFD_RELOC_BFIN_12_PCREL_JUMP_S));
+}
+
+INSTR_T
+bfin_gen_alu2op (REG_T dst, REG_T src, int opc)
+{
+ INIT (ALU2op);
+
+ ASSIGN_R (dst);
+ ASSIGN_R (src);
+ ASSIGN (opc);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_compi2opd (REG_T dst, int src, int opc)
+{
+ INIT (COMPI2opD);
+
+ ASSIGN_R (dst);
+ ASSIGN (src);
+ ASSIGNF (opc, op);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_compi2opp (REG_T dst, int src, int opc)
+{
+ INIT (COMPI2opP);
+
+ ASSIGN_R (dst);
+ ASSIGN (src);
+ ASSIGNF (opc, op);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_dagmodik (REG_T i, int opc)
+{
+ INIT (DagMODik);
+
+ ASSIGN_R (i);
+ ASSIGNF (opc, op);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_dagmodim (REG_T i, REG_T m, int opc, int br)
+{
+ INIT (DagMODim);
+
+ ASSIGN_R (i);
+ ASSIGN_R (m);
+ ASSIGNF (opc, op);
+ ASSIGN (br);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_ptr2op (REG_T dst, REG_T src, int opc)
+{
+ INIT (PTR2op);
+
+ ASSIGN_R (dst);
+ ASSIGN_R (src);
+ ASSIGN (opc);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_comp3op (REG_T src0, REG_T src1, REG_T dst, int opc)
+{
+ INIT (COMP3op);
+
+ ASSIGN_R (src0);
+ ASSIGN_R (src1);
+ ASSIGN_R (dst);
+ ASSIGN (opc);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_ccflag (REG_T x, int y, int opc, int I, int G)
+{
+ INIT (CCflag);
+
+ ASSIGN_R (x);
+ ASSIGN (y);
+ ASSIGN (opc);
+ ASSIGN (I);
+ ASSIGN (G);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_ccmv (REG_T src, REG_T dst, int T)
+{
+ int s, d;
+ INIT (CCmv);
+
+ ASSIGN_R (src);
+ ASSIGN_R (dst);
+ s = (GROUP (src));
+ ASSIGN (s);
+ d = (GROUP (dst));
+ ASSIGN (d);
+ ASSIGN (T);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_cc2stat (int cbit, int opc, int D)
+{
+ INIT (CC2stat);
+
+ ASSIGN (cbit);
+ ASSIGNF (opc, op);
+ ASSIGN (D);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_regmv (REG_T src, REG_T dst)
+{
+ int gs, gd;
+ INIT (RegMv);
+
+ ASSIGN_R (src);
+ ASSIGN_R (dst);
+
+ gs = (GROUP (src));
+ ASSIGN (gs);
+ gd = (GROUP (dst));
+ ASSIGN (gd);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_cc2dreg (int opc, REG_T reg)
+{
+ INIT (CC2dreg);
+
+ ASSIGNF (opc, op);
+ ASSIGN_R (reg);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_progctrl (int prgfunc, int poprnd)
+{
+ INIT (ProgCtrl);
+
+ ASSIGN (prgfunc);
+ ASSIGN (poprnd);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_cactrl (REG_T reg, int a, int opc)
+{
+ INIT (CaCTRL);
+
+ ASSIGN_R (reg);
+ ASSIGN (a);
+ ASSIGNF (opc, op);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_pushpopmultiple (int dr, int pr, int d, int p, int W)
+{
+ INIT (PushPopMultiple);
+
+ ASSIGN (dr);
+ ASSIGN (pr);
+ ASSIGN (d);
+ ASSIGN (p);
+ ASSIGN (W);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_pushpopreg (REG_T reg, int W)
+{
+ int grp;
+ INIT (PushPopReg);
+
+ ASSIGN_R (reg);
+ grp = (GROUP (reg));
+ ASSIGN (grp);
+ ASSIGN (W);
+
+ return GEN_OPCODE16 ();
+}
+
+/* Pseudo Debugging Support. */
+
+INSTR_T
+bfin_gen_pseudodbg (int fn, int reg, int grp)
+{
+ INIT (PseudoDbg);
+
+ ASSIGN (fn);
+ ASSIGN (reg);
+ ASSIGN (grp);
+
+ return GEN_OPCODE16 ();
+}
+
+INSTR_T
+bfin_gen_pseudodbg_assert (int dbgop, REG_T regtest, int expected)
+{
+ int grp;
+ INIT (PseudoDbg_Assert);
+
+ ASSIGN (dbgop);
+ ASSIGN_R (regtest);
+ grp = GROUP (regtest);
+ ASSIGN (grp);
+ ASSIGN (expected);
+
+ return GEN_OPCODE32 ();
+}
+
+INSTR_T
+bfin_gen_pseudochr (int ch)
+{
+ INIT (PseudoChr);
+
+ ASSIGN (ch);
+
+ return GEN_OPCODE16 ();
+}
+
+/* Multiple instruction generation. */
+
+INSTR_T
+bfin_gen_multi_instr (INSTR_T dsp32, INSTR_T dsp16_grp1, INSTR_T dsp16_grp2)
+{
+ INSTR_T walk;
+
+ /* If it's a 0, convert into MNOP. */
+ if (dsp32)
+ {
+ walk = dsp32->next;
+ SET_MULTI_INSTRUCTION_BIT (dsp32);
+ }
+ else
+ {
+ dsp32 = gencode (0xc803);
+ walk = gencode (0x1800);
+ dsp32->next = walk;
+ }
+
+ if (!dsp16_grp1)
+ {
+ dsp16_grp1 = gencode (0x0000);
+ }
+
+ if (!dsp16_grp2)
+ {
+ dsp16_grp2 = gencode (0x0000);
+ }
+
+ walk->next = dsp16_grp1;
+ dsp16_grp1->next = dsp16_grp2;
+ dsp16_grp2->next = NULL_CODE;
+
+ return dsp32;
+}
+
+INSTR_T
+bfin_gen_loop (Expr_Node *exp, REG_T reg, int rop, REG_T preg)
+{
+ const char *loopsym;
+ char *lbeginsym, *lendsym;
+ Expr_Node_Value lbeginval, lendval;
+ Expr_Node *lbegin, *lend;
+ symbolS *sym;
+
+ loopsym = exp->value.s_value;
+ lbeginsym = (char *) xmalloc (strlen (loopsym) + strlen ("__BEGIN") + 5);
+ lendsym = (char *) xmalloc (strlen (loopsym) + strlen ("__END") + 5);
+
+ lbeginsym[0] = 0;
+ lendsym[0] = 0;
+
+ strcat (lbeginsym, "L$L$");
+ strcat (lbeginsym, loopsym);
+ strcat (lbeginsym, "__BEGIN");
+
+ strcat (lendsym, "L$L$");
+ strcat (lendsym, loopsym);
+ strcat (lendsym, "__END");
+
+ lbeginval.s_value = lbeginsym;
+ lendval.s_value = lendsym;
+
+ lbegin = Expr_Node_Create (Expr_Node_Reloc, lbeginval, NULL, NULL);
+ lend = Expr_Node_Create (Expr_Node_Reloc, lendval, NULL, NULL);
+
+ sym = symbol_find(loopsym);
+ if (!S_IS_LOCAL (sym) || (S_IS_LOCAL (sym) && !symbol_used_p (sym)))
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+
+ return bfin_gen_loopsetup (lbegin, reg, rop, lend, preg);
+}
+
+void
+bfin_loop_attempt_create_label (Expr_Node *exp, int is_begin)
+{
+ char *name;
+ name = fb_label_name (exp->value.i_value, is_begin);
+ exp->value.s_value = xstrdup (name);
+ exp->type = Expr_Node_Reloc;
+}
+
+void
+bfin_loop_beginend (Expr_Node *exp, int begin)
+{
+ const char *loopsym;
+ char *label_name;
+ symbolS *linelabel;
+ const char *suffix = begin ? "__BEGIN" : "__END";
+
+ loopsym = exp->value.s_value;
+ label_name = (char *) xmalloc (strlen (loopsym) + strlen (suffix) + 5);
+
+ label_name[0] = 0;
+
+ strcat (label_name, "L$L$");
+ strcat (label_name, loopsym);
+ strcat (label_name, suffix);
+
+ linelabel = colon (label_name);
+
+ /* LOOP_END follows the last instruction in the loop.
+ Adjust label address. */
+ if (!begin)
+ ((struct local_symbol *) linelabel)->lsy_value -= last_insn_size;
+}
+
+bfd_boolean
+bfin_eol_in_insn (char *line)
+{
+ /* Allow a new-line to appear in the middle of a multi-issue instruction. */
+
+ char *temp = line;
+
+ if (*line != '\n')
+ return FALSE;
+
+ /* A semi-colon followed by a newline is always the end of a line. */
+ if (line[-1] == ';')
+ return FALSE;
+
+ if (line[-1] == '|')
+ return TRUE;
+
+ /* If the || is on the next line, there might be leading whitespace. */
+ temp++;
+ while (*temp == ' ' || *temp == '\t') temp++;
+
+ if (*temp == '|')
+ return TRUE;
+
+ return FALSE;
+}
+
+bfd_boolean
+bfin_start_label (char *s, char *ptr)
+{
+ while (s != ptr)
+ {
+ if (*s == '(' || *s == '[')
+ return FALSE;
+ s++;
+ }
+
+ return TRUE;
+}
+
+int
+bfin_force_relocation (struct fix *fixp)
+{
+ if (fixp->fx_r_type ==BFD_RELOC_BFIN_16_LOW
+ || fixp->fx_r_type == BFD_RELOC_BFIN_16_HIGH)
+ return TRUE;
+
+ return generic_force_reloc (fixp);
+}
+
+/* This is a stripped down version of the disassembler. The only thing it
+ does is return a mask of registers modified by an instruction. Only
+ instructions that can occur in a parallel-issue bundle are handled, and
+ only the registers that can cause a conflict are recorded. */
+
+#define DREG_MASK(n) (0x101 << (n))
+#define DREGH_MASK(n) (0x100 << (n))
+#define DREGL_MASK(n) (0x001 << (n))
+#define IREG_MASK(n) (1 << ((n) + 16))
+
+static int
+decode_ProgCtrl_0 (int iw0)
+{
+ if (iw0 == 0)
+ return 0;
+ abort ();
+}
+
+static int
+decode_LDSTpmod_0 (int iw0)
+{
+ /* LDSTpmod
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 0 | 0 |.W.|.aop...|.reg.......|.idx.......|.ptr.......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int W = ((iw0 >> LDSTpmod_W_bits) & LDSTpmod_W_mask);
+ int aop = ((iw0 >> LDSTpmod_aop_bits) & LDSTpmod_aop_mask);
+ int idx = ((iw0 >> LDSTpmod_idx_bits) & LDSTpmod_idx_mask);
+ int ptr = ((iw0 >> LDSTpmod_ptr_bits) & LDSTpmod_ptr_mask);
+ int reg = ((iw0 >> LDSTpmod_reg_bits) & LDSTpmod_reg_mask);
+
+ if (aop == 1 && W == 0 && idx == ptr)
+ return DREGL_MASK (reg);
+ else if (aop == 2 && W == 0 && idx == ptr)
+ return DREGH_MASK (reg);
+ else if (aop == 1 && W == 1 && idx == ptr)
+ return 0;
+ else if (aop == 2 && W == 1 && idx == ptr)
+ return 0;
+ else if (aop == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 1 && W == 0)
+ return DREGL_MASK (reg);
+ else if (aop == 2 && W == 0)
+ return DREGH_MASK (reg);
+ else if (aop == 3 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 3 && W == 1)
+ return DREG_MASK (reg);
+ else if (aop == 0 && W == 1)
+ return 0;
+ else if (aop == 1 && W == 1)
+ return 0;
+ else if (aop == 2 && W == 1)
+ return 0;
+ else
+ return 0;
+
+ return 2;
+}
+
+static int
+decode_dagMODim_0 (int iw0)
+{
+ /* dagMODim
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 0 |.br| 1 | 1 |.op|.m.....|.i.....|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int i = ((iw0 >> DagMODim_i_bits) & DagMODim_i_mask);
+ int opc = ((iw0 >> DagMODim_op_bits) & DagMODim_op_mask);
+
+ if (opc == 0 || opc == 1)
+ return IREG_MASK (i);
+ else
+ return 0;
+
+ return 2;
+}
+
+static int
+decode_dagMODik_0 (int iw0)
+{
+ /* dagMODik
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 0 |.op....|.i.....|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int i = ((iw0 >> DagMODik_i_bits) & DagMODik_i_mask);
+ return IREG_MASK (i);
+}
+
+/* GOOD */
+static int
+decode_dspLDST_0 (int iw0)
+{
+ /* dspLDST
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 0 | 1 | 1 | 1 |.W.|.aop...|.m.....|.i.....|.reg.......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int i = ((iw0 >> DspLDST_i_bits) & DspLDST_i_mask);
+ int m = ((iw0 >> DspLDST_m_bits) & DspLDST_m_mask);
+ int W = ((iw0 >> DspLDST_W_bits) & DspLDST_W_mask);
+ int aop = ((iw0 >> DspLDST_aop_bits) & DspLDST_aop_mask);
+ int reg = ((iw0 >> DspLDST_reg_bits) & DspLDST_reg_mask);
+
+ if (aop == 0 && W == 0 && m == 0)
+ return DREG_MASK (reg) | IREG_MASK (i);
+ else if (aop == 0 && W == 0 && m == 1)
+ return DREGL_MASK (reg) | IREG_MASK (i);
+ else if (aop == 0 && W == 0 && m == 2)
+ return DREGH_MASK (reg) | IREG_MASK (i);
+ else if (aop == 1 && W == 0 && m == 0)
+ return DREG_MASK (reg) | IREG_MASK (i);
+ else if (aop == 1 && W == 0 && m == 1)
+ return DREGL_MASK (reg) | IREG_MASK (i);
+ else if (aop == 1 && W == 0 && m == 2)
+ return DREGH_MASK (reg) | IREG_MASK (i);
+ else if (aop == 2 && W == 0 && m == 0)
+ return DREG_MASK (reg);
+ else if (aop == 2 && W == 0 && m == 1)
+ return DREGL_MASK (reg);
+ else if (aop == 2 && W == 0 && m == 2)
+ return DREGH_MASK (reg);
+ else if (aop == 0 && W == 1 && m == 0)
+ return IREG_MASK (i);
+ else if (aop == 0 && W == 1 && m == 1)
+ return IREG_MASK (i);
+ else if (aop == 0 && W == 1 && m == 2)
+ return IREG_MASK (i);
+ else if (aop == 1 && W == 1 && m == 0)
+ return IREG_MASK (i);
+ else if (aop == 1 && W == 1 && m == 1)
+ return IREG_MASK (i);
+ else if (aop == 1 && W == 1 && m == 2)
+ return IREG_MASK (i);
+ else if (aop == 2 && W == 1 && m == 0)
+ return 0;
+ else if (aop == 2 && W == 1 && m == 1)
+ return 0;
+ else if (aop == 2 && W == 1 && m == 2)
+ return 0;
+ else if (aop == 3 && W == 0)
+ return DREG_MASK (reg) | IREG_MASK (i);
+ else if (aop == 3 && W == 1)
+ return IREG_MASK (i);
+
+ abort ();
+}
+
+/* GOOD */
+static int
+decode_LDST_0 (int iw0)
+{
+ /* LDST
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 0 | 1 |.sz....|.W.|.aop...|.Z.|.ptr.......|.reg.......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int Z = ((iw0 >> LDST_Z_bits) & LDST_Z_mask);
+ int W = ((iw0 >> LDST_W_bits) & LDST_W_mask);
+ int sz = ((iw0 >> LDST_sz_bits) & LDST_sz_mask);
+ int aop = ((iw0 >> LDST_aop_bits) & LDST_aop_mask);
+ int reg = ((iw0 >> LDST_reg_bits) & LDST_reg_mask);
+
+ if (aop == 0 && sz == 0 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 0 && sz == 0 && Z == 1 && W == 0)
+ return 0;
+ else if (aop == 0 && sz == 1 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 0 && sz == 1 && Z == 1 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 0 && sz == 2 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 0 && sz == 2 && Z == 1 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 1 && sz == 0 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 1 && sz == 0 && Z == 1 && W == 0)
+ return 0;
+ else if (aop == 1 && sz == 1 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 1 && sz == 1 && Z == 1 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 1 && sz == 2 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 1 && sz == 2 && Z == 1 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 2 && sz == 0 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 2 && sz == 0 && Z == 1 && W == 0)
+ return 0;
+ else if (aop == 2 && sz == 1 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 2 && sz == 1 && Z == 1 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 2 && sz == 2 && Z == 0 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 2 && sz == 2 && Z == 1 && W == 0)
+ return DREG_MASK (reg);
+ else if (aop == 0 && sz == 0 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 0 && sz == 0 && Z == 1 && W == 1)
+ return 0;
+ else if (aop == 0 && sz == 1 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 0 && sz == 2 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 1 && sz == 0 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 1 && sz == 0 && Z == 1 && W == 1)
+ return 0;
+ else if (aop == 1 && sz == 1 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 1 && sz == 2 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 2 && sz == 0 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 2 && sz == 0 && Z == 1 && W == 1)
+ return 0;
+ else if (aop == 2 && sz == 1 && Z == 0 && W == 1)
+ return 0;
+ else if (aop == 2 && sz == 2 && Z == 0 && W == 1)
+ return 0;
+
+ abort ();
+}
+
+static int
+decode_LDSTiiFP_0 (int iw0)
+{
+ /* LDSTiiFP
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 1 | 1 | 1 | 0 |.W.|.offset............|.reg...........|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int reg = ((iw0 >> LDSTiiFP_reg_bits) & LDSTiiFP_reg_mask);
+ int W = ((iw0 >> LDSTiiFP_W_bits) & LDSTiiFP_W_mask);
+
+ if (W == 0)
+ return reg < 8 ? DREG_MASK (reg) : 0;
+ else
+ return 0;
+}
+
+static int
+decode_LDSTii_0 (int iw0)
+{
+ /* LDSTii
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 0 | 1 |.W.|.op....|.offset........|.ptr.......|.reg.......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int reg = ((iw0 >> LDSTii_reg_bit) & LDSTii_reg_mask);
+ int opc = ((iw0 >> LDSTii_op_bit) & LDSTii_op_mask);
+ int W = ((iw0 >> LDSTii_W_bit) & LDSTii_W_mask);
+
+ if (W == 0 && opc != 3)
+ return DREG_MASK (reg);
+ else if (W == 0 && opc == 3)
+ return 0;
+ else if (W == 1 && opc == 0)
+ return 0;
+ else if (W == 1 && opc == 1)
+ return 0;
+ else if (W == 1 && opc == 3)
+ return 0;
+
+ abort ();
+}
+
+static int
+decode_dsp32mac_0 (int iw0, int iw1)
+{
+ int result = 0;
+ /* dsp32mac
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 1 | 0 | 0 |.M.| 0 | 0 |.mmod..........|.MM|.P.|.w1|.op1...|
+ |.h01|.h11|.w0|.op0...|.h00|.h10|.dst.......|.src0......|.src1..|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int op1 = ((iw0 >> (DSP32Mac_op1_bits - 16)) & DSP32Mac_op1_mask);
+ int w1 = ((iw0 >> (DSP32Mac_w1_bits - 16)) & DSP32Mac_w1_mask);
+ int P = ((iw0 >> (DSP32Mac_p_bits - 16)) & DSP32Mac_p_mask);
+ int mmod = ((iw0 >> (DSP32Mac_mmod_bits - 16)) & DSP32Mac_mmod_mask);
+ int w0 = ((iw1 >> DSP32Mac_w0_bits) & DSP32Mac_w0_mask);
+ int MM = ((iw1 >> DSP32Mac_MM_bits) & DSP32Mac_MM_mask);
+ int dst = ((iw1 >> DSP32Mac_dst_bits) & DSP32Mac_dst_mask);
+ int op0 = ((iw1 >> DSP32Mac_op0_bits) & DSP32Mac_op0_mask);
+
+ if (w0 == 0 && w1 == 0 && op1 == 3 && op0 == 3)
+ return 0;
+
+ if (op1 == 3 && MM)
+ return 0;
+
+ if ((w1 || w0) && mmod == M_W32)
+ return 0;
+
+ if (((1 << mmod) & (P ? 0x131b : 0x1b5f)) == 0)
+ return 0;
+
+ if (w1 == 1 || op1 != 3)
+ {
+ if (w1)
+ {
+ if (P)
+ return DREG_MASK (dst + 1);
+ else
+ return DREGH_MASK (dst);
+ }
+ }
+
+ if (w0 == 1 || op0 != 3)
+ {
+ if (w0)
+ {
+ if (P)
+ return DREG_MASK (dst);
+ else
+ return DREGL_MASK (dst);
+ }
+ }
+
+ return result;
+}
+
+static int
+decode_dsp32mult_0 (int iw0, int iw1)
+{
+ /* dsp32mult
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 1 | 0 | 0 |.M.| 0 | 1 |.mmod..........|.MM|.P.|.w1|.op1...|
+ |.h01|.h11|.w0|.op0...|.h00|.h10|.dst.......|.src0......|.src1..|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int w1 = ((iw0 >> (DSP32Mac_w1_bits - 16)) & DSP32Mac_w1_mask);
+ int P = ((iw0 >> (DSP32Mac_p_bits - 16)) & DSP32Mac_p_mask);
+ int mmod = ((iw0 >> (DSP32Mac_mmod_bits - 16)) & DSP32Mac_mmod_mask);
+ int w0 = ((iw1 >> DSP32Mac_w0_bits) & DSP32Mac_w0_mask);
+ int dst = ((iw1 >> DSP32Mac_dst_bits) & DSP32Mac_dst_mask);
+ int result = 0;
+
+ if (w1 == 0 && w0 == 0)
+ return 0;
+
+ if (((1 << mmod) & (P ? 0x313 : 0x1b57)) == 0)
+ return 0;
+
+ if (w1)
+ {
+ if (P)
+ return DREG_MASK (dst | 1);
+ else
+ return DREGH_MASK (dst);
+ }
+
+ if (w0)
+ {
+ if (P)
+ return DREG_MASK (dst);
+ else
+ return DREGL_MASK (dst);
+ }
+
+ return result;
+}
+
+static int
+decode_dsp32alu_0 (int iw0, int iw1)
+{
+ /* dsp32alu
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 1 | 0 | 0 |.M.| 1 | 0 | - | - | - |.HL|.aopcde............|
+ |.aop...|.s.|.x.|.dst0......|.dst1......|.src0......|.src1......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int s = ((iw1 >> DSP32Alu_s_bits) & DSP32Alu_s_mask);
+ int x = ((iw1 >> DSP32Alu_x_bits) & DSP32Alu_x_mask);
+ int aop = ((iw1 >> DSP32Alu_aop_bits) & DSP32Alu_aop_mask);
+ int dst0 = ((iw1 >> DSP32Alu_dst0_bits) & DSP32Alu_dst0_mask);
+ int dst1 = ((iw1 >> DSP32Alu_dst1_bits) & DSP32Alu_dst1_mask);
+ int HL = ((iw0 >> (DSP32Alu_HL_bits - 16)) & DSP32Alu_HL_mask);
+ int aopcde = ((iw0 >> (DSP32Alu_aopcde_bits - 16)) & DSP32Alu_aopcde_mask);
+
+ if (aop == 0 && aopcde == 9 && s == 0)
+ return 0;
+ else if (aop == 2 && aopcde == 9 && HL == 0 && s == 0)
+ return 0;
+ else if (aop >= x * 2 && aopcde == 5)
+ return HL ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (HL == 0 && aopcde == 2)
+ return DREGL_MASK (dst0);
+ else if (HL == 1 && aopcde == 2)
+ return DREGH_MASK (dst0);
+ else if (HL == 0 && aopcde == 3)
+ return DREGL_MASK (dst0);
+ else if (HL == 1 && aopcde == 3)
+ return DREGH_MASK (dst0);
+
+ else if (aop == 0 && aopcde == 9 && s == 1)
+ return 0;
+ else if (aop == 1 && aopcde == 9 && s == 0)
+ return 0;
+ else if (aop == 2 && aopcde == 9 && s == 1)
+ return 0;
+ else if (aop == 3 && aopcde == 9 && s == 0)
+ return 0;
+ else if (aopcde == 8)
+ return 0;
+ else if (aop == 0 && aopcde == 11)
+ return DREG_MASK (dst0);
+ else if (aop == 1 && aopcde == 11)
+ return HL ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (aopcde == 11)
+ return 0;
+ else if (aopcde == 22)
+ return DREG_MASK (dst0);
+
+ else if ((aop == 0 || aop == 1) && aopcde == 14)
+ return 0;
+ else if (aop == 3 && HL == 0 && aopcde == 14)
+ return 0;
+
+ else if (aop == 3 && HL == 0 && aopcde == 15)
+ return DREG_MASK (dst0);
+
+ else if (aop == 1 && aopcde == 16)
+ return 0;
+
+ else if (aop == 0 && aopcde == 16)
+ return 0;
+
+ else if (aop == 3 && HL == 0 && aopcde == 16)
+ return 0;
+
+ else if (aop == 3 && HL == 0 && aopcde == 7)
+ return DREG_MASK (dst0);
+ else if ((aop == 0 || aop == 1 || aop == 2) && aopcde == 7)
+ return DREG_MASK (dst0);
+
+ else if (aop == 0 && aopcde == 12)
+ return DREG_MASK (dst0);
+ else if (aop == 1 && aopcde == 12)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+ else if (aop == 3 && aopcde == 12)
+ return HL ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+
+ else if (aopcde == 0)
+ return DREG_MASK (dst0);
+ else if (aopcde == 1)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+
+ else if (aop == 0 && aopcde == 10)
+ return DREGL_MASK (dst0);
+ else if (aop == 1 && aopcde == 10)
+ return DREGL_MASK (dst0);
+
+ else if ((aop == 1 || aop == 0) && aopcde == 4)
+ return DREG_MASK (dst0);
+ else if (aop == 2 && aopcde == 4)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+
+ else if (aop == 0 && aopcde == 17)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+ else if (aop == 1 && aopcde == 17)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+ else if (aop == 0 && aopcde == 18)
+ return 0;
+ else if (aop == 3 && aopcde == 18)
+ return 0;
+
+ else if ((aop == 0 || aop == 1 || aop == 2) && aopcde == 6)
+ return DREG_MASK (dst0);
+
+ else if ((aop == 0 || aop == 1) && aopcde == 20)
+ return DREG_MASK (dst0);
+
+ else if ((aop == 0 || aop == 1) && aopcde == 21)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+
+ else if (aop == 0 && aopcde == 23 && HL == 1)
+ return DREG_MASK (dst0);
+ else if (aop == 0 && aopcde == 23 && HL == 0)
+ return DREG_MASK (dst0);
+
+ else if (aop == 0 && aopcde == 24)
+ return DREG_MASK (dst0);
+ else if (aop == 1 && aopcde == 24)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+ else if (aopcde == 13)
+ return DREG_MASK (dst0) | DREG_MASK (dst1);
+ else
+ return 0;
+
+ return 4;
+}
+
+static int
+decode_dsp32shift_0 (int iw0, int iw1)
+{
+ /* dsp32shift
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 1 | 0 | 0 |.M.| 1 | 1 | 0 | 0 | - | - |.sopcde............|
+ |.sop...|.HLs...|.dst0......| - | - | - |.src0......|.src1......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int HLs = ((iw1 >> DSP32Shift_HLs_bits) & DSP32Shift_HLs_mask);
+ int sop = ((iw1 >> DSP32Shift_sop_bits) & DSP32Shift_sop_mask);
+ int src0 = ((iw1 >> DSP32Shift_src0_bits) & DSP32Shift_src0_mask);
+ int src1 = ((iw1 >> DSP32Shift_src1_bits) & DSP32Shift_src1_mask);
+ int dst0 = ((iw1 >> DSP32Shift_dst0_bits) & DSP32Shift_dst0_mask);
+ int sopcde = ((iw0 >> (DSP32Shift_sopcde_bits - 16)) & DSP32Shift_sopcde_mask);
+
+ if (sop == 0 && sopcde == 0)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 0)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 2 && sopcde == 0)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 0 && sopcde == 3)
+ return 0;
+ else if (sop == 1 && sopcde == 3)
+ return 0;
+ else if (sop == 2 && sopcde == 3)
+ return 0;
+ else if (sop == 3 && sopcde == 3)
+ return DREG_MASK (dst0);
+ else if (sop == 0 && sopcde == 1)
+ return DREG_MASK (dst0);
+ else if (sop == 1 && sopcde == 1)
+ return DREG_MASK (dst0);
+ else if (sop == 2 && sopcde == 1)
+ return DREG_MASK (dst0);
+ else if (sopcde == 2)
+ return DREG_MASK (dst0);
+ else if (sopcde == 4)
+ return DREG_MASK (dst0);
+ else if (sop == 0 && sopcde == 5)
+ return DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 5)
+ return DREGL_MASK (dst0);
+ else if (sop == 2 && sopcde == 5)
+ return DREGL_MASK (dst0);
+ else if (sop == 0 && sopcde == 6)
+ return DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 6)
+ return DREGL_MASK (dst0);
+ else if (sop == 3 && sopcde == 6)
+ return DREGL_MASK (dst0);
+ else if (sop == 0 && sopcde == 7)
+ return DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 7)
+ return DREGL_MASK (dst0);
+ else if (sop == 2 && sopcde == 7)
+ return DREGL_MASK (dst0);
+ else if (sop == 3 && sopcde == 7)
+ return DREGL_MASK (dst0);
+ else if (sop == 0 && sopcde == 8)
+ return DREG_MASK (src0) | DREG_MASK (src1);
+#if 0
+ {
+ OUTS (outf, "BITMUX (");
+ OUTS (outf, dregs (src0));
+ OUTS (outf, ", ");
+ OUTS (outf, dregs (src1));
+ OUTS (outf, ", A0) (ASR)");
+ }
+#endif
+ else if (sop == 1 && sopcde == 8)
+ return DREG_MASK (src0) | DREG_MASK (src1);
+#if 0
+ {
+ OUTS (outf, "BITMUX (");
+ OUTS (outf, dregs (src0));
+ OUTS (outf, ", ");
+ OUTS (outf, dregs (src1));
+ OUTS (outf, ", A0) (ASL)");
+ }
+#endif
+ else if (sopcde == 9)
+ return sop < 2 ? DREGL_MASK (dst0) : DREG_MASK (dst0);
+ else if (sopcde == 10)
+ return DREG_MASK (dst0);
+ else if (sop == 0 && sopcde == 11)
+ return DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 11)
+ return DREGL_MASK (dst0);
+ else if (sop == 0 && sopcde == 12)
+ return 0;
+ else if (sop == 1 && sopcde == 12)
+ return DREGL_MASK (dst0);
+ else if (sop == 0 && sopcde == 13)
+ return DREG_MASK (dst0);
+ else if (sop == 1 && sopcde == 13)
+ return DREG_MASK (dst0);
+ else if (sop == 2 && sopcde == 13)
+ return DREG_MASK (dst0);
+
+ abort ();
+}
+
+static int
+decode_dsp32shiftimm_0 (int iw0, int iw1)
+{
+ /* dsp32shiftimm
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+
+ | 1 | 1 | 0 | 0 |.M.| 1 | 1 | 0 | 1 | - | - |.sopcde............|
+ |.sop...|.HLs...|.dst0......|.immag.................|.src1......|
+ +---+---+---+---|---+---+---+---|---+---+---+---|---+---+---+---+ */
+ int sop = ((iw1 >> DSP32ShiftImm_sop_bits) & DSP32ShiftImm_sop_mask);
+ int bit8 = ((iw1 >> 8) & 0x1);
+ int dst0 = ((iw1 >> DSP32ShiftImm_dst0_bits) & DSP32ShiftImm_dst0_mask);
+ int sopcde = ((iw0 >> (DSP32ShiftImm_sopcde_bits - 16)) & DSP32ShiftImm_sopcde_mask);
+ int HLs = ((iw1 >> DSP32ShiftImm_HLs_bits) & DSP32ShiftImm_HLs_mask);
+
+
+ if (sop == 0 && sopcde == 0)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 0 && bit8 == 0)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 1 && sopcde == 0 && bit8 == 1)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 2 && sopcde == 0 && bit8 == 0)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 2 && sopcde == 0 && bit8 == 1)
+ return HLs & 2 ? DREGH_MASK (dst0) : DREGL_MASK (dst0);
+ else if (sop == 2 && sopcde == 3 && HLs == 1)
+ return 0;
+ else if (sop == 0 && sopcde == 3 && HLs == 0 && bit8 == 0)
+ return 0;
+ else if (sop == 0 && sopcde == 3 && HLs == 0 && bit8 == 1)
+ return 0;
+ else if (sop == 0 && sopcde == 3 && HLs == 1 && bit8 == 0)
+ return 0;
+ else if (sop == 0 && sopcde == 3 && HLs == 1 && bit8 == 1)
+ return 0;
+ else if (sop == 1 && sopcde == 3 && HLs == 0)
+ return 0;
+ else if (sop == 1 && sopcde == 3 && HLs == 1)
+ return 0;
+ else if (sop == 2 && sopcde == 3 && HLs == 0)
+ return 0;
+ else if (sop == 1 && sopcde == 1 && bit8 == 0)
+ return DREG_MASK (dst0);
+ else if (sop == 1 && sopcde == 1 && bit8 == 1)
+ return DREG_MASK (dst0);
+ else if (sop == 2 && sopcde == 1 && bit8 == 1)
+ return DREG_MASK (dst0);
+ else if (sop == 2 && sopcde == 1 && bit8 == 0)
+ return DREG_MASK (dst0);
+ else if (sop == 0 && sopcde == 1)
+ return DREG_MASK (dst0);
+ else if (sop == 1 && sopcde == 2)
+ return DREG_MASK (dst0);
+ else if (sop == 2 && sopcde == 2 && bit8 == 1)
+ return DREG_MASK (dst0);
+ else if (sop == 2 && sopcde == 2 && bit8 == 0)
+ return DREG_MASK (dst0);
+ else if (sop == 3 && sopcde == 2)
+ return DREG_MASK (dst0);
+ else if (sop == 0 && sopcde == 2)
+ return DREG_MASK (dst0);
+
+ abort ();
+}
+
+int
+insn_regmask (int iw0, int iw1)
+{
+ if ((iw0 & 0xf7ff) == 0xc003 && iw1 == 0x1800)
+ return 0; /* MNOP */
+ else if ((iw0 & 0xff00) == 0x0000)
+ return decode_ProgCtrl_0 (iw0);
+ else if ((iw0 & 0xffc0) == 0x0240)
+ abort ();
+ else if ((iw0 & 0xff80) == 0x0100)
+ abort ();
+ else if ((iw0 & 0xfe00) == 0x0400)
+ abort ();
+ else if ((iw0 & 0xfe00) == 0x0600)
+ abort ();
+ else if ((iw0 & 0xf800) == 0x0800)
+ abort ();
+ else if ((iw0 & 0xffe0) == 0x0200)
+ abort ();
+ else if ((iw0 & 0xff00) == 0x0300)
+ abort ();
+ else if ((iw0 & 0xf000) == 0x1000)
+ abort ();
+ else if ((iw0 & 0xf000) == 0x2000)
+ abort ();
+ else if ((iw0 & 0xf000) == 0x3000)
+ abort ();
+ else if ((iw0 & 0xfc00) == 0x4000)
+ abort ();
+ else if ((iw0 & 0xfe00) == 0x4400)
+ abort ();
+ else if ((iw0 & 0xf800) == 0x4800)
+ abort ();
+ else if ((iw0 & 0xf000) == 0x5000)
+ abort ();
+ else if ((iw0 & 0xf800) == 0x6000)
+ abort ();
+ else if ((iw0 & 0xf800) == 0x6800)
+ abort ();
+ else if ((iw0 & 0xf000) == 0x8000)
+ return decode_LDSTpmod_0 (iw0);
+ else if ((iw0 & 0xff60) == 0x9e60)
+ return decode_dagMODim_0 (iw0);
+ else if ((iw0 & 0xfff0) == 0x9f60)
+ return decode_dagMODik_0 (iw0);
+ else if ((iw0 & 0xfc00) == 0x9c00)
+ return decode_dspLDST_0 (iw0);
+ else if ((iw0 & 0xf000) == 0x9000)
+ return decode_LDST_0 (iw0);
+ else if ((iw0 & 0xfc00) == 0xb800)
+ return decode_LDSTiiFP_0 (iw0);
+ else if ((iw0 & 0xe000) == 0xA000)
+ return decode_LDSTii_0 (iw0);
+ else if ((iw0 & 0xff80) == 0xe080 && (iw1 & 0x0C00) == 0x0000)
+ abort ();
+ else if ((iw0 & 0xff00) == 0xe100 && (iw1 & 0x0000) == 0x0000)
+ abort ();
+ else if ((iw0 & 0xfe00) == 0xe200 && (iw1 & 0x0000) == 0x0000)
+ abort ();
+ else if ((iw0 & 0xfc00) == 0xe400 && (iw1 & 0x0000) == 0x0000)
+ abort ();
+ else if ((iw0 & 0xfffe) == 0xe800 && (iw1 & 0x0000) == 0x0000)
+ abort ();
+ else if ((iw0 & 0xf600) == 0xc000 && (iw1 & 0x0000) == 0x0000)
+ return decode_dsp32mac_0 (iw0, iw1);
+ else if ((iw0 & 0xf600) == 0xc200 && (iw1 & 0x0000) == 0x0000)
+ return decode_dsp32mult_0 (iw0, iw1);
+ else if ((iw0 & 0xf7c0) == 0xc400 && (iw1 & 0x0000) == 0x0000)
+ return decode_dsp32alu_0 (iw0, iw1);
+ else if ((iw0 & 0xf780) == 0xc600 && (iw1 & 0x01c0) == 0x0000)
+ return decode_dsp32shift_0 (iw0, iw1);
+ else if ((iw0 & 0xf780) == 0xc680 && (iw1 & 0x0000) == 0x0000)
+ return decode_dsp32shiftimm_0 (iw0, iw1);
+ else if ((iw0 & 0xff00) == 0xf800)
+ abort ();
+ else if ((iw0 & 0xFFC0) == 0xf000 && (iw1 & 0x0000) == 0x0000)
+ abort ();
+
+ abort ();
+}
diff --git a/gas/config/tc-bfin.h b/gas/config/tc-bfin.h
new file mode 100644
index 0000000..38f8c67
--- /dev/null
+++ b/gas/config/tc-bfin.h
@@ -0,0 +1,84 @@
+/* tc-bfin.h - header file for tc-bfin.c
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_BFIN 1
+#define TC_ADI_BFIN 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_ARCH bfd_arch_bfin
+
+/*
+ * Define the target format macro here. The value for this should be
+ * "elf32-bfin", not "elf32-little-bfin". Since the BFD source file
+ * elf32-bfin.c defines TARGET_LITTLE_NAME to be "elf32-little-bfin",
+ * we must use this value, until this is corrected and BFD is rebuilt. */
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-bfin"
+#endif
+
+#define LISTING_HEADER "BFIN GAS "
+
+#define WORKING_DOT_WORD
+
+extern bfd_boolean bfin_start_label (char *, char *);
+
+#define md_number_to_chars number_to_chars_littleendian
+#define md_convert_frag(b,s,f) as_fatal ("bfin convert_frag\n");
+
+/* Allow for [, ], etc. */
+#define LEX_BR 6
+
+#define TC_EOL_IN_INSN(PTR) (bfin_eol_in_insn(PTR) ? 1 : 0)
+extern bfd_boolean bfin_eol_in_insn (char *);
+
+/* Almost all instructions of Blackfin contain an = character. */
+#define TC_EQUAL_IN_INSN(C, NAME) 1
+
+#define NOP_OPCODE 0x0000
+
+#define LOCAL_LABELS_FB 1
+
+#define DOUBLESLASH_LINE_COMMENTS
+
+#define TC_START_LABEL(c, s, ptr) (c == ':' && bfin_start_label (s, ptr))
+#define tc_fix_adjustable(FIX) bfin_fix_adjustable (FIX)
+extern bfd_boolean bfin_fix_adjustable (struct fix *);
+
+#define TC_FORCE_RELOCATION(FIX) bfin_force_relocation (FIX)
+extern int bfin_force_relocation (struct fix *);
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* Values passed to md_apply_fix3 don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* This target is buggy, and sets fix size too large. */
+#define TC_FX_SIZE_SLACK(FIX) 2
+
+extern unsigned int bfin_anomaly_checks;
+
+/* Anomaly checking */
+#define AC_05000074 0x00000001
+#define ENABLE_AC_05000074 (bfin_anomaly_checks & AC_05000074)
+
+/* end of tc-bfin.h */
diff --git a/gas/config/tc-cr16.c b/gas/config/tc-cr16.c
new file mode 100644
index 0000000..bcdf978
--- /dev/null
+++ b/gas/config/tc-cr16.c
@@ -0,0 +1,2569 @@
+/* tc-cr16.c -- Assembler code for the CR16 CPU core.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ Contributed by M R Swami Reddy <MR.Swami.Reddy@nsc.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "opcode/cr16.h"
+#include "elf/cr16.h"
+
+
+/* Word is considered here as a 16-bit unsigned short int. */
+#define WORD_SHIFT 16
+
+/* Register is 2-byte size. */
+#define REG_SIZE 2
+
+/* Maximum size of a single instruction (in words). */
+#define INSN_MAX_SIZE 3
+
+/* Maximum bits which may be set in a `mask16' operand. */
+#define MAX_REGS_IN_MASK16 8
+
+/* Assign a number NUM, shifted by SHIFT bytes, into a location
+ pointed by index BYTE of array 'output_opcode'. */
+#define CR16_PRINT(BYTE, NUM, SHIFT) output_opcode[BYTE] |= (NUM << SHIFT)
+
+/* Operand errors. */
+typedef enum
+ {
+ OP_LEGAL = 0, /* Legal operand. */
+ OP_OUT_OF_RANGE, /* Operand not within permitted range. */
+ OP_NOT_EVEN /* Operand is Odd number, should be even. */
+ }
+op_err;
+
+/* Opcode mnemonics hash table. */
+static struct hash_control *cr16_inst_hash;
+/* CR16 registers hash table. */
+static struct hash_control *reg_hash;
+/* CR16 register pair hash table. */
+static struct hash_control *regp_hash;
+/* CR16 processor registers hash table. */
+static struct hash_control *preg_hash;
+/* CR16 processor registers 32 bit hash table. */
+static struct hash_control *pregp_hash;
+/* Current instruction we're assembling. */
+const inst *instruction;
+
+
+static int code_label = 0;
+
+/* Global variables. */
+
+/* Array to hold an instruction encoding. */
+long output_opcode[2];
+
+/* Nonzero means a relocatable symbol. */
+int relocatable;
+
+/* A copy of the original instruction (used in error messages). */
+char ins_parse[MAX_INST_LEN];
+
+/* The current processed argument number. */
+int cur_arg_num;
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant as in 0f12.456 */
+const char FLT_CHARS[] = "f'";
+
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+symbolS * GOT_symbol;
+#endif
+
+/* Target-specific multicharacter options, not const-declared at usage. */
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+static void
+l_cons (int nbytes)
+{
+ int c;
+ expressionS exp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef TC_ADDRESS_BYTES
+ if (nbytes == 0)
+ nbytes = TC_ADDRESS_BYTES ();
+#endif
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ c = 0;
+ do
+ {
+ unsigned int bits_available = BITS_PER_CHAR * nbytes;
+ char *hold = input_line_pointer;
+
+ expression (&exp);
+
+ if (*input_line_pointer == ':')
+ {
+ /* Bitfields. */
+ long value = 0;
+
+ for (;;)
+ {
+ unsigned long width;
+
+ if (*input_line_pointer != ':')
+ {
+ input_line_pointer = hold;
+ break;
+ }
+ if (exp.X_op == O_absent)
+ {
+ as_warn (_("using a bit field width of zero"));
+ exp.X_add_number = 0;
+ exp.X_op = O_constant;
+ }
+
+ if (exp.X_op != O_constant)
+ {
+ *input_line_pointer = '\0';
+ as_bad (_("field width \"%s\" too complex for a bitfield"), hold);
+ *input_line_pointer = ':';
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if ((width = exp.X_add_number) >
+ (unsigned int)(BITS_PER_CHAR * nbytes))
+ {
+ as_warn (_("field width %lu too big to fit in %d bytes: truncated to %d bits"), width, nbytes, (BITS_PER_CHAR * nbytes));
+ width = BITS_PER_CHAR * nbytes;
+ } /* Too big. */
+
+
+ if (width > bits_available)
+ {
+ /* FIXME-SOMEDAY: backing up and reparsing is wasteful. */
+ input_line_pointer = hold;
+ exp.X_add_number = value;
+ break;
+ }
+
+ /* Skip ':'. */
+ hold = ++input_line_pointer;
+
+ expression (&exp);
+ if (exp.X_op != O_constant)
+ {
+ char cache = *input_line_pointer;
+
+ *input_line_pointer = '\0';
+ as_bad (_("field value \"%s\" too complex for a bitfield"), hold);
+ *input_line_pointer = cache;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ value |= ((~(-1 << width) & exp.X_add_number)
+ << ((BITS_PER_CHAR * nbytes) - bits_available));
+
+ if ((bits_available -= width) == 0
+ || is_it_end_of_statement ()
+ || *input_line_pointer != ',')
+ break;
+
+ hold = ++input_line_pointer;
+ expression (&exp);
+ }
+
+ exp.X_add_number = value;
+ exp.X_op = O_constant;
+ exp.X_unsigned = 1;
+ }
+
+ if ((*(input_line_pointer) == '@') && (*(input_line_pointer +1) == 'c'))
+ code_label = 1;
+ emit_expr (&exp, (unsigned int) nbytes);
+ ++c;
+ if ((*(input_line_pointer) == '@') && (*(input_line_pointer +1) == 'c'))
+ {
+ input_line_pointer +=3;
+ break;
+ }
+ }
+ while ((*input_line_pointer++ == ','));
+
+ /* Put terminator back into stream. */
+ input_line_pointer--;
+
+ demand_empty_rest_of_line ();
+}
+
+/* This table describes all the machine specific pseudo-ops
+ the assembler has to support. The fields are:
+ *** Pseudo-op name without dot.
+ *** Function to call to execute this pseudo-op.
+ *** Integer arg to pass to the function. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* In CR16 machine, align is in bytes (not a ptwo boundary). */
+ {"align", s_align_bytes, 0},
+ {"long", l_cons, 4 },
+ {"4byte", l_cons, 4 },
+ {0, 0, 0}
+};
+
+/* CR16 relaxation table. */
+const relax_typeS md_relax_table[] =
+{
+ /* bCC */
+ {0x7f, -0x80, 2, 1}, /* 8 */
+ {0xfffe, -0x10000, 4, 2}, /* 16 */
+ {0xfffffe, -0x1000000, 6, 0}, /* 24 */
+};
+
+/* Return the bit size for a given operand. */
+
+static int
+get_opbits (operand_type op)
+{
+ if (op < MAX_OPRD)
+ return cr16_optab[op].bit_size;
+
+ return 0;
+}
+
+/* Return the argument type of a given operand. */
+
+static argtype
+get_optype (operand_type op)
+{
+ if (op < MAX_OPRD)
+ return cr16_optab[op].arg_type;
+ else
+ return nullargs;
+}
+
+/* Return the flags of a given operand. */
+
+static int
+get_opflags (operand_type op)
+{
+ if (op < MAX_OPRD)
+ return cr16_optab[op].flags;
+
+ return 0;
+}
+
+/* Get the cc code. */
+
+static int
+get_cc (char *cc_name)
+{
+ unsigned int i;
+
+ for (i = 0; i < cr16_num_cc; i++)
+ if (strcmp (cc_name, cr16_b_cond_tab[i]) == 0)
+ return i;
+
+ return -1;
+}
+
+/* Get the core processor register 'reg_name'. */
+
+static reg
+get_register (char *reg_name)
+{
+ const reg_entry *rreg;
+
+ rreg = (const reg_entry *) hash_find (reg_hash, reg_name);
+
+ if (rreg != NULL)
+ return rreg->value.reg_val;
+
+ return nullregister;
+}
+/* Get the core processor register-pair 'reg_name'. */
+
+static reg
+get_register_pair (char *reg_name)
+{
+ const reg_entry *rreg;
+ char tmp_rp[16]="\0";
+
+ /* Add '(' and ')' to the reg pair, if its not present. */
+ if (reg_name[0] != '(')
+ {
+ tmp_rp[0] = '(';
+ strcat (tmp_rp, reg_name);
+ strcat (tmp_rp,")");
+ rreg = (const reg_entry *) hash_find (regp_hash, tmp_rp);
+ }
+ else
+ rreg = (const reg_entry *) hash_find (regp_hash, reg_name);
+
+ if (rreg != NULL)
+ return rreg->value.reg_val;
+
+ return nullregister;
+}
+
+/* Get the index register 'reg_name'. */
+
+static reg
+get_index_register (char *reg_name)
+{
+ const reg_entry *rreg;
+
+ rreg = (const reg_entry *) hash_find (reg_hash, reg_name);
+
+ if ((rreg != NULL)
+ && ((rreg->value.reg_val == 12) || (rreg->value.reg_val == 13)))
+ return rreg->value.reg_val;
+
+ return nullregister;
+}
+/* Get the core processor index register-pair 'reg_name'. */
+
+static reg
+get_index_register_pair (char *reg_name)
+{
+ const reg_entry *rreg;
+
+ rreg = (const reg_entry *) hash_find (regp_hash, reg_name);
+
+ if (rreg != NULL)
+ {
+ if ((rreg->value.reg_val != 1) || (rreg->value.reg_val != 7)
+ || (rreg->value.reg_val != 9) || (rreg->value.reg_val > 10))
+ return rreg->value.reg_val;
+
+ as_bad (_("Unknown register pair - index relative mode: `%d'"), rreg->value.reg_val);
+ }
+
+ return nullregister;
+}
+
+/* Get the processor register 'preg_name'. */
+
+static preg
+get_pregister (char *preg_name)
+{
+ const reg_entry *prreg;
+
+ prreg = (const reg_entry *) hash_find (preg_hash, preg_name);
+
+ if (prreg != NULL)
+ return prreg->value.preg_val;
+
+ return nullpregister;
+}
+
+/* Get the processor register 'preg_name 32 bit'. */
+
+static preg
+get_pregisterp (char *preg_name)
+{
+ const reg_entry *prreg;
+
+ prreg = (const reg_entry *) hash_find (pregp_hash, preg_name);
+
+ if (prreg != NULL)
+ return prreg->value.preg_val;
+
+ return nullpregister;
+}
+
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT seg, valueT val)
+{
+ /* Round .text section to a multiple of 2. */
+ if (seg == text_section)
+ return (val + 1) & ~1;
+ return val;
+}
+
+/* Parse an operand that is machine-specific (remove '*'). */
+
+void
+md_operand (expressionS * exp)
+{
+ char c = *input_line_pointer;
+
+ switch (c)
+ {
+ case '*':
+ input_line_pointer++;
+ expression (exp);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Reset global variables before parsing a new instruction. */
+
+static void
+reset_vars (char *op)
+{
+ cur_arg_num = relocatable = 0;
+ memset (& output_opcode, '\0', sizeof (output_opcode));
+
+ /* Save a copy of the original OP (used in error messages). */
+ strncpy (ins_parse, op, sizeof ins_parse - 1);
+ ins_parse [sizeof ins_parse - 1] = 0;
+}
+
+/* This macro decides whether a particular reloc is an entry in a
+ switch table. It is used when relaxing, because the linker needs
+ to know about all such entries so that it can adjust them if
+ necessary. */
+
+#define SWITCH_TABLE(fix) \
+ ( (fix)->fx_addsy != NULL \
+ && (fix)->fx_subsy != NULL \
+ && S_GET_SEGMENT ((fix)->fx_addsy) == \
+ S_GET_SEGMENT ((fix)->fx_subsy) \
+ && S_GET_SEGMENT (fix->fx_addsy) != undefined_section \
+ && ( (fix)->fx_r_type == BFD_RELOC_CR16_NUM8 \
+ || (fix)->fx_r_type == BFD_RELOC_CR16_NUM16 \
+ || (fix)->fx_r_type == BFD_RELOC_CR16_NUM32 \
+ || (fix)->fx_r_type == BFD_RELOC_CR16_NUM32a))
+
+/* 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
+cr16_force_relocation (fixS *fix)
+{
+ if (generic_force_reloc (fix) || SWITCH_TABLE (fix))
+ return 1;
+
+ return 0;
+}
+
+/* Record a fixup for a cons expression. */
+
+void
+cr16_cons_fix_new (fragS *frag, int offset, int len, expressionS *exp,
+ bfd_reloc_code_real_type rtype)
+{
+ switch (len)
+ {
+ default: rtype = BFD_RELOC_NONE; break;
+ case 1: rtype = BFD_RELOC_CR16_NUM8 ; break;
+ case 2: rtype = BFD_RELOC_CR16_NUM16; break;
+ case 4:
+ if (code_label)
+ {
+ rtype = BFD_RELOC_CR16_NUM32a;
+ code_label = 0;
+ }
+ else
+ rtype = BFD_RELOC_CR16_NUM32;
+ break;
+ }
+
+ fix_new_exp (frag, offset, len, exp, 0, rtype);
+}
+
+/* Generate a relocation entry for a fixup. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
+{
+ arelent * reloc;
+
+ /* If symbols are local and resolved, then no relocation needed. */
+ if ( ((fixP->fx_addsy)
+ && (S_GET_SEGMENT (fixP->fx_addsy) == absolute_section))
+ || ((fixP->fx_subsy)
+ && (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)))
+ return NULL;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ reloc->addend = fixP->fx_offset;
+
+ if (fixP->fx_subsy != NULL)
+ {
+ if (SWITCH_TABLE (fixP))
+ {
+ /* Keep the current difference in the addend. */
+ reloc->addend = (S_GET_VALUE (fixP->fx_addsy)
+ - S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_CR16_NUM8:
+ fixP->fx_r_type = BFD_RELOC_CR16_SWITCH8;
+ break;
+ case BFD_RELOC_CR16_NUM16:
+ fixP->fx_r_type = BFD_RELOC_CR16_SWITCH16;
+ break;
+ case BFD_RELOC_CR16_NUM32:
+ fixP->fx_r_type = BFD_RELOC_CR16_SWITCH32;
+ break;
+ case BFD_RELOC_CR16_NUM32a:
+ fixP->fx_r_type = BFD_RELOC_CR16_NUM32a;
+ break;
+ default:
+ abort ();
+ break;
+ }
+ }
+ else
+ {
+ /* We only resolve difference expressions in the same section. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("can't resolve `%s' {%s section} - `%s' {%s section}"),
+ fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
+ segment_name (fixP->fx_addsy
+ ? S_GET_SEGMENT (fixP->fx_addsy)
+ : absolute_section),
+ S_GET_NAME (fixP->fx_subsy),
+ segment_name (S_GET_SEGMENT (fixP->fx_addsy)));
+ }
+ }
+#ifdef OBJ_ELF
+ if ((fixP->fx_r_type == BFD_RELOC_CR16_GOT_REGREL20)
+ && GOT_symbol
+ && fixP->fx_addsy == GOT_symbol)
+ {
+ reloc->addend = fixP->fx_offset = reloc->address;
+ }
+ else if ((fixP->fx_r_type == BFD_RELOC_CR16_GOTC_REGREL20)
+ && GOT_symbol
+ && fixP->fx_addsy == GOT_symbol)
+ {
+ reloc->addend = fixP->fx_offset = reloc->address;
+ }
+#endif
+
+ gas_assert ((int) fixP->fx_r_type > 0);
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: reloc %d (`%s') not supported by object file format"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ return reloc;
+}
+
+/* Prepare machine-dependent frags for relaxation. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *seg)
+{
+ /* If symbol is undefined or located in a different section,
+ select the largest supported relocation. */
+ relax_substateT subtype;
+ relax_substateT rlx_state[] = {0, 2};
+
+ for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
+ {
+ if (fragp->fr_subtype == rlx_state[subtype]
+ && (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol)))
+ {
+ fragp->fr_subtype = rlx_state[subtype + 1];
+ break;
+ }
+ }
+
+ if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
+{
+ /* 'opcode' points to the start of the instruction, whether
+ we need to change the instruction's fixed encoding. */
+ char *opcode = fragP->fr_literal + fragP->fr_fix;
+ bfd_reloc_code_real_type reloc;
+
+ subseg_change (sec, 0);
+
+ switch (fragP->fr_subtype)
+ {
+ case 0:
+ reloc = BFD_RELOC_CR16_DISP8;
+ break;
+ case 1:
+ /* If the subtype is not changed due to :m operand qualifier,
+ then no need to update the opcode value. */
+ if ((int)opcode[1] != 0x18)
+ {
+ opcode[0] = (opcode[0] & 0xf0);
+ opcode[1] = 0x18;
+ }
+ reloc = BFD_RELOC_CR16_DISP16;
+ break;
+ case 2:
+ /* If the subtype is not changed due to :l operand qualifier,
+ then no need to update the opcode value. */
+ if ((int)opcode[1] != 0)
+ {
+ opcode[2] = opcode[0];
+ opcode[0] = opcode[1];
+ opcode[1] = 0x0;
+ }
+ reloc = BFD_RELOC_CR16_DISP24;
+ break;
+ default:
+ abort();
+ }
+
+ fix_new (fragP, fragP->fr_fix,
+ bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)),
+ fragP->fr_symbol, fragP->fr_offset, 1, reloc);
+ fragP->fr_var = 0;
+ fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ if (*name == '_' && *(name + 1) == 'G'
+ && strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in symbol table"));
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ }
+ return GOT_symbol;
+ }
+ return 0;
+}
+
+/* Process machine-dependent command line options. Called once for
+ each option on the command line that the machine-independent part of
+ GAS does not understand. */
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Machine-dependent usage-output. */
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+ return;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag.
+ Since linkrelax is nonzero and TC_LINKRELAX_FIXUP is defined to disable
+ relaxation of debug sections, this function is called only when
+ fixuping relocations of debug sections. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ valueT val = * valP;
+
+ if (fixP->fx_addsy == NULL
+ && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+ else if (fixP->fx_pcrel == 1
+ && fixP->fx_addsy != NULL
+ && S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ fixP->fx_done = 1;
+ else
+ fixP->fx_done = 0;
+
+ if (fixP->fx_addsy != NULL && !fixP->fx_pcrel)
+ {
+ val = fixP->fx_offset;
+ fixP->fx_done = 1;
+ }
+
+ if (fixP->fx_done)
+ {
+ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ fixP->fx_offset = 0;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_CR16_NUM8:
+ bfd_put_8 (stdoutput, (unsigned char) val, buf);
+ break;
+ case BFD_RELOC_CR16_NUM16:
+ bfd_put_16 (stdoutput, val, buf);
+ break;
+ case BFD_RELOC_CR16_NUM32:
+ bfd_put_32 (stdoutput, val, buf);
+ break;
+ case BFD_RELOC_CR16_NUM32a:
+ bfd_put_32 (stdoutput, val, buf);
+ break;
+ default:
+ /* We shouldn't ever get here because linkrelax is nonzero. */
+ abort ();
+ break;
+ }
+ fixP->fx_done = 0;
+ }
+ else
+ fixP->fx_offset = * valP;
+}
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixS *fixp)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+static void
+initialise_reg_hash_table (struct hash_control ** hash_table,
+ const reg_entry * register_table,
+ const unsigned int num_entries)
+{
+ const reg_entry * rreg;
+ const char *hashret;
+
+ if ((* hash_table = hash_new ()) == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ for (rreg = register_table;
+ rreg < (register_table + num_entries);
+ rreg++)
+ {
+ hashret = hash_insert (* hash_table, rreg->name, (char *) rreg);
+ if (hashret)
+ as_fatal (_("Internal Error: Can't hash %s: %s"),
+ rreg->name, hashret);
+ }
+}
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs. */
+
+void
+md_begin (void)
+{
+ int i = 0;
+
+ /* Set up a hash table for the instructions. */
+ if ((cr16_inst_hash = hash_new ()) == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ while (cr16_instruction[i].mnemonic != NULL)
+ {
+ const char *hashret;
+ const char *mnemonic = cr16_instruction[i].mnemonic;
+
+ hashret = hash_insert (cr16_inst_hash, mnemonic,
+ (char *)(cr16_instruction + i));
+
+ if (hashret != NULL && *hashret != '\0')
+ as_fatal (_("Can't hash `%s': %s\n"), cr16_instruction[i].mnemonic,
+ *hashret == 0 ? _("(unknown reason)") : hashret);
+
+ /* Insert unique names into hash table. The CR16 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+ do
+ {
+ ++i;
+ }
+ while (cr16_instruction[i].mnemonic != NULL
+ && streq (cr16_instruction[i].mnemonic, mnemonic));
+ }
+
+ /* Initialize reg_hash hash table. */
+ initialise_reg_hash_table (& reg_hash, cr16_regtab, NUMREGS);
+ /* Initialize regp_hash hash table. */
+ initialise_reg_hash_table (& regp_hash, cr16_regptab, NUMREGPS);
+ /* Initialize preg_hash hash table. */
+ initialise_reg_hash_table (& preg_hash, cr16_pregtab, NUMPREGS);
+ /* Initialize pregp_hash hash table. */
+ initialise_reg_hash_table (& pregp_hash, cr16_pregptab, NUMPREGPS);
+
+ /* Set linkrelax here to avoid fixups in most sections. */
+ linkrelax = 1;
+}
+
+/* Process constants (immediate/absolute)
+ and labels (jump targets/Memory locations). */
+
+static void
+process_label_constant (char *str, ins * cr16_ins)
+{
+ char *saved_input_line_pointer;
+ int symbol_with_at = 0;
+ int symbol_with_s = 0;
+ int symbol_with_m = 0;
+ int symbol_with_l = 0;
+ int symbol_with_at_got = 0;
+ int symbol_with_at_gotc = 0;
+ argument *cur_arg = cr16_ins->arg + cur_arg_num; /* Current argument. */
+
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+
+ expression (&cr16_ins->exp);
+
+ switch (cr16_ins->exp.X_op)
+ {
+ case O_big:
+ case O_absent:
+ /* Missing or bad expr becomes absolute 0. */
+ as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
+ str);
+ cr16_ins->exp.X_op = O_constant;
+ cr16_ins->exp.X_add_number = 0;
+ cr16_ins->exp.X_add_symbol = NULL;
+ cr16_ins->exp.X_op_symbol = NULL;
+ /* Fall through. */
+
+ case O_constant:
+ cur_arg->X_op = O_constant;
+ cur_arg->constant = cr16_ins->exp.X_add_number;
+ break;
+
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ cur_arg->X_op = O_symbol;
+ cur_arg->constant = cr16_ins->exp.X_add_number;
+ cr16_ins->exp.X_add_number = 0;
+ cr16_ins->rtype = BFD_RELOC_NONE;
+ relocatable = 1;
+
+ if (strneq (input_line_pointer, "@c", 2))
+ symbol_with_at = 1;
+
+ if (strneq (input_line_pointer, "@l", 2)
+ || strneq (input_line_pointer, ":l", 2))
+ symbol_with_l = 1;
+
+ if (strneq (input_line_pointer, "@m", 2)
+ || strneq (input_line_pointer, ":m", 2))
+ symbol_with_m = 1;
+
+ if (strneq (input_line_pointer, "@s", 2)
+ || strneq (input_line_pointer, ":s", 2))
+ symbol_with_s = 1;
+
+ if (strneq (input_line_pointer, "@cGOT", 5)
+ || strneq (input_line_pointer, "@cgot", 5))
+ {
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ symbol_with_at_gotc = 1;
+ }
+ else if (strneq (input_line_pointer, "@GOT", 4)
+ || strneq (input_line_pointer, "@got", 4))
+ {
+ if ((strneq (input_line_pointer, "+", 1))
+ || (strneq (input_line_pointer, "-", 1)))
+ as_warn (_("GOT bad expression with %s."), input_line_pointer);
+
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ symbol_with_at_got = 1;
+ }
+
+ switch (cur_arg->type)
+ {
+ case arg_cr:
+ if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
+ {
+ if (symbol_with_at_got)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+ else if (symbol_with_at_gotc)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+ else if (cur_arg->size == 20)
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
+ else
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a;
+ }
+ break;
+
+ case arg_crp:
+ if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
+ {
+ if (symbol_with_at_got)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+ else if (symbol_with_at_gotc)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+ } else {
+ switch (instruction->size)
+ {
+ case 1:
+ switch (cur_arg->size)
+ {
+ case 0:
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL0;
+ break;
+ case 4:
+ if (IS_INSN_MNEMONIC ("loadb") || IS_INSN_MNEMONIC ("storb"))
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL4;
+ else
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL4a;
+ break;
+ default: break;
+ }
+ break;
+ case 2:
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL16;
+ break;
+ case 3:
+ if (cur_arg->size == 20)
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
+ else
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL20a;
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+
+ case arg_idxr:
+ if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
+ {
+ if (symbol_with_at_got)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+ else if (symbol_with_at_gotc)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+ else
+ cr16_ins->rtype = BFD_RELOC_CR16_REGREL20;
+ }
+ break;
+
+ case arg_idxrp:
+ if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (CSTBIT_INS))
+ {
+ if (symbol_with_at_got)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+ else if (symbol_with_at_gotc)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+ else {
+ switch (instruction->size)
+ {
+ case 1: cr16_ins->rtype = BFD_RELOC_CR16_REGREL0; break;
+ case 2: cr16_ins->rtype = BFD_RELOC_CR16_REGREL14; break;
+ case 3: cr16_ins->rtype = BFD_RELOC_CR16_REGREL20; break;
+ default: break;
+ }
+ }
+ }
+ break;
+
+ case arg_c:
+ if (IS_INSN_MNEMONIC ("bal"))
+ cr16_ins->rtype = BFD_RELOC_CR16_DISP24;
+ else if (IS_INSN_TYPE (BRANCH_INS))
+ {
+ if (symbol_with_l)
+ cr16_ins->rtype = BFD_RELOC_CR16_DISP24;
+ else if (symbol_with_m)
+ cr16_ins->rtype = BFD_RELOC_CR16_DISP16;
+ else
+ cr16_ins->rtype = BFD_RELOC_CR16_DISP8;
+ }
+ else if (IS_INSN_TYPE (STOR_IMM_INS) || IS_INSN_TYPE (LD_STOR_INS)
+ || IS_INSN_TYPE (CSTBIT_INS))
+ {
+ if (symbol_with_s)
+ as_bad (_("operand %d: illegal use expression: `%s`"), cur_arg_num + 1, str);
+ if (symbol_with_at_got)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+ else if (symbol_with_at_gotc)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+ else if (symbol_with_m)
+ cr16_ins->rtype = BFD_RELOC_CR16_ABS20;
+ else /* Default to (symbol_with_l) */
+ cr16_ins->rtype = BFD_RELOC_CR16_ABS24;
+ }
+ else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
+ cr16_ins->rtype = BFD_RELOC_CR16_DISP4;
+ break;
+
+ case arg_ic:
+ if (IS_INSN_TYPE (ARITH_INS))
+ {
+ if (symbol_with_at_got)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOT_REGREL20;
+ else if (symbol_with_at_gotc)
+ cr16_ins->rtype = BFD_RELOC_CR16_GOTC_REGREL20;
+ else if (symbol_with_s)
+ cr16_ins->rtype = BFD_RELOC_CR16_IMM4;
+ else if (symbol_with_m)
+ cr16_ins->rtype = BFD_RELOC_CR16_IMM20;
+ else if (symbol_with_at)
+ cr16_ins->rtype = BFD_RELOC_CR16_IMM32a;
+ else /* Default to (symbol_with_l) */
+ cr16_ins->rtype = BFD_RELOC_CR16_IMM32;
+ }
+ else if (IS_INSN_TYPE (ARITH_BYTE_INS))
+ {
+ cr16_ins->rtype = BFD_RELOC_CR16_IMM16;
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ cur_arg->X_op = cr16_ins->exp.X_op;
+ break;
+ }
+
+ input_line_pointer = saved_input_line_pointer;
+ return;
+}
+
+/* Retrieve the opcode image of a given register.
+ If the register is illegal for the current instruction,
+ issue an error. */
+
+static int
+getreg_image (reg r)
+{
+ const reg_entry *rreg;
+ char *reg_name;
+ int is_procreg = 0; /* Nonzero means argument should be processor reg. */
+
+ /* Check whether the register is in registers table. */
+ if (r < MAX_REG)
+ rreg = cr16_regtab + r;
+ else /* Register not found. */
+ {
+ as_bad (_("Unknown register: `%d'"), r);
+ return 0;
+ }
+
+ reg_name = rreg->name;
+
+/* Issue a error message when register is illegal. */
+#define IMAGE_ERR \
+ as_bad (_("Illegal register (`%s') in Instruction: `%s'"), \
+ reg_name, ins_parse); \
+ break;
+
+ switch (rreg->type)
+ {
+ case CR16_R_REGTYPE:
+ if (! is_procreg)
+ return rreg->image;
+ else
+ IMAGE_ERR;
+
+ case CR16_P_REGTYPE:
+ return rreg->image;
+ break;
+
+ default:
+ IMAGE_ERR;
+ }
+
+ return 0;
+}
+
+/* Parsing different types of operands
+ -> constants Immediate/Absolute/Relative numbers
+ -> Labels Relocatable symbols
+ -> (reg pair base) Register pair base
+ -> (rbase) Register base
+ -> disp(rbase) Register relative
+ -> [rinx]disp(reg pair) Register index with reg pair mode
+ -> disp(rbase,ridx,scl) Register index mode. */
+
+static void
+set_operand (char *operand, ins * cr16_ins)
+{
+ char *operandS; /* Pointer to start of sub-opearand. */
+ char *operandE; /* Pointer to end of sub-opearand. */
+
+ argument *cur_arg = &cr16_ins->arg[cur_arg_num]; /* Current argument. */
+
+ /* Initialize pointers. */
+ operandS = operandE = operand;
+
+ switch (cur_arg->type)
+ {
+ case arg_ic: /* Case $0x18. */
+ operandS++;
+ case arg_c: /* Case 0x18. */
+ /* Set constant. */
+ process_label_constant (operandS, cr16_ins);
+
+ if (cur_arg->type != arg_ic)
+ cur_arg->type = arg_c;
+ break;
+
+ case arg_icr: /* Case $0x18(r1). */
+ operandS++;
+ case arg_cr: /* Case 0x18(r1). */
+ /* Set displacement constant. */
+ while (*operandE != '(')
+ operandE++;
+ *operandE = '\0';
+ process_label_constant (operandS, cr16_ins);
+ operandS = operandE;
+ case arg_rbase: /* Case (r1) or (r1,r0). */
+ operandS++;
+ /* Set register base. */
+ while (*operandE != ')')
+ operandE++;
+ *operandE = '\0';
+ if ((cur_arg->r = get_register (operandS)) == nullregister)
+ as_bad (_("Illegal register `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+
+ /* set the arg->rp, if reg is "r12" or "r13" or "14" or "15" */
+ if ((cur_arg->type != arg_rbase)
+ && ((getreg_image (cur_arg->r) == 12)
+ || (getreg_image (cur_arg->r) == 13)
+ || (getreg_image (cur_arg->r) == 14)
+ || (getreg_image (cur_arg->r) == 15)))
+ {
+ cur_arg->type = arg_crp;
+ cur_arg->rp = cur_arg->r;
+ }
+ break;
+
+ case arg_crp: /* Case 0x18(r1,r0). */
+ /* Set displacement constant. */
+ while (*operandE != '(')
+ operandE++;
+ *operandE = '\0';
+ process_label_constant (operandS, cr16_ins);
+ operandS = operandE;
+ operandS++;
+ /* Set register pair base. */
+ while (*operandE != ')')
+ operandE++;
+ *operandE = '\0';
+ if ((cur_arg->rp = get_register_pair (operandS)) == nullregister)
+ as_bad (_("Illegal register pair `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+ break;
+
+ case arg_idxr:
+ /* Set register pair base. */
+ if ((strchr (operandS,'(') != NULL))
+ {
+ while ((*operandE != '(') && (! ISSPACE (*operandE)))
+ operandE++;
+ if ((cur_arg->rp = get_index_register_pair (operandE)) == nullregister)
+ as_bad (_("Illegal register pair `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+ *operandE++ = '\0';
+ cur_arg->type = arg_idxrp;
+ }
+ else
+ cur_arg->rp = -1;
+
+ operandE = operandS;
+ /* Set displacement constant. */
+ while (*operandE != ']')
+ operandE++;
+ process_label_constant (++operandE, cr16_ins);
+ *operandE++ = '\0';
+ operandE = operandS;
+
+ /* Set index register . */
+ operandS = strchr (operandE,'[');
+ if (operandS != NULL)
+ { /* Eliminate '[', detach from rest of operand. */
+ *operandS++ = '\0';
+
+ operandE = strchr (operandS, ']');
+
+ if (operandE == NULL)
+ as_bad (_("unmatched '['"));
+ else
+ { /* Eliminate ']' and make sure it was the last thing
+ in the string. */
+ *operandE = '\0';
+ if (*(operandE + 1) != '\0')
+ as_bad (_("garbage after index spec ignored"));
+ }
+ }
+
+ if ((cur_arg->i_r = get_index_register (operandS)) == nullregister)
+ as_bad (_("Illegal register `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+ *operandE = '\0';
+ *operandS = '\0';
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Parse a single operand.
+ operand - Current operand to parse.
+ cr16_ins - Current assembled instruction. */
+
+static void
+parse_operand (char *operand, ins * cr16_ins)
+{
+ int ret_val;
+ argument *cur_arg = cr16_ins->arg + cur_arg_num; /* Current argument. */
+
+ /* Initialize the type to NULL before parsing. */
+ cur_arg->type = nullargs;
+
+ /* Check whether this is a condition code . */
+ if ((IS_INSN_MNEMONIC ("b")) && ((ret_val = get_cc (operand)) != -1))
+ {
+ cur_arg->type = arg_cc;
+ cur_arg->cc = ret_val;
+ cur_arg->X_op = O_register;
+ return;
+ }
+
+ /* Check whether this is a general processor register. */
+ if ((ret_val = get_register (operand)) != nullregister)
+ {
+ cur_arg->type = arg_r;
+ cur_arg->r = ret_val;
+ cur_arg->X_op = 0;
+ return;
+ }
+
+ /* Check whether this is a general processor register pair. */
+ if ((operand[0] == '(')
+ && ((ret_val = get_register_pair (operand)) != nullregister))
+ {
+ cur_arg->type = arg_rp;
+ cur_arg->rp = ret_val;
+ cur_arg->X_op = O_register;
+ return;
+ }
+
+ /* Check whether the operand is a processor register.
+ For "lprd" and "sprd" instruction, only 32 bit
+ processor registers used. */
+ if (!(IS_INSN_MNEMONIC ("lprd") || (IS_INSN_MNEMONIC ("sprd")))
+ && ((ret_val = get_pregister (operand)) != nullpregister))
+ {
+ cur_arg->type = arg_pr;
+ cur_arg->pr = ret_val;
+ cur_arg->X_op = O_register;
+ return;
+ }
+
+ /* Check whether this is a processor register - 32 bit. */
+ if ((ret_val = get_pregisterp (operand)) != nullpregister)
+ {
+ cur_arg->type = arg_prp;
+ cur_arg->prp = ret_val;
+ cur_arg->X_op = O_register;
+ return;
+ }
+
+ /* Deal with special characters. */
+ switch (operand[0])
+ {
+ case '$':
+ if (strchr (operand, '(') != NULL)
+ cur_arg->type = arg_icr;
+ else
+ cur_arg->type = arg_ic;
+ goto set_params;
+ break;
+
+ case '(':
+ cur_arg->type = arg_rbase;
+ goto set_params;
+ break;
+
+ case '[':
+ cur_arg->type = arg_idxr;
+ goto set_params;
+ break;
+
+ default:
+ break;
+ }
+
+ if (strchr (operand, '(') != NULL)
+ {
+ if (strchr (operand, ',') != NULL
+ && (strchr (operand, ',') > strchr (operand, '(')))
+ cur_arg->type = arg_crp;
+ else
+ cur_arg->type = arg_cr;
+ }
+ else
+ cur_arg->type = arg_c;
+
+/* Parse an operand according to its type. */
+ set_params:
+ cur_arg->constant = 0;
+ set_operand (operand, cr16_ins);
+}
+
+/* Parse the various operands. Each operand is then analyzed to fillup
+ the fields in the cr16_ins data structure. */
+
+static void
+parse_operands (ins * cr16_ins, char *operands)
+{
+ char *operandS; /* Operands string. */
+ char *operandH, *operandT; /* Single operand head/tail pointers. */
+ int allocated = 0; /* Indicates a new operands string was allocated.*/
+ char *operand[MAX_OPERANDS];/* Separating the operands. */
+ int op_num = 0; /* Current operand number we are parsing. */
+ int bracket_flag = 0; /* Indicates a bracket '(' was found. */
+ int sq_bracket_flag = 0; /* Indicates a square bracket '[' was found. */
+
+ /* Preprocess the list of registers, if necessary. */
+ operandS = operandH = operandT = operands;
+
+ while (*operandT != '\0')
+ {
+ if (*operandT == ',' && bracket_flag != 1 && sq_bracket_flag != 1)
+ {
+ *operandT++ = '\0';
+ operand[op_num++] = strdup (operandH);
+ operandH = operandT;
+ continue;
+ }
+
+ if (*operandT == ' ')
+ as_bad (_("Illegal operands (whitespace): `%s'"), ins_parse);
+
+ if (*operandT == '(')
+ bracket_flag = 1;
+ else if (*operandT == '[')
+ sq_bracket_flag = 1;
+
+ if (*operandT == ')')
+ {
+ if (bracket_flag)
+ bracket_flag = 0;
+ else
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+ }
+ else if (*operandT == ']')
+ {
+ if (sq_bracket_flag)
+ sq_bracket_flag = 0;
+ else
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+ }
+
+ if (bracket_flag == 1 && *operandT == ')')
+ bracket_flag = 0;
+ else if (sq_bracket_flag == 1 && *operandT == ']')
+ sq_bracket_flag = 0;
+
+ operandT++;
+ }
+
+ /* Adding the last operand. */
+ operand[op_num++] = strdup (operandH);
+ cr16_ins->nargs = op_num;
+
+ /* Verifying correct syntax of operands (all brackets should be closed). */
+ if (bracket_flag || sq_bracket_flag)
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+
+ /* Now we parse each operand separately. */
+ for (op_num = 0; op_num < cr16_ins->nargs; op_num++)
+ {
+ cur_arg_num = op_num;
+ parse_operand (operand[op_num], cr16_ins);
+ free (operand[op_num]);
+ }
+
+ if (allocated)
+ free (operandS);
+}
+
+/* Get the trap index in dispatch table, given its name.
+ This routine is used by assembling the 'excp' instruction. */
+
+static int
+gettrap (char *s)
+{
+ const trap_entry *trap;
+
+ for (trap = cr16_traps; trap < (cr16_traps + NUMTRAPS); trap++)
+ if (strcasecmp (trap->name, s) == 0)
+ return trap->entry;
+
+ /* To make compatable with CR16 4.1 tools, the below 3-lines of
+ * code added. Refer: Development Tracker item #123 */
+ for (trap = cr16_traps; trap < (cr16_traps + NUMTRAPS); trap++)
+ if (trap->entry == (unsigned int) atoi (s))
+ return trap->entry;
+
+ as_bad (_("Unknown exception: `%s'"), s);
+ return 0;
+}
+
+/* Top level module where instruction parsing starts.
+ cr16_ins - data structure holds some information.
+ operands - holds the operands part of the whole instruction. */
+
+static void
+parse_insn (ins *insn, char *operands)
+{
+ int i;
+
+ /* Handle instructions with no operands. */
+ for (i = 0; cr16_no_op_insn[i] != NULL; i++)
+ {
+ if (streq (cr16_no_op_insn[i], instruction->mnemonic))
+ {
+ insn->nargs = 0;
+ return;
+ }
+ }
+
+ /* Handle 'excp' instructions. */
+ if (IS_INSN_MNEMONIC ("excp"))
+ {
+ insn->nargs = 1;
+ insn->arg[0].type = arg_ic;
+ insn->arg[0].constant = gettrap (operands);
+ insn->arg[0].X_op = O_constant;
+ return;
+ }
+
+ if (operands != NULL)
+ parse_operands (insn, operands);
+}
+
+/* bCC instruction requires special handling. */
+static char *
+get_b_cc (char * op)
+{
+ unsigned int i;
+ char op1[5];
+
+ for (i = 1; i < strlen (op); i++)
+ op1[i-1] = op[i];
+
+ op1[i-1] = '\0';
+
+ for (i = 0; i < cr16_num_cc ; i++)
+ if (streq (op1, cr16_b_cond_tab[i]))
+ return (char *) cr16_b_cond_tab[i];
+
+ return NULL;
+}
+
+/* bCC instruction requires special handling. */
+static int
+is_bcc_insn (char * op)
+{
+ if (!(streq (op, "bal") || streq (op, "beq0b") || streq (op, "bnq0b")
+ || streq (op, "beq0w") || streq (op, "bnq0w")))
+ if ((op[0] == 'b') && (get_b_cc (op) != NULL))
+ return 1;
+ return 0;
+}
+
+/* Cinv instruction requires special handling. */
+
+static void
+check_cinv_options (char * operand)
+{
+ char *p = operand;
+
+ while (*++p != ']')
+ {
+ switch (*p)
+ {
+ case ',':
+ case ' ':
+ case 'i':
+ case 'u':
+ case 'd':
+ break;
+ default:
+ as_bad (_("Illegal `cinv' parameter: `%c'"), *p);
+ }
+ }
+}
+
+/* Retrieve the opcode image of a given register pair.
+ If the register is illegal for the current instruction,
+ issue an error. */
+
+static int
+getregp_image (reg r)
+{
+ const reg_entry *rreg;
+ char *reg_name;
+
+ /* Check whether the register is in registers table. */
+ if (r < MAX_REG)
+ rreg = cr16_regptab + r;
+ /* Register not found. */
+ else
+ {
+ as_bad (_("Unknown register pair: `%d'"), r);
+ return 0;
+ }
+
+ reg_name = rreg->name;
+
+/* Issue a error message when register pair is illegal. */
+#define RPAIR_IMAGE_ERR \
+ as_bad (_("Illegal register pair (`%s') in Instruction: `%s'"), \
+ reg_name, ins_parse); \
+ break;
+
+ switch (rreg->type)
+ {
+ case CR16_RP_REGTYPE:
+ return rreg->image;
+ default:
+ RPAIR_IMAGE_ERR;
+ }
+
+ return 0;
+}
+
+/* Retrieve the opcode image of a given index register pair.
+ If the register is illegal for the current instruction,
+ issue an error. */
+
+static int
+getidxregp_image (reg r)
+{
+ const reg_entry *rreg;
+ char *reg_name;
+
+ /* Check whether the register is in registers table. */
+ if (r < MAX_REG)
+ rreg = cr16_regptab + r;
+ /* Register not found. */
+ else
+ {
+ as_bad (_("Unknown register pair: `%d'"), r);
+ return 0;
+ }
+
+ reg_name = rreg->name;
+
+/* Issue a error message when register pair is illegal. */
+#define IDX_RPAIR_IMAGE_ERR \
+ as_bad (_("Illegal index register pair (`%s') in Instruction: `%s'"), \
+ reg_name, ins_parse); \
+
+ if (rreg->type == CR16_RP_REGTYPE)
+ {
+ switch (rreg->image)
+ {
+ case 0: return 0; break;
+ case 2: return 1; break;
+ case 4: return 2; break;
+ case 6: return 3; break;
+ case 8: return 4; break;
+ case 10: return 5; break;
+ case 3: return 6; break;
+ case 5: return 7; break;
+ default:
+ break;
+ }
+ }
+
+ IDX_RPAIR_IMAGE_ERR;
+ return 0;
+}
+
+/* Retrieve the opcode image of a given processort register.
+ If the register is illegal for the current instruction,
+ issue an error. */
+static int
+getprocreg_image (int r)
+{
+ const reg_entry *rreg;
+ char *reg_name;
+
+ /* Check whether the register is in registers table. */
+ if (r >= MAX_REG && r < MAX_PREG)
+ rreg = &cr16_pregtab[r - MAX_REG];
+ /* Register not found. */
+ else
+ {
+ as_bad (_("Unknown processor register : `%d'"), r);
+ return 0;
+ }
+
+ reg_name = rreg->name;
+
+/* Issue a error message when register pair is illegal. */
+#define PROCREG_IMAGE_ERR \
+ as_bad (_("Illegal processor register (`%s') in Instruction: `%s'"), \
+ reg_name, ins_parse); \
+ break;
+
+ switch (rreg->type)
+ {
+ case CR16_P_REGTYPE:
+ return rreg->image;
+ default:
+ PROCREG_IMAGE_ERR;
+ }
+
+ return 0;
+}
+
+/* Retrieve the opcode image of a given processort register.
+ If the register is illegal for the current instruction,
+ issue an error. */
+static int
+getprocregp_image (int r)
+{
+ const reg_entry *rreg;
+ char *reg_name;
+ int pregptab_disp = 0;
+
+ /* Check whether the register is in registers table. */
+ if (r >= MAX_REG && r < MAX_PREG)
+ {
+ r = r - MAX_REG;
+ switch (r)
+ {
+ case 4: pregptab_disp = 1; break;
+ case 6: pregptab_disp = 2; break;
+ case 8:
+ case 9:
+ case 10:
+ pregptab_disp = 3; break;
+ case 12:
+ pregptab_disp = 4; break;
+ case 14:
+ pregptab_disp = 5; break;
+ default: break;
+ }
+ rreg = &cr16_pregptab[r - pregptab_disp];
+ }
+ /* Register not found. */
+ else
+ {
+ as_bad (_("Unknown processor register (32 bit) : `%d'"), r);
+ return 0;
+ }
+
+ reg_name = rreg->name;
+
+/* Issue a error message when register pair is illegal. */
+#define PROCREGP_IMAGE_ERR \
+ as_bad (_("Illegal 32 bit - processor register (`%s') in Instruction: `%s'"),\
+ reg_name, ins_parse); \
+ break;
+
+ switch (rreg->type)
+ {
+ case CR16_P_REGTYPE:
+ return rreg->image;
+ default:
+ PROCREGP_IMAGE_ERR;
+ }
+
+ return 0;
+}
+
+/* Routine used to represent integer X using NBITS bits. */
+
+static long
+getconstant (long x, int nbits)
+{
+ /* The following expression avoids overflow if
+ 'nbits' is the number of bits in 'bfd_vma'. */
+ return (x & ((((1 << (nbits - 1)) - 1) << 1) | 1));
+}
+
+/* Print a constant value to 'output_opcode':
+ ARG holds the operand's type and value.
+ SHIFT represents the location of the operand to be print into.
+ NBITS determines the size (in bits) of the constant. */
+
+static void
+print_constant (int nbits, int shift, argument *arg)
+{
+ unsigned long mask = 0;
+
+ long constant = getconstant (arg->constant, nbits);
+
+ switch (nbits)
+ {
+ case 32:
+ case 28:
+ /* mask the upper part of the constant, that is, the bits
+ going to the lowest byte of output_opcode[0].
+ The upper part of output_opcode[1] is always filled,
+ therefore it is always masked with 0xFFFF. */
+ mask = (1 << (nbits - 16)) - 1;
+ /* Divide the constant between two consecutive words :
+ 0 1 2 3
+ +---------+---------+---------+---------+
+ | | X X X X | x X x X | |
+ +---------+---------+---------+---------+
+ output_opcode[0] output_opcode[1] */
+
+ CR16_PRINT (0, (constant >> WORD_SHIFT) & mask, 0);
+ CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
+ break;
+
+ case 21:
+ if ((nbits == 21) && (IS_INSN_TYPE (LD_STOR_INS))) nbits = 20;
+ case 24:
+ case 22:
+ case 20:
+ /* mask the upper part of the constant, that is, the bits
+ going to the lowest byte of output_opcode[0].
+ The upper part of output_opcode[1] is always filled,
+ therefore it is always masked with 0xFFFF. */
+ mask = (1 << (nbits - 16)) - 1;
+ /* Divide the constant between two consecutive words :
+ 0 1 2 3
+ +---------+---------+---------+---------+
+ | | X X X X | - X - X | |
+ +---------+---------+---------+---------+
+ output_opcode[0] output_opcode[1] */
+
+ if ((instruction->size > 2) && (shift == WORD_SHIFT))
+ {
+ if (arg->type == arg_idxrp)
+ {
+ CR16_PRINT (0, ((constant >> WORD_SHIFT) & mask) << 8, 0);
+ CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
+ }
+ else
+ {
+ CR16_PRINT (0, (((((constant >> WORD_SHIFT) & mask) << 8) & 0x0f00) | ((((constant >> WORD_SHIFT) & mask) >> 4) & 0xf)),0);
+ CR16_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
+ }
+ }
+ else
+ CR16_PRINT (0, constant, shift);
+ break;
+
+ case 14:
+ if (arg->type == arg_idxrp)
+ {
+ if (instruction->size == 2)
+ {
+ CR16_PRINT (0, ((constant) & 0xf), shift); /* 0-3 bits. */
+ CR16_PRINT (0, ((constant >> 4) & 0x3), (shift + 20)); /* 4-5 bits. */
+ CR16_PRINT (0, ((constant >> 6) & 0x3), (shift + 14)); /* 6-7 bits. */
+ CR16_PRINT (0, ((constant >> 8) & 0x3f), (shift + 8)); /* 8-13 bits. */
+ }
+ else
+ CR16_PRINT (0, constant, shift);
+ }
+ break;
+
+ case 16:
+ case 12:
+ /* When instruction size is 3 and 'shift' is 16, a 16-bit constant is
+ always filling the upper part of output_opcode[1]. If we mistakenly
+ write it to output_opcode[0], the constant prefix (that is, 'match')
+ will be overriden.
+ 0 1 2 3
+ +---------+---------+---------+---------+
+ | 'match' | | X X X X | |
+ +---------+---------+---------+---------+
+ output_opcode[0] output_opcode[1] */
+
+ if ((instruction->size > 2) && (shift == WORD_SHIFT))
+ CR16_PRINT (1, constant, WORD_SHIFT);
+ else
+ CR16_PRINT (0, constant, shift);
+ break;
+
+ case 8:
+ CR16_PRINT (0, ((constant / 2) & 0xf), shift);
+ CR16_PRINT (0, ((constant / 2) >> 4), (shift + 8));
+ break;
+
+ default:
+ CR16_PRINT (0, constant, shift);
+ break;
+ }
+}
+
+/* Print an operand to 'output_opcode', which later on will be
+ printed to the object file:
+ ARG holds the operand's type, size and value.
+ SHIFT represents the printing location of operand.
+ NBITS determines the size (in bits) of a constant operand. */
+
+static void
+print_operand (int nbits, int shift, argument *arg)
+{
+ switch (arg->type)
+ {
+ case arg_cc:
+ CR16_PRINT (0, arg->cc, shift);
+ break;
+
+ case arg_r:
+ CR16_PRINT (0, getreg_image (arg->r), shift);
+ break;
+
+ case arg_rp:
+ CR16_PRINT (0, getregp_image (arg->rp), shift);
+ break;
+
+ case arg_pr:
+ CR16_PRINT (0, getprocreg_image (arg->pr), shift);
+ break;
+
+ case arg_prp:
+ CR16_PRINT (0, getprocregp_image (arg->prp), shift);
+ break;
+
+ case arg_idxrp:
+ /* 16 12 8 6 0
+ +-----------------------------+
+ | r_index | disp | rp_base |
+ +-----------------------------+ */
+
+ if (instruction->size == 3)
+ {
+ CR16_PRINT (0, getidxregp_image (arg->rp), 0);
+ if (getreg_image (arg->i_r) == 12)
+ CR16_PRINT (0, 0, 3);
+ else
+ CR16_PRINT (0, 1, 3);
+ }
+ else
+ {
+ CR16_PRINT (0, getidxregp_image (arg->rp), 16);
+ if (getreg_image (arg->i_r) == 12)
+ CR16_PRINT (0, 0, 19);
+ else
+ CR16_PRINT (0, 1, 19);
+ }
+ print_constant (nbits, shift, arg);
+ break;
+
+ case arg_idxr:
+ if (getreg_image (arg->i_r) == 12)
+ if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb")
+ || IS_INSN_MNEMONIC ("tbitb"))
+ CR16_PRINT (0, 0, 23);
+ else CR16_PRINT (0, 0, 24);
+ else
+ if (IS_INSN_MNEMONIC ("cbitb") || IS_INSN_MNEMONIC ("sbitb")
+ || IS_INSN_MNEMONIC ("tbitb"))
+ CR16_PRINT (0, 1, 23);
+ else CR16_PRINT (0, 1, 24);
+
+ print_constant (nbits, shift, arg);
+ break;
+
+ case arg_ic:
+ case arg_c:
+ print_constant (nbits, shift, arg);
+ break;
+
+ case arg_rbase:
+ CR16_PRINT (0, getreg_image (arg->r), shift);
+ break;
+
+ case arg_cr:
+ print_constant (nbits, shift , arg);
+ /* Add the register argument to the output_opcode. */
+ CR16_PRINT (0, getreg_image (arg->r), (shift+16));
+ break;
+
+ case arg_crp:
+ print_constant (nbits, shift , arg);
+ if (instruction->size > 1)
+ CR16_PRINT (0, getregp_image (arg->rp), (shift + 16));
+ else if (IS_INSN_TYPE (LD_STOR_INS) || (IS_INSN_TYPE (CSTBIT_INS)))
+ {
+ if (instruction->size == 2)
+ CR16_PRINT (0, getregp_image (arg->rp), (shift - 8));
+ else if (instruction->size == 1)
+ CR16_PRINT (0, getregp_image (arg->rp), 16);
+ }
+ else
+ CR16_PRINT (0, getregp_image (arg->rp), shift);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Retrieve the number of operands for the current assembled instruction. */
+
+static int
+get_number_of_operands (void)
+{
+ int i;
+
+ for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
+ ;
+ return i;
+}
+
+/* Verify that the number NUM can be represented in BITS bits (that is,
+ within its permitted range), based on the instruction's FLAGS.
+ If UPDATE is nonzero, update the value of NUM if necessary.
+ Return OP_LEGAL upon success, actual error type upon failure. */
+
+static op_err
+check_range (long *num, int bits, int unsigned flags, int update)
+{
+ long min, max;
+ int retval = OP_LEGAL;
+ long value = *num;
+
+ if (bits == 0 && value > 0) return OP_OUT_OF_RANGE;
+
+ /* For hosts witah longs bigger than 32-bits make sure that the top
+ bits of a 32-bit negative value read in by the parser are set,
+ so that the correct comparisons are made. */
+ if (value & 0x80000000)
+ value |= (-1L << 31);
+
+
+ /* Verify operand value is even. */
+ if (flags & OP_EVEN)
+ {
+ if (value % 2)
+ return OP_NOT_EVEN;
+ }
+
+ if (flags & OP_DEC)
+ {
+ value -= 1;
+ if (update)
+ *num = value;
+ }
+
+ if (flags & OP_SHIFT)
+ {
+ value >>= 1;
+ if (update)
+ *num = value;
+ }
+ else if (flags & OP_SHIFT_DEC)
+ {
+ value = (value >> 1) - 1;
+ if (update)
+ *num = value;
+ }
+
+ if (flags & OP_ABS20)
+ {
+ if (value > 0xEFFFF)
+ return OP_OUT_OF_RANGE;
+ }
+
+ if (flags & OP_ESC)
+ {
+ if (value == 0xB || value == 0x9)
+ return OP_OUT_OF_RANGE;
+ else if (value == -1)
+ {
+ if (update)
+ *num = 9;
+ return retval;
+ }
+ }
+
+ if (flags & OP_ESC1)
+ {
+ if (value > 13)
+ return OP_OUT_OF_RANGE;
+ }
+
+ if (flags & OP_SIGNED)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ if ((value > max) || (value < min))
+ retval = OP_OUT_OF_RANGE;
+ }
+ else if (flags & OP_UNSIGNED)
+ {
+ max = ((((1 << (bits - 1)) - 1) << 1) | 1);
+ min = 0;
+ if (((unsigned long) value > (unsigned long) max)
+ || ((unsigned long) value < (unsigned long) min))
+ retval = OP_OUT_OF_RANGE;
+ }
+ else if (flags & OP_NEG)
+ {
+ max = - 1;
+ min = - ((1 << (bits - 1)) - 1);
+ if ((value > max) || (value < min))
+ retval = OP_OUT_OF_RANGE;
+ }
+ return retval;
+}
+
+/* Bunch of error checkings.
+ The checks are made after a matching instruction was found. */
+
+static void
+warn_if_needed (ins *insn)
+{
+ /* If the post-increment address mode is used and the load/store
+ source register is the same as rbase, the result of the
+ instruction is undefined. */
+ if (IS_INSN_TYPE (LD_STOR_INS_INC))
+ {
+ /* Enough to verify that one of the arguments is a simple reg. */
+ if ((insn->arg[0].type == arg_r) || (insn->arg[1].type == arg_r))
+ if (insn->arg[0].r == insn->arg[1].r)
+ as_bad (_("Same src/dest register is used (`r%d'), result is undefined"), insn->arg[0].r);
+ }
+
+ if (IS_INSN_MNEMONIC ("pop")
+ || IS_INSN_MNEMONIC ("push")
+ || IS_INSN_MNEMONIC ("popret"))
+ {
+ unsigned int count = insn->arg[0].constant, reg_val;
+
+ /* Check if count operand caused to save/retrive the RA twice
+ to generate warning message. */
+ if (insn->nargs > 2)
+ {
+ reg_val = getreg_image (insn->arg[1].r);
+
+ if ( ((reg_val == 9) && (count > 7))
+ || ((reg_val == 10) && (count > 6))
+ || ((reg_val == 11) && (count > 5))
+ || ((reg_val == 12) && (count > 4))
+ || ((reg_val == 13) && (count > 2))
+ || ((reg_val == 14) && (count > 0)))
+ as_warn (_("RA register is saved twice."));
+
+ /* Check if the third operand is "RA" or "ra" */
+ if (!(((insn->arg[2].r) == ra) || ((insn->arg[2].r) == RA)))
+ as_bad (_("`%s' Illegal use of registers."), ins_parse);
+ }
+
+ if (insn->nargs > 1)
+ {
+ reg_val = getreg_image (insn->arg[1].r);
+
+ /* If register is a register pair ie r12/r13/r14 in operand1, then
+ the count constant should be validated. */
+ if (((reg_val == 11) && (count > 7))
+ || ((reg_val == 12) && (count > 6))
+ || ((reg_val == 13) && (count > 4))
+ || ((reg_val == 14) && (count > 2))
+ || ((reg_val == 15) && (count > 0)))
+ as_bad (_("`%s' Illegal count-register combination."), ins_parse);
+ }
+ else
+ {
+ /* Check if the operand is "RA" or "ra" */
+ if (!(((insn->arg[0].r) == ra) || ((insn->arg[0].r) == RA)))
+ as_bad (_("`%s' Illegal use of register."), ins_parse);
+ }
+ }
+
+ /* Some instruction assume the stack pointer as rptr operand.
+ Issue an error when the register to be loaded is also SP. */
+ if (instruction->flags & NO_SP)
+ {
+ if (getreg_image (insn->arg[1].r) == getreg_image (sp))
+ as_bad (_("`%s' has undefined result"), ins_parse);
+ }
+
+ /* If the rptr register is specified as one of the registers to be loaded,
+ the final contents of rptr are undefined. Thus, we issue an error. */
+ if (instruction->flags & NO_RPTR)
+ {
+ if ((1 << getreg_image (insn->arg[0].r)) & insn->arg[1].constant)
+ as_bad (_("Same src/dest register is used (`r%d'),result is undefined"),
+ getreg_image (insn->arg[0].r));
+ }
+}
+
+/* In some cases, we need to adjust the instruction pointer although a
+ match was already found. Here, we gather all these cases.
+ Returns 1 if instruction pointer was adjusted, otherwise 0. */
+
+static int
+adjust_if_needed (ins *insn ATTRIBUTE_UNUSED)
+{
+ int ret_value = 0;
+
+ if ((IS_INSN_TYPE (CSTBIT_INS)) || (IS_INSN_TYPE (LD_STOR_INS)))
+ {
+ if ((instruction->operands[0].op_type == abs24)
+ && ((insn->arg[0].constant) > 0xF00000))
+ {
+ insn->arg[0].constant &= 0xFFFFF;
+ instruction--;
+ ret_value = 1;
+ }
+ }
+
+ return ret_value;
+}
+
+/* Assemble a single instruction:
+ INSN is already parsed (that is, all operand values and types are set).
+ For instruction to be assembled, we need to find an appropriate template in
+ the instruction table, meeting the following conditions:
+ 1: Has the same number of operands.
+ 2: Has the same operand types.
+ 3: Each operand size is sufficient to represent the instruction's values.
+ Returns 1 upon success, 0 upon failure. */
+
+static int
+assemble_insn (char *mnemonic, ins *insn)
+{
+ /* Type of each operand in the current template. */
+ argtype cur_type[MAX_OPERANDS];
+ /* Size (in bits) of each operand in the current template. */
+ unsigned int cur_size[MAX_OPERANDS];
+ /* Flags of each operand in the current template. */
+ unsigned int cur_flags[MAX_OPERANDS];
+ /* Instruction type to match. */
+ unsigned int ins_type;
+ /* Boolean flag to mark whether a match was found. */
+ int match = 0;
+ int i;
+ /* Nonzero if an instruction with same number of operands was found. */
+ int found_same_number_of_operands = 0;
+ /* Nonzero if an instruction with same argument types was found. */
+ int found_same_argument_types = 0;
+ /* Nonzero if a constant was found within the required range. */
+ int found_const_within_range = 0;
+ /* Argument number of an operand with invalid type. */
+ int invalid_optype = -1;
+ /* Argument number of an operand with invalid constant value. */
+ int invalid_const = -1;
+ /* Operand error (used for issuing various constant error messages). */
+ op_err op_error, const_err = OP_LEGAL;
+
+/* Retrieve data (based on FUNC) for each operand of a given instruction. */
+#define GET_CURRENT_DATA(FUNC, ARRAY) \
+ for (i = 0; i < insn->nargs; i++) \
+ ARRAY[i] = FUNC (instruction->operands[i].op_type)
+
+#define GET_CURRENT_TYPE GET_CURRENT_DATA (get_optype, cur_type)
+#define GET_CURRENT_SIZE GET_CURRENT_DATA (get_opbits, cur_size)
+#define GET_CURRENT_FLAGS GET_CURRENT_DATA (get_opflags, cur_flags)
+
+ /* Instruction has no operands -> only copy the constant opcode. */
+ if (insn->nargs == 0)
+ {
+ output_opcode[0] = BIN (instruction->match, instruction->match_bits);
+ return 1;
+ }
+
+ /* In some case, same mnemonic can appear with different instruction types.
+ For example, 'storb' is supported with 3 different types :
+ LD_STOR_INS, LD_STOR_INS_INC, STOR_IMM_INS.
+ We assume that when reaching this point, the instruction type was
+ pre-determined. We need to make sure that the type stays the same
+ during a search for matching instruction. */
+ ins_type = CR16_INS_TYPE (instruction->flags);
+
+ while (/* Check that match is still not found. */
+ match != 1
+ /* Check we didn't get to end of table. */
+ && instruction->mnemonic != NULL
+ /* Check that the actual mnemonic is still available. */
+ && IS_INSN_MNEMONIC (mnemonic)
+ /* Check that the instruction type wasn't changed. */
+ && IS_INSN_TYPE (ins_type))
+ {
+ /* Check whether number of arguments is legal. */
+ if (get_number_of_operands () != insn->nargs)
+ goto next_insn;
+ found_same_number_of_operands = 1;
+
+ /* Initialize arrays with data of each operand in current template. */
+ GET_CURRENT_TYPE;
+ GET_CURRENT_SIZE;
+ GET_CURRENT_FLAGS;
+
+ /* Check for type compatibility. */
+ for (i = 0; i < insn->nargs; i++)
+ {
+ if (cur_type[i] != insn->arg[i].type)
+ {
+ if (invalid_optype == -1)
+ invalid_optype = i + 1;
+ goto next_insn;
+ }
+ }
+ found_same_argument_types = 1;
+
+ for (i = 0; i < insn->nargs; i++)
+ {
+ /* If 'bal' instruction size is '2' and reg operand is not 'ra'
+ then goto next instruction. */
+ if (IS_INSN_MNEMONIC ("bal") && (i == 0)
+ && (instruction->size == 2) && (insn->arg[i].rp != 14))
+ goto next_insn;
+
+ /* If 'storb' instruction with 'sp' reg and 16-bit disp of
+ * reg-pair, leads to undifined trap, so this should use
+ * 20-bit disp of reg-pair. */
+ if (IS_INSN_MNEMONIC ("storb") && (instruction->size == 2)
+ && (insn->arg[i].r == 15) && (insn->arg[i + 1].type == arg_crp))
+ goto next_insn;
+
+ /* Only check range - don't update the constant's value, since the
+ current instruction may not be the last we try to match.
+ The constant's value will be updated later, right before printing
+ it to the object file. */
+ if ((insn->arg[i].X_op == O_constant)
+ && (op_error = check_range (&insn->arg[i].constant, cur_size[i],
+ cur_flags[i], 0)))
+ {
+ if (invalid_const == -1)
+ {
+ invalid_const = i + 1;
+ const_err = op_error;
+ }
+ goto next_insn;
+ }
+ /* For symbols, we make sure the relocation size (which was already
+ determined) is sufficient. */
+ else if ((insn->arg[i].X_op == O_symbol)
+ && ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize
+ > cur_size[i]))
+ goto next_insn;
+ }
+ found_const_within_range = 1;
+
+ /* If we got till here -> Full match is found. */
+ match = 1;
+ break;
+
+/* Try again with next instruction. */
+next_insn:
+ instruction++;
+ }
+
+ if (!match)
+ {
+ /* We haven't found a match - instruction can't be assembled. */
+ if (!found_same_number_of_operands)
+ as_bad (_("Incorrect number of operands"));
+ else if (!found_same_argument_types)
+ as_bad (_("Illegal type of operand (arg %d)"), invalid_optype);
+ else if (!found_const_within_range)
+ {
+ switch (const_err)
+ {
+ case OP_OUT_OF_RANGE:
+ as_bad (_("Operand out of range (arg %d)"), invalid_const);
+ break;
+ case OP_NOT_EVEN:
+ as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
+ break;
+ default:
+ as_bad (_("Illegal operand (arg %d)"), invalid_const);
+ break;
+ }
+ }
+
+ return 0;
+ }
+ else
+ /* Full match - print the encoding to output file. */
+ {
+ /* Make further checkings (such that couldn't be made earlier).
+ Warn the user if necessary. */
+ warn_if_needed (insn);
+
+ /* Check whether we need to adjust the instruction pointer. */
+ if (adjust_if_needed (insn))
+ /* If instruction pointer was adjusted, we need to update
+ the size of the current template operands. */
+ GET_CURRENT_SIZE;
+
+ for (i = 0; i < insn->nargs; i++)
+ {
+ int j = instruction->flags & REVERSE_MATCH ?
+ i == 0 ? 1 :
+ i == 1 ? 0 : i :
+ i;
+
+ /* This time, update constant value before printing it. */
+ if ((insn->arg[j].X_op == O_constant)
+ && (check_range (&insn->arg[j].constant, cur_size[j],
+ cur_flags[j], 1) != OP_LEGAL))
+ as_fatal (_("Illegal operand (arg %d)"), j+1);
+ }
+
+ /* First, copy the instruction's opcode. */
+ output_opcode[0] = BIN (instruction->match, instruction->match_bits);
+
+ for (i = 0; i < insn->nargs; i++)
+ {
+ /* For BAL (ra),disp17 instuction only. And also set the
+ DISP24a relocation type. */
+ if (IS_INSN_MNEMONIC ("bal") && (instruction->size == 2) && i == 0)
+ {
+ insn->rtype = BFD_RELOC_CR16_DISP24a;
+ continue;
+ }
+ cur_arg_num = i;
+ print_operand (cur_size[i], instruction->operands[i].shift,
+ &insn->arg[i]);
+ }
+ }
+
+ return 1;
+}
+
+/* Print the instruction.
+ Handle also cases where the instruction is relaxable/relocatable. */
+
+static void
+print_insn (ins *insn)
+{
+ unsigned int i, j, insn_size;
+ char *this_frag;
+ unsigned short words[4];
+ int addr_mod;
+
+ /* Arrange the insn encodings in a WORD size array. */
+ for (i = 0, j = 0; i < 2; i++)
+ {
+ words[j++] = (output_opcode[i] >> 16) & 0xFFFF;
+ words[j++] = output_opcode[i] & 0xFFFF;
+ }
+
+ /* Handle relocation. */
+ if ((instruction->flags & RELAXABLE) && relocatable)
+ {
+ int relax_subtype;
+ /* Write the maximal instruction size supported. */
+ insn_size = INSN_MAX_SIZE;
+
+ if (IS_INSN_TYPE (BRANCH_INS))
+ {
+ switch (insn->rtype)
+ {
+ case BFD_RELOC_CR16_DISP24:
+ relax_subtype = 2;
+ break;
+ case BFD_RELOC_CR16_DISP16:
+ relax_subtype = 1;
+ break;
+ default:
+ relax_subtype = 0;
+ break;
+ }
+ }
+ else
+ abort ();
+
+ this_frag = frag_var (rs_machine_dependent, insn_size *2,
+ 4, relax_subtype,
+ insn->exp.X_add_symbol,
+ 0,
+ 0);
+ }
+ else
+ {
+ insn_size = instruction->size;
+ this_frag = frag_more (insn_size * 2);
+
+ if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
+
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size < 1 || size > 4)
+ abort ();
+
+ fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
+ size, &insn->exp, reloc_howto->pc_relative,
+ insn->rtype);
+ }
+ }
+
+ /* Verify a 2-byte code alignment. */
+ addr_mod = frag_now_fix () & 1;
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ as_bad (_("instruction address is not a multiple of 2"));
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+
+ /* Write the instruction encoding to frag. */
+ for (i = 0; i < insn_size; i++)
+ {
+ md_number_to_chars (this_frag, (valueT) words[i], 2);
+ this_frag += 2;
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. OP points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *op)
+{
+ ins cr16_ins;
+ char *param, param1[32];
+
+ /* Reset global variables for a new instruction. */
+ reset_vars (op);
+
+ /* Strip the mnemonic. */
+ for (param = op; *param != 0 && !ISSPACE (*param); param++)
+ ;
+ *param++ = '\0';
+
+ /* bCC instuctions and adjust the mnemonic by adding extra white spaces. */
+ if (is_bcc_insn (op))
+ {
+ strcpy (param1, get_b_cc (op));
+ op = "b";
+ strcat (param1,",");
+ strcat (param1, param);
+ param = (char *) &param1;
+ }
+
+ /* Checking the cinv options and adjust the mnemonic by removing the
+ extra white spaces. */
+ if (streq ("cinv", op))
+ {
+ /* Validate the cinv options. */
+ check_cinv_options (param);
+ strcat (op, param);
+ }
+
+ /* MAPPING - SHIFT INSN, if imm4/imm16 positive values
+ lsh[b/w] imm4/imm6, reg ==> ashu[b/w] imm4/imm16, reg
+ as CR16 core doesn't support lsh[b/w] right shift operaions. */
+ if ((streq ("lshb", op) || streq ("lshw", op) || streq ("lshd", op))
+ && (param [0] == '$'))
+ {
+ strcpy (param1, param);
+ /* Find the instruction. */
+ instruction = (const inst *) hash_find (cr16_inst_hash, op);
+ parse_operands (&cr16_ins, param1);
+ if (((&cr16_ins)->arg[0].type == arg_ic)
+ && ((&cr16_ins)->arg[0].constant >= 0))
+ {
+ if (streq ("lshb", op))
+ op = "ashub";
+ else if (streq ("lshd", op))
+ op = "ashud";
+ else
+ op = "ashuw";
+ }
+ }
+
+ /* Find the instruction. */
+ instruction = (const inst *) hash_find (cr16_inst_hash, op);
+ if (instruction == NULL)
+ {
+ as_bad (_("Unknown opcode: `%s'"), op);
+ return;
+ }
+
+ /* Tie dwarf2 debug info to the address at the start of the insn. */
+ dwarf2_emit_insn (0);
+
+ /* Parse the instruction's operands. */
+ parse_insn (&cr16_ins, param);
+
+ /* Assemble the instruction - return upon failure. */
+ if (assemble_insn (op, &cr16_ins) == 0)
+ return;
+
+ /* Print the instruction. */
+ print_insn (&cr16_ins);
+}
diff --git a/gas/config/tc-cr16.h b/gas/config/tc-cr16.h
new file mode 100644
index 0000000..9da8cb7
--- /dev/null
+++ b/gas/config/tc-cr16.h
@@ -0,0 +1,76 @@
+/* tc-cr16.h -- Header file for tc-cr16.c, the CR16 GAS port.
+ Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ Contributed by M R Swami Reddy <MR.Swami.Reddy@nsc.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef TC_CR16_H
+#define TC_CR16_H
+
+#define TC_CR16 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+
+#define TARGET_FORMAT "elf32-cr16"
+#define TARGET_ARCH bfd_arch_cr16
+
+#define WORKING_DOT_WORD
+#define LOCAL_LABEL_PREFIX '.'
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* We do relaxing in the assembler as well as the linker. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* We do not want to adjust any relocations to make implementation of
+ linker relaxations easier. */
+#define tc_fix_adjustable(fixP) 0
+
+/* We need to force out some relocations when relaxing. */
+#define TC_FORCE_RELOCATION(FIXP) cr16_force_relocation (FIXP)
+extern int cr16_force_relocation (struct fix *);
+
+/* Fixup non-code sections since we will never relax them. */
+#define TC_LINKRELAX_FIXUP(seg) \
+ ((seg->flags & (SEC_ALLOC | SEC_CODE)) == (SEC_ALLOC | SEC_CODE))
+
+/* CR16 instructions, with operands included, are a multiple
+ of two bytes long. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+extern void cr16_cons_fix_new (struct frag *, int, int, struct expressionS *,
+ bfd_reloc_code_real_type);
+/* This is called by emit_expr when creating a reloc for a cons.
+ We could use the definition there, except that we want to handle
+ the CR16 reloc type specially, rather than the BFD_RELOC type. */
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP, RELOC) \
+ cr16_cons_fix_new (FRAG, OFF, LEN, EXP, RELOC)
+
+/* Give an error if a frag containing code is not aligned to a 2-byte
+ boundary. */
+#define md_frag_check(FRAGP) \
+ if ((FRAGP)->has_code \
+ && (((FRAGP)->fr_address + (FRAGP)->insn_addr) & 1) != 0) \
+ as_bad_where ((FRAGP)->fr_file, (FRAGP)->fr_line, \
+ _("instruction address is not a multiple of 2"));
+
+#endif /* TC_CR16_H */
diff --git a/gas/config/tc-cris.c b/gas/config/tc-cris.c
new file mode 100644
index 0000000..2989f02
--- /dev/null
+++ b/gas/config/tc-cris.c
@@ -0,0 +1,4427 @@
+/* tc-cris.c -- Assembler code for the CRIS CPU core.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ Contributed by Axis Communications AB, Lund, Sweden.
+ Originally written for GAS 1.38.1 by Mikael Asker.
+ Updates, BFDizing, GNUifying and ELF support by Hans-Peter Nilsson.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/cris.h"
+#include "dwarf2dbg.h"
+
+/* Conventions used here:
+ Generally speaking, pointers to binutils types such as "fragS" and
+ "expressionS" get parameter and variable names ending in "P", such as
+ "fragP", to harmonize with the rest of the binutils code. Other
+ pointers get a "p" suffix, such as "bufp". Any function or type-name
+ that could clash with a current or future binutils or GAS function get
+ a "cris_" prefix. */
+
+#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 '$'
+
+/* True for expressions where getting X_add_symbol and X_add_number is
+ enough to get the "base" and "offset"; no need to make_expr_symbol.
+ It's not enough to check if X_op_symbol is NULL; that misses unary
+ operations like O_uminus. */
+#define SIMPLE_EXPR(EXP) \
+ ((EXP)->X_op == O_constant || (EXP)->X_op == O_symbol)
+
+/* Like in ":GOT", ":GOTOFF" etc. Other ports use '@', but that's in
+ line_separator_chars for CRIS, so we avoid it. */
+#define RELOC_SUFFIX_CHAR ':'
+
+/* This might be CRIS_INSN_NONE if we're assembling a prefix-insn only.
+ Note that some prefix-insns might be assembled as CRIS_INSN_NORMAL. */
+enum cris_insn_kind
+{
+ CRIS_INSN_NORMAL, CRIS_INSN_NONE, CRIS_INSN_BRANCH, CRIS_INSN_MUL
+};
+
+/* An instruction will have one of these prefixes.
+ Although the same bit-pattern, we handle BDAP with an immediate
+ expression (eventually quick or [pc+]) different from when we only have
+ register expressions. */
+enum prefix_kind
+{
+ PREFIX_NONE, PREFIX_BDAP_IMM, PREFIX_BDAP, PREFIX_BIAP, PREFIX_DIP,
+ PREFIX_PUSH
+};
+
+/* The prefix for an instruction. */
+struct cris_prefix
+{
+ enum prefix_kind kind;
+ int base_reg_number;
+ unsigned int opcode;
+
+ /* There might be an expression to be evaluated, like I in [rN+I]. */
+ expressionS expr;
+
+ /* If there's an expression, we might need a relocation. Here's the
+ type of what relocation to start relaxaton with.
+ The relocation is assumed to start immediately after the prefix insn,
+ so we don't provide an offset. */
+ enum bfd_reloc_code_real reloc;
+};
+
+/* The description of the instruction being assembled. */
+struct cris_instruction
+{
+ /* If CRIS_INSN_NONE, then this insn is of zero length. */
+ enum cris_insn_kind insn_type;
+
+ /* If a special register was mentioned, this is its description, else
+ it is NULL. */
+ const struct cris_spec_reg *spec_reg;
+
+ unsigned int opcode;
+
+ /* An insn may have at most one expression; theoretically there could be
+ another in its prefix (but I don't see how that could happen). */
+ expressionS expr;
+
+ /* The expression might need a relocation. Here's one to start
+ relaxation with. */
+ enum bfd_reloc_code_real reloc;
+
+ /* The size in bytes of an immediate expression, or zero if
+ nonapplicable. */
+ 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 (char **);
+static int cris_insn_ver_valid_for_arch (enum cris_insn_version_usage,
+ enum cris_archs);
+
+static void cris_process_instruction (char *, struct cris_instruction *,
+ struct cris_prefix *);
+static int get_bwd_size_modifier (char **, int *);
+static int get_bw_size_modifier (char **, int *);
+static int get_gen_reg (char **, int *);
+static int get_spec_reg (char **, const struct cris_spec_reg **);
+static int get_sup_reg (char **, int *);
+static int get_autoinc_prefix_or_indir_op (char **, struct cris_prefix *,
+ int *, int *, int *,
+ expressionS *);
+static int get_3op_or_dip_prefix_op (char **, struct cris_prefix *);
+static int cris_get_expression (char **, expressionS *);
+static int get_flags (char **, int *);
+static void gen_bdap (int, expressionS *);
+static int branch_disp (int);
+static void gen_cond_branch_32 (char *, char *, fragS *, symbolS *, symbolS *,
+ long int);
+static void cris_number_to_imm (char *, long, int, fixS *, segT);
+static void s_syntax (int);
+static void s_cris_file (int);
+static void s_cris_loc (int);
+static void s_cris_arch (int);
+static void s_cris_dtpoff (int);
+
+/* Get ":GOT", ":GOTOFF", ":PLT" etc. suffixes. */
+static void cris_get_reloc_suffix (char **, bfd_reloc_code_real_type *,
+ expressionS *);
+static unsigned int cris_get_specified_reloc_size (bfd_reloc_code_real_type);
+
+/* All the .syntax functions. */
+static void cris_force_reg_prefix (void);
+static void cris_relax_reg_prefix (void);
+static void cris_sym_leading_underscore (void);
+static void cris_sym_no_leading_underscore (void);
+static char *cris_insn_first_word_frag (void);
+
+/* Handle to the opcode hash table. */
+static struct hash_control *op_hash = NULL;
+
+/* If we target cris-axis-linux-gnu (as opposed to generic cris-axis-elf),
+ we default to no underscore and required register-prefixes. The
+ difference is in the default values. */
+#ifdef TE_LINUX
+#define DEFAULT_CRIS_AXIS_LINUX_GNU TRUE
+#else
+#define DEFAULT_CRIS_AXIS_LINUX_GNU FALSE
+#endif
+
+/* Whether we demand that registers have a `$' prefix. Default here. */
+static bfd_boolean demand_register_prefix = DEFAULT_CRIS_AXIS_LINUX_GNU;
+
+/* Whether global user symbols have a leading underscore. Default here. */
+static bfd_boolean symbols_have_leading_underscore
+ = !DEFAULT_CRIS_AXIS_LINUX_GNU;
+
+/* Whether or not we allow PIC, and expand to PIC-friendly constructs. */
+static bfd_boolean pic = FALSE;
+
+/* Whether or not we allow TLS suffixes. For the moment, we always do. */
+static const bfd_boolean tls = TRUE;
+
+/* If we're configured for "cris", default to allow all v0..v10
+ instructions and register names. */
+#ifndef DEFAULT_CRIS_ARCH
+#define DEFAULT_CRIS_ARCH cris_any_v0_v10
+#endif
+
+/* No whitespace in the CONCAT2 parameter list. */
+static enum cris_archs cris_arch = XCONCAT2 (arch_,DEFAULT_CRIS_ARCH);
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"dword", cons, 4},
+ {"dtpoffd", s_cris_dtpoff, 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;
+
+/* Whether to emit error when a MULS/MULU could be located last on a
+ cache-line. */
+static int err_for_dangerous_mul_placement
+ = (XCONCAT2 (arch_,DEFAULT_CRIS_ARCH) != arch_crisv32);
+
+const char cris_comment_chars[] = ";";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand-checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that slash-star will always start a comment. */
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "@";
+
+/* Now all floating point support is shut off. See md_atof. */
+const char EXP_CHARS[] = "";
+const char FLT_CHARS[] = "";
+
+/* For CRIS, we encode the relax_substateTs (in e.g. fr_substate) as:
+ 2 1 0
+ ---/ /--+-----------------+-----------------+-----------------+
+ | what state ? | how long ? |
+ ---/ /--+-----------------+-----------------+-----------------+
+
+ The "how long" bits are 00 = byte, 01 = word, 10 = dword (long).
+ Not all lengths are legit for a given value of (what state).
+
+ Groups for CRIS address relaxing:
+
+ 1. Bcc (pre-V32)
+ length: byte, word, 10-byte expansion
+
+ 2. BDAP
+ length: byte, word, dword
+
+ 3. MULS/MULU
+ Not really a relaxation (no infrastructure to get delay-slots
+ right), just an alignment and placement checker for the v10
+ multiply/cache-bug.
+
+ 4. Bcc (V32 and later)
+ length: byte, word, 14-byte expansion
+
+ 5. Bcc (V10+V32)
+ length: byte, word, error
+
+ 6. BA (V32)
+ length: byte, word, dword
+
+ 7. LAPC (V32)
+ length: byte, dword
+ */
+
+#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_COND_BRANCH_PIC (8)
+
+#define STATE_LENGTH_MASK (3)
+#define STATE_BYTE (0)
+#define STATE_WORD (1)
+#define STATE_DWORD (2)
+/* Symbol undefined. */
+#define STATE_UNDF (3)
+#define STATE_MAX_LENGTH (3)
+
+/* These displacements are relative to the address following the opcode
+ word of the instruction. The first letter is Byte, Word. The 2nd
+ letter is Forward, Backward. */
+
+#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[] =
+{
+ /* Error sentinel (0, 0). */
+ {1, 1, 0, 0},
+
+ /* Unused (0, 1). */
+ {1, 1, 0, 0},
+
+ /* Unused (0, 2). */
+ {1, 1, 0, 0},
+
+ /* Unused (0, 3). */
+ {1, 1, 0, 0},
+
+ /* Bcc o (1, 0). */
+ {BRANCH_BF, BRANCH_BB, 0, ENCODE_RELAX (1, 1)},
+
+ /* Bcc [PC+] (1, 1). */
+ {BRANCH_WF, BRANCH_WB, 2, ENCODE_RELAX (1, 2)},
+
+ /* BEXT/BWF, BA, JUMP (external), JUMP (always), Bnot_cc, JUMP (default)
+ (1, 2). */
+ {0, 0, 10, 0},
+
+ /* Unused (1, 3). */
+ {1, 1, 0, 0},
+
+ /* BDAP o (2, 0). */
+ {BDAP_BF, BDAP_BB, 0, ENCODE_RELAX (2, 1)},
+
+ /* BDAP.[bw] [PC+] (2, 1). */
+ {BDAP_WF, BDAP_WB, 2, ENCODE_RELAX (2, 2)},
+
+ /* BDAP.d [PC+] (2, 2). */
+ {0, 0, 4, 0},
+
+ /* Unused (2, 3). */
+ {1, 1, 0, 0},
+
+ /* MULS/MULU (3, 0). Positions (3, 1..3) are unused. */
+ {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0},
+
+ /* V32: Bcc o (4, 0). */
+ {BRANCH_BF_V32, BRANCH_BB_V32, 0, ENCODE_RELAX (4, 1)},
+
+ /* V32: Bcc [PC+] (4, 1). */
+ {BRANCH_WF_V32, BRANCH_WB_V32, 2, ENCODE_RELAX (4, 2)},
+
+ /* V32: BA .+12; NOP; BA32 target; NOP; Bcc .-6 (4, 2). */
+ {0, 0, 12, 0},
+
+ /* Unused (4, 3). */
+ {1, 1, 0, 0},
+
+ /* COMMON: Bcc o (5, 0). The offsets are calculated as for v32. Code
+ should contain two nop insns (or four if offset size is large or
+ unknown) after every label. */
+ {BRANCH_BF_V32, BRANCH_BB_V32, 0, ENCODE_RELAX (5, 1)},
+
+ /* COMMON: Bcc [PC+] (5, 1). */
+ {BRANCH_WF_V32, BRANCH_WB_V32, 2, ENCODE_RELAX (5, 2)},
+
+ /* COMMON: FIXME: ???. Treat as error currently. */
+ {0, 0, 12, 0},
+
+ /* Unused (5, 3). */
+ {1, 1, 0, 0},
+
+ /* V32: BA o (6, 0). */
+ {BRANCH_BF_V32, BRANCH_BB_V32, 0, ENCODE_RELAX (6, 1)},
+
+ /* V32: BA.W (6, 1). */
+ {BRANCH_WF_V32, BRANCH_WB_V32, 2, ENCODE_RELAX (6, 2)},
+
+ /* V32: BA.D (6, 2). */
+ {0, 0, 4, 0},
+
+ /* Unused (6, 3). */
+ {1, 1, 0, 0},
+
+ /* LAPC: LAPCQ .+0..15*2,Rn (7, 0). */
+ {14*2, -1*2, 0, ENCODE_RELAX (7, 2)},
+
+ /* Unused (7, 1).
+ While there's a shorter sequence, e.g. LAPCQ + an ADDQ or SUBQ,
+ that would affect flags, so we can't do that as it wouldn't be a
+ proper insn expansion of LAPCQ. This row is associated with a
+ 2-byte expansion, so it's unused rather than the next. */
+ {1, 1, 0, 0},
+
+ /* LAPC: LAPC.D (7, 2). */
+ {0, 0, 4, 0},
+
+ /* Unused (7, 3). */
+ {1, 1, 0, 0},
+
+ /* PIC for pre-v32: Bcc o (8, 0). */
+ {BRANCH_BF, BRANCH_BB, 0, ENCODE_RELAX (STATE_COND_BRANCH_PIC, 1)},
+
+ /* Bcc [PC+] (8, 1). */
+ {BRANCH_WF, BRANCH_WB, 2, ENCODE_RELAX (STATE_COND_BRANCH_PIC, 2)},
+
+ /* 32-bit expansion, PIC (8, 2). */
+ {0, 0, 12, 0},
+
+ /* Unused (8, 3). */
+ {1, 1, 0, 0}
+};
+
+#undef BDAP_BF
+#undef BDAP_BB
+#undef BDAP_WF
+#undef BDAP_WB
+
+/* Target-specific multicharacter options, not const-declared. */
+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}
+};
+
+/* Not const-declared. */
+size_t md_longopts_size = sizeof (md_longopts);
+const char *md_shortopts = "hHN";
+
+/* At first glance, this may seems wrong and should be 4 (ba + nop); but
+ since a short_jump must skip a *number* of long jumps, it must also be
+ a long jump. Here, we hope to make it a "ba [16bit_offs]" and a "nop"
+ for the delay slot and hope that the jump table at most needs
+ 32767/4=8191 long-jumps. A branch is better than a jump, since it is
+ relative; we will not have a reloc to fix up somewhere.
+
+ Note that we can't add relocs, because relaxation uses these fixed
+ numbers, and md_create_short_jump is called after relaxation. */
+
+int md_short_jump_size = 6;
+
+/* The v32 version has a delay-slot, hence two bytes longer.
+ The pre-v32 PIC version uses a prefixed insn. */
+#define cris_any_v0_v10_long_jump_size 6
+#define cris_any_v0_v10_long_jump_size_pic 8
+#define crisv32_long_jump_size 8
+
+int md_long_jump_size = XCONCAT2 (DEFAULT_CRIS_ARCH,_long_jump_size);
+
+/* Report output format. Small changes in output format (like elf
+ variants below) can happen until all options are parsed, but after
+ that, the output format must remain fixed. */
+
+const char *
+cris_target_format (void)
+{
+ 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;
+ }
+}
+
+/* Return a bfd_mach_cris... value corresponding to the value of
+ cris_arch. */
+
+unsigned int
+cris_mach (void)
+{
+ 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;
+}
+
+/* We need a port-specific relaxation function to cope with sym2 - sym1
+ relative expressions with both symbols in the same segment (but not
+ necessarily in the same frag as this insn), for example:
+ move.d [pc+sym2-(sym1-2)],r10
+ sym1:
+ The offset can be 8, 16 or 32 bits long. */
+
+long
+cris_relax_frag (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;
+
+ /* We only have to cope with frags as prepared by
+ md_estimate_size_before_relax. The dword cases may get here
+ because of the different reasons that they aren't relaxable. */
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_COND_BRANCH_PIC, STATE_DWORD):
+ 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):
+ /* When we get to these states, the frag won't grow any more. */
+ 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):
+ /* Nothing to do here. */
+ return 0;
+
+ default:
+ as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"),
+ __FUNCTION__, fragP->fr_subtype);
+ }
+
+ /* The rest is stolen from relax_frag. There's no obvious way to
+ share the code, but fortunately no requirement to keep in sync as
+ long as fragP->fr_symbol does not have its segment changed. */
+
+ this_state = fragP->fr_subtype;
+ start_type = this_type = table + this_state;
+
+ if (aim < 0)
+ {
+ /* Look backwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim >= this_type->rlx_backward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+ else
+ {
+ /* Look forwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim <= this_type->rlx_forward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ 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;
+}
+
+/* Prepare machine-dependent frags for relaxation.
+
+ Called just before relaxation starts. Any symbol that is now undefined
+ will not become defined.
+
+ Return the correct fr_subtype in the frag.
+
+ Return the initial "guess for fr_var" to caller. 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 value. */
+
+int
+md_estimate_size_before_relax (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)) \
+ /* The symbol lies in the same segment - a relaxable \
+ case. */ \
+ fragP->fr_subtype \
+ = ENCODE_RELAX (state, STATE_BYTE); \
+ else \
+ /* Unknown or not the same segment, so not relaxable. */ \
+ 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_COND_BRANCH_PIC);
+ 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))
+ {
+ /* The symbol lies in the same segment - a relaxable case.
+ Check if we currently have an odd offset; we can't code
+ that into the instruction. Relaxing presumably only cause
+ multiple-of-two changes, so we should only need to adjust
+ for that here. */
+ 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
+ /* Unknown or not the same segment, so not relaxable. */
+ 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):
+ /* Note that we can not do anything sane with relaxing
+ [rX + a_known_symbol_in_text], it will have to be a 32-bit
+ value.
+
+ We could play tricks with managing a constant pool and make
+ a_known_symbol_in_text a "bdap [pc + offset]" pointing there
+ (like the GOT for ELF shared libraries), but that's no use, it
+ would in general be no shorter or faster code, only more
+ complicated. */
+
+ if (S_GET_SEGMENT (symbolP) != absolute_section)
+ {
+ /* Go for dword if not absolute or same segment. */
+ 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))
+ {
+ /* The symbol will eventually be completely resolved as an
+ absolute expression, but right now it depends on the result
+ of relaxation and we don't know anything else about the
+ value. We start relaxation with the assumption that it'll
+ fit in a byte. */
+ 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
+ {
+ /* Absolute expression. */
+ long int value;
+ value = (symbolP != NULL
+ ? S_GET_VALUE (symbolP) : 0) + fragP->fr_offset;
+
+ if (value >= -128 && value <= 127)
+ {
+ /* Byte displacement. */
+ (fragP->fr_opcode)[0] = value;
+ }
+ else
+ {
+ /* Word or dword displacement. */
+ int pow2_of_size = 1;
+ char *writep;
+
+ if (value < -32768 || value > 32767)
+ {
+ /* Outside word range, make it a dword. */
+ pow2_of_size = 2;
+ }
+
+ /* Modify the byte-offset BDAP into a word or dword offset
+ BDAP. Or really, a BDAP rX,8bit into a
+ BDAP.[wd] rX,[PC+] followed by a word or dword. */
+ (fragP->fr_opcode)[0] = BDAP_PC_LOW + pow2_of_size * 16;
+
+ /* Keep the register number in the highest four bits. */
+ (fragP->fr_opcode)[1] &= 0xF0;
+ (fragP->fr_opcode)[1] |= BDAP_INCR_HIGH;
+
+ /* It grew by two or four bytes. */
+ 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_PIC, STATE_BYTE):
+ case ENCODE_RELAX (STATE_COND_BRANCH_PIC, STATE_WORD):
+ case ENCODE_RELAX (STATE_COND_BRANCH_PIC, 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):
+ /* When relaxing a section for the second time, we don't need to
+ do anything except making sure that fr_var is set right. */
+ fragP->fr_var = md_cris_relax_table[fragP->fr_subtype].rlx_length;
+ break;
+
+ case ENCODE_RELAX (STATE_MUL, STATE_BYTE):
+ /* Nothing to do here. */
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ }
+
+ return fragP->fr_var + (fragP->fr_fix - old_fr_fix);
+}
+
+/* Perform post-processing of machine-dependent frags after relaxation.
+ Called after relaxation is finished.
+ In: Address of frag.
+ fr_type == rs_machine_dependent.
+ fr_subtype is what the address relaxed to.
+
+ Out: Any fixS:s and constants are set up.
+
+ The caller will turn the frag into a ".space 0". */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ /* Pointer to first byte in variable-sized part of the frag. */
+ char *var_partp;
+
+ /* Pointer to first opcode byte in frag. */
+ char *opcodep;
+
+ /* Used to check integrity of the relaxation.
+ One of 2 = long, 1 = word, or 0 = byte. */
+ int length_code ATTRIBUTE_UNUSED;
+
+ /* Size in bytes of variable-sized part of frag. */
+ int var_part_size = 0;
+
+ /* This is part of *fragP. It contains all information about addresses
+ and offsets to varying parts. */
+ symbolS *symbolP;
+ unsigned long var_part_offset;
+
+ /* Where, in file space, is _var of *fragP? */
+ unsigned long address_of_var_part = 0;
+
+ /* Where, in file space, does addr point? */
+ 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_PIC, 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_PIC, 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):
+ /* We had a quick immediate branch, now turn it into a word one i.e. a
+ PC autoincrement. */
+ 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);
+ /* Ten bytes added: a branch, nop and a jump. */
+ var_part_size = 2 + 2 + 4 + 2;
+ break;
+
+ case ENCODE_RELAX (STATE_COND_BRANCH_PIC, STATE_DWORD):
+ gen_cond_branch_32 (fragP->fr_opcode, var_partp, fragP,
+ fragP->fr_symbol, (symbolS *) NULL,
+ fragP->fr_offset);
+ /* Twelve bytes added: a branch, nop and a pic-branch-32. */
+ var_part_size = 2 + 2 + 4 + 2 + 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);
+ /* Twelve bytes added: a branch, nop and another branch and nop. */
+ 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"));
+ /* Pretend we have twelve bytes for sake of quelling further
+ errors. */
+ var_part_size = 2 + 2 + 2 + 4 + 2;
+ break;
+
+ case ENCODE_RELAX (STATE_ABS_BRANCH_V32, STATE_DWORD):
+ /* We had a quick immediate branch or a word immediate ba. Now
+ turn it into a dword one. */
+ 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);
+
+ /* This is mostly a sanity check; useful occurrences (if there
+ really are any) should have been caught in
+ md_estimate_size_before_relax. We can (at least
+ theoretically) stumble over invalid code with odd sizes and
+ .p2aligns within the code, so emit an error if that happens.
+ (The generic relaxation machinery is not fit to check this.) */
+
+ 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"));
+
+ /* FIXME: This *is* a sanity check. Remove when done with. */
+ 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);
+ /* Remember that the reloc is against the position *after* the
+ relocated contents, so we need to adjust to the start of
+ the insn. */
+ 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):
+ /* We had a BDAP 8-bit "quick immediate", now turn it into a 16-bit
+ one that uses PC autoincrement. */
+ 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):
+ /* We had a BDAP 16-bit "word", change the offset to a 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):
+ /* This is the only time we check position and alignment of the
+ placement-tracking frag. */
+ 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 the address after the MULS/MULU has alignment which is
+ that of the section and may be that of a cache-size of the
+ buggy versions, then the MULS/MULU can be placed badly. */
+ 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;
+}
+
+/* Generate a short jump around a secondary jump table.
+ Also called from md_create_long_jump, when sufficient. */
+
+void
+md_create_short_jump (char *storep, addressT from_addr, addressT to_addr,
+ fragS *fragP ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ long int distance;
+
+ /* See md_create_long_jump about the comment on the "+ 2". */
+ long int max_minimal_minus_distance;
+ long int max_minimal_plus_distance;
+ long int max_minus_distance;
+ long int max_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;
+ max_minus_distance = BRANCH_WB_V32 + 2;
+ max_plus_distance = BRANCH_WF_V32 + 2;
+ nop_opcode = NOP_OPCODE_V32;
+ }
+ else if (cris_arch == arch_cris_common_v10_v32)
+ /* Bail out for compatibility mode. (It seems it can be implemented,
+ perhaps with a 10-byte sequence: "move.d NNNN,$pc/$acr", "jump
+ $acr", "nop"; but doesn't seem worth it at the moment.) */
+ as_fatal (_("Out-of-range .word offset handling\
+ is not implemented for .arch common_v10_v32"));
+ else
+ {
+ max_minimal_minus_distance = BRANCH_BB + 2;
+ max_minimal_plus_distance = BRANCH_BF + 2;
+ max_minus_distance = BRANCH_WB + 2;
+ max_plus_distance = BRANCH_WF + 2;
+ nop_opcode = NOP_OPCODE;
+ }
+
+ distance = to_addr - from_addr;
+
+ if (max_minimal_minus_distance <= distance
+ && distance <= max_minimal_plus_distance)
+ {
+ /* Create a "short" short jump: "BA distance - 2". */
+ storep[0] = branch_disp (distance - 2);
+ storep[1] = BA_QUICK_HIGH;
+
+ /* A nop for the delay slot. */
+ md_number_to_chars (storep + 2, nop_opcode, 2);
+
+ /* The extra word should be filled with something sane too. Make it
+ a nop to keep disassembly sane. */
+ md_number_to_chars (storep + 4, nop_opcode, 2);
+ }
+ else if (max_minus_distance <= distance
+ && distance <= max_plus_distance)
+ {
+ /* Make it a "long" short jump: "BA (PC+)". */
+ md_number_to_chars (storep, BA_PC_INCR_OPCODE, 2);
+
+ /* ".WORD distance - 4". */
+ md_number_to_chars (storep + 2,
+ (long) (distance - 4
+ - (cris_arch == arch_crisv32
+ ? -4 : 0)),
+ 2);
+
+ /* A nop for the delay slot. */
+ md_number_to_chars (storep + 4, nop_opcode, 2);
+ }
+ else
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _(".word case-table handling failed: table too large"));
+}
+
+/* Generate a long jump in a secondary jump table.
+
+ storep Where to store the jump instruction.
+ from_addr Address of the jump instruction.
+ to_addr Destination address of the jump.
+ fragP Which frag the destination address operand
+ lies in.
+ to_symbol Destination symbol. */
+
+void
+md_create_long_jump (char *storep, addressT from_addr, addressT to_addr,
+ fragS *fragP, symbolS *to_symbol)
+{
+ long int distance;
+
+ /* FIXME: What's that "+ 3"? It comes from the magic numbers that
+ used to be here, it's just translated to the limit macros used in
+ the relax table. But why + 3? */
+ 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;
+
+ distance = to_addr - from_addr;
+
+ if (max_short_minus_distance <= distance
+ && distance <= max_short_plus_distance)
+ {
+ /* Then make it a "short" long jump. */
+ md_create_short_jump (storep, from_addr, to_addr, fragP,
+ to_symbol);
+ if (cris_arch == arch_crisv32)
+ md_number_to_chars (storep + 6, NOP_OPCODE_V32, 2);
+ else
+ md_number_to_chars (storep + 6, NOP_OPCODE, 2);
+ }
+ else
+ {
+ /* We have a "long" long jump: "JUMP [PC+]". If CRISv32, always
+ make it a BA. Else make it an "MOVE [PC=PC+N],P0" if we're supposed
+ to emit PIC code. */
+ md_number_to_chars (storep,
+ cris_arch == arch_crisv32
+ ? BA_DWORD_OPCODE
+ : (pic ? MOVE_PC_INCR_OPCODE_PREFIX
+ : JUMP_PC_INCR_OPCODE),
+ 2);
+
+ /* Follow with a ".DWORD to_addr", PC-relative for PIC. */
+ 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);
+
+ /* Follow it with a "NOP" for CRISv32. */
+ if (cris_arch == arch_crisv32)
+ md_number_to_chars (storep + 6, NOP_OPCODE_V32, 2);
+ else if (pic)
+ /* ...and the rest of the move-opcode for pre-v32 PIC. */
+ md_number_to_chars (storep + 6, MOVE_PC_INCR_OPCODE_SUFFIX, 2);
+ }
+}
+
+/* Allocate space for the first piece of an insn, and mark it as the
+ start of the insn for debug-format use. */
+
+static char *
+cris_insn_first_word_frag (void)
+{
+ char *insnp = frag_more (2);
+
+ /* We need to mark the start of the insn by passing dwarf2_emit_insn
+ the offset from the current fragment position. This must be done
+ after the first fragment is created but before any other fragments
+ (fixed or varying) are created. Note that the offset only
+ corresponds to the "size" of the insn for a fixed-size,
+ non-expanded insn. */
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ dwarf2_emit_insn (2);
+
+ return insnp;
+}
+
+/* Port-specific assembler initialization. */
+
+void
+md_begin (void)
+{
+ const char *hashret = NULL;
+ int i = 0;
+
+ /* Set up a hash table for the instructions. */
+ op_hash = hash_new ();
+ if (op_hash == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ /* Enable use of ".if ..asm.arch.cris.v32"
+ and ".if ..asm.arch.cris.common_v10_v32" and a few others. */
+ 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;
+ }
+
+ /* Need to cast to get rid of "const". FIXME: Fix hash_insert instead. */
+ hashret = hash_insert (op_hash, name, (void *) &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);
+ }
+}
+
+/* Assemble a source line. */
+
+void
+md_assemble (char *str)
+{
+ struct cris_instruction output_instruction;
+ struct cris_prefix prefix;
+ char *opcodep;
+ char *p;
+
+ know (str);
+
+ /* Do the low-level grunt - assemble to bits and split up into a prefix
+ and ordinary insn. */
+ cris_process_instruction (str, &output_instruction, &prefix);
+
+ /* Handle any prefixes to the instruction. */
+ switch (prefix.kind)
+ {
+ case PREFIX_NONE:
+ break;
+
+ /* When the expression is unknown for a BDAP, it can need 0, 2 or 4
+ extra bytes, so we handle it separately. */
+ case PREFIX_BDAP_IMM:
+ /* We only do it if the relocation is unspecified, i.e. not a PIC or TLS
+ relocation. */
+ if (prefix.reloc == BFD_RELOC_NONE)
+ {
+ gen_bdap (prefix.base_reg_number, &prefix.expr);
+ break;
+ }
+ /* Fall through. */
+ case PREFIX_BDAP:
+ case PREFIX_BIAP:
+ case PREFIX_DIP:
+ opcodep = cris_insn_first_word_frag ();
+
+ /* Output the prefix opcode. */
+ md_number_to_chars (opcodep, (long) prefix.opcode, 2);
+
+ /* Having a specified reloc only happens for DIP and for BDAP with
+ PIC or TLS operands, but it is ok to drop through here for the other
+ prefixes as they can have no relocs specified. */
+ if (prefix.reloc != BFD_RELOC_NONE)
+ {
+ unsigned int relocsize
+ = (prefix.kind == PREFIX_DIP
+ ? 4 : cris_get_specified_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 ();
+
+ /* Output the prefix opcode. Being a "push", we add the negative
+ size of the register to "sp". */
+ if (output_instruction.spec_reg != NULL)
+ {
+ /* Special register. */
+ opcodep[0] = -output_instruction.spec_reg->reg_size;
+ }
+ else
+ {
+ /* General register. */
+ opcodep[0] = -4;
+ }
+ opcodep[1] = (REG_SP << 4) + (BDAP_QUICK_OPCODE >> 8);
+ break;
+
+ default:
+ BAD_CASE (prefix.kind);
+ }
+
+ /* If we only had a prefix insn, we're done. */
+ if (output_instruction.insn_type == CRIS_INSN_NONE)
+ return;
+
+ /* Done with the prefix. Continue with the main instruction. */
+ if (prefix.kind == PREFIX_NONE)
+ opcodep = cris_insn_first_word_frag ();
+ else
+ opcodep = frag_more (2);
+
+ /* Output the instruction opcode. */
+ md_number_to_chars (opcodep, (long) (output_instruction.opcode), 2);
+
+ /* Output the symbol-dependent instruction stuff. */
+ 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
+ /* In CRISv32, there *is* a 32-bit absolute branch, so don't
+ emit the 12-byte sequence for known symbols in other
+ segments. */
+ || (cris_arch == arch_crisv32
+ && output_instruction.opcode == BA_QUICK_OPCODE))
+ {
+ /* Handle complex expressions. */
+ 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));
+
+ /* If is_undefined, the expression may still become now_seg.
+ That case is handled by md_estimate_size_before_relax. */
+ length_code = to_seg == now_seg ? STATE_BYTE : STATE_UNDF;
+
+ /* Make room for max twelve bytes of variable length for v32 mode
+ or PIC, ten for v10 and older. */
+ frag_var (rs_machine_dependent,
+ (cris_arch == arch_crisv32
+ || cris_arch == arch_cris_common_v10_v32
+ || pic) ? 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
+ : (pic ? STATE_COND_BRANCH_PIC
+ : STATE_COND_BRANCH)),
+ length_code),
+ sym, addvalue, opcodep);
+ }
+ else
+ {
+ /* We have: to_seg != now_seg && to_seg != undefined_section.
+ This means it is a branch to a known symbol in another
+ section, perhaps an absolute address. Emit a 32-bit branch. */
+ char *cond_jump
+ = frag_more ((cris_arch == arch_crisv32
+ || cris_arch == arch_cris_common_v10_v32
+ || pic)
+ ? 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)
+ /* Create a frag which which we track the location of the mul insn
+ (in the last two bytes before the mul-frag). */
+ frag_variant (rs_machine_dependent, 0, 0,
+ ENCODE_RELAX (STATE_MUL, STATE_BYTE),
+ NULL, 0, opcodep);
+ else
+ {
+ if (output_instruction.imm_oprnd_size > 0)
+ {
+ /* The instruction has an immediate operand. */
+ enum bfd_reloc_code_real reloc = BFD_RELOC_NONE;
+
+ switch (output_instruction.imm_oprnd_size)
+ {
+ /* Any byte-size immediate constants are treated as
+ word-size. FIXME: Thus overflow check does not work
+ correctly. */
+
+ case 2:
+ /* Note that size-check for the explicit reloc has already
+ been done when we get here. */
+ if (output_instruction.reloc != BFD_RELOC_NONE)
+ reloc = output_instruction.reloc;
+ else
+ reloc = BFD_RELOC_16;
+ break;
+
+ case 4:
+ /* Allow a relocation specified in the operand. */
+ 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)
+ {
+ /* Handle complex expressions. */
+ 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);
+
+ /* This is a relaxing construct, so we need a frag_var rather
+ than the fix_new_exp call below. */
+ frag_var (rs_machine_dependent,
+ 4, 0,
+ ENCODE_RELAX (STATE_LAPC, STATE_UNDF),
+ sym, addvalue, opcodep);
+ }
+ else if (output_instruction.reloc != BFD_RELOC_NONE)
+ {
+ /* An immediate operand that has a relocation and needs to be
+ processed further. */
+
+ /* It is important to use fix_new_exp here and everywhere else
+ (and not fix_new), as fix_new_exp can handle "difference
+ expressions" - where the expression contains a difference of
+ two symbols in the same segment. */
+ 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);
+ }
+ }
+}
+
+/* Helper error-reporting function: calls as_bad for a format string
+ for a single value and zeroes the offending value (zero assumed
+ being a valid value) to avoid repeated error reports in later value
+ checking. */
+
+static void
+cris_bad (const char *format, offsetT *valp)
+{
+ /* We cast to long so the format string can assume that format. */
+ as_bad (format, (long) *valp);
+ *valp = 0;
+}
+
+/* Low level text-to-bits assembly. */
+
+static void
+cris_process_instruction (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;
+
+ /* Reset these fields to a harmless state in case we need to return in
+ error. */
+ prefixp->kind = PREFIX_NONE;
+ prefixp->reloc = BFD_RELOC_NONE;
+ out_insnp->insn_type = CRIS_INSN_NONE;
+ out_insnp->imm_oprnd_size = 0;
+
+ /* Find the end of the opcode mnemonic. We assume (true in 2.9.1)
+ that the caller has translated the opcode to lower-case, up to the
+ first non-letter. */
+ for (operands = insn_text; ISLOWER (*operands); ++operands)
+ ;
+
+ /* Terminate the opcode after letters, but save the character there if
+ it was of significance. */
+ switch (*operands)
+ {
+ case '\0':
+ break;
+
+ case '.':
+ /* Put back the modified character later. */
+ modified_char = *operands;
+ /* Fall through. */
+
+ case ' ':
+ /* Consume the character after the mnemonic
+ and replace it with '\0'. */
+ *operands++ = '\0';
+ break;
+
+ default:
+ as_bad (_("Unknown opcode: `%s'"), insn_text);
+ return;
+ }
+
+ /* Find the instruction. */
+ instruction = (struct cris_opcode *) hash_find (op_hash, insn_text);
+ if (instruction == NULL)
+ {
+ as_bad (_("Unknown opcode: `%s'"), insn_text);
+ return;
+ }
+
+ /* Put back the modified character. */
+ switch (modified_char)
+ {
+ case 0:
+ break;
+
+ default:
+ *--operands = modified_char;
+ }
+
+ /* Try to match an opcode table slot. */
+ for (s = operands;;)
+ {
+ int imm_expr_found;
+
+ /* Initialize *prefixp, perhaps after being modified for a
+ "near match". */
+ prefixp->kind = PREFIX_NONE;
+ prefixp->reloc = BFD_RELOC_NONE;
+
+ /* Initialize *out_insnp. */
+ 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;
+
+ /* Build the opcode, checking as we go to make sure that the
+ operands match. */
+ for (args = instruction->args;; ++args)
+ {
+ switch (*args)
+ {
+ case '\0':
+ /* If we've come to the end of arguments, we're done. */
+ if (*s == '\0')
+ match = 1;
+ break;
+
+ case '!':
+ /* Non-matcher character for disassembly.
+ Ignore it here. */
+ continue;
+
+ case '[':
+ case ']':
+ case ',':
+ case ' ':
+ /* These must match exactly. */
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case 'A':
+ /* "ACR", case-insensitive.
+ Handle a sometimes-mandatory dollar sign as register
+ prefix. */
+ 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':
+ /* This is not really an operand, but causes a "BDAP
+ -size,SP" prefix to be output, for PUSH instructions. */
+ prefixp->kind = PREFIX_PUSH;
+ continue;
+
+ case 'b':
+ /* This letter marks an operand that should not be matched
+ in the assembler. It is a branch with 16-bit
+ displacement. The assembler will create them from the
+ 8-bit flavor when necessary. The assembler does not
+ support the [rN+] operand, as the [r15+] that is
+ generated for 16-bit displacements. */
+ break;
+
+ case 'c':
+ /* A 5-bit unsigned immediate in bits <4:0>. */
+ 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))
+ cris_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':
+ /* A 4-bit unsigned immediate in bits <3:0>. */
+ 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))
+ cris_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;
+ }
+
+ /* For 'd', check for an optional ".d" or ".D" at the
+ start of the operands, followed by a space character. */
+ case 'd':
+ if (modified_char == '.' && *s == '.')
+ {
+ if ((s[1] != 'd' && s[1] == 'D')
+ || ! ISSPACE (s[2]))
+ break;
+ s += 2;
+ continue;
+ }
+ continue;
+
+ case 'D':
+ /* General register in bits <15:12> and <3:0>. */
+ if (! get_gen_reg (&s, &regno))
+ break;
+ else
+ {
+ out_insnp->opcode |= regno /* << 0 */;
+ out_insnp->opcode |= regno << 12;
+ continue;
+ }
+
+ case 'f':
+ /* Flags from the condition code register. */
+ {
+ int flags = 0;
+
+ if (! get_flags (&s, &flags))
+ break;
+
+ out_insnp->opcode |= ((flags & 0xf0) << 8) | (flags & 0xf);
+ continue;
+ }
+
+ case 'i':
+ /* A 6-bit signed immediate in bits <5:0>. */
+ 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))
+ cris_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':
+ /* A 6-bit unsigned immediate in bits <5:0>. */
+ 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))
+ cris_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':
+ /* A size modifier, B, W or D, to be put in a bit position
+ suitable for CLEAR instructions (i.e. reflecting a zero
+ register). */
+ 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':
+ /* A size modifier, B, W or D, to be put in bits <5:4>. */
+ if (modified_char != '.'
+ || ! get_bwd_size_modifier (&s, &size_bits))
+ break;
+ else
+ {
+ out_insnp->opcode |= size_bits << 4;
+ continue;
+ }
+
+ case 'o':
+ /* A branch expression. */
+ if (! cris_get_expression (&s, &out_insnp->expr))
+ break;
+ else
+ {
+ out_insnp->insn_type = CRIS_INSN_BRANCH;
+ continue;
+ }
+
+ case 'Q':
+ /* A 8-bit quick BDAP expression, "expr,R". */
+ if (! cris_get_expression (&s, &out_insnp->expr))
+ break;
+
+ if (*s != ',')
+ break;
+
+ s++;
+
+ if (!get_gen_reg (&s, &regno))
+ break;
+
+ out_insnp->opcode |= regno << 12;
+ out_insnp->reloc = BFD_RELOC_CRIS_SIGNED_8;
+ continue;
+
+ case 'O':
+ /* A BDAP expression for any size, "expr,R". */
+ if (! cris_get_expression (&s, &prefixp->expr))
+ break;
+ else
+ {
+ if (*s != ',')
+ break;
+
+ s++;
+
+ if (!get_gen_reg (&s, &prefixp->base_reg_number))
+ break;
+
+ /* Since 'O' is used with an explicit bdap, we have no
+ "real" instruction. */
+ 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':
+ /* Special register in bits <15:12>. */
+ if (! get_spec_reg (&s, &out_insnp->spec_reg))
+ break;
+ else
+ {
+ /* Use of some special register names come with a
+ specific warning. Note that we have no ".cpu type"
+ pseudo yet, so some of this is just unused
+ framework. */
+ if (out_insnp->spec_reg->warning)
+ as_warn ("%s", out_insnp->spec_reg->warning);
+ else if (out_insnp->spec_reg->applicable_version
+ == cris_ver_warning)
+ /* Others have a generic warning. */
+ as_warn (_("Unimplemented register `%s' specified"),
+ out_insnp->spec_reg->name);
+
+ out_insnp->opcode
+ |= out_insnp->spec_reg->number << 12;
+ continue;
+ }
+
+ case 'p':
+ /* This character is used in the disassembler to
+ recognize a prefix instruction to fold into the
+ addressing mode for the next instruction. It is
+ ignored here. */
+ continue;
+
+ case 'R':
+ /* General register in bits <15:12>. */
+ if (! get_gen_reg (&s, &regno))
+ break;
+ else
+ {
+ out_insnp->opcode |= regno << 12;
+ continue;
+ }
+
+ case 'r':
+ /* General register in bits <3:0>. */
+ if (! get_gen_reg (&s, &regno))
+ break;
+ else
+ {
+ out_insnp->opcode |= regno /* << 0 */;
+ continue;
+ }
+
+ case 'S':
+ /* Source operand in bit <10> and a prefix; a 3-operand
+ prefix. */
+ if (! get_3op_or_dip_prefix_op (&s, prefixp))
+ break;
+ else
+ continue;
+
+ case 's':
+ /* Source operand in bits <10>, <3:0> and optionally a
+ prefix; i.e. an indirect operand or an side-effect
+ prefix (where valid). */
+ if (! get_autoinc_prefix_or_indir_op (&s, prefixp, &mode,
+ &regno,
+ &imm_expr_found,
+ &out_insnp->expr))
+ break;
+ else
+ {
+ if (prefixp->kind != PREFIX_NONE)
+ {
+ /* A prefix, so it has the autoincrement bit
+ set. */
+ out_insnp->opcode |= (AUTOINCR_BIT << 8);
+ }
+ else
+ {
+ /* No prefix. The "mode" variable contains bits like
+ whether or not this is autoincrement mode. */
+ out_insnp->opcode |= (mode << 10);
+
+ /* If there was a reloc specifier, then it was
+ attached to the prefix. Note that we can't check
+ that the reloc size matches, since we don't have
+ all the operands yet in all cases. */
+ if (prefixp->reloc != BFD_RELOC_NONE)
+ out_insnp->reloc = prefixp->reloc;
+ }
+
+ out_insnp->opcode |= regno /* << 0 */ ;
+ continue;
+ }
+
+ case 'N':
+ case 'Y':
+ /* Like 's', but immediate operand only. Also do not
+ modify insn. There are no insns where an explicit reloc
+ specifier makes sense. */
+ if (cris_get_expression (&s, &out_insnp->expr))
+ {
+ imm_expr_found = 1;
+ continue;
+ }
+ break;
+
+ case 'n':
+ /* Like 'N', but PC-relative to the start of the insn.
+ There might be a :PLT to request a PLT entry. */
+ if (cris_get_expression (&s, &out_insnp->expr))
+ {
+ imm_expr_found = 1;
+ out_insnp->reloc = BFD_RELOC_32_PCREL;
+
+ /* We have to adjust the expression, because that
+ relocation is to the location *after* the
+ relocation. So add 2 for the insn and 4 for the
+ relocation. */
+ out_insnp->expr.X_add_number += 6;
+
+ /* TLS specifiers do not make sense here. */
+ if (pic && *s == RELOC_SUFFIX_CHAR)
+ cris_get_reloc_suffix (&s, &out_insnp->reloc,
+ &out_insnp->expr);
+
+ continue;
+ }
+ break;
+
+ case 'U':
+ /* Maybe 'u', maybe 'n'. Only for LAPC/LAPCQ. */
+ if (cris_get_expression (&s, &out_insnp->expr))
+ {
+ out_insnp->reloc = BFD_RELOC_CRIS_LAPCQ_OFFSET;
+
+ /* Define 1 as relaxing. */
+ out_insnp->expr.X_md = 1;
+ continue;
+ }
+ break;
+
+ case 'u':
+ /* Four PC-relative bits in <3:0> representing <4:1>:0 of
+ an offset relative to the beginning of the current
+ insn. */
+ if (cris_get_expression (&s, &out_insnp->expr))
+ {
+ out_insnp->reloc = BFD_RELOC_CRIS_LAPCQ_OFFSET;
+
+ /* Define 0 as non-relaxing. */
+ out_insnp->expr.X_md = 0;
+
+ /* We have to adjust the expression, because that
+ relocation is to the location *after* the
+ insn. So add 2 for the insn. */
+ out_insnp->expr.X_add_number += 2;
+ continue;
+ }
+ break;
+
+ case 'x':
+ /* Rs.m in bits <15:12> and <5:4>. */
+ if (! get_gen_reg (&s, &regno)
+ || ! get_bwd_size_modifier (&s, &size_bits))
+ break;
+ else
+ {
+ out_insnp->opcode |= (regno << 12) | (size_bits << 4);
+ continue;
+ }
+
+ case 'y':
+ /* Source operand in bits <10>, <3:0> and optionally a
+ prefix; i.e. an indirect operand or an side-effect
+ prefix.
+
+ The difference to 's' is that this does not allow an
+ "immediate" expression. */
+ if (! get_autoinc_prefix_or_indir_op (&s, prefixp,
+ &mode, &regno,
+ &imm_expr_found,
+ &out_insnp->expr)
+ || imm_expr_found)
+ break;
+ else
+ {
+ if (prefixp->kind != PREFIX_NONE)
+ {
+ /* A prefix, and those matched here always have
+ side-effects (see 's' case). */
+ out_insnp->opcode |= (AUTOINCR_BIT << 8);
+ }
+ else
+ {
+ /* No prefix. The "mode" variable contains bits
+ like whether or not this is autoincrement
+ mode. */
+ out_insnp->opcode |= (mode << 10);
+ }
+
+ out_insnp->opcode |= regno /* << 0 */;
+ continue;
+ }
+
+ case 'z':
+ /* Size modifier (B or W) in bit <4>. */
+ 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, &regno))
+ {
+ out_insnp->opcode |= regno << 12;
+ continue;
+ }
+ break;
+
+ default:
+ BAD_CASE (*args);
+ }
+
+ /* We get here when we fail a match above or we found a
+ complete match. Break out of this loop. */
+ break;
+ }
+
+ /* Was it a match or a miss? */
+ if (match == 0)
+ {
+ /* If it's just that the args don't match, maybe the next
+ item in the table is the same opcode but with
+ matching operands. First skip any invalid ones. */
+ 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))
+ {
+ /* Yep. Restart and try that one instead. */
+ ++instruction;
+ s = operands;
+ continue;
+ }
+ else
+ {
+ /* We've come to the end of instructions with this
+ opcode, so it must be an error. */
+ as_bad (_("Illegal operands"));
+
+ /* As discard_rest_of_line, but without continuing to the
+ next line. */
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ return;
+ }
+ }
+ else
+ {
+ /* We have a match. Check if there's anything more to do. */
+ if (imm_expr_found)
+ {
+ /* There was an immediate mode operand, so we must check
+ that it has an appropriate size. */
+ switch (instruction->imm_oprnd_size)
+ {
+ default:
+ case SIZE_NONE:
+ /* Shouldn't happen; this one does not have immediate
+ operands with different sizes. */
+ 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)
+ /* All immediate loads of special registers are
+ 32-bit on 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))
+ cris_bad (_("Immediate value not in 8 bit range: %ld"),
+ &out_insnp->expr.X_add_number);
+ /* Fall through. */
+ case 2:
+ /* FIXME: We need an indicator in the instruction
+ table to pass on, to indicate if we need to check
+ overflow for a signed or unsigned number. */
+ if (out_insnp->expr.X_op == O_constant
+ && (out_insnp->expr.X_add_number < -32768
+ || out_insnp->expr.X_add_number > 65535))
+ cris_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)
+ {
+ /* FIXME: Find way to pass un/signedness to
+ caller, and set reloc type instead, postponing
+ this check until cris_number_to_imm. That
+ necessarily corrects the reloc type for the
+ byte case, maybe requiring further changes. */
+ 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))
+ cris_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))
+ cris_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))
+ cris_bad (_("Immediate value not in 8 bit unsigned range: %ld"),
+ &out_insnp->expr.X_add_number);
+ }
+
+ /* Fall through. */
+ 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))
+ cris_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))
+ cris_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))
+ cris_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 there was a relocation specified for the immediate
+ expression (i.e. it had a PIC or TLS modifier) check that the
+ size of the relocation matches the size specified by
+ the opcode. */
+ if (out_insnp->reloc != BFD_RELOC_NONE
+ && (cris_get_specified_reloc_size (out_insnp->reloc)
+ != (unsigned int) out_insnp->imm_oprnd_size))
+ as_bad (out_insnp->reloc == BFD_RELOC_CRIS_32_GD
+ || out_insnp->reloc == BFD_RELOC_CRIS_32_TPREL
+ || out_insnp->reloc == BFD_RELOC_CRIS_16_TPREL
+ || out_insnp->reloc == BFD_RELOC_CRIS_32_IE
+ ? _("TLS relocation size does not match operand size")
+ : _("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;
+ }
+}
+
+/* Get a B, W, or D size modifier from the string pointed out by *cPP,
+ which must point to a '.' in front of the modifier. On successful
+ return, *cPP is advanced to the character following the size
+ modifier, and is undefined otherwise.
+
+ cPP Pointer to pointer to string starting
+ with the size modifier.
+
+ size_bitsp Pointer to variable to contain the size bits on
+ successful return.
+
+ Return 1 iff a correct size modifier is found, else 0. */
+
+static int
+get_bwd_size_modifier (char **cPP, int *size_bitsp)
+{
+ if (**cPP != '.')
+ return 0;
+ else
+ {
+ /* Consume the '.'. */
+ (*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;
+ }
+
+ /* Consume the size letter. */
+ (*cPP)++;
+ return 1;
+ }
+}
+
+/* Get a B or W size modifier from the string pointed out by *cPP,
+ which must point to a '.' in front of the modifier. On successful
+ return, *cPP is advanced to the character following the size
+ modifier, and is undefined otherwise.
+
+ cPP Pointer to pointer to string starting
+ with the size modifier.
+
+ size_bitsp Pointer to variable to contain the size bits on
+ successful return.
+
+ Return 1 iff a correct size modifier is found, else 0. */
+
+static int
+get_bw_size_modifier (char **cPP, int *size_bitsp)
+{
+ if (**cPP != '.')
+ return 0;
+ else
+ {
+ /* Consume the '.'. */
+ (*cPP)++;
+
+ switch (**cPP)
+ {
+ case 'B':
+ case 'b':
+ *size_bitsp = 0;
+ break;
+
+ case 'W':
+ case 'w':
+ *size_bitsp = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ /* Consume the size letter. */
+ (*cPP)++;
+ return 1;
+ }
+}
+
+/* Get a general register from the string pointed out by *cPP. The
+ variable *cPP is advanced to the character following the general
+ register name on a successful return, and has its initial position
+ otherwise.
+
+ cPP Pointer to pointer to string, beginning with a general
+ register name.
+
+ regnop Pointer to int containing the register number.
+
+ Return 1 iff a correct general register designator is found,
+ else 0. */
+
+static int
+get_gen_reg (char **cPP, int *regnop)
+{
+ char *oldp;
+ oldp = *cPP;
+
+ /* Handle a sometimes-mandatory dollar sign as register prefix. */
+ if (**cPP == REGISTER_PREFIX_CHAR)
+ (*cPP)++;
+ else if (demand_register_prefix)
+ return 0;
+
+ switch (**cPP)
+ {
+ case 'P':
+ case 'p':
+ /* "P" as in "PC"? Consume the "P". */
+ (*cPP)++;
+
+ if ((**cPP == 'C' || **cPP == 'c')
+ && ! ISALNUM ((*cPP)[1])
+ /* Here's a little twist: For v32 and the compatibility mode,
+ we only recognize PC as a register number if there's '+]'
+ after. We don't consume that, but the presence can only be
+ valid after a register in a post-increment context, which
+ is also the only valid context for PC as a register for
+ v32. Not that it's used very often, but saying "MOVE.D
+ [PC+],R5" should remain valid. It's not supported for
+ jump-type insns or other insns with no [Rn+] mode, though. */
+ && ((cris_arch != arch_crisv32
+ && cris_arch != arch_cris_common_v10_v32)
+ || ((*cPP)[1] == '+' && (*cPP)[2] == ']')))
+ {
+ /* It's "PC": consume the "c" and we're done. */
+ (*cPP)++;
+ *regnop = REG_PC;
+ return 1;
+ }
+ break;
+
+ /* Like with PC, we recognize ACR, but only if it's *not* followed
+ by '+', and only for v32. */
+ 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':
+ /* Hopefully r[0-9] or r1[0-5]. Consume 'R' or 'r'. */
+ (*cPP)++;
+
+ if (ISDIGIT (**cPP))
+ {
+ /* It's r[0-9]. Consume and check the next digit. */
+ *regnop = **cPP - '0';
+ (*cPP)++;
+
+ if (! ISALNUM (**cPP))
+ {
+ /* No more digits, we're done. */
+ return 1;
+ }
+ else
+ {
+ /* One more digit. Consume and add. */
+ *regnop = *regnop * 10 + (**cPP - '0');
+
+ /* We need to check for a valid register number; Rn,
+ 0 <= n <= MAX_REG. */
+ if (*regnop <= MAX_REG)
+ {
+ /* Consume second digit. */
+ (*cPP)++;
+ return 1;
+ }
+ }
+ }
+ break;
+
+ case 'S':
+ case 's':
+ /* "S" as in "SP"? Consume the "S". */
+ (*cPP)++;
+ if (**cPP == 'P' || **cPP == 'p')
+ {
+ /* It's "SP": consume the "p" and we're done. */
+ (*cPP)++;
+ *regnop = REG_SP;
+ return 1;
+ }
+ break;
+
+ default:
+ /* Just here to silence compilation warnings. */
+ ;
+ }
+
+ /* We get here if we fail. Restore the pointer. */
+ *cPP = oldp;
+ return 0;
+}
+
+/* Get a special register from the string pointed out by *cPP. The
+ variable *cPP is advanced to the character following the special
+ register name if one is found, and retains its original position
+ otherwise.
+
+ cPP Pointer to pointer to string starting with a special register
+ name.
+
+ sregpp Pointer to Pointer to struct spec_reg, where a pointer to the
+ register description will be stored.
+
+ Return 1 iff a correct special register name is found. */
+
+static int
+get_spec_reg (char **cPP, const struct cris_spec_reg **sregpp)
+{
+ char *s1;
+ const char *s2;
+ char *name_begin = *cPP;
+
+ const struct cris_spec_reg *sregp;
+
+ /* Handle a sometimes-mandatory dollar sign as register prefix. */
+ if (*name_begin == REGISTER_PREFIX_CHAR)
+ name_begin++;
+ else if (demand_register_prefix)
+ return 0;
+
+ /* Loop over all special registers. */
+ for (sregp = cris_spec_regs; sregp->name != NULL; sregp++)
+ {
+ /* Start over from beginning of the supposed name. */
+ s1 = name_begin;
+ s2 = sregp->name;
+
+ while (*s2 != '\0' && TOLOWER (*s1) == *s2)
+ {
+ s1++;
+ s2++;
+ }
+
+ /* For a match, we must have consumed the name in the table, and we
+ must be outside what could be part of a name. Assume here that a
+ test for alphanumerics is sufficient for a name test. */
+ if (*s2 == 0 && ! ISALNUM (*s1)
+ && cris_insn_ver_valid_for_arch (sregp->applicable_version,
+ cris_arch))
+ {
+ /* We have a match. Update the pointer and be done. */
+ *cPP = s1;
+ *sregpp = sregp;
+ return 1;
+ }
+ }
+
+ /* If we got here, we did not find any name. */
+ return 0;
+}
+
+/* Get a support register from the string pointed out by *cPP. The
+ variable *cPP is advanced to the character following the support-
+ register name if one is found, and retains its original position
+ otherwise.
+
+ cPP Pointer to pointer to string starting with a support-register
+ name.
+
+ sregpp Pointer to int containing the register number.
+
+ Return 1 iff a correct support-register name is found. */
+
+static int
+get_sup_reg (char **cPP, int *regnop)
+{
+ char *s1;
+ const char *s2;
+ char *name_begin = *cPP;
+
+ const struct cris_support_reg *sregp;
+
+ /* Handle a sometimes-mandatory dollar sign as register prefix. */
+ if (*name_begin == REGISTER_PREFIX_CHAR)
+ name_begin++;
+ else if (demand_register_prefix)
+ return 0;
+
+ /* Loop over all support-registers. */
+ for (sregp = cris_support_regs; sregp->name != NULL; sregp++)
+ {
+ /* Start over from beginning of the supposed name. */
+ s1 = name_begin;
+ s2 = sregp->name;
+
+ while (*s2 != '\0' && TOLOWER (*s1) == *s2)
+ {
+ s1++;
+ s2++;
+ }
+
+ /* For a match, we must have consumed the name in the table, and we
+ must be outside what could be part of a name. Assume here that a
+ test for alphanumerics is sufficient for a name test. */
+ if (*s2 == 0 && ! ISALNUM (*s1))
+ {
+ /* We have a match. Update the pointer and be done. */
+ *cPP = s1;
+ *regnop = sregp->number;
+ return 1;
+ }
+ }
+
+ /* If we got here, we did not find any name. */
+ return 0;
+}
+
+/* Get an unprefixed or side-effect-prefix operand from the string pointed
+ out by *cPP. The pointer *cPP is advanced to the character following
+ the indirect operand if we have success, else it contains an undefined
+ value.
+
+ cPP Pointer to pointer to string beginning with the first
+ character of the supposed operand.
+
+ prefixp Pointer to structure containing an optional instruction
+ prefix.
+
+ is_autoincp Pointer to int indicating the indirect or autoincrement
+ bits.
+
+ src_regnop Pointer to int containing the source register number in
+ the instruction.
+
+ imm_foundp Pointer to an int indicating if an immediate expression
+ is found.
+
+ imm_exprP Pointer to a structure containing an immediate
+ expression, if success and if *imm_foundp is nonzero.
+
+ Return 1 iff a correct indirect operand is found. */
+
+static int
+get_autoinc_prefix_or_indir_op (char **cPP, struct cris_prefix *prefixp,
+ int *is_autoincp, int *src_regnop,
+ int *imm_foundp, expressionS *imm_exprP)
+{
+ /* Assume there was no immediate mode expression. */
+ *imm_foundp = 0;
+
+ if (**cPP == '[')
+ {
+ /* So this operand is one of:
+ Indirect: [rN]
+ Autoincrement: [rN+]
+ Indexed with assign: [rN=rM+rO.S]
+ Offset with assign: [rN=rM+I], [rN=rM+[rO].s], [rN=rM+[rO+].s]
+
+ Either way, consume the '['. */
+ (*cPP)++;
+
+ /* Get the rN register. */
+ if (! get_gen_reg (cPP, src_regnop))
+ /* If there was no register, then this cannot match. */
+ return 0;
+ else
+ {
+ /* We got the register, now check the next character. */
+ switch (**cPP)
+ {
+ case ']':
+ /* Indirect mode. We're done here. */
+ prefixp->kind = PREFIX_NONE;
+ *is_autoincp = 0;
+ break;
+
+ case '+':
+ /* This must be an auto-increment mode, if there's a
+ match. */
+ prefixp->kind = PREFIX_NONE;
+ *is_autoincp = 1;
+
+ /* We consume this character and break out to check the
+ closing ']'. */
+ (*cPP)++;
+ break;
+
+ case '=':
+ /* This must be indexed with assign, or offset with assign
+ to match. Not supported for crisv32 or in
+ compatibility mode. */
+ if (cris_arch == arch_crisv32
+ || cris_arch == arch_cris_common_v10_v32)
+ return 0;
+
+ (*cPP)++;
+
+ /* Either way, the next thing must be a register. */
+ if (! get_gen_reg (cPP, &prefixp->base_reg_number))
+ /* No register, no match. */
+ return 0;
+ else
+ {
+ /* We've consumed "[rN=rM", so we must be looking at
+ "+rO.s]" or "+I]", or "-I]", or "+[rO].s]" or
+ "+[rO+].s]". */
+ if (**cPP == '+')
+ {
+ int index_reg_number;
+ (*cPP)++;
+
+ if (**cPP == '[')
+ {
+ int size_bits;
+ /* This must be [rx=ry+[rz].s] or
+ [rx=ry+[rz+].s] or no match. We must be
+ looking at rz after consuming the '['. */
+ (*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 == '+')
+ {
+ /* We've seen "[rx=ry+[rz+" here, so now we
+ know that there must be "].s]" left to
+ check. */
+ (*cPP)++;
+ prefixp->opcode |= AUTOINCR_BIT << 8;
+ }
+
+ /* If it wasn't autoincrement, we don't need to
+ add anything. */
+
+ /* Check the next-to-last ']'. */
+ if (**cPP != ']')
+ return 0;
+
+ (*cPP)++;
+
+ /* Check the ".s" modifier. */
+ if (! get_bwd_size_modifier (cPP, &size_bits))
+ return 0;
+
+ prefixp->opcode |= size_bits << 4;
+
+ /* Now we got [rx=ry+[rz+].s or [rx=ry+[rz].s.
+ We break out to check the final ']'. */
+ break;
+ }
+ /* It wasn't an indirection. Check if it's a
+ register. */
+ else if (get_gen_reg (cPP, &index_reg_number))
+ {
+ int size_bits;
+
+ /* Indexed with assign mode: "[rN+rM.S]". */
+ prefixp->kind = PREFIX_BIAP;
+ prefixp->opcode
+ = (BIAP_OPCODE + (index_reg_number << 12)
+ + prefixp->base_reg_number /* << 0 */);
+
+ if (! get_bwd_size_modifier (cPP, &size_bits))
+ /* Size missing, this isn't a match. */
+ return 0;
+ else
+ {
+ /* Size found, break out to check the
+ final ']'. */
+ prefixp->opcode |= size_bits << 4;
+ break;
+ }
+ }
+ /* Not a register. Then this must be "[rN+I]". */
+ else if (cris_get_expression (cPP, &prefixp->expr))
+ {
+ /* We've got offset with assign mode. Fill
+ in the blanks and break out to match the
+ final ']'. */
+ prefixp->kind = PREFIX_BDAP_IMM;
+
+ /* We tentatively put an opcode corresponding to
+ a 32-bit operand here, although it may be
+ relaxed when there's no relocation
+ specifier for the operand. */
+ prefixp->opcode
+ = (BDAP_INDIR_OPCODE
+ | (prefixp->base_reg_number << 12)
+ | (AUTOINCR_BIT << 8)
+ | (2 << 4)
+ | REG_PC /* << 0 */);
+
+ /* This can have a PIC suffix, specifying reloc
+ type to use. */
+ if ((pic || tls) && **cPP == RELOC_SUFFIX_CHAR)
+ {
+ unsigned int relocsize;
+
+ cris_get_reloc_suffix (cPP, &prefixp->reloc,
+ &prefixp->expr);
+
+ /* Tweak the size of the immediate operand
+ in the prefix opcode if it isn't what we
+ set. */
+ relocsize
+ = cris_get_specified_reloc_size (prefixp->reloc);
+ if (relocsize != 4)
+ prefixp->opcode
+ = ((prefixp->opcode & ~(3 << 4))
+ | ((relocsize >> 1) << 4));
+ }
+ break;
+ }
+ else
+ /* Neither register nor expression found, so
+ this can't be a match. */
+ return 0;
+ }
+ /* Not "[rN+" but perhaps "[rN-"? */
+ else if (**cPP == '-')
+ {
+ /* We must have an offset with assign mode. */
+ if (! cris_get_expression (cPP, &prefixp->expr))
+ /* No expression, no match. */
+ return 0;
+ else
+ {
+ /* We've got offset with assign mode. Fill
+ in the blanks and break out to match the
+ final ']'.
+
+ Note that we don't allow a relocation
+ suffix for an operand with a minus
+ sign. */
+ prefixp->kind = PREFIX_BDAP_IMM;
+ break;
+ }
+ }
+ else
+ /* Neither '+' nor '-' after "[rN=rM". Lose. */
+ return 0;
+ }
+ default:
+ /* Neither ']' nor '+' nor '=' after "[rN". Lose. */
+ return 0;
+ }
+ }
+
+ /* When we get here, we have a match and will just check the closing
+ ']'. We can still fail though. */
+ if (**cPP != ']')
+ return 0;
+ else
+ {
+ /* Don't forget to consume the final ']'.
+ Then return in glory. */
+ (*cPP)++;
+ return 1;
+ }
+ }
+ /* No indirection. Perhaps a constant? */
+ else if (cris_get_expression (cPP, imm_exprP))
+ {
+ /* Expression found, this is immediate mode. */
+ prefixp->kind = PREFIX_NONE;
+ *is_autoincp = 1;
+ *src_regnop = REG_PC;
+ *imm_foundp = 1;
+
+ /* This can have a PIC suffix, specifying reloc type to use. The
+ caller must check that the reloc size matches the operand size. */
+ if ((pic || tls) && **cPP == RELOC_SUFFIX_CHAR)
+ cris_get_reloc_suffix (cPP, &prefixp->reloc, imm_exprP);
+
+ return 1;
+ }
+
+ /* No luck today. */
+ return 0;
+}
+
+/* This function gets an indirect operand in a three-address operand
+ combination from the string pointed out by *cPP. The pointer *cPP is
+ advanced to the character following the indirect operand on success, or
+ has an unspecified value on failure.
+
+ cPP Pointer to pointer to string beginning
+ with the operand
+
+ prefixp Pointer to structure containing an
+ instruction prefix
+
+ Returns 1 iff a correct indirect operand is found. */
+
+static int
+get_3op_or_dip_prefix_op (char **cPP, struct cris_prefix *prefixp)
+{
+ int reg_number;
+
+ if (**cPP != '[')
+ /* We must have a '[' or it's a clean failure. */
+ return 0;
+
+ /* Eat the first '['. */
+ (*cPP)++;
+
+ if (**cPP == '[')
+ {
+ /* A second '[', so this must be double-indirect mode. */
+ (*cPP)++;
+ prefixp->kind = PREFIX_DIP;
+ prefixp->opcode = DIP_OPCODE;
+
+ /* Get the register or fail entirely. */
+ if (! get_gen_reg (cPP, &reg_number))
+ return 0;
+ else
+ {
+ prefixp->opcode |= reg_number /* << 0 */ ;
+ if (**cPP == '+')
+ {
+ /* Since we found a '+', this must be double-indirect
+ autoincrement mode. */
+ (*cPP)++;
+ prefixp->opcode |= AUTOINCR_BIT << 8;
+ }
+
+ /* There's nothing particular to do, if this was a
+ double-indirect *without* autoincrement. */
+ }
+
+ /* Check the first ']'. The second one is checked at the end. */
+ if (**cPP != ']')
+ return 0;
+
+ /* Eat the first ']', so we'll be looking at a second ']'. */
+ (*cPP)++;
+ }
+ /* No second '['. Then we should have a register here, making
+ it "[rN". */
+ else if (get_gen_reg (cPP, &prefixp->base_reg_number))
+ {
+ /* This must be indexed or offset mode: "[rN+I]" or
+ "[rN+rM.S]" or "[rN+[rM].S]" or "[rN+[rM+].S]". */
+ if (**cPP == '+')
+ {
+ int index_reg_number;
+
+ (*cPP)++;
+
+ if (**cPP == '[')
+ {
+ /* This is "[rx+["... Expect a register next. */
+ 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);
+
+ /* We've seen "[rx+[ry", so check if this is
+ autoincrement. */
+ if (**cPP == '+')
+ {
+ /* Yep, now at "[rx+[ry+". */
+ (*cPP)++;
+ prefixp->opcode |= AUTOINCR_BIT << 8;
+ }
+ /* If it wasn't autoincrement, we don't need to
+ add anything. */
+
+ /* Check a first closing ']': "[rx+[ry]" or
+ "[rx+[ry+]". */
+ if (**cPP != ']')
+ return 0;
+ (*cPP)++;
+
+ /* Now expect a size modifier ".S". */
+ if (! get_bwd_size_modifier (cPP, &size_bits))
+ return 0;
+
+ prefixp->opcode |= size_bits << 4;
+
+ /* Ok, all interesting stuff has been seen:
+ "[rx+[ry+].S" or "[rx+[ry].S". We only need to
+ expect a final ']', which we'll do in a common
+ closing session. */
+ }
+ /* Seen "[rN+", but not a '[', so check if we have a
+ register. */
+ else if (get_gen_reg (cPP, &index_reg_number))
+ {
+ /* This is indexed mode: "[rN+rM.S]" or
+ "[rN+rM.S+]". */
+ int size_bits;
+ prefixp->kind = PREFIX_BIAP;
+ prefixp->opcode
+ = (BIAP_OPCODE
+ | prefixp->base_reg_number /* << 0 */
+ | (index_reg_number << 12));
+
+ /* Consume the ".S". */
+ if (! get_bwd_size_modifier (cPP, &size_bits))
+ /* Missing size, so fail. */
+ return 0;
+ else
+ /* Size found. Add that piece and drop down to
+ the common checking of the closing ']'. */
+ prefixp->opcode |= size_bits << 4;
+ }
+ /* Seen "[rN+", but not a '[' or a register, so then
+ it must be a constant "I".
+
+ As a quality of implementation improvement, we check for a
+ closing ']', like in an erroneous "[rN+]". If we don't,
+ the expression parser will emit a confusing "bad
+ expression" when it sees the ']', probably because it
+ doesn't like seeing no expression. */
+ else if (**cPP != ']' && cris_get_expression (cPP, &prefixp->expr))
+ {
+ /* Expression found, so fill in the bits of offset
+ mode and drop down to check the closing ']'. */
+ prefixp->kind = PREFIX_BDAP_IMM;
+
+ /* We tentatively put an opcode corresponding to a 32-bit
+ operand here, although it may be relaxed when there's no
+ PIC specifier for the operand. */
+ prefixp->opcode
+ = (BDAP_INDIR_OPCODE
+ | (prefixp->base_reg_number << 12)
+ | (AUTOINCR_BIT << 8)
+ | (2 << 4)
+ | REG_PC /* << 0 */);
+
+ /* This can have a PIC suffix, specifying reloc type to use. */
+ if ((pic || tls) && **cPP == RELOC_SUFFIX_CHAR)
+ {
+ unsigned int relocsize;
+
+ cris_get_reloc_suffix (cPP, &prefixp->reloc, &prefixp->expr);
+
+ /* Tweak the size of the immediate operand in the prefix
+ opcode if it isn't what we set. */
+ relocsize = cris_get_specified_reloc_size (prefixp->reloc);
+ if (relocsize != 4)
+ prefixp->opcode
+ = ((prefixp->opcode & ~(3 << 4))
+ | ((relocsize >> 1) << 4));
+ }
+ }
+ else
+ /* Nothing valid here: lose. */
+ return 0;
+ }
+ /* Seen "[rN" but no '+', so check if it's a '-'. */
+ else if (**cPP == '-')
+ {
+ /* Yep, we must have offset mode. */
+ if (! cris_get_expression (cPP, &prefixp->expr))
+ /* No expression, so we lose. */
+ return 0;
+ else
+ {
+ /* Expression found to make this offset mode, so
+ fill those bits and drop down to check the
+ closing ']'.
+
+ Note that we don't allow a PIC suffix for
+ an operand with a minus sign like this. */
+ prefixp->kind = PREFIX_BDAP_IMM;
+ }
+ }
+ else
+ {
+ /* We've seen "[rN", but not '+' or '-'; rather a ']'.
+ Hmm. Normally this is a simple indirect mode that we
+ shouldn't match, but if we expect ']', then we have a
+ zero offset, so it can be a three-address-operand,
+ like "[rN],rO,rP", thus offset mode.
+
+ Don't eat the ']', that will be done in the closing
+ ceremony. */
+ 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;
+ }
+ }
+ /* A '[', but no second '[', and no register. Check if we
+ have an expression, making this "[I]" for a double-indirect
+ prefix. */
+ else if (cris_get_expression (cPP, &prefixp->expr))
+ {
+ /* Expression found, the so called absolute mode for a
+ double-indirect prefix on PC. */
+ prefixp->kind = PREFIX_DIP;
+ prefixp->opcode = DIP_OPCODE | (AUTOINCR_BIT << 8) | REG_PC;
+ prefixp->reloc = BFD_RELOC_32;
+
+ /* For :GD and :IE, it makes sense to have TLS specifiers here. */
+ if ((pic || tls) && **cPP == RELOC_SUFFIX_CHAR)
+ cris_get_reloc_suffix (cPP, &prefixp->reloc, &prefixp->expr);
+ }
+ else
+ /* Neither '[' nor register nor expression. We lose. */
+ return 0;
+
+ /* We get here as a closing ceremony to a successful match. We just
+ need to check the closing ']'. */
+ if (**cPP != ']')
+ /* Oops. Close but no air-polluter. */
+ return 0;
+
+ /* Don't forget to consume that ']', before returning in glory. */
+ (*cPP)++;
+ return 1;
+}
+
+/* Get an expression from the string pointed out by *cPP.
+ The pointer *cPP is advanced to the character following the expression
+ on a success, or retains its original value otherwise.
+
+ cPP Pointer to pointer to string beginning with the expression.
+
+ exprP Pointer to structure containing the expression.
+
+ Return 1 iff a correct expression is found. */
+
+static int
+cris_get_expression (char **cPP, expressionS *exprP)
+{
+ char *saved_input_line_pointer;
+
+ /* The "expression" function expects to find an expression at the
+ global variable input_line_pointer, so we have to save it to give
+ the impression that we don't fiddle with global variables. */
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = *cPP;
+
+ /* Avoid a common error, confusing addressing modes. Beware that the
+ call to expression below does not signal that error; it treats []
+ as parentheses, unless #define NEED_INDEX_OPERATOR in which case it
+ gives them other confusing semantics rather than plain outlawing
+ them, which is what we want. */
+ if (*input_line_pointer == '[')
+ {
+ input_line_pointer = saved_input_line_pointer;
+ return 0;
+ }
+
+ expression (exprP);
+ if (exprP->X_op == O_illegal || exprP->X_op == O_absent)
+ {
+ input_line_pointer = saved_input_line_pointer;
+ return 0;
+ }
+
+ /* Everything seems to be fine, just restore the global
+ input_line_pointer and say we're successful. */
+ *cPP = input_line_pointer;
+ input_line_pointer = saved_input_line_pointer;
+ return 1;
+}
+
+/* Get a sequence of flag characters from *spp. The pointer *cPP is
+ advanced to the character following the expression. The flag
+ characters are consecutive, no commas or spaces.
+
+ cPP Pointer to pointer to string beginning with the expression.
+
+ flagp Pointer to int to return the flags expression.
+
+ Return 1 iff a correct flags expression is found. */
+
+static int
+get_flags (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:
+ /* We consider this successful if we stop at a comma or
+ whitespace. Anything else, and we consider it a failure. */
+ if (**cPP != ','
+ && **cPP != 0
+ && ! ISSPACE (**cPP))
+ return 0;
+ else
+ return 1;
+ }
+
+ /* Don't forget to consume each flag character. */
+ (*cPP)++;
+ }
+}
+
+/* Generate code and fixes for a BDAP prefix.
+ For v32, this handles ADDOQ because thankfully the opcodes are the
+ same.
+
+ base_regno Int containing the base register number.
+
+ exprP Pointer to structure containing the offset expression. */
+
+static void
+gen_bdap (int base_regno, expressionS *exprP)
+{
+ unsigned int opcode;
+ char *opcodep;
+
+ /* Put out the prefix opcode; assume quick immediate mode at first. */
+ 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)
+ {
+ /* We have an absolute expression that we know the size of right
+ now. */
+ long int value;
+ int size;
+
+ value = exprP->X_add_number;
+ if (value < -32768 || value > 32767)
+ /* Outside range for a "word", make it a dword. */
+ size = 2;
+ else
+ /* Assume "word" size. */
+ size = 1;
+
+ /* If this is a signed-byte value, we can fit it into the prefix
+ insn itself. */
+ if (value >= -128 && value <= 127)
+ opcodep[0] = value;
+ else
+ {
+ /* This is a word or dword displacement, which will be put in a
+ word or dword after the prefix. */
+ 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
+ {
+ /* Handle complex expressions. */
+ valueT addvalue
+ = SIMPLE_EXPR (exprP) ? exprP->X_add_number : 0;
+ symbolS *sym
+ = (SIMPLE_EXPR (exprP)
+ ? exprP->X_add_symbol : make_expr_symbol (exprP));
+
+ /* The expression is not defined yet but may become absolute. We
+ make it a relocation to be relaxed. */
+ frag_var (rs_machine_dependent, 4, 0,
+ ENCODE_RELAX (STATE_BASE_PLUS_DISP_PREFIX, STATE_UNDF),
+ sym, addvalue, opcodep);
+ }
+}
+
+/* Encode a branch displacement in the range -256..254 into the form used
+ by CRIS conditional branch instructions.
+
+ offset The displacement value in bytes. */
+
+static int
+branch_disp (int offset)
+{
+ int disp;
+
+ /* Adjust all short branch offsets here. */
+ if (cris_arch == arch_crisv32 || cris_arch == arch_cris_common_v10_v32)
+ offset += 2;
+
+ disp = offset & 0xFE;
+
+ if (offset < 0)
+ disp |= 1;
+
+ return disp;
+}
+
+/* Generate code and fixes for a 32-bit conditional branch instruction
+ created by "extending" an existing 8-bit branch instruction.
+
+ opcodep Pointer to the word containing the original 8-bit branch
+ instruction.
+
+ writep Pointer to "extension area" following the first instruction
+ word.
+
+ fragP Pointer to the frag containing the instruction.
+
+ add_symP, Parts of the destination address expression.
+ sub_symP,
+ add_num. */
+
+static void
+gen_cond_branch_32 (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 if (pic)
+ {
+ nop_opcode = NOP_OPCODE;
+ opc_offset = 10;
+ branch_offset = -2 - 8;
+ }
+ else
+ {
+ nop_opcode = NOP_OPCODE;
+ opc_offset = 8;
+ branch_offset = -2 - 6;
+ }
+
+ /* We should never get here for compatibility mode. */
+ 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"));
+
+ /* Here, writep points to what will be opcodep + 2. First, we change
+ the actual branch in opcodep[0] and opcodep[1], so that in the
+ final insn, it will look like:
+ opcodep+10: Bcc .-6
+
+ This means we don't have to worry about changing the opcode or
+ messing with the delay-slot instruction. So, we move it to last in
+ the "extended" branch, and just change the displacement. Admittedly,
+ it's not the optimal extended construct, but we should get this
+ rarely enough that it shouldn't matter. */
+
+ writep[opc_offset] = branch_disp (branch_offset);
+ writep[opc_offset + 1] = opcodep[1];
+
+ /* Then, we change the branch to an unconditional branch over the
+ extended part, to the new location of the Bcc:
+ opcodep: BA .+10
+ opcodep+2: NOP
+
+ Note that these two writes are to currently different locations,
+ merged later. */
+
+ md_number_to_chars (opcodep, BA_QUICK_OPCODE
+ + (cris_arch == arch_crisv32 ? 12 : (pic ? 10 : 8)),
+ 2);
+ md_number_to_chars (writep, nop_opcode, 2);
+
+ /* Then the extended thing, the 32-bit jump insn.
+ opcodep+4: JUMP [PC+]
+ or, in the PIC case,
+ opcodep+4: MOVE [PC=PC+N],P0. */
+
+ md_number_to_chars (writep + 2,
+ cris_arch == arch_crisv32
+ ? BA_DWORD_OPCODE
+ : (pic ? MOVE_PC_INCR_OPCODE_PREFIX
+ : JUMP_PC_INCR_OPCODE), 2);
+
+ /* We have to fill in the actual value too.
+ opcodep+6: .DWORD
+ This is most probably an expression, but we can cope with an absolute
+ value too. FIXME: Testcase needed with and without pic. */
+
+ if (add_symP == NULL && sub_symP == NULL)
+ {
+ /* An absolute address. */
+ 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"));
+
+ /* Not absolute, we have to make it a frag for later evaluation. */
+ 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)
+ /* Follow it with a "NOP" for CRISv32. */
+ md_number_to_chars (writep + 8, NOP_OPCODE_V32, 2);
+ else if (pic)
+ /* ...and the rest of the move-opcode for pre-v32 PIC. */
+ md_number_to_chars (writep + 8, MOVE_PC_INCR_OPCODE_SUFFIX, 2);
+}
+
+/* Get the size of an immediate-reloc in bytes. Only valid for
+ specified relocs (TLS, PIC). */
+
+static unsigned int
+cris_get_specified_reloc_size (bfd_reloc_code_real_type reloc)
+{
+ return
+ reloc == BFD_RELOC_CRIS_16_GOTPLT
+ || reloc == BFD_RELOC_CRIS_16_GOT
+ || reloc == BFD_RELOC_CRIS_16_GOT_GD
+ || reloc == BFD_RELOC_CRIS_16_DTPREL
+ || reloc == BFD_RELOC_CRIS_16_GOT_TPREL
+ || reloc == BFD_RELOC_CRIS_16_TPREL
+ ? 2 : 4;
+}
+
+/* Store a reloc type at *RELOCP corresponding to the PIC suffix at *CPP.
+ Adjust *EXPRP with any addend found after the PIC suffix. */
+
+static void
+cris_get_reloc_suffix (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;
+ bfd_boolean pic_p;
+ bfd_boolean tls_p;
+ } pic_suffixes[] =
+ {
+#undef PICMAP
+#define PICMAP(s, r) {s, sizeof (s) - 1, r, TRUE, FALSE}
+#define PICTLSMAP(s, r) {s, sizeof (s) - 1, r, TRUE, TRUE}
+#define TLSMAP(s, r) {s, sizeof (s) - 1, r, FALSE, TRUE}
+ /* Keep this in order with longest unambiguous prefix first. */
+ 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),
+ PICTLSMAP ("GDGOTREL16", BFD_RELOC_CRIS_16_GOT_GD),
+ PICTLSMAP ("GDGOTREL", BFD_RELOC_CRIS_32_GOT_GD),
+ TLSMAP ("GD", BFD_RELOC_CRIS_32_GD),
+ PICTLSMAP ("DTPREL16", BFD_RELOC_CRIS_16_DTPREL),
+ PICTLSMAP ("DTPREL", BFD_RELOC_CRIS_32_DTPREL),
+ TLSMAP ("IE", BFD_RELOC_CRIS_32_IE),
+ PICTLSMAP ("TPOFFGOT16", BFD_RELOC_CRIS_16_GOT_TPREL),
+ PICTLSMAP ("TPOFFGOT", BFD_RELOC_CRIS_32_GOT_TPREL),
+ TLSMAP ("TPOFF16", BFD_RELOC_CRIS_16_TPREL),
+ TLSMAP ("TPOFF", BFD_RELOC_CRIS_32_TPREL)
+ };
+
+ /* We've already seen the ':', so consume it. */
+ 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])
+ /* PIC and non-PIC relocations are exclusive. */
+ && (pic != 0) == (pic_suffixes[i].pic_p != 0)
+ /* But TLS can be active for non-TLS relocations too. */
+ && (pic_suffixes[i].tls_p == 0 || tls))
+ {
+ /* We have a match. Consume the suffix and set the relocation
+ type. */
+ s += pic_suffixes[i].len;
+
+ /* There can be a constant term appended. If so, we will add it
+ to *EXPRP. */
+ if (*s == '+' || *s == '-')
+ {
+ if (! cris_get_expression (&s, &const_expr))
+ /* There was some kind of syntax error. Bail out. */
+ break;
+
+ /* Allow complex expressions as the constant part. It still
+ has to be an assembly-time constant or there will be an
+ error emitting the reloc. This makes the PIC qualifiers
+ idempotent; foo:GOTOFF+32 == foo+32:GOTOFF. The former we
+ recognize here; the latter is parsed in the incoming
+ expression. */
+ 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;
+ }
+ }
+
+ /* No match. Don't consume anything; fall back and there will be a
+ syntax error. */
+}
+
+/* This *could* have been:
+
+ 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.
+
+ type A character from FLTCHARS that describes what kind of
+ floating-point number is wanted.
+
+ litp A pointer to an array that the result should be stored in.
+
+ sizep A pointer to an integer where the size of the result is stored.
+
+ But we don't support floating point constants in assembly code *at all*,
+ since it's suboptimal and just opens up bug opportunities. GCC emits
+ the bit patterns as hex. All we could do here is to emit what GCC
+ would have done in the first place. *Nobody* writes floating-point
+ code as assembly code, but if they do, they should be able enough to
+ find out the correct bit patterns and use them. */
+
+char *
+md_atof (int type ATTRIBUTE_UNUSED, char *litp ATTRIBUTE_UNUSED,
+ int *sizep ATTRIBUTE_UNUSED)
+{
+ /* FIXME: Is this function mentioned in the internals.texi manual? If
+ not, add it. */
+ return _("Bad call to md_atof () - floating point formats are not supported");
+}
+
+/* Turn a number as a fixS * into a series of bytes that represents the
+ number on the target machine. The purpose of this procedure is the
+ same as that of md_number_to_chars but this procedure is supposed to
+ handle general bit field fixes and machine-dependent fixups.
+
+ bufp Pointer to an array where the result should be stored.
+
+ val The value to store.
+
+ n The number of bytes in "val" that should be stored.
+
+ fixP The fix to be applied to the bit field starting at bufp.
+
+ seg The segment containing this number. */
+
+static void
+cris_number_to_imm (char *bufp, long val, int n, fixS *fixP, segT seg)
+{
+ segT sym_seg;
+
+ know (n <= 4);
+ know (fixP);
+
+ /* We put the relative "vma" for the other segment for inter-segment
+ relocations in the object data to stay binary "compatible" (with an
+ uninteresting old version) for the relocation.
+ Maybe delete some day. */
+ 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)
+ {
+ /* These must be fully resolved when getting here. */
+ 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:
+ ;
+ }
+
+ /* Only use the computed value for old-arch binaries. For all
+ others, where we're going to output a relocation, put 0 in the
+ code. */
+ if (cris_arch != arch_cris_any_v0_v10
+ && (fixP->fx_addsy != NULL || fixP->fx_pcrel))
+ val = 0;
+
+ switch (fixP->fx_r_type)
+ {
+ /* Ditto here, we put the addend into the object code as
+ well as the reloc addend. Keep it that way for now, to simplify
+ regression tests on the object file contents. FIXME: Seems
+ uninteresting now that we have a test suite. */
+
+ case BFD_RELOC_CRIS_32_GOT_GD:
+ case BFD_RELOC_CRIS_16_GOT_GD:
+ case BFD_RELOC_CRIS_32_GD:
+ case BFD_RELOC_CRIS_32_IE:
+ case BFD_RELOC_CRIS_32_DTPREL:
+ case BFD_RELOC_CRIS_16_DTPREL:
+ case BFD_RELOC_CRIS_32_GOT_TPREL:
+ case BFD_RELOC_CRIS_16_GOT_TPREL:
+ case BFD_RELOC_CRIS_32_TPREL:
+ case BFD_RELOC_CRIS_16_TPREL:
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (IS_ELF && fixP->fx_addsy != NULL)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+#endif
+ /* Fall through. */
+
+ 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:
+ /* We don't want to put in any kind of non-zero bits in the data
+ being relocated for these. */
+ md_number_to_chars (bufp, 0, n);
+ break;
+
+ case BFD_RELOC_32_PCREL:
+ /* If this one isn't fully resolved, we don't want to put non-zero
+ in the object. */
+ if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
+ val = 0;
+
+ /* Fall through. */
+ case BFD_RELOC_32:
+ /* No use having warnings here, since most hosts have a 32-bit type
+ for "long" (which will probably change soon, now that I wrote
+ this). */
+ bufp[3] = (val >> 24) & 0xFF;
+ bufp[2] = (val >> 16) & 0xFF;
+ bufp[1] = (val >> 8) & 0xFF;
+ bufp[0] = val & 0xFF;
+ break;
+
+ /* FIXME: The 16 and 8-bit cases should have a way to check
+ whether a signed or unsigned (or any signedness) number is
+ accepted. */
+
+ 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);
+ 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);
+ 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);
+ 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);
+ bufp[0] = val & 0xFF;
+ break;
+
+ case BFD_RELOC_CRIS_LAPCQ_OFFSET:
+ /* FIXME: Test-cases for out-of-range values. Probably also need
+ to use as_bad_where. */
+ 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);
+ 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);
+ 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);
+ 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);
+ bufp[0] |= val & 0x3F;
+ break;
+
+ case BFD_RELOC_CRIS_BDISP8:
+ bufp[0] = branch_disp (val);
+ break;
+
+ case BFD_RELOC_NONE:
+ /* May actually happen automatically. For example at broken
+ words, if the word turns out not to be broken.
+ FIXME: When? Which testcase? */
+ if (! fixP->fx_addsy)
+ md_number_to_chars (bufp, val, n);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ /* This borrowed from tc-ppc.c on a whim. */
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ /* Fall through. */
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ BAD_CASE (fixP->fx_r_type);
+ }
+}
+
+/* Processes machine-dependent command line options. Called once for
+ each option on the command line that the machine-independent part of
+ GAS does not understand. */
+
+int
+md_parse_option (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:
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ as_bad (_("--pic is invalid for this object format"));
+ pic = TRUE;
+ if (cris_arch != arch_crisv32)
+ md_long_jump_size = cris_any_v0_v10_long_jump_size_pic;
+ else
+ md_long_jump_size = crisv32_long_jump_size;
+ 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
+ {
+ if (pic)
+ md_long_jump_size = cris_any_v0_v10_long_jump_size_pic;
+ 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;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segT segment, valueT size)
+{
+ /* Round all sects to multiple of 4, except the bss section, which
+ we'll round to word-size.
+
+ FIXME: Check if this really matters. All sections should be
+ rounded up, and all sections should (optionally) be assumed to be
+ dword-aligned, it's just that there is actual usage of linking to a
+ multiple of two. */
+ if (OUTPUT_FLAVOR == bfd_target_aout_flavour)
+ {
+ if (segment == bss_section)
+ return (size + 1) & ~1;
+ return (size + 3) & ~3;
+ }
+ else
+ {
+ /* FIXME: Is this wanted? It matches the testsuite, but that's not
+ really a valid reason. */
+ if (segment == text_section)
+ return (size + 3) & ~3;
+ }
+
+ return size;
+}
+
+/* Generate a machine-dependent relocation. */
+arelent *
+tc_gen_reloc (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:
+ case BFD_RELOC_CRIS_32_GOT_GD:
+ case BFD_RELOC_CRIS_16_GOT_GD:
+ case BFD_RELOC_CRIS_32_GD:
+ case BFD_RELOC_CRIS_32_IE:
+ case BFD_RELOC_CRIS_32_DTPREL:
+ case BFD_RELOC_CRIS_16_DTPREL:
+ case BFD_RELOC_CRIS_32_GOT_TPREL:
+ case BFD_RELOC_CRIS_16_GOT_TPREL:
+ case BFD_RELOC_CRIS_32_TPREL:
+ case BFD_RELOC_CRIS_16_TPREL:
+ 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));
+ gas_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;
+
+ /* This is the standard place for KLUDGEs to work around bugs in
+ bfd_install_relocation (first such note in the documentation
+ appears with binutils-2.8).
+
+ That function bfd_install_relocation does the wrong thing with
+ putting stuff into the addend of a reloc (it should stay out) for a
+ weak symbol. The really bad thing is that it adds the
+ "segment-relative offset" of the symbol into the reloc. In this
+ case, the reloc should instead be relative to the symbol with no
+ other offset than the assembly code shows; and since the symbol is
+ weak, any local definition should be ignored until link time (or
+ thereafter).
+ To wit: weaksym+42 should be weaksym+42 in the reloc,
+ not weaksym+(offset_from_segment_of_local_weaksym_definition)
+
+ To "work around" this, we subtract the segment-relative offset of
+ "known" weak symbols. This evens out the extra offset.
+
+ That happens for a.out but not for ELF, since for ELF,
+ bfd_install_relocation uses the "special function" field of the
+ howto, and does not execute the code that needs to be undone. */
+
+ 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;
+}
+
+/* Machine-dependent usage-output. */
+
+void
+md_show_usage (FILE *stream)
+{
+ /* The messages are formatted to line up with the generic options. */
+ 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"));
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ fprintf (stream, "%s",
+ _(" --pic Enable generation of position-independent code.\n"));
+#endif
+ fprintf (stream, "%s",
+ _(" --march=<arch> Generate code for <arch>. Valid choices for <arch>\n\
+ are v0_v10, v10, v32 and common_v10_v32.\n"));
+}
+
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ /* This assignment truncates upper bits if valueT is 64 bits (as with
+ --enable-64-bit-bfd), which is fine here, though we cast to avoid
+ any compiler warnings. */
+ 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
+ {
+ /* We can't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("expression too complex"));
+
+ /* This operand-type is scaled. */
+ if (fixP->fx_r_type == BFD_RELOC_CRIS_LAPCQ_OFFSET)
+ val /= 2;
+ cris_number_to_imm (buf, val, fixP->fx_size, fixP, seg);
+ }
+}
+
+/* All relocations are relative to the location just after the fixup;
+ the address of the fixup plus its size. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ /* FIXME: We get here only at the end of assembly, when X in ".-X" is
+ still unknown. Since we don't have pc-relative relocations in a.out,
+ this is invalid. What to do if anything for a.out, is to add
+ pc-relative relocations everywhere including the elinux program
+ loader. For ELF, allow straight-forward PC-relative relocations,
+ which are always relative to the location after the relocation. */
+ 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;
+}
+
+/* We have no need to give defaults for symbol-values. */
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* If this function returns non-zero, it prevents the relocation
+ against symbol(s) in the FIXP from being replaced with relocations
+ against section symbols, and guarantees that a relocation will be
+ emitted even when the value can be resolved locally. */
+int
+md_cris_force_relocation (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);
+}
+
+/* Check and emit error if broken-word handling has failed to fix up a
+ case-table. This is called from write.c, after doing everything it
+ knows about how to handle broken words. */
+
+void
+tc_cris_check_adjusted_broken_word (offsetT new_offset, struct broken_word *brokwP)
+{
+ if (new_offset > 32767 || new_offset < -32768)
+ /* We really want a genuine error, not a warning, so make it one. */
+ as_bad_where (brokwP->frag->fr_file, brokwP->frag->fr_line,
+ _("Adjusted signed .word (%ld) overflows: `switch'-statement too large."),
+ (long) new_offset);
+}
+
+/* Make a leading REGISTER_PREFIX_CHAR mandatory for all registers. */
+
+static void
+cris_force_reg_prefix (void)
+{
+ demand_register_prefix = TRUE;
+}
+
+/* Do not demand a leading REGISTER_PREFIX_CHAR for all registers. */
+
+static void
+cris_relax_reg_prefix (void)
+{
+ demand_register_prefix = FALSE;
+}
+
+/* Adjust for having a leading '_' on all user symbols. */
+
+static void
+cris_sym_leading_underscore (void)
+{
+ /* We can't really do anything more than assert that what the program
+ thinks symbol starts with agrees with the command-line options, since
+ the bfd is already created. */
+
+ if (!symbols_have_leading_underscore)
+ as_bad (_(".syntax %s requires command-line option `--underscore'"),
+ SYNTAX_USER_SYM_LEADING_UNDERSCORE);
+}
+
+/* Adjust for not having any particular prefix on user symbols. */
+
+static void cris_sym_no_leading_underscore (void)
+{
+ if (symbols_have_leading_underscore)
+ as_bad (_(".syntax %s requires command-line option `--no-underscore'"),
+ SYNTAX_USER_SYM_NO_LEADING_UNDERSCORE);
+}
+
+/* Handle the .syntax pseudo, which takes an argument that decides what
+ syntax the assembly code has. */
+
+static void
+s_syntax (int ignore ATTRIBUTE_UNUSED)
+{
+ static const struct syntaxes
+ {
+ const char *const operand;
+ void (*fn) (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"));
+}
+
+/* Wrapper for dwarf2_directive_file to emit error if this is seen when
+ not emitting ELF. */
+
+static void
+s_cris_file (int dummy)
+{
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ as_bad (_("Pseudodirective .file is only valid when generating ELF"));
+ else
+ dwarf2_directive_file (dummy);
+}
+
+/* Wrapper for dwarf2_directive_loc to emit error if this is seen when not
+ emitting ELF. */
+
+static void
+s_cris_loc (int dummy)
+{
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour)
+ as_bad (_("Pseudodirective .loc is only valid when generating ELF"));
+ else
+ dwarf2_directive_loc (dummy);
+}
+
+/* Worker for .dtpoffd: generate a R_CRIS_32_DTPREL reloc, as for
+ expr:DTPREL but for use in debug info. */
+
+static void
+s_cris_dtpoff (int bytes)
+{
+ expressionS ex;
+ char *p;
+
+ if (bytes != 4)
+ as_fatal (_("internal inconsistency problem: %s called for %d bytes"),
+ __FUNCTION__, bytes);
+
+ expression (&ex);
+
+ p = frag_more (bytes);
+ md_number_to_chars (p, 0, bytes);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE,
+ BFD_RELOC_CRIS_32_DTPREL);
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Translate a <arch> string (as common to --march=<arch> and .arch <arch>)
+ into an enum. If the string *STR is recognized, *STR is updated to point
+ to the end of the string. If the string is not recognized,
+ arch_cris_unknown is returned. */
+
+static enum cris_archs
+cris_arch_from_string (char **str)
+{
+ static const struct cris_arch_struct
+ {
+ const char *const name;
+ enum cris_archs arch;
+ } arch_table[] =
+ /* Keep in order longest-first for choices where one is a prefix
+ of another. */
+ {{"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;
+}
+
+/* Return nonzero if architecture version ARCH matches version range in
+ IVER. */
+
+static int
+cris_insn_ver_valid_for_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);
+ }
+}
+
+/* Assert that the .arch ARCHCHOICE1 is compatible with the specified or
+ default --march=<ARCHCHOICE2> option. */
+
+static void
+s_cris_arch (int dummy ATTRIBUTE_UNUSED)
+{
+ /* Right now we take the easy route and check for sameness. It's not
+ obvious that allowing e.g. --march=v32 and .arch common_v0_v32
+ would be more useful than confusing, implementation-wise and
+ user-wise. */
+
+ 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"));
+
+ /* For this one, str does not reflect the end of the operand,
+ since there was no matching arch. Skip it manually; skip
+ things that can be part of a word (a name). */
+ 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;
+}
+
+/*
+ * Local variables:
+ * eval: (c-set-style "gnu")
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/gas/config/tc-cris.h b/gas/config/tc-cris.h
new file mode 100644
index 0000000..ce3ad73
--- /dev/null
+++ b/gas/config/tc-cris.h
@@ -0,0 +1,170 @@
+/* tc-cris.h -- Header file for tc-cris.c, the CRIS GAS port.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ Contributed by Axis Communications AB, Lund, Sweden.
+ Originally written for GAS 1.38.1 by Mikael Asker.
+ Updates, BFDizing, GNUifying and ELF by Hans-Peter Nilsson.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/* See the GAS "internal" document for general documentation on this.
+ It is called internals.texi (internals.info when makeinfo:d), but is
+ not installed or makeinfo:d by "make info". */
+
+/* Functions and variables that aren't declared in tc.h are declared here,
+ with the type/prototype that is used in the local extern-declaration of
+ their usage. */
+
+#ifndef TC_CRIS
+#define TC_CRIS
+
+/* Multi-target support is always on. */
+extern const char *cris_target_format (void);
+#define TARGET_FORMAT cris_target_format ()
+
+#define TARGET_ARCH bfd_arch_cris
+
+extern unsigned int cris_mach (void);
+#define TARGET_MACH (cris_mach ())
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+extern const char *md_shortopts;
+extern struct option md_longopts[];
+extern size_t md_longopts_size;
+
+extern const pseudo_typeS md_pseudo_table[];
+
+#define tc_comment_chars cris_comment_chars
+extern const char cris_comment_chars[];
+extern const char line_comment_chars[];
+extern const char line_separator_chars[];
+extern const char EXP_CHARS[];
+extern const char FLT_CHARS[];
+
+/* This should be optional, since it is ignored as an escape (assumed to
+ be itself) if it is not recognized. */
+#define ONLY_STANDARD_ESCAPES
+
+/* Note that we do not define TC_EQUAL_IN_INSN, since its current use is
+ in the instruction rather than the operand, and thus does not come to
+ use for side-effect assignments such as "and.d [r0 = r1 + 42], r3". */
+#define md_operand(x)
+
+#define md_number_to_chars number_to_chars_littleendian
+extern const struct relax_type md_cris_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_cris_relax_table
+
+long cris_relax_frag (segT, fragS *, long);
+
+/* GAS only handles relaxations for pc-relative data targeting addresses
+ in the same segment, so we have to handle the rest on our own. */
+#define md_relax_frag(SEG, FRAGP, STRETCH) \
+ ((FRAGP)->fr_symbol != NULL \
+ && S_GET_SEGMENT ((FRAGP)->fr_symbol) == (SEG) \
+ ? relax_frag (SEG, FRAGP, STRETCH) \
+ : cris_relax_frag (SEG, FRAGP, STRETCH))
+
+#define TC_FORCE_RELOCATION(FIX) md_cris_force_relocation (FIX)
+extern int md_cris_force_relocation (struct fix *);
+
+#define IS_CRIS_PIC_RELOC(RTYPE) \
+ ((RTYPE) == BFD_RELOC_CRIS_16_GOT \
+ || (RTYPE) == BFD_RELOC_CRIS_32_GOT \
+ || (RTYPE) == BFD_RELOC_CRIS_16_GOTPLT \
+ || (RTYPE) == BFD_RELOC_CRIS_32_GOTPLT \
+ || (RTYPE) == BFD_RELOC_CRIS_32_GOTREL \
+ || (RTYPE) == BFD_RELOC_CRIS_32_PLT_GOTREL \
+ || (RTYPE) == BFD_RELOC_CRIS_32_PLT_PCREL)
+
+/* Make sure we don't resolve fixups for which we want to emit dynamic
+ relocations. */
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || IS_CRIS_PIC_RELOC ((FIX)->fx_r_type) \
+ || TC_FORCE_RELOCATION (FIX))
+
+/* For some reloc types, don't adjust fixups by reducing to a section
+ symbol. */
+#define tc_fix_adjustable(FIX) \
+ ((FIX)->fx_r_type != BFD_RELOC_VTABLE_INHERIT \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_ENTRY \
+ && (! IS_CRIS_PIC_RELOC ((FIX)->fx_r_type) \
+ || (FIX)->fx_r_type == BFD_RELOC_CRIS_32_GOTREL))
+
+/* FIXME: This *should* be a redundant definition, as the
+ TC_FORCE_RELOCATION* definitions already told about the cases where
+ we *don't* want the symbol value calculated. Here we seem to answer
+ the "are you sure" question. It certainly has very little to do with
+ whether the symbol value is passed to md_apply_fix. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* When we have fixups against constant expressions, we get a GAS-specific
+ section symbol at no extra charge for obscure reasons in
+ adjust_reloc_syms. Since ELF outputs section symbols, it gladly
+ outputs this "*ABS*" symbol in every object. Avoid that.
+ Also, don't emit undefined symbols (that aren't used in relocations).
+ They pop up when tentatively parsing register names as symbols. */
+#define tc_frob_symbol(symp, punt) \
+ do { \
+ if ((OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ && (symp) == section_symbol (absolute_section)) \
+ || ! S_IS_DEFINED (symp)) \
+ (punt) = 1; \
+ } while (0)
+
+#define LISTING_HEADER "GAS for CRIS"
+
+#if 0
+/* The testsuite does not let me define these, although they IMHO should
+ be preferred over the default. */
+#define LISTING_WORD_SIZE 2
+#define LISTING_LHS_WIDTH 4
+#define LISTING_LHS_WIDTH_SECOND 4
+#endif
+
+/* END of declaration and definitions described in the "internals"
+ document. */
+
+/* Do this, or we will never know what hit us when the
+ broken-word-fixes break. Do _not_ use WARN_SIGNED_OVERFLOW_WORD,
+ it is only for use with WORKING_DOT_WORD and warns about most stuff.
+ (still in 2.9.1). */
+struct broken_word;
+extern void tc_cris_check_adjusted_broken_word (offsetT,
+ struct broken_word *);
+#define TC_CHECK_ADJUSTED_BROKEN_DOT_WORD(new_offset, brokw) \
+ tc_cris_check_adjusted_broken_word ((offsetT) (new_offset), brokw)
+
+/* We don't want any implicit alignment, so we do nothing. */
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) do { } while (0)
+
+/* CRIS instructions, with operands and prefixes included, are a multiple
+ of two bytes long. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* Make port immune to unwanted difference in te-generic.h vs. te-linux.h. */
+#define LOCAL_LABELS_DOLLAR 1
+
+#endif /* TC_CRIS */
+/*
+ * Local variables:
+ * eval: (c-set-style "gnu")
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/gas/config/tc-crx.c b/gas/config/tc-crx.c
new file mode 100644
index 0000000..61a31b3
--- /dev/null
+++ b/gas/config/tc-crx.c
@@ -0,0 +1,2010 @@
+/* tc-crx.c -- Assembler code for the CRX CPU core.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ Contributed by Tomer Levi, NSC, Israel.
+ Originally written for GAS 2.12 by Tomer Levi, NSC, Israel.
+ Updates, BFDizing, GNUifying and ELF support by Tomer Levi.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include "bfd_stdint.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "opcode/crx.h"
+#include "elf/crx.h"
+
+/* Word is considered here as a 16-bit unsigned short int. */
+#define WORD_SHIFT 16
+
+/* Register is 4-bit size. */
+#define REG_SIZE 4
+
+/* Maximum size of a single instruction (in words). */
+#define INSN_MAX_SIZE 3
+
+/* Maximum bits which may be set in a `mask16' operand. */
+#define MAX_REGS_IN_MASK16 8
+
+/* Utility macros for string comparison. */
+#define streq(a, b) (strcmp (a, b) == 0)
+#define strneq(a, b, c) (strncmp (a, b, c) == 0)
+
+/* Assign a number NUM, shifted by SHIFT bytes, into a location
+ pointed by index BYTE of array 'output_opcode'. */
+#define CRX_PRINT(BYTE, NUM, SHIFT) output_opcode[BYTE] |= (NUM << SHIFT)
+
+/* Operand errors. */
+typedef enum
+ {
+ OP_LEGAL = 0, /* Legal operand. */
+ OP_OUT_OF_RANGE, /* Operand not within permitted range. */
+ OP_NOT_EVEN, /* Operand is Odd number, should be even. */
+ OP_ILLEGAL_DISPU4, /* Operand is not within DISPU4 range. */
+ OP_ILLEGAL_CST4, /* Operand is not within CST4 range. */
+ OP_NOT_UPPER_64KB /* Operand is not within the upper 64KB
+ (0xFFFF0000-0xFFFFFFFF). */
+ }
+op_err;
+
+/* Opcode mnemonics hash table. */
+static struct hash_control *crx_inst_hash;
+/* CRX registers hash table. */
+static struct hash_control *reg_hash;
+/* CRX coprocessor registers hash table. */
+static struct hash_control *copreg_hash;
+/* Current instruction we're assembling. */
+const inst *instruction;
+
+/* Global variables. */
+
+/* Array to hold an instruction encoding. */
+long output_opcode[2];
+
+/* Nonzero means a relocatable symbol. */
+int relocatable;
+
+/* A copy of the original instruction (used in error messages). */
+char ins_parse[MAX_INST_LEN];
+
+/* The current processed argument number. */
+int cur_arg_num;
+
+/* Generic assembler global variables which must be defined by all targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant as in 0f12.456 */
+const char FLT_CHARS[] = "f'";
+
+/* Target-specific multicharacter options, not const-declared at usage. */
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* This table describes all the machine specific pseudo-ops
+ the assembler has to support. The fields are:
+ *** Pseudo-op name without dot.
+ *** Function to call to execute this pseudo-op.
+ *** Integer arg to pass to the function. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* In CRX machine, align is in bytes (not a ptwo boundary). */
+ {"align", s_align_bytes, 0},
+ {0, 0, 0}
+};
+
+/* CRX relaxation table. */
+const relax_typeS md_relax_table[] =
+{
+ /* bCC */
+ {0xfa, -0x100, 2, 1}, /* 8 */
+ {0xfffe, -0x10000, 4, 2}, /* 16 */
+ {0xfffffffe, -0xfffffffe, 6, 0}, /* 32 */
+
+ /* bal */
+ {0xfffe, -0x10000, 4, 4}, /* 16 */
+ {0xfffffffe, -0xfffffffe, 6, 0}, /* 32 */
+
+ /* cmpbr/bcop */
+ {0xfe, -0x100, 4, 6}, /* 8 */
+ {0xfffffe, -0x1000000, 6, 0} /* 24 */
+};
+
+static void reset_vars (char *);
+static reg get_register (char *);
+static copreg get_copregister (char *);
+static argtype get_optype (operand_type);
+static int get_opbits (operand_type);
+static int get_opflags (operand_type);
+static int get_number_of_operands (void);
+static void parse_operand (char *, ins *);
+static int gettrap (const char *);
+static void handle_LoadStor (const char *);
+static int get_cinv_parameters (const char *);
+static long getconstant (long, int);
+static op_err check_range (long *, int, unsigned int, int);
+static int getreg_image (reg);
+static void parse_operands (ins *, char *);
+static void parse_insn (ins *, char *);
+static void print_operand (int, int, argument *);
+static void print_constant (int, int, argument *);
+static int exponent2scale (int);
+static void mask_reg (int, unsigned short *);
+static void process_label_constant (char *, ins *);
+static void set_operand (char *, ins *);
+static char * preprocess_reglist (char *, int *);
+static int assemble_insn (char *, ins *);
+static void print_insn (ins *);
+static void warn_if_needed (ins *);
+static int adjust_if_needed (ins *);
+
+/* Return the bit size for a given operand. */
+
+static int
+get_opbits (operand_type op)
+{
+ if (op < MAX_OPRD)
+ return crx_optab[op].bit_size;
+ else
+ return 0;
+}
+
+/* Return the argument type of a given operand. */
+
+static argtype
+get_optype (operand_type op)
+{
+ if (op < MAX_OPRD)
+ return crx_optab[op].arg_type;
+ else
+ return nullargs;
+}
+
+/* Return the flags of a given operand. */
+
+static int
+get_opflags (operand_type op)
+{
+ if (op < MAX_OPRD)
+ return crx_optab[op].flags;
+ else
+ return 0;
+}
+
+/* Get the core processor register 'reg_name'. */
+
+static reg
+get_register (char *reg_name)
+{
+ const reg_entry *rreg;
+
+ rreg = (const reg_entry *) hash_find (reg_hash, reg_name);
+
+ if (rreg != NULL)
+ return rreg->value.reg_val;
+ else
+ return nullregister;
+}
+
+/* Get the coprocessor register 'copreg_name'. */
+
+static copreg
+get_copregister (char *copreg_name)
+{
+ const reg_entry *coreg;
+
+ coreg = (const reg_entry *) hash_find (copreg_hash, copreg_name);
+
+ if (coreg != NULL)
+ return coreg->value.copreg_val;
+ else
+ return nullcopregister;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT seg, valueT val)
+{
+ /* Round .text section to a multiple of 2. */
+ if (seg == text_section)
+ return (val + 1) & ~1;
+ return val;
+}
+
+/* Parse an operand that is machine-specific (remove '*'). */
+
+void
+md_operand (expressionS * exp)
+{
+ char c = *input_line_pointer;
+
+ switch (c)
+ {
+ case '*':
+ input_line_pointer++;
+ expression (exp);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Reset global variables before parsing a new instruction. */
+
+static void
+reset_vars (char *op)
+{
+ cur_arg_num = relocatable = 0;
+ memset (& output_opcode, '\0', sizeof (output_opcode));
+
+ /* Save a copy of the original OP (used in error messages). */
+ strncpy (ins_parse, op, sizeof ins_parse - 1);
+ ins_parse [sizeof ins_parse - 1] = 0;
+}
+
+/* This macro decides whether a particular reloc is an entry in a
+ switch table. It is used when relaxing, because the linker needs
+ to know about all such entries so that it can adjust them if
+ necessary. */
+
+#define SWITCH_TABLE(fix) \
+ ( (fix)->fx_addsy != NULL \
+ && (fix)->fx_subsy != NULL \
+ && S_GET_SEGMENT ((fix)->fx_addsy) == \
+ S_GET_SEGMENT ((fix)->fx_subsy) \
+ && S_GET_SEGMENT (fix->fx_addsy) != undefined_section \
+ && ( (fix)->fx_r_type == BFD_RELOC_CRX_NUM8 \
+ || (fix)->fx_r_type == BFD_RELOC_CRX_NUM16 \
+ || (fix)->fx_r_type == BFD_RELOC_CRX_NUM32))
+
+/* 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
+crx_force_relocation (fixS *fix)
+{
+ if (generic_force_reloc (fix) || SWITCH_TABLE (fix))
+ return 1;
+
+ return 0;
+}
+
+/* Generate a relocation entry for a fixup. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP)
+{
+ arelent * reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ reloc->addend = fixP->fx_offset;
+
+ if (fixP->fx_subsy != NULL)
+ {
+ if (SWITCH_TABLE (fixP))
+ {
+ /* Keep the current difference in the addend. */
+ reloc->addend = (S_GET_VALUE (fixP->fx_addsy)
+ - S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_CRX_NUM8:
+ fixP->fx_r_type = BFD_RELOC_CRX_SWITCH8;
+ break;
+ case BFD_RELOC_CRX_NUM16:
+ fixP->fx_r_type = BFD_RELOC_CRX_SWITCH16;
+ break;
+ case BFD_RELOC_CRX_NUM32:
+ fixP->fx_r_type = BFD_RELOC_CRX_SWITCH32;
+ break;
+ default:
+ abort ();
+ break;
+ }
+ }
+ else
+ {
+ /* We only resolve difference expressions in the same section. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("can't resolve `%s' {%s section} - `%s' {%s section}"),
+ fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0",
+ segment_name (fixP->fx_addsy
+ ? S_GET_SEGMENT (fixP->fx_addsy)
+ : absolute_section),
+ S_GET_NAME (fixP->fx_subsy),
+ segment_name (S_GET_SEGMENT (fixP->fx_addsy)));
+ }
+ }
+
+ gas_assert ((int) fixP->fx_r_type > 0);
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: reloc %d (`%s') not supported by object file format"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ return reloc;
+}
+
+/* Prepare machine-dependent frags for relaxation. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *seg)
+{
+ /* If symbol is undefined or located in a different section,
+ select the largest supported relocation. */
+ relax_substateT subtype;
+ relax_substateT rlx_state[] = {0, 2,
+ 3, 4,
+ 5, 6};
+
+ for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
+ {
+ if (fragp->fr_subtype == rlx_state[subtype]
+ && (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol)))
+ {
+ fragp->fr_subtype = rlx_state[subtype + 1];
+ break;
+ }
+ }
+
+ if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, fragS *fragP)
+{
+ /* 'opcode' points to the start of the instruction, whether
+ we need to change the instruction's fixed encoding. */
+ char *opcode = fragP->fr_literal + fragP->fr_fix;
+ bfd_reloc_code_real_type reloc;
+
+ subseg_change (sec, 0);
+
+ switch (fragP->fr_subtype)
+ {
+ case 0:
+ reloc = BFD_RELOC_CRX_REL8;
+ break;
+ case 1:
+ *opcode = 0x7e;
+ reloc = BFD_RELOC_CRX_REL16;
+ break;
+ case 2:
+ *opcode = 0x7f;
+ reloc = BFD_RELOC_CRX_REL32;
+ break;
+ case 3:
+ reloc = BFD_RELOC_CRX_REL16;
+ break;
+ case 4:
+ *++opcode = 0x31;
+ reloc = BFD_RELOC_CRX_REL32;
+ break;
+ case 5:
+ reloc = BFD_RELOC_CRX_REL8_CMP;
+ break;
+ case 6:
+ *++opcode = 0x31;
+ reloc = BFD_RELOC_CRX_REL24;
+ break;
+ default:
+ abort ();
+ break;
+ }
+
+ fix_new (fragP, fragP->fr_fix,
+ bfd_get_reloc_size (bfd_reloc_type_lookup (stdoutput, reloc)),
+ fragP->fr_symbol, fragP->fr_offset, 1, reloc);
+ fragP->fr_var = 0;
+ fragP->fr_fix += md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* Process machine-dependent command line options. Called once for
+ each option on the command line that the machine-independent part of
+ GAS does not understand. */
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Machine-dependent usage-output. */
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+ return;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag.
+ Since linkrelax is nonzero and TC_LINKRELAX_FIXUP is defined to disable
+ relaxation of debug sections, this function is called only when
+ fixuping relocations of debug sections. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ valueT val = * valP;
+ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+ fixP->fx_offset = 0;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_CRX_NUM8:
+ bfd_put_8 (stdoutput, (unsigned char) val, buf);
+ break;
+ case BFD_RELOC_CRX_NUM16:
+ bfd_put_16 (stdoutput, val, buf);
+ break;
+ case BFD_RELOC_CRX_NUM32:
+ bfd_put_32 (stdoutput, val, buf);
+ break;
+ default:
+ /* We shouldn't ever get here because linkrelax is nonzero. */
+ abort ();
+ break;
+ }
+
+ fixP->fx_done = 0;
+
+ if (fixP->fx_addsy == NULL
+ && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+
+ if (fixP->fx_pcrel == 1
+ && fixP->fx_addsy != NULL
+ && S_GET_SEGMENT (fixP->fx_addsy) == seg)
+ fixP->fx_done = 1;
+}
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixS *fixp)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs. */
+
+void
+md_begin (void)
+{
+ const char *hashret = NULL;
+ int i = 0;
+
+ /* Set up a hash table for the instructions. */
+ if ((crx_inst_hash = hash_new ()) == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ while (crx_instruction[i].mnemonic != NULL)
+ {
+ const char *mnemonic = crx_instruction[i].mnemonic;
+
+ hashret = hash_insert (crx_inst_hash, mnemonic,
+ (void *) &crx_instruction[i]);
+
+ if (hashret != NULL && *hashret != '\0')
+ as_fatal (_("Can't hash `%s': %s\n"), crx_instruction[i].mnemonic,
+ *hashret == 0 ? _("(unknown reason)") : hashret);
+
+ /* Insert unique names into hash table. The CRX instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+ do
+ {
+ ++i;
+ }
+ while (crx_instruction[i].mnemonic != NULL
+ && streq (crx_instruction[i].mnemonic, mnemonic));
+ }
+
+ /* Initialize reg_hash hash table. */
+ if ((reg_hash = hash_new ()) == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ {
+ const reg_entry *regtab;
+
+ for (regtab = crx_regtab;
+ regtab < (crx_regtab + NUMREGS); regtab++)
+ {
+ hashret = hash_insert (reg_hash, regtab->name, (void *) regtab);
+ if (hashret)
+ as_fatal (_("Internal Error: Can't hash %s: %s"),
+ regtab->name,
+ hashret);
+ }
+ }
+
+ /* Initialize copreg_hash hash table. */
+ if ((copreg_hash = hash_new ()) == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ {
+ const reg_entry *copregtab;
+
+ for (copregtab = crx_copregtab; copregtab < (crx_copregtab + NUMCOPREGS);
+ copregtab++)
+ {
+ hashret = hash_insert (copreg_hash, copregtab->name,
+ (void *) copregtab);
+ if (hashret)
+ as_fatal (_("Internal Error: Can't hash %s: %s"),
+ copregtab->name,
+ hashret);
+ }
+ }
+ /* Set linkrelax here to avoid fixups in most sections. */
+ linkrelax = 1;
+}
+
+/* Process constants (immediate/absolute)
+ and labels (jump targets/Memory locations). */
+
+static void
+process_label_constant (char *str, ins * crx_ins)
+{
+ char *saved_input_line_pointer;
+ argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument. */
+
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+
+ expression (&crx_ins->exp);
+
+ switch (crx_ins->exp.X_op)
+ {
+ case O_big:
+ case O_absent:
+ /* Missing or bad expr becomes absolute 0. */
+ as_bad (_("missing or invalid displacement expression `%s' taken as 0"),
+ str);
+ crx_ins->exp.X_op = O_constant;
+ crx_ins->exp.X_add_number = 0;
+ crx_ins->exp.X_add_symbol = (symbolS *) 0;
+ crx_ins->exp.X_op_symbol = (symbolS *) 0;
+ /* Fall through. */
+
+ case O_constant:
+ cur_arg->X_op = O_constant;
+ cur_arg->constant = crx_ins->exp.X_add_number;
+ break;
+
+ case O_symbol:
+ case O_subtract:
+ case O_add:
+ cur_arg->X_op = O_symbol;
+ crx_ins->rtype = BFD_RELOC_NONE;
+ relocatable = 1;
+
+ switch (cur_arg->type)
+ {
+ case arg_cr:
+ if (IS_INSN_TYPE (LD_STOR_INS_INC))
+ crx_ins->rtype = BFD_RELOC_CRX_REGREL12;
+ else if (IS_INSN_TYPE (CSTBIT_INS)
+ || IS_INSN_TYPE (STOR_IMM_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_REGREL28;
+ else
+ crx_ins->rtype = BFD_RELOC_CRX_REGREL32;
+ break;
+
+ case arg_idxr:
+ crx_ins->rtype = BFD_RELOC_CRX_REGREL22;
+ break;
+
+ case arg_c:
+ if (IS_INSN_MNEMONIC ("bal") || IS_INSN_TYPE (DCR_BRANCH_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_REL16;
+ else if (IS_INSN_TYPE (BRANCH_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_REL8;
+ else if (IS_INSN_TYPE (LD_STOR_INS) || IS_INSN_TYPE (STOR_IMM_INS)
+ || IS_INSN_TYPE (CSTBIT_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_ABS32;
+ else if (IS_INSN_TYPE (BRANCH_NEQ_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_REL4;
+ else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_REL8_CMP;
+ break;
+
+ case arg_ic:
+ if (IS_INSN_TYPE (ARITH_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_IMM32;
+ else if (IS_INSN_TYPE (ARITH_BYTE_INS))
+ crx_ins->rtype = BFD_RELOC_CRX_IMM16;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ cur_arg->X_op = crx_ins->exp.X_op;
+ break;
+ }
+
+ input_line_pointer = saved_input_line_pointer;
+ return;
+}
+
+/* Get the values of the scale to be encoded -
+ used for the scaled index mode of addressing. */
+
+static int
+exponent2scale (int val)
+{
+ int exponent;
+
+ /* If 'val' is 0, the following 'for' will be an endless loop. */
+ if (val == 0)
+ return 0;
+
+ for (exponent = 0; (val != 1); val >>= 1, exponent++)
+ ;
+
+ return exponent;
+}
+
+/* Parsing different types of operands
+ -> constants Immediate/Absolute/Relative numbers
+ -> Labels Relocatable symbols
+ -> (rbase) Register base
+ -> disp(rbase) Register relative
+ -> disp(rbase)+ Post-increment mode
+ -> disp(rbase,ridx,scl) Register index mode */
+
+static void
+set_operand (char *operand, ins * crx_ins)
+{
+ char *operandS; /* Pointer to start of sub-opearand. */
+ char *operandE; /* Pointer to end of sub-opearand. */
+ expressionS scale;
+ int scale_val;
+ char *input_save, c;
+ argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument. */
+
+ /* Initialize pointers. */
+ operandS = operandE = operand;
+
+ switch (cur_arg->type)
+ {
+ case arg_sc: /* Case *+0x18. */
+ case arg_ic: /* Case $0x18. */
+ operandS++;
+ case arg_c: /* Case 0x18. */
+ /* Set constant. */
+ process_label_constant (operandS, crx_ins);
+
+ if (cur_arg->type != arg_ic)
+ cur_arg->type = arg_c;
+ break;
+
+ case arg_icr: /* Case $0x18(r1). */
+ operandS++;
+ case arg_cr: /* Case 0x18(r1). */
+ /* Set displacement constant. */
+ while (*operandE != '(')
+ operandE++;
+ *operandE = '\0';
+ process_label_constant (operandS, crx_ins);
+ operandS = operandE;
+ case arg_rbase: /* Case (r1). */
+ operandS++;
+ /* Set register base. */
+ while (*operandE != ')')
+ operandE++;
+ *operandE = '\0';
+ if ((cur_arg->r = get_register (operandS)) == nullregister)
+ as_bad (_("Illegal register `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+
+ if (cur_arg->type != arg_rbase)
+ cur_arg->type = arg_cr;
+ break;
+
+ case arg_idxr:
+ /* Set displacement constant. */
+ while (*operandE != '(')
+ operandE++;
+ *operandE = '\0';
+ process_label_constant (operandS, crx_ins);
+ operandS = ++operandE;
+
+ /* Set register base. */
+ while ((*operandE != ',') && (! ISSPACE (*operandE)))
+ operandE++;
+ *operandE++ = '\0';
+ if ((cur_arg->r = get_register (operandS)) == nullregister)
+ as_bad (_("Illegal register `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+
+ /* Skip leading white space. */
+ while (ISSPACE (*operandE))
+ operandE++;
+ operandS = operandE;
+
+ /* Set register index. */
+ while ((*operandE != ')') && (*operandE != ','))
+ operandE++;
+ c = *operandE;
+ *operandE++ = '\0';
+
+ if ((cur_arg->i_r = get_register (operandS)) == nullregister)
+ as_bad (_("Illegal register `%s' in Instruction `%s'"),
+ operandS, ins_parse);
+
+ /* Skip leading white space. */
+ while (ISSPACE (*operandE))
+ operandE++;
+ operandS = operandE;
+
+ /* Set the scale. */
+ if (c == ')')
+ cur_arg->scale = 0;
+ else
+ {
+ while (*operandE != ')')
+ operandE++;
+ *operandE = '\0';
+
+ /* Preprocess the scale string. */
+ input_save = input_line_pointer;
+ input_line_pointer = operandS;
+ expression (&scale);
+ input_line_pointer = input_save;
+
+ scale_val = scale.X_add_number;
+
+ /* Check if the scale value is legal. */
+ if (scale_val != 1 && scale_val != 2
+ && scale_val != 4 && scale_val != 8)
+ as_bad (_("Illegal Scale - `%d'"), scale_val);
+
+ cur_arg->scale = exponent2scale (scale_val);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Parse a single operand.
+ operand - Current operand to parse.
+ crx_ins - Current assembled instruction. */
+
+static void
+parse_operand (char *operand, ins * crx_ins)
+{
+ int ret_val;
+ argument *cur_arg = &crx_ins->arg[cur_arg_num]; /* Current argument. */
+
+ /* Initialize the type to NULL before parsing. */
+ cur_arg->type = nullargs;
+
+ /* Check whether this is a general processor register. */
+ if ((ret_val = get_register (operand)) != nullregister)
+ {
+ cur_arg->type = arg_r;
+ cur_arg->r = ret_val;
+ cur_arg->X_op = O_register;
+ return;
+ }
+
+ /* Check whether this is a core [special] coprocessor register. */
+ if ((ret_val = get_copregister (operand)) != nullcopregister)
+ {
+ cur_arg->type = arg_copr;
+ if (ret_val >= cs0)
+ cur_arg->type = arg_copsr;
+ cur_arg->cr = ret_val;
+ cur_arg->X_op = O_register;
+ return;
+ }
+
+ /* Deal with special characters. */
+ switch (operand[0])
+ {
+ case '$':
+ if (strchr (operand, '(') != NULL)
+ cur_arg->type = arg_icr;
+ else
+ cur_arg->type = arg_ic;
+ goto set_params;
+ break;
+
+ case '*':
+ cur_arg->type = arg_sc;
+ goto set_params;
+ break;
+
+ case '(':
+ cur_arg->type = arg_rbase;
+ goto set_params;
+ break;
+
+ default:
+ break;
+ }
+
+ if (strchr (operand, '(') != NULL)
+ {
+ if (strchr (operand, ',') != NULL
+ && (strchr (operand, ',') > strchr (operand, '(')))
+ cur_arg->type = arg_idxr;
+ else
+ cur_arg->type = arg_cr;
+ }
+ else
+ cur_arg->type = arg_c;
+ goto set_params;
+
+/* Parse an operand according to its type. */
+set_params:
+ cur_arg->constant = 0;
+ set_operand (operand, crx_ins);
+}
+
+/* Parse the various operands. Each operand is then analyzed to fillup
+ the fields in the crx_ins data structure. */
+
+static void
+parse_operands (ins * crx_ins, char *operands)
+{
+ char *operandS; /* Operands string. */
+ char *operandH, *operandT; /* Single operand head/tail pointers. */
+ int allocated = 0; /* Indicates a new operands string was allocated. */
+ char *operand[MAX_OPERANDS]; /* Separating the operands. */
+ int op_num = 0; /* Current operand number we are parsing. */
+ int bracket_flag = 0; /* Indicates a bracket '(' was found. */
+ int sq_bracket_flag = 0; /* Indicates a square bracket '[' was found. */
+
+ /* Preprocess the list of registers, if necessary. */
+ operandS = operandH = operandT = (INST_HAS_REG_LIST) ?
+ preprocess_reglist (operands, &allocated) : operands;
+
+ while (*operandT != '\0')
+ {
+ if (*operandT == ',' && bracket_flag != 1 && sq_bracket_flag != 1)
+ {
+ *operandT++ = '\0';
+ operand[op_num++] = strdup (operandH);
+ operandH = operandT;
+ continue;
+ }
+
+ if (*operandT == ' ')
+ as_bad (_("Illegal operands (whitespace): `%s'"), ins_parse);
+
+ if (*operandT == '(')
+ bracket_flag = 1;
+ else if (*operandT == '[')
+ sq_bracket_flag = 1;
+
+ if (*operandT == ')')
+ {
+ if (bracket_flag)
+ bracket_flag = 0;
+ else
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+ }
+ else if (*operandT == ']')
+ {
+ if (sq_bracket_flag)
+ sq_bracket_flag = 0;
+ else
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+ }
+
+ if (bracket_flag == 1 && *operandT == ')')
+ bracket_flag = 0;
+ else if (sq_bracket_flag == 1 && *operandT == ']')
+ sq_bracket_flag = 0;
+
+ operandT++;
+ }
+
+ /* Adding the last operand. */
+ operand[op_num++] = strdup (operandH);
+ crx_ins->nargs = op_num;
+
+ /* Verifying correct syntax of operands (all brackets should be closed). */
+ if (bracket_flag || sq_bracket_flag)
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+
+ /* Now we parse each operand separately. */
+ for (op_num = 0; op_num < crx_ins->nargs; op_num++)
+ {
+ cur_arg_num = op_num;
+ parse_operand (operand[op_num], crx_ins);
+ free (operand[op_num]);
+ }
+
+ if (allocated)
+ free (operandS);
+}
+
+/* Get the trap index in dispatch table, given its name.
+ This routine is used by assembling the 'excp' instruction. */
+
+static int
+gettrap (const char *s)
+{
+ const trap_entry *trap;
+
+ for (trap = crx_traps; trap < (crx_traps + NUMTRAPS); trap++)
+ if (strcasecmp (trap->name, s) == 0)
+ return trap->entry;
+
+ as_bad (_("Unknown exception: `%s'"), s);
+ return 0;
+}
+
+/* Post-Increment instructions, as well as Store-Immediate instructions, are a
+ sub-group within load/stor instruction groups.
+ Therefore, when parsing a Post-Increment/Store-Immediate insn, we have to
+ advance the instruction pointer to the start of that sub-group (that is, up
+ to the first instruction of that type).
+ Otherwise, the insn will be mistakenly identified as of type LD_STOR_INS. */
+
+static void
+handle_LoadStor (const char *operands)
+{
+ /* Post-Increment instructions precede Store-Immediate instructions in
+ CRX instruction table, hence they are handled before.
+ This synchronization should be kept. */
+
+ /* Assuming Post-Increment insn has the following format :
+ 'MNEMONIC DISP(REG)+, REG' (e.g. 'loadw 12(r5)+, r6').
+ LD_STOR_INS_INC are the only store insns containing a plus sign (+). */
+ if (strstr (operands, ")+") != NULL)
+ {
+ while (! IS_INSN_TYPE (LD_STOR_INS_INC))
+ instruction++;
+ return;
+ }
+
+ /* Assuming Store-Immediate insn has the following format :
+ 'MNEMONIC $DISP, ...' (e.g. 'storb $1, 12(r5)').
+ STOR_IMM_INS are the only store insns containing a dollar sign ($). */
+ if (strstr (operands, "$") != NULL)
+ while (! IS_INSN_TYPE (STOR_IMM_INS))
+ instruction++;
+}
+
+/* Top level module where instruction parsing starts.
+ crx_ins - data structure holds some information.
+ operands - holds the operands part of the whole instruction. */
+
+static void
+parse_insn (ins *insn, char *operands)
+{
+ int i;
+
+ /* Handle instructions with no operands. */
+ for (i = 0; no_op_insn[i] != NULL; i++)
+ {
+ if (streq (no_op_insn[i], instruction->mnemonic))
+ {
+ insn->nargs = 0;
+ return;
+ }
+ }
+
+ /* Handle 'excp'/'cinv' instructions. */
+ if (IS_INSN_MNEMONIC ("excp") || IS_INSN_MNEMONIC ("cinv"))
+ {
+ insn->nargs = 1;
+ insn->arg[0].type = arg_ic;
+ insn->arg[0].constant = IS_INSN_MNEMONIC ("excp") ?
+ gettrap (operands) : get_cinv_parameters (operands);
+ insn->arg[0].X_op = O_constant;
+ return;
+ }
+
+ /* Handle load/stor unique instructions before parsing. */
+ if (IS_INSN_TYPE (LD_STOR_INS))
+ handle_LoadStor (operands);
+
+ if (operands != NULL)
+ parse_operands (insn, operands);
+}
+
+/* Cinv instruction requires special handling. */
+
+static int
+get_cinv_parameters (const char *operand)
+{
+ const char *p = operand;
+ int d_used = 0, i_used = 0, u_used = 0, b_used = 0;
+
+ while (*++p != ']')
+ {
+ if (*p == ',' || *p == ' ')
+ continue;
+
+ if (*p == 'd')
+ d_used = 1;
+ else if (*p == 'i')
+ i_used = 1;
+ else if (*p == 'u')
+ u_used = 1;
+ else if (*p == 'b')
+ b_used = 1;
+ else
+ as_bad (_("Illegal `cinv' parameter: `%c'"), *p);
+ }
+
+ return ((b_used ? 8 : 0)
+ + (d_used ? 4 : 0)
+ + (i_used ? 2 : 0)
+ + (u_used ? 1 : 0));
+}
+
+/* Retrieve the opcode image of a given register.
+ If the register is illegal for the current instruction,
+ issue an error. */
+
+static int
+getreg_image (reg r)
+{
+ const reg_entry *rreg;
+ char *reg_name;
+ int is_procreg = 0; /* Nonzero means argument should be processor reg. */
+
+ if (((IS_INSN_MNEMONIC ("mtpr")) && (cur_arg_num == 1))
+ || ((IS_INSN_MNEMONIC ("mfpr")) && (cur_arg_num == 0)) )
+ is_procreg = 1;
+
+ /* Check whether the register is in registers table. */
+ if (r < MAX_REG)
+ rreg = &crx_regtab[r];
+ /* Check whether the register is in coprocessor registers table. */
+ else if (r < (int) MAX_COPREG)
+ rreg = &crx_copregtab[r-MAX_REG];
+ /* Register not found. */
+ else
+ {
+ as_bad (_("Unknown register: `%d'"), r);
+ return 0;
+ }
+
+ reg_name = rreg->name;
+
+/* Issue a error message when register is illegal. */
+#define IMAGE_ERR \
+ as_bad (_("Illegal register (`%s') in Instruction: `%s'"), \
+ reg_name, ins_parse); \
+ break;
+
+ switch (rreg->type)
+ {
+ case CRX_U_REGTYPE:
+ if (is_procreg || (instruction->flags & USER_REG))
+ return rreg->image;
+ else
+ IMAGE_ERR;
+
+ case CRX_CFG_REGTYPE:
+ if (is_procreg)
+ return rreg->image;
+ else
+ IMAGE_ERR;
+
+ case CRX_R_REGTYPE:
+ if (! is_procreg)
+ return rreg->image;
+ else
+ IMAGE_ERR;
+
+ case CRX_C_REGTYPE:
+ case CRX_CS_REGTYPE:
+ return rreg->image;
+ break;
+
+ default:
+ IMAGE_ERR;
+ }
+
+ return 0;
+}
+
+/* Routine used to represent integer X using NBITS bits. */
+
+static long
+getconstant (long x, int nbits)
+{
+ return x & ((((1U << (nbits - 1)) - 1) << 1) | 1);
+}
+
+/* Print a constant value to 'output_opcode':
+ ARG holds the operand's type and value.
+ SHIFT represents the location of the operand to be print into.
+ NBITS determines the size (in bits) of the constant. */
+
+static void
+print_constant (int nbits, int shift, argument *arg)
+{
+ unsigned long mask = 0;
+
+ long constant = getconstant (arg->constant, nbits);
+
+ switch (nbits)
+ {
+ case 32:
+ case 28:
+ case 24:
+ case 22:
+ /* mask the upper part of the constant, that is, the bits
+ going to the lowest byte of output_opcode[0].
+ The upper part of output_opcode[1] is always filled,
+ therefore it is always masked with 0xFFFF. */
+ mask = (1 << (nbits - 16)) - 1;
+ /* Divide the constant between two consecutive words :
+ 0 1 2 3
+ +---------+---------+---------+---------+
+ | | X X X X | X X X X | |
+ +---------+---------+---------+---------+
+ output_opcode[0] output_opcode[1] */
+
+ CRX_PRINT (0, (constant >> WORD_SHIFT) & mask, 0);
+ CRX_PRINT (1, (constant & 0xFFFF), WORD_SHIFT);
+ break;
+
+ case 16:
+ case 12:
+ /* Special case - in arg_cr, the SHIFT represents the location
+ of the REGISTER, not the constant, which is itself not shifted. */
+ if (arg->type == arg_cr)
+ {
+ CRX_PRINT (0, constant, 0);
+ break;
+ }
+
+ /* When instruction size is 3 and 'shift' is 16, a 16-bit constant is
+ always filling the upper part of output_opcode[1]. If we mistakenly
+ write it to output_opcode[0], the constant prefix (that is, 'match')
+ will be overridden.
+ 0 1 2 3
+ +---------+---------+---------+---------+
+ | 'match' | | X X X X | |
+ +---------+---------+---------+---------+
+ output_opcode[0] output_opcode[1] */
+
+ if ((instruction->size > 2) && (shift == WORD_SHIFT))
+ CRX_PRINT (1, constant, WORD_SHIFT);
+ else
+ CRX_PRINT (0, constant, shift);
+ break;
+
+ default:
+ CRX_PRINT (0, constant, shift);
+ break;
+ }
+}
+
+/* Print an operand to 'output_opcode', which later on will be
+ printed to the object file:
+ ARG holds the operand's type, size and value.
+ SHIFT represents the printing location of operand.
+ NBITS determines the size (in bits) of a constant operand. */
+
+static void
+print_operand (int nbits, int shift, argument *arg)
+{
+ switch (arg->type)
+ {
+ case arg_r:
+ CRX_PRINT (0, getreg_image (arg->r), shift);
+ break;
+
+ case arg_copr:
+ if (arg->cr < c0 || arg->cr > c15)
+ as_bad (_("Illegal Co-processor register in Instruction `%s' "),
+ ins_parse);
+ CRX_PRINT (0, getreg_image (arg->cr), shift);
+ break;
+
+ case arg_copsr:
+ if (arg->cr < cs0 || arg->cr > cs15)
+ as_bad (_("Illegal Co-processor special register in Instruction `%s' "),
+ ins_parse);
+ CRX_PRINT (0, getreg_image (arg->cr), shift);
+ break;
+
+ case arg_idxr:
+ /* 16 12 8 6 0
+ +--------------------------------+
+ | r_base | r_idx | scl| disp |
+ +--------------------------------+ */
+ CRX_PRINT (0, getreg_image (arg->r), 12);
+ CRX_PRINT (0, getreg_image (arg->i_r), 8);
+ CRX_PRINT (0, arg->scale, 6);
+ case arg_ic:
+ case arg_c:
+ print_constant (nbits, shift, arg);
+ break;
+
+ case arg_rbase:
+ CRX_PRINT (0, getreg_image (arg->r), shift);
+ break;
+
+ case arg_cr:
+ /* case base_cst4. */
+ if (instruction->flags & DISPU4MAP)
+ print_constant (nbits, shift + REG_SIZE, arg);
+ else
+ /* rbase_disps<NN> and other such cases. */
+ print_constant (nbits, shift, arg);
+ /* Add the register argument to the output_opcode. */
+ CRX_PRINT (0, getreg_image (arg->r), shift);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Retrieve the number of operands for the current assembled instruction. */
+
+static int
+get_number_of_operands (void)
+{
+ int i;
+
+ for (i = 0; instruction->operands[i].op_type && i < MAX_OPERANDS; i++)
+ ;
+ return i;
+}
+
+/* Verify that the number NUM can be represented in BITS bits (that is,
+ within its permitted range), based on the instruction's FLAGS.
+ If UPDATE is nonzero, update the value of NUM if necessary.
+ Return OP_LEGAL upon success, actual error type upon failure. */
+
+static op_err
+check_range (long *num, int bits, int unsigned flags, int update)
+{
+ uint32_t max;
+ int retval = OP_LEGAL;
+ int bin;
+ uint32_t upper_64kb = 0xffff0000;
+ uint32_t value = *num;
+
+ /* Verify operand value is even. */
+ if (flags & OP_EVEN)
+ {
+ if (value % 2)
+ return OP_NOT_EVEN;
+ }
+
+ if (flags & OP_UPPER_64KB)
+ {
+ /* Check if value is to be mapped to upper 64 KB memory area. */
+ if ((value & upper_64kb) == upper_64kb)
+ {
+ value -= upper_64kb;
+ if (update)
+ *num = value;
+ }
+ else
+ return OP_NOT_UPPER_64KB;
+ }
+
+ if (flags & OP_SHIFT)
+ {
+ /* All OP_SHIFT args are also OP_SIGNED, so we want to keep the
+ sign. However, right shift of a signed type with a negative
+ value is implementation defined. See ISO C 6.5.7. So we use
+ an unsigned type and sign extend afterwards. */
+ value >>= 1;
+ value = (value ^ 0x40000000) - 0x40000000;
+ if (update)
+ *num = value;
+ }
+ else if (flags & OP_SHIFT_DEC)
+ {
+ value = (value >> 1) - 1;
+ if (update)
+ *num = value;
+ }
+
+ if (flags & OP_ESC)
+ {
+ /* 0x7e and 0x7f are reserved escape sequences of dispe9. */
+ if (value == 0x7e || value == 0x7f)
+ return OP_OUT_OF_RANGE;
+ }
+
+ if (flags & OP_DISPU4)
+ {
+ int is_dispu4 = 0;
+
+ uint32_t mul = (instruction->flags & DISPUB4 ? 1
+ : instruction->flags & DISPUW4 ? 2
+ : instruction->flags & DISPUD4 ? 4
+ : 0);
+
+ for (bin = 0; bin < cst4_maps; bin++)
+ {
+ if (value == mul * bin)
+ {
+ is_dispu4 = 1;
+ if (update)
+ *num = bin;
+ break;
+ }
+ }
+ if (!is_dispu4)
+ retval = OP_ILLEGAL_DISPU4;
+ }
+ else if (flags & OP_CST4)
+ {
+ int is_cst4 = 0;
+
+ for (bin = 0; bin < cst4_maps; bin++)
+ {
+ if (value == (uint32_t) cst4_map[bin])
+ {
+ is_cst4 = 1;
+ if (update)
+ *num = bin;
+ break;
+ }
+ }
+ if (!is_cst4)
+ retval = OP_ILLEGAL_CST4;
+ }
+ else if (flags & OP_SIGNED)
+ {
+ max = 1;
+ max = max << (bits - 1);
+ value += max;
+ max = ((max - 1) << 1) | 1;
+ if (value > max)
+ retval = OP_OUT_OF_RANGE;
+ }
+ else if (flags & OP_UNSIGNED)
+ {
+ max = 1;
+ max = max << (bits - 1);
+ max = ((max - 1) << 1) | 1;
+ if (value > max)
+ retval = OP_OUT_OF_RANGE;
+ }
+ return retval;
+}
+
+/* Assemble a single instruction:
+ INSN is already parsed (that is, all operand values and types are set).
+ For instruction to be assembled, we need to find an appropriate template in
+ the instruction table, meeting the following conditions:
+ 1: Has the same number of operands.
+ 2: Has the same operand types.
+ 3: Each operand size is sufficient to represent the instruction's values.
+ Returns 1 upon success, 0 upon failure. */
+
+static int
+assemble_insn (char *mnemonic, ins *insn)
+{
+ /* Type of each operand in the current template. */
+ argtype cur_type[MAX_OPERANDS];
+ /* Size (in bits) of each operand in the current template. */
+ unsigned int cur_size[MAX_OPERANDS];
+ /* Flags of each operand in the current template. */
+ unsigned int cur_flags[MAX_OPERANDS];
+ /* Instruction type to match. */
+ unsigned int ins_type;
+ /* Boolean flag to mark whether a match was found. */
+ int match = 0;
+ int i;
+ /* Nonzero if an instruction with same number of operands was found. */
+ int found_same_number_of_operands = 0;
+ /* Nonzero if an instruction with same argument types was found. */
+ int found_same_argument_types = 0;
+ /* Nonzero if a constant was found within the required range. */
+ int found_const_within_range = 0;
+ /* Argument number of an operand with invalid type. */
+ int invalid_optype = -1;
+ /* Argument number of an operand with invalid constant value. */
+ int invalid_const = -1;
+ /* Operand error (used for issuing various constant error messages). */
+ op_err op_error, const_err = OP_LEGAL;
+
+/* Retrieve data (based on FUNC) for each operand of a given instruction. */
+#define GET_CURRENT_DATA(FUNC, ARRAY) \
+ for (i = 0; i < insn->nargs; i++) \
+ ARRAY[i] = FUNC (instruction->operands[i].op_type)
+
+#define GET_CURRENT_TYPE GET_CURRENT_DATA(get_optype, cur_type)
+#define GET_CURRENT_SIZE GET_CURRENT_DATA(get_opbits, cur_size)
+#define GET_CURRENT_FLAGS GET_CURRENT_DATA(get_opflags, cur_flags)
+
+ /* Instruction has no operands -> only copy the constant opcode. */
+ if (insn->nargs == 0)
+ {
+ output_opcode[0] = BIN (instruction->match, instruction->match_bits);
+ return 1;
+ }
+
+ /* In some case, same mnemonic can appear with different instruction types.
+ For example, 'storb' is supported with 3 different types :
+ LD_STOR_INS, LD_STOR_INS_INC, STOR_IMM_INS.
+ We assume that when reaching this point, the instruction type was
+ pre-determined. We need to make sure that the type stays the same
+ during a search for matching instruction. */
+ ins_type = CRX_INS_TYPE(instruction->flags);
+
+ while (/* Check that match is still not found. */
+ match != 1
+ /* Check we didn't get to end of table. */
+ && instruction->mnemonic != NULL
+ /* Check that the actual mnemonic is still available. */
+ && IS_INSN_MNEMONIC (mnemonic)
+ /* Check that the instruction type wasn't changed. */
+ && IS_INSN_TYPE(ins_type))
+ {
+ /* Check whether number of arguments is legal. */
+ if (get_number_of_operands () != insn->nargs)
+ goto next_insn;
+ found_same_number_of_operands = 1;
+
+ /* Initialize arrays with data of each operand in current template. */
+ GET_CURRENT_TYPE;
+ GET_CURRENT_SIZE;
+ GET_CURRENT_FLAGS;
+
+ /* Check for type compatibility. */
+ for (i = 0; i < insn->nargs; i++)
+ {
+ if (cur_type[i] != insn->arg[i].type)
+ {
+ if (invalid_optype == -1)
+ invalid_optype = i + 1;
+ goto next_insn;
+ }
+ }
+ found_same_argument_types = 1;
+
+ for (i = 0; i < insn->nargs; i++)
+ {
+ /* Reverse the operand indices for certain opcodes:
+ Index 0 -->> 1
+ Index 1 -->> 0
+ Other index -->> stays the same. */
+ int j = instruction->flags & REVERSE_MATCH ?
+ i == 0 ? 1 :
+ i == 1 ? 0 : i :
+ i;
+
+ /* Only check range - don't update the constant's value, since the
+ current instruction may not be the last we try to match.
+ The constant's value will be updated later, right before printing
+ it to the object file. */
+ if ((insn->arg[j].X_op == O_constant)
+ && (op_error = check_range (&insn->arg[j].constant, cur_size[j],
+ cur_flags[j], 0)))
+ {
+ if (invalid_const == -1)
+ {
+ invalid_const = j + 1;
+ const_err = op_error;
+ }
+ goto next_insn;
+ }
+ /* For symbols, we make sure the relocation size (which was already
+ determined) is sufficient. */
+ else if ((insn->arg[j].X_op == O_symbol)
+ && ((bfd_reloc_type_lookup (stdoutput, insn->rtype))->bitsize
+ > cur_size[j]))
+ goto next_insn;
+ }
+ found_const_within_range = 1;
+
+ /* If we got till here -> Full match is found. */
+ match = 1;
+ break;
+
+/* Try again with next instruction. */
+next_insn:
+ instruction++;
+ }
+
+ if (!match)
+ {
+ /* We haven't found a match - instruction can't be assembled. */
+ if (!found_same_number_of_operands)
+ as_bad (_("Incorrect number of operands"));
+ else if (!found_same_argument_types)
+ as_bad (_("Illegal type of operand (arg %d)"), invalid_optype);
+ else if (!found_const_within_range)
+ {
+ switch (const_err)
+ {
+ case OP_OUT_OF_RANGE:
+ as_bad (_("Operand out of range (arg %d)"), invalid_const);
+ break;
+ case OP_NOT_EVEN:
+ as_bad (_("Operand has odd displacement (arg %d)"), invalid_const);
+ break;
+ case OP_ILLEGAL_DISPU4:
+ as_bad (_("Invalid DISPU4 operand value (arg %d)"), invalid_const);
+ break;
+ case OP_ILLEGAL_CST4:
+ as_bad (_("Invalid CST4 operand value (arg %d)"), invalid_const);
+ break;
+ case OP_NOT_UPPER_64KB:
+ as_bad (_("Operand value is not within upper 64 KB (arg %d)"),
+ invalid_const);
+ break;
+ default:
+ as_bad (_("Illegal operand (arg %d)"), invalid_const);
+ break;
+ }
+ }
+
+ return 0;
+ }
+ else
+ /* Full match - print the encoding to output file. */
+ {
+ /* Make further checkings (such that couldn't be made earlier).
+ Warn the user if necessary. */
+ warn_if_needed (insn);
+
+ /* Check whether we need to adjust the instruction pointer. */
+ if (adjust_if_needed (insn))
+ /* If instruction pointer was adjusted, we need to update
+ the size of the current template operands. */
+ GET_CURRENT_SIZE;
+
+ for (i = 0; i < insn->nargs; i++)
+ {
+ int j = instruction->flags & REVERSE_MATCH ?
+ i == 0 ? 1 :
+ i == 1 ? 0 : i :
+ i;
+
+ /* This time, update constant value before printing it. */
+ if ((insn->arg[j].X_op == O_constant)
+ && (check_range (&insn->arg[j].constant, cur_size[j],
+ cur_flags[j], 1) != OP_LEGAL))
+ as_fatal (_("Illegal operand (arg %d)"), j+1);
+ }
+
+ /* First, copy the instruction's opcode. */
+ output_opcode[0] = BIN (instruction->match, instruction->match_bits);
+
+ for (i = 0; i < insn->nargs; i++)
+ {
+ cur_arg_num = i;
+ print_operand (cur_size[i], instruction->operands[i].shift,
+ &insn->arg[i]);
+ }
+ }
+
+ return 1;
+}
+
+/* Bunch of error checkings.
+ The checks are made after a matching instruction was found. */
+
+void
+warn_if_needed (ins *insn)
+{
+ /* If the post-increment address mode is used and the load/store
+ source register is the same as rbase, the result of the
+ instruction is undefined. */
+ if (IS_INSN_TYPE (LD_STOR_INS_INC))
+ {
+ /* Enough to verify that one of the arguments is a simple reg. */
+ if ((insn->arg[0].type == arg_r) || (insn->arg[1].type == arg_r))
+ if (insn->arg[0].r == insn->arg[1].r)
+ as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
+ insn->arg[0].r);
+ }
+
+ /* Some instruction assume the stack pointer as rptr operand.
+ Issue an error when the register to be loaded is also SP. */
+ if (instruction->flags & NO_SP)
+ {
+ if (getreg_image (insn->arg[0].r) == getreg_image (sp))
+ as_bad (_("`%s' has undefined result"), ins_parse);
+ }
+
+ /* If the rptr register is specified as one of the registers to be loaded,
+ the final contents of rptr are undefined. Thus, we issue an error. */
+ if (instruction->flags & NO_RPTR)
+ {
+ if ((1 << getreg_image (insn->arg[0].r)) & insn->arg[1].constant)
+ as_bad (_("Same src/dest register is used (`r%d'), result is undefined"),
+ getreg_image (insn->arg[0].r));
+ }
+}
+
+/* In some cases, we need to adjust the instruction pointer although a
+ match was already found. Here, we gather all these cases.
+ Returns 1 if instruction pointer was adjusted, otherwise 0. */
+
+int
+adjust_if_needed (ins *insn)
+{
+ int ret_value = 0;
+
+ /* Special check for 'addub $0, r0' instruction -
+ The opcode '0000 0000 0000 0000' is not allowed. */
+ if (IS_INSN_MNEMONIC ("addub"))
+ {
+ if ((instruction->operands[0].op_type == cst4)
+ && instruction->operands[1].op_type == regr)
+ {
+ if (insn->arg[0].constant == 0 && insn->arg[1].r == r0)
+ {
+ instruction++;
+ ret_value = 1;
+ }
+ }
+ }
+
+ /* Optimization: Omit a zero displacement in bit operations,
+ saving 2-byte encoding space (e.g., 'cbitw $8, 0(r1)'). */
+ if (IS_INSN_TYPE (CSTBIT_INS))
+ {
+ if ((instruction->operands[1].op_type == rbase_disps12)
+ && (insn->arg[1].X_op == O_constant)
+ && (insn->arg[1].constant == 0))
+ {
+ instruction--;
+ ret_value = 1;
+ }
+ }
+
+ return ret_value;
+}
+
+/* Set the appropriate bit for register 'r' in 'mask'.
+ This indicates that this register is loaded or stored by
+ the instruction. */
+
+static void
+mask_reg (int r, unsigned short int *mask)
+{
+ if ((reg)r > (reg)sp)
+ {
+ as_bad (_("Invalid Register in Register List"));
+ return;
+ }
+
+ *mask |= (1 << r);
+}
+
+/* Preprocess register list - create a 16-bit mask with one bit for each
+ of the 16 general purpose registers. If a bit is set, it indicates
+ that this register is loaded or stored by the instruction. */
+
+static char *
+preprocess_reglist (char *param, int *allocated)
+{
+ char reg_name[MAX_REGNAME_LEN]; /* Current parsed register name. */
+ char *regP; /* Pointer to 'reg_name' string. */
+ int reg_counter = 0; /* Count number of parsed registers. */
+ unsigned short int mask = 0; /* Mask for 16 general purpose registers. */
+ char *new_param; /* New created operands string. */
+ char *paramP = param; /* Pointer to original opearands string. */
+ char maskstring[10]; /* Array to print the mask as a string. */
+ int hi_found = 0, lo_found = 0; /* Boolean flags for hi/lo registers. */
+ reg r;
+ copreg cr;
+
+ /* If 'param' is already in form of a number, no need to preprocess. */
+ if (strchr (paramP, '{') == NULL)
+ return param;
+
+ /* Verifying correct syntax of operand. */
+ if (strchr (paramP, '}') == NULL)
+ as_fatal (_("Missing matching brackets : `%s'"), ins_parse);
+
+ while (*paramP++ != '{');
+
+ new_param = (char *)xcalloc (MAX_INST_LEN, sizeof (char));
+ *allocated = 1;
+ strncpy (new_param, param, paramP - param - 1);
+
+ while (*paramP != '}')
+ {
+ regP = paramP;
+ memset (&reg_name, '\0', sizeof (reg_name));
+
+ while (ISALNUM (*paramP))
+ paramP++;
+
+ strncpy (reg_name, regP, paramP - regP);
+
+ /* Coprocessor register c<N>. */
+ if (IS_INSN_TYPE (COP_REG_INS))
+ {
+ if (((cr = get_copregister (reg_name)) == nullcopregister)
+ || (crx_copregtab[cr-MAX_REG].type != CRX_C_REGTYPE))
+ as_fatal (_("Illegal register `%s' in cop-register list"), reg_name);
+ mask_reg (getreg_image (cr - c0), &mask);
+ }
+ /* Coprocessor Special register cs<N>. */
+ else if (IS_INSN_TYPE (COPS_REG_INS))
+ {
+ if (((cr = get_copregister (reg_name)) == nullcopregister)
+ || (crx_copregtab[cr-MAX_REG].type != CRX_CS_REGTYPE))
+ as_fatal (_("Illegal register `%s' in cop-special-register list"),
+ reg_name);
+ mask_reg (getreg_image (cr - cs0), &mask);
+ }
+ /* User register u<N>. */
+ else if (instruction->flags & USER_REG)
+ {
+ if (streq(reg_name, "uhi"))
+ {
+ hi_found = 1;
+ goto next_inst;
+ }
+ else if (streq(reg_name, "ulo"))
+ {
+ lo_found = 1;
+ goto next_inst;
+ }
+ else if (((r = get_register (reg_name)) == nullregister)
+ || (crx_regtab[r].type != CRX_U_REGTYPE))
+ as_fatal (_("Illegal register `%s' in user register list"), reg_name);
+
+ mask_reg (getreg_image (r - u0), &mask);
+ }
+ /* General purpose register r<N>. */
+ else
+ {
+ if (streq(reg_name, "hi"))
+ {
+ hi_found = 1;
+ goto next_inst;
+ }
+ else if (streq(reg_name, "lo"))
+ {
+ lo_found = 1;
+ goto next_inst;
+ }
+ else if (((r = get_register (reg_name)) == nullregister)
+ || (crx_regtab[r].type != CRX_R_REGTYPE))
+ as_fatal (_("Illegal register `%s' in register list"), reg_name);
+
+ mask_reg (getreg_image (r - r0), &mask);
+ }
+
+ if (++reg_counter > MAX_REGS_IN_MASK16)
+ as_bad (_("Maximum %d bits may be set in `mask16' operand"),
+ MAX_REGS_IN_MASK16);
+
+next_inst:
+ while (!ISALNUM (*paramP) && *paramP != '}')
+ paramP++;
+ }
+
+ if (*++paramP != '\0')
+ as_warn (_("rest of line ignored; first ignored character is `%c'"),
+ *paramP);
+
+ switch (hi_found + lo_found)
+ {
+ case 0:
+ /* At least one register should be specified. */
+ if (mask == 0)
+ as_bad (_("Illegal `mask16' operand, operation is undefined - `%s'"),
+ ins_parse);
+ break;
+
+ case 1:
+ /* HI can't be specified without LO (and vise-versa). */
+ as_bad (_("HI/LO registers should be specified together"));
+ break;
+
+ case 2:
+ /* HI/LO registers mustn't be masked with additional registers. */
+ if (mask != 0)
+ as_bad (_("HI/LO registers should be specified without additional registers"));
+
+ default:
+ break;
+ }
+
+ sprintf (maskstring, "$0x%x", mask);
+ strcat (new_param, maskstring);
+ return new_param;
+}
+
+/* Print the instruction.
+ Handle also cases where the instruction is relaxable/relocatable. */
+
+void
+print_insn (ins *insn)
+{
+ unsigned int i, j, insn_size;
+ char *this_frag;
+ unsigned short words[4];
+ int addr_mod;
+
+ /* Arrange the insn encodings in a WORD size array. */
+ for (i = 0, j = 0; i < 2; i++)
+ {
+ words[j++] = (output_opcode[i] >> 16) & 0xFFFF;
+ words[j++] = output_opcode[i] & 0xFFFF;
+ }
+
+ /* Handle relaxtion. */
+ if ((instruction->flags & RELAXABLE) && relocatable)
+ {
+ int relax_subtype;
+
+ /* Write the maximal instruction size supported. */
+ insn_size = INSN_MAX_SIZE;
+
+ /* bCC */
+ if (IS_INSN_TYPE (BRANCH_INS))
+ relax_subtype = 0;
+ /* bal */
+ else if (IS_INSN_TYPE (DCR_BRANCH_INS) || IS_INSN_MNEMONIC ("bal"))
+ relax_subtype = 3;
+ /* cmpbr/bcop */
+ else if (IS_INSN_TYPE (CMPBR_INS) || IS_INSN_TYPE (COP_BRANCH_INS))
+ relax_subtype = 5;
+ else
+ abort ();
+
+ this_frag = frag_var (rs_machine_dependent, insn_size * 2,
+ 4, relax_subtype,
+ insn->exp.X_add_symbol,
+ insn->exp.X_add_number,
+ 0);
+ }
+ else
+ {
+ insn_size = instruction->size;
+ this_frag = frag_more (insn_size * 2);
+
+ /* Handle relocation. */
+ if ((relocatable) && (insn->rtype != BFD_RELOC_NONE))
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, insn->rtype);
+
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size < 1 || size > 4)
+ abort ();
+
+ fix_new_exp (frag_now, this_frag - frag_now->fr_literal,
+ size, &insn->exp, reloc_howto->pc_relative,
+ insn->rtype);
+ }
+ }
+
+ /* Verify a 2-byte code alignment. */
+ addr_mod = frag_now_fix () & 1;
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ as_bad (_("instruction address is not a multiple of 2"));
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+
+ /* Write the instruction encoding to frag. */
+ for (i = 0; i < insn_size; i++)
+ {
+ md_number_to_chars (this_frag, (valueT) words[i], 2);
+ this_frag += 2;
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. OP points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *op)
+{
+ ins crx_ins;
+ char *param;
+ char c;
+
+ /* Reset global variables for a new instruction. */
+ reset_vars (op);
+
+ /* Strip the mnemonic. */
+ for (param = op; *param != 0 && !ISSPACE (*param); param++)
+ ;
+ c = *param;
+ *param++ = '\0';
+
+ /* Find the instruction. */
+ instruction = (const inst *) hash_find (crx_inst_hash, op);
+ if (instruction == NULL)
+ {
+ as_bad (_("Unknown opcode: `%s'"), op);
+ param[-1] = c;
+ return;
+ }
+
+ /* Tie dwarf2 debug info to the address at the start of the insn. */
+ dwarf2_emit_insn (0);
+
+ /* Parse the instruction's operands. */
+ parse_insn (&crx_ins, param);
+
+ /* Assemble the instruction - return upon failure. */
+ if (assemble_insn (op, &crx_ins) == 0)
+ {
+ param[-1] = c;
+ return;
+ }
+
+ /* Print the instruction. */
+ param[-1] = c;
+ print_insn (&crx_ins);
+}
diff --git a/gas/config/tc-crx.h b/gas/config/tc-crx.h
new file mode 100644
index 0000000..da6d710
--- /dev/null
+++ b/gas/config/tc-crx.h
@@ -0,0 +1,79 @@
+/* tc-crx.h -- Header file for tc-crx.c, the CRX GAS port.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ Contributed by Tomer Levi, NSC, Israel.
+ Originally written for GAS 2.12 by Tomer Levi, NSC, Israel.
+ Updates, BFDizing, GNUifying and ELF support by Tomer Levi.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef TC_CRX_H
+#define TC_CRX_H
+
+#define TC_CRX 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_FORMAT "elf32-crx"
+#define TARGET_ARCH bfd_arch_crx
+
+#define WORKING_DOT_WORD
+#define LOCAL_LABEL_PREFIX '.'
+
+#define md_undefined_symbol(s) 0
+#define md_number_to_chars number_to_chars_littleendian
+
+/* We do relaxing in the assembler as well as the linker. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* We do not want to adjust any relocations to make implementation of
+ linker relaxations easier. */
+#define tc_fix_adjustable(fixP) 0
+
+/* We need to force out some relocations when relaxing. */
+#define TC_FORCE_RELOCATION(FIXP) crx_force_relocation (FIXP)
+extern int crx_force_relocation (struct fix *);
+
+/* Fixup debug sections since we will never relax them. */
+#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_ALLOC)
+
+/* CRX instructions, with operands included, are a multiple
+ of two bytes long. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* This is called by emit_expr when creating a reloc for a cons.
+ We could use the definition there, except that we want to handle
+ the CRX reloc type specially, rather than the BFD_RELOC type. */
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP, RELOC) \
+ (void) RELOC, \
+ fix_new_exp (FRAG, OFF, (int) LEN, EXP, 0, \
+ LEN == 1 ? BFD_RELOC_CRX_NUM8 \
+ : LEN == 2 ? BFD_RELOC_CRX_NUM16 \
+ : LEN == 4 ? BFD_RELOC_CRX_NUM32 \
+ : BFD_RELOC_NONE);
+
+/* Give an error if a frag containing code is not aligned to a 2-byte
+ boundary. */
+#define md_frag_check(FRAGP) \
+ if ((FRAGP)->has_code \
+ && (((FRAGP)->fr_address + (FRAGP)->insn_addr) & 1) != 0) \
+ as_bad_where ((FRAGP)->fr_file, (FRAGP)->fr_line, \
+ _("instruction address is not a multiple of 2"));
+
+#endif /* TC_CRX_H */
diff --git a/gas/config/tc-d10v.c b/gas/config/tc-d10v.c
new file mode 100644
index 0000000..8e3d171
--- /dev/null
+++ b/gas/config/tc-d10v.c
@@ -0,0 +1,1819 @@
+/* tc-d10v.c -- Assembler code for the Mitsubishi D10V
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/d10v.h"
+#include "elf/ppc.h"
+#include "dwarf2dbg.h"
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "";
+const char *md_shortopts = "O";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+int Optimizing = 0;
+
+#define AT_WORD_P(X) ((X)->X_op == O_right_shift \
+ && (X)->X_op_symbol != NULL \
+ && symbol_constant_p ((X)->X_op_symbol) \
+ && S_GET_VALUE ((X)->X_op_symbol) == AT_WORD_RIGHT_SHIFT)
+#define AT_WORD_RIGHT_SHIFT 2
+
+/* Fixups. */
+#define MAX_INSN_FIXUPS 5
+
+struct d10v_fixup
+{
+ expressionS exp;
+ int operand;
+ int pcrel;
+ int size;
+ bfd_reloc_code_real_type reloc;
+};
+
+typedef struct _fixups
+{
+ int fc;
+ struct d10v_fixup fix[MAX_INSN_FIXUPS];
+ struct _fixups *next;
+} Fixups;
+
+static Fixups FixUps[2];
+static Fixups *fixups;
+
+static int do_not_ignore_hash = 0;
+
+typedef int packing_type;
+#define PACK_UNSPEC (0) /* Packing order not specified. */
+#define PACK_PARALLEL (1) /* "||" */
+#define PACK_LEFT_RIGHT (2) /* "->" */
+#define PACK_RIGHT_LEFT (3) /* "<-" */
+static packing_type etype = PACK_UNSPEC; /* Used by d10v_cleanup. */
+
+/* TRUE if instruction swapping warnings should be inhibited.
+ --nowarnswap. */
+static bfd_boolean flag_warn_suppress_instructionswap;
+
+/* TRUE if instruction packing should be performed when --gstabs is specified.
+ --gstabs-packing, --no-gstabs-packing. */
+static bfd_boolean flag_allow_gstabs_packing = 1;
+
+/* Local functions. */
+
+enum options
+{
+ OPTION_NOWARNSWAP = OPTION_MD_BASE,
+ OPTION_GSTABSPACKING,
+ OPTION_NOGSTABSPACKING
+};
+
+struct option md_longopts[] =
+{
+ {"nowarnswap", no_argument, NULL, OPTION_NOWARNSWAP},
+ {"gstabspacking", no_argument, NULL, OPTION_GSTABSPACKING},
+ {"gstabs-packing", no_argument, NULL, OPTION_GSTABSPACKING},
+ {"nogstabspacking", no_argument, NULL, OPTION_NOGSTABSPACKING},
+ {"no-gstabs-packing", no_argument, NULL, OPTION_NOGSTABSPACKING},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Opcode hash table. */
+static struct hash_control *d10v_hash;
+
+/* Do a binary search of the d10v_predefined_registers array to see if
+ NAME is a valid regiter name. Return the register number from the
+ array on success, or -1 on failure. */
+
+static int
+reg_name_search (char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = d10v_reg_name_cnt () - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, d10v_predefined_registers[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return d10v_predefined_registers[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+/* Check the string at input_line_pointer
+ to see if it is a valid register name. */
+
+static int
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char c, *p = input_line_pointer;
+
+ while (*p
+ && *p != '\n' && *p != '\r' && *p != ',' && *p != ' ' && *p != ')')
+ p++;
+
+ c = *p;
+ if (c)
+ *p++ = 0;
+
+ /* Look to see if it's in the register table. */
+ reg_number = reg_name_search (input_line_pointer);
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ /* Temporarily store a pointer to the string here. */
+ expressionP->X_op_symbol = (symbolS *) input_line_pointer;
+ expressionP->X_add_number = reg_number;
+ input_line_pointer = p;
+ return 1;
+ }
+ if (c)
+ *(p - 1) = c;
+ return 0;
+}
+
+static int
+check_range (unsigned long num, int bits, int flags)
+{
+ long min, max;
+ int retval = 0;
+
+ /* Don't bother checking 16-bit values. */
+ if (bits == 16)
+ return 0;
+
+ if (flags & OPERAND_SHIFT)
+ {
+ /* All special shift operands are unsigned and <= 16.
+ We allow 0 for now. */
+ if (num > 16)
+ return 1;
+ else
+ return 0;
+ }
+
+ if (flags & OPERAND_SIGNED)
+ {
+ /* Signed 3-bit integers are restricted to the (-2, 3) range. */
+ if (flags & RESTRICTED_NUM3)
+ {
+ if ((long) num < -2 || (long) num > 3)
+ retval = 1;
+ }
+ else
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ if (((long) num > max) || ((long) num < min))
+ retval = 1;
+ }
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ if (((long) num > max) || ((long) num < min))
+ retval = 1;
+ }
+ return retval;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("D10V options:\n\
+-O Optimize. Will do some operations in parallel.\n\
+--gstabs-packing Pack adjacent short instructions together even\n\
+ when --gstabs is specified. On by default.\n\
+--no-gstabs-packing If --gstabs is specified, do not pack adjacent\n\
+ instructions together.\n"));
+}
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case 'O':
+ /* Optimize. Will attempt to parallelize operations. */
+ Optimizing = 1;
+ break;
+ case OPTION_NOWARNSWAP:
+ flag_warn_suppress_instructionswap = 1;
+ break;
+ case OPTION_GSTABSPACKING:
+ flag_allow_gstabs_packing = 1;
+ break;
+ case OPTION_NOGSTABSPACKING:
+ flag_allow_gstabs_packing = 0;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin (void)
+{
+ char *prev_name = "";
+ struct d10v_opcode *opcode;
+ d10v_hash = hash_new ();
+
+ /* Insert unique names into hash table. The D10v instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ for (opcode = (struct d10v_opcode *) d10v_opcodes; opcode->name; opcode++)
+ {
+ if (strcmp (prev_name, opcode->name))
+ {
+ prev_name = (char *) opcode->name;
+ hash_insert (d10v_hash, opcode->name, (char *) opcode);
+ }
+ }
+
+ fixups = &FixUps[0];
+ FixUps[0].next = &FixUps[1];
+ FixUps[1].next = &FixUps[0];
+}
+
+/* Remove the postincrement or postdecrement operator ( '+' or '-' )
+ from an expression. */
+
+static int
+postfix (char *p)
+{
+ while (*p != '-' && *p != '+')
+ {
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ break;
+ p++;
+ }
+
+ if (*p == '-')
+ {
+ *p = ' ';
+ return -1;
+ }
+ if (*p == '+')
+ {
+ *p = ' ';
+ return 1;
+ }
+
+ return 0;
+}
+
+static bfd_reloc_code_real_type
+get_reloc (struct d10v_operand *op)
+{
+ int bits = op->bits;
+
+ if (bits <= 4)
+ return 0;
+
+ if (op->flags & OPERAND_ADDR)
+ {
+ if (bits == 8)
+ return BFD_RELOC_D10V_10_PCREL_R;
+ else
+ return BFD_RELOC_D10V_18_PCREL;
+ }
+
+ return BFD_RELOC_16;
+}
+
+/* Parse a string of operands. Return an array of expressions. */
+
+static int
+get_operands (expressionS exp[])
+{
+ char *p = input_line_pointer;
+ int numops = 0;
+ int post = 0;
+ int uses_at = 0;
+
+ while (*p)
+ {
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ break;
+
+ if (*p == '@')
+ {
+ uses_at = 1;
+
+ p++;
+ exp[numops].X_op = O_absent;
+ if (*p == '(')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATPAR;
+ }
+ else if (*p == '-')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATMINUS;
+ }
+ else
+ {
+ exp[numops].X_add_number = OPERAND_ATSIGN;
+ if (*p == '+')
+ {
+ numops++;
+ exp[numops].X_op = O_absent;
+ exp[numops].X_add_number = OPERAND_PLUS;
+ p++;
+ }
+ post = postfix (p);
+ }
+ numops++;
+ continue;
+ }
+
+ if (*p == ')')
+ {
+ /* Just skip the trailing paren. */
+ p++;
+ continue;
+ }
+
+ input_line_pointer = p;
+
+ /* Check to see if it might be a register name. */
+ if (!register_name (&exp[numops]))
+ {
+ /* Parse as an expression. */
+ if (uses_at)
+ {
+ /* Any expression that involves the indirect addressing
+ cannot also involve immediate addressing. Therefore
+ the use of the hash character is illegal. */
+ int save = do_not_ignore_hash;
+ do_not_ignore_hash = 1;
+
+ expression (&exp[numops]);
+
+ do_not_ignore_hash = save;
+ }
+ else
+ expression (&exp[numops]);
+ }
+
+ if (strncasecmp (input_line_pointer, "@word", 5) == 0)
+ {
+ input_line_pointer += 5;
+ if (exp[numops].X_op == O_register)
+ {
+ /* If it looked like a register name but was followed by
+ "@word" then it was really a symbol, so change it to
+ one. */
+ exp[numops].X_op = O_symbol;
+ exp[numops].X_add_symbol =
+ symbol_find_or_make ((char *) exp[numops].X_op_symbol);
+ }
+
+ /* Check for identifier@word+constant. */
+ if (*input_line_pointer == '-' || *input_line_pointer == '+')
+ {
+ expressionS new_exp;
+ expression (&new_exp);
+ exp[numops].X_add_number = new_exp.X_add_number;
+ }
+
+ /* Convert expr into a right shift by AT_WORD_RIGHT_SHIFT. */
+ {
+ expressionS new_exp;
+ memset (&new_exp, 0, sizeof new_exp);
+ new_exp.X_add_number = AT_WORD_RIGHT_SHIFT;
+ new_exp.X_op = O_constant;
+ new_exp.X_unsigned = 1;
+ exp[numops].X_op_symbol = make_expr_symbol (&new_exp);
+ exp[numops].X_op = O_right_shift;
+ }
+
+ know (AT_WORD_P (&exp[numops]));
+ }
+
+ if (exp[numops].X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (exp[numops].X_op == O_absent)
+ as_bad (_("missing operand"));
+
+ numops++;
+ p = input_line_pointer;
+ }
+
+ switch (post)
+ {
+ case -1: /* Postdecrement mode. */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_MINUS;
+ break;
+ case 1: /* Postincrement mode. */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_PLUS;
+ break;
+ }
+
+ exp[numops].X_op = 0;
+ return numops;
+}
+
+static unsigned long
+d10v_insert_operand (unsigned long insn,
+ int op_type,
+ offsetT value,
+ int left,
+ fixS *fix)
+{
+ int shift, bits;
+
+ shift = d10v_operands[op_type].shift;
+ if (left)
+ shift += 15;
+
+ bits = d10v_operands[op_type].bits;
+
+ /* Truncate to the proper number of bits. */
+ if (check_range (value, bits, d10v_operands[op_type].flags))
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("operand out of range: %ld"), (long) value);
+
+ value &= 0x7FFFFFFF >> (31 - bits);
+ insn |= (value << shift);
+
+ return insn;
+}
+
+/* Take a pointer to the opcode entry in the opcode table and the
+ array of operand expressions. Return the instruction. */
+
+static unsigned long
+build_insn (struct d10v_opcode *opcode,
+ expressionS *opers,
+ unsigned long insn)
+{
+ int i, bits, shift, flags, format;
+ unsigned long number;
+
+ /* The insn argument is only used for the DIVS kludge. */
+ if (insn)
+ format = LONG_R;
+ else
+ {
+ insn = opcode->opcode;
+ format = opcode->format;
+ }
+
+ for (i = 0; opcode->operands[i]; i++)
+ {
+ flags = d10v_operands[opcode->operands[i]].flags;
+ bits = d10v_operands[opcode->operands[i]].bits;
+ shift = d10v_operands[opcode->operands[i]].shift;
+ number = opers[i].X_add_number;
+
+ if (flags & OPERAND_REG)
+ {
+ number &= REGISTER_MASK;
+ if (format == LONG_L)
+ shift += 15;
+ }
+
+ if (opers[i].X_op != O_register && opers[i].X_op != O_constant)
+ {
+ /* Now create a fixup. */
+
+ if (fixups->fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ if (AT_WORD_P (&opers[i]))
+ {
+ /* Recognize XXX>>1+N aka XXX@word+N as special (AT_WORD). */
+ fixups->fix[fixups->fc].reloc = BFD_RELOC_D10V_18;
+ opers[i].X_op = O_symbol;
+ opers[i].X_op_symbol = NULL; /* Should free it. */
+ /* number is left shifted by AT_WORD_RIGHT_SHIFT so
+ that, it is aligned with the symbol's value. Later,
+ BFD_RELOC_D10V_18 will right shift (symbol_value +
+ X_add_number). */
+ number <<= AT_WORD_RIGHT_SHIFT;
+ opers[i].X_add_number = number;
+ }
+ else
+ {
+ fixups->fix[fixups->fc].reloc =
+ get_reloc ((struct d10v_operand *) &d10v_operands[opcode->operands[i]]);
+
+ /* Check that an immediate was passed to ops that expect one. */
+ if ((flags & OPERAND_NUM)
+ && (fixups->fix[fixups->fc].reloc == 0))
+ as_bad (_("operand is not an immediate"));
+ }
+
+ if (fixups->fix[fixups->fc].reloc == BFD_RELOC_16 ||
+ fixups->fix[fixups->fc].reloc == BFD_RELOC_D10V_18)
+ fixups->fix[fixups->fc].size = 2;
+ else
+ fixups->fix[fixups->fc].size = 4;
+
+ fixups->fix[fixups->fc].exp = opers[i];
+ fixups->fix[fixups->fc].operand = opcode->operands[i];
+ fixups->fix[fixups->fc].pcrel =
+ (flags & OPERAND_ADDR) ? TRUE : FALSE;
+ (fixups->fc)++;
+ }
+
+ /* Truncate to the proper number of bits. */
+ if ((opers[i].X_op == O_constant) && check_range (number, bits, flags))
+ as_bad (_("operand out of range: %lu"), number);
+ number &= 0x7FFFFFFF >> (31 - bits);
+ insn = insn | (number << shift);
+ }
+
+ /* kludge: for DIVS, we need to put the operands in twice on the second
+ pass, format is changed to LONG_R to force the second set of operands
+ to not be shifted over 15. */
+ if ((opcode->opcode == OPCODE_DIVS) && (format == LONG_L))
+ insn = build_insn (opcode, opers, insn);
+
+ return insn;
+}
+
+/* Write out a long form instruction. */
+
+static void
+write_long (unsigned long insn, Fixups *fx)
+{
+ int i, where;
+ char *f = frag_more (4);
+
+ dwarf2_emit_insn (4);
+ insn |= FM11;
+ number_to_chars_bigendian (f, insn, 4);
+
+ for (i = 0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ if (fx->fix[i].size == 2)
+ where += 2;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
+ fx->fix[i].operand |= 4096;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].operand|2048);
+ }
+ }
+ fx->fc = 0;
+}
+
+/* Write out a short form instruction by itself. */
+
+static void
+write_1_short (struct d10v_opcode *opcode,
+ unsigned long insn,
+ Fixups *fx)
+{
+ char *f = frag_more (4);
+ int i, where;
+
+ dwarf2_emit_insn (4);
+ if (opcode->exec_type & PARONLY)
+ as_fatal (_("Instruction must be executed in parallel with another instruction."));
+
+ /* The other container needs to be NOP.
+ According to 4.3.1: for FM=00, sub-instructions performed only by IU
+ cannot be encoded in L-container. */
+ if (opcode->unit == IU)
+ insn |= FM00 | (NOP << 15); /* Right container. */
+ else
+ insn = FM00 | (insn << 15) | NOP; /* Left container. */
+
+ number_to_chars_bigendian (f, insn, 4);
+ for (i = 0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ if (fx->fix[i].size == 2)
+ where += 2;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
+ fx->fix[i].operand |= 4096;
+
+ /* If it's an R reloc, we may have to switch it to L. */
+ if ((fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R)
+ && (opcode->unit != IU))
+ fx->fix[i].operand |= 1024;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].operand|2048);
+ }
+ }
+ fx->fc = 0;
+}
+
+/* Determine if there are any resource conflicts among two manually
+ parallelized instructions. Some of this was lifted from parallel_ok. */
+
+static void
+check_resource_conflict (struct d10v_opcode *op1,
+ unsigned long insn1,
+ struct d10v_opcode *op2,
+ unsigned long insn2)
+{
+ int i, j, flags, mask, shift, regno;
+ unsigned long ins, mod[2];
+ struct d10v_opcode *op;
+
+ if ((op1->exec_type & SEQ)
+ || ! ((op1->exec_type & PAR) || (op1->exec_type & PARONLY)))
+ {
+ as_warn (_("packing conflict: %s must dispatch sequentially"),
+ op1->name);
+ return;
+ }
+
+ if ((op2->exec_type & SEQ)
+ || ! ((op2->exec_type & PAR) || (op2->exec_type & PARONLY)))
+ {
+ as_warn (_("packing conflict: %s must dispatch sequentially"),
+ op2->name);
+ return;
+ }
+
+ /* See if both instructions write to the same resource.
+
+ The idea here is to create two sets of bitmasks (mod and used) which
+ indicate which registers are modified or used by each instruction.
+ The operation can only be done in parallel if neither instruction
+ modifies the same register. Accesses to control registers and memory
+ are treated as accesses to a single register. So if both instructions
+ write memory or if the first instruction writes memory and the second
+ reads, then they cannot be done in parallel. We treat reads to the PSW
+ (which includes C, F0, and F1) in isolation. So simultaneously writing
+ C and F0 in two different sub-instructions is permitted. */
+
+ /* The bitmasks (mod and used) look like this (bit 31 = MSB).
+ r0-r15 0-15
+ a0-a1 16-17
+ cr (not psw) 18
+ psw(other) 19
+ mem 20
+ psw(C flag) 21
+ psw(F0 flag) 22 */
+
+ for (j = 0; j < 2; j++)
+ {
+ if (j == 0)
+ {
+ op = op1;
+ ins = insn1;
+ }
+ else
+ {
+ op = op2;
+ ins = insn2;
+ }
+ mod[j] = 0;
+ if (op->exec_type & BRANCH_LINK)
+ mod[j] |= 1 << 13;
+
+ for (i = 0; op->operands[i]; i++)
+ {
+ flags = d10v_operands[op->operands[i]].flags;
+ shift = d10v_operands[op->operands[i]].shift;
+ mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
+ if (flags & OPERAND_REG)
+ {
+ regno = (ins >> shift) & mask;
+ if (flags & (OPERAND_ACC0 | OPERAND_ACC1))
+ regno += 16;
+ else if (flags & OPERAND_CONTROL) /* mvtc or mvfc */
+ {
+ if (regno == 0)
+ regno = 19;
+ else
+ regno = 18;
+ }
+ else if (flags & OPERAND_FFLAG)
+ regno = 22;
+ else if (flags & OPERAND_CFLAG)
+ regno = 21;
+
+ if (flags & OPERAND_DEST
+ /* Auto inc/dec also modifies the register. */
+ || (op->operands[i + 1] != 0
+ && (d10v_operands[op->operands[i + 1]].flags
+ & (OPERAND_PLUS | OPERAND_MINUS)) != 0))
+ {
+ mod[j] |= 1 << regno;
+ if (flags & OPERAND_EVEN)
+ mod[j] |= 1 << (regno + 1);
+ }
+ }
+ else if (flags & OPERAND_ATMINUS)
+ {
+ /* SP implicitly used/modified. */
+ mod[j] |= 1 << 15;
+ }
+ }
+
+ if (op->exec_type & WMEM)
+ mod[j] |= 1 << 20;
+ else if (op->exec_type & WF0)
+ mod[j] |= 1 << 22;
+ else if (op->exec_type & WCAR)
+ mod[j] |= 1 << 21;
+ }
+
+ if ((mod[0] & mod[1]) == 0)
+ return;
+ else
+ {
+ unsigned long x;
+ x = mod[0] & mod[1];
+
+ for (j = 0; j <= 15; j++)
+ if (x & (1 << j))
+ as_warn (_("resource conflict (R%d)"), j);
+ for (j = 16; j <= 17; j++)
+ if (x & (1 << j))
+ as_warn (_("resource conflict (A%d)"), j - 16);
+ if (x & (1 << 19))
+ as_warn (_("resource conflict (PSW)"));
+ if (x & (1 << 21))
+ as_warn (_("resource conflict (C flag)"));
+ if (x & (1 << 22))
+ as_warn (_("resource conflict (F flag)"));
+ }
+}
+
+/* Check 2 instructions and determine if they can be safely
+ executed in parallel. Return 1 if they can be. */
+
+static int
+parallel_ok (struct d10v_opcode *op1,
+ unsigned long insn1,
+ struct d10v_opcode *op2,
+ unsigned long insn2,
+ packing_type exec_type)
+{
+ int i, j, flags, mask, shift, regno;
+ unsigned long ins, mod[2], used[2];
+ struct d10v_opcode *op;
+
+ if ((op1->exec_type & SEQ) != 0 || (op2->exec_type & SEQ) != 0
+ || (op1->exec_type & PAR) == 0 || (op2->exec_type & PAR) == 0
+ || (op1->unit == BOTH) || (op2->unit == BOTH)
+ || (op1->unit == IU && op2->unit == IU)
+ || (op1->unit == MU && op2->unit == MU))
+ return 0;
+
+ /* If this is auto parallelization, and the first instruction is a
+ branch or should not be packed, then don't parallelize. */
+ if (exec_type == PACK_UNSPEC
+ && (op1->exec_type & (ALONE | BRANCH)))
+ return 0;
+
+ /* The idea here is to create two sets of bitmasks (mod and used)
+ which indicate which registers are modified or used by each
+ instruction. The operation can only be done in parallel if
+ instruction 1 and instruction 2 modify different registers, and
+ the first instruction does not modify registers that the second
+ is using (The second instruction can modify registers that the
+ first is using as they are only written back after the first
+ instruction has completed). Accesses to control registers, PSW,
+ and memory are treated as accesses to a single register. So if
+ both instructions write memory or if the first instruction writes
+ memory and the second reads, then they cannot be done in
+ parallel. Likewise, if the first instruction mucks with the psw
+ and the second reads the PSW (which includes C, F0, and F1), then
+ they cannot operate safely in parallel. */
+
+ /* The bitmasks (mod and used) look like this (bit 31 = MSB).
+ r0-r15 0-15
+ a0-a1 16-17
+ cr (not psw) 18
+ psw 19
+ mem 20 */
+
+ for (j = 0; j < 2; j++)
+ {
+ if (j == 0)
+ {
+ op = op1;
+ ins = insn1;
+ }
+ else
+ {
+ op = op2;
+ ins = insn2;
+ }
+ mod[j] = used[j] = 0;
+ if (op->exec_type & BRANCH_LINK)
+ mod[j] |= 1 << 13;
+
+ for (i = 0; op->operands[i]; i++)
+ {
+ flags = d10v_operands[op->operands[i]].flags;
+ shift = d10v_operands[op->operands[i]].shift;
+ mask = 0x7FFFFFFF >> (31 - d10v_operands[op->operands[i]].bits);
+ if (flags & OPERAND_REG)
+ {
+ regno = (ins >> shift) & mask;
+ if (flags & (OPERAND_ACC0 | OPERAND_ACC1))
+ regno += 16;
+ else if (flags & OPERAND_CONTROL) /* mvtc or mvfc. */
+ {
+ if (regno == 0)
+ regno = 19;
+ else
+ regno = 18;
+ }
+ else if (flags & (OPERAND_FFLAG | OPERAND_CFLAG))
+ regno = 19;
+
+ if (flags & OPERAND_DEST)
+ {
+ mod[j] |= 1 << regno;
+ if (flags & OPERAND_EVEN)
+ mod[j] |= 1 << (regno + 1);
+ }
+ else
+ {
+ used[j] |= 1 << regno;
+ if (flags & OPERAND_EVEN)
+ used[j] |= 1 << (regno + 1);
+
+ /* Auto inc/dec also modifies the register. */
+ if (op->operands[i + 1] != 0
+ && (d10v_operands[op->operands[i + 1]].flags
+ & (OPERAND_PLUS | OPERAND_MINUS)) != 0)
+ mod[j] |= 1 << regno;
+ }
+ }
+ else if (flags & OPERAND_ATMINUS)
+ {
+ /* SP implicitly used/modified. */
+ mod[j] |= 1 << 15;
+ used[j] |= 1 << 15;
+ }
+ }
+ if (op->exec_type & RMEM)
+ used[j] |= 1 << 20;
+ else if (op->exec_type & WMEM)
+ mod[j] |= 1 << 20;
+ else if (op->exec_type & RF0)
+ used[j] |= 1 << 19;
+ else if (op->exec_type & WF0)
+ mod[j] |= 1 << 19;
+ else if (op->exec_type & WCAR)
+ mod[j] |= 1 << 19;
+ }
+ if ((mod[0] & mod[1]) == 0 && (mod[0] & used[1]) == 0)
+ return 1;
+ return 0;
+}
+
+/* Expects two short instructions.
+ If possible, writes out both as a single packed instruction.
+ Otherwise, writes out the first one, packed with a NOP.
+ Returns number of instructions not written out. */
+
+static int
+write_2_short (struct d10v_opcode *opcode1,
+ unsigned long insn1,
+ struct d10v_opcode *opcode2,
+ unsigned long insn2,
+ packing_type exec_type,
+ Fixups *fx)
+{
+ unsigned long insn;
+ char *f;
+ int i, j, where;
+
+ if ((exec_type != PACK_PARALLEL)
+ && ((opcode1->exec_type & PARONLY) || (opcode2->exec_type & PARONLY)))
+ as_fatal (_("Instruction must be executed in parallel"));
+
+ if ((opcode1->format & LONG_OPCODE) || (opcode2->format & LONG_OPCODE))
+ as_fatal (_("Long instructions may not be combined."));
+
+ switch (exec_type)
+ {
+ case PACK_UNSPEC: /* Order not specified. */
+ if (opcode1->exec_type & ALONE)
+ {
+ /* Case of a short branch on a separate GAS line. Pack with NOP. */
+ write_1_short (opcode1, insn1, fx->next);
+ return 1;
+ }
+ if (Optimizing
+ && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
+ {
+ /* Parallel. */
+ if (opcode1->unit == IU)
+ insn = FM00 | (insn2 << 15) | insn1;
+ else if (opcode2->unit == MU)
+ insn = FM00 | (insn2 << 15) | insn1;
+ else
+ insn = FM00 | (insn1 << 15) | insn2;
+ }
+ else if (opcode1->unit == IU)
+ /* Reverse sequential with IU opcode1 on right and done first. */
+ insn = FM10 | (insn2 << 15) | insn1;
+ else
+ /* Sequential with non-IU opcode1 on left and done first. */
+ insn = FM01 | (insn1 << 15) | insn2;
+ break;
+
+ case PACK_PARALLEL:
+ if (opcode1->exec_type & SEQ || opcode2->exec_type & SEQ)
+ as_fatal
+ (_("One of these instructions may not be executed in parallel."));
+ if (opcode1->unit == IU)
+ {
+ if (opcode2->unit == IU)
+ as_fatal (_("Two IU instructions may not be executed in parallel"));
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 15) | insn1;
+ }
+ else if (opcode2->unit == MU)
+ {
+ if (opcode1->unit == MU)
+ as_fatal (_("Two MU instructions may not be executed in parallel"));
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 15) | insn1;
+ }
+ else
+ insn = FM00 | (insn1 << 15) | insn2;
+ check_resource_conflict (opcode1, insn1, opcode2, insn2);
+ break;
+
+ case PACK_LEFT_RIGHT:
+ if (opcode1->unit != IU)
+ insn = FM01 | (insn1 << 15) | insn2;
+ else if (opcode2->unit == MU || opcode2->unit == EITHER)
+ {
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM10 | (insn2 << 15) | insn1;
+ }
+ else
+ as_fatal (_("IU instruction may not be in the left container"));
+ if (opcode1->exec_type & ALONE)
+ as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
+ break;
+
+ case PACK_RIGHT_LEFT:
+ if (opcode2->unit != MU)
+ insn = FM10 | (insn1 << 15) | insn2;
+ else if (opcode1->unit == IU || opcode1->unit == EITHER)
+ {
+ if (!flag_warn_suppress_instructionswap)
+ as_warn (_("Swapping instruction order"));
+ insn = FM01 | (insn2 << 15) | insn1;
+ }
+ else
+ as_fatal (_("MU instruction may not be in the right container"));
+ if (opcode2->exec_type & ALONE)
+ as_warn (_("Instruction in R container is squashed by flow control instruction in L container."));
+ break;
+
+ default:
+ as_fatal (_("unknown execution type passed to write_2_short()"));
+ }
+
+ f = frag_more (4);
+ dwarf2_emit_insn (4);
+ number_to_chars_bigendian (f, insn, 4);
+
+ /* Process fixup chains. fx refers to insn2 when j == 0, and to
+ insn1 when j == 1. Yes, it's reversed. */
+
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ if (fx->fix[i].size == 2)
+ where += 2;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_10_PCREL_R
+ /* A BFD_RELOC_D10V_10_PCREL_R relocation applied to
+ the instruction in the L container has to be
+ adjusted to BDF_RELOC_D10V_10_PCREL_L. When
+ j==0, we're processing insn2's operands, so we
+ want to mark the operand if insn2 is *not* in the
+ R container. When j==1, we're processing insn1's
+ operands, so we want to mark the operand if insn2
+ *is* in the R container. Note that, if two
+ instructions are identical, we're never going to
+ swap them, so the test is safe. */
+ && j == ((insn & 0x7fff) == insn2))
+ fx->fix[i].operand |= 1024;
+
+ if (fx->fix[i].reloc == BFD_RELOC_D10V_18)
+ fx->fix[i].operand |= 4096;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].operand|2048);
+ }
+ }
+ fx->fc = 0;
+ fx = fx->next;
+ }
+ return 0;
+}
+
+/* This is the main entry point for the machine-dependent assembler.
+ str points to a machine-dependent instruction. This function is
+ supposed to emit the frags/bytes it assembles to. For the D10V, it
+ mostly handles the special VLIW parsing and packing and leaves the
+ difficult stuff to do_assemble(). */
+
+static unsigned long prev_insn;
+static struct d10v_opcode *prev_opcode = 0;
+static subsegT prev_subseg;
+static segT prev_seg = 0;
+
+/* Find the symbol which has the same name as the register in exp. */
+
+static symbolS *
+find_symbol_matching_register (expressionS *exp)
+{
+ int i;
+
+ if (exp->X_op != O_register)
+ return NULL;
+
+ /* Find the name of the register. */
+ for (i = d10v_reg_name_cnt (); i--;)
+ if (d10v_predefined_registers[i].value == exp->X_add_number)
+ break;
+
+ if (i < 0)
+ abort ();
+
+ /* Now see if a symbol has been defined with the same name. */
+ return symbol_find (d10v_predefined_registers[i].name);
+}
+
+/* Get a pointer to an entry in the opcode table.
+ The function must look at all opcodes with the same name and use
+ the operands to choose the correct opcode. */
+
+static struct d10v_opcode *
+find_opcode (struct d10v_opcode *opcode, expressionS myops[])
+{
+ int i, match;
+ struct d10v_opcode *next_opcode;
+
+ /* Get all the operands and save them as expressions. */
+ get_operands (myops);
+
+ /* Now see if the operand is a fake. If so, find the correct size
+ instruction, if possible. */
+ if (opcode->format == OPCODE_FAKE)
+ {
+ int opnum = opcode->operands[0];
+ int flags;
+
+ if (myops[opnum].X_op == O_register)
+ {
+ myops[opnum].X_op = O_symbol;
+ myops[opnum].X_add_symbol =
+ symbol_find_or_make ((char *) myops[opnum].X_op_symbol);
+ myops[opnum].X_add_number = 0;
+ myops[opnum].X_op_symbol = NULL;
+ }
+
+ next_opcode = opcode + 1;
+
+ /* If the first operand is supposed to be a register, make sure
+ we got a valid one. */
+ flags = d10v_operands[next_opcode->operands[0]].flags;
+ if (flags & OPERAND_REG)
+ {
+ int X_op = myops[0].X_op;
+ int num = myops[0].X_add_number;
+
+ if (X_op != O_register
+ || (num & ~flags
+ & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
+ | OPERAND_FFLAG | OPERAND_CFLAG | OPERAND_CONTROL))
+ || ((flags & OPERAND_SP) && ! (num & OPERAND_SP)))
+ {
+ as_bad (_("bad opcode or operands"));
+ return 0;
+ }
+ }
+
+ if (myops[opnum].X_op == O_constant
+ || (myops[opnum].X_op == O_symbol
+ && S_IS_DEFINED (myops[opnum].X_add_symbol)
+ && (S_GET_SEGMENT (myops[opnum].X_add_symbol) == now_seg)))
+ {
+ for (i = 0; opcode->operands[i + 1]; i++)
+ {
+ int bits = d10v_operands[next_opcode->operands[opnum]].bits;
+
+ flags = d10v_operands[next_opcode->operands[opnum]].flags;
+
+ if (flags & OPERAND_ADDR)
+ bits += 2;
+
+ if (myops[opnum].X_op == O_constant)
+ {
+ if (!check_range (myops[opnum].X_add_number, bits, flags))
+ break;
+ }
+ else
+ {
+ fragS *sym_frag;
+ fragS *f;
+ unsigned long current_position;
+ unsigned long symbol_position;
+ unsigned long value;
+ bfd_boolean found_symbol;
+
+ /* Calculate the address of the current instruction
+ and the address of the symbol. Do this by summing
+ the offsets of previous frags until we reach the
+ frag containing the symbol, and the current frag. */
+ sym_frag = symbol_get_frag (myops[opnum].X_add_symbol);
+ found_symbol = FALSE;
+
+ current_position =
+ obstack_next_free (&frchain_now->frch_obstack)
+ - frag_now->fr_literal;
+ symbol_position = S_GET_VALUE (myops[opnum].X_add_symbol);
+
+ for (f = frchain_now->frch_root; f; f = f->fr_next)
+ {
+ current_position += f->fr_fix + f->fr_offset;
+
+ if (f == sym_frag)
+ found_symbol = TRUE;
+
+ if (! found_symbol)
+ symbol_position += f->fr_fix + f->fr_offset;
+ }
+
+ value = symbol_position;
+
+ if (flags & OPERAND_ADDR)
+ value -= current_position;
+
+ if (AT_WORD_P (&myops[opnum]))
+ {
+ if (bits > 4)
+ {
+ bits += 2;
+ if (!check_range (value, bits, flags))
+ break;
+ }
+ }
+ else if (!check_range (value, bits, flags))
+ break;
+ }
+ next_opcode++;
+ }
+
+ if (opcode->operands [i + 1] == 0)
+ as_fatal (_("value out of range"));
+ else
+ opcode = next_opcode;
+ }
+ else
+ /* Not a constant, so use a long instruction. */
+ opcode += 2;
+ }
+
+ match = 0;
+
+ /* Now search the opcode table table for one with operands
+ that matches what we've got. */
+ while (!match)
+ {
+ match = 1;
+ for (i = 0; opcode->operands[i]; i++)
+ {
+ int flags = d10v_operands[opcode->operands[i]].flags;
+ int X_op = myops[i].X_op;
+ int num = myops[i].X_add_number;
+
+ if (X_op == 0)
+ {
+ match = 0;
+ break;
+ }
+
+ if (flags & OPERAND_REG)
+ {
+ if ((X_op != O_register)
+ || (num & ~flags
+ & (OPERAND_GPR | OPERAND_ACC0 | OPERAND_ACC1
+ | OPERAND_FFLAG | OPERAND_CFLAG
+ | OPERAND_CONTROL))
+ || ((flags & OPERAND_SP) && ! (num & OPERAND_SP)))
+ {
+ match = 0;
+ break;
+ }
+ }
+
+ if (((flags & OPERAND_MINUS) && ((X_op != O_absent) || (num != OPERAND_MINUS))) ||
+ ((flags & OPERAND_PLUS) && ((X_op != O_absent) || (num != OPERAND_PLUS))) ||
+ ((flags & OPERAND_ATMINUS) && ((X_op != O_absent) || (num != OPERAND_ATMINUS))) ||
+ ((flags & OPERAND_ATPAR) && ((X_op != O_absent) || (num != OPERAND_ATPAR))) ||
+ ((flags & OPERAND_ATSIGN) && ((X_op != O_absent) || ((num != OPERAND_ATSIGN) && (num != OPERAND_ATPAR)))))
+ {
+ match = 0;
+ break;
+ }
+
+ /* Unfortunately, for the indirect operand in instructions such
+ as ``ldb r1, @(c,r14)'' this function can be passed
+ X_op == O_register (because 'c' is a valid register name).
+ However we cannot just ignore the case when X_op == O_register
+ but flags & OPERAND_REG is null, so we check to see if a symbol
+ of the same name as the register exists. If the symbol does
+ exist, then the parser was unable to distinguish the two cases
+ and we fix things here. (Ref: PR14826) */
+
+ if (!(flags & OPERAND_REG) && (X_op == O_register))
+ {
+ symbolS * sym;
+
+ sym = find_symbol_matching_register (& myops[i]);
+
+ if (sym != NULL)
+ {
+ myops[i].X_op = X_op = O_symbol;
+ myops[i].X_add_symbol = sym;
+ }
+ else
+ as_bad
+ (_("illegal operand - register name found where none expected"));
+ }
+ }
+
+ /* We're only done if the operands matched so far AND there
+ are no more to check. */
+ if (match && myops[i].X_op == 0)
+ break;
+ else
+ match = 0;
+
+ next_opcode = opcode + 1;
+
+ if (next_opcode->opcode == 0)
+ break;
+
+ if (strcmp (next_opcode->name, opcode->name))
+ break;
+
+ opcode = next_opcode;
+ }
+
+ if (!match)
+ {
+ as_bad (_("bad opcode or operands"));
+ return 0;
+ }
+
+ /* Check that all registers that are required to be even are.
+ Also, if any operands were marked as registers, but were really symbols,
+ fix that here. */
+ for (i = 0; opcode->operands[i]; i++)
+ {
+ if ((d10v_operands[opcode->operands[i]].flags & OPERAND_EVEN) &&
+ (myops[i].X_add_number & 1))
+ as_fatal (_("Register number must be EVEN"));
+ if ((d10v_operands[opcode->operands[i]].flags & OPERAND_NOSP)
+ && (myops[i].X_add_number & OPERAND_SP))
+ as_bad (_("Unsupported use of sp"));
+ if (myops[i].X_op == O_register)
+ {
+ if (!(d10v_operands[opcode->operands[i]].flags & OPERAND_REG))
+ {
+ myops[i].X_op = O_symbol;
+ myops[i].X_add_symbol =
+ symbol_find_or_make ((char *) myops[i].X_op_symbol);
+ myops[i].X_add_number = 0;
+ myops[i].X_op_symbol = NULL;
+ }
+ }
+ if ((d10v_operands[opcode->operands[i]].flags & OPERAND_CONTROL)
+ && (myops[i].X_add_number == OPERAND_CONTROL + 4
+ || myops[i].X_add_number == OPERAND_CONTROL + 5
+ || myops[i].X_add_number == OPERAND_CONTROL + 6
+ || myops[i].X_add_number == OPERAND_CONTROL + 12
+ || myops[i].X_add_number == OPERAND_CONTROL + 13
+ || myops[i].X_add_number == OPERAND_CONTROL + 15))
+ as_warn (_("cr%ld is a reserved control register"),
+ myops[i].X_add_number - OPERAND_CONTROL);
+ }
+ return opcode;
+}
+
+/* Assemble a single instruction.
+ Return an opcode, or -1 (an invalid opcode) on error. */
+
+static unsigned long
+do_assemble (char *str, struct d10v_opcode **opcode)
+{
+ unsigned char *op_start, *op_end;
+ char *save;
+ char name[20];
+ int nlen = 0;
+ expressionS myops[6];
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the opcode end. */
+ for (op_start = op_end = (unsigned char *) str;
+ *op_end && !is_end_of_line[*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = TOLOWER (op_start[nlen]);
+ nlen++;
+ if (nlen == sizeof (name) - 1)
+ break;
+ }
+ name[nlen] = 0;
+
+ if (nlen == 0)
+ return -1;
+
+ /* Find the first opcode with the proper name. */
+ *opcode = (struct d10v_opcode *) hash_find (d10v_hash, name);
+ if (*opcode == NULL)
+ return -1;
+
+ save = input_line_pointer;
+ input_line_pointer = (char *) op_end;
+ *opcode = find_opcode (*opcode, myops);
+ if (*opcode == 0)
+ return -1;
+ input_line_pointer = save;
+
+ return build_insn ((*opcode), myops, 0);
+}
+
+/* If while processing a fixup, a reloc really needs to be created.
+ Then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ reloc->addend = 0;
+
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+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)))
+ return 0;
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where;
+ unsigned long insn;
+ long value = *valP;
+ int op_type;
+ int left = 0;
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ /* We don't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ op_type = fixP->fx_r_type;
+ if (op_type & 2048)
+ {
+ op_type -= 2048;
+ if (op_type & 1024)
+ {
+ op_type -= 1024;
+ fixP->fx_r_type = BFD_RELOC_D10V_10_PCREL_L;
+ left = 1;
+ }
+ else if (op_type & 4096)
+ {
+ op_type -= 4096;
+ fixP->fx_r_type = BFD_RELOC_D10V_18;
+ }
+ else
+ fixP->fx_r_type =
+ get_reloc ((struct d10v_operand *) &d10v_operands[op_type]);
+ }
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ insn = bfd_getb32 ((unsigned char *) where);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_D10V_10_PCREL_L:
+ case BFD_RELOC_D10V_10_PCREL_R:
+ case BFD_RELOC_D10V_18_PCREL:
+ /* If the fix is relative to a global symbol, not a section
+ symbol, then ignore the offset.
+ XXX - Do we have to worry about branches to a symbol + offset ? */
+ if (fixP->fx_addsy != NULL
+ && S_IS_EXTERNAL (fixP->fx_addsy) )
+ {
+ segT fseg = S_GET_SEGMENT (fixP->fx_addsy);
+ segment_info_type *segf = seg_info(fseg);
+
+ if ( segf && segf->sym != fixP->fx_addsy)
+ value = 0;
+ }
+ /* Drop through. */
+ case BFD_RELOC_D10V_18:
+ /* Instruction addresses are always right-shifted by 2. */
+ value >>= AT_WORD_RIGHT_SHIFT;
+ if (fixP->fx_size == 2)
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ else
+ {
+ struct d10v_opcode *rep, *repi;
+
+ rep = (struct d10v_opcode *) hash_find (d10v_hash, "rep");
+ repi = (struct d10v_opcode *) hash_find (d10v_hash, "repi");
+ if ((insn & FM11) == FM11
+ && ((repi != NULL
+ && (insn & repi->mask) == (unsigned) repi->opcode)
+ || (rep != NULL
+ && (insn & rep->mask) == (unsigned) rep->opcode))
+ && value < 4)
+ as_fatal
+ (_("line %d: rep or repi must include at least 4 instructions"),
+ fixP->fx_line);
+ insn =
+ d10v_insert_operand (insn, op_type, (offsetT) value, left, fixP);
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ break;
+ case BFD_RELOC_32:
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ break;
+ case BFD_RELOC_16:
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ default:
+ as_fatal (_("line %d: unknown relocation type: 0x%x"),
+ fixP->fx_line, fixP->fx_r_type);
+ }
+}
+
+/* d10v_cleanup() is called after the assembler has finished parsing
+ the input file, when a label is read from the input file, or when a
+ stab directive is output. Because the D10V assembler sometimes
+ saves short instructions to see if it can package them with the
+ next instruction, there may be a short instruction that still needs
+ to be written.
+
+ NOTE: accesses a global, etype.
+ NOTE: invoked by various macros such as md_cleanup: see. */
+
+int
+d10v_cleanup (void)
+{
+ segT seg;
+ subsegT subseg;
+
+ /* If cleanup was invoked because the assembler encountered, e.g., a
+ user label, we write out the pending instruction, if any. If it
+ was invoked because the assembler is outputting a piece of line
+ debugging information, though, we write out the pending
+ instruction only if the --no-gstabs-packing command line switch
+ has been specified. */
+ if (prev_opcode
+ && etype == PACK_UNSPEC
+ && (! outputting_stabs_line_debug || ! flag_allow_gstabs_packing))
+ {
+ seg = now_seg;
+ subseg = now_subseg;
+
+ if (prev_seg)
+ subseg_set (prev_seg, prev_subseg);
+
+ write_1_short (prev_opcode, prev_insn, fixups->next);
+ subseg_set (seg, subseg);
+ prev_opcode = NULL;
+ }
+ return 1;
+}
+
+void
+d10v_frob_label (symbolS *lab)
+{
+ d10v_cleanup ();
+ symbol_set_frag (lab, frag_now);
+ S_SET_VALUE (lab, (valueT) frag_now_fix ());
+ dwarf2_emit_label (lab);
+}
+
+/* Like normal .word, except support @word.
+ Clobbers input_line_pointer, checks end-of-line. */
+
+static void
+d10v_dot_word (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ char *p;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+ if (!strncasecmp (input_line_pointer, "@word", 5))
+ {
+ exp.X_add_number = 0;
+ input_line_pointer += 5;
+
+ p = frag_more (2);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
+ &exp, 0, BFD_RELOC_D10V_18);
+ }
+ else
+ emit_expr (&exp, 2);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+/* Mitsubishi asked that we support some old syntax that apparently
+ had immediate operands starting with '#'. This is in some of their
+ sample code but is not documented (although it appears in some
+ examples in their assembler manual). For now, we'll solve this
+ compatibility problem by simply ignoring any '#' at the beginning
+ of an operand. */
+
+/* Operands that begin with '#' should fall through to here.
+ From expr.c. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ if (*input_line_pointer == '#' && ! do_not_ignore_hash)
+ {
+ input_line_pointer++;
+ expression (expressionP);
+ }
+}
+
+bfd_boolean
+d10v_fix_adjustable (fixS *fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", d10v_dot_word, 2 },
+ { NULL, NULL, 0 }
+};
+
+void
+md_assemble (char *str)
+{
+ /* etype is saved extype. For multi-line instructions. */
+ packing_type extype = PACK_UNSPEC; /* Parallel, etc. */
+ struct d10v_opcode *opcode;
+ unsigned long insn;
+ char *str2;
+
+ if (etype == PACK_UNSPEC)
+ {
+ /* Look for the special multiple instruction separators. */
+ str2 = strstr (str, "||");
+ if (str2)
+ extype = PACK_PARALLEL;
+ else
+ {
+ str2 = strstr (str, "->");
+ if (str2)
+ extype = PACK_LEFT_RIGHT;
+ else
+ {
+ str2 = strstr (str, "<-");
+ if (str2)
+ extype = PACK_RIGHT_LEFT;
+ }
+ }
+
+ /* str2 points to the separator, if there is one. */
+ if (str2)
+ {
+ *str2 = 0;
+
+ /* If two instructions are present and we already have one saved,
+ then first write out the saved one. */
+ d10v_cleanup ();
+
+ /* Assemble first instruction and save it. */
+ prev_insn = do_assemble (str, &prev_opcode);
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ if (prev_insn == (unsigned long) -1)
+ as_fatal (_("can't find previous opcode "));
+ fixups = fixups->next;
+ str = str2 + 2;
+ }
+ }
+
+ insn = do_assemble (str, &opcode);
+ if (insn == (unsigned long) -1)
+ {
+ if (extype != PACK_UNSPEC)
+ etype = extype;
+ else
+ as_bad (_("could not assemble: %s"), str);
+ return;
+ }
+
+ if (etype != PACK_UNSPEC)
+ {
+ extype = etype;
+ etype = PACK_UNSPEC;
+ }
+
+ /* If this is a long instruction, write it and any previous short
+ instruction. */
+ if (opcode->format & LONG_OPCODE)
+ {
+ if (extype != PACK_UNSPEC)
+ as_fatal (_("Unable to mix instructions as specified"));
+ d10v_cleanup ();
+ write_long (insn, fixups);
+ prev_opcode = NULL;
+ return;
+ }
+
+ if (prev_opcode
+ && prev_seg
+ && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
+ d10v_cleanup ();
+
+ if (prev_opcode
+ && (0 == write_2_short (prev_opcode, prev_insn, opcode, insn, extype,
+ fixups)))
+ {
+ /* No instructions saved. */
+ prev_opcode = NULL;
+ }
+ else
+ {
+ if (extype != PACK_UNSPEC)
+ as_fatal (_("Unable to mix instructions as specified"));
+ /* Save last instruction so it may be packed on next pass. */
+ prev_opcode = opcode;
+ prev_insn = insn;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ fixups = fixups->next;
+ }
+}
+
diff --git a/gas/config/tc-d10v.h b/gas/config/tc-d10v.h
new file mode 100644
index 0000000..55dc1cc
--- /dev/null
+++ b/gas/config/tc-d10v.h
@@ -0,0 +1,63 @@
+/* tc-d10v.h -- Header file for tc-d10v.c.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+ Written by Martin Hunt, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_D10V
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_d10v
+
+#define TARGET_FORMAT "elf32-d10v"
+
+/* Call md_pcrel_from_section, not md_pcrel_from. */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+struct fix;
+long md_pcrel_from_section (struct fix *, segT);
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+int d10v_cleanup (void);
+void d10v_frob_label (symbolS *);
+#define md_cleanup() d10v_cleanup ()
+#define md_do_align(a,b,c,d,e) d10v_cleanup ()
+#define tc_frob_label(sym) d10v_frob_label (sym)
+
+#define tc_fix_adjustable(FIX) d10v_fix_adjustable(FIX)
+bfd_boolean d10v_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+#define md_flush_pending_output d10v_cleanup
diff --git a/gas/config/tc-d30v.c b/gas/config/tc-d30v.c
new file mode 100644
index 0000000..9076e41
--- /dev/null
+++ b/gas/config/tc-d30v.c
@@ -0,0 +1,2126 @@
+/* tc-d30v.c -- Assembler code for the Mitsubishi D30V
+ Copyright (C) 1997-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/d30v.h"
+#include "dwarf2dbg.h"
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "";
+const char *md_shortopts = "OnNcC";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+#if HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#define NOP_MULTIPLY 1
+#define NOP_ALL 2
+static int warn_nops = 0;
+static int Optimizing = 0;
+static int warn_register_name_conflicts = 1;
+
+#define FORCE_SHORT 1
+#define FORCE_LONG 2
+
+/* EXEC types. */
+typedef enum _exec_type
+{
+ EXEC_UNKNOWN, /* No order specified. */
+ EXEC_PARALLEL, /* Done in parallel (FM=00). */
+ EXEC_SEQ, /* Sequential (FM=01). */
+ EXEC_REVSEQ /* Reverse sequential (FM=10). */
+} exec_type_enum;
+
+/* Fixups. */
+#define MAX_INSN_FIXUPS 5
+
+struct d30v_fixup
+{
+ expressionS exp;
+ int operand;
+ int pcrel;
+ int size;
+ bfd_reloc_code_real_type reloc;
+};
+
+typedef struct _fixups
+{
+ int fc;
+ struct d30v_fixup fix[MAX_INSN_FIXUPS];
+ struct _fixups *next;
+} Fixups;
+
+static Fixups FixUps[2];
+static Fixups *fixups;
+
+/* Whether current and previous instruction are word multiply insns. */
+static int cur_mul32_p = 0;
+static int prev_mul32_p = 0;
+
+/* The flag_explicitly_parallel is true iff the instruction being assembled
+ has been explicitly written as a parallel short-instruction pair by the
+ human programmer. It is used in parallel_ok () to distinguish between
+ those dangerous parallelizations attempted by the human, which are to be
+ allowed, and those attempted by the assembler, which are not. It is set
+ from md_assemble (). */
+static int flag_explicitly_parallel = 0;
+static int flag_xp_state = 0;
+
+/* Whether current and previous left sub-instruction disables
+ execution of right sub-instruction. */
+static int cur_left_kills_right_p = 0;
+static int prev_left_kills_right_p = 0;
+
+/* The known current alignment of the current section. */
+static int d30v_current_align;
+static segT d30v_current_align_seg;
+
+/* The last seen label in the current section. This is used to auto-align
+ labels preceding instructions. */
+static symbolS *d30v_last_label;
+
+/* Two nops. */
+#define NOP_LEFT ((long long) NOP << 32)
+#define NOP_RIGHT ((long long) NOP)
+#define NOP2 (FM00 | NOP_LEFT | NOP_RIGHT)
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Opcode hash table. */
+static struct hash_control *d30v_hash;
+
+/* Do a binary search of the pre_defined_registers array to see if
+ NAME is a valid regiter name. Return the register number from the
+ array on success, or -1 on failure. */
+
+static int
+reg_name_search (char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = reg_name_cnt () - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, pre_defined_registers[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ {
+ if (symbol_find (name) != NULL)
+ {
+ if (warn_register_name_conflicts)
+ as_warn (_("Register name %s conflicts with symbol of the same name"),
+ name);
+ }
+
+ return pre_defined_registers[middle].value;
+ }
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/* Check the string at input_line_pointer to see if it is a valid
+ register name. */
+
+static int
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char c, *p = input_line_pointer;
+
+ while (*p && *p != '\n' && *p != '\r' && *p != ',' && *p != ' ' && *p != ')')
+ p++;
+
+ c = *p;
+ if (c)
+ *p++ = 0;
+
+ /* Look to see if it's in the register table. */
+ reg_number = reg_name_search (input_line_pointer);
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ /* Temporarily store a pointer to the string here. */
+ expressionP->X_op_symbol = (symbolS *) input_line_pointer;
+ expressionP->X_add_number = reg_number;
+ input_line_pointer = p;
+ return 1;
+ }
+ if (c)
+ *(p - 1) = c;
+ return 0;
+}
+
+static int
+check_range (unsigned long num, int bits, int flags)
+{
+ long min, max;
+
+ /* Don't bother checking 32-bit values. */
+ if (bits == 32)
+ {
+ if (sizeof (unsigned long) * CHAR_BIT == 32)
+ return 0;
+
+ /* We don't record signed or unsigned for 32-bit quantities.
+ Allow either. */
+ min = -((unsigned long) 1 << (bits - 1));
+ max = ((unsigned long) 1 << bits) - 1;
+ return (long) num < min || (long) num > max;
+ }
+
+ if (flags & OPERAND_SHIFT)
+ {
+ /* We know that all shifts are right by three bits. */
+ num >>= 3;
+
+ if (flags & OPERAND_SIGNED)
+ {
+ unsigned long sign_bit = ((unsigned long) -1L >> 4) + 1;
+ num = (num ^ sign_bit) - sign_bit;
+ }
+ }
+
+ if (flags & OPERAND_SIGNED)
+ {
+ max = ((unsigned long) 1 << (bits - 1)) - 1;
+ min = - ((unsigned long) 1 << (bits - 1));
+ return (long) num > max || (long) num < min;
+ }
+ else
+ {
+ max = ((unsigned long) 1 << bits) - 1;
+ return num > (unsigned long) max;
+ }
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\nD30V options:\n\
+-O Make adjacent short instructions parallel if possible.\n\
+-n Warn about all NOPs inserted by the assembler.\n\
+-N Warn about NOPs inserted after word multiplies.\n\
+-c Warn about symbols whoes names match register names.\n\
+-C Opposite of -C. -c is the default.\n"));
+}
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ /* Optimize. Will attempt to parallelize operations. */
+ case 'O':
+ Optimizing = 1;
+ break;
+
+ /* Warn about all NOPS that the assembler inserts. */
+ case 'n':
+ warn_nops = NOP_ALL;
+ break;
+
+ /* Warn about the NOPS that the assembler inserts because of the
+ multiply hazard. */
+ case 'N':
+ warn_nops = NOP_MULTIPLY;
+ break;
+
+ case 'c':
+ warn_register_name_conflicts = 1;
+ break;
+
+ case 'C':
+ warn_register_name_conflicts = 0;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin (void)
+{
+ struct d30v_opcode *opcode;
+ d30v_hash = hash_new ();
+
+ /* Insert opcode names into a hash table. */
+ for (opcode = (struct d30v_opcode *) d30v_opcode_table; opcode->name; opcode++)
+ hash_insert (d30v_hash, opcode->name, (char *) opcode);
+
+ fixups = &FixUps[0];
+ FixUps[0].next = &FixUps[1];
+ FixUps[1].next = &FixUps[0];
+
+ d30v_current_align_seg = now_seg;
+}
+
+/* Remove the postincrement or postdecrement operator ( '+' or '-' )
+ from an expression. */
+
+static int
+postfix (char *p)
+{
+ while (*p != '-' && *p != '+')
+ {
+ if (*p == 0 || *p == '\n' || *p == '\r' || *p == ' ' || *p == ',')
+ break;
+ p++;
+ }
+
+ if (*p == '-')
+ {
+ *p = ' ';
+ return -1;
+ }
+
+ if (*p == '+')
+ {
+ *p = ' ';
+ return 1;
+ }
+
+ return 0;
+}
+
+static bfd_reloc_code_real_type
+get_reloc (const struct d30v_operand *op, int rel_flag)
+{
+ switch (op->bits)
+ {
+ case 6:
+ if (op->flags & OPERAND_SHIFT)
+ return BFD_RELOC_D30V_9_PCREL;
+ else
+ return BFD_RELOC_D30V_6;
+ break;
+ case 12:
+ if (!(op->flags & OPERAND_SHIFT))
+ as_warn (_("unexpected 12-bit reloc type"));
+ if (rel_flag == RELOC_PCREL)
+ return BFD_RELOC_D30V_15_PCREL;
+ else
+ return BFD_RELOC_D30V_15;
+ case 18:
+ if (!(op->flags & OPERAND_SHIFT))
+ as_warn (_("unexpected 18-bit reloc type"));
+ if (rel_flag == RELOC_PCREL)
+ return BFD_RELOC_D30V_21_PCREL;
+ else
+ return BFD_RELOC_D30V_21;
+ case 32:
+ if (rel_flag == RELOC_PCREL)
+ return BFD_RELOC_D30V_32_PCREL;
+ else
+ return BFD_RELOC_D30V_32;
+ default:
+ return 0;
+ }
+}
+
+/* Parse a string of operands and return an array of expressions. */
+
+static int
+get_operands (expressionS exp[], int cmp_hack)
+{
+ char *p = input_line_pointer;
+ int numops = 0;
+ int post = 0;
+
+ if (cmp_hack)
+ {
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = cmp_hack - 1;
+ }
+
+ while (*p)
+ {
+ while (*p == ' ' || *p == '\t' || *p == ',')
+ p++;
+
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ break;
+
+ if (*p == '@')
+ {
+ p++;
+ exp[numops].X_op = O_absent;
+ if (*p == '(')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATPAR;
+ post = postfix (p);
+ }
+ else if (*p == '-')
+ {
+ p++;
+ exp[numops].X_add_number = OPERAND_ATMINUS;
+ }
+ else
+ {
+ exp[numops].X_add_number = OPERAND_ATSIGN;
+ post = postfix (p);
+ }
+ numops++;
+ continue;
+ }
+
+ if (*p == ')')
+ {
+ /* Just skip the trailing paren. */
+ p++;
+ continue;
+ }
+
+ input_line_pointer = p;
+
+ /* Check to see if it might be a register name. */
+ if (!register_name (&exp[numops]))
+ {
+ /* Parse as an expression. */
+ expression (&exp[numops]);
+ }
+
+ if (exp[numops].X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (exp[numops].X_op == O_absent)
+ as_bad (_("missing operand"));
+
+ numops++;
+ p = input_line_pointer;
+
+ switch (post)
+ {
+ case -1:
+ /* Postdecrement mode. */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_MINUS;
+ break;
+ case 1:
+ /* Postincrement mode. */
+ exp[numops].X_op = O_absent;
+ exp[numops++].X_add_number = OPERAND_PLUS;
+ break;
+ }
+ post = 0;
+ }
+
+ exp[numops].X_op = 0;
+
+ return numops;
+}
+
+/* Generate the instruction.
+ It does everything but write the FM bits. */
+
+static long long
+build_insn (struct d30v_insn *opcode, expressionS *opers)
+{
+ int i, bits, shift, flags;
+ unsigned long number, id = 0;
+ long long insn;
+ struct d30v_opcode *op = opcode->op;
+ struct d30v_format *form = opcode->form;
+
+ insn =
+ opcode->ecc << 28 | op->op1 << 25 | op->op2 << 20 | form->modifier << 18;
+
+ for (i = 0; form->operands[i]; i++)
+ {
+ flags = d30v_operand_table[form->operands[i]].flags;
+
+ /* Must be a register or number. */
+ if (!(flags & OPERAND_REG) && !(flags & OPERAND_NUM)
+ && !(flags & OPERAND_NAME) && !(flags & OPERAND_SPECIAL))
+ continue;
+
+ bits = d30v_operand_table[form->operands[i]].bits;
+ if (flags & OPERAND_SHIFT)
+ bits += 3;
+
+ shift = 12 - d30v_operand_table[form->operands[i]].position;
+ if (opers[i].X_op != O_symbol)
+ number = opers[i].X_add_number;
+ else
+ number = 0;
+ if (flags & OPERAND_REG)
+ {
+ /* Check for mvfsys or mvtsys control registers. */
+ if (flags & OPERAND_CONTROL && (number & 0x7f) > MAX_CONTROL_REG)
+ {
+ /* PSWL or PSWH. */
+ id = (number & 0x7f) - MAX_CONTROL_REG;
+ number = 0;
+ }
+ else if (number & OPERAND_FLAG)
+ /* NUMBER is a flag register. */
+ id = 3;
+
+ number &= 0x7F;
+ }
+ else if (flags & OPERAND_SPECIAL)
+ number = id;
+
+ if (opers[i].X_op != O_register && opers[i].X_op != O_constant
+ && !(flags & OPERAND_NAME))
+ {
+ /* Now create a fixup. */
+ if (fixups->fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups->fix[fixups->fc].reloc =
+ get_reloc (d30v_operand_table + form->operands[i], op->reloc_flag);
+ fixups->fix[fixups->fc].size = 4;
+ fixups->fix[fixups->fc].exp = opers[i];
+ fixups->fix[fixups->fc].operand = form->operands[i];
+ if (fixups->fix[fixups->fc].reloc == BFD_RELOC_D30V_9_PCREL)
+ fixups->fix[fixups->fc].pcrel = RELOC_PCREL;
+ else
+ fixups->fix[fixups->fc].pcrel = op->reloc_flag;
+ (fixups->fc)++;
+ }
+
+ /* Truncate to the proper number of bits. */
+ if ((opers[i].X_op == O_constant) && check_range (number, bits, flags))
+ as_bad (_("operand out of range: %ld"), number);
+ if (bits < 31)
+ number &= 0x7FFFFFFF >> (31 - bits);
+ if (flags & OPERAND_SHIFT)
+ number >>= 3;
+ if (bits == 32)
+ {
+ /* It's a LONG instruction. */
+ insn |= ((number & 0xffffffff) >> 26); /* Top 6 bits. */
+ insn <<= 32; /* Shift the first word over. */
+ insn |= ((number & 0x03FC0000) << 2); /* Next 8 bits. */
+ insn |= number & 0x0003FFFF; /* Bottom 18 bits. */
+ }
+ else
+ insn |= number << shift;
+ }
+
+ return insn;
+}
+
+static void
+d30v_number_to_chars (char *buf, /* Return 'nbytes' of chars here. */
+ long long value, /* The value of the bits. */
+ int n) /* Number of bytes in the output. */
+{
+ while (n--)
+ {
+ buf[n] = value & 0xff;
+ value >>= 8;
+ }
+}
+
+/* Write out a long form instruction. */
+
+static void
+write_long (struct d30v_insn *opcode ATTRIBUTE_UNUSED,
+ long long insn,
+ Fixups *fx)
+{
+ int i, where;
+ char *f = frag_more (8);
+
+ dwarf2_emit_insn (8);
+ insn |= FM11;
+ d30v_number_to_chars (f, insn, 8);
+
+ for (i = 0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ fix_new_exp (frag_now, where, fx->fix[i].size, &(fx->fix[i].exp),
+ fx->fix[i].pcrel, fx->fix[i].reloc);
+ }
+ }
+
+ fx->fc = 0;
+}
+
+/* Write out a short form instruction by itself. */
+
+static void
+write_1_short (struct d30v_insn *opcode,
+ long long insn,
+ Fixups *fx,
+ int use_sequential)
+{
+ char *f = frag_more (8);
+ int i, where;
+
+ dwarf2_emit_insn (8);
+ if (warn_nops == NOP_ALL)
+ as_warn (_("%s NOP inserted"), use_sequential ?
+ _("sequential") : _("parallel"));
+
+ /* The other container needs to be NOP. */
+ if (use_sequential)
+ {
+ /* Use a sequential NOP rather than a parallel one,
+ as the current instruction is a FLAG_MUL32 type one
+ and the next instruction is a load. */
+
+ /* According to 4.3.1: for FM=01, sub-instructions performed
+ only by IU cannot be encoded in L-container. */
+ if (opcode->op->unit == IU)
+ /* Right then left. */
+ insn |= FM10 | NOP_LEFT;
+ else
+ /* Left then right. */
+ insn = FM01 | (insn << 32) | NOP_RIGHT;
+ }
+ else
+ {
+ /* According to 4.3.1: for FM=00, sub-instructions performed
+ only by IU cannot be encoded in L-container. */
+ if (opcode->op->unit == IU)
+ /* Right container. */
+ insn |= FM00 | NOP_LEFT;
+ else
+ /* Left container. */
+ insn = FM00 | (insn << 32) | NOP_RIGHT;
+ }
+
+ d30v_number_to_chars (f, insn, 8);
+
+ for (i = 0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = f - frag_now->fr_literal;
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].reloc);
+ }
+ }
+
+ fx->fc = 0;
+}
+
+/* Check 2 instructions and determine if they can be safely
+ executed in parallel. Return 1 if they can be. */
+
+static int
+parallel_ok (struct d30v_insn *op1,
+ unsigned long insn1,
+ struct d30v_insn *op2,
+ unsigned long insn2,
+ exec_type_enum exec_type)
+{
+ int i, j, shift, regno, bits, ecc;
+ unsigned long flags, mask, flags_set1, flags_set2, flags_used1, flags_used2;
+ unsigned long ins, mod_reg[2][3], used_reg[2][3], flag_reg[2];
+ struct d30v_format *f;
+ struct d30v_opcode *op;
+
+ /* Section 4.3: Both instructions must not be IU or MU only. */
+ if ((op1->op->unit == IU && op2->op->unit == IU)
+ || (op1->op->unit == MU && op2->op->unit == MU))
+ return 0;
+
+ /* First instruction must not be a jump to safely optimize, unless this
+ is an explicit parallel operation. */
+ if (exec_type != EXEC_PARALLEL
+ && (op1->op->flags_used & (FLAG_JMP | FLAG_JSR)))
+ return 0;
+
+ /* If one instruction is /TX or /XT and the other is /FX or /XF respectively,
+ then it is safe to allow the two to be done as parallel ops, since only
+ one will ever be executed at a time. */
+ if ((op1->ecc == ECC_TX && op2->ecc == ECC_FX)
+ || (op1->ecc == ECC_FX && op2->ecc == ECC_TX)
+ || (op1->ecc == ECC_XT && op2->ecc == ECC_XF)
+ || (op1->ecc == ECC_XF && op2->ecc == ECC_XT))
+ return 1;
+
+ /* [0] r0-r31
+ [1] r32-r63
+ [2] a0, a1, flag registers. */
+ for (j = 0; j < 2; j++)
+ {
+ if (j == 0)
+ {
+ f = op1->form;
+ op = op1->op;
+ ecc = op1->ecc;
+ ins = insn1;
+ }
+ else
+ {
+ f = op2->form;
+ op = op2->op;
+ ecc = op2->ecc;
+ ins = insn2;
+ }
+
+ flag_reg[j] = 0;
+ mod_reg[j][0] = mod_reg[j][1] = 0;
+ used_reg[j][0] = used_reg[j][1] = 0;
+
+ if (flag_explicitly_parallel)
+ {
+ /* For human specified parallel instructions we have been asked
+ to ignore the possibility that both instructions could modify
+ bits in the PSW, so we initialise the mod & used arrays to 0.
+ We have been asked, however, to refuse to allow parallel
+ instructions which explicitly set the same flag register,
+ eg "cmpne f0,r1,0x10 || cmpeq f0, r5, 0x2", so further on we test
+ for the use of a flag register and set a bit in the mod or used
+ array appropriately. */
+ mod_reg[j][2] = 0;
+ used_reg[j][2] = 0;
+ }
+ else
+ {
+ mod_reg[j][2] = (op->flags_set & FLAG_ALL);
+ used_reg[j][2] = (op->flags_used & FLAG_ALL);
+ }
+
+ /* BSR/JSR always sets R62. */
+ if (op->flags_used & FLAG_JSR)
+ mod_reg[j][1] = (1L << (62 - 32));
+
+ /* Conditional execution affects the flags_used. */
+ switch (ecc)
+ {
+ case ECC_TX:
+ case ECC_FX:
+ used_reg[j][2] |= flag_reg[j] = FLAG_0;
+ break;
+
+ case ECC_XT:
+ case ECC_XF:
+ used_reg[j][2] |= flag_reg[j] = FLAG_1;
+ break;
+
+ case ECC_TT:
+ case ECC_TF:
+ used_reg[j][2] |= flag_reg[j] = (FLAG_0 | FLAG_1);
+ break;
+ }
+
+ for (i = 0; f->operands[i]; i++)
+ {
+ flags = d30v_operand_table[f->operands[i]].flags;
+ shift = 12 - d30v_operand_table[f->operands[i]].position;
+ bits = d30v_operand_table[f->operands[i]].bits;
+ if (bits == 32)
+ mask = 0xffffffff;
+ else
+ mask = 0x7FFFFFFF >> (31 - bits);
+
+ if ((flags & OPERAND_PLUS) || (flags & OPERAND_MINUS))
+ {
+ /* This is a post-increment or post-decrement.
+ The previous register needs to be marked as modified. */
+ shift = 12 - d30v_operand_table[f->operands[i - 1]].position;
+ regno = (ins >> shift) & 0x3f;
+ if (regno >= 32)
+ mod_reg[j][1] |= 1L << (regno - 32);
+ else
+ mod_reg[j][0] |= 1L << regno;
+ }
+ else if (flags & OPERAND_REG)
+ {
+ regno = (ins >> shift) & mask;
+ /* The memory write functions don't have a destination
+ register. */
+ if ((flags & OPERAND_DEST) && !(op->flags_set & FLAG_MEM))
+ {
+ /* MODIFIED registers and flags. */
+ if (flags & OPERAND_ACC)
+ {
+ if (regno == 0)
+ mod_reg[j][2] |= FLAG_A0;
+ else if (regno == 1)
+ mod_reg[j][2] |= FLAG_A1;
+ else
+ abort ();
+ }
+ else if (flags & OPERAND_FLAG)
+ mod_reg[j][2] |= 1L << regno;
+ else if (!(flags & OPERAND_CONTROL))
+ {
+ int r, z;
+
+ /* Need to check if there are two destination
+ registers, for example ld2w. */
+ if (flags & OPERAND_2REG)
+ z = 1;
+ else
+ z = 0;
+
+ for (r = regno; r <= regno + z; r++)
+ {
+ if (r >= 32)
+ mod_reg[j][1] |= 1L << (r - 32);
+ else
+ mod_reg[j][0] |= 1L << r;
+ }
+ }
+ }
+ else
+ {
+ /* USED, but not modified registers and flags. */
+ if (flags & OPERAND_ACC)
+ {
+ if (regno == 0)
+ used_reg[j][2] |= FLAG_A0;
+ else if (regno == 1)
+ used_reg[j][2] |= FLAG_A1;
+ else
+ abort ();
+ }
+ else if (flags & OPERAND_FLAG)
+ used_reg[j][2] |= 1L << regno;
+ else if (!(flags & OPERAND_CONTROL))
+ {
+ int r, z;
+
+ /* Need to check if there are two source
+ registers, for example st2w. */
+ if (flags & OPERAND_2REG)
+ z = 1;
+ else
+ z = 0;
+
+ for (r = regno; r <= regno + z; r++)
+ {
+ if (r >= 32)
+ used_reg[j][1] |= 1L << (r - 32);
+ else
+ used_reg[j][0] |= 1L << r;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ flags_set1 = op1->op->flags_set;
+ flags_set2 = op2->op->flags_set;
+ flags_used1 = op1->op->flags_used;
+ flags_used2 = op2->op->flags_used;
+
+ /* Check for illegal combinations with ADDppp/SUBppp. */
+ if (((flags_set1 & FLAG_NOT_WITH_ADDSUBppp) != 0
+ && (flags_used2 & FLAG_ADDSUBppp) != 0)
+ || ((flags_set2 & FLAG_NOT_WITH_ADDSUBppp) != 0
+ && (flags_used1 & FLAG_ADDSUBppp) != 0))
+ return 0;
+
+ /* Load instruction combined with half-word multiply is illegal. */
+ if (((flags_used1 & FLAG_MEM) != 0 && (flags_used2 & FLAG_MUL16))
+ || ((flags_used2 & FLAG_MEM) != 0 && (flags_used1 & FLAG_MUL16)))
+ return 0;
+
+ /* Specifically allow add || add by removing carry, overflow bits dependency.
+ This is safe, even if an addc follows since the IU takes the argument in
+ the right container, and it writes its results last.
+ However, don't paralellize add followed by addc or sub followed by
+ subb. */
+ if (mod_reg[0][2] == FLAG_CVVA && mod_reg[1][2] == FLAG_CVVA
+ && (used_reg[0][2] & ~flag_reg[0]) == 0
+ && (used_reg[1][2] & ~flag_reg[1]) == 0
+ && op1->op->unit == EITHER && op2->op->unit == EITHER)
+ {
+ mod_reg[0][2] = mod_reg[1][2] = 0;
+ }
+
+ for (j = 0; j < 3; j++)
+ {
+ /* If the second instruction depends on the first, we obviously
+ cannot parallelize. Note, the mod flag implies use, so
+ check that as well. */
+ /* If flag_explicitly_parallel is set, then the case of the
+ second instruction using a register the first instruction
+ modifies is assumed to be okay; we trust the human. We
+ don't trust the human if both instructions modify the same
+ register but we do trust the human if they modify the same
+ flags. */
+ /* We have now been requested not to trust the human if the
+ instructions modify the same flag registers either. */
+ if (flag_explicitly_parallel)
+ {
+ if ((mod_reg[0][j] & mod_reg[1][j]) != 0)
+ return 0;
+ }
+ else
+ if ((mod_reg[0][j] & (mod_reg[1][j] | used_reg[1][j])) != 0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Write out a short form instruction if possible.
+ Return number of instructions not written out. */
+
+static int
+write_2_short (struct d30v_insn *opcode1,
+ long long insn1,
+ struct d30v_insn *opcode2,
+ long long insn2,
+ exec_type_enum exec_type,
+ Fixups *fx)
+{
+ long long insn = NOP2;
+ char *f;
+ int i, j, where;
+
+ if (exec_type == EXEC_SEQ
+ && (opcode1->op->flags_used & (FLAG_JMP | FLAG_JSR))
+ && ((opcode1->op->flags_used & FLAG_DELAY) == 0)
+ && ((opcode1->ecc == ECC_AL) || ! Optimizing))
+ {
+ /* Unconditional, non-delayed branches kill instructions in
+ the right bin. Conditional branches don't always but if
+ we are not optimizing, then we have been asked to produce
+ an error about such constructs. For the purposes of this
+ test, subroutine calls are considered to be branches. */
+ write_1_short (opcode1, insn1, fx->next, FALSE);
+ return 1;
+ }
+
+ /* Note: we do not have to worry about subroutine calls occurring
+ in the right hand container. The return address is always
+ aligned to the next 64 bit boundary, be that 64 or 32 bit away. */
+ switch (exec_type)
+ {
+ case EXEC_UNKNOWN: /* Order not specified. */
+ if (Optimizing
+ && parallel_ok (opcode1, insn1, opcode2, insn2, exec_type)
+ && ! ( (opcode1->op->unit == EITHER_BUT_PREFER_MU
+ || opcode1->op->unit == MU)
+ &&
+ ( opcode2->op->unit == EITHER_BUT_PREFER_MU
+ || opcode2->op->unit == MU)))
+ {
+ /* Parallel. */
+ exec_type = EXEC_PARALLEL;
+
+ if (opcode1->op->unit == IU
+ || opcode2->op->unit == MU
+ || opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ insn = FM00 | (insn2 << 32) | insn1;
+ else
+ {
+ insn = FM00 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ }
+ }
+ else if ((opcode1->op->flags_used & (FLAG_JMP | FLAG_JSR)
+ && ((opcode1->op->flags_used & FLAG_DELAY) == 0))
+ || opcode1->op->flags_used & FLAG_RP)
+ {
+ /* We must emit (non-delayed) branch type instructions
+ on their own with nothing in the right container. */
+ /* We must treat repeat instructions likewise, since the
+ following instruction has to be separate from the repeat
+ in order to be repeated. */
+ write_1_short (opcode1, insn1, fx->next, FALSE);
+ return 1;
+ }
+ else if (prev_left_kills_right_p)
+ {
+ /* The left instruction kils the right slot, so we
+ must leave it empty. */
+ write_1_short (opcode1, insn1, fx->next, FALSE);
+ return 1;
+ }
+ else if (opcode1->op->unit == IU)
+ {
+ if (opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ {
+ /* Case 103810 is a request from Mitsubishi that opcodes
+ with EITHER_BUT_PREFER_MU should not be executed in
+ reverse sequential order. */
+ write_1_short (opcode1, insn1, fx->next, FALSE);
+ return 1;
+ }
+
+ /* Reverse sequential. */
+ insn = FM10 | (insn2 << 32) | insn1;
+ exec_type = EXEC_REVSEQ;
+ }
+ else
+ {
+ /* Sequential. */
+ insn = FM01 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ exec_type = EXEC_SEQ;
+ }
+ break;
+
+ case EXEC_PARALLEL: /* Parallel. */
+ flag_explicitly_parallel = flag_xp_state;
+ if (! parallel_ok (opcode1, insn1, opcode2, insn2, exec_type))
+ as_bad (_("Instructions may not be executed in parallel"));
+ else if (opcode1->op->unit == IU)
+ {
+ if (opcode2->op->unit == IU)
+ as_bad (_("Two IU instructions may not be executed in parallel"));
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 32) | insn1;
+ }
+ else if (opcode2->op->unit == MU)
+ {
+ if (opcode1->op->unit == MU)
+ as_bad (_("Two MU instructions may not be executed in parallel"));
+ else if (opcode1->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU may not work"), opcode1->op->name);
+ as_warn (_("Swapping instruction order"));
+ insn = FM00 | (insn2 << 32) | insn1;
+ }
+ else
+ {
+ if (opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU may not work in parallel execution"),
+ opcode2->op->name);
+
+ insn = FM00 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ }
+ flag_explicitly_parallel = 0;
+ break;
+
+ case EXEC_SEQ: /* Sequential. */
+ if (opcode1->op->unit == IU)
+ as_bad (_("IU instruction may not be in the left container"));
+ if (prev_left_kills_right_p)
+ as_bad (_("special left instruction `%s' kills instruction "
+ "`%s' in right container"),
+ opcode1->op->name, opcode2->op->name);
+ insn = FM01 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ break;
+
+ case EXEC_REVSEQ: /* Reverse sequential. */
+ if (opcode2->op->unit == MU)
+ as_bad (_("MU instruction may not be in the right container"));
+ if (opcode1->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in reverse serial with %s may not work"),
+ opcode1->op->name, opcode2->op->name);
+ else if (opcode2->op->unit == EITHER_BUT_PREFER_MU)
+ as_warn (_("Executing %s in IU in reverse serial may not work"),
+ opcode2->op->name);
+ insn = FM10 | (insn1 << 32) | insn2;
+ fx = fx->next;
+ break;
+
+ default:
+ as_fatal (_("unknown execution type passed to write_2_short()"));
+ }
+
+ f = frag_more (8);
+ dwarf2_emit_insn (8);
+ d30v_number_to_chars (f, insn, 8);
+
+ /* If the previous instruction was a 32-bit multiply but it is put into a
+ parallel container, mark the current instruction as being a 32-bit
+ multiply. */
+ if (prev_mul32_p && exec_type == EXEC_PARALLEL)
+ cur_mul32_p = 1;
+
+ for (j = 0; j < 2; j++)
+ {
+ for (i = 0; i < fx->fc; i++)
+ {
+ if (fx->fix[i].reloc)
+ {
+ where = (f - frag_now->fr_literal) + 4 * j;
+
+ fix_new_exp (frag_now,
+ where,
+ fx->fix[i].size,
+ &(fx->fix[i].exp),
+ fx->fix[i].pcrel,
+ fx->fix[i].reloc);
+ }
+ }
+
+ fx->fc = 0;
+ fx = fx->next;
+ }
+
+ return 0;
+}
+
+/* Get a pointer to an entry in the format table.
+ It must look at all formats for an opcode and use the operands
+ to choose the correct one. Return NULL on error. */
+
+static struct d30v_format *
+find_format (struct d30v_opcode *opcode,
+ expressionS myops[],
+ int fsize,
+ int cmp_hack)
+{
+ int match, opcode_index, i = 0, j, k;
+ struct d30v_format *fm;
+
+ if (opcode == NULL)
+ return NULL;
+
+ /* Get all the operands and save them as expressions. */
+ get_operands (myops, cmp_hack);
+
+ while ((opcode_index = opcode->format[i++]) != 0)
+ {
+ if (fsize == FORCE_SHORT && opcode_index >= LONG)
+ continue;
+
+ if (fsize == FORCE_LONG && opcode_index < LONG)
+ continue;
+
+ fm = (struct d30v_format *) &d30v_format_table[opcode_index];
+ k = opcode_index;
+ while (fm->form == opcode_index)
+ {
+ match = 1;
+ /* Now check the operands for compatibility. */
+ for (j = 0; match && fm->operands[j]; j++)
+ {
+ int flags = d30v_operand_table[fm->operands[j]].flags;
+ int bits = d30v_operand_table[fm->operands[j]].bits;
+ int X_op = myops[j].X_op;
+ int num = myops[j].X_add_number;
+
+ if (flags & OPERAND_SPECIAL)
+ break;
+ else if (X_op == O_illegal)
+ match = 0;
+ else if (flags & OPERAND_REG)
+ {
+ if (X_op != O_register
+ || ((flags & OPERAND_ACC) && !(num & OPERAND_ACC))
+ || (!(flags & OPERAND_ACC) && (num & OPERAND_ACC))
+ || ((flags & OPERAND_FLAG) && !(num & OPERAND_FLAG))
+ || (!(flags & (OPERAND_FLAG | OPERAND_CONTROL)) && (num & OPERAND_FLAG))
+ || ((flags & OPERAND_CONTROL)
+ && !(num & (OPERAND_CONTROL | OPERAND_FLAG))))
+ match = 0;
+ }
+ else if (((flags & OPERAND_MINUS)
+ && (X_op != O_absent || num != OPERAND_MINUS))
+ || ((flags & OPERAND_PLUS)
+ && (X_op != O_absent || num != OPERAND_PLUS))
+ || ((flags & OPERAND_ATMINUS)
+ && (X_op != O_absent || num != OPERAND_ATMINUS))
+ || ((flags & OPERAND_ATPAR)
+ && (X_op != O_absent || num != OPERAND_ATPAR))
+ || ((flags & OPERAND_ATSIGN)
+ && (X_op != O_absent || num != OPERAND_ATSIGN)))
+ match = 0;
+ else if (flags & OPERAND_NUM)
+ {
+ /* A number can be a constant or symbol expression. */
+
+ /* If we have found a register name, but that name
+ also matches a symbol, then re-parse the name as
+ an expression. */
+ if (X_op == O_register
+ && symbol_find ((char *) myops[j].X_op_symbol))
+ {
+ input_line_pointer = (char *) myops[j].X_op_symbol;
+ expression (&myops[j]);
+ }
+
+ /* Turn an expression into a symbol for later resolution. */
+ if (X_op != O_absent && X_op != O_constant
+ && X_op != O_symbol && X_op != O_register
+ && X_op != O_big)
+ {
+ symbolS *sym = make_expr_symbol (&myops[j]);
+ myops[j].X_op = X_op = O_symbol;
+ myops[j].X_add_symbol = sym;
+ myops[j].X_add_number = num = 0;
+ }
+
+ if (fm->form >= LONG)
+ {
+ /* If we're testing for a LONG format, either fits. */
+ if (X_op != O_constant && X_op != O_symbol)
+ match = 0;
+ }
+ else if (fm->form < LONG
+ && ((fsize == FORCE_SHORT && X_op == O_symbol)
+ || (fm->form == SHORT_D2 && j == 0)))
+ match = 1;
+
+ /* This is the tricky part. Will the constant or symbol
+ fit into the space in the current format? */
+ else if (X_op == O_constant)
+ {
+ if (check_range (num, bits, flags))
+ match = 0;
+ }
+ else if (X_op == O_symbol
+ && S_IS_DEFINED (myops[j].X_add_symbol)
+ && S_GET_SEGMENT (myops[j].X_add_symbol) == now_seg
+ && opcode->reloc_flag == RELOC_PCREL)
+ {
+ /* If the symbol is defined, see if the value will fit
+ into the form we're considering. */
+ fragS *f;
+ long value;
+
+ /* Calculate the current address by running through the
+ previous frags and adding our current offset. */
+ value = 0;
+ for (f = frchain_now->frch_root; f; f = f->fr_next)
+ value += f->fr_fix + f->fr_offset;
+ value = (S_GET_VALUE (myops[j].X_add_symbol) - value
+ - (obstack_next_free (&frchain_now->frch_obstack)
+ - frag_now->fr_literal));
+ if (check_range (value, bits, flags))
+ match = 0;
+ }
+ else
+ match = 0;
+ }
+ }
+ /* We're only done if the operands matched so far AND there
+ are no more to check. */
+ if (match && myops[j].X_op == 0)
+ {
+ /* Final check - issue a warning if an odd numbered register
+ is used as the first register in an instruction that reads
+ or writes 2 registers. */
+
+ for (j = 0; fm->operands[j]; j++)
+ if (myops[j].X_op == O_register
+ && (myops[j].X_add_number & 1)
+ && (d30v_operand_table[fm->operands[j]].flags & OPERAND_2REG))
+ as_warn (_("Odd numbered register used as target of multi-register instruction"));
+
+ return fm;
+ }
+ fm = (struct d30v_format *) &d30v_format_table[++k];
+ }
+ }
+ return NULL;
+}
+
+/* Assemble a single instruction and return an opcode.
+ Return -1 (an invalid opcode) on error. */
+
+#define NAME_BUF_LEN 20
+
+static long long
+do_assemble (char *str,
+ struct d30v_insn *opcode,
+ int shortp,
+ int is_parallel)
+{
+ char *op_start;
+ char *save;
+ char *op_end;
+ char name[NAME_BUF_LEN];
+ int cmp_hack;
+ int nlen = 0;
+ int fsize = (shortp ? FORCE_SHORT : 0);
+ expressionS myops[6];
+ long long insn;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the opcode end. */
+ for (op_start = op_end = str;
+ *op_end
+ && nlen < (NAME_BUF_LEN - 1)
+ && *op_end != '/'
+ && !is_end_of_line[(unsigned char) *op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = TOLOWER (op_start[nlen]);
+ nlen++;
+ }
+
+ if (nlen == 0)
+ return -1;
+
+ name[nlen] = 0;
+
+ /* If there is an execution condition code, handle it. */
+ if (*op_end == '/')
+ {
+ int i = 0;
+ while ((i < ECC_MAX) && strncasecmp (d30v_ecc_names[i], op_end + 1, 2))
+ i++;
+
+ if (i == ECC_MAX)
+ {
+ char tmp[4];
+ strncpy (tmp, op_end + 1, 2);
+ tmp[2] = 0;
+ as_bad (_("unknown condition code: %s"), tmp);
+ return -1;
+ }
+ opcode->ecc = i;
+ op_end += 3;
+ }
+ else
+ opcode->ecc = ECC_AL;
+
+ /* CMP and CMPU change their name based on condition codes. */
+ if (!strncmp (name, "cmp", 3))
+ {
+ int p, i;
+ char **d30v_str = (char **) d30v_cc_names;
+
+ if (name[3] == 'u')
+ p = 4;
+ else
+ p = 3;
+
+ for (i = 1; *d30v_str && strncmp (*d30v_str, &name[p], 2); i++, d30v_str++)
+ ;
+
+ /* cmpu only supports some condition codes. */
+ if (p == 4)
+ {
+ if (i < 3 || i > 6)
+ {
+ name[p + 2] = 0;
+ as_bad (_("cmpu doesn't support condition code %s"), &name[p]);
+ }
+ }
+
+ if (!*d30v_str)
+ {
+ name[p + 2] = 0;
+ as_bad (_("unknown condition code: %s"), &name[p]);
+ }
+
+ cmp_hack = i;
+ name[p] = 0;
+ }
+ else
+ cmp_hack = 0;
+
+ /* Need to look for .s or .l. */
+ if (name[nlen - 2] == '.')
+ {
+ switch (name[nlen - 1])
+ {
+ case 's':
+ fsize = FORCE_SHORT;
+ break;
+ case 'l':
+ fsize = FORCE_LONG;
+ break;
+ }
+ name[nlen - 2] = 0;
+ }
+
+ /* Find the first opcode with the proper name. */
+ opcode->op = (struct d30v_opcode *) hash_find (d30v_hash, name);
+ if (opcode->op == NULL)
+ {
+ as_bad (_("unknown opcode: %s"), name);
+ return -1;
+ }
+
+ save = input_line_pointer;
+ input_line_pointer = op_end;
+ while (!(opcode->form = find_format (opcode->op, myops, fsize, cmp_hack)))
+ {
+ opcode->op++;
+ if (opcode->op->name == NULL || strcmp (opcode->op->name, name))
+ {
+ as_bad (_("operands for opcode `%s' do not match any valid format"),
+ name);
+ return -1;
+ }
+ }
+ input_line_pointer = save;
+
+ insn = build_insn (opcode, myops);
+
+ /* Propagate multiply status. */
+ if (insn != -1)
+ {
+ if (is_parallel && prev_mul32_p)
+ cur_mul32_p = 1;
+ else
+ {
+ prev_mul32_p = cur_mul32_p;
+ cur_mul32_p = (opcode->op->flags_used & FLAG_MUL32) != 0;
+ }
+ }
+
+ /* Propagate left_kills_right status. */
+ if (insn != -1)
+ {
+ prev_left_kills_right_p = cur_left_kills_right_p;
+
+ if (opcode->op->flags_set & FLAG_LKR)
+ {
+ cur_left_kills_right_p = 1;
+
+ if (strcmp (opcode->op->name, "mvtsys") == 0)
+ {
+ /* Left kills right for only mvtsys only for
+ PSW/PSWH/PSWL/flags target. */
+ if ((myops[0].X_op == O_register) &&
+ ((myops[0].X_add_number == OPERAND_CONTROL) || /* psw */
+ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+2) || /* pswh */
+ (myops[0].X_add_number == OPERAND_CONTROL+MAX_CONTROL_REG+1) || /* pswl */
+ (myops[0].X_add_number == OPERAND_FLAG+0) || /* f0 */
+ (myops[0].X_add_number == OPERAND_FLAG+1) || /* f1 */
+ (myops[0].X_add_number == OPERAND_FLAG+2) || /* f2 */
+ (myops[0].X_add_number == OPERAND_FLAG+3) || /* f3 */
+ (myops[0].X_add_number == OPERAND_FLAG+4) || /* f4 */
+ (myops[0].X_add_number == OPERAND_FLAG+5) || /* f5 */
+ (myops[0].X_add_number == OPERAND_FLAG+6) || /* f6 */
+ (myops[0].X_add_number == OPERAND_FLAG+7))) /* f7 */
+ {
+ cur_left_kills_right_p = 1;
+ }
+ else
+ {
+ /* Other mvtsys target registers don't kill right
+ instruction. */
+ cur_left_kills_right_p = 0;
+ }
+ } /* mvtsys */
+ }
+ else
+ cur_left_kills_right_p = 0;
+ }
+
+ return insn;
+}
+
+/* Called internally to handle all alignment needs. This takes care
+ of eliding calls to frag_align if'n the cached current alignment
+ says we've already got it, as well as taking care of the auto-aligning
+ labels wrt code. */
+
+static void
+d30v_align (int n, char *pfill, symbolS *label)
+{
+ /* The front end is prone to changing segments out from under us
+ temporarily when -g is in effect. */
+ int switched_seg_p = (d30v_current_align_seg != now_seg);
+
+ /* Do not assume that if 'd30v_current_align >= n' and
+ '! switched_seg_p' that it is safe to avoid performing
+ this alignment request. The alignment of the current frag
+ can be changed under our feet, for example by a .ascii
+ directive in the source code. cf testsuite/gas/d30v/reloc.s */
+ d30v_cleanup (FALSE);
+
+ if (pfill == NULL)
+ {
+ if (n > 2
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ {
+ static char const nop[4] = { 0x00, 0xf0, 0x00, 0x00 };
+
+ /* First, make sure we're on a four-byte boundary, in case
+ someone has been putting .byte values the text section. */
+ if (d30v_current_align < 2 || switched_seg_p)
+ frag_align (2, 0, 0);
+ frag_align_pattern (n, nop, sizeof nop, 0);
+ }
+ else
+ frag_align (n, 0, 0);
+ }
+ else
+ frag_align (n, *pfill, 0);
+
+ if (!switched_seg_p)
+ d30v_current_align = n;
+
+ if (label != NULL)
+ {
+ symbolS *sym;
+ int label_seen = FALSE;
+ struct frag *old_frag;
+ valueT old_value;
+ valueT new_value;
+
+ gas_assert (S_GET_SEGMENT (label) == now_seg);
+
+ old_frag = symbol_get_frag (label);
+ old_value = S_GET_VALUE (label);
+ new_value = (valueT) frag_now_fix ();
+
+ /* It is possible to have more than one label at a particular
+ address, especially if debugging is enabled, so we must
+ take care to adjust all the labels at this address in this
+ fragment. To save time we search from the end of the symbol
+ list, backwards, since the symbols we are interested in are
+ almost certainly the ones that were most recently added.
+ Also to save time we stop searching once we have seen at least
+ one matching label, and we encounter a label that is no longer
+ in the target fragment. Note, this search is guaranteed to
+ find at least one match when sym == label, so no special case
+ code is necessary. */
+ for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym))
+ {
+ if (symbol_get_frag (sym) == old_frag
+ && S_GET_VALUE (sym) == old_value)
+ {
+ label_seen = TRUE;
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, new_value);
+ }
+ else if (label_seen && symbol_get_frag (sym) != old_frag)
+ break;
+ }
+ }
+
+ record_alignment (now_seg, n);
+}
+
+/* This is the main entry point for the machine-dependent assembler.
+ STR points to a machine-dependent instruction. This function is
+ supposed to emit the frags/bytes it assembles to. For the D30V, it
+ mostly handles the special VLIW parsing and packing and leaves the
+ difficult stuff to do_assemble (). */
+
+static long long prev_insn = -1;
+static struct d30v_insn prev_opcode;
+static subsegT prev_subseg;
+static segT prev_seg = 0;
+
+void
+md_assemble (char *str)
+{
+ struct d30v_insn opcode;
+ long long insn;
+ /* Execution type; parallel, etc. */
+ exec_type_enum extype = EXEC_UNKNOWN;
+ /* Saved extype. Used for multiline instructions. */
+ static exec_type_enum etype = EXEC_UNKNOWN;
+ char *str2;
+
+ if ((prev_insn != -1) && prev_seg
+ && ((prev_seg != now_seg) || (prev_subseg != now_subseg)))
+ d30v_cleanup (FALSE);
+
+ if (d30v_current_align < 3)
+ d30v_align (3, NULL, d30v_last_label);
+ else if (d30v_current_align > 3)
+ d30v_current_align = 3;
+ d30v_last_label = NULL;
+
+ flag_explicitly_parallel = 0;
+ flag_xp_state = 0;
+ if (etype == EXEC_UNKNOWN)
+ {
+ /* Look for the special multiple instruction separators. */
+ str2 = strstr (str, "||");
+ if (str2)
+ {
+ extype = EXEC_PARALLEL;
+ flag_xp_state = 1;
+ }
+ else
+ {
+ str2 = strstr (str, "->");
+ if (str2)
+ extype = EXEC_SEQ;
+ else
+ {
+ str2 = strstr (str, "<-");
+ if (str2)
+ extype = EXEC_REVSEQ;
+ }
+ }
+
+ /* STR2 points to the separator, if one. */
+ if (str2)
+ {
+ *str2 = 0;
+
+ /* If two instructions are present and we already have one saved,
+ then first write it out. */
+ d30v_cleanup (FALSE);
+
+ /* Assemble first instruction and save it. */
+ prev_insn = do_assemble (str, &prev_opcode, 1, 0);
+ if (prev_insn == -1)
+ as_bad (_("Cannot assemble instruction"));
+ if (prev_opcode.form != NULL && prev_opcode.form->form >= LONG)
+ as_bad (_("First opcode is long. Unable to mix instructions as specified."));
+ fixups = fixups->next;
+ str = str2 + 2;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ }
+ }
+
+ insn = do_assemble (str, &opcode,
+ (extype != EXEC_UNKNOWN || etype != EXEC_UNKNOWN),
+ extype == EXEC_PARALLEL);
+ if (insn == -1)
+ {
+ if (extype != EXEC_UNKNOWN)
+ etype = extype;
+ as_bad (_("Cannot assemble instruction"));
+ return;
+ }
+
+ if (etype != EXEC_UNKNOWN)
+ {
+ extype = etype;
+ etype = EXEC_UNKNOWN;
+ }
+
+ /* Word multiply instructions must not be followed by either a load or a
+ 16-bit multiply instruction in the next cycle. */
+ if ( (extype != EXEC_REVSEQ)
+ && prev_mul32_p
+ && (opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16)))
+ {
+ /* However, load and multiply should able to be combined in a parallel
+ operation, so check for that first. */
+ if (prev_insn != -1
+ && (opcode.op->flags_used & FLAG_MEM)
+ && opcode.form->form < LONG
+ && (extype == EXEC_PARALLEL || (Optimizing && extype == EXEC_UNKNOWN))
+ && parallel_ok (&prev_opcode, (long) prev_insn,
+ &opcode, (long) insn, extype)
+ && write_2_short (&prev_opcode, (long) prev_insn,
+ &opcode, (long) insn, extype, fixups) == 0)
+ {
+ /* No instructions saved. */
+ prev_insn = -1;
+ return;
+ }
+ else
+ {
+ /* Can't parallelize, flush previous instruction and emit a
+ word of NOPS, unless the previous instruction is a NOP,
+ in which case just flush it, as this will generate a word
+ of NOPs for us. */
+
+ if (prev_insn != -1 && (strcmp (prev_opcode.op->name, "nop") == 0))
+ d30v_cleanup (FALSE);
+ else
+ {
+ char *f;
+
+ if (prev_insn != -1)
+ d30v_cleanup (TRUE);
+ else
+ {
+ f = frag_more (8);
+ dwarf2_emit_insn (8);
+ d30v_number_to_chars (f, NOP2, 8);
+
+ if (warn_nops == NOP_ALL || warn_nops == NOP_MULTIPLY)
+ {
+ if (opcode.op->flags_used & FLAG_MEM)
+ as_warn (_("word of NOPs added between word multiply and load"));
+ else
+ as_warn (_("word of NOPs added between word multiply and 16-bit multiply"));
+ }
+ }
+ }
+
+ extype = EXEC_UNKNOWN;
+ }
+ }
+ else if ( (extype == EXEC_REVSEQ)
+ && cur_mul32_p
+ && (prev_opcode.op->flags_used & (FLAG_MEM | FLAG_MUL16)))
+ {
+ /* Can't parallelize, flush current instruction and add a
+ sequential NOP. */
+ write_1_short (&opcode, (long) insn, fixups->next->next, TRUE);
+
+ /* Make the previous instruction the current one. */
+ extype = EXEC_UNKNOWN;
+ insn = prev_insn;
+ now_seg = prev_seg;
+ now_subseg = prev_subseg;
+ prev_insn = -1;
+ cur_mul32_p = prev_mul32_p;
+ prev_mul32_p = 0;
+ memcpy (&opcode, &prev_opcode, sizeof (prev_opcode));
+ }
+
+ /* If this is a long instruction, write it and any previous short
+ instruction. */
+ if (opcode.form->form >= LONG)
+ {
+ if (extype != EXEC_UNKNOWN)
+ as_bad (_("Instruction uses long version, so it cannot be mixed as specified"));
+ d30v_cleanup (FALSE);
+ write_long (&opcode, insn, fixups);
+ prev_insn = -1;
+ }
+ else if ((prev_insn != -1)
+ && (write_2_short
+ (&prev_opcode, (long) prev_insn, &opcode,
+ (long) insn, extype, fixups) == 0))
+ {
+ /* No instructions saved. */
+ prev_insn = -1;
+ }
+ else
+ {
+ if (extype != EXEC_UNKNOWN)
+ as_bad (_("Unable to mix instructions as specified"));
+
+ /* Save off last instruction so it may be packed on next pass. */
+ memcpy (&prev_opcode, &opcode, sizeof (prev_opcode));
+ prev_insn = insn;
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+ fixups = fixups->next;
+ prev_mul32_p = cur_mul32_p;
+ }
+}
+
+/* If while processing a fixup, a reloc really needs to be created,
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ reloc->addend = 0;
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+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)))
+ return 0;
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+/* Called after the assembler has finished parsing the input file or
+ after a label is defined. Because the D30V assembler sometimes
+ saves short instructions to see if it can package them with the
+ next instruction, there may be a short instruction that still needs
+ written. */
+
+int
+d30v_cleanup (int use_sequential)
+{
+ segT seg;
+ subsegT subseg;
+
+ if (prev_insn != -1)
+ {
+ seg = now_seg;
+ subseg = now_subseg;
+ subseg_set (prev_seg, prev_subseg);
+ write_1_short (&prev_opcode, (long) prev_insn, fixups->next,
+ use_sequential);
+ subseg_set (seg, subseg);
+ prev_insn = -1;
+ if (use_sequential)
+ prev_mul32_p = FALSE;
+ }
+
+ return 1;
+}
+
+/* This function is called at the start of every line. It checks to
+ see if the first character is a '.', which indicates the start of a
+ pseudo-op. If it is, then write out any unwritten instructions. */
+
+void
+d30v_start_line (void)
+{
+ char *c = input_line_pointer;
+
+ while (ISSPACE (*c))
+ c++;
+
+ if (*c == '.')
+ d30v_cleanup (FALSE);
+}
+
+static void
+check_size (long value, int bits, char *file, int line)
+{
+ int tmp, max;
+
+ if (value < 0)
+ tmp = ~value;
+ else
+ tmp = value;
+
+ max = (1 << (bits - 1)) - 1;
+
+ if (tmp > max)
+ as_bad_where (file, line, _("value too large to fit in %d bits"), bits);
+}
+
+/* d30v_frob_label() is called when after a label is recognized. */
+
+void
+d30v_frob_label (symbolS *lab)
+{
+ /* Emit any pending instructions. */
+ d30v_cleanup (FALSE);
+
+ /* Update the label's address with the current output pointer. */
+ symbol_set_frag (lab, frag_now);
+ S_SET_VALUE (lab, (valueT) frag_now_fix ());
+
+ /* Record this label for future adjustment after we find out what
+ kind of data it references, and the required alignment therewith. */
+ d30v_last_label = lab;
+
+ dwarf2_emit_label (lab);
+}
+
+/* Hook into cons for capturing alignment changes. */
+
+void
+d30v_cons_align (int size)
+{
+ int log_size;
+
+ /* Don't specially align anything in debug sections. */
+ if ((now_seg->flags & SEC_ALLOC) == 0
+ || strcmp (now_seg->name, ".eh_frame") == 0)
+ return;
+
+ log_size = 0;
+ while ((size >>= 1) != 0)
+ ++log_size;
+
+ if (d30v_current_align < log_size)
+ d30v_align (log_size, (char *) NULL, NULL);
+ else if (d30v_current_align > log_size)
+ d30v_current_align = log_size;
+ d30v_last_label = NULL;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where;
+ unsigned long insn, insn2;
+ long value = *valP;
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ /* We don't support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ insn = bfd_getb32 ((unsigned char *) where);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8: /* Check for a bad .byte directive. */
+ if (fixP->fx_addsy != NULL)
+ as_bad (_("line %d: unable to place address of symbol '%s' into a byte"),
+ fixP->fx_line, S_GET_NAME (fixP->fx_addsy));
+ else if (((unsigned)value) > 0xff)
+ as_bad (_("line %d: unable to place value %lx into a byte"),
+ fixP->fx_line, value);
+ else
+ *(unsigned char *) where = value;
+ break;
+
+ case BFD_RELOC_16: /* Check for a bad .short directive. */
+ if (fixP->fx_addsy != NULL)
+ as_bad (_("line %d: unable to place address of symbol '%s' into a short"),
+ fixP->fx_line, S_GET_NAME (fixP->fx_addsy));
+ else if (((unsigned)value) > 0xffff)
+ as_bad (_("line %d: unable to place value %lx into a short"),
+ fixP->fx_line, value);
+ else
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_64: /* Check for a bad .quad directive. */
+ if (fixP->fx_addsy != NULL)
+ as_bad (_("line %d: unable to place address of symbol '%s' into a quad"),
+ fixP->fx_line, S_GET_NAME (fixP->fx_addsy));
+ else
+ {
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ bfd_putb32 (0, ((unsigned char *) where) + 4);
+ }
+ break;
+
+ case BFD_RELOC_D30V_6:
+ check_size (value, 6, fixP->fx_file, fixP->fx_line);
+ insn |= value & 0x3F;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_9_PCREL:
+ if (fixP->fx_where & 0x7)
+ {
+ if (fixP->fx_done)
+ value += 4;
+ else
+ fixP->fx_r_type = BFD_RELOC_D30V_9_PCREL_R;
+ }
+ check_size (value, 9, fixP->fx_file, fixP->fx_line);
+ insn |= ((value >> 3) & 0x3F) << 12;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_15:
+ check_size (value, 15, fixP->fx_file, fixP->fx_line);
+ insn |= (value >> 3) & 0xFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_15_PCREL:
+ if (fixP->fx_where & 0x7)
+ {
+ if (fixP->fx_done)
+ value += 4;
+ else
+ fixP->fx_r_type = BFD_RELOC_D30V_15_PCREL_R;
+ }
+ check_size (value, 15, fixP->fx_file, fixP->fx_line);
+ insn |= (value >> 3) & 0xFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_21:
+ check_size (value, 21, fixP->fx_file, fixP->fx_line);
+ insn |= (value >> 3) & 0x3FFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_21_PCREL:
+ if (fixP->fx_where & 0x7)
+ {
+ if (fixP->fx_done)
+ value += 4;
+ else
+ fixP->fx_r_type = BFD_RELOC_D30V_21_PCREL_R;
+ }
+ check_size (value, 21, fixP->fx_file, fixP->fx_line);
+ insn |= (value >> 3) & 0x3FFFF;
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_D30V_32:
+ insn2 = bfd_getb32 ((unsigned char *) where + 4);
+ insn |= (value >> 26) & 0x3F; /* Top 6 bits. */
+ insn2 |= ((value & 0x03FC0000) << 2); /* Next 8 bits. */
+ insn2 |= value & 0x0003FFFF; /* Bottom 18 bits. */
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ bfd_putb32 ((bfd_vma) insn2, (unsigned char *) where + 4);
+ break;
+
+ case BFD_RELOC_D30V_32_PCREL:
+ insn2 = bfd_getb32 ((unsigned char *) where + 4);
+ insn |= (value >> 26) & 0x3F; /* Top 6 bits. */
+ insn2 |= ((value & 0x03FC0000) << 2); /* Next 8 bits. */
+ insn2 |= value & 0x0003FFFF; /* Bottom 18 bits. */
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ bfd_putb32 ((bfd_vma) insn2, (unsigned char *) where + 4);
+ break;
+
+ case BFD_RELOC_32:
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ default:
+ as_bad (_("line %d: unknown relocation type: 0x%x"),
+ fixP->fx_line, fixP->fx_r_type);
+ }
+}
+
+/* Handle the .align pseudo-op. This aligns to a power of two. We
+ hook here to latch the current alignment. */
+
+static void
+s_d30v_align (int ignore ATTRIBUTE_UNUSED)
+{
+ int align;
+ char fill, *pfill = NULL;
+ long max_alignment = 15;
+
+ align = get_absolute_expression ();
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("Alignment too large: %d assumed"), align);
+ }
+ else if (align < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed"));
+ align = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ fill = get_absolute_expression ();
+ pfill = &fill;
+ }
+
+ d30v_last_label = NULL;
+ d30v_align (align, pfill, NULL);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .text pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+
+static void
+s_d30v_text (int i)
+
+{
+ s_text (i);
+ d30v_last_label = NULL;
+ d30v_current_align = 0;
+ d30v_current_align_seg = now_seg;
+}
+
+/* Handle the .data pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+
+static void
+s_d30v_data (int i)
+{
+ s_data (i);
+ d30v_last_label = NULL;
+ d30v_current_align = 0;
+ d30v_current_align_seg = now_seg;
+}
+
+/* Handle the .section pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+
+static void
+s_d30v_section (int ignore)
+{
+ obj_elf_section (ignore);
+ d30v_last_label = NULL;
+ d30v_current_align = 0;
+ d30v_current_align_seg = now_seg;
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { "hword", cons, 2 },
+ { "align", s_d30v_align, 0 },
+ { "text", s_d30v_text, 0 },
+ { "data", s_d30v_data, 0 },
+ { "section", s_d30v_section, 0 },
+ { "section.s", s_d30v_section, 0 },
+ { "sect", s_d30v_section, 0 },
+ { "sect.s", s_d30v_section, 0 },
+ { NULL, NULL, 0 }
+};
diff --git a/gas/config/tc-d30v.h b/gas/config/tc-d30v.h
new file mode 100644
index 0000000..3e11a1c
--- /dev/null
+++ b/gas/config/tc-d30v.h
@@ -0,0 +1,64 @@
+/* tc-310v.h -- Header file for tc-d30v.c.
+ Copyright (C) 1997-2014 Free Software Foundation, Inc.
+ Written by Martin Hunt, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_D30V
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_d30v
+#define TARGET_FORMAT "elf32-d30v"
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define md_operand(x)
+
+/* Call md_pcrel_from_section, not md_pcrel_from. */
+struct fix;
+extern long md_pcrel_from_section (struct fix *, segT);
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+int d30v_cleanup (int);
+#define md_cleanup() d30v_cleanup (FALSE)
+#define TC_START_LABEL(ch, s, ptr) (ch == ':' && d30v_cleanup (FALSE))
+void d30v_start_line (void);
+#define md_start_line_hook() d30v_start_line ()
+
+void d30v_frob_label (symbolS *);
+#define tc_frob_label(sym) d30v_frob_label (sym)
+
+void d30v_cons_align (int);
+#define md_cons_align(nbytes) d30v_cons_align (nbytes)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
diff --git a/gas/config/tc-dlx.c b/gas/config/tc-dlx.c
new file mode 100644
index 0000000..3c487f2
--- /dev/null
+++ b/gas/config/tc-dlx.c
@@ -0,0 +1,1235 @@
+/* tc-dlx.c -- Assemble for the DLX
+ Copyright (C) 2002-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Initially created by Kuang Hwa Lin, 3/20/2002. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "tc-dlx.h"
+#include "opcode/dlx.h"
+
+/* Make it easier to clone this machine desc into another one. */
+#define machine_opcode dlx_opcode
+#define machine_opcodes dlx_opcodes
+#define machine_ip dlx_ip
+#define machine_it dlx_it
+
+#define NO_RELOC BFD_RELOC_NONE
+#define RELOC_DLX_REL26 BFD_RELOC_DLX_JMP26
+#define RELOC_DLX_16 BFD_RELOC_16
+#define RELOC_DLX_REL16 BFD_RELOC_16_PCREL_S2
+#define RELOC_DLX_HI16 BFD_RELOC_HI16_S
+#define RELOC_DLX_LO16 BFD_RELOC_LO16
+#define RELOC_DLX_VTINHERIT BFD_RELOC_VTABLE_INHERIT
+#define RELOC_DLX_VTENTRY BFD_RELOC_VTABLE_ENTRY
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+struct machine_it
+{
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ int pcrel;
+ int size;
+ int reloc_offset; /* Offset of reloc within insn. */
+ int reloc;
+ int HI;
+ int LO;
+}
+the_insn;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = ";";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#";
+
+/* We needed an unused char for line separation to work around the
+ lack of macros, using sed and such. */
+const char line_separator_chars[] = "@";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456
+ or 0d1.2345e12. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+static void
+insert_sreg (char *regname, int regnum)
+{
+ /* Must be large enough to hold the names of the special registers. */
+ char buf[80];
+ int i;
+
+ symbol_table_insert (symbol_new (regname, reg_section, (valueT) regnum,
+ &zero_address_frag));
+ for (i = 0; regname[i]; i++)
+ buf[i] = ISLOWER (regname[i]) ? TOUPPER (regname[i]) : regname[i];
+ buf[i] = '\0';
+
+ symbol_table_insert (symbol_new (buf, reg_section, (valueT) regnum,
+ &zero_address_frag));
+}
+
+/* Install symbol definitions for assorted special registers.
+ See MIPS Assembly Language Programmer's Guide page 1-4 */
+
+static void
+define_some_regs (void)
+{
+ /* Software representation. */
+ insert_sreg ("zero", 0);
+ insert_sreg ("at", 1);
+ insert_sreg ("v0", 2);
+ insert_sreg ("v1", 3);
+ insert_sreg ("a0", 4);
+ insert_sreg ("a1", 5);
+ insert_sreg ("a2", 6);
+ insert_sreg ("a3", 7);
+ insert_sreg ("t0", 8);
+ insert_sreg ("t1", 9);
+ insert_sreg ("t2", 10);
+ insert_sreg ("t3", 11);
+ insert_sreg ("t4", 12);
+ insert_sreg ("t5", 13);
+ insert_sreg ("t6", 14);
+ insert_sreg ("t7", 15);
+ insert_sreg ("s0", 16);
+ insert_sreg ("s1", 17);
+ insert_sreg ("s2", 18);
+ insert_sreg ("s3", 19);
+ insert_sreg ("s4", 20);
+ insert_sreg ("s5", 21);
+ insert_sreg ("s6", 22);
+ insert_sreg ("s7", 23);
+ insert_sreg ("t8", 24);
+ insert_sreg ("t9", 25);
+ insert_sreg ("k0", 26);
+ insert_sreg ("k1", 27);
+ insert_sreg ("gp", 28);
+ insert_sreg ("sp", 29);
+ insert_sreg ("fp", 30);
+ insert_sreg ("ra", 31);
+ /* Special registers. */
+ insert_sreg ("pc", 0);
+ insert_sreg ("npc", 1);
+ insert_sreg ("iad", 2);
+}
+
+/* Subroutine check the string to match an register. */
+
+static int
+match_sft_register (char *name)
+{
+#define MAX_REG_NO 35
+/* Currently we have 35 software registers defined -
+ we borrowed from MIPS. */
+ static char *soft_reg[] =
+ {
+ "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3",
+ "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9",
+ "s0", "s1", "s2", "s3", "s4", "s5", "s7", "k0", "k1",
+ "gp", "sp", "fp", "ra", "pc", "npc", "iad",
+ "EndofTab" /* End of the Table indicator */
+ };
+ char low_name[21], *ptr;
+ int idx;
+
+ for (ptr = name,idx = 0; *ptr != '\0'; ptr++)
+ low_name[idx++] = TOLOWER (*ptr);
+
+ low_name[idx] = '\0';
+ idx = 0;
+
+ while (idx < MAX_REG_NO && strcmp (soft_reg[idx], & low_name [0]))
+ idx += 1;
+
+ return idx < MAX_REG_NO;
+}
+
+/* Subroutine check the string to match an register. */
+
+static int
+is_ldst_registers (char *name)
+{
+ char *ptr = name;
+
+ /* The first character of the register name got to be either %, $, r of R. */
+ if ((ptr[0] == '%' || ptr[0] == '$' || ptr[0] == 'r' || ptr[0] == 'R')
+ && ISDIGIT ((unsigned char) ptr[1]))
+ return 1;
+
+ /* Now check the software register representation. */
+ return match_sft_register (ptr);
+}
+
+/* Subroutine of s_proc so targets can choose a different default prefix.
+ If DEFAULT_PREFIX is NULL, use the target's "leading char". */
+
+static void
+s_proc (int end_p)
+{
+ /* Record the current function so that we can issue an error message for
+ misplaced .func,.endfunc, and also so that .endfunc needs no
+ arguments. */
+ static char *current_name;
+ static char *current_label;
+
+ if (end_p)
+ {
+ if (current_name == NULL)
+ {
+ as_bad (_("missing .proc"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ current_name = current_label = NULL;
+ SKIP_WHITESPACE ();
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ }
+ else
+ {
+ char *name, *label;
+ char delim1, delim2;
+
+ if (current_name != NULL)
+ {
+ as_bad (_(".endfunc missing for previous .proc"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ delim1 = get_symbol_end ();
+ name = xstrdup (name);
+ *input_line_pointer = delim1;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ char leading_char = 0;
+
+ leading_char = bfd_get_symbol_leading_char (stdoutput);
+ /* Missing entry point, use function's name with the leading
+ char prepended. */
+ if (leading_char)
+ {
+ unsigned len = strlen (name) + 1;
+ label = xmalloc (len + 1);
+ label[0] = leading_char;
+ memcpy (label + 1, name, len);
+ }
+ else
+ label = name;
+ }
+ else
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ label = input_line_pointer;
+ delim2 = get_symbol_end ();
+ label = xstrdup (label);
+ *input_line_pointer = delim2;
+ }
+
+ current_name = name;
+ current_label = label;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc., that the MD part of the assembler will
+ need. */
+
+void
+md_begin (void)
+{
+ const char *retval = NULL;
+ int lose = 0;
+ unsigned int i;
+
+ /* Create a new hash table. */
+ op_hash = hash_new ();
+
+ /* Hash up all the opcodes for fast use later. */
+ for (i = 0; i < num_dlx_opcodes; i++)
+ {
+ const char *name = machine_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (void *) &machine_opcodes[i]);
+
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ machine_opcodes[i].name, retval);
+ lose = 1;
+ }
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ define_some_regs ();
+}
+
+/* This function will check the opcode and return 1 if the opcode is one
+ of the load/store instruction, and it will fix the operand string to
+ the standard form so we can use the standard parse_operand routine. */
+
+#define READ_OP 0x100
+#define WRITE_OP 0x200
+static char iBuf[81];
+
+static char *
+dlx_parse_loadop (char * str)
+{
+ char *ptr = str;
+ int idx = 0;
+
+ /* The last pair of ()/[] is the register, all other are the
+ reloc displacement, and if there is a register then it ought
+ to have a pair of ()/[]
+ This is not necessarily true, what if the load instruction come
+ without the register and with %hi/%lo modifier? */
+ for (idx = 0; idx < 72 && ptr[idx] != '\0'; idx++)
+ ;
+
+ if (idx == 72)
+ {
+ badoperand_load:
+ as_bad (_("Bad operand for a load instruction: <%s>"), str);
+ return NULL;
+ }
+ else
+ {
+ int i, pb = 0;
+ int m2 = 0;
+ char rs1[7], rd[7], endm, match = '0';
+ char imm[72];
+
+ idx -= 1;
+ switch (str[idx])
+ {
+ case ')':
+ match = '(';
+ endm = ')';
+ break;
+ case ']':
+ match = '[';
+ endm = ']';
+ break;
+ default:
+ /* No register indicated, fill in zero. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ match = 0;
+ endm = 0;
+ m2 = 1;
+ }
+
+ if (!m2)
+ {
+ /* Searching for (/[ which will match the ]/). */
+ for (pb = idx - 1; str[pb] != match; pb -= 1)
+ /* Match can only be either '[' or '(', if it is
+ '(' then this can be a normal expression, we'll treat
+ it as an operand. */
+ if (str[pb] == endm || pb < (idx - 5))
+ goto load_no_rs1;
+ pb += 1;
+
+ for (i = 0; (pb + i) < idx; i++)
+ rs1[i] = str[pb+i];
+
+ rs1[i] = '\0';
+
+ if (is_ldst_registers (& rs1[0]))
+ /* Point to the last character of the imm. */
+ pb -= 1;
+ else
+ {
+ load_no_rs1:
+ if (match == '[')
+ goto badoperand_load;
+ /* No register indicated, fill in zero and restore the imm. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ m2 = 1;
+ }
+ }
+
+ /* Duplicate the first register. */
+ for (i = 0; i < 7 && str[i] != ','; i++)
+ rd[i] = ptr[i];
+
+ if (str[i] != ',')
+ goto badoperand_load;
+ else
+ rd[i] = '\0';
+
+ /* Copy the immd. */
+ if (m2)
+ /* Put the '\0' back in. */
+ pb = idx + 1;
+
+ for (i++, m2 = 0; i < pb; m2++,i++)
+ imm[m2] = ptr[i];
+
+ imm[m2] = '\0';
+
+ /* Assemble the instruction to gas internal format. */
+ for (i = 0; rd[i] != '\0'; i++)
+ iBuf[i] = rd[i];
+
+ iBuf[i++] = ',';
+
+ for (pb = 0 ; rs1[pb] != '\0'; i++, pb++)
+ iBuf[i] = rs1[pb];
+
+ iBuf[i++] = ',';
+
+ for (pb = 0; imm[pb] != '\0'; i++, pb++)
+ iBuf[i] = imm[pb];
+
+ iBuf[i] = '\0';
+ return iBuf;
+ }
+}
+
+static char *
+dlx_parse_storeop (char * str)
+{
+ char *ptr = str;
+ int idx = 0;
+
+ /* Search for the ','. */
+ for (idx = 0; idx < 72 && ptr[idx] != ','; idx++)
+ ;
+
+ if (idx == 72)
+ {
+ badoperand_store:
+ as_bad (_("Bad operand for a store instruction: <%s>"), str);
+ return NULL;
+ }
+ else
+ {
+ /* idx now points to the ','. */
+ int i, pb = 0;
+ int comma = idx;
+ int m2 = 0;
+ char rs1[7], rd[7], endm, match = '0';
+ char imm[72];
+
+ /* Now parse the '(' and ')', and make idx point to ')'. */
+ idx -= 1;
+ switch (str[idx])
+ {
+ case ')':
+ match = '(';
+ endm = ')';
+ break;
+ case ']':
+ match = '[';
+ endm = ']';
+ break;
+ default:
+ /* No register indicated, fill in zero. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ match = 0;
+ endm = 0;
+ m2 = 1;
+ }
+
+ if (!m2)
+ {
+ /* Searching for (/[ which will match the ]/). */
+ for (pb = idx - 1; str[pb] != match; pb -= 1)
+ if (pb < (idx - 5) || str[pb] == endm)
+ goto store_no_rs1;
+ pb += 1;
+
+ for (i = 0; (pb + i) < idx; i++)
+ rs1[i] = str[pb + i];
+
+ rs1[i] = '\0';
+
+ if (is_ldst_registers (& rs1[0]))
+ /* Point to the last character of the imm. */
+ pb -= 1;
+ else
+ {
+ store_no_rs1:
+ if (match == '[')
+ goto badoperand_store;
+
+ /* No register indicated, fill in zero and restore the imm. */
+ rs1[0] = 'r';
+ rs1[1] = '0';
+ rs1[2] = '\0';
+ pb = comma;
+ }
+ }
+ else
+ /* No register was specified. */
+ pb = comma;
+
+ /* Duplicate the first register. */
+ for (i = comma + 1; (str[i] == ' ' || str[i] == '\t'); i++)
+ ;
+
+ for (m2 = 0; (m2 < 7 && str[i] != '\0'); i++, m2++)
+ {
+ if (str[i] != ' ' && str[i] != '\t')
+ rd[m2] = str[i];
+ else
+ goto badoperand_store;
+ }
+
+ if (str[i] != '\0')
+ goto badoperand_store;
+ else
+ rd[m2] = '\0';
+
+ /* Copy the immd. */
+ for (i = 0; i < pb; i++)
+ imm[i] = ptr[i];
+
+ imm[i] = '\0';
+
+ /* Assemble the instruction to gas internal format. */
+ for (i = 0; rd[i] != '\0'; i++)
+ iBuf[i] = rd[i];
+ iBuf[i++] = ',';
+ for (pb = 0 ; rs1[pb] != '\0'; i++, pb++)
+ iBuf[i] = rs1[pb];
+ iBuf[i++] = ',';
+ for (pb = 0; imm[pb] != '\0'; i++, pb++)
+ iBuf[i] = imm[pb];
+ iBuf[i] = '\0';
+ return iBuf;
+ }
+}
+
+static char *
+fix_ld_st_operand (unsigned long opcode, char* str)
+{
+ /* Check the opcode. */
+ switch ((int) opcode)
+ {
+ case LBOP:
+ case LBUOP:
+ case LSBUOP:
+ case LHOP:
+ case LHUOP:
+ case LSHUOP:
+ case LWOP:
+ case LSWOP:
+ return dlx_parse_loadop (str);
+ case SBOP:
+ case SHOP:
+ case SWOP:
+ return dlx_parse_storeop (str);
+ default:
+ return str;
+ }
+}
+
+static int
+hilo_modifier_ok (char *s)
+{
+ char *ptr = s;
+ int idx, count = 1;
+
+ if (*ptr != '(')
+ return 1;
+
+ for (idx = 1; ptr[idx] != '\0' && ptr[idx] != '[' && idx < 73; idx += 1)
+ {
+ if (count == 0)
+ return count;
+
+ if (ptr[idx] == '(')
+ count += 1;
+
+ if (ptr[idx] == ')')
+ count -= 1;
+ }
+
+ return (count == 0) ? 1:0;
+}
+
+static char *
+parse_operand (char *s, expressionS *operandp)
+{
+ char *save = input_line_pointer;
+ char *new_pos;
+
+ the_insn.HI = the_insn.LO = 0;
+
+ /* Search for %hi and %lo, make a mark and skip it. */
+ if (strncmp (s, "%hi", 3) == 0)
+ {
+ s += 3;
+ the_insn.HI = 1;
+ }
+ else
+ {
+ if (strncmp (s, "%lo", 3) == 0)
+ {
+ s += 3;
+ the_insn.LO = 1;
+ }
+ else
+ the_insn.LO = 0;
+ }
+
+ if (the_insn.HI || the_insn.LO)
+ {
+ if (!hilo_modifier_ok (s))
+ as_bad (_("Expression Error for operand modifier %%hi/%%lo\n"));
+ }
+
+ /* Check for the % and $ register representation */
+ if ((s[0] == '%' || s[0] == '$' || s[0] == 'r' || s[0] == 'R')
+ && ISDIGIT ((unsigned char) s[1]))
+ {
+ /* We have a numeric register expression. No biggy. */
+ s += 1;
+ input_line_pointer = s;
+ (void) expression (operandp);
+ if (operandp->X_op != O_constant
+ || operandp->X_add_number > 31)
+ as_bad (_("Invalid expression after %%%%\n"));
+ operandp->X_op = O_register;
+ }
+ else
+ {
+ /* Normal operand parsing. */
+ input_line_pointer = s;
+ (void) expression (operandp);
+ }
+
+ new_pos = input_line_pointer;
+ input_line_pointer = save;
+ return new_pos;
+}
+
+/* Instruction parsing. Takes a string containing the opcode.
+ Operands are at input_line_pointer. Output is in the_insn.
+ Warnings or errors are generated. */
+
+static void
+machine_ip (char *str)
+{
+ char *s;
+ const char *args;
+ struct machine_opcode *insn;
+ unsigned long opcode;
+ expressionS the_operand;
+ expressionS *operand = &the_operand;
+ unsigned int reg, reg_shift = 0;
+
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = NO_RELOC;
+
+ /* Fixup the opcode string to all lower cases, and also
+ allow numerical digits. */
+ s = str;
+
+ if (ISALPHA (*s))
+ for (; ISALNUM (*s); ++s)
+ if (ISUPPER (*s))
+ *s = TOLOWER (*s);
+
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ /* FIXME-SOMEDAY more whitespace. */
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+
+ /* Hash the opcode, insn will have the string from opcode table. */
+ if ((insn = (struct machine_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ /* Handle the ret and return macro here. */
+ if ((strcmp (str, "ret") == 0) || (strcmp (str, "return") == 0))
+ the_insn.opcode = JROP | 0x03e00000; /* 0x03e00000 = r31 << 21 */
+ else
+ as_bad (_("Unknown opcode `%s'."), str);
+
+ return;
+ }
+
+ opcode = insn->opcode;
+
+ /* Set the sip reloc HI16 flag. */
+ if (!set_dlx_skip_hi16_flag (1))
+ as_bad (_("Can not set dlx_skip_hi16_flag"));
+
+ /* Fix the operand string if it is one of load store instructions. */
+ s = fix_ld_st_operand (opcode, s);
+
+ /* Build the opcode, checking as we go to make sure that the
+ operands match.
+ If an operand matches, we modify the_insn or opcode appropriately,
+ and do a "continue". If an operand fails to match, we "break". */
+ if (insn->args[0] != '\0' && insn->args[0] != 'N')
+ {
+ /* Prime the pump. */
+ if (*s == '\0')
+ {
+ as_bad (_("Missing arguments for opcode <%s>."), str);
+ return;
+ }
+ else
+ s = parse_operand (s, operand);
+ }
+ else if (insn->args[0] == 'N')
+ {
+ /* Clean up the insn and done! */
+ the_insn.opcode = opcode;
+ return;
+ }
+
+ /* Parse through the args (this is from opcode table), *s point to
+ the current character of the instruction stream. */
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+ /* End of Line. */
+ case '\0':
+ /* End of args. */
+ if (*s == '\0')
+ {
+ /* We are truly done. */
+ the_insn.opcode = opcode;
+ /* Clean up the HI and LO mark. */
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ return;
+ }
+
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ as_bad (_("Too many operands: %s"), s);
+ break;
+
+ /* ',' Args separator */
+ case ',':
+ /* Must match a comma. */
+ if (*s++ == ',')
+ {
+ /* Parse next operand. */
+ s = parse_operand (s, operand);
+ continue;
+ }
+ break;
+
+ /* It can be a 'a' register or 'i' operand. */
+ case 'P':
+ /* Macro move operand/reg. */
+ if (operand->X_op == O_register)
+ {
+ /* Its a register. */
+ reg_shift = 21;
+ goto general_reg;
+ }
+
+ /* The immediate 16 bits literal, bit 0-15. */
+ case 'i':
+ /* offset, unsigned. */
+ case 'I':
+ /* offset, signed. */
+ if (operand->X_op == O_constant)
+ {
+ if (the_insn.HI)
+ operand->X_add_number >>= 16;
+
+ opcode |= operand->X_add_number & 0xFFFF;
+
+ if (the_insn.HI && the_insn.LO)
+ as_bad (_("Both the_insn.HI and the_insn.LO are set : %s"), s);
+ else
+ {
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ }
+ continue;
+ }
+
+ the_insn.reloc = (the_insn.HI) ? RELOC_DLX_HI16
+ : (the_insn.LO ? RELOC_DLX_LO16 : RELOC_DLX_16);
+ the_insn.reloc_offset = 2;
+ the_insn.size = 2;
+ the_insn.pcrel = 0;
+ the_insn.exp = * operand;
+ the_insn.HI = 0;
+ the_insn.LO = 0;
+ continue;
+
+ case 'd':
+ /* offset, signed. */
+ if (operand->X_op == O_constant)
+ {
+ opcode |= operand->X_add_number & 0xFFFF;
+ continue;
+ }
+ the_insn.reloc = RELOC_DLX_REL16;
+ the_insn.reloc_offset = 0; /* BIG-ENDIAN Byte 3 of insn. */
+ the_insn.size = 4;
+ the_insn.pcrel = 1;
+ the_insn.exp = *operand;
+ continue;
+
+ /* The immediate 26 bits literal, bit 0-25. */
+ case 'D':
+ /* offset, signed. */
+ if (operand->X_op == O_constant)
+ {
+ opcode |= operand->X_add_number & 0x3FFFFFF;
+ continue;
+ }
+ the_insn.reloc = RELOC_DLX_REL26;
+ the_insn.reloc_offset = 0; /* BIG-ENDIAN Byte 3 of insn. */
+ the_insn.size = 4;
+ the_insn.pcrel = 1;
+ the_insn.exp = *operand;
+ continue;
+
+ /* Type 'a' Register. */
+ case 'a':
+ /* A general register at bits 21-25, rs1. */
+ reg_shift = 21;
+ goto general_reg;
+
+ /* Type 'b' Register. */
+ case 'b':
+ /* A general register at bits 16-20, rs2/rd. */
+ reg_shift = 16;
+ goto general_reg;
+
+ /* Type 'c' Register. */
+ case 'c':
+ /* A general register at bits 11-15, rd. */
+ reg_shift = 11;
+
+ general_reg:
+ know (operand->X_add_symbol == 0);
+ know (operand->X_op_symbol == 0);
+ reg = operand->X_add_number;
+ if (reg & 0xffffffe0)
+ as_fatal (_("failed regnum sanity check."));
+ else
+ /* Got the register, now figure out where it goes in the opcode. */
+ opcode |= reg << reg_shift;
+
+ switch (*args)
+ {
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'P':
+ continue;
+ }
+ as_fatal (_("failed general register sanity check."));
+ break;
+
+ default:
+ BAD_CASE (*args);
+ }
+
+ /* Types or values of args don't match. */
+ as_bad (_("Invalid operands"));
+ return;
+ }
+}
+
+/* Assemble a single instruction. Its label has already been handled
+ by the generic front end. We just parse opcode and operands, and
+ produce the bytes of data and relocation. */
+
+void
+md_assemble (char *str)
+{
+ char *toP;
+ fixS *fixP;
+ bit_fixS *bitP;
+
+ know (str);
+ machine_ip (str);
+ toP = frag_more (4);
+ dwarf2_emit_insn (4);
+
+ /* Put out the opcode. */
+ md_number_to_chars (toP, the_insn.opcode, 4);
+
+ /* Put out the symbol-dependent stuff. */
+ if (the_insn.reloc != NO_RELOC)
+ {
+ fixP = fix_new_exp (frag_now,
+ (toP - frag_now->fr_literal + the_insn.reloc_offset),
+ the_insn.size, & the_insn.exp, the_insn.pcrel,
+ the_insn.reloc);
+
+ /* Turn off complaints that the addend is
+ too large for things like foo+100000@ha. */
+ switch (the_insn.reloc)
+ {
+ case RELOC_DLX_HI16:
+ case RELOC_DLX_LO16:
+ fixP->fx_no_overflow = 1;
+ break;
+ default:
+ break;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_DLX_REL26:
+ bitP = malloc (sizeof (bit_fixS));
+ bitP->fx_bit_size = 26;
+ bitP->fx_bit_offset = 25;
+ bitP->fx_bit_base = the_insn.opcode & 0xFC000000;
+ bitP->fx_bit_base_adj = 0;
+ bitP->fx_bit_max = 0;
+ bitP->fx_bit_min = 0;
+ bitP->fx_bit_add = 0x03FFFFFF;
+ fixP->fx_bit_fixP = bitP;
+ break;
+ case RELOC_DLX_LO16:
+ case RELOC_DLX_REL16:
+ bitP = malloc (sizeof (bit_fixS));
+ bitP->fx_bit_size = 16;
+ bitP->fx_bit_offset = 15;
+ bitP->fx_bit_base = the_insn.opcode & 0xFFFF0000;
+ bitP->fx_bit_base_adj = 0;
+ bitP->fx_bit_max = 0;
+ bitP->fx_bit_min = 0;
+ bitP->fx_bit_add = 0x0000FFFF;
+ fixP->fx_bit_fixP = bitP;
+ break;
+ case RELOC_DLX_HI16:
+ bitP = malloc (sizeof (bit_fixS));
+ bitP->fx_bit_size = 16;
+ bitP->fx_bit_offset = 15;
+ bitP->fx_bit_base = the_insn.opcode & 0xFFFF0000;
+ bitP->fx_bit_base_adj = 0;
+ bitP->fx_bit_max = 0;
+ bitP->fx_bit_min = 0;
+ bitP->fx_bit_add = 0x0000FFFF;
+ fixP->fx_bit_fixP = bitP;
+ break;
+ default:
+ fixP->fx_bit_fixP = NULL;
+ break;
+ }
+ }
+}
+
+/* This is identical to the md_atof in m68k.c. I think this is right,
+ but I'm not sure. Dlx will not use it anyway, so I just leave it
+ here for now. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Write out big-endian. */
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+bfd_boolean
+md_dlx_fix_adjustable (fixS *fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ return (fixP->fx_r_type != BFD_RELOC_VTABLE_INHERIT
+ && fixP->fx_r_type != BFD_RELOC_VTABLE_ENTRY);
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ long val = *valP;
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_r_type)
+ {
+ case RELOC_DLX_LO16:
+ case RELOC_DLX_REL16:
+ if (fixP->fx_bit_fixP != NULL)
+ {
+ val = (val & 0x0000FFFF) | fixP->fx_bit_fixP->fx_bit_base;
+ free (fixP->fx_bit_fixP);
+ fixP->fx_bit_fixP = NULL;
+ }
+#ifdef DEBUG
+ else
+ know ((fixP->fx_bit_fixP != NULL));
+#endif
+ break;
+
+ case RELOC_DLX_HI16:
+ if (fixP->fx_bit_fixP != NULL)
+ {
+ val = (val >> 16) | fixP->fx_bit_fixP->fx_bit_base;
+ free (fixP->fx_bit_fixP);
+ fixP->fx_bit_fixP = NULL;
+ }
+#ifdef DEBUG
+ else
+ know ((fixP->fx_bit_fixP != NULL));
+#endif
+ break;
+
+ case RELOC_DLX_REL26:
+ if (fixP->fx_bit_fixP != NULL)
+ {
+ val = (val & 0x03FFFFFF) | fixP->fx_bit_fixP->fx_bit_base;
+ free (fixP->fx_bit_fixP);
+ fixP->fx_bit_fixP = NULL;
+ }
+#ifdef DEBUG
+ else
+ know ((fixP->fx_bit_fixP != NULL));
+#endif
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ /* This borrowed from tc-ppc.c on a whim. */
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ return;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ default:
+ break;
+ }
+
+ number_to_chars_bigendian (place, val, fixP->fx_size);
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+}
+
+const char *md_shortopts = "";
+
+struct option md_longopts[] =
+ {
+ {NULL, no_argument, NULL, 0}
+ };
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+}
+
+/* This is called when a line is unrecognized. */
+
+int
+dlx_unrecognized_line (int c)
+{
+ int lab;
+ char *s;
+
+ if (c != '$' || ! ISDIGIT ((unsigned char) input_line_pointer[0]))
+ return 0;
+
+ s = input_line_pointer;
+
+ lab = 0;
+ while (ISDIGIT ((unsigned char) *s))
+ {
+ lab = lab * 10 + *s - '0';
+ ++s;
+ }
+
+ if (*s != ':')
+ /* Not a label definition. */
+ return 0;
+
+ if (dollar_label_defined (lab))
+ {
+ as_bad (_("label \"$%d\" redefined"), lab);
+ return 0;
+ }
+
+ define_dollar_label (lab);
+ colon (dollar_label_name (lab, 0));
+ input_line_pointer = s + 1;
+
+ return 1;
+}
+
+/* Default the values of symbols known that should be "predefined". We
+ don't bother to predefine them unless you actually use one, since there
+ are a lot of them. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Parse an operand that is machine-specific, the function was called
+ in expr.c by operand() function, when everything failed before it
+ call a quit. */
+
+void
+md_operand (expressionS* expressionP)
+{
+ /* Check for the #number representation */
+ if (input_line_pointer[0] == '#' &&
+ ISDIGIT ((unsigned char) input_line_pointer[1]))
+ {
+ /* We have a numeric number expression. No biggy. */
+ input_line_pointer += 1; /* Skip # */
+
+ (void) expression (expressionP);
+
+ if (expressionP->X_op != O_constant)
+ as_bad (_("Invalid expression after # number\n"));
+ }
+
+ return;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
+{
+ /* Byte alignment is fine. */
+ return size;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the 29000, they're relative to the address of the instruction,
+ which we have set up as the address of the fixup too. */
+
+long
+md_pcrel_from (fixS* fixP)
+{
+ return 4 + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format.
+ FIXME: To what extent can we get all relevant targets to use this?
+ The above FIXME is from a29k, but I think it is also needed here. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixP)
+{
+ arelent * reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixP->fx_offset;
+ reloc->addend = 0;
+
+ return reloc;
+}
+
+const pseudo_typeS
+dlx_pseudo_table[] =
+{
+ /* Some additional ops that are used by gcc-dlx. */
+ {"asciiz", stringer, 8 + 1},
+ {"half", cons, 2},
+ {"dword", cons, 8},
+ {"word", cons, 4},
+ {"proc", s_proc, 0},
+ {"endproc", s_proc, 1},
+ {NULL, NULL, 0}
+};
+
+void
+dlx_pop_insert (void)
+{
+ pop_insert (dlx_pseudo_table);
+ return ;
+}
diff --git a/gas/config/tc-dlx.h b/gas/config/tc-dlx.h
new file mode 100644
index 0000000..e299f27
--- /dev/null
+++ b/gas/config/tc-dlx.h
@@ -0,0 +1,66 @@
+/* tc-dlx.h -- Assemble for the DLX
+ Copyright (C) 2002-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Initially created by Kuang Hwa Lin, 3/20/2002. */
+
+#define TC_DLX
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_dlx
+#define TARGET_FORMAT "elf32-dlx"
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define WORKING_DOT_WORD
+
+#define LEX_DOLLAR 1
+
+extern void dlx_pop_insert (void);
+extern int set_dlx_skip_hi16_flag (int);
+extern int dlx_unrecognized_line (int);
+extern bfd_boolean md_dlx_fix_adjustable (struct fix *);
+
+#define md_pop_insert() dlx_pop_insert ()
+
+#define md_convert_frag(b,s,f) as_fatal ("convert_frag called\n")
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal ("estimate_size_before_relax called"),1)
+
+#define tc_unrecognized_line(c) dlx_unrecognized_line (c)
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+#define tc_fix_adjustable(FIX) md_dlx_fix_adjustable (FIX)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* Zero Based Segment?? sound very dangerous to me! */
+#define ZERO_BASED_SEGMENTS
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#undef LOCAL_LABELS_DOLLAR
+#define LOCAL_LABELS_DOLLAR 0
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
diff --git a/gas/config/tc-epiphany.c b/gas/config/tc-epiphany.c
new file mode 100644
index 0000000..ebaedc4
--- /dev/null
+++ b/gas/config/tc-epiphany.c
@@ -0,0 +1,1111 @@
+/* tc-epiphany.c -- Assembler for the Adapteva EPIPHANY
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Contributed by Embecosm on behalf of Adapteva, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/epiphany-desc.h"
+#include "opcodes/epiphany-opc.h"
+#include "cgen.h"
+#include "elf/common.h"
+#include "elf/epiphany.h"
+#include "dwarf2dbg.h"
+#include "libbfd.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];
+}
+epiphany_insn;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "`";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "fFdD";
+
+/* Flag to detect when switching to code section where insn alignment is
+ implied. */
+static bfd_boolean force_code_align = FALSE;
+
+static void
+epiphany_elf_section_rtn (int i)
+{
+ obj_elf_section (i);
+
+ if (force_code_align)
+ {
+ /* The s_align_ptwo function expects that we are just after a .align
+ directive and it will either try and read the align value or stop
+ if end of line so we must fake it out so it thinks we are at the
+ end of the line. */
+ char *old_input_line_pointer = input_line_pointer;
+
+ input_line_pointer = "\n";
+ s_align_ptwo (1);
+ force_code_align = FALSE;
+
+ /* Restore. */
+ input_line_pointer = old_input_line_pointer;
+ }
+}
+
+static void
+epiphany_elf_section_text (int i)
+{
+ char *old_input_line_pointer;
+
+ obj_elf_text (i);
+
+ /* The s_align_ptwo function expects that we are just after a .align
+ directive and it will either try and read the align value or stop if
+ end of line so we must fake it out so it thinks we are at the end of
+ the line. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = "\n";
+ s_align_ptwo (1);
+ force_code_align = FALSE;
+ /* Restore. */
+ input_line_pointer = old_input_line_pointer;
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "text", epiphany_elf_section_text, 0 },
+ { "sect", epiphany_elf_section_rtn, 0 },
+ /* .word should be 32 bits. */
+ { "word", cons, 4 },
+ { "cpu", s_ignore, 0 },
+ { "thumb_func", s_ignore, 0 },
+ { "code", s_ignore, 0 },
+ { NULL, NULL, 0 }
+};
+
+
+
+enum options
+{
+ OPTION_CPU_EPIPHANY = OPTION_MD_BASE,
+ OPTION_CPU_EPIPHANY16
+};
+
+struct option md_longopts[] =
+{
+ { "mepiphany ", no_argument, NULL, OPTION_CPU_EPIPHANY },
+ { "mepiphany16", no_argument, NULL, OPTION_CPU_EPIPHANY16 },
+ { NULL, no_argument, NULL, 0 },
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+const char * md_shortopts = "";
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED)
+{
+ return 0; /* No target-specific options. */
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("EPIPHANY specific command line options:\n"));
+}
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = epiphany_cgen_cpu_open (CGEN_CPU_OPEN_MACHS,
+ bfd_mach_epiphany32,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_LITTLE,
+ CGEN_CPU_OPEN_END);
+ epiphany_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 machine type. */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_epiphany, bfd_mach_epiphany32);
+}
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+
+/* Functions concerning relocs. */
+
+long
+md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+/* 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);
+}
+
+int
+epiphany_elf_section_flags (int flags,
+ int attr ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED)
+{
+ /* This is used to detect when the section changes to an executable section.
+ This function is called by the elf section processing. When we note an
+ executable section specifier we set an internal flag to denote when
+ word alignment should be forced. */
+ if (flags & SEC_CODE)
+ force_code_align = TRUE;
+
+ return flags;
+}
+
+/* Non-zero if we are generating PIC code. */
+int pic_code;
+
+/* Epiphany er_flags. */
+static int epiphany_flags = 0;
+
+/* Relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct epiphany_hi_fixup
+{
+ /* Next HI fixup. */
+ struct epiphany_hi_fixup *next;
+
+ /* This fixup. */
+ fixS *fixp;
+
+ /* The section this fixup is in. */
+ segT seg;
+};
+
+
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+static symbolS * GOT_symbol;
+
+static inline bfd_boolean
+epiphany_PIC_related_p (symbolS *sym)
+{
+ expressionS *exp;
+
+ if (! sym)
+ return FALSE;
+
+ if (sym == GOT_symbol)
+ return TRUE;
+
+ exp = symbol_get_value_expression (sym);
+
+ return (exp->X_op == O_PIC_reloc
+ || exp->X_md == BFD_RELOC_EPIPHANY_SIMM24
+ || exp->X_md == BFD_RELOC_EPIPHANY_SIMM8
+ || epiphany_PIC_related_p (exp->X_add_symbol)
+ || epiphany_PIC_related_p (exp->X_op_symbol));
+}
+
+/* Perform target dependent relocations that are done at compile time.
+ There aren't very many of these. */
+
+void
+epiphany_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ if (((int) fixP->fx_r_type < (int) BFD_RELOC_UNUSED)
+ && fixP->fx_done)
+ {
+ /* Install EPIPHANY-dependent relocations HERE because nobody else
+ will. */
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ unsigned char *insn = (unsigned char *)where;
+ valueT value = * valP;
+
+ switch (fixP->fx_r_type)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_NONE:
+ return;
+
+ case BFD_RELOC_EPIPHANY_SIMM11:
+ where[0] = where[0] | ((value & 1) << 7);
+ where[1] = where[1] | ((value & 6) >> 1);
+ where[2] = (value >> 3) & 0xff;
+ return;
+
+ case BFD_RELOC_EPIPHANY_IMM11:
+ where[0] = where[0] | ((value & 1) << 7);
+ where[1] = where[1] | ((value & 6) >> 1);
+ where[2] = (value >> 3) & 0xff;
+ return;
+
+ case BFD_RELOC_EPIPHANY_SIMM8:
+ md_number_to_chars (where+1, value>>1, 1);
+ return;
+
+ case BFD_RELOC_EPIPHANY_SIMM24:
+ md_number_to_chars (where+1, value>>1, 3);
+ return;
+
+ case BFD_RELOC_EPIPHANY_HIGH:
+ value >>= 16;
+ /* fall thru */
+ case BFD_RELOC_EPIPHANY_LOW:
+ value = (((value & 0xff) << 5) | insn[0])
+ | (insn[1] << 8)
+ | ((value & 0xff00) << 12)
+ | (insn[2] << 16);
+ md_number_to_chars (where, value, 3);
+ return;
+ }
+ }
+
+ /* Just do the default if we can't special case. */
+ return gas_cgen_md_apply_fix (fixP, valP, seg);
+}
+
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. 0x01a2 is 16-bit pattern for a "nop". */
+
+static const unsigned char nop_pattern[] = { 0xa2, 0x01 };
+
+void
+epiphany_handle_align (fragS *fragp)
+{
+ int bytes, fix;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ if (bytes & 1)
+ {
+ fix = 1;
+ *p++ = 0;
+ bytes--;
+ }
+
+ if (bytes & 2)
+ {
+ memcpy (p, nop_pattern, 2);
+ p += 2;
+ bytes -= 2;
+ fix += 2;
+ }
+ fragp->fr_fix += fix;
+}
+
+/* Read a comma separated incrementing list of register names
+ and form a bit mask of upto 15 registers 0..14. */
+
+static const char *
+parse_reglist (const char * s, int * mask)
+{
+ int regmask = 0;
+
+ while (*s)
+ {
+ long value;
+
+ while (*s == ' ')
+ ++s;
+
+ /* Parse a list with "," or "}" as limiters. */
+ const char *errmsg
+ = cgen_parse_keyword (gas_cgen_cpu_desc, &s,
+ &epiphany_cgen_opval_gr_names, &value);
+ if (errmsg)
+ return errmsg;
+
+ if (value > 15)
+ return _("register number too large for push/pop");
+
+ regmask |= 1 << value;
+ if (regmask < *mask)
+ return _("register is out of order");
+ *mask |= regmask;
+
+ while (*s==' ')
+ ++s;
+
+ if (*s == '}')
+ return NULL;
+ else if (*s++ == ',')
+ continue;
+ else
+ return _("bad register list");
+ }
+
+ return _("malformed reglist in push/pop");
+}
+
+
+void
+md_assemble (char *str)
+{
+ epiphany_insn insn;
+ char *errmsg = 0;
+ const char * pperr = 0;
+ int regmask=0, push=0, pop=0;
+
+ memset (&insn, 0, sizeof (insn));
+
+ /* Special-case push/pop instruction macros. */
+ if (0 == strncmp (str, "push {", 6))
+ {
+ char * s = str + 6;
+ push = 1;
+ pperr = parse_reglist (s, &regmask);
+ }
+ else if (0 == strncmp (str, "pop {", 5))
+ {
+ char * s = str + 5;
+ pop = 1;
+ pperr = parse_reglist (s, &regmask);
+ }
+
+ if (pperr)
+ {
+ as_bad ("%s", pperr);
+ return;
+ }
+
+ if (push && regmask)
+ {
+ char buff[20];
+ int i,p ATTRIBUTE_UNUSED;
+
+ md_assemble ("mov r15,4");
+ md_assemble ("sub sp,sp,r15");
+
+ for (i = 0, p = 1; i <= 15; ++i, regmask >>= 1)
+ {
+ if (regmask == 1)
+ sprintf (buff, "str r%d,[sp]", i); /* Last one. */
+ else if (regmask & 1)
+ sprintf (buff, "str r%d,[sp],-r15", i);
+ else
+ continue;
+ md_assemble (buff);
+ }
+ return;
+ }
+ else if (pop && regmask)
+ {
+ char buff[20];
+ int i,p;
+
+ md_assemble ("mov r15,4");
+
+ for (i = 15, p = 1 << 15; i >= 0; --i, p >>= 1)
+ if (regmask & p)
+ {
+ sprintf (buff, "ldr r%d,[sp],+r15", i);
+ md_assemble (buff);
+ }
+ return;
+ }
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = epiphany_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, &insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ if (CGEN_INSN_BITSIZE (insn.insn) == 32)
+ {
+ /* 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);
+ }
+ else
+ {
+ if (CGEN_INSN_BITSIZE (insn.insn) != 16)
+ abort ();
+
+ insn.orig_insn = insn.insn;
+
+ gas_cgen_finish_insn (insn.orig_insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (&insn.fields),
+ 1 /* relax_p */, NULL);
+ }
+
+ /* Checks for behavioral restrictions on LD/ST instructions. */
+#define DISPMOD _("destination register modified by displacement-post-modified address")
+#define LDSTODD _("ldrd/strd requires even:odd register pair")
+
+ /* Helper macros for spliting apart instruction fields. */
+#define ADDR_POST_MODIFIED(i) (((i) >> 25) & 0x1)
+#define ADDR_SIZE(i) (((i) >> 5) & 3)
+#define ADDR_LOADSTORE(i) (((i) >> 4) & 0x1)
+
+ switch (insn.buffer[0] & 0xf)
+ {
+ /* Post-modify registers cannot be destinations. */
+ case OP4_LDSTR16P:
+ {
+ if (ADDR_LOADSTORE (insn.buffer[0]) == OP_LOAD)
+ if (insn.fields.f_rd == insn.fields.f_rn /* Postmodify dest. */
+ || (insn.fields.f_rd+1 == insn.fields.f_rn
+ && ADDR_SIZE (insn.buffer[0]) == OPW_DOUBLE))
+ {
+ as_bad ("%s", DISPMOD);
+ return;
+ }
+ if ((insn.fields.f_rd & 1) /* Odd-numbered register... */
+ && insn.fields.f_wordsize == OPW_DOUBLE) /* ...and 64 bit transfer. */
+ {
+ as_bad ("%s", LDSTODD);
+ return;
+ }
+ break;
+ }
+
+ case OP4_LDSTRP:
+ {
+ if (ADDR_LOADSTORE (insn.buffer[0]) == OP_LOAD) /* A load. */
+ if (insn.fields.f_rd6 == insn.fields.f_rn6 /* Postmodify dest. */
+ /* Check for regpair postindexed. */
+ || (insn.fields.f_rd6 + 1 == insn.fields.f_rn6
+ && ADDR_SIZE (insn.buffer[0]) == OPW_DOUBLE))
+ {
+ as_bad ("%s", DISPMOD);
+ return;
+ }
+ if ((insn.fields.f_rd6 & 1) && ADDR_SIZE (insn.buffer[0]) == OPW_DOUBLE)
+ /* Lsb of RD odd and 64 bit transfer. */
+ {
+ as_bad ("%s", LDSTODD);
+ return;
+ }
+ break;
+ }
+
+ case OP4_LDSTR16X:
+ case OP4_LDSTR16D:
+ {
+ /* Check for unaligned load/store double. */
+ if ((insn.fields.f_rd & 1) && ADDR_SIZE (insn.buffer[0]) == OPW_DOUBLE)
+ /* Lsb of RD odd and 64 bit transfer. */
+ {
+ as_bad ("%s", LDSTODD);
+ return;
+ }
+ break;
+ }
+
+ case OP4_LDSTRD:
+ {
+ /* Check for load to post-modified register. */
+ if (ADDR_LOADSTORE (insn.buffer[0]) == OP_LOAD /* A load. */
+ && ADDR_POST_MODIFIED (insn.buffer[0]) == PMOD_POST /* Post-mod. */
+ && (insn.fields.f_rd6 == insn.fields.f_rn6
+ || (insn.fields.f_rd6+1 == insn.fields.f_rn6
+ && ADDR_SIZE (insn.buffer[0]) == OPW_DOUBLE)))
+ {
+ as_bad ("%s", DISPMOD);
+ return;
+ }
+ }
+ /* fall-thru. */
+
+ case OP4_LDSTRX:
+ {
+ /* Check for unaligned load/store double. */
+ if ((insn.fields.f_rd6 & 1) && ADDR_SIZE (insn.buffer[0]) == OPW_DOUBLE)
+ {
+ as_bad ("%s", LDSTODD);
+ return;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer++;
+ expression (expressionP);
+ }
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+
+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 add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one. */
+
+ /* The first entry must be unused because an `rlx_more' value of zero ends
+ each list. */
+ {1, 1, 0, EPIPHANY_RELAX_NONE},
+ {0, 0, 0, EPIPHANY_RELAX_NONE}, /* Also a dummy entry to indicate we need to expand codes. */
+
+ /* The displacement used by GAS is from the end of the 2 byte insn,
+ so we subtract 2 from the following. */
+ /* 16 bit insn, 8 bit disp -> +127 words, -128 words. */
+ {0x00000100 - 1 - 2, -0x00000100 - 2, 0, EPIPHANY_RELAX_BRANCH_LONG },
+ /* 32 bit insn, 24 bit disp -> 25 bit range. */
+ {0x01000000 - 1 - 2, -0x01000000 - 2, 2, EPIPHANY_RELAX_NONE },
+
+ /* addi/subi 3 bits -4..+3. */
+ { 3, -4,0, EPIPHANY_RELAX_ARITH_SIMM11 },
+ /* addi/subi 11 bits. */
+ { 1023, -1024,2, EPIPHANY_RELAX_NONE },
+
+ /* mov r,imm8. */
+ { 255, 0,0, EPIPHANY_RELAX_MOV_IMM16 },
+ /* mov r,imm16. */
+ { 65535, 0,2, EPIPHANY_RELAX_NONE },
+
+ /* ld/st rd,[rn,imm3]. */
+ { 7, 0,0, EPIPHANY_RELAX_LDST_IMM11},
+ /* ld/st rd,[rn,imm11]. */
+ { 2047, 0,2, EPIPHANY_RELAX_NONE }
+
+};
+
+static const EPIPHANY_RELAX_TYPES relax_insn[] =
+{
+ EPIPHANY_RELAX_BRANCH_SHORT, /* OP4_BRANCH16 */
+ EPIPHANY_RELAX_NONE, /* OP4_LDSTR16X */
+ EPIPHANY_RELAX_NONE, /* OP4_FLOW16 */
+ EPIPHANY_RELAX_ARITH_SIMM3, /* OP4_IMM16 - special */
+ EPIPHANY_RELAX_LDST_IMM3, /* OP4_LDSTR16D */
+ EPIPHANY_RELAX_NONE, /* OP4_LDSTR126P */
+ EPIPHANY_RELAX_NONE, /* OP4_LSHIFT16 */
+ EPIPHANY_RELAX_NONE, /* OP4_DSP16 */
+ EPIPHANY_RELAX_BRANCH_LONG, /* OP4_BRANCH */
+ EPIPHANY_RELAX_NONE, /* OP4_LDSTRX */
+ EPIPHANY_RELAX_NONE, /* OP4_ALU16 */
+ EPIPHANY_RELAX_ARITH_SIMM11, /* OP4_IMM32 - special */
+ EPIPHANY_RELAX_LDST_IMM11, /* OP4_LDSTRD */
+ EPIPHANY_RELAX_NONE, /* OP4_LDSTRP */
+ EPIPHANY_RELAX_NONE, /* OP4_ASHIFT16 */
+ EPIPHANY_RELAX_NONE /* OP4_MISC */
+};
+
+long
+epiphany_relax_frag (segT segment, fragS *fragP, long stretch)
+{
+ /* Address of branch insn. */
+ long address ATTRIBUTE_UNUSED = fragP->fr_address + fragP->fr_fix - 2;
+ long growth = 0;
+
+ if (fragP->fr_subtype == EPIPHANY_RELAX_NEED_RELAXING)
+ {
+ EPIPHANY_RELAX_TYPES subtype = relax_insn [*fragP->fr_opcode & 0xf];
+
+ /* Special cases add/sub vs mov immediates. */
+ if (subtype == EPIPHANY_RELAX_ARITH_SIMM3)
+ {
+ if ((*fragP->fr_opcode & 0x10) == 0)
+ subtype = EPIPHANY_RELAX_MOV_IMM8;
+ }
+ else if (subtype == EPIPHANY_RELAX_ARITH_SIMM11)
+ {
+ if ((*fragP->fr_opcode & 0x10) == 0)
+ subtype = EPIPHANY_RELAX_MOV_IMM16;
+ }
+
+ /* Remember refinements for the future. */
+ fragP->fr_subtype = subtype;
+ }
+
+ growth = relax_frag (segment, fragP, stretch);
+
+ return growth;
+}
+
+/* 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)
+{
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the reloc as insn
+ alignment requirements may move the insn about. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ /* The symbol is undefined in this segment. Change the
+ relaxation subtype to the max allowable and leave all further
+ handling to md_convert_frag. */
+
+ EPIPHANY_RELAX_TYPES subtype;
+ /* We haven't relaxed this at all, so the relaxation type may be
+ completely wrong. Set the subtype correctly. */
+ epiphany_relax_frag (segment, fragP, 0);
+ subtype = fragP->fr_subtype;
+
+ switch (subtype)
+ {
+ case EPIPHANY_RELAX_LDST_IMM3:
+ subtype = EPIPHANY_RELAX_LDST_IMM11;
+ break;
+ case EPIPHANY_RELAX_BRANCH_SHORT:
+ subtype = EPIPHANY_RELAX_BRANCH_LONG;
+ break;
+ case EPIPHANY_RELAX_MOV_IMM8:
+ subtype = EPIPHANY_RELAX_MOV_IMM16;
+ break;
+ case EPIPHANY_RELAX_ARITH_SIMM3:
+ subtype = EPIPHANY_RELAX_ARITH_SIMM11;
+ break;
+
+ default:
+ break;
+ }
+
+ fragP->fr_subtype = subtype;
+
+ {
+ const CGEN_INSN *insn;
+ int i;
+
+ /* Update the recorded insn. */
+
+ for (i = 0, insn = fragP->fr_cgen.insn; i < 4; i++, insn++)
+ {
+ if ((strcmp (CGEN_INSN_MNEMONIC (insn),
+ CGEN_INSN_MNEMONIC (fragP->fr_cgen.insn))
+ == 0)
+ && CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_RELAXED))
+ break;
+ }
+
+ if (i == 4)
+ abort ();
+
+ fragP->fr_cgen.insn = insn;
+ }
+ }
+
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec,
+ fragS *fragP)
+{
+ char *opcode;
+ char *displacement;
+ int target_address;
+ int opcode_address;
+ int extension;
+ int addend;
+ int opindx = -1;
+
+ opcode = fragP->fr_opcode;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - 2;
+ extension = 0;
+ displacement = &opcode[1];
+
+ /* Set up any addend necessary for branches. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ /* Symbol must be resolved by linker. */
+ if (fragP->fr_offset & 1)
+ as_warn (_("Addend to unresolved symbol not on word boundary."));
+ addend = 0;
+ }
+ else
+ {
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+ addend = (target_address - (opcode_address & -2));
+ }
+
+ /* Do all the housekeeping for frag conversions. */
+ switch (fragP->fr_subtype)
+ {
+ case EPIPHANY_RELAX_ARITH_SIMM11:
+ *opcode |= OP4_IMM32;
+ displacement = &opcode[0];
+ extension += 3;
+
+ addend
+ = (((addend & 0x7) << 7)
+ | opcode[0]
+ | ((addend & 0x7f8) << 13)
+ | (opcode[1] << 8)
+ | (opcode[2] << 16));
+
+ opindx = EPIPHANY_OPERAND_SIMM11;
+ break;
+
+ case EPIPHANY_RELAX_BRANCH_LONG:
+ /* Branches differ only in low nibble of instruction being 8 not 0.
+ 24 bit displacement goes to bytes 1..3 . */
+ *opcode |= OP4_BRANCH;
+ extension += 2;
+
+ addend >>= 1; /* Convert to word offset. */
+ opindx = EPIPHANY_OPERAND_SIMM24;
+ break;
+
+ case EPIPHANY_RELAX_MOV_IMM16:
+ *opcode |= OP4_IMM32;
+ extension += 3;
+
+ addend
+ = (((addend & 0xff00) << 12)
+ | (opcode[2] << 16)
+ | ((addend & 0x00ff) << 5)
+ | (opcode[1] << 8)
+ | opcode[0]);
+ displacement = &opcode[0];
+ opindx = EPIPHANY_OPERAND_IMM16;
+ break;
+
+ case EPIPHANY_RELAX_LDST_IMM11:
+ *opcode |= OP4_LDSTRD;
+ displacement = &opcode[0];
+ extension += 3;
+
+ if (addend < 0)
+ /* Convert twos-complement address value to sign-magnitude. */
+ addend = (-addend & 0x7ff) | 0x800;
+
+ addend
+ = (((addend & 0x7) << 5)
+ | opcode[0]
+ | ((addend & 0xff8) << 13)
+ | (opcode[1] << 8)
+ | (opcode[2] << 16));
+
+ opindx = EPIPHANY_OPERAND_DISP11;
+ break;
+
+ case EPIPHANY_RELAX_ARITH_SIMM3:
+ addend = ((addend & 7) << 5) | opcode[0];
+ opindx = EPIPHANY_OPERAND_SIMM3;
+ break;
+
+ case EPIPHANY_RELAX_LDST_IMM3:
+ addend = ((addend & 7) << 5) | opcode[0];
+ opindx = EPIPHANY_OPERAND_DISP3;
+ break;
+
+ case EPIPHANY_RELAX_BRANCH_SHORT:
+ addend >>= 1; /* Convert to a word offset. */
+ displacement = & opcode[1];
+ opindx = EPIPHANY_OPERAND_SIMM8;
+ break;
+
+ case EPIPHANY_RELAX_MOV_IMM8:
+ addend
+ = (((addend & 0xff) << 5)
+ | opcode[0]
+ | (opcode[1] << 8));
+ opindx = EPIPHANY_OPERAND_IMM8;
+ break;
+
+ case EPIPHANY_RELAX_NONE:
+ case EPIPHANY_RELAX_NEED_RELAXING:
+ default: /* Anything else? */
+ as_bad ("unrecognized fragment subtype");
+ break;
+ }
+
+ /* Create a relocation for symbols that must be resolved by the linker.
+ Otherwise output the completed insn. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ fixS *fixP;
+ const CGEN_OPERAND *operand
+ = cgen_operand_lookup_by_num (gas_cgen_cpu_desc, opindx);
+ bfd_reloc_code_real_type reloc_type;
+
+ gas_assert (fragP->fr_cgen.insn != 0);
+
+ reloc_type = md_cgen_lookup_reloc (fragP->fr_cgen.insn, operand, NULL);
+
+ fixP = gas_cgen_record_fixup (fragP,
+ /* Offset of insn in frag. */
+ (opcode - fragP->fr_literal),
+ fragP->fr_cgen.insn,
+ CGEN_INSN_BITSIZE (fragP->fr_cgen.insn) / 8,
+ operand,
+ reloc_type,
+ fragP->fr_symbol, fragP->fr_offset);
+ fixP->fx_r_type = fixP->fx_cgen.opinfo;
+ }
+
+ md_number_to_chars (displacement, (valueT) addend, extension + 1);
+
+ fragP->fr_fix += (extension & -2); /* 0,2 or 4 bytes added. */
+}
+
+
+/* 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)
+ || S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)))
+ 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)
+{
+ switch (operand->type)
+ {
+ case EPIPHANY_OPERAND_SIMM11:
+ return BFD_RELOC_EPIPHANY_SIMM11;
+ case EPIPHANY_OPERAND_DISP11:
+ return BFD_RELOC_EPIPHANY_IMM11;
+
+ case EPIPHANY_OPERAND_SIMM8:
+ return BFD_RELOC_EPIPHANY_SIMM8;
+ case EPIPHANY_OPERAND_SIMM24:
+ return BFD_RELOC_EPIPHANY_SIMM24;
+
+ case EPIPHANY_OPERAND_IMM8:
+ return BFD_RELOC_EPIPHANY_IMM8;
+
+ case EPIPHANY_OPERAND_IMM16:
+ if (0 == strcmp ("movt", CGEN_INSN_MNEMONIC (insn)))
+ return BFD_RELOC_EPIPHANY_HIGH;
+ else if (0 == strcmp ("mov", CGEN_INSN_MNEMONIC (insn)))
+ return BFD_RELOC_EPIPHANY_LOW;
+ else
+ as_bad ("unknown imm16 operand");
+ /* fall-thru */
+
+ default:
+ break;
+ }
+ return BFD_RELOC_NONE;
+}
+
+
+/* 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)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+/* Return true if can adjust the reloc to be relative to its section
+ (such as .data) instead of relative to some symbol. */
+
+bfd_boolean
+epiphany_fix_adjustable (fixS *fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = fixP->fx_cgen.insn;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand =
+ cgen_operand_lookup_by_num (gas_cgen_cpu_desc, opindex);
+
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ if (fixP->fx_addsy == NULL)
+ return TRUE;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return FALSE;
+
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return FALSE;
+
+ if (pic_code
+ && (reloc_type == BFD_RELOC_EPIPHANY_SIMM24
+ || reloc_type == BFD_RELOC_EPIPHANY_SIMM8
+ || reloc_type == BFD_RELOC_EPIPHANY_HIGH
+ || reloc_type == BFD_RELOC_EPIPHANY_LOW))
+ return FALSE;
+
+ /* Since we don't use partial_inplace, we must not reduce symbols in
+ mergable sections to their section symbol. */
+ if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+void
+epiphany_elf_final_processing (void)
+{
+ elf_elfheader (stdoutput)->e_flags |= epiphany_flags;
+}
+
+int
+epiphany_cgen_parse_fix_exp (int opinfo, expressionS *exp ATTRIBUTE_UNUSED)
+{
+ LITTLENUM_TYPE words[2];
+
+ switch (opinfo)
+ {
+ case BFD_RELOC_EPIPHANY_LOW:
+ case BFD_RELOC_EPIPHANY_HIGH:
+ break;
+ default:
+ return opinfo;
+ }
+
+ /* Doing a %LOW or %HIGH. */
+ switch (exp->X_op)
+ {
+ default:
+ return opinfo;
+ case O_big: /* Bignum. */
+ if (exp->X_add_number > 0) /* Integer value too large. */
+ return opinfo;
+ }
+
+ /* Convert to SP number. */
+ gen_to_words (words, 2, 8L);
+ exp->X_add_number = words[1] | (words[0] << 16);
+ exp->X_op = O_constant;
+ return opinfo;
+}
diff --git a/gas/config/tc-epiphany.h b/gas/config/tc-epiphany.h
new file mode 100644
index 0000000..ddfb475
--- /dev/null
+++ b/gas/config/tc-epiphany.h
@@ -0,0 +1,102 @@
+/* tc-epiphany.h -- Header file for tc-epiphany.c.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Contributed by Embecosm on behalf of Adapteva, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_EPIPHANY
+
+#define LISTING_HEADER "EPIPHANY GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_epiphany
+
+#define TARGET_FORMAT "elf32-epiphany"
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define LITERAL_PREFIXDOLLAR_HEX
+#define LITERAL_PREFIXPERCENT_BIN
+#define DOUBLESLASH_LINE_COMMENTS
+
+#define GAS_CGEN_PCREL_R_TYPE(R_TYPE) gas_cgen_pcrel_r_type (R_TYPE)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define tc_fix_adjustable(FIX) epiphany_fix_adjustable (FIX)
+extern bfd_boolean epiphany_fix_adjustable (struct fix *);
+
+extern long md_pcrel_from_section (struct fix *, segT);
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP,SEC)
+
+#define TC_HANDLES_FX_DONE
+
+#define elf_tc_final_processing epiphany_elf_final_processing
+extern void epiphany_elf_final_processing (void);
+
+#define md_elf_section_flags epiphany_elf_section_flags
+extern int epiphany_elf_section_flags (int, int, int);
+
+#define md_operand(x) epiphany_cgen_md_operand (x)
+extern void epiphany_cgen_md_operand (expressionS *);
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define TC_CGEN_MAX_RELAX(insn, len) 4
+
+#define O_PIC_reloc O_md1
+
+#define TC_CGEN_PARSE_FIX_EXP(opinfo, exp) \
+ epiphany_cgen_parse_fix_exp (opinfo, exp)
+extern int epiphany_cgen_parse_fix_exp (int, expressionS *);
+
+#define HANDLE_ALIGN(f) epiphany_handle_align (f)
+extern void epiphany_handle_align (fragS *);
+
+#define TARGET_FORMAT "elf32-epiphany"
+
+#define md_relax_frag epiphany_relax_frag
+
+extern long epiphany_relax_frag (segT, fragS *, long);
+
+/* If you don't define md_relax_frag, md_cgen_record_fixup_exp
+ but do have TC_GENERIC_RELAX_TABLE gas will do the relaxation for you.
+
+ If we have to add support for %LO and %HI relocations, we probably need
+ to define the fixup_exp function to generate fancier relocations. */
+
+/* For 8 vs 24 bit branch selection. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+
+#define md_apply_fix epiphany_apply_fix
+#include "write.h"
+
+extern void epiphany_apply_fix (fixS *fixP, valueT *valP, segT seg);
diff --git a/gas/config/tc-fr30.c b/gas/config/tc-fr30.c
new file mode 100644
index 0000000..07c2b8f
--- /dev/null
+++ b/gas/config/tc-fr30.c
@@ -0,0 +1,419 @@
+/* tc-fr30.c -- Assembler for the Fujitsu FR30.
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/fr30-desc.h"
+#include "opcodes/fr30-opc.h"
+#include "cgen.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];
+}
+fr30_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 FR30_SHORTOPTS ""
+const char * md_shortopts = FR30_SHORTOPTS;
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _(" FR30 specific command line options:\n"));
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { NULL, NULL, 0 }
+};
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = fr30_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ fr30_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);
+}
+
+void
+md_assemble (char *str)
+{
+ static int last_insn_had_delay_slot = 0;
+ fr30_insn insn;
+ char *errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = fr30_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", 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);
+
+ /* Warn about invalid insns in delay slots. */
+ if (last_insn_had_delay_slot
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_NOT_IN_DELAY_SLOT))
+ as_warn (_("Instruction %s not allowed in a delay slot."),
+ CGEN_INSN_NAME (insn.insn));
+
+ last_insn_had_delay_slot
+ = CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionS * expressionP)
+{
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+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 NULL;
+}
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+
+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 add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one. */
+
+ /* The first entry must be unused because an `rlx_more' value of zero ends
+ each list. */
+ {1, 1, 0, 0},
+
+ /* The displacement used by GAS is from the end of the 2 byte insn,
+ so we subtract 2 from the following. */
+ /* 16 bit insn, 8 bit disp -> 10 bit range.
+ This doesn't handle a branch in the right slot at the border:
+ the "& -4" isn't taken into account. It's not important enough to
+ complicate things over it, so we subtract an extra 2 (or + 2 in -ve
+ case). */
+ {511 - 2 - 2, -512 - 2 + 2, 0, 2 },
+ /* 32 bit insn, 24 bit disp -> 26 bit range. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 2, 0 },
+ /* Same thing, but with leading nop for alignment. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 4, 0 }
+};
+
+/* 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)
+{
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the reloc as insn
+ alignment requirements may move the insn about. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
+ {
+ /* The symbol is undefined in this segment.
+ Change the relaxation subtype to the max allowable and leave
+ all further handling to md_convert_frag. */
+ fragP->fr_subtype = 2;
+
+ {
+ const CGEN_INSN * insn;
+ int i;
+
+ /* Update the recorded insn.
+ Fortunately we don't have to look very far.
+ FIXME: Change this to record in the instruction the next higher
+ relaxable insn to use. */
+ for (i = 0, insn = fragP->fr_cgen.insn; i < 4; i++, insn++)
+ {
+ if ((strcmp (CGEN_INSN_MNEMONIC (insn),
+ CGEN_INSN_MNEMONIC (fragP->fr_cgen.insn))
+ == 0)
+ && CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_RELAXED))
+ break;
+ }
+ if (i == 4)
+ abort ();
+
+ fragP->fr_cgen.insn = insn;
+ return 2;
+ }
+ }
+
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+}
+
+/* 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) & ~1;
+}
+
+/* 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)
+{
+ switch (operand->type)
+ {
+ case FR30_OPERAND_LABEL9: fixP->fx_pcrel = 1; return BFD_RELOC_FR30_9_PCREL;
+ case FR30_OPERAND_LABEL12: fixP->fx_pcrel = 1; return BFD_RELOC_FR30_12_PCREL;
+ case FR30_OPERAND_DISP10: return BFD_RELOC_FR30_10_IN_8;
+ case FR30_OPERAND_DISP9: return BFD_RELOC_FR30_9_IN_8;
+ case FR30_OPERAND_DISP8: return BFD_RELOC_FR30_8_IN_8;
+ case FR30_OPERAND_UDISP6: return BFD_RELOC_FR30_6_IN_4;
+ case FR30_OPERAND_I8: return BFD_RELOC_8;
+ case FR30_OPERAND_I32: return BFD_RELOC_FR30_48;
+ case FR30_OPERAND_I20: return BFD_RELOC_FR30_20;
+ default : /* Avoid -Wall warning. */
+ break;
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* 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_bigendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Worker function for fr30_is_colon_insn(). */
+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
+ LDI:8, LDI:20, LDI:32, CALL:D. JMP:D, RET:D or Bcc:D instruction.
+ 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
+fr30_is_colon_insn (char * start)
+{
+ char * i_l_p = input_line_pointer;
+
+ /* Check to see if the symbol parsed so far is 'ldi'. */
+ if ( (start[0] != 'l' && start[0] != 'L')
+ || (start[1] != 'd' && start[1] != 'D')
+ || (start[2] != 'i' && start[2] != 'I')
+ || start[3] != 0)
+ {
+ /* Nope - check to see a 'd' follows the colon. */
+ if ( (i_l_p[1] == 'd' || i_l_p[1] == 'D')
+ && (i_l_p[2] == ' ' || i_l_p[2] == '\t' || i_l_p[2] == '\n'))
+ {
+ /* Yup - it might be delay slot instruction. */
+ int i;
+ static char * delay_insns [] =
+ {
+ "call", "jmp", "ret", "bra", "bno",
+ "beq", "bne", "bc", "bnc", "bn",
+ "bp", "bv", "bnv", "blt", "bge",
+ "ble", "bgt", "bls", "bhi"
+ };
+
+ for (i = sizeof (delay_insns) / sizeof (delay_insns[0]); i--;)
+ {
+ char * insn = delay_insns[i];
+ int len = strlen (insn);
+
+ if (start [len] != 0)
+ continue;
+
+ while (len --)
+ if (TOLOWER (start [len]) != insn [len])
+ break;
+
+ if (len == -1)
+ return restore_colon (1);
+ }
+ }
+
+ /* Nope - it is a normal label. */
+ return 0;
+ }
+
+ /* Check to see if the text following the colon is '8'. */
+ if (i_l_p[1] == '8' && (i_l_p[2] == ' ' || i_l_p[2] == '\t'))
+ return restore_colon (2);
+
+ /* Check to see if the text following the colon is '20'. */
+ else if (i_l_p[1] == '2' && i_l_p[2] =='0' && (i_l_p[3] == ' ' || i_l_p[3] == '\t'))
+ return restore_colon (3);
+
+ /* Check to see if the text following the colon is '32'. */
+ else if (i_l_p[1] == '3' && i_l_p[2] =='2' && (i_l_p[3] == ' ' || i_l_p[3] == '\t'))
+ return restore_colon (3);
+
+ return 0;
+}
+
+bfd_boolean
+fr30_fix_adjustable (fixS * fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
diff --git a/gas/config/tc-fr30.h b/gas/config/tc-fr30.h
new file mode 100644
index 0000000..3ed399b
--- /dev/null
+++ b/gas/config/tc-fr30.h
@@ -0,0 +1,65 @@
+/* tc-fr30.h -- Header file for tc-fr30.c.
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_FR30
+
+#define LISTING_HEADER "FR30 GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_fr30
+
+#define TARGET_FORMAT "elf32-fr30"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs. */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_apply_fix gas_cgen_md_apply_fix
+
+#define tc_fix_adjustable(FIX) fr30_fix_adjustable (FIX)
+struct fix;
+extern bfd_boolean fr30_fix_adjustable (struct fix *);
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* For 8 vs 16 vs 32 bit branch selection. */
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+extern const struct relax_type md_relax_table[];
+
+/* We need a special version of the TC_START_LABEL macro so that we
+ allow the LDI:8, LDI:20, LDI:32 and delay slot instructions to be
+ parsed as such. We need to be able to change the contents of
+ the local variable 'c' which is passed to this macro as 'character'. */
+#define TC_START_LABEL(character, s, i_l_p) \
+ ((character) != ':' ? 0 : (character = fr30_is_colon_insn (s)) ? 0 : ((character = ':'), 1))
+extern char fr30_is_colon_insn (char *);
diff --git a/gas/config/tc-frv.c b/gas/config/tc-frv.c
new file mode 100644
index 0000000..faaa1c2
--- /dev/null
+++ b/gas/config/tc-frv.c
@@ -0,0 +1,1833 @@
+/* tc-frv.c -- Assembler for the Fujitsu FRV.
+ Copyright (C) 2002-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/frv-desc.h"
+#include "opcodes/frv-opc.h"
+#include "cgen.h"
+#include "libbfd.h"
+#include "elf/common.h"
+#include "elf/frv.h"
+#include "dwarf2dbg.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];
+}
+frv_insn;
+
+enum vliw_insn_type
+{
+ VLIW_GENERIC_TYPE, /* Don't care about this insn. */
+ VLIW_BRANCH_TYPE, /* A Branch. */
+ VLIW_LABEL_TYPE, /* A Label. */
+ VLIW_NOP_TYPE, /* A NOP. */
+ VLIW_BRANCH_HAS_NOPS /* A Branch that requires NOPS. */
+};
+
+/* We're going to use these in the fr_subtype field to mark
+ whether to keep inserted nops. */
+
+#define NOP_KEEP 1 /* Keep these NOPS. */
+#define NOP_DELETE 2 /* Delete these NOPS. */
+
+#define DO_COUNT TRUE
+#define DONT_COUNT FALSE
+
+/* A list of insns within a VLIW insn. */
+struct vliw_insn_list
+{
+ /* The type of this insn. */
+ enum vliw_insn_type type;
+
+ /* The corresponding gas insn information. */
+ const CGEN_INSN *insn;
+
+ /* For branches and labels, the symbol that is referenced. */
+ symbolS *sym;
+
+ /* For branches, the frag containing the single nop that was generated. */
+ fragS *snop_frag;
+
+ /* For branches, the frag containing the double nop that was generated. */
+ fragS *dnop_frag;
+
+ /* Pointer to raw data for this insn. */
+ char *address;
+
+ /* Next insn in list. */
+ struct vliw_insn_list *next;
+};
+
+static struct vliw_insn_list single_nop_insn = {
+ VLIW_NOP_TYPE, NULL, NULL, NULL, NULL, NULL, NULL };
+
+static struct vliw_insn_list double_nop_insn = {
+ VLIW_NOP_TYPE, NULL, NULL, NULL, NULL, NULL, NULL };
+
+struct vliw_chain
+{
+ int num;
+ int insn_count;
+ struct vliw_insn_list *insn_list;
+ struct vliw_chain *next;
+};
+
+static struct vliw_chain *vliw_chain_top;
+static struct vliw_chain *current_vliw_chain;
+static struct vliw_chain *previous_vliw_chain;
+static struct vliw_insn_list *current_vliw_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";
+
+static FRV_VLIW vliw;
+
+/* Default machine */
+
+#ifdef DEFAULT_CPU_FRV
+#define DEFAULT_MACHINE bfd_mach_frv
+#define DEFAULT_FLAGS EF_FRV_CPU_GENERIC
+
+#else
+#ifdef DEFAULT_CPU_FR300
+#define DEFAULT_MACHINE bfd_mach_fr300
+#define DEFAULT_FLAGS EF_FRV_CPU_FR300
+
+#else
+#ifdef DEFAULT_CPU_SIMPLE
+#define DEFAULT_MACHINE bfd_mach_frvsimple
+#define DEFAULT_FLAGS EF_FRV_CPU_SIMPLE
+
+#else
+#ifdef DEFAULT_CPU_TOMCAT
+#define DEFAULT_MACHINE bfd_mach_frvtomcat
+#define DEFAULT_FLAGS EF_FRV_CPU_TOMCAT
+
+#else
+#ifdef DEFAULT_CPU_FR400
+#define DEFAULT_MACHINE bfd_mach_fr400
+#define DEFAULT_FLAGS EF_FRV_CPU_FR400
+
+#else
+#ifdef DEFAULT_CPU_FR550
+#define DEFAULT_MACHINE bfd_mach_fr550
+#define DEFAULT_FLAGS EF_FRV_CPU_FR550
+
+#else
+#define DEFAULT_MACHINE bfd_mach_fr500
+#define DEFAULT_FLAGS EF_FRV_CPU_FR500
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+#ifdef TE_LINUX
+# define DEFAULT_FDPIC EF_FRV_FDPIC
+#else
+# define DEFAULT_FDPIC 0
+#endif
+
+static unsigned long frv_mach = bfd_mach_frv;
+static bfd_boolean fr400_audio;
+
+/* Flags to set in the elf header */
+static flagword frv_flags = DEFAULT_FLAGS | DEFAULT_FDPIC;
+
+static int frv_user_set_flags_p = 0;
+static int frv_pic_p = 0;
+static const char *frv_pic_flag = DEFAULT_FDPIC ? "-mfdpic" : (const char *)0;
+
+/* Print tomcat-specific debugging info. */
+static int tomcat_debug = 0;
+
+/* Tomcat-specific NOP statistics. */
+static int tomcat_stats = 0;
+static int tomcat_doubles = 0;
+static int tomcat_singles = 0;
+
+/* Forward reference to static functions */
+static void frv_set_flags (int);
+static void frv_pic_ptr (int);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "eflags", frv_set_flags, 0 },
+ { "word", cons, 4 },
+ { "picptr", frv_pic_ptr, 4 },
+ { NULL, NULL, 0 }
+};
+
+
+#define FRV_SHORTOPTS "G:"
+const char * md_shortopts = FRV_SHORTOPTS;
+
+#define OPTION_GPR_32 (OPTION_MD_BASE)
+#define OPTION_GPR_64 (OPTION_MD_BASE + 1)
+#define OPTION_FPR_32 (OPTION_MD_BASE + 2)
+#define OPTION_FPR_64 (OPTION_MD_BASE + 3)
+#define OPTION_SOFT_FLOAT (OPTION_MD_BASE + 4)
+#define OPTION_DWORD_YES (OPTION_MD_BASE + 5)
+#define OPTION_DWORD_NO (OPTION_MD_BASE + 6)
+#define OPTION_DOUBLE (OPTION_MD_BASE + 7)
+#define OPTION_NO_DOUBLE (OPTION_MD_BASE + 8)
+#define OPTION_MEDIA (OPTION_MD_BASE + 9)
+#define OPTION_NO_MEDIA (OPTION_MD_BASE + 10)
+#define OPTION_CPU (OPTION_MD_BASE + 11)
+#define OPTION_PIC (OPTION_MD_BASE + 12)
+#define OPTION_BIGPIC (OPTION_MD_BASE + 13)
+#define OPTION_LIBPIC (OPTION_MD_BASE + 14)
+#define OPTION_MULADD (OPTION_MD_BASE + 15)
+#define OPTION_NO_MULADD (OPTION_MD_BASE + 16)
+#define OPTION_TOMCAT_DEBUG (OPTION_MD_BASE + 17)
+#define OPTION_TOMCAT_STATS (OPTION_MD_BASE + 18)
+#define OPTION_PACK (OPTION_MD_BASE + 19)
+#define OPTION_NO_PACK (OPTION_MD_BASE + 20)
+#define OPTION_FDPIC (OPTION_MD_BASE + 21)
+#define OPTION_NOPIC (OPTION_MD_BASE + 22)
+
+struct option md_longopts[] =
+{
+ { "mgpr-32", no_argument, NULL, OPTION_GPR_32 },
+ { "mgpr-64", no_argument, NULL, OPTION_GPR_64 },
+ { "mfpr-32", no_argument, NULL, OPTION_FPR_32 },
+ { "mfpr-64", no_argument, NULL, OPTION_FPR_64 },
+ { "mhard-float", no_argument, NULL, OPTION_FPR_64 },
+ { "msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT },
+ { "mdword", no_argument, NULL, OPTION_DWORD_YES },
+ { "mno-dword", no_argument, NULL, OPTION_DWORD_NO },
+ { "mdouble", no_argument, NULL, OPTION_DOUBLE },
+ { "mno-double", no_argument, NULL, OPTION_NO_DOUBLE },
+ { "mmedia", no_argument, NULL, OPTION_MEDIA },
+ { "mno-media", no_argument, NULL, OPTION_NO_MEDIA },
+ { "mcpu", required_argument, NULL, OPTION_CPU },
+ { "mpic", no_argument, NULL, OPTION_PIC },
+ { "mPIC", no_argument, NULL, OPTION_BIGPIC },
+ { "mlibrary-pic", no_argument, NULL, OPTION_LIBPIC },
+ { "mmuladd", no_argument, NULL, OPTION_MULADD },
+ { "mno-muladd", no_argument, NULL, OPTION_NO_MULADD },
+ { "mtomcat-debug", no_argument, NULL, OPTION_TOMCAT_DEBUG },
+ { "mtomcat-stats", no_argument, NULL, OPTION_TOMCAT_STATS },
+ { "mpack", no_argument, NULL, OPTION_PACK },
+ { "mno-pack", no_argument, NULL, OPTION_NO_PACK },
+ { "mfdpic", no_argument, NULL, OPTION_FDPIC },
+ { "mnopic", no_argument, NULL, OPTION_NOPIC },
+ { NULL, no_argument, NULL, 0 },
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* What value to give to bfd_set_gp_size. */
+static int g_switch_value = 8;
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+
+ case 'G':
+ g_switch_value = atoi (arg);
+ if (! g_switch_value)
+ frv_flags |= EF_FRV_G0;
+ break;
+
+ case OPTION_GPR_32:
+ frv_flags = (frv_flags & ~EF_FRV_GPR_MASK) | EF_FRV_GPR_32;
+ break;
+
+ case OPTION_GPR_64:
+ frv_flags = (frv_flags & ~EF_FRV_GPR_MASK) | EF_FRV_GPR_64;
+ break;
+
+ case OPTION_FPR_32:
+ frv_flags = (frv_flags & ~EF_FRV_FPR_MASK) | EF_FRV_FPR_32;
+ break;
+
+ case OPTION_FPR_64:
+ frv_flags = (frv_flags & ~EF_FRV_FPR_MASK) | EF_FRV_FPR_64;
+ break;
+
+ case OPTION_SOFT_FLOAT:
+ frv_flags = (frv_flags & ~EF_FRV_FPR_MASK) | EF_FRV_FPR_NONE;
+ break;
+
+ case OPTION_DWORD_YES:
+ frv_flags = (frv_flags & ~EF_FRV_DWORD_MASK) | EF_FRV_DWORD_YES;
+ break;
+
+ case OPTION_DWORD_NO:
+ frv_flags = (frv_flags & ~EF_FRV_DWORD_MASK) | EF_FRV_DWORD_NO;
+ break;
+
+ case OPTION_DOUBLE:
+ frv_flags |= EF_FRV_DOUBLE;
+ break;
+
+ case OPTION_NO_DOUBLE:
+ frv_flags &= ~EF_FRV_DOUBLE;
+ break;
+
+ case OPTION_MEDIA:
+ frv_flags |= EF_FRV_MEDIA;
+ break;
+
+ case OPTION_NO_MEDIA:
+ frv_flags &= ~EF_FRV_MEDIA;
+ break;
+
+ case OPTION_MULADD:
+ frv_flags |= EF_FRV_MULADD;
+ break;
+
+ case OPTION_NO_MULADD:
+ frv_flags &= ~EF_FRV_MULADD;
+ break;
+
+ case OPTION_PACK:
+ frv_flags &= ~EF_FRV_NOPACK;
+ break;
+
+ case OPTION_NO_PACK:
+ frv_flags |= EF_FRV_NOPACK;
+ break;
+
+ case OPTION_CPU:
+ {
+ char *p;
+ int cpu_flags = EF_FRV_CPU_GENERIC;
+
+ /* Identify the processor type */
+ p = arg;
+ if (strcmp (p, "frv") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_GENERIC;
+ frv_mach = bfd_mach_frv;
+ }
+
+ else if (strcmp (p, "fr500") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR500;
+ frv_mach = bfd_mach_fr500;
+ }
+
+ else if (strcmp (p, "fr550") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR550;
+ frv_mach = bfd_mach_fr550;
+ }
+
+ else if (strcmp (p, "fr450") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR450;
+ frv_mach = bfd_mach_fr450;
+ }
+
+ else if (strcmp (p, "fr405") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR405;
+ frv_mach = bfd_mach_fr400;
+ fr400_audio = TRUE;
+ }
+
+ else if (strcmp (p, "fr400") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR400;
+ frv_mach = bfd_mach_fr400;
+ fr400_audio = FALSE;
+ }
+
+ else if (strcmp (p, "fr300") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_FR300;
+ frv_mach = bfd_mach_fr300;
+ }
+
+ else if (strcmp (p, "simple") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_SIMPLE;
+ frv_mach = bfd_mach_frvsimple;
+ frv_flags |= EF_FRV_NOPACK;
+ }
+
+ else if (strcmp (p, "tomcat") == 0)
+ {
+ cpu_flags = EF_FRV_CPU_TOMCAT;
+ frv_mach = bfd_mach_frvtomcat;
+ }
+
+ else
+ {
+ as_fatal (_("Unknown cpu -mcpu=%s"), arg);
+ return 0;
+ }
+
+ frv_flags = (frv_flags & ~EF_FRV_CPU_MASK) | cpu_flags;
+ }
+ break;
+
+ case OPTION_PIC:
+ frv_flags |= EF_FRV_PIC;
+ frv_pic_p = 1;
+ frv_pic_flag = "-fpic";
+ break;
+
+ case OPTION_BIGPIC:
+ frv_flags |= EF_FRV_BIGPIC;
+ frv_pic_p = 1;
+ frv_pic_flag = "-fPIC";
+ break;
+
+ case OPTION_LIBPIC:
+ frv_flags |= (EF_FRV_LIBPIC | EF_FRV_G0);
+ frv_pic_p = 1;
+ frv_pic_flag = "-mlibrary-pic";
+ g_switch_value = 0;
+ break;
+
+ case OPTION_FDPIC:
+ frv_flags |= EF_FRV_FDPIC;
+ frv_pic_flag = "-mfdpic";
+ break;
+
+ case OPTION_NOPIC:
+ frv_flags &= ~(EF_FRV_FDPIC | EF_FRV_PIC
+ | EF_FRV_BIGPIC | EF_FRV_LIBPIC);
+ frv_pic_flag = 0;
+ break;
+
+ case OPTION_TOMCAT_DEBUG:
+ tomcat_debug = 1;
+ break;
+
+ case OPTION_TOMCAT_STATS:
+ tomcat_stats = 1;
+ break;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("FRV specific command line options:\n"));
+ fprintf (stream, _("-G n Put data <= n bytes in the small data area\n"));
+ fprintf (stream, _("-mgpr-32 Mark generated file as only using 32 GPRs\n"));
+ fprintf (stream, _("-mgpr-64 Mark generated file as using all 64 GPRs\n"));
+ fprintf (stream, _("-mfpr-32 Mark generated file as only using 32 FPRs\n"));
+ fprintf (stream, _("-mfpr-64 Mark generated file as using all 64 FPRs\n"));
+ fprintf (stream, _("-msoft-float Mark generated file as using software FP\n"));
+ fprintf (stream, _("-mdword Mark generated file as using a 8-byte stack alignment\n"));
+ fprintf (stream, _("-mno-dword Mark generated file as using a 4-byte stack alignment\n"));
+ fprintf (stream, _("-mdouble Mark generated file as using double precision FP insns\n"));
+ fprintf (stream, _("-mmedia Mark generated file as using media insns\n"));
+ fprintf (stream, _("-mmuladd Mark generated file as using multiply add/subtract insns\n"));
+ fprintf (stream, _("-mpack Allow instructions to be packed\n"));
+ fprintf (stream, _("-mno-pack Do not allow instructions to be packed\n"));
+ fprintf (stream, _("-mpic Mark generated file as using small position independent code\n"));
+ fprintf (stream, _("-mPIC Mark generated file as using large position independent code\n"));
+ fprintf (stream, _("-mlibrary-pic Mark generated file as using position indepedent code for libraries\n"));
+ fprintf (stream, _("-mfdpic Assemble for the FDPIC ABI\n"));
+ fprintf (stream, _("-mnopic Disable -mpic, -mPIC, -mlibrary-pic and -mfdpic\n"));
+ fprintf (stream, _("-mcpu={fr500|fr550|fr400|fr405|fr450|fr300|frv|simple|tomcat}\n"));
+ fprintf (stream, _(" Record the cpu type\n"));
+ fprintf (stream, _("-mtomcat-stats Print out stats for tomcat workarounds\n"));
+ fprintf (stream, _("-mtomcat-debug Debug tomcat workarounds\n"));
+}
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = frv_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ frv_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 (frv_flags)
+ bfd_set_private_flags (stdoutput, frv_flags);
+
+ /* Set the machine type */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_frv, frv_mach);
+
+ /* Set up gp size so we can put local common items in .sbss */
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+ frv_vliw_reset (& vliw, frv_mach, frv_flags);
+}
+
+bfd_boolean
+frv_md_fdpic_enabled (void)
+{
+ return (frv_flags & EF_FRV_FDPIC) != 0;
+}
+
+int chain_num = 0;
+
+static struct vliw_insn_list *
+frv_insert_vliw_insn (bfd_boolean count)
+{
+ struct vliw_insn_list *vliw_insn_list_entry;
+ struct vliw_chain *vliw_chain_entry;
+
+ if (current_vliw_chain == NULL)
+ {
+ vliw_chain_entry = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ vliw_chain_entry->insn_count = 0;
+ vliw_chain_entry->insn_list = NULL;
+ vliw_chain_entry->next = NULL;
+ vliw_chain_entry->num = chain_num++;
+
+ if (!vliw_chain_top)
+ vliw_chain_top = vliw_chain_entry;
+ current_vliw_chain = vliw_chain_entry;
+ if (previous_vliw_chain)
+ previous_vliw_chain->next = vliw_chain_entry;
+ }
+
+ vliw_insn_list_entry = (struct vliw_insn_list *) xmalloc (sizeof (struct vliw_insn_list));
+ vliw_insn_list_entry->type = VLIW_GENERIC_TYPE;
+ vliw_insn_list_entry->insn = NULL;
+ vliw_insn_list_entry->sym = NULL;
+ vliw_insn_list_entry->snop_frag = NULL;
+ vliw_insn_list_entry->dnop_frag = NULL;
+ vliw_insn_list_entry->next = NULL;
+
+ if (count)
+ current_vliw_chain->insn_count++;
+
+ if (current_vliw_insn)
+ current_vliw_insn->next = vliw_insn_list_entry;
+ current_vliw_insn = vliw_insn_list_entry;
+
+ if (!current_vliw_chain->insn_list)
+ current_vliw_chain->insn_list = current_vliw_insn;
+
+ return vliw_insn_list_entry;
+}
+
+ /* Identify the following cases:
+
+ 1) A VLIW insn that contains both a branch and the branch destination.
+ This requires the insertion of two vliw instructions before the
+ branch. The first consists of two nops. The second consists of
+ a single nop.
+
+ 2) A single instruction VLIW insn which is the destination of a branch
+ that is in the next VLIW insn. This requires the insertion of a vliw
+ insn containing two nops before the branch.
+
+ 3) A double instruction VLIW insn which contains the destination of a
+ branch that is in the next VLIW insn. This requires the insertion of
+ a VLIW insn containing a single nop before the branch.
+
+ 4) A single instruction VLIW insn which contains branch destination (x),
+ followed by a single instruction VLIW insn which does not contain
+ the branch to (x), followed by a VLIW insn which does contain the branch
+ to (x). This requires the insertion of a VLIW insn containing a single
+ nop before the VLIW instruction containing the branch.
+
+ */
+#define FRV_IS_NOP(insn) (insn.buffer[0] == FRV_NOP_PACK || insn.buffer[0] == FRV_NOP_NOPACK)
+#define FRV_NOP_PACK 0x00880000 /* ori.p gr0,0,gr0 */
+#define FRV_NOP_NOPACK 0x80880000 /* ori gr0,0,gr0 */
+
+/* Check a vliw insn for an insn of type containing the sym passed in label_sym. */
+
+static struct vliw_insn_list *
+frv_find_in_vliw (enum vliw_insn_type vliw_insn_type,
+ struct vliw_chain *this_chain,
+ symbolS *label_sym)
+{
+
+ struct vliw_insn_list *the_insn;
+
+ if (!this_chain)
+ return NULL;
+
+ for (the_insn = this_chain->insn_list; the_insn; the_insn = the_insn->next)
+ {
+ if (the_insn->type == vliw_insn_type
+ && the_insn->sym == label_sym)
+ return the_insn;
+ }
+
+ return NULL;
+}
+
+enum vliw_nop_type
+{
+ /* A Vliw insn containing a single nop insn. */
+ VLIW_SINGLE_NOP,
+
+ /* A Vliw insn containing two nop insns. */
+ VLIW_DOUBLE_NOP,
+
+ /* Two vliw insns. The first containing two nop insns.
+ The second contain a single nop insn. */
+ VLIW_DOUBLE_THEN_SINGLE_NOP
+};
+
+static void
+frv_debug_tomcat (struct vliw_chain *start_chain)
+{
+ struct vliw_chain *this_chain;
+ struct vliw_insn_list *this_insn;
+ int i = 1;
+
+ for (this_chain = start_chain; this_chain; this_chain = this_chain->next, i++)
+ {
+ fprintf (stderr, "\nVliw Insn #%d, #insns: %d\n", i, this_chain->insn_count);
+
+ for (this_insn = this_chain->insn_list; this_insn; this_insn = this_insn->next)
+ {
+ if (this_insn->type == VLIW_LABEL_TYPE)
+ fprintf (stderr, "Label Value: %p\n", this_insn->sym);
+ else if (this_insn->type == VLIW_BRANCH_TYPE)
+ fprintf (stderr, "%s to %p\n", this_insn->insn->base->name, this_insn->sym);
+ else if (this_insn->type == VLIW_BRANCH_HAS_NOPS)
+ fprintf (stderr, "nop'd %s to %p\n", this_insn->insn->base->name, this_insn->sym);
+ else if (this_insn->type == VLIW_NOP_TYPE)
+ fprintf (stderr, "Nop\n");
+ else
+ fprintf (stderr, " %s\n", this_insn->insn->base->name);
+ }
+ }
+}
+
+static void
+frv_adjust_vliw_count (struct vliw_chain *this_chain)
+{
+ struct vliw_insn_list *this_insn;
+
+ this_chain->insn_count = 0;
+
+ for (this_insn = this_chain->insn_list;
+ this_insn;
+ this_insn = this_insn->next)
+ {
+ if (this_insn->type != VLIW_LABEL_TYPE)
+ this_chain->insn_count++;
+ }
+
+}
+
+/* Insert the desired nop combination in the vliw chain before insert_before_insn.
+ Rechain the vliw insn. */
+
+static struct vliw_chain *
+frv_tomcat_shuffle (enum vliw_nop_type this_nop_type,
+ struct vliw_chain *vliw_to_split,
+ struct vliw_insn_list *insert_before_insn)
+{
+
+ bfd_boolean pack_prev = FALSE;
+ struct vliw_chain *return_me = NULL;
+ struct vliw_insn_list *prev_insn = NULL;
+ struct vliw_insn_list *curr_insn = vliw_to_split->insn_list;
+
+ struct vliw_chain *double_nop = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ struct vliw_chain *single_nop = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ struct vliw_chain *second_part = (struct vliw_chain *) xmalloc (sizeof (struct vliw_chain));
+ struct vliw_chain *curr_vliw = vliw_chain_top;
+ struct vliw_chain *prev_vliw = NULL;
+
+ while (curr_insn && curr_insn != insert_before_insn)
+ {
+ /* We can't set the packing bit on a label. If we have the case
+ label 1:
+ label 2:
+ label 3:
+ branch that needs nops
+ Then don't set pack bit later. */
+
+ if (curr_insn->type != VLIW_LABEL_TYPE)
+ pack_prev = TRUE;
+ prev_insn = curr_insn;
+ curr_insn = curr_insn->next;
+ }
+
+ while (curr_vliw && curr_vliw != vliw_to_split)
+ {
+ prev_vliw = curr_vliw;
+ curr_vliw = curr_vliw->next;
+ }
+
+ switch (this_nop_type)
+ {
+ case VLIW_SINGLE_NOP:
+ if (!prev_insn)
+ {
+ /* Branch is first, Insert the NOP prior to this vliw insn. */
+ if (prev_vliw)
+ prev_vliw->next = single_nop;
+ else
+ vliw_chain_top = single_nop;
+ single_nop->next = vliw_to_split;
+ vliw_to_split->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ return_me = vliw_to_split;
+ }
+ else
+ {
+ /* Set the packing bit on the previous insn. */
+ if (pack_prev)
+ {
+ char *buffer = prev_insn->address;
+ buffer[0] |= 0x80;
+ }
+ /* The branch is in the middle. Split this vliw insn into first
+ and second parts. Insert the NOP inbetween. */
+
+ second_part->insn_list = insert_before_insn;
+ second_part->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ second_part->next = vliw_to_split->next;
+ frv_adjust_vliw_count (second_part);
+
+ single_nop->next = second_part;
+
+ vliw_to_split->next = single_nop;
+ prev_insn->next = NULL;
+
+ return_me = second_part;
+ frv_adjust_vliw_count (vliw_to_split);
+ }
+ break;
+
+ case VLIW_DOUBLE_NOP:
+ if (!prev_insn)
+ {
+ /* Branch is first, Insert the NOP prior to this vliw insn. */
+ if (prev_vliw)
+ prev_vliw->next = double_nop;
+ else
+ vliw_chain_top = double_nop;
+
+ double_nop->next = vliw_to_split;
+ return_me = vliw_to_split;
+ vliw_to_split->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ }
+ else
+ {
+ /* Set the packing bit on the previous insn. */
+ if (pack_prev)
+ {
+ char *buffer = prev_insn->address;
+ buffer[0] |= 0x80;
+ }
+
+ /* The branch is in the middle. Split this vliw insn into first
+ and second parts. Insert the NOP inbetween. */
+ second_part->insn_list = insert_before_insn;
+ second_part->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ second_part->next = vliw_to_split->next;
+ frv_adjust_vliw_count (second_part);
+
+ double_nop->next = second_part;
+
+ vliw_to_split->next = single_nop;
+ prev_insn->next = NULL;
+ frv_adjust_vliw_count (vliw_to_split);
+
+ return_me = second_part;
+ }
+ break;
+
+ case VLIW_DOUBLE_THEN_SINGLE_NOP:
+ double_nop->next = single_nop;
+ double_nop->insn_count = 2;
+ double_nop->insn_list = &double_nop_insn;
+ single_nop->insn_count = 1;
+ single_nop->insn_list = &single_nop_insn;
+
+ if (!prev_insn)
+ {
+ /* The branch is the first insn in this vliw. Don't split the vliw. Insert
+ the nops prior to this vliw. */
+ if (prev_vliw)
+ prev_vliw->next = double_nop;
+ else
+ vliw_chain_top = double_nop;
+
+ single_nop->next = vliw_to_split;
+ return_me = vliw_to_split;
+ vliw_to_split->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ }
+ else
+ {
+ /* Set the packing bit on the previous insn. */
+ if (pack_prev)
+ {
+ char *buffer = prev_insn->address;
+ buffer[0] |= 0x80;
+ }
+
+ /* The branch is in the middle of this vliw insn. Split into first and
+ second parts. Insert the nop vliws in between. */
+ second_part->insn_list = insert_before_insn;
+ second_part->insn_list->type = VLIW_BRANCH_HAS_NOPS;
+ second_part->next = vliw_to_split->next;
+ frv_adjust_vliw_count (second_part);
+
+ single_nop->next = second_part;
+
+ vliw_to_split->next = double_nop;
+ prev_insn->next = NULL;
+ frv_adjust_vliw_count (vliw_to_split);
+
+ return_me = second_part;
+ }
+ break;
+ }
+
+ return return_me;
+}
+
+static void
+frv_tomcat_analyze_vliw_chains (void)
+{
+ struct vliw_chain *vliw1 = NULL;
+ struct vliw_chain *vliw2 = NULL;
+ struct vliw_chain *vliw3 = NULL;
+
+ struct vliw_insn_list *this_insn = NULL;
+ struct vliw_insn_list *temp_insn = NULL;
+
+ /* We potentially need to look at three VLIW insns to determine if the
+ workaround is required. Set them up. Ignore existing nops during analysis. */
+
+#define FRV_SET_VLIW_WINDOW(VLIW1, VLIW2, VLIW3) \
+ if (VLIW1 && VLIW1->next) \
+ VLIW2 = VLIW1->next; \
+ else \
+ VLIW2 = NULL; \
+ if (VLIW2 && VLIW2->next) \
+ VLIW3 = VLIW2->next; \
+ else \
+ VLIW3 = NULL
+
+ vliw1 = vliw_chain_top;
+
+workaround_top:
+
+ FRV_SET_VLIW_WINDOW (vliw1, vliw2, vliw3);
+
+ if (!vliw1)
+ return;
+
+ if (vliw1->insn_count == 1)
+ {
+ /* check vliw1 for a label. */
+ if (vliw1->insn_list->type == VLIW_LABEL_TYPE)
+ {
+ temp_insn = frv_find_in_vliw (VLIW_BRANCH_TYPE, vliw2, vliw1->insn_list->sym);
+ if (temp_insn)
+ {
+ vliw1 = frv_tomcat_shuffle (VLIW_DOUBLE_NOP, vliw2, vliw1->insn_list);
+ temp_insn->dnop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = vliw1->next;
+ if (tomcat_stats)
+ tomcat_doubles++;
+ goto workaround_top;
+ }
+ else if (vliw2
+ && vliw2->insn_count == 1
+ && (temp_insn = frv_find_in_vliw (VLIW_BRANCH_TYPE, vliw3, vliw1->insn_list->sym)) != NULL)
+ {
+ temp_insn->snop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = frv_tomcat_shuffle (VLIW_SINGLE_NOP, vliw3, vliw3->insn_list);
+ if (tomcat_stats)
+ tomcat_singles++;
+ goto workaround_top;
+ }
+ }
+ }
+
+ if (vliw1->insn_count == 2)
+ {
+ /* Check vliw1 for a label. */
+ for (this_insn = vliw1->insn_list; this_insn; this_insn = this_insn->next)
+ {
+ if (this_insn->type == VLIW_LABEL_TYPE)
+ {
+ if ((temp_insn = frv_find_in_vliw (VLIW_BRANCH_TYPE, vliw2, this_insn->sym)) != NULL)
+ {
+ temp_insn->snop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = frv_tomcat_shuffle (VLIW_SINGLE_NOP, vliw2, this_insn);
+ if (tomcat_stats)
+ tomcat_singles++;
+ }
+ else
+ vliw1 = vliw1->next;
+ goto workaround_top;
+ }
+ }
+ }
+ /* Examine each insn in this VLIW. Look for the workaround criteria. */
+ for (this_insn = vliw1->insn_list; this_insn; this_insn = this_insn->next)
+ {
+ /* Don't look at labels or nops. */
+ while (this_insn
+ && (this_insn->type == VLIW_LABEL_TYPE
+ || this_insn->type == VLIW_NOP_TYPE
+ || this_insn->type == VLIW_BRANCH_HAS_NOPS))
+ this_insn = this_insn->next;
+
+ if (!this_insn)
+ {
+ vliw1 = vliw2;
+ goto workaround_top;
+ }
+
+ if (frv_is_branch_insn (this_insn->insn))
+ {
+ if ((temp_insn = frv_find_in_vliw (VLIW_LABEL_TYPE, vliw1, this_insn->sym)) != NULL)
+ {
+ /* Insert [nop/nop] [nop] before branch. */
+ this_insn->snop_frag->fr_subtype = NOP_KEEP;
+ this_insn->dnop_frag->fr_subtype = NOP_KEEP;
+ vliw1 = frv_tomcat_shuffle (VLIW_DOUBLE_THEN_SINGLE_NOP, vliw1, this_insn);
+ goto workaround_top;
+ }
+ }
+
+
+ }
+ /* This vliw insn checks out okay. Take a look at the next one. */
+ vliw1 = vliw1->next;
+ goto workaround_top;
+}
+
+void
+frv_tomcat_workaround (void)
+{
+ if (frv_mach != bfd_mach_frvtomcat)
+ return;
+
+ if (tomcat_debug)
+ frv_debug_tomcat (vliw_chain_top);
+
+ frv_tomcat_analyze_vliw_chains ();
+
+ if (tomcat_stats)
+ {
+ fprintf (stderr, "Inserted %d Single Nops\n", tomcat_singles);
+ fprintf (stderr, "Inserted %d Double Nops\n", tomcat_doubles);
+ }
+}
+
+static int
+fr550_check_insn_acc_range (frv_insn *insn, int low, int hi)
+{
+ int acc;
+ switch (CGEN_INSN_NUM (insn->insn))
+ {
+ case FRV_INSN_MADDACCS:
+ case FRV_INSN_MSUBACCS:
+ case FRV_INSN_MDADDACCS:
+ case FRV_INSN_MDSUBACCS:
+ case FRV_INSN_MASACCS:
+ case FRV_INSN_MDASACCS:
+ acc = insn->fields.f_ACC40Si;
+ if (acc < low || acc > hi)
+ return 1; /* out of range */
+ acc = insn->fields.f_ACC40Sk;
+ if (acc < low || acc > hi)
+ return 1; /* out of range */
+ break;
+ case FRV_INSN_MMULHS:
+ case FRV_INSN_MMULHU:
+ case FRV_INSN_MMULXHS:
+ case FRV_INSN_MMULXHU:
+ case FRV_INSN_CMMULHS:
+ case FRV_INSN_CMMULHU:
+ case FRV_INSN_MQMULHS:
+ case FRV_INSN_MQMULHU:
+ case FRV_INSN_MQMULXHS:
+ case FRV_INSN_MQMULXHU:
+ case FRV_INSN_CMQMULHS:
+ case FRV_INSN_CMQMULHU:
+ case FRV_INSN_MMACHS:
+ case FRV_INSN_MMRDHS:
+ case FRV_INSN_CMMACHS:
+ case FRV_INSN_MQMACHS:
+ case FRV_INSN_CMQMACHS:
+ case FRV_INSN_MQXMACHS:
+ case FRV_INSN_MQXMACXHS:
+ case FRV_INSN_MQMACXHS:
+ case FRV_INSN_MCPXRS:
+ case FRV_INSN_MCPXIS:
+ case FRV_INSN_CMCPXRS:
+ case FRV_INSN_CMCPXIS:
+ case FRV_INSN_MQCPXRS:
+ case FRV_INSN_MQCPXIS:
+ acc = insn->fields.f_ACC40Sk;
+ if (acc < low || acc > hi)
+ return 1; /* out of range */
+ break;
+ case FRV_INSN_MMACHU:
+ case FRV_INSN_MMRDHU:
+ case FRV_INSN_CMMACHU:
+ case FRV_INSN_MQMACHU:
+ case FRV_INSN_CMQMACHU:
+ case FRV_INSN_MCPXRU:
+ case FRV_INSN_MCPXIU:
+ case FRV_INSN_CMCPXRU:
+ case FRV_INSN_CMCPXIU:
+ case FRV_INSN_MQCPXRU:
+ case FRV_INSN_MQCPXIU:
+ acc = insn->fields.f_ACC40Uk;
+ if (acc < low || acc > hi)
+ return 1; /* out of range */
+ break;
+ default:
+ break;
+ }
+ return 0; /* all is ok */
+}
+
+static int
+fr550_check_acc_range (FRV_VLIW *vlw, frv_insn *insn)
+{
+ switch ((*vlw->current_vliw)[vlw->next_slot - 1])
+ {
+ case UNIT_FM0:
+ case UNIT_FM2:
+ return fr550_check_insn_acc_range (insn, 0, 3);
+ case UNIT_FM1:
+ case UNIT_FM3:
+ return fr550_check_insn_acc_range (insn, 4, 7);
+ default:
+ break;
+ }
+ return 0; /* all is ok */
+}
+
+/* Return true if the target implements instruction INSN. */
+
+static bfd_boolean
+target_implements_insn_p (const CGEN_INSN *insn)
+{
+ switch (frv_mach)
+ {
+ default:
+ /* bfd_mach_frv or generic. */
+ return TRUE;
+
+ case bfd_mach_fr300:
+ case bfd_mach_frvsimple:
+ return CGEN_INSN_MACH_HAS_P (insn, MACH_SIMPLE);
+
+ case bfd_mach_fr400:
+ return ((fr400_audio || !CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_AUDIO))
+ && CGEN_INSN_MACH_HAS_P (insn, MACH_FR400));
+
+ case bfd_mach_fr450:
+ return CGEN_INSN_MACH_HAS_P (insn, MACH_FR450);
+
+ case bfd_mach_fr500:
+ return CGEN_INSN_MACH_HAS_P (insn, MACH_FR500);
+
+ case bfd_mach_fr550:
+ return CGEN_INSN_MACH_HAS_P (insn, MACH_FR550);
+ }
+}
+
+void
+md_assemble (char *str)
+{
+ frv_insn insn;
+ char *errmsg;
+ int packing_constraint;
+ finished_insnS finished_insn;
+ fragS *double_nop_frag = NULL;
+ fragS *single_nop_frag = NULL;
+ struct vliw_insn_list *vliw_insn_list_entry = NULL;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ memset (&insn, 0, sizeof (insn));
+
+ insn.insn = frv_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, &errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* If the cpu is tomcat, then we need to insert nops to workaround
+ hardware limitations. We need to keep track of each vliw unit
+ and examine the length of the unit and the individual insns
+ within the unit to determine the number and location of the
+ required nops. */
+ if (frv_mach == bfd_mach_frvtomcat)
+ {
+ /* If we've just finished a VLIW insn OR this is a branch,
+ then start up a new frag. Fill it with nops. We will get rid
+ of those that are not required after we've seen all of the
+ instructions but before we start resolving fixups. */
+ if ( !FRV_IS_NOP (insn)
+ && (frv_is_branch_insn (insn.insn) || insn.fields.f_pack))
+ {
+ char *buffer;
+
+ frag_wane (frag_now);
+ frag_new (0);
+ double_nop_frag = frag_now;
+ buffer = frag_var (rs_machine_dependent, 8, 8, NOP_DELETE, NULL, 0, 0);
+ md_number_to_chars (buffer, FRV_NOP_PACK, 4);
+ md_number_to_chars (buffer+4, FRV_NOP_NOPACK, 4);
+
+ frag_wane (frag_now);
+ frag_new (0);
+ single_nop_frag = frag_now;
+ buffer = frag_var (rs_machine_dependent, 4, 4, NOP_DELETE, NULL, 0, 0);
+ md_number_to_chars (buffer, FRV_NOP_NOPACK, 4);
+ }
+
+ vliw_insn_list_entry = frv_insert_vliw_insn (DO_COUNT);
+ vliw_insn_list_entry->insn = insn.insn;
+ if (frv_is_branch_insn (insn.insn))
+ vliw_insn_list_entry->type = VLIW_BRANCH_TYPE;
+
+ if ( !FRV_IS_NOP (insn)
+ && (frv_is_branch_insn (insn.insn) || insn.fields.f_pack))
+ {
+ vliw_insn_list_entry->snop_frag = single_nop_frag;
+ vliw_insn_list_entry->dnop_frag = double_nop_frag;
+ }
+ }
+
+ /* Make sure that this insn does not violate the VLIW packing constraints. */
+ /* -mno-pack disallows any packing whatsoever. */
+ if (frv_flags & EF_FRV_NOPACK)
+ {
+ if (! insn.fields.f_pack)
+ {
+ as_bad (_("VLIW packing used for -mno-pack"));
+ return;
+ }
+ }
+ /* -mcpu=FRV is an idealized FR-V implementation that supports all of the
+ instructions, don't do vliw checking. */
+ else if (frv_mach != bfd_mach_frv)
+ {
+ if (!target_implements_insn_p (insn.insn))
+ {
+ as_bad (_("Instruction not supported by this architecture"));
+ return;
+ }
+ packing_constraint = frv_vliw_add_insn (& vliw, insn.insn);
+ if (frv_mach == bfd_mach_fr550 && ! packing_constraint)
+ packing_constraint = fr550_check_acc_range (& vliw, & insn);
+ if (insn.fields.f_pack)
+ frv_vliw_reset (& vliw, frv_mach, frv_flags);
+ if (packing_constraint)
+ {
+ as_bad (_("VLIW packing constraint violation"));
+ 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, &finished_insn);
+
+
+ /* If the cpu is tomcat, then we need to insert nops to workaround
+ hardware limitations. We need to keep track of each vliw unit
+ and examine the length of the unit and the individual insns
+ within the unit to determine the number and location of the
+ required nops. */
+ if (frv_mach == bfd_mach_frvtomcat)
+ {
+ if (vliw_insn_list_entry)
+ vliw_insn_list_entry->address = finished_insn.addr;
+ else
+ abort();
+
+ if (insn.fields.f_pack)
+ {
+ /* We've completed a VLIW insn. */
+ previous_vliw_chain = current_vliw_chain;
+ current_vliw_chain = NULL;
+ current_vliw_insn = NULL;
+ }
+ }
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+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;
+}
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+const relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0},
+ {511 - 2 - 2, -512 - 2 + 2, 0, 2 },
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 2, 0 },
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 4, 0 }
+};
+
+long
+frv_relax_frag (fragS *fragP ATTRIBUTE_UNUSED, long stretch ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* 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)
+{
+ switch (fragP->fr_subtype)
+ {
+ case NOP_KEEP:
+ return fragP->fr_var;
+
+ default:
+ case NOP_DELETE:
+ return 0;
+ }
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ switch (fragP->fr_subtype)
+ {
+ default:
+ case NOP_DELETE:
+ return;
+
+ case NOP_KEEP:
+ fragP->fr_fix = fragP->fr_var;
+ fragP->fr_var = 0;
+ return;
+ }
+}
+
+/* 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 (TC_FORCE_RELOCATION (fixP)
+ || (fixP->fx_addsy != (symbolS *) NULL
+ && S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ /* If we can't adjust this relocation, or if it references a
+ local symbol in a different section (which
+ TC_FORCE_RELOCATION can't check), let the linker figure it
+ out. */
+ return 0;
+ }
+
+ return (fixP->fx_frag->fr_address + fixP->fx_where) & ~1;
+}
+
+/* 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)
+{
+ switch (operand->type)
+ {
+ case FRV_OPERAND_LABEL16:
+ fixP->fx_pcrel = TRUE;
+ return BFD_RELOC_FRV_LABEL16;
+
+ case FRV_OPERAND_LABEL24:
+ fixP->fx_pcrel = TRUE;
+
+ if (fixP->fx_cgen.opinfo != 0)
+ return fixP->fx_cgen.opinfo;
+
+ return BFD_RELOC_FRV_LABEL24;
+
+ case FRV_OPERAND_UHI16:
+ case FRV_OPERAND_ULO16:
+ case FRV_OPERAND_SLO16:
+ case FRV_OPERAND_CALLANN:
+ case FRV_OPERAND_LDANN:
+ case FRV_OPERAND_LDDANN:
+ /* The relocation type should be recorded in opinfo */
+ if (fixP->fx_cgen.opinfo != 0)
+ return fixP->fx_cgen.opinfo;
+ break;
+
+ case FRV_OPERAND_D12:
+ case FRV_OPERAND_S12:
+ if (fixP->fx_cgen.opinfo != 0)
+ return fixP->fx_cgen.opinfo;
+
+ return BFD_RELOC_FRV_GPREL12;
+
+ case FRV_OPERAND_U12:
+ return BFD_RELOC_FRV_GPRELU12;
+
+ default:
+ break;
+ }
+ 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
+frv_force_relocation (fixS *fix)
+{
+ switch (fix->fx_r_type < BFD_RELOC_UNUSED
+ ? (int) fix->fx_r_type
+ : fix->fx_cgen.opinfo)
+ {
+ case BFD_RELOC_FRV_GPREL12:
+ case BFD_RELOC_FRV_GPRELU12:
+ case BFD_RELOC_FRV_GPREL32:
+ case BFD_RELOC_FRV_GPRELHI:
+ case BFD_RELOC_FRV_GPRELLO:
+ case BFD_RELOC_FRV_GOT12:
+ case BFD_RELOC_FRV_GOTHI:
+ case BFD_RELOC_FRV_GOTLO:
+ case BFD_RELOC_FRV_FUNCDESC_VALUE:
+ case BFD_RELOC_FRV_FUNCDESC_GOTOFF12:
+ case BFD_RELOC_FRV_FUNCDESC_GOTOFFHI:
+ case BFD_RELOC_FRV_FUNCDESC_GOTOFFLO:
+ case BFD_RELOC_FRV_GOTOFF12:
+ case BFD_RELOC_FRV_GOTOFFHI:
+ case BFD_RELOC_FRV_GOTOFFLO:
+ case BFD_RELOC_FRV_GETTLSOFF:
+ case BFD_RELOC_FRV_TLSDESC_VALUE:
+ case BFD_RELOC_FRV_GOTTLSDESC12:
+ case BFD_RELOC_FRV_GOTTLSDESCHI:
+ case BFD_RELOC_FRV_GOTTLSDESCLO:
+ case BFD_RELOC_FRV_TLSMOFF12:
+ case BFD_RELOC_FRV_TLSMOFFHI:
+ case BFD_RELOC_FRV_TLSMOFFLO:
+ case BFD_RELOC_FRV_GOTTLSOFF12:
+ case BFD_RELOC_FRV_GOTTLSOFFHI:
+ case BFD_RELOC_FRV_GOTTLSOFFLO:
+ case BFD_RELOC_FRV_TLSOFF:
+ case BFD_RELOC_FRV_TLSDESC_RELAX:
+ case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+ case BFD_RELOC_FRV_TLSOFF_RELAX:
+ return 1;
+
+ default:
+ break;
+ }
+
+ return generic_force_reloc (fix);
+}
+
+/* Apply a fixup that could be resolved within the assembler. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ if (fixP->fx_addsy == 0)
+ switch (fixP->fx_cgen.opinfo)
+ {
+ case BFD_RELOC_FRV_HI16:
+ *valP >>= 16;
+ /* Fall through. */
+ case BFD_RELOC_FRV_LO16:
+ *valP &= 0xffff;
+ break;
+
+ /* We need relocations for these, even if their symbols reduce
+ to constants. */
+ case BFD_RELOC_FRV_GPREL12:
+ case BFD_RELOC_FRV_GPRELU12:
+ case BFD_RELOC_FRV_GPREL32:
+ case BFD_RELOC_FRV_GPRELHI:
+ case BFD_RELOC_FRV_GPRELLO:
+ case BFD_RELOC_FRV_GOT12:
+ case BFD_RELOC_FRV_GOTHI:
+ case BFD_RELOC_FRV_GOTLO:
+ case BFD_RELOC_FRV_FUNCDESC_VALUE:
+ case BFD_RELOC_FRV_FUNCDESC_GOTOFF12:
+ case BFD_RELOC_FRV_FUNCDESC_GOTOFFHI:
+ case BFD_RELOC_FRV_FUNCDESC_GOTOFFLO:
+ case BFD_RELOC_FRV_GOTOFF12:
+ case BFD_RELOC_FRV_GOTOFFHI:
+ case BFD_RELOC_FRV_GOTOFFLO:
+ case BFD_RELOC_FRV_GETTLSOFF:
+ case BFD_RELOC_FRV_TLSDESC_VALUE:
+ case BFD_RELOC_FRV_GOTTLSDESC12:
+ case BFD_RELOC_FRV_GOTTLSDESCHI:
+ case BFD_RELOC_FRV_GOTTLSDESCLO:
+ case BFD_RELOC_FRV_TLSMOFF12:
+ case BFD_RELOC_FRV_TLSMOFFHI:
+ case BFD_RELOC_FRV_TLSMOFFLO:
+ case BFD_RELOC_FRV_GOTTLSOFF12:
+ case BFD_RELOC_FRV_GOTTLSOFFHI:
+ case BFD_RELOC_FRV_GOTTLSOFFLO:
+ case BFD_RELOC_FRV_TLSOFF:
+ case BFD_RELOC_FRV_TLSDESC_RELAX:
+ case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+ case BFD_RELOC_FRV_TLSOFF_RELAX:
+ fixP->fx_addsy = abs_section_sym;
+ break;
+ }
+ else
+ switch (fixP->fx_cgen.opinfo)
+ {
+ case BFD_RELOC_FRV_GETTLSOFF:
+ case BFD_RELOC_FRV_TLSDESC_VALUE:
+ case BFD_RELOC_FRV_GOTTLSDESC12:
+ case BFD_RELOC_FRV_GOTTLSDESCHI:
+ case BFD_RELOC_FRV_GOTTLSDESCLO:
+ case BFD_RELOC_FRV_TLSMOFF12:
+ case BFD_RELOC_FRV_TLSMOFFHI:
+ case BFD_RELOC_FRV_TLSMOFFLO:
+ case BFD_RELOC_FRV_GOTTLSOFF12:
+ case BFD_RELOC_FRV_GOTTLSOFFHI:
+ case BFD_RELOC_FRV_GOTTLSOFFLO:
+ case BFD_RELOC_FRV_TLSOFF:
+ case BFD_RELOC_FRV_TLSDESC_RELAX:
+ case BFD_RELOC_FRV_GETTLSOFF_RELAX:
+ case BFD_RELOC_FRV_TLSOFF_RELAX:
+ /* Mark TLS symbols as such. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+ }
+
+ gas_cgen_md_apply_fix (fixP, valP, seg);
+ return;
+}
+
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+frv_md_number_to_chars (char *buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+bfd_boolean
+frv_fix_adjustable (fixS *fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ /* We need the symbol name for the VTABLE entries */
+ if ( reloc_type == BFD_RELOC_VTABLE_INHERIT
+ || reloc_type == BFD_RELOC_VTABLE_ENTRY
+ || reloc_type == BFD_RELOC_FRV_GPREL12
+ || reloc_type == BFD_RELOC_FRV_GPRELU12)
+ return 0;
+
+ return 1;
+}
+
+/* Allow user to set flags bits. */
+void
+frv_set_flags (int arg ATTRIBUTE_UNUSED)
+{
+ flagword new_flags = get_absolute_expression ();
+ flagword new_mask = ~ (flagword)0;
+
+ frv_user_set_flags_p = 1;
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ new_mask = get_absolute_expression ();
+ }
+
+ frv_flags = (frv_flags & ~new_mask) | (new_flags & new_mask);
+ bfd_set_private_flags (stdoutput, frv_flags);
+}
+
+/* Frv specific function to handle 4 byte initializations for pointers that are
+ considered 'safe' for use with pic support. Until frv_frob_file{,_section}
+ is run, we encode it a BFD_RELOC_CTOR, and it is turned back into a normal
+ BFD_RELOC_32 at that time. */
+
+void
+frv_pic_ptr (int nbytes)
+{
+ expressionS exp;
+ char *p;
+
+ if (nbytes != 4)
+ abort ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ do
+ {
+ bfd_reloc_code_real_type reloc_type = BFD_RELOC_CTOR;
+
+ if (strncasecmp (input_line_pointer, "funcdesc(", 9) == 0)
+ {
+ input_line_pointer += 9;
+ expression (&exp);
+ if (*input_line_pointer == ')')
+ input_line_pointer++;
+ else
+ as_bad (_("missing ')'"));
+ reloc_type = BFD_RELOC_FRV_FUNCDESC;
+ }
+ else if (strncasecmp (input_line_pointer, "tlsmoff(", 8) == 0)
+ {
+ input_line_pointer += 8;
+ expression (&exp);
+ if (*input_line_pointer == ')')
+ input_line_pointer++;
+ else
+ as_bad (_("missing ')'"));
+ reloc_type = BFD_RELOC_FRV_TLSMOFF;
+ }
+ else
+ expression (&exp);
+
+ p = frag_more (4);
+ memset (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &exp, 0,
+ reloc_type);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+
+
+#ifdef DEBUG
+#define DPRINTF1(A) fprintf (stderr, A)
+#define DPRINTF2(A,B) fprintf (stderr, A, B)
+#define DPRINTF3(A,B,C) fprintf (stderr, A, B, C)
+
+#else
+#define DPRINTF1(A)
+#define DPRINTF2(A,B)
+#define DPRINTF3(A,B,C)
+#endif
+
+/* Go through a the sections looking for relocations that are problematical for
+ pic. If not pic, just note that this object can't be linked with pic. If
+ it is pic, see if it needs to be marked so that it will be fixed up, or if
+ not possible, issue an error. */
+
+static void
+frv_frob_file_section (bfd *abfd, asection *sec, void *ptr ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fixS *fixp;
+ CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
+ flagword flags = bfd_get_section_flags (abfd, sec);
+
+ /* Skip relocations in known sections (.ctors, .dtors, and .gcc_except_table)
+ since we can fix those up by hand. */
+ int known_section_p = (sec->name
+ && sec->name[0] == '.'
+ && ((sec->name[1] == 'c'
+ && strcmp (sec->name, ".ctor") == 0)
+ || (sec->name[1] == 'd'
+ && strcmp (sec->name, ".dtor") == 0)
+ || (sec->name[1] == 'g'
+ && strcmp (sec->name, ".gcc_except_table") == 0)));
+
+ DPRINTF3 ("\nFrv section %s%s\n", sec->name, (known_section_p) ? ", known section" : "");
+ if ((flags & SEC_ALLOC) == 0)
+ {
+ DPRINTF1 ("\tSkipping non-loaded section\n");
+ return;
+ }
+
+ for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+ {
+ symbolS *s = fixp->fx_addsy;
+ bfd_reloc_code_real_type reloc;
+ int non_pic_p;
+ int opindex;
+ const CGEN_OPERAND *operand;
+ const CGEN_INSN *insn = fixp->fx_cgen.insn;
+
+ if (fixp->fx_done)
+ {
+ DPRINTF1 ("\tSkipping reloc that has already been done\n");
+ continue;
+ }
+
+ if (fixp->fx_pcrel)
+ {
+ DPRINTF1 ("\tSkipping reloc that is PC relative\n");
+ continue;
+ }
+
+ if (! s)
+ {
+ DPRINTF1 ("\tSkipping reloc without symbol\n");
+ continue;
+ }
+
+ if (fixp->fx_r_type < BFD_RELOC_UNUSED)
+ {
+ opindex = -1;
+ reloc = fixp->fx_r_type;
+ }
+ else
+ {
+ opindex = (int) fixp->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = cgen_operand_lookup_by_num (cd, opindex);
+ reloc = md_cgen_lookup_reloc (insn, operand, fixp);
+ }
+
+ DPRINTF3 ("\treloc %s\t%s", bfd_get_reloc_code_name (reloc), S_GET_NAME (s));
+
+ non_pic_p = 0;
+ switch (reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_32:
+ /* Skip relocations in known sections (.ctors, .dtors, and
+ .gcc_except_table) since we can fix those up by hand. Also
+ skip forward references to constants. Also skip a difference
+ of two symbols, which still uses the BFD_RELOC_32 at this
+ point. */
+ if (! known_section_p
+ && S_GET_SEGMENT (s) != absolute_section
+ && !fixp->fx_subsy
+ && (flags & (SEC_READONLY | SEC_CODE)) == 0)
+ {
+ non_pic_p = 1;
+ }
+ break;
+
+ /* FIXME -- should determine if any of the GP relocation really uses
+ gr16 (which is not pic safe) or not. Right now, assume if we
+ aren't being compiled with -mpic, the usage is non pic safe, but
+ is safe with -mpic. */
+ case BFD_RELOC_FRV_GPREL12:
+ case BFD_RELOC_FRV_GPRELU12:
+ case BFD_RELOC_FRV_GPREL32:
+ case BFD_RELOC_FRV_GPRELHI:
+ case BFD_RELOC_FRV_GPRELLO:
+ non_pic_p = ! frv_pic_p;
+ break;
+
+ case BFD_RELOC_FRV_LO16:
+ case BFD_RELOC_FRV_HI16:
+ if (S_GET_SEGMENT (s) != absolute_section)
+ non_pic_p = 1;
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ non_pic_p = 1;
+ break;
+
+ /* If this is a blessed BFD_RELOC_32, convert it back to the normal
+ relocation. */
+ case BFD_RELOC_CTOR:
+ fixp->fx_r_type = BFD_RELOC_32;
+ break;
+ }
+
+ if (non_pic_p)
+ {
+ DPRINTF1 (" (Non-pic relocation)\n");
+ if (frv_pic_p)
+ as_warn_where (fixp->fx_file, fixp->fx_line,
+ _("Relocation %s is not safe for %s"),
+ bfd_get_reloc_code_name (reloc), frv_pic_flag);
+
+ else if ((frv_flags & EF_FRV_NON_PIC_RELOCS) == 0)
+ {
+ frv_flags |= EF_FRV_NON_PIC_RELOCS;
+ bfd_set_private_flags (abfd, frv_flags);
+ }
+ }
+#ifdef DEBUG
+ else
+ DPRINTF1 ("\n");
+#endif
+ }
+}
+
+/* After all of the symbols have been adjusted, go over the file looking
+ for any relocations that pic won't support. */
+
+void
+frv_frob_file (void)
+{
+ bfd_map_over_sections (stdoutput, frv_frob_file_section, (void *) 0);
+}
+
+void
+frv_frob_label (symbolS *this_label)
+{
+ struct vliw_insn_list *vliw_insn_list_entry;
+
+ dwarf2_emit_label (this_label);
+ if (frv_mach != bfd_mach_frvtomcat)
+ return;
+
+ if (now_seg != text_section)
+ return;
+
+ vliw_insn_list_entry = frv_insert_vliw_insn(DONT_COUNT);
+ vliw_insn_list_entry->type = VLIW_LABEL_TYPE;
+ vliw_insn_list_entry->sym = this_label;
+}
+
+fixS *
+frv_cgen_record_fixup_exp (fragS *frag,
+ int where,
+ const CGEN_INSN *insn,
+ int length,
+ const CGEN_OPERAND *operand,
+ int opinfo,
+ expressionS *exp)
+{
+ fixS * fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+
+ if (frv_mach == bfd_mach_frvtomcat
+ && current_vliw_insn
+ && current_vliw_insn->type == VLIW_BRANCH_TYPE
+ && exp != NULL)
+ current_vliw_insn->sym = exp->X_add_symbol;
+
+ return fixP;
+}
diff --git a/gas/config/tc-frv.h b/gas/config/tc-frv.h
new file mode 100644
index 0000000..9b4e606
--- /dev/null
+++ b/gas/config/tc-frv.h
@@ -0,0 +1,125 @@
+/* tc-frv.h -- Header file for tc-frv.c.
+ Copyright (C) 2002-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_FRV
+
+#define LISTING_HEADER "FRV GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_frv
+
+#define TARGET_FORMAT (frv_md_fdpic_enabled () \
+ ? "elf32-frvfdpic" : "elf32-frv")
+extern bfd_boolean frv_md_fdpic_enabled (void);
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+extern void frv_tomcat_workaround (void);
+#define md_cleanup frv_tomcat_workaround
+
+#define md_number_to_chars frv_md_number_to_chars
+extern void frv_md_number_to_chars (char *, valueT, int);
+
+extern long frv_relax_frag (fragS *, long);
+#define md_relax_frag(segment, fragP, stretch) frv_relax_frag(fragP, stretch)
+
+#define tc_fix_adjustable(FIX) frv_fix_adjustable (FIX)
+struct fix;
+extern bfd_boolean frv_fix_adjustable (struct fix *);
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) frv_force_relocation (fix)
+extern int frv_force_relocation (struct fix *);
+
+/* If we simplify subtractions that aren't SUB_SAME or SUB_ABS, we end
+ up with PCrel fixups, but since we don't have any PCrel relocs, we
+ crash. Preventing simplification gets us a good, early error. */
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
+
+#undef GAS_CGEN_MAX_FIXUPS
+#define GAS_CGEN_MAX_FIXUPS 1
+
+void frv_frob_label (symbolS *);
+#define tc_frob_label(sym) frv_frob_label(sym)
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+#define md_cgen_record_fixup_exp frv_cgen_record_fixup_exp
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* After all of the symbols have been adjusted, go over the file looking
+ for any relocations that pic won't support. */
+#define tc_frob_file() frv_frob_file ()
+extern void frv_frob_file (void);
+
+/* We don't want 0x00 for code alignment because this generates `add.p
+ gr0, gr0, gr0' patterns. Although it's fine as a nop instruction,
+ it has the VLIW packing bit set, which means if you have a bunch of
+ them in a row and attempt to execute them, you'll exceed the VLIW
+ capacity and fail. This also gets GDB confused sometimes, because
+ it won't set breakpoints in instructions other than the first of a
+ VLIW pack, so you used to be unable to set a breakpoint in the
+ initial instruction of a function that followed such
+ alignment-introduced instructions.
+
+ We could have arranged to emit `nop' instructions (0x80880000),
+ maybe even VLIW-pack sequences of nop instructions as much as
+ possible for the selected machine type, just in case the alignment
+ code actually happens to run, but this is probably too much effort
+ for little gain. This code is not meant to be run anyway, so just
+ emit nops. */
+#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4)
+#define HANDLE_ALIGN(FRAGP) do \
+ if ((FRAGP)->fr_type == rs_align_code) \
+ { \
+ valueT count = ((FRAGP)->fr_next->fr_address \
+ - ((FRAGP)->fr_address + (FRAGP)->fr_fix)); \
+ char *dest = (FRAGP)->fr_literal + (FRAGP)->fr_fix; \
+ if ((count & 3) != 0) \
+ { \
+ memset (dest, 0, (count & 3)); \
+ (FRAGP)->fr_fix += (count & 3); \
+ dest += (count & 3); \
+ count -= (count & 3); \
+ } \
+ if (count) \
+ { \
+ (FRAGP)->fr_var = 4; \
+ *dest++ = 0x80; \
+ *dest++ = 0x88; \
+ *dest++ = 0x00; \
+ *dest++ = 0x00; \
+ } \
+ } \
+ while (0)
diff --git a/gas/config/tc-generic.c b/gas/config/tc-generic.c
new file mode 100644
index 0000000..778429a
--- /dev/null
+++ b/gas/config/tc-generic.c
@@ -0,0 +1,22 @@
+/* This file is tc-generic.c
+
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* This file is tc-generic.c and is intended to be a template for
+ target cpu specific files. */
diff --git a/gas/config/tc-generic.h b/gas/config/tc-generic.h
new file mode 100644
index 0000000..ec7593f
--- /dev/null
+++ b/gas/config/tc-generic.h
@@ -0,0 +1,35 @@
+/* This file is tc-generic.h
+
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/* This file is tc-generic.h and is intended to be a template for target cpu
+ specific header files. It is my intent that this file compile. It is also
+ my intent that this file grow into something that can be used as both a
+ template for porting, and a stub for testing. xoxorich. */
+
+#define TC_GENERIC 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* Local Variables:
+ comment-column: 0
+ fill-column: 131
+ End:
+ */
+
diff --git a/gas/config/tc-h8300.c b/gas/config/tc-h8300.c
new file mode 100644
index 0000000..e4b827b
--- /dev/null
+++ b/gas/config/tc-h8300.c
@@ -0,0 +1,2258 @@
+/* tc-h8300.c -- Assemble code for the Renesas H8/300
+ Copyright (C) 1991-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Written By Steve Chamberlain <sac@cygnus.com>. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "dwarf2dbg.h"
+
+#define DEFINE_TABLE
+#define h8_opcodes ops
+#include "opcode/h8300.h"
+#include "safe-ctype.h"
+
+#ifdef OBJ_ELF
+#include "elf/h8.h"
+#endif
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "";
+
+static void sbranch (int);
+static void h8300hmode (int);
+static void h8300smode (int);
+static void h8300hnmode (int);
+static void h8300snmode (int);
+static void h8300sxmode (int);
+static void h8300sxnmode (int);
+static void pint (int);
+
+int Hmode;
+int Smode;
+int Nmode;
+int SXmode;
+
+#define PSIZE (Hmode && !Nmode ? L_32 : L_16)
+
+static int bsize = L_8; /* Default branch displacement. */
+
+struct h8_instruction
+{
+ int length;
+ int noperands;
+ int idx;
+ int size;
+ const struct h8_opcode *opcode;
+};
+
+static struct h8_instruction *h8_instructions;
+
+static void
+h8300hmode (int arg ATTRIBUTE_UNUSED)
+{
+ Hmode = 1;
+ Smode = 0;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300h))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+h8300smode (int arg ATTRIBUTE_UNUSED)
+{
+ Smode = 1;
+ Hmode = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300s))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+h8300hnmode (int arg ATTRIBUTE_UNUSED)
+{
+ Hmode = 1;
+ Smode = 0;
+ Nmode = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300hn))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+h8300snmode (int arg ATTRIBUTE_UNUSED)
+{
+ Smode = 1;
+ Hmode = 1;
+ Nmode = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sn))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+h8300sxmode (int arg ATTRIBUTE_UNUSED)
+{
+ Smode = 1;
+ Hmode = 1;
+ SXmode = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sx))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+h8300sxnmode (int arg ATTRIBUTE_UNUSED)
+{
+ Smode = 1;
+ Hmode = 1;
+ SXmode = 1;
+ Nmode = 1;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300sxn))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+sbranch (int size)
+{
+ bsize = size;
+}
+
+static void
+pint (int arg ATTRIBUTE_UNUSED)
+{
+ cons (Hmode ? 4 : 2);
+}
+
+/* Like obj_elf_section, but issues a warning for new
+ sections which do not have an attribute specification. */
+
+static void
+h8300_elf_section (int push)
+{
+ static const char * known_data_sections [] = { ".rodata", ".tdata", ".tbss" };
+ static const char * known_data_prefixes [] = { ".debug", ".zdebug", ".gnu.warning" };
+ char * saved_ilp = input_line_pointer;
+ char * name;
+
+ name = obj_elf_section_name ();
+ if (name == NULL)
+ return;
+
+ if (* input_line_pointer != ','
+ && bfd_get_section_by_name (stdoutput, name) == NULL)
+ {
+ signed int i;
+
+ /* Ignore this warning for well known data sections. */
+ for (i = ARRAY_SIZE (known_data_sections); i--;)
+ if (strcmp (name, known_data_sections[i]) == 0)
+ break;
+
+ if (i < 0)
+ for (i = ARRAY_SIZE (known_data_prefixes); i--;)
+ if (strncmp (name, known_data_prefixes[i],
+ strlen (known_data_prefixes[i])) == 0)
+ break;
+
+ if (i < 0)
+ as_warn (_("new section '%s' defined without attributes - this might cause problems"), name);
+ }
+
+ /* FIXME: We ought to free the memory allocated by obj_elf_section_name()
+ for 'name', but we do not know if it was taken from the obstack, via
+ demand_copy_C_string(), or xmalloc()ed. */
+ input_line_pointer = saved_ilp;
+ obj_elf_section (push);
+}
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"h8300h", h8300hmode, 0},
+ {"h8300hn", h8300hnmode, 0},
+ {"h8300s", h8300smode, 0},
+ {"h8300sn", h8300snmode, 0},
+ {"h8300sx", h8300sxmode, 0},
+ {"h8300sxn", h8300sxnmode, 0},
+ {"sbranch", sbranch, L_8},
+ {"lbranch", sbranch, L_16},
+
+ {"int", pint, 0},
+ {"data.b", cons, 1},
+ {"data.w", cons, 2},
+ {"data.l", cons, 4},
+ {"form", listing_psize, 0},
+ {"heading", listing_title, 0},
+ {"import", s_ignore, 0},
+ {"page", listing_eject, 0},
+ {"program", s_ignore, 0},
+
+#ifdef OBJ_ELF
+ {"section", h8300_elf_section, 0},
+ {"section.s", h8300_elf_section, 0},
+ {"sect", h8300_elf_section, 0},
+ {"sect.s", h8300_elf_section, 0},
+#endif
+
+ {0, 0, 0}
+};
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ As in 0f12.456
+ or 0d1.2345e12. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics. */
+
+/* This function is called once, at assembler startup time. This
+ should set up all the tables, etc. that the MD part of the assembler
+ needs. */
+
+void
+md_begin (void)
+{
+ unsigned int nopcodes;
+ struct h8_opcode *p, *p1;
+ struct h8_instruction *pi;
+ char prev_buffer[100];
+ int idx = 0;
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_h8300, bfd_mach_h8300))
+ as_warn (_("could not set architecture and machine"));
+
+ opcode_hash_control = hash_new ();
+ prev_buffer[0] = 0;
+
+ nopcodes = sizeof (h8_opcodes) / sizeof (struct h8_opcode);
+
+ h8_instructions = (struct h8_instruction *)
+ xmalloc (nopcodes * sizeof (struct h8_instruction));
+
+ pi = h8_instructions;
+ p1 = h8_opcodes;
+ /* We do a minimum amount of sorting on the opcode table; this is to
+ make it easy to describe the mova instructions without unnecessary
+ code duplication.
+ Sorting only takes place inside blocks of instructions of the form
+ X/Y, so for example mova/b, mova/w and mova/l can be intermixed. */
+ while (p1)
+ {
+ struct h8_opcode *first_skipped = 0;
+ int len, cmplen = 0;
+ char *src = p1->name;
+ char *dst, *buffer;
+
+ if (p1->name == 0)
+ break;
+ /* Strip off any . part when inserting the opcode and only enter
+ unique codes into the hash table. */
+ dst = buffer = malloc (strlen (src) + 1);
+ while (*src)
+ {
+ if (*src == '.')
+ {
+ src++;
+ break;
+ }
+ if (*src == '/')
+ cmplen = src - p1->name + 1;
+ *dst++ = *src++;
+ }
+ *dst = 0;
+ len = dst - buffer;
+ if (cmplen == 0)
+ cmplen = len;
+ hash_insert (opcode_hash_control, buffer, (char *) pi);
+ strcpy (prev_buffer, buffer);
+ idx++;
+
+ for (p = p1; p->name; p++)
+ {
+ /* A negative TIME is used to indicate that we've added this opcode
+ already. */
+ if (p->time == -1)
+ continue;
+ if (strncmp (p->name, buffer, cmplen) != 0
+ || (p->name[cmplen] != '\0' && p->name[cmplen] != '.'
+ && p->name[cmplen - 1] != '/'))
+ {
+ if (first_skipped == 0)
+ first_skipped = p;
+ break;
+ }
+ if (strncmp (p->name, buffer, len) != 0)
+ {
+ if (first_skipped == 0)
+ first_skipped = p;
+ continue;
+ }
+
+ p->time = -1;
+ pi->size = p->name[len] == '.' ? p->name[len + 1] : 0;
+ pi->idx = idx;
+
+ /* Find the number of operands. */
+ pi->noperands = 0;
+ while (pi->noperands < 3 && p->args.nib[pi->noperands] != (op_type) E)
+ pi->noperands++;
+
+ /* Find the length of the opcode in bytes. */
+ pi->length = 0;
+ while (p->data.nib[pi->length * 2] != (op_type) E)
+ pi->length++;
+
+ pi->opcode = p;
+ pi++;
+ }
+ p1 = first_skipped;
+ }
+
+ /* Add entry for the NULL vector terminator. */
+ pi->length = 0;
+ pi->noperands = 0;
+ pi->idx = 0;
+ pi->size = 0;
+ pi->opcode = 0;
+
+ linkrelax = 1;
+}
+
+struct h8_op
+{
+ op_type mode;
+ unsigned reg;
+ expressionS exp;
+};
+
+static void clever_message (const struct h8_instruction *, struct h8_op *);
+static void fix_operand_size (struct h8_op *, int);
+static void build_bytes (const struct h8_instruction *, struct h8_op *);
+static void do_a_fix_imm (int, int, struct h8_op *, int, const struct h8_instruction *);
+static void check_operand (struct h8_op *, unsigned int, char *);
+static const struct h8_instruction * get_specific (const struct h8_instruction *, struct h8_op *, int) ;
+static char *get_operands (unsigned, char *, struct h8_op *);
+static void get_operand (char **, struct h8_op *, int);
+static int parse_reg (char *, op_type *, unsigned *, int);
+static char *skip_colonthing (char *, int *);
+static char *parse_exp (char *, struct h8_op *);
+
+static int constant_fits_size_p (struct h8_op *, int, int);
+
+/*
+ parse operands
+ WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
+ r0l,r0h,..r7l,r7h
+ @WREG
+ @WREG+
+ @-WREG
+ #const
+ ccr
+*/
+
+/* Try to parse a reg name. Return the number of chars consumed. */
+
+static int
+parse_reg (char *src, op_type *mode, unsigned int *reg, int direction)
+{
+ char *end;
+ int len;
+
+ /* Cribbed from get_symbol_end. */
+ if (!is_name_beginner (*src) || *src == '\001')
+ return 0;
+ end = src + 1;
+ while ((is_part_of_name (*end) && *end != '.') || *end == '\001')
+ end++;
+ len = end - src;
+
+ if (len == 2 && TOLOWER (src[0]) == 's' && TOLOWER (src[1]) == 'p')
+ {
+ *mode = PSIZE | REG | direction;
+ *reg = 7;
+ return len;
+ }
+ if (len == 3 &&
+ TOLOWER (src[0]) == 'c' &&
+ TOLOWER (src[1]) == 'c' &&
+ TOLOWER (src[2]) == 'r')
+ {
+ *mode = CCR;
+ *reg = 0;
+ return len;
+ }
+ if (len == 3 &&
+ TOLOWER (src[0]) == 'e' &&
+ TOLOWER (src[1]) == 'x' &&
+ TOLOWER (src[2]) == 'r')
+ {
+ *mode = EXR;
+ *reg = 1;
+ return len;
+ }
+ if (len == 3 &&
+ TOLOWER (src[0]) == 'v' &&
+ TOLOWER (src[1]) == 'b' &&
+ TOLOWER (src[2]) == 'r')
+ {
+ *mode = VBR;
+ *reg = 6;
+ return len;
+ }
+ if (len == 3 &&
+ TOLOWER (src[0]) == 's' &&
+ TOLOWER (src[1]) == 'b' &&
+ TOLOWER (src[2]) == 'r')
+ {
+ *mode = SBR;
+ *reg = 7;
+ return len;
+ }
+ if (len == 2 && TOLOWER (src[0]) == 'f' && TOLOWER (src[1]) == 'p')
+ {
+ *mode = PSIZE | REG | direction;
+ *reg = 6;
+ return len;
+ }
+ if (len == 3 && TOLOWER (src[0]) == 'e' && TOLOWER (src[1]) == 'r' &&
+ src[2] >= '0' && src[2] <= '7')
+ {
+ *mode = L_32 | REG | direction;
+ *reg = src[2] - '0';
+ if (!Hmode)
+ as_warn (_("Reg not valid for H8/300"));
+ return len;
+ }
+ if (len == 2 && TOLOWER (src[0]) == 'e' && src[1] >= '0' && src[1] <= '7')
+ {
+ *mode = L_16 | REG | direction;
+ *reg = src[1] - '0' + 8;
+ if (!Hmode)
+ as_warn (_("Reg not valid for H8/300"));
+ return len;
+ }
+
+ if (TOLOWER (src[0]) == 'r')
+ {
+ if (src[1] >= '0' && src[1] <= '7')
+ {
+ if (len == 3 && TOLOWER (src[2]) == 'l')
+ {
+ *mode = L_8 | REG | direction;
+ *reg = (src[1] - '0') + 8;
+ return len;
+ }
+ if (len == 3 && TOLOWER (src[2]) == 'h')
+ {
+ *mode = L_8 | REG | direction;
+ *reg = (src[1] - '0');
+ return len;
+ }
+ if (len == 2)
+ {
+ *mode = L_16 | REG | direction;
+ *reg = (src[1] - '0');
+ return len;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/* Parse an immediate or address-related constant and store it in OP.
+ If the user also specifies the operand's size, store that size
+ in OP->MODE, otherwise leave it for later code to decide. */
+
+static char *
+parse_exp (char *src, struct h8_op *op)
+{
+ char *save;
+
+ save = input_line_pointer;
+ input_line_pointer = src;
+ expression (&op->exp);
+ if (op->exp.X_op == O_absent)
+ as_bad (_("missing operand"));
+ src = input_line_pointer;
+ input_line_pointer = save;
+
+ return skip_colonthing (src, &op->mode);
+}
+
+
+/* If SRC starts with an explicit operand size, skip it and store the size
+ in *MODE. Leave *MODE unchanged otherwise. */
+
+static char *
+skip_colonthing (char *src, int *mode)
+{
+ if (*src == ':')
+ {
+ src++;
+ *mode &= ~SIZE;
+ if (src[0] == '8' && !ISDIGIT (src[1]))
+ *mode |= L_8;
+ else if (src[0] == '2' && !ISDIGIT (src[1]))
+ *mode |= L_2;
+ else if (src[0] == '3' && !ISDIGIT (src[1]))
+ *mode |= L_3;
+ else if (src[0] == '4' && !ISDIGIT (src[1]))
+ *mode |= L_4;
+ else if (src[0] == '5' && !ISDIGIT (src[1]))
+ *mode |= L_5;
+ else if (src[0] == '2' && src[1] == '4' && !ISDIGIT (src[2]))
+ *mode |= L_24;
+ else if (src[0] == '3' && src[1] == '2' && !ISDIGIT (src[2]))
+ *mode |= L_32;
+ else if (src[0] == '1' && src[1] == '6' && !ISDIGIT (src[2]))
+ *mode |= L_16;
+ else
+ as_bad (_("invalid operand size requested"));
+
+ while (ISDIGIT (*src))
+ src++;
+ }
+ return src;
+}
+
+/* The many forms of operand:
+
+ Rn Register direct
+ @Rn Register indirect
+ @(exp[:16], Rn) Register indirect with displacement
+ @Rn+
+ @-Rn
+ @aa:8 absolute 8 bit
+ @aa:16 absolute 16 bit
+ @aa absolute 16 bit
+
+ #xx[:size] immediate data
+ @(exp:[8], pc) pc rel
+ @@aa[:8] memory indirect. */
+
+static int
+constant_fits_width_p (struct h8_op *operand, offsetT width)
+{
+ offsetT num;
+
+ num = ((operand->exp.X_add_number & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ return (num & ~width) == 0 || (num | width) == ~0;
+}
+
+static int
+constant_fits_size_p (struct h8_op *operand, int size, int no_symbols)
+{
+ offsetT num;
+
+ if (no_symbols
+ && (operand->exp.X_add_symbol != 0 || operand->exp.X_op_symbol != 0))
+ return 0;
+ num = operand->exp.X_add_number & 0xffffffff;
+ switch (size)
+ {
+ case L_2:
+ return (num & ~3) == 0;
+ case L_3:
+ return (num & ~7) == 0;
+ case L_3NZ:
+ return num >= 1 && num < 8;
+ case L_4:
+ return (num & ~15) == 0;
+ case L_5:
+ return num >= 1 && num < 32;
+ case L_8:
+ num = (num ^ 0x80000000) - 0x80000000;
+ return (num & ~0xFF) == 0 || (num | 0x7F) == ~0;
+ case L_8U:
+ return (num & ~0xFF) == 0;
+ case L_16:
+ num = (num ^ 0x80000000) - 0x80000000;
+ return (num & ~0xFFFF) == 0 || (num | 0x7FFF) == ~0;
+ case L_16U:
+ return (num & ~0xFFFF) == 0;
+ case L_32:
+ return 1;
+ default:
+ abort ();
+ }
+}
+
+static void
+get_operand (char **ptr, struct h8_op *op, int direction)
+{
+ char *src = *ptr;
+ op_type mode;
+ unsigned int num;
+ unsigned int len;
+
+ op->mode = 0;
+
+ /* Check for '(' and ')' for instructions ldm and stm. */
+ if (src[0] == '(' && src[8] == ')')
+ ++ src;
+
+ /* Gross. Gross. ldm and stm have a format not easily handled
+ by get_operand. We deal with it explicitly here. */
+ if (TOLOWER (src[0]) == 'e' && TOLOWER (src[1]) == 'r' &&
+ ISDIGIT (src[2]) && src[3] == '-' &&
+ TOLOWER (src[4]) == 'e' && TOLOWER (src[5]) == 'r' && ISDIGIT (src[6]))
+ {
+ int low, high;
+
+ low = src[2] - '0';
+ high = src[6] - '0';
+
+ /* Check register pair's validity as per tech note TN-H8*-193A/E
+ from Renesas for H8S and H8SX hardware manual. */
+ if ( !(low == 0 && (high == 1 || high == 2 || high == 3))
+ && !(low == 1 && (high == 2 || high == 3 || high == 4) && SXmode)
+ && !(low == 2 && (high == 3 || ((high == 4 || high == 5) && SXmode)))
+ && !(low == 3 && (high == 4 || high == 5 || high == 6) && SXmode)
+ && !(low == 4 && (high == 5 || high == 6))
+ && !(low == 4 && high == 7 && SXmode)
+ && !(low == 5 && (high == 6 || high == 7) && SXmode)
+ && !(low == 6 && high == 7 && SXmode))
+ as_bad (_("Invalid register list for ldm/stm\n"));
+
+ /* Even sicker. We encode two registers into op->reg. One
+ for the low register to save, the other for the high
+ register to save; we also set the high bit in op->reg
+ so we know this is "very special". */
+ op->reg = 0x80000000 | (high << 8) | low;
+ op->mode = REG;
+ if (src[7] == ')')
+ *ptr = src + 8;
+ else
+ *ptr = src + 7;
+ return;
+ }
+
+ len = parse_reg (src, &op->mode, &op->reg, direction);
+ if (len)
+ {
+ src += len;
+ if (*src == '.')
+ {
+ int size = op->mode & SIZE;
+ switch (src[1])
+ {
+ case 'l': case 'L':
+ if (size != L_32)
+ as_warn (_("mismatch between register and suffix"));
+ op->mode = (op->mode & ~MODE) | LOWREG;
+ break;
+ case 'w': case 'W':
+ if (size != L_32 && size != L_16)
+ as_warn (_("mismatch between register and suffix"));
+ op->mode = (op->mode & ~MODE) | LOWREG;
+ op->mode = (op->mode & ~SIZE) | L_16;
+ break;
+ case 'b': case 'B':
+ op->mode = (op->mode & ~MODE) | LOWREG;
+ if (size != L_32 && size != L_8)
+ as_warn (_("mismatch between register and suffix"));
+ op->mode = (op->mode & ~MODE) | LOWREG;
+ op->mode = (op->mode & ~SIZE) | L_8;
+ break;
+ default:
+ as_warn (_("invalid suffix after register."));
+ break;
+ }
+ src += 2;
+ }
+ *ptr = src;
+ return;
+ }
+
+ if (*src == '@')
+ {
+ src++;
+ if (*src == '@')
+ {
+ *ptr = parse_exp (src + 1, op);
+ if (op->exp.X_add_number >= 0x100)
+ {
+ int divisor = 1;
+
+ op->mode = VECIND;
+ /* FIXME : 2? or 4? */
+ if (op->exp.X_add_number >= 0x400)
+ as_bad (_("address too high for vector table jmp/jsr"));
+ else if (op->exp.X_add_number >= 0x200)
+ divisor = 4;
+ else
+ divisor = 2;
+
+ op->exp.X_add_number = op->exp.X_add_number / divisor - 0x80;
+ }
+ else
+ op->mode = MEMIND;
+ return;
+ }
+
+ if (*src == '-' || *src == '+')
+ {
+ len = parse_reg (src + 1, &mode, &num, direction);
+ if (len == 0)
+ {
+ /* Oops, not a reg after all, must be ordinary exp. */
+ op->mode = ABS | direction;
+ *ptr = parse_exp (src, op);
+ return;
+ }
+
+ if (((mode & SIZE) != PSIZE)
+ /* For Normal mode accept 16 bit and 32 bit pointer registers. */
+ && (!Nmode || ((mode & SIZE) != L_32)))
+ as_bad (_("Wrong size pointer register for architecture."));
+
+ op->mode = src[0] == '-' ? RDPREDEC : RDPREINC;
+ op->reg = num;
+ *ptr = src + 1 + len;
+ return;
+ }
+ if (*src == '(')
+ {
+ src++;
+
+ /* See if this is @(ERn.x, PC). */
+ len = parse_reg (src, &mode, &op->reg, direction);
+ if (len != 0 && (mode & MODE) == REG && src[len] == '.')
+ {
+ switch (TOLOWER (src[len + 1]))
+ {
+ case 'b':
+ mode = PCIDXB | direction;
+ break;
+ case 'w':
+ mode = PCIDXW | direction;
+ break;
+ case 'l':
+ mode = PCIDXL | direction;
+ break;
+ default:
+ mode = 0;
+ break;
+ }
+ if (mode
+ && src[len + 2] == ','
+ && TOLOWER (src[len + 3]) != 'p'
+ && TOLOWER (src[len + 4]) != 'c'
+ && src[len + 5] != ')')
+ {
+ *ptr = src + len + 6;
+ op->mode |= mode;
+ return;
+ }
+ /* Fall through into disp case - the grammar is somewhat
+ ambiguous, so we should try whether it's a DISP operand
+ after all ("ER3.L" might be a poorly named label...). */
+ }
+
+ /* Disp. */
+
+ /* Start off assuming a 16 bit offset. */
+
+ src = parse_exp (src, op);
+ if (*src == ')')
+ {
+ op->mode |= ABS | direction;
+ *ptr = src + 1;
+ return;
+ }
+
+ if (*src != ',')
+ {
+ as_bad (_("expected @(exp, reg16)"));
+ return;
+ }
+ src++;
+
+ len = parse_reg (src, &mode, &op->reg, direction);
+ if (len == 0 || (mode & MODE) != REG)
+ {
+ as_bad (_("expected @(exp, reg16)"));
+ return;
+ }
+ src += len;
+ if (src[0] == '.')
+ {
+ switch (TOLOWER (src[1]))
+ {
+ case 'b':
+ op->mode |= INDEXB | direction;
+ break;
+ case 'w':
+ op->mode |= INDEXW | direction;
+ break;
+ case 'l':
+ op->mode |= INDEXL | direction;
+ break;
+ default:
+ as_bad (_("expected .L, .W or .B for register in indexed addressing mode"));
+ }
+ src += 2;
+ op->reg &= 7;
+ }
+ else
+ op->mode |= DISP | direction;
+ src = skip_colonthing (src, &op->mode);
+
+ if (*src != ')' && '(')
+ {
+ as_bad (_("expected @(exp, reg16)"));
+ return;
+ }
+ *ptr = src + 1;
+ return;
+ }
+ len = parse_reg (src, &mode, &num, direction);
+
+ if (len)
+ {
+ src += len;
+ if (*src == '+' || *src == '-')
+ {
+ if (((mode & SIZE) != PSIZE)
+ /* For Normal mode accept 16 bit and 32 bit pointer registers. */
+ && (!Nmode || ((mode & SIZE) != L_32)))
+ as_bad (_("Wrong size pointer register for architecture."));
+ op->mode = *src == '+' ? RSPOSTINC : RSPOSTDEC;
+ op->reg = num;
+ src++;
+ *ptr = src;
+ return;
+ }
+ if (((mode & SIZE) != PSIZE)
+ /* For Normal mode accept 16 bit and 32 bit pointer registers. */
+ && (!Nmode || ((mode & SIZE) != L_32)))
+ as_bad (_("Wrong size pointer register for architecture."));
+
+ op->mode = direction | IND | PSIZE;
+ op->reg = num;
+ *ptr = src;
+
+ return;
+ }
+ else
+ {
+ /* must be a symbol */
+
+ op->mode = ABS | direction;
+ *ptr = parse_exp (src, op);
+ return;
+ }
+ }
+
+ if (*src == '#')
+ {
+ op->mode = IMM;
+ *ptr = parse_exp (src + 1, op);
+ return;
+ }
+ else if (strncmp (src, "mach", 4) == 0 ||
+ strncmp (src, "macl", 4) == 0 ||
+ strncmp (src, "MACH", 4) == 0 ||
+ strncmp (src, "MACL", 4) == 0)
+ {
+ op->reg = TOLOWER (src[3]) == 'l';
+ op->mode = MACREG;
+ *ptr = src + 4;
+ return;
+ }
+ else
+ {
+ op->mode = PCREL;
+ *ptr = parse_exp (src, op);
+ }
+}
+
+static char *
+get_operands (unsigned int noperands, char *op_end, struct h8_op *operand)
+{
+ char *ptr = op_end;
+
+ switch (noperands)
+ {
+ case 0:
+ break;
+
+ case 1:
+ ptr++;
+ get_operand (&ptr, operand + 0, SRC);
+ if (*ptr == ',')
+ {
+ ptr++;
+ get_operand (&ptr, operand + 1, DST);
+ }
+ break;
+
+ case 2:
+ ptr++;
+ get_operand (&ptr, operand + 0, SRC);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, DST);
+ break;
+
+ case 3:
+ ptr++;
+ get_operand (&ptr, operand + 0, SRC);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, DST);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 2, OP3);
+ break;
+
+ default:
+ abort ();
+ }
+
+ return ptr;
+}
+
+/* MOVA has special requirements. Rather than adding twice the amount of
+ addressing modes, we simply special case it a bit. */
+static void
+get_mova_operands (char *op_end, struct h8_op *operand)
+{
+ char *ptr = op_end;
+
+ if (ptr[1] != '@' || ptr[2] != '(')
+ goto error;
+ ptr += 3;
+ operand[0].mode = 0;
+ ptr = parse_exp (ptr, &operand[0]);
+
+ if (*ptr !=',')
+ goto error;
+ ptr++;
+ get_operand (&ptr, operand + 1, DST);
+
+ if (*ptr =='.')
+ {
+ ptr++;
+ switch (*ptr++)
+ {
+ case 'b': case 'B':
+ operand[0].mode = (operand[0].mode & ~MODE) | INDEXB;
+ break;
+ case 'w': case 'W':
+ operand[0].mode = (operand[0].mode & ~MODE) | INDEXW;
+ break;
+ case 'l': case 'L':
+ operand[0].mode = (operand[0].mode & ~MODE) | INDEXL;
+ break;
+ default:
+ goto error;
+ }
+ }
+ else if ((operand[1].mode & MODE) == LOWREG)
+ {
+ switch (operand[1].mode & SIZE)
+ {
+ case L_8:
+ operand[0].mode = (operand[0].mode & ~MODE) | INDEXB;
+ break;
+ case L_16:
+ operand[0].mode = (operand[0].mode & ~MODE) | INDEXW;
+ break;
+ case L_32:
+ operand[0].mode = (operand[0].mode & ~MODE) | INDEXL;
+ break;
+ default:
+ goto error;
+ }
+ }
+ else
+ goto error;
+
+ if (*ptr++ != ')' || *ptr++ != ',')
+ goto error;
+ get_operand (&ptr, operand + 2, OP3);
+ /* See if we can use the short form of MOVA. */
+ if (((operand[1].mode & MODE) == REG || (operand[1].mode & MODE) == LOWREG)
+ && (operand[2].mode & MODE) == REG
+ && (operand[1].reg & 7) == (operand[2].reg & 7))
+ {
+ operand[1].mode = operand[2].mode = 0;
+ operand[0].reg = operand[2].reg & 7;
+ }
+ return;
+
+ error:
+ as_bad (_("expected valid addressing mode for mova: \"@(disp, ea.sz),ERn\""));
+}
+
+static void
+get_rtsl_operands (char *ptr, struct h8_op *operand)
+{
+ int mode, len, type = 0;
+ unsigned int num, num2;
+
+ ptr++;
+ if (*ptr == '(')
+ {
+ ptr++;
+ type = 1;
+ }
+ len = parse_reg (ptr, &mode, &num, SRC);
+ if (len == 0 || (mode & MODE) != REG)
+ {
+ as_bad (_("expected register"));
+ return;
+ }
+ ptr += len;
+ if (*ptr == '-')
+ {
+ len = parse_reg (++ptr, &mode, &num2, SRC);
+ if (len == 0 || (mode & MODE) != REG)
+ {
+ as_bad (_("expected register"));
+ return;
+ }
+ ptr += len;
+ /* CONST_xxx are used as placeholders in the opcode table. */
+ num = num2 - num;
+ if (num > 3)
+ {
+ as_bad (_("invalid register list"));
+ return;
+ }
+ }
+ else
+ num2 = num, num = 0;
+ if (type == 1 && *ptr++ != ')')
+ {
+ as_bad (_("expected closing paren"));
+ return;
+ }
+ operand[0].mode = RS32;
+ operand[1].mode = RD32;
+ operand[0].reg = num;
+ operand[1].reg = num2;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided. */
+
+static const struct h8_instruction *
+get_specific (const struct h8_instruction *instruction,
+ struct h8_op *operands, int size)
+{
+ const struct h8_instruction *this_try = instruction;
+ const struct h8_instruction *found_other = 0, *found_mismatched = 0;
+ int found = 0;
+ int this_index = instruction->idx;
+ int noperands = 0;
+
+ /* There's only one ldm/stm and it's easier to just
+ get out quick for them. */
+ if (OP_KIND (instruction->opcode->how) == O_LDM
+ || OP_KIND (instruction->opcode->how) == O_STM)
+ return this_try;
+
+ while (noperands < 3 && operands[noperands].mode != 0)
+ noperands++;
+
+ while (this_index == instruction->idx && !found)
+ {
+ int this_size;
+
+ found = 1;
+ this_try = instruction++;
+ this_size = this_try->opcode->how & SN;
+
+ if (this_try->noperands != noperands)
+ found = 0;
+ else if (this_try->noperands > 0)
+ {
+ int i;
+
+ for (i = 0; i < this_try->noperands && found; i++)
+ {
+ op_type op = this_try->opcode->args.nib[i];
+ int op_mode = op & MODE;
+ int op_size = op & SIZE;
+ int x = operands[i].mode;
+ int x_mode = x & MODE;
+ int x_size = x & SIZE;
+
+ if (op_mode == LOWREG && (x_mode == REG || x_mode == LOWREG))
+ {
+ if ((x_size == L_8 && (operands[i].reg & 8) == 0)
+ || (x_size == L_16 && (operands[i].reg & 8) == 8))
+ as_warn (_("can't use high part of register in operand %d"), i);
+
+ if (x_size != op_size)
+ found = 0;
+ }
+ else if (op_mode == REG)
+ {
+ if (x_mode == LOWREG)
+ x_mode = REG;
+ if (x_mode != REG)
+ found = 0;
+
+ if (x_size == L_P)
+ x_size = (Hmode ? L_32 : L_16);
+ if (op_size == L_P)
+ op_size = (Hmode ? L_32 : L_16);
+
+ /* The size of the reg is v important. */
+ if (op_size != x_size)
+ found = 0;
+ }
+ else if (op_mode & CTRL) /* control register */
+ {
+ if (!(x_mode & CTRL))
+ found = 0;
+
+ switch (x_mode)
+ {
+ case CCR:
+ if (op_mode != CCR &&
+ op_mode != CCR_EXR &&
+ op_mode != CC_EX_VB_SB)
+ found = 0;
+ break;
+ case EXR:
+ if (op_mode != EXR &&
+ op_mode != CCR_EXR &&
+ op_mode != CC_EX_VB_SB)
+ found = 0;
+ break;
+ case MACH:
+ if (op_mode != MACH &&
+ op_mode != MACREG)
+ found = 0;
+ break;
+ case MACL:
+ if (op_mode != MACL &&
+ op_mode != MACREG)
+ found = 0;
+ break;
+ case VBR:
+ if (op_mode != VBR &&
+ op_mode != VBR_SBR &&
+ op_mode != CC_EX_VB_SB)
+ found = 0;
+ break;
+ case SBR:
+ if (op_mode != SBR &&
+ op_mode != VBR_SBR &&
+ op_mode != CC_EX_VB_SB)
+ found = 0;
+ break;
+ }
+ }
+ else if ((op & ABSJMP) && (x_mode == ABS || x_mode == PCREL))
+ {
+ operands[i].mode &= ~MODE;
+ operands[i].mode |= ABSJMP;
+ /* But it may not be 24 bits long. */
+ if (x_mode == ABS && !Hmode)
+ {
+ operands[i].mode &= ~SIZE;
+ operands[i].mode |= L_16;
+ }
+ if ((operands[i].mode & SIZE) == L_32
+ && (op_mode & SIZE) != L_32)
+ found = 0;
+ }
+ else if (x_mode == IMM && op_mode != IMM)
+ {
+ offsetT num = operands[i].exp.X_add_number & 0xffffffff;
+ if (op_mode == KBIT || op_mode == DBIT)
+ /* This is ok if the immediate value is sensible. */;
+ else if (op_mode == CONST_2)
+ found = num == 2;
+ else if (op_mode == CONST_4)
+ found = num == 4;
+ else if (op_mode == CONST_8)
+ found = num == 8;
+ else if (op_mode == CONST_16)
+ found = num == 16;
+ else
+ found = 0;
+ }
+ else if (op_mode == PCREL && op_mode == x_mode)
+ {
+ /* movsd, bsr/bc and bsr/bs only come in PCREL16 flavour:
+ If x_size is L_8, promote it. */
+ if (OP_KIND (this_try->opcode->how) == O_MOVSD
+ || OP_KIND (this_try->opcode->how) == O_BSRBC
+ || OP_KIND (this_try->opcode->how) == O_BSRBS)
+ if (x_size == L_8)
+ x_size = L_16;
+
+ /* The size of the displacement is important. */
+ if (op_size != x_size)
+ found = 0;
+ }
+ else if ((op_mode == DISP || op_mode == IMM || op_mode == ABS
+ || op_mode == INDEXB || op_mode == INDEXW
+ || op_mode == INDEXL)
+ && op_mode == x_mode)
+ {
+ /* Promote a L_24 to L_32 if it makes us match. */
+ if (x_size == L_24 && op_size == L_32)
+ {
+ x &= ~SIZE;
+ x |= x_size = L_32;
+ }
+
+ if (((x_size == L_16 && op_size == L_16U)
+ || (x_size == L_8 && op_size == L_8U)
+ || (x_size == L_3 && op_size == L_3NZ))
+ /* We're deliberately more permissive for ABS modes. */
+ && (op_mode == ABS
+ || constant_fits_size_p (operands + i, op_size,
+ op & NO_SYMBOLS)))
+ x_size = op_size;
+
+ if (x_size != 0 && op_size != x_size)
+ found = 0;
+ else if (x_size == 0
+ && ! constant_fits_size_p (operands + i, op_size,
+ op & NO_SYMBOLS))
+ found = 0;
+ }
+ else if (op_mode != x_mode)
+ {
+ found = 0;
+ }
+ }
+ }
+ if (found)
+ {
+ if ((this_try->opcode->available == AV_H8SX && ! SXmode)
+ || (this_try->opcode->available == AV_H8S && ! Smode)
+ || (this_try->opcode->available == AV_H8H && ! Hmode))
+ found = 0, found_other = this_try;
+ else if (this_size != size && (this_size != SN && size != SN))
+ found_mismatched = this_try, found = 0;
+
+ }
+ }
+ if (found)
+ return this_try;
+ if (found_other)
+ {
+ as_warn (_("Opcode `%s' with these operand types not available in %s mode"),
+ found_other->opcode->name,
+ (! Hmode && ! Smode ? "H8/300"
+ : SXmode ? "H8sx"
+ : Smode ? "H8/300S"
+ : "H8/300H"));
+ }
+ else if (found_mismatched)
+ {
+ as_warn (_("mismatch between opcode size and operand size"));
+ return found_mismatched;
+ }
+ return 0;
+}
+
+static void
+check_operand (struct h8_op *operand, unsigned int width, char *string)
+{
+ if (operand->exp.X_add_symbol == 0
+ && operand->exp.X_op_symbol == 0)
+ {
+ /* No symbol involved, let's look at offset, it's dangerous if
+ any of the high bits are not 0 or ff's, find out by oring or
+ anding with the width and seeing if the answer is 0 or all
+ fs. */
+
+ if (! constant_fits_width_p (operand, width))
+ {
+ if (width == 255
+ && (operand->exp.X_add_number & 0xff00) == 0xff00)
+ {
+ /* Just ignore this one - which happens when trying to
+ fit a 16 bit address truncated into an 8 bit address
+ of something like bset. */
+ }
+ else if (strcmp (string, "@") == 0
+ && width == 0xffff
+ && (operand->exp.X_add_number & 0xff8000) == 0xff8000)
+ {
+ /* Just ignore this one - which happens when trying to
+ fit a 24 bit address truncated into a 16 bit address
+ of something like mov.w. */
+ }
+ else
+ {
+ as_warn (_("operand %s0x%lx out of range."), string,
+ (unsigned long) operand->exp.X_add_number);
+ }
+ }
+ }
+}
+
+/* RELAXMODE has one of 3 values:
+
+ 0 Output a "normal" reloc, no relaxing possible for this insn/reloc
+
+ 1 Output a relaxable 24bit absolute mov.w address relocation
+ (may relax into a 16bit absolute address).
+
+ 2 Output a relaxable 16/24 absolute mov.b address relocation
+ (may relax into an 8bit absolute address). */
+
+static void
+do_a_fix_imm (int offset, int nibble, struct h8_op *operand, int relaxmode, const struct h8_instruction *this_try)
+{
+ int idx;
+ int size;
+ int where;
+ char *bytes = frag_now->fr_literal + offset;
+
+ char *t = ((operand->mode & MODE) == IMM) ? "#" : "@";
+
+ if (operand->exp.X_add_symbol == 0)
+ {
+ switch (operand->mode & SIZE)
+ {
+ case L_2:
+ check_operand (operand, 0x3, t);
+ bytes[0] |= (operand->exp.X_add_number & 3) << (nibble ? 0 : 4);
+ break;
+ case L_3:
+ case L_3NZ:
+ check_operand (operand, 0x7, t);
+ bytes[0] |= (operand->exp.X_add_number & 7) << (nibble ? 0 : 4);
+ break;
+ case L_4:
+ check_operand (operand, 0xF, t);
+ bytes[0] |= (operand->exp.X_add_number & 15) << (nibble ? 0 : 4);
+ break;
+ case L_5:
+ check_operand (operand, 0x1F, t);
+ bytes[0] |= operand->exp.X_add_number & 31;
+ break;
+ case L_8:
+ case L_8U:
+ check_operand (operand, 0xff, t);
+ bytes[0] |= operand->exp.X_add_number;
+ break;
+ case L_16:
+ case L_16U:
+ check_operand (operand, 0xffff, t);
+ bytes[0] |= operand->exp.X_add_number >> 8;
+ bytes[1] |= operand->exp.X_add_number >> 0;
+#ifdef OBJ_ELF
+ /* MOVA needs both relocs to relax the second operand properly. */
+ if (relaxmode != 0
+ && (OP_KIND(this_try->opcode->how) == O_MOVAB
+ || OP_KIND(this_try->opcode->how) == O_MOVAW
+ || OP_KIND(this_try->opcode->how) == O_MOVAL))
+ {
+ idx = BFD_RELOC_16;
+ fix_new_exp (frag_now, offset, 2, &operand->exp, 0, idx);
+ }
+#endif
+ break;
+ case L_24:
+ check_operand (operand, 0xffffff, t);
+ bytes[0] |= operand->exp.X_add_number >> 16;
+ bytes[1] |= operand->exp.X_add_number >> 8;
+ bytes[2] |= operand->exp.X_add_number >> 0;
+ break;
+
+ case L_32:
+ /* This should be done with bfd. */
+ bytes[0] |= operand->exp.X_add_number >> 24;
+ bytes[1] |= operand->exp.X_add_number >> 16;
+ bytes[2] |= operand->exp.X_add_number >> 8;
+ bytes[3] |= operand->exp.X_add_number >> 0;
+ if (relaxmode != 0)
+ {
+#ifdef OBJ_ELF
+ if ((operand->mode & MODE) == DISP && relaxmode == 1)
+ idx = BFD_RELOC_H8_DISP32A16;
+ else
+#endif
+ idx = (relaxmode == 2) ? R_MOV24B1 : R_MOVL1;
+ fix_new_exp (frag_now, offset, 4, &operand->exp, 0, idx);
+ }
+ break;
+ }
+ }
+ else
+ {
+ switch (operand->mode & SIZE)
+ {
+ case L_24:
+ case L_32:
+ size = 4;
+ where = (operand->mode & SIZE) == L_24 ? -1 : 0;
+#ifdef OBJ_ELF
+ if ((operand->mode & MODE) == DISP && relaxmode == 1)
+ idx = BFD_RELOC_H8_DISP32A16;
+ else
+#endif
+ if (relaxmode == 2)
+ idx = R_MOV24B1;
+ else if (relaxmode == 1)
+ idx = R_MOVL1;
+ else
+ idx = R_RELLONG;
+ break;
+ default:
+ as_bad (_("Can't work out size of operand.\n"));
+ case L_16:
+ case L_16U:
+ size = 2;
+ where = 0;
+ if (relaxmode == 2)
+ idx = R_MOV16B1;
+ else
+ idx = R_RELWORD;
+ operand->exp.X_add_number =
+ ((operand->exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000;
+ operand->exp.X_add_number |= (bytes[0] << 8) | bytes[1];
+ break;
+ case L_8:
+ size = 1;
+ where = 0;
+ idx = R_RELBYTE;
+ operand->exp.X_add_number =
+ ((operand->exp.X_add_number & 0xff) ^ 0x80) - 0x80;
+ operand->exp.X_add_number |= bytes[0];
+ }
+
+ fix_new_exp (frag_now,
+ offset + where,
+ size,
+ &operand->exp,
+ 0,
+ idx);
+ }
+}
+
+/* Now we know what sort of opcodes it is, let's build the bytes. */
+
+static void
+build_bytes (const struct h8_instruction *this_try, struct h8_op *operand)
+{
+ int i;
+ char *output = frag_more (this_try->length);
+ const op_type *nibble_ptr = this_try->opcode->data.nib;
+ op_type c;
+ unsigned int nibble_count = 0;
+ int op_at[3];
+ int nib = 0;
+ int movb = 0;
+ char asnibbles[100];
+ char *p = asnibbles;
+ int high, low;
+
+ if (!Hmode && this_try->opcode->available != AV_H8)
+ as_warn (_("Opcode `%s' with these operand types not available in H8/300 mode"),
+ this_try->opcode->name);
+ else if (!Smode
+ && this_try->opcode->available != AV_H8
+ && this_try->opcode->available != AV_H8H)
+ as_warn (_("Opcode `%s' with these operand types not available in H8/300H mode"),
+ this_try->opcode->name);
+ else if (!SXmode
+ && this_try->opcode->available != AV_H8
+ && this_try->opcode->available != AV_H8H
+ && this_try->opcode->available != AV_H8S)
+ as_warn (_("Opcode `%s' with these operand types not available in H8/300S mode"),
+ this_try->opcode->name);
+
+ while (*nibble_ptr != (op_type) E)
+ {
+ int d;
+
+ nib = 0;
+ c = *nibble_ptr++;
+
+ d = (c & OP3) == OP3 ? 2 : (c & DST) == DST ? 1 : 0;
+
+ if (c < 16)
+ nib = c;
+ else
+ {
+ int c2 = c & MODE;
+
+ if (c2 == REG || c2 == LOWREG
+ || c2 == IND || c2 == PREINC || c2 == PREDEC
+ || c2 == POSTINC || c2 == POSTDEC)
+ {
+ nib = operand[d].reg;
+ if (c2 == LOWREG)
+ nib &= 7;
+ }
+
+ else if (c & CTRL) /* Control reg operand. */
+ nib = operand[d].reg;
+
+ else if ((c & DISPREG) == (DISPREG))
+ {
+ nib = operand[d].reg;
+ }
+ else if (c2 == ABS)
+ {
+ operand[d].mode = c;
+ op_at[d] = nibble_count;
+ nib = 0;
+ }
+ else if (c2 == IMM || c2 == PCREL || c2 == ABS
+ || (c & ABSJMP) || c2 == DISP)
+ {
+ operand[d].mode = c;
+ op_at[d] = nibble_count;
+ nib = 0;
+ }
+ else if ((c & IGNORE) || (c & DATA))
+ nib = 0;
+
+ else if (c2 == DBIT)
+ {
+ switch (operand[0].exp.X_add_number)
+ {
+ case 1:
+ nib = c;
+ break;
+ case 2:
+ nib = 0x8 | c;
+ break;
+ default:
+ as_bad (_("Need #1 or #2 here"));
+ }
+ }
+ else if (c2 == KBIT)
+ {
+ switch (operand[0].exp.X_add_number)
+ {
+ case 1:
+ nib = 0;
+ break;
+ case 2:
+ nib = 8;
+ break;
+ case 4:
+ if (!Hmode)
+ as_warn (_("#4 not valid on H8/300."));
+ nib = 9;
+ break;
+
+ default:
+ as_bad (_("Need #1 or #2 here"));
+ break;
+ }
+ /* Stop it making a fix. */
+ operand[0].mode = 0;
+ }
+
+ if (c & MEMRELAX)
+ operand[d].mode |= MEMRELAX;
+
+ if (c & B31)
+ nib |= 0x8;
+
+ if (c & B21)
+ nib |= 0x4;
+
+ if (c & B11)
+ nib |= 0x2;
+
+ if (c & B01)
+ nib |= 0x1;
+
+ if (c2 == MACREG)
+ {
+ if (operand[0].mode == MACREG)
+ /* stmac has mac[hl] as the first operand. */
+ nib = 2 + operand[0].reg;
+ else
+ /* ldmac has mac[hl] as the second operand. */
+ nib = 2 + operand[1].reg;
+ }
+ }
+ nibble_count++;
+
+ *p++ = nib;
+ }
+
+ /* Disgusting. Why, oh why didn't someone ask us for advice
+ on the assembler format. */
+ if (OP_KIND (this_try->opcode->how) == O_LDM)
+ {
+ high = (operand[1].reg >> 8) & 0xf;
+ low = (operand[1].reg) & 0xf;
+ asnibbles[2] = high - low;
+ asnibbles[7] = high;
+ }
+ else if (OP_KIND (this_try->opcode->how) == O_STM)
+ {
+ high = (operand[0].reg >> 8) & 0xf;
+ low = (operand[0].reg) & 0xf;
+ asnibbles[2] = high - low;
+ asnibbles[7] = low;
+ }
+
+ for (i = 0; i < this_try->length; i++)
+ output[i] = (asnibbles[i * 2] << 4) | asnibbles[i * 2 + 1];
+
+ /* Note if this is a mov.b or a bit manipulation instruction
+ there is a special relaxation which only applies. */
+ if ( this_try->opcode->how == O (O_MOV, SB)
+ || this_try->opcode->how == O (O_BCLR, SB)
+ || this_try->opcode->how == O (O_BAND, SB)
+ || this_try->opcode->how == O (O_BIAND, SB)
+ || this_try->opcode->how == O (O_BILD, SB)
+ || this_try->opcode->how == O (O_BIOR, SB)
+ || this_try->opcode->how == O (O_BIST, SB)
+ || this_try->opcode->how == O (O_BIXOR, SB)
+ || this_try->opcode->how == O (O_BLD, SB)
+ || this_try->opcode->how == O (O_BNOT, SB)
+ || this_try->opcode->how == O (O_BOR, SB)
+ || this_try->opcode->how == O (O_BSET, SB)
+ || this_try->opcode->how == O (O_BST, SB)
+ || this_try->opcode->how == O (O_BTST, SB)
+ || this_try->opcode->how == O (O_BXOR, SB))
+ movb = 1;
+
+ /* Output any fixes. */
+ for (i = 0; i < this_try->noperands; i++)
+ {
+ int x = operand[i].mode;
+ int x_mode = x & MODE;
+
+ if (x_mode == IMM || x_mode == DISP)
+ {
+#ifndef OBJ_ELF
+ /* Remove MEMRELAX flag added in h8300.h on mov with
+ addressing mode "register indirect with displacement". */
+ if (x_mode == DISP)
+ x &= ~MEMRELAX;
+#endif
+ do_a_fix_imm (output - frag_now->fr_literal + op_at[i] / 2,
+ op_at[i] & 1, operand + i, (x & MEMRELAX) != 0,
+ this_try);
+ }
+ else if (x_mode == ABS)
+ do_a_fix_imm (output - frag_now->fr_literal + op_at[i] / 2,
+ op_at[i] & 1, operand + i,
+ (x & MEMRELAX) ? movb + 1 : 0,
+ this_try);
+
+ else if (x_mode == PCREL)
+ {
+ int size16 = (x & SIZE) == L_16;
+ int size = size16 ? 2 : 1;
+ int type = size16 ? R_PCRWORD : R_PCRBYTE;
+ fixS *fixP;
+
+ check_operand (operand + i, size16 ? 0x7fff : 0x7f, "@");
+
+ if (operand[i].exp.X_add_number & 1)
+ as_warn (_("branch operand has odd offset (%lx)\n"),
+ (unsigned long) operand->exp.X_add_number);
+#ifndef OBJ_ELF
+ /* The COFF port has always been off by one, changing it
+ now would be an incompatible change, so we leave it as-is.
+
+ We don't want to do this for ELF as we want to be
+ compatible with the proposed ELF format from Hitachi. */
+ operand[i].exp.X_add_number -= 1;
+#endif
+ if (size16)
+ {
+ operand[i].exp.X_add_number =
+ ((operand[i].exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000;
+ }
+ else
+ {
+ operand[i].exp.X_add_number =
+ ((operand[i].exp.X_add_number & 0xff) ^ 0x80) - 0x80;
+ }
+
+ /* For BRA/S. */
+ if (! size16)
+ operand[i].exp.X_add_number |= output[op_at[i] / 2];
+
+ fixP = fix_new_exp (frag_now,
+ output - frag_now->fr_literal + op_at[i] / 2,
+ size,
+ &operand[i].exp,
+ 1,
+ type);
+ fixP->fx_signed = 1;
+ }
+ else if (x_mode == MEMIND)
+ {
+ check_operand (operand + i, 0xff, "@@");
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ &operand[i].exp,
+ 0,
+ R_MEM_INDIRECT);
+ }
+ else if (x_mode == VECIND)
+ {
+ check_operand (operand + i, 0x7f, "@@");
+ /* FIXME: approximating the effect of "B31" here...
+ This is very hackish, and ought to be done a better way. */
+ operand[i].exp.X_add_number |= 0x80;
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + 1,
+ 1,
+ &operand[i].exp,
+ 0,
+ R_MEM_INDIRECT);
+ }
+ else if (x & ABSJMP)
+ {
+ int where = 0;
+ bfd_reloc_code_real_type reloc_type = R_JMPL1;
+
+#ifdef OBJ_ELF
+ /* To be compatible with the proposed H8 ELF format, we
+ want the relocation's offset to point to the first byte
+ that will be modified, not to the start of the instruction. */
+
+ if ((operand->mode & SIZE) == L_32)
+ {
+ where = 2;
+ reloc_type = R_RELLONG;
+ }
+ else
+ where = 1;
+#endif
+
+ /* This jmp may be a jump or a branch. */
+
+ check_operand (operand + i,
+ SXmode ? 0xffffffff : Hmode ? 0xffffff : 0xffff,
+ "@");
+
+ if (operand[i].exp.X_add_number & 1)
+ as_warn (_("branch operand has odd offset (%lx)\n"),
+ (unsigned long) operand->exp.X_add_number);
+
+ if (!Hmode)
+ operand[i].exp.X_add_number =
+ ((operand[i].exp.X_add_number & 0xffff) ^ 0x8000) - 0x8000;
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + where,
+ 4,
+ &operand[i].exp,
+ 0,
+ reloc_type);
+ }
+ }
+}
+
+/* Try to give an intelligent error message for common and simple to
+ detect errors. */
+
+static void
+clever_message (const struct h8_instruction *instruction,
+ struct h8_op *operand)
+{
+ /* Find out if there was more than one possible opcode. */
+
+ if ((instruction + 1)->idx != instruction->idx)
+ {
+ int argn;
+
+ /* Only one opcode of this flavour, try to guess which operand
+ didn't match. */
+ for (argn = 0; argn < instruction->noperands; argn++)
+ {
+ switch (instruction->opcode->args.nib[argn])
+ {
+ case RD16:
+ if (operand[argn].mode != RD16)
+ {
+ as_bad (_("destination operand must be 16 bit register"));
+ return;
+
+ }
+ break;
+
+ case RS8:
+ if (operand[argn].mode != RS8)
+ {
+ as_bad (_("source operand must be 8 bit register"));
+ return;
+ }
+ break;
+
+ case ABS16DST:
+ if (operand[argn].mode != ABS16DST)
+ {
+ as_bad (_("destination operand must be 16bit absolute address"));
+ return;
+ }
+ break;
+ case RD8:
+ if (operand[argn].mode != RD8)
+ {
+ as_bad (_("destination operand must be 8 bit register"));
+ return;
+ }
+ break;
+
+ case ABS16SRC:
+ if (operand[argn].mode != ABS16SRC)
+ {
+ as_bad (_("source operand must be 16bit absolute address"));
+ return;
+ }
+ break;
+
+ }
+ }
+ }
+ as_bad (_("invalid operands"));
+}
+
+
+/* If OPERAND is part of an address, adjust its size and value given
+ that it addresses SIZE bytes.
+
+ This function decides how big non-immediate constants are when no
+ size was explicitly given. It also scales down the assembly-level
+ displacement in an @(d:2,ERn) operand. */
+
+static void
+fix_operand_size (struct h8_op *operand, int size)
+{
+ if (SXmode && (operand->mode & MODE) == DISP)
+ {
+ /* If the user didn't specify an operand width, see if we
+ can use @(d:2,ERn). */
+ if ((operand->mode & SIZE) == 0
+ && operand->exp.X_add_symbol == 0
+ && operand->exp.X_op_symbol == 0
+ && (operand->exp.X_add_number == size
+ || operand->exp.X_add_number == size * 2
+ || operand->exp.X_add_number == size * 3))
+ operand->mode |= L_2;
+
+ /* Scale down the displacement in an @(d:2,ERn) operand.
+ X_add_number then contains the desired field value. */
+ if ((operand->mode & SIZE) == L_2)
+ {
+ if (operand->exp.X_add_number % size != 0)
+ as_warn (_("operand/size mis-match"));
+ operand->exp.X_add_number /= size;
+ }
+ }
+
+ if ((operand->mode & SIZE) == 0)
+ switch (operand->mode & MODE)
+ {
+ case DISP:
+ case INDEXB:
+ case INDEXW:
+ case INDEXL:
+ case ABS:
+ /* Pick a 24-bit address unless we know that a 16-bit address
+ is safe. get_specific() will relax L_24 into L_32 where
+ necessary. */
+ if (Hmode
+ && !Nmode
+ && ((((addressT) operand->exp.X_add_number + 0x8000)
+ & 0xffffffff) > 0xffff
+ || operand->exp.X_add_symbol != 0
+ || operand->exp.X_op_symbol != 0))
+ operand->mode |= L_24;
+ else
+ operand->mode |= L_16;
+ break;
+
+ case PCREL:
+ if ((((addressT) operand->exp.X_add_number + 0x80)
+ & 0xffffffff) <= 0xff)
+ {
+ if (operand->exp.X_add_symbol != NULL)
+ operand->mode |= bsize;
+ else
+ operand->mode |= L_8;
+ }
+ else
+ operand->mode |= L_16;
+ break;
+ }
+}
+
+
+/* This is the guts of the machine-dependent assembler. STR points to
+ a machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles. */
+
+void
+md_assemble (char *str)
+{
+ char *op_start;
+ char *op_end;
+ struct h8_op operand[3];
+ const struct h8_instruction *instruction;
+ const struct h8_instruction *prev_instruction;
+
+ char *dot = 0;
+ char *slash = 0;
+ char c;
+ int size, i;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the op code end. */
+ for (op_start = op_end = str;
+ *op_end != 0 && *op_end != ' ';
+ op_end++)
+ {
+ if (*op_end == '.')
+ {
+ dot = op_end + 1;
+ *op_end = 0;
+ op_end += 2;
+ break;
+ }
+ else if (*op_end == '/' && ! slash)
+ slash = op_end;
+ }
+
+ if (op_end == op_start)
+ {
+ as_bad (_("can't find opcode "));
+ }
+ c = *op_end;
+
+ *op_end = 0;
+
+ /* The assembler stops scanning the opcode at slashes, so it fails
+ to make characters following them lower case. Fix them. */
+ if (slash)
+ while (*++slash)
+ *slash = TOLOWER (*slash);
+
+ instruction = (const struct h8_instruction *)
+ hash_find (opcode_hash_control, op_start);
+
+ if (instruction == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ /* We used to set input_line_pointer to the result of get_operands,
+ but that is wrong. Our caller assumes we don't change it. */
+
+ operand[0].mode = 0;
+ operand[1].mode = 0;
+ operand[2].mode = 0;
+
+ if (OP_KIND (instruction->opcode->how) == O_MOVAB
+ || OP_KIND (instruction->opcode->how) == O_MOVAW
+ || OP_KIND (instruction->opcode->how) == O_MOVAL)
+ get_mova_operands (op_end, operand);
+ else if (OP_KIND (instruction->opcode->how) == O_RTEL
+ || OP_KIND (instruction->opcode->how) == O_RTSL)
+ get_rtsl_operands (op_end, operand);
+ else
+ get_operands (instruction->noperands, op_end, operand);
+
+ *op_end = c;
+ prev_instruction = instruction;
+
+ /* Now we have operands from instruction.
+ Let's check them out for ldm and stm. */
+ if (OP_KIND (instruction->opcode->how) == O_LDM)
+ {
+ /* The first operand must be @er7+, and the
+ second operand must be a register pair. */
+ if ((operand[0].mode != RSINC)
+ || (operand[0].reg != 7)
+ || ((operand[1].reg & 0x80000000) == 0))
+ as_bad (_("invalid operand in ldm"));
+ }
+ else if (OP_KIND (instruction->opcode->how) == O_STM)
+ {
+ /* The first operand must be a register pair,
+ and the second operand must be @-er7. */
+ if (((operand[0].reg & 0x80000000) == 0)
+ || (operand[1].mode != RDDEC)
+ || (operand[1].reg != 7))
+ as_bad (_("invalid operand in stm"));
+ }
+
+ size = SN;
+ if (dot)
+ {
+ switch (TOLOWER (*dot))
+ {
+ case 'b':
+ size = SB;
+ break;
+
+ case 'w':
+ size = SW;
+ break;
+
+ case 'l':
+ size = SL;
+ break;
+ }
+ }
+ if (OP_KIND (instruction->opcode->how) == O_MOVAB ||
+ OP_KIND (instruction->opcode->how) == O_MOVAW ||
+ OP_KIND (instruction->opcode->how) == O_MOVAL)
+ {
+ switch (operand[0].mode & MODE)
+ {
+ case INDEXB:
+ default:
+ fix_operand_size (&operand[1], 1);
+ break;
+ case INDEXW:
+ fix_operand_size (&operand[1], 2);
+ break;
+ case INDEXL:
+ fix_operand_size (&operand[1], 4);
+ break;
+ }
+ }
+ else
+ {
+ for (i = 0; i < 3 && operand[i].mode != 0; i++)
+ switch (size)
+ {
+ case SN:
+ case SB:
+ default:
+ fix_operand_size (&operand[i], 1);
+ break;
+ case SW:
+ fix_operand_size (&operand[i], 2);
+ break;
+ case SL:
+ fix_operand_size (&operand[i], 4);
+ break;
+ }
+ }
+
+ instruction = get_specific (instruction, operand, size);
+
+ if (instruction == 0)
+ {
+ /* Couldn't find an opcode which matched the operands. */
+ char *where = frag_more (2);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ clever_message (prev_instruction, operand);
+
+ return;
+ }
+
+ build_bytes (instruction, operand);
+
+ dwarf2_emit_insn (instruction->length);
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Various routines to kill one day. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+#define OPTION_H_TICK_HEX (OPTION_MD_BASE)
+
+const char *md_shortopts = "";
+struct option md_longopts[] = {
+ { "h-tick-hex", no_argument, NULL, OPTION_H_TICK_HEX },
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_H_TICK_HEX:
+ enable_h_tick_hex = 1;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+}
+
+void tc_aout_fix_to_chars (void);
+
+void
+tc_aout_fix_to_chars (void)
+{
+ printf (_("call to tc_aout_fix_to_chars \n"));
+ abort ();
+}
+
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ printf (_("call to md_convert_frag \n"));
+ abort ();
+}
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ long val = *valP;
+
+ switch (fixP->fx_size)
+ {
+ case 1:
+ *buf++ = val;
+ break;
+ case 2:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case 4:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ break;
+ case 8:
+ /* This can arise when the .quad or .8byte pseudo-ops are used.
+ Returning here (without setting fx_done) will cause the code
+ to attempt to generate a reloc which will then fail with the
+ slightly more helpful error message: "Cannot represent
+ relocation type BFD_RELOC_64". */
+ return;
+ default:
+ abort ();
+ }
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segment_type ATTRIBUTE_UNUSED)
+{
+ printf (_("call to md_estimate_size_before_relax \n"));
+ abort ();
+}
+
+/* Put number into target byte order. */
+void
+md_number_to_chars (char *ptr, valueT use, int nbytes)
+{
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+
+long
+md_pcrel_from (fixS *fixp)
+{
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Unexpected reference to a symbol in a non-code section"));
+ return 0;
+}
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type r_type;
+
+ if (fixp->fx_addsy && fixp->fx_subsy)
+ {
+ if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
+ || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Difference of symbols in different sections is not supported"));
+ return NULL;
+ }
+ }
+
+ rel = xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ rel->addend = fixp->fx_offset;
+
+ r_type = fixp->fx_r_type;
+
+#define DEBUG 0
+#if DEBUG
+ fprintf (stderr, "%s\n", bfd_get_reloc_code_name (r_type));
+ fflush (stderr);
+#endif
+ rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (r_type));
+ return NULL;
+ }
+
+ return rel;
+}
diff --git a/gas/config/tc-h8300.h b/gas/config/tc-h8300.h
new file mode 100644
index 0000000..35dfea8
--- /dev/null
+++ b/gas/config/tc-h8300.h
@@ -0,0 +1,90 @@
+/* This file is tc-h8300.h
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_H8300
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define TARGET_ARCH bfd_arch_h8300
+
+/* Fixup debug sections since we will never relax them. */
+#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_ALLOC)
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-h8300"
+#define LOCAL_LABEL_PREFIX '.'
+#define LOCAL_LABEL(NAME) (NAME[0] == '.' && NAME[1] == 'L')
+#define FAKE_LABEL_NAME ".L0\001"
+#endif
+
+struct fix;
+struct internal_reloc;
+
+#define WORKING_DOT_WORD
+
+#define COFF_MAGIC ( Smode && Nmode ? 0x8304 : Hmode && Nmode ? 0x8303 : Smode ? 0x8302 : Hmode ? 0x8301 : 0x8300)
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define tc_coff_symbol_emit_hook(a) ; /* not used */
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* Minimum instruction is of 16 bits. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+#define DWARF2_USE_FIXED_ADVANCE_PC 0
+
+#ifdef OBJ_ELF
+/* Provide mappings from the original H8 COFF relocation names to
+ their corresponding BFD relocation names. This allows us to use
+ most of tc-h8300.c without modifications for both ELF and COFF
+ ports. */
+#define R_MOV24B1 BFD_RELOC_H8_DIR24A8
+#define R_MOVL1 BFD_RELOC_H8_DIR32A16
+#define R_RELLONG BFD_RELOC_32
+#define R_MOV16B1 BFD_RELOC_H8_DIR16A8
+#define R_RELWORD BFD_RELOC_16
+#define R_RELBYTE BFD_RELOC_8
+#define R_PCRWORD BFD_RELOC_16_PCREL
+#define R_PCRBYTE BFD_RELOC_8_PCREL
+#define R_JMPL1 BFD_RELOC_H8_DIR24R8
+#define R_MEM_INDIRECT BFD_RELOC_8
+
+/* We do not want to adjust any relocations to make implementation of
+ linker relaxations easier. */
+#define tc_fix_adjustable(FIX) 0
+#endif
+
+#define LISTING_HEADER "Renesas H8/300 GAS "
+#ifndef OBJ_ELF
+#define RELOC_32 1234
+#endif
+
+extern int Hmode;
+extern int Smode;
+extern int Nmode;
+extern int SXmode;
+
+#define md_operand(x)
+
+/* This target is buggy, and sets fix size too large. */
+#define TC_FX_SIZE_SLACK(FIX) 1
+
+#define H_TICK_HEX 1
diff --git a/gas/config/tc-hppa.c b/gas/config/tc-hppa.c
new file mode 100644
index 0000000..5ee7f72
--- /dev/null
+++ b/gas/config/tc-hppa.c
@@ -0,0 +1,8798 @@
+/* tc-hppa.c -- Assemble for the PA
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* HP PA-RISC support was contributed by the Center for Software Science
+ at the University of Utah. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "dw2gencfi.h"
+
+#include "bfd/libhppa.h"
+
+/* Be careful, this file includes data *declarations*. */
+#include "opcode/hppa.h"
+
+#if defined (OBJ_ELF) && defined (OBJ_SOM)
+error only one of OBJ_ELF and OBJ_SOM can be defined
+#endif
+
+/* If we are using ELF, then we probably can support dwarf2 debug
+ records. Furthermore, if we are supporting dwarf2 debug records,
+ then we want to use the assembler support for compact line numbers. */
+#ifdef OBJ_ELF
+#include "dwarf2dbg.h"
+
+/* A "convenient" place to put object file dependencies which do
+ not need to be seen outside of tc-hppa.c. */
+
+/* Object file formats specify relocation types. */
+typedef enum elf_hppa_reloc_type reloc_type;
+
+/* Object file formats specify BFD symbol types. */
+typedef elf_symbol_type obj_symbol_type;
+#define symbol_arg_reloc_info(sym)\
+ (((obj_symbol_type *) symbol_get_bfdsym (sym))->tc_data.hppa_arg_reloc)
+
+#if TARGET_ARCH_SIZE == 64
+/* How to generate a relocation. */
+#define hppa_gen_reloc_type _bfd_elf64_hppa_gen_reloc_type
+#define elf_hppa_reloc_final_type elf64_hppa_reloc_final_type
+#else
+#define hppa_gen_reloc_type _bfd_elf32_hppa_gen_reloc_type
+#define elf_hppa_reloc_final_type elf32_hppa_reloc_final_type
+#endif
+
+/* ELF objects can have versions, but apparently do not have anywhere
+ to store a copyright string. */
+#define obj_version obj_elf_version
+#define obj_copyright obj_elf_version
+
+#define UNWIND_SECTION_NAME ".PARISC.unwind"
+#endif /* OBJ_ELF */
+
+#ifdef OBJ_SOM
+/* Names of various debugging spaces/subspaces. */
+#define GDB_DEBUG_SPACE_NAME "$GDB_DEBUG$"
+#define GDB_STRINGS_SUBSPACE_NAME "$GDB_STRINGS$"
+#define GDB_SYMBOLS_SUBSPACE_NAME "$GDB_SYMBOLS$"
+#define UNWIND_SECTION_NAME "$UNWIND$"
+
+/* Object file formats specify relocation types. */
+typedef int reloc_type;
+
+/* SOM objects can have both a version string and a copyright string. */
+#define obj_version obj_som_version
+#define obj_copyright obj_som_copyright
+
+/* How to generate a relocation. */
+#define hppa_gen_reloc_type hppa_som_gen_reloc_type
+
+/* Object file formats specify BFD symbol types. */
+typedef som_symbol_type obj_symbol_type;
+#define symbol_arg_reloc_info(sym)\
+ (((obj_symbol_type *) symbol_get_bfdsym (sym))->tc_data.ap.hppa_arg_reloc)
+
+/* This apparently isn't in older versions of hpux reloc.h. */
+#ifndef R_DLT_REL
+#define R_DLT_REL 0x78
+#endif
+
+#ifndef R_N0SEL
+#define R_N0SEL 0xd8
+#endif
+
+#ifndef R_N1SEL
+#define R_N1SEL 0xd9
+#endif
+#endif /* OBJ_SOM */
+
+#if TARGET_ARCH_SIZE == 64
+#define DEFAULT_LEVEL 25
+#else
+#define DEFAULT_LEVEL 10
+#endif
+
+/* Various structures and types used internally in tc-hppa.c. */
+
+/* Unwind table and descriptor. FIXME: Sync this with GDB version. */
+
+struct unwind_desc
+ {
+ unsigned int cannot_unwind:1;
+ unsigned int millicode:1;
+ unsigned int millicode_save_rest:1;
+ unsigned int region_desc:2;
+ unsigned int save_sr:2;
+ unsigned int entry_fr:4;
+ unsigned int entry_gr:5;
+ unsigned int args_stored:1;
+ unsigned int call_fr:5;
+ unsigned int call_gr:5;
+ unsigned int save_sp:1;
+ unsigned int save_rp:1;
+ unsigned int save_rp_in_frame:1;
+ unsigned int extn_ptr_defined:1;
+ unsigned int cleanup_defined:1;
+
+ unsigned int hpe_interrupt_marker:1;
+ unsigned int hpux_interrupt_marker:1;
+ unsigned int reserved:3;
+ unsigned int frame_size:27;
+ };
+
+/* We can't rely on compilers placing bitfields in any particular
+ place, so use these macros when dumping unwind descriptors to
+ object files. */
+#define UNWIND_LOW32(U) \
+ (((U)->cannot_unwind << 31) \
+ | ((U)->millicode << 30) \
+ | ((U)->millicode_save_rest << 29) \
+ | ((U)->region_desc << 27) \
+ | ((U)->save_sr << 25) \
+ | ((U)->entry_fr << 21) \
+ | ((U)->entry_gr << 16) \
+ | ((U)->args_stored << 15) \
+ | ((U)->call_fr << 10) \
+ | ((U)->call_gr << 5) \
+ | ((U)->save_sp << 4) \
+ | ((U)->save_rp << 3) \
+ | ((U)->save_rp_in_frame << 2) \
+ | ((U)->extn_ptr_defined << 1) \
+ | ((U)->cleanup_defined << 0))
+
+#define UNWIND_HIGH32(U) \
+ (((U)->hpe_interrupt_marker << 31) \
+ | ((U)->hpux_interrupt_marker << 30) \
+ | ((U)->frame_size << 0))
+
+struct unwind_table
+ {
+ /* Starting and ending offsets of the region described by
+ descriptor. */
+ unsigned int start_offset;
+ unsigned int end_offset;
+ struct unwind_desc descriptor;
+ };
+
+/* This structure is used by the .callinfo, .enter, .leave pseudo-ops to
+ control the entry and exit code they generate. It is also used in
+ creation of the correct stack unwind descriptors.
+
+ NOTE: GAS does not support .enter and .leave for the generation of
+ prologues and epilogues. FIXME.
+
+ The fields in structure roughly correspond to the arguments available on the
+ .callinfo pseudo-op. */
+
+struct call_info
+ {
+ /* The unwind descriptor being built. */
+ struct unwind_table ci_unwind;
+
+ /* Name of this function. */
+ symbolS *start_symbol;
+
+ /* (temporary) symbol used to mark the end of this function. */
+ symbolS *end_symbol;
+
+ /* Next entry in the chain. */
+ struct call_info *ci_next;
+ };
+
+/* Operand formats for FP instructions. Note not all FP instructions
+ allow all four formats to be used (for example fmpysub only allows
+ SGL and DBL). */
+typedef enum
+ {
+ SGL, DBL, ILLEGAL_FMT, QUAD, W, UW, DW, UDW, QW, UQW
+ }
+fp_operand_format;
+
+/* This fully describes the symbol types which may be attached to
+ an EXPORT or IMPORT directive. Only SOM uses this formation
+ (ELF has no need for it). */
+typedef enum
+ {
+ SYMBOL_TYPE_UNKNOWN,
+ SYMBOL_TYPE_ABSOLUTE,
+ SYMBOL_TYPE_CODE,
+ SYMBOL_TYPE_DATA,
+ SYMBOL_TYPE_ENTRY,
+ SYMBOL_TYPE_MILLICODE,
+ SYMBOL_TYPE_PLABEL,
+ SYMBOL_TYPE_PRI_PROG,
+ SYMBOL_TYPE_SEC_PROG,
+ }
+pa_symbol_type;
+
+/* This structure contains information needed to assemble
+ individual instructions. */
+struct pa_it
+ {
+ /* Holds the opcode after parsing by pa_ip. */
+ unsigned long opcode;
+
+ /* Holds an expression associated with the current instruction. */
+ expressionS exp;
+
+ /* Does this instruction use PC-relative addressing. */
+ int pcrel;
+
+ /* Floating point formats for operand1 and operand2. */
+ fp_operand_format fpof1;
+ fp_operand_format fpof2;
+
+ /* Whether or not we saw a truncation request on an fcnv insn. */
+ int trunc;
+
+ /* Holds the field selector for this instruction
+ (for example L%, LR%, etc). */
+ long field_selector;
+
+ /* Holds any argument relocation bits associated with this
+ instruction. (instruction should be some sort of call). */
+ unsigned int arg_reloc;
+
+ /* The format specification for this instruction. */
+ int format;
+
+ /* The relocation (if any) associated with this instruction. */
+ reloc_type reloc;
+ };
+
+/* PA-89 floating point registers are arranged like this:
+
+ +--------------+--------------+
+ | 0 or 16L | 16 or 16R |
+ +--------------+--------------+
+ | 1 or 17L | 17 or 17R |
+ +--------------+--------------+
+ | | |
+
+ . . .
+ . . .
+ . . .
+
+ | | |
+ +--------------+--------------+
+ | 14 or 30L | 30 or 30R |
+ +--------------+--------------+
+ | 15 or 31L | 31 or 31R |
+ +--------------+--------------+ */
+
+/* Additional information needed to build argument relocation stubs. */
+struct call_desc
+ {
+ /* The argument relocation specification. */
+ unsigned int arg_reloc;
+
+ /* Number of arguments. */
+ unsigned int arg_count;
+ };
+
+#ifdef OBJ_SOM
+/* This structure defines an entry in the subspace dictionary
+ chain. */
+
+struct subspace_dictionary_chain
+ {
+ /* Nonzero if this space has been defined by the user code. */
+ unsigned int ssd_defined;
+
+ /* Name of this subspace. */
+ char *ssd_name;
+
+ /* GAS segment and subsegment associated with this subspace. */
+ asection *ssd_seg;
+ int ssd_subseg;
+
+ /* Next space in the subspace dictionary chain. */
+ struct subspace_dictionary_chain *ssd_next;
+ };
+
+typedef struct subspace_dictionary_chain ssd_chain_struct;
+
+/* This structure defines an entry in the subspace dictionary
+ chain. */
+
+struct space_dictionary_chain
+ {
+ /* Nonzero if this space has been defined by the user code or
+ as a default space. */
+ unsigned int sd_defined;
+
+ /* Nonzero if this spaces has been defined by the user code. */
+ unsigned int sd_user_defined;
+
+ /* The space number (or index). */
+ unsigned int sd_spnum;
+
+ /* The name of this subspace. */
+ char *sd_name;
+
+ /* GAS segment to which this subspace corresponds. */
+ asection *sd_seg;
+
+ /* Current subsegment number being used. */
+ int sd_last_subseg;
+
+ /* The chain of subspaces contained within this space. */
+ ssd_chain_struct *sd_subspaces;
+
+ /* The next entry in the space dictionary chain. */
+ struct space_dictionary_chain *sd_next;
+ };
+
+typedef struct space_dictionary_chain sd_chain_struct;
+
+/* This structure defines attributes of the default subspace
+ dictionary entries. */
+
+struct default_subspace_dict
+ {
+ /* Name of the subspace. */
+ char *name;
+
+ /* FIXME. Is this still needed? */
+ char defined;
+
+ /* Nonzero if this subspace is loadable. */
+ char loadable;
+
+ /* Nonzero if this subspace contains only code. */
+ char code_only;
+
+ /* Nonzero if this is a comdat subspace. */
+ char comdat;
+
+ /* Nonzero if this is a common subspace. */
+ char common;
+
+ /* Nonzero if this is a common subspace which allows symbols
+ to be multiply defined. */
+ char dup_common;
+
+ /* Nonzero if this subspace should be zero filled. */
+ char zero;
+
+ /* Sort key for this subspace. */
+ unsigned char sort;
+
+ /* Access control bits for this subspace. Can represent RWX access
+ as well as privilege level changes for gateways. */
+ int access;
+
+ /* Index of containing space. */
+ int space_index;
+
+ /* Alignment (in bytes) of this subspace. */
+ int alignment;
+
+ /* Quadrant within space where this subspace should be loaded. */
+ int quadrant;
+
+ /* An index into the default spaces array. */
+ int def_space_index;
+
+ /* Subsegment associated with this subspace. */
+ subsegT subsegment;
+ };
+
+/* This structure defines attributes of the default space
+ dictionary entries. */
+
+struct default_space_dict
+ {
+ /* Name of the space. */
+ char *name;
+
+ /* Space number. It is possible to identify spaces within
+ assembly code numerically! */
+ int spnum;
+
+ /* Nonzero if this space is loadable. */
+ char loadable;
+
+ /* Nonzero if this space is "defined". FIXME is still needed */
+ char defined;
+
+ /* Nonzero if this space can not be shared. */
+ char private;
+
+ /* Sort key for this space. */
+ unsigned char sort;
+
+ /* Segment associated with this space. */
+ asection *segment;
+ };
+#endif
+
+/* Structure for previous label tracking. Needed so that alignments,
+ callinfo declarations, etc can be easily attached to a particular
+ label. */
+typedef struct label_symbol_struct
+ {
+ struct symbol *lss_label;
+#ifdef OBJ_SOM
+ sd_chain_struct *lss_space;
+#endif
+#ifdef OBJ_ELF
+ segT lss_segment;
+#endif
+ struct label_symbol_struct *lss_next;
+ }
+label_symbol_struct;
+
+/* Extra information needed to perform fixups (relocations) on the PA. */
+struct hppa_fix_struct
+ {
+ /* The field selector. */
+ enum hppa_reloc_field_selector_type_alt fx_r_field;
+
+ /* Type of fixup. */
+ int fx_r_type;
+
+ /* Format of fixup. */
+ int fx_r_format;
+
+ /* Argument relocation bits. */
+ unsigned int fx_arg_reloc;
+
+ /* The segment this fixup appears in. */
+ segT segment;
+ };
+
+/* Structure to hold information about predefined registers. */
+
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* This structure defines the mapping from a FP condition string
+ to a condition number which can be recorded in an instruction. */
+struct fp_cond_map
+ {
+ char *string;
+ int cond;
+ };
+
+/* This structure defines a mapping from a field selector
+ string to a field selector type. */
+struct selector_entry
+ {
+ char *prefix;
+ int field_selector;
+ };
+
+/* Prototypes for functions local to tc-hppa.c. */
+
+#ifdef OBJ_SOM
+static void pa_check_current_space_and_subspace (void);
+#endif
+
+#if !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD)))
+static void pa_text (int);
+static void pa_data (int);
+static void pa_comm (int);
+#endif
+#ifdef OBJ_SOM
+static int exact_log2 (int);
+static void pa_compiler (int);
+static void pa_align (int);
+static void pa_space (int);
+static void pa_spnum (int);
+static void pa_subspace (int);
+static sd_chain_struct *create_new_space (char *, int, int,
+ int, int, int,
+ asection *, int);
+static ssd_chain_struct *create_new_subspace (sd_chain_struct *,
+ char *, int, int,
+ int, int, int, int,
+ int, int, int, int,
+ int, asection *);
+static ssd_chain_struct *update_subspace (sd_chain_struct *,
+ char *, int, int, int,
+ int, int, int, int,
+ int, int, int, int,
+ asection *);
+static sd_chain_struct *is_defined_space (char *);
+static ssd_chain_struct *is_defined_subspace (char *);
+static sd_chain_struct *pa_segment_to_space (asection *);
+static ssd_chain_struct *pa_subsegment_to_subspace (asection *,
+ subsegT);
+static sd_chain_struct *pa_find_space_by_number (int);
+static unsigned int pa_subspace_start (sd_chain_struct *, int);
+static sd_chain_struct *pa_parse_space_stmt (char *, int);
+#endif
+
+/* File and globally scoped variable declarations. */
+
+#ifdef OBJ_SOM
+/* Root and final entry in the space chain. */
+static sd_chain_struct *space_dict_root;
+static sd_chain_struct *space_dict_last;
+
+/* The current space and subspace. */
+static sd_chain_struct *current_space;
+static ssd_chain_struct *current_subspace;
+#endif
+
+/* Root of the call_info chain. */
+static struct call_info *call_info_root;
+
+/* The last call_info (for functions) structure
+ seen so it can be associated with fixups and
+ function labels. */
+static struct call_info *last_call_info;
+
+/* The last call description (for actual calls). */
+static struct call_desc last_call_desc;
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* These characters can be suffixes of opcode names and they may be
+ followed by meaningful whitespace. We don't include `,' and `!'
+ as they never appear followed by meaningful whitespace. */
+const char hppa_symbol_chars[] = "*?=<>";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output.
+
+ Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output.
+
+ Also note that C style comments will always work. */
+const char line_comment_chars[] = "#";
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = ";";
+
+/* This array holds the characters which act as line separators. */
+const char line_separator_chars[] = "!";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456 or 0d1.2345e12.
+
+ Be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it
+ at all, but nothing is ideal around here. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+static struct pa_it the_insn;
+
+/* Points to the end of an expression just parsed by get_expression
+ and friends. FIXME. This shouldn't be handled with a file-global
+ variable. */
+static char *expr_end;
+
+/* Nonzero if a .callinfo appeared within the current procedure. */
+static int callinfo_found;
+
+/* Nonzero if the assembler is currently within a .entry/.exit pair. */
+static int within_entry_exit;
+
+/* Nonzero if the assembler is currently within a procedure definition. */
+static int within_procedure;
+
+/* Handle on structure which keep track of the last symbol
+ seen in each subspace. */
+static label_symbol_struct *label_symbols_rootp = NULL;
+
+/* Nonzero when strict matching is enabled. Zero otherwise.
+
+ Each opcode in the table has a flag which indicates whether or
+ not strict matching should be enabled for that instruction.
+
+ Mainly, strict causes errors to be ignored when a match failure
+ occurs. However, it also affects the parsing of register fields
+ by pa_parse_number. */
+static int strict;
+
+/* pa_parse_number returns values in `pa_number'. Mostly
+ pa_parse_number is used to return a register number, with floating
+ point registers being numbered from FP_REG_BASE upwards.
+ The bit specified with FP_REG_RSEL is set if the floating point
+ register has a `r' suffix. */
+#define FP_REG_BASE 64
+#define FP_REG_RSEL 128
+static int pa_number;
+
+#ifdef OBJ_SOM
+/* A dummy bfd symbol so that all relocations have symbols of some kind. */
+static symbolS *dummy_symbol;
+#endif
+
+/* Nonzero if errors are to be printed. */
+static int print_errors = 1;
+
+/* List of registers that are pre-defined:
+
+ Each general register has one predefined name of the form
+ %r<REGNUM> which has the value <REGNUM>.
+
+ Space and control registers are handled in a similar manner,
+ but use %sr<REGNUM> and %cr<REGNUM> as their predefined names.
+
+ Likewise for the floating point registers, but of the form
+ %fr<REGNUM>. Floating point registers have additional predefined
+ names with 'L' and 'R' suffixes (e.g. %fr19L, %fr19R) which
+ again have the value <REGNUM>.
+
+ Many registers also have synonyms:
+
+ %r26 - %r23 have %arg0 - %arg3 as synonyms
+ %r28 - %r29 have %ret0 - %ret1 as synonyms
+ %fr4 - %fr7 have %farg0 - %farg3 as synonyms
+ %r30 has %sp as a synonym
+ %r27 has %dp as a synonym
+ %r2 has %rp as a synonym
+
+ Almost every control register has a synonym; they are not listed
+ here for brevity.
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ {"%arg0", 26},
+ {"%arg1", 25},
+ {"%arg2", 24},
+ {"%arg3", 23},
+ {"%cr0", 0},
+ {"%cr10", 10},
+ {"%cr11", 11},
+ {"%cr12", 12},
+ {"%cr13", 13},
+ {"%cr14", 14},
+ {"%cr15", 15},
+ {"%cr16", 16},
+ {"%cr17", 17},
+ {"%cr18", 18},
+ {"%cr19", 19},
+ {"%cr20", 20},
+ {"%cr21", 21},
+ {"%cr22", 22},
+ {"%cr23", 23},
+ {"%cr24", 24},
+ {"%cr25", 25},
+ {"%cr26", 26},
+ {"%cr27", 27},
+ {"%cr28", 28},
+ {"%cr29", 29},
+ {"%cr30", 30},
+ {"%cr31", 31},
+ {"%cr8", 8},
+ {"%cr9", 9},
+ {"%dp", 27},
+ {"%eiem", 15},
+ {"%eirr", 23},
+ {"%farg0", 4 + FP_REG_BASE},
+ {"%farg1", 5 + FP_REG_BASE},
+ {"%farg2", 6 + FP_REG_BASE},
+ {"%farg3", 7 + FP_REG_BASE},
+ {"%fr0", 0 + FP_REG_BASE},
+ {"%fr0l", 0 + FP_REG_BASE},
+ {"%fr0r", 0 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr1", 1 + FP_REG_BASE},
+ {"%fr10", 10 + FP_REG_BASE},
+ {"%fr10l", 10 + FP_REG_BASE},
+ {"%fr10r", 10 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr11", 11 + FP_REG_BASE},
+ {"%fr11l", 11 + FP_REG_BASE},
+ {"%fr11r", 11 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr12", 12 + FP_REG_BASE},
+ {"%fr12l", 12 + FP_REG_BASE},
+ {"%fr12r", 12 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr13", 13 + FP_REG_BASE},
+ {"%fr13l", 13 + FP_REG_BASE},
+ {"%fr13r", 13 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr14", 14 + FP_REG_BASE},
+ {"%fr14l", 14 + FP_REG_BASE},
+ {"%fr14r", 14 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr15", 15 + FP_REG_BASE},
+ {"%fr15l", 15 + FP_REG_BASE},
+ {"%fr15r", 15 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr16", 16 + FP_REG_BASE},
+ {"%fr16l", 16 + FP_REG_BASE},
+ {"%fr16r", 16 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr17", 17 + FP_REG_BASE},
+ {"%fr17l", 17 + FP_REG_BASE},
+ {"%fr17r", 17 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr18", 18 + FP_REG_BASE},
+ {"%fr18l", 18 + FP_REG_BASE},
+ {"%fr18r", 18 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr19", 19 + FP_REG_BASE},
+ {"%fr19l", 19 + FP_REG_BASE},
+ {"%fr19r", 19 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr1l", 1 + FP_REG_BASE},
+ {"%fr1r", 1 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr2", 2 + FP_REG_BASE},
+ {"%fr20", 20 + FP_REG_BASE},
+ {"%fr20l", 20 + FP_REG_BASE},
+ {"%fr20r", 20 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr21", 21 + FP_REG_BASE},
+ {"%fr21l", 21 + FP_REG_BASE},
+ {"%fr21r", 21 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr22", 22 + FP_REG_BASE},
+ {"%fr22l", 22 + FP_REG_BASE},
+ {"%fr22r", 22 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr23", 23 + FP_REG_BASE},
+ {"%fr23l", 23 + FP_REG_BASE},
+ {"%fr23r", 23 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr24", 24 + FP_REG_BASE},
+ {"%fr24l", 24 + FP_REG_BASE},
+ {"%fr24r", 24 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr25", 25 + FP_REG_BASE},
+ {"%fr25l", 25 + FP_REG_BASE},
+ {"%fr25r", 25 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr26", 26 + FP_REG_BASE},
+ {"%fr26l", 26 + FP_REG_BASE},
+ {"%fr26r", 26 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr27", 27 + FP_REG_BASE},
+ {"%fr27l", 27 + FP_REG_BASE},
+ {"%fr27r", 27 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr28", 28 + FP_REG_BASE},
+ {"%fr28l", 28 + FP_REG_BASE},
+ {"%fr28r", 28 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr29", 29 + FP_REG_BASE},
+ {"%fr29l", 29 + FP_REG_BASE},
+ {"%fr29r", 29 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr2l", 2 + FP_REG_BASE},
+ {"%fr2r", 2 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr3", 3 + FP_REG_BASE},
+ {"%fr30", 30 + FP_REG_BASE},
+ {"%fr30l", 30 + FP_REG_BASE},
+ {"%fr30r", 30 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr31", 31 + FP_REG_BASE},
+ {"%fr31l", 31 + FP_REG_BASE},
+ {"%fr31r", 31 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr3l", 3 + FP_REG_BASE},
+ {"%fr3r", 3 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr4", 4 + FP_REG_BASE},
+ {"%fr4l", 4 + FP_REG_BASE},
+ {"%fr4r", 4 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr5", 5 + FP_REG_BASE},
+ {"%fr5l", 5 + FP_REG_BASE},
+ {"%fr5r", 5 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr6", 6 + FP_REG_BASE},
+ {"%fr6l", 6 + FP_REG_BASE},
+ {"%fr6r", 6 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr7", 7 + FP_REG_BASE},
+ {"%fr7l", 7 + FP_REG_BASE},
+ {"%fr7r", 7 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr8", 8 + FP_REG_BASE},
+ {"%fr8l", 8 + FP_REG_BASE},
+ {"%fr8r", 8 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fr9", 9 + FP_REG_BASE},
+ {"%fr9l", 9 + FP_REG_BASE},
+ {"%fr9r", 9 + FP_REG_BASE + FP_REG_RSEL},
+ {"%fret", 4},
+ {"%hta", 25},
+ {"%iir", 19},
+ {"%ior", 21},
+ {"%ipsw", 22},
+ {"%isr", 20},
+ {"%itmr", 16},
+ {"%iva", 14},
+#if TARGET_ARCH_SIZE == 64
+ {"%mrp", 2},
+#else
+ {"%mrp", 31},
+#endif
+ {"%pcoq", 18},
+ {"%pcsq", 17},
+ {"%pidr1", 8},
+ {"%pidr2", 9},
+ {"%pidr3", 12},
+ {"%pidr4", 13},
+ {"%ppda", 24},
+ {"%r0", 0},
+ {"%r1", 1},
+ {"%r10", 10},
+ {"%r11", 11},
+ {"%r12", 12},
+ {"%r13", 13},
+ {"%r14", 14},
+ {"%r15", 15},
+ {"%r16", 16},
+ {"%r17", 17},
+ {"%r18", 18},
+ {"%r19", 19},
+ {"%r2", 2},
+ {"%r20", 20},
+ {"%r21", 21},
+ {"%r22", 22},
+ {"%r23", 23},
+ {"%r24", 24},
+ {"%r25", 25},
+ {"%r26", 26},
+ {"%r27", 27},
+ {"%r28", 28},
+ {"%r29", 29},
+ {"%r3", 3},
+ {"%r30", 30},
+ {"%r31", 31},
+ {"%r4", 4},
+ {"%r5", 5},
+ {"%r6", 6},
+ {"%r7", 7},
+ {"%r8", 8},
+ {"%r9", 9},
+ {"%rctr", 0},
+ {"%ret0", 28},
+ {"%ret1", 29},
+ {"%rp", 2},
+ {"%sar", 11},
+ {"%sp", 30},
+ {"%sr0", 0},
+ {"%sr1", 1},
+ {"%sr2", 2},
+ {"%sr3", 3},
+ {"%sr4", 4},
+ {"%sr5", 5},
+ {"%sr6", 6},
+ {"%sr7", 7},
+ {"%t1", 22},
+ {"%t2", 21},
+ {"%t3", 20},
+ {"%t4", 19},
+ {"%tf1", 11},
+ {"%tf2", 10},
+ {"%tf3", 9},
+ {"%tf4", 8},
+ {"%tr0", 24},
+ {"%tr1", 25},
+ {"%tr2", 26},
+ {"%tr3", 27},
+ {"%tr4", 28},
+ {"%tr5", 29},
+ {"%tr6", 30},
+ {"%tr7", 31}
+};
+
+/* This table is sorted by order of the length of the string. This is
+ so we check for <> before we check for <. If we had a <> and checked
+ for < first, we would get a false match. */
+static const struct fp_cond_map fp_cond_map[] =
+{
+ {"false?", 0},
+ {"false", 1},
+ {"true?", 30},
+ {"true", 31},
+ {"!<=>", 3},
+ {"!?>=", 8},
+ {"!?<=", 16},
+ {"!<>", 7},
+ {"!>=", 11},
+ {"!?>", 12},
+ {"?<=", 14},
+ {"!<=", 19},
+ {"!?<", 20},
+ {"?>=", 22},
+ {"!?=", 24},
+ {"!=t", 27},
+ {"<=>", 29},
+ {"=t", 5},
+ {"?=", 6},
+ {"?<", 10},
+ {"<=", 13},
+ {"!>", 15},
+ {"?>", 18},
+ {">=", 21},
+ {"!<", 23},
+ {"<>", 25},
+ {"!=", 26},
+ {"!?", 28},
+ {"?", 2},
+ {"=", 4},
+ {"<", 9},
+ {">", 17}
+};
+
+static const struct selector_entry selector_table[] =
+{
+ {"f", e_fsel},
+ {"l", e_lsel},
+ {"ld", e_ldsel},
+ {"lp", e_lpsel},
+ {"lr", e_lrsel},
+ {"ls", e_lssel},
+ {"lt", e_ltsel},
+ {"ltp", e_ltpsel},
+ {"n", e_nsel},
+ {"nl", e_nlsel},
+ {"nlr", e_nlrsel},
+ {"p", e_psel},
+ {"r", e_rsel},
+ {"rd", e_rdsel},
+ {"rp", e_rpsel},
+ {"rr", e_rrsel},
+ {"rs", e_rssel},
+ {"rt", e_rtsel},
+ {"rtp", e_rtpsel},
+ {"t", e_tsel},
+};
+
+#ifdef OBJ_SOM
+/* default space and subspace dictionaries */
+
+#define GDB_SYMBOLS GDB_SYMBOLS_SUBSPACE_NAME
+#define GDB_STRINGS GDB_STRINGS_SUBSPACE_NAME
+
+/* pre-defined subsegments (subspaces) for the HPPA. */
+#define SUBSEG_CODE 0
+#define SUBSEG_LIT 1
+#define SUBSEG_MILLI 2
+#define SUBSEG_DATA 0
+#define SUBSEG_BSS 2
+#define SUBSEG_UNWIND 3
+#define SUBSEG_GDB_STRINGS 0
+#define SUBSEG_GDB_SYMBOLS 1
+
+static struct default_subspace_dict pa_def_subspaces[] =
+{
+ {"$CODE$", 1, 1, 1, 0, 0, 0, 0, 24, 0x2c, 0, 8, 0, 0, SUBSEG_CODE},
+ {"$DATA$", 1, 1, 0, 0, 0, 0, 0, 24, 0x1f, 1, 8, 1, 1, SUBSEG_DATA},
+ {"$LIT$", 1, 1, 0, 0, 0, 0, 0, 16, 0x2c, 0, 8, 0, 0, SUBSEG_LIT},
+ {"$MILLICODE$", 1, 1, 0, 0, 0, 0, 0, 8, 0x2c, 0, 8, 0, 0, SUBSEG_MILLI},
+ {"$BSS$", 1, 1, 0, 0, 0, 0, 1, 80, 0x1f, 1, 8, 1, 1, SUBSEG_BSS},
+ {NULL, 0, 1, 0, 0, 0, 0, 0, 255, 0x1f, 0, 4, 0, 0, 0}
+};
+
+static struct default_space_dict pa_def_spaces[] =
+{
+ {"$TEXT$", 0, 1, 1, 0, 8, ASEC_NULL},
+ {"$PRIVATE$", 1, 1, 1, 1, 16, ASEC_NULL},
+ {NULL, 0, 0, 0, 0, 0, ASEC_NULL}
+};
+
+/* Misc local definitions used by the assembler. */
+
+/* These macros are used to maintain spaces/subspaces. */
+#define SPACE_DEFINED(space_chain) (space_chain)->sd_defined
+#define SPACE_USER_DEFINED(space_chain) (space_chain)->sd_user_defined
+#define SPACE_SPNUM(space_chain) (space_chain)->sd_spnum
+#define SPACE_NAME(space_chain) (space_chain)->sd_name
+
+#define SUBSPACE_DEFINED(ss_chain) (ss_chain)->ssd_defined
+#define SUBSPACE_NAME(ss_chain) (ss_chain)->ssd_name
+#endif
+
+/* Return nonzero if the string pointed to by S potentially represents
+ a right or left half of a FP register */
+#define IS_R_SELECT(S) (*(S) == 'R' || *(S) == 'r')
+#define IS_L_SELECT(S) (*(S) == 'L' || *(S) == 'l')
+
+/* Store immediate values of shift/deposit/extract functions. */
+
+#define SAVE_IMMEDIATE(VALUE) \
+ { \
+ if (immediate_check) \
+ { \
+ if (pos == -1) \
+ pos = (VALUE); \
+ else if (len == -1) \
+ len = (VALUE); \
+ } \
+ }
+
+/* Insert FIELD into OPCODE starting at bit START. Continue pa_ip
+ main loop after insertion. */
+
+#define INSERT_FIELD_AND_CONTINUE(OPCODE, FIELD, START) \
+ { \
+ ((OPCODE) |= (FIELD) << (START)); \
+ continue; \
+ }
+
+/* Simple range checking for FIELD against HIGH and LOW bounds.
+ IGNORE is used to suppress the error message. */
+
+#define CHECK_FIELD(FIELD, HIGH, LOW, IGNORE) \
+ { \
+ if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \
+ { \
+ if (! IGNORE) \
+ as_bad (_("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \
+ (int) (FIELD));\
+ break; \
+ } \
+ }
+
+/* Variant of CHECK_FIELD for use in md_apply_fix and other places where
+ the current file and line number are not valid. */
+
+#define CHECK_FIELD_WHERE(FIELD, HIGH, LOW, FILENAME, LINE) \
+ { \
+ if ((FIELD) > (HIGH) || (FIELD) < (LOW)) \
+ { \
+ as_bad_where ((FILENAME), (LINE), \
+ _("Field out of range [%d..%d] (%d)."), (LOW), (HIGH), \
+ (int) (FIELD));\
+ break; \
+ } \
+ }
+
+/* Simple alignment checking for FIELD against ALIGN (a power of two).
+ IGNORE is used to suppress the error message. */
+
+#define CHECK_ALIGN(FIELD, ALIGN, IGNORE) \
+ { \
+ if ((FIELD) & ((ALIGN) - 1)) \
+ { \
+ if (! IGNORE) \
+ as_bad (_("Field not properly aligned [%d] (%d)."), (ALIGN), \
+ (int) (FIELD));\
+ break; \
+ } \
+ }
+
+#define is_DP_relative(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$global$") == 0)
+
+#define is_SB_relative(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$segrel$") == 0)
+
+#define is_PC_relative(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$PIC_pcrel$0") == 0)
+
+#define is_tls_gdidx(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_gdidx$") == 0)
+
+#define is_tls_ldidx(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_ldidx$") == 0)
+
+#define is_tls_dtpoff(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_dtpoff$") == 0)
+
+#define is_tls_ieoff(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_ieoff$") == 0)
+
+#define is_tls_leoff(exp) \
+ ((exp).X_op == O_subtract \
+ && strcmp (S_GET_NAME ((exp).X_op_symbol), "$tls_leoff$") == 0)
+
+/* We need some complex handling for stabs (sym1 - sym2). Luckily, we'll
+ always be able to reduce the expression to a constant, so we don't
+ need real complex handling yet. */
+#define is_complex(exp) \
+ ((exp).X_op != O_constant && (exp).X_op != O_symbol)
+
+/* Actual functions to implement the PA specific code for the assembler. */
+
+/* Called before writing the object file. Make sure entry/exit and
+ proc/procend pairs match. */
+
+void
+pa_check_eof (void)
+{
+ if (within_entry_exit)
+ as_fatal (_("Missing .exit\n"));
+
+ if (within_procedure)
+ as_fatal (_("Missing .procend\n"));
+}
+
+/* Returns a pointer to the label_symbol_struct for the current space.
+ or NULL if no label_symbol_struct exists for the current space. */
+
+static label_symbol_struct *
+pa_get_label (void)
+{
+ label_symbol_struct *label_chain;
+
+ for (label_chain = label_symbols_rootp;
+ label_chain;
+ label_chain = label_chain->lss_next)
+ {
+#ifdef OBJ_SOM
+ if (current_space == label_chain->lss_space && label_chain->lss_label)
+ return label_chain;
+#endif
+#ifdef OBJ_ELF
+ if (now_seg == label_chain->lss_segment && label_chain->lss_label)
+ return label_chain;
+#endif
+ }
+
+ return NULL;
+}
+
+/* Defines a label for the current space. If one is already defined,
+ this function will replace it with the new label. */
+
+void
+pa_define_label (symbolS *symbol)
+{
+ label_symbol_struct *label_chain = pa_get_label ();
+
+ if (label_chain)
+ label_chain->lss_label = symbol;
+ else
+ {
+ /* Create a new label entry and add it to the head of the chain. */
+ label_chain = xmalloc (sizeof (label_symbol_struct));
+ label_chain->lss_label = symbol;
+#ifdef OBJ_SOM
+ label_chain->lss_space = current_space;
+#endif
+#ifdef OBJ_ELF
+ label_chain->lss_segment = now_seg;
+#endif
+ label_chain->lss_next = NULL;
+
+ if (label_symbols_rootp)
+ label_chain->lss_next = label_symbols_rootp;
+
+ label_symbols_rootp = label_chain;
+ }
+
+#ifdef OBJ_ELF
+ dwarf2_emit_label (symbol);
+#endif
+}
+
+/* Removes a label definition for the current space.
+ If there is no label_symbol_struct entry, then no action is taken. */
+
+static void
+pa_undefine_label (void)
+{
+ label_symbol_struct *label_chain;
+ label_symbol_struct *prev_label_chain = NULL;
+
+ for (label_chain = label_symbols_rootp;
+ label_chain;
+ label_chain = label_chain->lss_next)
+ {
+ if (1
+#ifdef OBJ_SOM
+ && current_space == label_chain->lss_space && label_chain->lss_label
+#endif
+#ifdef OBJ_ELF
+ && now_seg == label_chain->lss_segment && label_chain->lss_label
+#endif
+ )
+ {
+ /* Remove the label from the chain and free its memory. */
+ if (prev_label_chain)
+ prev_label_chain->lss_next = label_chain->lss_next;
+ else
+ label_symbols_rootp = label_chain->lss_next;
+
+ free (label_chain);
+ break;
+ }
+ prev_label_chain = label_chain;
+ }
+}
+
+/* An HPPA-specific version of fix_new. This is required because the HPPA
+ code needs to keep track of some extra stuff. Each call to fix_new_hppa
+ results in the creation of an instance of an hppa_fix_struct. An
+ hppa_fix_struct stores the extra information along with a pointer to the
+ original fixS. This is attached to the original fixup via the
+ tc_fix_data field. */
+
+static void
+fix_new_hppa (fragS *frag,
+ int where,
+ int size,
+ symbolS *add_symbol,
+ offsetT offset,
+ expressionS *exp,
+ int pcrel,
+ bfd_reloc_code_real_type r_type,
+ enum hppa_reloc_field_selector_type_alt r_field,
+ int r_format,
+ unsigned int arg_reloc,
+ int unwind_bits ATTRIBUTE_UNUSED)
+{
+ fixS *new_fix;
+ struct hppa_fix_struct *hppa_fix = obstack_alloc (&notes, sizeof (struct hppa_fix_struct));
+
+ if (exp != NULL)
+ new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
+ else
+ new_fix = fix_new (frag, where, size, add_symbol, offset, pcrel, r_type);
+ new_fix->tc_fix_data = (void *) hppa_fix;
+ hppa_fix->fx_r_type = r_type;
+ hppa_fix->fx_r_field = r_field;
+ hppa_fix->fx_r_format = r_format;
+ hppa_fix->fx_arg_reloc = arg_reloc;
+ hppa_fix->segment = now_seg;
+#ifdef OBJ_SOM
+ if (r_type == R_ENTRY || r_type == R_EXIT)
+ new_fix->fx_offset = unwind_bits;
+#endif
+
+ /* foo-$global$ is used to access non-automatic storage. $global$
+ is really just a marker and has served its purpose, so eliminate
+ it now so as not to confuse write.c. Ditto for $PIC_pcrel$0. */
+ if (new_fix->fx_subsy
+ && (strcmp (S_GET_NAME (new_fix->fx_subsy), "$global$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$segrel$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$PIC_pcrel$0") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_gdidx$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_ldidx$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_dtpoff$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_ieoff$") == 0
+ || strcmp (S_GET_NAME (new_fix->fx_subsy), "$tls_leoff$") == 0))
+ new_fix->fx_subsy = NULL;
+}
+
+/* This fix_new is called by cons via TC_CONS_FIX_NEW.
+ hppa_field_selector is set by the parse_cons_expression_hppa. */
+
+void
+cons_fix_new_hppa (fragS *frag, int where, int size, expressionS *exp,
+ int hppa_field_selector)
+{
+ unsigned int rel_type;
+
+ /* Get a base relocation type. */
+ if (is_DP_relative (*exp))
+ rel_type = R_HPPA_GOTOFF;
+ else if (is_PC_relative (*exp))
+ rel_type = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_SB_relative (*exp))
+ rel_type = R_PARISC_SEGREL32;
+ else if (is_tls_gdidx (*exp))
+ rel_type = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (*exp))
+ rel_type = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (*exp))
+ rel_type = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (*exp))
+ rel_type = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (*exp))
+ rel_type = R_PARISC_TLS_LE21L;
+#endif
+ else if (is_complex (*exp))
+ rel_type = R_HPPA_COMPLEX;
+ else
+ rel_type = R_HPPA;
+
+ if (hppa_field_selector != e_psel && hppa_field_selector != e_fsel)
+ {
+ as_warn (_("Invalid field selector. Assuming F%%."));
+ hppa_field_selector = e_fsel;
+ }
+
+ fix_new_hppa (frag, where, size,
+ (symbolS *) NULL, (offsetT) 0, exp, 0, rel_type,
+ hppa_field_selector, size * 8, 0, 0);
+}
+
+/* Mark (via expr_end) the end of an expression (I think). FIXME. */
+
+static void
+get_expression (char *str)
+{
+ char *save_in;
+ asection *seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&the_insn.exp);
+ if (!(seg == absolute_section
+ || seg == undefined_section
+ || SEG_NORMAL (seg)))
+ {
+ as_warn (_("Bad segment in expression."));
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+}
+
+/* Parse a PA nullification completer (,n). Return nonzero if the
+ completer was found; return zero if no completer was found. */
+
+static int
+pa_parse_nullif (char **s)
+{
+ int nullif;
+
+ nullif = 0;
+ if (**s == ',')
+ {
+ *s = *s + 1;
+ if (strncasecmp (*s, "n", 1) == 0)
+ nullif = 1;
+ else
+ {
+ as_bad (_("Invalid Nullification: (%c)"), **s);
+ nullif = 0;
+ }
+ *s = *s + 1;
+ }
+
+ return nullif;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Write out big-endian. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (asection *section, fixS *fixp)
+{
+ arelent *reloc;
+ struct hppa_fix_struct *hppa_fixp;
+ static arelent *no_relocs = NULL;
+ arelent **relocs;
+ reloc_type **codes;
+ reloc_type code;
+ int n_relocs;
+ int i;
+
+ hppa_fixp = (struct hppa_fix_struct *) fixp->tc_fix_data;
+ if (fixp->fx_addsy == 0)
+ return &no_relocs;
+
+ gas_assert (hppa_fixp != 0);
+ gas_assert (section != 0);
+
+ reloc = xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ /* Allow fixup_segment to recognize hand-written pc-relative relocations.
+ When we went through cons_fix_new_hppa, we classified them as complex. */
+ /* ??? It might be better to hide this +8 stuff in tc_cfi_emit_pcrel_expr,
+ undefine DIFF_EXPR_OK, and let these sorts of complex expressions fail
+ when R_HPPA_COMPLEX == R_PARISC_UNIMPLEMENTED. */
+ if (fixp->fx_r_type == (bfd_reloc_code_real_type) R_HPPA_COMPLEX
+ && fixp->fx_pcrel)
+ {
+ fixp->fx_r_type = R_HPPA_PCREL_CALL;
+ fixp->fx_offset += 8;
+ }
+
+ codes = hppa_gen_reloc_type (stdoutput,
+ fixp->fx_r_type,
+ hppa_fixp->fx_r_format,
+ hppa_fixp->fx_r_field,
+ fixp->fx_subsy != NULL,
+ symbol_get_bfdsym (fixp->fx_addsy));
+
+ if (codes == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line, _("Cannot handle fixup"));
+ abort ();
+ }
+
+ for (n_relocs = 0; codes[n_relocs]; n_relocs++)
+ ;
+
+ relocs = xmalloc (sizeof (arelent *) * n_relocs + 1);
+ reloc = xmalloc (sizeof (arelent) * n_relocs);
+ for (i = 0; i < n_relocs; i++)
+ relocs[i] = &reloc[i];
+
+ relocs[n_relocs] = NULL;
+
+#ifdef OBJ_ELF
+ switch (fixp->fx_r_type)
+ {
+ default:
+ gas_assert (n_relocs == 1);
+
+ code = *codes[0];
+
+ /* Now, do any processing that is dependent on the relocation type. */
+ switch (code)
+ {
+ case R_PARISC_DLTREL21L:
+ case R_PARISC_DLTREL14R:
+ case R_PARISC_DLTREL14F:
+ case R_PARISC_PLABEL32:
+ case R_PARISC_PLABEL21L:
+ case R_PARISC_PLABEL14R:
+ /* For plabel relocations, the addend of the
+ relocation should be either 0 (no static link) or 2
+ (static link required). This adjustment is done in
+ bfd/elf32-hppa.c:elf32_hppa_relocate_section.
+
+ We also slam a zero addend into the DLT relative relocs;
+ it doesn't make a lot of sense to use any addend since
+ it gets you a different (eg unknown) DLT entry. */
+ reloc->addend = 0;
+ break;
+
+#ifdef ELF_ARG_RELOC
+ case R_PARISC_PCREL17R:
+ case R_PARISC_PCREL17F:
+ case R_PARISC_PCREL17C:
+ case R_PARISC_DIR17R:
+ case R_PARISC_DIR17F:
+ case R_PARISC_PCREL21L:
+ case R_PARISC_DIR21L:
+ reloc->addend = HPPA_R_ADDEND (hppa_fixp->fx_arg_reloc,
+ fixp->fx_offset);
+ break;
+#endif
+
+ case R_PARISC_DIR32:
+ /* Facilitate hand-crafted unwind info. */
+ if (strcmp (section->name, UNWIND_SECTION_NAME) == 0)
+ code = R_PARISC_SEGREL32;
+ /* Fall thru */
+
+ default:
+ reloc->addend = fixp->fx_offset;
+ break;
+ }
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->howto = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) code);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ gas_assert (reloc->howto && (unsigned int) code == reloc->howto->type);
+ break;
+ }
+#else /* OBJ_SOM */
+
+ /* Walk over reach relocation returned by the BFD backend. */
+ for (i = 0; i < n_relocs; i++)
+ {
+ code = *codes[i];
+
+ relocs[i]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *relocs[i]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ relocs[i]->howto =
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) code);
+ relocs[i]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ switch (code)
+ {
+ case R_COMP2:
+ /* The only time we ever use a R_COMP2 fixup is for the difference
+ of two symbols. With that in mind we fill in all four
+ relocs now and break out of the loop. */
+ gas_assert (i == 1);
+ relocs[0]->sym_ptr_ptr
+ = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
+ relocs[0]->howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) *codes[0]);
+ relocs[0]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[0]->addend = 0;
+ relocs[1]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *relocs[1]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ relocs[1]->howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) *codes[1]);
+ relocs[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[1]->addend = 0;
+ relocs[2]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *relocs[2]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ relocs[2]->howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) *codes[2]);
+ relocs[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[2]->addend = 0;
+ relocs[3]->sym_ptr_ptr
+ = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
+ relocs[3]->howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) *codes[3]);
+ relocs[3]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[3]->addend = 0;
+ relocs[4]->sym_ptr_ptr
+ = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
+ relocs[4]->howto
+ = bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) *codes[4]);
+ relocs[4]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[4]->addend = 0;
+ goto done;
+ case R_PCREL_CALL:
+ case R_ABS_CALL:
+ relocs[i]->addend = HPPA_R_ADDEND (hppa_fixp->fx_arg_reloc, 0);
+ break;
+
+ case R_DLT_REL:
+ case R_DATA_PLABEL:
+ case R_CODE_PLABEL:
+ /* For plabel relocations, the addend of the
+ relocation should be either 0 (no static link) or 2
+ (static link required).
+
+ FIXME: We always assume no static link!
+
+ We also slam a zero addend into the DLT relative relocs;
+ it doesn't make a lot of sense to use any addend since
+ it gets you a different (eg unknown) DLT entry. */
+ relocs[i]->addend = 0;
+ break;
+
+ case R_N_MODE:
+ case R_S_MODE:
+ case R_D_MODE:
+ case R_R_MODE:
+ case R_FSEL:
+ case R_LSEL:
+ case R_RSEL:
+ case R_BEGIN_BRTAB:
+ case R_END_BRTAB:
+ case R_BEGIN_TRY:
+ case R_N0SEL:
+ case R_N1SEL:
+ /* There is no symbol or addend associated with these fixups. */
+ relocs[i]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *relocs[i]->sym_ptr_ptr = symbol_get_bfdsym (dummy_symbol);
+ relocs[i]->addend = 0;
+ break;
+
+ case R_END_TRY:
+ case R_ENTRY:
+ case R_EXIT:
+ /* There is no symbol associated with these fixups. */
+ relocs[i]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *relocs[i]->sym_ptr_ptr = symbol_get_bfdsym (dummy_symbol);
+ relocs[i]->addend = fixp->fx_offset;
+ break;
+
+ default:
+ relocs[i]->addend = fixp->fx_offset;
+ }
+ }
+
+ done:
+#endif
+
+ return relocs;
+}
+
+/* Process any machine dependent frag types. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ unsigned int address;
+
+ if (fragP->fr_type == rs_machine_dependent)
+ {
+ switch ((int) fragP->fr_subtype)
+ {
+ case 0:
+ fragP->fr_type = rs_fill;
+ know (fragP->fr_var == 1);
+ know (fragP->fr_next);
+ address = fragP->fr_address + fragP->fr_fix;
+ if (address % fragP->fr_offset)
+ {
+ fragP->fr_offset =
+ fragP->fr_next->fr_address
+ - fragP->fr_address
+ - fragP->fr_fix;
+ }
+ else
+ fragP->fr_offset = 0;
+ break;
+ }
+ }
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (asection *segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ int align2 = (1 << align) - 1;
+
+ return (size + align2) & ~align2;
+}
+
+/* Return the approximate size of a frag before relaxation has occurred. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, asection *segment ATTRIBUTE_UNUSED)
+{
+ int size;
+
+ size = 0;
+
+ while ((fragP->fr_fix + size) % fragP->fr_offset)
+ size++;
+
+ return size;
+}
+
+#ifdef OBJ_ELF
+# ifdef WARN_COMMENTS
+const char *md_shortopts = "Vc";
+# else
+const char *md_shortopts = "V";
+# endif
+#else
+# ifdef WARN_COMMENTS
+const char *md_shortopts = "c";
+# else
+const char *md_shortopts = "";
+# endif
+#endif
+
+struct option md_longopts[] =
+{
+#ifdef WARN_COMMENTS
+ {"warn-comment", no_argument, NULL, 'c'},
+#endif
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+
+#ifdef OBJ_ELF
+ case 'V':
+ print_version_id ();
+ break;
+#endif
+#ifdef WARN_COMMENTS
+ case 'c':
+ warn_comment = 1;
+ break;
+#endif
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+ -Q ignored\n"));
+#endif
+#ifdef WARN_COMMENTS
+ fprintf (stream, _("\
+ -c print a warning if a comment is found\n"));
+#endif
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+#if defined (OBJ_SOM) || defined (ELF_ARG_RELOC)
+#define nonzero_dibits(x) \
+ ((x) | (((x) & 0x55555555) << 1) | (((x) & 0xAAAAAAAA) >> 1))
+#define arg_reloc_stub_needed(CALLER, CALLEE) \
+ (((CALLER) ^ (CALLEE)) & nonzero_dibits (CALLER) & nonzero_dibits (CALLEE))
+#else
+#define arg_reloc_stub_needed(CALLER, CALLEE) 0
+#endif
+
+/* Apply a fixup to an instruction. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *fixpos;
+ struct hppa_fix_struct *hppa_fixP;
+ offsetT new_val;
+ int insn, val, fmt;
+
+ /* SOM uses R_HPPA_ENTRY and R_HPPA_EXIT relocations which can
+ never be "applied" (they are just markers). Likewise for
+ R_HPPA_BEGIN_BRTAB and R_HPPA_END_BRTAB. */
+#ifdef OBJ_SOM
+ if (fixP->fx_r_type == R_HPPA_ENTRY
+ || fixP->fx_r_type == R_HPPA_EXIT
+ || fixP->fx_r_type == R_HPPA_BEGIN_BRTAB
+ || fixP->fx_r_type == R_HPPA_END_BRTAB
+ || fixP->fx_r_type == R_HPPA_BEGIN_TRY)
+ return;
+
+ /* Disgusting. We must set fx_offset ourselves -- R_HPPA_END_TRY
+ fixups are considered not adjustable, which in turn causes
+ adjust_reloc_syms to not set fx_offset. Ugh. */
+ if (fixP->fx_r_type == R_HPPA_END_TRY)
+ {
+ fixP->fx_offset = * valP;
+ return;
+ }
+#endif
+#ifdef OBJ_ELF
+ if (fixP->fx_r_type == (int) R_PARISC_GNU_VTENTRY
+ || fixP->fx_r_type == (int) R_PARISC_GNU_VTINHERIT)
+ return;
+#endif
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+
+ /* There should be a HPPA specific fixup associated with the GAS fixup. */
+ hppa_fixP = (struct hppa_fix_struct *) fixP->tc_fix_data;
+ if (hppa_fixP == NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("no hppa_fixup entry for fixup type 0x%x"),
+ fixP->fx_r_type);
+ return;
+ }
+
+ fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (fixP->fx_size != 4 || hppa_fixP->fx_r_format == 32)
+ {
+ /* Handle constant output. */
+ number_to_chars_bigendian (fixpos, *valP, fixP->fx_size);
+ return;
+ }
+
+ insn = bfd_get_32 (stdoutput, fixpos);
+ fmt = bfd_hppa_insn2fmt (stdoutput, insn);
+
+ /* If there is a symbol associated with this fixup, then it's something
+ which will need a SOM relocation (except for some PC-relative relocs).
+ In such cases we should treat the "val" or "addend" as zero since it
+ will be added in as needed from fx_offset in tc_gen_reloc. */
+ if ((fixP->fx_addsy != NULL
+ || fixP->fx_r_type == (int) R_HPPA_NONE)
+#ifdef OBJ_SOM
+ && fmt != 32
+#endif
+ )
+ new_val = ((fmt == 12 || fmt == 17 || fmt == 22) ? 8 : 0);
+#ifdef OBJ_SOM
+ /* These field selectors imply that we do not want an addend. */
+ else if (hppa_fixP->fx_r_field == e_psel
+ || hppa_fixP->fx_r_field == e_rpsel
+ || hppa_fixP->fx_r_field == e_lpsel
+ || hppa_fixP->fx_r_field == e_tsel
+ || hppa_fixP->fx_r_field == e_rtsel
+ || hppa_fixP->fx_r_field == e_ltsel)
+ new_val = ((fmt == 12 || fmt == 17 || fmt == 22) ? 8 : 0);
+#endif
+ else
+ new_val = hppa_field_adjust (* valP, 0, hppa_fixP->fx_r_field);
+
+ /* Handle pc-relative exceptions from above. */
+ if ((fmt == 12 || fmt == 17 || fmt == 22)
+ && fixP->fx_addsy
+ && fixP->fx_pcrel
+ && !arg_reloc_stub_needed (symbol_arg_reloc_info (fixP->fx_addsy),
+ hppa_fixP->fx_arg_reloc)
+#ifdef OBJ_ELF
+ && (* valP - 8 + 8192 < 16384
+ || (fmt == 17 && * valP - 8 + 262144 < 524288)
+ || (fmt == 22 && * valP - 8 + 8388608 < 16777216))
+#endif
+#ifdef OBJ_SOM
+ && (* valP - 8 + 262144 < 524288
+ || (fmt == 22 && * valP - 8 + 8388608 < 16777216))
+#endif
+ && !S_IS_EXTERNAL (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy)
+ && S_GET_SEGMENT (fixP->fx_addsy) == hppa_fixP->segment
+ && !(fixP->fx_subsy
+ && S_GET_SEGMENT (fixP->fx_subsy) != hppa_fixP->segment))
+ {
+ new_val = hppa_field_adjust (* valP, 0, hppa_fixP->fx_r_field);
+ }
+
+ switch (fmt)
+ {
+ case 10:
+ CHECK_FIELD_WHERE (new_val, 8191, -8192,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val;
+
+ insn = (insn & ~ 0x3ff1) | (((val & 0x1ff8) << 1)
+ | ((val & 0x2000) >> 13));
+ break;
+ case -11:
+ CHECK_FIELD_WHERE (new_val, 8191, -8192,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val;
+
+ insn = (insn & ~ 0x3ff9) | (((val & 0x1ffc) << 1)
+ | ((val & 0x2000) >> 13));
+ break;
+ /* Handle all opcodes with the 'j' operand type. */
+ case 14:
+ CHECK_FIELD_WHERE (new_val, 8191, -8192,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val;
+
+ insn = ((insn & ~ 0x3fff) | low_sign_unext (val, 14));
+ break;
+
+ /* Handle all opcodes with the 'k' operand type. */
+ case 21:
+ CHECK_FIELD_WHERE (new_val, 1048575, -1048576,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val;
+
+ insn = (insn & ~ 0x1fffff) | re_assemble_21 (val);
+ break;
+
+ /* Handle all the opcodes with the 'i' operand type. */
+ case 11:
+ CHECK_FIELD_WHERE (new_val, 1023, -1024,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val;
+
+ insn = (insn & ~ 0x7ff) | low_sign_unext (val, 11);
+ break;
+
+ /* Handle all the opcodes with the 'w' operand type. */
+ case 12:
+ CHECK_FIELD_WHERE (new_val - 8, 8191, -8192,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val - 8;
+
+ insn = (insn & ~ 0x1ffd) | re_assemble_12 (val >> 2);
+ break;
+
+ /* Handle some of the opcodes with the 'W' operand type. */
+ case 17:
+ {
+ offsetT distance = * valP;
+
+ /* If this is an absolute branch (ie no link) with an out of
+ range target, then we want to complain. */
+ if (fixP->fx_r_type == (int) R_HPPA_PCREL_CALL
+ && (insn & 0xffe00000) == 0xe8000000)
+ CHECK_FIELD_WHERE (distance - 8, 262143, -262144,
+ fixP->fx_file, fixP->fx_line);
+
+ CHECK_FIELD_WHERE (new_val - 8, 262143, -262144,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val - 8;
+
+ insn = (insn & ~ 0x1f1ffd) | re_assemble_17 (val >> 2);
+ break;
+ }
+
+ case 22:
+ {
+ offsetT distance = * valP;
+
+ /* If this is an absolute branch (ie no link) with an out of
+ range target, then we want to complain. */
+ if (fixP->fx_r_type == (int) R_HPPA_PCREL_CALL
+ && (insn & 0xffe00000) == 0xe8000000)
+ CHECK_FIELD_WHERE (distance - 8, 8388607, -8388608,
+ fixP->fx_file, fixP->fx_line);
+
+ CHECK_FIELD_WHERE (new_val - 8, 8388607, -8388608,
+ fixP->fx_file, fixP->fx_line);
+ val = new_val - 8;
+
+ insn = (insn & ~ 0x3ff1ffd) | re_assemble_22 (val >> 2);
+ break;
+ }
+
+ case -10:
+ val = new_val;
+ insn = (insn & ~ 0xfff1) | re_assemble_16 (val & -8);
+ break;
+
+ case -16:
+ val = new_val;
+ insn = (insn & ~ 0xfff9) | re_assemble_16 (val & -4);
+ break;
+
+ case 16:
+ val = new_val;
+ insn = (insn & ~ 0xffff) | re_assemble_16 (val);
+ break;
+
+ case 32:
+ insn = new_val;
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Unknown relocation encountered in md_apply_fix."));
+ return;
+ }
+
+#ifdef OBJ_ELF
+ switch (fixP->fx_r_type)
+ {
+ case R_PARISC_TLS_GD21L:
+ case R_PARISC_TLS_GD14R:
+ case R_PARISC_TLS_LDM21L:
+ case R_PARISC_TLS_LDM14R:
+ case R_PARISC_TLS_LE21L:
+ case R_PARISC_TLS_LE14R:
+ case R_PARISC_TLS_IE21L:
+ case R_PARISC_TLS_IE14R:
+ if (fixP->fx_addsy)
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+ default:
+ break;
+ }
+#endif
+
+ /* Insert the relocation. */
+ bfd_put_32 (stdoutput, insn, fixpos);
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the PA, they're relative to the address of the offset. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Return nonzero if the input line pointer is at the end of
+ a statement. */
+
+static int
+is_end_of_statement (void)
+{
+ return ((*input_line_pointer == '\n')
+ || (*input_line_pointer == ';')
+ || (*input_line_pointer == '!'));
+}
+
+#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
+
+/* Given NAME, find the register number associated with that name, return
+ the integer value associated with the given name or -1 on failure. */
+
+static int
+reg_name_search (char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = REG_NAME_CNT - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, pre_defined_registers[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return pre_defined_registers[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/* Read a number from S. The number might come in one of many forms,
+ the most common will be a hex or decimal constant, but it could be
+ a pre-defined register (Yuk!), or an absolute symbol.
+
+ Return 1 on success or 0 on failure. If STRICT, then a missing
+ register prefix will cause a failure. The number itself is
+ returned in `pa_number'.
+
+ IS_FLOAT indicates that a PA-89 FP register number should be
+ parsed; A `l' or `r' suffix is checked for if but 2 of IS_FLOAT is
+ not set.
+
+ pa_parse_number can not handle negative constants and will fail
+ horribly if it is passed such a constant. */
+
+static int
+pa_parse_number (char **s, int is_float)
+{
+ int num;
+ char *name;
+ char c;
+ symbolS *sym;
+ int status;
+ char *p = *s;
+ bfd_boolean have_prefix;
+
+ /* Skip whitespace before the number. */
+ while (*p == ' ' || *p == '\t')
+ p = p + 1;
+
+ pa_number = -1;
+ have_prefix = 0;
+ num = 0;
+ if (!strict && ISDIGIT (*p))
+ {
+ /* Looks like a number. */
+
+ if (*p == '0' && (*(p + 1) == 'x' || *(p + 1) == 'X'))
+ {
+ /* The number is specified in hex. */
+ p += 2;
+ while (ISDIGIT (*p) || ((*p >= 'a') && (*p <= 'f'))
+ || ((*p >= 'A') && (*p <= 'F')))
+ {
+ if (ISDIGIT (*p))
+ num = num * 16 + *p - '0';
+ else if (*p >= 'a' && *p <= 'f')
+ num = num * 16 + *p - 'a' + 10;
+ else
+ num = num * 16 + *p - 'A' + 10;
+ ++p;
+ }
+ }
+ else
+ {
+ /* The number is specified in decimal. */
+ while (ISDIGIT (*p))
+ {
+ num = num * 10 + *p - '0';
+ ++p;
+ }
+ }
+
+ pa_number = num;
+
+ /* Check for a `l' or `r' suffix. */
+ if (is_float)
+ {
+ pa_number += FP_REG_BASE;
+ if (! (is_float & 2))
+ {
+ if (IS_R_SELECT (p))
+ {
+ pa_number += FP_REG_RSEL;
+ ++p;
+ }
+ else if (IS_L_SELECT (p))
+ {
+ ++p;
+ }
+ }
+ }
+ }
+ else if (*p == '%')
+ {
+ /* The number might be a predefined register. */
+ have_prefix = 1;
+ name = p;
+ p++;
+ c = *p;
+ /* Tege hack: Special case for general registers as the general
+ code makes a binary search with case translation, and is VERY
+ slow. */
+ if (c == 'r')
+ {
+ p++;
+ if (*p == 'e' && *(p + 1) == 't'
+ && (*(p + 2) == '0' || *(p + 2) == '1'))
+ {
+ p += 2;
+ num = *p - '0' + 28;
+ p++;
+ }
+ else if (*p == 'p')
+ {
+ num = 2;
+ p++;
+ }
+ else if (!ISDIGIT (*p))
+ {
+ if (print_errors)
+ as_bad (_("Undefined register: '%s'."), name);
+ num = -1;
+ }
+ else
+ {
+ do
+ num = num * 10 + *p++ - '0';
+ while (ISDIGIT (*p));
+ }
+ }
+ else
+ {
+ /* Do a normal register search. */
+ while (is_part_of_name (c))
+ {
+ p = p + 1;
+ c = *p;
+ }
+ *p = 0;
+ status = reg_name_search (name);
+ if (status >= 0)
+ num = status;
+ else
+ {
+ if (print_errors)
+ as_bad (_("Undefined register: '%s'."), name);
+ num = -1;
+ }
+ *p = c;
+ }
+
+ pa_number = num;
+ }
+ else
+ {
+ /* And finally, it could be a symbol in the absolute section which
+ is effectively a constant, or a register alias symbol. */
+ name = p;
+ c = *p;
+ while (is_part_of_name (c))
+ {
+ p = p + 1;
+ c = *p;
+ }
+ *p = 0;
+ if ((sym = symbol_find (name)) != NULL)
+ {
+ if (S_GET_SEGMENT (sym) == reg_section)
+ {
+ num = S_GET_VALUE (sym);
+ /* Well, we don't really have one, but we do have a
+ register, so... */
+ have_prefix = TRUE;
+ }
+ else if (S_GET_SEGMENT (sym) == bfd_abs_section_ptr)
+ num = S_GET_VALUE (sym);
+ else if (!strict)
+ {
+ if (print_errors)
+ as_bad (_("Non-absolute symbol: '%s'."), name);
+ num = -1;
+ }
+ }
+ else if (!strict)
+ {
+ /* There is where we'd come for an undefined symbol
+ or for an empty string. For an empty string we
+ will return zero. That's a concession made for
+ compatibility with the braindamaged HP assemblers. */
+ if (*name == 0)
+ num = 0;
+ else
+ {
+ if (print_errors)
+ as_bad (_("Undefined absolute constant: '%s'."), name);
+ num = -1;
+ }
+ }
+ *p = c;
+
+ pa_number = num;
+ }
+
+ if (!strict || have_prefix)
+ {
+ *s = p;
+ return 1;
+ }
+ return 0;
+}
+
+/* Return nonzero if the given INSN and L/R information will require
+ a new PA-1.1 opcode. */
+
+static int
+need_pa11_opcode (void)
+{
+ if ((pa_number & FP_REG_RSEL) != 0
+ && !(the_insn.fpof1 == DBL && the_insn.fpof2 == DBL))
+ {
+ /* If this instruction is specific to a particular architecture,
+ then set a new architecture. */
+ if (bfd_get_mach (stdoutput) < pa11)
+ {
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, pa11))
+ as_warn (_("could not update architecture and machine"));
+ }
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/* Parse a condition for a fcmp instruction. Return the numerical
+ code associated with the condition. */
+
+static int
+pa_parse_fp_cmp_cond (char **s)
+{
+ int cond, i;
+
+ cond = 0;
+
+ for (i = 0; i < 32; i++)
+ {
+ if (strncasecmp (*s, fp_cond_map[i].string,
+ strlen (fp_cond_map[i].string)) == 0)
+ {
+ cond = fp_cond_map[i].cond;
+ *s += strlen (fp_cond_map[i].string);
+ /* If not a complete match, back up the input string and
+ report an error. */
+ if (**s != ' ' && **s != '\t')
+ {
+ *s -= strlen (fp_cond_map[i].string);
+ break;
+ }
+ while (**s == ' ' || **s == '\t')
+ *s = *s + 1;
+ return cond;
+ }
+ }
+
+ as_bad (_("Invalid FP Compare Condition: %s"), *s);
+
+ /* Advance over the bogus completer. */
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+
+ return 0;
+}
+
+/* Parse a graphics test complete for ftest. */
+
+static int
+pa_parse_ftest_gfx_completer (char **s)
+{
+ int value;
+
+ value = 0;
+ if (strncasecmp (*s, "acc8", 4) == 0)
+ {
+ value = 5;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "acc6", 4) == 0)
+ {
+ value = 9;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "acc4", 4) == 0)
+ {
+ value = 13;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "acc2", 4) == 0)
+ {
+ value = 17;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "acc", 3) == 0)
+ {
+ value = 1;
+ *s += 3;
+ }
+ else if (strncasecmp (*s, "rej8", 4) == 0)
+ {
+ value = 6;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "rej", 3) == 0)
+ {
+ value = 2;
+ *s += 3;
+ }
+ else
+ {
+ value = 0;
+ as_bad (_("Invalid FTEST completer: %s"), *s);
+ }
+
+ return value;
+}
+
+/* Parse an FP operand format completer returning the completer
+ type. */
+
+static fp_operand_format
+pa_parse_fp_cnv_format (char **s)
+{
+ int format;
+
+ format = SGL;
+ if (**s == ',')
+ {
+ *s += 1;
+ if (strncasecmp (*s, "sgl", 3) == 0)
+ {
+ format = SGL;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "dbl", 3) == 0)
+ {
+ format = DBL;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "quad", 4) == 0)
+ {
+ format = QUAD;
+ *s += 5;
+ }
+ else if (strncasecmp (*s, "w", 1) == 0)
+ {
+ format = W;
+ *s += 2;
+ }
+ else if (strncasecmp (*s, "uw", 2) == 0)
+ {
+ format = UW;
+ *s += 3;
+ }
+ else if (strncasecmp (*s, "dw", 2) == 0)
+ {
+ format = DW;
+ *s += 3;
+ }
+ else if (strncasecmp (*s, "udw", 3) == 0)
+ {
+ format = UDW;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "qw", 2) == 0)
+ {
+ format = QW;
+ *s += 3;
+ }
+ else if (strncasecmp (*s, "uqw", 3) == 0)
+ {
+ format = UQW;
+ *s += 4;
+ }
+ else
+ {
+ format = ILLEGAL_FMT;
+ as_bad (_("Invalid FP Operand Format: %3s"), *s);
+ }
+ }
+
+ return format;
+}
+
+/* Parse an FP operand format completer returning the completer
+ type. */
+
+static fp_operand_format
+pa_parse_fp_format (char **s)
+{
+ int format;
+
+ format = SGL;
+ if (**s == ',')
+ {
+ *s += 1;
+ if (strncasecmp (*s, "sgl", 3) == 0)
+ {
+ format = SGL;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "dbl", 3) == 0)
+ {
+ format = DBL;
+ *s += 4;
+ }
+ else if (strncasecmp (*s, "quad", 4) == 0)
+ {
+ format = QUAD;
+ *s += 5;
+ }
+ else
+ {
+ format = ILLEGAL_FMT;
+ as_bad (_("Invalid FP Operand Format: %3s"), *s);
+ }
+ }
+
+ return format;
+}
+
+/* Convert from a selector string into a selector type. */
+
+static int
+pa_chk_field_selector (char **str)
+{
+ int middle, low, high;
+ int cmp;
+ char name[4];
+
+ /* Read past any whitespace. */
+ /* FIXME: should we read past newlines and formfeeds??? */
+ while (**str == ' ' || **str == '\t' || **str == '\n' || **str == '\f')
+ *str = *str + 1;
+
+ if ((*str)[1] == '\'' || (*str)[1] == '%')
+ name[0] = TOLOWER ((*str)[0]),
+ name[1] = 0;
+ else if ((*str)[2] == '\'' || (*str)[2] == '%')
+ name[0] = TOLOWER ((*str)[0]),
+ name[1] = TOLOWER ((*str)[1]),
+ name[2] = 0;
+ else if ((*str)[3] == '\'' || (*str)[3] == '%')
+ name[0] = TOLOWER ((*str)[0]),
+ name[1] = TOLOWER ((*str)[1]),
+ name[2] = TOLOWER ((*str)[2]),
+ name[3] = 0;
+ else
+ return e_fsel;
+
+ low = 0;
+ high = sizeof (selector_table) / sizeof (struct selector_entry) - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcmp (name, selector_table[middle].prefix);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ {
+ *str += strlen (name) + 1;
+#ifndef OBJ_SOM
+ if (selector_table[middle].field_selector == e_nsel)
+ return e_fsel;
+#endif
+ return selector_table[middle].field_selector;
+ }
+ }
+ while (low <= high);
+
+ return e_fsel;
+}
+
+/* Parse a .byte, .word, .long expression for the HPPA. Called by
+ cons via the TC_PARSE_CONS_EXPRESSION macro. */
+
+int
+parse_cons_expression_hppa (expressionS *exp)
+{
+ int hppa_field_selector = pa_chk_field_selector (&input_line_pointer);
+ expression (exp);
+ return hppa_field_selector;
+}
+
+/* Evaluate an absolute expression EXP which may be modified by
+ the selector FIELD_SELECTOR. Return the value of the expression. */
+static int
+evaluate_absolute (struct pa_it *insn)
+{
+ offsetT value;
+ expressionS exp;
+ int field_selector = insn->field_selector;
+
+ exp = insn->exp;
+ value = exp.X_add_number;
+
+ return hppa_field_adjust (0, value, field_selector);
+}
+
+/* Mark (via expr_end) the end of an absolute expression. FIXME. */
+
+static int
+pa_get_absolute_expression (struct pa_it *insn, char **strp)
+{
+ char *save_in;
+
+ insn->field_selector = pa_chk_field_selector (strp);
+ save_in = input_line_pointer;
+ input_line_pointer = *strp;
+ expression (&insn->exp);
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ if (insn->exp.X_op != O_constant)
+ {
+ /* We have a non-match in strict mode. */
+ if (!strict)
+ as_bad (_("Bad segment (should be absolute)."));
+ return 0;
+ }
+ return evaluate_absolute (insn);
+}
+
+/* Get an absolute number. The input string is terminated at the
+ first whitespace character. */
+
+static int
+pa_get_number (struct pa_it *insn, char **strp)
+{
+ char *save_in;
+ char *s, c;
+ int result;
+
+ save_in = input_line_pointer;
+ input_line_pointer = *strp;
+
+ /* The PA assembly syntax is ambiguous in a variety of ways. Consider
+ this string "4 %r5" Is that the number 4 followed by the register
+ r5, or is that 4 MOD r5? This situation occurs for example in the
+ coprocessor load and store instructions. Previously, calling
+ pa_get_absolute_expression directly results in r5 being entered
+ in the symbol table.
+
+ So, when looking for an absolute number, we cut off the input string
+ at the first whitespace character. Thus, expressions should generally
+ contain no whitespace. */
+
+ s = *strp;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s++;
+
+ c = *s;
+ *s = 0;
+
+ result = pa_get_absolute_expression (insn, strp);
+
+ input_line_pointer = save_in;
+ *s = c;
+ return result;
+}
+
+/* Given an argument location specification return the associated
+ argument location number. */
+
+static unsigned int
+pa_build_arg_reloc (char *type_name)
+{
+
+ if (strncasecmp (type_name, "no", 2) == 0)
+ return 0;
+ if (strncasecmp (type_name, "gr", 2) == 0)
+ return 1;
+ else if (strncasecmp (type_name, "fr", 2) == 0)
+ return 2;
+ else if (strncasecmp (type_name, "fu", 2) == 0)
+ return 3;
+ else
+ as_bad (_("Invalid argument location: %s\n"), type_name);
+
+ return 0;
+}
+
+/* Encode and return an argument relocation specification for
+ the given register in the location specified by arg_reloc. */
+
+static unsigned int
+pa_align_arg_reloc (unsigned int reg, unsigned int arg_reloc)
+{
+ unsigned int new_reloc;
+
+ new_reloc = arg_reloc;
+ switch (reg)
+ {
+ case 0:
+ new_reloc <<= 8;
+ break;
+ case 1:
+ new_reloc <<= 6;
+ break;
+ case 2:
+ new_reloc <<= 4;
+ break;
+ case 3:
+ new_reloc <<= 2;
+ break;
+ default:
+ as_bad (_("Invalid argument description: %d"), reg);
+ }
+
+ return new_reloc;
+}
+
+/* Parse a non-negated compare/subtract completer returning the
+ number (for encoding in instructions) of the given completer. */
+
+static int
+pa_parse_nonneg_cmpsub_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+
+ if (strcmp (name, "=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcmp (name, "<<") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcmp (name, "<<=") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "sv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "od") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Parse a negated compare/subtract completer returning the
+ number (for encoding in instructions) of the given completer. */
+
+static int
+pa_parse_neg_cmpsub_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+
+ if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcmp (name, ">>=") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcmp (name, ">>") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Parse a 64 bit compare and branch completer returning the number (for
+ encoding in instructions) of the given completer.
+
+ Nonnegated comparisons are returned as 0-7, negated comparisons are
+ returned as 8-15. */
+
+static int
+pa_parse_cmpb_64_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+
+ cmpltr = -1;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+
+ if (strcmp (name, "*") == 0)
+ {
+ cmpltr = 0;
+ }
+ else if (strcmp (name, "*=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "*<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "*<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcmp (name, "*<<") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcmp (name, "*<<=") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "*sv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "*od") == 0)
+ {
+ cmpltr = 7;
+ }
+ else if (strcasecmp (name, "*tr") == 0)
+ {
+ cmpltr = 8;
+ }
+ else if (strcmp (name, "*<>") == 0)
+ {
+ cmpltr = 9;
+ }
+ else if (strcmp (name, "*>=") == 0)
+ {
+ cmpltr = 10;
+ }
+ else if (strcmp (name, "*>") == 0)
+ {
+ cmpltr = 11;
+ }
+ else if (strcmp (name, "*>>=") == 0)
+ {
+ cmpltr = 12;
+ }
+ else if (strcmp (name, "*>>") == 0)
+ {
+ cmpltr = 13;
+ }
+ else if (strcasecmp (name, "*nsv") == 0)
+ {
+ cmpltr = 14;
+ }
+ else if (strcasecmp (name, "*ev") == 0)
+ {
+ cmpltr = 15;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ return cmpltr;
+}
+
+/* Parse a 64 bit compare immediate and branch completer returning the number
+ (for encoding in instructions) of the given completer. */
+
+static int
+pa_parse_cmpib_64_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+
+ cmpltr = -1;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+
+ if (strcmp (name, "*<<") == 0)
+ {
+ cmpltr = 0;
+ }
+ else if (strcmp (name, "*=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "*<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "*<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcmp (name, "*>>=") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcmp (name, "*<>") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "*>=") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "*>") == 0)
+ {
+ cmpltr = 7;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ return cmpltr;
+}
+
+/* Parse a non-negated addition completer returning the number
+ (for encoding in instructions) of the given completer. */
+
+static int
+pa_parse_nonneg_add_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+ if (strcmp (name, "=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcasecmp (name, "nuv") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcasecmp (name, "znv") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "sv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "od") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Parse a negated addition completer returning the number
+ (for encoding in instructions) of the given completer. */
+
+static int
+pa_parse_neg_add_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+ if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcasecmp (name, "uv") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcasecmp (name, "vnz") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Parse a 64 bit wide mode add and branch completer returning the number (for
+ encoding in instructions) of the given completer. */
+
+static int
+pa_parse_addb_64_cmpltr (char **s)
+{
+ int cmpltr;
+ char *name = *s + 1;
+ char c;
+ char *save_s = *s;
+ int nullify = 0;
+
+ cmpltr = 0;
+ if (**s == ',')
+ {
+ *s += 1;
+ while (**s != ',' && **s != ' ' && **s != '\t')
+ *s += 1;
+ c = **s;
+ **s = 0x00;
+ if (strcmp (name, "=") == 0)
+ {
+ cmpltr = 1;
+ }
+ else if (strcmp (name, "<") == 0)
+ {
+ cmpltr = 2;
+ }
+ else if (strcmp (name, "<=") == 0)
+ {
+ cmpltr = 3;
+ }
+ else if (strcasecmp (name, "nuv") == 0)
+ {
+ cmpltr = 4;
+ }
+ else if (strcasecmp (name, "*=") == 0)
+ {
+ cmpltr = 5;
+ }
+ else if (strcasecmp (name, "*<") == 0)
+ {
+ cmpltr = 6;
+ }
+ else if (strcasecmp (name, "*<=") == 0)
+ {
+ cmpltr = 7;
+ }
+ else if (strcmp (name, "tr") == 0)
+ {
+ cmpltr = 8;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 9;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 10;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 11;
+ }
+ else if (strcasecmp (name, "uv") == 0)
+ {
+ cmpltr = 12;
+ }
+ else if (strcasecmp (name, "*<>") == 0)
+ {
+ cmpltr = 13;
+ }
+ else if (strcasecmp (name, "*>=") == 0)
+ {
+ cmpltr = 14;
+ }
+ else if (strcasecmp (name, "*>") == 0)
+ {
+ cmpltr = 15;
+ }
+ /* If we have something like addb,n then there is no condition
+ completer. */
+ else if (strcasecmp (name, "n") == 0)
+ {
+ cmpltr = 0;
+ nullify = 1;
+ }
+ else
+ {
+ cmpltr = -1;
+ }
+ **s = c;
+ }
+
+ /* Reset pointers if this was really a ,n for a branch instruction. */
+ if (nullify)
+ *s = save_s;
+
+ return cmpltr;
+}
+
+/* Do the real work for assembling a single instruction. Store results
+ into the global "the_insn" variable. */
+
+static void
+pa_ip (char *str)
+{
+ char *error_message = "";
+ char *s, c, *argstart, *name, *save_s;
+ const char *args;
+ int match = FALSE;
+ int comma = 0;
+ int cmpltr, nullif, flag, cond, need_cond, num;
+ int immediate_check = 0, pos = -1, len = -1;
+ unsigned long opcode;
+ struct pa_opcode *insn;
+
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ /* Convert everything up to the first whitespace character into lower
+ case. */
+ for (s = str; *s != ' ' && *s != '\t' && *s != '\n' && *s != '\0'; s++)
+ *s = TOLOWER (*s);
+
+ /* Skip to something interesting. */
+ for (s = str;
+ ISUPPER (*s) || ISLOWER (*s) || (*s >= '0' && *s <= '3');
+ ++s)
+ ;
+
+ switch (*s)
+ {
+
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH */
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+
+ /* Look up the opcode in the hash table. */
+ if ((insn = (struct pa_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+
+ if (comma)
+ *--s = ',';
+
+ /* Mark the location where arguments for the instruction start, then
+ start processing them. */
+ argstart = s;
+ for (;;)
+ {
+ /* Do some initialization. */
+ opcode = insn->match;
+ strict = (insn->flags & FLAG_STRICT);
+ memset (&the_insn, 0, sizeof (the_insn));
+ need_cond = 1;
+
+ the_insn.reloc = R_HPPA_NONE;
+
+ if (insn->arch >= pa20
+ && bfd_get_mach (stdoutput) < insn->arch)
+ goto failed;
+
+ /* Build the opcode, checking as we go to make
+ sure that the operands match. */
+ for (args = insn->args;; ++args)
+ {
+ /* Absorb white space in instruction. */
+ while (*s == ' ' || *s == '\t')
+ s++;
+
+ switch (*args)
+ {
+ /* End of arguments. */
+ case '\0':
+ if (*s == '\0')
+ match = TRUE;
+ break;
+
+ case '+':
+ if (*s == '+')
+ {
+ ++s;
+ continue;
+ }
+ if (*s == '-')
+ continue;
+ break;
+
+ /* These must match exactly. */
+ case '(':
+ case ')':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ /* Handle a 5 bit register or control register field at 10. */
+ case 'b':
+ case '^':
+ if (!pa_parse_number (&s, 0))
+ break;
+ num = pa_number;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 21);
+
+ /* Handle %sar or %cr11. No bits get set, we just verify that it
+ is there. */
+ case '!':
+ /* Skip whitespace before register. */
+ while (*s == ' ' || *s == '\t')
+ s = s + 1;
+
+ if (!strncasecmp (s, "%sar", 4))
+ {
+ s += 4;
+ continue;
+ }
+ else if (!strncasecmp (s, "%cr11", 5))
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ /* Handle a 5 bit register field at 15. */
+ case 'x':
+ if (!pa_parse_number (&s, 0))
+ break;
+ num = pa_number;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle a 5 bit register field at 31. */
+ case 't':
+ if (!pa_parse_number (&s, 0))
+ break;
+ num = pa_number;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 5 bit register field at 10 and 15. */
+ case 'a':
+ if (!pa_parse_number (&s, 0))
+ break;
+ num = pa_number;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num << 16;
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 21);
+
+ /* Handle a 5 bit field length at 31. */
+ case 'T':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 32, 1, 0);
+ SAVE_IMMEDIATE(num);
+ INSERT_FIELD_AND_CONTINUE (opcode, 32 - num, 0);
+
+ /* Handle a 5 bit immediate at 15. */
+ case '5':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ /* When in strict mode, we want to just reject this
+ match instead of giving an out of range error. */
+ CHECK_FIELD (num, 15, -16, strict);
+ num = low_sign_unext (num, 5);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle a 5 bit immediate at 31. */
+ case 'V':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ /* When in strict mode, we want to just reject this
+ match instead of giving an out of range error. */
+ CHECK_FIELD (num, 15, -16, strict);
+ num = low_sign_unext (num, 5);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle an unsigned 5 bit immediate at 31. */
+ case 'r':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle an unsigned 5 bit immediate at 15. */
+ case 'R':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle an unsigned 10 bit immediate at 15. */
+ case 'U':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 1023, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ /* Handle a 2 bit space identifier at 17. */
+ case 's':
+ if (!pa_parse_number (&s, 0))
+ break;
+ num = pa_number;
+ CHECK_FIELD (num, 3, 0, 1);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 14);
+
+ /* Handle a 3 bit space identifier at 18. */
+ case 'S':
+ if (!pa_parse_number (&s, 0))
+ break;
+ num = pa_number;
+ CHECK_FIELD (num, 7, 0, 1);
+ opcode |= re_assemble_3 (num);
+ continue;
+
+ /* Handle all completers. */
+ case 'c':
+ switch (*++args)
+ {
+
+ /* Handle a completer for an indexing load or store. */
+ case 'X':
+ case 'x':
+ {
+ int uu = 0;
+ int m = 0;
+ int i = 0;
+ while (*s == ',' && i < 2)
+ {
+ s++;
+ if (strncasecmp (s, "sm", 2) == 0)
+ {
+ uu = 1;
+ m = 1;
+ s++;
+ i++;
+ }
+ else if (strncasecmp (s, "m", 1) == 0)
+ m = 1;
+ else if ((strncasecmp (s, "s ", 2) == 0)
+ || (strncasecmp (s, "s,", 2) == 0))
+ uu = 1;
+ else if (strict)
+ {
+ /* This is a match failure. */
+ s--;
+ break;
+ }
+ else
+ as_bad (_("Invalid Indexed Load Completer."));
+ s++;
+ i++;
+ }
+ if (i > 2)
+ as_bad (_("Invalid Indexed Load Completer Syntax."));
+ opcode |= m << 5;
+ INSERT_FIELD_AND_CONTINUE (opcode, uu, 13);
+ }
+
+ /* Handle a short load/store completer. */
+ case 'M':
+ case 'm':
+ case 'q':
+ case 'J':
+ case 'e':
+ {
+ int a = 0;
+ int m = 0;
+ if (*s == ',')
+ {
+ s++;
+ if (strncasecmp (s, "ma", 2) == 0)
+ {
+ a = 0;
+ m = 1;
+ s += 2;
+ }
+ else if (strncasecmp (s, "mb", 2) == 0)
+ {
+ a = 1;
+ m = 1;
+ s += 2;
+ }
+ else if (strict)
+ /* This is a match failure. */
+ s--;
+ else
+ {
+ as_bad (_("Invalid Short Load/Store Completer."));
+ s += 2;
+ }
+ }
+ /* If we did not get a ma/mb completer, then we do not
+ consider this a positive match for 'ce'. */
+ else if (*args == 'e')
+ break;
+
+ /* 'J', 'm', 'M' and 'q' are the same, except for where they
+ encode the before/after field. */
+ if (*args == 'm' || *args == 'M')
+ {
+ opcode |= m << 5;
+ INSERT_FIELD_AND_CONTINUE (opcode, a, 13);
+ }
+ else if (*args == 'q')
+ {
+ opcode |= m << 3;
+ INSERT_FIELD_AND_CONTINUE (opcode, a, 2);
+ }
+ else if (*args == 'J')
+ {
+ /* M bit is explicit in the major opcode. */
+ INSERT_FIELD_AND_CONTINUE (opcode, a, 2);
+ }
+ else if (*args == 'e')
+ {
+ /* Stash the ma/mb flag temporarily in the
+ instruction. We will use (and remove it)
+ later when handling 'J', 'K', '<' & '>'. */
+ opcode |= a;
+ continue;
+ }
+ }
+
+ /* Handle a stbys completer. */
+ case 'A':
+ case 's':
+ {
+ int a = 0;
+ int m = 0;
+ int i = 0;
+ while (*s == ',' && i < 2)
+ {
+ s++;
+ if (strncasecmp (s, "m", 1) == 0)
+ m = 1;
+ else if ((strncasecmp (s, "b ", 2) == 0)
+ || (strncasecmp (s, "b,", 2) == 0))
+ a = 0;
+ else if (strncasecmp (s, "e", 1) == 0)
+ a = 1;
+ /* In strict mode, this is a match failure. */
+ else if (strict)
+ {
+ s--;
+ break;
+ }
+ else
+ as_bad (_("Invalid Store Bytes Short Completer"));
+ s++;
+ i++;
+ }
+ if (i > 2)
+ as_bad (_("Invalid Store Bytes Short Completer"));
+ opcode |= m << 5;
+ INSERT_FIELD_AND_CONTINUE (opcode, a, 13);
+ }
+
+ /* Handle load cache hint completer. */
+ case 'c':
+ cmpltr = 0;
+ if (!strncmp (s, ",sl", 3))
+ {
+ s += 3;
+ cmpltr = 2;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 10);
+
+ /* Handle store cache hint completer. */
+ case 'C':
+ cmpltr = 0;
+ if (!strncmp (s, ",sl", 3))
+ {
+ s += 3;
+ cmpltr = 2;
+ }
+ else if (!strncmp (s, ",bc", 3))
+ {
+ s += 3;
+ cmpltr = 1;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 10);
+
+ /* Handle load and clear cache hint completer. */
+ case 'd':
+ cmpltr = 0;
+ if (!strncmp (s, ",co", 3))
+ {
+ s += 3;
+ cmpltr = 1;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 10);
+
+ /* Handle load ordering completer. */
+ case 'o':
+ if (strncmp (s, ",o", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ /* Handle a branch gate completer. */
+ case 'g':
+ if (strncasecmp (s, ",gate", 5) != 0)
+ break;
+ s += 5;
+ continue;
+
+ /* Handle a branch link and push completer. */
+ case 'p':
+ if (strncasecmp (s, ",l,push", 7) != 0)
+ break;
+ s += 7;
+ continue;
+
+ /* Handle a branch link completer. */
+ case 'l':
+ if (strncasecmp (s, ",l", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ /* Handle a branch pop completer. */
+ case 'P':
+ if (strncasecmp (s, ",pop", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ /* Handle a local processor completer. */
+ case 'L':
+ if (strncasecmp (s, ",l", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ /* Handle a PROBE read/write completer. */
+ case 'w':
+ flag = 0;
+ if (!strncasecmp (s, ",w", 2))
+ {
+ flag = 1;
+ s += 2;
+ }
+ else if (!strncasecmp (s, ",r", 2))
+ {
+ flag = 0;
+ s += 2;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 6);
+
+ /* Handle MFCTL wide completer. */
+ case 'W':
+ if (strncasecmp (s, ",w", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ /* Handle an RFI restore completer. */
+ case 'r':
+ flag = 0;
+ if (!strncasecmp (s, ",r", 2))
+ {
+ flag = 5;
+ s += 2;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 5);
+
+ /* Handle a system control completer. */
+ case 'Z':
+ if (*s == ',' && (*(s + 1) == 'm' || *(s + 1) == 'M'))
+ {
+ flag = 1;
+ s += 2;
+ }
+ else
+ flag = 0;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 5);
+
+ /* Handle intermediate/final completer for DCOR. */
+ case 'i':
+ flag = 0;
+ if (!strncasecmp (s, ",i", 2))
+ {
+ flag = 1;
+ s += 2;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 6);
+
+ /* Handle zero/sign extension completer. */
+ case 'z':
+ flag = 1;
+ if (!strncasecmp (s, ",z", 2))
+ {
+ flag = 0;
+ s += 2;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 10);
+
+ /* Handle add completer. */
+ case 'a':
+ flag = 1;
+ if (!strncasecmp (s, ",l", 2))
+ {
+ flag = 2;
+ s += 2;
+ }
+ else if (!strncasecmp (s, ",tsv", 4))
+ {
+ flag = 3;
+ s += 4;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 10);
+
+ /* Handle 64 bit carry for ADD. */
+ case 'Y':
+ flag = 0;
+ if (!strncasecmp (s, ",dc,tsv", 7) ||
+ !strncasecmp (s, ",tsv,dc", 7))
+ {
+ flag = 1;
+ s += 7;
+ }
+ else if (!strncasecmp (s, ",dc", 3))
+ {
+ flag = 0;
+ s += 3;
+ }
+ else
+ break;
+
+ /* Condition is not required with "dc". */
+ need_cond = 0;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle 32 bit carry for ADD. */
+ case 'y':
+ flag = 0;
+ if (!strncasecmp (s, ",c,tsv", 6) ||
+ !strncasecmp (s, ",tsv,c", 6))
+ {
+ flag = 1;
+ s += 6;
+ }
+ else if (!strncasecmp (s, ",c", 2))
+ {
+ flag = 0;
+ s += 2;
+ }
+ else
+ break;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle trap on signed overflow. */
+ case 'v':
+ flag = 0;
+ if (!strncasecmp (s, ",tsv", 4))
+ {
+ flag = 1;
+ s += 4;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle trap on condition and overflow. */
+ case 't':
+ flag = 0;
+ if (!strncasecmp (s, ",tc,tsv", 7) ||
+ !strncasecmp (s, ",tsv,tc", 7))
+ {
+ flag = 1;
+ s += 7;
+ }
+ else if (!strncasecmp (s, ",tc", 3))
+ {
+ flag = 0;
+ s += 3;
+ }
+ else
+ break;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle 64 bit borrow for SUB. */
+ case 'B':
+ flag = 0;
+ if (!strncasecmp (s, ",db,tsv", 7) ||
+ !strncasecmp (s, ",tsv,db", 7))
+ {
+ flag = 1;
+ s += 7;
+ }
+ else if (!strncasecmp (s, ",db", 3))
+ {
+ flag = 0;
+ s += 3;
+ }
+ else
+ break;
+
+ /* Condition is not required with "db". */
+ need_cond = 0;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle 32 bit borrow for SUB. */
+ case 'b':
+ flag = 0;
+ if (!strncasecmp (s, ",b,tsv", 6) ||
+ !strncasecmp (s, ",tsv,b", 6))
+ {
+ flag = 1;
+ s += 6;
+ }
+ else if (!strncasecmp (s, ",b", 2))
+ {
+ flag = 0;
+ s += 2;
+ }
+ else
+ break;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle trap condition completer for UADDCM. */
+ case 'T':
+ flag = 0;
+ if (!strncasecmp (s, ",tc", 3))
+ {
+ flag = 1;
+ s += 3;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 6);
+
+ /* Handle signed/unsigned at 21. */
+ case 'S':
+ {
+ int sign = 1;
+ if (strncasecmp (s, ",s", 2) == 0)
+ {
+ sign = 1;
+ s += 2;
+ }
+ else if (strncasecmp (s, ",u", 2) == 0)
+ {
+ sign = 0;
+ s += 2;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, sign, 10);
+ }
+
+ /* Handle left/right combination at 17:18. */
+ case 'h':
+ if (*s++ == ',')
+ {
+ int lr = 0;
+ if (*s == 'r')
+ lr = 2;
+ else if (*s == 'l')
+ lr = 0;
+ else
+ as_bad (_("Invalid left/right combination completer"));
+
+ s++;
+ INSERT_FIELD_AND_CONTINUE (opcode, lr, 13);
+ }
+ else
+ as_bad (_("Invalid left/right combination completer"));
+ break;
+
+ /* Handle saturation at 24:25. */
+ case 'H':
+ {
+ int sat = 3;
+ if (strncasecmp (s, ",ss", 3) == 0)
+ {
+ sat = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, ",us", 3) == 0)
+ {
+ sat = 0;
+ s += 3;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, sat, 6);
+ }
+
+ /* Handle permutation completer. */
+ case '*':
+ if (*s++ == ',')
+ {
+ int permloc[4];
+ int perm = 0;
+ int i = 0;
+ permloc[0] = 13;
+ permloc[1] = 10;
+ permloc[2] = 8;
+ permloc[3] = 6;
+ for (; i < 4; i++)
+ {
+ switch (*s++)
+ {
+ case '0':
+ perm = 0;
+ break;
+ case '1':
+ perm = 1;
+ break;
+ case '2':
+ perm = 2;
+ break;
+ case '3':
+ perm = 3;
+ break;
+ default:
+ as_bad (_("Invalid permutation completer"));
+ }
+ opcode |= perm << permloc[i];
+ }
+ continue;
+ }
+ else
+ as_bad (_("Invalid permutation completer"));
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ /* Handle all conditions. */
+ case '?':
+ {
+ args++;
+ switch (*args)
+ {
+ /* Handle FP compare conditions. */
+ case 'f':
+ cond = pa_parse_fp_cmp_cond (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, cond, 0);
+
+ /* Handle an add condition. */
+ case 'A':
+ case 'a':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+
+ /* 64 bit conditions. */
+ if (*args == 'A')
+ {
+ if (*s == '*')
+ s++;
+ else
+ break;
+ }
+ else if (*s == '*')
+ break;
+
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcmp (name, "<=") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "nuv") == 0)
+ cmpltr = 4;
+ else if (strcasecmp (name, "znv") == 0)
+ cmpltr = 5;
+ else if (strcasecmp (name, "sv") == 0)
+ cmpltr = 6;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 7;
+ else if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "uv") == 0)
+ {
+ cmpltr = 4;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "vnz") == 0)
+ {
+ cmpltr = 5;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ }
+ /* ",*" is a valid condition. */
+ else if (*args == 'a' || *name)
+ as_bad (_("Invalid Add Condition: %s"), name);
+ *s = c;
+ }
+ /* Except with "dc", we have a match failure with
+ 'A' if we don't have a doubleword condition. */
+ else if (*args == 'A' && need_cond)
+ break;
+
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* Handle non-negated add and branch condition. */
+ case 'd':
+ cmpltr = pa_parse_nonneg_add_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Add and Branch Condition"));
+ cmpltr = 0;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle 64 bit wide-mode add and branch condition. */
+ case 'W':
+ cmpltr = pa_parse_addb_64_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Add and Branch Condition"));
+ cmpltr = 0;
+ }
+ else
+ {
+ /* Negated condition requires an opcode change. */
+ opcode |= (cmpltr & 8) << 24;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr & 7, 13);
+
+ /* Handle a negated or non-negated add and branch
+ condition. */
+ case '@':
+ save_s = s;
+ cmpltr = pa_parse_nonneg_add_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ s = save_s;
+ cmpltr = pa_parse_neg_add_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare/Subtract Condition"));
+ cmpltr = 0;
+ }
+ else
+ {
+ /* Negated condition requires an opcode change. */
+ opcode |= 1 << 27;
+ }
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle branch on bit conditions. */
+ case 'B':
+ case 'b':
+ cmpltr = 0;
+ if (*s == ',')
+ {
+ s++;
+
+ if (*args == 'B')
+ {
+ if (*s == '*')
+ s++;
+ else
+ break;
+ }
+ else if (*s == '*')
+ break;
+
+ if (strncmp (s, "<", 1) == 0)
+ {
+ cmpltr = 0;
+ s++;
+ }
+ else if (strncmp (s, ">=", 2) == 0)
+ {
+ cmpltr = 1;
+ s += 2;
+ }
+ else
+ as_bad (_("Invalid Branch On Bit Condition: %c"), *s);
+ }
+ else
+ as_bad (_("Missing Branch On Bit Condition"));
+
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 15);
+
+ /* Handle a compare/subtract condition. */
+ case 'S':
+ case 's':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+
+ /* 64 bit conditions. */
+ if (*args == 'S')
+ {
+ if (*s == '*')
+ s++;
+ else
+ break;
+ }
+ else if (*s == '*')
+ break;
+
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcmp (name, "<=") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "<<") == 0)
+ cmpltr = 4;
+ else if (strcasecmp (name, "<<=") == 0)
+ cmpltr = 5;
+ else if (strcasecmp (name, "sv") == 0)
+ cmpltr = 6;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 7;
+ else if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ }
+ else if (strcasecmp (name, ">>=") == 0)
+ {
+ cmpltr = 4;
+ flag = 1;
+ }
+ else if (strcasecmp (name, ">>") == 0)
+ {
+ cmpltr = 5;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "nsv") == 0)
+ {
+ cmpltr = 6;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ }
+ /* ",*" is a valid condition. */
+ else if (*args != 'S' || *name)
+ as_bad (_("Invalid Compare/Subtract Condition: %s"),
+ name);
+ *s = c;
+ }
+ /* Except with "db", we have a match failure with
+ 'S' if we don't have a doubleword condition. */
+ else if (*args == 'S' && need_cond)
+ break;
+
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* Handle a non-negated compare condition. */
+ case 't':
+ cmpltr = pa_parse_nonneg_cmpsub_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare/Subtract Condition"));
+ cmpltr = 0;
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a 32 bit compare and branch condition. */
+ case 'n':
+ save_s = s;
+ cmpltr = pa_parse_nonneg_cmpsub_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ s = save_s;
+ cmpltr = pa_parse_neg_cmpsub_cmpltr (&s);
+ if (cmpltr < 0)
+ {
+ as_bad (_("Invalid Compare and Branch Condition"));
+ cmpltr = 0;
+ }
+ else
+ {
+ /* Negated condition requires an opcode change. */
+ opcode |= 1 << 27;
+ }
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a 64 bit compare and branch condition. */
+ case 'N':
+ cmpltr = pa_parse_cmpb_64_cmpltr (&s);
+ if (cmpltr >= 0)
+ {
+ /* Negated condition requires an opcode change. */
+ opcode |= (cmpltr & 8) << 26;
+ }
+ else
+ /* Not a 64 bit cond. Give 32 bit a chance. */
+ break;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr & 7, 13);
+
+ /* Handle a 64 bit cmpib condition. */
+ case 'Q':
+ cmpltr = pa_parse_cmpib_64_cmpltr (&s);
+ if (cmpltr < 0)
+ /* Not a 64 bit cond. Give 32 bit a chance. */
+ break;
+
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a logical instruction condition. */
+ case 'L':
+ case 'l':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ s++;
+
+ /* 64 bit conditions. */
+ if (*args == 'L')
+ {
+ if (*s == '*')
+ s++;
+ else
+ break;
+ }
+ else if (*s == '*')
+ break;
+
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcmp (name, "<=") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 7;
+ else if (strcasecmp (name, "tr") == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ }
+ else if (strcmp (name, "<>") == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ }
+ else if (strcmp (name, ">=") == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ }
+ else if (strcmp (name, ">") == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ }
+ else if (strcasecmp (name, "ev") == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ }
+ /* ",*" is a valid condition. */
+ else if (*args != 'L' || *name)
+ as_bad (_("Invalid Logical Instruction Condition."));
+ *s = c;
+ }
+ /* 32-bit is default for no condition. */
+ else if (*args == 'L')
+ break;
+
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ /* Handle a shift/extract/deposit condition. */
+ case 'X':
+ case 'x':
+ case 'y':
+ cmpltr = 0;
+ /* Check immediate values in shift/extract/deposit
+ * instructions if they will give undefined behaviour. */
+ immediate_check = 1;
+ if (*s == ',')
+ {
+ save_s = s++;
+
+ /* 64 bit conditions. */
+ if (*args == 'X')
+ {
+ if (*s == '*')
+ s++;
+ else
+ break;
+ }
+ else if (*s == '*')
+ break;
+
+ name = s;
+ while (*s != ',' && *s != ' ' && *s != '\t')
+ s += 1;
+ c = *s;
+ *s = 0x00;
+ if (strcmp (name, "=") == 0)
+ cmpltr = 1;
+ else if (strcmp (name, "<") == 0)
+ cmpltr = 2;
+ else if (strcasecmp (name, "od") == 0)
+ cmpltr = 3;
+ else if (strcasecmp (name, "tr") == 0)
+ cmpltr = 4;
+ else if (strcmp (name, "<>") == 0)
+ cmpltr = 5;
+ else if (strcmp (name, ">=") == 0)
+ cmpltr = 6;
+ else if (strcasecmp (name, "ev") == 0)
+ cmpltr = 7;
+ /* Handle movb,n. Put things back the way they were.
+ This includes moving s back to where it started. */
+ else if (strcasecmp (name, "n") == 0 && *args == 'y')
+ {
+ *s = c;
+ s = save_s;
+ continue;
+ }
+ /* ",*" is a valid condition. */
+ else if (*args != 'X' || *name)
+ as_bad (_("Invalid Shift/Extract/Deposit Condition."));
+ *s = c;
+ }
+
+ INSERT_FIELD_AND_CONTINUE (opcode, cmpltr, 13);
+
+ /* Handle a unit instruction condition. */
+ case 'U':
+ case 'u':
+ cmpltr = 0;
+ flag = 0;
+ if (*s == ',')
+ {
+ int uxor;
+ s++;
+
+ /* 64 bit conditions. */
+ if (*args == 'U')
+ {
+ if (*s == '*')
+ s++;
+ else
+ break;
+ }
+ else if (*s == '*')
+ break;
+
+ /* The uxor instruction only supports unit conditions
+ not involving carries. */
+ uxor = (opcode & 0xfc000fc0) == 0x08000380;
+ if (strncasecmp (s, "sbz", 3) == 0)
+ {
+ cmpltr = 2;
+ s += 3;
+ }
+ else if (strncasecmp (s, "shz", 3) == 0)
+ {
+ cmpltr = 3;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "sdc", 3) == 0)
+ {
+ cmpltr = 4;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "sbc", 3) == 0)
+ {
+ cmpltr = 6;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "shc", 3) == 0)
+ {
+ cmpltr = 7;
+ s += 3;
+ }
+ else if (strncasecmp (s, "tr", 2) == 0)
+ {
+ cmpltr = 0;
+ flag = 1;
+ s += 2;
+ }
+ else if (strncasecmp (s, "nbz", 3) == 0)
+ {
+ cmpltr = 2;
+ flag = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, "nhz", 3) == 0)
+ {
+ cmpltr = 3;
+ flag = 1;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "ndc", 3) == 0)
+ {
+ cmpltr = 4;
+ flag = 1;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "nbc", 3) == 0)
+ {
+ cmpltr = 6;
+ flag = 1;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "nhc", 3) == 0)
+ {
+ cmpltr = 7;
+ flag = 1;
+ s += 3;
+ }
+ else if (strncasecmp (s, "swz", 3) == 0)
+ {
+ cmpltr = 1;
+ flag = 0;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "swc", 3) == 0)
+ {
+ cmpltr = 5;
+ flag = 0;
+ s += 3;
+ }
+ else if (strncasecmp (s, "nwz", 3) == 0)
+ {
+ cmpltr = 1;
+ flag = 1;
+ s += 3;
+ }
+ else if (!uxor && strncasecmp (s, "nwc", 3) == 0)
+ {
+ cmpltr = 5;
+ flag = 1;
+ s += 3;
+ }
+ /* ",*" is a valid condition. */
+ else if (*args != 'U' || (*s != ' ' && *s != '\t'))
+ as_bad (_("Invalid Unit Instruction Condition."));
+ }
+ /* 32-bit is default for no condition. */
+ else if (*args == 'U')
+ break;
+
+ opcode |= cmpltr << 13;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 12);
+
+ default:
+ abort ();
+ }
+ break;
+ }
+
+ /* Handle a nullification completer for branch instructions. */
+ case 'n':
+ nullif = pa_parse_nullif (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, nullif, 1);
+
+ /* Handle a nullification completer for copr and spop insns. */
+ case 'N':
+ nullif = pa_parse_nullif (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, nullif, 5);
+
+ /* Handle ,%r2 completer for new syntax branches. */
+ case 'L':
+ if (*s == ',' && strncasecmp (s + 1, "%r2", 3) == 0)
+ s += 4;
+ else if (*s == ',' && strncasecmp (s + 1, "%rp", 3) == 0)
+ s += 4;
+ else
+ break;
+ continue;
+
+ /* Handle 3 bit entry into the fp compare array. Valid values
+ are 0..6 inclusive. */
+ case 'h':
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 6, 0, 0);
+ num++;
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 13);
+ }
+ else
+ break;
+
+ /* Handle 3 bit entry into the fp compare array. Valid values
+ are 0..6 inclusive. */
+ case 'm':
+ get_expression (s);
+ if (the_insn.exp.X_op == O_constant)
+ {
+ s = expr_end;
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 6, 0, 0);
+ num = (num + 1) ^ 1;
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 13);
+ }
+ else
+ break;
+
+ /* Handle graphics test completers for ftest */
+ case '=':
+ {
+ num = pa_parse_ftest_gfx_completer (&s);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+
+ /* Handle a 11 bit immediate at 31. */
+ case 'i':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 1023, -1024, 0);
+ num = low_sign_unext (num, 11);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 11;
+ continue;
+ }
+
+ /* Handle a 14 bit immediate at 31. */
+ case 'J':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ int mb;
+
+ /* XXX the completer stored away tidbits of information
+ for us to extract. We need a cleaner way to do this.
+ Now that we have lots of letters again, it would be
+ good to rethink this. */
+ mb = opcode & 1;
+ opcode -= mb;
+ num = evaluate_absolute (&the_insn);
+ if (mb != (num < 0))
+ break;
+ CHECK_FIELD (num, 8191, -8192, 0);
+ num = low_sign_unext (num, 14);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ break;
+
+ /* Handle a 14 bit immediate at 31. */
+ case 'K':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ int mb;
+
+ mb = opcode & 1;
+ opcode -= mb;
+ num = evaluate_absolute (&the_insn);
+ if (mb == (num < 0))
+ break;
+ if (num % 4)
+ break;
+ CHECK_FIELD (num, 8191, -8192, 0);
+ num = low_sign_unext (num, 14);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ break;
+
+ /* Handle a 16 bit immediate at 31. */
+ case '<':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ int mb;
+
+ mb = opcode & 1;
+ opcode -= mb;
+ num = evaluate_absolute (&the_insn);
+ if (mb != (num < 0))
+ break;
+ CHECK_FIELD (num, 32767, -32768, 0);
+ num = re_assemble_16 (num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ break;
+
+ /* Handle a 16 bit immediate at 31. */
+ case '>':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ int mb;
+
+ mb = opcode & 1;
+ opcode -= mb;
+ num = evaluate_absolute (&the_insn);
+ if (mb == (num < 0))
+ break;
+ if (num % 4)
+ break;
+ CHECK_FIELD (num, 32767, -32768, 0);
+ num = re_assemble_16 (num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ break;
+
+ /* Handle 14 bit immediate, shifted left three times. */
+ case '#':
+ if (bfd_get_mach (stdoutput) != pa20)
+ break;
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ if (num & 0x7)
+ break;
+ CHECK_FIELD (num, 8191, -8192, 0);
+ if (num < 0)
+ opcode |= 1;
+ num &= 0x1fff;
+ num >>= 3;
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 4);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+ break;
+
+ /* Handle 14 bit immediate, shifted left twice. */
+ case 'd':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ if (num & 0x3)
+ break;
+ CHECK_FIELD (num, 8191, -8192, 0);
+ if (num < 0)
+ opcode |= 1;
+ num &= 0x1fff;
+ num >>= 2;
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 3);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+
+ /* Handle a 14 bit immediate at 31. */
+ case 'j':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 8191, -8192, 0);
+ num = low_sign_unext (num, 14);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+
+ /* Handle a 21 bit immediate at 31. */
+ case 'k':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num >> 11, 1048575, -1048576, 0);
+ opcode |= re_assemble_21 (num);
+ continue;
+ }
+ else
+ {
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 21;
+ continue;
+ }
+
+ /* Handle a 16 bit immediate at 31 (PA 2.0 wide mode only). */
+ case 'l':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 32767, -32768, 0);
+ opcode |= re_assemble_16 (num);
+ continue;
+ }
+ else
+ {
+ /* ??? Is this valid for wide mode? */
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+
+ /* Handle a word-aligned 16-bit imm. at 31 (PA2.0 wide). */
+ case 'y':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 32767, -32768, 0);
+ CHECK_ALIGN (num, 4, 0);
+ opcode |= re_assemble_16 (num);
+ continue;
+ }
+ else
+ {
+ /* ??? Is this valid for wide mode? */
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+
+ /* Handle a dword-aligned 16-bit imm. at 31 (PA2.0 wide). */
+ case '&':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ if (the_insn.exp.X_op == O_constant)
+ {
+ num = evaluate_absolute (&the_insn);
+ CHECK_FIELD (num, 32767, -32768, 0);
+ CHECK_ALIGN (num, 8, 0);
+ opcode |= re_assemble_16 (num);
+ continue;
+ }
+ else
+ {
+ /* ??? Is this valid for wide mode? */
+ if (is_DP_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_GOTOFF;
+ else if (is_PC_relative (the_insn.exp))
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+#ifdef OBJ_ELF
+ else if (is_tls_gdidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_GD21L;
+ else if (is_tls_ldidx (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDM21L;
+ else if (is_tls_dtpoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LDO21L;
+ else if (is_tls_ieoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_IE21L;
+ else if (is_tls_leoff (the_insn.exp))
+ the_insn.reloc = R_PARISC_TLS_LE21L;
+#endif
+ else
+ the_insn.reloc = R_HPPA;
+ the_insn.format = 14;
+ continue;
+ }
+
+ /* Handle a 12 bit branch displacement. */
+ case 'w':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 1;
+ if (!the_insn.exp.X_add_symbol
+ || !strcmp (S_GET_NAME (the_insn.exp.X_add_symbol),
+ FAKE_LABEL_NAME))
+ {
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ if (the_insn.exp.X_add_symbol)
+ num -= 8;
+ CHECK_FIELD (num, 8191, -8192, 0);
+ opcode |= re_assemble_12 (num >> 2);
+ continue;
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ the_insn.format = 12;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ s = expr_end;
+ continue;
+ }
+
+ /* Handle a 17 bit branch displacement. */
+ case 'W':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 1;
+ if (!the_insn.exp.X_add_symbol
+ || !strcmp (S_GET_NAME (the_insn.exp.X_add_symbol),
+ FAKE_LABEL_NAME))
+ {
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ if (the_insn.exp.X_add_symbol)
+ num -= 8;
+ CHECK_FIELD (num, 262143, -262144, 0);
+ opcode |= re_assemble_17 (num >> 2);
+ continue;
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ the_insn.format = 17;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ continue;
+ }
+
+ /* Handle a 22 bit branch displacement. */
+ case 'X':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 1;
+ if (!the_insn.exp.X_add_symbol
+ || !strcmp (S_GET_NAME (the_insn.exp.X_add_symbol),
+ FAKE_LABEL_NAME))
+ {
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ if (the_insn.exp.X_add_symbol)
+ num -= 8;
+ CHECK_FIELD (num, 8388607, -8388608, 0);
+ opcode |= re_assemble_22 (num >> 2);
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_PCREL_CALL;
+ the_insn.format = 22;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ continue;
+ }
+
+ /* Handle an absolute 17 bit branch target. */
+ case 'z':
+ the_insn.field_selector = pa_chk_field_selector (&s);
+ get_expression (s);
+ s = expr_end;
+ the_insn.pcrel = 0;
+ if (!the_insn.exp.X_add_symbol
+ || !strcmp (S_GET_NAME (the_insn.exp.X_add_symbol),
+ FAKE_LABEL_NAME))
+ {
+ num = evaluate_absolute (&the_insn);
+ if (num % 4)
+ {
+ as_bad (_("Branch to unaligned address"));
+ break;
+ }
+ if (the_insn.exp.X_add_symbol)
+ num -= 8;
+ CHECK_FIELD (num, 262143, -262144, 0);
+ opcode |= re_assemble_17 (num >> 2);
+ continue;
+ }
+ else
+ {
+ the_insn.reloc = R_HPPA_ABS_CALL;
+ the_insn.format = 17;
+ the_insn.arg_reloc = last_call_desc.arg_reloc;
+ memset (&last_call_desc, 0, sizeof (struct call_desc));
+ continue;
+ }
+
+ /* Handle '%r1' implicit operand of addil instruction. */
+ case 'Z':
+ if (*s == ',' && *(s + 1) == '%' && *(s + 3) == '1'
+ && (*(s + 2) == 'r' || *(s + 2) == 'R'))
+ {
+ s += 4;
+ continue;
+ }
+ else
+ break;
+
+ /* Handle '%sr0,%r31' implicit operand of be,l instruction. */
+ case 'Y':
+ if (strncasecmp (s, "%sr0,%r31", 9) != 0)
+ break;
+ s += 9;
+ continue;
+
+ /* Handle immediate value of 0 for ordered load/store instructions. */
+ case '@':
+ if (*s != '0')
+ break;
+ s++;
+ continue;
+
+ /* Handle a 2 bit shift count at 25. */
+ case '.':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 3, 1, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+
+ /* Handle a 4 bit shift count at 25. */
+ case '*':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 15, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+
+ /* Handle a 5 bit shift count at 26. */
+ case 'p':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, strict);
+ SAVE_IMMEDIATE(num);
+ INSERT_FIELD_AND_CONTINUE (opcode, 31 - num, 5);
+
+ /* Handle a 6 bit shift count at 20,22:26. */
+ case '~':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 63, 0, strict);
+ SAVE_IMMEDIATE(num);
+ num = 63 - num;
+ opcode |= (num & 0x20) << 6;
+ INSERT_FIELD_AND_CONTINUE (opcode, num & 0x1f, 5);
+
+ /* Handle a 6 bit field length at 23,27:31. */
+ case '%':
+ flag = 0;
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 64, 1, strict);
+ SAVE_IMMEDIATE(num);
+ num--;
+ opcode |= (num & 0x20) << 3;
+ num = 31 - (num & 0x1f);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 6 bit field length at 19,27:31. */
+ case '|':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 64, 1, strict);
+ SAVE_IMMEDIATE(num);
+ num--;
+ opcode |= (num & 0x20) << 7;
+ num = 31 - (num & 0x1f);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 5 bit bit position at 26. */
+ case 'P':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, strict);
+ SAVE_IMMEDIATE(num);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 5);
+
+ /* Handle a 6 bit bit position at 20,22:26. */
+ case 'q':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 63, 0, strict);
+ SAVE_IMMEDIATE(num);
+ opcode |= (num & 0x20) << 6;
+ INSERT_FIELD_AND_CONTINUE (opcode, num & 0x1f, 5);
+
+ /* Handle a 5 bit immediate at 10 with 'd' as the complement
+ of the high bit of the immediate. */
+ case 'B':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 63, 0, strict);
+ if (num & 0x20)
+ ;
+ else
+ opcode |= (1 << 13);
+ INSERT_FIELD_AND_CONTINUE (opcode, num & 0x1f, 21);
+
+ /* Handle a 5 bit immediate at 10. */
+ case 'Q':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 31, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 21);
+
+ /* Handle a 9 bit immediate at 28. */
+ case '$':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 511, 1, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 3);
+
+ /* Handle a 13 bit immediate at 18. */
+ case 'A':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 8191, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 13);
+
+ /* Handle a 26 bit immediate at 31. */
+ case 'D':
+ num = pa_get_absolute_expression (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 67108863, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 3 bit SFU identifier at 25. */
+ case 'v':
+ if (*s++ != ',')
+ as_bad (_("Invalid SFU identifier"));
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 7, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+
+ /* Handle a 20 bit SOP field for spop0. */
+ case 'O':
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 1048575, 0, strict);
+ num = (num & 0x1f) | ((num & 0x000fffe0) << 6);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 15bit SOP field for spop1. */
+ case 'o':
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 32767, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 11);
+
+ /* Handle a 10bit SOP field for spop3. */
+ case '0':
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 1023, 0, strict);
+ num = (num & 0x1f) | ((num & 0x000003e0) << 6);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 15 bit SOP field for spop2. */
+ case '1':
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 32767, 0, strict);
+ num = (num & 0x1f) | ((num & 0x00007fe0) << 6);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a 3-bit co-processor ID field. */
+ case 'u':
+ if (*s++ != ',')
+ as_bad (_("Invalid COPR identifier"));
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 7, 0, strict);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+
+ /* Handle a 22bit SOP field for copr. */
+ case '2':
+ num = pa_get_number (&the_insn, &s);
+ if (strict && the_insn.exp.X_op != O_constant)
+ break;
+ s = expr_end;
+ CHECK_FIELD (num, 4194303, 0, strict);
+ num = (num & 0x1f) | ((num & 0x003fffe0) << 4);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Handle a source FP operand format completer. */
+ case '{':
+ if (*s == ',' && *(s+1) == 't')
+ {
+ the_insn.trunc = 1;
+ s += 2;
+ }
+ else
+ the_insn.trunc = 0;
+ flag = pa_parse_fp_cnv_format (&s);
+ the_insn.fpof1 = flag;
+ if (flag == W || flag == UW)
+ flag = SGL;
+ if (flag == DW || flag == UDW)
+ flag = DBL;
+ if (flag == QW || flag == UQW)
+ flag = QUAD;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle a destination FP operand format completer. */
+ case '_':
+ /* pa_parse_format needs the ',' prefix. */
+ s--;
+ flag = pa_parse_fp_cnv_format (&s);
+ the_insn.fpof2 = flag;
+ if (flag == W || flag == UW)
+ flag = SGL;
+ if (flag == DW || flag == UDW)
+ flag = DBL;
+ if (flag == QW || flag == UQW)
+ flag = QUAD;
+ opcode |= flag << 13;
+ if (the_insn.fpof1 == SGL
+ || the_insn.fpof1 == DBL
+ || the_insn.fpof1 == QUAD)
+ {
+ if (the_insn.fpof2 == SGL
+ || the_insn.fpof2 == DBL
+ || the_insn.fpof2 == QUAD)
+ flag = 0;
+ else if (the_insn.fpof2 == W
+ || the_insn.fpof2 == DW
+ || the_insn.fpof2 == QW)
+ flag = 2;
+ else if (the_insn.fpof2 == UW
+ || the_insn.fpof2 == UDW
+ || the_insn.fpof2 == UQW)
+ flag = 6;
+ else
+ abort ();
+ }
+ else if (the_insn.fpof1 == W
+ || the_insn.fpof1 == DW
+ || the_insn.fpof1 == QW)
+ {
+ if (the_insn.fpof2 == SGL
+ || the_insn.fpof2 == DBL
+ || the_insn.fpof2 == QUAD)
+ flag = 1;
+ else
+ abort ();
+ }
+ else if (the_insn.fpof1 == UW
+ || the_insn.fpof1 == UDW
+ || the_insn.fpof1 == UQW)
+ {
+ if (the_insn.fpof2 == SGL
+ || the_insn.fpof2 == DBL
+ || the_insn.fpof2 == QUAD)
+ flag = 5;
+ else
+ abort ();
+ }
+ flag |= the_insn.trunc;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 15);
+
+ /* Handle a source FP operand format completer. */
+ case 'F':
+ flag = pa_parse_fp_format (&s);
+ the_insn.fpof1 = flag;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle a destination FP operand format completer. */
+ case 'G':
+ /* pa_parse_format needs the ',' prefix. */
+ s--;
+ flag = pa_parse_fp_format (&s);
+ the_insn.fpof2 = flag;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 13);
+
+ /* Handle a source FP operand format completer at 20. */
+ case 'I':
+ flag = pa_parse_fp_format (&s);
+ the_insn.fpof1 = flag;
+ INSERT_FIELD_AND_CONTINUE (opcode, flag, 11);
+
+ /* Handle a floating point operand format at 26.
+ Only allows single and double precision. */
+ case 'H':
+ flag = pa_parse_fp_format (&s);
+ switch (flag)
+ {
+ case SGL:
+ opcode |= 0x20;
+ case DBL:
+ the_insn.fpof1 = flag;
+ continue;
+
+ case QUAD:
+ case ILLEGAL_FMT:
+ default:
+ as_bad (_("Invalid Floating Point Operand Format."));
+ }
+ break;
+
+ /* Handle all floating point registers. */
+ case 'f':
+ switch (*++args)
+ {
+ /* Float target register. */
+ case 't':
+ if (!pa_parse_number (&s, 3))
+ break;
+ /* RSEL should not be set. */
+ if (pa_number & FP_REG_RSEL)
+ break;
+ num = pa_number - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+
+ /* Float target register with L/R selection. */
+ case 'T':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num;
+
+ /* 0x30 opcodes are FP arithmetic operation opcodes
+ and need to be turned into 0x38 opcodes. This
+ is not necessary for loads/stores. */
+ if (need_pa11_opcode ()
+ && ((opcode & 0xfc000000) == 0x30000000))
+ opcode |= 1 << 27;
+
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 6 : 0);
+ continue;
+ }
+
+ /* Float operand 1. */
+ case 'a':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num << 21;
+ if (need_pa11_opcode ())
+ {
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 7 : 0);
+ opcode |= 1 << 27;
+ }
+ continue;
+ }
+
+ /* Float operand 1 with L/R selection. */
+ case 'X':
+ case 'A':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num << 21;
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 7 : 0);
+ continue;
+ }
+
+ /* Float operand 2. */
+ case 'b':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num << 16;
+ if (need_pa11_opcode ())
+ {
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 12 : 0);
+ opcode |= 1 << 27;
+ }
+ continue;
+ }
+
+ /* Float operand 2 with L/R selection. */
+ case 'B':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num << 16;
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 12 : 0);
+ continue;
+ }
+
+ /* Float operand 3 for fmpyfadd, fmpynfadd. */
+ case 'C':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= (num & 0x1c) << 11;
+ opcode |= (num & 0x03) << 9;
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 8 : 0);
+ continue;
+ }
+
+ /* Float mult operand 1 for fmpyadd, fmpysub */
+ case 'i':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (num < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 21);
+ }
+
+ /* Float mult operand 2 for fmpyadd, fmpysub */
+ case 'j':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (num < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+ }
+
+ /* Float mult target for fmpyadd, fmpysub */
+ case 'k':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (num < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 0);
+ }
+
+ /* Float add operand 1 for fmpyadd, fmpysub */
+ case 'l':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (num < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 6);
+ }
+
+ /* Float add target for fmpyadd, fmpysub */
+ case 'm':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ if (the_insn.fpof1 == SGL)
+ {
+ if (num < 16)
+ {
+ as_bad (_("Invalid register for single precision fmpyadd or fmpysub"));
+ break;
+ }
+ num &= 0xF;
+ num |= (pa_number & FP_REG_RSEL ? 1 << 4 : 0);
+ }
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 11);
+ }
+
+ /* Handle L/R register halves like 'x'. */
+ case 'E':
+ case 'e':
+ {
+ if (!pa_parse_number (&s, 1))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ opcode |= num << 16;
+ if (need_pa11_opcode ())
+ {
+ opcode |= (pa_number & FP_REG_RSEL ? 1 << 1 : 0);
+ }
+ continue;
+ }
+
+ /* Float target register (PA 2.0 wide). */
+ case 'x':
+ if (!pa_parse_number (&s, 3))
+ break;
+ num = (pa_number & ~FP_REG_RSEL) - FP_REG_BASE;
+ CHECK_FIELD (num, 31, 0, 0);
+ INSERT_FIELD_AND_CONTINUE (opcode, num, 16);
+
+ default:
+ abort ();
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+ }
+
+ /* If this instruction is specific to a particular architecture,
+ then set a new architecture. This automatic promotion crud is
+ for compatibility with HP's old assemblers only. */
+ if (match == TRUE
+ && bfd_get_mach (stdoutput) < insn->arch
+ && !bfd_set_arch_mach (stdoutput, bfd_arch_hppa, insn->arch))
+ {
+ as_warn (_("could not update architecture and machine"));
+ match = FALSE;
+ }
+
+ failed:
+ /* Check if the args matched. */
+ if (!match)
+ {
+ if (&insn[1] - pa_opcodes < (int) NUMOPCODES
+ && !strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = argstart;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Invalid operands %s"), error_message);
+ return;
+ }
+ }
+ break;
+ }
+
+ if (immediate_check)
+ {
+ if (pos != -1 && len != -1 && pos < len - 1)
+ as_warn (_("Immediates %d and %d will give undefined behavior."),
+ pos, len);
+ }
+
+ the_insn.opcode = opcode;
+}
+
+/* Assemble a single instruction storing it into a frag. */
+
+void
+md_assemble (char *str)
+{
+ char *to;
+
+ /* The had better be something to assemble. */
+ gas_assert (str);
+
+ /* If we are within a procedure definition, make sure we've
+ defined a label for the procedure; handle case where the
+ label was defined after the .PROC directive.
+
+ Note there's not need to diddle with the segment or fragment
+ for the label symbol in this case. We have already switched
+ into the new $CODE$ subspace at this point. */
+ if (within_procedure && last_call_info->start_symbol == NULL)
+ {
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ {
+ if (label_symbol->lss_label)
+ {
+ last_call_info->start_symbol = label_symbol->lss_label;
+ symbol_get_bfdsym (label_symbol->lss_label)->flags
+ |= BSF_FUNCTION;
+#ifdef OBJ_SOM
+ /* Also handle allocation of a fixup to hold the unwind
+ information when the label appears after the proc/procend. */
+ if (within_entry_exit)
+ {
+ char *where;
+ unsigned int u;
+
+ where = frag_more (0);
+ u = UNWIND_LOW32 (&last_call_info->ci_unwind.descriptor);
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, R_HPPA_ENTRY, e_fsel, 0, 0, u);
+ }
+#endif
+ }
+ else
+ as_bad (_("Missing function name for .PROC (corrupted label chain)"));
+ }
+ else
+ as_bad (_("Missing function name for .PROC"));
+ }
+
+ /* Assemble the instruction. Results are saved into "the_insn". */
+ pa_ip (str);
+
+ /* Get somewhere to put the assembled instruction. */
+ to = frag_more (4);
+
+ /* Output the opcode. */
+ md_number_to_chars (to, the_insn.opcode, 4);
+
+ /* If necessary output more stuff. */
+ if (the_insn.reloc != R_HPPA_NONE)
+ fix_new_hppa (frag_now, (to - frag_now->fr_literal), 4, NULL,
+ (offsetT) 0, &the_insn.exp, the_insn.pcrel,
+ the_insn.reloc, the_insn.field_selector,
+ the_insn.format, the_insn.arg_reloc, 0);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (4);
+#endif
+}
+
+#ifdef OBJ_SOM
+/* Handle an alignment directive. Special so that we can update the
+ alignment of the subspace if necessary. */
+static void
+pa_align (int bytes)
+{
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+
+ /* Let the generic gas code do most of the work. */
+ s_align_bytes (bytes);
+
+ /* If bytes is a power of 2, then update the current subspace's
+ alignment if necessary. */
+ if (exact_log2 (bytes) != -1)
+ record_alignment (current_subspace->ssd_seg, exact_log2 (bytes));
+}
+#endif
+
+/* Handle a .BLOCK type pseudo-op. */
+
+static void
+pa_block (int z ATTRIBUTE_UNUSED)
+{
+ unsigned int temp_size;
+
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ temp_size = get_absolute_expression ();
+
+ if (temp_size > 0x3FFFFFFF)
+ {
+ as_bad (_("Argument to .BLOCK/.BLOCKZ must be between 0 and 0x3fffffff"));
+ temp_size = 0;
+ }
+ else
+ {
+ /* Always fill with zeros, that's what the HP assembler does. */
+ char *p = frag_var (rs_fill, 1, 1, 0, NULL, temp_size, NULL);
+ *p = 0;
+ }
+
+ pa_undefine_label ();
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .begin_brtab and .end_brtab pseudo-op. */
+
+static void
+pa_brtab (int begin ATTRIBUTE_UNUSED)
+{
+
+#ifdef OBJ_SOM
+ /* The BRTAB relocations are only available in SOM (to denote
+ the beginning and end of branch tables). */
+ char *where = frag_more (0);
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, begin ? R_HPPA_BEGIN_BRTAB : R_HPPA_END_BRTAB,
+ e_fsel, 0, 0, 0);
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .begin_try and .end_try pseudo-op. */
+
+static void
+pa_try (int begin ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ expressionS exp;
+ char *where = frag_more (0);
+
+ if (! begin)
+ expression (&exp);
+
+ /* The TRY relocations are only available in SOM (to denote
+ the beginning and end of exception handling regions). */
+
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, begin ? NULL : &exp,
+ 0, begin ? R_HPPA_BEGIN_TRY : R_HPPA_END_TRY,
+ e_fsel, 0, 0, 0);
+#endif
+
+ demand_empty_rest_of_line ();
+}
+
+/* Do the dirty work of building a call descriptor which describes
+ where the caller placed arguments to a function call. */
+
+static void
+pa_call_args (struct call_desc *call_desc)
+{
+ char *name, c, *p;
+ unsigned int temp, arg_reloc;
+
+ while (!is_end_of_statement ())
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Process a source argument. */
+ if ((strncasecmp (name, "argw", 4) == 0))
+ {
+ temp = atoi (name + 4);
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_build_arg_reloc (name);
+ call_desc->arg_reloc |= pa_align_arg_reloc (temp, arg_reloc);
+ }
+ /* Process a return value. */
+ else if ((strncasecmp (name, "rtnval", 6) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_build_arg_reloc (name);
+ call_desc->arg_reloc |= (arg_reloc & 0x3);
+ }
+ else
+ {
+ as_bad (_("Invalid .CALL argument: %s"), name);
+ }
+ p = input_line_pointer;
+ *p = c;
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+}
+
+/* Handle a .CALL pseudo-op. This involves storing away information
+ about where arguments are to be found so the linker can detect
+ (and correct) argument location mismatches between caller and callee. */
+
+static void
+pa_call (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ pa_call_args (&last_call_desc);
+ demand_empty_rest_of_line ();
+}
+
+#ifdef OBJ_ELF
+/* Build an entry in the UNWIND subspace from the given function
+ attributes in CALL_INFO. This is not needed for SOM as using
+ R_ENTRY and R_EXIT relocations allow the linker to handle building
+ of the unwind spaces. */
+
+static void
+pa_build_unwind_subspace (struct call_info *call_info)
+{
+ asection *seg, *save_seg;
+ subsegT save_subseg;
+ unsigned int unwind;
+ int reloc;
+ char *name, *p;
+ symbolS *symbolP;
+
+ if ((bfd_get_section_flags (stdoutput, now_seg)
+ & (SEC_ALLOC | SEC_LOAD | SEC_READONLY))
+ != (SEC_ALLOC | SEC_LOAD | SEC_READONLY))
+ return;
+
+ if (call_info->start_symbol == NULL)
+ /* This can happen if there were errors earlier on in the assembly. */
+ return;
+
+ /* Replace the start symbol with a local symbol that will be reduced
+ to a section offset. This avoids problems with weak functions with
+ multiple definitions, etc. */
+ name = xmalloc (strlen ("L$\001start_")
+ + strlen (S_GET_NAME (call_info->start_symbol))
+ + 1);
+ strcpy (name, "L$\001start_");
+ strcat (name, S_GET_NAME (call_info->start_symbol));
+
+ /* If we have a .procend preceded by a .exit, then the symbol will have
+ already been defined. In that case, we don't want another unwind
+ entry. */
+ symbolP = symbol_find (name);
+ if (symbolP)
+ {
+ xfree (name);
+ return;
+ }
+ else
+ {
+ symbolP = symbol_new (name, now_seg,
+ S_GET_VALUE (call_info->start_symbol), frag_now);
+ gas_assert (symbolP);
+ S_CLEAR_EXTERNAL (symbolP);
+ symbol_table_insert (symbolP);
+ }
+
+ reloc = R_PARISC_SEGREL32;
+ save_seg = now_seg;
+ save_subseg = now_subseg;
+ /* Get into the right seg/subseg. This may involve creating
+ the seg the first time through. Make sure to have the
+ old seg/subseg so that we can reset things when we are done. */
+ seg = bfd_get_section_by_name (stdoutput, UNWIND_SECTION_NAME);
+ if (seg == ASEC_NULL)
+ {
+ seg = subseg_new (UNWIND_SECTION_NAME, 0);
+ bfd_set_section_flags (stdoutput, seg,
+ SEC_READONLY | SEC_HAS_CONTENTS
+ | SEC_LOAD | SEC_RELOC | SEC_ALLOC | SEC_DATA);
+ bfd_set_section_alignment (stdoutput, seg, 2);
+ }
+
+ subseg_set (seg, 0);
+
+ /* Get some space to hold relocation information for the unwind
+ descriptor. */
+ p = frag_more (16);
+
+ /* Relocation info. for start offset of the function. */
+ md_number_to_chars (p, 0, 4);
+ fix_new_hppa (frag_now, p - frag_now->fr_literal, 4,
+ symbolP, (offsetT) 0,
+ (expressionS *) NULL, 0, reloc,
+ e_fsel, 32, 0, 0);
+
+ /* Relocation info. for end offset of the function.
+
+ Because we allow reductions of 32bit relocations for ELF, this will be
+ reduced to section_sym + offset which avoids putting the temporary
+ symbol into the symbol table. It (should) end up giving the same
+ value as call_info->start_symbol + function size once the linker is
+ finished with its work. */
+ md_number_to_chars (p + 4, 0, 4);
+ fix_new_hppa (frag_now, p + 4 - frag_now->fr_literal, 4,
+ call_info->end_symbol, (offsetT) 0,
+ (expressionS *) NULL, 0, reloc,
+ e_fsel, 32, 0, 0);
+
+ /* Dump the descriptor. */
+ unwind = UNWIND_LOW32 (&call_info->ci_unwind.descriptor);
+ md_number_to_chars (p + 8, unwind, 4);
+
+ unwind = UNWIND_HIGH32 (&call_info->ci_unwind.descriptor);
+ md_number_to_chars (p + 12, unwind, 4);
+
+ /* Return back to the original segment/subsegment. */
+ subseg_set (save_seg, save_subseg);
+}
+#endif
+
+/* Process a .CALLINFO pseudo-op. This information is used later
+ to build unwind descriptors and maybe one day to support
+ .ENTER and .LEAVE. */
+
+static void
+pa_callinfo (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, c, *p;
+ int temp;
+
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ /* .CALLINFO must appear within a procedure definition. */
+ if (!within_procedure)
+ as_bad (_(".callinfo is not within a procedure definition"));
+
+ /* Mark the fact that we found the .CALLINFO for the
+ current procedure. */
+ callinfo_found = TRUE;
+
+ /* Iterate over the .CALLINFO arguments. */
+ while (!is_end_of_statement ())
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Frame size specification. */
+ if ((strncasecmp (name, "frame", 5) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ if ((temp & 0x3) != 0)
+ {
+ as_bad (_("FRAME parameter must be a multiple of 8: %d\n"), temp);
+ temp = 0;
+ }
+
+ /* callinfo is in bytes and unwind_desc is in 8 byte units. */
+ last_call_info->ci_unwind.descriptor.frame_size = temp / 8;
+
+ }
+ /* Entry register (GR, GR and SR) specifications. */
+ else if ((strncasecmp (name, "entry_gr", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ /* The HP assembler accepts 19 as the high bound for ENTRY_GR
+ even though %r19 is caller saved. I think this is a bug in
+ the HP assembler, and we are not going to emulate it. */
+ if (temp < 3 || temp > 18)
+ as_bad (_("Value for ENTRY_GR must be in the range 3..18\n"));
+ last_call_info->ci_unwind.descriptor.entry_gr = temp - 2;
+ }
+ else if ((strncasecmp (name, "entry_fr", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ /* Similarly the HP assembler takes 31 as the high bound even
+ though %fr21 is the last callee saved floating point register. */
+ if (temp < 12 || temp > 21)
+ as_bad (_("Value for ENTRY_FR must be in the range 12..21\n"));
+ last_call_info->ci_unwind.descriptor.entry_fr = temp - 11;
+ }
+ else if ((strncasecmp (name, "entry_sr", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = get_absolute_expression ();
+ if (temp != 3)
+ as_bad (_("Value for ENTRY_SR must be 3\n"));
+ }
+ /* Note whether or not this function performs any calls. */
+ else if ((strncasecmp (name, "calls", 5) == 0) ||
+ (strncasecmp (name, "caller", 6) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ }
+ else if ((strncasecmp (name, "no_calls", 8) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ }
+ /* Should RP be saved into the stack. */
+ else if ((strncasecmp (name, "save_rp", 7) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.save_rp = 1;
+ }
+ /* Likewise for SP. */
+ else if ((strncasecmp (name, "save_sp", 7) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.save_sp = 1;
+ }
+ /* Is this an unwindable procedure. If so mark it so
+ in the unwind descriptor. */
+ else if ((strncasecmp (name, "no_unwind", 9) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.cannot_unwind = 1;
+ }
+ /* Is this an interrupt routine. If so mark it in the
+ unwind descriptor. */
+ else if ((strncasecmp (name, "hpux_int", 7) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.hpux_interrupt_marker = 1;
+ }
+ /* Is this a millicode routine. "millicode" isn't in my
+ assembler manual, but my copy is old. The HP assembler
+ accepts it, and there's a place in the unwind descriptor
+ to drop the information, so we'll accept it too. */
+ else if ((strncasecmp (name, "millicode", 9) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ last_call_info->ci_unwind.descriptor.millicode = 1;
+ }
+ else
+ {
+ as_bad (_("Invalid .CALLINFO argument: %s"), name);
+ *input_line_pointer = c;
+ }
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+#if !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD)))
+/* Switch to the text space. Like s_text, but delete our
+ label when finished. */
+
+static void
+pa_text (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ current_space = is_defined_space ("$TEXT$");
+ current_subspace
+ = pa_subsegment_to_subspace (current_space->sd_seg, 0);
+#endif
+
+ s_text (0);
+ pa_undefine_label ();
+}
+
+/* Switch to the data space. As usual delete our label. */
+
+static void
+pa_data (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ current_space = is_defined_space ("$PRIVATE$");
+ current_subspace
+ = pa_subsegment_to_subspace (current_space->sd_seg, 0);
+#endif
+ s_data (0);
+ pa_undefine_label ();
+}
+
+/* This is different than the standard GAS s_comm(). On HP9000/800 machines,
+ the .comm pseudo-op has the following syntax:
+
+ <label> .comm <length>
+
+ where <label> is optional and is a symbol whose address will be the start of
+ a block of memory <length> bytes long. <length> must be an absolute
+ expression. <length> bytes will be allocated in the current space
+ and subspace.
+
+ Also note the label may not even be on the same line as the .comm.
+
+ This difference in syntax means the colon function will be called
+ on the symbol before we arrive in pa_comm. colon will set a number
+ of attributes of the symbol that need to be fixed here. In particular
+ the value, section pointer, fragment pointer, flags, etc. What
+ a pain.
+
+ This also makes error detection all but impossible. */
+
+static void
+pa_comm (int unused ATTRIBUTE_UNUSED)
+{
+ unsigned int size;
+ symbolS *symbol;
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ symbol = label_symbol->lss_label;
+ else
+ symbol = NULL;
+
+ SKIP_WHITESPACE ();
+ size = get_absolute_expression ();
+
+ if (symbol)
+ {
+ symbol_get_bfdsym (symbol)->flags |= BSF_OBJECT;
+ S_SET_VALUE (symbol, size);
+ S_SET_SEGMENT (symbol, bfd_com_section_ptr);
+ S_SET_EXTERNAL (symbol);
+
+ /* colon() has already set the frag to the current location in the
+ current subspace; we need to reset the fragment to the zero address
+ fragment. We also need to reset the segment pointer. */
+ symbol_set_frag (symbol, &zero_address_frag);
+ }
+ demand_empty_rest_of_line ();
+}
+#endif /* !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD))) */
+
+/* Process a .END pseudo-op. */
+
+static void
+pa_end (int unused ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+}
+
+/* Process a .ENTER pseudo-op. This is not supported. */
+
+static void
+pa_enter (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ as_bad (_("The .ENTER pseudo-op is not supported"));
+ demand_empty_rest_of_line ();
+}
+
+/* Process a .ENTRY pseudo-op. .ENTRY marks the beginning of the
+ procedure. */
+
+static void
+pa_entry (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ if (!within_procedure)
+ as_bad (_("Misplaced .entry. Ignored."));
+ else
+ {
+ if (!callinfo_found)
+ as_bad (_("Missing .callinfo."));
+ }
+ demand_empty_rest_of_line ();
+ within_entry_exit = TRUE;
+
+#ifdef OBJ_SOM
+ /* SOM defers building of unwind descriptors until the link phase.
+ The assembler is responsible for creating an R_ENTRY relocation
+ to mark the beginning of a region and hold the unwind bits, and
+ for creating an R_EXIT relocation to mark the end of the region.
+
+ FIXME. ELF should be using the same conventions! The problem
+ is an unwind requires too much relocation space. Hmmm. Maybe
+ if we split the unwind bits up between the relocations which
+ denote the entry and exit points. */
+ if (last_call_info->start_symbol != NULL)
+ {
+ char *where;
+ unsigned int u;
+
+ where = frag_more (0);
+ u = UNWIND_LOW32 (&last_call_info->ci_unwind.descriptor);
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, R_HPPA_ENTRY, e_fsel, 0, 0, u);
+ }
+#endif
+}
+
+/* Silly nonsense for pa_equ. The only half-sensible use for this is
+ being able to subtract two register symbols that specify a range of
+ registers, to get the size of the range. */
+static int fudge_reg_expressions;
+
+int
+hppa_force_reg_syms_absolute (expressionS *resultP,
+ operatorT op ATTRIBUTE_UNUSED,
+ expressionS *rightP)
+{
+ if (fudge_reg_expressions
+ && rightP->X_op == O_register
+ && resultP->X_op == O_register)
+ {
+ rightP->X_op = O_constant;
+ resultP->X_op = O_constant;
+ }
+ return 0; /* Continue normal expr handling. */
+}
+
+/* Handle a .EQU pseudo-op. */
+
+static void
+pa_equ (int reg)
+{
+ label_symbol_struct *label_symbol = pa_get_label ();
+ symbolS *symbol;
+
+ if (label_symbol)
+ {
+ symbol = label_symbol->lss_label;
+ if (reg)
+ {
+ strict = 1;
+ if (!pa_parse_number (&input_line_pointer, 0))
+ as_bad (_(".REG expression must be a register"));
+ S_SET_VALUE (symbol, pa_number);
+ S_SET_SEGMENT (symbol, reg_section);
+ }
+ else
+ {
+ expressionS exp;
+ segT seg;
+
+ fudge_reg_expressions = 1;
+ seg = expression (&exp);
+ fudge_reg_expressions = 0;
+ if (exp.X_op != O_constant
+ && exp.X_op != O_register)
+ {
+ if (exp.X_op != O_absent)
+ as_bad (_("bad or irreducible absolute expression; zero assumed"));
+ exp.X_add_number = 0;
+ seg = absolute_section;
+ }
+ S_SET_VALUE (symbol, (unsigned int) exp.X_add_number);
+ S_SET_SEGMENT (symbol, seg);
+ }
+ }
+ else
+ {
+ if (reg)
+ as_bad (_(".REG must use a label"));
+ else
+ as_bad (_(".EQU must use a label"));
+ }
+
+ pa_undefine_label ();
+ demand_empty_rest_of_line ();
+}
+
+#ifdef OBJ_ELF
+/* Mark the end of a function so that it's possible to compute
+ the size of the function in elf_hppa_final_processing. */
+
+static void
+hppa_elf_mark_end_of_function (void)
+{
+ /* ELF does not have EXIT relocations. All we do is create a
+ temporary symbol marking the end of the function. */
+ char *name;
+
+ if (last_call_info == NULL || last_call_info->start_symbol == NULL)
+ {
+ /* We have already warned about a missing label,
+ or other problems. */
+ return;
+ }
+
+ name = xmalloc (strlen ("L$\001end_")
+ + strlen (S_GET_NAME (last_call_info->start_symbol))
+ + 1);
+ if (name)
+ {
+ symbolS *symbolP;
+
+ strcpy (name, "L$\001end_");
+ strcat (name, S_GET_NAME (last_call_info->start_symbol));
+
+ /* If we have a .exit followed by a .procend, then the
+ symbol will have already been defined. */
+ symbolP = symbol_find (name);
+ if (symbolP)
+ {
+ /* The symbol has already been defined! This can
+ happen if we have a .exit followed by a .procend.
+
+ This is *not* an error. All we want to do is free
+ the memory we just allocated for the name and continue. */
+ xfree (name);
+ }
+ else
+ {
+ /* symbol value should be the offset of the
+ last instruction of the function */
+ symbolP = symbol_new (name, now_seg, (valueT) (frag_now_fix () - 4),
+ frag_now);
+
+ gas_assert (symbolP);
+ S_CLEAR_EXTERNAL (symbolP);
+ symbol_table_insert (symbolP);
+ }
+
+ if (symbolP)
+ last_call_info->end_symbol = symbolP;
+ else
+ as_bad (_("Symbol '%s' could not be created."), name);
+
+ }
+ else
+ as_bad (_("No memory for symbol name."));
+}
+#endif
+
+/* Helper function. Does processing for the end of a function. This
+ usually involves creating some relocations or building special
+ symbols to mark the end of the function. */
+
+static void
+process_exit (void)
+{
+ char *where;
+
+ where = frag_more (0);
+
+#ifdef OBJ_ELF
+ /* Mark the end of the function, stuff away the location of the frag
+ for the end of the function, and finally call pa_build_unwind_subspace
+ to add an entry in the unwind table. */
+ (void) where;
+ hppa_elf_mark_end_of_function ();
+ pa_build_unwind_subspace (last_call_info);
+#else
+ /* SOM defers building of unwind descriptors until the link phase.
+ The assembler is responsible for creating an R_ENTRY relocation
+ to mark the beginning of a region and hold the unwind bits, and
+ for creating an R_EXIT relocation to mark the end of the region.
+
+ FIXME. ELF should be using the same conventions! The problem
+ is an unwind requires too much relocation space. Hmmm. Maybe
+ if we split the unwind bits up between the relocations which
+ denote the entry and exit points. */
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0,
+ NULL, 0, R_HPPA_EXIT, e_fsel, 0, 0,
+ UNWIND_HIGH32 (&last_call_info->ci_unwind.descriptor));
+#endif
+}
+
+/* Process a .EXIT pseudo-op. */
+
+static void
+pa_exit (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ if (!within_procedure)
+ as_bad (_(".EXIT must appear within a procedure"));
+ else
+ {
+ if (!callinfo_found)
+ as_bad (_("Missing .callinfo"));
+ else
+ {
+ if (!within_entry_exit)
+ as_bad (_("No .ENTRY for this .EXIT"));
+ else
+ {
+ within_entry_exit = FALSE;
+ process_exit ();
+ }
+ }
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Helper function to process arguments to a .EXPORT pseudo-op. */
+
+static void
+pa_type_args (symbolS *symbolP, int is_export)
+{
+ char *name, c, *p;
+ unsigned int temp, arg_reloc;
+ pa_symbol_type type = SYMBOL_TYPE_UNKNOWN;
+ asymbol *bfdsym = symbol_get_bfdsym (symbolP);
+
+ if (strncasecmp (input_line_pointer, "absolute", 8) == 0)
+ {
+ input_line_pointer += 8;
+ bfdsym->flags &= ~BSF_FUNCTION;
+ S_SET_SEGMENT (symbolP, bfd_abs_section_ptr);
+ type = SYMBOL_TYPE_ABSOLUTE;
+ }
+ else if (strncasecmp (input_line_pointer, "code", 4) == 0)
+ {
+ input_line_pointer += 4;
+ /* IMPORTing/EXPORTing CODE types for functions is meaningless for SOM,
+ instead one should be IMPORTing/EXPORTing ENTRY types.
+
+ Complain if one tries to EXPORT a CODE type since that's never
+ done. Both GCC and HP C still try to IMPORT CODE types, so
+ silently fix them to be ENTRY types. */
+ if (S_IS_FUNCTION (symbolP))
+ {
+ if (is_export)
+ as_tsktsk (_("Using ENTRY rather than CODE in export directive for %s"),
+ S_GET_NAME (symbolP));
+
+ bfdsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_ENTRY;
+ }
+ else
+ {
+ bfdsym->flags &= ~BSF_FUNCTION;
+ type = SYMBOL_TYPE_CODE;
+ }
+ }
+ else if (strncasecmp (input_line_pointer, "data", 4) == 0)
+ {
+ input_line_pointer += 4;
+ bfdsym->flags &= ~BSF_FUNCTION;
+ bfdsym->flags |= BSF_OBJECT;
+ type = SYMBOL_TYPE_DATA;
+ }
+ else if ((strncasecmp (input_line_pointer, "entry", 5) == 0))
+ {
+ input_line_pointer += 5;
+ bfdsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_ENTRY;
+ }
+ else if (strncasecmp (input_line_pointer, "millicode", 9) == 0)
+ {
+ input_line_pointer += 9;
+ bfdsym->flags |= BSF_FUNCTION;
+#ifdef OBJ_ELF
+ {
+ elf_symbol_type *elfsym = (elf_symbol_type *) bfdsym;
+ elfsym->internal_elf_sym.st_info =
+ ELF_ST_INFO (ELF_ST_BIND (elfsym->internal_elf_sym.st_info),
+ STT_PARISC_MILLI);
+ }
+#endif
+ type = SYMBOL_TYPE_MILLICODE;
+ }
+ else if (strncasecmp (input_line_pointer, "plabel", 6) == 0)
+ {
+ input_line_pointer += 6;
+ bfdsym->flags &= ~BSF_FUNCTION;
+ type = SYMBOL_TYPE_PLABEL;
+ }
+ else if (strncasecmp (input_line_pointer, "pri_prog", 8) == 0)
+ {
+ input_line_pointer += 8;
+ bfdsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_PRI_PROG;
+ }
+ else if (strncasecmp (input_line_pointer, "sec_prog", 8) == 0)
+ {
+ input_line_pointer += 8;
+ bfdsym->flags |= BSF_FUNCTION;
+ type = SYMBOL_TYPE_SEC_PROG;
+ }
+
+ /* SOM requires much more information about symbol types
+ than BFD understands. This is how we get this information
+ to the SOM BFD backend. */
+#ifdef obj_set_symbol_type
+ obj_set_symbol_type (bfdsym, (int) type);
+#else
+ (void) type;
+#endif
+
+ /* Now that the type of the exported symbol has been handled,
+ handle any argument relocation information. */
+ while (!is_end_of_statement ())
+ {
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Argument sources. */
+ if ((strncasecmp (name, "argw", 4) == 0))
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = atoi (name + 4);
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_align_arg_reloc (temp, pa_build_arg_reloc (name));
+#if defined (OBJ_SOM) || defined (ELF_ARG_RELOC)
+ symbol_arg_reloc_info (symbolP) |= arg_reloc;
+#else
+ (void) arg_reloc;
+#endif
+ *input_line_pointer = c;
+ }
+ /* The return value. */
+ else if ((strncasecmp (name, "rtnval", 6)) == 0)
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ arg_reloc = pa_build_arg_reloc (name);
+#if defined (OBJ_SOM) || defined (ELF_ARG_RELOC)
+ symbol_arg_reloc_info (symbolP) |= arg_reloc;
+#else
+ (void) arg_reloc;
+#endif
+ *input_line_pointer = c;
+ }
+ /* Privilege level. */
+ else if ((strncasecmp (name, "priv_lev", 8)) == 0)
+ {
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ temp = atoi (input_line_pointer);
+#ifdef OBJ_SOM
+ ((obj_symbol_type *) bfdsym)->tc_data.ap.hppa_priv_level = temp;
+#endif
+ c = get_symbol_end ();
+ *input_line_pointer = c;
+ }
+ else
+ {
+ as_bad (_("Undefined .EXPORT/.IMPORT argument (ignored): %s"), name);
+ p = input_line_pointer;
+ *p = c;
+ }
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+}
+
+/* Process a .EXPORT directive. This makes functions external
+ and provides information such as argument relocation entries
+ to callers. */
+
+static void
+pa_export (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, c, *p;
+ symbolS *symbol;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Make sure the given symbol exists. */
+ if ((symbol = symbol_find_or_make (name)) == NULL)
+ {
+ as_bad (_("Cannot define export symbol: %s\n"), name);
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ }
+ else
+ {
+ /* OK. Set the external bits and process argument relocations.
+ For the HP, weak and global are not mutually exclusive.
+ S_SET_EXTERNAL will not set BSF_GLOBAL if WEAK is set.
+ Call S_SET_EXTERNAL to get the other processing. Manually
+ set BSF_GLOBAL when we get back. */
+ S_SET_EXTERNAL (symbol);
+ symbol_get_bfdsym (symbol)->flags |= BSF_GLOBAL;
+ p = input_line_pointer;
+ *p = c;
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ pa_type_args (symbol, 1);
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle an .IMPORT pseudo-op. Any symbol referenced in a given
+ assembly file must either be defined in the assembly file, or
+ explicitly IMPORTED from another. */
+
+static void
+pa_import (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, c, *p;
+ symbolS *symbol;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ symbol = symbol_find (name);
+ /* Ugh. We might be importing a symbol defined earlier in the file,
+ in which case all the code below will really screw things up
+ (set the wrong segment, symbol flags & type, etc). */
+ if (symbol == NULL || !S_IS_DEFINED (symbol))
+ {
+ symbol = symbol_find_or_make (name);
+ p = input_line_pointer;
+ *p = c;
+
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ pa_type_args (symbol, 0);
+ }
+ else
+ {
+ /* Sigh. To be compatible with the HP assembler and to help
+ poorly written assembly code, we assign a type based on
+ the current segment. Note only BSF_FUNCTION really
+ matters, we do not need to set the full SYMBOL_TYPE_* info. */
+ if (now_seg == text_section)
+ symbol_get_bfdsym (symbol)->flags |= BSF_FUNCTION;
+
+ /* If the section is undefined, then the symbol is undefined
+ Since this is an import, leave the section undefined. */
+ S_SET_SEGMENT (symbol, bfd_und_section_ptr);
+ }
+ }
+ else
+ {
+ /* The symbol was already defined. Just eat everything up to
+ the end of the current statement. */
+ while (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .LABEL pseudo-op. */
+
+static void
+pa_label (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, c, *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strlen (name) > 0)
+ {
+ colon (name);
+ p = input_line_pointer;
+ *p = c;
+ }
+ else
+ {
+ as_warn (_("Missing label name on .LABEL"));
+ }
+
+ if (!is_end_of_statement ())
+ {
+ as_warn (_("extra .LABEL arguments ignored."));
+ ignore_rest_of_line ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .LEAVE pseudo-op. This is not supported yet. */
+
+static void
+pa_leave (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ as_bad (_("The .LEAVE pseudo-op is not supported"));
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .LEVEL pseudo-op. */
+
+static void
+pa_level (int unused ATTRIBUTE_UNUSED)
+{
+ char *level;
+
+ level = input_line_pointer;
+ if (strncmp (level, "1.0", 3) == 0)
+ {
+ input_line_pointer += 3;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 10))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else if (strncmp (level, "1.1", 3) == 0)
+ {
+ input_line_pointer += 3;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 11))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else if (strncmp (level, "2.0w", 4) == 0)
+ {
+ input_line_pointer += 4;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 25))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else if (strncmp (level, "2.0", 3) == 0)
+ {
+ input_line_pointer += 3;
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, 20))
+ as_warn (_("could not set architecture and machine"));
+ }
+ else
+ {
+ as_bad (_("Unrecognized .LEVEL argument\n"));
+ ignore_rest_of_line ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .ORIGIN pseudo-op. */
+
+static void
+pa_origin (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ s_org (0);
+ pa_undefine_label ();
+}
+
+/* Handle a .PARAM pseudo-op. This is much like a .EXPORT, except it
+ is for static functions. FIXME. Should share more code with .EXPORT. */
+
+static void
+pa_param (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, c, *p;
+ symbolS *symbol;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if ((symbol = symbol_find_or_make (name)) == NULL)
+ {
+ as_bad (_("Cannot define static symbol: %s\n"), name);
+ p = input_line_pointer;
+ *p = c;
+ input_line_pointer++;
+ }
+ else
+ {
+ S_CLEAR_EXTERNAL (symbol);
+ p = input_line_pointer;
+ *p = c;
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ pa_type_args (symbol, 0);
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .PROC pseudo-op. It is used to mark the beginning
+ of a procedure from a syntactical point of view. */
+
+static void
+pa_proc (int unused ATTRIBUTE_UNUSED)
+{
+ struct call_info *call_info;
+
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ if (within_procedure)
+ as_fatal (_("Nested procedures"));
+
+ /* Reset global variables for new procedure. */
+ callinfo_found = FALSE;
+ within_procedure = TRUE;
+
+ /* Create another call_info structure. */
+ call_info = xmalloc (sizeof (struct call_info));
+
+ if (!call_info)
+ as_fatal (_("Cannot allocate unwind descriptor\n"));
+
+ memset (call_info, 0, sizeof (struct call_info));
+
+ call_info->ci_next = NULL;
+
+ if (call_info_root == NULL)
+ {
+ call_info_root = call_info;
+ last_call_info = call_info;
+ }
+ else
+ {
+ last_call_info->ci_next = call_info;
+ last_call_info = call_info;
+ }
+
+ /* set up defaults on call_info structure */
+
+ call_info->ci_unwind.descriptor.cannot_unwind = 0;
+ call_info->ci_unwind.descriptor.region_desc = 1;
+ call_info->ci_unwind.descriptor.hpux_interrupt_marker = 0;
+
+ /* If we got a .PROC pseudo-op, we know that the function is defined
+ locally. Make sure it gets into the symbol table. */
+ {
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ {
+ if (label_symbol->lss_label)
+ {
+ last_call_info->start_symbol = label_symbol->lss_label;
+ symbol_get_bfdsym (label_symbol->lss_label)->flags |= BSF_FUNCTION;
+ }
+ else
+ as_bad (_("Missing function name for .PROC (corrupted label chain)"));
+ }
+ else
+ last_call_info->start_symbol = NULL;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Process the syntactical end of a procedure. Make sure all the
+ appropriate pseudo-ops were found within the procedure. */
+
+static void
+pa_procend (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ /* If we are within a procedure definition, make sure we've
+ defined a label for the procedure; handle case where the
+ label was defined after the .PROC directive.
+
+ Note there's not need to diddle with the segment or fragment
+ for the label symbol in this case. We have already switched
+ into the new $CODE$ subspace at this point. */
+ if (within_procedure && last_call_info->start_symbol == NULL)
+ {
+ label_symbol_struct *label_symbol = pa_get_label ();
+
+ if (label_symbol)
+ {
+ if (label_symbol->lss_label)
+ {
+ last_call_info->start_symbol = label_symbol->lss_label;
+ symbol_get_bfdsym (label_symbol->lss_label)->flags
+ |= BSF_FUNCTION;
+#ifdef OBJ_SOM
+ /* Also handle allocation of a fixup to hold the unwind
+ information when the label appears after the proc/procend. */
+ if (within_entry_exit)
+ {
+ char *where;
+ unsigned int u;
+
+ where = frag_more (0);
+ u = UNWIND_LOW32 (&last_call_info->ci_unwind.descriptor);
+ fix_new_hppa (frag_now, where - frag_now->fr_literal, 0,
+ NULL, (offsetT) 0, NULL,
+ 0, R_HPPA_ENTRY, e_fsel, 0, 0, u);
+ }
+#endif
+ }
+ else
+ as_bad (_("Missing function name for .PROC (corrupted label chain)"));
+ }
+ else
+ as_bad (_("Missing function name for .PROC"));
+ }
+
+ if (!within_procedure)
+ as_bad (_("misplaced .procend"));
+
+ if (!callinfo_found)
+ as_bad (_("Missing .callinfo for this procedure"));
+
+ if (within_entry_exit)
+ as_bad (_("Missing .EXIT for a .ENTRY"));
+
+#ifdef OBJ_ELF
+ /* ELF needs to mark the end of each function so that it can compute
+ the size of the function (apparently its needed in the symbol table). */
+ hppa_elf_mark_end_of_function ();
+#endif
+
+ within_procedure = FALSE;
+ demand_empty_rest_of_line ();
+ pa_undefine_label ();
+}
+
+#ifdef OBJ_SOM
+/* If VALUE is an exact power of two between zero and 2^31, then
+ return log2 (VALUE). Else return -1. */
+
+static int
+exact_log2 (int value)
+{
+ int shift = 0;
+
+ while ((1 << shift) != value && shift < 32)
+ shift++;
+
+ if (shift >= 32)
+ return -1;
+ else
+ return shift;
+}
+
+/* Check to make sure we have a valid space and subspace. */
+
+static void
+pa_check_current_space_and_subspace (void)
+{
+ if (current_space == NULL)
+ as_fatal (_("Not in a space.\n"));
+
+ if (current_subspace == NULL)
+ as_fatal (_("Not in a subspace.\n"));
+}
+
+/* Parse the parameters to a .SPACE directive; if CREATE_FLAG is nonzero,
+ then create a new space entry to hold the information specified
+ by the parameters to the .SPACE directive. */
+
+static sd_chain_struct *
+pa_parse_space_stmt (char *space_name, int create_flag)
+{
+ char *name, *ptemp, c;
+ char loadable, defined, private, sort;
+ int spnum;
+ asection *seg = NULL;
+ sd_chain_struct *space;
+
+ /* Load default values. */
+ spnum = 0;
+ sort = 0;
+ loadable = TRUE;
+ defined = TRUE;
+ private = FALSE;
+ if (strcmp (space_name, "$TEXT$") == 0)
+ {
+ seg = pa_def_spaces[0].segment;
+ defined = pa_def_spaces[0].defined;
+ private = pa_def_spaces[0].private;
+ sort = pa_def_spaces[0].sort;
+ spnum = pa_def_spaces[0].spnum;
+ }
+ else if (strcmp (space_name, "$PRIVATE$") == 0)
+ {
+ seg = pa_def_spaces[1].segment;
+ defined = pa_def_spaces[1].defined;
+ private = pa_def_spaces[1].private;
+ sort = pa_def_spaces[1].sort;
+ spnum = pa_def_spaces[1].spnum;
+ }
+
+ if (!is_end_of_statement ())
+ {
+ print_errors = FALSE;
+ ptemp = input_line_pointer + 1;
+ /* First see if the space was specified as a number rather than
+ as a name. According to the PA assembly manual the rest of
+ the line should be ignored. */
+ strict = 0;
+ pa_parse_number (&ptemp, 0);
+ if (pa_number >= 0)
+ {
+ spnum = pa_number;
+ input_line_pointer = ptemp;
+ }
+ else
+ {
+ while (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if ((strncasecmp (name, "spnum", 5) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ spnum = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "sort", 4) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ sort = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "unloadable", 10) == 0))
+ {
+ *input_line_pointer = c;
+ loadable = FALSE;
+ }
+ else if ((strncasecmp (name, "notdefined", 10) == 0))
+ {
+ *input_line_pointer = c;
+ defined = FALSE;
+ }
+ else if ((strncasecmp (name, "private", 7) == 0))
+ {
+ *input_line_pointer = c;
+ private = TRUE;
+ }
+ else
+ {
+ as_bad (_("Invalid .SPACE argument"));
+ *input_line_pointer = c;
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+ }
+ }
+ print_errors = TRUE;
+ }
+
+ if (create_flag && seg == NULL)
+ seg = subseg_new (space_name, 0);
+
+ /* If create_flag is nonzero, then create the new space with
+ the attributes computed above. Else set the values in
+ an already existing space -- this can only happen for
+ the first occurrence of a built-in space. */
+ if (create_flag)
+ space = create_new_space (space_name, spnum, loadable, defined,
+ private, sort, seg, 1);
+ else
+ {
+ space = is_defined_space (space_name);
+ SPACE_SPNUM (space) = spnum;
+ SPACE_DEFINED (space) = defined & 1;
+ SPACE_USER_DEFINED (space) = 1;
+ }
+
+#ifdef obj_set_section_attributes
+ obj_set_section_attributes (seg, defined, private, sort, spnum);
+#endif
+
+ return space;
+}
+
+/* Handle a .SPACE pseudo-op; this switches the current space to the
+ given space, creating the new space if necessary. */
+
+static void
+pa_space (int unused ATTRIBUTE_UNUSED)
+{
+ char *name, c, *space_name, *save_s;
+ sd_chain_struct *sd_chain;
+
+ if (within_procedure)
+ {
+ as_bad (_("Can\'t change spaces within a procedure definition. Ignored"));
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ /* Check for some of the predefined spaces. FIXME: most of the code
+ below is repeated several times, can we extract the common parts
+ and place them into a subroutine or something similar? */
+ /* FIXME Is this (and the next IF stmt) really right?
+ What if INPUT_LINE_POINTER points to "$TEXT$FOO"? */
+ if (strncmp (input_line_pointer, "$TEXT$", 6) == 0)
+ {
+ input_line_pointer += 6;
+ sd_chain = is_defined_space ("$TEXT$");
+ if (sd_chain == NULL)
+ sd_chain = pa_parse_space_stmt ("$TEXT$", 1);
+ else if (SPACE_USER_DEFINED (sd_chain) == 0)
+ sd_chain = pa_parse_space_stmt ("$TEXT$", 0);
+
+ current_space = sd_chain;
+ subseg_set (text_section, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (text_section,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (strncmp (input_line_pointer, "$PRIVATE$", 9) == 0)
+ {
+ input_line_pointer += 9;
+ sd_chain = is_defined_space ("$PRIVATE$");
+ if (sd_chain == NULL)
+ sd_chain = pa_parse_space_stmt ("$PRIVATE$", 1);
+ else if (SPACE_USER_DEFINED (sd_chain) == 0)
+ sd_chain = pa_parse_space_stmt ("$PRIVATE$", 0);
+
+ current_space = sd_chain;
+ subseg_set (data_section, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (data_section,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (!strncasecmp (input_line_pointer,
+ GDB_DEBUG_SPACE_NAME,
+ strlen (GDB_DEBUG_SPACE_NAME)))
+ {
+ input_line_pointer += strlen (GDB_DEBUG_SPACE_NAME);
+ sd_chain = is_defined_space (GDB_DEBUG_SPACE_NAME);
+ if (sd_chain == NULL)
+ sd_chain = pa_parse_space_stmt (GDB_DEBUG_SPACE_NAME, 1);
+ else if (SPACE_USER_DEFINED (sd_chain) == 0)
+ sd_chain = pa_parse_space_stmt (GDB_DEBUG_SPACE_NAME, 0);
+
+ current_space = sd_chain;
+
+ {
+ asection *gdb_section
+ = bfd_make_section_old_way (stdoutput, GDB_DEBUG_SPACE_NAME);
+
+ subseg_set (gdb_section, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (gdb_section,
+ sd_chain->sd_last_subseg);
+ }
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ /* It could be a space specified by number. */
+ print_errors = 0;
+ save_s = input_line_pointer;
+ strict = 0;
+ pa_parse_number (&input_line_pointer, 0);
+ if (pa_number >= 0)
+ {
+ if ((sd_chain = pa_find_space_by_number (pa_number)))
+ {
+ current_space = sd_chain;
+
+ subseg_set (sd_chain->sd_seg, sd_chain->sd_last_subseg);
+ current_subspace
+ = pa_subsegment_to_subspace (sd_chain->sd_seg,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ return;
+ }
+ }
+
+ /* Not a number, attempt to create a new space. */
+ print_errors = 1;
+ input_line_pointer = save_s;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ space_name = xmalloc (strlen (name) + 1);
+ strcpy (space_name, name);
+ *input_line_pointer = c;
+
+ sd_chain = pa_parse_space_stmt (space_name, 1);
+ current_space = sd_chain;
+
+ subseg_set (sd_chain->sd_seg, sd_chain->sd_last_subseg);
+ current_subspace = pa_subsegment_to_subspace (sd_chain->sd_seg,
+ sd_chain->sd_last_subseg);
+ demand_empty_rest_of_line ();
+ }
+}
+
+/* Switch to a new space. (I think). FIXME. */
+
+static void
+pa_spnum (int unused ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ sd_chain_struct *space;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ space = is_defined_space (name);
+ if (space)
+ {
+ p = frag_more (4);
+ md_number_to_chars (p, SPACE_SPNUM (space), 4);
+ }
+ else
+ as_warn (_("Undefined space: '%s' Assuming space number = 0."), name);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .SUBSPACE pseudo-op; this switches the current subspace to the
+ given subspace, creating the new subspace if necessary.
+
+ FIXME. Should mirror pa_space more closely, in particular how
+ they're broken up into subroutines. */
+
+static void
+pa_subspace (int create_new)
+{
+ char *name, *ss_name, c;
+ char loadable, code_only, comdat, common, dup_common, zero, sort;
+ int i, access_ctr, space_index, alignment, quadrant, applicable, flags;
+ sd_chain_struct *space;
+ ssd_chain_struct *ssd;
+ asection *section;
+
+ if (current_space == NULL)
+ as_fatal (_("Must be in a space before changing or declaring subspaces.\n"));
+
+ if (within_procedure)
+ {
+ as_bad (_("Can\'t change subspaces within a procedure definition. Ignored"));
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ ss_name = xmalloc (strlen (name) + 1);
+ strcpy (ss_name, name);
+ *input_line_pointer = c;
+
+ /* Load default values. */
+ sort = 0;
+ access_ctr = 0x7f;
+ loadable = 1;
+ comdat = 0;
+ common = 0;
+ dup_common = 0;
+ code_only = 0;
+ zero = 0;
+ space_index = ~0;
+ alignment = 1;
+ quadrant = 0;
+
+ space = current_space;
+ if (create_new)
+ ssd = NULL;
+ else
+ ssd = is_defined_subspace (ss_name);
+ /* Allow user to override the builtin attributes of subspaces. But
+ only allow the attributes to be changed once! */
+ if (ssd && SUBSPACE_DEFINED (ssd))
+ {
+ subseg_set (ssd->ssd_seg, ssd->ssd_subseg);
+ current_subspace = ssd;
+ if (!is_end_of_statement ())
+ as_warn (_("Parameters of an existing subspace can\'t be modified"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ else
+ {
+ /* A new subspace. Load default values if it matches one of
+ the builtin subspaces. */
+ i = 0;
+ while (pa_def_subspaces[i].name)
+ {
+ if (strcasecmp (pa_def_subspaces[i].name, ss_name) == 0)
+ {
+ loadable = pa_def_subspaces[i].loadable;
+ comdat = pa_def_subspaces[i].comdat;
+ common = pa_def_subspaces[i].common;
+ dup_common = pa_def_subspaces[i].dup_common;
+ code_only = pa_def_subspaces[i].code_only;
+ zero = pa_def_subspaces[i].zero;
+ space_index = pa_def_subspaces[i].space_index;
+ alignment = pa_def_subspaces[i].alignment;
+ quadrant = pa_def_subspaces[i].quadrant;
+ access_ctr = pa_def_subspaces[i].access;
+ sort = pa_def_subspaces[i].sort;
+ break;
+ }
+ i++;
+ }
+ }
+
+ /* We should be working with a new subspace now. Fill in
+ any information as specified by the user. */
+ if (!is_end_of_statement ())
+ {
+ input_line_pointer++;
+ while (!is_end_of_statement ())
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if ((strncasecmp (name, "quad", 4) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ quadrant = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "align", 5) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ alignment = get_absolute_expression ();
+ if (exact_log2 (alignment) == -1)
+ {
+ as_bad (_("Alignment must be a power of 2"));
+ alignment = 1;
+ }
+ }
+ else if ((strncasecmp (name, "access", 6) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ access_ctr = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "sort", 4) == 0))
+ {
+ *input_line_pointer = c;
+ input_line_pointer++;
+ sort = get_absolute_expression ();
+ }
+ else if ((strncasecmp (name, "code_only", 9) == 0))
+ {
+ *input_line_pointer = c;
+ code_only = 1;
+ }
+ else if ((strncasecmp (name, "unloadable", 10) == 0))
+ {
+ *input_line_pointer = c;
+ loadable = 0;
+ }
+ else if ((strncasecmp (name, "comdat", 6) == 0))
+ {
+ *input_line_pointer = c;
+ comdat = 1;
+ }
+ else if ((strncasecmp (name, "common", 6) == 0))
+ {
+ *input_line_pointer = c;
+ common = 1;
+ }
+ else if ((strncasecmp (name, "dup_comm", 8) == 0))
+ {
+ *input_line_pointer = c;
+ dup_common = 1;
+ }
+ else if ((strncasecmp (name, "zero", 4) == 0))
+ {
+ *input_line_pointer = c;
+ zero = 1;
+ }
+ else if ((strncasecmp (name, "first", 5) == 0))
+ as_bad (_("FIRST not supported as a .SUBSPACE argument"));
+ else
+ as_bad (_("Invalid .SUBSPACE argument"));
+ if (!is_end_of_statement ())
+ input_line_pointer++;
+ }
+ }
+
+ /* Compute a reasonable set of BFD flags based on the information
+ in the .subspace directive. */
+ applicable = bfd_applicable_section_flags (stdoutput);
+ flags = 0;
+ if (loadable)
+ flags |= (SEC_ALLOC | SEC_LOAD);
+ if (code_only)
+ flags |= SEC_CODE;
+
+ /* These flags are used to implement various flavors of initialized
+ common. The SOM linker discards duplicate subspaces when they
+ have the same "key" symbol name. This support is more like
+ GNU linkonce than BFD common. Further, pc-relative relocations
+ are converted to section relative relocations in BFD common
+ sections. This complicates the handling of relocations in
+ common sections containing text and isn't currently supported
+ correctly in the SOM BFD backend. */
+ if (comdat || common || dup_common)
+ flags |= SEC_LINK_ONCE;
+
+ flags |= SEC_RELOC | SEC_HAS_CONTENTS;
+
+ /* This is a zero-filled subspace (eg BSS). */
+ if (zero)
+ flags &= ~(SEC_LOAD | SEC_HAS_CONTENTS);
+
+ applicable &= flags;
+
+ /* If this is an existing subspace, then we want to use the
+ segment already associated with the subspace.
+
+ FIXME NOW! ELF BFD doesn't appear to be ready to deal with
+ lots of sections. It might be a problem in the PA ELF
+ code, I do not know yet. For now avoid creating anything
+ but the "standard" sections for ELF. */
+ if (create_new)
+ section = subseg_force_new (ss_name, 0);
+ else if (ssd)
+ section = ssd->ssd_seg;
+ else
+ section = subseg_new (ss_name, 0);
+
+ if (zero)
+ seg_info (section)->bss = 1;
+
+ /* Now set the flags. */
+ bfd_set_section_flags (stdoutput, section, applicable);
+
+ /* Record any alignment request for this section. */
+ record_alignment (section, exact_log2 (alignment));
+
+ /* Set the starting offset for this section. */
+ bfd_set_section_vma (stdoutput, section,
+ pa_subspace_start (space, quadrant));
+
+ /* Now that all the flags are set, update an existing subspace,
+ or create a new one. */
+ if (ssd)
+
+ current_subspace = update_subspace (space, ss_name, loadable,
+ code_only, comdat, common,
+ dup_common, sort, zero, access_ctr,
+ space_index, alignment, quadrant,
+ section);
+ else
+ current_subspace = create_new_subspace (space, ss_name, loadable,
+ code_only, comdat, common,
+ dup_common, zero, sort,
+ access_ctr, space_index,
+ alignment, quadrant, section);
+
+ demand_empty_rest_of_line ();
+ current_subspace->ssd_seg = section;
+ subseg_set (current_subspace->ssd_seg, current_subspace->ssd_subseg);
+ }
+ SUBSPACE_DEFINED (current_subspace) = 1;
+}
+
+/* Create default space and subspace dictionaries. */
+
+static void
+pa_spaces_begin (void)
+{
+ int i;
+
+ space_dict_root = NULL;
+ space_dict_last = NULL;
+
+ i = 0;
+ while (pa_def_spaces[i].name)
+ {
+ char *name;
+
+ /* Pick the right name to use for the new section. */
+ name = pa_def_spaces[i].name;
+
+ pa_def_spaces[i].segment = subseg_new (name, 0);
+ create_new_space (pa_def_spaces[i].name, pa_def_spaces[i].spnum,
+ pa_def_spaces[i].loadable, pa_def_spaces[i].defined,
+ pa_def_spaces[i].private, pa_def_spaces[i].sort,
+ pa_def_spaces[i].segment, 0);
+ i++;
+ }
+
+ i = 0;
+ while (pa_def_subspaces[i].name)
+ {
+ char *name;
+ int applicable, subsegment;
+ asection *segment = NULL;
+ sd_chain_struct *space;
+
+ /* Pick the right name for the new section and pick the right
+ subsegment number. */
+ name = pa_def_subspaces[i].name;
+ subsegment = 0;
+
+ /* Create the new section. */
+ segment = subseg_new (name, subsegment);
+
+ /* For SOM we want to replace the standard .text, .data, and .bss
+ sections with our own. We also want to set BFD flags for
+ all the built-in subspaces. */
+ if (!strcmp (pa_def_subspaces[i].name, "$CODE$"))
+ {
+ text_section = segment;
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC | SEC_CODE
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$DATA$"))
+ {
+ data_section = segment;
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_HAS_CONTENTS));
+
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$BSS$"))
+ {
+ bss_section = segment;
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & SEC_ALLOC);
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$LIT$"))
+ {
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$MILLICODE$"))
+ {
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+ else if (!strcmp (pa_def_subspaces[i].name, "$UNWIND$"))
+ {
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, segment,
+ applicable & (SEC_ALLOC | SEC_LOAD
+ | SEC_RELOC
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS));
+ }
+
+ /* Find the space associated with this subspace. */
+ space = pa_segment_to_space (pa_def_spaces[pa_def_subspaces[i].
+ def_space_index].segment);
+ if (space == NULL)
+ {
+ as_fatal (_("Internal error: Unable to find containing space for %s."),
+ pa_def_subspaces[i].name);
+ }
+
+ create_new_subspace (space, name,
+ pa_def_subspaces[i].loadable,
+ pa_def_subspaces[i].code_only,
+ pa_def_subspaces[i].comdat,
+ pa_def_subspaces[i].common,
+ pa_def_subspaces[i].dup_common,
+ pa_def_subspaces[i].zero,
+ pa_def_subspaces[i].sort,
+ pa_def_subspaces[i].access,
+ pa_def_subspaces[i].space_index,
+ pa_def_subspaces[i].alignment,
+ pa_def_subspaces[i].quadrant,
+ segment);
+ i++;
+ }
+}
+
+/* Create a new space NAME, with the appropriate flags as defined
+ by the given parameters. */
+
+static sd_chain_struct *
+create_new_space (char *name,
+ int spnum,
+ int loadable ATTRIBUTE_UNUSED,
+ int defined,
+ int private,
+ int sort,
+ asection *seg,
+ int user_defined)
+{
+ sd_chain_struct *chain_entry;
+
+ chain_entry = xmalloc (sizeof (sd_chain_struct));
+ if (!chain_entry)
+ as_fatal (_("Out of memory: could not allocate new space chain entry: %s\n"),
+ name);
+
+ SPACE_NAME (chain_entry) = xmalloc (strlen (name) + 1);
+ strcpy (SPACE_NAME (chain_entry), name);
+ SPACE_DEFINED (chain_entry) = defined;
+ SPACE_USER_DEFINED (chain_entry) = user_defined;
+ SPACE_SPNUM (chain_entry) = spnum;
+
+ chain_entry->sd_seg = seg;
+ chain_entry->sd_last_subseg = -1;
+ chain_entry->sd_subspaces = NULL;
+ chain_entry->sd_next = NULL;
+
+ /* Find spot for the new space based on its sort key. */
+ if (!space_dict_last)
+ space_dict_last = chain_entry;
+
+ if (space_dict_root == NULL)
+ space_dict_root = chain_entry;
+ else
+ {
+ sd_chain_struct *chain_pointer;
+ sd_chain_struct *prev_chain_pointer;
+
+ chain_pointer = space_dict_root;
+ prev_chain_pointer = NULL;
+
+ while (chain_pointer)
+ {
+ prev_chain_pointer = chain_pointer;
+ chain_pointer = chain_pointer->sd_next;
+ }
+
+ /* At this point we've found the correct place to add the new
+ entry. So add it and update the linked lists as appropriate. */
+ if (prev_chain_pointer)
+ {
+ chain_entry->sd_next = chain_pointer;
+ prev_chain_pointer->sd_next = chain_entry;
+ }
+ else
+ {
+ space_dict_root = chain_entry;
+ chain_entry->sd_next = chain_pointer;
+ }
+
+ if (chain_entry->sd_next == NULL)
+ space_dict_last = chain_entry;
+ }
+
+ /* This is here to catch predefined spaces which do not get
+ modified by the user's input. Another call is found at
+ the bottom of pa_parse_space_stmt to handle cases where
+ the user modifies a predefined space. */
+#ifdef obj_set_section_attributes
+ obj_set_section_attributes (seg, defined, private, sort, spnum);
+#endif
+
+ return chain_entry;
+}
+
+/* Create a new subspace NAME, with the appropriate flags as defined
+ by the given parameters.
+
+ Add the new subspace to the subspace dictionary chain in numerical
+ order as defined by the SORT entries. */
+
+static ssd_chain_struct *
+create_new_subspace (sd_chain_struct *space,
+ char *name,
+ int loadable ATTRIBUTE_UNUSED,
+ int code_only ATTRIBUTE_UNUSED,
+ int comdat,
+ int common,
+ int dup_common,
+ int is_zero ATTRIBUTE_UNUSED,
+ int sort,
+ int access_ctr,
+ int space_index ATTRIBUTE_UNUSED,
+ int alignment ATTRIBUTE_UNUSED,
+ int quadrant,
+ asection *seg)
+{
+ ssd_chain_struct *chain_entry;
+
+ chain_entry = xmalloc (sizeof (ssd_chain_struct));
+ if (!chain_entry)
+ as_fatal (_("Out of memory: could not allocate new subspace chain entry: %s\n"), name);
+
+ SUBSPACE_NAME (chain_entry) = xmalloc (strlen (name) + 1);
+ strcpy (SUBSPACE_NAME (chain_entry), name);
+
+ /* Initialize subspace_defined. When we hit a .subspace directive
+ we'll set it to 1 which "locks-in" the subspace attributes. */
+ SUBSPACE_DEFINED (chain_entry) = 0;
+
+ chain_entry->ssd_subseg = 0;
+ chain_entry->ssd_seg = seg;
+ chain_entry->ssd_next = NULL;
+
+ /* Find spot for the new subspace based on its sort key. */
+ if (space->sd_subspaces == NULL)
+ space->sd_subspaces = chain_entry;
+ else
+ {
+ ssd_chain_struct *chain_pointer;
+ ssd_chain_struct *prev_chain_pointer;
+
+ chain_pointer = space->sd_subspaces;
+ prev_chain_pointer = NULL;
+
+ while (chain_pointer)
+ {
+ prev_chain_pointer = chain_pointer;
+ chain_pointer = chain_pointer->ssd_next;
+ }
+
+ /* Now we have somewhere to put the new entry. Insert it and update
+ the links. */
+ if (prev_chain_pointer)
+ {
+ chain_entry->ssd_next = chain_pointer;
+ prev_chain_pointer->ssd_next = chain_entry;
+ }
+ else
+ {
+ space->sd_subspaces = chain_entry;
+ chain_entry->ssd_next = chain_pointer;
+ }
+ }
+
+#ifdef obj_set_subsection_attributes
+ obj_set_subsection_attributes (seg, space->sd_seg, access_ctr, sort,
+ quadrant, comdat, common, dup_common);
+#endif
+
+ return chain_entry;
+}
+
+/* Update the information for the given subspace based upon the
+ various arguments. Return the modified subspace chain entry. */
+
+static ssd_chain_struct *
+update_subspace (sd_chain_struct *space,
+ char *name,
+ int loadable ATTRIBUTE_UNUSED,
+ int code_only ATTRIBUTE_UNUSED,
+ int comdat,
+ int common,
+ int dup_common,
+ int sort,
+ int zero ATTRIBUTE_UNUSED,
+ int access_ctr,
+ int space_index ATTRIBUTE_UNUSED,
+ int alignment ATTRIBUTE_UNUSED,
+ int quadrant,
+ asection *section)
+{
+ ssd_chain_struct *chain_entry;
+
+ chain_entry = is_defined_subspace (name);
+
+#ifdef obj_set_subsection_attributes
+ obj_set_subsection_attributes (section, space->sd_seg, access_ctr, sort,
+ quadrant, comdat, common, dup_common);
+#endif
+
+ return chain_entry;
+}
+
+/* Return the space chain entry for the space with the name NAME or
+ NULL if no such space exists. */
+
+static sd_chain_struct *
+is_defined_space (char *name)
+{
+ sd_chain_struct *chain_pointer;
+
+ for (chain_pointer = space_dict_root;
+ chain_pointer;
+ chain_pointer = chain_pointer->sd_next)
+ if (strcmp (SPACE_NAME (chain_pointer), name) == 0)
+ return chain_pointer;
+
+ /* No mapping from segment to space was found. Return NULL. */
+ return NULL;
+}
+
+/* Find and return the space associated with the given seg. If no mapping
+ from the given seg to a space is found, then return NULL.
+
+ Unlike subspaces, the number of spaces is not expected to grow much,
+ so a linear exhaustive search is OK here. */
+
+static sd_chain_struct *
+pa_segment_to_space (asection *seg)
+{
+ sd_chain_struct *space_chain;
+
+ /* Walk through each space looking for the correct mapping. */
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ if (space_chain->sd_seg == seg)
+ return space_chain;
+
+ /* Mapping was not found. Return NULL. */
+ return NULL;
+}
+
+/* Return the first space chain entry for the subspace with the name
+ NAME or NULL if no such subspace exists.
+
+ When there are multiple subspaces with the same name, switching to
+ the first (i.e., default) subspace is preferable in most situations.
+ For example, it wouldn't be desirable to merge COMDAT data with non
+ COMDAT data.
+
+ Uses a linear search through all the spaces and subspaces, this may
+ not be appropriate if we ever being placing each function in its
+ own subspace. */
+
+static ssd_chain_struct *
+is_defined_subspace (char *name)
+{
+ sd_chain_struct *space_chain;
+ ssd_chain_struct *subspace_chain;
+
+ /* Walk through each space. */
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ /* Walk through each subspace looking for a name which matches. */
+ for (subspace_chain = space_chain->sd_subspaces;
+ subspace_chain;
+ subspace_chain = subspace_chain->ssd_next)
+ if (strcmp (SUBSPACE_NAME (subspace_chain), name) == 0)
+ return subspace_chain;
+ }
+
+ /* Subspace wasn't found. Return NULL. */
+ return NULL;
+}
+
+/* Find and return the subspace associated with the given seg. If no
+ mapping from the given seg to a subspace is found, then return NULL.
+
+ If we ever put each procedure/function within its own subspace
+ (to make life easier on the compiler and linker), then this will have
+ to become more efficient. */
+
+static ssd_chain_struct *
+pa_subsegment_to_subspace (asection *seg, subsegT subseg)
+{
+ sd_chain_struct *space_chain;
+ ssd_chain_struct *subspace_chain;
+
+ /* Walk through each space. */
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ if (space_chain->sd_seg == seg)
+ {
+ /* Walk through each subspace within each space looking for
+ the correct mapping. */
+ for (subspace_chain = space_chain->sd_subspaces;
+ subspace_chain;
+ subspace_chain = subspace_chain->ssd_next)
+ if (subspace_chain->ssd_subseg == (int) subseg)
+ return subspace_chain;
+ }
+ }
+
+ /* No mapping from subsegment to subspace found. Return NULL. */
+ return NULL;
+}
+
+/* Given a number, try and find a space with the name number.
+
+ Return a pointer to a space dictionary chain entry for the space
+ that was found or NULL on failure. */
+
+static sd_chain_struct *
+pa_find_space_by_number (int number)
+{
+ sd_chain_struct *space_chain;
+
+ for (space_chain = space_dict_root;
+ space_chain;
+ space_chain = space_chain->sd_next)
+ {
+ if (SPACE_SPNUM (space_chain) == (unsigned int) number)
+ return space_chain;
+ }
+
+ /* No appropriate space found. Return NULL. */
+ return NULL;
+}
+
+/* Return the starting address for the given subspace. If the starting
+ address is unknown then return zero. */
+
+static unsigned int
+pa_subspace_start (sd_chain_struct *space, int quadrant)
+{
+ /* FIXME. Assumes everyone puts read/write data at 0x4000000, this
+ is not correct for the PA OSF1 port. */
+ if ((strcmp (SPACE_NAME (space), "$PRIVATE$") == 0) && quadrant == 1)
+ return 0x40000000;
+ else if (space->sd_seg == data_section && quadrant == 1)
+ return 0x40000000;
+ else
+ return 0;
+ return 0;
+}
+#endif
+
+/* Helper function for pa_stringer. Used to find the end of
+ a string. */
+
+static unsigned int
+pa_stringer_aux (char *s)
+{
+ unsigned int c = *s & CHAR_MASK;
+
+ switch (c)
+ {
+ case '\"':
+ c = NOT_A_CHAR;
+ break;
+ default:
+ break;
+ }
+ return c;
+}
+
+/* Handle a .STRING type pseudo-op. */
+
+static void
+pa_stringer (int append_zero)
+{
+ char *s, num_buf[4];
+ unsigned int c;
+ int i;
+
+ /* Preprocess the string to handle PA-specific escape sequences.
+ For example, \xDD where DD is a hexadecimal number should be
+ changed to \OOO where OOO is an octal number. */
+
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ /* Skip the opening quote. */
+ s = input_line_pointer + 1;
+
+ while (is_a_char (c = pa_stringer_aux (s++)))
+ {
+ if (c == '\\')
+ {
+ c = *s;
+ switch (c)
+ {
+ /* Handle \x<num>. */
+ case 'x':
+ {
+ unsigned int number;
+ int num_digit;
+ char dg;
+ char *s_start = s;
+
+ /* Get past the 'x'. */
+ s++;
+ for (num_digit = 0, number = 0, dg = *s;
+ num_digit < 2
+ && (ISDIGIT (dg) || (dg >= 'a' && dg <= 'f')
+ || (dg >= 'A' && dg <= 'F'));
+ num_digit++)
+ {
+ if (ISDIGIT (dg))
+ number = number * 16 + dg - '0';
+ else if (dg >= 'a' && dg <= 'f')
+ number = number * 16 + dg - 'a' + 10;
+ else
+ number = number * 16 + dg - 'A' + 10;
+
+ s++;
+ dg = *s;
+ }
+ if (num_digit > 0)
+ {
+ switch (num_digit)
+ {
+ case 1:
+ sprintf (num_buf, "%02o", number);
+ break;
+ case 2:
+ sprintf (num_buf, "%03o", number);
+ break;
+ }
+ for (i = 0; i <= num_digit; i++)
+ s_start[i] = num_buf[i];
+ }
+ break;
+ }
+ /* This might be a "\"", skip over the escaped char. */
+ default:
+ s++;
+ break;
+ }
+ }
+ }
+ stringer (8 + append_zero);
+ pa_undefine_label ();
+}
+
+/* Handle a .VERSION pseudo-op. */
+
+static void
+pa_version (int unused ATTRIBUTE_UNUSED)
+{
+ obj_version (0);
+ pa_undefine_label ();
+}
+
+#ifdef OBJ_SOM
+
+/* Handle a .COMPILER pseudo-op. */
+
+static void
+pa_compiler (int unused ATTRIBUTE_UNUSED)
+{
+ obj_som_compiler (0);
+ pa_undefine_label ();
+}
+
+#endif
+
+/* Handle a .COPYRIGHT pseudo-op. */
+
+static void
+pa_copyright (int unused ATTRIBUTE_UNUSED)
+{
+ obj_copyright (0);
+ pa_undefine_label ();
+}
+
+/* Just like a normal cons, but when finished we have to undefine
+ the latest space label. */
+
+static void
+pa_cons (int nbytes)
+{
+ cons (nbytes);
+ pa_undefine_label ();
+}
+
+/* Like float_cons, but we need to undefine our label. */
+
+static void
+pa_float_cons (int float_type)
+{
+ float_cons (float_type);
+ pa_undefine_label ();
+}
+
+/* Like s_fill, but delete our label when finished. */
+
+static void
+pa_fill (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ s_fill (0);
+ pa_undefine_label ();
+}
+
+/* Like lcomm, but delete our label when finished. */
+
+static void
+pa_lcomm (int needs_align)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ s_lcomm (needs_align);
+ pa_undefine_label ();
+}
+
+/* Like lsym, but delete our label when finished. */
+
+static void
+pa_lsym (int unused ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_SOM
+ /* We must have a valid space and subspace. */
+ pa_check_current_space_and_subspace ();
+#endif
+
+ s_lsym (0);
+ pa_undefine_label ();
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will need. */
+
+void
+md_begin (void)
+{
+ const char *retval = NULL;
+ int lose = 0;
+ unsigned int i = 0;
+
+ last_call_info = NULL;
+ call_info_root = NULL;
+
+ /* Set the default machine type. */
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_hppa, DEFAULT_LEVEL))
+ as_warn (_("could not set architecture and machine"));
+
+ /* Folding of text and data segments fails miserably on the PA.
+ Warn user and disable "-R" option. */
+ if (flag_readonly_data_in_text)
+ {
+ as_warn (_("-R option not supported on this target."));
+ flag_readonly_data_in_text = 0;
+ }
+
+#ifdef OBJ_SOM
+ pa_spaces_begin ();
+#endif
+
+ op_hash = hash_new ();
+
+ while (i < NUMOPCODES)
+ {
+ const char *name = pa_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (struct pa_opcode *) &pa_opcodes[i]);
+ if (retval != NULL && *retval != '\0')
+ {
+ as_fatal (_("Internal error: can't hash `%s': %s\n"), name, retval);
+ lose = 1;
+ }
+
+ do
+ {
+ if ((pa_opcodes[i].match & pa_opcodes[i].mask)
+ != pa_opcodes[i].match)
+ {
+ fprintf (stderr, _("internal error: losing opcode: `%s' \"%s\"\n"),
+ pa_opcodes[i].name, pa_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ }
+ while (i < NUMOPCODES && !strcmp (pa_opcodes[i].name, name));
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+#ifdef OBJ_SOM
+ /* SOM will change text_section. To make sure we never put
+ anything into the old one switch to the new one now. */
+ subseg_set (text_section, 0);
+#endif
+
+#ifdef OBJ_SOM
+ dummy_symbol = symbol_find_or_make ("L$dummy");
+ S_SET_SEGMENT (dummy_symbol, text_section);
+ /* Force the symbol to be converted to a real symbol. */
+ symbol_get_bfdsym (dummy_symbol)->flags |= BSF_KEEP;
+#endif
+}
+
+/* On the PA relocations which involve function symbols must not be
+ adjusted. This so that the linker can know when/how to create argument
+ relocation stubs for indirect calls and calls to static functions.
+
+ "T" field selectors create DLT relative fixups for accessing
+ globals and statics in PIC code; each DLT relative fixup creates
+ an entry in the DLT table. The entries contain the address of
+ the final target (eg accessing "foo" would create a DLT entry
+ with the address of "foo").
+
+ Unfortunately, the HP linker doesn't take into account any addend
+ when generating the DLT; so accessing $LIT$+8 puts the address of
+ $LIT$ into the DLT rather than the address of $LIT$+8.
+
+ The end result is we can't perform relocation symbol reductions for
+ any fixup which creates entries in the DLT (eg they use "T" field
+ selectors).
+
+ ??? Reject reductions involving symbols with external scope; such
+ reductions make life a living hell for object file editors. */
+
+int
+hppa_fix_adjustable (fixS *fixp)
+{
+#ifdef OBJ_ELF
+ reloc_type code;
+#endif
+ struct hppa_fix_struct *hppa_fix;
+
+ hppa_fix = (struct hppa_fix_struct *) fixp->tc_fix_data;
+
+#ifdef OBJ_ELF
+ /* LR/RR selectors are implicitly used for a number of different relocation
+ types. We must ensure that none of these types are adjusted (see below)
+ even if they occur with a different selector. */
+ code = elf_hppa_reloc_final_type (stdoutput, fixp->fx_r_type,
+ hppa_fix->fx_r_format,
+ hppa_fix->fx_r_field);
+
+ switch (code)
+ {
+ /* Relocation types which use e_lrsel. */
+ case R_PARISC_DIR21L:
+ case R_PARISC_DLTREL21L:
+ case R_PARISC_DPREL21L:
+ case R_PARISC_PLTOFF21L:
+
+ /* Relocation types which use e_rrsel. */
+ case R_PARISC_DIR14R:
+ case R_PARISC_DIR14DR:
+ case R_PARISC_DIR14WR:
+ case R_PARISC_DIR17R:
+ case R_PARISC_DLTREL14R:
+ case R_PARISC_DLTREL14DR:
+ case R_PARISC_DLTREL14WR:
+ case R_PARISC_DPREL14R:
+ case R_PARISC_DPREL14DR:
+ case R_PARISC_DPREL14WR:
+ case R_PARISC_PLTOFF14R:
+ case R_PARISC_PLTOFF14DR:
+ case R_PARISC_PLTOFF14WR:
+
+ /* Other types that we reject for reduction. */
+ case R_PARISC_GNU_VTENTRY:
+ case R_PARISC_GNU_VTINHERIT:
+ return 0;
+ default:
+ break;
+ }
+#endif
+
+ /* Reject reductions of symbols in sym1-sym2 expressions when
+ the fixup will occur in a CODE subspace.
+
+ XXX FIXME: Long term we probably want to reject all of these;
+ for example reducing in the debug section would lose if we ever
+ supported using the optimizing hp linker. */
+ if (fixp->fx_addsy
+ && fixp->fx_subsy
+ && (hppa_fix->segment->flags & SEC_CODE))
+ return 0;
+
+ /* We can't adjust any relocs that use LR% and RR% field selectors.
+
+ If a symbol is reduced to a section symbol, the assembler will
+ adjust the addend unless the symbol happens to reside right at
+ the start of the section. Additionally, the linker has no choice
+ but to manipulate the addends when coalescing input sections for
+ "ld -r". Since an LR% field selector is defined to round the
+ addend, we can't change the addend without risking that a LR% and
+ it's corresponding (possible multiple) RR% field will no longer
+ sum to the right value.
+
+ eg. Suppose we have
+ . ldil LR%foo+0,%r21
+ . ldw RR%foo+0(%r21),%r26
+ . ldw RR%foo+4(%r21),%r25
+
+ If foo is at address 4092 (decimal) in section `sect', then after
+ reducing to the section symbol we get
+ . LR%sect+4092 == (L%sect)+0
+ . RR%sect+4092 == (R%sect)+4092
+ . RR%sect+4096 == (R%sect)-4096
+ and the last address loses because rounding the addend to 8k
+ multiples takes us up to 8192 with an offset of -4096.
+
+ In cases where the LR% expression is identical to the RR% one we
+ will never have a problem, but is so happens that gcc rounds
+ addends involved in LR% field selectors to work around a HP
+ linker bug. ie. We often have addresses like the last case
+ above where the LR% expression is offset from the RR% one. */
+
+ if (hppa_fix->fx_r_field == e_lrsel
+ || hppa_fix->fx_r_field == e_rrsel
+ || hppa_fix->fx_r_field == e_nlrsel)
+ return 0;
+
+ /* Reject reductions of symbols in DLT relative relocs,
+ relocations with plabels. */
+ if (hppa_fix->fx_r_field == e_tsel
+ || hppa_fix->fx_r_field == e_ltsel
+ || hppa_fix->fx_r_field == e_rtsel
+ || hppa_fix->fx_r_field == e_psel
+ || hppa_fix->fx_r_field == e_rpsel
+ || hppa_fix->fx_r_field == e_lpsel)
+ return 0;
+
+ /* Reject absolute calls (jumps). */
+ if (hppa_fix->fx_r_type == R_HPPA_ABS_CALL)
+ return 0;
+
+ /* Reject reductions of function symbols. */
+ if (fixp->fx_addsy != 0 && S_IS_FUNCTION (fixp->fx_addsy))
+ return 0;
+
+ return 1;
+}
+
+/* Return nonzero if the fixup in FIXP will require a relocation,
+ even it if appears that the fixup could be completely handled
+ within GAS. */
+
+int
+hppa_force_relocation (struct fix *fixp)
+{
+ struct hppa_fix_struct *hppa_fixp;
+
+ hppa_fixp = (struct hppa_fix_struct *) fixp->tc_fix_data;
+#ifdef OBJ_SOM
+ if (fixp->fx_r_type == (int) R_HPPA_ENTRY
+ || fixp->fx_r_type == (int) R_HPPA_EXIT
+ || fixp->fx_r_type == (int) R_HPPA_BEGIN_BRTAB
+ || fixp->fx_r_type == (int) R_HPPA_END_BRTAB
+ || fixp->fx_r_type == (int) R_HPPA_BEGIN_TRY
+ || fixp->fx_r_type == (int) R_HPPA_END_TRY
+ || (fixp->fx_addsy != NULL && fixp->fx_subsy != NULL
+ && (hppa_fixp->segment->flags & SEC_CODE) != 0))
+ return 1;
+#endif
+#ifdef OBJ_ELF
+ if (fixp->fx_r_type == (int) R_PARISC_GNU_VTINHERIT
+ || fixp->fx_r_type == (int) R_PARISC_GNU_VTENTRY)
+ return 1;
+#endif
+
+ gas_assert (fixp->fx_addsy != NULL);
+
+ /* Ensure we emit a relocation for global symbols so that dynamic
+ linking works. */
+ if (S_FORCE_RELOC (fixp->fx_addsy, 1))
+ return 1;
+
+ /* It is necessary to force PC-relative calls/jumps to have a relocation
+ entry if they're going to need either an argument relocation or long
+ call stub. */
+ if (fixp->fx_pcrel
+ && arg_reloc_stub_needed (symbol_arg_reloc_info (fixp->fx_addsy),
+ hppa_fixp->fx_arg_reloc))
+ return 1;
+
+ /* Now check to see if we're going to need a long-branch stub. */
+ if (fixp->fx_r_type == (int) R_HPPA_PCREL_CALL)
+ {
+ long pc = md_pcrel_from (fixp);
+ valueT distance, min_stub_distance;
+
+ distance = fixp->fx_offset + S_GET_VALUE (fixp->fx_addsy) - pc - 8;
+
+ /* Distance to the closest possible stub. This will detect most
+ but not all circumstances where a stub will not work. */
+ min_stub_distance = pc + 16;
+#ifdef OBJ_SOM
+ if (last_call_info != NULL)
+ min_stub_distance -= S_GET_VALUE (last_call_info->start_symbol);
+#endif
+
+ if ((distance + 8388608 >= 16777216
+ && min_stub_distance <= 8388608)
+ || (hppa_fixp->fx_r_format == 17
+ && distance + 262144 >= 524288
+ && min_stub_distance <= 262144)
+ || (hppa_fixp->fx_r_format == 12
+ && distance + 8192 >= 16384
+ && min_stub_distance <= 8192)
+ )
+ return 1;
+ }
+
+ if (fixp->fx_r_type == (int) R_HPPA_ABS_CALL)
+ return 1;
+
+ /* No need (yet) to force another relocations to be emitted. */
+ return 0;
+}
+
+/* Now for some ELF specific code. FIXME. */
+#ifdef OBJ_ELF
+/* For ELF, this function serves one purpose: to setup the st_size
+ field of STT_FUNC symbols. To do this, we need to scan the
+ call_info structure list, determining st_size in by taking the
+ difference in the address of the beginning/end marker symbols. */
+
+void
+elf_hppa_final_processing (void)
+{
+ struct call_info *call_info_pointer;
+
+ for (call_info_pointer = call_info_root;
+ call_info_pointer;
+ call_info_pointer = call_info_pointer->ci_next)
+ {
+ elf_symbol_type *esym
+ = ((elf_symbol_type *)
+ symbol_get_bfdsym (call_info_pointer->start_symbol));
+ esym->internal_elf_sym.st_size =
+ S_GET_VALUE (call_info_pointer->end_symbol)
+ - S_GET_VALUE (call_info_pointer->start_symbol) + 4;
+ }
+}
+
+static void
+pa_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+{
+ struct fix *new_fix;
+
+ new_fix = obj_elf_vtable_entry (0);
+
+ if (new_fix)
+ {
+ struct hppa_fix_struct * hppa_fix = obstack_alloc (&notes, sizeof (struct hppa_fix_struct));
+
+ hppa_fix->fx_r_type = R_HPPA;
+ hppa_fix->fx_r_field = e_fsel;
+ hppa_fix->fx_r_format = 32;
+ hppa_fix->fx_arg_reloc = 0;
+ hppa_fix->segment = now_seg;
+ new_fix->tc_fix_data = (void *) hppa_fix;
+ new_fix->fx_r_type = (int) R_PARISC_GNU_VTENTRY;
+ }
+}
+
+static void
+pa_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+{
+ struct fix *new_fix;
+
+ new_fix = obj_elf_vtable_inherit (0);
+
+ if (new_fix)
+ {
+ struct hppa_fix_struct * hppa_fix = obstack_alloc (&notes, sizeof (struct hppa_fix_struct));
+
+ hppa_fix->fx_r_type = R_HPPA;
+ hppa_fix->fx_r_field = e_fsel;
+ hppa_fix->fx_r_format = 32;
+ hppa_fix->fx_arg_reloc = 0;
+ hppa_fix->segment = now_seg;
+ new_fix->tc_fix_data = (void *) hppa_fix;
+ new_fix->fx_r_type = (int) R_PARISC_GNU_VTINHERIT;
+ }
+}
+#endif
+
+/* Table of pseudo ops for the PA. FIXME -- how many of these
+ are now redundant with the overall GAS and the object file
+ dependent tables? */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* align pseudo-ops on the PA specify the actual alignment requested,
+ not the log2 of the requested alignment. */
+#ifdef OBJ_SOM
+ {"align", pa_align, 8},
+#endif
+#ifdef OBJ_ELF
+ {"align", s_align_bytes, 8},
+#endif
+ {"begin_brtab", pa_brtab, 1},
+ {"begin_try", pa_try, 1},
+ {"block", pa_block, 1},
+ {"blockz", pa_block, 0},
+ {"byte", pa_cons, 1},
+ {"call", pa_call, 0},
+ {"callinfo", pa_callinfo, 0},
+#if defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD))
+ {"code", obj_elf_text, 0},
+#else
+ {"code", pa_text, 0},
+ {"comm", pa_comm, 0},
+#endif
+#ifdef OBJ_SOM
+ {"compiler", pa_compiler, 0},
+#endif
+ {"copyright", pa_copyright, 0},
+#if !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD)))
+ {"data", pa_data, 0},
+#endif
+ {"double", pa_float_cons, 'd'},
+ {"dword", pa_cons, 8},
+ {"end", pa_end, 0},
+ {"end_brtab", pa_brtab, 0},
+#if !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD)))
+ {"end_try", pa_try, 0},
+#endif
+ {"enter", pa_enter, 0},
+ {"entry", pa_entry, 0},
+ {"equ", pa_equ, 0},
+ {"exit", pa_exit, 0},
+ {"export", pa_export, 0},
+ {"fill", pa_fill, 0},
+ {"float", pa_float_cons, 'f'},
+ {"half", pa_cons, 2},
+ {"import", pa_import, 0},
+ {"int", pa_cons, 4},
+ {"label", pa_label, 0},
+ {"lcomm", pa_lcomm, 0},
+ {"leave", pa_leave, 0},
+ {"level", pa_level, 0},
+ {"long", pa_cons, 4},
+ {"lsym", pa_lsym, 0},
+#ifdef OBJ_SOM
+ {"nsubspa", pa_subspace, 1},
+#endif
+ {"octa", pa_cons, 16},
+ {"org", pa_origin, 0},
+ {"origin", pa_origin, 0},
+ {"param", pa_param, 0},
+ {"proc", pa_proc, 0},
+ {"procend", pa_procend, 0},
+ {"quad", pa_cons, 8},
+ {"reg", pa_equ, 1},
+ {"short", pa_cons, 2},
+ {"single", pa_float_cons, 'f'},
+#ifdef OBJ_SOM
+ {"space", pa_space, 0},
+ {"spnum", pa_spnum, 0},
+#endif
+ {"string", pa_stringer, 0},
+ {"stringz", pa_stringer, 1},
+#ifdef OBJ_SOM
+ {"subspa", pa_subspace, 0},
+#endif
+#if !(defined (OBJ_ELF) && (defined (TE_LINUX) || defined (TE_NetBSD)))
+ {"text", pa_text, 0},
+#endif
+ {"version", pa_version, 0},
+#ifdef OBJ_ELF
+ {"vtable_entry", pa_vtable_entry, 0},
+ {"vtable_inherit", pa_vtable_inherit, 0},
+#endif
+ {"word", pa_cons, 4},
+ {NULL, 0, 0}
+};
+
+#ifdef OBJ_ELF
+void
+hppa_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (30, 0);
+}
+
+int
+hppa_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int i;
+ const char *p;
+ char *q;
+ static struct { char *name; int dw2regnum; } regnames[] =
+ {
+ { "sp", 30 }, { "rp", 2 },
+ };
+
+ for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+ if (strcmp (regnames[i].name, regname) == 0)
+ return regnames[i].dw2regnum;
+
+ if (regname[0] == 'r')
+ {
+ p = regname + 1;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 32)
+ return -1;
+ }
+ else if (regname[0] == 'f' && regname[1] == 'r')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+#if TARGET_ARCH_SIZE == 64
+ if (p == q || *q || regnum <= 4 || regnum >= 32)
+ return -1;
+ regnum += 32 - 4;
+#else
+ if (p == q
+ || (*q && ((*q != 'L' && *q != 'R') || *(q + 1)))
+ || regnum <= 4 || regnum >= 32)
+ return -1;
+ regnum = (regnum - 4) * 2 + 32;
+ if (*q == 'R')
+ regnum++;
+#endif
+ }
+ return regnum;
+}
+#endif
diff --git a/gas/config/tc-hppa.h b/gas/config/tc-hppa.h
new file mode 100644
index 0000000..4277e10
--- /dev/null
+++ b/gas/config/tc-hppa.h
@@ -0,0 +1,240 @@
+/* tc-hppa.h -- Header file for the PA
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* HP PA-RISC support was contributed by the Center for Software Science
+ at the University of Utah. */
+
+/* Please refrain from exposing the world to the internals of tc-hppa.c
+ when this file is included. This means only declaring exported functions,
+ (please PARAMize them!) not exporting structures and data items which
+ are used solely within tc-hppa.c, etc.
+
+ Also refrain from adding any more object file dependent code, there is
+ already far too much object file format dependent code in this file.
+ In theory this file should contain only exported functions, structures
+ and data declarations common to all PA assemblers. */
+
+#ifndef _TC_HPPA_H
+#define _TC_HPPA_H
+
+#ifndef TC_HPPA
+#define TC_HPPA 1
+#endif
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define TARGET_ARCH bfd_arch_hppa
+
+#define WORKING_DOT_WORD
+
+#ifdef OBJ_ELF
+#if TARGET_ARCH_SIZE == 64
+#include "bfd/elf64-hppa.h"
+#if defined (TE_LINUX) || defined (TE_NetBSD)
+#define TARGET_FORMAT "elf64-hppa-linux"
+#else
+#define TARGET_FORMAT "elf64-hppa"
+#endif
+#else /* TARGET_ARCH_SIZE == 32 */
+#include "bfd/elf32-hppa.h"
+#if defined (TE_LINUX)
+#define TARGET_FORMAT "elf32-hppa-linux"
+#else
+#if defined (TE_NetBSD)
+#define TARGET_FORMAT "elf32-hppa-netbsd"
+#else
+#define TARGET_FORMAT "elf32-hppa"
+#endif
+#endif
+#endif
+#endif
+
+#ifdef OBJ_SOM
+#include "bfd/som.h"
+#define TARGET_FORMAT "som"
+#endif
+
+#if defined(TE_LINUX) || defined(TE_NetBSD)
+/* Define to compile in an extra assembler option, -c, which enables a
+ warning (once per file) when a comment is encountered.
+ The hppa comment char is a `;' which tends to occur in random C asm
+ statements. A semicolon is a line separator for most assemblers.
+ It's hard to find these lurking semicolons. Thus... */
+#define WARN_COMMENTS 1
+#endif
+
+/* FIXME. Why oh why aren't these defined somewhere globally? */
+#ifndef FALSE
+#define FALSE (0)
+#define TRUE (!FALSE)
+#endif
+
+#define ASEC_NULL (asection *)0
+
+/* pa_define_label gets used outside of tc-hppa.c via tc_frob_label. */
+extern void pa_define_label (symbolS *);
+extern int parse_cons_expression_hppa (expressionS *);
+extern void cons_fix_new_hppa (fragS *, int, int, expressionS *, int);
+extern int hppa_force_relocation (struct fix *);
+
+/* This gets called before writing the object file to make sure
+ things like entry/exit and proc/procend pairs match. */
+extern void pa_check_eof (void);
+#define tc_frob_file pa_check_eof
+
+#define tc_frob_label(sym) pa_define_label (sym)
+
+extern const char hppa_symbol_chars[];
+#define tc_symbol_chars hppa_symbol_chars
+
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 6
+
+/* The PA needs to parse field selectors in .byte, etc. */
+
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+ parse_cons_expression_hppa (EXP)
+#define TC_CONS_FIX_NEW cons_fix_new_hppa
+#define TC_PARSE_CONS_RETURN_TYPE int
+#define TC_PARSE_CONS_RETURN_NONE e_fsel
+
+/* On the PA, an exclamation point can appear in an instruction. It is
+ used in FP comparison instructions and as an end of line marker.
+ When used in an instruction it will always follow a comma. */
+#define TC_EOL_IN_INSN(PTR) (*(PTR) == '!' && (PTR)[-1] == ',')
+
+int hppa_fix_adjustable (struct fix *);
+#define tc_fix_adjustable hppa_fix_adjustable
+
+#define EXTERN_FORCE_RELOC 1
+
+/* Because of the strange PA calling conventions, it is sometimes
+ necessary to emit a relocation for a call even though it would
+ normally appear safe to handle it completely within GAS. */
+#define TC_FORCE_RELOCATION(FIX) hppa_force_relocation (FIX)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#ifdef OBJ_SOM
+/* If a symbol is imported, but never used, then the symbol should
+ *not* end up in the symbol table. Likewise for absolute symbols
+ with local scope. */
+#define tc_frob_symbol(sym,punt) \
+ if ((S_GET_SEGMENT (sym) == bfd_und_section_ptr && ! symbol_used_p (sym)) \
+ || (S_GET_SEGMENT (sym) == bfd_abs_section_ptr \
+ && ! S_IS_EXTERNAL (sym))) \
+ punt = 1
+
+/* We need to be able to make relocations involving the difference of
+ two symbols. This includes the difference of two symbols when
+ one of them is undefined (this comes up in PIC code generation).
+
+ We allow the difference of two symbols when the subtract symbol is
+ local to the relocation. This is implemented using R_HPPA_COMPLEX.
+
+ This has some limitations. Difference expressions only work between
+ symbols in the same segment/quadrant of a module since the HP dynamic
+ loader relocates the text and data segments independently. Thus, a
+ difference expression can't be used between text and data symbols,
+ or between symbols in different executable modules. */
+#define DIFF_EXPR_OK 1
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
+#define UNDEFINED_DIFFERENCE_OK
+#endif
+
+#ifdef OBJ_ELF
+
+/* Difference expressions for the 64-bit HP-UX target have the same
+ limitations as those for the 32-bit SOM target. */
+#define DIFF_EXPR_OK 1
+
+/* Handle .type psuedo. Given a type string of `millicode', set the
+ internal elf symbol type to STT_PARISC_MILLI, and return
+ BSF_FUNCTION for the BFD symbol type. */
+#define md_elf_symbol_type(name, sym, elf) \
+ ((strcmp ((name), "millicode") == 0 \
+ || strcmp ((name), "STT_PARISC_MILLI") == 0) \
+ ? (((elf)->internal_elf_sym.st_info = ELF_ST_INFO \
+ (ELF_ST_BIND ((elf)->internal_elf_sym.st_info), STT_PARISC_MILLI)\
+ ), BSF_FUNCTION) \
+ : -1)
+
+#define tc_frob_symbol(sym,punt) \
+ { \
+ if ((S_GET_SEGMENT (sym) == bfd_und_section_ptr \
+ && ! symbol_used_p (sym) \
+ && ELF_ST_VISIBILITY (S_GET_OTHER (sym)) == STV_DEFAULT) \
+ || strcmp (S_GET_NAME (sym), "$global$") == 0 \
+ || strcmp (S_GET_NAME (sym), "$segrel$") == 0 \
+ || strcmp (S_GET_NAME (sym), "$PIC_pcrel$0") == 0 \
+ || strcmp (S_GET_NAME (sym), "$tls_gdidx$") == 0 \
+ || strcmp (S_GET_NAME (sym), "$tls_ldidx$") == 0 \
+ || strcmp (S_GET_NAME (sym), "$tls_dtpoff$") == 0 \
+ || strcmp (S_GET_NAME (sym), "$tls_ieoff$") == 0 \
+ || strcmp (S_GET_NAME (sym), "$tls_leoff$") == 0) \
+ punt = 1; \
+ }
+
+#define elf_tc_final_processing elf_hppa_final_processing
+void elf_hppa_final_processing (void);
+#endif /* OBJ_ELF */
+
+#define md_operand(x)
+
+/* Allow register expressions to be treated as absolute expressions.
+ A silly fudge required for backwards compatibility. */
+#define md_optimize_expr hppa_force_reg_syms_absolute
+
+int hppa_force_reg_syms_absolute (expressionS *, operatorT, expressionS *);
+
+#define TC_FIX_TYPE void *
+#define TC_INIT_FIX_DATA(FIX) ((FIX)->tc_fix_data = NULL)
+
+#ifdef OBJ_ELF
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions hppa_cfi_frame_initial_instructions
+extern void hppa_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum hppa_regname_to_dw2regnum
+extern int hppa_regname_to_dw2regnum (char *regname);
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+#define DWARF2_DEFAULT_RETURN_COLUMN 2
+#if TARGET_ARCH_SIZE == 64
+#define DWARF2_CIE_DATA_ALIGNMENT 8
+#define DWARF2_FDE_RELOC_SIZE 8
+#else
+#define DWARF2_CIE_DATA_ALIGNMENT 4
+#endif
+
+#if !defined (TE_LINUX) && !defined (TE_NetBSD)
+/* Due to the way dynamic linking to personality functions is handled
+ on HP-UX, we need to have a read-write .eh_frame section. */
+#define DWARF2_EH_FRAME_READ_ONLY 0
+
+/* Because differences between text and data symbols don't work, we
+ can't use difference expressions during CFI generation. */
+#define CFI_DIFF_EXPR_OK 0
+#endif
+
+#endif /* OBJ_ELF */
+#endif /* _TC_HPPA_H */
diff --git a/gas/config/tc-i370.c b/gas/config/tc-i370.c
new file mode 100644
index 0000000..399b7f3
--- /dev/null
+++ b/gas/config/tc-i370.c
@@ -0,0 +1,2666 @@
+/* tc-i370.c -- Assembler for the IBM 360/370/390 instruction set.
+ Loosely based on the ppc files by Linas Vepstas <linas@linas.org> 1998, 99
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This assembler implements a very hacked version of an elf-like thing
+ that gcc emits (when gcc is suitably hacked). To make it behave more
+ HLASM-like, try turning on the -M or --mri flag (as there are various
+ similarities between HLASM and the MRI assemblers, such as section
+ names, lack of leading . in pseudo-ops, DC and DS, etc. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "struc-symbol.h"
+
+#include "opcode/i370.h"
+
+#ifdef OBJ_ELF
+#include "elf/i370.h"
+#endif
+
+/* This is the assembler for the System/390 Architecture. */
+
+/* Tell the main code what the endianness is. */
+extern int target_big_endian;
+
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+#ifdef OBJ_ELF
+/* This string holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The macro
+ tc_comment_chars points to this. We use this, rather than the
+ usual comment_chars, so that we can switch for Solaris conventions. */
+static const char i370_eabi_comment_chars[] = "#";
+
+const char *i370_comment_chars = i370_eabi_comment_chars;
+#else
+const char comment_chars[] = "#";
+#endif
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#*";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, "\
+S/370 options: (these have not yet been tested and may not work) \n\
+-u ignored\n\
+-mregnames Allow symbolic names for registers\n\
+-mno-regnames Do not allow symbolic names for registers\n");
+#ifdef OBJ_ELF
+ fprintf (stream, "\
+-mrelocatable support for GCC's -mrelocatble option\n\
+-mrelocatable-lib support for GCC's -mrelocatble-lib option\n\
+-V print assembler version number\n");
+#endif
+}
+
+/* Whether to use user friendly register names. */
+#define TARGET_REG_NAMES_P TRUE
+
+static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
+
+
+/* Predefined register names if -mregnames
+ In general, there are lots of them, in an attempt to be compatible
+ with a number of assemblers. */
+
+/* Structure to hold information about predefined registers. */
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* List of registers that are pre-defined:
+
+ Each general register has predefined names of the form:
+ 1. r<reg_num> which has the value <reg_num>.
+ 2. r.<reg_num> which has the value <reg_num>.
+
+ Each floating point register has predefined names of the form:
+ 1. f<reg_num> which has the value <reg_num>.
+ 2. f.<reg_num> which has the value <reg_num>.
+
+ There are only four floating point registers, and these are
+ commonly labelled 0,2,4 and 6. Thus, there is no f1, f3, etc.
+
+ There are individual registers as well:
+ rbase or r.base has the value 3 (base register)
+ rpgt or r.pgt has the value 4 (page origin table pointer)
+ rarg or r.arg has the value 11 (argument pointer)
+ rtca or r.tca has the value 12 (table of contents pointer)
+ rtoc or r.toc has the value 12 (table of contents pointer)
+ sp or r.sp has the value 13 (stack pointer)
+ dsa or r.dsa has the value 13 (stack pointer)
+ lr has the value 14 (link reg)
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ { "arg", 11 }, /* Argument Pointer. */
+ { "base", 3 }, /* Base Reg. */
+
+ { "f.0", 0 }, /* Floating point registers. */
+ { "f.2", 2 },
+ { "f.4", 4 },
+ { "f.6", 6 },
+
+ { "f0", 0 },
+ { "f2", 2 },
+ { "f4", 4 },
+ { "f6", 6 },
+
+ { "dsa",13 }, /* Stack pointer. */
+ { "lr", 14 }, /* Link Register. */
+ { "pgt", 4 }, /* Page Origin Table Pointer. */
+
+ { "r.0", 0 }, /* General Purpose Registers. */
+ { "r.1", 1 },
+ { "r.10", 10 },
+ { "r.11", 11 },
+ { "r.12", 12 },
+ { "r.13", 13 },
+ { "r.14", 14 },
+ { "r.15", 15 },
+ { "r.2", 2 },
+ { "r.3", 3 },
+ { "r.4", 4 },
+ { "r.5", 5 },
+ { "r.6", 6 },
+ { "r.7", 7 },
+ { "r.8", 8 },
+ { "r.9", 9 },
+
+ { "r.arg", 11 }, /* Argument Pointer. */
+ { "r.base", 3 }, /* Base Reg. */
+ { "r.dsa", 13 }, /* Stack Pointer. */
+ { "r.pgt", 4 }, /* Page Origin Table Pointer. */
+ { "r.sp", 13 }, /* Stack Pointer. */
+
+ { "r.tca", 12 }, /* Pointer to the table of contents. */
+ { "r.toc", 12 }, /* Pointer to the table of contents. */
+
+ { "r0", 0 }, /* More general purpose registers. */
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r2", 2 },
+ { "r3", 3 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+
+ { "rbase", 3 }, /* Base Reg. */
+
+ { "rtca", 12 }, /* Pointer to the table of contents. */
+ { "rtoc", 12 }, /* Pointer to the table of contents. */
+
+ { "sp", 13 }, /* Stack Pointer. */
+
+};
+
+#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
+
+/* Given NAME, find the register number associated with that name, return
+ the integer value associated with the given name or -1 on failure. */
+
+static int
+reg_name_search (const struct pd_reg *regs,
+ int regcount,
+ const char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/* Summary of register_name().
+
+ in: Input_line_pointer points to 1st char of operand.
+
+ out: An expressionS.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in its
+ original state. */
+
+static bfd_boolean
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+ if (name[0] == '%' && ISALPHA (name[1]))
+ name = ++input_line_pointer;
+
+ else if (!reg_names_p)
+ return FALSE;
+
+ while (' ' == *name)
+ name = ++input_line_pointer;
+
+ /* If it's a number, treat it as a number. If it's alpha, look to
+ see if it's in the register table. */
+ if (!ISALPHA (name[0]))
+ reg_number = get_single_number ();
+ else
+ {
+ c = get_symbol_end ();
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+ }
+
+ /* If numeric, make sure its not out of bounds. */
+ if ((0 <= reg_number) && (16 >= reg_number))
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+/* Local variables. */
+
+/* The type of processor we are assembling for. This is one or more
+ of the I370_OPCODE flags defined in opcode/i370.h. */
+static int i370_cpu = 0;
+
+/* The base register to use for opcode with optional operands.
+ We define two of these: "text" and "other". Normally, "text"
+ would get used in the .text section for branches, while "other"
+ gets used in the .data section for address constants.
+
+ The idea of a second base register in a different section
+ is foreign to the usual HLASM-style semantics; however, it
+ allows us to provide support for dynamically loaded libraries,
+ by allowing us to place address constants in a section other
+ than the text section. The "other" section need not be the
+ .data section, it can be any section that isn't the .text section.
+
+ Note that HLASM defines a multiple, concurrent .using semantic
+ that we do not: in calculating offsets, it uses either the most
+ recent .using directive, or the one with the smallest displacement.
+ This allows HLASM to support a quasi-block-scope-like behaviour.
+ Handy for people writing assembly by hand ... but not supported
+ by us. */
+static int i370_using_text_regno = -1;
+static int i370_using_other_regno = -1;
+
+/* The base address for address literals. */
+static expressionS i370_using_text_baseaddr;
+static expressionS i370_using_other_baseaddr;
+
+/* the "other" section, used only for syntax error detection. */
+static segT i370_other_section = undefined_section;
+
+/* Opcode hash table. */
+static struct hash_control *i370_hash;
+
+/* Macro hash table. */
+static struct hash_control *i370_macro_hash;
+
+#ifdef OBJ_ELF
+/* What type of shared library support to use. */
+static enum { SHLIB_NONE, SHLIB_PIC, SHILB_MRELOCATABLE } shlib = SHLIB_NONE;
+#endif
+
+/* Flags to set in the elf header. */
+static flagword i370_flags = 0;
+
+#ifndef WORKING_DOT_WORD
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
+#endif
+
+#ifdef OBJ_ELF
+const char *md_shortopts = "l:um:K:VQ:";
+#else
+const char *md_shortopts = "um:";
+#endif
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'u':
+ /* -u means that any undefined symbols should be treated as
+ external, which is the default for gas anyhow. */
+ break;
+
+#ifdef OBJ_ELF
+ case 'K':
+ /* Recognize -K PIC */
+ if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0)
+ {
+ shlib = SHLIB_PIC;
+ i370_flags |= EF_I370_RELOCATABLE_LIB;
+ }
+ else
+ return 0;
+
+ break;
+#endif
+
+ case 'm':
+
+ /* -m360 mean to assemble for the ancient 360 architecture. */
+ if (strcmp (arg, "360") == 0 || strcmp (arg, "i360") == 0)
+ i370_cpu = I370_OPCODE_360;
+ /* -mxa means to assemble for the IBM 370 XA. */
+ else if (strcmp (arg, "xa") == 0)
+ i370_cpu = I370_OPCODE_370_XA;
+ /* -many means to assemble for any architecture (370/XA). */
+ else if (strcmp (arg, "any") == 0)
+ i370_cpu = I370_OPCODE_370;
+
+ else if (strcmp (arg, "regnames") == 0)
+ reg_names_p = TRUE;
+
+ else if (strcmp (arg, "no-regnames") == 0)
+ reg_names_p = FALSE;
+
+#ifdef OBJ_ELF
+ /* -mrelocatable/-mrelocatable-lib -- warn about
+ initializations that require relocation. */
+ else if (strcmp (arg, "relocatable") == 0)
+ {
+ shlib = SHILB_MRELOCATABLE;
+ i370_flags |= EF_I370_RELOCATABLE;
+ }
+ else if (strcmp (arg, "relocatable-lib") == 0)
+ {
+ shlib = SHILB_MRELOCATABLE;
+ i370_flags |= EF_I370_RELOCATABLE_LIB;
+ }
+#endif
+ else
+ {
+ as_bad (_("invalid switch -m%s"), arg);
+ return 0;
+ }
+ break;
+
+#ifdef OBJ_ELF
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/* Set i370_cpu if it is not already set.
+ Currently defaults to the reasonable superset;
+ but can be made more fine grained if desred. */
+
+static void
+i370_set_cpu (void)
+{
+ const char *default_os = TARGET_OS;
+ const char *default_cpu = TARGET_CPU;
+
+ /* Override with the superset for the moment. */
+ i370_cpu = I370_OPCODE_ESA390_SUPERSET;
+ if (i370_cpu == 0)
+ {
+ if (strcmp (default_cpu, "i360") == 0)
+ i370_cpu = I370_OPCODE_360;
+ else if (strcmp (default_cpu, "i370") == 0)
+ i370_cpu = I370_OPCODE_370;
+ else if (strcmp (default_cpu, "XA") == 0)
+ i370_cpu = I370_OPCODE_370_XA;
+ else
+ as_fatal ("Unknown default cpu = %s, os = %s", default_cpu, default_os);
+ }
+}
+
+/* Figure out the BFD architecture to use.
+ FIXME: specify the different 370 architectures. */
+
+enum bfd_architecture
+i370_arch (void)
+{
+ return bfd_arch_i370;
+}
+
+/* This function is called when the assembler starts up. It is called
+ after the options have been parsed and the output file has been
+ opened. */
+
+void
+md_begin (void)
+{
+ const struct i370_opcode *op;
+ const struct i370_opcode *op_end;
+ const struct i370_macro *macro;
+ const struct i370_macro *macro_end;
+ bfd_boolean dup_insn = FALSE;
+
+ i370_set_cpu ();
+
+#ifdef OBJ_ELF
+ /* Set the ELF flags if desired. */
+ if (i370_flags)
+ bfd_set_private_flags (stdoutput, i370_flags);
+#endif
+
+ /* Insert the opcodes into a hash table. */
+ i370_hash = hash_new ();
+
+ op_end = i370_opcodes + i370_num_opcodes;
+ for (op = i370_opcodes; op < op_end; op++)
+ {
+ know ((op->opcode.i[0] & op->mask.i[0]) == op->opcode.i[0]
+ && (op->opcode.i[1] & op->mask.i[1]) == op->opcode.i[1]);
+
+ if ((op->flags & i370_cpu) != 0)
+ {
+ const char *retval;
+
+ retval = hash_insert (i370_hash, op->name, (void *) op);
+ if (retval != (const char *) NULL)
+ {
+ as_bad (_("Internal assembler error for instruction %s"), op->name);
+ dup_insn = TRUE;
+ }
+ }
+ }
+
+ /* Insert the macros into a hash table. */
+ i370_macro_hash = hash_new ();
+
+ macro_end = i370_macros + i370_num_macros;
+ for (macro = i370_macros; macro < macro_end; macro++)
+ {
+ if ((macro->flags & i370_cpu) != 0)
+ {
+ const char *retval;
+
+ retval = hash_insert (i370_macro_hash, macro->name, (void *) macro);
+ if (retval != (const char *) NULL)
+ {
+ as_bad (_("Internal assembler error for macro %s"), macro->name);
+ dup_insn = TRUE;
+ }
+ }
+ }
+
+ if (dup_insn)
+ abort ();
+}
+
+/* Insert an operand value into an instruction. */
+
+static i370_insn_t
+i370_insert_operand (i370_insn_t insn,
+ const struct i370_operand *operand,
+ offsetT val)
+{
+ if (operand->insert)
+ {
+ const char *errmsg;
+
+ /* Used for 48-bit insn's. */
+ errmsg = NULL;
+ insn = (*operand->insert) (insn, (long) val, &errmsg);
+ if (errmsg)
+ as_bad ("%s", errmsg);
+ }
+ else
+ /* This is used only for 16, 32 bit insn's. */
+ insn.i[0] |= (((long) val & ((1 << operand->bits) - 1))
+ << operand->shift);
+
+ return insn;
+}
+
+
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation.
+ Currently, i370 does not support (don't really need to support) any
+ of these fancier markups ... for example, no one is going to
+ write 'L 6,=V(bogus)@got' it just doesn't make sense (at least to me).
+ So basically, we could get away with this routine returning
+ BFD_RELOC_UNUSED in all circumstances. However, I'll leave
+ in for now in case someone ambitious finds a good use for this stuff ...
+ this routine was pretty much just copied from the powerpc code ... */
+
+static bfd_reloc_code_real_type
+i370_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ struct map_bfd
+ {
+ char *string;
+ int length;
+ bfd_reloc_code_real_type reloc;
+ };
+
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ struct map_bfd *ptr;
+
+#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
+
+ static struct map_bfd mapping[] =
+ {
+ /* warnings with -mrelocatable. */
+ MAP ("fixup", BFD_RELOC_CTOR),
+ { (char *)0, 0, BFD_RELOC_UNUSED }
+ };
+
+ if (*str++ != '@')
+ return BFD_RELOC_UNUSED;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ *str2++ = TOLOWER (ch);
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->string[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->string, ptr->length) == 0)
+ {
+ if (exp_p->X_add_number != 0
+ && (ptr->reloc == BFD_RELOC_16_GOTOFF
+ || ptr->reloc == BFD_RELOC_LO16_GOTOFF
+ || ptr->reloc == BFD_RELOC_HI16_GOTOFF
+ || ptr->reloc == BFD_RELOC_HI16_S_GOTOFF))
+ as_warn (_("identifier+constant@got means identifier@got+constant"));
+
+ /* Now check for identifier@suffix+constant */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+
+ *str_p = str;
+ return ptr->reloc;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+/* Like normal .long/.short/.word, except support @got, etc.
+ Clobbers input_line_pointer, checks end-of-line. */
+
+static void
+i370_elf_cons (int nbytes) /* 1=.byte, 2=.word, 4=.long. */
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && (reloc = i370_elf_suffix (&input_line_pointer, &exp)) != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ int size = bfd_get_reloc_size (reloc_howto);
+
+ if (size > nbytes)
+ as_bad (_("%s relocations do not fit in %d bytes\n"),
+ reloc_howto->name, nbytes);
+ else
+ {
+ char *p = frag_more ((int) nbytes);
+ int offset = nbytes - size;
+
+ fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, &exp, 0, reloc);
+ }
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+
+/* ASCII to EBCDIC conversion table. */
+static unsigned char ascebc[256] =
+{
+ /*00 NL SH SX EX ET NQ AK BL */
+ 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F,
+ /*08 BS HT LF VT FF CR SO SI */
+ 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ /*10 DL D1 D2 D3 D4 NK SN EB */
+ 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26,
+ /*18 CN EM SB EC FS GS RS US */
+ 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F,
+ /*20 SP ! " # $ % & ' */
+ 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D,
+ /*28 ( ) * + , - . / */
+ 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61,
+ /*30 0 1 2 3 4 5 6 7 */
+ 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
+ /*38 8 9 : ; < = > ? */
+ 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F,
+ /*40 @ A B C D E F G */
+ 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+ /*48 H I J K L M N O */
+ 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+ /*50 P Q R S T U V W */
+ 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6,
+ /*58 X Y Z [ \ ] ^ _ */
+ 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D,
+ /*60 ` a b c d e f g */
+ 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ /*68 h i j k l m n o */
+ 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96,
+ /*70 p q r s t u v w */
+ 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
+ /*78 x y z { | } ~ DL */
+ 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F,
+ 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF
+};
+
+/* EBCDIC to ASCII conversion table. */
+unsigned char ebcasc[256] =
+{
+ /*00 NU SH SX EX PF HT LC DL */
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7F,
+ /*08 SM VT FF CR SO SI */
+ 0x00, 0x00, 0x00, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ /*10 DE D1 D2 TM RS NL BS IL */
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x0A, 0x08, 0x00,
+ /*18 CN EM CC C1 FS GS RS US */
+ 0x18, 0x19, 0x00, 0x00, 0x1C, 0x1D, 0x1E, 0x1F,
+ /*20 DS SS FS BP LF EB EC */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x1B,
+ /*28 SM C2 EQ AK BL */
+ 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x00,
+ /*30 SY PN RS UC ET */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04,
+ /*38 C3 D4 NK SU */
+ 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1A,
+ /*40 SP */
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*48 . < ( + | */
+ 0x00, 0x00, 0x00, 0x2E, 0x3C, 0x28, 0x2B, 0x7C,
+ /*50 & */
+ 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*58 ! $ * ) ; ^ */
+ 0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E,
+ /*60 - / */
+ 0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*68 , % _ > ? */
+ 0x00, 0x00, 0x00, 0x2C, 0x25, 0x5F, 0x3E, 0x3F,
+ /*70 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*78 ` : # @ ' = " */
+ 0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22,
+ /*80 a b c d e f g */
+ 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ /*88 h i { */
+ 0x68, 0x69, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00,
+ /*90 j k l m n o p */
+ 0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+ /*98 q r } */
+ 0x71, 0x72, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00,
+ /*A0 ~ s t u v w x */
+ 0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ /*A8 y z [ */
+ 0x79, 0x7A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00,
+ /*B0 */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*B8 ] */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00,
+ /*C0 { A B C D E F G */
+ 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ /*C8 H I */
+ 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*D0 } J K L M N O P */
+ 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
+ /*D8 Q R */
+ 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*E0 \ S T U V W X */
+ 0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ /*E8 Y Z */
+ 0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /*F0 0 1 2 3 4 5 6 7 */
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ /*F8 8 9 */
+ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF
+};
+
+/* EBCDIC translation tables needed for 3270 support. */
+
+static void
+i370_ebcdic (int unused ATTRIBUTE_UNUSED)
+{
+ char *p, *end;
+ char delim = 0;
+ size_t nbytes;
+
+ nbytes = strlen (input_line_pointer);
+ end = input_line_pointer + nbytes;
+ while ('\r' == *end) end --;
+ while ('\n' == *end) end --;
+
+ delim = *input_line_pointer;
+ if (('\'' == delim) || ('\"' == delim))
+ {
+ input_line_pointer ++;
+ end = rindex (input_line_pointer, delim);
+ }
+
+ if (end > input_line_pointer)
+ {
+ nbytes = end - input_line_pointer +1;
+ p = frag_more (nbytes);
+ while (end > input_line_pointer)
+ {
+ *p = ascebc [(unsigned char) (*input_line_pointer)];
+ ++p; ++input_line_pointer;
+ }
+ *p = '\0';
+ }
+ if (delim == *input_line_pointer) ++input_line_pointer;
+}
+
+
+/* Stub out a couple of routines. */
+
+static void
+i370_rmode (int unused ATTRIBUTE_UNUSED)
+{
+ as_tsktsk ("rmode ignored");
+}
+
+static void
+i370_dsect (int sect)
+{
+ char *save_line = input_line_pointer;
+ static char section[] = ".data\n";
+
+ /* Just pretend this is .section .data. */
+ input_line_pointer = section;
+ obj_elf_section (sect);
+
+ input_line_pointer = save_line;
+}
+
+static void
+i370_csect (int unused ATTRIBUTE_UNUSED)
+{
+ as_tsktsk ("csect not supported");
+}
+
+
+/* DC Define Const is only partially supported.
+ For samplecode on what to do, look at i370_elf_cons() above.
+ This code handles pseudoops of the style
+ DC D'3.141592653' # in sysv4, .double 3.14159265
+ DC F'1' # in sysv4, .long 1. */
+
+static void
+i370_dc (int unused ATTRIBUTE_UNUSED)
+{
+ char * p, tmp[50];
+ int nbytes=0;
+ expressionS exp;
+ char type=0;
+ char * clse;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ /* Figure out the size. */
+ type = *input_line_pointer++;
+ switch (type)
+ {
+ case 'H': /* 16-bit */
+ nbytes = 2;
+ break;
+ case 'E': /* 32-bit */
+ case 'F': /* 32-bit */
+ nbytes = 4;
+ break;
+ case 'D': /* 64-bit */
+ nbytes = 8;
+ break;
+ default:
+ as_bad (_("unsupported DC type"));
+ return;
+ }
+
+ /* Get rid of pesky quotes. */
+ if ('\'' == *input_line_pointer)
+ {
+ ++input_line_pointer;
+ clse = strchr (input_line_pointer, '\'');
+ if (clse)
+ *clse= ' ';
+ else
+ as_bad (_("missing end-quote"));
+ }
+
+ if ('\"' == *input_line_pointer)
+ {
+ ++input_line_pointer;
+ clse = strchr (input_line_pointer, '\"');
+ if (clse)
+ *clse= ' ';
+ else
+ as_bad (_("missing end-quote"));
+ }
+
+ switch (type)
+ {
+ case 'H': /* 16-bit */
+ case 'F': /* 32-bit */
+ expression (&exp);
+ emit_expr (&exp, nbytes);
+ break;
+ case 'E': /* 32-bit */
+ type = 'f';
+ case 'D': /* 64-bit */
+ md_atof (type, tmp, &nbytes);
+ p = frag_more (nbytes);
+ memcpy (p, tmp, nbytes);
+ break;
+ default:
+ as_bad (_("unsupported DC type"));
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Provide minimal support for DS Define Storage. */
+
+static void
+i370_ds (int unused ATTRIBUTE_UNUSED)
+{
+ /* DS 0H or DS 0F or DS 0D. */
+ if ('0' == *input_line_pointer)
+ {
+ int alignment = 0; /* Left shift 1 << align. */
+ input_line_pointer ++;
+ switch (*input_line_pointer++)
+ {
+ case 'H': /* 16-bit */
+ alignment = 1;
+ break;
+ case 'F': /* 32-bit */
+ alignment = 2;
+ break;
+ case 'D': /* 64-bit */
+ alignment = 3;
+ break;
+ default:
+ as_bad (_("unsupported alignment"));
+ return;
+ }
+ frag_align (alignment, 0, 0);
+ record_alignment (now_seg, alignment);
+ }
+ else
+ as_bad (_("this DS form not yet supported"));
+}
+
+/* Solaris pseudo op to change to the .rodata section. */
+
+static void
+i370_elf_rdata (int sect)
+{
+ char *save_line = input_line_pointer;
+ static char section[] = ".rodata\n";
+
+ /* Just pretend this is .section .rodata. */
+ input_line_pointer = section;
+ obj_elf_section (sect);
+
+ input_line_pointer = save_line;
+}
+
+/* Pseudo op to make file scope bss items. */
+
+static void
+i370_elf_lcomm (int unused ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+ offsetT align;
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Skip ','. */
+ input_line_pointer++;
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Allocate_bss: */
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ if (align)
+ {
+ /* Convert to a power of 2 alignment. */
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2)
+ ;
+ if (align != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ record_alignment (bss_section, align2);
+ subseg_set (bss_section, 0);
+ if (align2)
+ frag_align (align2, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, bss_section);
+ subseg_set (old_sec, old_subsec);
+ demand_empty_rest_of_line ();
+}
+
+/* Validate any relocations emitted for -mrelocatable, possibly adding
+ fixups for word relocations in writable segments, so we can adjust
+ them at runtime. */
+
+static void
+i370_elf_validate_fix (fixS *fixp, segT seg)
+{
+ if (fixp->fx_done || fixp->fx_pcrel)
+ return;
+
+ switch (shlib)
+ {
+ case SHLIB_NONE:
+ case SHLIB_PIC:
+ return;
+
+ case SHILB_MRELOCATABLE:
+ if (fixp->fx_r_type <= BFD_RELOC_UNUSED
+ && fixp->fx_r_type != BFD_RELOC_16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_32_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_LO16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL
+ && strcmp (segment_name (seg), ".got2") != 0
+ && strcmp (segment_name (seg), ".dtors") != 0
+ && strcmp (segment_name (seg), ".ctors") != 0
+ && strcmp (segment_name (seg), ".fixup") != 0
+ && strcmp (segment_name (seg), ".stab") != 0
+ && strcmp (segment_name (seg), ".gcc_except_table") != 0
+ && strcmp (segment_name (seg), ".ex_shared") != 0)
+ {
+ if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0
+ || fixp->fx_r_type != BFD_RELOC_CTOR)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "Relocation cannot be done when using -mrelocatable");
+ }
+ return;
+ default:
+ break;
+ }
+}
+#endif /* OBJ_ELF */
+
+
+#define LITERAL_POOL_SUPPORT
+#ifdef LITERAL_POOL_SUPPORT
+/* Provide support for literal pools within the text section.
+ Loosely based on similar code from tc-arm.c.
+ We will use four symbols to locate four parts of the literal pool.
+ These four sections contain 64,32,16 and 8-bit constants; we use
+ four sections so that all memory access can be appropriately aligned.
+ That is, we want to avoid mixing these together so that we don't
+ waste space padding out to alignments. The four pointers
+ longlong_poolP, word_poolP, etc. point to a symbol labeling the
+ start of each pool part.
+
+ lit_pool_num increments from zero to infinity and uniquely id's
+ -- its used to generate the *_poolP symbol name. */
+
+#define MAX_LITERAL_POOL_SIZE 1024
+
+typedef struct literalS
+{
+ struct expressionS exp;
+ char * sym_name;
+ char size; /* 1,2,4 or 8 */
+ short offset;
+} literalT;
+
+literalT literals[MAX_LITERAL_POOL_SIZE];
+int next_literal_pool_place = 0; /* Next free entry in the pool. */
+
+static symbolS *longlong_poolP = NULL; /* 64-bit pool entries. */
+static symbolS *word_poolP = NULL; /* 32-bit pool entries. */
+static symbolS *short_poolP = NULL; /* 16-bit pool entries. */
+static symbolS *byte_poolP = NULL; /* 8-bit pool entries. */
+
+static int lit_pool_num = 1;
+
+/* Create a new, empty symbol. */
+static symbolS *
+symbol_make_empty (void)
+{
+ return symbol_create (FAKE_LABEL_NAME, undefined_section,
+ (valueT) 0, &zero_address_frag);
+}
+
+/* Make the first argument an address-relative expression
+ by subtracting the second argument. */
+
+static void
+i370_make_relative (expressionS *exx, expressionS *baseaddr)
+{
+ if (O_constant == baseaddr->X_op)
+ {
+ exx->X_op = O_symbol;
+ exx->X_add_number -= baseaddr->X_add_number;
+ }
+ else if (O_symbol == baseaddr->X_op)
+ {
+ exx->X_op = O_subtract;
+ exx->X_op_symbol = baseaddr->X_add_symbol;
+ exx->X_add_number -= baseaddr->X_add_number;
+ }
+ else if (O_uminus == baseaddr->X_op)
+ {
+ exx->X_op = O_add;
+ exx->X_op_symbol = baseaddr->X_add_symbol;
+ exx->X_add_number += baseaddr->X_add_number;
+ }
+ else
+ as_bad (_("Missing or bad .using directive"));
+}
+/* Add an expression to the literal pool. */
+
+static void
+add_to_lit_pool (expressionS *exx, char *name, int sz)
+{
+ int lit_count = 0;
+ int offset_in_pool = 0;
+
+ /* Start a new pool, if necessary. */
+ if (8 == sz && NULL == longlong_poolP)
+ longlong_poolP = symbol_make_empty ();
+ else if (4 == sz && NULL == word_poolP)
+ word_poolP = symbol_make_empty ();
+ else if (2 == sz && NULL == short_poolP)
+ short_poolP = symbol_make_empty ();
+ else if (1 == sz && NULL == byte_poolP)
+ byte_poolP = symbol_make_empty ();
+
+ /* Check if this literal value is already in the pool.
+ FIXME: We should probably be checking expressions
+ of type O_symbol as well.
+ FIXME: This is probably(certainly?) broken for O_big,
+ which includes 64-bit long-longs. */
+ while (lit_count < next_literal_pool_place)
+ {
+ if (exx->X_op == O_constant
+ && literals[lit_count].exp.X_op == exx->X_op
+ && literals[lit_count].exp.X_add_number == exx->X_add_number
+ && literals[lit_count].exp.X_unsigned == exx->X_unsigned
+ && literals[lit_count].size == sz)
+ break;
+ else if (literals[lit_count].sym_name
+ && name
+ && !strcmp (name, literals[lit_count].sym_name))
+ break;
+ if (sz == literals[lit_count].size)
+ offset_in_pool += sz;
+ lit_count ++;
+ }
+
+ if (lit_count == next_literal_pool_place) /* new entry */
+ {
+ if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
+ as_bad (_("Literal Pool Overflow"));
+
+ literals[next_literal_pool_place].exp = *exx;
+ literals[next_literal_pool_place].size = sz;
+ literals[next_literal_pool_place].offset = offset_in_pool;
+ if (name)
+ literals[next_literal_pool_place].sym_name = strdup (name);
+ else
+ literals[next_literal_pool_place].sym_name = NULL;
+ next_literal_pool_place++;
+ }
+
+ /* ???_poolP points to the beginning of the literal pool.
+ X_add_number is the offset from the beginning of the
+ literal pool to this expr minus the location of the most
+ recent .using directive. Thus, the grand total value of the
+ expression is the distance from .using to the literal. */
+ if (8 == sz)
+ exx->X_add_symbol = longlong_poolP;
+ else if (4 == sz)
+ exx->X_add_symbol = word_poolP;
+ else if (2 == sz)
+ exx->X_add_symbol = short_poolP;
+ else if (1 == sz)
+ exx->X_add_symbol = byte_poolP;
+ exx->X_add_number = offset_in_pool;
+ exx->X_op_symbol = NULL;
+
+ /* If the user has set up a base reg in another section,
+ use that; otherwise use the text section. */
+ if (0 < i370_using_other_regno)
+ i370_make_relative (exx, &i370_using_other_baseaddr);
+ else
+ i370_make_relative (exx, &i370_using_text_baseaddr);
+}
+
+/* The symbol setup for the literal pool is done in two steps. First,
+ a symbol that represents the start of the literal pool is created,
+ above, in the add_to_pool() routine. This sym ???_poolP.
+ However, we don't know what fragment its in until a bit later.
+ So we defer the frag_now thing, and the symbol name, until .ltorg time. */
+
+/* Can't use symbol_new here, so have to create a symbol and then at
+ a later date assign it a value. Thats what these functions do. */
+
+static void
+symbol_locate (symbolS *symbolP,
+ const char *name, /* It is copied, the caller can modify. */
+ segT segment, /* Segment identifier (SEG_<something>). */
+ valueT valu, /* Symbol value. */
+ fragS *frag) /* Associated fragment. */
+{
+ size_t name_length;
+ char *preserved_copy_of_name;
+
+ name_length = strlen (name) + 1; /* +1 for \0 */
+ obstack_grow (&notes, name, name_length);
+ preserved_copy_of_name = obstack_finish (&notes);
+
+ S_SET_NAME (symbolP, preserved_copy_of_name);
+
+ S_SET_SEGMENT (symbolP, segment);
+ S_SET_VALUE (symbolP, valu);
+ symbol_clear_list_pointers (symbolP);
+
+ symbol_set_frag (symbolP, frag);
+
+ /* Link to end of symbol chain. */
+ {
+ extern int symbol_table_frozen;
+
+ if (symbol_table_frozen)
+ abort ();
+ }
+
+ symbol_append (symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP);
+
+ obj_symbol_new_hook (symbolP);
+
+#ifdef tc_symbol_new_hook
+ tc_symbol_new_hook (symbolP);
+#endif
+
+#define DEBUG_SYMS
+#ifdef DEBUG_SYMS
+ verify_symbol_chain(symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS */
+}
+
+/* i370_addr_offset() will convert operand expressions
+ that appear to be absolute into thier base-register
+ relative form. These expressions come in two types:
+
+ (1) of the form "* + const" * where "*" means
+ relative offset since the last using
+ i.e. "*" means ".-using_baseaddr"
+
+ (2) labels, which are never absolute, but are always
+ relative to the last "using". Anything with an alpha
+ character is considered to be a label (since symbols
+ can never be operands), and since we've already handled
+ register operands. For example, "BL .L33" branch low
+ to .L33 RX form insn frequently terminates for-loops. */
+
+static bfd_boolean
+i370_addr_offset (expressionS *exx)
+{
+ char *dot, *lab;
+ int islabel = 0;
+ int all_digits = 0;
+
+ /* Search for a label; anything with an alpha char will do.
+ Local labels consist of N digits followed by either b or f. */
+ lab = input_line_pointer;
+ while (*lab && (',' != *lab) && ('(' != *lab))
+ {
+ if (ISDIGIT (*lab))
+ all_digits = 1;
+ else if (ISALPHA (*lab))
+ {
+ if (!all_digits)
+ {
+ islabel = 1;
+ break;
+ }
+ else if (('f' == *lab) || ('b' == *lab))
+ {
+ islabel = 1;
+ break;
+ }
+ if (all_digits)
+ break;
+ }
+ else if ('.' != *lab)
+ break;
+ ++lab;
+ }
+
+ /* See if operand has a * in it. */
+ dot = strchr (input_line_pointer, '*');
+
+ if (!dot && !islabel)
+ return FALSE;
+
+ /* Replace * with . and let expr munch on it. */
+ if (dot)
+ *dot = '.';
+ expression (exx);
+
+ /* OK, now we have to subtract the "using" location.
+ Normally branches appear in the text section only. */
+ if (0 == strncmp (now_seg->name, ".text", 5) || 0 > i370_using_other_regno)
+ i370_make_relative (exx, &i370_using_text_baseaddr);
+ else
+ i370_make_relative (exx, &i370_using_other_baseaddr);
+
+ /* Put the * back. */
+ if (dot)
+ *dot = '*';
+
+ return TRUE;
+}
+
+/* Handle address constants of various sorts. */
+/* The currently supported types are
+ =A(some_symb)
+ =V(some_extern)
+ =X'deadbeef' hexadecimal
+ =F'1234' 32-bit const int
+ =H'1234' 16-bit const int. */
+
+static bfd_boolean
+i370_addr_cons (expressionS *exp)
+{
+ char *name;
+ char *sym_name, delim;
+ int name_len;
+ int hex_len = 0;
+ int cons_len = 0;
+
+ name = input_line_pointer;
+ sym_name = input_line_pointer;
+ /* Find the spelling of the operand. */
+ if (name[0] == '=' && ISALPHA (name[1]))
+ name = ++input_line_pointer;
+ else
+ return FALSE;
+
+ switch (name[0])
+ {
+ case 'A': /* A == address-of. */
+ case 'V': /* V == extern. */
+ ++input_line_pointer;
+ expression (exp);
+
+ /* We use a simple string name to collapse together
+ multiple refrences to the same address literal. */
+ name_len = strcspn (sym_name, ", ");
+ delim = *(sym_name + name_len);
+ *(sym_name + name_len) = 0x0;
+ add_to_lit_pool (exp, sym_name, 4);
+ *(sym_name + name_len) = delim;
+
+ break;
+ case 'H':
+ case 'F':
+ case 'X':
+ case 'E': /* Single-precision float point. */
+ case 'D': /* Double-precision float point. */
+
+ /* H == 16-bit fixed-point const; expression must be const. */
+ /* F == fixed-point const; expression must be const. */
+ /* X == fixed-point const; expression must be const. */
+ if ('H' == name[0]) cons_len = 2;
+ else if ('F' == name[0]) cons_len = 4;
+ else if ('X' == name[0]) cons_len = -1;
+ else if ('E' == name[0]) cons_len = 4;
+ else if ('D' == name[0]) cons_len = 8;
+
+ /* Extract length, if it is present;
+ FIXME: assume single-digit length. */
+ if ('L' == name[1])
+ {
+ /* Should work for ASCII and EBCDIC. */
+ cons_len = name[2] - '0';
+ input_line_pointer += 2;
+ }
+
+ ++input_line_pointer;
+
+ /* Get rid of pesky quotes. */
+ if ('\'' == *input_line_pointer)
+ {
+ char * clse;
+
+ ++input_line_pointer;
+ clse = strchr (input_line_pointer, '\'');
+ if (clse)
+ *clse= ' ';
+ else
+ as_bad (_("missing end-quote"));
+ }
+ if ('\"' == *input_line_pointer)
+ {
+ char * clse;
+
+ ++input_line_pointer;
+ clse = strchr (input_line_pointer, '\"');
+ if (clse)
+ *clse= ' ';
+ else
+ as_bad (_("missing end-quote"));
+ }
+ if (('X' == name[0]) || ('E' == name[0]) || ('D' == name[0]))
+ {
+ char tmp[50];
+ char *save;
+
+ /* The length of hex constants is specified directly with L,
+ or implied through the number of hex digits. For example:
+ =X'AB' one byte
+ =X'abcd' two bytes
+ =X'000000AB' four bytes
+ =XL4'AB' four bytes, left-padded withn zero. */
+ if (('X' == name[0]) && (0 > cons_len))
+ {
+ save = input_line_pointer;
+ while (*save)
+ {
+ if (ISXDIGIT (*save))
+ hex_len++;
+ save++;
+ }
+ cons_len = (hex_len+1) /2;
+ }
+ /* I believe this works even for =XL8'dada0000beeebaaa'
+ which should parse out to X_op == O_big
+ Note that floats and doubles get represented as
+ 0d3.14159265358979 or 0f 2.7. */
+ tmp[0] = '0';
+ tmp[1] = name[0];
+ tmp[2] = 0;
+ strcat (tmp, input_line_pointer);
+ save = input_line_pointer;
+ input_line_pointer = tmp;
+ expression (exp);
+ input_line_pointer = save + (input_line_pointer-tmp-2);
+
+ /* Fix up lengths for floats and doubles. */
+ if (O_big == exp->X_op)
+ exp->X_add_number = cons_len / CHARS_PER_LITTLENUM;
+ }
+ else
+ expression (exp);
+
+ /* O_big occurs when more than 4 bytes worth gets parsed. */
+ if ((exp->X_op != O_constant) && (exp->X_op != O_big))
+ {
+ as_bad (_("expression not a constant"));
+ return FALSE;
+ }
+ add_to_lit_pool (exp, 0x0, cons_len);
+ break;
+
+ default:
+ as_bad (_("Unknown/unsupported address literal type"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/* Dump the contents of the literal pool that we've accumulated so far.
+ This aligns the pool to the size of the largest literal in the pool. */
+
+static void
+i370_ltorg (int ignore ATTRIBUTE_UNUSED)
+{
+ int litsize;
+ int lit_count = 0;
+ int biggest_literal_size = 0;
+ int biggest_align = 0;
+ char pool_name[20];
+
+ if (strncmp (now_seg->name, ".text", 5))
+ {
+ if (i370_other_section == undefined_section)
+ as_bad (_(".ltorg without prior .using in section %s"),
+ now_seg->name);
+
+ if (i370_other_section != now_seg)
+ as_bad (_(".ltorg in section %s paired to .using in section %s"),
+ now_seg->name, i370_other_section->name);
+ }
+
+ if (! longlong_poolP
+ && ! word_poolP
+ && ! short_poolP
+ && ! byte_poolP)
+ /* Nothing to do. */
+ return;
+
+ /* Find largest literal .. 2 4 or 8. */
+ lit_count = 0;
+ while (lit_count < next_literal_pool_place)
+ {
+ if (biggest_literal_size < literals[lit_count].size)
+ biggest_literal_size = literals[lit_count].size;
+ lit_count ++;
+ }
+ if (1 == biggest_literal_size) biggest_align = 0;
+ else if (2 == biggest_literal_size) biggest_align = 1;
+ else if (4 == biggest_literal_size) biggest_align = 2;
+ else if (8 == biggest_literal_size) biggest_align = 3;
+ else as_bad (_("bad alignment of %d bytes in literal pool"), biggest_literal_size);
+ if (0 == biggest_align) biggest_align = 1;
+
+ /* Align pool for short, word, double word accesses. */
+ frag_align (biggest_align, 0, 0);
+ record_alignment (now_seg, biggest_align);
+
+ /* Note that the gas listing will print only the first five
+ entries in the pool .... wonder how to make it print more. */
+ /* Output largest literals first, then the smaller ones. */
+ for (litsize=8; litsize; litsize /=2)
+ {
+ symbolS *current_poolP = NULL;
+ switch (litsize)
+ {
+ case 8:
+ current_poolP = longlong_poolP; break;
+ case 4:
+ current_poolP = word_poolP; break;
+ case 2:
+ current_poolP = short_poolP; break;
+ case 1:
+ current_poolP = byte_poolP; break;
+ default:
+ as_bad (_("bad literal size\n"));
+ }
+ if (NULL == current_poolP)
+ continue;
+ sprintf (pool_name, ".LITP%01d%06d", litsize, lit_pool_num);
+ symbol_locate (current_poolP, pool_name, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ symbol_table_insert (current_poolP);
+
+ lit_count = 0;
+ while (lit_count < next_literal_pool_place)
+ {
+ if (litsize == literals[lit_count].size)
+ {
+#define EMIT_ADDR_CONS_SYMBOLS
+#ifdef EMIT_ADDR_CONS_SYMBOLS
+ /* Create a bogus symbol, add it to the pool ...
+ For the most part, I think this is a useless exercise,
+ except that having these symbol names in the objects
+ is vaguely useful for debugging. */
+ if (literals[lit_count].sym_name)
+ {
+ symbolS * symP = symbol_make_empty ();
+ symbol_locate (symP, literals[lit_count].sym_name, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ symbol_table_insert (symP);
+ }
+#endif /* EMIT_ADDR_CONS_SYMBOLS */
+
+ emit_expr (&(literals[lit_count].exp), literals[lit_count].size);
+ }
+ lit_count ++;
+ }
+ }
+
+ next_literal_pool_place = 0;
+ longlong_poolP = NULL;
+ word_poolP = NULL;
+ short_poolP = NULL;
+ byte_poolP = NULL;
+ lit_pool_num++;
+}
+
+#endif /* LITERAL_POOL_SUPPORT */
+
+
+/* Add support for the HLASM-like USING directive to indicate
+ the base register to use ... we don't support the full
+ hlasm semantics for this ... we merely pluck a base address
+ and a register number out. We print a warning if using is
+ called multiple times. I suppose we should check to see
+ if the regno is valid. */
+
+static void
+i370_using (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex, baseaddr;
+ int iregno;
+ char *star;
+
+ /* If "*" appears in a using, it means "."
+ replace it with "." so that expr doesn't get confused. */
+ star = strchr (input_line_pointer, '*');
+ if (star)
+ *star = '.';
+
+ /* The first arg to using will usually be ".", but it can
+ be a more complex expression too. */
+ expression (&baseaddr);
+ if (star)
+ *star = '*';
+ if (O_constant != baseaddr.X_op
+ && O_symbol != baseaddr.X_op
+ && O_uminus != baseaddr.X_op)
+ as_bad (_(".using: base address expression illegal or too complex"));
+
+ if (*input_line_pointer != '\0') ++input_line_pointer;
+
+ /* The second arg to using had better be a register. */
+ register_name (&ex);
+ demand_empty_rest_of_line ();
+ iregno = ex.X_add_number;
+
+ if (0 == strncmp (now_seg->name, ".text", 5))
+ {
+ i370_using_text_baseaddr = baseaddr;
+ i370_using_text_regno = iregno;
+ }
+ else
+ {
+ i370_using_other_baseaddr = baseaddr;
+ i370_using_other_regno = iregno;
+ i370_other_section = now_seg;
+ }
+}
+
+static void
+i370_drop (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+ int iregno;
+
+ register_name (&ex);
+ demand_empty_rest_of_line ();
+ iregno = ex.X_add_number;
+
+ if (0 == strncmp (now_seg->name, ".text", 5))
+ {
+ if (iregno != i370_using_text_regno)
+ as_bad (_("droping register %d in section %s does not match using register %d"),
+ iregno, now_seg->name, i370_using_text_regno);
+
+ i370_using_text_regno = -1;
+ i370_using_text_baseaddr.X_op = O_absent;
+ }
+ else
+ {
+ if (iregno != i370_using_other_regno)
+ as_bad (_("droping register %d in section %s does not match using register %d"),
+ iregno, now_seg->name, i370_using_other_regno);
+
+ if (i370_other_section != now_seg)
+ as_bad (_("droping register %d in section %s previously used in section %s"),
+ iregno, now_seg->name, i370_other_section->name);
+
+ i370_using_other_regno = -1;
+ i370_using_other_baseaddr.X_op = O_absent;
+ i370_other_section = undefined_section;
+ }
+}
+
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct i370_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+#define MAX_INSN_FIXUPS 5
+
+/* Handle a macro. Gather all the operands, transform them as
+ described by the macro, and call md_assemble recursively. All the
+ operands are separated by commas; we don't accept parentheses
+ around operands here. */
+
+static void
+i370_macro (char *str, const struct i370_macro *macro)
+{
+ char *operands[10];
+ unsigned int count;
+ char *s;
+ unsigned int len;
+ const char *format;
+ int arg;
+ char *send;
+ char *complete;
+
+ /* Gather the users operands into the operands array. */
+ count = 0;
+ s = str;
+ while (1)
+ {
+ if (count >= sizeof operands / sizeof operands[0])
+ break;
+ operands[count++] = s;
+ s = strchr (s, ',');
+ if (s == (char *) NULL)
+ break;
+ *s++ = '\0';
+ }
+
+ if (count != macro->operands)
+ {
+ as_bad (_("wrong number of operands"));
+ return;
+ }
+
+ /* Work out how large the string must be (the size is unbounded
+ because it includes user input). */
+ len = 0;
+ format = macro->format;
+ while (*format != '\0')
+ {
+ if (*format != '%')
+ {
+ ++len;
+ ++format;
+ }
+ else
+ {
+ arg = strtol (format + 1, &send, 10);
+ know (send != format && arg >= 0 && (unsigned) arg < count);
+ len += strlen (operands[arg]);
+ format = send;
+ }
+ }
+
+ /* Put the string together. */
+ complete = s = alloca (len + 1);
+ format = macro->format;
+ while (*format != '\0')
+ {
+ if (*format != '%')
+ *s++ = *format++;
+ else
+ {
+ arg = strtol (format + 1, &send, 10);
+ strcpy (s, operands[arg]);
+ s += strlen (s);
+ format = send;
+ }
+ }
+ *s = '\0';
+
+ /* Assemble the constructed instruction. */
+ md_assemble (complete);
+}
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (char *str)
+{
+ char *s;
+ const struct i370_opcode *opcode;
+ i370_insn_t insn;
+ const unsigned char *opindex_ptr;
+ int have_optional_index, have_optional_basereg, have_optional_reg;
+ int skip_optional_index, skip_optional_basereg, skip_optional_reg;
+ int use_text=0, use_other=0;
+ int off_by_one;
+ struct i370_fixup fixups[MAX_INSN_FIXUPS];
+ int fc;
+ char *f;
+ int i;
+#ifdef OBJ_ELF
+ bfd_reloc_code_real_type reloc;
+#endif
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct i370_opcode *) hash_find (i370_hash, str);
+ if (opcode == (const struct i370_opcode *) NULL)
+ {
+ const struct i370_macro *macro;
+
+ gas_assert (i370_macro_hash);
+ macro = (const struct i370_macro *) hash_find (i370_macro_hash, str);
+ if (macro == (const struct i370_macro *) NULL)
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ else
+ i370_macro (s, macro);
+
+ return;
+ }
+
+ insn = opcode->opcode;
+
+ str = s;
+ while (ISSPACE (*str))
+ ++str;
+
+ /* I370 operands are either expressions or address constants.
+ Many operand types are optional. The optional operands
+ are always surrounded by parens, and are used to denote the base
+ register ... e.g. "A R1, D2" or "A R1, D2(,B2) as opposed to
+ the fully-formed "A R1, D2(X2,B2)". Note also the = sign,
+ such as A R1,=A(i) where the address-of operator =A implies
+ use of both a base register, and a missing index register.
+
+ So, before we start seriously parsing the operands, we check
+ to see if we have an optional operand, and, if we do, we count
+ the number of commas to see which operand should be omitted. */
+
+ have_optional_index = have_optional_basereg = have_optional_reg = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct i370_operand *operand;
+
+ operand = &i370_operands[*opindex_ptr];
+ if ((operand->flags & I370_OPERAND_INDEX) != 0)
+ have_optional_index = 1;
+ if ((operand->flags & I370_OPERAND_BASE) != 0)
+ have_optional_basereg = 1;
+ if ((operand->flags & I370_OPERAND_OPTIONAL) != 0)
+ have_optional_reg = 1;
+ }
+
+ skip_optional_index = skip_optional_basereg = skip_optional_reg = 0;
+ if (have_optional_index || have_optional_basereg)
+ {
+ unsigned int opcount, nwanted;
+
+ /* There is an optional operand. Count the number of
+ commas and open-parens in the input line. */
+ if (*str == '\0')
+ opcount = 0;
+ else
+ {
+ opcount = 1;
+ s = str;
+ while ((s = strpbrk (s, ",(=")) != (char *) NULL)
+ {
+ ++opcount;
+ ++s;
+ if (',' == *s) ++s; /* avoid counting things like (, */
+ if ('=' == *s) { ++s; --opcount; }
+ }
+ }
+
+ /* If there are fewer operands in the line then are called
+ for by the instruction, we want to skip the optional
+ operand. */
+ nwanted = strlen ((char *) opcode->operands);
+ if (have_optional_index)
+ {
+ if (opcount < nwanted)
+ skip_optional_index = 1;
+ if (have_optional_basereg && ((opcount+1) < nwanted))
+ skip_optional_basereg = 1;
+ if (have_optional_reg && ((opcount+1) < nwanted))
+ skip_optional_reg = 1;
+ }
+ else
+ {
+ if (have_optional_basereg && (opcount < nwanted))
+ skip_optional_basereg = 1;
+ if (have_optional_reg && (opcount < nwanted))
+ skip_optional_reg = 1;
+ }
+ }
+
+ /* Perform some off-by-one hacks on the length field of certain instructions.
+ Its such a shame to have to do this, but the problem is that HLASM got
+ defined so that the lengths differ by one from the actual machine instructions.
+ this code should probably be moved to a special inster-operand routine.
+ Sigh. Affected instructions are Compare Logical, Move and Exclusive OR
+ hack alert -- aren't *all* SS instructions affected ?? */
+ off_by_one = 0;
+ if (0 == strcasecmp ("CLC", opcode->name)
+ || 0 == strcasecmp ("ED", opcode->name)
+ || 0 == strcasecmp ("EDMK", opcode->name)
+ || 0 == strcasecmp ("MVC", opcode->name)
+ || 0 == strcasecmp ("MVCIN", opcode->name)
+ || 0 == strcasecmp ("MVN", opcode->name)
+ || 0 == strcasecmp ("MVZ", opcode->name)
+ || 0 == strcasecmp ("NC", opcode->name)
+ || 0 == strcasecmp ("OC", opcode->name)
+ || 0 == strcasecmp ("XC", opcode->name))
+ off_by_one = 1;
+
+ /* Gather the operands. */
+ fc = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct i370_operand *operand;
+ char *hold;
+ expressionS ex;
+
+ operand = &i370_operands[*opindex_ptr];
+
+ /* If this is an index operand, and we are skipping it,
+ just insert a zero. */
+ if (skip_optional_index &&
+ ((operand->flags & I370_OPERAND_INDEX) != 0))
+ {
+ insn = i370_insert_operand (insn, operand, 0);
+ continue;
+ }
+
+ /* If this is the base operand, and we are skipping it,
+ just insert the current using basreg. */
+ if (skip_optional_basereg &&
+ ((operand->flags & I370_OPERAND_BASE) != 0))
+ {
+ int basereg = -1;
+ if (use_text)
+ {
+ if (0 == strncmp (now_seg->name, ".text", 5)
+ || 0 > i370_using_other_regno)
+ basereg = i370_using_text_regno;
+ else
+ basereg = i370_using_other_regno;
+ }
+ else if (use_other)
+ {
+ if (0 > i370_using_other_regno)
+ basereg = i370_using_text_regno;
+ else
+ basereg = i370_using_other_regno;
+ }
+ if (0 > basereg)
+ as_bad (_("not using any base register"));
+
+ insn = i370_insert_operand (insn, operand, basereg);
+ continue;
+ }
+
+ /* If this is an optional operand, and we are skipping it,
+ Use zero (since a non-zero value would denote a register) */
+ if (skip_optional_reg
+ && ((operand->flags & I370_OPERAND_OPTIONAL) != 0))
+ {
+ insn = i370_insert_operand (insn, operand, 0);
+ continue;
+ }
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ /* Register names are only allowed where there are registers. */
+ if ((operand->flags & I370_OPERAND_GPR) != 0)
+ {
+ /* Quickie hack to get past things like (,r13). */
+ if (skip_optional_index && (',' == *input_line_pointer))
+ {
+ *input_line_pointer = ' ';
+ input_line_pointer ++;
+ }
+
+ if (! register_name (&ex))
+ as_bad (_("expecting a register for operand %d"),
+ (int) (opindex_ptr - opcode->operands + 1));
+ }
+
+ /* Check for an address constant expression. */
+ /* We will put PSW-relative addresses in the text section,
+ and address literals in the .data (or other) section. */
+ else if (i370_addr_cons (&ex))
+ use_other = 1;
+ else if (i370_addr_offset (&ex))
+ use_text = 1;
+ else expression (&ex);
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ /* Perform some off-by-one hacks on the length field of certain instructions.
+ Its such a shame to have to do this, but the problem is that HLASM got
+ defined so that the programmer specifies a length that is one greater
+ than what the machine instruction wants. Sigh. */
+ if (off_by_one && (0 == strcasecmp ("SS L", operand->name)))
+ ex.X_add_number --;
+
+ if (ex.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (ex.X_op == O_absent)
+ as_bad (_("missing operand"));
+ else if (ex.X_op == O_register)
+ insn = i370_insert_operand (insn, operand, ex.X_add_number);
+ else if (ex.X_op == O_constant)
+ {
+#ifdef OBJ_ELF
+ /* Allow @HA, @L, @H on constants.
+ Well actually, no we don't; there really don't make sense
+ (at least not to me) for the i370. However, this code is
+ left here for any dubious future expansion reasons. */
+ char *orig_str = str;
+
+ if ((reloc = i370_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+ switch (reloc)
+ {
+ default:
+ str = orig_str;
+ break;
+
+ case BFD_RELOC_LO16:
+ /* X_unsigned is the default, so if the user has done
+ something which cleared it, we always produce a
+ signed value. */
+ ex.X_add_number = (((ex.X_add_number & 0xffff)
+ ^ 0x8000)
+ - 0x8000);
+ break;
+
+ case BFD_RELOC_HI16:
+ ex.X_add_number = (ex.X_add_number >> 16) & 0xffff;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ ex.X_add_number = (((ex.X_add_number >> 16) & 0xffff)
+ + ((ex.X_add_number >> 15) & 1));
+ break;
+ }
+#endif
+ insn = i370_insert_operand (insn, operand, ex.X_add_number);
+ }
+#ifdef OBJ_ELF
+ else if ((reloc = i370_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+ {
+ as_tsktsk ("md_assemble(): suffixed relocations not supported\n");
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal ("too many fixups");
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = 0;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+#endif /* OBJ_ELF */
+ else
+ {
+ /* We need to generate a fixup for this expression. */
+ /* Typically, the expression will just be a symbol ...
+ printf ("insn %s needs fixup for %s \n",
+ opcode->name, ex.X_add_symbol->bsym->name); */
+
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal ("too many fixups");
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ }
+
+ /* Skip over delimiter (close paren, or comma). */
+ if ((')' == *str) && (',' == *(str+1)))
+ ++str;
+ if (*str != '\0')
+ ++str;
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ /* Write out the instruction. */
+ f = frag_more (opcode->len);
+ if (4 >= opcode->len)
+ md_number_to_chars (f, insn.i[0], opcode->len);
+ else
+ {
+ md_number_to_chars (f, insn.i[0], 4);
+
+ if (6 == opcode->len)
+ md_number_to_chars ((f + 4), ((insn.i[1])>>16), 2);
+ else
+ {
+ /* Not used --- don't have any 8 byte instructions. */
+ as_bad (_("Internal Error: bad instruction length"));
+ md_number_to_chars ((f + 4), insn.i[1], opcode->len -4);
+ }
+ }
+
+ /* Create any fixups. At this point we do not use a
+ bfd_reloc_code_real_type, but instead just use the
+ BFD_RELOC_UNUSED plus the operand index. This lets us easily
+ handle fixups for any operand type, although that is admittedly
+ not a very exciting feature. We pick a BFD reloc type in
+ md_apply_fix. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct i370_operand *operand;
+
+ operand = &i370_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+ int size;
+ fixS *fixP;
+
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size < 1 || size > 4)
+ abort ();
+
+ printf (" gwana doo fixup %d \n", i);
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal, size,
+ &fixups[i].exp, reloc_howto->pc_relative,
+ fixups[i].reloc);
+
+ /* Turn off complaints that the addend is too large for things like
+ foo+100000@ha. */
+ switch (fixups[i].reloc)
+ {
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ fixP->fx_no_overflow = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ fix_new_exp (frag_now, f - frag_now->fr_literal, opcode->len,
+ &fixups[i].exp,
+ (operand->flags & I370_OPERAND_RELATIVE) != 0,
+ ((bfd_reloc_code_real_type)
+ (fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
+ }
+ }
+}
+
+
+/* Pseudo-op handling. */
+
+/* The .byte pseudo-op. This is similar to the normal .byte
+ pseudo-op, but it can also take a single ASCII string. */
+
+static void
+i370_byte (int ignore ATTRIBUTE_UNUSED)
+{
+ if (*input_line_pointer != '\"')
+ {
+ cons (1);
+ return;
+ }
+
+ /* Gather characters. A real double quote is doubled. Unusual
+ characters are not permitted. */
+ ++input_line_pointer;
+ while (1)
+ {
+ char c;
+
+ c = *input_line_pointer++;
+
+ if (c == '\"')
+ {
+ if (*input_line_pointer != '\"')
+ break;
+ ++input_line_pointer;
+ }
+
+ FRAG_APPEND_1_CHAR (c);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .tc pseudo-op. This is used when generating XCOFF and ELF.
+ This takes two or more arguments.
+
+ When generating XCOFF output, the first argument is the name to
+ give to this location in the toc; this will be a symbol with class
+ TC. The rest of the arguments are 4 byte values to actually put at
+ this location in the TOC; often there is just one more argument, a
+ relocatable symbol reference.
+
+ When not generating XCOFF output, the arguments are the same, but
+ the first argument is simply ignored. */
+
+static void
+i370_tc (int ignore ATTRIBUTE_UNUSED)
+{
+
+ /* Skip the TOC symbol name. */
+ while (is_part_of_name (*input_line_pointer)
+ || *input_line_pointer == '['
+ || *input_line_pointer == ']'
+ || *input_line_pointer == '{'
+ || *input_line_pointer == '}')
+ ++input_line_pointer;
+
+ /* Align to a four byte boundary. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+ if (*input_line_pointer != ',')
+ demand_empty_rest_of_line ();
+ else
+ {
+ ++input_line_pointer;
+ cons (4);
+ }
+}
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ /* 360/370/390 have two float formats: an old, funky 360 single-precision
+ format, and the ieee format. Support only the ieee format. */
+ return ieee_md_atof (type, litp, sizep, TRUE);
+}
+
+/* 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_bigendian (buf, val, n);
+}
+
+/* Align a section (I don't know why this is machine dependent). */
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return (addr + (1 << align) - 1) & (-1 << align);
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* 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 ATTRIBUTE_UNUSED)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the call to fix_new_exp, above. In the call
+ above we used a reloc code which was the largest legal reloc code
+ plus the operand index. Here we undo that to recover the operand
+ index. At this point all symbol values should be fully resolved,
+ and we attempt to completely resolve the reloc. If we can not do
+ that, we determine the correct reloc code and put it back in the
+ fixup.
+
+ See gas/cgen.c for more sample code and explanations of what's
+ going on here. */
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg)
+{
+ valueT value = * valP;
+
+ if (fixP->fx_addsy != NULL)
+ {
+#ifdef DEBUG
+ printf ("\nmd_apply_fix: symbol %s at 0x%x (%s:%d) val=0x%x addend=0x%x\n",
+ S_GET_NAME (fixP->fx_addsy),
+ fixP->fx_frag->fr_address + fixP->fx_where,
+ fixP->fx_file, fixP->fx_line,
+ S_GET_VALUE (fixP->fx_addsy), value);
+#endif
+ }
+ else
+ fixP->fx_done = 1;
+
+ /* Apply fixups to operands. Note that there should be no relocations
+ for any operands, since no instruction ever takes an operand
+ that requires reloc. */
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex;
+ const struct i370_operand *operand;
+ char *where;
+ i370_insn_t insn;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+
+ operand = &i370_operands[opindex];
+
+#ifdef DEBUG
+ printf ("\nmd_apply_fix: fixup operand %s at 0x%x in %s:%d addend=0x%x\n",
+ operand->name,
+ fixP->fx_frag->fr_address + fixP->fx_where,
+ fixP->fx_file, fixP->fx_line,
+ value);
+#endif
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again.
+ fisxp->fx_size is the length of the instruction. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ insn.i[0] = bfd_getb32 ((unsigned char *) where);
+
+ if (6 <= fixP->fx_size)
+ /* Deal with 48-bit insn's. */
+ insn.i[1] = bfd_getb32 (((unsigned char *) where)+4);
+
+ insn = i370_insert_operand (insn, operand, (offsetT) value);
+ bfd_putb32 ((bfd_vma) insn.i[0], (unsigned char *) where);
+
+ if (6 <= fixP->fx_size)
+ /* Deal with 48-bit insn's. */
+ bfd_putb32 ((bfd_vma) insn.i[1], (((unsigned char *) where)+4));
+
+ /* We are done, right? right !! */
+ fixP->fx_done = 1;
+ if (fixP->fx_done)
+ /* Nothing else to do here. */
+ return;
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into
+ relocs. In fact, we support *zero* operand relocations ...
+ Why? Because we are not expecting the compiler to generate
+ any operands that need relocation. Due to the 12-bit naturew of
+ i370 addressing, this would be unusual. */
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an expression
+ symbol. */
+ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "unresolved expression that must be resolved");
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "unsupported relocation type");
+ fixP->fx_done = 1;
+ return;
+ }
+ }
+ else
+ {
+ /* We branch to here if the fixup is not to a symbol that
+ appears in an instruction operand, but is rather some
+ declared storage. */
+#ifdef OBJ_ELF
+ i370_elf_validate_fix (fixP, seg);
+#endif
+#ifdef DEBUG
+ printf ("md_apply_fix: reloc case %d in segment %s %s:%d\n",
+ fixP->fx_r_type, segment_name (seg), fixP->fx_file, fixP->fx_line);
+ printf ("\tcurrent fixup value is 0x%x \n", value);
+#endif
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ case BFD_RELOC_CTOR:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ /* Fall through. */
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_32_BASEREL:
+#ifdef DEBUG
+ printf ("\t32 bit relocation at 0x%x\n",
+ fixP->fx_frag->fr_address + fixP->fx_where);
+#endif
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 4);
+ break;
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_16:
+ if (fixP->fx_pcrel)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "cannot emit PC relative %s relocation%s%s",
+ bfd_get_reloc_code_name (fixP->fx_r_type),
+ fixP->fx_addsy != NULL ? " against " : "",
+ (fixP->fx_addsy != NULL
+ ? S_GET_NAME (fixP->fx_addsy)
+ : ""));
+
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 2);
+ break;
+
+ /* This case happens when you write, for example,
+ lis %r3,(L1-L2)@ha
+ where L1 and L2 are defined later. */
+ case BFD_RELOC_HI16:
+ if (fixP->fx_pcrel)
+ abort ();
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value >> 16, 2);
+ break;
+ case BFD_RELOC_HI16_S:
+ if (fixP->fx_pcrel)
+ abort ();
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ (value + 0x8000) >> 16, 2);
+ break;
+
+ case BFD_RELOC_8:
+ if (fixP->fx_pcrel)
+ abort ();
+
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ value, 1);
+ break;
+
+ default:
+ fprintf (stderr,
+ "Gas failure, reloc value %d\n", fixP->fx_r_type);
+ fflush (stderr);
+ abort ();
+ }
+ }
+
+ fixP->fx_addnumber = value;
+}
+
+/* Generate a reloc for a fixup. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "reloc %d not supported by object file format", (int)fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_addnumber;
+
+#ifdef DEBUG
+ printf ("\ngen_reloc(): sym %s (%s:%d) at addr 0x%x addend=0x%x\n",
+ fixp->fx_addsy->bsym->name,
+ fixp->fx_file, fixp->fx_line,
+ reloc->address, reloc->addend);
+#endif
+
+ return reloc;
+}
+
+/* The target specific pseudo-ops which we support. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Pseudo-ops which must be overridden. */
+ { "byte", i370_byte, 0 },
+
+ { "dc", i370_dc, 0 },
+ { "ds", i370_ds, 0 },
+ { "rmode", i370_rmode, 0 },
+ { "csect", i370_csect, 0 },
+ { "dsect", i370_dsect, 0 },
+
+ /* enable ebcdic strings e.g. for 3270 support */
+ { "ebcdic", i370_ebcdic, 0 },
+
+#ifdef OBJ_ELF
+ { "long", i370_elf_cons, 4 },
+ { "word", i370_elf_cons, 4 },
+ { "short", i370_elf_cons, 2 },
+ { "rdata", i370_elf_rdata, 0 },
+ { "rodata", i370_elf_rdata, 0 },
+ { "lcomm", i370_elf_lcomm, 0 },
+#endif
+
+ /* This pseudo-op is used even when not generating XCOFF output. */
+ { "tc", i370_tc, 0 },
+
+ /* dump the literal pool */
+ { "ltorg", i370_ltorg, 0 },
+
+ /* support the hlasm-style USING directive */
+ { "using", i370_using, 0 },
+ { "drop", i370_drop, 0 },
+
+ { NULL, NULL, 0 }
+};
diff --git a/gas/config/tc-i370.h b/gas/config/tc-i370.h
new file mode 100644
index 0000000..6ee29a3
--- /dev/null
+++ b/gas/config/tc-i370.h
@@ -0,0 +1,63 @@
+/* tc-i370.h -- Header file for tc-i370.c.
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_I370
+
+struct fix;
+
+/* Set the endianness we are using. Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH (i370_arch ())
+extern enum bfd_architecture i370_arch (void);
+
+/* Whether or not the target is big endian. */
+extern int target_big_endian;
+
+/* The target BFD format. */
+#define TARGET_FORMAT ("elf32-i370")
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* $ is used to refer to the current location. */
+/* #define DOLLAR_DOT */
+
+/* foo-. gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Call md_pcrel_from_section, not md_pcrel_from. */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define md_operand(x)
+
+#define tc_comment_chars i370_comment_chars
+extern const char *i370_comment_chars;
diff --git a/gas/config/tc-i386-intel.c b/gas/config/tc-i386-intel.c
new file mode 100644
index 0000000..86b96eb
--- /dev/null
+++ b/gas/config/tc-i386-intel.c
@@ -0,0 +1,1002 @@
+/* tc-i386.c -- Assemble Intel syntax code for ix86/x86-64
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+static struct
+ {
+ operatorT op_modifier; /* Operand modifier. */
+ int is_mem; /* 1 if operand is memory reference. */
+ int is_indirect; /* 1 if operand is indirect reference. */
+ int has_offset; /* 1 if operand has offset. */
+ unsigned int in_offset; /* >=1 if processing operand of offset. */
+ unsigned int in_bracket; /* >=1 if processing operand in brackets. */
+ unsigned int in_scale; /* >=1 if processing multipication operand
+ * in brackets. */
+ i386_operand_type reloc_types; /* Value obtained from lex_got(). */
+ const reg_entry *base; /* Base register (if any). */
+ const reg_entry *index; /* Index register (if any). */
+ offsetT scale_factor; /* Accumulated scale factor. */
+ symbolS *seg;
+ }
+intel_state;
+
+/* offset X_add_symbol */
+#define O_offset O_md32
+/* offset X_add_symbol */
+#define O_short O_md31
+/* near ptr X_add_symbol */
+#define O_near_ptr O_md30
+/* far ptr X_add_symbol */
+#define O_far_ptr O_md29
+/* byte ptr X_add_symbol */
+#define O_byte_ptr O_md28
+/* word ptr X_add_symbol */
+#define O_word_ptr O_md27
+/* dword ptr X_add_symbol */
+#define O_dword_ptr O_md26
+/* qword ptr X_add_symbol */
+#define O_qword_ptr O_md25
+/* oword ptr X_add_symbol */
+#define O_oword_ptr O_md24
+/* fword ptr X_add_symbol */
+#define O_fword_ptr O_md23
+/* tbyte ptr X_add_symbol */
+#define O_tbyte_ptr O_md22
+/* xmmword ptr X_add_symbol */
+#define O_xmmword_ptr O_md21
+/* ymmword ptr X_add_symbol */
+#define O_ymmword_ptr O_md20
+/* zmmword ptr X_add_symbol */
+#define O_zmmword_ptr O_md19
+
+static struct
+ {
+ const char *name;
+ operatorT op;
+ unsigned int operands;
+ }
+const i386_operators[] =
+ {
+ { "and", O_bit_and, 2 },
+ { "eq", O_eq, 2 },
+ { "ge", O_ge, 2 },
+ { "gt", O_gt, 2 },
+ { "le", O_le, 2 },
+ { "lt", O_lt, 2 },
+ { "mod", O_modulus, 2 },
+ { "ne", O_ne, 2 },
+ { "not", O_bit_not, 1 },
+ { "offset", O_offset, 1 },
+ { "or", O_bit_inclusive_or, 2 },
+ { "shl", O_left_shift, 2 },
+ { "short", O_short, 1 },
+ { "shr", O_right_shift, 2 },
+ { "xor", O_bit_exclusive_or, 2 },
+ { NULL, O_illegal, 0 }
+ };
+
+static struct
+ {
+ const char *name;
+ operatorT op;
+ unsigned short sz[3];
+ }
+const i386_types[] =
+ {
+#define I386_TYPE(t, n) { #t, O_##t##_ptr, { n, n, n } }
+ I386_TYPE(byte, 1),
+ I386_TYPE(word, 2),
+ I386_TYPE(dword, 4),
+ I386_TYPE(fword, 6),
+ I386_TYPE(qword, 8),
+ I386_TYPE(tbyte, 10),
+ I386_TYPE(oword, 16),
+ I386_TYPE(xmmword, 16),
+ I386_TYPE(ymmword, 32),
+ I386_TYPE(zmmword, 64),
+#undef I386_TYPE
+ { "near", O_near_ptr, { 0xff04, 0xff02, 0xff08 } },
+ { "far", O_far_ptr, { 0xff06, 0xff05, 0xff06 } },
+ { NULL, O_illegal, { 0, 0, 0 } }
+ };
+
+operatorT i386_operator (const char *name, unsigned int operands, char *pc)
+{
+ unsigned int j;
+
+ if (!intel_syntax)
+ return O_absent;
+
+ if (!name)
+ {
+ if (operands != 2)
+ return O_illegal;
+ switch (*input_line_pointer)
+ {
+ case ':':
+ ++input_line_pointer;
+ return O_full_ptr;
+ case '[':
+ ++input_line_pointer;
+ return O_index;
+ case '@':
+ if (this_operand >= 0 && i.reloc[this_operand] == NO_RELOC)
+ {
+ int adjust = 0;
+ char *gotfree_input_line = lex_got (&i.reloc[this_operand],
+ &adjust,
+ &intel_state.reloc_types);
+
+ if (!gotfree_input_line)
+ break;
+ free (gotfree_input_line);
+ *input_line_pointer++ = '+';
+ memset (input_line_pointer, '0', adjust - 1);
+ input_line_pointer[adjust - 1] = ' ';
+ return O_add;
+ }
+ break;
+ }
+ return O_illegal;
+ }
+
+ for (j = 0; i386_operators[j].name; ++j)
+ if (strcasecmp (i386_operators[j].name, name) == 0)
+ {
+ if (i386_operators[j].operands
+ && i386_operators[j].operands != operands)
+ return O_illegal;
+ return i386_operators[j].op;
+ }
+
+ for (j = 0; i386_types[j].name; ++j)
+ if (strcasecmp (i386_types[j].name, name) == 0)
+ break;
+ if (i386_types[j].name && *pc == ' ')
+ {
+ char *pname = ++input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (pname, "ptr") == 0)
+ {
+ pname[-1] = *pc;
+ *pc = c;
+ if (intel_syntax > 0 || operands != 1)
+ return O_illegal;
+ return i386_types[j].op;
+ }
+
+ *input_line_pointer = c;
+ input_line_pointer = pname - 1;
+ }
+
+ return O_absent;
+}
+
+static int i386_intel_parse_name (const char *name, expressionS *e)
+{
+ unsigned int j;
+
+ if (! strcmp (name, "$"))
+ {
+ current_location (e);
+ return 1;
+ }
+
+ for (j = 0; i386_types[j].name; ++j)
+ if (strcasecmp(i386_types[j].name, name) == 0)
+ {
+ e->X_op = O_constant;
+ e->X_add_number = i386_types[j].sz[flag_code];
+ e->X_add_symbol = NULL;
+ e->X_op_symbol = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
+static INLINE int i386_intel_check (const reg_entry *rreg,
+ const reg_entry *base,
+ const reg_entry *iindex)
+{
+ if ((this_operand >= 0
+ && rreg != i.op[this_operand].regs)
+ || base != intel_state.base
+ || iindex != intel_state.index)
+ {
+ as_bad (_("invalid use of register"));
+ return 0;
+ }
+ return 1;
+}
+
+static INLINE void i386_intel_fold (expressionS *e, symbolS *sym)
+{
+ expressionS *exp = symbol_get_value_expression (sym);
+ if (S_GET_SEGMENT (sym) == absolute_section)
+ {
+ offsetT val = e->X_add_number;
+
+ *e = *exp;
+ e->X_add_number += val;
+ }
+ else
+ {
+ if (exp->X_op == O_symbol
+ && strcmp (S_GET_NAME (exp->X_add_symbol),
+ GLOBAL_OFFSET_TABLE_NAME) == 0)
+ sym = exp->X_add_symbol;
+ e->X_add_symbol = sym;
+ e->X_op_symbol = NULL;
+ e->X_op = O_symbol;
+ }
+}
+
+static int
+i386_intel_simplify_register (expressionS *e)
+{
+ int reg_num;
+
+ if (this_operand < 0 || intel_state.in_offset)
+ {
+ as_bad (_("invalid use of register"));
+ return 0;
+ }
+
+ if (e->X_op == O_register)
+ reg_num = e->X_add_number;
+ else
+ reg_num = e->X_md - 1;
+
+ if (!intel_state.in_bracket)
+ {
+ if (i.op[this_operand].regs)
+ {
+ as_bad (_("invalid use of register"));
+ return 0;
+ }
+ if (i386_regtab[reg_num].reg_type.bitfield.sreg3
+ && i386_regtab[reg_num].reg_num == RegFlat)
+ {
+ as_bad (_("invalid use of pseudo-register"));
+ return 0;
+ }
+ i.op[this_operand].regs = i386_regtab + reg_num;
+ }
+ else if (!intel_state.index
+ && (i386_regtab[reg_num].reg_type.bitfield.regxmm
+ || i386_regtab[reg_num].reg_type.bitfield.regymm
+ || i386_regtab[reg_num].reg_type.bitfield.regzmm))
+ intel_state.index = i386_regtab + reg_num;
+ else if (!intel_state.base && !intel_state.in_scale)
+ intel_state.base = i386_regtab + reg_num;
+ else if (!intel_state.index)
+ {
+ if (intel_state.in_scale
+ || current_templates->start->base_opcode == 0xf30f1b /* bndmk */
+ || (current_templates->start->base_opcode & ~1) == 0x0f1a /* bnd{ld,st}x */
+ || i386_regtab[reg_num].reg_type.bitfield.baseindex)
+ intel_state.index = i386_regtab + reg_num;
+ else
+ {
+ /* Convert base to index and make ESP/RSP the base. */
+ intel_state.index = intel_state.base;
+ intel_state.base = i386_regtab + reg_num;
+ }
+ }
+ else
+ {
+ /* esp is invalid as index */
+ intel_state.index = i386_regtab + REGNAM_EAX + ESP_REG_NUM;
+ }
+ return 2;
+}
+
+static int i386_intel_simplify (expressionS *);
+
+static INLINE int i386_intel_simplify_symbol(symbolS *sym)
+{
+ int ret = i386_intel_simplify (symbol_get_value_expression (sym));
+
+ if (ret == 2)
+ {
+ S_SET_SEGMENT(sym, absolute_section);
+ ret = 1;
+ }
+ return ret;
+}
+
+static int i386_intel_simplify (expressionS *e)
+{
+ const reg_entry *the_reg = (this_operand >= 0
+ ? i.op[this_operand].regs : NULL);
+ const reg_entry *base = intel_state.base;
+ const reg_entry *state_index = intel_state.index;
+ int ret;
+
+ if (!intel_syntax)
+ return 1;
+
+ switch (e->X_op)
+ {
+ case O_index:
+ if (e->X_add_symbol)
+ {
+ if (!i386_intel_simplify_symbol (e->X_add_symbol)
+ || !i386_intel_check(the_reg, intel_state.base,
+ intel_state.index))
+ return 0;
+ }
+ if (!intel_state.in_offset)
+ ++intel_state.in_bracket;
+ ret = i386_intel_simplify_symbol (e->X_op_symbol);
+ if (!intel_state.in_offset)
+ --intel_state.in_bracket;
+ if (!ret)
+ return 0;
+ if (e->X_add_symbol)
+ e->X_op = O_add;
+ else
+ i386_intel_fold (e, e->X_op_symbol);
+ break;
+
+ case O_offset:
+ intel_state.has_offset = 1;
+ ++intel_state.in_offset;
+ ret = i386_intel_simplify_symbol (e->X_add_symbol);
+ --intel_state.in_offset;
+ if (!ret || !i386_intel_check(the_reg, base, state_index))
+ return 0;
+ i386_intel_fold (e, e->X_add_symbol);
+ return ret;
+
+ case O_byte_ptr:
+ case O_word_ptr:
+ case O_dword_ptr:
+ case O_fword_ptr:
+ case O_qword_ptr:
+ case O_tbyte_ptr:
+ case O_oword_ptr:
+ case O_xmmword_ptr:
+ case O_ymmword_ptr:
+ case O_zmmword_ptr:
+ case O_near_ptr:
+ case O_far_ptr:
+ if (intel_state.op_modifier == O_absent)
+ intel_state.op_modifier = e->X_op;
+ /* FALLTHROUGH */
+ case O_short:
+ if (symbol_get_value_expression (e->X_add_symbol)->X_op
+ == O_register)
+ {
+ as_bad (_("invalid use of register"));
+ return 0;
+ }
+ if (!i386_intel_simplify_symbol (e->X_add_symbol))
+ return 0;
+ i386_intel_fold (e, e->X_add_symbol);
+ break;
+
+ case O_full_ptr:
+ if (symbol_get_value_expression (e->X_op_symbol)->X_op
+ == O_register)
+ {
+ as_bad (_("invalid use of register"));
+ return 0;
+ }
+ if (!i386_intel_simplify_symbol (e->X_op_symbol)
+ || !i386_intel_check(the_reg, intel_state.base,
+ intel_state.index))
+ return 0;
+ if (!intel_state.in_offset)
+ intel_state.seg = e->X_add_symbol;
+ i386_intel_fold (e, e->X_op_symbol);
+ break;
+
+ case O_multiply:
+ if (this_operand >= 0 && intel_state.in_bracket)
+ {
+ expressionS *scale = NULL;
+ int has_index = (intel_state.index != NULL);
+
+ if (!intel_state.in_scale++)
+ intel_state.scale_factor = 1;
+
+ ret = i386_intel_simplify_symbol (e->X_add_symbol);
+ if (ret && !has_index && intel_state.index)
+ scale = symbol_get_value_expression (e->X_op_symbol);
+
+ if (ret)
+ ret = i386_intel_simplify_symbol (e->X_op_symbol);
+ if (ret && !scale && !has_index && intel_state.index)
+ scale = symbol_get_value_expression (e->X_add_symbol);
+
+ if (ret && scale)
+ {
+ resolve_expression (scale);
+ if (scale->X_op != O_constant
+ || intel_state.index->reg_type.bitfield.reg16)
+ scale->X_add_number = 0;
+ intel_state.scale_factor *= scale->X_add_number;
+ }
+
+ --intel_state.in_scale;
+ if (!ret)
+ return 0;
+
+ if (!intel_state.in_scale)
+ switch (intel_state.scale_factor)
+ {
+ case 1:
+ i.log2_scale_factor = 0;
+ break;
+ case 2:
+ i.log2_scale_factor = 1;
+ break;
+ case 4:
+ i.log2_scale_factor = 2;
+ break;
+ case 8:
+ i.log2_scale_factor = 3;
+ break;
+ default:
+ /* esp is invalid as index */
+ intel_state.index = i386_regtab + REGNAM_EAX + ESP_REG_NUM;
+ break;
+ }
+
+ break;
+ }
+ goto fallthrough;
+
+ case O_register:
+ ret = i386_intel_simplify_register (e);
+ if (ret == 2)
+ {
+ gas_assert (e->X_add_number < (unsigned short) -1);
+ e->X_md = (unsigned short) e->X_add_number + 1;
+ e->X_op = O_constant;
+ e->X_add_number = 0;
+ }
+ return ret;
+
+ case O_constant:
+ if (e->X_md)
+ return i386_intel_simplify_register (e);
+
+ /* FALLTHROUGH */
+ default:
+fallthrough:
+ if (e->X_add_symbol
+ && !i386_intel_simplify_symbol (e->X_add_symbol))
+ return 0;
+ if (e->X_op == O_add || e->X_op == O_subtract)
+ {
+ base = intel_state.base;
+ state_index = intel_state.index;
+ }
+ if (!i386_intel_check (the_reg, base, state_index)
+ || (e->X_op_symbol
+ && !i386_intel_simplify_symbol (e->X_op_symbol))
+ || !i386_intel_check (the_reg,
+ (e->X_op != O_add
+ ? base : intel_state.base),
+ (e->X_op != O_add
+ ? state_index : intel_state.index)))
+ return 0;
+ break;
+ }
+
+ if (this_operand >= 0
+ && e->X_op == O_symbol
+ && !intel_state.in_offset)
+ {
+ segT seg = S_GET_SEGMENT (e->X_add_symbol);
+
+ if (seg != absolute_section
+ && seg != reg_section
+ && seg != expr_section)
+ intel_state.is_mem |= 2 - !intel_state.in_bracket;
+ }
+
+ return 1;
+}
+
+int i386_need_index_operator (void)
+{
+ return intel_syntax < 0;
+}
+
+static int
+i386_intel_operand (char *operand_string, int got_a_float)
+{
+ char *saved_input_line_pointer, *buf;
+ segT exp_seg;
+ expressionS exp, *expP;
+ char suffix = 0;
+ int ret;
+
+ /* Handle vector immediates. */
+ if (RC_SAE_immediate (operand_string))
+ return 1;
+
+ /* Initialize state structure. */
+ intel_state.op_modifier = O_absent;
+ intel_state.is_mem = 0;
+ intel_state.is_indirect = 0;
+ intel_state.has_offset = 0;
+ intel_state.base = NULL;
+ intel_state.index = NULL;
+ intel_state.seg = NULL;
+ operand_type_set (&intel_state.reloc_types, ~0);
+ gas_assert (!intel_state.in_offset);
+ gas_assert (!intel_state.in_bracket);
+ gas_assert (!intel_state.in_scale);
+
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = buf = xstrdup (operand_string);
+
+ intel_syntax = -1;
+ memset (&exp, 0, sizeof(exp));
+ exp_seg = expression (&exp);
+ ret = i386_intel_simplify (&exp);
+ intel_syntax = 1;
+
+ SKIP_WHITESPACE ();
+
+ /* Handle vector operations. */
+ if (*input_line_pointer == '{')
+ {
+ char *end = check_VecOperations (input_line_pointer, NULL);
+ if (end)
+ input_line_pointer = end;
+ else
+ ret = 0;
+ }
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ as_bad (_("junk `%s' after expression"), input_line_pointer);
+ ret = 0;
+ }
+ else if (exp.X_op == O_illegal || exp.X_op == O_absent)
+ {
+ as_bad (_("invalid expression"));
+ ret = 0;
+ }
+ else if (!intel_state.has_offset
+ && input_line_pointer > buf
+ && *(input_line_pointer - 1) == ']')
+ {
+ intel_state.is_mem |= 1;
+ intel_state.is_indirect = 1;
+ }
+
+ input_line_pointer = saved_input_line_pointer;
+ free (buf);
+
+ gas_assert (!intel_state.in_offset);
+ gas_assert (!intel_state.in_bracket);
+ gas_assert (!intel_state.in_scale);
+
+ if (!ret)
+ return 0;
+
+ if (intel_state.op_modifier != O_absent
+ && current_templates->start->base_opcode != 0x8d /* lea */)
+ {
+ i.types[this_operand].bitfield.unspecified = 0;
+
+ switch (intel_state.op_modifier)
+ {
+ case O_byte_ptr:
+ i.types[this_operand].bitfield.byte = 1;
+ suffix = BYTE_MNEM_SUFFIX;
+ break;
+
+ case O_word_ptr:
+ i.types[this_operand].bitfield.word = 1;
+ if ((current_templates->start->name[0] == 'l'
+ && current_templates->start->name[2] == 's'
+ && current_templates->start->name[3] == 0)
+ || current_templates->start->base_opcode == 0x62 /* bound */)
+ suffix = BYTE_MNEM_SUFFIX; /* so it will cause an error */
+ else if (got_a_float == 2) /* "fi..." */
+ suffix = SHORT_MNEM_SUFFIX;
+ else
+ suffix = WORD_MNEM_SUFFIX;
+ break;
+
+ case O_dword_ptr:
+ i.types[this_operand].bitfield.dword = 1;
+ if ((current_templates->start->name[0] == 'l'
+ && current_templates->start->name[2] == 's'
+ && current_templates->start->name[3] == 0)
+ || current_templates->start->base_opcode == 0x62 /* bound */)
+ suffix = WORD_MNEM_SUFFIX;
+ else if (flag_code == CODE_16BIT
+ && (current_templates->start->opcode_modifier.jump
+ || current_templates->start->opcode_modifier.jumpdword))
+ suffix = LONG_DOUBLE_MNEM_SUFFIX;
+ else if (got_a_float == 1) /* "f..." */
+ suffix = SHORT_MNEM_SUFFIX;
+ else
+ suffix = LONG_MNEM_SUFFIX;
+ break;
+
+ case O_fword_ptr:
+ i.types[this_operand].bitfield.fword = 1;
+ if (current_templates->start->name[0] == 'l'
+ && current_templates->start->name[2] == 's'
+ && current_templates->start->name[3] == 0)
+ suffix = LONG_MNEM_SUFFIX;
+ else if (!got_a_float)
+ {
+ if (flag_code == CODE_16BIT)
+ add_prefix (DATA_PREFIX_OPCODE);
+ suffix = LONG_DOUBLE_MNEM_SUFFIX;
+ }
+ else
+ suffix = BYTE_MNEM_SUFFIX; /* so it will cause an error */
+ break;
+
+ case O_qword_ptr:
+ i.types[this_operand].bitfield.qword = 1;
+ if (current_templates->start->base_opcode == 0x62 /* bound */
+ || got_a_float == 1) /* "f..." */
+ suffix = LONG_MNEM_SUFFIX;
+ else
+ suffix = QWORD_MNEM_SUFFIX;
+ break;
+
+ case O_tbyte_ptr:
+ i.types[this_operand].bitfield.tbyte = 1;
+ if (got_a_float == 1)
+ suffix = LONG_DOUBLE_MNEM_SUFFIX;
+ else
+ suffix = BYTE_MNEM_SUFFIX; /* so it will cause an error */
+ break;
+
+ case O_oword_ptr:
+ case O_xmmword_ptr:
+ i.types[this_operand].bitfield.xmmword = 1;
+ suffix = XMMWORD_MNEM_SUFFIX;
+ break;
+
+ case O_ymmword_ptr:
+ i.types[this_operand].bitfield.ymmword = 1;
+ suffix = YMMWORD_MNEM_SUFFIX;
+ break;
+
+ case O_zmmword_ptr:
+ i.types[this_operand].bitfield.zmmword = 1;
+ suffix = ZMMWORD_MNEM_SUFFIX;
+ break;
+
+ case O_far_ptr:
+ suffix = LONG_DOUBLE_MNEM_SUFFIX;
+ /* FALLTHROUGH */
+ case O_near_ptr:
+ if (!current_templates->start->opcode_modifier.jump
+ && !current_templates->start->opcode_modifier.jumpdword)
+ suffix = got_a_float /* so it will cause an error */
+ ? BYTE_MNEM_SUFFIX
+ : LONG_DOUBLE_MNEM_SUFFIX;
+ break;
+
+ default:
+ BAD_CASE (intel_state.op_modifier);
+ break;
+ }
+
+ if (!i.suffix)
+ i.suffix = suffix;
+ else if (i.suffix != suffix)
+ {
+ as_bad (_("conflicting operand size modifiers"));
+ return 0;
+ }
+ }
+
+ /* Operands for jump/call need special consideration. */
+ if (current_templates->start->opcode_modifier.jump
+ || current_templates->start->opcode_modifier.jumpdword
+ || current_templates->start->opcode_modifier.jumpintersegment)
+ {
+ if (i.op[this_operand].regs
+ || intel_state.base
+ || intel_state.index
+ || intel_state.is_mem > 1)
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ else
+ switch (intel_state.op_modifier)
+ {
+ case O_near_ptr:
+ if (intel_state.seg)
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ else
+ intel_state.is_mem = 1;
+ break;
+ case O_far_ptr:
+ case O_absent:
+ if (!intel_state.seg)
+ {
+ intel_state.is_mem = 1;
+ if (intel_state.op_modifier == O_absent)
+ {
+ if (intel_state.is_indirect == 1)
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ break;
+ }
+ as_bad (_("cannot infer the segment part of the operand"));
+ return 0;
+ }
+ else if (S_GET_SEGMENT (intel_state.seg) == reg_section)
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ else
+ {
+ i386_operand_type types;
+
+ if (i.imm_operands >= MAX_IMMEDIATE_OPERANDS)
+ {
+ as_bad (_("at most %d immediate operands are allowed"),
+ MAX_IMMEDIATE_OPERANDS);
+ return 0;
+ }
+ expP = &im_expressions[i.imm_operands++];
+ memset (expP, 0, sizeof(*expP));
+ expP->X_op = O_symbol;
+ expP->X_add_symbol = intel_state.seg;
+ i.op[this_operand].imms = expP;
+
+ resolve_expression (expP);
+ operand_type_set (&types, ~0);
+ if (!i386_finalize_immediate (S_GET_SEGMENT (intel_state.seg),
+ expP, types, operand_string))
+ return 0;
+ if (i.operands < MAX_OPERANDS)
+ {
+ this_operand = i.operands++;
+ i.types[this_operand].bitfield.unspecified = 1;
+ }
+ if (suffix == LONG_DOUBLE_MNEM_SUFFIX)
+ i.suffix = 0;
+ intel_state.seg = NULL;
+ intel_state.is_mem = 0;
+ }
+ break;
+ default:
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ break;
+ }
+ if (i.types[this_operand].bitfield.jumpabsolute)
+ intel_state.is_mem |= 1;
+ }
+ else if (intel_state.seg)
+ intel_state.is_mem |= 1;
+
+ if (i.op[this_operand].regs)
+ {
+ i386_operand_type temp;
+
+ /* Register operand. */
+ if (intel_state.base || intel_state.index || intel_state.seg)
+ {
+ as_bad (_("invalid operand"));
+ return 0;
+ }
+
+ temp = i.op[this_operand].regs->reg_type;
+ temp.bitfield.baseindex = 0;
+ i.types[this_operand] = operand_type_or (i.types[this_operand],
+ temp);
+ i.types[this_operand].bitfield.unspecified = 0;
+ ++i.reg_operands;
+ }
+ else if (intel_state.base
+ || intel_state.index
+ || intel_state.seg
+ || intel_state.is_mem)
+ {
+ /* Memory operand. */
+ if ((int) i.mem_operands
+ >= 2 - !current_templates->start->opcode_modifier.isstring)
+ {
+ /* Handle
+
+ call 0x9090,0x90909090
+ lcall 0x9090,0x90909090
+ jmp 0x9090,0x90909090
+ ljmp 0x9090,0x90909090
+ */
+
+ if ((current_templates->start->opcode_modifier.jumpintersegment
+ || current_templates->start->opcode_modifier.jumpdword
+ || current_templates->start->opcode_modifier.jump)
+ && this_operand == 1
+ && intel_state.seg == NULL
+ && i.mem_operands == 1
+ && i.disp_operands == 1
+ && intel_state.op_modifier == O_absent)
+ {
+ /* Try to process the first operand as immediate, */
+ this_operand = 0;
+ if (i386_finalize_immediate (exp_seg, i.op[0].imms,
+ intel_state.reloc_types,
+ NULL))
+ {
+ this_operand = 1;
+ expP = &im_expressions[0];
+ i.op[this_operand].imms = expP;
+ *expP = exp;
+
+ /* Try to process the second operand as immediate, */
+ if (i386_finalize_immediate (exp_seg, expP,
+ intel_state.reloc_types,
+ NULL))
+ {
+ i.mem_operands = 0;
+ i.disp_operands = 0;
+ i.imm_operands = 2;
+ i.types[0].bitfield.mem = 0;
+ i.types[0].bitfield.disp16 = 0;
+ i.types[0].bitfield.disp32 = 0;
+ i.types[0].bitfield.disp32s = 0;
+ return 1;
+ }
+ }
+ }
+
+ as_bad (_("too many memory references for `%s'"),
+ current_templates->start->name);
+ return 0;
+ }
+
+ expP = &disp_expressions[i.disp_operands];
+ memcpy (expP, &exp, sizeof(exp));
+ resolve_expression (expP);
+
+ if (expP->X_op != O_constant
+ || expP->X_add_number
+ || (!intel_state.base
+ && !intel_state.index))
+ {
+ i.op[this_operand].disps = expP;
+ i.disp_operands++;
+
+ if (flag_code == CODE_64BIT)
+ {
+ i.types[this_operand].bitfield.disp32 = 1;
+ if (!i.prefix[ADDR_PREFIX])
+ {
+ i.types[this_operand].bitfield.disp64 = 1;
+ i.types[this_operand].bitfield.disp32s = 1;
+ }
+ }
+ else if (!i.prefix[ADDR_PREFIX] ^ (flag_code == CODE_16BIT))
+ i.types[this_operand].bitfield.disp32 = 1;
+ else
+ i.types[this_operand].bitfield.disp16 = 1;
+
+#if defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)
+ /*
+ * exp_seg is used only for verification in
+ * i386_finalize_displacement, and we can end up seeing reg_section
+ * here - but we know we removed all registers from the expression
+ * (or error-ed on any remaining ones) in i386_intel_simplify. I
+ * consider the check in i386_finalize_displacement bogus anyway, in
+ * particular because it doesn't allow for expr_section, so I'd
+ * rather see that check (and the similar one in
+ * i386_finalize_immediate) use SEG_NORMAL(), but not being an a.out
+ * expert I can't really say whether that would have other bad side
+ * effects.
+ */
+ if (OUTPUT_FLAVOR == bfd_target_aout_flavour
+ && exp_seg == reg_section)
+ exp_seg = expP->X_op != O_constant ? undefined_section
+ : absolute_section;
+#endif
+
+ if (!i386_finalize_displacement (exp_seg, expP,
+ intel_state.reloc_types,
+ operand_string))
+ return 0;
+ }
+
+ if (intel_state.base || intel_state.index)
+ i.types[this_operand].bitfield.baseindex = 1;
+
+ if (intel_state.seg)
+ {
+ for (;;)
+ {
+ expP = symbol_get_value_expression (intel_state.seg);
+ if (expP->X_op != O_full_ptr)
+ break;
+ intel_state.seg = expP->X_add_symbol;
+ }
+ if (expP->X_op != O_register)
+ {
+ as_bad (_("segment register name expected"));
+ return 0;
+ }
+ if (!i386_regtab[expP->X_add_number].reg_type.bitfield.sreg2
+ && !i386_regtab[expP->X_add_number].reg_type.bitfield.sreg3)
+ {
+ as_bad (_("invalid use of register"));
+ return 0;
+ }
+ switch (i386_regtab[expP->X_add_number].reg_num)
+ {
+ case 0: i.seg[i.mem_operands] = &es; break;
+ case 1: i.seg[i.mem_operands] = &cs; break;
+ case 2: i.seg[i.mem_operands] = &ss; break;
+ case 3: i.seg[i.mem_operands] = &ds; break;
+ case 4: i.seg[i.mem_operands] = &fs; break;
+ case 5: i.seg[i.mem_operands] = &gs; break;
+ case RegFlat: i.seg[i.mem_operands] = NULL; break;
+ }
+ }
+
+ /* Swap base and index in 16-bit memory operands like
+ [si+bx]. Since i386_index_check is also used in AT&T
+ mode we have to do that here. */
+ if (intel_state.base
+ && intel_state.index
+ && intel_state.base->reg_type.bitfield.reg16
+ && intel_state.index->reg_type.bitfield.reg16
+ && intel_state.base->reg_num >= 6
+ && intel_state.index->reg_num < 6)
+ {
+ i.base_reg = intel_state.index;
+ i.index_reg = intel_state.base;
+ }
+ else
+ {
+ i.base_reg = intel_state.base;
+ i.index_reg = intel_state.index;
+ }
+
+ if (!i386_index_check (operand_string))
+ return 0;
+
+ i.types[this_operand].bitfield.mem = 1;
+ ++i.mem_operands;
+ }
+ else
+ {
+ /* Immediate. */
+ if (i.imm_operands >= MAX_IMMEDIATE_OPERANDS)
+ {
+ as_bad (_("at most %d immediate operands are allowed"),
+ MAX_IMMEDIATE_OPERANDS);
+ return 0;
+ }
+
+ expP = &im_expressions[i.imm_operands++];
+ i.op[this_operand].imms = expP;
+ *expP = exp;
+
+ return i386_finalize_immediate (exp_seg, expP, intel_state.reloc_types,
+ operand_string);
+ }
+
+ return 1;
+}
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
new file mode 100644
index 0000000..6f7a1ae
--- /dev/null
+++ b/gas/config/tc-i386.c
@@ -0,0 +1,10676 @@
+/* tc-i386.c -- Assemble code for the Intel 80386
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Intel 80386 machine specific gas.
+ Written by Eliot Dresselhaus (eliot@mgm.mit.edu).
+ x86_64 support by Jan Hubicka (jh@suse.cz)
+ VIA PadLock support by Michal Ludvig (mludvig@suse.cz)
+ Bugs & suggestions are completely welcome. This is free software.
+ Please help us make it better. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "elf/x86-64.h"
+#include "opcodes/i386-init.h"
+
+#ifndef REGISTER_WARNINGS
+#define REGISTER_WARNINGS 1
+#endif
+
+#ifndef INFER_ADDR_PREFIX
+#define INFER_ADDR_PREFIX 1
+#endif
+
+#ifndef DEFAULT_ARCH
+#define DEFAULT_ARCH "i386"
+#endif
+
+#ifndef INLINE
+#if __GNUC__ >= 2
+#define INLINE __inline__
+#else
+#define INLINE
+#endif
+#endif
+
+/* Prefixes will be emitted in the order defined below.
+ WAIT_PREFIX must be the first prefix since FWAIT is really is an
+ instruction, and so must come before any prefixes.
+ The preferred prefix order is SEG_PREFIX, ADDR_PREFIX, DATA_PREFIX,
+ REP_PREFIX/HLE_PREFIX, LOCK_PREFIX. */
+#define WAIT_PREFIX 0
+#define SEG_PREFIX 1
+#define ADDR_PREFIX 2
+#define DATA_PREFIX 3
+#define REP_PREFIX 4
+#define HLE_PREFIX REP_PREFIX
+#define BND_PREFIX REP_PREFIX
+#define LOCK_PREFIX 5
+#define REX_PREFIX 6 /* must come last. */
+#define MAX_PREFIXES 7 /* max prefixes per opcode */
+
+/* we define the syntax here (modulo base,index,scale syntax) */
+#define REGISTER_PREFIX '%'
+#define IMMEDIATE_PREFIX '$'
+#define ABSOLUTE_PREFIX '*'
+
+/* these are the instruction mnemonic suffixes in AT&T syntax or
+ memory operand size in Intel syntax. */
+#define WORD_MNEM_SUFFIX 'w'
+#define BYTE_MNEM_SUFFIX 'b'
+#define SHORT_MNEM_SUFFIX 's'
+#define LONG_MNEM_SUFFIX 'l'
+#define QWORD_MNEM_SUFFIX 'q'
+#define XMMWORD_MNEM_SUFFIX 'x'
+#define YMMWORD_MNEM_SUFFIX 'y'
+#define ZMMWORD_MNEM_SUFFIX 'z'
+/* Intel Syntax. Use a non-ascii letter since since it never appears
+ in instructions. */
+#define LONG_DOUBLE_MNEM_SUFFIX '\1'
+
+#define END_OF_INSN '\0'
+
+/*
+ 'templates' is for grouping together 'template' structures for opcodes
+ of the same name. This is only used for storing the insns in the grand
+ ole hash table of insns.
+ The templates themselves start at START and range up to (but not including)
+ END.
+ */
+typedef struct
+{
+ const insn_template *start;
+ const insn_template *end;
+}
+templates;
+
+/* 386 operand encoding bytes: see 386 book for details of this. */
+typedef struct
+{
+ unsigned int regmem; /* codes register or memory operand */
+ unsigned int reg; /* codes register operand (or extended opcode) */
+ unsigned int mode; /* how to interpret regmem & reg */
+}
+modrm_byte;
+
+/* x86-64 extension prefix. */
+typedef int rex_byte;
+
+/* 386 opcode byte to code indirect addressing. */
+typedef struct
+{
+ unsigned base;
+ unsigned index;
+ unsigned scale;
+}
+sib_byte;
+
+/* x86 arch names, types and features */
+typedef struct
+{
+ const char *name; /* arch name */
+ unsigned int len; /* arch string length */
+ enum processor_type type; /* arch type */
+ i386_cpu_flags flags; /* cpu feature flags */
+ unsigned int skip; /* show_arch should skip this. */
+ unsigned int negated; /* turn off indicated flags. */
+}
+arch_entry;
+
+static void update_code_flag (int, int);
+static void set_code_flag (int);
+static void set_16bit_gcc_code_flag (int);
+static void set_intel_syntax (int);
+static void set_intel_mnemonic (int);
+static void set_allow_index_reg (int);
+static void set_check (int);
+static void set_cpu_arch (int);
+#ifdef TE_PE
+static void pe_directive_secrel (int);
+#endif
+static void signed_cons (int);
+static char *output_invalid (int c);
+static int i386_finalize_immediate (segT, expressionS *, i386_operand_type,
+ const char *);
+static int i386_finalize_displacement (segT, expressionS *, i386_operand_type,
+ const char *);
+static int i386_att_operand (char *);
+static int i386_intel_operand (char *, int);
+static int i386_intel_simplify (expressionS *);
+static int i386_intel_parse_name (const char *, expressionS *);
+static const reg_entry *parse_register (char *, char **);
+static char *parse_insn (char *, char *);
+static char *parse_operands (char *, const char *);
+static void swap_operands (void);
+static void swap_2_operands (int, int);
+static void optimize_imm (void);
+static void optimize_disp (void);
+static const insn_template *match_template (void);
+static int check_string (void);
+static int process_suffix (void);
+static int check_byte_reg (void);
+static int check_long_reg (void);
+static int check_qword_reg (void);
+static int check_word_reg (void);
+static int finalize_imm (void);
+static int process_operands (void);
+static const seg_entry *build_modrm_byte (void);
+static void output_insn (void);
+static void output_imm (fragS *, offsetT);
+static void output_disp (fragS *, offsetT);
+#ifndef I386COFF
+static void s_bss (int);
+#endif
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+static void handle_large_common (int small ATTRIBUTE_UNUSED);
+#endif
+
+static const char *default_arch = DEFAULT_ARCH;
+
+/* This struct describes rounding control and SAE in the instruction. */
+struct RC_Operation
+{
+ enum rc_type
+ {
+ rne = 0,
+ rd,
+ ru,
+ rz,
+ saeonly
+ } type;
+ int operand;
+};
+
+static struct RC_Operation rc_op;
+
+/* The struct describes masking, applied to OPERAND in the instruction.
+ MASK is a pointer to the corresponding mask register. ZEROING tells
+ whether merging or zeroing mask is used. */
+struct Mask_Operation
+{
+ const reg_entry *mask;
+ unsigned int zeroing;
+ /* The operand where this operation is associated. */
+ int operand;
+};
+
+static struct Mask_Operation mask_op;
+
+/* The struct describes broadcasting, applied to OPERAND. FACTOR is
+ broadcast factor. */
+struct Broadcast_Operation
+{
+ /* Type of broadcast: no broadcast, {1to8}, or {1to16}. */
+ int type;
+
+ /* Index of broadcasted operand. */
+ int operand;
+};
+
+static struct Broadcast_Operation broadcast_op;
+
+/* VEX prefix. */
+typedef struct
+{
+ /* VEX prefix is either 2 byte or 3 byte. EVEX is 4 byte. */
+ unsigned char bytes[4];
+ unsigned int length;
+ /* Destination or source register specifier. */
+ const reg_entry *register_specifier;
+} vex_prefix;
+
+/* 'md_assemble ()' gathers together information and puts it into a
+ i386_insn. */
+
+union i386_op
+ {
+ expressionS *disps;
+ expressionS *imms;
+ const reg_entry *regs;
+ };
+
+enum i386_error
+ {
+ operand_size_mismatch,
+ operand_type_mismatch,
+ register_type_mismatch,
+ number_of_operands_mismatch,
+ invalid_instruction_suffix,
+ bad_imm4,
+ old_gcc_only,
+ unsupported_with_intel_mnemonic,
+ unsupported_syntax,
+ unsupported,
+ invalid_vsib_address,
+ invalid_vector_register_set,
+ unsupported_vector_index_register,
+ unsupported_broadcast,
+ broadcast_not_on_src_operand,
+ broadcast_needed,
+ unsupported_masking,
+ mask_not_on_destination,
+ no_default_mask,
+ unsupported_rc_sae,
+ rc_sae_operand_not_last_imm,
+ invalid_register_operand,
+ try_vector_disp8
+ };
+
+struct _i386_insn
+ {
+ /* TM holds the template for the insn were currently assembling. */
+ insn_template tm;
+
+ /* SUFFIX holds the instruction size suffix for byte, word, dword
+ or qword, if given. */
+ char suffix;
+
+ /* OPERANDS gives the number of given operands. */
+ unsigned int operands;
+
+ /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number
+ of given register, displacement, memory operands and immediate
+ operands. */
+ unsigned int reg_operands, disp_operands, mem_operands, imm_operands;
+
+ /* TYPES [i] is the type (see above #defines) which tells us how to
+ use OP[i] for the corresponding operand. */
+ i386_operand_type types[MAX_OPERANDS];
+
+ /* Displacement expression, immediate expression, or register for each
+ operand. */
+ union i386_op op[MAX_OPERANDS];
+
+ /* Flags for operands. */
+ unsigned int flags[MAX_OPERANDS];
+#define Operand_PCrel 1
+
+ /* Relocation type for operand */
+ enum bfd_reloc_code_real reloc[MAX_OPERANDS];
+
+ /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode
+ the base index byte below. */
+ const reg_entry *base_reg;
+ const reg_entry *index_reg;
+ unsigned int log2_scale_factor;
+
+ /* SEG gives the seg_entries of this insn. They are zero unless
+ explicit segment overrides are given. */
+ const seg_entry *seg[2];
+
+ /* PREFIX holds all the given prefix opcodes (usually null).
+ PREFIXES is the number of prefix opcodes. */
+ unsigned int prefixes;
+ unsigned char prefix[MAX_PREFIXES];
+
+ /* RM and SIB are the modrm byte and the sib byte where the
+ addressing modes of this insn are encoded. */
+ modrm_byte rm;
+ rex_byte rex;
+ rex_byte vrex;
+ sib_byte sib;
+ vex_prefix vex;
+
+ /* Masking attributes. */
+ struct Mask_Operation *mask;
+
+ /* Rounding control and SAE attributes. */
+ struct RC_Operation *rounding;
+
+ /* Broadcasting attributes. */
+ struct Broadcast_Operation *broadcast;
+
+ /* Compressed disp8*N attribute. */
+ unsigned int memshift;
+
+ /* Swap operand in encoding. */
+ unsigned int swap_operand;
+
+ /* Prefer 8bit or 32bit displacement in encoding. */
+ enum
+ {
+ disp_encoding_default = 0,
+ disp_encoding_8bit,
+ disp_encoding_32bit
+ } disp_encoding;
+
+ /* REP prefix. */
+ const char *rep_prefix;
+
+ /* HLE prefix. */
+ const char *hle_prefix;
+
+ /* Have BND prefix. */
+ const char *bnd_prefix;
+
+ /* Need VREX to support upper 16 registers. */
+ int need_vrex;
+
+ /* Error message. */
+ enum i386_error error;
+ };
+
+typedef struct _i386_insn i386_insn;
+
+/* Link RC type with corresponding string, that'll be looked for in
+ asm. */
+struct RC_name
+{
+ enum rc_type type;
+ const char *name;
+ unsigned int len;
+};
+
+static const struct RC_name RC_NamesTable[] =
+{
+ { rne, STRING_COMMA_LEN ("rn-sae") },
+ { rd, STRING_COMMA_LEN ("rd-sae") },
+ { ru, STRING_COMMA_LEN ("ru-sae") },
+ { rz, STRING_COMMA_LEN ("rz-sae") },
+ { saeonly, STRING_COMMA_LEN ("sae") },
+};
+
+/* List of chars besides those in app.c:symbol_chars that can start an
+ operand. Used to prevent the scrubber eating vital white-space. */
+const char extra_symbol_chars[] = "*%-([{"
+#ifdef LEX_AT
+ "@"
+#endif
+#ifdef LEX_QM
+ "?"
+#endif
+ ;
+
+#if (defined (TE_I386AIX) \
+ || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) \
+ && !defined (TE_GNU) \
+ && !defined (TE_LINUX) \
+ && !defined (TE_NACL) \
+ && !defined (TE_NETWARE) \
+ && !defined (TE_FreeBSD) \
+ && !defined (TE_DragonFly) \
+ && !defined (TE_NetBSD)))
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The option
+ --divide will remove '/' from this list. */
+const char *i386_comment_chars = "#/";
+#define SVR4_COMMENT_CHARS 1
+#define PREFIX_SEPARATOR '\\'
+
+#else
+const char *i386_comment_chars = "#";
+#define PREFIX_SEPARATOR '/'
+#endif
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output.
+ Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output.
+ Also note that comments started like this one will always work if
+ '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#/";
+
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point
+ nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ As in 0f12.456
+ or 0d1.2345e12. */
+const char FLT_CHARS[] = "fFdDxX";
+
+/* Tables for lexical analysis. */
+static char mnemonic_chars[256];
+static char register_chars[256];
+static char operand_chars[256];
+static char identifier_chars[256];
+static char digit_chars[256];
+
+/* Lexical macros. */
+#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char) x])
+#define is_operand_char(x) (operand_chars[(unsigned char) x])
+#define is_register_char(x) (register_chars[(unsigned char) x])
+#define is_space_char(x) ((x) == ' ')
+#define is_identifier_char(x) (identifier_chars[(unsigned char) x])
+#define is_digit_char(x) (digit_chars[(unsigned char) x])
+
+/* All non-digit non-letter characters that may occur in an operand. */
+static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:[@]";
+
+/* md_assemble() always leaves the strings it's passed unaltered. To
+ effect this we maintain a stack of saved characters that we've smashed
+ with '\0's (indicating end of strings for various sub-fields of the
+ assembler instruction). */
+static char save_stack[32];
+static char *save_stack_p;
+#define END_STRING_AND_SAVE(s) \
+ do { *save_stack_p++ = *(s); *(s) = '\0'; } while (0)
+#define RESTORE_END_STRING(s) \
+ do { *(s) = *--save_stack_p; } while (0)
+
+/* The instruction we're assembling. */
+static i386_insn i;
+
+/* Possible templates for current insn. */
+static const templates *current_templates;
+
+/* Per instruction expressionS buffers: max displacements & immediates. */
+static expressionS disp_expressions[MAX_MEMORY_OPERANDS];
+static expressionS im_expressions[MAX_IMMEDIATE_OPERANDS];
+
+/* Current operand we are working on. */
+static int this_operand = -1;
+
+/* We support four different modes. FLAG_CODE variable is used to distinguish
+ these. */
+
+enum flag_code {
+ CODE_32BIT,
+ CODE_16BIT,
+ CODE_64BIT };
+
+static enum flag_code flag_code;
+static unsigned int object_64bit;
+static unsigned int disallow_64bit_reloc;
+static int use_rela_relocations = 0;
+
+#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \
+ || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+ || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O))
+
+/* The ELF ABI to use. */
+enum x86_elf_abi
+{
+ I386_ABI,
+ X86_64_ABI,
+ X86_64_X32_ABI
+};
+
+static enum x86_elf_abi x86_elf_abi = I386_ABI;
+#endif
+
+#if defined (TE_PE) || defined (TE_PEP)
+/* Use big object file format. */
+static int use_big_obj = 0;
+#endif
+
+/* 1 for intel syntax,
+ 0 if att syntax. */
+static int intel_syntax = 0;
+
+/* 1 for intel mnemonic,
+ 0 if att mnemonic. */
+static int intel_mnemonic = !SYSV386_COMPAT;
+
+/* 1 if support old (<= 2.8.1) versions of gcc. */
+static int old_gcc = OLDGCC_COMPAT;
+
+/* 1 if pseudo registers are permitted. */
+static int allow_pseudo_reg = 0;
+
+/* 1 if register prefix % not required. */
+static int allow_naked_reg = 0;
+
+/* 1 if the assembler should add BND prefix for all control-tranferring
+ instructions supporting it, even if this prefix wasn't specified
+ explicitly. */
+static int add_bnd_prefix = 0;
+
+/* 1 if pseudo index register, eiz/riz, is allowed . */
+static int allow_index_reg = 0;
+
+/* 1 if the assembler should ignore LOCK prefix, even if it was
+ specified explicitly. */
+static int omit_lock_prefix = 0;
+
+static enum check_kind
+ {
+ check_none = 0,
+ check_warning,
+ check_error
+ }
+sse_check, operand_check = check_warning;
+
+/* Register prefix used for error message. */
+static const char *register_prefix = "%";
+
+/* Used in 16 bit gcc mode to add an l suffix to call, ret, enter,
+ leave, push, and pop instructions so that gcc has the same stack
+ frame as in 32 bit mode. */
+static char stackop_size = '\0';
+
+/* Non-zero to optimize code alignment. */
+int optimize_align_code = 1;
+
+/* Non-zero to quieten some warnings. */
+static int quiet_warnings = 0;
+
+/* CPU name. */
+static const char *cpu_arch_name = NULL;
+static char *cpu_sub_arch_name = NULL;
+
+/* CPU feature flags. */
+static i386_cpu_flags cpu_arch_flags = CPU_UNKNOWN_FLAGS;
+
+/* If we have selected a cpu we are generating instructions for. */
+static int cpu_arch_tune_set = 0;
+
+/* Cpu we are generating instructions for. */
+enum processor_type cpu_arch_tune = PROCESSOR_UNKNOWN;
+
+/* CPU feature flags of cpu we are generating instructions for. */
+static i386_cpu_flags cpu_arch_tune_flags;
+
+/* CPU instruction set architecture used. */
+enum processor_type cpu_arch_isa = PROCESSOR_UNKNOWN;
+
+/* CPU feature flags of instruction set architecture used. */
+i386_cpu_flags cpu_arch_isa_flags;
+
+/* If set, conditional jumps are not automatically promoted to handle
+ larger than a byte offset. */
+static unsigned int no_cond_jump_promotion = 0;
+
+/* Encode SSE instructions with VEX prefix. */
+static unsigned int sse2avx;
+
+/* Encode scalar AVX instructions with specific vector length. */
+static enum
+ {
+ vex128 = 0,
+ vex256
+ } avxscalar;
+
+/* Encode scalar EVEX LIG instructions with specific vector length. */
+static enum
+ {
+ evexl128 = 0,
+ evexl256,
+ evexl512
+ } evexlig;
+
+/* Encode EVEX WIG instructions with specific evex.w. */
+static enum
+ {
+ evexw0 = 0,
+ evexw1
+ } evexwig;
+
+/* Value to encode in EVEX RC bits, for SAE-only instructions. */
+static enum rc_type evexrcig = rne;
+
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_". */
+static symbolS *GOT_symbol;
+
+/* The dwarf2 return column, adjusted for 32 or 64 bit. */
+unsigned int x86_dwarf2_return_column;
+
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */
+int x86_cie_data_alignment;
+
+/* Interface to relax_segment.
+ There are 3 major relax states for 386 jump insns because the
+ different types of jumps add different sizes to frags when we're
+ figuring out what sort of jump to choose to reach a given label. */
+
+/* Types. */
+#define UNCOND_JUMP 0
+#define COND_JUMP 1
+#define COND_JUMP86 2
+
+/* Sizes. */
+#define CODE16 1
+#define SMALL 0
+#define SMALL16 (SMALL | CODE16)
+#define BIG 2
+#define BIG16 (BIG | CODE16)
+
+#ifndef INLINE
+#ifdef __GNUC__
+#define INLINE __inline__
+#else
+#define INLINE
+#endif
+#endif
+
+#define ENCODE_RELAX_STATE(type, size) \
+ ((relax_substateT) (((type) << 2) | (size)))
+#define TYPE_FROM_RELAX_STATE(s) \
+ ((s) >> 2)
+#define DISP_SIZE_FROM_RELAX_STATE(s) \
+ ((((s) & 3) == BIG ? 4 : (((s) & 3) == BIG16 ? 2 : 1)))
+
+/* This table is used by relax_frag to promote short jumps to long
+ ones where necessary. SMALL (short) jumps may be promoted to BIG
+ (32 bit long) ones, and SMALL16 jumps to BIG16 (16 bit long). We
+ don't allow a short jump in a 32 bit code segment to be promoted to
+ a 16 bit offset jump because it's slower (requires data size
+ prefix), and doesn't work, unless the destination is in the bottom
+ 64k of the code segment (The top 16 bits of eip are zeroed). */
+
+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. */
+
+ /* UNCOND_JUMP states. */
+ {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG)},
+ {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16)},
+ /* dword jmp adds 4 bytes to frag:
+ 0 extra opcode bytes, 4 displacement bytes. */
+ {0, 0, 4, 0},
+ /* word jmp adds 2 byte2 to frag:
+ 0 extra opcode bytes, 2 displacement bytes. */
+ {0, 0, 2, 0},
+
+ /* COND_JUMP states. */
+ {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP, BIG)},
+ {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP, BIG16)},
+ /* dword conditionals adds 5 bytes to frag:
+ 1 extra opcode byte, 4 displacement bytes. */
+ {0, 0, 5, 0},
+ /* word conditionals add 3 bytes to frag:
+ 1 extra opcode byte, 2 displacement bytes. */
+ {0, 0, 3, 0},
+
+ /* COND_JUMP86 states. */
+ {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP86, BIG)},
+ {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP86, BIG16)},
+ /* dword conditionals adds 5 bytes to frag:
+ 1 extra opcode byte, 4 displacement bytes. */
+ {0, 0, 5, 0},
+ /* word conditionals add 4 bytes to frag:
+ 1 displacement byte and a 3 byte long branch insn. */
+ {0, 0, 4, 0}
+};
+
+static const arch_entry cpu_arch[] =
+{
+ /* Do not replace the first two entries - i386_target_format()
+ relies on them being there in this order. */
+ { STRING_COMMA_LEN ("generic32"), PROCESSOR_GENERIC32,
+ CPU_GENERIC32_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("generic64"), PROCESSOR_GENERIC64,
+ CPU_GENERIC64_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i8086"), PROCESSOR_UNKNOWN,
+ CPU_NONE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i186"), PROCESSOR_UNKNOWN,
+ CPU_I186_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i286"), PROCESSOR_UNKNOWN,
+ CPU_I286_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i386"), PROCESSOR_I386,
+ CPU_I386_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i486"), PROCESSOR_I486,
+ CPU_I486_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i586"), PROCESSOR_PENTIUM,
+ CPU_I586_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("i686"), PROCESSOR_PENTIUMPRO,
+ CPU_I686_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("pentium"), PROCESSOR_PENTIUM,
+ CPU_I586_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("pentiumpro"), PROCESSOR_PENTIUMPRO,
+ CPU_PENTIUMPRO_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("pentiumii"), PROCESSOR_PENTIUMPRO,
+ CPU_P2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("pentiumiii"),PROCESSOR_PENTIUMPRO,
+ CPU_P3_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("pentium4"), PROCESSOR_PENTIUM4,
+ CPU_P4_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("prescott"), PROCESSOR_NOCONA,
+ CPU_CORE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("nocona"), PROCESSOR_NOCONA,
+ CPU_NOCONA_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("yonah"), PROCESSOR_CORE,
+ CPU_CORE_FLAGS, 1, 0 },
+ { STRING_COMMA_LEN ("core"), PROCESSOR_CORE,
+ CPU_CORE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("merom"), PROCESSOR_CORE2,
+ CPU_CORE2_FLAGS, 1, 0 },
+ { STRING_COMMA_LEN ("core2"), PROCESSOR_CORE2,
+ CPU_CORE2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("corei7"), PROCESSOR_COREI7,
+ CPU_COREI7_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("l1om"), PROCESSOR_L1OM,
+ CPU_L1OM_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("k1om"), PROCESSOR_K1OM,
+ CPU_K1OM_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("k6"), PROCESSOR_K6,
+ CPU_K6_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("k6_2"), PROCESSOR_K6,
+ CPU_K6_2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("athlon"), PROCESSOR_ATHLON,
+ CPU_ATHLON_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("sledgehammer"), PROCESSOR_K8,
+ CPU_K8_FLAGS, 1, 0 },
+ { STRING_COMMA_LEN ("opteron"), PROCESSOR_K8,
+ CPU_K8_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("k8"), PROCESSOR_K8,
+ CPU_K8_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("amdfam10"), PROCESSOR_AMDFAM10,
+ CPU_AMDFAM10_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("bdver1"), PROCESSOR_BD,
+ CPU_BDVER1_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("bdver2"), PROCESSOR_BD,
+ CPU_BDVER2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("bdver3"), PROCESSOR_BD,
+ CPU_BDVER3_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("bdver4"), PROCESSOR_BD,
+ CPU_BDVER4_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("btver1"), PROCESSOR_BT,
+ CPU_BTVER1_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN ("btver2"), PROCESSOR_BT,
+ CPU_BTVER2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".8087"), PROCESSOR_UNKNOWN,
+ CPU_8087_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".287"), PROCESSOR_UNKNOWN,
+ CPU_287_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".387"), PROCESSOR_UNKNOWN,
+ CPU_387_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".no87"), PROCESSOR_UNKNOWN,
+ CPU_ANY87_FLAGS, 0, 1 },
+ { STRING_COMMA_LEN (".mmx"), PROCESSOR_UNKNOWN,
+ CPU_MMX_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".nommx"), PROCESSOR_UNKNOWN,
+ CPU_3DNOWA_FLAGS, 0, 1 },
+ { STRING_COMMA_LEN (".sse"), PROCESSOR_UNKNOWN,
+ CPU_SSE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sse2"), PROCESSOR_UNKNOWN,
+ CPU_SSE2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sse3"), PROCESSOR_UNKNOWN,
+ CPU_SSE3_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".ssse3"), PROCESSOR_UNKNOWN,
+ CPU_SSSE3_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sse4.1"), PROCESSOR_UNKNOWN,
+ CPU_SSE4_1_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sse4.2"), PROCESSOR_UNKNOWN,
+ CPU_SSE4_2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sse4"), PROCESSOR_UNKNOWN,
+ CPU_SSE4_2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".nosse"), PROCESSOR_UNKNOWN,
+ CPU_ANY_SSE_FLAGS, 0, 1 },
+ { STRING_COMMA_LEN (".avx"), PROCESSOR_UNKNOWN,
+ CPU_AVX_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx2"), PROCESSOR_UNKNOWN,
+ CPU_AVX2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512f"), PROCESSOR_UNKNOWN,
+ CPU_AVX512F_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512cd"), PROCESSOR_UNKNOWN,
+ CPU_AVX512CD_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512er"), PROCESSOR_UNKNOWN,
+ CPU_AVX512ER_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512pf"), PROCESSOR_UNKNOWN,
+ CPU_AVX512PF_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512dq"), PROCESSOR_UNKNOWN,
+ CPU_AVX512DQ_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512bw"), PROCESSOR_UNKNOWN,
+ CPU_AVX512BW_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512vl"), PROCESSOR_UNKNOWN,
+ CPU_AVX512VL_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".noavx"), PROCESSOR_UNKNOWN,
+ CPU_ANY_AVX_FLAGS, 0, 1 },
+ { STRING_COMMA_LEN (".vmx"), PROCESSOR_UNKNOWN,
+ CPU_VMX_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".vmfunc"), PROCESSOR_UNKNOWN,
+ CPU_VMFUNC_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".smx"), PROCESSOR_UNKNOWN,
+ CPU_SMX_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".xsave"), PROCESSOR_UNKNOWN,
+ CPU_XSAVE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".xsaveopt"), PROCESSOR_UNKNOWN,
+ CPU_XSAVEOPT_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".xsavec"), PROCESSOR_UNKNOWN,
+ CPU_XSAVEC_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".xsaves"), PROCESSOR_UNKNOWN,
+ CPU_XSAVES_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".aes"), PROCESSOR_UNKNOWN,
+ CPU_AES_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".pclmul"), PROCESSOR_UNKNOWN,
+ CPU_PCLMUL_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".clmul"), PROCESSOR_UNKNOWN,
+ CPU_PCLMUL_FLAGS, 1, 0 },
+ { STRING_COMMA_LEN (".fsgsbase"), PROCESSOR_UNKNOWN,
+ CPU_FSGSBASE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".rdrnd"), PROCESSOR_UNKNOWN,
+ CPU_RDRND_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".f16c"), PROCESSOR_UNKNOWN,
+ CPU_F16C_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".bmi2"), PROCESSOR_UNKNOWN,
+ CPU_BMI2_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".fma"), PROCESSOR_UNKNOWN,
+ CPU_FMA_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".fma4"), PROCESSOR_UNKNOWN,
+ CPU_FMA4_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".xop"), PROCESSOR_UNKNOWN,
+ CPU_XOP_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".lwp"), PROCESSOR_UNKNOWN,
+ CPU_LWP_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".movbe"), PROCESSOR_UNKNOWN,
+ CPU_MOVBE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".cx16"), PROCESSOR_UNKNOWN,
+ CPU_CX16_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".ept"), PROCESSOR_UNKNOWN,
+ CPU_EPT_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".lzcnt"), PROCESSOR_UNKNOWN,
+ CPU_LZCNT_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".hle"), PROCESSOR_UNKNOWN,
+ CPU_HLE_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".rtm"), PROCESSOR_UNKNOWN,
+ CPU_RTM_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".invpcid"), PROCESSOR_UNKNOWN,
+ CPU_INVPCID_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".clflush"), PROCESSOR_UNKNOWN,
+ CPU_CLFLUSH_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".nop"), PROCESSOR_UNKNOWN,
+ CPU_NOP_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".syscall"), PROCESSOR_UNKNOWN,
+ CPU_SYSCALL_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".rdtscp"), PROCESSOR_UNKNOWN,
+ CPU_RDTSCP_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".3dnow"), PROCESSOR_UNKNOWN,
+ CPU_3DNOW_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".3dnowa"), PROCESSOR_UNKNOWN,
+ CPU_3DNOWA_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".padlock"), PROCESSOR_UNKNOWN,
+ CPU_PADLOCK_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".pacifica"), PROCESSOR_UNKNOWN,
+ CPU_SVME_FLAGS, 1, 0 },
+ { STRING_COMMA_LEN (".svme"), PROCESSOR_UNKNOWN,
+ CPU_SVME_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sse4a"), PROCESSOR_UNKNOWN,
+ CPU_SSE4A_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".abm"), PROCESSOR_UNKNOWN,
+ CPU_ABM_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".bmi"), PROCESSOR_UNKNOWN,
+ CPU_BMI_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".tbm"), PROCESSOR_UNKNOWN,
+ CPU_TBM_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".adx"), PROCESSOR_UNKNOWN,
+ CPU_ADX_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".rdseed"), PROCESSOR_UNKNOWN,
+ CPU_RDSEED_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".prfchw"), PROCESSOR_UNKNOWN,
+ CPU_PRFCHW_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".smap"), PROCESSOR_UNKNOWN,
+ CPU_SMAP_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".mpx"), PROCESSOR_UNKNOWN,
+ CPU_MPX_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".sha"), PROCESSOR_UNKNOWN,
+ CPU_SHA_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".clflushopt"), PROCESSOR_UNKNOWN,
+ CPU_CLFLUSHOPT_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".prefetchwt1"), PROCESSOR_UNKNOWN,
+ CPU_PREFETCHWT1_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".se1"), PROCESSOR_UNKNOWN,
+ CPU_SE1_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".clwb"), PROCESSOR_UNKNOWN,
+ CPU_CLWB_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".pcommit"), PROCESSOR_UNKNOWN,
+ CPU_PCOMMIT_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512ifma"), PROCESSOR_UNKNOWN,
+ CPU_AVX512IFMA_FLAGS, 0, 0 },
+ { STRING_COMMA_LEN (".avx512vbmi"), PROCESSOR_UNKNOWN,
+ CPU_AVX512VBMI_FLAGS, 0, 0 },
+};
+
+#ifdef I386COFF
+/* Like s_lcomm_internal in gas/read.c but the alignment string
+ is allowed to be optional. */
+
+static symbolS *
+pe_lcomm_internal (int needs_align, symbolS *symbolP, addressT size)
+{
+ addressT align = 0;
+
+ SKIP_WHITESPACE ();
+
+ if (needs_align
+ && *input_line_pointer == ',')
+ {
+ align = parse_align (needs_align - 1);
+
+ if (align == (addressT) -1)
+ return NULL;
+ }
+ else
+ {
+ if (size >= 8)
+ align = 3;
+ else if (size >= 4)
+ align = 2;
+ else if (size >= 2)
+ align = 1;
+ else
+ align = 0;
+ }
+
+ bss_alloc (symbolP, size, align);
+ return symbolP;
+}
+
+static void
+pe_lcomm (int needs_align)
+{
+ s_comm_internal (needs_align * 2, pe_lcomm_internal);
+}
+#endif
+
+const pseudo_typeS md_pseudo_table[] =
+{
+#if !defined(OBJ_AOUT) && !defined(USE_ALIGN_PTWO)
+ {"align", s_align_bytes, 0},
+#else
+ {"align", s_align_ptwo, 0},
+#endif
+ {"arch", set_cpu_arch, 0},
+#ifndef I386COFF
+ {"bss", s_bss, 0},
+#else
+ {"lcomm", pe_lcomm, 1},
+#endif
+ {"ffloat", float_cons, 'f'},
+ {"dfloat", float_cons, 'd'},
+ {"tfloat", float_cons, 'x'},
+ {"value", cons, 2},
+ {"slong", signed_cons, 4},
+ {"noopt", s_ignore, 0},
+ {"optim", s_ignore, 0},
+ {"code16gcc", set_16bit_gcc_code_flag, CODE_16BIT},
+ {"code16", set_code_flag, CODE_16BIT},
+ {"code32", set_code_flag, CODE_32BIT},
+ {"code64", set_code_flag, CODE_64BIT},
+ {"intel_syntax", set_intel_syntax, 1},
+ {"att_syntax", set_intel_syntax, 0},
+ {"intel_mnemonic", set_intel_mnemonic, 1},
+ {"att_mnemonic", set_intel_mnemonic, 0},
+ {"allow_index_reg", set_allow_index_reg, 1},
+ {"disallow_index_reg", set_allow_index_reg, 0},
+ {"sse_check", set_check, 0},
+ {"operand_check", set_check, 1},
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ {"largecomm", handle_large_common, 0},
+#else
+ {"file", (void (*) (int)) dwarf2_directive_file, 0},
+ {"loc", dwarf2_directive_loc, 0},
+ {"loc_mark_labels", dwarf2_directive_loc_mark_labels, 0},
+#endif
+#ifdef TE_PE
+ {"secrel32", pe_directive_secrel, 0},
+#endif
+ {0, 0, 0}
+};
+
+/* For interface with expression (). */
+extern char *input_line_pointer;
+
+/* Hash table for instruction mnemonic lookup. */
+static struct hash_control *op_hash;
+
+/* Hash table for register lookup. */
+static struct hash_control *reg_hash;
+
+void
+i386_align_code (fragS *fragP, int count)
+{
+ /* Various efficient no-op patterns for aligning code labels.
+ Note: Don't try to assemble the instructions in the comments.
+ 0L and 0w are not legal. */
+ static const char f32_1[] =
+ {0x90}; /* nop */
+ static const char f32_2[] =
+ {0x66,0x90}; /* xchg %ax,%ax */
+ static const char f32_3[] =
+ {0x8d,0x76,0x00}; /* leal 0(%esi),%esi */
+ static const char f32_4[] =
+ {0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */
+ static const char f32_5[] =
+ {0x90, /* nop */
+ 0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */
+ static const char f32_6[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00}; /* leal 0L(%esi),%esi */
+ static const char f32_7[] =
+ {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */
+ static const char f32_8[] =
+ {0x90, /* nop */
+ 0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */
+ static const char f32_9[] =
+ {0x89,0xf6, /* movl %esi,%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_10[] =
+ {0x8d,0x76,0x00, /* leal 0(%esi),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_11[] =
+ {0x8d,0x74,0x26,0x00, /* leal 0(%esi,1),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_12[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */
+ 0x8d,0xbf,0x00,0x00,0x00,0x00}; /* leal 0L(%edi),%edi */
+ static const char f32_13[] =
+ {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f32_14[] =
+ {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00, /* leal 0L(%esi,1),%esi */
+ 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */
+ static const char f16_3[] =
+ {0x8d,0x74,0x00}; /* lea 0(%esi),%esi */
+ static const char f16_4[] =
+ {0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */
+ static const char f16_5[] =
+ {0x90, /* nop */
+ 0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */
+ static const char f16_6[] =
+ {0x89,0xf6, /* mov %si,%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char f16_7[] =
+ {0x8d,0x74,0x00, /* lea 0(%si),%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char f16_8[] =
+ {0x8d,0xb4,0x00,0x00, /* lea 0w(%si),%si */
+ 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */
+ static const char jump_31[] =
+ {0xeb,0x1d,0x90,0x90,0x90,0x90,0x90, /* jmp .+31; lotsa nops */
+ 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
+ 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90,
+ 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90};
+ static const char *const f32_patt[] = {
+ f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8,
+ f32_9, f32_10, f32_11, f32_12, f32_13, f32_14
+ };
+ static const char *const f16_patt[] = {
+ f32_1, f32_2, f16_3, f16_4, f16_5, f16_6, f16_7, f16_8
+ };
+ /* nopl (%[re]ax) */
+ static const char alt_3[] =
+ {0x0f,0x1f,0x00};
+ /* nopl 0(%[re]ax) */
+ static const char alt_4[] =
+ {0x0f,0x1f,0x40,0x00};
+ /* nopl 0(%[re]ax,%[re]ax,1) */
+ static const char alt_5[] =
+ {0x0f,0x1f,0x44,0x00,0x00};
+ /* nopw 0(%[re]ax,%[re]ax,1) */
+ static const char alt_6[] =
+ {0x66,0x0f,0x1f,0x44,0x00,0x00};
+ /* nopl 0L(%[re]ax) */
+ static const char alt_7[] =
+ {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
+ /* nopl 0L(%[re]ax,%[re]ax,1) */
+ static const char alt_8[] =
+ {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* nopw 0L(%[re]ax,%[re]ax,1) */
+ static const char alt_9[] =
+ {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* nopw %cs:0L(%[re]ax,%[re]ax,1) */
+ static const char alt_10[] =
+ {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* data16
+ nopw %cs:0L(%[re]ax,%[re]ax,1) */
+ static const char alt_long_11[] =
+ {0x66,
+ 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* data16
+ data16
+ nopw %cs:0L(%[re]ax,%[re]ax,1) */
+ static const char alt_long_12[] =
+ {0x66,
+ 0x66,
+ 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* data16
+ data16
+ data16
+ nopw %cs:0L(%[re]ax,%[re]ax,1) */
+ static const char alt_long_13[] =
+ {0x66,
+ 0x66,
+ 0x66,
+ 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* data16
+ data16
+ data16
+ data16
+ nopw %cs:0L(%[re]ax,%[re]ax,1) */
+ static const char alt_long_14[] =
+ {0x66,
+ 0x66,
+ 0x66,
+ 0x66,
+ 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* data16
+ data16
+ data16
+ data16
+ data16
+ nopw %cs:0L(%[re]ax,%[re]ax,1) */
+ static const char alt_long_15[] =
+ {0x66,
+ 0x66,
+ 0x66,
+ 0x66,
+ 0x66,
+ 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ /* nopl 0(%[re]ax,%[re]ax,1)
+ nopw 0(%[re]ax,%[re]ax,1) */
+ static const char alt_short_11[] =
+ {0x0f,0x1f,0x44,0x00,0x00,
+ 0x66,0x0f,0x1f,0x44,0x00,0x00};
+ /* nopw 0(%[re]ax,%[re]ax,1)
+ nopw 0(%[re]ax,%[re]ax,1) */
+ static const char alt_short_12[] =
+ {0x66,0x0f,0x1f,0x44,0x00,0x00,
+ 0x66,0x0f,0x1f,0x44,0x00,0x00};
+ /* nopw 0(%[re]ax,%[re]ax,1)
+ nopl 0L(%[re]ax) */
+ static const char alt_short_13[] =
+ {0x66,0x0f,0x1f,0x44,0x00,0x00,
+ 0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
+ /* nopl 0L(%[re]ax)
+ nopl 0L(%[re]ax) */
+ static const char alt_short_14[] =
+ {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00,
+ 0x0f,0x1f,0x80,0x00,0x00,0x00,0x00};
+ /* nopl 0L(%[re]ax)
+ nopl 0L(%[re]ax,%[re]ax,1) */
+ static const char alt_short_15[] =
+ {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00,
+ 0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00};
+ static const char *const alt_short_patt[] = {
+ f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8,
+ alt_9, alt_10, alt_short_11, alt_short_12, alt_short_13,
+ alt_short_14, alt_short_15
+ };
+ static const char *const alt_long_patt[] = {
+ f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8,
+ alt_9, alt_10, alt_long_11, alt_long_12, alt_long_13,
+ alt_long_14, alt_long_15
+ };
+
+ /* Only align for at least a positive non-zero boundary. */
+ if (count <= 0 || count > MAX_MEM_FOR_RS_ALIGN_CODE)
+ return;
+
+ /* We need to decide which NOP sequence to use for 32bit and
+ 64bit. When -mtune= is used:
+
+ 1. For PROCESSOR_I386, PROCESSOR_I486, PROCESSOR_PENTIUM and
+ PROCESSOR_GENERIC32, f32_patt will be used.
+ 2. For PROCESSOR_PENTIUMPRO, PROCESSOR_PENTIUM4, PROCESSOR_NOCONA,
+ PROCESSOR_CORE, PROCESSOR_CORE2, PROCESSOR_COREI7, and
+ PROCESSOR_GENERIC64, alt_long_patt will be used.
+ 3. For PROCESSOR_ATHLON, PROCESSOR_K6, PROCESSOR_K8 and
+ PROCESSOR_AMDFAM10, PROCESSOR_BD and PROCESSOR_BT, alt_short_patt
+ will be used.
+
+ When -mtune= isn't used, alt_long_patt will be used if
+ cpu_arch_isa_flags has CpuNop. Otherwise, f32_patt will
+ be used.
+
+ When -march= or .arch is used, we can't use anything beyond
+ cpu_arch_isa_flags. */
+
+ if (flag_code == CODE_16BIT)
+ {
+ if (count > 8)
+ {
+ memcpy (fragP->fr_literal + fragP->fr_fix,
+ jump_31, count);
+ /* Adjust jump offset. */
+ fragP->fr_literal[fragP->fr_fix + 1] = count - 2;
+ }
+ else
+ memcpy (fragP->fr_literal + fragP->fr_fix,
+ f16_patt[count - 1], count);
+ }
+ else
+ {
+ const char *const *patt = NULL;
+
+ if (fragP->tc_frag_data.isa == PROCESSOR_UNKNOWN)
+ {
+ /* PROCESSOR_UNKNOWN means that all ISAs may be used. */
+ switch (cpu_arch_tune)
+ {
+ case PROCESSOR_UNKNOWN:
+ /* We use cpu_arch_isa_flags to check if we SHOULD
+ optimize with nops. */
+ if (fragP->tc_frag_data.isa_flags.bitfield.cpunop)
+ patt = alt_long_patt;
+ else
+ patt = f32_patt;
+ break;
+ case PROCESSOR_PENTIUM4:
+ case PROCESSOR_NOCONA:
+ case PROCESSOR_CORE:
+ case PROCESSOR_CORE2:
+ case PROCESSOR_COREI7:
+ case PROCESSOR_L1OM:
+ case PROCESSOR_K1OM:
+ case PROCESSOR_GENERIC64:
+ patt = alt_long_patt;
+ break;
+ case PROCESSOR_K6:
+ case PROCESSOR_ATHLON:
+ case PROCESSOR_K8:
+ case PROCESSOR_AMDFAM10:
+ case PROCESSOR_BD:
+ case PROCESSOR_BT:
+ patt = alt_short_patt;
+ break;
+ case PROCESSOR_I386:
+ case PROCESSOR_I486:
+ case PROCESSOR_PENTIUM:
+ case PROCESSOR_PENTIUMPRO:
+ case PROCESSOR_GENERIC32:
+ patt = f32_patt;
+ break;
+ }
+ }
+ else
+ {
+ switch (fragP->tc_frag_data.tune)
+ {
+ case PROCESSOR_UNKNOWN:
+ /* When cpu_arch_isa is set, cpu_arch_tune shouldn't be
+ PROCESSOR_UNKNOWN. */
+ abort ();
+ break;
+
+ case PROCESSOR_I386:
+ case PROCESSOR_I486:
+ case PROCESSOR_PENTIUM:
+ case PROCESSOR_K6:
+ case PROCESSOR_ATHLON:
+ case PROCESSOR_K8:
+ case PROCESSOR_AMDFAM10:
+ case PROCESSOR_BD:
+ case PROCESSOR_BT:
+ case PROCESSOR_GENERIC32:
+ /* We use cpu_arch_isa_flags to check if we CAN optimize
+ with nops. */
+ if (fragP->tc_frag_data.isa_flags.bitfield.cpunop)
+ patt = alt_short_patt;
+ else
+ patt = f32_patt;
+ break;
+ case PROCESSOR_PENTIUMPRO:
+ case PROCESSOR_PENTIUM4:
+ case PROCESSOR_NOCONA:
+ case PROCESSOR_CORE:
+ case PROCESSOR_CORE2:
+ case PROCESSOR_COREI7:
+ case PROCESSOR_L1OM:
+ case PROCESSOR_K1OM:
+ if (fragP->tc_frag_data.isa_flags.bitfield.cpunop)
+ patt = alt_long_patt;
+ else
+ patt = f32_patt;
+ break;
+ case PROCESSOR_GENERIC64:
+ patt = alt_long_patt;
+ break;
+ }
+ }
+
+ if (patt == f32_patt)
+ {
+ /* If the padding is less than 15 bytes, we use the normal
+ ones. Otherwise, we use a jump instruction and adjust
+ its offset. */
+ int limit;
+
+ /* For 64bit, the limit is 3 bytes. */
+ if (flag_code == CODE_64BIT
+ && fragP->tc_frag_data.isa_flags.bitfield.cpulm)
+ limit = 3;
+ else
+ limit = 15;
+ if (count < limit)
+ memcpy (fragP->fr_literal + fragP->fr_fix,
+ patt[count - 1], count);
+ else
+ {
+ memcpy (fragP->fr_literal + fragP->fr_fix,
+ jump_31, count);
+ /* Adjust jump offset. */
+ fragP->fr_literal[fragP->fr_fix + 1] = count - 2;
+ }
+ }
+ else
+ {
+ /* Maximum length of an instruction is 15 byte. If the
+ padding is greater than 15 bytes and we don't use jump,
+ we have to break it into smaller pieces. */
+ int padding = count;
+ while (padding > 15)
+ {
+ padding -= 15;
+ memcpy (fragP->fr_literal + fragP->fr_fix + padding,
+ patt [14], 15);
+ }
+
+ if (padding)
+ memcpy (fragP->fr_literal + fragP->fr_fix,
+ patt [padding - 1], padding);
+ }
+ }
+ fragP->fr_var = count;
+}
+
+static INLINE int
+operand_type_all_zero (const union i386_operand_type *x)
+{
+ switch (ARRAY_SIZE(x->array))
+ {
+ case 3:
+ if (x->array[2])
+ return 0;
+ case 2:
+ if (x->array[1])
+ return 0;
+ case 1:
+ return !x->array[0];
+ default:
+ abort ();
+ }
+}
+
+static INLINE void
+operand_type_set (union i386_operand_type *x, unsigned int v)
+{
+ switch (ARRAY_SIZE(x->array))
+ {
+ case 3:
+ x->array[2] = v;
+ case 2:
+ x->array[1] = v;
+ case 1:
+ x->array[0] = v;
+ break;
+ default:
+ abort ();
+ }
+}
+
+static INLINE int
+operand_type_equal (const union i386_operand_type *x,
+ const union i386_operand_type *y)
+{
+ switch (ARRAY_SIZE(x->array))
+ {
+ case 3:
+ if (x->array[2] != y->array[2])
+ return 0;
+ case 2:
+ if (x->array[1] != y->array[1])
+ return 0;
+ case 1:
+ return x->array[0] == y->array[0];
+ break;
+ default:
+ abort ();
+ }
+}
+
+static INLINE int
+cpu_flags_all_zero (const union i386_cpu_flags *x)
+{
+ switch (ARRAY_SIZE(x->array))
+ {
+ case 3:
+ if (x->array[2])
+ return 0;
+ case 2:
+ if (x->array[1])
+ return 0;
+ case 1:
+ return !x->array[0];
+ default:
+ abort ();
+ }
+}
+
+static INLINE void
+cpu_flags_set (union i386_cpu_flags *x, unsigned int v)
+{
+ switch (ARRAY_SIZE(x->array))
+ {
+ case 3:
+ x->array[2] = v;
+ case 2:
+ x->array[1] = v;
+ case 1:
+ x->array[0] = v;
+ break;
+ default:
+ abort ();
+ }
+}
+
+static INLINE int
+cpu_flags_equal (const union i386_cpu_flags *x,
+ const union i386_cpu_flags *y)
+{
+ switch (ARRAY_SIZE(x->array))
+ {
+ case 3:
+ if (x->array[2] != y->array[2])
+ return 0;
+ case 2:
+ if (x->array[1] != y->array[1])
+ return 0;
+ case 1:
+ return x->array[0] == y->array[0];
+ break;
+ default:
+ abort ();
+ }
+}
+
+static INLINE int
+cpu_flags_check_cpu64 (i386_cpu_flags f)
+{
+ return !((flag_code == CODE_64BIT && f.bitfield.cpuno64)
+ || (flag_code != CODE_64BIT && f.bitfield.cpu64));
+}
+
+static INLINE i386_cpu_flags
+cpu_flags_and (i386_cpu_flags x, i386_cpu_flags y)
+{
+ switch (ARRAY_SIZE (x.array))
+ {
+ case 3:
+ x.array [2] &= y.array [2];
+ case 2:
+ x.array [1] &= y.array [1];
+ case 1:
+ x.array [0] &= y.array [0];
+ break;
+ default:
+ abort ();
+ }
+ return x;
+}
+
+static INLINE i386_cpu_flags
+cpu_flags_or (i386_cpu_flags x, i386_cpu_flags y)
+{
+ switch (ARRAY_SIZE (x.array))
+ {
+ case 3:
+ x.array [2] |= y.array [2];
+ case 2:
+ x.array [1] |= y.array [1];
+ case 1:
+ x.array [0] |= y.array [0];
+ break;
+ default:
+ abort ();
+ }
+ return x;
+}
+
+static INLINE i386_cpu_flags
+cpu_flags_and_not (i386_cpu_flags x, i386_cpu_flags y)
+{
+ switch (ARRAY_SIZE (x.array))
+ {
+ case 3:
+ x.array [2] &= ~y.array [2];
+ case 2:
+ x.array [1] &= ~y.array [1];
+ case 1:
+ x.array [0] &= ~y.array [0];
+ break;
+ default:
+ abort ();
+ }
+ return x;
+}
+
+#define CPU_FLAGS_ARCH_MATCH 0x1
+#define CPU_FLAGS_64BIT_MATCH 0x2
+#define CPU_FLAGS_AES_MATCH 0x4
+#define CPU_FLAGS_PCLMUL_MATCH 0x8
+#define CPU_FLAGS_AVX_MATCH 0x10
+
+#define CPU_FLAGS_32BIT_MATCH \
+ (CPU_FLAGS_ARCH_MATCH | CPU_FLAGS_AES_MATCH \
+ | CPU_FLAGS_PCLMUL_MATCH | CPU_FLAGS_AVX_MATCH)
+#define CPU_FLAGS_PERFECT_MATCH \
+ (CPU_FLAGS_32BIT_MATCH | CPU_FLAGS_64BIT_MATCH)
+
+/* Return CPU flags match bits. */
+
+static int
+cpu_flags_match (const insn_template *t)
+{
+ i386_cpu_flags x = t->cpu_flags;
+ int match = cpu_flags_check_cpu64 (x) ? CPU_FLAGS_64BIT_MATCH : 0;
+
+ x.bitfield.cpu64 = 0;
+ x.bitfield.cpuno64 = 0;
+
+ if (cpu_flags_all_zero (&x))
+ {
+ /* This instruction is available on all archs. */
+ match |= CPU_FLAGS_32BIT_MATCH;
+ }
+ else
+ {
+ /* This instruction is available only on some archs. */
+ i386_cpu_flags cpu = cpu_arch_flags;
+
+ cpu.bitfield.cpu64 = 0;
+ cpu.bitfield.cpuno64 = 0;
+ cpu = cpu_flags_and (x, cpu);
+ if (!cpu_flags_all_zero (&cpu))
+ {
+ if (x.bitfield.cpuavx)
+ {
+ /* We only need to check AES/PCLMUL/SSE2AVX with AVX. */
+ if (cpu.bitfield.cpuavx)
+ {
+ /* Check SSE2AVX. */
+ if (!t->opcode_modifier.sse2avx|| sse2avx)
+ {
+ match |= (CPU_FLAGS_ARCH_MATCH
+ | CPU_FLAGS_AVX_MATCH);
+ /* Check AES. */
+ if (!x.bitfield.cpuaes || cpu.bitfield.cpuaes)
+ match |= CPU_FLAGS_AES_MATCH;
+ /* Check PCLMUL. */
+ if (!x.bitfield.cpupclmul
+ || cpu.bitfield.cpupclmul)
+ match |= CPU_FLAGS_PCLMUL_MATCH;
+ }
+ }
+ else
+ match |= CPU_FLAGS_ARCH_MATCH;
+ }
+ else
+ match |= CPU_FLAGS_32BIT_MATCH;
+ }
+ }
+ return match;
+}
+
+static INLINE i386_operand_type
+operand_type_and (i386_operand_type x, i386_operand_type y)
+{
+ switch (ARRAY_SIZE (x.array))
+ {
+ case 3:
+ x.array [2] &= y.array [2];
+ case 2:
+ x.array [1] &= y.array [1];
+ case 1:
+ x.array [0] &= y.array [0];
+ break;
+ default:
+ abort ();
+ }
+ return x;
+}
+
+static INLINE i386_operand_type
+operand_type_or (i386_operand_type x, i386_operand_type y)
+{
+ switch (ARRAY_SIZE (x.array))
+ {
+ case 3:
+ x.array [2] |= y.array [2];
+ case 2:
+ x.array [1] |= y.array [1];
+ case 1:
+ x.array [0] |= y.array [0];
+ break;
+ default:
+ abort ();
+ }
+ return x;
+}
+
+static INLINE i386_operand_type
+operand_type_xor (i386_operand_type x, i386_operand_type y)
+{
+ switch (ARRAY_SIZE (x.array))
+ {
+ case 3:
+ x.array [2] ^= y.array [2];
+ case 2:
+ x.array [1] ^= y.array [1];
+ case 1:
+ x.array [0] ^= y.array [0];
+ break;
+ default:
+ abort ();
+ }
+ return x;
+}
+
+static const i386_operand_type acc32 = OPERAND_TYPE_ACC32;
+static const i386_operand_type acc64 = OPERAND_TYPE_ACC64;
+static const i386_operand_type control = OPERAND_TYPE_CONTROL;
+static const i386_operand_type inoutportreg
+ = OPERAND_TYPE_INOUTPORTREG;
+static const i386_operand_type reg16_inoutportreg
+ = OPERAND_TYPE_REG16_INOUTPORTREG;
+static const i386_operand_type disp16 = OPERAND_TYPE_DISP16;
+static const i386_operand_type disp32 = OPERAND_TYPE_DISP32;
+static const i386_operand_type disp32s = OPERAND_TYPE_DISP32S;
+static const i386_operand_type disp16_32 = OPERAND_TYPE_DISP16_32;
+static const i386_operand_type anydisp
+ = OPERAND_TYPE_ANYDISP;
+static const i386_operand_type regxmm = OPERAND_TYPE_REGXMM;
+static const i386_operand_type regymm = OPERAND_TYPE_REGYMM;
+static const i386_operand_type regzmm = OPERAND_TYPE_REGZMM;
+static const i386_operand_type regmask = OPERAND_TYPE_REGMASK;
+static const i386_operand_type imm8 = OPERAND_TYPE_IMM8;
+static const i386_operand_type imm8s = OPERAND_TYPE_IMM8S;
+static const i386_operand_type imm16 = OPERAND_TYPE_IMM16;
+static const i386_operand_type imm32 = OPERAND_TYPE_IMM32;
+static const i386_operand_type imm32s = OPERAND_TYPE_IMM32S;
+static const i386_operand_type imm64 = OPERAND_TYPE_IMM64;
+static const i386_operand_type imm16_32 = OPERAND_TYPE_IMM16_32;
+static const i386_operand_type imm16_32s = OPERAND_TYPE_IMM16_32S;
+static const i386_operand_type imm16_32_32s = OPERAND_TYPE_IMM16_32_32S;
+static const i386_operand_type vec_imm4 = OPERAND_TYPE_VEC_IMM4;
+
+enum operand_type
+{
+ reg,
+ imm,
+ disp,
+ anymem
+};
+
+static INLINE int
+operand_type_check (i386_operand_type t, enum operand_type c)
+{
+ switch (c)
+ {
+ case reg:
+ return (t.bitfield.reg8
+ || t.bitfield.reg16
+ || t.bitfield.reg32
+ || t.bitfield.reg64);
+
+ case imm:
+ return (t.bitfield.imm8
+ || t.bitfield.imm8s
+ || t.bitfield.imm16
+ || t.bitfield.imm32
+ || t.bitfield.imm32s
+ || t.bitfield.imm64);
+
+ case disp:
+ return (t.bitfield.disp8
+ || t.bitfield.disp16
+ || t.bitfield.disp32
+ || t.bitfield.disp32s
+ || t.bitfield.disp64);
+
+ case anymem:
+ return (t.bitfield.disp8
+ || t.bitfield.disp16
+ || t.bitfield.disp32
+ || t.bitfield.disp32s
+ || t.bitfield.disp64
+ || t.bitfield.baseindex);
+
+ default:
+ abort ();
+ }
+
+ return 0;
+}
+
+/* Return 1 if there is no conflict in 8bit/16bit/32bit/64bit on
+ operand J for instruction template T. */
+
+static INLINE int
+match_reg_size (const insn_template *t, unsigned int j)
+{
+ return !((i.types[j].bitfield.byte
+ && !t->operand_types[j].bitfield.byte)
+ || (i.types[j].bitfield.word
+ && !t->operand_types[j].bitfield.word)
+ || (i.types[j].bitfield.dword
+ && !t->operand_types[j].bitfield.dword)
+ || (i.types[j].bitfield.qword
+ && !t->operand_types[j].bitfield.qword));
+}
+
+/* Return 1 if there is no conflict in any size on operand J for
+ instruction template T. */
+
+static INLINE int
+match_mem_size (const insn_template *t, unsigned int j)
+{
+ return (match_reg_size (t, j)
+ && !((i.types[j].bitfield.unspecified
+ && !t->operand_types[j].bitfield.unspecified)
+ || (i.types[j].bitfield.fword
+ && !t->operand_types[j].bitfield.fword)
+ || (i.types[j].bitfield.tbyte
+ && !t->operand_types[j].bitfield.tbyte)
+ || (i.types[j].bitfield.xmmword
+ && !t->operand_types[j].bitfield.xmmword)
+ || (i.types[j].bitfield.ymmword
+ && !t->operand_types[j].bitfield.ymmword)
+ || (i.types[j].bitfield.zmmword
+ && !t->operand_types[j].bitfield.zmmword)));
+}
+
+/* Return 1 if there is no size conflict on any operands for
+ instruction template T. */
+
+static INLINE int
+operand_size_match (const insn_template *t)
+{
+ unsigned int j;
+ int match = 1;
+
+ /* Don't check jump instructions. */
+ if (t->opcode_modifier.jump
+ || t->opcode_modifier.jumpbyte
+ || t->opcode_modifier.jumpdword
+ || t->opcode_modifier.jumpintersegment)
+ return match;
+
+ /* Check memory and accumulator operand size. */
+ for (j = 0; j < i.operands; j++)
+ {
+ if (t->operand_types[j].bitfield.anysize)
+ continue;
+
+ if (t->operand_types[j].bitfield.acc && !match_reg_size (t, j))
+ {
+ match = 0;
+ break;
+ }
+
+ if (i.types[j].bitfield.mem && !match_mem_size (t, j))
+ {
+ match = 0;
+ break;
+ }
+ }
+
+ if (match)
+ return match;
+ else if (!t->opcode_modifier.d && !t->opcode_modifier.floatd)
+ {
+mismatch:
+ i.error = operand_size_mismatch;
+ return 0;
+ }
+
+ /* Check reverse. */
+ gas_assert (i.operands == 2);
+
+ match = 1;
+ for (j = 0; j < 2; j++)
+ {
+ if (t->operand_types[j].bitfield.acc
+ && !match_reg_size (t, j ? 0 : 1))
+ goto mismatch;
+
+ if (i.types[j].bitfield.mem
+ && !match_mem_size (t, j ? 0 : 1))
+ goto mismatch;
+ }
+
+ return match;
+}
+
+static INLINE int
+operand_type_match (i386_operand_type overlap,
+ i386_operand_type given)
+{
+ i386_operand_type temp = overlap;
+
+ temp.bitfield.jumpabsolute = 0;
+ temp.bitfield.unspecified = 0;
+ temp.bitfield.byte = 0;
+ temp.bitfield.word = 0;
+ temp.bitfield.dword = 0;
+ temp.bitfield.fword = 0;
+ temp.bitfield.qword = 0;
+ temp.bitfield.tbyte = 0;
+ temp.bitfield.xmmword = 0;
+ temp.bitfield.ymmword = 0;
+ temp.bitfield.zmmword = 0;
+ if (operand_type_all_zero (&temp))
+ goto mismatch;
+
+ if (given.bitfield.baseindex == overlap.bitfield.baseindex
+ && given.bitfield.jumpabsolute == overlap.bitfield.jumpabsolute)
+ return 1;
+
+mismatch:
+ i.error = operand_type_mismatch;
+ return 0;
+}
+
+/* If given types g0 and g1 are registers they must be of the same type
+ unless the expected operand type register overlap is null.
+ Note that Acc in a template matches every size of reg. */
+
+static INLINE int
+operand_type_register_match (i386_operand_type m0,
+ i386_operand_type g0,
+ i386_operand_type t0,
+ i386_operand_type m1,
+ i386_operand_type g1,
+ i386_operand_type t1)
+{
+ if (!operand_type_check (g0, reg))
+ return 1;
+
+ if (!operand_type_check (g1, reg))
+ return 1;
+
+ if (g0.bitfield.reg8 == g1.bitfield.reg8
+ && g0.bitfield.reg16 == g1.bitfield.reg16
+ && g0.bitfield.reg32 == g1.bitfield.reg32
+ && g0.bitfield.reg64 == g1.bitfield.reg64)
+ return 1;
+
+ if (m0.bitfield.acc)
+ {
+ t0.bitfield.reg8 = 1;
+ t0.bitfield.reg16 = 1;
+ t0.bitfield.reg32 = 1;
+ t0.bitfield.reg64 = 1;
+ }
+
+ if (m1.bitfield.acc)
+ {
+ t1.bitfield.reg8 = 1;
+ t1.bitfield.reg16 = 1;
+ t1.bitfield.reg32 = 1;
+ t1.bitfield.reg64 = 1;
+ }
+
+ if (!(t0.bitfield.reg8 & t1.bitfield.reg8)
+ && !(t0.bitfield.reg16 & t1.bitfield.reg16)
+ && !(t0.bitfield.reg32 & t1.bitfield.reg32)
+ && !(t0.bitfield.reg64 & t1.bitfield.reg64))
+ return 1;
+
+ i.error = register_type_mismatch;
+
+ return 0;
+}
+
+static INLINE unsigned int
+register_number (const reg_entry *r)
+{
+ unsigned int nr = r->reg_num;
+
+ if (r->reg_flags & RegRex)
+ nr += 8;
+
+ return nr;
+}
+
+static INLINE unsigned int
+mode_from_disp_size (i386_operand_type t)
+{
+ if (t.bitfield.disp8 || t.bitfield.vec_disp8)
+ return 1;
+ else if (t.bitfield.disp16
+ || t.bitfield.disp32
+ || t.bitfield.disp32s)
+ return 2;
+ else
+ return 0;
+}
+
+static INLINE int
+fits_in_signed_byte (addressT num)
+{
+ return num + 0x80 <= 0xff;
+}
+
+static INLINE int
+fits_in_unsigned_byte (addressT num)
+{
+ return num <= 0xff;
+}
+
+static INLINE int
+fits_in_unsigned_word (addressT num)
+{
+ return num <= 0xffff;
+}
+
+static INLINE int
+fits_in_signed_word (addressT num)
+{
+ return num + 0x8000 <= 0xffff;
+}
+
+static INLINE int
+fits_in_signed_long (addressT num ATTRIBUTE_UNUSED)
+{
+#ifndef BFD64
+ return 1;
+#else
+ return num + 0x80000000 <= 0xffffffff;
+#endif
+} /* fits_in_signed_long() */
+
+static INLINE int
+fits_in_unsigned_long (addressT num ATTRIBUTE_UNUSED)
+{
+#ifndef BFD64
+ return 1;
+#else
+ return num <= 0xffffffff;
+#endif
+} /* fits_in_unsigned_long() */
+
+static INLINE int
+fits_in_vec_disp8 (offsetT num)
+{
+ int shift = i.memshift;
+ unsigned int mask;
+
+ if (shift == -1)
+ abort ();
+
+ mask = (1 << shift) - 1;
+
+ /* Return 0 if NUM isn't properly aligned. */
+ if ((num & mask))
+ return 0;
+
+ /* Check if NUM will fit in 8bit after shift. */
+ return fits_in_signed_byte (num >> shift);
+}
+
+static INLINE int
+fits_in_imm4 (offsetT num)
+{
+ return (num & 0xf) == num;
+}
+
+static i386_operand_type
+smallest_imm_type (offsetT num)
+{
+ i386_operand_type t;
+
+ operand_type_set (&t, 0);
+ t.bitfield.imm64 = 1;
+
+ if (cpu_arch_tune != PROCESSOR_I486 && num == 1)
+ {
+ /* This code is disabled on the 486 because all the Imm1 forms
+ in the opcode table are slower on the i486. They're the
+ versions with the implicitly specified single-position
+ displacement, which has another syntax if you really want to
+ use that form. */
+ t.bitfield.imm1 = 1;
+ t.bitfield.imm8 = 1;
+ t.bitfield.imm8s = 1;
+ t.bitfield.imm16 = 1;
+ t.bitfield.imm32 = 1;
+ t.bitfield.imm32s = 1;
+ }
+ else if (fits_in_signed_byte (num))
+ {
+ t.bitfield.imm8 = 1;
+ t.bitfield.imm8s = 1;
+ t.bitfield.imm16 = 1;
+ t.bitfield.imm32 = 1;
+ t.bitfield.imm32s = 1;
+ }
+ else if (fits_in_unsigned_byte (num))
+ {
+ t.bitfield.imm8 = 1;
+ t.bitfield.imm16 = 1;
+ t.bitfield.imm32 = 1;
+ t.bitfield.imm32s = 1;
+ }
+ else if (fits_in_signed_word (num) || fits_in_unsigned_word (num))
+ {
+ t.bitfield.imm16 = 1;
+ t.bitfield.imm32 = 1;
+ t.bitfield.imm32s = 1;
+ }
+ else if (fits_in_signed_long (num))
+ {
+ t.bitfield.imm32 = 1;
+ t.bitfield.imm32s = 1;
+ }
+ else if (fits_in_unsigned_long (num))
+ t.bitfield.imm32 = 1;
+
+ return t;
+}
+
+static offsetT
+offset_in_range (offsetT val, int size)
+{
+ addressT mask;
+
+ switch (size)
+ {
+ case 1: mask = ((addressT) 1 << 8) - 1; break;
+ case 2: mask = ((addressT) 1 << 16) - 1; break;
+ case 4: mask = ((addressT) 2 << 31) - 1; break;
+#ifdef BFD64
+ case 8: mask = ((addressT) 2 << 63) - 1; break;
+#endif
+ default: abort ();
+ }
+
+#ifdef BFD64
+ /* If BFD64, sign extend val for 32bit address mode. */
+ if (flag_code != CODE_64BIT
+ || i.prefix[ADDR_PREFIX])
+ if ((val & ~(((addressT) 2 << 31) - 1)) == 0)
+ val = (val ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31);
+#endif
+
+ if ((val & ~mask) != 0 && (val & ~mask) != ~mask)
+ {
+ char buf1[40], buf2[40];
+
+ sprint_value (buf1, val);
+ sprint_value (buf2, val & mask);
+ as_warn (_("%s shortened to %s"), buf1, buf2);
+ }
+ return val & mask;
+}
+
+enum PREFIX_GROUP
+{
+ PREFIX_EXIST = 0,
+ PREFIX_LOCK,
+ PREFIX_REP,
+ PREFIX_OTHER
+};
+
+/* Returns
+ a. PREFIX_EXIST if attempting to add a prefix where one from the
+ same class already exists.
+ b. PREFIX_LOCK if lock prefix is added.
+ c. PREFIX_REP if rep/repne prefix is added.
+ d. PREFIX_OTHER if other prefix is added.
+ */
+
+static enum PREFIX_GROUP
+add_prefix (unsigned int prefix)
+{
+ enum PREFIX_GROUP ret = PREFIX_OTHER;
+ unsigned int q;
+
+ if (prefix >= REX_OPCODE && prefix < REX_OPCODE + 16
+ && flag_code == CODE_64BIT)
+ {
+ if ((i.prefix[REX_PREFIX] & prefix & REX_W)
+ || ((i.prefix[REX_PREFIX] & (REX_R | REX_X | REX_B))
+ && (prefix & (REX_R | REX_X | REX_B))))
+ ret = PREFIX_EXIST;
+ q = REX_PREFIX;
+ }
+ else
+ {
+ switch (prefix)
+ {
+ default:
+ abort ();
+
+ case CS_PREFIX_OPCODE:
+ case DS_PREFIX_OPCODE:
+ case ES_PREFIX_OPCODE:
+ case FS_PREFIX_OPCODE:
+ case GS_PREFIX_OPCODE:
+ case SS_PREFIX_OPCODE:
+ q = SEG_PREFIX;
+ break;
+
+ case REPNE_PREFIX_OPCODE:
+ case REPE_PREFIX_OPCODE:
+ q = REP_PREFIX;
+ ret = PREFIX_REP;
+ break;
+
+ case LOCK_PREFIX_OPCODE:
+ q = LOCK_PREFIX;
+ ret = PREFIX_LOCK;
+ break;
+
+ case FWAIT_OPCODE:
+ q = WAIT_PREFIX;
+ break;
+
+ case ADDR_PREFIX_OPCODE:
+ q = ADDR_PREFIX;
+ break;
+
+ case DATA_PREFIX_OPCODE:
+ q = DATA_PREFIX;
+ break;
+ }
+ if (i.prefix[q] != 0)
+ ret = PREFIX_EXIST;
+ }
+
+ if (ret)
+ {
+ if (!i.prefix[q])
+ ++i.prefixes;
+ i.prefix[q] |= prefix;
+ }
+ else
+ as_bad (_("same type of prefix used twice"));
+
+ return ret;
+}
+
+static void
+update_code_flag (int value, int check)
+{
+ PRINTF_LIKE ((*as_error));
+
+ flag_code = (enum flag_code) value;
+ if (flag_code == CODE_64BIT)
+ {
+ cpu_arch_flags.bitfield.cpu64 = 1;
+ cpu_arch_flags.bitfield.cpuno64 = 0;
+ }
+ else
+ {
+ cpu_arch_flags.bitfield.cpu64 = 0;
+ cpu_arch_flags.bitfield.cpuno64 = 1;
+ }
+ if (value == CODE_64BIT && !cpu_arch_flags.bitfield.cpulm )
+ {
+ if (check)
+ as_error = as_fatal;
+ else
+ as_error = as_bad;
+ (*as_error) (_("64bit mode not supported on `%s'."),
+ cpu_arch_name ? cpu_arch_name : default_arch);
+ }
+ if (value == CODE_32BIT && !cpu_arch_flags.bitfield.cpui386)
+ {
+ if (check)
+ as_error = as_fatal;
+ else
+ as_error = as_bad;
+ (*as_error) (_("32bit mode not supported on `%s'."),
+ cpu_arch_name ? cpu_arch_name : default_arch);
+ }
+ stackop_size = '\0';
+}
+
+static void
+set_code_flag (int value)
+{
+ update_code_flag (value, 0);
+}
+
+static void
+set_16bit_gcc_code_flag (int new_code_flag)
+{
+ flag_code = (enum flag_code) new_code_flag;
+ if (flag_code != CODE_16BIT)
+ abort ();
+ cpu_arch_flags.bitfield.cpu64 = 0;
+ cpu_arch_flags.bitfield.cpuno64 = 1;
+ stackop_size = LONG_MNEM_SUFFIX;
+}
+
+static void
+set_intel_syntax (int syntax_flag)
+{
+ /* Find out if register prefixing is specified. */
+ int ask_naked_reg = 0;
+
+ SKIP_WHITESPACE ();
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *string = input_line_pointer;
+ int e = get_symbol_end ();
+
+ if (strcmp (string, "prefix") == 0)
+ ask_naked_reg = 1;
+ else if (strcmp (string, "noprefix") == 0)
+ ask_naked_reg = -1;
+ else
+ as_bad (_("bad argument to syntax directive."));
+ *input_line_pointer = e;
+ }
+ demand_empty_rest_of_line ();
+
+ intel_syntax = syntax_flag;
+
+ if (ask_naked_reg == 0)
+ allow_naked_reg = (intel_syntax
+ && (bfd_get_symbol_leading_char (stdoutput) != '\0'));
+ else
+ allow_naked_reg = (ask_naked_reg < 0);
+
+ expr_set_rank (O_full_ptr, syntax_flag ? 10 : 0);
+
+ identifier_chars['%'] = intel_syntax && allow_naked_reg ? '%' : 0;
+ identifier_chars['$'] = intel_syntax ? '$' : 0;
+ register_prefix = allow_naked_reg ? "" : "%";
+}
+
+static void
+set_intel_mnemonic (int mnemonic_flag)
+{
+ intel_mnemonic = mnemonic_flag;
+}
+
+static void
+set_allow_index_reg (int flag)
+{
+ allow_index_reg = flag;
+}
+
+static void
+set_check (int what)
+{
+ enum check_kind *kind;
+ const char *str;
+
+ if (what)
+ {
+ kind = &operand_check;
+ str = "operand";
+ }
+ else
+ {
+ kind = &sse_check;
+ str = "sse";
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *string = input_line_pointer;
+ int e = get_symbol_end ();
+
+ if (strcmp (string, "none") == 0)
+ *kind = check_none;
+ else if (strcmp (string, "warning") == 0)
+ *kind = check_warning;
+ else if (strcmp (string, "error") == 0)
+ *kind = check_error;
+ else
+ as_bad (_("bad argument to %s_check directive."), str);
+ *input_line_pointer = e;
+ }
+ else
+ as_bad (_("missing argument for %s_check directive"), str);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+check_cpu_arch_compatible (const char *name ATTRIBUTE_UNUSED,
+ i386_cpu_flags new_flag ATTRIBUTE_UNUSED)
+{
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ static const char *arch;
+
+ /* Intel LIOM is only supported on ELF. */
+ if (!IS_ELF)
+ return;
+
+ if (!arch)
+ {
+ /* Use cpu_arch_name if it is set in md_parse_option. Otherwise
+ use default_arch. */
+ arch = cpu_arch_name;
+ if (!arch)
+ arch = default_arch;
+ }
+
+ /* If we are targeting Intel L1OM, we must enable it. */
+ if (get_elf_backend_data (stdoutput)->elf_machine_code != EM_L1OM
+ || new_flag.bitfield.cpul1om)
+ return;
+
+ /* If we are targeting Intel K1OM, we must enable it. */
+ if (get_elf_backend_data (stdoutput)->elf_machine_code != EM_K1OM
+ || new_flag.bitfield.cpuk1om)
+ return;
+
+ as_bad (_("`%s' is not supported on `%s'"), name, arch);
+#endif
+}
+
+static void
+set_cpu_arch (int dummy ATTRIBUTE_UNUSED)
+{
+ SKIP_WHITESPACE ();
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ char *string = input_line_pointer;
+ int e = get_symbol_end ();
+ unsigned int j;
+ i386_cpu_flags flags;
+
+ for (j = 0; j < ARRAY_SIZE (cpu_arch); j++)
+ {
+ if (strcmp (string, cpu_arch[j].name) == 0)
+ {
+ check_cpu_arch_compatible (string, cpu_arch[j].flags);
+
+ if (*string != '.')
+ {
+ cpu_arch_name = cpu_arch[j].name;
+ cpu_sub_arch_name = NULL;
+ cpu_arch_flags = cpu_arch[j].flags;
+ if (flag_code == CODE_64BIT)
+ {
+ cpu_arch_flags.bitfield.cpu64 = 1;
+ cpu_arch_flags.bitfield.cpuno64 = 0;
+ }
+ else
+ {
+ cpu_arch_flags.bitfield.cpu64 = 0;
+ cpu_arch_flags.bitfield.cpuno64 = 1;
+ }
+ cpu_arch_isa = cpu_arch[j].type;
+ cpu_arch_isa_flags = cpu_arch[j].flags;
+ if (!cpu_arch_tune_set)
+ {
+ cpu_arch_tune = cpu_arch_isa;
+ cpu_arch_tune_flags = cpu_arch_isa_flags;
+ }
+ break;
+ }
+
+ if (!cpu_arch[j].negated)
+ flags = cpu_flags_or (cpu_arch_flags,
+ cpu_arch[j].flags);
+ else
+ flags = cpu_flags_and_not (cpu_arch_flags,
+ cpu_arch[j].flags);
+ if (!cpu_flags_equal (&flags, &cpu_arch_flags))
+ {
+ if (cpu_sub_arch_name)
+ {
+ char *name = cpu_sub_arch_name;
+ cpu_sub_arch_name = concat (name,
+ cpu_arch[j].name,
+ (const char *) NULL);
+ free (name);
+ }
+ else
+ cpu_sub_arch_name = xstrdup (cpu_arch[j].name);
+ cpu_arch_flags = flags;
+ cpu_arch_isa_flags = flags;
+ }
+ *input_line_pointer = e;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ }
+ if (j >= ARRAY_SIZE (cpu_arch))
+ as_bad (_("no such architecture: `%s'"), string);
+
+ *input_line_pointer = e;
+ }
+ else
+ as_bad (_("missing cpu architecture"));
+
+ no_cond_jump_promotion = 0;
+ if (*input_line_pointer == ','
+ && !is_end_of_line[(unsigned char) input_line_pointer[1]])
+ {
+ char *string = ++input_line_pointer;
+ int e = get_symbol_end ();
+
+ if (strcmp (string, "nojumps") == 0)
+ no_cond_jump_promotion = 1;
+ else if (strcmp (string, "jumps") == 0)
+ ;
+ else
+ as_bad (_("no such architecture modifier: `%s'"), string);
+
+ *input_line_pointer = e;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+enum bfd_architecture
+i386_arch (void)
+{
+ if (cpu_arch_isa == PROCESSOR_L1OM)
+ {
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || flag_code != CODE_64BIT)
+ as_fatal (_("Intel L1OM is 64bit ELF only"));
+ return bfd_arch_l1om;
+ }
+ else if (cpu_arch_isa == PROCESSOR_K1OM)
+ {
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || flag_code != CODE_64BIT)
+ as_fatal (_("Intel K1OM is 64bit ELF only"));
+ return bfd_arch_k1om;
+ }
+ else
+ return bfd_arch_i386;
+}
+
+unsigned long
+i386_mach (void)
+{
+ if (!strncmp (default_arch, "x86_64", 6))
+ {
+ if (cpu_arch_isa == PROCESSOR_L1OM)
+ {
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || default_arch[6] != '\0')
+ as_fatal (_("Intel L1OM is 64bit ELF only"));
+ return bfd_mach_l1om;
+ }
+ else if (cpu_arch_isa == PROCESSOR_K1OM)
+ {
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || default_arch[6] != '\0')
+ as_fatal (_("Intel K1OM is 64bit ELF only"));
+ return bfd_mach_k1om;
+ }
+ else if (default_arch[6] == '\0')
+ return bfd_mach_x86_64;
+ else
+ return bfd_mach_x64_32;
+ }
+ else if (!strcmp (default_arch, "i386"))
+ return bfd_mach_i386_i386;
+ else
+ as_fatal (_("unknown architecture"));
+}
+
+void
+md_begin (void)
+{
+ const char *hash_err;
+
+ /* Initialize op_hash hash table. */
+ op_hash = hash_new ();
+
+ {
+ const insn_template *optab;
+ templates *core_optab;
+
+ /* Setup for loop. */
+ optab = i386_optab;
+ core_optab = (templates *) xmalloc (sizeof (templates));
+ core_optab->start = optab;
+
+ while (1)
+ {
+ ++optab;
+ if (optab->name == NULL
+ || strcmp (optab->name, (optab - 1)->name) != 0)
+ {
+ /* different name --> ship out current template list;
+ add to hash table; & begin anew. */
+ core_optab->end = optab;
+ hash_err = hash_insert (op_hash,
+ (optab - 1)->name,
+ (void *) core_optab);
+ if (hash_err)
+ {
+ as_fatal (_("can't hash %s: %s"),
+ (optab - 1)->name,
+ hash_err);
+ }
+ if (optab->name == NULL)
+ break;
+ core_optab = (templates *) xmalloc (sizeof (templates));
+ core_optab->start = optab;
+ }
+ }
+ }
+
+ /* Initialize reg_hash hash table. */
+ reg_hash = hash_new ();
+ {
+ const reg_entry *regtab;
+ unsigned int regtab_size = i386_regtab_size;
+
+ for (regtab = i386_regtab; regtab_size--; regtab++)
+ {
+ hash_err = hash_insert (reg_hash, regtab->reg_name, (void *) regtab);
+ if (hash_err)
+ as_fatal (_("can't hash %s: %s"),
+ regtab->reg_name,
+ hash_err);
+ }
+ }
+
+ /* Fill in lexical tables: mnemonic_chars, operand_chars. */
+ {
+ int c;
+ char *p;
+
+ for (c = 0; c < 256; c++)
+ {
+ if (ISDIGIT (c))
+ {
+ digit_chars[c] = c;
+ mnemonic_chars[c] = c;
+ register_chars[c] = c;
+ operand_chars[c] = c;
+ }
+ else if (ISLOWER (c))
+ {
+ mnemonic_chars[c] = c;
+ register_chars[c] = c;
+ operand_chars[c] = c;
+ }
+ else if (ISUPPER (c))
+ {
+ mnemonic_chars[c] = TOLOWER (c);
+ register_chars[c] = mnemonic_chars[c];
+ operand_chars[c] = c;
+ }
+ else if (c == '{' || c == '}')
+ operand_chars[c] = c;
+
+ if (ISALPHA (c) || ISDIGIT (c))
+ identifier_chars[c] = c;
+ else if (c >= 128)
+ {
+ identifier_chars[c] = c;
+ operand_chars[c] = c;
+ }
+ }
+
+#ifdef LEX_AT
+ identifier_chars['@'] = '@';
+#endif
+#ifdef LEX_QM
+ identifier_chars['?'] = '?';
+ operand_chars['?'] = '?';
+#endif
+ digit_chars['-'] = '-';
+ mnemonic_chars['_'] = '_';
+ mnemonic_chars['-'] = '-';
+ mnemonic_chars['.'] = '.';
+ identifier_chars['_'] = '_';
+ identifier_chars['.'] = '.';
+
+ for (p = operand_special_chars; *p != '\0'; p++)
+ operand_chars[(unsigned char) *p] = *p;
+ }
+
+ if (flag_code == CODE_64BIT)
+ {
+#if defined (OBJ_COFF) && defined (TE_PE)
+ x86_dwarf2_return_column = (OUTPUT_FLAVOR == bfd_target_coff_flavour
+ ? 32 : 16);
+#else
+ x86_dwarf2_return_column = 16;
+#endif
+ x86_cie_data_alignment = -8;
+ }
+ else
+ {
+ x86_dwarf2_return_column = 8;
+ x86_cie_data_alignment = -4;
+ }
+}
+
+void
+i386_print_statistics (FILE *file)
+{
+ hash_print_statistics (file, "i386 opcode", op_hash);
+ hash_print_statistics (file, "i386 register", reg_hash);
+}
+
+#ifdef DEBUG386
+
+/* Debugging routines for md_assemble. */
+static void pte (insn_template *);
+static void pt (i386_operand_type);
+static void pe (expressionS *);
+static void ps (symbolS *);
+
+static void
+pi (char *line, i386_insn *x)
+{
+ unsigned int j;
+
+ fprintf (stdout, "%s: template ", line);
+ pte (&x->tm);
+ fprintf (stdout, " address: base %s index %s scale %x\n",
+ x->base_reg ? x->base_reg->reg_name : "none",
+ x->index_reg ? x->index_reg->reg_name : "none",
+ x->log2_scale_factor);
+ fprintf (stdout, " modrm: mode %x reg %x reg/mem %x\n",
+ x->rm.mode, x->rm.reg, x->rm.regmem);
+ fprintf (stdout, " sib: base %x index %x scale %x\n",
+ x->sib.base, x->sib.index, x->sib.scale);
+ fprintf (stdout, " rex: 64bit %x extX %x extY %x extZ %x\n",
+ (x->rex & REX_W) != 0,
+ (x->rex & REX_R) != 0,
+ (x->rex & REX_X) != 0,
+ (x->rex & REX_B) != 0);
+ for (j = 0; j < x->operands; j++)
+ {
+ fprintf (stdout, " #%d: ", j + 1);
+ pt (x->types[j]);
+ fprintf (stdout, "\n");
+ if (x->types[j].bitfield.reg8
+ || x->types[j].bitfield.reg16
+ || x->types[j].bitfield.reg32
+ || x->types[j].bitfield.reg64
+ || x->types[j].bitfield.regmmx
+ || x->types[j].bitfield.regxmm
+ || x->types[j].bitfield.regymm
+ || x->types[j].bitfield.regzmm
+ || x->types[j].bitfield.sreg2
+ || x->types[j].bitfield.sreg3
+ || x->types[j].bitfield.control
+ || x->types[j].bitfield.debug
+ || x->types[j].bitfield.test)
+ fprintf (stdout, "%s\n", x->op[j].regs->reg_name);
+ if (operand_type_check (x->types[j], imm))
+ pe (x->op[j].imms);
+ if (operand_type_check (x->types[j], disp))
+ pe (x->op[j].disps);
+ }
+}
+
+static void
+pte (insn_template *t)
+{
+ unsigned int j;
+ fprintf (stdout, " %d operands ", t->operands);
+ fprintf (stdout, "opcode %x ", t->base_opcode);
+ if (t->extension_opcode != None)
+ fprintf (stdout, "ext %x ", t->extension_opcode);
+ if (t->opcode_modifier.d)
+ fprintf (stdout, "D");
+ if (t->opcode_modifier.w)
+ fprintf (stdout, "W");
+ fprintf (stdout, "\n");
+ for (j = 0; j < t->operands; j++)
+ {
+ fprintf (stdout, " #%d type ", j + 1);
+ pt (t->operand_types[j]);
+ fprintf (stdout, "\n");
+ }
+}
+
+static void
+pe (expressionS *e)
+{
+ fprintf (stdout, " operation %d\n", e->X_op);
+ fprintf (stdout, " add_number %ld (%lx)\n",
+ (long) e->X_add_number, (long) e->X_add_number);
+ if (e->X_add_symbol)
+ {
+ fprintf (stdout, " add_symbol ");
+ ps (e->X_add_symbol);
+ fprintf (stdout, "\n");
+ }
+ if (e->X_op_symbol)
+ {
+ fprintf (stdout, " op_symbol ");
+ ps (e->X_op_symbol);
+ fprintf (stdout, "\n");
+ }
+}
+
+static void
+ps (symbolS *s)
+{
+ fprintf (stdout, "%s type %s%s",
+ S_GET_NAME (s),
+ S_IS_EXTERNAL (s) ? "EXTERNAL " : "",
+ segment_name (S_GET_SEGMENT (s)));
+}
+
+static struct type_name
+ {
+ i386_operand_type mask;
+ const char *name;
+ }
+const type_names[] =
+{
+ { OPERAND_TYPE_REG8, "r8" },
+ { OPERAND_TYPE_REG16, "r16" },
+ { OPERAND_TYPE_REG32, "r32" },
+ { OPERAND_TYPE_REG64, "r64" },
+ { OPERAND_TYPE_IMM8, "i8" },
+ { OPERAND_TYPE_IMM8, "i8s" },
+ { OPERAND_TYPE_IMM16, "i16" },
+ { OPERAND_TYPE_IMM32, "i32" },
+ { OPERAND_TYPE_IMM32S, "i32s" },
+ { OPERAND_TYPE_IMM64, "i64" },
+ { OPERAND_TYPE_IMM1, "i1" },
+ { OPERAND_TYPE_BASEINDEX, "BaseIndex" },
+ { OPERAND_TYPE_DISP8, "d8" },
+ { OPERAND_TYPE_DISP16, "d16" },
+ { OPERAND_TYPE_DISP32, "d32" },
+ { OPERAND_TYPE_DISP32S, "d32s" },
+ { OPERAND_TYPE_DISP64, "d64" },
+ { OPERAND_TYPE_VEC_DISP8, "Vector d8" },
+ { OPERAND_TYPE_INOUTPORTREG, "InOutPortReg" },
+ { OPERAND_TYPE_SHIFTCOUNT, "ShiftCount" },
+ { OPERAND_TYPE_CONTROL, "control reg" },
+ { OPERAND_TYPE_TEST, "test reg" },
+ { OPERAND_TYPE_DEBUG, "debug reg" },
+ { OPERAND_TYPE_FLOATREG, "FReg" },
+ { OPERAND_TYPE_FLOATACC, "FAcc" },
+ { OPERAND_TYPE_SREG2, "SReg2" },
+ { OPERAND_TYPE_SREG3, "SReg3" },
+ { OPERAND_TYPE_ACC, "Acc" },
+ { OPERAND_TYPE_JUMPABSOLUTE, "Jump Absolute" },
+ { OPERAND_TYPE_REGMMX, "rMMX" },
+ { OPERAND_TYPE_REGXMM, "rXMM" },
+ { OPERAND_TYPE_REGYMM, "rYMM" },
+ { OPERAND_TYPE_REGZMM, "rZMM" },
+ { OPERAND_TYPE_REGMASK, "Mask reg" },
+ { OPERAND_TYPE_ESSEG, "es" },
+};
+
+static void
+pt (i386_operand_type t)
+{
+ unsigned int j;
+ i386_operand_type a;
+
+ for (j = 0; j < ARRAY_SIZE (type_names); j++)
+ {
+ a = operand_type_and (t, type_names[j].mask);
+ if (!operand_type_all_zero (&a))
+ fprintf (stdout, "%s, ", type_names[j].name);
+ }
+ fflush (stdout);
+}
+
+#endif /* DEBUG386 */
+
+static bfd_reloc_code_real_type
+reloc (unsigned int size,
+ int pcrel,
+ int sign,
+ bfd_reloc_code_real_type other)
+{
+ if (other != NO_RELOC)
+ {
+ reloc_howto_type *rel;
+
+ if (size == 8)
+ switch (other)
+ {
+ case BFD_RELOC_X86_64_GOT32:
+ return BFD_RELOC_X86_64_GOT64;
+ break;
+ case BFD_RELOC_X86_64_PLTOFF64:
+ return BFD_RELOC_X86_64_PLTOFF64;
+ break;
+ case BFD_RELOC_X86_64_GOTPC32:
+ other = BFD_RELOC_X86_64_GOTPC64;
+ break;
+ case BFD_RELOC_X86_64_GOTPCREL:
+ other = BFD_RELOC_X86_64_GOTPCREL64;
+ break;
+ case BFD_RELOC_X86_64_TPOFF32:
+ other = BFD_RELOC_X86_64_TPOFF64;
+ break;
+ case BFD_RELOC_X86_64_DTPOFF32:
+ other = BFD_RELOC_X86_64_DTPOFF64;
+ break;
+ default:
+ break;
+ }
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (other == BFD_RELOC_SIZE32)
+ {
+ if (size == 8)
+ other = BFD_RELOC_SIZE64;
+ if (pcrel)
+ {
+ as_bad (_("there are no pc-relative size relocations"));
+ return NO_RELOC;
+ }
+ }
+#endif
+
+ /* Sign-checking 4-byte relocations in 16-/32-bit code is pointless. */
+ if (size == 4 && (flag_code != CODE_64BIT || disallow_64bit_reloc))
+ sign = -1;
+
+ rel = bfd_reloc_type_lookup (stdoutput, other);
+ if (!rel)
+ as_bad (_("unknown relocation (%u)"), other);
+ else if (size != bfd_get_reloc_size (rel))
+ as_bad (_("%u-byte relocation cannot be applied to %u-byte field"),
+ bfd_get_reloc_size (rel),
+ size);
+ else if (pcrel && !rel->pc_relative)
+ as_bad (_("non-pc-relative relocation for pc-relative field"));
+ else if ((rel->complain_on_overflow == complain_overflow_signed
+ && !sign)
+ || (rel->complain_on_overflow == complain_overflow_unsigned
+ && sign > 0))
+ as_bad (_("relocated field and relocation type differ in signedness"));
+ else
+ return other;
+ return NO_RELOC;
+ }
+
+ if (pcrel)
+ {
+ if (!sign)
+ as_bad (_("there are no unsigned pc-relative relocations"));
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8_PCREL;
+ case 2: return BFD_RELOC_16_PCREL;
+ case 4: return BFD_RELOC_32_PCREL;
+ case 8: return BFD_RELOC_64_PCREL;
+ }
+ as_bad (_("cannot do %u byte pc-relative relocation"), size);
+ }
+ else
+ {
+ if (sign > 0)
+ switch (size)
+ {
+ case 4: return BFD_RELOC_X86_64_32S;
+ }
+ else
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8;
+ case 2: return BFD_RELOC_16;
+ case 4: return BFD_RELOC_32;
+ case 8: return BFD_RELOC_64;
+ }
+ as_bad (_("cannot do %s %u byte relocation"),
+ sign > 0 ? "signed" : "unsigned", size);
+ }
+
+ return NO_RELOC;
+}
+
+/* Here we decide which fixups can be adjusted to make them relative to
+ the beginning of the section instead of the symbol. Basically we need
+ to make sure that the dynamic relocations are done correctly, so in
+ some cases we force the original symbol to be used. */
+
+int
+tc_i386_fix_adjustable (fixS *fixP ATTRIBUTE_UNUSED)
+{
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (!IS_ELF)
+ return 1;
+
+ /* Don't adjust pc-relative references to merge sections in 64-bit
+ mode. */
+ if (use_rela_relocations
+ && (S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0
+ && fixP->fx_pcrel)
+ return 0;
+
+ /* The x86_64 GOTPCREL are represented as 32bit PCrel relocations
+ and changed later by validate_fix. */
+ if (GOT_symbol && fixP->fx_subsy == GOT_symbol
+ && fixP->fx_r_type == BFD_RELOC_32_PCREL)
+ return 0;
+
+ /* Adjust_reloc_syms doesn't know about the GOT. Need to keep symbol
+ for size relocations. */
+ if (fixP->fx_r_type == BFD_RELOC_SIZE32
+ || fixP->fx_r_type == BFD_RELOC_SIZE64
+ || fixP->fx_r_type == BFD_RELOC_386_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_386_PLT32
+ || fixP->fx_r_type == BFD_RELOC_386_GOT32
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_GD
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_LDM
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_LDO_32
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_IE_32
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_IE
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_GOTIE
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_LE_32
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_LE
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_GOTDESC
+ || fixP->fx_r_type == BFD_RELOC_386_TLS_DESC_CALL
+ || fixP->fx_r_type == BFD_RELOC_X86_64_PLT32
+ || fixP->fx_r_type == BFD_RELOC_X86_64_GOT32
+ || fixP->fx_r_type == BFD_RELOC_X86_64_GOTPCREL
+ || fixP->fx_r_type == BFD_RELOC_X86_64_TLSGD
+ || fixP->fx_r_type == BFD_RELOC_X86_64_TLSLD
+ || fixP->fx_r_type == BFD_RELOC_X86_64_DTPOFF32
+ || fixP->fx_r_type == BFD_RELOC_X86_64_DTPOFF64
+ || fixP->fx_r_type == BFD_RELOC_X86_64_GOTTPOFF
+ || fixP->fx_r_type == BFD_RELOC_X86_64_TPOFF32
+ || fixP->fx_r_type == BFD_RELOC_X86_64_TPOFF64
+ || fixP->fx_r_type == BFD_RELOC_X86_64_GOTOFF64
+ || fixP->fx_r_type == BFD_RELOC_X86_64_GOTPC32_TLSDESC
+ || fixP->fx_r_type == BFD_RELOC_X86_64_TLSDESC_CALL
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+#endif
+ return 1;
+}
+
+static int
+intel_float_operand (const char *mnemonic)
+{
+ /* Note that the value returned is meaningful only for opcodes with (memory)
+ operands, hence the code here is free to improperly handle opcodes that
+ have no operands (for better performance and smaller code). */
+
+ if (mnemonic[0] != 'f')
+ return 0; /* non-math */
+
+ switch (mnemonic[1])
+ {
+ /* fclex, fdecstp, fdisi, femms, feni, fincstp, finit, fsetpm, and
+ the fs segment override prefix not currently handled because no
+ call path can make opcodes without operands get here */
+ case 'i':
+ return 2 /* integer op */;
+ case 'l':
+ if (mnemonic[2] == 'd' && (mnemonic[3] == 'c' || mnemonic[3] == 'e'))
+ return 3; /* fldcw/fldenv */
+ break;
+ case 'n':
+ if (mnemonic[2] != 'o' /* fnop */)
+ return 3; /* non-waiting control op */
+ break;
+ case 'r':
+ if (mnemonic[2] == 's')
+ return 3; /* frstor/frstpm */
+ break;
+ case 's':
+ if (mnemonic[2] == 'a')
+ return 3; /* fsave */
+ if (mnemonic[2] == 't')
+ {
+ switch (mnemonic[3])
+ {
+ case 'c': /* fstcw */
+ case 'd': /* fstdw */
+ case 'e': /* fstenv */
+ case 's': /* fsts[gw] */
+ return 3;
+ }
+ }
+ break;
+ case 'x':
+ if (mnemonic[2] == 'r' || mnemonic[2] == 's')
+ return 0; /* fxsave/fxrstor are not really math ops */
+ break;
+ }
+
+ return 1;
+}
+
+/* Build the VEX prefix. */
+
+static void
+build_vex_prefix (const insn_template *t)
+{
+ unsigned int register_specifier;
+ unsigned int implied_prefix;
+ unsigned int vector_length;
+
+ /* Check register specifier. */
+ if (i.vex.register_specifier)
+ {
+ register_specifier =
+ ~register_number (i.vex.register_specifier) & 0xf;
+ gas_assert ((i.vex.register_specifier->reg_flags & RegVRex) == 0);
+ }
+ else
+ register_specifier = 0xf;
+
+ /* Use 2-byte VEX prefix by swappping destination and source
+ operand. */
+ if (!i.swap_operand
+ && i.operands == i.reg_operands
+ && i.tm.opcode_modifier.vexopcode == VEX0F
+ && i.tm.opcode_modifier.s
+ && i.rex == REX_B)
+ {
+ unsigned int xchg = i.operands - 1;
+ union i386_op temp_op;
+ i386_operand_type temp_type;
+
+ temp_type = i.types[xchg];
+ i.types[xchg] = i.types[0];
+ i.types[0] = temp_type;
+ temp_op = i.op[xchg];
+ i.op[xchg] = i.op[0];
+ i.op[0] = temp_op;
+
+ gas_assert (i.rm.mode == 3);
+
+ i.rex = REX_R;
+ xchg = i.rm.regmem;
+ i.rm.regmem = i.rm.reg;
+ i.rm.reg = xchg;
+
+ /* Use the next insn. */
+ i.tm = t[1];
+ }
+
+ if (i.tm.opcode_modifier.vex == VEXScalar)
+ vector_length = avxscalar;
+ else
+ vector_length = i.tm.opcode_modifier.vex == VEX256 ? 1 : 0;
+
+ switch ((i.tm.base_opcode >> 8) & 0xff)
+ {
+ case 0:
+ implied_prefix = 0;
+ break;
+ case DATA_PREFIX_OPCODE:
+ implied_prefix = 1;
+ break;
+ case REPE_PREFIX_OPCODE:
+ implied_prefix = 2;
+ break;
+ case REPNE_PREFIX_OPCODE:
+ implied_prefix = 3;
+ break;
+ default:
+ abort ();
+ }
+
+ /* Use 2-byte VEX prefix if possible. */
+ if (i.tm.opcode_modifier.vexopcode == VEX0F
+ && i.tm.opcode_modifier.vexw != VEXW1
+ && (i.rex & (REX_W | REX_X | REX_B)) == 0)
+ {
+ /* 2-byte VEX prefix. */
+ unsigned int r;
+
+ i.vex.length = 2;
+ i.vex.bytes[0] = 0xc5;
+
+ /* Check the REX.R bit. */
+ r = (i.rex & REX_R) ? 0 : 1;
+ i.vex.bytes[1] = (r << 7
+ | register_specifier << 3
+ | vector_length << 2
+ | implied_prefix);
+ }
+ else
+ {
+ /* 3-byte VEX prefix. */
+ unsigned int m, w;
+
+ i.vex.length = 3;
+
+ switch (i.tm.opcode_modifier.vexopcode)
+ {
+ case VEX0F:
+ m = 0x1;
+ i.vex.bytes[0] = 0xc4;
+ break;
+ case VEX0F38:
+ m = 0x2;
+ i.vex.bytes[0] = 0xc4;
+ break;
+ case VEX0F3A:
+ m = 0x3;
+ i.vex.bytes[0] = 0xc4;
+ break;
+ case XOP08:
+ m = 0x8;
+ i.vex.bytes[0] = 0x8f;
+ break;
+ case XOP09:
+ m = 0x9;
+ i.vex.bytes[0] = 0x8f;
+ break;
+ case XOP0A:
+ m = 0xa;
+ i.vex.bytes[0] = 0x8f;
+ break;
+ default:
+ abort ();
+ }
+
+ /* The high 3 bits of the second VEX byte are 1's compliment
+ of RXB bits from REX. */
+ i.vex.bytes[1] = (~i.rex & 0x7) << 5 | m;
+
+ /* Check the REX.W bit. */
+ w = (i.rex & REX_W) ? 1 : 0;
+ if (i.tm.opcode_modifier.vexw == VEXW1)
+ w = 1;
+
+ i.vex.bytes[2] = (w << 7
+ | register_specifier << 3
+ | vector_length << 2
+ | implied_prefix);
+ }
+}
+
+/* Build the EVEX prefix. */
+
+static void
+build_evex_prefix (void)
+{
+ unsigned int register_specifier;
+ unsigned int implied_prefix;
+ unsigned int m, w;
+ rex_byte vrex_used = 0;
+
+ /* Check register specifier. */
+ if (i.vex.register_specifier)
+ {
+ gas_assert ((i.vrex & REX_X) == 0);
+
+ register_specifier = i.vex.register_specifier->reg_num;
+ if ((i.vex.register_specifier->reg_flags & RegRex))
+ register_specifier += 8;
+ /* The upper 16 registers are encoded in the fourth byte of the
+ EVEX prefix. */
+ if (!(i.vex.register_specifier->reg_flags & RegVRex))
+ i.vex.bytes[3] = 0x8;
+ register_specifier = ~register_specifier & 0xf;
+ }
+ else
+ {
+ register_specifier = 0xf;
+
+ /* Encode upper 16 vector index register in the fourth byte of
+ the EVEX prefix. */
+ if (!(i.vrex & REX_X))
+ i.vex.bytes[3] = 0x8;
+ else
+ vrex_used |= REX_X;
+ }
+
+ switch ((i.tm.base_opcode >> 8) & 0xff)
+ {
+ case 0:
+ implied_prefix = 0;
+ break;
+ case DATA_PREFIX_OPCODE:
+ implied_prefix = 1;
+ break;
+ case REPE_PREFIX_OPCODE:
+ implied_prefix = 2;
+ break;
+ case REPNE_PREFIX_OPCODE:
+ implied_prefix = 3;
+ break;
+ default:
+ abort ();
+ }
+
+ /* 4 byte EVEX prefix. */
+ i.vex.length = 4;
+ i.vex.bytes[0] = 0x62;
+
+ /* mmmm bits. */
+ switch (i.tm.opcode_modifier.vexopcode)
+ {
+ case VEX0F:
+ m = 1;
+ break;
+ case VEX0F38:
+ m = 2;
+ break;
+ case VEX0F3A:
+ m = 3;
+ break;
+ default:
+ abort ();
+ break;
+ }
+
+ /* The high 3 bits of the second EVEX byte are 1's compliment of RXB
+ bits from REX. */
+ i.vex.bytes[1] = (~i.rex & 0x7) << 5 | m;
+
+ /* The fifth bit of the second EVEX byte is 1's compliment of the
+ REX_R bit in VREX. */
+ if (!(i.vrex & REX_R))
+ i.vex.bytes[1] |= 0x10;
+ else
+ vrex_used |= REX_R;
+
+ if ((i.reg_operands + i.imm_operands) == i.operands)
+ {
+ /* When all operands are registers, the REX_X bit in REX is not
+ used. We reuse it to encode the upper 16 registers, which is
+ indicated by the REX_B bit in VREX. The REX_X bit is encoded
+ as 1's compliment. */
+ if ((i.vrex & REX_B))
+ {
+ vrex_used |= REX_B;
+ i.vex.bytes[1] &= ~0x40;
+ }
+ }
+
+ /* EVEX instructions shouldn't need the REX prefix. */
+ i.vrex &= ~vrex_used;
+ gas_assert (i.vrex == 0);
+
+ /* Check the REX.W bit. */
+ w = (i.rex & REX_W) ? 1 : 0;
+ if (i.tm.opcode_modifier.vexw)
+ {
+ if (i.tm.opcode_modifier.vexw == VEXW1)
+ w = 1;
+ }
+ /* If w is not set it means we are dealing with WIG instruction. */
+ else if (!w)
+ {
+ if (evexwig == evexw1)
+ w = 1;
+ }
+
+ /* Encode the U bit. */
+ implied_prefix |= 0x4;
+
+ /* The third byte of the EVEX prefix. */
+ i.vex.bytes[2] = (w << 7 | register_specifier << 3 | implied_prefix);
+
+ /* The fourth byte of the EVEX prefix. */
+ /* The zeroing-masking bit. */
+ if (i.mask && i.mask->zeroing)
+ i.vex.bytes[3] |= 0x80;
+
+ /* Don't always set the broadcast bit if there is no RC. */
+ if (!i.rounding)
+ {
+ /* Encode the vector length. */
+ unsigned int vec_length;
+
+ switch (i.tm.opcode_modifier.evex)
+ {
+ case EVEXLIG: /* LL' is ignored */
+ vec_length = evexlig << 5;
+ break;
+ case EVEX128:
+ vec_length = 0 << 5;
+ break;
+ case EVEX256:
+ vec_length = 1 << 5;
+ break;
+ case EVEX512:
+ vec_length = 2 << 5;
+ break;
+ default:
+ abort ();
+ break;
+ }
+ i.vex.bytes[3] |= vec_length;
+ /* Encode the broadcast bit. */
+ if (i.broadcast)
+ i.vex.bytes[3] |= 0x10;
+ }
+ else
+ {
+ if (i.rounding->type != saeonly)
+ i.vex.bytes[3] |= 0x10 | (i.rounding->type << 5);
+ else
+ i.vex.bytes[3] |= 0x10 | (evexrcig << 5);
+ }
+
+ if (i.mask && i.mask->mask)
+ i.vex.bytes[3] |= i.mask->mask->reg_num;
+}
+
+static void
+process_immext (void)
+{
+ expressionS *exp;
+
+ if ((i.tm.cpu_flags.bitfield.cpusse3 || i.tm.cpu_flags.bitfield.cpusvme)
+ && i.operands > 0)
+ {
+ /* MONITOR/MWAIT as well as SVME instructions have fixed operands
+ with an opcode suffix which is coded in the same place as an
+ 8-bit immediate field would be.
+ Here we check those operands and remove them afterwards. */
+ unsigned int x;
+
+ for (x = 0; x < i.operands; x++)
+ if (register_number (i.op[x].regs) != x)
+ as_bad (_("can't use register '%s%s' as operand %d in '%s'."),
+ register_prefix, i.op[x].regs->reg_name, x + 1,
+ i.tm.name);
+
+ i.operands = 0;
+ }
+
+ /* These AMD 3DNow! and SSE2 instructions have an opcode suffix
+ which is coded in the same place as an 8-bit immediate field
+ would be. Here we fake an 8-bit immediate operand from the
+ opcode suffix stored in tm.extension_opcode.
+
+ AVX instructions also use this encoding, for some of
+ 3 argument instructions. */
+
+ gas_assert (i.imm_operands <= 1
+ && (i.operands <= 2
+ || ((i.tm.opcode_modifier.vex
+ || i.tm.opcode_modifier.evex)
+ && i.operands <= 4)));
+
+ exp = &im_expressions[i.imm_operands++];
+ i.op[i.operands].imms = exp;
+ i.types[i.operands] = imm8;
+ i.operands++;
+ exp->X_op = O_constant;
+ exp->X_add_number = i.tm.extension_opcode;
+ i.tm.extension_opcode = None;
+}
+
+
+static int
+check_hle (void)
+{
+ switch (i.tm.opcode_modifier.hleprefixok)
+ {
+ default:
+ abort ();
+ case HLEPrefixNone:
+ as_bad (_("invalid instruction `%s' after `%s'"),
+ i.tm.name, i.hle_prefix);
+ return 0;
+ case HLEPrefixLock:
+ if (i.prefix[LOCK_PREFIX])
+ return 1;
+ as_bad (_("missing `lock' with `%s'"), i.hle_prefix);
+ return 0;
+ case HLEPrefixAny:
+ return 1;
+ case HLEPrefixRelease:
+ if (i.prefix[HLE_PREFIX] != XRELEASE_PREFIX_OPCODE)
+ {
+ as_bad (_("instruction `%s' after `xacquire' not allowed"),
+ i.tm.name);
+ return 0;
+ }
+ if (i.mem_operands == 0
+ || !operand_type_check (i.types[i.operands - 1], anymem))
+ {
+ as_bad (_("memory destination needed for instruction `%s'"
+ " after `xrelease'"), i.tm.name);
+ return 0;
+ }
+ return 1;
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. LINE points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *line)
+{
+ unsigned int j;
+ char mnemonic[MAX_MNEM_SIZE];
+ const insn_template *t;
+
+ /* Initialize globals. */
+ memset (&i, '\0', sizeof (i));
+ for (j = 0; j < MAX_OPERANDS; j++)
+ i.reloc[j] = NO_RELOC;
+ memset (disp_expressions, '\0', sizeof (disp_expressions));
+ memset (im_expressions, '\0', sizeof (im_expressions));
+ save_stack_p = save_stack;
+
+ /* First parse an instruction mnemonic & call i386_operand for the operands.
+ We assume that the scrubber has arranged it so that line[0] is the valid
+ start of a (possibly prefixed) mnemonic. */
+
+ line = parse_insn (line, mnemonic);
+ if (line == NULL)
+ return;
+
+ line = parse_operands (line, mnemonic);
+ this_operand = -1;
+ if (line == NULL)
+ return;
+
+ /* Now we've parsed the mnemonic into a set of templates, and have the
+ operands at hand. */
+
+ /* All intel opcodes have reversed operands except for "bound" and
+ "enter". We also don't reverse intersegment "jmp" and "call"
+ instructions with 2 immediate operands so that the immediate segment
+ precedes the offset, as it does when in AT&T mode. */
+ if (intel_syntax
+ && i.operands > 1
+ && (strcmp (mnemonic, "bound") != 0)
+ && (strcmp (mnemonic, "invlpga") != 0)
+ && !(operand_type_check (i.types[0], imm)
+ && operand_type_check (i.types[1], imm)))
+ swap_operands ();
+
+ /* The order of the immediates should be reversed
+ for 2 immediates extrq and insertq instructions */
+ if (i.imm_operands == 2
+ && (strcmp (mnemonic, "extrq") == 0
+ || strcmp (mnemonic, "insertq") == 0))
+ swap_2_operands (0, 1);
+
+ if (i.imm_operands)
+ optimize_imm ();
+
+ /* Don't optimize displacement for movabs since it only takes 64bit
+ displacement. */
+ if (i.disp_operands
+ && i.disp_encoding != disp_encoding_32bit
+ && (flag_code != CODE_64BIT
+ || strcmp (mnemonic, "movabs") != 0))
+ optimize_disp ();
+
+ /* Next, we find a template that matches the given insn,
+ making sure the overlap of the given operands types is consistent
+ with the template operand types. */
+
+ if (!(t = match_template ()))
+ return;
+
+ if (sse_check != check_none
+ && !i.tm.opcode_modifier.noavx
+ && (i.tm.cpu_flags.bitfield.cpusse
+ || i.tm.cpu_flags.bitfield.cpusse2
+ || i.tm.cpu_flags.bitfield.cpusse3
+ || i.tm.cpu_flags.bitfield.cpussse3
+ || i.tm.cpu_flags.bitfield.cpusse4_1
+ || i.tm.cpu_flags.bitfield.cpusse4_2))
+ {
+ (sse_check == check_warning
+ ? as_warn
+ : as_bad) (_("SSE instruction `%s' is used"), i.tm.name);
+ }
+
+ /* Zap movzx and movsx suffix. The suffix has been set from
+ "word ptr" or "byte ptr" on the source operand in Intel syntax
+ or extracted from mnemonic in AT&T syntax. But we'll use
+ the destination register to choose the suffix for encoding. */
+ if ((i.tm.base_opcode & ~9) == 0x0fb6)
+ {
+ /* In Intel syntax, there must be a suffix. In AT&T syntax, if
+ there is no suffix, the default will be byte extension. */
+ if (i.reg_operands != 2
+ && !i.suffix
+ && intel_syntax)
+ as_bad (_("ambiguous operand size for `%s'"), i.tm.name);
+
+ i.suffix = 0;
+ }
+
+ if (i.tm.opcode_modifier.fwait)
+ if (!add_prefix (FWAIT_OPCODE))
+ return;
+
+ /* Check if REP prefix is OK. */
+ if (i.rep_prefix && !i.tm.opcode_modifier.repprefixok)
+ {
+ as_bad (_("invalid instruction `%s' after `%s'"),
+ i.tm.name, i.rep_prefix);
+ return;
+ }
+
+ /* Check for lock without a lockable instruction. Destination operand
+ must be memory unless it is xchg (0x86). */
+ if (i.prefix[LOCK_PREFIX]
+ && (!i.tm.opcode_modifier.islockable
+ || i.mem_operands == 0
+ || (i.tm.base_opcode != 0x86
+ && !operand_type_check (i.types[i.operands - 1], anymem))))
+ {
+ as_bad (_("expecting lockable instruction after `lock'"));
+ return;
+ }
+
+ /* Check if HLE prefix is OK. */
+ if (i.hle_prefix && !check_hle ())
+ return;
+
+ /* Check BND prefix. */
+ if (i.bnd_prefix && !i.tm.opcode_modifier.bndprefixok)
+ as_bad (_("expecting valid branch instruction after `bnd'"));
+
+ if (i.tm.cpu_flags.bitfield.cpumpx
+ && flag_code == CODE_64BIT
+ && i.prefix[ADDR_PREFIX])
+ as_bad (_("32-bit address isn't allowed in 64-bit MPX instructions."));
+
+ /* Insert BND prefix. */
+ if (add_bnd_prefix
+ && i.tm.opcode_modifier.bndprefixok
+ && !i.prefix[BND_PREFIX])
+ add_prefix (BND_PREFIX_OPCODE);
+
+ /* Check string instruction segment overrides. */
+ if (i.tm.opcode_modifier.isstring && i.mem_operands != 0)
+ {
+ if (!check_string ())
+ return;
+ i.disp_operands = 0;
+ }
+
+ if (!process_suffix ())
+ return;
+
+ /* Update operand types. */
+ for (j = 0; j < i.operands; j++)
+ i.types[j] = operand_type_and (i.types[j], i.tm.operand_types[j]);
+
+ /* Make still unresolved immediate matches conform to size of immediate
+ given in i.suffix. */
+ if (!finalize_imm ())
+ return;
+
+ if (i.types[0].bitfield.imm1)
+ i.imm_operands = 0; /* kludge for shift insns. */
+
+ /* We only need to check those implicit registers for instructions
+ with 3 operands or less. */
+ if (i.operands <= 3)
+ for (j = 0; j < i.operands; j++)
+ if (i.types[j].bitfield.inoutportreg
+ || i.types[j].bitfield.shiftcount
+ || i.types[j].bitfield.acc
+ || i.types[j].bitfield.floatacc)
+ i.reg_operands--;
+
+ /* ImmExt should be processed after SSE2AVX. */
+ if (!i.tm.opcode_modifier.sse2avx
+ && i.tm.opcode_modifier.immext)
+ process_immext ();
+
+ /* For insns with operands there are more diddles to do to the opcode. */
+ if (i.operands)
+ {
+ if (!process_operands ())
+ return;
+ }
+ else if (!quiet_warnings && i.tm.opcode_modifier.ugh)
+ {
+ /* UnixWare fsub no args is alias for fsubp, fadd -> faddp, etc. */
+ as_warn (_("translating to `%sp'"), i.tm.name);
+ }
+
+ if (i.tm.opcode_modifier.vex || i.tm.opcode_modifier.evex)
+ {
+ if (flag_code == CODE_16BIT)
+ {
+ as_bad (_("instruction `%s' isn't supported in 16-bit mode."),
+ i.tm.name);
+ return;
+ }
+
+ if (i.tm.opcode_modifier.vex)
+ build_vex_prefix (t);
+ else
+ build_evex_prefix ();
+ }
+
+ /* Handle conversion of 'int $3' --> special int3 insn. XOP or FMA4
+ instructions may define INT_OPCODE as well, so avoid this corner
+ case for those instructions that use MODRM. */
+ if (i.tm.base_opcode == INT_OPCODE
+ && !i.tm.opcode_modifier.modrm
+ && i.op[0].imms->X_add_number == 3)
+ {
+ i.tm.base_opcode = INT3_OPCODE;
+ i.imm_operands = 0;
+ }
+
+ if ((i.tm.opcode_modifier.jump
+ || i.tm.opcode_modifier.jumpbyte
+ || i.tm.opcode_modifier.jumpdword)
+ && i.op[0].disps->X_op == O_constant)
+ {
+ /* Convert "jmp constant" (and "call constant") to a jump (call) to
+ the absolute address given by the constant. Since ix86 jumps and
+ calls are pc relative, we need to generate a reloc. */
+ i.op[0].disps->X_add_symbol = &abs_symbol;
+ i.op[0].disps->X_op = O_symbol;
+ }
+
+ if (i.tm.opcode_modifier.rex64)
+ i.rex |= REX_W;
+
+ /* For 8 bit registers we need an empty rex prefix. Also if the
+ instruction already has a prefix, we need to convert old
+ registers to new ones. */
+
+ if ((i.types[0].bitfield.reg8
+ && (i.op[0].regs->reg_flags & RegRex64) != 0)
+ || (i.types[1].bitfield.reg8
+ && (i.op[1].regs->reg_flags & RegRex64) != 0)
+ || ((i.types[0].bitfield.reg8
+ || i.types[1].bitfield.reg8)
+ && i.rex != 0))
+ {
+ int x;
+
+ i.rex |= REX_OPCODE;
+ for (x = 0; x < 2; x++)
+ {
+ /* Look for 8 bit operand that uses old registers. */
+ if (i.types[x].bitfield.reg8
+ && (i.op[x].regs->reg_flags & RegRex64) == 0)
+ {
+ /* In case it is "hi" register, give up. */
+ if (i.op[x].regs->reg_num > 3)
+ as_bad (_("can't encode register '%s%s' in an "
+ "instruction requiring REX prefix."),
+ register_prefix, i.op[x].regs->reg_name);
+
+ /* Otherwise it is equivalent to the extended register.
+ Since the encoding doesn't change this is merely
+ cosmetic cleanup for debug output. */
+
+ i.op[x].regs = i.op[x].regs + 8;
+ }
+ }
+ }
+
+ if (i.rex != 0)
+ add_prefix (REX_OPCODE | i.rex);
+
+ /* We are ready to output the insn. */
+ output_insn ();
+}
+
+static char *
+parse_insn (char *line, char *mnemonic)
+{
+ char *l = line;
+ char *token_start = l;
+ char *mnem_p;
+ int supported;
+ const insn_template *t;
+ char *dot_p = NULL;
+
+ while (1)
+ {
+ mnem_p = mnemonic;
+ while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0)
+ {
+ if (*mnem_p == '.')
+ dot_p = mnem_p;
+ mnem_p++;
+ if (mnem_p >= mnemonic + MAX_MNEM_SIZE)
+ {
+ as_bad (_("no such instruction: `%s'"), token_start);
+ return NULL;
+ }
+ l++;
+ }
+ if (!is_space_char (*l)
+ && *l != END_OF_INSN
+ && (intel_syntax
+ || (*l != PREFIX_SEPARATOR
+ && *l != ',')))
+ {
+ as_bad (_("invalid character %s in mnemonic"),
+ output_invalid (*l));
+ return NULL;
+ }
+ if (token_start == l)
+ {
+ if (!intel_syntax && *l == PREFIX_SEPARATOR)
+ as_bad (_("expecting prefix; got nothing"));
+ else
+ as_bad (_("expecting mnemonic; got nothing"));
+ return NULL;
+ }
+
+ /* Look up instruction (or prefix) via hash table. */
+ current_templates = (const templates *) hash_find (op_hash, mnemonic);
+
+ if (*l != END_OF_INSN
+ && (!is_space_char (*l) || l[1] != END_OF_INSN)
+ && current_templates
+ && current_templates->start->opcode_modifier.isprefix)
+ {
+ if (!cpu_flags_check_cpu64 (current_templates->start->cpu_flags))
+ {
+ as_bad ((flag_code != CODE_64BIT
+ ? _("`%s' is only supported in 64-bit mode")
+ : _("`%s' is not supported in 64-bit mode")),
+ current_templates->start->name);
+ return NULL;
+ }
+ /* If we are in 16-bit mode, do not allow addr16 or data16.
+ Similarly, in 32-bit mode, do not allow addr32 or data32. */
+ if ((current_templates->start->opcode_modifier.size16
+ || current_templates->start->opcode_modifier.size32)
+ && flag_code != CODE_64BIT
+ && (current_templates->start->opcode_modifier.size32
+ ^ (flag_code == CODE_16BIT)))
+ {
+ as_bad (_("redundant %s prefix"),
+ current_templates->start->name);
+ return NULL;
+ }
+ /* Add prefix, checking for repeated prefixes. */
+ switch (add_prefix (current_templates->start->base_opcode))
+ {
+ case PREFIX_EXIST:
+ return NULL;
+ case PREFIX_REP:
+ if (current_templates->start->cpu_flags.bitfield.cpuhle)
+ i.hle_prefix = current_templates->start->name;
+ else if (current_templates->start->cpu_flags.bitfield.cpumpx)
+ i.bnd_prefix = current_templates->start->name;
+ else
+ i.rep_prefix = current_templates->start->name;
+ break;
+ default:
+ break;
+ }
+ /* Skip past PREFIX_SEPARATOR and reset token_start. */
+ token_start = ++l;
+ }
+ else
+ break;
+ }
+
+ if (!current_templates)
+ {
+ /* Check if we should swap operand or force 32bit displacement in
+ encoding. */
+ if (mnem_p - 2 == dot_p && dot_p[1] == 's')
+ i.swap_operand = 1;
+ else if (mnem_p - 3 == dot_p
+ && dot_p[1] == 'd'
+ && dot_p[2] == '8')
+ i.disp_encoding = disp_encoding_8bit;
+ else if (mnem_p - 4 == dot_p
+ && dot_p[1] == 'd'
+ && dot_p[2] == '3'
+ && dot_p[3] == '2')
+ i.disp_encoding = disp_encoding_32bit;
+ else
+ goto check_suffix;
+ mnem_p = dot_p;
+ *dot_p = '\0';
+ current_templates = (const templates *) hash_find (op_hash, mnemonic);
+ }
+
+ if (!current_templates)
+ {
+check_suffix:
+ /* See if we can get a match by trimming off a suffix. */
+ switch (mnem_p[-1])
+ {
+ case WORD_MNEM_SUFFIX:
+ if (intel_syntax && (intel_float_operand (mnemonic) & 2))
+ i.suffix = SHORT_MNEM_SUFFIX;
+ else
+ case BYTE_MNEM_SUFFIX:
+ case QWORD_MNEM_SUFFIX:
+ i.suffix = mnem_p[-1];
+ mnem_p[-1] = '\0';
+ current_templates = (const templates *) hash_find (op_hash,
+ mnemonic);
+ break;
+ case SHORT_MNEM_SUFFIX:
+ case LONG_MNEM_SUFFIX:
+ if (!intel_syntax)
+ {
+ i.suffix = mnem_p[-1];
+ mnem_p[-1] = '\0';
+ current_templates = (const templates *) hash_find (op_hash,
+ mnemonic);
+ }
+ break;
+
+ /* Intel Syntax. */
+ case 'd':
+ if (intel_syntax)
+ {
+ if (intel_float_operand (mnemonic) == 1)
+ i.suffix = SHORT_MNEM_SUFFIX;
+ else
+ i.suffix = LONG_MNEM_SUFFIX;
+ mnem_p[-1] = '\0';
+ current_templates = (const templates *) hash_find (op_hash,
+ mnemonic);
+ }
+ break;
+ }
+ if (!current_templates)
+ {
+ as_bad (_("no such instruction: `%s'"), token_start);
+ return NULL;
+ }
+ }
+
+ if (current_templates->start->opcode_modifier.jump
+ || current_templates->start->opcode_modifier.jumpbyte)
+ {
+ /* Check for a branch hint. We allow ",pt" and ",pn" for
+ predict taken and predict not taken respectively.
+ I'm not sure that branch hints actually do anything on loop
+ and jcxz insns (JumpByte) for current Pentium4 chips. They
+ may work in the future and it doesn't hurt to accept them
+ now. */
+ if (l[0] == ',' && l[1] == 'p')
+ {
+ if (l[2] == 't')
+ {
+ if (!add_prefix (DS_PREFIX_OPCODE))
+ return NULL;
+ l += 3;
+ }
+ else if (l[2] == 'n')
+ {
+ if (!add_prefix (CS_PREFIX_OPCODE))
+ return NULL;
+ l += 3;
+ }
+ }
+ }
+ /* Any other comma loses. */
+ if (*l == ',')
+ {
+ as_bad (_("invalid character %s in mnemonic"),
+ output_invalid (*l));
+ return NULL;
+ }
+
+ /* Check if instruction is supported on specified architecture. */
+ supported = 0;
+ for (t = current_templates->start; t < current_templates->end; ++t)
+ {
+ supported |= cpu_flags_match (t);
+ if (supported == CPU_FLAGS_PERFECT_MATCH)
+ goto skip;
+ }
+
+ if (!(supported & CPU_FLAGS_64BIT_MATCH))
+ {
+ as_bad (flag_code == CODE_64BIT
+ ? _("`%s' is not supported in 64-bit mode")
+ : _("`%s' is only supported in 64-bit mode"),
+ current_templates->start->name);
+ return NULL;
+ }
+ if (supported != CPU_FLAGS_PERFECT_MATCH)
+ {
+ as_bad (_("`%s' is not supported on `%s%s'"),
+ current_templates->start->name,
+ cpu_arch_name ? cpu_arch_name : default_arch,
+ cpu_sub_arch_name ? cpu_sub_arch_name : "");
+ return NULL;
+ }
+
+skip:
+ if (!cpu_arch_flags.bitfield.cpui386
+ && (flag_code != CODE_16BIT))
+ {
+ as_warn (_("use .code16 to ensure correct addressing mode"));
+ }
+
+ return l;
+}
+
+static char *
+parse_operands (char *l, const char *mnemonic)
+{
+ char *token_start;
+
+ /* 1 if operand is pending after ','. */
+ unsigned int expecting_operand = 0;
+
+ /* Non-zero if operand parens not balanced. */
+ unsigned int paren_not_balanced;
+
+ while (*l != END_OF_INSN)
+ {
+ /* Skip optional white space before operand. */
+ if (is_space_char (*l))
+ ++l;
+ if (!is_operand_char (*l) && *l != END_OF_INSN)
+ {
+ as_bad (_("invalid character %s before operand %d"),
+ output_invalid (*l),
+ i.operands + 1);
+ return NULL;
+ }
+ token_start = l; /* after white space */
+ paren_not_balanced = 0;
+ while (paren_not_balanced || *l != ',')
+ {
+ if (*l == END_OF_INSN)
+ {
+ if (paren_not_balanced)
+ {
+ if (!intel_syntax)
+ as_bad (_("unbalanced parenthesis in operand %d."),
+ i.operands + 1);
+ else
+ as_bad (_("unbalanced brackets in operand %d."),
+ i.operands + 1);
+ return NULL;
+ }
+ else
+ break; /* we are done */
+ }
+ else if (!is_operand_char (*l) && !is_space_char (*l))
+ {
+ as_bad (_("invalid character %s in operand %d"),
+ output_invalid (*l),
+ i.operands + 1);
+ return NULL;
+ }
+ if (!intel_syntax)
+ {
+ if (*l == '(')
+ ++paren_not_balanced;
+ if (*l == ')')
+ --paren_not_balanced;
+ }
+ else
+ {
+ if (*l == '[')
+ ++paren_not_balanced;
+ if (*l == ']')
+ --paren_not_balanced;
+ }
+ l++;
+ }
+ if (l != token_start)
+ { /* Yes, we've read in another operand. */
+ unsigned int operand_ok;
+ this_operand = i.operands++;
+ i.types[this_operand].bitfield.unspecified = 1;
+ if (i.operands > MAX_OPERANDS)
+ {
+ as_bad (_("spurious operands; (%d operands/instruction max)"),
+ MAX_OPERANDS);
+ return NULL;
+ }
+ /* Now parse operand adding info to 'i' as we go along. */
+ END_STRING_AND_SAVE (l);
+
+ if (intel_syntax)
+ operand_ok =
+ i386_intel_operand (token_start,
+ intel_float_operand (mnemonic));
+ else
+ operand_ok = i386_att_operand (token_start);
+
+ RESTORE_END_STRING (l);
+ if (!operand_ok)
+ return NULL;
+ }
+ else
+ {
+ if (expecting_operand)
+ {
+ expecting_operand_after_comma:
+ as_bad (_("expecting operand after ','; got nothing"));
+ return NULL;
+ }
+ if (*l == ',')
+ {
+ as_bad (_("expecting operand before ','; got nothing"));
+ return NULL;
+ }
+ }
+
+ /* Now *l must be either ',' or END_OF_INSN. */
+ if (*l == ',')
+ {
+ if (*++l == END_OF_INSN)
+ {
+ /* Just skip it, if it's \n complain. */
+ goto expecting_operand_after_comma;
+ }
+ expecting_operand = 1;
+ }
+ }
+ return l;
+}
+
+static void
+swap_2_operands (int xchg1, int xchg2)
+{
+ union i386_op temp_op;
+ i386_operand_type temp_type;
+ enum bfd_reloc_code_real temp_reloc;
+
+ temp_type = i.types[xchg2];
+ i.types[xchg2] = i.types[xchg1];
+ i.types[xchg1] = temp_type;
+ temp_op = i.op[xchg2];
+ i.op[xchg2] = i.op[xchg1];
+ i.op[xchg1] = temp_op;
+ temp_reloc = i.reloc[xchg2];
+ i.reloc[xchg2] = i.reloc[xchg1];
+ i.reloc[xchg1] = temp_reloc;
+
+ if (i.mask)
+ {
+ if (i.mask->operand == xchg1)
+ i.mask->operand = xchg2;
+ else if (i.mask->operand == xchg2)
+ i.mask->operand = xchg1;
+ }
+ if (i.broadcast)
+ {
+ if (i.broadcast->operand == xchg1)
+ i.broadcast->operand = xchg2;
+ else if (i.broadcast->operand == xchg2)
+ i.broadcast->operand = xchg1;
+ }
+ if (i.rounding)
+ {
+ if (i.rounding->operand == xchg1)
+ i.rounding->operand = xchg2;
+ else if (i.rounding->operand == xchg2)
+ i.rounding->operand = xchg1;
+ }
+}
+
+static void
+swap_operands (void)
+{
+ switch (i.operands)
+ {
+ case 5:
+ case 4:
+ swap_2_operands (1, i.operands - 2);
+ case 3:
+ case 2:
+ swap_2_operands (0, i.operands - 1);
+ break;
+ default:
+ abort ();
+ }
+
+ if (i.mem_operands == 2)
+ {
+ const seg_entry *temp_seg;
+ temp_seg = i.seg[0];
+ i.seg[0] = i.seg[1];
+ i.seg[1] = temp_seg;
+ }
+}
+
+/* Try to ensure constant immediates are represented in the smallest
+ opcode possible. */
+static void
+optimize_imm (void)
+{
+ char guess_suffix = 0;
+ int op;
+
+ if (i.suffix)
+ guess_suffix = i.suffix;
+ else if (i.reg_operands)
+ {
+ /* Figure out a suffix from the last register operand specified.
+ We can't do this properly yet, ie. excluding InOutPortReg,
+ but the following works for instructions with immediates.
+ In any case, we can't set i.suffix yet. */
+ for (op = i.operands; --op >= 0;)
+ if (i.types[op].bitfield.reg8)
+ {
+ guess_suffix = BYTE_MNEM_SUFFIX;
+ break;
+ }
+ else if (i.types[op].bitfield.reg16)
+ {
+ guess_suffix = WORD_MNEM_SUFFIX;
+ break;
+ }
+ else if (i.types[op].bitfield.reg32)
+ {
+ guess_suffix = LONG_MNEM_SUFFIX;
+ break;
+ }
+ else if (i.types[op].bitfield.reg64)
+ {
+ guess_suffix = QWORD_MNEM_SUFFIX;
+ break;
+ }
+ }
+ else if ((flag_code == CODE_16BIT) ^ (i.prefix[DATA_PREFIX] != 0))
+ guess_suffix = WORD_MNEM_SUFFIX;
+
+ for (op = i.operands; --op >= 0;)
+ if (operand_type_check (i.types[op], imm))
+ {
+ switch (i.op[op].imms->X_op)
+ {
+ case O_constant:
+ /* If a suffix is given, this operand may be shortened. */
+ switch (guess_suffix)
+ {
+ case LONG_MNEM_SUFFIX:
+ i.types[op].bitfield.imm32 = 1;
+ i.types[op].bitfield.imm64 = 1;
+ break;
+ case WORD_MNEM_SUFFIX:
+ i.types[op].bitfield.imm16 = 1;
+ i.types[op].bitfield.imm32 = 1;
+ i.types[op].bitfield.imm32s = 1;
+ i.types[op].bitfield.imm64 = 1;
+ break;
+ case BYTE_MNEM_SUFFIX:
+ i.types[op].bitfield.imm8 = 1;
+ i.types[op].bitfield.imm8s = 1;
+ i.types[op].bitfield.imm16 = 1;
+ i.types[op].bitfield.imm32 = 1;
+ i.types[op].bitfield.imm32s = 1;
+ i.types[op].bitfield.imm64 = 1;
+ break;
+ }
+
+ /* If this operand is at most 16 bits, convert it
+ to a signed 16 bit number before trying to see
+ whether it will fit in an even smaller size.
+ This allows a 16-bit operand such as $0xffe0 to
+ be recognised as within Imm8S range. */
+ if ((i.types[op].bitfield.imm16)
+ && (i.op[op].imms->X_add_number & ~(offsetT) 0xffff) == 0)
+ {
+ i.op[op].imms->X_add_number =
+ (((i.op[op].imms->X_add_number & 0xffff) ^ 0x8000) - 0x8000);
+ }
+ if ((i.types[op].bitfield.imm32)
+ && ((i.op[op].imms->X_add_number & ~(((offsetT) 2 << 31) - 1))
+ == 0))
+ {
+ i.op[op].imms->X_add_number = ((i.op[op].imms->X_add_number
+ ^ ((offsetT) 1 << 31))
+ - ((offsetT) 1 << 31));
+ }
+ i.types[op]
+ = operand_type_or (i.types[op],
+ smallest_imm_type (i.op[op].imms->X_add_number));
+
+ /* We must avoid matching of Imm32 templates when 64bit
+ only immediate is available. */
+ if (guess_suffix == QWORD_MNEM_SUFFIX)
+ i.types[op].bitfield.imm32 = 0;
+ break;
+
+ case O_absent:
+ case O_register:
+ abort ();
+
+ /* Symbols and expressions. */
+ default:
+ /* Convert symbolic operand to proper sizes for matching, but don't
+ prevent matching a set of insns that only supports sizes other
+ than those matching the insn suffix. */
+ {
+ i386_operand_type mask, allowed;
+ const insn_template *t;
+
+ operand_type_set (&mask, 0);
+ operand_type_set (&allowed, 0);
+
+ for (t = current_templates->start;
+ t < current_templates->end;
+ ++t)
+ allowed = operand_type_or (allowed,
+ t->operand_types[op]);
+ switch (guess_suffix)
+ {
+ case QWORD_MNEM_SUFFIX:
+ mask.bitfield.imm64 = 1;
+ mask.bitfield.imm32s = 1;
+ break;
+ case LONG_MNEM_SUFFIX:
+ mask.bitfield.imm32 = 1;
+ break;
+ case WORD_MNEM_SUFFIX:
+ mask.bitfield.imm16 = 1;
+ break;
+ case BYTE_MNEM_SUFFIX:
+ mask.bitfield.imm8 = 1;
+ break;
+ default:
+ break;
+ }
+ allowed = operand_type_and (mask, allowed);
+ if (!operand_type_all_zero (&allowed))
+ i.types[op] = operand_type_and (i.types[op], mask);
+ }
+ break;
+ }
+ }
+}
+
+/* Try to use the smallest displacement type too. */
+static void
+optimize_disp (void)
+{
+ int op;
+
+ for (op = i.operands; --op >= 0;)
+ if (operand_type_check (i.types[op], disp))
+ {
+ if (i.op[op].disps->X_op == O_constant)
+ {
+ offsetT op_disp = i.op[op].disps->X_add_number;
+
+ if (i.types[op].bitfield.disp16
+ && (op_disp & ~(offsetT) 0xffff) == 0)
+ {
+ /* If this operand is at most 16 bits, convert
+ to a signed 16 bit number and don't use 64bit
+ displacement. */
+ op_disp = (((op_disp & 0xffff) ^ 0x8000) - 0x8000);
+ i.types[op].bitfield.disp64 = 0;
+ }
+ if (i.types[op].bitfield.disp32
+ && (op_disp & ~(((offsetT) 2 << 31) - 1)) == 0)
+ {
+ /* If this operand is at most 32 bits, convert
+ to a signed 32 bit number and don't use 64bit
+ displacement. */
+ op_disp &= (((offsetT) 2 << 31) - 1);
+ op_disp = (op_disp ^ ((offsetT) 1 << 31)) - ((addressT) 1 << 31);
+ i.types[op].bitfield.disp64 = 0;
+ }
+ if (!op_disp && i.types[op].bitfield.baseindex)
+ {
+ i.types[op].bitfield.disp8 = 0;
+ i.types[op].bitfield.disp16 = 0;
+ i.types[op].bitfield.disp32 = 0;
+ i.types[op].bitfield.disp32s = 0;
+ i.types[op].bitfield.disp64 = 0;
+ i.op[op].disps = 0;
+ i.disp_operands--;
+ }
+ else if (flag_code == CODE_64BIT)
+ {
+ if (fits_in_signed_long (op_disp))
+ {
+ i.types[op].bitfield.disp64 = 0;
+ i.types[op].bitfield.disp32s = 1;
+ }
+ if (i.prefix[ADDR_PREFIX]
+ && fits_in_unsigned_long (op_disp))
+ i.types[op].bitfield.disp32 = 1;
+ }
+ if ((i.types[op].bitfield.disp32
+ || i.types[op].bitfield.disp32s
+ || i.types[op].bitfield.disp16)
+ && fits_in_signed_byte (op_disp))
+ i.types[op].bitfield.disp8 = 1;
+ }
+ else if (i.reloc[op] == BFD_RELOC_386_TLS_DESC_CALL
+ || i.reloc[op] == BFD_RELOC_X86_64_TLSDESC_CALL)
+ {
+ fix_new_exp (frag_now, frag_more (0) - frag_now->fr_literal, 0,
+ i.op[op].disps, 0, i.reloc[op]);
+ i.types[op].bitfield.disp8 = 0;
+ i.types[op].bitfield.disp16 = 0;
+ i.types[op].bitfield.disp32 = 0;
+ i.types[op].bitfield.disp32s = 0;
+ i.types[op].bitfield.disp64 = 0;
+ }
+ else
+ /* We only support 64bit displacement on constants. */
+ i.types[op].bitfield.disp64 = 0;
+ }
+}
+
+/* Check if operands are valid for the instruction. */
+
+static int
+check_VecOperands (const insn_template *t)
+{
+ unsigned int op;
+
+ /* Without VSIB byte, we can't have a vector register for index. */
+ if (!t->opcode_modifier.vecsib
+ && i.index_reg
+ && (i.index_reg->reg_type.bitfield.regxmm
+ || i.index_reg->reg_type.bitfield.regymm
+ || i.index_reg->reg_type.bitfield.regzmm))
+ {
+ i.error = unsupported_vector_index_register;
+ return 1;
+ }
+
+ /* Check if default mask is allowed. */
+ if (t->opcode_modifier.nodefmask
+ && (!i.mask || i.mask->mask->reg_num == 0))
+ {
+ i.error = no_default_mask;
+ return 1;
+ }
+
+ /* For VSIB byte, we need a vector register for index, and all vector
+ registers must be distinct. */
+ if (t->opcode_modifier.vecsib)
+ {
+ if (!i.index_reg
+ || !((t->opcode_modifier.vecsib == VecSIB128
+ && i.index_reg->reg_type.bitfield.regxmm)
+ || (t->opcode_modifier.vecsib == VecSIB256
+ && i.index_reg->reg_type.bitfield.regymm)
+ || (t->opcode_modifier.vecsib == VecSIB512
+ && i.index_reg->reg_type.bitfield.regzmm)))
+ {
+ i.error = invalid_vsib_address;
+ return 1;
+ }
+
+ gas_assert (i.reg_operands == 2 || i.mask);
+ if (i.reg_operands == 2 && !i.mask)
+ {
+ gas_assert (i.types[0].bitfield.regxmm
+ || i.types[0].bitfield.regymm);
+ gas_assert (i.types[2].bitfield.regxmm
+ || i.types[2].bitfield.regymm);
+ if (operand_check == check_none)
+ return 0;
+ if (register_number (i.op[0].regs)
+ != register_number (i.index_reg)
+ && register_number (i.op[2].regs)
+ != register_number (i.index_reg)
+ && register_number (i.op[0].regs)
+ != register_number (i.op[2].regs))
+ return 0;
+ if (operand_check == check_error)
+ {
+ i.error = invalid_vector_register_set;
+ return 1;
+ }
+ as_warn (_("mask, index, and destination registers should be distinct"));
+ }
+ else if (i.reg_operands == 1 && i.mask)
+ {
+ if ((i.types[1].bitfield.regymm
+ || i.types[1].bitfield.regzmm)
+ && (register_number (i.op[1].regs)
+ == register_number (i.index_reg)))
+ {
+ if (operand_check == check_error)
+ {
+ i.error = invalid_vector_register_set;
+ return 1;
+ }
+ if (operand_check != check_none)
+ as_warn (_("index and destination registers should be distinct"));
+ }
+ }
+ }
+
+ /* Check if broadcast is supported by the instruction and is applied
+ to the memory operand. */
+ if (i.broadcast)
+ {
+ int broadcasted_opnd_size;
+
+ /* Check if specified broadcast is supported in this instruction,
+ and it's applied to memory operand of DWORD or QWORD type,
+ depending on VecESize. */
+ if (i.broadcast->type != t->opcode_modifier.broadcast
+ || !i.types[i.broadcast->operand].bitfield.mem
+ || (t->opcode_modifier.vecesize == 0
+ && !i.types[i.broadcast->operand].bitfield.dword
+ && !i.types[i.broadcast->operand].bitfield.unspecified)
+ || (t->opcode_modifier.vecesize == 1
+ && !i.types[i.broadcast->operand].bitfield.qword
+ && !i.types[i.broadcast->operand].bitfield.unspecified))
+ goto bad_broadcast;
+
+ broadcasted_opnd_size = t->opcode_modifier.vecesize ? 64 : 32;
+ if (i.broadcast->type == BROADCAST_1TO16)
+ broadcasted_opnd_size <<= 4; /* Broadcast 1to16. */
+ else if (i.broadcast->type == BROADCAST_1TO8)
+ broadcasted_opnd_size <<= 3; /* Broadcast 1to8. */
+ else if (i.broadcast->type == BROADCAST_1TO4)
+ broadcasted_opnd_size <<= 2; /* Broadcast 1to4. */
+ else if (i.broadcast->type == BROADCAST_1TO2)
+ broadcasted_opnd_size <<= 1; /* Broadcast 1to2. */
+ else
+ goto bad_broadcast;
+
+ if ((broadcasted_opnd_size == 256
+ && !t->operand_types[i.broadcast->operand].bitfield.ymmword)
+ || (broadcasted_opnd_size == 512
+ && !t->operand_types[i.broadcast->operand].bitfield.zmmword))
+ {
+ bad_broadcast:
+ i.error = unsupported_broadcast;
+ return 1;
+ }
+ }
+ /* If broadcast is supported in this instruction, we need to check if
+ operand of one-element size isn't specified without broadcast. */
+ else if (t->opcode_modifier.broadcast && i.mem_operands)
+ {
+ /* Find memory operand. */
+ for (op = 0; op < i.operands; op++)
+ if (operand_type_check (i.types[op], anymem))
+ break;
+ gas_assert (op < i.operands);
+ /* Check size of the memory operand. */
+ if ((t->opcode_modifier.vecesize == 0
+ && i.types[op].bitfield.dword)
+ || (t->opcode_modifier.vecesize == 1
+ && i.types[op].bitfield.qword))
+ {
+ i.error = broadcast_needed;
+ return 1;
+ }
+ }
+
+ /* Check if requested masking is supported. */
+ if (i.mask
+ && (!t->opcode_modifier.masking
+ || (i.mask->zeroing
+ && t->opcode_modifier.masking == MERGING_MASKING)))
+ {
+ i.error = unsupported_masking;
+ return 1;
+ }
+
+ /* Check if masking is applied to dest operand. */
+ if (i.mask && (i.mask->operand != (int) (i.operands - 1)))
+ {
+ i.error = mask_not_on_destination;
+ return 1;
+ }
+
+ /* Check RC/SAE. */
+ if (i.rounding)
+ {
+ if ((i.rounding->type != saeonly
+ && !t->opcode_modifier.staticrounding)
+ || (i.rounding->type == saeonly
+ && (t->opcode_modifier.staticrounding
+ || !t->opcode_modifier.sae)))
+ {
+ i.error = unsupported_rc_sae;
+ return 1;
+ }
+ /* If the instruction has several immediate operands and one of
+ them is rounding, the rounding operand should be the last
+ immediate operand. */
+ if (i.imm_operands > 1
+ && i.rounding->operand != (int) (i.imm_operands - 1))
+ {
+ i.error = rc_sae_operand_not_last_imm;
+ return 1;
+ }
+ }
+
+ /* Check vector Disp8 operand. */
+ if (t->opcode_modifier.disp8memshift)
+ {
+ if (i.broadcast)
+ i.memshift = t->opcode_modifier.vecesize ? 3 : 2;
+ else
+ i.memshift = t->opcode_modifier.disp8memshift;
+
+ for (op = 0; op < i.operands; op++)
+ if (operand_type_check (i.types[op], disp)
+ && i.op[op].disps->X_op == O_constant)
+ {
+ offsetT value = i.op[op].disps->X_add_number;
+ int vec_disp8_ok = fits_in_vec_disp8 (value);
+ if (t->operand_types [op].bitfield.vec_disp8)
+ {
+ if (vec_disp8_ok)
+ i.types[op].bitfield.vec_disp8 = 1;
+ else
+ {
+ /* Vector insn can only have Vec_Disp8/Disp32 in
+ 32/64bit modes, and Vec_Disp8/Disp16 in 16bit
+ mode. */
+ i.types[op].bitfield.disp8 = 0;
+ if (flag_code != CODE_16BIT)
+ i.types[op].bitfield.disp16 = 0;
+ }
+ }
+ else if (flag_code != CODE_16BIT)
+ {
+ /* One form of this instruction supports vector Disp8.
+ Try vector Disp8 if we need to use Disp32. */
+ if (vec_disp8_ok && !fits_in_signed_byte (value))
+ {
+ i.error = try_vector_disp8;
+ return 1;
+ }
+ }
+ }
+ }
+ else
+ i.memshift = -1;
+
+ return 0;
+}
+
+/* Check if operands are valid for the instruction. Update VEX
+ operand types. */
+
+static int
+VEX_check_operands (const insn_template *t)
+{
+ /* VREX is only valid with EVEX prefix. */
+ if (i.need_vrex && !t->opcode_modifier.evex)
+ {
+ i.error = invalid_register_operand;
+ return 1;
+ }
+
+ if (!t->opcode_modifier.vex)
+ return 0;
+
+ /* Only check VEX_Imm4, which must be the first operand. */
+ if (t->operand_types[0].bitfield.vec_imm4)
+ {
+ if (i.op[0].imms->X_op != O_constant
+ || !fits_in_imm4 (i.op[0].imms->X_add_number))
+ {
+ i.error = bad_imm4;
+ return 1;
+ }
+
+ /* Turn off Imm8 so that update_imm won't complain. */
+ i.types[0] = vec_imm4;
+ }
+
+ return 0;
+}
+
+static const insn_template *
+match_template (void)
+{
+ /* Points to template once we've found it. */
+ const insn_template *t;
+ i386_operand_type overlap0, overlap1, overlap2, overlap3;
+ i386_operand_type overlap4;
+ unsigned int found_reverse_match;
+ i386_opcode_modifier suffix_check;
+ i386_operand_type operand_types [MAX_OPERANDS];
+ int addr_prefix_disp;
+ unsigned int j;
+ unsigned int found_cpu_match;
+ unsigned int check_register;
+ enum i386_error specific_error = 0;
+
+#if MAX_OPERANDS != 5
+# error "MAX_OPERANDS must be 5."
+#endif
+
+ found_reverse_match = 0;
+ addr_prefix_disp = -1;
+
+ memset (&suffix_check, 0, sizeof (suffix_check));
+ if (i.suffix == BYTE_MNEM_SUFFIX)
+ suffix_check.no_bsuf = 1;
+ else if (i.suffix == WORD_MNEM_SUFFIX)
+ suffix_check.no_wsuf = 1;
+ else if (i.suffix == SHORT_MNEM_SUFFIX)
+ suffix_check.no_ssuf = 1;
+ else if (i.suffix == LONG_MNEM_SUFFIX)
+ suffix_check.no_lsuf = 1;
+ else if (i.suffix == QWORD_MNEM_SUFFIX)
+ suffix_check.no_qsuf = 1;
+ else if (i.suffix == LONG_DOUBLE_MNEM_SUFFIX)
+ suffix_check.no_ldsuf = 1;
+
+ /* Must have right number of operands. */
+ i.error = number_of_operands_mismatch;
+
+ for (t = current_templates->start; t < current_templates->end; t++)
+ {
+ addr_prefix_disp = -1;
+
+ if (i.operands != t->operands)
+ continue;
+
+ /* Check processor support. */
+ i.error = unsupported;
+ found_cpu_match = (cpu_flags_match (t)
+ == CPU_FLAGS_PERFECT_MATCH);
+ if (!found_cpu_match)
+ continue;
+
+ /* Check old gcc support. */
+ i.error = old_gcc_only;
+ if (!old_gcc && t->opcode_modifier.oldgcc)
+ continue;
+
+ /* Check AT&T mnemonic. */
+ i.error = unsupported_with_intel_mnemonic;
+ if (intel_mnemonic && t->opcode_modifier.attmnemonic)
+ continue;
+
+ /* Check AT&T/Intel syntax. */
+ i.error = unsupported_syntax;
+ if ((intel_syntax && t->opcode_modifier.attsyntax)
+ || (!intel_syntax && t->opcode_modifier.intelsyntax))
+ continue;
+
+ /* Check the suffix, except for some instructions in intel mode. */
+ i.error = invalid_instruction_suffix;
+ if ((!intel_syntax || !t->opcode_modifier.ignoresize)
+ && ((t->opcode_modifier.no_bsuf && suffix_check.no_bsuf)
+ || (t->opcode_modifier.no_wsuf && suffix_check.no_wsuf)
+ || (t->opcode_modifier.no_lsuf && suffix_check.no_lsuf)
+ || (t->opcode_modifier.no_ssuf && suffix_check.no_ssuf)
+ || (t->opcode_modifier.no_qsuf && suffix_check.no_qsuf)
+ || (t->opcode_modifier.no_ldsuf && suffix_check.no_ldsuf)))
+ continue;
+
+ if (!operand_size_match (t))
+ continue;
+
+ for (j = 0; j < MAX_OPERANDS; j++)
+ operand_types[j] = t->operand_types[j];
+
+ /* In general, don't allow 64-bit operands in 32-bit mode. */
+ if (i.suffix == QWORD_MNEM_SUFFIX
+ && flag_code != CODE_64BIT
+ && (intel_syntax
+ ? (!t->opcode_modifier.ignoresize
+ && !intel_float_operand (t->name))
+ : intel_float_operand (t->name) != 2)
+ && ((!operand_types[0].bitfield.regmmx
+ && !operand_types[0].bitfield.regxmm
+ && !operand_types[0].bitfield.regymm
+ && !operand_types[0].bitfield.regzmm)
+ || (!operand_types[t->operands > 1].bitfield.regmmx
+ && operand_types[t->operands > 1].bitfield.regxmm
+ && operand_types[t->operands > 1].bitfield.regymm
+ && operand_types[t->operands > 1].bitfield.regzmm))
+ && (t->base_opcode != 0x0fc7
+ || t->extension_opcode != 1 /* cmpxchg8b */))
+ continue;
+
+ /* In general, don't allow 32-bit operands on pre-386. */
+ else if (i.suffix == LONG_MNEM_SUFFIX
+ && !cpu_arch_flags.bitfield.cpui386
+ && (intel_syntax
+ ? (!t->opcode_modifier.ignoresize
+ && !intel_float_operand (t->name))
+ : intel_float_operand (t->name) != 2)
+ && ((!operand_types[0].bitfield.regmmx
+ && !operand_types[0].bitfield.regxmm)
+ || (!operand_types[t->operands > 1].bitfield.regmmx
+ && operand_types[t->operands > 1].bitfield.regxmm)))
+ continue;
+
+ /* Do not verify operands when there are none. */
+ else
+ {
+ if (!t->operands)
+ /* We've found a match; break out of loop. */
+ break;
+ }
+
+ /* Address size prefix will turn Disp64/Disp32/Disp16 operand
+ into Disp32/Disp16/Disp32 operand. */
+ if (i.prefix[ADDR_PREFIX] != 0)
+ {
+ /* There should be only one Disp operand. */
+ switch (flag_code)
+ {
+ case CODE_16BIT:
+ for (j = 0; j < MAX_OPERANDS; j++)
+ {
+ if (operand_types[j].bitfield.disp16)
+ {
+ addr_prefix_disp = j;
+ operand_types[j].bitfield.disp32 = 1;
+ operand_types[j].bitfield.disp16 = 0;
+ break;
+ }
+ }
+ break;
+ case CODE_32BIT:
+ for (j = 0; j < MAX_OPERANDS; j++)
+ {
+ if (operand_types[j].bitfield.disp32)
+ {
+ addr_prefix_disp = j;
+ operand_types[j].bitfield.disp32 = 0;
+ operand_types[j].bitfield.disp16 = 1;
+ break;
+ }
+ }
+ break;
+ case CODE_64BIT:
+ for (j = 0; j < MAX_OPERANDS; j++)
+ {
+ if (operand_types[j].bitfield.disp64)
+ {
+ addr_prefix_disp = j;
+ operand_types[j].bitfield.disp64 = 0;
+ operand_types[j].bitfield.disp32 = 1;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ /* We check register size if needed. */
+ check_register = t->opcode_modifier.checkregsize;
+ overlap0 = operand_type_and (i.types[0], operand_types[0]);
+ switch (t->operands)
+ {
+ case 1:
+ if (!operand_type_match (overlap0, i.types[0]))
+ continue;
+ break;
+ case 2:
+ /* xchg %eax, %eax is a special case. It is an aliase for nop
+ only in 32bit mode and we can use opcode 0x90. In 64bit
+ mode, we can't use 0x90 for xchg %eax, %eax since it should
+ zero-extend %eax to %rax. */
+ if (flag_code == CODE_64BIT
+ && t->base_opcode == 0x90
+ && operand_type_equal (&i.types [0], &acc32)
+ && operand_type_equal (&i.types [1], &acc32))
+ continue;
+ if (i.swap_operand)
+ {
+ /* If we swap operand in encoding, we either match
+ the next one or reverse direction of operands. */
+ if (t->opcode_modifier.s)
+ continue;
+ else if (t->opcode_modifier.d)
+ goto check_reverse;
+ }
+
+ case 3:
+ /* If we swap operand in encoding, we match the next one. */
+ if (i.swap_operand && t->opcode_modifier.s)
+ continue;
+ case 4:
+ case 5:
+ overlap1 = operand_type_and (i.types[1], operand_types[1]);
+ if (!operand_type_match (overlap0, i.types[0])
+ || !operand_type_match (overlap1, i.types[1])
+ || (check_register
+ && !operand_type_register_match (overlap0, i.types[0],
+ operand_types[0],
+ overlap1, i.types[1],
+ operand_types[1])))
+ {
+ /* Check if other direction is valid ... */
+ if (!t->opcode_modifier.d && !t->opcode_modifier.floatd)
+ continue;
+
+check_reverse:
+ /* Try reversing direction of operands. */
+ overlap0 = operand_type_and (i.types[0], operand_types[1]);
+ overlap1 = operand_type_and (i.types[1], operand_types[0]);
+ if (!operand_type_match (overlap0, i.types[0])
+ || !operand_type_match (overlap1, i.types[1])
+ || (check_register
+ && !operand_type_register_match (overlap0,
+ i.types[0],
+ operand_types[1],
+ overlap1,
+ i.types[1],
+ operand_types[0])))
+ {
+ /* Does not match either direction. */
+ continue;
+ }
+ /* found_reverse_match holds which of D or FloatDR
+ we've found. */
+ if (t->opcode_modifier.d)
+ found_reverse_match = Opcode_D;
+ else if (t->opcode_modifier.floatd)
+ found_reverse_match = Opcode_FloatD;
+ else
+ found_reverse_match = 0;
+ if (t->opcode_modifier.floatr)
+ found_reverse_match |= Opcode_FloatR;
+ }
+ else
+ {
+ /* Found a forward 2 operand match here. */
+ switch (t->operands)
+ {
+ case 5:
+ overlap4 = operand_type_and (i.types[4],
+ operand_types[4]);
+ case 4:
+ overlap3 = operand_type_and (i.types[3],
+ operand_types[3]);
+ case 3:
+ overlap2 = operand_type_and (i.types[2],
+ operand_types[2]);
+ break;
+ }
+
+ switch (t->operands)
+ {
+ case 5:
+ if (!operand_type_match (overlap4, i.types[4])
+ || !operand_type_register_match (overlap3,
+ i.types[3],
+ operand_types[3],
+ overlap4,
+ i.types[4],
+ operand_types[4]))
+ continue;
+ case 4:
+ if (!operand_type_match (overlap3, i.types[3])
+ || (check_register
+ && !operand_type_register_match (overlap2,
+ i.types[2],
+ operand_types[2],
+ overlap3,
+ i.types[3],
+ operand_types[3])))
+ continue;
+ case 3:
+ /* Here we make use of the fact that there are no
+ reverse match 3 operand instructions, and all 3
+ operand instructions only need to be checked for
+ register consistency between operands 2 and 3. */
+ if (!operand_type_match (overlap2, i.types[2])
+ || (check_register
+ && !operand_type_register_match (overlap1,
+ i.types[1],
+ operand_types[1],
+ overlap2,
+ i.types[2],
+ operand_types[2])))
+ continue;
+ break;
+ }
+ }
+ /* Found either forward/reverse 2, 3 or 4 operand match here:
+ slip through to break. */
+ }
+ if (!found_cpu_match)
+ {
+ found_reverse_match = 0;
+ continue;
+ }
+
+ /* Check if vector and VEX operands are valid. */
+ if (check_VecOperands (t) || VEX_check_operands (t))
+ {
+ specific_error = i.error;
+ continue;
+ }
+
+ /* We've found a match; break out of loop. */
+ break;
+ }
+
+ if (t == current_templates->end)
+ {
+ /* We found no match. */
+ const char *err_msg;
+ switch (specific_error ? specific_error : i.error)
+ {
+ default:
+ abort ();
+ case operand_size_mismatch:
+ err_msg = _("operand size mismatch");
+ break;
+ case operand_type_mismatch:
+ err_msg = _("operand type mismatch");
+ break;
+ case register_type_mismatch:
+ err_msg = _("register type mismatch");
+ break;
+ case number_of_operands_mismatch:
+ err_msg = _("number of operands mismatch");
+ break;
+ case invalid_instruction_suffix:
+ err_msg = _("invalid instruction suffix");
+ break;
+ case bad_imm4:
+ err_msg = _("constant doesn't fit in 4 bits");
+ break;
+ case old_gcc_only:
+ err_msg = _("only supported with old gcc");
+ break;
+ case unsupported_with_intel_mnemonic:
+ err_msg = _("unsupported with Intel mnemonic");
+ break;
+ case unsupported_syntax:
+ err_msg = _("unsupported syntax");
+ break;
+ case unsupported:
+ as_bad (_("unsupported instruction `%s'"),
+ current_templates->start->name);
+ return NULL;
+ case invalid_vsib_address:
+ err_msg = _("invalid VSIB address");
+ break;
+ case invalid_vector_register_set:
+ err_msg = _("mask, index, and destination registers must be distinct");
+ break;
+ case unsupported_vector_index_register:
+ err_msg = _("unsupported vector index register");
+ break;
+ case unsupported_broadcast:
+ err_msg = _("unsupported broadcast");
+ break;
+ case broadcast_not_on_src_operand:
+ err_msg = _("broadcast not on source memory operand");
+ break;
+ case broadcast_needed:
+ err_msg = _("broadcast is needed for operand of such type");
+ break;
+ case unsupported_masking:
+ err_msg = _("unsupported masking");
+ break;
+ case mask_not_on_destination:
+ err_msg = _("mask not on destination operand");
+ break;
+ case no_default_mask:
+ err_msg = _("default mask isn't allowed");
+ break;
+ case unsupported_rc_sae:
+ err_msg = _("unsupported static rounding/sae");
+ break;
+ case rc_sae_operand_not_last_imm:
+ if (intel_syntax)
+ err_msg = _("RC/SAE operand must precede immediate operands");
+ else
+ err_msg = _("RC/SAE operand must follow immediate operands");
+ break;
+ case invalid_register_operand:
+ err_msg = _("invalid register operand");
+ break;
+ }
+ as_bad (_("%s for `%s'"), err_msg,
+ current_templates->start->name);
+ return NULL;
+ }
+
+ if (!quiet_warnings)
+ {
+ if (!intel_syntax
+ && (i.types[0].bitfield.jumpabsolute
+ != operand_types[0].bitfield.jumpabsolute))
+ {
+ as_warn (_("indirect %s without `*'"), t->name);
+ }
+
+ if (t->opcode_modifier.isprefix
+ && t->opcode_modifier.ignoresize)
+ {
+ /* Warn them that a data or address size prefix doesn't
+ affect assembly of the next line of code. */
+ as_warn (_("stand-alone `%s' prefix"), t->name);
+ }
+ }
+
+ /* Copy the template we found. */
+ i.tm = *t;
+
+ if (addr_prefix_disp != -1)
+ i.tm.operand_types[addr_prefix_disp]
+ = operand_types[addr_prefix_disp];
+
+ if (found_reverse_match)
+ {
+ /* If we found a reverse match we must alter the opcode
+ direction bit. found_reverse_match holds bits to change
+ (different for int & float insns). */
+
+ i.tm.base_opcode ^= found_reverse_match;
+
+ i.tm.operand_types[0] = operand_types[1];
+ i.tm.operand_types[1] = operand_types[0];
+ }
+
+ return t;
+}
+
+static int
+check_string (void)
+{
+ int mem_op = operand_type_check (i.types[0], anymem) ? 0 : 1;
+ if (i.tm.operand_types[mem_op].bitfield.esseg)
+ {
+ if (i.seg[0] != NULL && i.seg[0] != &es)
+ {
+ as_bad (_("`%s' operand %d must use `%ses' segment"),
+ i.tm.name,
+ mem_op + 1,
+ register_prefix);
+ return 0;
+ }
+ /* There's only ever one segment override allowed per instruction.
+ This instruction possibly has a legal segment override on the
+ second operand, so copy the segment to where non-string
+ instructions store it, allowing common code. */
+ i.seg[0] = i.seg[1];
+ }
+ else if (i.tm.operand_types[mem_op + 1].bitfield.esseg)
+ {
+ if (i.seg[1] != NULL && i.seg[1] != &es)
+ {
+ as_bad (_("`%s' operand %d must use `%ses' segment"),
+ i.tm.name,
+ mem_op + 2,
+ register_prefix);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+process_suffix (void)
+{
+ /* If matched instruction specifies an explicit instruction mnemonic
+ suffix, use it. */
+ if (i.tm.opcode_modifier.size16)
+ i.suffix = WORD_MNEM_SUFFIX;
+ else if (i.tm.opcode_modifier.size32)
+ i.suffix = LONG_MNEM_SUFFIX;
+ else if (i.tm.opcode_modifier.size64)
+ i.suffix = QWORD_MNEM_SUFFIX;
+ else if (i.reg_operands)
+ {
+ /* If there's no instruction mnemonic suffix we try to invent one
+ based on register operands. */
+ if (!i.suffix)
+ {
+ /* We take i.suffix from the last register operand specified,
+ Destination register type is more significant than source
+ register type. crc32 in SSE4.2 prefers source register
+ type. */
+ if (i.tm.base_opcode == 0xf20f38f1)
+ {
+ if (i.types[0].bitfield.reg16)
+ i.suffix = WORD_MNEM_SUFFIX;
+ else if (i.types[0].bitfield.reg32)
+ i.suffix = LONG_MNEM_SUFFIX;
+ else if (i.types[0].bitfield.reg64)
+ i.suffix = QWORD_MNEM_SUFFIX;
+ }
+ else if (i.tm.base_opcode == 0xf20f38f0)
+ {
+ if (i.types[0].bitfield.reg8)
+ i.suffix = BYTE_MNEM_SUFFIX;
+ }
+
+ if (!i.suffix)
+ {
+ int op;
+
+ if (i.tm.base_opcode == 0xf20f38f1
+ || i.tm.base_opcode == 0xf20f38f0)
+ {
+ /* We have to know the operand size for crc32. */
+ as_bad (_("ambiguous memory operand size for `%s`"),
+ i.tm.name);
+ return 0;
+ }
+
+ for (op = i.operands; --op >= 0;)
+ if (!i.tm.operand_types[op].bitfield.inoutportreg)
+ {
+ if (i.types[op].bitfield.reg8)
+ {
+ i.suffix = BYTE_MNEM_SUFFIX;
+ break;
+ }
+ else if (i.types[op].bitfield.reg16)
+ {
+ i.suffix = WORD_MNEM_SUFFIX;
+ break;
+ }
+ else if (i.types[op].bitfield.reg32)
+ {
+ i.suffix = LONG_MNEM_SUFFIX;
+ break;
+ }
+ else if (i.types[op].bitfield.reg64)
+ {
+ i.suffix = QWORD_MNEM_SUFFIX;
+ break;
+ }
+ }
+ }
+ }
+ else if (i.suffix == BYTE_MNEM_SUFFIX)
+ {
+ if (intel_syntax
+ && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.no_bsuf)
+ i.suffix = 0;
+ else if (!check_byte_reg ())
+ return 0;
+ }
+ else if (i.suffix == LONG_MNEM_SUFFIX)
+ {
+ if (intel_syntax
+ && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.no_lsuf)
+ i.suffix = 0;
+ else if (!check_long_reg ())
+ return 0;
+ }
+ else if (i.suffix == QWORD_MNEM_SUFFIX)
+ {
+ if (intel_syntax
+ && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.no_qsuf)
+ i.suffix = 0;
+ else if (!check_qword_reg ())
+ return 0;
+ }
+ else if (i.suffix == WORD_MNEM_SUFFIX)
+ {
+ if (intel_syntax
+ && i.tm.opcode_modifier.ignoresize
+ && i.tm.opcode_modifier.no_wsuf)
+ i.suffix = 0;
+ else if (!check_word_reg ())
+ return 0;
+ }
+ else if (i.suffix == XMMWORD_MNEM_SUFFIX
+ || i.suffix == YMMWORD_MNEM_SUFFIX
+ || i.suffix == ZMMWORD_MNEM_SUFFIX)
+ {
+ /* Skip if the instruction has x/y/z suffix. match_template
+ should check if it is a valid suffix. */
+ }
+ else if (intel_syntax && i.tm.opcode_modifier.ignoresize)
+ /* Do nothing if the instruction is going to ignore the prefix. */
+ ;
+ else
+ abort ();
+ }
+ else if (i.tm.opcode_modifier.defaultsize
+ && !i.suffix
+ /* exclude fldenv/frstor/fsave/fstenv */
+ && i.tm.opcode_modifier.no_ssuf)
+ {
+ i.suffix = stackop_size;
+ }
+ else if (intel_syntax
+ && !i.suffix
+ && (i.tm.operand_types[0].bitfield.jumpabsolute
+ || i.tm.opcode_modifier.jumpbyte
+ || i.tm.opcode_modifier.jumpintersegment
+ || (i.tm.base_opcode == 0x0f01 /* [ls][gi]dt */
+ && i.tm.extension_opcode <= 3)))
+ {
+ switch (flag_code)
+ {
+ case CODE_64BIT:
+ if (!i.tm.opcode_modifier.no_qsuf)
+ {
+ i.suffix = QWORD_MNEM_SUFFIX;
+ break;
+ }
+ case CODE_32BIT:
+ if (!i.tm.opcode_modifier.no_lsuf)
+ i.suffix = LONG_MNEM_SUFFIX;
+ break;
+ case CODE_16BIT:
+ if (!i.tm.opcode_modifier.no_wsuf)
+ i.suffix = WORD_MNEM_SUFFIX;
+ break;
+ }
+ }
+
+ if (!i.suffix)
+ {
+ if (!intel_syntax)
+ {
+ if (i.tm.opcode_modifier.w)
+ {
+ as_bad (_("no instruction mnemonic suffix given and "
+ "no register operands; can't size instruction"));
+ return 0;
+ }
+ }
+ else
+ {
+ unsigned int suffixes;
+
+ suffixes = !i.tm.opcode_modifier.no_bsuf;
+ if (!i.tm.opcode_modifier.no_wsuf)
+ suffixes |= 1 << 1;
+ if (!i.tm.opcode_modifier.no_lsuf)
+ suffixes |= 1 << 2;
+ if (!i.tm.opcode_modifier.no_ldsuf)
+ suffixes |= 1 << 3;
+ if (!i.tm.opcode_modifier.no_ssuf)
+ suffixes |= 1 << 4;
+ if (!i.tm.opcode_modifier.no_qsuf)
+ suffixes |= 1 << 5;
+
+ /* There are more than suffix matches. */
+ if (i.tm.opcode_modifier.w
+ || ((suffixes & (suffixes - 1))
+ && !i.tm.opcode_modifier.defaultsize
+ && !i.tm.opcode_modifier.ignoresize))
+ {
+ as_bad (_("ambiguous operand size for `%s'"), i.tm.name);
+ return 0;
+ }
+ }
+ }
+
+ /* Change the opcode based on the operand size given by i.suffix;
+ We don't need to change things for byte insns. */
+
+ if (i.suffix
+ && i.suffix != BYTE_MNEM_SUFFIX
+ && i.suffix != XMMWORD_MNEM_SUFFIX
+ && i.suffix != YMMWORD_MNEM_SUFFIX
+ && i.suffix != ZMMWORD_MNEM_SUFFIX)
+ {
+ /* It's not a byte, select word/dword operation. */
+ if (i.tm.opcode_modifier.w)
+ {
+ if (i.tm.opcode_modifier.shortform)
+ i.tm.base_opcode |= 8;
+ else
+ i.tm.base_opcode |= 1;
+ }
+
+ /* Now select between word & dword operations via the operand
+ size prefix, except for instructions that will ignore this
+ prefix anyway. */
+ if (i.tm.opcode_modifier.addrprefixop0)
+ {
+ /* The address size override prefix changes the size of the
+ first operand. */
+ if ((flag_code == CODE_32BIT
+ && i.op->regs[0].reg_type.bitfield.reg16)
+ || (flag_code != CODE_32BIT
+ && i.op->regs[0].reg_type.bitfield.reg32))
+ if (!add_prefix (ADDR_PREFIX_OPCODE))
+ return 0;
+ }
+ else if (i.suffix != QWORD_MNEM_SUFFIX
+ && i.suffix != LONG_DOUBLE_MNEM_SUFFIX
+ && !i.tm.opcode_modifier.ignoresize
+ && !i.tm.opcode_modifier.floatmf
+ && ((i.suffix == LONG_MNEM_SUFFIX) == (flag_code == CODE_16BIT)
+ || (flag_code == CODE_64BIT
+ && i.tm.opcode_modifier.jumpbyte)))
+ {
+ unsigned int prefix = DATA_PREFIX_OPCODE;
+
+ if (i.tm.opcode_modifier.jumpbyte) /* jcxz, loop */
+ prefix = ADDR_PREFIX_OPCODE;
+
+ if (!add_prefix (prefix))
+ return 0;
+ }
+
+ /* Set mode64 for an operand. */
+ if (i.suffix == QWORD_MNEM_SUFFIX
+ && flag_code == CODE_64BIT
+ && !i.tm.opcode_modifier.norex64)
+ {
+ /* Special case for xchg %rax,%rax. It is NOP and doesn't
+ need rex64. cmpxchg8b is also a special case. */
+ if (! (i.operands == 2
+ && i.tm.base_opcode == 0x90
+ && i.tm.extension_opcode == None
+ && operand_type_equal (&i.types [0], &acc64)
+ && operand_type_equal (&i.types [1], &acc64))
+ && ! (i.operands == 1
+ && i.tm.base_opcode == 0xfc7
+ && i.tm.extension_opcode == 1
+ && !operand_type_check (i.types [0], reg)
+ && operand_type_check (i.types [0], anymem)))
+ i.rex |= REX_W;
+ }
+
+ /* Size floating point instruction. */
+ if (i.suffix == LONG_MNEM_SUFFIX)
+ if (i.tm.opcode_modifier.floatmf)
+ i.tm.base_opcode ^= 4;
+ }
+
+ return 1;
+}
+
+static int
+check_byte_reg (void)
+{
+ int op;
+
+ for (op = i.operands; --op >= 0;)
+ {
+ /* If this is an eight bit register, it's OK. If it's the 16 or
+ 32 bit version of an eight bit register, we will just use the
+ low portion, and that's OK too. */
+ if (i.types[op].bitfield.reg8)
+ continue;
+
+ /* I/O port address operands are OK too. */
+ if (i.tm.operand_types[op].bitfield.inoutportreg)
+ continue;
+
+ /* crc32 doesn't generate this warning. */
+ if (i.tm.base_opcode == 0xf20f38f0)
+ continue;
+
+ if ((i.types[op].bitfield.reg16
+ || i.types[op].bitfield.reg32
+ || i.types[op].bitfield.reg64)
+ && i.op[op].regs->reg_num < 4
+ /* Prohibit these changes in 64bit mode, since the lowering
+ would be more complicated. */
+ && flag_code != CODE_64BIT)
+ {
+#if REGISTER_WARNINGS
+ if (!quiet_warnings)
+ as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"),
+ register_prefix,
+ (i.op[op].regs + (i.types[op].bitfield.reg16
+ ? REGNAM_AL - REGNAM_AX
+ : REGNAM_AL - REGNAM_EAX))->reg_name,
+ register_prefix,
+ i.op[op].regs->reg_name,
+ i.suffix);
+#endif
+ continue;
+ }
+ /* Any other register is bad. */
+ if (i.types[op].bitfield.reg16
+ || i.types[op].bitfield.reg32
+ || i.types[op].bitfield.reg64
+ || i.types[op].bitfield.regmmx
+ || i.types[op].bitfield.regxmm
+ || i.types[op].bitfield.regymm
+ || i.types[op].bitfield.regzmm
+ || i.types[op].bitfield.sreg2
+ || i.types[op].bitfield.sreg3
+ || i.types[op].bitfield.control
+ || i.types[op].bitfield.debug
+ || i.types[op].bitfield.test
+ || i.types[op].bitfield.floatreg
+ || i.types[op].bitfield.floatacc)
+ {
+ as_bad (_("`%s%s' not allowed with `%s%c'"),
+ register_prefix,
+ i.op[op].regs->reg_name,
+ i.tm.name,
+ i.suffix);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+check_long_reg (void)
+{
+ int op;
+
+ for (op = i.operands; --op >= 0;)
+ /* Reject eight bit registers, except where the template requires
+ them. (eg. movzb) */
+ if (i.types[op].bitfield.reg8
+ && (i.tm.operand_types[op].bitfield.reg16
+ || i.tm.operand_types[op].bitfield.reg32
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ as_bad (_("`%s%s' not allowed with `%s%c'"),
+ register_prefix,
+ i.op[op].regs->reg_name,
+ i.tm.name,
+ i.suffix);
+ return 0;
+ }
+ /* Warn if the e prefix on a general reg is missing. */
+ else if ((!quiet_warnings || flag_code == CODE_64BIT)
+ && i.types[op].bitfield.reg16
+ && (i.tm.operand_types[op].bitfield.reg32
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ /* Prohibit these changes in the 64bit mode, since the
+ lowering is more complicated. */
+ if (flag_code == CODE_64BIT)
+ {
+ as_bad (_("incorrect register `%s%s' used with `%c' suffix"),
+ register_prefix, i.op[op].regs->reg_name,
+ i.suffix);
+ return 0;
+ }
+#if REGISTER_WARNINGS
+ as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"),
+ register_prefix,
+ (i.op[op].regs + REGNAM_EAX - REGNAM_AX)->reg_name,
+ register_prefix, i.op[op].regs->reg_name, i.suffix);
+#endif
+ }
+ /* Warn if the r prefix on a general reg is present. */
+ else if (i.types[op].bitfield.reg64
+ && (i.tm.operand_types[op].bitfield.reg32
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ if (intel_syntax
+ && i.tm.opcode_modifier.toqword
+ && !i.types[0].bitfield.regxmm)
+ {
+ /* Convert to QWORD. We want REX byte. */
+ i.suffix = QWORD_MNEM_SUFFIX;
+ }
+ else
+ {
+ as_bad (_("incorrect register `%s%s' used with `%c' suffix"),
+ register_prefix, i.op[op].regs->reg_name,
+ i.suffix);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+check_qword_reg (void)
+{
+ int op;
+
+ for (op = i.operands; --op >= 0; )
+ /* Reject eight bit registers, except where the template requires
+ them. (eg. movzb) */
+ if (i.types[op].bitfield.reg8
+ && (i.tm.operand_types[op].bitfield.reg16
+ || i.tm.operand_types[op].bitfield.reg32
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ as_bad (_("`%s%s' not allowed with `%s%c'"),
+ register_prefix,
+ i.op[op].regs->reg_name,
+ i.tm.name,
+ i.suffix);
+ return 0;
+ }
+ /* Warn if the r prefix on a general reg is missing. */
+ else if ((i.types[op].bitfield.reg16
+ || i.types[op].bitfield.reg32)
+ && (i.tm.operand_types[op].bitfield.reg32
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ /* Prohibit these changes in the 64bit mode, since the
+ lowering is more complicated. */
+ if (intel_syntax
+ && i.tm.opcode_modifier.todword
+ && !i.types[0].bitfield.regxmm)
+ {
+ /* Convert to DWORD. We don't want REX byte. */
+ i.suffix = LONG_MNEM_SUFFIX;
+ }
+ else
+ {
+ as_bad (_("incorrect register `%s%s' used with `%c' suffix"),
+ register_prefix, i.op[op].regs->reg_name,
+ i.suffix);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int
+check_word_reg (void)
+{
+ int op;
+ for (op = i.operands; --op >= 0;)
+ /* Reject eight bit registers, except where the template requires
+ them. (eg. movzb) */
+ if (i.types[op].bitfield.reg8
+ && (i.tm.operand_types[op].bitfield.reg16
+ || i.tm.operand_types[op].bitfield.reg32
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ as_bad (_("`%s%s' not allowed with `%s%c'"),
+ register_prefix,
+ i.op[op].regs->reg_name,
+ i.tm.name,
+ i.suffix);
+ return 0;
+ }
+ /* Warn if the e or r prefix on a general reg is present. */
+ else if ((!quiet_warnings || flag_code == CODE_64BIT)
+ && (i.types[op].bitfield.reg32
+ || i.types[op].bitfield.reg64)
+ && (i.tm.operand_types[op].bitfield.reg16
+ || i.tm.operand_types[op].bitfield.acc))
+ {
+ /* Prohibit these changes in the 64bit mode, since the
+ lowering is more complicated. */
+ if (flag_code == CODE_64BIT)
+ {
+ as_bad (_("incorrect register `%s%s' used with `%c' suffix"),
+ register_prefix, i.op[op].regs->reg_name,
+ i.suffix);
+ return 0;
+ }
+#if REGISTER_WARNINGS
+ as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"),
+ register_prefix,
+ (i.op[op].regs + REGNAM_AX - REGNAM_EAX)->reg_name,
+ register_prefix, i.op[op].regs->reg_name, i.suffix);
+#endif
+ }
+ return 1;
+}
+
+static int
+update_imm (unsigned int j)
+{
+ i386_operand_type overlap = i.types[j];
+ if ((overlap.bitfield.imm8
+ || overlap.bitfield.imm8s
+ || overlap.bitfield.imm16
+ || overlap.bitfield.imm32
+ || overlap.bitfield.imm32s
+ || overlap.bitfield.imm64)
+ && !operand_type_equal (&overlap, &imm8)
+ && !operand_type_equal (&overlap, &imm8s)
+ && !operand_type_equal (&overlap, &imm16)
+ && !operand_type_equal (&overlap, &imm32)
+ && !operand_type_equal (&overlap, &imm32s)
+ && !operand_type_equal (&overlap, &imm64))
+ {
+ if (i.suffix)
+ {
+ i386_operand_type temp;
+
+ operand_type_set (&temp, 0);
+ if (i.suffix == BYTE_MNEM_SUFFIX)
+ {
+ temp.bitfield.imm8 = overlap.bitfield.imm8;
+ temp.bitfield.imm8s = overlap.bitfield.imm8s;
+ }
+ else if (i.suffix == WORD_MNEM_SUFFIX)
+ temp.bitfield.imm16 = overlap.bitfield.imm16;
+ else if (i.suffix == QWORD_MNEM_SUFFIX)
+ {
+ temp.bitfield.imm64 = overlap.bitfield.imm64;
+ temp.bitfield.imm32s = overlap.bitfield.imm32s;
+ }
+ else
+ temp.bitfield.imm32 = overlap.bitfield.imm32;
+ overlap = temp;
+ }
+ else if (operand_type_equal (&overlap, &imm16_32_32s)
+ || operand_type_equal (&overlap, &imm16_32)
+ || operand_type_equal (&overlap, &imm16_32s))
+ {
+ if ((flag_code == CODE_16BIT) ^ (i.prefix[DATA_PREFIX] != 0))
+ overlap = imm16;
+ else
+ overlap = imm32s;
+ }
+ if (!operand_type_equal (&overlap, &imm8)
+ && !operand_type_equal (&overlap, &imm8s)
+ && !operand_type_equal (&overlap, &imm16)
+ && !operand_type_equal (&overlap, &imm32)
+ && !operand_type_equal (&overlap, &imm32s)
+ && !operand_type_equal (&overlap, &imm64))
+ {
+ as_bad (_("no instruction mnemonic suffix given; "
+ "can't determine immediate size"));
+ return 0;
+ }
+ }
+ i.types[j] = overlap;
+
+ return 1;
+}
+
+static int
+finalize_imm (void)
+{
+ unsigned int j, n;
+
+ /* Update the first 2 immediate operands. */
+ n = i.operands > 2 ? 2 : i.operands;
+ if (n)
+ {
+ for (j = 0; j < n; j++)
+ if (update_imm (j) == 0)
+ return 0;
+
+ /* The 3rd operand can't be immediate operand. */
+ gas_assert (operand_type_check (i.types[2], imm) == 0);
+ }
+
+ return 1;
+}
+
+static int
+bad_implicit_operand (int xmm)
+{
+ const char *ireg = xmm ? "xmm0" : "ymm0";
+
+ if (intel_syntax)
+ as_bad (_("the last operand of `%s' must be `%s%s'"),
+ i.tm.name, register_prefix, ireg);
+ else
+ as_bad (_("the first operand of `%s' must be `%s%s'"),
+ i.tm.name, register_prefix, ireg);
+ return 0;
+}
+
+static int
+process_operands (void)
+{
+ /* Default segment register this instruction will use for memory
+ accesses. 0 means unknown. This is only for optimizing out
+ unnecessary segment overrides. */
+ const seg_entry *default_seg = 0;
+
+ if (i.tm.opcode_modifier.sse2avx && i.tm.opcode_modifier.vexvvvv)
+ {
+ unsigned int dupl = i.operands;
+ unsigned int dest = dupl - 1;
+ unsigned int j;
+
+ /* The destination must be an xmm register. */
+ gas_assert (i.reg_operands
+ && MAX_OPERANDS > dupl
+ && operand_type_equal (&i.types[dest], &regxmm));
+
+ if (i.tm.opcode_modifier.firstxmm0)
+ {
+ /* The first operand is implicit and must be xmm0. */
+ gas_assert (operand_type_equal (&i.types[0], &regxmm));
+ if (register_number (i.op[0].regs) != 0)
+ return bad_implicit_operand (1);
+
+ if (i.tm.opcode_modifier.vexsources == VEX3SOURCES)
+ {
+ /* Keep xmm0 for instructions with VEX prefix and 3
+ sources. */
+ goto duplicate;
+ }
+ else
+ {
+ /* We remove the first xmm0 and keep the number of
+ operands unchanged, which in fact duplicates the
+ destination. */
+ for (j = 1; j < i.operands; j++)
+ {
+ i.op[j - 1] = i.op[j];
+ i.types[j - 1] = i.types[j];
+ i.tm.operand_types[j - 1] = i.tm.operand_types[j];
+ }
+ }
+ }
+ else if (i.tm.opcode_modifier.implicit1stxmm0)
+ {
+ gas_assert ((MAX_OPERANDS - 1) > dupl
+ && (i.tm.opcode_modifier.vexsources
+ == VEX3SOURCES));
+
+ /* Add the implicit xmm0 for instructions with VEX prefix
+ and 3 sources. */
+ for (j = i.operands; j > 0; j--)
+ {
+ i.op[j] = i.op[j - 1];
+ i.types[j] = i.types[j - 1];
+ i.tm.operand_types[j] = i.tm.operand_types[j - 1];
+ }
+ i.op[0].regs
+ = (const reg_entry *) hash_find (reg_hash, "xmm0");
+ i.types[0] = regxmm;
+ i.tm.operand_types[0] = regxmm;
+
+ i.operands += 2;
+ i.reg_operands += 2;
+ i.tm.operands += 2;
+
+ dupl++;
+ dest++;
+ i.op[dupl] = i.op[dest];
+ i.types[dupl] = i.types[dest];
+ i.tm.operand_types[dupl] = i.tm.operand_types[dest];
+ }
+ else
+ {
+duplicate:
+ i.operands++;
+ i.reg_operands++;
+ i.tm.operands++;
+
+ i.op[dupl] = i.op[dest];
+ i.types[dupl] = i.types[dest];
+ i.tm.operand_types[dupl] = i.tm.operand_types[dest];
+ }
+
+ if (i.tm.opcode_modifier.immext)
+ process_immext ();
+ }
+ else if (i.tm.opcode_modifier.firstxmm0)
+ {
+ unsigned int j;
+
+ /* The first operand is implicit and must be xmm0/ymm0/zmm0. */
+ gas_assert (i.reg_operands
+ && (operand_type_equal (&i.types[0], &regxmm)
+ || operand_type_equal (&i.types[0], &regymm)
+ || operand_type_equal (&i.types[0], &regzmm)));
+ if (register_number (i.op[0].regs) != 0)
+ return bad_implicit_operand (i.types[0].bitfield.regxmm);
+
+ for (j = 1; j < i.operands; j++)
+ {
+ i.op[j - 1] = i.op[j];
+ i.types[j - 1] = i.types[j];
+
+ /* We need to adjust fields in i.tm since they are used by
+ build_modrm_byte. */
+ i.tm.operand_types [j - 1] = i.tm.operand_types [j];
+ }
+
+ i.operands--;
+ i.reg_operands--;
+ i.tm.operands--;
+ }
+ else if (i.tm.opcode_modifier.regkludge)
+ {
+ /* The imul $imm, %reg instruction is converted into
+ imul $imm, %reg, %reg, and the clr %reg instruction
+ is converted into xor %reg, %reg. */
+
+ unsigned int first_reg_op;
+
+ if (operand_type_check (i.types[0], reg))
+ first_reg_op = 0;
+ else
+ first_reg_op = 1;
+ /* Pretend we saw the extra register operand. */
+ gas_assert (i.reg_operands == 1
+ && i.op[first_reg_op + 1].regs == 0);
+ i.op[first_reg_op + 1].regs = i.op[first_reg_op].regs;
+ i.types[first_reg_op + 1] = i.types[first_reg_op];
+ i.operands++;
+ i.reg_operands++;
+ }
+
+ if (i.tm.opcode_modifier.shortform)
+ {
+ if (i.types[0].bitfield.sreg2
+ || i.types[0].bitfield.sreg3)
+ {
+ if (i.tm.base_opcode == POP_SEG_SHORT
+ && i.op[0].regs->reg_num == 1)
+ {
+ as_bad (_("you can't `pop %scs'"), register_prefix);
+ return 0;
+ }
+ i.tm.base_opcode |= (i.op[0].regs->reg_num << 3);
+ if ((i.op[0].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ }
+ else
+ {
+ /* The register or float register operand is in operand
+ 0 or 1. */
+ unsigned int op;
+
+ if (i.types[0].bitfield.floatreg
+ || operand_type_check (i.types[0], reg))
+ op = 0;
+ else
+ op = 1;
+ /* Register goes in low 3 bits of opcode. */
+ i.tm.base_opcode |= i.op[op].regs->reg_num;
+ if ((i.op[op].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ if (!quiet_warnings && i.tm.opcode_modifier.ugh)
+ {
+ /* Warn about some common errors, but press on regardless.
+ The first case can be generated by gcc (<= 2.8.1). */
+ if (i.operands == 2)
+ {
+ /* Reversed arguments on faddp, fsubp, etc. */
+ as_warn (_("translating to `%s %s%s,%s%s'"), i.tm.name,
+ register_prefix, i.op[!intel_syntax].regs->reg_name,
+ register_prefix, i.op[intel_syntax].regs->reg_name);
+ }
+ else
+ {
+ /* Extraneous `l' suffix on fp insn. */
+ as_warn (_("translating to `%s %s%s'"), i.tm.name,
+ register_prefix, i.op[0].regs->reg_name);
+ }
+ }
+ }
+ }
+ else if (i.tm.opcode_modifier.modrm)
+ {
+ /* The opcode is completed (modulo i.tm.extension_opcode which
+ must be put into the modrm byte). Now, we make the modrm and
+ index base bytes based on all the info we've collected. */
+
+ default_seg = build_modrm_byte ();
+ }
+ else if ((i.tm.base_opcode & ~0x3) == MOV_AX_DISP32)
+ {
+ default_seg = &ds;
+ }
+ else if (i.tm.opcode_modifier.isstring)
+ {
+ /* For the string instructions that allow a segment override
+ on one of their operands, the default segment is ds. */
+ default_seg = &ds;
+ }
+
+ if (i.tm.base_opcode == 0x8d /* lea */
+ && i.seg[0]
+ && !quiet_warnings)
+ as_warn (_("segment override on `%s' is ineffectual"), i.tm.name);
+
+ /* If a segment was explicitly specified, and the specified segment
+ is not the default, use an opcode prefix to select it. If we
+ never figured out what the default segment is, then default_seg
+ will be zero at this point, and the specified segment prefix will
+ always be used. */
+ if ((i.seg[0]) && (i.seg[0] != default_seg))
+ {
+ if (!add_prefix (i.seg[0]->seg_prefix))
+ return 0;
+ }
+ return 1;
+}
+
+static const seg_entry *
+build_modrm_byte (void)
+{
+ const seg_entry *default_seg = 0;
+ unsigned int source, dest;
+ int vex_3_sources;
+
+ /* The first operand of instructions with VEX prefix and 3 sources
+ must be VEX_Imm4. */
+ vex_3_sources = i.tm.opcode_modifier.vexsources == VEX3SOURCES;
+ if (vex_3_sources)
+ {
+ unsigned int nds, reg_slot;
+ expressionS *exp;
+
+ if (i.tm.opcode_modifier.veximmext
+ && i.tm.opcode_modifier.immext)
+ {
+ dest = i.operands - 2;
+ gas_assert (dest == 3);
+ }
+ else
+ dest = i.operands - 1;
+ nds = dest - 1;
+
+ /* There are 2 kinds of instructions:
+ 1. 5 operands: 4 register operands or 3 register operands
+ plus 1 memory operand plus one Vec_Imm4 operand, VexXDS, and
+ VexW0 or VexW1. The destination must be either XMM, YMM or
+ ZMM register.
+ 2. 4 operands: 4 register operands or 3 register operands
+ plus 1 memory operand, VexXDS, and VexImmExt */
+ gas_assert ((i.reg_operands == 4
+ || (i.reg_operands == 3 && i.mem_operands == 1))
+ && i.tm.opcode_modifier.vexvvvv == VEXXDS
+ && (i.tm.opcode_modifier.veximmext
+ || (i.imm_operands == 1
+ && i.types[0].bitfield.vec_imm4
+ && (i.tm.opcode_modifier.vexw == VEXW0
+ || i.tm.opcode_modifier.vexw == VEXW1)
+ && (operand_type_equal (&i.tm.operand_types[dest], &regxmm)
+ || operand_type_equal (&i.tm.operand_types[dest], &regymm)
+ || operand_type_equal (&i.tm.operand_types[dest], &regzmm)))));
+
+ if (i.imm_operands == 0)
+ {
+ /* When there is no immediate operand, generate an 8bit
+ immediate operand to encode the first operand. */
+ exp = &im_expressions[i.imm_operands++];
+ i.op[i.operands].imms = exp;
+ i.types[i.operands] = imm8;
+ i.operands++;
+ /* If VexW1 is set, the first operand is the source and
+ the second operand is encoded in the immediate operand. */
+ if (i.tm.opcode_modifier.vexw == VEXW1)
+ {
+ source = 0;
+ reg_slot = 1;
+ }
+ else
+ {
+ source = 1;
+ reg_slot = 0;
+ }
+
+ /* FMA swaps REG and NDS. */
+ if (i.tm.cpu_flags.bitfield.cpufma)
+ {
+ unsigned int tmp;
+ tmp = reg_slot;
+ reg_slot = nds;
+ nds = tmp;
+ }
+
+ gas_assert (operand_type_equal (&i.tm.operand_types[reg_slot],
+ &regxmm)
+ || operand_type_equal (&i.tm.operand_types[reg_slot],
+ &regymm)
+ || operand_type_equal (&i.tm.operand_types[reg_slot],
+ &regzmm));
+ exp->X_op = O_constant;
+ exp->X_add_number = register_number (i.op[reg_slot].regs) << 4;
+ gas_assert ((i.op[reg_slot].regs->reg_flags & RegVRex) == 0);
+ }
+ else
+ {
+ unsigned int imm_slot;
+
+ if (i.tm.opcode_modifier.vexw == VEXW0)
+ {
+ /* If VexW0 is set, the third operand is the source and
+ the second operand is encoded in the immediate
+ operand. */
+ source = 2;
+ reg_slot = 1;
+ }
+ else
+ {
+ /* VexW1 is set, the second operand is the source and
+ the third operand is encoded in the immediate
+ operand. */
+ source = 1;
+ reg_slot = 2;
+ }
+
+ if (i.tm.opcode_modifier.immext)
+ {
+ /* When ImmExt is set, the immdiate byte is the last
+ operand. */
+ imm_slot = i.operands - 1;
+ source--;
+ reg_slot--;
+ }
+ else
+ {
+ imm_slot = 0;
+
+ /* Turn on Imm8 so that output_imm will generate it. */
+ i.types[imm_slot].bitfield.imm8 = 1;
+ }
+
+ gas_assert (operand_type_equal (&i.tm.operand_types[reg_slot],
+ &regxmm)
+ || operand_type_equal (&i.tm.operand_types[reg_slot],
+ &regymm)
+ || operand_type_equal (&i.tm.operand_types[reg_slot],
+ &regzmm));
+ i.op[imm_slot].imms->X_add_number
+ |= register_number (i.op[reg_slot].regs) << 4;
+ gas_assert ((i.op[reg_slot].regs->reg_flags & RegVRex) == 0);
+ }
+
+ gas_assert (operand_type_equal (&i.tm.operand_types[nds], &regxmm)
+ || operand_type_equal (&i.tm.operand_types[nds],
+ &regymm)
+ || operand_type_equal (&i.tm.operand_types[nds],
+ &regzmm));
+ i.vex.register_specifier = i.op[nds].regs;
+ }
+ else
+ source = dest = 0;
+
+ /* i.reg_operands MUST be the number of real register operands;
+ implicit registers do not count. If there are 3 register
+ operands, it must be a instruction with VexNDS. For a
+ instruction with VexNDD, the destination register is encoded
+ in VEX prefix. If there are 4 register operands, it must be
+ a instruction with VEX prefix and 3 sources. */
+ if (i.mem_operands == 0
+ && ((i.reg_operands == 2
+ && i.tm.opcode_modifier.vexvvvv <= VEXXDS)
+ || (i.reg_operands == 3
+ && i.tm.opcode_modifier.vexvvvv == VEXXDS)
+ || (i.reg_operands == 4 && vex_3_sources)))
+ {
+ switch (i.operands)
+ {
+ case 2:
+ source = 0;
+ break;
+ case 3:
+ /* When there are 3 operands, one of them may be immediate,
+ which may be the first or the last operand. Otherwise,
+ the first operand must be shift count register (cl) or it
+ is an instruction with VexNDS. */
+ gas_assert (i.imm_operands == 1
+ || (i.imm_operands == 0
+ && (i.tm.opcode_modifier.vexvvvv == VEXXDS
+ || i.types[0].bitfield.shiftcount)));
+ if (operand_type_check (i.types[0], imm)
+ || i.types[0].bitfield.shiftcount)
+ source = 1;
+ else
+ source = 0;
+ break;
+ case 4:
+ /* When there are 4 operands, the first two must be 8bit
+ immediate operands. The source operand will be the 3rd
+ one.
+
+ For instructions with VexNDS, if the first operand
+ an imm8, the source operand is the 2nd one. If the last
+ operand is imm8, the source operand is the first one. */
+ gas_assert ((i.imm_operands == 2
+ && i.types[0].bitfield.imm8
+ && i.types[1].bitfield.imm8)
+ || (i.tm.opcode_modifier.vexvvvv == VEXXDS
+ && i.imm_operands == 1
+ && (i.types[0].bitfield.imm8
+ || i.types[i.operands - 1].bitfield.imm8
+ || i.rounding)));
+ if (i.imm_operands == 2)
+ source = 2;
+ else
+ {
+ if (i.types[0].bitfield.imm8)
+ source = 1;
+ else
+ source = 0;
+ }
+ break;
+ case 5:
+ if (i.tm.opcode_modifier.evex)
+ {
+ /* For EVEX instructions, when there are 5 operands, the
+ first one must be immediate operand. If the second one
+ is immediate operand, the source operand is the 3th
+ one. If the last one is immediate operand, the source
+ operand is the 2nd one. */
+ gas_assert (i.imm_operands == 2
+ && i.tm.opcode_modifier.sae
+ && operand_type_check (i.types[0], imm));
+ if (operand_type_check (i.types[1], imm))
+ source = 2;
+ else if (operand_type_check (i.types[4], imm))
+ source = 1;
+ else
+ abort ();
+ }
+ break;
+ default:
+ abort ();
+ }
+
+ if (!vex_3_sources)
+ {
+ dest = source + 1;
+
+ /* RC/SAE operand could be between DEST and SRC. That happens
+ when one operand is GPR and the other one is XMM/YMM/ZMM
+ register. */
+ if (i.rounding && i.rounding->operand == (int) dest)
+ dest++;
+
+ if (i.tm.opcode_modifier.vexvvvv == VEXXDS)
+ {
+ /* For instructions with VexNDS, the register-only source
+ operand must be 32/64bit integer, XMM, YMM or ZMM
+ register. It is encoded in VEX prefix. We need to
+ clear RegMem bit before calling operand_type_equal. */
+
+ i386_operand_type op;
+ unsigned int vvvv;
+
+ /* Check register-only source operand when two source
+ operands are swapped. */
+ if (!i.tm.operand_types[source].bitfield.baseindex
+ && i.tm.operand_types[dest].bitfield.baseindex)
+ {
+ vvvv = source;
+ source = dest;
+ }
+ else
+ vvvv = dest;
+
+ op = i.tm.operand_types[vvvv];
+ op.bitfield.regmem = 0;
+ if ((dest + 1) >= i.operands
+ || (!op.bitfield.reg32
+ && op.bitfield.reg64
+ && !operand_type_equal (&op, &regxmm)
+ && !operand_type_equal (&op, &regymm)
+ && !operand_type_equal (&op, &regzmm)
+ && !operand_type_equal (&op, &regmask)))
+ abort ();
+ i.vex.register_specifier = i.op[vvvv].regs;
+ dest++;
+ }
+ }
+
+ i.rm.mode = 3;
+ /* One of the register operands will be encoded in the i.tm.reg
+ field, the other in the combined i.tm.mode and i.tm.regmem
+ fields. If no form of this instruction supports a memory
+ destination operand, then we assume the source operand may
+ sometimes be a memory operand and so we need to store the
+ destination in the i.rm.reg field. */
+ if (!i.tm.operand_types[dest].bitfield.regmem
+ && operand_type_check (i.tm.operand_types[dest], anymem) == 0)
+ {
+ i.rm.reg = i.op[dest].regs->reg_num;
+ i.rm.regmem = i.op[source].regs->reg_num;
+ if ((i.op[dest].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_R;
+ if ((i.op[dest].regs->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_R;
+ if ((i.op[source].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ if ((i.op[source].regs->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_B;
+ }
+ else
+ {
+ i.rm.reg = i.op[source].regs->reg_num;
+ i.rm.regmem = i.op[dest].regs->reg_num;
+ if ((i.op[dest].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ if ((i.op[dest].regs->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_B;
+ if ((i.op[source].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_R;
+ if ((i.op[source].regs->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_R;
+ }
+ if (flag_code != CODE_64BIT && (i.rex & (REX_R | REX_B)))
+ {
+ if (!i.types[0].bitfield.control
+ && !i.types[1].bitfield.control)
+ abort ();
+ i.rex &= ~(REX_R | REX_B);
+ add_prefix (LOCK_PREFIX_OPCODE);
+ }
+ }
+ else
+ { /* If it's not 2 reg operands... */
+ unsigned int mem;
+
+ if (i.mem_operands)
+ {
+ unsigned int fake_zero_displacement = 0;
+ unsigned int op;
+
+ for (op = 0; op < i.operands; op++)
+ if (operand_type_check (i.types[op], anymem))
+ break;
+ gas_assert (op < i.operands);
+
+ if (i.tm.opcode_modifier.vecsib)
+ {
+ if (i.index_reg->reg_num == RegEiz
+ || i.index_reg->reg_num == RegRiz)
+ abort ();
+
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ if (!i.base_reg)
+ {
+ i.sib.base = NO_BASE_REGISTER;
+ i.sib.scale = i.log2_scale_factor;
+ /* No Vec_Disp8 if there is no base. */
+ i.types[op].bitfield.vec_disp8 = 0;
+ i.types[op].bitfield.disp8 = 0;
+ i.types[op].bitfield.disp16 = 0;
+ i.types[op].bitfield.disp64 = 0;
+ if (flag_code != CODE_64BIT)
+ {
+ /* Must be 32 bit */
+ i.types[op].bitfield.disp32 = 1;
+ i.types[op].bitfield.disp32s = 0;
+ }
+ else
+ {
+ i.types[op].bitfield.disp32 = 0;
+ i.types[op].bitfield.disp32s = 1;
+ }
+ }
+ i.sib.index = i.index_reg->reg_num;
+ if ((i.index_reg->reg_flags & RegRex) != 0)
+ i.rex |= REX_X;
+ if ((i.index_reg->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_X;
+ }
+
+ default_seg = &ds;
+
+ if (i.base_reg == 0)
+ {
+ i.rm.mode = 0;
+ if (!i.disp_operands)
+ {
+ fake_zero_displacement = 1;
+ /* Instructions with VSIB byte need 32bit displacement
+ if there is no base register. */
+ if (i.tm.opcode_modifier.vecsib)
+ i.types[op].bitfield.disp32 = 1;
+ }
+ if (i.index_reg == 0)
+ {
+ gas_assert (!i.tm.opcode_modifier.vecsib);
+ /* Operand is just <disp> */
+ if (flag_code == CODE_64BIT)
+ {
+ /* 64bit mode overwrites the 32bit absolute
+ addressing by RIP relative addressing and
+ absolute addressing is encoded by one of the
+ redundant SIB forms. */
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ i.sib.base = NO_BASE_REGISTER;
+ i.sib.index = NO_INDEX_REGISTER;
+ i.types[op] = ((i.prefix[ADDR_PREFIX] == 0)
+ ? disp32s : disp32);
+ }
+ else if ((flag_code == CODE_16BIT)
+ ^ (i.prefix[ADDR_PREFIX] != 0))
+ {
+ i.rm.regmem = NO_BASE_REGISTER_16;
+ i.types[op] = disp16;
+ }
+ else
+ {
+ i.rm.regmem = NO_BASE_REGISTER;
+ i.types[op] = disp32;
+ }
+ }
+ else if (!i.tm.opcode_modifier.vecsib)
+ {
+ /* !i.base_reg && i.index_reg */
+ if (i.index_reg->reg_num == RegEiz
+ || i.index_reg->reg_num == RegRiz)
+ i.sib.index = NO_INDEX_REGISTER;
+ else
+ i.sib.index = i.index_reg->reg_num;
+ i.sib.base = NO_BASE_REGISTER;
+ i.sib.scale = i.log2_scale_factor;
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ /* No Vec_Disp8 if there is no base. */
+ i.types[op].bitfield.vec_disp8 = 0;
+ i.types[op].bitfield.disp8 = 0;
+ i.types[op].bitfield.disp16 = 0;
+ i.types[op].bitfield.disp64 = 0;
+ if (flag_code != CODE_64BIT)
+ {
+ /* Must be 32 bit */
+ i.types[op].bitfield.disp32 = 1;
+ i.types[op].bitfield.disp32s = 0;
+ }
+ else
+ {
+ i.types[op].bitfield.disp32 = 0;
+ i.types[op].bitfield.disp32s = 1;
+ }
+ if ((i.index_reg->reg_flags & RegRex) != 0)
+ i.rex |= REX_X;
+ }
+ }
+ /* RIP addressing for 64bit mode. */
+ else if (i.base_reg->reg_num == RegRip ||
+ i.base_reg->reg_num == RegEip)
+ {
+ gas_assert (!i.tm.opcode_modifier.vecsib);
+ i.rm.regmem = NO_BASE_REGISTER;
+ i.types[op].bitfield.disp8 = 0;
+ i.types[op].bitfield.disp16 = 0;
+ i.types[op].bitfield.disp32 = 0;
+ i.types[op].bitfield.disp32s = 1;
+ i.types[op].bitfield.disp64 = 0;
+ i.types[op].bitfield.vec_disp8 = 0;
+ i.flags[op] |= Operand_PCrel;
+ if (! i.disp_operands)
+ fake_zero_displacement = 1;
+ }
+ else if (i.base_reg->reg_type.bitfield.reg16)
+ {
+ gas_assert (!i.tm.opcode_modifier.vecsib);
+ switch (i.base_reg->reg_num)
+ {
+ case 3: /* (%bx) */
+ if (i.index_reg == 0)
+ i.rm.regmem = 7;
+ else /* (%bx,%si) -> 0, or (%bx,%di) -> 1 */
+ i.rm.regmem = i.index_reg->reg_num - 6;
+ break;
+ case 5: /* (%bp) */
+ default_seg = &ss;
+ if (i.index_reg == 0)
+ {
+ i.rm.regmem = 6;
+ if (operand_type_check (i.types[op], disp) == 0)
+ {
+ /* fake (%bp) into 0(%bp) */
+ if (i.tm.operand_types[op].bitfield.vec_disp8)
+ i.types[op].bitfield.vec_disp8 = 1;
+ else
+ i.types[op].bitfield.disp8 = 1;
+ fake_zero_displacement = 1;
+ }
+ }
+ else /* (%bp,%si) -> 2, or (%bp,%di) -> 3 */
+ i.rm.regmem = i.index_reg->reg_num - 6 + 2;
+ break;
+ default: /* (%si) -> 4 or (%di) -> 5 */
+ i.rm.regmem = i.base_reg->reg_num - 6 + 4;
+ }
+ i.rm.mode = mode_from_disp_size (i.types[op]);
+ }
+ else /* i.base_reg and 32/64 bit mode */
+ {
+ if (flag_code == CODE_64BIT
+ && operand_type_check (i.types[op], disp))
+ {
+ i386_operand_type temp;
+ operand_type_set (&temp, 0);
+ temp.bitfield.disp8 = i.types[op].bitfield.disp8;
+ temp.bitfield.vec_disp8
+ = i.types[op].bitfield.vec_disp8;
+ i.types[op] = temp;
+ if (i.prefix[ADDR_PREFIX] == 0)
+ i.types[op].bitfield.disp32s = 1;
+ else
+ i.types[op].bitfield.disp32 = 1;
+ }
+
+ if (!i.tm.opcode_modifier.vecsib)
+ i.rm.regmem = i.base_reg->reg_num;
+ if ((i.base_reg->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ i.sib.base = i.base_reg->reg_num;
+ /* x86-64 ignores REX prefix bit here to avoid decoder
+ complications. */
+ if (!(i.base_reg->reg_flags & RegRex)
+ && (i.base_reg->reg_num == EBP_REG_NUM
+ || i.base_reg->reg_num == ESP_REG_NUM))
+ default_seg = &ss;
+ if (i.base_reg->reg_num == 5 && i.disp_operands == 0)
+ {
+ fake_zero_displacement = 1;
+ if (i.tm.operand_types [op].bitfield.vec_disp8)
+ i.types[op].bitfield.vec_disp8 = 1;
+ else
+ i.types[op].bitfield.disp8 = 1;
+ }
+ i.sib.scale = i.log2_scale_factor;
+ if (i.index_reg == 0)
+ {
+ gas_assert (!i.tm.opcode_modifier.vecsib);
+ /* <disp>(%esp) becomes two byte modrm with no index
+ register. We've already stored the code for esp
+ in i.rm.regmem ie. ESCAPE_TO_TWO_BYTE_ADDRESSING.
+ Any base register besides %esp will not use the
+ extra modrm byte. */
+ i.sib.index = NO_INDEX_REGISTER;
+ }
+ else if (!i.tm.opcode_modifier.vecsib)
+ {
+ if (i.index_reg->reg_num == RegEiz
+ || i.index_reg->reg_num == RegRiz)
+ i.sib.index = NO_INDEX_REGISTER;
+ else
+ i.sib.index = i.index_reg->reg_num;
+ i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING;
+ if ((i.index_reg->reg_flags & RegRex) != 0)
+ i.rex |= REX_X;
+ }
+
+ if (i.disp_operands
+ && (i.reloc[op] == BFD_RELOC_386_TLS_DESC_CALL
+ || i.reloc[op] == BFD_RELOC_X86_64_TLSDESC_CALL))
+ i.rm.mode = 0;
+ else
+ {
+ if (!fake_zero_displacement
+ && !i.disp_operands
+ && i.disp_encoding)
+ {
+ fake_zero_displacement = 1;
+ if (i.disp_encoding == disp_encoding_8bit)
+ i.types[op].bitfield.disp8 = 1;
+ else
+ i.types[op].bitfield.disp32 = 1;
+ }
+ i.rm.mode = mode_from_disp_size (i.types[op]);
+ }
+ }
+
+ if (fake_zero_displacement)
+ {
+ /* Fakes a zero displacement assuming that i.types[op]
+ holds the correct displacement size. */
+ expressionS *exp;
+
+ gas_assert (i.op[op].disps == 0);
+ exp = &disp_expressions[i.disp_operands++];
+ i.op[op].disps = exp;
+ exp->X_op = O_constant;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_op_symbol = (symbolS *) 0;
+ }
+
+ mem = op;
+ }
+ else
+ mem = ~0;
+
+ if (i.tm.opcode_modifier.vexsources == XOP2SOURCES)
+ {
+ if (operand_type_check (i.types[0], imm))
+ i.vex.register_specifier = NULL;
+ else
+ {
+ /* VEX.vvvv encodes one of the sources when the first
+ operand is not an immediate. */
+ if (i.tm.opcode_modifier.vexw == VEXW0)
+ i.vex.register_specifier = i.op[0].regs;
+ else
+ i.vex.register_specifier = i.op[1].regs;
+ }
+
+ /* Destination is a XMM register encoded in the ModRM.reg
+ and VEX.R bit. */
+ i.rm.reg = i.op[2].regs->reg_num;
+ if ((i.op[2].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_R;
+
+ /* ModRM.rm and VEX.B encodes the other source. */
+ if (!i.mem_operands)
+ {
+ i.rm.mode = 3;
+
+ if (i.tm.opcode_modifier.vexw == VEXW0)
+ i.rm.regmem = i.op[1].regs->reg_num;
+ else
+ i.rm.regmem = i.op[0].regs->reg_num;
+
+ if ((i.op[1].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ }
+ }
+ else if (i.tm.opcode_modifier.vexvvvv == VEXLWP)
+ {
+ i.vex.register_specifier = i.op[2].regs;
+ if (!i.mem_operands)
+ {
+ i.rm.mode = 3;
+ i.rm.regmem = i.op[1].regs->reg_num;
+ if ((i.op[1].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ }
+ }
+ /* Fill in i.rm.reg or i.rm.regmem field with register operand
+ (if any) based on i.tm.extension_opcode. Again, we must be
+ careful to make sure that segment/control/debug/test/MMX
+ registers are coded into the i.rm.reg field. */
+ else if (i.reg_operands)
+ {
+ unsigned int op;
+ unsigned int vex_reg = ~0;
+
+ for (op = 0; op < i.operands; op++)
+ if (i.types[op].bitfield.reg8
+ || i.types[op].bitfield.reg16
+ || i.types[op].bitfield.reg32
+ || i.types[op].bitfield.reg64
+ || i.types[op].bitfield.regmmx
+ || i.types[op].bitfield.regxmm
+ || i.types[op].bitfield.regymm
+ || i.types[op].bitfield.regbnd
+ || i.types[op].bitfield.regzmm
+ || i.types[op].bitfield.regmask
+ || i.types[op].bitfield.sreg2
+ || i.types[op].bitfield.sreg3
+ || i.types[op].bitfield.control
+ || i.types[op].bitfield.debug
+ || i.types[op].bitfield.test)
+ break;
+
+ if (vex_3_sources)
+ op = dest;
+ else if (i.tm.opcode_modifier.vexvvvv == VEXXDS)
+ {
+ /* For instructions with VexNDS, the register-only
+ source operand is encoded in VEX prefix. */
+ gas_assert (mem != (unsigned int) ~0);
+
+ if (op > mem)
+ {
+ vex_reg = op++;
+ gas_assert (op < i.operands);
+ }
+ else
+ {
+ /* Check register-only source operand when two source
+ operands are swapped. */
+ if (!i.tm.operand_types[op].bitfield.baseindex
+ && i.tm.operand_types[op + 1].bitfield.baseindex)
+ {
+ vex_reg = op;
+ op += 2;
+ gas_assert (mem == (vex_reg + 1)
+ && op < i.operands);
+ }
+ else
+ {
+ vex_reg = op + 1;
+ gas_assert (vex_reg < i.operands);
+ }
+ }
+ }
+ else if (i.tm.opcode_modifier.vexvvvv == VEXNDD)
+ {
+ /* For instructions with VexNDD, the register destination
+ is encoded in VEX prefix. */
+ if (i.mem_operands == 0)
+ {
+ /* There is no memory operand. */
+ gas_assert ((op + 2) == i.operands);
+ vex_reg = op + 1;
+ }
+ else
+ {
+ /* There are only 2 operands. */
+ gas_assert (op < 2 && i.operands == 2);
+ vex_reg = 1;
+ }
+ }
+ else
+ gas_assert (op < i.operands);
+
+ if (vex_reg != (unsigned int) ~0)
+ {
+ i386_operand_type *type = &i.tm.operand_types[vex_reg];
+
+ if (type->bitfield.reg32 != 1
+ && type->bitfield.reg64 != 1
+ && !operand_type_equal (type, &regxmm)
+ && !operand_type_equal (type, &regymm)
+ && !operand_type_equal (type, &regzmm)
+ && !operand_type_equal (type, &regmask))
+ abort ();
+
+ i.vex.register_specifier = i.op[vex_reg].regs;
+ }
+
+ /* Don't set OP operand twice. */
+ if (vex_reg != op)
+ {
+ /* If there is an extension opcode to put here, the
+ register number must be put into the regmem field. */
+ if (i.tm.extension_opcode != None)
+ {
+ i.rm.regmem = i.op[op].regs->reg_num;
+ if ((i.op[op].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_B;
+ if ((i.op[op].regs->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_B;
+ }
+ else
+ {
+ i.rm.reg = i.op[op].regs->reg_num;
+ if ((i.op[op].regs->reg_flags & RegRex) != 0)
+ i.rex |= REX_R;
+ if ((i.op[op].regs->reg_flags & RegVRex) != 0)
+ i.vrex |= REX_R;
+ }
+ }
+
+ /* Now, if no memory operand has set i.rm.mode = 0, 1, 2 we
+ must set it to 3 to indicate this is a register operand
+ in the regmem field. */
+ if (!i.mem_operands)
+ i.rm.mode = 3;
+ }
+
+ /* Fill in i.rm.reg field with extension opcode (if any). */
+ if (i.tm.extension_opcode != None)
+ i.rm.reg = i.tm.extension_opcode;
+ }
+ return default_seg;
+}
+
+static void
+output_branch (void)
+{
+ char *p;
+ int size;
+ int code16;
+ int prefix;
+ relax_substateT subtype;
+ symbolS *sym;
+ offsetT off;
+
+ code16 = flag_code == CODE_16BIT ? CODE16 : 0;
+ size = i.disp_encoding == disp_encoding_32bit ? BIG : SMALL;
+
+ prefix = 0;
+ if (i.prefix[DATA_PREFIX] != 0)
+ {
+ prefix = 1;
+ i.prefixes -= 1;
+ code16 ^= CODE16;
+ }
+ /* Pentium4 branch hints. */
+ if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE /* not taken */
+ || i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE /* taken */)
+ {
+ prefix++;
+ i.prefixes--;
+ }
+ if (i.prefix[REX_PREFIX] != 0)
+ {
+ prefix++;
+ i.prefixes--;
+ }
+
+ /* BND prefixed jump. */
+ if (i.prefix[BND_PREFIX] != 0)
+ {
+ FRAG_APPEND_1_CHAR (i.prefix[BND_PREFIX]);
+ i.prefixes -= 1;
+ }
+
+ if (i.prefixes != 0 && !intel_syntax)
+ as_warn (_("skipping prefixes on this instruction"));
+
+ /* It's always a symbol; End frag & setup for relax.
+ Make sure there is enough room in this frag for the largest
+ instruction we may generate in md_convert_frag. This is 2
+ bytes for the opcode and room for the prefix and largest
+ displacement. */
+ frag_grow (prefix + 2 + 4);
+ /* Prefix and 1 opcode byte go in fr_fix. */
+ p = frag_more (prefix + 1);
+ if (i.prefix[DATA_PREFIX] != 0)
+ *p++ = DATA_PREFIX_OPCODE;
+ if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE
+ || i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE)
+ *p++ = i.prefix[SEG_PREFIX];
+ if (i.prefix[REX_PREFIX] != 0)
+ *p++ = i.prefix[REX_PREFIX];
+ *p = i.tm.base_opcode;
+
+ if ((unsigned char) *p == JUMP_PC_RELATIVE)
+ subtype = ENCODE_RELAX_STATE (UNCOND_JUMP, size);
+ else if (cpu_arch_flags.bitfield.cpui386)
+ subtype = ENCODE_RELAX_STATE (COND_JUMP, size);
+ else
+ subtype = ENCODE_RELAX_STATE (COND_JUMP86, size);
+ subtype |= code16;
+
+ sym = i.op[0].disps->X_add_symbol;
+ off = i.op[0].disps->X_add_number;
+
+ if (i.op[0].disps->X_op != O_constant
+ && i.op[0].disps->X_op != O_symbol)
+ {
+ /* Handle complex expressions. */
+ sym = make_expr_symbol (i.op[0].disps);
+ off = 0;
+ }
+
+ /* 1 possible extra opcode + 4 byte displacement go in var part.
+ Pass reloc in fr_var. */
+ frag_var (rs_machine_dependent, 5, i.reloc[0], subtype, sym, off, p);
+}
+
+static void
+output_jump (void)
+{
+ char *p;
+ int size;
+ fixS *fixP;
+
+ if (i.tm.opcode_modifier.jumpbyte)
+ {
+ /* This is a loop or jecxz type instruction. */
+ size = 1;
+ if (i.prefix[ADDR_PREFIX] != 0)
+ {
+ FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE);
+ i.prefixes -= 1;
+ }
+ /* Pentium4 branch hints. */
+ if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE /* not taken */
+ || i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE /* taken */)
+ {
+ FRAG_APPEND_1_CHAR (i.prefix[SEG_PREFIX]);
+ i.prefixes--;
+ }
+ }
+ else
+ {
+ int code16;
+
+ code16 = 0;
+ if (flag_code == CODE_16BIT)
+ code16 = CODE16;
+
+ if (i.prefix[DATA_PREFIX] != 0)
+ {
+ FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE);
+ i.prefixes -= 1;
+ code16 ^= CODE16;
+ }
+
+ size = 4;
+ if (code16)
+ size = 2;
+ }
+
+ if (i.prefix[REX_PREFIX] != 0)
+ {
+ FRAG_APPEND_1_CHAR (i.prefix[REX_PREFIX]);
+ i.prefixes -= 1;
+ }
+
+ /* BND prefixed jump. */
+ if (i.prefix[BND_PREFIX] != 0)
+ {
+ FRAG_APPEND_1_CHAR (i.prefix[BND_PREFIX]);
+ i.prefixes -= 1;
+ }
+
+ if (i.prefixes != 0 && !intel_syntax)
+ as_warn (_("skipping prefixes on this instruction"));
+
+ p = frag_more (i.tm.opcode_length + size);
+ switch (i.tm.opcode_length)
+ {
+ case 2:
+ *p++ = i.tm.base_opcode >> 8;
+ case 1:
+ *p++ = i.tm.base_opcode;
+ break;
+ default:
+ abort ();
+ }
+
+ fixP = fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.op[0].disps, 1, reloc (size, 1, 1, i.reloc[0]));
+
+ /* All jumps handled here are signed, but don't use a signed limit
+ check for 32 and 16 bit jumps as we want to allow wrap around at
+ 4G and 64k respectively. */
+ if (size == 1)
+ fixP->fx_signed = 1;
+}
+
+static void
+output_interseg_jump (void)
+{
+ char *p;
+ int size;
+ int prefix;
+ int code16;
+
+ code16 = 0;
+ if (flag_code == CODE_16BIT)
+ code16 = CODE16;
+
+ prefix = 0;
+ if (i.prefix[DATA_PREFIX] != 0)
+ {
+ prefix = 1;
+ i.prefixes -= 1;
+ code16 ^= CODE16;
+ }
+ if (i.prefix[REX_PREFIX] != 0)
+ {
+ prefix++;
+ i.prefixes -= 1;
+ }
+
+ size = 4;
+ if (code16)
+ size = 2;
+
+ if (i.prefixes != 0 && !intel_syntax)
+ as_warn (_("skipping prefixes on this instruction"));
+
+ /* 1 opcode; 2 segment; offset */
+ p = frag_more (prefix + 1 + 2 + size);
+
+ if (i.prefix[DATA_PREFIX] != 0)
+ *p++ = DATA_PREFIX_OPCODE;
+
+ if (i.prefix[REX_PREFIX] != 0)
+ *p++ = i.prefix[REX_PREFIX];
+
+ *p++ = i.tm.base_opcode;
+ if (i.op[1].imms->X_op == O_constant)
+ {
+ offsetT n = i.op[1].imms->X_add_number;
+
+ if (size == 2
+ && !fits_in_unsigned_word (n)
+ && !fits_in_signed_word (n))
+ {
+ as_bad (_("16-bit jump out of range"));
+ return;
+ }
+ md_number_to_chars (p, n, size);
+ }
+ else
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.op[1].imms, 0, reloc (size, 0, 0, i.reloc[1]));
+ if (i.op[0].imms->X_op != O_constant)
+ as_bad (_("can't handle non absolute segment in `%s'"),
+ i.tm.name);
+ md_number_to_chars (p + size, (valueT) i.op[0].imms->X_add_number, 2);
+}
+
+static void
+output_insn (void)
+{
+ fragS *insn_start_frag;
+ offsetT insn_start_off;
+
+ /* Tie dwarf2 debug info to the address at the start of the insn.
+ We can't do this after the insn has been output as the current
+ frag may have been closed off. eg. by frag_var. */
+ dwarf2_emit_insn (0);
+
+ insn_start_frag = frag_now;
+ insn_start_off = frag_now_fix ();
+
+ /* Output jumps. */
+ if (i.tm.opcode_modifier.jump)
+ output_branch ();
+ else if (i.tm.opcode_modifier.jumpbyte
+ || i.tm.opcode_modifier.jumpdword)
+ output_jump ();
+ else if (i.tm.opcode_modifier.jumpintersegment)
+ output_interseg_jump ();
+ else
+ {
+ /* Output normal instructions here. */
+ char *p;
+ unsigned char *q;
+ unsigned int j;
+ unsigned int prefix;
+
+ /* Some processors fail on LOCK prefix. This options makes
+ assembler ignore LOCK prefix and serves as a workaround. */
+ if (omit_lock_prefix)
+ {
+ if (i.tm.base_opcode == LOCK_PREFIX_OPCODE)
+ return;
+ i.prefix[LOCK_PREFIX] = 0;
+ }
+
+ /* Since the VEX/EVEX prefix contains the implicit prefix, we
+ don't need the explicit prefix. */
+ if (!i.tm.opcode_modifier.vex && !i.tm.opcode_modifier.evex)
+ {
+ switch (i.tm.opcode_length)
+ {
+ case 3:
+ if (i.tm.base_opcode & 0xff000000)
+ {
+ prefix = (i.tm.base_opcode >> 24) & 0xff;
+ goto check_prefix;
+ }
+ break;
+ case 2:
+ if ((i.tm.base_opcode & 0xff0000) != 0)
+ {
+ prefix = (i.tm.base_opcode >> 16) & 0xff;
+ if (i.tm.cpu_flags.bitfield.cpupadlock)
+ {
+check_prefix:
+ if (prefix != REPE_PREFIX_OPCODE
+ || (i.prefix[REP_PREFIX]
+ != REPE_PREFIX_OPCODE))
+ add_prefix (prefix);
+ }
+ else
+ add_prefix (prefix);
+ }
+ break;
+ case 1:
+ break;
+ default:
+ abort ();
+ }
+
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+ /* For x32, add a dummy REX_OPCODE prefix for mov/add with
+ R_X86_64_GOTTPOFF relocation so that linker can safely
+ perform IE->LE optimization. */
+ if (x86_elf_abi == X86_64_X32_ABI
+ && i.operands == 2
+ && i.reloc[0] == BFD_RELOC_X86_64_GOTTPOFF
+ && i.prefix[REX_PREFIX] == 0)
+ add_prefix (REX_OPCODE);
+#endif
+
+ /* The prefix bytes. */
+ for (j = ARRAY_SIZE (i.prefix), q = i.prefix; j > 0; j--, q++)
+ if (*q)
+ FRAG_APPEND_1_CHAR (*q);
+ }
+ else
+ {
+ for (j = 0, q = i.prefix; j < ARRAY_SIZE (i.prefix); j++, q++)
+ if (*q)
+ switch (j)
+ {
+ case REX_PREFIX:
+ /* REX byte is encoded in VEX prefix. */
+ break;
+ case SEG_PREFIX:
+ case ADDR_PREFIX:
+ FRAG_APPEND_1_CHAR (*q);
+ break;
+ default:
+ /* There should be no other prefixes for instructions
+ with VEX prefix. */
+ abort ();
+ }
+
+ /* For EVEX instructions i.vrex should become 0 after
+ build_evex_prefix. For VEX instructions upper 16 registers
+ aren't available, so VREX should be 0. */
+ if (i.vrex)
+ abort ();
+ /* Now the VEX prefix. */
+ p = frag_more (i.vex.length);
+ for (j = 0; j < i.vex.length; j++)
+ p[j] = i.vex.bytes[j];
+ }
+
+ /* Now the opcode; be careful about word order here! */
+ if (i.tm.opcode_length == 1)
+ {
+ FRAG_APPEND_1_CHAR (i.tm.base_opcode);
+ }
+ else
+ {
+ switch (i.tm.opcode_length)
+ {
+ case 4:
+ p = frag_more (4);
+ *p++ = (i.tm.base_opcode >> 24) & 0xff;
+ *p++ = (i.tm.base_opcode >> 16) & 0xff;
+ break;
+ case 3:
+ p = frag_more (3);
+ *p++ = (i.tm.base_opcode >> 16) & 0xff;
+ break;
+ case 2:
+ p = frag_more (2);
+ break;
+ default:
+ abort ();
+ break;
+ }
+
+ /* Put out high byte first: can't use md_number_to_chars! */
+ *p++ = (i.tm.base_opcode >> 8) & 0xff;
+ *p = i.tm.base_opcode & 0xff;
+ }
+
+ /* Now the modrm byte and sib byte (if present). */
+ if (i.tm.opcode_modifier.modrm)
+ {
+ FRAG_APPEND_1_CHAR ((i.rm.regmem << 0
+ | i.rm.reg << 3
+ | i.rm.mode << 6));
+ /* If i.rm.regmem == ESP (4)
+ && i.rm.mode != (Register mode)
+ && not 16 bit
+ ==> need second modrm byte. */
+ if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING
+ && i.rm.mode != 3
+ && !(i.base_reg && i.base_reg->reg_type.bitfield.reg16))
+ FRAG_APPEND_1_CHAR ((i.sib.base << 0
+ | i.sib.index << 3
+ | i.sib.scale << 6));
+ }
+
+ if (i.disp_operands)
+ output_disp (insn_start_frag, insn_start_off);
+
+ if (i.imm_operands)
+ output_imm (insn_start_frag, insn_start_off);
+ }
+
+#ifdef DEBUG386
+ if (flag_debug)
+ {
+ pi ("" /*line*/, &i);
+ }
+#endif /* DEBUG386 */
+}
+
+/* Return the size of the displacement operand N. */
+
+static int
+disp_size (unsigned int n)
+{
+ int size = 4;
+
+ /* Vec_Disp8 has to be 8bit. */
+ if (i.types[n].bitfield.vec_disp8)
+ size = 1;
+ else if (i.types[n].bitfield.disp64)
+ size = 8;
+ else if (i.types[n].bitfield.disp8)
+ size = 1;
+ else if (i.types[n].bitfield.disp16)
+ size = 2;
+ return size;
+}
+
+/* Return the size of the immediate operand N. */
+
+static int
+imm_size (unsigned int n)
+{
+ int size = 4;
+ if (i.types[n].bitfield.imm64)
+ size = 8;
+ else if (i.types[n].bitfield.imm8 || i.types[n].bitfield.imm8s)
+ size = 1;
+ else if (i.types[n].bitfield.imm16)
+ size = 2;
+ return size;
+}
+
+static void
+output_disp (fragS *insn_start_frag, offsetT insn_start_off)
+{
+ char *p;
+ unsigned int n;
+
+ for (n = 0; n < i.operands; n++)
+ {
+ if (i.types[n].bitfield.vec_disp8
+ || operand_type_check (i.types[n], disp))
+ {
+ if (i.op[n].disps->X_op == O_constant)
+ {
+ int size = disp_size (n);
+ offsetT val = i.op[n].disps->X_add_number;
+
+ if (i.types[n].bitfield.vec_disp8)
+ val >>= i.memshift;
+ val = offset_in_range (val, size);
+ p = frag_more (size);
+ md_number_to_chars (p, val, size);
+ }
+ else
+ {
+ enum bfd_reloc_code_real reloc_type;
+ int size = disp_size (n);
+ int sign = i.types[n].bitfield.disp32s;
+ int pcrel = (i.flags[n] & Operand_PCrel) != 0;
+
+ /* We can't have 8 bit displacement here. */
+ gas_assert (!i.types[n].bitfield.disp8);
+
+ /* The PC relative address is computed relative
+ to the instruction boundary, so in case immediate
+ fields follows, we need to adjust the value. */
+ if (pcrel && i.imm_operands)
+ {
+ unsigned int n1;
+ int sz = 0;
+
+ for (n1 = 0; n1 < i.operands; n1++)
+ if (operand_type_check (i.types[n1], imm))
+ {
+ /* Only one immediate is allowed for PC
+ relative address. */
+ gas_assert (sz == 0);
+ sz = imm_size (n1);
+ i.op[n].disps->X_add_number -= sz;
+ }
+ /* We should find the immediate. */
+ gas_assert (sz != 0);
+ }
+
+ p = frag_more (size);
+ reloc_type = reloc (size, pcrel, sign, i.reloc[n]);
+ if (GOT_symbol
+ && GOT_symbol == i.op[n].disps->X_add_symbol
+ && (((reloc_type == BFD_RELOC_32
+ || reloc_type == BFD_RELOC_X86_64_32S
+ || (reloc_type == BFD_RELOC_64
+ && object_64bit))
+ && (i.op[n].disps->X_op == O_symbol
+ || (i.op[n].disps->X_op == O_add
+ && ((symbol_get_value_expression
+ (i.op[n].disps->X_op_symbol)->X_op)
+ == O_subtract))))
+ || reloc_type == BFD_RELOC_32_PCREL))
+ {
+ offsetT add;
+
+ if (insn_start_frag == frag_now)
+ add = (p - frag_now->fr_literal) - insn_start_off;
+ else
+ {
+ fragS *fr;
+
+ add = insn_start_frag->fr_fix - insn_start_off;
+ for (fr = insn_start_frag->fr_next;
+ fr && fr != frag_now; fr = fr->fr_next)
+ add += fr->fr_fix;
+ add += p - frag_now->fr_literal;
+ }
+
+ if (!object_64bit)
+ {
+ reloc_type = BFD_RELOC_386_GOTPC;
+ i.op[n].imms->X_add_number += add;
+ }
+ else if (reloc_type == BFD_RELOC_64)
+ reloc_type = BFD_RELOC_X86_64_GOTPC64;
+ else
+ /* Don't do the adjustment for x86-64, as there
+ the pcrel addressing is relative to the _next_
+ insn, and that is taken care of in other code. */
+ reloc_type = BFD_RELOC_X86_64_GOTPC32;
+ }
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.op[n].disps, pcrel, reloc_type);
+ }
+ }
+ }
+}
+
+static void
+output_imm (fragS *insn_start_frag, offsetT insn_start_off)
+{
+ char *p;
+ unsigned int n;
+
+ for (n = 0; n < i.operands; n++)
+ {
+ /* Skip SAE/RC Imm operand in EVEX. They are already handled. */
+ if (i.rounding && (int) n == i.rounding->operand)
+ continue;
+
+ if (operand_type_check (i.types[n], imm))
+ {
+ if (i.op[n].imms->X_op == O_constant)
+ {
+ int size = imm_size (n);
+ offsetT val;
+
+ val = offset_in_range (i.op[n].imms->X_add_number,
+ size);
+ p = frag_more (size);
+ md_number_to_chars (p, val, size);
+ }
+ else
+ {
+ /* Not absolute_section.
+ Need a 32-bit fixup (don't support 8bit
+ non-absolute imms). Try to support other
+ sizes ... */
+ enum bfd_reloc_code_real reloc_type;
+ int size = imm_size (n);
+ int sign;
+
+ if (i.types[n].bitfield.imm32s
+ && (i.suffix == QWORD_MNEM_SUFFIX
+ || (!i.suffix && i.tm.opcode_modifier.no_lsuf)))
+ sign = 1;
+ else
+ sign = 0;
+
+ p = frag_more (size);
+ reloc_type = reloc (size, 0, sign, i.reloc[n]);
+
+ /* This is tough to explain. We end up with this one if we
+ * have operands that look like
+ * "_GLOBAL_OFFSET_TABLE_+[.-.L284]". The goal here is to
+ * obtain the absolute address of the GOT, and it is strongly
+ * preferable from a performance point of view to avoid using
+ * a runtime relocation for this. The actual sequence of
+ * instructions often look something like:
+ *
+ * call .L66
+ * .L66:
+ * popl %ebx
+ * addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx
+ *
+ * The call and pop essentially return the absolute address
+ * of the label .L66 and store it in %ebx. The linker itself
+ * will ultimately change the first operand of the addl so
+ * that %ebx points to the GOT, but to keep things simple, the
+ * .o file must have this operand set so that it generates not
+ * the absolute address of .L66, but the absolute address of
+ * itself. This allows the linker itself simply treat a GOTPC
+ * relocation as asking for a pcrel offset to the GOT to be
+ * added in, and the addend of the relocation is stored in the
+ * operand field for the instruction itself.
+ *
+ * Our job here is to fix the operand so that it would add
+ * the correct offset so that %ebx would point to itself. The
+ * thing that is tricky is that .-.L66 will point to the
+ * beginning of the instruction, so we need to further modify
+ * the operand so that it will point to itself. There are
+ * other cases where you have something like:
+ *
+ * .long $_GLOBAL_OFFSET_TABLE_+[.-.L66]
+ *
+ * and here no correction would be required. Internally in
+ * the assembler we treat operands of this form as not being
+ * pcrel since the '.' is explicitly mentioned, and I wonder
+ * whether it would simplify matters to do it this way. Who
+ * knows. In earlier versions of the PIC patches, the
+ * pcrel_adjust field was used to store the correction, but
+ * since the expression is not pcrel, I felt it would be
+ * confusing to do it this way. */
+
+ if ((reloc_type == BFD_RELOC_32
+ || reloc_type == BFD_RELOC_X86_64_32S
+ || reloc_type == BFD_RELOC_64)
+ && GOT_symbol
+ && GOT_symbol == i.op[n].imms->X_add_symbol
+ && (i.op[n].imms->X_op == O_symbol
+ || (i.op[n].imms->X_op == O_add
+ && ((symbol_get_value_expression
+ (i.op[n].imms->X_op_symbol)->X_op)
+ == O_subtract))))
+ {
+ offsetT add;
+
+ if (insn_start_frag == frag_now)
+ add = (p - frag_now->fr_literal) - insn_start_off;
+ else
+ {
+ fragS *fr;
+
+ add = insn_start_frag->fr_fix - insn_start_off;
+ for (fr = insn_start_frag->fr_next;
+ fr && fr != frag_now; fr = fr->fr_next)
+ add += fr->fr_fix;
+ add += p - frag_now->fr_literal;
+ }
+
+ if (!object_64bit)
+ reloc_type = BFD_RELOC_386_GOTPC;
+ else if (size == 4)
+ reloc_type = BFD_RELOC_X86_64_GOTPC32;
+ else if (size == 8)
+ reloc_type = BFD_RELOC_X86_64_GOTPC64;
+ i.op[n].imms->X_add_number += add;
+ }
+ fix_new_exp (frag_now, p - frag_now->fr_literal, size,
+ i.op[n].imms, 0, reloc_type);
+ }
+ }
+ }
+}
+
+/* x86_cons_fix_new is called via the expression parsing code when a
+ reloc is needed. We use this hook to get the correct .got reloc. */
+static int cons_sign = -1;
+
+void
+x86_cons_fix_new (fragS *frag, unsigned int off, unsigned int len,
+ expressionS *exp, bfd_reloc_code_real_type r)
+{
+ r = reloc (len, 0, cons_sign, r);
+
+#ifdef TE_PE
+ if (exp->X_op == O_secrel)
+ {
+ exp->X_op = O_symbol;
+ r = BFD_RELOC_32_SECREL;
+ }
+#endif
+
+ fix_new_exp (frag, off, len, exp, 0, r);
+}
+
+/* Export the ABI address size for use by TC_ADDRESS_BYTES for the
+ purpose of the `.dc.a' internal pseudo-op. */
+
+int
+x86_address_bytes (void)
+{
+ if ((stdoutput->arch_info->mach & bfd_mach_x64_32))
+ return 4;
+ return stdoutput->arch_info->bits_per_address / 8;
+}
+
+#if !(defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined (OBJ_MACH_O)) \
+ || defined (LEX_AT)
+# define lex_got(reloc, adjust, types) NULL
+#else
+/* Parse operands of the form
+ <symbol>@GOTOFF+<nnn>
+ and similar .plt or .got references.
+
+ If we find one, set up the correct relocation in RELOC and copy the
+ input string, minus the `@GOTOFF' into a malloc'd buffer for
+ parsing by the calling routine. Return this buffer, and if ADJUST
+ is non-null set it to the length of the string we removed from the
+ input line. Otherwise return NULL. */
+static char *
+lex_got (enum bfd_reloc_code_real *rel,
+ int *adjust,
+ i386_operand_type *types)
+{
+ /* Some of the relocations depend on the size of what field is to
+ be relocated. But in our callers i386_immediate and i386_displacement
+ we don't yet know the operand size (this will be set by insn
+ matching). Hence we record the word32 relocation here,
+ and adjust the reloc according to the real size in reloc(). */
+ static const struct {
+ const char *str;
+ int len;
+ const enum bfd_reloc_code_real rel[2];
+ const i386_operand_type types64;
+ } gotrel[] = {
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ { STRING_COMMA_LEN ("SIZE"), { BFD_RELOC_SIZE32,
+ BFD_RELOC_SIZE32 },
+ OPERAND_TYPE_IMM32_64 },
+#endif
+ { STRING_COMMA_LEN ("PLTOFF"), { _dummy_first_bfd_reloc_code_real,
+ BFD_RELOC_X86_64_PLTOFF64 },
+ OPERAND_TYPE_IMM64 },
+ { STRING_COMMA_LEN ("PLT"), { BFD_RELOC_386_PLT32,
+ BFD_RELOC_X86_64_PLT32 },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("GOTPLT"), { _dummy_first_bfd_reloc_code_real,
+ BFD_RELOC_X86_64_GOTPLT64 },
+ OPERAND_TYPE_IMM64_DISP64 },
+ { STRING_COMMA_LEN ("GOTOFF"), { BFD_RELOC_386_GOTOFF,
+ BFD_RELOC_X86_64_GOTOFF64 },
+ OPERAND_TYPE_IMM64_DISP64 },
+ { STRING_COMMA_LEN ("GOTPCREL"), { _dummy_first_bfd_reloc_code_real,
+ BFD_RELOC_X86_64_GOTPCREL },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("TLSGD"), { BFD_RELOC_386_TLS_GD,
+ BFD_RELOC_X86_64_TLSGD },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("TLSLDM"), { BFD_RELOC_386_TLS_LDM,
+ _dummy_first_bfd_reloc_code_real },
+ OPERAND_TYPE_NONE },
+ { STRING_COMMA_LEN ("TLSLD"), { _dummy_first_bfd_reloc_code_real,
+ BFD_RELOC_X86_64_TLSLD },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("GOTTPOFF"), { BFD_RELOC_386_TLS_IE_32,
+ BFD_RELOC_X86_64_GOTTPOFF },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("TPOFF"), { BFD_RELOC_386_TLS_LE_32,
+ BFD_RELOC_X86_64_TPOFF32 },
+ OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
+ { STRING_COMMA_LEN ("NTPOFF"), { BFD_RELOC_386_TLS_LE,
+ _dummy_first_bfd_reloc_code_real },
+ OPERAND_TYPE_NONE },
+ { STRING_COMMA_LEN ("DTPOFF"), { BFD_RELOC_386_TLS_LDO_32,
+ BFD_RELOC_X86_64_DTPOFF32 },
+ OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
+ { STRING_COMMA_LEN ("GOTNTPOFF"),{ BFD_RELOC_386_TLS_GOTIE,
+ _dummy_first_bfd_reloc_code_real },
+ OPERAND_TYPE_NONE },
+ { STRING_COMMA_LEN ("INDNTPOFF"),{ BFD_RELOC_386_TLS_IE,
+ _dummy_first_bfd_reloc_code_real },
+ OPERAND_TYPE_NONE },
+ { STRING_COMMA_LEN ("GOT"), { BFD_RELOC_386_GOT32,
+ BFD_RELOC_X86_64_GOT32 },
+ OPERAND_TYPE_IMM32_32S_64_DISP32 },
+ { STRING_COMMA_LEN ("TLSDESC"), { BFD_RELOC_386_TLS_GOTDESC,
+ BFD_RELOC_X86_64_GOTPC32_TLSDESC },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ { STRING_COMMA_LEN ("TLSCALL"), { BFD_RELOC_386_TLS_DESC_CALL,
+ BFD_RELOC_X86_64_TLSDESC_CALL },
+ OPERAND_TYPE_IMM32_32S_DISP32 },
+ };
+ char *cp;
+ unsigned int j;
+
+#if defined (OBJ_MAYBE_ELF)
+ if (!IS_ELF)
+ return NULL;
+#endif
+
+ for (cp = input_line_pointer; *cp != '@'; cp++)
+ if (is_end_of_line[(unsigned char) *cp] || *cp == ',')
+ return NULL;
+
+ for (j = 0; j < ARRAY_SIZE (gotrel); j++)
+ {
+ int len = gotrel[j].len;
+ if (strncasecmp (cp + 1, gotrel[j].str, len) == 0)
+ {
+ if (gotrel[j].rel[object_64bit] != 0)
+ {
+ int first, second;
+ char *tmpbuf, *past_reloc;
+
+ *rel = gotrel[j].rel[object_64bit];
+
+ if (types)
+ {
+ if (flag_code != CODE_64BIT)
+ {
+ types->bitfield.imm32 = 1;
+ types->bitfield.disp32 = 1;
+ }
+ else
+ *types = gotrel[j].types64;
+ }
+
+ if (j != 0 && GOT_symbol == NULL)
+ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
+
+ /* The length of the first part of our input line. */
+ first = cp - input_line_pointer;
+
+ /* The second part goes from after the reloc token until
+ (and including) an end_of_line char or comma. */
+ past_reloc = cp + 1 + len;
+ cp = past_reloc;
+ while (!is_end_of_line[(unsigned char) *cp] && *cp != ',')
+ ++cp;
+ second = cp + 1 - past_reloc;
+
+ /* Allocate and copy string. The trailing NUL shouldn't
+ be necessary, but be safe. */
+ tmpbuf = (char *) xmalloc (first + second + 2);
+ memcpy (tmpbuf, input_line_pointer, first);
+ if (second != 0 && *past_reloc != ' ')
+ /* Replace the relocation token with ' ', so that
+ errors like foo@GOTOFF1 will be detected. */
+ tmpbuf[first++] = ' ';
+ else
+ /* Increment length by 1 if the relocation token is
+ removed. */
+ len++;
+ if (adjust)
+ *adjust = len;
+ memcpy (tmpbuf + first, past_reloc, second);
+ tmpbuf[first + second] = '\0';
+ return tmpbuf;
+ }
+
+ as_bad (_("@%s reloc is not supported with %d-bit output format"),
+ gotrel[j].str, 1 << (5 + object_64bit));
+ return NULL;
+ }
+ }
+
+ /* Might be a symbol version string. Don't as_bad here. */
+ return NULL;
+}
+#endif
+
+#ifdef TE_PE
+#ifdef lex_got
+#undef lex_got
+#endif
+/* Parse operands of the form
+ <symbol>@SECREL32+<nnn>
+
+ If we find one, set up the correct relocation in RELOC and copy the
+ input string, minus the `@SECREL32' into a malloc'd buffer for
+ parsing by the calling routine. Return this buffer, and if ADJUST
+ is non-null set it to the length of the string we removed from the
+ input line. Otherwise return NULL.
+
+ This function is copied from the ELF version above adjusted for PE targets. */
+
+static char *
+lex_got (enum bfd_reloc_code_real *rel ATTRIBUTE_UNUSED,
+ int *adjust ATTRIBUTE_UNUSED,
+ i386_operand_type *types)
+{
+ static const struct
+ {
+ const char *str;
+ int len;
+ const enum bfd_reloc_code_real rel[2];
+ const i386_operand_type types64;
+ }
+ gotrel[] =
+ {
+ { STRING_COMMA_LEN ("SECREL32"), { BFD_RELOC_32_SECREL,
+ BFD_RELOC_32_SECREL },
+ OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
+ };
+
+ char *cp;
+ unsigned j;
+
+ for (cp = input_line_pointer; *cp != '@'; cp++)
+ if (is_end_of_line[(unsigned char) *cp] || *cp == ',')
+ return NULL;
+
+ for (j = 0; j < ARRAY_SIZE (gotrel); j++)
+ {
+ int len = gotrel[j].len;
+
+ if (strncasecmp (cp + 1, gotrel[j].str, len) == 0)
+ {
+ if (gotrel[j].rel[object_64bit] != 0)
+ {
+ int first, second;
+ char *tmpbuf, *past_reloc;
+
+ *rel = gotrel[j].rel[object_64bit];
+ if (adjust)
+ *adjust = len;
+
+ if (types)
+ {
+ if (flag_code != CODE_64BIT)
+ {
+ types->bitfield.imm32 = 1;
+ types->bitfield.disp32 = 1;
+ }
+ else
+ *types = gotrel[j].types64;
+ }
+
+ /* The length of the first part of our input line. */
+ first = cp - input_line_pointer;
+
+ /* The second part goes from after the reloc token until
+ (and including) an end_of_line char or comma. */
+ past_reloc = cp + 1 + len;
+ cp = past_reloc;
+ while (!is_end_of_line[(unsigned char) *cp] && *cp != ',')
+ ++cp;
+ second = cp + 1 - past_reloc;
+
+ /* Allocate and copy string. The trailing NUL shouldn't
+ be necessary, but be safe. */
+ tmpbuf = (char *) xmalloc (first + second + 2);
+ memcpy (tmpbuf, input_line_pointer, first);
+ if (second != 0 && *past_reloc != ' ')
+ /* Replace the relocation token with ' ', so that
+ errors like foo@SECLREL321 will be detected. */
+ tmpbuf[first++] = ' ';
+ memcpy (tmpbuf + first, past_reloc, second);
+ tmpbuf[first + second] = '\0';
+ return tmpbuf;
+ }
+
+ as_bad (_("@%s reloc is not supported with %d-bit output format"),
+ gotrel[j].str, 1 << (5 + object_64bit));
+ return NULL;
+ }
+ }
+
+ /* Might be a symbol version string. Don't as_bad here. */
+ return NULL;
+}
+
+#endif /* TE_PE */
+
+bfd_reloc_code_real_type
+x86_cons (expressionS *exp, int size)
+{
+ bfd_reloc_code_real_type got_reloc = NO_RELOC;
+
+ intel_syntax = -intel_syntax;
+
+ exp->X_md = 0;
+ if (size == 4 || (object_64bit && size == 8))
+ {
+ /* Handle @GOTOFF and the like in an expression. */
+ char *save;
+ char *gotfree_input_line;
+ int adjust = 0;
+
+ save = input_line_pointer;
+ gotfree_input_line = lex_got (&got_reloc, &adjust, NULL);
+ if (gotfree_input_line)
+ input_line_pointer = gotfree_input_line;
+
+ expression (exp);
+
+ if (gotfree_input_line)
+ {
+ /* expression () has merrily parsed up to the end of line,
+ or a comma - in the wrong buffer. Transfer how far
+ input_line_pointer has moved to the right buffer. */
+ input_line_pointer = (save
+ + (input_line_pointer - gotfree_input_line)
+ + adjust);
+ free (gotfree_input_line);
+ if (exp->X_op == O_constant
+ || exp->X_op == O_absent
+ || exp->X_op == O_illegal
+ || exp->X_op == O_register
+ || exp->X_op == O_big)
+ {
+ char c = *input_line_pointer;
+ *input_line_pointer = 0;
+ as_bad (_("missing or invalid expression `%s'"), save);
+ *input_line_pointer = c;
+ }
+ }
+ }
+ else
+ expression (exp);
+
+ intel_syntax = -intel_syntax;
+
+ if (intel_syntax)
+ i386_intel_simplify (exp);
+
+ return got_reloc;
+}
+
+static void
+signed_cons (int size)
+{
+ if (flag_code == CODE_64BIT)
+ cons_sign = 1;
+ cons (size);
+ cons_sign = -1;
+}
+
+#ifdef TE_PE
+static void
+pe_directive_secrel (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol)
+ exp.X_op = O_secrel;
+
+ emit_expr (&exp, 4);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+#endif
+
+/* Handle Vector operations. */
+
+static char *
+check_VecOperations (char *op_string, char *op_end)
+{
+ const reg_entry *mask;
+ const char *saved;
+ char *end_op;
+
+ while (*op_string
+ && (op_end == NULL || op_string < op_end))
+ {
+ saved = op_string;
+ if (*op_string == '{')
+ {
+ op_string++;
+
+ /* Check broadcasts. */
+ if (strncmp (op_string, "1to", 3) == 0)
+ {
+ int bcst_type;
+
+ if (i.broadcast)
+ goto duplicated_vec_op;
+
+ op_string += 3;
+ if (*op_string == '8')
+ bcst_type = BROADCAST_1TO8;
+ else if (*op_string == '4')
+ bcst_type = BROADCAST_1TO4;
+ else if (*op_string == '2')
+ bcst_type = BROADCAST_1TO2;
+ else if (*op_string == '1'
+ && *(op_string+1) == '6')
+ {
+ bcst_type = BROADCAST_1TO16;
+ op_string++;
+ }
+ else
+ {
+ as_bad (_("Unsupported broadcast: `%s'"), saved);
+ return NULL;
+ }
+ op_string++;
+
+ broadcast_op.type = bcst_type;
+ broadcast_op.operand = this_operand;
+ i.broadcast = &broadcast_op;
+ }
+ /* Check masking operation. */
+ else if ((mask = parse_register (op_string, &end_op)) != NULL)
+ {
+ /* k0 can't be used for write mask. */
+ if (mask->reg_num == 0)
+ {
+ as_bad (_("`%s' can't be used for write mask"),
+ op_string);
+ return NULL;
+ }
+
+ if (!i.mask)
+ {
+ mask_op.mask = mask;
+ mask_op.zeroing = 0;
+ mask_op.operand = this_operand;
+ i.mask = &mask_op;
+ }
+ else
+ {
+ if (i.mask->mask)
+ goto duplicated_vec_op;
+
+ i.mask->mask = mask;
+
+ /* Only "{z}" is allowed here. No need to check
+ zeroing mask explicitly. */
+ if (i.mask->operand != this_operand)
+ {
+ as_bad (_("invalid write mask `%s'"), saved);
+ return NULL;
+ }
+ }
+
+ op_string = end_op;
+ }
+ /* Check zeroing-flag for masking operation. */
+ else if (*op_string == 'z')
+ {
+ if (!i.mask)
+ {
+ mask_op.mask = NULL;
+ mask_op.zeroing = 1;
+ mask_op.operand = this_operand;
+ i.mask = &mask_op;
+ }
+ else
+ {
+ if (i.mask->zeroing)
+ {
+ duplicated_vec_op:
+ as_bad (_("duplicated `%s'"), saved);
+ return NULL;
+ }
+
+ i.mask->zeroing = 1;
+
+ /* Only "{%k}" is allowed here. No need to check mask
+ register explicitly. */
+ if (i.mask->operand != this_operand)
+ {
+ as_bad (_("invalid zeroing-masking `%s'"),
+ saved);
+ return NULL;
+ }
+ }
+
+ op_string++;
+ }
+ else
+ goto unknown_vec_op;
+
+ if (*op_string != '}')
+ {
+ as_bad (_("missing `}' in `%s'"), saved);
+ return NULL;
+ }
+ op_string++;
+ continue;
+ }
+ unknown_vec_op:
+ /* We don't know this one. */
+ as_bad (_("unknown vector operation: `%s'"), saved);
+ return NULL;
+ }
+
+ return op_string;
+}
+
+static int
+i386_immediate (char *imm_start)
+{
+ char *save_input_line_pointer;
+ char *gotfree_input_line;
+ segT exp_seg = 0;
+ expressionS *exp;
+ i386_operand_type types;
+
+ operand_type_set (&types, ~0);
+
+ if (i.imm_operands == MAX_IMMEDIATE_OPERANDS)
+ {
+ as_bad (_("at most %d immediate operands are allowed"),
+ MAX_IMMEDIATE_OPERANDS);
+ return 0;
+ }
+
+ exp = &im_expressions[i.imm_operands++];
+ i.op[this_operand].imms = exp;
+
+ if (is_space_char (*imm_start))
+ ++imm_start;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = imm_start;
+
+ gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types);
+ if (gotfree_input_line)
+ input_line_pointer = gotfree_input_line;
+
+ exp_seg = expression (exp);
+
+ SKIP_WHITESPACE ();
+
+ /* Handle vector operations. */
+ if (*input_line_pointer == '{')
+ {
+ input_line_pointer = check_VecOperations (input_line_pointer,
+ NULL);
+ if (input_line_pointer == NULL)
+ return 0;
+ }
+
+ if (*input_line_pointer)
+ as_bad (_("junk `%s' after expression"), input_line_pointer);
+
+ input_line_pointer = save_input_line_pointer;
+ if (gotfree_input_line)
+ {
+ free (gotfree_input_line);
+
+ if (exp->X_op == O_constant || exp->X_op == O_register)
+ exp->X_op = O_illegal;
+ }
+
+ return i386_finalize_immediate (exp_seg, exp, types, imm_start);
+}
+
+static int
+i386_finalize_immediate (segT exp_seg ATTRIBUTE_UNUSED, expressionS *exp,
+ i386_operand_type types, const char *imm_start)
+{
+ if (exp->X_op == O_absent || exp->X_op == O_illegal || exp->X_op == O_big)
+ {
+ if (imm_start)
+ as_bad (_("missing or invalid immediate expression `%s'"),
+ imm_start);
+ return 0;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ /* Size it properly later. */
+ i.types[this_operand].bitfield.imm64 = 1;
+ /* If not 64bit, sign extend val. */
+ if (flag_code != CODE_64BIT
+ && (exp->X_add_number & ~(((addressT) 2 << 31) - 1)) == 0)
+ exp->X_add_number
+ = (exp->X_add_number ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31);
+ }
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))
+ else if (OUTPUT_FLAVOR == bfd_target_aout_flavour
+ && exp_seg != absolute_section
+ && exp_seg != text_section
+ && exp_seg != data_section
+ && exp_seg != bss_section
+ && exp_seg != undefined_section
+ && !bfd_is_com_section (exp_seg))
+ {
+ as_bad (_("unimplemented segment %s in operand"), exp_seg->name);
+ return 0;
+ }
+#endif
+ else if (!intel_syntax && exp_seg == reg_section)
+ {
+ if (imm_start)
+ as_bad (_("illegal immediate register operand %s"), imm_start);
+ return 0;
+ }
+ else
+ {
+ /* This is an address. The size of the address will be
+ determined later, depending on destination register,
+ suffix, or the default for the section. */
+ i.types[this_operand].bitfield.imm8 = 1;
+ i.types[this_operand].bitfield.imm16 = 1;
+ i.types[this_operand].bitfield.imm32 = 1;
+ i.types[this_operand].bitfield.imm32s = 1;
+ i.types[this_operand].bitfield.imm64 = 1;
+ i.types[this_operand] = operand_type_and (i.types[this_operand],
+ types);
+ }
+
+ return 1;
+}
+
+static char *
+i386_scale (char *scale)
+{
+ offsetT val;
+ char *save = input_line_pointer;
+
+ input_line_pointer = scale;
+ val = get_absolute_expression ();
+
+ switch (val)
+ {
+ case 1:
+ i.log2_scale_factor = 0;
+ break;
+ case 2:
+ i.log2_scale_factor = 1;
+ break;
+ case 4:
+ i.log2_scale_factor = 2;
+ break;
+ case 8:
+ i.log2_scale_factor = 3;
+ break;
+ default:
+ {
+ char sep = *input_line_pointer;
+
+ *input_line_pointer = '\0';
+ as_bad (_("expecting scale factor of 1, 2, 4, or 8: got `%s'"),
+ scale);
+ *input_line_pointer = sep;
+ input_line_pointer = save;
+ return NULL;
+ }
+ }
+ if (i.log2_scale_factor != 0 && i.index_reg == 0)
+ {
+ as_warn (_("scale factor of %d without an index register"),
+ 1 << i.log2_scale_factor);
+ i.log2_scale_factor = 0;
+ }
+ scale = input_line_pointer;
+ input_line_pointer = save;
+ return scale;
+}
+
+static int
+i386_displacement (char *disp_start, char *disp_end)
+{
+ expressionS *exp;
+ segT exp_seg = 0;
+ char *save_input_line_pointer;
+ char *gotfree_input_line;
+ int override;
+ i386_operand_type bigdisp, types = anydisp;
+ int ret;
+
+ if (i.disp_operands == MAX_MEMORY_OPERANDS)
+ {
+ as_bad (_("at most %d displacement operands are allowed"),
+ MAX_MEMORY_OPERANDS);
+ return 0;
+ }
+
+ operand_type_set (&bigdisp, 0);
+ if ((i.types[this_operand].bitfield.jumpabsolute)
+ || (!current_templates->start->opcode_modifier.jump
+ && !current_templates->start->opcode_modifier.jumpdword))
+ {
+ bigdisp.bitfield.disp32 = 1;
+ override = (i.prefix[ADDR_PREFIX] != 0);
+ if (flag_code == CODE_64BIT)
+ {
+ if (!override)
+ {
+ bigdisp.bitfield.disp32s = 1;
+ bigdisp.bitfield.disp64 = 1;
+ }
+ }
+ else if ((flag_code == CODE_16BIT) ^ override)
+ {
+ bigdisp.bitfield.disp32 = 0;
+ bigdisp.bitfield.disp16 = 1;
+ }
+ }
+ else
+ {
+ /* For PC-relative branches, the width of the displacement
+ is dependent upon data size, not address size. */
+ override = (i.prefix[DATA_PREFIX] != 0);
+ if (flag_code == CODE_64BIT)
+ {
+ if (override || i.suffix == WORD_MNEM_SUFFIX)
+ bigdisp.bitfield.disp16 = 1;
+ else
+ {
+ bigdisp.bitfield.disp32 = 1;
+ bigdisp.bitfield.disp32s = 1;
+ }
+ }
+ else
+ {
+ if (!override)
+ override = (i.suffix == (flag_code != CODE_16BIT
+ ? WORD_MNEM_SUFFIX
+ : LONG_MNEM_SUFFIX));
+ bigdisp.bitfield.disp32 = 1;
+ if ((flag_code == CODE_16BIT) ^ override)
+ {
+ bigdisp.bitfield.disp32 = 0;
+ bigdisp.bitfield.disp16 = 1;
+ }
+ }
+ }
+ i.types[this_operand] = operand_type_or (i.types[this_operand],
+ bigdisp);
+
+ exp = &disp_expressions[i.disp_operands];
+ i.op[this_operand].disps = exp;
+ i.disp_operands++;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = disp_start;
+ END_STRING_AND_SAVE (disp_end);
+
+#ifndef GCC_ASM_O_HACK
+#define GCC_ASM_O_HACK 0
+#endif
+#if GCC_ASM_O_HACK
+ END_STRING_AND_SAVE (disp_end + 1);
+ if (i.types[this_operand].bitfield.baseIndex
+ && displacement_string_end[-1] == '+')
+ {
+ /* This hack is to avoid a warning when using the "o"
+ constraint within gcc asm statements.
+ For instance:
+
+ #define _set_tssldt_desc(n,addr,limit,type) \
+ __asm__ __volatile__ ( \
+ "movw %w2,%0\n\t" \
+ "movw %w1,2+%0\n\t" \
+ "rorl $16,%1\n\t" \
+ "movb %b1,4+%0\n\t" \
+ "movb %4,5+%0\n\t" \
+ "movb $0,6+%0\n\t" \
+ "movb %h1,7+%0\n\t" \
+ "rorl $16,%1" \
+ : "=o"(*(n)) : "q" (addr), "ri"(limit), "i"(type))
+
+ This works great except that the output assembler ends
+ up looking a bit weird if it turns out that there is
+ no offset. You end up producing code that looks like:
+
+ #APP
+ movw $235,(%eax)
+ movw %dx,2+(%eax)
+ rorl $16,%edx
+ movb %dl,4+(%eax)
+ movb $137,5+(%eax)
+ movb $0,6+(%eax)
+ movb %dh,7+(%eax)
+ rorl $16,%edx
+ #NO_APP
+
+ So here we provide the missing zero. */
+
+ *displacement_string_end = '0';
+ }
+#endif
+ gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types);
+ if (gotfree_input_line)
+ input_line_pointer = gotfree_input_line;
+
+ exp_seg = expression (exp);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer)
+ as_bad (_("junk `%s' after expression"), input_line_pointer);
+#if GCC_ASM_O_HACK
+ RESTORE_END_STRING (disp_end + 1);
+#endif
+ input_line_pointer = save_input_line_pointer;
+ if (gotfree_input_line)
+ {
+ free (gotfree_input_line);
+
+ if (exp->X_op == O_constant || exp->X_op == O_register)
+ exp->X_op = O_illegal;
+ }
+
+ ret = i386_finalize_displacement (exp_seg, exp, types, disp_start);
+
+ RESTORE_END_STRING (disp_end);
+
+ return ret;
+}
+
+static int
+i386_finalize_displacement (segT exp_seg ATTRIBUTE_UNUSED, expressionS *exp,
+ i386_operand_type types, const char *disp_start)
+{
+ i386_operand_type bigdisp;
+ int ret = 1;
+
+ /* We do this to make sure that the section symbol is in
+ the symbol table. We will ultimately change the relocation
+ to be relative to the beginning of the section. */
+ if (i.reloc[this_operand] == BFD_RELOC_386_GOTOFF
+ || i.reloc[this_operand] == BFD_RELOC_X86_64_GOTPCREL
+ || i.reloc[this_operand] == BFD_RELOC_X86_64_GOTOFF64)
+ {
+ if (exp->X_op != O_symbol)
+ goto inv_disp;
+
+ if (S_IS_LOCAL (exp->X_add_symbol)
+ && S_GET_SEGMENT (exp->X_add_symbol) != undefined_section
+ && S_GET_SEGMENT (exp->X_add_symbol) != expr_section)
+ section_symbol (S_GET_SEGMENT (exp->X_add_symbol));
+ exp->X_op = O_subtract;
+ exp->X_op_symbol = GOT_symbol;
+ if (i.reloc[this_operand] == BFD_RELOC_X86_64_GOTPCREL)
+ i.reloc[this_operand] = BFD_RELOC_32_PCREL;
+ else if (i.reloc[this_operand] == BFD_RELOC_X86_64_GOTOFF64)
+ i.reloc[this_operand] = BFD_RELOC_64;
+ else
+ i.reloc[this_operand] = BFD_RELOC_32;
+ }
+
+ else if (exp->X_op == O_absent
+ || exp->X_op == O_illegal
+ || exp->X_op == O_big)
+ {
+ inv_disp:
+ as_bad (_("missing or invalid displacement expression `%s'"),
+ disp_start);
+ ret = 0;
+ }
+
+ else if (flag_code == CODE_64BIT
+ && !i.prefix[ADDR_PREFIX]
+ && exp->X_op == O_constant)
+ {
+ /* Since displacement is signed extended to 64bit, don't allow
+ disp32 and turn off disp32s if they are out of range. */
+ i.types[this_operand].bitfield.disp32 = 0;
+ if (!fits_in_signed_long (exp->X_add_number))
+ {
+ i.types[this_operand].bitfield.disp32s = 0;
+ if (i.types[this_operand].bitfield.baseindex)
+ {
+ as_bad (_("0x%lx out range of signed 32bit displacement"),
+ (long) exp->X_add_number);
+ ret = 0;
+ }
+ }
+ }
+
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))
+ else if (exp->X_op != O_constant
+ && OUTPUT_FLAVOR == bfd_target_aout_flavour
+ && exp_seg != absolute_section
+ && exp_seg != text_section
+ && exp_seg != data_section
+ && exp_seg != bss_section
+ && exp_seg != undefined_section
+ && !bfd_is_com_section (exp_seg))
+ {
+ as_bad (_("unimplemented segment %s in operand"), exp_seg->name);
+ ret = 0;
+ }
+#endif
+
+ /* Check if this is a displacement only operand. */
+ bigdisp = i.types[this_operand];
+ bigdisp.bitfield.disp8 = 0;
+ bigdisp.bitfield.disp16 = 0;
+ bigdisp.bitfield.disp32 = 0;
+ bigdisp.bitfield.disp32s = 0;
+ bigdisp.bitfield.disp64 = 0;
+ if (operand_type_all_zero (&bigdisp))
+ i.types[this_operand] = operand_type_and (i.types[this_operand],
+ types);
+
+ return ret;
+}
+
+/* Make sure the memory operand we've been dealt is valid.
+ Return 1 on success, 0 on a failure. */
+
+static int
+i386_index_check (const char *operand_string)
+{
+ const char *kind = "base/index";
+ enum flag_code addr_mode;
+
+ if (i.prefix[ADDR_PREFIX])
+ addr_mode = flag_code == CODE_32BIT ? CODE_16BIT : CODE_32BIT;
+ else
+ {
+ addr_mode = flag_code;
+
+#if INFER_ADDR_PREFIX
+ if (i.mem_operands == 0)
+ {
+ /* Infer address prefix from the first memory operand. */
+ const reg_entry *addr_reg = i.base_reg;
+
+ if (addr_reg == NULL)
+ addr_reg = i.index_reg;
+
+ if (addr_reg)
+ {
+ if (addr_reg->reg_num == RegEip
+ || addr_reg->reg_num == RegEiz
+ || addr_reg->reg_type.bitfield.reg32)
+ addr_mode = CODE_32BIT;
+ else if (flag_code != CODE_64BIT
+ && addr_reg->reg_type.bitfield.reg16)
+ addr_mode = CODE_16BIT;
+
+ if (addr_mode != flag_code)
+ {
+ i.prefix[ADDR_PREFIX] = ADDR_PREFIX_OPCODE;
+ i.prefixes += 1;
+ /* Change the size of any displacement too. At most one
+ of Disp16 or Disp32 is set.
+ FIXME. There doesn't seem to be any real need for
+ separate Disp16 and Disp32 flags. The same goes for
+ Imm16 and Imm32. Removing them would probably clean
+ up the code quite a lot. */
+ if (flag_code != CODE_64BIT
+ && (i.types[this_operand].bitfield.disp16
+ || i.types[this_operand].bitfield.disp32))
+ i.types[this_operand]
+ = operand_type_xor (i.types[this_operand], disp16_32);
+ }
+ }
+ }
+#endif
+ }
+
+ if (current_templates->start->opcode_modifier.isstring
+ && !current_templates->start->opcode_modifier.immext
+ && (current_templates->end[-1].opcode_modifier.isstring
+ || i.mem_operands))
+ {
+ /* Memory operands of string insns are special in that they only allow
+ a single register (rDI, rSI, or rBX) as their memory address. */
+ const reg_entry *expected_reg;
+ static const char *di_si[][2] =
+ {
+ { "esi", "edi" },
+ { "si", "di" },
+ { "rsi", "rdi" }
+ };
+ static const char *bx[] = { "ebx", "bx", "rbx" };
+
+ kind = "string address";
+
+ if (current_templates->start->opcode_modifier.w)
+ {
+ i386_operand_type type = current_templates->end[-1].operand_types[0];
+
+ if (!type.bitfield.baseindex
+ || ((!i.mem_operands != !intel_syntax)
+ && current_templates->end[-1].operand_types[1]
+ .bitfield.baseindex))
+ type = current_templates->end[-1].operand_types[1];
+ expected_reg = hash_find (reg_hash,
+ di_si[addr_mode][type.bitfield.esseg]);
+
+ }
+ else
+ expected_reg = hash_find (reg_hash, bx[addr_mode]);
+
+ if (i.base_reg != expected_reg
+ || i.index_reg
+ || operand_type_check (i.types[this_operand], disp))
+ {
+ /* The second memory operand must have the same size as
+ the first one. */
+ if (i.mem_operands
+ && i.base_reg
+ && !((addr_mode == CODE_64BIT
+ && i.base_reg->reg_type.bitfield.reg64)
+ || (addr_mode == CODE_32BIT
+ ? i.base_reg->reg_type.bitfield.reg32
+ : i.base_reg->reg_type.bitfield.reg16)))
+ goto bad_address;
+
+ as_warn (_("`%s' is not valid here (expected `%c%s%s%c')"),
+ operand_string,
+ intel_syntax ? '[' : '(',
+ register_prefix,
+ expected_reg->reg_name,
+ intel_syntax ? ']' : ')');
+ return 1;
+ }
+ else
+ return 1;
+
+bad_address:
+ as_bad (_("`%s' is not a valid %s expression"),
+ operand_string, kind);
+ return 0;
+ }
+ else
+ {
+ if (addr_mode != CODE_16BIT)
+ {
+ /* 32-bit/64-bit checks. */
+ if ((i.base_reg
+ && (addr_mode == CODE_64BIT
+ ? !i.base_reg->reg_type.bitfield.reg64
+ : !i.base_reg->reg_type.bitfield.reg32)
+ && (i.index_reg
+ || (i.base_reg->reg_num
+ != (addr_mode == CODE_64BIT ? RegRip : RegEip))))
+ || (i.index_reg
+ && !i.index_reg->reg_type.bitfield.regxmm
+ && !i.index_reg->reg_type.bitfield.regymm
+ && !i.index_reg->reg_type.bitfield.regzmm
+ && ((addr_mode == CODE_64BIT
+ ? !(i.index_reg->reg_type.bitfield.reg64
+ || i.index_reg->reg_num == RegRiz)
+ : !(i.index_reg->reg_type.bitfield.reg32
+ || i.index_reg->reg_num == RegEiz))
+ || !i.index_reg->reg_type.bitfield.baseindex)))
+ goto bad_address;
+ }
+ else
+ {
+ /* 16-bit checks. */
+ if ((i.base_reg
+ && (!i.base_reg->reg_type.bitfield.reg16
+ || !i.base_reg->reg_type.bitfield.baseindex))
+ || (i.index_reg
+ && (!i.index_reg->reg_type.bitfield.reg16
+ || !i.index_reg->reg_type.bitfield.baseindex
+ || !(i.base_reg
+ && i.base_reg->reg_num < 6
+ && i.index_reg->reg_num >= 6
+ && i.log2_scale_factor == 0))))
+ goto bad_address;
+ }
+ }
+ return 1;
+}
+
+/* Handle vector immediates. */
+
+static int
+RC_SAE_immediate (const char *imm_start)
+{
+ unsigned int match_found, j;
+ const char *pstr = imm_start;
+ expressionS *exp;
+
+ if (*pstr != '{')
+ return 0;
+
+ pstr++;
+ match_found = 0;
+ for (j = 0; j < ARRAY_SIZE (RC_NamesTable); j++)
+ {
+ if (!strncmp (pstr, RC_NamesTable[j].name, RC_NamesTable[j].len))
+ {
+ if (!i.rounding)
+ {
+ rc_op.type = RC_NamesTable[j].type;
+ rc_op.operand = this_operand;
+ i.rounding = &rc_op;
+ }
+ else
+ {
+ as_bad (_("duplicated `%s'"), imm_start);
+ return 0;
+ }
+ pstr += RC_NamesTable[j].len;
+ match_found = 1;
+ break;
+ }
+ }
+ if (!match_found)
+ return 0;
+
+ if (*pstr++ != '}')
+ {
+ as_bad (_("Missing '}': '%s'"), imm_start);
+ return 0;
+ }
+ /* RC/SAE immediate string should contain nothing more. */;
+ if (*pstr != 0)
+ {
+ as_bad (_("Junk after '}': '%s'"), imm_start);
+ return 0;
+ }
+
+ exp = &im_expressions[i.imm_operands++];
+ i.op[this_operand].imms = exp;
+
+ exp->X_op = O_constant;
+ exp->X_add_number = 0;
+ exp->X_add_symbol = (symbolS *) 0;
+ exp->X_op_symbol = (symbolS *) 0;
+
+ i.types[this_operand].bitfield.imm8 = 1;
+ return 1;
+}
+
+/* Parse OPERAND_STRING into the i386_insn structure I. Returns zero
+ on error. */
+
+static int
+i386_att_operand (char *operand_string)
+{
+ const reg_entry *r;
+ char *end_op;
+ char *op_string = operand_string;
+
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ /* We check for an absolute prefix (differentiating,
+ for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */
+ if (*op_string == ABSOLUTE_PREFIX)
+ {
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ }
+
+ /* Check if operand is a register. */
+ if ((r = parse_register (op_string, &end_op)) != NULL)
+ {
+ i386_operand_type temp;
+
+ /* Check for a segment override by searching for ':' after a
+ segment register. */
+ op_string = end_op;
+ if (is_space_char (*op_string))
+ ++op_string;
+ if (*op_string == ':'
+ && (r->reg_type.bitfield.sreg2
+ || r->reg_type.bitfield.sreg3))
+ {
+ switch (r->reg_num)
+ {
+ case 0:
+ i.seg[i.mem_operands] = &es;
+ break;
+ case 1:
+ i.seg[i.mem_operands] = &cs;
+ break;
+ case 2:
+ i.seg[i.mem_operands] = &ss;
+ break;
+ case 3:
+ i.seg[i.mem_operands] = &ds;
+ break;
+ case 4:
+ i.seg[i.mem_operands] = &fs;
+ break;
+ case 5:
+ i.seg[i.mem_operands] = &gs;
+ break;
+ }
+
+ /* Skip the ':' and whitespace. */
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+
+ if (!is_digit_char (*op_string)
+ && !is_identifier_char (*op_string)
+ && *op_string != '('
+ && *op_string != ABSOLUTE_PREFIX)
+ {
+ as_bad (_("bad memory operand `%s'"), op_string);
+ return 0;
+ }
+ /* Handle case of %es:*foo. */
+ if (*op_string == ABSOLUTE_PREFIX)
+ {
+ ++op_string;
+ if (is_space_char (*op_string))
+ ++op_string;
+ i.types[this_operand].bitfield.jumpabsolute = 1;
+ }
+ goto do_memory_reference;
+ }
+
+ /* Handle vector operations. */
+ if (*op_string == '{')
+ {
+ op_string = check_VecOperations (op_string, NULL);
+ if (op_string == NULL)
+ return 0;
+ }
+
+ if (*op_string)
+ {
+ as_bad (_("junk `%s' after register"), op_string);
+ return 0;
+ }
+ temp = r->reg_type;
+ temp.bitfield.baseindex = 0;
+ i.types[this_operand] = operand_type_or (i.types[this_operand],
+ temp);
+ i.types[this_operand].bitfield.unspecified = 0;
+ i.op[this_operand].regs = r;
+ i.reg_operands++;
+ }
+ else if (*op_string == REGISTER_PREFIX)
+ {
+ as_bad (_("bad register name `%s'"), op_string);
+ return 0;
+ }
+ else if (*op_string == IMMEDIATE_PREFIX)
+ {
+ ++op_string;
+ if (i.types[this_operand].bitfield.jumpabsolute)
+ {
+ as_bad (_("immediate operand illegal with absolute jump"));
+ return 0;
+ }
+ if (!i386_immediate (op_string))
+ return 0;
+ }
+ else if (RC_SAE_immediate (operand_string))
+ {
+ /* If it is a RC or SAE immediate, do nothing. */
+ ;
+ }
+ else if (is_digit_char (*op_string)
+ || is_identifier_char (*op_string)
+ || *op_string == '(')
+ {
+ /* This is a memory reference of some sort. */
+ char *base_string;
+
+ /* Start and end of displacement string expression (if found). */
+ char *displacement_string_start;
+ char *displacement_string_end;
+ char *vop_start;
+
+ do_memory_reference:
+ if ((i.mem_operands == 1
+ && !current_templates->start->opcode_modifier.isstring)
+ || i.mem_operands == 2)
+ {
+ as_bad (_("too many memory references for `%s'"),
+ current_templates->start->name);
+ return 0;
+ }
+
+ /* Check for base index form. We detect the base index form by
+ looking for an ')' at the end of the operand, searching
+ for the '(' matching it, and finding a REGISTER_PREFIX or ','
+ after the '('. */
+ base_string = op_string + strlen (op_string);
+
+ /* Handle vector operations. */
+ vop_start = strchr (op_string, '{');
+ if (vop_start && vop_start < base_string)
+ {
+ if (check_VecOperations (vop_start, base_string) == NULL)
+ return 0;
+ base_string = vop_start;
+ }
+
+ --base_string;
+ if (is_space_char (*base_string))
+ --base_string;
+
+ /* If we only have a displacement, set-up for it to be parsed later. */
+ displacement_string_start = op_string;
+ displacement_string_end = base_string + 1;
+
+ if (*base_string == ')')
+ {
+ char *temp_string;
+ unsigned int parens_balanced = 1;
+ /* We've already checked that the number of left & right ()'s are
+ equal, so this loop will not be infinite. */
+ do
+ {
+ base_string--;
+ if (*base_string == ')')
+ parens_balanced++;
+ if (*base_string == '(')
+ parens_balanced--;
+ }
+ while (parens_balanced);
+
+ temp_string = base_string;
+
+ /* Skip past '(' and whitespace. */
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+
+ if (*base_string == ','
+ || ((i.base_reg = parse_register (base_string, &end_op))
+ != NULL))
+ {
+ displacement_string_end = temp_string;
+
+ i.types[this_operand].bitfield.baseindex = 1;
+
+ if (i.base_reg)
+ {
+ base_string = end_op;
+ if (is_space_char (*base_string))
+ ++base_string;
+ }
+
+ /* There may be an index reg or scale factor here. */
+ if (*base_string == ',')
+ {
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+
+ if ((i.index_reg = parse_register (base_string, &end_op))
+ != NULL)
+ {
+ base_string = end_op;
+ if (is_space_char (*base_string))
+ ++base_string;
+ if (*base_string == ',')
+ {
+ ++base_string;
+ if (is_space_char (*base_string))
+ ++base_string;
+ }
+ else if (*base_string != ')')
+ {
+ as_bad (_("expecting `,' or `)' "
+ "after index register in `%s'"),
+ operand_string);
+ return 0;
+ }
+ }
+ else if (*base_string == REGISTER_PREFIX)
+ {
+ end_op = strchr (base_string, ',');
+ if (end_op)
+ *end_op = '\0';
+ as_bad (_("bad register name `%s'"), base_string);
+ return 0;
+ }
+
+ /* Check for scale factor. */
+ if (*base_string != ')')
+ {
+ char *end_scale = i386_scale (base_string);
+
+ if (!end_scale)
+ return 0;
+
+ base_string = end_scale;
+ if (is_space_char (*base_string))
+ ++base_string;
+ if (*base_string != ')')
+ {
+ as_bad (_("expecting `)' "
+ "after scale factor in `%s'"),
+ operand_string);
+ return 0;
+ }
+ }
+ else if (!i.index_reg)
+ {
+ as_bad (_("expecting index register or scale factor "
+ "after `,'; got '%c'"),
+ *base_string);
+ return 0;
+ }
+ }
+ else if (*base_string != ')')
+ {
+ as_bad (_("expecting `,' or `)' "
+ "after base register in `%s'"),
+ operand_string);
+ return 0;
+ }
+ }
+ else if (*base_string == REGISTER_PREFIX)
+ {
+ end_op = strchr (base_string, ',');
+ if (end_op)
+ *end_op = '\0';
+ as_bad (_("bad register name `%s'"), base_string);
+ return 0;
+ }
+ }
+
+ /* If there's an expression beginning the operand, parse it,
+ assuming displacement_string_start and
+ displacement_string_end are meaningful. */
+ if (displacement_string_start != displacement_string_end)
+ {
+ if (!i386_displacement (displacement_string_start,
+ displacement_string_end))
+ return 0;
+ }
+
+ /* Special case for (%dx) while doing input/output op. */
+ if (i.base_reg
+ && operand_type_equal (&i.base_reg->reg_type,
+ &reg16_inoutportreg)
+ && i.index_reg == 0
+ && i.log2_scale_factor == 0
+ && i.seg[i.mem_operands] == 0
+ && !operand_type_check (i.types[this_operand], disp))
+ {
+ i.types[this_operand] = inoutportreg;
+ return 1;
+ }
+
+ if (i386_index_check (operand_string) == 0)
+ return 0;
+ i.types[this_operand].bitfield.mem = 1;
+ i.mem_operands++;
+ }
+ else
+ {
+ /* It's not a memory operand; argh! */
+ as_bad (_("invalid char %s beginning operand %d `%s'"),
+ output_invalid (*op_string),
+ this_operand + 1,
+ op_string);
+ return 0;
+ }
+ return 1; /* Normal return. */
+}
+
+/* Calculate the maximum variable size (i.e., excluding fr_fix)
+ that an rs_machine_dependent frag may reach. */
+
+unsigned int
+i386_frag_max_var (fragS *frag)
+{
+ /* The only relaxable frags are for jumps.
+ Unconditional jumps can grow by 4 bytes and others by 5 bytes. */
+ gas_assert (frag->fr_type == rs_machine_dependent);
+ return TYPE_FROM_RELAX_STATE (frag->fr_subtype) == UNCOND_JUMP ? 4 : 5;
+}
+
+/* md_estimate_size_before_relax()
+
+ Called just before relax() for rs_machine_dependent frags. The x86
+ assembler uses these frags to handle variable size jump
+ instructions.
+
+ Any symbol that is now undefined will not become defined.
+ Return the correct fr_subtype in the frag.
+ Return the initial "guess for variable size of frag" to caller.
+ The guess is actually the growth beyond the fixed part. Whatever
+ we do to grow the fixed or variable part contributes to our
+ returned value. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment)
+{
+ /* We've already got fragP->fr_subtype right; all we have to do is
+ check for un-relaxable symbols. On an ELF system, we can't relax
+ an externally visible symbol, because it may be overridden by a
+ shared library. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ || (IS_ELF
+ && (S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol)
+ || ((symbol_get_bfdsym (fragP->fr_symbol)->flags
+ & BSF_GNU_INDIRECT_FUNCTION))))
+#endif
+#if defined (OBJ_COFF) && defined (TE_PE)
+ || (OUTPUT_FLAVOR == bfd_target_coff_flavour
+ && S_IS_WEAK (fragP->fr_symbol))
+#endif
+ )
+ {
+ /* Symbol is undefined in this segment, or we need to keep a
+ reloc so that weak symbols can be overridden. */
+ int size = (fragP->fr_subtype & CODE16) ? 2 : 4;
+ enum bfd_reloc_code_real reloc_type;
+ unsigned char *opcode;
+ int old_fr_fix;
+
+ if (fragP->fr_var != NO_RELOC)
+ reloc_type = (enum bfd_reloc_code_real) fragP->fr_var;
+ else if (size == 2)
+ reloc_type = BFD_RELOC_16_PCREL;
+ else
+ reloc_type = BFD_RELOC_32_PCREL;
+
+ old_fr_fix = fragP->fr_fix;
+ opcode = (unsigned char *) fragP->fr_opcode;
+
+ switch (TYPE_FROM_RELAX_STATE (fragP->fr_subtype))
+ {
+ case UNCOND_JUMP:
+ /* Make jmp (0xeb) a (d)word displacement jump. */
+ opcode[0] = 0xe9;
+ fragP->fr_fix += size;
+ fix_new (fragP, old_fr_fix, size,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ reloc_type);
+ break;
+
+ case COND_JUMP86:
+ if (size == 2
+ && (!no_cond_jump_promotion || fragP->fr_var != NO_RELOC))
+ {
+ /* Negate the condition, and branch past an
+ unconditional jump. */
+ opcode[0] ^= 1;
+ opcode[1] = 3;
+ /* Insert an unconditional jump. */
+ opcode[2] = 0xe9;
+ /* We added two extra opcode bytes, and have a two byte
+ offset. */
+ fragP->fr_fix += 2 + 2;
+ fix_new (fragP, old_fr_fix + 2, 2,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ reloc_type);
+ break;
+ }
+ /* Fall through. */
+
+ case COND_JUMP:
+ if (no_cond_jump_promotion && fragP->fr_var == NO_RELOC)
+ {
+ fixS *fixP;
+
+ fragP->fr_fix += 1;
+ fixP = fix_new (fragP, old_fr_fix, 1,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ BFD_RELOC_8_PCREL);
+ fixP->fx_signed = 1;
+ break;
+ }
+
+ /* This changes the byte-displacement jump 0x7N
+ to the (d)word-displacement jump 0x0f,0x8N. */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ /* We've added an opcode byte. */
+ fragP->fr_fix += 1 + size;
+ fix_new (fragP, old_fr_fix + 1, size,
+ fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ reloc_type);
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ frag_wane (fragP);
+ return fragP->fr_fix - old_fr_fix;
+ }
+
+ /* Guess size depending on current relax state. Initially the relax
+ state will correspond to a short jump and we return 1, because
+ the variable part of the frag (the branch offset) is one byte
+ long. However, we can relax a section more than once and in that
+ case we must either set fr_subtype back to the unrelaxed state,
+ or return the value for the appropriate branch. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* Called after relax() is finished.
+
+ In: Address of frag.
+ fr_type == rs_machine_dependent.
+ fr_subtype is what the address relaxed to.
+
+ Out: Any fixSs and constants are set up.
+ Caller will turn frag into a ".space 0". */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ unsigned char *opcode;
+ unsigned char *where_to_put_displacement = NULL;
+ offsetT target_address;
+ offsetT opcode_address;
+ unsigned int extension = 0;
+ offsetT displacement_from_opcode_start;
+
+ opcode = (unsigned char *) fragP->fr_opcode;
+
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix;
+
+ /* Displacement from opcode start to fill into instruction. */
+ displacement_from_opcode_start = target_address - opcode_address;
+
+ if ((fragP->fr_subtype & BIG) == 0)
+ {
+ /* Don't have to change opcode. */
+ extension = 1; /* 1 opcode + 1 displacement */
+ where_to_put_displacement = &opcode[1];
+ }
+ else
+ {
+ if (no_cond_jump_promotion
+ && TYPE_FROM_RELAX_STATE (fragP->fr_subtype) != UNCOND_JUMP)
+ as_warn_where (fragP->fr_file, fragP->fr_line,
+ _("long jump required"));
+
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG):
+ extension = 4; /* 1 opcode + 4 displacement */
+ opcode[0] = 0xe9;
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16):
+ extension = 2; /* 1 opcode + 2 displacement */
+ opcode[0] = 0xe9;
+ where_to_put_displacement = &opcode[1];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, BIG):
+ case ENCODE_RELAX_STATE (COND_JUMP86, BIG):
+ extension = 5; /* 2 opcode + 4 displacement */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP, BIG16):
+ extension = 3; /* 2 opcode + 2 displacement */
+ opcode[1] = opcode[0] + 0x10;
+ opcode[0] = TWO_BYTE_OPCODE_ESCAPE;
+ where_to_put_displacement = &opcode[2];
+ break;
+
+ case ENCODE_RELAX_STATE (COND_JUMP86, BIG16):
+ extension = 4;
+ opcode[0] ^= 1;
+ opcode[1] = 3;
+ opcode[2] = 0xe9;
+ where_to_put_displacement = &opcode[3];
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ }
+
+ /* If size if less then four we are sure that the operand fits,
+ but if it's 4, then it could be that the displacement is larger
+ then -/+ 2GB. */
+ if (DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype) == 4
+ && object_64bit
+ && ((addressT) (displacement_from_opcode_start - extension
+ + ((addressT) 1 << 31))
+ > (((addressT) 2 << 31) - 1)))
+ {
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("jump target out of range"));
+ /* Make us emit 0. */
+ displacement_from_opcode_start = extension;
+ }
+ /* Now put displacement after opcode. */
+ md_number_to_chars ((char *) where_to_put_displacement,
+ (valueT) (displacement_from_opcode_start - extension),
+ DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+ fragP->fr_fix += extension;
+}
+
+/* Apply a fixup (fixP) to segment data, once it has been determined
+ by our caller that we have all the info we need to fix it up.
+
+ Parameter valP is the pointer to the value of the bits.
+
+ On the 386, immediates, displacements, and data pointers are all in
+ the same (little-endian) format, so we don't need to care about which
+ we are handling. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *p = fixP->fx_where + fixP->fx_frag->fr_literal;
+ valueT value = *valP;
+
+#if !defined (TE_Mach)
+ if (fixP->fx_pcrel)
+ {
+ switch (fixP->fx_r_type)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_64:
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ break;
+ case BFD_RELOC_32:
+ case BFD_RELOC_X86_64_32S:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_8_PCREL;
+ break;
+ }
+ }
+
+ if (fixP->fx_addsy != NULL
+ && (fixP->fx_r_type == BFD_RELOC_32_PCREL
+ || fixP->fx_r_type == BFD_RELOC_64_PCREL
+ || fixP->fx_r_type == BFD_RELOC_16_PCREL
+ || fixP->fx_r_type == BFD_RELOC_8_PCREL)
+ && !use_rela_relocations)
+ {
+ /* This is a hack. There should be a better way to handle this.
+ This covers for the fact that bfd_install_relocation will
+ subtract the current location (for partial_inplace, PC relative
+ relocations); see more below. */
+#ifndef OBJ_AOUT
+ if (IS_ELF
+#ifdef TE_PE
+ || OUTPUT_FLAVOR == bfd_target_coff_flavour
+#endif
+ )
+ value += fixP->fx_where + fixP->fx_frag->fr_address;
+#endif
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (IS_ELF)
+ {
+ segT sym_seg = S_GET_SEGMENT (fixP->fx_addsy);
+
+ if ((sym_seg == seg
+ || (symbol_section_p (fixP->fx_addsy)
+ && sym_seg != absolute_section))
+ && !generic_force_reloc (fixP))
+ {
+ /* Yes, we add the values in twice. This is because
+ bfd_install_relocation subtracts them out again. I think
+ bfd_install_relocation is broken, but I don't dare change
+ it. FIXME. */
+ value += fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+ }
+#endif
+#if defined (OBJ_COFF) && defined (TE_PE)
+ /* For some reason, the PE format does not store a
+ section address offset for a PC relative symbol. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) != seg
+ || S_IS_WEAK (fixP->fx_addsy))
+ value += md_pcrel_from (fixP);
+#endif
+ }
+#if defined (OBJ_COFF) && defined (TE_PE)
+ if (fixP->fx_addsy != NULL
+ && S_IS_WEAK (fixP->fx_addsy)
+ /* PR 16858: Do not modify weak function references. */
+ && ! fixP->fx_pcrel)
+ {
+#if !defined (TE_PEP)
+ /* For x86 PE weak function symbols are neither PC-relative
+ nor do they set S_IS_FUNCTION. So the only reliable way
+ to detect them is to check the flags of their containing
+ section. */
+ if (S_GET_SEGMENT (fixP->fx_addsy) != NULL
+ && S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_CODE)
+ ;
+ else
+#endif
+ value -= S_GET_VALUE (fixP->fx_addsy);
+ }
+#endif
+
+ /* Fix a few things - the dynamic linker expects certain values here,
+ and we must not disappoint it. */
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (IS_ELF && fixP->fx_addsy)
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_386_PLT32:
+ case BFD_RELOC_X86_64_PLT32:
+ /* Make the jump instruction point to the address of the operand. At
+ runtime we merely add the offset to the actual PLT entry. */
+ value = -4;
+ break;
+
+ case BFD_RELOC_386_TLS_GD:
+ case BFD_RELOC_386_TLS_LDM:
+ case BFD_RELOC_386_TLS_IE_32:
+ case BFD_RELOC_386_TLS_IE:
+ case BFD_RELOC_386_TLS_GOTIE:
+ case BFD_RELOC_386_TLS_GOTDESC:
+ case BFD_RELOC_X86_64_TLSGD:
+ case BFD_RELOC_X86_64_TLSLD:
+ case BFD_RELOC_X86_64_GOTTPOFF:
+ case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
+ value = 0; /* Fully resolved at runtime. No addend. */
+ /* Fallthrough */
+ case BFD_RELOC_386_TLS_LE:
+ case BFD_RELOC_386_TLS_LDO_32:
+ case BFD_RELOC_386_TLS_LE_32:
+ case BFD_RELOC_X86_64_DTPOFF32:
+ case BFD_RELOC_X86_64_DTPOFF64:
+ case BFD_RELOC_X86_64_TPOFF32:
+ case BFD_RELOC_X86_64_TPOFF64:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_386_TLS_DESC_CALL:
+ case BFD_RELOC_X86_64_TLSDESC_CALL:
+ value = 0; /* Fully resolved at runtime. No addend. */
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ fixP->fx_done = 0;
+ return;
+
+ case BFD_RELOC_386_GOT32:
+ case BFD_RELOC_X86_64_GOT32:
+ value = 0; /* Fully resolved at runtime. No addend. */
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ default:
+ break;
+ }
+#endif /* defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) */
+ *valP = value;
+#endif /* !defined (TE_Mach) */
+
+ /* Are we finished with this relocation now? */
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+#if defined (OBJ_COFF) && defined (TE_PE)
+ else if (fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy))
+ {
+ fixP->fx_done = 0;
+ /* Remember value for tc_gen_reloc. */
+ fixP->fx_addnumber = value;
+ /* Clear out the frag for now. */
+ value = 0;
+ }
+#endif
+ else if (use_rela_relocations)
+ {
+ fixP->fx_no_overflow = 1;
+ /* Remember value for tc_gen_reloc. */
+ fixP->fx_addnumber = value;
+ value = 0;
+ }
+
+ md_number_to_chars (p, value, fixP->fx_size);
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ /* This outputs the LITTLENUMs in REVERSE order;
+ in accord with the bigendian 386. */
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+static char output_invalid_buf[sizeof (unsigned char) * 2 + 6];
+
+static char *
+output_invalid (int c)
+{
+ if (ISPRINT (c))
+ snprintf (output_invalid_buf, sizeof (output_invalid_buf),
+ "'%c'", c);
+ else
+ snprintf (output_invalid_buf, sizeof (output_invalid_buf),
+ "(0x%x)", (unsigned char) c);
+ return output_invalid_buf;
+}
+
+/* REG_STRING starts *before* REGISTER_PREFIX. */
+
+static const reg_entry *
+parse_real_register (char *reg_string, char **end_op)
+{
+ char *s = reg_string;
+ char *p;
+ char reg_name_given[MAX_REG_NAME_SIZE + 1];
+ const reg_entry *r;
+
+ /* Skip possible REGISTER_PREFIX and possible whitespace. */
+ if (*s == REGISTER_PREFIX)
+ ++s;
+
+ if (is_space_char (*s))
+ ++s;
+
+ p = reg_name_given;
+ while ((*p++ = register_chars[(unsigned char) *s]) != '\0')
+ {
+ if (p >= reg_name_given + MAX_REG_NAME_SIZE)
+ return (const reg_entry *) NULL;
+ s++;
+ }
+
+ /* For naked regs, make sure that we are not dealing with an identifier.
+ This prevents confusing an identifier like `eax_var' with register
+ `eax'. */
+ if (allow_naked_reg && identifier_chars[(unsigned char) *s])
+ return (const reg_entry *) NULL;
+
+ *end_op = s;
+
+ r = (const reg_entry *) hash_find (reg_hash, reg_name_given);
+
+ /* Handle floating point regs, allowing spaces in the (i) part. */
+ if (r == i386_regtab /* %st is first entry of table */)
+ {
+ if (is_space_char (*s))
+ ++s;
+ if (*s == '(')
+ {
+ ++s;
+ if (is_space_char (*s))
+ ++s;
+ if (*s >= '0' && *s <= '7')
+ {
+ int fpr = *s - '0';
+ ++s;
+ if (is_space_char (*s))
+ ++s;
+ if (*s == ')')
+ {
+ *end_op = s + 1;
+ r = (const reg_entry *) hash_find (reg_hash, "st(0)");
+ know (r);
+ return r + fpr;
+ }
+ }
+ /* We have "%st(" then garbage. */
+ return (const reg_entry *) NULL;
+ }
+ }
+
+ if (r == NULL || allow_pseudo_reg)
+ return r;
+
+ if (operand_type_all_zero (&r->reg_type))
+ return (const reg_entry *) NULL;
+
+ if ((r->reg_type.bitfield.reg32
+ || r->reg_type.bitfield.sreg3
+ || r->reg_type.bitfield.control
+ || r->reg_type.bitfield.debug
+ || r->reg_type.bitfield.test)
+ && !cpu_arch_flags.bitfield.cpui386)
+ return (const reg_entry *) NULL;
+
+ if (r->reg_type.bitfield.floatreg
+ && !cpu_arch_flags.bitfield.cpu8087
+ && !cpu_arch_flags.bitfield.cpu287
+ && !cpu_arch_flags.bitfield.cpu387)
+ return (const reg_entry *) NULL;
+
+ if (r->reg_type.bitfield.regmmx && !cpu_arch_flags.bitfield.cpummx)
+ return (const reg_entry *) NULL;
+
+ if (r->reg_type.bitfield.regxmm && !cpu_arch_flags.bitfield.cpusse)
+ return (const reg_entry *) NULL;
+
+ if (r->reg_type.bitfield.regymm && !cpu_arch_flags.bitfield.cpuavx)
+ return (const reg_entry *) NULL;
+
+ if ((r->reg_type.bitfield.regzmm || r->reg_type.bitfield.regmask)
+ && !cpu_arch_flags.bitfield.cpuavx512f)
+ return (const reg_entry *) NULL;
+
+ /* Don't allow fake index register unless allow_index_reg isn't 0. */
+ if (!allow_index_reg
+ && (r->reg_num == RegEiz || r->reg_num == RegRiz))
+ return (const reg_entry *) NULL;
+
+ /* Upper 16 vector register is only available with VREX in 64bit
+ mode. */
+ if ((r->reg_flags & RegVRex))
+ {
+ if (!cpu_arch_flags.bitfield.cpuvrex
+ || flag_code != CODE_64BIT)
+ return (const reg_entry *) NULL;
+
+ i.need_vrex = 1;
+ }
+
+ if (((r->reg_flags & (RegRex64 | RegRex))
+ || r->reg_type.bitfield.reg64)
+ && (!cpu_arch_flags.bitfield.cpulm
+ || !operand_type_equal (&r->reg_type, &control))
+ && flag_code != CODE_64BIT)
+ return (const reg_entry *) NULL;
+
+ if (r->reg_type.bitfield.sreg3 && r->reg_num == RegFlat && !intel_syntax)
+ return (const reg_entry *) NULL;
+
+ return r;
+}
+
+/* REG_STRING starts *before* REGISTER_PREFIX. */
+
+static const reg_entry *
+parse_register (char *reg_string, char **end_op)
+{
+ const reg_entry *r;
+
+ if (*reg_string == REGISTER_PREFIX || allow_naked_reg)
+ r = parse_real_register (reg_string, end_op);
+ else
+ r = NULL;
+ if (!r)
+ {
+ char *save = input_line_pointer;
+ char c;
+ symbolS *symbolP;
+
+ input_line_pointer = reg_string;
+ c = get_symbol_end ();
+ symbolP = symbol_find (reg_string);
+ if (symbolP && S_GET_SEGMENT (symbolP) == reg_section)
+ {
+ const expressionS *e = symbol_get_value_expression (symbolP);
+
+ know (e->X_op == O_register);
+ know (e->X_add_number >= 0
+ && (valueT) e->X_add_number < i386_regtab_size);
+ r = i386_regtab + e->X_add_number;
+ if ((r->reg_flags & RegVRex))
+ i.need_vrex = 1;
+ *end_op = input_line_pointer;
+ }
+ *input_line_pointer = c;
+ input_line_pointer = save;
+ }
+ return r;
+}
+
+int
+i386_parse_name (char *name, expressionS *e, char *nextcharP)
+{
+ const reg_entry *r;
+ char *end = input_line_pointer;
+
+ *end = *nextcharP;
+ r = parse_register (name, &input_line_pointer);
+ if (r && end <= input_line_pointer)
+ {
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = 0;
+ e->X_op = O_register;
+ e->X_add_number = r - i386_regtab;
+ return 1;
+ }
+ input_line_pointer = end;
+ *end = 0;
+ return intel_syntax ? i386_intel_parse_name (name, e) : 0;
+}
+
+void
+md_operand (expressionS *e)
+{
+ char *end;
+ const reg_entry *r;
+
+ switch (*input_line_pointer)
+ {
+ case REGISTER_PREFIX:
+ r = parse_real_register (input_line_pointer, &end);
+ if (r)
+ {
+ e->X_op = O_register;
+ e->X_add_number = r - i386_regtab;
+ input_line_pointer = end;
+ }
+ break;
+
+ case '[':
+ gas_assert (intel_syntax);
+ end = input_line_pointer++;
+ expression (e);
+ if (*input_line_pointer == ']')
+ {
+ ++input_line_pointer;
+ e->X_op_symbol = make_expr_symbol (e);
+ e->X_add_symbol = NULL;
+ e->X_add_number = 0;
+ e->X_op = O_index;
+ }
+ else
+ {
+ e->X_op = O_absent;
+ input_line_pointer = end;
+ }
+ break;
+ }
+}
+
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+const char *md_shortopts = "kVQ:sqn";
+#else
+const char *md_shortopts = "qn";
+#endif
+
+#define OPTION_32 (OPTION_MD_BASE + 0)
+#define OPTION_64 (OPTION_MD_BASE + 1)
+#define OPTION_DIVIDE (OPTION_MD_BASE + 2)
+#define OPTION_MARCH (OPTION_MD_BASE + 3)
+#define OPTION_MTUNE (OPTION_MD_BASE + 4)
+#define OPTION_MMNEMONIC (OPTION_MD_BASE + 5)
+#define OPTION_MSYNTAX (OPTION_MD_BASE + 6)
+#define OPTION_MINDEX_REG (OPTION_MD_BASE + 7)
+#define OPTION_MNAKED_REG (OPTION_MD_BASE + 8)
+#define OPTION_MOLD_GCC (OPTION_MD_BASE + 9)
+#define OPTION_MSSE2AVX (OPTION_MD_BASE + 10)
+#define OPTION_MSSE_CHECK (OPTION_MD_BASE + 11)
+#define OPTION_MOPERAND_CHECK (OPTION_MD_BASE + 12)
+#define OPTION_MAVXSCALAR (OPTION_MD_BASE + 13)
+#define OPTION_X32 (OPTION_MD_BASE + 14)
+#define OPTION_MADD_BND_PREFIX (OPTION_MD_BASE + 15)
+#define OPTION_MEVEXLIG (OPTION_MD_BASE + 16)
+#define OPTION_MEVEXWIG (OPTION_MD_BASE + 17)
+#define OPTION_MBIG_OBJ (OPTION_MD_BASE + 18)
+#define OPTION_OMIT_LOCK_PREFIX (OPTION_MD_BASE + 19)
+#define OPTION_MEVEXRCIG (OPTION_MD_BASE + 20)
+
+struct option md_longopts[] =
+{
+ {"32", no_argument, NULL, OPTION_32},
+#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+ || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O))
+ {"64", no_argument, NULL, OPTION_64},
+#endif
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ {"x32", no_argument, NULL, OPTION_X32},
+#endif
+ {"divide", no_argument, NULL, OPTION_DIVIDE},
+ {"march", required_argument, NULL, OPTION_MARCH},
+ {"mtune", required_argument, NULL, OPTION_MTUNE},
+ {"mmnemonic", required_argument, NULL, OPTION_MMNEMONIC},
+ {"msyntax", required_argument, NULL, OPTION_MSYNTAX},
+ {"mindex-reg", no_argument, NULL, OPTION_MINDEX_REG},
+ {"mnaked-reg", no_argument, NULL, OPTION_MNAKED_REG},
+ {"mold-gcc", no_argument, NULL, OPTION_MOLD_GCC},
+ {"msse2avx", no_argument, NULL, OPTION_MSSE2AVX},
+ {"msse-check", required_argument, NULL, OPTION_MSSE_CHECK},
+ {"moperand-check", required_argument, NULL, OPTION_MOPERAND_CHECK},
+ {"mavxscalar", required_argument, NULL, OPTION_MAVXSCALAR},
+ {"madd-bnd-prefix", no_argument, NULL, OPTION_MADD_BND_PREFIX},
+ {"mevexlig", required_argument, NULL, OPTION_MEVEXLIG},
+ {"mevexwig", required_argument, NULL, OPTION_MEVEXWIG},
+# if defined (TE_PE) || defined (TE_PEP)
+ {"mbig-obj", no_argument, NULL, OPTION_MBIG_OBJ},
+#endif
+ {"momit-lock-prefix", required_argument, NULL, OPTION_OMIT_LOCK_PREFIX},
+ {"mevexrcig", required_argument, NULL, OPTION_MEVEXRCIG},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ unsigned int j;
+ char *arch, *next;
+
+ switch (c)
+ {
+ case 'n':
+ optimize_align_code = 0;
+ break;
+
+ case 'q':
+ quiet_warnings = 1;
+ break;
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -k: Ignore for FreeBSD compatibility. */
+ case 'k':
+ break;
+
+ case 's':
+ /* -s: On i386 Solaris, this tells the native assembler to use
+ .stab instead of .stab.excl. We always use .stab anyhow. */
+ break;
+#endif
+#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+ || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O))
+ case OPTION_64:
+ {
+ const char **list, **l;
+
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+ if (CONST_STRNEQ (*l, "elf64-x86-64")
+ || strcmp (*l, "coff-x86-64") == 0
+ || strcmp (*l, "pe-x86-64") == 0
+ || strcmp (*l, "pei-x86-64") == 0
+ || strcmp (*l, "mach-o-x86-64") == 0)
+ {
+ default_arch = "x86_64";
+ break;
+ }
+ if (*l == NULL)
+ as_fatal (_("no compiled in support for x86_64"));
+ free (list);
+ }
+ break;
+#endif
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ case OPTION_X32:
+ if (IS_ELF)
+ {
+ const char **list, **l;
+
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+ if (CONST_STRNEQ (*l, "elf32-x86-64"))
+ {
+ default_arch = "x86_64:32";
+ break;
+ }
+ if (*l == NULL)
+ as_fatal (_("no compiled in support for 32bit x86_64"));
+ free (list);
+ }
+ else
+ as_fatal (_("32bit x86_64 is only supported for ELF"));
+ break;
+#endif
+
+ case OPTION_32:
+ default_arch = "i386";
+ break;
+
+ case OPTION_DIVIDE:
+#ifdef SVR4_COMMENT_CHARS
+ {
+ char *n, *t;
+ const char *s;
+
+ n = (char *) xmalloc (strlen (i386_comment_chars) + 1);
+ t = n;
+ for (s = i386_comment_chars; *s != '\0'; s++)
+ if (*s != '/')
+ *t++ = *s;
+ *t = '\0';
+ i386_comment_chars = n;
+ }
+#endif
+ break;
+
+ case OPTION_MARCH:
+ arch = xstrdup (arg);
+ do
+ {
+ if (*arch == '.')
+ as_fatal (_("invalid -march= option: `%s'"), arg);
+ next = strchr (arch, '+');
+ if (next)
+ *next++ = '\0';
+ for (j = 0; j < ARRAY_SIZE (cpu_arch); j++)
+ {
+ if (strcmp (arch, cpu_arch [j].name) == 0)
+ {
+ /* Processor. */
+ if (! cpu_arch[j].flags.bitfield.cpui386)
+ continue;
+
+ cpu_arch_name = cpu_arch[j].name;
+ cpu_sub_arch_name = NULL;
+ cpu_arch_flags = cpu_arch[j].flags;
+ cpu_arch_isa = cpu_arch[j].type;
+ cpu_arch_isa_flags = cpu_arch[j].flags;
+ if (!cpu_arch_tune_set)
+ {
+ cpu_arch_tune = cpu_arch_isa;
+ cpu_arch_tune_flags = cpu_arch_isa_flags;
+ }
+ break;
+ }
+ else if (*cpu_arch [j].name == '.'
+ && strcmp (arch, cpu_arch [j].name + 1) == 0)
+ {
+ /* ISA entension. */
+ i386_cpu_flags flags;
+
+ if (!cpu_arch[j].negated)
+ flags = cpu_flags_or (cpu_arch_flags,
+ cpu_arch[j].flags);
+ else
+ flags = cpu_flags_and_not (cpu_arch_flags,
+ cpu_arch[j].flags);
+ if (!cpu_flags_equal (&flags, &cpu_arch_flags))
+ {
+ if (cpu_sub_arch_name)
+ {
+ char *name = cpu_sub_arch_name;
+ cpu_sub_arch_name = concat (name,
+ cpu_arch[j].name,
+ (const char *) NULL);
+ free (name);
+ }
+ else
+ cpu_sub_arch_name = xstrdup (cpu_arch[j].name);
+ cpu_arch_flags = flags;
+ cpu_arch_isa_flags = flags;
+ }
+ break;
+ }
+ }
+
+ if (j >= ARRAY_SIZE (cpu_arch))
+ as_fatal (_("invalid -march= option: `%s'"), arg);
+
+ arch = next;
+ }
+ while (next != NULL );
+ break;
+
+ case OPTION_MTUNE:
+ if (*arg == '.')
+ as_fatal (_("invalid -mtune= option: `%s'"), arg);
+ for (j = 0; j < ARRAY_SIZE (cpu_arch); j++)
+ {
+ if (strcmp (arg, cpu_arch [j].name) == 0)
+ {
+ cpu_arch_tune_set = 1;
+ cpu_arch_tune = cpu_arch [j].type;
+ cpu_arch_tune_flags = cpu_arch[j].flags;
+ break;
+ }
+ }
+ if (j >= ARRAY_SIZE (cpu_arch))
+ as_fatal (_("invalid -mtune= option: `%s'"), arg);
+ break;
+
+ case OPTION_MMNEMONIC:
+ if (strcasecmp (arg, "att") == 0)
+ intel_mnemonic = 0;
+ else if (strcasecmp (arg, "intel") == 0)
+ intel_mnemonic = 1;
+ else
+ as_fatal (_("invalid -mmnemonic= option: `%s'"), arg);
+ break;
+
+ case OPTION_MSYNTAX:
+ if (strcasecmp (arg, "att") == 0)
+ intel_syntax = 0;
+ else if (strcasecmp (arg, "intel") == 0)
+ intel_syntax = 1;
+ else
+ as_fatal (_("invalid -msyntax= option: `%s'"), arg);
+ break;
+
+ case OPTION_MINDEX_REG:
+ allow_index_reg = 1;
+ break;
+
+ case OPTION_MNAKED_REG:
+ allow_naked_reg = 1;
+ break;
+
+ case OPTION_MOLD_GCC:
+ old_gcc = 1;
+ break;
+
+ case OPTION_MSSE2AVX:
+ sse2avx = 1;
+ break;
+
+ case OPTION_MSSE_CHECK:
+ if (strcasecmp (arg, "error") == 0)
+ sse_check = check_error;
+ else if (strcasecmp (arg, "warning") == 0)
+ sse_check = check_warning;
+ else if (strcasecmp (arg, "none") == 0)
+ sse_check = check_none;
+ else
+ as_fatal (_("invalid -msse-check= option: `%s'"), arg);
+ break;
+
+ case OPTION_MOPERAND_CHECK:
+ if (strcasecmp (arg, "error") == 0)
+ operand_check = check_error;
+ else if (strcasecmp (arg, "warning") == 0)
+ operand_check = check_warning;
+ else if (strcasecmp (arg, "none") == 0)
+ operand_check = check_none;
+ else
+ as_fatal (_("invalid -moperand-check= option: `%s'"), arg);
+ break;
+
+ case OPTION_MAVXSCALAR:
+ if (strcasecmp (arg, "128") == 0)
+ avxscalar = vex128;
+ else if (strcasecmp (arg, "256") == 0)
+ avxscalar = vex256;
+ else
+ as_fatal (_("invalid -mavxscalar= option: `%s'"), arg);
+ break;
+
+ case OPTION_MADD_BND_PREFIX:
+ add_bnd_prefix = 1;
+ break;
+
+ case OPTION_MEVEXLIG:
+ if (strcmp (arg, "128") == 0)
+ evexlig = evexl128;
+ else if (strcmp (arg, "256") == 0)
+ evexlig = evexl256;
+ else if (strcmp (arg, "512") == 0)
+ evexlig = evexl512;
+ else
+ as_fatal (_("invalid -mevexlig= option: `%s'"), arg);
+ break;
+
+ case OPTION_MEVEXRCIG:
+ if (strcmp (arg, "rne") == 0)
+ evexrcig = rne;
+ else if (strcmp (arg, "rd") == 0)
+ evexrcig = rd;
+ else if (strcmp (arg, "ru") == 0)
+ evexrcig = ru;
+ else if (strcmp (arg, "rz") == 0)
+ evexrcig = rz;
+ else
+ as_fatal (_("invalid -mevexrcig= option: `%s'"), arg);
+ break;
+
+ case OPTION_MEVEXWIG:
+ if (strcmp (arg, "0") == 0)
+ evexwig = evexw0;
+ else if (strcmp (arg, "1") == 0)
+ evexwig = evexw1;
+ else
+ as_fatal (_("invalid -mevexwig= option: `%s'"), arg);
+ break;
+
+# if defined (TE_PE) || defined (TE_PEP)
+ case OPTION_MBIG_OBJ:
+ use_big_obj = 1;
+ break;
+#endif
+
+ case OPTION_OMIT_LOCK_PREFIX:
+ if (strcasecmp (arg, "yes") == 0)
+ omit_lock_prefix = 1;
+ else if (strcasecmp (arg, "no") == 0)
+ omit_lock_prefix = 0;
+ else
+ as_fatal (_("invalid -momit-lock-prefix= option: `%s'"), arg);
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+#define MESSAGE_TEMPLATE \
+" "
+
+static void
+show_arch (FILE *stream, int ext, int check)
+{
+ static char message[] = MESSAGE_TEMPLATE;
+ char *start = message + 27;
+ char *p;
+ int size = sizeof (MESSAGE_TEMPLATE);
+ int left;
+ const char *name;
+ int len;
+ unsigned int j;
+
+ p = start;
+ left = size - (start - message);
+ for (j = 0; j < ARRAY_SIZE (cpu_arch); j++)
+ {
+ /* Should it be skipped? */
+ if (cpu_arch [j].skip)
+ continue;
+
+ name = cpu_arch [j].name;
+ len = cpu_arch [j].len;
+ if (*name == '.')
+ {
+ /* It is an extension. Skip if we aren't asked to show it. */
+ if (ext)
+ {
+ name++;
+ len--;
+ }
+ else
+ continue;
+ }
+ else if (ext)
+ {
+ /* It is an processor. Skip if we show only extension. */
+ continue;
+ }
+ else if (check && ! cpu_arch[j].flags.bitfield.cpui386)
+ {
+ /* It is an impossible processor - skip. */
+ continue;
+ }
+
+ /* Reserve 2 spaces for ", " or ",\0" */
+ left -= len + 2;
+
+ /* Check if there is any room. */
+ if (left >= 0)
+ {
+ if (p != start)
+ {
+ *p++ = ',';
+ *p++ = ' ';
+ }
+ p = mempcpy (p, name, len);
+ }
+ else
+ {
+ /* Output the current message now and start a new one. */
+ *p++ = ',';
+ *p = '\0';
+ fprintf (stream, "%s\n", message);
+ p = start;
+ left = size - (start - message) - len - 2;
+
+ gas_assert (left >= 0);
+
+ p = mempcpy (p, name, len);
+ }
+ }
+
+ *p = '\0';
+ fprintf (stream, "%s\n", message);
+}
+
+void
+md_show_usage (FILE *stream)
+{
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ fprintf (stream, _("\
+ -Q ignored\n\
+ -V print assembler version number\n\
+ -k ignored\n"));
+#endif
+ fprintf (stream, _("\
+ -n Do not optimize code alignment\n\
+ -q quieten some warnings\n"));
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ fprintf (stream, _("\
+ -s ignored\n"));
+#endif
+#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+ || defined (TE_PE) || defined (TE_PEP))
+ fprintf (stream, _("\
+ --32/--64/--x32 generate 32bit/64bit/x32 code\n"));
+#endif
+#ifdef SVR4_COMMENT_CHARS
+ fprintf (stream, _("\
+ --divide do not treat `/' as a comment character\n"));
+#else
+ fprintf (stream, _("\
+ --divide ignored\n"));
+#endif
+ fprintf (stream, _("\
+ -march=CPU[,+EXTENSION...]\n\
+ generate code for CPU and EXTENSION, CPU is one of:\n"));
+ show_arch (stream, 0, 1);
+ fprintf (stream, _("\
+ EXTENSION is combination of:\n"));
+ show_arch (stream, 1, 0);
+ fprintf (stream, _("\
+ -mtune=CPU optimize for CPU, CPU is one of:\n"));
+ show_arch (stream, 0, 0);
+ fprintf (stream, _("\
+ -msse2avx encode SSE instructions with VEX prefix\n"));
+ fprintf (stream, _("\
+ -msse-check=[none|error|warning]\n\
+ check SSE instructions\n"));
+ fprintf (stream, _("\
+ -moperand-check=[none|error|warning]\n\
+ check operand combinations for validity\n"));
+ fprintf (stream, _("\
+ -mavxscalar=[128|256] encode scalar AVX instructions with specific vector\n\
+ length\n"));
+ fprintf (stream, _("\
+ -mevexlig=[128|256|512] encode scalar EVEX instructions with specific vector\n\
+ length\n"));
+ fprintf (stream, _("\
+ -mevexwig=[0|1] encode EVEX instructions with specific EVEX.W value\n\
+ for EVEX.W bit ignored instructions\n"));
+ fprintf (stream, _("\
+ -mevexrcig=[rne|rd|ru|rz]\n\
+ encode EVEX instructions with specific EVEX.RC value\n\
+ for SAE-only ignored instructions\n"));
+ fprintf (stream, _("\
+ -mmnemonic=[att|intel] use AT&T/Intel mnemonic\n"));
+ fprintf (stream, _("\
+ -msyntax=[att|intel] use AT&T/Intel syntax\n"));
+ fprintf (stream, _("\
+ -mindex-reg support pseudo index registers\n"));
+ fprintf (stream, _("\
+ -mnaked-reg don't require `%%' prefix for registers\n"));
+ fprintf (stream, _("\
+ -mold-gcc support old (<= 2.8.1) versions of gcc\n"));
+ fprintf (stream, _("\
+ -madd-bnd-prefix add BND prefix for all valid branches\n"));
+# if defined (TE_PE) || defined (TE_PEP)
+ fprintf (stream, _("\
+ -mbig-obj generate big object files\n"));
+#endif
+ fprintf (stream, _("\
+ -momit-lock-prefix=[no|yes]\n\
+ strip all lock prefixes\n"));
+}
+
+#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \
+ || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+ || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O))
+
+/* Pick the target format to use. */
+
+const char *
+i386_target_format (void)
+{
+ if (!strncmp (default_arch, "x86_64", 6))
+ {
+ update_code_flag (CODE_64BIT, 1);
+ if (default_arch[6] == '\0')
+ x86_elf_abi = X86_64_ABI;
+ else
+ x86_elf_abi = X86_64_X32_ABI;
+ }
+ else if (!strcmp (default_arch, "i386"))
+ update_code_flag (CODE_32BIT, 1);
+ else
+ as_fatal (_("unknown architecture"));
+
+ if (cpu_flags_all_zero (&cpu_arch_isa_flags))
+ cpu_arch_isa_flags = cpu_arch[flag_code == CODE_64BIT].flags;
+ if (cpu_flags_all_zero (&cpu_arch_tune_flags))
+ cpu_arch_tune_flags = cpu_arch[flag_code == CODE_64BIT].flags;
+
+ switch (OUTPUT_FLAVOR)
+ {
+#if defined (OBJ_MAYBE_AOUT) || defined (OBJ_AOUT)
+ case bfd_target_aout_flavour:
+ return AOUT_TARGET_FORMAT;
+#endif
+#if defined (OBJ_MAYBE_COFF) || defined (OBJ_COFF)
+# if defined (TE_PE) || defined (TE_PEP)
+ case bfd_target_coff_flavour:
+ if (flag_code == CODE_64BIT)
+ return use_big_obj ? "pe-bigobj-x86-64" : "pe-x86-64";
+ else
+ return "pe-i386";
+# elif defined (TE_GO32)
+ case bfd_target_coff_flavour:
+ return "coff-go32";
+# else
+ case bfd_target_coff_flavour:
+ return "coff-i386";
+# endif
+#endif
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+ case bfd_target_elf_flavour:
+ {
+ const char *format;
+
+ switch (x86_elf_abi)
+ {
+ default:
+ format = ELF_TARGET_FORMAT;
+ break;
+ case X86_64_ABI:
+ use_rela_relocations = 1;
+ object_64bit = 1;
+ format = ELF_TARGET_FORMAT64;
+ break;
+ case X86_64_X32_ABI:
+ use_rela_relocations = 1;
+ object_64bit = 1;
+ disallow_64bit_reloc = 1;
+ format = ELF_TARGET_FORMAT32;
+ break;
+ }
+ if (cpu_arch_isa == PROCESSOR_L1OM)
+ {
+ if (x86_elf_abi != X86_64_ABI)
+ as_fatal (_("Intel L1OM is 64bit only"));
+ return ELF_TARGET_L1OM_FORMAT;
+ }
+ if (cpu_arch_isa == PROCESSOR_K1OM)
+ {
+ if (x86_elf_abi != X86_64_ABI)
+ as_fatal (_("Intel K1OM is 64bit only"));
+ return ELF_TARGET_K1OM_FORMAT;
+ }
+ else
+ return format;
+ }
+#endif
+#if defined (OBJ_MACH_O)
+ case bfd_target_mach_o_flavour:
+ if (flag_code == CODE_64BIT)
+ {
+ use_rela_relocations = 1;
+ object_64bit = 1;
+ return "mach-o-x86-64";
+ }
+ else
+ return "mach-o-i386";
+#endif
+ default:
+ abort ();
+ return NULL;
+ }
+}
+
+#endif /* OBJ_MAYBE_ more than one */
+
+#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF))
+void
+i386_elf_emit_arch_note (void)
+{
+ if (IS_ELF && cpu_arch_name != NULL)
+ {
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ Elf_Internal_Note i_note;
+ Elf_External_Note e_note;
+ asection *note_secp;
+ int len;
+
+ /* Create the .note section. */
+ note_secp = subseg_new (".note", 0);
+ bfd_set_section_flags (stdoutput,
+ note_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* Process the arch string. */
+ len = strlen (cpu_arch_name);
+
+ i_note.namesz = len + 1;
+ i_note.descsz = 0;
+ i_note.type = NT_ARCH;
+ p = frag_more (sizeof (e_note.namesz));
+ md_number_to_chars (p, (valueT) i_note.namesz, sizeof (e_note.namesz));
+ p = frag_more (sizeof (e_note.descsz));
+ md_number_to_chars (p, (valueT) i_note.descsz, sizeof (e_note.descsz));
+ p = frag_more (sizeof (e_note.type));
+ md_number_to_chars (p, (valueT) i_note.type, sizeof (e_note.type));
+ p = frag_more (len + 1);
+ strcpy (p, cpu_arch_name);
+
+ frag_align (2, 0, 0);
+
+ subseg_set (seg, subseg);
+ }
+}
+#endif
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ if (name[0] == GLOBAL_OFFSET_TABLE_NAME[0]
+ && name[1] == GLOBAL_OFFSET_TABLE_NAME[1]
+ && name[2] == GLOBAL_OFFSET_TABLE_NAME[2]
+ && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in symbol table"));
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ };
+ return GOT_symbol;
+ }
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))
+ if (OUTPUT_FLAVOR == bfd_target_aout_flavour)
+ {
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+ }
+#endif
+
+ return size;
+}
+
+/* On the i386, PC-relative offsets are relative to the start of the
+ next instruction. That is, the address of the offset, plus its
+ size, since the offset is always the last part of the insn. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+#ifndef I386COFF
+
+static void
+s_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ if (IS_ELF)
+ obj_elf_section_change_hook ();
+#endif
+ temp = get_absolute_expression ();
+ subseg_set (bss_section, (subsegT) temp);
+ demand_empty_rest_of_line ();
+}
+
+#endif
+
+void
+i386_validate_fix (fixS *fixp)
+{
+ if (fixp->fx_subsy && fixp->fx_subsy == GOT_symbol)
+ {
+ if (fixp->fx_r_type == BFD_RELOC_32_PCREL)
+ {
+ if (!object_64bit)
+ abort ();
+ fixp->fx_r_type = BFD_RELOC_X86_64_GOTPCREL;
+ }
+ else
+ {
+ if (!object_64bit)
+ fixp->fx_r_type = BFD_RELOC_386_GOTOFF;
+ else
+ fixp->fx_r_type = BFD_RELOC_X86_64_GOTOFF64;
+ }
+ fixp->fx_subsy = 0;
+ }
+}
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code;
+
+ switch (fixp->fx_r_type)
+ {
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+ case BFD_RELOC_SIZE32:
+ case BFD_RELOC_SIZE64:
+ if (S_IS_DEFINED (fixp->fx_addsy)
+ && !S_IS_EXTERNAL (fixp->fx_addsy))
+ {
+ /* Resolve size relocation against local symbol to size of
+ the symbol plus addend. */
+ valueT value = S_GET_SIZE (fixp->fx_addsy) + fixp->fx_offset;
+ if (fixp->fx_r_type == BFD_RELOC_SIZE32
+ && !fits_in_unsigned_long (value))
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("symbol size computation overflow"));
+ fixp->fx_addsy = NULL;
+ fixp->fx_subsy = NULL;
+ md_apply_fix (fixp, (valueT *) &value, NULL);
+ return NULL;
+ }
+#endif
+
+ case BFD_RELOC_X86_64_PLT32:
+ case BFD_RELOC_X86_64_GOT32:
+ case BFD_RELOC_X86_64_GOTPCREL:
+ case BFD_RELOC_386_PLT32:
+ case BFD_RELOC_386_GOT32:
+ case BFD_RELOC_386_GOTOFF:
+ case BFD_RELOC_386_GOTPC:
+ case BFD_RELOC_386_TLS_GD:
+ case BFD_RELOC_386_TLS_LDM:
+ case BFD_RELOC_386_TLS_LDO_32:
+ case BFD_RELOC_386_TLS_IE_32:
+ case BFD_RELOC_386_TLS_IE:
+ case BFD_RELOC_386_TLS_GOTIE:
+ case BFD_RELOC_386_TLS_LE_32:
+ case BFD_RELOC_386_TLS_LE:
+ case BFD_RELOC_386_TLS_GOTDESC:
+ case BFD_RELOC_386_TLS_DESC_CALL:
+ case BFD_RELOC_X86_64_TLSGD:
+ case BFD_RELOC_X86_64_TLSLD:
+ case BFD_RELOC_X86_64_DTPOFF32:
+ case BFD_RELOC_X86_64_DTPOFF64:
+ case BFD_RELOC_X86_64_GOTTPOFF:
+ case BFD_RELOC_X86_64_TPOFF32:
+ case BFD_RELOC_X86_64_TPOFF64:
+ case BFD_RELOC_X86_64_GOTOFF64:
+ case BFD_RELOC_X86_64_GOTPC32:
+ case BFD_RELOC_X86_64_GOT64:
+ case BFD_RELOC_X86_64_GOTPCREL64:
+ case BFD_RELOC_X86_64_GOTPC64:
+ case BFD_RELOC_X86_64_GOTPLT64:
+ case BFD_RELOC_X86_64_PLTOFF64:
+ case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
+ case BFD_RELOC_X86_64_TLSDESC_CALL:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+#ifdef TE_PE
+ case BFD_RELOC_32_SECREL:
+#endif
+ code = fixp->fx_r_type;
+ break;
+ case BFD_RELOC_X86_64_32S:
+ if (!fixp->fx_pcrel)
+ {
+ /* Don't turn BFD_RELOC_X86_64_32S into BFD_RELOC_32. */
+ code = fixp->fx_r_type;
+ break;
+ }
+ default:
+ if (fixp->fx_pcrel)
+ {
+ switch (fixp->fx_size)
+ {
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("can not do %d byte pc-relative relocation"),
+ fixp->fx_size);
+ code = BFD_RELOC_32_PCREL;
+ break;
+ case 1: code = BFD_RELOC_8_PCREL; break;
+ case 2: code = BFD_RELOC_16_PCREL; break;
+ case 4: code = BFD_RELOC_32_PCREL; break;
+#ifdef BFD64
+ case 8: code = BFD_RELOC_64_PCREL; break;
+#endif
+ }
+ }
+ else
+ {
+ switch (fixp->fx_size)
+ {
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("can not do %d byte relocation"),
+ fixp->fx_size);
+ code = BFD_RELOC_32;
+ break;
+ case 1: code = BFD_RELOC_8; break;
+ case 2: code = BFD_RELOC_16; break;
+ case 4: code = BFD_RELOC_32; break;
+#ifdef BFD64
+ case 8: code = BFD_RELOC_64; break;
+#endif
+ }
+ }
+ break;
+ }
+
+ if ((code == BFD_RELOC_32
+ || code == BFD_RELOC_32_PCREL
+ || code == BFD_RELOC_X86_64_32S)
+ && GOT_symbol
+ && fixp->fx_addsy == GOT_symbol)
+ {
+ if (!object_64bit)
+ code = BFD_RELOC_386_GOTPC;
+ else
+ code = BFD_RELOC_X86_64_GOTPC32;
+ }
+ if ((code == BFD_RELOC_64 || code == BFD_RELOC_64_PCREL)
+ && GOT_symbol
+ && fixp->fx_addsy == GOT_symbol)
+ {
+ code = BFD_RELOC_X86_64_GOTPC64;
+ }
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (!use_rela_relocations)
+ {
+ /* HACK: Since i386 ELF uses Rel instead of Rela, encode the
+ vtable entry to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ rel->address = fixp->fx_offset;
+#if defined (OBJ_COFF) && defined (TE_PE)
+ else if (fixp->fx_addsy && S_IS_WEAK (fixp->fx_addsy))
+ rel->addend = fixp->fx_addnumber - (S_GET_VALUE (fixp->fx_addsy) * 2);
+ else
+#endif
+ rel->addend = 0;
+ }
+ /* Use the rela in 64bit mode. */
+ else
+ {
+ if (disallow_64bit_reloc)
+ switch (code)
+ {
+ case BFD_RELOC_X86_64_DTPOFF64:
+ case BFD_RELOC_X86_64_TPOFF64:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_X86_64_GOTOFF64:
+ case BFD_RELOC_X86_64_GOT64:
+ case BFD_RELOC_X86_64_GOTPCREL64:
+ case BFD_RELOC_X86_64_GOTPC64:
+ case BFD_RELOC_X86_64_GOTPLT64:
+ case BFD_RELOC_X86_64_PLTOFF64:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent relocation type %s in x32 mode"),
+ bfd_get_reloc_code_name (code));
+ break;
+ default:
+ break;
+ }
+
+ if (!fixp->fx_pcrel)
+ rel->addend = fixp->fx_offset;
+ else
+ switch (code)
+ {
+ case BFD_RELOC_X86_64_PLT32:
+ case BFD_RELOC_X86_64_GOT32:
+ case BFD_RELOC_X86_64_GOTPCREL:
+ case BFD_RELOC_X86_64_TLSGD:
+ case BFD_RELOC_X86_64_TLSLD:
+ case BFD_RELOC_X86_64_GOTTPOFF:
+ case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
+ case BFD_RELOC_X86_64_TLSDESC_CALL:
+ rel->addend = fixp->fx_offset - fixp->fx_size;
+ break;
+ default:
+ rel->addend = (section->vma
+ - fixp->fx_size
+ + fixp->fx_addnumber
+ + md_pcrel_from (fixp));
+ break;
+ }
+ }
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (rel->howto != NULL);
+ }
+
+ return rel;
+}
+
+#include "tc-i386-intel.c"
+
+void
+tc_x86_parse_to_dw2regnum (expressionS *exp)
+{
+ int saved_naked_reg;
+ char saved_register_dot;
+
+ saved_naked_reg = allow_naked_reg;
+ allow_naked_reg = 1;
+ saved_register_dot = register_chars['.'];
+ register_chars['.'] = '.';
+ allow_pseudo_reg = 1;
+ expression_and_evaluate (exp);
+ allow_pseudo_reg = 0;
+ register_chars['.'] = saved_register_dot;
+ allow_naked_reg = saved_naked_reg;
+
+ if (exp->X_op == O_register && exp->X_add_number >= 0)
+ {
+ if ((addressT) exp->X_add_number < i386_regtab_size)
+ {
+ exp->X_op = O_constant;
+ exp->X_add_number = i386_regtab[exp->X_add_number]
+ .dw2_regnum[flag_code >> 1];
+ }
+ else
+ exp->X_op = O_illegal;
+ }
+}
+
+void
+tc_x86_frame_initial_instructions (void)
+{
+ static unsigned int sp_regno[2];
+
+ if (!sp_regno[flag_code >> 1])
+ {
+ char *saved_input = input_line_pointer;
+ char sp[][4] = {"esp", "rsp"};
+ expressionS exp;
+
+ input_line_pointer = sp[flag_code >> 1];
+ tc_x86_parse_to_dw2regnum (&exp);
+ gas_assert (exp.X_op == O_constant);
+ sp_regno[flag_code >> 1] = exp.X_add_number;
+ input_line_pointer = saved_input;
+ }
+
+ cfi_add_CFA_def_cfa (sp_regno[flag_code >> 1], -x86_cie_data_alignment);
+ cfi_add_CFA_offset (x86_dwarf2_return_column, x86_cie_data_alignment);
+}
+
+int
+x86_dwarf2_addr_size (void)
+{
+#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)
+ if (x86_elf_abi == X86_64_X32_ABI)
+ return 4;
+#endif
+ return bfd_arch_bits_per_address (stdoutput) / 8;
+}
+
+int
+i386_elf_section_type (const char *str, size_t len)
+{
+ if (flag_code == CODE_64BIT
+ && len == sizeof ("unwind") - 1
+ && strncmp (str, "unwind", 6) == 0)
+ return SHT_X86_64_UNWIND;
+
+ return -1;
+}
+
+#ifdef TE_SOLARIS
+void
+i386_solaris_fix_up_eh_frame (segT sec)
+{
+ if (flag_code == CODE_64BIT)
+ elf_section_type (sec) = SHT_X86_64_UNWIND;
+}
+#endif
+
+#ifdef TE_PE
+void
+tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+ expressionS exp;
+
+ exp.X_op = O_secrel;
+ exp.X_add_symbol = symbol;
+ exp.X_add_number = 0;
+ emit_expr (&exp, size);
+}
+#endif
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+/* For ELF on x86-64, add support for SHF_X86_64_LARGE. */
+
+bfd_vma
+x86_64_section_letter (int letter, char **ptr_msg)
+{
+ if (flag_code == CODE_64BIT)
+ {
+ if (letter == 'l')
+ return SHF_X86_64_LARGE;
+
+ *ptr_msg = _("bad .section directive: want a,l,w,x,M,S,G,T in string");
+ }
+ else
+ *ptr_msg = _("bad .section directive: want a,w,x,M,S,G,T in string");
+ return -1;
+}
+
+bfd_vma
+x86_64_section_word (char *str, size_t len)
+{
+ if (len == 5 && flag_code == CODE_64BIT && CONST_STRNEQ (str, "large"))
+ return SHF_X86_64_LARGE;
+
+ return -1;
+}
+
+static void
+handle_large_common (int small ATTRIBUTE_UNUSED)
+{
+ if (flag_code != CODE_64BIT)
+ {
+ s_comm_internal (0, elf_common_parse);
+ as_warn (_(".largecomm supported only in 64bit mode, producing .comm"));
+ }
+ else
+ {
+ static segT lbss_section;
+ asection *saved_com_section_ptr = elf_com_section_ptr;
+ asection *saved_bss_section = bss_section;
+
+ if (lbss_section == NULL)
+ {
+ flagword applicable;
+ segT seg = now_seg;
+ subsegT subseg = now_subseg;
+
+ /* The .lbss section is for local .largecomm symbols. */
+ lbss_section = subseg_new (".lbss", 0);
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, lbss_section,
+ applicable & SEC_ALLOC);
+ seg_info (lbss_section)->bss = 1;
+
+ subseg_set (seg, subseg);
+ }
+
+ elf_com_section_ptr = &_bfd_elf_large_com_section;
+ bss_section = lbss_section;
+
+ s_comm_internal (0, elf_common_parse);
+
+ elf_com_section_ptr = saved_com_section_ptr;
+ bss_section = saved_bss_section;
+ }
+}
+#endif /* OBJ_ELF || OBJ_MAYBE_ELF */
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
new file mode 100644
index 0000000..8b5c7d7
--- /dev/null
+++ b/gas/config/tc-i386.h
@@ -0,0 +1,341 @@
+/* tc-i386.h -- Header file for tc-i386.c
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_I386
+#define TC_I386 1
+
+#include "opcodes/i386-opc.h"
+
+struct fix;
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_ARCH (i386_arch ())
+#define TARGET_MACH (i386_mach ())
+extern enum bfd_architecture i386_arch (void);
+extern unsigned long i386_mach (void);
+
+#ifdef TE_FreeBSD
+#define AOUT_TARGET_FORMAT "a.out-i386-freebsd"
+#endif
+#ifdef TE_NetBSD
+#define AOUT_TARGET_FORMAT "a.out-i386-netbsd"
+#endif
+#ifdef TE_386BSD
+#define AOUT_TARGET_FORMAT "a.out-i386-bsd"
+#endif
+#ifdef TE_LINUX
+#define AOUT_TARGET_FORMAT "a.out-i386-linux"
+#endif
+#ifdef TE_Mach
+#define AOUT_TARGET_FORMAT "a.out-mach3"
+#endif
+#ifdef TE_DYNIX
+#define AOUT_TARGET_FORMAT "a.out-i386-dynix"
+#endif
+#ifndef AOUT_TARGET_FORMAT
+#define AOUT_TARGET_FORMAT "a.out-i386"
+#endif
+
+#ifdef TE_FreeBSD
+#define ELF_TARGET_FORMAT "elf32-i386-freebsd"
+#define ELF_TARGET_FORMAT64 "elf64-x86-64-freebsd"
+#elif defined (TE_VXWORKS)
+#define ELF_TARGET_FORMAT "elf32-i386-vxworks"
+#elif defined (TE_NACL)
+#define ELF_TARGET_FORMAT "elf32-i386-nacl"
+#define ELF_TARGET_FORMAT32 "elf32-x86-64-nacl"
+#define ELF_TARGET_FORMAT64 "elf64-x86-64-nacl"
+#endif
+
+#ifdef TE_SOLARIS
+#define ELF_TARGET_FORMAT "elf32-i386-sol2"
+#define ELF_TARGET_FORMAT64 "elf64-x86-64-sol2"
+#endif
+
+#ifndef ELF_TARGET_FORMAT
+#define ELF_TARGET_FORMAT "elf32-i386"
+#endif
+
+#ifndef ELF_TARGET_FORMAT64
+#define ELF_TARGET_FORMAT64 "elf64-x86-64"
+#endif
+
+#ifndef ELF_TARGET_FORMAT32
+#define ELF_TARGET_FORMAT32 "elf32-x86-64"
+#endif
+
+#ifndef ELF_TARGET_L1OM_FORMAT
+#define ELF_TARGET_L1OM_FORMAT "elf64-l1om"
+#endif
+
+#ifndef ELF_TARGET_K1OM_FORMAT
+#define ELF_TARGET_K1OM_FORMAT "elf64-k1om"
+#endif
+
+#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \
+ || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \
+ || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O))
+extern const char *i386_target_format (void);
+#define TARGET_FORMAT i386_target_format ()
+#else
+#ifdef TE_GO32
+#define TARGET_FORMAT "coff-go32"
+#endif
+#ifdef OBJ_AOUT
+#define TARGET_FORMAT AOUT_TARGET_FORMAT
+#endif
+#endif
+
+#if (defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF))
+#define md_end i386_elf_emit_arch_note
+extern void i386_elf_emit_arch_note (void);
+#endif
+
+#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
+
+/* '$' may be used as immediate prefix. */
+#undef LOCAL_LABELS_DOLLAR
+#define LOCAL_LABELS_DOLLAR 0
+#undef LOCAL_LABELS_FB
+#define LOCAL_LABELS_FB 1
+
+extern const char extra_symbol_chars[];
+#define tc_symbol_chars extra_symbol_chars
+
+extern const char *i386_comment_chars;
+#define tc_comment_chars i386_comment_chars
+
+/* The name of the global offset table generated by the compiler. Allow
+ this to be overridden if need be. */
+#ifndef GLOBAL_OFFSET_TABLE_NAME
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+#endif
+
+#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && !defined (LEX_AT)
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) x86_cons (EXP, NBYTES)
+#endif
+extern bfd_reloc_code_real_type x86_cons (expressionS *, int);
+
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP, RELOC) \
+ x86_cons_fix_new(FRAG, OFF, LEN, EXP, RELOC)
+extern void x86_cons_fix_new
+(fragS *, unsigned int, unsigned int, expressionS *, bfd_reloc_code_real_type);
+
+#define TC_ADDRESS_BYTES x86_address_bytes
+extern int x86_address_bytes (void);
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+#define NO_RELOC BFD_RELOC_NONE
+
+void i386_validate_fix (struct fix *);
+#define TC_VALIDATE_FIX(FIX,SEGTYPE,SKIP) i386_validate_fix(FIX)
+
+#define tc_fix_adjustable(X) tc_i386_fix_adjustable(X)
+extern int tc_i386_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* ELF wants external syms kept, as does PE COFF. */
+#if defined (TE_PE) && defined (STRICT_PE_FORMAT)
+#define EXTERN_FORCE_RELOC \
+ (OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ || OUTPUT_FLAVOR == bfd_target_coff_flavour)
+#else
+#define EXTERN_FORCE_RELOC \
+ (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+#endif
+
+/* This expression evaluates to true if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ False if we are willing to perform this relocation while building
+ the .o file. GOTOFF and GOT32 do not need to be checked here because
+ they are not pcrel. .*/
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_386_PLT32 \
+ || (FIX)->fx_r_type == BFD_RELOC_386_GOTPC \
+ || (FIX)->fx_r_type == BFD_RELOC_X86_64_GOTPCREL \
+ || TC_FORCE_RELOCATION (FIX))
+
+extern int i386_parse_name (char *, expressionS *, char *);
+#define md_parse_name(s, e, m, c) i386_parse_name (s, e, c)
+
+extern operatorT i386_operator (const char *name, unsigned int operands, char *);
+#define md_operator i386_operator
+
+extern int i386_need_index_operator (void);
+#define md_need_index_operator i386_need_index_operator
+
+#define md_register_arithmetic 0
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+extern int optimize_align_code;
+
+#define md_do_align(n, fill, len, max, around) \
+if ((n) \
+ && !need_pass_2 \
+ && optimize_align_code \
+ && (!(fill) \
+ || ((char)*(fill) == (char)0x90 && (len) == 1)) \
+ && subseg_text_p (now_seg)) \
+ { \
+ frag_align_code ((n), (max)); \
+ goto around; \
+ }
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE 31
+
+extern void i386_align_code (fragS *, int);
+
+#define HANDLE_ALIGN(fragP) \
+if (fragP->fr_type == rs_align_code) \
+ i386_align_code (fragP, (fragP->fr_next->fr_address \
+ - fragP->fr_address \
+ - fragP->fr_fix));
+
+void i386_print_statistics (FILE *);
+#define tc_print_statistics i386_print_statistics
+
+extern unsigned int i386_frag_max_var (fragS *);
+#define md_frag_max_var i386_frag_max_var
+
+#define md_number_to_chars number_to_chars_littleendian
+
+enum processor_type
+{
+ PROCESSOR_UNKNOWN,
+ PROCESSOR_I386,
+ PROCESSOR_I486,
+ PROCESSOR_PENTIUM,
+ PROCESSOR_PENTIUMPRO,
+ PROCESSOR_PENTIUM4,
+ PROCESSOR_NOCONA,
+ PROCESSOR_CORE,
+ PROCESSOR_CORE2,
+ PROCESSOR_COREI7,
+ PROCESSOR_L1OM,
+ PROCESSOR_K1OM,
+ PROCESSOR_K6,
+ PROCESSOR_ATHLON,
+ PROCESSOR_K8,
+ PROCESSOR_GENERIC32,
+ PROCESSOR_GENERIC64,
+ PROCESSOR_AMDFAM10,
+ PROCESSOR_BD,
+ PROCESSOR_BT
+};
+
+extern enum processor_type cpu_arch_tune;
+extern enum processor_type cpu_arch_isa;
+extern i386_cpu_flags cpu_arch_isa_flags;
+
+struct i386_tc_frag_data
+{
+ enum processor_type isa;
+ i386_cpu_flags isa_flags;
+ enum processor_type tune;
+};
+
+/* We need to emit the right NOP pattern in .align frags. This is
+ done after the text-to-bits assembly pass, so we need to mark it with
+ the isa/tune settings at the time the .align was assembled. */
+#define TC_FRAG_TYPE struct i386_tc_frag_data
+
+#define TC_FRAG_INIT(FRAGP) \
+ do \
+ { \
+ (FRAGP)->tc_frag_data.isa = cpu_arch_isa; \
+ (FRAGP)->tc_frag_data.isa_flags = cpu_arch_isa_flags; \
+ (FRAGP)->tc_frag_data.tune = cpu_arch_tune; \
+ } \
+ while (0)
+
+#ifdef SCO_ELF
+#define tc_init_after_args() sco_id ()
+extern void sco_id (void);
+#endif
+
+#define WORKING_DOT_WORD 1
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+extern unsigned int x86_dwarf2_return_column;
+#define DWARF2_DEFAULT_RETURN_COLUMN x86_dwarf2_return_column
+
+extern int x86_cie_data_alignment;
+#define DWARF2_CIE_DATA_ALIGNMENT x86_cie_data_alignment
+
+extern int x86_dwarf2_addr_size (void);
+#define DWARF2_ADDR_SIZE(bfd) x86_dwarf2_addr_size ()
+
+#define tc_parse_to_dw2regnum tc_x86_parse_to_dw2regnum
+extern void tc_x86_parse_to_dw2regnum (expressionS *);
+
+#define tc_cfi_frame_initial_instructions tc_x86_frame_initial_instructions
+extern void tc_x86_frame_initial_instructions (void);
+
+#define md_elf_section_type(str,len) i386_elf_section_type (str, len)
+extern int i386_elf_section_type (const char *, size_t);
+
+#ifdef TE_SOLARIS
+#define md_fix_up_eh_frame(sec) i386_solaris_fix_up_eh_frame (sec)
+extern void i386_solaris_fix_up_eh_frame (segT);
+#endif
+
+/* Support for SHF_X86_64_LARGE */
+extern bfd_vma x86_64_section_word (char *, size_t);
+extern bfd_vma x86_64_section_letter (int, char **);
+#define md_elf_section_letter(LETTER, PTR_MSG) x86_64_section_letter (LETTER, PTR_MSG)
+#define md_elf_section_word(STR, LEN) x86_64_section_word (STR, LEN)
+
+#ifdef TE_PE
+
+#define O_secrel O_md1
+
+#define TC_DWARF2_EMIT_OFFSET tc_pe_dwarf2_emit_offset
+void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int);
+
+#endif /* TE_PE */
+
+/* X_add_symbol:X_op_symbol (Intel mode only) */
+#define O_full_ptr O_md2
+
+#ifdef OBJ_MACH_O
+
+#define TC_FORCE_RELOCATION(FIX) (obj_mach_o_force_reloc (FIX))
+
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX,SEG) \
+ (obj_mach_o_force_reloc_sub_same (FIX, SEG))
+
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX,SEG) \
+ (obj_mach_o_force_reloc_sub_local (FIX, SEG))
+
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1
+
+#endif /* OBJ_MACH_O */
+
+#endif /* TC_I386 */
diff --git a/gas/config/tc-i860.c b/gas/config/tc-i860.c
new file mode 100644
index 0000000..1b55b80
--- /dev/null
+++ b/gas/config/tc-i860.c
@@ -0,0 +1,1491 @@
+/* tc-i860.c -- Assembler for the Intel i860 architecture.
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ Brought back from the dead and completely reworked
+ by Jason Eckhardt <jle@cygnus.com>.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/i860.h"
+#include "elf/i860.h"
+
+
+/* The opcode hash table. */
+static struct hash_control *op_hash = NULL;
+
+/* These characters always start a comment. */
+const char comment_chars[] = "#!/";
+
+/* These characters start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#/";
+
+const char line_separator_chars[] = ";";
+
+/* Characters that can be used to separate the mantissa from the exponent
+ in floating point numbers. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters that indicate this number is a floating point constant.
+ As in 0f12.456 or 0d1.2345e12. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Register prefix (depends on syntax). */
+static char reg_prefix;
+
+#define MAX_FIXUPS 2
+
+struct i860_it
+{
+ char *error;
+ unsigned long opcode;
+ enum expand_type expand;
+ struct i860_fi
+ {
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+ int pcrel;
+ valueT fup;
+ } fi[MAX_FIXUPS];
+} the_insn;
+
+/* The current fixup count. */
+static int fc;
+
+static char *expr_end;
+
+/* Indicates error if a pseudo operation was expanded after a branch. */
+static char last_expand;
+
+/* If true, then warn if any pseudo operations were expanded. */
+static int target_warn_expand = 0;
+
+/* If true, then XP support is enabled. */
+static int target_xp = 0;
+
+/* If true, then Intel syntax is enabled (default to AT&T/SVR4 syntax). */
+static int target_intel_syntax = 0;
+
+
+/* Prototypes. */
+static void i860_process_insn (char *);
+static void s_dual (int);
+static void s_enddual (int);
+static void s_atmp (int);
+static void s_align_wrapper (int);
+static int i860_get_expression (char *);
+static bfd_reloc_code_real_type obtain_reloc_for_imm16 (fixS *, long *);
+#ifdef DEBUG_I860
+static void print_insn (struct i860_it *);
+#endif
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_wrapper, 0},
+ {"dual", s_dual, 0},
+ {"enddual", s_enddual, 0},
+ {"atmp", s_atmp, 0},
+ {NULL, 0, 0},
+};
+
+/* Dual-instruction mode handling. */
+enum dual
+{
+ DUAL_OFF = 0, DUAL_ON, DUAL_DDOT, DUAL_ONDDOT,
+};
+static enum dual dual_mode = DUAL_OFF;
+
+/* Handle ".dual" directive. */
+static void
+s_dual (int ignore ATTRIBUTE_UNUSED)
+{
+ if (target_intel_syntax)
+ dual_mode = DUAL_ON;
+ else
+ as_bad (_("Directive .dual available only with -mintel-syntax option"));
+}
+
+/* Handle ".enddual" directive. */
+static void
+s_enddual (int ignore ATTRIBUTE_UNUSED)
+{
+ if (target_intel_syntax)
+ dual_mode = DUAL_OFF;
+ else
+ as_bad (_("Directive .enddual available only with -mintel-syntax option"));
+}
+
+/* Temporary register used when expanding assembler pseudo operations. */
+static int atmp = 31;
+
+static void
+s_atmp (int ignore ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+ if (! target_intel_syntax)
+ {
+ as_bad (_("Directive .atmp available only with -mintel-syntax option"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (strncmp (input_line_pointer, "sp", 2) == 0)
+ {
+ input_line_pointer += 2;
+ atmp = 2;
+ }
+ else if (strncmp (input_line_pointer, "fp", 2) == 0)
+ {
+ input_line_pointer += 2;
+ atmp = 3;
+ }
+ else if (strncmp (input_line_pointer, "r", 1) == 0)
+ {
+ input_line_pointer += 1;
+ temp = get_absolute_expression ();
+ if (temp >= 0 && temp <= 31)
+ atmp = temp;
+ else
+ as_bad (_("Unknown temporary pseudo register"));
+ }
+ else
+ {
+ as_bad (_("Unknown temporary pseudo register"));
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle ".align" directive depending on syntax mode.
+ AT&T/SVR4 syntax uses the standard align directive. However,
+ the Intel syntax additionally allows keywords for the alignment
+ parameter: ".align type", where type is one of {.short, .long,
+ .quad, .single, .double} representing alignments of 2, 4,
+ 16, 4, and 8, respectively. */
+static void
+s_align_wrapper (int arg)
+{
+ char *parm = input_line_pointer;
+
+ if (target_intel_syntax)
+ {
+ /* Replace a keyword with the equivalent integer so the
+ standard align routine can parse the directive. */
+ if (strncmp (parm, ".short", 6) == 0)
+ strncpy (parm, " 2", 6);
+ else if (strncmp (parm, ".long", 5) == 0)
+ strncpy (parm, " 4", 5);
+ else if (strncmp (parm, ".quad", 5) == 0)
+ strncpy (parm, " 16", 5);
+ else if (strncmp (parm, ".single", 7) == 0)
+ strncpy (parm, " 4", 7);
+ else if (strncmp (parm, ".double", 7) == 0)
+ strncpy (parm, " 8", 7);
+
+ while (*input_line_pointer == ' ')
+ ++input_line_pointer;
+ }
+
+ s_align_bytes (arg);
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables and data structures that the MD part of the
+ assembler will need. */
+void
+md_begin (void)
+{
+ const char *retval = NULL;
+ int lose = 0;
+ unsigned int i = 0;
+
+ op_hash = hash_new ();
+
+ while (i860_opcodes[i].name != NULL)
+ {
+ const char *name = i860_opcodes[i].name;
+ retval = hash_insert (op_hash, name, (void *) &i860_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ i860_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if (i860_opcodes[i].match & i860_opcodes[i].lose)
+ {
+ fprintf (stderr,
+ _("internal error: losing opcode: `%s' \"%s\"\n"),
+ i860_opcodes[i].name, i860_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ }
+ while (i860_opcodes[i].name != NULL
+ && strcmp (i860_opcodes[i].name, name) == 0);
+ }
+
+ if (lose)
+ as_fatal (_("Defective assembler. No assembly attempted."));
+
+ /* Set the register prefix for either Intel or AT&T/SVR4 syntax. */
+ reg_prefix = target_intel_syntax ? 0 : '%';
+}
+
+/* This is the core of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function emits the frags/bytes
+ it assembles to. */
+void
+md_assemble (char *str)
+{
+ char *destp;
+ int num_opcodes = 1;
+ int i;
+ struct i860_it pseudo[3];
+
+ gas_assert (str);
+ fc = 0;
+
+ /* Assemble the instruction. */
+ i860_process_insn (str);
+
+ /* Check for expandable flag to produce pseudo-instructions. This
+ is an undesirable feature that should be avoided. */
+ if (the_insn.expand != 0 && the_insn.expand != XP_ONLY
+ && ! (the_insn.fi[0].fup & (OP_SEL_HA | OP_SEL_H | OP_SEL_L | OP_SEL_GOT
+ | OP_SEL_GOTOFF | OP_SEL_PLT)))
+ {
+ for (i = 0; i < 3; i++)
+ pseudo[i] = the_insn;
+
+ fc = 1;
+ switch (the_insn.expand)
+ {
+
+ case E_DELAY:
+ num_opcodes = 1;
+ break;
+
+ case E_MOV:
+ if (the_insn.fi[0].exp.X_add_symbol == NULL
+ && the_insn.fi[0].exp.X_op_symbol == NULL
+ && (the_insn.fi[0].exp.X_add_number < (1 << 15)
+ && the_insn.fi[0].exp.X_add_number >= -(1 << 15)))
+ break;
+
+ /* Emit "or l%const,r0,ireg_dest". */
+ pseudo[0].opcode = (the_insn.opcode & 0x001f0000) | 0xe4000000;
+ pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_L);
+
+ /* Emit "orh h%const,ireg_dest,ireg_dest". */
+ pseudo[1].opcode = (the_insn.opcode & 0x03ffffff) | 0xec000000
+ | ((the_insn.opcode & 0x001f0000) << 5);
+ pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_H);
+
+ num_opcodes = 2;
+ break;
+
+ case E_ADDR:
+ if (the_insn.fi[0].exp.X_add_symbol == NULL
+ && the_insn.fi[0].exp.X_op_symbol == NULL
+ && (the_insn.fi[0].exp.X_add_number < (1 << 15)
+ && the_insn.fi[0].exp.X_add_number >= -(1 << 15)))
+ break;
+
+ /* Emit "orh ha%addr_expr,ireg_src2,r31". */
+ pseudo[0].opcode = 0xec000000 | (the_insn.opcode & 0x03e00000)
+ | (atmp << 16);
+ pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_HA);
+
+ /* Emit "l%addr_expr(r31),ireg_dest". We pick up the fixup
+ information from the original instruction. */
+ pseudo[1].opcode = (the_insn.opcode & ~0x03e00000) | (atmp << 21);
+ pseudo[1].fi[0].fup = the_insn.fi[0].fup | OP_SEL_L;
+
+ num_opcodes = 2;
+ break;
+
+ case E_U32:
+ if (the_insn.fi[0].exp.X_add_symbol == NULL
+ && the_insn.fi[0].exp.X_op_symbol == NULL
+ && (the_insn.fi[0].exp.X_add_number < (1 << 16)
+ && the_insn.fi[0].exp.X_add_number >= 0))
+ break;
+
+ /* Emit "$(opcode)h h%const,ireg_src2,r31". */
+ pseudo[0].opcode = (the_insn.opcode & 0xf3e0ffff) | 0x0c000000
+ | (atmp << 16);
+ pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_H);
+
+ /* Emit "$(opcode) l%const,r31,ireg_dest". */
+ pseudo[1].opcode = (the_insn.opcode & 0xf01f0000) | 0x04000000
+ | (atmp << 21);
+ pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_L);
+
+ num_opcodes = 2;
+ break;
+
+ case E_AND:
+ if (the_insn.fi[0].exp.X_add_symbol == NULL
+ && the_insn.fi[0].exp.X_op_symbol == NULL
+ && (the_insn.fi[0].exp.X_add_number < (1 << 16)
+ && the_insn.fi[0].exp.X_add_number >= 0))
+ break;
+
+ /* Emit "andnot h%const,ireg_src2,r31". */
+ pseudo[0].opcode = (the_insn.opcode & 0x03e0ffff) | 0xd4000000
+ | (atmp << 16);
+ pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_H);
+ pseudo[0].fi[0].exp.X_add_number =
+ -1 - the_insn.fi[0].exp.X_add_number;
+
+ /* Emit "andnot l%const,r31,ireg_dest". */
+ pseudo[1].opcode = (the_insn.opcode & 0x001f0000) | 0xd4000000
+ | (atmp << 21);
+ pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_L);
+ pseudo[1].fi[0].exp.X_add_number =
+ -1 - the_insn.fi[0].exp.X_add_number;
+
+ num_opcodes = 2;
+ break;
+
+ case E_S32:
+ if (the_insn.fi[0].exp.X_add_symbol == NULL
+ && the_insn.fi[0].exp.X_op_symbol == NULL
+ && (the_insn.fi[0].exp.X_add_number < (1 << 15)
+ && the_insn.fi[0].exp.X_add_number >= -(1 << 15)))
+ break;
+
+ /* Emit "orh h%const,r0,r31". */
+ pseudo[0].opcode = 0xec000000 | (atmp << 16);
+ pseudo[0].fi[0].fup = (OP_IMM_S16 | OP_SEL_H);
+
+ /* Emit "or l%const,r31,r31". */
+ pseudo[1].opcode = 0xe4000000 | (atmp << 21) | (atmp << 16);
+ pseudo[1].fi[0].fup = (OP_IMM_S16 | OP_SEL_L);
+
+ /* Emit "r31,ireg_src2,ireg_dest". */
+ pseudo[2].opcode = (the_insn.opcode & ~0x0400ffff) | (atmp << 11);
+ pseudo[2].fi[0].fup = OP_IMM_S16;
+
+ num_opcodes = 3;
+ break;
+
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+
+ the_insn = pseudo[0];
+
+ /* Warn if an opcode is expanded after a delayed branch. */
+ if (num_opcodes > 1 && last_expand == 1)
+ as_warn (_("Expanded opcode after delayed branch: `%s'"), str);
+
+ /* Warn if an opcode is expanded in dual mode. */
+ if (num_opcodes > 1 && dual_mode != DUAL_OFF)
+ as_warn (_("Expanded opcode in dual mode: `%s'"), str);
+
+ /* Notify if any expansions happen. */
+ if (target_warn_expand && num_opcodes > 1)
+ as_warn (_("An instruction was expanded (%s)"), str);
+ }
+
+ dwarf2_emit_insn (0);
+ i = 0;
+ do
+ {
+ int tmp;
+
+ /* Output the opcode. Note that the i860 always reads instructions
+ as little-endian data. */
+ destp = frag_more (4);
+ number_to_chars_littleendian (destp, the_insn.opcode, 4);
+
+ /* Check for expanded opcode after branch or in dual mode. */
+ last_expand = the_insn.fi[0].pcrel;
+
+ /* Output the symbol-dependent stuff. Only btne and bte will ever
+ loop more than once here, since only they (possibly) have more
+ than one fixup. */
+ for (tmp = 0; tmp < fc; tmp++)
+ {
+ if (the_insn.fi[tmp].fup != OP_NONE)
+ {
+ fixS *fix;
+ fix = fix_new_exp (frag_now,
+ destp - frag_now->fr_literal,
+ 4,
+ &the_insn.fi[tmp].exp,
+ the_insn.fi[tmp].pcrel,
+ the_insn.fi[tmp].reloc);
+
+ /* Despite the odd name, this is a scratch field. We use
+ it to encode operand type information. */
+ fix->fx_addnumber = the_insn.fi[tmp].fup;
+ }
+ }
+ the_insn = pseudo[++i];
+ }
+ while (--num_opcodes > 0);
+
+}
+
+/* Assemble the instruction pointed to by STR. */
+static void
+i860_process_insn (char *str)
+{
+ char *s;
+ const char *args;
+ char c;
+ struct i860_opcode *insn;
+ char *args_start;
+ unsigned long opcode;
+ unsigned int mask;
+ int match = 0;
+ int comma = 0;
+
+#if 1 /* For compiler warnings. */
+ args = 0;
+ insn = 0;
+ args_start = 0;
+ opcode = 0;
+#endif
+
+ for (s = str; ISLOWER (*s) || *s == '.' || *s == '3'
+ || *s == '2' || *s == '1'; ++s)
+ ;
+
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+
+ /*FALLTHROUGH*/
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_fatal (_("Unknown opcode: `%s'"), str);
+ }
+
+ /* Check for dual mode ("d.") opcode prefix. */
+ if (strncmp (str, "d.", 2) == 0)
+ {
+ if (dual_mode == DUAL_ON)
+ dual_mode = DUAL_ONDDOT;
+ else
+ dual_mode = DUAL_DDOT;
+ str += 2;
+ }
+
+ if ((insn = (struct i860_opcode *) hash_find (op_hash, str)) == NULL)
+ {
+ if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT)
+ str -= 2;
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return;
+ }
+
+ if (comma)
+ *--s = ',';
+
+ args_start = s;
+ for (;;)
+ {
+ int t;
+ opcode = insn->match;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ fc = 0;
+ for (t = 0; t < MAX_FIXUPS; t++)
+ {
+ the_insn.fi[t].reloc = BFD_RELOC_NONE;
+ the_insn.fi[t].pcrel = 0;
+ the_insn.fi[t].fup = OP_NONE;
+ }
+
+ /* Build the opcode, checking as we go that the operands match. */
+ for (args = insn->args; ; ++args)
+ {
+ if (fc > MAX_FIXUPS)
+ abort ();
+
+ switch (*args)
+ {
+
+ /* End of args. */
+ case '\0':
+ if (*s == '\0')
+ match = 1;
+ break;
+
+ /* These must match exactly. */
+ case '+':
+ case '(':
+ case ')':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ /* Must be at least one digit. */
+ case '#':
+ if (ISDIGIT (*s++))
+ {
+ while (ISDIGIT (*s))
+ ++s;
+ continue;
+ }
+ break;
+
+ /* Next operand must be a register. */
+ case '1':
+ case '2':
+ case 'd':
+ /* Check for register prefix if necessary. */
+ if (reg_prefix && *s != reg_prefix)
+ goto error;
+ else if (reg_prefix)
+ s++;
+
+ switch (*s)
+ {
+ /* Frame pointer. */
+ case 'f':
+ s++;
+ if (*s++ == 'p')
+ {
+ mask = 0x3;
+ break;
+ }
+ goto error;
+
+ /* Stack pointer. */
+ case 's':
+ s++;
+ if (*s++ == 'p')
+ {
+ mask = 0x2;
+ break;
+ }
+ goto error;
+
+ /* Any register r0..r31. */
+ case 'r':
+ s++;
+ if (!ISDIGIT (c = *s++))
+ {
+ goto error;
+ }
+ if (ISDIGIT (*s))
+ {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
+ goto error;
+ }
+ else
+ c -= '0';
+ mask = c;
+ break;
+
+ /* Not this opcode. */
+ default:
+ goto error;
+ }
+
+ /* Obtained the register, now place it in the opcode. */
+ switch (*args)
+ {
+ case '1':
+ opcode |= mask << 11;
+ continue;
+
+ case '2':
+ opcode |= mask << 21;
+ continue;
+
+ case 'd':
+ opcode |= mask << 16;
+ continue;
+
+ }
+ break;
+
+ /* Next operand is a floating point register. */
+ case 'e':
+ case 'f':
+ case 'g':
+ /* Check for register prefix if necessary. */
+ if (reg_prefix && *s != reg_prefix)
+ goto error;
+ else if (reg_prefix)
+ s++;
+
+ if (*s++ == 'f' && ISDIGIT (*s))
+ {
+ mask = *s++;
+ if (ISDIGIT (*s))
+ {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32)
+ {
+ break;
+ }
+ }
+ else
+ mask -= '0';
+
+ switch (*args)
+ {
+
+ case 'e':
+ opcode |= mask << 11;
+ continue;
+
+ case 'f':
+ opcode |= mask << 21;
+ continue;
+
+ case 'g':
+ opcode |= mask << 16;
+ if ((opcode & (1 << 10)) && mask != 0
+ && (mask == ((opcode >> 11) & 0x1f)))
+ as_warn (_("Pipelined instruction: fsrc1 = fdest"));
+ continue;
+ }
+ }
+ break;
+
+ /* Next operand must be a control register. */
+ case 'c':
+ /* Check for register prefix if necessary. */
+ if (reg_prefix && *s != reg_prefix)
+ goto error;
+ else if (reg_prefix)
+ s++;
+
+ if (strncmp (s, "fir", 3) == 0)
+ {
+ opcode |= 0x0 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp (s, "psr", 3) == 0)
+ {
+ opcode |= 0x1 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp (s, "dirbase", 7) == 0)
+ {
+ opcode |= 0x2 << 21;
+ s += 7;
+ continue;
+ }
+ if (strncmp (s, "db", 2) == 0)
+ {
+ opcode |= 0x3 << 21;
+ s += 2;
+ continue;
+ }
+ if (strncmp (s, "fsr", 3) == 0)
+ {
+ opcode |= 0x4 << 21;
+ s += 3;
+ continue;
+ }
+ if (strncmp (s, "epsr", 4) == 0)
+ {
+ opcode |= 0x5 << 21;
+ s += 4;
+ continue;
+ }
+ /* The remaining control registers are XP only. */
+ if (target_xp && strncmp (s, "bear", 4) == 0)
+ {
+ opcode |= 0x6 << 21;
+ s += 4;
+ continue;
+ }
+ if (target_xp && strncmp (s, "ccr", 3) == 0)
+ {
+ opcode |= 0x7 << 21;
+ s += 3;
+ continue;
+ }
+ if (target_xp && strncmp (s, "p0", 2) == 0)
+ {
+ opcode |= 0x8 << 21;
+ s += 2;
+ continue;
+ }
+ if (target_xp && strncmp (s, "p1", 2) == 0)
+ {
+ opcode |= 0x9 << 21;
+ s += 2;
+ continue;
+ }
+ if (target_xp && strncmp (s, "p2", 2) == 0)
+ {
+ opcode |= 0xa << 21;
+ s += 2;
+ continue;
+ }
+ if (target_xp && strncmp (s, "p3", 2) == 0)
+ {
+ opcode |= 0xb << 21;
+ s += 2;
+ continue;
+ }
+ break;
+
+ /* 5-bit immediate in src1. */
+ case '5':
+ if (! i860_get_expression (s))
+ {
+ s = expr_end;
+ the_insn.fi[fc].fup |= OP_IMM_U5;
+ fc++;
+ continue;
+ }
+ break;
+
+ /* 26-bit immediate, relative branch (lbroff). */
+ case 'l':
+ the_insn.fi[fc].pcrel = 1;
+ the_insn.fi[fc].fup |= OP_IMM_BR26;
+ goto immediate;
+
+ /* 16-bit split immediate, relative branch (sbroff). */
+ case 'r':
+ the_insn.fi[fc].pcrel = 1;
+ the_insn.fi[fc].fup |= OP_IMM_BR16;
+ goto immediate;
+
+ /* 16-bit split immediate. */
+ case 's':
+ the_insn.fi[fc].fup |= OP_IMM_SPLIT16;
+ goto immediate;
+
+ /* 16-bit split immediate, byte aligned (st.b). */
+ case 'S':
+ the_insn.fi[fc].fup |= OP_IMM_SPLIT16;
+ goto immediate;
+
+ /* 16-bit split immediate, half-word aligned (st.s). */
+ case 'T':
+ the_insn.fi[fc].fup |= (OP_IMM_SPLIT16 | OP_ENCODE1 | OP_ALIGN2);
+ goto immediate;
+
+ /* 16-bit split immediate, word aligned (st.l). */
+ case 'U':
+ the_insn.fi[fc].fup |= (OP_IMM_SPLIT16 | OP_ENCODE1 | OP_ALIGN4);
+ goto immediate;
+
+ /* 16-bit immediate. */
+ case 'i':
+ the_insn.fi[fc].fup |= OP_IMM_S16;
+ goto immediate;
+
+ /* 16-bit immediate, byte aligned (ld.b). */
+ case 'I':
+ the_insn.fi[fc].fup |= OP_IMM_S16;
+ goto immediate;
+
+ /* 16-bit immediate, half-word aligned (ld.s). */
+ case 'J':
+ the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE1 | OP_ALIGN2);
+ goto immediate;
+
+ /* 16-bit immediate, word aligned (ld.l, {p}fld.l, fst.l). */
+ case 'K':
+ if (insn->name[0] == 'l')
+ the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE1 | OP_ALIGN4);
+ else
+ the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE2 | OP_ALIGN4);
+ goto immediate;
+
+ /* 16-bit immediate, double-word aligned ({p}fld.d, fst.d). */
+ case 'L':
+ the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE3 | OP_ALIGN8);
+ goto immediate;
+
+ /* 16-bit immediate, quad-word aligned (fld.q, fst.q). */
+ case 'M':
+ the_insn.fi[fc].fup |= (OP_IMM_S16 | OP_ENCODE3 | OP_ALIGN16);
+
+ /*FALLTHROUGH*/
+
+ /* Handle the immediate for either the Intel syntax or
+ SVR4 syntax. The Intel syntax is "ha%immediate"
+ whereas SVR4 syntax is "[immediate]@ha". */
+ immediate:
+ if (target_intel_syntax == 0)
+ {
+ /* AT&T/SVR4 syntax. */
+ if (*s == ' ')
+ s++;
+
+ /* Note that if i860_get_expression() fails, we will still
+ have created U entries in the symbol table for the
+ 'symbols' in the input string. Try not to create U
+ symbols for registers, etc. */
+ if (! i860_get_expression (s))
+ s = expr_end;
+ else
+ goto error;
+
+ if (strncmp (s, "@ha", 3) == 0)
+ {
+ the_insn.fi[fc].fup |= OP_SEL_HA;
+ s += 3;
+ }
+ else if (strncmp (s, "@h", 2) == 0)
+ {
+ the_insn.fi[fc].fup |= OP_SEL_H;
+ s += 2;
+ }
+ else if (strncmp (s, "@l", 2) == 0)
+ {
+ the_insn.fi[fc].fup |= OP_SEL_L;
+ s += 2;
+ }
+ else if (strncmp (s, "@gotoff", 7) == 0
+ || strncmp (s, "@GOTOFF", 7) == 0)
+ {
+ as_bad (_("Assembler does not yet support PIC"));
+ the_insn.fi[fc].fup |= OP_SEL_GOTOFF;
+ s += 7;
+ }
+ else if (strncmp (s, "@got", 4) == 0
+ || strncmp (s, "@GOT", 4) == 0)
+ {
+ as_bad (_("Assembler does not yet support PIC"));
+ the_insn.fi[fc].fup |= OP_SEL_GOT;
+ s += 4;
+ }
+ else if (strncmp (s, "@plt", 4) == 0
+ || strncmp (s, "@PLT", 4) == 0)
+ {
+ as_bad (_("Assembler does not yet support PIC"));
+ the_insn.fi[fc].fup |= OP_SEL_PLT;
+ s += 4;
+ }
+
+ the_insn.expand = insn->expand;
+ fc++;
+
+ continue;
+ }
+ else
+ {
+ /* Intel syntax. */
+ if (*s == ' ')
+ s++;
+ if (strncmp (s, "ha%", 3) == 0)
+ {
+ the_insn.fi[fc].fup |= OP_SEL_HA;
+ s += 3;
+ }
+ else if (strncmp (s, "h%", 2) == 0)
+ {
+ the_insn.fi[fc].fup |= OP_SEL_H;
+ s += 2;
+ }
+ else if (strncmp (s, "l%", 2) == 0)
+ {
+ the_insn.fi[fc].fup |= OP_SEL_L;
+ s += 2;
+ }
+ the_insn.expand = insn->expand;
+
+ /* Note that if i860_get_expression() fails, we will still
+ have created U entries in the symbol table for the
+ 'symbols' in the input string. Try not to create U
+ symbols for registers, etc. */
+ if (! i860_get_expression (s))
+ s = expr_end;
+ else
+ goto error;
+
+ fc++;
+ continue;
+ }
+ break;
+
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+ break;
+ }
+ error:
+ if (match == 0)
+ {
+ /* Args don't match. */
+ if (insn[1].name != NULL
+ && ! strcmp (insn->name, insn[1].name))
+ {
+ ++insn;
+ s = args_start;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Illegal operands for %s"), insn->name);
+ return;
+ }
+ }
+ break;
+ }
+
+ /* Set the dual bit on this instruction if necessary. */
+ if (dual_mode != DUAL_OFF)
+ {
+ if ((opcode & 0xfc000000) == 0x48000000 || opcode == 0xb0000000)
+ {
+ /* The instruction is a flop or a fnop, so set its dual bit
+ (but check that it is 8-byte aligned). */
+ if (((frag_now->fr_address + frag_now_fix_octets ()) & 7) == 0)
+ opcode |= (1 << 9);
+ else
+ as_bad (_("'d.%s' must be 8-byte aligned"), insn->name);
+
+ if (dual_mode == DUAL_DDOT)
+ dual_mode = DUAL_OFF;
+ else if (dual_mode == DUAL_ONDDOT)
+ dual_mode = DUAL_ON;
+ }
+ else if (dual_mode == DUAL_DDOT || dual_mode == DUAL_ONDDOT)
+ as_bad (_("Prefix 'd.' invalid for instruction `%s'"), insn->name);
+ }
+
+ the_insn.opcode = opcode;
+
+ /* Only recognize XP instructions when the user has requested it. */
+ if (insn->expand == XP_ONLY && ! target_xp)
+ as_bad (_("Unknown opcode: `%s'"), insn->name);
+}
+
+static int
+i860_get_expression (char *str)
+{
+ char *save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&the_insn.fi[fc].exp);
+ if (seg != absolute_section
+ && seg != undefined_section
+ && ! SEG_NORMAL (seg))
+ {
+ the_insn.error = _("bad segment");
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Write out in current endian mode. */
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* This should never be called for i860. */
+int
+md_estimate_size_before_relax (register fragS *fragP ATTRIBUTE_UNUSED,
+ segT segtype ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("relaxation not supported\n"));
+}
+
+#ifdef DEBUG_I860
+static void
+print_insn (struct i860_it *insn)
+{
+ if (insn->error)
+ fprintf (stderr, "ERROR: %s\n", insn->error);
+
+ fprintf (stderr, "opcode = 0x%08lx\t", insn->opcode);
+ fprintf (stderr, "expand = 0x%x\t", insn->expand);
+ fprintf (stderr, "reloc = %s\t\n",
+ bfd_get_reloc_code_name (insn->reloc));
+ fprintf (stderr, "exp = {\n");
+ fprintf (stderr, "\t\tX_add_symbol = %s\n",
+ insn->exp.X_add_symbol ?
+ (S_GET_NAME (insn->exp.X_add_symbol) ?
+ S_GET_NAME (insn->exp.X_add_symbol) : "???") : "0");
+ fprintf (stderr, "\t\tX_op_symbol = %s\n",
+ insn->exp.X_op_symbol ?
+ (S_GET_NAME (insn->exp.X_op_symbol) ?
+ S_GET_NAME (insn->exp.X_op_symbol) : "???") : "0");
+ fprintf (stderr, "\t\tX_add_number = %lx\n",
+ insn->exp.X_add_number);
+ fprintf (stderr, "}\n");
+}
+#endif /* DEBUG_I860 */
+
+
+#ifdef OBJ_ELF
+const char *md_shortopts = "VQ:";
+#else
+const char *md_shortopts = "";
+#endif
+
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#define OPTION_WARN_EXPAND (OPTION_MD_BASE + 2)
+#define OPTION_XP (OPTION_MD_BASE + 3)
+#define OPTION_INTEL_SYNTAX (OPTION_MD_BASE + 4)
+
+struct option md_longopts[] = {
+ { "EB", no_argument, NULL, OPTION_EB },
+ { "EL", no_argument, NULL, OPTION_EL },
+ { "mwarn-expand", no_argument, NULL, OPTION_WARN_EXPAND },
+ { "mxp", no_argument, NULL, OPTION_XP },
+ { "mintel-syntax",no_argument, NULL, OPTION_INTEL_SYNTAX },
+ { NULL, no_argument, NULL, 0 }
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+
+ case OPTION_WARN_EXPAND:
+ target_warn_expand = 1;
+ break;
+
+ case OPTION_XP:
+ target_xp = 1;
+ break;
+
+ case OPTION_INTEL_SYNTAX:
+ target_intel_syntax = 1;
+ break;
+
+#ifdef OBJ_ELF
+ /* SVR4 argument compatibility (-V): print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* SVR4 argument compatibility (-Qy, -Qn): controls whether
+ a .comment section should be emitted or not (ignored). */
+ case 'Q':
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+ -EL generate code for little endian mode (default)\n\
+ -EB generate code for big endian mode\n\
+ -mwarn-expand warn if pseudo operations are expanded\n\
+ -mxp enable i860XP support (disabled by default)\n\
+ -mintel-syntax enable Intel syntax (default to AT&T/SVR4)\n"));
+#ifdef OBJ_ELF
+ /* SVR4 compatibility flags. */
+ fprintf (stream, _("\
+ -V print assembler version number\n\
+ -Qy, -Qn ignored\n"));
+#endif
+}
+
+
+/* We have no need to default values of symbols. */
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* The i860 denotes auto-increment with '++'. */
+void
+md_operand (expressionS *exp)
+{
+ char *s;
+
+ for (s = input_line_pointer; *s; s++)
+ {
+ if (s[0] == '+' && s[1] == '+')
+ {
+ input_line_pointer += 2;
+ exp->X_op = O_register;
+ break;
+ }
+ }
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size ATTRIBUTE_UNUSED)
+{
+ /* Byte alignment is fine. */
+ return size;
+}
+
+/* On the i860, a PC-relative offset is relative to the address of the
+ offset plus its size. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Determine the relocation needed for non PC-relative 16-bit immediates.
+ Also adjust the given immediate as necessary. Finally, check that
+ all constraints (such as alignment) are satisfied. */
+static bfd_reloc_code_real_type
+obtain_reloc_for_imm16 (fixS *fix, long *val)
+{
+ valueT fup = fix->fx_addnumber;
+ bfd_reloc_code_real_type reloc;
+
+ if (fix->fx_pcrel)
+ abort ();
+
+ /* Check alignment restrictions. */
+ if ((fup & OP_ALIGN2) && (*val & 0x1))
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("This immediate requires 0 MOD 2 alignment"));
+ else if ((fup & OP_ALIGN4) && (*val & 0x3))
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("This immediate requires 0 MOD 4 alignment"));
+ else if ((fup & OP_ALIGN8) && (*val & 0x7))
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("This immediate requires 0 MOD 8 alignment"));
+ else if ((fup & OP_ALIGN16) && (*val & 0xf))
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("This immediate requires 0 MOD 16 alignment"));
+
+ if (fup & OP_SEL_HA)
+ {
+ *val = (*val >> 16) + (*val & 0x8000 ? 1 : 0);
+ reloc = BFD_RELOC_860_HIGHADJ;
+ }
+ else if (fup & OP_SEL_H)
+ {
+ *val >>= 16;
+ reloc = BFD_RELOC_860_HIGH;
+ }
+ else if (fup & OP_SEL_L)
+ {
+ int num_encode;
+ if (fup & OP_IMM_SPLIT16)
+ {
+ if (fup & OP_ENCODE1)
+ {
+ num_encode = 1;
+ reloc = BFD_RELOC_860_SPLIT1;
+ }
+ else if (fup & OP_ENCODE2)
+ {
+ num_encode = 2;
+ reloc = BFD_RELOC_860_SPLIT2;
+ }
+ else
+ {
+ num_encode = 0;
+ reloc = BFD_RELOC_860_SPLIT0;
+ }
+ }
+ else
+ {
+ if (fup & OP_ENCODE1)
+ {
+ num_encode = 1;
+ reloc = BFD_RELOC_860_LOW1;
+ }
+ else if (fup & OP_ENCODE2)
+ {
+ num_encode = 2;
+ reloc = BFD_RELOC_860_LOW2;
+ }
+ else if (fup & OP_ENCODE3)
+ {
+ num_encode = 3;
+ reloc = BFD_RELOC_860_LOW3;
+ }
+ else
+ {
+ num_encode = 0;
+ reloc = BFD_RELOC_860_LOW0;
+ }
+ }
+
+ /* Preserve size encode bits. */
+ *val &= ~((1 << num_encode) - 1);
+ }
+ else
+ {
+ /* No selector. What reloc do we generate (???)? */
+ reloc = BFD_RELOC_32;
+ }
+
+ return reloc;
+}
+
+/* Attempt to simplify or eliminate a fixup. To indicate that a fixup
+ has been eliminated, set fix->fx_done. If fix->fx_addsy is non-NULL,
+ we will have to generate a reloc entry. */
+
+void
+md_apply_fix (fixS *fix, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf;
+ long val = *valP;
+ unsigned long insn;
+ valueT fup;
+
+ buf = fix->fx_frag->fr_literal + fix->fx_where;
+
+ /* Recall that earlier we stored the opcode little-endian. */
+ insn = bfd_getl32 (buf);
+
+ /* We stored a fix-up in this oddly-named scratch field. */
+ fup = fix->fx_addnumber;
+
+ /* Determine the necessary relocations as well as inserting an
+ immediate into the instruction. */
+ if (fup & OP_IMM_U5)
+ {
+ if (val & ~0x1f)
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("5-bit immediate too large"));
+ if (fix->fx_addsy)
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("5-bit field must be absolute"));
+
+ insn |= (val & 0x1f) << 11;
+ bfd_putl32 (insn, buf);
+ fix->fx_r_type = BFD_RELOC_NONE;
+ fix->fx_done = 1;
+ }
+ else if (fup & OP_IMM_S16)
+ {
+ fix->fx_r_type = obtain_reloc_for_imm16 (fix, &val);
+
+ /* Insert the immediate. */
+ if (fix->fx_addsy)
+ fix->fx_done = 0;
+ else
+ {
+ insn |= val & 0xffff;
+ bfd_putl32 (insn, buf);
+ fix->fx_r_type = BFD_RELOC_NONE;
+ fix->fx_done = 1;
+ }
+ }
+ else if (fup & OP_IMM_U16)
+ abort ();
+
+ else if (fup & OP_IMM_SPLIT16)
+ {
+ fix->fx_r_type = obtain_reloc_for_imm16 (fix, &val);
+
+ /* Insert the immediate. */
+ if (fix->fx_addsy)
+ fix->fx_done = 0;
+ else
+ {
+ insn |= val & 0x7ff;
+ insn |= (val & 0xf800) << 5;
+ bfd_putl32 (insn, buf);
+ fix->fx_r_type = BFD_RELOC_NONE;
+ fix->fx_done = 1;
+ }
+ }
+ else if (fup & OP_IMM_BR16)
+ {
+ if (val & 0x3)
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("A branch offset requires 0 MOD 4 alignment"));
+
+ val = val >> 2;
+
+ /* Insert the immediate. */
+ if (fix->fx_addsy)
+ {
+ fix->fx_done = 0;
+ fix->fx_r_type = BFD_RELOC_860_PC16;
+ }
+ else
+ {
+ insn |= (val & 0x7ff);
+ insn |= ((val & 0xf800) << 5);
+ bfd_putl32 (insn, buf);
+ fix->fx_r_type = BFD_RELOC_NONE;
+ fix->fx_done = 1;
+ }
+ }
+ else if (fup & OP_IMM_BR26)
+ {
+ if (val & 0x3)
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("A branch offset requires 0 MOD 4 alignment"));
+
+ val >>= 2;
+
+ /* Insert the immediate. */
+ if (fix->fx_addsy)
+ {
+ fix->fx_r_type = BFD_RELOC_860_PC26;
+ fix->fx_done = 0;
+ }
+ else
+ {
+ insn |= (val & 0x3ffffff);
+ bfd_putl32 (insn, buf);
+ fix->fx_r_type = BFD_RELOC_NONE;
+ fix->fx_done = 1;
+ }
+ }
+ else if (fup != OP_NONE)
+ {
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("Unrecognized fix-up (0x%08lx)"), (unsigned long) fup);
+ abort ();
+ }
+ else
+ {
+ /* I believe only fix-ups such as ".long .ep.main-main+0xc8000000"
+ reach here (???). */
+ if (fix->fx_addsy)
+ {
+ fix->fx_r_type = BFD_RELOC_32;
+ fix->fx_done = 0;
+ }
+ else
+ {
+ insn |= (val & 0xffffffff);
+ bfd_putl32 (insn, buf);
+ fix->fx_r_type = BFD_RELOC_NONE;
+ fix->fx_done = 1;
+ }
+ }
+}
+
+/* Generate a machine dependent reloc from a fixup. */
+arelent*
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (*reloc));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = fixp->fx_offset;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (! reloc->howto)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ "Cannot represent %s relocation in object file",
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ }
+ return reloc;
+}
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+i860_handle_align (fragS *fragp)
+{
+ /* Instructions are always stored little-endian on the i860. */
+ static const unsigned char le_nop[] = { 0x00, 0x00, 0x00, 0xA0 };
+
+ int bytes;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+
+ /* Make sure we are on a 4-byte boundary, in case someone has been
+ putting data into a text section. */
+ if (bytes & 3)
+ {
+ int fix = bytes & 3;
+ memset (p, 0, fix);
+ p += fix;
+ fragp->fr_fix += fix;
+ }
+
+ memcpy (p, le_nop, 4);
+ fragp->fr_var = 4;
+}
+
+/* This is called after a user-defined label is seen. We check
+ if the label has a double colon (valid in Intel syntax mode only),
+ in which case it should be externalized. */
+
+void
+i860_check_label (symbolS *labelsym)
+{
+ /* At this point, the current line pointer is sitting on the character
+ just after the first colon on the label. */
+ if (target_intel_syntax && *input_line_pointer == ':')
+ {
+ S_SET_EXTERNAL (labelsym);
+ input_line_pointer++;
+ }
+}
diff --git a/gas/config/tc-i860.h b/gas/config/tc-i860.h
new file mode 100644
index 0000000..692ea30
--- /dev/null
+++ b/gas/config/tc-i860.h
@@ -0,0 +1,95 @@
+/* tc-i860.h -- Header file for the i860.
+ Copyright (C) 1991-2014 Free Software Foundation, Inc.
+
+ Brought back from the dead and completely reworked
+ by Jason Eckhardt <jle@cygnus.com>.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifndef TC_I860
+#define TC_I860 1
+
+enum i860_fix_info
+{
+ OP_NONE = 0x00000,
+ OP_IMM_U5 = 0x00001,
+ OP_IMM_S16 = 0x00002,
+ OP_IMM_U16 = 0x00004,
+ OP_IMM_SPLIT16 = 0x00008,
+ OP_IMM_BR26 = 0x00010,
+ OP_IMM_BR16 = 0x00020,
+ OP_ENCODE1 = 0x00040,
+ OP_ENCODE2 = 0x00080,
+ OP_ENCODE3 = 0x00100,
+ OP_SEL_HA = 0x00200,
+ OP_SEL_H = 0x00400,
+ OP_SEL_L = 0x00800,
+ OP_SEL_GOT = 0x01000,
+ OP_SEL_GOTOFF = 0x02000,
+ OP_SEL_PLT = 0x04000,
+ OP_ALIGN2 = 0x08000,
+ OP_ALIGN4 = 0x10000,
+ OP_ALIGN8 = 0x20000,
+ OP_ALIGN16 = 0x40000
+};
+
+/* Set the endianness we are using. Default to little endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 0
+#endif
+
+/* Whether or not the target is big endian. */
+extern int target_big_endian;
+
+/* BFD target architecture. */
+#define TARGET_ARCH bfd_arch_i860
+
+/* The target BFD format. */
+#ifdef OBJ_ELF
+#define TARGET_FORMAT (target_big_endian ? "elf32-i860" : "elf32-i860-little")
+#else
+#error i860 GAS currently supports only the ELF object format
+#endif
+
+#define WORKING_DOT_WORD
+#define DIFF_EXPR_OK
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+#define LISTING_HEADER "GAS for i860"
+
+#define md_convert_frag(b,s,f) abort ()
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* Bits for post-processing of a user defined label to check if
+ it has a double colon (Intel syntax only). */
+extern void i860_check_label (symbolS *labelsym);
+#define tc_check_label(ls) i860_check_label (ls)
+
+/* Bits for filling in rs_align_code fragments with NOPs. */
+extern void i860_handle_align (struct frag *);
+#define HANDLE_ALIGN(fragp) i860_handle_align (fragp)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4 + 4)
+
+#endif /* TC_I860 */
diff --git a/gas/config/tc-i960.c b/gas/config/tc-i960.c
new file mode 100644
index 0000000..91469eb
--- /dev/null
+++ b/gas/config/tc-i960.c
@@ -0,0 +1,2666 @@
+/* tc-i960.c - All the i80960-specific stuff
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* See comment on md_parse_option for 80960-specific invocation options. */
+
+/* There are 4 different lengths of (potentially) symbol-based displacements
+ in the 80960 instruction set, each of which could require address fix-ups
+ and (in the case of external symbols) emission of relocation directives:
+
+ 32-bit (MEMB)
+ This is a standard length for the base assembler and requires no
+ special action.
+
+ 13-bit (COBR)
+ This is a non-standard length, but the base assembler has a
+ hook for bit field address fixups: the fixS structure can
+ point to a descriptor of the field, in which case our
+ md_number_to_field() routine gets called to process it.
+
+ I made the hook a little cleaner by having fix_new() (in the base
+ assembler) return a pointer to the fixS in question. And I made it a
+ little simpler by storing the field size (in this case 13) instead of
+ of a pointer to another structure: 80960 displacements are ALWAYS
+ stored in the low-order bits of a 4-byte word.
+
+ Since the target of a COBR cannot be external, no relocation
+ directives for this size displacement have to be generated.
+ But the base assembler had to be modified to issue error
+ messages if the symbol did turn out to be external.
+
+ 24-bit (CTRL)
+ Fixups are handled as for the 13-bit case (except that 24 is stored
+ in the fixS).
+
+ The relocation directive generated is the same as that for the 32-bit
+ displacement, except that it's PC-relative (the 32-bit displacement
+ never is). The i80960 version of the linker needs a mod to
+ distinguish and handle the 24-bit case.
+
+ 12-bit (MEMA)
+ MEMA formats are always promoted to MEMB (32-bit) if the displacement
+ is based on a symbol, because it could be relocated at link time.
+ The only time we use the 12-bit format is if an absolute value of
+ less than 4096 is specified, in which case we need neither a fixup nor
+ a relocation directive. */
+
+#include "as.h"
+
+#include "safe-ctype.h"
+
+#include "opcode/i960.h"
+
+#if defined (OBJ_AOUT) || defined (OBJ_BOUT)
+
+#define TC_S_IS_SYSPROC(s) ((1 <= S_GET_OTHER (s)) && (S_GET_OTHER (s) <= 32))
+#define TC_S_IS_BALNAME(s) (S_GET_OTHER (s) == N_BALNAME)
+#define TC_S_IS_CALLNAME(s) (S_GET_OTHER (s) == N_CALLNAME)
+#define TC_S_IS_BADPROC(s) ((S_GET_OTHER (s) != 0) && !TC_S_IS_CALLNAME (s) && !TC_S_IS_BALNAME (s) && !TC_S_IS_SYSPROC (s))
+
+#define TC_S_SET_SYSPROC(s, p) (S_SET_OTHER ((s), (p) + 1))
+#define TC_S_GET_SYSPROC(s) (S_GET_OTHER (s) - 1)
+
+#define TC_S_FORCE_TO_BALNAME(s) (S_SET_OTHER ((s), N_BALNAME))
+#define TC_S_FORCE_TO_CALLNAME(s) (S_SET_OTHER ((s), N_CALLNAME))
+#define TC_S_FORCE_TO_SYSPROC(s) {;}
+
+#else /* ! OBJ_A/BOUT */
+#ifdef OBJ_COFF
+
+#define TC_S_IS_SYSPROC(s) (S_GET_STORAGE_CLASS (s) == C_SCALL)
+#define TC_S_IS_BALNAME(s) (SF_GET_BALNAME (s))
+#define TC_S_IS_CALLNAME(s) (SF_GET_CALLNAME (s))
+#define TC_S_IS_BADPROC(s) (TC_S_IS_SYSPROC (s) && TC_S_GET_SYSPROC (s) < 0 && 31 < TC_S_GET_SYSPROC (s))
+
+#define TC_S_SET_SYSPROC(s, p) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx = (p))
+#define TC_S_GET_SYSPROC(s) ((s)->sy_symbol.ost_auxent[1].x_sc.x_stindx)
+
+#define TC_S_FORCE_TO_BALNAME(s) (SF_SET_BALNAME (s))
+#define TC_S_FORCE_TO_CALLNAME(s) (SF_SET_CALLNAME (s))
+#define TC_S_FORCE_TO_SYSPROC(s) (S_SET_STORAGE_CLASS ((s), C_SCALL))
+
+#else /* ! OBJ_COFF */
+#ifdef OBJ_ELF
+#define TC_S_IS_SYSPROC(s) 0
+
+#define TC_S_IS_BALNAME(s) 0
+#define TC_S_IS_CALLNAME(s) 0
+#define TC_S_IS_BADPROC(s) 0
+
+#define TC_S_SET_SYSPROC(s, p)
+#define TC_S_GET_SYSPROC(s) 0
+
+#define TC_S_FORCE_TO_BALNAME(s)
+#define TC_S_FORCE_TO_CALLNAME(s)
+#define TC_S_FORCE_TO_SYSPROC(s)
+#else
+ #error COFF, a.out, b.out, and ELF are the only supported formats.
+#endif /* ! OBJ_ELF */
+#endif /* ! OBJ_COFF */
+#endif /* ! OBJ_A/BOUT */
+
+extern char *input_line_pointer;
+
+/* Local i80960 routines. */
+struct memS;
+struct regop;
+
+/* See md_parse_option() for meanings of these options. */
+static char norelax; /* True if -norelax switch seen. */
+static char instrument_branches; /* True if -b switch seen. */
+
+/* Characters that always start a comment.
+ If the pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "#";
+
+/* Characters that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output.
+
+ Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+
+/* Also note that comments started like this one will always work. */
+
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant,
+ as in 0f12.456 or 0d1.2345e12. */
+const char FLT_CHARS[] = "fFdDtT";
+
+/* Table used by base assembler to relax addresses based on varying length
+ instructions. The fields are:
+ 1) most positive reach of this state,
+ 2) most negative reach of this state,
+ 3) how many bytes this mode will add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one.
+
+ For i80960, the only application is the (de-)optimization of cobr
+ instructions into separate compare and branch instructions when a 13-bit
+ displacement won't hack it. */
+const relax_typeS md_relax_table[] =
+{
+ {0, 0, 0, 0}, /* State 0 => no more relaxation possible. */
+ {4088, -4096, 0, 2}, /* State 1: conditional branch (cobr). */
+ {0x800000 - 8, -0x800000, 4, 0}, /* State 2: compare (reg) & branch (ctrl). */
+};
+
+/* These are the machine dependent pseudo-ops.
+
+ This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ integer arg to pass to the function. */
+#define S_LEAFPROC 1
+#define S_SYSPROC 2
+
+/* Macros to extract info from an 'expressionS' structure 'e'. */
+#define adds(e) e.X_add_symbol
+#define offs(e) e.X_add_number
+
+/* Branch-prediction bits for CTRL/COBR format opcodes. */
+#define BP_MASK 0x00000002 /* Mask for branch-prediction bit. */
+#define BP_TAKEN 0x00000000 /* Value to OR in to predict branch. */
+#define BP_NOT_TAKEN 0x00000002 /* Value to OR in to predict no branch. */
+
+/* Some instruction opcodes that we need explicitly. */
+#define BE 0x12000000
+#define BG 0x11000000
+#define BGE 0x13000000
+#define BL 0x14000000
+#define BLE 0x16000000
+#define BNE 0x15000000
+#define BNO 0x10000000
+#define BO 0x17000000
+#define CHKBIT 0x5a002700
+#define CMPI 0x5a002080
+#define CMPO 0x5a002000
+
+#define B 0x08000000
+#define BAL 0x0b000000
+#define CALL 0x09000000
+#define CALLS 0x66003800
+#define RET 0x0a000000
+
+/* These masks are used to build up a set of MEMB mode bits. */
+#define A_BIT 0x0400
+#define I_BIT 0x0800
+#define MEMB_BIT 0x1000
+#define D_BIT 0x2000
+
+/* Mask for the only mode bit in a MEMA instruction (if set, abase reg is
+ used). */
+#define MEMA_ABASE 0x2000
+
+/* Info from which a MEMA or MEMB format instruction can be generated. */
+typedef struct memS
+ {
+ /* (First) 32 bits of instruction. */
+ long opcode;
+ /* 0-(none), 12- or, 32-bit displacement needed. */
+ int disp;
+ /* The expression in the source instruction from which the
+ displacement should be determined. */
+ char *e;
+ }
+memS;
+
+/* The two pieces of info we need to generate a register operand. */
+struct regop
+ {
+ int mode; /* 0 =>local/global/spec reg; 1=> literal or fp reg. */
+ int special; /* 0 =>not a sfr; 1=> is a sfr (not valid w/mode=0). */
+ int n; /* Register number or literal value. */
+ };
+
+/* Number and assembler mnemonic for all registers that can appear in
+ operands. */
+static const struct
+ {
+ char *reg_name;
+ int reg_num;
+ }
+regnames[] =
+{
+ { "pfp", 0 },
+ { "sp", 1 },
+ { "rip", 2 },
+ { "r3", 3 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "g0", 16 },
+ { "g1", 17 },
+ { "g2", 18 },
+ { "g3", 19 },
+ { "g4", 20 },
+ { "g5", 21 },
+ { "g6", 22 },
+ { "g7", 23 },
+ { "g8", 24 },
+ { "g9", 25 },
+ { "g10", 26 },
+ { "g11", 27 },
+ { "g12", 28 },
+ { "g13", 29 },
+ { "g14", 30 },
+ { "fp", 31 },
+
+ /* Numbers for special-function registers are for assembler internal
+ use only: they are scaled back to range [0-31] for binary output. */
+#define SF0 32
+
+ { "sf0", 32 },
+ { "sf1", 33 },
+ { "sf2", 34 },
+ { "sf3", 35 },
+ { "sf4", 36 },
+ { "sf5", 37 },
+ { "sf6", 38 },
+ { "sf7", 39 },
+ { "sf8", 40 },
+ { "sf9", 41 },
+ { "sf10", 42 },
+ { "sf11", 43 },
+ { "sf12", 44 },
+ { "sf13", 45 },
+ { "sf14", 46 },
+ { "sf15", 47 },
+ { "sf16", 48 },
+ { "sf17", 49 },
+ { "sf18", 50 },
+ { "sf19", 51 },
+ { "sf20", 52 },
+ { "sf21", 53 },
+ { "sf22", 54 },
+ { "sf23", 55 },
+ { "sf24", 56 },
+ { "sf25", 57 },
+ { "sf26", 58 },
+ { "sf27", 59 },
+ { "sf28", 60 },
+ { "sf29", 61 },
+ { "sf30", 62 },
+ { "sf31", 63 },
+
+ /* Numbers for floating point registers are for assembler internal
+ use only: they are scaled back to [0-3] for binary output. */
+#define FP0 64
+
+ { "fp0", 64 },
+ { "fp1", 65 },
+ { "fp2", 66 },
+ { "fp3", 67 },
+
+ { NULL, 0 }, /* END OF LIST */
+};
+
+#define IS_RG_REG(n) ((0 <= (n)) && ((n) < SF0))
+#define IS_SF_REG(n) ((SF0 <= (n)) && ((n) < FP0))
+#define IS_FP_REG(n) ((n) >= FP0)
+
+/* Number and assembler mnemonic for all registers that can appear as
+ 'abase' (indirect addressing) registers. */
+static const struct
+{
+ char *areg_name;
+ int areg_num;
+}
+aregs[] =
+{
+ { "(pfp)", 0 },
+ { "(sp)", 1 },
+ { "(rip)", 2 },
+ { "(r3)", 3 },
+ { "(r4)", 4 },
+ { "(r5)", 5 },
+ { "(r6)", 6 },
+ { "(r7)", 7 },
+ { "(r8)", 8 },
+ { "(r9)", 9 },
+ { "(r10)", 10 },
+ { "(r11)", 11 },
+ { "(r12)", 12 },
+ { "(r13)", 13 },
+ { "(r14)", 14 },
+ { "(r15)", 15 },
+ { "(g0)", 16 },
+ { "(g1)", 17 },
+ { "(g2)", 18 },
+ { "(g3)", 19 },
+ { "(g4)", 20 },
+ { "(g5)", 21 },
+ { "(g6)", 22 },
+ { "(g7)", 23 },
+ { "(g8)", 24 },
+ { "(g9)", 25 },
+ { "(g10)", 26 },
+ { "(g11)", 27 },
+ { "(g12)", 28 },
+ { "(g13)", 29 },
+ { "(g14)", 30 },
+ { "(fp)", 31 },
+
+#define IPREL 32
+ /* For assembler internal use only: this number never appears in binary
+ output. */
+ { "(ip)", IPREL },
+
+ { NULL, 0 }, /* END OF LIST */
+};
+
+/* Hash tables. */
+static struct hash_control *op_hash; /* Opcode mnemonics. */
+static struct hash_control *reg_hash; /* Register name hash table. */
+static struct hash_control *areg_hash; /* Abase register hash table. */
+
+/* Architecture for which we are assembling. */
+#define ARCH_ANY 0 /* Default: no architecture checking done. */
+#define ARCH_KA 1
+#define ARCH_KB 2
+#define ARCH_MC 3
+#define ARCH_CA 4
+#define ARCH_JX 5
+#define ARCH_HX 6
+int architecture = ARCH_ANY; /* Architecture requested on invocation line. */
+int iclasses_seen; /* OR of instruction classes (I_* constants)
+ for which we've actually assembled
+ instructions. */
+
+/* BRANCH-PREDICTION INSTRUMENTATION
+
+ The following supports generation of branch-prediction instrumentation
+ (turned on by -b switch). The instrumentation collects counts
+ of branches taken/not-taken for later input to a utility that will
+ set the branch prediction bits of the instructions in accordance with
+ the behavior observed. (Note that the KX series does not have
+ brach-prediction.)
+
+ The instrumentation consists of:
+
+ (1) before and after each conditional branch, a call to an external
+ routine that increments and steps over an inline counter. The
+ counter itself, initialized to 0, immediately follows the call
+ instruction. For each branch, the counter following the branch
+ is the number of times the branch was not taken, and the difference
+ between the counters is the number of times it was taken. An
+ example of an instrumented conditional branch:
+
+ call BR_CNT_FUNC
+ .word 0
+ LBRANCH23: be label
+ call BR_CNT_FUNC
+ .word 0
+
+ (2) a table of pointers to the instrumented branches, so that an
+ external postprocessing routine can locate all of the counters.
+ the table begins with a 2-word header: a pointer to the next in
+ a linked list of such tables (initialized to 0); and a count
+ of the number of entries in the table (exclusive of the header.
+
+ Note that input source code is expected to already contain calls
+ an external routine that will link the branch local table into a
+ list of such tables. */
+
+/* Number of branches instrumented so far. Also used to generate
+ unique local labels for each instrumented branch. */
+static int br_cnt;
+
+#define BR_LABEL_BASE "LBRANCH"
+/* Basename of local labels on instrumented branches, to avoid
+ conflict with compiler- generated local labels. */
+
+#define BR_CNT_FUNC "__inc_branch"
+/* Name of the external routine that will increment (and step over) an
+ inline counter. */
+
+#define BR_TAB_NAME "__BRANCH_TABLE__"
+/* Name of the table of pointers to branches. A local (i.e.,
+ non-external) symbol. */
+
+static void ctrl_fmt (char *, long, int);
+
+
+void
+md_begin (void)
+{
+ int i; /* Loop counter. */
+ const struct i960_opcode *oP; /* Pointer into opcode table. */
+ const char *retval; /* Value returned by hash functions. */
+
+ op_hash = hash_new ();
+ reg_hash = hash_new ();
+ areg_hash = hash_new ();
+
+ /* For some reason, the base assembler uses an empty string for "no
+ error message", instead of a NULL pointer. */
+ retval = 0;
+
+ for (oP = i960_opcodes; oP->name && !retval; oP++)
+ retval = hash_insert (op_hash, oP->name, (void *) oP);
+
+ for (i = 0; regnames[i].reg_name && !retval; i++)
+ retval = hash_insert (reg_hash, regnames[i].reg_name,
+ (char *) &regnames[i].reg_num);
+
+ for (i = 0; aregs[i].areg_name && !retval; i++)
+ retval = hash_insert (areg_hash, aregs[i].areg_name,
+ (char *) &aregs[i].areg_num);
+
+ if (retval)
+ as_fatal (_("Hashing returned \"%s\"."), retval);
+}
+
+/* parse_expr: parse an expression
+
+ Use base assembler's expression parser to parse an expression.
+ It, unfortunately, runs off a global which we have to save/restore
+ in order to make it work for us.
+
+ An empty expression string is treated as an absolute 0.
+
+ Sets O_illegal regardless of expression evaluation if entire input
+ string is not consumed in the evaluation -- tolerate no dangling junk! */
+
+static void
+parse_expr (char *textP, /* Text of expression to be parsed. */
+ expressionS *expP) /* Where to put the results of parsing. */
+{
+ char *save_in; /* Save global here. */
+ symbolS *symP;
+
+ know (textP);
+
+ if (*textP == '\0')
+ {
+ /* Treat empty string as absolute 0. */
+ expP->X_add_symbol = expP->X_op_symbol = NULL;
+ expP->X_add_number = 0;
+ expP->X_op = O_constant;
+ }
+ else
+ {
+ save_in = input_line_pointer; /* Save global. */
+ input_line_pointer = textP; /* Make parser work for us. */
+
+ (void) expression (expP);
+ if ((size_t) (input_line_pointer - textP) != strlen (textP))
+ /* Did not consume all of the input. */
+ expP->X_op = O_illegal;
+
+ symP = expP->X_add_symbol;
+ if (symP && (hash_find (reg_hash, S_GET_NAME (symP))))
+ /* Register name in an expression. */
+ /* FIXME: this isn't much of a check any more. */
+ expP->X_op = O_illegal;
+
+ input_line_pointer = save_in; /* Restore global. */
+ }
+}
+
+/* emit: output instruction binary
+
+ Output instruction binary, in target byte order, 4 bytes at a time.
+ Return pointer to where it was placed. */
+
+static char *
+emit (long instr) /* Word to be output, host byte order. */
+{
+ char *toP; /* Where to output it. */
+
+ toP = frag_more (4); /* Allocate storage. */
+ md_number_to_chars (toP, instr, 4); /* Convert to target byte order. */
+ return toP;
+}
+
+/* get_cdisp: handle displacement for a COBR or CTRL instruction.
+
+ Parse displacement for a COBR or CTRL instruction.
+
+ If successful, output the instruction opcode and set up for it,
+ depending on the arg 'var_frag', either:
+ o an address fixup to be done when all symbol values are known, or
+ o a varying length code fragment, with address fixup info. This
+ will be done for cobr instructions that may have to be relaxed
+ in to compare/branch instructions (8 bytes) if the final
+ address displacement is greater than 13 bits. */
+
+static void
+get_cdisp (char *dispP, /* Displacement as specified in source instruction. */
+ char *ifmtP, /* "COBR" or "CTRL" (for use in error message). */
+ long instr, /* Instruction needing the displacement. */
+ int numbits, /* # bits of displacement (13 for COBR, 24 for CTRL). */
+ int var_frag,/* 1 if varying length code fragment should be emitted;
+ 0 if an address fix should be emitted. */
+ int callj) /* 1 if callj relocation should be done; else 0. */
+{
+ expressionS e; /* Parsed expression. */
+ fixS *fixP; /* Structure describing needed address fix. */
+ char *outP; /* Where instruction binary is output to. */
+
+ fixP = NULL;
+
+ parse_expr (dispP, &e);
+ switch (e.X_op)
+ {
+ case O_illegal:
+ as_bad (_("expression syntax error"));
+
+ case O_symbol:
+ if (S_GET_SEGMENT (e.X_add_symbol) == now_seg
+ || S_GET_SEGMENT (e.X_add_symbol) == undefined_section)
+ {
+ if (var_frag)
+ {
+ outP = frag_more (8); /* Allocate worst-case storage. */
+ md_number_to_chars (outP, instr, 4);
+ frag_variant (rs_machine_dependent, 4, 4, 1,
+ adds (e), offs (e), outP);
+ }
+ else
+ {
+ /* Set up a new fix structure, so address can be updated
+ when all symbol values are known. */
+ outP = emit (instr);
+ fixP = fix_new (frag_now,
+ outP - frag_now->fr_literal,
+ 4,
+ adds (e),
+ offs (e),
+ 1,
+ NO_RELOC);
+
+ fixP->fx_tcbit = callj;
+
+ /* We want to modify a bit field when the address is
+ known. But we don't need all the garbage in the
+ bit_fix structure. So we're going to lie and store
+ the number of bits affected instead of a pointer. */
+ fixP->fx_bit_fixP = (bit_fixS *) (size_t) numbits;
+ }
+ }
+ else
+ as_bad (_("attempt to branch into different segment"));
+ break;
+
+ default:
+ as_bad (_("target of %s instruction must be a label"), ifmtP);
+ break;
+ }
+}
+
+static int
+md_chars_to_number (char * val, /* Value in target byte order. */
+ int n) /* Number of bytes in the input. */
+{
+ int retval;
+
+ for (retval = 0; n--;)
+ {
+ retval <<= 8;
+ retval |= (unsigned char) val[n];
+ }
+ return retval;
+}
+
+/* mema_to_memb: convert a MEMA-format opcode to a MEMB-format opcode.
+
+ There are 2 possible MEMA formats:
+ - displacement only
+ - displacement + abase
+
+ They are distinguished by the setting of the MEMA_ABASE bit. */
+
+static void
+mema_to_memb (char * opcodeP) /* Where to find the opcode, in target byte order. */
+{
+ long opcode; /* Opcode in host byte order. */
+ long mode; /* Mode bits for MEMB instruction. */
+
+ opcode = md_chars_to_number (opcodeP, 4);
+ know (!(opcode & MEMB_BIT));
+
+ mode = MEMB_BIT | D_BIT;
+ if (opcode & MEMA_ABASE)
+ mode |= A_BIT;
+
+ opcode &= 0xffffc000; /* Clear MEMA offset and mode bits. */
+ opcode |= mode; /* Set MEMB mode bits. */
+
+ md_number_to_chars (opcodeP, opcode, 4);
+}
+
+/* targ_has_sfr:
+
+ Return TRUE iff the target architecture supports the specified
+ special-function register (sfr). */
+
+static int
+targ_has_sfr (int n) /* Number (0-31) of sfr. */
+{
+ switch (architecture)
+ {
+ case ARCH_KA:
+ case ARCH_KB:
+ case ARCH_MC:
+ case ARCH_JX:
+ return 0;
+ case ARCH_HX:
+ return ((0 <= n) && (n <= 4));
+ case ARCH_CA:
+ default:
+ return ((0 <= n) && (n <= 2));
+ }
+}
+
+/* Look up a (suspected) register name in the register table and return the
+ associated register number (or -1 if not found). */
+
+static int
+get_regnum (char *regname) /* Suspected register name. */
+{
+ int *rP;
+
+ rP = (int *) hash_find (reg_hash, regname);
+ return (rP == NULL) ? -1 : *rP;
+}
+
+/* syntax: Issue a syntax error. */
+
+static void
+syntax (void)
+{
+ as_bad (_("syntax error"));
+}
+
+/* parse_regop: parse a register operand.
+
+ In case of illegal operand, issue a message and return some valid
+ information so instruction processing can continue. */
+
+static void
+parse_regop (struct regop *regopP, /* Where to put description of register operand. */
+ char *optext, /* Text of operand. */
+ char opdesc) /* Descriptor byte: what's legal for this operand. */
+{
+ int n; /* Register number. */
+ expressionS e; /* Parsed expression. */
+
+ /* See if operand is a register. */
+ n = get_regnum (optext);
+ if (n >= 0)
+ {
+ if (IS_RG_REG (n))
+ {
+ /* Global or local register. */
+ if (!REG_ALIGN (opdesc, n))
+ as_bad (_("unaligned register"));
+
+ regopP->n = n;
+ regopP->mode = 0;
+ regopP->special = 0;
+ return;
+ }
+ else if (IS_FP_REG (n) && FP_OK (opdesc))
+ {
+ /* Floating point register, and it's allowed. */
+ regopP->n = n - FP0;
+ regopP->mode = 1;
+ regopP->special = 0;
+ return;
+ }
+ else if (IS_SF_REG (n) && SFR_OK (opdesc))
+ {
+ /* Special-function register, and it's allowed. */
+ regopP->n = n - SF0;
+ regopP->mode = 0;
+ regopP->special = 1;
+ if (!targ_has_sfr (regopP->n))
+ as_bad (_("no such sfr in this architecture"));
+
+ return;
+ }
+ }
+ else if (LIT_OK (opdesc))
+ {
+ /* How about a literal? */
+ regopP->mode = 1;
+ regopP->special = 0;
+ if (FP_OK (opdesc))
+ {
+ /* Floating point literal acceptable. */
+ /* Skip over 0f, 0d, or 0e prefix. */
+ if ((optext[0] == '0')
+ && (optext[1] >= 'd')
+ && (optext[1] <= 'f'))
+ optext += 2;
+
+ if (!strcmp (optext, "0.0") || !strcmp (optext, "0"))
+ {
+ regopP->n = 0x10;
+ return;
+ }
+
+ if (!strcmp (optext, "1.0") || !strcmp (optext, "1"))
+ {
+ regopP->n = 0x16;
+ return;
+ }
+ }
+ else
+ {
+ /* Fixed point literal acceptable. */
+ parse_expr (optext, &e);
+ if (e.X_op != O_constant
+ || (offs (e) < 0) || (offs (e) > 31))
+ {
+ as_bad (_("illegal literal"));
+ offs (e) = 0;
+ }
+ regopP->n = offs (e);
+ return;
+ }
+ }
+
+ /* Nothing worked. */
+ syntax ();
+ regopP->mode = 0; /* Register r0 is always a good one. */
+ regopP->n = 0;
+ regopP->special = 0;
+}
+
+/* get_ispec: parse a memory operand for an index specification
+
+ Here, an "index specification" is taken to be anything surrounded
+ by square brackets and NOT followed by anything else.
+
+ If it's found, detach it from the input string, remove the surrounding
+ square brackets, and return a pointer to it. Otherwise, return NULL. */
+
+static char *
+get_ispec (char *textP) /* Pointer to memory operand from source instruction, no white space. */
+
+{
+ /* Points to start of index specification. */
+ char *start;
+ /* Points to end of index specification. */
+ char *end;
+
+ /* Find opening square bracket, if any. */
+ start = strchr (textP, '[');
+
+ if (start != NULL)
+ {
+ /* Eliminate '[', detach from rest of operand. */
+ *start++ = '\0';
+
+ end = strchr (start, ']');
+
+ if (end == NULL)
+ as_bad (_("unmatched '['"));
+ else
+ {
+ /* Eliminate ']' and make sure it was the last thing
+ in the string. */
+ *end = '\0';
+ if (*(end + 1) != '\0')
+ as_bad (_("garbage after index spec ignored"));
+ }
+ }
+ return start;
+}
+
+/* parse_memop: parse a memory operand
+
+ This routine is based on the observation that the 4 mode bits of the
+ MEMB format, taken individually, have fairly consistent meaning:
+
+ M3 (bit 13): 1 if displacement is present (D_BIT)
+ M2 (bit 12): 1 for MEMB instructions (MEMB_BIT)
+ M1 (bit 11): 1 if index is present (I_BIT)
+ M0 (bit 10): 1 if abase is present (A_BIT)
+
+ So we parse the memory operand and set bits in the mode as we find
+ things. Then at the end, if we go to MEMB format, we need only set
+ the MEMB bit (M2) and our mode is built for us.
+
+ Unfortunately, I said "fairly consistent". The exceptions:
+
+ DBIA
+ 0100 Would seem illegal, but means "abase-only".
+
+ 0101 Would seem to mean "abase-only" -- it means IP-relative.
+ Must be converted to 0100.
+
+ 0110 Would seem to mean "index-only", but is reserved.
+ We turn on the D bit and provide a 0 displacement.
+
+ The other thing to observe is that we parse from the right, peeling
+ things * off as we go: first any index spec, then any abase, then
+ the displacement. */
+
+static void
+parse_memop (memS *memP, /* Where to put the results. */
+ char *argP, /* Text of the operand to be parsed. */
+ int optype) /* MEM1, MEM2, MEM4, MEM8, MEM12, or MEM16. */
+{
+ char *indexP; /* Pointer to index specification with "[]" removed. */
+ char *p; /* Temp char pointer. */
+ char iprel_flag; /* True if this is an IP-relative operand. */
+ int regnum; /* Register number. */
+ /* Scale factor: 1,2,4,8, or 16. Later converted to internal format
+ (0,1,2,3,4 respectively). */
+ int scale;
+ int mode; /* MEMB mode bits. */
+ int *intP; /* Pointer to register number. */
+
+ /* The following table contains the default scale factors for each
+ type of memory instruction. It is accessed using (optype-MEM1)
+ as an index -- thus it assumes the 'optype' constants are
+ assigned consecutive values, in the order they appear in this
+ table. */
+ static const int def_scale[] =
+ {
+ 1, /* MEM1 */
+ 2, /* MEM2 */
+ 4, /* MEM4 */
+ 8, /* MEM8 */
+ -1, /* MEM12 -- no valid default */
+ 16 /* MEM16 */
+ };
+
+ iprel_flag = mode = 0;
+
+ /* Any index present? */
+ indexP = get_ispec (argP);
+ if (indexP)
+ {
+ p = strchr (indexP, '*');
+ if (p == NULL)
+ {
+ /* No explicit scale -- use default for this instruction
+ type and assembler mode. */
+ if (flag_mri)
+ scale = 1;
+ else
+ /* GNU960 compatibility */
+ scale = def_scale[optype - MEM1];
+ }
+ else
+ {
+ *p++ = '\0'; /* Eliminate '*' */
+
+ /* Now indexP->a '\0'-terminated register name,
+ and p->a scale factor. */
+
+ if (!strcmp (p, "16"))
+ scale = 16;
+ else if (strchr ("1248", *p) && (p[1] == '\0'))
+ scale = *p - '0';
+ else
+ scale = -1;
+ }
+
+ regnum = get_regnum (indexP); /* Get index reg. # */
+ if (!IS_RG_REG (regnum))
+ {
+ as_bad (_("invalid index register"));
+ return;
+ }
+
+ /* Convert scale to its binary encoding. */
+ switch (scale)
+ {
+ case 1:
+ scale = 0 << 7;
+ break;
+ case 2:
+ scale = 1 << 7;
+ break;
+ case 4:
+ scale = 2 << 7;
+ break;
+ case 8:
+ scale = 3 << 7;
+ break;
+ case 16:
+ scale = 4 << 7;
+ break;
+ default:
+ as_bad (_("invalid scale factor"));
+ return;
+ };
+
+ memP->opcode |= scale | regnum; /* Set index bits in opcode. */
+ mode |= I_BIT; /* Found a valid index spec. */
+ }
+
+ /* Any abase (Register Indirect) specification present? */
+ if ((p = strrchr (argP, '(')) != NULL)
+ {
+ /* "(" is there -- does it start a legal abase spec? If not, it
+ could be part of a displacement expression. */
+ intP = (int *) hash_find (areg_hash, p);
+ if (intP != NULL)
+ {
+ /* Got an abase here. */
+ regnum = *intP;
+ *p = '\0'; /* Discard register spec. */
+ if (regnum == IPREL)
+ /* We have to specialcase ip-rel mode. */
+ iprel_flag = 1;
+ else
+ {
+ memP->opcode |= regnum << 14;
+ mode |= A_BIT;
+ }
+ }
+ }
+
+ /* Any expression present? */
+ memP->e = argP;
+ if (*argP != '\0')
+ mode |= D_BIT;
+
+ /* Special-case ip-relative addressing. */
+ if (iprel_flag)
+ {
+ if (mode & I_BIT)
+ syntax ();
+ else
+ {
+ memP->opcode |= 5 << 10; /* IP-relative mode. */
+ memP->disp = 32;
+ }
+ return;
+ }
+
+ /* Handle all other modes. */
+ switch (mode)
+ {
+ case D_BIT | A_BIT:
+ /* Go with MEMA instruction format for now (grow to MEMB later
+ if 12 bits is not enough for the displacement). MEMA format
+ has a single mode bit: set it to indicate that abase is
+ present. */
+ memP->opcode |= MEMA_ABASE;
+ memP->disp = 12;
+ break;
+
+ case D_BIT:
+ /* Go with MEMA instruction format for now (grow to MEMB later
+ if 12 bits is not enough for the displacement). */
+ memP->disp = 12;
+ break;
+
+ case A_BIT:
+ /* For some reason, the bit string for this mode is not
+ consistent: it should be 0 (exclusive of the MEMB bit), so we
+ set it "by hand" here. */
+ memP->opcode |= MEMB_BIT;
+ break;
+
+ case A_BIT | I_BIT:
+ /* set MEMB bit in mode, and OR in mode bits. */
+ memP->opcode |= mode | MEMB_BIT;
+ break;
+
+ case I_BIT:
+ /* Treat missing displacement as displacement of 0. */
+ mode |= D_BIT;
+ /* Fall into next case. */
+ case D_BIT | A_BIT | I_BIT:
+ case D_BIT | I_BIT:
+ /* Set MEMB bit in mode, and OR in mode bits. */
+ memP->opcode |= mode | MEMB_BIT;
+ memP->disp = 32;
+ break;
+
+ default:
+ syntax ();
+ break;
+ }
+}
+
+/* Generate a MEMA- or MEMB-format instruction. */
+
+static void
+mem_fmt (char *args[], /* args[0]->opcode mnemonic, args[1-3]->operands. */
+ struct i960_opcode *oP,/* Pointer to description of instruction. */
+ int callx) /* Is this a callx opcode. */
+{
+ int i; /* Loop counter. */
+ struct regop regop; /* Description of register operand. */
+ char opdesc; /* Operand descriptor byte. */
+ memS instr; /* Description of binary to be output. */
+ char *outP; /* Where the binary was output to. */
+ expressionS exp; /* Parsed expression. */
+ /* ->description of deferred address fixup. */
+ fixS *fixP;
+
+#ifdef OBJ_COFF
+ /* COFF support isn't in place yet for callx relaxing. */
+ callx = 0;
+#endif
+
+ memset (&instr, '\0', sizeof (memS));
+ instr.opcode = oP->opcode;
+
+ /* Process operands. */
+ for (i = 1; i <= oP->num_ops; i++)
+ {
+ opdesc = oP->operand[i - 1];
+
+ if (MEMOP (opdesc))
+ parse_memop (&instr, args[i], oP->format);
+ else
+ {
+ parse_regop (&regop, args[i], opdesc);
+ instr.opcode |= regop.n << 19;
+ }
+ }
+
+ /* Parse the displacement; this must be done before emitting the
+ opcode, in case it is an expression using `.'. */
+ parse_expr (instr.e, &exp);
+
+ /* Output opcode. */
+ outP = emit (instr.opcode);
+
+ if (instr.disp == 0)
+ return;
+
+ /* Process the displacement. */
+ switch (exp.X_op)
+ {
+ case O_illegal:
+ as_bad (_("expression syntax error"));
+ break;
+
+ case O_constant:
+ if (instr.disp == 32)
+ (void) emit (offs (exp)); /* Output displacement. */
+ else
+ {
+ /* 12-bit displacement. */
+ if (offs (exp) & ~0xfff)
+ {
+ /* Won't fit in 12 bits: convert already-output
+ instruction to MEMB format, output
+ displacement. */
+ mema_to_memb (outP);
+ (void) emit (offs (exp));
+ }
+ else
+ {
+ /* WILL fit in 12 bits: OR into opcode and
+ overwrite the binary we already put out. */
+ instr.opcode |= offs (exp);
+ md_number_to_chars (outP, instr.opcode, 4);
+ }
+ }
+ break;
+
+ default:
+ if (instr.disp == 12)
+ /* Displacement is dependent on a symbol, whose value
+ may change at link time. We HAVE to reserve 32 bits.
+ Convert already-output opcode to MEMB format. */
+ mema_to_memb (outP);
+
+ /* Output 0 displacement and set up address fixup for when
+ this symbol's value becomes known. */
+ outP = emit ((long) 0);
+ fixP = fix_new_exp (frag_now,
+ outP - frag_now->fr_literal,
+ 4, &exp, 0, NO_RELOC);
+ /* Steve's linker relaxing hack. Mark this 32-bit relocation as
+ being in the instruction stream, specifically as part of a callx
+ instruction. */
+ fixP->fx_bsr = callx;
+ break;
+ }
+}
+
+/* targ_has_iclass:
+
+ Return TRUE iff the target architecture supports the indicated
+ class of instructions. */
+
+static int
+targ_has_iclass (int ic) /* Instruction class; one of:
+ I_BASE, I_CX, I_DEC, I_KX, I_FP, I_MIL, I_CASIM, I_CX2, I_HX, I_HX2. */
+{
+ iclasses_seen |= ic;
+
+ switch (architecture)
+ {
+ case ARCH_KA:
+ return ic & (I_BASE | I_KX);
+ case ARCH_KB:
+ return ic & (I_BASE | I_KX | I_FP | I_DEC);
+ case ARCH_MC:
+ return ic & (I_BASE | I_KX | I_FP | I_DEC | I_MIL);
+ case ARCH_CA:
+ return ic & (I_BASE | I_CX | I_CX2 | I_CASIM);
+ case ARCH_JX:
+ return ic & (I_BASE | I_CX2 | I_JX);
+ case ARCH_HX:
+ return ic & (I_BASE | I_CX2 | I_JX | I_HX);
+ default:
+ if ((iclasses_seen & (I_KX | I_FP | I_DEC | I_MIL))
+ && (iclasses_seen & (I_CX | I_CX2)))
+ {
+ as_warn (_("architecture of opcode conflicts with that of earlier instruction(s)"));
+ iclasses_seen &= ~ic;
+ }
+ return 1;
+ }
+}
+
+/* shift_ok:
+ Determine if a "shlo" instruction can be used to implement a "ldconst".
+ This means that some number X < 32 can be shifted left to produce the
+ constant of interest.
+
+ Return the shift count, or 0 if we can't do it.
+ Caller calculates X by shifting original constant right 'shift' places. */
+
+static int
+shift_ok (int n) /* The constant of interest. */
+{
+ int shift; /* The shift count. */
+
+ if (n <= 0)
+ /* Can't do it for negative numbers. */
+ return 0;
+
+ /* Shift 'n' right until a 1 is about to be lost. */
+ for (shift = 0; (n & 1) == 0; shift++)
+ n >>= 1;
+
+ if (n >= 32)
+ return 0;
+
+ return shift;
+}
+
+/* parse_ldcont:
+ Parse and replace a 'ldconst' pseudo-instruction with an appropriate
+ i80960 instruction.
+
+ Assumes the input consists of:
+ arg[0] opcode mnemonic ('ldconst')
+ arg[1] first operand (constant)
+ arg[2] name of register to be loaded
+
+ Replaces opcode and/or operands as appropriate.
+
+ Returns the new number of arguments, or -1 on failure. */
+
+static int
+parse_ldconst (char *arg[]) /* See above. */
+{
+ int n; /* Constant to be loaded. */
+ int shift; /* Shift count for "shlo" instruction. */
+ static char buf[5]; /* Literal for first operand. */
+ static char buf2[5]; /* Literal for second operand. */
+ expressionS e; /* Parsed expression. */
+
+ arg[3] = NULL; /* So we can tell at the end if it got used or not. */
+
+ parse_expr (arg[1], &e);
+ switch (e.X_op)
+ {
+ default:
+ /* We're dependent on one or more symbols -- use "lda". */
+ arg[0] = "lda";
+ break;
+
+ case O_constant:
+ /* Try the following mappings:
+ ldconst 0,<reg> -> mov 0,<reg>
+ ldconst 31,<reg> -> mov 31,<reg>
+ ldconst 32,<reg> -> addo 1,31,<reg>
+ ldconst 62,<reg> -> addo 31,31,<reg>
+ ldconst 64,<reg> -> shlo 8,3,<reg>
+ ldconst -1,<reg> -> subo 1,0,<reg>
+ ldconst -31,<reg> -> subo 31,0,<reg>
+
+ Anything else becomes:
+ lda xxx,<reg>. */
+ n = offs (e);
+ if ((0 <= n) && (n <= 31))
+ arg[0] = "mov";
+ else if ((-31 <= n) && (n <= -1))
+ {
+ arg[0] = "subo";
+ arg[3] = arg[2];
+ sprintf (buf, "%d", -n);
+ arg[1] = buf;
+ arg[2] = "0";
+ }
+ else if ((32 <= n) && (n <= 62))
+ {
+ arg[0] = "addo";
+ arg[3] = arg[2];
+ arg[1] = "31";
+ sprintf (buf, "%d", n - 31);
+ arg[2] = buf;
+ }
+ else if ((shift = shift_ok (n)) != 0)
+ {
+ arg[0] = "shlo";
+ arg[3] = arg[2];
+ sprintf (buf, "%d", shift);
+ arg[1] = buf;
+ sprintf (buf2, "%d", n >> shift);
+ arg[2] = buf2;
+ }
+ else
+ arg[0] = "lda";
+ break;
+
+ case O_illegal:
+ as_bad (_("invalid constant"));
+ return -1;
+ break;
+ }
+ return (arg[3] == 0) ? 2 : 3;
+}
+
+/* reg_fmt: generate a REG-format instruction. */
+
+static void
+reg_fmt (char *args[], /* args[0]->opcode mnemonic, args[1-3]->operands. */
+ struct i960_opcode *oP)/* Pointer to description of instruction. */
+{
+ long instr; /* Binary to be output. */
+ struct regop regop; /* Description of register operand. */
+ int n_ops; /* Number of operands. */
+
+ instr = oP->opcode;
+ n_ops = oP->num_ops;
+
+ if (n_ops >= 1)
+ {
+ parse_regop (&regop, args[1], oP->operand[0]);
+
+ if ((n_ops == 1) && !(instr & M3))
+ {
+ /* 1-operand instruction in which the dst field should
+ be used (instead of src1). */
+ regop.n <<= 19;
+ if (regop.special)
+ regop.mode = regop.special;
+ regop.mode <<= 13;
+ regop.special = 0;
+ }
+ else
+ {
+ /* regop.n goes in bit 0, needs no shifting. */
+ regop.mode <<= 11;
+ regop.special <<= 5;
+ }
+ instr |= regop.n | regop.mode | regop.special;
+ }
+
+ if (n_ops >= 2)
+ {
+ parse_regop (&regop, args[2], oP->operand[1]);
+
+ if ((n_ops == 2) && !(instr & M3))
+ {
+ /* 2-operand instruction in which the dst field should
+ be used instead of src2). */
+ regop.n <<= 19;
+ if (regop.special)
+ regop.mode = regop.special;
+ regop.mode <<= 13;
+ regop.special = 0;
+ }
+ else
+ {
+ regop.n <<= 14;
+ regop.mode <<= 12;
+ regop.special <<= 6;
+ }
+ instr |= regop.n | regop.mode | regop.special;
+ }
+ if (n_ops == 3)
+ {
+ parse_regop (&regop, args[3], oP->operand[2]);
+ if (regop.special)
+ regop.mode = regop.special;
+ instr |= (regop.n <<= 19) | (regop.mode <<= 13);
+ }
+ emit (instr);
+}
+
+/* get_args: break individual arguments out of comma-separated list
+
+ Input assumptions:
+ - all comments and labels have been removed
+ - all strings of whitespace have been collapsed to a single blank.
+ - all character constants ('x') have been replaced with decimal
+
+ Output:
+ args[0] is untouched. args[1] points to first operand, etc. All args:
+ - are NULL-terminated
+ - contain no whitespace
+
+ Return value:
+ Number of operands (0,1,2, or 3) or -1 on error. */
+
+static int
+get_args (char *p, /* Pointer to comma-separated operands; Mucked by us. */
+ char *args[]) /* Output arg: pointers to operands placed in args[1-3].
+ Must accommodate 4 entries (args[0-3]). */
+
+{
+ int n; /* Number of operands. */
+ char *to;
+
+ /* Skip lead white space. */
+ while (*p == ' ')
+ p++;
+
+ if (*p == '\0')
+ return 0;
+
+ n = 1;
+ args[1] = p;
+
+ /* Squeze blanks out by moving non-blanks toward start of string.
+ Isolate operands, whenever comma is found. */
+ to = p;
+ while (*p != '\0')
+ {
+ if (*p == ' '
+ && (! ISALNUM (p[1])
+ || ! ISALNUM (p[-1])))
+ p++;
+ else if (*p == ',')
+ {
+ /* Start of operand. */
+ if (n == 3)
+ {
+ as_bad (_("too many operands"));
+ return -1;
+ }
+ *to++ = '\0'; /* Terminate argument. */
+ args[++n] = to; /* Start next argument. */
+ p++;
+ }
+ else
+ *to++ = *p++;
+ }
+ *to = '\0';
+ return n;
+}
+
+/* i_scan: perform lexical scan of ascii assembler instruction.
+
+ Input assumptions:
+ - input string is an i80960 instruction (not a pseudo-op)
+ - all comments and labels have been removed
+ - all strings of whitespace have been collapsed to a single blank.
+
+ Output:
+ args[0] points to opcode, other entries point to operands. All strings:
+ - are NULL-terminated
+ - contain no whitespace
+ - have character constants ('x') replaced with a decimal number
+
+ Return value:
+ Number of operands (0,1,2, or 3) or -1 on error. */
+
+static int
+i_scan (char *iP, /* Pointer to ascii instruction; Mucked by us. */
+ char *args[]) /* Output arg: pointers to opcode and operands placed here.
+ Must accommodate 4 entries. */
+{
+ /* Isolate opcode. */
+ if (*(iP) == ' ')
+ iP++;
+
+ args[0] = iP;
+ for (; *iP != ' '; iP++)
+ {
+ if (*iP == '\0')
+ {
+ /* There are no operands. */
+ if (args[0] == iP)
+ {
+ /* We never moved: there was no opcode either! */
+ as_bad (_("missing opcode"));
+ return -1;
+ }
+ return 0;
+ }
+ }
+ *iP++ = '\0';
+ return (get_args (iP, args));
+}
+
+static void
+brcnt_emit (void)
+{
+ /* Emit call to "increment" routine. */
+ ctrl_fmt (BR_CNT_FUNC, CALL, 1);
+ /* Emit inline counter to be incremented. */
+ emit (0);
+}
+
+static char *
+brlab_next (void)
+{
+ static char buf[20];
+
+ sprintf (buf, "%s%d", BR_LABEL_BASE, br_cnt++);
+ return buf;
+}
+
+static void
+ctrl_fmt (char *targP, /* Pointer to text of lone operand (if any). */
+ long opcode, /* Template of instruction. */
+ int num_ops) /* Number of operands. */
+{
+ int instrument; /* TRUE iff we should add instrumentation to track
+ how often the branch is taken. */
+
+ if (num_ops == 0)
+ emit (opcode); /* Output opcode. */
+ else
+ {
+ instrument = instrument_branches && (opcode != CALL)
+ && (opcode != B) && (opcode != RET) && (opcode != BAL);
+
+ if (instrument)
+ {
+ brcnt_emit ();
+ colon (brlab_next ());
+ }
+
+ /* The operand MUST be an ip-relative displacement. Parse it
+ and set up address fix for the instruction we just output. */
+ get_cdisp (targP, "CTRL", opcode, 24, 0, 0);
+
+ if (instrument)
+ brcnt_emit ();
+ }
+}
+
+static void
+cobr_fmt (/* arg[0]->opcode mnemonic, arg[1-3]->operands (ascii) */
+ char *arg[],
+ /* Opcode, with branch-prediction bits already set if necessary. */
+ long opcode,
+ /* Pointer to description of instruction. */
+ struct i960_opcode *oP)
+{
+ long instr; /* 32-bit instruction. */
+ struct regop regop; /* Description of register operand. */
+ int n; /* Number of operands. */
+ int var_frag; /* 1 if varying length code fragment should
+ be emitted; 0 if an address fix
+ should be emitted. */
+
+ instr = opcode;
+ n = oP->num_ops;
+
+ if (n >= 1)
+ {
+ /* First operand (if any) of a COBR is always a register
+ operand. Parse it. */
+ parse_regop (&regop, arg[1], oP->operand[0]);
+ instr |= (regop.n << 19) | (regop.mode << 13);
+ }
+
+ if (n >= 2)
+ {
+ /* Second operand (if any) of a COBR is always a register
+ operand. Parse it. */
+ parse_regop (&regop, arg[2], oP->operand[1]);
+ instr |= (regop.n << 14) | regop.special;
+ }
+
+ if (n < 3)
+ emit (instr);
+ else
+ {
+ if (instrument_branches)
+ {
+ brcnt_emit ();
+ colon (brlab_next ());
+ }
+
+ /* A third operand to a COBR is always a displacement. Parse
+ it; if it's relaxable (a cobr "j" directive, or any cobr
+ other than bbs/bbc when the "-norelax" option is not in use)
+ set up a variable code fragment; otherwise set up an address
+ fix. */
+ var_frag = !norelax || (oP->format == COJ); /* TRUE or FALSE */
+ get_cdisp (arg[3], "COBR", instr, 13, var_frag, 0);
+
+ if (instrument_branches)
+ brcnt_emit ();
+ }
+}
+
+/* Assumptions about the passed-in text:
+ - all comments, labels removed
+ - text is an instruction
+ - all white space compressed to single blanks
+ - all character constants have been replaced with decimal. */
+
+void
+md_assemble (char *textP)
+{
+ /* Parsed instruction text, containing NO whitespace: arg[0]->opcode
+ mnemonic arg[1-3]->operands, with char constants replaced by
+ decimal numbers. */
+ char *args[4];
+ /* Number of instruction operands. */
+ int n_ops;
+ /* Pointer to instruction description. */
+ struct i960_opcode *oP;
+ /* TRUE iff opcode mnemonic included branch-prediction suffix (".f"
+ or ".t"). */
+ int branch_predict;
+ /* Setting of branch-prediction bit(s) to be OR'd into instruction
+ opcode of CTRL/COBR format instructions. */
+ long bp_bits;
+ /* Offset of last character in opcode mnemonic. */
+ int n;
+ const char *bp_error_msg = _("branch prediction invalid on this opcode");
+
+ /* Parse instruction into opcode and operands. */
+ memset (args, '\0', sizeof (args));
+
+ n_ops = i_scan (textP, args);
+
+ if (n_ops == -1)
+ return; /* Error message already issued. */
+
+ /* Do "macro substitution" (sort of) on 'ldconst' pseudo-instruction. */
+ if (!strcmp (args[0], "ldconst"))
+ {
+ n_ops = parse_ldconst (args);
+ if (n_ops == -1)
+ return;
+ }
+
+ /* Check for branch-prediction suffix on opcode mnemonic, strip it off. */
+ n = strlen (args[0]) - 1;
+ branch_predict = 0;
+ bp_bits = 0;
+
+ if (args[0][n - 1] == '.' && (args[0][n] == 't' || args[0][n] == 'f'))
+ {
+ /* We could check here to see if the target architecture
+ supports branch prediction, but why bother? The bit will
+ just be ignored by processors that don't use it. */
+ branch_predict = 1;
+ bp_bits = (args[0][n] == 't') ? BP_TAKEN : BP_NOT_TAKEN;
+ args[0][n - 1] = '\0'; /* Strip suffix from opcode mnemonic */
+ }
+
+ /* Look up opcode mnemonic in table and check number of operands.
+ Check that opcode is legal for the target architecture. If all
+ looks good, assemble instruction. */
+ oP = (struct i960_opcode *) hash_find (op_hash, args[0]);
+ if (!oP || !targ_has_iclass (oP->iclass))
+ as_bad (_("invalid opcode, \"%s\"."), args[0]);
+ else if (n_ops != oP->num_ops)
+ as_bad (_("improper number of operands. expecting %d, got %d"),
+ oP->num_ops, n_ops);
+ else
+ {
+ switch (oP->format)
+ {
+ case FBRA:
+ case CTRL:
+ ctrl_fmt (args[1], oP->opcode | bp_bits, oP->num_ops);
+ if (oP->format == FBRA)
+ /* Now generate a 'bno' to same arg */
+ ctrl_fmt (args[1], BNO | bp_bits, 1);
+ break;
+ case COBR:
+ case COJ:
+ cobr_fmt (args, oP->opcode | bp_bits, oP);
+ break;
+ case REG:
+ if (branch_predict)
+ as_warn ("%s", bp_error_msg);
+ reg_fmt (args, oP);
+ break;
+ case MEM1:
+ if (args[0][0] == 'c' && args[0][1] == 'a')
+ {
+ if (branch_predict)
+ as_warn ("%s", bp_error_msg);
+ mem_fmt (args, oP, 1);
+ break;
+ }
+ case MEM2:
+ case MEM4:
+ case MEM8:
+ case MEM12:
+ case MEM16:
+ if (branch_predict)
+ as_warn ("%s", bp_error_msg);
+ mem_fmt (args, oP, 0);
+ break;
+ case CALLJ:
+ if (branch_predict)
+ as_warn ("%s", bp_error_msg);
+ /* Output opcode & set up "fixup" (relocation); flag
+ relocation as 'callj' type. */
+ know (oP->num_ops == 1);
+ get_cdisp (args[1], "CTRL", oP->opcode, 24, 0, 1);
+ break;
+ default:
+ BAD_CASE (oP->format);
+ break;
+ }
+ }
+}
+
+void
+md_number_to_chars (char *buf,
+ valueT value,
+ int n)
+{
+ number_to_chars_littleendian (buf, value, n);
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+static void
+md_number_to_imm (char *buf, long val, int n)
+{
+ md_number_to_chars (buf, val, n);
+}
+
+static void
+md_number_to_field (char *instrP, /* Pointer to instruction to be fixed. */
+ long val, /* Address fixup value. */
+ bit_fixS *bfixP) /* Description of bit field to be fixed up. */
+{
+ int numbits; /* Length of bit field to be fixed. */
+ long instr; /* 32-bit instruction to be fixed-up. */
+ long sign; /* 0 or -1, according to sign bit of 'val'. */
+
+ /* Convert instruction back to host byte order. */
+ instr = md_chars_to_number (instrP, 4);
+
+ /* Surprise! -- we stored the number of bits to be modified rather
+ than a pointer to a structure. */
+ numbits = (int) (size_t) bfixP;
+ if (numbits == 1)
+ /* This is a no-op, stuck here by reloc_callj(). */
+ return;
+
+ know ((numbits == 13) || (numbits == 24));
+
+ /* Propagate sign bit of 'val' for the given number of bits. Result
+ should be all 0 or all 1. */
+ sign = val >> ((int) numbits - 1);
+ if (((val < 0) && (sign != -1))
+ || ((val > 0) && (sign != 0)))
+ as_bad (_("Fixup of %ld too large for field width of %d"),
+ val, numbits);
+ else
+ {
+ /* Put bit field into instruction and write back in target
+ * byte order. */
+ val &= ~(-1 << (int) numbits); /* Clear unused sign bits. */
+ instr |= val;
+ md_number_to_chars (instrP, instr, 4);
+ }
+}
+
+
+/* md_parse_option
+ Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option. For the 960, these are:
+
+ -norelax:
+ Conditional branch instructions that require displacements
+ greater than 13 bits (or that have external targets) should
+ generate errors. The default is to replace each such
+ instruction with the corresponding compare (or chkbit) and
+ branch instructions. Note that the Intel "j" cobr directives
+ are ALWAYS "de-optimized" in this way when necessary,
+ regardless of the setting of this option.
+
+ -b:
+ Add code to collect information about branches taken, for
+ later optimization of branch prediction bits by a separate
+ tool. COBR and CNTL format instructions have branch
+ prediction bits (in the CX architecture); if "BR" represents
+ an instruction in one of these classes, the following rep-
+ resents the code generated by the assembler:
+
+ call <increment routine>
+ .word 0 # pre-counter
+ Label: BR
+ call <increment routine>
+ .word 0 # post-counter
+
+ A table of all such "Labels" is also generated.
+
+ -AKA, -AKB, -AKC, -ASA, -ASB, -AMC, -ACA:
+ Select the 80960 architecture. Instructions or features not
+ supported by the selected architecture cause fatal errors.
+ The default is to generate code for any instruction or feature
+ that is supported by SOME version of the 960 (even if this
+ means mixing architectures!). */
+
+const char *md_shortopts = "A:b";
+struct option md_longopts[] =
+{
+#define OPTION_LINKRELAX (OPTION_MD_BASE)
+ {"linkrelax", no_argument, NULL, OPTION_LINKRELAX},
+ {"link-relax", no_argument, NULL, OPTION_LINKRELAX},
+#define OPTION_NORELAX (OPTION_MD_BASE + 1)
+ {"norelax", no_argument, NULL, OPTION_NORELAX},
+ {"no-relax", no_argument, NULL, OPTION_NORELAX},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+struct tabentry
+{
+ char *flag;
+ int arch;
+};
+static const struct tabentry arch_tab[] =
+{
+ {"KA", ARCH_KA},
+ {"KB", ARCH_KB},
+ {"SA", ARCH_KA}, /* Synonym for KA. */
+ {"SB", ARCH_KB}, /* Synonym for KB. */
+ {"KC", ARCH_MC}, /* Synonym for MC. */
+ {"MC", ARCH_MC},
+ {"CA", ARCH_CA},
+ {"JX", ARCH_JX},
+ {"HX", ARCH_HX},
+ {NULL, 0}
+};
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_LINKRELAX:
+ linkrelax = 1;
+ flag_keep_locals = 1;
+ break;
+
+ case OPTION_NORELAX:
+ norelax = 1;
+ break;
+
+ case 'b':
+ instrument_branches = 1;
+ break;
+
+ case 'A':
+ {
+ const struct tabentry *tp;
+ char *p = arg;
+
+ for (tp = arch_tab; tp->flag != NULL; tp++)
+ if (!strcmp (p, tp->flag))
+ break;
+
+ if (tp->flag == NULL)
+ {
+ as_bad (_("invalid architecture %s"), p);
+ return 0;
+ }
+ else
+ architecture = tp->arch;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ int i;
+
+ fprintf (stream, _("I960 options:\n"));
+ for (i = 0; arch_tab[i].flag; i++)
+ fprintf (stream, "%s-A%s", i ? " | " : "", arch_tab[i].flag);
+ fprintf (stream, _("\n\
+ specify variant of 960 architecture\n\
+-b add code to collect statistics about branches taken\n\
+-link-relax preserve individual alignment directives so linker\n\
+ can do relaxing (b.out format only)\n\
+-no-relax don't alter compare-and-branch instructions for\n\
+ long displacements\n"));
+}
+
+/* relax_cobr:
+ Replace cobr instruction in a code fragment with equivalent branch and
+ compare instructions, so it can reach beyond a 13-bit displacement.
+ Set up an address fix/relocation for the new branch instruction. */
+
+/* This "conditional jump" table maps cobr instructions into
+ equivalent compare and branch opcodes. */
+
+static const
+struct
+{
+ long compare;
+ long branch;
+}
+
+coj[] =
+{ /* COBR OPCODE: */
+ { CHKBIT, BNO }, /* 0x30 - bbc */
+ { CMPO, BG }, /* 0x31 - cmpobg */
+ { CMPO, BE }, /* 0x32 - cmpobe */
+ { CMPO, BGE }, /* 0x33 - cmpobge */
+ { CMPO, BL }, /* 0x34 - cmpobl */
+ { CMPO, BNE }, /* 0x35 - cmpobne */
+ { CMPO, BLE }, /* 0x36 - cmpoble */
+ { CHKBIT, BO }, /* 0x37 - bbs */
+ { CMPI, BNO }, /* 0x38 - cmpibno */
+ { CMPI, BG }, /* 0x39 - cmpibg */
+ { CMPI, BE }, /* 0x3a - cmpibe */
+ { CMPI, BGE }, /* 0x3b - cmpibge */
+ { CMPI, BL }, /* 0x3c - cmpibl */
+ { CMPI, BNE }, /* 0x3d - cmpibne */
+ { CMPI, BLE }, /* 0x3e - cmpible */
+ { CMPI, BO }, /* 0x3f - cmpibo */
+};
+
+static void
+relax_cobr (fragS *fragP) /* fragP->fr_opcode is assumed to point to
+ the cobr instruction, which comes at the
+ end of the code fragment. */
+{
+ int opcode, src1, src2, m1, s2;
+ /* Bit fields from cobr instruction. */
+ long bp_bits; /* Branch prediction bits from cobr instruction. */
+ long instr; /* A single i960 instruction. */
+ /* ->instruction to be replaced. */
+ char *iP;
+ fixS *fixP; /* Relocation that can be done at assembly time. */
+
+ /* Pick up & parse cobr instruction. */
+ iP = fragP->fr_opcode;
+ instr = md_chars_to_number (iP, 4);
+ opcode = ((instr >> 24) & 0xff) - 0x30; /* "-0x30" for table index. */
+ src1 = (instr >> 19) & 0x1f;
+ m1 = (instr >> 13) & 1;
+ s2 = instr & 1;
+ src2 = (instr >> 14) & 0x1f;
+ bp_bits = instr & BP_MASK;
+
+ /* Generate and output compare instruction. */
+ instr = coj[opcode].compare
+ | src1 | (m1 << 11) | (s2 << 6) | (src2 << 14);
+ md_number_to_chars (iP, instr, 4);
+
+ /* Output branch instruction. */
+ md_number_to_chars (iP + 4, coj[opcode].branch | bp_bits, 4);
+
+ /* Set up address fixup/relocation. */
+ fixP = fix_new (fragP,
+ iP + 4 - fragP->fr_literal,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 1,
+ NO_RELOC);
+
+ fixP->fx_bit_fixP = (bit_fixS *) 24; /* Store size of bit field. */
+
+ fragP->fr_fix += 4;
+ frag_wane (fragP);
+}
+
+/* md_convert_frag:
+
+ Called by base assembler after address relaxation is finished: modify
+ variable fragments according to how much relaxation was done.
+
+ If the fragment substate is still 1, a 13-bit displacement was enough
+ to reach the symbol in question. Set up an address fixup, but otherwise
+ leave the cobr instruction alone.
+
+ If the fragment substate is 2, a 13-bit displacement was not enough.
+ Replace the cobr with a two instructions (a compare and a branch). */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ /* Structure describing needed address fix. */
+ fixS *fixP;
+
+ switch (fragP->fr_subtype)
+ {
+ case 1:
+ /* Leave single cobr instruction. */
+ fixP = fix_new (fragP,
+ fragP->fr_opcode - fragP->fr_literal,
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 1,
+ NO_RELOC);
+
+ fixP->fx_bit_fixP = (bit_fixS *) 13; /* Size of bit field. */
+ break;
+ case 2:
+ /* Replace cobr with compare/branch instructions. */
+ relax_cobr (fragP);
+ break;
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+}
+
+/* md_estimate_size_before_relax: How much does it look like *fragP will grow?
+
+ Called by base assembler just before address relaxation.
+ Return the amount by which the fragment will grow.
+
+ Any symbol that is now undefined will not become defined; cobr's
+ based on undefined symbols will have to be replaced with a compare
+ instruction and a branch instruction, and the code fragment will grow
+ by 4 bytes. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment_type)
+{
+ /* If symbol is undefined in this segment, go to "relaxed" state
+ (compare and branch instructions instead of cobr) right now. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment_type)
+ {
+ relax_cobr (fragP);
+ return 4;
+ }
+
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+
+/* md_ri_to_chars:
+ This routine exists in order to overcome machine byte-order problems
+ when dealing with bit-field entries in the relocation_info struct.
+
+ But relocation info will be used on the host machine only (only
+ executable code is actually downloaded to the i80960). Therefore,
+ we leave it in host byte order. */
+
+static void
+md_ri_to_chars (char *where, struct relocation_info *ri)
+{
+ host_number_to_chars (where, ri->r_address, 4);
+ host_number_to_chars (where + 4, ri->r_index, 3);
+#if WORDS_BIGENDIAN
+ where[7] = (ri->r_pcrel << 7
+ | ri->r_length << 5
+ | ri->r_extern << 4
+ | ri->r_bsr << 3
+ | ri->r_disp << 2
+ | ri->r_callj << 1
+ | ri->nuthin << 0);
+#else
+ where[7] = (ri->r_pcrel << 0
+ | ri->r_length << 1
+ | ri->r_extern << 3
+ | ri->r_bsr << 4
+ | ri->r_disp << 5
+ | ri->r_callj << 6
+ | ri->nuthin << 7);
+#endif
+}
+
+#endif /* defined(OBJ_AOUT) | defined(OBJ_BOUT) */
+
+
+/* brtab_emit: generate the fetch-prediction branch table.
+
+ See the comments above the declaration of 'br_cnt' for details on
+ branch-prediction instrumentation.
+
+ The code emitted here would be functionally equivalent to the following
+ example assembler source.
+
+ .data
+ .align 2
+ BR_TAB_NAME:
+ .word 0 # link to next table
+ .word 3 # length of table
+ .word LBRANCH0 # 1st entry in table proper
+ .word LBRANCH1
+ .word LBRANCH2 */
+
+void
+brtab_emit (void)
+{
+ int i;
+ char buf[20];
+ /* Where the binary was output to. */
+ char *p;
+
+ if (!instrument_branches)
+ return;
+
+ subseg_set (data_section, 0); /* .data */
+ frag_align (2, 0, 0); /* .align 2 */
+ record_alignment (now_seg, 2);
+ colon (BR_TAB_NAME); /* BR_TAB_NAME: */
+ emit (0); /* .word 0 #link to next table */
+ emit (br_cnt); /* .word n #length of table */
+
+ for (i = 0; i < br_cnt; i++)
+ {
+ sprintf (buf, "%s%d", BR_LABEL_BASE, i);
+ p = emit (0);
+ fix_new (frag_now,
+ p - frag_now->fr_literal,
+ 4, symbol_find (buf), 0, 0, NO_RELOC);
+ }
+}
+
+/* s_leafproc: process .leafproc pseudo-op
+
+ .leafproc takes two arguments, the second one is optional:
+ arg[1]: name of 'call' entry point to leaf procedure
+ arg[2]: name of 'bal' entry point to leaf procedure
+
+ If the two arguments are identical, or if the second one is missing,
+ the first argument is taken to be the 'bal' entry point.
+
+ If there are 2 distinct arguments, we must make sure that the 'bal'
+ entry point immediately follows the 'call' entry point in the linked
+ list of symbols. */
+
+static void
+s_leafproc (int n_ops, /* Number of operands. */
+ char *args[]) /* args[1]->1st operand, args[2]->2nd operand. */
+{
+ symbolS *callP; /* Pointer to leafproc 'call' entry point symbol. */
+ symbolS *balP; /* Pointer to leafproc 'bal' entry point symbol. */
+
+ if ((n_ops != 1) && (n_ops != 2))
+ {
+ as_bad (_("should have 1 or 2 operands"));
+ return;
+ }
+
+ /* Find or create symbol for 'call' entry point. */
+ callP = symbol_find_or_make (args[1]);
+
+ if (TC_S_IS_CALLNAME (callP))
+ as_warn (_("Redefining leafproc %s"), S_GET_NAME (callP));
+
+ /* If that was the only argument, use it as the 'bal' entry point.
+ Otherwise, mark it as the 'call' entry point and find or create
+ another symbol for the 'bal' entry point. */
+ if ((n_ops == 1) || !strcmp (args[1], args[2]))
+ {
+ TC_S_FORCE_TO_BALNAME (callP);
+ }
+ else
+ {
+ TC_S_FORCE_TO_CALLNAME (callP);
+
+ balP = symbol_find_or_make (args[2]);
+ if (TC_S_IS_CALLNAME (balP))
+ as_warn (_("Redefining leafproc %s"), S_GET_NAME (balP));
+
+ TC_S_FORCE_TO_BALNAME (balP);
+
+#ifndef OBJ_ELF
+ tc_set_bal_of_call (callP, balP);
+#endif
+ }
+}
+
+/* s_sysproc: process .sysproc pseudo-op
+
+ .sysproc takes two arguments:
+ arg[1]: name of entry point to system procedure
+ arg[2]: 'entry_num' (index) of system procedure in the range
+ [0,31] inclusive.
+
+ For [ab].out, we store the 'entrynum' in the 'n_other' field of
+ the symbol. Since that entry is normally 0, we bias 'entrynum'
+ by adding 1 to it. It must be unbiased before it is used. */
+
+static void
+s_sysproc (int n_ops, /* Number of operands. */
+ char *args[]) /* args[1]->1st operand, args[2]->2nd operand. */
+{
+ expressionS exp;
+ symbolS *symP;
+
+ if (n_ops != 2)
+ {
+ as_bad (_("should have two operands"));
+ return;
+ }
+
+ /* Parse "entry_num" argument and check it for validity. */
+ parse_expr (args[2], &exp);
+ if (exp.X_op != O_constant
+ || (offs (exp) < 0)
+ || (offs (exp) > 31))
+ {
+ as_bad (_("'entry_num' must be absolute number in [0,31]"));
+ return;
+ }
+
+ /* Find/make symbol and stick entry number (biased by +1) into it. */
+ symP = symbol_find_or_make (args[1]);
+
+ if (TC_S_IS_SYSPROC (symP))
+ as_warn (_("Redefining entrynum for sysproc %s"), S_GET_NAME (symP));
+
+ TC_S_SET_SYSPROC (symP, offs (exp)); /* Encode entry number. */
+ TC_S_FORCE_TO_SYSPROC (symP);
+}
+
+/* parse_po: parse machine-dependent pseudo-op
+
+ This is a top-level routine for machine-dependent pseudo-ops. It slurps
+ up the rest of the input line, breaks out the individual arguments,
+ and dispatches them to the correct handler. */
+
+static void
+parse_po (int po_num) /* Pseudo-op number: currently S_LEAFPROC or S_SYSPROC. */
+{
+ /* Pointers operands, with no embedded whitespace.
+ arg[0] unused, arg[1-3]->operands. */
+ char *args[4];
+ int n_ops; /* Number of operands. */
+ char *p; /* Pointer to beginning of unparsed argument string. */
+ char eol; /* Character that indicated end of line. */
+
+ extern char is_end_of_line[];
+
+ /* Advance input pointer to end of line. */
+ p = input_line_pointer;
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+
+ eol = *input_line_pointer; /* Save end-of-line char. */
+ *input_line_pointer = '\0'; /* Terminate argument list. */
+
+ /* Parse out operands. */
+ n_ops = get_args (p, args);
+ if (n_ops == -1)
+ return;
+
+ /* Dispatch to correct handler. */
+ switch (po_num)
+ {
+ case S_SYSPROC:
+ s_sysproc (n_ops, args);
+ break;
+ case S_LEAFPROC:
+ s_leafproc (n_ops, args);
+ break;
+ default:
+ BAD_CASE (po_num);
+ break;
+ }
+
+ /* Restore eol, so line numbers get updated correctly. Base
+ assembler assumes we leave input pointer pointing at char
+ following the eol. */
+ *input_line_pointer++ = eol;
+}
+
+/* reloc_callj: Relocate a 'callj' instruction
+
+ This is a "non-(GNU)-standard" machine-dependent hook. The base
+ assembler calls it when it decides it can relocate an address at
+ assembly time instead of emitting a relocation directive.
+
+ Check to see if the relocation involves a 'callj' instruction to a:
+ sysproc: Replace the default 'call' instruction with a 'calls'
+ leafproc: Replace the default 'call' instruction with a 'bal'.
+ other proc: Do nothing.
+
+ See b.out.h for details on the 'n_other' field in a symbol structure.
+
+ IMPORTANT!:
+ Assumes the caller has already figured out, in the case of a leafproc,
+ to use the 'bal' entry point, and has substituted that symbol into the
+ passed fixup structure. */
+
+int
+reloc_callj (fixS *fixP) /* Relocation that can be done at assembly time. */
+{
+ /* Points to the binary for the instruction being relocated. */
+ char *where;
+
+ if (!fixP->fx_tcbit)
+ /* This wasn't a callj instruction in the first place. */
+ return 0;
+
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (TC_S_IS_SYSPROC (fixP->fx_addsy))
+ {
+ /* Symbol is a .sysproc: replace 'call' with 'calls'. System
+ procedure number is (other-1). */
+ md_number_to_chars (where, CALLS | TC_S_GET_SYSPROC (fixP->fx_addsy), 4);
+
+ /* Nothing else needs to be done for this instruction. Make
+ sure 'md_number_to_field()' will perform a no-op. */
+ fixP->fx_bit_fixP = (bit_fixS *) 1;
+ }
+ else if (TC_S_IS_CALLNAME (fixP->fx_addsy))
+ {
+ /* Should not happen: see block comment above. */
+ as_fatal (_("Trying to 'bal' to %s"), S_GET_NAME (fixP->fx_addsy));
+ }
+ else if (TC_S_IS_BALNAME (fixP->fx_addsy))
+ {
+ /* Replace 'call' with 'bal'; both instructions have the same
+ format, so calling code should complete relocation as if
+ nothing happened here. */
+ md_number_to_chars (where, BAL, 4);
+ }
+ else if (TC_S_IS_BADPROC (fixP->fx_addsy))
+ as_bad (_("Looks like a proc, but can't tell what kind.\n"));
+
+ /* Otherwise Symbol is neither a sysproc nor a leafproc. */
+ return 0;
+}
+
+/* Handle the MRI .endian pseudo-op. */
+
+static void
+s_endian (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ if (strcasecmp (name, "little") == 0)
+ ;
+ else if (strcasecmp (name, "big") == 0)
+ as_bad (_("big endian mode is not supported"));
+ else
+ as_warn (_("ignoring unrecognized .endian type `%s'"), name);
+
+ *input_line_pointer = c;
+
+ demand_empty_rest_of_line ();
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the i960, they're relative to the address of the instruction,
+ which we have set up as the address of the fixup too. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+void
+md_apply_fix (fixS *fixP,
+ valueT *valP,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ long val = *valP;
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (!fixP->fx_bit_fixP)
+ {
+ md_number_to_imm (place, val, fixP->fx_size);
+ }
+ else if ((int) (size_t) fixP->fx_bit_fixP == 13
+ && fixP->fx_addsy != NULL
+ && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section)
+ {
+ /* This is a COBR instruction. They have only a
+ 13-bit displacement and are only to be used
+ for local branches: flag as error, don't generate
+ relocation. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("can't use COBR format with external label"));
+ fixP->fx_addsy = NULL;
+ }
+ else
+ md_number_to_field (place, val, fixP->fx_bit_fixP);
+
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+}
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+void
+tc_bout_fix_to_chars (char *where,
+ fixS *fixP,
+ relax_addressT segment_address_in_file)
+{
+ static const unsigned char nbytes_r_length[] = {42, 0, 1, 42, 2};
+ struct relocation_info ri;
+ symbolS *symbolP;
+
+ memset ((char *) &ri, '\0', sizeof (ri));
+ symbolP = fixP->fx_addsy;
+ know (symbolP != 0 || fixP->fx_r_type != NO_RELOC);
+ ri.r_bsr = fixP->fx_bsr; /*SAC LD RELAX HACK */
+ /* These two 'cuz of NS32K */
+ ri.r_callj = fixP->fx_tcbit;
+ if (fixP->fx_bit_fixP)
+ ri.r_length = 2;
+ else
+ ri.r_length = nbytes_r_length[fixP->fx_size];
+ ri.r_pcrel = fixP->fx_pcrel;
+ ri.r_address = fixP->fx_frag->fr_address + fixP->fx_where - segment_address_in_file;
+
+ if (fixP->fx_r_type != NO_RELOC)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case rs_align:
+ ri.r_index = -2;
+ ri.r_pcrel = 1;
+ ri.r_length = fixP->fx_size - 1;
+ break;
+ case rs_org:
+ ri.r_index = -2;
+ ri.r_pcrel = 0;
+ break;
+ case rs_fill:
+ ri.r_index = -1;
+ break;
+ default:
+ abort ();
+ }
+ ri.r_extern = 0;
+ }
+ else if (linkrelax || !S_IS_DEFINED (symbolP) || fixP->fx_bsr)
+ {
+ ri.r_extern = 1;
+ ri.r_index = symbolP->sy_number;
+ }
+ else
+ {
+ ri.r_extern = 0;
+ ri.r_index = S_GET_TYPE (symbolP);
+ }
+
+ /* Output the relocation information in machine-dependent form. */
+ md_ri_to_chars (where, &ri);
+}
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+/* Align an address by rounding it up to the specified boundary. */
+
+valueT
+md_section_align (segT seg,
+ valueT addr) /* Address to be rounded up. */
+{
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, seg);
+ return (addr + (1 << align) - 1) & (-1 << align);
+}
+
+extern int coff_flags;
+
+/* For aout or bout, the bal immediately follows the call.
+
+ For coff, we cheat and store a pointer to the bal symbol in the
+ second aux entry of the call. */
+
+#undef OBJ_ABOUT
+#ifdef OBJ_AOUT
+#define OBJ_ABOUT
+#endif
+#ifdef OBJ_BOUT
+#define OBJ_ABOUT
+#endif
+
+void
+tc_set_bal_of_call (symbolS *callP ATTRIBUTE_UNUSED,
+ symbolS *balP ATTRIBUTE_UNUSED)
+{
+ know (TC_S_IS_CALLNAME (callP));
+ know (TC_S_IS_BALNAME (balP));
+
+#ifdef OBJ_COFF
+
+ callP->sy_tc = balP;
+ S_SET_NUMBER_AUXILIARY (callP, 2);
+
+#else /* ! OBJ_COFF */
+#ifdef OBJ_ABOUT
+
+ /* If the 'bal' entry doesn't immediately follow the 'call'
+ symbol, unlink it from the symbol list and re-insert it. */
+ if (symbol_next (callP) != balP)
+ {
+ symbol_remove (balP, &symbol_rootP, &symbol_lastP);
+ symbol_append (balP, callP, &symbol_rootP, &symbol_lastP);
+ } /* if not in order */
+
+#else /* ! OBJ_ABOUT */
+ as_fatal ("Only supported for a.out, b.out, or COFF");
+#endif /* ! OBJ_ABOUT */
+#endif /* ! OBJ_COFF */
+}
+
+symbolS *
+tc_get_bal_of_call (symbolS *callP ATTRIBUTE_UNUSED)
+{
+ symbolS *retval;
+
+ know (TC_S_IS_CALLNAME (callP));
+
+#ifdef OBJ_COFF
+ retval = callP->sy_tc;
+#else
+#ifdef OBJ_ABOUT
+ retval = symbol_next (callP);
+#else
+ as_fatal ("Only supported for a.out, b.out, or COFF");
+#endif /* ! OBJ_ABOUT */
+#endif /* ! OBJ_COFF */
+
+ know (TC_S_IS_BALNAME (retval));
+ return retval;
+}
+
+#ifdef OBJ_COFF
+void
+tc_coff_symbol_emit_hook (symbolS *symbolP ATTRIBUTE_UNUSED)
+{
+ if (TC_S_IS_CALLNAME (symbolP))
+ {
+ symbolS *balP = tc_get_bal_of_call (symbolP);
+
+ symbolP->sy_symbol.ost_auxent[1].x_bal.x_balntry = S_GET_VALUE (balP);
+ if (S_GET_STORAGE_CLASS (symbolP) == C_EXT)
+ S_SET_STORAGE_CLASS (symbolP, C_LEAFEXT);
+ else
+ S_SET_STORAGE_CLASS (symbolP, C_LEAFSTAT);
+ S_SET_DATA_TYPE (symbolP, S_GET_DATA_TYPE (symbolP) | (DT_FCN << N_BTSHFT));
+ /* Fix up the bal symbol. */
+ S_SET_STORAGE_CLASS (balP, C_LABEL);
+ }
+}
+#endif /* OBJ_COFF */
+
+void
+i960_handle_align (fragS *fragp ATTRIBUTE_UNUSED)
+{
+ if (!linkrelax)
+ return;
+
+#ifndef OBJ_BOUT
+ as_bad (_("option --link-relax is only supported in b.out format"));
+ linkrelax = 0;
+ return;
+#else
+
+ /* The text section "ends" with another alignment reloc, to which we
+ aren't adding padding. */
+ if (fragp->fr_next == text_last_frag
+ || fragp->fr_next == data_last_frag)
+ return;
+
+ /* alignment directive */
+ fix_new (fragp, fragp->fr_fix, fragp->fr_offset, 0, 0, 0,
+ (int) fragp->fr_type);
+#endif /* OBJ_BOUT */
+}
+
+int
+i960_validate_fix (fixS *fixP, segT this_segment_type ATTRIBUTE_UNUSED)
+{
+ if (fixP->fx_tcbit && TC_S_IS_CALLNAME (fixP->fx_addsy))
+ {
+ /* Relocation should be done via the associated 'bal'
+ entry point symbol. */
+ if (!TC_S_IS_BALNAME (tc_get_bal_of_call (fixP->fx_addsy)))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("No 'bal' entry point for leafproc %s"),
+ S_GET_NAME (fixP->fx_addsy));
+ return 0;
+ }
+ fixP->fx_addsy = tc_get_bal_of_call (fixP->fx_addsy);
+ }
+
+ return 1;
+}
+
+/* From cgen.c: */
+
+static short
+tc_bfd_fix2rtype (fixS *fixP)
+{
+ if (fixP->fx_pcrel == 0 && fixP->fx_size == 4)
+ return BFD_RELOC_32;
+
+ if (fixP->fx_pcrel != 0 && fixP->fx_size == 4)
+ return BFD_RELOC_24_PCREL;
+
+ abort ();
+ return 0;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format.
+
+ FIXME: To what extent can we get all relevant targets to use this? */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
+{
+ arelent * reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+
+ /* HACK: Is this right? */
+ fixP->fx_r_type = tc_bfd_fix2rtype (fixP);
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ return NULL;
+ }
+
+ gas_assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ reloc->addend = fixP->fx_addnumber;
+
+ return reloc;
+}
+
+/* end from cgen.c */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"bss", s_lcomm, 1},
+ {"endian", s_endian, 0},
+ {"extended", float_cons, 't'},
+ {"leafproc", parse_po, S_LEAFPROC},
+ {"sysproc", parse_po, S_SYSPROC},
+
+ {"word", cons, 4},
+ {"quad", cons, 16},
+
+ {0, 0, 0}
+};
diff --git a/gas/config/tc-i960.h b/gas/config/tc-i960.h
new file mode 100644
index 0000000..ee6d050
--- /dev/null
+++ b/gas/config/tc-i960.h
@@ -0,0 +1,186 @@
+/* tc-i960.h - Basic 80960 instruction formats.
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_I960
+#define TC_I960 1
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-i960"
+#define TARGET_ARCH bfd_arch_i960
+#endif
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define WORKING_DOT_WORD
+
+/*
+ * The 'COJ' instructions are actually COBR instructions with the 'b' in
+ * the mnemonic replaced by a 'j'; they are ALWAYS "de-optimized" if necessary:
+ * if the displacement will not fit in 13 bits, the assembler will replace them
+ * with the corresponding compare and branch instructions.
+ *
+ * All of the 'MEMn' instructions are the same format; the 'n' in the name
+ * indicates the default index scale factor (the size of the datum operated on).
+ *
+ * The FBRA formats are not actually an instruction format. They are the
+ * "convenience directives" for branching on floating-point comparisons,
+ * each of which generates 2 instructions (a 'bno' and one other branch).
+ *
+ * The CALLJ format is not actually an instruction format. It indicates that
+ * the instruction generated (a CTRL-format 'call') should have its relocation
+ * specially flagged for link-time replacement with a 'bal' or 'calls' if
+ * appropriate.
+ */
+
+/* tailor gas */
+#define LOCAL_LABELS_FB 1
+#define BITFIELD_CONS_EXPRESSIONS
+
+/* tailor the coff format */
+#define COFF_MAGIC I960ROMAGIC
+#define OBJ_COFF_MAX_AUXENTRIES (2)
+
+/* MEANING OF 'n_other' in the symbol record.
+ *
+ * If non-zero, the 'n_other' fields indicates either a leaf procedure or
+ * a system procedure, as follows:
+ *
+ * 1 <= n_other <= 32 :
+ * The symbol is the entry point to a system procedure.
+ * 'n_value' is the address of the entry, as for any other
+ * procedure. The system procedure number (which can be used in
+ * a 'calls' instruction) is (n_other-1). These entries come from
+ * '.sysproc' directives.
+ *
+ * n_other == N_CALLNAME
+ * the symbol is the 'call' entry point to a leaf procedure.
+ * The *next* symbol in the symbol table must be the corresponding
+ * 'bal' entry point to the procedure (see following). These
+ * entries come from '.leafproc' directives in which two different
+ * symbols are specified (the first one is represented here).
+ *
+ *
+ * n_other == N_BALNAME
+ * the symbol is the 'bal' entry point to a leaf procedure.
+ * These entries result from '.leafproc' directives in which only
+ * one symbol is specified, or in which the same symbol is
+ * specified twice.
+ *
+ * Note that an N_CALLNAME entry *must* have a corresponding N_BALNAME entry,
+ * but not every N_BALNAME entry must have an N_CALLNAME entry.
+ */
+#define N_CALLNAME ((char)-1)
+#define N_BALNAME ((char)-2)
+
+/* i960 uses a custom relocation record. */
+
+/* let obj-aout.h know */
+#define CUSTOM_RELOC_FORMAT 1
+/* let aout_gnu.h know */
+#define N_RELOCATION_INFO_DECLARED 1
+struct relocation_info
+ {
+ int r_address; /* File address of item to be relocated */
+ unsigned
+ r_index:24, /* Index of symbol on which relocation is based*/
+ r_pcrel:1, /* 1 => relocate PC-relative; else absolute
+ * On i960, pc-relative implies 24-bit
+ * address, absolute implies 32-bit.
+ */
+ r_length:2, /* Number of bytes to relocate:
+ * 0 => 1 byte
+ * 1 => 2 bytes
+ * 2 => 4 bytes -- only value used for i960
+ */
+ r_extern:1, r_bsr:1, /* Something for the GNU NS32K assembler */
+ r_disp:1, /* Something for the GNU NS32K assembler */
+ r_callj:1, /* 1 if relocation target is an i960 'callj' */
+ nuthin:1; /* Unused */
+ };
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* Makes no sense to use the difference of 2 arbitrary symbols
+ as the target of a call instruction. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+ ((FIX)->fx_tcbit \
+ || ! SEG_NORMAL (SEG) \
+ || TC_FORCE_RELOCATION (FIX))
+
+/* reloc_callj() may replace a 'call' with a 'calls' or a
+ 'bal', in which cases it modifies *fixP as appropriate.
+ In the case of a 'calls', no further work is required. */
+extern int reloc_callj (struct fix *);
+
+#define TC_FORCE_RELOCATION_ABS(FIX) \
+ (TC_FORCE_RELOCATION (FIX) \
+ || reloc_callj (FIX))
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || TC_FORCE_RELOCATION (FIX) \
+ || reloc_callj (FIX))
+
+#ifdef OBJ_COFF
+
+/* We store the bal information in the sy_tc field. */
+#define TC_SYMFIELD_TYPE symbolS *
+
+#endif
+
+extern int i960_validate_fix (struct fix *, segT);
+#define TC_VALIDATE_FIX(FIX,SEGTYPE,LABEL) \
+ if (!i960_validate_fix (FIX, SEGTYPE)) goto LABEL
+
+#define tc_fix_adjustable(FIX) ((FIX)->fx_bsr == 0)
+
+#ifndef OBJ_ELF
+/* Values passed to md_apply_fix sometimes include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) tc_fix_adjustable (FIX)
+#else
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#endif
+
+extern void brtab_emit (void);
+#define md_end() brtab_emit ()
+
+extern void tc_set_bal_of_call (symbolS *, symbolS *);
+
+extern struct symbol *tc_get_bal_of_call (symbolS *);
+
+extern void i960_handle_align (struct frag *);
+#define HANDLE_ALIGN(FRAG) i960_handle_align (FRAG)
+#define NO_RELOC -1
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define LINKER_RELAXING_SHRINKS_ONLY
+
+#define TC_FIX_TYPE struct { unsigned bsr : 1; }
+#define fx_bsr tc_fix_data.bsr
+#define TC_INIT_FIX_DATA(F) ((F)->tc_fix_data.bsr = 0)
+
+#endif
diff --git a/gas/config/tc-ia64.c b/gas/config/tc-ia64.c
new file mode 100644
index 0000000..38b6b67
--- /dev/null
+++ b/gas/config/tc-ia64.c
@@ -0,0 +1,12070 @@
+/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/*
+ TODO:
+
+ - optional operands
+ - directives:
+ .eb
+ .estate
+ .lb
+ .popsection
+ .previous
+ .psr
+ .pushsection
+ - labels are wrong if automatic alignment is introduced
+ (e.g., checkout the second real10 definition in test-data.s)
+ - DV-related stuff:
+ <reg>.safe_across_calls and any other DV-related directives I don't
+ have documentation for.
+ verify mod-sched-brs reads/writes are checked/marked (and other
+ notes)
+
+ */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "subsegs.h"
+
+#include "opcode/ia64.h"
+
+#include "elf/ia64.h"
+#include "bfdver.h"
+#include <time.h>
+
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#define NELEMS(a) ((int) (sizeof (a)/sizeof ((a)[0])))
+
+/* Some systems define MIN in, e.g., param.h. */
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#define NUM_SLOTS 4
+#define PREV_SLOT md.slot[(md.curr_slot + NUM_SLOTS - 1) % NUM_SLOTS]
+#define CURR_SLOT md.slot[md.curr_slot]
+
+#define O_pseudo_fixup (O_max + 1)
+
+enum special_section
+ {
+ /* IA-64 ABI section pseudo-ops. */
+ SPECIAL_SECTION_BSS = 0,
+ SPECIAL_SECTION_SBSS,
+ SPECIAL_SECTION_SDATA,
+ SPECIAL_SECTION_RODATA,
+ SPECIAL_SECTION_COMMENT,
+ SPECIAL_SECTION_UNWIND,
+ SPECIAL_SECTION_UNWIND_INFO,
+ /* HPUX specific section pseudo-ops. */
+ SPECIAL_SECTION_INIT_ARRAY,
+ SPECIAL_SECTION_FINI_ARRAY,
+ };
+
+enum reloc_func
+ {
+ FUNC_DTP_MODULE,
+ FUNC_DTP_RELATIVE,
+ FUNC_FPTR_RELATIVE,
+ FUNC_GP_RELATIVE,
+ FUNC_LT_RELATIVE,
+ FUNC_LT_RELATIVE_X,
+ FUNC_PC_RELATIVE,
+ FUNC_PLT_RELATIVE,
+ FUNC_SEC_RELATIVE,
+ FUNC_SEG_RELATIVE,
+ FUNC_TP_RELATIVE,
+ FUNC_LTV_RELATIVE,
+ FUNC_LT_FPTR_RELATIVE,
+ FUNC_LT_DTP_MODULE,
+ FUNC_LT_DTP_RELATIVE,
+ FUNC_LT_TP_RELATIVE,
+ FUNC_IPLT_RELOC,
+#ifdef TE_VMS
+ FUNC_SLOTCOUNT_RELOC,
+#endif
+ };
+
+enum reg_symbol
+ {
+ REG_GR = 0,
+ REG_FR = (REG_GR + 128),
+ REG_AR = (REG_FR + 128),
+ REG_CR = (REG_AR + 128),
+ REG_DAHR = (REG_CR + 128),
+ REG_P = (REG_DAHR + 8),
+ REG_BR = (REG_P + 64),
+ REG_IP = (REG_BR + 8),
+ REG_CFM,
+ REG_PR,
+ REG_PR_ROT,
+ REG_PSR,
+ REG_PSR_L,
+ REG_PSR_UM,
+ /* The following are pseudo-registers for use by gas only. */
+ IND_CPUID,
+ IND_DBR,
+ IND_DTR,
+ IND_ITR,
+ IND_IBR,
+ IND_MSR,
+ IND_PKR,
+ IND_PMC,
+ IND_PMD,
+ IND_DAHR,
+ IND_RR,
+ /* The following pseudo-registers are used for unwind directives only: */
+ REG_PSP,
+ REG_PRIUNAT,
+ REG_NUM
+ };
+
+enum dynreg_type
+ {
+ DYNREG_GR = 0, /* dynamic general purpose register */
+ DYNREG_FR, /* dynamic floating point register */
+ DYNREG_PR, /* dynamic predicate register */
+ DYNREG_NUM_TYPES
+ };
+
+enum operand_match_result
+ {
+ OPERAND_MATCH,
+ OPERAND_OUT_OF_RANGE,
+ OPERAND_MISMATCH
+ };
+
+/* On the ia64, we can't know the address of a text label until the
+ instructions are packed into a bundle. To handle this, we keep
+ track of the list of labels that appear in front of each
+ instruction. */
+struct label_fix
+{
+ struct label_fix *next;
+ struct symbol *sym;
+ bfd_boolean dw2_mark_labels;
+};
+
+#ifdef TE_VMS
+/* An internally used relocation. */
+#define DUMMY_RELOC_IA64_SLOTCOUNT (BFD_RELOC_UNUSED + 1)
+#endif
+
+/* This is the endianness of the current section. */
+extern int target_big_endian;
+
+/* This is the default endianness. */
+static int default_big_endian = TARGET_BYTES_BIG_ENDIAN;
+
+void (*ia64_number_to_chars) (char *, valueT, int);
+
+static void ia64_float_to_chars_bigendian (char *, LITTLENUM_TYPE *, int);
+static void ia64_float_to_chars_littleendian (char *, LITTLENUM_TYPE *, int);
+
+static void (*ia64_float_to_chars) (char *, LITTLENUM_TYPE *, int);
+
+static struct hash_control *alias_hash;
+static struct hash_control *alias_name_hash;
+static struct hash_control *secalias_hash;
+static struct hash_control *secalias_name_hash;
+
+/* List of chars besides those in app.c:symbol_chars that can start an
+ operand. Used to prevent the scrubber eating vital white-space. */
+const char ia64_symbol_chars[] = "@?";
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";{}";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* ia64-specific option processing: */
+
+const char *md_shortopts = "m:N:x::";
+
+struct option md_longopts[] =
+ {
+#define OPTION_MCONSTANT_GP (OPTION_MD_BASE + 1)
+ {"mconstant-gp", no_argument, NULL, OPTION_MCONSTANT_GP},
+#define OPTION_MAUTO_PIC (OPTION_MD_BASE + 2)
+ {"mauto-pic", no_argument, NULL, OPTION_MAUTO_PIC}
+ };
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+static struct
+ {
+ struct hash_control *pseudo_hash; /* pseudo opcode hash table */
+ struct hash_control *reg_hash; /* register name hash table */
+ struct hash_control *dynreg_hash; /* dynamic register hash table */
+ struct hash_control *const_hash; /* constant hash table */
+ struct hash_control *entry_hash; /* code entry hint hash table */
+
+ /* If X_op is != O_absent, the registername for the instruction's
+ qualifying predicate. If NULL, p0 is assumed for instructions
+ that are predictable. */
+ expressionS qp;
+
+ /* Optimize for which CPU. */
+ enum
+ {
+ itanium1,
+ itanium2
+ } tune;
+
+ /* What to do when hint.b is used. */
+ enum
+ {
+ hint_b_error,
+ hint_b_warning,
+ hint_b_ok
+ } hint_b;
+
+ unsigned int
+ manual_bundling : 1,
+ debug_dv: 1,
+ detect_dv: 1,
+ explicit_mode : 1, /* which mode we're in */
+ default_explicit_mode : 1, /* which mode is the default */
+ mode_explicitly_set : 1, /* was the current mode explicitly set? */
+ auto_align : 1,
+ keep_pending_output : 1;
+
+ /* What to do when something is wrong with unwind directives. */
+ enum
+ {
+ unwind_check_warning,
+ unwind_check_error
+ } unwind_check;
+
+ /* Each bundle consists of up to three instructions. We keep
+ track of four most recent instructions so we can correctly set
+ the end_of_insn_group for the last instruction in a bundle. */
+ int curr_slot;
+ int num_slots_in_use;
+ struct slot
+ {
+ unsigned int
+ end_of_insn_group : 1,
+ manual_bundling_on : 1,
+ manual_bundling_off : 1,
+ loc_directive_seen : 1;
+ signed char user_template; /* user-selected template, if any */
+ unsigned char qp_regno; /* qualifying predicate */
+ /* This duplicates a good fraction of "struct fix" but we
+ can't use a "struct fix" instead since we can't call
+ fix_new_exp() until we know the address of the instruction. */
+ int num_fixups;
+ struct insn_fix
+ {
+ bfd_reloc_code_real_type code;
+ enum ia64_opnd opnd; /* type of operand in need of fix */
+ unsigned int is_pcrel : 1; /* is operand pc-relative? */
+ expressionS expr; /* the value to be inserted */
+ }
+ fixup[2]; /* at most two fixups per insn */
+ struct ia64_opcode *idesc;
+ struct label_fix *label_fixups;
+ struct label_fix *tag_fixups;
+ struct unw_rec_list *unwind_record; /* Unwind directive. */
+ expressionS opnd[6];
+ char *src_file;
+ unsigned int src_line;
+ struct dwarf2_line_info debug_line;
+ }
+ slot[NUM_SLOTS];
+
+ segT last_text_seg;
+
+ struct dynreg
+ {
+ struct dynreg *next; /* next dynamic register */
+ const char *name;
+ unsigned short base; /* the base register number */
+ unsigned short num_regs; /* # of registers in this set */
+ }
+ *dynreg[DYNREG_NUM_TYPES], in, loc, out, rot;
+
+ flagword flags; /* ELF-header flags */
+
+ struct mem_offset {
+ unsigned hint:1; /* is this hint currently valid? */
+ bfd_vma offset; /* mem.offset offset */
+ bfd_vma base; /* mem.offset base */
+ } mem_offset;
+
+ int path; /* number of alt. entry points seen */
+ const char **entry_labels; /* labels of all alternate paths in
+ the current DV-checking block. */
+ int maxpaths; /* size currently allocated for
+ entry_labels */
+
+ int pointer_size; /* size in bytes of a pointer */
+ int pointer_size_shift; /* shift size of a pointer for alignment */
+
+ symbolS *indregsym[IND_RR - IND_CPUID + 1];
+ }
+md;
+
+/* These are not const, because they are modified to MMI for non-itanium1
+ targets below. */
+/* MFI bundle of nops. */
+static unsigned char le_nop[16] =
+{
+ 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+/* MFI bundle of nops with stop-bit. */
+static unsigned char le_nop_stop[16] =
+{
+ 0x0d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00
+};
+
+/* application registers: */
+
+#define AR_K0 0
+#define AR_K7 7
+#define AR_RSC 16
+#define AR_BSP 17
+#define AR_BSPSTORE 18
+#define AR_RNAT 19
+#define AR_FCR 21
+#define AR_EFLAG 24
+#define AR_CSD 25
+#define AR_SSD 26
+#define AR_CFLG 27
+#define AR_FSR 28
+#define AR_FIR 29
+#define AR_FDR 30
+#define AR_CCV 32
+#define AR_UNAT 36
+#define AR_FPSR 40
+#define AR_ITC 44
+#define AR_RUC 45
+#define AR_PFS 64
+#define AR_LC 65
+#define AR_EC 66
+
+static const struct
+ {
+ const char *name;
+ unsigned int regnum;
+ }
+ar[] =
+ {
+ {"ar.k0", AR_K0}, {"ar.k1", AR_K0 + 1},
+ {"ar.k2", AR_K0 + 2}, {"ar.k3", AR_K0 + 3},
+ {"ar.k4", AR_K0 + 4}, {"ar.k5", AR_K0 + 5},
+ {"ar.k6", AR_K0 + 6}, {"ar.k7", AR_K7},
+ {"ar.rsc", AR_RSC}, {"ar.bsp", AR_BSP},
+ {"ar.bspstore", AR_BSPSTORE}, {"ar.rnat", AR_RNAT},
+ {"ar.fcr", AR_FCR}, {"ar.eflag", AR_EFLAG},
+ {"ar.csd", AR_CSD}, {"ar.ssd", AR_SSD},
+ {"ar.cflg", AR_CFLG}, {"ar.fsr", AR_FSR},
+ {"ar.fir", AR_FIR}, {"ar.fdr", AR_FDR},
+ {"ar.ccv", AR_CCV}, {"ar.unat", AR_UNAT},
+ {"ar.fpsr", AR_FPSR}, {"ar.itc", AR_ITC},
+ {"ar.ruc", AR_RUC}, {"ar.pfs", AR_PFS},
+ {"ar.lc", AR_LC}, {"ar.ec", AR_EC},
+ };
+
+/* control registers: */
+
+#define CR_DCR 0
+#define CR_ITM 1
+#define CR_IVA 2
+#define CR_PTA 8
+#define CR_GPTA 9
+#define CR_IPSR 16
+#define CR_ISR 17
+#define CR_IIP 19
+#define CR_IFA 20
+#define CR_ITIR 21
+#define CR_IIPA 22
+#define CR_IFS 23
+#define CR_IIM 24
+#define CR_IHA 25
+#define CR_IIB0 26
+#define CR_IIB1 27
+#define CR_LID 64
+#define CR_IVR 65
+#define CR_TPR 66
+#define CR_EOI 67
+#define CR_IRR0 68
+#define CR_IRR3 71
+#define CR_ITV 72
+#define CR_PMV 73
+#define CR_CMCV 74
+#define CR_LRR0 80
+#define CR_LRR1 81
+
+static const struct
+ {
+ const char *name;
+ unsigned int regnum;
+ }
+cr[] =
+ {
+ {"cr.dcr", CR_DCR},
+ {"cr.itm", CR_ITM},
+ {"cr.iva", CR_IVA},
+ {"cr.pta", CR_PTA},
+ {"cr.gpta", CR_GPTA},
+ {"cr.ipsr", CR_IPSR},
+ {"cr.isr", CR_ISR},
+ {"cr.iip", CR_IIP},
+ {"cr.ifa", CR_IFA},
+ {"cr.itir", CR_ITIR},
+ {"cr.iipa", CR_IIPA},
+ {"cr.ifs", CR_IFS},
+ {"cr.iim", CR_IIM},
+ {"cr.iha", CR_IHA},
+ {"cr.iib0", CR_IIB0},
+ {"cr.iib1", CR_IIB1},
+ {"cr.lid", CR_LID},
+ {"cr.ivr", CR_IVR},
+ {"cr.tpr", CR_TPR},
+ {"cr.eoi", CR_EOI},
+ {"cr.irr0", CR_IRR0},
+ {"cr.irr1", CR_IRR0 + 1},
+ {"cr.irr2", CR_IRR0 + 2},
+ {"cr.irr3", CR_IRR3},
+ {"cr.itv", CR_ITV},
+ {"cr.pmv", CR_PMV},
+ {"cr.cmcv", CR_CMCV},
+ {"cr.lrr0", CR_LRR0},
+ {"cr.lrr1", CR_LRR1}
+ };
+
+#define PSR_MFL 4
+#define PSR_IC 13
+#define PSR_DFL 18
+#define PSR_CPL 32
+
+static const struct const_desc
+ {
+ const char *name;
+ valueT value;
+ }
+const_bits[] =
+ {
+ /* PSR constant masks: */
+
+ /* 0: reserved */
+ {"psr.be", ((valueT) 1) << 1},
+ {"psr.up", ((valueT) 1) << 2},
+ {"psr.ac", ((valueT) 1) << 3},
+ {"psr.mfl", ((valueT) 1) << 4},
+ {"psr.mfh", ((valueT) 1) << 5},
+ /* 6-12: reserved */
+ {"psr.ic", ((valueT) 1) << 13},
+ {"psr.i", ((valueT) 1) << 14},
+ {"psr.pk", ((valueT) 1) << 15},
+ /* 16: reserved */
+ {"psr.dt", ((valueT) 1) << 17},
+ {"psr.dfl", ((valueT) 1) << 18},
+ {"psr.dfh", ((valueT) 1) << 19},
+ {"psr.sp", ((valueT) 1) << 20},
+ {"psr.pp", ((valueT) 1) << 21},
+ {"psr.di", ((valueT) 1) << 22},
+ {"psr.si", ((valueT) 1) << 23},
+ {"psr.db", ((valueT) 1) << 24},
+ {"psr.lp", ((valueT) 1) << 25},
+ {"psr.tb", ((valueT) 1) << 26},
+ {"psr.rt", ((valueT) 1) << 27},
+ /* 28-31: reserved */
+ /* 32-33: cpl (current privilege level) */
+ {"psr.is", ((valueT) 1) << 34},
+ {"psr.mc", ((valueT) 1) << 35},
+ {"psr.it", ((valueT) 1) << 36},
+ {"psr.id", ((valueT) 1) << 37},
+ {"psr.da", ((valueT) 1) << 38},
+ {"psr.dd", ((valueT) 1) << 39},
+ {"psr.ss", ((valueT) 1) << 40},
+ /* 41-42: ri (restart instruction) */
+ {"psr.ed", ((valueT) 1) << 43},
+ {"psr.bn", ((valueT) 1) << 44},
+ };
+
+/* indirect register-sets/memory: */
+
+static const struct
+ {
+ const char *name;
+ unsigned int regnum;
+ }
+indirect_reg[] =
+ {
+ { "CPUID", IND_CPUID },
+ { "cpuid", IND_CPUID },
+ { "dbr", IND_DBR },
+ { "dtr", IND_DTR },
+ { "itr", IND_ITR },
+ { "ibr", IND_IBR },
+ { "msr", IND_MSR },
+ { "pkr", IND_PKR },
+ { "pmc", IND_PMC },
+ { "pmd", IND_PMD },
+ { "dahr", IND_DAHR },
+ { "rr", IND_RR },
+ };
+
+/* Pseudo functions used to indicate relocation types (these functions
+ start with an at sign (@). */
+static struct
+ {
+ const char *name;
+ enum pseudo_type
+ {
+ PSEUDO_FUNC_NONE,
+ PSEUDO_FUNC_RELOC,
+ PSEUDO_FUNC_CONST,
+ PSEUDO_FUNC_REG,
+ PSEUDO_FUNC_FLOAT
+ }
+ type;
+ union
+ {
+ unsigned long ival;
+ symbolS *sym;
+ }
+ u;
+ }
+pseudo_func[] =
+ {
+ /* reloc pseudo functions (these must come first!): */
+ { "dtpmod", PSEUDO_FUNC_RELOC, { 0 } },
+ { "dtprel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "fptr", PSEUDO_FUNC_RELOC, { 0 } },
+ { "gprel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "ltoff", PSEUDO_FUNC_RELOC, { 0 } },
+ { "ltoffx", PSEUDO_FUNC_RELOC, { 0 } },
+ { "pcrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "pltoff", PSEUDO_FUNC_RELOC, { 0 } },
+ { "secrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "segrel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "tprel", PSEUDO_FUNC_RELOC, { 0 } },
+ { "ltv", PSEUDO_FUNC_RELOC, { 0 } },
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_DTP_MODULE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_DTP_RELATIVE */
+ { NULL, 0, { 0 } }, /* placeholder for FUNC_LT_TP_RELATIVE */
+ { "iplt", PSEUDO_FUNC_RELOC, { 0 } },
+#ifdef TE_VMS
+ { "slotcount", PSEUDO_FUNC_RELOC, { 0 } },
+#endif
+
+ /* mbtype4 constants: */
+ { "alt", PSEUDO_FUNC_CONST, { 0xa } },
+ { "brcst", PSEUDO_FUNC_CONST, { 0x0 } },
+ { "mix", PSEUDO_FUNC_CONST, { 0x8 } },
+ { "rev", PSEUDO_FUNC_CONST, { 0xb } },
+ { "shuf", PSEUDO_FUNC_CONST, { 0x9 } },
+
+ /* fclass constants: */
+ { "nat", PSEUDO_FUNC_CONST, { 0x100 } },
+ { "qnan", PSEUDO_FUNC_CONST, { 0x080 } },
+ { "snan", PSEUDO_FUNC_CONST, { 0x040 } },
+ { "pos", PSEUDO_FUNC_CONST, { 0x001 } },
+ { "neg", PSEUDO_FUNC_CONST, { 0x002 } },
+ { "zero", PSEUDO_FUNC_CONST, { 0x004 } },
+ { "unorm", PSEUDO_FUNC_CONST, { 0x008 } },
+ { "norm", PSEUDO_FUNC_CONST, { 0x010 } },
+ { "inf", PSEUDO_FUNC_CONST, { 0x020 } },
+
+ { "natval", PSEUDO_FUNC_CONST, { 0x100 } }, /* old usage */
+
+ /* hint constants: */
+ { "pause", PSEUDO_FUNC_CONST, { 0x0 } },
+ { "priority", PSEUDO_FUNC_CONST, { 0x1 } },
+
+ /* tf constants: */
+ { "clz", PSEUDO_FUNC_CONST, { 32 } },
+ { "mpy", PSEUDO_FUNC_CONST, { 33 } },
+ { "datahints", PSEUDO_FUNC_CONST, { 34 } },
+
+ /* unwind-related constants: */
+ { "svr4", PSEUDO_FUNC_CONST, { ELFOSABI_NONE } },
+ { "hpux", PSEUDO_FUNC_CONST, { ELFOSABI_HPUX } },
+ { "nt", PSEUDO_FUNC_CONST, { 2 } }, /* conflicts w/ELFOSABI_NETBSD */
+ { "linux", PSEUDO_FUNC_CONST, { ELFOSABI_GNU } },
+ { "freebsd", PSEUDO_FUNC_CONST, { ELFOSABI_FREEBSD } },
+ { "openvms", PSEUDO_FUNC_CONST, { ELFOSABI_OPENVMS } },
+ { "nsk", PSEUDO_FUNC_CONST, { ELFOSABI_NSK } },
+
+ /* unwind-related registers: */
+ { "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
+ };
+
+/* 41-bit nop opcodes (one per unit): */
+static const bfd_vma nop[IA64_NUM_UNITS] =
+ {
+ 0x0000000000LL, /* NIL => break 0 */
+ 0x0008000000LL, /* I-unit nop */
+ 0x0008000000LL, /* M-unit nop */
+ 0x4000000000LL, /* B-unit nop */
+ 0x0008000000LL, /* F-unit nop */
+ 0x0000000000LL, /* L-"unit" nop immediate */
+ 0x0008000000LL, /* X-unit nop */
+ };
+
+/* Can't be `const' as it's passed to input routines (which have the
+ habit of setting temporary sentinels. */
+static char special_section_name[][20] =
+ {
+ {".bss"}, {".sbss"}, {".sdata"}, {".rodata"}, {".comment"},
+ {".IA_64.unwind"}, {".IA_64.unwind_info"},
+ {".init_array"}, {".fini_array"}
+ };
+
+/* The best template for a particular sequence of up to three
+ instructions: */
+#define N IA64_NUM_TYPES
+static unsigned char best_template[N][N][N];
+#undef N
+
+/* Resource dependencies currently in effect */
+static struct rsrc {
+ int depind; /* dependency index */
+ const struct ia64_dependency *dependency; /* actual dependency */
+ unsigned specific:1, /* is this a specific bit/regno? */
+ link_to_qp_branch:1; /* will a branch on the same QP clear it?*/
+ int index; /* specific regno/bit within dependency */
+ int note; /* optional qualifying note (0 if none) */
+#define STATE_NONE 0
+#define STATE_STOP 1
+#define STATE_SRLZ 2
+ int insn_srlz; /* current insn serialization state */
+ int data_srlz; /* current data serialization state */
+ int qp_regno; /* qualifying predicate for this usage */
+ char *file; /* what file marked this dependency */
+ unsigned int line; /* what line marked this dependency */
+ struct mem_offset mem_offset; /* optional memory offset hint */
+ enum { CMP_NONE, CMP_OR, CMP_AND } cmp_type; /* OR or AND compare? */
+ int path; /* corresponding code entry index */
+} *regdeps = NULL;
+static int regdepslen = 0;
+static int regdepstotlen = 0;
+static const char *dv_mode[] = { "RAW", "WAW", "WAR" };
+static const char *dv_sem[] = { "none", "implied", "impliedf",
+ "data", "instr", "specific", "stop", "other" };
+static const char *dv_cmp_type[] = { "none", "OR", "AND" };
+
+/* Current state of PR mutexation */
+static struct qpmutex {
+ valueT prmask;
+ int path;
+} *qp_mutexes = NULL; /* QP mutex bitmasks */
+static int qp_mutexeslen = 0;
+static int qp_mutexestotlen = 0;
+static valueT qp_safe_across_calls = 0;
+
+/* Current state of PR implications */
+static struct qp_imply {
+ unsigned p1:6;
+ unsigned p2:6;
+ unsigned p2_branched:1;
+ int path;
+} *qp_implies = NULL;
+static int qp_implieslen = 0;
+static int qp_impliestotlen = 0;
+
+/* Keep track of static GR values so that indirect register usage can
+ sometimes be tracked. */
+static struct gr {
+ unsigned known:1;
+ int path;
+ valueT value;
+} gr_values[128] = {
+ {
+ 1,
+#ifdef INT_MAX
+ INT_MAX,
+#else
+ (((1 << (8 * sizeof(gr_values->path) - 2)) - 1) << 1) + 1,
+#endif
+ 0
+ }
+};
+
+/* Remember the alignment frag. */
+static fragS *align_frag;
+
+/* These are the routines required to output the various types of
+ unwind records. */
+
+/* A slot_number is a frag address plus the slot index (0-2). We use the
+ frag address here so that if there is a section switch in the middle of
+ a function, then instructions emitted to a different section are not
+ counted. Since there may be more than one frag for a function, this
+ means we also need to keep track of which frag this address belongs to
+ so we can compute inter-frag distances. This also nicely solves the
+ problem with nops emitted for align directives, which can't easily be
+ counted, but can easily be derived from frag sizes. */
+
+typedef struct unw_rec_list {
+ unwind_record r;
+ unsigned long slot_number;
+ fragS *slot_frag;
+ struct unw_rec_list *next;
+} unw_rec_list;
+
+#define SLOT_NUM_NOT_SET (unsigned)-1
+
+/* Linked list of saved prologue counts. A very poor
+ implementation of a map from label numbers to prologue counts. */
+typedef struct label_prologue_count
+{
+ struct label_prologue_count *next;
+ unsigned long label_number;
+ unsigned int prologue_count;
+} label_prologue_count;
+
+typedef struct proc_pending
+{
+ symbolS *sym;
+ struct proc_pending *next;
+} proc_pending;
+
+static struct
+{
+ /* Maintain a list of unwind entries for the current function. */
+ unw_rec_list *list;
+ unw_rec_list *tail;
+
+ /* Any unwind entries that should be attached to the current slot
+ that an insn is being constructed for. */
+ unw_rec_list *current_entry;
+
+ /* These are used to create the unwind table entry for this function. */
+ proc_pending proc_pending;
+ symbolS *info; /* pointer to unwind info */
+ symbolS *personality_routine;
+ segT saved_text_seg;
+ subsegT saved_text_subseg;
+ unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */
+
+ /* TRUE if processing unwind directives in a prologue region. */
+ unsigned int prologue : 1;
+ unsigned int prologue_mask : 4;
+ unsigned int prologue_gr : 7;
+ unsigned int body : 1;
+ unsigned int insn : 1;
+ unsigned int prologue_count; /* number of .prologues seen so far */
+ /* Prologue counts at previous .label_state directives. */
+ struct label_prologue_count * saved_prologue_counts;
+
+ /* List of split up .save-s. */
+ unw_p_record *pending_saves;
+} unwind;
+
+/* The input value is a negated offset from psp, and specifies an address
+ psp - offset. The encoded value is psp + 16 - (4 * offset). Thus we
+ must add 16 and divide by 4 to get the encoded value. */
+
+#define ENCODED_PSP_OFFSET(OFFSET) (((OFFSET) + 16) / 4)
+
+typedef void (*vbyte_func) (int, char *, char *);
+
+/* Forward declarations: */
+static void dot_alias (int);
+static int parse_operand_and_eval (expressionS *, int);
+static void emit_one_bundle (void);
+static bfd_reloc_code_real_type ia64_gen_real_reloc_type (struct symbol *,
+ bfd_reloc_code_real_type);
+static void insn_group_break (int, int, int);
+static void add_qp_mutex (valueT);
+static void add_qp_imply (int, int);
+static void clear_qp_mutex (valueT);
+static void clear_qp_implies (valueT, valueT);
+static void print_dependency (const char *, int);
+static void instruction_serialization (void);
+static void data_serialization (void);
+static void output_R3_format (vbyte_func, unw_record_type, unsigned long);
+static void output_B3_format (vbyte_func, unsigned long, unsigned long);
+static void output_B4_format (vbyte_func, unw_record_type, unsigned long);
+static void free_saved_prologue_counts (void);
+
+/* Determine if application register REGNUM resides only in the integer
+ unit (as opposed to the memory unit). */
+static int
+ar_is_only_in_integer_unit (int reg)
+{
+ reg -= REG_AR;
+ return reg >= 64 && reg <= 111;
+}
+
+/* Determine if application register REGNUM resides only in the memory
+ unit (as opposed to the integer unit). */
+static int
+ar_is_only_in_memory_unit (int reg)
+{
+ reg -= REG_AR;
+ return reg >= 0 && reg <= 47;
+}
+
+/* Switch to section NAME and create section if necessary. It's
+ rather ugly that we have to manipulate input_line_pointer but I
+ don't see any other way to accomplish the same thing without
+ changing obj-elf.c (which may be the Right Thing, in the end). */
+static void
+set_section (char *name)
+{
+ char *saved_input_line_pointer;
+
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = name;
+ obj_elf_section (0);
+ input_line_pointer = saved_input_line_pointer;
+}
+
+/* Map 's' to SHF_IA_64_SHORT. */
+
+bfd_vma
+ia64_elf_section_letter (int letter, char **ptr_msg)
+{
+ if (letter == 's')
+ return SHF_IA_64_SHORT;
+ else if (letter == 'o')
+ return SHF_LINK_ORDER;
+#ifdef TE_VMS
+ else if (letter == 'O')
+ return SHF_IA_64_VMS_OVERLAID;
+ else if (letter == 'g')
+ return SHF_IA_64_VMS_GLOBAL;
+#endif
+
+ *ptr_msg = _("bad .section directive: want a,o,s,w,x,M,S,G,T in string");
+ return -1;
+}
+
+/* Map SHF_IA_64_SHORT to SEC_SMALL_DATA. */
+
+flagword
+ia64_elf_section_flags (flagword flags,
+ bfd_vma attr,
+ int type ATTRIBUTE_UNUSED)
+{
+ if (attr & SHF_IA_64_SHORT)
+ flags |= SEC_SMALL_DATA;
+ return flags;
+}
+
+int
+ia64_elf_section_type (const char *str, size_t len)
+{
+#define STREQ(s) ((len == sizeof (s) - 1) && (strncmp (str, s, sizeof (s) - 1) == 0))
+
+ if (STREQ (ELF_STRING_ia64_unwind_info))
+ return SHT_PROGBITS;
+
+ if (STREQ (ELF_STRING_ia64_unwind_info_once))
+ return SHT_PROGBITS;
+
+ if (STREQ (ELF_STRING_ia64_unwind))
+ return SHT_IA_64_UNWIND;
+
+ if (STREQ (ELF_STRING_ia64_unwind_once))
+ return SHT_IA_64_UNWIND;
+
+ if (STREQ ("unwind"))
+ return SHT_IA_64_UNWIND;
+
+ return -1;
+#undef STREQ
+}
+
+static unsigned int
+set_regstack (unsigned int ins,
+ unsigned int locs,
+ unsigned int outs,
+ unsigned int rots)
+{
+ /* Size of frame. */
+ unsigned int sof;
+
+ sof = ins + locs + outs;
+ if (sof > 96)
+ {
+ as_bad (_("Size of frame exceeds maximum of 96 registers"));
+ return 0;
+ }
+ if (rots > sof)
+ {
+ as_warn (_("Size of rotating registers exceeds frame size"));
+ return 0;
+ }
+ md.in.base = REG_GR + 32;
+ md.loc.base = md.in.base + ins;
+ md.out.base = md.loc.base + locs;
+
+ md.in.num_regs = ins;
+ md.loc.num_regs = locs;
+ md.out.num_regs = outs;
+ md.rot.num_regs = rots;
+ return sof;
+}
+
+void
+ia64_flush_insns (void)
+{
+ struct label_fix *lfix;
+ segT saved_seg;
+ subsegT saved_subseg;
+ unw_rec_list *ptr;
+ bfd_boolean mark;
+
+ if (!md.last_text_seg)
+ return;
+
+ saved_seg = now_seg;
+ saved_subseg = now_subseg;
+
+ subseg_set (md.last_text_seg, 0);
+
+ while (md.num_slots_in_use > 0)
+ emit_one_bundle (); /* force out queued instructions */
+
+ /* In case there are labels following the last instruction, resolve
+ those now. */
+ mark = FALSE;
+ for (lfix = CURR_SLOT.label_fixups; lfix; lfix = lfix->next)
+ {
+ symbol_set_value_now (lfix->sym);
+ mark |= lfix->dw2_mark_labels;
+ }
+ if (mark)
+ {
+ dwarf2_where (&CURR_SLOT.debug_line);
+ CURR_SLOT.debug_line.flags |= DWARF2_FLAG_BASIC_BLOCK;
+ dwarf2_gen_line_info (frag_now_fix (), &CURR_SLOT.debug_line);
+ dwarf2_consume_line_info ();
+ }
+ CURR_SLOT.label_fixups = 0;
+
+ for (lfix = CURR_SLOT.tag_fixups; lfix; lfix = lfix->next)
+ symbol_set_value_now (lfix->sym);
+ CURR_SLOT.tag_fixups = 0;
+
+ /* In case there are unwind directives following the last instruction,
+ resolve those now. We only handle prologue, body, and endp directives
+ here. Give an error for others. */
+ for (ptr = unwind.current_entry; ptr; ptr = ptr->next)
+ {
+ switch (ptr->r.type)
+ {
+ case prologue:
+ case prologue_gr:
+ case body:
+ case endp:
+ ptr->slot_number = (unsigned long) frag_more (0);
+ ptr->slot_frag = frag_now;
+ break;
+
+ /* Allow any record which doesn't have a "t" field (i.e.,
+ doesn't relate to a particular instruction). */
+ case unwabi:
+ case br_gr:
+ case copy_state:
+ case fr_mem:
+ case frgr_mem:
+ case gr_gr:
+ case gr_mem:
+ case label_state:
+ case rp_br:
+ case spill_base:
+ case spill_mask:
+ /* nothing */
+ break;
+
+ default:
+ as_bad (_("Unwind directive not followed by an instruction."));
+ break;
+ }
+ }
+ unwind.current_entry = NULL;
+
+ subseg_set (saved_seg, saved_subseg);
+
+ if (md.qp.X_op == O_register)
+ as_bad (_("qualifying predicate not followed by instruction"));
+}
+
+static void
+ia64_do_align (int nbytes)
+{
+ char *saved_input_line_pointer = input_line_pointer;
+
+ input_line_pointer = "";
+ s_align_bytes (nbytes);
+ input_line_pointer = saved_input_line_pointer;
+}
+
+void
+ia64_cons_align (int nbytes)
+{
+ if (md.auto_align)
+ {
+ char *saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = "";
+ s_align_bytes (nbytes);
+ input_line_pointer = saved_input_line_pointer;
+ }
+}
+
+#ifdef TE_VMS
+
+/* .vms_common section, symbol, size, alignment */
+
+static void
+obj_elf_vms_common (int ignore ATTRIBUTE_UNUSED)
+{
+ char *sec_name;
+ char *sym_name;
+ char c;
+ offsetT size;
+ offsetT cur_size;
+ offsetT temp;
+ symbolS *symbolP;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ offsetT log_align;
+
+ /* Section name. */
+ sec_name = obj_elf_section_name ();
+ if (sec_name == NULL)
+ return;
+
+ /* Symbol name. */
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ else
+ {
+ as_bad (_("expected ',' after section name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ sym_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (input_line_pointer == sym_name)
+ {
+ *input_line_pointer = c;
+ as_bad (_("expected symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ symbolP = symbol_find_or_make (sym_name);
+ *input_line_pointer = c;
+
+ if ((S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP))
+ && !S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Symbol size. */
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ else
+ {
+ as_bad (_("expected ',' after symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ temp = get_absolute_expression ();
+ size = temp;
+ size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1;
+ if (temp != size)
+ {
+ as_warn (_("size (%ld) out of range, ignored"), (long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Alignment. */
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+ else
+ {
+ as_bad (_("expected ',' after symbol size"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ log_align = get_absolute_expression ();
+
+ demand_empty_rest_of_line ();
+
+ obj_elf_change_section
+ (sec_name, SHT_NOBITS,
+ SHF_ALLOC | SHF_WRITE | SHF_IA_64_VMS_OVERLAID | SHF_IA_64_VMS_GLOBAL,
+ 0, NULL, 1, 0);
+
+ S_SET_VALUE (symbolP, 0);
+ S_SET_SIZE (symbolP, size);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, now_seg);
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+
+ record_alignment (now_seg, log_align);
+
+ cur_size = bfd_section_size (stdoutput, now_seg);
+ if ((int) size > cur_size)
+ {
+ char *pfrag
+ = frag_var (rs_fill, 1, 1, (relax_substateT)0, NULL,
+ (valueT)size - (valueT)cur_size, NULL);
+ *pfrag = 0;
+ bfd_section_size (stdoutput, now_seg) = size;
+ }
+
+ /* Switch back to current segment. */
+ subseg_set (current_seg, current_subseg);
+
+#ifdef md_elf_section_change_hook
+ md_elf_section_change_hook ();
+#endif
+}
+
+#endif /* TE_VMS */
+
+/* Output COUNT bytes to a memory location. */
+static char *vbyte_mem_ptr = NULL;
+
+static void
+output_vbyte_mem (int count, char *ptr, char *comment ATTRIBUTE_UNUSED)
+{
+ int x;
+ if (vbyte_mem_ptr == NULL)
+ abort ();
+
+ if (count == 0)
+ return;
+ for (x = 0; x < count; x++)
+ *(vbyte_mem_ptr++) = ptr[x];
+}
+
+/* Count the number of bytes required for records. */
+static int vbyte_count = 0;
+static void
+count_output (int count,
+ char *ptr ATTRIBUTE_UNUSED,
+ char *comment ATTRIBUTE_UNUSED)
+{
+ vbyte_count += count;
+}
+
+static void
+output_R1_format (vbyte_func f, unw_record_type rtype, int rlen)
+{
+ int r = 0;
+ char byte;
+ if (rlen > 0x1f)
+ {
+ output_R3_format (f, rtype, rlen);
+ return;
+ }
+
+ if (rtype == body)
+ r = 1;
+ else if (rtype != prologue)
+ as_bad (_("record type is not valid"));
+
+ byte = UNW_R1 | (r << 5) | (rlen & 0x1f);
+ (*f) (1, &byte, NULL);
+}
+
+static void
+output_R2_format (vbyte_func f, int mask, int grsave, unsigned long rlen)
+{
+ char bytes[20];
+ int count = 2;
+ mask = (mask & 0x0f);
+ grsave = (grsave & 0x7f);
+
+ bytes[0] = (UNW_R2 | (mask >> 1));
+ bytes[1] = (((mask & 0x01) << 7) | grsave);
+ count += output_leb128 (bytes + 2, rlen, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_R3_format (vbyte_func f, unw_record_type rtype, unsigned long rlen)
+{
+ int r = 0, count;
+ char bytes[20];
+ if (rlen <= 0x1f)
+ {
+ output_R1_format (f, rtype, rlen);
+ return;
+ }
+
+ if (rtype == body)
+ r = 1;
+ else if (rtype != prologue)
+ as_bad (_("record type is not valid"));
+ bytes[0] = (UNW_R3 | r);
+ count = output_leb128 (bytes + 1, rlen, 0);
+ (*f) (count + 1, bytes, NULL);
+}
+
+static void
+output_P1_format (vbyte_func f, int brmask)
+{
+ char byte;
+ byte = UNW_P1 | (brmask & 0x1f);
+ (*f) (1, &byte, NULL);
+}
+
+static void
+output_P2_format (vbyte_func f, int brmask, int gr)
+{
+ char bytes[2];
+ brmask = (brmask & 0x1f);
+ bytes[0] = UNW_P2 | (brmask >> 1);
+ bytes[1] = (((brmask & 1) << 7) | gr);
+ (*f) (2, bytes, NULL);
+}
+
+static void
+output_P3_format (vbyte_func f, unw_record_type rtype, int reg)
+{
+ char bytes[2];
+ int r = 0;
+ reg = (reg & 0x7f);
+ switch (rtype)
+ {
+ case psp_gr:
+ r = 0;
+ break;
+ case rp_gr:
+ r = 1;
+ break;
+ case pfs_gr:
+ r = 2;
+ break;
+ case preds_gr:
+ r = 3;
+ break;
+ case unat_gr:
+ r = 4;
+ break;
+ case lc_gr:
+ r = 5;
+ break;
+ case rp_br:
+ r = 6;
+ break;
+ case rnat_gr:
+ r = 7;
+ break;
+ case bsp_gr:
+ r = 8;
+ break;
+ case bspstore_gr:
+ r = 9;
+ break;
+ case fpsr_gr:
+ r = 10;
+ break;
+ case priunat_gr:
+ r = 11;
+ break;
+ default:
+ as_bad (_("Invalid record type for P3 format."));
+ }
+ bytes[0] = (UNW_P3 | (r >> 1));
+ bytes[1] = (((r & 1) << 7) | reg);
+ (*f) (2, bytes, NULL);
+}
+
+static void
+output_P4_format (vbyte_func f, unsigned char *imask, unsigned long imask_size)
+{
+ imask[0] = UNW_P4;
+ (*f) (imask_size, (char *) imask, NULL);
+}
+
+static void
+output_P5_format (vbyte_func f, int grmask, unsigned long frmask)
+{
+ char bytes[4];
+ grmask = (grmask & 0x0f);
+
+ bytes[0] = UNW_P5;
+ bytes[1] = ((grmask << 4) | ((frmask & 0x000f0000) >> 16));
+ bytes[2] = ((frmask & 0x0000ff00) >> 8);
+ bytes[3] = (frmask & 0x000000ff);
+ (*f) (4, bytes, NULL);
+}
+
+static void
+output_P6_format (vbyte_func f, unw_record_type rtype, int rmask)
+{
+ char byte;
+ int r = 0;
+
+ if (rtype == gr_mem)
+ r = 1;
+ else if (rtype != fr_mem)
+ as_bad (_("Invalid record type for format P6"));
+ byte = (UNW_P6 | (r << 4) | (rmask & 0x0f));
+ (*f) (1, &byte, NULL);
+}
+
+static void
+output_P7_format (vbyte_func f,
+ unw_record_type rtype,
+ unsigned long w1,
+ unsigned long w2)
+{
+ char bytes[20];
+ int count = 1;
+ int r = 0;
+ count += output_leb128 (bytes + 1, w1, 0);
+ switch (rtype)
+ {
+ case mem_stack_f:
+ r = 0;
+ count += output_leb128 (bytes + count, w2 >> 4, 0);
+ break;
+ case mem_stack_v:
+ r = 1;
+ break;
+ case spill_base:
+ r = 2;
+ break;
+ case psp_sprel:
+ r = 3;
+ break;
+ case rp_when:
+ r = 4;
+ break;
+ case rp_psprel:
+ r = 5;
+ break;
+ case pfs_when:
+ r = 6;
+ break;
+ case pfs_psprel:
+ r = 7;
+ break;
+ case preds_when:
+ r = 8;
+ break;
+ case preds_psprel:
+ r = 9;
+ break;
+ case lc_when:
+ r = 10;
+ break;
+ case lc_psprel:
+ r = 11;
+ break;
+ case unat_when:
+ r = 12;
+ break;
+ case unat_psprel:
+ r = 13;
+ break;
+ case fpsr_when:
+ r = 14;
+ break;
+ case fpsr_psprel:
+ r = 15;
+ break;
+ default:
+ break;
+ }
+ bytes[0] = (UNW_P7 | r);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_P8_format (vbyte_func f, unw_record_type rtype, unsigned long t)
+{
+ char bytes[20];
+ int r = 0;
+ int count = 2;
+ bytes[0] = UNW_P8;
+ switch (rtype)
+ {
+ case rp_sprel:
+ r = 1;
+ break;
+ case pfs_sprel:
+ r = 2;
+ break;
+ case preds_sprel:
+ r = 3;
+ break;
+ case lc_sprel:
+ r = 4;
+ break;
+ case unat_sprel:
+ r = 5;
+ break;
+ case fpsr_sprel:
+ r = 6;
+ break;
+ case bsp_when:
+ r = 7;
+ break;
+ case bsp_psprel:
+ r = 8;
+ break;
+ case bsp_sprel:
+ r = 9;
+ break;
+ case bspstore_when:
+ r = 10;
+ break;
+ case bspstore_psprel:
+ r = 11;
+ break;
+ case bspstore_sprel:
+ r = 12;
+ break;
+ case rnat_when:
+ r = 13;
+ break;
+ case rnat_psprel:
+ r = 14;
+ break;
+ case rnat_sprel:
+ r = 15;
+ break;
+ case priunat_when_gr:
+ r = 16;
+ break;
+ case priunat_psprel:
+ r = 17;
+ break;
+ case priunat_sprel:
+ r = 18;
+ break;
+ case priunat_when_mem:
+ r = 19;
+ break;
+ default:
+ break;
+ }
+ bytes[1] = r;
+ count += output_leb128 (bytes + 2, t, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_P9_format (vbyte_func f, int grmask, int gr)
+{
+ char bytes[3];
+ bytes[0] = UNW_P9;
+ bytes[1] = (grmask & 0x0f);
+ bytes[2] = (gr & 0x7f);
+ (*f) (3, bytes, NULL);
+}
+
+static void
+output_P10_format (vbyte_func f, int abi, int context)
+{
+ char bytes[3];
+ bytes[0] = UNW_P10;
+ bytes[1] = (abi & 0xff);
+ bytes[2] = (context & 0xff);
+ (*f) (3, bytes, NULL);
+}
+
+static void
+output_B1_format (vbyte_func f, unw_record_type rtype, unsigned long label)
+{
+ char byte;
+ int r = 0;
+ if (label > 0x1f)
+ {
+ output_B4_format (f, rtype, label);
+ return;
+ }
+ if (rtype == copy_state)
+ r = 1;
+ else if (rtype != label_state)
+ as_bad (_("Invalid record type for format B1"));
+
+ byte = (UNW_B1 | (r << 5) | (label & 0x1f));
+ (*f) (1, &byte, NULL);
+}
+
+static void
+output_B2_format (vbyte_func f, unsigned long ecount, unsigned long t)
+{
+ char bytes[20];
+ int count = 1;
+ if (ecount > 0x1f)
+ {
+ output_B3_format (f, ecount, t);
+ return;
+ }
+ bytes[0] = (UNW_B2 | (ecount & 0x1f));
+ count += output_leb128 (bytes + 1, t, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_B3_format (vbyte_func f, unsigned long ecount, unsigned long t)
+{
+ char bytes[20];
+ int count = 1;
+ if (ecount <= 0x1f)
+ {
+ output_B2_format (f, ecount, t);
+ return;
+ }
+ bytes[0] = UNW_B3;
+ count += output_leb128 (bytes + 1, t, 0);
+ count += output_leb128 (bytes + count, ecount, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_B4_format (vbyte_func f, unw_record_type rtype, unsigned long label)
+{
+ char bytes[20];
+ int r = 0;
+ int count = 1;
+ if (label <= 0x1f)
+ {
+ output_B1_format (f, rtype, label);
+ return;
+ }
+
+ if (rtype == copy_state)
+ r = 1;
+ else if (rtype != label_state)
+ as_bad (_("Invalid record type for format B1"));
+
+ bytes[0] = (UNW_B4 | (r << 3));
+ count += output_leb128 (bytes + 1, label, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static char
+format_ab_reg (int ab, int reg)
+{
+ int ret;
+ ab = (ab & 3);
+ reg = (reg & 0x1f);
+ ret = (ab << 5) | reg;
+ return ret;
+}
+
+static void
+output_X1_format (vbyte_func f,
+ unw_record_type rtype,
+ int ab,
+ int reg,
+ unsigned long t,
+ unsigned long w1)
+{
+ char bytes[20];
+ int r = 0;
+ int count = 2;
+ bytes[0] = UNW_X1;
+
+ if (rtype == spill_sprel)
+ r = 1;
+ else if (rtype != spill_psprel)
+ as_bad (_("Invalid record type for format X1"));
+ bytes[1] = ((r << 7) | format_ab_reg (ab, reg));
+ count += output_leb128 (bytes + 2, t, 0);
+ count += output_leb128 (bytes + count, w1, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_X2_format (vbyte_func f,
+ int ab,
+ int reg,
+ int x,
+ int y,
+ int treg,
+ unsigned long t)
+{
+ char bytes[20];
+ int count = 3;
+ bytes[0] = UNW_X2;
+ bytes[1] = (((x & 1) << 7) | format_ab_reg (ab, reg));
+ bytes[2] = (((y & 1) << 7) | (treg & 0x7f));
+ count += output_leb128 (bytes + 3, t, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_X3_format (vbyte_func f,
+ unw_record_type rtype,
+ int qp,
+ int ab,
+ int reg,
+ unsigned long t,
+ unsigned long w1)
+{
+ char bytes[20];
+ int r = 0;
+ int count = 3;
+ bytes[0] = UNW_X3;
+
+ if (rtype == spill_sprel_p)
+ r = 1;
+ else if (rtype != spill_psprel_p)
+ as_bad (_("Invalid record type for format X3"));
+ bytes[1] = ((r << 7) | (qp & 0x3f));
+ bytes[2] = format_ab_reg (ab, reg);
+ count += output_leb128 (bytes + 3, t, 0);
+ count += output_leb128 (bytes + count, w1, 0);
+ (*f) (count, bytes, NULL);
+}
+
+static void
+output_X4_format (vbyte_func f,
+ int qp,
+ int ab,
+ int reg,
+ int x,
+ int y,
+ int treg,
+ unsigned long t)
+{
+ char bytes[20];
+ int count = 4;
+ bytes[0] = UNW_X4;
+ bytes[1] = (qp & 0x3f);
+ bytes[2] = (((x & 1) << 7) | format_ab_reg (ab, reg));
+ bytes[3] = (((y & 1) << 7) | (treg & 0x7f));
+ count += output_leb128 (bytes + 4, t, 0);
+ (*f) (count, bytes, NULL);
+}
+
+/* This function checks whether there are any outstanding .save-s and
+ discards them if so. */
+
+static void
+check_pending_save (void)
+{
+ if (unwind.pending_saves)
+ {
+ unw_rec_list *cur, *prev;
+
+ as_warn (_("Previous .save incomplete"));
+ for (cur = unwind.list, prev = NULL; cur; )
+ if (&cur->r.record.p == unwind.pending_saves)
+ {
+ if (prev)
+ prev->next = cur->next;
+ else
+ unwind.list = cur->next;
+ if (cur == unwind.tail)
+ unwind.tail = prev;
+ if (cur == unwind.current_entry)
+ unwind.current_entry = cur->next;
+ /* Don't free the first discarded record, it's being used as
+ terminator for (currently) br_gr and gr_gr processing, and
+ also prevents leaving a dangling pointer to it in its
+ predecessor. */
+ cur->r.record.p.grmask = 0;
+ cur->r.record.p.brmask = 0;
+ cur->r.record.p.frmask = 0;
+ prev = cur->r.record.p.next;
+ cur->r.record.p.next = NULL;
+ cur = prev;
+ break;
+ }
+ else
+ {
+ prev = cur;
+ cur = cur->next;
+ }
+ while (cur)
+ {
+ prev = cur;
+ cur = cur->r.record.p.next;
+ free (prev);
+ }
+ unwind.pending_saves = NULL;
+ }
+}
+
+/* This function allocates a record list structure, and initializes fields. */
+
+static unw_rec_list *
+alloc_record (unw_record_type t)
+{
+ unw_rec_list *ptr;
+ ptr = xmalloc (sizeof (*ptr));
+ memset (ptr, 0, sizeof (*ptr));
+ ptr->slot_number = SLOT_NUM_NOT_SET;
+ ptr->r.type = t;
+ return ptr;
+}
+
+/* Dummy unwind record used for calculating the length of the last prologue or
+ body region. */
+
+static unw_rec_list *
+output_endp (void)
+{
+ unw_rec_list *ptr = alloc_record (endp);
+ return ptr;
+}
+
+static unw_rec_list *
+output_prologue (void)
+{
+ unw_rec_list *ptr = alloc_record (prologue);
+ memset (&ptr->r.record.r.mask, 0, sizeof (ptr->r.record.r.mask));
+ return ptr;
+}
+
+static unw_rec_list *
+output_prologue_gr (unsigned int saved_mask, unsigned int reg)
+{
+ unw_rec_list *ptr = alloc_record (prologue_gr);
+ memset (&ptr->r.record.r.mask, 0, sizeof (ptr->r.record.r.mask));
+ ptr->r.record.r.grmask = saved_mask;
+ ptr->r.record.r.grsave = reg;
+ return ptr;
+}
+
+static unw_rec_list *
+output_body (void)
+{
+ unw_rec_list *ptr = alloc_record (body);
+ return ptr;
+}
+
+static unw_rec_list *
+output_mem_stack_f (unsigned int size)
+{
+ unw_rec_list *ptr = alloc_record (mem_stack_f);
+ ptr->r.record.p.size = size;
+ return ptr;
+}
+
+static unw_rec_list *
+output_mem_stack_v (void)
+{
+ unw_rec_list *ptr = alloc_record (mem_stack_v);
+ return ptr;
+}
+
+static unw_rec_list *
+output_psp_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (psp_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_psp_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (psp_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_rp_when (void)
+{
+ unw_rec_list *ptr = alloc_record (rp_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_rp_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (rp_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_rp_br (unsigned int br)
+{
+ unw_rec_list *ptr = alloc_record (rp_br);
+ ptr->r.record.p.r.br = br;
+ return ptr;
+}
+
+static unw_rec_list *
+output_rp_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (rp_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_rp_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (rp_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_pfs_when (void)
+{
+ unw_rec_list *ptr = alloc_record (pfs_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_pfs_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (pfs_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_pfs_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (pfs_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_pfs_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (pfs_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_preds_when (void)
+{
+ unw_rec_list *ptr = alloc_record (preds_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_preds_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (preds_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_preds_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (preds_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_preds_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (preds_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_fr_mem (unsigned int mask)
+{
+ unw_rec_list *ptr = alloc_record (fr_mem);
+ unw_rec_list *cur = ptr;
+
+ ptr->r.record.p.frmask = mask;
+ unwind.pending_saves = &ptr->r.record.p;
+ for (;;)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ mask &= ~(mask & (~mask + 1));
+ if (!mask)
+ return ptr;
+ cur = alloc_record (fr_mem);
+ cur->r.record.p.frmask = mask;
+ /* Retain only least significant bit. */
+ prev->r.record.p.frmask ^= mask;
+ prev->r.record.p.next = cur;
+ }
+}
+
+static unw_rec_list *
+output_frgr_mem (unsigned int gr_mask, unsigned int fr_mask)
+{
+ unw_rec_list *ptr = alloc_record (frgr_mem);
+ unw_rec_list *cur = ptr;
+
+ unwind.pending_saves = &cur->r.record.p;
+ cur->r.record.p.frmask = fr_mask;
+ while (fr_mask)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ fr_mask &= ~(fr_mask & (~fr_mask + 1));
+ if (!gr_mask && !fr_mask)
+ return ptr;
+ cur = alloc_record (frgr_mem);
+ cur->r.record.p.frmask = fr_mask;
+ /* Retain only least significant bit. */
+ prev->r.record.p.frmask ^= fr_mask;
+ prev->r.record.p.next = cur;
+ }
+ cur->r.record.p.grmask = gr_mask;
+ for (;;)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ gr_mask &= ~(gr_mask & (~gr_mask + 1));
+ if (!gr_mask)
+ return ptr;
+ cur = alloc_record (frgr_mem);
+ cur->r.record.p.grmask = gr_mask;
+ /* Retain only least significant bit. */
+ prev->r.record.p.grmask ^= gr_mask;
+ prev->r.record.p.next = cur;
+ }
+}
+
+static unw_rec_list *
+output_gr_gr (unsigned int mask, unsigned int reg)
+{
+ unw_rec_list *ptr = alloc_record (gr_gr);
+ unw_rec_list *cur = ptr;
+
+ ptr->r.record.p.grmask = mask;
+ ptr->r.record.p.r.gr = reg;
+ unwind.pending_saves = &ptr->r.record.p;
+ for (;;)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ mask &= ~(mask & (~mask + 1));
+ if (!mask)
+ return ptr;
+ cur = alloc_record (gr_gr);
+ cur->r.record.p.grmask = mask;
+ /* Indicate this record shouldn't be output. */
+ cur->r.record.p.r.gr = REG_NUM;
+ /* Retain only least significant bit. */
+ prev->r.record.p.grmask ^= mask;
+ prev->r.record.p.next = cur;
+ }
+}
+
+static unw_rec_list *
+output_gr_mem (unsigned int mask)
+{
+ unw_rec_list *ptr = alloc_record (gr_mem);
+ unw_rec_list *cur = ptr;
+
+ ptr->r.record.p.grmask = mask;
+ unwind.pending_saves = &ptr->r.record.p;
+ for (;;)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ mask &= ~(mask & (~mask + 1));
+ if (!mask)
+ return ptr;
+ cur = alloc_record (gr_mem);
+ cur->r.record.p.grmask = mask;
+ /* Retain only least significant bit. */
+ prev->r.record.p.grmask ^= mask;
+ prev->r.record.p.next = cur;
+ }
+}
+
+static unw_rec_list *
+output_br_mem (unsigned int mask)
+{
+ unw_rec_list *ptr = alloc_record (br_mem);
+ unw_rec_list *cur = ptr;
+
+ ptr->r.record.p.brmask = mask;
+ unwind.pending_saves = &ptr->r.record.p;
+ for (;;)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ mask &= ~(mask & (~mask + 1));
+ if (!mask)
+ return ptr;
+ cur = alloc_record (br_mem);
+ cur->r.record.p.brmask = mask;
+ /* Retain only least significant bit. */
+ prev->r.record.p.brmask ^= mask;
+ prev->r.record.p.next = cur;
+ }
+}
+
+static unw_rec_list *
+output_br_gr (unsigned int mask, unsigned int reg)
+{
+ unw_rec_list *ptr = alloc_record (br_gr);
+ unw_rec_list *cur = ptr;
+
+ ptr->r.record.p.brmask = mask;
+ ptr->r.record.p.r.gr = reg;
+ unwind.pending_saves = &ptr->r.record.p;
+ for (;;)
+ {
+ unw_rec_list *prev = cur;
+
+ /* Clear least significant set bit. */
+ mask &= ~(mask & (~mask + 1));
+ if (!mask)
+ return ptr;
+ cur = alloc_record (br_gr);
+ cur->r.record.p.brmask = mask;
+ /* Indicate this record shouldn't be output. */
+ cur->r.record.p.r.gr = REG_NUM;
+ /* Retain only least significant bit. */
+ prev->r.record.p.brmask ^= mask;
+ prev->r.record.p.next = cur;
+ }
+}
+
+static unw_rec_list *
+output_spill_base (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (spill_base);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_unat_when (void)
+{
+ unw_rec_list *ptr = alloc_record (unat_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_unat_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (unat_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_unat_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (unat_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_unat_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (unat_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_lc_when (void)
+{
+ unw_rec_list *ptr = alloc_record (lc_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_lc_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (lc_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_lc_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (lc_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_lc_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (lc_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_fpsr_when (void)
+{
+ unw_rec_list *ptr = alloc_record (fpsr_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_fpsr_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (fpsr_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_fpsr_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (fpsr_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_fpsr_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (fpsr_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_priunat_when_gr (void)
+{
+ unw_rec_list *ptr = alloc_record (priunat_when_gr);
+ return ptr;
+}
+
+static unw_rec_list *
+output_priunat_when_mem (void)
+{
+ unw_rec_list *ptr = alloc_record (priunat_when_mem);
+ return ptr;
+}
+
+static unw_rec_list *
+output_priunat_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (priunat_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_priunat_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (priunat_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_priunat_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (priunat_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_bsp_when (void)
+{
+ unw_rec_list *ptr = alloc_record (bsp_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_bsp_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (bsp_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_bsp_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (bsp_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_bsp_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (bsp_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_bspstore_when (void)
+{
+ unw_rec_list *ptr = alloc_record (bspstore_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_bspstore_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (bspstore_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_bspstore_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (bspstore_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_bspstore_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (bspstore_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_rnat_when (void)
+{
+ unw_rec_list *ptr = alloc_record (rnat_when);
+ return ptr;
+}
+
+static unw_rec_list *
+output_rnat_gr (unsigned int gr)
+{
+ unw_rec_list *ptr = alloc_record (rnat_gr);
+ ptr->r.record.p.r.gr = gr;
+ return ptr;
+}
+
+static unw_rec_list *
+output_rnat_psprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (rnat_psprel);
+ ptr->r.record.p.off.psp = ENCODED_PSP_OFFSET (offset);
+ return ptr;
+}
+
+static unw_rec_list *
+output_rnat_sprel (unsigned int offset)
+{
+ unw_rec_list *ptr = alloc_record (rnat_sprel);
+ ptr->r.record.p.off.sp = offset / 4;
+ return ptr;
+}
+
+static unw_rec_list *
+output_unwabi (unsigned long abi, unsigned long context)
+{
+ unw_rec_list *ptr = alloc_record (unwabi);
+ ptr->r.record.p.abi = abi;
+ ptr->r.record.p.context = context;
+ return ptr;
+}
+
+static unw_rec_list *
+output_epilogue (unsigned long ecount)
+{
+ unw_rec_list *ptr = alloc_record (epilogue);
+ ptr->r.record.b.ecount = ecount;
+ return ptr;
+}
+
+static unw_rec_list *
+output_label_state (unsigned long label)
+{
+ unw_rec_list *ptr = alloc_record (label_state);
+ ptr->r.record.b.label = label;
+ return ptr;
+}
+
+static unw_rec_list *
+output_copy_state (unsigned long label)
+{
+ unw_rec_list *ptr = alloc_record (copy_state);
+ ptr->r.record.b.label = label;
+ return ptr;
+}
+
+static unw_rec_list *
+output_spill_psprel (unsigned int ab,
+ unsigned int reg,
+ unsigned int offset,
+ unsigned int predicate)
+{
+ unw_rec_list *ptr = alloc_record (predicate ? spill_psprel_p : spill_psprel);
+ ptr->r.record.x.ab = ab;
+ ptr->r.record.x.reg = reg;
+ ptr->r.record.x.where.pspoff = ENCODED_PSP_OFFSET (offset);
+ ptr->r.record.x.qp = predicate;
+ return ptr;
+}
+
+static unw_rec_list *
+output_spill_sprel (unsigned int ab,
+ unsigned int reg,
+ unsigned int offset,
+ unsigned int predicate)
+{
+ unw_rec_list *ptr = alloc_record (predicate ? spill_sprel_p : spill_sprel);
+ ptr->r.record.x.ab = ab;
+ ptr->r.record.x.reg = reg;
+ ptr->r.record.x.where.spoff = offset / 4;
+ ptr->r.record.x.qp = predicate;
+ return ptr;
+}
+
+static unw_rec_list *
+output_spill_reg (unsigned int ab,
+ unsigned int reg,
+ unsigned int targ_reg,
+ unsigned int xy,
+ unsigned int predicate)
+{
+ unw_rec_list *ptr = alloc_record (predicate ? spill_reg_p : spill_reg);
+ ptr->r.record.x.ab = ab;
+ ptr->r.record.x.reg = reg;
+ ptr->r.record.x.where.reg = targ_reg;
+ ptr->r.record.x.xy = xy;
+ ptr->r.record.x.qp = predicate;
+ return ptr;
+}
+
+/* Given a unw_rec_list process the correct format with the
+ specified function. */
+
+static void
+process_one_record (unw_rec_list *ptr, vbyte_func f)
+{
+ unsigned int fr_mask, gr_mask;
+
+ switch (ptr->r.type)
+ {
+ /* This is a dummy record that takes up no space in the output. */
+ case endp:
+ break;
+
+ case gr_mem:
+ case fr_mem:
+ case br_mem:
+ case frgr_mem:
+ /* These are taken care of by prologue/prologue_gr. */
+ break;
+
+ case prologue_gr:
+ case prologue:
+ if (ptr->r.type == prologue_gr)
+ output_R2_format (f, ptr->r.record.r.grmask,
+ ptr->r.record.r.grsave, ptr->r.record.r.rlen);
+ else
+ output_R1_format (f, ptr->r.type, ptr->r.record.r.rlen);
+
+ /* Output descriptor(s) for union of register spills (if any). */
+ gr_mask = ptr->r.record.r.mask.gr_mem;
+ fr_mask = ptr->r.record.r.mask.fr_mem;
+ if (fr_mask)
+ {
+ if ((fr_mask & ~0xfUL) == 0)
+ output_P6_format (f, fr_mem, fr_mask);
+ else
+ {
+ output_P5_format (f, gr_mask, fr_mask);
+ gr_mask = 0;
+ }
+ }
+ if (gr_mask)
+ output_P6_format (f, gr_mem, gr_mask);
+ if (ptr->r.record.r.mask.br_mem)
+ output_P1_format (f, ptr->r.record.r.mask.br_mem);
+
+ /* output imask descriptor if necessary: */
+ if (ptr->r.record.r.mask.i)
+ output_P4_format (f, ptr->r.record.r.mask.i,
+ ptr->r.record.r.imask_size);
+ break;
+
+ case body:
+ output_R1_format (f, ptr->r.type, ptr->r.record.r.rlen);
+ break;
+ case mem_stack_f:
+ case mem_stack_v:
+ output_P7_format (f, ptr->r.type, ptr->r.record.p.t,
+ ptr->r.record.p.size);
+ break;
+ case psp_gr:
+ case rp_gr:
+ case pfs_gr:
+ case preds_gr:
+ case unat_gr:
+ case lc_gr:
+ case fpsr_gr:
+ case priunat_gr:
+ case bsp_gr:
+ case bspstore_gr:
+ case rnat_gr:
+ output_P3_format (f, ptr->r.type, ptr->r.record.p.r.gr);
+ break;
+ case rp_br:
+ output_P3_format (f, rp_br, ptr->r.record.p.r.br);
+ break;
+ case psp_sprel:
+ output_P7_format (f, psp_sprel, ptr->r.record.p.off.sp, 0);
+ break;
+ case rp_when:
+ case pfs_when:
+ case preds_when:
+ case unat_when:
+ case lc_when:
+ case fpsr_when:
+ output_P7_format (f, ptr->r.type, ptr->r.record.p.t, 0);
+ break;
+ case rp_psprel:
+ case pfs_psprel:
+ case preds_psprel:
+ case unat_psprel:
+ case lc_psprel:
+ case fpsr_psprel:
+ case spill_base:
+ output_P7_format (f, ptr->r.type, ptr->r.record.p.off.psp, 0);
+ break;
+ case rp_sprel:
+ case pfs_sprel:
+ case preds_sprel:
+ case unat_sprel:
+ case lc_sprel:
+ case fpsr_sprel:
+ case priunat_sprel:
+ case bsp_sprel:
+ case bspstore_sprel:
+ case rnat_sprel:
+ output_P8_format (f, ptr->r.type, ptr->r.record.p.off.sp);
+ break;
+ case gr_gr:
+ if (ptr->r.record.p.r.gr < REG_NUM)
+ {
+ const unw_rec_list *cur = ptr;
+
+ gr_mask = cur->r.record.p.grmask;
+ while ((cur = cur->r.record.p.next) != NULL)
+ gr_mask |= cur->r.record.p.grmask;
+ output_P9_format (f, gr_mask, ptr->r.record.p.r.gr);
+ }
+ break;
+ case br_gr:
+ if (ptr->r.record.p.r.gr < REG_NUM)
+ {
+ const unw_rec_list *cur = ptr;
+
+ gr_mask = cur->r.record.p.brmask;
+ while ((cur = cur->r.record.p.next) != NULL)
+ gr_mask |= cur->r.record.p.brmask;
+ output_P2_format (f, gr_mask, ptr->r.record.p.r.gr);
+ }
+ break;
+ case spill_mask:
+ as_bad (_("spill_mask record unimplemented."));
+ break;
+ case priunat_when_gr:
+ case priunat_when_mem:
+ case bsp_when:
+ case bspstore_when:
+ case rnat_when:
+ output_P8_format (f, ptr->r.type, ptr->r.record.p.t);
+ break;
+ case priunat_psprel:
+ case bsp_psprel:
+ case bspstore_psprel:
+ case rnat_psprel:
+ output_P8_format (f, ptr->r.type, ptr->r.record.p.off.psp);
+ break;
+ case unwabi:
+ output_P10_format (f, ptr->r.record.p.abi, ptr->r.record.p.context);
+ break;
+ case epilogue:
+ output_B3_format (f, ptr->r.record.b.ecount, ptr->r.record.b.t);
+ break;
+ case label_state:
+ case copy_state:
+ output_B4_format (f, ptr->r.type, ptr->r.record.b.label);
+ break;
+ case spill_psprel:
+ output_X1_format (f, ptr->r.type, ptr->r.record.x.ab,
+ ptr->r.record.x.reg, ptr->r.record.x.t,
+ ptr->r.record.x.where.pspoff);
+ break;
+ case spill_sprel:
+ output_X1_format (f, ptr->r.type, ptr->r.record.x.ab,
+ ptr->r.record.x.reg, ptr->r.record.x.t,
+ ptr->r.record.x.where.spoff);
+ break;
+ case spill_reg:
+ output_X2_format (f, ptr->r.record.x.ab, ptr->r.record.x.reg,
+ ptr->r.record.x.xy >> 1, ptr->r.record.x.xy,
+ ptr->r.record.x.where.reg, ptr->r.record.x.t);
+ break;
+ case spill_psprel_p:
+ output_X3_format (f, ptr->r.type, ptr->r.record.x.qp,
+ ptr->r.record.x.ab, ptr->r.record.x.reg,
+ ptr->r.record.x.t, ptr->r.record.x.where.pspoff);
+ break;
+ case spill_sprel_p:
+ output_X3_format (f, ptr->r.type, ptr->r.record.x.qp,
+ ptr->r.record.x.ab, ptr->r.record.x.reg,
+ ptr->r.record.x.t, ptr->r.record.x.where.spoff);
+ break;
+ case spill_reg_p:
+ output_X4_format (f, ptr->r.record.x.qp, ptr->r.record.x.ab,
+ ptr->r.record.x.reg, ptr->r.record.x.xy >> 1,
+ ptr->r.record.x.xy, ptr->r.record.x.where.reg,
+ ptr->r.record.x.t);
+ break;
+ default:
+ as_bad (_("record_type_not_valid"));
+ break;
+ }
+}
+
+/* Given a unw_rec_list list, process all the records with
+ the specified function. */
+static void
+process_unw_records (unw_rec_list *list, vbyte_func f)
+{
+ unw_rec_list *ptr;
+ for (ptr = list; ptr; ptr = ptr->next)
+ process_one_record (ptr, f);
+}
+
+/* Determine the size of a record list in bytes. */
+static int
+calc_record_size (unw_rec_list *list)
+{
+ vbyte_count = 0;
+ process_unw_records (list, count_output);
+ return vbyte_count;
+}
+
+/* Return the number of bits set in the input value.
+ Perhaps this has a better place... */
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+# define popcount __builtin_popcount
+#else
+static int
+popcount (unsigned x)
+{
+ static const unsigned char popcnt[16] =
+ {
+ 0, 1, 1, 2,
+ 1, 2, 2, 3,
+ 1, 2, 2, 3,
+ 2, 3, 3, 4
+ };
+
+ if (x < NELEMS (popcnt))
+ return popcnt[x];
+ return popcnt[x % NELEMS (popcnt)] + popcount (x / NELEMS (popcnt));
+}
+#endif
+
+/* Update IMASK bitmask to reflect the fact that one or more registers
+ of type TYPE are saved starting at instruction with index T. If N
+ bits are set in REGMASK, it is assumed that instructions T through
+ T+N-1 save these registers.
+
+ TYPE values:
+ 0: no save
+ 1: instruction saves next fp reg
+ 2: instruction saves next general reg
+ 3: instruction saves next branch reg */
+static void
+set_imask (unw_rec_list *region,
+ unsigned long regmask,
+ unsigned long t,
+ unsigned int type)
+{
+ unsigned char *imask;
+ unsigned long imask_size;
+ unsigned int i;
+ int pos;
+
+ imask = region->r.record.r.mask.i;
+ imask_size = region->r.record.r.imask_size;
+ if (!imask)
+ {
+ imask_size = (region->r.record.r.rlen * 2 + 7) / 8 + 1;
+ imask = xmalloc (imask_size);
+ memset (imask, 0, imask_size);
+
+ region->r.record.r.imask_size = imask_size;
+ region->r.record.r.mask.i = imask;
+ }
+
+ i = (t / 4) + 1;
+ pos = 2 * (3 - t % 4);
+ while (regmask)
+ {
+ if (i >= imask_size)
+ {
+ as_bad (_("Ignoring attempt to spill beyond end of region"));
+ return;
+ }
+
+ imask[i] |= (type & 0x3) << pos;
+
+ regmask &= (regmask - 1);
+ pos -= 2;
+ if (pos < 0)
+ {
+ pos = 0;
+ ++i;
+ }
+ }
+}
+
+/* Return the number of instruction slots from FIRST_ADDR to SLOT_ADDR.
+ SLOT_FRAG is the frag containing SLOT_ADDR, and FIRST_FRAG is the frag
+ containing FIRST_ADDR. If BEFORE_RELAX, then we use worst-case estimates
+ for frag sizes. */
+
+static unsigned long
+slot_index (unsigned long slot_addr,
+ fragS *slot_frag,
+ unsigned long first_addr,
+ fragS *first_frag,
+ int before_relax)
+{
+ unsigned long s_index = 0;
+
+ /* First time we are called, the initial address and frag are invalid. */
+ if (first_addr == 0)
+ return 0;
+
+ /* If the two addresses are in different frags, then we need to add in
+ the remaining size of this frag, and then the entire size of intermediate
+ frags. */
+ while (slot_frag != first_frag)
+ {
+ unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
+
+ if (! before_relax)
+ {
+ /* We can get the final addresses only during and after
+ relaxation. */
+ if (first_frag->fr_next && first_frag->fr_next->fr_address)
+ s_index += 3 * ((first_frag->fr_next->fr_address
+ - first_frag->fr_address
+ - first_frag->fr_fix) >> 4);
+ }
+ else
+ /* We don't know what the final addresses will be. We try our
+ best to estimate. */
+ switch (first_frag->fr_type)
+ {
+ default:
+ break;
+
+ case rs_space:
+ as_fatal (_("Only constant space allocation is supported"));
+ break;
+
+ case rs_align:
+ case rs_align_code:
+ case rs_align_test:
+ /* Take alignment into account. Assume the worst case
+ before relaxation. */
+ s_index += 3 * ((1 << first_frag->fr_offset) >> 4);
+ break;
+
+ case rs_org:
+ if (first_frag->fr_symbol)
+ {
+ as_fatal (_("Only constant offsets are supported"));
+ break;
+ }
+ case rs_fill:
+ s_index += 3 * (first_frag->fr_offset >> 4);
+ break;
+ }
+
+ /* Add in the full size of the frag converted to instruction slots. */
+ s_index += 3 * (first_frag->fr_fix >> 4);
+ /* Subtract away the initial part before first_addr. */
+ s_index -= (3 * ((first_addr >> 4) - (start_addr >> 4))
+ + ((first_addr & 0x3) - (start_addr & 0x3)));
+
+ /* Move to the beginning of the next frag. */
+ first_frag = first_frag->fr_next;
+ first_addr = (unsigned long) &first_frag->fr_literal;
+
+ /* This can happen if there is section switching in the middle of a
+ function, causing the frag chain for the function to be broken.
+ It is too difficult to recover safely from this problem, so we just
+ exit with an error. */
+ if (first_frag == NULL)
+ as_fatal (_("Section switching in code is not supported."));
+ }
+
+ /* Add in the used part of the last frag. */
+ s_index += (3 * ((slot_addr >> 4) - (first_addr >> 4))
+ + ((slot_addr & 0x3) - (first_addr & 0x3)));
+ return s_index;
+}
+
+/* Optimize unwind record directives. */
+
+static unw_rec_list *
+optimize_unw_records (unw_rec_list *list)
+{
+ if (!list)
+ return NULL;
+
+ /* If the only unwind record is ".prologue" or ".prologue" followed
+ by ".body", then we can optimize the unwind directives away. */
+ if (list->r.type == prologue
+ && (list->next->r.type == endp
+ || (list->next->r.type == body && list->next->next->r.type == endp)))
+ return NULL;
+
+ return list;
+}
+
+/* Given a complete record list, process any records which have
+ unresolved fields, (ie length counts for a prologue). After
+ this has been run, all necessary information should be available
+ within each record to generate an image. */
+
+static void
+fixup_unw_records (unw_rec_list *list, int before_relax)
+{
+ unw_rec_list *ptr, *region = 0;
+ unsigned long first_addr = 0, rlen = 0, t;
+ fragS *first_frag = 0;
+
+ for (ptr = list; ptr; ptr = ptr->next)
+ {
+ if (ptr->slot_number == SLOT_NUM_NOT_SET)
+ as_bad (_(" Insn slot not set in unwind record."));
+ t = slot_index (ptr->slot_number, ptr->slot_frag,
+ first_addr, first_frag, before_relax);
+ switch (ptr->r.type)
+ {
+ case prologue:
+ case prologue_gr:
+ case body:
+ {
+ unw_rec_list *last;
+ int size;
+ unsigned long last_addr = 0;
+ fragS *last_frag = NULL;
+
+ first_addr = ptr->slot_number;
+ first_frag = ptr->slot_frag;
+ /* Find either the next body/prologue start, or the end of
+ the function, and determine the size of the region. */
+ for (last = ptr->next; last != NULL; last = last->next)
+ if (last->r.type == prologue || last->r.type == prologue_gr
+ || last->r.type == body || last->r.type == endp)
+ {
+ last_addr = last->slot_number;
+ last_frag = last->slot_frag;
+ break;
+ }
+ size = slot_index (last_addr, last_frag, first_addr, first_frag,
+ before_relax);
+ rlen = ptr->r.record.r.rlen = size;
+ if (ptr->r.type == body)
+ /* End of region. */
+ region = 0;
+ else
+ region = ptr;
+ break;
+ }
+ case epilogue:
+ if (t < rlen)
+ ptr->r.record.b.t = rlen - 1 - t;
+ else
+ /* This happens when a memory-stack-less procedure uses a
+ ".restore sp" directive at the end of a region to pop
+ the frame state. */
+ ptr->r.record.b.t = 0;
+ break;
+
+ case mem_stack_f:
+ case mem_stack_v:
+ case rp_when:
+ case pfs_when:
+ case preds_when:
+ case unat_when:
+ case lc_when:
+ case fpsr_when:
+ case priunat_when_gr:
+ case priunat_when_mem:
+ case bsp_when:
+ case bspstore_when:
+ case rnat_when:
+ ptr->r.record.p.t = t;
+ break;
+
+ case spill_reg:
+ case spill_sprel:
+ case spill_psprel:
+ case spill_reg_p:
+ case spill_sprel_p:
+ case spill_psprel_p:
+ ptr->r.record.x.t = t;
+ break;
+
+ case frgr_mem:
+ if (!region)
+ {
+ as_bad (_("frgr_mem record before region record!"));
+ return;
+ }
+ region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
+ region->r.record.r.mask.gr_mem |= ptr->r.record.p.grmask;
+ set_imask (region, ptr->r.record.p.frmask, t, 1);
+ set_imask (region, ptr->r.record.p.grmask, t, 2);
+ break;
+ case fr_mem:
+ if (!region)
+ {
+ as_bad (_("fr_mem record before region record!"));
+ return;
+ }
+ region->r.record.r.mask.fr_mem |= ptr->r.record.p.frmask;
+ set_imask (region, ptr->r.record.p.frmask, t, 1);
+ break;
+ case gr_mem:
+ if (!region)
+ {
+ as_bad (_("gr_mem record before region record!"));
+ return;
+ }
+ region->r.record.r.mask.gr_mem |= ptr->r.record.p.grmask;
+ set_imask (region, ptr->r.record.p.grmask, t, 2);
+ break;
+ case br_mem:
+ if (!region)
+ {
+ as_bad (_("br_mem record before region record!"));
+ return;
+ }
+ region->r.record.r.mask.br_mem |= ptr->r.record.p.brmask;
+ set_imask (region, ptr->r.record.p.brmask, t, 3);
+ break;
+
+ case gr_gr:
+ if (!region)
+ {
+ as_bad (_("gr_gr record before region record!"));
+ return;
+ }
+ set_imask (region, ptr->r.record.p.grmask, t, 2);
+ break;
+ case br_gr:
+ if (!region)
+ {
+ as_bad (_("br_gr record before region record!"));
+ return;
+ }
+ set_imask (region, ptr->r.record.p.brmask, t, 3);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/* Estimate the size of a frag before relaxing. We only have one type of frag
+ to handle here, which is the unwind info frag. */
+
+int
+ia64_estimate_size_before_relax (fragS *frag,
+ asection *segtype ATTRIBUTE_UNUSED)
+{
+ unw_rec_list *list;
+ int len, size, pad;
+
+ /* ??? This code is identical to the first part of ia64_convert_frag. */
+ list = (unw_rec_list *) frag->fr_opcode;
+ fixup_unw_records (list, 0);
+
+ len = calc_record_size (list);
+ /* pad to pointer-size boundary. */
+ pad = len % md.pointer_size;
+ if (pad != 0)
+ len += md.pointer_size - pad;
+ /* Add 8 for the header. */
+ size = len + 8;
+ /* Add a pointer for the personality offset. */
+ if (frag->fr_offset)
+ size += md.pointer_size;
+
+ /* fr_var carries the max_chars that we created the fragment with.
+ We must, of course, have allocated enough memory earlier. */
+ gas_assert (frag->fr_var >= size);
+
+ return frag->fr_fix + size;
+}
+
+/* This function converts a rs_machine_dependent variant frag into a
+ normal fill frag with the unwind image from the record list. */
+void
+ia64_convert_frag (fragS *frag)
+{
+ unw_rec_list *list;
+ int len, size, pad;
+ valueT flag_value;
+
+ /* ??? This code is identical to ia64_estimate_size_before_relax. */
+ list = (unw_rec_list *) frag->fr_opcode;
+ fixup_unw_records (list, 0);
+
+ len = calc_record_size (list);
+ /* pad to pointer-size boundary. */
+ pad = len % md.pointer_size;
+ if (pad != 0)
+ len += md.pointer_size - pad;
+ /* Add 8 for the header. */
+ size = len + 8;
+ /* Add a pointer for the personality offset. */
+ if (frag->fr_offset)
+ size += md.pointer_size;
+
+ /* fr_var carries the max_chars that we created the fragment with.
+ We must, of course, have allocated enough memory earlier. */
+ gas_assert (frag->fr_var >= size);
+
+ /* Initialize the header area. fr_offset is initialized with
+ unwind.personality_routine. */
+ if (frag->fr_offset)
+ {
+ if (md.flags & EF_IA_64_ABI64)
+ flag_value = (bfd_vma) 3 << 32;
+ else
+ /* 32-bit unwind info block. */
+ flag_value = (bfd_vma) 0x1003 << 32;
+ }
+ else
+ flag_value = 0;
+
+ md_number_to_chars (frag->fr_literal,
+ (((bfd_vma) 1 << 48) /* Version. */
+ | flag_value /* U & E handler flags. */
+ | (len / md.pointer_size)), /* Length. */
+ 8);
+
+ /* Skip the header. */
+ vbyte_mem_ptr = frag->fr_literal + 8;
+ process_unw_records (list, output_vbyte_mem);
+
+ /* Fill the padding bytes with zeros. */
+ if (pad != 0)
+ md_number_to_chars (frag->fr_literal + len + 8 - md.pointer_size + pad, 0,
+ md.pointer_size - pad);
+ /* Fill the unwind personality with zeros. */
+ if (frag->fr_offset)
+ md_number_to_chars (frag->fr_literal + size - md.pointer_size, 0,
+ md.pointer_size);
+
+ frag->fr_fix += size;
+ frag->fr_type = rs_fill;
+ frag->fr_var = 0;
+ frag->fr_offset = 0;
+}
+
+static int
+parse_predicate_and_operand (expressionS *e, unsigned *qp, const char *po)
+{
+ int sep = parse_operand_and_eval (e, ',');
+
+ *qp = e->X_add_number - REG_P;
+ if (e->X_op != O_register || *qp > 63)
+ {
+ as_bad (_("First operand to .%s must be a predicate"), po);
+ *qp = 0;
+ }
+ else if (*qp == 0)
+ as_warn (_("Pointless use of p0 as first operand to .%s"), po);
+ if (sep == ',')
+ sep = parse_operand_and_eval (e, ',');
+ else
+ e->X_op = O_absent;
+ return sep;
+}
+
+static void
+convert_expr_to_ab_reg (const expressionS *e,
+ unsigned int *ab,
+ unsigned int *regp,
+ const char *po,
+ int n)
+{
+ unsigned int reg = e->X_add_number;
+
+ *ab = *regp = 0; /* Anything valid is good here. */
+
+ if (e->X_op != O_register)
+ reg = REG_GR; /* Anything invalid is good here. */
+
+ if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7))
+ {
+ *ab = 0;
+ *regp = reg - REG_GR;
+ }
+ else if ((reg >= (REG_FR + 2) && reg <= (REG_FR + 5))
+ || (reg >= (REG_FR + 16) && reg <= (REG_FR + 31)))
+ {
+ *ab = 1;
+ *regp = reg - REG_FR;
+ }
+ else if (reg >= (REG_BR + 1) && reg <= (REG_BR + 5))
+ {
+ *ab = 2;
+ *regp = reg - REG_BR;
+ }
+ else
+ {
+ *ab = 3;
+ switch (reg)
+ {
+ case REG_PR: *regp = 0; break;
+ case REG_PSP: *regp = 1; break;
+ case REG_PRIUNAT: *regp = 2; break;
+ case REG_BR + 0: *regp = 3; break;
+ case REG_AR + AR_BSP: *regp = 4; break;
+ case REG_AR + AR_BSPSTORE: *regp = 5; break;
+ case REG_AR + AR_RNAT: *regp = 6; break;
+ case REG_AR + AR_UNAT: *regp = 7; break;
+ case REG_AR + AR_FPSR: *regp = 8; break;
+ case REG_AR + AR_PFS: *regp = 9; break;
+ case REG_AR + AR_LC: *regp = 10; break;
+
+ default:
+ as_bad (_("Operand %d to .%s must be a preserved register"), n, po);
+ break;
+ }
+ }
+}
+
+static void
+convert_expr_to_xy_reg (const expressionS *e,
+ unsigned int *xy,
+ unsigned int *regp,
+ const char *po,
+ int n)
+{
+ unsigned int reg = e->X_add_number;
+
+ *xy = *regp = 0; /* Anything valid is good here. */
+
+ if (e->X_op != O_register)
+ reg = REG_GR; /* Anything invalid is good here. */
+
+ if (reg >= (REG_GR + 1) && reg <= (REG_GR + 127))
+ {
+ *xy = 0;
+ *regp = reg - REG_GR;
+ }
+ else if (reg >= (REG_FR + 2) && reg <= (REG_FR + 127))
+ {
+ *xy = 1;
+ *regp = reg - REG_FR;
+ }
+ else if (reg >= REG_BR && reg <= (REG_BR + 7))
+ {
+ *xy = 2;
+ *regp = reg - REG_BR;
+ }
+ else
+ as_bad (_("Operand %d to .%s must be a writable register"), n, po);
+}
+
+static void
+dot_align (int arg)
+{
+ /* The current frag is an alignment frag. */
+ align_frag = frag_now;
+ s_align_bytes (arg);
+}
+
+static void
+dot_radix (int dummy ATTRIBUTE_UNUSED)
+{
+ char *radix;
+ int ch;
+
+ SKIP_WHITESPACE ();
+
+ if (is_it_end_of_statement ())
+ return;
+ radix = input_line_pointer;
+ ch = get_symbol_end ();
+ ia64_canonicalize_symbol_name (radix);
+ if (strcasecmp (radix, "C"))
+ as_bad (_("Radix `%s' unsupported or invalid"), radix);
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Helper function for .loc directives. If the assembler is not generating
+ line number info, then we need to remember which instructions have a .loc
+ directive, and only call dwarf2_gen_line_info for those instructions. */
+
+static void
+dot_loc (int x)
+{
+ CURR_SLOT.loc_directive_seen = 1;
+ dwarf2_directive_loc (x);
+}
+
+/* .sbss, .bss etc. are macros that expand into ".section SECNAME". */
+static void
+dot_special_section (int which)
+{
+ set_section ((char *) special_section_name[which]);
+}
+
+/* Return -1 for warning and 0 for error. */
+
+static int
+unwind_diagnostic (const char * region, const char *directive)
+{
+ if (md.unwind_check == unwind_check_warning)
+ {
+ as_warn (_(".%s outside of %s"), directive, region);
+ return -1;
+ }
+ else
+ {
+ as_bad (_(".%s outside of %s"), directive, region);
+ ignore_rest_of_line ();
+ return 0;
+ }
+}
+
+/* Return 1 if a directive is in a procedure, -1 if a directive isn't in
+ a procedure but the unwind directive check is set to warning, 0 if
+ a directive isn't in a procedure and the unwind directive check is set
+ to error. */
+
+static int
+in_procedure (const char *directive)
+{
+ if (unwind.proc_pending.sym
+ && (!unwind.saved_text_seg || strcmp (directive, "endp") == 0))
+ return 1;
+ return unwind_diagnostic ("procedure", directive);
+}
+
+/* Return 1 if a directive is in a prologue, -1 if a directive isn't in
+ a prologue but the unwind directive check is set to warning, 0 if
+ a directive isn't in a prologue and the unwind directive check is set
+ to error. */
+
+static int
+in_prologue (const char *directive)
+{
+ int in = in_procedure (directive);
+
+ if (in > 0 && !unwind.prologue)
+ in = unwind_diagnostic ("prologue", directive);
+ check_pending_save ();
+ return in;
+}
+
+/* Return 1 if a directive is in a body, -1 if a directive isn't in
+ a body but the unwind directive check is set to warning, 0 if
+ a directive isn't in a body and the unwind directive check is set
+ to error. */
+
+static int
+in_body (const char *directive)
+{
+ int in = in_procedure (directive);
+
+ if (in > 0 && !unwind.body)
+ in = unwind_diagnostic ("body region", directive);
+ return in;
+}
+
+static void
+add_unwind_entry (unw_rec_list *ptr, int sep)
+{
+ if (ptr)
+ {
+ if (unwind.tail)
+ unwind.tail->next = ptr;
+ else
+ unwind.list = ptr;
+ unwind.tail = ptr;
+
+ /* The current entry can in fact be a chain of unwind entries. */
+ if (unwind.current_entry == NULL)
+ unwind.current_entry = ptr;
+ }
+
+ /* The current entry can in fact be a chain of unwind entries. */
+ if (unwind.current_entry == NULL)
+ unwind.current_entry = ptr;
+
+ if (sep == ',')
+ {
+ /* Parse a tag permitted for the current directive. */
+ int ch;
+
+ SKIP_WHITESPACE ();
+ ch = get_symbol_end ();
+ /* FIXME: For now, just issue a warning that this isn't implemented. */
+ {
+ static int warned;
+
+ if (!warned)
+ {
+ warned = 1;
+ as_warn (_("Tags on unwind pseudo-ops aren't supported, yet"));
+ }
+ }
+ *input_line_pointer = ch;
+ }
+ if (sep != NOT_A_CHAR)
+ demand_empty_rest_of_line ();
+}
+
+static void
+dot_fframe (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ int sep;
+
+ if (!in_prologue ("fframe"))
+ return;
+
+ sep = parse_operand_and_eval (&e, ',');
+
+ if (e.X_op != O_constant)
+ {
+ as_bad (_("First operand to .fframe must be a constant"));
+ e.X_add_number = 0;
+ }
+ add_unwind_entry (output_mem_stack_f (e.X_add_number), sep);
+}
+
+static void
+dot_vframe (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ unsigned reg;
+ int sep;
+
+ if (!in_prologue ("vframe"))
+ return;
+
+ sep = parse_operand_and_eval (&e, ',');
+ reg = e.X_add_number - REG_GR;
+ if (e.X_op != O_register || reg > 127)
+ {
+ as_bad (_("First operand to .vframe must be a general register"));
+ reg = 0;
+ }
+ add_unwind_entry (output_mem_stack_v (), sep);
+ if (! (unwind.prologue_mask & 2))
+ add_unwind_entry (output_psp_gr (reg), NOT_A_CHAR);
+ else if (reg != unwind.prologue_gr
+ + (unsigned) popcount (unwind.prologue_mask & (-2 << 1)))
+ as_warn (_("Operand of .vframe contradicts .prologue"));
+}
+
+static void
+dot_vframesp (int psp)
+{
+ expressionS e;
+ int sep;
+
+ if (psp)
+ as_warn (_(".vframepsp is meaningless, assuming .vframesp was meant"));
+
+ if (!in_prologue ("vframesp"))
+ return;
+
+ sep = parse_operand_and_eval (&e, ',');
+ if (e.X_op != O_constant)
+ {
+ as_bad (_("Operand to .vframesp must be a constant (sp-relative offset)"));
+ e.X_add_number = 0;
+ }
+ add_unwind_entry (output_mem_stack_v (), sep);
+ add_unwind_entry (output_psp_sprel (e.X_add_number), NOT_A_CHAR);
+}
+
+static void
+dot_save (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e1, e2;
+ unsigned reg1, reg2;
+ int sep;
+
+ if (!in_prologue ("save"))
+ return;
+
+ sep = parse_operand_and_eval (&e1, ',');
+ if (sep == ',')
+ sep = parse_operand_and_eval (&e2, ',');
+ else
+ e2.X_op = O_absent;
+
+ reg1 = e1.X_add_number;
+ /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'. */
+ if (e1.X_op != O_register)
+ {
+ as_bad (_("First operand to .save not a register"));
+ reg1 = REG_PR; /* Anything valid is good here. */
+ }
+ reg2 = e2.X_add_number - REG_GR;
+ if (e2.X_op != O_register || reg2 > 127)
+ {
+ as_bad (_("Second operand to .save not a valid register"));
+ reg2 = 0;
+ }
+ switch (reg1)
+ {
+ case REG_AR + AR_BSP:
+ add_unwind_entry (output_bsp_when (), sep);
+ add_unwind_entry (output_bsp_gr (reg2), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_BSPSTORE:
+ add_unwind_entry (output_bspstore_when (), sep);
+ add_unwind_entry (output_bspstore_gr (reg2), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_RNAT:
+ add_unwind_entry (output_rnat_when (), sep);
+ add_unwind_entry (output_rnat_gr (reg2), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_UNAT:
+ add_unwind_entry (output_unat_when (), sep);
+ add_unwind_entry (output_unat_gr (reg2), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_FPSR:
+ add_unwind_entry (output_fpsr_when (), sep);
+ add_unwind_entry (output_fpsr_gr (reg2), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_PFS:
+ add_unwind_entry (output_pfs_when (), sep);
+ if (! (unwind.prologue_mask & 4))
+ add_unwind_entry (output_pfs_gr (reg2), NOT_A_CHAR);
+ else if (reg2 != unwind.prologue_gr
+ + (unsigned) popcount (unwind.prologue_mask & (-4 << 1)))
+ as_warn (_("Second operand of .save contradicts .prologue"));
+ break;
+ case REG_AR + AR_LC:
+ add_unwind_entry (output_lc_when (), sep);
+ add_unwind_entry (output_lc_gr (reg2), NOT_A_CHAR);
+ break;
+ case REG_BR:
+ add_unwind_entry (output_rp_when (), sep);
+ if (! (unwind.prologue_mask & 8))
+ add_unwind_entry (output_rp_gr (reg2), NOT_A_CHAR);
+ else if (reg2 != unwind.prologue_gr)
+ as_warn (_("Second operand of .save contradicts .prologue"));
+ break;
+ case REG_PR:
+ add_unwind_entry (output_preds_when (), sep);
+ if (! (unwind.prologue_mask & 1))
+ add_unwind_entry (output_preds_gr (reg2), NOT_A_CHAR);
+ else if (reg2 != unwind.prologue_gr
+ + (unsigned) popcount (unwind.prologue_mask & (-1 << 1)))
+ as_warn (_("Second operand of .save contradicts .prologue"));
+ break;
+ case REG_PRIUNAT:
+ add_unwind_entry (output_priunat_when_gr (), sep);
+ add_unwind_entry (output_priunat_gr (reg2), NOT_A_CHAR);
+ break;
+ default:
+ as_bad (_("First operand to .save not a valid register"));
+ add_unwind_entry (NULL, sep);
+ break;
+ }
+}
+
+static void
+dot_restore (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e1;
+ unsigned long ecount; /* # of _additional_ regions to pop */
+ int sep;
+
+ if (!in_body ("restore"))
+ return;
+
+ sep = parse_operand_and_eval (&e1, ',');
+ if (e1.X_op != O_register || e1.X_add_number != REG_GR + 12)
+ as_bad (_("First operand to .restore must be stack pointer (sp)"));
+
+ if (sep == ',')
+ {
+ expressionS e2;
+
+ sep = parse_operand_and_eval (&e2, ',');
+ if (e2.X_op != O_constant || e2.X_add_number < 0)
+ {
+ as_bad (_("Second operand to .restore must be a constant >= 0"));
+ e2.X_add_number = 0;
+ }
+ ecount = e2.X_add_number;
+ }
+ else
+ ecount = unwind.prologue_count - 1;
+
+ if (ecount >= unwind.prologue_count)
+ {
+ as_bad (_("Epilogue count of %lu exceeds number of nested prologues (%u)"),
+ ecount + 1, unwind.prologue_count);
+ ecount = 0;
+ }
+
+ add_unwind_entry (output_epilogue (ecount), sep);
+
+ if (ecount < unwind.prologue_count)
+ unwind.prologue_count -= ecount + 1;
+ else
+ unwind.prologue_count = 0;
+}
+
+static void
+dot_restorereg (int pred)
+{
+ unsigned int qp, ab, reg;
+ expressionS e;
+ int sep;
+ const char * const po = pred ? "restorereg.p" : "restorereg";
+
+ if (!in_procedure (po))
+ return;
+
+ if (pred)
+ sep = parse_predicate_and_operand (&e, &qp, po);
+ else
+ {
+ sep = parse_operand_and_eval (&e, ',');
+ qp = 0;
+ }
+ convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
+
+ add_unwind_entry (output_spill_reg (ab, reg, 0, 0, qp), sep);
+}
+
+static char *special_linkonce_name[] =
+ {
+ ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi."
+ };
+
+static void
+start_unwind_section (const segT text_seg, int sec_index)
+{
+ /*
+ Use a slightly ugly scheme to derive the unwind section names from
+ the text section name:
+
+ text sect. unwind table sect.
+ name: name: comments:
+ ---------- ----------------- --------------------------------
+ .text .IA_64.unwind
+ .text.foo .IA_64.unwind.text.foo
+ .foo .IA_64.unwind.foo
+ .gnu.linkonce.t.foo
+ .gnu.linkonce.ia64unw.foo
+ _info .IA_64.unwind_info gas issues error message (ditto)
+ _infoFOO .IA_64.unwind_infoFOO gas issues error message (ditto)
+
+ This mapping is done so that:
+
+ (a) An object file with unwind info only in .text will use
+ unwind section names .IA_64.unwind and .IA_64.unwind_info.
+ This follows the letter of the ABI and also ensures backwards
+ compatibility with older toolchains.
+
+ (b) An object file with unwind info in multiple text sections
+ will use separate unwind sections for each text section.
+ This allows us to properly set the "sh_info" and "sh_link"
+ fields in SHT_IA_64_UNWIND as required by the ABI and also
+ lets GNU ld support programs with multiple segments
+ containing unwind info (as might be the case for certain
+ embedded applications).
+
+ (c) An error is issued if there would be a name clash.
+ */
+
+ const char *text_name, *sec_text_name;
+ char *sec_name;
+ const char *prefix = special_section_name [sec_index];
+ const char *suffix;
+ size_t prefix_len, suffix_len, sec_name_len;
+
+ sec_text_name = segment_name (text_seg);
+ text_name = sec_text_name;
+ if (strncmp (text_name, "_info", 5) == 0)
+ {
+ as_bad (_("Illegal section name `%s' (causes unwind section name clash)"),
+ text_name);
+ ignore_rest_of_line ();
+ return;
+ }
+ if (strcmp (text_name, ".text") == 0)
+ text_name = "";
+
+ /* Build the unwind section name by appending the (possibly stripped)
+ text section name to the unwind prefix. */
+ suffix = text_name;
+ if (strncmp (text_name, ".gnu.linkonce.t.",
+ sizeof (".gnu.linkonce.t.") - 1) == 0)
+ {
+ prefix = special_linkonce_name [sec_index - SPECIAL_SECTION_UNWIND];
+ suffix += sizeof (".gnu.linkonce.t.") - 1;
+ }
+
+ prefix_len = strlen (prefix);
+ suffix_len = strlen (suffix);
+ sec_name_len = prefix_len + suffix_len;
+ sec_name = alloca (sec_name_len + 1);
+ memcpy (sec_name, prefix, prefix_len);
+ memcpy (sec_name + prefix_len, suffix, suffix_len);
+ sec_name [sec_name_len] = '\0';
+
+ /* Handle COMDAT group. */
+ if ((text_seg->flags & SEC_LINK_ONCE) != 0
+ && (elf_section_flags (text_seg) & SHF_GROUP) != 0)
+ {
+ char *section;
+ size_t len, group_name_len;
+ const char *group_name = elf_group_name (text_seg);
+
+ if (group_name == NULL)
+ {
+ as_bad (_("Group section `%s' has no group signature"),
+ sec_text_name);
+ ignore_rest_of_line ();
+ return;
+ }
+ /* We have to construct a fake section directive. */
+ group_name_len = strlen (group_name);
+ len = (sec_name_len
+ + 16 /* ,"aG",@progbits, */
+ + group_name_len /* ,group_name */
+ + 7); /* ,comdat */
+
+ section = alloca (len + 1);
+ memcpy (section, sec_name, sec_name_len);
+ memcpy (section + sec_name_len, ",\"aG\",@progbits,", 16);
+ memcpy (section + sec_name_len + 16, group_name, group_name_len);
+ memcpy (section + len - 7, ",comdat", 7);
+ section [len] = '\0';
+ set_section (section);
+ }
+ else
+ {
+ set_section (sec_name);
+ bfd_set_section_flags (stdoutput, now_seg,
+ SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+ }
+
+ elf_linked_to_section (now_seg) = text_seg;
+}
+
+static void
+generate_unwind_image (const segT text_seg)
+{
+ int size, pad;
+ unw_rec_list *list;
+
+ /* Mark the end of the unwind info, so that we can compute the size of the
+ last unwind region. */
+ add_unwind_entry (output_endp (), NOT_A_CHAR);
+
+ /* Force out pending instructions, to make sure all unwind records have
+ a valid slot_number field. */
+ ia64_flush_insns ();
+
+ /* Generate the unwind record. */
+ list = optimize_unw_records (unwind.list);
+ fixup_unw_records (list, 1);
+ size = calc_record_size (list);
+
+ if (size > 0 || unwind.force_unwind_entry)
+ {
+ unwind.force_unwind_entry = 0;
+ /* pad to pointer-size boundary. */
+ pad = size % md.pointer_size;
+ if (pad != 0)
+ size += md.pointer_size - pad;
+ /* Add 8 for the header. */
+ size += 8;
+ /* Add a pointer for the personality offset. */
+ if (unwind.personality_routine)
+ size += md.pointer_size;
+ }
+
+ /* If there are unwind records, switch sections, and output the info. */
+ if (size != 0)
+ {
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ start_unwind_section (text_seg, SPECIAL_SECTION_UNWIND_INFO);
+
+ /* Make sure the section has 4 byte alignment for ILP32 and
+ 8 byte alignment for LP64. */
+ frag_align (md.pointer_size_shift, 0, 0);
+ record_alignment (now_seg, md.pointer_size_shift);
+
+ /* Set expression which points to start of unwind descriptor area. */
+ unwind.info = expr_build_dot ();
+
+ frag_var (rs_machine_dependent, size, size, 0, 0,
+ (offsetT) (long) unwind.personality_routine,
+ (char *) list);
+
+ /* Add the personality address to the image. */
+ if (unwind.personality_routine != 0)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = unwind.personality_routine;
+ exp.X_add_number = 0;
+
+ if (md.flags & EF_IA_64_BE)
+ {
+ if (md.flags & EF_IA_64_ABI64)
+ reloc = BFD_RELOC_IA64_LTOFF_FPTR64MSB;
+ else
+ reloc = BFD_RELOC_IA64_LTOFF_FPTR32MSB;
+ }
+ else
+ {
+ if (md.flags & EF_IA_64_ABI64)
+ reloc = BFD_RELOC_IA64_LTOFF_FPTR64LSB;
+ else
+ reloc = BFD_RELOC_IA64_LTOFF_FPTR32LSB;
+ }
+
+ fix_new_exp (frag_now, frag_now_fix () - md.pointer_size,
+ md.pointer_size, &exp, 0, reloc);
+ unwind.personality_routine = 0;
+ }
+ }
+
+ free_saved_prologue_counts ();
+ unwind.list = unwind.tail = unwind.current_entry = NULL;
+}
+
+static void
+dot_handlerdata (int dummy ATTRIBUTE_UNUSED)
+{
+ if (!in_procedure ("handlerdata"))
+ return;
+ unwind.force_unwind_entry = 1;
+
+ /* Remember which segment we're in so we can switch back after .endp */
+ unwind.saved_text_seg = now_seg;
+ unwind.saved_text_subseg = now_subseg;
+
+ /* Generate unwind info into unwind-info section and then leave that
+ section as the currently active one so dataXX directives go into
+ the language specific data area of the unwind info block. */
+ generate_unwind_image (now_seg);
+ demand_empty_rest_of_line ();
+}
+
+static void
+dot_unwentry (int dummy ATTRIBUTE_UNUSED)
+{
+ if (!in_procedure ("unwentry"))
+ return;
+ unwind.force_unwind_entry = 1;
+ demand_empty_rest_of_line ();
+}
+
+static void
+dot_altrp (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ unsigned reg;
+
+ if (!in_prologue ("altrp"))
+ return;
+
+ parse_operand_and_eval (&e, 0);
+ reg = e.X_add_number - REG_BR;
+ if (e.X_op != O_register || reg > 7)
+ {
+ as_bad (_("First operand to .altrp not a valid branch register"));
+ reg = 0;
+ }
+ add_unwind_entry (output_rp_br (reg), 0);
+}
+
+static void
+dot_savemem (int psprel)
+{
+ expressionS e1, e2;
+ int sep;
+ int reg1, val;
+ const char * const po = psprel ? "savepsp" : "savesp";
+
+ if (!in_prologue (po))
+ return;
+
+ sep = parse_operand_and_eval (&e1, ',');
+ if (sep == ',')
+ sep = parse_operand_and_eval (&e2, ',');
+ else
+ e2.X_op = O_absent;
+
+ reg1 = e1.X_add_number;
+ val = e2.X_add_number;
+
+ /* Make sure its a valid ar.xxx reg, OR its br0, aka 'rp'. */
+ if (e1.X_op != O_register)
+ {
+ as_bad (_("First operand to .%s not a register"), po);
+ reg1 = REG_PR; /* Anything valid is good here. */
+ }
+ if (e2.X_op != O_constant)
+ {
+ as_bad (_("Second operand to .%s not a constant"), po);
+ val = 0;
+ }
+
+ switch (reg1)
+ {
+ case REG_AR + AR_BSP:
+ add_unwind_entry (output_bsp_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_bsp_psprel
+ : output_bsp_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_BSPSTORE:
+ add_unwind_entry (output_bspstore_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_bspstore_psprel
+ : output_bspstore_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_RNAT:
+ add_unwind_entry (output_rnat_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_rnat_psprel
+ : output_rnat_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_UNAT:
+ add_unwind_entry (output_unat_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_unat_psprel
+ : output_unat_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_FPSR:
+ add_unwind_entry (output_fpsr_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_fpsr_psprel
+ : output_fpsr_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_PFS:
+ add_unwind_entry (output_pfs_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_pfs_psprel
+ : output_pfs_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_AR + AR_LC:
+ add_unwind_entry (output_lc_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_lc_psprel
+ : output_lc_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_BR:
+ add_unwind_entry (output_rp_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_rp_psprel
+ : output_rp_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_PR:
+ add_unwind_entry (output_preds_when (), sep);
+ add_unwind_entry ((psprel
+ ? output_preds_psprel
+ : output_preds_sprel) (val), NOT_A_CHAR);
+ break;
+ case REG_PRIUNAT:
+ add_unwind_entry (output_priunat_when_mem (), sep);
+ add_unwind_entry ((psprel
+ ? output_priunat_psprel
+ : output_priunat_sprel) (val), NOT_A_CHAR);
+ break;
+ default:
+ as_bad (_("First operand to .%s not a valid register"), po);
+ add_unwind_entry (NULL, sep);
+ break;
+ }
+}
+
+static void
+dot_saveg (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ unsigned grmask;
+ int sep;
+
+ if (!in_prologue ("save.g"))
+ return;
+
+ sep = parse_operand_and_eval (&e, ',');
+
+ grmask = e.X_add_number;
+ if (e.X_op != O_constant
+ || e.X_add_number <= 0
+ || e.X_add_number > 0xf)
+ {
+ as_bad (_("First operand to .save.g must be a positive 4-bit constant"));
+ grmask = 0;
+ }
+
+ if (sep == ',')
+ {
+ unsigned reg;
+ int n = popcount (grmask);
+
+ parse_operand_and_eval (&e, 0);
+ reg = e.X_add_number - REG_GR;
+ if (e.X_op != O_register || reg > 127)
+ {
+ as_bad (_("Second operand to .save.g must be a general register"));
+ reg = 0;
+ }
+ else if (reg > 128U - n)
+ {
+ as_bad (_("Second operand to .save.g must be the first of %d general registers"), n);
+ reg = 0;
+ }
+ add_unwind_entry (output_gr_gr (grmask, reg), 0);
+ }
+ else
+ add_unwind_entry (output_gr_mem (grmask), 0);
+}
+
+static void
+dot_savef (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+
+ if (!in_prologue ("save.f"))
+ return;
+
+ parse_operand_and_eval (&e, 0);
+
+ if (e.X_op != O_constant
+ || e.X_add_number <= 0
+ || e.X_add_number > 0xfffff)
+ {
+ as_bad (_("Operand to .save.f must be a positive 20-bit constant"));
+ e.X_add_number = 0;
+ }
+ add_unwind_entry (output_fr_mem (e.X_add_number), 0);
+}
+
+static void
+dot_saveb (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ unsigned brmask;
+ int sep;
+
+ if (!in_prologue ("save.b"))
+ return;
+
+ sep = parse_operand_and_eval (&e, ',');
+
+ brmask = e.X_add_number;
+ if (e.X_op != O_constant
+ || e.X_add_number <= 0
+ || e.X_add_number > 0x1f)
+ {
+ as_bad (_("First operand to .save.b must be a positive 5-bit constant"));
+ brmask = 0;
+ }
+
+ if (sep == ',')
+ {
+ unsigned reg;
+ int n = popcount (brmask);
+
+ parse_operand_and_eval (&e, 0);
+ reg = e.X_add_number - REG_GR;
+ if (e.X_op != O_register || reg > 127)
+ {
+ as_bad (_("Second operand to .save.b must be a general register"));
+ reg = 0;
+ }
+ else if (reg > 128U - n)
+ {
+ as_bad (_("Second operand to .save.b must be the first of %d general registers"), n);
+ reg = 0;
+ }
+ add_unwind_entry (output_br_gr (brmask, reg), 0);
+ }
+ else
+ add_unwind_entry (output_br_mem (brmask), 0);
+}
+
+static void
+dot_savegf (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e1, e2;
+
+ if (!in_prologue ("save.gf"))
+ return;
+
+ if (parse_operand_and_eval (&e1, ',') == ',')
+ parse_operand_and_eval (&e2, 0);
+ else
+ e2.X_op = O_absent;
+
+ if (e1.X_op != O_constant
+ || e1.X_add_number < 0
+ || e1.X_add_number > 0xf)
+ {
+ as_bad (_("First operand to .save.gf must be a non-negative 4-bit constant"));
+ e1.X_op = O_absent;
+ e1.X_add_number = 0;
+ }
+ if (e2.X_op != O_constant
+ || e2.X_add_number < 0
+ || e2.X_add_number > 0xfffff)
+ {
+ as_bad (_("Second operand to .save.gf must be a non-negative 20-bit constant"));
+ e2.X_op = O_absent;
+ e2.X_add_number = 0;
+ }
+ if (e1.X_op == O_constant
+ && e2.X_op == O_constant
+ && e1.X_add_number == 0
+ && e2.X_add_number == 0)
+ as_bad (_("Operands to .save.gf may not be both zero"));
+
+ add_unwind_entry (output_frgr_mem (e1.X_add_number, e2.X_add_number), 0);
+}
+
+static void
+dot_spill (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+
+ if (!in_prologue ("spill"))
+ return;
+
+ parse_operand_and_eval (&e, 0);
+
+ if (e.X_op != O_constant)
+ {
+ as_bad (_("Operand to .spill must be a constant"));
+ e.X_add_number = 0;
+ }
+ add_unwind_entry (output_spill_base (e.X_add_number), 0);
+}
+
+static void
+dot_spillreg (int pred)
+{
+ int sep;
+ unsigned int qp, ab, xy, reg, treg;
+ expressionS e;
+ const char * const po = pred ? "spillreg.p" : "spillreg";
+
+ if (!in_procedure (po))
+ return;
+
+ if (pred)
+ sep = parse_predicate_and_operand (&e, &qp, po);
+ else
+ {
+ sep = parse_operand_and_eval (&e, ',');
+ qp = 0;
+ }
+ convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
+
+ if (sep == ',')
+ sep = parse_operand_and_eval (&e, ',');
+ else
+ e.X_op = O_absent;
+ convert_expr_to_xy_reg (&e, &xy, &treg, po, 2 + pred);
+
+ add_unwind_entry (output_spill_reg (ab, reg, treg, xy, qp), sep);
+}
+
+static void
+dot_spillmem (int psprel)
+{
+ expressionS e;
+ int pred = (psprel < 0), sep;
+ unsigned int qp, ab, reg;
+ const char * po;
+
+ if (pred)
+ {
+ psprel = ~psprel;
+ po = psprel ? "spillpsp.p" : "spillsp.p";
+ }
+ else
+ po = psprel ? "spillpsp" : "spillsp";
+
+ if (!in_procedure (po))
+ return;
+
+ if (pred)
+ sep = parse_predicate_and_operand (&e, &qp, po);
+ else
+ {
+ sep = parse_operand_and_eval (&e, ',');
+ qp = 0;
+ }
+ convert_expr_to_ab_reg (&e, &ab, &reg, po, 1 + pred);
+
+ if (sep == ',')
+ sep = parse_operand_and_eval (&e, ',');
+ else
+ e.X_op = O_absent;
+ if (e.X_op != O_constant)
+ {
+ as_bad (_("Operand %d to .%s must be a constant"), 2 + pred, po);
+ e.X_add_number = 0;
+ }
+
+ if (psprel)
+ add_unwind_entry (output_spill_psprel (ab, reg, e.X_add_number, qp), sep);
+ else
+ add_unwind_entry (output_spill_sprel (ab, reg, e.X_add_number, qp), sep);
+}
+
+static unsigned int
+get_saved_prologue_count (unsigned long lbl)
+{
+ label_prologue_count *lpc = unwind.saved_prologue_counts;
+
+ while (lpc != NULL && lpc->label_number != lbl)
+ lpc = lpc->next;
+
+ if (lpc != NULL)
+ return lpc->prologue_count;
+
+ as_bad (_("Missing .label_state %ld"), lbl);
+ return 1;
+}
+
+static void
+save_prologue_count (unsigned long lbl, unsigned int count)
+{
+ label_prologue_count *lpc = unwind.saved_prologue_counts;
+
+ while (lpc != NULL && lpc->label_number != lbl)
+ lpc = lpc->next;
+
+ if (lpc != NULL)
+ lpc->prologue_count = count;
+ else
+ {
+ label_prologue_count *new_lpc = xmalloc (sizeof (* new_lpc));
+
+ new_lpc->next = unwind.saved_prologue_counts;
+ new_lpc->label_number = lbl;
+ new_lpc->prologue_count = count;
+ unwind.saved_prologue_counts = new_lpc;
+ }
+}
+
+static void
+free_saved_prologue_counts ()
+{
+ label_prologue_count *lpc = unwind.saved_prologue_counts;
+ label_prologue_count *next;
+
+ while (lpc != NULL)
+ {
+ next = lpc->next;
+ free (lpc);
+ lpc = next;
+ }
+
+ unwind.saved_prologue_counts = NULL;
+}
+
+static void
+dot_label_state (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+
+ if (!in_body ("label_state"))
+ return;
+
+ parse_operand_and_eval (&e, 0);
+ if (e.X_op == O_constant)
+ save_prologue_count (e.X_add_number, unwind.prologue_count);
+ else
+ {
+ as_bad (_("Operand to .label_state must be a constant"));
+ e.X_add_number = 0;
+ }
+ add_unwind_entry (output_label_state (e.X_add_number), 0);
+}
+
+static void
+dot_copy_state (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+
+ if (!in_body ("copy_state"))
+ return;
+
+ parse_operand_and_eval (&e, 0);
+ if (e.X_op == O_constant)
+ unwind.prologue_count = get_saved_prologue_count (e.X_add_number);
+ else
+ {
+ as_bad (_("Operand to .copy_state must be a constant"));
+ e.X_add_number = 0;
+ }
+ add_unwind_entry (output_copy_state (e.X_add_number), 0);
+}
+
+static void
+dot_unwabi (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e1, e2;
+ unsigned char sep;
+
+ if (!in_prologue ("unwabi"))
+ return;
+
+ sep = parse_operand_and_eval (&e1, ',');
+ if (sep == ',')
+ parse_operand_and_eval (&e2, 0);
+ else
+ e2.X_op = O_absent;
+
+ if (e1.X_op != O_constant)
+ {
+ as_bad (_("First operand to .unwabi must be a constant"));
+ e1.X_add_number = 0;
+ }
+
+ if (e2.X_op != O_constant)
+ {
+ as_bad (_("Second operand to .unwabi must be a constant"));
+ e2.X_add_number = 0;
+ }
+
+ add_unwind_entry (output_unwabi (e1.X_add_number, e2.X_add_number), 0);
+}
+
+static void
+dot_personality (int dummy ATTRIBUTE_UNUSED)
+{
+ char *name, *p, c;
+ if (!in_procedure ("personality"))
+ return;
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ unwind.personality_routine = symbol_find_or_make (name);
+ unwind.force_unwind_entry = 1;
+ *p = c;
+ SKIP_WHITESPACE ();
+ demand_empty_rest_of_line ();
+}
+
+static void
+dot_proc (int dummy ATTRIBUTE_UNUSED)
+{
+ char *name, *p, c;
+ symbolS *sym;
+ proc_pending *pending, *last_pending;
+
+ if (unwind.proc_pending.sym)
+ {
+ (md.unwind_check == unwind_check_warning
+ ? as_warn
+ : as_bad) (_("Missing .endp after previous .proc"));
+ while (unwind.proc_pending.next)
+ {
+ pending = unwind.proc_pending.next;
+ unwind.proc_pending.next = pending->next;
+ free (pending);
+ }
+ }
+ last_pending = NULL;
+
+ /* Parse names of main and alternate entry points and mark them as
+ function symbols: */
+ while (1)
+ {
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ if (!*name)
+ as_bad (_("Empty argument of .proc"));
+ else
+ {
+ sym = symbol_find_or_make (name);
+ if (S_IS_DEFINED (sym))
+ as_bad (_("`%s' was already defined"), name);
+ else if (!last_pending)
+ {
+ unwind.proc_pending.sym = sym;
+ last_pending = &unwind.proc_pending;
+ }
+ else
+ {
+ pending = xmalloc (sizeof (*pending));
+ pending->sym = sym;
+ last_pending = last_pending->next = pending;
+ }
+ symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION;
+ }
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ break;
+ ++input_line_pointer;
+ }
+ if (!last_pending)
+ {
+ unwind.proc_pending.sym = expr_build_dot ();
+ last_pending = &unwind.proc_pending;
+ }
+ last_pending->next = NULL;
+ demand_empty_rest_of_line ();
+ ia64_do_align (16);
+
+ unwind.prologue = 0;
+ unwind.prologue_count = 0;
+ unwind.body = 0;
+ unwind.insn = 0;
+ unwind.list = unwind.tail = unwind.current_entry = NULL;
+ unwind.personality_routine = 0;
+}
+
+static void
+dot_body (int dummy ATTRIBUTE_UNUSED)
+{
+ if (!in_procedure ("body"))
+ return;
+ if (!unwind.prologue && !unwind.body && unwind.insn)
+ as_warn (_("Initial .body should precede any instructions"));
+ check_pending_save ();
+
+ unwind.prologue = 0;
+ unwind.prologue_mask = 0;
+ unwind.body = 1;
+
+ add_unwind_entry (output_body (), 0);
+}
+
+static void
+dot_prologue (int dummy ATTRIBUTE_UNUSED)
+{
+ unsigned mask = 0, grsave = 0;
+
+ if (!in_procedure ("prologue"))
+ return;
+ if (unwind.prologue)
+ {
+ as_bad (_(".prologue within prologue"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (!unwind.body && unwind.insn)
+ as_warn (_("Initial .prologue should precede any instructions"));
+
+ if (!is_it_end_of_statement ())
+ {
+ expressionS e;
+ int n, sep = parse_operand_and_eval (&e, ',');
+
+ if (e.X_op != O_constant
+ || e.X_add_number < 0
+ || e.X_add_number > 0xf)
+ as_bad (_("First operand to .prologue must be a positive 4-bit constant"));
+ else if (e.X_add_number == 0)
+ as_warn (_("Pointless use of zero first operand to .prologue"));
+ else
+ mask = e.X_add_number;
+ n = popcount (mask);
+
+ if (sep == ',')
+ parse_operand_and_eval (&e, 0);
+ else
+ e.X_op = O_absent;
+ if (e.X_op == O_constant
+ && e.X_add_number >= 0
+ && e.X_add_number < 128)
+ {
+ if (md.unwind_check == unwind_check_error)
+ as_warn (_("Using a constant as second operand to .prologue is deprecated"));
+ grsave = e.X_add_number;
+ }
+ else if (e.X_op != O_register
+ || (grsave = e.X_add_number - REG_GR) > 127)
+ {
+ as_bad (_("Second operand to .prologue must be a general register"));
+ grsave = 0;
+ }
+ else if (grsave > 128U - n)
+ {
+ as_bad (_("Second operand to .prologue must be the first of %d general registers"), n);
+ grsave = 0;
+ }
+
+ }
+
+ if (mask)
+ add_unwind_entry (output_prologue_gr (mask, grsave), 0);
+ else
+ add_unwind_entry (output_prologue (), 0);
+
+ unwind.prologue = 1;
+ unwind.prologue_mask = mask;
+ unwind.prologue_gr = grsave;
+ unwind.body = 0;
+ ++unwind.prologue_count;
+}
+
+static void
+dot_endp (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS e;
+ int bytes_per_address;
+ long where;
+ segT saved_seg;
+ subsegT saved_subseg;
+ proc_pending *pending;
+ int unwind_check = md.unwind_check;
+
+ md.unwind_check = unwind_check_error;
+ if (!in_procedure ("endp"))
+ return;
+ md.unwind_check = unwind_check;
+
+ if (unwind.saved_text_seg)
+ {
+ saved_seg = unwind.saved_text_seg;
+ saved_subseg = unwind.saved_text_subseg;
+ unwind.saved_text_seg = NULL;
+ }
+ else
+ {
+ saved_seg = now_seg;
+ saved_subseg = now_subseg;
+ }
+
+ insn_group_break (1, 0, 0);
+
+ /* If there wasn't a .handlerdata, we haven't generated an image yet. */
+ if (!unwind.info)
+ generate_unwind_image (saved_seg);
+
+ if (unwind.info || unwind.force_unwind_entry)
+ {
+ symbolS *proc_end;
+
+ subseg_set (md.last_text_seg, 0);
+ proc_end = expr_build_dot ();
+
+ start_unwind_section (saved_seg, SPECIAL_SECTION_UNWIND);
+
+ /* Make sure that section has 4 byte alignment for ILP32 and
+ 8 byte alignment for LP64. */
+ record_alignment (now_seg, md.pointer_size_shift);
+
+ /* Need space for 3 pointers for procedure start, procedure end,
+ and unwind info. */
+ memset (frag_more (3 * md.pointer_size), 0, 3 * md.pointer_size);
+ where = frag_now_fix () - (3 * md.pointer_size);
+ bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8;
+
+ /* Issue the values of a) Proc Begin, b) Proc End, c) Unwind Record. */
+ e.X_op = O_pseudo_fixup;
+ e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+ e.X_add_number = 0;
+ if (!S_IS_LOCAL (unwind.proc_pending.sym)
+ && S_IS_DEFINED (unwind.proc_pending.sym))
+ e.X_add_symbol = symbol_temp_new (S_GET_SEGMENT (unwind.proc_pending.sym),
+ S_GET_VALUE (unwind.proc_pending.sym),
+ symbol_get_frag (unwind.proc_pending.sym));
+ else
+ e.X_add_symbol = unwind.proc_pending.sym;
+ ia64_cons_fix_new (frag_now, where, bytes_per_address, &e,
+ BFD_RELOC_NONE);
+
+ e.X_op = O_pseudo_fixup;
+ e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+ e.X_add_number = 0;
+ e.X_add_symbol = proc_end;
+ ia64_cons_fix_new (frag_now, where + bytes_per_address,
+ bytes_per_address, &e, BFD_RELOC_NONE);
+
+ if (unwind.info)
+ {
+ e.X_op = O_pseudo_fixup;
+ e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym;
+ e.X_add_number = 0;
+ e.X_add_symbol = unwind.info;
+ ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2),
+ bytes_per_address, &e, BFD_RELOC_NONE);
+ }
+ }
+ subseg_set (saved_seg, saved_subseg);
+
+ /* Set symbol sizes. */
+ pending = &unwind.proc_pending;
+ if (S_GET_NAME (pending->sym))
+ {
+ do
+ {
+ symbolS *sym = pending->sym;
+
+ if (!S_IS_DEFINED (sym))
+ as_bad (_("`%s' was not defined within procedure"), S_GET_NAME (sym));
+ else if (S_GET_SIZE (sym) == 0
+ && symbol_get_obj (sym)->size == NULL)
+ {
+ fragS *frag = symbol_get_frag (sym);
+
+ if (frag)
+ {
+ if (frag == frag_now && SEG_NORMAL (now_seg))
+ S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym));
+ else
+ {
+ symbol_get_obj (sym)->size =
+ (expressionS *) xmalloc (sizeof (expressionS));
+ symbol_get_obj (sym)->size->X_op = O_subtract;
+ symbol_get_obj (sym)->size->X_add_symbol
+ = symbol_new (FAKE_LABEL_NAME, now_seg,
+ frag_now_fix (), frag_now);
+ symbol_get_obj (sym)->size->X_op_symbol = sym;
+ symbol_get_obj (sym)->size->X_add_number = 0;
+ }
+ }
+ }
+ } while ((pending = pending->next) != NULL);
+ }
+
+ /* Parse names of main and alternate entry points. */
+ while (1)
+ {
+ char *name, *p, c;
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ if (!*name)
+ (md.unwind_check == unwind_check_warning
+ ? as_warn
+ : as_bad) (_("Empty argument of .endp"));
+ else
+ {
+ symbolS *sym = symbol_find (name);
+
+ for (pending = &unwind.proc_pending; pending; pending = pending->next)
+ {
+ if (sym == pending->sym)
+ {
+ pending->sym = NULL;
+ break;
+ }
+ }
+ if (!sym || !pending)
+ as_warn (_("`%s' was not specified with previous .proc"), name);
+ }
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ break;
+ ++input_line_pointer;
+ }
+ demand_empty_rest_of_line ();
+
+ /* Deliberately only checking for the main entry point here; the
+ language spec even says all arguments to .endp are ignored. */
+ if (unwind.proc_pending.sym
+ && S_GET_NAME (unwind.proc_pending.sym)
+ && strcmp (S_GET_NAME (unwind.proc_pending.sym), FAKE_LABEL_NAME))
+ as_warn (_("`%s' should be an operand to this .endp"),
+ S_GET_NAME (unwind.proc_pending.sym));
+ while (unwind.proc_pending.next)
+ {
+ pending = unwind.proc_pending.next;
+ unwind.proc_pending.next = pending->next;
+ free (pending);
+ }
+ unwind.proc_pending.sym = unwind.info = NULL;
+}
+
+static void
+dot_template (int template_val)
+{
+ CURR_SLOT.user_template = template_val;
+}
+
+static void
+dot_regstk (int dummy ATTRIBUTE_UNUSED)
+{
+ int ins, locs, outs, rots;
+
+ if (is_it_end_of_statement ())
+ ins = locs = outs = rots = 0;
+ else
+ {
+ ins = get_absolute_expression ();
+ if (*input_line_pointer++ != ',')
+ goto err;
+ locs = get_absolute_expression ();
+ if (*input_line_pointer++ != ',')
+ goto err;
+ outs = get_absolute_expression ();
+ if (*input_line_pointer++ != ',')
+ goto err;
+ rots = get_absolute_expression ();
+ }
+ set_regstack (ins, locs, outs, rots);
+ return;
+
+ err:
+ as_bad (_("Comma expected"));
+ ignore_rest_of_line ();
+}
+
+static void
+dot_rot (int type)
+{
+ offsetT num_regs;
+ valueT num_alloced = 0;
+ struct dynreg **drpp, *dr;
+ int ch, base_reg = 0;
+ char *name, *start;
+ size_t len;
+
+ switch (type)
+ {
+ case DYNREG_GR: base_reg = REG_GR + 32; break;
+ case DYNREG_FR: base_reg = REG_FR + 32; break;
+ case DYNREG_PR: base_reg = REG_P + 16; break;
+ default: break;
+ }
+
+ /* First, remove existing names from hash table. */
+ for (dr = md.dynreg[type]; dr && dr->num_regs; dr = dr->next)
+ {
+ hash_delete (md.dynreg_hash, dr->name, FALSE);
+ /* FIXME: Free dr->name. */
+ dr->num_regs = 0;
+ }
+
+ drpp = &md.dynreg[type];
+ while (1)
+ {
+ start = input_line_pointer;
+ ch = get_symbol_end ();
+ len = strlen (ia64_canonicalize_symbol_name (start));
+ *input_line_pointer = ch;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '[')
+ {
+ as_bad (_("Expected '['"));
+ goto err;
+ }
+ ++input_line_pointer; /* skip '[' */
+
+ num_regs = get_absolute_expression ();
+
+ if (*input_line_pointer++ != ']')
+ {
+ as_bad (_("Expected ']'"));
+ goto err;
+ }
+ if (num_regs <= 0)
+ {
+ as_bad (_("Number of elements must be positive"));
+ goto err;
+ }
+ SKIP_WHITESPACE ();
+
+ num_alloced += num_regs;
+ switch (type)
+ {
+ case DYNREG_GR:
+ if (num_alloced > md.rot.num_regs)
+ {
+ as_bad (_("Used more than the declared %d rotating registers"),
+ md.rot.num_regs);
+ goto err;
+ }
+ break;
+ case DYNREG_FR:
+ if (num_alloced > 96)
+ {
+ as_bad (_("Used more than the available 96 rotating registers"));
+ goto err;
+ }
+ break;
+ case DYNREG_PR:
+ if (num_alloced > 48)
+ {
+ as_bad (_("Used more than the available 48 rotating registers"));
+ goto err;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!*drpp)
+ {
+ *drpp = obstack_alloc (&notes, sizeof (*dr));
+ memset (*drpp, 0, sizeof (*dr));
+ }
+
+ name = obstack_alloc (&notes, len + 1);
+ memcpy (name, start, len);
+ name[len] = '\0';
+
+ dr = *drpp;
+ dr->name = name;
+ dr->num_regs = num_regs;
+ dr->base = base_reg;
+ drpp = &dr->next;
+ base_reg += num_regs;
+
+ if (hash_insert (md.dynreg_hash, name, dr))
+ {
+ as_bad (_("Attempt to redefine register set `%s'"), name);
+ obstack_free (&notes, name);
+ goto err;
+ }
+
+ if (*input_line_pointer != ',')
+ break;
+ ++input_line_pointer; /* skip comma */
+ SKIP_WHITESPACE ();
+ }
+ demand_empty_rest_of_line ();
+ return;
+
+ err:
+ ignore_rest_of_line ();
+}
+
+static void
+dot_byteorder (int byteorder)
+{
+ segment_info_type *seginfo = seg_info (now_seg);
+
+ if (byteorder == -1)
+ {
+ if (seginfo->tc_segment_info_data.endian == 0)
+ seginfo->tc_segment_info_data.endian = default_big_endian ? 1 : 2;
+ byteorder = seginfo->tc_segment_info_data.endian == 1;
+ }
+ else
+ seginfo->tc_segment_info_data.endian = byteorder ? 1 : 2;
+
+ if (target_big_endian != byteorder)
+ {
+ target_big_endian = byteorder;
+ if (target_big_endian)
+ {
+ ia64_number_to_chars = number_to_chars_bigendian;
+ ia64_float_to_chars = ia64_float_to_chars_bigendian;
+ }
+ else
+ {
+ ia64_number_to_chars = number_to_chars_littleendian;
+ ia64_float_to_chars = ia64_float_to_chars_littleendian;
+ }
+ }
+}
+
+static void
+dot_psr (int dummy ATTRIBUTE_UNUSED)
+{
+ char *option;
+ int ch;
+
+ while (1)
+ {
+ option = input_line_pointer;
+ ch = get_symbol_end ();
+ if (strcmp (option, "lsb") == 0)
+ md.flags &= ~EF_IA_64_BE;
+ else if (strcmp (option, "msb") == 0)
+ md.flags |= EF_IA_64_BE;
+ else if (strcmp (option, "abi32") == 0)
+ md.flags &= ~EF_IA_64_ABI64;
+ else if (strcmp (option, "abi64") == 0)
+ md.flags |= EF_IA_64_ABI64;
+ else
+ as_bad (_("Unknown psr option `%s'"), option);
+ *input_line_pointer = ch;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ break;
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+dot_ln (int dummy ATTRIBUTE_UNUSED)
+{
+ new_logical_line (0, get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+cross_section (int ref, void (*builder) (int), int ua)
+{
+ char *start, *end;
+ int saved_auto_align;
+ unsigned int section_count;
+
+ SKIP_WHITESPACE ();
+ start = input_line_pointer;
+ if (*start == '"')
+ {
+ int len;
+ char *name;
+
+ name = demand_copy_C_string (&len);
+ obstack_free(&notes, name);
+ if (!name)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ {
+ char c = get_symbol_end ();
+
+ if (input_line_pointer == start)
+ {
+ as_bad (_("Missing section name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+ }
+ end = input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Comma expected after section name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ *end = '\0';
+ end = input_line_pointer + 1; /* skip comma */
+ input_line_pointer = start;
+ md.keep_pending_output = 1;
+ section_count = bfd_count_sections (stdoutput);
+ obj_elf_section (0);
+ if (section_count != bfd_count_sections (stdoutput))
+ as_warn (_("Creating sections with .xdataN/.xrealN/.xstringZ is deprecated."));
+ input_line_pointer = end;
+ saved_auto_align = md.auto_align;
+ if (ua)
+ md.auto_align = 0;
+ (*builder) (ref);
+ if (ua)
+ md.auto_align = saved_auto_align;
+ obj_elf_previous (0);
+ md.keep_pending_output = 0;
+}
+
+static void
+dot_xdata (int size)
+{
+ cross_section (size, cons, 0);
+}
+
+/* Why doesn't float_cons() call md_cons_align() the way cons() does? */
+
+static void
+stmt_float_cons (int kind)
+{
+ size_t alignment;
+
+ switch (kind)
+ {
+ case 'd':
+ alignment = 8;
+ break;
+
+ case 'x':
+ case 'X':
+ alignment = 16;
+ break;
+
+ case 'f':
+ default:
+ alignment = 4;
+ break;
+ }
+ ia64_do_align (alignment);
+ float_cons (kind);
+}
+
+static void
+stmt_cons_ua (int size)
+{
+ int saved_auto_align = md.auto_align;
+
+ md.auto_align = 0;
+ cons (size);
+ md.auto_align = saved_auto_align;
+}
+
+static void
+dot_xfloat_cons (int kind)
+{
+ cross_section (kind, stmt_float_cons, 0);
+}
+
+static void
+dot_xstringer (int zero)
+{
+ cross_section (zero, stringer, 0);
+}
+
+static void
+dot_xdata_ua (int size)
+{
+ cross_section (size, cons, 1);
+}
+
+static void
+dot_xfloat_cons_ua (int kind)
+{
+ cross_section (kind, float_cons, 1);
+}
+
+/* .reg.val <regname>,value */
+
+static void
+dot_reg_val (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS reg;
+
+ expression_and_evaluate (&reg);
+ if (reg.X_op != O_register)
+ {
+ as_bad (_("Register name expected"));
+ ignore_rest_of_line ();
+ }
+ else if (*input_line_pointer++ != ',')
+ {
+ as_bad (_("Comma expected"));
+ ignore_rest_of_line ();
+ }
+ else
+ {
+ valueT value = get_absolute_expression ();
+ int regno = reg.X_add_number;
+ if (regno <= REG_GR || regno > REG_GR + 127)
+ as_warn (_("Register value annotation ignored"));
+ else
+ {
+ gr_values[regno - REG_GR].known = 1;
+ gr_values[regno - REG_GR].value = value;
+ gr_values[regno - REG_GR].path = md.path;
+ }
+ }
+ demand_empty_rest_of_line ();
+}
+
+/*
+ .serialize.data
+ .serialize.instruction
+ */
+static void
+dot_serialize (int type)
+{
+ insn_group_break (0, 0, 0);
+ if (type)
+ instruction_serialization ();
+ else
+ data_serialization ();
+ insn_group_break (0, 0, 0);
+ demand_empty_rest_of_line ();
+}
+
+/* select dv checking mode
+ .auto
+ .explicit
+ .default
+
+ A stop is inserted when changing modes
+ */
+
+static void
+dot_dv_mode (int type)
+{
+ if (md.manual_bundling)
+ as_warn (_("Directive invalid within a bundle"));
+
+ if (type == 'E' || type == 'A')
+ md.mode_explicitly_set = 0;
+ else
+ md.mode_explicitly_set = 1;
+
+ md.detect_dv = 1;
+ switch (type)
+ {
+ case 'A':
+ case 'a':
+ if (md.explicit_mode)
+ insn_group_break (1, 0, 0);
+ md.explicit_mode = 0;
+ break;
+ case 'E':
+ case 'e':
+ if (!md.explicit_mode)
+ insn_group_break (1, 0, 0);
+ md.explicit_mode = 1;
+ break;
+ default:
+ case 'd':
+ if (md.explicit_mode != md.default_explicit_mode)
+ insn_group_break (1, 0, 0);
+ md.explicit_mode = md.default_explicit_mode;
+ md.mode_explicitly_set = 0;
+ break;
+ }
+}
+
+static void
+print_prmask (valueT mask)
+{
+ int regno;
+ char *comma = "";
+ for (regno = 0; regno < 64; regno++)
+ {
+ if (mask & ((valueT) 1 << regno))
+ {
+ fprintf (stderr, "%s p%d", comma, regno);
+ comma = ",";
+ }
+ }
+}
+
+/*
+ .pred.rel.clear [p1 [,p2 [,...]]] (also .pred.rel "clear" or @clear)
+ .pred.rel.imply p1, p2 (also .pred.rel "imply" or @imply)
+ .pred.rel.mutex p1, p2 [,...] (also .pred.rel "mutex" or @mutex)
+ .pred.safe_across_calls p1 [, p2 [,...]]
+ */
+
+static void
+dot_pred_rel (int type)
+{
+ valueT mask = 0;
+ int count = 0;
+ int p1 = -1, p2 = -1;
+
+ if (type == 0)
+ {
+ if (*input_line_pointer == '"')
+ {
+ int len;
+ char *form = demand_copy_C_string (&len);
+
+ if (strcmp (form, "mutex") == 0)
+ type = 'm';
+ else if (strcmp (form, "clear") == 0)
+ type = 'c';
+ else if (strcmp (form, "imply") == 0)
+ type = 'i';
+ obstack_free (&notes, form);
+ }
+ else if (*input_line_pointer == '@')
+ {
+ char *form = ++input_line_pointer;
+ char c = get_symbol_end();
+
+ if (strcmp (form, "mutex") == 0)
+ type = 'm';
+ else if (strcmp (form, "clear") == 0)
+ type = 'c';
+ else if (strcmp (form, "imply") == 0)
+ type = 'i';
+ *input_line_pointer = c;
+ }
+ else
+ {
+ as_bad (_("Missing predicate relation type"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (type == 0)
+ {
+ as_bad (_("Unrecognized predicate relation type"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ while (1)
+ {
+ valueT bits = 1;
+ int sep, regno;
+ expressionS pr, *pr1, *pr2;
+
+ sep = parse_operand_and_eval (&pr, ',');
+ if (pr.X_op == O_register
+ && pr.X_add_number >= REG_P
+ && pr.X_add_number <= REG_P + 63)
+ {
+ regno = pr.X_add_number - REG_P;
+ bits <<= regno;
+ count++;
+ if (p1 == -1)
+ p1 = regno;
+ else if (p2 == -1)
+ p2 = regno;
+ }
+ else if (type != 'i'
+ && pr.X_op == O_subtract
+ && (pr1 = symbol_get_value_expression (pr.X_add_symbol))
+ && pr1->X_op == O_register
+ && pr1->X_add_number >= REG_P
+ && pr1->X_add_number <= REG_P + 63
+ && (pr2 = symbol_get_value_expression (pr.X_op_symbol))
+ && pr2->X_op == O_register
+ && pr2->X_add_number >= REG_P
+ && pr2->X_add_number <= REG_P + 63)
+ {
+ /* It's a range. */
+ int stop;
+
+ regno = pr1->X_add_number - REG_P;
+ stop = pr2->X_add_number - REG_P;
+ if (regno >= stop)
+ {
+ as_bad (_("Bad register range"));
+ ignore_rest_of_line ();
+ return;
+ }
+ bits = ((bits << stop) << 1) - (bits << regno);
+ count += stop - regno + 1;
+ }
+ else
+ {
+ as_bad (_("Predicate register expected"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (mask & bits)
+ as_warn (_("Duplicate predicate register ignored"));
+ mask |= bits;
+ if (sep != ',')
+ break;
+ }
+
+ switch (type)
+ {
+ case 'c':
+ if (count == 0)
+ mask = ~(valueT) 0;
+ clear_qp_mutex (mask);
+ clear_qp_implies (mask, (valueT) 0);
+ break;
+ case 'i':
+ if (count != 2 || p1 == -1 || p2 == -1)
+ as_bad (_("Predicate source and target required"));
+ else if (p1 == 0 || p2 == 0)
+ as_bad (_("Use of p0 is not valid in this context"));
+ else
+ add_qp_imply (p1, p2);
+ break;
+ case 'm':
+ if (count < 2)
+ {
+ as_bad (_("At least two PR arguments expected"));
+ break;
+ }
+ else if (mask & 1)
+ {
+ as_bad (_("Use of p0 is not valid in this context"));
+ break;
+ }
+ add_qp_mutex (mask);
+ break;
+ case 's':
+ /* note that we don't override any existing relations */
+ if (count == 0)
+ {
+ as_bad (_("At least one PR argument expected"));
+ break;
+ }
+ if (md.debug_dv)
+ {
+ fprintf (stderr, "Safe across calls: ");
+ print_prmask (mask);
+ fprintf (stderr, "\n");
+ }
+ qp_safe_across_calls = mask;
+ break;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* .entry label [, label [, ...]]
+ Hint to DV code that the given labels are to be considered entry points.
+ Otherwise, only global labels are considered entry points. */
+
+static void
+dot_entry (int dummy ATTRIBUTE_UNUSED)
+{
+ const char *err;
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+
+ err = hash_insert (md.entry_hash, S_GET_NAME (symbolP), (void *) symbolP);
+ if (err)
+ as_fatal (_("Inserting \"%s\" into entry hint table failed: %s"),
+ name, err);
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* .mem.offset offset, base
+ "base" is used to distinguish between offsets from a different base. */
+
+static void
+dot_mem_offset (int dummy ATTRIBUTE_UNUSED)
+{
+ md.mem_offset.hint = 1;
+ md.mem_offset.offset = get_absolute_expression ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Comma expected"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+ md.mem_offset.base = get_absolute_expression ();
+ demand_empty_rest_of_line ();
+}
+
+/* ia64-specific pseudo-ops: */
+const pseudo_typeS md_pseudo_table[] =
+ {
+ { "radix", dot_radix, 0 },
+ { "lcomm", s_lcomm_bytes, 1 },
+ { "loc", dot_loc, 0 },
+ { "bss", dot_special_section, SPECIAL_SECTION_BSS },
+ { "sbss", dot_special_section, SPECIAL_SECTION_SBSS },
+ { "sdata", dot_special_section, SPECIAL_SECTION_SDATA },
+ { "rodata", dot_special_section, SPECIAL_SECTION_RODATA },
+ { "comment", dot_special_section, SPECIAL_SECTION_COMMENT },
+ { "ia_64.unwind", dot_special_section, SPECIAL_SECTION_UNWIND },
+ { "ia_64.unwind_info", dot_special_section, SPECIAL_SECTION_UNWIND_INFO },
+ { "init_array", dot_special_section, SPECIAL_SECTION_INIT_ARRAY },
+ { "fini_array", dot_special_section, SPECIAL_SECTION_FINI_ARRAY },
+ { "proc", dot_proc, 0 },
+ { "body", dot_body, 0 },
+ { "prologue", dot_prologue, 0 },
+ { "endp", dot_endp, 0 },
+
+ { "fframe", dot_fframe, 0 },
+ { "vframe", dot_vframe, 0 },
+ { "vframesp", dot_vframesp, 0 },
+ { "vframepsp", dot_vframesp, 1 },
+ { "save", dot_save, 0 },
+ { "restore", dot_restore, 0 },
+ { "restorereg", dot_restorereg, 0 },
+ { "restorereg.p", dot_restorereg, 1 },
+ { "handlerdata", dot_handlerdata, 0 },
+ { "unwentry", dot_unwentry, 0 },
+ { "altrp", dot_altrp, 0 },
+ { "savesp", dot_savemem, 0 },
+ { "savepsp", dot_savemem, 1 },
+ { "save.g", dot_saveg, 0 },
+ { "save.f", dot_savef, 0 },
+ { "save.b", dot_saveb, 0 },
+ { "save.gf", dot_savegf, 0 },
+ { "spill", dot_spill, 0 },
+ { "spillreg", dot_spillreg, 0 },
+ { "spillsp", dot_spillmem, 0 },
+ { "spillpsp", dot_spillmem, 1 },
+ { "spillreg.p", dot_spillreg, 1 },
+ { "spillsp.p", dot_spillmem, ~0 },
+ { "spillpsp.p", dot_spillmem, ~1 },
+ { "label_state", dot_label_state, 0 },
+ { "copy_state", dot_copy_state, 0 },
+ { "unwabi", dot_unwabi, 0 },
+ { "personality", dot_personality, 0 },
+ { "mii", dot_template, 0x0 },
+ { "mli", dot_template, 0x2 }, /* old format, for compatibility */
+ { "mlx", dot_template, 0x2 },
+ { "mmi", dot_template, 0x4 },
+ { "mfi", dot_template, 0x6 },
+ { "mmf", dot_template, 0x7 },
+ { "mib", dot_template, 0x8 },
+ { "mbb", dot_template, 0x9 },
+ { "bbb", dot_template, 0xb },
+ { "mmb", dot_template, 0xc },
+ { "mfb", dot_template, 0xe },
+ { "align", dot_align, 0 },
+ { "regstk", dot_regstk, 0 },
+ { "rotr", dot_rot, DYNREG_GR },
+ { "rotf", dot_rot, DYNREG_FR },
+ { "rotp", dot_rot, DYNREG_PR },
+ { "lsb", dot_byteorder, 0 },
+ { "msb", dot_byteorder, 1 },
+ { "psr", dot_psr, 0 },
+ { "alias", dot_alias, 0 },
+ { "secalias", dot_alias, 1 },
+ { "ln", dot_ln, 0 }, /* source line info (for debugging) */
+
+ { "xdata1", dot_xdata, 1 },
+ { "xdata2", dot_xdata, 2 },
+ { "xdata4", dot_xdata, 4 },
+ { "xdata8", dot_xdata, 8 },
+ { "xdata16", dot_xdata, 16 },
+ { "xreal4", dot_xfloat_cons, 'f' },
+ { "xreal8", dot_xfloat_cons, 'd' },
+ { "xreal10", dot_xfloat_cons, 'x' },
+ { "xreal16", dot_xfloat_cons, 'X' },
+ { "xstring", dot_xstringer, 8 + 0 },
+ { "xstringz", dot_xstringer, 8 + 1 },
+
+ /* unaligned versions: */
+ { "xdata2.ua", dot_xdata_ua, 2 },
+ { "xdata4.ua", dot_xdata_ua, 4 },
+ { "xdata8.ua", dot_xdata_ua, 8 },
+ { "xdata16.ua", dot_xdata_ua, 16 },
+ { "xreal4.ua", dot_xfloat_cons_ua, 'f' },
+ { "xreal8.ua", dot_xfloat_cons_ua, 'd' },
+ { "xreal10.ua", dot_xfloat_cons_ua, 'x' },
+ { "xreal16.ua", dot_xfloat_cons_ua, 'X' },
+
+ /* annotations/DV checking support */
+ { "entry", dot_entry, 0 },
+ { "mem.offset", dot_mem_offset, 0 },
+ { "pred.rel", dot_pred_rel, 0 },
+ { "pred.rel.clear", dot_pred_rel, 'c' },
+ { "pred.rel.imply", dot_pred_rel, 'i' },
+ { "pred.rel.mutex", dot_pred_rel, 'm' },
+ { "pred.safe_across_calls", dot_pred_rel, 's' },
+ { "reg.val", dot_reg_val, 0 },
+ { "serialize.data", dot_serialize, 0 },
+ { "serialize.instruction", dot_serialize, 1 },
+ { "auto", dot_dv_mode, 'a' },
+ { "explicit", dot_dv_mode, 'e' },
+ { "default", dot_dv_mode, 'd' },
+
+ /* ??? These are needed to make gas/testsuite/gas/elf/ehopt.s work.
+ IA-64 aligns data allocation pseudo-ops by default, so we have to
+ tell it that these ones are supposed to be unaligned. Long term,
+ should rewrite so that only IA-64 specific data allocation pseudo-ops
+ are aligned by default. */
+ {"2byte", stmt_cons_ua, 2},
+ {"4byte", stmt_cons_ua, 4},
+ {"8byte", stmt_cons_ua, 8},
+
+#ifdef TE_VMS
+ {"vms_common", obj_elf_vms_common, 0},
+#endif
+
+ { NULL, 0, 0 }
+ };
+
+static const struct pseudo_opcode
+ {
+ const char *name;
+ void (*handler) (int);
+ int arg;
+ }
+pseudo_opcode[] =
+ {
+ /* these are more like pseudo-ops, but don't start with a dot */
+ { "data1", cons, 1 },
+ { "data2", cons, 2 },
+ { "data4", cons, 4 },
+ { "data8", cons, 8 },
+ { "data16", cons, 16 },
+ { "real4", stmt_float_cons, 'f' },
+ { "real8", stmt_float_cons, 'd' },
+ { "real10", stmt_float_cons, 'x' },
+ { "real16", stmt_float_cons, 'X' },
+ { "string", stringer, 8 + 0 },
+ { "stringz", stringer, 8 + 1 },
+
+ /* unaligned versions: */
+ { "data2.ua", stmt_cons_ua, 2 },
+ { "data4.ua", stmt_cons_ua, 4 },
+ { "data8.ua", stmt_cons_ua, 8 },
+ { "data16.ua", stmt_cons_ua, 16 },
+ { "real4.ua", float_cons, 'f' },
+ { "real8.ua", float_cons, 'd' },
+ { "real10.ua", float_cons, 'x' },
+ { "real16.ua", float_cons, 'X' },
+ };
+
+/* Declare a register by creating a symbol for it and entering it in
+ the symbol table. */
+
+static symbolS *
+declare_register (const char *name, unsigned int regnum)
+{
+ const char *err;
+ symbolS *sym;
+
+ sym = symbol_create (name, reg_section, regnum, &zero_address_frag);
+
+ err = hash_insert (md.reg_hash, S_GET_NAME (sym), (void *) sym);
+ if (err)
+ as_fatal ("Inserting \"%s\" into register table failed: %s",
+ name, err);
+
+ return sym;
+}
+
+static void
+declare_register_set (const char *prefix,
+ unsigned int num_regs,
+ unsigned int base_regnum)
+{
+ char name[8];
+ unsigned int i;
+
+ for (i = 0; i < num_regs; ++i)
+ {
+ snprintf (name, sizeof (name), "%s%u", prefix, i);
+ declare_register (name, base_regnum + i);
+ }
+}
+
+static unsigned int
+operand_width (enum ia64_opnd opnd)
+{
+ const struct ia64_operand *odesc = &elf64_ia64_operands[opnd];
+ unsigned int bits = 0;
+ int i;
+
+ bits = 0;
+ for (i = 0; i < NELEMS (odesc->field) && odesc->field[i].bits; ++i)
+ bits += odesc->field[i].bits;
+
+ return bits;
+}
+
+static enum operand_match_result
+operand_match (const struct ia64_opcode *idesc, int res_index, expressionS *e)
+{
+ enum ia64_opnd opnd = idesc->operands[res_index];
+ int bits, relocatable = 0;
+ struct insn_fix *fix;
+ bfd_signed_vma val;
+
+ switch (opnd)
+ {
+ /* constants: */
+
+ case IA64_OPND_AR_CCV:
+ if (e->X_op == O_register && e->X_add_number == REG_AR + 32)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_AR_CSD:
+ if (e->X_op == O_register && e->X_add_number == REG_AR + 25)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_AR_PFS:
+ if (e->X_op == O_register && e->X_add_number == REG_AR + 64)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_GR0:
+ if (e->X_op == O_register && e->X_add_number == REG_GR + 0)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_IP:
+ if (e->X_op == O_register && e->X_add_number == REG_IP)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_PR:
+ if (e->X_op == O_register && e->X_add_number == REG_PR)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_PR_ROT:
+ if (e->X_op == O_register && e->X_add_number == REG_PR_ROT)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_PSR:
+ if (e->X_op == O_register && e->X_add_number == REG_PSR)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_PSR_L:
+ if (e->X_op == O_register && e->X_add_number == REG_PSR_L)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_PSR_UM:
+ if (e->X_op == O_register && e->X_add_number == REG_PSR_UM)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_C1:
+ if (e->X_op == O_constant)
+ {
+ if (e->X_add_number == 1)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_C8:
+ if (e->X_op == O_constant)
+ {
+ if (e->X_add_number == 8)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_C16:
+ if (e->X_op == O_constant)
+ {
+ if (e->X_add_number == 16)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ /* register operands: */
+
+ case IA64_OPND_AR3:
+ if (e->X_op == O_register && e->X_add_number >= REG_AR
+ && e->X_add_number < REG_AR + 128)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_B1:
+ case IA64_OPND_B2:
+ if (e->X_op == O_register && e->X_add_number >= REG_BR
+ && e->X_add_number < REG_BR + 8)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_CR3:
+ if (e->X_op == O_register && e->X_add_number >= REG_CR
+ && e->X_add_number < REG_CR + 128)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_DAHR3:
+ if (e->X_op == O_register && e->X_add_number >= REG_DAHR
+ && e->X_add_number < REG_DAHR + 8)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_F1:
+ case IA64_OPND_F2:
+ case IA64_OPND_F3:
+ case IA64_OPND_F4:
+ if (e->X_op == O_register && e->X_add_number >= REG_FR
+ && e->X_add_number < REG_FR + 128)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_P1:
+ case IA64_OPND_P2:
+ if (e->X_op == O_register && e->X_add_number >= REG_P
+ && e->X_add_number < REG_P + 64)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_R1:
+ case IA64_OPND_R2:
+ case IA64_OPND_R3:
+ if (e->X_op == O_register && e->X_add_number >= REG_GR
+ && e->X_add_number < REG_GR + 128)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_R3_2:
+ if (e->X_op == O_register && e->X_add_number >= REG_GR)
+ {
+ if (e->X_add_number < REG_GR + 4)
+ return OPERAND_MATCH;
+ else if (e->X_add_number < REG_GR + 128)
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ /* indirect operands: */
+ case IA64_OPND_CPUID_R3:
+ case IA64_OPND_DBR_R3:
+ case IA64_OPND_DTR_R3:
+ case IA64_OPND_ITR_R3:
+ case IA64_OPND_IBR_R3:
+ case IA64_OPND_MSR_R3:
+ case IA64_OPND_PKR_R3:
+ case IA64_OPND_PMC_R3:
+ case IA64_OPND_PMD_R3:
+ case IA64_OPND_DAHR_R3:
+ case IA64_OPND_RR_R3:
+ if (e->X_op == O_index && e->X_op_symbol
+ && (S_GET_VALUE (e->X_op_symbol) - IND_CPUID
+ == opnd - IA64_OPND_CPUID_R3))
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_MR3:
+ if (e->X_op == O_index && !e->X_op_symbol)
+ return OPERAND_MATCH;
+ break;
+
+ /* immediate operands: */
+ case IA64_OPND_CNT2a:
+ case IA64_OPND_LEN4:
+ case IA64_OPND_LEN6:
+ bits = operand_width (idesc->operands[res_index]);
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_CNT2b:
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) (e->X_add_number - 1) < 3)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_CNT2c:
+ val = e->X_add_number;
+ if (e->X_op == O_constant)
+ {
+ if ((val == 0 || val == 7 || val == 15 || val == 16))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_SOR:
+ /* SOR must be an integer multiple of 8 */
+ if (e->X_op == O_constant && e->X_add_number & 0x7)
+ return OPERAND_OUT_OF_RANGE;
+ case IA64_OPND_SOF:
+ case IA64_OPND_SOL:
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) e->X_add_number <= 96)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_IMMU62:
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << 62))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ else
+ {
+ /* FIXME -- need 62-bit relocation type */
+ as_bad (_("62-bit relocation not yet implemented"));
+ }
+ break;
+
+ case IA64_OPND_IMMU64:
+ if (e->X_op == O_symbol || e->X_op == O_pseudo_fixup
+ || e->X_op == O_subtract)
+ {
+ fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+ fix->code = BFD_RELOC_IA64_IMM64;
+ if (e->X_op != O_subtract)
+ {
+ fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
+ if (e->X_op == O_pseudo_fixup)
+ e->X_op = O_symbol;
+ }
+
+ fix->opnd = idesc->operands[res_index];
+ fix->expr = *e;
+ fix->is_pcrel = 0;
+ ++CURR_SLOT.num_fixups;
+ return OPERAND_MATCH;
+ }
+ else if (e->X_op == O_constant)
+ return OPERAND_MATCH;
+ break;
+
+ case IA64_OPND_IMMU5b:
+ if (e->X_op == O_constant)
+ {
+ val = e->X_add_number;
+ if (val >= 32 && val <= 63)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_CCNT5:
+ case IA64_OPND_CNT5:
+ case IA64_OPND_CNT6:
+ case IA64_OPND_CPOS6a:
+ case IA64_OPND_CPOS6b:
+ case IA64_OPND_CPOS6c:
+ case IA64_OPND_IMMU2:
+ case IA64_OPND_IMMU7a:
+ case IA64_OPND_IMMU7b:
+ case IA64_OPND_IMMU16:
+ case IA64_OPND_IMMU19:
+ case IA64_OPND_IMMU21:
+ case IA64_OPND_IMMU24:
+ case IA64_OPND_MBTYPE4:
+ case IA64_OPND_MHTYPE8:
+ case IA64_OPND_POS6:
+ bits = operand_width (idesc->operands[res_index]);
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_IMMU9:
+ bits = operand_width (idesc->operands[res_index]);
+ if (e->X_op == O_constant)
+ {
+ if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits))
+ {
+ int lobits = e->X_add_number & 0x3;
+ if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0)
+ e->X_add_number |= (bfd_vma) 0x3;
+ return OPERAND_MATCH;
+ }
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_IMM44:
+ /* least 16 bits must be zero */
+ if ((e->X_add_number & 0xffff) != 0)
+ /* XXX technically, this is wrong: we should not be issuing warning
+ messages until we're sure this instruction pattern is going to
+ be used! */
+ as_warn (_("lower 16 bits of mask ignored"));
+
+ if (e->X_op == O_constant)
+ {
+ if (((e->X_add_number >= 0
+ && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 44))
+ || (e->X_add_number < 0
+ && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 44))))
+ {
+ /* sign-extend */
+ if (e->X_add_number >= 0
+ && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0)
+ {
+ e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1);
+ }
+ return OPERAND_MATCH;
+ }
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_IMM17:
+ /* bit 0 is a don't care (pr0 is hardwired to 1) */
+ if (e->X_op == O_constant)
+ {
+ if (((e->X_add_number >= 0
+ && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 17))
+ || (e->X_add_number < 0
+ && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 17))))
+ {
+ /* sign-extend */
+ if (e->X_add_number >= 0
+ && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0)
+ {
+ e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1);
+ }
+ return OPERAND_MATCH;
+ }
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_IMM14:
+ case IA64_OPND_IMM22:
+ relocatable = 1;
+ case IA64_OPND_IMM1:
+ case IA64_OPND_IMM8:
+ case IA64_OPND_IMM8U4:
+ case IA64_OPND_IMM8M1:
+ case IA64_OPND_IMM8M1U4:
+ case IA64_OPND_IMM8M1U8:
+ case IA64_OPND_IMM9a:
+ case IA64_OPND_IMM9b:
+ bits = operand_width (idesc->operands[res_index]);
+ if (relocatable && (e->X_op == O_symbol
+ || e->X_op == O_subtract
+ || e->X_op == O_pseudo_fixup))
+ {
+ fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+
+ if (idesc->operands[res_index] == IA64_OPND_IMM14)
+ fix->code = BFD_RELOC_IA64_IMM14;
+ else
+ fix->code = BFD_RELOC_IA64_IMM22;
+
+ if (e->X_op != O_subtract)
+ {
+ fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
+ if (e->X_op == O_pseudo_fixup)
+ e->X_op = O_symbol;
+ }
+
+ fix->opnd = idesc->operands[res_index];
+ fix->expr = *e;
+ fix->is_pcrel = 0;
+ ++CURR_SLOT.num_fixups;
+ return OPERAND_MATCH;
+ }
+ else if (e->X_op != O_constant
+ && ! (e->X_op == O_big && opnd == IA64_OPND_IMM8M1U8))
+ return OPERAND_MISMATCH;
+
+ if (opnd == IA64_OPND_IMM8M1U4)
+ {
+ /* Zero is not valid for unsigned compares that take an adjusted
+ constant immediate range. */
+ if (e->X_add_number == 0)
+ return OPERAND_OUT_OF_RANGE;
+
+ /* Sign-extend 32-bit unsigned numbers, so that the following range
+ checks will work. */
+ val = e->X_add_number;
+ if (((val & (~(bfd_vma) 0 << 32)) == 0)
+ && ((val & ((bfd_vma) 1 << 31)) != 0))
+ val = ((val << 32) >> 32);
+
+ /* Check for 0x100000000. This is valid because
+ 0x100000000-1 is the same as ((uint32_t) -1). */
+ if (val == ((bfd_signed_vma) 1 << 32))
+ return OPERAND_MATCH;
+
+ val = val - 1;
+ }
+ else if (opnd == IA64_OPND_IMM8M1U8)
+ {
+ /* Zero is not valid for unsigned compares that take an adjusted
+ constant immediate range. */
+ if (e->X_add_number == 0)
+ return OPERAND_OUT_OF_RANGE;
+
+ /* Check for 0x10000000000000000. */
+ if (e->X_op == O_big)
+ {
+ if (generic_bignum[0] == 0
+ && generic_bignum[1] == 0
+ && generic_bignum[2] == 0
+ && generic_bignum[3] == 0
+ && generic_bignum[4] == 1)
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ else
+ val = e->X_add_number - 1;
+ }
+ else if (opnd == IA64_OPND_IMM8M1)
+ val = e->X_add_number - 1;
+ else if (opnd == IA64_OPND_IMM8U4)
+ {
+ /* Sign-extend 32-bit unsigned numbers, so that the following range
+ checks will work. */
+ val = e->X_add_number;
+ if (((val & (~(bfd_vma) 0 << 32)) == 0)
+ && ((val & ((bfd_vma) 1 << 31)) != 0))
+ val = ((val << 32) >> 32);
+ }
+ else
+ val = e->X_add_number;
+
+ if ((val >= 0 && (bfd_vma) val < ((bfd_vma) 1 << (bits - 1)))
+ || (val < 0 && (bfd_vma) -val <= ((bfd_vma) 1 << (bits - 1))))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+
+ case IA64_OPND_INC3:
+ /* +/- 1, 4, 8, 16 */
+ val = e->X_add_number;
+ if (val < 0)
+ val = -val;
+ if (e->X_op == O_constant)
+ {
+ if ((val == 1 || val == 4 || val == 8 || val == 16))
+ return OPERAND_MATCH;
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ case IA64_OPND_TGT25:
+ case IA64_OPND_TGT25b:
+ case IA64_OPND_TGT25c:
+ case IA64_OPND_TGT64:
+ if (e->X_op == O_symbol)
+ {
+ fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+ if (opnd == IA64_OPND_TGT25)
+ fix->code = BFD_RELOC_IA64_PCREL21F;
+ else if (opnd == IA64_OPND_TGT25b)
+ fix->code = BFD_RELOC_IA64_PCREL21M;
+ else if (opnd == IA64_OPND_TGT25c)
+ fix->code = BFD_RELOC_IA64_PCREL21B;
+ else if (opnd == IA64_OPND_TGT64)
+ fix->code = BFD_RELOC_IA64_PCREL60B;
+ else
+ abort ();
+
+ fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
+ fix->opnd = idesc->operands[res_index];
+ fix->expr = *e;
+ fix->is_pcrel = 1;
+ ++CURR_SLOT.num_fixups;
+ return OPERAND_MATCH;
+ }
+ case IA64_OPND_TAG13:
+ case IA64_OPND_TAG13b:
+ switch (e->X_op)
+ {
+ case O_constant:
+ return OPERAND_MATCH;
+
+ case O_symbol:
+ fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+ /* There are no external relocs for TAG13/TAG13b fields, so we
+ create a dummy reloc. This will not live past md_apply_fix. */
+ fix->code = BFD_RELOC_UNUSED;
+ fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code);
+ fix->opnd = idesc->operands[res_index];
+ fix->expr = *e;
+ fix->is_pcrel = 1;
+ ++CURR_SLOT.num_fixups;
+ return OPERAND_MATCH;
+
+ default:
+ break;
+ }
+ break;
+
+ case IA64_OPND_LDXMOV:
+ fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups;
+ fix->code = BFD_RELOC_IA64_LDXMOV;
+ fix->opnd = idesc->operands[res_index];
+ fix->expr = *e;
+ fix->is_pcrel = 0;
+ ++CURR_SLOT.num_fixups;
+ return OPERAND_MATCH;
+
+ case IA64_OPND_STRD5b:
+ if (e->X_op == O_constant)
+ {
+ /* 5-bit signed scaled by 64 */
+ if ((e->X_add_number <= ( 0xf << 6 ))
+ && (e->X_add_number >= -( 0x10 << 6 )))
+ {
+
+ /* Must be a multiple of 64 */
+ if ((e->X_add_number & 0x3f) != 0)
+ as_warn (_("stride must be a multiple of 64; lower 6 bits ignored"));
+
+ e->X_add_number &= ~ 0x3f;
+ return OPERAND_MATCH;
+ }
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+ case IA64_OPND_CNT6a:
+ if (e->X_op == O_constant)
+ {
+ /* 6-bit unsigned biased by 1 -- count 0 is meaningless */
+ if ((e->X_add_number <= 64)
+ && (e->X_add_number > 0) )
+ {
+ return OPERAND_MATCH;
+ }
+ else
+ return OPERAND_OUT_OF_RANGE;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return OPERAND_MISMATCH;
+}
+
+static int
+parse_operand (expressionS *e, int more)
+{
+ int sep = '\0';
+
+ memset (e, 0, sizeof (*e));
+ e->X_op = O_absent;
+ SKIP_WHITESPACE ();
+ expression (e);
+ sep = *input_line_pointer;
+ if (more && (sep == ',' || sep == more))
+ ++input_line_pointer;
+ return sep;
+}
+
+static int
+parse_operand_and_eval (expressionS *e, int more)
+{
+ int sep = parse_operand (e, more);
+ resolve_expression (e);
+ return sep;
+}
+
+static int
+parse_operand_maybe_eval (expressionS *e, int more, enum ia64_opnd op)
+{
+ int sep = parse_operand (e, more);
+ switch (op)
+ {
+ case IA64_OPND_IMM14:
+ case IA64_OPND_IMM22:
+ case IA64_OPND_IMMU64:
+ case IA64_OPND_TGT25:
+ case IA64_OPND_TGT25b:
+ case IA64_OPND_TGT25c:
+ case IA64_OPND_TGT64:
+ case IA64_OPND_TAG13:
+ case IA64_OPND_TAG13b:
+ case IA64_OPND_LDXMOV:
+ break;
+ default:
+ resolve_expression (e);
+ break;
+ }
+ return sep;
+}
+
+/* Returns the next entry in the opcode table that matches the one in
+ IDESC, and frees the entry in IDESC. If no matching entry is
+ found, NULL is returned instead. */
+
+static struct ia64_opcode *
+get_next_opcode (struct ia64_opcode *idesc)
+{
+ struct ia64_opcode *next = ia64_find_next_opcode (idesc);
+ ia64_free_opcode (idesc);
+ return next;
+}
+
+/* Parse the operands for the opcode and find the opcode variant that
+ matches the specified operands, or NULL if no match is possible. */
+
+static struct ia64_opcode *
+parse_operands (struct ia64_opcode *idesc)
+{
+ int i = 0, highest_unmatched_operand, num_operands = 0, num_outputs = 0;
+ int error_pos, out_of_range_pos, curr_out_of_range_pos, sep = 0;
+ int reg1, reg2;
+ char reg_class;
+ enum ia64_opnd expected_operand = IA64_OPND_NIL;
+ enum operand_match_result result;
+ char mnemonic[129];
+ char *first_arg = 0, *end, *saved_input_pointer;
+ unsigned int sof;
+
+ gas_assert (strlen (idesc->name) <= 128);
+
+ strcpy (mnemonic, idesc->name);
+ if (idesc->operands[2] == IA64_OPND_SOF
+ || idesc->operands[1] == IA64_OPND_SOF)
+ {
+ /* To make the common idiom "alloc loc?=ar.pfs,0,1,0,0" work, we
+ can't parse the first operand until we have parsed the
+ remaining operands of the "alloc" instruction. */
+ SKIP_WHITESPACE ();
+ first_arg = input_line_pointer;
+ end = strchr (input_line_pointer, '=');
+ if (!end)
+ {
+ as_bad (_("Expected separator `='"));
+ return 0;
+ }
+ input_line_pointer = end + 1;
+ ++i;
+ ++num_outputs;
+ }
+
+ for (; ; ++i)
+ {
+ if (i < NELEMS (CURR_SLOT.opnd))
+ {
+ sep = parse_operand_maybe_eval (CURR_SLOT.opnd + i, '=',
+ idesc->operands[i]);
+ if (CURR_SLOT.opnd[i].X_op == O_absent)
+ break;
+ }
+ else
+ {
+ expressionS dummy;
+
+ sep = parse_operand (&dummy, '=');
+ if (dummy.X_op == O_absent)
+ break;
+ }
+
+ ++num_operands;
+
+ if (sep != '=' && sep != ',')
+ break;
+
+ if (sep == '=')
+ {
+ if (num_outputs > 0)
+ as_bad (_("Duplicate equal sign (=) in instruction"));
+ else
+ num_outputs = i + 1;
+ }
+ }
+ if (sep != '\0')
+ {
+ as_bad (_("Illegal operand separator `%c'"), sep);
+ return 0;
+ }
+
+ if (idesc->operands[2] == IA64_OPND_SOF
+ || idesc->operands[1] == IA64_OPND_SOF)
+ {
+ /* Map alloc r1=ar.pfs,i,l,o,r to alloc r1=ar.pfs,(i+l+o),(i+l),r.
+ Note, however, that due to that mapping operand numbers in error
+ messages for any of the constant operands will not be correct. */
+ know (strcmp (idesc->name, "alloc") == 0);
+ /* The first operand hasn't been parsed/initialized, yet (but
+ num_operands intentionally doesn't account for that). */
+ i = num_operands > 4 ? 2 : 1;
+#define FORCE_CONST(n) (CURR_SLOT.opnd[n].X_op == O_constant \
+ ? CURR_SLOT.opnd[n].X_add_number \
+ : 0)
+ sof = set_regstack (FORCE_CONST(i),
+ FORCE_CONST(i + 1),
+ FORCE_CONST(i + 2),
+ FORCE_CONST(i + 3));
+#undef FORCE_CONST
+
+ /* now we can parse the first arg: */
+ saved_input_pointer = input_line_pointer;
+ input_line_pointer = first_arg;
+ sep = parse_operand_maybe_eval (CURR_SLOT.opnd + 0, '=',
+ idesc->operands[0]);
+ if (sep != '=')
+ --num_outputs; /* force error */
+ input_line_pointer = saved_input_pointer;
+
+ CURR_SLOT.opnd[i].X_add_number = sof;
+ if (CURR_SLOT.opnd[i + 1].X_op == O_constant
+ && CURR_SLOT.opnd[i + 2].X_op == O_constant)
+ CURR_SLOT.opnd[i + 1].X_add_number
+ = sof - CURR_SLOT.opnd[i + 2].X_add_number;
+ else
+ CURR_SLOT.opnd[i + 1].X_op = O_illegal;
+ CURR_SLOT.opnd[i + 2] = CURR_SLOT.opnd[i + 3];
+ }
+
+ highest_unmatched_operand = -4;
+ curr_out_of_range_pos = -1;
+ error_pos = 0;
+ for (; idesc; idesc = get_next_opcode (idesc))
+ {
+ if (num_outputs != idesc->num_outputs)
+ continue; /* mismatch in # of outputs */
+ if (highest_unmatched_operand < 0)
+ highest_unmatched_operand |= 1;
+ if (num_operands > NELEMS (idesc->operands)
+ || (num_operands < NELEMS (idesc->operands)
+ && idesc->operands[num_operands])
+ || (num_operands > 0 && !idesc->operands[num_operands - 1]))
+ continue; /* mismatch in number of arguments */
+ if (highest_unmatched_operand < 0)
+ highest_unmatched_operand |= 2;
+
+ CURR_SLOT.num_fixups = 0;
+
+ /* Try to match all operands. If we see an out-of-range operand,
+ then continue trying to match the rest of the operands, since if
+ the rest match, then this idesc will give the best error message. */
+
+ out_of_range_pos = -1;
+ for (i = 0; i < num_operands && idesc->operands[i]; ++i)
+ {
+ result = operand_match (idesc, i, CURR_SLOT.opnd + i);
+ if (result != OPERAND_MATCH)
+ {
+ if (result != OPERAND_OUT_OF_RANGE)
+ break;
+ if (out_of_range_pos < 0)
+ /* remember position of the first out-of-range operand: */
+ out_of_range_pos = i;
+ }
+ }
+
+ /* If we did not match all operands, or if at least one operand was
+ out-of-range, then this idesc does not match. Keep track of which
+ idesc matched the most operands before failing. If we have two
+ idescs that failed at the same position, and one had an out-of-range
+ operand, then prefer the out-of-range operand. Thus if we have
+ "add r0=0x1000000,r1" we get an error saying the constant is out
+ of range instead of an error saying that the constant should have been
+ a register. */
+
+ if (i != num_operands || out_of_range_pos >= 0)
+ {
+ if (i > highest_unmatched_operand
+ || (i == highest_unmatched_operand
+ && out_of_range_pos > curr_out_of_range_pos))
+ {
+ highest_unmatched_operand = i;
+ if (out_of_range_pos >= 0)
+ {
+ expected_operand = idesc->operands[out_of_range_pos];
+ error_pos = out_of_range_pos;
+ }
+ else
+ {
+ expected_operand = idesc->operands[i];
+ error_pos = i;
+ }
+ curr_out_of_range_pos = out_of_range_pos;
+ }
+ continue;
+ }
+
+ break;
+ }
+ if (!idesc)
+ {
+ if (expected_operand)
+ as_bad (_("Operand %u of `%s' should be %s"),
+ error_pos + 1, mnemonic,
+ elf64_ia64_operands[expected_operand].desc);
+ else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 1))
+ as_bad (_("Wrong number of output operands"));
+ else if (highest_unmatched_operand < 0 && !(highest_unmatched_operand & 2))
+ as_bad (_("Wrong number of input operands"));
+ else
+ as_bad (_("Operand mismatch"));
+ return 0;
+ }
+
+ /* Check that the instruction doesn't use
+ - r0, f0, or f1 as output operands
+ - the same predicate twice as output operands
+ - r0 as address of a base update load or store
+ - the same GR as output and address of a base update load
+ - two even- or two odd-numbered FRs as output operands of a floating
+ point parallel load.
+ At most two (conflicting) output (or output-like) operands can exist,
+ (floating point parallel loads have three outputs, but the base register,
+ if updated, cannot conflict with the actual outputs). */
+ reg2 = reg1 = -1;
+ for (i = 0; i < num_operands; ++i)
+ {
+ int regno = 0;
+
+ reg_class = 0;
+ switch (idesc->operands[i])
+ {
+ case IA64_OPND_R1:
+ case IA64_OPND_R2:
+ case IA64_OPND_R3:
+ if (i < num_outputs)
+ {
+ if (CURR_SLOT.opnd[i].X_add_number == REG_GR)
+ reg_class = 'r';
+ else if (reg1 < 0)
+ reg1 = CURR_SLOT.opnd[i].X_add_number;
+ else if (reg2 < 0)
+ reg2 = CURR_SLOT.opnd[i].X_add_number;
+ }
+ break;
+ case IA64_OPND_P1:
+ case IA64_OPND_P2:
+ if (i < num_outputs)
+ {
+ if (reg1 < 0)
+ reg1 = CURR_SLOT.opnd[i].X_add_number;
+ else if (reg2 < 0)
+ reg2 = CURR_SLOT.opnd[i].X_add_number;
+ }
+ break;
+ case IA64_OPND_F1:
+ case IA64_OPND_F2:
+ case IA64_OPND_F3:
+ case IA64_OPND_F4:
+ if (i < num_outputs)
+ {
+ if (CURR_SLOT.opnd[i].X_add_number >= REG_FR
+ && CURR_SLOT.opnd[i].X_add_number <= REG_FR + 1)
+ {
+ reg_class = 'f';
+ regno = CURR_SLOT.opnd[i].X_add_number - REG_FR;
+ }
+ else if (reg1 < 0)
+ reg1 = CURR_SLOT.opnd[i].X_add_number;
+ else if (reg2 < 0)
+ reg2 = CURR_SLOT.opnd[i].X_add_number;
+ }
+ break;
+ case IA64_OPND_MR3:
+ if (idesc->flags & IA64_OPCODE_POSTINC)
+ {
+ if (CURR_SLOT.opnd[i].X_add_number == REG_GR)
+ reg_class = 'm';
+ else if (reg1 < 0)
+ reg1 = CURR_SLOT.opnd[i].X_add_number;
+ else if (reg2 < 0)
+ reg2 = CURR_SLOT.opnd[i].X_add_number;
+ }
+ break;
+ default:
+ break;
+ }
+ switch (reg_class)
+ {
+ case 0:
+ break;
+ default:
+ as_warn (_("Invalid use of `%c%d' as output operand"), reg_class, regno);
+ break;
+ case 'm':
+ as_warn (_("Invalid use of `r%d' as base update address operand"), regno);
+ break;
+ }
+ }
+ if (reg1 == reg2)
+ {
+ if (reg1 >= REG_GR && reg1 <= REG_GR + 127)
+ {
+ reg1 -= REG_GR;
+ reg_class = 'r';
+ }
+ else if (reg1 >= REG_P && reg1 <= REG_P + 63)
+ {
+ reg1 -= REG_P;
+ reg_class = 'p';
+ }
+ else if (reg1 >= REG_FR && reg1 <= REG_FR + 127)
+ {
+ reg1 -= REG_FR;
+ reg_class = 'f';
+ }
+ else
+ reg_class = 0;
+ if (reg_class)
+ as_warn (_("Invalid duplicate use of `%c%d'"), reg_class, reg1);
+ }
+ else if (((reg1 >= REG_FR && reg1 <= REG_FR + 31
+ && reg2 >= REG_FR && reg2 <= REG_FR + 31)
+ || (reg1 >= REG_FR + 32 && reg1 <= REG_FR + 127
+ && reg2 >= REG_FR + 32 && reg2 <= REG_FR + 127))
+ && ! ((reg1 ^ reg2) & 1))
+ as_warn (_("Invalid simultaneous use of `f%d' and `f%d'"),
+ reg1 - REG_FR, reg2 - REG_FR);
+ else if ((reg1 >= REG_FR && reg1 <= REG_FR + 31
+ && reg2 >= REG_FR + 32 && reg2 <= REG_FR + 127)
+ || (reg1 >= REG_FR + 32 && reg1 <= REG_FR + 127
+ && reg2 >= REG_FR && reg2 <= REG_FR + 31))
+ as_warn (_("Dangerous simultaneous use of `f%d' and `f%d'"),
+ reg1 - REG_FR, reg2 - REG_FR);
+ return idesc;
+}
+
+static void
+build_insn (struct slot *slot, bfd_vma *insnp)
+{
+ const struct ia64_operand *odesc, *o2desc;
+ struct ia64_opcode *idesc = slot->idesc;
+ bfd_vma insn;
+ bfd_signed_vma val;
+ const char *err;
+ int i;
+
+ insn = idesc->opcode | slot->qp_regno;
+
+ for (i = 0; i < NELEMS (idesc->operands) && idesc->operands[i]; ++i)
+ {
+ if (slot->opnd[i].X_op == O_register
+ || slot->opnd[i].X_op == O_constant
+ || slot->opnd[i].X_op == O_index)
+ val = slot->opnd[i].X_add_number;
+ else if (slot->opnd[i].X_op == O_big)
+ {
+ /* This must be the value 0x10000000000000000. */
+ gas_assert (idesc->operands[i] == IA64_OPND_IMM8M1U8);
+ val = 0;
+ }
+ else
+ val = 0;
+
+ switch (idesc->operands[i])
+ {
+ case IA64_OPND_IMMU64:
+ *insnp++ = (val >> 22) & 0x1ffffffffffLL;
+ insn |= (((val & 0x7f) << 13) | (((val >> 7) & 0x1ff) << 27)
+ | (((val >> 16) & 0x1f) << 22) | (((val >> 21) & 0x1) << 21)
+ | (((val >> 63) & 0x1) << 36));
+ continue;
+
+ case IA64_OPND_IMMU62:
+ val &= 0x3fffffffffffffffULL;
+ if (val != slot->opnd[i].X_add_number)
+ as_warn (_("Value truncated to 62 bits"));
+ *insnp++ = (val >> 21) & 0x1ffffffffffLL;
+ insn |= (((val & 0xfffff) << 6) | (((val >> 20) & 0x1) << 36));
+ continue;
+
+ case IA64_OPND_TGT64:
+ val >>= 4;
+ *insnp++ = ((val >> 20) & 0x7fffffffffLL) << 2;
+ insn |= ((((val >> 59) & 0x1) << 36)
+ | (((val >> 0) & 0xfffff) << 13));
+ continue;
+
+ case IA64_OPND_AR3:
+ val -= REG_AR;
+ break;
+
+ case IA64_OPND_B1:
+ case IA64_OPND_B2:
+ val -= REG_BR;
+ break;
+
+ case IA64_OPND_CR3:
+ val -= REG_CR;
+ break;
+
+ case IA64_OPND_DAHR3:
+ val -= REG_DAHR;
+ break;
+
+ case IA64_OPND_F1:
+ case IA64_OPND_F2:
+ case IA64_OPND_F3:
+ case IA64_OPND_F4:
+ val -= REG_FR;
+ break;
+
+ case IA64_OPND_P1:
+ case IA64_OPND_P2:
+ val -= REG_P;
+ break;
+
+ case IA64_OPND_R1:
+ case IA64_OPND_R2:
+ case IA64_OPND_R3:
+ case IA64_OPND_R3_2:
+ case IA64_OPND_CPUID_R3:
+ case IA64_OPND_DBR_R3:
+ case IA64_OPND_DTR_R3:
+ case IA64_OPND_ITR_R3:
+ case IA64_OPND_IBR_R3:
+ case IA64_OPND_MR3:
+ case IA64_OPND_MSR_R3:
+ case IA64_OPND_PKR_R3:
+ case IA64_OPND_PMC_R3:
+ case IA64_OPND_PMD_R3:
+ case IA64_OPND_DAHR_R3:
+ case IA64_OPND_RR_R3:
+ val -= REG_GR;
+ break;
+
+ default:
+ break;
+ }
+
+ odesc = elf64_ia64_operands + idesc->operands[i];
+ err = (*odesc->insert) (odesc, val, &insn);
+ if (err)
+ as_bad_where (slot->src_file, slot->src_line,
+ _("Bad operand value: %s"), err);
+ if (idesc->flags & IA64_OPCODE_PSEUDO)
+ {
+ if ((idesc->flags & IA64_OPCODE_F2_EQ_F3)
+ && odesc == elf64_ia64_operands + IA64_OPND_F3)
+ {
+ o2desc = elf64_ia64_operands + IA64_OPND_F2;
+ (*o2desc->insert) (o2desc, val, &insn);
+ }
+ if ((idesc->flags & IA64_OPCODE_LEN_EQ_64MCNT)
+ && (odesc == elf64_ia64_operands + IA64_OPND_CPOS6a
+ || odesc == elf64_ia64_operands + IA64_OPND_POS6))
+ {
+ o2desc = elf64_ia64_operands + IA64_OPND_LEN6;
+ (*o2desc->insert) (o2desc, 64 - val, &insn);
+ }
+ }
+ }
+ *insnp = insn;
+}
+
+static void
+emit_one_bundle (void)
+{
+ int manual_bundling_off = 0, manual_bundling = 0;
+ enum ia64_unit required_unit, insn_unit = 0;
+ enum ia64_insn_type type[3], insn_type;
+ unsigned int template_val, orig_template;
+ bfd_vma insn[3] = { -1, -1, -1 };
+ struct ia64_opcode *idesc;
+ int end_of_insn_group = 0, user_template = -1;
+ int n, i, j, first, curr, last_slot;
+ bfd_vma t0 = 0, t1 = 0;
+ struct label_fix *lfix;
+ bfd_boolean mark_label;
+ struct insn_fix *ifix;
+ char mnemonic[16];
+ fixS *fix;
+ char *f;
+ int addr_mod;
+
+ first = (md.curr_slot + NUM_SLOTS - md.num_slots_in_use) % NUM_SLOTS;
+ know (first >= 0 && first < NUM_SLOTS);
+ n = MIN (3, md.num_slots_in_use);
+
+ /* Determine template: user user_template if specified, best match
+ otherwise: */
+
+ if (md.slot[first].user_template >= 0)
+ user_template = template_val = md.slot[first].user_template;
+ else
+ {
+ /* Auto select appropriate template. */
+ memset (type, 0, sizeof (type));
+ curr = first;
+ for (i = 0; i < n; ++i)
+ {
+ if (md.slot[curr].label_fixups && i != 0)
+ break;
+ type[i] = md.slot[curr].idesc->type;
+ curr = (curr + 1) % NUM_SLOTS;
+ }
+ template_val = best_template[type[0]][type[1]][type[2]];
+ }
+
+ /* initialize instructions with appropriate nops: */
+ for (i = 0; i < 3; ++i)
+ insn[i] = nop[ia64_templ_desc[template_val].exec_unit[i]];
+
+ f = frag_more (16);
+
+ /* Check to see if this bundle is at an offset that is a multiple of 16-bytes
+ from the start of the frag. */
+ addr_mod = frag_now_fix () & 15;
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ as_bad (_("instruction address is not a multiple of 16"));
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+
+ /* now fill in slots with as many insns as possible: */
+ curr = first;
+ idesc = md.slot[curr].idesc;
+ end_of_insn_group = 0;
+ last_slot = -1;
+ for (i = 0; i < 3 && md.num_slots_in_use > 0; ++i)
+ {
+ /* If we have unwind records, we may need to update some now. */
+ unw_rec_list *ptr = md.slot[curr].unwind_record;
+ unw_rec_list *end_ptr = NULL;
+
+ if (ptr)
+ {
+ /* Find the last prologue/body record in the list for the current
+ insn, and set the slot number for all records up to that point.
+ This needs to be done now, because prologue/body records refer to
+ the current point, not the point after the instruction has been
+ issued. This matters because there may have been nops emitted
+ meanwhile. Any non-prologue non-body record followed by a
+ prologue/body record must also refer to the current point. */
+ unw_rec_list *last_ptr;
+
+ for (j = 1; end_ptr == NULL && j < md.num_slots_in_use; ++j)
+ end_ptr = md.slot[(curr + j) % NUM_SLOTS].unwind_record;
+ for (last_ptr = NULL; ptr != end_ptr; ptr = ptr->next)
+ if (ptr->r.type == prologue || ptr->r.type == prologue_gr
+ || ptr->r.type == body)
+ last_ptr = ptr;
+ if (last_ptr)
+ {
+ /* Make last_ptr point one after the last prologue/body
+ record. */
+ last_ptr = last_ptr->next;
+ for (ptr = md.slot[curr].unwind_record; ptr != last_ptr;
+ ptr = ptr->next)
+ {
+ ptr->slot_number = (unsigned long) f + i;
+ ptr->slot_frag = frag_now;
+ }
+ /* Remove the initialized records, so that we won't accidentally
+ update them again if we insert a nop and continue. */
+ md.slot[curr].unwind_record = last_ptr;
+ }
+ }
+
+ manual_bundling_off = md.slot[curr].manual_bundling_off;
+ if (md.slot[curr].manual_bundling_on)
+ {
+ if (curr == first)
+ manual_bundling = 1;
+ else
+ break; /* Need to start a new bundle. */
+ }
+
+ /* If this instruction specifies a template, then it must be the first
+ instruction of a bundle. */
+ if (curr != first && md.slot[curr].user_template >= 0)
+ break;
+
+ if (idesc->flags & IA64_OPCODE_SLOT2)
+ {
+ if (manual_bundling && !manual_bundling_off)
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("`%s' must be last in bundle"), idesc->name);
+ if (i < 2)
+ manual_bundling = -1; /* Suppress meaningless post-loop errors. */
+ }
+ i = 2;
+ }
+ if (idesc->flags & IA64_OPCODE_LAST)
+ {
+ int required_slot;
+ unsigned int required_template;
+
+ /* If we need a stop bit after an M slot, our only choice is
+ template 5 (M;;MI). If we need a stop bit after a B
+ slot, our only choice is to place it at the end of the
+ bundle, because the only available templates are MIB,
+ MBB, BBB, MMB, and MFB. We don't handle anything other
+ than M and B slots because these are the only kind of
+ instructions that can have the IA64_OPCODE_LAST bit set. */
+ required_template = template_val;
+ switch (idesc->type)
+ {
+ case IA64_TYPE_M:
+ required_slot = 0;
+ required_template = 5;
+ break;
+
+ case IA64_TYPE_B:
+ required_slot = 2;
+ break;
+
+ default:
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("Internal error: don't know how to force %s to end of instruction group"),
+ idesc->name);
+ required_slot = i;
+ break;
+ }
+ if (manual_bundling
+ && (i > required_slot
+ || (required_slot == 2 && !manual_bundling_off)
+ || (user_template >= 0
+ /* Changing from MMI to M;MI is OK. */
+ && (template_val ^ required_template) > 1)))
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("`%s' must be last in instruction group"),
+ idesc->name);
+ if (i < 2 && required_slot == 2 && !manual_bundling_off)
+ manual_bundling = -1; /* Suppress meaningless post-loop errors. */
+ }
+ if (required_slot < i)
+ /* Can't fit this instruction. */
+ break;
+
+ i = required_slot;
+ if (required_template != template_val)
+ {
+ /* If we switch the template, we need to reset the NOPs
+ after slot i. The slot-types of the instructions ahead
+ of i never change, so we don't need to worry about
+ changing NOPs in front of this slot. */
+ for (j = i; j < 3; ++j)
+ insn[j] = nop[ia64_templ_desc[required_template].exec_unit[j]];
+
+ /* We just picked a template that includes the stop bit in the
+ middle, so we don't need another one emitted later. */
+ md.slot[curr].end_of_insn_group = 0;
+ }
+ template_val = required_template;
+ }
+ if (curr != first && md.slot[curr].label_fixups)
+ {
+ if (manual_bundling)
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("Label must be first in a bundle"));
+ manual_bundling = -1; /* Suppress meaningless post-loop errors. */
+ }
+ /* This insn must go into the first slot of a bundle. */
+ break;
+ }
+
+ if (end_of_insn_group && md.num_slots_in_use >= 1)
+ {
+ /* We need an instruction group boundary in the middle of a
+ bundle. See if we can switch to an other template with
+ an appropriate boundary. */
+
+ orig_template = template_val;
+ if (i == 1 && (user_template == 4
+ || (user_template < 0
+ && (ia64_templ_desc[template_val].exec_unit[0]
+ == IA64_UNIT_M))))
+ {
+ template_val = 5;
+ end_of_insn_group = 0;
+ }
+ else if (i == 2 && (user_template == 0
+ || (user_template < 0
+ && (ia64_templ_desc[template_val].exec_unit[1]
+ == IA64_UNIT_I)))
+ /* This test makes sure we don't switch the template if
+ the next instruction is one that needs to be first in
+ an instruction group. Since all those instructions are
+ in the M group, there is no way such an instruction can
+ fit in this bundle even if we switch the template. The
+ reason we have to check for this is that otherwise we
+ may end up generating "MI;;I M.." which has the deadly
+ effect that the second M instruction is no longer the
+ first in the group! --davidm 99/12/16 */
+ && (idesc->flags & IA64_OPCODE_FIRST) == 0)
+ {
+ template_val = 1;
+ end_of_insn_group = 0;
+ }
+ else if (i == 1
+ && user_template == 0
+ && !(idesc->flags & IA64_OPCODE_FIRST))
+ /* Use the next slot. */
+ continue;
+ else if (curr != first)
+ /* can't fit this insn */
+ break;
+
+ if (template_val != orig_template)
+ /* if we switch the template, we need to reset the NOPs
+ after slot i. The slot-types of the instructions ahead
+ of i never change, so we don't need to worry about
+ changing NOPs in front of this slot. */
+ for (j = i; j < 3; ++j)
+ insn[j] = nop[ia64_templ_desc[template_val].exec_unit[j]];
+ }
+ required_unit = ia64_templ_desc[template_val].exec_unit[i];
+
+ /* resolve dynamic opcodes such as "break", "hint", and "nop": */
+ if (idesc->type == IA64_TYPE_DYN)
+ {
+ enum ia64_opnd opnd1, opnd2;
+
+ if ((strcmp (idesc->name, "nop") == 0)
+ || (strcmp (idesc->name, "break") == 0))
+ insn_unit = required_unit;
+ else if (strcmp (idesc->name, "hint") == 0)
+ {
+ insn_unit = required_unit;
+ if (required_unit == IA64_UNIT_B)
+ {
+ switch (md.hint_b)
+ {
+ case hint_b_ok:
+ break;
+ case hint_b_warning:
+ as_warn (_("hint in B unit may be treated as nop"));
+ break;
+ case hint_b_error:
+ /* When manual bundling is off and there is no
+ user template, we choose a different unit so
+ that hint won't go into the current slot. We
+ will fill the current bundle with nops and
+ try to put hint into the next bundle. */
+ if (!manual_bundling && user_template < 0)
+ insn_unit = IA64_UNIT_I;
+ else
+ as_bad (_("hint in B unit can't be used"));
+ break;
+ }
+ }
+ }
+ else if (strcmp (idesc->name, "chk.s") == 0
+ || strcmp (idesc->name, "mov") == 0)
+ {
+ insn_unit = IA64_UNIT_M;
+ if (required_unit == IA64_UNIT_I
+ || (required_unit == IA64_UNIT_F && template_val == 6))
+ insn_unit = IA64_UNIT_I;
+ }
+ else
+ as_fatal (_("emit_one_bundle: unexpected dynamic op"));
+
+ snprintf (mnemonic, sizeof (mnemonic), "%s.%c",
+ idesc->name, "?imbfxx"[insn_unit]);
+ opnd1 = idesc->operands[0];
+ opnd2 = idesc->operands[1];
+ ia64_free_opcode (idesc);
+ idesc = ia64_find_opcode (mnemonic);
+ /* moves to/from ARs have collisions */
+ if (opnd1 == IA64_OPND_AR3 || opnd2 == IA64_OPND_AR3)
+ {
+ while (idesc != NULL
+ && (idesc->operands[0] != opnd1
+ || idesc->operands[1] != opnd2))
+ idesc = get_next_opcode (idesc);
+ }
+ md.slot[curr].idesc = idesc;
+ }
+ else
+ {
+ insn_type = idesc->type;
+ insn_unit = IA64_UNIT_NIL;
+ switch (insn_type)
+ {
+ case IA64_TYPE_A:
+ if (required_unit == IA64_UNIT_I || required_unit == IA64_UNIT_M)
+ insn_unit = required_unit;
+ break;
+ case IA64_TYPE_X: insn_unit = IA64_UNIT_L; break;
+ case IA64_TYPE_I: insn_unit = IA64_UNIT_I; break;
+ case IA64_TYPE_M: insn_unit = IA64_UNIT_M; break;
+ case IA64_TYPE_B: insn_unit = IA64_UNIT_B; break;
+ case IA64_TYPE_F: insn_unit = IA64_UNIT_F; break;
+ default: break;
+ }
+ }
+
+ if (insn_unit != required_unit)
+ continue; /* Try next slot. */
+
+ /* Now is a good time to fix up the labels for this insn. */
+ mark_label = FALSE;
+ for (lfix = md.slot[curr].label_fixups; lfix; lfix = lfix->next)
+ {
+ S_SET_VALUE (lfix->sym, frag_now_fix () - 16);
+ symbol_set_frag (lfix->sym, frag_now);
+ mark_label |= lfix->dw2_mark_labels;
+ }
+ for (lfix = md.slot[curr].tag_fixups; lfix; lfix = lfix->next)
+ {
+ S_SET_VALUE (lfix->sym, frag_now_fix () - 16 + i);
+ symbol_set_frag (lfix->sym, frag_now);
+ }
+
+ if (debug_type == DEBUG_DWARF2
+ || md.slot[curr].loc_directive_seen
+ || mark_label)
+ {
+ bfd_vma addr = frag_now->fr_address + frag_now_fix () - 16 + i;
+
+ md.slot[curr].loc_directive_seen = 0;
+ if (mark_label)
+ md.slot[curr].debug_line.flags |= DWARF2_FLAG_BASIC_BLOCK;
+
+ dwarf2_gen_line_info (addr, &md.slot[curr].debug_line);
+ }
+
+ build_insn (md.slot + curr, insn + i);
+
+ ptr = md.slot[curr].unwind_record;
+ if (ptr)
+ {
+ /* Set slot numbers for all remaining unwind records belonging to the
+ current insn. There can not be any prologue/body unwind records
+ here. */
+ for (; ptr != end_ptr; ptr = ptr->next)
+ {
+ ptr->slot_number = (unsigned long) f + i;
+ ptr->slot_frag = frag_now;
+ }
+ md.slot[curr].unwind_record = NULL;
+ }
+
+ for (j = 0; j < md.slot[curr].num_fixups; ++j)
+ {
+ ifix = md.slot[curr].fixup + j;
+ fix = fix_new_exp (frag_now, frag_now_fix () - 16 + i, 8,
+ &ifix->expr, ifix->is_pcrel, ifix->code);
+ fix->tc_fix_data.opnd = ifix->opnd;
+ fix->fx_file = md.slot[curr].src_file;
+ fix->fx_line = md.slot[curr].src_line;
+ }
+
+ end_of_insn_group = md.slot[curr].end_of_insn_group;
+
+ /* This adjustment to "i" must occur after the fix, otherwise the fix
+ is assigned to the wrong slot, and the VMS linker complains. */
+ if (required_unit == IA64_UNIT_L)
+ {
+ know (i == 1);
+ /* skip one slot for long/X-unit instructions */
+ ++i;
+ }
+ --md.num_slots_in_use;
+ last_slot = i;
+
+ /* clear slot: */
+ ia64_free_opcode (md.slot[curr].idesc);
+ memset (md.slot + curr, 0, sizeof (md.slot[curr]));
+ md.slot[curr].user_template = -1;
+
+ if (manual_bundling_off)
+ {
+ manual_bundling = 0;
+ break;
+ }
+ curr = (curr + 1) % NUM_SLOTS;
+ idesc = md.slot[curr].idesc;
+ }
+
+ /* A user template was specified, but the first following instruction did
+ not fit. This can happen with or without manual bundling. */
+ if (md.num_slots_in_use > 0 && last_slot < 0)
+ {
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("`%s' does not fit into %s template"),
+ idesc->name, ia64_templ_desc[template_val].name);
+ /* Drop first insn so we don't livelock. */
+ --md.num_slots_in_use;
+ know (curr == first);
+ ia64_free_opcode (md.slot[curr].idesc);
+ memset (md.slot + curr, 0, sizeof (md.slot[curr]));
+ md.slot[curr].user_template = -1;
+ }
+ else if (manual_bundling > 0)
+ {
+ if (md.num_slots_in_use > 0)
+ {
+ if (last_slot >= 2)
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("`%s' does not fit into bundle"), idesc->name);
+ else
+ {
+ const char *where;
+
+ if (template_val == 2)
+ where = "X slot";
+ else if (last_slot == 0)
+ where = "slots 2 or 3";
+ else
+ where = "slot 3";
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("`%s' can't go in %s of %s template"),
+ idesc->name, where, ia64_templ_desc[template_val].name);
+ }
+ }
+ else
+ as_bad_where (md.slot[curr].src_file, md.slot[curr].src_line,
+ _("Missing '}' at end of file"));
+ }
+
+ know (md.num_slots_in_use < NUM_SLOTS);
+
+ t0 = end_of_insn_group | (template_val << 1) | (insn[0] << 5) | (insn[1] << 46);
+ t1 = ((insn[1] >> 18) & 0x7fffff) | (insn[2] << 23);
+
+ number_to_chars_littleendian (f + 0, t0, 8);
+ number_to_chars_littleendian (f + 8, t1, 8);
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+
+ switch (c)
+ {
+ /* Switches from the Intel assembler. */
+ case 'm':
+ if (strcmp (arg, "ilp64") == 0
+ || strcmp (arg, "lp64") == 0
+ || strcmp (arg, "p64") == 0)
+ {
+ md.flags |= EF_IA_64_ABI64;
+ }
+ else if (strcmp (arg, "ilp32") == 0)
+ {
+ md.flags &= ~EF_IA_64_ABI64;
+ }
+ else if (strcmp (arg, "le") == 0)
+ {
+ md.flags &= ~EF_IA_64_BE;
+ default_big_endian = 0;
+ }
+ else if (strcmp (arg, "be") == 0)
+ {
+ md.flags |= EF_IA_64_BE;
+ default_big_endian = 1;
+ }
+ else if (strncmp (arg, "unwind-check=", 13) == 0)
+ {
+ arg += 13;
+ if (strcmp (arg, "warning") == 0)
+ md.unwind_check = unwind_check_warning;
+ else if (strcmp (arg, "error") == 0)
+ md.unwind_check = unwind_check_error;
+ else
+ return 0;
+ }
+ else if (strncmp (arg, "hint.b=", 7) == 0)
+ {
+ arg += 7;
+ if (strcmp (arg, "ok") == 0)
+ md.hint_b = hint_b_ok;
+ else if (strcmp (arg, "warning") == 0)
+ md.hint_b = hint_b_warning;
+ else if (strcmp (arg, "error") == 0)
+ md.hint_b = hint_b_error;
+ else
+ return 0;
+ }
+ else if (strncmp (arg, "tune=", 5) == 0)
+ {
+ arg += 5;
+ if (strcmp (arg, "itanium1") == 0)
+ md.tune = itanium1;
+ else if (strcmp (arg, "itanium2") == 0)
+ md.tune = itanium2;
+ else
+ return 0;
+ }
+ else
+ return 0;
+ break;
+
+ case 'N':
+ if (strcmp (arg, "so") == 0)
+ {
+ /* Suppress signon message. */
+ }
+ else if (strcmp (arg, "pi") == 0)
+ {
+ /* Reject privileged instructions. FIXME */
+ }
+ else if (strcmp (arg, "us") == 0)
+ {
+ /* Allow union of signed and unsigned range. FIXME */
+ }
+ else if (strcmp (arg, "close_fcalls") == 0)
+ {
+ /* Do not resolve global function calls. */
+ }
+ else
+ return 0;
+ break;
+
+ case 'C':
+ /* temp[="prefix"] Insert temporary labels into the object file
+ symbol table prefixed by "prefix".
+ Default prefix is ":temp:".
+ */
+ break;
+
+ case 'a':
+ /* indirect=<tgt> Assume unannotated indirect branches behavior
+ according to <tgt> --
+ exit: branch out from the current context (default)
+ labels: all labels in context may be branch targets
+ */
+ if (strncmp (arg, "indirect=", 9) != 0)
+ return 0;
+ break;
+
+ case 'x':
+ /* -X conflicts with an ignored option, use -x instead */
+ md.detect_dv = 1;
+ if (!arg || strcmp (arg, "explicit") == 0)
+ {
+ /* set default mode to explicit */
+ md.default_explicit_mode = 1;
+ break;
+ }
+ else if (strcmp (arg, "auto") == 0)
+ {
+ md.default_explicit_mode = 0;
+ }
+ else if (strcmp (arg, "none") == 0)
+ {
+ md.detect_dv = 0;
+ }
+ else if (strcmp (arg, "debug") == 0)
+ {
+ md.debug_dv = 1;
+ }
+ else if (strcmp (arg, "debugx") == 0)
+ {
+ md.default_explicit_mode = 1;
+ md.debug_dv = 1;
+ }
+ else if (strcmp (arg, "debugn") == 0)
+ {
+ md.debug_dv = 1;
+ md.detect_dv = 0;
+ }
+ else
+ {
+ as_bad (_("Unrecognized option '-x%s'"), arg);
+ }
+ break;
+
+ case 'S':
+ /* nops Print nops statistics. */
+ break;
+
+ /* GNU specific switches for gcc. */
+ case OPTION_MCONSTANT_GP:
+ md.flags |= EF_IA_64_CONS_GP;
+ break;
+
+ case OPTION_MAUTO_PIC:
+ md.flags |= EF_IA_64_NOFUNCDESC_CONS_GP;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fputs (_("\
+IA-64 options:\n\
+ --mconstant-gp mark output file as using the constant-GP model\n\
+ (sets ELF header flag EF_IA_64_CONS_GP)\n\
+ --mauto-pic mark output file as using the constant-GP model\n\
+ without function descriptors (sets ELF header flag\n\
+ EF_IA_64_NOFUNCDESC_CONS_GP)\n\
+ -milp32|-milp64|-mlp64|-mp64 select data model (default -mlp64)\n\
+ -mle | -mbe select little- or big-endian byte order (default -mle)\n\
+ -mtune=[itanium1|itanium2]\n\
+ tune for a specific CPU (default -mtune=itanium2)\n\
+ -munwind-check=[warning|error]\n\
+ unwind directive check (default -munwind-check=warning)\n\
+ -mhint.b=[ok|warning|error]\n\
+ hint.b check (default -mhint.b=error)\n\
+ -x | -xexplicit turn on dependency violation checking\n"), stream);
+ /* Note for translators: "automagically" can be translated as "automatically" here. */
+ fputs (_("\
+ -xauto automagically remove dependency violations (default)\n\
+ -xnone turn off dependency violation checking\n\
+ -xdebug debug dependency violation checker\n\
+ -xdebugn debug dependency violation checker but turn off\n\
+ dependency violation checking\n\
+ -xdebugx debug dependency violation checker and turn on\n\
+ dependency violation checking\n"),
+ stream);
+}
+
+void
+ia64_after_parse_args (void)
+{
+ if (debug_type == DEBUG_STABS)
+ as_fatal (_("--gstabs is not supported for ia64"));
+}
+
+/* Return true if TYPE fits in TEMPL at SLOT. */
+
+static int
+match (int templ, int type, int slot)
+{
+ enum ia64_unit unit;
+ int result;
+
+ unit = ia64_templ_desc[templ].exec_unit[slot];
+ switch (type)
+ {
+ case IA64_TYPE_DYN: result = 1; break; /* for nop and break */
+ case IA64_TYPE_A:
+ result = (unit == IA64_UNIT_I || unit == IA64_UNIT_M);
+ break;
+ case IA64_TYPE_X: result = (unit == IA64_UNIT_L); break;
+ case IA64_TYPE_I: result = (unit == IA64_UNIT_I); break;
+ case IA64_TYPE_M: result = (unit == IA64_UNIT_M); break;
+ case IA64_TYPE_B: result = (unit == IA64_UNIT_B); break;
+ case IA64_TYPE_F: result = (unit == IA64_UNIT_F); break;
+ default: result = 0; break;
+ }
+ return result;
+}
+
+/* For Itanium 1, add a bit of extra goodness if a nop of type F or B would fit
+ in TEMPL at SLOT. For Itanium 2, add a bit of extra goodness if a nop of
+ type M or I would fit in TEMPL at SLOT. */
+
+static inline int
+extra_goodness (int templ, int slot)
+{
+ switch (md.tune)
+ {
+ case itanium1:
+ if (slot == 1 && match (templ, IA64_TYPE_F, slot))
+ return 2;
+ else if (slot == 2 && match (templ, IA64_TYPE_B, slot))
+ return 1;
+ else
+ return 0;
+ break;
+ case itanium2:
+ if (match (templ, IA64_TYPE_M, slot)
+ || match (templ, IA64_TYPE_I, slot))
+ /* Favor M- and I-unit NOPs. We definitely want to avoid
+ F-unit and B-unit may cause split-issue or less-than-optimal
+ branch-prediction. */
+ return 2;
+ else
+ return 0;
+ break;
+ default:
+ abort ();
+ return 0;
+ }
+}
+
+/* This function is called once, at assembler startup time. It sets
+ up all the tables, etc. that the MD part of the assembler will need
+ that can be determined before arguments are parsed. */
+void
+md_begin (void)
+{
+ int i, j, k, t, goodness, best, ok;
+ const char *err;
+ char name[8];
+
+ md.auto_align = 1;
+ md.explicit_mode = md.default_explicit_mode;
+
+ bfd_set_section_alignment (stdoutput, text_section, 4);
+
+ /* Make sure function pointers get initialized. */
+ target_big_endian = -1;
+ dot_byteorder (default_big_endian);
+
+ alias_hash = hash_new ();
+ alias_name_hash = hash_new ();
+ secalias_hash = hash_new ();
+ secalias_name_hash = hash_new ();
+
+ pseudo_func[FUNC_DTP_MODULE].u.sym =
+ symbol_new (".<dtpmod>", undefined_section, FUNC_DTP_MODULE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_DTP_RELATIVE].u.sym =
+ symbol_new (".<dtprel>", undefined_section, FUNC_DTP_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_FPTR_RELATIVE].u.sym =
+ symbol_new (".<fptr>", undefined_section, FUNC_FPTR_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_GP_RELATIVE].u.sym =
+ symbol_new (".<gprel>", undefined_section, FUNC_GP_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_RELATIVE].u.sym =
+ symbol_new (".<ltoff>", undefined_section, FUNC_LT_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_RELATIVE_X].u.sym =
+ symbol_new (".<ltoffx>", undefined_section, FUNC_LT_RELATIVE_X,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_PC_RELATIVE].u.sym =
+ symbol_new (".<pcrel>", undefined_section, FUNC_PC_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_PLT_RELATIVE].u.sym =
+ symbol_new (".<pltoff>", undefined_section, FUNC_PLT_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_SEC_RELATIVE].u.sym =
+ symbol_new (".<secrel>", undefined_section, FUNC_SEC_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_SEG_RELATIVE].u.sym =
+ symbol_new (".<segrel>", undefined_section, FUNC_SEG_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_TP_RELATIVE].u.sym =
+ symbol_new (".<tprel>", undefined_section, FUNC_TP_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LTV_RELATIVE].u.sym =
+ symbol_new (".<ltv>", undefined_section, FUNC_LTV_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_FPTR_RELATIVE].u.sym =
+ symbol_new (".<ltoff.fptr>", undefined_section, FUNC_LT_FPTR_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_DTP_MODULE].u.sym =
+ symbol_new (".<ltoff.dtpmod>", undefined_section, FUNC_LT_DTP_MODULE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_DTP_RELATIVE].u.sym =
+ symbol_new (".<ltoff.dptrel>", undefined_section, FUNC_LT_DTP_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_LT_TP_RELATIVE].u.sym =
+ symbol_new (".<ltoff.tprel>", undefined_section, FUNC_LT_TP_RELATIVE,
+ &zero_address_frag);
+
+ pseudo_func[FUNC_IPLT_RELOC].u.sym =
+ symbol_new (".<iplt>", undefined_section, FUNC_IPLT_RELOC,
+ &zero_address_frag);
+
+#ifdef TE_VMS
+ pseudo_func[FUNC_SLOTCOUNT_RELOC].u.sym =
+ symbol_new (".<slotcount>", undefined_section, FUNC_SLOTCOUNT_RELOC,
+ &zero_address_frag);
+#endif
+
+ if (md.tune != itanium1)
+ {
+ /* Convert MFI NOPs bundles into MMI NOPs bundles. */
+ le_nop[0] = 0x8;
+ le_nop_stop[0] = 0x9;
+ }
+
+ /* Compute the table of best templates. We compute goodness as a
+ base 4 value, in which each match counts for 3. Match-failures
+ result in NOPs and we use extra_goodness() to pick the execution
+ units that are best suited for issuing the NOP. */
+ for (i = 0; i < IA64_NUM_TYPES; ++i)
+ for (j = 0; j < IA64_NUM_TYPES; ++j)
+ for (k = 0; k < IA64_NUM_TYPES; ++k)
+ {
+ best = 0;
+ for (t = 0; t < NELEMS (ia64_templ_desc); ++t)
+ {
+ goodness = 0;
+ if (match (t, i, 0))
+ {
+ if (match (t, j, 1))
+ {
+ if ((t == 2 && j == IA64_TYPE_X) || match (t, k, 2))
+ goodness = 3 + 3 + 3;
+ else
+ goodness = 3 + 3 + extra_goodness (t, 2);
+ }
+ else if (match (t, j, 2))
+ goodness = 3 + 3 + extra_goodness (t, 1);
+ else
+ {
+ goodness = 3;
+ goodness += extra_goodness (t, 1);
+ goodness += extra_goodness (t, 2);
+ }
+ }
+ else if (match (t, i, 1))
+ {
+ if ((t == 2 && i == IA64_TYPE_X) || match (t, j, 2))
+ goodness = 3 + 3;
+ else
+ goodness = 3 + extra_goodness (t, 2);
+ }
+ else if (match (t, i, 2))
+ goodness = 3 + extra_goodness (t, 1);
+
+ if (goodness > best)
+ {
+ best = goodness;
+ best_template[i][j][k] = t;
+ }
+ }
+ }
+
+#ifdef DEBUG_TEMPLATES
+ /* For debugging changes to the best_template calculations. We don't care
+ about combinations with invalid instructions, so start the loops at 1. */
+ for (i = 0; i < IA64_NUM_TYPES; ++i)
+ for (j = 0; j < IA64_NUM_TYPES; ++j)
+ for (k = 0; k < IA64_NUM_TYPES; ++k)
+ {
+ char type_letter[IA64_NUM_TYPES] = { 'n', 'a', 'i', 'm', 'b', 'f',
+ 'x', 'd' };
+ fprintf (stderr, "%c%c%c %s\n", type_letter[i], type_letter[j],
+ type_letter[k],
+ ia64_templ_desc[best_template[i][j][k]].name);
+ }
+#endif
+
+ for (i = 0; i < NUM_SLOTS; ++i)
+ md.slot[i].user_template = -1;
+
+ md.pseudo_hash = hash_new ();
+ for (i = 0; i < NELEMS (pseudo_opcode); ++i)
+ {
+ err = hash_insert (md.pseudo_hash, pseudo_opcode[i].name,
+ (void *) (pseudo_opcode + i));
+ if (err)
+ as_fatal (_("ia64.md_begin: can't hash `%s': %s"),
+ pseudo_opcode[i].name, err);
+ }
+
+ md.reg_hash = hash_new ();
+ md.dynreg_hash = hash_new ();
+ md.const_hash = hash_new ();
+ md.entry_hash = hash_new ();
+
+ /* general registers: */
+ declare_register_set ("r", 128, REG_GR);
+ declare_register ("gp", REG_GR + 1);
+ declare_register ("sp", REG_GR + 12);
+ declare_register ("tp", REG_GR + 13);
+ declare_register_set ("ret", 4, REG_GR + 8);
+
+ /* floating point registers: */
+ declare_register_set ("f", 128, REG_FR);
+ declare_register_set ("farg", 8, REG_FR + 8);
+ declare_register_set ("fret", 8, REG_FR + 8);
+
+ /* branch registers: */
+ declare_register_set ("b", 8, REG_BR);
+ declare_register ("rp", REG_BR + 0);
+
+ /* predicate registers: */
+ declare_register_set ("p", 64, REG_P);
+ declare_register ("pr", REG_PR);
+ declare_register ("pr.rot", REG_PR_ROT);
+
+ /* application registers: */
+ declare_register_set ("ar", 128, REG_AR);
+ for (i = 0; i < NELEMS (ar); ++i)
+ declare_register (ar[i].name, REG_AR + ar[i].regnum);
+
+ /* control registers: */
+ declare_register_set ("cr", 128, REG_CR);
+ for (i = 0; i < NELEMS (cr); ++i)
+ declare_register (cr[i].name, REG_CR + cr[i].regnum);
+
+ /* dahr registers: */
+ declare_register_set ("dahr", 8, REG_DAHR);
+
+ declare_register ("ip", REG_IP);
+ declare_register ("cfm", REG_CFM);
+ declare_register ("psr", REG_PSR);
+ declare_register ("psr.l", REG_PSR_L);
+ declare_register ("psr.um", REG_PSR_UM);
+
+ for (i = 0; i < NELEMS (indirect_reg); ++i)
+ {
+ unsigned int regnum = indirect_reg[i].regnum;
+
+ md.indregsym[regnum - IND_CPUID] = declare_register (indirect_reg[i].name, regnum);
+ }
+
+ /* pseudo-registers used to specify unwind info: */
+ declare_register ("psp", REG_PSP);
+
+ for (i = 0; i < NELEMS (const_bits); ++i)
+ {
+ err = hash_insert (md.const_hash, const_bits[i].name,
+ (void *) (const_bits + i));
+ if (err)
+ as_fatal (_("Inserting \"%s\" into constant hash table failed: %s"),
+ name, err);
+ }
+
+ /* Set the architecture and machine depending on defaults and command line
+ options. */
+ if (md.flags & EF_IA_64_ABI64)
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_ia64, bfd_mach_ia64_elf64);
+ else
+ ok = bfd_set_arch_mach (stdoutput, bfd_arch_ia64, bfd_mach_ia64_elf32);
+
+ if (! ok)
+ as_warn (_("Could not set architecture and machine"));
+
+ /* Set the pointer size and pointer shift size depending on md.flags */
+
+ if (md.flags & EF_IA_64_ABI64)
+ {
+ md.pointer_size = 8; /* pointers are 8 bytes */
+ md.pointer_size_shift = 3; /* alignment is 8 bytes = 2^2 */
+ }
+ else
+ {
+ md.pointer_size = 4; /* pointers are 4 bytes */
+ md.pointer_size_shift = 2; /* alignment is 4 bytes = 2^2 */
+ }
+
+ md.mem_offset.hint = 0;
+ md.path = 0;
+ md.maxpaths = 0;
+ md.entry_labels = NULL;
+}
+
+/* Set the default options in md. Cannot do this in md_begin because
+ that is called after md_parse_option which is where we set the
+ options in md based on command line options. */
+
+void
+ia64_init (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
+{
+ md.flags = MD_FLAGS_DEFAULT;
+#ifndef TE_VMS
+ /* Don't turn on dependency checking for VMS, doesn't work. */
+ md.detect_dv = 1;
+#endif
+ /* FIXME: We should change it to unwind_check_error someday. */
+ md.unwind_check = unwind_check_warning;
+ md.hint_b = hint_b_error;
+ md.tune = itanium2;
+}
+
+/* Return a string for the target object file format. */
+
+const char *
+ia64_target_format (void)
+{
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ if (md.flags & EF_IA_64_BE)
+ {
+ if (md.flags & EF_IA_64_ABI64)
+#if defined(TE_AIX50)
+ return "elf64-ia64-aix-big";
+#elif defined(TE_HPUX)
+ return "elf64-ia64-hpux-big";
+#else
+ return "elf64-ia64-big";
+#endif
+ else
+#if defined(TE_AIX50)
+ return "elf32-ia64-aix-big";
+#elif defined(TE_HPUX)
+ return "elf32-ia64-hpux-big";
+#else
+ return "elf32-ia64-big";
+#endif
+ }
+ else
+ {
+ if (md.flags & EF_IA_64_ABI64)
+#if defined (TE_AIX50)
+ return "elf64-ia64-aix-little";
+#elif defined (TE_VMS)
+ {
+ md.flags |= EF_IA_64_ARCHVER_1;
+ return "elf64-ia64-vms";
+ }
+#else
+ return "elf64-ia64-little";
+#endif
+ else
+#ifdef TE_AIX50
+ return "elf32-ia64-aix-little";
+#else
+ return "elf32-ia64-little";
+#endif
+ }
+ }
+ else
+ return "unknown-format";
+}
+
+void
+ia64_end_of_source (void)
+{
+ /* terminate insn group upon reaching end of file: */
+ insn_group_break (1, 0, 0);
+
+ /* emits slots we haven't written yet: */
+ ia64_flush_insns ();
+
+ bfd_set_private_flags (stdoutput, md.flags);
+
+ md.mem_offset.hint = 0;
+}
+
+void
+ia64_start_line (void)
+{
+ static int first;
+
+ if (!first) {
+ /* Make sure we don't reference input_line_pointer[-1] when that's
+ not valid. */
+ first = 1;
+ return;
+ }
+
+ if (md.qp.X_op == O_register)
+ as_bad (_("qualifying predicate not followed by instruction"));
+ md.qp.X_op = O_absent;
+
+ if (ignore_input ())
+ return;
+
+ if (input_line_pointer[0] == ';' && input_line_pointer[-1] == ';')
+ {
+ if (md.detect_dv && !md.explicit_mode)
+ {
+ static int warned;
+
+ if (!warned)
+ {
+ warned = 1;
+ as_warn (_("Explicit stops are ignored in auto mode"));
+ }
+ }
+ else
+ insn_group_break (1, 0, 0);
+ }
+ else if (input_line_pointer[-1] == '{')
+ {
+ if (md.manual_bundling)
+ as_warn (_("Found '{' when manual bundling is already turned on"));
+ else
+ CURR_SLOT.manual_bundling_on = 1;
+ md.manual_bundling = 1;
+
+ /* Bundling is only acceptable in explicit mode
+ or when in default automatic mode. */
+ if (md.detect_dv && !md.explicit_mode)
+ {
+ if (!md.mode_explicitly_set
+ && !md.default_explicit_mode)
+ dot_dv_mode ('E');
+ else
+ as_warn (_("Found '{' after explicit switch to automatic mode"));
+ }
+ }
+ else if (input_line_pointer[-1] == '}')
+ {
+ if (!md.manual_bundling)
+ as_warn (_("Found '}' when manual bundling is off"));
+ else
+ PREV_SLOT.manual_bundling_off = 1;
+ md.manual_bundling = 0;
+
+ /* switch back to automatic mode, if applicable */
+ if (md.detect_dv
+ && md.explicit_mode
+ && !md.mode_explicitly_set
+ && !md.default_explicit_mode)
+ dot_dv_mode ('A');
+ }
+}
+
+/* This is a hook for ia64_frob_label, so that it can distinguish tags from
+ labels. */
+static int defining_tag = 0;
+
+int
+ia64_unrecognized_line (int ch)
+{
+ switch (ch)
+ {
+ case '(':
+ expression_and_evaluate (&md.qp);
+ if (*input_line_pointer++ != ')')
+ {
+ as_bad (_("Expected ')'"));
+ return 0;
+ }
+ if (md.qp.X_op != O_register)
+ {
+ as_bad (_("Qualifying predicate expected"));
+ return 0;
+ }
+ if (md.qp.X_add_number < REG_P || md.qp.X_add_number >= REG_P + 64)
+ {
+ as_bad (_("Predicate register expected"));
+ return 0;
+ }
+ return 1;
+
+ case '[':
+ {
+ char *s;
+ char c;
+ symbolS *tag;
+ int temp;
+
+ if (md.qp.X_op == O_register)
+ {
+ as_bad (_("Tag must come before qualifying predicate."));
+ return 0;
+ }
+
+ /* This implements just enough of read_a_source_file in read.c to
+ recognize labels. */
+ if (is_name_beginner (*input_line_pointer))
+ {
+ s = input_line_pointer;
+ c = get_symbol_end ();
+ }
+ else if (LOCAL_LABELS_FB
+ && ISDIGIT (*input_line_pointer))
+ {
+ temp = 0;
+ while (ISDIGIT (*input_line_pointer))
+ temp = (temp * 10) + *input_line_pointer++ - '0';
+ fb_label_instance_inc (temp);
+ s = fb_label_name (temp, 0);
+ c = *input_line_pointer;
+ }
+ else
+ {
+ s = NULL;
+ c = '\0';
+ }
+ if (c != ':')
+ {
+ /* Put ':' back for error messages' sake. */
+ *input_line_pointer++ = ':';
+ as_bad (_("Expected ':'"));
+ return 0;
+ }
+
+ defining_tag = 1;
+ tag = colon (s);
+ defining_tag = 0;
+ /* Put ':' back for error messages' sake. */
+ *input_line_pointer++ = ':';
+ if (*input_line_pointer++ != ']')
+ {
+ as_bad (_("Expected ']'"));
+ return 0;
+ }
+ if (! tag)
+ {
+ as_bad (_("Tag name expected"));
+ return 0;
+ }
+ return 1;
+ }
+
+ default:
+ break;
+ }
+
+ /* Not a valid line. */
+ return 0;
+}
+
+void
+ia64_frob_label (struct symbol *sym)
+{
+ struct label_fix *fix;
+
+ /* Tags need special handling since they are not bundle breaks like
+ labels. */
+ if (defining_tag)
+ {
+ fix = obstack_alloc (&notes, sizeof (*fix));
+ fix->sym = sym;
+ fix->next = CURR_SLOT.tag_fixups;
+ fix->dw2_mark_labels = FALSE;
+ CURR_SLOT.tag_fixups = fix;
+
+ return;
+ }
+
+ if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
+ {
+ md.last_text_seg = now_seg;
+ fix = obstack_alloc (&notes, sizeof (*fix));
+ fix->sym = sym;
+ fix->next = CURR_SLOT.label_fixups;
+ fix->dw2_mark_labels = dwarf2_loc_mark_labels;
+ CURR_SLOT.label_fixups = fix;
+
+ /* Keep track of how many code entry points we've seen. */
+ if (md.path == md.maxpaths)
+ {
+ md.maxpaths += 20;
+ md.entry_labels = (const char **)
+ xrealloc ((void *) md.entry_labels,
+ md.maxpaths * sizeof (char *));
+ }
+ md.entry_labels[md.path++] = S_GET_NAME (sym);
+ }
+}
+
+#ifdef TE_HPUX
+/* The HP-UX linker will give unresolved symbol errors for symbols
+ that are declared but unused. This routine removes declared,
+ unused symbols from an object. */
+int
+ia64_frob_symbol (struct symbol *sym)
+{
+ if ((S_GET_SEGMENT (sym) == bfd_und_section_ptr && ! symbol_used_p (sym) &&
+ ELF_ST_VISIBILITY (S_GET_OTHER (sym)) == STV_DEFAULT)
+ || (S_GET_SEGMENT (sym) == bfd_abs_section_ptr
+ && ! S_IS_EXTERNAL (sym)))
+ return 1;
+ return 0;
+}
+#endif
+
+void
+ia64_flush_pending_output (void)
+{
+ if (!md.keep_pending_output
+ && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)
+ {
+ /* ??? This causes many unnecessary stop bits to be emitted.
+ Unfortunately, it isn't clear if it is safe to remove this. */
+ insn_group_break (1, 0, 0);
+ ia64_flush_insns ();
+ }
+}
+
+/* Do ia64-specific expression optimization. All that's done here is
+ to transform index expressions that are either due to the indexing
+ of rotating registers or due to the indexing of indirect register
+ sets. */
+int
+ia64_optimize_expr (expressionS *l, operatorT op, expressionS *r)
+{
+ if (op != O_index)
+ return 0;
+ resolve_expression (l);
+ if (l->X_op == O_register)
+ {
+ unsigned num_regs = l->X_add_number >> 16;
+
+ resolve_expression (r);
+ if (num_regs)
+ {
+ /* Left side is a .rotX-allocated register. */
+ if (r->X_op != O_constant)
+ {
+ as_bad (_("Rotating register index must be a non-negative constant"));
+ r->X_add_number = 0;
+ }
+ else if ((valueT) r->X_add_number >= num_regs)
+ {
+ as_bad (_("Index out of range 0..%u"), num_regs - 1);
+ r->X_add_number = 0;
+ }
+ l->X_add_number = (l->X_add_number & 0xffff) + r->X_add_number;
+ return 1;
+ }
+ else if (l->X_add_number >= IND_CPUID && l->X_add_number <= IND_RR)
+ {
+ if (r->X_op != O_register
+ || r->X_add_number < REG_GR
+ || r->X_add_number > REG_GR + 127)
+ {
+ as_bad (_("Indirect register index must be a general register"));
+ r->X_add_number = REG_GR;
+ }
+ l->X_op = O_index;
+ l->X_op_symbol = md.indregsym[l->X_add_number - IND_CPUID];
+ l->X_add_number = r->X_add_number;
+ return 1;
+ }
+ }
+ as_bad (_("Index can only be applied to rotating or indirect registers"));
+ /* Fall back to some register use of which has as little as possible
+ side effects, to minimize subsequent error messages. */
+ l->X_op = O_register;
+ l->X_add_number = REG_GR + 3;
+ return 1;
+}
+
+int
+ia64_parse_name (char *name, expressionS *e, char *nextcharP)
+{
+ struct const_desc *cdesc;
+ struct dynreg *dr = 0;
+ unsigned int idx;
+ struct symbol *sym;
+ char *end;
+
+ if (*name == '@')
+ {
+ enum pseudo_type pseudo_type = PSEUDO_FUNC_NONE;
+
+ /* Find what relocation pseudo-function we're dealing with. */
+ for (idx = 0; idx < NELEMS (pseudo_func); ++idx)
+ if (pseudo_func[idx].name
+ && pseudo_func[idx].name[0] == name[1]
+ && strcmp (pseudo_func[idx].name + 1, name + 2) == 0)
+ {
+ pseudo_type = pseudo_func[idx].type;
+ break;
+ }
+ switch (pseudo_type)
+ {
+ case PSEUDO_FUNC_RELOC:
+ end = input_line_pointer;
+ if (*nextcharP != '(')
+ {
+ as_bad (_("Expected '('"));
+ break;
+ }
+ /* Skip '('. */
+ ++input_line_pointer;
+ expression (e);
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("Missing ')'"));
+ goto done;
+ }
+ /* Skip ')'. */
+ ++input_line_pointer;
+#ifdef TE_VMS
+ if (idx == FUNC_SLOTCOUNT_RELOC)
+ {
+ /* @slotcount can accept any expression. Canonicalize. */
+ e->X_add_symbol = make_expr_symbol (e);
+ e->X_op = O_symbol;
+ e->X_add_number = 0;
+ }
+#endif
+ if (e->X_op != O_symbol)
+ {
+ if (e->X_op != O_pseudo_fixup)
+ {
+ as_bad (_("Not a symbolic expression"));
+ goto done;
+ }
+ if (idx != FUNC_LT_RELATIVE)
+ {
+ as_bad (_("Illegal combination of relocation functions"));
+ goto done;
+ }
+ switch (S_GET_VALUE (e->X_op_symbol))
+ {
+ case FUNC_FPTR_RELATIVE:
+ idx = FUNC_LT_FPTR_RELATIVE; break;
+ case FUNC_DTP_MODULE:
+ idx = FUNC_LT_DTP_MODULE; break;
+ case FUNC_DTP_RELATIVE:
+ idx = FUNC_LT_DTP_RELATIVE; break;
+ case FUNC_TP_RELATIVE:
+ idx = FUNC_LT_TP_RELATIVE; break;
+ default:
+ as_bad (_("Illegal combination of relocation functions"));
+ goto done;
+ }
+ }
+ /* Make sure gas doesn't get rid of local symbols that are used
+ in relocs. */
+ e->X_op = O_pseudo_fixup;
+ e->X_op_symbol = pseudo_func[idx].u.sym;
+ done:
+ *nextcharP = *input_line_pointer;
+ break;
+
+ case PSEUDO_FUNC_CONST:
+ e->X_op = O_constant;
+ e->X_add_number = pseudo_func[idx].u.ival;
+ break;
+
+ case PSEUDO_FUNC_REG:
+ e->X_op = O_register;
+ e->X_add_number = pseudo_func[idx].u.ival;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+ }
+
+ /* first see if NAME is a known register name: */
+ sym = hash_find (md.reg_hash, name);
+ if (sym)
+ {
+ e->X_op = O_register;
+ e->X_add_number = S_GET_VALUE (sym);
+ return 1;
+ }
+
+ cdesc = hash_find (md.const_hash, name);
+ if (cdesc)
+ {
+ e->X_op = O_constant;
+ e->X_add_number = cdesc->value;
+ return 1;
+ }
+
+ /* check for inN, locN, or outN: */
+ idx = 0;
+ switch (name[0])
+ {
+ case 'i':
+ if (name[1] == 'n' && ISDIGIT (name[2]))
+ {
+ dr = &md.in;
+ idx = 2;
+ }
+ break;
+
+ case 'l':
+ if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3]))
+ {
+ dr = &md.loc;
+ idx = 3;
+ }
+ break;
+
+ case 'o':
+ if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3]))
+ {
+ dr = &md.out;
+ idx = 3;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Ignore register numbers with leading zeroes, except zero itself. */
+ if (dr && (name[idx] != '0' || name[idx + 1] == '\0'))
+ {
+ unsigned long regnum;
+
+ /* The name is inN, locN, or outN; parse the register number. */
+ regnum = strtoul (name + idx, &end, 10);
+ if (end > name + idx && *end == '\0' && regnum < 96)
+ {
+ if (regnum >= dr->num_regs)
+ {
+ if (!dr->num_regs)
+ as_bad (_("No current frame"));
+ else
+ as_bad (_("Register number out of range 0..%u"),
+ dr->num_regs - 1);
+ regnum = 0;
+ }
+ e->X_op = O_register;
+ e->X_add_number = dr->base + regnum;
+ return 1;
+ }
+ }
+
+ end = alloca (strlen (name) + 1);
+ strcpy (end, name);
+ name = ia64_canonicalize_symbol_name (end);
+ if ((dr = hash_find (md.dynreg_hash, name)))
+ {
+ /* We've got ourselves the name of a rotating register set.
+ Store the base register number in the low 16 bits of
+ X_add_number and the size of the register set in the top 16
+ bits. */
+ e->X_op = O_register;
+ e->X_add_number = dr->base | (dr->num_regs << 16);
+ return 1;
+ }
+ return 0;
+}
+
+/* Remove the '#' suffix that indicates a symbol as opposed to a register. */
+
+char *
+ia64_canonicalize_symbol_name (char *name)
+{
+ size_t len = strlen (name), full = len;
+
+ while (len > 0 && name[len - 1] == '#')
+ --len;
+ if (len <= 0)
+ {
+ if (full > 0)
+ as_bad (_("Standalone `#' is illegal"));
+ }
+ else if (len < full - 1)
+ as_warn (_("Redundant `#' suffix operators"));
+ name[len] = '\0';
+ return name;
+}
+
+/* Return true if idesc is a conditional branch instruction. This excludes
+ the modulo scheduled branches, and br.ia. Mod-sched branches are excluded
+ because they always read/write resources regardless of the value of the
+ qualifying predicate. br.ia must always use p0, and hence is always
+ taken. Thus this function returns true for branches which can fall
+ through, and which use no resources if they do fall through. */
+
+static int
+is_conditional_branch (struct ia64_opcode *idesc)
+{
+ /* br is a conditional branch. Everything that starts with br. except
+ br.ia, br.c{loop,top,exit}, and br.w{top,exit} is a conditional branch.
+ Everything that starts with brl is a conditional branch. */
+ return (idesc->name[0] == 'b' && idesc->name[1] == 'r'
+ && (idesc->name[2] == '\0'
+ || (idesc->name[2] == '.' && idesc->name[3] != 'i'
+ && idesc->name[3] != 'c' && idesc->name[3] != 'w')
+ || idesc->name[2] == 'l'
+ /* br.cond, br.call, br.clr */
+ || (idesc->name[2] == '.' && idesc->name[3] == 'c'
+ && (idesc->name[4] == 'a' || idesc->name[4] == 'o'
+ || (idesc->name[4] == 'l' && idesc->name[5] == 'r')))));
+}
+
+/* Return whether the given opcode is a taken branch. If there's any doubt,
+ returns zero. */
+
+static int
+is_taken_branch (struct ia64_opcode *idesc)
+{
+ return ((is_conditional_branch (idesc) && CURR_SLOT.qp_regno == 0)
+ || strncmp (idesc->name, "br.ia", 5) == 0);
+}
+
+/* Return whether the given opcode is an interruption or rfi. If there's any
+ doubt, returns zero. */
+
+static int
+is_interruption_or_rfi (struct ia64_opcode *idesc)
+{
+ if (strcmp (idesc->name, "rfi") == 0)
+ return 1;
+ return 0;
+}
+
+/* Returns the index of the given dependency in the opcode's list of chks, or
+ -1 if there is no dependency. */
+
+static int
+depends_on (int depind, struct ia64_opcode *idesc)
+{
+ int i;
+ const struct ia64_opcode_dependency *dep = idesc->dependencies;
+ for (i = 0; i < dep->nchks; i++)
+ {
+ if (depind == DEP (dep->chks[i]))
+ return i;
+ }
+ return -1;
+}
+
+/* Determine a set of specific resources used for a particular resource
+ class. Returns the number of specific resources identified For those
+ cases which are not determinable statically, the resource returned is
+ marked nonspecific.
+
+ Meanings of value in 'NOTE':
+ 1) only read/write when the register number is explicitly encoded in the
+ insn.
+ 2) only read CFM when accessing a rotating GR, FR, or PR. mov pr only
+ accesses CFM when qualifying predicate is in the rotating region.
+ 3) general register value is used to specify an indirect register; not
+ determinable statically.
+ 4) only read the given resource when bits 7:0 of the indirect index
+ register value does not match the register number of the resource; not
+ determinable statically.
+ 5) all rules are implementation specific.
+ 6) only when both the index specified by the reader and the index specified
+ by the writer have the same value in bits 63:61; not determinable
+ statically.
+ 7) only access the specified resource when the corresponding mask bit is
+ set
+ 8) PSR.dfh is only read when these insns reference FR32-127. PSR.dfl is
+ only read when these insns reference FR2-31
+ 9) PSR.mfl is only written when these insns write FR2-31. PSR.mfh is only
+ written when these insns write FR32-127
+ 10) The PSR.bn bit is only accessed when one of GR16-31 is specified in the
+ instruction
+ 11) The target predicates are written independently of PR[qp], but source
+ registers are only read if PR[qp] is true. Since the state of PR[qp]
+ cannot statically be determined, all source registers are marked used.
+ 12) This insn only reads the specified predicate register when that
+ register is the PR[qp].
+ 13) This reference to ld-c only applies to the GR whose value is loaded
+ with data returned from memory, not the post-incremented address register.
+ 14) The RSE resource includes the implementation-specific RSE internal
+ state resources. At least one (and possibly more) of these resources are
+ read by each instruction listed in IC:rse-readers. At least one (and
+ possibly more) of these resources are written by each insn listed in
+ IC:rse-writers.
+ 15+16) Represents reserved instructions, which the assembler does not
+ generate.
+ 17) CR[TPR] has a RAW dependency only between mov-to-CR-TPR and
+ mov-to-PSR-l or ssm instructions that set PSR.i, PSR.pp or PSR.up.
+
+ Memory resources (i.e. locations in memory) are *not* marked or tracked by
+ this code; there are no dependency violations based on memory access.
+*/
+
+#define MAX_SPECS 256
+#define DV_CHK 1
+#define DV_REG 0
+
+static int
+specify_resource (const struct ia64_dependency *dep,
+ struct ia64_opcode *idesc,
+ /* is this a DV chk or a DV reg? */
+ int type,
+ /* returned specific resources */
+ struct rsrc specs[MAX_SPECS],
+ /* resource note for this insn's usage */
+ int note,
+ /* which execution path to examine */
+ int path)
+{
+ int count = 0;
+ int i;
+ int rsrc_write = 0;
+ struct rsrc tmpl;
+
+ if (dep->mode == IA64_DV_WAW
+ || (dep->mode == IA64_DV_RAW && type == DV_REG)
+ || (dep->mode == IA64_DV_WAR && type == DV_CHK))
+ rsrc_write = 1;
+
+ /* template for any resources we identify */
+ tmpl.dependency = dep;
+ tmpl.note = note;
+ tmpl.insn_srlz = tmpl.data_srlz = 0;
+ tmpl.qp_regno = CURR_SLOT.qp_regno;
+ tmpl.link_to_qp_branch = 1;
+ tmpl.mem_offset.hint = 0;
+ tmpl.mem_offset.offset = 0;
+ tmpl.mem_offset.base = 0;
+ tmpl.specific = 1;
+ tmpl.index = -1;
+ tmpl.cmp_type = CMP_NONE;
+ tmpl.depind = 0;
+ tmpl.file = NULL;
+ tmpl.line = 0;
+ tmpl.path = 0;
+
+#define UNHANDLED \
+as_warn (_("Unhandled dependency %s for %s (%s), note %d"), \
+dep->name, idesc->name, (rsrc_write?"write":"read"), note)
+#define KNOWN(REG) (gr_values[REG].known && gr_values[REG].path >= path)
+
+ /* we don't need to track these */
+ if (dep->semantics == IA64_DVS_NONE)
+ return 0;
+
+ switch (dep->specifier)
+ {
+ case IA64_RS_AR_K:
+ if (note == 1)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_AR3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_AR;
+ if (regno >= 0 && regno <= 7)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ }
+ else if (note == 0)
+ {
+ for (i = 0; i < 8; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_AR_UNAT:
+ /* This is a mov =AR or mov AR= instruction. */
+ if (idesc->operands[!rsrc_write] == IA64_OPND_AR3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_AR;
+ if (regno == AR_UNAT)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ /* This is a spill/fill, or other instruction that modifies the
+ unat register. */
+
+ /* Unless we can determine the specific bits used, mark the whole
+ thing; bits 8:3 of the memory address indicate the bit used in
+ UNAT. The .mem.offset hint may be used to eliminate a small
+ subset of conflicts. */
+ specs[count] = tmpl;
+ if (md.mem_offset.hint)
+ {
+ if (md.debug_dv)
+ fprintf (stderr, " Using hint for spill/fill\n");
+ /* The index isn't actually used, just set it to something
+ approximating the bit index. */
+ specs[count].index = (md.mem_offset.offset >> 3) & 0x3F;
+ specs[count].mem_offset.hint = 1;
+ specs[count].mem_offset.offset = md.mem_offset.offset;
+ specs[count++].mem_offset.base = md.mem_offset.base;
+ }
+ else
+ {
+ specs[count++].specific = 0;
+ }
+ }
+ break;
+
+ case IA64_RS_AR:
+ if (note == 1)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_AR3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_AR;
+ if ((regno >= 8 && regno <= 15)
+ || (regno >= 20 && regno <= 23)
+ || (regno >= 31 && regno <= 39)
+ || (regno >= 41 && regno <= 47)
+ || (regno >= 67 && regno <= 111))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_ARb:
+ if (note == 1)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_AR3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_AR;
+ if ((regno >= 48 && regno <= 63)
+ || (regno >= 112 && regno <= 127))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ }
+ else if (note == 0)
+ {
+ for (i = 48; i < 64; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ for (i = 112; i < 128; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_BR:
+ if (note != 1)
+ {
+ UNHANDLED;
+ }
+ else
+ {
+ if (rsrc_write)
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ if (idesc->operands[i] == IA64_OPND_B1
+ || idesc->operands[i] == IA64_OPND_B2)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[i].X_add_number - REG_BR;
+ }
+ }
+ else
+ {
+ for (i = idesc->num_outputs; i < NELEMS (idesc->operands); i++)
+ if (idesc->operands[i] == IA64_OPND_B1
+ || idesc->operands[i] == IA64_OPND_B2)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[i].X_add_number - REG_BR;
+ }
+ }
+ }
+ break;
+
+ case IA64_RS_CPUID: /* four or more registers */
+ if (note == 3)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_CPUID_R3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = gr_values[regno].value & 0xFF;
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_DBR: /* four or more registers */
+ if (note == 3)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_DBR_R3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = gr_values[regno].value & 0xFF;
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else if (note == 0 && !rsrc_write)
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_IBR: /* four or more registers */
+ if (note == 3)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_IBR_R3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = gr_values[regno].value & 0xFF;
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_MSR:
+ if (note == 5)
+ {
+ /* These are implementation specific. Force all references to
+ conflict with all other references. */
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_PKR: /* 16 or more registers */
+ if (note == 3 || note == 4)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_PKR_R3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ if (note == 3)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = gr_values[regno].value & 0xFF;
+ }
+ else
+ for (i = 0; i < NELEMS (gr_values); i++)
+ {
+ /* Uses all registers *except* the one in R3. */
+ if ((unsigned)i != (gr_values[regno].value & 0xFF))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else if (note == 0)
+ {
+ /* probe et al. */
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ break;
+
+ case IA64_RS_PMC: /* four or more registers */
+ if (note == 3)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_PMC_R3
+ || (!rsrc_write && idesc->operands[1] == IA64_OPND_PMD_R3))
+
+ {
+ int reg_index = ((idesc->operands[1] == IA64_OPND_R3 && !rsrc_write)
+ ? 1 : !rsrc_write);
+ int regno = CURR_SLOT.opnd[reg_index].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = gr_values[regno].value & 0xFF;
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_PMD: /* four or more registers */
+ if (note == 3)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_PMD_R3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = gr_values[regno].value & 0xFF;
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_RR: /* eight registers */
+ if (note == 6)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_RR_R3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_GR;
+ if (regno >= 0 && regno < NELEMS (gr_values)
+ && KNOWN (regno))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = (gr_values[regno].value >> 61) & 0x7;
+ }
+ else
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ else if (note == 0 && !rsrc_write)
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_CR_IRR:
+ if (note == 0)
+ {
+ /* handle mov-from-CR-IVR; it's a read that writes CR[IRR] */
+ int regno = CURR_SLOT.opnd[1].X_add_number - REG_CR;
+ if (rsrc_write
+ && idesc->operands[1] == IA64_OPND_CR3
+ && regno == CR_IVR)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CR_IRR0 + i;
+ }
+ }
+ }
+ else if (note == 1)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_CR;
+ if (idesc->operands[!rsrc_write] == IA64_OPND_CR3
+ && regno >= CR_IRR0
+ && regno <= CR_IRR3)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_CR_IIB:
+ if (note != 0)
+ {
+ UNHANDLED;
+ }
+ else
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_CR;
+ if (idesc->operands[!rsrc_write] == IA64_OPND_CR3
+ && (regno == CR_IIB0 || regno == CR_IIB1))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ break;
+
+ case IA64_RS_CR_LRR:
+ if (note != 1)
+ {
+ UNHANDLED;
+ }
+ else
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_CR;
+ if (idesc->operands[!rsrc_write] == IA64_OPND_CR3
+ && (regno == CR_LRR0 || regno == CR_LRR1))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ break;
+
+ case IA64_RS_CR:
+ if (note == 1)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_CR3)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_CR;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_DAHR:
+ if (note == 0)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_DAHR3)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_DAHR;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_FR:
+ case IA64_RS_FRb:
+ if (note != 1)
+ {
+ UNHANDLED;
+ }
+ else if (rsrc_write)
+ {
+ if (dep->specifier == IA64_RS_FRb
+ && idesc->operands[0] == IA64_OPND_F1)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.opnd[0].X_add_number - REG_FR;
+ }
+ }
+ else
+ {
+ for (i = idesc->num_outputs; i < NELEMS (idesc->operands); i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_F2
+ || idesc->operands[i] == IA64_OPND_F3
+ || idesc->operands[i] == IA64_OPND_F4)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[i].X_add_number - REG_FR;
+ }
+ }
+ }
+ break;
+
+ case IA64_RS_GR:
+ if (note == 13)
+ {
+ /* This reference applies only to the GR whose value is loaded with
+ data returned from memory. */
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.opnd[0].X_add_number - REG_GR;
+ }
+ else if (note == 1)
+ {
+ if (rsrc_write)
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ if (idesc->operands[i] == IA64_OPND_R1
+ || idesc->operands[i] == IA64_OPND_R2
+ || idesc->operands[i] == IA64_OPND_R3)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ }
+ if (idesc->flags & IA64_OPCODE_POSTINC)
+ for (i = 0; i < NELEMS (idesc->operands); i++)
+ if (idesc->operands[i] == IA64_OPND_MR3)
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ }
+ }
+ else
+ {
+ /* Look for anything that reads a GR. */
+ for (i = 0; i < NELEMS (idesc->operands); i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_MR3
+ || idesc->operands[i] == IA64_OPND_CPUID_R3
+ || idesc->operands[i] == IA64_OPND_DBR_R3
+ || idesc->operands[i] == IA64_OPND_IBR_R3
+ || idesc->operands[i] == IA64_OPND_MSR_R3
+ || idesc->operands[i] == IA64_OPND_PKR_R3
+ || idesc->operands[i] == IA64_OPND_PMC_R3
+ || idesc->operands[i] == IA64_OPND_PMD_R3
+ || idesc->operands[i] == IA64_OPND_DAHR_R3
+ || idesc->operands[i] == IA64_OPND_RR_R3
+ || ((i >= idesc->num_outputs)
+ && (idesc->operands[i] == IA64_OPND_R1
+ || idesc->operands[i] == IA64_OPND_R2
+ || idesc->operands[i] == IA64_OPND_R3
+ /* addl source register. */
+ || idesc->operands[i] == IA64_OPND_R3_2)))
+ {
+ specs[count] = tmpl;
+ specs[count++].index =
+ CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ /* This is the same as IA64_RS_PRr, except that the register range is
+ from 1 - 15, and there are no rotating register reads/writes here. */
+ case IA64_RS_PR:
+ if (note == 0)
+ {
+ for (i = 1; i < 16; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else if (note == 7)
+ {
+ valueT mask = 0;
+ /* Mark only those registers indicated by the mask. */
+ if (rsrc_write)
+ {
+ mask = CURR_SLOT.opnd[2].X_add_number;
+ for (i = 1; i < 16; i++)
+ if (mask & ((valueT) 1 << i))
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ }
+ else if (note == 11) /* note 11 implies note 1 as well */
+ {
+ if (rsrc_write)
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_P1
+ || idesc->operands[i] == IA64_OPND_P2)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_P;
+ if (regno >= 1 && regno < 16)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ }
+ else if (note == 12)
+ {
+ if (CURR_SLOT.qp_regno >= 1 && CURR_SLOT.qp_regno < 16)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.qp_regno;
+ }
+ }
+ else if (note == 1)
+ {
+ if (rsrc_write)
+ {
+ int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+ int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
+ int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
+
+ if ((idesc->operands[0] == IA64_OPND_P1
+ || idesc->operands[0] == IA64_OPND_P2)
+ && p1 >= 1 && p1 < 16)
+ {
+ specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
+ specs[count++].index = p1;
+ }
+ if ((idesc->operands[1] == IA64_OPND_P1
+ || idesc->operands[1] == IA64_OPND_P2)
+ && p2 >= 1 && p2 < 16)
+ {
+ specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
+ specs[count++].index = p2;
+ }
+ }
+ else
+ {
+ if (CURR_SLOT.qp_regno >= 1 && CURR_SLOT.qp_regno < 16)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.qp_regno;
+ }
+ if (idesc->operands[1] == IA64_OPND_PR)
+ {
+ for (i = 1; i < 16; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ /* This is the general case for PRs. IA64_RS_PR and IA64_RS_PR63 are
+ simplified cases of this. */
+ case IA64_RS_PRr:
+ if (note == 0)
+ {
+ for (i = 16; i < 63; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else if (note == 7)
+ {
+ valueT mask = 0;
+ /* Mark only those registers indicated by the mask. */
+ if (rsrc_write
+ && idesc->operands[0] == IA64_OPND_PR)
+ {
+ mask = CURR_SLOT.opnd[2].X_add_number;
+ if (mask & ((valueT) 1 << 16))
+ for (i = 16; i < 63; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else if (rsrc_write
+ && idesc->operands[0] == IA64_OPND_PR_ROT)
+ {
+ for (i = 16; i < 63; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ }
+ else if (note == 11) /* note 11 implies note 1 as well */
+ {
+ if (rsrc_write)
+ {
+ for (i = 0; i < idesc->num_outputs; i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_P1
+ || idesc->operands[i] == IA64_OPND_P2)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_P;
+ if (regno >= 16 && regno < 63)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = regno;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ }
+ else if (note == 12)
+ {
+ if (CURR_SLOT.qp_regno >= 16 && CURR_SLOT.qp_regno < 63)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.qp_regno;
+ }
+ }
+ else if (note == 1)
+ {
+ if (rsrc_write)
+ {
+ int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+ int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
+ int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
+
+ if ((idesc->operands[0] == IA64_OPND_P1
+ || idesc->operands[0] == IA64_OPND_P2)
+ && p1 >= 16 && p1 < 63)
+ {
+ specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
+ specs[count++].index = p1;
+ }
+ if ((idesc->operands[1] == IA64_OPND_P1
+ || idesc->operands[1] == IA64_OPND_P2)
+ && p2 >= 16 && p2 < 63)
+ {
+ specs[count] = tmpl;
+ specs[count].cmp_type =
+ (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
+ specs[count++].index = p2;
+ }
+ }
+ else
+ {
+ if (CURR_SLOT.qp_regno >= 16 && CURR_SLOT.qp_regno < 63)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = CURR_SLOT.qp_regno;
+ }
+ if (idesc->operands[1] == IA64_OPND_PR)
+ {
+ for (i = 16; i < 63; i++)
+ {
+ specs[count] = tmpl;
+ specs[count++].index = i;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_PSR:
+ /* Verify that the instruction is using the PSR bit indicated in
+ dep->regindex. */
+ if (note == 0)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_PSR_UM)
+ {
+ if (dep->regindex < 6)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else if (idesc->operands[!rsrc_write] == IA64_OPND_PSR)
+ {
+ if (dep->regindex < 32
+ || dep->regindex == 35
+ || dep->regindex == 36
+ || (!rsrc_write && dep->regindex == PSR_CPL))
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else if (idesc->operands[!rsrc_write] == IA64_OPND_PSR_L)
+ {
+ if (dep->regindex < 32
+ || dep->regindex == 35
+ || dep->regindex == 36
+ || (rsrc_write && dep->regindex == PSR_CPL))
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ /* Several PSR bits have very specific dependencies. */
+ switch (dep->regindex)
+ {
+ default:
+ specs[count++] = tmpl;
+ break;
+ case PSR_IC:
+ if (rsrc_write)
+ {
+ specs[count++] = tmpl;
+ }
+ else
+ {
+ /* Only certain CR accesses use PSR.ic */
+ if (idesc->operands[0] == IA64_OPND_CR3
+ || idesc->operands[1] == IA64_OPND_CR3)
+ {
+ int reg_index =
+ ((idesc->operands[0] == IA64_OPND_CR3)
+ ? 0 : 1);
+ int regno =
+ CURR_SLOT.opnd[reg_index].X_add_number - REG_CR;
+
+ switch (regno)
+ {
+ default:
+ break;
+ case CR_ITIR:
+ case CR_IFS:
+ case CR_IIM:
+ case CR_IIP:
+ case CR_IPSR:
+ case CR_ISR:
+ case CR_IFA:
+ case CR_IHA:
+ case CR_IIB0:
+ case CR_IIB1:
+ case CR_IIPA:
+ specs[count++] = tmpl;
+ break;
+ }
+ }
+ }
+ break;
+ case PSR_CPL:
+ if (rsrc_write)
+ {
+ specs[count++] = tmpl;
+ }
+ else
+ {
+ /* Only some AR accesses use cpl */
+ if (idesc->operands[0] == IA64_OPND_AR3
+ || idesc->operands[1] == IA64_OPND_AR3)
+ {
+ int reg_index =
+ ((idesc->operands[0] == IA64_OPND_AR3)
+ ? 0 : 1);
+ int regno =
+ CURR_SLOT.opnd[reg_index].X_add_number - REG_AR;
+
+ if (regno == AR_ITC
+ || regno == AR_RUC
+ || (reg_index == 0
+ && (regno == AR_RSC
+ || (regno >= AR_K0
+ && regno <= AR_K7))))
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ specs[count++] = tmpl;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else if (note == 7)
+ {
+ valueT mask = 0;
+ if (idesc->operands[0] == IA64_OPND_IMMU24)
+ {
+ mask = CURR_SLOT.opnd[0].X_add_number;
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ if (mask & ((valueT) 1 << dep->regindex))
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else if (note == 8)
+ {
+ int min = dep->regindex == PSR_DFL ? 2 : 32;
+ int max = dep->regindex == PSR_DFL ? 31 : 127;
+ /* dfh is read on FR32-127; dfl is read on FR2-31 */
+ for (i = 0; i < NELEMS (idesc->operands); i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_F1
+ || idesc->operands[i] == IA64_OPND_F2
+ || idesc->operands[i] == IA64_OPND_F3
+ || idesc->operands[i] == IA64_OPND_F4)
+ {
+ int reg = CURR_SLOT.opnd[i].X_add_number - REG_FR;
+ if (reg >= min && reg <= max)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ }
+ }
+ else if (note == 9)
+ {
+ int min = dep->regindex == PSR_MFL ? 2 : 32;
+ int max = dep->regindex == PSR_MFL ? 31 : 127;
+ /* mfh is read on writes to FR32-127; mfl is read on writes to
+ FR2-31 */
+ for (i = 0; i < idesc->num_outputs; i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_F1)
+ {
+ int reg = CURR_SLOT.opnd[i].X_add_number - REG_FR;
+ if (reg >= min && reg <= max)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ }
+ }
+ else if (note == 10)
+ {
+ for (i = 0; i < NELEMS (idesc->operands); i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_R1
+ || idesc->operands[i] == IA64_OPND_R2
+ || idesc->operands[i] == IA64_OPND_R3)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ if (regno >= 16 && regno <= 31)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_AR_FPSR:
+ if (idesc->operands[!rsrc_write] == IA64_OPND_AR3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_AR;
+ if (regno == AR_FPSR)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ specs[count++] = tmpl;
+ }
+ break;
+
+ case IA64_RS_ARX:
+ /* Handle all AR[REG] resources */
+ if (note == 0 || note == 1)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_AR;
+ if (idesc->operands[!rsrc_write] == IA64_OPND_AR3
+ && regno == dep->regindex)
+ {
+ specs[count++] = tmpl;
+ }
+ /* other AR[REG] resources may be affected by AR accesses */
+ else if (idesc->operands[0] == IA64_OPND_AR3)
+ {
+ /* AR[] writes */
+ regno = CURR_SLOT.opnd[0].X_add_number - REG_AR;
+ switch (dep->regindex)
+ {
+ default:
+ break;
+ case AR_BSP:
+ case AR_RNAT:
+ if (regno == AR_BSPSTORE)
+ {
+ specs[count++] = tmpl;
+ }
+ case AR_RSC:
+ if (!rsrc_write &&
+ (regno == AR_BSPSTORE
+ || regno == AR_RNAT))
+ {
+ specs[count++] = tmpl;
+ }
+ break;
+ }
+ }
+ else if (idesc->operands[1] == IA64_OPND_AR3)
+ {
+ /* AR[] reads */
+ regno = CURR_SLOT.opnd[1].X_add_number - REG_AR;
+ switch (dep->regindex)
+ {
+ default:
+ break;
+ case AR_RSC:
+ if (regno == AR_BSPSTORE || regno == AR_RNAT)
+ {
+ specs[count++] = tmpl;
+ }
+ break;
+ }
+ }
+ else
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_CRX:
+ /* Handle all CR[REG] resources.
+ ??? FIXME: The rule 17 isn't really handled correctly. */
+ if (note == 0 || note == 1 || note == 17)
+ {
+ if (idesc->operands[!rsrc_write] == IA64_OPND_CR3)
+ {
+ int regno = CURR_SLOT.opnd[!rsrc_write].X_add_number - REG_CR;
+ if (regno == dep->regindex)
+ {
+ specs[count++] = tmpl;
+ }
+ else if (!rsrc_write)
+ {
+ /* Reads from CR[IVR] affect other resources. */
+ if (regno == CR_IVR)
+ {
+ if ((dep->regindex >= CR_IRR0
+ && dep->regindex <= CR_IRR3)
+ || dep->regindex == CR_TPR)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ }
+ }
+ else
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_INSERVICE:
+ /* look for write of EOI (67) or read of IVR (65) */
+ if ((idesc->operands[0] == IA64_OPND_CR3
+ && CURR_SLOT.opnd[0].X_add_number - REG_CR == CR_EOI)
+ || (idesc->operands[1] == IA64_OPND_CR3
+ && CURR_SLOT.opnd[1].X_add_number - REG_CR == CR_IVR))
+ {
+ specs[count++] = tmpl;
+ }
+ break;
+
+ case IA64_RS_GR0:
+ if (note == 1)
+ {
+ specs[count++] = tmpl;
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_CFM:
+ if (note != 2)
+ {
+ specs[count++] = tmpl;
+ }
+ else
+ {
+ /* Check if any of the registers accessed are in the rotating region.
+ mov to/from pr accesses CFM only when qp_regno is in the rotating
+ region */
+ for (i = 0; i < NELEMS (idesc->operands); i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_R1
+ || idesc->operands[i] == IA64_OPND_R2
+ || idesc->operands[i] == IA64_OPND_R3)
+ {
+ int num = CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ /* Assumes that md.rot.num_regs is always valid */
+ if (md.rot.num_regs > 0
+ && num > 31
+ && num < 31 + md.rot.num_regs)
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ else if (idesc->operands[i] == IA64_OPND_F1
+ || idesc->operands[i] == IA64_OPND_F2
+ || idesc->operands[i] == IA64_OPND_F3
+ || idesc->operands[i] == IA64_OPND_F4)
+ {
+ int num = CURR_SLOT.opnd[i].X_add_number - REG_FR;
+ if (num > 31)
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ else if (idesc->operands[i] == IA64_OPND_P1
+ || idesc->operands[i] == IA64_OPND_P2)
+ {
+ int num = CURR_SLOT.opnd[i].X_add_number - REG_P;
+ if (num > 15)
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ }
+ if (CURR_SLOT.qp_regno > 15)
+ {
+ specs[count] = tmpl;
+ specs[count++].specific = 0;
+ }
+ }
+ break;
+
+ /* This is the same as IA64_RS_PRr, except simplified to account for
+ the fact that there is only one register. */
+ case IA64_RS_PR63:
+ if (note == 0)
+ {
+ specs[count++] = tmpl;
+ }
+ else if (note == 7)
+ {
+ valueT mask = 0;
+ if (idesc->operands[2] == IA64_OPND_IMM17)
+ mask = CURR_SLOT.opnd[2].X_add_number;
+ if (mask & ((valueT) 1 << 63))
+ specs[count++] = tmpl;
+ }
+ else if (note == 11)
+ {
+ if ((idesc->operands[0] == IA64_OPND_P1
+ && CURR_SLOT.opnd[0].X_add_number - REG_P == 63)
+ || (idesc->operands[1] == IA64_OPND_P2
+ && CURR_SLOT.opnd[1].X_add_number - REG_P == 63))
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else if (note == 12)
+ {
+ if (CURR_SLOT.qp_regno == 63)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else if (note == 1)
+ {
+ if (rsrc_write)
+ {
+ int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+ int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ int or_andcm = strstr (idesc->name, "or.andcm") != NULL;
+ int and_orcm = strstr (idesc->name, "and.orcm") != NULL;
+
+ if (p1 == 63
+ && (idesc->operands[0] == IA64_OPND_P1
+ || idesc->operands[0] == IA64_OPND_P2))
+ {
+ specs[count] = tmpl;
+ specs[count++].cmp_type =
+ (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE));
+ }
+ if (p2 == 63
+ && (idesc->operands[1] == IA64_OPND_P1
+ || idesc->operands[1] == IA64_OPND_P2))
+ {
+ specs[count] = tmpl;
+ specs[count++].cmp_type =
+ (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE));
+ }
+ }
+ else
+ {
+ if (CURR_SLOT.qp_regno == 63)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ }
+ else
+ {
+ UNHANDLED;
+ }
+ break;
+
+ case IA64_RS_RSE:
+ /* FIXME we can identify some individual RSE written resources, but RSE
+ read resources have not yet been completely identified, so for now
+ treat RSE as a single resource */
+ if (strncmp (idesc->name, "mov", 3) == 0)
+ {
+ if (rsrc_write)
+ {
+ if (idesc->operands[0] == IA64_OPND_AR3
+ && CURR_SLOT.opnd[0].X_add_number - REG_AR == AR_BSPSTORE)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else
+ {
+ if (idesc->operands[0] == IA64_OPND_AR3)
+ {
+ if (CURR_SLOT.opnd[0].X_add_number - REG_AR == AR_BSPSTORE
+ || CURR_SLOT.opnd[0].X_add_number - REG_AR == AR_RNAT)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ else if (idesc->operands[1] == IA64_OPND_AR3)
+ {
+ if (CURR_SLOT.opnd[1].X_add_number - REG_AR == AR_BSP
+ || CURR_SLOT.opnd[1].X_add_number - REG_AR == AR_BSPSTORE
+ || CURR_SLOT.opnd[1].X_add_number - REG_AR == AR_RNAT)
+ {
+ specs[count++] = tmpl;
+ }
+ }
+ }
+ }
+ else
+ {
+ specs[count++] = tmpl;
+ }
+ break;
+
+ case IA64_RS_ANY:
+ /* FIXME -- do any of these need to be non-specific? */
+ specs[count++] = tmpl;
+ break;
+
+ default:
+ as_bad (_("Unrecognized dependency specifier %d\n"), dep->specifier);
+ break;
+ }
+
+ return count;
+}
+
+/* Clear branch flags on marked resources. This breaks the link between the
+ QP of the marking instruction and a subsequent branch on the same QP. */
+
+static void
+clear_qp_branch_flag (valueT mask)
+{
+ int i;
+ for (i = 0; i < regdepslen; i++)
+ {
+ valueT bit = ((valueT) 1 << regdeps[i].qp_regno);
+ if ((bit & mask) != 0)
+ {
+ regdeps[i].link_to_qp_branch = 0;
+ }
+ }
+}
+
+/* MASK contains 2 and only 2 PRs which are mutually exclusive. Remove
+ any mutexes which contain one of the PRs and create new ones when
+ needed. */
+
+static int
+update_qp_mutex (valueT mask)
+{
+ int i;
+ int add = 0;
+
+ i = 0;
+ while (i < qp_mutexeslen)
+ {
+ if ((qp_mutexes[i].prmask & mask) != 0)
+ {
+ /* If it destroys and creates the same mutex, do nothing. */
+ if (qp_mutexes[i].prmask == mask
+ && qp_mutexes[i].path == md.path)
+ {
+ i++;
+ add = -1;
+ }
+ else
+ {
+ int keep = 0;
+
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Clearing mutex relation");
+ print_prmask (qp_mutexes[i].prmask);
+ fprintf (stderr, "\n");
+ }
+
+ /* Deal with the old mutex with more than 3+ PRs only if
+ the new mutex on the same execution path with it.
+
+ FIXME: The 3+ mutex support is incomplete.
+ dot_pred_rel () may be a better place to fix it. */
+ if (qp_mutexes[i].path == md.path)
+ {
+ /* If it is a proper subset of the mutex, create a
+ new mutex. */
+ if (add == 0
+ && (qp_mutexes[i].prmask & mask) == mask)
+ add = 1;
+
+ qp_mutexes[i].prmask &= ~mask;
+ if (qp_mutexes[i].prmask & (qp_mutexes[i].prmask - 1))
+ {
+ /* Modify the mutex if there are more than one
+ PR left. */
+ keep = 1;
+ i++;
+ }
+ }
+
+ if (keep == 0)
+ /* Remove the mutex. */
+ qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (add == 1)
+ add_qp_mutex (mask);
+
+ return add;
+}
+
+/* Remove any mutexes which contain any of the PRs indicated in the mask.
+
+ Any changes to a PR clears the mutex relations which include that PR. */
+
+static void
+clear_qp_mutex (valueT mask)
+{
+ int i;
+
+ i = 0;
+ while (i < qp_mutexeslen)
+ {
+ if ((qp_mutexes[i].prmask & mask) != 0)
+ {
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Clearing mutex relation");
+ print_prmask (qp_mutexes[i].prmask);
+ fprintf (stderr, "\n");
+ }
+ qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+ }
+ else
+ ++i;
+ }
+}
+
+/* Clear implies relations which contain PRs in the given masks.
+ P1_MASK indicates the source of the implies relation, while P2_MASK
+ indicates the implied PR. */
+
+static void
+clear_qp_implies (valueT p1_mask, valueT p2_mask)
+{
+ int i;
+
+ i = 0;
+ while (i < qp_implieslen)
+ {
+ if ((((valueT) 1 << qp_implies[i].p1) & p1_mask) != 0
+ || (((valueT) 1 << qp_implies[i].p2) & p2_mask) != 0)
+ {
+ if (md.debug_dv)
+ fprintf (stderr, "Clearing implied relation PR%d->PR%d\n",
+ qp_implies[i].p1, qp_implies[i].p2);
+ qp_implies[i] = qp_implies[--qp_implieslen];
+ }
+ else
+ ++i;
+ }
+}
+
+/* Add the PRs specified to the list of implied relations. */
+
+static void
+add_qp_imply (int p1, int p2)
+{
+ valueT mask;
+ valueT bit;
+ int i;
+
+ /* p0 is not meaningful here. */
+ if (p1 == 0 || p2 == 0)
+ abort ();
+
+ if (p1 == p2)
+ return;
+
+ /* If it exists already, ignore it. */
+ for (i = 0; i < qp_implieslen; i++)
+ {
+ if (qp_implies[i].p1 == p1
+ && qp_implies[i].p2 == p2
+ && qp_implies[i].path == md.path
+ && !qp_implies[i].p2_branched)
+ return;
+ }
+
+ if (qp_implieslen == qp_impliestotlen)
+ {
+ qp_impliestotlen += 20;
+ qp_implies = (struct qp_imply *)
+ xrealloc ((void *) qp_implies,
+ qp_impliestotlen * sizeof (struct qp_imply));
+ }
+ if (md.debug_dv)
+ fprintf (stderr, " Registering PR%d implies PR%d\n", p1, p2);
+ qp_implies[qp_implieslen].p1 = p1;
+ qp_implies[qp_implieslen].p2 = p2;
+ qp_implies[qp_implieslen].path = md.path;
+ qp_implies[qp_implieslen++].p2_branched = 0;
+
+ /* Add in the implied transitive relations; for everything that p2 implies,
+ make p1 imply that, too; for everything that implies p1, make it imply p2
+ as well. */
+ for (i = 0; i < qp_implieslen; i++)
+ {
+ if (qp_implies[i].p1 == p2)
+ add_qp_imply (p1, qp_implies[i].p2);
+ if (qp_implies[i].p2 == p1)
+ add_qp_imply (qp_implies[i].p1, p2);
+ }
+ /* Add in mutex relations implied by this implies relation; for each mutex
+ relation containing p2, duplicate it and replace p2 with p1. */
+ bit = (valueT) 1 << p1;
+ mask = (valueT) 1 << p2;
+ for (i = 0; i < qp_mutexeslen; i++)
+ {
+ if (qp_mutexes[i].prmask & mask)
+ add_qp_mutex ((qp_mutexes[i].prmask & ~mask) | bit);
+ }
+}
+
+/* Add the PRs specified in the mask to the mutex list; this means that only
+ one of the PRs can be true at any time. PR0 should never be included in
+ the mask. */
+
+static void
+add_qp_mutex (valueT mask)
+{
+ if (mask & 0x1)
+ abort ();
+
+ if (qp_mutexeslen == qp_mutexestotlen)
+ {
+ qp_mutexestotlen += 20;
+ qp_mutexes = (struct qpmutex *)
+ xrealloc ((void *) qp_mutexes,
+ qp_mutexestotlen * sizeof (struct qpmutex));
+ }
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Registering mutex on");
+ print_prmask (mask);
+ fprintf (stderr, "\n");
+ }
+ qp_mutexes[qp_mutexeslen].path = md.path;
+ qp_mutexes[qp_mutexeslen++].prmask = mask;
+}
+
+static int
+has_suffix_p (const char *name, const char *suffix)
+{
+ size_t namelen = strlen (name);
+ size_t sufflen = strlen (suffix);
+
+ if (namelen <= sufflen)
+ return 0;
+ return strcmp (name + namelen - sufflen, suffix) == 0;
+}
+
+static void
+clear_register_values (void)
+{
+ int i;
+ if (md.debug_dv)
+ fprintf (stderr, " Clearing register values\n");
+ for (i = 1; i < NELEMS (gr_values); i++)
+ gr_values[i].known = 0;
+}
+
+/* Keep track of register values/changes which affect DV tracking.
+
+ optimization note: should add a flag to classes of insns where otherwise we
+ have to examine a group of strings to identify them. */
+
+static void
+note_register_values (struct ia64_opcode *idesc)
+{
+ valueT qp_changemask = 0;
+ int i;
+
+ /* Invalidate values for registers being written to. */
+ for (i = 0; i < idesc->num_outputs; i++)
+ {
+ if (idesc->operands[i] == IA64_OPND_R1
+ || idesc->operands[i] == IA64_OPND_R2
+ || idesc->operands[i] == IA64_OPND_R3)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ if (regno > 0 && regno < NELEMS (gr_values))
+ gr_values[regno].known = 0;
+ }
+ else if (idesc->operands[i] == IA64_OPND_R3_2)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_GR;
+ if (regno > 0 && regno < 4)
+ gr_values[regno].known = 0;
+ }
+ else if (idesc->operands[i] == IA64_OPND_P1
+ || idesc->operands[i] == IA64_OPND_P2)
+ {
+ int regno = CURR_SLOT.opnd[i].X_add_number - REG_P;
+ qp_changemask |= (valueT) 1 << regno;
+ }
+ else if (idesc->operands[i] == IA64_OPND_PR)
+ {
+ if (idesc->operands[2] & (valueT) 0x10000)
+ qp_changemask = ~(valueT) 0x1FFFF | idesc->operands[2];
+ else
+ qp_changemask = idesc->operands[2];
+ break;
+ }
+ else if (idesc->operands[i] == IA64_OPND_PR_ROT)
+ {
+ if (idesc->operands[1] & ((valueT) 1 << 43))
+ qp_changemask = -((valueT) 1 << 44) | idesc->operands[1];
+ else
+ qp_changemask = idesc->operands[1];
+ qp_changemask &= ~(valueT) 0xFFFF;
+ break;
+ }
+ }
+
+ /* Always clear qp branch flags on any PR change. */
+ /* FIXME there may be exceptions for certain compares. */
+ clear_qp_branch_flag (qp_changemask);
+
+ /* Invalidate rotating registers on insns which affect RRBs in CFM. */
+ if (idesc->flags & IA64_OPCODE_MOD_RRBS)
+ {
+ qp_changemask |= ~(valueT) 0xFFFF;
+ if (strcmp (idesc->name, "clrrrb.pr") != 0)
+ {
+ for (i = 32; i < 32 + md.rot.num_regs; i++)
+ gr_values[i].known = 0;
+ }
+ clear_qp_mutex (qp_changemask);
+ clear_qp_implies (qp_changemask, qp_changemask);
+ }
+ /* After a call, all register values are undefined, except those marked
+ as "safe". */
+ else if (strncmp (idesc->name, "br.call", 6) == 0
+ || strncmp (idesc->name, "brl.call", 7) == 0)
+ {
+ /* FIXME keep GR values which are marked as "safe_across_calls" */
+ clear_register_values ();
+ clear_qp_mutex (~qp_safe_across_calls);
+ clear_qp_implies (~qp_safe_across_calls, ~qp_safe_across_calls);
+ clear_qp_branch_flag (~qp_safe_across_calls);
+ }
+ else if (is_interruption_or_rfi (idesc)
+ || is_taken_branch (idesc))
+ {
+ clear_register_values ();
+ clear_qp_mutex (~(valueT) 0);
+ clear_qp_implies (~(valueT) 0, ~(valueT) 0);
+ }
+ /* Look for mutex and implies relations. */
+ else if ((idesc->operands[0] == IA64_OPND_P1
+ || idesc->operands[0] == IA64_OPND_P2)
+ && (idesc->operands[1] == IA64_OPND_P1
+ || idesc->operands[1] == IA64_OPND_P2))
+ {
+ int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
+ int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
+ valueT p1mask = (p1 != 0) ? (valueT) 1 << p1 : 0;
+ valueT p2mask = (p2 != 0) ? (valueT) 1 << p2 : 0;
+
+ /* If both PRs are PR0, we can't really do anything. */
+ if (p1 == 0 && p2 == 0)
+ {
+ if (md.debug_dv)
+ fprintf (stderr, " Ignoring PRs due to inclusion of p0\n");
+ }
+ /* In general, clear mutexes and implies which include P1 or P2,
+ with the following exceptions. */
+ else if (has_suffix_p (idesc->name, ".or.andcm")
+ || has_suffix_p (idesc->name, ".and.orcm"))
+ {
+ clear_qp_implies (p2mask, p1mask);
+ }
+ else if (has_suffix_p (idesc->name, ".andcm")
+ || has_suffix_p (idesc->name, ".and"))
+ {
+ clear_qp_implies (0, p1mask | p2mask);
+ }
+ else if (has_suffix_p (idesc->name, ".orcm")
+ || has_suffix_p (idesc->name, ".or"))
+ {
+ clear_qp_mutex (p1mask | p2mask);
+ clear_qp_implies (p1mask | p2mask, 0);
+ }
+ else
+ {
+ int added = 0;
+
+ clear_qp_implies (p1mask | p2mask, p1mask | p2mask);
+
+ /* If one of the PRs is PR0, we call clear_qp_mutex. */
+ if (p1 == 0 || p2 == 0)
+ clear_qp_mutex (p1mask | p2mask);
+ else
+ added = update_qp_mutex (p1mask | p2mask);
+
+ if (CURR_SLOT.qp_regno == 0
+ || has_suffix_p (idesc->name, ".unc"))
+ {
+ if (added == 0 && p1 && p2)
+ add_qp_mutex (p1mask | p2mask);
+ if (CURR_SLOT.qp_regno != 0)
+ {
+ if (p1)
+ add_qp_imply (p1, CURR_SLOT.qp_regno);
+ if (p2)
+ add_qp_imply (p2, CURR_SLOT.qp_regno);
+ }
+ }
+ }
+ }
+ /* Look for mov imm insns into GRs. */
+ else if (idesc->operands[0] == IA64_OPND_R1
+ && (idesc->operands[1] == IA64_OPND_IMM22
+ || idesc->operands[1] == IA64_OPND_IMMU64)
+ && CURR_SLOT.opnd[1].X_op == O_constant
+ && (strcmp (idesc->name, "mov") == 0
+ || strcmp (idesc->name, "movl") == 0))
+ {
+ int regno = CURR_SLOT.opnd[0].X_add_number - REG_GR;
+ if (regno > 0 && regno < NELEMS (gr_values))
+ {
+ gr_values[regno].known = 1;
+ gr_values[regno].value = CURR_SLOT.opnd[1].X_add_number;
+ gr_values[regno].path = md.path;
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Know gr%d = ", regno);
+ fprintf_vma (stderr, gr_values[regno].value);
+ fputs ("\n", stderr);
+ }
+ }
+ }
+ /* Look for dep.z imm insns. */
+ else if (idesc->operands[0] == IA64_OPND_R1
+ && idesc->operands[1] == IA64_OPND_IMM8
+ && strcmp (idesc->name, "dep.z") == 0)
+ {
+ int regno = CURR_SLOT.opnd[0].X_add_number - REG_GR;
+ if (regno > 0 && regno < NELEMS (gr_values))
+ {
+ valueT value = CURR_SLOT.opnd[1].X_add_number;
+
+ if (CURR_SLOT.opnd[3].X_add_number < 64)
+ value &= ((valueT)1 << CURR_SLOT.opnd[3].X_add_number) - 1;
+ value <<= CURR_SLOT.opnd[2].X_add_number;
+ gr_values[regno].known = 1;
+ gr_values[regno].value = value;
+ gr_values[regno].path = md.path;
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Know gr%d = ", regno);
+ fprintf_vma (stderr, gr_values[regno].value);
+ fputs ("\n", stderr);
+ }
+ }
+ }
+ else
+ {
+ clear_qp_mutex (qp_changemask);
+ clear_qp_implies (qp_changemask, qp_changemask);
+ }
+}
+
+/* Return whether the given predicate registers are currently mutex. */
+
+static int
+qp_mutex (int p1, int p2, int path)
+{
+ int i;
+ valueT mask;
+
+ if (p1 != p2)
+ {
+ mask = ((valueT) 1 << p1) | (valueT) 1 << p2;
+ for (i = 0; i < qp_mutexeslen; i++)
+ {
+ if (qp_mutexes[i].path >= path
+ && (qp_mutexes[i].prmask & mask) == mask)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Return whether the given resource is in the given insn's list of chks
+ Return 1 if the conflict is absolutely determined, 2 if it's a potential
+ conflict. */
+
+static int
+resources_match (struct rsrc *rs,
+ struct ia64_opcode *idesc,
+ int note,
+ int qp_regno,
+ int path)
+{
+ struct rsrc specs[MAX_SPECS];
+ int count;
+
+ /* If the marked resource's qp_regno and the given qp_regno are mutex,
+ we don't need to check. One exception is note 11, which indicates that
+ target predicates are written regardless of PR[qp]. */
+ if (qp_mutex (rs->qp_regno, qp_regno, path)
+ && note != 11)
+ return 0;
+
+ count = specify_resource (rs->dependency, idesc, DV_CHK, specs, note, path);
+ while (count-- > 0)
+ {
+ /* UNAT checking is a bit more specific than other resources */
+ if (rs->dependency->specifier == IA64_RS_AR_UNAT
+ && specs[count].mem_offset.hint
+ && rs->mem_offset.hint)
+ {
+ if (rs->mem_offset.base == specs[count].mem_offset.base)
+ {
+ if (((rs->mem_offset.offset >> 3) & 0x3F) ==
+ ((specs[count].mem_offset.offset >> 3) & 0x3F))
+ return 1;
+ else
+ continue;
+ }
+ }
+
+ /* Skip apparent PR write conflicts where both writes are an AND or both
+ writes are an OR. */
+ if (rs->dependency->specifier == IA64_RS_PR
+ || rs->dependency->specifier == IA64_RS_PRr
+ || rs->dependency->specifier == IA64_RS_PR63)
+ {
+ if (specs[count].cmp_type != CMP_NONE
+ && specs[count].cmp_type == rs->cmp_type)
+ {
+ if (md.debug_dv)
+ fprintf (stderr, " %s on parallel compare allowed (PR%d)\n",
+ dv_mode[rs->dependency->mode],
+ rs->dependency->specifier != IA64_RS_PR63 ?
+ specs[count].index : 63);
+ continue;
+ }
+ if (md.debug_dv)
+ fprintf (stderr,
+ " %s on parallel compare conflict %s vs %s on PR%d\n",
+ dv_mode[rs->dependency->mode],
+ dv_cmp_type[rs->cmp_type],
+ dv_cmp_type[specs[count].cmp_type],
+ rs->dependency->specifier != IA64_RS_PR63 ?
+ specs[count].index : 63);
+
+ }
+
+ /* If either resource is not specific, conservatively assume a conflict
+ */
+ if (!specs[count].specific || !rs->specific)
+ return 2;
+ else if (specs[count].index == rs->index)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Indicate an instruction group break; if INSERT_STOP is non-zero, then
+ insert a stop to create the break. Update all resource dependencies
+ appropriately. If QP_REGNO is non-zero, only apply the break to resources
+ which use the same QP_REGNO and have the link_to_qp_branch flag set.
+ If SAVE_CURRENT is non-zero, don't affect resources marked by the current
+ instruction. */
+
+static void
+insn_group_break (int insert_stop, int qp_regno, int save_current)
+{
+ int i;
+
+ if (insert_stop && md.num_slots_in_use > 0)
+ PREV_SLOT.end_of_insn_group = 1;
+
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Insn group break%s",
+ (insert_stop ? " (w/stop)" : ""));
+ if (qp_regno != 0)
+ fprintf (stderr, " effective for QP=%d", qp_regno);
+ fprintf (stderr, "\n");
+ }
+
+ i = 0;
+ while (i < regdepslen)
+ {
+ const struct ia64_dependency *dep = regdeps[i].dependency;
+
+ if (qp_regno != 0
+ && regdeps[i].qp_regno != qp_regno)
+ {
+ ++i;
+ continue;
+ }
+
+ if (save_current
+ && CURR_SLOT.src_file == regdeps[i].file
+ && CURR_SLOT.src_line == regdeps[i].line)
+ {
+ ++i;
+ continue;
+ }
+
+ /* clear dependencies which are automatically cleared by a stop, or
+ those that have reached the appropriate state of insn serialization */
+ if (dep->semantics == IA64_DVS_IMPLIED
+ || dep->semantics == IA64_DVS_IMPLIEDF
+ || regdeps[i].insn_srlz == STATE_SRLZ)
+ {
+ print_dependency ("Removing", i);
+ regdeps[i] = regdeps[--regdepslen];
+ }
+ else
+ {
+ if (dep->semantics == IA64_DVS_DATA
+ || dep->semantics == IA64_DVS_INSTR
+ || dep->semantics == IA64_DVS_SPECIFIC)
+ {
+ if (regdeps[i].insn_srlz == STATE_NONE)
+ regdeps[i].insn_srlz = STATE_STOP;
+ if (regdeps[i].data_srlz == STATE_NONE)
+ regdeps[i].data_srlz = STATE_STOP;
+ }
+ ++i;
+ }
+ }
+}
+
+/* Add the given resource usage spec to the list of active dependencies. */
+
+static void
+mark_resource (struct ia64_opcode *idesc ATTRIBUTE_UNUSED,
+ const struct ia64_dependency *dep ATTRIBUTE_UNUSED,
+ struct rsrc *spec,
+ int depind,
+ int path)
+{
+ if (regdepslen == regdepstotlen)
+ {
+ regdepstotlen += 20;
+ regdeps = (struct rsrc *)
+ xrealloc ((void *) regdeps,
+ regdepstotlen * sizeof (struct rsrc));
+ }
+
+ regdeps[regdepslen] = *spec;
+ regdeps[regdepslen].depind = depind;
+ regdeps[regdepslen].path = path;
+ regdeps[regdepslen].file = CURR_SLOT.src_file;
+ regdeps[regdepslen].line = CURR_SLOT.src_line;
+
+ print_dependency ("Adding", regdepslen);
+
+ ++regdepslen;
+}
+
+static void
+print_dependency (const char *action, int depind)
+{
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " %s %s '%s'",
+ action, dv_mode[(regdeps[depind].dependency)->mode],
+ (regdeps[depind].dependency)->name);
+ if (regdeps[depind].specific && regdeps[depind].index >= 0)
+ fprintf (stderr, " (%d)", regdeps[depind].index);
+ if (regdeps[depind].mem_offset.hint)
+ {
+ fputs (" ", stderr);
+ fprintf_vma (stderr, regdeps[depind].mem_offset.base);
+ fputs ("+", stderr);
+ fprintf_vma (stderr, regdeps[depind].mem_offset.offset);
+ }
+ fprintf (stderr, "\n");
+ }
+}
+
+static void
+instruction_serialization (void)
+{
+ int i;
+ if (md.debug_dv)
+ fprintf (stderr, " Instruction serialization\n");
+ for (i = 0; i < regdepslen; i++)
+ if (regdeps[i].insn_srlz == STATE_STOP)
+ regdeps[i].insn_srlz = STATE_SRLZ;
+}
+
+static void
+data_serialization (void)
+{
+ int i = 0;
+ if (md.debug_dv)
+ fprintf (stderr, " Data serialization\n");
+ while (i < regdepslen)
+ {
+ if (regdeps[i].data_srlz == STATE_STOP
+ /* Note: as of 991210, all "other" dependencies are cleared by a
+ data serialization. This might change with new tables */
+ || (regdeps[i].dependency)->semantics == IA64_DVS_OTHER)
+ {
+ print_dependency ("Removing", i);
+ regdeps[i] = regdeps[--regdepslen];
+ }
+ else
+ ++i;
+ }
+}
+
+/* Insert stops and serializations as needed to avoid DVs. */
+
+static void
+remove_marked_resource (struct rsrc *rs)
+{
+ switch (rs->dependency->semantics)
+ {
+ case IA64_DVS_SPECIFIC:
+ if (md.debug_dv)
+ fprintf (stderr, "Implementation-specific, assume worst case...\n");
+ /* ...fall through... */
+ case IA64_DVS_INSTR:
+ if (md.debug_dv)
+ fprintf (stderr, "Inserting instr serialization\n");
+ if (rs->insn_srlz < STATE_STOP)
+ insn_group_break (1, 0, 0);
+ if (rs->insn_srlz < STATE_SRLZ)
+ {
+ struct slot oldslot = CURR_SLOT;
+ /* Manually jam a srlz.i insn into the stream */
+ memset (&CURR_SLOT, 0, sizeof (CURR_SLOT));
+ CURR_SLOT.user_template = -1;
+ CURR_SLOT.idesc = ia64_find_opcode ("srlz.i");
+ instruction_serialization ();
+ md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
+ if (++md.num_slots_in_use >= NUM_SLOTS)
+ emit_one_bundle ();
+ CURR_SLOT = oldslot;
+ }
+ insn_group_break (1, 0, 0);
+ break;
+ case IA64_DVS_OTHER: /* as of rev2 (991220) of the DV tables, all
+ "other" types of DV are eliminated
+ by a data serialization */
+ case IA64_DVS_DATA:
+ if (md.debug_dv)
+ fprintf (stderr, "Inserting data serialization\n");
+ if (rs->data_srlz < STATE_STOP)
+ insn_group_break (1, 0, 0);
+ {
+ struct slot oldslot = CURR_SLOT;
+ /* Manually jam a srlz.d insn into the stream */
+ memset (&CURR_SLOT, 0, sizeof (CURR_SLOT));
+ CURR_SLOT.user_template = -1;
+ CURR_SLOT.idesc = ia64_find_opcode ("srlz.d");
+ data_serialization ();
+ md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
+ if (++md.num_slots_in_use >= NUM_SLOTS)
+ emit_one_bundle ();
+ CURR_SLOT = oldslot;
+ }
+ break;
+ case IA64_DVS_IMPLIED:
+ case IA64_DVS_IMPLIEDF:
+ if (md.debug_dv)
+ fprintf (stderr, "Inserting stop\n");
+ insn_group_break (1, 0, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Check the resources used by the given opcode against the current dependency
+ list.
+
+ The check is run once for each execution path encountered. In this case,
+ a unique execution path is the sequence of instructions following a code
+ entry point, e.g. the following has three execution paths, one starting
+ at L0, one at L1, and one at L2.
+
+ L0: nop
+ L1: add
+ L2: add
+ br.ret
+*/
+
+static void
+check_dependencies (struct ia64_opcode *idesc)
+{
+ const struct ia64_opcode_dependency *opdeps = idesc->dependencies;
+ int path;
+ int i;
+
+ /* Note that the number of marked resources may change within the
+ loop if in auto mode. */
+ i = 0;
+ while (i < regdepslen)
+ {
+ struct rsrc *rs = &regdeps[i];
+ const struct ia64_dependency *dep = rs->dependency;
+ int chkind;
+ int note;
+ int start_over = 0;
+
+ if (dep->semantics == IA64_DVS_NONE
+ || (chkind = depends_on (rs->depind, idesc)) == -1)
+ {
+ ++i;
+ continue;
+ }
+
+ note = NOTE (opdeps->chks[chkind]);
+
+ /* Check this resource against each execution path seen thus far. */
+ for (path = 0; path <= md.path; path++)
+ {
+ int matchtype;
+
+ /* If the dependency wasn't on the path being checked, ignore it. */
+ if (rs->path < path)
+ continue;
+
+ /* If the QP for this insn implies a QP which has branched, don't
+ bother checking. Ed. NOTE: I don't think this check is terribly
+ useful; what's the point of generating code which will only be
+ reached if its QP is zero?
+ This code was specifically inserted to handle the following code,
+ based on notes from Intel's DV checking code, where p1 implies p2.
+
+ mov r4 = 2
+ (p2) br.cond L
+ (p1) mov r4 = 7
+ */
+ if (CURR_SLOT.qp_regno != 0)
+ {
+ int skip = 0;
+ int implies;
+ for (implies = 0; implies < qp_implieslen; implies++)
+ {
+ if (qp_implies[implies].path >= path
+ && qp_implies[implies].p1 == CURR_SLOT.qp_regno
+ && qp_implies[implies].p2_branched)
+ {
+ skip = 1;
+ break;
+ }
+ }
+ if (skip)
+ continue;
+ }
+
+ if ((matchtype = resources_match (rs, idesc, note,
+ CURR_SLOT.qp_regno, path)) != 0)
+ {
+ char msg[1024];
+ char pathmsg[256] = "";
+ char indexmsg[256] = "";
+ int certain = (matchtype == 1 && CURR_SLOT.qp_regno == 0);
+
+ if (path != 0)
+ snprintf (pathmsg, sizeof (pathmsg),
+ " when entry is at label '%s'",
+ md.entry_labels[path - 1]);
+ if (matchtype == 1 && rs->index >= 0)
+ snprintf (indexmsg, sizeof (indexmsg),
+ ", specific resource number is %d",
+ rs->index);
+ snprintf (msg, sizeof (msg),
+ "Use of '%s' %s %s dependency '%s' (%s)%s%s",
+ idesc->name,
+ (certain ? "violates" : "may violate"),
+ dv_mode[dep->mode], dep->name,
+ dv_sem[dep->semantics],
+ pathmsg, indexmsg);
+
+ if (md.explicit_mode)
+ {
+ as_warn ("%s", msg);
+ if (path < md.path)
+ as_warn (_("Only the first path encountering the conflict is reported"));
+ as_warn_where (rs->file, rs->line,
+ _("This is the location of the conflicting usage"));
+ /* Don't bother checking other paths, to avoid duplicating
+ the same warning */
+ break;
+ }
+ else
+ {
+ if (md.debug_dv)
+ fprintf (stderr, "%s @ %s:%d\n", msg, rs->file, rs->line);
+
+ remove_marked_resource (rs);
+
+ /* since the set of dependencies has changed, start over */
+ /* FIXME -- since we're removing dvs as we go, we
+ probably don't really need to start over... */
+ start_over = 1;
+ break;
+ }
+ }
+ }
+ if (start_over)
+ i = 0;
+ else
+ ++i;
+ }
+}
+
+/* Register new dependencies based on the given opcode. */
+
+static void
+mark_resources (struct ia64_opcode *idesc)
+{
+ int i;
+ const struct ia64_opcode_dependency *opdeps = idesc->dependencies;
+ int add_only_qp_reads = 0;
+
+ /* A conditional branch only uses its resources if it is taken; if it is
+ taken, we stop following that path. The other branch types effectively
+ *always* write their resources. If it's not taken, register only QP
+ reads. */
+ if (is_conditional_branch (idesc) || is_interruption_or_rfi (idesc))
+ {
+ add_only_qp_reads = 1;
+ }
+
+ if (md.debug_dv)
+ fprintf (stderr, "Registering '%s' resource usage\n", idesc->name);
+
+ for (i = 0; i < opdeps->nregs; i++)
+ {
+ const struct ia64_dependency *dep;
+ struct rsrc specs[MAX_SPECS];
+ int note;
+ int path;
+ int count;
+
+ dep = ia64_find_dependency (opdeps->regs[i]);
+ note = NOTE (opdeps->regs[i]);
+
+ if (add_only_qp_reads
+ && !(dep->mode == IA64_DV_WAR
+ && (dep->specifier == IA64_RS_PR
+ || dep->specifier == IA64_RS_PRr
+ || dep->specifier == IA64_RS_PR63)))
+ continue;
+
+ count = specify_resource (dep, idesc, DV_REG, specs, note, md.path);
+
+ while (count-- > 0)
+ {
+ mark_resource (idesc, dep, &specs[count],
+ DEP (opdeps->regs[i]), md.path);
+ }
+
+ /* The execution path may affect register values, which may in turn
+ affect which indirect-access resources are accessed. */
+ switch (dep->specifier)
+ {
+ default:
+ break;
+ case IA64_RS_CPUID:
+ case IA64_RS_DBR:
+ case IA64_RS_IBR:
+ case IA64_RS_MSR:
+ case IA64_RS_PKR:
+ case IA64_RS_PMC:
+ case IA64_RS_PMD:
+ case IA64_RS_RR:
+ for (path = 0; path < md.path; path++)
+ {
+ count = specify_resource (dep, idesc, DV_REG, specs, note, path);
+ while (count-- > 0)
+ mark_resource (idesc, dep, &specs[count],
+ DEP (opdeps->regs[i]), path);
+ }
+ break;
+ }
+ }
+}
+
+/* Remove dependencies when they no longer apply. */
+
+static void
+update_dependencies (struct ia64_opcode *idesc)
+{
+ int i;
+
+ if (strcmp (idesc->name, "srlz.i") == 0)
+ {
+ instruction_serialization ();
+ }
+ else if (strcmp (idesc->name, "srlz.d") == 0)
+ {
+ data_serialization ();
+ }
+ else if (is_interruption_or_rfi (idesc)
+ || is_taken_branch (idesc))
+ {
+ /* Although technically the taken branch doesn't clear dependencies
+ which require a srlz.[id], we don't follow the branch; the next
+ instruction is assumed to start with a clean slate. */
+ regdepslen = 0;
+ md.path = 0;
+ }
+ else if (is_conditional_branch (idesc)
+ && CURR_SLOT.qp_regno != 0)
+ {
+ int is_call = strstr (idesc->name, ".call") != NULL;
+
+ for (i = 0; i < qp_implieslen; i++)
+ {
+ /* If the conditional branch's predicate is implied by the predicate
+ in an existing dependency, remove that dependency. */
+ if (qp_implies[i].p2 == CURR_SLOT.qp_regno)
+ {
+ int depind = 0;
+ /* Note that this implied predicate takes a branch so that if
+ a later insn generates a DV but its predicate implies this
+ one, we can avoid the false DV warning. */
+ qp_implies[i].p2_branched = 1;
+ while (depind < regdepslen)
+ {
+ if (regdeps[depind].qp_regno == qp_implies[i].p1)
+ {
+ print_dependency ("Removing", depind);
+ regdeps[depind] = regdeps[--regdepslen];
+ }
+ else
+ ++depind;
+ }
+ }
+ }
+ /* Any marked resources which have this same predicate should be
+ cleared, provided that the QP hasn't been modified between the
+ marking instruction and the branch. */
+ if (is_call)
+ {
+ insn_group_break (0, CURR_SLOT.qp_regno, 1);
+ }
+ else
+ {
+ i = 0;
+ while (i < regdepslen)
+ {
+ if (regdeps[i].qp_regno == CURR_SLOT.qp_regno
+ && regdeps[i].link_to_qp_branch
+ && (regdeps[i].file != CURR_SLOT.src_file
+ || regdeps[i].line != CURR_SLOT.src_line))
+ {
+ /* Treat like a taken branch */
+ print_dependency ("Removing", i);
+ regdeps[i] = regdeps[--regdepslen];
+ }
+ else
+ ++i;
+ }
+ }
+ }
+}
+
+/* Examine the current instruction for dependency violations. */
+
+static int
+check_dv (struct ia64_opcode *idesc)
+{
+ if (md.debug_dv)
+ {
+ fprintf (stderr, "Checking %s for violations (line %d, %d/%d)\n",
+ idesc->name, CURR_SLOT.src_line,
+ idesc->dependencies->nchks,
+ idesc->dependencies->nregs);
+ }
+
+ /* Look through the list of currently marked resources; if the current
+ instruction has the dependency in its chks list which uses that resource,
+ check against the specific resources used. */
+ check_dependencies (idesc);
+
+ /* Look up the instruction's regdeps (RAW writes, WAW writes, and WAR reads),
+ then add them to the list of marked resources. */
+ mark_resources (idesc);
+
+ /* There are several types of dependency semantics, and each has its own
+ requirements for being cleared
+
+ Instruction serialization (insns separated by interruption, rfi, or
+ writer + srlz.i + reader, all in separate groups) clears DVS_INSTR.
+
+ Data serialization (instruction serialization, or writer + srlz.d +
+ reader, where writer and srlz.d are in separate groups) clears
+ DVS_DATA. (This also clears DVS_OTHER, but that is not guaranteed to
+ always be the case).
+
+ Instruction group break (groups separated by stop, taken branch,
+ interruption or rfi) clears DVS_IMPLIED and DVS_IMPLIEDF.
+ */
+ update_dependencies (idesc);
+
+ /* Sometimes, knowing a register value allows us to avoid giving a false DV
+ warning. Keep track of as many as possible that are useful. */
+ note_register_values (idesc);
+
+ /* We don't need or want this anymore. */
+ md.mem_offset.hint = 0;
+
+ return 0;
+}
+
+/* Translate one line of assembly. Pseudo ops and labels do not show
+ here. */
+void
+md_assemble (char *str)
+{
+ char *saved_input_line_pointer, *mnemonic;
+ const struct pseudo_opcode *pdesc;
+ struct ia64_opcode *idesc;
+ unsigned char qp_regno;
+ unsigned int flags;
+ int ch;
+
+ saved_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+
+ /* extract the opcode (mnemonic): */
+
+ mnemonic = input_line_pointer;
+ ch = get_symbol_end ();
+ pdesc = (struct pseudo_opcode *) hash_find (md.pseudo_hash, mnemonic);
+ if (pdesc)
+ {
+ *input_line_pointer = ch;
+ (*pdesc->handler) (pdesc->arg);
+ goto done;
+ }
+
+ /* Find the instruction descriptor matching the arguments. */
+
+ idesc = ia64_find_opcode (mnemonic);
+ *input_line_pointer = ch;
+ if (!idesc)
+ {
+ as_bad (_("Unknown opcode `%s'"), mnemonic);
+ goto done;
+ }
+
+ idesc = parse_operands (idesc);
+ if (!idesc)
+ goto done;
+
+ /* Handle the dynamic ops we can handle now: */
+ if (idesc->type == IA64_TYPE_DYN)
+ {
+ if (strcmp (idesc->name, "add") == 0)
+ {
+ if (CURR_SLOT.opnd[2].X_op == O_register
+ && CURR_SLOT.opnd[2].X_add_number < 4)
+ mnemonic = "addl";
+ else
+ mnemonic = "adds";
+ ia64_free_opcode (idesc);
+ idesc = ia64_find_opcode (mnemonic);
+ }
+ else if (strcmp (idesc->name, "mov") == 0)
+ {
+ enum ia64_opnd opnd1, opnd2;
+ int rop;
+
+ opnd1 = idesc->operands[0];
+ opnd2 = idesc->operands[1];
+ if (opnd1 == IA64_OPND_AR3)
+ rop = 0;
+ else if (opnd2 == IA64_OPND_AR3)
+ rop = 1;
+ else
+ abort ();
+ if (CURR_SLOT.opnd[rop].X_op == O_register)
+ {
+ if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
+ mnemonic = "mov.i";
+ else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
+ mnemonic = "mov.m";
+ else
+ rop = -1;
+ }
+ else
+ abort ();
+ if (rop >= 0)
+ {
+ ia64_free_opcode (idesc);
+ idesc = ia64_find_opcode (mnemonic);
+ while (idesc != NULL
+ && (idesc->operands[0] != opnd1
+ || idesc->operands[1] != opnd2))
+ idesc = get_next_opcode (idesc);
+ }
+ }
+ }
+ else if (strcmp (idesc->name, "mov.i") == 0
+ || strcmp (idesc->name, "mov.m") == 0)
+ {
+ enum ia64_opnd opnd1, opnd2;
+ int rop;
+
+ opnd1 = idesc->operands[0];
+ opnd2 = idesc->operands[1];
+ if (opnd1 == IA64_OPND_AR3)
+ rop = 0;
+ else if (opnd2 == IA64_OPND_AR3)
+ rop = 1;
+ else
+ abort ();
+ if (CURR_SLOT.opnd[rop].X_op == O_register)
+ {
+ char unit = 'a';
+ if (ar_is_only_in_integer_unit (CURR_SLOT.opnd[rop].X_add_number))
+ unit = 'i';
+ else if (ar_is_only_in_memory_unit (CURR_SLOT.opnd[rop].X_add_number))
+ unit = 'm';
+ if (unit != 'a' && unit != idesc->name [4])
+ as_bad (_("AR %d can only be accessed by %c-unit"),
+ (int) (CURR_SLOT.opnd[rop].X_add_number - REG_AR),
+ TOUPPER (unit));
+ }
+ }
+ else if (strcmp (idesc->name, "hint.b") == 0)
+ {
+ switch (md.hint_b)
+ {
+ case hint_b_ok:
+ break;
+ case hint_b_warning:
+ as_warn (_("hint.b may be treated as nop"));
+ break;
+ case hint_b_error:
+ as_bad (_("hint.b shouldn't be used"));
+ break;
+ }
+ }
+
+ qp_regno = 0;
+ if (md.qp.X_op == O_register)
+ {
+ qp_regno = md.qp.X_add_number - REG_P;
+ md.qp.X_op = O_absent;
+ }
+
+ flags = idesc->flags;
+
+ if ((flags & IA64_OPCODE_FIRST) != 0)
+ {
+ /* The alignment frag has to end with a stop bit only if the
+ next instruction after the alignment directive has to be
+ the first instruction in an instruction group. */
+ if (align_frag)
+ {
+ while (align_frag->fr_type != rs_align_code)
+ {
+ align_frag = align_frag->fr_next;
+ if (!align_frag)
+ break;
+ }
+ /* align_frag can be NULL if there are directives in
+ between. */
+ if (align_frag && align_frag->fr_next == frag_now)
+ align_frag->tc_frag_data = 1;
+ }
+
+ insn_group_break (1, 0, 0);
+ }
+ align_frag = NULL;
+
+ if ((flags & IA64_OPCODE_NO_PRED) != 0 && qp_regno != 0)
+ {
+ as_bad (_("`%s' cannot be predicated"), idesc->name);
+ goto done;
+ }
+
+ /* Build the instruction. */
+ CURR_SLOT.qp_regno = qp_regno;
+ CURR_SLOT.idesc = idesc;
+ as_where (&CURR_SLOT.src_file, &CURR_SLOT.src_line);
+ dwarf2_where (&CURR_SLOT.debug_line);
+ dwarf2_consume_line_info ();
+
+ /* Add unwind entries, if there are any. */
+ if (unwind.current_entry)
+ {
+ CURR_SLOT.unwind_record = unwind.current_entry;
+ unwind.current_entry = NULL;
+ }
+ if (unwind.pending_saves)
+ {
+ if (unwind.pending_saves->next)
+ {
+ /* Attach the next pending save to the next slot so that its
+ slot number will get set correctly. */
+ add_unwind_entry (unwind.pending_saves->next, NOT_A_CHAR);
+ unwind.pending_saves = &unwind.pending_saves->next->r.record.p;
+ }
+ else
+ unwind.pending_saves = NULL;
+ }
+ if (unwind.proc_pending.sym && S_IS_DEFINED (unwind.proc_pending.sym))
+ unwind.insn = 1;
+
+ /* Check for dependency violations. */
+ if (md.detect_dv)
+ check_dv (idesc);
+
+ md.curr_slot = (md.curr_slot + 1) % NUM_SLOTS;
+ if (++md.num_slots_in_use >= NUM_SLOTS)
+ emit_one_bundle ();
+
+ if ((flags & IA64_OPCODE_LAST) != 0)
+ insn_group_break (1, 0, 0);
+
+ md.last_text_seg = now_seg;
+
+ done:
+ input_line_pointer = saved_input_line_pointer;
+}
+
+/* Called when symbol NAME cannot be found in the symbol table.
+ Should be used for dynamic valued symbols only. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Called for any expression that can not be recognized. When the
+ function is called, `input_line_pointer' will point to the start of
+ the expression. */
+
+void
+md_operand (expressionS *e)
+{
+ switch (*input_line_pointer)
+ {
+ case '[':
+ ++input_line_pointer;
+ expression_and_evaluate (e);
+ if (*input_line_pointer != ']')
+ {
+ as_bad (_("Closing bracket missing"));
+ goto err;
+ }
+ else
+ {
+ if (e->X_op != O_register
+ || e->X_add_number < REG_GR
+ || e->X_add_number > REG_GR + 127)
+ {
+ as_bad (_("Index must be a general register"));
+ e->X_add_number = REG_GR;
+ }
+
+ ++input_line_pointer;
+ e->X_op = O_index;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return;
+
+ err:
+ ignore_rest_of_line ();
+}
+
+/* Return 1 if it's OK to adjust a reloc by replacing the symbol with
+ a section symbol plus some offset. For relocs involving @fptr(),
+ directives we don't want such adjustments since we need to have the
+ original symbol's name in the reloc. */
+int
+ia64_fix_adjustable (fixS *fix)
+{
+ /* Prevent all adjustments to global symbols */
+ if (S_IS_EXTERNAL (fix->fx_addsy) || S_IS_WEAK (fix->fx_addsy))
+ return 0;
+
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_IA64_FPTR64I:
+ case BFD_RELOC_IA64_FPTR32MSB:
+ case BFD_RELOC_IA64_FPTR32LSB:
+ case BFD_RELOC_IA64_FPTR64MSB:
+ case BFD_RELOC_IA64_FPTR64LSB:
+ case BFD_RELOC_IA64_LTOFF_FPTR22:
+ case BFD_RELOC_IA64_LTOFF_FPTR64I:
+ return 0;
+ default:
+ break;
+ }
+
+ return 1;
+}
+
+int
+ia64_force_relocation (fixS *fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_IA64_FPTR64I:
+ case BFD_RELOC_IA64_FPTR32MSB:
+ case BFD_RELOC_IA64_FPTR32LSB:
+ case BFD_RELOC_IA64_FPTR64MSB:
+ case BFD_RELOC_IA64_FPTR64LSB:
+
+ case BFD_RELOC_IA64_LTOFF22:
+ case BFD_RELOC_IA64_LTOFF64I:
+ case BFD_RELOC_IA64_LTOFF_FPTR22:
+ case BFD_RELOC_IA64_LTOFF_FPTR64I:
+ case BFD_RELOC_IA64_PLTOFF22:
+ case BFD_RELOC_IA64_PLTOFF64I:
+ case BFD_RELOC_IA64_PLTOFF64MSB:
+ case BFD_RELOC_IA64_PLTOFF64LSB:
+
+ case BFD_RELOC_IA64_LTOFF22X:
+ case BFD_RELOC_IA64_LDXMOV:
+ return 1;
+
+ default:
+ break;
+ }
+
+ return generic_force_reloc (fix);
+}
+
+/* Decide from what point a pc-relative relocation is relative to,
+ relative to the pc-relative fixup. Er, relatively speaking. */
+long
+ia64_pcrel_from_section (fixS *fix, segT sec)
+{
+ unsigned long off = fix->fx_frag->fr_address + fix->fx_where;
+
+ if (bfd_get_section_flags (stdoutput, sec) & SEC_CODE)
+ off &= ~0xfUL;
+
+ return off;
+}
+
+
+/* Used to emit section-relative relocs for the dwarf2 debug data. */
+void
+ia64_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+ expressionS exp;
+
+ exp.X_op = O_pseudo_fixup;
+ exp.X_op_symbol = pseudo_func[FUNC_SEC_RELATIVE].u.sym;
+ exp.X_add_number = 0;
+ exp.X_add_symbol = symbol;
+ emit_expr (&exp, size);
+}
+
+/* This is called whenever some data item (not an instruction) needs a
+ fixup. We pick the right reloc code depending on the byteorder
+ currently in effect. */
+void
+ia64_cons_fix_new (fragS *f, int where, int nbytes, expressionS *exp,
+ bfd_reloc_code_real_type code)
+{
+ fixS *fix;
+
+ switch (nbytes)
+ {
+ /* There are no reloc for 8 and 16 bit quantities, but we allow
+ them here since they will work fine as long as the expression
+ is fully defined at the end of the pass over the source file. */
+ case 1: code = BFD_RELOC_8; break;
+ case 2: code = BFD_RELOC_16; break;
+ case 4:
+ if (target_big_endian)
+ code = BFD_RELOC_IA64_DIR32MSB;
+ else
+ code = BFD_RELOC_IA64_DIR32LSB;
+ break;
+
+ case 8:
+ /* In 32-bit mode, data8 could mean function descriptors too. */
+ if (exp->X_op == O_pseudo_fixup
+ && exp->X_op_symbol
+ && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC
+ && !(md.flags & EF_IA_64_ABI64))
+ {
+ if (target_big_endian)
+ code = BFD_RELOC_IA64_IPLTMSB;
+ else
+ code = BFD_RELOC_IA64_IPLTLSB;
+ exp->X_op = O_symbol;
+ break;
+ }
+ else
+ {
+ if (target_big_endian)
+ code = BFD_RELOC_IA64_DIR64MSB;
+ else
+ code = BFD_RELOC_IA64_DIR64LSB;
+ break;
+ }
+
+ case 16:
+ if (exp->X_op == O_pseudo_fixup
+ && exp->X_op_symbol
+ && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC)
+ {
+ if (target_big_endian)
+ code = BFD_RELOC_IA64_IPLTMSB;
+ else
+ code = BFD_RELOC_IA64_IPLTLSB;
+ exp->X_op = O_symbol;
+ break;
+ }
+ /* FALLTHRU */
+
+ default:
+ as_bad (_("Unsupported fixup size %d"), nbytes);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (exp->X_op == O_pseudo_fixup)
+ {
+ exp->X_op = O_symbol;
+ code = ia64_gen_real_reloc_type (exp->X_op_symbol, code);
+ /* ??? If code unchanged, unsupported. */
+ }
+
+ fix = fix_new_exp (f, where, nbytes, exp, 0, code);
+ /* We need to store the byte order in effect in case we're going
+ to fix an 8 or 16 bit relocation (for which there no real
+ relocs available). See md_apply_fix(). */
+ fix->tc_fix_data.bigendian = target_big_endian;
+}
+
+/* Return the actual relocation we wish to associate with the pseudo
+ reloc described by SYM and R_TYPE. SYM should be one of the
+ symbols in the pseudo_func array, or NULL. */
+
+static bfd_reloc_code_real_type
+ia64_gen_real_reloc_type (struct symbol *sym, bfd_reloc_code_real_type r_type)
+{
+ bfd_reloc_code_real_type newr = 0;
+ const char *type = NULL, *suffix = "";
+
+ if (sym == NULL)
+ {
+ return r_type;
+ }
+
+ switch (S_GET_VALUE (sym))
+ {
+ case FUNC_FPTR_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM64: newr = BFD_RELOC_IA64_FPTR64I; break;
+ case BFD_RELOC_IA64_DIR32MSB: newr = BFD_RELOC_IA64_FPTR32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB: newr = BFD_RELOC_IA64_FPTR32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_FPTR64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_FPTR64LSB; break;
+ default: type = "FPTR"; break;
+ }
+ break;
+
+ case FUNC_GP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22: newr = BFD_RELOC_IA64_GPREL22; break;
+ case BFD_RELOC_IA64_IMM64: newr = BFD_RELOC_IA64_GPREL64I; break;
+ case BFD_RELOC_IA64_DIR32MSB: newr = BFD_RELOC_IA64_GPREL32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB: newr = BFD_RELOC_IA64_GPREL32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_GPREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_GPREL64LSB; break;
+ default: type = "GPREL"; break;
+ }
+ break;
+
+ case FUNC_LT_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22: newr = BFD_RELOC_IA64_LTOFF22; break;
+ case BFD_RELOC_IA64_IMM64: newr = BFD_RELOC_IA64_LTOFF64I; break;
+ default: type = "LTOFF"; break;
+ }
+ break;
+
+ case FUNC_LT_RELATIVE_X:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22: newr = BFD_RELOC_IA64_LTOFF22X; break;
+ default: type = "LTOFF"; suffix = "X"; break;
+ }
+ break;
+
+ case FUNC_PC_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22: newr = BFD_RELOC_IA64_PCREL22; break;
+ case BFD_RELOC_IA64_IMM64: newr = BFD_RELOC_IA64_PCREL64I; break;
+ case BFD_RELOC_IA64_DIR32MSB: newr = BFD_RELOC_IA64_PCREL32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB: newr = BFD_RELOC_IA64_PCREL32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_PCREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_PCREL64LSB; break;
+ default: type = "PCREL"; break;
+ }
+ break;
+
+ case FUNC_PLT_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22: newr = BFD_RELOC_IA64_PLTOFF22; break;
+ case BFD_RELOC_IA64_IMM64: newr = BFD_RELOC_IA64_PLTOFF64I; break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_PLTOFF64MSB;break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_PLTOFF64LSB;break;
+ default: type = "PLTOFF"; break;
+ }
+ break;
+
+ case FUNC_SEC_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB: newr = BFD_RELOC_IA64_SECREL32MSB;break;
+ case BFD_RELOC_IA64_DIR32LSB: newr = BFD_RELOC_IA64_SECREL32LSB;break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_SECREL64MSB;break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_SECREL64LSB;break;
+ default: type = "SECREL"; break;
+ }
+ break;
+
+ case FUNC_SEG_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB: newr = BFD_RELOC_IA64_SEGREL32MSB;break;
+ case BFD_RELOC_IA64_DIR32LSB: newr = BFD_RELOC_IA64_SEGREL32LSB;break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_SEGREL64MSB;break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_SEGREL64LSB;break;
+ default: type = "SEGREL"; break;
+ }
+ break;
+
+ case FUNC_LTV_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB: newr = BFD_RELOC_IA64_LTV32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB: newr = BFD_RELOC_IA64_LTV32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_LTV64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_LTV64LSB; break;
+ default: type = "LTV"; break;
+ }
+ break;
+
+ case FUNC_LT_FPTR_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ newr = BFD_RELOC_IA64_LTOFF_FPTR22; break;
+ case BFD_RELOC_IA64_IMM64:
+ newr = BFD_RELOC_IA64_LTOFF_FPTR64I; break;
+ case BFD_RELOC_IA64_DIR32MSB:
+ newr = BFD_RELOC_IA64_LTOFF_FPTR32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB:
+ newr = BFD_RELOC_IA64_LTOFF_FPTR32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB:
+ newr = BFD_RELOC_IA64_LTOFF_FPTR64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ newr = BFD_RELOC_IA64_LTOFF_FPTR64LSB; break;
+ default:
+ type = "LTOFF_FPTR"; break;
+ }
+ break;
+
+ case FUNC_TP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM14: newr = BFD_RELOC_IA64_TPREL14; break;
+ case BFD_RELOC_IA64_IMM22: newr = BFD_RELOC_IA64_TPREL22; break;
+ case BFD_RELOC_IA64_IMM64: newr = BFD_RELOC_IA64_TPREL64I; break;
+ case BFD_RELOC_IA64_DIR64MSB: newr = BFD_RELOC_IA64_TPREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB: newr = BFD_RELOC_IA64_TPREL64LSB; break;
+ default: type = "TPREL"; break;
+ }
+ break;
+
+ case FUNC_LT_TP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ newr = BFD_RELOC_IA64_LTOFF_TPREL22; break;
+ default:
+ type = "LTOFF_TPREL"; break;
+ }
+ break;
+
+ case FUNC_DTP_MODULE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR64MSB:
+ newr = BFD_RELOC_IA64_DTPMOD64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ newr = BFD_RELOC_IA64_DTPMOD64LSB; break;
+ default:
+ type = "DTPMOD"; break;
+ }
+ break;
+
+ case FUNC_LT_DTP_MODULE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ newr = BFD_RELOC_IA64_LTOFF_DTPMOD22; break;
+ default:
+ type = "LTOFF_DTPMOD"; break;
+ }
+ break;
+
+ case FUNC_DTP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB:
+ newr = BFD_RELOC_IA64_DTPREL32MSB; break;
+ case BFD_RELOC_IA64_DIR32LSB:
+ newr = BFD_RELOC_IA64_DTPREL32LSB; break;
+ case BFD_RELOC_IA64_DIR64MSB:
+ newr = BFD_RELOC_IA64_DTPREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ newr = BFD_RELOC_IA64_DTPREL64LSB; break;
+ case BFD_RELOC_IA64_IMM14:
+ newr = BFD_RELOC_IA64_DTPREL14; break;
+ case BFD_RELOC_IA64_IMM22:
+ newr = BFD_RELOC_IA64_DTPREL22; break;
+ case BFD_RELOC_IA64_IMM64:
+ newr = BFD_RELOC_IA64_DTPREL64I; break;
+ default:
+ type = "DTPREL"; break;
+ }
+ break;
+
+ case FUNC_LT_DTP_RELATIVE:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IMM22:
+ newr = BFD_RELOC_IA64_LTOFF_DTPREL22; break;
+ default:
+ type = "LTOFF_DTPREL"; break;
+ }
+ break;
+
+ case FUNC_IPLT_RELOC:
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_IPLTMSB: return r_type;
+ case BFD_RELOC_IA64_IPLTLSB: return r_type;
+ default: type = "IPLT"; break;
+ }
+ break;
+
+#ifdef TE_VMS
+ case FUNC_SLOTCOUNT_RELOC:
+ return DUMMY_RELOC_IA64_SLOTCOUNT;
+#endif
+
+ default:
+ abort ();
+ }
+
+ if (newr)
+ return newr;
+ else
+ {
+ int width;
+
+ if (!type)
+ abort ();
+ switch (r_type)
+ {
+ case BFD_RELOC_IA64_DIR32MSB: width = 32; suffix = "MSB"; break;
+ case BFD_RELOC_IA64_DIR32LSB: width = 32; suffix = "LSB"; break;
+ case BFD_RELOC_IA64_DIR64MSB: width = 64; suffix = "MSB"; break;
+ case BFD_RELOC_IA64_DIR64LSB: width = 64; suffix = "LSB"; break;
+ case BFD_RELOC_UNUSED: width = 13; break;
+ case BFD_RELOC_IA64_IMM14: width = 14; break;
+ case BFD_RELOC_IA64_IMM22: width = 22; break;
+ case BFD_RELOC_IA64_IMM64: width = 64; suffix = "I"; break;
+ default: abort ();
+ }
+
+ /* This should be an error, but since previously there wasn't any
+ diagnostic here, don't make it fail because of this for now. */
+ as_warn (_("Cannot express %s%d%s relocation"), type, width, suffix);
+ return r_type;
+ }
+}
+
+/* Here is where generate the appropriate reloc for pseudo relocation
+ functions. */
+void
+ia64_validate_fix (fixS *fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_IA64_FPTR64I:
+ case BFD_RELOC_IA64_FPTR32MSB:
+ case BFD_RELOC_IA64_FPTR64LSB:
+ case BFD_RELOC_IA64_LTOFF_FPTR22:
+ case BFD_RELOC_IA64_LTOFF_FPTR64I:
+ if (fix->fx_offset != 0)
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("No addend allowed in @fptr() relocation"));
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+fix_insn (fixS *fix, const struct ia64_operand *odesc, valueT value)
+{
+ bfd_vma insn[3], t0, t1, control_bits;
+ const char *err;
+ char *fixpos;
+ long slot;
+
+ slot = fix->fx_where & 0x3;
+ fixpos = fix->fx_frag->fr_literal + (fix->fx_where - slot);
+
+ /* Bundles are always in little-endian byte order */
+ t0 = bfd_getl64 (fixpos);
+ t1 = bfd_getl64 (fixpos + 8);
+ control_bits = t0 & 0x1f;
+ insn[0] = (t0 >> 5) & 0x1ffffffffffLL;
+ insn[1] = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18);
+ insn[2] = (t1 >> 23) & 0x1ffffffffffLL;
+
+ err = NULL;
+ if (odesc - elf64_ia64_operands == IA64_OPND_IMMU64)
+ {
+ insn[1] = (value >> 22) & 0x1ffffffffffLL;
+ insn[2] |= (((value & 0x7f) << 13)
+ | (((value >> 7) & 0x1ff) << 27)
+ | (((value >> 16) & 0x1f) << 22)
+ | (((value >> 21) & 0x1) << 21)
+ | (((value >> 63) & 0x1) << 36));
+ }
+ else if (odesc - elf64_ia64_operands == IA64_OPND_IMMU62)
+ {
+ if (value & ~0x3fffffffffffffffULL)
+ err = _("integer operand out of range");
+ insn[1] = (value >> 21) & 0x1ffffffffffLL;
+ insn[2] |= (((value & 0xfffff) << 6) | (((value >> 20) & 0x1) << 36));
+ }
+ else if (odesc - elf64_ia64_operands == IA64_OPND_TGT64)
+ {
+ value >>= 4;
+ insn[1] = ((value >> 20) & 0x7fffffffffLL) << 2;
+ insn[2] |= ((((value >> 59) & 0x1) << 36)
+ | (((value >> 0) & 0xfffff) << 13));
+ }
+ else
+ err = (*odesc->insert) (odesc, value, insn + slot);
+
+ if (err)
+ as_bad_where (fix->fx_file, fix->fx_line, "%s", err);
+
+ t0 = control_bits | (insn[0] << 5) | (insn[1] << 46);
+ t1 = ((insn[1] >> 18) & 0x7fffff) | (insn[2] << 23);
+ number_to_chars_littleendian (fixpos + 0, t0, 8);
+ number_to_chars_littleendian (fixpos + 8, t1, 8);
+}
+
+/* Attempt to simplify or even eliminate a fixup. The return value is
+ ignored; perhaps it was once meaningful, but now it is historical.
+ To indicate that a fixup has been eliminated, set FIXP->FX_DONE.
+
+ If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry
+ (if possible). */
+
+void
+md_apply_fix (fixS *fix, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *fixpos;
+ valueT value = *valP;
+
+ fixpos = fix->fx_frag->fr_literal + fix->fx_where;
+
+ if (fix->fx_pcrel)
+ {
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_IA64_PCREL21B: break;
+ case BFD_RELOC_IA64_PCREL21BI: break;
+ case BFD_RELOC_IA64_PCREL21F: break;
+ case BFD_RELOC_IA64_PCREL21M: break;
+ case BFD_RELOC_IA64_PCREL60B: break;
+ case BFD_RELOC_IA64_PCREL22: break;
+ case BFD_RELOC_IA64_PCREL64I: break;
+ case BFD_RELOC_IA64_PCREL32MSB: break;
+ case BFD_RELOC_IA64_PCREL32LSB: break;
+ case BFD_RELOC_IA64_PCREL64MSB: break;
+ case BFD_RELOC_IA64_PCREL64LSB: break;
+ default:
+ fix->fx_r_type = ia64_gen_real_reloc_type (pseudo_func[FUNC_PC_RELATIVE].u.sym,
+ fix->fx_r_type);
+ break;
+ }
+ }
+ if (fix->fx_addsy)
+ {
+ switch ((unsigned) fix->fx_r_type)
+ {
+ case BFD_RELOC_UNUSED:
+ /* This must be a TAG13 or TAG13b operand. There are no external
+ relocs defined for them, so we must give an error. */
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("%s must have a constant value"),
+ elf64_ia64_operands[fix->tc_fix_data.opnd].desc);
+ fix->fx_done = 1;
+ return;
+
+ case BFD_RELOC_IA64_TPREL14:
+ case BFD_RELOC_IA64_TPREL22:
+ case BFD_RELOC_IA64_TPREL64I:
+ case BFD_RELOC_IA64_LTOFF_TPREL22:
+ case BFD_RELOC_IA64_LTOFF_DTPMOD22:
+ case BFD_RELOC_IA64_DTPREL14:
+ case BFD_RELOC_IA64_DTPREL22:
+ case BFD_RELOC_IA64_DTPREL64I:
+ case BFD_RELOC_IA64_LTOFF_DTPREL22:
+ S_SET_THREAD_LOCAL (fix->fx_addsy);
+ break;
+
+#ifdef TE_VMS
+ case DUMMY_RELOC_IA64_SLOTCOUNT:
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("cannot resolve @slotcount parameter"));
+ fix->fx_done = 1;
+ return;
+#endif
+
+ default:
+ break;
+ }
+ }
+ else if (fix->tc_fix_data.opnd == IA64_OPND_NIL)
+ {
+#ifdef TE_VMS
+ if (fix->fx_r_type == DUMMY_RELOC_IA64_SLOTCOUNT)
+ {
+ /* For @slotcount, convert an addresses difference to a slots
+ difference. */
+ valueT v;
+
+ v = (value >> 4) * 3;
+ switch (value & 0x0f)
+ {
+ case 0:
+ case 1:
+ case 2:
+ v += value & 0x0f;
+ break;
+ case 0x0f:
+ v += 2;
+ break;
+ case 0x0e:
+ v += 1;
+ break;
+ default:
+ as_bad (_("invalid @slotcount value"));
+ }
+ value = v;
+ }
+#endif
+
+ if (fix->tc_fix_data.bigendian)
+ number_to_chars_bigendian (fixpos, value, fix->fx_size);
+ else
+ number_to_chars_littleendian (fixpos, value, fix->fx_size);
+ fix->fx_done = 1;
+ }
+ else
+ {
+ fix_insn (fix, elf64_ia64_operands + fix->tc_fix_data.opnd, value);
+ fix->fx_done = 1;
+ }
+}
+
+/* Generate the BFD reloc to be stuck in the object file from the
+ fixup used internally in the assembler. */
+
+arelent *
+tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (*reloc));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = fixp->fx_offset;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (!reloc->howto)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent %s relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ free (reloc);
+ return NULL;
+ }
+ return reloc;
+}
+
+/* Turn a string in input_line_pointer into a floating point constant
+ of type TYPE, and store the appropriate bytes in *LIT. The number
+ of LITTLENUMS emitted is stored in *SIZE. An error message is
+ returned, or NULL on OK. */
+
+#define MAX_LITTLENUMS 5
+
+char *
+md_atof (int type, char *lit, int *size)
+{
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char *t;
+ int prec;
+
+ switch (type)
+ {
+ /* IEEE floats */
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ case 'p':
+ case 'P':
+ prec = 5;
+ break;
+
+ default:
+ *size = 0;
+ return _("Unrecognized or unsupported floating point constant");
+ }
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+
+ (*ia64_float_to_chars) (lit, words, prec);
+
+ if (type == 'X')
+ {
+ /* It is 10 byte floating point with 6 byte padding. */
+ memset (&lit [10], 0, 6);
+ *size = 8 * sizeof (LITTLENUM_TYPE);
+ }
+ else
+ *size = prec * sizeof (LITTLENUM_TYPE);
+
+ return NULL;
+}
+
+/* Handle ia64 specific semantics of the align directive. */
+
+void
+ia64_md_do_align (int n ATTRIBUTE_UNUSED,
+ const char *fill ATTRIBUTE_UNUSED,
+ int len ATTRIBUTE_UNUSED,
+ int max ATTRIBUTE_UNUSED)
+{
+ if (subseg_text_p (now_seg))
+ ia64_flush_insns ();
+}
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+ia64_handle_align (fragS *fragp)
+{
+ int bytes;
+ char *p;
+ const unsigned char *nop_type;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ /* Check if this frag has to end with a stop bit. */
+ nop_type = fragp->tc_frag_data ? le_nop_stop : le_nop;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+
+ /* If no paddings are needed, we check if we need a stop bit. */
+ if (!bytes && fragp->tc_frag_data)
+ {
+ if (fragp->fr_fix < 16)
+#if 1
+ /* FIXME: It won't work with
+ .align 16
+ alloc r32=ar.pfs,1,2,4,0
+ */
+ ;
+#else
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("Can't add stop bit to mark end of instruction group"));
+#endif
+ else
+ /* Bundles are always in little-endian byte order. Make sure
+ the previous bundle has the stop bit. */
+ *(p - 16) |= 1;
+ }
+
+ /* Make sure we are on a 16-byte boundary, in case someone has been
+ putting data into a text section. */
+ if (bytes & 15)
+ {
+ int fix = bytes & 15;
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ fragp->fr_fix += fix;
+ }
+
+ /* Instruction bundles are always little-endian. */
+ memcpy (p, nop_type, 16);
+ fragp->fr_var = 16;
+}
+
+static void
+ia64_float_to_chars_bigendian (char *lit, LITTLENUM_TYPE *words,
+ int prec)
+{
+ while (prec--)
+ {
+ number_to_chars_bigendian (lit, (long) (*words++),
+ sizeof (LITTLENUM_TYPE));
+ lit += sizeof (LITTLENUM_TYPE);
+ }
+}
+
+static void
+ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words,
+ int prec)
+{
+ while (prec--)
+ {
+ number_to_chars_littleendian (lit, (long) (words[prec]),
+ sizeof (LITTLENUM_TYPE));
+ lit += sizeof (LITTLENUM_TYPE);
+ }
+}
+
+void
+ia64_elf_section_change_hook (void)
+{
+ if (elf_section_type (now_seg) == SHT_IA_64_UNWIND
+ && elf_linked_to_section (now_seg) == NULL)
+ elf_linked_to_section (now_seg) = text_section;
+ dot_byteorder (-1);
+}
+
+/* Check if a label should be made global. */
+void
+ia64_check_label (symbolS *label)
+{
+ if (*input_line_pointer == ':')
+ {
+ S_SET_EXTERNAL (label);
+ input_line_pointer++;
+ }
+}
+
+/* Used to remember where .alias and .secalias directives are seen. We
+ will rename symbol and section names when we are about to output
+ the relocatable file. */
+struct alias
+{
+ char *file; /* The file where the directive is seen. */
+ unsigned int line; /* The line number the directive is at. */
+ const char *name; /* The original name of the symbol. */
+};
+
+/* Called for .alias and .secalias directives. If SECTION is 1, it is
+ .secalias. Otherwise, it is .alias. */
+static void
+dot_alias (int section)
+{
+ char *name, *alias;
+ char delim;
+ char *end_name;
+ int len;
+ const char *error_string;
+ struct alias *h;
+ const char *a;
+ struct hash_control *ahash, *nhash;
+ const char *kind;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = delim;
+
+ if (name == end_name)
+ {
+ as_bad (_("expected symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ *end_name = 0;
+ as_bad (_("expected comma after \"%s\""), name);
+ *end_name = delim;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++;
+ *end_name = 0;
+ ia64_canonicalize_symbol_name (name);
+
+ /* We call demand_copy_C_string to check if alias string is valid.
+ There should be a closing `"' and no `\0' in the string. */
+ alias = demand_copy_C_string (&len);
+ if (alias == NULL)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Make a copy of name string. */
+ len = strlen (name) + 1;
+ obstack_grow (&notes, name, len);
+ name = obstack_finish (&notes);
+
+ if (section)
+ {
+ kind = "section";
+ ahash = secalias_hash;
+ nhash = secalias_name_hash;
+ }
+ else
+ {
+ kind = "symbol";
+ ahash = alias_hash;
+ nhash = alias_name_hash;
+ }
+
+ /* Check if alias has been used before. */
+ h = (struct alias *) hash_find (ahash, alias);
+ if (h)
+ {
+ if (strcmp (h->name, name))
+ as_bad (_("`%s' is already the alias of %s `%s'"),
+ alias, kind, h->name);
+ goto out;
+ }
+
+ /* Check if name already has an alias. */
+ a = (const char *) hash_find (nhash, name);
+ if (a)
+ {
+ if (strcmp (a, alias))
+ as_bad (_("%s `%s' already has an alias `%s'"), kind, name, a);
+ goto out;
+ }
+
+ h = (struct alias *) xmalloc (sizeof (struct alias));
+ as_where (&h->file, &h->line);
+ h->name = name;
+
+ error_string = hash_jam (ahash, alias, (void *) h);
+ if (error_string)
+ {
+ as_fatal (_("inserting \"%s\" into %s alias hash table failed: %s"),
+ alias, kind, error_string);
+ goto out;
+ }
+
+ error_string = hash_jam (nhash, name, (void *) alias);
+ if (error_string)
+ {
+ as_fatal (_("inserting \"%s\" into %s name hash table failed: %s"),
+ alias, kind, error_string);
+out:
+ obstack_free (&notes, name);
+ obstack_free (&notes, alias);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* It renames the original symbol name to its alias. */
+static void
+do_alias (const char *alias, void *value)
+{
+ struct alias *h = (struct alias *) value;
+ symbolS *sym = symbol_find (h->name);
+
+ if (sym == NULL)
+ {
+#ifdef TE_VMS
+ /* Uses .alias extensively to alias CRTL functions to same with
+ decc$ prefix. Sometimes function gets optimized away and a
+ warning results, which should be suppressed. */
+ if (strncmp (alias, "decc$", 5) != 0)
+#endif
+ as_warn_where (h->file, h->line,
+ _("symbol `%s' aliased to `%s' is not used"),
+ h->name, alias);
+ }
+ else
+ S_SET_NAME (sym, (char *) alias);
+}
+
+/* Called from write_object_file. */
+void
+ia64_adjust_symtab (void)
+{
+ hash_traverse (alias_hash, do_alias);
+}
+
+/* It renames the original section name to its alias. */
+static void
+do_secalias (const char *alias, void *value)
+{
+ struct alias *h = (struct alias *) value;
+ segT sec = bfd_get_section_by_name (stdoutput, h->name);
+
+ if (sec == NULL)
+ as_warn_where (h->file, h->line,
+ _("section `%s' aliased to `%s' is not used"),
+ h->name, alias);
+ else
+ sec->name = alias;
+}
+
+/* Called from write_object_file. */
+void
+ia64_frob_file (void)
+{
+ hash_traverse (secalias_hash, do_secalias);
+}
+
+#ifdef TE_VMS
+#define NT_VMS_MHD 1
+#define NT_VMS_LNM 2
+
+/* Integrity VMS 8.x identifies it's ELF modules with a standard ELF
+ .note section. */
+
+/* Manufacture a VMS-like time string. */
+static void
+get_vms_time (char *Now)
+{
+ char *pnt;
+ time_t timeb;
+
+ time (&timeb);
+ pnt = ctime (&timeb);
+ pnt[3] = 0;
+ pnt[7] = 0;
+ pnt[10] = 0;
+ pnt[16] = 0;
+ pnt[24] = 0;
+ sprintf (Now, "%2s-%3s-%s %s", pnt + 8, pnt + 4, pnt + 20, pnt + 11);
+}
+
+void
+ia64_vms_note (void)
+{
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ asection *secp = NULL;
+ char *bname;
+ char buf [256];
+ symbolS *sym;
+
+ /* Create the .note section. */
+
+ secp = subseg_new (".note", 0);
+ bfd_set_section_flags (stdoutput,
+ secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* Module header note (MHD). */
+ bname = xstrdup (lbasename (out_file_name));
+ if ((p = strrchr (bname, '.')))
+ *p = '\0';
+
+ /* VMS note header is 24 bytes long. */
+ p = frag_more (8 + 8 + 8);
+ number_to_chars_littleendian (p + 0, 8, 8);
+ number_to_chars_littleendian (p + 8, 40 + strlen (bname), 8);
+ number_to_chars_littleendian (p + 16, NT_VMS_MHD, 8);
+
+ p = frag_more (8);
+ strcpy (p, "IPF/VMS");
+
+ p = frag_more (17 + 17 + strlen (bname) + 1 + 5);
+ get_vms_time (p);
+ strcpy (p + 17, "24-FEB-2005 15:00");
+ p += 17 + 17;
+ strcpy (p, bname);
+ p += strlen (bname) + 1;
+ free (bname);
+ strcpy (p, "V1.0");
+
+ frag_align (3, 0, 0);
+
+ /* Language processor name note. */
+ sprintf (buf, "GNU assembler version %s (%s) using BFD version %s",
+ VERSION, TARGET_ALIAS, BFD_VERSION_STRING);
+
+ p = frag_more (8 + 8 + 8);
+ number_to_chars_littleendian (p + 0, 8, 8);
+ number_to_chars_littleendian (p + 8, strlen (buf) + 1, 8);
+ number_to_chars_littleendian (p + 16, NT_VMS_LNM, 8);
+
+ p = frag_more (8);
+ strcpy (p, "IPF/VMS");
+
+ p = frag_more (strlen (buf) + 1);
+ strcpy (p, buf);
+
+ frag_align (3, 0, 0);
+
+ secp = subseg_new (".vms_display_name_info", 0);
+ bfd_set_section_flags (stdoutput,
+ secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ /* This symbol should be passed on the command line and be variable
+ according to language. */
+ sym = symbol_new ("__gnat_vms_display_name@gnat_demangler_rtl",
+ absolute_section, 0, &zero_address_frag);
+ symbol_table_insert (sym);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING | BSF_DYNAMIC;
+
+ p = frag_more (4);
+ /* Format 3 of VMS demangler Spec. */
+ number_to_chars_littleendian (p, 3, 4);
+
+ p = frag_more (4);
+ /* Place holder for symbol table index of above symbol. */
+ number_to_chars_littleendian (p, -1, 4);
+
+ frag_align (3, 0, 0);
+
+ /* We probably can't restore the current segment, for there likely
+ isn't one yet... */
+ if (seg && subseg)
+ subseg_set (seg, subseg);
+}
+
+#endif /* TE_VMS */
diff --git a/gas/config/tc-ia64.h b/gas/config/tc-ia64.h
new file mode 100644
index 0000000..6884c59
--- /dev/null
+++ b/gas/config/tc-ia64.h
@@ -0,0 +1,331 @@
+/* tc-ia64.h -- Header file for tc-ia64.c.
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+ Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "opcode/ia64.h"
+#include "elf/ia64.h"
+
+#define TC_IA64
+
+/* Linux is little endian by default. HPUX is big endian by default. */
+#ifdef TE_HPUX
+#define TARGET_BYTES_BIG_ENDIAN 1
+#define MD_FLAGS_DEFAULT EF_IA_64_BE
+#else
+#define TARGET_BYTES_BIG_ENDIAN 0
+#define MD_FLAGS_DEFAULT EF_IA_64_ABI64
+#endif /* TE_HPUX */
+
+extern void (*ia64_number_to_chars) (char *, valueT, int);
+#define md_number_to_chars (*ia64_number_to_chars)
+
+extern void ia64_elf_section_change_hook (void);
+#define md_elf_section_change_hook ia64_elf_section_change_hook
+
+/* We record the endian for this section. 0 means default, 1 means
+ big endian and 2 means little endian. */
+struct ia64_segment_info_type
+{
+ unsigned int endian : 2;
+};
+
+#define TC_SEGMENT_INFO_TYPE struct ia64_segment_info_type
+
+extern void ia64_adjust_symtab (void);
+#define tc_adjust_symtab() ia64_adjust_symtab ()
+
+extern void ia64_frob_file (void);
+#define tc_frob_file() ia64_frob_file ()
+
+/* We need to set the default object file format in ia64_init and not in
+ md_begin. This is because parse_args is called before md_begin, and we
+ do not want md_begin to wipe out the flag settings set by options parsed in
+ md_parse_args. */
+
+#define HOST_SPECIAL_INIT ia64_init
+extern void ia64_init (int, char **);
+
+#define TARGET_FORMAT ia64_target_format()
+extern const char *ia64_target_format (void);
+
+#define TARGET_ARCH bfd_arch_ia64
+#define DOUBLESLASH_LINE_COMMENTS /* allow //-style comments */
+
+#define NEED_LITERAL_POOL /* need gp literal pool */
+#define RELOC_REQUIRES_SYMBOL
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+#define NEED_INDEX_OPERATOR /* [ ] is index operator */
+
+#define QUOTES_IN_INSN /* allow `string "foo;bar"' */
+#define LEX_AT (LEX_NAME|LEX_BEGIN_NAME) /* allow `@' inside name */
+#define LEX_QM (LEX_NAME|LEX_BEGIN_NAME) /* allow `?' inside name */
+#define LEX_HASH LEX_END_NAME /* allow `#' ending a name */
+
+#define TC_PREDICATE_START_CHAR '('
+#define TC_PREDICATE_END_CHAR ')'
+
+extern const char ia64_symbol_chars[];
+#define tc_symbol_chars ia64_symbol_chars
+
+#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
+
+struct ia64_fix
+ {
+ int bigendian; /* byte order at fix location */
+ enum ia64_opnd opnd;
+ };
+
+extern void ia64_end_of_source (void);
+extern void ia64_start_line (void);
+extern int ia64_unrecognized_line (int);
+extern void ia64_frob_label (struct symbol *);
+#ifdef TE_HPUX
+extern int ia64_frob_symbol (struct symbol *);
+#endif
+extern void ia64_flush_pending_output (void);
+extern int ia64_parse_name (char *, expressionS *, char *);
+extern int ia64_optimize_expr (expressionS *, operatorT, expressionS *);
+extern void ia64_cons_align (int);
+extern void ia64_flush_insns (void);
+extern int ia64_fix_adjustable (struct fix *);
+extern int ia64_force_relocation (struct fix *);
+extern void ia64_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+extern void ia64_validate_fix (struct fix *);
+extern char * ia64_canonicalize_symbol_name (char *);
+extern bfd_vma ia64_elf_section_letter (int, char **);
+extern flagword ia64_elf_section_flags (flagword, bfd_vma, int);
+extern int ia64_elf_section_type (const char *, size_t);
+extern long ia64_pcrel_from_section (struct fix *, segT);
+extern void ia64_md_do_align (int, const char *, int, int);
+extern void ia64_handle_align (fragS *);
+extern void ia64_after_parse_args (void);
+extern void ia64_dwarf2_emit_offset (symbolS *, unsigned int);
+extern void ia64_check_label (symbolS *);
+extern int ia64_estimate_size_before_relax (fragS *, asection *);
+extern void ia64_convert_frag (fragS *);
+
+#define md_end() ia64_end_of_source ()
+#define md_start_line_hook() ia64_start_line ()
+#define tc_unrecognized_line(ch) ia64_unrecognized_line (ch)
+#define tc_frob_label(s) ia64_frob_label (s)
+#ifdef TE_HPUX
+#define tc_frob_symbol(s,p) p |= ia64_frob_symbol (s)
+#endif /* TE_HPUX */
+#define md_flush_pending_output() ia64_flush_pending_output ()
+#define md_parse_name(s,e,m,c) ia64_parse_name (s, e, c)
+#define md_register_arithmetic 0
+#define tc_canonicalize_symbol_name(s) ia64_canonicalize_symbol_name (s)
+#define tc_canonicalize_section_name(s) ia64_canonicalize_symbol_name (s)
+#define md_optimize_expr(l,o,r) ia64_optimize_expr (l, o, r)
+#define md_cons_align(n) ia64_cons_align (n)
+#define TC_FORCE_RELOCATION(f) ia64_force_relocation (f)
+#define tc_fix_adjustable(f) ia64_fix_adjustable (f)
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#define md_convert_frag(b,s,f) ia64_convert_frag (f)
+#define md_create_long_jump(p,f,t,fr,s) as_fatal ("ia64_create_long_jump")
+#define md_create_short_jump(p,f,t,fr,s) \
+ as_fatal ("ia64_create_short_jump")
+#define md_estimate_size_before_relax(f,s) \
+ ia64_estimate_size_before_relax(f,s)
+#define md_elf_section_letter ia64_elf_section_letter
+#define md_elf_section_flags ia64_elf_section_flags
+#define TC_FIX_TYPE struct ia64_fix
+#define TC_INIT_FIX_DATA(f) { f->tc_fix_data.opnd = 0; }
+#define TC_CONS_FIX_NEW(f,o,l,e,r) ia64_cons_fix_new (f, o, l, e, r)
+#define TC_VALIDATE_FIX(fix,seg,skip) ia64_validate_fix (fix)
+#define MD_PCREL_FROM_SECTION(fix,sec) ia64_pcrel_from_section (fix, sec)
+#define md_section_align(seg,size) (size)
+#define md_do_align(n,f,l,m,j) ia64_md_do_align (n,f,l,m)
+#define HANDLE_ALIGN(f) ia64_handle_align (f)
+#define md_elf_section_type(str,len) ia64_elf_section_type (str, len)
+#define md_after_parse_args() ia64_after_parse_args ()
+#define TC_DWARF2_EMIT_OFFSET ia64_dwarf2_emit_offset
+#define tc_check_label(l) ia64_check_label (l)
+#ifdef TE_VMS
+#define tc_init_after_args() ia64_vms_note ()
+void ia64_vms_note (void);
+#endif
+
+/* Record if an alignment frag should end with a stop bit. */
+#define TC_FRAG_TYPE int
+#define TC_FRAG_INIT(FRAGP) do {(FRAGP)->tc_frag_data = 0;}while (0)
+
+/* Give an error if a frag containing code is not aligned to a 16 byte
+ boundary. */
+#define md_frag_check(FRAGP) \
+ if ((FRAGP)->has_code \
+ && (((FRAGP)->fr_address + (FRAGP)->insn_addr) & 15) != 0) \
+ as_bad_where ((FRAGP)->fr_file, (FRAGP)->fr_line, \
+ _("instruction address is not a multiple of 16"));
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (15 + 16)
+
+#define WORKING_DOT_WORD /* don't do broken word processing for now */
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1 /* so slot-multipliers can be 1 */
+
+/* This is the information required for unwind records in an ia64
+ object file. This is required by GAS and the compiler runtime. */
+
+/* These are the starting point masks for the various types of
+ unwind records. To create a record of type R3 for instance, one
+ starts by using the value UNW_R3 and or-ing in any other required values.
+ These values are also unique (in context), so they can be used to identify
+ the various record types as well. UNW_Bx and some UNW_Px do have the
+ same value, but Px can only occur in a prologue context, and Bx in
+ a body context. */
+
+#define UNW_R1 0x00
+#define UNW_R2 0x40
+#define UNW_R3 0x60
+#define UNW_P1 0x80
+#define UNW_P2 0xA0
+#define UNW_P3 0xB0
+#define UNW_P4 0xB8
+#define UNW_P5 0xB9
+#define UNW_P6 0xC0
+#define UNW_P7 0xE0
+#define UNW_P8 0xF0
+#define UNW_P9 0xF1
+#define UNW_P10 0xFF
+#define UNW_X1 0xF9
+#define UNW_X2 0xFA
+#define UNW_X3 0xFB
+#define UNW_X4 0xFC
+#define UNW_B1 0x80
+#define UNW_B2 0xC0
+#define UNW_B3 0xE0
+#define UNW_B4 0xF0
+
+/* These are all the various types of unwind records. */
+
+typedef enum
+{
+ prologue, prologue_gr, body, mem_stack_f, mem_stack_v, psp_gr, psp_sprel,
+ rp_when, rp_gr, rp_br, rp_psprel, rp_sprel, pfs_when, pfs_gr, pfs_psprel,
+ pfs_sprel, preds_when, preds_gr, preds_psprel, preds_sprel,
+ fr_mem, frgr_mem, gr_gr, gr_mem, br_mem, br_gr, spill_base, spill_mask,
+ unat_when, unat_gr, unat_psprel, unat_sprel, lc_when, lc_gr, lc_psprel,
+ lc_sprel, fpsr_when, fpsr_gr, fpsr_psprel, fpsr_sprel,
+ priunat_when_gr, priunat_when_mem, priunat_gr, priunat_psprel,
+ priunat_sprel, bsp_when, bsp_gr, bsp_psprel, bsp_sprel, bspstore_when,
+ bspstore_gr, bspstore_psprel, bspstore_sprel, rnat_when, rnat_gr,
+ rnat_psprel, rnat_sprel, epilogue, label_state, copy_state,
+ spill_psprel, spill_sprel, spill_reg, spill_psprel_p, spill_sprel_p,
+ spill_reg_p, unwabi, endp
+} unw_record_type;
+
+/* These structures declare the fields that can be used in each of the
+ 4 record formats, R, P, B and X. */
+
+typedef struct unw_r_record
+{
+ unsigned long rlen;
+ unsigned short grmask;
+ unsigned short grsave;
+ /* masks to represent the union of save.g, save.f, save.b, and
+ save.gf: */
+ unsigned long imask_size;
+ struct
+ {
+ unsigned char *i;
+ unsigned int fr_mem;
+ unsigned char gr_mem;
+ unsigned char br_mem;
+ } mask;
+} unw_r_record;
+
+typedef struct unw_p_record
+{
+ struct unw_rec_list *next;
+ unsigned long t;
+ unsigned long size;
+ union
+ {
+ unsigned long sp;
+ unsigned long psp;
+ } off;
+ union
+ {
+ unsigned short gr;
+ unsigned short br;
+ } r;
+ unsigned char grmask;
+ unsigned char brmask;
+ unsigned int frmask;
+ unsigned char abi;
+ unsigned char context;
+} unw_p_record;
+
+typedef struct unw_b_record
+{
+ unsigned long t;
+ unsigned long label;
+ unsigned short ecount;
+} unw_b_record;
+
+typedef struct unw_x_record
+{
+ unsigned long t;
+ union
+ {
+ unsigned long spoff;
+ unsigned long pspoff;
+ unsigned int reg;
+ } where;
+ unsigned short reg;
+ unsigned short qp;
+ unsigned short ab; /* Value of the AB field.. */
+ unsigned short xy; /* Value of the XY field.. */
+} unw_x_record;
+
+/* This structure is used to determine the specific record type and
+ its fields. */
+typedef struct unwind_record
+{
+ unw_record_type type;
+ union {
+ unw_r_record r;
+ unw_p_record p;
+ unw_b_record b;
+ unw_x_record x;
+ } record;
+} unwind_record;
+
+/* This expression evaluates to true if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ False if we are willing to perform this relocation while building
+ the .o file. */
+
+/* If the reloc type is BFD_RELOC_UNUSED, then this is for a TAG13/TAG13b field
+ which has no external reloc, so we must resolve the value now. */
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ ((FIX)->fx_r_type != BFD_RELOC_UNUSED \
+ && (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_IA64_PLTOFF22 \
+ || TC_FORCE_RELOCATION (FIX)))
+
+/* VMS backtraces expect dwarf version 3. */
+#ifdef TE_VMS
+#define DWARF2_VERSION 3
+#define DWARF2_LINE_VERSION 3
+#endif
diff --git a/gas/config/tc-ip2k.c b/gas/config/tc-ip2k.c
new file mode 100644
index 0000000..9c8c22c
--- /dev/null
+++ b/gas/config/tc-ip2k.c
@@ -0,0 +1,426 @@
+/* tc-ip2k.c -- Assembler for the Scenix IP2xxx.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/ip2k-desc.h"
+#include "opcodes/ip2k-opc.h"
+#include "cgen.h"
+#include "elf/common.h"
+#include "elf/ip2k.h"
+#include "libbfd.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];
+}
+ip2k_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";
+
+/* Flag to detect when switching to code section where insn alignment is
+ implied. */
+static int force_code_align = 0;
+
+/* Mach selected from command line. */
+static int ip2k_mach = 0;
+static unsigned ip2k_mach_bitmask = 0;
+
+
+static void
+ip2k_elf_section_rtn (int i)
+{
+ obj_elf_section(i);
+
+ if (force_code_align)
+ {
+ /* The s_align_ptwo function expects that we are just after a .align
+ directive and it will either try and read the align value or stop
+ if end of line so we must fake it out so it thinks we are at the
+ end of the line. */
+ char *old_input_line_pointer = input_line_pointer;
+ input_line_pointer = "\n";
+ s_align_ptwo (1);
+ force_code_align = 0;
+ /* Restore. */
+ input_line_pointer = old_input_line_pointer;
+ }
+}
+
+static void
+ip2k_elf_section_text (int i)
+{
+ char *old_input_line_pointer;
+ obj_elf_text(i);
+
+ /* the s_align_ptwo function expects that we are just after a .align
+ directive and it will either try and read the align value or stop if
+ end of line so we must fake it out so it thinks we are at the end of
+ the line. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = "\n";
+ s_align_ptwo (1);
+ force_code_align = 0;
+ /* Restore. */
+ input_line_pointer = old_input_line_pointer;
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "text", ip2k_elf_section_text, 0 },
+ { "sect", ip2k_elf_section_rtn, 0 },
+ { NULL, NULL, 0 }
+};
+
+
+
+enum options
+{
+ OPTION_CPU_IP2022 = OPTION_MD_BASE,
+ OPTION_CPU_IP2022EXT
+};
+
+struct option md_longopts[] =
+{
+ { "mip2022", no_argument, NULL, OPTION_CPU_IP2022 },
+ { "mip2022ext", no_argument, NULL, OPTION_CPU_IP2022EXT },
+ { NULL, no_argument, NULL, 0 },
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+const char * md_shortopts = "";
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_CPU_IP2022:
+ ip2k_mach = bfd_mach_ip2022;
+ ip2k_mach_bitmask = 1 << MACH_IP2022;
+ break;
+
+ case OPTION_CPU_IP2022EXT:
+ ip2k_mach = bfd_mach_ip2022ext;
+ ip2k_mach_bitmask = 1 << MACH_IP2022EXT;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("IP2K specific command line options:\n"));
+ fprintf (stream, _(" -mip2022 restrict to IP2022 insns \n"));
+ fprintf (stream, _(" -mip2022ext permit extended IP2022 insn\n"));
+}
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = ip2k_cgen_cpu_open (CGEN_CPU_OPEN_MACHS,
+ ip2k_mach_bitmask,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ ip2k_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 machine type. */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_ip2k, ip2k_mach);
+}
+
+
+void
+md_assemble (char * str)
+{
+ ip2k_insn insn;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = ip2k_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Check for special relocation required by SKIP instructions. */
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SKIPA))
+ /* Unconditional skip has a 1-bit relocation of the current pc, so
+ that we emit either sb pcl.0 or snb pcl.0 depending on whether
+ the PCL (pc + 2) >> 1 is odd or even. */
+ {
+ enum cgen_parse_operand_result result_type;
+ bfd_vma value;
+ const char *curpc_plus_2 = ".+2";
+ const char *err;
+
+ err = cgen_parse_address (gas_cgen_cpu_desc, & curpc_plus_2,
+ IP2K_OPERAND_ADDR16CJP,
+ BFD_RELOC_IP2K_PC_SKIP,
+ & result_type, & value);
+ if (err)
+ {
+ as_bad ("%s", err);
+ 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);
+}
+
+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;
+}
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("relaxation not supported\n"));
+ return 1;
+}
+
+
+/* *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. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+}
+
+
+/* Functions concerning relocs. */
+
+long
+md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+
+/* 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)
+{
+ bfd_reloc_code_real_type result;
+
+ result = BFD_RELOC_NONE;
+
+ switch (operand->type)
+ {
+ case IP2K_OPERAND_FR:
+ case IP2K_OPERAND_ADDR16L:
+ case IP2K_OPERAND_ADDR16H:
+ case IP2K_OPERAND_LIT8:
+ /* These may have been processed at parse time. */
+ if (fixP->fx_cgen.opinfo != 0)
+ result = fixP->fx_cgen.opinfo;
+ fixP->fx_no_overflow = 1;
+ break;
+
+ case IP2K_OPERAND_ADDR16CJP:
+ result = fixP->fx_cgen.opinfo;
+ if (result == 0 || result == BFD_RELOC_NONE)
+ result = BFD_RELOC_IP2K_ADDR16CJP;
+ fixP->fx_no_overflow = 1;
+ break;
+
+ case IP2K_OPERAND_ADDR16P:
+ result = BFD_RELOC_IP2K_PAGE3;
+ fixP->fx_no_overflow = 1;
+ break;
+
+ default:
+ result = BFD_RELOC_NONE;
+ break;
+ }
+
+ return result;
+}
+
+
+/* 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_bigendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+
+/* See whether we need to force a relocation into the output file.
+ Force most of them, since the linker's bfd relocation engine
+ understands range limits better than gas' cgen fixup engine.
+ Consider the case of a fixup intermediate value being larger than
+ the instruction it will be eventually encoded within. */
+
+int
+ip2k_force_relocation (fixS * fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_IP2K_FR9:
+ case BFD_RELOC_IP2K_FR_OFFSET:
+ case BFD_RELOC_IP2K_BANK:
+ case BFD_RELOC_IP2K_ADDR16CJP:
+ case BFD_RELOC_IP2K_PAGE3:
+ case BFD_RELOC_IP2K_LO8DATA:
+ case BFD_RELOC_IP2K_HI8DATA:
+ case BFD_RELOC_IP2K_EX8DATA:
+ case BFD_RELOC_IP2K_LO8INSN:
+ case BFD_RELOC_IP2K_HI8INSN:
+ case BFD_RELOC_IP2K_PC_SKIP:
+ case BFD_RELOC_IP2K_TEXT:
+ return 1;
+
+ case BFD_RELOC_16:
+ if (fix->fx_subsy && S_IS_DEFINED (fix->fx_subsy)
+ && fix->fx_addsy && S_IS_DEFINED (fix->fx_addsy)
+ && (S_GET_SEGMENT (fix->fx_addsy)->flags & SEC_CODE))
+ {
+ fix->fx_r_type = BFD_RELOC_IP2K_TEXT;
+ return 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return generic_force_reloc (fix);
+}
+
+void
+ip2k_apply_fix (fixS *fixP, valueT *valueP, segT seg)
+{
+ if (fixP->fx_r_type == BFD_RELOC_IP2K_TEXT
+ && ! fixP->fx_addsy
+ && ! fixP->fx_subsy)
+ {
+ *valueP = ((int)(* valueP)) / 2;
+ fixP->fx_r_type = BFD_RELOC_16;
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_UNUSED + IP2K_OPERAND_FR)
+ {
+ /* Must be careful when we are fixing up an FR. We could be
+ fixing up an offset to (SP) or (DP) in which case we don't
+ want to step on the top 2 bits of the FR operand. The
+ gas_cgen_md_apply_fix doesn't know any better and overwrites
+ the entire operand. We counter this by adding the bits
+ to the new value. */
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ /* Canonical name, since used a lot. */
+ CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
+ CGEN_INSN_INT insn_value
+ = cgen_get_insn_value (cd, (unsigned char *) where,
+ CGEN_INSN_BITSIZE (fixP->fx_cgen.insn));
+ /* Preserve (DP) or (SP) specification. */
+ *valueP += (insn_value & 0x180);
+ }
+
+ gas_cgen_md_apply_fix (fixP, valueP, seg);
+}
+
+int
+ip2k_elf_section_flags (flagword flags,
+ bfd_vma attr ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED)
+{
+ /* This is used to detect when the section changes to an executable section.
+ This function is called by the elf section processing. When we note an
+ executable section specifier we set an internal flag to denote when
+ word alignment should be forced. */
+ if (flags & SEC_CODE)
+ force_code_align = 1;
+
+ return flags;
+}
+
diff --git a/gas/config/tc-ip2k.h b/gas/config/tc-ip2k.h
new file mode 100644
index 0000000..c33ab2b
--- /dev/null
+++ b/gas/config/tc-ip2k.h
@@ -0,0 +1,65 @@
+/* tc-ip2k.h -- Header file for tc-ip2k.c.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_IP2K
+
+#define LISTING_HEADER "IP2xxx GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_ip2k
+
+#define TARGET_FORMAT "elf32-ip2k"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define LITERAL_PREFIXDOLLAR_HEX
+#define LITERAL_PREFIXPERCENT_BIN
+#define DOUBLESLASH_LINE_COMMENTS
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_apply_fix ip2k_apply_fix
+
+#define TC_HANDLES_FX_DONE
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+#define TC_FORCE_RELOCATION(FIX) ip2k_force_relocation (FIX)
+extern int ip2k_force_relocation (struct fix *);
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+#define md_elf_section_flags ip2k_elf_section_flags
+extern int ip2k_elf_section_flags (flagword, bfd_vma, int);
+
+#define md_operand(x) gas_cgen_md_operand (x)
+extern void gas_cgen_md_operand (expressionS *);
diff --git a/gas/config/tc-iq2000.c b/gas/config/tc-iq2000.c
new file mode 100644
index 0000000..7939abc
--- /dev/null
+++ b/gas/config/tc-iq2000.c
@@ -0,0 +1,987 @@
+/* tc-iq2000.c -- Assembler for the Sitera IQ2000.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/iq2000-desc.h"
+#include "opcodes/iq2000-opc.h"
+#include "cgen.h"
+#include "elf/common.h"
+#include "elf/iq2000.h"
+#include "libbfd.h"
+#include "sb.h"
+#include "macro.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];
+}
+iq2000_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";
+
+/* Default machine. */
+#define DEFAULT_MACHINE bfd_mach_iq2000
+#define DEFAULT_FLAGS EF_IQ2000_CPU_IQ2000
+
+static unsigned long iq2000_mach = bfd_mach_iq2000;
+static int cpu_mach = (1 << MACH_IQ2000);
+
+/* Flags to set in the elf header. */
+static flagword iq2000_flags = DEFAULT_FLAGS;
+
+typedef struct proc
+{
+ symbolS *isym;
+ unsigned long reg_mask;
+ unsigned long reg_offset;
+ unsigned long fpreg_mask;
+ unsigned long fpreg_offset;
+ unsigned long frame_offset;
+ unsigned long frame_reg;
+ unsigned long pc_reg;
+} procS;
+
+static procS cur_proc;
+static procS *cur_proc_ptr;
+static int numprocs;
+
+/* Relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct iq2000_hi_fixup
+{
+ struct iq2000_hi_fixup * next; /* Next HI fixup. */
+ fixS * fixp; /* This fixup. */
+ segT seg; /* The section this fixup is in. */
+};
+
+/* The list of unmatched HI relocs. */
+static struct iq2000_hi_fixup * iq2000_hi_fixup_list;
+
+/* Macro hash table, which we will add to. */
+extern struct hash_control *macro_hash;
+
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char * arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_show_usage (FILE * stream ATTRIBUTE_UNUSED)
+{
+}
+
+/* Automatically enter conditional branch macros. */
+
+typedef struct
+{
+ const char * mnemonic;
+ const char ** expansion;
+ const char ** args;
+} iq2000_macro_defs_s;
+
+static const char * abs_args[] = { "rd", "rs", "scratch=%1", NULL };
+static const char * abs_expn = "\n sra \\rd,\\rs,31\n xor \\scratch,\\rd,\\rs\n sub \\rd,\\scratch,\\rd\n";
+static const char * la_expn = "\n lui \\reg,%hi(\\label)\n ori \\reg,\\reg,%lo(\\label)\n";
+static const char * la_args[] = { "reg", "label", NULL };
+static const char * bxx_args[] = { "rs", "rt", "label", "scratch=%1", NULL };
+static const char * bge_expn = "\n slt \\scratch,\\rs,\\rt\n beq %0,\\scratch,\\label\n";
+static const char * bgeu_expn = "\n sltu \\scratch,\\rs,\\rt\n beq %0,\\scratch,\\label\n";
+static const char * bgt_expn = "\n slt \\scratch,\\rt,\\rs\n bne %0,\\scratch,\\label\n";
+static const char * bgtu_expn = "\n sltu \\scratch,\\rt,\\rs\n bne %0,\\scratch,\\label\n";
+static const char * ble_expn = "\n slt \\scratch,\\rt,\\rs\n beq %0,\\scratch,\\label\n";
+static const char * bleu_expn = "\n sltu \\scratch,\\rt,\\rs\n beq %0,\\scratch,\\label\n";
+static const char * blt_expn = "\n slt \\scratch,\\rs,\\rt\n bne %0,\\scratch,\\label\n";
+static const char * bltu_expn = "\n sltu \\scratch,\\rs,\\rt\n bne %0,\\scratch,\\label\n";
+static const char * sxx_args[] = { "rd", "rs", "rt", NULL };
+static const char * sge_expn = "\n slt \\rd,\\rs,\\rt\n xori \\rd,\\rd,1\n";
+static const char * sgeu_expn = "\n sltu \\rd,\\rs,\\rt\n xori \\rd,\\rd,1\n";
+static const char * sle_expn = "\n slt \\rd,\\rt,\\rs\n xori \\rd,\\rd,1\n";
+static const char * sleu_expn = "\n sltu \\rd,\\rt,\\rs\n xori \\rd,\\rd,1\n";
+static const char * sgt_expn = "\n slt \\rd,\\rt,\\rs\n";
+static const char * sgtu_expn = "\n sltu \\rd,\\rt,\\rs\n";
+static const char * sne_expn = "\n xor \\rd,\\rt,\\rs\n sltu \\rd,%0,\\rd\n";
+static const char * seq_expn = "\n xor \\rd,\\rt,\\rs\n sltu \\rd,%0,\\rd\n xori \\rd,\\rd,1\n";
+static const char * ai32_args[] = { "rt", "rs", "imm", NULL };
+static const char * andi32_expn = "\n\
+ .if (\\imm & 0xffff0000 == 0xffff0000)\n\
+ andoi \\rt,\\rs,%lo(\\imm)\n\
+ .elseif (\\imm & 0x0000ffff == 0x0000ffff)\n\
+ andoui \\rt,\\rs,%uhi(\\imm)\n\
+ .elseif (\\imm & 0xffff0000 == 0x00000000)\n\
+ andi \\rt,\\rs,%lo(\\imm)\n\
+ .else\n\
+ andoui \\rt,\\rs,%uhi(\\imm)\n\
+ andoi \\rt,\\rt,%lo(\\imm)\n\
+ .endif\n";
+static const char * ori32_expn = "\n\
+ .if (\\imm & 0xffff == 0)\n\
+ orui \\rt,\\rs,%uhi(\\imm)\n\
+ .elseif (\\imm & 0xffff0000 == 0)\n\
+ ori \\rt,\\rs,%lo(\\imm)\n\
+ .else\n\
+ orui \\rt,\\rs,%uhi(\\imm)\n\
+ ori \\rt,\\rt,%lo(\\imm)\n\
+ .endif\n";
+
+static const char * neg_args[] = { "rd", "rs", NULL };
+static const char * neg_expn = "\n sub \\rd,%0,\\rs\n";
+static const char * negu_expn = "\n subu \\rd,%0,\\rs\n";
+static const char * li_args[] = { "rt", "imm", NULL };
+static const char * li_expn = "\n\
+ .if (\\imm & 0xffff0000 == 0x0)\n\
+ ori \\rt,%0,\\imm\n\
+ .elseif (\\imm & 0xffff0000 == 0xffff0000)\n\
+ addi \\rt,%0,\\imm\n\
+ .elseif (\\imm & 0x0000ffff == 0)\n\
+ lui \\rt,%uhi(\\imm)\n\
+ .else\n\
+ lui \\rt,%uhi(\\imm)\n\
+ ori \\rt,\\rt,%lo(\\imm)\n\
+ .endif\n";
+
+static iq2000_macro_defs_s iq2000_macro_defs[] =
+{
+ {"abs", (const char **) & abs_expn, (const char **) & abs_args},
+ {"la", (const char **) & la_expn, (const char **) & la_args},
+ {"bge", (const char **) & bge_expn, (const char **) & bxx_args},
+ {"bgeu", (const char **) & bgeu_expn, (const char **) & bxx_args},
+ {"bgt", (const char **) & bgt_expn, (const char **) & bxx_args},
+ {"bgtu", (const char **) & bgtu_expn, (const char **) & bxx_args},
+ {"ble", (const char **) & ble_expn, (const char **) & bxx_args},
+ {"bleu", (const char **) & bleu_expn, (const char **) & bxx_args},
+ {"blt", (const char **) & blt_expn, (const char **) & bxx_args},
+ {"bltu", (const char **) & bltu_expn, (const char **) & bxx_args},
+ {"sge", (const char **) & sge_expn, (const char **) & sxx_args},
+ {"sgeu", (const char **) & sgeu_expn, (const char **) & sxx_args},
+ {"sle", (const char **) & sle_expn, (const char **) & sxx_args},
+ {"sleu", (const char **) & sleu_expn, (const char **) & sxx_args},
+ {"sgt", (const char **) & sgt_expn, (const char **) & sxx_args},
+ {"sgtu", (const char **) & sgtu_expn, (const char **) & sxx_args},
+ {"seq", (const char **) & seq_expn, (const char **) & sxx_args},
+ {"sne", (const char **) & sne_expn, (const char **) & sxx_args},
+ {"neg", (const char **) & neg_expn, (const char **) & neg_args},
+ {"negu", (const char **) & negu_expn, (const char **) & neg_args},
+ {"li", (const char **) & li_expn, (const char **) & li_args},
+ {"ori32", (const char **) & ori32_expn, (const char **) & ai32_args},
+ {"andi32",(const char **) & andi32_expn,(const char **) & ai32_args},
+};
+
+static void
+iq2000_add_macro (const char * name,
+ const char * semantics,
+ const char ** arguments)
+{
+ macro_entry *macro;
+ sb macro_name;
+ const char *namestr;
+
+ macro = xmalloc (sizeof (macro_entry));
+ sb_new (& macro->sub);
+ sb_new (& macro_name);
+
+ macro->formal_count = 0;
+ macro->formals = 0;
+
+ sb_add_string (& macro->sub, semantics);
+
+ if (arguments != NULL)
+ {
+ formal_entry ** p = &macro->formals;
+
+ macro->formal_count = 0;
+ macro->formal_hash = hash_new ();
+
+ while (*arguments != NULL)
+ {
+ formal_entry *formal;
+
+ formal = xmalloc (sizeof (formal_entry));
+
+ sb_new (& formal->name);
+ sb_new (& formal->def);
+ sb_new (& formal->actual);
+
+ /* chlm: Added the following to allow defaulted args. */
+ if (strchr (*arguments,'='))
+ {
+ char * tt_args = strdup (*arguments);
+ char * tt_dflt = strchr (tt_args,'=');
+
+ *tt_dflt = 0;
+ sb_add_string (& formal->name, tt_args);
+ sb_add_string (& formal->def, tt_dflt + 1);
+ }
+ else
+ sb_add_string (& formal->name, *arguments);
+
+ /* Add to macro's hash table. */
+ hash_jam (macro->formal_hash, sb_terminate (& formal->name), formal);
+
+ formal->index = macro->formal_count;
+ macro->formal_count++;
+ *p = formal;
+ p = & formal->next;
+ *p = NULL;
+ ++arguments;
+ }
+ }
+
+ sb_add_string (&macro_name, name);
+ namestr = sb_terminate (&macro_name);
+ hash_jam (macro_hash, namestr, macro);
+
+ macro_defined = 1;
+}
+
+static void
+iq2000_load_macros (void)
+{
+ int i;
+ int mcnt = ARRAY_SIZE (iq2000_macro_defs);
+
+ for (i = 0; i < mcnt; i++)
+ iq2000_add_macro (iq2000_macro_defs[i].mnemonic,
+ *iq2000_macro_defs[i].expansion,
+ iq2000_macro_defs[i].args);
+}
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = iq2000_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, cpu_mach,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ iq2000_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 (iq2000_flags)
+ bfd_set_private_flags (stdoutput, iq2000_flags);
+
+ /* Set the machine type */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_iq2000, iq2000_mach);
+
+ iq2000_load_macros ();
+}
+
+void
+md_assemble (char * str)
+{
+ static long delayed_load_register = 0;
+ static int last_insn_had_delay_slot = 0;
+ static int last_insn_has_load_delay = 0;
+ static int last_insn_unconditional_jump = 0;
+ static int last_insn_was_ldw = 0;
+
+ iq2000_insn insn;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = iq2000_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", 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);
+
+ /* We need to generate an error if there's a yielding instruction in the delay
+ slot of a control flow modifying instruction (jump (yes), load (no)) */
+ if ((last_insn_had_delay_slot && !last_insn_has_load_delay) &&
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_YIELD_INSN))
+ as_bad (_("the yielding instruction %s may not be in a delay slot."),
+ CGEN_INSN_NAME (insn.insn));
+
+ /* Warn about odd numbered base registers for paired-register
+ instructions like LDW. On iq2000, result is always rt. */
+ if (iq2000_mach == bfd_mach_iq2000
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_EVEN_REG_NUM)
+ && (insn.fields.f_rt % 2))
+ as_bad (_("Register number (R%ld) for double word access must be even."),
+ insn.fields.f_rt);
+
+ /* Warn about insns that reference the target of a previous load. */
+ /* NOTE: R0 is a special case and is not subject to load delays (except for ldw). */
+ if (delayed_load_register && (last_insn_has_load_delay || last_insn_was_ldw))
+ {
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_RD) &&
+ insn.fields.f_rd == delayed_load_register)
+ as_warn (_("operand references R%ld of previous load."),
+ insn.fields.f_rd);
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_RS) &&
+ insn.fields.f_rs == delayed_load_register)
+ as_warn (_("operand references R%ld of previous load."),
+ insn.fields.f_rs);
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_RT) &&
+ insn.fields.f_rt == delayed_load_register)
+ as_warn (_("operand references R%ld of previous load."),
+ insn.fields.f_rt);
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_R31) &&
+ delayed_load_register == 31)
+ as_warn (_("instruction implicitly accesses R31 of previous load."));
+ }
+
+ /* Warn about insns that reference the (target + 1) of a previous ldw. */
+ if (last_insn_was_ldw)
+ {
+ if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_RD)
+ && insn.fields.f_rd == delayed_load_register + 1)
+ || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_RS)
+ && insn.fields.f_rs == delayed_load_register + 1)
+ || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_RT)
+ && insn.fields.f_rt == delayed_load_register + 1))
+ as_warn (_("operand references R%ld of previous load."),
+ delayed_load_register + 1);
+ }
+
+ last_insn_had_delay_slot =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
+
+ last_insn_has_load_delay =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_LOAD_DELAY);
+
+ if (last_insn_unconditional_jump)
+ last_insn_has_load_delay = last_insn_unconditional_jump = 0;
+ else if (! strcmp (CGEN_INSN_MNEMONIC (insn.insn), "j")
+ || ! strcmp (CGEN_INSN_MNEMONIC (insn.insn), "jal"))
+ last_insn_unconditional_jump = 1;
+
+ /* The meaning of EVEN_REG_NUM was overloaded to also imply LDW. Since
+ that's not true for IQ10, let's make the above logic specific to LDW. */
+ last_insn_was_ldw = ! strcmp ("ldw", CGEN_INSN_NAME (insn.insn));
+
+ /* The assumption here is that the target of a load is always rt. */
+ delayed_load_register = insn.fields.f_rt;
+}
+
+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;
+}
+
+/* Interface to relax_segment. */
+
+/* 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 old_fr_fix = fragP->fr_fix;
+
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the reloc as insn
+ alignment requirements may move the insn about. */
+
+ return (fragP->fr_var + fragP->fr_fix - old_fr_fix);
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+}
+
+
+/* Functions concerning relocs. */
+
+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 the address of the delay slot. */
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* 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)
+{
+ switch (operand->type)
+ {
+ case IQ2000_OPERAND_OFFSET: return BFD_RELOC_16_PCREL_S2;
+ case IQ2000_OPERAND_JMPTARG: return BFD_RELOC_IQ2000_OFFSET_16;
+ case IQ2000_OPERAND_JMPTARGQ10: return BFD_RELOC_NONE;
+ case IQ2000_OPERAND_HI16: return BFD_RELOC_HI16;
+ case IQ2000_OPERAND_LO16: return BFD_RELOC_LO16;
+ default: break;
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* Record a HI16 reloc for later matching with its LO16 cousin. */
+
+static void
+iq2000_record_hi16 (int reloc_type,
+ fixS * fixP,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ struct iq2000_hi_fixup * hi_fixup;
+
+ gas_assert (reloc_type == BFD_RELOC_HI16);
+
+ hi_fixup = xmalloc (sizeof * hi_fixup);
+ hi_fixup->fixp = fixP;
+ hi_fixup->seg = now_seg;
+ hi_fixup->next = iq2000_hi_fixup_list;
+
+ iq2000_hi_fixup_list = hi_fixup;
+}
+
+/* Called while parsing an instruction to create a fixup.
+ We need to check for HI16 relocs and queue them up for later sorting. */
+
+fixS *
+iq2000_cgen_record_fixup_exp (fragS * frag,
+ int where,
+ const CGEN_INSN * insn,
+ int length,
+ const CGEN_OPERAND * operand,
+ int opinfo,
+ expressionS * exp)
+{
+ fixS * fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+
+ if (operand->type == IQ2000_OPERAND_HI16
+ /* If low/high was used, it is recorded in `opinfo'. */
+ && (fixP->fx_cgen.opinfo == BFD_RELOC_HI16
+ || fixP->fx_cgen.opinfo == BFD_RELOC_LO16))
+ iq2000_record_hi16 (fixP->fx_cgen.opinfo, fixP, now_seg);
+
+ return fixP;
+}
+
+/* Return BFD reloc type from opinfo field in a fixS.
+ It's tricky using fx_r_type in iq2000_frob_file because the values
+ are BFD_RELOC_UNUSED + operand number. */
+#define FX_OPINFO_R_TYPE(f) ((f)->fx_cgen.opinfo)
+
+/* Sort any unmatched HI16 relocs so that they immediately precede
+ the corresponding LO16 reloc. This is called before md_apply_fix and
+ tc_gen_reloc. */
+
+void
+iq2000_frob_file (void)
+{
+ struct iq2000_hi_fixup * l;
+
+ for (l = iq2000_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type * seginfo;
+ int pass;
+
+ gas_assert (FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_HI16
+ || FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_LO16);
+
+ /* Check quickly whether the next fixup happens to be a matching low. */
+ if (l->fixp->fx_next != NULL
+ && FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_LO16
+ && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+ && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+ continue;
+
+ /* Look through the fixups for this segment for a matching
+ `low'. When we find one, move the high just in front of it.
+ We do this in two passes. In the first pass, we try to find
+ a unique `low'. In the second pass, we permit multiple
+ high's relocs for a single `low'. */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS * f;
+ fixS * prev;
+
+ prev = NULL;
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a `low' fixup which matches l->fixp. */
+ if (FX_OPINFO_R_TYPE (f) == BFD_RELOC_LO16
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || (FX_OPINFO_R_TYPE (prev) != BFD_RELOC_HI16)
+ || prev->fx_addsy != f->fx_addsy
+ || prev->fx_offset != f->fx_offset))
+ {
+ fixS ** pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root;
+ * pf != l->fixp;
+ pf = & (* pf)->fx_next)
+ gas_assert (* pf != NULL);
+
+ * pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+ if (pass == 1)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched high relocation"));
+ }
+ }
+}
+
+/* See whether we need to force a relocation into the output file. */
+
+int
+iq2000_force_relocation (fixS * fix)
+{
+ if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ return 0;
+}
+
+/* Handle the .set pseudo-op. */
+
+static void
+s_iq2000_set (int x ATTRIBUTE_UNUSED)
+{
+ static const char * ignored_arguments [] =
+ {
+ "reorder",
+ "noreorder",
+ "at",
+ "noat",
+ "macro",
+ "nomacro",
+ "move",
+ "novolatile",
+ "nomove",
+ "volatile",
+ "bopt",
+ "nobopt",
+ NULL
+ };
+ const char ** ignored;
+ char *name = input_line_pointer, ch;
+ char *save_ILP = input_line_pointer;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ for (ignored = ignored_arguments; * ignored; ignored ++)
+ if (strcmp (* ignored, name) == 0)
+ break;
+ if (* ignored == NULL)
+ {
+ /* We'd like to be able to use .set symbol, expn */
+ input_line_pointer = save_ILP;
+ s_set (0);
+ return;
+ }
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* 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_bigendian (buf, val, n);
+}
+
+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);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+bfd_boolean
+iq2000_fix_adjustable (fixS * fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
+
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ if (fixP->fx_addsy == NULL)
+ return TRUE;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return FALSE;
+
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return FALSE;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if ( reloc_type == BFD_RELOC_VTABLE_INHERIT
+ || reloc_type == BFD_RELOC_VTABLE_ENTRY)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+s_change_sec (int sec)
+{
+#ifdef OBJ_ELF
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for a obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ obj_elf_section_change_hook ();
+#endif
+
+ switch (sec)
+ {
+ case 't':
+ s_text (0);
+ break;
+ case 'd':
+ case 'r':
+ s_data (0);
+ break;
+ }
+}
+
+static symbolS *
+get_symbol (void)
+{
+ int c;
+ char *name;
+ symbolS *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = (symbolS *) symbol_find_or_make (name);
+ *input_line_pointer = c;
+ return p;
+}
+
+/* The .end directive. */
+
+static void
+s_iq2000_end (int x ATTRIBUTE_UNUSED)
+{
+ symbolS *p;
+ int maybe_text;
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ p = get_symbol ();
+ demand_empty_rest_of_line ();
+ }
+ else
+ p = NULL;
+
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+
+ if (!maybe_text)
+ as_warn (_(".end not in text section"));
+
+ if (!cur_proc_ptr)
+ {
+ as_warn (_(".end directive without a preceding .ent directive."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (p != NULL)
+ {
+ gas_assert (S_GET_NAME (p));
+ if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->isym)))
+ as_warn (_(".end symbol does not match .ent symbol."));
+ }
+ else
+ as_warn (_(".end directive missing or unknown symbol"));
+
+ cur_proc_ptr = NULL;
+}
+
+static int
+get_number (void)
+{
+ int negative = 0;
+ long val = 0;
+
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ negative = 1;
+ }
+
+ if (! ISDIGIT (*input_line_pointer))
+ as_bad (_("Expected simple number."));
+
+ if (input_line_pointer[0] == '0')
+ {
+ if (input_line_pointer[1] == 'x')
+ {
+ input_line_pointer += 2;
+ while (ISXDIGIT (*input_line_pointer))
+ {
+ val <<= 4;
+ val |= hex_value (*input_line_pointer++);
+ }
+ return negative ? -val : val;
+ }
+ else
+ {
+ ++input_line_pointer;
+
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val <<= 3;
+ val |= *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+ }
+ }
+
+ if (! ISDIGIT (*input_line_pointer))
+ {
+ printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
+ *input_line_pointer, *input_line_pointer);
+ as_warn (_("Invalid number"));
+ return -1;
+ }
+
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val *= 10;
+ val += *input_line_pointer++ - '0';
+ }
+
+ return negative ? -val : val;
+}
+
+/* The .aent and .ent directives. */
+
+static void
+s_iq2000_ent (int aent)
+{
+ symbolS *symbolP;
+ int maybe_text;
+
+ symbolP = get_symbol ();
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-')
+ get_number ();
+
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+
+ if (!maybe_text)
+ as_warn (_(".ent or .aent not in text section."));
+
+ if (!aent && cur_proc_ptr)
+ as_warn (_("missing `.end'"));
+
+ if (!aent)
+ {
+ cur_proc_ptr = &cur_proc;
+ memset (cur_proc_ptr, '\0', sizeof (procS));
+
+ cur_proc_ptr->isym = symbolP;
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
+
+ numprocs++;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .frame directive. If the mdebug section is present (IRIX 5 native)
+ then ecoff.c (ecoff_directive_frame) is used. For embedded targets,
+ s_iq2000_frame is used so that we can set the PDR information correctly.
+ We can't use the ecoff routines because they make reference to the ecoff
+ symbol table (in the mdebug section). */
+
+static void
+s_iq2000_frame (int ignore)
+{
+ s_ignore (ignore);
+}
+
+/* The .fmask and .mask directives. If the mdebug section is present
+ (IRIX 5 native) then ecoff.c (ecoff_directive_mask) is used. For
+ embedded targets, s_iq2000_mask is used so that we can set the PDR
+ information correctly. We can't use the ecoff routines because they
+ make reference to the ecoff symbol table (in the mdebug section). */
+
+static void
+s_iq2000_mask (int reg_type)
+{
+ s_ignore (reg_type);
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 },
+ { "word", cons, 4 },
+ { "rdata", s_change_sec, 'r'},
+ { "sdata", s_change_sec, 's'},
+ { "set", s_iq2000_set, 0 },
+ { "ent", s_iq2000_ent, 0 },
+ { "end", s_iq2000_end, 0 },
+ { "frame", s_iq2000_frame, 0 },
+ { "fmask", s_iq2000_mask, 'F'},
+ { "mask", s_iq2000_mask, 'R'},
+ { "dword", cons, 8 },
+ { "half", cons, 2 },
+ { NULL, NULL, 0 }
+};
diff --git a/gas/config/tc-iq2000.h b/gas/config/tc-iq2000.h
new file mode 100644
index 0000000..ab4f877
--- /dev/null
+++ b/gas/config/tc-iq2000.h
@@ -0,0 +1,65 @@
+/* tc-iq2000.h -- Header file for tc-iq2000.c.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_IQ2000
+
+#define LISTING_HEADER "IQ2000 GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_iq2000
+
+#define TARGET_FORMAT "elf32-iq2000"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_apply_fix gas_cgen_md_apply_fix
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+
+#define tc_frob_file() iq2000_frob_file ()
+
+#define obj_fix_adjustable(fixP) iq2000_fix_adjustable (fixP)
+
+/* After creating a fixup for an instruction operand, we need to check
+ for HI16 relocs and queue them up for later sorting. */
+#define md_cgen_record_fixup_exp iq2000_cgen_record_fixup_exp
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) iq2000_force_relocation (fix)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+extern void iq2000_frob_file (void);
+extern bfd_boolean iq2000_fix_adjustable (struct fix *);
+extern int iq2000_force_relocation (struct fix *);
+extern long md_pcrel_from_section (struct fix *, segT);
diff --git a/gas/config/tc-lm32.c b/gas/config/tc-lm32.c
new file mode 100644
index 0000000..07c8c82
--- /dev/null
+++ b/gas/config/tc-lm32.c
@@ -0,0 +1,421 @@
+/* tc-lm32.c - Lattice Mico32 assembler.
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Contributed by Jon Beniston <jon@beniston.com>
+
+ 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, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include <string.h>
+#include <stdlib.h>
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "bfd.h"
+#include "safe-ctype.h"
+#include "opcodes/lm32-desc.h"
+#include "opcodes/lm32-opc.h"
+#include "cgen.h"
+#include "elf/lm32.h"
+
+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];
+} lm32_insn;
+
+/* Configuration options */
+
+#define LM_CFG_MULTIPLIY_ENABLED 0x0001
+#define LM_CFG_DIVIDE_ENABLED 0x0002
+#define LM_CFG_BARREL_SHIFT_ENABLED 0x0004
+#define LM_CFG_SIGN_EXTEND_ENABLED 0x0008
+#define LM_CFG_USER_ENABLED 0x0010
+#define LM_CFG_ICACHE_ENABLED 0x0020
+#define LM_CFG_DCACHE_ENABLED 0x0040
+#define LM_CFG_BREAK_ENABLED 0x0080
+
+static unsigned config = 0U;
+
+/* Target specific assembler tokens / delimiters. */
+
+const char comment_chars[] = "#";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* Target specific assembly directives. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 },
+ { "byte", cons, 1 },
+ { "hword", cons, 2 },
+ { "word", cons, 4 },
+ { "dword", cons, 8 },
+ {(char *)0 , (void(*)(int))0, 0}
+};
+
+/* Target specific command line options. */
+
+const char * md_shortopts = "";
+
+struct option md_longopts[] =
+{
+#define OPTION_MULTIPLY_ENABLED (OPTION_MD_BASE + 1)
+ { "mmultiply-enabled", no_argument, NULL, OPTION_MULTIPLY_ENABLED },
+#define OPTION_DIVIDE_ENABLED (OPTION_MD_BASE + 2)
+ { "mdivide-enabled", no_argument, NULL, OPTION_DIVIDE_ENABLED },
+#define OPTION_BARREL_SHIFT_ENABLED (OPTION_MD_BASE + 3)
+ { "mbarrel-shift-enabled", no_argument, NULL, OPTION_BARREL_SHIFT_ENABLED },
+#define OPTION_SIGN_EXTEND_ENABLED (OPTION_MD_BASE + 4)
+ { "msign-extend-enabled", no_argument, NULL, OPTION_SIGN_EXTEND_ENABLED },
+#define OPTION_USER_ENABLED (OPTION_MD_BASE + 5)
+ { "muser-enabled", no_argument, NULL, OPTION_USER_ENABLED },
+#define OPTION_ICACHE_ENABLED (OPTION_MD_BASE + 6)
+ { "micache-enabled", no_argument, NULL, OPTION_ICACHE_ENABLED },
+#define OPTION_DCACHE_ENABLED (OPTION_MD_BASE + 7)
+ { "mdcache-enabled", no_argument, NULL, OPTION_DCACHE_ENABLED },
+#define OPTION_BREAK_ENABLED (OPTION_MD_BASE + 8)
+ { "mbreak-enabled", no_argument, NULL, OPTION_BREAK_ENABLED },
+#define OPTION_ALL_ENABLED (OPTION_MD_BASE + 9)
+ { "mall-enabled", no_argument, NULL, OPTION_ALL_ENABLED },
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Display architecture specific options. */
+
+void
+md_show_usage (FILE * fp)
+{
+ fprintf (fp, "LM32 specific options:\n"
+ " -mmultiply-enabled enable multiply instructions\n"
+ " -mdivide-enabled enable divide and modulus instructions\n"
+ " -mbarrel-shift-enabled enable multi-bit shift instructions\n"
+ " -msign-extend-enabled enable sign-extension instructions\n"
+ " -muser-enabled enable user-defined instructions\n"
+ " -micache-enabled enable instruction cache instructions\n"
+ " -mdcache-enabled enable data cache instructions\n"
+ " -mbreak-enabled enable the break instruction\n"
+ " -mall-enabled enable all optional instructions\n"
+ );
+}
+
+/* Parse command line options. */
+
+int
+md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_MULTIPLY_ENABLED:
+ config |= LM_CFG_MULTIPLIY_ENABLED;
+ break;
+ case OPTION_DIVIDE_ENABLED:
+ config |= LM_CFG_DIVIDE_ENABLED;
+ break;
+ case OPTION_BARREL_SHIFT_ENABLED:
+ config |= LM_CFG_BARREL_SHIFT_ENABLED;
+ break;
+ case OPTION_SIGN_EXTEND_ENABLED:
+ config |= LM_CFG_SIGN_EXTEND_ENABLED;
+ break;
+ case OPTION_USER_ENABLED:
+ config |= LM_CFG_USER_ENABLED;
+ break;
+ case OPTION_ICACHE_ENABLED:
+ config |= LM_CFG_ICACHE_ENABLED;
+ break;
+ case OPTION_DCACHE_ENABLED:
+ config |= LM_CFG_DCACHE_ENABLED;
+ break;
+ case OPTION_BREAK_ENABLED:
+ config |= LM_CFG_BREAK_ENABLED;
+ break;
+ case OPTION_ALL_ENABLED:
+ config |= LM_CFG_MULTIPLIY_ENABLED;
+ config |= LM_CFG_DIVIDE_ENABLED;
+ config |= LM_CFG_BARREL_SHIFT_ENABLED;
+ config |= LM_CFG_SIGN_EXTEND_ENABLED;
+ config |= LM_CFG_USER_ENABLED;
+ config |= LM_CFG_ICACHE_ENABLED;
+ config |= LM_CFG_DCACHE_ENABLED;
+ config |= LM_CFG_BREAK_ENABLED;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+/* Do any architecture specific initialisation. */
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = lm32_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ lm32_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);
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_lm32, bfd_mach_lm32))
+ as_warn (_("could not set architecture and machine"));
+}
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf. */
+
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* 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. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int i;
+ int prec;
+ LITTLENUM_TYPE words[4];
+
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+ case 'd':
+ prec = 4;
+ break;
+ 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);
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+
+ return NULL;
+}
+
+/* Called for each undefined symbol. */
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* This function assembles the instructions. It emits the frags/bytes to the
+ sections and creates the relocation entries. */
+
+void
+md_assemble (char * str)
+{
+ lm32_insn insn;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = lm32_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, &insn.fields, insn.buffer, &errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ gas_cgen_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (&insn.fields), 1, NULL);
+}
+
+/* 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)
+{
+ switch (operand->type)
+ {
+ case LM32_OPERAND_GOT16:
+ return BFD_RELOC_LM32_16_GOT;
+ case LM32_OPERAND_GOTOFFHI16:
+ return BFD_RELOC_LM32_GOTOFF_HI16;
+ case LM32_OPERAND_GOTOFFLO16:
+ return BFD_RELOC_LM32_GOTOFF_LO16;
+ case LM32_OPERAND_GP16:
+ return BFD_RELOC_GPREL16;
+ case LM32_OPERAND_LO16:
+ return BFD_RELOC_LO16;
+ case LM32_OPERAND_HI16:
+ return BFD_RELOC_HI16;
+ case LM32_OPERAND_BRANCH:
+ return BFD_RELOC_LM32_BRANCH;
+ case LM32_OPERAND_CALL:
+ return BFD_RELOC_LM32_CALL;
+ default:
+ break;
+ }
+ return BFD_RELOC_NONE;
+}
+
+/* Return the position from which the PC relative adjustment for a PC relative
+ fixup should be made. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ /* Shouldn't get called. */
+ abort ();
+ /* Return address of current instruction. */
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* 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;
+ }
+
+ /*fprintf(stderr, "%s extern %d local %d\n", S_GET_NAME (fixP->fx_addsy), S_IS_EXTERN (fixP->fx_addsy), S_IS_LOCAL (fixP->fx_addsy));*/
+ /* FIXME: Weak problem? */
+ if ((fixP->fx_addsy != (symbolS *) NULL)
+ && S_IS_EXTERNAL (fixP->fx_addsy))
+ {
+ /* If the symbol is external, let the linker handle it. */
+ return 0;
+ }
+
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* Return true if we can partially resolve a relocation now. */
+
+bfd_boolean
+lm32_fix_adjustable (fixS * fixP)
+{
+ /* We need the symbol name for the VTABLE entries */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Relaxation isn't required/supported on this target. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+void
+md_apply_fix (fixS * fixP, valueT * valP, segT seg)
+{
+ /* Fix for weak symbols. Why do we have fx_addsy for weak symbols? */
+ if (fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy))
+ *valP = 0;
+
+ gas_cgen_md_apply_fix (fixP, valP, seg);
+ return;
+}
diff --git a/gas/config/tc-lm32.h b/gas/config/tc-lm32.h
new file mode 100644
index 0000000..295efc1
--- /dev/null
+++ b/gas/config/tc-lm32.h
@@ -0,0 +1,50 @@
+/* tc-lm32.h -- Header file for tc-lm32.c
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ Contributed by Jon Beniston <jon@beniston.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef TC_LM32_H
+#define TC_LM32_H
+
+#define TARGET_FORMAT "elf32-lm32"
+#define TARGET_ARCH bfd_arch_lm32
+#define TARGET_MACH bfd_mach_lm32
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix3 don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_operand(X)
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+extern long md_pcrel_from_section (struct fix *, segT);
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+
+extern bfd_boolean lm32_fix_adjustable (struct fix *);
+#define tc_fix_adjustable(FIX) lm32_fix_adjustable (FIX)
+
+#endif /* TC_LM32_H */
+
diff --git a/gas/config/tc-m32c.c b/gas/config/tc-m32c.c
new file mode 100644
index 0000000..8e24edb
--- /dev/null
+++ b/gas/config/tc-m32c.c
@@ -0,0 +1,1294 @@
+/* tc-m32c.c -- Assembler for the Renesas M32C.
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+ 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 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#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 "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;
+
+#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[] = "|";
+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)
+#define OPTION_LINKRELAX (OPTION_MD_BASE + 2)
+#define OPTION_H_TICK_HEX (OPTION_MD_BASE + 3)
+
+struct option md_longopts[] =
+{
+ { "m16c", no_argument, NULL, OPTION_CPU_M16C },
+ { "m32c", no_argument, NULL, OPTION_CPU_M32C },
+ { "relax", no_argument, NULL, OPTION_LINKRELAX },
+ { "h-tick-hex", no_argument, NULL, OPTION_H_TICK_HEX },
+ {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;
+static int m32c_relax = 0;
+
+/* Flags to set in the elf header */
+static flagword m32c_flags = DEFAULT_FLAGS;
+
+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)
+{
+ cgen_bitset_set (& m32c_isa, 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;
+
+ case OPTION_LINKRELAX:
+ m32c_relax = 1;
+ break;
+
+ case OPTION_H_TICK_HEX:
+ enable_h_tick_hex = 1;
+ 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},
+ { "3byte", cons, 3 },
+ { "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;
+
+ 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
+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;
+ finished_insnS results;
+ int rl_type;
+
+ 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 ("%s", errmsg);
+ 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, &results);
+
+ last_insn_had_delay_slot
+ = CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
+ (void) last_insn_had_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 '#'.
+ 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 */
+
+ /* 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_5_W,
+ M32C_MACRO_JCND16_5_A,
+ M32C_MACRO_JCND16_W,
+ 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 {
+ 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 },
+
+ /* 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]))
+
+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 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 (inum == 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, 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, 0);
+ }
+
+ 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 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 rl_addend = 0;
+
+ addend = target_address_for (fragP) - (fragP->fr_address + where);
+
+ 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;
+ rl_addend = 0x21;
+ 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;
+ rl_addend = 0x51;
+ break;
+
+ case -M32C_MACRO_JCND16_5_A:
+ op[0] ^= 0x04;
+ op[1] = 5;
+ op[2] = 0xfc;
+ operand = M32C_OPERAND_LAB_8_24;
+ where += 2;
+ rl_addend = 0x61;
+ break;
+
+
+ case M32C_INSN_JCND16:
+ op[2] = addend - 2;
+ operand = M32C_OPERAND_LAB_16_8;
+ rl_addend = 0x31;
+ 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;
+ rl_addend = 0x61;
+ break;
+
+ case -M32C_MACRO_JCND16_A:
+ op[1] ^= 0x04;
+ op[2] = 5;
+ op[3] = 0xfc;
+ operand = M32C_OPERAND_LAB_8_24;
+ where += 3;
+ rl_addend = 0x71;
+ break;
+
+ case M32C_INSN_JMP16_S:
+ op[0] = 0x60 | ((addend-2) & 0x07);
+ operand = M32C_OPERAND_LAB_5_3;
+ rl_addend = 0x10;
+ break;
+
+ case M32C_INSN_JMP16_B:
+ op[0] = 0xfe;
+ op[1] = addend - 1;
+ operand = M32C_OPERAND_LAB_8_8;
+ rl_addend = 0x21;
+ break;
+
+ case M32C_INSN_JMP16_W:
+ op[0] = 0xf4;
+ op[1] = addend - 1;
+ op[2] = (addend - 1) >> 8;
+ operand = M32C_OPERAND_LAB_8_16;
+ rl_addend = 0x31;
+ break;
+
+ case M32C_INSN_JMP16_A:
+ op[0] = 0xfc;
+ op[1] = 0;
+ op[2] = 0;
+ op[3] = 0;
+ operand = M32C_OPERAND_LAB_8_24;
+ rl_addend = 0x41;
+ break;
+
+ case M32C_INSN_JCND32:
+ op[1] = addend - 1;
+ operand = M32C_OPERAND_LAB_8_8;
+ rl_addend = 0x21;
+ 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;
+ rl_addend = 0x51;
+ break;
+
+ case -M32C_MACRO_JCND32_A:
+ op[0] ^= 0x40;
+ op[1] = 5;
+ op[2] = 0xcc;
+ operand = M32C_OPERAND_LAB_8_24;
+ where += 2;
+ rl_addend = 0x61;
+ break;
+
+ case M32C_INSN_JMP32_S:
+ addend = ((addend-2) & 0x07);
+ op[0] = 0x4a | (addend & 0x01) | ((addend << 3) & 0x30);
+ operand = M32C_OPERAND_LAB32_JMP_S;
+ rl_addend = 0x10;
+ break;
+
+ case M32C_INSN_JMP32_B:
+ op[0] = 0xbb;
+ op[1] = addend - 1;
+ operand = M32C_OPERAND_LAB_8_8;
+ rl_addend = 0x21;
+ break;
+
+ case M32C_INSN_JMP32_W:
+ op[0] = 0xce;
+ op[1] = addend - 1;
+ op[2] = (addend - 1) >> 8;
+ operand = M32C_OPERAND_LAB_8_16;
+ rl_addend = 0x31;
+ break;
+
+ case M32C_INSN_JMP32_A:
+ op[0] = 0xcc;
+ op[1] = 0;
+ op[2] = 0;
+ op[3] = 0;
+ operand = M32C_OPERAND_LAB_8_24;
+ 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;
+ 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;
+ 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;
+ 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;
+ rl_addend = 0x41;
+ break;
+
+ case -M32C_MACRO_ADJNZ_2:
+ rl_addend = 0x31;
+ op[2] = addend - 2;
+ operand = M32C_OPERAND_LAB_16_8;
+ break;
+ case -M32C_MACRO_ADJNZ_3:
+ rl_addend = 0x41;
+ op[3] = addend - 2;
+ operand = M32C_OPERAND_LAB_24_8;
+ break;
+ case -M32C_MACRO_ADJNZ_4:
+ rl_addend = 0x51;
+ op[4] = addend - 2;
+ operand = M32C_OPERAND_LAB_32_8;
+ break;
+ case -M32C_MACRO_ADJNZ_5:
+ rl_addend = 0x61;
+ op[5] = addend - 2;
+ operand = M32C_OPERAND_LAB_40_8;
+ break;
+
+ default:
+ printf("\nHey! Need more opcode converters! missing: %d %s\n\n",
+ fragP->fr_subtype,
+ fragP->fr_cgen.insn->base->name);
+ 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
+ || (m32c_relax && (operand != M32C_OPERAND_LAB_5_3
+ && operand != M32C_OPERAND_LAB32_JMP_S)))
+ {
+ gas_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[] = {
+
+ /* 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 },
+ { M32C_OPERAND_IMM_24_QI, BFD_RELOC_8, 3 },
+ { M32C_OPERAND_IMM_32_QI, BFD_RELOC_8, 4 },
+ { M32C_OPERAND_IMM_40_QI, BFD_RELOC_8, 5 },
+ { M32C_OPERAND_IMM_48_QI, BFD_RELOC_8, 6 },
+ { M32C_OPERAND_IMM_56_QI, BFD_RELOC_8, 7 },
+ { M32C_OPERAND_DSP_8_S8, BFD_RELOC_8, 1 },
+ { M32C_OPERAND_DSP_16_S8, BFD_RELOC_8, 2 },
+ { M32C_OPERAND_DSP_24_S8, BFD_RELOC_8, 3 },
+ { M32C_OPERAND_DSP_32_S8, BFD_RELOC_8, 4 },
+ { M32C_OPERAND_DSP_40_S8, BFD_RELOC_8, 5 },
+ { M32C_OPERAND_DSP_48_S8, BFD_RELOC_8, 6 },
+ { M32C_OPERAND_DSP_8_U8, BFD_RELOC_8, 1 },
+ { M32C_OPERAND_DSP_16_U8, BFD_RELOC_8, 2 },
+ { M32C_OPERAND_DSP_24_U8, BFD_RELOC_8, 3 },
+ { M32C_OPERAND_DSP_32_U8, BFD_RELOC_8, 4 },
+ { M32C_OPERAND_DSP_40_U8, BFD_RELOC_8, 5 },
+ { M32C_OPERAND_DSP_48_U8, BFD_RELOC_8, 6 },
+ { M32C_OPERAND_BITBASE32_16_S11_UNPREFIXED, BFD_RELOC_8, 2 },
+ { M32C_OPERAND_BITBASE32_16_U11_UNPREFIXED, BFD_RELOC_8, 2 },
+ { M32C_OPERAND_BITBASE32_24_S11_PREFIXED, BFD_RELOC_8, 3 },
+ { M32C_OPERAND_BITBASE32_24_U11_PREFIXED, BFD_RELOC_8, 3 },
+
+ /* Absolute relocs for 16-bit fields. */
+ { M32C_OPERAND_IMM_8_HI, BFD_RELOC_16, 1 },
+ { 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_IMM_40_HI, BFD_RELOC_16, 5 },
+ { M32C_OPERAND_IMM_48_HI, BFD_RELOC_16, 6 },
+ { M32C_OPERAND_IMM_56_HI, BFD_RELOC_16, 7 },
+ { M32C_OPERAND_IMM_64_HI, BFD_RELOC_16, 8 },
+ { 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 },
+ { M32C_OPERAND_DSP_40_U16, BFD_RELOC_16, 5 },
+ { M32C_OPERAND_DSP_48_U16, BFD_RELOC_16, 6 },
+ { M32C_OPERAND_BITBASE32_16_S19_UNPREFIXED, BFD_RELOC_16, 2 },
+ { M32C_OPERAND_BITBASE32_16_U19_UNPREFIXED, BFD_RELOC_16, 2 },
+ { M32C_OPERAND_BITBASE32_24_S19_PREFIXED, BFD_RELOC_16, 3 },
+ { M32C_OPERAND_BITBASE32_24_U19_PREFIXED, BFD_RELOC_16, 3 },
+
+ /* Absolute relocs for 24-bit fields. */
+ { M32C_OPERAND_LAB_8_24, BFD_RELOC_24, 1 },
+ { M32C_OPERAND_DSP_8_S24, BFD_RELOC_24, 1 },
+ { M32C_OPERAND_DSP_8_U24, 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 },
+ { M32C_OPERAND_DSP_48_U24, BFD_RELOC_24, 6 },
+ { M32C_OPERAND_DSP_16_U20, BFD_RELOC_24, 2 },
+ { M32C_OPERAND_DSP_24_U20, BFD_RELOC_24, 3 },
+ { M32C_OPERAND_DSP_32_U20, BFD_RELOC_24, 4 },
+ { M32C_OPERAND_BITBASE32_16_U27_UNPREFIXED, BFD_RELOC_24, 2 },
+ { M32C_OPERAND_BITBASE32_24_U27_PREFIXED, BFD_RELOC_24, 3 },
+
+ /* 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;
+
+ if (fixP->fx_cgen.opinfo
+ && fixP->fx_cgen.opinfo != BFD_RELOC_NONE)
+ return fixP->fx_cgen.opinfo;
+
+ return or->reloc;
+ }
+ }
+
+ fprintf
+ (stderr,
+ "Error: tc-m32c.c:md_cgen_lookup_reloc Unimplemented relocation for operand %s\n",
+ operand->name);
+
+ 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. */
+
+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;
+
+ /* 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
+ {
+ 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);
+}
+
+/* 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)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+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;
+
+ if (m32c_relax)
+ return 0;
+
+ return 1;
+}
+
+/* Worker function for m32c_is_colon_insn(). */
+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;
+}
diff --git a/gas/config/tc-m32c.h b/gas/config/tc-m32c.h
new file mode 100644
index 0000000..3af0092
--- /dev/null
+++ b/gas/config/tc-m32c.h
@@ -0,0 +1,87 @@
+/* tc-m32c.h -- Header file for tc-m32c.c.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define TC_M32C
+
+#define LISTING_HEADER "M16C/M32C GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_m32c
+
+#define TARGET_FORMAT "elf32-m32c"
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define md_end m32c_md_end
+extern void m32c_md_end (void);
+
+#define md_start_line_hook m32c_start_line_hook
+extern void m32c_start_line_hook (void);
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+long md_pcrel_from_section (struct fix *, segT);
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_apply_fix m32c_apply_fix
+extern void m32c_apply_fix (struct fix *, valueT *, segT);
+
+#define tc_fix_adjustable(fixP) m32c_fix_adjustable (fixP)
+extern bfd_boolean m32c_fix_adjustable (struct fix *);
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) m32c_force_relocation (fix)
+extern int m32c_force_relocation (struct fix *);
+
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \
+ m32c_cons_fix_new (FRAG, WHERE, NBYTES, EXP, RELOC)
+extern void m32c_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+extern void m32c_prepare_relax_scan (fragS *, offsetT *, relax_substateT);
+#define md_prepare_relax_scan(FRAGP, ADDR, AIM, STATE, TYPE) \
+ m32c_prepare_relax_scan(FRAGP, &AIM, STATE)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* We need a special version of the TC_START_LABEL macro so that we
+ allow the :Z, :S, :Q and :G suffixes to be
+ parsed as such. We need to be able to change the contents of
+ the local variable 'c' which is passed to this macro as 'character'. */
+#define TC_START_LABEL(character, s, i_l_p) \
+ ((character) != ':' ? 0 : (character = m32c_is_colon_insn (s)) ? 0 : ((character = ':'), 1))
+extern char m32c_is_colon_insn (char *);
+
+#define H_TICK_HEX 1
diff --git a/gas/config/tc-m32r.c b/gas/config/tc-m32r.c
new file mode 100644
index 0000000..172b8f9
--- /dev/null
+++ b/gas/config/tc-m32r.c
@@ -0,0 +1,2409 @@
+/* tc-m32r.c -- Assembler for the Renesas M32R.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/m32r-desc.h"
+#include "opcodes/m32r-opc.h"
+#include "cgen.h"
+#include "elf/m32r.h"
+
+/* Linked list of symbols that are debugging symbols to be defined as the
+ beginning of the current instruction. */
+typedef struct sym_link
+{
+ struct sym_link *next;
+ symbolS *symbol;
+} sym_linkS;
+
+static sym_linkS *debug_sym_link = (sym_linkS *) 0;
+
+/* 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];
+ sym_linkS *debug_sym_link;
+}
+m32r_insn;
+
+/* prev_insn.insn is non-null if last insn was a 16 bit insn on a 32 bit
+ boundary (i.e. was the first of two 16 bit insns). */
+static m32r_insn prev_insn;
+
+/* Non-zero if we've seen a relaxable insn since the last 32 bit
+ alignment request. */
+static int seen_relaxable_p = 0;
+
+/* Non-zero if we are generating PIC code. */
+int pic_code;
+
+/* Non-zero if -relax specified, in which case sufficient relocs are output
+ for the linker to do relaxing.
+ We do simple forms of relaxing internally, but they are always done.
+ This flag does not apply to them. */
+static int m32r_relax;
+
+/* Non-zero if warn when a high/shigh reloc has no matching low reloc.
+ Each high/shigh reloc must be paired with it's low cousin in order to
+ properly calculate the addend in a relocatable link (since there is a
+ potential carry from the low to the high/shigh).
+ This option is off by default though for user-written assembler code it
+ might make sense to make the default be on (i.e. have gcc pass a flag
+ to turn it off). This warning must not be on for GCC created code as
+ optimization may delete the low but not the high/shigh (at least we
+ shouldn't assume or require it to). */
+static int warn_unmatched_high = 0;
+
+/* 1 if -m32rx has been specified, in which case support for
+ the extended M32RX instruction set should be enabled.
+ 2 if -m32r2 has been specified, in which case support for
+ the extended M32R2 instruction set should be enabled. */
+static int enable_m32rx = 0; /* Default to M32R. */
+
+/* Non-zero if -m32rx -hidden has been specified, in which case support for
+ the special M32RX instruction set should be enabled. */
+static int enable_special = 0;
+
+/* Non-zero if -bitinst has been specified, in which case support
+ for extended M32R bit-field instruction set should be enabled. */
+static int enable_special_m32r = 1;
+
+/* Non-zero if -float has been specified, in which case support for
+ extended M32R floating point instruction set should be enabled. */
+static int enable_special_float = 0;
+
+/* Non-zero if the programmer should be warned when an explicit parallel
+ instruction might have constraint violations. */
+static int warn_explicit_parallel_conflicts = 1;
+
+/* Non-zero if the programmer should not receive any messages about
+ parallel instruction with potential or real constraint violations.
+ The ability to suppress these messages is intended only for hardware
+ vendors testing the chip. It superceedes
+ warn_explicit_parallel_conflicts. */
+static int ignore_parallel_conflicts = 0;
+
+/* Non-zero if insns can be made parallel. */
+static int use_parallel = 0;
+
+/* Non-zero if optimizations should be performed. */
+static int optimize;
+
+/* m32r er_flags. */
+static int m32r_flags = 0;
+
+/* Stuff for .scomm symbols. */
+static segT sbss_section;
+static asection scom_section;
+static asymbol scom_symbol;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "!";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* Relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct m32r_hi_fixup
+{
+ /* Next HI fixup. */
+ struct m32r_hi_fixup *next;
+
+ /* This fixup. */
+ fixS *fixp;
+
+ /* The section this fixup is in. */
+ segT seg;
+};
+
+/* The list of unmatched HI relocs. */
+
+static struct m32r_hi_fixup *m32r_hi_fixup_list;
+
+struct
+{
+ enum bfd_architecture bfd_mach;
+ int mach_flags;
+} mach_table[] =
+{
+ { bfd_mach_m32r, (1<<MACH_M32R) },
+ { bfd_mach_m32rx, (1<<MACH_M32RX) },
+ { bfd_mach_m32r2, (1<<MACH_M32R2) }
+};
+
+static void
+allow_m32rx (int on)
+{
+ enable_m32rx = on;
+
+ if (stdoutput != NULL)
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach_table[on].bfd_mach);
+
+ if (gas_cgen_cpu_desc != NULL)
+ gas_cgen_cpu_desc->machs = mach_table[on].mach_flags;
+}
+
+#define M32R_SHORTOPTS "O::K:"
+
+const char *md_shortopts = M32R_SHORTOPTS;
+
+enum md_option_enums
+{
+ OPTION_M32R = OPTION_MD_BASE,
+ OPTION_M32RX,
+ OPTION_M32R2,
+ OPTION_BIG,
+ OPTION_LITTLE,
+ OPTION_PARALLEL,
+ OPTION_NO_PARALLEL,
+ OPTION_WARN_PARALLEL,
+ OPTION_NO_WARN_PARALLEL,
+ OPTION_IGNORE_PARALLEL,
+ OPTION_NO_IGNORE_PARALLEL,
+ OPTION_SPECIAL,
+ OPTION_SPECIAL_M32R,
+ OPTION_NO_SPECIAL_M32R,
+ OPTION_SPECIAL_FLOAT,
+ OPTION_WARN_UNMATCHED,
+ OPTION_NO_WARN_UNMATCHED
+};
+
+struct option md_longopts[] =
+{
+ {"m32r", no_argument, NULL, OPTION_M32R},
+ {"m32rx", no_argument, NULL, OPTION_M32RX},
+ {"m32r2", no_argument, NULL, OPTION_M32R2},
+ {"big", no_argument, NULL, OPTION_BIG},
+ {"little", no_argument, NULL, OPTION_LITTLE},
+ {"EB", no_argument, NULL, OPTION_BIG},
+ {"EL", no_argument, NULL, OPTION_LITTLE},
+ {"parallel", no_argument, NULL, OPTION_PARALLEL},
+ {"no-parallel", no_argument, NULL, OPTION_NO_PARALLEL},
+ {"warn-explicit-parallel-conflicts", no_argument, NULL, OPTION_WARN_PARALLEL},
+ {"Wp", no_argument, NULL, OPTION_WARN_PARALLEL},
+ {"no-warn-explicit-parallel-conflicts", no_argument, NULL, OPTION_NO_WARN_PARALLEL},
+ {"Wnp", no_argument, NULL, OPTION_NO_WARN_PARALLEL},
+ {"ignore-parallel-conflicts", no_argument, NULL, OPTION_IGNORE_PARALLEL},
+ {"Ip", no_argument, NULL, OPTION_IGNORE_PARALLEL},
+ {"no-ignore-parallel-conflicts", no_argument, NULL, OPTION_NO_IGNORE_PARALLEL},
+ {"nIp", no_argument, NULL, OPTION_NO_IGNORE_PARALLEL},
+ {"hidden", no_argument, NULL, OPTION_SPECIAL},
+ {"bitinst", no_argument, NULL, OPTION_SPECIAL_M32R},
+ {"no-bitinst", no_argument, NULL, OPTION_NO_SPECIAL_M32R},
+ {"float", no_argument, NULL, OPTION_SPECIAL_FLOAT},
+ /* Sigh. I guess all warnings must now have both variants. */
+ {"warn-unmatched-high", no_argument, NULL, OPTION_WARN_UNMATCHED},
+ {"Wuh", no_argument, NULL, OPTION_WARN_UNMATCHED},
+ {"no-warn-unmatched-high", no_argument, NULL, OPTION_NO_WARN_UNMATCHED},
+ {"Wnuh", no_argument, NULL, OPTION_NO_WARN_UNMATCHED},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+static void
+little (int on)
+{
+ target_big_endian = ! on;
+}
+
+/* Use parallel execution. */
+
+static int
+parallel (void)
+{
+ if (! enable_m32rx)
+ return 0;
+
+ if (use_parallel == 1)
+ return 1;
+
+ return 0;
+}
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case 'O':
+ optimize = 1;
+ use_parallel = 1;
+ break;
+
+ case OPTION_M32R:
+ allow_m32rx (0);
+ break;
+
+ case OPTION_M32RX:
+ allow_m32rx (1);
+ break;
+
+ case OPTION_M32R2:
+ allow_m32rx (2);
+ enable_special = 1;
+ enable_special_m32r = 1;
+ break;
+
+ case OPTION_BIG:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_LITTLE:
+ target_big_endian = 0;
+ break;
+
+ case OPTION_PARALLEL:
+ use_parallel = 1;
+ break;
+
+ case OPTION_NO_PARALLEL:
+ use_parallel = 0;
+ break;
+
+ case OPTION_WARN_PARALLEL:
+ warn_explicit_parallel_conflicts = 1;
+ break;
+
+ case OPTION_NO_WARN_PARALLEL:
+ warn_explicit_parallel_conflicts = 0;
+ break;
+
+ case OPTION_IGNORE_PARALLEL:
+ ignore_parallel_conflicts = 1;
+ break;
+
+ case OPTION_NO_IGNORE_PARALLEL:
+ ignore_parallel_conflicts = 0;
+ break;
+
+ case OPTION_SPECIAL:
+ if (enable_m32rx)
+ enable_special = 1;
+ else
+ {
+ /* Pretend that we do not recognise this option. */
+ as_bad (_("Unrecognised option: -hidden"));
+ return 0;
+ }
+ break;
+
+ case OPTION_SPECIAL_M32R:
+ enable_special_m32r = 1;
+ break;
+
+ case OPTION_NO_SPECIAL_M32R:
+ enable_special_m32r = 0;
+ break;
+
+ case OPTION_SPECIAL_FLOAT:
+ enable_special_float = 1;
+ break;
+
+ case OPTION_WARN_UNMATCHED:
+ warn_unmatched_high = 1;
+ break;
+
+ case OPTION_NO_WARN_UNMATCHED:
+ warn_unmatched_high = 0;
+ break;
+
+ case 'K':
+ if (strcmp (arg, "PIC") != 0)
+ as_warn (_("Unrecognized option following -K"));
+ else
+ pic_code = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _(" M32R specific command line options:\n"));
+
+ fprintf (stream, _("\
+ -m32r disable support for the m32rx instruction set\n"));
+ fprintf (stream, _("\
+ -m32rx support the extended m32rx instruction set\n"));
+ fprintf (stream, _("\
+ -m32r2 support the extended m32r2 instruction set\n"));
+ fprintf (stream, _("\
+ -EL,-little produce little endian code and data\n"));
+ fprintf (stream, _("\
+ -EB,-big produce big endian code and data\n"));
+ fprintf (stream, _("\
+ -parallel try to combine instructions in parallel\n"));
+ fprintf (stream, _("\
+ -no-parallel disable -parallel\n"));
+ fprintf (stream, _("\
+ -no-bitinst disallow the M32R2's extended bit-field instructions\n"));
+ fprintf (stream, _("\
+ -O try to optimize code. Implies -parallel\n"));
+
+ fprintf (stream, _("\
+ -warn-explicit-parallel-conflicts warn when parallel instructions\n"));
+ fprintf (stream, _("\
+ might violate contraints\n"));
+ fprintf (stream, _("\
+ -no-warn-explicit-parallel-conflicts do not warn when parallel\n"));
+ fprintf (stream, _("\
+ instructions might violate contraints\n"));
+ fprintf (stream, _("\
+ -Wp synonym for -warn-explicit-parallel-conflicts\n"));
+ fprintf (stream, _("\
+ -Wnp synonym for -no-warn-explicit-parallel-conflicts\n"));
+ fprintf (stream, _("\
+ -ignore-parallel-conflicts do not check parallel instructions\n"));
+ fprintf (stream, _("\
+ for constraint violations\n"));
+ fprintf (stream, _("\
+ -no-ignore-parallel-conflicts check parallel instructions for\n"));
+ fprintf (stream, _("\
+ constraint violations\n"));
+ fprintf (stream, _("\
+ -Ip synonym for -ignore-parallel-conflicts\n"));
+ fprintf (stream, _("\
+ -nIp synonym for -no-ignore-parallel-conflicts\n"));
+
+ fprintf (stream, _("\
+ -warn-unmatched-high warn when an (s)high reloc has no matching low reloc\n"));
+ fprintf (stream, _("\
+ -no-warn-unmatched-high do not warn about missing low relocs\n"));
+ fprintf (stream, _("\
+ -Wuh synonym for -warn-unmatched-high\n"));
+ fprintf (stream, _("\
+ -Wnuh synonym for -no-warn-unmatched-high\n"));
+
+ fprintf (stream, _("\
+ -KPIC generate PIC\n"));
+}
+
+/* Set by md_assemble for use by m32r_fill_insn. */
+static subsegT prev_subseg;
+static segT prev_seg;
+
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+symbolS * GOT_symbol;
+
+static inline int
+m32r_PIC_related_p (symbolS *sym)
+{
+ expressionS *exp;
+
+ if (! sym)
+ return 0;
+
+ if (sym == GOT_symbol)
+ return 1;
+
+ exp = symbol_get_value_expression (sym);
+
+ return (exp->X_op == O_PIC_reloc
+ || exp->X_md == BFD_RELOC_M32R_26_PLTREL
+ || m32r_PIC_related_p (exp->X_add_symbol)
+ || m32r_PIC_related_p (exp->X_op_symbol));
+}
+
+static inline int
+m32r_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
+{
+ expressionS *exp = main_exp;
+
+ if (exp->X_op == O_add && m32r_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp->X_op == O_symbol && exp->X_add_symbol)
+ {
+ if (exp->X_add_symbol == GOT_symbol)
+ {
+ *r_type_p = BFD_RELOC_M32R_GOTPC24;
+ return 0;
+ }
+ }
+ else if (exp->X_op == O_add)
+ {
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+ if (! exp)
+ return 0;
+ }
+
+ if (exp->X_op == O_PIC_reloc)
+ {
+ *r_type_p = exp->X_md;
+ if (exp == main_exp)
+ exp->X_op = O_symbol;
+ else
+ {
+ main_exp->X_add_symbol = exp->X_add_symbol;
+ main_exp->X_add_number += exp->X_add_number;
+ }
+ }
+ else
+ return (m32r_PIC_related_p (exp->X_add_symbol)
+ || m32r_PIC_related_p (exp->X_op_symbol));
+
+ return 0;
+}
+
+/* FIXME: Should be machine generated. */
+#define NOP_INSN 0x7000
+#define PAR_NOP_INSN 0xf000 /* Can only be used in 2nd slot. */
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+m32r_handle_align (fragS *fragp)
+{
+ static const unsigned char nop_pattern[] = { 0xf0, 0x00 };
+ static const unsigned char multi_nop_pattern[] = { 0x70, 0x00, 0xf0, 0x00 };
+
+ int bytes, fix;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ if (bytes & 1)
+ {
+ fix = 1;
+ *p++ = 0;
+ bytes--;
+ }
+
+ if (bytes & 2)
+ {
+ memcpy (p, nop_pattern, 2);
+ p += 2;
+ bytes -= 2;
+ fix += 2;
+ }
+
+ memcpy (p, multi_nop_pattern, 4);
+
+ fragp->fr_fix += fix;
+ fragp->fr_var = 4;
+}
+
+/* If the last instruction was the first of 2 16 bit insns,
+ output a nop to move the PC to a 32 bit boundary.
+
+ This is done via an alignment specification since branch relaxing
+ may make it unnecessary.
+
+ Internally, we need to output one of these each time a 32 bit insn is
+ seen after an insn that is relaxable. */
+
+static void
+fill_insn (int ignore ATTRIBUTE_UNUSED)
+{
+ frag_align_code (2, 0);
+ prev_insn.insn = NULL;
+ seen_relaxable_p = 0;
+}
+
+/* Record the symbol so that when we output the insn, we can create
+ a symbol that is at the start of the instruction. This is used
+ to emit the label for the start of a breakpoint without causing
+ the assembler to emit a NOP if the previous instruction was a
+ 16 bit instruction. */
+
+static void
+debug_sym (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char delim;
+ char *end_name;
+ symbolS *symbolP;
+ sym_linkS *lnk;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+
+ if ((symbolP = symbol_find (name)) == NULL
+ && (symbolP = md_undefined_symbol (name)) == NULL)
+ symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
+
+ symbol_table_insert (symbolP);
+ if (S_IS_DEFINED (symbolP) && (S_GET_SEGMENT (symbolP) != reg_section
+ || S_IS_EXTERNAL (symbolP)
+ || S_IS_WEAK (symbolP)))
+ /* xgettext:c-format */
+ as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
+
+ else
+ {
+ lnk = (sym_linkS *) xmalloc (sizeof (sym_linkS));
+ lnk->symbol = symbolP;
+ lnk->next = debug_sym_link;
+ debug_sym_link = lnk;
+ symbol_get_obj (symbolP)->local = 1;
+ }
+
+ *end_name = delim;
+ demand_empty_rest_of_line ();
+}
+
+/* Second pass to expanding the debug symbols, go through linked
+ list of symbols and reassign the address. */
+
+static void
+expand_debug_syms (sym_linkS *syms, int align)
+{
+ char *save_input_line = input_line_pointer;
+ sym_linkS *next_syms;
+
+ if (!syms)
+ return;
+
+ (void) frag_align_code (align, 0);
+ for (; syms != (sym_linkS *) 0; syms = next_syms)
+ {
+ symbolS *symbolP = syms->symbol;
+ next_syms = syms->next;
+ input_line_pointer = ".\n";
+ pseudo_set (symbolP);
+ free ((char *) syms);
+ }
+
+ input_line_pointer = save_input_line;
+}
+
+void
+m32r_flush_pending_output (void)
+{
+ if (debug_sym_link)
+ {
+ expand_debug_syms (debug_sym_link, 1);
+ debug_sym_link = (sym_linkS *) 0;
+ }
+}
+
+/* Cover function to fill_insn called after a label and at end of assembly.
+ The result is always 1: we're called in a conditional to see if the
+ current line is a label. */
+
+int
+m32r_fill_insn (int done)
+{
+ if (prev_seg != NULL)
+ {
+ segT seg = now_seg;
+ subsegT subseg = now_subseg;
+
+ subseg_set (prev_seg, prev_subseg);
+
+ fill_insn (0);
+
+ subseg_set (seg, subseg);
+ }
+
+ if (done && debug_sym_link)
+ {
+ expand_debug_syms (debug_sym_link, 1);
+ debug_sym_link = (sym_linkS *) 0;
+ }
+
+ return 1;
+}
+
+/* The default target format to use. */
+
+const char *
+m32r_target_format (void)
+{
+#ifdef TE_LINUX
+ if (target_big_endian)
+ return "elf32-m32r-linux";
+ else
+ return "elf32-m32rle-linux";
+#else
+ if (target_big_endian)
+ return "elf32-m32r";
+ else
+ return "elf32-m32rle";
+#endif
+}
+
+void
+md_begin (void)
+{
+ flagword applicable;
+ segT seg;
+ subsegT subseg;
+
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = m32r_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ (target_big_endian ?
+ CGEN_ENDIAN_BIG : CGEN_ENDIAN_LITTLE),
+ CGEN_CPU_OPEN_END);
+ m32r_cgen_init_asm (gas_cgen_cpu_desc);
+
+ /* The operand instance table is used during optimization to determine
+ which insns can be executed in parallel. It is also used to give
+ warnings regarding operand interference in parallel insns. */
+ m32r_cgen_init_opinst_table (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);
+
+ /* Save the current subseg so we can restore it [it's the default one and
+ we don't want the initial section to be .sbss]. */
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The sbss section is for local .scomm symbols. */
+ sbss_section = subseg_new (".sbss", 0);
+ seg_info (sbss_section)->bss = 1;
+
+ /* This is copied from perform_an_assembly_pass. */
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, sbss_section, applicable & SEC_ALLOC);
+
+ subseg_set (seg, subseg);
+
+ /* We must construct a fake section similar to bfd_com_section
+ but with the name .scommon. */
+ scom_section = *bfd_com_section_ptr;
+ scom_section.name = ".scommon";
+ scom_section.output_section = & scom_section;
+ scom_section.symbol = & scom_symbol;
+ scom_section.symbol_ptr_ptr = & scom_section.symbol;
+ scom_symbol = * bfd_com_section_ptr->symbol;
+ scom_symbol.name = ".scommon";
+ scom_symbol.section = & scom_section;
+
+ allow_m32rx (enable_m32rx);
+
+ gas_cgen_initialize_saved_fixups_array ();
+}
+
+#define OPERAND_IS_COND_BIT(operand, indices, index) \
+ ((operand)->hw_type == HW_H_COND \
+ || ((operand)->hw_type == HW_H_PSW) \
+ || ((operand)->hw_type == HW_H_CR \
+ && (indices [index] == 0 || indices [index] == 1)))
+
+/* Returns true if an output of instruction 'a' is referenced by an operand
+ of instruction 'b'. If 'check_outputs' is true then b's outputs are
+ checked, otherwise its inputs are examined. */
+
+static int
+first_writes_to_seconds_operands (m32r_insn *a,
+ m32r_insn *b,
+ const int check_outputs)
+{
+ const CGEN_OPINST *a_operands = CGEN_INSN_OPERANDS (a->insn);
+ const CGEN_OPINST *b_ops = CGEN_INSN_OPERANDS (b->insn);
+ int a_index;
+
+ if (ignore_parallel_conflicts)
+ return 0;
+
+ /* If at least one of the instructions takes no operands, then there is
+ nothing to check. There really are instructions without operands,
+ eg 'nop'. */
+ if (a_operands == NULL || b_ops == NULL)
+ return 0;
+
+ /* Scan the operand list of 'a' looking for an output operand. */
+ for (a_index = 0;
+ a_operands->type != CGEN_OPINST_END;
+ a_index ++, a_operands ++)
+ {
+ if (a_operands->type == CGEN_OPINST_OUTPUT)
+ {
+ int b_index;
+ const CGEN_OPINST *b_operands = b_ops;
+
+ /* Special Case:
+ The Condition bit 'C' is a shadow of the CBR register (control
+ register 1) and also a shadow of bit 31 of the program status
+ word (control register 0). For now this is handled here, rather
+ than by cgen.... */
+
+ if (OPERAND_IS_COND_BIT (a_operands, a->indices, a_index))
+ {
+ /* Scan operand list of 'b' looking for another reference to the
+ condition bit, which goes in the right direction. */
+ for (b_index = 0;
+ b_operands->type != CGEN_OPINST_END;
+ b_index++, b_operands++)
+ {
+ if ((b_operands->type
+ == (check_outputs
+ ? CGEN_OPINST_OUTPUT
+ : CGEN_OPINST_INPUT))
+ && OPERAND_IS_COND_BIT (b_operands, b->indices, b_index))
+ return 1;
+ }
+ }
+ else
+ {
+ /* Scan operand list of 'b' looking for an operand that
+ references the same hardware element, and which goes in the
+ right direction. */
+ for (b_index = 0;
+ b_operands->type != CGEN_OPINST_END;
+ b_index++, b_operands++)
+ {
+ if ((b_operands->type
+ == (check_outputs
+ ? CGEN_OPINST_OUTPUT
+ : CGEN_OPINST_INPUT))
+ && (b_operands->hw_type == a_operands->hw_type)
+ && (a->indices[a_index] == b->indices[b_index]))
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Returns true if the insn can (potentially) alter the program counter. */
+
+static int
+writes_to_pc (m32r_insn *a)
+{
+ if (CGEN_INSN_ATTR_VALUE (a->insn, CGEN_INSN_UNCOND_CTI)
+ || CGEN_INSN_ATTR_VALUE (a->insn, CGEN_INSN_COND_CTI))
+ return 1;
+ return 0;
+}
+
+/* Return NULL if the two 16 bit insns can be executed in parallel.
+ Otherwise return a pointer to an error message explaining why not. */
+
+static const char *
+can_make_parallel (m32r_insn *a, m32r_insn *b)
+{
+ PIPE_ATTR a_pipe;
+ PIPE_ATTR b_pipe;
+
+ /* Make sure the instructions are the right length. */
+ if (CGEN_FIELDS_BITSIZE (&a->fields) != 16
+ || CGEN_FIELDS_BITSIZE (&b->fields) != 16)
+ abort ();
+
+ if (first_writes_to_seconds_operands (a, b, TRUE))
+ return _("instructions write to the same destination register.");
+
+ a_pipe = CGEN_INSN_ATTR_VALUE (a->insn, CGEN_INSN_PIPE);
+ b_pipe = CGEN_INSN_ATTR_VALUE (b->insn, CGEN_INSN_PIPE);
+
+ /* Make sure that the instructions use the correct execution pipelines. */
+ if (a_pipe == PIPE_NONE
+ || b_pipe == PIPE_NONE)
+ return _("Instructions do not use parallel execution pipelines.");
+
+ /* Leave this test for last, since it is the only test that can
+ go away if the instructions are swapped, and we want to make
+ sure that any other errors are detected before this happens. */
+ if (a_pipe == PIPE_S
+ || b_pipe == PIPE_O
+ || (b_pipe == PIPE_O_OS && (enable_m32rx != 2)))
+ return _("Instructions share the same execution pipeline");
+
+ return NULL;
+}
+
+/* Force the top bit of the second 16-bit insn to be set. */
+
+static void
+make_parallel (CGEN_INSN_BYTES_PTR buffer)
+{
+#if CGEN_INT_INSN_P
+ *buffer |= 0x8000;
+#else
+ buffer[CGEN_CPU_ENDIAN (gas_cgen_cpu_desc) == CGEN_ENDIAN_BIG ? 0 : 1]
+ |= 0x80;
+#endif
+}
+
+/* Same as make_parallel except buffer contains the bytes in target order. */
+
+static void
+target_make_parallel (char *buffer)
+{
+ buffer[CGEN_CPU_ENDIAN (gas_cgen_cpu_desc) == CGEN_ENDIAN_BIG ? 0 : 1]
+ |= 0x80;
+}
+
+/* Assemble two instructions with an explicit parallel operation (||) or
+ sequential operation (->). */
+
+static void
+assemble_two_insns (char *str1, char *str2, int parallel_p)
+{
+ char *str3;
+ m32r_insn first;
+ m32r_insn second;
+ char *errmsg;
+ char save_str2 = *str2;
+
+ /* Separate the two instructions. */
+ *str2 = 0;
+
+ /* Make sure the two insns begin on a 32 bit boundary.
+ This is also done for the serial case (foo -> bar), relaxing doesn't
+ affect insns written like this.
+ Note that we must always do this as we can't assume anything about
+ whether we're currently on a 32 bit boundary or not. Relaxing may
+ change this. */
+ fill_insn (0);
+
+ first.debug_sym_link = debug_sym_link;
+ debug_sym_link = (sym_linkS *) 0;
+
+ /* Parse the first instruction. */
+ if (! (first.insn = m32r_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str1, & first.fields, first.buffer, & errmsg)))
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Check it. */
+ if (CGEN_FIELDS_BITSIZE (&first.fields) != 16)
+ {
+ /* xgettext:c-format */
+ as_bad (_("not a 16 bit instruction '%s'"), str1);
+ return;
+ }
+#ifdef E_M32R2_ARCH
+ else if ((enable_m32rx == 1)
+ /* FIXME: Need standard macro to perform this test. */
+ && ((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32R2))
+ && !((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32RX)))))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32R2 only"), str1);
+ return;
+ }
+ else if ((! enable_special
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL))
+ || (! enable_special_m32r
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL_M32R)))
+#else
+ else if (! enable_special
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL))
+#endif
+ {
+ /* xgettext:c-format */
+ as_bad (_("unknown instruction '%s'"), str1);
+ return;
+ }
+ else if (! enable_m32rx
+ /* FIXME: Need standard macro to perform this test. */
+ && (CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ == (1 << MACH_M32RX)))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32RX only"), str1);
+ return;
+ }
+
+ /* Check to see if this is an allowable parallel insn. */
+ if (parallel_p
+ && CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_PIPE) == PIPE_NONE)
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' cannot be executed in parallel."), str1);
+ return;
+ }
+
+ /* Restore the original assembly text, just in case it is needed. */
+ *str2 = save_str2;
+
+ /* Save the original string pointer. */
+ str3 = str1;
+
+ /* Advanced past the parsed string. */
+ str1 = str2 + 2;
+
+ /* Remember the entire string in case it is needed for error
+ messages. */
+ str2 = str3;
+
+ /* Convert the opcode to lower case. */
+ {
+ char *s2 = str1;
+
+ while (ISSPACE (*s2++))
+ continue;
+
+ --s2;
+
+ while (ISALNUM (*s2))
+ {
+ *s2 = TOLOWER (*s2);
+ s2++;
+ }
+ }
+
+ /* Preserve any fixups that have been generated and reset the list
+ to empty. */
+ gas_cgen_save_fixups (0);
+
+ /* Get the indices of the operands of the instruction. */
+ /* FIXME: CGEN_FIELDS is already recorded, but relying on that fact
+ doesn't seem right. Perhaps allow passing fields like we do insn. */
+ /* FIXME: ALIAS insns do not have operands, so we use this function
+ to find the equivalent insn and overwrite the value stored in our
+ structure. We still need the original insn, however, since this
+ may have certain attributes that are not present in the unaliased
+ version (eg relaxability). When aliases behave differently this
+ may have to change. */
+ first.orig_insn = first.insn;
+ {
+ CGEN_FIELDS tmp_fields;
+ first.insn = cgen_lookup_get_insn_operands
+ (gas_cgen_cpu_desc, NULL, INSN_VALUE (first.buffer), NULL, 16,
+ first.indices, &tmp_fields);
+ }
+
+ if (first.insn == NULL)
+ as_fatal (_("internal error: lookup/get operands failed"));
+
+ second.debug_sym_link = NULL;
+
+ /* Parse the second instruction. */
+ if (! (second.insn = m32r_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str1, & second.fields, second.buffer, & errmsg)))
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Check it. */
+ if (CGEN_FIELDS_BITSIZE (&second.fields) != 16)
+ {
+ /* xgettext:c-format */
+ as_bad (_("not a 16 bit instruction '%s'"), str1);
+ return;
+ }
+#ifdef E_M32R2_ARCH
+ else if ((enable_m32rx == 1)
+ /* FIXME: Need standard macro to perform this test. */
+ && ((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32R2))
+ && !((CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32RX)))))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32R2 only"), str1);
+ return;
+ }
+ else if ((! enable_special
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL))
+ || (! enable_special_m32r
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL_M32R)))
+#else
+ else if (! enable_special
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL))
+#endif
+ {
+ /* xgettext:c-format */
+ as_bad (_("unknown instruction '%s'"), str1);
+ return;
+ }
+ else if (! enable_m32rx
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32RX only"), str1);
+ return;
+ }
+
+ /* Check to see if this is an allowable parallel insn. */
+ if (parallel_p
+ && CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_PIPE) == PIPE_NONE)
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' cannot be executed in parallel."), str1);
+ return;
+ }
+
+ if (parallel_p && ! enable_m32rx)
+ {
+ if (CGEN_INSN_NUM (first.insn) != M32R_INSN_NOP
+ && CGEN_INSN_NUM (second.insn) != M32R_INSN_NOP)
+ {
+ /* xgettext:c-format */
+ as_bad (_("'%s': only the NOP instruction can be issued in parallel on the m32r"), str2);
+ return;
+ }
+ }
+
+ /* Get the indices of the operands of the instruction. */
+ second.orig_insn = second.insn;
+ {
+ CGEN_FIELDS tmp_fields;
+ second.insn = cgen_lookup_get_insn_operands
+ (gas_cgen_cpu_desc, NULL, INSN_VALUE (second.buffer), NULL, 16,
+ second.indices, &tmp_fields);
+ }
+
+ if (second.insn == NULL)
+ as_fatal (_("internal error: lookup/get operands failed"));
+
+ /* We assume that if the first instruction writes to a register that is
+ read by the second instruction it is because the programmer intended
+ this to happen, (after all they have explicitly requested that these
+ two instructions be executed in parallel). Although if the global
+ variable warn_explicit_parallel_conflicts is true then we do generate
+ a warning message. Similarly we assume that parallel branch and jump
+ instructions are deliberate and should not produce errors. */
+
+ if (parallel_p && warn_explicit_parallel_conflicts)
+ {
+ if (first_writes_to_seconds_operands (&first, &second, FALSE))
+ /* xgettext:c-format */
+ as_warn (_("%s: output of 1st instruction is the same as an input to 2nd instruction - is this intentional ?"), str2);
+
+ if (first_writes_to_seconds_operands (&second, &first, FALSE))
+ /* xgettext:c-format */
+ as_warn (_("%s: output of 2nd instruction is the same as an input to 1st instruction - is this intentional ?"), str2);
+ }
+
+ if (!parallel_p
+ || (errmsg = (char *) can_make_parallel (&first, &second)) == NULL)
+ {
+ /* Get the fixups for the first instruction. */
+ gas_cgen_swap_fixups (0);
+
+ /* Write it out. */
+ expand_debug_syms (first.debug_sym_link, 1);
+ gas_cgen_finish_insn (first.orig_insn, first.buffer,
+ CGEN_FIELDS_BITSIZE (&first.fields), 0, NULL);
+
+ /* Force the top bit of the second insn to be set. */
+ if (parallel_p)
+ make_parallel (second.buffer);
+
+ /* Get its fixups. */
+ gas_cgen_restore_fixups (0);
+
+ /* Write it out. */
+ expand_debug_syms (second.debug_sym_link, 1);
+ gas_cgen_finish_insn (second.orig_insn, second.buffer,
+ CGEN_FIELDS_BITSIZE (&second.fields), 0, NULL);
+ }
+ /* Try swapping the instructions to see if they work that way. */
+ else if (can_make_parallel (&second, &first) == NULL)
+ {
+ /* Write out the second instruction first. */
+ expand_debug_syms (second.debug_sym_link, 1);
+ gas_cgen_finish_insn (second.orig_insn, second.buffer,
+ CGEN_FIELDS_BITSIZE (&second.fields), 0, NULL);
+
+ /* Force the top bit of the first instruction to be set. */
+ make_parallel (first.buffer);
+
+ /* Get the fixups for the first instruction. */
+ gas_cgen_restore_fixups (0);
+
+ /* Write out the first instruction. */
+ expand_debug_syms (first.debug_sym_link, 1);
+ gas_cgen_finish_insn (first.orig_insn, first.buffer,
+ CGEN_FIELDS_BITSIZE (&first.fields), 0, NULL);
+ }
+ else
+ {
+ as_bad ("'%s': %s", str2, errmsg);
+ return;
+ }
+
+ if (CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL)
+ || CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL))
+ m32r_flags |= E_M32R_HAS_HIDDEN_INST;
+ if (CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL_M32R)
+ || CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL_M32R))
+ m32r_flags |= E_M32R_HAS_BIT_INST;
+ if (CGEN_INSN_ATTR_VALUE (first.insn, CGEN_INSN_SPECIAL_FLOAT)
+ || CGEN_INSN_ATTR_VALUE (second.insn, CGEN_INSN_SPECIAL_FLOAT))
+ m32r_flags |= E_M32R_HAS_FLOAT_INST;
+
+ /* Set these so m32r_fill_insn can use them. */
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+}
+
+void
+md_assemble (char *str)
+{
+ m32r_insn insn;
+ char *errmsg;
+ char *str2 = NULL;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ /* Look for a parallel instruction separator. */
+ if ((str2 = strstr (str, "||")) != NULL)
+ {
+ assemble_two_insns (str, str2, 1);
+ m32r_flags |= E_M32R_HAS_PARALLEL;
+ return;
+ }
+
+ /* Also look for a sequential instruction separator. */
+ if ((str2 = strstr (str, "->")) != NULL)
+ {
+ assemble_two_insns (str, str2, 0);
+ return;
+ }
+
+ insn.debug_sym_link = debug_sym_link;
+ debug_sym_link = (sym_linkS *) 0;
+
+ insn.insn = m32r_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, &insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+#ifdef E_M32R2_ARCH
+ if ((enable_m32rx == 1)
+ /* FIXME: Need standard macro to perform this test. */
+ && ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32R2))
+ && !((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MACH)
+ & (1 << MACH_M32RX)))))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32R2 only"), str);
+ return;
+ }
+ else if ((! enable_special
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SPECIAL))
+ || (! enable_special_m32r
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SPECIAL_M32R)))
+#else
+ if (! enable_special
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SPECIAL))
+#endif
+ {
+ /* xgettext:c-format */
+ as_bad (_("unknown instruction '%s'"), str);
+ return;
+ }
+ else if (! enable_m32rx
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MACH) == (1 << MACH_M32RX))
+ {
+ /* xgettext:c-format */
+ as_bad (_("instruction '%s' is for the M32RX only"), str);
+ return;
+ }
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SPECIAL))
+ m32r_flags |= E_M32R_HAS_HIDDEN_INST;
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SPECIAL_M32R))
+ m32r_flags |= E_M32R_HAS_BIT_INST;
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_SPECIAL_FLOAT))
+ m32r_flags |= E_M32R_HAS_FLOAT_INST;
+
+ if (CGEN_INSN_BITSIZE (insn.insn) == 32)
+ {
+ /* 32 bit insns must live on 32 bit boundaries. */
+ if (prev_insn.insn || seen_relaxable_p)
+ {
+ /* ??? If calling fill_insn too many times turns us into a memory
+ pig, can we call a fn to assemble a nop instead of
+ !seen_relaxable_p? */
+ fill_insn (0);
+ }
+
+ expand_debug_syms (insn.debug_sym_link, 2);
+
+ /* 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);
+ }
+ else
+ {
+ int on_32bit_boundary_p;
+ int swap = FALSE;
+
+ if (CGEN_INSN_BITSIZE (insn.insn) != 16)
+ abort ();
+
+ insn.orig_insn = insn.insn;
+
+ /* If the previous insn was relaxable, then it may be expanded
+ to fill the current 16 bit slot. Emit a NOP here to occupy
+ this slot, so that we can start at optimizing at a 32 bit
+ boundary. */
+ if (prev_insn.insn && seen_relaxable_p && optimize)
+ fill_insn (0);
+
+ if (enable_m32rx)
+ {
+ /* Get the indices of the operands of the instruction.
+ FIXME: See assemble_parallel for notes on orig_insn. */
+ {
+ CGEN_FIELDS tmp_fields;
+ insn.insn = cgen_lookup_get_insn_operands
+ (gas_cgen_cpu_desc, NULL, INSN_VALUE (insn.buffer), NULL,
+ 16, insn.indices, &tmp_fields);
+ }
+
+ if (insn.insn == NULL)
+ as_fatal (_("internal error: lookup/get operands failed"));
+ }
+
+ /* Compute whether we're on a 32 bit boundary or not.
+ prev_insn.insn is NULL when we're on a 32 bit boundary. */
+ on_32bit_boundary_p = prev_insn.insn == NULL;
+
+ /* Change a frag to, if each insn to swap is in a different frag.
+ It must keep only one instruction in a frag. */
+ if (parallel() && on_32bit_boundary_p)
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+
+ /* Look to see if this instruction can be combined with the
+ previous instruction to make one, parallel, 32 bit instruction.
+ If the previous instruction (potentially) changed the flow of
+ program control, then it cannot be combined with the current
+ instruction. If the current instruction is relaxable, then it
+ might be replaced with a longer version, so we cannot combine it.
+ Also if the output of the previous instruction is used as an
+ input to the current instruction then it cannot be combined.
+ Otherwise call can_make_parallel() with both orderings of the
+ instructions to see if they can be combined. */
+ if (! on_32bit_boundary_p
+ && parallel ()
+ && CGEN_INSN_ATTR_VALUE (insn.orig_insn, CGEN_INSN_RELAXABLE) == 0
+ && ! writes_to_pc (&prev_insn)
+ && ! first_writes_to_seconds_operands (&prev_insn, &insn, FALSE))
+ {
+ if (can_make_parallel (&prev_insn, &insn) == NULL)
+ make_parallel (insn.buffer);
+ else if (can_make_parallel (&insn, &prev_insn) == NULL)
+ swap = TRUE;
+ }
+
+ expand_debug_syms (insn.debug_sym_link, 1);
+
+ {
+ int i;
+ finished_insnS fi;
+
+ /* Ensure each pair of 16 bit insns is in the same frag. */
+ frag_grow (4);
+
+ gas_cgen_finish_insn (insn.orig_insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (&insn.fields),
+ 1 /* relax_p */, &fi);
+ insn.addr = fi.addr;
+ insn.frag = fi.frag;
+ insn.num_fixups = fi.num_fixups;
+ for (i = 0; i < fi.num_fixups; ++i)
+ insn.fixups[i] = fi.fixups[i];
+ }
+
+ if (swap)
+ {
+ int i, tmp;
+
+#define SWAP_BYTES(a,b) tmp = a; a = b; b = tmp
+
+ /* Swap the two insns */
+ SWAP_BYTES (prev_insn.addr[0], insn.addr[0]);
+ SWAP_BYTES (prev_insn.addr[1], insn.addr[1]);
+
+ target_make_parallel (insn.addr);
+
+ /* Swap any relaxable frags recorded for the two insns. */
+ /* FIXME: Clarify. relaxation precludes parallel insns */
+ if (prev_insn.frag->fr_opcode == prev_insn.addr)
+ prev_insn.frag->fr_opcode = insn.addr;
+ else if (insn.frag->fr_opcode == insn.addr)
+ insn.frag->fr_opcode = prev_insn.addr;
+
+ /* Change a frag to, if each insn is in a different frag.
+ It must keep only one instruction in a frag. */
+ if (prev_insn.frag != insn.frag)
+ {
+ for (i = 0; i < prev_insn.num_fixups; ++i)
+ prev_insn.fixups[i]->fx_frag = insn.frag;
+ for (i = 0; i < insn.num_fixups; ++i)
+ insn.fixups[i]->fx_frag = prev_insn.frag;
+ }
+ else
+ {
+ /* Update the addresses in any fixups.
+ Note that we don't have to handle the case where each insn is in
+ a different frag as we ensure they're in the same frag above. */
+ for (i = 0; i < prev_insn.num_fixups; ++i)
+ prev_insn.fixups[i]->fx_where += 2;
+ for (i = 0; i < insn.num_fixups; ++i)
+ insn.fixups[i]->fx_where -= 2;
+ }
+ }
+
+ /* Keep track of whether we've seen a pair of 16 bit insns.
+ prev_insn.insn is NULL when we're on a 32 bit boundary. */
+ if (on_32bit_boundary_p)
+ prev_insn = insn;
+ else
+ prev_insn.insn = NULL;
+
+ /* If the insn needs the following one to be on a 32 bit boundary
+ (e.g. subroutine calls), fill this insn's slot. */
+ if (on_32bit_boundary_p
+ && CGEN_INSN_ATTR_VALUE (insn.orig_insn, CGEN_INSN_FILL_SLOT) != 0)
+ fill_insn (0);
+
+ /* If this is a relaxable insn (can be replaced with a larger version)
+ mark the fact so that we can emit an alignment directive for a
+ following 32 bit insn if we see one. */
+ if (CGEN_INSN_ATTR_VALUE (insn.orig_insn, CGEN_INSN_RELAXABLE) != 0)
+ seen_relaxable_p = 1;
+ }
+
+ /* Set these so m32r_fill_insn can use them. */
+ prev_seg = now_seg;
+ prev_subseg = now_subseg;
+}
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer++;
+ expression (expressionP);
+ }
+}
+
+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;
+}
+
+/* .scomm pseudo-op handler.
+
+ This is a new pseudo-op to handle putting objects in .scommon.
+ By doing this the linker won't need to do any work,
+ and more importantly it removes the implicit -G arg necessary to
+ correctly link the object file. */
+
+static void
+m32r_scomm (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+ offsetT align;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Skip ','. */
+ input_line_pointer++;
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ /* xgettext:c-format */
+ as_warn (_(".SCOMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .scomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+
+ /* Convert to a power of 2 alignment. */
+ if (align)
+ {
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2)
+ continue;
+ if (align != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP))
+ {
+ /* xgettext:c-format */
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ /* xgettext:c-format */
+ as_bad (_("Length of .scomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (symbol_get_obj (symbolP)->local)
+ {
+ segT old_sec = now_seg;
+ int old_subsec = now_subseg;
+ char *pfrag;
+
+ record_alignment (sbss_section, align2);
+ subseg_set (sbss_section, 0);
+
+ if (align2)
+ frag_align (align2, 0, 0);
+
+ if (S_GET_SEGMENT (symbolP) == sbss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+
+ symbol_set_frag (symbolP, frag_now);
+
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, sbss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, align2);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, &scom_section);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { "fillinsn", fill_insn, 0 },
+ { "scomm", m32r_scomm, 0 },
+ { "debugsym", debug_sym, 0 },
+ { "m32r", allow_m32rx, 0 },
+ { "m32rx", allow_m32rx, 1 },
+ { "m32r2", allow_m32rx, 2 },
+ { "little", little, 1 },
+ { "big", little, 0 },
+ { NULL, NULL, 0 }
+};
+
+/* Interface to relax_segment. */
+
+/* FIXME: Build table by hand, get it working, then machine generate. */
+
+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 add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one. */
+
+ /* The first entry must be unused because an `rlx_more' value of zero ends
+ each list. */
+ {1, 1, 0, 0},
+
+ /* The displacement used by GAS is from the end of the 2 byte insn,
+ so we subtract 2 from the following. */
+ /* 16 bit insn, 8 bit disp -> 10 bit range.
+ This doesn't handle a branch in the right slot at the border:
+ the "& -4" isn't taken into account. It's not important enough to
+ complicate things over it, so we subtract an extra 2 (or + 2 in -ve
+ case). */
+ {511 - 2 - 2, -512 - 2 + 2, 0, 2 },
+ /* 32 bit insn, 24 bit disp -> 26 bit range. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 2, 0 },
+ /* Same thing, but with leading nop for alignment. */
+ {0x2000000 - 1 - 2, -0x2000000 - 2, 4, 0 }
+};
+
+long
+m32r_relax_frag (segT segment, fragS *fragP, long stretch)
+{
+ /* Address of branch insn. */
+ long address = fragP->fr_address + fragP->fr_fix - 2;
+ long growth = 0;
+
+ /* Keep 32 bit insns aligned on 32 bit boundaries. */
+ if (fragP->fr_subtype == 2)
+ {
+ if ((address & 3) != 0)
+ {
+ fragP->fr_subtype = 3;
+ growth = 2;
+ }
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ if ((address & 3) == 0)
+ {
+ fragP->fr_subtype = 2;
+ growth = -2;
+ }
+ }
+ else
+ {
+ growth = relax_frag (segment, fragP, stretch);
+
+ /* Long jump on odd halfword boundary? */
+ if (fragP->fr_subtype == 2 && (address & 3) != 0)
+ {
+ fragP->fr_subtype = 3;
+ growth += 2;
+ }
+ }
+
+ return growth;
+}
+
+/* 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)
+{
+ /* The only thing we have to handle here are symbols outside of the
+ current segment. They may be undefined or in a different segment in
+ which case linker scripts may place them anywhere.
+ However, we can't finish the fragment here and emit the reloc as insn
+ alignment requirements may move the insn about. */
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ /* The symbol is undefined in this segment.
+ Change the relaxation subtype to the max allowable and leave
+ all further handling to md_convert_frag. */
+ fragP->fr_subtype = 2;
+
+ {
+ const CGEN_INSN *insn;
+ int i;
+
+ /* Update the recorded insn.
+ Fortunately we don't have to look very far.
+ FIXME: Change this to record in the instruction the next higher
+ relaxable insn to use. */
+ for (i = 0, insn = fragP->fr_cgen.insn; i < 4; i++, insn++)
+ {
+ if ((strcmp (CGEN_INSN_MNEMONIC (insn),
+ CGEN_INSN_MNEMONIC (fragP->fr_cgen.insn))
+ == 0)
+ && CGEN_INSN_ATTR_VALUE (insn, CGEN_INSN_RELAXED))
+ break;
+ }
+ if (i == 4)
+ abort ();
+
+ fragP->fr_cgen.insn = insn;
+ return 2;
+ }
+ }
+
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec,
+ fragS *fragP)
+{
+ char *opcode;
+ char *displacement;
+ int target_address;
+ int opcode_address;
+ int extension;
+ int addend;
+
+ opcode = fragP->fr_opcode;
+
+ /* Address opcode resides at in file space. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - 2;
+
+ switch (fragP->fr_subtype)
+ {
+ case 1:
+ extension = 0;
+ displacement = &opcode[1];
+ break;
+ case 2:
+ opcode[0] |= 0x80;
+ extension = 2;
+ displacement = &opcode[1];
+ break;
+ case 3:
+ opcode[2] = opcode[0] | 0x80;
+ md_number_to_chars (opcode, PAR_NOP_INSN, 2);
+ opcode_address += 2;
+ extension = 4;
+ displacement = &opcode[3];
+ break;
+ default:
+ abort ();
+ }
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ /* Symbol must be resolved by linker. */
+ if (fragP->fr_offset & 3)
+ as_warn (_("Addend to unresolved symbol not on word boundary."));
+#ifdef USE_M32R_OLD_RELOC
+ addend = fragP->fr_offset >> 2; /* Old M32R used USE_REL. */
+#else
+ addend = 0;
+#endif
+ }
+ else
+ {
+ /* Address we want to reach in file space. */
+ target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+ addend = (target_address - (opcode_address & -4)) >> 2;
+ }
+
+ /* Create a relocation for symbols that must be resolved by the linker.
+ Otherwise output the completed insn. */
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != sec
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol))
+ {
+ fixS *fixP;
+
+ gas_assert (fragP->fr_subtype != 1);
+ gas_assert (fragP->fr_cgen.insn != 0);
+
+ fixP = gas_cgen_record_fixup (fragP,
+ /* Offset of branch insn in frag. */
+ fragP->fr_fix + extension - 4,
+ fragP->fr_cgen.insn,
+ 4 /* Length. */,
+ /* FIXME: quick hack. */
+ cgen_operand_lookup_by_num (gas_cgen_cpu_desc,
+ M32R_OPERAND_DISP24),
+ fragP->fr_cgen.opinfo,
+ fragP->fr_symbol, fragP->fr_offset);
+ if (fragP->fr_cgen.opinfo)
+ fixP->fx_r_type = fragP->fr_cgen.opinfo;
+ }
+
+#define SIZE_FROM_RELAX_STATE(n) ((n) == 1 ? 1 : 3)
+
+ md_number_to_chars (displacement, (valueT) addend,
+ SIZE_FROM_RELAX_STATE (fragP->fr_subtype));
+
+ fragP->fr_fix += extension;
+}
+
+/* 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
+ || S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)))
+ {
+ if (S_GET_SEGMENT (fixP->fx_addsy) != sec
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! S_IS_EXTERNAL (fixP->fx_addsy)
+ && ! S_IS_WEAK (fixP->fx_addsy))
+ return fixP->fx_offset;
+
+ /* 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) & -4L;
+}
+
+/* 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)
+{
+ switch (operand->type)
+ {
+ case M32R_OPERAND_DISP8: return BFD_RELOC_M32R_10_PCREL;
+ case M32R_OPERAND_DISP16: return BFD_RELOC_M32R_18_PCREL;
+ case M32R_OPERAND_DISP24: return BFD_RELOC_M32R_26_PCREL;
+ case M32R_OPERAND_UIMM24: return BFD_RELOC_M32R_24;
+ case M32R_OPERAND_HI16:
+ case M32R_OPERAND_SLO16:
+ case M32R_OPERAND_ULO16:
+ /* If low/high/shigh/sda was used, it is recorded in `opinfo'. */
+ if (fixP->fx_cgen.opinfo != 0)
+ return fixP->fx_cgen.opinfo;
+ break;
+ default:
+ /* Avoid -Wall warning. */
+ break;
+ }
+ return BFD_RELOC_NONE;
+}
+
+/* Record a HI16 reloc for later matching with its LO16 cousin. */
+
+static void
+m32r_record_hi16 (int reloc_type,
+ fixS *fixP,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ struct m32r_hi_fixup *hi_fixup;
+
+ gas_assert (reloc_type == BFD_RELOC_M32R_HI16_SLO
+ || reloc_type == BFD_RELOC_M32R_HI16_ULO);
+
+ hi_fixup = xmalloc (sizeof (* hi_fixup));
+ hi_fixup->fixp = fixP;
+ hi_fixup->seg = now_seg;
+ hi_fixup->next = m32r_hi_fixup_list;
+
+ m32r_hi_fixup_list = hi_fixup;
+}
+
+/* Called while parsing an instruction to create a fixup.
+ We need to check for HI16 relocs and queue them up for later sorting. */
+
+fixS *
+m32r_cgen_record_fixup_exp (fragS *frag,
+ int where,
+ const CGEN_INSN *insn,
+ int length,
+ const CGEN_OPERAND *operand,
+ int opinfo,
+ expressionS *exp)
+{
+ fixS *fixP;
+ bfd_reloc_code_real_type r_type = BFD_RELOC_UNUSED;
+
+ if (m32r_check_fixup (exp, &r_type))
+ as_bad (_("Invalid PIC expression."));
+
+ fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+
+ switch (operand->type)
+ {
+ case M32R_OPERAND_HI16:
+ /* If low/high/shigh/sda was used, it is recorded in `opinfo'. */
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_SLO
+ || fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_ULO)
+ m32r_record_hi16 (fixP->fx_cgen.opinfo, fixP, now_seg);
+ break;
+
+ default:
+ /* Avoid -Wall warning. */
+ break;
+ }
+
+ switch (r_type)
+ {
+ case BFD_RELOC_UNUSED:
+ default:
+ return fixP;
+
+ case BFD_RELOC_M32R_GOTPC24:
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_SLO)
+ r_type = BFD_RELOC_M32R_GOTPC_HI_SLO;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_ULO)
+ r_type = BFD_RELOC_M32R_GOTPC_HI_ULO;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_LO16)
+ r_type = BFD_RELOC_M32R_GOTPC_LO;
+ break;
+
+ case BFD_RELOC_M32R_GOT24:
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_SLO)
+ r_type = BFD_RELOC_M32R_GOT16_HI_SLO;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_ULO)
+ r_type = BFD_RELOC_M32R_GOT16_HI_ULO;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_LO16)
+ r_type = BFD_RELOC_M32R_GOT16_LO;
+ break;
+
+ case BFD_RELOC_M32R_GOTOFF:
+ if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_SLO)
+ r_type = BFD_RELOC_M32R_GOTOFF_HI_SLO;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_HI16_ULO)
+ r_type = BFD_RELOC_M32R_GOTOFF_HI_ULO;
+ else if (fixP->fx_cgen.opinfo == BFD_RELOC_M32R_LO16)
+ r_type = BFD_RELOC_M32R_GOTOFF_LO;
+ break;
+
+ case BFD_RELOC_M32R_26_PLTREL:
+ as_bad (_("Invalid PIC expression."));
+ break;
+ }
+
+ fixP->fx_r_type = r_type;
+
+ return fixP;
+}
+
+/* Return BFD reloc type from opinfo field in a fixS.
+ It's tricky using fx_r_type in m32r_frob_file because the values
+ are BFD_RELOC_UNUSED + operand number. */
+#define FX_OPINFO_R_TYPE(f) ((f)->fx_cgen.opinfo)
+
+/* Sort any unmatched HI16 relocs so that they immediately precede
+ the corresponding LO16 reloc. This is called before md_apply_fix and
+ tc_gen_reloc. */
+
+void
+m32r_frob_file (void)
+{
+ struct m32r_hi_fixup *l;
+
+ for (l = m32r_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type *seginfo;
+ int pass;
+
+ gas_assert (FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_M32R_HI16_SLO
+ || FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_M32R_HI16_ULO);
+
+ /* Check quickly whether the next fixup happens to be a matching low. */
+ if (l->fixp->fx_next != NULL
+ && FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_M32R_LO16
+ && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+ && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+ continue;
+
+ /* Look through the fixups for this segment for a matching `low'.
+ When we find one, move the high/shigh just in front of it. We do
+ this in two passes. In the first pass, we try to find a
+ unique `low'. In the second pass, we permit multiple high's
+ relocs for a single `low'. */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS *f;
+ fixS *prev;
+
+ prev = NULL;
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a `low' fixup which matches l->fixp. */
+ if (FX_OPINFO_R_TYPE (f) == BFD_RELOC_M32R_LO16
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || (FX_OPINFO_R_TYPE (prev) != BFD_RELOC_M32R_HI16_SLO
+ && FX_OPINFO_R_TYPE (prev) != BFD_RELOC_M32R_HI16_ULO)
+ || prev->fx_addsy != f->fx_addsy
+ || prev->fx_offset != f->fx_offset))
+ {
+ fixS **pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root;
+ *pf != l->fixp;
+ pf = & (*pf)->fx_next)
+ gas_assert (*pf != NULL);
+
+ *pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+ if (pass == 1
+ && warn_unmatched_high)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched high/shigh reloc"));
+ }
+ }
+}
+
+/* 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
+m32r_force_relocation (fixS *fix)
+{
+ if (generic_force_reloc (fix))
+ return 1;
+
+ if (! m32r_relax)
+ return 0;
+
+ return fix->fx_pcrel;
+}
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* 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)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+void
+m32r_elf_section_change_hook (void)
+{
+ /* If we have reached the end of a section and we have just emitted a
+ 16 bit insn, then emit a nop to make sure that the section ends on
+ a 32 bit boundary. */
+
+ if (prev_insn.insn || seen_relaxable_p)
+ (void) m32r_fill_insn (0);
+}
+
+/* Return true if can adjust the reloc to be relative to its section
+ (such as .data) instead of relative to some symbol. */
+
+bfd_boolean
+m32r_fix_adjustable (fixS *fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand =
+ cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
+
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ if (pic_code
+ && (reloc_type == BFD_RELOC_M32R_24
+ || reloc_type == BFD_RELOC_M32R_26_PCREL
+ || reloc_type == BFD_RELOC_M32R_HI16_SLO
+ || reloc_type == BFD_RELOC_M32R_HI16_ULO
+ || reloc_type == BFD_RELOC_M32R_LO16))
+ return 0;
+
+ if (reloc_type == BFD_RELOC_M32R_GOT24
+ || reloc_type == BFD_RELOC_M32R_26_PLTREL
+ || reloc_type == BFD_RELOC_M32R_GOTPC_HI_SLO
+ || reloc_type == BFD_RELOC_M32R_GOTPC_HI_ULO
+ || reloc_type == BFD_RELOC_M32R_GOTPC_LO
+ || reloc_type == BFD_RELOC_M32R_GOT16_HI_SLO
+ || reloc_type == BFD_RELOC_M32R_GOT16_HI_ULO
+ || reloc_type == BFD_RELOC_M32R_GOT16_LO)
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if (reloc_type == BFD_RELOC_VTABLE_INHERIT
+ || reloc_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+void
+m32r_elf_final_processing (void)
+{
+ if (use_parallel)
+ m32r_flags |= E_M32R_HAS_PARALLEL;
+ elf_elfheader (stdoutput)->e_flags |= m32r_flags;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection * section, fixS * fixP)
+{
+ arelent * reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = xmalloc (sizeof (* reloc));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ if (fixP->fx_pcrel)
+ {
+ if (fixP->fx_r_type == BFD_RELOC_32)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ else if (fixP->fx_r_type == BFD_RELOC_16)
+ {
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ bfd_set_error (bfd_error_bad_value);
+ }
+ }
+
+ code = fixP->fx_r_type;
+ if (pic_code)
+ {
+#ifdef DEBUG_PIC
+printf("%s",bfd_get_reloc_code_name(code));
+#endif
+ switch (code)
+ {
+ case BFD_RELOC_M32R_26_PCREL:
+ code = BFD_RELOC_M32R_26_PLTREL;
+ break;
+
+ case BFD_RELOC_M32R_24:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_M32R_GOTPC24;
+ else
+ code = BFD_RELOC_M32R_GOT24;
+ break;
+
+ case BFD_RELOC_M32R_HI16_ULO:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_M32R_GOTPC_HI_ULO;
+ else
+ code = BFD_RELOC_M32R_GOT16_HI_ULO;
+ break;
+
+ case BFD_RELOC_M32R_HI16_SLO:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_M32R_GOTPC_HI_SLO;
+ else
+ code = BFD_RELOC_M32R_GOT16_HI_SLO;
+ break;
+
+ case BFD_RELOC_M32R_LO16:
+ if (fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_M32R_GOTPC_LO;
+ else
+ code = BFD_RELOC_M32R_GOT16_LO;
+ break;
+
+ default:
+ break;
+ }
+#ifdef DEBUG_PIC
+printf(" => %s",bfd_get_reloc_code_name(code));
+#endif
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+#ifdef DEBUG_PIC
+printf(" => %s\n",reloc->howto->name);
+#endif
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type, bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ /* Use fx_offset for these cases. */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_32_PCREL)
+ reloc->addend = fixP->fx_offset;
+ else if ((!pic_code
+ && code != BFD_RELOC_M32R_26_PLTREL)
+ && fixP->fx_pcrel
+ && fixP->fx_addsy != NULL
+ && (S_GET_SEGMENT(fixP->fx_addsy) != section)
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! S_IS_EXTERNAL(fixP->fx_addsy)
+ && ! S_IS_WEAK(fixP->fx_addsy))
+ /* Already used fx_offset in the opcode field itseld. */
+ reloc->addend = fixP->fx_offset;
+ else
+ reloc->addend = fixP->fx_addnumber;
+
+ return reloc;
+}
+
+inline static char *
+m32r_end_of_match (char *cont, char *what)
+{
+ int len = strlen (what);
+
+ if (strncasecmp (cont, what, strlen (what)) == 0
+ && ! is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+int
+m32r_parse_name (char const *name,
+ expressionS *exprP,
+ enum expr_mode mode,
+ char *nextcharP)
+{
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ operatorT op_type;
+ segT segment;
+
+ exprP->X_op_symbol = NULL;
+ exprP->X_md = BFD_RELOC_UNUSED;
+
+ if (strcmp (name, GOT_NAME) == 0)
+ {
+ if (! GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+ no_suffix:
+ /* If we have an absolute symbol or a
+ reg, then we know its value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (mode != expr_defer && segment == absolute_section)
+ {
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else if (mode != expr_defer && segment == reg_section)
+ {
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else
+ {
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+ }
+
+ return 1;
+ }
+
+ exprP->X_add_symbol = symbol_find_or_make (name);
+
+ if (*nextcharP != '@')
+ goto no_suffix;
+ else if ((next_end = m32r_end_of_match (next + 1, "GOTOFF")))
+ {
+ reloc_type = BFD_RELOC_M32R_GOTOFF;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = m32r_end_of_match (next + 1, "GOT")))
+ {
+ reloc_type = BFD_RELOC_M32R_GOT24;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = m32r_end_of_match (next + 1, "PLT")))
+ {
+ reloc_type = BFD_RELOC_M32R_26_PLTREL;
+ op_type = O_PIC_reloc;
+ }
+ else
+ goto no_suffix;
+
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = op_type;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
+}
+
+int
+m32r_cgen_parse_fix_exp(int opinfo, expressionS *exp)
+{
+ if (exp->X_op == O_PIC_reloc
+ && exp->X_md == BFD_RELOC_M32R_26_PLTREL)
+ {
+ exp->X_op = O_symbol;
+ opinfo = exp->X_md;
+ }
+
+ return opinfo;
+}
diff --git a/gas/config/tc-m32r.h b/gas/config/tc-m32r.h
new file mode 100644
index 0000000..892b3ab
--- /dev/null
+++ b/gas/config/tc-m32r.h
@@ -0,0 +1,125 @@
+/* tc-m32r.h -- Header file for tc-m32r.c.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_M32R
+
+#define LISTING_HEADER \
+ (target_big_endian ? "M32R GAS" : "M32R GAS Little Endian")
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_m32r
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+#define TARGET_FORMAT m32r_target_format()
+extern const char *m32r_target_format (void);
+
+/* Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+/* Call md_pcrel_from_section, not md_pcrel_from. */
+long md_pcrel_from_section (struct fix *, segT);
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs. */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* For 8 vs 16 vs 32 bit branch selection. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+extern long m32r_relax_frag (segT, fragS *, long);
+#define md_relax_frag(segment, fragP, stretch) \
+ m32r_relax_frag (segment, fragP, stretch)
+
+/* Account for nop if 32 bit insn falls on odd halfword boundary. */
+#define TC_CGEN_MAX_RELAX(insn, len) 6
+
+/* Fill in rs_align_code fragments. */
+extern void m32r_handle_align (fragS *);
+#define HANDLE_ALIGN(f) m32r_handle_align (f)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (1 + 2 + 4)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_apply_fix gas_cgen_md_apply_fix
+
+#define tc_fix_adjustable(FIX) m32r_fix_adjustable (FIX)
+bfd_boolean m32r_fix_adjustable (struct fix *);
+
+/* After creating a fixup for an instruction operand, we need to check for
+ HI16 relocs and queue them up for later sorting. */
+#define md_cgen_record_fixup_exp m32r_cgen_record_fixup_exp
+
+#define TC_HANDLES_FX_DONE
+
+extern int pic_code;
+
+extern bfd_boolean m32r_fix_adjustable (struct fix *);
+
+/* This arranges for gas/write.c to not apply a relocation if
+ obj_fix_adjustable() says it is not adjustable. */
+#define TC_FIX_ADJUSTABLE(fixP) obj_fix_adjustable (fixP)
+
+#define tc_frob_file_before_fix() m32r_frob_file ()
+extern void m32r_frob_file (void);
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden.
+#define EXTERN_FORCE_RELOC 0 */
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) m32r_force_relocation (fix)
+extern int m32r_force_relocation (struct fix *);
+
+/* Ensure insns at labels are aligned to 32 bit boundaries. */
+int m32r_fill_insn (int);
+#define TC_START_LABEL(ch, s, ptr) (ch == ':' && m32r_fill_insn (0))
+
+#define md_cleanup() m32r_fill_insn (1)
+#define md_elf_section_change_hook m32r_elf_section_change_hook
+extern void m32r_elf_section_change_hook (void);
+
+#define md_flush_pending_output() m32r_flush_pending_output ()
+extern void m32r_flush_pending_output (void);
+
+#define elf_tc_final_processing m32r_elf_final_processing
+extern void m32r_elf_final_processing (void);
+
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ m32r_parse_name ((name), (exprP), (mode), (nextcharP))
+extern int m32r_parse_name (char const *, expressionS *, enum expr_mode, char *);
+
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+ symbols. The relocation type is stored in X_md. */
+#define O_PIC_reloc O_md1
+
+#define TC_CGEN_PARSE_FIX_EXP(opinfo, exp) \
+ m32r_cgen_parse_fix_exp(opinfo, exp)
+extern int m32r_cgen_parse_fix_exp (int, expressionS *);
diff --git a/gas/config/tc-m68851.h b/gas/config/tc-m68851.h
new file mode 100644
index 0000000..7c449d6
--- /dev/null
+++ b/gas/config/tc-m68851.h
@@ -0,0 +1,276 @@
+/* This file is tc-m68851.h
+
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
+ License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifdef m68851
+
+/*
+ I didn't use much imagination in choosing the
+ following codes, so many of them aren't very
+ mnemonic. -rab
+
+ P pmmu register
+ Possible values:
+ 000 TC Translation Control reg
+ 100 CAL Current Access Level
+ 101 VAL Validate Access Level
+ 110 SCC Stack Change Control
+ 111 AC Access Control
+
+ W wide pmmu registers
+ Possible values:
+ 001 DRP Dma Root Pointer
+ 010 SRP Supervisor Root Pointer
+ 011 CRP Cpu Root Pointer
+
+ f function code register
+ 0 SFC
+ 1 DFC
+
+ V VAL register only
+
+ X BADx, BACx
+ 100 BAD Breakpoint Acknowledge Data
+ 101 BAC Breakpoint Acknowledge Control
+
+ Y PSR
+ Z PCSR
+
+ | memory (modes 2-6, 7.*)
+
+ */
+
+/*
+ * these defines should be in m68k.c but
+ * i put them here to keep all the m68851 stuff
+ * together -rab
+ * JF--Make sure these #s don't clash with the ones in m68k.c
+ * That would be BAD.
+ */
+#define TC (FPS+1) /* 48 */
+#define DRP (TC+1) /* 49 */
+#define SRP (DRP+1) /* 50 */
+#define CRP (SRP+1) /* 51 */
+#define CAL (CRP+1) /* 52 */
+#define VAL (CAL+1) /* 53 */
+#define SCC (VAL+1) /* 54 */
+#define AC (SCC+1) /* 55 */
+#define BAD (AC+1) /* 56,57,58,59, 60,61,62,63 */
+#define BAC (BAD+8) /* 64,65,66,67, 68,69,70,71 */
+#define PSR (BAC+8) /* 72 */
+#define PCSR (PSR+1) /* 73 */
+
+/* name */ /* opcode */ /* match */ /* args */
+
+{"pbac", one(0xf0c7), one(0xffbf), "Bc"},
+{"pbacw", one(0xf087), one(0xffbf), "Bc"},
+{"pbas", one(0xf0c6), one(0xffbf), "Bc"},
+{"pbasw", one(0xf086), one(0xffbf), "Bc"},
+{"pbbc", one(0xf0c1), one(0xffbf), "Bc"},
+{"pbbcw", one(0xf081), one(0xffbf), "Bc"},
+{"pbbs", one(0xf0c0), one(0xffbf), "Bc"},
+{"pbbsw", one(0xf080), one(0xffbf), "Bc"},
+{"pbcc", one(0xf0cf), one(0xffbf), "Bc"},
+{"pbccw", one(0xf08f), one(0xffbf), "Bc"},
+{"pbcs", one(0xf0ce), one(0xffbf), "Bc"},
+{"pbcsw", one(0xf08e), one(0xffbf), "Bc"},
+{"pbgc", one(0xf0cd), one(0xffbf), "Bc"},
+{"pbgcw", one(0xf08d), one(0xffbf), "Bc"},
+{"pbgs", one(0xf0cc), one(0xffbf), "Bc"},
+{"pbgsw", one(0xf08c), one(0xffbf), "Bc"},
+{"pbic", one(0xf0cb), one(0xffbf), "Bc"},
+{"pbicw", one(0xf08b), one(0xffbf), "Bc"},
+{"pbis", one(0xf0ca), one(0xffbf), "Bc"},
+{"pbisw", one(0xf08a), one(0xffbf), "Bc"},
+{"pblc", one(0xf0c3), one(0xffbf), "Bc"},
+{"pblcw", one(0xf083), one(0xffbf), "Bc"},
+{"pbls", one(0xf0c2), one(0xffbf), "Bc"},
+{"pblsw", one(0xf082), one(0xffbf), "Bc"},
+{"pbsc", one(0xf0c5), one(0xffbf), "Bc"},
+{"pbscw", one(0xf085), one(0xffbf), "Bc"},
+{"pbss", one(0xf0c4), one(0xffbf), "Bc"},
+{"pbssw", one(0xf084), one(0xffbf), "Bc"},
+{"pbwc", one(0xf0c9), one(0xffbf), "Bc"},
+{"pbwcw", one(0xf089), one(0xffbf), "Bc"},
+{"pbws", one(0xf0c8), one(0xffbf), "Bc"},
+{"pbwsw", one(0xf088), one(0xffbf), "Bc"},
+
+{"pdbac", two(0xf048, 0x0007), two(0xfff8, 0xffff), "DsBw"},
+{"pdbas", two(0xf048, 0x0006), two(0xfff8, 0xffff), "DsBw"},
+{"pdbbc", two(0xf048, 0x0001), two(0xfff8, 0xffff), "DsBw"},
+{"pdbbs", two(0xf048, 0x0000), two(0xfff8, 0xffff), "DsBw"},
+{"pdbcc", two(0xf048, 0x000f), two(0xfff8, 0xffff), "DsBw"},
+{"pdbcs", two(0xf048, 0x000e), two(0xfff8, 0xffff), "DsBw"},
+{"pdbgc", two(0xf048, 0x000d), two(0xfff8, 0xffff), "DsBw"},
+{"pdbgs", two(0xf048, 0x000c), two(0xfff8, 0xffff), "DsBw"},
+{"pdbic", two(0xf048, 0x000b), two(0xfff8, 0xffff), "DsBw"},
+{"pdbis", two(0xf048, 0x000a), two(0xfff8, 0xffff), "DsBw"},
+{"pdblc", two(0xf048, 0x0003), two(0xfff8, 0xffff), "DsBw"},
+{"pdbls", two(0xf048, 0x0002), two(0xfff8, 0xffff), "DsBw"},
+{"pdbsc", two(0xf048, 0x0005), two(0xfff8, 0xffff), "DsBw"},
+{"pdbss", two(0xf048, 0x0004), two(0xfff8, 0xffff), "DsBw"},
+{"pdbwc", two(0xf048, 0x0009), two(0xfff8, 0xffff), "DsBw"},
+{"pdbws", two(0xf048, 0x0008), two(0xfff8, 0xffff), "DsBw"},
+
+{"pflusha", two(0xf000, 0x2400), two(0xffff, 0xffff), "" },
+
+{"pflush", two(0xf000, 0x3010), two(0xffc0, 0xfe10), "T3T9" },
+{"pflush", two(0xf000, 0x3810), two(0xffc0, 0xfe10), "T3T9&s" },
+{"pflush", two(0xf000, 0x3008), two(0xffc0, 0xfe18), "D3T9" },
+{"pflush", two(0xf000, 0x3808), two(0xffc0, 0xfe18), "D3T9&s" },
+{"pflush", two(0xf000, 0x3000), two(0xffc0, 0xfe1e), "f3T9" },
+{"pflush", two(0xf000, 0x3800), two(0xffc0, 0xfe1e), "f3T9&s" },
+
+{"pflushs", two(0xf000, 0x3410), two(0xfff8, 0xfe10), "T3T9" },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe00), "T3T9&s" },
+{"pflushs", two(0xf000, 0x3408), two(0xfff8, 0xfe18), "D3T9" },
+{"pflushs", two(0xf000, 0x3c08), two(0xfff8, 0xfe18), "D3T9&s" },
+{"pflushs", two(0xf000, 0x3400), two(0xfff8, 0xfe1e), "f3T9" },
+{"pflushs", two(0xf000, 0x3c00), two(0xfff8, 0xfe1e), "f3T9&s"},
+
+{"pflushr", two(0xf000, 0xa000), two(0xffc0, 0xffff), "|s" },
+
+{"ploadr", two(0xf000, 0x2210), two(0xffc0, 0xfff0), "T3&s" },
+{"ploadr", two(0xf000, 0x2208), two(0xffc0, 0xfff8), "D3&s" },
+{"ploadr", two(0xf000, 0x2200), two(0xffc0, 0xfffe), "f3&s" },
+{"ploadw", two(0xf000, 0x2010), two(0xffc0, 0xfff0), "T3&s" },
+{"ploadw", two(0xf000, 0x2008), two(0xffc0, 0xfff8), "D3&s" },
+{"ploadw", two(0xf000, 0x2000), two(0xffc0, 0xfffe), "f3&s" },
+
+ /* TC, CRP, DRP, SRP, CAL, VAL, SCC, AC */
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "*sP8" },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "P8%s" },
+{"pmove", two(0xf000, 0x4000), two(0xffc0, 0xe3ff), "|sW8" },
+{"pmove", two(0xf000, 0x4200), two(0xffc0, 0xe3ff), "W8~s" },
+
+ /* BADx, BACx */
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xe3e3), "*sX3" },
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xe3e3), "X3%s" },
+
+ /* PSR, PCSR */
+ /* {"pmove", two(0xf000, 0x6100), two(oxffc0, oxffff), "*sZ8" }, */
+{"pmove", two(0xf000, 0x6000), two(0xffc0, 0xffff), "*sY8" },
+{"pmove", two(0xf000, 0x6200), two(0xffc0, 0xffff), "Y8%s" },
+{"pmove", two(0xf000, 0x6600), two(0xffc0, 0xffff), "Z8%s" },
+
+{"prestore", one(0xf140), one(0xffc0), "&s"},
+{"prestore", one(0xf158), one(0xfff8), "+s"},
+{"psave", one(0xf100), one(0xffc0), "&s"},
+{"psave", one(0xf100), one(0xffc0), "+s"},
+
+{"psac", two(0xf040, 0x0007), two(0xffc0, 0xffff), "@s"},
+{"psas", two(0xf040, 0x0006), two(0xffc0, 0xffff), "@s"},
+{"psbc", two(0xf040, 0x0001), two(0xffc0, 0xffff), "@s"},
+{"psbs", two(0xf040, 0x0000), two(0xffc0, 0xffff), "@s"},
+{"pscc", two(0xf040, 0x000f), two(0xffc0, 0xffff), "@s"},
+{"pscs", two(0xf040, 0x000e), two(0xffc0, 0xffff), "@s"},
+{"psgc", two(0xf040, 0x000d), two(0xffc0, 0xffff), "@s"},
+{"psgs", two(0xf040, 0x000c), two(0xffc0, 0xffff), "@s"},
+{"psic", two(0xf040, 0x000b), two(0xffc0, 0xffff), "@s"},
+{"psis", two(0xf040, 0x000a), two(0xffc0, 0xffff), "@s"},
+{"pslc", two(0xf040, 0x0003), two(0xffc0, 0xffff), "@s"},
+{"psls", two(0xf040, 0x0002), two(0xffc0, 0xffff), "@s"},
+{"pssc", two(0xf040, 0x0005), two(0xffc0, 0xffff), "@s"},
+{"psss", two(0xf040, 0x0004), two(0xffc0, 0xffff), "@s"},
+{"pswc", two(0xf040, 0x0009), two(0xffc0, 0xffff), "@s"},
+{"psws", two(0xf040, 0x0008), two(0xffc0, 0xffff), "@s"},
+
+{"ptestr", two(0xf000, 0x8210), two(0xffc0, 0xe3f0), "T3&sQ8" },
+{"ptestr", two(0xf000, 0x8310), two(0xffc0, 0xe310), "T3&sQ8A9" },
+{"ptestr", two(0xf000, 0x8208), two(0xffc0, 0xe3f8), "D3&sQ8" },
+{"ptestr", two(0xf000, 0x8308), two(0xffc0, 0xe318), "D3&sQ8A9" },
+{"ptestr", two(0xf000, 0x8200), two(0xffc0, 0xe3fe), "f3&sQ8" },
+{"ptestr", two(0xf000, 0x8300), two(0xffc0, 0xe31e), "f3&sQ8A9" },
+
+{"ptestw", two(0xf000, 0x8010), two(0xffc0, 0xe3f0), "T3&sQ8" },
+{"ptestw", two(0xf000, 0x8110), two(0xffc0, 0xe310), "T3&sQ8A9" },
+{"ptestw", two(0xf000, 0x8008), two(0xffc0, 0xe3f8), "D3&sQ8" },
+{"ptestw", two(0xf000, 0x8108), two(0xffc0, 0xe318), "D3&sQ8A9" },
+{"ptestw", two(0xf000, 0x8000), two(0xffc0, 0xe3fe), "f3&sQ8" },
+{"ptestw", two(0xf000, 0x8100), two(0xffc0, 0xe31e), "f3&sQ8A9" },
+
+{"ptrapacw", two(0xf07a, 0x0007), two(0xffff, 0xffff), "#w"},
+{"ptrapacl", two(0xf07b, 0x0007), two(0xffff, 0xffff), "#l"},
+{"ptrapac", two(0xf07c, 0x0007), two(0xffff, 0xffff), ""},
+
+{"ptrapasw", two(0xf07a, 0x0006), two(0xffff, 0xffff), "#w"},
+{"ptrapasl", two(0xf07b, 0x0006), two(0xffff, 0xffff), "#l"},
+{"ptrapas", two(0xf07c, 0x0006), two(0xffff, 0xffff), ""},
+
+{"ptrapbcw", two(0xf07a, 0x0001), two(0xffff, 0xffff), "#w"},
+{"ptrapbcl", two(0xf07b, 0x0001), two(0xffff, 0xffff), "#l"},
+{"ptrapbc", two(0xf07c, 0x0001), two(0xffff, 0xffff), ""},
+
+{"ptrapbsw", two(0xf07a, 0x0000), two(0xffff, 0xffff), "#w"},
+{"ptrapbsl", two(0xf07b, 0x0000), two(0xffff, 0xffff), "#l"},
+{"ptrapbs", two(0xf07c, 0x0000), two(0xffff, 0xffff), ""},
+
+{"ptrapccw", two(0xf07a, 0x000f), two(0xffff, 0xffff), "#w"},
+{"ptrapccl", two(0xf07b, 0x000f), two(0xffff, 0xffff), "#l"},
+{"ptrapcc", two(0xf07c, 0x000f), two(0xffff, 0xffff), ""},
+
+{"ptrapcsw", two(0xf07a, 0x000e), two(0xffff, 0xffff), "#w"},
+{"ptrapcsl", two(0xf07b, 0x000e), two(0xffff, 0xffff), "#l"},
+{"ptrapcs", two(0xf07c, 0x000e), two(0xffff, 0xffff), ""},
+
+{"ptrapgcw", two(0xf07a, 0x000d), two(0xffff, 0xffff), "#w"},
+{"ptrapgcl", two(0xf07b, 0x000d), two(0xffff, 0xffff), "#l"},
+{"ptrapgc", two(0xf07c, 0x000d), two(0xffff, 0xffff), ""},
+
+{"ptrapgsw", two(0xf07a, 0x000c), two(0xffff, 0xffff), "#w"},
+{"ptrapgsl", two(0xf07b, 0x000c), two(0xffff, 0xffff), "#l"},
+{"ptrapgs", two(0xf07c, 0x000c), two(0xffff, 0xffff), ""},
+
+{"ptrapicw", two(0xf07a, 0x000b), two(0xffff, 0xffff), "#w"},
+{"ptrapicl", two(0xf07b, 0x000b), two(0xffff, 0xffff), "#l"},
+{"ptrapic", two(0xf07c, 0x000b), two(0xffff, 0xffff), ""},
+
+{"ptrapisw", two(0xf07a, 0x000a), two(0xffff, 0xffff), "#w"},
+{"ptrapisl", two(0xf07b, 0x000a), two(0xffff, 0xffff), "#l"},
+{"ptrapis", two(0xf07c, 0x000a), two(0xffff, 0xffff), ""},
+
+{"ptraplcw", two(0xf07a, 0x0003), two(0xffff, 0xffff), "#w"},
+{"ptraplcl", two(0xf07b, 0x0003), two(0xffff, 0xffff), "#l"},
+{"ptraplc", two(0xf07c, 0x0003), two(0xffff, 0xffff), ""},
+
+{"ptraplsw", two(0xf07a, 0x0002), two(0xffff, 0xffff), "#w"},
+{"ptraplsl", two(0xf07b, 0x0002), two(0xffff, 0xffff), "#l"},
+{"ptrapls", two(0xf07c, 0x0002), two(0xffff, 0xffff), ""},
+
+{"ptrapscw", two(0xf07a, 0x0005), two(0xffff, 0xffff), "#w"},
+{"ptrapscl", two(0xf07b, 0x0005), two(0xffff, 0xffff), "#l"},
+{"ptrapsc", two(0xf07c, 0x0005), two(0xffff, 0xffff), ""},
+
+{"ptrapssw", two(0xf07a, 0x0004), two(0xffff, 0xffff), "#w"},
+{"ptrapssl", two(0xf07b, 0x0004), two(0xffff, 0xffff), "#l"},
+{"ptrapss", two(0xf07c, 0x0004), two(0xffff, 0xffff), ""},
+
+{"ptrapwcw", two(0xf07a, 0x0009), two(0xffff, 0xffff), "#w"},
+{"ptrapwcl", two(0xf07b, 0x0009), two(0xffff, 0xffff), "#l"},
+{"ptrapwc", two(0xf07c, 0x0009), two(0xffff, 0xffff), ""},
+
+{"ptrapwsw", two(0xf07a, 0x0008), two(0xffff, 0xffff), "#w"},
+{"ptrapwsl", two(0xf07b, 0x0008), two(0xffff, 0xffff), "#l"},
+{"ptrapws", two(0xf07c, 0x0008), two(0xffff, 0xffff), ""},
+
+{"pvalid", two(0xf000, 0x2800), two(0xffc0, 0xffff), "Vs&s"},
+{"pvalid", two(0xf000, 0x2c00), two(0xffc0, 0xfff8), "A3&s" },
+
+#endif /* m68851 */
diff --git a/gas/config/tc-m68hc11.c b/gas/config/tc-m68hc11.c
new file mode 100644
index 0000000..3641912
--- /dev/null
+++ b/gas/config/tc-m68hc11.c
@@ -0,0 +1,4499 @@
+/* tc-m68hc11.c -- Assembler code for the Motorola 68HC11 & 68HC12.
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Written by Stephane Carrez (stcarrez@nerim.fr)
+ XGATE and S12X added by James Murray (jsm@jsm-net.demon.co.uk)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/m68hc11.h"
+#include "dwarf2dbg.h"
+#include "elf/m68hc11.h"
+
+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 STATE_CONDITIONAL_BRANCH (1)
+#define STATE_PC_RELATIVE (2)
+#define STATE_INDEXED_OFFSET (3)
+#define STATE_INDEXED_PCREL (4)
+#define STATE_XBCC_BRANCH (5)
+#define STATE_CONDITIONAL_BRANCH_6812 (6)
+
+#define STATE_BYTE (0)
+#define STATE_BITS5 (0)
+#define STATE_WORD (1)
+#define STATE_BITS9 (1)
+#define STATE_LONG (2)
+#define STATE_BITS16 (2)
+#define STATE_UNDF (3) /* Symbol undefined in pass1 */
+
+/* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+#define RELAX_STATE(s) ((s) >> 2)
+#define RELAX_LENGTH(s) ((s) & 3)
+
+#define IS_OPCODE(C1,C2) (((C1) & 0x0FF) == ((C2) & 0x0FF))
+
+/* This table describes how you change sizes for the various types of variable
+ size expressions. This version only supports two kinds. */
+
+/* The fields are:
+ How far Forward this mode will reach.
+ How far Backward this mode will reach.
+ How many bytes this mode will add to the size of the frag.
+ Which mode to go to if the offset won't fit in this one. */
+
+relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0}, /* First entries aren't used. */
+ {1, 1, 0, 0}, /* For no good reason except. */
+ {1, 1, 0, 0}, /* that the VAX doesn't either. */
+ {1, 1, 0, 0},
+
+ /* Relax for bcc <L>.
+ These insns are translated into b!cc +3 jmp L. */
+ {(127), (-128), 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD)},
+ {0, 0, 3, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ /* Relax for bsr <L> and bra <L>.
+ These insns are translated into jsr and jmp. */
+ {(127), (-128), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)},
+ {0, 0, 1, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ /* Relax for indexed offset: 5-bits, 9-bits, 16-bits. */
+ {(15), (-16), 0, ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9)},
+ {(255), (-256), 1, ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16)},
+ {0, 0, 2, 0},
+ {1, 1, 0, 0},
+
+ /* Relax for PC relative offset: 5-bits, 9-bits, 16-bits.
+ For the 9-bit case, there will be a -1 correction to take into
+ account the new byte that's why the range is -255..256. */
+ {(15), (-16), 0, ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS9)},
+ {(256), (-255), 1, ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS16)},
+ {0, 0, 2, 0},
+ {1, 1, 0, 0},
+
+ /* Relax for dbeq/ibeq/tbeq r,<L>:
+ These insns are translated into db!cc +3 jmp L. */
+ {(255), (-256), 0, ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_WORD)},
+ {0, 0, 3, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ /* Relax for bcc <L> on 68HC12.
+ These insns are translated into lbcc <L>. */
+ {(127), (-128), 0, ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_WORD)},
+ {0, 0, 2, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+};
+
+/* 68HC11 and 68HC12 registers. They are numbered according to the 68HC12. */
+typedef enum register_id
+{
+ REG_NONE = -1,
+ REG_A = 0,
+ REG_B = 1,
+ REG_CCR = 2,
+ REG_D = 4,
+ REG_X = 5,
+ REG_Y = 6,
+ REG_SP = 7,
+ REG_PC = 8,
+ REG_R0 = 0,
+ REG_R1 = 1,
+ REG_R2 = 2,
+ REG_R3 = 3,
+ REG_R4 = 4,
+ REG_R5 = 5,
+ REG_R6 = 6,
+ REG_R7 = 7,
+ REG_SP_XG = 8,
+ REG_PC_XG = 9,
+ REG_CCR_XG = 10
+} register_id;
+
+typedef struct operand
+{
+ expressionS exp;
+ register_id reg1;
+ register_id reg2;
+ int mode;
+} operand;
+
+struct m68hc11_opcode_def
+{
+ long format;
+ int min_operands;
+ int max_operands;
+ int nb_modes;
+ int used;
+ struct m68hc11_opcode *opcode;
+};
+
+static struct m68hc11_opcode_def *m68hc11_opcode_defs = 0;
+static int m68hc11_nb_opcode_defs = 0;
+
+typedef struct alias
+{
+ const char *name;
+ const char *alias;
+} alias;
+
+static alias alias_opcodes[] =
+{
+ {"cpd", "cmpd"},
+ {"cpx", "cmpx"},
+ {"cpy", "cmpy"},
+ {0, 0}
+};
+
+struct m9s12xg_opcode_def
+{
+ long format;
+ int min_operands;
+ int max_operands;
+ int nb_modes;
+ int used;
+ struct m9s12xg_opcode *opcode;
+};
+
+/* Local functions. */
+static register_id reg_name_search (char *);
+static register_id register_name (void);
+static int cmp_opcode (struct m68hc11_opcode *, struct m68hc11_opcode *);
+static char *print_opcode_format (struct m68hc11_opcode *, int);
+static char *skip_whites (char *);
+static int check_range (long, int);
+static void print_opcode_list (void);
+static void get_default_target (void);
+static void print_insn_format (char *);
+static int get_operand (operand *, int, long);
+static void fixup8 (expressionS *, int, int);
+static void fixup16 (expressionS *, int, int);
+static void fixup24 (expressionS *, int, int);
+static void fixup8_xg (expressionS *, int, int);
+static unsigned char convert_branch (unsigned char);
+static char *m68hc11_new_insn (int);
+static void build_dbranch_insn (struct m68hc11_opcode *,
+ operand *, int, int);
+static int build_indexed_byte (operand *, int, int);
+static int build_reg_mode (operand *, int);
+
+static struct m68hc11_opcode *find (struct m68hc11_opcode_def *,
+ operand *, int);
+static struct m68hc11_opcode *find_opcode (struct m68hc11_opcode_def *,
+ operand *, int *);
+static void build_jump_insn (struct m68hc11_opcode *, operand *, int, int);
+static void build_insn_xg (struct m68hc11_opcode *, operand *, int);
+static void build_insn (struct m68hc11_opcode *, operand *, int);
+static int relaxable_symbol (symbolS *);
+
+/* Pseudo op to indicate a relax group. */
+static void s_m68hc11_relax (int);
+
+/* Pseudo op to control the ELF flags. */
+static void s_m68hc11_mode (int);
+
+/* Process directives specified via pseudo ops. */
+static void s_m68hc11_parse_pseudo_instruction (int);
+
+/* Mark the symbols with STO_M68HC12_FAR to indicate the functions
+ are using 'rtc' for returning. It is necessary to use 'call'
+ to invoke them. This is also used by the debugger to correctly
+ find the stack frame. */
+static void s_m68hc11_mark_symbol (int);
+
+/* Controls whether relative branches can be turned into long branches.
+ When the relative offset is too large, the insn are changed:
+ bra -> jmp
+ bsr -> jsr
+ bcc -> b!cc +3
+ jmp L
+ dbcc -> db!cc +3
+ jmp L
+
+ Setting the flag forbidds this. */
+static short flag_fixed_branches = 0;
+
+/* Force to use long jumps (absolute) instead of relative branches. */
+static short flag_force_long_jumps = 0;
+
+/* Change the direct addressing mode into an absolute addressing mode
+ when the insn does not support direct addressing.
+ For example, "clr *ZD0" is normally not possible and is changed
+ into "clr ZDO". */
+static short flag_strict_direct_addressing = 1;
+
+/* When an opcode has invalid operand, print out the syntax of the opcode
+ to stderr. */
+static short flag_print_insn_syntax = 0;
+
+/* Dumps the list of instructions with syntax and then exit:
+ 1 -> Only dumps the list (sorted by name)
+ 2 -> Generate an example (or test) that can be compiled. */
+static short flag_print_opcodes = 0;
+
+/* Opcode hash table. */
+static struct hash_control *m68hc11_hash;
+
+/* Current cpu (either cpu6811 or cpu6812). This is determined automagically
+ by 'get_default_target' by looking at default BFD vector. This is overridden
+ with the -m<cpu> option. */
+static int current_architecture = 0;
+
+/* Default cpu determined by 'get_default_target'. */
+static const char *default_cpu;
+
+/* Number of opcodes in the sorted table (filtered by current cpu). */
+static int num_opcodes;
+
+/* The opcodes sorted by name and filtered by current cpu. */
+static struct m68hc11_opcode *m68hc11_sorted_opcodes;
+
+/* ELF flags to set in the output file header. */
+static int elf_flags = E_M68HC11_F64;
+
+/* These are the machine dependent pseudo-ops. These are included so
+ the assembler can work on the output from the SUN C compiler, which
+ generates these. */
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* The following pseudo-ops are supported for MRI compatibility. */
+ {"fcb", cons, 1},
+ {"fdb", cons, 2},
+ {"fqb", cons, 4},
+ {"fcc", stringer, 8 + 1},
+ {"rmb", s_space, 0},
+
+ /* Motorola ALIS. */
+ {"xrefb", s_ignore, 0}, /* Same as xref */
+
+ /* Gcc driven relaxation. */
+ {"relax", s_m68hc11_relax, 0},
+
+ /* .mode instruction (ala SH). */
+ {"mode", s_m68hc11_mode, 0},
+
+ /* .far instruction. */
+ {"far", s_m68hc11_mark_symbol, STO_M68HC12_FAR},
+
+ /* .interrupt instruction. */
+ {"interrupt", s_m68hc11_mark_symbol, STO_M68HC12_INTERRUPT},
+
+ /* .nobankwarning instruction. */
+ {"nobankwarning", s_m68hc11_parse_pseudo_instruction, E_M68HC11_NO_BANK_WARNING},
+
+ {0, 0, 0}
+};
+
+/* Options and initialization. */
+
+const char *md_shortopts = "Sm:";
+
+struct option md_longopts[] =
+{
+#define OPTION_FORCE_LONG_BRANCH (OPTION_MD_BASE)
+ {"force-long-branches", no_argument, NULL, OPTION_FORCE_LONG_BRANCH},
+ {"force-long-branchs", no_argument, NULL, OPTION_FORCE_LONG_BRANCH}, /* Misspelt version kept for backwards compatibility. */
+
+#define OPTION_SHORT_BRANCHES (OPTION_MD_BASE + 1)
+ {"short-branches", no_argument, NULL, OPTION_SHORT_BRANCHES},
+ {"short-branchs", no_argument, NULL, OPTION_SHORT_BRANCHES}, /* Misspelt version kept for backwards compatibility. */
+
+#define OPTION_STRICT_DIRECT_MODE (OPTION_MD_BASE + 2)
+ {"strict-direct-mode", no_argument, NULL, OPTION_STRICT_DIRECT_MODE},
+
+#define OPTION_PRINT_INSN_SYNTAX (OPTION_MD_BASE + 3)
+ {"print-insn-syntax", no_argument, NULL, OPTION_PRINT_INSN_SYNTAX},
+
+#define OPTION_PRINT_OPCODES (OPTION_MD_BASE + 4)
+ {"print-opcodes", no_argument, NULL, OPTION_PRINT_OPCODES},
+
+#define OPTION_GENERATE_EXAMPLE (OPTION_MD_BASE + 5)
+ {"generate-example", no_argument, NULL, OPTION_GENERATE_EXAMPLE},
+
+#define OPTION_MSHORT (OPTION_MD_BASE + 6)
+ {"mshort", no_argument, NULL, OPTION_MSHORT},
+
+#define OPTION_MLONG (OPTION_MD_BASE + 7)
+ {"mlong", no_argument, NULL, OPTION_MLONG},
+
+#define OPTION_MSHORT_DOUBLE (OPTION_MD_BASE + 8)
+ {"mshort-double", no_argument, NULL, OPTION_MSHORT_DOUBLE},
+
+#define OPTION_MLONG_DOUBLE (OPTION_MD_BASE + 9)
+ {"mlong-double", no_argument, NULL, OPTION_MLONG_DOUBLE},
+
+#define OPTION_XGATE_RAMOFFSET (OPTION_MD_BASE + 10)
+ {"xgate-ramoffset", no_argument, NULL, OPTION_XGATE_RAMOFFSET},
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Get the target cpu for the assembler. This is based on the configure
+ options and on the -m68hc11/-m68hc12 option. If no option is specified,
+ we must get the default. */
+const char *
+m68hc11_arch_format (void)
+{
+ get_default_target ();
+ if (current_architecture & cpu6811)
+ return "elf32-m68hc11";
+ else
+ return "elf32-m68hc12";
+}
+
+enum bfd_architecture
+m68hc11_arch (void)
+{
+ get_default_target ();
+ if (current_architecture & cpu6811)
+ return bfd_arch_m68hc11;
+ else
+ return bfd_arch_m68hc12;
+}
+
+int
+m68hc11_mach (void)
+{
+ return 0;
+}
+
+/* Listing header selected according to cpu. */
+const char *
+m68hc11_listing_header (void)
+{
+ if (current_architecture & cpu6811)
+ return "M68HC11 GAS ";
+ else if (current_architecture & cpuxgate)
+ return "XGATE GAS ";
+ else if (current_architecture & cpu9s12x)
+ return "S12X GAS ";
+ else
+ return "M68HC12 GAS ";
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ get_default_target ();
+ fprintf (stream, _("\
+Motorola 68HC11/68HC12/68HCS12 options:\n\
+ -m68hc11 | -m68hc12 |\n\
+ -m68hcs12 | -mm9s12x |\n\
+ -mm9s12xg specify the processor [default %s]\n\
+ -mshort use 16-bit int ABI (default)\n\
+ -mlong use 32-bit int ABI\n\
+ -mshort-double use 32-bit double ABI\n\
+ -mlong-double use 64-bit double ABI (default)\n\
+ --force-long-branches always turn relative branches into absolute ones\n\
+ -S,--short-branches do not turn relative branches into absolute ones\n\
+ when the offset is out of range\n\
+ --strict-direct-mode do not turn the direct mode into extended mode\n\
+ when the instruction does not support direct mode\n\
+ --print-insn-syntax print the syntax of instruction in case of error\n\
+ --print-opcodes print the list of instructions with syntax\n\
+ --xgate-ramoffset offset ram addresses by 0xc000\n\
+ --generate-example generate an example of each instruction\n\
+ (used for testing)\n"), default_cpu);
+
+}
+
+/* Try to identify the default target based on the BFD library. */
+static void
+get_default_target (void)
+{
+ const bfd_target *target;
+ bfd abfd;
+
+ if (current_architecture != 0)
+ return;
+
+ default_cpu = "unknown";
+ target = bfd_find_target (0, &abfd);
+ if (target && target->name)
+ {
+ if (strcmp (target->name, "elf32-m68hc12") == 0)
+ {
+ current_architecture = cpu6812;
+ default_cpu = "m68hc12";
+ }
+ else if (strcmp (target->name, "elf32-m68hc11") == 0)
+ {
+ current_architecture = cpu6811;
+ default_cpu = "m68hc11";
+ }
+ else
+ {
+ as_bad (_("Default target `%s' is not supported."), target->name);
+ }
+ }
+}
+
+void
+m68hc11_print_statistics (FILE *file)
+{
+ int i;
+ struct m68hc11_opcode_def *opc;
+
+ hash_print_statistics (file, "opcode table", m68hc11_hash);
+
+ opc = m68hc11_opcode_defs;
+ if (opc == 0 || m68hc11_nb_opcode_defs == 0)
+ return;
+
+ /* Dump the opcode statistics table. */
+ fprintf (file, _("Name # Modes Min ops Max ops Modes mask # Used\n"));
+ for (i = 0; i < m68hc11_nb_opcode_defs; i++, opc++)
+ {
+ fprintf (file, "%-7.7s %5d %7d %7d 0x%08lx %7d\n",
+ opc->opcode->name,
+ opc->nb_modes,
+ opc->min_operands, opc->max_operands, opc->format, opc->used);
+ }
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ get_default_target ();
+ switch (c)
+ {
+ /* -S means keep external to 2 bit offset rather than 16 bit one. */
+ case OPTION_SHORT_BRANCHES:
+ case 'S':
+ flag_fixed_branches = 1;
+ break;
+
+ case OPTION_FORCE_LONG_BRANCH:
+ flag_force_long_jumps = 1;
+ break;
+
+ case OPTION_PRINT_INSN_SYNTAX:
+ flag_print_insn_syntax = 1;
+ break;
+
+ case OPTION_PRINT_OPCODES:
+ flag_print_opcodes = 1;
+ break;
+
+ case OPTION_STRICT_DIRECT_MODE:
+ flag_strict_direct_addressing = 0;
+ break;
+
+ case OPTION_GENERATE_EXAMPLE:
+ flag_print_opcodes = 2;
+ break;
+
+ case OPTION_MSHORT:
+ elf_flags &= ~E_M68HC11_I32;
+ break;
+
+ case OPTION_MLONG:
+ elf_flags |= E_M68HC11_I32;
+ break;
+
+ case OPTION_MSHORT_DOUBLE:
+ elf_flags &= ~E_M68HC11_F64;
+ break;
+
+ case OPTION_MLONG_DOUBLE:
+ elf_flags |= E_M68HC11_F64;
+ break;
+
+ case OPTION_XGATE_RAMOFFSET:
+ elf_flags |= E_M68HC11_XGATE_RAMOFFSET;
+ break;
+
+ case 'm':
+ if ((strcasecmp (arg, "68hc11") == 0)
+ || (strcasecmp (arg, "m68hc11") == 0))
+ current_architecture = cpu6811;
+ else if ((strcasecmp (arg, "68hc12") == 0)
+ || (strcasecmp (arg, "m68hc12") == 0))
+ current_architecture = cpu6812;
+ else if ((strcasecmp (arg, "68hcs12") == 0)
+ || (strcasecmp (arg, "m68hcs12") == 0))
+ current_architecture = cpu6812 | cpu6812s;
+ else if (strcasecmp (arg, "m9s12x") == 0)
+ current_architecture = cpu6812 | cpu6812s | cpu9s12x;
+ else if ((strcasecmp (arg, "m9s12xg") == 0)
+ || (strcasecmp (arg, "xgate") == 0))
+ /* xgate for backwards compatability */
+ current_architecture = cpuxgate;
+ else
+ as_bad (_("Option `%s' is not recognized."), arg);
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+static int
+cmp_opcode (struct m68hc11_opcode *op1, struct m68hc11_opcode *op2)
+{
+ return strcmp (op1->name, op2->name);
+}
+
+#define IS_CALL_SYMBOL(MODE) \
+(((MODE) & (M6812_OP_PAGE|M6811_OP_IND16)) \
+ == ((M6812_OP_PAGE|M6811_OP_IND16)))
+
+/* Initialize the assembler. Create the opcode hash table
+ (sorted on the names) with the M6811 opcode table
+ (from opcode library). */
+void
+md_begin (void)
+{
+ char *prev_name = "";
+ struct m68hc11_opcode *opcodes;
+ struct m68hc11_opcode_def *opc = 0;
+ int i, j;
+
+ get_default_target ();
+
+ m68hc11_hash = hash_new ();
+
+ /* Get a writable copy of the opcode table and sort it on the names. */
+ opcodes = (struct m68hc11_opcode *) xmalloc (m68hc11_num_opcodes *
+ sizeof (struct
+ m68hc11_opcode));
+ m68hc11_sorted_opcodes = opcodes;
+ num_opcodes = 0;
+ for (i = 0; i < m68hc11_num_opcodes; i++)
+ {
+ if (m68hc11_opcodes[i].arch & current_architecture)
+ {
+ opcodes[num_opcodes] = m68hc11_opcodes[i];
+ if (opcodes[num_opcodes].name[0] == 'b'
+ && opcodes[num_opcodes].format & M6811_OP_JUMP_REL
+ && !(opcodes[num_opcodes].format & M6811_OP_BITMASK))
+ {
+ num_opcodes++;
+ opcodes[num_opcodes] = m68hc11_opcodes[i];
+ }
+ num_opcodes++;
+ for (j = 0; alias_opcodes[j].name != 0; j++)
+ if (strcmp (m68hc11_opcodes[i].name, alias_opcodes[j].name) == 0)
+ {
+ opcodes[num_opcodes] = m68hc11_opcodes[i];
+ opcodes[num_opcodes].name = alias_opcodes[j].alias;
+ num_opcodes++;
+ break;
+ }
+ }
+ }
+ qsort (opcodes, num_opcodes, sizeof (struct m68hc11_opcode),
+ (int (*) (const void*, const void*)) cmp_opcode);
+
+ opc = (struct m68hc11_opcode_def *)
+ xmalloc (num_opcodes * sizeof (struct m68hc11_opcode_def));
+ m68hc11_opcode_defs = opc--;
+
+ /* Insert unique names into hash table. The M6811 instruction set
+ has several identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+ for (i = 0; i < num_opcodes; i++, opcodes++)
+ {
+ int expect;
+
+ if (strcmp (prev_name, opcodes->name))
+ {
+ prev_name = (char *) opcodes->name;
+
+ opc++;
+ opc->format = 0;
+ opc->min_operands = 100;
+ opc->max_operands = 0;
+ opc->nb_modes = 0;
+ opc->opcode = opcodes;
+ opc->used = 0;
+ hash_insert (m68hc11_hash, opcodes->name, opc);
+ }
+ opc->nb_modes++;
+ opc->format |= opcodes->format;
+
+ /* See how many operands this opcode needs. */
+ expect = 0;
+ if (opcodes->arch == cpuxgate)
+ {
+ if (opcodes->format & (M68XG_OP_IMM3 | M68XG_OP_R | M68XG_OP_REL9
+ | M68XG_OP_REL10 ))
+ expect = 1;
+ else if (opcodes->format & (M68XG_OP_R_R | M68XG_OP_R_IMM4
+ | M68XG_OP_R_IMM8 | M68XG_OP_R_IMM8))
+ expect = 2;
+ else if (opcodes->format & (M68XG_OP_R_R_R | M68XG_OP_R_R_OFFS5
+ | M68XG_OP_RD_RB_RI | M68XG_OP_RD_RB_RIp
+ | M68XG_OP_RD_RB_mRI))
+ expect = 3;
+ }
+ else
+ {
+ if (opcodes->format & M6811_OP_MASK)
+ expect++;
+ if (opcodes->format & M6811_OP_BITMASK)
+ expect++;
+ if (opcodes->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+ expect++;
+ if (opcodes->format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))
+ expect++;
+ /* Special case for call instruction. */
+ if ((opcodes->format & M6812_OP_PAGE)
+ && !(opcodes->format & M6811_OP_IND16))
+ expect++;
+ }
+
+ if (expect < opc->min_operands)
+ opc->min_operands = expect;
+ if (IS_CALL_SYMBOL (opcodes->format))
+ expect++;
+ if (expect > opc->max_operands)
+ opc->max_operands = expect;
+ }
+ opc++;
+ m68hc11_nb_opcode_defs = opc - m68hc11_opcode_defs;
+
+ if (flag_print_opcodes)
+ {
+ print_opcode_list ();
+ exit (EXIT_SUCCESS);
+ }
+}
+
+void
+m68hc11_init_after_args (void)
+{
+}
+
+/* Builtin help. */
+
+/* Return a string that represents the operand format for the instruction.
+ When example is true, this generates an example of operand. This is used
+ to give an example and also to generate a test. */
+
+static char *
+print_opcode_format (struct m68hc11_opcode *opcode, int example)
+{
+ static char buf[128];
+ int format = opcode->format;
+ char *p;
+
+ p = buf;
+ buf[0] = 0;
+
+ if (current_architecture == cpuxgate)
+ {
+ if (format & M68XG_OP_IMM3)
+ {
+ if (example)
+ sprintf (p, "#%d", rand () & 0x007);
+ else
+ strcpy (p, _("imm3"));
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R)
+ {
+ if (example)
+ sprintf (p, "R%d", rand () & 0x07);
+ else
+ strcpy (p, _("RD"));
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R_R)
+ {
+ if (example)
+ sprintf (p, "R%d,R%d", rand () & 0x07, rand () & 0x07);
+ else
+ strcpy (p, _("RD,RS"));
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R_IMM4)
+ {
+ if (example)
+ sprintf (p, "R%d,#%d", rand () & 0x07, rand () & 0x0f);
+ else
+ strcpy (p, _("RI, #imm4"));
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R_R_R)
+ {
+ if (example)
+ sprintf (p, "R%d,R%d,R%d", rand () & 0x07, rand () & 0x07, rand () & 0x07);
+ else
+ strcpy (p, "RD,RS1,RS2");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_REL9)
+ {
+ if (example)
+ sprintf (p, "%d", rand () & 0x1FF);
+ else
+ strcpy (p, "<rel9>");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_REL10)
+ {
+ if (example)
+ sprintf (p, "%d", rand () & 0x3FF);
+ else
+ strcpy (p, "<rel10>");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R_R_OFFS5)
+ {
+ if (example)
+ sprintf (p, "R%d, (R%d, #0x%x)", rand () & 0x07, rand () & 0x07, rand () & 0x1f);
+ else
+ strcpy (p, _("RD, (RI,#offs5)"));
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_RD_RB_RI)
+ {
+ if (example)
+ sprintf (p, "R%d, (R%d, R%d)", rand () & 0x07, rand () & 0x07, rand () & 0x07);
+ else
+ strcpy (p, "RD, (RB, RI)");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_RD_RB_RIp)
+ {
+ if (example)
+ sprintf (p, "R%d, (R%d, R%d+)", rand () & 0x07, rand () & 0x07, rand () & 0x07);
+ else
+ strcpy (p, "RD, (RB, RI+)");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_RD_RB_mRI)
+ {
+ if (example)
+ sprintf (p, "R%d, (R%d, -R%d)", rand () & 0x07, rand () & 0x07, rand () & 0x07);
+ else
+ strcpy (p, "RD, (RB, -RI)");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R_IMM8)
+ {
+ if (example)
+ sprintf (p, "R%d, #0x%x", rand () & 0x07, rand () & 0xff);
+ else
+ strcpy (p, "RD, #imm8");
+ p = &p[strlen (p)];
+ }
+ else if (format & M68XG_OP_R_IMM16)
+ {
+ if (example)
+ sprintf (p, "R%d, #0x%x", rand () & 0x07, rand () & 0xffff);
+ else
+ strcpy (p, "RD, #imm16");
+ p = &p[strlen (p)];
+ }
+ }
+ else
+ {
+
+ if (format & M6811_OP_IMM8)
+ {
+ if (example)
+ sprintf (p, "#%d", rand () & 0x0FF);
+ else
+ strcpy (p, _("#<imm8>"));
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6811_OP_IMM16)
+ {
+ if (example)
+ sprintf (p, "#%d", rand () & 0x0FFFF);
+ else
+ strcpy (p, _("#<imm16>"));
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6811_OP_IX)
+ {
+ if (example)
+ sprintf (p, "%d,X", rand () & 0x0FF);
+ else
+ strcpy (p, _("<imm8>,X"));
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6811_OP_IY)
+ {
+ if (example)
+ sprintf (p, "%d,X", rand () & 0x0FF);
+ else
+ strcpy (p, _("<imm8>,X"));
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6812_OP_IDX)
+ {
+ if (example)
+ sprintf (p, "%d,X", rand () & 0x0FF);
+ else
+ strcpy (p, "n,r");
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6812_OP_PAGE)
+ {
+ if (example)
+ sprintf (p, ", %d", rand () & 0x0FF);
+ else
+ strcpy (p, ", <page>");
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6811_OP_DIRECT)
+ {
+ if (example)
+ sprintf (p, "*Z%d", rand () & 0x0FF);
+ else
+ strcpy (p, _("*<abs8>"));
+ p = &p[strlen (p)];
+ }
+
+ if (format & M6811_OP_BITMASK)
+ {
+ if (buf[0])
+ *p++ = ' ';
+
+ if (example)
+ sprintf (p, "#$%02x", rand () & 0x0FF);
+ else
+ strcpy (p, _("#<mask>"));
+
+ p = &p[strlen (p)];
+ if (format & M6811_OP_JUMP_REL)
+ *p++ = ' ';
+ }
+
+ if (format & M6811_OP_IND16)
+ {
+ if (example)
+ sprintf (p, _("symbol%d"), rand () & 0x0FF);
+ else
+ strcpy (p, _("<abs>"));
+
+ p = &p[strlen (p)];
+ }
+
+ if (format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+ {
+ if (example)
+ {
+ if (format & M6811_OP_BITMASK)
+ {
+ sprintf (p, ".+%d", rand () & 0x7F);
+ }
+ else
+ {
+ sprintf (p, "L%d", rand () & 0x0FF);
+ }
+ }
+ else
+ strcpy (p, _("<label>"));
+ }
+ }
+ return buf;
+}
+
+/* Prints the list of instructions with the possible operands. */
+static void
+print_opcode_list (void)
+{
+ int i;
+ char *prev_name = "";
+ struct m68hc11_opcode *opcodes;
+ int example = flag_print_opcodes == 2;
+
+ if (example)
+ printf (_("# Example of `%s' instructions\n\t.sect .text\n_start:\n"),
+ default_cpu);
+
+ opcodes = m68hc11_sorted_opcodes;
+
+ /* Walk the list sorted on names (by md_begin). We only report
+ one instruction per line, and we collect the different operand
+ formats. */
+ for (i = 0; i < num_opcodes; i++, opcodes++)
+ {
+ char *fmt = print_opcode_format (opcodes, example);
+
+ if (example)
+ {
+ printf ("L%d:\t", i);
+ printf ("%s %s\n", opcodes->name, fmt);
+ }
+ else
+ {
+ if (strcmp (prev_name, opcodes->name))
+ {
+ if (i > 0)
+ printf ("\n");
+
+ printf ("%-5.5s ", opcodes->name);
+ prev_name = (char *) opcodes->name;
+ }
+ if (fmt[0])
+ printf (" [%s]", fmt);
+ }
+ }
+ printf ("\n");
+}
+
+/* Print the instruction format. This operation is called when some
+ instruction is not correct. Instruction format is printed as an
+ error message. */
+static void
+print_insn_format (char *name)
+{
+ struct m68hc11_opcode_def *opc;
+ struct m68hc11_opcode *opcode;
+ char buf[128];
+
+ opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name);
+ if (opc == NULL)
+ {
+ as_bad (_("Instruction `%s' is not recognized."), name);
+ return;
+ }
+ opcode = opc->opcode;
+
+ as_bad (_("Instruction formats for `%s':"), name);
+ do
+ {
+ char *fmt;
+
+ fmt = print_opcode_format (opcode, 0);
+ sprintf (buf, "\t%-5.5s %s", opcode->name, fmt);
+
+ as_bad ("%s", buf);
+ opcode++;
+ }
+ while (strcmp (opcode->name, name) == 0);
+}
+
+/* Analysis of 68HC11 and 68HC12 operands. */
+
+/* reg_name_search() finds the register number given its name.
+ Returns the register number or REG_NONE on failure. */
+static register_id
+reg_name_search (char *name)
+{
+ if (strcasecmp (name, "x") == 0 || strcasecmp (name, "ix") == 0)
+ return REG_X;
+ if (strcasecmp (name, "y") == 0 || strcasecmp (name, "iy") == 0)
+ return REG_Y;
+ if (strcasecmp (name, "a") == 0)
+ return REG_A;
+ if (strcasecmp (name, "b") == 0)
+ return REG_B;
+ if (strcasecmp (name, "d") == 0)
+ return REG_D;
+ if (strcasecmp (name, "sp") == 0)
+ return REG_SP;
+ if (strcasecmp (name, "pc") == 0)
+ return REG_PC;
+ if (strcasecmp (name, "ccr") == 0)
+ return REG_CCR;
+/* XGATE */
+ if (strcasecmp (name, "r0") == 0)
+ return REG_R0;
+ if (strcasecmp (name, "r1") == 0)
+ return REG_R1;
+ if (strcasecmp (name, "r2") == 0)
+ return REG_R2;
+ if (strcasecmp (name, "r3") == 0)
+ return REG_R3;
+ if (strcasecmp (name, "r4") == 0)
+ return REG_R4;
+ if (strcasecmp (name, "r5") == 0)
+ return REG_R5;
+ if (strcasecmp (name, "r6") == 0)
+ return REG_R6;
+ if (strcasecmp (name, "r7") == 0)
+ return REG_R7;
+ if (strcasecmp (name, "sp") == 0)
+ return REG_SP_XG;
+ if (strcasecmp (name, "pc") == 0)
+ return REG_PC_XG;
+ if (strcasecmp (name, "ccr") == 0)
+ return REG_CCR_XG;
+ return REG_NONE;
+}
+
+static char *
+skip_whites (char *p)
+{
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ return p;
+}
+
+/* Check the string at input_line_pointer
+ to see if it is a valid register name. */
+static register_id
+register_name (void)
+{
+ register_id reg_number;
+ char c, *p = input_line_pointer;
+
+ if (!is_name_beginner (*p++))
+ return REG_NONE;
+
+ while (is_part_of_name (*p++))
+ continue;
+
+ c = *--p;
+ if (c)
+ *p++ = 0;
+
+ /* Look to see if it's in the register table. */
+ reg_number = reg_name_search (input_line_pointer);
+ if (reg_number != REG_NONE)
+ {
+ if (c)
+ *--p = c;
+
+ input_line_pointer = p;
+ return reg_number;
+ }
+ if (c)
+ *--p = c;
+
+ return reg_number;
+}
+#define M6811_OP_CALL_ADDR 0x00800000
+#define M6811_OP_PAGE_ADDR 0x04000000
+
+/* Parse a string of operands and return an array of expressions.
+
+ Operand mode[0] mode[1] exp[0] exp[1]
+ #n M6811_OP_IMM16 - O_*
+ *<exp> M6811_OP_DIRECT - O_*
+ .{+-}<exp> M6811_OP_JUMP_REL - O_*
+ <exp> M6811_OP_IND16 - O_*
+ ,r N,r M6812_OP_IDX M6812_OP_REG O_constant O_register
+ n,-r M6812_PRE_DEC M6812_OP_REG O_constant O_register
+ n,+r M6812_PRE_INC " "
+ n,r- M6812_POST_DEC " "
+ n,r+ M6812_POST_INC " "
+ A,r B,r D,r M6811_OP_REG M6812_OP_REG O_register O_register
+ [D,r] M6811_OP_D_IDX M6812_OP_REG O_register O_register
+ [n,r] M6811_OP_D_IDX_2 M6812_OP_REG O_constant O_register */
+static int
+get_operand (operand *oper, int which, long opmode)
+{
+ char *p = input_line_pointer;
+ int mode;
+ register_id reg;
+
+ oper->exp.X_op = O_absent;
+ oper->reg1 = REG_NONE;
+ oper->reg2 = REG_NONE;
+ mode = M6811_OP_NONE;
+
+ p = skip_whites (p);
+
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ {
+ input_line_pointer = p;
+ return 0;
+ }
+
+ if (*p == '*' && (opmode & (M6811_OP_DIRECT | M6811_OP_IND16)))
+ {
+ mode = M6811_OP_DIRECT;
+ p++;
+ }
+ else if (*p == '#')
+ {
+ if (!(opmode & (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_BITMASK)))
+ {
+ as_bad (_("Immediate operand is not allowed for operand %d."),
+ which);
+ return -1;
+ }
+
+ mode = M6811_OP_IMM16;
+ p++;
+ if (strncmp (p, "%hi", 3) == 0)
+ {
+ p += 3;
+ mode |= M6811_OP_HIGH_ADDR;
+ }
+ else if (strncmp (p, "%lo", 3) == 0)
+ {
+ p += 3;
+ mode |= M6811_OP_LOW_ADDR;
+ }
+ /* %page modifier is used to obtain only the page number
+ of the address of a function. */
+ else if (strncmp (p, "%page", 5) == 0)
+ {
+ p += 5;
+ mode |= M6811_OP_PAGE_ADDR;
+ }
+
+ /* %addr modifier is used to obtain the physical address part
+ of the function (16-bit). For 68HC12 the function will be
+ mapped in the 16K window at 0x8000 and the value will be
+ within that window (although the function address may not fit
+ in 16-bit). See bfd/elf32-m68hc12.c for the translation. */
+ else if (strncmp (p, "%addr", 5) == 0)
+ {
+ p += 5;
+ mode |= M6811_OP_CALL_ADDR;
+ }
+ }
+ else if (*p == '.' && (p[1] == '+' || p[1] == '-'))
+ {
+ p++;
+ mode = M6811_OP_JUMP_REL;
+ }
+ else if (*p == '[')
+ {
+ if (current_architecture & cpu6811)
+ as_bad (_("Indirect indexed addressing is not valid for 68HC11."));
+
+ p++;
+ mode = M6812_OP_D_IDX;
+ p = skip_whites (p);
+ }
+ else if (*p == ',') /* Special handling of ,x and ,y. */
+ {
+ p++;
+ input_line_pointer = p;
+
+ reg = register_name ();
+ if (reg != REG_NONE)
+ {
+ oper->reg1 = reg;
+ oper->exp.X_op = O_constant;
+ oper->exp.X_add_number = 0;
+ oper->mode = M6812_OP_IDX;
+ return 1;
+ }
+ as_bad (_("Spurious `,' or bad indirect register addressing mode."));
+ return -1;
+ }
+ /* Handle 68HC12 page specification in 'call foo,%page(bar)'. */
+ else if ((opmode & M6812_OP_PAGE) && strncmp (p, "%page", 5) == 0)
+ {
+ p += 5;
+ mode = M6811_OP_PAGE_ADDR | M6812_OP_PAGE | M6811_OP_IND16;
+ }
+ input_line_pointer = p;
+
+ if (mode == M6811_OP_NONE || mode == M6812_OP_D_IDX)
+ reg = register_name ();
+ else
+ reg = REG_NONE;
+
+ if (reg != REG_NONE)
+ {
+ p = skip_whites (input_line_pointer);
+ if (*p == ']' && mode == M6812_OP_D_IDX)
+ {
+ as_bad
+ (_("Missing second register or offset for indexed-indirect mode."));
+ return -1;
+ }
+
+ oper->reg1 = reg;
+ oper->mode = mode | M6812_OP_REG;
+ if (*p != ',')
+ {
+ if (mode == M6812_OP_D_IDX)
+ {
+ as_bad (_("Missing second register for indexed-indirect mode."));
+ return -1;
+ }
+ return 1;
+ }
+
+ p++;
+ input_line_pointer = p;
+ reg = register_name ();
+ if (reg != REG_NONE)
+ {
+ p = skip_whites (input_line_pointer);
+ if (mode == M6812_OP_D_IDX)
+ {
+ if (*p != ']')
+ {
+ as_bad (_("Missing `]' to close indexed-indirect mode."));
+ return -1;
+ }
+ p++;
+ oper->mode = M6812_OP_D_IDX;
+ }
+ input_line_pointer = p;
+
+ oper->reg2 = reg;
+ return 1;
+ }
+ return 1;
+ }
+
+ /* In MRI mode, isolate the operand because we can't distinguish
+ operands from comments. */
+ if (flag_mri)
+ {
+ char c = 0;
+
+ p = skip_whites (p);
+ while (*p && *p != ' ' && *p != '\t')
+ p++;
+
+ if (*p)
+ {
+ c = *p;
+ *p = 0;
+ }
+
+ /* Parse as an expression. */
+ expression (&oper->exp);
+
+ if (c)
+ {
+ *p = c;
+ }
+ }
+ else
+ {
+ expression (&oper->exp);
+ }
+
+ if (oper->exp.X_op == O_illegal)
+ {
+ as_bad (_("Illegal operand."));
+ return -1;
+ }
+ else if (oper->exp.X_op == O_absent)
+ {
+ as_bad (_("Missing operand."));
+ return -1;
+ }
+
+ p = input_line_pointer;
+
+ if (mode == M6811_OP_NONE || mode == M6811_OP_DIRECT
+ || mode == M6812_OP_D_IDX)
+ {
+ p = skip_whites (input_line_pointer);
+
+ if (*p == ',')
+ {
+ int possible_mode = M6811_OP_NONE;
+ char *old_input_line;
+
+ old_input_line = p;
+ p++;
+
+ /* 68HC12 pre increment or decrement. */
+ if (mode == M6811_OP_NONE)
+ {
+ if (*p == '-')
+ {
+ possible_mode = M6812_PRE_DEC;
+ p++;
+ }
+ else if (*p == '+')
+ {
+ possible_mode = M6812_PRE_INC;
+ p++;
+ }
+ p = skip_whites (p);
+ }
+ input_line_pointer = p;
+ reg = register_name ();
+
+ /* Backtrack if we have a valid constant expression and
+ it does not correspond to the offset of the 68HC12 indexed
+ addressing mode (as in N,x). */
+ if (reg == REG_NONE && mode == M6811_OP_NONE
+ && possible_mode != M6811_OP_NONE)
+ {
+ oper->mode = M6811_OP_IND16 | M6811_OP_JUMP_REL;
+ input_line_pointer = skip_whites (old_input_line);
+ return 1;
+ }
+
+ if (possible_mode != M6811_OP_NONE)
+ mode = possible_mode;
+
+ if ((current_architecture & cpu6811)
+ && possible_mode != M6811_OP_NONE)
+ as_bad (_("Pre-increment mode is not valid for 68HC11"));
+ /* Backtrack. */
+ if (which == 0 && opmode & M6812_OP_IDX_P2
+ && reg != REG_X && reg != REG_Y
+ && reg != REG_PC && reg != REG_SP)
+ {
+ reg = REG_NONE;
+ input_line_pointer = p;
+ }
+
+ if (reg == REG_NONE && mode != M6811_OP_DIRECT
+ && !(mode == M6811_OP_NONE && opmode & M6811_OP_IND16))
+ {
+ as_bad (_("Wrong register in register indirect mode."));
+ return -1;
+ }
+ if (mode == M6812_OP_D_IDX)
+ {
+ p = skip_whites (input_line_pointer);
+ if (*p++ != ']')
+ {
+ as_bad (_("Missing `]' to close register indirect operand."));
+ return -1;
+ }
+ input_line_pointer = p;
+ oper->reg1 = reg;
+ oper->mode = M6812_OP_D_IDX_2;
+ return 1;
+ }
+ if (reg != REG_NONE)
+ {
+ oper->reg1 = reg;
+ if (mode == M6811_OP_NONE)
+ {
+ p = input_line_pointer;
+ if (*p == '-')
+ {
+ mode = M6812_POST_DEC;
+ p++;
+ if (current_architecture & cpu6811)
+ as_bad
+ (_("Post-decrement mode is not valid for 68HC11."));
+ }
+ else if (*p == '+')
+ {
+ mode = M6812_POST_INC;
+ p++;
+ if (current_architecture & cpu6811)
+ as_bad
+ (_("Post-increment mode is not valid for 68HC11."));
+ }
+ else
+ mode = M6812_OP_IDX;
+
+ input_line_pointer = p;
+ }
+ else
+ mode |= M6812_OP_IDX;
+
+ oper->mode = mode;
+ return 1;
+ }
+ input_line_pointer = old_input_line;
+ }
+
+ if (mode == M6812_OP_D_IDX_2)
+ {
+ as_bad (_("Invalid indexed indirect mode."));
+ return -1;
+ }
+ }
+
+ /* If the mode is not known until now, this is either a label
+ or an indirect address. */
+ if (mode == M6811_OP_NONE)
+ mode = M6811_OP_IND16 | M6811_OP_JUMP_REL;
+
+ p = input_line_pointer;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ input_line_pointer = p;
+ oper->mode = mode;
+
+ return 1;
+}
+
+#define M6812_AUTO_INC_DEC (M6812_PRE_INC | M6812_PRE_DEC \
+ | M6812_POST_INC | M6812_POST_DEC)
+
+/* Checks that the number 'num' fits for a given mode. */
+static int
+check_range (long num, int mode)
+{
+ if (current_architecture == cpuxgate)
+ {
+ switch (mode)
+ {
+ case M68XG_OP_IMM3:
+ return (num >= 0 && num <= 7) ? 1 : 0;
+
+ case M68XG_OP_R_IMM4:
+ return (num >= 0 && num <= 15) ? 1 : 0;
+
+ case M68XG_OP_R_R_OFFS5:
+ return (num >= 0 && num <= 31) ? 1 : 0;
+
+ case M68XG_OP_R_IMM8:
+ return (num >= 0 && num <= 255) ? 1 : 0;
+
+ case M68XG_OP_R_IMM16:
+ return (num >= 0 && num <= 65535) ? 1 : 0;
+
+ case M68XG_OP_B_MARKER:
+ return (num >= -512 && num <= 511) ? 1 : 0;
+
+ case M68XG_OP_BRA_MARKER:
+ return (num >= -1024 && num <= 1023) ? 1 : 0;
+
+ default:
+ return 0;
+ }
+ }
+ else
+ {
+ /* Auto increment and decrement are ok for [-8..8] without 0. */
+ if (mode & M6812_AUTO_INC_DEC)
+ return (num != 0 && num <= 8 && num >= -8);
+
+ /* The 68HC12 supports 5, 9 and 16-bit offsets. */
+ if (mode & (M6812_INDEXED_IND | M6812_INDEXED | M6812_OP_IDX))
+ mode = M6811_OP_IND16;
+
+ if (mode & M6812_OP_JUMP_REL16)
+ mode = M6811_OP_IND16;
+
+ mode &= ~M6811_OP_BRANCH;
+ switch (mode)
+ {
+ case M6811_OP_IX:
+ case M6811_OP_IY:
+ case M6811_OP_DIRECT:
+ return (num >= 0 && num <= 255) ? 1 : 0;
+
+ case M6811_OP_BITMASK:
+ case M6811_OP_IMM8:
+ case M6812_OP_PAGE:
+ return (((num & 0xFFFFFF00) == 0) || ((num & 0xFFFFFF00) == 0xFFFFFF00))
+ ? 1 : 0;
+
+ case M6811_OP_JUMP_REL:
+ return (num >= -128 && num <= 127) ? 1 : 0;
+
+ case M6811_OP_IND16:
+ case M6811_OP_IND16 | M6812_OP_PAGE:
+ case M6811_OP_IMM16:
+ return (((num & 0xFFFF0000) == 0) || ((num & 0xFFFF0000) == 0xFFFF0000))
+ ? 1 : 0;
+
+ case M6812_OP_IBCC_MARKER:
+ case M6812_OP_TBCC_MARKER:
+ case M6812_OP_DBCC_MARKER:
+ return (num >= -256 && num <= 255) ? 1 : 0;
+
+ case M6812_OP_TRAP_ID:
+ return ((num >= 0x30 && num <= 0x39)
+ || (num >= 0x40 && num <= 0x0ff)) ? 1 : 0;
+
+ default:
+ return 0;
+ }
+ }
+}
+
+/* Gas fixup generation. */
+
+/* Put a 1 byte expression described by 'oper'. If this expression contains
+ unresolved symbols, generate an 8-bit fixup. */
+static void
+fixup8 (expressionS *oper, int mode, int opmode)
+{
+ char *f;
+
+ f = frag_more (1);
+
+ if (oper->X_op == O_constant)
+ {
+ if (mode & M6812_OP_TRAP_ID
+ && !check_range (oper->X_add_number, M6812_OP_TRAP_ID))
+ {
+ static char trap_id_warn_once = 0;
+
+ as_bad (_("Trap id `%ld' is out of range."), oper->X_add_number);
+ if (trap_id_warn_once == 0)
+ {
+ trap_id_warn_once = 1;
+ as_bad (_("Trap id must be within [0x30..0x39] or [0x40..0xff]."));
+ }
+ }
+
+ if (!(mode & M6812_OP_TRAP_ID)
+ && !check_range (oper->X_add_number, mode))
+ {
+ as_bad (_("Operand out of 8-bit range: `%ld'."), oper->X_add_number);
+ }
+ number_to_chars_bigendian (f, oper->X_add_number & 0x0FF, 1);
+ }
+ else if (oper->X_op != O_register)
+ {
+ if (mode & M6812_OP_TRAP_ID)
+ as_bad (_("The trap id must be a constant."));
+
+ if (mode == M6811_OP_JUMP_REL)
+ {
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+ oper, TRUE, BFD_RELOC_8_PCREL);
+ }
+ else
+ {
+ fixS *fixp;
+ int reloc;
+
+ /* Now create an 8-bit fixup. If there was some %hi, %lo
+ or %page modifier, generate the reloc accordingly. */
+ if (opmode & M6811_OP_HIGH_ADDR)
+ reloc = BFD_RELOC_M68HC11_HI8;
+ else if (opmode & M6811_OP_LOW_ADDR)
+ reloc = BFD_RELOC_M68HC11_LO8;
+ else if (opmode & M6811_OP_PAGE_ADDR)
+ reloc = BFD_RELOC_M68HC11_PAGE;
+ else
+ reloc = BFD_RELOC_8;
+
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+ oper, FALSE, reloc);
+ if (reloc != BFD_RELOC_8)
+ fixp->fx_no_overflow = 1;
+ }
+ number_to_chars_bigendian (f, 0, 1);
+ }
+ else
+ {
+ as_fatal (_("Operand `%x' not recognized in fixup8."), oper->X_op);
+ }
+}
+
+/* Put a 2 byte expression described by 'oper'. If this expression contains
+ unresolved symbols, generate a 16-bit fixup. */
+static void
+fixup16 (expressionS *oper, int mode, int opmode ATTRIBUTE_UNUSED)
+{
+ char *f;
+
+ f = frag_more (2);
+
+ if (oper->X_op == O_constant)
+ {
+ if (!check_range (oper->X_add_number, mode))
+ {
+ as_bad (_("Operand out of 16-bit range: `%ld'."),
+ oper->X_add_number);
+ }
+ number_to_chars_bigendian (f, oper->X_add_number & 0x0FFFF, 2);
+ }
+ else if (oper->X_op != O_register)
+ {
+ fixS *fixp;
+ int reloc;
+
+ if ((opmode & M6811_OP_CALL_ADDR) && (mode & M6811_OP_IMM16))
+ reloc = BFD_RELOC_M68HC11_LO16;
+ else if (mode & M6812_OP_JUMP_REL16)
+ reloc = BFD_RELOC_16_PCREL;
+ else if (mode & M6812_OP_PAGE)
+ reloc = BFD_RELOC_M68HC11_LO16;
+ else
+ reloc = BFD_RELOC_16;
+
+ /* Now create a 16-bit fixup. */
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 2,
+ oper,
+ reloc == BFD_RELOC_16_PCREL,
+ reloc);
+ number_to_chars_bigendian (f, 0, 2);
+
+ if (reloc == BFD_RELOC_M68HC11_LO16)
+ fixp->fx_no_overflow = 1;
+ }
+ else
+ {
+ as_fatal (_("Operand `%x' not recognized in fixup16."), oper->X_op);
+ }
+}
+
+/* Put a 3 byte expression described by 'oper'. If this expression contains
+ unresolved symbols, generate a 24-bit fixup. */
+static void
+fixup24 (expressionS *oper, int mode, int opmode ATTRIBUTE_UNUSED)
+{
+ char *f;
+
+ f = frag_more (3);
+
+ if (oper->X_op == O_constant)
+ {
+ if (!check_range (oper->X_add_number, mode))
+ {
+ as_bad (_("Operand out of 16-bit range: `%ld'."),
+ oper->X_add_number);
+ }
+ number_to_chars_bigendian (f, oper->X_add_number & 0x0FFFFFF, 3);
+ }
+ else if (oper->X_op != O_register)
+ {
+ /* Now create a 24-bit fixup. */
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 3,
+ oper, FALSE, BFD_RELOC_M68HC11_24);
+ number_to_chars_bigendian (f, 0, 3);
+ }
+ else
+ {
+ as_fatal (_("Operand `%x' not recognized in fixup16."), oper->X_op);
+ }
+}
+
+/* XGATE Put a 1 byte expression described by 'oper'. If this expression
+ containts unresolved symbols, generate an 8-bit fixup. */
+static void
+fixup8_xg (expressionS *oper, int mode, int opmode)
+{
+ char *f;
+
+ f = frag_more (1);
+
+ if (oper->X_op == O_constant)
+ {
+ fixS *fixp;
+ int reloc;
+
+ if ((opmode & M6811_OP_HIGH_ADDR) || (opmode & M6811_OP_LOW_ADDR))
+ {
+ if (opmode & M6811_OP_HIGH_ADDR)
+ reloc = BFD_RELOC_M68HC11_HI8;
+ else
+ reloc = BFD_RELOC_M68HC11_LO8;
+
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+ oper, FALSE, reloc);
+ fixp->fx_no_overflow = 1;
+ number_to_chars_bigendian (f, 0, 1);
+ }
+ else
+ {
+ if (!(check_range (oper->X_add_number, mode)))
+ as_bad (_("Operand out of 8-bit range: `%ld'."),
+ oper->X_add_number);
+ number_to_chars_bigendian (f, oper->X_add_number & 0x0FF, 1);
+ }
+ }
+ else if (oper->X_op != O_register)
+ {
+ if (mode == M68XG_OP_REL9)
+ {
+ /* Future improvement:
+ This fixup/reloc isn't adding on constants to symbols. */
+ fix_new_exp (frag_now, f - frag_now->fr_literal -1, 2,
+ oper, TRUE, BFD_RELOC_M68HC12_9_PCREL);
+ }
+ else if (mode == M68XG_OP_REL10)
+ {
+ /* Future improvement:
+ This fixup/reloc isn't adding on constants to symbols. */
+ fix_new_exp (frag_now, f - frag_now->fr_literal -1, 2,
+ oper, TRUE, BFD_RELOC_M68HC12_10_PCREL);
+ }
+ else
+ {
+ fixS *fixp;
+ int reloc;
+
+ /* Now create an 8-bit fixup. If there was some %hi, %lo
+ modifier, generate the reloc accordingly. */
+ if (opmode & M6811_OP_HIGH_ADDR)
+ reloc = BFD_RELOC_M68HC11_HI8;
+ else if (opmode & M6811_OP_LOW_ADDR)
+ reloc = BFD_RELOC_M68HC11_LO8;
+ else
+ reloc = BFD_RELOC_8;
+
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+ oper, FALSE, reloc);
+ if (reloc != BFD_RELOC_8)
+ fixp->fx_no_overflow = 1;
+ }
+ number_to_chars_bigendian (f, 0, 1);
+ }
+ else
+ as_fatal (_("Operand `%x' not recognized in fixup8."), oper->X_op);
+}
+
+/* 68HC11 and 68HC12 code generation. */
+
+/* Translate the short branch/bsr instruction into a long branch. */
+
+static unsigned char
+convert_branch (unsigned char code)
+{
+ if (IS_OPCODE (code, M6812_BSR))
+ return M6812_JSR;
+ else if (IS_OPCODE (code, M6811_BSR))
+ return M6811_JSR;
+ else if (IS_OPCODE (code, M6811_BRA))
+ return (current_architecture & cpu6812) ? M6812_JMP : M6811_JMP;
+ else
+ as_fatal (_("Unexpected branch conversion with `%x'"), code);
+
+ /* Keep gcc happy. */
+ return M6811_JSR;
+}
+
+/* Start a new insn that contains at least 'size' bytes. Record the
+ line information of that insn in the dwarf2 debug sections. */
+static char *
+m68hc11_new_insn (int size)
+{
+ char *f;
+
+ f = frag_more (size);
+
+ dwarf2_emit_insn (size);
+
+ return f;
+}
+
+/* Builds a jump instruction (bra, bcc, bsr). */
+static void
+build_jump_insn (struct m68hc11_opcode *opcode, operand operands[],
+ int nb_operands, int jmp_mode)
+{
+ unsigned char code;
+ char *f;
+ unsigned long n;
+
+ /* The relative branch conversion is not supported for
+ brclr and brset. */
+ gas_assert ((opcode->format & M6811_OP_BITMASK) == 0);
+ gas_assert (nb_operands == 1);
+ gas_assert (operands[0].reg1 == REG_NONE && operands[0].reg2 == REG_NONE);
+
+ code = opcode->opcode;
+
+ n = operands[0].exp.X_add_number;
+
+ /* Turn into a long branch:
+ - when force long branch option (and not for jbcc pseudos),
+ - when jbcc and the constant is out of -128..127 range,
+ - when branch optimization is allowed and branch out of range. */
+ if ((jmp_mode == 0 && flag_force_long_jumps)
+ || (operands[0].exp.X_op == O_constant
+ && (!check_range (n, opcode->format) &&
+ (jmp_mode == 1 || flag_fixed_branches == 0))))
+ {
+ fix_new (frag_now, frag_now_fix (), 0,
+ &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
+
+ if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR)
+ {
+ code = convert_branch (code);
+
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, code, 1);
+ }
+ else if (current_architecture & cpu6812)
+ {
+ /* 68HC12: translate the bcc into a lbcc. */
+ f = m68hc11_new_insn (2);
+ number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1);
+ number_to_chars_bigendian (f + 1, code, 1);
+ fixup16 (&operands[0].exp, M6812_OP_JUMP_REL16,
+ M6812_OP_JUMP_REL16);
+ return;
+ }
+ else
+ {
+ /* 68HC11: translate the bcc into b!cc +3; jmp <L>. */
+ f = m68hc11_new_insn (3);
+ code ^= 1;
+ number_to_chars_bigendian (f, code, 1);
+ number_to_chars_bigendian (f + 1, 3, 1);
+ number_to_chars_bigendian (f + 2, M6811_JMP, 1);
+ }
+ fixup16 (&operands[0].exp, M6811_OP_IND16, M6811_OP_IND16);
+ return;
+ }
+
+ /* Branch with a constant that must fit in 8-bits. */
+ if (operands[0].exp.X_op == O_constant)
+ {
+ if (!check_range (n, opcode->format))
+ {
+ as_bad (_("Operand out of range for a relative branch: `%ld'"),
+ n);
+ }
+ else if (opcode->format & M6812_OP_JUMP_REL16)
+ {
+ f = m68hc11_new_insn (4);
+ number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1);
+ number_to_chars_bigendian (f + 1, code, 1);
+ number_to_chars_bigendian (f + 2, n & 0x0ffff, 2);
+ }
+ else
+ {
+ f = m68hc11_new_insn (2);
+ number_to_chars_bigendian (f, code, 1);
+ number_to_chars_bigendian (f + 1, n & 0x0FF, 1);
+ }
+ }
+ else if (opcode->format & M6812_OP_JUMP_REL16)
+ {
+ fix_new (frag_now, frag_now_fix (), 0,
+ &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
+
+ f = m68hc11_new_insn (2);
+ number_to_chars_bigendian (f, M6811_OPCODE_PAGE2, 1);
+ number_to_chars_bigendian (f + 1, code, 1);
+ fixup16 (&operands[0].exp, M6812_OP_JUMP_REL16, M6812_OP_JUMP_REL16);
+ }
+ else
+ {
+ char *op;
+
+ fix_new (frag_now, frag_now_fix (), 0,
+ &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
+
+ /* Branch offset must fit in 8-bits, don't do some relax. */
+ if (jmp_mode == 0 && flag_fixed_branches)
+ {
+ op = m68hc11_new_insn (1);
+ number_to_chars_bigendian (op, code, 1);
+ fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL);
+ }
+
+ /* bra/bsr made be changed into jmp/jsr. */
+ else if (code == M6811_BSR || code == M6811_BRA || code == M6812_BSR)
+ {
+ /* Allocate worst case storage. */
+ op = m68hc11_new_insn (3);
+ number_to_chars_bigendian (op, code, 1);
+ number_to_chars_bigendian (op + 1, 0, 1);
+ frag_variant (rs_machine_dependent, 1, 1,
+ ENCODE_RELAX (STATE_PC_RELATIVE, STATE_UNDF),
+ operands[0].exp.X_add_symbol, (offsetT) n,
+ op);
+ }
+ else if (current_architecture & cpu6812)
+ {
+ op = m68hc11_new_insn (2);
+ number_to_chars_bigendian (op, code, 1);
+ number_to_chars_bigendian (op + 1, 0, 1);
+ frag_var (rs_machine_dependent, 2, 2,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_UNDF),
+ operands[0].exp.X_add_symbol, (offsetT) n, op);
+ }
+ else
+ {
+ op = m68hc11_new_insn (2);
+ number_to_chars_bigendian (op, code, 1);
+ number_to_chars_bigendian (op + 1, 0, 1);
+ frag_var (rs_machine_dependent, 3, 3,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_UNDF),
+ operands[0].exp.X_add_symbol, (offsetT) n, op);
+ }
+ }
+}
+
+/* Builds a dbne/dbeq/tbne/tbeq instruction. */
+static void
+build_dbranch_insn (struct m68hc11_opcode *opcode, operand operands[],
+ int nb_operands, int jmp_mode)
+{
+ unsigned char code;
+ char *f;
+ unsigned long n;
+
+ /* The relative branch conversion is not supported for
+ brclr and brset. */
+ gas_assert ((opcode->format & M6811_OP_BITMASK) == 0);
+ gas_assert (nb_operands == 2);
+ gas_assert (operands[0].reg1 != REG_NONE);
+
+ code = opcode->opcode & 0x0FF;
+
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, code, 1);
+
+ n = operands[1].exp.X_add_number;
+ code = operands[0].reg1;
+
+ if (operands[0].reg1 == REG_NONE || operands[0].reg1 == REG_CCR
+ || operands[0].reg1 == REG_PC)
+ as_bad (_("Invalid register for dbcc/tbcc instruction."));
+
+ if (opcode->format & M6812_OP_IBCC_MARKER)
+ code |= 0x80;
+ else if (opcode->format & M6812_OP_TBCC_MARKER)
+ code |= 0x40;
+
+ if (!(opcode->format & M6812_OP_EQ_MARKER))
+ code |= 0x20;
+
+ /* Turn into a long branch:
+ - when force long branch option (and not for jbcc pseudos),
+ - when jdbcc and the constant is out of -256..255 range,
+ - when branch optimization is allowed and branch out of range. */
+ if ((jmp_mode == 0 && flag_force_long_jumps)
+ || (operands[1].exp.X_op == O_constant
+ && (!check_range (n, M6812_OP_IBCC_MARKER) &&
+ (jmp_mode == 1 || flag_fixed_branches == 0))))
+ {
+ f = frag_more (2);
+ code ^= 0x20;
+ number_to_chars_bigendian (f, code, 1);
+ number_to_chars_bigendian (f + 1, M6812_JMP, 1);
+ fixup16 (&operands[0].exp, M6811_OP_IND16, M6811_OP_IND16);
+ return;
+ }
+
+ /* Branch with a constant that must fit in 9-bits. */
+ if (operands[1].exp.X_op == O_constant)
+ {
+ if (!check_range (n, M6812_OP_IBCC_MARKER))
+ {
+ as_bad (_("Operand out of range for a relative branch: `%ld'"),
+ n);
+ }
+ else
+ {
+ if ((long) n < 0)
+ code |= 0x10;
+
+ f = frag_more (2);
+ number_to_chars_bigendian (f, code, 1);
+ number_to_chars_bigendian (f + 1, n & 0x0FF, 1);
+ }
+ }
+ else
+ {
+ /* Branch offset must fit in 8-bits, don't do some relax. */
+ if (jmp_mode == 0 && flag_fixed_branches)
+ {
+ fixup8 (&operands[0].exp, M6811_OP_JUMP_REL, M6811_OP_JUMP_REL);
+ }
+
+ else
+ {
+ f = frag_more (2);
+ number_to_chars_bigendian (f, code, 1);
+ number_to_chars_bigendian (f + 1, 0, 1);
+ frag_var (rs_machine_dependent, 3, 3,
+ ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_UNDF),
+ operands[1].exp.X_add_symbol, (offsetT) n, f);
+ }
+ }
+}
+
+#define OP_EXTENDED (M6811_OP_PAGE2 | M6811_OP_PAGE3 | M6811_OP_PAGE4)
+
+/* Assemble the post index byte for 68HC12 extended addressing modes. */
+
+static int
+build_indexed_byte (operand *op, int format ATTRIBUTE_UNUSED, int move_insn)
+{
+ unsigned char byte = 0;
+ char *f;
+ int mode;
+ long val;
+
+ val = op->exp.X_add_number;
+ mode = op->mode;
+ if (mode & M6812_AUTO_INC_DEC)
+ {
+ byte = 0x20;
+ if (mode & (M6812_POST_INC | M6812_POST_DEC))
+ byte |= 0x10;
+
+ if (op->exp.X_op == O_constant)
+ {
+ if (!check_range (val, mode))
+ as_bad (_("Increment/decrement value is out of range: `%ld'."),
+ val);
+
+ if (mode & (M6812_POST_INC | M6812_PRE_INC))
+ byte |= (val - 1) & 0x07;
+ else
+ byte |= (8 - ((val) & 7)) | 0x8;
+ }
+
+ switch (op->reg1)
+ {
+ case REG_NONE:
+ as_fatal (_("Expecting a register."));
+
+ case REG_X:
+ byte |= 0;
+ break;
+
+ case REG_Y:
+ byte |= 0x40;
+ break;
+
+ case REG_SP:
+ byte |= 0x80;
+ break;
+
+ default:
+ as_bad (_("Invalid register for post/pre increment."));
+ break;
+ }
+
+ f = frag_more (1);
+ number_to_chars_bigendian (f, byte, 1);
+ return 1;
+ }
+
+ if (mode & (M6812_OP_IDX | M6812_OP_D_IDX_2))
+ {
+ switch (op->reg1)
+ {
+ case REG_X:
+ byte = 0;
+ break;
+
+ case REG_Y:
+ byte = 1;
+ break;
+
+ case REG_SP:
+ byte = 2;
+ break;
+
+ case REG_PC:
+ byte = 3;
+ break;
+
+ default:
+ as_bad (_("Invalid register."));
+ break;
+ }
+
+ if (op->exp.X_op == O_constant)
+ {
+ if (!check_range (val, M6812_OP_IDX))
+ as_bad (_("Offset out of 16-bit range: %ld."), val);
+
+ if (move_insn && !(val >= -16 && val <= 15)
+ && ((!(mode & M6812_OP_IDX) && !(mode & M6812_OP_D_IDX_2))
+ || !(current_architecture & cpu9s12x)))
+ {
+ as_bad (_("Offset out of 5-bit range for movw/movb insn: %ld."),
+ val);
+ return -1;
+ }
+
+ if (val >= -16 && val <= 15 && !(mode & M6812_OP_D_IDX_2))
+ {
+ byte = byte << 6;
+ byte |= val & 0x1f;
+ f = frag_more (1);
+ number_to_chars_bigendian (f, byte, 1);
+ return 1;
+ }
+ else if (val >= -256 && val <= 255 && !(mode & M6812_OP_D_IDX_2))
+ {
+ byte = byte << 3;
+ byte |= 0xe0;
+ if (val < 0)
+ byte |= 0x1;
+ f = frag_more (2);
+ number_to_chars_bigendian (f, byte, 1);
+ number_to_chars_bigendian (f + 1, val & 0x0FF, 1);
+ return 2;
+ }
+ else
+ {
+ byte = byte << 3;
+ if (mode & M6812_OP_D_IDX_2)
+ byte |= 0xe3;
+ else
+ byte |= 0xe2;
+
+ f = frag_more (3);
+ number_to_chars_bigendian (f, byte, 1);
+ number_to_chars_bigendian (f + 1, val & 0x0FFFF, 2);
+ return 3;
+ }
+ }
+
+ if (mode & M6812_OP_D_IDX_2)
+ {
+ byte = (byte << 3) | 0xe3;
+ f = frag_more (1);
+ number_to_chars_bigendian (f, byte, 1);
+
+ fixup16 (&op->exp, 0, 0);
+ }
+ else if (op->reg1 != REG_PC)
+ {
+ symbolS *sym;
+ offsetT off;
+
+ f = frag_more (1);
+ number_to_chars_bigendian (f, byte, 1);
+ sym = op->exp.X_add_symbol;
+ off = op->exp.X_add_number;
+ if (op->exp.X_op != O_symbol)
+ {
+ sym = make_expr_symbol (&op->exp);
+ off = 0;
+ }
+
+ /* movb/movw cannot be relaxed. */
+ if (move_insn)
+ {
+ if ((mode & M6812_OP_IDX) && (current_architecture & cpu9s12x))
+ {
+ /* Must treat as a 16bit relocate as size of final result is unknown. */
+
+ byte <<= 3;
+ byte |= 0xe2;
+ number_to_chars_bigendian (f, byte, 1);
+ f = frag_more (2);
+ fix_new (frag_now, f - frag_now->fr_literal, 2,
+ sym, off, 0, BFD_RELOC_M68HC12_16B);
+ return 1;
+ }
+ else
+ {
+ /* Non-S12X will fail at relocate stage if offset out of range. */
+ byte <<= 6;
+ number_to_chars_bigendian (f, byte, 1);
+ fix_new (frag_now, f - frag_now->fr_literal, 1,
+ sym, off, 0, BFD_RELOC_M68HC12_5B);
+ return 1;
+ }
+ }
+ else
+ {
+ number_to_chars_bigendian (f, byte, 1);
+ frag_var (rs_machine_dependent, 2, 2,
+ ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_UNDF),
+ sym, off, f);
+ }
+ }
+ else
+ {
+ f = frag_more (1);
+
+ /* movb/movw cannot be relaxed. */
+ if (move_insn)
+ {
+ byte <<= 6;
+ number_to_chars_bigendian (f, byte, 1);
+ fix_new (frag_now, f - frag_now->fr_literal, 1,
+ op->exp.X_add_symbol, op->exp.X_add_number, 0, BFD_RELOC_M68HC12_5B);
+ return 1;
+ }
+ else
+ {
+ number_to_chars_bigendian (f, byte, 1);
+ frag_var (rs_machine_dependent, 2, 2,
+ ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_UNDF),
+ op->exp.X_add_symbol,
+ op->exp.X_add_number, f);
+ }
+ }
+ return 3;
+ }
+
+ if (mode & (M6812_OP_REG | M6812_OP_D_IDX))
+ {
+ if (mode & M6812_OP_D_IDX)
+ {
+ if (op->reg1 != REG_D)
+ as_bad (_("Expecting register D for indexed indirect mode."));
+ if ((move_insn) && (!(current_architecture & cpu9s12x)))
+ as_bad (_("Indexed indirect mode is not allowed for movb/movw."));
+
+ byte = 0xE7;
+ }
+ else
+ {
+ switch (op->reg1)
+ {
+ case REG_A:
+ byte = 0xE4;
+ break;
+
+ case REG_B:
+ byte = 0xE5;
+ break;
+
+ default:
+ as_bad (_("Invalid accumulator register."));
+
+ case REG_D:
+ byte = 0xE6;
+ break;
+ }
+ }
+ switch (op->reg2)
+ {
+ case REG_X:
+ break;
+
+ case REG_Y:
+ byte |= (1 << 3);
+ break;
+
+ case REG_SP:
+ byte |= (2 << 3);
+ break;
+
+ case REG_PC:
+ byte |= (3 << 3);
+ break;
+
+ default:
+ as_bad (_("Invalid indexed register."));
+ break;
+ }
+ f = frag_more (1);
+ number_to_chars_bigendian (f, byte, 1);
+ return 1;
+ }
+
+ fprintf (stderr, "mode = 0x%x\nop->reg1 = 0x%x\nop->reg2 = 0x%x\n",
+ mode, op->reg1, op->reg2);
+ as_fatal (_("Addressing mode not implemented yet."));
+ return 0;
+}
+
+/* Assemble the 68HC12 register mode byte. */
+static int
+build_reg_mode (operand *op, int format)
+{
+ unsigned char byte;
+ char *f;
+
+ if ((format & M6812_OP_SEX_MARKER)
+ && (op->reg1 != REG_A) && (op->reg1 != REG_B) && (op->reg1 != REG_CCR)
+ && (!(current_architecture & cpu9s12x)))
+ as_bad (_("Invalid source register for this instruction, use 'tfr'."));
+ else if (op->reg1 == REG_NONE || op->reg1 == REG_PC)
+ as_bad (_("Invalid source register."));
+
+ if (format & M6812_OP_SEX_MARKER
+ && op->reg2 != REG_D
+ && op->reg2 != REG_X && op->reg2 != REG_Y && op->reg2 != REG_SP)
+ as_bad (_("Invalid destination register for this instruction, use 'tfr'."));
+ else if (op->reg2 == REG_NONE || op->reg2 == REG_PC)
+ as_bad (_("Invalid destination register."));
+
+ byte = (op->reg1 << 4) | (op->reg2);
+ if (format & M6812_OP_EXG_MARKER)
+ byte |= 0x80;
+
+ if ((format & M6812_OP_SEX_MARKER)
+ && (op->reg1 == REG_D) && (current_architecture & cpu9s12x))
+ byte |= 0x08;
+
+ f = frag_more (1);
+ number_to_chars_bigendian (f, byte, 1);
+ return 1;
+}
+
+/* build_insn_xg takes a pointer to the opcode entry in the opcode table,
+ the array of operand expressions and builds the corresponding instruction. */
+
+static void
+build_insn_xg (struct m68hc11_opcode *opcode,
+ operand operands[],
+ int nb_operands ATTRIBUTE_UNUSED)
+{
+ char *f;
+ long format;
+
+ /* Put the page code instruction if there is one. */
+ format = opcode->format;
+
+ if (!(operands[0].mode & (M6811_OP_LOW_ADDR | M6811_OP_HIGH_ADDR)))
+ /* Need to retain those two modes, but clear for others. */
+ operands[0].mode = 0;
+
+ if (format & M68XG_OP_R_IMM8)
+ {
+ /* These opcodes are byte followed by imm8. */
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, opcode->opcode >> 8, 1);
+ fixup8_xg (&operands[0].exp, format, operands[0].mode);
+ }
+ else if (format & M68XG_OP_R_IMM16)
+ {
+ fixS *fixp;
+ /* These opcodes expand into two imm8 instructions.
+ Emit as low:high as per the Freescale datasheet.
+ The linker requires them to be adjacent to handle the upper byte. */
+
+ /* Build low byte. */
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, opcode->opcode >> 8, 1);
+ operands[0].mode = M6811_OP_LOW_ADDR;
+ f = frag_more (1);
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+ &operands[0].exp, FALSE, BFD_RELOC_M68HC12_LO8XG);
+ fixp->fx_no_overflow = 1;
+ number_to_chars_bigendian (f, 0, 1);
+
+ /* Build high byte. */
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, (opcode->opcode >> 8) | 0x08, 1);
+ operands[0].mode = M6811_OP_HIGH_ADDR;
+ f = frag_more (1);
+ fixp = fix_new_exp (frag_now, f - frag_now->fr_literal, 1,
+ &operands[0].exp, FALSE, BFD_RELOC_M68HC12_HI8XG);
+ fixp->fx_no_overflow = 1;
+ number_to_chars_bigendian (f, 0, 1);
+
+ }
+ else if (format & M68XG_OP_REL9)
+ {
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, opcode->opcode >> 8, 1); /* High byte. */
+ fixup8_xg (&operands[0].exp, format, M68XG_OP_REL9);
+ }
+ else if (format & M68XG_OP_REL10)
+ {
+ f = m68hc11_new_insn (1);
+ number_to_chars_bigendian (f, opcode->opcode >> 8, 1); /* High byte. */
+ fixup8_xg (&operands[0].exp, format, M68XG_OP_REL10);
+ }
+ else
+ {
+ f = m68hc11_new_insn (2);
+ number_to_chars_bigendian (f, opcode->opcode, 2);
+ }
+ return;
+}
+
+/* build_insn takes a pointer to the opcode entry in the opcode table,
+ the array of operand expressions and builds the corresponding instruction.
+ This operation only deals with non relative jumps insn (need special
+ handling). */
+
+static void
+build_insn (struct m68hc11_opcode *opcode,
+ operand operands[],
+ int nb_operands ATTRIBUTE_UNUSED)
+{
+ int i;
+ char *f;
+ long format;
+ int move_insn = 0;
+
+ /* Put the page code instruction if there is one. */
+ format = opcode->format;
+
+ if (format & M6811_OP_BRANCH)
+ fix_new (frag_now, frag_now_fix (), 0,
+ &abs_symbol, 0, 1, BFD_RELOC_M68HC11_RL_JUMP);
+
+ if (format & OP_EXTENDED)
+ {
+ int page_code;
+
+ f = m68hc11_new_insn (2);
+ if (format & M6811_OP_PAGE2)
+ page_code = M6811_OPCODE_PAGE2;
+ else if (format & M6811_OP_PAGE3)
+ page_code = M6811_OPCODE_PAGE3;
+ else
+ page_code = M6811_OPCODE_PAGE4;
+
+ number_to_chars_bigendian (f, page_code, 1);
+ f++;
+ }
+ else
+ f = m68hc11_new_insn (1);
+
+ number_to_chars_bigendian (f, opcode->opcode, 1);
+
+ i = 0;
+
+ /* The 68HC12 movb and movw instructions are special. We have to handle
+ them in a special way. */
+ if (format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))
+ {
+ move_insn = 1;
+ if (format & M6812_OP_IDX)
+ {
+ build_indexed_byte (&operands[0], format, 1);
+ i = 1;
+ format &= ~M6812_OP_IDX;
+ }
+ if (format & M6812_OP_IDX_P2)
+ {
+ build_indexed_byte (&operands[1], format, 1);
+ i = 0;
+ format &= ~M6812_OP_IDX_P2;
+ }
+ }
+
+ if (format & (M6811_OP_DIRECT | M6811_OP_IMM8))
+ {
+ fixup8 (&operands[i].exp,
+ format & (M6811_OP_DIRECT | M6811_OP_IMM8 | M6812_OP_TRAP_ID),
+ operands[i].mode);
+ i++;
+ }
+ else if (IS_CALL_SYMBOL (format) && nb_operands == 1)
+ {
+ format &= ~M6812_OP_PAGE;
+ fixup24 (&operands[i].exp, format & M6811_OP_IND16,
+ operands[i].mode);
+ i++;
+ }
+ else if (format & (M6811_OP_IMM16 | M6811_OP_IND16))
+ {
+ fixup16 (&operands[i].exp,
+ format & (M6811_OP_IMM16 | M6811_OP_IND16 | M6812_OP_PAGE),
+ operands[i].mode);
+ i++;
+ }
+ else if (format & (M6811_OP_IX | M6811_OP_IY))
+ {
+ if ((format & M6811_OP_IX) && (operands[0].reg1 != REG_X))
+ as_bad (_("Invalid indexed register, expecting register X."));
+ if ((format & M6811_OP_IY) && (operands[0].reg1 != REG_Y))
+ as_bad (_("Invalid indexed register, expecting register Y."));
+
+ fixup8 (&operands[0].exp, M6811_OP_IX, operands[0].mode);
+ i = 1;
+ }
+ else if (format &
+ (M6812_OP_IDX | M6812_OP_IDX_2 | M6812_OP_IDX_1
+ | M6812_OP_D_IDX | M6812_OP_D_IDX_2))
+ {
+ build_indexed_byte (&operands[i], format, move_insn);
+ i++;
+ }
+ else if (format & M6812_OP_REG && current_architecture & cpu6812)
+ {
+ build_reg_mode (&operands[i], format);
+ i++;
+ }
+ if (format & M6811_OP_BITMASK)
+ {
+ fixup8 (&operands[i].exp, M6811_OP_BITMASK, operands[i].mode);
+ i++;
+ }
+ if (format & M6811_OP_JUMP_REL)
+ {
+ fixup8 (&operands[i].exp, M6811_OP_JUMP_REL, operands[i].mode);
+ }
+ else if (format & M6812_OP_IND16_P2)
+ {
+ fixup16 (&operands[1].exp, M6811_OP_IND16, operands[1].mode);
+ }
+ if (format & M6812_OP_PAGE)
+ {
+ fixup8 (&operands[i].exp, M6812_OP_PAGE, operands[i].mode);
+ }
+}
+
+/* Opcode identification and operand analysis. */
+
+/* find() gets a pointer to an entry in the opcode table. It must look at all
+ opcodes with the same name and use the operands to choose the correct
+ opcode. Returns the opcode pointer if there was a match and 0 if none. */
+static struct m68hc11_opcode *
+find (struct m68hc11_opcode_def *opc, operand operands[], int nb_operands)
+{
+ int i, match, pos;
+ struct m68hc11_opcode *opcode;
+ struct m68hc11_opcode *op_indirect;
+
+ op_indirect = 0;
+ opcode = opc->opcode;
+
+ /* Now search the opcode table table for one with operands
+ that matches what we've got. */
+
+ if (current_architecture & cpuxgate)
+ {
+ /* Many XGATE insns are simple enough that we get an exact match. */
+ for (pos = match = 0; match == 0 && pos < opc->nb_modes; pos++, opcode++)
+ if (opcode->format == operands[nb_operands-1].mode)
+ return opcode;
+
+ return 0;
+ }
+
+ /* Non XGATE */
+
+ /* Now search the opcode table table for one with operands
+ that matches what we've got. We're only done if the operands matched so
+ far AND there are no more to check. */
+ for (pos = match = 0; match == 0 && pos < opc->nb_modes; pos++, opcode++)
+ {
+ int poss_indirect = 0;
+ long format = opcode->format;
+ int expect;
+
+ expect = 0;
+ if (opcode->format & M6811_OP_MASK)
+ expect++;
+ if (opcode->format & M6811_OP_BITMASK)
+ expect++;
+ if (opcode->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+ expect++;
+ if (opcode->format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2))
+ expect++;
+ if ((opcode->format & M6812_OP_PAGE)
+ && (!IS_CALL_SYMBOL (opcode->format) || nb_operands == 2))
+ expect++;
+
+ for (i = 0; expect == nb_operands && i < nb_operands; i++)
+ {
+ int mode = operands[i].mode;
+
+ if (mode & M6811_OP_IMM16)
+ {
+ if (format &
+ (M6811_OP_IMM8 | M6811_OP_IMM16 | M6811_OP_BITMASK))
+ continue;
+ break;
+ }
+ if (mode == M6811_OP_DIRECT)
+ {
+ if (format & M6811_OP_DIRECT)
+ continue;
+
+ /* If the operand is a page 0 operand, remember a
+ possible <abs-16> addressing mode. We mark
+ this and continue to check other operands. */
+ if (format & M6811_OP_IND16
+ && flag_strict_direct_addressing && op_indirect == 0)
+ {
+ poss_indirect = 1;
+ continue;
+ }
+ break;
+ }
+ if (mode & M6811_OP_IND16)
+ {
+ if (i == 0 && (format & M6811_OP_IND16) != 0)
+ continue;
+ if (i != 0 && (format & M6812_OP_PAGE) != 0)
+ continue;
+ if (i != 0 && (format & M6812_OP_IND16_P2) != 0)
+ continue;
+ if (i == 0 && (format & M6811_OP_BITMASK))
+ break;
+ }
+ if (mode & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+ {
+ if (format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+ continue;
+ }
+ if (mode & M6812_OP_REG)
+ {
+ if (i == 0
+ && (format & M6812_OP_REG)
+ && (operands[i].reg2 == REG_NONE))
+ continue;
+ if (i == 0
+ && (format & M6812_OP_REG)
+ && (format & M6812_OP_REG_2)
+ && (operands[i].reg2 != REG_NONE))
+ continue;
+ if (i == 0
+ && (format & M6812_OP_IDX)
+ && (operands[i].reg2 != REG_NONE))
+ continue;
+ if (i == 0
+ && (format & M6812_OP_IDX)
+ && (format & (M6812_OP_IND16_P2 | M6812_OP_IDX_P2)))
+ continue;
+ if (i == 1
+ && (format & M6812_OP_IDX_P2))
+ continue;
+ break;
+ }
+ if (mode & M6812_OP_IDX)
+ {
+ if (format & M6811_OP_IX && operands[i].reg1 == REG_X)
+ continue;
+ if (format & M6811_OP_IY && operands[i].reg1 == REG_Y)
+ continue;
+ if (i == 0
+ && format & (M6812_OP_IDX | M6812_OP_IDX_1 | M6812_OP_IDX_2)
+ && (operands[i].reg1 == REG_X
+ || operands[i].reg1 == REG_Y
+ || operands[i].reg1 == REG_SP
+ || operands[i].reg1 == REG_PC))
+ continue;
+ if (i == 1 && (format & M6812_OP_IDX_P2))
+ continue;
+ }
+ if (mode & format & (M6812_OP_D_IDX | M6812_OP_D_IDX_2))
+ {
+ if (i == 0)
+ continue;
+ }
+ if (mode & M6812_AUTO_INC_DEC)
+ {
+ if (i == 0
+ && format & (M6812_OP_IDX | M6812_OP_IDX_1 |
+ M6812_OP_IDX_2))
+ continue;
+ if (i == 1 && format & M6812_OP_IDX_P2)
+ continue;
+ }
+ break;
+ }
+ match = i == nb_operands;
+
+ /* Operands are ok but an operand uses page 0 addressing mode
+ while the insn supports abs-16 mode. Keep a reference to this
+ insns in case there is no insn supporting page 0 addressing. */
+ if (match && poss_indirect)
+ {
+ op_indirect = opcode;
+ match = 0;
+ }
+ if (match)
+ break;
+ }
+
+ /* Page 0 addressing is used but not supported by any insn.
+ If absolute addresses are supported, we use that insn. */
+ if (match == 0 && op_indirect)
+ {
+ opcode = op_indirect;
+ match = 1;
+ }
+
+ return match ? opcode : 0;
+}
+
+/* Find the real opcode and its associated operands. We use a progressive
+ approach here. On entry, 'opc' points to the first opcode in the
+ table that matches the opcode name in the source line. We try to
+ isolate an operand, find a possible match in the opcode table.
+ We isolate another operand if no match were found. The table 'operands'
+ is filled while operands are recognized.
+
+ Returns the opcode pointer that matches the opcode name in the
+ source line and the associated operands. */
+static struct m68hc11_opcode *
+find_opcode (struct m68hc11_opcode_def *opc, operand operands[],
+ int *nb_operands)
+{
+ struct m68hc11_opcode *opcode;
+ int i;
+
+ if (opc->max_operands == 0)
+ {
+ *nb_operands = 0;
+ return opc->opcode;
+ }
+
+ for (i = 0; i < opc->max_operands;)
+ {
+ int result;
+
+ result = get_operand (&operands[i], i, opc->format);
+ if (result <= 0)
+ return 0;
+
+ /* Special case where the bitmask of the bclr/brclr
+ instructions is not introduced by #.
+ Example: bclr 3,x $80. */
+ if (i == 1 && (opc->format & M6811_OP_BITMASK)
+ && (operands[i].mode & M6811_OP_IND16))
+ {
+ operands[i].mode = M6811_OP_IMM16;
+ }
+
+ i += result;
+ *nb_operands = i;
+ if (i >= opc->min_operands)
+ {
+ opcode = find (opc, operands, i);
+
+ /* Another special case for 'call foo,page' instructions.
+ Since we support 'call foo' and 'call foo,page' we must look
+ if the optional page specification is present otherwise we will
+ assemble immediately and treat the page spec as garbage. */
+ if (opcode && !(opcode->format & M6812_OP_PAGE))
+ return opcode;
+
+ if (opcode && *input_line_pointer != ',')
+ return opcode;
+ }
+
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+ }
+
+ return 0;
+}
+
+#define M6812_XBCC_MARKER (M6812_OP_TBCC_MARKER \
+ | M6812_OP_DBCC_MARKER \
+ | M6812_OP_IBCC_MARKER)
+
+/* Gas line assembler entry point. */
+
+/* This is the main entry point for the machine-dependent assembler. str
+ points to a machine-dependent instruction. This function is supposed to
+ emit the frags/bytes it assembles to. */
+void
+md_assemble (char *str)
+{
+ struct m68hc11_opcode_def *opc;
+ struct m68hc11_opcode *opcode;
+
+ struct m68hc11_opcode opcode_local;
+ unsigned char *op_start, *op_end;
+ char *save;
+ char name[20];
+ int nlen = 0;
+ operand operands[M6811_MAX_OPERANDS];
+ int nb_operands = 0;
+ int branch_optimize = 0;
+ int alias_id = -1;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the opcode end and get the opcode in 'name'. The opcode is forced
+ lower case (the opcode table only has lower case op-codes). */
+ for (op_start = op_end = (unsigned char *) str;
+ *op_end && !is_end_of_line[*op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = TOLOWER (op_start[nlen]);
+ nlen++;
+ if (nlen == sizeof (name) - 1)
+ break;
+ }
+ name[nlen] = 0;
+
+ if (nlen == 0)
+ {
+ as_bad (_("No instruction or missing opcode."));
+ return;
+ }
+
+ if (current_architecture == cpuxgate)
+ {
+ /* Find the opcode definition given its name. */
+ opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name);
+ if (opc == NULL)
+ {
+ as_bad (_("Opcode `%s' is not recognized."), name);
+ return;
+ }
+
+ /* Grab a local copy. */
+ opcode_local.name = opc->opcode->name;
+ /* These will be incomplete where multiple variants exist. */
+ opcode_local.opcode = opc->opcode->opcode;
+ opcode_local.format = opc->opcode->format;
+
+ save = input_line_pointer;
+ input_line_pointer = (char *) op_end;
+
+ if (opc->format == M68XG_OP_NONE)
+ {
+ /* No special handling required. */
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (opc->opcode, operands, 0);
+ return;
+ }
+
+ /* Special handling of TFR. */
+ if (strncmp (opc->opcode->name, "tfr",3) == 0)
+ {
+ /* There must be two operands with a comma. */
+ input_line_pointer = skip_whites (input_line_pointer);
+ operands[0].reg1 = register_name ();
+ if (operands[0].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ input_line_pointer = skip_whites (input_line_pointer);
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("Missing comma.\n");
+ return;
+ }
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+ operands[1].reg1 = register_name ();
+ if (operands[1].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ input_line_pointer = skip_whites (input_line_pointer);
+ if (*input_line_pointer != '\n' && *input_line_pointer)
+ {
+ as_bad (_("Garbage at end of instruction: `%s'."),
+ input_line_pointer);
+ return;
+ }
+ if (operands[1].reg1 == REG_CCR) /* ,CCR */
+ opc->opcode->opcode = 0x00f8 | ( operands[0].reg1 << 8);
+ else if (operands[0].reg1 == REG_CCR) /* CCR, */
+ opc->opcode->opcode = 0x00f9 | ( operands[1].reg1 << 8);
+ else if (operands[1].reg1 == REG_PC) /* ,PC */
+ opc->opcode->opcode = 0x00fa | ( operands[0].reg1 << 8);
+ else
+ {
+ as_bad ("Invalid operand to TFR\n");
+ return;
+ }
+ /* no special handling required */
+ opcode_local.format = M68XG_OP_NONE;
+ opcode_local.opcode = opc->opcode->opcode;
+ build_insn_xg (&opcode_local, operands, 0);
+ return;
+ }
+
+ /* CSEM, SSEM */
+ if (opc->format & M68XG_OP_IMM3)
+ {
+ /* Either IMM3 or R */
+ input_line_pointer = skip_whites (input_line_pointer);
+ if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r'))
+ {
+ operands[0].reg1 = register_name ();
+ if (operands[0].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ operands[0].mode = M68XG_OP_R;
+ /* One opcode has multiple modes, so find right one. */
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8);
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ else
+ as_bad ("No opcode found\n");
+
+ return;
+ }
+ else
+ {
+ if (*input_line_pointer == '#')
+ input_line_pointer++;
+
+ expression (&operands[0].exp);
+ if (operands[0].exp.X_op == O_illegal)
+ {
+ as_bad (_("Illegal operand."));
+ return;
+ }
+ else if (operands[0].exp.X_op == O_absent)
+ {
+ as_bad (_("Missing operand."));
+ return;
+ }
+
+ if (check_range (operands[0].exp.X_add_number,M68XG_OP_IMM3))
+ {
+ opcode_local.opcode |= (operands[0].exp.X_add_number);
+ operands[0].mode = M68XG_OP_IMM3;
+
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ opcode_local.opcode = opcode->opcode;
+ opcode_local.opcode
+ |= (operands[0].exp.X_add_number) << 8;
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ else
+ as_bad ("No opcode found\n");
+
+ return;
+ }
+ else
+ {
+ as_bad ("Number out of range for IMM3\n");
+ return;
+ }
+ }
+ }
+
+ /* Special handling of SIF. */
+ if (strncmp (opc->opcode->name, "sif",3) == 0)
+ {
+ /* Either OP_NONE or OP_RS. */
+ if (*input_line_pointer != '\n')
+ input_line_pointer = skip_whites (input_line_pointer);
+
+ if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r')
+ || (*input_line_pointer == '\0'))
+ opc->opcode->opcode = 0x0300;
+ else
+ {
+ operands[0].reg1 = register_name ();
+ if (operands[0].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ opcode_local.opcode = 0x00f7 | (operands[0].reg1 << 8);
+ }
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 0);
+ return;
+ }
+
+ /* SEX, PAR, JAL plus aliases NEG, TST, COM */
+ if (opc->format & M68XG_OP_R)
+ {
+ input_line_pointer = skip_whites (input_line_pointer);
+ operands[0].reg1 = register_name ();
+ if (operands[0].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r')
+ || (*input_line_pointer == '\0'))
+ {
+ /* Likely to be OP R. */
+ if (opc->format & M68XG_OP_R)
+ {
+ operands[0].mode = M68XG_OP_R;
+
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ if ((strncmp (opc->opcode->name, "com",3) == 0)
+ || (strncmp (opc->opcode->name, "neg",3) == 0))
+ /* Special case for com RD as alias for sub RD,R0,RS */
+ /* Special case for neg RD as alias for sub RD,R0,RS */
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8) | (operands[0].reg1 << 2);
+ else if (strncmp (opc->opcode->name, "tst",3) == 0)
+ /* Special case for tst RS alias for sub R0, RS, R0 */
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 5);
+ else
+ opcode_local.opcode |= (operands[0].reg1 << 8);
+ }
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 0);
+ }
+ else
+ as_bad ("No valid mode found\n");
+
+ return;
+ }
+ }
+
+ if (opc->format & (M68XG_OP_REL9 | M68XG_OP_REL10))
+ {
+ opcode_local.format = opc->format;
+ input_line_pointer = skip_whites (input_line_pointer);
+ expression (&operands[0].exp);
+ if (operands[0].exp.X_op == O_illegal)
+ {
+ as_bad (_("Illegal operand."));
+ return;
+ }
+ else if (operands[0].exp.X_op == O_absent)
+ {
+ as_bad (_("Missing operand."));
+ return;
+ }
+ opcode_local.opcode = opc->opcode->opcode;
+ build_insn_xg (&opcode_local, operands, 1);
+ return;
+ }
+
+
+ /* For other command formats, parse input line and determine the mode
+ we are using as we go. */
+
+ input_line_pointer = skip_whites (input_line_pointer);
+ if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r')
+ || (*input_line_pointer == '\0'))
+ return; /* nothing left */
+
+ if (*input_line_pointer == '#')
+ {
+ as_bad ("No register specified before hash\n");
+ return;
+ }
+
+ /* first operand is expected to be a register */
+ if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r'))
+ {
+ operands[0].reg1 = register_name ();
+ if (operands[0].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ }
+
+ input_line_pointer = skip_whites (input_line_pointer);
+ if (*input_line_pointer != ',')
+ {
+ as_bad ("Missing operand\n");
+ return;
+ }
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+
+ if (*input_line_pointer == '#')
+ {
+ /* Some kind of immediate mode, check if this is possible. */
+ if (!(opc->format
+ & (M68XG_OP_R_IMM8 | M68XG_OP_R_IMM16 | M68XG_OP_R_IMM4)))
+ as_bad ("Invalid immediate mode for `%s'", opc->opcode->name);
+ else
+ {
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+ if (strncmp (input_line_pointer, "%hi", 3) == 0)
+ {
+ input_line_pointer += 3;
+ operands[0].mode = M6811_OP_HIGH_ADDR;
+ }
+ else if (strncmp (input_line_pointer, "%lo", 3) == 0)
+ {
+ input_line_pointer += 3;
+ operands[0].mode = M6811_OP_LOW_ADDR;
+ }
+ else
+ operands[0].mode = 0;
+
+ expression (&operands[0].exp);
+ if (operands[0].exp.X_op == O_illegal)
+ {
+ as_bad (_("Illegal operand."));
+ return;
+ }
+ else if (operands[0].exp.X_op == O_absent)
+ {
+ as_bad (_("Missing operand."));
+ return;
+ }
+ /* ok so far, can only be one mode */
+ opcode_local.format = opc->format
+ & (M68XG_OP_R_IMM8 | M68XG_OP_R_IMM16 | M68XG_OP_R_IMM4);
+ if (opcode_local.format & M68XG_OP_R_IMM4)
+ {
+ operands[0].mode = M68XG_OP_R_IMM4;
+ /* same opcodes have multiple modes, so find right one */
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8);
+
+ if (operands[0].exp.X_op != O_constant)
+ as_bad ("Only constants supported at for IMM4 mode\n");
+ else
+ {
+ if (check_range
+ (operands[0].exp.X_add_number,M68XG_OP_R_IMM4))
+ opcode_local.opcode
+ |= (operands[0].exp.X_add_number << 4);
+ else
+ as_bad ("Number out of range for IMM4\n");
+ }
+ opcode_local.format = M68XG_OP_NONE;
+ }
+ else if (opcode_local.format & M68XG_OP_R_IMM16)
+ {
+ operands[0].mode = M68XG_OP_R_IMM16;
+
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8);
+ }
+ }
+ else
+ {
+ opcode_local.opcode = opc->opcode->opcode
+ | (operands[0].reg1 << 8);
+ }
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ }
+ else if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r'))
+ {
+ /* we've got as far as OP R, R */
+ operands[1].reg1 = register_name ();
+ if (operands[1].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r')
+ || (*input_line_pointer == '\0'))
+ {
+ /* looks like OP_R_R */
+ if (opc->format & M68XG_OP_R_R)
+ {
+ operands[0].mode = M68XG_OP_R_R;
+ /* same opcodes have multiple modes, so find right one */
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ if ((strncmp (opc->opcode->name, "com",3) == 0)
+ || (strncmp (opc->opcode->name, "mov",3) == 0)
+ || (strncmp (opc->opcode->name, "neg",3) == 0))
+ {
+ /* Special cases for:
+ com RD, RS alias for xnor RD,R0,RS
+ mov RD, RS alias for or RD, R0, RS
+ neg RD, RS alias for sub RD, R0, RS */
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8) | (operands[1].reg1 << 2);
+ }
+ else if ((strncmp (opc->opcode->name, "cmp",3) == 0)
+ || (strncmp (opc->opcode->name, "cpc",3) == 0))
+ {
+ /* special cases for:
+ cmp RS1, RS2 alias for sub R0, RS1, RS2
+ cpc RS1, RS2 alias for sbc R0, RS1, RS2 */
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 5) | (operands[1].reg1 << 2);
+ }
+ else
+ {
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8) | (operands[1].reg1 << 5);
+ }
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ }
+ else
+ {
+ as_bad ("No valid mode found\n");
+ }
+ }
+ else
+ {
+ /* more data */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Missing operand."));
+ return;
+ }
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+ if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r'))
+ {
+ operands[2].reg1 = register_name ();
+ if (operands[2].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+ if (opc->format & M68XG_OP_R_R_R)
+ {
+ operands[0].mode = M68XG_OP_R_R_R;
+
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8) | (operands[1].reg1 << 5)
+ | (operands[2].reg1 << 2);
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ }
+ else
+ {
+ as_bad ("No valid mode found\n");
+ }
+ }
+ }
+ }
+ else if (*input_line_pointer == '(') /* Indexed modes */
+ {
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+ if ((*input_line_pointer == 'R') || (*input_line_pointer == 'r'))
+ {
+ /* we've got as far as OP R, (R */
+ operands[1].reg1 = register_name ();
+ if (operands[1].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+
+ if ((*input_line_pointer == '\n') || (*input_line_pointer == '\r')
+ || (*input_line_pointer == '\0'))
+ {
+ /* Looks like OP_R_R. */
+ as_bad (_("Missing operand."));
+ return;
+ }
+
+ input_line_pointer = skip_whites (input_line_pointer);
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Missing operand."));
+ return;
+ }
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer++;
+ input_line_pointer = skip_whites (input_line_pointer);
+ expression (&operands[0].exp);
+ if (operands[0].exp.X_op == O_illegal)
+ {
+ as_bad (_("Illegal operand."));
+ return;
+ }
+ else if (operands[0].exp.X_op == O_absent)
+ {
+ as_bad (_("Missing operand."));
+ return;
+ }
+
+ input_line_pointer = skip_whites (input_line_pointer);
+ if (*input_line_pointer != ')')
+ {
+ as_bad ("Missing `)' to close register indirect operand.");
+ return;
+ }
+ else
+ {
+ input_line_pointer++;
+ }
+
+ /* Ok so far, can only be one mode. */
+ opcode_local.format = M68XG_OP_R_R_OFFS5;
+ operands[0].mode = M68XG_OP_R_R_OFFS5;
+
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8) | (operands[1].reg1 << 5);
+ if (operands[0].exp.X_op != O_constant)
+ {
+ as_bad
+ ("Only constants supported for indexed OFFS5 mode\n");
+ }
+ else
+ {
+ if (check_range (operands[0].exp.X_add_number,
+ M68XG_OP_R_R_OFFS5))
+ {
+ opcode_local.opcode
+ |= (operands[0].exp.X_add_number);
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ else
+ {
+ as_bad ("Number out of range for OFFS5\n");
+ }
+ }
+ }
+ }
+ else
+ {
+ operands[0].mode = M68XG_OP_RD_RB_RI;
+
+ if (*input_line_pointer == '-')
+ {
+ operands[0].mode = M68XG_OP_RD_RB_mRI;
+ input_line_pointer++;
+ }
+ operands[2].reg1 = register_name ();
+ if (operands[2].reg1 == REG_NONE)
+ {
+ as_bad ("Invalid register\n");
+ return;
+ }
+
+ if (*input_line_pointer == '+')
+ {
+ if (opcode_local.format == M68XG_OP_RD_RB_mRI)
+ {
+ as_bad (_("Illegal operand."));
+ return;
+ }
+ operands[0].mode = M68XG_OP_RD_RB_RIp;
+ input_line_pointer++;
+ }
+
+ input_line_pointer = skip_whites (input_line_pointer);
+ if (*input_line_pointer != ')')
+ {
+ as_bad
+ ("Missing `)' to close register indirect operand.");
+ return;
+ }
+ else
+ {
+ input_line_pointer++;
+ }
+
+ opcode = find (opc, operands, 1);
+ if (opcode)
+ {
+ opcode_local.opcode = opcode->opcode
+ | (operands[0].reg1 << 8) | (operands[1].reg1 << 5)
+ | (operands[2].reg1 << 2);
+ opcode_local.format = M68XG_OP_NONE;
+ build_insn_xg (&opcode_local, operands, 1);
+ }
+ else
+ {
+ as_bad ("Failed to find opcode for %s %s\n",
+ opc->opcode->name, (char *)op_end);
+ }
+ }
+ }
+ }
+ else
+ {
+ as_bad (_("Failed to find a valid mode for `%s'."),
+ opc->opcode->name);
+ }
+
+ if (opc->opcode && !flag_mri)
+ {
+ char *p = input_line_pointer;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
+ p++;
+
+ if (*p != '\n' && *p)
+ as_bad (_("Garbage at end of instruction: `%s'."), p);
+ }
+
+ input_line_pointer = save;
+
+ /* Opcode is known but does not have valid operands. Print out the
+ syntax for this opcode. */
+ if (opc->opcode == 0)
+ {
+ if (flag_print_insn_syntax)
+ print_insn_format (name);
+
+ as_bad (_("Invalid operand for `%s'"), name);
+ return;
+ }
+
+ return;
+ }
+
+ /* Find the opcode definition given its name. */
+ opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, name);
+
+ /* If it's not recognized, look for 'jbsr' and 'jbxx'. These are
+ pseudo insns for relative branch. For these branches, we always
+ optimize them (turned into absolute branches) even if --short-branches
+ is given. */
+ if (opc == NULL && name[0] == 'j' && name[1] == 'b')
+ {
+ opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash, &name[1]);
+ if (opc
+ && (!(opc->format & M6811_OP_JUMP_REL)
+ || (opc->format & M6811_OP_BITMASK)))
+ opc = 0;
+ if (opc)
+ branch_optimize = 1;
+ }
+
+ /* The following test should probably be removed. This does not conform
+ to Motorola assembler specs. */
+ if (opc == NULL && flag_mri)
+ {
+ if (*op_end == ' ' || *op_end == '\t')
+ {
+ while (*op_end == ' ' || *op_end == '\t')
+ op_end++;
+
+ if (nlen < 19
+ && (*op_end &&
+ (is_end_of_line[op_end[1]]
+ || op_end[1] == ' ' || op_end[1] == '\t'
+ || !ISALNUM (op_end[1])))
+ && (*op_end == 'a' || *op_end == 'b'
+ || *op_end == 'A' || *op_end == 'B'
+ || *op_end == 'd' || *op_end == 'D'
+ || *op_end == 'x' || *op_end == 'X'
+ || *op_end == 'y' || *op_end == 'Y'))
+ {
+ name[nlen++] = TOLOWER (*op_end++);
+ name[nlen] = 0;
+ opc = (struct m68hc11_opcode_def *) hash_find (m68hc11_hash,
+ name);
+ }
+ }
+ }
+
+ /* Identify a possible instruction alias. There are some on the
+ 68HC12 to emulate a few 68HC11 instructions. */
+ if (opc == NULL && (current_architecture & cpu6812))
+ {
+ int i;
+
+ for (i = 0; i < m68hc12_num_alias; i++)
+ if (strcmp (m68hc12_alias[i].name, name) == 0)
+ {
+ alias_id = i;
+ break;
+ }
+ }
+ if (opc == NULL && alias_id < 0)
+ {
+ as_bad (_("Opcode `%s' is not recognized."), name);
+ return;
+ }
+ save = input_line_pointer;
+ input_line_pointer = (char *) op_end;
+
+ if (opc)
+ {
+ opc->used++;
+ opcode = find_opcode (opc, operands, &nb_operands);
+ }
+ else
+ opcode = 0;
+
+ if ((opcode || alias_id >= 0) && !flag_mri)
+ {
+ char *p = input_line_pointer;
+
+ while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')
+ p++;
+
+ if (*p != '\n' && *p)
+ as_bad (_("Garbage at end of instruction: `%s'."), p);
+ }
+
+ input_line_pointer = save;
+
+ if (alias_id >= 0)
+ {
+ char *f = m68hc11_new_insn (m68hc12_alias[alias_id].size);
+
+ number_to_chars_bigendian (f, m68hc12_alias[alias_id].code1, 1);
+ if (m68hc12_alias[alias_id].size > 1)
+ number_to_chars_bigendian (f + 1, m68hc12_alias[alias_id].code2, 1);
+
+ return;
+ }
+
+ /* Opcode is known but does not have valid operands. Print out the
+ syntax for this opcode. */
+ if (opcode == 0)
+ {
+ if (flag_print_insn_syntax)
+ print_insn_format (name);
+
+ if (((strcmp (name, "movb") == 0) || (strcmp (name, "movw") == 0))
+ && (current_architecture & cpu9s12x))
+ {
+ char *f;
+ int movb;
+ if (strcmp (name, "movb") == 0)
+ movb = 8;
+ else
+ movb = 0;
+
+ /* The existing operand extract code fell over if these additional modes
+ were enabled in m68hc11-opc.c. So they are commented there and
+ decoded here instead. */
+
+ if (operands[1].mode & (M6812_OP_IDX | M6812_OP_IDX_1
+ | M6812_OP_IDX_2 | M6812_OP_D_IDX | M6812_OP_D_IDX_2 | M6812_PRE_INC
+ | M6812_PRE_DEC | M6812_POST_INC | M6812_POST_DEC ))
+ {
+ /* first check if valid mode then start building it up */
+ if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16
+ | M6811_OP_IND16 | M6812_OP_IDX | M6812_OP_IDX_1
+ | M6812_OP_IDX_2 | M6812_OP_D_IDX | M6812_OP_D_IDX_2))
+ {
+ int opr16a;
+ if (operands[1].mode & (M6811_OP_IND16))
+ opr16a = 3;
+ else
+ opr16a = 0;
+
+ f = m68hc11_new_insn (2);
+
+ if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16))
+ {
+ number_to_chars_bigendian (f, 0x1800 + movb + opr16a, 2);
+ build_indexed_byte (&operands[1], operands[1].mode, 1);
+ if (movb)
+ fixup8 (&operands[0].exp, M6811_OP_IMM8,
+ operands[0].mode);
+ else
+ fixup16 (&operands[0].exp, M6811_OP_IMM16,
+ operands[0].mode);
+
+ return;
+ }
+ else if (operands[0].mode & M6811_OP_IND16)
+ {
+ number_to_chars_bigendian (f, 0x1801 + movb + opr16a, 2);
+ build_indexed_byte (&operands[1], operands[1].mode, 1);
+ fixup16 (&operands[0].exp, M6811_OP_IND16, operands[0].mode);
+ return;
+ }
+ else
+ {
+ number_to_chars_bigendian (f, 0x1802 + movb + opr16a, 2);
+ build_indexed_byte (&operands[0], operands[0].mode, 1);
+ build_indexed_byte (&operands[1], operands[1].mode, 1);
+ return;
+ }
+ }
+ }
+ else if (operands[1].mode & M6811_OP_IND16)
+ {
+ /* First check if this is valid mode, then start building it up. */
+ if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16
+ | M6811_OP_IND16 | M6812_OP_IDX | M6812_OP_IDX_1
+ | M6812_OP_IDX_2 | M6812_OP_D_IDX | M6812_OP_D_IDX_2))
+ {
+ int opr16a;
+ if (operands[1].mode & (M6811_OP_IND16))
+ opr16a = 3;
+ else
+ opr16a = 0;
+
+ f = m68hc11_new_insn (2);
+
+ /* The first two cases here should actually be covered by the
+ normal operand code. */
+ if (operands[0].mode & (M6811_OP_IMM8 | M6811_OP_IMM16))
+ {
+ number_to_chars_bigendian (f, 0x1800 + movb + opr16a, 2);
+ if (movb)
+ fixup8 (&operands[0].exp, M6811_OP_IMM8, operands[0].mode);
+ else
+ fixup16 (&operands[0].exp, M6811_OP_IMM16, operands[0].mode);
+
+ fixup16 (&operands[0].exp, M6811_OP_IND16, operands[0].mode);
+ return;
+ }
+ else if (operands[0].mode & M6811_OP_IND16)
+ {
+ number_to_chars_bigendian (f, 0x1801 + movb + opr16a, 2);
+ build_indexed_byte (&operands[1], operands[1].mode, 1);
+ fixup16 (&operands[0].exp, M6811_OP_IND16, operands[0].mode);
+ return;
+ }
+ else
+ {
+ number_to_chars_bigendian (f, 0x1802 + movb + opr16a, 2);
+ build_indexed_byte (&operands[0], operands[0].mode, 1);
+ fixup16 (&operands[1].exp, M6811_OP_IND16, operands[1].mode);
+ return;
+ }
+ }
+ }
+
+ as_bad (_("Invalid operand for `%s'"), name);
+ return;
+
+ }
+ else
+ {
+ as_bad (_("Invalid operand for `%s'"), name);
+ return;
+ }
+ }
+
+ /* Treat dbeq/ibeq/tbeq instructions in a special way. The branch is
+ relative and must be in the range -256..255 (9-bits). */
+ if ((opcode->format & M6812_XBCC_MARKER)
+ && (opcode->format & M6811_OP_JUMP_REL))
+ build_dbranch_insn (opcode, operands, nb_operands, branch_optimize);
+
+ /* Relative jumps instructions are taken care of separately. We have to make
+ sure that the relative branch is within the range -128..127. If it's out
+ of range, the instructions are changed into absolute instructions.
+ This is not supported for the brset and brclr instructions. */
+ else if ((opcode->format & (M6811_OP_JUMP_REL | M6812_OP_JUMP_REL16))
+ && !(opcode->format & M6811_OP_BITMASK))
+ build_jump_insn (opcode, operands, nb_operands, branch_optimize);
+ else
+ build_insn (opcode, operands, nb_operands);
+}
+
+
+/* Pseudo op to control the ELF flags. */
+static void
+s_m68hc11_mode (int x ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (strcmp (name, "mshort") == 0)
+ {
+ elf_flags &= ~E_M68HC11_I32;
+ }
+ else if (strcmp (name, "mlong") == 0)
+ {
+ elf_flags |= E_M68HC11_I32;
+ }
+ else if (strcmp (name, "mshort-double") == 0)
+ {
+ elf_flags &= ~E_M68HC11_F64;
+ }
+ else if (strcmp (name, "mlong-double") == 0)
+ {
+ elf_flags |= E_M68HC11_F64;
+ }
+ else
+ {
+ as_warn (_("Invalid mode: %s\n"), name);
+ }
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Mark the symbols with STO_M68HC12_FAR to indicate the functions
+ are using 'rtc' for returning. It is necessary to use 'call'
+ to invoke them. This is also used by the debugger to correctly
+ find the stack frame. */
+static void
+s_m68hc11_mark_symbol (int mark)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ bfdsym = symbol_get_bfdsym (symbolP);
+ elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+
+ gas_assert (elfsym);
+
+ /* Mark the symbol far (using rtc for function return). */
+ elfsym->internal_elf_sym.st_other |= mark;
+
+ if (c == ',')
+ {
+ input_line_pointer ++;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_m68hc11_relax (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("bad .relax format"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ fix_new_exp (frag_now, frag_now_fix (), 0, &ex, 1,
+ BFD_RELOC_M68HC11_RL_GROUP);
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Relocation, relaxation and frag conversions. */
+
+/* PC-relative offsets are relative to the start of the
+ next instruction. That is, the address of the offset, plus its
+ size, since the offset is always the last part of the insn. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_M68HC11_RL_JUMP)
+ return 0;
+
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* If while processing a fixup, a reloc really needs to be created
+ then it is done here. */
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ if (fixp->fx_r_type == 0)
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
+ else
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Relocation %d is not supported by object file format."),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ /* Since we use Rel instead of Rela, encode the vtable entry to be
+ used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ reloc->addend = 0;
+ return reloc;
+}
+
+/* We need a port-specific relaxation function to cope with sym2 - sym1
+ relative expressions with both symbols in the same segment (but not
+ necessarily in the same frag as this insn), for example:
+ ldab sym2-(sym1-2),pc
+ sym1:
+ The offset can be 5, 9 or 16 bits long. */
+
+long
+m68hc11_relax_frag (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;
+
+ /* We only have to cope with frags as prepared by
+ md_estimate_size_before_relax. The STATE_BITS16 case may geet here
+ because of the different reasons that it's not relaxable. */
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS16):
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
+ /* When we get to this state, the frag won't grow any more. */
+ return 0;
+
+ case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS5):
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+ case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS9):
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+ 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;
+
+ default:
+ as_fatal (_("internal inconsistency problem in %s: fr_subtype %d"),
+ __FUNCTION__, fragP->fr_subtype);
+ }
+
+ /* The rest is stolen from relax_frag. There's no obvious way to
+ share the code, but fortunately no requirement to keep in sync as
+ long as fragP->fr_symbol does not have its segment changed. */
+
+ this_state = fragP->fr_subtype;
+ start_type = this_type = table + this_state;
+
+ if (aim < 0)
+ {
+ /* Look backwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim >= this_type->rlx_backward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+ else
+ {
+ /* Look forwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim <= this_type->rlx_forward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ 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;
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ long value;
+ long disp;
+ char *buffer_address = fragP->fr_literal;
+
+ /* Address in object code of the displacement. */
+ register int object_address = fragP->fr_fix + fragP->fr_address;
+
+ buffer_address += fragP->fr_fix;
+
+ /* The displacement of the address, from current location. */
+ value = S_GET_VALUE (fragP->fr_symbol);
+ disp = (value + fragP->fr_offset) - object_address;
+
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE):
+ fragP->fr_opcode[1] = disp;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD):
+ /* This relax is only for bsr and bra. */
+ gas_assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+ || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA)
+ || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR));
+
+ fragP->fr_opcode[0] = convert_branch (fragP->fr_opcode[0]);
+
+ fix_new (fragP, fragP->fr_fix - 1, 2,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 1;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_BYTE):
+ fragP->fr_opcode[1] = disp;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD):
+ /* Invert branch. */
+ fragP->fr_opcode[0] ^= 1;
+ fragP->fr_opcode[1] = 3; /* Branch offset. */
+ buffer_address[0] = M6811_JMP;
+ fix_new (fragP, fragP->fr_fix + 1, 2,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 3;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812, STATE_WORD):
+ /* Translate branch into a long branch. */
+ fragP->fr_opcode[1] = fragP->fr_opcode[0];
+ fragP->fr_opcode[0] = M6811_OPCODE_PAGE2;
+
+ fix_new (fragP, fragP->fr_fix, 2,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_16_PCREL);
+ fragP->fr_fix += 2;
+ break;
+
+ case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS5):
+ if (fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ value = disp;
+ /* fall through */
+
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS5):
+ fragP->fr_opcode[0] = fragP->fr_opcode[0] << 6;
+ fragP->fr_opcode[0] |= value & 0x1f;
+ break;
+
+ case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS9):
+ /* For a PC-relative offset, use the displacement with a -1 correction
+ to take into account the additional byte of the insn. */
+ if (fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ value = disp - 1;
+ /* fall through */
+
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS9):
+ fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
+ fragP->fr_opcode[0] |= 0xE0;
+ fragP->fr_opcode[0] |= (value >> 8) & 1;
+ fragP->fr_opcode[1] = value;
+ fragP->fr_fix += 1;
+ break;
+
+ case ENCODE_RELAX (STATE_INDEXED_PCREL, STATE_BITS16):
+ case ENCODE_RELAX (STATE_INDEXED_OFFSET, STATE_BITS16):
+ fragP->fr_opcode[0] = (fragP->fr_opcode[0] << 3);
+ fragP->fr_opcode[0] |= 0xe2;
+ if ((fragP->fr_opcode[0] & 0x0ff) == 0x0fa
+ && fragP->fr_symbol != 0
+ && S_GET_SEGMENT (fragP->fr_symbol) != absolute_section)
+ {
+ fix_new (fragP, fragP->fr_fix, 2,
+ fragP->fr_symbol, fragP->fr_offset,
+ 1, BFD_RELOC_16_PCREL);
+ }
+ else
+ {
+ fix_new (fragP, fragP->fr_fix, 2,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+ }
+ fragP->fr_fix += 2;
+ break;
+
+ case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE):
+ if (disp < 0)
+ fragP->fr_opcode[0] |= 0x10;
+
+ fragP->fr_opcode[1] = disp & 0x0FF;
+ break;
+
+ case ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_WORD):
+ /* Invert branch. */
+ fragP->fr_opcode[0] ^= 0x20;
+ fragP->fr_opcode[1] = 3; /* Branch offset. */
+ buffer_address[0] = M6812_JMP;
+ fix_new (fragP, fragP->fr_fix + 1, 2,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 3;
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* On an ELF system, we can't relax a weak symbol. The weak symbol
+ can be overridden at final link time by a non weak symbol. We can
+ relax externally visible symbol because there is no shared library
+ and such symbol can't be overridden (unless they are weak). */
+static int
+relaxable_symbol (symbolS *symbol)
+{
+ return ! S_IS_WEAK (symbol);
+}
+
+/* Force truly undefined symbols to their maximum size, and generally set up
+ the frag list to be relaxed. */
+int
+md_estimate_size_before_relax (fragS *fragP, asection *segment)
+{
+ if (RELAX_LENGTH (fragP->fr_subtype) == STATE_UNDF)
+ {
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment
+ || !relaxable_symbol (fragP->fr_symbol)
+ || (segment != absolute_section
+ && RELAX_STATE (fragP->fr_subtype) == STATE_INDEXED_OFFSET))
+ {
+ /* Non-relaxable cases. */
+ int old_fr_fix;
+ char *buffer_address;
+
+ old_fr_fix = fragP->fr_fix;
+ buffer_address = fragP->fr_fix + fragP->fr_literal;
+
+ switch (RELAX_STATE (fragP->fr_subtype))
+ {
+ case STATE_PC_RELATIVE:
+
+ /* This relax is only for bsr and bra. */
+ gas_assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+ || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA)
+ || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR));
+
+ if (flag_fixed_branches)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("bra or bsr with undefined symbol."));
+
+ /* The symbol is undefined or in a separate section.
+ Turn bra into a jmp and bsr into a jsr. The insn
+ becomes 3 bytes long (instead of 2). A fixup is
+ necessary for the unresolved symbol address. */
+ fragP->fr_opcode[0] = convert_branch (fragP->fr_opcode[0]);
+
+ fix_new (fragP, fragP->fr_fix - 1, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix++;
+ break;
+
+ case STATE_CONDITIONAL_BRANCH:
+ gas_assert (current_architecture & cpu6811);
+
+ fragP->fr_opcode[0] ^= 1; /* Reverse sense of branch. */
+ fragP->fr_opcode[1] = 3; /* Skip next jmp insn (3 bytes). */
+
+ /* Don't use fr_opcode[2] because this may be
+ in a different frag. */
+ buffer_address[0] = M6811_JMP;
+
+ fragP->fr_fix++;
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 2;
+ break;
+
+ case STATE_INDEXED_OFFSET:
+ gas_assert (current_architecture & cpu6812);
+
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET,
+ STATE_BITS5);
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+ }
+ else
+ {
+ /* Switch the indexed operation to 16-bit mode. */
+ fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
+ fragP->fr_opcode[0] |= 0xe2;
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 2;
+ }
+ break;
+
+ case STATE_INDEXED_PCREL:
+ gas_assert (current_architecture & cpu6812);
+
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == absolute_section)
+ {
+ fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_PCREL,
+ STATE_BITS5);
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+ }
+ else
+ {
+ fragP->fr_opcode[0] = fragP->fr_opcode[0] << 3;
+ fragP->fr_opcode[0] |= 0xe2;
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_fix += 2;
+ }
+ break;
+
+ case STATE_XBCC_BRANCH:
+ gas_assert (current_architecture & cpu6812);
+
+ fragP->fr_opcode[0] ^= 0x20; /* Reverse sense of branch. */
+ fragP->fr_opcode[1] = 3; /* Skip next jmp insn (3 bytes). */
+
+ /* Don't use fr_opcode[2] because this may be
+ in a different frag. */
+ buffer_address[0] = M6812_JMP;
+
+ fragP->fr_fix++;
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 0, BFD_RELOC_16);
+ fragP->fr_fix += 2;
+ break;
+
+ case STATE_CONDITIONAL_BRANCH_6812:
+ gas_assert (current_architecture & cpu6812);
+
+ /* Translate into a lbcc branch. */
+ fragP->fr_opcode[1] = fragP->fr_opcode[0];
+ fragP->fr_opcode[0] = M6811_OPCODE_PAGE2;
+
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_fix += 2;
+ break;
+
+ default:
+ as_fatal (_("Subtype %d is not recognized."), fragP->fr_subtype);
+ }
+ frag_wane (fragP);
+
+ /* Return the growth in the fixed part of the frag. */
+ return fragP->fr_fix - old_fr_fix;
+ }
+
+ /* Relaxable cases. */
+ switch (RELAX_STATE (fragP->fr_subtype))
+ {
+ case STATE_PC_RELATIVE:
+ /* This relax is only for bsr and bra. */
+ gas_assert (IS_OPCODE (fragP->fr_opcode[0], M6811_BSR)
+ || IS_OPCODE (fragP->fr_opcode[0], M6811_BRA)
+ || IS_OPCODE (fragP->fr_opcode[0], M6812_BSR));
+
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE);
+ break;
+
+ case STATE_CONDITIONAL_BRANCH:
+ gas_assert (current_architecture & cpu6811);
+
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH,
+ STATE_BYTE);
+ break;
+
+ case STATE_INDEXED_OFFSET:
+ gas_assert (current_architecture & cpu6812);
+
+ fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_OFFSET,
+ STATE_BITS5);
+ break;
+
+ case STATE_INDEXED_PCREL:
+ gas_assert (current_architecture & cpu6812);
+
+ fragP->fr_subtype = ENCODE_RELAX (STATE_INDEXED_PCREL,
+ STATE_BITS5);
+ break;
+
+ case STATE_XBCC_BRANCH:
+ gas_assert (current_architecture & cpu6812);
+
+ fragP->fr_subtype = ENCODE_RELAX (STATE_XBCC_BRANCH, STATE_BYTE);
+ break;
+
+ case STATE_CONDITIONAL_BRANCH_6812:
+ gas_assert (current_architecture & cpu6812);
+
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH_6812,
+ STATE_BYTE);
+ break;
+ }
+ }
+
+ if (fragP->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ as_fatal (_("Subtype %d is not recognized."), fragP->fr_subtype);
+
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* See whether we need to force a relocation into the output file. */
+int
+tc_m68hc11_force_relocation (fixS *fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_M68HC11_RL_GROUP)
+ return 1;
+
+ return generic_force_reloc (fixP);
+}
+
+/* Here we decide which fixups can be adjusted to make them relative
+ to the beginning of the section instead of the symbol. Basically
+ we need to make sure that the linker relaxation is done
+ correctly, so in some cases we force the original symbol to be
+ used. */
+int
+tc_m68hc11_fix_adjustable (fixS *fixP)
+{
+ switch (fixP->fx_r_type)
+ {
+ /* For the linker relaxation to work correctly, these relocs
+ need to be on the symbol itself. */
+ case BFD_RELOC_16:
+ case BFD_RELOC_M68HC11_RL_JUMP:
+ case BFD_RELOC_M68HC11_RL_GROUP:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_32:
+
+ /* The memory bank addressing translation also needs the original
+ symbol. */
+ case BFD_RELOC_M68HC11_LO16:
+ case BFD_RELOC_M68HC11_PAGE:
+ case BFD_RELOC_M68HC11_24:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where;
+ long value = * valP;
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ /* We don't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("Expression too complex."));
+
+ /* Patch the instruction with the resolved operand. Elf relocation
+ info will also be generated to take care of linker/loader fixups.
+ The 68HC11 addresses only 64Kb, we are only concerned by 8 and 16-bit
+ relocs. BFD_RELOC_8 is basically used for .page0 access (the linker
+ will warn for overflows). BFD_RELOC_8_PCREL should not be generated
+ because it's either resolved or turned out into non-relative insns (see
+ relax table, bcc, bra, bsr transformations)
+
+ The BFD_RELOC_32 is necessary for the support of --gstabs. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_24:
+ case BFD_RELOC_M68HC11_24:
+ bfd_putb16 ((bfd_vma) (value & 0x0ffff), (unsigned char *) where);
+ ((bfd_byte*) where)[2] = ((value >> 16) & 0x0ff);
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_M68HC11_LO16:
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ if (value < -65537 || value > 65535)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value out of 16-bit range."));
+ break;
+
+ case BFD_RELOC_M68HC11_HI8:
+ /* Caution, %hi(<symbol>+%ld) will generate incorrect code if %lo
+ causes a carry. */
+ case BFD_RELOC_M68HC12_HI8XG:
+ value = value >> 8;
+ /* Fall through. */
+
+ case BFD_RELOC_M68HC12_LO8XG:
+ case BFD_RELOC_M68HC11_LO8:
+ case BFD_RELOC_8:
+ case BFD_RELOC_M68HC11_PAGE:
+ ((bfd_byte *) where)[0] = (bfd_byte) value;
+ break;
+
+ case BFD_RELOC_8_PCREL:
+ ((bfd_byte *) where)[0] = (bfd_byte) value;
+
+ if (value < -128 || value > 127)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value %ld too large for 8-bit PC-relative branch."),
+ value);
+ break;
+
+ /* These next two are for XGATE. */
+ case BFD_RELOC_M68HC12_9_PCREL:
+ ((bfd_byte *) where)[0] |= (bfd_byte) ((value >>9) & 0x01);
+ ((bfd_byte *) where)[1] = (bfd_byte) ((value>>1) & 0xff);
+ if (value < -512 || value > 511)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value %ld too large for 9-bit PC-relative branch."),
+ value);
+ break;
+
+ case BFD_RELOC_M68HC12_10_PCREL:
+ ((bfd_byte *) where)[0] |= (bfd_byte) ((value >>9) & 0x03);
+ ((bfd_byte *) where)[1] = (bfd_byte) ((value>>1) & 0xff);
+ if (value < -1024 || value > 1023)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value %ld too large for 10-bit PC-relative branch."),
+ value);
+
+ break;
+
+ case BFD_RELOC_M68HC11_3B:
+ if (value <= 0 || value > 8)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Auto increment/decrement offset '%ld' is out of range."),
+ value);
+ if (where[0] & 0x8)
+ value = 8 - value;
+ else
+ value--;
+
+ where[0] = where[0] | (value & 0x07);
+ break;
+
+ case BFD_RELOC_M68HC12_5B:
+ if (value < -16 || value > 15)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Offset out of 5-bit range for movw/movb insn: %ld"),
+ value);
+ if (value >= 0)
+ where[0] |= value;
+ else
+ where[0] |= (0x10 | (16 + value));
+ break;
+
+ case BFD_RELOC_M68HC12_9B:
+ if (value < -256 || value > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Offset out of 9-bit range for movw/movb insn: %ld"),
+ value);
+ /* sign bit already in xb postbyte */
+ if (value >= 0)
+ where[1] = value;
+ else
+ where[1] = (256 + value);
+ break;
+
+ case BFD_RELOC_M68HC12_16B:
+ if (value < -32768 || value > 32767)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Offset out of 16-bit range for movw/movb insn: %ld"),
+ value);
+ if (value < 0)
+ value += 65536;
+
+ where[0] = (value >> 8);
+ where[1] = (value & 0xff);
+ break;
+
+ case BFD_RELOC_M68HC11_RL_JUMP:
+ case BFD_RELOC_M68HC11_RL_GROUP:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ default:
+ as_fatal (_("Line %d: unknown relocation type: 0x%x."),
+ fixP->fx_line, fixP->fx_r_type);
+ }
+}
+
+/* Set the ELF specific flags. */
+void
+m68hc11_elf_final_processing (void)
+{
+ if (current_architecture & cpu6812s)
+ elf_flags |= EF_M68HCS12_MACH;
+ elf_elfheader (stdoutput)->e_flags &= ~EF_M68HC11_ABI;
+ elf_elfheader (stdoutput)->e_flags |= elf_flags;
+}
+
+/* Process directives specified via pseudo ops */
+static void
+s_m68hc11_parse_pseudo_instruction (int pseudo_insn)
+{
+ switch (pseudo_insn)
+ {
+ case E_M68HC11_NO_BANK_WARNING:
+ elf_flags |= E_M68HC11_NO_BANK_WARNING;
+ break;
+ default:
+ as_bad (_("Invalid directive"));
+ }
+}
diff --git a/gas/config/tc-m68hc11.h b/gas/config/tc-m68hc11.h
new file mode 100644
index 0000000..59079dd
--- /dev/null
+++ b/gas/config/tc-m68hc11.h
@@ -0,0 +1,108 @@
+/* tc-m68hc11.h -- Header file for tc-m68hc11.c.
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_M68HC11
+#define TC_M68HC12
+
+struct fix;
+
+/* Define TC_M68K so that we can use the MRI mode. */
+#define TC_M68K
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Motorola assembler specs does not require '.' before pseudo-ops. */
+#define NO_PSEUDO_DOT 1
+
+/* The target BFD architecture. */
+#define TARGET_ARCH (m68hc11_arch ())
+extern enum bfd_architecture m68hc11_arch (void);
+
+#define TARGET_MACH (m68hc11_mach ())
+extern int m68hc11_mach (void);
+
+#define TARGET_FORMAT (m68hc11_arch_format ())
+extern const char *m68hc11_arch_format (void);
+
+#define LISTING_WORD_SIZE 1 /* A word is 1 bytes */
+#define LISTING_LHS_WIDTH 4 /* One word on the first line */
+#define LISTING_LHS_WIDTH_SECOND 4 /* One word on the second line */
+#define LISTING_LHS_CONT_LINES 4 /* And 4 lines max */
+#define LISTING_HEADER m68hc11_listing_header ()
+extern const char *m68hc11_listing_header (void);
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define tc_init_after_args m68hc11_init_after_args
+extern void m68hc11_init_after_args (void);
+
+#define md_parse_long_option m68hc11_parse_long_option
+extern int m68hc11_parse_long_option (char *);
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+/* Use 32-bit address to represent a symbol address so that we can
+ represent them with their page number. */
+#define DWARF2_ADDR_SIZE(bfd) 4
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+/* Relax table to translate short relative branches (-128..127) into
+ absolute branches. */
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+extern struct relax_type md_relax_table[];
+
+/* GAS only handles relaxations for pc-relative data targeting addresses
+ in the same segment, so we have to handle the rest on our own. */
+#define md_relax_frag(SEG, FRAGP, STRETCH) \
+ ((FRAGP)->fr_symbol != NULL \
+ && S_GET_SEGMENT ((FRAGP)->fr_symbol) == (SEG) \
+ ? relax_frag (SEG, FRAGP, STRETCH) \
+ : m68hc11_relax_frag (SEG, FRAGP, STRETCH))
+extern long m68hc11_relax_frag (segT, fragS*, long);
+
+#define TC_HANDLES_FX_DONE
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+#define TC_FORCE_RELOCATION(fix) tc_m68hc11_force_relocation (fix)
+extern int tc_m68hc11_force_relocation (struct fix *);
+
+#define tc_fix_adjustable(X) tc_m68hc11_fix_adjustable(X)
+extern int tc_m68hc11_fix_adjustable (struct fix *);
+
+#define md_operand(x)
+
+#define elf_tc_final_processing m68hc11_elf_final_processing
+extern void m68hc11_elf_final_processing (void);
+
+#define tc_print_statistics(FILE) m68hc11_print_statistics (FILE)
+extern void m68hc11_print_statistics (FILE *);
diff --git a/gas/config/tc-m68k.c b/gas/config/tc-m68k.c
new file mode 100644
index 0000000..7fc4efe
--- /dev/null
+++ b/gas/config/tc-m68k.c
@@ -0,0 +1,8144 @@
+/* tc-m68k.c -- Assemble for the m68k family
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "obstack.h"
+#include "subsegs.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+#include "opcode/m68k.h"
+#include "m68k-parse.h"
+
+#if defined (OBJ_ELF)
+#include "elf/m68k.h"
+#endif
+
+#ifdef M68KCOFF
+#include "obj-coff.h"
+#endif
+
+#ifdef OBJ_ELF
+static void m68k_elf_cons (int);
+#endif
+
+/* This string holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The macro
+ tc_comment_chars points to this. We use this, rather than the
+ usual comment_chars, so that the --bitwise-or option will work. */
+#if defined (TE_SVR4) || defined (TE_DELTA)
+const char *m68k_comment_chars = "|#";
+#else
+const char *m68k_comment_chars = "|";
+#endif
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#*";
+
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant, as
+ in "0f12.456" or "0d1.2345e12". */
+
+const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+/* Are we trying to generate PIC code? If so, absolute references
+ ought to be made into linkage table references or pc-relative
+ references. Not implemented. For ELF there are other means
+ to denote pic relocations. */
+int flag_want_pic;
+
+static int flag_short_refs; /* -l option. */
+static int flag_long_jumps; /* -S option. */
+static int flag_keep_pcrel; /* --pcrel option. */
+
+#ifdef REGISTER_PREFIX_OPTIONAL
+int flag_reg_prefix_optional = REGISTER_PREFIX_OPTIONAL;
+#else
+int flag_reg_prefix_optional;
+#endif
+
+/* Whether --register-prefix-optional was used on the command line. */
+static int reg_prefix_optional_seen;
+
+/* The floating point coprocessor to use by default. */
+static enum m68k_register m68k_float_copnum = COP1;
+
+/* If this is non-zero, then references to number(%pc) will be taken
+ to refer to number, rather than to %pc + number. */
+static int m68k_abspcadd;
+
+/* If this is non-zero, then the quick forms of the move, add, and sub
+ instructions are used when possible. */
+static int m68k_quick = 1;
+
+/* If this is non-zero, then if the size is not specified for a base
+ or outer displacement, the assembler assumes that the size should
+ be 32 bits. */
+static int m68k_rel32 = 1;
+
+/* This is non-zero if m68k_rel32 was set from the command line. */
+static int m68k_rel32_from_cmdline;
+
+/* The default width to use for an index register when using a base
+ displacement. */
+static enum m68k_size m68k_index_width_default = SIZE_LONG;
+
+/* We want to warn if any text labels are misaligned. In order to get
+ the right line number, we need to record the line number for each
+ label. */
+struct label_line
+{
+ struct label_line *next;
+ symbolS *label;
+ char *file;
+ unsigned int line;
+ int text;
+};
+
+/* The list of labels. */
+
+static struct label_line *labels;
+
+/* The current label. */
+
+static struct label_line *current_label;
+
+/* Pointer to list holding the opcodes sorted by name. */
+static struct m68k_opcode const ** m68k_sorted_opcodes;
+
+/* Its an arbitrary name: This means I don't approve of it.
+ See flames below. */
+static struct obstack robyn;
+
+struct m68k_incant
+ {
+ const char *m_operands;
+ unsigned long m_opcode;
+ short m_opnum;
+ short m_codenum;
+ int m_arch;
+ struct m68k_incant *m_next;
+ };
+
+#define getone(x) ((((x)->m_opcode)>>16)&0xffff)
+#define gettwo(x) (((x)->m_opcode)&0xffff)
+
+static const enum m68k_register m68000_ctrl[] = { 0 };
+static const enum m68k_register m68010_ctrl[] = {
+ SFC, DFC, USP, VBR,
+ 0
+};
+static const enum m68k_register m68020_ctrl[] = {
+ SFC, DFC, USP, VBR, CACR, CAAR, MSP, ISP,
+ 0
+};
+static const enum m68k_register m68040_ctrl[] = {
+ SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1,
+ USP, VBR, MSP, ISP, MMUSR, URP, SRP,
+ 0
+};
+static const enum m68k_register m68060_ctrl[] = {
+ SFC, DFC, CACR, TC, ITT0, ITT1, DTT0, DTT1, BUSCR,
+ USP, VBR, URP, SRP, PCR,
+ 0
+};
+static const enum m68k_register mcf_ctrl[] = {
+ CACR, TC, ACR0, ACR1, ACR2, ACR3, VBR, ROMBAR,
+ RAMBAR0, RAMBAR1, RAMBAR, MBAR,
+ 0
+};
+static const enum m68k_register mcf51_ctrl[] = {
+ VBR, CPUCR,
+ 0
+};
+static const enum m68k_register mcf5206_ctrl[] = {
+ CACR, ACR0, ACR1, VBR, RAMBAR0, RAMBAR_ALT, MBAR,
+ 0
+};
+static const enum m68k_register mcf5208_ctrl[] = {
+ CACR, ACR0, ACR1, VBR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5210a_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, ROMBAR, RAMBAR, RAMBAR1, MBAR,
+ 0
+};
+static const enum m68k_register mcf5213_ctrl[] = {
+ VBR, RAMBAR, RAMBAR1, FLASHBAR,
+ 0
+};
+static const enum m68k_register mcf5216_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, FLASHBAR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5221x_ctrl[] = {
+ VBR, FLASHBAR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf52223_ctrl[] = {
+ VBR, FLASHBAR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf52235_ctrl[] = {
+ VBR, FLASHBAR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5225_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, FLASHBAR, RAMBAR, MBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf52259_ctrl[] = {
+ VBR, FLASHBAR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf52277_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5235_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5249_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR0, RAMBAR1, RAMBAR, MBAR, MBAR2,
+ 0
+};
+static const enum m68k_register mcf5250_ctrl[] = {
+ VBR,
+ 0
+};
+static const enum m68k_register mcf5253_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR0, RAMBAR1, RAMBAR, MBAR, MBAR2,
+ 0
+};
+static const enum m68k_register mcf5271_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5272_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, ROMBAR, RAMBAR_ALT, RAMBAR0, MBAR,
+ 0
+};
+static const enum m68k_register mcf5275_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5282_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, FLASHBAR, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf53017_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5307_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR0, RAMBAR_ALT, MBAR,
+ 0
+};
+static const enum m68k_register mcf5329_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcf5373_ctrl[] = {
+ VBR, CACR, ACR0, ACR1, RAMBAR, RAMBAR1,
+ 0
+};
+static const enum m68k_register mcfv4e_ctrl[] = {
+ CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR,
+ VBR, PC, ROMBAR0, ROMBAR1, RAMBAR0, RAMBAR1,
+ MBAR, SECMBAR,
+ MPCR /* Multiprocessor Control register */,
+ EDRAMBAR /* Embedded DRAM Base Address Register */,
+ /* Permutation control registers. */
+ PCR1U0, PCR1L0, PCR1U1, PCR1L1, PCR2U0, PCR2L0, PCR2U1, PCR2L1,
+ PCR3U0, PCR3L0, PCR3U1, PCR3L1,
+ /* Legacy names */
+ TC /* ASID */, BUSCR /* MMUBAR */,
+ ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */,
+ MBAR1 /* MBAR */, MBAR2 /* SECMBAR */, MBAR0 /* SECMBAR */,
+ ROMBAR /* ROMBAR0 */, RAMBAR /* RAMBAR1 */,
+ 0
+};
+static const enum m68k_register mcf5407_ctrl[] = {
+ CACR, ASID, ACR0, ACR1, ACR2, ACR3,
+ VBR, PC, RAMBAR0, RAMBAR1, MBAR,
+ /* Legacy names */
+ TC /* ASID */,
+ ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */,
+ MBAR1 /* MBAR */, RAMBAR /* RAMBAR1 */,
+ 0
+};
+static const enum m68k_register mcf54418_ctrl[] = {
+ CACR, ASID, ACR0, ACR1, ACR2, ACR3, ACR4, ACR5, ACR6, ACR7, MMUBAR, RGPIOBAR,
+ VBR, PC, RAMBAR1,
+ /* Legacy names */
+ TC /* ASID */, BUSCR /* MMUBAR */,
+ ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */,
+ RAMBAR /* RAMBAR1 */,
+ 0
+};
+static const enum m68k_register mcf54455_ctrl[] = {
+ CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR,
+ VBR, PC, RAMBAR1,
+ /* Legacy names */
+ TC /* ASID */, BUSCR /* MMUBAR */,
+ ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */,
+ RAMBAR /* RAMBAR1 */,
+ 0
+};
+static const enum m68k_register mcf5475_ctrl[] = {
+ CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR,
+ VBR, PC, RAMBAR0, RAMBAR1, MBAR,
+ /* Legacy names */
+ TC /* ASID */, BUSCR /* MMUBAR */,
+ ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */,
+ MBAR1 /* MBAR */, RAMBAR /* RAMBAR1 */,
+ 0
+};
+static const enum m68k_register mcf5485_ctrl[] = {
+ CACR, ASID, ACR0, ACR1, ACR2, ACR3, MMUBAR,
+ VBR, PC, RAMBAR0, RAMBAR1, MBAR,
+ /* Legacy names */
+ TC /* ASID */, BUSCR /* MMUBAR */,
+ ITT0 /* ACR0 */, ITT1 /* ACR1 */, DTT0 /* ACR2 */, DTT1 /* ACR3 */,
+ MBAR1 /* MBAR */, RAMBAR /* RAMBAR1 */,
+ 0
+};
+static const enum m68k_register fido_ctrl[] = {
+ SFC, DFC, USP, VBR, CAC, MBO,
+ 0
+};
+#define cpu32_ctrl m68010_ctrl
+
+static const enum m68k_register *control_regs;
+
+/* Internal form of a 68020 instruction. */
+struct m68k_it
+{
+ const char *error;
+ const char *args; /* List of opcode info. */
+ int numargs;
+
+ int numo; /* Number of shorts in opcode. */
+ short opcode[11];
+
+ struct m68k_op operands[6];
+
+ int nexp; /* Number of exprs in use. */
+ struct m68k_exp exprs[4];
+
+ int nfrag; /* Number of frags we have to produce. */
+ struct
+ {
+ int fragoff; /* Where in the current opcode the frag ends. */
+ symbolS *fadd;
+ offsetT foff;
+ int fragty;
+ }
+ fragb[4];
+
+ int nrel; /* Num of reloc strucs in use. */
+ struct
+ {
+ int n;
+ expressionS exp;
+ char wid;
+ char pcrel;
+ /* In a pc relative address the difference between the address
+ of the offset and the address that the offset is relative
+ to. This depends on the addressing mode. Basically this
+ is the value to put in the offset field to address the
+ first byte of the offset, without regarding the special
+ significance of some values (in the branch instruction, for
+ example). */
+ int pcrel_fix;
+#ifdef OBJ_ELF
+ /* Whether this expression needs special pic relocation, and if
+ so, which. */
+ enum pic_relocation pic_reloc;
+#endif
+ }
+ reloc[5]; /* Five is enough??? */
+};
+
+#define cpu_of_arch(x) ((x) & (m68000up | mcfisa_a | fido_a))
+#define float_of_arch(x) ((x) & mfloat)
+#define mmu_of_arch(x) ((x) & mmmu)
+#define arch_coldfire_p(x) ((x) & mcfisa_a)
+#define arch_coldfire_fpu(x) ((x) & cfloat)
+
+/* Macros for determining if cpu supports a specific addressing mode. */
+#define HAVE_LONG_DISP(x) \
+ ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b|mcfisa_c))
+#define HAVE_LONG_CALL(x) \
+ ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b|mcfisa_c))
+#define HAVE_LONG_COND(x) \
+ ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b|mcfisa_c))
+#define HAVE_LONG_BRANCH(x) \
+ ((x) & (m68020|m68030|m68040|m68060|cpu32|fido_a|mcfisa_b))
+#define LONG_BRANCH_VIA_COND(x) (HAVE_LONG_COND(x) && !HAVE_LONG_BRANCH(x))
+
+static struct m68k_it the_ins; /* The instruction being assembled. */
+
+#define op(ex) ((ex)->exp.X_op)
+#define adds(ex) ((ex)->exp.X_add_symbol)
+#define subs(ex) ((ex)->exp.X_op_symbol)
+#define offs(ex) ((ex)->exp.X_add_number)
+
+/* Macros for adding things to the m68k_it struct. */
+#define addword(w) (the_ins.opcode[the_ins.numo++] = (w))
+
+/* Like addword, but goes BEFORE general operands. */
+
+static void
+insop (int w, const struct m68k_incant *opcode)
+{
+ int z;
+ for (z = the_ins.numo; z > opcode->m_codenum; --z)
+ the_ins.opcode[z] = the_ins.opcode[z - 1];
+ for (z = 0; z < the_ins.nrel; z++)
+ the_ins.reloc[z].n += 2;
+ for (z = 0; z < the_ins.nfrag; z++)
+ the_ins.fragb[z].fragoff++;
+ the_ins.opcode[opcode->m_codenum] = w;
+ the_ins.numo++;
+}
+
+/* The numo+1 kludge is so we can hit the low order byte of the prev word.
+ Blecch. */
+static void
+add_fix (int width, struct m68k_exp *exp, int pc_rel, int pc_fix)
+{
+ the_ins.reloc[the_ins.nrel].n = (width == 'B' || width == '3'
+ ? the_ins.numo * 2 - 1
+ : (width == 'b'
+ ? the_ins.numo * 2 + 1
+ : the_ins.numo * 2));
+ the_ins.reloc[the_ins.nrel].exp = exp->exp;
+ the_ins.reloc[the_ins.nrel].wid = width;
+ the_ins.reloc[the_ins.nrel].pcrel_fix = pc_fix;
+#ifdef OBJ_ELF
+ the_ins.reloc[the_ins.nrel].pic_reloc = exp->pic_reloc;
+#endif
+ the_ins.reloc[the_ins.nrel++].pcrel = pc_rel;
+}
+
+/* Cause an extra frag to be generated here, inserting up to 10 bytes
+ (that value is chosen in the frag_var call in md_assemble). TYPE
+ is the subtype of the frag to be generated; its primary type is
+ rs_machine_dependent.
+
+ The TYPE parameter is also used by md_convert_frag_1 and
+ md_estimate_size_before_relax. The appropriate type of fixup will
+ be emitted by md_convert_frag_1.
+
+ ADD becomes the FR_SYMBOL field of the frag, and OFF the FR_OFFSET. */
+static void
+add_frag (symbolS *add, offsetT off, int type)
+{
+ the_ins.fragb[the_ins.nfrag].fragoff = the_ins.numo;
+ the_ins.fragb[the_ins.nfrag].fadd = add;
+ the_ins.fragb[the_ins.nfrag].foff = off;
+ the_ins.fragb[the_ins.nfrag++].fragty = type;
+}
+
+#define isvar(ex) \
+ (op (ex) != O_constant && op (ex) != O_big)
+
+static char *crack_operand (char *str, struct m68k_op *opP);
+static int get_num (struct m68k_exp *exp, int ok);
+static int reverse_16_bits (int in);
+static int reverse_8_bits (int in);
+static void install_gen_operand (int mode, int val);
+static void install_operand (int mode, int val);
+static void s_bss (int);
+static void s_data1 (int);
+static void s_data2 (int);
+static void s_even (int);
+static void s_proc (int);
+static void s_chip (int);
+static void s_fopt (int);
+static void s_opt (int);
+static void s_reg (int);
+static void s_restore (int);
+static void s_save (int);
+static void s_mri_if (int);
+static void s_mri_else (int);
+static void s_mri_endi (int);
+static void s_mri_break (int);
+static void s_mri_next (int);
+static void s_mri_for (int);
+static void s_mri_endf (int);
+static void s_mri_repeat (int);
+static void s_mri_until (int);
+static void s_mri_while (int);
+static void s_mri_endw (int);
+static void s_m68k_cpu (int);
+static void s_m68k_arch (int);
+
+struct m68k_cpu
+{
+ unsigned long arch; /* Architecture features. */
+ const enum m68k_register *control_regs; /* Control regs on chip */
+ const char *name; /* Name */
+ int alias; /* Alias for a cannonical name. If 1, then
+ succeeds canonical name, if -1 then
+ succeeds canonical name, if <-1 ||>1 this is a
+ deprecated name, and the next/previous name
+ should be used. */
+};
+
+/* We hold flags for features explicitly enabled and explicitly
+ disabled. */
+static int current_architecture;
+static int not_current_architecture;
+static const struct m68k_cpu *selected_arch;
+static const struct m68k_cpu *selected_cpu;
+static int initialized;
+
+/* Architecture models. */
+static const struct m68k_cpu m68k_archs[] =
+{
+ {m68000, m68000_ctrl, "68000", 0},
+ {m68010, m68010_ctrl, "68010", 0},
+ {m68020|m68881|m68851, m68020_ctrl, "68020", 0},
+ {m68030|m68881|m68851, m68020_ctrl, "68030", 0},
+ {m68040, m68040_ctrl, "68040", 0},
+ {m68060, m68060_ctrl, "68060", 0},
+ {cpu32|m68881, cpu32_ctrl, "cpu32", 0},
+ {fido_a, fido_ctrl, "fidoa", 0},
+ {mcfisa_a|mcfhwdiv, NULL, "isaa", 0},
+ {mcfisa_a|mcfhwdiv|mcfisa_aa|mcfusp, NULL, "isaaplus", 0},
+ {mcfisa_a|mcfhwdiv|mcfisa_b|mcfusp, NULL, "isab", 0},
+ {mcfisa_a|mcfhwdiv|mcfisa_c|mcfusp, NULL, "isac", 0},
+ {mcfisa_a|mcfhwdiv|mcfisa_b|mcfmac|mcfusp, mcf_ctrl, "cfv4", 0},
+ {mcfisa_a|mcfhwdiv|mcfisa_b|mcfemac|mcfusp|cfloat, mcfv4e_ctrl, "cfv4e", 0},
+ {0,0,NULL, 0}
+};
+
+/* For -mno-mac we want to turn off all types of mac. */
+static const unsigned no_mac = mcfmac | mcfemac;
+
+/* Architecture extensions, here 'alias' -1 for m68k, +1 for cf and 0
+ for either. */
+static const struct m68k_cpu m68k_extensions[] =
+{
+ {m68851, NULL, "68851", -1},
+ {m68881, NULL, "68881", -1},
+ {m68881, NULL, "68882", -1},
+
+ {cfloat|m68881, NULL, "float", 0},
+
+ {mcfhwdiv, NULL, "div", 1},
+ {mcfusp, NULL, "usp", 1},
+ {mcfmac, (void *)&no_mac, "mac", 1},
+ {mcfemac, NULL, "emac", 1},
+
+ {0,NULL,NULL, 0}
+};
+
+/* Processor list */
+static const struct m68k_cpu m68k_cpus[] =
+{
+ {m68000, m68000_ctrl, "68000", 0},
+ {m68000, m68000_ctrl, "68ec000", 1},
+ {m68000, m68000_ctrl, "68hc000", 1},
+ {m68000, m68000_ctrl, "68hc001", 1},
+ {m68000, m68000_ctrl, "68008", 1},
+ {m68000, m68000_ctrl, "68302", 1},
+ {m68000, m68000_ctrl, "68306", 1},
+ {m68000, m68000_ctrl, "68307", 1},
+ {m68000, m68000_ctrl, "68322", 1},
+ {m68000, m68000_ctrl, "68356", 1},
+ {m68010, m68010_ctrl, "68010", 0},
+ {m68020|m68881|m68851, m68020_ctrl, "68020", 0},
+ {m68020|m68881|m68851, m68020_ctrl, "68k", 1},
+ {m68020|m68881|m68851, m68020_ctrl, "68ec020", 1},
+ {m68030|m68881|m68851, m68020_ctrl, "68030", 0},
+ {m68030|m68881|m68851, m68020_ctrl, "68ec030", 1},
+ {m68040, m68040_ctrl, "68040", 0},
+ {m68040, m68040_ctrl, "68ec040", 1},
+ {m68060, m68060_ctrl, "68060", 0},
+ {m68060, m68060_ctrl, "68ec060", 1},
+
+ {cpu32|m68881, cpu32_ctrl, "cpu32", 0},
+ {cpu32|m68881, cpu32_ctrl, "68330", 1},
+ {cpu32|m68881, cpu32_ctrl, "68331", 1},
+ {cpu32|m68881, cpu32_ctrl, "68332", 1},
+ {cpu32|m68881, cpu32_ctrl, "68333", 1},
+ {cpu32|m68881, cpu32_ctrl, "68334", 1},
+ {cpu32|m68881, cpu32_ctrl, "68336", 1},
+ {cpu32|m68881, cpu32_ctrl, "68340", 1},
+ {cpu32|m68881, cpu32_ctrl, "68341", 1},
+ {cpu32|m68881, cpu32_ctrl, "68349", 1},
+ {cpu32|m68881, cpu32_ctrl, "68360", 1},
+
+ {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51", 0},
+ {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51ac", 1},
+ {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51ag", 1},
+ {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51cn", 1},
+ {mcfisa_a|mcfisa_c|mcfusp|mcfmac, mcf51_ctrl, "51em", 1},
+ {mcfisa_a|mcfisa_c|mcfusp|mcfmac, mcf51_ctrl, "51je", 1},
+ {mcfisa_a|mcfisa_c|mcfusp|mcfemac, mcf51_ctrl, "51jf", 1},
+ {mcfisa_a|mcfisa_c|mcfusp|mcfemac, mcf51_ctrl, "51jg", 1},
+ {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51jm", 1},
+ {mcfisa_a|mcfisa_c|mcfusp|mcfmac, mcf51_ctrl, "51mm", 1},
+ {mcfisa_a|mcfisa_c|mcfusp, mcf51_ctrl, "51qe", 1},
+ {mcfisa_a|mcfisa_c|mcfusp|mcfemac, mcf51_ctrl, "51qm", 1},
+
+ {mcfisa_a, mcf_ctrl, "5200", 0},
+ {mcfisa_a, mcf_ctrl, "5202", 1},
+ {mcfisa_a, mcf_ctrl, "5204", 1},
+ {mcfisa_a, mcf5206_ctrl, "5206", 1},
+
+ {mcfisa_a|mcfhwdiv|mcfmac, mcf5206_ctrl, "5206e", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5208_ctrl, "5207", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5208_ctrl, "5208", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5210a_ctrl, "5210a", 0},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5210a_ctrl, "5211a", 1},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5213_ctrl, "5211", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5213_ctrl, "5212", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5213_ctrl, "5213", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5216_ctrl, "5214", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5216_ctrl, "5216", 0},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5216_ctrl, "521x", 2},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5221x_ctrl, "5221x", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf52223_ctrl, "52221", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf52223_ctrl, "52223", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52230", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52233", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52234", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52235_ctrl, "52235", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5225_ctrl, "5224", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfmac|mcfusp, mcf5225_ctrl, "5225", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52277_ctrl, "52274", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52277_ctrl, "52277", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5232", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5233", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5234", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "5235", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5235_ctrl, "523x", 0},
+
+ {mcfisa_a|mcfhwdiv|mcfemac, mcf5249_ctrl, "5249", 0},
+ {mcfisa_a|mcfhwdiv|mcfemac, mcf5250_ctrl, "5250", 0},
+ {mcfisa_a|mcfhwdiv|mcfemac, mcf5253_ctrl, "5253", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52252", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52254", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52255", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52256", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52258", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf52259_ctrl, "52259", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5271_ctrl, "5270", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5271_ctrl, "5271", 0},
+
+ {mcfisa_a|mcfhwdiv|mcfmac, mcf5272_ctrl, "5272", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5275_ctrl, "5274", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5275_ctrl, "5275", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "5280", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "5281", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "5282", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5282_ctrl, "528x", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53011", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53012", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53013", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53014", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53015", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53016", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf53017_ctrl, "53017", 0},
+
+ {mcfisa_a|mcfhwdiv|mcfmac, mcf5307_ctrl, "5307", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "5327", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "5328", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "5329", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5329_ctrl, "532x", 0},
+
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5373_ctrl, "5372", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5373_ctrl, "5373", -1},
+ {mcfisa_a|mcfisa_aa|mcfhwdiv|mcfemac|mcfusp, mcf5373_ctrl, "537x", 0},
+
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfmac, mcf5407_ctrl, "5407",0},
+
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54410", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54415", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54416", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54417", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54418_ctrl, "54418", 0},
+
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54450", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54451", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54452", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54453", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54454", -1},
+ {mcfisa_a|mcfisa_c|mcfhwdiv|mcfemac|mcfusp, mcf54455_ctrl, "54455", 0},
+
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5470", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5471", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5472", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5473", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5474", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "5475", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5475_ctrl, "547x", 0},
+
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5480", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5481", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5482", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5483", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5484", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "5485", -1},
+ {mcfisa_a|mcfisa_b|mcfhwdiv|mcfemac|mcfusp|cfloat, mcf5485_ctrl, "548x", 0},
+
+ {fido_a, fido_ctrl, "fidoa", 0},
+ {fido_a, fido_ctrl, "fido", 1},
+
+ {0,NULL,NULL, 0}
+ };
+
+static const struct m68k_cpu *m68k_lookup_cpu
+(const char *, const struct m68k_cpu *, int, int *);
+static int m68k_set_arch (const char *, int, int);
+static int m68k_set_cpu (const char *, int, int);
+static int m68k_set_extension (const char *, int, int);
+static void m68k_init_arch (void);
+
+/* This is the assembler relaxation table for m68k. m68k is a rich CISC
+ architecture and we have a lot of relaxation modes. */
+
+/* Macros used in the relaxation code. */
+#define TAB(x,y) (((x) << 2) + (y))
+#define TABTYPE(x) ((x) >> 2)
+
+/* Relaxation states. */
+#define BYTE 0
+#define SHORT 1
+#define LONG 2
+#define SZ_UNDEF 3
+
+/* Here are all the relaxation modes we support. First we can relax ordinary
+ branches. On 68020 and higher and on CPU32 all branch instructions take
+ three forms, so on these CPUs all branches always remain as such. When we
+ have to expand to the LONG form on a 68000, though, we substitute an
+ absolute jump instead. This is a direct replacement for unconditional
+ branches and a branch over a jump for conditional branches. However, if the
+ user requires PIC and disables this with --pcrel, we can only relax between
+ BYTE and SHORT forms, punting if that isn't enough. This gives us four
+ different relaxation modes for branches: */
+
+#define BRANCHBWL 0 /* Branch byte, word, or long. */
+#define BRABSJUNC 1 /* Absolute jump for LONG, unconditional. */
+#define BRABSJCOND 2 /* Absolute jump for LONG, conditional. */
+#define BRANCHBW 3 /* Branch byte or word. */
+
+/* We also relax coprocessor branches and DBcc's. All CPUs that support
+ coprocessor branches support them in word and long forms, so we have only
+ one relaxation mode for them. DBcc's are word only on all CPUs. We can
+ relax them to the LONG form with a branch-around sequence. This sequence
+ can use a long branch (if available) or an absolute jump (if acceptable).
+ This gives us two relaxation modes. If long branches are not available and
+ absolute jumps are not acceptable, we don't relax DBcc's. */
+
+#define FBRANCH 4 /* Coprocessor branch. */
+#define DBCCLBR 5 /* DBcc relaxable with a long branch. */
+#define DBCCABSJ 6 /* DBcc relaxable with an absolute jump. */
+
+/* That's all for instruction relaxation. However, we also relax PC-relative
+ operands. Specifically, we have three operand relaxation modes. On the
+ 68000 PC-relative operands can only be 16-bit, but on 68020 and higher and
+ on CPU32 they may be 16-bit or 32-bit. For the latter we relax between the
+ two. Also PC+displacement+index operands in their simple form (with a non-
+ suppressed index without memory indirection) are supported on all CPUs, but
+ on the 68000 the displacement can be 8-bit only, whereas on 68020 and higher
+ and on CPU32 we relax it to SHORT and LONG forms as well using the extended
+ form of the PC+displacement+index operand. Finally, some absolute operands
+ can be relaxed down to 16-bit PC-relative. */
+
+#define PCREL1632 7 /* 16-bit or 32-bit PC-relative. */
+#define PCINDEX 8 /* PC + displacement + index. */
+#define ABSTOPCREL 9 /* Absolute relax down to 16-bit PC-relative. */
+
+/* This relaxation is required for branches where there is no long
+ branch and we are in pcrel mode. We generate a bne/beq pair. */
+#define BRANCHBWPL 10 /* Branch byte, word or pair of longs
+ */
+
+/* Note that calls to frag_var need to specify the maximum expansion
+ needed; this is currently 12 bytes for bne/beq pair. */
+#define FRAG_VAR_SIZE 12
+
+/* The fields are:
+ How far Forward this mode will reach:
+ How far Backward this mode will reach:
+ How many bytes this mode will add to the size of the frag
+ Which mode to go to if the offset won't fit in this one
+
+ Please check tc-m68k.h:md_prepare_relax_scan if changing this table. */
+relax_typeS md_relax_table[] =
+{
+ { 127, -128, 0, TAB (BRANCHBWL, SHORT) },
+ { 32767, -32768, 2, TAB (BRANCHBWL, LONG) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 127, -128, 0, TAB (BRABSJUNC, SHORT) },
+ { 32767, -32768, 2, TAB (BRABSJUNC, LONG) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 127, -128, 0, TAB (BRABSJCOND, SHORT) },
+ { 32767, -32768, 2, TAB (BRABSJCOND, LONG) },
+ { 0, 0, 6, 0 },
+ { 1, 1, 0, 0 },
+
+ { 127, -128, 0, TAB (BRANCHBW, SHORT) },
+ { 0, 0, 2, 0 },
+ { 1, 1, 0, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* FBRANCH doesn't come BYTE. */
+ { 32767, -32768, 2, TAB (FBRANCH, LONG) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* DBCC doesn't come BYTE. */
+ { 32767, -32768, 2, TAB (DBCCLBR, LONG) },
+ { 0, 0, 10, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* DBCC doesn't come BYTE. */
+ { 32767, -32768, 2, TAB (DBCCABSJ, LONG) },
+ { 0, 0, 10, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* PCREL1632 doesn't come BYTE. */
+ { 32767, -32768, 2, TAB (PCREL1632, LONG) },
+ { 0, 0, 6, 0 },
+ { 1, 1, 0, 0 },
+
+ { 125, -130, 0, TAB (PCINDEX, SHORT) },
+ { 32765, -32770, 2, TAB (PCINDEX, LONG) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 1, 1, 0, 0 }, /* ABSTOPCREL doesn't come BYTE. */
+ { 32767, -32768, 2, TAB (ABSTOPCREL, LONG) },
+ { 0, 0, 4, 0 },
+ { 1, 1, 0, 0 },
+
+ { 127, -128, 0, TAB (BRANCHBWPL, SHORT) },
+ { 32767, -32768, 2, TAB (BRANCHBWPL, LONG) },
+ { 0, 0, 10, 0 },
+ { 1, 1, 0, 0 },
+};
+
+/* These are the machine dependent pseudo-ops. These are included so
+ the assembler can work on the output from the SUN C compiler, which
+ generates these. */
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"data1", s_data1, 0},
+ {"data2", s_data2, 0},
+ {"bss", s_bss, 0},
+ {"even", s_even, 0},
+ {"skip", s_space, 0},
+ {"proc", s_proc, 0},
+#if defined (TE_SUN3) || defined (OBJ_ELF)
+ {"align", s_align_bytes, 0},
+#endif
+#ifdef OBJ_ELF
+ {"swbeg", s_ignore, 0},
+ {"long", m68k_elf_cons, 4},
+#endif
+ {"extend", float_cons, 'x'},
+ {"ldouble", float_cons, 'x'},
+
+ {"arch", s_m68k_arch, 0},
+ {"cpu", s_m68k_cpu, 0},
+
+ /* The following pseudo-ops are supported for MRI compatibility. */
+ {"chip", s_chip, 0},
+ {"comline", s_space, 1},
+ {"fopt", s_fopt, 0},
+ {"mask2", s_ignore, 0},
+ {"opt", s_opt, 0},
+ {"reg", s_reg, 0},
+ {"restore", s_restore, 0},
+ {"save", s_save, 0},
+
+ {"if", s_mri_if, 0},
+ {"if.b", s_mri_if, 'b'},
+ {"if.w", s_mri_if, 'w'},
+ {"if.l", s_mri_if, 'l'},
+ {"else", s_mri_else, 0},
+ {"else.s", s_mri_else, 's'},
+ {"else.l", s_mri_else, 'l'},
+ {"endi", s_mri_endi, 0},
+ {"break", s_mri_break, 0},
+ {"break.s", s_mri_break, 's'},
+ {"break.l", s_mri_break, 'l'},
+ {"next", s_mri_next, 0},
+ {"next.s", s_mri_next, 's'},
+ {"next.l", s_mri_next, 'l'},
+ {"for", s_mri_for, 0},
+ {"for.b", s_mri_for, 'b'},
+ {"for.w", s_mri_for, 'w'},
+ {"for.l", s_mri_for, 'l'},
+ {"endf", s_mri_endf, 0},
+ {"repeat", s_mri_repeat, 0},
+ {"until", s_mri_until, 0},
+ {"until.b", s_mri_until, 'b'},
+ {"until.w", s_mri_until, 'w'},
+ {"until.l", s_mri_until, 'l'},
+ {"while", s_mri_while, 0},
+ {"while.b", s_mri_while, 'b'},
+ {"while.w", s_mri_while, 'w'},
+ {"while.l", s_mri_while, 'l'},
+ {"endw", s_mri_endw, 0},
+
+ {0, 0, 0}
+};
+
+/* The mote pseudo ops are put into the opcode table, since they
+ don't start with a . they look like opcodes to gas. */
+
+const pseudo_typeS mote_pseudo_table[] =
+{
+
+ {"dcl", cons, 4},
+ {"dc", cons, 2},
+ {"dcw", cons, 2},
+ {"dcb", cons, 1},
+
+ {"dsl", s_space, 4},
+ {"ds", s_space, 2},
+ {"dsw", s_space, 2},
+ {"dsb", s_space, 1},
+
+ {"xdef", s_globl, 0},
+#ifdef OBJ_ELF
+ {"align", s_align_bytes, 0},
+#else
+ {"align", s_align_ptwo, 0},
+#endif
+#ifdef M68KCOFF
+ {"sect", obj_coff_section, 0},
+ {"section", obj_coff_section, 0},
+#endif
+ {0, 0, 0}
+};
+
+/* Truncate and sign-extend at 32 bits, so that building on a 64-bit host
+ gives identical results to a 32-bit host. */
+#define TRUNC(X) ((valueT) (X) & 0xffffffff)
+#define SEXT(X) ((TRUNC (X) ^ 0x80000000) - 0x80000000)
+
+#define issbyte(x) ((valueT) SEXT (x) + 0x80 < 0x100)
+#define isubyte(x) ((valueT) TRUNC (x) < 0x100)
+#define issword(x) ((valueT) SEXT (x) + 0x8000 < 0x10000)
+#define isuword(x) ((valueT) TRUNC (x) < 0x10000)
+
+#define isbyte(x) ((valueT) SEXT (x) + 0xff < 0x1ff)
+#define isword(x) ((valueT) SEXT (x) + 0xffff < 0x1ffff)
+#define islong(x) (1)
+
+static char notend_table[256];
+static char alt_notend_table[256];
+#define notend(s) \
+ (! (notend_table[(unsigned char) *s] \
+ || (*s == ':' \
+ && alt_notend_table[(unsigned char) s[1]])))
+
+#ifdef OBJ_ELF
+
+/* Return zero if the reference to SYMBOL from within the same segment may
+ be relaxed. */
+
+/* On an ELF system, we can't relax an externally visible symbol,
+ because it may be overridden by a shared library. However, if
+ TARGET_OS is "elf", then we presume that we are assembling for an
+ embedded system, in which case we don't have to worry about shared
+ libraries, and we can relax any external sym. */
+
+#define relaxable_symbol(symbol) \
+ (!((S_IS_EXTERNAL (symbol) && EXTERN_FORCE_RELOC) \
+ || S_IS_WEAK (symbol)))
+
+/* Compute the relocation code for a fixup of SIZE bytes, using pc
+ relative relocation if PCREL is non-zero. PIC says whether a special
+ pic relocation was requested. */
+
+static bfd_reloc_code_real_type
+get_reloc_code (int size, int pcrel, enum pic_relocation pic)
+{
+ switch (pic)
+ {
+ case pic_got_pcrel:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_GOT_PCREL;
+ case 2:
+ return BFD_RELOC_16_GOT_PCREL;
+ case 4:
+ return BFD_RELOC_32_GOT_PCREL;
+ }
+ break;
+
+ case pic_got_off:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_GOTOFF;
+ case 2:
+ return BFD_RELOC_16_GOTOFF;
+ case 4:
+ return BFD_RELOC_32_GOTOFF;
+ }
+ break;
+
+ case pic_plt_pcrel:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_PLT_PCREL;
+ case 2:
+ return BFD_RELOC_16_PLT_PCREL;
+ case 4:
+ return BFD_RELOC_32_PLT_PCREL;
+ }
+ break;
+
+ case pic_plt_off:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_PLTOFF;
+ case 2:
+ return BFD_RELOC_16_PLTOFF;
+ case 4:
+ return BFD_RELOC_32_PLTOFF;
+ }
+ break;
+
+ case pic_tls_gd:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_68K_TLS_GD8;
+ case 2:
+ return BFD_RELOC_68K_TLS_GD16;
+ case 4:
+ return BFD_RELOC_68K_TLS_GD32;
+ }
+ break;
+
+ case pic_tls_ldm:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_68K_TLS_LDM8;
+ case 2:
+ return BFD_RELOC_68K_TLS_LDM16;
+ case 4:
+ return BFD_RELOC_68K_TLS_LDM32;
+ }
+ break;
+
+ case pic_tls_ldo:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_68K_TLS_LDO8;
+ case 2:
+ return BFD_RELOC_68K_TLS_LDO16;
+ case 4:
+ return BFD_RELOC_68K_TLS_LDO32;
+ }
+ break;
+
+ case pic_tls_ie:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_68K_TLS_IE8;
+ case 2:
+ return BFD_RELOC_68K_TLS_IE16;
+ case 4:
+ return BFD_RELOC_68K_TLS_IE32;
+ }
+ break;
+
+ case pic_tls_le:
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_68K_TLS_LE8;
+ case 2:
+ return BFD_RELOC_68K_TLS_LE16;
+ case 4:
+ return BFD_RELOC_68K_TLS_LE32;
+ }
+ break;
+
+ case pic_none:
+ if (pcrel)
+ {
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8_PCREL;
+ case 2:
+ return BFD_RELOC_16_PCREL;
+ case 4:
+ return BFD_RELOC_32_PCREL;
+ }
+ }
+ else
+ {
+ switch (size)
+ {
+ case 1:
+ return BFD_RELOC_8;
+ case 2:
+ return BFD_RELOC_16;
+ case 4:
+ return BFD_RELOC_32;
+ }
+ }
+ }
+
+ if (pcrel)
+ {
+ if (pic == pic_none)
+ as_bad (_("Can not do %d byte pc-relative relocation"), size);
+ else
+ as_bad (_("Can not do %d byte pc-relative pic relocation"), size);
+ }
+ else
+ {
+ if (pic == pic_none)
+ as_bad (_("Can not do %d byte relocation"), size);
+ else
+ as_bad (_("Can not do %d byte pic relocation"), size);
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* Here we decide which fixups can be adjusted to make them relative
+ to the beginning of the section instead of the symbol. Basically
+ we need to make sure that the dynamic relocations are done
+ correctly, so in some cases we force the original symbol to be
+ used. */
+int
+tc_m68k_fix_adjustable (fixS *fixP)
+{
+ /* Adjust_reloc_syms doesn't know about the GOT. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8_GOT_PCREL:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_8_GOTOFF:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_8_PLT_PCREL:
+ case BFD_RELOC_16_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_8_PLTOFF:
+ case BFD_RELOC_16_PLTOFF:
+ case BFD_RELOC_32_PLTOFF:
+ case BFD_RELOC_68K_TLS_GD32:
+ case BFD_RELOC_68K_TLS_GD16:
+ case BFD_RELOC_68K_TLS_GD8:
+ case BFD_RELOC_68K_TLS_LDM32:
+ case BFD_RELOC_68K_TLS_LDM16:
+ case BFD_RELOC_68K_TLS_LDM8:
+ case BFD_RELOC_68K_TLS_LDO32:
+ case BFD_RELOC_68K_TLS_LDO16:
+ case BFD_RELOC_68K_TLS_LDO8:
+ case BFD_RELOC_68K_TLS_IE32:
+ case BFD_RELOC_68K_TLS_IE16:
+ case BFD_RELOC_68K_TLS_IE8:
+ case BFD_RELOC_68K_TLS_LE32:
+ case BFD_RELOC_68K_TLS_LE16:
+ case BFD_RELOC_68K_TLS_LE8:
+ return 0;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+#else /* !OBJ_ELF */
+
+#define get_reloc_code(SIZE,PCREL,OTHER) NO_RELOC
+
+/* PR gas/3041 Weak symbols are not relaxable
+ because they must be treated as extern. */
+#define relaxable_symbol(symbol) (!(S_IS_WEAK (symbol)))
+
+#endif /* OBJ_ELF */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ /* If the tcbit is set, then this was a fixup of a negative value
+ that was never resolved. We do not have a reloc to handle this,
+ so just return. We assume that other code will have detected this
+ situation and produced a helpful error message, so we just tell the
+ user that the reloc cannot be produced. */
+ if (fixp->fx_tcbit)
+ {
+ if (fixp->fx_addsy)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Unable to produce reloc against symbol '%s'"),
+ S_GET_NAME (fixp->fx_addsy));
+ return NULL;
+ }
+
+ if (fixp->fx_r_type != BFD_RELOC_NONE)
+ {
+ code = fixp->fx_r_type;
+
+ /* Since DIFF_EXPR_OK is defined in tc-m68k.h, it is possible
+ that fixup_segment converted a non-PC relative reloc into a
+ PC relative reloc. In such a case, we need to convert the
+ reloc code. */
+ if (fixp->fx_pcrel)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_8:
+ code = BFD_RELOC_8_PCREL;
+ break;
+ case BFD_RELOC_16:
+ code = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_32:
+ code = BFD_RELOC_32_PCREL;
+ break;
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_8_GOT_PCREL:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_8_GOTOFF:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_8_PLT_PCREL:
+ case BFD_RELOC_16_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_8_PLTOFF:
+ case BFD_RELOC_16_PLTOFF:
+ case BFD_RELOC_32_PLTOFF:
+ case BFD_RELOC_68K_TLS_GD32:
+ case BFD_RELOC_68K_TLS_GD16:
+ case BFD_RELOC_68K_TLS_GD8:
+ case BFD_RELOC_68K_TLS_LDM32:
+ case BFD_RELOC_68K_TLS_LDM16:
+ case BFD_RELOC_68K_TLS_LDM8:
+ case BFD_RELOC_68K_TLS_LDO32:
+ case BFD_RELOC_68K_TLS_LDO16:
+ case BFD_RELOC_68K_TLS_LDO8:
+ case BFD_RELOC_68K_TLS_IE32:
+ case BFD_RELOC_68K_TLS_IE16:
+ case BFD_RELOC_68K_TLS_IE8:
+ case BFD_RELOC_68K_TLS_LE32:
+ case BFD_RELOC_68K_TLS_LE16:
+ case BFD_RELOC_68K_TLS_LE8:
+ break;
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot make %s relocation PC relative"),
+ bfd_get_reloc_code_name (code));
+ }
+ }
+ }
+ else
+ {
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ abort ();
+ }
+ }
+#undef F
+#undef MAP
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+#ifndef OBJ_ELF
+ 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)))
+ {
+ /* PR gas/3041 References to weak symbols must be treated as extern
+ in order to be overridable by the linker, even if they are defined
+ in the same object file. So the original addend must be written
+ "as is" into the output section without further processing.
+ The addend value must be hacked here in order to force
+ bfd_install_relocation() to write the original value into the
+ output section.
+ 1) MD_APPLY_SYM_VALUE() is set to 1 for m68k/a.out, so the symbol
+ value has already been added to the addend in fixup_segment(). We
+ have to remove it.
+ 2) bfd_install_relocation() will incorrectly treat this symbol as
+ resolved, so it will write the symbol value plus its addend and
+ section VMA. As a workaround we can tweak the addend value here in
+ order to get the original value in the section after the call to
+ bfd_install_relocation(). */
+ reloc->addend = fixp->fx_addnumber
+ /* Fix because of MD_APPLY_SYM_VALUE() */
+ - S_GET_VALUE (fixp->fx_addsy)
+ /* Fix for bfd_install_relocation() */
+ - (S_GET_VALUE (fixp->fx_addsy)
+ + S_GET_SEGMENT (fixp->fx_addsy)->vma);
+ }
+ else if (fixp->fx_pcrel)
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = 0;
+#else
+ if (!fixp->fx_pcrel)
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = (section->vma
+ + fixp->fx_pcrel_adjust
+ + fixp->fx_addnumber
+ + md_pcrel_from (fixp));
+#endif
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ gas_assert (reloc->howto != 0);
+
+ return reloc;
+}
+
+/* Handle of the OPCODE hash table. NULL means any use before
+ m68k_ip_begin() will crash. */
+static struct hash_control *op_hash;
+
+/* Assemble an m68k instruction. */
+
+static void
+m68k_ip (char *instring)
+{
+ register char *p;
+ register struct m68k_op *opP;
+ register const struct m68k_incant *opcode;
+ register const char *s;
+ register int tmpreg = 0, baseo = 0, outro = 0, nextword;
+ char *pdot, *pdotmove;
+ enum m68k_size siz1, siz2;
+ char c;
+ int losing;
+ int opsfound;
+ struct m68k_op operands_backup[6];
+ LITTLENUM_TYPE words[6];
+ LITTLENUM_TYPE *wordp;
+ unsigned long ok_arch = 0;
+
+ if (*instring == ' ')
+ instring++; /* Skip leading whitespace. */
+
+ /* Scan up to end of operation-code, which MUST end in end-of-string
+ or exactly 1 space. */
+ pdot = 0;
+ for (p = instring; *p != '\0'; p++)
+ {
+ if (*p == ' ')
+ break;
+ if (*p == '.')
+ pdot = p;
+ }
+
+ if (p == instring)
+ {
+ the_ins.error = _("No operator");
+ return;
+ }
+
+ /* p now points to the end of the opcode name, probably whitespace.
+ Make sure the name is null terminated by clobbering the
+ whitespace, look it up in the hash table, then fix it back.
+ Remove a dot, first, since the opcode tables have none. */
+ if (pdot != NULL)
+ {
+ for (pdotmove = pdot; pdotmove < p; pdotmove++)
+ *pdotmove = pdotmove[1];
+ p--;
+ }
+
+ c = *p;
+ *p = '\0';
+ opcode = (const struct m68k_incant *) hash_find (op_hash, instring);
+ *p = c;
+
+ if (pdot != NULL)
+ {
+ for (pdotmove = p; pdotmove > pdot; pdotmove--)
+ *pdotmove = pdotmove[-1];
+ *pdot = '.';
+ ++p;
+ }
+
+ if (opcode == NULL)
+ {
+ the_ins.error = _("Unknown operator");
+ return;
+ }
+
+ /* Found a legitimate opcode, start matching operands. */
+ while (*p == ' ')
+ ++p;
+
+ if (opcode->m_operands == 0)
+ {
+ char *old = input_line_pointer;
+ *old = '\n';
+ input_line_pointer = p;
+ /* Ahh - it's a motorola style psuedo op. */
+ mote_pseudo_table[opcode->m_opnum].poc_handler
+ (mote_pseudo_table[opcode->m_opnum].poc_val);
+ input_line_pointer = old;
+ *old = 0;
+
+ return;
+ }
+
+ if (flag_mri && opcode->m_opnum == 0)
+ {
+ /* In MRI mode, random garbage is allowed after an instruction
+ which accepts no operands. */
+ the_ins.args = opcode->m_operands;
+ the_ins.numargs = opcode->m_opnum;
+ the_ins.numo = opcode->m_codenum;
+ the_ins.opcode[0] = getone (opcode);
+ the_ins.opcode[1] = gettwo (opcode);
+ return;
+ }
+
+ for (opP = &the_ins.operands[0]; *p; opP++)
+ {
+ p = crack_operand (p, opP);
+
+ if (opP->error)
+ {
+ the_ins.error = opP->error;
+ return;
+ }
+ }
+
+ opsfound = opP - &the_ins.operands[0];
+
+ /* This ugly hack is to support the floating pt opcodes in their
+ standard form. Essentially, we fake a first enty of type COP#1 */
+ if (opcode->m_operands[0] == 'I')
+ {
+ int n;
+
+ for (n = opsfound; n > 0; --n)
+ the_ins.operands[n] = the_ins.operands[n - 1];
+
+ memset (&the_ins.operands[0], '\0', sizeof (the_ins.operands[0]));
+ the_ins.operands[0].mode = CONTROL;
+ the_ins.operands[0].reg = m68k_float_copnum;
+ opsfound++;
+ }
+
+ /* We've got the operands. Find an opcode that'll accept them. */
+ for (losing = 0;;)
+ {
+ /* If we didn't get the right number of ops, or we have no
+ common model with this pattern then reject this pattern. */
+
+ ok_arch |= opcode->m_arch;
+ if (opsfound != opcode->m_opnum
+ || ((opcode->m_arch & current_architecture) == 0))
+ ++losing;
+ else
+ {
+ int i;
+
+ /* Make a copy of the operands of this insn so that
+ we can modify them safely, should we want to. */
+ gas_assert (opsfound <= (int) ARRAY_SIZE (operands_backup));
+ for (i = 0; i < opsfound; i++)
+ operands_backup[i] = the_ins.operands[i];
+
+ for (s = opcode->m_operands, opP = &operands_backup[0];
+ *s && !losing;
+ s += 2, opP++)
+ {
+ /* Warning: this switch is huge! */
+ /* I've tried to organize the cases into this order:
+ non-alpha first, then alpha by letter. Lower-case
+ goes directly before uppercase counterpart. */
+ /* Code with multiple case ...: gets sorted by the lowest
+ case ... it belongs to. I hope this makes sense. */
+ switch (*s)
+ {
+ case '!':
+ switch (opP->mode)
+ {
+ case IMMED:
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case AINC:
+ case ADEC:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '<':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case IMMED:
+ case ADEC:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '>':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case IMMED:
+ case AINC:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC
+ || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case 'm':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'n':
+ switch (opP->mode)
+ {
+ case DISP:
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'o':
+ switch (opP->mode)
+ {
+ case BASE:
+ case ABSL:
+ case IMMED:
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'p':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ break;
+ case DISP:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ default:
+ losing++;
+ }
+ break;
+
+ case 'q':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ break;
+ case DISP:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ default:
+ losing++;
+ break;
+ }
+ break;
+
+ case 'v':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AINDR:
+ case AINC:
+ case ADEC:
+ case ABSL:
+ break;
+ case DISP:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ default:
+ losing++;
+ break;
+ }
+ break;
+
+ case '#':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (s[1] == 'b'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! isbyte (opP->disp.exp.X_add_number)))
+ losing++;
+ else if (s[1] == 'B'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! issbyte (opP->disp.exp.X_add_number)))
+ losing++;
+ else if (s[1] == 'w'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! isword (opP->disp.exp.X_add_number)))
+ losing++;
+ else if (s[1] == 'W'
+ && ! isvar (&opP->disp)
+ && (opP->disp.exp.X_op != O_constant
+ || ! issword (opP->disp.exp.X_add_number)))
+ losing++;
+ break;
+
+ case '^':
+ case 'T':
+ if (opP->mode != IMMED)
+ losing++;
+ break;
+
+ case '$':
+ if (opP->mode == AREG
+ || opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == IMMED
+ || opP->mode == REGLST
+ || (opP->mode != ABSL
+ && (opP->reg == PC
+ || opP->reg == ZPC)))
+ losing++;
+ break;
+
+ case '%':
+ if (opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == REGLST
+ || opP->mode == IMMED
+ || (opP->mode != ABSL
+ && (opP->reg == PC
+ || opP->reg == ZPC)))
+ losing++;
+ break;
+
+ case '&':
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case IMMED:
+ case AINC:
+ case ADEC:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC
+ || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case '*':
+ if (opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == REGLST)
+ losing++;
+ break;
+
+ case '+':
+ if (opP->mode != AINC)
+ losing++;
+ break;
+
+ case '-':
+ if (opP->mode != ADEC)
+ losing++;
+ break;
+
+ case '/':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case AINC:
+ case ADEC:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case ';':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '?':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case AINC:
+ case ADEC:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case '@':
+ switch (opP->mode)
+ {
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case '~': /* For now! (JF FOO is this right?) */
+ switch (opP->mode)
+ {
+ case DREG:
+ case AREG:
+ case CONTROL:
+ case FPREG:
+ case IMMED:
+ case REGLST:
+ losing++;
+ break;
+ case ABSL:
+ break;
+ default:
+ if (opP->reg == PC
+ || opP->reg == ZPC)
+ losing++;
+ break;
+ }
+ break;
+
+ case '3':
+ if (opP->mode != CONTROL
+ || (opP->reg != TT0 && opP->reg != TT1))
+ losing++;
+ break;
+
+ case 'A':
+ if (opP->mode != AREG)
+ losing++;
+ break;
+
+ case 'a':
+ if (opP->mode != AINDR)
+ ++losing;
+ break;
+
+ case '4':
+ if (opP->mode != AINDR && opP->mode != AINC && opP->mode != ADEC
+ && (opP->mode != DISP
+ || opP->reg < ADDR0
+ || opP->reg > ADDR7))
+ ++losing;
+ break;
+
+ case 'B': /* FOO */
+ if (opP->mode != ABSL
+ || (flag_long_jumps
+ && strncmp (instring, "jbsr", 4) == 0))
+ losing++;
+ break;
+
+ case 'b':
+ switch (opP->mode)
+ {
+ case IMMED:
+ case ABSL:
+ case AREG:
+ case FPREG:
+ case CONTROL:
+ case POST:
+ case PRE:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 'C':
+ if (opP->mode != CONTROL || opP->reg != CCR)
+ losing++;
+ break;
+
+ case 'd':
+ if (opP->mode != DISP
+ || opP->reg < ADDR0
+ || opP->reg > ADDR7)
+ losing++;
+ break;
+
+ case 'D':
+ if (opP->mode != DREG)
+ losing++;
+ break;
+
+ case 'E':
+ if (opP->reg != ACC)
+ losing++;
+ break;
+
+ case 'e':
+ if (opP->reg != ACC && opP->reg != ACC1
+ && opP->reg != ACC2 && opP->reg != ACC3)
+ losing++;
+ break;
+
+ case 'F':
+ if (opP->mode != FPREG)
+ losing++;
+ break;
+
+ case 'G':
+ if (opP->reg != MACSR)
+ losing++;
+ break;
+
+ case 'g':
+ if (opP->reg != ACCEXT01 && opP->reg != ACCEXT23)
+ losing++;
+ break;
+
+ case 'H':
+ if (opP->reg != MASK)
+ losing++;
+ break;
+
+ case 'I':
+ if (opP->mode != CONTROL
+ || opP->reg < COP0
+ || opP->reg > COP7)
+ losing++;
+ break;
+
+ case 'i':
+ if (opP->mode != LSH && opP->mode != RSH)
+ losing++;
+ break;
+
+ case 'J':
+ if (opP->mode != CONTROL
+ || opP->reg < USP
+ || opP->reg > last_movec_reg
+ || !control_regs)
+ losing++;
+ else
+ {
+ const enum m68k_register *rp;
+
+ for (rp = control_regs; *rp; rp++)
+ {
+ if (*rp == opP->reg)
+ break;
+ /* In most CPUs RAMBAR refers to control reg
+ c05 (RAMBAR1), but a few CPUs have it
+ refer to c04 (RAMBAR0). */
+ else if (*rp == RAMBAR_ALT && opP->reg == RAMBAR)
+ {
+ opP->reg = RAMBAR_ALT;
+ break;
+ }
+ }
+ if (*rp == 0)
+ losing++;
+ }
+ break;
+
+ case 'k':
+ if (opP->mode != IMMED)
+ losing++;
+ break;
+
+ case 'l':
+ case 'L':
+ if (opP->mode == DREG
+ || opP->mode == AREG
+ || opP->mode == FPREG)
+ {
+ if (s[1] == '8')
+ losing++;
+ else
+ {
+ switch (opP->mode)
+ {
+ case DREG:
+ opP->mask = 1 << (opP->reg - DATA0);
+ break;
+ case AREG:
+ opP->mask = 1 << (opP->reg - ADDR0 + 8);
+ break;
+ case FPREG:
+ opP->mask = 1 << (opP->reg - FP0 + 16);
+ break;
+ default:
+ abort ();
+ }
+ opP->mode = REGLST;
+ }
+ }
+ else if (opP->mode == CONTROL)
+ {
+ if (s[1] != '8')
+ losing++;
+ else
+ {
+ switch (opP->reg)
+ {
+ case FPI:
+ opP->mask = 1 << 24;
+ break;
+ case FPS:
+ opP->mask = 1 << 25;
+ break;
+ case FPC:
+ opP->mask = 1 << 26;
+ break;
+ default:
+ losing++;
+ break;
+ }
+ opP->mode = REGLST;
+ }
+ }
+ else if (opP->mode != REGLST)
+ losing++;
+ else if (s[1] == '8' && (opP->mask & 0x0ffffff) != 0)
+ losing++;
+ else if (s[1] == '3' && (opP->mask & 0x7000000) != 0)
+ losing++;
+ break;
+
+ case 'M':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || ! issbyte (opP->disp.exp.X_add_number))
+ losing++;
+ else if (! m68k_quick
+ && instring[3] != 'q'
+ && instring[4] != 'q')
+ losing++;
+ break;
+
+ case 'O':
+ if (opP->mode != DREG
+ && opP->mode != IMMED
+ && opP->mode != ABSL)
+ losing++;
+ break;
+
+ case 'Q':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || TRUNC (opP->disp.exp.X_add_number) - 1 > 7)
+ losing++;
+ else if (! m68k_quick
+ && (strncmp (instring, "add", 3) == 0
+ || strncmp (instring, "sub", 3) == 0)
+ && instring[3] != 'q')
+ losing++;
+ break;
+
+ case 'R':
+ if (opP->mode != DREG && opP->mode != AREG)
+ losing++;
+ break;
+
+ case 'r':
+ if (opP->mode != AINDR
+ && (opP->mode != BASE
+ || (opP->reg != 0
+ && opP->reg != ZADDR0)
+ || opP->disp.exp.X_op != O_absent
+ || ((opP->index.reg < DATA0
+ || opP->index.reg > DATA7)
+ && (opP->index.reg < ADDR0
+ || opP->index.reg > ADDR7))
+ || opP->index.size != SIZE_UNSPEC
+ || opP->index.scale != 1))
+ losing++;
+ break;
+
+ case 's':
+ if (opP->mode != CONTROL
+ || ! (opP->reg == FPI
+ || opP->reg == FPS
+ || opP->reg == FPC))
+ losing++;
+ break;
+
+ case 'S':
+ if (opP->mode != CONTROL || opP->reg != SR)
+ losing++;
+ break;
+
+ case 't':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || TRUNC (opP->disp.exp.X_add_number) > 7)
+ losing++;
+ break;
+
+ case 'U':
+ if (opP->mode != CONTROL || opP->reg != USP)
+ losing++;
+ break;
+
+ case 'x':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || (TRUNC (opP->disp.exp.X_add_number) != 0xffffffff
+ && TRUNC (opP->disp.exp.X_add_number) - 1 > 6))
+ losing++;
+ break;
+
+ case 'j':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || TRUNC (opP->disp.exp.X_add_number) - 1 > 7)
+ losing++;
+ break;
+
+ case 'K':
+ if (opP->mode != IMMED)
+ losing++;
+ else if (opP->disp.exp.X_op != O_constant
+ || TRUNC (opP->disp.exp.X_add_number) > 511)
+ losing++;
+ break;
+
+ /* JF these are out of order. We could put them
+ in order if we were willing to put up with
+ bunches of #ifdef m68851s in the code.
+
+ Don't forget that you need these operands
+ to use 68030 MMU instructions. */
+#ifndef NO_68851
+ /* Memory addressing mode used by pflushr. */
+ case '|':
+ if (opP->mode == CONTROL
+ || opP->mode == FPREG
+ || opP->mode == DREG
+ || opP->mode == AREG
+ || opP->mode == REGLST)
+ losing++;
+ /* We should accept immediate operands, but they
+ supposedly have to be quad word, and we don't
+ handle that. I would like to see what a Motorola
+ assembler does before doing something here. */
+ if (opP->mode == IMMED)
+ losing++;
+ break;
+
+ case 'f':
+ if (opP->mode != CONTROL
+ || (opP->reg != SFC && opP->reg != DFC))
+ losing++;
+ break;
+
+ case '0':
+ if (opP->mode != CONTROL || opP->reg != TC)
+ losing++;
+ break;
+
+ case '1':
+ if (opP->mode != CONTROL || opP->reg != AC)
+ losing++;
+ break;
+
+ case '2':
+ if (opP->mode != CONTROL
+ || (opP->reg != CAL
+ && opP->reg != VAL
+ && opP->reg != SCC))
+ losing++;
+ break;
+
+ case 'V':
+ if (opP->mode != CONTROL
+ || opP->reg != VAL)
+ losing++;
+ break;
+
+ case 'W':
+ if (opP->mode != CONTROL
+ || (opP->reg != DRP
+ && opP->reg != SRP
+ && opP->reg != CRP))
+ losing++;
+ break;
+
+ case 'w':
+ switch (opP->mode)
+ {
+ case IMMED:
+ case ABSL:
+ case AREG:
+ case DREG:
+ case FPREG:
+ case CONTROL:
+ case POST:
+ case PRE:
+ case REGLST:
+ losing++;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case 'X':
+ if (opP->mode != CONTROL
+ || (!(opP->reg >= BAD && opP->reg <= BAD + 7)
+ && !(opP->reg >= BAC && opP->reg <= BAC + 7)))
+ losing++;
+ break;
+
+ case 'Y':
+ if (opP->mode != CONTROL || opP->reg != PSR)
+ losing++;
+ break;
+
+ case 'Z':
+ if (opP->mode != CONTROL || opP->reg != PCSR)
+ losing++;
+ break;
+#endif
+ case 'c':
+ if (opP->mode != CONTROL
+ || (opP->reg != NC
+ && opP->reg != IC
+ && opP->reg != DC
+ && opP->reg != BC))
+ losing++;
+ break;
+
+ case '_':
+ if (opP->mode != ABSL)
+ ++losing;
+ break;
+
+ case 'u':
+ if (opP->reg < DATA0L || opP->reg > ADDR7U)
+ losing++;
+ /* FIXME: kludge instead of fixing parser:
+ upper/lower registers are *not* CONTROL
+ registers, but ordinary ones. */
+ if ((opP->reg >= DATA0L && opP->reg <= DATA7L)
+ || (opP->reg >= DATA0U && opP->reg <= DATA7U))
+ opP->mode = DREG;
+ else
+ opP->mode = AREG;
+ break;
+
+ case 'y':
+ if (!(opP->mode == AINDR
+ || (opP->mode == DISP
+ && !(opP->reg == PC || opP->reg == ZPC))))
+ losing++;
+ break;
+
+ case 'z':
+ if (!(opP->mode == AINDR || opP->mode == DISP))
+ losing++;
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (losing)
+ break;
+ }
+
+ /* Since we have found the correct instruction, copy
+ in the modifications that we may have made. */
+ if (!losing)
+ for (i = 0; i < opsfound; i++)
+ the_ins.operands[i] = operands_backup[i];
+ }
+
+ if (!losing)
+ break;
+
+ opcode = opcode->m_next;
+
+ if (!opcode)
+ {
+ if (ok_arch
+ && !(ok_arch & current_architecture))
+ {
+ const struct m68k_cpu *cpu;
+ int any = 0;
+ size_t space = 400;
+ char *buf = xmalloc (space + 1);
+ size_t len;
+ int paren = 1;
+
+ the_ins.error = buf;
+ /* Make sure there's a NUL at the end of the buffer -- strncpy
+ won't write one when it runs out of buffer. */
+ buf[space] = 0;
+#define APPEND(STRING) \
+ (strncpy (buf, STRING, space), len = strlen (buf), buf += len, space -= len)
+
+ APPEND (_("invalid instruction for this architecture; needs "));
+ switch (ok_arch)
+ {
+ case mcfisa_a:
+ APPEND ("ColdFire ISA_A");
+ break;
+ case mcfhwdiv:
+ APPEND ("ColdFire ");
+ APPEND (_("hardware divide"));
+ break;
+ case mcfisa_aa:
+ APPEND ("ColdFire ISA_A+");
+ break;
+ case mcfisa_b:
+ APPEND ("ColdFire ISA_B");
+ break;
+ case mcfisa_c:
+ APPEND ("ColdFire ISA_C");
+ break;
+ case cfloat:
+ APPEND ("ColdFire fpu");
+ break;
+ case mfloat:
+ APPEND ("M68K fpu");
+ break;
+ case mmmu:
+ APPEND ("M68K mmu");
+ break;
+ case m68020up:
+ APPEND ("68020 ");
+ APPEND (_("or higher"));
+ break;
+ case m68000up:
+ APPEND ("68000 ");
+ APPEND (_("or higher"));
+ break;
+ case m68010up:
+ APPEND ("68010 ");
+ APPEND (_("or higher"));
+ break;
+ default:
+ paren = 0;
+ }
+ if (paren)
+ APPEND (" (");
+
+ for (cpu = m68k_cpus; cpu->name; cpu++)
+ if (!cpu->alias && (cpu->arch & ok_arch))
+ {
+ const struct m68k_cpu *alias;
+ int seen_master = 0;
+
+ if (any)
+ APPEND (", ");
+ any = 0;
+ APPEND (cpu->name);
+ for (alias = cpu; alias != m68k_cpus; alias--)
+ if (alias[-1].alias >= 0)
+ break;
+ for (; !seen_master || alias->alias > 0; alias++)
+ {
+ if (!alias->alias)
+ seen_master = 1;
+ else
+ {
+ if (any)
+ APPEND (", ");
+ else
+ APPEND (" [");
+ APPEND (alias->name);
+ any = 1;
+ }
+ }
+ if (any)
+ APPEND ("]");
+ any = 1;
+ }
+ if (paren)
+ APPEND (")");
+#undef APPEND
+ if (!space)
+ {
+ /* We ran out of space, so replace the end of the list
+ with ellipsis. */
+ buf -= 4;
+ while (*buf != ' ')
+ buf--;
+ strcpy (buf, " ...");
+ }
+ }
+ else
+ the_ins.error = _("operands mismatch");
+ return;
+ }
+
+ losing = 0;
+ }
+
+ /* Now assemble it. */
+ the_ins.args = opcode->m_operands;
+ the_ins.numargs = opcode->m_opnum;
+ the_ins.numo = opcode->m_codenum;
+ the_ins.opcode[0] = getone (opcode);
+ the_ins.opcode[1] = gettwo (opcode);
+
+ for (s = the_ins.args, opP = &the_ins.operands[0]; *s; s += 2, opP++)
+ {
+ int have_disp = 0;
+ int use_pl = 0;
+
+ /* This switch is a doozy.
+ Watch the first step; its a big one! */
+ switch (s[0])
+ {
+
+ case '*':
+ case '~':
+ case '%':
+ case ';':
+ case '@':
+ case '!':
+ case '&':
+ case '$':
+ case '?':
+ case '/':
+ case '<':
+ case '>':
+ case 'b':
+ case 'm':
+ case 'n':
+ case 'o':
+ case 'p':
+ case 'q':
+ case 'v':
+ case 'w':
+ case 'y':
+ case 'z':
+ case '4':
+#ifndef NO_68851
+ case '|':
+#endif
+ switch (opP->mode)
+ {
+ case IMMED:
+ tmpreg = 0x3c; /* 7.4 */
+ if (strchr ("bwl", s[1]))
+ nextword = get_num (&opP->disp, 90);
+ else
+ nextword = get_num (&opP->disp, 0);
+ if (isvar (&opP->disp))
+ add_fix (s[1], &opP->disp, 0, 0);
+ switch (s[1])
+ {
+ case 'b':
+ if (!isbyte (nextword))
+ opP->error = _("operand out of range");
+ addword (nextword);
+ baseo = 0;
+ break;
+ case 'w':
+ if (!isword (nextword))
+ opP->error = _("operand out of range");
+ addword (nextword);
+ baseo = 0;
+ break;
+ case 'W':
+ if (!issword (nextword))
+ opP->error = _("operand out of range");
+ addword (nextword);
+ baseo = 0;
+ break;
+ case 'l':
+ addword (nextword >> 16);
+ addword (nextword);
+ baseo = 0;
+ break;
+
+ case 'f':
+ baseo = 2;
+ outro = 8;
+ break;
+ case 'F':
+ baseo = 4;
+ outro = 11;
+ break;
+ case 'x':
+ baseo = 6;
+ outro = 15;
+ break;
+ case 'p':
+ baseo = 6;
+ outro = -1;
+ break;
+ default:
+ abort ();
+ }
+ if (!baseo)
+ break;
+
+ /* We gotta put out some float. */
+ if (op (&opP->disp) != O_big)
+ {
+ valueT val;
+ int gencnt;
+
+ /* Can other cases happen here? */
+ if (op (&opP->disp) != O_constant)
+ abort ();
+
+ val = (valueT) offs (&opP->disp);
+ gencnt = 0;
+ do
+ {
+ generic_bignum[gencnt] = (LITTLENUM_TYPE) val;
+ val >>= LITTLENUM_NUMBER_OF_BITS;
+ ++gencnt;
+ }
+ while (val != 0);
+ offs (&opP->disp) = gencnt;
+ }
+ if (offs (&opP->disp) > 0)
+ {
+ if (offs (&opP->disp) > baseo)
+ {
+ as_warn (_("Bignum too big for %c format; truncated"),
+ s[1]);
+ offs (&opP->disp) = baseo;
+ }
+ baseo -= offs (&opP->disp);
+ while (baseo--)
+ addword (0);
+ for (wordp = generic_bignum + offs (&opP->disp) - 1;
+ offs (&opP->disp)--;
+ --wordp)
+ addword (*wordp);
+ break;
+ }
+ gen_to_words (words, baseo, (long) outro);
+ for (wordp = words; baseo--; wordp++)
+ addword (*wordp);
+ break;
+ case DREG:
+ tmpreg = opP->reg - DATA; /* 0.dreg */
+ break;
+ case AREG:
+ tmpreg = 0x08 + opP->reg - ADDR; /* 1.areg */
+ break;
+ case AINDR:
+ tmpreg = 0x10 + opP->reg - ADDR; /* 2.areg */
+ break;
+ case ADEC:
+ tmpreg = 0x20 + opP->reg - ADDR; /* 4.areg */
+ break;
+ case AINC:
+ tmpreg = 0x18 + opP->reg - ADDR; /* 3.areg */
+ break;
+ case DISP:
+
+ nextword = get_num (&opP->disp, 90);
+
+ /* Convert mode 5 addressing with a zero offset into
+ mode 2 addressing to reduce the instruction size by a
+ word. */
+ if (! isvar (&opP->disp)
+ && (nextword == 0)
+ && (opP->disp.size == SIZE_UNSPEC)
+ && (opP->reg >= ADDR0)
+ && (opP->reg <= ADDR7))
+ {
+ tmpreg = 0x10 + opP->reg - ADDR; /* 2.areg */
+ break;
+ }
+
+ if (opP->reg == PC
+ && ! isvar (&opP->disp)
+ && m68k_abspcadd)
+ {
+ opP->disp.exp.X_op = O_symbol;
+ opP->disp.exp.X_add_symbol =
+ section_symbol (absolute_section);
+ }
+
+ /* Force into index mode. Hope this works. */
+
+ /* We do the first bit for 32-bit displacements, and the
+ second bit for 16 bit ones. It is possible that we
+ should make the default be WORD instead of LONG, but
+ I think that'd break GCC, so we put up with a little
+ inefficiency for the sake of working output. */
+
+ if (!issword (nextword)
+ || (isvar (&opP->disp)
+ && ((opP->disp.size == SIZE_UNSPEC
+ && flag_short_refs == 0
+ && cpu_of_arch (current_architecture) >= m68020
+ && ! arch_coldfire_p (current_architecture))
+ || opP->disp.size == SIZE_LONG)))
+ {
+ if (cpu_of_arch (current_architecture) < m68020
+ || arch_coldfire_p (current_architecture))
+ opP->error =
+ _("displacement too large for this architecture; needs 68020 or higher");
+ if (opP->reg == PC)
+ tmpreg = 0x3B; /* 7.3 */
+ else
+ tmpreg = 0x30 + opP->reg - ADDR; /* 6.areg */
+ if (isvar (&opP->disp))
+ {
+ if (opP->reg == PC)
+ {
+ if (opP->disp.size == SIZE_LONG
+#ifdef OBJ_ELF
+ /* If the displacement needs pic
+ relocation it cannot be relaxed. */
+ || opP->disp.pic_reloc != pic_none
+#endif
+ )
+ {
+ addword (0x0170);
+ add_fix ('l', &opP->disp, 1, 2);
+ }
+ else
+ {
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (PCREL1632, SZ_UNDEF));
+ break;
+ }
+ }
+ else
+ {
+ addword (0x0170);
+ add_fix ('l', &opP->disp, 0, 0);
+ }
+ }
+ else
+ addword (0x0170);
+ addword (nextword >> 16);
+ }
+ else
+ {
+ if (opP->reg == PC)
+ tmpreg = 0x3A; /* 7.2 */
+ else
+ tmpreg = 0x28 + opP->reg - ADDR; /* 5.areg */
+
+ if (isvar (&opP->disp))
+ {
+ if (opP->reg == PC)
+ {
+ add_fix ('w', &opP->disp, 1, 0);
+ }
+ else
+ add_fix ('w', &opP->disp, 0, 0);
+ }
+ }
+ addword (nextword);
+ break;
+
+ case POST:
+ case PRE:
+ case BASE:
+ nextword = 0;
+ baseo = get_num (&opP->disp, 90);
+ if (opP->mode == POST || opP->mode == PRE)
+ outro = get_num (&opP->odisp, 90);
+ /* Figure out the `addressing mode'.
+ Also turn on the BASE_DISABLE bit, if needed. */
+ if (opP->reg == PC || opP->reg == ZPC)
+ {
+ tmpreg = 0x3b; /* 7.3 */
+ if (opP->reg == ZPC)
+ nextword |= 0x80;
+ }
+ else if (opP->reg == 0)
+ {
+ nextword |= 0x80;
+ tmpreg = 0x30; /* 6.garbage */
+ }
+ else if (opP->reg >= ZADDR0 && opP->reg <= ZADDR7)
+ {
+ nextword |= 0x80;
+ tmpreg = 0x30 + opP->reg - ZADDR0;
+ }
+ else
+ tmpreg = 0x30 + opP->reg - ADDR; /* 6.areg */
+
+ siz1 = opP->disp.size;
+ if (opP->mode == POST || opP->mode == PRE)
+ siz2 = opP->odisp.size;
+ else
+ siz2 = SIZE_UNSPEC;
+
+ /* Index register stuff. */
+ if (opP->index.reg != 0
+ && opP->index.reg >= DATA
+ && opP->index.reg <= ADDR7)
+ {
+ nextword |= (opP->index.reg - DATA) << 12;
+
+ if (opP->index.size == SIZE_LONG
+ || (opP->index.size == SIZE_UNSPEC
+ && m68k_index_width_default == SIZE_LONG))
+ nextword |= 0x800;
+
+ if ((opP->index.scale != 1
+ && cpu_of_arch (current_architecture) < m68020)
+ || (opP->index.scale == 8
+ && (arch_coldfire_p (current_architecture)
+ && !arch_coldfire_fpu (current_architecture))))
+ {
+ opP->error =
+ _("scale factor invalid on this architecture; needs cpu32 or 68020 or higher");
+ }
+
+ if (arch_coldfire_p (current_architecture)
+ && opP->index.size == SIZE_WORD)
+ opP->error = _("invalid index size for coldfire");
+
+ switch (opP->index.scale)
+ {
+ case 1:
+ break;
+ case 2:
+ nextword |= 0x200;
+ break;
+ case 4:
+ nextword |= 0x400;
+ break;
+ case 8:
+ nextword |= 0x600;
+ break;
+ default:
+ abort ();
+ }
+ /* IF its simple,
+ GET US OUT OF HERE! */
+
+ /* Must be INDEX, with an index register. Address
+ register cannot be ZERO-PC, and either :b was
+ forced, or we know it will fit. For a 68000 or
+ 68010, force this mode anyways, because the
+ larger modes aren't supported. */
+ if (opP->mode == BASE
+ && ((opP->reg >= ADDR0
+ && opP->reg <= ADDR7)
+ || opP->reg == PC))
+ {
+ if (siz1 == SIZE_BYTE
+ || cpu_of_arch (current_architecture) < m68020
+ || arch_coldfire_p (current_architecture)
+ || (siz1 == SIZE_UNSPEC
+ && ! isvar (&opP->disp)
+ && issbyte (baseo)))
+ {
+ nextword += baseo & 0xff;
+ addword (nextword);
+ if (isvar (&opP->disp))
+ {
+ /* Do a byte relocation. If it doesn't
+ fit (possible on m68000) let the
+ fixup processing complain later. */
+ if (opP->reg == PC)
+ add_fix ('B', &opP->disp, 1, 1);
+ else
+ add_fix ('B', &opP->disp, 0, 0);
+ }
+ else if (siz1 != SIZE_BYTE)
+ {
+ if (siz1 != SIZE_UNSPEC)
+ as_warn (_("Forcing byte displacement"));
+ if (! issbyte (baseo))
+ opP->error = _("byte displacement out of range");
+ }
+
+ break;
+ }
+ else if (siz1 == SIZE_UNSPEC
+ && opP->reg == PC
+ && isvar (&opP->disp)
+ && subs (&opP->disp) == NULL
+#ifdef OBJ_ELF
+ /* If the displacement needs pic
+ relocation it cannot be relaxed. */
+ && opP->disp.pic_reloc == pic_none
+#endif
+ )
+ {
+ /* The code in md_convert_frag_1 needs to be
+ able to adjust nextword. Call frag_grow
+ to ensure that we have enough space in
+ the frag obstack to make all the bytes
+ contiguous. */
+ frag_grow (14);
+ nextword += baseo & 0xff;
+ addword (nextword);
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (PCINDEX, SZ_UNDEF));
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ nextword |= 0x40; /* No index reg. */
+ if (opP->index.reg >= ZDATA0
+ && opP->index.reg <= ZDATA7)
+ nextword |= (opP->index.reg - ZDATA0) << 12;
+ else if (opP->index.reg >= ZADDR0
+ || opP->index.reg <= ZADDR7)
+ nextword |= (opP->index.reg - ZADDR0 + 8) << 12;
+ }
+
+ /* It isn't simple. */
+
+ if (cpu_of_arch (current_architecture) < m68020
+ || arch_coldfire_p (current_architecture))
+ opP->error =
+ _("invalid operand mode for this architecture; needs 68020 or higher");
+
+ nextword |= 0x100;
+ /* If the guy specified a width, we assume that it is
+ wide enough. Maybe it isn't. If so, we lose. */
+ switch (siz1)
+ {
+ case SIZE_UNSPEC:
+ if (isvar (&opP->disp)
+ ? m68k_rel32
+ : ! issword (baseo))
+ {
+ siz1 = SIZE_LONG;
+ nextword |= 0x30;
+ }
+ else if (! isvar (&opP->disp) && baseo == 0)
+ nextword |= 0x10;
+ else
+ {
+ nextword |= 0x20;
+ siz1 = SIZE_WORD;
+ }
+ break;
+ case SIZE_BYTE:
+ as_warn (_(":b not permitted; defaulting to :w"));
+ /* Fall through. */
+ case SIZE_WORD:
+ nextword |= 0x20;
+ break;
+ case SIZE_LONG:
+ nextword |= 0x30;
+ break;
+ }
+
+ /* Figure out inner displacement stuff. */
+ if (opP->mode == POST || opP->mode == PRE)
+ {
+ if (cpu_of_arch (current_architecture) & cpu32)
+ opP->error = _("invalid operand mode for this architecture; needs 68020 or higher");
+ switch (siz2)
+ {
+ case SIZE_UNSPEC:
+ if (isvar (&opP->odisp)
+ ? m68k_rel32
+ : ! issword (outro))
+ {
+ siz2 = SIZE_LONG;
+ nextword |= 0x3;
+ }
+ else if (! isvar (&opP->odisp) && outro == 0)
+ nextword |= 0x1;
+ else
+ {
+ nextword |= 0x2;
+ siz2 = SIZE_WORD;
+ }
+ break;
+ case 1:
+ as_warn (_(":b not permitted; defaulting to :w"));
+ /* Fall through. */
+ case 2:
+ nextword |= 0x2;
+ break;
+ case 3:
+ nextword |= 0x3;
+ break;
+ }
+ if (opP->mode == POST
+ && (nextword & 0x40) == 0)
+ nextword |= 0x04;
+ }
+ addword (nextword);
+
+ if (siz1 != SIZE_UNSPEC && isvar (&opP->disp))
+ {
+ if (opP->reg == PC || opP->reg == ZPC)
+ add_fix (siz1 == SIZE_LONG ? 'l' : 'w', &opP->disp, 1, 2);
+ else
+ add_fix (siz1 == SIZE_LONG ? 'l' : 'w', &opP->disp, 0, 0);
+ }
+ if (siz1 == SIZE_LONG)
+ addword (baseo >> 16);
+ if (siz1 != SIZE_UNSPEC)
+ addword (baseo);
+
+ if (siz2 != SIZE_UNSPEC && isvar (&opP->odisp))
+ add_fix (siz2 == SIZE_LONG ? 'l' : 'w', &opP->odisp, 0, 0);
+ if (siz2 == SIZE_LONG)
+ addword (outro >> 16);
+ if (siz2 != SIZE_UNSPEC)
+ addword (outro);
+
+ break;
+
+ case ABSL:
+ nextword = get_num (&opP->disp, 90);
+ switch (opP->disp.size)
+ {
+ default:
+ abort ();
+ case SIZE_UNSPEC:
+ if (!isvar (&opP->disp) && issword (offs (&opP->disp)))
+ {
+ tmpreg = 0x38; /* 7.0 */
+ addword (nextword);
+ break;
+ }
+ if (isvar (&opP->disp)
+ && !subs (&opP->disp)
+ && adds (&opP->disp)
+#ifdef OBJ_ELF
+ /* If the displacement needs pic relocation it
+ cannot be relaxed. */
+ && opP->disp.pic_reloc == pic_none
+#endif
+ && !flag_long_jumps
+ && !strchr ("~%&$?", s[0]))
+ {
+ tmpreg = 0x3A; /* 7.2 */
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (ABSTOPCREL, SZ_UNDEF));
+ break;
+ }
+ /* Fall through into long. */
+ case SIZE_LONG:
+ if (isvar (&opP->disp))
+ add_fix ('l', &opP->disp, 0, 0);
+
+ tmpreg = 0x39;/* 7.1 mode */
+ addword (nextword >> 16);
+ addword (nextword);
+ break;
+
+ case SIZE_BYTE:
+ as_bad (_("unsupported byte value; use a different suffix"));
+ /* Fall through. */
+
+ case SIZE_WORD:
+ if (isvar (&opP->disp))
+ add_fix ('w', &opP->disp, 0, 0);
+
+ tmpreg = 0x38;/* 7.0 mode */
+ addword (nextword);
+ break;
+ }
+ break;
+ case CONTROL:
+ case FPREG:
+ default:
+ as_bad (_("unknown/incorrect operand"));
+ /* abort (); */
+ }
+
+ /* If s[0] is '4', then this is for the mac instructions
+ that can have a trailing_ampersand set. If so, set 0x100
+ bit on tmpreg so install_gen_operand can check for it and
+ set the appropriate bit (word2, bit 5). */
+ if (s[0] == '4')
+ {
+ if (opP->trailing_ampersand)
+ tmpreg |= 0x100;
+ }
+ install_gen_operand (s[1], tmpreg);
+ break;
+
+ case '#':
+ case '^':
+ switch (s[1])
+ { /* JF: I hate floating point! */
+ case 'j':
+ tmpreg = 70;
+ break;
+ case '8':
+ tmpreg = 20;
+ break;
+ case 'C':
+ tmpreg = 50;
+ break;
+ case '3':
+ default:
+ tmpreg = 90;
+ break;
+ }
+ tmpreg = get_num (&opP->disp, tmpreg);
+ if (isvar (&opP->disp))
+ add_fix (s[1], &opP->disp, 0, 0);
+ switch (s[1])
+ {
+ case 'b': /* Danger: These do no check for
+ certain types of overflow.
+ user beware! */
+ if (!isbyte (tmpreg))
+ opP->error = _("out of range");
+ insop (tmpreg, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n =
+ (opcode->m_codenum) * 2 + 1;
+ break;
+ case 'B':
+ if (!issbyte (tmpreg))
+ opP->error = _("out of range");
+ the_ins.opcode[the_ins.numo - 1] |= tmpreg & 0xff;
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = opcode->m_codenum * 2 - 1;
+ break;
+ case 'w':
+ if (!isword (tmpreg))
+ opP->error = _("out of range");
+ insop (tmpreg, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2;
+ break;
+ case 'W':
+ if (!issword (tmpreg))
+ opP->error = _("out of range");
+ insop (tmpreg, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2;
+ break;
+ case 'l':
+ /* Because of the way insop works, we put these two out
+ backwards. */
+ insop (tmpreg, opcode);
+ insop (tmpreg >> 16, opcode);
+ if (isvar (&opP->disp))
+ the_ins.reloc[the_ins.nrel - 1].n = (opcode->m_codenum) * 2;
+ break;
+ case '3':
+ tmpreg &= 0xFF;
+ case '8':
+ case 'C':
+ case 'j':
+ install_operand (s[1], tmpreg);
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case '+':
+ case '-':
+ case 'A':
+ case 'a':
+ install_operand (s[1], opP->reg - ADDR);
+ break;
+
+ case 'B':
+ tmpreg = get_num (&opP->disp, 90);
+
+ switch (s[1])
+ {
+ case 'B':
+ add_fix ('B', &opP->disp, 1, -1);
+ break;
+ case 'W':
+ add_fix ('w', &opP->disp, 1, 0);
+ addword (0);
+ break;
+ case 'L':
+ long_branch:
+ the_ins.opcode[0] |= 0xff;
+ add_fix ('l', &opP->disp, 1, 0);
+ addword (0);
+ addword (0);
+ break;
+ case 'g': /* Conditional branch */
+ have_disp = HAVE_LONG_CALL (current_architecture);
+ goto var_branch;
+
+ case 'b': /* Unconditional branch */
+ have_disp = HAVE_LONG_BRANCH (current_architecture);
+ use_pl = LONG_BRANCH_VIA_COND (current_architecture);
+ goto var_branch;
+
+ case 's': /* Unconditional subroutine */
+ have_disp = HAVE_LONG_CALL (current_architecture);
+
+ var_branch:
+ if (subs (&opP->disp) /* We can't relax it. */
+#ifdef OBJ_ELF
+ /* If the displacement needs pic relocation it cannot be
+ relaxed. */
+ || opP->disp.pic_reloc != pic_none
+#endif
+ || 0)
+ {
+ if (!have_disp)
+ as_warn (_("Can't use long branches on this architecture"));
+ goto long_branch;
+ }
+
+ /* This could either be a symbol, or an absolute
+ address. If it's an absolute address, turn it into
+ an absolute jump right here and keep it out of the
+ relaxer. */
+ if (adds (&opP->disp) == 0)
+ {
+ if (the_ins.opcode[0] == 0x6000) /* jbra */
+ the_ins.opcode[0] = 0x4EF9;
+ else if (the_ins.opcode[0] == 0x6100) /* jbsr */
+ the_ins.opcode[0] = 0x4EB9;
+ else /* jCC */
+ {
+ the_ins.opcode[0] ^= 0x0100;
+ the_ins.opcode[0] |= 0x0006;
+ addword (0x4EF9);
+ }
+ add_fix ('l', &opP->disp, 0, 0);
+ addword (0);
+ addword (0);
+ break;
+ }
+
+ /* Now we know it's going into the relaxer. Now figure
+ out which mode. We try in this order of preference:
+ long branch, absolute jump, byte/word branches only. */
+ if (have_disp)
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (BRANCHBWL, SZ_UNDEF));
+ else if (! flag_keep_pcrel)
+ {
+ if ((the_ins.opcode[0] == 0x6000)
+ || (the_ins.opcode[0] == 0x6100))
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (BRABSJUNC, SZ_UNDEF));
+ else
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (BRABSJCOND, SZ_UNDEF));
+ }
+ else
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ (use_pl ? TAB (BRANCHBWPL, SZ_UNDEF)
+ : TAB (BRANCHBW, SZ_UNDEF)));
+ break;
+ case 'w':
+ if (isvar (&opP->disp))
+ {
+ /* Check for DBcc instructions. We can relax them,
+ but only if we have long branches and/or absolute
+ jumps. */
+ if (((the_ins.opcode[0] & 0xf0f8) == 0x50c8)
+ && (HAVE_LONG_BRANCH (current_architecture)
+ || ! flag_keep_pcrel))
+ {
+ if (HAVE_LONG_BRANCH (current_architecture))
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (DBCCLBR, SZ_UNDEF));
+ else
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (DBCCABSJ, SZ_UNDEF));
+ break;
+ }
+ add_fix ('w', &opP->disp, 1, 0);
+ }
+ addword (0);
+ break;
+ case 'C': /* Fixed size LONG coproc branches. */
+ add_fix ('l', &opP->disp, 1, 0);
+ addword (0);
+ addword (0);
+ break;
+ case 'c': /* Var size Coprocesssor branches. */
+ if (subs (&opP->disp) || (adds (&opP->disp) == 0))
+ {
+ the_ins.opcode[the_ins.numo - 1] |= 0x40;
+ add_fix ('l', &opP->disp, 1, 0);
+ addword (0);
+ addword (0);
+ }
+ else
+ add_frag (adds (&opP->disp),
+ SEXT (offs (&opP->disp)),
+ TAB (FBRANCH, SZ_UNDEF));
+ break;
+ default:
+ abort ();
+ }
+ break;
+
+ case 'C': /* Ignore it. */
+ break;
+
+ case 'd': /* JF this is a kludge. */
+ install_operand ('s', opP->reg - ADDR);
+ tmpreg = get_num (&opP->disp, 90);
+ if (!issword (tmpreg))
+ {
+ as_warn (_("Expression out of range, using 0"));
+ tmpreg = 0;
+ }
+ addword (tmpreg);
+ break;
+
+ case 'D':
+ install_operand (s[1], opP->reg - DATA);
+ break;
+
+ case 'e': /* EMAC ACCx, reg/reg. */
+ install_operand (s[1], opP->reg - ACC);
+ break;
+
+ case 'E': /* Ignore it. */
+ break;
+
+ case 'F':
+ install_operand (s[1], opP->reg - FP0);
+ break;
+
+ case 'g': /* EMAC ACCEXTx. */
+ install_operand (s[1], opP->reg - ACCEXT01);
+ break;
+
+ case 'G': /* Ignore it. */
+ case 'H':
+ break;
+
+ case 'I':
+ tmpreg = opP->reg - COP0;
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'i': /* MAC/EMAC scale factor. */
+ install_operand (s[1], opP->mode == LSH ? 0x1 : 0x3);
+ break;
+
+ case 'J': /* JF foo. */
+ switch (opP->reg)
+ {
+ case SFC:
+ tmpreg = 0x000;
+ break;
+ case DFC:
+ tmpreg = 0x001;
+ break;
+ case CACR:
+ tmpreg = 0x002;
+ break;
+ case TC:
+ case ASID:
+ tmpreg = 0x003;
+ break;
+ case ACR0:
+ case ITT0:
+ tmpreg = 0x004;
+ break;
+ case ACR1:
+ case ITT1:
+ tmpreg = 0x005;
+ break;
+ case ACR2:
+ case DTT0:
+ tmpreg = 0x006;
+ break;
+ case ACR3:
+ case DTT1:
+ tmpreg = 0x007;
+ break;
+ case BUSCR:
+ case MMUBAR:
+ tmpreg = 0x008;
+ break;
+ case RGPIOBAR:
+ tmpreg = 0x009;
+ break;
+ case ACR4:
+ case ACR5:
+ case ACR6:
+ case ACR7:
+ tmpreg = 0x00c + (opP->reg - ACR4);
+ break;
+
+ case USP:
+ tmpreg = 0x800;
+ break;
+ case VBR:
+ tmpreg = 0x801;
+ break;
+ case CAAR:
+ case CPUCR:
+ tmpreg = 0x802;
+ break;
+ case MSP:
+ tmpreg = 0x803;
+ break;
+ case ISP:
+ tmpreg = 0x804;
+ break;
+ case MMUSR:
+ tmpreg = 0x805;
+ break;
+ case URP:
+ tmpreg = 0x806;
+ break;
+ case SRP:
+ tmpreg = 0x807;
+ break;
+ case PCR:
+ tmpreg = 0x808;
+ break;
+ case ROMBAR:
+ case ROMBAR0:
+ tmpreg = 0xC00;
+ break;
+ case ROMBAR1:
+ tmpreg = 0xC01;
+ break;
+ case FLASHBAR:
+ case RAMBAR0:
+ case RAMBAR_ALT:
+ tmpreg = 0xC04;
+ break;
+ case RAMBAR:
+ case RAMBAR1:
+ tmpreg = 0xC05;
+ break;
+ case MPCR:
+ tmpreg = 0xC0C;
+ break;
+ case EDRAMBAR:
+ tmpreg = 0xC0D;
+ break;
+ case MBAR0:
+ case MBAR2:
+ case SECMBAR:
+ tmpreg = 0xC0E;
+ break;
+ case MBAR1:
+ case MBAR:
+ tmpreg = 0xC0F;
+ break;
+ case PCR1U0:
+ tmpreg = 0xD02;
+ break;
+ case PCR1L0:
+ tmpreg = 0xD03;
+ break;
+ case PCR2U0:
+ tmpreg = 0xD04;
+ break;
+ case PCR2L0:
+ tmpreg = 0xD05;
+ break;
+ case PCR3U0:
+ tmpreg = 0xD06;
+ break;
+ case PCR3L0:
+ tmpreg = 0xD07;
+ break;
+ case PCR1L1:
+ tmpreg = 0xD0A;
+ break;
+ case PCR1U1:
+ tmpreg = 0xD0B;
+ break;
+ case PCR2L1:
+ tmpreg = 0xD0C;
+ break;
+ case PCR2U1:
+ tmpreg = 0xD0D;
+ break;
+ case PCR3L1:
+ tmpreg = 0xD0E;
+ break;
+ case PCR3U1:
+ tmpreg = 0xD0F;
+ break;
+ case CAC:
+ tmpreg = 0xFFE;
+ break;
+ case MBO:
+ tmpreg = 0xFFF;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'k':
+ tmpreg = get_num (&opP->disp, 55);
+ install_operand (s[1], tmpreg & 0x7f);
+ break;
+
+ case 'l':
+ tmpreg = opP->mask;
+ if (s[1] == 'w')
+ {
+ if (tmpreg & 0x7FF0000)
+ as_bad (_("Floating point register in register list"));
+ insop (reverse_16_bits (tmpreg), opcode);
+ }
+ else
+ {
+ if (tmpreg & 0x700FFFF)
+ as_bad (_("Wrong register in floating-point reglist"));
+ install_operand (s[1], reverse_8_bits (tmpreg >> 16));
+ }
+ break;
+
+ case 'L':
+ tmpreg = opP->mask;
+ if (s[1] == 'w')
+ {
+ if (tmpreg & 0x7FF0000)
+ as_bad (_("Floating point register in register list"));
+ insop (tmpreg, opcode);
+ }
+ else if (s[1] == '8')
+ {
+ if (tmpreg & 0x0FFFFFF)
+ as_bad (_("incorrect register in reglist"));
+ install_operand (s[1], tmpreg >> 24);
+ }
+ else
+ {
+ if (tmpreg & 0x700FFFF)
+ as_bad (_("wrong register in floating-point reglist"));
+ else
+ install_operand (s[1], tmpreg >> 16);
+ }
+ break;
+
+ case 'M':
+ install_operand (s[1], get_num (&opP->disp, 60));
+ break;
+
+ case 'O':
+ tmpreg = ((opP->mode == DREG)
+ ? 0x20 + (int) (opP->reg - DATA)
+ : (get_num (&opP->disp, 40) & 0x1F));
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'Q':
+ tmpreg = get_num (&opP->disp, 10);
+ if (tmpreg == 8)
+ tmpreg = 0;
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'R':
+ /* This depends on the fact that ADDR registers are eight
+ more than their corresponding DATA regs, so the result
+ will have the ADDR_REG bit set. */
+ install_operand (s[1], opP->reg - DATA);
+ break;
+
+ case 'r':
+ if (opP->mode == AINDR)
+ install_operand (s[1], opP->reg - DATA);
+ else
+ install_operand (s[1], opP->index.reg - DATA);
+ break;
+
+ case 's':
+ if (opP->reg == FPI)
+ tmpreg = 0x1;
+ else if (opP->reg == FPS)
+ tmpreg = 0x2;
+ else if (opP->reg == FPC)
+ tmpreg = 0x4;
+ else
+ abort ();
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'S': /* Ignore it. */
+ break;
+
+ case 'T':
+ install_operand (s[1], get_num (&opP->disp, 30));
+ break;
+
+ case 'U': /* Ignore it. */
+ break;
+
+ case 'c':
+ switch (opP->reg)
+ {
+ case NC:
+ tmpreg = 0;
+ break;
+ case DC:
+ tmpreg = 1;
+ break;
+ case IC:
+ tmpreg = 2;
+ break;
+ case BC:
+ tmpreg = 3;
+ break;
+ default:
+ as_fatal (_("failed sanity check"));
+ } /* switch on cache token. */
+ install_operand (s[1], tmpreg);
+ break;
+#ifndef NO_68851
+ /* JF: These are out of order, I fear. */
+ case 'f':
+ switch (opP->reg)
+ {
+ case SFC:
+ tmpreg = 0;
+ break;
+ case DFC:
+ tmpreg = 1;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case '0':
+ case '1':
+ case '2':
+ switch (opP->reg)
+ {
+ case TC:
+ tmpreg = 0;
+ break;
+ case CAL:
+ tmpreg = 4;
+ break;
+ case VAL:
+ tmpreg = 5;
+ break;
+ case SCC:
+ tmpreg = 6;
+ break;
+ case AC:
+ tmpreg = 7;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'V':
+ if (opP->reg == VAL)
+ break;
+ abort ();
+
+ case 'W':
+ switch (opP->reg)
+ {
+ case DRP:
+ tmpreg = 1;
+ break;
+ case SRP:
+ tmpreg = 2;
+ break;
+ case CRP:
+ tmpreg = 3;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+
+ case 'X':
+ switch (opP->reg)
+ {
+ case BAD:
+ case BAD + 1:
+ case BAD + 2:
+ case BAD + 3:
+ case BAD + 4:
+ case BAD + 5:
+ case BAD + 6:
+ case BAD + 7:
+ tmpreg = (4 << 10) | ((opP->reg - BAD) << 2);
+ break;
+
+ case BAC:
+ case BAC + 1:
+ case BAC + 2:
+ case BAC + 3:
+ case BAC + 4:
+ case BAC + 5:
+ case BAC + 6:
+ case BAC + 7:
+ tmpreg = (5 << 10) | ((opP->reg - BAC) << 2);
+ break;
+
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+ case 'Y':
+ know (opP->reg == PSR);
+ break;
+ case 'Z':
+ know (opP->reg == PCSR);
+ break;
+#endif /* m68851 */
+ case '3':
+ switch (opP->reg)
+ {
+ case TT0:
+ tmpreg = 2;
+ break;
+ case TT1:
+ tmpreg = 3;
+ break;
+ default:
+ abort ();
+ }
+ install_operand (s[1], tmpreg);
+ break;
+ case 't':
+ tmpreg = get_num (&opP->disp, 20);
+ install_operand (s[1], tmpreg);
+ break;
+ case '_': /* used only for move16 absolute 32-bit address. */
+ if (isvar (&opP->disp))
+ add_fix ('l', &opP->disp, 0, 0);
+ tmpreg = get_num (&opP->disp, 90);
+ addword (tmpreg >> 16);
+ addword (tmpreg & 0xFFFF);
+ break;
+ case 'u':
+ install_operand (s[1], opP->reg - DATA0L);
+ opP->reg -= (DATA0L);
+ opP->reg &= 0x0F; /* remove upper/lower bit. */
+ break;
+ case 'x':
+ tmpreg = get_num (&opP->disp, 80);
+ if (tmpreg == -1)
+ tmpreg = 0;
+ install_operand (s[1], tmpreg);
+ break;
+ case 'j':
+ tmpreg = get_num (&opP->disp, 10);
+ install_operand (s[1], tmpreg - 1);
+ break;
+ case 'K':
+ tmpreg = get_num (&opP->disp, 65);
+ install_operand (s[1], tmpreg);
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ /* By the time whe get here (FINALLY) the_ins contains the complete
+ instruction, ready to be emitted. . . */
+}
+
+static int
+reverse_16_bits (int in)
+{
+ int out = 0;
+ int n;
+
+ static int mask[16] =
+ {
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000
+ };
+ for (n = 0; n < 16; n++)
+ {
+ if (in & mask[n])
+ out |= mask[15 - n];
+ }
+ return out;
+} /* reverse_16_bits() */
+
+static int
+reverse_8_bits (int in)
+{
+ int out = 0;
+ int n;
+
+ static int mask[8] =
+ {
+ 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080,
+ };
+
+ for (n = 0; n < 8; n++)
+ {
+ if (in & mask[n])
+ out |= mask[7 - n];
+ }
+ return out;
+} /* reverse_8_bits() */
+
+/* Cause an extra frag to be generated here, inserting up to
+ FRAG_VAR_SIZE bytes. TYPE is the subtype of the frag to be
+ generated; its primary type is rs_machine_dependent.
+
+ The TYPE parameter is also used by md_convert_frag_1 and
+ md_estimate_size_before_relax. The appropriate type of fixup will
+ be emitted by md_convert_frag_1.
+
+ ADD becomes the FR_SYMBOL field of the frag, and OFF the FR_OFFSET. */
+static void
+install_operand (int mode, int val)
+{
+ switch (mode)
+ {
+ case 's':
+ the_ins.opcode[0] |= val & 0xFF; /* JF FF is for M kludge. */
+ break;
+ case 'd':
+ the_ins.opcode[0] |= val << 9;
+ break;
+ case 'E':
+ the_ins.opcode[1] |= val << 9;
+ break;
+ case '1':
+ the_ins.opcode[1] |= val << 12;
+ break;
+ case '2':
+ the_ins.opcode[1] |= val << 6;
+ break;
+ case '3':
+ the_ins.opcode[1] |= val;
+ break;
+ case '4':
+ the_ins.opcode[2] |= val << 12;
+ break;
+ case '5':
+ the_ins.opcode[2] |= val << 6;
+ break;
+ case '6':
+ /* DANGER! This is a hack to force cas2l and cas2w cmds to be
+ three words long! */
+ the_ins.numo++;
+ the_ins.opcode[2] |= val;
+ break;
+ case '7':
+ the_ins.opcode[1] |= val << 7;
+ break;
+ case '8':
+ the_ins.opcode[1] |= val << 10;
+ break;
+#ifndef NO_68851
+ case '9':
+ the_ins.opcode[1] |= val << 5;
+ break;
+#endif
+
+ case 't':
+ the_ins.opcode[1] |= (val << 10) | (val << 7);
+ break;
+ case 'D':
+ the_ins.opcode[1] |= (val << 12) | val;
+ break;
+ case 'g':
+ the_ins.opcode[0] |= val = 0xff;
+ break;
+ case 'i':
+ the_ins.opcode[0] |= val << 9;
+ break;
+ case 'C':
+ the_ins.opcode[1] |= val;
+ break;
+ case 'j':
+ the_ins.opcode[1] |= val;
+ the_ins.numo++; /* What a hack. */
+ break;
+ case 'k':
+ the_ins.opcode[1] |= val << 4;
+ break;
+ case 'b':
+ case 'w':
+ case 'W':
+ case 'l':
+ break;
+ case 'e':
+ the_ins.opcode[0] |= (val << 6);
+ break;
+ case 'L':
+ the_ins.opcode[1] = (val >> 16);
+ the_ins.opcode[2] = val & 0xffff;
+ break;
+ case 'm':
+ the_ins.opcode[0] |= ((val & 0x8) << (6 - 3));
+ the_ins.opcode[0] |= ((val & 0x7) << 9);
+ the_ins.opcode[1] |= ((val & 0x10) << (7 - 4));
+ break;
+ case 'n': /* MAC/EMAC Rx on !load. */
+ the_ins.opcode[0] |= ((val & 0x8) << (6 - 3));
+ the_ins.opcode[0] |= ((val & 0x7) << 9);
+ the_ins.opcode[1] |= ((val & 0x10) << (7 - 4));
+ break;
+ case 'o': /* MAC/EMAC Rx on load. */
+ the_ins.opcode[1] |= val << 12;
+ the_ins.opcode[1] |= ((val & 0x10) << (7 - 4));
+ break;
+ case 'M': /* MAC/EMAC Ry on !load. */
+ the_ins.opcode[0] |= (val & 0xF);
+ the_ins.opcode[1] |= ((val & 0x10) << (6 - 4));
+ break;
+ case 'N': /* MAC/EMAC Ry on load. */
+ the_ins.opcode[1] |= (val & 0xF);
+ the_ins.opcode[1] |= ((val & 0x10) << (6 - 4));
+ break;
+ case 'h':
+ the_ins.opcode[1] |= ((val != 1) << 10);
+ break;
+ case 'F':
+ the_ins.opcode[0] |= ((val & 0x3) << 9);
+ break;
+ case 'f':
+ the_ins.opcode[0] |= ((val & 0x3) << 0);
+ break;
+ case 'G': /* EMAC accumulator in a EMAC load instruction. */
+ the_ins.opcode[0] |= ((~val & 0x1) << 7);
+ the_ins.opcode[1] |= ((val & 0x2) << (4 - 1));
+ break;
+ case 'H': /* EMAC accumulator in a EMAC non-load instruction. */
+ the_ins.opcode[0] |= ((val & 0x1) << 7);
+ the_ins.opcode[1] |= ((val & 0x2) << (4 - 1));
+ break;
+ case 'I':
+ the_ins.opcode[1] |= ((val & 0x3) << 9);
+ break;
+ case ']':
+ the_ins.opcode[0] |= (val & 0x1) <<10;
+ break;
+ case 'c':
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+}
+
+static void
+install_gen_operand (int mode, int val)
+{
+ switch (mode)
+ {
+ case '/': /* Special for mask loads for mac/msac insns with
+ possible mask; trailing_ampersend set in bit 8. */
+ the_ins.opcode[0] |= (val & 0x3f);
+ the_ins.opcode[1] |= (((val & 0x100) >> 8) << 5);
+ break;
+ case 's':
+ the_ins.opcode[0] |= val;
+ break;
+ case 'd':
+ /* This is a kludge!!! */
+ the_ins.opcode[0] |= (val & 0x07) << 9 | (val & 0x38) << 3;
+ break;
+ case 'b':
+ case 'w':
+ case 'l':
+ case 'f':
+ case 'F':
+ case 'x':
+ case 'p':
+ the_ins.opcode[0] |= val;
+ break;
+ /* more stuff goes here. */
+ default:
+ as_fatal (_("failed sanity check."));
+ }
+}
+
+/* Verify that we have some number of paren pairs, do m68k_ip_op(), and
+ then deal with the bitfield hack. */
+
+static char *
+crack_operand (char *str, struct m68k_op *opP)
+{
+ register int parens;
+ register int c;
+ register char *beg_str;
+ int inquote = 0;
+
+ if (!str)
+ {
+ return str;
+ }
+ beg_str = str;
+ for (parens = 0; *str && (parens > 0 || inquote || notend (str)); str++)
+ {
+ if (! inquote)
+ {
+ if (*str == '(')
+ parens++;
+ else if (*str == ')')
+ {
+ if (!parens)
+ { /* ERROR. */
+ opP->error = _("Extra )");
+ return str;
+ }
+ --parens;
+ }
+ }
+ if (flag_mri && *str == '\'')
+ inquote = ! inquote;
+ }
+ if (!*str && parens)
+ { /* ERROR. */
+ opP->error = _("Missing )");
+ return str;
+ }
+ c = *str;
+ *str = '\0';
+ if (m68k_ip_op (beg_str, opP) != 0)
+ {
+ *str = c;
+ return str;
+ }
+ *str = c;
+ if (c == '}')
+ c = *++str; /* JF bitfield hack. */
+ if (c)
+ {
+ c = *++str;
+ if (!c)
+ as_bad (_("Missing operand"));
+ }
+
+ /* Detect MRI REG symbols and convert them to REGLSTs. */
+ if (opP->mode == CONTROL && (int)opP->reg < 0)
+ {
+ opP->mode = REGLST;
+ opP->mask = ~(int)opP->reg;
+ opP->reg = 0;
+ }
+
+ return str;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to.
+ */
+
+static void
+insert_reg (const char *regname, int regnum)
+{
+ char buf[100];
+ int i;
+
+#ifdef REGISTER_PREFIX
+ if (!flag_reg_prefix_optional)
+ {
+ buf[0] = REGISTER_PREFIX;
+ strcpy (buf + 1, regname);
+ regname = buf;
+ }
+#endif
+
+ symbol_table_insert (symbol_new (regname, reg_section, regnum,
+ &zero_address_frag));
+
+ for (i = 0; regname[i]; i++)
+ buf[i] = TOUPPER (regname[i]);
+ buf[i] = '\0';
+
+ symbol_table_insert (symbol_new (buf, reg_section, regnum,
+ &zero_address_frag));
+}
+
+struct init_entry
+ {
+ const char *name;
+ int number;
+ };
+
+static const struct init_entry init_table[] =
+{
+ { "d0", DATA0 },
+ { "d1", DATA1 },
+ { "d2", DATA2 },
+ { "d3", DATA3 },
+ { "d4", DATA4 },
+ { "d5", DATA5 },
+ { "d6", DATA6 },
+ { "d7", DATA7 },
+ { "a0", ADDR0 },
+ { "a1", ADDR1 },
+ { "a2", ADDR2 },
+ { "a3", ADDR3 },
+ { "a4", ADDR4 },
+ { "a5", ADDR5 },
+ { "a6", ADDR6 },
+ { "fp", ADDR6 },
+ { "a7", ADDR7 },
+ { "sp", ADDR7 },
+ { "ssp", ADDR7 },
+ { "fp0", FP0 },
+ { "fp1", FP1 },
+ { "fp2", FP2 },
+ { "fp3", FP3 },
+ { "fp4", FP4 },
+ { "fp5", FP5 },
+ { "fp6", FP6 },
+ { "fp7", FP7 },
+ { "fpi", FPI },
+ { "fpiar", FPI },
+ { "fpc", FPI },
+ { "fps", FPS },
+ { "fpsr", FPS },
+ { "fpc", FPC },
+ { "fpcr", FPC },
+ { "control", FPC },
+ { "status", FPS },
+ { "iaddr", FPI },
+
+ { "cop0", COP0 },
+ { "cop1", COP1 },
+ { "cop2", COP2 },
+ { "cop3", COP3 },
+ { "cop4", COP4 },
+ { "cop5", COP5 },
+ { "cop6", COP6 },
+ { "cop7", COP7 },
+ { "pc", PC },
+ { "zpc", ZPC },
+ { "sr", SR },
+
+ { "ccr", CCR },
+ { "cc", CCR },
+
+ { "acc", ACC },
+ { "acc0", ACC },
+ { "acc1", ACC1 },
+ { "acc2", ACC2 },
+ { "acc3", ACC3 },
+ { "accext01", ACCEXT01 },
+ { "accext23", ACCEXT23 },
+ { "macsr", MACSR },
+ { "mask", MASK },
+
+ /* Control registers. */
+ { "sfc", SFC }, /* Source Function Code. */
+ { "sfcr", SFC },
+ { "dfc", DFC }, /* Destination Function Code. */
+ { "dfcr", DFC },
+ { "cacr", CACR }, /* Cache Control Register. */
+ { "caar", CAAR }, /* Cache Address Register. */
+ { "cpucr", CPUCR }, /* CPU Control Register. */
+
+ { "usp", USP }, /* User Stack Pointer. */
+ { "vbr", VBR }, /* Vector Base Register. */
+ { "msp", MSP }, /* Master Stack Pointer. */
+ { "isp", ISP }, /* Interrupt Stack Pointer. */
+
+ { "itt0", ITT0 }, /* Instruction Transparent Translation Reg 0. */
+ { "itt1", ITT1 }, /* Instruction Transparent Translation Reg 1. */
+ { "dtt0", DTT0 }, /* Data Transparent Translation Register 0. */
+ { "dtt1", DTT1 }, /* Data Transparent Translation Register 1. */
+
+ /* 68ec040 versions of same */
+ { "iacr0", ITT0 }, /* Instruction Access Control Register 0. */
+ { "iacr1", ITT1 }, /* Instruction Access Control Register 0. */
+ { "dacr0", DTT0 }, /* Data Access Control Register 0. */
+ { "dacr1", DTT1 }, /* Data Access Control Register 0. */
+
+ /* Coldfire versions of same. The ColdFire programmer's reference
+ manual indicated that the order is 2,3,0,1, but Ken Rose
+ <rose@netcom.com> says that 0,1,2,3 is the correct order. */
+ { "acr0", ACR0 }, /* Access Control Unit 0. */
+ { "acr1", ACR1 }, /* Access Control Unit 1. */
+ { "acr2", ACR2 }, /* Access Control Unit 2. */
+ { "acr3", ACR3 }, /* Access Control Unit 3. */
+ { "acr4", ACR4 }, /* Access Control Unit 4. */
+ { "acr5", ACR5 }, /* Access Control Unit 5. */
+ { "acr6", ACR6 }, /* Access Control Unit 6. */
+ { "acr7", ACR7 }, /* Access Control Unit 7. */
+
+ { "tc", TC }, /* MMU Translation Control Register. */
+ { "tcr", TC },
+ { "asid", ASID },
+
+ { "mmusr", MMUSR }, /* MMU Status Register. */
+ { "srp", SRP }, /* User Root Pointer. */
+ { "urp", URP }, /* Supervisor Root Pointer. */
+
+ { "buscr", BUSCR },
+ { "mmubar", MMUBAR },
+ { "pcr", PCR },
+
+ { "rombar", ROMBAR }, /* ROM Base Address Register. */
+ { "rambar0", RAMBAR0 }, /* ROM Base Address Register. */
+ { "rambar1", RAMBAR1 }, /* ROM Base Address Register. */
+ { "mbar", MBAR }, /* Module Base Address Register. */
+
+ { "mbar0", MBAR0 }, /* mcfv4e registers. */
+ { "mbar1", MBAR1 }, /* mcfv4e registers. */
+ { "rombar0", ROMBAR0 }, /* mcfv4e registers. */
+ { "rombar1", ROMBAR1 }, /* mcfv4e registers. */
+ { "mpcr", MPCR }, /* mcfv4e registers. */
+ { "edrambar", EDRAMBAR }, /* mcfv4e registers. */
+ { "secmbar", SECMBAR }, /* mcfv4e registers. */
+ { "asid", TC }, /* mcfv4e registers. */
+ { "mmubar", BUSCR }, /* mcfv4e registers. */
+ { "pcr1u0", PCR1U0 }, /* mcfv4e registers. */
+ { "pcr1l0", PCR1L0 }, /* mcfv4e registers. */
+ { "pcr2u0", PCR2U0 }, /* mcfv4e registers. */
+ { "pcr2l0", PCR2L0 }, /* mcfv4e registers. */
+ { "pcr3u0", PCR3U0 }, /* mcfv4e registers. */
+ { "pcr3l0", PCR3L0 }, /* mcfv4e registers. */
+ { "pcr1u1", PCR1U1 }, /* mcfv4e registers. */
+ { "pcr1l1", PCR1L1 }, /* mcfv4e registers. */
+ { "pcr2u1", PCR2U1 }, /* mcfv4e registers. */
+ { "pcr2l1", PCR2L1 }, /* mcfv4e registers. */
+ { "pcr3u1", PCR3U1 }, /* mcfv4e registers. */
+ { "pcr3l1", PCR3L1 }, /* mcfv4e registers. */
+
+ { "flashbar", FLASHBAR }, /* mcf528x registers. */
+ { "rambar", RAMBAR }, /* mcf528x registers. */
+
+ { "mbar2", MBAR2 }, /* mcf5249 registers. */
+
+ { "rgpiobar", RGPIOBAR }, /* mcf54418 registers. */
+
+ { "cac", CAC }, /* fido registers. */
+ { "mbb", MBO }, /* fido registers (obsolete). */
+ { "mbo", MBO }, /* fido registers. */
+ /* End of control registers. */
+
+ { "ac", AC },
+ { "bc", BC },
+ { "cal", CAL },
+ { "crp", CRP },
+ { "drp", DRP },
+ { "pcsr", PCSR },
+ { "psr", PSR },
+ { "scc", SCC },
+ { "val", VAL },
+ { "bad0", BAD0 },
+ { "bad1", BAD1 },
+ { "bad2", BAD2 },
+ { "bad3", BAD3 },
+ { "bad4", BAD4 },
+ { "bad5", BAD5 },
+ { "bad6", BAD6 },
+ { "bad7", BAD7 },
+ { "bac0", BAC0 },
+ { "bac1", BAC1 },
+ { "bac2", BAC2 },
+ { "bac3", BAC3 },
+ { "bac4", BAC4 },
+ { "bac5", BAC5 },
+ { "bac6", BAC6 },
+ { "bac7", BAC7 },
+
+ { "ic", IC },
+ { "dc", DC },
+ { "nc", NC },
+
+ { "tt0", TT0 },
+ { "tt1", TT1 },
+ /* 68ec030 versions of same. */
+ { "ac0", TT0 },
+ { "ac1", TT1 },
+ /* 68ec030 access control unit, identical to 030 MMU status reg. */
+ { "acusr", PSR },
+
+ /* Suppressed data and address registers. */
+ { "zd0", ZDATA0 },
+ { "zd1", ZDATA1 },
+ { "zd2", ZDATA2 },
+ { "zd3", ZDATA3 },
+ { "zd4", ZDATA4 },
+ { "zd5", ZDATA5 },
+ { "zd6", ZDATA6 },
+ { "zd7", ZDATA7 },
+ { "za0", ZADDR0 },
+ { "za1", ZADDR1 },
+ { "za2", ZADDR2 },
+ { "za3", ZADDR3 },
+ { "za4", ZADDR4 },
+ { "za5", ZADDR5 },
+ { "za6", ZADDR6 },
+ { "za7", ZADDR7 },
+
+ /* Upper and lower data and address registers, used by macw and msacw. */
+ { "d0l", DATA0L },
+ { "d1l", DATA1L },
+ { "d2l", DATA2L },
+ { "d3l", DATA3L },
+ { "d4l", DATA4L },
+ { "d5l", DATA5L },
+ { "d6l", DATA6L },
+ { "d7l", DATA7L },
+
+ { "a0l", ADDR0L },
+ { "a1l", ADDR1L },
+ { "a2l", ADDR2L },
+ { "a3l", ADDR3L },
+ { "a4l", ADDR4L },
+ { "a5l", ADDR5L },
+ { "a6l", ADDR6L },
+ { "a7l", ADDR7L },
+
+ { "d0u", DATA0U },
+ { "d1u", DATA1U },
+ { "d2u", DATA2U },
+ { "d3u", DATA3U },
+ { "d4u", DATA4U },
+ { "d5u", DATA5U },
+ { "d6u", DATA6U },
+ { "d7u", DATA7U },
+
+ { "a0u", ADDR0U },
+ { "a1u", ADDR1U },
+ { "a2u", ADDR2U },
+ { "a3u", ADDR3U },
+ { "a4u", ADDR4U },
+ { "a5u", ADDR5U },
+ { "a6u", ADDR6U },
+ { "a7u", ADDR7U },
+
+ { 0, 0 }
+};
+
+static void
+init_regtable (void)
+{
+ int i;
+ for (i = 0; init_table[i].name; i++)
+ insert_reg (init_table[i].name, init_table[i].number);
+}
+
+void
+md_assemble (char *str)
+{
+ const char *er;
+ short *fromP;
+ char *toP = NULL;
+ int m, n = 0;
+ char *to_beg_P;
+ int shorts_this_frag;
+ fixS *fixP;
+
+ if (!selected_cpu && !selected_arch)
+ {
+ /* We've not selected an architecture yet. Set the default
+ now. We do this lazily so that an initial .cpu or .arch directive
+ can specify. */
+ if (!m68k_set_cpu (TARGET_CPU, 1, 1))
+ as_bad (_("unrecognized default cpu `%s'"), TARGET_CPU);
+ }
+ if (!initialized)
+ m68k_init_arch ();
+
+ /* In MRI mode, the instruction and operands are separated by a
+ space. Anything following the operands is a comment. The label
+ has already been removed. */
+ if (flag_mri)
+ {
+ char *s;
+ int fields = 0;
+ int infield = 0;
+ int inquote = 0;
+
+ for (s = str; *s != '\0'; s++)
+ {
+ if ((*s == ' ' || *s == '\t') && ! inquote)
+ {
+ if (infield)
+ {
+ ++fields;
+ if (fields >= 2)
+ {
+ *s = '\0';
+ break;
+ }
+ infield = 0;
+ }
+ }
+ else
+ {
+ if (! infield)
+ infield = 1;
+ if (*s == '\'')
+ inquote = ! inquote;
+ }
+ }
+ }
+
+ memset (&the_ins, '\0', sizeof (the_ins));
+ m68k_ip (str);
+ er = the_ins.error;
+ if (!er)
+ {
+ for (n = 0; n < the_ins.numargs; n++)
+ if (the_ins.operands[n].error)
+ {
+ er = the_ins.operands[n].error;
+ break;
+ }
+ }
+ if (er)
+ {
+ as_bad (_("%s -- statement `%s' ignored"), er, str);
+ return;
+ }
+
+ /* If there is a current label, record that it marks an instruction. */
+ if (current_label != NULL)
+ {
+ current_label->text = 1;
+ current_label = NULL;
+ }
+
+#ifdef OBJ_ELF
+ /* Tie dwarf2 debug info to the address at the start of the insn. */
+ dwarf2_emit_insn (0);
+#endif
+
+ if (the_ins.nfrag == 0)
+ {
+ /* No frag hacking involved; just put it out. */
+ toP = frag_more (2 * the_ins.numo);
+ fromP = &the_ins.opcode[0];
+ for (m = the_ins.numo; m; --m)
+ {
+ md_number_to_chars (toP, (long) (*fromP), 2);
+ toP += 2;
+ fromP++;
+ }
+ /* Put out symbol-dependent info. */
+ for (m = 0; m < the_ins.nrel; m++)
+ {
+ switch (the_ins.reloc[m].wid)
+ {
+ case 'B':
+ n = 1;
+ break;
+ case 'b':
+ n = 1;
+ break;
+ case '3':
+ n = 1;
+ break;
+ case 'w':
+ case 'W':
+ n = 2;
+ break;
+ case 'l':
+ n = 4;
+ break;
+ default:
+ as_fatal (_("Don't know how to figure width of %c in md_assemble()"),
+ the_ins.reloc[m].wid);
+ }
+
+ fixP = fix_new_exp (frag_now,
+ ((toP - frag_now->fr_literal)
+ - the_ins.numo * 2 + the_ins.reloc[m].n),
+ n,
+ &the_ins.reloc[m].exp,
+ the_ins.reloc[m].pcrel,
+ get_reloc_code (n, the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].pic_reloc));
+ fixP->fx_pcrel_adjust = the_ins.reloc[m].pcrel_fix;
+ if (the_ins.reloc[m].wid == 'B')
+ fixP->fx_signed = 1;
+ }
+ return;
+ }
+
+ /* There's some frag hacking. */
+ {
+ /* Calculate the max frag size. */
+ int wid;
+
+ wid = 2 * the_ins.fragb[0].fragoff;
+ for (n = 1; n < the_ins.nfrag; n++)
+ wid += 2 * (the_ins.numo - the_ins.fragb[n - 1].fragoff);
+ /* frag_var part. */
+ wid += FRAG_VAR_SIZE;
+ /* Make sure the whole insn fits in one chunk, in particular that
+ the var part is attached, as we access one byte before the
+ variable frag for byte branches. */
+ frag_grow (wid);
+ }
+
+ for (n = 0, fromP = &the_ins.opcode[0]; n < the_ins.nfrag; n++)
+ {
+ int wid;
+
+ if (n == 0)
+ wid = 2 * the_ins.fragb[n].fragoff;
+ else
+ wid = 2 * (the_ins.numo - the_ins.fragb[n - 1].fragoff);
+ toP = frag_more (wid);
+ to_beg_P = toP;
+ shorts_this_frag = 0;
+ for (m = wid / 2; m; --m)
+ {
+ md_number_to_chars (toP, (long) (*fromP), 2);
+ toP += 2;
+ fromP++;
+ shorts_this_frag++;
+ }
+ for (m = 0; m < the_ins.nrel; m++)
+ {
+ if ((the_ins.reloc[m].n) >= 2 * shorts_this_frag)
+ {
+ the_ins.reloc[m].n -= 2 * shorts_this_frag;
+ break;
+ }
+ wid = the_ins.reloc[m].wid;
+ if (wid == 0)
+ continue;
+ the_ins.reloc[m].wid = 0;
+ wid = (wid == 'b') ? 1 : (wid == 'w') ? 2 : (wid == 'l') ? 4 : 4000;
+
+ fixP = fix_new_exp (frag_now,
+ ((toP - frag_now->fr_literal)
+ - the_ins.numo * 2 + the_ins.reloc[m].n),
+ wid,
+ &the_ins.reloc[m].exp,
+ the_ins.reloc[m].pcrel,
+ get_reloc_code (wid, the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].pic_reloc));
+ fixP->fx_pcrel_adjust = the_ins.reloc[m].pcrel_fix;
+ }
+ (void) frag_var (rs_machine_dependent, FRAG_VAR_SIZE, 0,
+ (relax_substateT) (the_ins.fragb[n].fragty),
+ the_ins.fragb[n].fadd, the_ins.fragb[n].foff, to_beg_P);
+ }
+ gas_assert (the_ins.nfrag >= 1);
+ n = the_ins.numo - the_ins.fragb[the_ins.nfrag - 1].fragoff;
+ shorts_this_frag = 0;
+ if (n)
+ {
+ toP = frag_more (n * 2);
+ while (n--)
+ {
+ md_number_to_chars (toP, (long) (*fromP), 2);
+ toP += 2;
+ fromP++;
+ shorts_this_frag++;
+ }
+ }
+ for (m = 0; m < the_ins.nrel; m++)
+ {
+ int wid;
+
+ wid = the_ins.reloc[m].wid;
+ if (wid == 0)
+ continue;
+ the_ins.reloc[m].wid = 0;
+ wid = (wid == 'b') ? 1 : (wid == 'w') ? 2 : (wid == 'l') ? 4 : 4000;
+
+ fixP = fix_new_exp (frag_now,
+ ((the_ins.reloc[m].n + toP - frag_now->fr_literal)
+ - shorts_this_frag * 2),
+ wid,
+ &the_ins.reloc[m].exp,
+ the_ins.reloc[m].pcrel,
+ get_reloc_code (wid, the_ins.reloc[m].pcrel,
+ the_ins.reloc[m].pic_reloc));
+ fixP->fx_pcrel_adjust = the_ins.reloc[m].pcrel_fix;
+ }
+}
+
+/* Comparison function used by qsort to rank the opcode entries by name. */
+
+static int
+m68k_compare_opcode (const void * v1, const void * v2)
+{
+ struct m68k_opcode * op1, * op2;
+ int ret;
+
+ if (v1 == v2)
+ return 0;
+
+ op1 = *(struct m68k_opcode **) v1;
+ op2 = *(struct m68k_opcode **) v2;
+
+ /* Compare the two names. If different, return the comparison.
+ If the same, return the order they are in the opcode table. */
+ ret = strcmp (op1->name, op2->name);
+ if (ret)
+ return ret;
+ if (op1 < op2)
+ return -1;
+ return 1;
+}
+
+void
+md_begin (void)
+{
+ const struct m68k_opcode *ins;
+ struct m68k_incant *hack, *slak;
+ const char *retval = 0; /* Empty string, or error msg text. */
+ int i;
+
+ /* Set up hash tables with 68000 instructions.
+ similar to what the vax assembler does. */
+ /* RMS claims the thing to do is take the m68k-opcode.h table, and make
+ a copy of it at runtime, adding in the information we want but isn't
+ there. I think it'd be better to have an awk script hack the table
+ at compile time. Or even just xstr the table and use it as-is. But
+ my lord ghod hath spoken, so we do it this way. Excuse the ugly var
+ names. */
+
+ if (flag_mri)
+ {
+ flag_reg_prefix_optional = 1;
+ m68k_abspcadd = 1;
+ if (! m68k_rel32_from_cmdline)
+ m68k_rel32 = 0;
+ }
+
+ /* First sort the opcode table into alphabetical order to seperate
+ the order that the assembler wants to see the opcodes from the
+ order that the disassembler wants to see them. */
+ m68k_sorted_opcodes = xmalloc (m68k_numopcodes * sizeof (* m68k_sorted_opcodes));
+ if (!m68k_sorted_opcodes)
+ as_fatal (_("Internal Error: Can't allocate m68k_sorted_opcodes of size %d"),
+ m68k_numopcodes * ((int) sizeof (* m68k_sorted_opcodes)));
+
+ for (i = m68k_numopcodes; i--;)
+ m68k_sorted_opcodes[i] = m68k_opcodes + i;
+
+ qsort (m68k_sorted_opcodes, m68k_numopcodes,
+ sizeof (m68k_sorted_opcodes[0]), m68k_compare_opcode);
+
+ op_hash = hash_new ();
+
+ obstack_begin (&robyn, 4000);
+ for (i = 0; i < m68k_numopcodes; i++)
+ {
+ hack = slak = obstack_alloc (&robyn, sizeof (struct m68k_incant));
+ do
+ {
+ ins = m68k_sorted_opcodes[i];
+
+ /* We must enter all insns into the table, because .arch and
+ .cpu directives can change things. */
+ slak->m_operands = ins->args;
+ slak->m_arch = ins->arch;
+ slak->m_opcode = ins->opcode;
+
+ /* In most cases we can determine the number of opcode words
+ by checking the second word of the mask. Unfortunately
+ some instructions have 2 opcode words, but no fixed bits
+ in the second word. A leading dot in the operands
+ string also indicates 2 opcodes. */
+ if (*slak->m_operands == '.')
+ {
+ slak->m_operands++;
+ slak->m_codenum = 2;
+ }
+ else if (ins->match & 0xffffL)
+ slak->m_codenum = 2;
+ else
+ slak->m_codenum = 1;
+ slak->m_opnum = strlen (slak->m_operands) / 2;
+
+ if (i + 1 != m68k_numopcodes
+ && !strcmp (ins->name, m68k_sorted_opcodes[i + 1]->name))
+ {
+ slak->m_next = obstack_alloc (&robyn, sizeof (struct m68k_incant));
+ i++;
+ }
+ else
+ slak->m_next = 0;
+ slak = slak->m_next;
+ }
+ while (slak);
+
+ retval = hash_insert (op_hash, ins->name, (char *) hack);
+ if (retval)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), ins->name, retval);
+ }
+
+ for (i = 0; i < m68k_numaliases; i++)
+ {
+ const char *name = m68k_opcode_aliases[i].primary;
+ const char *alias = m68k_opcode_aliases[i].alias;
+ void *val = hash_find (op_hash, name);
+
+ if (!val)
+ as_fatal (_("Internal Error: Can't find %s in hash table"), name);
+ retval = hash_insert (op_hash, alias, val);
+ if (retval)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), alias, retval);
+ }
+
+ /* In MRI mode, all unsized branches are variable sized. Normally,
+ they are word sized. */
+ if (flag_mri)
+ {
+ static struct m68k_opcode_alias mri_aliases[] =
+ {
+ { "bhi", "jhi", },
+ { "bls", "jls", },
+ { "bcc", "jcc", },
+ { "bcs", "jcs", },
+ { "bne", "jne", },
+ { "beq", "jeq", },
+ { "bvc", "jvc", },
+ { "bvs", "jvs", },
+ { "bpl", "jpl", },
+ { "bmi", "jmi", },
+ { "bge", "jge", },
+ { "blt", "jlt", },
+ { "bgt", "jgt", },
+ { "ble", "jle", },
+ { "bra", "jra", },
+ { "bsr", "jbsr", },
+ };
+
+ for (i = 0;
+ i < (int) (sizeof mri_aliases / sizeof mri_aliases[0]);
+ i++)
+ {
+ const char *name = mri_aliases[i].primary;
+ const char *alias = mri_aliases[i].alias;
+ void *val = hash_find (op_hash, name);
+
+ if (!val)
+ as_fatal (_("Internal Error: Can't find %s in hash table"), name);
+ retval = hash_jam (op_hash, alias, val);
+ if (retval)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), alias, retval);
+ }
+ }
+
+ for (i = 0; i < (int) sizeof (notend_table); i++)
+ {
+ notend_table[i] = 0;
+ alt_notend_table[i] = 0;
+ }
+
+ notend_table[','] = 1;
+ notend_table['{'] = 1;
+ notend_table['}'] = 1;
+ alt_notend_table['a'] = 1;
+ alt_notend_table['A'] = 1;
+ alt_notend_table['d'] = 1;
+ alt_notend_table['D'] = 1;
+ alt_notend_table['#'] = 1;
+ alt_notend_table['&'] = 1;
+ alt_notend_table['f'] = 1;
+ alt_notend_table['F'] = 1;
+#ifdef REGISTER_PREFIX
+ alt_notend_table[REGISTER_PREFIX] = 1;
+#endif
+
+ /* We need to put '(' in alt_notend_table to handle
+ cas2 %d0:%d2,%d3:%d4,(%a0):(%a1) */
+ alt_notend_table['('] = 1;
+
+ /* We need to put '@' in alt_notend_table to handle
+ cas2 %d0:%d2,%d3:%d4,@(%d0):@(%d1) */
+ alt_notend_table['@'] = 1;
+
+ /* We need to put digits in alt_notend_table to handle
+ bfextu %d0{24:1},%d0 */
+ alt_notend_table['0'] = 1;
+ alt_notend_table['1'] = 1;
+ alt_notend_table['2'] = 1;
+ alt_notend_table['3'] = 1;
+ alt_notend_table['4'] = 1;
+ alt_notend_table['5'] = 1;
+ alt_notend_table['6'] = 1;
+ alt_notend_table['7'] = 1;
+ alt_notend_table['8'] = 1;
+ alt_notend_table['9'] = 1;
+
+#ifndef MIT_SYNTAX_ONLY
+ /* Insert pseudo ops, these have to go into the opcode table since
+ gas expects pseudo ops to start with a dot. */
+ {
+ int n = 0;
+
+ while (mote_pseudo_table[n].poc_name)
+ {
+ hack = obstack_alloc (&robyn, sizeof (struct m68k_incant));
+ hash_insert (op_hash,
+ mote_pseudo_table[n].poc_name, (char *) hack);
+ hack->m_operands = 0;
+ hack->m_opnum = n;
+ n++;
+ }
+ }
+#endif
+
+ init_regtable ();
+
+#ifdef OBJ_ELF
+ record_alignment (text_section, 2);
+ record_alignment (data_section, 2);
+ record_alignment (bss_section, 2);
+#endif
+}
+
+
+/* This is called when a label is defined. */
+
+void
+m68k_frob_label (symbolS *sym)
+{
+ struct label_line *n;
+
+ n = (struct label_line *) xmalloc (sizeof *n);
+ n->next = labels;
+ n->label = sym;
+ as_where (&n->file, &n->line);
+ n->text = 0;
+ labels = n;
+ current_label = n;
+
+#ifdef OBJ_ELF
+ dwarf2_emit_label (sym);
+#endif
+}
+
+/* This is called when a value that is not an instruction is emitted. */
+
+void
+m68k_flush_pending_output (void)
+{
+ current_label = NULL;
+}
+
+/* This is called at the end of the assembly, when the final value of
+ the label is known. We warn if this is a text symbol aligned at an
+ odd location. */
+
+void
+m68k_frob_symbol (symbolS *sym)
+{
+ if (S_GET_SEGMENT (sym) == reg_section
+ && (int) S_GET_VALUE (sym) < 0)
+ {
+ S_SET_SEGMENT (sym, absolute_section);
+ S_SET_VALUE (sym, ~(int)S_GET_VALUE (sym));
+ }
+ else if ((S_GET_VALUE (sym) & 1) != 0)
+ {
+ struct label_line *l;
+
+ for (l = labels; l != NULL; l = l->next)
+ {
+ if (l->label == sym)
+ {
+ if (l->text)
+ as_warn_where (l->file, l->line,
+ _("text label `%s' aligned to odd boundary"),
+ S_GET_NAME (sym));
+ break;
+ }
+ }
+ }
+}
+
+/* This is called if we go in or out of MRI mode because of the .mri
+ pseudo-op. */
+
+void
+m68k_mri_mode_change (int on)
+{
+ if (on)
+ {
+ if (! flag_reg_prefix_optional)
+ {
+ flag_reg_prefix_optional = 1;
+#ifdef REGISTER_PREFIX
+ init_regtable ();
+#endif
+ }
+ m68k_abspcadd = 1;
+ if (! m68k_rel32_from_cmdline)
+ m68k_rel32 = 0;
+ }
+ else
+ {
+ if (! reg_prefix_optional_seen)
+ {
+#ifdef REGISTER_PREFIX_OPTIONAL
+ flag_reg_prefix_optional = REGISTER_PREFIX_OPTIONAL;
+#else
+ flag_reg_prefix_optional = 0;
+#endif
+#ifdef REGISTER_PREFIX
+ init_regtable ();
+#endif
+ }
+ m68k_abspcadd = 0;
+ if (! m68k_rel32_from_cmdline)
+ m68k_rel32 = 1;
+ }
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ number_to_chars_bigendian (buf, val, n);
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ offsetT val = *valP;
+ addressT upper_limit;
+ offsetT lower_limit;
+
+ /* This is unnecessary but it convinces the native rs6000 compiler
+ to generate the code we want. */
+ char *buf = fixP->fx_frag->fr_literal;
+ buf += fixP->fx_where;
+ /* End ibm compiler workaround. */
+
+ val = SEXT (val);
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+
+#ifdef OBJ_ELF
+ if (fixP->fx_addsy)
+ {
+ memset (buf, 0, fixP->fx_size);
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc. */
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_68K_TLS_GD32:
+ case BFD_RELOC_68K_TLS_GD16:
+ case BFD_RELOC_68K_TLS_GD8:
+ case BFD_RELOC_68K_TLS_LDM32:
+ case BFD_RELOC_68K_TLS_LDM16:
+ case BFD_RELOC_68K_TLS_LDM8:
+ case BFD_RELOC_68K_TLS_LDO32:
+ case BFD_RELOC_68K_TLS_LDO16:
+ case BFD_RELOC_68K_TLS_LDO8:
+ case BFD_RELOC_68K_TLS_IE32:
+ case BFD_RELOC_68K_TLS_IE16:
+ case BFD_RELOC_68K_TLS_IE8:
+ case BFD_RELOC_68K_TLS_LE32:
+ case BFD_RELOC_68K_TLS_LE16:
+ case BFD_RELOC_68K_TLS_LE8:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+ }
+#elif defined(OBJ_AOUT)
+ /* PR gas/3041 Do not fix frags referencing a weak symbol. */
+ if (fixP->fx_addsy && S_IS_WEAK (fixP->fx_addsy))
+ {
+ memset (buf, 0, fixP->fx_size);
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc. */
+ return;
+ }
+#endif
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return;
+
+ switch (fixP->fx_size)
+ {
+ /* The cast to offsetT below are necessary to make code
+ correct for machines where ints are smaller than offsetT. */
+ case 1:
+ *buf++ = val;
+ upper_limit = 0x7f;
+ lower_limit = - (offsetT) 0x80;
+ break;
+ case 2:
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ upper_limit = 0x7fff;
+ lower_limit = - (offsetT) 0x8000;
+ break;
+ case 4:
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ upper_limit = 0x7fffffff;
+ lower_limit = - (offsetT) 0x7fffffff - 1; /* Avoid constant overflow. */
+ break;
+ default:
+ BAD_CASE (fixP->fx_size);
+ }
+
+ /* Fix up a negative reloc. */
+ if (fixP->fx_addsy == NULL && fixP->fx_subsy != NULL)
+ {
+ fixP->fx_addsy = fixP->fx_subsy;
+ fixP->fx_subsy = NULL;
+ fixP->fx_tcbit = 1;
+ }
+
+ /* For non-pc-relative values, it's conceivable we might get something
+ like "0xff" for a byte field. So extend the upper part of the range
+ to accept such numbers. We arbitrarily disallow "-0xff" or "0xff+0xff",
+ so that we can do any range checking at all. */
+ if (! fixP->fx_pcrel && ! fixP->fx_signed)
+ upper_limit = upper_limit * 2 + 1;
+
+ if ((addressT) val > upper_limit
+ && (val > 0 || val < lower_limit))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("value %ld out of range"), (long)val);
+
+ /* A one byte PC-relative reloc means a short branch. We can't use
+ a short branch with a value of 0 or -1, because those indicate
+ different opcodes (branches with longer offsets). fixup_segment
+ in write.c may have clobbered fx_pcrel, so we need to examine the
+ reloc type. */
+ if ((fixP->fx_pcrel
+ || fixP->fx_r_type == BFD_RELOC_8_PCREL)
+ && fixP->fx_size == 1
+ && (fixP->fx_addsy == NULL
+ || S_IS_DEFINED (fixP->fx_addsy))
+ && (val == 0 || val == -1))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid byte branch offset"));
+}
+
+/* *fragP has been relaxed to its final size, and now needs to have
+ the bytes inside it modified to conform to the new size There is UGLY
+ MAGIC here. ..
+ */
+static void
+md_convert_frag_1 (fragS *fragP)
+{
+ long disp;
+ fixS *fixP = NULL;
+
+ /* Address in object code of the displacement. */
+ register int object_address = fragP->fr_fix + fragP->fr_address;
+
+ /* Address in gas core of the place to store the displacement. */
+ /* This convinces the native rs6000 compiler to generate the code we
+ want. */
+ register char *buffer_address = fragP->fr_literal;
+ buffer_address += fragP->fr_fix;
+ /* End ibm compiler workaround. */
+
+ /* The displacement of the address, from current location. */
+ disp = fragP->fr_symbol ? S_GET_VALUE (fragP->fr_symbol) : 0;
+ disp = (disp + fragP->fr_offset) - object_address;
+
+ switch (fragP->fr_subtype)
+ {
+ case TAB (BRANCHBWL, BYTE):
+ case TAB (BRABSJUNC, BYTE):
+ case TAB (BRABSJCOND, BYTE):
+ case TAB (BRANCHBW, BYTE):
+ case TAB (BRANCHBWPL, BYTE):
+ know (issbyte (disp));
+ if (disp == 0)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("short branch with zero offset: use :w"));
+ fixP = fix_new (fragP, fragP->fr_fix - 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC8);
+ fixP->fx_pcrel_adjust = -1;
+ break;
+ case TAB (BRANCHBWL, SHORT):
+ case TAB (BRABSJUNC, SHORT):
+ case TAB (BRABSJCOND, SHORT):
+ case TAB (BRANCHBW, SHORT):
+ case TAB (BRANCHBWPL, SHORT):
+ fragP->fr_opcode[1] = 0x00;
+ fixP = fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC16);
+ fragP->fr_fix += 2;
+ break;
+ case TAB (BRANCHBWL, LONG):
+ fragP->fr_opcode[1] = (char) 0xFF;
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fragP->fr_fix += 4;
+ break;
+ case TAB (BRANCHBWPL, LONG):
+ /* Here we are converting an unconditional branch into a pair of
+ conditional branches, in order to get the range. */
+ fragP->fr_opcode[0] = 0x66; /* bne */
+ fragP->fr_opcode[1] = 0xFF;
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fixP->fx_file = fragP->fr_file;
+ fixP->fx_line = fragP->fr_line;
+ fragP->fr_fix += 4; /* Skip first offset */
+ buffer_address += 4;
+ *buffer_address++ = 0x67; /* beq */
+ *buffer_address++ = 0xff;
+ fragP->fr_fix += 2; /* Skip second branch opcode */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fragP->fr_fix += 4;
+ break;
+ case TAB (BRABSJUNC, LONG):
+ if (fragP->fr_opcode[0] == 0x61) /* jbsr */
+ {
+ if (flag_keep_pcrel)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("Conversion of PC relative BSR to absolute JSR"));
+ fragP->fr_opcode[0] = 0x4E;
+ fragP->fr_opcode[1] = (char) 0xB9; /* JSR with ABSL LONG operand. */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, RELAX_RELOC_ABS32);
+ fragP->fr_fix += 4;
+ }
+ else if (fragP->fr_opcode[0] == 0x60) /* jbra */
+ {
+ if (flag_keep_pcrel)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("Conversion of PC relative branch to absolute jump"));
+ fragP->fr_opcode[0] = 0x4E;
+ fragP->fr_opcode[1] = (char) 0xF9; /* JMP with ABSL LONG operand. */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, RELAX_RELOC_ABS32);
+ fragP->fr_fix += 4;
+ }
+ else
+ {
+ /* This cannot happen, because jbsr and jbra are the only two
+ unconditional branches. */
+ abort ();
+ }
+ break;
+ case TAB (BRABSJCOND, LONG):
+ if (flag_keep_pcrel)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("Conversion of PC relative conditional branch to absolute jump"));
+
+ /* Only Bcc 68000 instructions can come here
+ Change bcc into b!cc/jmp absl long. */
+ fragP->fr_opcode[0] ^= 0x01; /* Invert bcc. */
+ fragP->fr_opcode[1] = 0x06; /* Branch offset = 6. */
+
+ /* JF: these used to be fr_opcode[2,3], but they may be in a
+ different frag, in which case referring to them is a no-no.
+ Only fr_opcode[0,1] are guaranteed to work. */
+ *buffer_address++ = 0x4e; /* put in jmp long (0x4ef9) */
+ *buffer_address++ = (char) 0xf9;
+ fragP->fr_fix += 2; /* Account for jmp instruction. */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, RELAX_RELOC_ABS32);
+ fragP->fr_fix += 4;
+ break;
+ case TAB (FBRANCH, SHORT):
+ know ((fragP->fr_opcode[1] & 0x40) == 0);
+ fixP = fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC16);
+ fragP->fr_fix += 2;
+ break;
+ case TAB (FBRANCH, LONG):
+ fragP->fr_opcode[1] |= 0x40; /* Turn on LONG bit. */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fragP->fr_fix += 4;
+ break;
+ case TAB (DBCCLBR, SHORT):
+ case TAB (DBCCABSJ, SHORT):
+ fixP = fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC16);
+ fragP->fr_fix += 2;
+ break;
+ case TAB (DBCCLBR, LONG):
+ /* Only DBcc instructions can come here.
+ Change dbcc into dbcc/bral.
+ JF: these used to be fr_opcode[2-7], but that's wrong. */
+ *buffer_address++ = 0x00; /* Branch offset = 4. */
+ *buffer_address++ = 0x04;
+ *buffer_address++ = 0x60; /* Put in bra pc+6. */
+ *buffer_address++ = 0x06;
+ *buffer_address++ = 0x60; /* Put in bral (0x60ff). */
+ *buffer_address++ = (char) 0xff;
+
+ fragP->fr_fix += 6; /* Account for bra/jmp instructions. */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fragP->fr_fix += 4;
+ break;
+ case TAB (DBCCABSJ, LONG):
+ /* Only DBcc instructions can come here.
+ Change dbcc into dbcc/jmp.
+ JF: these used to be fr_opcode[2-7], but that's wrong. */
+ if (flag_keep_pcrel)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("Conversion of PC relative conditional branch to absolute jump"));
+
+ *buffer_address++ = 0x00; /* Branch offset = 4. */
+ *buffer_address++ = 0x04;
+ *buffer_address++ = 0x60; /* Put in bra pc + 6. */
+ *buffer_address++ = 0x06;
+ *buffer_address++ = 0x4e; /* Put in jmp long (0x4ef9). */
+ *buffer_address++ = (char) 0xf9;
+
+ fragP->fr_fix += 6; /* Account for bra/jmp instructions. */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, RELAX_RELOC_ABS32);
+ fragP->fr_fix += 4;
+ break;
+ case TAB (PCREL1632, SHORT):
+ fragP->fr_opcode[1] &= ~0x3F;
+ fragP->fr_opcode[1] |= 0x3A; /* 072 - mode 7.2 */
+ fixP = fix_new (fragP, (int) (fragP->fr_fix), 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC16);
+ fragP->fr_fix += 2;
+ break;
+ case TAB (PCREL1632, LONG):
+ /* Already set to mode 7.3; this indicates: PC indirect with
+ suppressed index, 32-bit displacement. */
+ *buffer_address++ = 0x01;
+ *buffer_address++ = 0x70;
+ fragP->fr_fix += 2;
+ fixP = fix_new (fragP, (int) (fragP->fr_fix), 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fixP->fx_pcrel_adjust = 2;
+ fragP->fr_fix += 4;
+ break;
+ case TAB (PCINDEX, BYTE):
+ gas_assert (fragP->fr_fix >= 2);
+ buffer_address[-2] &= ~1;
+ fixP = fix_new (fragP, fragP->fr_fix - 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC8);
+ fixP->fx_pcrel_adjust = 1;
+ break;
+ case TAB (PCINDEX, SHORT):
+ gas_assert (fragP->fr_fix >= 2);
+ buffer_address[-2] |= 0x1;
+ buffer_address[-1] = 0x20;
+ fixP = fix_new (fragP, (int) (fragP->fr_fix), 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC16);
+ fixP->fx_pcrel_adjust = 2;
+ fragP->fr_fix += 2;
+ break;
+ case TAB (PCINDEX, LONG):
+ gas_assert (fragP->fr_fix >= 2);
+ buffer_address[-2] |= 0x1;
+ buffer_address[-1] = 0x30;
+ fixP = fix_new (fragP, (int) (fragP->fr_fix), 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC32);
+ fixP->fx_pcrel_adjust = 2;
+ fragP->fr_fix += 4;
+ break;
+ case TAB (ABSTOPCREL, SHORT):
+ fixP = fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, RELAX_RELOC_PC16);
+ fragP->fr_fix += 2;
+ break;
+ case TAB (ABSTOPCREL, LONG):
+ if (flag_keep_pcrel)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("Conversion of PC relative displacement to absolute"));
+ /* The thing to do here is force it to ABSOLUTE LONG, since
+ ABSTOPCREL is really trying to shorten an ABSOLUTE address anyway. */
+ if ((fragP->fr_opcode[1] & 0x3F) != 0x3A)
+ abort ();
+ fragP->fr_opcode[1] &= ~0x3F;
+ fragP->fr_opcode[1] |= 0x39; /* Mode 7.1 */
+ fixP = fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0, RELAX_RELOC_ABS32);
+ fragP->fr_fix += 4;
+ break;
+ }
+ if (fixP)
+ {
+ fixP->fx_file = fragP->fr_file;
+ fixP->fx_line = fragP->fr_line;
+ }
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ md_convert_frag_1 (fragP);
+}
+
+/* Force truly undefined symbols to their maximum size, and generally set up
+ the frag list to be relaxed
+ */
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment)
+{
+ /* Handle SZ_UNDEF first, it can be changed to BYTE or SHORT. */
+ switch (fragP->fr_subtype)
+ {
+ case TAB (BRANCHBWL, SZ_UNDEF):
+ case TAB (BRANCHBWPL, SZ_UNDEF):
+ case TAB (BRABSJUNC, SZ_UNDEF):
+ case TAB (BRABSJCOND, SZ_UNDEF):
+ {
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ {
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), BYTE);
+ }
+ else if (flag_short_refs)
+ {
+ /* Symbol is undefined and we want short ref. */
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), SHORT);
+ }
+ else
+ {
+ /* Symbol is still undefined. Make it LONG. */
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), LONG);
+ }
+ break;
+ }
+
+ case TAB (BRANCHBW, SZ_UNDEF):
+ {
+ if (S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ {
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), BYTE);
+ }
+ else
+ {
+ /* Symbol is undefined and we don't have long branches. */
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), SHORT);
+ }
+ break;
+ }
+
+ case TAB (FBRANCH, SZ_UNDEF):
+ case TAB (DBCCLBR, SZ_UNDEF):
+ case TAB (DBCCABSJ, SZ_UNDEF):
+ case TAB (PCREL1632, SZ_UNDEF):
+ {
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol))
+ || flag_short_refs)
+ {
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), SHORT);
+ }
+ else
+ {
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), LONG);
+ }
+ break;
+ }
+
+ case TAB (PCINDEX, SZ_UNDEF):
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol)))
+ {
+ fragP->fr_subtype = TAB (PCINDEX, BYTE);
+ }
+ else
+ {
+ fragP->fr_subtype = TAB (PCINDEX, LONG);
+ }
+ break;
+
+ case TAB (ABSTOPCREL, SZ_UNDEF):
+ {
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && relaxable_symbol (fragP->fr_symbol)))
+ {
+ fragP->fr_subtype = TAB (ABSTOPCREL, SHORT);
+ }
+ else
+ {
+ fragP->fr_subtype = TAB (ABSTOPCREL, LONG);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ /* Now that SZ_UNDEF are taken care of, check others. */
+ switch (fragP->fr_subtype)
+ {
+ case TAB (BRANCHBWL, BYTE):
+ case TAB (BRABSJUNC, BYTE):
+ case TAB (BRABSJCOND, BYTE):
+ case TAB (BRANCHBW, BYTE):
+ /* We can't do a short jump to the next instruction, so in that
+ case we force word mode. If the symbol is at the start of a
+ frag, and it is the next frag with any data in it (usually
+ this is just the next frag, but assembler listings may
+ introduce empty frags), we must use word mode. */
+ if (fragP->fr_symbol)
+ {
+ fragS *sym_frag;
+
+ sym_frag = symbol_get_frag (fragP->fr_symbol);
+ if (S_GET_VALUE (fragP->fr_symbol) == sym_frag->fr_address)
+ {
+ fragS *l;
+
+ for (l = fragP->fr_next; l && l != sym_frag; l = l->fr_next)
+ if (l->fr_fix != 0)
+ break;
+ if (l == sym_frag)
+ fragP->fr_subtype = TAB (TABTYPE (fragP->fr_subtype), SHORT);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+#if defined(OBJ_AOUT) | defined(OBJ_BOUT)
+/* the bit-field entries in the relocation_info struct plays hell
+ with the byte-order problems of cross-assembly. So as a hack,
+ I added this mach. dependent ri twiddler. Ugly, but it gets
+ you there. -KWK */
+/* on m68k: first 4 bytes are normal unsigned long, next three bytes
+ are symbolnum, most sig. byte first. Last byte is broken up with
+ bit 7 as pcrel, bits 6 & 5 as length, bit 4 as pcrel, and the lower
+ nibble as nuthin. (on Sun 3 at least) */
+/* Translate the internal relocation information into target-specific
+ format. */
+#ifdef comment
+void
+md_ri_to_chars (char *the_bytes, struct reloc_info_generic *ri)
+{
+ /* This is easy. */
+ md_number_to_chars (the_bytes, ri->r_address, 4);
+ /* Now the fun stuff. */
+ the_bytes[4] = (ri->r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri->r_symbolnum >> 8) & 0x0ff;
+ the_bytes[6] = ri->r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri->r_pcrel << 7) & 0x80)
+ | ((ri->r_length << 5) & 0x60)
+ | ((ri->r_extern << 4) & 0x10));
+}
+
+#endif
+
+#endif /* OBJ_AOUT or OBJ_BOUT */
+
+#ifndef WORKING_DOT_WORD
+int md_short_jump_size = 4;
+int md_long_jump_size = 6;
+
+void
+md_create_short_jump (char *ptr, addressT from_addr, addressT to_addr,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ valueT offset;
+
+ offset = to_addr - (from_addr + 2);
+
+ md_number_to_chars (ptr, (valueT) 0x6000, 2);
+ md_number_to_chars (ptr + 2, (valueT) offset, 2);
+}
+
+void
+md_create_long_jump (char *ptr, addressT from_addr, addressT to_addr,
+ fragS *frag, symbolS *to_symbol)
+{
+ valueT offset;
+
+ if (!HAVE_LONG_BRANCH (current_architecture))
+ {
+ if (flag_keep_pcrel)
+ as_fatal (_("Tried to convert PC relative branch to absolute jump"));
+ offset = to_addr - S_GET_VALUE (to_symbol);
+ md_number_to_chars (ptr, (valueT) 0x4EF9, 2);
+ md_number_to_chars (ptr + 2, (valueT) offset, 4);
+ fix_new (frag, (ptr + 2) - frag->fr_literal, 4, to_symbol, (offsetT) 0,
+ 0, NO_RELOC);
+ }
+ else
+ {
+ offset = to_addr - (from_addr + 2);
+ md_number_to_chars (ptr, (valueT) 0x60ff, 2);
+ md_number_to_chars (ptr + 2, (valueT) offset, 4);
+ }
+}
+
+#endif
+
+/* Different values of OK tell what its OK to return. Things that
+ aren't OK are an error (what a shock, no?)
+
+ 0: Everything is OK
+ 10: Absolute 1:8 only
+ 20: Absolute 0:7 only
+ 30: absolute 0:15 only
+ 40: Absolute 0:31 only
+ 50: absolute 0:127 only
+ 55: absolute -64:63 only
+ 60: absolute -128:127 only
+ 65: absolute 0:511 only
+ 70: absolute 0:4095 only
+ 80: absolute -1, 1:7 only
+ 90: No bignums. */
+
+static int
+get_num (struct m68k_exp *exp, int ok)
+{
+ if (exp->exp.X_op == O_absent)
+ {
+ /* Do the same thing the VAX asm does. */
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = 0;
+ if (ok == 10)
+ {
+ as_warn (_("expression out of range: defaulting to 1"));
+ offs (exp) = 1;
+ }
+ }
+ else if (exp->exp.X_op == O_constant)
+ {
+ switch (ok)
+ {
+ case 10:
+ if ((valueT) TRUNC (offs (exp)) - 1 > 7)
+ {
+ as_warn (_("expression out of range: defaulting to 1"));
+ offs (exp) = 1;
+ }
+ break;
+ case 20:
+ if ((valueT) TRUNC (offs (exp)) > 7)
+ goto outrange;
+ break;
+ case 30:
+ if ((valueT) TRUNC (offs (exp)) > 15)
+ goto outrange;
+ break;
+ case 40:
+ if ((valueT) TRUNC (offs (exp)) > 32)
+ goto outrange;
+ break;
+ case 50:
+ if ((valueT) TRUNC (offs (exp)) > 127)
+ goto outrange;
+ break;
+ case 55:
+ if ((valueT) SEXT (offs (exp)) + 64 > 127)
+ goto outrange;
+ break;
+ case 60:
+ if ((valueT) SEXT (offs (exp)) + 128 > 255)
+ goto outrange;
+ break;
+ case 65:
+ if ((valueT) TRUNC (offs (exp)) > 511)
+ goto outrange;
+ break;
+ case 70:
+ if ((valueT) TRUNC (offs (exp)) > 4095)
+ {
+ outrange:
+ as_warn (_("expression out of range: defaulting to 0"));
+ offs (exp) = 0;
+ }
+ break;
+ case 80:
+ if ((valueT) TRUNC (offs (exp)) != 0xffffffff
+ && (valueT) TRUNC (offs (exp)) - 1 > 6)
+ {
+ as_warn (_("expression out of range: defaulting to 1"));
+ offs (exp) = 1;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (exp->exp.X_op == O_big)
+ {
+ if (offs (exp) <= 0 /* flonum. */
+ && (ok == 90 /* no bignums */
+ || (ok > 10 /* Small-int ranges including 0 ok. */
+ /* If we have a flonum zero, a zero integer should
+ do as well (e.g., in moveq). */
+ && generic_floating_point_number.exponent == 0
+ && generic_floating_point_number.low[0] == 0)))
+ {
+ /* HACK! Turn it into a long. */
+ LITTLENUM_TYPE words[6];
+
+ gen_to_words (words, 2, 8L); /* These numbers are magic! */
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = words[1] | (words[0] << 16);
+ }
+ else if (ok != 0)
+ {
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = (ok == 10) ? 1 : 0;
+ as_warn (_("Can't deal with expression; defaulting to %ld"),
+ (long) offs (exp));
+ }
+ }
+ else
+ {
+ if (ok >= 10 && ok <= 80)
+ {
+ op (exp) = O_constant;
+ adds (exp) = 0;
+ subs (exp) = 0;
+ offs (exp) = (ok == 10) ? 1 : 0;
+ as_warn (_("Can't deal with expression; defaulting to %ld"),
+ (long) offs (exp));
+ }
+ }
+
+ if (exp->size != SIZE_UNSPEC)
+ {
+ switch (exp->size)
+ {
+ case SIZE_UNSPEC:
+ case SIZE_LONG:
+ break;
+ case SIZE_BYTE:
+ if (!isbyte (offs (exp)))
+ as_warn (_("expression doesn't fit in BYTE"));
+ break;
+ case SIZE_WORD:
+ if (!isword (offs (exp)))
+ as_warn (_("expression doesn't fit in WORD"));
+ break;
+ }
+ }
+
+ return offs (exp);
+}
+
+/* These are the back-ends for the various machine dependent pseudo-ops. */
+
+static void
+s_data1 (int ignore ATTRIBUTE_UNUSED)
+{
+ subseg_set (data_section, 1);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_data2 (int ignore ATTRIBUTE_UNUSED)
+{
+ subseg_set (data_section, 2);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ /* We don't support putting frags in the BSS segment, we fake it
+ by marking in_bss, then looking at s_skip for clues. */
+
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_even (int ignore ATTRIBUTE_UNUSED)
+{
+ register int temp;
+ register long temp_fill;
+
+ temp = 1; /* JF should be 2? */
+ temp_fill = get_absolute_expression ();
+ if (!need_pass_2) /* Never make frag if expect extra pass. */
+ frag_align (temp, (int) temp_fill, 0);
+ demand_empty_rest_of_line ();
+ record_alignment (now_seg, temp);
+}
+
+static void
+s_proc (int ignore ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+}
+
+/* Pseudo-ops handled for MRI compatibility. */
+
+/* This function returns non-zero if the argument is a conditional
+ pseudo-op. This is called when checking whether a pending
+ alignment is needed. */
+
+int
+m68k_conditional_pseudoop (pseudo_typeS *pop)
+{
+ return (pop->poc_handler == s_mri_if
+ || pop->poc_handler == s_mri_else);
+}
+
+/* Handle an MRI style chip specification. */
+
+static void
+mri_chip (void)
+{
+ char *s;
+ char c;
+ int i;
+
+ s = input_line_pointer;
+ /* We can't use get_symbol_end since the processor names are not proper
+ symbols. */
+ while (is_part_of_name (c = *input_line_pointer++))
+ ;
+ *--input_line_pointer = 0;
+ for (i = 0; m68k_cpus[i].name; i++)
+ if (strcasecmp (s, m68k_cpus[i].name) == 0)
+ break;
+ if (!m68k_cpus[i].name)
+ {
+ as_bad (_("%s: unrecognized processor name"), s);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ if (*input_line_pointer == '/')
+ current_architecture = 0;
+ else
+ current_architecture &= m68881 | m68851;
+ current_architecture |= m68k_cpus[i].arch & ~(m68881 | m68851);
+ control_regs = m68k_cpus[i].control_regs;
+
+ while (*input_line_pointer == '/')
+ {
+ ++input_line_pointer;
+ s = input_line_pointer;
+ /* We can't use get_symbol_end since the processor names are not
+ proper symbols. */
+ while (is_part_of_name (c = *input_line_pointer++))
+ ;
+ *--input_line_pointer = 0;
+ if (strcmp (s, "68881") == 0)
+ current_architecture |= m68881;
+ else if (strcmp (s, "68851") == 0)
+ current_architecture |= m68851;
+ *input_line_pointer = c;
+ }
+}
+
+/* The MRI CHIP pseudo-op. */
+
+static void
+s_chip (int ignore ATTRIBUTE_UNUSED)
+{
+ char *stop = NULL;
+ char stopc;
+
+ if (flag_mri)
+ stop = mri_comment_field (&stopc);
+ mri_chip ();
+ if (flag_mri)
+ mri_comment_end (stop, stopc);
+ demand_empty_rest_of_line ();
+}
+
+/* The MRI FOPT pseudo-op. */
+
+static void
+s_fopt (int ignore ATTRIBUTE_UNUSED)
+{
+ SKIP_WHITESPACE ();
+
+ if (strncasecmp (input_line_pointer, "ID=", 3) == 0)
+ {
+ int temp;
+
+ input_line_pointer += 3;
+ temp = get_absolute_expression ();
+ if (temp < 0 || temp > 7)
+ as_bad (_("bad coprocessor id"));
+ else
+ m68k_float_copnum = COP0 + temp;
+ }
+ else
+ {
+ as_bad (_("unrecognized fopt option"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The structure used to handle the MRI OPT pseudo-op. */
+
+struct opt_action
+{
+ /* The name of the option. */
+ const char *name;
+
+ /* If this is not NULL, just call this function. The first argument
+ is the ARG field of this structure, the second argument is
+ whether the option was negated. */
+ void (*pfn) (int arg, int on);
+
+ /* If this is not NULL, and the PFN field is NULL, set the variable
+ this points to. Set it to the ARG field if the option was not
+ negated, and the NOTARG field otherwise. */
+ int *pvar;
+
+ /* The value to pass to PFN or to assign to *PVAR. */
+ int arg;
+
+ /* The value to assign to *PVAR if the option is negated. If PFN is
+ NULL, and PVAR is not NULL, and ARG and NOTARG are the same, then
+ the option may not be negated. */
+ int notarg;
+};
+
+/* The table used to handle the MRI OPT pseudo-op. */
+
+static void skip_to_comma (int, int);
+static void opt_nest (int, int);
+static void opt_chip (int, int);
+static void opt_list (int, int);
+static void opt_list_symbols (int, int);
+
+static const struct opt_action opt_table[] =
+{
+ { "abspcadd", 0, &m68k_abspcadd, 1, 0 },
+
+ /* We do relaxing, so there is little use for these options. */
+ { "b", 0, 0, 0, 0 },
+ { "brs", 0, 0, 0, 0 },
+ { "brb", 0, 0, 0, 0 },
+ { "brl", 0, 0, 0, 0 },
+ { "brw", 0, 0, 0, 0 },
+
+ { "c", 0, 0, 0, 0 },
+ { "cex", 0, 0, 0, 0 },
+ { "case", 0, &symbols_case_sensitive, 1, 0 },
+ { "cl", 0, 0, 0, 0 },
+ { "cre", 0, 0, 0, 0 },
+ { "d", 0, &flag_keep_locals, 1, 0 },
+ { "e", 0, 0, 0, 0 },
+ { "f", 0, &flag_short_refs, 1, 0 },
+ { "frs", 0, &flag_short_refs, 1, 0 },
+ { "frl", 0, &flag_short_refs, 0, 1 },
+ { "g", 0, 0, 0, 0 },
+ { "i", 0, 0, 0, 0 },
+ { "m", 0, 0, 0, 0 },
+ { "mex", 0, 0, 0, 0 },
+ { "mc", 0, 0, 0, 0 },
+ { "md", 0, 0, 0, 0 },
+ { "nest", opt_nest, 0, 0, 0 },
+ { "next", skip_to_comma, 0, 0, 0 },
+ { "o", 0, 0, 0, 0 },
+ { "old", 0, 0, 0, 0 },
+ { "op", skip_to_comma, 0, 0, 0 },
+ { "pco", 0, 0, 0, 0 },
+ { "p", opt_chip, 0, 0, 0 },
+ { "pcr", 0, 0, 0, 0 },
+ { "pcs", 0, 0, 0, 0 },
+ { "r", 0, 0, 0, 0 },
+ { "quick", 0, &m68k_quick, 1, 0 },
+ { "rel32", 0, &m68k_rel32, 1, 0 },
+ { "s", opt_list, 0, 0, 0 },
+ { "t", opt_list_symbols, 0, 0, 0 },
+ { "w", 0, &flag_no_warnings, 0, 1 },
+ { "x", 0, 0, 0, 0 }
+};
+
+#define OPTCOUNT ((int) (sizeof opt_table / sizeof opt_table[0]))
+
+/* The MRI OPT pseudo-op. */
+
+static void
+s_opt (int ignore ATTRIBUTE_UNUSED)
+{
+ do
+ {
+ int t;
+ char *s;
+ char c;
+ int i;
+ const struct opt_action *o;
+
+ SKIP_WHITESPACE ();
+
+ t = 1;
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ t = 0;
+ }
+ else if (strncasecmp (input_line_pointer, "NO", 2) == 0)
+ {
+ input_line_pointer += 2;
+ t = 0;
+ }
+
+ s = input_line_pointer;
+ c = get_symbol_end ();
+
+ for (i = 0, o = opt_table; i < OPTCOUNT; i++, o++)
+ {
+ if (strcasecmp (s, o->name) == 0)
+ {
+ if (o->pfn)
+ {
+ /* Restore input_line_pointer now in case the option
+ takes arguments. */
+ *input_line_pointer = c;
+ (*o->pfn) (o->arg, t);
+ }
+ else if (o->pvar != NULL)
+ {
+ if (! t && o->arg == o->notarg)
+ as_bad (_("option `%s' may not be negated"), s);
+ *input_line_pointer = c;
+ *o->pvar = t ? o->arg : o->notarg;
+ }
+ else
+ *input_line_pointer = c;
+ break;
+ }
+ }
+ if (i >= OPTCOUNT)
+ {
+ as_bad (_("option `%s' not recognized"), s);
+ *input_line_pointer = c;
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Move back to terminating character. */
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+}
+
+/* Skip ahead to a comma. This is used for OPT options which we do
+ not support and which take arguments. */
+
+static void
+skip_to_comma (int arg ATTRIBUTE_UNUSED, int on ATTRIBUTE_UNUSED)
+{
+ while (*input_line_pointer != ','
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+}
+
+/* Handle the OPT NEST=depth option. */
+
+static void
+opt_nest (int arg ATTRIBUTE_UNUSED, int on ATTRIBUTE_UNUSED)
+{
+ if (*input_line_pointer != '=')
+ {
+ as_bad (_("bad format of OPT NEST=depth"));
+ return;
+ }
+
+ ++input_line_pointer;
+ max_macro_nest = get_absolute_expression ();
+}
+
+/* Handle the OPT P=chip option. */
+
+static void
+opt_chip (int arg ATTRIBUTE_UNUSED, int on ATTRIBUTE_UNUSED)
+{
+ if (*input_line_pointer != '=')
+ {
+ /* This is just OPT P, which we do not support. */
+ return;
+ }
+
+ ++input_line_pointer;
+ mri_chip ();
+}
+
+/* Handle the OPT S option. */
+
+static void
+opt_list (int arg ATTRIBUTE_UNUSED, int on)
+{
+ listing_list (on);
+}
+
+/* Handle the OPT T option. */
+
+static void
+opt_list_symbols (int arg ATTRIBUTE_UNUSED, int on)
+{
+ if (on)
+ listing |= LISTING_SYMBOLS;
+ else
+ listing &= ~LISTING_SYMBOLS;
+}
+
+/* Handle the MRI REG pseudo-op. */
+
+static void
+s_reg (int ignore ATTRIBUTE_UNUSED)
+{
+ char *s;
+ int c;
+ struct m68k_op rop;
+ int mask;
+ char *stop = NULL;
+ char stopc;
+
+ if (line_label == NULL)
+ {
+ as_bad (_("missing label"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (flag_mri)
+ stop = mri_comment_field (&stopc);
+
+ SKIP_WHITESPACE ();
+
+ s = input_line_pointer;
+ while (ISALNUM (*input_line_pointer)
+#ifdef REGISTER_PREFIX
+ || *input_line_pointer == REGISTER_PREFIX
+#endif
+ || *input_line_pointer == '/'
+ || *input_line_pointer == '-')
+ ++input_line_pointer;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (m68k_ip_op (s, &rop) != 0)
+ {
+ if (rop.error == NULL)
+ as_bad (_("bad register list"));
+ else
+ as_bad (_("bad register list: %s"), rop.error);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = c;
+
+ if (rop.mode == REGLST)
+ mask = rop.mask;
+ else if (rop.mode == DREG)
+ mask = 1 << (rop.reg - DATA0);
+ else if (rop.mode == AREG)
+ mask = 1 << (rop.reg - ADDR0 + 8);
+ else if (rop.mode == FPREG)
+ mask = 1 << (rop.reg - FP0 + 16);
+ else if (rop.mode == CONTROL
+ && rop.reg == FPI)
+ mask = 1 << 24;
+ else if (rop.mode == CONTROL
+ && rop.reg == FPS)
+ mask = 1 << 25;
+ else if (rop.mode == CONTROL
+ && rop.reg == FPC)
+ mask = 1 << 26;
+ else
+ {
+ as_bad (_("bad register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ S_SET_SEGMENT (line_label, reg_section);
+ S_SET_VALUE (line_label, ~mask);
+ symbol_set_frag (line_label, &zero_address_frag);
+
+ if (flag_mri)
+ mri_comment_end (stop, stopc);
+
+ demand_empty_rest_of_line ();
+}
+
+/* This structure is used for the MRI SAVE and RESTORE pseudo-ops. */
+
+struct save_opts
+{
+ struct save_opts *next;
+ int abspcadd;
+ int symbols_case_sensitive;
+ int keep_locals;
+ int short_refs;
+ int architecture;
+ const enum m68k_register *control_regs;
+ int quick;
+ int rel32;
+ int listing;
+ int no_warnings;
+ /* FIXME: We don't save OPT S. */
+};
+
+/* This variable holds the stack of saved options. */
+
+static struct save_opts *save_stack;
+
+/* The MRI SAVE pseudo-op. */
+
+static void
+s_save (int ignore ATTRIBUTE_UNUSED)
+{
+ struct save_opts *s;
+
+ s = (struct save_opts *) xmalloc (sizeof (struct save_opts));
+ s->abspcadd = m68k_abspcadd;
+ s->symbols_case_sensitive = symbols_case_sensitive;
+ s->keep_locals = flag_keep_locals;
+ s->short_refs = flag_short_refs;
+ s->architecture = current_architecture;
+ s->control_regs = control_regs;
+ s->quick = m68k_quick;
+ s->rel32 = m68k_rel32;
+ s->listing = listing;
+ s->no_warnings = flag_no_warnings;
+
+ s->next = save_stack;
+ save_stack = s;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The MRI RESTORE pseudo-op. */
+
+static void
+s_restore (int ignore ATTRIBUTE_UNUSED)
+{
+ struct save_opts *s;
+
+ if (save_stack == NULL)
+ {
+ as_bad (_("restore without save"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ s = save_stack;
+ save_stack = s->next;
+
+ m68k_abspcadd = s->abspcadd;
+ symbols_case_sensitive = s->symbols_case_sensitive;
+ flag_keep_locals = s->keep_locals;
+ flag_short_refs = s->short_refs;
+ current_architecture = s->architecture;
+ control_regs = s->control_regs;
+ m68k_quick = s->quick;
+ m68k_rel32 = s->rel32;
+ listing = s->listing;
+ flag_no_warnings = s->no_warnings;
+
+ free (s);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Types of MRI structured control directives. */
+
+enum mri_control_type
+{
+ mri_for,
+ mri_if,
+ mri_repeat,
+ mri_while
+};
+
+/* This structure is used to stack the MRI structured control
+ directives. */
+
+struct mri_control_info
+{
+ /* The directive within which this one is enclosed. */
+ struct mri_control_info *outer;
+
+ /* The type of directive. */
+ enum mri_control_type type;
+
+ /* Whether an ELSE has been in an IF. */
+ int else_seen;
+
+ /* The add or sub statement at the end of a FOR. */
+ char *incr;
+
+ /* The label of the top of a FOR or REPEAT loop. */
+ char *top;
+
+ /* The label to jump to for the next iteration, or the else
+ expression of a conditional. */
+ char *next;
+
+ /* The label to jump to to break out of the loop, or the label past
+ the end of a conditional. */
+ char *bottom;
+};
+
+/* The stack of MRI structured control directives. */
+
+static struct mri_control_info *mri_control_stack;
+
+/* The current MRI structured control directive index number, used to
+ generate label names. */
+
+static int mri_control_index;
+
+/* Assemble an instruction for an MRI structured control directive. */
+
+static void
+mri_assemble (char *str)
+{
+ char *s;
+
+ /* md_assemble expects the opcode to be in lower case. */
+ for (s = str; *s != ' ' && *s != '\0'; s++)
+ *s = TOLOWER (*s);
+
+ md_assemble (str);
+}
+
+/* Generate a new MRI label structured control directive label name. */
+
+static char *
+mri_control_label (void)
+{
+ char *n;
+
+ n = (char *) xmalloc (20);
+ sprintf (n, "%smc%d", FAKE_LABEL_NAME, mri_control_index);
+ ++mri_control_index;
+ return n;
+}
+
+/* Create a new MRI structured control directive. */
+
+static struct mri_control_info *
+push_mri_control (enum mri_control_type type)
+{
+ struct mri_control_info *n;
+
+ n = (struct mri_control_info *) xmalloc (sizeof (struct mri_control_info));
+
+ n->type = type;
+ n->else_seen = 0;
+ if (type == mri_if || type == mri_while)
+ n->top = NULL;
+ else
+ n->top = mri_control_label ();
+ n->next = mri_control_label ();
+ n->bottom = mri_control_label ();
+
+ n->outer = mri_control_stack;
+ mri_control_stack = n;
+
+ return n;
+}
+
+/* Pop off the stack of MRI structured control directives. */
+
+static void
+pop_mri_control (void)
+{
+ struct mri_control_info *n;
+
+ n = mri_control_stack;
+ mri_control_stack = n->outer;
+ if (n->top != NULL)
+ free (n->top);
+ free (n->next);
+ free (n->bottom);
+ free (n);
+}
+
+/* Recognize a condition code in an MRI structured control expression. */
+
+static int
+parse_mri_condition (int *pcc)
+{
+ char c1, c2;
+
+ know (*input_line_pointer == '<');
+
+ ++input_line_pointer;
+ c1 = *input_line_pointer++;
+ c2 = *input_line_pointer++;
+
+ if (*input_line_pointer != '>')
+ {
+ as_bad (_("syntax error in structured control directive"));
+ return 0;
+ }
+
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ c1 = TOLOWER (c1);
+ c2 = TOLOWER (c2);
+
+ *pcc = (c1 << 8) | c2;
+
+ return 1;
+}
+
+/* Parse a single operand in an MRI structured control expression. */
+
+static int
+parse_mri_control_operand (int *pcc, char **leftstart, char **leftstop,
+ char **rightstart, char **rightstop)
+{
+ char *s;
+
+ SKIP_WHITESPACE ();
+
+ *pcc = -1;
+ *leftstart = NULL;
+ *leftstop = NULL;
+ *rightstart = NULL;
+ *rightstop = NULL;
+
+ if (*input_line_pointer == '<')
+ {
+ /* It's just a condition code. */
+ return parse_mri_condition (pcc);
+ }
+
+ /* Look ahead for the condition code. */
+ for (s = input_line_pointer; *s != '\0'; ++s)
+ {
+ if (*s == '<' && s[1] != '\0' && s[2] != '\0' && s[3] == '>')
+ break;
+ }
+ if (*s == '\0')
+ {
+ as_bad (_("missing condition code in structured control directive"));
+ return 0;
+ }
+
+ *leftstart = input_line_pointer;
+ *leftstop = s;
+ if (*leftstop > *leftstart
+ && ((*leftstop)[-1] == ' ' || (*leftstop)[-1] == '\t'))
+ --*leftstop;
+
+ input_line_pointer = s;
+ if (! parse_mri_condition (pcc))
+ return 0;
+
+ /* Look ahead for AND or OR or end of line. */
+ for (s = input_line_pointer; *s != '\0'; ++s)
+ {
+ /* We must make sure we don't misinterpret AND/OR at the end of labels!
+ if d0 <eq> #FOOAND and d1 <ne> #BAROR then
+ ^^^ ^^ */
+ if ((s == input_line_pointer
+ || *(s-1) == ' '
+ || *(s-1) == '\t')
+ && ((strncasecmp (s, "AND", 3) == 0
+ && (s[3] == '.' || ! is_part_of_name (s[3])))
+ || (strncasecmp (s, "OR", 2) == 0
+ && (s[2] == '.' || ! is_part_of_name (s[2])))))
+ break;
+ }
+
+ *rightstart = input_line_pointer;
+ *rightstop = s;
+ if (*rightstop > *rightstart
+ && ((*rightstop)[-1] == ' ' || (*rightstop)[-1] == '\t'))
+ --*rightstop;
+
+ input_line_pointer = s;
+
+ return 1;
+}
+
+#define MCC(b1, b2) (((b1) << 8) | (b2))
+
+/* Swap the sense of a condition. This changes the condition so that
+ it generates the same result when the operands are swapped. */
+
+static int
+swap_mri_condition (int cc)
+{
+ switch (cc)
+ {
+ case MCC ('h', 'i'): return MCC ('c', 's');
+ case MCC ('l', 's'): return MCC ('c', 'c');
+ /* <HS> is an alias for <CC>. */
+ case MCC ('h', 's'):
+ case MCC ('c', 'c'): return MCC ('l', 's');
+ /* <LO> is an alias for <CS>. */
+ case MCC ('l', 'o'):
+ case MCC ('c', 's'): return MCC ('h', 'i');
+ case MCC ('p', 'l'): return MCC ('m', 'i');
+ case MCC ('m', 'i'): return MCC ('p', 'l');
+ case MCC ('g', 'e'): return MCC ('l', 'e');
+ case MCC ('l', 't'): return MCC ('g', 't');
+ case MCC ('g', 't'): return MCC ('l', 't');
+ case MCC ('l', 'e'): return MCC ('g', 'e');
+ /* Issue a warning for conditions we can not swap. */
+ case MCC ('n', 'e'): return MCC ('n', 'e'); /* no problem here */
+ case MCC ('e', 'q'): return MCC ('e', 'q'); /* also no problem */
+ case MCC ('v', 'c'):
+ case MCC ('v', 's'):
+ default :
+ as_warn (_("Condition <%c%c> in structured control directive can not be encoded correctly"),
+ (char) (cc >> 8), (char) (cc));
+ break;
+ }
+ return cc;
+}
+
+/* Reverse the sense of a condition. */
+
+static int
+reverse_mri_condition (int cc)
+{
+ switch (cc)
+ {
+ case MCC ('h', 'i'): return MCC ('l', 's');
+ case MCC ('l', 's'): return MCC ('h', 'i');
+ /* <HS> is an alias for <CC> */
+ case MCC ('h', 's'): return MCC ('l', 'o');
+ case MCC ('c', 'c'): return MCC ('c', 's');
+ /* <LO> is an alias for <CS> */
+ case MCC ('l', 'o'): return MCC ('h', 's');
+ case MCC ('c', 's'): return MCC ('c', 'c');
+ case MCC ('n', 'e'): return MCC ('e', 'q');
+ case MCC ('e', 'q'): return MCC ('n', 'e');
+ case MCC ('v', 'c'): return MCC ('v', 's');
+ case MCC ('v', 's'): return MCC ('v', 'c');
+ case MCC ('p', 'l'): return MCC ('m', 'i');
+ case MCC ('m', 'i'): return MCC ('p', 'l');
+ case MCC ('g', 'e'): return MCC ('l', 't');
+ case MCC ('l', 't'): return MCC ('g', 'e');
+ case MCC ('g', 't'): return MCC ('l', 'e');
+ case MCC ('l', 'e'): return MCC ('g', 't');
+ }
+ return cc;
+}
+
+/* Build an MRI structured control expression. This generates test
+ and branch instructions. It goes to TRUELAB if the condition is
+ true, and to FALSELAB if the condition is false. Exactly one of
+ TRUELAB and FALSELAB will be NULL, meaning to fall through. QUAL
+ is the size qualifier for the expression. EXTENT is the size to
+ use for the branch. */
+
+static void
+build_mri_control_operand (int qual, int cc, char *leftstart, char *leftstop,
+ char *rightstart, char *rightstop,
+ const char *truelab, const char *falselab,
+ int extent)
+{
+ char *buf;
+ char *s;
+
+ if (leftstart != NULL)
+ {
+ struct m68k_op leftop, rightop;
+ char c;
+
+ /* Swap the compare operands, if necessary, to produce a legal
+ m68k compare instruction. Comparing a register operand with
+ a non-register operand requires the register to be on the
+ right (cmp, cmpa). Comparing an immediate value with
+ anything requires the immediate value to be on the left
+ (cmpi). */
+
+ c = *leftstop;
+ *leftstop = '\0';
+ (void) m68k_ip_op (leftstart, &leftop);
+ *leftstop = c;
+
+ c = *rightstop;
+ *rightstop = '\0';
+ (void) m68k_ip_op (rightstart, &rightop);
+ *rightstop = c;
+
+ if (rightop.mode == IMMED
+ || ((leftop.mode == DREG || leftop.mode == AREG)
+ && (rightop.mode != DREG && rightop.mode != AREG)))
+ {
+ char *temp;
+
+ /* Correct conditional handling:
+ if #1 <lt> d0 then ;means if (1 < d0)
+ ...
+ endi
+
+ should assemble to:
+
+ cmp #1,d0 if we do *not* swap the operands
+ bgt true we need the swapped condition!
+ ble false
+ true:
+ ...
+ false:
+ */
+ temp = leftstart;
+ leftstart = rightstart;
+ rightstart = temp;
+ temp = leftstop;
+ leftstop = rightstop;
+ rightstop = temp;
+ }
+ else
+ {
+ cc = swap_mri_condition (cc);
+ }
+ }
+
+ if (truelab == NULL)
+ {
+ cc = reverse_mri_condition (cc);
+ truelab = falselab;
+ }
+
+ if (leftstart != NULL)
+ {
+ buf = (char *) xmalloc (20
+ + (leftstop - leftstart)
+ + (rightstop - rightstart));
+ s = buf;
+ *s++ = 'c';
+ *s++ = 'm';
+ *s++ = 'p';
+ if (qual != '\0')
+ *s++ = TOLOWER (qual);
+ *s++ = ' ';
+ memcpy (s, leftstart, leftstop - leftstart);
+ s += leftstop - leftstart;
+ *s++ = ',';
+ memcpy (s, rightstart, rightstop - rightstart);
+ s += rightstop - rightstart;
+ *s = '\0';
+ mri_assemble (buf);
+ free (buf);
+ }
+
+ buf = (char *) xmalloc (20 + strlen (truelab));
+ s = buf;
+ *s++ = 'b';
+ *s++ = cc >> 8;
+ *s++ = cc & 0xff;
+ if (extent != '\0')
+ *s++ = TOLOWER (extent);
+ *s++ = ' ';
+ strcpy (s, truelab);
+ mri_assemble (buf);
+ free (buf);
+}
+
+/* Parse an MRI structured control expression. This generates test
+ and branch instructions. STOP is where the expression ends. It
+ goes to TRUELAB if the condition is true, and to FALSELAB if the
+ condition is false. Exactly one of TRUELAB and FALSELAB will be
+ NULL, meaning to fall through. QUAL is the size qualifier for the
+ expression. EXTENT is the size to use for the branch. */
+
+static void
+parse_mri_control_expression (char *stop, int qual, const char *truelab,
+ const char *falselab, int extent)
+{
+ int c;
+ int cc;
+ char *leftstart;
+ char *leftstop;
+ char *rightstart;
+ char *rightstop;
+
+ c = *stop;
+ *stop = '\0';
+
+ if (! parse_mri_control_operand (&cc, &leftstart, &leftstop,
+ &rightstart, &rightstop))
+ {
+ *stop = c;
+ return;
+ }
+
+ if (strncasecmp (input_line_pointer, "AND", 3) == 0)
+ {
+ const char *flab;
+
+ if (falselab != NULL)
+ flab = falselab;
+ else
+ flab = mri_control_label ();
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, (const char *) NULL, flab, extent);
+
+ input_line_pointer += 3;
+ if (*input_line_pointer != '.'
+ || input_line_pointer[1] == '\0')
+ qual = '\0';
+ else
+ {
+ qual = input_line_pointer[1];
+ input_line_pointer += 2;
+ }
+
+ if (! parse_mri_control_operand (&cc, &leftstart, &leftstop,
+ &rightstart, &rightstop))
+ {
+ *stop = c;
+ return;
+ }
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent);
+
+ if (falselab == NULL)
+ colon (flab);
+ }
+ else if (strncasecmp (input_line_pointer, "OR", 2) == 0)
+ {
+ const char *tlab;
+
+ if (truelab != NULL)
+ tlab = truelab;
+ else
+ tlab = mri_control_label ();
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, tlab, (const char *) NULL, extent);
+
+ input_line_pointer += 2;
+ if (*input_line_pointer != '.'
+ || input_line_pointer[1] == '\0')
+ qual = '\0';
+ else
+ {
+ qual = input_line_pointer[1];
+ input_line_pointer += 2;
+ }
+
+ if (! parse_mri_control_operand (&cc, &leftstart, &leftstop,
+ &rightstart, &rightstop))
+ {
+ *stop = c;
+ return;
+ }
+
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent);
+
+ if (truelab == NULL)
+ colon (tlab);
+ }
+ else
+ {
+ build_mri_control_operand (qual, cc, leftstart, leftstop, rightstart,
+ rightstop, truelab, falselab, extent);
+ }
+
+ *stop = c;
+ if (input_line_pointer != stop)
+ as_bad (_("syntax error in structured control directive"));
+}
+
+/* Handle the MRI IF pseudo-op. This may be a structured control
+ directive, or it may be a regular assembler conditional, depending
+ on its operands. */
+
+static void
+s_mri_if (int qual)
+{
+ char *s;
+ int c;
+ struct mri_control_info *n;
+
+ /* A structured control directive must end with THEN with an
+ optional qualifier. */
+ s = input_line_pointer;
+ /* We only accept '*' as introduction of comments if preceded by white space
+ or at first column of a line (I think this can't actually happen here?)
+ This is important when assembling:
+ if d0 <ne> 12(a0,d0*2) then
+ if d0 <ne> #CONST*20 then. */
+ while (! (is_end_of_line[(unsigned char) *s]
+ || (flag_mri
+ && *s == '*'
+ && (s == input_line_pointer
+ || *(s-1) == ' '
+ || *(s-1) == '\t'))))
+ ++s;
+ --s;
+ while (s > input_line_pointer && (*s == ' ' || *s == '\t'))
+ --s;
+
+ if (s - input_line_pointer > 1
+ && s[-1] == '.')
+ s -= 2;
+
+ if (s - input_line_pointer < 3
+ || strncasecmp (s - 3, "THEN", 4) != 0)
+ {
+ if (qual != '\0')
+ {
+ as_bad (_("missing then"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* It's a conditional. */
+ s_if (O_ne);
+ return;
+ }
+
+ /* Since this might be a conditional if, this pseudo-op will be
+ called even if we are supported to be ignoring input. Double
+ check now. Clobber *input_line_pointer so that ignore_input
+ thinks that this is not a special pseudo-op. */
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+ if (ignore_input ())
+ {
+ *input_line_pointer = c;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ n = push_mri_control (mri_if);
+
+ parse_mri_control_expression (s - 3, qual, (const char *) NULL,
+ n->next, s[1] == '.' ? s[2] : '\0');
+
+ if (s[1] == '.')
+ input_line_pointer = s + 3;
+ else
+ input_line_pointer = s + 1;
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI else pseudo-op. If we are currently doing an MRI
+ structured IF, associate the ELSE with the IF. Otherwise, assume
+ it is a conditional else. */
+
+static void
+s_mri_else (int qual)
+{
+ int c;
+ char *buf;
+ char q[2];
+
+ if (qual == '\0'
+ && (mri_control_stack == NULL
+ || mri_control_stack->type != mri_if
+ || mri_control_stack->else_seen))
+ {
+ s_else (0);
+ return;
+ }
+
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+ if (ignore_input ())
+ {
+ *input_line_pointer = c;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_if
+ || mri_control_stack->else_seen)
+ {
+ as_bad (_("else without matching if"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ mri_control_stack->else_seen = 1;
+
+ buf = (char *) xmalloc (20 + strlen (mri_control_stack->bottom));
+ q[0] = TOLOWER (qual);
+ q[1] = '\0';
+ sprintf (buf, "bra%s %s", q, mri_control_stack->bottom);
+ mri_assemble (buf);
+ free (buf);
+
+ colon (mri_control_stack->next);
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI ENDI pseudo-op. */
+
+static void
+s_mri_endi (int ignore ATTRIBUTE_UNUSED)
+{
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_if)
+ {
+ as_bad (_("endi without matching if"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* ignore_input will not return true for ENDI, so we don't need to
+ worry about checking it again here. */
+
+ if (! mri_control_stack->else_seen)
+ colon (mri_control_stack->next);
+ colon (mri_control_stack->bottom);
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI BREAK pseudo-op. */
+
+static void
+s_mri_break (int extent)
+{
+ struct mri_control_info *n;
+ char *buf;
+ char ex[2];
+
+ n = mri_control_stack;
+ while (n != NULL
+ && n->type != mri_for
+ && n->type != mri_repeat
+ && n->type != mri_while)
+ n = n->outer;
+ if (n == NULL)
+ {
+ as_bad (_("break outside of structured loop"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ buf = (char *) xmalloc (20 + strlen (n->bottom));
+ ex[0] = TOLOWER (extent);
+ ex[1] = '\0';
+ sprintf (buf, "bra%s %s", ex, n->bottom);
+ mri_assemble (buf);
+ free (buf);
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI NEXT pseudo-op. */
+
+static void
+s_mri_next (int extent)
+{
+ struct mri_control_info *n;
+ char *buf;
+ char ex[2];
+
+ n = mri_control_stack;
+ while (n != NULL
+ && n->type != mri_for
+ && n->type != mri_repeat
+ && n->type != mri_while)
+ n = n->outer;
+ if (n == NULL)
+ {
+ as_bad (_("next outside of structured loop"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ buf = (char *) xmalloc (20 + strlen (n->next));
+ ex[0] = TOLOWER (extent);
+ ex[1] = '\0';
+ sprintf (buf, "bra%s %s", ex, n->next);
+ mri_assemble (buf);
+ free (buf);
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI FOR pseudo-op. */
+
+static void
+s_mri_for (int qual)
+{
+ const char *varstart, *varstop;
+ const char *initstart, *initstop;
+ const char *endstart, *endstop;
+ const char *bystart, *bystop;
+ int up;
+ int by;
+ int extent;
+ struct mri_control_info *n;
+ char *buf;
+ char *s;
+ char ex[2];
+
+ /* The syntax is
+ FOR.q var = init { TO | DOWNTO } end [ BY by ] DO.e
+ */
+
+ SKIP_WHITESPACE ();
+ varstart = input_line_pointer;
+
+ /* Look for the '='. */
+ while (! is_end_of_line[(unsigned char) *input_line_pointer]
+ && *input_line_pointer != '=')
+ ++input_line_pointer;
+ if (*input_line_pointer != '=')
+ {
+ as_bad (_("missing ="));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ varstop = input_line_pointer;
+ if (varstop > varstart
+ && (varstop[-1] == ' ' || varstop[-1] == '\t'))
+ --varstop;
+
+ ++input_line_pointer;
+
+ initstart = input_line_pointer;
+
+ /* Look for TO or DOWNTO. */
+ up = 1;
+ initstop = NULL;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (strncasecmp (input_line_pointer, "TO", 2) == 0
+ && ! is_part_of_name (input_line_pointer[2]))
+ {
+ initstop = input_line_pointer;
+ input_line_pointer += 2;
+ break;
+ }
+ if (strncasecmp (input_line_pointer, "DOWNTO", 6) == 0
+ && ! is_part_of_name (input_line_pointer[6]))
+ {
+ initstop = input_line_pointer;
+ up = 0;
+ input_line_pointer += 6;
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (initstop == NULL)
+ {
+ as_bad (_("missing to or downto"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (initstop > initstart
+ && (initstop[-1] == ' ' || initstop[-1] == '\t'))
+ --initstop;
+
+ SKIP_WHITESPACE ();
+ endstart = input_line_pointer;
+
+ /* Look for BY or DO. */
+ by = 0;
+ endstop = NULL;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (strncasecmp (input_line_pointer, "BY", 2) == 0
+ && ! is_part_of_name (input_line_pointer[2]))
+ {
+ endstop = input_line_pointer;
+ by = 1;
+ input_line_pointer += 2;
+ break;
+ }
+ if (strncasecmp (input_line_pointer, "DO", 2) == 0
+ && (input_line_pointer[2] == '.'
+ || ! is_part_of_name (input_line_pointer[2])))
+ {
+ endstop = input_line_pointer;
+ input_line_pointer += 2;
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (endstop == NULL)
+ {
+ as_bad (_("missing do"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (endstop > endstart
+ && (endstop[-1] == ' ' || endstop[-1] == '\t'))
+ --endstop;
+
+ if (! by)
+ {
+ bystart = "#1";
+ bystop = bystart + 2;
+ }
+ else
+ {
+ SKIP_WHITESPACE ();
+ bystart = input_line_pointer;
+
+ /* Look for DO. */
+ bystop = NULL;
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (strncasecmp (input_line_pointer, "DO", 2) == 0
+ && (input_line_pointer[2] == '.'
+ || ! is_part_of_name (input_line_pointer[2])))
+ {
+ bystop = input_line_pointer;
+ input_line_pointer += 2;
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (bystop == NULL)
+ {
+ as_bad (_("missing do"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (bystop > bystart
+ && (bystop[-1] == ' ' || bystop[-1] == '\t'))
+ --bystop;
+ }
+
+ if (*input_line_pointer != '.')
+ extent = '\0';
+ else
+ {
+ extent = input_line_pointer[1];
+ input_line_pointer += 2;
+ }
+
+ /* We have fully parsed the FOR operands. Now build the loop. */
+ n = push_mri_control (mri_for);
+
+ buf = (char *) xmalloc (50 + (input_line_pointer - varstart));
+
+ /* Move init,var. */
+ s = buf;
+ *s++ = 'm';
+ *s++ = 'o';
+ *s++ = 'v';
+ *s++ = 'e';
+ if (qual != '\0')
+ *s++ = TOLOWER (qual);
+ *s++ = ' ';
+ memcpy (s, initstart, initstop - initstart);
+ s += initstop - initstart;
+ *s++ = ',';
+ memcpy (s, varstart, varstop - varstart);
+ s += varstop - varstart;
+ *s = '\0';
+ mri_assemble (buf);
+
+ colon (n->top);
+
+ /* cmp end,var. */
+ s = buf;
+ *s++ = 'c';
+ *s++ = 'm';
+ *s++ = 'p';
+ if (qual != '\0')
+ *s++ = TOLOWER (qual);
+ *s++ = ' ';
+ memcpy (s, endstart, endstop - endstart);
+ s += endstop - endstart;
+ *s++ = ',';
+ memcpy (s, varstart, varstop - varstart);
+ s += varstop - varstart;
+ *s = '\0';
+ mri_assemble (buf);
+
+ /* bcc bottom. */
+ ex[0] = TOLOWER (extent);
+ ex[1] = '\0';
+ if (up)
+ sprintf (buf, "blt%s %s", ex, n->bottom);
+ else
+ sprintf (buf, "bgt%s %s", ex, n->bottom);
+ mri_assemble (buf);
+
+ /* Put together the add or sub instruction used by ENDF. */
+ s = buf;
+ if (up)
+ strcpy (s, "add");
+ else
+ strcpy (s, "sub");
+ s += 3;
+ if (qual != '\0')
+ *s++ = TOLOWER (qual);
+ *s++ = ' ';
+ memcpy (s, bystart, bystop - bystart);
+ s += bystop - bystart;
+ *s++ = ',';
+ memcpy (s, varstart, varstop - varstart);
+ s += varstop - varstart;
+ *s = '\0';
+ n->incr = buf;
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI ENDF pseudo-op. */
+
+static void
+s_mri_endf (int ignore ATTRIBUTE_UNUSED)
+{
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_for)
+ {
+ as_bad (_("endf without for"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ colon (mri_control_stack->next);
+
+ mri_assemble (mri_control_stack->incr);
+
+ sprintf (mri_control_stack->incr, "bra %s", mri_control_stack->top);
+ mri_assemble (mri_control_stack->incr);
+
+ free (mri_control_stack->incr);
+
+ colon (mri_control_stack->bottom);
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI REPEAT pseudo-op. */
+
+static void
+s_mri_repeat (int ignore ATTRIBUTE_UNUSED)
+{
+ struct mri_control_info *n;
+
+ n = push_mri_control (mri_repeat);
+ colon (n->top);
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI UNTIL pseudo-op. */
+
+static void
+s_mri_until (int qual)
+{
+ char *s;
+
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_repeat)
+ {
+ as_bad (_("until without repeat"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ colon (mri_control_stack->next);
+
+ for (s = input_line_pointer; ! is_end_of_line[(unsigned char) *s]; s++)
+ ;
+
+ parse_mri_control_expression (s, qual, (const char *) NULL,
+ mri_control_stack->top, '\0');
+
+ colon (mri_control_stack->bottom);
+
+ input_line_pointer = s;
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI WHILE pseudo-op. */
+
+static void
+s_mri_while (int qual)
+{
+ char *s;
+
+ struct mri_control_info *n;
+
+ s = input_line_pointer;
+ /* We only accept '*' as introduction of comments if preceded by white space
+ or at first column of a line (I think this can't actually happen here?)
+ This is important when assembling:
+ while d0 <ne> 12(a0,d0*2) do
+ while d0 <ne> #CONST*20 do. */
+ while (! (is_end_of_line[(unsigned char) *s]
+ || (flag_mri
+ && *s == '*'
+ && (s == input_line_pointer
+ || *(s-1) == ' '
+ || *(s-1) == '\t'))))
+ s++;
+ --s;
+ while (*s == ' ' || *s == '\t')
+ --s;
+ if (s - input_line_pointer > 1
+ && s[-1] == '.')
+ s -= 2;
+ if (s - input_line_pointer < 2
+ || strncasecmp (s - 1, "DO", 2) != 0)
+ {
+ as_bad (_("missing do"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ n = push_mri_control (mri_while);
+
+ colon (n->next);
+
+ parse_mri_control_expression (s - 1, qual, (const char *) NULL, n->bottom,
+ s[1] == '.' ? s[2] : '\0');
+
+ input_line_pointer = s + 1;
+ if (*input_line_pointer == '.')
+ input_line_pointer += 2;
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the MRI ENDW pseudo-op. */
+
+static void
+s_mri_endw (int ignore ATTRIBUTE_UNUSED)
+{
+ char *buf;
+
+ if (mri_control_stack == NULL
+ || mri_control_stack->type != mri_while)
+ {
+ as_bad (_("endw without while"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ buf = (char *) xmalloc (20 + strlen (mri_control_stack->next));
+ sprintf (buf, "bra %s", mri_control_stack->next);
+ mri_assemble (buf);
+ free (buf);
+
+ colon (mri_control_stack->bottom);
+
+ pop_mri_control ();
+
+ if (flag_mri)
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a .cpu directive. */
+
+static void
+s_m68k_cpu (int ignored ATTRIBUTE_UNUSED)
+{
+ char saved_char;
+ char *name;
+
+ if (initialized)
+ {
+ as_bad (_("already assembled instructions"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE(*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ m68k_set_cpu (name, 1, 0);
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+}
+
+/* Parse a .arch directive. */
+
+static void
+s_m68k_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ char saved_char;
+ char *name;
+
+ if (initialized)
+ {
+ as_bad (_("already assembled instructions"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ while (*input_line_pointer && *input_line_pointer != ','
+ && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ if (m68k_set_arch (name, 1, 0))
+ {
+ /* Scan extensions. */
+ do
+ {
+ *input_line_pointer++ = saved_char;
+ if (!*input_line_pointer || ISSPACE (*input_line_pointer))
+ break;
+ name = input_line_pointer;
+ while (*input_line_pointer && *input_line_pointer != ','
+ && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+ }
+ while (m68k_set_extension (name, 1, 0));
+ }
+
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+}
+
+/* Lookup a cpu name in TABLE and return the slot found. Return NULL
+ if none is found, the caller is responsible for emitting an error
+ message. If ALLOW_M is non-zero, we allow an initial 'm' on the
+ cpu name, if it begins with a '6' (possibly skipping an intervening
+ 'c'. We also allow a 'c' in the same place. if NEGATED is
+ non-zero, we accept a leading 'no-' and *NEGATED is set to true, if
+ the option is indeed negated. */
+
+static const struct m68k_cpu *
+m68k_lookup_cpu (const char *arg, const struct m68k_cpu *table,
+ int allow_m, int *negated)
+{
+ /* allow negated value? */
+ if (negated)
+ {
+ *negated = 0;
+
+ if (arg[0] == 'n' && arg[1] == 'o' && arg[2] == '-')
+ {
+ arg += 3;
+ *negated = 1;
+ }
+ }
+
+ /* Remove 'm' or 'mc' prefix from 68k variants. */
+ if (allow_m)
+ {
+ if (arg[0] == 'm')
+ {
+ if (arg[1] == '6')
+ arg += 1;
+ else if (arg[1] == 'c' && arg[2] == '6')
+ arg += 2;
+ }
+ }
+ else if (arg[0] == 'c' && arg[1] == '6')
+ arg += 1;
+
+ for (; table->name; table++)
+ if (!strcmp (arg, table->name))
+ {
+ if (table->alias < -1 || table->alias > 1)
+ as_bad (_("`%s' is deprecated, use `%s'"),
+ table->name, table[table->alias < 0 ? 1 : -1].name);
+ return table;
+ }
+ return 0;
+}
+
+/* Set the cpu, issuing errors if it is unrecognized. */
+
+static int
+m68k_set_cpu (char const *name, int allow_m, int silent)
+{
+ const struct m68k_cpu *cpu;
+
+ cpu = m68k_lookup_cpu (name, m68k_cpus, allow_m, NULL);
+
+ if (!cpu)
+ {
+ if (!silent)
+ as_bad (_("cpu `%s' unrecognized"), name);
+ return 0;
+ }
+ selected_cpu = cpu;
+ return 1;
+}
+
+/* Set the architecture, issuing errors if it is unrecognized. */
+
+static int
+m68k_set_arch (char const *name, int allow_m, int silent)
+{
+ const struct m68k_cpu *arch;
+
+ arch = m68k_lookup_cpu (name, m68k_archs, allow_m, NULL);
+
+ if (!arch)
+ {
+ if (!silent)
+ as_bad (_("architecture `%s' unrecognized"), name);
+ return 0;
+ }
+ selected_arch = arch;
+ return 1;
+}
+
+/* Set the architecture extension, issuing errors if it is
+ unrecognized, or invalid */
+
+static int
+m68k_set_extension (char const *name, int allow_m, int silent)
+{
+ int negated;
+ const struct m68k_cpu *ext;
+
+ ext = m68k_lookup_cpu (name, m68k_extensions, allow_m, &negated);
+
+ if (!ext)
+ {
+ if (!silent)
+ as_bad (_("extension `%s' unrecognized"), name);
+ return 0;
+ }
+
+ if (negated)
+ not_current_architecture |= (ext->control_regs
+ ? *(unsigned *)ext->control_regs: ext->arch);
+ else
+ current_architecture |= ext->arch;
+ return 1;
+}
+
+/* md_parse_option
+ Invocation line includes a switch not recognized by the base assembler.
+ */
+
+#ifdef OBJ_ELF
+const char *md_shortopts = "lSA:m:kQ:V";
+#else
+const char *md_shortopts = "lSA:m:k";
+#endif
+
+struct option md_longopts[] = {
+#define OPTION_PIC (OPTION_MD_BASE)
+ {"pic", no_argument, NULL, OPTION_PIC},
+#define OPTION_REGISTER_PREFIX_OPTIONAL (OPTION_MD_BASE + 1)
+ {"register-prefix-optional", no_argument, NULL,
+ OPTION_REGISTER_PREFIX_OPTIONAL},
+#define OPTION_BITWISE_OR (OPTION_MD_BASE + 2)
+ {"bitwise-or", no_argument, NULL, OPTION_BITWISE_OR},
+#define OPTION_BASE_SIZE_DEFAULT_16 (OPTION_MD_BASE + 3)
+ {"base-size-default-16", no_argument, NULL, OPTION_BASE_SIZE_DEFAULT_16},
+#define OPTION_BASE_SIZE_DEFAULT_32 (OPTION_MD_BASE + 4)
+ {"base-size-default-32", no_argument, NULL, OPTION_BASE_SIZE_DEFAULT_32},
+#define OPTION_DISP_SIZE_DEFAULT_16 (OPTION_MD_BASE + 5)
+ {"disp-size-default-16", no_argument, NULL, OPTION_DISP_SIZE_DEFAULT_16},
+#define OPTION_DISP_SIZE_DEFAULT_32 (OPTION_MD_BASE + 6)
+ {"disp-size-default-32", no_argument, NULL, OPTION_DISP_SIZE_DEFAULT_32},
+#define OPTION_PCREL (OPTION_MD_BASE + 7)
+ {"pcrel", no_argument, NULL, OPTION_PCREL},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'l': /* -l means keep external to 2 bit offset
+ rather than 16 bit one. */
+ flag_short_refs = 1;
+ break;
+
+ case 'S': /* -S means that jbsr's always turn into
+ jsr's. */
+ flag_long_jumps = 1;
+ break;
+
+ case OPTION_PCREL: /* --pcrel means never turn PC-relative
+ branches into absolute jumps. */
+ flag_keep_pcrel = 1;
+ break;
+
+ case OPTION_PIC:
+ case 'k':
+ flag_want_pic = 1;
+ break; /* -pic, Position Independent Code. */
+
+ case OPTION_REGISTER_PREFIX_OPTIONAL:
+ flag_reg_prefix_optional = 1;
+ reg_prefix_optional_seen = 1;
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ case OPTION_BITWISE_OR:
+ {
+ char *n, *t;
+ const char *s;
+
+ n = (char *) xmalloc (strlen (m68k_comment_chars) + 1);
+ t = n;
+ for (s = m68k_comment_chars; *s != '\0'; s++)
+ if (*s != '|')
+ *t++ = *s;
+ *t = '\0';
+ m68k_comment_chars = n;
+ }
+ break;
+
+ case OPTION_BASE_SIZE_DEFAULT_16:
+ m68k_index_width_default = SIZE_WORD;
+ break;
+
+ case OPTION_BASE_SIZE_DEFAULT_32:
+ m68k_index_width_default = SIZE_LONG;
+ break;
+
+ case OPTION_DISP_SIZE_DEFAULT_16:
+ m68k_rel32 = 0;
+ m68k_rel32_from_cmdline = 1;
+ break;
+
+ case OPTION_DISP_SIZE_DEFAULT_32:
+ m68k_rel32 = 1;
+ m68k_rel32_from_cmdline = 1;
+ break;
+
+ case 'A':
+#if WARN_DEPRECATED
+ as_tsktsk (_ ("option `-A%s' is deprecated: use `-%s'",
+ arg, arg));
+#endif
+ /* Intentional fall-through. */
+ case 'm':
+ if (!strncmp (arg, "arch=", 5))
+ m68k_set_arch (arg + 5, 1, 0);
+ else if (!strncmp (arg, "cpu=", 4))
+ m68k_set_cpu (arg + 4, 1, 0);
+ else if (m68k_set_extension (arg, 0, 1))
+ ;
+ else if (m68k_set_arch (arg, 0, 1))
+ ;
+ else if (m68k_set_cpu (arg, 0, 1))
+ ;
+ else
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Setup tables from the selected arch and/or cpu */
+
+static void
+m68k_init_arch (void)
+{
+ if (not_current_architecture & current_architecture)
+ {
+ as_bad (_("architecture features both enabled and disabled"));
+ not_current_architecture &= ~current_architecture;
+ }
+ if (selected_arch)
+ {
+ current_architecture |= selected_arch->arch;
+ control_regs = selected_arch->control_regs;
+ }
+ else
+ current_architecture |= selected_cpu->arch;
+
+ current_architecture &= ~not_current_architecture;
+
+ if ((current_architecture & (cfloat | m68881)) == (cfloat | m68881))
+ {
+ /* Determine which float is really meant. */
+ if (current_architecture & (m68k_mask & ~m68881))
+ current_architecture ^= cfloat;
+ else
+ current_architecture ^= m68881;
+ }
+
+ if (selected_cpu)
+ {
+ control_regs = selected_cpu->control_regs;
+ if (current_architecture & ~selected_cpu->arch)
+ {
+ as_bad (_("selected processor does not have all features of selected architecture"));
+ current_architecture
+ = selected_cpu->arch & ~not_current_architecture;
+ }
+ }
+
+ if ((current_architecture & m68k_mask)
+ && (current_architecture & ~m68k_mask))
+ {
+ as_bad (_ ("m68k and cf features both selected"));
+ if (current_architecture & m68k_mask)
+ current_architecture &= m68k_mask;
+ else
+ current_architecture &= ~m68k_mask;
+ }
+
+ /* Permit m68881 specification with all cpus; those that can't work
+ with a coprocessor could be doing emulation. */
+ if (current_architecture & m68851)
+ {
+ if (current_architecture & m68040)
+ as_warn (_("68040 and 68851 specified; mmu instructions may assemble incorrectly"));
+ }
+ /* What other incompatibilities could we check for? */
+
+ if (cpu_of_arch (current_architecture) < m68020
+ || arch_coldfire_p (current_architecture))
+ md_relax_table[TAB (PCINDEX, BYTE)].rlx_more = 0;
+
+ initialized = 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ const char *default_cpu = TARGET_CPU;
+ int i;
+
+ /* Get the canonical name for the default target CPU. */
+ if (*default_cpu == 'm')
+ default_cpu++;
+ for (i = 0; m68k_cpus[i].name; i++)
+ {
+ if (strcasecmp (default_cpu, m68k_cpus[i].name) == 0)
+ {
+ while (m68k_cpus[i].alias > 0)
+ i--;
+ while (m68k_cpus[i].alias < 0)
+ i++;
+ default_cpu = m68k_cpus[i].name;
+ }
+ }
+
+ fprintf (stream, _("\
+-march=<arch> set architecture\n\
+-mcpu=<cpu> set cpu [default %s]\n\
+"), default_cpu);
+ for (i = 0; m68k_extensions[i].name; i++)
+ fprintf (stream, _("\
+-m[no-]%-16s enable/disable%s architecture extension\n\
+"), m68k_extensions[i].name,
+ m68k_extensions[i].alias > 0 ? " ColdFire"
+ : m68k_extensions[i].alias < 0 ? " m68k" : "");
+
+ fprintf (stream, _("\
+-l use 1 word for refs to undefined symbols [default 2]\n\
+-pic, -k generate position independent code\n\
+-S turn jbsr into jsr\n\
+--pcrel never turn PC-relative branches into absolute jumps\n\
+--register-prefix-optional\n\
+ recognize register names without prefix character\n\
+--bitwise-or do not treat `|' as a comment character\n\
+--base-size-default-16 base reg without size is 16 bits\n\
+--base-size-default-32 base reg without size is 32 bits (default)\n\
+--disp-size-default-16 displacement with unknown size is 16 bits\n\
+--disp-size-default-32 displacement with unknown size is 32 bits (default)\n\
+"));
+
+ fprintf (stream, _("Architecture variants are: "));
+ for (i = 0; m68k_archs[i].name; i++)
+ {
+ if (i)
+ fprintf (stream, " | ");
+ fprintf (stream, "%s", m68k_archs[i].name);
+ }
+ fprintf (stream, "\n");
+
+ fprintf (stream, _("Processor variants are: "));
+ for (i = 0; m68k_cpus[i].name; i++)
+ {
+ if (i)
+ fprintf (stream, " | ");
+ fprintf (stream, "%s", m68k_cpus[i].name);
+ }
+ fprintf (stream, _("\n"));
+}
+
+#ifdef TEST2
+
+/* TEST2: Test md_assemble() */
+/* Warning, this routine probably doesn't work anymore. */
+int
+main (void)
+{
+ struct m68k_it the_ins;
+ char buf[120];
+ char *cp;
+ int n;
+
+ m68k_ip_begin ();
+ for (;;)
+ {
+ if (!gets (buf) || !*buf)
+ break;
+ if (buf[0] == '|' || buf[1] == '.')
+ continue;
+ for (cp = buf; *cp; cp++)
+ if (*cp == '\t')
+ *cp = ' ';
+ if (is_label (buf))
+ continue;
+ memset (&the_ins, '\0', sizeof (the_ins));
+ m68k_ip (&the_ins, buf);
+ if (the_ins.error)
+ {
+ printf (_("Error %s in %s\n"), the_ins.error, buf);
+ }
+ else
+ {
+ printf (_("Opcode(%d.%s): "), the_ins.numo, the_ins.args);
+ for (n = 0; n < the_ins.numo; n++)
+ printf (" 0x%x", the_ins.opcode[n] & 0xffff);
+ printf (" ");
+ print_the_insn (&the_ins.opcode[0], stdout);
+ (void) putchar ('\n');
+ }
+ for (n = 0; n < strlen (the_ins.args) / 2; n++)
+ {
+ if (the_ins.operands[n].error)
+ {
+ printf ("op%d Error %s in %s\n", n, the_ins.operands[n].error, buf);
+ continue;
+ }
+ printf ("mode %d, reg %d, ", the_ins.operands[n].mode,
+ the_ins.operands[n].reg);
+ if (the_ins.operands[n].b_const)
+ printf ("Constant: '%.*s', ",
+ 1 + the_ins.operands[n].e_const - the_ins.operands[n].b_const,
+ the_ins.operands[n].b_const);
+ printf ("ireg %d, isiz %d, imul %d, ", the_ins.operands[n].ireg,
+ the_ins.operands[n].isiz, the_ins.operands[n].imul);
+ if (the_ins.operands[n].b_iadd)
+ printf ("Iadd: '%.*s',",
+ 1 + the_ins.operands[n].e_iadd - the_ins.operands[n].b_iadd,
+ the_ins.operands[n].b_iadd);
+ putchar ('\n');
+ }
+ }
+ m68k_ip_end ();
+ return 0;
+}
+
+int
+is_label (char *str)
+{
+ while (*str == ' ')
+ str++;
+ while (*str && *str != ' ')
+ str++;
+ if (str[-1] == ':' || str[1] == '=')
+ return 1;
+ return 0;
+}
+
+#endif
+
+/* Possible states for relaxation:
+
+ 0 0 branch offset byte (bra, etc)
+ 0 1 word
+ 0 2 long
+
+ 1 0 indexed offsets byte a0@(32,d4:w:1) etc
+ 1 1 word
+ 1 2 long
+
+ 2 0 two-offset index word-word a0@(32,d4)@(45) etc
+ 2 1 word-long
+ 2 2 long-word
+ 2 3 long-long
+
+ */
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+#ifdef OBJ_AOUT
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+#endif
+
+ return size;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the 68k, it is relative to the address of the first extension
+ word. The difference between the addresses of the offset and the
+ first extension word is stored in fx_pcrel_adjust. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ int adjust;
+
+ adjust = fixP->fx_pcrel_adjust;
+ if (adjust == 64)
+ adjust = -1;
+ return fixP->fx_where + fixP->fx_frag->fr_address - adjust;
+}
+
+#ifdef OBJ_ELF
+void
+m68k_elf_final_processing (void)
+{
+ unsigned flags = 0;
+
+ if (arch_coldfire_fpu (current_architecture))
+ flags |= EF_M68K_CFV4E;
+ /* Set file-specific flags if this is a cpu32 processor. */
+ if (cpu_of_arch (current_architecture) & cpu32)
+ flags |= EF_M68K_CPU32;
+ else if (cpu_of_arch (current_architecture) & fido_a)
+ flags |= EF_M68K_FIDO;
+ else if ((cpu_of_arch (current_architecture) & m68000up)
+ && !(cpu_of_arch (current_architecture) & m68020up))
+ flags |= EF_M68K_M68000;
+
+ if (current_architecture & mcfisa_a)
+ {
+ static const unsigned isa_features[][2] =
+ {
+ {EF_M68K_CF_ISA_A_NODIV,mcfisa_a},
+ {EF_M68K_CF_ISA_A, mcfisa_a|mcfhwdiv},
+ {EF_M68K_CF_ISA_A_PLUS, mcfisa_a|mcfisa_aa|mcfhwdiv|mcfusp},
+ {EF_M68K_CF_ISA_B_NOUSP,mcfisa_a|mcfisa_b|mcfhwdiv},
+ {EF_M68K_CF_ISA_B, mcfisa_a|mcfisa_b|mcfhwdiv|mcfusp},
+ {EF_M68K_CF_ISA_C, mcfisa_a|mcfisa_c|mcfhwdiv|mcfusp},
+ {EF_M68K_CF_ISA_C_NODIV,mcfisa_a|mcfisa_c|mcfusp},
+ {0,0},
+ };
+ static const unsigned mac_features[][2] =
+ {
+ {EF_M68K_CF_MAC, mcfmac},
+ {EF_M68K_CF_EMAC, mcfemac},
+ {0,0},
+ };
+ unsigned ix;
+ unsigned pattern;
+
+ pattern = (current_architecture
+ & (mcfisa_a|mcfisa_aa|mcfisa_b|mcfisa_c|mcfhwdiv|mcfusp));
+ for (ix = 0; isa_features[ix][1]; ix++)
+ {
+ if (pattern == isa_features[ix][1])
+ {
+ flags |= isa_features[ix][0];
+ break;
+ }
+ }
+ if (!isa_features[ix][1])
+ {
+ cf_bad:
+ as_warn (_("Not a defined coldfire architecture"));
+ }
+ else
+ {
+ if (current_architecture & cfloat)
+ flags |= EF_M68K_CF_FLOAT | EF_M68K_CFV4E;
+
+ pattern = current_architecture & (mcfmac|mcfemac);
+ if (pattern)
+ {
+ for (ix = 0; mac_features[ix][1]; ix++)
+ {
+ if (pattern == mac_features[ix][1])
+ {
+ flags |= mac_features[ix][0];
+ break;
+ }
+ }
+ if (!mac_features[ix][1])
+ goto cf_bad;
+ }
+ }
+ }
+ elf_elfheader (stdoutput)->e_flags |= flags;
+}
+
+/* Parse @TLSLDO and return the desired relocation. */
+static bfd_reloc_code_real_type
+m68k_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+
+ if (*str++ != '@')
+ return BFD_RELOC_UNUSED;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = ch;
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ if (strncmp (ident, "TLSLDO", 6) == 0
+ && len == 6)
+ {
+ /* Now check for identifier@suffix+constant. */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+ *str_p = str;
+
+ return BFD_RELOC_68K_TLS_LDO32;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+/* Handles .long <tls_symbol>+0x8000 debug info.
+ Clobbers input_line_pointer, checks end-of-line.
+ Adapted from tc-ppc.c:ppc_elf_cons. */
+static void
+m68k_elf_cons (int nbytes /* 4=.long */)
+{
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ expression (&exp);
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && (reloc = m68k_elf_suffix (&input_line_pointer,
+ &exp)) != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ size = bfd_get_reloc_size (reloc_howto);
+
+ if (size > nbytes)
+ {
+ as_bad (_("%s relocations do not fit in %d bytes\n"),
+ reloc_howto->name, nbytes);
+ }
+ else
+ {
+ char *p;
+ int offset;
+
+ p = frag_more (nbytes);
+ offset = 0;
+ if (target_big_endian)
+ offset = nbytes - size;
+ fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+ &exp, 0, reloc);
+ }
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Put terminator back into stream. */
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+#endif
+
+int
+tc_m68k_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum;
+ static const char *const regnames[] =
+ {
+ "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+ "a0", "a1", "a2", "a3", "a4", "a5", "a6", "sp",
+ "fp0", "fp1", "fp2", "fp3", "fp4", "fp5", "fp6", "fp7",
+ "pc"
+ };
+
+ for (regnum = 0; regnum < ARRAY_SIZE (regnames); regnum++)
+ if (strcmp (regname, regnames[regnum]) == 0)
+ return regnum;
+
+ return -1;
+}
+
+void
+tc_m68k_frame_initial_instructions (void)
+{
+ static int sp_regno = -1;
+
+ if (sp_regno < 0)
+ sp_regno = tc_m68k_regname_to_dw2regnum ("sp");
+
+ cfi_add_CFA_def_cfa (sp_regno, -DWARF2_CIE_DATA_ALIGNMENT);
+ cfi_add_CFA_offset (DWARF2_DEFAULT_RETURN_COLUMN, DWARF2_CIE_DATA_ALIGNMENT);
+}
+
+/* Check and emit error if broken-word handling has failed to fix up a
+ case-table. This is called from write.c, after doing everything it
+ knows about how to handle broken words. */
+
+void
+tc_m68k_check_adjusted_broken_word (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 (%#lx) overflows: `switch'-statement too large."),
+ (long) new_offset);
+}
+
diff --git a/gas/config/tc-m68k.h b/gas/config/tc-m68k.h
new file mode 100644
index 0000000..3978b6a
--- /dev/null
+++ b/gas/config/tc-m68k.h
@@ -0,0 +1,194 @@
+/* This file is tc-m68k.h
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_M68K 1
+
+struct fix;
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#ifdef OBJ_AOUT
+#ifdef TE_SUN3
+#define TARGET_FORMAT "a.out-sunos-big"
+#endif
+#ifdef TE_NetBSD
+#define TARGET_FORMAT "a.out-m68k-netbsd"
+#endif
+#ifdef TE_LINUX
+#define TARGET_FORMAT "a.out-m68k-linux"
+#endif
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "a.out-zero-big"
+#endif
+#endif
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-m68k"
+#endif
+
+#ifdef TE_APOLLO
+#define COFF_MAGIC APOLLOM68KMAGIC
+#define COFF_AOUTHDR_MAGIC APOLLO_COFF_VERSION_NUMBER
+#undef OBJ_COFF_OMIT_OPTIONAL_HEADER
+#endif
+
+#ifdef TE_AUX
+#define TARGET_FORMAT "coff-m68k-aux"
+#endif
+#ifdef TE_DELTA
+#define TARGET_FORMAT "coff-m68k-sysv"
+#endif
+
+#ifndef COFF_MAGIC
+#define COFF_MAGIC MC68MAGIC
+#endif
+#define TARGET_ARCH bfd_arch_m68k
+
+#define tc_comment_chars m68k_comment_chars
+extern const char *m68k_comment_chars;
+
+#define LISTING_WORD_SIZE 2 /* A word is 2 bytes */
+#define LISTING_LHS_WIDTH 2 /* One word on the first line */
+#define LISTING_LHS_WIDTH_SECOND 2 /* One word on the second line */
+#define LISTING_LHS_CONT_LINES 4/* And 4 lines max */
+#define LISTING_HEADER "68K GAS "
+
+#ifndef REGISTER_PREFIX
+#define REGISTER_PREFIX '%'
+#endif
+
+#if !defined (REGISTER_PREFIX_OPTIONAL)
+#if defined (M68KCOFF) || defined (OBJ_ELF)
+#define REGISTER_PREFIX_OPTIONAL 0
+#else /* ! (COFF || ELF) */
+#define REGISTER_PREFIX_OPTIONAL 1
+#endif /* ! (COFF || ELF) */
+#endif /* not def REGISTER_PREFIX and not def OPTIONAL_REGISTER_PREFIX */
+
+#ifdef TE_DELTA
+/* On the Delta, `%' can occur within a label name, but not as the
+ initial character. */
+#define LEX_PCT LEX_NAME
+/* On the Delta, `~' can start a label name, but is converted to '.'. */
+#define LEX_TILDE LEX_BEGIN_NAME
+#define tc_canonicalize_symbol_name(s) ((*(s) == '~' ? *(s) = '.' : '.'), s)
+/* On the Delta, dots are not required before pseudo-ops. */
+#define NO_PSEUDO_DOT 1
+#endif
+
+extern void m68k_mri_mode_change (int);
+#define MRI_MODE_CHANGE(i) m68k_mri_mode_change (i)
+
+extern int m68k_conditional_pseudoop (pseudo_typeS *);
+#define tc_conditional_pseudoop(pop) m68k_conditional_pseudoop (pop)
+
+extern void m68k_frob_label (symbolS *);
+#define tc_frob_label(sym) m68k_frob_label (sym)
+
+extern void m68k_flush_pending_output (void);
+#define md_flush_pending_output() m68k_flush_pending_output ()
+
+extern void m68k_frob_symbol (symbolS *);
+
+#define tc_frob_symbol(sym,punt) \
+do \
+ { \
+ if (S_GET_SEGMENT (sym) == reg_section) \
+ punt = 1; \
+ m68k_frob_symbol (sym); \
+ } \
+while (0)
+
+#define NO_RELOC BFD_RELOC_NONE
+#define RELAX_RELOC_ABS8 BFD_RELOC_8
+#define RELAX_RELOC_ABS16 BFD_RELOC_16
+#define RELAX_RELOC_ABS32 BFD_RELOC_32
+#define RELAX_RELOC_PC8 BFD_RELOC_8_PCREL
+#define RELAX_RELOC_PC16 BFD_RELOC_16_PCREL
+#define RELAX_RELOC_PC32 BFD_RELOC_32_PCREL
+
+#ifdef OBJ_ELF
+#define tc_fix_adjustable(X) tc_m68k_fix_adjustable(X)
+extern int tc_m68k_fix_adjustable (struct fix *);
+
+/* Target *-*-elf implies an embedded target. No shared libs.
+ *-*-uclinux also requires special casing to prevent GAS from
+ generating unsupported R_68K_PC16 relocs. */
+#define EXTERN_FORCE_RELOC \
+ ((strcmp (TARGET_OS, "elf") != 0) && (strcmp (TARGET_OS, "uclinux") != 0))
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define elf_tc_final_processing m68k_elf_final_processing
+extern void m68k_elf_final_processing (void);
+#endif
+
+#define DIFF_EXPR_OK
+
+extern int m68k_parse_long_option (char *);
+#define md_parse_long_option m68k_parse_long_option
+
+#define md_operand(x)
+
+#define TARGET_ARCH bfd_arch_m68k
+
+extern struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* We can't do a byte jump to the next instruction, so in that case
+ force word mode by faking AIM. */
+#define md_prepare_relax_scan(fragP, address, aim, this_state, this_type) \
+ do \
+ { \
+ if (aim == 0 && this_type->rlx_forward == 127) \
+ aim = 128; \
+ } \
+ while (0)
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+#define DWARF2_DEFAULT_RETURN_COLUMN 24
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+#define tc_regname_to_dw2regnum tc_m68k_regname_to_dw2regnum
+extern int tc_m68k_regname_to_dw2regnum (char *regname);
+
+#define tc_cfi_frame_initial_instructions tc_m68k_frame_initial_instructions
+extern void tc_m68k_frame_initial_instructions (void);
+
+#ifdef TE_UCLINUX
+/* elf2flt does not honor PT_LOAD's from the executable.
+ .text and .eh_frame sections will not end up in the same segment and so
+ we cannot use PC-relative encoding for CFI. */
+# define CFI_DIFF_EXPR_OK 0
+
+/* However, follow compiler's guidance when it specifies encoding for LSDA. */
+# define CFI_DIFF_LSDA_OK 1
+#endif
+
+struct broken_word;
+#define TC_CHECK_ADJUSTED_BROKEN_DOT_WORD(new_offset, brokw) \
+ tc_m68k_check_adjusted_broken_word ((offsetT) (new_offset), (brokw))
+extern void tc_m68k_check_adjusted_broken_word (offsetT,
+ struct broken_word *);
diff --git a/gas/config/tc-mcore.c b/gas/config/tc-mcore.c
new file mode 100644
index 0000000..d111eda
--- /dev/null
+++ b/gas/config/tc-mcore.c
@@ -0,0 +1,2235 @@
+/* tc-mcore.c -- Assemble code for M*Core
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#include "../opcodes/mcore-opc.h"
+#include "safe-ctype.h"
+
+#ifdef OBJ_ELF
+#include "elf/mcore.h"
+#endif
+
+#ifndef streq
+#define streq(a,b) (strcmp (a, b) == 0)
+#endif
+
+/* Forward declarations for dumb compilers. */
+
+/* Several places in this file insert raw instructions into the
+ object. They should use MCORE_INST_XXX macros to get the opcodes
+ and then use these two macros to crack the MCORE_INST value into
+ the appropriate byte values. */
+#define INST_BYTE0(x) (target_big_endian ? (((x) >> 8) & 0xFF) : ((x) & 0xFF))
+#define INST_BYTE1(x) (target_big_endian ? ((x) & 0xFF) : (((x) >> 8) & 0xFF))
+
+const char comment_chars[] = "#/";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "#/";
+
+static int do_jsri2bsr = 0; /* Change here from 1 by Cruess 19 August 97. */
+static int sifilter_mode = 0;
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ As in 0f12.456
+ or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+#define C(what,length) (((what) << 2) + (length))
+#define GET_WHAT(x) ((x >> 2))
+
+/* These are the two types of relaxable instruction. */
+#define COND_JUMP 1
+#define UNCD_JUMP 2
+
+#define UNDEF_DISP 0
+#define DISP12 1
+#define DISP32 2
+#define UNDEF_WORD_DISP 3
+
+#define C12_LEN 2
+#define C32_LEN 10 /* Allow for align. */
+#define U12_LEN 2
+#define U32_LEN 8 /* Allow for align. */
+
+typedef enum
+{
+ M210,
+ M340
+}
+cpu_type;
+
+cpu_type cpu = M340;
+
+/* Initialize the relax table. */
+const relax_typeS md_relax_table[] =
+{
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+
+ /* COND_JUMP */
+ { 0, 0, 0, 0 }, /* UNDEF_DISP */
+ { 2048, -2046, C12_LEN, C(COND_JUMP, DISP32) }, /* DISP12 */
+ { 0, 0, C32_LEN, 0 }, /* DISP32 */
+ { 0, 0, C32_LEN, 0 }, /* UNDEF_WORD_DISP */
+
+ /* UNCD_JUMP */
+ { 0, 0, 0, 0 }, /* UNDEF_DISP */
+ { 2048, -2046, U12_LEN, C(UNCD_JUMP, DISP32) }, /* DISP12 */
+ { 0, 0, U32_LEN, 0 }, /* DISP32 */
+ { 0, 0, U32_LEN, 0 } /* UNDEF_WORD_DISP */
+
+};
+
+/* Literal pool data structures. */
+struct literal
+{
+ unsigned short refcnt;
+ unsigned char ispcrel;
+ unsigned char unused;
+ expressionS e;
+};
+
+#define MAX_POOL_SIZE (1024/4)
+static struct literal litpool [MAX_POOL_SIZE];
+static unsigned poolsize;
+static unsigned poolnumber;
+static unsigned long poolspan;
+
+/* SPANPANIC: the point at which we get too scared and force a dump
+ of the literal pool, and perhaps put a branch in place.
+ Calculated as:
+ 1024 span of lrw/jmpi/jsri insn (actually span+1)
+ -2 possible alignment at the insn.
+ -2 possible alignment to get the table aligned.
+ -2 an inserted branch around the table.
+ == 1018
+ at 1018, we might be in trouble.
+ -- so we have to be smaller than 1018 and since we deal with 2-byte
+ instructions, the next good choice is 1016.
+ -- Note we have a test case that fails when we've got 1018 here. */
+#define SPANPANIC (1016) /* 1024 - 1 entry - 2 byte rounding. */
+#define SPANCLOSE (900)
+#define SPANEXIT (600)
+static symbolS * poolsym; /* Label for current pool. */
+static char poolname[8];
+static struct hash_control * opcode_hash_control; /* Opcode mnemonics. */
+
+#define POOL_END_LABEL ".LE"
+#define POOL_START_LABEL ".LS"
+
+static void
+make_name (char * s, char * p, int n)
+{
+ static const char hex[] = "0123456789ABCDEF";
+
+ s[0] = p[0];
+ s[1] = p[1];
+ s[2] = p[2];
+ s[3] = hex[(n >> 12) & 0xF];
+ s[4] = hex[(n >> 8) & 0xF];
+ s[5] = hex[(n >> 4) & 0xF];
+ s[6] = hex[(n) & 0xF];
+ s[7] = 0;
+}
+
+static void
+dump_literals (int isforce)
+{
+ unsigned int i;
+ struct literal * p;
+ symbolS * brarsym = NULL;
+
+ if (poolsize == 0)
+ return;
+
+ /* Must we branch around the literal table? */
+ if (isforce)
+ {
+ char * output;
+ char brarname[8];
+
+ make_name (brarname, POOL_END_LABEL, poolnumber);
+
+ brarsym = symbol_make (brarname);
+
+ symbol_table_insert (brarsym);
+
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (UNCD_JUMP, DISP32)].rlx_length,
+ md_relax_table[C (UNCD_JUMP, DISP12)].rlx_length,
+ C (UNCD_JUMP, 0), brarsym, 0, 0);
+ output[0] = INST_BYTE0 (MCORE_INST_BR); /* br .+xxx */
+ output[1] = INST_BYTE1 (MCORE_INST_BR);
+ }
+
+ /* Make sure that the section is sufficiently aligned and that
+ the literal table is aligned within it. */
+ record_alignment (now_seg, 2);
+ frag_align (2, 0, 0);
+
+ colon (S_GET_NAME (poolsym));
+
+ for (i = 0, p = litpool; i < poolsize; i++, p++)
+ emit_expr (& p->e, 4);
+
+ if (brarsym != NULL)
+ colon (S_GET_NAME (brarsym));
+
+ poolsize = 0;
+}
+
+static void
+mcore_s_literals (int ignore ATTRIBUTE_UNUSED)
+{
+ dump_literals (0);
+ demand_empty_rest_of_line ();
+}
+
+/* Perform FUNC (ARG), and track number of bytes added to frag. */
+
+static void
+mcore_pool_count (void (*func) (int), int arg)
+{
+ const fragS *curr_frag = frag_now;
+ offsetT added = -frag_now_fix_octets ();
+
+ (*func) (arg);
+
+ while (curr_frag != frag_now)
+ {
+ added += curr_frag->fr_fix;
+ curr_frag = curr_frag->fr_next;
+ }
+
+ added += frag_now_fix_octets ();
+ poolspan += added;
+}
+
+static void
+check_literals (int kind, int offset)
+{
+ poolspan += offset;
+
+ /* SPANCLOSE and SPANEXIT are smaller numbers than SPANPANIC.
+ SPANPANIC means that we must dump now.
+ kind == 0 is any old instruction.
+ kind > 0 means we just had a control transfer instruction.
+ kind == 1 means within a function
+ kind == 2 means we just left a function
+
+ The dump_literals (1) call inserts a branch around the table, so
+ we first look to see if its a situation where we won't have to
+ insert a branch (e.g., the previous instruction was an unconditional
+ branch).
+
+ SPANPANIC is the point where we must dump a single-entry pool.
+ it accounts for alignments and an inserted branch.
+ the 'poolsize*2' accounts for the scenario where we do:
+ lrw r1,lit1; lrw r2,lit2; lrw r3,lit3
+ Note that the 'lit2' reference is 2 bytes further along
+ but the literal it references will be 4 bytes further along,
+ so we must consider the poolsize into this equation.
+ This is slightly over-cautious, but guarantees that we won't
+ panic because a relocation is too distant. */
+
+ if (poolspan > SPANCLOSE && kind > 0)
+ dump_literals (0);
+ else if (poolspan > SPANEXIT && kind > 1)
+ dump_literals (0);
+ else if (poolspan >= (SPANPANIC - poolsize * 2))
+ dump_literals (1);
+}
+
+static void
+mcore_cons (int nbytes)
+{
+ if (now_seg == text_section)
+ mcore_pool_count (cons, nbytes);
+ else
+ cons (nbytes);
+
+ /* In theory we ought to call check_literals (2,0) here in case
+ we need to dump the literal table. We cannot do this however,
+ as the directives that we are intercepting may be being used
+ to build a switch table, and we must not interfere with its
+ contents. Instead we cross our fingers and pray... */
+}
+
+static void
+mcore_float_cons (int float_type)
+{
+ if (now_seg == text_section)
+ mcore_pool_count (float_cons, float_type);
+ else
+ float_cons (float_type);
+
+ /* See the comment in mcore_cons () about calling check_literals.
+ It is unlikely that a switch table will be constructed using
+ floating point values, but it is still likely that an indexed
+ table of floating point constants is being created by these
+ directives, so again we must not interfere with their placement. */
+}
+
+static void
+mcore_stringer (int append_zero)
+{
+ if (now_seg == text_section)
+ mcore_pool_count (stringer, append_zero);
+ else
+ stringer (append_zero);
+
+ /* We call check_literals here in case a large number of strings are
+ being placed into the text section with a sequence of stringer
+ directives. In theory we could be upsetting something if these
+ strings are actually in an indexed table instead of referenced by
+ individual labels. Let us hope that that never happens. */
+ check_literals (2, 0);
+}
+
+static void
+mcore_fill (int unused)
+{
+ if (now_seg == text_section)
+ mcore_pool_count (s_fill, unused);
+ else
+ s_fill (unused);
+
+ check_literals (2, 0);
+}
+
+/* Handle the section changing pseudo-ops. These call through to the
+ normal implementations, but they dump the literal pool first. */
+
+static void
+mcore_s_text (int ignore)
+{
+ dump_literals (0);
+
+#ifdef OBJ_ELF
+ obj_elf_text (ignore);
+#else
+ s_text (ignore);
+#endif
+}
+
+static void
+mcore_s_data (int ignore)
+{
+ dump_literals (0);
+
+#ifdef OBJ_ELF
+ obj_elf_data (ignore);
+#else
+ s_data (ignore);
+#endif
+}
+
+static void
+mcore_s_section (int ignore)
+{
+ /* Scan forwards to find the name of the section. If the section
+ being switched to is ".line" then this is a DWARF1 debug section
+ which is arbitrarily placed inside generated code. In this case
+ do not dump the literal pool because it is a) inefficient and
+ b) would require the generation of extra code to jump around the
+ pool. */
+ char * ilp = input_line_pointer;
+
+ while (*ilp != 0 && ISSPACE (*ilp))
+ ++ ilp;
+
+ if (strncmp (ilp, ".line", 5) == 0
+ && (ISSPACE (ilp[5]) || *ilp == '\n' || *ilp == '\r'))
+ ;
+ else
+ dump_literals (0);
+
+#ifdef OBJ_ELF
+ obj_elf_section (ignore);
+#endif
+#ifdef OBJ_COFF
+ obj_coff_section (ignore);
+#endif
+}
+
+static void
+mcore_s_bss (int needs_align)
+{
+ dump_literals (0);
+
+ s_lcomm_bytes (needs_align);
+}
+
+#ifdef OBJ_ELF
+static void
+mcore_s_comm (int needs_align)
+{
+ dump_literals (0);
+
+ obj_elf_common (needs_align);
+}
+#endif
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ Pseudo-op name without dot
+ Function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "export", s_globl, 0 },
+ { "import", s_ignore, 0 },
+ { "literals", mcore_s_literals, 0 },
+ { "page", listing_eject, 0 },
+
+ /* The following are to intercept the placement of data into the text
+ section (eg addresses for a switch table), so that the space they
+ occupy can be taken into account when deciding whether or not to
+ dump the current literal pool.
+ XXX - currently we do not cope with the .space and .dcb.d directives. */
+ { "ascii", mcore_stringer, 8 + 0 },
+ { "asciz", mcore_stringer, 8 + 1 },
+ { "byte", mcore_cons, 1 },
+ { "dc", mcore_cons, 2 },
+ { "dc.b", mcore_cons, 1 },
+ { "dc.d", mcore_float_cons, 'd'},
+ { "dc.l", mcore_cons, 4 },
+ { "dc.s", mcore_float_cons, 'f'},
+ { "dc.w", mcore_cons, 2 },
+ { "dc.x", mcore_float_cons, 'x'},
+ { "double", mcore_float_cons, 'd'},
+ { "float", mcore_float_cons, 'f'},
+ { "hword", mcore_cons, 2 },
+ { "int", mcore_cons, 4 },
+ { "long", mcore_cons, 4 },
+ { "octa", mcore_cons, 16 },
+ { "quad", mcore_cons, 8 },
+ { "short", mcore_cons, 2 },
+ { "single", mcore_float_cons, 'f'},
+ { "string", mcore_stringer, 8 + 1 },
+ { "word", mcore_cons, 2 },
+ { "fill", mcore_fill, 0 },
+
+ /* Allow for the effect of section changes. */
+ { "text", mcore_s_text, 0 },
+ { "data", mcore_s_data, 0 },
+ { "bss", mcore_s_bss, 1 },
+#ifdef OBJ_ELF
+ { "comm", mcore_s_comm, 0 },
+#endif
+ { "section", mcore_s_section, 0 },
+ { "section.s", mcore_s_section, 0 },
+ { "sect", mcore_s_section, 0 },
+ { "sect.s", mcore_s_section, 0 },
+
+ { 0, 0, 0 }
+};
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs. */
+
+void
+md_begin (void)
+{
+ const mcore_opcode_info * opcode;
+ char * prev_name = "";
+
+ opcode_hash_control = hash_new ();
+
+ /* Insert unique names into hash table. */
+ for (opcode = mcore_table; opcode->name; opcode ++)
+ {
+ if (! streq (prev_name, opcode->name))
+ {
+ prev_name = opcode->name;
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ }
+ }
+}
+
+/* Get a log2(val). */
+
+static int
+mylog2 (unsigned int val)
+{
+ int log = -1;
+
+ while (val != 0)
+ {
+ log ++;
+ val >>= 1;
+ }
+
+ return log;
+}
+
+/* Try to parse a reg name. */
+
+static char *
+parse_reg (char * s, unsigned * reg)
+{
+ /* Strip leading whitespace. */
+ while (ISSPACE (* s))
+ ++ s;
+
+ if (TOLOWER (s[0]) == 'r')
+ {
+ if (s[1] == '1' && s[2] >= '0' && s[2] <= '5')
+ {
+ *reg = 10 + s[2] - '0';
+ return s + 3;
+ }
+
+ if (s[1] >= '0' && s[1] <= '9')
+ {
+ *reg = s[1] - '0';
+ return s + 2;
+ }
+ }
+ else if ( TOLOWER (s[0]) == 's'
+ && TOLOWER (s[1]) == 'p'
+ && ! ISALNUM (s[2]))
+ {
+ * reg = 0;
+ return s + 2;
+ }
+
+ as_bad (_("register expected, but saw '%.6s'"), s);
+ return s;
+}
+
+static struct Cregs
+{
+ char * name;
+ unsigned int crnum;
+}
+cregs[] =
+{
+ { "psr", 0},
+ { "vbr", 1},
+ { "epsr", 2},
+ { "fpsr", 3},
+ { "epc", 4},
+ { "fpc", 5},
+ { "ss0", 6},
+ { "ss1", 7},
+ { "ss2", 8},
+ { "ss3", 9},
+ { "ss4", 10},
+ { "gcr", 11},
+ { "gsr", 12},
+ { "", 0}
+};
+
+static char *
+parse_creg (char * s, unsigned * reg)
+{
+ int i;
+
+ /* Strip leading whitespace. */
+ while (ISSPACE (* s))
+ ++s;
+
+ if ((TOLOWER (s[0]) == 'c' && TOLOWER (s[1]) == 'r'))
+ {
+ if (s[2] == '3' && s[3] >= '0' && s[3] <= '1')
+ {
+ *reg = 30 + s[3] - '0';
+ return s + 4;
+ }
+
+ if (s[2] == '2' && s[3] >= '0' && s[3] <= '9')
+ {
+ *reg = 20 + s[3] - '0';
+ return s + 4;
+ }
+
+ if (s[2] == '1' && s[3] >= '0' && s[3] <= '9')
+ {
+ *reg = 10 + s[3] - '0';
+ return s + 4;
+ }
+
+ if (s[2] >= '0' && s[2] <= '9')
+ {
+ *reg = s[2] - '0';
+ return s + 3;
+ }
+ }
+
+ /* Look at alternate creg names before giving error. */
+ for (i = 0; cregs[i].name[0] != '\0'; i++)
+ {
+ char buf [10];
+ int length;
+ int j;
+
+ length = strlen (cregs[i].name);
+
+ for (j = 0; j < length; j++)
+ buf[j] = TOLOWER (s[j]);
+
+ if (strncmp (cregs[i].name, buf, length) == 0)
+ {
+ *reg = cregs[i].crnum;
+ return s + length;
+ }
+ }
+
+ as_bad (_("control register expected, but saw '%.6s'"), s);
+
+ return s;
+}
+
+static char *
+parse_psrmod (char * s, unsigned * reg)
+{
+ int i;
+ char buf[10];
+ static struct psrmods
+ {
+ char * name;
+ unsigned int value;
+ }
+ psrmods[] =
+ {
+ { "ie", 1 },
+ { "fe", 2 },
+ { "ee", 4 },
+ { "af", 8 } /* Really 0 and non-combinable. */
+ };
+
+ for (i = 0; i < 2; i++)
+ buf[i] = TOLOWER (s[i]);
+
+ for (i = sizeof (psrmods) / sizeof (psrmods[0]); i--;)
+ {
+ if (! strncmp (psrmods[i].name, buf, 2))
+ {
+ * reg = psrmods[i].value;
+
+ return s + 2;
+ }
+ }
+
+ as_bad (_("bad/missing psr specifier"));
+
+ * reg = 0;
+
+ return s;
+}
+
+static char *
+parse_exp (char * s, expressionS * e)
+{
+ char * save;
+ char * new_pointer;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* s))
+ ++ s;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+
+ expression (e);
+
+ if (e->X_op == O_absent)
+ as_bad (_("missing operand"));
+
+ new_pointer = input_line_pointer;
+ input_line_pointer = save;
+
+ return new_pointer;
+}
+
+static int
+enter_literal (expressionS * e, int ispcrel)
+{
+ unsigned int i;
+ struct literal * p;
+
+ if (poolsize >= MAX_POOL_SIZE - 2)
+ /* The literal pool is as full as we can handle. We have
+ to be 2 entries shy of the 1024/4=256 entries because we
+ have to allow for the branch (2 bytes) and the alignment
+ (2 bytes before the first insn referencing the pool and
+ 2 bytes before the pool itself) == 6 bytes, rounds up
+ to 2 entries. */
+ dump_literals (1);
+
+ if (poolsize == 0)
+ {
+ /* Create new literal pool. */
+ if (++ poolnumber > 0xFFFF)
+ as_fatal (_("more than 65K literal pools"));
+
+ make_name (poolname, POOL_START_LABEL, poolnumber);
+ poolsym = symbol_make (poolname);
+ symbol_table_insert (poolsym);
+ poolspan = 0;
+ }
+
+ /* Search pool for value so we don't have duplicates. */
+ for (p = litpool, i = 0; i < poolsize; i++, p++)
+ {
+ if (e->X_op == p->e.X_op
+ && e->X_add_symbol == p->e.X_add_symbol
+ && e->X_add_number == p->e.X_add_number
+ && ispcrel == p->ispcrel)
+ {
+ p->refcnt ++;
+ return i;
+ }
+ }
+
+ p->refcnt = 1;
+ p->ispcrel = ispcrel;
+ p->e = * e;
+
+ poolsize ++;
+
+ return i;
+}
+
+/* Parse a literal specification. -- either new or old syntax.
+ old syntax: the user supplies the label and places the literal.
+ new syntax: we put it into the literal pool. */
+
+static char *
+parse_rt (char * s,
+ char ** outputp,
+ int ispcrel,
+ expressionS * ep)
+{
+ expressionS e;
+ int n;
+
+ if (ep)
+ /* Indicate nothing there. */
+ ep->X_op = O_absent;
+
+ if (*s == '[')
+ {
+ s = parse_exp (s + 1, & e);
+
+ if (*s == ']')
+ s++;
+ else
+ as_bad (_("missing ']'"));
+ }
+ else
+ {
+ s = parse_exp (s, & e);
+
+ n = enter_literal (& e, ispcrel);
+
+ if (ep)
+ *ep = e;
+
+ /* Create a reference to pool entry. */
+ e.X_op = O_symbol;
+ e.X_add_symbol = poolsym;
+ e.X_add_number = n << 2;
+ }
+
+ * outputp = frag_more (2);
+
+ fix_new_exp (frag_now, (*outputp) - frag_now->fr_literal, 2, & e, 1,
+ BFD_RELOC_MCORE_PCREL_IMM8BY4);
+
+ return s;
+}
+
+static char *
+parse_imm (char * s,
+ unsigned * val,
+ unsigned min,
+ unsigned max)
+{
+ char * new_pointer;
+ expressionS e;
+
+ new_pointer = parse_exp (s, & e);
+
+ if (e.X_op == O_absent)
+ ; /* An error message has already been emitted. */
+ else if (e.X_op != O_constant)
+ as_bad (_("operand must be a constant"));
+ else if ((addressT) e.X_add_number < min || (addressT) e.X_add_number > max)
+ as_bad (_("operand must be absolute in range %u..%u, not %ld"),
+ min, max, (long) e.X_add_number);
+
+ * val = e.X_add_number;
+
+ return new_pointer;
+}
+
+static char *
+parse_mem (char * s,
+ unsigned * reg,
+ unsigned * off,
+ unsigned siz)
+{
+ * off = 0;
+
+ while (ISSPACE (* s))
+ ++ s;
+
+ if (* s == '(')
+ {
+ s = parse_reg (s + 1, reg);
+
+ while (ISSPACE (* s))
+ ++ s;
+
+ if (* s == ',')
+ {
+ s = parse_imm (s + 1, off, 0, 63);
+
+ if (siz > 1)
+ {
+ if (siz > 2)
+ {
+ if (* off & 0x3)
+ as_bad (_("operand must be a multiple of 4"));
+
+ * off >>= 2;
+ }
+ else
+ {
+ if (* off & 0x1)
+ as_bad (_("operand must be a multiple of 2"));
+
+ * off >>= 1;
+ }
+ }
+ }
+
+ while (ISSPACE (* s))
+ ++ s;
+
+ if (* s == ')')
+ s ++;
+ }
+ else
+ as_bad (_("base register expected"));
+
+ return s;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char * str)
+{
+ char * op_start;
+ char * op_end;
+ mcore_opcode_info * opcode;
+ char * output;
+ int nlen = 0;
+ unsigned short inst;
+ unsigned reg;
+ unsigned off;
+ unsigned isize;
+ expressionS e;
+ char name[21];
+
+ /* Drop leading whitespace. */
+ while (ISSPACE (* str))
+ str ++;
+
+ /* Find the op code end. */
+ for (op_start = op_end = str;
+ nlen < 20 && !is_end_of_line [(unsigned char) *op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = op_start[nlen];
+ nlen++;
+ }
+
+ name [nlen] = 0;
+
+ if (nlen == 0)
+ {
+ as_bad (_("can't find opcode "));
+ return;
+ }
+
+ opcode = (mcore_opcode_info *) hash_find (opcode_hash_control, name);
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), name);
+ return;
+ }
+
+ inst = opcode->inst;
+ isize = 2;
+
+ switch (opcode->opclass)
+ {
+ case O0:
+ output = frag_more (2);
+ break;
+
+ case OT:
+ op_end = parse_imm (op_end + 1, & reg, 0, 3);
+ inst |= reg;
+ output = frag_more (2);
+ break;
+
+ case O1:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+ output = frag_more (2);
+ break;
+
+ case JMP:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+ output = frag_more (2);
+ /* In a sifilter mode, we emit this insn 2 times,
+ fixes problem of an interrupt during a jmp.. */
+ if (sifilter_mode)
+ {
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+ output = frag_more (2);
+ }
+ break;
+
+ case JSR:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg == 15)
+ as_bad (_("invalid register: r15 illegal"));
+
+ inst |= reg;
+ output = frag_more (2);
+
+ if (sifilter_mode)
+ {
+ /* Replace with: bsr .+2 ; addi r15,6; jmp rx ; jmp rx. */
+ inst = MCORE_INST_BSR; /* With 0 displacement. */
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+
+ output = frag_more (2);
+ inst = MCORE_INST_ADDI;
+ inst |= 15; /* addi r15,6 */
+ inst |= (6 - 1) << 4; /* Over the jmp's. */
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+
+ output = frag_more (2);
+ inst = MCORE_INST_JMP | reg;
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+
+ /* 2nd emitted in fallthrough. */
+ output = frag_more (2);
+ }
+ break;
+
+ case OC:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (*op_end == ',')
+ {
+ op_end = parse_creg (op_end + 1, & reg);
+ inst |= reg << 4;
+ }
+
+ output = frag_more (2);
+ break;
+
+ case MULSH:
+ if (cpu == M210)
+ {
+ as_bad (_("M340 specific opcode used when assembling for M210"));
+ break;
+ }
+ /* drop through... */
+ case O2:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case X1:
+ /* Handle both syntax-> xtrb- r1,rx OR xtrb- rx. */
+ op_end = parse_reg (op_end + 1, & reg);
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',') /* xtrb- r1,rx. */
+ {
+ if (reg != 1)
+ as_bad (_("destination register must be r1"));
+
+ op_end = parse_reg (op_end + 1, & reg);
+ }
+
+ inst |= reg;
+ output = frag_more (2);
+ break;
+
+ case O1R1: /* div- rx,r1. */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+ if (reg != 1)
+ as_bad (_("source register must be r1"));
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OI:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 32);
+ inst |= (reg - 1) << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OB:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 0, 31);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OB2:
+ /* Like OB, but arg is 2^n instead of n. */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 1 << 31);
+ /* Further restrict the immediate to a power of two. */
+ if ((reg & (reg - 1)) == 0)
+ reg = mylog2 (reg);
+ else
+ {
+ reg = 0;
+ as_bad (_("immediate is not a power of two"));
+ }
+ inst |= (reg) << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OBRa: /* Specific for bgeni: imm of 0->6 translate to movi. */
+ case OBRb:
+ case OBRc:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 0, 31);
+ /* Immediate values of 0 -> 6 translate to movi. */
+ if (reg <= 6)
+ {
+ inst = (inst & 0xF) | MCORE_INST_BGENI_ALT;
+ reg = 0x1 << reg;
+ as_warn (_("translating bgeni to movi"));
+ }
+ inst &= ~ 0x01f0;
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OBR2: /* Like OBR, but arg is 2^n instead of n. */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 1 << 31);
+
+ /* Further restrict the immediate to a power of two. */
+ if ((reg & (reg - 1)) == 0)
+ reg = mylog2 (reg);
+ else
+ {
+ reg = 0;
+ as_bad (_("immediate is not a power of two"));
+ }
+
+ /* Immediate values of 0 -> 6 translate to movi. */
+ if (reg <= 6)
+ {
+ inst = (inst & 0xF) | MCORE_INST_BGENI_ALT;
+ reg = 0x1 << reg;
+ as_warn (_("translating mgeni to movi"));
+ }
+
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OMa: /* Specific for bmaski: imm 1->7 translate to movi. */
+ case OMb:
+ case OMc:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 32);
+
+ /* Immediate values of 1 -> 7 translate to movi. */
+ if (reg <= 7)
+ {
+ inst = (inst & 0xF) | MCORE_INST_BMASKI_ALT;
+ reg = (0x1 << reg) - 1;
+ inst |= reg << 4;
+
+ as_warn (_("translating bmaski to movi"));
+ }
+ else
+ {
+ inst &= ~ 0x01F0;
+ inst |= (reg & 0x1F) << 4;
+ }
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case SI:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case I7:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 0, 0x7F);
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case LS:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg << 8;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ int size;
+
+ if ((inst & 0x6000) == 0)
+ size = 4;
+ else if ((inst & 0x6000) == 0x4000)
+ size = 2;
+ else if ((inst & 0x6000) == 0x2000)
+ size = 1;
+ else
+ abort ();
+
+ op_end = parse_mem (op_end + 1, & reg, & off, size);
+
+ if (off > 16)
+ as_bad (_("displacement too large (%d)"), off);
+ else
+ inst |= (reg) | (off << 4);
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case LR:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg == 0 || reg == 15)
+ as_bad (_("Invalid register: r0 and r15 illegal"));
+
+ inst |= (reg << 8);
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ /* parse_rt calls frag_more() for us. */
+ input_line_pointer = parse_rt (op_end + 1, & output, 0, 0);
+ op_end = input_line_pointer;
+ }
+ else
+ {
+ as_bad (_("second operand missing"));
+ output = frag_more (2); /* save its space */
+ }
+ break;
+
+ case LJ:
+ input_line_pointer = parse_rt (op_end + 1, & output, 1, 0);
+ /* parse_rt() calls frag_more() for us. */
+ op_end = input_line_pointer;
+ break;
+
+ case RM:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg == 0 || reg == 15)
+ as_bad (_("bad starting register: r0 and r15 invalid"));
+
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == '-')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 15)
+ as_bad (_("ending register must be r15"));
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+ }
+
+ if (* op_end == ',')
+ {
+ op_end ++;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == '(')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 0)
+ as_bad (_("bad base register: must be r0"));
+
+ if (* op_end == ')')
+ op_end ++;
+ }
+ else
+ as_bad (_("base register expected"));
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case RQ:
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 4)
+ as_fatal (_("first register must be r4"));
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == '-')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg != 7)
+ as_fatal (_("last register must be r7"));
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end ++;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == '(')
+ {
+ op_end = parse_reg (op_end + 1, & reg);
+
+ if (reg >= 4 && reg <= 7)
+ as_fatal ("base register cannot be r4, r5, r6, or r7");
+
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ')')
+ op_end ++;
+ }
+ else
+ as_bad (_("base register expected"));
+ }
+ else
+ as_bad (_("second operand missing"));
+ }
+ else
+ as_bad (_("reg-reg expected"));
+
+ output = frag_more (2);
+ break;
+
+ case BR:
+ input_line_pointer = parse_exp (op_end + 1, & e);
+ op_end = input_line_pointer;
+
+ output = frag_more (2);
+
+ fix_new_exp (frag_now, output-frag_now->fr_literal,
+ 2, & e, 1, BFD_RELOC_MCORE_PCREL_IMM11BY2);
+ break;
+
+ case BL:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg << 4;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_exp (op_end + 1, & e);
+ output = frag_more (2);
+
+ fix_new_exp (frag_now, output-frag_now->fr_literal,
+ 2, & e, 1, BFD_RELOC_MCORE_PCREL_IMM4BY2);
+ }
+ else
+ {
+ as_bad (_("second operand missing"));
+ output = frag_more (2);
+ }
+ break;
+
+ case JC:
+ input_line_pointer = parse_exp (op_end + 1, & e);
+ op_end = input_line_pointer;
+
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (COND_JUMP, DISP32)].rlx_length,
+ md_relax_table[C (COND_JUMP, DISP12)].rlx_length,
+ C (COND_JUMP, 0), e.X_add_symbol, e.X_add_number, 0);
+ isize = C32_LEN;
+ break;
+
+ case JU:
+ input_line_pointer = parse_exp (op_end + 1, & e);
+ op_end = input_line_pointer;
+
+ output = frag_var (rs_machine_dependent,
+ md_relax_table[C (UNCD_JUMP, DISP32)].rlx_length,
+ md_relax_table[C (UNCD_JUMP, DISP12)].rlx_length,
+ C (UNCD_JUMP, 0), e.X_add_symbol, e.X_add_number, 0);
+ isize = U32_LEN;
+ break;
+
+ case JL:
+ inst = MCORE_INST_JSRI; /* jsri */
+ input_line_pointer = parse_rt (op_end + 1, & output, 1, & e);
+ /* parse_rt() calls frag_more for us. */
+ op_end = input_line_pointer;
+
+ /* Only do this if we know how to do it ... */
+ if (e.X_op != O_absent && do_jsri2bsr)
+ {
+ /* Look at adding the R_PCREL_JSRIMM11BY2. */
+ fix_new_exp (frag_now, output-frag_now->fr_literal,
+ 2, & e, 1, BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2);
+ }
+ break;
+
+ case RSI:
+ /* SI, but imm becomes 32-imm. */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+
+ reg = 32 - reg;
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case DO21: /* O2, dup rd, lit must be 1 */
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+ inst |= reg << 4;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+
+ if (reg != 1)
+ as_bad (_("second operand must be 1"));
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case SIa:
+ op_end = parse_reg (op_end + 1, & reg);
+ inst |= reg;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* op_end))
+ ++ op_end;
+
+ if (* op_end == ',')
+ {
+ op_end = parse_imm (op_end + 1, & reg, 1, 31);
+
+ if (reg == 0)
+ as_bad (_("zero used as immediate value"));
+
+ inst |= reg << 4;
+ }
+ else
+ as_bad (_("second operand missing"));
+
+ output = frag_more (2);
+ break;
+
+ case OPSR:
+ if (cpu == M210)
+ {
+ as_bad (_("M340 specific opcode used when assembling for M210"));
+ break;
+ }
+
+ op_end = parse_psrmod (op_end + 1, & reg);
+
+ /* Look for further selectors. */
+ while (* op_end == ',')
+ {
+ unsigned value;
+
+ op_end = parse_psrmod (op_end + 1, & value);
+
+ if (value & reg)
+ as_bad (_("duplicated psr bit specifier"));
+
+ reg |= value;
+ }
+
+ if (reg > 8)
+ as_bad (_("`af' must appear alone"));
+
+ inst |= (reg & 0x7);
+ output = frag_more (2);
+ break;
+
+ default:
+ as_bad (_("unimplemented opcode \"%s\""), name);
+ }
+
+ /* Drop whitespace after all the operands have been parsed. */
+ while (ISSPACE (* op_end))
+ op_end ++;
+
+ /* Give warning message if the insn has more operands than required. */
+ if (strcmp (op_end, opcode->name) && strcmp (op_end, ""))
+ as_warn (_("ignoring operands: %s "), op_end);
+
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+
+ check_literals (opcode->transfer, isize);
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_mcore_end (void)
+{
+ dump_literals (0);
+ subseg_set (text_section, 0);
+}
+
+/* Various routines to kill one day. */
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+const char * md_shortopts = "";
+
+enum options
+{
+ OPTION_JSRI2BSR_ON = OPTION_MD_BASE,
+ OPTION_JSRI2BSR_OFF,
+ OPTION_SIFILTER_ON,
+ OPTION_SIFILTER_OFF,
+ OPTION_CPU,
+ OPTION_EB,
+ OPTION_EL,
+};
+
+struct option md_longopts[] =
+{
+ { "no-jsri2bsr", no_argument, NULL, OPTION_JSRI2BSR_OFF},
+ { "jsri2bsr", no_argument, NULL, OPTION_JSRI2BSR_ON},
+ { "sifilter", no_argument, NULL, OPTION_SIFILTER_ON},
+ { "no-sifilter", no_argument, NULL, OPTION_SIFILTER_OFF},
+ { "cpu", required_argument, NULL, OPTION_CPU},
+ { "EB", no_argument, NULL, OPTION_EB},
+ { "EL", no_argument, NULL, OPTION_EL},
+ { NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char * arg)
+{
+ switch (c)
+ {
+ case OPTION_CPU:
+ if (streq (arg, "210"))
+ {
+ cpu = M210;
+ target_big_endian = 1;
+ }
+ else if (streq (arg, "340"))
+ cpu = M340;
+ else
+ as_warn (_("unrecognised cpu type '%s'"), arg);
+ break;
+
+ case OPTION_EB: target_big_endian = 1; break;
+ case OPTION_EL: target_big_endian = 0; cpu = M340; break;
+ case OPTION_JSRI2BSR_ON: do_jsri2bsr = 1; break;
+ case OPTION_JSRI2BSR_OFF: do_jsri2bsr = 0; break;
+ case OPTION_SIFILTER_ON: sifilter_mode = 1; break;
+ case OPTION_SIFILTER_OFF: sifilter_mode = 0; break;
+ default: return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("\
+MCORE specific options:\n\
+ -{no-}jsri2bsr {dis}able jsri to bsr transformation (def: dis)\n\
+ -{no-}sifilter {dis}able silicon filter behavior (def: dis)\n\
+ -cpu=[210|340] select CPU type\n\
+ -EB assemble for a big endian system (default)\n\
+ -EL assemble for a little endian system\n"));
+}
+
+int md_short_jump_size;
+
+void
+md_create_short_jump (char * ptr ATTRIBUTE_UNUSED,
+ addressT from_Nddr ATTRIBUTE_UNUSED,
+ addressT to_Nddr ATTRIBUTE_UNUSED,
+ fragS * frag ATTRIBUTE_UNUSED,
+ symbolS * to_symbol ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("failed sanity check: short_jump"));
+}
+
+void
+md_create_long_jump (char * ptr ATTRIBUTE_UNUSED,
+ addressT from_Nddr ATTRIBUTE_UNUSED,
+ addressT to_Nddr ATTRIBUTE_UNUSED,
+ fragS * frag ATTRIBUTE_UNUSED,
+ symbolS * to_symbol ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("failed sanity check: long_jump"));
+}
+
+/* Called after relaxing, change the frags so they know how big they are. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP)
+{
+ char *buffer;
+ int targ_addr = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset;
+
+ buffer = fragP->fr_fix + fragP->fr_literal;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (COND_JUMP, DISP12):
+ case C (UNCD_JUMP, DISP12):
+ {
+ /* Get the address of the end of the instruction. */
+ int next_inst = fragP->fr_fix + fragP->fr_address + 2;
+ unsigned char t0;
+ int disp = targ_addr - next_inst;
+
+ if (disp & 1)
+ as_bad (_("odd displacement at %x"), next_inst - 2);
+
+ disp >>= 1;
+
+ if (! target_big_endian)
+ {
+ t0 = buffer[1] & 0xF8;
+
+ md_number_to_chars (buffer, disp, 2);
+
+ buffer[1] = (buffer[1] & 0x07) | t0;
+ }
+ else
+ {
+ t0 = buffer[0] & 0xF8;
+
+ md_number_to_chars (buffer, disp, 2);
+
+ buffer[0] = (buffer[0] & 0x07) | t0;
+ }
+
+ fragP->fr_fix += 2;
+ }
+ break;
+
+ case C (COND_JUMP, DISP32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ {
+ /* A conditional branch wont fit into 12 bits so:
+ b!cond 1f
+ jmpi 0f
+ .align 2
+ 0: .long disp
+ 1:
+
+ If the b!cond is 4 byte aligned, the literal which would
+ go at x+4 will also be aligned. */
+ int first_inst = fragP->fr_fix + fragP->fr_address;
+ int needpad = (first_inst & 3);
+
+ if (! target_big_endian)
+ buffer[1] ^= 0x08;
+ else
+ buffer[0] ^= 0x08; /* Toggle T/F bit. */
+
+ buffer[2] = INST_BYTE0 (MCORE_INST_JMPI); /* Build jmpi. */
+ buffer[3] = INST_BYTE1 (MCORE_INST_JMPI);
+
+ if (needpad)
+ {
+ if (! target_big_endian)
+ {
+ buffer[0] = 4; /* Branch over jmpi, pad, and ptr. */
+ buffer[2] = 1; /* Jmpi offset of 1 gets the pointer. */
+ }
+ else
+ {
+ buffer[1] = 4; /* Branch over jmpi, pad, and ptr. */
+ buffer[3] = 1; /* Jmpi offset of 1 gets the pointer. */
+ }
+
+ buffer[4] = 0; /* Alignment/pad. */
+ buffer[5] = 0;
+ buffer[6] = 0; /* Space for 32 bit address. */
+ buffer[7] = 0;
+ buffer[8] = 0;
+ buffer[9] = 0;
+
+ /* Make reloc for the long disp. */
+ fix_new (fragP, fragP->fr_fix + 6, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+
+ fragP->fr_fix += C32_LEN;
+ }
+ else
+ {
+ /* See comment below about this given gas' limitations for
+ shrinking the fragment. '3' is the amount of code that
+ we inserted here, but '4' is right for the space we reserved
+ for this fragment. */
+ if (! target_big_endian)
+ {
+ buffer[0] = 3; /* Branch over jmpi, and ptr. */
+ buffer[2] = 0; /* Jmpi offset of 0 gets the pointer. */
+ }
+ else
+ {
+ buffer[1] = 3; /* Branch over jmpi, and ptr. */
+ buffer[3] = 0; /* Jmpi offset of 0 gets the pointer. */
+ }
+
+ buffer[4] = 0; /* Space for 32 bit address. */
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+
+ /* Make reloc for the long disp. */
+ fix_new (fragP, fragP->fr_fix + 4, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+ fragP->fr_fix += C32_LEN;
+
+ /* Frag is actually shorter (see the other side of this ifdef)
+ but gas isn't prepared for that. We have to re-adjust
+ the branch displacement so that it goes beyond the
+ full length of the fragment, not just what we actually
+ filled in. */
+ if (! target_big_endian)
+ buffer[0] = 4; /* Jmpi, ptr, and the 'tail pad'. */
+ else
+ buffer[1] = 4; /* Jmpi, ptr, and the 'tail pad'. */
+ }
+ }
+ break;
+
+ case C (UNCD_JUMP, DISP32):
+ case C (UNCD_JUMP, UNDEF_WORD_DISP):
+ {
+ /* An unconditional branch will not fit in 12 bits, make code which
+ looks like:
+ jmpi 0f
+ .align 2
+ 0: .long disp
+ we need a pad if "first_inst" is 4 byte aligned.
+ [because the natural literal place is x + 2]. */
+ int first_inst = fragP->fr_fix + fragP->fr_address;
+ int needpad = !(first_inst & 3);
+
+ buffer[0] = INST_BYTE0 (MCORE_INST_JMPI); /* Build jmpi. */
+ buffer[1] = INST_BYTE1 (MCORE_INST_JMPI);
+
+ if (needpad)
+ {
+ if (! target_big_endian)
+ buffer[0] = 1; /* Jmpi offset of 1 since padded. */
+ else
+ buffer[1] = 1; /* Jmpi offset of 1 since padded. */
+ buffer[2] = 0; /* Alignment. */
+ buffer[3] = 0;
+ buffer[4] = 0; /* Space for 32 bit address. */
+ buffer[5] = 0;
+ buffer[6] = 0;
+ buffer[7] = 0;
+
+ /* Make reloc for the long disp. */
+ fix_new (fragP, fragP->fr_fix + 4, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+
+ fragP->fr_fix += U32_LEN;
+ }
+ else
+ {
+ if (! target_big_endian)
+ buffer[0] = 0; /* Jmpi offset of 0 if no pad. */
+ else
+ buffer[1] = 0; /* Jmpi offset of 0 if no pad. */
+ buffer[2] = 0; /* Space for 32 bit address. */
+ buffer[3] = 0;
+ buffer[4] = 0;
+ buffer[5] = 0;
+
+ /* Make reloc for the long disp. */
+ fix_new (fragP, fragP->fr_fix + 2, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_32);
+ fragP->fr_fix += U32_LEN;
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Applies the desired value to the specified location.
+ Also sets up addends for 'rela' type relocations. */
+
+void
+md_apply_fix (fixS * fixP,
+ valueT * valP,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ char * file = fixP->fx_file ? fixP->fx_file : _("unknown");
+ const char * symname;
+ /* Note: use offsetT because it is signed, valueT is unsigned. */
+ offsetT val = *valP;
+
+ symname = fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : _("<unknown>");
+ /* Save this for the addend in the relocation record. */
+ fixP->fx_addnumber = val;
+
+ if (fixP->fx_addsy != NULL)
+ {
+#ifdef OBJ_ELF
+ /* For ELF we can just return and let the reloc that will be generated
+ take care of everything. For COFF we still have to insert 'val'
+ into the insn since the addend field will be ignored. */
+ return;
+#endif
+ }
+ else
+ fixP->fx_done = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ /* Second byte of 2 byte opcode. */
+ case BFD_RELOC_MCORE_PCREL_IMM11BY2:
+ if ((val & 1) != 0)
+ as_bad_where (file, fixP->fx_line,
+ _("odd distance branch (0x%lx bytes)"), (long) val);
+ val /= 2;
+ if (((val & ~0x3ff) != 0) && ((val | 0x3ff) != -1))
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for branch to %s too far (0x%lx)"),
+ symname, (long) val);
+ if (target_big_endian)
+ {
+ buf[0] |= ((val >> 8) & 0x7);
+ buf[1] |= (val & 0xff);
+ }
+ else
+ {
+ buf[1] |= ((val >> 8) & 0x7);
+ buf[0] |= (val & 0xff);
+ }
+ break;
+
+ /* Lower 8 bits of 2 byte opcode. */
+ case BFD_RELOC_MCORE_PCREL_IMM8BY4:
+ val += 3;
+ val /= 4;
+ if (val & ~0xff)
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for lrw/jmpi/jsri to %s too far (0x%lx)"),
+ symname, (long) val);
+ else if (! target_big_endian)
+ buf[0] |= (val & 0xff);
+ else
+ buf[1] |= (val & 0xff);
+ break;
+
+ /* Loopt instruction. */
+ case BFD_RELOC_MCORE_PCREL_IMM4BY2:
+ if ((val < -32) || (val > -2))
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for loopt too far (0x%lx)"), (long) val);
+ val /= 2;
+ if (! target_big_endian)
+ buf[0] |= (val & 0xf);
+ else
+ buf[1] |= (val & 0xf);
+ break;
+
+ case BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2:
+ /* Conditional linker map jsri to bsr. */
+ /* If its a local target and close enough, fix it.
+ NB: >= -2k for backwards bsr; < 2k for forwards... */
+ if (fixP->fx_addsy == 0 && val >= -2048 && val < 2048)
+ {
+ long nval = (val / 2) & 0x7ff;
+ nval |= MCORE_INST_BSR;
+
+ /* REPLACE the instruction, don't just modify it. */
+ buf[0] = INST_BYTE0 (nval);
+ buf[1] = INST_BYTE1 (nval);
+ }
+ else
+ fixP->fx_done = 0;
+ break;
+
+ case BFD_RELOC_MCORE_PCREL_32:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ if (fixP->fx_addsy != NULL)
+ {
+ /* If the fix is an absolute reloc based on a symbol's
+ address, then it cannot be resolved until the final link. */
+ fixP->fx_done = 0;
+ }
+#ifdef OBJ_ELF
+ else
+#endif
+ {
+ if (fixP->fx_size == 4)
+ ;
+ else if (fixP->fx_size == 2 && val >= -32768 && val <= 32767)
+ ;
+ else if (fixP->fx_size == 1 && val >= -256 && val <= 255)
+ ;
+ else
+ abort ();
+ md_number_to_chars (buf, val, fixP->fx_size);
+ }
+ break;
+ }
+}
+
+void
+md_operand (expressionS * expressionP)
+{
+ /* Ignore leading hash symbol, if poresent. */
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+int md_long_jump_size;
+
+/* Called just before address relaxation, return the length
+ by which a fragment must grow to reach it's destination. */
+int
+md_estimate_size_before_relax (fragS * fragP, segT segment_type)
+{
+ switch (fragP->fr_subtype)
+ {
+ default:
+ abort ();
+
+ case C (UNCD_JUMP, UNDEF_DISP):
+ /* Used to be a branch to somewhere which was unknown. */
+ if (!fragP->fr_symbol)
+ fragP->fr_subtype = C (UNCD_JUMP, DISP12);
+ else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ fragP->fr_subtype = C (UNCD_JUMP, DISP12);
+ else
+ fragP->fr_subtype = C (UNCD_JUMP, UNDEF_WORD_DISP);
+ break;
+
+ case C (COND_JUMP, UNDEF_DISP):
+ /* Used to be a branch to somewhere which was unknown. */
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ /* Got a symbol and it's defined in this segment, become byte
+ sized - maybe it will fix up */
+ fragP->fr_subtype = C (COND_JUMP, DISP12);
+ else if (fragP->fr_symbol)
+ /* Its got a segment, but its not ours, so it will always be long. */
+ fragP->fr_subtype = C (COND_JUMP, UNDEF_WORD_DISP);
+ else
+ /* We know the abs value. */
+ fragP->fr_subtype = C (COND_JUMP, DISP12);
+ break;
+
+ case C (UNCD_JUMP, DISP12):
+ case C (UNCD_JUMP, DISP32):
+ case C (UNCD_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP, DISP12):
+ case C (COND_JUMP, DISP32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ /* When relaxing a section for the second time, we don't need to
+ do anything besides return the current size. */
+ break;
+ }
+
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* Put number into target byte order. */
+
+void
+md_number_to_chars (char * ptr, valueT use, int nbytes)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (ptr, use, nbytes);
+ else
+ number_to_chars_littleendian (ptr, use, nbytes);
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
+{
+ /* Byte alignment is fine. */
+ return size;
+}
+
+/* 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 ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ /* If the symbol is undefined or defined in another section
+ we leave the add number alone for the linker to fix it later.
+ Only account for the PC pre-bump (which is 2 bytes on the MCore). */
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixp->fx_addsy)
+ || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+
+ {
+ gas_assert (fixp->fx_size == 2); /* must be an insn */
+ return fixp->fx_size;
+ }
+#endif
+
+ /* The case where we are going to resolve things... */
+ return fixp->fx_size + fixp->fx_where + fixp->fx_frag->fr_address;
+}
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F (SZ, PCREL): code = (TYPE); break
+
+arelent *
+tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ arelent * rel;
+ bfd_reloc_code_real_type code;
+
+ switch (fixp->fx_r_type)
+ {
+ /* These confuse the size/pcrel macro approach. */
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_MCORE_PCREL_IMM4BY2:
+ case BFD_RELOC_MCORE_PCREL_IMM8BY4:
+ case BFD_RELOC_MCORE_PCREL_IMM11BY2:
+ case BFD_RELOC_MCORE_PCREL_JSR_IMM11BY2:
+ case BFD_RELOC_RVA:
+ code = fixp->fx_r_type;
+ break;
+
+ default:
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ code = fixp->fx_r_type;
+ as_bad (_("Can not do %d byte %srelocation"),
+ fixp->fx_size,
+ fixp->fx_pcrel ? _("pc-relative") : "");
+ }
+ break;
+ }
+
+ rel = xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ /* Always pass the addend along! */
+ rel->addend = fixp->fx_addnumber;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (rel->howto != NULL);
+ }
+
+ return rel;
+}
+
+#ifdef OBJ_ELF
+/* 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
+mcore_force_relocation (fixS * fix)
+{
+ if (fix->fx_r_type == BFD_RELOC_RVA)
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+/* Return true if the fix can be handled by GAS, false if it must
+ be passed through to the linker. */
+
+bfd_boolean
+mcore_fix_adjustable (fixS * fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+#endif /* OBJ_ELF */
diff --git a/gas/config/tc-mcore.h b/gas/config/tc-mcore.h
new file mode 100644
index 0000000..3437e00
--- /dev/null
+++ b/gas/config/tc-mcore.h
@@ -0,0 +1,95 @@
+/* This file is tc-mcore.h
+
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the
+ Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_MCORE
+#define TC_MCORE 1
+
+#define TARGET_ARCH bfd_arch_mcore
+/* Used to initialise target_big_endian. */
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define IGNORE_NONSTANDARD_ESCAPES
+
+/* Some pseudo-op semantic extensions. */
+#define PSEUDO_LCOMM_OPTIONAL_ALIGN
+
+#define LISTING_HEADER "M.CORE GAS"
+#define LISTING_LHS_CONT_LINES 4
+
+/* We want local label support. */
+#define LOCAL_LABELS_FB 1
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+#define md_end md_mcore_end
+
+/* Want the section information too... */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+
+#ifdef OBJ_COFF
+
+#define TARGET_FORMAT (target_big_endian ? "pe-mcore-big" : "pe-mcore-little")
+
+struct mcore_tc_sy
+{
+ int sy_flags;
+};
+
+#define TC_SYMFIELD_TYPE struct mcore_tc_sy
+
+# if defined TE_PE
+# define TC_FORCE_RELOCATION(x) \
+ ((x)->fx_r_type == BFD_RELOC_RVA || generic_force_reloc (x))
+# endif
+
+#endif /* OBJ_COFF */
+
+#ifdef OBJ_ELF
+
+#define TARGET_FORMAT (target_big_endian ? "elf32-mcore-big" : "elf32-mcore-little")
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) mcore_force_relocation (fix)
+
+#define tc_fix_adjustable(FIX) mcore_fix_adjustable (FIX)
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#endif /* OBJ_ELF */
+
+#ifndef TARGET_FORMAT
+# error No target format specified.
+#endif
+
+#include "write.h" /* For definition of fixS */
+
+extern void md_mcore_end (void);
+extern long md_pcrel_from_section (fixS *, segT);
+extern arelent * tc_gen_reloc (asection *, fixS *);
+extern int mcore_force_relocation (fixS *);
+extern bfd_boolean mcore_fix_adjustable (fixS *);
+
+#endif /* TC_MCORE */
diff --git a/gas/config/tc-mep.c b/gas/config/tc-mep.c
new file mode 100644
index 0000000..cb06881
--- /dev/null
+++ b/gas/config/tc-mep.c
@@ -0,0 +1,2202 @@
+/* tc-mep.c -- Assembler for the Toshiba Media Processor.
+ Copyright (C) 2001-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include <stdio.h>
+#include "dwarf2dbg.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/mep-desc.h"
+#include "opcodes/mep-opc.h"
+#include "cgen.h"
+#include "elf/common.h"
+#include "elf/mep.h"
+#include "libbfd.h"
+#include "xregex.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];
+} mep_insn;
+
+static int mode = CORE; /* Start in core mode. */
+static int pluspresent = 0;
+static int allow_disabled_registers = 0;
+static int library_flag = 0;
+static int mep_cop = EF_MEP_COP_NONE;
+
+/* We're going to need to store all of the instructions along with
+ their fixups so that we can parallelization grouping rules. */
+
+static mep_insn saved_insns[MAX_SAVED_FIXUP_CHAINS];
+static int num_insns_saved = 0;
+
+const char comment_chars[] = "#";
+const char line_comment_chars[] = ";#";
+const char line_separator_chars[] = ";";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+static void mep_switch_to_vliw_mode (int);
+static void mep_switch_to_core_mode (int);
+static void mep_s_vtext (int);
+static void mep_noregerr (int);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { "vliw", mep_switch_to_vliw_mode, 0 },
+ { "core", mep_switch_to_core_mode, 0 },
+ { "vtext", mep_s_vtext, 0 },
+ { "noregerr", mep_noregerr, 0 },
+ { NULL, NULL, 0 }
+};
+
+/* Relocations against symbols are done in two
+ parts, with a HI relocation and a LO relocation. Each relocation
+ has only 16 bits of space to store an addend. This means that in
+ order for the linker to handle carries correctly, it must be able
+ to locate both the HI and the LO relocation. This means that the
+ relocations must appear in order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct mep_hi_fixup
+{
+ struct mep_hi_fixup * next; /* Next HI fixup. */
+ fixS * fixp; /* This fixup. */
+ segT seg; /* The section this fixup is in. */
+};
+
+/* The list of unmatched HI relocs. */
+static struct mep_hi_fixup * mep_hi_fixup_list;
+
+
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#define OPTION_CONFIG (OPTION_MD_BASE + 2)
+#define OPTION_AVERAGE (OPTION_MD_BASE + 3)
+#define OPTION_NOAVERAGE (OPTION_MD_BASE + 4)
+#define OPTION_MULT (OPTION_MD_BASE + 5)
+#define OPTION_NOMULT (OPTION_MD_BASE + 6)
+#define OPTION_DIV (OPTION_MD_BASE + 7)
+#define OPTION_NODIV (OPTION_MD_BASE + 8)
+#define OPTION_BITOPS (OPTION_MD_BASE + 9)
+#define OPTION_NOBITOPS (OPTION_MD_BASE + 10)
+#define OPTION_LEADZ (OPTION_MD_BASE + 11)
+#define OPTION_NOLEADZ (OPTION_MD_BASE + 12)
+#define OPTION_ABSDIFF (OPTION_MD_BASE + 13)
+#define OPTION_NOABSDIFF (OPTION_MD_BASE + 14)
+#define OPTION_MINMAX (OPTION_MD_BASE + 15)
+#define OPTION_NOMINMAX (OPTION_MD_BASE + 16)
+#define OPTION_CLIP (OPTION_MD_BASE + 17)
+#define OPTION_NOCLIP (OPTION_MD_BASE + 18)
+#define OPTION_SATUR (OPTION_MD_BASE + 19)
+#define OPTION_NOSATUR (OPTION_MD_BASE + 20)
+#define OPTION_COP32 (OPTION_MD_BASE + 21)
+#define OPTION_REPEAT (OPTION_MD_BASE + 25)
+#define OPTION_NOREPEAT (OPTION_MD_BASE + 26)
+#define OPTION_DEBUG (OPTION_MD_BASE + 27)
+#define OPTION_NODEBUG (OPTION_MD_BASE + 28)
+#define OPTION_UCI (OPTION_MD_BASE + 29)
+#define OPTION_NOUCI (OPTION_MD_BASE + 30)
+#define OPTION_DSP (OPTION_MD_BASE + 31)
+#define OPTION_NODSP (OPTION_MD_BASE + 32)
+#define OPTION_LIBRARY (OPTION_MD_BASE + 33)
+
+struct option md_longopts[] = {
+ { "EB", no_argument, NULL, OPTION_EB},
+ { "EL", no_argument, NULL, OPTION_EL},
+ { "mconfig", required_argument, NULL, OPTION_CONFIG},
+ { "maverage", no_argument, NULL, OPTION_AVERAGE},
+ { "mno-average", no_argument, NULL, OPTION_NOAVERAGE},
+ { "mmult", no_argument, NULL, OPTION_MULT},
+ { "mno-mult", no_argument, NULL, OPTION_NOMULT},
+ { "mdiv", no_argument, NULL, OPTION_DIV},
+ { "mno-div", no_argument, NULL, OPTION_NODIV},
+ { "mbitops", no_argument, NULL, OPTION_BITOPS},
+ { "mno-bitops", no_argument, NULL, OPTION_NOBITOPS},
+ { "mleadz", no_argument, NULL, OPTION_LEADZ},
+ { "mno-leadz", no_argument, NULL, OPTION_NOLEADZ},
+ { "mabsdiff", no_argument, NULL, OPTION_ABSDIFF},
+ { "mno-absdiff", no_argument, NULL, OPTION_NOABSDIFF},
+ { "mminmax", no_argument, NULL, OPTION_MINMAX},
+ { "mno-minmax", no_argument, NULL, OPTION_NOMINMAX},
+ { "mclip", no_argument, NULL, OPTION_CLIP},
+ { "mno-clip", no_argument, NULL, OPTION_NOCLIP},
+ { "msatur", no_argument, NULL, OPTION_SATUR},
+ { "mno-satur", no_argument, NULL, OPTION_NOSATUR},
+ { "mcop32", no_argument, NULL, OPTION_COP32},
+ { "mdebug", no_argument, NULL, OPTION_DEBUG},
+ { "mno-debug", no_argument, NULL, OPTION_NODEBUG},
+ { "muci", no_argument, NULL, OPTION_UCI},
+ { "mno-uci", no_argument, NULL, OPTION_NOUCI},
+ { "mdsp", no_argument, NULL, OPTION_DSP},
+ { "mno-dsp", no_argument, NULL, OPTION_NODSP},
+ { "mlibrary", no_argument, NULL, OPTION_LIBRARY},
+ { NULL, 0, NULL, 0 } };
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Options which default to on/off together. See the comment where
+ this is used for details. Note that CP and CP64 are not in this
+ list because disabling those overrides the -mivc2 option. */
+#define OPTION_MASK \
+ ( (1 << CGEN_INSN_OPTIONAL_BIT_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_MUL_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_DIV_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_DEBUG_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_LDZ_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_ABS_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_AVE_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_MINMAX_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_CLIP_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_SAT_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_UCI_INSN) \
+ | (1 << CGEN_INSN_OPTIONAL_DSP_INSN) )
+
+const char * md_shortopts = "";
+static int optbits = 0;
+static int optbitset = 0;
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ int i, idx;
+ switch (c)
+ {
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+ case OPTION_CONFIG:
+ idx = 0;
+ for (i=1; mep_config_map[i].name; i++)
+ if (strcmp (mep_config_map[i].name, arg) == 0)
+ {
+ idx = i;
+ break;
+ }
+ if (!idx)
+ {
+ fprintf (stderr, "Error: unknown configuration %s\n", arg);
+ return 0;
+ }
+ mep_config_index = idx;
+ target_big_endian = mep_config_map[idx].big_endian;
+ break;
+ case OPTION_AVERAGE:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_AVE_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_AVE_INSN;
+ break;
+ case OPTION_NOAVERAGE:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_AVE_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_AVE_INSN;
+ break;
+ case OPTION_MULT:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_MUL_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_MUL_INSN;
+ break;
+ case OPTION_NOMULT:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_MUL_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_MUL_INSN;
+ break;
+ case OPTION_DIV:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_DIV_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_DIV_INSN;
+ break;
+ case OPTION_NODIV:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_DIV_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_DIV_INSN;
+ break;
+ case OPTION_BITOPS:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_BIT_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_BIT_INSN;
+ break;
+ case OPTION_NOBITOPS:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_BIT_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_BIT_INSN;
+ break;
+ case OPTION_LEADZ:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_LDZ_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_LDZ_INSN;
+ break;
+ case OPTION_NOLEADZ:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_LDZ_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_LDZ_INSN;
+ break;
+ case OPTION_ABSDIFF:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_ABS_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_ABS_INSN;
+ break;
+ case OPTION_NOABSDIFF:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_ABS_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_ABS_INSN;
+ break;
+ case OPTION_MINMAX:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN;
+ break;
+ case OPTION_NOMINMAX:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_MINMAX_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_MINMAX_INSN;
+ break;
+ case OPTION_CLIP:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_CLIP_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_CLIP_INSN;
+ break;
+ case OPTION_NOCLIP:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_CLIP_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_CLIP_INSN;
+ break;
+ case OPTION_SATUR:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_SAT_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_SAT_INSN;
+ break;
+ case OPTION_NOSATUR:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_SAT_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_SAT_INSN;
+ break;
+ case OPTION_COP32:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_CP_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_CP_INSN;
+ break;
+ case OPTION_DEBUG:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN;
+ break;
+ case OPTION_NODEBUG:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_DEBUG_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_DEBUG_INSN;
+ break;
+ case OPTION_UCI:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_UCI_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_UCI_INSN;
+ break;
+ case OPTION_NOUCI:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_UCI_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_UCI_INSN;
+ break;
+ case OPTION_DSP:
+ optbits |= 1 << CGEN_INSN_OPTIONAL_DSP_INSN;
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_DSP_INSN;
+ break;
+ case OPTION_NODSP:
+ optbits &= ~(1 << CGEN_INSN_OPTIONAL_DSP_INSN);
+ optbitset |= 1 << CGEN_INSN_OPTIONAL_DSP_INSN;
+ break;
+ case OPTION_LIBRARY:
+ library_flag = EF_MEP_LIBRARY;
+ break;
+ case OPTION_REPEAT:
+ case OPTION_NOREPEAT:
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("MeP specific command line options:\n\
+ -EB assemble for a big endian system\n\
+ -EL assemble for a little endian system (default)\n\
+ -mconfig=<name> specify a chip configuration to use\n\
+ -maverage -mno-average -mmult -mno-mult -mdiv -mno-div\n\
+ -mbitops -mno-bitops -mleadz -mno-leadz -mabsdiff -mno-absdiff\n\
+ -mminmax -mno-minmax -mclip -mno-clip -msatur -mno-satur -mcop32\n\
+ enable/disable the given opcodes\n\
+\n\
+ If -mconfig is given, the other -m options modify it. Otherwise,\n\
+ if no -m options are given, all core opcodes are enabled;\n\
+ if any enabling -m options are given, only those are enabled;\n\
+ if only disabling -m options are given, only those are disabled.\n\
+"));
+ if (mep_config_map[1].name)
+ {
+ int i;
+ fprintf (stream, " -mconfig=STR specify the configuration to use\n");
+ fprintf (stream, " Configurations:");
+ for (i=0; mep_config_map[i].name; i++)
+ fprintf (stream, " %s", mep_config_map[i].name);
+ fprintf (stream, "\n");
+ }
+}
+
+
+
+static void
+mep_check_for_disabled_registers (mep_insn *insn)
+{
+ static int initted = 0;
+ static int has_mul_div = 0;
+ static int has_cop = 0;
+ static int has_debug = 0;
+ unsigned int b, r;
+
+ if (allow_disabled_registers)
+ return;
+
+#if !CGEN_INT_INSN_P
+ if (target_big_endian)
+ b = insn->buffer[0] * 256 + insn->buffer[1];
+ else
+ b = insn->buffer[1] * 256 + insn->buffer[0];
+#else
+ b = insn->buffer[0];
+#endif
+
+ if ((b & 0xfffff00e) == 0x7008 /* stc */
+ || (b & 0xfffff00e) == 0x700a /* ldc */)
+ {
+ if (!initted)
+ {
+ initted = 1;
+ if ((MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_MUL_INSN))
+ || (MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_DIV_INSN)))
+ has_mul_div = 1;
+ if (MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_DEBUG_INSN))
+ has_debug = 1;
+ if (MEP_OMASK & (1 << CGEN_INSN_OPTIONAL_CP_INSN))
+ has_cop = 1;
+ }
+
+ r = ((b & 0x00f0) >> 4) | ((b & 0x0001) << 4);
+ switch (r)
+ {
+ case 7: /* $hi */
+ case 8: /* $lo */
+ if (!has_mul_div)
+ as_bad (_("$hi and $lo are disabled when MUL and DIV are off"));
+ break;
+ case 12: /* $mb0 */
+ case 13: /* $me0 */
+ case 14: /* $mb1 */
+ case 15: /* $me1 */
+ if (!has_cop)
+ as_bad (_("$mb0, $me0, $mb1, and $me1 are disabled when COP is off"));
+ break;
+ case 24: /* $dbg */
+ case 25: /* $depc */
+ if (!has_debug)
+ as_bad (_("$dbg and $depc are disabled when DEBUG is off"));
+ break;
+ }
+ }
+}
+
+static int
+mep_machine (void)
+{
+ switch (MEP_CPU & EF_MEP_CPU_MASK)
+ {
+ default: break;
+ case EF_MEP_CPU_C2: return bfd_mach_mep;
+ case EF_MEP_CPU_C3: return bfd_mach_mep;
+ case EF_MEP_CPU_C4: return bfd_mach_mep;
+ case EF_MEP_CPU_C5: return bfd_mach_mep_c5;
+ case EF_MEP_CPU_H1: return bfd_mach_mep_h1;
+ }
+
+ return bfd_mach_mep;
+}
+
+/* The MeP version of the cgen parse_operand function. The only difference
+ from the standard version is that we want to avoid treating '$foo' and
+ '($foo...)' as references to a symbol called '$foo'. The chances are
+ that '$foo' is really a misspelt register. */
+
+static const char *
+mep_parse_operand (CGEN_CPU_DESC cd, enum cgen_parse_operand_type want,
+ const char **strP, int opindex, int opinfo,
+ enum cgen_parse_operand_result *resultP, bfd_vma *valueP)
+{
+ if (want == CGEN_PARSE_OPERAND_INTEGER || want == CGEN_PARSE_OPERAND_ADDRESS)
+ {
+ const char *next;
+
+ next = *strP;
+ while (*next == '(')
+ next++;
+ if (*next == '$')
+ return "Not a valid literal";
+ }
+ return gas_cgen_parse_operand (cd, want, strP, opindex, opinfo,
+ resultP, valueP);
+}
+
+void
+md_begin ()
+{
+ /* Initialize the `cgen' interface. */
+
+ /* If the user specifies no options, we default to allowing
+ everything. If the user specifies any enabling options, we
+ default to allowing only what is specified. If the user
+ specifies only disabling options, we only disable what is
+ specified. If the user specifies options and a config, the
+ options modify the config. */
+ if (optbits && mep_config_index == 0)
+ {
+ MEP_OMASK &= ~OPTION_MASK;
+ MEP_OMASK |= optbits;
+ }
+ else
+ MEP_OMASK = (MEP_OMASK & ~optbitset) | optbits;
+
+ mep_cop = mep_config_map[mep_config_index].cpu_flag & EF_MEP_COP_MASK;
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = mep_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ target_big_endian
+ ? CGEN_ENDIAN_BIG
+ : CGEN_ENDIAN_LITTLE,
+ CGEN_CPU_OPEN_ISAS, 0,
+ CGEN_CPU_OPEN_END);
+ mep_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, mep_parse_operand);
+
+ /* Identify the architecture. */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_mep, mep_machine ());
+
+ /* Store the configuration number and core. */
+ bfd_set_private_flags (stdoutput, MEP_CPU | MEP_CONFIG | library_flag);
+
+ /* Initialize the array we'll be using to store fixups. */
+ gas_cgen_initialize_saved_fixups_array();
+}
+
+/* Variant of mep_cgen_assemble_insn. Assemble insn STR of cpu CD as a
+ coprocessor instruction, if possible, into FIELDS, BUF, and INSN. */
+
+static const CGEN_INSN *
+mep_cgen_assemble_cop_insn (CGEN_CPU_DESC cd,
+ const char *str,
+ CGEN_FIELDS *fields,
+ CGEN_INSN_BYTES_PTR buf,
+ const struct cgen_insn *pinsn)
+{
+ const char *start;
+ CGEN_INSN_LIST *ilist;
+ const char *errmsg = NULL;
+
+ /* The instructions are stored in hashed lists. */
+ ilist = CGEN_ASM_LOOKUP_INSN (gas_cgen_cpu_desc,
+ CGEN_INSN_MNEMONIC (pinsn));
+
+ start = str;
+ for ( ; ilist != NULL ; ilist = CGEN_ASM_NEXT_INSN (ilist))
+ {
+ const CGEN_INSN *insn = ilist->insn;
+ if (strcmp (CGEN_INSN_MNEMONIC (ilist->insn),
+ CGEN_INSN_MNEMONIC (pinsn)) == 0
+ && MEP_INSN_COP_P (ilist->insn)
+ && mep_cgen_insn_supported (cd, insn))
+ {
+ str = start;
+
+ /* skip this insn if str doesn't look right lexically */
+ if (CGEN_INSN_RX (insn) != NULL &&
+ regexec ((regex_t *) CGEN_INSN_RX (insn), str, 0, NULL, 0) == REG_NOMATCH)
+ continue;
+
+ /* Allow parse/insert handlers to obtain length of insn. */
+ CGEN_FIELDS_BITSIZE (fields) = CGEN_INSN_BITSIZE (insn);
+
+ errmsg = CGEN_PARSE_FN (cd, insn) (cd, insn, & str, fields);
+ if (errmsg != NULL)
+ continue;
+
+ errmsg = CGEN_INSERT_FN (cd, insn) (cd, insn, fields, buf,
+ (bfd_vma) 0);
+ if (errmsg != NULL)
+ continue;
+
+ return insn;
+ }
+ }
+ return pinsn;
+}
+
+static void
+mep_save_insn (mep_insn insn)
+{
+ /* Consider change MAX_SAVED_FIXUP_CHAINS to MAX_PARALLEL_INSNS. */
+ if (num_insns_saved < 0 || num_insns_saved >= MAX_SAVED_FIXUP_CHAINS)
+ {
+ as_fatal("index into saved_insns[] out of bounds.");
+ return;
+ }
+ saved_insns[num_insns_saved] = insn;
+ gas_cgen_save_fixups(num_insns_saved);
+ num_insns_saved++;
+}
+
+static void
+mep_check_parallel32_scheduling (void)
+{
+ int insn0iscopro, insn1iscopro, insn0length, insn1length;
+
+ /* More than two instructions means that either someone is referring to
+ an internally parallel core or an internally parallel coprocessor,
+ neither of which are supported at this time. */
+ if ( num_insns_saved > 2 )
+ as_fatal("Internally paralled cores and coprocessors not supported.");
+
+ /* If there are no insns saved, that's ok. Just return. This will
+ happen when mep_process_saved_insns is called when the end of the
+ source file is reached and there are no insns left to be processed. */
+ if (num_insns_saved == 0)
+ return;
+
+ /* Check some of the attributes of the first insn. */
+ insn0iscopro = MEP_INSN_COP_P (saved_insns[0].insn);
+ insn0length = CGEN_FIELDS_BITSIZE (& saved_insns[0].fields);
+
+ if (num_insns_saved == 2)
+ {
+ /* Check some of the attributes of the first insn. */
+ insn1iscopro = MEP_INSN_COP_P (saved_insns[1].insn);
+ insn1length = CGEN_FIELDS_BITSIZE (& saved_insns[1].fields);
+
+ if ((insn0iscopro && !insn1iscopro)
+ || (insn1iscopro && !insn0iscopro))
+ {
+ /* We have one core and one copro insn. If their sizes
+ add up to 32, then the combination is valid. */
+ if (insn0length + insn1length == 32)
+ return;
+ else
+ as_bad (_("core and copro insn lengths must total 32 bits."));
+ }
+ else
+ as_bad (_("vliw group must consist of 1 core and 1 copro insn."));
+ }
+ else
+ {
+ /* If we arrive here, we have one saved instruction. There are a
+ number of possible cases:
+
+ 1. The instruction is a 32 bit core or coprocessor insn and
+ can be executed by itself. Valid.
+
+ 2. The instrucion is a core instruction for which a cop nop
+ exists. In this case, insert the cop nop into the saved
+ insn array after the core insn and return. Valid.
+
+ 3. The instruction is a coprocessor insn for which a core nop
+ exists. In this case, move the coprocessor insn to the
+ second element of the array and put the nop in the first
+ element then return. Valid.
+
+ 4. The instruction is a core or coprocessor instruction for
+ which there is no matching coprocessor or core nop to use
+ to form a valid vliw insn combination. In this case, we
+ we have to abort. */
+
+ if (insn0length > 32)
+ as_fatal ("Cannot use 48- or 64-bit insns with a 32 bit datapath.");
+
+ if (insn0length == 32)
+ return;
+
+ /* Insn is smaller than datapath. If there are no matching
+ nops for this insn, then terminate assembly. */
+ if (CGEN_INSN_ATTR_VALUE (saved_insns[0].insn,
+ CGEN_INSN_VLIW32_NO_MATCHING_NOP))
+ as_fatal ("No valid nop.");
+
+ /* At this point we know that we have a single 16-bit insn that has
+ a matching nop. We have to assemble it and put it into the saved
+ insn and fixup chain arrays. */
+
+ if (insn0iscopro)
+ {
+ char *errmsg;
+ mep_insn insn;
+
+ /* Move the insn and it's fixups to the second element of the
+ saved insns arrary and insert a 16 bit core nope into the
+ first element. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "nop",
+ &insn.fields, insn.buffer,
+ &errmsg);
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Move the insn in element 0 to element 1 and insert the
+ nop into element 0. Move the fixups in element 0 to
+ element 1 and save the current fixups to element 0.
+ Really there aren't any fixups at this point because we're
+ inserting a nop but we might as well be general so that
+ if there's ever a need to insert a general insn, we'll
+ have an example. */
+ saved_insns[1] = saved_insns[0];
+ saved_insns[0] = insn;
+ num_insns_saved++;
+ gas_cgen_swap_fixups (0);
+ gas_cgen_save_fixups (1);
+ }
+ else
+ {
+ char * errmsg;
+ mep_insn insn;
+ int insn_num = saved_insns[0].insn->base->num;
+
+ /* Use 32 bit branches and skip the nop. */
+ if (insn_num == MEP_INSN_BSR12
+ || insn_num == MEP_INSN_BEQZ
+ || insn_num == MEP_INSN_BNEZ)
+ return;
+
+ /* Insert a 16-bit coprocessor nop. Note that at the time */
+ /* this was done, no 16-bit coprocessor nop was defined. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop16",
+ &insn.fields, insn.buffer,
+ &errmsg);
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Now put the insn and fixups into the arrays. */
+ mep_save_insn (insn);
+ }
+ }
+}
+
+static void
+mep_check_parallel64_scheduling (void)
+{
+ int insn0iscopro, insn1iscopro, insn0length, insn1length;
+
+ /* More than two instructions means that someone is referring to an
+ internally parallel core or an internally parallel coprocessor. */
+ /* These are not currently supported. */
+ if (num_insns_saved > 2)
+ as_fatal ("Internally parallel cores of coprocessors not supported.");
+
+ /* If there are no insns saved, that's ok. Just return. This will
+ happen when mep_process_saved_insns is called when the end of the
+ source file is reached and there are no insns left to be processed. */
+ if (num_insns_saved == 0)
+ return;
+
+ /* Check some of the attributes of the first insn. */
+ insn0iscopro = MEP_INSN_COP_P (saved_insns[0].insn);
+ insn0length = CGEN_FIELDS_BITSIZE (& saved_insns[0].fields);
+
+ if (num_insns_saved == 2)
+ {
+ /* Check some of the attributes of the first insn. */
+ insn1iscopro = MEP_INSN_COP_P (saved_insns[1].insn);
+ insn1length = CGEN_FIELDS_BITSIZE (& saved_insns[1].fields);
+
+ if ((insn0iscopro && !insn1iscopro)
+ || (insn1iscopro && !insn0iscopro))
+ {
+ /* We have one core and one copro insn. If their sizes
+ add up to 64, then the combination is valid. */
+ if (insn0length + insn1length == 64)
+ return;
+ else
+ as_bad (_("core and copro insn lengths must total 64 bits."));
+ }
+ else
+ as_bad (_("vliw group must consist of 1 core and 1 copro insn."));
+ }
+ else
+ {
+ /* If we arrive here, we have one saved instruction. There are a
+ number of possible cases:
+
+ 1. The instruction is a 64 bit coprocessor insn and can be
+ executed by itself. Valid.
+
+ 2. The instrucion is a core instruction for which a cop nop
+ exists. In this case, insert the cop nop into the saved
+ insn array after the core insn and return. Valid.
+
+ 3. The instruction is a coprocessor insn for which a core nop
+ exists. In this case, move the coprocessor insn to the
+ second element of the array and put the nop in the first
+ element then return. Valid.
+
+ 4. The instruction is a core or coprocessor instruction for
+ which there is no matching coprocessor or core nop to use
+ to form a valid vliw insn combination. In this case, we
+ we have to abort. */
+
+ /* If the insn is 64 bits long, it can run alone. The size check
+ is done indepependantly of whether the insn is core or copro
+ in case 64 bit coprocessor insns are added later. */
+ if (insn0length == 64)
+ return;
+
+ /* Insn is smaller than datapath. If there are no matching
+ nops for this insn, then terminate assembly. */
+ if (CGEN_INSN_ATTR_VALUE (saved_insns[0].insn,
+ CGEN_INSN_VLIW64_NO_MATCHING_NOP))
+ as_fatal ("No valid nop.");
+
+ if (insn0iscopro)
+ {
+ char *errmsg;
+ mep_insn insn;
+
+ /* Initialize the insn buffer. */
+ memset (insn.buffer, 0, sizeof(insn.buffer));
+
+ /* We have a coprocessor insn. At this point in time there
+ are is 32-bit core nop. There is only a 16-bit core
+ nop. The idea is to allow for a relatively arbitrary
+ coprocessor to be specified. We aren't looking at
+ trying to cover future changes in the core at this time
+ since it is assumed that the core will remain fairly
+ static. If there ever are 32 or 48 bit core nops added,
+ they will require entries below. */
+
+ if (insn0length == 48)
+ {
+ /* Move the insn and fixups to the second element of the
+ arrays then assemble and insert a 16 bit core nop. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "nop",
+ & insn.fields, insn.buffer,
+ & errmsg);
+ }
+ else
+ {
+ /* If this is reached, then we have a single coprocessor
+ insn that is not 48 bits long, but for which the assembler
+ thinks there is a matching core nop. If a 32-bit core
+ nop has been added, then make the necessary changes and
+ handle its assembly and insertion here. Otherwise,
+ go figure out why either:
+
+ 1. The assembler thinks that there is a 32-bit core nop
+ to match a 32-bit coprocessor insn, or
+ 2. The assembler thinks that there is a 48-bit core nop
+ to match a 16-bit coprocessor insn. */
+
+ as_fatal ("Assembler expects a non-existent core nop.");
+ }
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Move the insn in element 0 to element 1 and insert the
+ nop into element 0. Move the fixups in element 0 to
+ element 1 and save the current fixups to element 0.
+ Really there aren't any fixups at this point because we're
+ inserting a nop but we might as well be general so that
+ if there's ever a need to insert a general insn, we'll
+ have an example. */
+
+ saved_insns[1] = saved_insns[0];
+ saved_insns[0] = insn;
+ num_insns_saved++;
+ gas_cgen_swap_fixups(0);
+ gas_cgen_save_fixups(1);
+
+ }
+ else
+ {
+ char * errmsg;
+ mep_insn insn;
+
+ /* Initialize the insn buffer */
+ memset (insn.buffer, 0, sizeof(insn.buffer));
+
+ /* We have a core insn. We have to handle all possible nop
+ lengths. If a coprocessor doesn't have a nop of a certain
+ length but there exists core insns that when combined with
+ a nop of that length would fill the datapath, those core
+ insns will be flagged with the VLIW_NO_CORRESPONDING_NOP
+ attribute. That will ensure that when used in a way that
+ requires a nop to be inserted, assembly will terminate
+ before reaching this section of code. This guarantees
+ that cases below which would result in the attempted
+ insertion of nop that doesn't exist will never be entered. */
+ if (insn0length == 16)
+ {
+ /* Insert 48 bit coprocessor nop. */
+ /* Assemble it and put it into the arrays. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop48",
+ &insn.fields, insn.buffer,
+ &errmsg);
+ }
+ else if (insn0length == 32)
+ {
+ /* Insert 32 bit coprocessor nop. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop32",
+ &insn.fields, insn.buffer,
+ &errmsg);
+ }
+ else if (insn0length == 48)
+ {
+ /* Insert 16 bit coprocessor nop. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, "cpnop16",
+ &insn.fields, insn.buffer,
+ &errmsg);
+ }
+ else
+ /* Core insn has an invalid length. Something has gone wrong. */
+ as_fatal ("Core insn has invalid length! Something is wrong!");
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+
+ /* Now put the insn and fixups into the arrays. */
+ mep_save_insn (insn);
+ }
+ }
+}
+
+#ifdef MEP_IVC2_SUPPORTED
+
+/* IVC2 packing is different than other VLIW coprocessors. Many of
+ the COP insns can be placed in any of three different types of
+ slots, and each bundle can hold up to three insns - zero or one
+ core insns and one or two IVC2 insns. The insns in CGEN are tagged
+ with which slots they're allowed in, and we have to decide based on
+ that whether or not the user had given us a possible bundling. */
+
+static int
+slot_ok (int idx, int slot)
+{
+ const CGEN_INSN *insn = saved_insns[idx].insn;
+ return CGEN_ATTR_CGEN_INSN_SLOTS_VALUE (CGEN_INSN_ATTRS (insn)) & (1 << slot);
+}
+
+static void
+mep_check_ivc2_scheduling (void)
+{
+ /* VLIW modes:
+
+ V1 [-----core-----][--------p0s-------][------------p1------------]
+ V2 [-------------core-------------]xxxx[------------p1------------]
+ V3 1111[--p0--]0111[--------p0--------][------------p1------------]
+ */
+
+ int slots[5]; /* Indexed off the SLOTS_ATTR enum. */
+ int corelength, realcorelength;
+ int i;
+ bfd_byte temp[4];
+ bfd_byte *f;
+ int e = target_big_endian ? 0 : 1;
+
+ /* If there are no insns saved, that's ok. Just return. This will
+ happen when mep_process_saved_insns is called when the end of the
+ source file is reached and there are no insns left to be processed. */
+ if (num_insns_saved == 0)
+ return;
+
+ for (i=0; i<5; i++)
+ slots[i] = -1;
+
+ if (slot_ok (0, SLOTS_CORE))
+ {
+ slots[SLOTS_CORE] = 0;
+ realcorelength = corelength = CGEN_FIELDS_BITSIZE (& saved_insns[0].fields);
+
+ /* If we encounter one of these, it may get relaxed later into a
+ longer instruction. We can't just push the other opcodes
+ away, the bigger insn has to fit into the existing slot. So,
+ we make room for the relaxed instruction here. */
+
+ if (saved_insns[0].insn->base->num == MEP_INSN_BSR12
+ || saved_insns[0].insn->base->num == MEP_INSN_BRA)
+ corelength = 32;
+ }
+ else
+ realcorelength = corelength = 0;
+
+ if (corelength == 16)
+ {
+ /* V1 mode: we need a P0S slot and a P1 slot. */
+ switch (num_insns_saved)
+ {
+ case 1:
+ /* No other insns, fill with NOPs. */
+ break;
+
+ case 2:
+ if (slot_ok (1, SLOTS_P1))
+ slots[SLOTS_P1] = 1;
+ else if (slot_ok (1, SLOTS_P0S))
+ slots[SLOTS_P0S] = 1;
+ else
+ as_bad (_("cannot pack %s with a 16-bit insn"),
+ CGEN_INSN_NAME (saved_insns[1].insn));
+ break;
+
+ case 3:
+ if (slot_ok (1, SLOTS_P0S)
+ && slot_ok (2, SLOTS_P1))
+ {
+ slots[SLOTS_P0S] = 1;
+ slots[SLOTS_P1] = 2;
+ }
+ else if (slot_ok (1, SLOTS_P1)
+ && slot_ok (2, SLOTS_P0S))
+ {
+ slots[SLOTS_P1] = 1;
+ slots[SLOTS_P0S] = 2;
+ }
+ else
+ as_bad (_("cannot pack %s and %s together with a 16-bit insn"),
+ CGEN_INSN_NAME (saved_insns[1].insn),
+ CGEN_INSN_NAME (saved_insns[2].insn));
+ break;
+
+ default:
+ as_bad (_("too many IVC2 insns to pack with a 16-bit core insn"));
+ break;
+ }
+ }
+ else if (corelength == 32)
+ {
+ /* V2 mode: we need a P1 slot. */
+ switch (num_insns_saved)
+ {
+ case 1:
+ /* No other insns, fill with NOPs. */
+ break;
+ case 2:
+ /* The other insn must allow P1. */
+ if (!slot_ok (1, SLOTS_P1))
+ as_bad (_("cannot pack %s into slot P1"),
+ CGEN_INSN_NAME (saved_insns[1].insn));
+ else
+ slots[SLOTS_P1] = 1;
+ break;
+ default:
+ as_bad (_("too many IVC2 insns to pack with a 32-bit core insn"));
+ break;
+ }
+ }
+ else if (corelength == 0)
+ {
+ /* V3 mode: we need a P0 slot and a P1 slot, or a P0S+P1 with a
+ core NOP. */
+ switch (num_insns_saved)
+ {
+ case 1:
+ if (slot_ok (0, SLOTS_P0))
+ slots[SLOTS_P0] = 0;
+ else if (slot_ok (0, SLOTS_P1))
+ slots[SLOTS_P1] = 0;
+ else if (slot_ok (0, SLOTS_P0S))
+ slots[SLOTS_P0S] = 0;
+ else
+ as_bad (_("unable to pack %s by itself?"),
+ CGEN_INSN_NAME (saved_insns[0].insn));
+ break;
+
+ case 2:
+ if (slot_ok (0, SLOTS_P0)
+ && slot_ok (1, SLOTS_P1))
+ {
+ slots[SLOTS_P0] = 0;
+ slots[SLOTS_P1] = 1;
+ }
+ else if (slot_ok (0, SLOTS_P1)
+ && slot_ok (1, SLOTS_P0))
+ {
+ slots[SLOTS_P1] = 0;
+ slots[SLOTS_P0] = 1;
+ }
+ else if (slot_ok (0, SLOTS_P0S)
+ && slot_ok (1, SLOTS_P1))
+ {
+ slots[SLOTS_P0S] = 0;
+ slots[SLOTS_P1] = 1;
+ }
+ else if (slot_ok (0, SLOTS_P1)
+ && slot_ok (1, SLOTS_P0S))
+ {
+ slots[SLOTS_P1] = 0;
+ slots[SLOTS_P0S] = 1;
+ }
+ else
+ as_bad (_("cannot pack %s and %s together"),
+ CGEN_INSN_NAME (saved_insns[0].insn),
+ CGEN_INSN_NAME (saved_insns[1].insn));
+ break;
+
+ default:
+ as_bad (_("too many IVC2 insns to pack together"));
+ break;
+ }
+ }
+
+ /* The core insn needs to be done normally so that fixups,
+ relaxation, etc are done. Other IVC2 insns need only be resolved
+ to bit patterns; there are no relocations for them. */
+ if (slots[SLOTS_CORE] != -1)
+ {
+ gas_cgen_restore_fixups (0);
+ gas_cgen_finish_insn (saved_insns[0].insn, saved_insns[0].buffer,
+ CGEN_FIELDS_BITSIZE (& saved_insns[0].fields),
+ 1, NULL);
+ }
+
+ /* Allocate whatever bytes remain in our insn word. Adjust the
+ pointer to point (as if it were) to the beginning of the whole
+ word, so that we don't have to adjust for it elsewhere. */
+ f = (bfd_byte *) frag_more (8 - realcorelength / 8);
+ /* Unused slots are filled with NOPs, which happen to be all zeros. */
+ memset (f, 0, 8 - realcorelength / 8);
+ f -= realcorelength / 8;
+
+ for (i=1; i<5; i++)
+ {
+ mep_insn *m;
+
+ if (slots[i] == -1)
+ continue;
+
+ m = & saved_insns[slots[i]];
+
+#if CGEN_INT_INSN_P
+ cgen_put_insn_value (gas_cgen_cpu_desc, (unsigned char *) temp, 32,
+ m->buffer[0]);
+#else
+ memcpy (temp, m->buffer, byte_len);
+#endif
+
+ switch (i)
+ {
+ case SLOTS_P0S:
+ f[2^e] = temp[1^e];
+ f[3^e] = temp[2^e];
+ f[4^e] |= temp[3^e] & 0xf0;
+ break;
+ case SLOTS_P0:
+ f[0^e] = 0xf0 | temp[0^e] >> 4;
+ f[1^e] = temp[0^e] << 4 | 0x07;
+ f[2^e] = temp[1^e];
+ f[3^e] = temp[2^e];
+ f[4^e] |= temp[3^e] & 0xf0;
+ break;
+ case SLOTS_P1:
+ f[4^e] |= temp[0^e] >> 4;
+ f[5^e] = temp[0^e] << 4 | temp[1^e] >> 4;
+ f[6^e] = temp[1^e] << 4 | temp[2^e] >> 4;
+ f[7^e] = temp[2^e] << 4 | temp[3^e] >> 4;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+#endif /* MEP_IVC2_SUPPORTED */
+
+/* The scheduling functions are just filters for invalid combinations.
+ If there is a violation, they terminate assembly. Otherise they
+ just fall through. Succesful combinations cause no side effects
+ other than valid nop insertion. */
+
+static void
+mep_check_parallel_scheduling (void)
+{
+ /* This is where we will eventually read the config information
+ and choose which scheduling checking function to call. */
+#ifdef MEP_IVC2_SUPPORTED
+ if (mep_cop == EF_MEP_COP_IVC2)
+ mep_check_ivc2_scheduling ();
+ else
+#endif /* MEP_IVC2_SUPPORTED */
+ if (MEP_VLIW64)
+ mep_check_parallel64_scheduling ();
+ else
+ mep_check_parallel32_scheduling ();
+}
+
+static void
+mep_process_saved_insns (void)
+{
+ int i;
+
+ gas_cgen_save_fixups (MAX_SAVED_FIXUP_CHAINS - 1);
+
+ /* We have to check for valid scheduling here. */
+ mep_check_parallel_scheduling ();
+
+ /* IVC2 has to pack instructions in a funny way, so it does it
+ itself. */
+ if (mep_cop != EF_MEP_COP_IVC2)
+ {
+ /* If the last call didn't cause assembly to terminate, we have
+ a valid vliw insn/insn pair saved. Restore this instructions'
+ fixups and process the insns. */
+ for (i = 0;i<num_insns_saved;i++)
+ {
+ gas_cgen_restore_fixups (i);
+ gas_cgen_finish_insn (saved_insns[i].insn, saved_insns[i].buffer,
+ CGEN_FIELDS_BITSIZE (& saved_insns[i].fields),
+ 1, NULL);
+ }
+ }
+ gas_cgen_restore_fixups (MAX_SAVED_FIXUP_CHAINS - 1);
+
+ /* Clear the fixups and reset the number insn saved to 0. */
+ gas_cgen_initialize_saved_fixups_array ();
+ num_insns_saved = 0;
+ listing_prev_line ();
+}
+
+void
+md_assemble (char * str)
+{
+ static CGEN_BITSET* isas = NULL;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ /* There are two possible modes: core and vliw. We have to assemble
+ differently for each.
+
+ Core Mode: We assemble normally. All instructions are on a
+ single line and are made up of one mnemonic and one
+ set of operands.
+ VLIW Mode: Vliw combinations are indicated as follows:
+
+ core insn
+ + copro insn
+
+ We want to handle the general case where more than
+ one instruction can be preceeded by a +. This will
+ happen later if we add support for internally parallel
+ coprocessors. We'll make the parsing nice and general
+ so that it can handle an arbitrary number of insns
+ with leading +'s. The actual checking for valid
+ combinations is done elsewhere. */
+
+ /* Initialize the isa to refer to the core. */
+ if (isas == NULL)
+ isas = cgen_bitset_copy (& MEP_CORE_ISA);
+ else
+ {
+ cgen_bitset_clear (isas);
+ cgen_bitset_union (isas, & MEP_CORE_ISA, isas);
+ }
+ gas_cgen_cpu_desc->isas = isas;
+
+ if (mode == VLIW)
+ {
+ /* VLIW mode. */
+
+ int thisInsnIsCopro = 0;
+ mep_insn insn;
+ int i;
+
+ /* Initialize the insn buffer */
+
+ if (! CGEN_INT_INSN_P)
+ for (i=0; i < CGEN_MAX_INSN_SIZE; i++)
+ insn.buffer[i]='\0';
+
+
+ /* IVC2 has two sets of coprocessor opcodes, one for CORE mode
+ and one for VLIW mode. They have the same names. To specify
+ which one we want, we use the COP isas - the 32 bit ISA is
+ for the core instructions (which are always 32 bits), and the
+ other ISAs are for the VLIW ones (which always pack into 64
+ bit insns). We use other attributes to determine slotting
+ later. */
+ if (mep_cop == EF_MEP_COP_IVC2)
+ {
+ cgen_bitset_union (isas, & MEP_COP16_ISA, isas);
+ cgen_bitset_union (isas, & MEP_COP48_ISA, isas);
+ cgen_bitset_union (isas, & MEP_COP64_ISA, isas);
+ }
+ else
+ {
+ /* Can't tell core / copro insns apart at parse time! */
+ cgen_bitset_union (isas, & MEP_COP_ISA, isas);
+ }
+
+ /* Assemble the insn so we can examine its attributes. */
+ insn.insn = mep_cgen_assemble_insn (gas_cgen_cpu_desc, str,
+ &insn.fields, insn.buffer,
+ &errmsg);
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+ mep_check_for_disabled_registers (&insn);
+
+ /* Check to see if it's a coprocessor instruction. */
+ thisInsnIsCopro = MEP_INSN_COP_P (insn.insn);
+
+ if (!thisInsnIsCopro)
+ {
+ insn.insn = mep_cgen_assemble_cop_insn (gas_cgen_cpu_desc, str,
+ &insn.fields, insn.buffer,
+ insn.insn);
+ thisInsnIsCopro = MEP_INSN_COP_P (insn.insn);
+ mep_check_for_disabled_registers (&insn);
+ }
+
+ if (pluspresent)
+ {
+ /* A plus was present. */
+ /* Check for a + with a core insn and abort if found. */
+ if (!thisInsnIsCopro)
+ {
+ as_fatal("A core insn cannot be preceeded by a +.\n");
+ return;
+ }
+
+ if (num_insns_saved > 0)
+ {
+ /* There are insns in the queue. Add this one. */
+ mep_save_insn (insn);
+ }
+ else
+ {
+ /* There are no insns in the queue and a plus is present.
+ This is a syntax error. Let's not tolerate this.
+ We can relax this later if necessary. */
+ as_bad (_("Invalid use of parallelization operator."));
+ return;
+ }
+ }
+ else
+ {
+ /* No plus was present. */
+ if (num_insns_saved > 0)
+ {
+ /* There are insns saved and we came across an insn without a
+ leading +. That's the signal to process the saved insns
+ before proceeding then treat the current insn as the first
+ in a new vliw group. */
+ mep_process_saved_insns ();
+ num_insns_saved = 0;
+ /* mep_save_insn (insn); */
+ }
+ mep_save_insn (insn);
+#if 0
+ else
+ {
+
+ /* Core Insn. Add it to the beginning of the queue. */
+ mep_save_insn (insn);
+ /* gas_cgen_save_fixups(num_insns_saved); */
+ }
+#endif
+ }
+
+ pluspresent = 0;
+ }
+ else
+ {
+ /* Core mode. */
+
+ /* Only single instructions are assembled in core mode. */
+ mep_insn insn;
+
+ /* See comment in the VLIW clause above about this. */
+ if (mep_cop & EF_MEP_COP_IVC2)
+ cgen_bitset_union (isas, & MEP_COP32_ISA, isas);
+
+ /* If a leading '+' was present, issue an error.
+ That's not allowed in core mode. */
+ if (pluspresent)
+ {
+ as_bad (_("Leading plus sign not allowed in core mode"));
+ return;
+ }
+
+ insn.insn = mep_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", errmsg);
+ return;
+ }
+ gas_cgen_finish_insn (insn.insn, insn.buffer,
+ CGEN_FIELDS_BITSIZE (& insn.fields), 1, NULL);
+ mep_check_for_disabled_registers (&insn);
+ }
+}
+
+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;
+}
+
+/* Interface to relax_segment. */
+
+
+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. */
+ /* Note that we use "beq" because "jmp" has a peculiarity - it cannot
+ jump to addresses with any bits 27..24 set. So, we use beq as a
+ 17-bit pc-relative branch to avoid using jmp, just in case. */
+
+ /* 0 */ { 0, 0, 0, 0 }, /* unused */
+ /* 1 */ { 0, 0, 0, 0 }, /* marker for "don't know yet" */
+
+ /* 2 */ { 2047, -2048, 0, 3 }, /* bsr12 */
+ /* 3 */ { 0, 0, 2, 0 }, /* bsr16 */
+
+ /* 4 */ { 2047, -2048, 0, 5 }, /* bra */
+ /* 5 */ { 65535, -65536, 2, 6 }, /* beq $0,$0 */
+ /* 6 */ { 0, 0, 2, 0 }, /* jmp24 */
+
+ /* 7 */ { 65535, -65536, 0, 8 }, /* beqi */
+ /* 8 */ { 0, 0, 4, 0 }, /* bnei/jmp */
+
+ /* 9 */ { 127, -128, 0, 10 }, /* beqz */
+ /* 10 */ { 65535, -65536, 2, 11 }, /* beqi */
+ /* 11 */ { 0, 0, 4, 0 }, /* bnei/jmp */
+
+ /* 12 */ { 65535, -65536, 0, 13 }, /* bnei */
+ /* 13 */ { 0, 0, 4, 0 }, /* beqi/jmp */
+
+ /* 14 */ { 127, -128, 0, 15 }, /* bnez */
+ /* 15 */ { 65535, -65536, 2, 16 }, /* bnei */
+ /* 16 */ { 0, 0, 4, 0 }, /* beqi/jmp */
+
+ /* 17 */ { 65535, -65536, 0, 13 }, /* bgei */
+ /* 18 */ { 0, 0, 4, 0 },
+ /* 19 */ { 65535, -65536, 0, 13 }, /* blti */
+ /* 20 */ { 0, 0, 4, 0 },
+ /* 19 */ { 65535, -65536, 0, 13 }, /* bcpeq */
+ /* 20 */ { 0, 0, 4, 0 },
+ /* 19 */ { 65535, -65536, 0, 13 }, /* bcpne */
+ /* 20 */ { 0, 0, 4, 0 },
+ /* 19 */ { 65535, -65536, 0, 13 }, /* bcpat */
+ /* 20 */ { 0, 0, 4, 0 },
+ /* 19 */ { 65535, -65536, 0, 13 }, /* bcpaf */
+ /* 20 */ { 0, 0, 4, 0 }
+};
+
+/* Pseudo-values for 64 bit "insns" which are combinations of two 32
+ bit insns. */
+typedef enum {
+ MEP_PSEUDO64_NONE,
+ MEP_PSEUDO64_16BITCC,
+ MEP_PSEUDO64_32BITCC,
+} MepPseudo64Values;
+
+static struct {
+ int insn;
+ int growth;
+ int insn_for_extern;
+} subtype_mappings[] = {
+ { 0, 0, 0 },
+ { 0, 0, 0 },
+ { MEP_INSN_BSR12, 0, MEP_INSN_BSR24 },
+ { MEP_INSN_BSR24, 2, MEP_INSN_BSR24 },
+ { MEP_INSN_BRA, 0, MEP_INSN_BRA },
+ { MEP_INSN_BEQ, 2, MEP_INSN_BEQ },
+ { MEP_INSN_JMP, 2, MEP_INSN_JMP },
+ { MEP_INSN_BEQI, 0, MEP_INSN_BEQI },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BEQZ, 0, MEP_INSN_BEQZ },
+ { MEP_INSN_BEQI, 2, MEP_INSN_BEQI },
+ { -1, 4, MEP_PSEUDO64_16BITCC },
+ { MEP_INSN_BNEI, 0, MEP_INSN_BNEI },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BNEZ, 0, MEP_INSN_BNEZ },
+ { MEP_INSN_BNEI, 2, MEP_INSN_BNEI },
+ { -1, 4, MEP_PSEUDO64_16BITCC },
+ { MEP_INSN_BGEI, 0, MEP_INSN_BGEI },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BLTI, 0, MEP_INSN_BLTI },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BCPEQ, 0, MEP_INSN_BCPEQ },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BCPNE, 0, MEP_INSN_BCPNE },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BCPAT, 0, MEP_INSN_BCPAT },
+ { -1, 4, MEP_PSEUDO64_32BITCC },
+ { MEP_INSN_BCPAF, 0, MEP_INSN_BCPAF },
+ { -1, 4, MEP_PSEUDO64_32BITCC }
+};
+#define NUM_MAPPINGS (sizeof (subtype_mappings) / sizeof (subtype_mappings[0]))
+
+void
+mep_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 MeP pcrel not being relative to the next opcode. */
+ *aim += 2 + md_relax_table[this_state].rlx_length;
+}
+
+static int
+insn_to_subtype (int insn)
+{
+ unsigned int i;
+ for (i=0; i<NUM_MAPPINGS; i++)
+ if (insn == subtype_mappings[i].insn)
+ 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
+ and 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)
+{
+ 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
+ || S_IS_WEAK (fragP->fr_symbol)
+#ifdef MEP_IVC2_SUPPORTED
+ || (mep_cop == EF_MEP_COP_IVC2
+ && bfd_get_section_flags (stdoutput, segment) & SEC_MEP_VLIW)
+#endif /* MEP_IVC2_SUPPORTED */
+ )
+ {
+ int new_insn;
+
+ new_insn = subtype_mappings[fragP->fr_subtype].insn_for_extern;
+ fragP->fr_subtype = insn_to_subtype (new_insn);
+ }
+
+ if (MEP_VLIW && ! MEP_VLIW64
+ && (bfd_get_section_flags (stdoutput, segment) & SEC_MEP_VLIW))
+ {
+ /* Use 32 bit branches for vliw32 so the vliw word is not split. */
+ switch (fragP->fr_cgen.insn->base->num)
+ {
+ case MEP_INSN_BSR12:
+ fragP->fr_subtype = insn_to_subtype
+ (subtype_mappings[fragP->fr_subtype].insn_for_extern);
+ break;
+ case MEP_INSN_BEQZ:
+ fragP->fr_subtype ++;
+ break;
+ case MEP_INSN_BNEZ:
+ fragP->fr_subtype ++;
+ break;
+ }
+ }
+
+ if (fragP->fr_cgen.insn->base
+ && fragP->fr_cgen.insn->base->num
+ != subtype_mappings[fragP->fr_subtype].insn)
+ {
+ int new_insn= subtype_mappings[fragP->fr_subtype].insn;
+ if (new_insn != -1)
+ {
+ fragP->fr_cgen.insn = (fragP->fr_cgen.insn
+ - fragP->fr_cgen.insn->base->num
+ + new_insn);
+ }
+ }
+
+#ifdef MEP_IVC2_SUPPORTED
+ if (mep_cop == EF_MEP_COP_IVC2
+ && bfd_get_section_flags (stdoutput, segment) & SEC_MEP_VLIW)
+ return 0;
+#endif /* MEP_IVC2_SUPPORTED */
+
+ return subtype_mappings[fragP->fr_subtype].growth;
+}
+
+/* VLIW does relaxing, but not growth. */
+
+long
+mep_relax_frag (segT segment, fragS *fragP, long stretch)
+{
+ long rv = relax_frag (segment, fragP, stretch);
+#ifdef MEP_IVC2_SUPPORTED
+ if (mep_cop == EF_MEP_COP_IVC2
+ && bfd_get_section_flags (stdoutput, segment) & SEC_MEP_VLIW)
+ return 0;
+#endif
+ return rv;
+}
+
+/* *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);
+
+ return rv;
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ int addend, rn, bit = 0;
+ int operand;
+ int where = fragP->fr_opcode - fragP->fr_literal;
+ int e = target_big_endian ? 0 : 1;
+ int core_mode;
+
+#ifdef MEP_IVC2_SUPPORTED
+ if (bfd_get_section_flags (stdoutput, seg) & SEC_MEP_VLIW
+ && mep_cop == EF_MEP_COP_IVC2)
+ core_mode = 0;
+ else
+#endif /* MEP_IVC2_SUPPORTED */
+ core_mode = 1;
+
+ addend = target_address_for (fragP) - (fragP->fr_address + where);
+
+ if (subtype_mappings[fragP->fr_subtype].insn == -1)
+ {
+ if (core_mode)
+ fragP->fr_fix += subtype_mappings[fragP->fr_subtype].growth;
+ switch (subtype_mappings[fragP->fr_subtype].insn_for_extern)
+ {
+ case MEP_PSEUDO64_16BITCC:
+ fragP->fr_opcode[1^e] = ((fragP->fr_opcode[1^e] & 1) ^ 1) | 0x06;
+ fragP->fr_opcode[2^e] = 0xd8;
+ fragP->fr_opcode[3^e] = 0x08;
+ fragP->fr_opcode[4^e] = 0;
+ fragP->fr_opcode[5^e] = 0;
+ where += 2;
+ break;
+ case MEP_PSEUDO64_32BITCC:
+ if (fragP->fr_opcode[0^e] & 0x10)
+ fragP->fr_opcode[1^e] ^= 0x01;
+ else
+ fragP->fr_opcode[1^e] ^= 0x04;
+ fragP->fr_opcode[2^e] = 0;
+ fragP->fr_opcode[3^e] = 4;
+ fragP->fr_opcode[4^e] = 0xd8;
+ fragP->fr_opcode[5^e] = 0x08;
+ fragP->fr_opcode[6^e] = 0;
+ fragP->fr_opcode[7^e] = 0;
+ where += 4;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_cgen.insn = (fragP->fr_cgen.insn
+ - fragP->fr_cgen.insn->base->num
+ + MEP_INSN_JMP);
+ operand = MEP_OPERAND_PCABS24A2;
+ }
+ else
+ switch (fragP->fr_cgen.insn->base->num)
+ {
+ case MEP_INSN_BSR12:
+ fragP->fr_opcode[0^e] = 0xb0 | ((addend >> 8) & 0x0f);
+ fragP->fr_opcode[1^e] = 0x01 | (addend & 0xfe);
+ operand = MEP_OPERAND_PCREL12A2;
+ break;
+
+ case MEP_INSN_BSR24:
+ if (core_mode)
+ fragP->fr_fix += 2;
+ fragP->fr_opcode[0^e] = 0xd8 | ((addend >> 5) & 0x07);
+ fragP->fr_opcode[1^e] = 0x09 | ((addend << 3) & 0xf0);
+ fragP->fr_opcode[2^e] = 0x00 | ((addend >>16) & 0xff);
+ fragP->fr_opcode[3^e] = 0x00 | ((addend >> 8) & 0xff);
+ operand = MEP_OPERAND_PCREL24A2;
+ break;
+
+ case MEP_INSN_BRA:
+ fragP->fr_opcode[0^e] = 0xb0 | ((addend >> 8) & 0x0f);
+ fragP->fr_opcode[1^e] = 0x00 | (addend & 0xfe);
+ operand = MEP_OPERAND_PCREL12A2;
+ break;
+
+ case MEP_INSN_BEQ:
+ /* The default relax_frag doesn't change the state if there is no
+ growth, so we must manually handle converting out-of-range BEQ
+ instructions to JMP. */
+ if (addend <= 65535 && addend >= -65536)
+ {
+ if (core_mode)
+ fragP->fr_fix += 2;
+ fragP->fr_opcode[0^e] = 0xe0;
+ fragP->fr_opcode[1^e] = 0x01;
+ fragP->fr_opcode[2^e] = 0x00 | ((addend >> 9) & 0xff);
+ fragP->fr_opcode[3^e] = 0x00 | ((addend >> 1) & 0xff);
+ operand = MEP_OPERAND_PCREL17A2;
+ break;
+ }
+ /* ...FALLTHROUGH... */
+
+ case MEP_INSN_JMP:
+ addend = target_address_for (fragP);
+ if (core_mode)
+ fragP->fr_fix += 2;
+ fragP->fr_opcode[0^e] = 0xd8 | ((addend >> 5) & 0x07);
+ fragP->fr_opcode[1^e] = 0x08 | ((addend << 3) & 0xf0);
+ fragP->fr_opcode[2^e] = 0x00 | ((addend >>16) & 0xff);
+ fragP->fr_opcode[3^e] = 0x00 | ((addend >> 8) & 0xff);
+ operand = MEP_OPERAND_PCABS24A2;
+ break;
+
+ case MEP_INSN_BNEZ:
+ bit = 1;
+ case MEP_INSN_BEQZ:
+ fragP->fr_opcode[1^e] = bit | (addend & 0xfe);
+ operand = MEP_OPERAND_PCREL8A2;
+ break;
+
+ case MEP_INSN_BNEI:
+ bit = 4;
+ case MEP_INSN_BEQI:
+ if (subtype_mappings[fragP->fr_subtype].growth)
+ {
+ if (core_mode)
+ fragP->fr_fix += subtype_mappings[fragP->fr_subtype].growth;
+ rn = fragP->fr_opcode[0^e] & 0x0f;
+ fragP->fr_opcode[0^e] = 0xe0 | rn;
+ fragP->fr_opcode[1^e] = bit;
+ }
+ fragP->fr_opcode[2^e] = 0x00 | ((addend >> 9) & 0xff);
+ fragP->fr_opcode[3^e] = 0x00 | ((addend >> 1) & 0xff);
+ operand = MEP_OPERAND_PCREL17A2;
+ break;
+
+ case MEP_INSN_BLTI:
+ case MEP_INSN_BGEI:
+ case MEP_INSN_BCPEQ:
+ case MEP_INSN_BCPNE:
+ case MEP_INSN_BCPAT:
+ case MEP_INSN_BCPAF:
+ /* No opcode change needed, just operand. */
+ fragP->fr_opcode[2^e] = (addend >> 9) & 0xff;
+ fragP->fr_opcode[3^e] = (addend >> 1) & 0xff;
+ operand = MEP_OPERAND_PCREL17A2;
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (S_GET_SEGMENT (fragP->fr_symbol) != seg
+ || S_IS_WEAK (fragP->fr_symbol)
+ || operand == MEP_OPERAND_PCABS24A2)
+ {
+ gas_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. */
+
+void
+mep_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ /* If we already know the fixup value, adjust it in the same
+ way that the linker would have done. */
+ if (fixP->fx_addsy == 0)
+ switch (fixP->fx_cgen.opinfo)
+ {
+ case BFD_RELOC_MEP_LOW16:
+ *valP = ((long)(*valP & 0xffff)) << 16 >> 16;
+ break;
+ case BFD_RELOC_MEP_HI16U:
+ *valP >>= 16;
+ break;
+ case BFD_RELOC_MEP_HI16S:
+ *valP = (*valP + 0x8000) >> 16;
+ break;
+ }
+
+ /* Now call cgen's md_aply_fix. */
+ gas_cgen_md_apply_fix (fixP, valP, seg);
+}
+
+long
+md_pcrel_from_section (fixS *fixP, segT sec)
+{
+ if (fixP->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || S_IS_WEAK (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;
+
+ /* If we've got other reasons for emitting this relocation, let the
+ linker handle pc-rel also. */
+ if (mep_force_relocation (fixP))
+ return 0;
+
+ /* Return the address of the opcode - cgen adjusts for opcode size
+ itself, to be consistent with the disassembler, which must do
+ so. */
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* 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. */
+
+#if defined (__STDC__) || defined (ALMOST_STDC) || defined (HAVE_STRINGIZE)
+#define MAP(n) case MEP_OPERAND_##n: return BFD_RELOC_MEP_##n;
+#else
+#define MAP(n) case MEP_OPERAND_/**/n: return BFD_RELOC_MEP_/**/n;
+#endif
+
+bfd_reloc_code_real_type
+md_cgen_lookup_reloc (const CGEN_INSN *insn ATTRIBUTE_UNUSED,
+ const CGEN_OPERAND *operand,
+ fixS *fixP)
+{
+ enum bfd_reloc_code_real reloc = fixP->fx_cgen.opinfo;
+ static char printed[MEP_OPERAND_MAX] = { 0 };
+
+ /* If there's a reloc here, it's because the parser saw a %foo() and
+ is giving us the correct reloc to use, or because we converted to
+ a different size reloc below and want to avoid "converting" more
+ than once. */
+ if (reloc && reloc != BFD_RELOC_NONE)
+ return reloc;
+
+ switch (operand->type)
+ {
+ MAP (PCREL8A2); /* beqz */
+ MAP (PCREL12A2); /* bsr16 */
+ MAP (PCREL17A2); /* beqi */
+ MAP (PCREL24A2); /* bsr24 */
+ MAP (PCABS24A2); /* jmp */
+ MAP (UIMM24); /* mov */
+ MAP (ADDR24A4); /* sw/lw */
+
+ /* The rest of the relocs should be generated by the parser,
+ for things such as %tprel(), etc. */
+ case MEP_OPERAND_SIMM16:
+#ifdef OBJ_COMPLEX_RELC
+ /* coalescing this into RELOC_MEP_16 is actually a bug,
+ since it's a signed operand. let the relc code handle it. */
+ return BFD_RELOC_RELC;
+#endif
+
+ case MEP_OPERAND_UIMM16:
+ case MEP_OPERAND_SDISP16:
+ case MEP_OPERAND_CODE16:
+ fixP->fx_where += 2;
+ /* to avoid doing the above add twice */
+ fixP->fx_cgen.opinfo = BFD_RELOC_MEP_16;
+ return BFD_RELOC_MEP_16;
+
+ default:
+#ifdef OBJ_COMPLEX_RELC
+ /* this is not an error, yet.
+ pass it to the linker. */
+ return BFD_RELOC_RELC;
+#endif
+ if (printed[operand->type])
+ return BFD_RELOC_NONE;
+ printed[operand->type] = 1;
+
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Don't know how to relocate plain operands of type %s"),
+ operand->name);
+
+ /* Print some helpful hints for the user. */
+ switch (operand->type)
+ {
+ case MEP_OPERAND_UDISP7:
+ case MEP_OPERAND_UDISP7A2:
+ case MEP_OPERAND_UDISP7A4:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Perhaps you are missing %%tpoff()?"));
+ break;
+ default:
+ break;
+ }
+ return BFD_RELOC_NONE;
+ }
+}
+
+/* Called while parsing an instruction to create a fixup.
+ We need to check for HI16 relocs and queue them up for later sorting. */
+
+fixS *
+mep_cgen_record_fixup_exp (fragS *frag,
+ int where,
+ const CGEN_INSN *insn,
+ int length,
+ const CGEN_OPERAND *operand,
+ int opinfo,
+ expressionS *exp)
+{
+ fixS * fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+ return fixP;
+}
+
+/* Return BFD reloc type from opinfo field in a fixS.
+ It's tricky using fx_r_type in mep_frob_file because the values
+ are BFD_RELOC_UNUSED + operand number. */
+#define FX_OPINFO_R_TYPE(f) ((f)->fx_cgen.opinfo)
+
+/* Sort any unmatched HI16 relocs so that they immediately precede
+ the corresponding LO16 reloc. This is called before md_apply_fix and
+ tc_gen_reloc. */
+
+void
+mep_frob_file ()
+{
+ struct mep_hi_fixup * l;
+
+ for (l = mep_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type * seginfo;
+ int pass;
+
+ gas_assert (FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_HI16
+ || FX_OPINFO_R_TYPE (l->fixp) == BFD_RELOC_LO16);
+
+ /* Check quickly whether the next fixup happens to be a matching low. */
+ if (l->fixp->fx_next != NULL
+ && FX_OPINFO_R_TYPE (l->fixp->fx_next) == BFD_RELOC_LO16
+ && l->fixp->fx_addsy == l->fixp->fx_next->fx_addsy
+ && l->fixp->fx_offset == l->fixp->fx_next->fx_offset)
+ continue;
+
+ /* Look through the fixups for this segment for a matching
+ `low'. When we find one, move the high just in front of it.
+ We do this in two passes. In the first pass, we try to find
+ a unique `low'. In the second pass, we permit multiple
+ high's relocs for a single `low'. */
+ seginfo = seg_info (l->seg);
+ for (pass = 0; pass < 2; pass++)
+ {
+ fixS * f;
+ fixS * prev;
+
+ prev = NULL;
+ for (f = seginfo->fix_root; f != NULL; f = f->fx_next)
+ {
+ /* Check whether this is a `low' fixup which matches l->fixp. */
+ if (FX_OPINFO_R_TYPE (f) == BFD_RELOC_LO16
+ && f->fx_addsy == l->fixp->fx_addsy
+ && f->fx_offset == l->fixp->fx_offset
+ && (pass == 1
+ || prev == NULL
+ || (FX_OPINFO_R_TYPE (prev) != BFD_RELOC_HI16)
+ || prev->fx_addsy != f->fx_addsy
+ || prev->fx_offset != f->fx_offset))
+ {
+ fixS ** pf;
+
+ /* Move l->fixp before f. */
+ for (pf = &seginfo->fix_root;
+ * pf != l->fixp;
+ pf = & (* pf)->fx_next)
+ gas_assert (* pf != NULL);
+
+ * pf = l->fixp->fx_next;
+
+ l->fixp->fx_next = f;
+ if (prev == NULL)
+ seginfo->fix_root = l->fixp;
+ else
+ prev->fx_next = l->fixp;
+
+ break;
+ }
+
+ prev = f;
+ }
+
+ if (f != NULL)
+ break;
+
+ if (pass == 1)
+ as_warn_where (l->fixp->fx_file, l->fixp->fx_line,
+ _("Unmatched high relocation"));
+ }
+ }
+}
+
+/* See whether we need to force a relocation into the output file. */
+
+int
+mep_force_relocation (fixS *fixp)
+{
+ if ( fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 1;
+
+ if (generic_force_reloc (fixp))
+ return 1;
+
+ /* Allow branches to global symbols to be resolved at assembly time.
+ This is consistent with way relaxable branches are handled, since
+ branches to both global and local symbols are relaxed. It also
+ corresponds to the assumptions made in md_pcrel_from_section. */
+ return S_FORCE_RELOC (fixp->fx_addsy, !fixp->fx_pcrel);
+}
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+bfd_boolean
+mep_fix_adjustable (fixS *fixP)
+{
+ bfd_reloc_code_real_type reloc_type;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand
+ = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+ else
+ reloc_type = fixP->fx_r_type;
+
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return 0;
+
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries */
+ if (reloc_type == BFD_RELOC_VTABLE_INHERIT
+ || reloc_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+bfd_vma
+mep_elf_section_letter (int letter, char **ptrmsg)
+{
+ if (letter == 'v')
+ return SHF_MEP_VLIW;
+
+ *ptrmsg = _("bad .section directive: want a,v,w,x,M,S in string");
+ return -1;
+}
+
+flagword
+mep_elf_section_flags (flagword flags, bfd_vma attr, int type ATTRIBUTE_UNUSED)
+{
+ if (attr & SHF_MEP_VLIW)
+ flags |= SEC_MEP_VLIW;
+ return flags;
+}
+
+/* In vliw mode, the default section is .vtext. We have to be able
+ to switch into .vtext using only the .vtext directive. */
+
+static segT
+mep_vtext_section (void)
+{
+ static segT vtext_section;
+
+ if (! vtext_section)
+ {
+ flagword applicable = bfd_applicable_section_flags (stdoutput);
+ vtext_section = subseg_new (VTEXT_SECTION_NAME, 0);
+ bfd_set_section_flags (stdoutput, vtext_section,
+ applicable & (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_CODE | SEC_READONLY
+ | SEC_MEP_VLIW));
+ }
+
+ return vtext_section;
+}
+
+static void
+mep_s_vtext (int ignore ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+ /* Record previous_section and previous_subsection. */
+ obj_elf_section_change_hook ();
+
+ temp = get_absolute_expression ();
+ subseg_set (mep_vtext_section (), (subsegT) temp);
+ demand_empty_rest_of_line ();
+}
+
+static void
+mep_switch_to_core_mode (int dummy ATTRIBUTE_UNUSED)
+{
+ mep_process_saved_insns ();
+ pluspresent = 0;
+ mode = CORE;
+}
+
+static void
+mep_switch_to_vliw_mode (int dummy ATTRIBUTE_UNUSED)
+{
+ if (! MEP_VLIW)
+ as_bad (_(".vliw unavailable when VLIW is disabled."));
+ mode = VLIW;
+ /* Switch into .vtext here too. */
+ /* mep_s_vtext(); */
+}
+
+/* This is an undocumented pseudo-op used to disable gas's
+ "disabled_registers" check. Used for code which checks for those
+ registers at runtime. */
+static void
+mep_noregerr (int i ATTRIBUTE_UNUSED)
+{
+ allow_disabled_registers = 1;
+}
+
+/* mep_unrecognized_line: This is called when a line that can't be parsed
+ is encountered. We use it to check for a leading '+' sign which indicates
+ that the current instruction is a coprocessor instruction that is to be
+ parallelized with a previous core insn. This function accepts the '+' and
+ rejects all other characters that might indicate garbage at the beginning
+ of the line. The '+' character gets lost as the calling loop continues,
+ so we need to indicate that we saw it. */
+
+int
+mep_unrecognized_line (int ch)
+{
+ switch (ch)
+ {
+ case '+':
+ pluspresent = 1;
+ return 1; /* '+' indicates an instruction to be parallelized. */
+ default:
+ return 0; /* If it's not a '+', the line can't be parsed. */
+ }
+}
+
+void
+mep_cleanup (void)
+{
+ /* Take care of any insns left to be parallelized when the file ends.
+ This is mainly here to handle the case where the file ends with an
+ insn preceeded by a + or the file ends unexpectedly. */
+ if (mode == VLIW)
+ mep_process_saved_insns ();
+}
+
+int
+mep_flush_pending_output (void)
+{
+ if (mode == VLIW)
+ {
+ mep_process_saved_insns ();
+ pluspresent = 0;
+ }
+
+ return 1;
+}
diff --git a/gas/config/tc-mep.h b/gas/config/tc-mep.h
new file mode 100644
index 0000000..0df5de8
--- /dev/null
+++ b/gas/config/tc-mep.h
@@ -0,0 +1,119 @@
+/* tc-mep.h -- Header file for tc-mep.c.
+ Copyright (C) 2001-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_MEP
+
+/* Support computed relocations. */
+#define OBJ_COMPLEX_RELC
+
+/* Support many operands per instruction. */
+#define GAS_CGEN_MAX_FIXUPS 10
+
+#define LISTING_HEADER "MEP GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_mep
+
+#define TARGET_FORMAT (target_big_endian ? "elf32-mep" : "elf32-mep-little")
+
+/* This is the default. */
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* Do not define DIFF_EXPR_OK - the MeP does not have a 32-bit PC-relative reloc. */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define MD_APPLY_FIX
+#define md_apply_fix mep_apply_fix
+extern void mep_apply_fix (struct fix *, valueT *, segT);
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define tc_frob_file() mep_frob_file ()
+extern void mep_frob_file (void);
+
+#define tc_fix_adjustable(fixP) mep_fix_adjustable (fixP)
+extern bfd_boolean mep_fix_adjustable (struct fix *);
+
+/* After creating a fixup for an instruction operand, we need
+ to check for HI16 relocs and queue them up for later sorting. */
+#define md_cgen_record_fixup_exp mep_cgen_record_fixup_exp
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) mep_force_relocation (fix)
+extern int mep_force_relocation (struct fix *);
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+extern void gas_cgen_md_operand (expressionS *);
+#define md_operand(x) gas_cgen_md_operand (x)
+
+#define md_flush_pending_output() mep_flush_pending_output()
+extern int mep_flush_pending_output(void);
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+extern long mep_relax_frag (segT, fragS *, long);
+#define md_relax_frag mep_relax_frag
+
+/* Account for inserting a jmp after the insn. */
+#define TC_CGEN_MAX_RELAX(insn, len) ((len) + 4)
+
+extern void mep_prepare_relax_scan (fragS *, offsetT *, relax_substateT);
+#define md_prepare_relax_scan(FRAGP, ADDR, AIM, STATE, TYPE) \
+ mep_prepare_relax_scan (FRAGP, &AIM, STATE)
+
+/* Support for core/vliw mode switching. */
+#define CORE 0
+#define VLIW 1
+#define MAX_PARALLEL_INSNS 56 /* From email from Toshiba. */
+#define VTEXT_SECTION_NAME ".vtext"
+
+/* Needed to process pending instructions when a label is encountered. */
+#define TC_START_LABEL(ch, s, ptr) ((ch == ':') && mep_flush_pending_output ())
+
+#define tc_unrecognized_line(c) mep_unrecognized_line (c)
+extern int mep_unrecognized_line (int);
+#define md_cleanup mep_cleanup
+extern void mep_cleanup (void);
+
+#define md_elf_section_letter mep_elf_section_letter
+extern bfd_vma mep_elf_section_letter (int, char **);
+#define md_elf_section_flags mep_elf_section_flags
+extern flagword mep_elf_section_flags (flagword, bfd_vma, int);
+
+#define ELF_TC_SPECIAL_SECTIONS \
+ { VTEXT_SECTION_NAME, SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR|SHF_MEP_VLIW },
+
+/* The values of the following enum are for use with parinsnum, which
+ is a variable in md_assemble that keeps track of whether or not the
+ next instruction is expected to be the first or second instrucion in
+ a parallelization group. */
+typedef enum exp_par_insn_{FIRST, SECOND} EXP_PAR_INSN;
diff --git a/gas/config/tc-metag.c b/gas/config/tc-metag.c
new file mode 100644
index 0000000..cb2fc99
--- /dev/null
+++ b/gas/config/tc-metag.c
@@ -0,0 +1,7141 @@
+/* tc-metag.c -- Assembler for the Imagination Technologies Meta.
+ Copyright (C) 2013-2014 Free Software Foundation, Inc.
+ Contributed by Imagination Technologies Ltd.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "safe-ctype.h"
+#include "hashtab.h"
+#include "libbfd.h"
+
+#include <stdio.h>
+
+#include "opcode/metag.h"
+
+const char comment_chars[] = "!";
+const char line_comment_chars[] = "!#";
+const char line_separator_chars[] = ";";
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+const char EXP_CHARS[] = "eE";
+const char metag_symbol_chars[] = "[";
+
+static char register_chars[256];
+static char mnemonic_chars[256];
+
+#define is_register_char(x) (register_chars[(unsigned char) x])
+#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char) x])
+#define is_whitespace_char(x) (((x) == ' ') || ((x) == '\t'))
+#define is_space_char(x) ((x) == ' ')
+
+#define FPU_PREFIX_CHAR 'f'
+#define DSP_PREFIX_CHAR 'd'
+
+/* Instruction mnemonics that need disambiguating with respect to prefixes. */
+#define FFB_INSN "ffb"
+#define DCACHE_INSN "dcache"
+#define DEFR_INSN "defr"
+
+#define FPU_DOUBLE_CHAR 'd'
+#define FPU_PAIR_CHAR 'l'
+
+#define DSP_DUAL_CHAR 'l'
+
+#define END_OF_INSN '\0'
+
+/* Maximum length of a mnemonic including all suffixes. */
+#define MAX_MNEMONIC_LEN 16
+/* Maximum length of a register name. */
+#define MAX_REG_LEN 17
+
+/* Addressing modes must be enclosed with square brackets. */
+#define ADDR_BEGIN_CHAR '['
+#define ADDR_END_CHAR ']'
+/* Immediates must be prefixed with a hash. */
+#define IMM_CHAR '#'
+
+#define COMMA ','
+#define PLUS '+'
+#define MINUS '-'
+
+/* Short units are those that can be encoded with 2 bits. */
+#define SHORT_UNITS "D0, D1, A0 or A1"
+
+static unsigned int mcpu_opt = CoreMeta12;
+static unsigned int mfpu_opt = 0;
+static unsigned int mdsp_opt = 0;
+
+const char * md_shortopts = "m:";
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Parser hash tables. */
+static htab_t mnemonic_htab;
+static htab_t reg_htab;
+static htab_t dsp_reg_htab;
+static htab_t dsp_tmpl_reg_htab[2];
+static htab_t scond_htab;
+
+#define GOT_NAME "__GLOBAL_OFFSET_TABLE__"
+symbolS * GOT_symbol;
+
+enum fpu_insn_width {
+ FPU_WIDTH_SINGLE,
+ FPU_WIDTH_DOUBLE,
+ FPU_WIDTH_PAIR,
+};
+
+#define FPU_ACTION_ABS_CHAR 'a'
+#define FPU_ACTION_INV_CHAR 'i'
+#define FPU_ACTION_QUIET_CHAR 'q'
+#define FPU_ACTION_ZERO_CHAR 'z'
+
+#define FPU_ACTION_ABS 0x1
+#define FPU_ACTION_INV 0x2
+#define FPU_ACTION_QUIET 0x4
+#define FPU_ACTION_ZERO 0x8
+
+enum dsp_insn_width {
+ DSP_WIDTH_SINGLE,
+ DSP_WIDTH_DUAL,
+};
+
+#define DSP_ACTION_QR64_CHAR 'q'
+#define DSP_ACTION_UMUL_CHAR 'u'
+#define DSP_ACTION_ROUND_CHAR 'r'
+#define DSP_ACTION_CLAMP9_CHAR 'g'
+#define DSP_ACTION_CLAMP8_CHAR 'b'
+#define DSP_ACTION_MOD_CHAR 'm'
+#define DSP_ACTION_ACC_ZERO_CHAR 'z'
+#define DSP_ACTION_ACC_ADD_CHAR 'p'
+#define DSP_ACTION_ACC_SUB_CHAR 'n'
+#define DSP_ACTION_OV_CHAR 'o'
+
+#define DSP_ACTION_QR64 0x001
+#define DSP_ACTION_UMUL 0x002
+#define DSP_ACTION_ROUND 0x004
+#define DSP_ACTION_CLAMP9 0x008
+#define DSP_ACTION_CLAMP8 0x010
+#define DSP_ACTION_MOD 0x020
+#define DSP_ACTION_ACC_ZERO 0x040
+#define DSP_ACTION_ACC_ADD 0x080
+#define DSP_ACTION_ACC_SUB 0x100
+#define DSP_ACTION_OV 0x200
+
+#define DSP_DAOPPAME_8_CHAR 'b'
+#define DSP_DAOPPAME_16_CHAR 'w'
+#define DSP_DAOPPAME_TEMP_CHAR 't'
+#define DSP_DAOPPAME_HIGH_CHAR 'h'
+
+#define DSP_DAOPPAME_8 0x1
+#define DSP_DAOPPAME_16 0x2
+#define DSP_DAOPPAME_TEMP 0x4
+#define DSP_DAOPPAME_HIGH 0x8
+
+/* Structure holding information about a parsed instruction. */
+typedef struct {
+ /* Instruction type. */
+ enum insn_type type;
+ /* Split condition code. */
+ enum scond_code scond;
+
+ /* Instruction bits. */
+ unsigned int bits;
+ /* Size of the instruction in bytes. */
+ size_t len;
+
+ /* FPU instruction encoding. */
+ enum fpu_insn_width fpu_width;
+ unsigned int fpu_action_flags;
+
+ /* DSP instruction encoding. */
+ enum dsp_insn_width dsp_width;
+ unsigned int dsp_action_flags;
+ unsigned int dsp_daoppame_flags;
+
+ /* Reloc encoding information, maximum of one reloc per insn. */
+ enum bfd_reloc_code_real reloc_type;
+ int reloc_pcrel;
+ expressionS reloc_exp;
+ unsigned int reloc_size;
+} metag_insn;
+
+/* Structure holding information about a parsed addressing mode. */
+typedef struct {
+ const metag_reg *base_reg;
+ const metag_reg *offset_reg;
+
+ expressionS exp;
+
+ enum bfd_reloc_code_real reloc_type;
+
+ /* Whether we have an immediate or not. */
+ unsigned short immediate:1;
+ /* Whether or not the base register is updated. */
+ unsigned short update:1;
+ /* Whether the operation uses the address pre or post increment. */
+ unsigned short post_increment:1;
+ /* Whether the immediate should be negated. */
+ unsigned short negate:1;
+} metag_addr;
+
+/* Linked list of possible parsers for this instruction. */
+typedef struct _insn_templates {
+ const insn_template *template;
+ struct _insn_templates *next;
+} insn_templates;
+
+/* Parse an instruction that takes no operands. */
+static const char *
+parse_none (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ insn->bits = template->meta_opcode;
+ insn->len = 4;
+ return line;
+}
+
+/* Return the next non-whitespace character in LINE or NULL. */
+static const char *
+skip_whitespace (const char *line)
+{
+ const char *l = line;
+
+ if (is_whitespace_char (*l))
+ {
+ l++;
+ }
+
+ return l;
+}
+
+/* Return the next non-space character in LINE or NULL. */
+static const char *
+skip_space (const char *line)
+{
+ const char *l = line;
+
+ if (is_space_char (*l))
+ {
+ l++;
+ }
+
+ return l;
+}
+
+/* Return the character after the current one in LINE if the current
+ character is a comma, otherwise NULL. */
+static const char *
+skip_comma (const char *line)
+{
+ const char *l = line;
+
+ if (l == NULL || *l != COMMA)
+ return NULL;
+
+ l++;
+
+ return l;
+}
+
+/* Return the metag_reg struct corresponding to NAME or NULL if no such
+ register exists. */
+static const metag_reg *
+parse_gp_reg (const char *name)
+{
+ const metag_reg *reg;
+ metag_reg entry;
+
+ entry.name = name;
+
+ reg = (const metag_reg *) htab_find (reg_htab, &entry);
+
+ return reg;
+}
+
+/* Parse a list of up to COUNT GP registers from LINE, returning the
+ registers parsed in REGS and the number parsed in REGS_READ. Return
+ a pointer to the next character or NULL. */
+static const char *
+parse_gp_regs_list (const char *line, const metag_reg **regs, size_t count,
+ size_t *regs_read)
+{
+ const char *l = line;
+ char reg_buf[MAX_REG_LEN];
+ int seen_regs = 0;
+ size_t i;
+
+ for (i = 0; i < count; i++)
+ {
+ size_t len = 0;
+ const char *next;
+
+ next = l;
+
+ if (i > 0)
+ {
+ l = skip_comma (l);
+ if (l == NULL)
+ {
+ *regs_read = seen_regs;
+ return next;
+ }
+ }
+
+ while (is_register_char (*l))
+ {
+ reg_buf[len] = *l;
+ l++;
+ len++;
+ if (!(len < MAX_REG_LEN))
+ return NULL;
+ }
+
+ reg_buf[len] = '\0';
+
+ if (len)
+ {
+ const metag_reg *reg = parse_gp_reg (reg_buf);
+
+ if (!reg)
+ {
+ *regs_read = seen_regs;
+ return next;
+ }
+ else
+ {
+ regs[i] = reg;
+ seen_regs++;
+ }
+ }
+ else
+ {
+ *regs_read = seen_regs;
+ return next;
+ }
+ }
+
+ *regs_read = seen_regs;
+ return l;
+}
+
+/* Parse a list of exactly COUNT GP registers from LINE, returning the
+ registers parsed in REGS. Return a pointer to the next character or NULL. */
+static const char *
+parse_gp_regs (const char *line, const metag_reg **regs, size_t count)
+{
+ const char *l = line;
+ size_t regs_read = 0;
+
+ l = parse_gp_regs_list (l, regs, count, &regs_read);
+
+ if (regs_read != count)
+ return NULL;
+ else
+ return l;
+}
+
+/* Parse a list of exactly COUNT FPU registers from LINE, returning the
+ registers parsed in REGS. Return a pointer to the next character or NULL. */
+static const char *
+parse_fpu_regs (const char *line, const metag_reg **regs, size_t count)
+{
+ const char *l = line;
+ size_t regs_read = 0;
+
+ l = parse_gp_regs_list (l, regs, count, &regs_read);
+
+ if (regs_read != count)
+ return NULL;
+ else
+ {
+ size_t i;
+ for (i = 0; i < count; i++)
+ {
+ if (regs[i]->unit != UNIT_FX)
+ return NULL;
+ }
+ return l;
+ }
+}
+
+/* Return TRUE if REG1 and REG2 are in paired units. */
+static bfd_boolean
+is_unit_pair (const metag_reg *reg1, const metag_reg *reg2)
+{
+ if ((reg1->unit == UNIT_A0 &&
+ (reg2->unit == UNIT_A1)) ||
+ (reg1->unit == UNIT_A1 &&
+ (reg2->unit == UNIT_A0)) ||
+ (reg1->unit == UNIT_D0 &&
+ (reg2->unit == UNIT_D1)) ||
+ (reg1->unit == UNIT_D1 &&
+ (reg2->unit == UNIT_D0)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Return TRUE if REG1 and REG2 form a register pair. */
+static bfd_boolean
+is_reg_pair (const metag_reg *reg1, const metag_reg *reg2)
+{
+ if (reg1->unit == UNIT_FX &&
+ reg2->unit == UNIT_FX &&
+ reg2->no == reg1->no + 1)
+ return TRUE;
+
+ if (reg1->no != reg2->no)
+ return FALSE;
+
+ return is_unit_pair (reg1, reg2);
+}
+
+/* Parse a pair of GP registers from LINE, returning the registers parsed
+ in REGS. Return a pointer to the next character or NULL. */
+static const char *
+parse_pair_gp_regs (const char *line, const metag_reg **regs)
+{
+ const char *l = line;
+
+ l = parse_gp_regs (line, regs, 2);
+
+ if (l == NULL)
+ {
+ l = parse_gp_regs (line, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->unit == UNIT_RD)
+ return l;
+ else
+ return NULL;
+ }
+
+ if (is_reg_pair (regs[0], regs[1]))
+ return l;
+
+ return NULL;
+}
+
+/* Parse a unit-to-unit MOV instruction. */
+static const char *
+parse_mov_u2u (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const metag_reg *regs[2];
+
+ line = parse_gp_regs (line, regs, 2);
+
+ if (line == NULL)
+ return NULL;
+
+ if (!mfpu_opt && (regs[0]->unit == UNIT_FX || regs[1]->unit == UNIT_FX))
+ {
+ as_bad (_("no floating point unit specified"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[1]->no << 19) |
+ (regs[0]->no << 14) |
+ (regs[1]->unit << 10) |
+ (regs[0]->unit << 5));
+ insn->len = 4;
+ return line;
+}
+
+/* Parse a MOV to port instruction. */
+static const char *
+parse_mov_port (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ unsigned int is_movl = MINOR_OPCODE (template->meta_opcode) == MOVL_MINOR;
+ const metag_reg *dest_regs[2];
+ const metag_reg *port_regs[1];
+
+ if (is_movl)
+ l = parse_gp_regs (l, dest_regs, 2);
+ else
+ l = parse_gp_regs (l, dest_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (template->insn_type == INSN_FPU && dest_regs[0]->unit != UNIT_FX)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_gp_regs (l, port_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (port_regs[0]->unit != UNIT_RD ||
+ port_regs[0]->no != 0)
+ return NULL;
+
+ if (is_movl)
+ {
+ if (!is_unit_pair (dest_regs[0], dest_regs[1]))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 14) |
+ (dest_regs[1]->no << 9) |
+ ((dest_regs[0]->unit & SHORT_UNIT_MASK) << 5));
+ }
+ else
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 14) |
+ (dest_regs[0]->unit << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a MOVL to TTREC instruction. */
+static const char *
+parse_movl_ttrec (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *src_regs[2];
+ const metag_reg *dest_regs[1];
+
+ l = parse_gp_regs (l, dest_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (dest_regs[0]->unit != UNIT_TT ||
+ dest_regs[0]->no != 3)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_gp_regs (l, src_regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!is_unit_pair (src_regs[0], src_regs[1]))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (src_regs[0]->no << 19) |
+ (src_regs[1]->no << 14) |
+ ((src_regs[0]->unit & SHORT_UNIT_MASK) << 7));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an incrementing or decrementing addressing mode. */
+static const char *
+parse_addr_incr_op (const char *line, metag_addr *addr)
+{
+ const char *l = line;
+ const char *ll;
+
+ ll = l + 1;
+
+ if (*l == PLUS &&
+ *ll == PLUS)
+ {
+ addr->update = 1;
+ ll++;
+ return ll;
+ }
+ else if (*l == MINUS &&
+ *ll == MINUS)
+ {
+ addr->update = 1;
+ addr->negate = 1;
+ ll++;
+ return ll;
+ }
+ return NULL;
+}
+
+/* Parse an pre-incrementing or pre-decrementing addressing mode. */
+static const char *
+parse_addr_pre_incr_op (const char *line, metag_addr *addr)
+{
+ return parse_addr_incr_op (line, addr);
+}
+
+/* Parse an post-incrementing or post-decrementing addressing mode. */
+static const char *
+parse_addr_post_incr_op (const char *line, metag_addr *addr)
+{
+ const char *l;
+
+ l = parse_addr_incr_op (line, addr);
+
+ if (l == NULL)
+ return NULL;
+
+ addr->post_increment = 1;
+
+ return l;
+}
+
+/* Parse an infix addressing mode. */
+static const char *
+parse_addr_op (const char *line, metag_addr *addr)
+{
+ const char *l = line;
+ const char *ll;
+
+ ll = l + 1;
+
+ if (*l == PLUS)
+ {
+ if (*ll == PLUS)
+ {
+ addr->update = 1;
+ ll++;
+ return ll;
+ }
+ l++;
+ return l;
+ }
+ return NULL;
+}
+
+/* Parse the immediate portion of an addrssing mode. */
+static const char *
+parse_imm_addr (const char *line, metag_addr *addr)
+{
+ const char *l = line;
+ char *save_input_line_pointer;
+ expressionS *exp = &addr->exp;
+
+ /* Skip #. */
+ if (*l == '#')
+ l++;
+ else
+ return NULL;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = (char *) l;
+
+ expression (exp);
+
+ l = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_absent || exp->X_op == O_big)
+ {
+ return NULL;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ return l;
+ }
+ else
+ {
+ if (exp->X_op == O_PIC_reloc &&
+ exp->X_md == BFD_RELOC_METAG_GETSET_GOT)
+ {
+ exp->X_op = O_symbol;
+ addr->reloc_type = BFD_RELOC_METAG_GETSET_GOT;
+ }
+ else if (exp->X_op == O_PIC_reloc &&
+ exp->X_md == BFD_RELOC_METAG_TLS_IE)
+ {
+ exp->X_op = O_symbol;
+ addr->reloc_type = BFD_RELOC_METAG_TLS_IE;
+ }
+ else if (exp->X_op == O_PIC_reloc &&
+ exp->X_md == BFD_RELOC_METAG_GOTOFF)
+ {
+ exp->X_op = O_symbol;
+ addr->reloc_type = BFD_RELOC_METAG_GETSET_GOTOFF;
+ }
+ else
+ addr->reloc_type = BFD_RELOC_METAG_GETSETOFF;
+ return l;
+ }
+}
+
+/* Parse the offset portion of an addressing mode (register or immediate). */
+static const char *
+parse_addr_offset (const char *line, metag_addr *addr, int size)
+{
+ const char *l = line;
+ const metag_reg *regs[1];
+
+ if (*l == IMM_CHAR)
+ {
+ /* ++ is a valid operator in our addressing but not in an expr. Make
+ sure that the expression parser never sees it. */
+ char *ppp = strstr(l, "++");
+ char ppch = '+';
+
+ if (ppp)
+ *ppp = '\0';
+
+ l = parse_imm_addr (l, addr);
+
+ if (ppp)
+ *ppp = ppch;
+
+ if (l == NULL)
+ return NULL;
+
+ if (addr->exp.X_add_number % size)
+ {
+ as_bad (_("offset must be a multiple of %d"), size);
+ return NULL;
+ }
+
+ addr->immediate = 1;
+ return l;
+ }
+ else
+ {
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->unit != addr->base_reg->unit)
+ {
+ as_bad (_("offset and base must be from the same unit"));
+ return NULL;
+ }
+
+ addr->offset_reg = regs[0];
+ return l;
+ }
+}
+
+/* Parse an addressing mode. */
+static const char *
+parse_addr (const char *line, metag_addr *addr, unsigned int size)
+{
+ const char *l = line;
+ const char *ll;
+ const metag_reg *regs[1];
+
+ /* Skip opening square bracket. */
+ l++;
+
+ ll = parse_addr_pre_incr_op (l, addr);
+
+ if (ll != NULL)
+ l = ll;
+
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ addr->base_reg = regs[0];
+
+ if (*l == ADDR_END_CHAR)
+ {
+ addr->exp.X_op = O_constant;
+ addr->exp.X_add_symbol = NULL;
+ addr->exp.X_op_symbol = NULL;
+ if (addr->update == 1)
+ {
+ /* We have a pre increment/decrement. */
+ addr->exp.X_add_number = size;
+ }
+ else
+ {
+ /* Simple register with no offset (0 immediate). */
+ addr->exp.X_add_number = 0;
+ }
+ addr->immediate = 1;
+ l++;
+ return l;
+ }
+
+ /* We already had a pre increment/decrement. */
+ if (addr->update == 1)
+ return NULL;
+
+ ll = parse_addr_post_incr_op (l, addr);
+
+ if (ll && *ll == ADDR_END_CHAR)
+ {
+ if (addr->update == 1)
+ {
+ /* We have a post increment/decrement. */
+ addr->exp.X_op = O_constant;
+ addr->exp.X_add_number = size;
+ addr->exp.X_add_symbol = NULL;
+ addr->exp.X_op_symbol = NULL;
+ addr->post_increment = 1;
+ }
+ addr->immediate = 1;
+ ll++;
+ return ll;
+ }
+
+ addr->post_increment = 0;
+
+ l = parse_addr_op (l, addr);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_addr_offset (l, addr, size);
+
+ if (l == NULL)
+ return NULL;
+
+ if (*l == ADDR_END_CHAR)
+ {
+ l++;
+ return l;
+ }
+
+ /* We already had a pre increment/decrement. */
+ if (addr->update == 1)
+ return NULL;
+
+ l = parse_addr_post_incr_op (l, addr);
+
+ if (l == NULL)
+ return NULL;
+
+ if (*l == ADDR_END_CHAR)
+ {
+ l++;
+ return l;
+ }
+
+ return NULL;
+}
+
+/* Parse a GET or pipeline MOV instruction. */
+static const char *
+parse_get (const char *line, const metag_reg **regs, metag_addr *addr,
+ unsigned int size, bfd_boolean is_mov)
+{
+ const char *l = line;
+
+ if (size == 8)
+ {
+ l = parse_pair_gp_regs (l, regs);
+
+ if (l == NULL)
+ return NULL;
+ }
+ else
+ {
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ {
+ if (!is_mov)
+ as_bad (_("invalid destination register"));
+ return NULL;
+ }
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_addr (l, addr, size);
+
+ if (l == NULL)
+ {
+ if (!is_mov)
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ return l;
+}
+
+/* Parse a SET instruction. */
+static const char *
+parse_set (const char *line, const metag_reg **regs, metag_addr *addr,
+ unsigned int size)
+{
+ const char *l = line;
+
+ l = parse_addr (l, addr, size);
+
+ if (l == NULL)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ if (size == 8)
+ {
+ const char *ll = l;
+
+ ll = parse_pair_gp_regs (l, regs);
+
+ if (ll == NULL)
+ {
+ /* Maybe this is an RD register, which is 64 bits wide so needs no
+ pair. */
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL ||
+ regs[0]->unit != UNIT_RD)
+ {
+ return NULL;
+ }
+ }
+ else
+ l = ll;
+ }
+ else
+ {
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ {
+ as_bad (_("invalid source register"));
+ return NULL;
+ }
+ }
+
+ return l;
+}
+
+/* Check a signed integer value can be represented in the given number
+ of bits. */
+static bfd_boolean
+within_signed_range (int value, unsigned int bits)
+{
+ int min_val = -(1 << (bits - 1));
+ int max_val = (1 << (bits - 1)) - 1;
+ return (value <= max_val) && (value >= min_val);
+}
+
+/* Check an unsigned integer value can be represented in the given number
+ of bits. */
+static bfd_boolean
+within_unsigned_range (unsigned int value, unsigned int bits)
+{
+ return value < (unsigned int)(1 << bits);
+}
+
+/* Return TRUE if UNIT can be expressed using a short code. */
+static bfd_boolean
+is_short_unit (enum metag_unit unit)
+{
+ switch (unit)
+ {
+ case UNIT_A0:
+ case UNIT_A1:
+ case UNIT_D0:
+ case UNIT_D1:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* Copy reloc data from ADDR to INSN. */
+static void
+copy_addr_reloc (metag_insn *insn, metag_addr *addr)
+{
+ memcpy (&insn->reloc_exp, &addr->exp, sizeof(insn->reloc_exp));
+ insn->reloc_type = addr->reloc_type;
+}
+
+/* Parse a GET, SET or pipeline MOV instruction. */
+static const char *
+parse_get_set (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+ metag_addr addr;
+ unsigned int size = metag_get_set_size_bytes (template->meta_opcode);
+ bfd_boolean is_get = MAJOR_OPCODE (template->meta_opcode) == OPC_GET;
+ unsigned int reg_no;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ if (is_get)
+ {
+ bfd_boolean is_mov = strncmp (template->name, "MOV", 3) == 0;
+
+ l = parse_get (l, regs, &addr, size, is_mov);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!(regs[0]->unit == UNIT_D0 ||
+ regs[0]->unit == UNIT_D1 ||
+ regs[0]->unit == UNIT_A0 ||
+ regs[0]->unit == UNIT_A1 ||
+ (regs[0]->unit == UNIT_RD && is_mov) ||
+ (regs[0]->unit == UNIT_CT && size == 4) ||
+ (regs[0]->unit == UNIT_PC && size == 4) ||
+ (regs[0]->unit == UNIT_TR && size == 4) ||
+ (regs[0]->unit == UNIT_TT && (size == 4 || size == 8)) ||
+ regs[0]->unit == UNIT_FX))
+ {
+ as_bad (_("invalid destination unit"));
+ return NULL;
+ }
+
+ if (regs[0]->unit == UNIT_RD)
+ {
+ if (regs[0]->no == 0)
+ {
+ as_bad (_("mov cannot use RD port as destination"));
+ return NULL;
+ }
+ }
+
+ reg_no = regs[0]->no;
+ }
+ else
+ {
+ l = parse_set (l, regs, &addr, size);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!(regs[0]->unit == UNIT_D0 ||
+ regs[0]->unit == UNIT_D1 ||
+ regs[0]->unit == UNIT_A0 ||
+ regs[0]->unit == UNIT_A1 ||
+ regs[0]->unit == UNIT_RD ||
+ (regs[0]->unit == UNIT_CT && size == 4) ||
+ (regs[0]->unit == UNIT_PC && size == 4) ||
+ (regs[0]->unit == UNIT_TR && size == 4) ||
+ (regs[0]->unit == UNIT_TT && (size == 4 || size == 8)) ||
+ regs[0]->unit == UNIT_FX))
+ {
+ as_bad (_("invalid source unit"));
+ return NULL;
+ }
+
+ if (addr.immediate == 0 &&
+ (regs[0]->unit == addr.base_reg->unit ||
+ (size == 8 && is_unit_pair (regs[0], addr.base_reg))))
+ {
+ as_bad (_("source and address units must not be shared for this addressing mode"));
+ return NULL;
+ }
+
+ if (regs[0]->unit == UNIT_RD)
+ {
+ if (regs[0]->no != 0)
+ {
+ as_bad (_("set can only use RD port as source"));
+ return NULL;
+ }
+ reg_no = 16;
+ }
+ else
+ reg_no = regs[0]->no;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (reg_no << 19) |
+ (regs[0]->unit << 1));
+
+ if (!is_short_unit (addr.base_reg->unit))
+ {
+ as_bad (_("base unit must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ insn->bits |= ((addr.base_reg->no << 14) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5));
+
+ if (addr.immediate)
+ {
+ int offset = addr.exp.X_add_number;
+
+ copy_addr_reloc (insn, &addr);
+
+ if (addr.negate)
+ offset = -offset;
+
+ offset = offset / (int)size;
+
+ if (!within_signed_range (offset, GET_SET_IMM_BITS))
+ {
+ /* We already tried to encode as an extended GET/SET. */
+ as_bad (_("offset value out of range"));
+ return NULL;
+ }
+
+ offset = offset & GET_SET_IMM_MASK;
+
+ insn->bits |= (0x1 << 25);
+ insn->bits |= (offset << 8);
+ }
+ else
+ {
+ insn->bits |= (addr.offset_reg->no << 9);
+ }
+
+ if (addr.update)
+ insn->bits |= (0x1 << 7);
+
+ if (addr.post_increment)
+ insn->bits |= 0x1;
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an extended GET or SET instruction. */
+static const char *
+parse_get_set_ext (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+ metag_addr addr;
+ unsigned int size = metag_get_set_ext_size_bytes (template->meta_opcode);
+ bfd_boolean is_get = MINOR_OPCODE (template->meta_opcode) == GET_EXT_MINOR;
+ bfd_boolean is_mov = MINOR_OPCODE (template->meta_opcode) == MOV_EXT_MINOR;
+ unsigned int reg_unit;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ if (is_get || is_mov)
+ {
+ l = parse_get (l, regs, &addr, size, is_mov);
+ }
+ else
+ {
+ l = parse_set (l, regs, &addr, size);
+ }
+
+ if (l == NULL)
+ return NULL;
+
+ /* Extended GET/SET does not support incrementing addressing. */
+ if (addr.update)
+ return NULL;
+
+ if (is_mov)
+ {
+ if (regs[0]->unit != UNIT_RD)
+ {
+ as_bad (_("destination unit must be RD"));
+ return NULL;
+ }
+ reg_unit = 0;
+ }
+ else
+ {
+ if (!is_short_unit (regs[0]->unit))
+ {
+ return NULL;
+ }
+ reg_unit = regs[0]->unit;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ ((reg_unit & SHORT_UNIT_MASK) << 3));
+
+ if (!is_short_unit (addr.base_reg->unit))
+ {
+ as_bad (_("base unit must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ if (addr.base_reg->no > 1)
+ {
+ return NULL;
+ }
+
+ insn->bits |= ((addr.base_reg->no & EXT_BASE_REG_MASK) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5));
+
+ if (addr.immediate)
+ {
+ int offset = addr.exp.X_add_number;
+
+ copy_addr_reloc (insn, &addr);
+
+ if (addr.negate)
+ offset = -offset;
+
+ offset = offset / (int)size;
+
+ if (!within_signed_range (offset, GET_SET_EXT_IMM_BITS))
+ {
+ /* Parsing as a standard GET/SET provides a smaller offset. */
+ as_bad (_("offset value out of range"));
+ return NULL;
+ }
+
+ offset = offset & GET_SET_EXT_IMM_MASK;
+
+ insn->bits |= (offset << 7);
+ }
+ else
+ {
+ return NULL;
+ }
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an MGET or MSET instruction addressing mode. */
+static const char *
+parse_mget_mset_addr (const char *line, metag_addr *addr)
+{
+ const char *l = line;
+ const char *ll;
+ const metag_reg *regs[1];
+
+ /* Skip opening square bracket. */
+ l++;
+
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ addr->base_reg = regs[0];
+
+ ll = parse_addr_post_incr_op (l, addr);
+
+ if (ll != NULL)
+ l = ll;
+
+ if (addr->negate == 1)
+ return NULL;
+
+ if (*l == ADDR_END_CHAR)
+ {
+ l++;
+ return l;
+ }
+
+ return NULL;
+}
+
+/* Parse an MGET instruction. */
+static const char *
+parse_mget (const char *line, const metag_reg **regs, metag_addr *addr,
+ size_t *regs_read)
+{
+ const char *l = line;
+
+ l = parse_gp_regs_list (l, regs, MGET_MSET_MAX_REGS, regs_read);
+
+ if (l == NULL ||
+ *regs_read == 0)
+ {
+ as_bad (_("invalid destination register list"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_mget_mset_addr (l, addr);
+
+ if (l == NULL)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ return l;
+}
+
+/* Parse an MSET instruction. */
+static const char *
+parse_mset (const char *line, const metag_reg **regs, metag_addr *addr,
+ size_t *regs_read)
+{
+ const char *l = line;
+
+ l = parse_mget_mset_addr (l, addr);
+
+ if (l == NULL)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_gp_regs_list (l, regs, MGET_MSET_MAX_REGS, regs_read);
+
+ if (l == NULL ||
+ *regs_read == 0)
+ {
+ as_bad (_("invalid source register list"));
+ return NULL;
+ }
+
+ return l;
+}
+
+/* Take a register list REGS of size REGS_READ and convert it into an
+ rmask value if possible. Return the rmask value in RMASK and the
+ lowest numbered register in LOWEST_REG. Return TRUE if the conversion
+ was successful. */
+static bfd_boolean
+check_rmask (const metag_reg **regs, size_t regs_read, bfd_boolean is_fpu,
+ bfd_boolean is_64bit, unsigned int *lowest_reg,
+ unsigned int *rmask)
+{
+ unsigned int reg_unit = regs[0]->unit;
+ size_t i;
+
+ for (i = 0; i < regs_read; i++)
+ {
+ if (is_fpu)
+ {
+ if (is_64bit && regs[i]->no % 2)
+ {
+ as_bad (_("register list must be even numbered"));
+ return FALSE;
+ }
+ }
+ else if (regs[i]->unit != reg_unit)
+ {
+ as_bad (_("register list must be from the same unit"));
+ return FALSE;
+ }
+
+ if (regs[i]->no < *lowest_reg)
+ *lowest_reg = regs[i]->no;
+ }
+
+ for (i = 0; i < regs_read; i++)
+ {
+ unsigned int next_bit, next_reg;
+ if (regs[i]->no == *lowest_reg)
+ continue;
+
+ if (is_fpu && is_64bit)
+ next_reg = ((regs[i]->no / 2) - ((*lowest_reg / 2) + 1));
+ else
+ next_reg = (regs[i]->no - (*lowest_reg + 1));
+
+ next_bit = (1 << next_reg);
+
+ if (*rmask & next_bit)
+ {
+ as_bad (_("register list must not contain duplicates"));
+ return FALSE;
+ }
+
+ *rmask |= next_bit;
+ }
+
+ return TRUE;
+}
+
+/* Parse an MGET or MSET instruction. */
+static const char *
+parse_mget_mset (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[MGET_MSET_MAX_REGS];
+ metag_addr addr;
+ bfd_boolean is_get = MAJOR_OPCODE (template->meta_opcode) == OPC_GET;
+ bfd_boolean is_fpu = (MINOR_OPCODE (template->meta_opcode) & 0x6) == 0x6;
+ bfd_boolean is_64bit = (MINOR_OPCODE (template->meta_opcode) & 0x1) == 0x1;
+ size_t regs_read = 0;
+ unsigned int rmask = 0, reg_unit = 0, lowest_reg = 0xffffffff;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ if (is_get)
+ {
+ l = parse_mget (l, regs, &addr, &regs_read);
+ }
+ else
+ {
+ l = parse_mset (l, regs, &addr, &regs_read);
+ }
+
+ if (l == NULL)
+ return NULL;
+
+ if (!check_rmask (regs, regs_read, is_fpu, is_64bit, &lowest_reg, &rmask))
+ return NULL;
+
+ reg_unit = regs[0]->unit;
+
+ if (is_fpu)
+ {
+ if (reg_unit != UNIT_FX)
+ return NULL;
+
+ reg_unit = 0;
+ }
+ else if (reg_unit == UNIT_FX)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (lowest_reg << 19) |
+ ((reg_unit & SHORT_UNIT_MASK) << 3));
+
+ if (!is_short_unit (addr.base_reg->unit))
+ {
+ as_bad (_("base unit must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ insn->bits |= ((addr.base_reg->no << 14) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5));
+
+ insn->bits |= (rmask & RMASK_MASK) << 7;
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a list of registers for MMOV pipeline prime. */
+static const char *
+parse_mmov_prime_list (const char *line, const metag_reg **regs,
+ unsigned int *rmask)
+{
+ const char *l = line;
+ const metag_reg *ra_regs[MMOV_MAX_REGS];
+ size_t regs_read = 0, i;
+ unsigned int mask = 0;
+
+ l = parse_gp_regs_list (l, regs, 1, &regs_read);
+
+ /* First register must be a port. */
+ if (l == NULL || regs[0]->unit != UNIT_RD)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_gp_regs_list (l, ra_regs, MMOV_MAX_REGS, &regs_read);
+
+ if (l == NULL)
+ return NULL;
+
+ /* Check remaining registers match the first.
+
+ Note that we also accept RA (0x10) as input for the remaining registers.
+ Whilst this doesn't represent the instruction in any way we're stuck
+ with it because the embedded assembler accepts it. */
+ for (i = 0; i < regs_read; i++)
+ {
+ if (ra_regs[i]->unit != UNIT_RD ||
+ (ra_regs[i]->no != 0x10 && ra_regs[i]->no != regs[0]->no))
+ return NULL;
+
+ mask = (mask << 1) | 0x1;
+ }
+
+ *rmask = mask;
+
+ return l;
+}
+
+/* Parse a MMOV instruction. */
+static const char *
+parse_mmov (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ unsigned int is_fpu = template->insn_type == INSN_FPU;
+ unsigned int is_prime = ((MINOR_OPCODE (template->meta_opcode) & 0x2) &&
+ !is_fpu);
+ unsigned int is_64bit = MINOR_OPCODE (template->meta_opcode) & 0x1;
+ unsigned int rmask = 0;
+
+ if (is_prime)
+ {
+ const metag_reg *reg;
+ metag_addr addr;
+
+ memset (&addr, 0, sizeof(addr));
+
+ l = parse_mmov_prime_list (l, &reg, &rmask);
+
+ if (l == NULL)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_mget_mset_addr (l, &addr);
+
+ if (l == NULL)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (reg->no << 19) |
+ (addr.base_reg->no << 14) |
+ ((rmask & RMASK_MASK) << 7) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5));
+ }
+ else
+ {
+ const metag_reg *regs[MMOV_MAX_REGS + 1];
+ unsigned int lowest_reg = 0xffffffff;
+ size_t regs_read = 0;
+
+ l = parse_gp_regs_list (l, regs, MMOV_MAX_REGS + 1, &regs_read);
+
+ if (l == NULL || regs_read == 0)
+ return NULL;
+
+ if (!is_short_unit (regs[0]->unit) &&
+ !(is_fpu && regs[0]->unit == UNIT_FX))
+ {
+ return NULL;
+ }
+
+ if (!(regs[regs_read-1]->unit == UNIT_RD &&
+ regs[regs_read-1]->no == 0))
+ {
+ return NULL;
+ }
+
+ if (!check_rmask (regs, regs_read - 1, is_fpu, is_64bit, &lowest_reg,
+ &rmask))
+ return NULL;
+
+ if (is_fpu)
+ {
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 14) |
+ ((rmask & RMASK_MASK) << 7));
+ }
+ else
+ {
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ ((rmask & RMASK_MASK) << 7) |
+ ((regs[0]->unit & SHORT_UNIT_MASK) << 3));
+ }
+ }
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an immediate constant. */
+static const char *
+parse_imm_constant (const char *line, metag_insn *insn, int *value)
+{
+ const char *l = line;
+ char *save_input_line_pointer;
+ expressionS *exp = &insn->reloc_exp;
+
+ /* Skip #. */
+ if (*l == '#')
+ l++;
+ else
+ return NULL;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = (char *) l;
+
+ expression (exp);
+
+ l = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_constant)
+ {
+ *value = exp->X_add_number;
+
+ return l;
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/* Parse an MDRD instruction. */
+static const char *
+parse_mdrd (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ unsigned int rmask = 0;
+ int value = 0, i;
+
+ l = parse_imm_constant (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (value < 1 || value > 8)
+ {
+ as_bad (_("MDRD value must be between 1 and 8"));
+ return NULL;
+ }
+
+ for (i = 1; i < value; i++)
+ {
+ rmask <<= 1;
+ rmask |= 1;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (rmask << 7));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a conditional SET instruction. */
+static const char *
+parse_cond_set (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+ metag_addr addr;
+ unsigned int size = metag_cond_set_size_bytes (template->meta_opcode);
+ unsigned int reg_no;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ l = parse_set (l, regs, &addr, size);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->unit == UNIT_RD)
+ {
+ if (regs[0]->no != 0)
+ {
+ as_bad (_("set can only use RD port as source"));
+ return NULL;
+ }
+ reg_no = 16;
+ }
+ else
+ reg_no = regs[0]->no;
+
+ if (addr.update)
+ return NULL;
+
+ if (!(addr.immediate &&
+ addr.exp.X_add_number == 0))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (reg_no << 19) |
+ (regs[0]->unit << 10));
+
+ if (!is_short_unit (addr.base_reg->unit))
+ {
+ as_bad (_("base unit must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ insn->bits |= ((addr.base_reg->no << 14) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an XFR instruction. */
+static const char *
+parse_xfr (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ metag_addr dest_addr, src_addr;
+ unsigned int size = 4;
+
+ memset(&dest_addr, 0, sizeof(dest_addr));
+ memset(&src_addr, 0, sizeof(src_addr));
+ dest_addr.reloc_type = BFD_RELOC_UNUSED;
+ src_addr.reloc_type = BFD_RELOC_UNUSED;
+
+ l = parse_addr (l, &dest_addr, size);
+
+ if (l == NULL ||
+ dest_addr.immediate == 1)
+ {
+ as_bad (_("invalid destination memory operand"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_addr (l, &src_addr, size);
+
+ if (l == NULL ||
+ src_addr.immediate == 1)
+ {
+ as_bad (_("invalid source memory operand"));
+ return NULL;
+ }
+
+ if (!is_short_unit (dest_addr.base_reg->unit) ||
+ !is_short_unit (src_addr.base_reg->unit))
+ {
+ as_bad (_("address units must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ if ((dest_addr.base_reg->unit != dest_addr.offset_reg->unit) ||
+ (src_addr.base_reg->unit != src_addr.offset_reg->unit))
+ {
+ as_bad (_("base and offset must be from the same unit"));
+ return NULL;
+ }
+
+ if (dest_addr.update == 1 &&
+ src_addr.update == 1 &&
+ dest_addr.post_increment != src_addr.post_increment)
+ {
+ as_bad (_("source and destination increment mode must agree"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (src_addr.base_reg->no << 19) |
+ (src_addr.offset_reg->no << 14) |
+ ((src_addr.base_reg->unit & SHORT_UNIT_MASK) << 2));
+
+ insn->bits |= ((dest_addr.base_reg->no << 9) |
+ (dest_addr.offset_reg->no << 4) |
+ ((dest_addr.base_reg->unit & SHORT_UNIT_MASK)));
+
+ if (dest_addr.update == 1)
+ insn->bits |= (1 << 26);
+
+ if (src_addr.update == 1)
+ insn->bits |= (1 << 27);
+
+ if (dest_addr.post_increment == 1 ||
+ src_addr.post_increment == 1)
+ insn->bits |= (1 << 24);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an 8bit immediate value. */
+static const char *
+parse_imm8 (const char *line, metag_insn *insn, int *value)
+{
+ const char *l = line;
+ char *save_input_line_pointer;
+ expressionS *exp = &insn->reloc_exp;
+
+ /* Skip #. */
+ if (*l == '#')
+ l++;
+ else
+ return NULL;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = (char *) l;
+
+ expression (exp);
+
+ l = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_absent || exp->X_op == O_big)
+ {
+ return NULL;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ *value = exp->X_add_number;
+ }
+ else
+ {
+ insn->reloc_type = BFD_RELOC_METAG_REL8;
+ insn->reloc_pcrel = 0;
+ }
+
+ return l;
+}
+
+/* Parse a 16bit immediate value. */
+static const char *
+parse_imm16 (const char *line, metag_insn *insn, int *value)
+{
+ const char *l = line;
+ char *save_input_line_pointer;
+ expressionS *exp = &insn->reloc_exp;
+ bfd_boolean is_hi = FALSE;
+ bfd_boolean is_lo = FALSE;
+
+ /* Skip #. */
+ if (*l == '#')
+ l++;
+ else
+ return NULL;
+
+ if (strncasecmp (l, "HI", 2) == 0)
+ {
+ is_hi = TRUE;
+ l += 2;
+ }
+ else if (strncasecmp (l, "LO", 2) == 0)
+ {
+ is_lo = TRUE;
+ l += 2;
+ }
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = (char *) l;
+
+ expression (exp);
+
+ l = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_absent || exp->X_op == O_big)
+ {
+ return NULL;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ if (is_hi)
+ *value = (exp->X_add_number >> 16) & IMM16_MASK;
+ else if (is_lo)
+ *value = exp->X_add_number & IMM16_MASK;
+ else
+ *value = exp->X_add_number;
+ }
+ else
+ {
+ if (exp->X_op == O_PIC_reloc)
+ {
+ exp->X_op = O_symbol;
+
+ if (exp->X_md == BFD_RELOC_METAG_GOTOFF)
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_HI16_GOTOFF;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_LO16_GOTOFF;
+ else
+ return NULL;
+ }
+ else if (exp->X_md == BFD_RELOC_METAG_PLT)
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_HI16_PLT;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_LO16_PLT;
+ else
+ return NULL;
+ }
+ else if (exp->X_md == BFD_RELOC_METAG_TLS_LDO)
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_TLS_LDO_HI16;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_TLS_LDO_LO16;
+ else
+ return NULL;
+ }
+ else if (exp->X_md == BFD_RELOC_METAG_TLS_IENONPIC)
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_TLS_IENONPIC_HI16;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_TLS_IENONPIC_LO16;
+ else
+ return NULL;
+ }
+ else if (exp->X_md == BFD_RELOC_METAG_TLS_LE)
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_TLS_LE_HI16;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_TLS_LE_LO16;
+ else
+ return NULL;
+ }
+ else if (exp->X_md == BFD_RELOC_METAG_TLS_GD ||
+ exp->X_md == BFD_RELOC_METAG_TLS_LDM)
+ insn->reloc_type = exp->X_md;
+ }
+ else
+ {
+ if (exp->X_op == O_symbol && exp->X_add_symbol == GOT_symbol)
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_HI16_GOTPC;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_LO16_GOTPC;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (is_hi)
+ insn->reloc_type = BFD_RELOC_METAG_HIADDR16;
+ else if (is_lo)
+ insn->reloc_type = BFD_RELOC_METAG_LOADDR16;
+ else
+ insn->reloc_type = BFD_RELOC_METAG_REL16;
+ }
+ }
+
+ insn->reloc_pcrel = 0;
+ }
+
+ return l;
+}
+
+/* Parse a MOV to control unit instruction. */
+static const char *
+parse_mov_ct (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[1];
+ unsigned int top = template->meta_opcode & 0x1;
+ unsigned int is_trace = (template->meta_opcode >> 2) & 0x1;
+ unsigned int sign_extend = 0;
+ int value = 0;
+
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (is_trace)
+ {
+ if (regs[0]->unit != UNIT_TT)
+ return NULL;
+ }
+ else
+ {
+ if (regs[0]->unit != UNIT_CT)
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_imm16 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (value < 0)
+ sign_extend = 1;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ ((value & IMM16_MASK) << 3));
+
+ if (sign_extend == 1 && top == 0)
+ insn->bits |= (1 << 1);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a SWAP instruction. */
+static const char *
+parse_swap (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+
+ l = parse_gp_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ /* PC.r | CT.r | TR.r | TT.r are treated as if they are a single unit. */
+ switch (regs[0]->unit)
+ {
+ case UNIT_PC:
+ case UNIT_CT:
+ case UNIT_TR:
+ case UNIT_TT:
+ if (regs[1]->unit == UNIT_PC
+ || regs[1]->unit == UNIT_CT
+ || regs[1]->unit == UNIT_TR
+ || regs[1]->unit == UNIT_TT)
+ {
+ as_bad (_("PC, CT, TR and TT are treated as if they are a single unit but operands must be in different units"));
+ return NULL;
+ }
+
+ default:
+ /* Registers must be in different units. */
+ if (regs[0]->unit == regs[1]->unit)
+ {
+ as_bad (_("source and destination register must be in different units"));
+ return NULL;
+ }
+ break;
+ }
+
+ insn->bits = (template->meta_opcode
+ | (regs[1]->no << 19)
+ | (regs[0]->no << 14)
+ | (regs[1]->unit << 10)
+ | (regs[0]->unit << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a JUMP instruction. */
+static const char *
+parse_jump (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[1];
+ int value = 0;
+
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!is_short_unit (regs[0]->unit))
+ {
+ as_bad (_("register unit must be one of %s"), SHORT_UNITS);
+ return FALSE;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_imm16 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[0]->unit & SHORT_UNIT_MASK) |
+ ((value & IMM16_MASK) << 3));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a 19bit immediate value. */
+static const char *
+parse_imm19 (const char *line, metag_insn *insn, int *value)
+{
+ const char *l = line;
+ char *save_input_line_pointer;
+ expressionS *exp = &insn->reloc_exp;
+
+ /* Skip #. */
+ if (*l == '#')
+ l++;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = (char *) l;
+
+ expression (exp);
+
+ l = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+
+ if (exp->X_op == O_absent || exp->X_op == O_big)
+ {
+ return NULL;
+ }
+ else if (exp->X_op == O_constant)
+ {
+ *value = exp->X_add_number;
+ }
+ else
+ {
+ if (exp->X_op == O_PIC_reloc)
+ {
+ exp->X_op = O_symbol;
+
+ if (exp->X_md == BFD_RELOC_METAG_PLT)
+ insn->reloc_type = BFD_RELOC_METAG_RELBRANCH_PLT;
+ else
+ return NULL;
+ }
+ else
+ insn->reloc_type = BFD_RELOC_METAG_RELBRANCH;
+ insn->reloc_pcrel = 1;
+ }
+
+ return l;
+}
+
+/* Parse a CALLR instruction. */
+static const char *
+parse_callr (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[1];
+ int value = 0;
+
+ l = parse_gp_regs (l, regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!is_short_unit (regs[0]->unit))
+ {
+ as_bad (_("link register unit must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ if (regs[0]->no & ~CALLR_REG_MASK)
+ {
+ as_bad (_("link register must be in a low numbered register"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_imm19 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_signed_range (value / 4, IMM19_BITS))
+ {
+ as_bad (_("target out of range"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no & CALLR_REG_MASK) |
+ ((regs[0]->unit & SHORT_UNIT_MASK) << 3) |
+ ((value & IMM19_MASK) << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Return the value for the register field if we apply the O2R modifier
+ to operand 2 REG, combined with UNIT_BIT derived from the destination
+ register or source1. Uses address unit O2R if IS_ADDR is set. */
+static int
+lookup_o2r (unsigned int is_addr, unsigned int unit_bit, const metag_reg *reg)
+{
+ if (reg->no & ~O2R_REG_MASK)
+ return -1;
+
+ if (is_addr)
+ {
+ if (unit_bit)
+ {
+ switch (reg->unit)
+ {
+ case UNIT_D1:
+ return reg->no;
+ case UNIT_D0:
+ return (1 << 3) | reg->no;
+ case UNIT_RD:
+ return (2 << 3) | reg->no;
+ case UNIT_A0:
+ return (3 << 3) | reg->no;
+ default:
+ return -1;
+ }
+ }
+ else
+ {
+ switch (reg->unit)
+ {
+ case UNIT_A1:
+ return reg->no;
+ case UNIT_D0:
+ return (1 << 3) | reg->no;
+ case UNIT_RD:
+ return (2 << 3) | reg->no;
+ case UNIT_D1:
+ return (3 << 3) | reg->no;
+ default:
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ if (unit_bit)
+ {
+ switch (reg->unit)
+ {
+ case UNIT_A1:
+ return reg->no;
+ case UNIT_D0:
+ return (1 << 3) | reg->no;
+ case UNIT_RD:
+ return (2 << 3) | reg->no;
+ case UNIT_A0:
+ return (3 << 3) | reg->no;
+ default:
+ return -1;
+ }
+ }
+ else
+ {
+ switch (reg->unit)
+ {
+ case UNIT_A1:
+ return reg->no;
+ case UNIT_D1:
+ return (1 << 3) | reg->no;
+ case UNIT_RD:
+ return (2 << 3) | reg->no;
+ case UNIT_A0:
+ return (3 << 3) | reg->no;
+ default:
+ return -1;
+ }
+ }
+ }
+}
+
+/* Parse GP ALU instruction. */
+static const char *
+parse_alu (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *dest_regs[1];
+ const metag_reg *src_regs[2];
+ int value = 0;
+ unsigned int o1z = 0;
+ unsigned int imm = (template->meta_opcode >> 25) & 0x1;
+ unsigned int cond = (template->meta_opcode >> 26) & 0x1;
+ unsigned int ca = (template->meta_opcode >> 5) & 0x1;
+ unsigned int top = template->meta_opcode & 0x1;
+ unsigned int sign_extend = 0;
+ unsigned int is_addr_op = MAJOR_OPCODE (template->meta_opcode) == OPC_ADDR;
+ unsigned int is_mul = MAJOR_OPCODE (template->meta_opcode) == OPC_MUL;
+ unsigned int unit_bit = 0;
+ bfd_boolean is_quickrot = template->arg_type & GP_ARGS_QR;
+
+ l = parse_gp_regs (l, dest_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ if (is_addr_op)
+ {
+ if (dest_regs[0]->unit == UNIT_A0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_A1)
+ unit_bit = 1;
+ }
+ else
+ {
+ if (dest_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ }
+
+ if ((MAJOR_OPCODE (template->meta_opcode) == OPC_ADDR ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_ADD ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_SUB) &&
+ ((template->meta_opcode >> 2) & 0x1))
+ o1z = 1;
+
+ if (imm)
+ {
+ if (!cond)
+ {
+ if (is_addr_op)
+ {
+ if (dest_regs[0]->unit == UNIT_A0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_A1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (dest_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ }
+
+ if (cond)
+ {
+ l = parse_gp_regs (l, src_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ if (is_addr_op)
+ {
+ if (src_regs[0]->unit == UNIT_A0)
+ unit_bit = 0;
+ else if (src_regs[0]->unit == UNIT_A1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (src_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (src_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+
+ if (src_regs[0]->unit != dest_regs[0]->unit && !ca)
+ return NULL;
+
+ l = parse_imm8 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_unsigned_range (value, IMM8_BITS))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (src_regs[0]->no << 14) |
+ ((value & IMM8_MASK) << 6));
+
+ if (ca)
+ {
+ if (is_addr_op)
+ {
+ if (src_regs[0]->unit == UNIT_A0)
+ unit_bit = 0;
+ else if (src_regs[0]->unit == UNIT_A1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (src_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (src_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+
+ insn->bits |= dest_regs[0]->unit << 1;
+ }
+ }
+ else if (o1z)
+ {
+ l = parse_imm16 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (value < 0)
+ {
+ if (!within_signed_range (value, IMM16_BITS))
+ {
+ as_bad (_("immediate out of range"));
+ return NULL;
+ }
+ sign_extend = 1;
+ }
+ else
+ {
+ if (!within_unsigned_range (value, IMM16_BITS))
+ {
+ as_bad (_("immediate out of range"));
+ return NULL;
+ }
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ ((value & IMM16_MASK) << 3));
+ }
+ else
+ {
+ l = parse_gp_regs (l, src_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!(src_regs[0]->unit == dest_regs[0]->unit))
+ return NULL;
+
+ /* CPC is valid for address ops. */
+ if (src_regs[0]->no != dest_regs[0]->no &&
+ !(is_addr_op && src_regs[0]->no == 0x10))
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_imm16 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (value < 0)
+ {
+ if (!within_signed_range (value, IMM16_BITS))
+ {
+ as_bad (_("immediate out of range"));
+ return NULL;
+ }
+ sign_extend = 1;
+ }
+ else
+ {
+ if (!within_unsigned_range (value, IMM16_BITS))
+ {
+ as_bad (_("immediate out of range"));
+ return NULL;
+ }
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (src_regs[0]->no << 19) |
+ ((value & IMM16_MASK) << 3));
+ }
+ }
+ else
+ {
+ unsigned int o2r = 0;
+ int rs2;
+
+ if (cond || !o1z)
+ l = parse_gp_regs (l, src_regs, 2);
+ else
+ l = parse_gp_regs (l, src_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (cond || !o1z)
+ {
+ if (is_addr_op)
+ {
+ if (src_regs[0]->unit == UNIT_A0)
+ unit_bit = 0;
+ else if (src_regs[0]->unit == UNIT_A1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (src_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (src_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ }
+ else
+ {
+ if (is_addr_op)
+ {
+ if (dest_regs[0]->unit == UNIT_A0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_A1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (dest_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+ }
+ }
+
+ if (cond)
+ {
+ if (src_regs[0]->unit != src_regs[1]->unit)
+ {
+ rs2 = lookup_o2r (is_addr_op, unit_bit, src_regs[1]);
+
+ if (rs2 < 0)
+ return NULL;
+
+ o2r = 1;
+ }
+ else
+ {
+ rs2 = src_regs[1]->no;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (src_regs[0]->no << 14) |
+ (rs2 << 9));
+
+ if (is_mul)
+ {
+ if (dest_regs[0]->unit != src_regs[0]->unit && is_mul)
+ {
+ if (ca)
+ {
+ insn->bits |= dest_regs[0]->unit << 1;
+ }
+ else
+ return NULL;
+ }
+ }
+ else
+ insn->bits |= dest_regs[0]->unit << 5;
+ }
+ else if (o1z)
+ {
+ if (dest_regs[0]->unit != src_regs[0]->unit)
+ {
+ rs2 = lookup_o2r (is_addr_op, unit_bit, src_regs[0]);
+
+ if (rs2 < 0)
+ return NULL;
+
+ o2r = 1;
+ }
+ else
+ {
+ rs2 = src_regs[0]->no;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (rs2 << 9));
+ }
+ else
+ {
+ if (dest_regs[0]->unit != src_regs[0]->unit)
+ return NULL;
+
+ if (dest_regs[0]->unit != src_regs[1]->unit)
+ {
+ rs2 = lookup_o2r (is_addr_op, unit_bit, src_regs[1]);
+
+ if (rs2 < 0)
+ return NULL;
+
+ o2r = 1;
+ }
+ else
+ {
+ rs2 = src_regs[1]->no;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (src_regs[0]->no << 14) |
+ (rs2 << 9));
+ }
+
+ if (o2r)
+ insn->bits |= 1;
+ }
+
+ if (is_quickrot)
+ {
+ const metag_reg *qr_regs[1];
+ bfd_boolean limit_regs = imm && cond;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_gp_regs (l, qr_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!((unit_bit == 0 && qr_regs[0]->unit != UNIT_A0) ||
+ !(unit_bit == 1 && qr_regs[0]->unit != UNIT_A1)))
+ {
+ as_bad (_("invalid quickrot unit specified"));
+ return NULL;
+ }
+
+ switch (qr_regs[0]->no)
+ {
+ case 2:
+ break;
+ case 3:
+ if (!limit_regs)
+ {
+ insn->bits |= (1 << 7);
+ break;
+ }
+ default:
+ as_bad (_("invalid quickrot register specified"));
+ return NULL;
+ }
+ }
+
+ if (sign_extend == 1 && top == 0)
+ insn->bits |= (1 << 1);
+
+ insn->bits |= unit_bit << 24;
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a B instruction. */
+static const char *
+parse_branch (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ int value = 0;
+
+ l = parse_imm19 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_signed_range (value / 4, IMM19_BITS))
+ {
+ as_bad (_("target out of range"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ ((value & IMM19_MASK) << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a KICK instruction. */
+static const char *
+parse_kick (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+
+ l = parse_gp_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[1]->unit != UNIT_TR)
+ {
+ as_bad (_("source register must be in the trigger unit"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[1]->no << 19) |
+ (regs[0]->no << 14) |
+ (regs[0]->unit << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a SWITCH instruction. */
+static const char *
+parse_switch (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ int value = 0;
+
+ l = parse_imm_constant (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_unsigned_range (value, IMM24_BITS))
+ {
+ as_bad (_("target out of range"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (value & IMM24_MASK));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a shift instruction. */
+static const char *
+parse_shift (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+ const metag_reg *src2_regs[1];
+ int value = 0;
+ unsigned int cond = (template->meta_opcode >> 26) & 0x1;
+ unsigned int ca = (template->meta_opcode >> 5) & 0x1;
+ unsigned int unit_bit = 0;
+
+ l = parse_gp_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ if (regs[1]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (regs[1]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+
+ if (regs[0]->unit != regs[1]->unit && !(cond && ca))
+ return NULL;
+
+ if (*l == '#')
+ {
+ l = parse_imm_constant (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_unsigned_range (value, IMM5_BITS))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (1 << 25) |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ ((value & IMM5_MASK) << 9));
+ }
+ else
+ {
+ l = parse_gp_regs (l, src2_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ (src2_regs[0]->no << 9));
+
+ if (src2_regs[0]->unit != regs[1]->unit)
+ {
+ as_bad(_("Source registers must be in the same unit"));
+ return NULL;
+ }
+ }
+
+ if (regs[0]->unit != regs[1]->unit)
+ {
+ if (cond && ca)
+ {
+ if (regs[1]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (regs[1]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+
+ insn->bits |= ((1 << 5) |
+ (regs[0]->unit << 1));
+ }
+ else
+ return NULL;
+ }
+
+ insn->bits |= unit_bit << 24;
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a MIN or MAX instruction. */
+static const char *
+parse_min_max (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[3];
+
+ l = parse_gp_regs (l, regs, 3);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!(regs[0]->unit == UNIT_D0 ||
+ regs[0]->unit == UNIT_D1))
+ return NULL;
+
+ if (!(regs[0]->unit == regs[1]->unit &&
+ regs[1]->unit == regs[2]->unit))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ (regs[2]->no << 9));
+
+ if (regs[0]->unit == UNIT_D1)
+ insn->bits |= (1 << 24);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a bit operation instruction. */
+static const char *
+parse_bitop (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+ unsigned int swap_inst = MAJOR_OPCODE (template->meta_opcode) == OPC_MISC;
+ unsigned int is_bexl = 0;
+
+ if (swap_inst &&
+ ((template->meta_opcode >> 1) & 0xb) == 0xa)
+ is_bexl = 1;
+
+ l = parse_gp_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!(regs[0]->unit == UNIT_D0 ||
+ regs[0]->unit == UNIT_D1))
+ return NULL;
+
+ if (is_bexl)
+ {
+ if (regs[0]->unit == UNIT_D0 &&
+ regs[1]->unit != UNIT_D1)
+ return NULL;
+ else if (regs[0]->unit == UNIT_D1 &&
+ regs[1]->unit != UNIT_D0)
+ return NULL;
+ }
+ else if (!(regs[0]->unit == regs[1]->unit))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14));
+
+ if (swap_inst)
+ {
+ if (regs[1]->unit == UNIT_D1)
+ insn->bits |= 1;
+ }
+ else
+ {
+ if (regs[1]->unit == UNIT_D1)
+ insn->bits |= (1 << 24);
+ }
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a CMP or TST instruction. */
+static const char *
+parse_cmp (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *dest_regs[1];
+ const metag_reg *src_regs[1];
+ int value = 0;
+ unsigned int imm = (template->meta_opcode >> 25) & 0x1;
+ unsigned int cond = (template->meta_opcode >> 26) & 0x1;
+ unsigned int top = template->meta_opcode & 0x1;
+ unsigned int sign_extend = 0;
+ unsigned int unit_bit = 0;
+
+ l = parse_gp_regs (l, dest_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ if (dest_regs[0]->unit == UNIT_D0)
+ unit_bit = 0;
+ else if (dest_regs[0]->unit == UNIT_D1)
+ unit_bit = 1;
+ else
+ return NULL;
+
+ if (imm)
+ {
+ if (cond)
+ {
+ l = parse_imm_constant (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_unsigned_range (value, IMM8_BITS))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 14) |
+ ((value & IMM8_MASK) << 6));
+
+ }
+ else
+ {
+ l = parse_imm16 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ if (value < 0)
+ {
+ if (!within_signed_range (value, IMM16_BITS))
+ {
+ as_bad (_("immediate out of range"));
+ return NULL;
+ }
+ sign_extend = 1;
+ }
+ else
+ {
+ if (!within_unsigned_range (value, IMM16_BITS))
+ {
+ as_bad (_("immediate out of range"));
+ return NULL;
+ }
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ ((value & IMM16_MASK) << 3));
+ }
+ }
+ else
+ {
+ unsigned int o2r = 0;
+ int rs2;
+
+ l = parse_gp_regs (l, src_regs, 1);
+
+ if (l == NULL)
+ return NULL;
+
+ if (dest_regs[0]->unit != src_regs[0]->unit)
+ {
+ rs2 = lookup_o2r (0, unit_bit, src_regs[0]);
+
+ if (rs2 < 0)
+ return NULL;
+
+ o2r = 1;
+ }
+ else
+ {
+ rs2 = src_regs[0]->no;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 14) |
+ (rs2 << 9));
+
+ if (o2r)
+ insn->bits |= 1;
+ }
+
+ if (sign_extend == 1 && top == 0)
+ insn->bits |= (1 << 1);
+
+ insn->bits |= unit_bit << 24;
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a CACHEW instruction. */
+static const char *
+parse_cachew (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *src_regs[2];
+ unsigned int size = ((template->meta_opcode >> 1) & 0x1) ? 8 : 4;
+ metag_addr addr;
+ int offset;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ l = parse_addr (l, &addr, size);
+
+ if (l == NULL ||
+ !is_short_unit (addr.base_reg->unit) ||
+ addr.update ||
+ !addr.immediate)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ if (size == 4)
+ l = parse_gp_regs (l, src_regs, 1);
+ else
+ l = parse_pair_gp_regs (l, src_regs);
+
+ if (l == NULL ||
+ !is_short_unit (src_regs[0]->unit))
+ {
+ as_bad (_("invalid source register"));
+ return NULL;
+ }
+
+ offset = addr.exp.X_add_number;
+
+ if (addr.negate)
+ offset = -offset;
+
+ offset = offset / 64;
+
+ if (!within_signed_range (offset, GET_SET_IMM_BITS))
+ {
+ as_bad (_("offset value out of range"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (src_regs[0]->no << 19) |
+ (addr.base_reg->no << 14) |
+ ((offset & GET_SET_IMM_MASK) << 8) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5) |
+ ((src_regs[0]->unit & SHORT_UNIT_MASK) << 3));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a CACHEW instruction. */
+static const char *
+parse_cacher (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *dest_regs[2];
+ unsigned int size = ((template->meta_opcode >> 1) & 0x1) ? 8 : 4;
+ metag_addr addr;
+ int offset;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ if (size == 4)
+ l = parse_gp_regs (l, dest_regs, 1);
+ else
+ l = parse_pair_gp_regs (l, dest_regs);
+
+ if (l == NULL ||
+ !is_short_unit (dest_regs[0]->unit))
+ {
+ as_bad (_("invalid destination register"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_addr (l, &addr, size);
+
+ if (l == NULL ||
+ !is_short_unit (addr.base_reg->unit) ||
+ addr.update ||
+ !addr.immediate)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ offset = addr.exp.X_add_number;
+
+ if (addr.negate)
+ offset = -offset;
+
+ offset = offset / (int)size;
+
+ if (!within_signed_range (offset, GET_SET_IMM_BITS))
+ {
+ as_bad (_("offset value out of range"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (addr.base_reg->no << 14) |
+ ((offset & GET_SET_IMM_MASK) << 8) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5) |
+ ((dest_regs[0]->unit & SHORT_UNIT_MASK) << 3));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an ICACHE instruction. */
+static const char *
+parse_icache (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ int offset;
+ int pfcount;
+
+ l = parse_imm_constant (l, insn, &offset);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_signed_range (offset, IMM15_BITS))
+ return NULL;
+
+ l = skip_comma (l);
+
+ l = parse_imm_constant (l, insn, &pfcount);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!within_unsigned_range (pfcount, IMM4_BITS))
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ ((offset & IMM15_MASK) << 9) |
+ ((pfcount & IMM4_MASK) << 1));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a LNKGET instruction. */
+static const char *
+parse_lnkget (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *dest_regs[2];
+ unsigned int size = metag_get_set_ext_size_bytes (template->meta_opcode);
+ metag_addr addr;
+ int offset;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ if (size == 8)
+ l = parse_pair_gp_regs (l, dest_regs);
+ else
+ l = parse_gp_regs (l, dest_regs, 1);
+
+ if (l == NULL ||
+ !is_short_unit (dest_regs[0]->unit))
+ {
+ as_bad (_("invalid destination register"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_addr (l, &addr, size);
+
+ if (l == NULL ||
+ !is_short_unit (addr.base_reg->unit) ||
+ addr.update ||
+ !addr.immediate)
+ {
+ as_bad (_("invalid memory operand"));
+ return NULL;
+ }
+
+ offset = addr.exp.X_add_number;
+
+ if (addr.negate)
+ offset = -offset;
+
+ offset = offset / size;
+
+ if (!within_signed_range (offset, GET_SET_IMM_BITS))
+ {
+ as_bad (_("offset value out of range"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (dest_regs[0]->no << 19) |
+ (addr.base_reg->no << 14) |
+ ((offset & GET_SET_IMM_MASK) << 8) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5) |
+ ((dest_regs[0]->unit & SHORT_UNIT_MASK) << 3));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU MOV instruction. */
+static const char *
+parse_fmov (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+
+ l = parse_fpu_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14));
+
+ if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 5);
+ else if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU MMOV instruction. */
+static const char *
+parse_fmmov (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ bfd_boolean to_fpu = MAJOR_OPCODE (template->meta_opcode) == OPC_GET;
+ bfd_boolean is_mmovl = MINOR_OPCODE (template->meta_opcode) & 0x1;
+ size_t regs_read = 0;
+ const metag_reg *regs[16];
+ unsigned int lowest_data_reg = 0xffffffff;
+ unsigned int lowest_fpu_reg = 0xffffffff;
+ unsigned int rmask = 0, data_unit;
+ size_t i;
+ int last_reg = -1;
+
+ if (insn->fpu_width != FPU_WIDTH_SINGLE)
+ return NULL;
+
+ l = parse_gp_regs_list (l, regs, 16, &regs_read);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs_read % 2)
+ return NULL;
+
+ if (to_fpu)
+ {
+ for (i = 0; i < regs_read / 2; i++)
+ {
+ if (regs[i]->unit != UNIT_FX)
+ return NULL;
+
+ if (last_reg == -1)
+ {
+ last_reg = regs[i]->no;
+ lowest_fpu_reg = last_reg;
+ }
+ else
+ {
+ if (is_mmovl)
+ {
+ if (regs[i]->no != (unsigned int)(last_reg + 2))
+ return NULL;
+ }
+ else if (regs[i]->no != (unsigned int)(last_reg + 1))
+ return NULL;
+
+ last_reg = regs[i]->no;
+ }
+ }
+
+ if (regs[i]->unit == UNIT_D0)
+ data_unit = 0;
+ else if (regs[i]->unit == UNIT_D1)
+ data_unit = 1;
+ else
+ return NULL;
+
+ if (!check_rmask (&regs[i], regs_read / 2, TRUE, FALSE, &lowest_data_reg,
+ &rmask))
+ return NULL;
+ }
+ else
+ {
+ if (regs[0]->unit == UNIT_D0)
+ data_unit = 0;
+ else if (regs[0]->unit == UNIT_D1)
+ data_unit = 1;
+ else
+ return NULL;
+
+ if (!check_rmask (regs, regs_read / 2, TRUE, FALSE, &lowest_data_reg,
+ &rmask))
+ return NULL;
+
+ for (i = regs_read / 2; i < regs_read; i++)
+ {
+ if (regs[i]->unit != UNIT_FX)
+ return NULL;
+
+ if (last_reg == -1)
+ {
+ last_reg = regs[i]->no;
+ lowest_fpu_reg = last_reg;
+ }
+ else
+ {
+ if (is_mmovl)
+ {
+ if (regs[i]->no != (unsigned int)(last_reg + 2))
+ return NULL;
+ }
+ else if (regs[i]->no != (unsigned int)(last_reg + 1))
+ return NULL;
+
+ last_reg = regs[i]->no;
+ }
+ }
+ }
+
+ insn->bits = (template->meta_opcode |
+ ((lowest_data_reg & REG_MASK) << 19) |
+ ((lowest_fpu_reg & REG_MASK) << 14) |
+ ((rmask & RMASK_MASK) << 7) |
+ data_unit);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU data unit MOV instruction. */
+static const char *
+parse_fmov_data (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ unsigned int to_fpu = ((template->meta_opcode >> 7) & 0x1);
+ const metag_reg *regs[2];
+ unsigned int base_unit;
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ return NULL;
+
+ l = parse_gp_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ if (to_fpu)
+ {
+ if (regs[0]->unit != UNIT_FX)
+ return NULL;
+
+ if (regs[1]->unit == UNIT_D0)
+ base_unit = 0;
+ else if (regs[1]->unit == UNIT_D1)
+ base_unit = 1;
+ else
+ return NULL;
+ }
+ else
+ {
+ if (regs[0]->unit == UNIT_D0)
+ base_unit = 0;
+ else if (regs[0]->unit == UNIT_D1)
+ base_unit = 1;
+ else
+ return NULL;
+
+ if (regs[1]->unit != UNIT_FX)
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (base_unit << 24) |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 9));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU immediate MOV instruction. */
+static const char *
+parse_fmov_i (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[1];
+ int value = 0;
+
+ l = parse_fpu_regs (l, regs, 1);
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_imm16 (l, insn, &value);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ ((value & IMM16_MASK) << 3));
+
+ if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 1);
+ else if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 2);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU PACK instruction. */
+static const char *
+parse_fpack (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[3];
+
+ l = parse_fpu_regs (l, regs, 3);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->no % 2)
+ {
+ as_bad (_("destination register should be even numbered"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ (regs[2]->no << 9));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU SWAP instruction. */
+static const char *
+parse_fswap (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+
+ if (insn->fpu_width != FPU_WIDTH_PAIR)
+ return NULL;
+
+ l = parse_fpu_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->no % 2)
+ return NULL;
+
+ if (regs[1]->no % 2)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU CMP instruction. */
+static const char *
+parse_fcmp (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line, *l2;
+ const metag_reg *regs1[1];
+ const metag_reg *regs2[1];
+
+ l = parse_fpu_regs (l, regs1, 1);
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l2 = parse_fpu_regs (l, regs2, 1);
+
+ if (l2 != NULL)
+ {
+ insn->bits = (regs2[0]->no << 9);
+ }
+ else
+ {
+ int constant = 0;
+ l2 = parse_imm_constant (l, insn, &constant);
+ if (!l2 || constant != 0)
+ {
+ as_bad (_("comparison must be with register or #0"));
+ return NULL;
+ }
+ insn->bits = (1 << 8);
+ }
+
+ insn->bits |= (template->meta_opcode |
+ (regs1[0]->no << 14));
+
+ if (insn->fpu_action_flags & FPU_ACTION_ABS)
+ insn->bits |= (1 << 19);
+
+ if (insn->fpu_action_flags & FPU_ACTION_QUIET)
+ insn->bits |= (1 << 7);
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+ else if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 5);
+
+ insn->len = 4;
+ return l2;
+}
+
+/* Parse an FPU MIN or MAX instruction. */
+static const char *
+parse_fminmax (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[3];
+
+ l = parse_fpu_regs (l, regs, 3);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ (regs[2]->no << 9));
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+ else if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 5);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU data conversion instruction. */
+static const char *
+parse_fconv (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ {
+ if (strncasecmp (template->name, "FTOH", 4) &&
+ strncasecmp (template->name, "HTOF", 4) &&
+ strncasecmp (template->name, "FTOI", 4) &&
+ strncasecmp (template->name, "ITOF", 4))
+ {
+ as_bad (_("instruction cannot operate on pair values"));
+ return NULL;
+ }
+ }
+
+ if (insn->fpu_action_flags & FPU_ACTION_ZERO)
+ {
+ if (strncasecmp (template->name, "FTOI", 4) &&
+ strncasecmp (template->name, "DTOI", 4) &&
+ strncasecmp (template->name, "DTOL", 4))
+ {
+ as_bad (_("zero flag is not valid for this instruction"));
+ return NULL;
+ }
+ }
+
+ l = parse_fpu_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!strncasecmp (template->name, "DTOL", 4) ||
+ !strncasecmp (template->name, "LTOD", 4))
+ {
+ if (regs[0]->no % 2)
+ {
+ as_bad (_("destination register should be even numbered"));
+ return NULL;
+ }
+
+ if (regs[1]->no % 2)
+ {
+ as_bad (_("source register should be even numbered"));
+ return NULL;
+ }
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14));
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+
+ if (insn->fpu_action_flags & FPU_ACTION_ZERO)
+ insn->bits |= (1 << 12);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU extended data conversion instruction. */
+static const char *
+parse_fconvx (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+ int fraction_bits = 0;
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ {
+ if (strncasecmp (template->name, "FTOX", 4) &&
+ strncasecmp (template->name, "XTOF", 4))
+ {
+ as_bad (_("instruction cannot operate on pair values"));
+ return NULL;
+ }
+ }
+
+ l = parse_fpu_regs (l, regs, 2);
+
+ l = skip_comma (l);
+
+ if (l == NULL ||
+ *l == END_OF_INSN)
+ return NULL;
+
+ l = parse_imm_constant (l, insn, &fraction_bits);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14));
+
+ if (strncasecmp (template->name, "DTOXL", 5) &&
+ strncasecmp (template->name, "XLTOD", 5))
+ {
+ if (!within_unsigned_range (fraction_bits, IMM5_BITS))
+ {
+ as_bad (_("fraction bits value out of range"));
+ return NULL;
+ }
+ insn->bits |= ((fraction_bits & IMM5_MASK) << 9);
+ }
+ else
+ {
+ if (!within_unsigned_range (fraction_bits, IMM6_BITS))
+ {
+ as_bad (_("fraction bits value out of range"));
+ return NULL;
+ }
+ insn->bits |= ((fraction_bits & IMM6_MASK) << 8);
+ }
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU basic arithmetic instruction. */
+static const char *
+parse_fbarith (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[3];
+
+ l = parse_fpu_regs (l, regs, 3);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ (regs[2]->no << 9));
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+ else if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 5);
+
+ if (insn->fpu_action_flags & FPU_ACTION_INV)
+ insn->bits |= (1 << 7);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse a floating point accumulator name. */
+static const char *
+parse_acf (const char *line, int *part)
+{
+ const char *l = line;
+ size_t i;
+
+ for (i = 0; i < sizeof(metag_acftab)/sizeof(metag_acftab[0]); i++)
+ {
+ const metag_acf *acf = &metag_acftab[i];
+ size_t name_len = strlen (acf->name);
+
+ if (strncasecmp (l, acf->name, name_len) == 0)
+ {
+ l += name_len;
+ *part = acf->part;
+ return l;
+ }
+ }
+ return NULL;
+}
+
+/* Parse an FPU extended arithmetic instruction. */
+static const char *
+parse_fearith (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[3];
+ bfd_boolean is_muz = (MINOR_OPCODE (template->meta_opcode) == 0x6 &&
+ ((template->meta_opcode >> 4) & 0x1));
+ unsigned int is_o3o = template->meta_opcode & 0x1;
+ unsigned int is_mac = 0;
+ unsigned int is_maw = 0;
+
+ if (!strncasecmp (template->name, "MAW", 3))
+ is_maw = 1;
+
+ if (!strncasecmp (template->name, "MAC", 3))
+ {
+ int part;
+ l = parse_acf (l, &part);
+
+ if (l == NULL || part != 0)
+ return NULL;
+
+ l = skip_comma (l);
+
+ l = parse_fpu_regs (l, &regs[1], 2);
+
+ is_mac = 1;
+ }
+ else
+ {
+ if (is_o3o && is_maw)
+ l = parse_fpu_regs (l, regs, 2);
+ else
+ l = parse_fpu_regs (l, regs, 3);
+ }
+
+ if (l == NULL)
+ return NULL;
+
+ if (is_o3o && is_maw)
+ insn->bits = (template->meta_opcode |
+ (regs[1]->no << 9));
+ else
+ insn->bits = (template->meta_opcode |
+ (regs[1]->no << 14));
+
+ if (!(is_o3o && is_maw))
+ insn->bits |= (regs[2]->no << 9);
+
+ if (is_o3o && is_maw)
+ insn->bits |= (regs[0]->no << 14);
+ else if (!is_mac)
+ insn->bits |= (regs[0]->no << 19);
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+ else if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 5);
+
+ if (!is_mac && !is_maw)
+ if (insn->fpu_action_flags & FPU_ACTION_INV)
+ insn->bits |= (1 << 7);
+
+ if (is_muz)
+ if (insn->fpu_action_flags & FPU_ACTION_QUIET)
+ insn->bits |= (1 << 1);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU RCP or RSQ instruction. */
+static const char *
+parse_frec (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[2];
+
+ l = parse_fpu_regs (l, regs, 2);
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14));
+
+ if (insn->fpu_width == FPU_WIDTH_PAIR)
+ insn->bits |= (1 << 6);
+ else if (insn->fpu_width == FPU_WIDTH_DOUBLE)
+ insn->bits |= (1 << 5);
+
+ if (insn->fpu_action_flags & FPU_ACTION_ZERO)
+ insn->bits |= (1 << 10);
+ else if (insn->fpu_action_flags & FPU_ACTION_QUIET)
+ insn->bits |= (1 << 9);
+
+ if (insn->fpu_action_flags & FPU_ACTION_INV)
+ insn->bits |= (1 << 7);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU vector arithmetic instruction. */
+static const char *
+parse_fsimd (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[3];
+
+ if (insn->fpu_width != FPU_WIDTH_PAIR)
+ {
+ as_bad (_("simd instructions operate on pair values (L prefix)"));
+ return NULL;
+ }
+
+ l = parse_fpu_regs (l, regs, 3);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->no % 2)
+ {
+ as_bad (_("destination register should be even numbered"));
+ return NULL;
+ }
+
+ if ((regs[1]->no % 2) ||
+ (regs[2]->no % 2))
+ {
+ as_bad (_("source registers should be even numbered"));
+ return NULL;
+ }
+
+ insn->bits = (template->meta_opcode |
+ (regs[0]->no << 19) |
+ (regs[1]->no << 14) |
+ (regs[2]->no << 9));
+
+ if (insn->fpu_action_flags & FPU_ACTION_INV)
+ insn->bits |= (1 << 7);
+
+ insn->len = 4;
+ return l;
+}
+
+/* Parse an FPU accumulator GET or SET instruction. */
+static const char *
+parse_fget_set_acf (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ int part;
+ metag_addr addr;
+ bfd_boolean is_get = MAJOR_OPCODE (template->meta_opcode) == OPC_GET;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ if (is_get)
+ {
+ l = parse_acf (l, &part);
+
+ l = skip_comma (l);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_mget_mset_addr (l, &addr);
+ }
+ else
+ {
+ l = parse_mget_mset_addr (l, &addr);
+
+ l = skip_comma (l);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_acf (l, &part);
+ }
+
+ if (l == NULL)
+ return NULL;
+
+ insn->bits = (template->meta_opcode |
+ (part << 19));
+
+ if (!is_short_unit (addr.base_reg->unit))
+ {
+ as_bad (_("base unit must be one of %s"), SHORT_UNITS);
+ return NULL;
+ }
+
+ insn->bits |= ((addr.base_reg->no << 14) |
+ ((addr.base_reg->unit & SHORT_UNIT_MASK) << 5));
+
+ insn->len = 4;
+ return l;
+}
+
+/* Copy the name of the next register in LINE to REG_BUF. */
+static size_t
+strip_reg_name(const char *line, char *reg_buf)
+{
+ const char *l = line;
+ size_t len = 0;
+
+ while (is_register_char (*l))
+ {
+ reg_buf[len] = *l;
+ l++;
+ len++;
+ if (!(len < MAX_REG_LEN))
+ return 0;
+ }
+
+ if (len)
+ reg_buf[len] = '\0';
+
+ return len;
+}
+
+/* Parse a DSP register from LINE into REG using only the registers
+ from DSP_REGTAB. Return the next character or NULL. */
+static const char *
+__parse_dsp_reg (const char *line, const metag_reg **reg, htab_t dsp_regtab)
+{
+ const char *l = line;
+ char name[MAX_REG_LEN];
+ size_t len = 0;
+ metag_reg entry;
+ const metag_reg *_reg;
+
+ /* We don't entirely strip the register name because we might
+ actually want to match whole string in the register table,
+ e.g. "D0AW.1++" not just "D0AW.1". The string length of the table
+ entry limits our comaprison to a reasonable bound anyway. */
+ while (is_register_char (*l) || *l == PLUS)
+ {
+ name[len] = *l;
+ l++;
+ len++;
+ if (!(len < MAX_REG_LEN))
+ return NULL;
+ }
+
+ if (!len)
+ return NULL;
+
+ name[len] = '\0';
+ entry.name = name;
+
+ _reg = (const metag_reg *) htab_find (dsp_regtab, &entry);
+ if (!_reg)
+ return NULL;
+
+ *reg = _reg;
+
+ return l;
+}
+
+/* Parse a DSP register and setup "reg" with a metag_reg whose "no"
+ member is suitable for encoding into a DSP insn register field. */
+static const char *
+parse_dsp_insn_reg (const char *line, const metag_reg **reg)
+{
+ return __parse_dsp_reg (line, reg, dsp_reg_htab);
+}
+
+/* Parse a DSP register and setup "reg" with a metag_reg whose "no"
+ member is suitable for encoding into a DSP template definition insn
+ register field.
+
+ There is a separate table for whether we're doing a load or a store
+ definition. "load" specifies which table to look at. */
+static const char *
+parse_dsp_template_reg (const char *line, const metag_reg **reg,
+ bfd_boolean load)
+{
+ return __parse_dsp_reg (line, reg, dsp_tmpl_reg_htab[load]);
+}
+
+/* Parse a single DSP register from LINE. */
+static const char *
+parse_dsp_reg (const char *line, const metag_reg **reg,
+ bfd_boolean tmpl, bfd_boolean load)
+{
+ if (tmpl)
+ return parse_dsp_template_reg (line, reg, load);
+ else
+ return parse_dsp_insn_reg (line, reg);
+}
+
+/* Return TRUE if UNIT is an address unit. */
+static bfd_boolean
+is_addr_unit (enum metag_unit unit)
+{
+ switch (unit)
+ {
+ case UNIT_A0:
+ case UNIT_A1:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* Return TRUE if UNIT1 and UNIT2 are equivalent units. */
+static bfd_boolean
+is_same_data_unit (enum metag_unit unit1, enum metag_unit unit2)
+{
+ if (unit1 == unit2)
+ return TRUE;
+
+ switch (unit1)
+ {
+ case UNIT_D0:
+ if (unit2 == UNIT_ACC_D0 || unit2 == UNIT_RAM_D0)
+ return TRUE;
+ break;
+ case UNIT_D1:
+ if (unit2 == UNIT_ACC_D1 || unit2 == UNIT_RAM_D1)
+ return TRUE;
+ break;
+ case UNIT_ACC_D0:
+ if (unit2 == UNIT_D0 || unit2 == UNIT_RAM_D0)
+ return TRUE;
+ break;
+ case UNIT_ACC_D1:
+ if (unit2 == UNIT_D1 || unit2 == UNIT_RAM_D1)
+ return TRUE;
+ break;
+ case UNIT_RAM_D0:
+ if (unit2 == UNIT_ACC_D0 || unit2 == UNIT_D0)
+ return TRUE;
+ break;
+ case UNIT_RAM_D1:
+ if (unit2 == UNIT_ACC_D1 || unit2 == UNIT_D1)
+ return TRUE;
+ break;
+ default:
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/* Return TRUE if the register NUM is a quickrot control register. */
+static bfd_boolean
+is_quickrot_reg (unsigned int num)
+{
+ switch (num)
+ {
+ case 2:
+ case 3:
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Return TRUE if REG is an accumulator register. */
+static bfd_boolean
+is_accumulator_reg (const metag_reg *reg)
+{
+ if (reg->unit == UNIT_ACC_D0 || reg->unit == UNIT_ACC_D1)
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Return TRUE if REG is a DSP RAM register. */
+static bfd_boolean
+is_dspram_reg (const metag_reg *reg)
+{
+ if (reg->unit == UNIT_RAM_D0 || reg->unit == UNIT_RAM_D1)
+ return TRUE;
+
+ return FALSE;
+}
+
+static const char *
+__parse_gp_reg (const char *line, const metag_reg **reg, bfd_boolean load)
+{
+ const char *l = line;
+ char reg_buf[MAX_REG_LEN];
+ size_t len = 0;
+
+ if (l == NULL)
+ return NULL;
+
+ /* Parse [DSPRAM.x]. */
+ if (*l == ADDR_BEGIN_CHAR)
+ {
+ l++;
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_dsp_reg (l, reg, TRUE, load);
+ if (l == NULL)
+ return NULL;
+
+ if (*l == ADDR_END_CHAR)
+ l++;
+ else
+ {
+ as_bad (_("expected ']', not %c in %s"), *l, l);
+ return NULL;
+ }
+
+ return l;
+ }
+ else
+ {
+
+ len = strip_reg_name (l, reg_buf);
+ if (!len)
+ return NULL;
+
+ l += len;
+ *reg = parse_gp_reg (reg_buf);
+ if (*reg == NULL)
+ return NULL;
+ }
+
+ return l;
+}
+
+/* Parse a list of DSP/GP registers. TRY_GP indicates whether we
+ should try to parse the register as a general-purpose register if
+ we fail to parse it as a DSP one. TMPL indicates whether the
+ registers are part of a template definition instruction. If this is
+ a template definition instruction LOAD says whether it's a load
+ template insn. FIRST_DST indicates whether the first register is
+ a destination operand. */
+static const char *
+parse_dsp_regs_list (const char *line, const metag_reg **regs, size_t count,
+ size_t *regs_read, bfd_boolean try_gp, bfd_boolean tmpl,
+ bfd_boolean load, bfd_boolean first_dst)
+{
+ const char *l = line;
+ int seen_regs = 0;
+ size_t i;
+ const metag_reg *reg;
+
+ for (i = 0; i < count; i++)
+ {
+ const char *next, *ll;
+
+ next = l;
+
+ if (i > 0)
+ {
+ l = skip_comma (l);
+ if (l == NULL)
+ {
+ *regs_read = seen_regs;
+ return next;
+ }
+ }
+
+ ll = parse_dsp_reg (l, &reg, tmpl, load);
+
+ if (!ll)
+ {
+ if (try_gp)
+ {
+ l = __parse_gp_reg (l, &reg, !(first_dst && i == 0));
+ if (l == NULL)
+ {
+ *regs_read = seen_regs;
+ return next;
+ }
+ regs[i] = reg;
+ seen_regs++;
+ }
+ else
+ {
+ *regs_read = seen_regs;
+ return l;
+ }
+ }
+ else
+ {
+ regs[i] = reg;
+ seen_regs++;
+ l = ll;
+ }
+ }
+
+ *regs_read = seen_regs;
+ return l;
+}
+
+/* Parse the following memory references:
+
+ - [Ax.r]
+ - [Ax.r++]
+ - [Ax.r--]
+ - [Ax.r+Ax.r++]
+ - [Ax.r-Ax.r--]
+
+ - [DSPRam]
+ - [DSPRam++]
+ - [DSPRam+DSPRam++]
+ - [DSPRam-DSPRam--] */
+static const char *
+parse_dsp_addr (const char *line, metag_addr *addr, unsigned int size,
+ bfd_boolean load)
+{
+ const char *l = line, *ll;
+ const metag_reg *regs[1];
+ size_t regs_read;
+
+ /* Skip opening square bracket. */
+ l++;
+
+ l = parse_dsp_regs_list (l, regs, 1, &regs_read, TRUE, TRUE, load, FALSE);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!is_addr_unit (regs[0]->unit) &&
+ !is_dspram_reg (regs[0]))
+ {
+ as_bad (_("invalid register for memory access"));
+ return NULL;
+ }
+
+ addr->base_reg = regs[0];
+
+ if (*l == ADDR_END_CHAR)
+ {
+ addr->exp.X_op = O_constant;
+ addr->exp.X_add_symbol = NULL;
+ addr->exp.X_op_symbol = NULL;
+
+ /* Simple register with no offset (0 immediate). */
+ addr->exp.X_add_number = 0;
+
+ addr->immediate = 1;
+ l++;
+
+ return l;
+ }
+
+ ll = parse_addr_post_incr_op (l, addr);
+
+ if (ll && *ll == ADDR_END_CHAR)
+ {
+ if (addr->update == 1)
+ {
+ /* We have a post increment/decrement. */
+ addr->exp.X_op = O_constant;
+ addr->exp.X_add_number = size;
+ addr->exp.X_add_symbol = NULL;
+ addr->exp.X_op_symbol = NULL;
+ addr->post_increment = 1;
+ }
+ addr->immediate = 1;
+ ll++;
+ return ll;
+ }
+
+ addr->post_increment = 0;
+
+ l = parse_addr_op (l, addr);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_dsp_regs_list (l, regs, 1, &regs_read, TRUE, TRUE, load, FALSE);
+
+ if (l == NULL)
+ return NULL;
+
+ if (regs[0]->unit != addr->base_reg->unit)
+ {
+ as_bad (_("offset and base must be from the same unit"));
+ return NULL;
+ }
+
+ addr->offset_reg = regs[0];
+
+ if (*l == ADDR_END_CHAR)
+ {
+ l++;
+ return l;
+ }
+
+ l = parse_addr_post_incr_op (l, addr);
+
+ if (l == NULL)
+ return NULL;
+
+ if (*l == ADDR_END_CHAR)
+ {
+ l++;
+ return l;
+ }
+
+ return NULL;
+}
+
+/* Parse a DSP GET or SET instruction. */
+static const char *
+parse_dget_set (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ metag_addr addr;
+ int unit = 0;
+ int rd_reg = 0;
+ bfd_boolean is_get = (template->meta_opcode & 0x100);
+ bfd_boolean is_dual = (template->meta_opcode & 0x4);
+ bfd_boolean is_template = FALSE;
+ const metag_reg *regs[2];
+ unsigned int size;
+ size_t count, regs_read;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.reloc_type = BFD_RELOC_UNUSED;
+
+ size = is_dual ? 8 : 4;
+ count = is_dual ? 2 : 1;
+
+ if (is_get)
+ {
+ /* GETL can be used on one template table entry. */
+ if (*l == 'T')
+ count = 1;
+
+ l = parse_dsp_regs_list (l, regs, count, &regs_read, FALSE,
+ FALSE, FALSE, FALSE);
+ l = skip_comma (l);
+
+ if (l == NULL)
+ {
+ as_bad (_("unexpected end of line"));
+ return NULL;
+ }
+
+ l = parse_addr (l, &addr, size);
+ }
+ else
+ {
+ l = parse_addr (l, &addr, size);
+
+ l = skip_comma (l);
+
+ if (l == NULL)
+ return NULL;
+
+ /* GETL can be used on one template table entry. */
+ if (*l == 'T')
+ count = 1;
+
+ l = parse_dsp_regs_list (l, regs, count, &regs_read, FALSE, FALSE,
+ FALSE, FALSE);
+ }
+
+ if (l == NULL)
+ return NULL;
+
+ /* The first register dictates the unit. */
+ if (regs[0]->unit == UNIT_DT)
+ is_template = TRUE;
+ else
+ {
+ if (regs[0]->unit == UNIT_D0 || regs[0]->unit == UNIT_RAM_D0 ||
+ regs[0]->unit == UNIT_ACC_D0)
+ unit = 0;
+ else
+ unit = 1;
+ }
+
+ rd_reg = regs[0]->no;
+
+ /* The 'H' modifier allows a DSP GET/SET instruction to target the
+ upper 8-bits of an accumulator. It is _only_ valid for the
+ accumulators. */
+ if (insn->dsp_daoppame_flags & DSP_DAOPPAME_HIGH)
+ {
+ if (is_template || !(rd_reg >= 16 && rd_reg < 20))
+ {
+ as_bad (_("'H' modifier only valid for accumulator registers"));
+ return NULL;
+ }
+
+ /* Top 8-bits of the accumulator. */
+ rd_reg |= 8;
+ }
+
+ if (is_template)
+ {
+ insn->bits = (template->meta_opcode | (1 << 1));
+ }
+ else
+ {
+ insn->bits = (template->meta_opcode | unit);
+ }
+
+ insn->bits |= (rd_reg << 19);
+
+ if (addr.immediate)
+ {
+ int offset = addr.exp.X_add_number;
+
+ if (addr.negate)
+ offset = -offset;
+
+ offset = offset / (int)size;
+
+ if (!within_signed_range (offset, DGET_SET_IMM_BITS))
+ {
+ as_bad (_("offset value out of range"));
+ return NULL;
+ }
+
+ offset = offset & DGET_SET_IMM_MASK;
+
+ insn->bits |= (1 << 13);
+ insn->bits |= (offset << 9);
+ }
+ else
+ {
+ int au = (addr.base_reg->unit == UNIT_A1);
+
+ insn->bits |= (au << 18);
+ insn->bits |= ((addr.base_reg->no & REG_MASK) << 14);
+ insn->bits |= ((addr.offset_reg->no & REG_MASK) << 9);
+ }
+
+ if (is_dual)
+ insn->bits |= (1 << 2);
+
+ if (!is_addr_unit (addr.base_reg->unit))
+ {
+ as_bad (_("base unit must be either A0 or A1"));
+ return NULL;
+ }
+
+ unit = (addr.base_reg->unit == UNIT_A0) ? 0 : 1;
+ insn->bits |= ((addr.base_reg->no << 14) | (unit << 18));
+
+ insn->len = 4;
+
+ return l;
+}
+
+/* Parse a DSP template instruction. */
+static const char *
+parse_dtemplate (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const metag_reg *regs[TEMPLATE_NUM_REGS];
+ bfd_boolean daop_only = FALSE;
+ int regs_val[4];
+ int regs_which[4] = { -1, -1, -1, -1}; /* Register or immediate? */
+ int i;
+
+ for (i = 0; i < TEMPLATE_NUM_REGS; i++)
+ {
+ if (l == NULL)
+ {
+ as_bad (_("unexpected end of line"));
+ return NULL;
+ }
+
+ /* We may only have 3 register operands. */
+ if (*l == END_OF_INSN && i == 3)
+ {
+ daop_only = TRUE;
+ break;
+ }
+
+ if (i != 0)
+ {
+ l = skip_comma (l);
+ if (l == NULL)
+ return NULL;
+ }
+
+ if (*l == IMM_CHAR)
+ {
+ l = parse_imm_constant (l, insn, &regs_val[i]);
+ if (l == NULL)
+ {
+ as_bad (_("invalid immediate"));
+ return NULL;
+ }
+ regs_which[i] = 0;
+ }
+ else
+ {
+ /* We can't tell from the template instantiation whether
+ this is a load or store. So we have to try looking up the
+ register name in both the load and store tables. */
+ const char *l2 = l;
+ l = __parse_gp_reg (l, &regs[i], TRUE);
+ if (l == NULL)
+ {
+ /* Try the store table too. */
+ l = __parse_gp_reg (l2, &regs[i], FALSE);
+ if (l == NULL)
+ {
+ /* Then try a DSP register. */
+ l = parse_dsp_insn_reg (l2, &regs[i]);
+ if (l == NULL || regs[i]->unit == UNIT_DT)
+ {
+ as_bad (_("invalid register"));
+ return NULL;
+ }
+ }
+ }
+ regs_which[i] = 1;
+ }
+ }
+
+ insn->bits = template->meta_opcode;
+
+ if (regs_which[0] == 0)
+ insn->bits |= (regs_val[0] << 19);
+ else if (regs_which[0] == 1)
+ insn->bits |= (regs[0]->no << 19);
+
+ if (regs_which[1] == 0)
+ insn->bits |= (regs_val[1] << 14);
+ else if (regs_which[1] == 1)
+ insn->bits |= (regs[1]->no << 14);
+
+ if (regs_which[2] == 0)
+ insn->bits |= (regs_val[2] << 9);
+ else if (regs_which[2] == 1)
+ insn->bits |= (regs[2]->no << 9);
+
+ if (regs_which[3] == 0)
+ insn->bits |= (regs_val[3] << 4);
+ else if (regs_which[3] == 1)
+ insn->bits |= (regs[3]->no << 4);
+
+ /* DaOp only. */
+ if (daop_only)
+ insn->bits |= (0x3 << 24); /* Set the minor opcode. */
+ else if (insn->dsp_daoppame_flags & DSP_DAOPPAME_HIGH) /* Half Load/Store. */
+ insn->bits |= (0x5 << 24); /* Set the minor opcode. */
+
+ insn->len = 4;
+
+ return l;
+}
+
+/* Parse a DSP Template definiton memory reference, e.g
+ [A0.7+A0.5++]. DSPRAM is set to true by this function if this
+ template definition is a DSP RAM template definition. */
+static const char *
+template_mem_ref(const char *line, metag_addr *addr,
+ bfd_boolean *dspram, int size, bfd_boolean load)
+{
+ const char *l = line;
+
+ l = parse_dsp_addr (l, addr, size, load);
+
+ if (l != NULL)
+ {
+ if (is_addr_unit(addr->base_reg->unit))
+ *dspram = FALSE;
+ else
+ *dspram = TRUE;
+ }
+
+ return l;
+}
+
+/* Sets LOAD to TRUE if this is a Template load definiton (otherwise
+ it's a store). Fills out ADDR, TEMPLATE_REG and ADDR_UNIT. */
+static const char *
+parse_template_regs (const char *line, bfd_boolean *load,
+ unsigned int *addr_unit,
+ const metag_reg **template_reg, metag_addr *addr,
+ bfd_boolean *dspram, int size)
+{
+ const char *l = line;
+
+ if (l == NULL)
+ return NULL;
+
+ /* DSP Template load definition (Tx, [Ax]) */
+ if (*l == 'T')
+ {
+ *load = TRUE;
+ l = parse_dsp_reg (l, &template_reg[0], FALSE, FALSE);
+ if (l == NULL)
+ return NULL;
+
+ l = skip_comma (l);
+
+ l = template_mem_ref (l, addr, dspram, size, *load);
+
+ if (addr->base_reg->unit == UNIT_A1)
+ *addr_unit = 1;
+
+ }
+ else if (*l == ADDR_BEGIN_CHAR) /* DSP Template store ([Ax], Tx) */
+ {
+ *load = FALSE;
+ l = template_mem_ref (l, addr, dspram, size, *load);
+ l = skip_comma(l);
+
+ if (l == NULL)
+ return NULL;
+
+ l = parse_dsp_reg (l, &template_reg[0], FALSE, FALSE);
+ if (l == NULL)
+ return NULL;
+
+ if (addr->base_reg->unit == UNIT_A1)
+ *addr_unit = 1;
+ }
+ else
+ {
+ as_bad (_("invalid register operand"));
+ return NULL;
+ }
+
+ return l;
+}
+
+#define INVALID_SHIFT (-1)
+
+static metag_reg _reg;
+
+/* Parse a template instruction definition. */
+static const char *
+interpret_template_regs(const char *line, metag_insn *insn,
+ const metag_reg **regs,
+ int *regs_shift, bfd_boolean *load, bfd_boolean *dspram,
+ int size, int *ls_shift, int *au_shift,
+ unsigned int *au, int *imm, int *imm_shift,
+ unsigned int *imm_mask)
+{
+ const char *l = line;
+ metag_addr addr;
+ const metag_reg *template_reg[1];
+
+ memset (&addr, 0, sizeof(addr));
+
+ regs_shift[0] = 19;
+ regs_shift[1] = INVALID_SHIFT;
+
+ insn->bits |= (1 << 1);
+
+ l = skip_whitespace (l);
+
+ l = parse_template_regs (l, load, au, template_reg,
+ &addr, dspram, size);
+ if (l == NULL)
+ {
+ as_bad (_("could not parse template definition"));
+ return NULL;
+ }
+
+ regs[2] = template_reg[0];
+ regs_shift[2] = 9;
+
+ /* DSPRAM definition. */
+ if (*dspram)
+ {
+
+ _reg = *addr.base_reg;
+
+ if (addr.immediate)
+ {
+ /* Set the post-increment bit in the register field. */
+ if (addr.update)
+ _reg.no |= 0x1;
+ }
+ else
+ {
+ /* The bottom bit of the increment register tells us
+ whether it's increment register 0 or 1. */
+ if (addr.offset_reg->no & 0x1)
+ _reg.no |= 0x3;
+ else
+ _reg.no |= 0x2;
+ }
+
+ regs[0] = &_reg;
+
+ insn->bits |= (0x3 << 17); /* This signifies a DSPRAM definition. */
+ }
+ else /* DaOpPaMe definition. */
+ {
+ regs[0] = addr.base_reg;
+ if (addr.immediate)
+ {
+ /* Set the I bit. */
+ insn->bits |= (1 << 18);
+
+ if (addr.update == 1)
+ {
+ if (addr.negate == 1)
+ *imm = 0x3;
+ else
+ *imm = 0x1;
+ }
+
+ *imm_shift = 14;
+ *imm_mask = 0x3;
+ }
+ else
+ {
+ /* Setup the offset register. */
+ regs[1] = addr.offset_reg;
+ regs_shift[1] = 14;
+ }
+ *au_shift = 23;
+ }
+
+ *ls_shift = 13;
+
+ return l;
+}
+
+/* Does this combination of units need the O2R bit and can it be encoded? */
+static bfd_boolean
+units_need_o2r (enum metag_unit unit1, enum metag_unit unit2)
+{
+ if (unit1 == unit2)
+ return FALSE;
+
+ if (unit1 == UNIT_D0 || unit1 == UNIT_ACC_D0 || unit1 == UNIT_RAM_D0)
+ {
+ if (unit2 == UNIT_ACC_D0 || unit2 == UNIT_RAM_D0 || unit2 == UNIT_D0)
+ return FALSE;
+
+ switch (unit2)
+ {
+ case UNIT_A1:
+ case UNIT_D1:
+ case UNIT_RD:
+ case UNIT_A0:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
+ if (unit1 == UNIT_D1 || unit1 == UNIT_ACC_D1 || unit1 == UNIT_RAM_D1)
+ {
+ if (unit2 == UNIT_ACC_D1 || unit2 == UNIT_RAM_D1 || unit2 == UNIT_D1)
+ return FALSE;
+
+ switch (unit2)
+ {
+ case UNIT_A1:
+ case UNIT_D0:
+ case UNIT_RD:
+ case UNIT_A0:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Return TRUE if this is a DSP data unit. */
+static bfd_boolean
+is_dsp_data_unit (const metag_reg *reg)
+{
+ switch (reg->unit)
+ {
+ case UNIT_D0:
+ case UNIT_D1:
+ case UNIT_ACC_D0:
+ case UNIT_ACC_D1:
+ case UNIT_RAM_D0:
+ case UNIT_RAM_D1:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+static metag_reg o2r_reg;
+
+/* Parse a DaOpPaMe load template definition. */
+static const char *
+parse_dalu (const char *line, metag_insn *insn,
+ const insn_template *template)
+{
+ const char *l = line;
+ const char *ll;
+ const metag_reg *regs[4];
+ metag_addr addr;
+ size_t regs_read;
+ bfd_boolean is_mov = MAJOR_OPCODE (template->meta_opcode) == OPC_ADD;
+ bfd_boolean is_cmp = ((MAJOR_OPCODE (template->meta_opcode) == OPC_CMP) &&
+ ((template->meta_opcode & 0xee) == 0));
+ bfd_boolean is_dual = (insn->dsp_width == DSP_WIDTH_DUAL);
+ bfd_boolean is_quickrot64 = ((insn->dsp_action_flags & DSP_ACTION_QR64) != 0);
+ int l1_shift = INVALID_SHIFT;
+ bfd_boolean load = FALSE;
+ int ls_shift = INVALID_SHIFT;
+ bfd_boolean ar = FALSE;
+ int ar_shift = INVALID_SHIFT;
+ int regs_shift[3] = { INVALID_SHIFT, INVALID_SHIFT, INVALID_SHIFT };
+ int imm = 0;
+ int imm_shift = INVALID_SHIFT;
+ unsigned int imm_mask = 0;
+ unsigned int au = 0;
+ int au_shift = INVALID_SHIFT;
+ unsigned int du = 0;
+ int du_shift = INVALID_SHIFT;
+ unsigned int sc = ((insn->dsp_action_flags & DSP_ACTION_OV) != 0);
+ int sc_shift = INVALID_SHIFT;
+ unsigned int om = ((insn->dsp_action_flags & DSP_ACTION_MOD) != 0);
+ int om_shift = INVALID_SHIFT;
+ unsigned int o2r = 0;
+ int o2r_shift = INVALID_SHIFT;
+ unsigned int qr = 0;
+ int qr_shift = INVALID_SHIFT;
+ int qd_shift = INVALID_SHIFT;
+ unsigned int qn = 0;
+ int qn_shift = INVALID_SHIFT;
+ unsigned int a1 = ((insn->dsp_action_flags & (DSP_ACTION_ACC_SUB|DSP_ACTION_ACC_ZERO)) != 0);
+ int a1_shift = INVALID_SHIFT;
+ unsigned int a2 = ((insn->dsp_action_flags & (DSP_ACTION_ACC_SUB|DSP_ACTION_ACC_ADD)) != 0);
+ int a2_shift = INVALID_SHIFT;
+ unsigned su = ((insn->dsp_action_flags & DSP_ACTION_UMUL) != 0);
+ int su_shift = INVALID_SHIFT;
+ unsigned int ac;
+ int ac_shift = INVALID_SHIFT;
+ unsigned int mx = (((insn->dsp_daoppame_flags & DSP_DAOPPAME_8) != 0) ||
+ (insn->dsp_daoppame_flags & DSP_DAOPPAME_16) != 0);
+ int mx_shift = INVALID_SHIFT;
+ int size = is_dual ? 8 : 4;
+ bfd_boolean dspram;
+ bfd_boolean conditional = (MINOR_OPCODE (template->meta_opcode) & 0x4);
+
+ /* XFIXME: check the flags are valid with the instruction. */
+ if (is_quickrot64 && !(template->arg_type & DSP_ARGS_QR))
+ {
+ as_bad (_("QUICKRoT 64-bit extension not applicable to this instruction"));
+ return NULL;
+ }
+
+ insn->bits = template->meta_opcode;
+
+ memset (regs, 0, sizeof (regs));
+ memset (&addr, 0, sizeof (addr));
+
+ /* There are the following forms of DSP ALU instructions,
+
+ * Group 1:
+ 19. D[T] Op De.r,Dx.r,De.r
+ 1. D[T] Op De.r,Dx.r,De.r|ACe.r [Accumulator in src 2]
+ 3. D[T] Op De.r,Dx.r,De.r[,Ae.r] [QUICKRoT]
+ 2. D[T] Op ACe.e,ACx.r,ACo.e [cross-unit accumulator op]
+ 5. D[T] Op De.r|ACe.r,Dx.r,De.r
+ 20. D[T] Op De.r,Dx.r|ACx.r,De.r
+ 8. D Opcc De.r,Dx.r,Rx.r
+ 6. D Op De.r,Dx.r,Rx.r|RD
+ 17. D Op De.r|ACe.r,Dx.r,Rx.r|RD
+ 7. D Op De.e,Dx.r,#I16
+
+ * Group 2:
+ 4. D[T] Op Dx.r,De.r
+ 10. D Op Dx.r,Rx.r|RD
+ 13. D Op Dx.r,Rx.r
+ 11. D Op Dx.r,#I16
+ 12. D[T] Op De.r,Dx.r
+ 14. D Op DSPe.r,Dx.r
+ 15. D Op DSPx.r,#I16
+ 16. D Op De.r,DSPx.r
+ 18. D Op De.r,Dx.r|ACx.r
+
+ * Group 3:
+ 22. D Op De.r,Dx.r|ACx.r,De.r|#I5
+ 23. D Op Ux.r,Dx.r|ACx.r,De.r|#I5
+ 21. D Op De.r,Dx.r|ACx.r,#I5 */
+
+ /* Group 1. */
+ if (template->arg_type & DSP_ARGS_1)
+ {
+ du_shift = 24;
+
+ /* Could this be a cross-unit accumulator op,
+ e.g. ACe.e,ACx.r,ACo.e */
+ if (template->arg_type & DSP_ARGS_XACC)
+ {
+ ll = parse_dsp_regs_list (l, regs, 3, &regs_read, FALSE, FALSE,
+ FALSE, FALSE);
+ if (ll != NULL && regs_read == 3
+ && is_accumulator_reg (regs[0]))
+ {
+ if (regs[0]->unit != regs[1]->unit ||
+ regs[2]->unit == regs[1]->unit)
+ {
+ as_bad (_("invalid operands for cross-unit op"));
+ return NULL;
+ }
+
+ du = (regs[1]->unit == UNIT_ACC_D1);
+ regs_shift[1] = 19;
+ l = ll;
+
+ /* All cross-unit accumulator ops have bits 8 and 6 set. */
+ insn->bits |= (5 << 6);
+
+ goto check_for_template;
+ }
+
+ /* If we reach here, this instruction is not a
+ cross-unit accumulator op. */
+ }
+
+ if (template->arg_type & DSP_ARGS_SPLIT8)
+ om_shift = 7;
+
+ sc_shift = 5;
+ l1_shift = 4;
+ o2r_shift = 0;
+
+ /* De.r|ACe.r,Dx.r,De.r */
+ if (template->arg_type & DSP_ARGS_DACC)
+ {
+ /* XFIXME: these need moving? */
+ a2_shift = 7;
+ su_shift = 6;
+ a1_shift = 2;
+ om_shift = 3;
+
+ ll = parse_dsp_reg (l, &regs[0], FALSE, FALSE);
+ if (ll != NULL)
+ {
+ /* Using ACe.r as the dst requires one of the P,N or Z
+ flags to be used. */
+ if (!(insn->dsp_action_flags &
+ (DSP_ACTION_ACC_SUB|DSP_ACTION_ACC_ADD|DSP_ACTION_ACC_ZERO)))
+ {
+ as_bad (_("missing flags: one of 'P', 'N' or 'Z' required"));
+ return NULL;
+ }
+
+ l = ll;
+ l = skip_comma (l);
+ l = parse_dsp_regs_list (l, &regs[1], 2, &regs_read,
+ TRUE, FALSE, FALSE, FALSE);
+ if (l == NULL || regs_read != 2)
+ {
+ as_bad (_("invalid register"));
+ return NULL;
+ }
+
+ if (regs[1]->unit == UNIT_D1 || regs[1]->unit == UNIT_RAM_D1)
+ du = 1;
+
+ regs_shift[0] = 19;
+ regs_shift[1] = 14;
+ regs_shift[2] = 9;
+ goto check_for_template;
+ }
+
+ /* If we reach here, this instruction does not use the
+ accumulator as the destination register. */
+ if ((insn->dsp_action_flags &
+ (DSP_ACTION_ACC_SUB|DSP_ACTION_ACC_ADD|DSP_ACTION_ACC_ZERO)))
+ {
+ as_bad (_("'P', 'N' or 'Z' flags may only be specified when accumulating"));
+ return NULL;
+ }
+ }
+
+ regs_shift[0] = 19;
+
+
+ l = parse_dsp_regs_list (l, regs, 2, &regs_read, TRUE, FALSE, FALSE, TRUE);
+ if (l == NULL || regs_read != 2)
+ return NULL;
+
+ l = skip_comma (l);
+ if (l == NULL)
+ return NULL;
+
+ if (regs[1]->unit == UNIT_D1 || regs[1]->unit == UNIT_RAM_D1)
+ du = 1;
+
+ if (is_accumulator_reg(regs[0]) && !(template->arg_type & DSP_ARGS_DACC))
+ {
+ as_bad (_("accumulator not a valid destination"));
+ return NULL;
+ }
+
+ /* Check for immediate, e.g. De.r,Dx.r,#I16 */
+ if (*l == IMM_CHAR)
+ {
+ l = parse_imm16 (l, insn, &imm);
+ if (l == NULL)
+ {
+ as_bad (_("invalid immediate value"));
+ return NULL;
+ }
+
+ if (!within_signed_range (imm, IMM16_BITS))
+ {
+ as_bad (_("immediate value out of range"));
+ return NULL;
+ }
+
+ if (regs[0]->unit != regs[1]->unit || regs[0]->no != regs[1]->no)
+ {
+ as_bad (_("immediate value not allowed when source & dest differ"));
+ return NULL;
+ }
+
+ imm_mask = 0xffff;
+ imm_shift = 3;
+
+ /* Set the I-bit */
+ insn->bits |= (1 << 25);
+
+ insn->bits |= (0x3 << 0);
+
+ l1_shift = 2;
+
+ /* Remove any bits that have been set in the immediate
+ field. */
+ insn->bits &= ~(imm_mask << imm_shift);
+ }
+ else
+ {
+
+ regs_shift[1] = 14;
+ regs_shift[2] = 9;
+
+ /* Is Rs2 an accumulator reg, e.g. De.r,Dx.r,De.r|ACe.r */
+ ll = parse_dsp_reg (l, &regs[2], FALSE, FALSE);
+ if (ll != NULL)
+ {
+ l = ll;
+
+ if (!(template->arg_type & DSP_ARGS_ACC2))
+ {
+ as_bad (_("invalid register operand: %s"), regs[2]->name);
+ return NULL;
+ }
+
+ om_shift = 3;
+ ar_shift = 7;
+ ar = TRUE;
+ }
+ else
+ {
+ /* De.r,Dx.r,De.r */
+ l = __parse_gp_reg (l, &regs[2], TRUE);
+ if (l == NULL)
+ return NULL;
+ }
+
+ if (template->arg_type & DSP_ARGS_ACC2)
+ om_shift = 3;
+
+ /* Is this a QUICKRoT instruction? De.r,Dx.r,De.r[,Ae.r] */
+ if (template->arg_type & DSP_ARGS_QR)
+ {
+ if (conditional)
+ qn_shift = 5;
+ else
+ {
+ qn_shift = 7;
+ qr_shift = 6;
+ qd_shift = 5;
+ }
+
+ l = skip_comma (l);
+ if (l == NULL)
+ {
+ as_bad (_("QUICKRoT extension requires 4 registers"));
+ return NULL;
+ }
+
+ l = __parse_gp_reg (l, &regs[3], TRUE);
+ if (l == NULL)
+ {
+ as_bad (_("invalid fourth register"));
+ return NULL;
+ }
+
+ if (!is_addr_unit (regs[3]->unit) ||
+ !is_quickrot_reg (regs[3]->no))
+ {
+ as_bad (_("A0.2,A0.3,A1.2,A1.3 required for QUICKRoT register"));
+ return NULL;
+ }
+
+ qn = (regs[3]->no == 3);
+ }
+ }
+
+ check_for_template:
+ /* This is the common exit path. Check for o2r. */
+ if (regs[2] != NULL)
+ {
+ o2r = units_need_o2r (regs[1]->unit, regs[2]->unit);
+ if (o2r)
+ {
+ o2r_reg.no = lookup_o2r (0, du, regs[2]);
+ o2r_reg.unit = regs[2]->unit;
+ regs[2] = &o2r_reg;
+ }
+ }
+
+ /* Check any DSP RAM pointers are valid for this unit. */
+ if ((du && (regs[0]->unit == UNIT_RAM_D0)) ||
+ (!du && (regs[0]->unit == UNIT_RAM_D1)) ||
+ (du && (regs[1]->unit == UNIT_RAM_D0)) ||
+ (!du && (regs[1]->unit == UNIT_RAM_D1)) ||
+ (du && regs[2] && (regs[2]->unit == UNIT_RAM_D0)) ||
+ (!du && regs[2] && (regs[2]->unit == UNIT_RAM_D1))) {
+ as_bad (_("DSP RAM pointer in incorrect unit"));
+ return NULL;
+ }
+
+ /* Is this a template definition? */
+ if (IS_TEMPLATE_DEF (insn))
+ {
+ l = interpret_template_regs(l, insn, regs, regs_shift, &load,
+ &dspram, size, &ls_shift, &au_shift,
+ &au, &imm, &imm_shift, &imm_mask);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!dspram)
+ mx_shift = 0;
+ }
+
+ goto matched;
+ }
+
+ /* Group 2. */
+ if (template->arg_type & DSP_ARGS_2)
+ {
+ bfd_boolean is_xsd = ((MAJOR_OPCODE (template->meta_opcode) == OPC_MISC) &&
+ (MINOR_OPCODE (template->meta_opcode) == 0xa));
+ bfd_boolean is_fpu_mov = template->insn_type == INSN_DSP_FPU;
+ bfd_boolean to_fpu = (template->meta_opcode >> 7) & 0x1;
+
+ if (is_xsd)
+ du_shift = 0;
+ else
+ du_shift = 24;
+
+ l1_shift = 4;
+
+ /* CMPs and TSTs don't store to their destination operand. */
+ ll = __parse_gp_reg (l, regs, is_cmp);
+ if (ll == NULL)
+ {
+ /* DSPe.r,Dx.r or DSPx.r,#I16 */
+ if (template->arg_type & DSP_ARGS_DSP_SRC1)
+ {
+ l = parse_dsp_reg (l, regs, FALSE, FALSE);
+ if (l == NULL)
+ {
+ as_bad (_("invalid register operand #1"));
+ return NULL;
+ }
+
+ /* Only MOV instructions have a DSP register as a
+ destination. Set the MOV DSPe.r opcode. The simple
+ OR'ing is OK because the usual MOV opcode is 0x00. */
+ insn->bits = (0x91 << 24);
+ du_shift = 0;
+ l1_shift = 2;
+ regs_shift[0] = 19;
+ }
+ else
+ {
+ as_bad (_("invalid register operand #2"));
+ return NULL;
+ }
+ }
+ else
+ {
+ l = ll;
+
+ /* Everything but CMP and TST. */
+ if (MAJOR_OPCODE (template->meta_opcode) == OPC_ADD ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_SUB ||
+ MAJOR_OPCODE (insn->bits) == OPC_9 ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_MISC ||
+ ((template->meta_opcode & 0x0000002c) != 0))
+ regs_shift[0] = 19;
+ else
+ regs_shift[0] = 14;
+ }
+
+ if (!is_dsp_data_unit (regs[0]) && !(regs[0]->unit == UNIT_FX &&
+ is_fpu_mov && to_fpu))
+ return NULL;
+
+ du = (regs[0]->unit == UNIT_D1 || regs[0]->unit == UNIT_RAM_D1 ||
+ regs[0]->unit == UNIT_ACC_D1);
+
+ l = skip_comma (l);
+
+ if (*l == IMM_CHAR)
+ {
+ if (template->arg_type & DSP_ARGS_IMM &&
+ !(is_mov && (MAJOR_OPCODE (insn->bits) != OPC_9)))
+ {
+ l = parse_imm16 (l, insn, &imm);
+ if (l == NULL)
+ {
+ as_bad (_("invalid immediate value"));
+ return NULL;
+ }
+
+ if (!within_signed_range (imm, IMM16_BITS))
+ return NULL;
+
+ l1_shift = 2;
+ regs_shift[0] = 19;
+
+ imm_mask = 0xffff;
+ imm_shift = 3;
+
+ /* Set the I-bit unless it's a MOV because they're
+ different. */
+ if (!(is_mov && MAJOR_OPCODE (insn->bits) == OPC_9))
+ insn->bits |= (1 << 25);
+
+ /* All instructions that takes immediates also have bit 1 set. */
+ insn->bits |= (1 << 1);
+
+ if (MAJOR_OPCODE (insn->bits) != OPC_9)
+ insn->bits |= (1 << 0);
+
+ insn->bits &= ~(1 << 8);
+ }
+ else
+ {
+ as_bad (_("this instruction does not accept an immediate"));
+ return NULL;
+ }
+ }
+ else
+ {
+ if (MAJOR_OPCODE (insn->bits) != OPC_9)
+ {
+ insn->bits |= (1 << 8);
+ l1_shift = 4;
+ }
+
+ ll = __parse_gp_reg (l, &regs[1], TRUE);
+ if (ll == NULL)
+ {
+ if (template->arg_type & DSP_ARGS_DSP_SRC2)
+ {
+ l = parse_dsp_reg (l, &regs[1], FALSE, FALSE);
+ if (l == NULL)
+ {
+ as_bad (_("invalid register operand #3"));
+ return NULL;
+ }
+
+ /* MOV and NEG. */
+ if ((is_mov && (MAJOR_OPCODE (insn->bits) != OPC_9)) ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_SUB)
+ {
+ if (is_accumulator_reg (regs[1]))
+ {
+ if (is_fpu_mov)
+ {
+ as_bad (_("this instruction does not accept an accumulator"));
+ return NULL;
+ }
+ ar_shift = 7;
+ ar = 1;
+ regs_shift[1] = 9;
+ }
+ else
+ {
+ du_shift = 0;
+ l1_shift = 2;
+ regs_shift[1] = 14;
+ insn->bits = (0x92 << 24); /* Set opcode. */
+ }
+ }
+ }
+ else
+ {
+ as_bad (_("invalid register operand #4"));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* Set the o2r bit if required. */
+ if (!is_fpu_mov && units_need_o2r (regs[0]->unit, regs[1]->unit))
+ {
+ o2r_reg = *regs[1];
+ o2r_reg.no = lookup_o2r (0, du, regs[1]);
+ regs[1] = &o2r_reg;
+ o2r_shift = 0;
+ o2r = 1;
+ }
+ else if (!is_dsp_data_unit (regs[1]) &&
+ !(is_fpu_mov && !to_fpu && regs[1]->unit == UNIT_FX))
+ return NULL;
+
+ if (is_fpu_mov && to_fpu)
+ du = (regs[1]->unit == UNIT_D1 ||
+ regs[1]->unit == UNIT_RAM_D1 ||
+ regs[1]->unit == UNIT_ACC_D1);
+
+ l = ll;
+
+ if (MAJOR_OPCODE (insn->bits) == OPC_ADD ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_SUB ||
+ (((template->meta_opcode & 0x0000002c) == 0) &&
+ MAJOR_OPCODE (template->meta_opcode) != OPC_MISC))
+ regs_shift[1] = 9;
+ else
+ regs_shift[1] = 14;
+ }
+ }
+
+ /* If it's an 0x0 MOV or NEG set some lower bits. */
+ if ((MAJOR_OPCODE (insn->bits) == OPC_ADD ||
+ MAJOR_OPCODE (template->meta_opcode) == OPC_SUB) && !is_fpu_mov)
+ {
+ om_shift = 3;
+ sc_shift = 5;
+ insn->bits |= (1 << 2);
+ }
+
+ /* Check for template definitons. */
+ if (IS_TEMPLATE_DEF (insn))
+ {
+ l = interpret_template_regs(l, insn, regs, regs_shift, &load,
+ &dspram, size, &ls_shift, &au_shift,
+ &au, &imm, &imm_shift, &imm_mask);
+ mx_shift = 0;
+
+ if (l == NULL)
+ return NULL;
+ }
+ goto matched;
+ }
+
+ /* Group 3. */
+ du_shift = 24;
+ l1_shift = 4;
+
+ l = __parse_gp_reg (l, regs, FALSE);
+ if (l == NULL)
+ {
+ as_bad (_("invalid register operand"));
+ return NULL;
+ }
+
+ l = skip_comma (l);
+
+ if (*l == 'A')
+ {
+ l = parse_dsp_reg (l, &regs[1], FALSE, FALSE);
+ if (l == NULL)
+ {
+ as_bad (_("invalid accumulator register"));
+ return NULL;
+ }
+ ac = 1;
+ ac_shift = 0;
+ }
+ else
+ {
+ l = __parse_gp_reg (l, &regs[1], TRUE);
+ if (l == NULL)
+ {
+ as_bad (_("invalid register operand"));
+ return NULL;
+ }
+ }
+
+ regs_shift[0] = 19;
+ regs_shift[1] = 14;
+
+ du = (regs[1]->unit == UNIT_D1 || regs[1]->unit == UNIT_ACC_D1
+ || regs[1]->unit == UNIT_RAM_D1);
+
+ l = skip_comma (l);
+
+ if (*l == IMM_CHAR)
+ {
+ l = parse_imm_constant (l, insn, &imm);
+ if (l == NULL)
+ {
+ as_bad (_("invalid immediate value"));
+ return NULL;
+ }
+
+ if (!within_unsigned_range (imm, IMM5_BITS))
+ return NULL;
+
+ imm_mask = 0x1f;
+ imm_shift = 9;
+
+ /* Set the I-bit */
+ insn->bits |= (1 << 25);
+ }
+ else
+ {
+ regs_shift[2] = 9;
+ l = __parse_gp_reg (l, &regs[2], TRUE);
+ if (l == NULL)
+ return NULL;
+ }
+
+ /* Check for post-processing R,G,B flags. Conditional instructions
+ do not have these bits. */
+ if (insn->dsp_action_flags & DSP_ACTION_CLAMP9)
+ {
+ if ((template->meta_opcode >> 26) & 0x1)
+ {
+ as_bad (_("conditional instruction cannot use G flag"));
+ return NULL;
+ }
+
+ insn->bits |= (1 << 3);
+ }
+
+ if (insn->dsp_action_flags & DSP_ACTION_CLAMP8)
+ {
+ if ((template->meta_opcode >> 26) & 0x1)
+ {
+ as_bad (_("conditional instruction cannot use B flag"));
+ return NULL;
+ }
+
+ insn->bits |= (0x3 << 2);
+ }
+
+ if (insn->dsp_action_flags & DSP_ACTION_ROUND)
+ {
+ if ((template->meta_opcode >> 26) & 0x1)
+ {
+ as_bad (_("conditional instruction cannot use R flag"));
+ return NULL;
+ }
+ insn->bits |= (1 << 2);
+ }
+
+ /* Conditional Data Unit Shift instructions cannot be dual unit. */
+ if ((template->meta_opcode >> 26) & 0x1)
+ ls_shift = INVALID_SHIFT;
+
+ /* The Condition Is Always (CA) bit must be set if we're targetting a
+ Ux.r register as the destination. This means that we can't have
+ any other condition bits set. */
+ if (!is_same_data_unit (regs[1]->unit, regs[0]->unit))
+ {
+ /* Set both the Conditional bit and the Condition is Always bit. */
+ insn->bits |= (1 << 26);
+ insn->bits |= (1 << 5);
+
+ /* Fill out the Ud field. */
+ insn->bits |= (regs[0]->unit << 1);
+ }
+
+ if (IS_TEMPLATE_DEF (insn))
+ {
+ l = interpret_template_regs(l, insn, regs, regs_shift, &load,
+ &dspram, size, &ls_shift, &au_shift,
+ &au, &imm, &imm_shift, &imm_mask);
+
+ if (l == NULL)
+ return NULL;
+
+ if (!dspram)
+ mx_shift = 5;
+ }
+
+ /* Fall through. */
+ matched:
+
+ /* Set the registers and immediate values. */
+ if (regs_shift[0] != INVALID_SHIFT)
+ insn->bits |= (regs[0]->no << regs_shift[0]);
+
+ if (regs_shift[1] != INVALID_SHIFT)
+ insn->bits |= (regs[1]->no << regs_shift[1]);
+
+ if (regs_shift[2] != INVALID_SHIFT)
+ insn->bits |= (regs[2]->no << regs_shift[2]);
+
+ /* Does this insn have an 'IMM' bit? The immediate value should
+ already have been masked. */
+ if (imm_shift != INVALID_SHIFT)
+ insn->bits |= ((imm & imm_mask) << imm_shift);
+
+ /* Does this insn have an 'AU' bit? */
+ if (au_shift != INVALID_SHIFT)
+ insn->bits |= (au << au_shift);
+
+ /* Does this instruction have an 'LS' bit? */
+ if (ls_shift != INVALID_SHIFT)
+ insn->bits |= (load << ls_shift);
+
+ /* Does this instruction have an 'AR' bit? */
+ if (ar)
+ insn->bits |= (1 << ar_shift);
+
+ if (du_shift != INVALID_SHIFT)
+ insn->bits |= (du << du_shift);
+
+ if (sc_shift != INVALID_SHIFT)
+ insn->bits |= (sc << sc_shift);
+
+ if (om_shift != INVALID_SHIFT)
+ insn->bits |= (om << om_shift);
+
+ if (o2r_shift != INVALID_SHIFT)
+ insn->bits |= (o2r << o2r_shift);
+
+ if (qn_shift != INVALID_SHIFT)
+ insn->bits |= (qn << qn_shift);
+
+ if (qr_shift != INVALID_SHIFT)
+ insn->bits |= (qr << qr_shift);
+
+ if (qd_shift != INVALID_SHIFT)
+ insn->bits |= (is_quickrot64 << qd_shift);
+
+ if (a1_shift != INVALID_SHIFT)
+ insn->bits |= (a1 << a1_shift);
+
+ if (a2_shift != INVALID_SHIFT)
+ insn->bits |= (a2 << a2_shift);
+
+ if (su_shift != INVALID_SHIFT)
+ insn->bits |= (su << su_shift);
+
+ if (imm_shift != INVALID_SHIFT)
+ insn->bits |= ((imm & imm_mask) << imm_shift);
+
+ if (ac_shift != INVALID_SHIFT)
+ insn->bits |= (ac << ac_shift);
+
+ if (mx_shift != INVALID_SHIFT)
+ insn->bits |= (mx << mx_shift);
+
+ if (is_dual)
+ {
+ if (l1_shift == INVALID_SHIFT)
+ {
+ as_bad (_("'L' modifier not valid for this instruction"));
+ return NULL;
+ }
+
+ insn->bits |= (1 << l1_shift);
+ }
+
+ insn->len = 4;
+
+ return l;
+}
+
+typedef const char *(*insn_parser)(const char *, metag_insn *,
+ const insn_template *);
+
+/* Parser table. */
+static const insn_parser insn_parsers[ENC_MAX] =
+ {
+ [ENC_NONE] = parse_none,
+ [ENC_MOV_U2U] = parse_mov_u2u,
+ [ENC_MOV_PORT] = parse_mov_port,
+ [ENC_MMOV] = parse_mmov,
+ [ENC_MDRD] = parse_mdrd,
+ [ENC_MOVL_TTREC] = parse_movl_ttrec,
+ [ENC_GET_SET] = parse_get_set,
+ [ENC_GET_SET_EXT] = parse_get_set_ext,
+ [ENC_MGET_MSET] = parse_mget_mset,
+ [ENC_COND_SET] = parse_cond_set,
+ [ENC_XFR] = parse_xfr,
+ [ENC_MOV_CT] = parse_mov_ct,
+ [ENC_SWAP] = parse_swap,
+ [ENC_JUMP] = parse_jump,
+ [ENC_CALLR] = parse_callr,
+ [ENC_ALU] = parse_alu,
+ [ENC_SHIFT] = parse_shift,
+ [ENC_MIN_MAX] = parse_min_max,
+ [ENC_BITOP] = parse_bitop,
+ [ENC_CMP] = parse_cmp,
+ [ENC_BRANCH] = parse_branch,
+ [ENC_KICK] = parse_kick,
+ [ENC_SWITCH] = parse_switch,
+ [ENC_CACHER] = parse_cacher,
+ [ENC_CACHEW] = parse_cachew,
+ [ENC_ICACHE] = parse_icache,
+ [ENC_LNKGET] = parse_lnkget,
+ [ENC_FMOV] = parse_fmov,
+ [ENC_FMMOV] = parse_fmmov,
+ [ENC_FMOV_DATA] = parse_fmov_data,
+ [ENC_FMOV_I] = parse_fmov_i,
+ [ENC_FPACK] = parse_fpack,
+ [ENC_FSWAP] = parse_fswap,
+ [ENC_FCMP] = parse_fcmp,
+ [ENC_FMINMAX] = parse_fminmax,
+ [ENC_FCONV] = parse_fconv,
+ [ENC_FCONVX] = parse_fconvx,
+ [ENC_FBARITH] = parse_fbarith,
+ [ENC_FEARITH] = parse_fearith,
+ [ENC_FREC] = parse_frec,
+ [ENC_FSIMD] = parse_fsimd,
+ [ENC_FGET_SET_ACF] = parse_fget_set_acf,
+ [ENC_DGET_SET] = parse_dget_set,
+ [ENC_DTEMPLATE] = parse_dtemplate,
+ [ENC_DALU] = parse_dalu,
+ };
+
+struct metag_core_option
+{
+ char *name;
+ unsigned int value;
+};
+
+/* CPU type options. */
+static const struct metag_core_option metag_cpus[] =
+ {
+ {"all", CoreMeta11|CoreMeta12|CoreMeta21},
+ {"metac11", CoreMeta11},
+ {"metac12", CoreMeta12},
+ {"metac21", CoreMeta21},
+ {NULL, 0},
+ };
+
+/* FPU type options. */
+static const struct metag_core_option metag_fpus[] =
+ {
+ {"metac21", FpuMeta21},
+ {NULL, 0},
+ };
+
+/* DSP type options. */
+static const struct metag_core_option metag_dsps[] =
+ {
+ {"metac21", DspMeta21},
+ {NULL, 0},
+ };
+
+/* Parse a CPU command line option. */
+static int
+metag_parse_cpu (char * str)
+{
+ const struct metag_core_option * opt;
+ int optlen;
+
+ optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing cpu name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = metag_cpus; opt->name != NULL; opt++)
+ if (strncmp (opt->name, str, optlen) == 0)
+ {
+ mcpu_opt = opt->value;
+ return 1;
+ }
+
+ as_bad (_("unknown cpu `%s'"), str);
+ return 0;
+}
+
+/* Parse an FPU command line option. */
+static int
+metag_parse_fpu (char * str)
+{
+ const struct metag_core_option * opt;
+ int optlen;
+
+ optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing fpu name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = metag_fpus; opt->name != NULL; opt++)
+ if (strncmp (opt->name, str, optlen) == 0)
+ {
+ mfpu_opt = opt->value;
+ return 1;
+ }
+
+ as_bad (_("unknown fpu `%s'"), str);
+ return 0;
+}
+
+/* Parse a DSP command line option. */
+static int
+metag_parse_dsp (char * str)
+{
+ const struct metag_core_option * opt;
+ int optlen;
+
+ optlen = strlen (str);
+
+ if (optlen == 0)
+ {
+ as_bad (_("missing DSP name `%s'"), str);
+ return 0;
+ }
+
+ for (opt = metag_dsps; opt->name != NULL; opt++)
+ if (strncmp (opt->name, str, optlen) == 0)
+ {
+ mdsp_opt = opt->value;
+ return 1;
+ }
+
+ as_bad (_("unknown DSP `%s'"), str);
+ return 0;
+}
+
+struct metag_long_option
+{
+ char * option; /* Substring to match. */
+ char * help; /* Help information. */
+ int (* func) (char * subopt); /* Function to decode sub-option. */
+ char * deprecated; /* If non-null, print this message. */
+};
+
+struct metag_long_option metag_long_opts[] =
+ {
+ {"mcpu=", N_("<cpu name>\t assemble for CPU <cpu name>"),
+ metag_parse_cpu, NULL},
+ {"mfpu=", N_("<fpu name>\t assemble for FPU architecture <fpu name>"),
+ metag_parse_fpu, NULL},
+ {"mdsp=", N_("<dsp name>\t assemble for DSP architecture <dsp name>"),
+ metag_parse_dsp, NULL},
+ {NULL, NULL, 0, NULL}
+ };
+
+int
+md_parse_option (int c, char * arg)
+{
+ struct metag_long_option *lopt;
+
+ for (lopt = metag_long_opts; lopt->option != NULL; lopt++)
+ {
+ /* These options are expected to have an argument. */
+ if (c == lopt->option[0]
+ && arg != NULL
+ && strncmp (arg, lopt->option + 1,
+ strlen (lopt->option + 1)) == 0)
+ {
+#if WARN_DEPRECATED
+ /* If the option is deprecated, tell the user. */
+ if (lopt->deprecated != NULL)
+ as_tsktsk (_("option `-%c%s' is deprecated: %s"), c, arg,
+ _(lopt->deprecated));
+#endif
+
+ /* Call the sup-option parser. */
+ return lopt->func (arg + strlen (lopt->option) - 1);
+ }
+ }
+
+ return 0;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ struct metag_long_option *lopt;
+
+ fprintf (stream, _(" Meta specific command line options:\n"));
+
+ for (lopt = metag_long_opts; lopt->option != NULL; lopt++)
+ if (lopt->help != NULL)
+ fprintf (stream, " -%s%s\n", lopt->option, _(lopt->help));
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 2 },
+ { NULL, NULL, 0 }
+};
+
+void
+md_begin (void)
+{
+ int c;
+
+ for (c = 0; c < 256; c++)
+ {
+ if (ISDIGIT (c))
+ {
+ register_chars[c] = c;
+ /* LOCK0, LOCK1, LOCK2. */
+ mnemonic_chars[c] = c;
+ }
+ else if (ISLOWER (c))
+ {
+ register_chars[c] = c;
+ mnemonic_chars[c] = c;
+ }
+ else if (ISUPPER (c))
+ {
+ register_chars[c] = c;
+ mnemonic_chars[c] = c;
+ }
+ else if (c == '.')
+ {
+ register_chars[c] = c;
+ }
+ }
+}
+
+/* Parse a split condition code prefix. */
+static const char *
+parse_split_condition (const char *line, metag_insn *insn)
+{
+ const char *l = line;
+ const split_condition *scond;
+ split_condition entry;
+ char buf[4];
+
+ memcpy (buf, l, 4);
+ buf[3] = '\0';
+
+ entry.name = buf;
+
+ scond = (const split_condition *) htab_find (scond_htab, &entry);
+
+ if (!scond)
+ return NULL;
+
+ insn->scond = scond->code;
+
+ return l + strlen (scond->name);
+}
+
+/* Parse an instruction prefix - F for float, D for DSP - and associated
+ flags and condition codes. */
+static const char *
+parse_prefix (const char *line, metag_insn *insn)
+{
+ const char *l = line;
+
+ l = skip_whitespace (l);
+
+ insn->type = INSN_GP;
+
+ if (TOLOWER (*l) == FPU_PREFIX_CHAR)
+ {
+ if (strncasecmp (l, FFB_INSN, strlen(FFB_INSN)))
+ {
+ insn->type = INSN_FPU;
+
+ l++;
+
+ if (*l == END_OF_INSN)
+ {
+ as_bad (_("premature end of floating point prefix"));
+ return NULL;
+ }
+
+ if (TOLOWER (*l) == FPU_DOUBLE_CHAR)
+ {
+ insn->fpu_width = FPU_WIDTH_DOUBLE;
+ l++;
+ }
+ else if (TOLOWER (*l) == FPU_PAIR_CHAR)
+ {
+ const char *l2 = l;
+
+ /* Check this isn't a split condition beginning with L. */
+ l2 = parse_split_condition (l2, insn);
+
+ if (l2 && is_whitespace_char (*l2))
+ {
+ l = l2;
+ }
+ else
+ {
+ insn->fpu_width = FPU_WIDTH_PAIR;
+ l++;
+ }
+ }
+ else
+ {
+ insn->fpu_width = FPU_WIDTH_SINGLE;
+ }
+
+ if (TOLOWER (*l) == FPU_ACTION_ABS_CHAR)
+ {
+ insn->fpu_action_flags |= FPU_ACTION_ABS;
+ l++;
+ }
+ else if (TOLOWER (*l) == FPU_ACTION_INV_CHAR)
+ {
+ insn->fpu_action_flags |= FPU_ACTION_INV;
+ l++;
+ }
+
+ if (TOLOWER (*l) == FPU_ACTION_QUIET_CHAR)
+ {
+ insn->fpu_action_flags |= FPU_ACTION_QUIET;
+ l++;
+ }
+
+ if (TOLOWER (*l) == FPU_ACTION_ZERO_CHAR)
+ {
+ insn->fpu_action_flags |= FPU_ACTION_ZERO;
+ l++;
+ }
+
+ if (! is_whitespace_char (*l))
+ {
+ l = parse_split_condition (l, insn);
+
+ if (!l)
+ {
+ as_bad (_("unknown floating point prefix character"));
+ return NULL;
+ }
+ }
+
+ l = skip_space (l);
+ }
+ }
+ else if (TOLOWER (*l) == DSP_PREFIX_CHAR)
+ {
+ if (strncasecmp (l, DCACHE_INSN, strlen (DCACHE_INSN)) &&
+ strncasecmp (l, DEFR_INSN, strlen (DEFR_INSN)))
+ {
+ const char *ll = l;
+ insn->type = INSN_DSP;
+
+ l++;
+
+ insn->dsp_width = DSP_WIDTH_SINGLE;
+
+ while (!is_whitespace_char (*l))
+ {
+ /* We have to check for split condition codes first
+ because they are the longest strings to match,
+ e.g. if the string contains "LLS" we want it to match
+ the split condition code "LLS", not the dual unit
+ character "L". */
+ ll = l;
+ l = parse_split_condition (l, insn);
+
+ if (l == NULL)
+ l = ll;
+ else
+ continue;
+
+ /* Accept an FPU prefix char which may be used when doing
+ template MOV with FPU registers. */
+ if (TOLOWER(*l) == FPU_PREFIX_CHAR)
+ {
+ insn->type = INSN_DSP_FPU;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_DUAL_CHAR)
+ {
+ insn->dsp_width = DSP_WIDTH_DUAL;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_QR64_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_QR64;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_UMUL_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_UMUL;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_ROUND_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_ROUND;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_CLAMP9_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_CLAMP9;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_CLAMP8_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_CLAMP8;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_MOD_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_MOD;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_ACC_ZERO_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_ACC_ZERO;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_ACC_ADD_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_ACC_ADD;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_ACC_SUB_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_ACC_SUB;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_ACTION_OV_CHAR)
+ {
+ insn->dsp_action_flags |= DSP_ACTION_OV;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_DAOPPAME_8_CHAR)
+ {
+ insn->dsp_daoppame_flags |= DSP_DAOPPAME_8;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_DAOPPAME_16_CHAR)
+ {
+ insn->dsp_daoppame_flags |= DSP_DAOPPAME_16;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_DAOPPAME_TEMP_CHAR)
+ {
+ insn->dsp_daoppame_flags |= DSP_DAOPPAME_TEMP;
+ l++;
+ continue;
+ }
+
+ if (TOLOWER(*l) == DSP_DAOPPAME_HIGH_CHAR)
+ {
+ insn->dsp_daoppame_flags |= DSP_DAOPPAME_HIGH;
+ l++;
+ continue;
+ }
+
+ as_bad (_("unknown DSP prefix character %c %s"), *l, l);
+ return NULL;
+ }
+
+ l = skip_space (l);
+ }
+ }
+
+ return l;
+}
+
+/* Return a list of appropriate instruction parsers for MNEMONIC. */
+static insn_templates *
+find_insn_templates (const char *mnemonic)
+{
+ insn_template template;
+ insn_templates entry;
+ insn_templates *slot;
+
+ entry.template = &template;
+
+ memcpy ((void *)&entry.template->name, &mnemonic, sizeof (char *));
+
+ slot = (insn_templates *) htab_find (mnemonic_htab, &entry);
+
+ if (slot)
+ return slot;
+
+ return NULL;
+}
+
+/* Make an uppercase copy of SRC into DST and return DST. */
+static char *
+strupper (char * dst, const char *src)
+{
+ size_t i = 0;
+
+ while (src[i])
+ {
+ dst[i] = TOUPPER (src[i]);
+ i++;
+ }
+
+ dst[i] = 0;
+
+ return dst;
+}
+
+/* Calculate a hash value for a template. */
+static hashval_t
+hash_templates (const void *p)
+{
+ insn_templates *tp = (insn_templates *)p;
+ char buf[MAX_MNEMONIC_LEN];
+
+ strupper (buf, tp->template->name);
+
+ return htab_hash_string (buf);
+}
+
+/* Check if two templates are equal. */
+static int
+eq_templates (const void *a, const void *b)
+{
+ insn_templates *ta = (insn_templates *)a;
+ insn_templates *tb = (insn_templates *)b;
+ return strcasecmp (ta->template->name, tb->template->name) == 0;
+}
+
+/* Create the hash table required for parsing instructions. */
+static void
+create_mnemonic_htab (void)
+{
+ size_t i, num_templates = sizeof(metag_optab)/sizeof(metag_optab[0]);
+
+ mnemonic_htab = htab_create_alloc (num_templates, hash_templates,
+ eq_templates, NULL, xcalloc, free);
+
+ for (i = 0; i < num_templates; i++)
+ {
+ const insn_template *template = &metag_optab[i];
+ insn_templates **slot = NULL;
+ insn_templates *new_entry;
+
+ new_entry = xmalloc (sizeof (insn_templates));
+
+ new_entry->template = template;
+ new_entry->next = NULL;
+
+ slot = (insn_templates **) htab_find_slot (mnemonic_htab, new_entry,
+ INSERT);
+
+ if (*slot)
+ {
+ insn_templates *last_entry = *slot;
+
+ while (last_entry->next)
+ last_entry = last_entry->next;
+
+ last_entry->next = new_entry;
+ }
+ else
+ {
+ *slot = new_entry;
+ }
+ }
+}
+
+/* Calculate a hash value for a register. */
+static hashval_t
+hash_regs (const void *p)
+{
+ metag_reg *rp = (metag_reg *)p;
+ char buf[MAX_REG_LEN];
+
+ strupper (buf, rp->name);
+
+ return htab_hash_string (buf);
+}
+
+/* Check if two registers are equal. */
+static int
+eq_regs (const void *a, const void *b)
+{
+ metag_reg *ra = (metag_reg *)a;
+ metag_reg *rb = (metag_reg *)b;
+ return strcasecmp (ra->name, rb->name) == 0;
+}
+
+/* Create the hash table required for parsing registers. */
+static void
+create_reg_htab (void)
+{
+ size_t i, num_regs = sizeof(metag_regtab)/sizeof(metag_regtab[0]);
+
+ reg_htab = htab_create_alloc (num_regs, hash_regs,
+ eq_regs, NULL, xcalloc, free);
+
+ for (i = 0; i < num_regs; i++)
+ {
+ const metag_reg *reg = &metag_regtab[i];
+ const metag_reg **slot;
+
+ slot = (const metag_reg **) htab_find_slot (reg_htab, reg, INSERT);
+
+ if (!*slot)
+ *slot = reg;
+ }
+}
+
+/* Create the hash table required for parsing DSP registers. */
+static void
+create_dspreg_htabs (void)
+{
+ size_t i, num_regs = sizeof(metag_dsp_regtab)/sizeof(metag_dsp_regtab[0]);
+ size_t h;
+
+ dsp_reg_htab = htab_create_alloc (num_regs, hash_regs,
+ eq_regs, NULL, xcalloc, free);
+
+ for (i = 0; i < num_regs; i++)
+ {
+ const metag_reg *reg = &metag_dsp_regtab[i];
+ const metag_reg **slot;
+
+ slot = (const metag_reg **) htab_find_slot (dsp_reg_htab, reg, INSERT);
+
+ /* Make sure there are no hash table collisions, which would
+ require chaining entries. */
+ BFD_ASSERT (*slot == NULL);
+ *slot = reg;
+ }
+
+ num_regs = sizeof(metag_dsp_tmpl_regtab[0])/sizeof(metag_dsp_tmpl_regtab[0][0]);
+
+ for (h = 0; h < 2; h++)
+ {
+ dsp_tmpl_reg_htab[h] = htab_create_alloc (num_regs, hash_regs,
+ eq_regs, NULL, xcalloc, free);
+ }
+
+ for (h = 0; h < 2; h++)
+ {
+ for (i = 0; i < num_regs; i++)
+ {
+ const metag_reg *reg = &metag_dsp_tmpl_regtab[h][i];
+ const metag_reg **slot;
+ slot = (const metag_reg **) htab_find_slot (dsp_tmpl_reg_htab[h],
+ reg, INSERT);
+
+ /* Make sure there are no hash table collisions, which would
+ require chaining entries. */
+ BFD_ASSERT (*slot == NULL);
+ *slot = reg;
+ }
+ }
+}
+
+/* Calculate a hash value for a split condition code. */
+static hashval_t
+hash_scond (const void *p)
+{
+ split_condition *cp = (split_condition *)p;
+ char buf[4];
+
+ strupper (buf, cp->name);
+
+ return htab_hash_string (buf);
+}
+
+/* Check if two split condition codes are equal. */
+static int
+eq_scond (const void *a, const void *b)
+{
+ split_condition *ra = (split_condition *)a;
+ split_condition *rb = (split_condition *)b;
+
+ return strcasecmp (ra->name, rb->name) == 0;
+}
+
+/* Create the hash table required for parsing split condition codes. */
+static void
+create_scond_htab (void)
+{
+ size_t i, nentries;
+
+ nentries = sizeof (metag_scondtab) / sizeof (metag_scondtab[0]);
+
+ scond_htab = htab_create_alloc (nentries, hash_scond, eq_scond,
+ NULL, xcalloc, free);
+ for (i = 0; i < nentries; i++)
+ {
+ const split_condition *scond = &metag_scondtab[i];
+ const split_condition **slot;
+
+ slot = (const split_condition **) htab_find_slot (scond_htab,
+ scond, INSERT);
+ /* Make sure there are no hash table collisions, which would
+ require chaining entries. */
+ BFD_ASSERT (*slot == NULL);
+ *slot = scond;
+ }
+}
+
+/* Entry point for instruction parsing. */
+static bfd_boolean
+parse_insn (const char *line, metag_insn *insn)
+{
+ char mnemonic[MAX_MNEMONIC_LEN];
+ const char *l = line;
+ size_t mnemonic_len = 0;
+ insn_templates *templates;
+
+ l = skip_space (l);
+
+ while (is_mnemonic_char(*l))
+ {
+ l++;
+ mnemonic_len++;
+ }
+
+ if (mnemonic_len >= MAX_MNEMONIC_LEN)
+ {
+ as_bad (_("instruction mnemonic too long: %s"), line);
+ return FALSE;
+ }
+
+ strncpy(mnemonic, line, mnemonic_len);
+
+ mnemonic[mnemonic_len] = '\0';
+
+ templates = find_insn_templates (mnemonic);
+
+ if (templates)
+ {
+ insn_templates *current_template = templates;
+
+ l = skip_space (l);
+
+ while (current_template)
+ {
+ const insn_template *template = current_template->template;
+ enum insn_encoding encoding = template->encoding;
+ insn_parser parser = insn_parsers[encoding];
+
+ current_template = current_template->next;
+
+ if (template->insn_type == INSN_GP &&
+ !(template->core_flags & mcpu_opt))
+ continue;
+
+ if (template->insn_type == INSN_FPU &&
+ !(template->core_flags & mfpu_opt))
+ continue;
+
+ if (template->insn_type == INSN_DSP &&
+ !(template->core_flags & mdsp_opt))
+ continue;
+
+ if (template->insn_type == INSN_DSP_FPU &&
+ !((template->core_flags & mdsp_opt) &&
+ (template->core_flags & mfpu_opt)))
+ continue;
+
+ /* DSP instructions always require special decoding */
+ if ((insn->type == INSN_DSP && (template->insn_type != INSN_DSP)) ||
+ ((template->insn_type == INSN_DSP) && insn->type != INSN_DSP) ||
+ (insn->type == INSN_DSP_FPU && (template->insn_type != INSN_DSP_FPU)) ||
+ ((template->insn_type == INSN_DSP_FPU) && insn->type != INSN_DSP_FPU))
+ continue;
+
+ if (parser)
+ {
+ const char *end = parser(l, insn, template);
+
+ if (end != NULL)
+ {
+ if (*end != END_OF_INSN)
+ as_bad (_("junk at end of line: \"%s\""), line);
+ else
+ return TRUE;
+ }
+ }
+ }
+
+ as_bad (_("failed to assemble instruction: \"%s\""), line);
+ }
+ else
+ {
+ if (insn->type == INSN_FPU)
+ as_bad (_("unknown floating point mnemonic: \"%s\""), mnemonic);
+ else
+ as_bad (_("unknown mnemonic: \"%s\""), mnemonic);
+ }
+ return FALSE;
+}
+
+static void
+output_insn (metag_insn *insn)
+{
+ char *output;
+
+ output = frag_more (insn->len);
+ dwarf2_emit_insn (insn->len);
+
+ if (insn->reloc_type != BFD_RELOC_UNUSED)
+ {
+ fix_new_exp (frag_now, output - frag_now->fr_literal,
+ insn->reloc_size, &insn->reloc_exp,
+ insn->reloc_pcrel, insn->reloc_type);
+ }
+
+ md_number_to_chars (output, insn->bits, insn->len);
+}
+
+void
+md_assemble (char *line)
+{
+ const char *l = line;
+ metag_insn insn;
+
+ memset (&insn, 0, sizeof(insn));
+
+ insn.reloc_type = BFD_RELOC_UNUSED;
+ insn.reloc_pcrel = 0;
+ insn.reloc_size = 4;
+
+ if (!mnemonic_htab)
+ {
+ create_mnemonic_htab ();
+ create_reg_htab ();
+ create_dspreg_htabs ();
+ create_scond_htab ();
+ }
+
+ l = parse_prefix (l, &insn);
+
+ if (l == NULL)
+ return;
+
+ if (insn.type == INSN_DSP &&
+ !mdsp_opt)
+ {
+ as_bad (_("cannot assemble DSP instruction, DSP option not set: %s"),
+ line);
+ return;
+ }
+ else if (insn.type == INSN_FPU &&
+ !mfpu_opt)
+ {
+ as_bad (_("cannot assemble FPU instruction, FPU option not set: %s"),
+ line);
+ return;
+ }
+
+ if (!parse_insn (l, &insn))
+ return;
+
+ output_insn (&insn);
+}
+
+void
+md_operand (expressionS * expressionP)
+{
+ if (* input_line_pointer == IMM_CHAR)
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ return size;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* 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))
+ || metag_force_relocation (fixP))
+ {
+ /* 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;
+}
+
+/* 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;
+}
+
+/* If this function returns non-zero, it prevents the relocation
+ against symbol(s) in the FIXP from being replaced with relocations
+ against section symbols, and guarantees that a relocation will be
+ emitted even when the value can be resolved locally. */
+
+int
+metag_force_relocation (fixS * fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_METAG_RELBRANCH_PLT:
+ case BFD_RELOC_METAG_TLS_LE:
+ case BFD_RELOC_METAG_TLS_IE:
+ case BFD_RELOC_METAG_TLS_LDO:
+ case BFD_RELOC_METAG_TLS_LDM:
+ case BFD_RELOC_METAG_TLS_GD:
+ return 1;
+ default:
+ ;
+ }
+
+ return generic_force_reloc (fix);
+}
+
+bfd_boolean
+metag_fix_adjustable (fixS * fixP)
+{
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return 0;
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return 0;
+
+ if (fixP->fx_r_type == BFD_RELOC_METAG_HI16_GOTOFF ||
+ fixP->fx_r_type == BFD_RELOC_METAG_LO16_GOTOFF ||
+ fixP->fx_r_type == BFD_RELOC_METAG_GETSET_GOTOFF ||
+ fixP->fx_r_type == BFD_RELOC_METAG_GETSET_GOT ||
+ fixP->fx_r_type == BFD_RELOC_METAG_HI16_GOTPC ||
+ fixP->fx_r_type == BFD_RELOC_METAG_LO16_GOTPC ||
+ fixP->fx_r_type == BFD_RELOC_METAG_HI16_PLT ||
+ fixP->fx_r_type == BFD_RELOC_METAG_LO16_PLT ||
+ fixP->fx_r_type == BFD_RELOC_METAG_RELBRANCH_PLT)
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+/* 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 ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ /* No assembler relaxation is defined (or necessary) for this port. */
+ abort ();
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+ /* No assembler relaxation is defined (or necessary) for this port. */
+ abort ();
+}
+
+/* This is called from HANDLE_ALIGN in tc-metag.h. */
+
+void
+metag_handle_align (fragS * fragP)
+{
+ static char const noop[4] = { 0xfe, 0xff, 0xff, 0xa0 };
+ int bytes, fix;
+ char *p;
+
+ if (fragP->fr_type != rs_align_code)
+ return;
+
+ bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ p = fragP->fr_literal + fragP->fr_fix;
+ fix = 0;
+
+ if (bytes & 3)
+ {
+ fix = bytes & 3;
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ while (bytes >= 4)
+ {
+ memcpy (p, noop, 4);
+ p += 4;
+ bytes -= 4;
+ fix += 4;
+ }
+
+ fragP->fr_fix += fix;
+ fragP->fr_var = 4;
+}
+
+static char *
+metag_end_of_match (char * cont, char * what)
+{
+ int len = strlen (what);
+
+ if (strncasecmp (cont, what, strlen (what)) == 0
+ && ! is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+int
+metag_parse_name (char const * name, expressionS * exprP, enum expr_mode mode,
+ char * nextcharP)
+{
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ operatorT op_type;
+ segT segment;
+
+ exprP->X_op_symbol = NULL;
+ exprP->X_md = BFD_RELOC_UNUSED;
+
+ if (strcmp (name, GOT_NAME) == 0)
+ {
+ if (! GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+ no_suffix:
+ /* If we have an absolute symbol or a
+ reg, then we know its value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (mode != expr_defer && segment == absolute_section)
+ {
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else if (mode != expr_defer && segment == reg_section)
+ {
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else
+ {
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+ }
+
+ return 1;
+ }
+
+ exprP->X_add_symbol = symbol_find_or_make (name);
+
+ if (*nextcharP != '@')
+ goto no_suffix;
+ else if ((next_end = metag_end_of_match (next + 1, "GOTOFF")))
+ {
+ reloc_type = BFD_RELOC_METAG_GOTOFF;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "GOT")))
+ {
+ reloc_type = BFD_RELOC_METAG_GETSET_GOT;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "PLT")))
+ {
+ reloc_type = BFD_RELOC_METAG_PLT;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "TLSGD")))
+ {
+ reloc_type = BFD_RELOC_METAG_TLS_GD;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "TLSLDM")))
+ {
+ reloc_type = BFD_RELOC_METAG_TLS_LDM;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "TLSLDO")))
+ {
+ reloc_type = BFD_RELOC_METAG_TLS_LDO;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "TLSIE")))
+ {
+ reloc_type = BFD_RELOC_METAG_TLS_IE;
+ op_type = O_PIC_reloc;
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "TLSIENONPIC")))
+ {
+ reloc_type = BFD_RELOC_METAG_TLS_IENONPIC;
+ op_type = O_PIC_reloc; /* FIXME: is this correct? */
+ }
+ else if ((next_end = metag_end_of_match (next + 1, "TLSLE")))
+ {
+ reloc_type = BFD_RELOC_METAG_TLS_LE;
+ op_type = O_PIC_reloc;
+ }
+ else
+ goto no_suffix;
+
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = op_type;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
+}
+
+/* If while processing a fixup, a reloc really needs to be created
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (seg, fixp)
+ asection *seg ATTRIBUTE_UNUSED;
+ fixS *fixp;
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc->addend = fixp->fx_offset;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ /* xgettext:c-format. */
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+
+ xfree (reloc);
+
+ return NULL;
+ }
+
+ return reloc;
+}
+
+static unsigned int
+md_chars_to_number (char *val, int n)
+{
+ int retval;
+ unsigned char * where = (unsigned char *) val;
+
+ for (retval = 0; n--;)
+ {
+ retval <<= 8;
+ retval |= where[n];
+ }
+ return retval;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int value = (int)*valP;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_METAG_TLS_GD:
+ case BFD_RELOC_METAG_TLS_LE_HI16:
+ case BFD_RELOC_METAG_TLS_LE_LO16:
+ case BFD_RELOC_METAG_TLS_IE:
+ case BFD_RELOC_METAG_TLS_IENONPIC_HI16:
+ case BFD_RELOC_METAG_TLS_IENONPIC_LO16:
+ case BFD_RELOC_METAG_TLS_LDM:
+ case BFD_RELOC_METAG_TLS_LDO_HI16:
+ case BFD_RELOC_METAG_TLS_LDO_LO16:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fall through */
+
+ case BFD_RELOC_METAG_HIADDR16:
+ case BFD_RELOC_METAG_LOADDR16:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = FALSE;
+ break;
+
+ case BFD_RELOC_METAG_REL8:
+ if (!within_unsigned_range (value, IMM8_BITS))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "rel8 out of range %d", value);
+ }
+ else
+ {
+ unsigned int newval;
+ newval = md_chars_to_number (buf, 4);
+ newval = (newval & 0xffffc03f) | ((value & IMM8_MASK) << 6);
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+ case BFD_RELOC_METAG_REL16:
+ if (!within_unsigned_range (value, IMM16_BITS))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "rel16 out of range %d", value);
+ }
+ else
+ {
+ unsigned int newval;
+ newval = md_chars_to_number (buf, 4);
+ newval = (newval & 0xfff80007) | ((value & IMM16_MASK) << 3);
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_8:
+ md_number_to_chars (buf, value, 1);
+ break;
+ case BFD_RELOC_16:
+ md_number_to_chars (buf, value, 2);
+ break;
+ case BFD_RELOC_32:
+ md_number_to_chars (buf, value, 4);
+ break;
+ case BFD_RELOC_64:
+ md_number_to_chars (buf, value, 8);
+
+ case BFD_RELOC_METAG_RELBRANCH:
+ if (!value)
+ break;
+
+ value = value / 4;
+
+ if (!within_signed_range (value, IMM19_BITS))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ "relbranch out of range %d", value);
+ }
+ else
+ {
+ unsigned int newval;
+ newval = md_chars_to_number (buf, 4);
+ newval = (newval & 0xff00001f) | ((value & IMM19_MASK) << 5);
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = TRUE;
+}
diff --git a/gas/config/tc-metag.h b/gas/config/tc-metag.h
new file mode 100644
index 0000000..10a2c7b
--- /dev/null
+++ b/gas/config/tc-metag.h
@@ -0,0 +1,72 @@
+/* tc-metag.h -- Header file for tc-metag.c.
+ Copyright (C) 2013-2014 Free Software Foundation, Inc.
+ Contributed by Imagination Technologies Ltd.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_METAG
+
+#define LISTING_HEADER "META GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_metag
+
+#define TARGET_FORMAT "elf32-metag"
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define tc_fix_adjustable(FIX) metag_fix_adjustable (FIX)
+extern bfd_boolean metag_fix_adjustable (struct fix *);
+
+#define TC_FORCE_RELOCATION(fix) metag_force_relocation (fix)
+extern int metag_force_relocation (struct fix *);
+
+#define TC_HANDLES_FX_DONE
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define HANDLE_ALIGN(fragp) metag_handle_align (fragp)
+extern void metag_handle_align (struct frag *);
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ metag_parse_name ((name), (exprP), (mode), (nextcharP))
+extern int metag_parse_name (char const *, expressionS *, enum expr_mode, char *);
+
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+ symbols. The relocation type is stored in X_md. */
+#define O_PIC_reloc O_md1
+
+#define TC_CASE_SENSITIVE
+
+extern const char metag_symbol_chars[];
+#define tc_symbol_chars metag_symbol_chars
diff --git a/gas/config/tc-microblaze.c b/gas/config/tc-microblaze.c
new file mode 100644
index 0000000..cf4ee44
--- /dev/null
+++ b/gas/config/tc-microblaze.c
@@ -0,0 +1,2524 @@
+/* tc-microblaze.c -- Assemble code for Xilinx MicroBlaze
+
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include <stdio.h>
+#include "bfd.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#include "../opcodes/microblaze-opc.h"
+#include "../opcodes/microblaze-opcm.h"
+#include "safe-ctype.h"
+#include <string.h>
+#include <dwarf2dbg.h>
+#include "aout/stab_gnu.h"
+
+#ifndef streq
+#define streq(a,b) (strcmp (a, b) == 0)
+#endif
+
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+
+void microblaze_generate_symbol (char *sym);
+static bfd_boolean check_spl_reg (unsigned *);
+
+/* Several places in this file insert raw instructions into the
+ object. They should generate the instruction
+ and then use these four macros to crack the instruction value into
+ the appropriate byte values. */
+#define INST_BYTE0(x) (target_big_endian ? (((x) >> 24) & 0xFF) : ((x) & 0xFF))
+#define INST_BYTE1(x) (target_big_endian ? (((x) >> 16) & 0xFF) : (((x) >> 8) & 0xFF))
+#define INST_BYTE2(x) (target_big_endian ? (((x) >> 8) & 0xFF) : (((x) >> 16) & 0xFF))
+#define INST_BYTE3(x) (target_big_endian ? ((x) & 0xFF) : (((x) >> 24) & 0xFF))
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "#";
+
+const char line_separator_chars[] = ";";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. */
+const char line_comment_chars[] = "#";
+
+const int md_reloc_size = 8; /* Size of relocation record. */
+
+/* Chars that can be used to separate mant
+ from exp in floating point numbers. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ As in 0f12.456
+ or 0d1.2345e12. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* INST_PC_OFFSET and INST_NO_OFFSET are 0 and 1. */
+#define UNDEFINED_PC_OFFSET 2
+#define DEFINED_ABS_SEGMENT 3
+#define DEFINED_PC_OFFSET 4
+#define DEFINED_RO_SEGMENT 5
+#define DEFINED_RW_SEGMENT 6
+#define LARGE_DEFINED_PC_OFFSET 7
+#define GOT_OFFSET 8
+#define PLT_OFFSET 9
+#define GOTOFF_OFFSET 10
+#define TLSGD_OFFSET 11
+#define TLSLD_OFFSET 12
+#define TLSDTPMOD_OFFSET 13
+#define TLSDTPREL_OFFSET 14
+#define TLSGOTTPREL_OFFSET 15
+#define TLSTPREL_OFFSET 16
+
+/* Initialize the relax table. */
+const relax_typeS md_relax_table[] =
+{
+ { 1, 1, 0, 0 }, /* 0: Unused. */
+ { 1, 1, 0, 0 }, /* 1: Unused. */
+ { 1, 1, 0, 0 }, /* 2: Unused. */
+ { 1, 1, 0, 0 }, /* 3: Unused. */
+ { 32767, -32768, INST_WORD_SIZE, LARGE_DEFINED_PC_OFFSET }, /* 4: DEFINED_PC_OFFSET. */
+ { 1, 1, 0, 0 }, /* 5: Unused. */
+ { 1, 1, 0, 0 }, /* 6: Unused. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 7: LARGE_DEFINED_PC_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 8: GOT_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 9: PLT_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 10: GOTOFF_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 11: TLSGD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 12: TLSLD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*1, 0 }, /* 13: TLSDTPMOD_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 14: TLSDTPREL_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 }, /* 15: TLSGOTTPREL_OFFSET. */
+ { 0x7fffffff, 0x80000000, INST_WORD_SIZE*2, 0 } /* 16: TLSTPREL_OFFSET. */
+};
+
+static struct hash_control * opcode_hash_control; /* Opcode mnemonics. */
+
+static segT sbss_segment = 0; /* Small bss section. */
+static segT sbss2_segment = 0; /* Section not used. */
+static segT sdata_segment = 0; /* Small data section. */
+static segT sdata2_segment = 0; /* Small read-only section. */
+static segT rodata_segment = 0; /* read-only section. */
+
+/* Generate a symbol for stabs information. */
+
+void
+microblaze_generate_symbol (char *sym)
+{
+#define MICROBLAZE_FAKE_LABEL_NAME "XL0\001"
+ static int microblaze_label_count;
+ sprintf (sym, "%sL%d", MICROBLAZE_FAKE_LABEL_NAME, microblaze_label_count);
+ ++microblaze_label_count;
+}
+
+/* Handle the section changing pseudo-ops. */
+
+static void
+microblaze_s_text (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ obj_elf_text (ignore);
+#else
+ s_text (ignore);
+#endif
+}
+
+static void
+microblaze_s_data (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ obj_elf_change_section (".data", SHT_PROGBITS, SHF_ALLOC+SHF_WRITE, 0, 0, 0, 0);
+#else
+ s_data (ignore);
+#endif
+}
+
+/* Things in the .sdata segment are always considered to be in the small data section. */
+
+static void
+microblaze_s_sdata (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ obj_elf_change_section (".sdata", SHT_PROGBITS, SHF_ALLOC+SHF_WRITE, 0, 0, 0, 0);
+#else
+ s_data (ignore);
+#endif
+}
+
+/* Pseudo op to make file scope bss items. */
+
+static void
+microblaze_s_lcomm (int xxx ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+ offsetT align;
+ char *pfrag;
+ int align2;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Allocate_bss. */
+ if (align)
+ {
+ /* Convert to a power of 2 alignment. */
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2);
+ if (align != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ record_alignment (current_seg, align2);
+ subseg_set (current_seg, current_subseg);
+ if (align2)
+ frag_align (align2, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == current_seg)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, current_seg);
+ subseg_set (current_seg, current_subseg);
+ demand_empty_rest_of_line ();
+}
+
+static void
+microblaze_s_rdata (int localvar)
+{
+#ifdef OBJ_ELF
+ if (localvar == 0)
+ {
+ /* rodata. */
+ obj_elf_change_section (".rodata", SHT_PROGBITS, SHF_ALLOC, 0, 0, 0, 0);
+ if (rodata_segment == 0)
+ rodata_segment = subseg_new (".rodata", 0);
+ }
+ else
+ {
+ /* 1 .sdata2. */
+ obj_elf_change_section (".sdata2", SHT_PROGBITS, SHF_ALLOC, 0, 0, 0, 0);
+ }
+#else
+ s_data (ignore);
+#endif
+}
+
+static void
+microblaze_s_bss (int localvar)
+{
+#ifdef OBJ_ELF
+ if (localvar == 0) /* bss. */
+ obj_elf_change_section (".bss", SHT_NOBITS, SHF_ALLOC+SHF_WRITE, 0, 0, 0, 0);
+ else if (localvar == 1)
+ {
+ /* sbss. */
+ obj_elf_change_section (".sbss", SHT_NOBITS, SHF_ALLOC+SHF_WRITE, 0, 0, 0, 0);
+ if (sbss_segment == 0)
+ sbss_segment = subseg_new (".sbss", 0);
+ }
+#else
+ s_data (ignore);
+#endif
+}
+
+/* endp_p is always 1 as this func is called only for .end <funcname>
+ This func consumes the <funcname> and calls regular processing
+ s_func(1) with arg 1 (1 for end). */
+
+static void
+microblaze_s_func (int end_p ATTRIBUTE_UNUSED)
+{
+ *input_line_pointer = get_symbol_end ();
+ s_func (1);
+}
+
+/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */
+
+static void
+microblaze_s_weakext (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ expressionS exp;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_WEAK (symbolP);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (S_IS_DEFINED (symbolP))
+ {
+ as_bad ("Ignoring attempt to redefine symbol `%s'.",
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_bad ("bad .weakext directive");
+ ignore_rest_of_line ();
+ return;
+ }
+ symbol_set_value_expression (symbolP, &exp);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ Pseudo-op name without dot
+ Function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+/* If the pseudo-op is not found in this table, it searches in the obj-elf.c,
+ and then in the read.c table. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"lcomm", microblaze_s_lcomm, 1},
+ {"data", microblaze_s_data, 0},
+ {"data8", cons, 1}, /* Same as byte. */
+ {"data16", cons, 2}, /* Same as hword. */
+ {"data32", cons, 4}, /* Same as word. */
+ {"ent", s_func, 0}, /* Treat ent as function entry point. */
+ {"end", microblaze_s_func, 1}, /* Treat end as function end point. */
+ {"gpword", s_rva, 4}, /* gpword label => store resolved label address in data section. */
+ {"weakext", microblaze_s_weakext, 0},
+ {"rodata", microblaze_s_rdata, 0},
+ {"sdata2", microblaze_s_rdata, 1},
+ {"sdata", microblaze_s_sdata, 0},
+ {"bss", microblaze_s_bss, 0},
+ {"sbss", microblaze_s_bss, 1},
+ {"text", microblaze_s_text, 0},
+ {"word", cons, 4},
+ {"frame", s_ignore, 0},
+ {"mask", s_ignore, 0}, /* Emitted by gcc. */
+ {NULL, NULL, 0}
+};
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs. */
+
+void
+md_begin (void)
+{
+ struct op_code_struct * opcode;
+
+ opcode_hash_control = hash_new ();
+
+ /* Insert unique names into hash table. */
+ for (opcode = opcodes; opcode->name; opcode ++)
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+}
+
+/* Try to parse a reg name. */
+
+static char *
+parse_reg (char * s, unsigned * reg)
+{
+ unsigned tmpreg = 0;
+
+ /* Strip leading whitespace. */
+ while (ISSPACE (* s))
+ ++ s;
+
+ if (strncasecmp (s, "rpc", 3) == 0)
+ {
+ *reg = REG_PC;
+ return s + 3;
+ }
+ else if (strncasecmp (s, "rmsr", 4) == 0)
+ {
+ *reg = REG_MSR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rear", 4) == 0)
+ {
+ *reg = REG_EAR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "resr", 4) == 0)
+ {
+ *reg = REG_ESR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rfsr", 4) == 0)
+ {
+ *reg = REG_FSR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rbtr", 4) == 0)
+ {
+ *reg = REG_BTR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "redr", 4) == 0)
+ {
+ *reg = REG_EDR;
+ return s + 4;
+ }
+ /* MMU registers start. */
+ else if (strncasecmp (s, "rpid", 4) == 0)
+ {
+ *reg = REG_PID;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rzpr", 4) == 0)
+ {
+ *reg = REG_ZPR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rtlbx", 5) == 0)
+ {
+ *reg = REG_TLBX;
+ return s + 5;
+ }
+ else if (strncasecmp (s, "rtlblo", 6) == 0)
+ {
+ *reg = REG_TLBLO;
+ return s + 6;
+ }
+ else if (strncasecmp (s, "rtlbhi", 6) == 0)
+ {
+ *reg = REG_TLBHI;
+ return s + 6;
+ }
+ else if (strncasecmp (s, "rtlbsx", 6) == 0)
+ {
+ *reg = REG_TLBSX;
+ return s + 6;
+ }
+ /* MMU registers end. */
+ else if (strncasecmp (s, "rpvr", 4) == 0)
+ {
+ if (ISDIGIT (s[4]) && ISDIGIT (s[5]))
+ {
+ tmpreg = (s[4]-'0')*10 + s[5] - '0';
+ s += 6;
+ }
+
+ else if (ISDIGIT (s[4]))
+ {
+ tmpreg = s[4] - '0';
+ s += 5;
+ }
+ else
+ as_bad (_("register expected, but saw '%.6s'"), s);
+ if ((int) tmpreg >= MIN_PVR_REGNUM && tmpreg <= MAX_PVR_REGNUM)
+ *reg = REG_PVR + tmpreg;
+ else
+ {
+ as_bad (_("Invalid register number at '%.6s'"), s);
+ *reg = REG_PVR;
+ }
+ return s;
+ }
+ else if (strncasecmp (s, "rsp", 3) == 0)
+ {
+ *reg = REG_SP;
+ return s + 3;
+ }
+ else if (strncasecmp (s, "rfsl", 4) == 0)
+ {
+ if (ISDIGIT (s[4]) && ISDIGIT (s[5]))
+ {
+ tmpreg = (s[4] - '0') * 10 + s[5] - '0';
+ s += 6;
+ }
+ else if (ISDIGIT (s[4]))
+ {
+ tmpreg = s[4] - '0';
+ s += 5;
+ }
+ else
+ as_bad (_("register expected, but saw '%.6s'"), s);
+
+ if ((int) tmpreg >= MIN_REGNUM && tmpreg <= MAX_REGNUM)
+ *reg = tmpreg;
+ else
+ {
+ as_bad (_("Invalid register number at '%.6s'"), s);
+ *reg = 0;
+ }
+ return s;
+ }
+ /* Stack protection registers. */
+ else if (strncasecmp (s, "rshr", 4) == 0)
+ {
+ *reg = REG_SHR;
+ return s + 4;
+ }
+ else if (strncasecmp (s, "rslr", 4) == 0)
+ {
+ *reg = REG_SLR;
+ return s + 4;
+ }
+ else
+ {
+ if (TOLOWER (s[0]) == 'r')
+ {
+ if (ISDIGIT (s[1]) && ISDIGIT (s[2]))
+ {
+ tmpreg = (s[1] - '0') * 10 + s[2] - '0';
+ s += 3;
+ }
+ else if (ISDIGIT (s[1]))
+ {
+ tmpreg = s[1] - '0';
+ s += 2;
+ }
+ else
+ as_bad (_("register expected, but saw '%.6s'"), s);
+
+ if ((int)tmpreg >= MIN_REGNUM && tmpreg <= MAX_REGNUM)
+ *reg = tmpreg;
+ else
+ {
+ as_bad (_("Invalid register number at '%.6s'"), s);
+ *reg = 0;
+ }
+ return s;
+ }
+ }
+ as_bad (_("register expected, but saw '%.6s'"), s);
+ *reg = 0;
+ return s;
+}
+
+static char *
+parse_exp (char *s, expressionS *e)
+{
+ char *save;
+ char *new_pointer;
+
+ /* Skip whitespace. */
+ while (ISSPACE (* s))
+ ++ s;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+
+ expression (e);
+
+ if (e->X_op == O_absent)
+ as_fatal (_("missing operand"));
+
+ new_pointer = input_line_pointer;
+ input_line_pointer = save;
+
+ return new_pointer;
+}
+
+/* Symbol modifiers (@GOT, @PLT, @GOTOFF). */
+#define IMM_NONE 0
+#define IMM_GOT 1
+#define IMM_PLT 2
+#define IMM_GOTOFF 3
+#define IMM_TLSGD 4
+#define IMM_TLSLD 5
+#define IMM_TLSDTPMOD 6
+#define IMM_TLSDTPREL 7
+#define IMM_TLSTPREL 8
+#define IMM_MAX 9
+
+struct imm_type {
+ char *isuffix; /* Suffix String */
+ int itype; /* Suffix Type */
+ int otype; /* Offset Type */
+};
+
+/* These are NOT in assending order of type, GOTOFF is ahead to make
+ sure @GOTOFF does not get matched with @GOT */
+static struct imm_type imm_types[] = {
+ { "NONE", IMM_NONE , 0 },
+ { "GOTOFF", IMM_GOTOFF , GOTOFF_OFFSET },
+ { "GOT", IMM_GOT , GOT_OFFSET },
+ { "PLT", IMM_PLT , PLT_OFFSET },
+ { "TLSGD", IMM_TLSGD , TLSGD_OFFSET },
+ { "TLSLDM", IMM_TLSLD, TLSLD_OFFSET },
+ { "TLSDTPMOD", IMM_TLSDTPMOD, TLSDTPMOD_OFFSET },
+ { "TLSDTPREL", IMM_TLSDTPREL, TLSDTPREL_OFFSET },
+ { "TLSTPREL", IMM_TLSTPREL, TLSTPREL_OFFSET }
+};
+
+static int
+match_imm (const char *s, int *ilen)
+{
+ int i;
+ int slen;
+
+ /* Check for matching suffix */
+ for (i = 1; i < IMM_MAX; i++)
+ {
+ slen = strlen (imm_types[i].isuffix);
+
+ if (strncmp (imm_types[i].isuffix, s, slen) == 0)
+ {
+ *ilen = slen;
+ return imm_types[i].itype;
+ }
+ } /* for */
+ *ilen = 0;
+ return 0;
+}
+
+static int
+get_imm_otype (int itype)
+{
+ int i, otype;
+
+ otype = 0;
+ /* Check for matching itype */
+ for (i = 1; i < IMM_MAX; i++)
+ {
+ if (imm_types[i].itype == itype)
+ {
+ otype = imm_types[i].otype;
+ break;
+ }
+ }
+ return otype;
+}
+
+static symbolS * GOT_symbol;
+
+#define GOT_SYMBOL_NAME "_GLOBAL_OFFSET_TABLE_"
+
+static char *
+parse_imm (char * s, expressionS * e, int min, int max)
+{
+ char *new_pointer;
+ char *atp;
+ int itype, ilen;
+
+ ilen = 0;
+
+ /* Find the start of "@GOT" or "@PLT" suffix (if any) */
+ for (atp = s; *atp != '@'; atp++)
+ if (is_end_of_line[(unsigned char) *atp])
+ break;
+
+ if (*atp == '@')
+ {
+ itype = match_imm (atp + 1, &ilen);
+ if (itype != 0)
+ {
+ *atp = 0;
+ e->X_md = itype;
+ }
+ else
+ {
+ atp = NULL;
+ e->X_md = 0;
+ ilen = 0;
+ }
+ *atp = 0;
+ }
+ else
+ {
+ atp = NULL;
+ e->X_md = 0;
+ }
+
+ if (atp && !GOT_symbol)
+ {
+ GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
+ }
+
+ new_pointer = parse_exp (s, e);
+
+ if (!GOT_symbol && ! strncmp (s, GOT_SYMBOL_NAME, 20))
+ {
+ GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
+ }
+
+ if (e->X_op == O_absent)
+ ; /* An error message has already been emitted. */
+ else if ((e->X_op != O_constant && e->X_op != O_symbol) )
+ as_fatal (_("operand must be a constant or a label"));
+ else if ((e->X_op == O_constant) && ((int) e->X_add_number < min
+ || (int) e->X_add_number > max))
+ {
+ as_fatal (_("operand must be absolute in range %d..%d, not %d"),
+ min, max, (int) e->X_add_number);
+ }
+
+ if (atp)
+ {
+ *atp = '@'; /* restore back (needed?) */
+ if (new_pointer >= atp)
+ new_pointer += ilen + 1; /* sizeof (imm_suffix) + 1 for '@' */
+ }
+ return new_pointer;
+}
+
+static char *
+check_got (int * got_type, int * got_len)
+{
+ char *new_pointer;
+ char *atp;
+ char *past_got;
+ int first, second;
+ char *tmpbuf;
+
+ /* Find the start of "@GOT" or "@PLT" suffix (if any). */
+ for (atp = input_line_pointer; *atp != '@'; atp++)
+ if (is_end_of_line[(unsigned char) *atp])
+ return NULL;
+
+ if (strncmp (atp + 1, "GOTOFF", 5) == 0)
+ {
+ *got_len = 6;
+ *got_type = IMM_GOTOFF;
+ }
+ else if (strncmp (atp + 1, "GOT", 3) == 0)
+ {
+ *got_len = 3;
+ *got_type = IMM_GOT;
+ }
+ else if (strncmp (atp + 1, "PLT", 3) == 0)
+ {
+ *got_len = 3;
+ *got_type = IMM_PLT;
+ }
+ else
+ return NULL;
+
+ if (!GOT_symbol)
+ GOT_symbol = symbol_find_or_make (GOT_SYMBOL_NAME);
+
+ first = atp - input_line_pointer;
+
+ past_got = atp + *got_len + 1;
+ for (new_pointer = past_got; !is_end_of_line[(unsigned char) *new_pointer++];)
+ ;
+ second = new_pointer - past_got;
+ tmpbuf = xmalloc (first + second + 2); /* One extra byte for ' ' and one for NUL. */
+ memcpy (tmpbuf, input_line_pointer, first);
+ tmpbuf[first] = ' '; /* @GOTOFF is replaced with a single space. */
+ memcpy (tmpbuf + first + 1, past_got, second);
+ tmpbuf[first + second + 1] = '\0';
+
+ return tmpbuf;
+}
+
+extern bfd_reloc_code_real_type
+parse_cons_expression_microblaze (expressionS *exp, int size)
+{
+ if (size == 4)
+ {
+ /* Handle @GOTOFF et.al. */
+ char *save, *gotfree_copy;
+ int got_len, got_type;
+
+ save = input_line_pointer;
+ gotfree_copy = check_got (& got_type, & got_len);
+ if (gotfree_copy)
+ input_line_pointer = gotfree_copy;
+
+ expression (exp);
+
+ if (gotfree_copy)
+ {
+ exp->X_md = got_type;
+ input_line_pointer = save + (input_line_pointer - gotfree_copy)
+ + got_len;
+ free (gotfree_copy);
+ }
+ }
+ else
+ expression (exp);
+ return BFD_RELOC_NONE;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+static char * str_microblaze_ro_anchor = "RO";
+static char * str_microblaze_rw_anchor = "RW";
+
+static bfd_boolean
+check_spl_reg (unsigned * reg)
+{
+ if ((*reg == REG_MSR) || (*reg == REG_PC)
+ || (*reg == REG_EAR) || (*reg == REG_ESR)
+ || (*reg == REG_FSR) || (*reg == REG_BTR) || (*reg == REG_EDR)
+ || (*reg == REG_PID) || (*reg == REG_ZPR)
+ || (*reg == REG_TLBX) || (*reg == REG_TLBLO)
+ || (*reg == REG_TLBHI) || (*reg == REG_TLBSX)
+ || (*reg == REG_SHR) || (*reg == REG_SLR)
+ || (*reg >= REG_PVR+MIN_PVR_REGNUM && *reg <= REG_PVR+MAX_PVR_REGNUM))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Here we decide which fixups can be adjusted to make them relative to
+ the beginning of the section instead of the symbol. Basically we need
+ to make sure that the dynamic relocations are done correctly, so in
+ some cases we force the original symbol to be used. */
+
+int
+tc_microblaze_fix_adjustable (struct fix *fixP)
+{
+ if (GOT_symbol && fixP->fx_subsy == GOT_symbol)
+ return 0;
+
+ if (fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOT
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSLD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPMOD
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_32_TLSDTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSDTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL
+ || fixP->fx_r_type == BFD_RELOC_MICROBLAZE_64_TLSTPREL)
+ return 0;
+
+ return 1;
+}
+
+void
+md_assemble (char * str)
+{
+ char * op_start;
+ char * op_end;
+ struct op_code_struct * opcode, *opcode1;
+ char * output = NULL;
+ int nlen = 0;
+ int i;
+ unsigned long inst, inst1;
+ unsigned reg1;
+ unsigned reg2;
+ unsigned reg3;
+ unsigned isize;
+ unsigned int immed, temp;
+ expressionS exp;
+ char name[20];
+
+ /* Drop leading whitespace. */
+ while (ISSPACE (* str))
+ str ++;
+
+ /* Find the op code end. */
+ for (op_start = op_end = str;
+ *op_end && !is_end_of_line[(unsigned char) *op_end] && *op_end != ' ';
+ op_end++)
+ {
+ name[nlen] = op_start[nlen];
+ nlen++;
+ if (nlen == sizeof (name) - 1)
+ break;
+ }
+
+ name [nlen] = 0;
+
+ if (nlen == 0)
+ {
+ as_bad (_("can't find opcode "));
+ return;
+ }
+
+ opcode = (struct op_code_struct *) hash_find (opcode_hash_control, name);
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), name);
+ return;
+ }
+
+ inst = opcode->bit_sequence;
+ isize = 4;
+
+ switch (opcode->inst_type)
+ {
+ case INST_TYPE_RD_R1_R2:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg3); /* Get r2. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg3 = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (& reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (& reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (& reg3))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ if (streq (name, "sub"))
+ {
+ /* sub rd, r1, r2 becomes rsub rd, r2, r1. */
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg3 << RA_LOW) & RA_MASK;
+ inst |= (reg2 << RB_LOW) & RB_MASK;
+ }
+ else
+ {
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ inst |= (reg3 << RB_LOW) & RB_MASK;
+ }
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RD_R1_IMM:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
+ else
+ as_fatal (_("Error in statement syntax"));
+
+ /* Check for spl registers. */
+ if (check_spl_reg (& reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (& reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ if (exp.X_op != O_constant)
+ {
+ char *opc;
+ relax_substateT subtype;
+
+ if (streq (name, "lmi"))
+ as_fatal (_("lmi pseudo instruction should not use a label in imm field"));
+ else if (streq (name, "smi"))
+ as_fatal (_("smi pseudo instruction should not use a label in imm field"));
+
+ if (reg2 == REG_ROSDP)
+ opc = str_microblaze_ro_anchor;
+ else if (reg2 == REG_RWSDP)
+ opc = str_microblaze_rw_anchor;
+ else
+ opc = NULL;
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
+ subtype = opcode->inst_offset_type;
+
+ output = frag_var (rs_machine_dependent,
+ isize * 2, /* maxm of 2 words. */
+ isize, /* minm of 1 word. */
+ subtype, /* PC-relative or not. */
+ exp.X_add_symbol,
+ exp.X_add_number,
+ opc);
+ immed = 0;
+ }
+ else
+ {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+
+ if (streq (name, "lmi") || streq (name, "smi"))
+ {
+ /* Load/store 32-d consecutive registers. Used on exit/entry
+ to subroutines to save and restore registers to stack.
+ Generate 32-d insts. */
+ int count;
+
+ count = 32 - reg1;
+ if (streq (name, "lmi"))
+ opcode = (struct op_code_struct *) hash_find (opcode_hash_control, "lwi");
+ else
+ opcode = (struct op_code_struct *) hash_find (opcode_hash_control, "swi");
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "lwi");
+ return;
+ }
+ inst = opcode->bit_sequence;
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+
+ for (i = 0; i < count - 1; i++)
+ {
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+ output[2] = INST_BYTE2 (inst);
+ output[3] = INST_BYTE3 (inst);
+ output = frag_more (isize);
+ immed = immed + 4;
+ reg1++;
+ inst = opcode->bit_sequence;
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ }
+ }
+ else
+ {
+ temp = immed & 0xFFFF8000;
+ if ((temp != 0) && (temp != 0xFFFF8000))
+ {
+ /* Needs an immediate inst. */
+ opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
+ if (opcode1 == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "imm");
+ return;
+ }
+
+ inst1 = opcode1->bit_sequence;
+ inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
+ output[0] = INST_BYTE0 (inst1);
+ output[1] = INST_BYTE1 (inst1);
+ output[2] = INST_BYTE2 (inst1);
+ output[3] = INST_BYTE3 (inst1);
+ output = frag_more (isize);
+ }
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ }
+ break;
+
+ case INST_TYPE_RD_R1_IMM5:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
+ else
+ as_fatal (_("Error in statement syntax"));
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (&reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ if (exp.X_op != O_constant)
+ as_warn (_("Symbol used as immediate for shift instruction"));
+ else
+ {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+
+ if (immed != (immed % 32))
+ {
+ as_warn (_("Shift value > 32. using <value %% 32>"));
+ immed = immed % 32;
+ }
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & IMM5_MASK;
+ break;
+
+ case INST_TYPE_R1_R2:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r2. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (& reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (& reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RA_LOW) & RA_MASK;
+ inst |= (reg2 << RB_LOW) & RB_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RD_R1:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 =0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (&reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RD_RFSL:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &immed); /* Get rfslN. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ immed = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (immed << IMM_LOW) & RFSL_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RD_IMM15:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+
+ if (strcmp (op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM15, MAX_IMM15);
+ else
+ as_fatal (_("Error in statement syntax"));
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ if (exp.X_op != O_constant)
+ as_fatal (_("Symbol used as immediate value for msrset/msrclr instructions"));
+ else
+ {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (immed << IMM_LOW) & IMM15_MASK;
+ break;
+
+ case INST_TYPE_R1_RFSL:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &immed); /* Get rfslN. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ immed = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & RFSL_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RFSL:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &immed); /* Get rfslN. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ immed = 0;
+ }
+ inst |= (immed << IMM_LOW) & RFSL_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_R1:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RA_LOW) & RA_MASK;
+ output = frag_more (isize);
+ break;
+
+ /* For tuqula insn...:) */
+ case INST_TYPE_RD:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RD_SPECIAL:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+
+ if (reg2 == REG_MSR)
+ immed = opcode->immval_mask | REG_MSR_MASK;
+ else if (reg2 == REG_PC)
+ immed = opcode->immval_mask | REG_PC_MASK;
+ else if (reg2 == REG_EAR)
+ immed = opcode->immval_mask | REG_EAR_MASK;
+ else if (reg2 == REG_ESR)
+ immed = opcode->immval_mask | REG_ESR_MASK;
+ else if (reg2 == REG_FSR)
+ immed = opcode->immval_mask | REG_FSR_MASK;
+ else if (reg2 == REG_BTR)
+ immed = opcode->immval_mask | REG_BTR_MASK;
+ else if (reg2 == REG_EDR)
+ immed = opcode->immval_mask | REG_EDR_MASK;
+ else if (reg2 == REG_PID)
+ immed = opcode->immval_mask | REG_PID_MASK;
+ else if (reg2 == REG_ZPR)
+ immed = opcode->immval_mask | REG_ZPR_MASK;
+ else if (reg2 == REG_TLBX)
+ immed = opcode->immval_mask | REG_TLBX_MASK;
+ else if (reg2 == REG_TLBLO)
+ immed = opcode->immval_mask | REG_TLBLO_MASK;
+ else if (reg2 == REG_TLBHI)
+ immed = opcode->immval_mask | REG_TLBHI_MASK;
+ else if (reg2 == REG_SHR)
+ immed = opcode->immval_mask | REG_SHR_MASK;
+ else if (reg2 == REG_SLR)
+ immed = opcode->immval_mask | REG_SLR_MASK;
+ else if (reg2 >= (REG_PVR+MIN_PVR_REGNUM) && reg2 <= (REG_PVR+MAX_PVR_REGNUM))
+ immed = opcode->immval_mask | REG_PVR_MASK | reg2;
+ else
+ as_fatal (_("invalid value for special purpose register"));
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_SPECIAL_R1:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+
+ if (reg1 == REG_MSR)
+ immed = opcode->immval_mask | REG_MSR_MASK;
+ else if (reg1 == REG_PC)
+ immed = opcode->immval_mask | REG_PC_MASK;
+ else if (reg1 == REG_EAR)
+ immed = opcode->immval_mask | REG_EAR_MASK;
+ else if (reg1 == REG_ESR)
+ immed = opcode->immval_mask | REG_ESR_MASK;
+ else if (reg1 == REG_FSR)
+ immed = opcode->immval_mask | REG_FSR_MASK;
+ else if (reg1 == REG_BTR)
+ immed = opcode->immval_mask | REG_BTR_MASK;
+ else if (reg1 == REG_EDR)
+ immed = opcode->immval_mask | REG_EDR_MASK;
+ else if (reg1 == REG_PID)
+ immed = opcode->immval_mask | REG_PID_MASK;
+ else if (reg1 == REG_ZPR)
+ immed = opcode->immval_mask | REG_ZPR_MASK;
+ else if (reg1 == REG_TLBX)
+ immed = opcode->immval_mask | REG_TLBX_MASK;
+ else if (reg1 == REG_TLBLO)
+ immed = opcode->immval_mask | REG_TLBLO_MASK;
+ else if (reg1 == REG_TLBHI)
+ immed = opcode->immval_mask | REG_TLBHI_MASK;
+ else if (reg1 == REG_TLBSX)
+ immed = opcode->immval_mask | REG_TLBSX_MASK;
+ else if (reg1 == REG_SHR)
+ immed = opcode->immval_mask | REG_SHR_MASK;
+ else if (reg1 == REG_SLR)
+ immed = opcode->immval_mask | REG_SLR_MASK;
+ else
+ as_fatal (_("invalid value for special purpose register"));
+ inst |= (reg2 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_R1_R2_SPECIAL:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r2. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 =0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (&reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ /* insn wic ra, rb => wic ra, ra, rb. */
+ inst |= (reg1 << RA_LOW) & RA_MASK;
+ inst |= (reg2 << RB_LOW) & RB_MASK;
+
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_RD_R2:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r2. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+ if (check_spl_reg (&reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (reg2 << RB_LOW) & RB_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_R1_IMM:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get r1. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
+ else
+ as_fatal (_("Error in statement syntax"));
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ if (exp.X_op != O_constant)
+ {
+ char *opc = NULL;
+ relax_substateT subtype;
+
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
+ subtype = opcode->inst_offset_type;
+
+ output = frag_var (rs_machine_dependent,
+ isize * 2, /* maxm of 2 words. */
+ isize, /* minm of 1 word. */
+ subtype, /* PC-relative or not. */
+ exp.X_add_symbol,
+ exp.X_add_number,
+ opc);
+ immed = 0;
+ }
+ else
+ {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+
+ temp = immed & 0xFFFF8000;
+ if ((temp != 0) && (temp != 0xFFFF8000))
+ {
+ /* Needs an immediate inst. */
+ opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
+ if (opcode1 == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "imm");
+ return;
+ }
+
+ inst1 = opcode1->bit_sequence;
+ inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
+ output[0] = INST_BYTE0 (inst1);
+ output[1] = INST_BYTE1 (inst1);
+ output[2] = INST_BYTE2 (inst1);
+ output[3] = INST_BYTE3 (inst1);
+ output = frag_more (isize);
+ }
+
+ inst |= (reg1 << RA_LOW) & RA_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ break;
+
+ case INST_TYPE_RD_IMM:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg1); /* Get rd. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg1 = 0;
+ }
+ if (strcmp (op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
+ else
+ as_fatal (_("Error in statement syntax"));
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg1))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ if (exp.X_op != O_constant)
+ {
+ char *opc = NULL;
+ relax_substateT subtype;
+
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
+ subtype = opcode->inst_offset_type;
+
+ output = frag_var (rs_machine_dependent,
+ isize * 2, /* maxm of 2 words. */
+ isize, /* minm of 1 word. */
+ subtype, /* PC-relative or not. */
+ exp.X_add_symbol,
+ exp.X_add_number,
+ opc);
+ immed = 0;
+ }
+ else
+ {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+
+ temp = immed & 0xFFFF8000;
+ if ((temp != 0) && (temp != 0xFFFF8000))
+ {
+ /* Needs an immediate inst. */
+ opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
+ if (opcode1 == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "imm");
+ return;
+ }
+
+ inst1 = opcode1->bit_sequence;
+ inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
+ output[0] = INST_BYTE0 (inst1);
+ output[1] = INST_BYTE1 (inst1);
+ output[2] = INST_BYTE2 (inst1);
+ output[3] = INST_BYTE3 (inst1);
+ output = frag_more (isize);
+ }
+
+ inst |= (reg1 << RD_LOW) & RD_MASK;
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ break;
+
+ case INST_TYPE_R2:
+ if (strcmp (op_end, ""))
+ op_end = parse_reg (op_end + 1, &reg2); /* Get r2. */
+ else
+ {
+ as_fatal (_("Error in statement syntax"));
+ reg2 = 0;
+ }
+
+ /* Check for spl registers. */
+ if (check_spl_reg (&reg2))
+ as_fatal (_("Cannot use special register with this instruction"));
+
+ inst |= (reg2 << RB_LOW) & RB_MASK;
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_IMM:
+ if (streq (name, "imm"))
+ as_fatal (_("An IMM instruction should not be present in the .s file"));
+
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM, MAX_IMM);
+
+ if (exp.X_op != O_constant)
+ {
+ char *opc = NULL;
+ relax_substateT subtype;
+
+ if (exp.X_md != 0)
+ subtype = get_imm_otype(exp.X_md);
+ else
+ subtype = opcode->inst_offset_type;
+
+ output = frag_var (rs_machine_dependent,
+ isize * 2, /* maxm of 2 words. */
+ isize, /* minm of 1 word. */
+ subtype, /* PC-relative or not. */
+ exp.X_add_symbol,
+ exp.X_add_number,
+ opc);
+ immed = 0;
+ }
+ else
+ {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+
+
+ temp = immed & 0xFFFF8000;
+ if ((temp != 0) && (temp != 0xFFFF8000))
+ {
+ /* Needs an immediate inst. */
+ opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
+ if (opcode1 == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "imm");
+ return;
+ }
+
+ inst1 = opcode1->bit_sequence;
+ inst1 |= ((immed & 0xFFFF0000) >> 16) & IMM_MASK;
+ output[0] = INST_BYTE0 (inst1);
+ output[1] = INST_BYTE1 (inst1);
+ output[2] = INST_BYTE2 (inst1);
+ output[3] = INST_BYTE3 (inst1);
+ output = frag_more (isize);
+ }
+ inst |= (immed << IMM_LOW) & IMM_MASK;
+ break;
+
+ case INST_TYPE_NONE:
+ output = frag_more (isize);
+ break;
+
+ case INST_TYPE_IMM5:
+ if (strcmp(op_end, ""))
+ op_end = parse_imm (op_end + 1, & exp, MIN_IMM5, MAX_IMM5);
+ else
+ as_fatal(_("Error in statement syntax"));
+ if (exp.X_op != O_constant) {
+ as_warn(_("Symbol used as immediate for mbar instruction"));
+ } else {
+ output = frag_more (isize);
+ immed = exp.X_add_number;
+ }
+ if (immed != (immed % 32)) {
+ as_warn(_("Immediate value for mbar > 32. using <value %% 32>"));
+ immed = immed % 32;
+ }
+ inst |= (immed << IMM_MBAR);
+ break;
+
+ default:
+ as_fatal (_("unimplemented opcode \"%s\""), name);
+ }
+
+ /* Drop whitespace after all the operands have been parsed. */
+ while (ISSPACE (* op_end))
+ op_end ++;
+
+ /* Give warning message if the insn has more operands than required. */
+ if (strcmp (op_end, opcode->name) && strcmp (op_end, ""))
+ as_warn (_("ignoring operands: %s "), op_end);
+
+ output[0] = INST_BYTE0 (inst);
+ output[1] = INST_BYTE1 (inst);
+ output[2] = INST_BYTE2 (inst);
+ output[3] = INST_BYTE3 (inst);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (4);
+#endif
+}
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Various routines to kill one day. */
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* 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.*/
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ int i;
+ 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;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Bad call to MD_NTOF()");
+ }
+
+ t = atof_ieee (input_line_pointer, type, words);
+
+ if (t)
+ input_line_pointer = t;
+
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ if (! target_big_endian)
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ return NULL;
+}
+
+const char * md_shortopts = "";
+
+struct option md_longopts[] =
+{
+ {"EB", no_argument, NULL, OPTION_EB},
+ {"EL", no_argument, NULL, OPTION_EL},
+ { NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int md_short_jump_size;
+
+void
+md_create_short_jump (char * ptr ATTRIBUTE_UNUSED,
+ addressT from_Nddr ATTRIBUTE_UNUSED,
+ addressT to_Nddr ATTRIBUTE_UNUSED,
+ fragS * frag ATTRIBUTE_UNUSED,
+ symbolS * to_symbol ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("failed sanity check: short_jump"));
+}
+
+void
+md_create_long_jump (char * ptr ATTRIBUTE_UNUSED,
+ addressT from_Nddr ATTRIBUTE_UNUSED,
+ addressT to_Nddr ATTRIBUTE_UNUSED,
+ fragS * frag ATTRIBUTE_UNUSED,
+ symbolS * to_symbol ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("failed sanity check: long_jump"));
+}
+
+/* Called after relaxing, change the frags so they know how big they are. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP)
+{
+ fixS *fixP;
+
+ switch (fragP->fr_subtype)
+ {
+ case UNDEFINED_PC_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, BFD_RELOC_64_PCREL);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case DEFINED_ABS_SEGMENT:
+ if (fragP->fr_symbol == GOT_symbol)
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, BFD_RELOC_MICROBLAZE_64_GOTPC);
+ else
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_64);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case DEFINED_RO_SEGMENT:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_32_ROSDA);
+ fragP->fr_fix += INST_WORD_SIZE;
+ fragP->fr_var = 0;
+ break;
+ case DEFINED_RW_SEGMENT:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_32_RWSDA);
+ fragP->fr_fix += INST_WORD_SIZE;
+ fragP->fr_var = 0;
+ break;
+ case DEFINED_PC_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, BFD_RELOC_MICROBLAZE_32_LO_PCREL);
+ fragP->fr_fix += INST_WORD_SIZE;
+ fragP->fr_var = 0;
+ break;
+ case LARGE_DEFINED_PC_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, BFD_RELOC_64_PCREL);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case GOT_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_GOT);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case PLT_OFFSET:
+ fixP = fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, BFD_RELOC_MICROBLAZE_64_PLT);
+ /* fixP->fx_plt = 1; */
+ (void) fixP;
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case GOTOFF_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_GOTOFF);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSGD_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSGD);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSLD_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSLD);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+ case TLSDTPREL_OFFSET:
+ fix_new (fragP, fragP->fr_fix, INST_WORD_SIZE * 2, fragP->fr_symbol,
+ fragP->fr_offset, FALSE, BFD_RELOC_MICROBLAZE_64_TLSDTPREL);
+ fragP->fr_fix += INST_WORD_SIZE * 2;
+ fragP->fr_var = 0;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Applies the desired value to the specified location.
+ Also sets up addends for 'rela' type relocations. */
+void
+md_apply_fix (fixS * fixP,
+ valueT * valp,
+ segT segment)
+{
+ char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ char * file = fixP->fx_file ? fixP->fx_file : _("unknown");
+ const char * symname;
+ /* Note: use offsetT because it is signed, valueT is unsigned. */
+ offsetT val = (offsetT) * valp;
+ int i;
+ struct op_code_struct * opcode1;
+ unsigned long inst1;
+
+ symname = fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : _("<unknown>");
+
+ /* fixP->fx_offset is supposed to be set up correctly for all
+ symbol relocations. */
+ if (fixP->fx_addsy == NULL)
+ {
+ if (!fixP->fx_pcrel)
+ fixP->fx_offset = val; /* Absolute relocation. */
+ else
+ fprintf (stderr, "NULL symbol PC-relative relocation? offset = %08x, val = %08x\n",
+ (unsigned int) fixP->fx_offset, (unsigned int) val);
+ }
+
+ /* If we aren't adjusting this fixup to be against the section
+ symbol, we need to adjust the value. */
+ if (fixP->fx_addsy != NULL)
+ {
+ if (S_IS_WEAK (fixP->fx_addsy)
+ || (symbol_used_in_reloc_p (fixP->fx_addsy)
+ && (((bfd_get_section_flags (stdoutput,
+ S_GET_SEGMENT (fixP->fx_addsy))
+ & SEC_LINK_ONCE) != 0)
+ || !strncmp (segment_name (S_GET_SEGMENT (fixP->fx_addsy)),
+ ".gnu.linkonce",
+ sizeof (".gnu.linkonce") - 1))))
+ {
+ val -= S_GET_VALUE (fixP->fx_addsy);
+ if (val != 0 && ! fixP->fx_pcrel)
+ {
+ /* In this case, the bfd_install_relocation routine will
+ incorrectly add the symbol value back in. We just want
+ the addend to appear in the object file.
+ FIXME: If this makes VALUE zero, we're toast. */
+ val -= S_GET_VALUE (fixP->fx_addsy);
+ }
+ }
+ }
+
+ /* If the fix is relative to a symbol which is not defined, or not
+ in the same segment as the fix, we cannot resolve it here. */
+ /* fixP->fx_addsy is NULL if valp contains the entire relocation. */
+ if (fixP->fx_addsy != NULL
+ && (!S_IS_DEFINED (fixP->fx_addsy)
+ || (S_GET_SEGMENT (fixP->fx_addsy) != segment)))
+ {
+ fixP->fx_done = 0;
+#ifdef OBJ_ELF
+ /* For ELF we can just return and let the reloc that will be generated
+ take care of everything. For COFF we still have to insert 'val'
+ into the insn since the addend field will be ignored. */
+ /* return; */
+#endif
+ }
+ /* All fixups in the text section must be handled in the linker. */
+ else if (segment->flags & SEC_CODE)
+ fixP->fx_done = 0;
+ else if (!fixP->fx_pcrel && fixP->fx_addsy != NULL)
+ fixP->fx_done = 0;
+ else
+ fixP->fx_done = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MICROBLAZE_32_LO:
+ case BFD_RELOC_MICROBLAZE_32_LO_PCREL:
+ if (target_big_endian)
+ {
+ buf[2] |= ((val >> 8) & 0xff);
+ buf[3] |= (val & 0xff);
+ }
+ else
+ {
+ buf[1] |= ((val >> 8) & 0xff);
+ buf[0] |= (val & 0xff);
+ }
+ break;
+ case BFD_RELOC_MICROBLAZE_32_ROSDA:
+ case BFD_RELOC_MICROBLAZE_32_RWSDA:
+ /* Don't do anything if the symbol is not defined. */
+ if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
+ {
+ if (((val & 0xFFFF8000) != 0) && ((val & 0xFFFF8000) != 0xFFFF8000))
+ as_bad_where (file, fixP->fx_line,
+ _("pcrel for branch to %s too far (0x%x)"),
+ symname, (int) val);
+ if (target_big_endian)
+ {
+ buf[2] |= ((val >> 8) & 0xff);
+ buf[3] |= (val & 0xff);
+ }
+ else
+ {
+ buf[1] |= ((val >> 8) & 0xff);
+ buf[0] |= (val & 0xff);
+ }
+ }
+ break;
+ case BFD_RELOC_32:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM:
+ /* Don't do anything if the symbol is not defined. */
+ if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
+ {
+ if (target_big_endian)
+ {
+ buf[0] |= ((val >> 24) & 0xff);
+ buf[1] |= ((val >> 16) & 0xff);
+ buf[2] |= ((val >> 8) & 0xff);
+ buf[3] |= (val & 0xff);
+ }
+ else
+ {
+ buf[3] |= ((val >> 24) & 0xff);
+ buf[2] |= ((val >> 16) & 0xff);
+ buf[1] |= ((val >> 8) & 0xff);
+ buf[0] |= (val & 0xff);
+ }
+ }
+ break;
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_64:
+ /* Add an imm instruction. First save the current instruction. */
+ for (i = 0; i < INST_WORD_SIZE; i++)
+ buf[i + INST_WORD_SIZE] = buf[i];
+
+ /* Generate the imm instruction. */
+ opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
+ if (opcode1 == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "imm");
+ return;
+ }
+
+ inst1 = opcode1->bit_sequence;
+ if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
+ inst1 |= ((val & 0xFFFF0000) >> 16) & IMM_MASK;
+
+ buf[0] = INST_BYTE0 (inst1);
+ buf[1] = INST_BYTE1 (inst1);
+ buf[2] = INST_BYTE2 (inst1);
+ buf[3] = INST_BYTE3 (inst1);
+
+ /* Add the value only if the symbol is defined. */
+ if (fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
+ {
+ if (target_big_endian)
+ {
+ buf[6] |= ((val >> 8) & 0xff);
+ buf[7] |= (val & 0xff);
+ }
+ else
+ {
+ buf[5] |= ((val >> 8) & 0xff);
+ buf[4] |= (val & 0xff);
+ }
+ }
+ break;
+
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+
+ case BFD_RELOC_MICROBLAZE_64_GOTPC:
+ case BFD_RELOC_MICROBLAZE_64_GOT:
+ case BFD_RELOC_MICROBLAZE_64_PLT:
+ case BFD_RELOC_MICROBLAZE_64_GOTOFF:
+ /* Add an imm instruction. First save the current instruction. */
+ for (i = 0; i < INST_WORD_SIZE; i++)
+ buf[i + INST_WORD_SIZE] = buf[i];
+
+ /* Generate the imm instruction. */
+ opcode1 = (struct op_code_struct *) hash_find (opcode_hash_control, "imm");
+ if (opcode1 == NULL)
+ {
+ as_bad (_("unknown opcode \"%s\""), "imm");
+ return;
+ }
+
+ inst1 = opcode1->bit_sequence;
+
+ /* We can fixup call to a defined non-global address
+ within the same section only. */
+ buf[0] = INST_BYTE0 (inst1);
+ buf[1] = INST_BYTE1 (inst1);
+ buf[2] = INST_BYTE2 (inst1);
+ buf[3] = INST_BYTE3 (inst1);
+ return;
+
+ default:
+ break;
+ }
+
+ if (fixP->fx_addsy == NULL)
+ {
+ /* This fixup has been resolved. Create a reloc in case the linker
+ moves code around due to relaxing. */
+ if (fixP->fx_r_type == BFD_RELOC_64_PCREL)
+ fixP->fx_r_type = BFD_RELOC_MICROBLAZE_64_NONE;
+ else
+ fixP->fx_r_type = BFD_RELOC_NONE;
+ fixP->fx_addsy = section_symbol (absolute_section);
+ }
+ return;
+}
+
+void
+md_operand (expressionS * expressionP)
+{
+ /* Ignore leading hash symbol, if present. */
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+/* Called just before address relaxation, return the length
+ by which a fragment must grow to reach it's destination. */
+
+int
+md_estimate_size_before_relax (fragS * fragP,
+ segT segment_type)
+{
+ sbss_segment = bfd_get_section_by_name (stdoutput, ".sbss");
+ sbss2_segment = bfd_get_section_by_name (stdoutput, ".sbss2");
+ sdata_segment = bfd_get_section_by_name (stdoutput, ".sdata");
+ sdata2_segment = bfd_get_section_by_name (stdoutput, ".sdata2");
+
+ switch (fragP->fr_subtype)
+ {
+ case INST_PC_OFFSET:
+ /* Used to be a PC-relative branch. */
+ if (!fragP->fr_symbol)
+ {
+ /* We know the abs value: Should never happen. */
+ as_bad (_("Absolute PC-relative value in relaxation code. Assembler error....."));
+ abort ();
+ }
+ else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type &&
+ !S_IS_WEAK (fragP->fr_symbol))
+ {
+ fragP->fr_subtype = DEFINED_PC_OFFSET;
+ /* Don't know now whether we need an imm instruction. */
+ fragP->fr_var = INST_WORD_SIZE;
+ }
+ else if (S_IS_DEFINED (fragP->fr_symbol)
+ && (((S_GET_SEGMENT (fragP->fr_symbol))->flags & SEC_CODE) == 0))
+ {
+ /* Cannot have a PC-relative branch to a diff segment. */
+ as_bad (_("PC relative branch to label %s which is not in the instruction space"),
+ S_GET_NAME (fragP->fr_symbol));
+ fragP->fr_subtype = UNDEFINED_PC_OFFSET;
+ fragP->fr_var = INST_WORD_SIZE*2;
+ }
+ else
+ {
+ fragP->fr_subtype = UNDEFINED_PC_OFFSET;
+ fragP->fr_var = INST_WORD_SIZE*2;
+ }
+ break;
+
+ case INST_NO_OFFSET:
+ /* Used to be a reference to somewhere which was unknown. */
+ if (fragP->fr_symbol)
+ {
+ if (fragP->fr_opcode == NULL)
+ {
+ /* Used as an absolute value. */
+ fragP->fr_subtype = DEFINED_ABS_SEGMENT;
+ /* Variable part does not change. */
+ fragP->fr_var = INST_WORD_SIZE*2;
+ }
+ else if (streq (fragP->fr_opcode, str_microblaze_ro_anchor))
+ {
+ /* It is accessed using the small data read only anchor. */
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == bfd_com_section_ptr)
+ || (S_GET_SEGMENT (fragP->fr_symbol) == sdata2_segment)
+ || (S_GET_SEGMENT (fragP->fr_symbol) == sbss2_segment)
+ || (! S_IS_DEFINED (fragP->fr_symbol)))
+ {
+ fragP->fr_subtype = DEFINED_RO_SEGMENT;
+ fragP->fr_var = INST_WORD_SIZE;
+ }
+ else
+ {
+ /* Variable not in small data read only segment accessed
+ using small data read only anchor. */
+ char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
+
+ as_bad_where (file, fragP->fr_line,
+ _("Variable is accessed using small data read "
+ "only anchor, but it is not in the small data "
+ "read only section"));
+ fragP->fr_subtype = DEFINED_RO_SEGMENT;
+ fragP->fr_var = INST_WORD_SIZE;
+ }
+ }
+ else if (streq (fragP->fr_opcode, str_microblaze_rw_anchor))
+ {
+ if ((S_GET_SEGMENT (fragP->fr_symbol) == bfd_com_section_ptr)
+ || (S_GET_SEGMENT (fragP->fr_symbol) == sdata_segment)
+ || (S_GET_SEGMENT (fragP->fr_symbol) == sbss_segment)
+ || (!S_IS_DEFINED (fragP->fr_symbol)))
+ {
+ /* It is accessed using the small data read write anchor. */
+ fragP->fr_subtype = DEFINED_RW_SEGMENT;
+ fragP->fr_var = INST_WORD_SIZE;
+ }
+ else
+ {
+ char *file = fragP->fr_file ? fragP->fr_file : _("unknown");
+
+ as_bad_where (file, fragP->fr_line,
+ _("Variable is accessed using small data read "
+ "write anchor, but it is not in the small data "
+ "read write section"));
+ fragP->fr_subtype = DEFINED_RW_SEGMENT;
+ fragP->fr_var = INST_WORD_SIZE;
+ }
+ }
+ else
+ {
+ as_bad (_("Incorrect fr_opcode value in frag. Internal error....."));
+ abort ();
+ }
+ }
+ else
+ {
+ /* We know the abs value: Should never happen. */
+ as_bad (_("Absolute value in relaxation code. Assembler error....."));
+ abort ();
+ }
+ break;
+
+ case UNDEFINED_PC_OFFSET:
+ case LARGE_DEFINED_PC_OFFSET:
+ case DEFINED_ABS_SEGMENT:
+ case GOT_OFFSET:
+ case PLT_OFFSET:
+ case GOTOFF_OFFSET:
+ case TLSGD_OFFSET:
+ case TLSLD_OFFSET:
+ case TLSTPREL_OFFSET:
+ case TLSDTPREL_OFFSET:
+ fragP->fr_var = INST_WORD_SIZE*2;
+ break;
+ case DEFINED_RO_SEGMENT:
+ case DEFINED_RW_SEGMENT:
+ case DEFINED_PC_OFFSET:
+ case TLSDTPMOD_OFFSET:
+ fragP->fr_var = INST_WORD_SIZE;
+ break;
+ default:
+ abort ();
+ }
+
+ return fragP->fr_var;
+}
+
+/* Put number into target byte order. */
+
+void
+md_number_to_chars (char * ptr, valueT use, int nbytes)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (ptr, use, nbytes);
+ else
+ number_to_chars_littleendian (ptr, use, nbytes);
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ return size; /* Byte alignment is fine. */
+}
+
+
+/* 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 ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ /* If the symbol is undefined or defined in another section
+ we leave the add number alone for the linker to fix it later.
+ Only account for the PC pre-bump (No PC-pre-bump on the Microblaze). */
+
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixp->fx_addsy)
+ || (S_GET_SEGMENT (fixp->fx_addsy) != sec)))
+ return 0;
+ else
+ {
+ /* The case where we are going to resolve things... */
+ if (fixp->fx_r_type == BFD_RELOC_64_PCREL)
+ return fixp->fx_where + fixp->fx_frag->fr_address + INST_WORD_SIZE;
+ else
+ return fixp->fx_where + fixp->fx_frag->fr_address;
+ }
+#endif
+}
+
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F (SZ, PCREL): code = (TYPE); break
+
+arelent *
+tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ arelent * rel;
+ bfd_reloc_code_real_type code;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_MICROBLAZE_64_NONE:
+ case BFD_RELOC_32:
+ case BFD_RELOC_MICROBLAZE_32_LO:
+ case BFD_RELOC_MICROBLAZE_32_LO_PCREL:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_MICROBLAZE_32_ROSDA:
+ case BFD_RELOC_MICROBLAZE_32_RWSDA:
+ case BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM:
+ case BFD_RELOC_MICROBLAZE_64_GOTPC:
+ case BFD_RELOC_MICROBLAZE_64_GOT:
+ case BFD_RELOC_MICROBLAZE_64_PLT:
+ case BFD_RELOC_MICROBLAZE_64_GOTOFF:
+ case BFD_RELOC_MICROBLAZE_32_GOTOFF:
+ case BFD_RELOC_MICROBLAZE_64_TLSGD:
+ case BFD_RELOC_MICROBLAZE_64_TLSLD:
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPMOD:
+ case BFD_RELOC_MICROBLAZE_32_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSDTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSGOTTPREL:
+ case BFD_RELOC_MICROBLAZE_64_TLSTPREL:
+ code = fixp->fx_r_type;
+ break;
+
+ default:
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ code = fixp->fx_r_type;
+ as_bad (_("Can not do %d byte %srelocation"),
+ fixp->fx_size,
+ fixp->fx_pcrel ? _("pc-relative") : "");
+ }
+ break;
+ }
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+
+ if (code == BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM)
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ else
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ /* Always pass the addend along! */
+ rel->addend = fixp->fx_offset;
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (rel->howto != NULL);
+ }
+ return rel;
+}
+
+int
+md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE * stream ATTRIBUTE_UNUSED)
+{
+ /* fprintf(stream, _("\
+ MicroBlaze options:\n\
+ -noSmall Data in the comm and data sections do not go into the small data section\n")); */
+}
+
+
+/* Create a fixup for a cons expression. If parse_cons_expression_microblaze
+ found a machine specific op in an expression,
+ then we create relocs accordingly. */
+
+void
+cons_fix_new_microblaze (fragS * frag,
+ int where,
+ int size,
+ expressionS *exp,
+ bfd_reloc_code_real_type r)
+{
+ if ((exp->X_op == O_subtract) && (exp->X_add_symbol) &&
+ (exp->X_op_symbol) && (now_seg != absolute_section) && (size == 4)
+ && (!S_IS_LOCAL (exp->X_op_symbol)))
+ r = BFD_RELOC_MICROBLAZE_32_SYM_OP_SYM;
+ else if (exp->X_md == IMM_GOTOFF && exp->X_op == O_symbol_rva)
+ {
+ exp->X_op = O_symbol;
+ r = BFD_RELOC_MICROBLAZE_32_GOTOFF;
+ }
+ else
+ {
+ switch (size)
+ {
+ case 1:
+ r = BFD_RELOC_8;
+ break;
+ case 2:
+ r = BFD_RELOC_16;
+ break;
+ case 4:
+ r = BFD_RELOC_32;
+ break;
+ case 8:
+ r = BFD_RELOC_64;
+ break;
+ default:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ r = BFD_RELOC_32;
+ break;
+ }
+ }
+ fix_new_exp (frag, where, size, exp, 0, r);
+}
diff --git a/gas/config/tc-microblaze.h b/gas/config/tc-microblaze.h
new file mode 100644
index 0000000..d44f2fa
--- /dev/null
+++ b/gas/config/tc-microblaze.h
@@ -0,0 +1,120 @@
+/* tc-microblaze.h -- Header file for tc-microblaze.c.
+
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_MICROBLAZE
+#define TC_MICROBLAZE 1
+
+#define TARGET_ARCH bfd_arch_microblaze
+#ifndef TARGET_BYTES_BIG_ENDIAN
+/* Used to initialise target_big_endian. */
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define TC_RELOC_MANGLE(a,b,c) tc_reloc_mangle (a, b, c)
+
+/* We need to handle expressions of type "symbol op symbol" and create
+ relocs for such expressions as -relax in linker can change the value
+ of such expressions */
+#define TC_CONS_FIX_NEW cons_fix_new_microblaze
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+ parse_cons_expression_microblaze (EXP, NBYTES)
+extern bfd_reloc_code_real_type parse_cons_expression_microblaze
+ (expressionS *, int);
+
+#define TC_FORCE_RELOCATION_SECTION(FIXP,SEG) 1
+#define UNDEFINED_DIFFERENCE_OK 1
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOT \
+ || (FIX)->fx_r_type == BFD_RELOC_MICROBLAZE_64_PLT \
+ || (FIX)->fx_r_type == BFD_RELOC_MICROBLAZE_64_GOTOFF \
+ || (FIX)->fx_r_type == BFD_RELOC_MICROBLAZE_32_GOTOFF \
+ || TC_FORCE_RELOCATION (FIX))
+
+#define tc_fix_adjustable(X) tc_microblaze_fix_adjustable(X)
+extern int tc_microblaze_fix_adjustable (struct fix *);
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define LISTING_HEADER "Xilinx MicroBlaze GAS Listing"
+#define LISTING_LHS_CONT_LINES 4
+
+#define NEED_FX_R_TYPE 1
+
+/* We want local label support. */
+#define LOCAL_LABELS_FB 1
+
+/* Want the section information too... */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+
+#define MD_APPLY_FIX3 /* We want the segment as well. */
+
+/* We set the fx_done field appropriately in md_apply_fix. */
+#define TC_HANDLES_FX_DONE
+
+#ifdef OBJ_ELF
+
+#define TARGET_FORMAT (target_big_endian ? "elf32-microblaze" : "elf32-microblazeel")
+
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE }, \
+ { ".sdata2", SHT_PROGBITS, SHF_ALLOC }, \
+ { ".sbss2", SHT_PROGBITS, SHF_ALLOC }, \
+/* Other special sections not generated by the assembler: .reginfo,
+ .liblist, .conflict, .gptab, .got, .dynamic, .rel.dyn. */
+
+#endif /* OBJ_ELF */
+
+#ifndef TARGET_FORMAT
+# error No target format specified.
+#endif
+
+#include "write.h" /* For definition of fixS */
+
+extern void md_begin (void);
+extern void md_assemble (char *);
+extern symbolS * md_undefined_symbol (char *);
+extern char * md_atof (int, char *, int *);
+extern int md_parse_option (int, char *);
+extern void md_show_usage (FILE *);
+extern void md_convert_frag (bfd *, segT, fragS *);
+extern void md_operand (expressionS *);
+extern int md_estimate_size_before_relax (fragS *, segT);
+extern void md_number_to_chars (char *, valueT, int);
+extern valueT md_section_align (segT, valueT);
+extern long md_pcrel_from_section (fixS *, segT);
+extern arelent * tc_gen_reloc (asection *, fixS *);
+extern void cons_fix_new_microblaze (fragS *, int, int,
+ expressionS *,
+ bfd_reloc_code_real_type);
+extern void md_apply_fix3 (fixS *, valueT *, segT);
+
+#define EXTERN_FORCE_RELOC -1
+
+#endif /* TC_MICROBLAZE */
diff --git a/gas/config/tc-mips.c b/gas/config/tc-mips.c
new file mode 100644
index 0000000..c3e3e2a
--- /dev/null
+++ b/gas/config/tc-mips.c
@@ -0,0 +1,19112 @@
+/* tc-mips.c -- assemble code for a MIPS chip.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF and R4000 support by Ian Lance Taylor of Cygnus
+ Support.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "config.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+
+#include "opcode/mips.h"
+#include "itbl-ops.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+/* Check assumptions made in this file. */
+typedef char static_assert1[sizeof (offsetT) < 8 ? -1 : 1];
+typedef char static_assert2[sizeof (valueT) < 8 ? -1 : 1];
+
+#ifdef DEBUG
+#define DBG(x) printf x
+#else
+#define DBG(x)
+#endif
+
+#define streq(a, b) (strcmp (a, b) == 0)
+
+#define SKIP_SPACE_TABS(S) \
+ do { while (*(S) == ' ' || *(S) == '\t') ++(S); } while (0)
+
+/* Clean up namespace so we can include obj-elf.h too. */
+static int mips_output_flavor (void);
+static int mips_output_flavor (void) { return OUTPUT_FLAVOR; }
+#undef OBJ_PROCESS_STAB
+#undef OUTPUT_FLAVOR
+#undef S_GET_ALIGN
+#undef S_GET_SIZE
+#undef S_SET_ALIGN
+#undef S_SET_SIZE
+#undef obj_frob_file
+#undef obj_frob_file_after_relocs
+#undef obj_frob_symbol
+#undef obj_pop_insert
+#undef obj_sec_sym_ok_for_reloc
+#undef OBJ_COPY_SYMBOL_ATTRIBUTES
+
+#include "obj-elf.h"
+/* Fix any of them that we actually care about. */
+#undef OUTPUT_FLAVOR
+#define OUTPUT_FLAVOR mips_output_flavor()
+
+#include "elf/mips.h"
+
+#ifndef ECOFF_DEBUGGING
+#define NO_ECOFF_DEBUGGING
+#define ECOFF_DEBUGGING 0
+#endif
+
+int mips_flag_mdebug = -1;
+
+/* Control generation of .pdr sections. Off by default on IRIX: the native
+ linker doesn't know about and discards them, but relocations against them
+ remain, leading to rld crashes. */
+#ifdef TE_IRIX
+int mips_flag_pdr = FALSE;
+#else
+int mips_flag_pdr = TRUE;
+#endif
+
+#include "ecoff.h"
+
+static char *mips_regmask_frag;
+static char *mips_flags_frag;
+
+#define ZERO 0
+#define ATREG 1
+#define S0 16
+#define S7 23
+#define TREG 24
+#define PIC_CALL_REG 25
+#define KT0 26
+#define KT1 27
+#define GP 28
+#define SP 29
+#define FP 30
+#define RA 31
+
+#define ILLEGAL_REG (32)
+
+#define AT mips_opts.at
+
+extern int target_big_endian;
+
+/* The name of the readonly data section. */
+#define RDATA_SECTION_NAME ".rodata"
+
+/* Ways in which an instruction can be "appended" to the output. */
+enum append_method {
+ /* Just add it normally. */
+ APPEND_ADD,
+
+ /* Add it normally and then add a nop. */
+ APPEND_ADD_WITH_NOP,
+
+ /* Turn an instruction with a delay slot into a "compact" version. */
+ APPEND_ADD_COMPACT,
+
+ /* Insert the instruction before the last one. */
+ APPEND_SWAP
+};
+
+/* Information about an instruction, including its format, operands
+ and fixups. */
+struct mips_cl_insn
+{
+ /* The opcode's entry in mips_opcodes or mips16_opcodes. */
+ const struct mips_opcode *insn_mo;
+
+ /* The 16-bit or 32-bit bitstring of the instruction itself. This is
+ a copy of INSN_MO->match with the operands filled in. If we have
+ decided to use an extended MIPS16 instruction, this includes the
+ extension. */
+ unsigned long insn_opcode;
+
+ /* The frag that contains the instruction. */
+ struct frag *frag;
+
+ /* The offset into FRAG of the first instruction byte. */
+ long where;
+
+ /* The relocs associated with the instruction, if any. */
+ fixS *fixp[3];
+
+ /* True if this entry cannot be moved from its current position. */
+ unsigned int fixed_p : 1;
+
+ /* True if this instruction occurred in a .set noreorder block. */
+ unsigned int noreorder_p : 1;
+
+ /* True for mips16 instructions that jump to an absolute address. */
+ unsigned int mips16_absolute_jump_p : 1;
+
+ /* True if this instruction is complete. */
+ unsigned int complete_p : 1;
+
+ /* True if this instruction is cleared from history by unconditional
+ branch. */
+ unsigned int cleared_p : 1;
+};
+
+/* The ABI to use. */
+enum mips_abi_level
+{
+ NO_ABI = 0,
+ O32_ABI,
+ O64_ABI,
+ N32_ABI,
+ N64_ABI,
+ EABI_ABI
+};
+
+/* MIPS ABI we are using for this output file. */
+static enum mips_abi_level mips_abi = NO_ABI;
+
+/* Whether or not we have code that can call pic code. */
+int mips_abicalls = FALSE;
+
+/* Whether or not we have code which can be put into a shared
+ library. */
+static bfd_boolean mips_in_shared = TRUE;
+
+/* This is the set of options which may be modified by the .set
+ pseudo-op. We use a struct so that .set push and .set pop are more
+ reliable. */
+
+struct mips_set_options
+{
+ /* MIPS ISA (Instruction Set Architecture) level. This is set to -1
+ if it has not been initialized. Changed by `.set mipsN', and the
+ -mipsN command line option, and the default CPU. */
+ int isa;
+ /* Enabled Application Specific Extensions (ASEs). Changed by `.set
+ <asename>', by command line options, and based on the default
+ architecture. */
+ int ase;
+ /* Whether we are assembling for the mips16 processor. 0 if we are
+ not, 1 if we are, and -1 if the value has not been initialized.
+ Changed by `.set mips16' and `.set nomips16', and the -mips16 and
+ -nomips16 command line options, and the default CPU. */
+ int mips16;
+ /* Whether we are assembling for the mipsMIPS ASE. 0 if we are not,
+ 1 if we are, and -1 if the value has not been initialized. Changed
+ by `.set micromips' and `.set nomicromips', and the -mmicromips
+ and -mno-micromips command line options, and the default CPU. */
+ int micromips;
+ /* Non-zero if we should not reorder instructions. Changed by `.set
+ reorder' and `.set noreorder'. */
+ int noreorder;
+ /* Non-zero if we should not permit the register designated "assembler
+ temporary" to be used in instructions. The value is the register
+ number, normally $at ($1). Changed by `.set at=REG', `.set noat'
+ (same as `.set at=$0') and `.set at' (same as `.set at=$1'). */
+ unsigned int at;
+ /* Non-zero if we should warn when a macro instruction expands into
+ more than one machine instruction. Changed by `.set nomacro' and
+ `.set macro'. */
+ int warn_about_macros;
+ /* Non-zero if we should not move instructions. Changed by `.set
+ move', `.set volatile', `.set nomove', and `.set novolatile'. */
+ int nomove;
+ /* Non-zero if we should not optimize branches by moving the target
+ of the branch into the delay slot. Actually, we don't perform
+ this optimization anyhow. Changed by `.set bopt' and `.set
+ nobopt'. */
+ int nobopt;
+ /* Non-zero if we should not autoextend mips16 instructions.
+ Changed by `.set autoextend' and `.set noautoextend'. */
+ int noautoextend;
+ /* True if we should only emit 32-bit microMIPS instructions.
+ Changed by `.set insn32' and `.set noinsn32', and the -minsn32
+ and -mno-insn32 command line options. */
+ bfd_boolean insn32;
+ /* Restrict general purpose registers and floating point registers
+ to 32 bit. This is initially determined when -mgp32 or -mfp32
+ is passed but can changed if the assembler code uses .set mipsN. */
+ int gp;
+ int fp;
+ /* MIPS architecture (CPU) type. Changed by .set arch=FOO, the -march
+ command line option, and the default CPU. */
+ int arch;
+ /* True if ".set sym32" is in effect. */
+ bfd_boolean sym32;
+ /* True if floating-point operations are not allowed. Changed by .set
+ softfloat or .set hardfloat, by command line options -msoft-float or
+ -mhard-float. The default is false. */
+ bfd_boolean soft_float;
+
+ /* True if only single-precision floating-point operations are allowed.
+ Changed by .set singlefloat or .set doublefloat, command-line options
+ -msingle-float or -mdouble-float. The default is false. */
+ bfd_boolean single_float;
+
+ /* 1 if single-precision operations on odd-numbered registers are
+ allowed. */
+ int oddspreg;
+};
+
+/* Specifies whether module level options have been checked yet. */
+static bfd_boolean file_mips_opts_checked = FALSE;
+
+/* Do we support nan2008? 0 if we don't, 1 if we do, and -1 if the
+ value has not been initialized. Changed by `.nan legacy' and
+ `.nan 2008', and the -mnan=legacy and -mnan=2008 command line
+ options, and the default CPU. */
+static int mips_nan2008 = -1;
+
+/* This is the struct we use to hold the module level set of options.
+ Note that we must set the isa field to ISA_UNKNOWN and the ASE, gp and
+ fp fields to -1 to indicate that they have not been initialized. */
+
+static struct mips_set_options file_mips_opts =
+{
+ /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
+ /* noreorder */ 0, /* at */ ATREG, /* warn_about_macros */ 0,
+ /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
+ /* gp */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+ /* soft_float */ FALSE, /* single_float */ FALSE, /* oddspreg */ -1
+};
+
+/* This is similar to file_mips_opts, but for the current set of options. */
+
+static struct mips_set_options mips_opts =
+{
+ /* isa */ ISA_UNKNOWN, /* ase */ 0, /* mips16 */ -1, /* micromips */ -1,
+ /* noreorder */ 0, /* at */ ATREG, /* warn_about_macros */ 0,
+ /* nomove */ 0, /* nobopt */ 0, /* noautoextend */ 0, /* insn32 */ FALSE,
+ /* gp */ -1, /* fp */ -1, /* arch */ CPU_UNKNOWN, /* sym32 */ FALSE,
+ /* soft_float */ FALSE, /* single_float */ FALSE, /* oddspreg */ -1
+};
+
+/* Which bits of file_ase were explicitly set or cleared by ASE options. */
+static unsigned int file_ase_explicit;
+
+/* These variables are filled in with the masks of registers used.
+ The object format code reads them and puts them in the appropriate
+ place. */
+unsigned long mips_gprmask;
+unsigned long mips_cprmask[4];
+
+/* True if any MIPS16 code was produced. */
+static int file_ase_mips16;
+
+#define ISA_SUPPORTS_MIPS16E (mips_opts.isa == ISA_MIPS32 \
+ || mips_opts.isa == ISA_MIPS32R2 \
+ || mips_opts.isa == ISA_MIPS32R3 \
+ || mips_opts.isa == ISA_MIPS32R5 \
+ || mips_opts.isa == ISA_MIPS64 \
+ || mips_opts.isa == ISA_MIPS64R2 \
+ || mips_opts.isa == ISA_MIPS64R3 \
+ || mips_opts.isa == ISA_MIPS64R5)
+
+/* True if any microMIPS code was produced. */
+static int file_ase_micromips;
+
+/* True if we want to create R_MIPS_JALR for jalr $25. */
+#ifdef TE_IRIX
+#define MIPS_JALR_HINT_P(EXPR) HAVE_NEWABI
+#else
+/* As a GNU extension, we use R_MIPS_JALR for o32 too. However,
+ because there's no place for any addend, the only acceptable
+ expression is a bare symbol. */
+#define MIPS_JALR_HINT_P(EXPR) \
+ (!HAVE_IN_PLACE_ADDENDS \
+ || ((EXPR)->X_op == O_symbol && (EXPR)->X_add_number == 0))
+#endif
+
+/* The argument of the -march= flag. The architecture we are assembling. */
+static const char *mips_arch_string;
+
+/* The argument of the -mtune= flag. The architecture for which we
+ are optimizing. */
+static int mips_tune = CPU_UNKNOWN;
+static const char *mips_tune_string;
+
+/* True when generating 32-bit code for a 64-bit processor. */
+static int mips_32bitmode = 0;
+
+/* True if the given ABI requires 32-bit registers. */
+#define ABI_NEEDS_32BIT_REGS(ABI) ((ABI) == O32_ABI)
+
+/* Likewise 64-bit registers. */
+#define ABI_NEEDS_64BIT_REGS(ABI) \
+ ((ABI) == N32_ABI \
+ || (ABI) == N64_ABI \
+ || (ABI) == O64_ABI)
+
+#define ISA_IS_R6(ISA) \
+ ((ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64R6)
+
+/* Return true if ISA supports 64 bit wide gp registers. */
+#define ISA_HAS_64BIT_REGS(ISA) \
+ ((ISA) == ISA_MIPS3 \
+ || (ISA) == ISA_MIPS4 \
+ || (ISA) == ISA_MIPS5 \
+ || (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6)
+
+/* Return true if ISA supports 64 bit wide float registers. */
+#define ISA_HAS_64BIT_FPRS(ISA) \
+ ((ISA) == ISA_MIPS3 \
+ || (ISA) == ISA_MIPS4 \
+ || (ISA) == ISA_MIPS5 \
+ || (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6)
+
+/* Return true if ISA supports 64-bit right rotate (dror et al.)
+ instructions. */
+#define ISA_HAS_DROR(ISA) \
+ ((ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6 \
+ || (mips_opts.micromips \
+ && ISA_HAS_64BIT_REGS (ISA)) \
+ )
+
+/* Return true if ISA supports 32-bit right rotate (ror et al.)
+ instructions. */
+#define ISA_HAS_ROR(ISA) \
+ ((ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6 \
+ || (mips_opts.ase & ASE_SMARTMIPS) \
+ || mips_opts.micromips \
+ )
+
+/* Return true if ISA supports single-precision floats in odd registers. */
+#define ISA_HAS_ODD_SINGLE_FPR(ISA, CPU)\
+ (((ISA) == ISA_MIPS32 \
+ || (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6 \
+ || (CPU) == CPU_R5900) \
+ && (CPU) != CPU_LOONGSON_3A)
+
+/* Return true if ISA supports move to/from high part of a 64-bit
+ floating-point register. */
+#define ISA_HAS_MXHC1(ISA) \
+ ((ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS32R6 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5 \
+ || (ISA) == ISA_MIPS64R6)
+
+/* Return true if ISA supports legacy NAN. */
+#define ISA_HAS_LEGACY_NAN(ISA) \
+ ((ISA) == ISA_MIPS1 \
+ || (ISA) == ISA_MIPS2 \
+ || (ISA) == ISA_MIPS3 \
+ || (ISA) == ISA_MIPS4 \
+ || (ISA) == ISA_MIPS5 \
+ || (ISA) == ISA_MIPS32 \
+ || (ISA) == ISA_MIPS32R2 \
+ || (ISA) == ISA_MIPS32R3 \
+ || (ISA) == ISA_MIPS32R5 \
+ || (ISA) == ISA_MIPS64 \
+ || (ISA) == ISA_MIPS64R2 \
+ || (ISA) == ISA_MIPS64R3 \
+ || (ISA) == ISA_MIPS64R5)
+
+#define GPR_SIZE \
+ (mips_opts.gp == 64 && !ISA_HAS_64BIT_REGS (mips_opts.isa) \
+ ? 32 \
+ : mips_opts.gp)
+
+#define FPR_SIZE \
+ (mips_opts.fp == 64 && !ISA_HAS_64BIT_FPRS (mips_opts.isa) \
+ ? 32 \
+ : mips_opts.fp)
+
+#define HAVE_NEWABI (mips_abi == N32_ABI || mips_abi == N64_ABI)
+
+#define HAVE_64BIT_OBJECTS (mips_abi == N64_ABI)
+
+/* True if relocations are stored in-place. */
+#define HAVE_IN_PLACE_ADDENDS (!HAVE_NEWABI)
+
+/* The ABI-derived address size. */
+#define HAVE_64BIT_ADDRESSES \
+ (GPR_SIZE == 64 && (mips_abi == EABI_ABI || mips_abi == N64_ABI))
+#define HAVE_32BIT_ADDRESSES (!HAVE_64BIT_ADDRESSES)
+
+/* The size of symbolic constants (i.e., expressions of the form
+ "SYMBOL" or "SYMBOL + OFFSET"). */
+#define HAVE_32BIT_SYMBOLS \
+ (HAVE_32BIT_ADDRESSES || !HAVE_64BIT_OBJECTS || mips_opts.sym32)
+#define HAVE_64BIT_SYMBOLS (!HAVE_32BIT_SYMBOLS)
+
+/* Addresses are loaded in different ways, depending on the address size
+ in use. The n32 ABI Documentation also mandates the use of additions
+ with overflow checking, but existing implementations don't follow it. */
+#define ADDRESS_ADD_INSN \
+ (HAVE_32BIT_ADDRESSES ? "addu" : "daddu")
+
+#define ADDRESS_ADDI_INSN \
+ (HAVE_32BIT_ADDRESSES ? "addiu" : "daddiu")
+
+#define ADDRESS_LOAD_INSN \
+ (HAVE_32BIT_ADDRESSES ? "lw" : "ld")
+
+#define ADDRESS_STORE_INSN \
+ (HAVE_32BIT_ADDRESSES ? "sw" : "sd")
+
+/* Return true if the given CPU supports the MIPS16 ASE. */
+#define CPU_HAS_MIPS16(cpu) \
+ (strncmp (TARGET_CPU, "mips16", sizeof ("mips16") - 1) == 0 \
+ || strncmp (TARGET_CANONICAL, "mips-lsi-elf", sizeof ("mips-lsi-elf") - 1) == 0)
+
+/* Return true if the given CPU supports the microMIPS ASE. */
+#define CPU_HAS_MICROMIPS(cpu) 0
+
+/* True if CPU has a dror instruction. */
+#define CPU_HAS_DROR(CPU) ((CPU) == CPU_VR5400 || (CPU) == CPU_VR5500)
+
+/* True if CPU has a ror instruction. */
+#define CPU_HAS_ROR(CPU) CPU_HAS_DROR (CPU)
+
+/* True if CPU is in the Octeon family */
+#define CPU_IS_OCTEON(CPU) ((CPU) == CPU_OCTEON || (CPU) == CPU_OCTEONP || (CPU) == CPU_OCTEON2)
+
+/* True if CPU has seq/sne and seqi/snei instructions. */
+#define CPU_HAS_SEQ(CPU) (CPU_IS_OCTEON (CPU))
+
+/* True, if CPU has support for ldc1 and sdc1. */
+#define CPU_HAS_LDC1_SDC1(CPU) \
+ ((mips_opts.isa != ISA_MIPS1) && ((CPU) != CPU_R5900))
+
+/* True if mflo and mfhi can be immediately followed by instructions
+ which write to the HI and LO registers.
+
+ According to MIPS specifications, MIPS ISAs I, II, and III need
+ (at least) two instructions between the reads of HI/LO and
+ instructions which write them, and later ISAs do not. Contradicting
+ the MIPS specifications, some MIPS IV processor user manuals (e.g.
+ the UM for the NEC Vr5000) document needing the instructions between
+ HI/LO reads and writes, as well. Therefore, we declare only MIPS32,
+ MIPS64 and later ISAs to have the interlocks, plus any specific
+ earlier-ISA CPUs for which CPU documentation declares that the
+ instructions are really interlocked. */
+#define hilo_interlocks \
+ (mips_opts.isa == ISA_MIPS32 \
+ || mips_opts.isa == ISA_MIPS32R2 \
+ || mips_opts.isa == ISA_MIPS32R3 \
+ || mips_opts.isa == ISA_MIPS32R5 \
+ || mips_opts.isa == ISA_MIPS32R6 \
+ || mips_opts.isa == ISA_MIPS64 \
+ || mips_opts.isa == ISA_MIPS64R2 \
+ || mips_opts.isa == ISA_MIPS64R3 \
+ || mips_opts.isa == ISA_MIPS64R5 \
+ || mips_opts.isa == ISA_MIPS64R6 \
+ || mips_opts.arch == CPU_R4010 \
+ || mips_opts.arch == CPU_R5900 \
+ || mips_opts.arch == CPU_R10000 \
+ || mips_opts.arch == CPU_R12000 \
+ || mips_opts.arch == CPU_R14000 \
+ || mips_opts.arch == CPU_R16000 \
+ || mips_opts.arch == CPU_RM7000 \
+ || mips_opts.arch == CPU_VR5500 \
+ || mips_opts.micromips \
+ )
+
+/* Whether the processor uses hardware interlocks to protect reads
+ from the GPRs after they are loaded from memory, and thus does not
+ require nops to be inserted. This applies to instructions marked
+ INSN_LOAD_MEMORY. These nops are only required at MIPS ISA
+ level I and microMIPS mode instructions are always interlocked. */
+#define gpr_interlocks \
+ (mips_opts.isa != ISA_MIPS1 \
+ || mips_opts.arch == CPU_R3900 \
+ || mips_opts.arch == CPU_R5900 \
+ || mips_opts.micromips \
+ )
+
+/* Whether the processor uses hardware interlocks to avoid delays
+ required by coprocessor instructions, and thus does not require
+ nops to be inserted. This applies to instructions marked
+ INSN_LOAD_COPROC, INSN_COPROC_MOVE, and to delays between
+ instructions marked INSN_WRITE_COND_CODE and ones marked
+ INSN_READ_COND_CODE. These nops are only required at MIPS ISA
+ levels I, II, and III and microMIPS mode instructions are always
+ interlocked. */
+/* Itbl support may require additional care here. */
+#define cop_interlocks \
+ ((mips_opts.isa != ISA_MIPS1 \
+ && mips_opts.isa != ISA_MIPS2 \
+ && mips_opts.isa != ISA_MIPS3) \
+ || mips_opts.arch == CPU_R4300 \
+ || mips_opts.micromips \
+ )
+
+/* Whether the processor uses hardware interlocks to protect reads
+ from coprocessor registers after they are loaded from memory, and
+ thus does not require nops to be inserted. This applies to
+ instructions marked INSN_COPROC_MEMORY_DELAY. These nops are only
+ requires at MIPS ISA level I and microMIPS mode instructions are
+ always interlocked. */
+#define cop_mem_interlocks \
+ (mips_opts.isa != ISA_MIPS1 \
+ || mips_opts.micromips \
+ )
+
+/* Is this a mfhi or mflo instruction? */
+#define MF_HILO_INSN(PINFO) \
+ ((PINFO & INSN_READ_HI) || (PINFO & INSN_READ_LO))
+
+/* Whether code compression (either of the MIPS16 or the microMIPS ASEs)
+ has been selected. This implies, in particular, that addresses of text
+ labels have their LSB set. */
+#define HAVE_CODE_COMPRESSION \
+ ((mips_opts.mips16 | mips_opts.micromips) != 0)
+
+/* The minimum and maximum signed values that can be stored in a GPR. */
+#define GPR_SMAX ((offsetT) (((valueT) 1 << (GPR_SIZE - 1)) - 1))
+#define GPR_SMIN (-GPR_SMAX - 1)
+
+/* MIPS PIC level. */
+
+enum mips_pic_level mips_pic;
+
+/* 1 if we should generate 32 bit offsets from the $gp register in
+ SVR4_PIC mode. Currently has no meaning in other modes. */
+static int mips_big_got = 0;
+
+/* 1 if trap instructions should used for overflow rather than break
+ instructions. */
+static int mips_trap = 0;
+
+/* 1 if double width floating point constants should not be constructed
+ by assembling two single width halves into two single width floating
+ point registers which just happen to alias the double width destination
+ register. On some architectures this aliasing can be disabled by a bit
+ in the status register, and the setting of this bit cannot be determined
+ automatically at assemble time. */
+static int mips_disable_float_construction;
+
+/* Non-zero if any .set noreorder directives were used. */
+
+static int mips_any_noreorder;
+
+/* Non-zero if nops should be inserted when the register referenced in
+ an mfhi/mflo instruction is read in the next two instructions. */
+static int mips_7000_hilo_fix;
+
+/* The size of objects in the small data section. */
+static unsigned int g_switch_value = 8;
+/* Whether the -G option was used. */
+static int g_switch_seen = 0;
+
+#define N_RMASK 0xc4
+#define N_VFP 0xd4
+
+/* If we can determine in advance that GP optimization won't be
+ possible, we can skip the relaxation stuff that tries to produce
+ GP-relative references. This makes delay slot optimization work
+ better.
+
+ This function can only provide a guess, but it seems to work for
+ gcc output. It needs to guess right for gcc, otherwise gcc
+ will put what it thinks is a GP-relative instruction in a branch
+ delay slot.
+
+ I don't know if a fix is needed for the SVR4_PIC mode. I've only
+ fixed it for the non-PIC mode. KR 95/04/07 */
+static int nopic_need_relax (symbolS *, int);
+
+/* handle of the OPCODE hash table */
+static struct hash_control *op_hash = NULL;
+
+/* The opcode hash table we use for the mips16. */
+static struct hash_control *mips16_op_hash = NULL;
+
+/* The opcode hash table we use for the microMIPS ASE. */
+static struct hash_control *micromips_op_hash = NULL;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful */
+const char comment_chars[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that C style comments are always supported. */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here.
+ */
+
+/* Types of printf format used for instruction-related error messages.
+ "I" means int ("%d") and "S" means string ("%s"). */
+enum mips_insn_error_format {
+ ERR_FMT_PLAIN,
+ ERR_FMT_I,
+ ERR_FMT_SS,
+};
+
+/* Information about an error that was found while assembling the current
+ instruction. */
+struct mips_insn_error {
+ /* We sometimes need to match an instruction against more than one
+ opcode table entry. Errors found during this matching are reported
+ against a particular syntactic argument rather than against the
+ instruction as a whole. We grade these messages so that errors
+ against argument N have a greater priority than an error against
+ any argument < N, since the former implies that arguments up to N
+ were acceptable and that the opcode entry was therefore a closer match.
+ If several matches report an error against the same argument,
+ we only use that error if it is the same in all cases.
+
+ min_argnum is the minimum argument number for which an error message
+ should be accepted. It is 0 if MSG is against the instruction as
+ a whole. */
+ int min_argnum;
+
+ /* The printf()-style message, including its format and arguments. */
+ enum mips_insn_error_format format;
+ const char *msg;
+ union {
+ int i;
+ const char *ss[2];
+ } u;
+};
+
+/* The error that should be reported for the current instruction. */
+static struct mips_insn_error insn_error;
+
+static int auto_align = 1;
+
+/* When outputting SVR4 PIC code, the assembler needs to know the
+ offset in the stack frame from which to restore the $gp register.
+ This is set by the .cprestore pseudo-op, and saved in this
+ variable. */
+static offsetT mips_cprestore_offset = -1;
+
+/* Similar for NewABI PIC code, where $gp is callee-saved. NewABI has some
+ more optimizations, it can use a register value instead of a memory-saved
+ offset and even an other register than $gp as global pointer. */
+static offsetT mips_cpreturn_offset = -1;
+static int mips_cpreturn_register = -1;
+static int mips_gp_register = GP;
+static int mips_gprel_offset = 0;
+
+/* Whether mips_cprestore_offset has been set in the current function
+ (or whether it has already been warned about, if not). */
+static int mips_cprestore_valid = 0;
+
+/* This is the register which holds the stack frame, as set by the
+ .frame pseudo-op. This is needed to implement .cprestore. */
+static int mips_frame_reg = SP;
+
+/* Whether mips_frame_reg has been set in the current function
+ (or whether it has already been warned about, if not). */
+static int mips_frame_reg_valid = 0;
+
+/* To output NOP instructions correctly, we need to keep information
+ about the previous two instructions. */
+
+/* Whether we are optimizing. The default value of 2 means to remove
+ unneeded NOPs and swap branch instructions when possible. A value
+ of 1 means to not swap branches. A value of 0 means to always
+ insert NOPs. */
+static int mips_optimize = 2;
+
+/* Debugging level. -g sets this to 2. -gN sets this to N. -g0 is
+ equivalent to seeing no -g option at all. */
+static int mips_debug = 0;
+
+/* The maximum number of NOPs needed to avoid the VR4130 mflo/mfhi errata. */
+#define MAX_VR4130_NOPS 4
+
+/* The maximum number of NOPs needed to fill delay slots. */
+#define MAX_DELAY_NOPS 2
+
+/* The maximum number of NOPs needed for any purpose. */
+#define MAX_NOPS 4
+
+/* A list of previous instructions, with index 0 being the most recent.
+ We need to look back MAX_NOPS instructions when filling delay slots
+ or working around processor errata. We need to look back one
+ instruction further if we're thinking about using history[0] to
+ fill a branch delay slot. */
+static struct mips_cl_insn history[1 + MAX_NOPS];
+
+/* Arrays of operands for each instruction. */
+#define MAX_OPERANDS 6
+struct mips_operand_array {
+ const struct mips_operand *operand[MAX_OPERANDS];
+};
+static struct mips_operand_array *mips_operands;
+static struct mips_operand_array *mips16_operands;
+static struct mips_operand_array *micromips_operands;
+
+/* Nop instructions used by emit_nop. */
+static struct mips_cl_insn nop_insn;
+static struct mips_cl_insn mips16_nop_insn;
+static struct mips_cl_insn micromips_nop16_insn;
+static struct mips_cl_insn micromips_nop32_insn;
+
+/* The appropriate nop for the current mode. */
+#define NOP_INSN (mips_opts.mips16 \
+ ? &mips16_nop_insn \
+ : (mips_opts.micromips \
+ ? (mips_opts.insn32 \
+ ? &micromips_nop32_insn \
+ : &micromips_nop16_insn) \
+ : &nop_insn))
+
+/* The size of NOP_INSN in bytes. */
+#define NOP_INSN_SIZE ((mips_opts.mips16 \
+ || (mips_opts.micromips && !mips_opts.insn32)) \
+ ? 2 : 4)
+
+/* If this is set, it points to a frag holding nop instructions which
+ were inserted before the start of a noreorder section. If those
+ nops turn out to be unnecessary, the size of the frag can be
+ decreased. */
+static fragS *prev_nop_frag;
+
+/* The number of nop instructions we created in prev_nop_frag. */
+static int prev_nop_frag_holds;
+
+/* The number of nop instructions that we know we need in
+ prev_nop_frag. */
+static int prev_nop_frag_required;
+
+/* The number of instructions we've seen since prev_nop_frag. */
+static int prev_nop_frag_since;
+
+/* Relocations against symbols are sometimes done in two parts, with a HI
+ relocation and a LO relocation. Each relocation has only 16 bits of
+ space to store an addend. This means that in order for the linker to
+ handle carries correctly, it must be able to locate both the HI and
+ the LO relocation. This means that the relocations must appear in
+ order in the relocation table.
+
+ In order to implement this, we keep track of each unmatched HI
+ relocation. We then sort them so that they immediately precede the
+ corresponding LO relocation. */
+
+struct mips_hi_fixup
+{
+ /* Next HI fixup. */
+ struct mips_hi_fixup *next;
+ /* This fixup. */
+ fixS *fixp;
+ /* The section this fixup is in. */
+ segT seg;
+};
+
+/* The list of unmatched HI relocs. */
+
+static struct mips_hi_fixup *mips_hi_fixup_list;
+
+/* The frag containing the last explicit relocation operator.
+ Null if explicit relocations have not been used. */
+
+static fragS *prev_reloc_op_frag;
+
+/* Map mips16 register numbers to normal MIPS register numbers. */
+
+static const unsigned int mips16_to_32_reg_map[] =
+{
+ 16, 17, 2, 3, 4, 5, 6, 7
+};
+
+/* Map microMIPS register numbers to normal MIPS register numbers. */
+
+#define micromips_to_32_reg_d_map mips16_to_32_reg_map
+
+/* The microMIPS registers with type h. */
+static const unsigned int micromips_to_32_reg_h_map1[] =
+{
+ 5, 5, 6, 4, 4, 4, 4, 4
+};
+static const unsigned int micromips_to_32_reg_h_map2[] =
+{
+ 6, 7, 7, 21, 22, 5, 6, 7
+};
+
+/* The microMIPS registers with type m. */
+static const unsigned int micromips_to_32_reg_m_map[] =
+{
+ 0, 17, 2, 3, 16, 18, 19, 20
+};
+
+#define micromips_to_32_reg_n_map micromips_to_32_reg_m_map
+
+/* Classifies the kind of instructions we're interested in when
+ implementing -mfix-vr4120. */
+enum fix_vr4120_class
+{
+ FIX_VR4120_MACC,
+ FIX_VR4120_DMACC,
+ FIX_VR4120_MULT,
+ FIX_VR4120_DMULT,
+ FIX_VR4120_DIV,
+ FIX_VR4120_MTHILO,
+ NUM_FIX_VR4120_CLASSES
+};
+
+/* ...likewise -mfix-loongson2f-jump. */
+static bfd_boolean mips_fix_loongson2f_jump;
+
+/* ...likewise -mfix-loongson2f-nop. */
+static bfd_boolean mips_fix_loongson2f_nop;
+
+/* True if -mfix-loongson2f-nop or -mfix-loongson2f-jump passed. */
+static bfd_boolean mips_fix_loongson2f;
+
+/* Given two FIX_VR4120_* values X and Y, bit Y of element X is set if
+ there must be at least one other instruction between an instruction
+ of type X and an instruction of type Y. */
+static unsigned int vr4120_conflicts[NUM_FIX_VR4120_CLASSES];
+
+/* True if -mfix-vr4120 is in force. */
+static int mips_fix_vr4120;
+
+/* ...likewise -mfix-vr4130. */
+static int mips_fix_vr4130;
+
+/* ...likewise -mfix-24k. */
+static int mips_fix_24k;
+
+/* ...likewise -mfix-rm7000 */
+static int mips_fix_rm7000;
+
+/* ...likewise -mfix-cn63xxp1 */
+static bfd_boolean mips_fix_cn63xxp1;
+
+/* We don't relax branches by default, since this causes us to expand
+ `la .l2 - .l1' if there's a branch between .l1 and .l2, because we
+ fail to compute the offset before expanding the macro to the most
+ efficient expansion. */
+
+static int mips_relax_branch;
+
+/* The expansion of many macros depends on the type of symbol that
+ they refer to. For example, when generating position-dependent code,
+ a macro that refers to a symbol may have two different expansions,
+ one which uses GP-relative addresses and one which uses absolute
+ addresses. When generating SVR4-style PIC, a macro may have
+ different expansions for local and global symbols.
+
+ We handle these situations by generating both sequences and putting
+ them in variant frags. In position-dependent code, the first sequence
+ will be the GP-relative one and the second sequence will be the
+ absolute one. In SVR4 PIC, the first sequence will be for global
+ symbols and the second will be for local symbols.
+
+ The frag's "subtype" is RELAX_ENCODE (FIRST, SECOND), where FIRST and
+ SECOND are the lengths of the two sequences in bytes. These fields
+ can be extracted using RELAX_FIRST() and RELAX_SECOND(). In addition,
+ the subtype has the following flags:
+
+ RELAX_USE_SECOND
+ Set if it has been decided that we should use the second
+ sequence instead of the first.
+
+ RELAX_SECOND_LONGER
+ Set in the first variant frag if the macro's second implementation
+ is longer than its first. This refers to the macro as a whole,
+ not an individual relaxation.
+
+ RELAX_NOMACRO
+ Set in the first variant frag if the macro appeared in a .set nomacro
+ block and if one alternative requires a warning but the other does not.
+
+ RELAX_DELAY_SLOT
+ Like RELAX_NOMACRO, but indicates that the macro appears in a branch
+ delay slot.
+
+ RELAX_DELAY_SLOT_16BIT
+ Like RELAX_DELAY_SLOT, but indicates that the delay slot requires a
+ 16-bit instruction.
+
+ RELAX_DELAY_SLOT_SIZE_FIRST
+ Like RELAX_DELAY_SLOT, but indicates that the first implementation of
+ the macro is of the wrong size for the branch delay slot.
+
+ RELAX_DELAY_SLOT_SIZE_SECOND
+ Like RELAX_DELAY_SLOT, but indicates that the second implementation of
+ the macro is of the wrong size for the branch delay slot.
+
+ The frag's "opcode" points to the first fixup for relaxable code.
+
+ Relaxable macros are generated using a sequence such as:
+
+ relax_start (SYMBOL);
+ ... generate first expansion ...
+ relax_switch ();
+ ... generate second expansion ...
+ relax_end ();
+
+ The code and fixups for the unwanted alternative are discarded
+ by md_convert_frag. */
+#define RELAX_ENCODE(FIRST, SECOND) (((FIRST) << 8) | (SECOND))
+
+#define RELAX_FIRST(X) (((X) >> 8) & 0xff)
+#define RELAX_SECOND(X) ((X) & 0xff)
+#define RELAX_USE_SECOND 0x10000
+#define RELAX_SECOND_LONGER 0x20000
+#define RELAX_NOMACRO 0x40000
+#define RELAX_DELAY_SLOT 0x80000
+#define RELAX_DELAY_SLOT_16BIT 0x100000
+#define RELAX_DELAY_SLOT_SIZE_FIRST 0x200000
+#define RELAX_DELAY_SLOT_SIZE_SECOND 0x400000
+
+/* Branch without likely bit. If label is out of range, we turn:
+
+ beq reg1, reg2, label
+ delay slot
+
+ into
+
+ bne reg1, reg2, 0f
+ nop
+ j label
+ 0: delay slot
+
+ with the following opcode replacements:
+
+ beq <-> bne
+ blez <-> bgtz
+ bltz <-> bgez
+ bc1f <-> bc1t
+
+ bltzal <-> bgezal (with jal label instead of j label)
+
+ Even though keeping the delay slot instruction in the delay slot of
+ the branch would be more efficient, it would be very tricky to do
+ correctly, because we'd have to introduce a variable frag *after*
+ the delay slot instruction, and expand that instead. Let's do it
+ the easy way for now, even if the branch-not-taken case now costs
+ one additional instruction. Out-of-range branches are not supposed
+ to be common, anyway.
+
+ Branch likely. If label is out of range, we turn:
+
+ beql reg1, reg2, label
+ delay slot (annulled if branch not taken)
+
+ into
+
+ beql reg1, reg2, 1f
+ nop
+ beql $0, $0, 2f
+ nop
+ 1: j[al] label
+ delay slot (executed only if branch taken)
+ 2:
+
+ It would be possible to generate a shorter sequence by losing the
+ likely bit, generating something like:
+
+ bne reg1, reg2, 0f
+ nop
+ j[al] label
+ delay slot (executed only if branch taken)
+ 0:
+
+ beql -> bne
+ bnel -> beq
+ blezl -> bgtz
+ bgtzl -> blez
+ bltzl -> bgez
+ bgezl -> bltz
+ bc1fl -> bc1t
+ bc1tl -> bc1f
+
+ bltzall -> bgezal (with jal label instead of j label)
+ bgezall -> bltzal (ditto)
+
+
+ but it's not clear that it would actually improve performance. */
+#define RELAX_BRANCH_ENCODE(at, uncond, likely, link, toofar) \
+ ((relax_substateT) \
+ (0xc0000000 \
+ | ((at) & 0x1f) \
+ | ((toofar) ? 0x20 : 0) \
+ | ((link) ? 0x40 : 0) \
+ | ((likely) ? 0x80 : 0) \
+ | ((uncond) ? 0x100 : 0)))
+#define RELAX_BRANCH_P(i) (((i) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_UNCOND(i) (((i) & 0x100) != 0)
+#define RELAX_BRANCH_LIKELY(i) (((i) & 0x80) != 0)
+#define RELAX_BRANCH_LINK(i) (((i) & 0x40) != 0)
+#define RELAX_BRANCH_TOOFAR(i) (((i) & 0x20) != 0)
+#define RELAX_BRANCH_AT(i) ((i) & 0x1f)
+
+/* For mips16 code, we use an entirely different form of relaxation.
+ mips16 supports two versions of most instructions which take
+ immediate values: a small one which takes some small value, and a
+ larger one which takes a 16 bit value. Since branches also follow
+ this pattern, relaxing these values is required.
+
+ We can assemble both mips16 and normal MIPS code in a single
+ object. Therefore, we need to support this type of relaxation at
+ the same time that we support the relaxation described above. We
+ use the high bit of the subtype field to distinguish these cases.
+
+ The information we store for this type of relaxation is the
+ argument code found in the opcode file for this relocation, whether
+ the user explicitly requested a small or extended form, and whether
+ the relocation is in a jump or jal delay slot. That tells us the
+ size of the value, and how it should be stored. We also store
+ whether the fragment is considered to be extended or not. We also
+ store whether this is known to be a branch to a different section,
+ whether we have tried to relax this frag yet, and whether we have
+ ever extended a PC relative fragment because of a shift count. */
+#define RELAX_MIPS16_ENCODE(type, small, ext, dslot, jal_dslot) \
+ (0x80000000 \
+ | ((type) & 0xff) \
+ | ((small) ? 0x100 : 0) \
+ | ((ext) ? 0x200 : 0) \
+ | ((dslot) ? 0x400 : 0) \
+ | ((jal_dslot) ? 0x800 : 0))
+#define RELAX_MIPS16_P(i) (((i) & 0xc0000000) == 0x80000000)
+#define RELAX_MIPS16_TYPE(i) ((i) & 0xff)
+#define RELAX_MIPS16_USER_SMALL(i) (((i) & 0x100) != 0)
+#define RELAX_MIPS16_USER_EXT(i) (((i) & 0x200) != 0)
+#define RELAX_MIPS16_DSLOT(i) (((i) & 0x400) != 0)
+#define RELAX_MIPS16_JAL_DSLOT(i) (((i) & 0x800) != 0)
+#define RELAX_MIPS16_EXTENDED(i) (((i) & 0x1000) != 0)
+#define RELAX_MIPS16_MARK_EXTENDED(i) ((i) | 0x1000)
+#define RELAX_MIPS16_CLEAR_EXTENDED(i) ((i) &~ 0x1000)
+#define RELAX_MIPS16_LONG_BRANCH(i) (((i) & 0x2000) != 0)
+#define RELAX_MIPS16_MARK_LONG_BRANCH(i) ((i) | 0x2000)
+#define RELAX_MIPS16_CLEAR_LONG_BRANCH(i) ((i) &~ 0x2000)
+
+/* For microMIPS code, we use relaxation similar to one we use for
+ MIPS16 code. Some instructions that take immediate values support
+ two encodings: a small one which takes some small value, and a
+ larger one which takes a 16 bit value. As some branches also follow
+ this pattern, relaxing these values is required.
+
+ We can assemble both microMIPS and normal MIPS code in a single
+ object. Therefore, we need to support this type of relaxation at
+ the same time that we support the relaxation described above. We
+ use one of the high bits of the subtype field to distinguish these
+ cases.
+
+ The information we store for this type of relaxation is the argument
+ code found in the opcode file for this relocation, the register
+ selected as the assembler temporary, whether the branch is
+ unconditional, whether it is compact, whether it stores the link
+ address implicitly in $ra, whether relaxation of out-of-range 32-bit
+ branches to a sequence of instructions is enabled, and whether the
+ displacement of a branch is too large to fit as an immediate argument
+ of a 16-bit and a 32-bit branch, respectively. */
+#define RELAX_MICROMIPS_ENCODE(type, at, uncond, compact, link, \
+ relax32, toofar16, toofar32) \
+ (0x40000000 \
+ | ((type) & 0xff) \
+ | (((at) & 0x1f) << 8) \
+ | ((uncond) ? 0x2000 : 0) \
+ | ((compact) ? 0x4000 : 0) \
+ | ((link) ? 0x8000 : 0) \
+ | ((relax32) ? 0x10000 : 0) \
+ | ((toofar16) ? 0x20000 : 0) \
+ | ((toofar32) ? 0x40000 : 0))
+#define RELAX_MICROMIPS_P(i) (((i) & 0xc0000000) == 0x40000000)
+#define RELAX_MICROMIPS_TYPE(i) ((i) & 0xff)
+#define RELAX_MICROMIPS_AT(i) (((i) >> 8) & 0x1f)
+#define RELAX_MICROMIPS_UNCOND(i) (((i) & 0x2000) != 0)
+#define RELAX_MICROMIPS_COMPACT(i) (((i) & 0x4000) != 0)
+#define RELAX_MICROMIPS_LINK(i) (((i) & 0x8000) != 0)
+#define RELAX_MICROMIPS_RELAX32(i) (((i) & 0x10000) != 0)
+
+#define RELAX_MICROMIPS_TOOFAR16(i) (((i) & 0x20000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR16(i) ((i) | 0x20000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR16(i) ((i) & ~0x20000)
+#define RELAX_MICROMIPS_TOOFAR32(i) (((i) & 0x40000) != 0)
+#define RELAX_MICROMIPS_MARK_TOOFAR32(i) ((i) | 0x40000)
+#define RELAX_MICROMIPS_CLEAR_TOOFAR32(i) ((i) & ~0x40000)
+
+/* Sign-extend 16-bit value X. */
+#define SEXT_16BIT(X) ((((X) + 0x8000) & 0xffff) - 0x8000)
+
+/* Is the given value a sign-extended 32-bit value? */
+#define IS_SEXT_32BIT_NUM(x) \
+ (((x) &~ (offsetT) 0x7fffffff) == 0 \
+ || (((x) &~ (offsetT) 0x7fffffff) == ~ (offsetT) 0x7fffffff))
+
+/* Is the given value a sign-extended 16-bit value? */
+#define IS_SEXT_16BIT_NUM(x) \
+ (((x) &~ (offsetT) 0x7fff) == 0 \
+ || (((x) &~ (offsetT) 0x7fff) == ~ (offsetT) 0x7fff))
+
+/* Is the given value a sign-extended 12-bit value? */
+#define IS_SEXT_12BIT_NUM(x) \
+ (((((x) & 0xfff) ^ 0x800LL) - 0x800LL) == (x))
+
+/* Is the given value a sign-extended 9-bit value? */
+#define IS_SEXT_9BIT_NUM(x) \
+ (((((x) & 0x1ff) ^ 0x100LL) - 0x100LL) == (x))
+
+/* Is the given value a zero-extended 32-bit value? Or a negated one? */
+#define IS_ZEXT_32BIT_NUM(x) \
+ (((x) &~ (offsetT) 0xffffffff) == 0 \
+ || (((x) &~ (offsetT) 0xffffffff) == ~ (offsetT) 0xffffffff))
+
+/* Extract bits MASK << SHIFT from STRUCT and shift them right
+ SHIFT places. */
+#define EXTRACT_BITS(STRUCT, MASK, SHIFT) \
+ (((STRUCT) >> (SHIFT)) & (MASK))
+
+/* Extract the operand given by FIELD from mips_cl_insn INSN. */
+#define EXTRACT_OPERAND(MICROMIPS, FIELD, INSN) \
+ (!(MICROMIPS) \
+ ? EXTRACT_BITS ((INSN).insn_opcode, OP_MASK_##FIELD, OP_SH_##FIELD) \
+ : EXTRACT_BITS ((INSN).insn_opcode, \
+ MICROMIPSOP_MASK_##FIELD, MICROMIPSOP_SH_##FIELD))
+#define MIPS16_EXTRACT_OPERAND(FIELD, INSN) \
+ EXTRACT_BITS ((INSN).insn_opcode, \
+ MIPS16OP_MASK_##FIELD, \
+ MIPS16OP_SH_##FIELD)
+
+/* The MIPS16 EXTEND opcode, shifted left 16 places. */
+#define MIPS16_EXTEND (0xf000U << 16)
+
+/* Whether or not we are emitting a branch-likely macro. */
+static bfd_boolean emit_branch_likely_macro = FALSE;
+
+/* Global variables used when generating relaxable macros. See the
+ comment above RELAX_ENCODE for more details about how relaxation
+ is used. */
+static struct {
+ /* 0 if we're not emitting a relaxable macro.
+ 1 if we're emitting the first of the two relaxation alternatives.
+ 2 if we're emitting the second alternative. */
+ int sequence;
+
+ /* The first relaxable fixup in the current frag. (In other words,
+ the first fixup that refers to relaxable code.) */
+ fixS *first_fixup;
+
+ /* sizes[0] says how many bytes of the first alternative are stored in
+ the current frag. Likewise sizes[1] for the second alternative. */
+ unsigned int sizes[2];
+
+ /* The symbol on which the choice of sequence depends. */
+ symbolS *symbol;
+} mips_relax;
+
+/* Global variables used to decide whether a macro needs a warning. */
+static struct {
+ /* True if the macro is in a branch delay slot. */
+ bfd_boolean delay_slot_p;
+
+ /* Set to the length in bytes required if the macro is in a delay slot
+ that requires a specific length of instruction, otherwise zero. */
+ unsigned int delay_slot_length;
+
+ /* For relaxable macros, sizes[0] is the length of the first alternative
+ in bytes and sizes[1] is the length of the second alternative.
+ For non-relaxable macros, both elements give the length of the
+ macro in bytes. */
+ unsigned int sizes[2];
+
+ /* For relaxable macros, first_insn_sizes[0] is the length of the first
+ instruction of the first alternative in bytes and first_insn_sizes[1]
+ is the length of the first instruction of the second alternative.
+ For non-relaxable macros, both elements give the length of the first
+ instruction in bytes.
+
+ Set to zero if we haven't yet seen the first instruction. */
+ unsigned int first_insn_sizes[2];
+
+ /* For relaxable macros, insns[0] is the number of instructions for the
+ first alternative and insns[1] is the number of instructions for the
+ second alternative.
+
+ For non-relaxable macros, both elements give the number of
+ instructions for the macro. */
+ unsigned int insns[2];
+
+ /* The first variant frag for this macro. */
+ fragS *first_frag;
+} mips_macro_warning;
+
+/* Prototypes for static functions. */
+
+enum mips_regclass { MIPS_GR_REG, MIPS_FP_REG, MIPS16_REG };
+
+static void append_insn
+ (struct mips_cl_insn *, expressionS *, bfd_reloc_code_real_type *,
+ bfd_boolean expansionp);
+static void mips_no_prev_insn (void);
+static void macro_build (expressionS *, const char *, const char *, ...);
+static void mips16_macro_build
+ (expressionS *, const char *, const char *, va_list *);
+static void load_register (int, expressionS *, int);
+static void macro_start (void);
+static void macro_end (void);
+static void macro (struct mips_cl_insn *ip, char *str);
+static void mips16_macro (struct mips_cl_insn * ip);
+static void mips_ip (char *str, struct mips_cl_insn * ip);
+static void mips16_ip (char *str, struct mips_cl_insn * ip);
+static void mips16_immed
+ (char *, unsigned int, int, bfd_reloc_code_real_type, offsetT,
+ unsigned int, unsigned long *);
+static size_t my_getSmallExpression
+ (expressionS *, bfd_reloc_code_real_type *, char *);
+static void my_getExpression (expressionS *, char *);
+static void s_align (int);
+static void s_change_sec (int);
+static void s_change_section (int);
+static void s_cons (int);
+static void s_float_cons (int);
+static void s_mips_globl (int);
+static void s_option (int);
+static void s_mipsset (int);
+static void s_abicalls (int);
+static void s_cpload (int);
+static void s_cpsetup (int);
+static void s_cplocal (int);
+static void s_cprestore (int);
+static void s_cpreturn (int);
+static void s_dtprelword (int);
+static void s_dtpreldword (int);
+static void s_tprelword (int);
+static void s_tpreldword (int);
+static void s_gpvalue (int);
+static void s_gpword (int);
+static void s_gpdword (int);
+static void s_ehword (int);
+static void s_cpadd (int);
+static void s_insn (int);
+static void s_nan (int);
+static void s_module (int);
+static void s_mips_ent (int);
+static void s_mips_end (int);
+static void s_mips_frame (int);
+static void s_mips_mask (int reg_type);
+static void s_mips_stab (int);
+static void s_mips_weakext (int);
+static void s_mips_file (int);
+static void s_mips_loc (int);
+static bfd_boolean pic_need_relax (symbolS *, asection *);
+static int relaxed_branch_length (fragS *, asection *, int);
+static int relaxed_micromips_16bit_branch_length (fragS *, asection *, int);
+static int relaxed_micromips_32bit_branch_length (fragS *, asection *, int);
+static void file_mips_check_options (void);
+
+/* Table and functions used to map between CPU/ISA names, and
+ ISA levels, and CPU numbers. */
+
+struct mips_cpu_info
+{
+ const char *name; /* CPU or ISA name. */
+ int flags; /* MIPS_CPU_* flags. */
+ int ase; /* Set of ASEs implemented by the CPU. */
+ int isa; /* ISA level. */
+ int cpu; /* CPU number (default CPU if ISA). */
+};
+
+#define MIPS_CPU_IS_ISA 0x0001 /* Is this an ISA? (If 0, a CPU.) */
+
+static const struct mips_cpu_info *mips_parse_cpu (const char *, const char *);
+static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
+static const struct mips_cpu_info *mips_cpu_info_from_arch (int);
+
+/* Command-line options. */
+const char *md_shortopts = "O::g::G:";
+
+enum options
+ {
+ OPTION_MARCH = OPTION_MD_BASE,
+ OPTION_MTUNE,
+ OPTION_MIPS1,
+ OPTION_MIPS2,
+ OPTION_MIPS3,
+ OPTION_MIPS4,
+ OPTION_MIPS5,
+ OPTION_MIPS32,
+ OPTION_MIPS64,
+ OPTION_MIPS32R2,
+ OPTION_MIPS32R3,
+ OPTION_MIPS32R5,
+ OPTION_MIPS32R6,
+ OPTION_MIPS64R2,
+ OPTION_MIPS64R3,
+ OPTION_MIPS64R5,
+ OPTION_MIPS64R6,
+ OPTION_MIPS16,
+ OPTION_NO_MIPS16,
+ OPTION_MIPS3D,
+ OPTION_NO_MIPS3D,
+ OPTION_MDMX,
+ OPTION_NO_MDMX,
+ OPTION_DSP,
+ OPTION_NO_DSP,
+ OPTION_MT,
+ OPTION_NO_MT,
+ OPTION_VIRT,
+ OPTION_NO_VIRT,
+ OPTION_MSA,
+ OPTION_NO_MSA,
+ OPTION_SMARTMIPS,
+ OPTION_NO_SMARTMIPS,
+ OPTION_DSPR2,
+ OPTION_NO_DSPR2,
+ OPTION_EVA,
+ OPTION_NO_EVA,
+ OPTION_XPA,
+ OPTION_NO_XPA,
+ OPTION_MICROMIPS,
+ OPTION_NO_MICROMIPS,
+ OPTION_MCU,
+ OPTION_NO_MCU,
+ OPTION_COMPAT_ARCH_BASE,
+ OPTION_M4650,
+ OPTION_NO_M4650,
+ OPTION_M4010,
+ OPTION_NO_M4010,
+ OPTION_M4100,
+ OPTION_NO_M4100,
+ OPTION_M3900,
+ OPTION_NO_M3900,
+ OPTION_M7000_HILO_FIX,
+ OPTION_MNO_7000_HILO_FIX,
+ OPTION_FIX_24K,
+ OPTION_NO_FIX_24K,
+ OPTION_FIX_RM7000,
+ OPTION_NO_FIX_RM7000,
+ OPTION_FIX_LOONGSON2F_JUMP,
+ OPTION_NO_FIX_LOONGSON2F_JUMP,
+ OPTION_FIX_LOONGSON2F_NOP,
+ OPTION_NO_FIX_LOONGSON2F_NOP,
+ OPTION_FIX_VR4120,
+ OPTION_NO_FIX_VR4120,
+ OPTION_FIX_VR4130,
+ OPTION_NO_FIX_VR4130,
+ OPTION_FIX_CN63XXP1,
+ OPTION_NO_FIX_CN63XXP1,
+ OPTION_TRAP,
+ OPTION_BREAK,
+ OPTION_EB,
+ OPTION_EL,
+ OPTION_FP32,
+ OPTION_GP32,
+ OPTION_CONSTRUCT_FLOATS,
+ OPTION_NO_CONSTRUCT_FLOATS,
+ OPTION_FP64,
+ OPTION_FPXX,
+ OPTION_GP64,
+ OPTION_RELAX_BRANCH,
+ OPTION_NO_RELAX_BRANCH,
+ OPTION_INSN32,
+ OPTION_NO_INSN32,
+ OPTION_MSHARED,
+ OPTION_MNO_SHARED,
+ OPTION_MSYM32,
+ OPTION_MNO_SYM32,
+ OPTION_SOFT_FLOAT,
+ OPTION_HARD_FLOAT,
+ OPTION_SINGLE_FLOAT,
+ OPTION_DOUBLE_FLOAT,
+ OPTION_32,
+ OPTION_CALL_SHARED,
+ OPTION_CALL_NONPIC,
+ OPTION_NON_SHARED,
+ OPTION_XGOT,
+ OPTION_MABI,
+ OPTION_N32,
+ OPTION_64,
+ OPTION_MDEBUG,
+ OPTION_NO_MDEBUG,
+ OPTION_PDR,
+ OPTION_NO_PDR,
+ OPTION_MVXWORKS_PIC,
+ OPTION_NAN,
+ OPTION_ODD_SPREG,
+ OPTION_NO_ODD_SPREG,
+ OPTION_END_OF_ENUM
+ };
+
+struct option md_longopts[] =
+{
+ /* Options which specify architecture. */
+ {"march", required_argument, NULL, OPTION_MARCH},
+ {"mtune", required_argument, NULL, OPTION_MTUNE},
+ {"mips0", no_argument, NULL, OPTION_MIPS1},
+ {"mips1", no_argument, NULL, OPTION_MIPS1},
+ {"mips2", no_argument, NULL, OPTION_MIPS2},
+ {"mips3", no_argument, NULL, OPTION_MIPS3},
+ {"mips4", no_argument, NULL, OPTION_MIPS4},
+ {"mips5", no_argument, NULL, OPTION_MIPS5},
+ {"mips32", no_argument, NULL, OPTION_MIPS32},
+ {"mips64", no_argument, NULL, OPTION_MIPS64},
+ {"mips32r2", no_argument, NULL, OPTION_MIPS32R2},
+ {"mips32r3", no_argument, NULL, OPTION_MIPS32R3},
+ {"mips32r5", no_argument, NULL, OPTION_MIPS32R5},
+ {"mips32r6", no_argument, NULL, OPTION_MIPS32R6},
+ {"mips64r2", no_argument, NULL, OPTION_MIPS64R2},
+ {"mips64r3", no_argument, NULL, OPTION_MIPS64R3},
+ {"mips64r5", no_argument, NULL, OPTION_MIPS64R5},
+ {"mips64r6", no_argument, NULL, OPTION_MIPS64R6},
+
+ /* Options which specify Application Specific Extensions (ASEs). */
+ {"mips16", no_argument, NULL, OPTION_MIPS16},
+ {"no-mips16", no_argument, NULL, OPTION_NO_MIPS16},
+ {"mips3d", no_argument, NULL, OPTION_MIPS3D},
+ {"no-mips3d", no_argument, NULL, OPTION_NO_MIPS3D},
+ {"mdmx", no_argument, NULL, OPTION_MDMX},
+ {"no-mdmx", no_argument, NULL, OPTION_NO_MDMX},
+ {"mdsp", no_argument, NULL, OPTION_DSP},
+ {"mno-dsp", no_argument, NULL, OPTION_NO_DSP},
+ {"mmt", no_argument, NULL, OPTION_MT},
+ {"mno-mt", no_argument, NULL, OPTION_NO_MT},
+ {"msmartmips", no_argument, NULL, OPTION_SMARTMIPS},
+ {"mno-smartmips", no_argument, NULL, OPTION_NO_SMARTMIPS},
+ {"mdspr2", no_argument, NULL, OPTION_DSPR2},
+ {"mno-dspr2", no_argument, NULL, OPTION_NO_DSPR2},
+ {"meva", no_argument, NULL, OPTION_EVA},
+ {"mno-eva", no_argument, NULL, OPTION_NO_EVA},
+ {"mmicromips", no_argument, NULL, OPTION_MICROMIPS},
+ {"mno-micromips", no_argument, NULL, OPTION_NO_MICROMIPS},
+ {"mmcu", no_argument, NULL, OPTION_MCU},
+ {"mno-mcu", no_argument, NULL, OPTION_NO_MCU},
+ {"mvirt", no_argument, NULL, OPTION_VIRT},
+ {"mno-virt", no_argument, NULL, OPTION_NO_VIRT},
+ {"mmsa", no_argument, NULL, OPTION_MSA},
+ {"mno-msa", no_argument, NULL, OPTION_NO_MSA},
+ {"mxpa", no_argument, NULL, OPTION_XPA},
+ {"mno-xpa", no_argument, NULL, OPTION_NO_XPA},
+
+ /* Old-style architecture options. Don't add more of these. */
+ {"m4650", no_argument, NULL, OPTION_M4650},
+ {"no-m4650", no_argument, NULL, OPTION_NO_M4650},
+ {"m4010", no_argument, NULL, OPTION_M4010},
+ {"no-m4010", no_argument, NULL, OPTION_NO_M4010},
+ {"m4100", no_argument, NULL, OPTION_M4100},
+ {"no-m4100", no_argument, NULL, OPTION_NO_M4100},
+ {"m3900", no_argument, NULL, OPTION_M3900},
+ {"no-m3900", no_argument, NULL, OPTION_NO_M3900},
+
+ /* Options which enable bug fixes. */
+ {"mfix7000", no_argument, NULL, OPTION_M7000_HILO_FIX},
+ {"no-fix-7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+ {"mno-fix7000", no_argument, NULL, OPTION_MNO_7000_HILO_FIX},
+ {"mfix-loongson2f-jump", no_argument, NULL, OPTION_FIX_LOONGSON2F_JUMP},
+ {"mno-fix-loongson2f-jump", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_JUMP},
+ {"mfix-loongson2f-nop", no_argument, NULL, OPTION_FIX_LOONGSON2F_NOP},
+ {"mno-fix-loongson2f-nop", no_argument, NULL, OPTION_NO_FIX_LOONGSON2F_NOP},
+ {"mfix-vr4120", no_argument, NULL, OPTION_FIX_VR4120},
+ {"mno-fix-vr4120", no_argument, NULL, OPTION_NO_FIX_VR4120},
+ {"mfix-vr4130", no_argument, NULL, OPTION_FIX_VR4130},
+ {"mno-fix-vr4130", no_argument, NULL, OPTION_NO_FIX_VR4130},
+ {"mfix-24k", no_argument, NULL, OPTION_FIX_24K},
+ {"mno-fix-24k", no_argument, NULL, OPTION_NO_FIX_24K},
+ {"mfix-rm7000", no_argument, NULL, OPTION_FIX_RM7000},
+ {"mno-fix-rm7000", no_argument, NULL, OPTION_NO_FIX_RM7000},
+ {"mfix-cn63xxp1", no_argument, NULL, OPTION_FIX_CN63XXP1},
+ {"mno-fix-cn63xxp1", no_argument, NULL, OPTION_NO_FIX_CN63XXP1},
+
+ /* Miscellaneous options. */
+ {"trap", no_argument, NULL, OPTION_TRAP},
+ {"no-break", no_argument, NULL, OPTION_TRAP},
+ {"break", no_argument, NULL, OPTION_BREAK},
+ {"no-trap", no_argument, NULL, OPTION_BREAK},
+ {"EB", no_argument, NULL, OPTION_EB},
+ {"EL", no_argument, NULL, OPTION_EL},
+ {"mfp32", no_argument, NULL, OPTION_FP32},
+ {"mgp32", no_argument, NULL, OPTION_GP32},
+ {"construct-floats", no_argument, NULL, OPTION_CONSTRUCT_FLOATS},
+ {"no-construct-floats", no_argument, NULL, OPTION_NO_CONSTRUCT_FLOATS},
+ {"mfp64", no_argument, NULL, OPTION_FP64},
+ {"mfpxx", no_argument, NULL, OPTION_FPXX},
+ {"mgp64", no_argument, NULL, OPTION_GP64},
+ {"relax-branch", no_argument, NULL, OPTION_RELAX_BRANCH},
+ {"no-relax-branch", no_argument, NULL, OPTION_NO_RELAX_BRANCH},
+ {"minsn32", no_argument, NULL, OPTION_INSN32},
+ {"mno-insn32", no_argument, NULL, OPTION_NO_INSN32},
+ {"mshared", no_argument, NULL, OPTION_MSHARED},
+ {"mno-shared", no_argument, NULL, OPTION_MNO_SHARED},
+ {"msym32", no_argument, NULL, OPTION_MSYM32},
+ {"mno-sym32", no_argument, NULL, OPTION_MNO_SYM32},
+ {"msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT},
+ {"mhard-float", no_argument, NULL, OPTION_HARD_FLOAT},
+ {"msingle-float", no_argument, NULL, OPTION_SINGLE_FLOAT},
+ {"mdouble-float", no_argument, NULL, OPTION_DOUBLE_FLOAT},
+ {"modd-spreg", no_argument, NULL, OPTION_ODD_SPREG},
+ {"mno-odd-spreg", no_argument, NULL, OPTION_NO_ODD_SPREG},
+
+ /* Strictly speaking this next option is ELF specific,
+ but we allow it for other ports as well in order to
+ make testing easier. */
+ {"32", no_argument, NULL, OPTION_32},
+
+ /* ELF-specific options. */
+ {"KPIC", no_argument, NULL, OPTION_CALL_SHARED},
+ {"call_shared", no_argument, NULL, OPTION_CALL_SHARED},
+ {"call_nonpic", no_argument, NULL, OPTION_CALL_NONPIC},
+ {"non_shared", no_argument, NULL, OPTION_NON_SHARED},
+ {"xgot", no_argument, NULL, OPTION_XGOT},
+ {"mabi", required_argument, NULL, OPTION_MABI},
+ {"n32", no_argument, NULL, OPTION_N32},
+ {"64", no_argument, NULL, OPTION_64},
+ {"mdebug", no_argument, NULL, OPTION_MDEBUG},
+ {"no-mdebug", no_argument, NULL, OPTION_NO_MDEBUG},
+ {"mpdr", no_argument, NULL, OPTION_PDR},
+ {"mno-pdr", no_argument, NULL, OPTION_NO_PDR},
+ {"mvxworks-pic", no_argument, NULL, OPTION_MVXWORKS_PIC},
+ {"mnan", required_argument, NULL, OPTION_NAN},
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Information about either an Application Specific Extension or an
+ optional architecture feature that, for simplicity, we treat in the
+ same way as an ASE. */
+struct mips_ase
+{
+ /* The name of the ASE, used in both the command-line and .set options. */
+ const char *name;
+
+ /* The associated ASE_* flags. If the ASE is available on both 32-bit
+ and 64-bit architectures, the flags here refer to the subset that
+ is available on both. */
+ unsigned int flags;
+
+ /* The ASE_* flag used for instructions that are available on 64-bit
+ architectures but that are not included in FLAGS. */
+ unsigned int flags64;
+
+ /* The command-line options that turn the ASE on and off. */
+ int option_on;
+ int option_off;
+
+ /* The minimum required architecture revisions for MIPS32, MIPS64,
+ microMIPS32 and microMIPS64, or -1 if the extension isn't supported. */
+ int mips32_rev;
+ int mips64_rev;
+ int micromips32_rev;
+ int micromips64_rev;
+
+ /* The architecture where the ASE was removed or -1 if the extension has not
+ been removed. */
+ int rem_rev;
+};
+
+/* A table of all supported ASEs. */
+static const struct mips_ase mips_ases[] = {
+ { "dsp", ASE_DSP, ASE_DSP64,
+ OPTION_DSP, OPTION_NO_DSP,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "dspr2", ASE_DSP | ASE_DSPR2, 0,
+ OPTION_DSPR2, OPTION_NO_DSPR2,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "eva", ASE_EVA, 0,
+ OPTION_EVA, OPTION_NO_EVA,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "mcu", ASE_MCU, 0,
+ OPTION_MCU, OPTION_NO_MCU,
+ 2, 2, 2, 2,
+ -1 },
+
+ /* Deprecated in MIPS64r5, but we don't implement that yet. */
+ { "mdmx", ASE_MDMX, 0,
+ OPTION_MDMX, OPTION_NO_MDMX,
+ -1, 1, -1, -1,
+ 6 },
+
+ /* Requires 64-bit FPRs, so the minimum MIPS32 revision is 2. */
+ { "mips3d", ASE_MIPS3D, 0,
+ OPTION_MIPS3D, OPTION_NO_MIPS3D,
+ 2, 1, -1, -1,
+ 6 },
+
+ { "mt", ASE_MT, 0,
+ OPTION_MT, OPTION_NO_MT,
+ 2, 2, -1, -1,
+ -1 },
+
+ { "smartmips", ASE_SMARTMIPS, 0,
+ OPTION_SMARTMIPS, OPTION_NO_SMARTMIPS,
+ 1, -1, -1, -1,
+ 6 },
+
+ { "virt", ASE_VIRT, ASE_VIRT64,
+ OPTION_VIRT, OPTION_NO_VIRT,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "msa", ASE_MSA, ASE_MSA64,
+ OPTION_MSA, OPTION_NO_MSA,
+ 2, 2, 2, 2,
+ -1 },
+
+ { "xpa", ASE_XPA, 0,
+ OPTION_XPA, OPTION_NO_XPA,
+ 2, 2, -1, -1,
+ -1 },
+};
+
+/* The set of ASEs that require -mfp64. */
+#define FP64_ASES (ASE_MIPS3D | ASE_MDMX | ASE_MSA)
+
+/* Groups of ASE_* flags that represent different revisions of an ASE. */
+static const unsigned int mips_ase_groups[] = {
+ ASE_DSP | ASE_DSPR2
+};
+
+/* Pseudo-op table.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book
+ should be defined here, but are currently unsupported: .alias,
+ .galive, .gjaldef, .gjrlive, .livereg, .noalias.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ specific to the type of debugging information being generated, and
+ should be defined by the object format: .aent, .begin, .bend,
+ .bgnb, .end, .endb, .ent, .fmask, .frame, .loc, .mask, .verstamp,
+ .vreg.
+
+ The following pseudo-ops from the Kane and Heinrich MIPS book are
+ not MIPS CPU specific, but are also not specific to the object file
+ format. This file is probably the best place to define them, but
+ they are not currently supported: .asm0, .endr, .lab, .struct. */
+
+static const pseudo_typeS mips_pseudo_table[] =
+{
+ /* MIPS specific pseudo-ops. */
+ {"option", s_option, 0},
+ {"set", s_mipsset, 0},
+ {"rdata", s_change_sec, 'r'},
+ {"sdata", s_change_sec, 's'},
+ {"livereg", s_ignore, 0},
+ {"abicalls", s_abicalls, 0},
+ {"cpload", s_cpload, 0},
+ {"cpsetup", s_cpsetup, 0},
+ {"cplocal", s_cplocal, 0},
+ {"cprestore", s_cprestore, 0},
+ {"cpreturn", s_cpreturn, 0},
+ {"dtprelword", s_dtprelword, 0},
+ {"dtpreldword", s_dtpreldword, 0},
+ {"tprelword", s_tprelword, 0},
+ {"tpreldword", s_tpreldword, 0},
+ {"gpvalue", s_gpvalue, 0},
+ {"gpword", s_gpword, 0},
+ {"gpdword", s_gpdword, 0},
+ {"ehword", s_ehword, 0},
+ {"cpadd", s_cpadd, 0},
+ {"insn", s_insn, 0},
+ {"nan", s_nan, 0},
+ {"module", s_module, 0},
+
+ /* Relatively generic pseudo-ops that happen to be used on MIPS
+ chips. */
+ {"asciiz", stringer, 8 + 1},
+ {"bss", s_change_sec, 'b'},
+ {"err", s_err, 0},
+ {"half", s_cons, 1},
+ {"dword", s_cons, 3},
+ {"weakext", s_mips_weakext, 0},
+ {"origin", s_org, 0},
+ {"repeat", s_rept, 0},
+
+ /* For MIPS this is non-standard, but we define it for consistency. */
+ {"sbss", s_change_sec, 'B'},
+
+ /* These pseudo-ops are defined in read.c, but must be overridden
+ here for one reason or another. */
+ {"align", s_align, 0},
+ {"byte", s_cons, 0},
+ {"data", s_change_sec, 'd'},
+ {"double", s_float_cons, 'd'},
+ {"float", s_float_cons, 'f'},
+ {"globl", s_mips_globl, 0},
+ {"global", s_mips_globl, 0},
+ {"hword", s_cons, 1},
+ {"int", s_cons, 2},
+ {"long", s_cons, 2},
+ {"octa", s_cons, 4},
+ {"quad", s_cons, 3},
+ {"section", s_change_section, 0},
+ {"short", s_cons, 1},
+ {"single", s_float_cons, 'f'},
+ {"stabd", s_mips_stab, 'd'},
+ {"stabn", s_mips_stab, 'n'},
+ {"stabs", s_mips_stab, 's'},
+ {"text", s_change_sec, 't'},
+ {"word", s_cons, 2},
+
+ { "extern", ecoff_directive_extern, 0},
+
+ { NULL, NULL, 0 },
+};
+
+static const pseudo_typeS mips_nonecoff_pseudo_table[] =
+{
+ /* These pseudo-ops should be defined by the object file format.
+ However, a.out doesn't support them, so we have versions here. */
+ {"aent", s_mips_ent, 1},
+ {"bgnb", s_ignore, 0},
+ {"end", s_mips_end, 0},
+ {"endb", s_ignore, 0},
+ {"ent", s_mips_ent, 0},
+ {"file", s_mips_file, 0},
+ {"fmask", s_mips_mask, 'F'},
+ {"frame", s_mips_frame, 0},
+ {"loc", s_mips_loc, 0},
+ {"mask", s_mips_mask, 'R'},
+ {"verstamp", s_ignore, 0},
+ { NULL, NULL, 0 },
+};
+
+/* Export the ABI address size for use by TC_ADDRESS_BYTES for the
+ purpose of the `.dc.a' internal pseudo-op. */
+
+int
+mips_address_bytes (void)
+{
+ file_mips_check_options ();
+ return HAVE_64BIT_ADDRESSES ? 8 : 4;
+}
+
+extern void pop_insert (const pseudo_typeS *);
+
+void
+mips_pop_insert (void)
+{
+ pop_insert (mips_pseudo_table);
+ if (! ECOFF_DEBUGGING)
+ pop_insert (mips_nonecoff_pseudo_table);
+}
+
+/* Symbols labelling the current insn. */
+
+struct insn_label_list
+{
+ struct insn_label_list *next;
+ symbolS *label;
+};
+
+static struct insn_label_list *free_insn_labels;
+#define label_list tc_segment_info_data.labels
+
+static void mips_clear_insn_labels (void);
+static void mips_mark_labels (void);
+static void mips_compressed_mark_labels (void);
+
+static inline void
+mips_clear_insn_labels (void)
+{
+ register struct insn_label_list **pl;
+ segment_info_type *si;
+
+ if (now_seg)
+ {
+ for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+ ;
+
+ si = seg_info (now_seg);
+ *pl = si->label_list;
+ si->label_list = NULL;
+ }
+}
+
+/* Mark instruction labels in MIPS16/microMIPS mode. */
+
+static inline void
+mips_mark_labels (void)
+{
+ if (HAVE_CODE_COMPRESSION)
+ mips_compressed_mark_labels ();
+}
+
+static char *expr_end;
+
+/* An expression in a macro instruction. This is set by mips_ip and
+ mips16_ip and when populated is always an O_constant. */
+
+static expressionS imm_expr;
+
+/* The relocatable field in an instruction and the relocs associated
+ with it. These variables are used for instructions like LUI and
+ JAL as well as true offsets. They are also used for address
+ operands in macros. */
+
+static expressionS offset_expr;
+static bfd_reloc_code_real_type offset_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+/* This is set to the resulting size of the instruction to be produced
+ by mips16_ip if an explicit extension is used or by mips_ip if an
+ explicit size is supplied. */
+
+static unsigned int forced_insn_length;
+
+/* True if we are assembling an instruction. All dot symbols defined during
+ this time should be treated as code labels. */
+
+static bfd_boolean mips_assembling_insn;
+
+/* The pdr segment for per procedure frame/regmask info. Not used for
+ ECOFF debugging. */
+
+static segT pdr_seg;
+
+/* The default target format to use. */
+
+#if defined (TE_FreeBSD)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips-freebsd"
+#elif defined (TE_TMIPS)
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX "trad" ENDIAN "mips"
+#else
+#define ELF_TARGET(PREFIX, ENDIAN) PREFIX ENDIAN "mips"
+#endif
+
+const char *
+mips_target_format (void)
+{
+ switch (OUTPUT_FLAVOR)
+ {
+ case bfd_target_elf_flavour:
+#ifdef TE_VXWORKS
+ if (!HAVE_64BIT_OBJECTS && !HAVE_NEWABI)
+ return (target_big_endian
+ ? "elf32-bigmips-vxworks"
+ : "elf32-littlemips-vxworks");
+#endif
+ return (target_big_endian
+ ? (HAVE_64BIT_OBJECTS
+ ? ELF_TARGET ("elf64-", "big")
+ : (HAVE_NEWABI
+ ? ELF_TARGET ("elf32-n", "big")
+ : ELF_TARGET ("elf32-", "big")))
+ : (HAVE_64BIT_OBJECTS
+ ? ELF_TARGET ("elf64-", "little")
+ : (HAVE_NEWABI
+ ? ELF_TARGET ("elf32-n", "little")
+ : ELF_TARGET ("elf32-", "little"))));
+ default:
+ abort ();
+ return NULL;
+ }
+}
+
+/* Return the ISA revision that is currently in use, or 0 if we are
+ generating code for MIPS V or below. */
+
+static int
+mips_isa_rev (void)
+{
+ if (mips_opts.isa == ISA_MIPS32R2 || mips_opts.isa == ISA_MIPS64R2)
+ return 2;
+
+ if (mips_opts.isa == ISA_MIPS32R3 || mips_opts.isa == ISA_MIPS64R3)
+ return 3;
+
+ if (mips_opts.isa == ISA_MIPS32R5 || mips_opts.isa == ISA_MIPS64R5)
+ return 5;
+
+ if (mips_opts.isa == ISA_MIPS32R6 || mips_opts.isa == ISA_MIPS64R6)
+ return 6;
+
+ /* microMIPS implies revision 2 or above. */
+ if (mips_opts.micromips)
+ return 2;
+
+ if (mips_opts.isa == ISA_MIPS32 || mips_opts.isa == ISA_MIPS64)
+ return 1;
+
+ return 0;
+}
+
+/* Return the mask of all ASEs that are revisions of those in FLAGS. */
+
+static unsigned int
+mips_ase_mask (unsigned int flags)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ase_groups); i++)
+ if (flags & mips_ase_groups[i])
+ flags |= mips_ase_groups[i];
+ return flags;
+}
+
+/* Check whether the current ISA supports ASE. Issue a warning if
+ appropriate. */
+
+static void
+mips_check_isa_supports_ase (const struct mips_ase *ase)
+{
+ const char *base;
+ int min_rev, size;
+ static unsigned int warned_isa;
+ static unsigned int warned_fp32;
+
+ if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+ min_rev = mips_opts.micromips ? ase->micromips64_rev : ase->mips64_rev;
+ else
+ min_rev = mips_opts.micromips ? ase->micromips32_rev : ase->mips32_rev;
+ if ((min_rev < 0 || mips_isa_rev () < min_rev)
+ && (warned_isa & ase->flags) != ase->flags)
+ {
+ warned_isa |= ase->flags;
+ base = mips_opts.micromips ? "microMIPS" : "MIPS";
+ size = ISA_HAS_64BIT_REGS (mips_opts.isa) ? 64 : 32;
+ if (min_rev < 0)
+ as_warn (_("the %d-bit %s architecture does not support the"
+ " `%s' extension"), size, base, ase->name);
+ else
+ as_warn (_("the `%s' extension requires %s%d revision %d or greater"),
+ ase->name, base, size, min_rev);
+ }
+ else if ((ase->rem_rev > 0 && mips_isa_rev () >= ase->rem_rev)
+ && (warned_isa & ase->flags) != ase->flags)
+ {
+ warned_isa |= ase->flags;
+ base = mips_opts.micromips ? "microMIPS" : "MIPS";
+ size = ISA_HAS_64BIT_REGS (mips_opts.isa) ? 64 : 32;
+ as_warn (_("the `%s' extension was removed in %s%d revision %d"),
+ ase->name, base, size, ase->rem_rev);
+ }
+
+ if ((ase->flags & FP64_ASES)
+ && mips_opts.fp != 64
+ && (warned_fp32 & ase->flags) != ase->flags)
+ {
+ warned_fp32 |= ase->flags;
+ as_warn (_("the `%s' extension requires 64-bit FPRs"), ase->name);
+ }
+}
+
+/* Check all enabled ASEs to see whether they are supported by the
+ chosen architecture. */
+
+static void
+mips_check_isa_supports_ases (void)
+{
+ unsigned int i, mask;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ {
+ mask = mips_ase_mask (mips_ases[i].flags);
+ if ((mips_opts.ase & mask) == mips_ases[i].flags)
+ mips_check_isa_supports_ase (&mips_ases[i]);
+ }
+}
+
+/* Set the state of ASE to ENABLED_P. Return the mask of ASE_* flags
+ that were affected. */
+
+static unsigned int
+mips_set_ase (const struct mips_ase *ase, struct mips_set_options *opts,
+ bfd_boolean enabled_p)
+{
+ unsigned int mask;
+
+ mask = mips_ase_mask (ase->flags);
+ opts->ase &= ~mask;
+ if (enabled_p)
+ opts->ase |= ase->flags;
+ return mask;
+}
+
+/* Return the ASE called NAME, or null if none. */
+
+static const struct mips_ase *
+mips_lookup_ase (const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ if (strcmp (name, mips_ases[i].name) == 0)
+ return &mips_ases[i];
+ return NULL;
+}
+
+/* Return the length of a microMIPS instruction in bytes. If bits of
+ the mask beyond the low 16 are 0, then it is a 16-bit instruction.
+ Otherwise assume a 32-bit instruction; 48-bit instructions (0x1f
+ major opcode) will require further modifications to the opcode
+ table. */
+
+static inline unsigned int
+micromips_insn_length (const struct mips_opcode *mo)
+{
+ return (mo->mask >> 16) == 0 ? 2 : 4;
+}
+
+/* Return the length of MIPS16 instruction OPCODE. */
+
+static inline unsigned int
+mips16_opcode_length (unsigned long opcode)
+{
+ return (opcode >> 16) == 0 ? 2 : 4;
+}
+
+/* Return the length of instruction INSN. */
+
+static inline unsigned int
+insn_length (const struct mips_cl_insn *insn)
+{
+ if (mips_opts.micromips)
+ return micromips_insn_length (insn->insn_mo);
+ else if (mips_opts.mips16)
+ return mips16_opcode_length (insn->insn_opcode);
+ else
+ return 4;
+}
+
+/* Initialise INSN from opcode entry MO. Leave its position unspecified. */
+
+static void
+create_insn (struct mips_cl_insn *insn, const struct mips_opcode *mo)
+{
+ size_t i;
+
+ insn->insn_mo = mo;
+ insn->insn_opcode = mo->match;
+ insn->frag = NULL;
+ insn->where = 0;
+ for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+ insn->fixp[i] = NULL;
+ insn->fixed_p = (mips_opts.noreorder > 0);
+ insn->noreorder_p = (mips_opts.noreorder > 0);
+ insn->mips16_absolute_jump_p = 0;
+ insn->complete_p = 0;
+ insn->cleared_p = 0;
+}
+
+/* Get a list of all the operands in INSN. */
+
+static const struct mips_operand_array *
+insn_operands (const struct mips_cl_insn *insn)
+{
+ if (insn->insn_mo >= &mips_opcodes[0]
+ && insn->insn_mo < &mips_opcodes[NUMOPCODES])
+ return &mips_operands[insn->insn_mo - &mips_opcodes[0]];
+
+ if (insn->insn_mo >= &mips16_opcodes[0]
+ && insn->insn_mo < &mips16_opcodes[bfd_mips16_num_opcodes])
+ return &mips16_operands[insn->insn_mo - &mips16_opcodes[0]];
+
+ if (insn->insn_mo >= &micromips_opcodes[0]
+ && insn->insn_mo < &micromips_opcodes[bfd_micromips_num_opcodes])
+ return &micromips_operands[insn->insn_mo - &micromips_opcodes[0]];
+
+ abort ();
+}
+
+/* Get a description of operand OPNO of INSN. */
+
+static const struct mips_operand *
+insn_opno (const struct mips_cl_insn *insn, unsigned opno)
+{
+ const struct mips_operand_array *operands;
+
+ operands = insn_operands (insn);
+ if (opno >= MAX_OPERANDS || !operands->operand[opno])
+ abort ();
+ return operands->operand[opno];
+}
+
+/* Install UVAL as the value of OPERAND in INSN. */
+
+static inline void
+insn_insert_operand (struct mips_cl_insn *insn,
+ const struct mips_operand *operand, unsigned int uval)
+{
+ insn->insn_opcode = mips_insert_operand (operand, insn->insn_opcode, uval);
+}
+
+/* Extract the value of OPERAND from INSN. */
+
+static inline unsigned
+insn_extract_operand (const struct mips_cl_insn *insn,
+ const struct mips_operand *operand)
+{
+ return mips_extract_operand (operand, insn->insn_opcode);
+}
+
+/* Record the current MIPS16/microMIPS mode in now_seg. */
+
+static void
+mips_record_compressed_mode (void)
+{
+ segment_info_type *si;
+
+ si = seg_info (now_seg);
+ if (si->tc_segment_info_data.mips16 != mips_opts.mips16)
+ si->tc_segment_info_data.mips16 = mips_opts.mips16;
+ if (si->tc_segment_info_data.micromips != mips_opts.micromips)
+ si->tc_segment_info_data.micromips = mips_opts.micromips;
+}
+
+/* Read a standard MIPS instruction from BUF. */
+
+static unsigned long
+read_insn (char *buf)
+{
+ if (target_big_endian)
+ return bfd_getb32 ((bfd_byte *) buf);
+ else
+ return bfd_getl32 ((bfd_byte *) buf);
+}
+
+/* Write standard MIPS instruction INSN to BUF. Return a pointer to
+ the next byte. */
+
+static char *
+write_insn (char *buf, unsigned int insn)
+{
+ md_number_to_chars (buf, insn, 4);
+ return buf + 4;
+}
+
+/* Read a microMIPS or MIPS16 opcode from BUF, given that it
+ has length LENGTH. */
+
+static unsigned long
+read_compressed_insn (char *buf, unsigned int length)
+{
+ unsigned long insn;
+ unsigned int i;
+
+ insn = 0;
+ for (i = 0; i < length; i += 2)
+ {
+ insn <<= 16;
+ if (target_big_endian)
+ insn |= bfd_getb16 ((char *) buf);
+ else
+ insn |= bfd_getl16 ((char *) buf);
+ buf += 2;
+ }
+ return insn;
+}
+
+/* Write microMIPS or MIPS16 instruction INSN to BUF, given that the
+ instruction is LENGTH bytes long. Return a pointer to the next byte. */
+
+static char *
+write_compressed_insn (char *buf, unsigned int insn, unsigned int length)
+{
+ unsigned int i;
+
+ for (i = 0; i < length; i += 2)
+ md_number_to_chars (buf + i, insn >> ((length - i - 2) * 8), 2);
+ return buf + length;
+}
+
+/* Install INSN at the location specified by its "frag" and "where" fields. */
+
+static void
+install_insn (const struct mips_cl_insn *insn)
+{
+ char *f = insn->frag->fr_literal + insn->where;
+ if (HAVE_CODE_COMPRESSION)
+ write_compressed_insn (f, insn->insn_opcode, insn_length (insn));
+ else
+ write_insn (f, insn->insn_opcode);
+ mips_record_compressed_mode ();
+}
+
+/* Move INSN to offset WHERE in FRAG. Adjust the fixups accordingly
+ and install the opcode in the new location. */
+
+static void
+move_insn (struct mips_cl_insn *insn, fragS *frag, long where)
+{
+ size_t i;
+
+ insn->frag = frag;
+ insn->where = where;
+ for (i = 0; i < ARRAY_SIZE (insn->fixp); i++)
+ if (insn->fixp[i] != NULL)
+ {
+ insn->fixp[i]->fx_frag = frag;
+ insn->fixp[i]->fx_where = where;
+ }
+ install_insn (insn);
+}
+
+/* Add INSN to the end of the output. */
+
+static void
+add_fixed_insn (struct mips_cl_insn *insn)
+{
+ char *f = frag_more (insn_length (insn));
+ move_insn (insn, frag_now, f - frag_now->fr_literal);
+}
+
+/* Start a variant frag and move INSN to the start of the variant part,
+ marking it as fixed. The other arguments are as for frag_var. */
+
+static void
+add_relaxed_insn (struct mips_cl_insn *insn, int max_chars, int var,
+ relax_substateT subtype, symbolS *symbol, offsetT offset)
+{
+ frag_grow (max_chars);
+ move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal);
+ insn->fixed_p = 1;
+ frag_var (rs_machine_dependent, max_chars, var,
+ subtype, symbol, offset, NULL);
+}
+
+/* Insert N copies of INSN into the history buffer, starting at
+ position FIRST. Neither FIRST nor N need to be clipped. */
+
+static void
+insert_into_history (unsigned int first, unsigned int n,
+ const struct mips_cl_insn *insn)
+{
+ if (mips_relax.sequence != 2)
+ {
+ unsigned int i;
+
+ for (i = ARRAY_SIZE (history); i-- > first;)
+ if (i >= first + n)
+ history[i] = history[i - n];
+ else
+ history[i] = *insn;
+ }
+}
+
+/* Clear the error in insn_error. */
+
+static void
+clear_insn_error (void)
+{
+ memset (&insn_error, 0, sizeof (insn_error));
+}
+
+/* Possibly record error message MSG for the current instruction.
+ If the error is about a particular argument, ARGNUM is the 1-based
+ number of that argument, otherwise it is 0. FORMAT is the format
+ of MSG. Return true if MSG was used, false if the current message
+ was kept. */
+
+static bfd_boolean
+set_insn_error_format (int argnum, enum mips_insn_error_format format,
+ const char *msg)
+{
+ if (argnum == 0)
+ {
+ /* Give priority to errors against specific arguments, and to
+ the first whole-instruction message. */
+ if (insn_error.msg)
+ return FALSE;
+ }
+ else
+ {
+ /* Keep insn_error if it is against a later argument. */
+ if (argnum < insn_error.min_argnum)
+ return FALSE;
+
+ /* If both errors are against the same argument but are different,
+ give up on reporting a specific error for this argument.
+ See the comment about mips_insn_error for details. */
+ if (argnum == insn_error.min_argnum
+ && insn_error.msg
+ && strcmp (insn_error.msg, msg) != 0)
+ {
+ insn_error.msg = 0;
+ insn_error.min_argnum += 1;
+ return FALSE;
+ }
+ }
+ insn_error.min_argnum = argnum;
+ insn_error.format = format;
+ insn_error.msg = msg;
+ return TRUE;
+}
+
+/* Record an instruction error with no % format fields. ARGNUM and MSG are
+ as for set_insn_error_format. */
+
+static void
+set_insn_error (int argnum, const char *msg)
+{
+ set_insn_error_format (argnum, ERR_FMT_PLAIN, msg);
+}
+
+/* Record an instruction error with one %d field I. ARGNUM and MSG are
+ as for set_insn_error_format. */
+
+static void
+set_insn_error_i (int argnum, const char *msg, int i)
+{
+ if (set_insn_error_format (argnum, ERR_FMT_I, msg))
+ insn_error.u.i = i;
+}
+
+/* Record an instruction error with two %s fields S1 and S2. ARGNUM and MSG
+ are as for set_insn_error_format. */
+
+static void
+set_insn_error_ss (int argnum, const char *msg, const char *s1, const char *s2)
+{
+ if (set_insn_error_format (argnum, ERR_FMT_SS, msg))
+ {
+ insn_error.u.ss[0] = s1;
+ insn_error.u.ss[1] = s2;
+ }
+}
+
+/* Report the error in insn_error, which is against assembly code STR. */
+
+static void
+report_insn_error (const char *str)
+{
+ const char *msg;
+
+ msg = ACONCAT ((insn_error.msg, " `%s'", NULL));
+ switch (insn_error.format)
+ {
+ case ERR_FMT_PLAIN:
+ as_bad (msg, str);
+ break;
+
+ case ERR_FMT_I:
+ as_bad (msg, insn_error.u.i, str);
+ break;
+
+ case ERR_FMT_SS:
+ as_bad (msg, insn_error.u.ss[0], insn_error.u.ss[1], str);
+ break;
+ }
+}
+
+/* Initialize vr4120_conflicts. There is a bit of duplication here:
+ the idea is to make it obvious at a glance that each errata is
+ included. */
+
+static void
+init_vr4120_conflicts (void)
+{
+#define CONFLICT(FIRST, SECOND) \
+ vr4120_conflicts[FIX_VR4120_##FIRST] |= 1 << FIX_VR4120_##SECOND
+
+ /* Errata 21 - [D]DIV[U] after [D]MACC */
+ CONFLICT (MACC, DIV);
+ CONFLICT (DMACC, DIV);
+
+ /* Errata 23 - Continuous DMULT[U]/DMACC instructions. */
+ CONFLICT (DMULT, DMULT);
+ CONFLICT (DMULT, DMACC);
+ CONFLICT (DMACC, DMULT);
+ CONFLICT (DMACC, DMACC);
+
+ /* Errata 24 - MT{LO,HI} after [D]MACC */
+ CONFLICT (MACC, MTHILO);
+ CONFLICT (DMACC, MTHILO);
+
+ /* VR4181A errata MD(1): "If a MULT, MULTU, DMULT or DMULTU
+ instruction is executed immediately after a MACC or DMACC
+ instruction, the result of [either instruction] is incorrect." */
+ CONFLICT (MACC, MULT);
+ CONFLICT (MACC, DMULT);
+ CONFLICT (DMACC, MULT);
+ CONFLICT (DMACC, DMULT);
+
+ /* VR4181A errata MD(4): "If a MACC or DMACC instruction is
+ executed immediately after a DMULT, DMULTU, DIV, DIVU,
+ DDIV or DDIVU instruction, the result of the MACC or
+ DMACC instruction is incorrect.". */
+ CONFLICT (DMULT, MACC);
+ CONFLICT (DMULT, DMACC);
+ CONFLICT (DIV, MACC);
+ CONFLICT (DIV, DMACC);
+
+#undef CONFLICT
+}
+
+struct regname {
+ const char *name;
+ unsigned int num;
+};
+
+#define RNUM_MASK 0x00000ff
+#define RTYPE_MASK 0x0ffff00
+#define RTYPE_NUM 0x0000100
+#define RTYPE_FPU 0x0000200
+#define RTYPE_FCC 0x0000400
+#define RTYPE_VEC 0x0000800
+#define RTYPE_GP 0x0001000
+#define RTYPE_CP0 0x0002000
+#define RTYPE_PC 0x0004000
+#define RTYPE_ACC 0x0008000
+#define RTYPE_CCC 0x0010000
+#define RTYPE_VI 0x0020000
+#define RTYPE_VF 0x0040000
+#define RTYPE_R5900_I 0x0080000
+#define RTYPE_R5900_Q 0x0100000
+#define RTYPE_R5900_R 0x0200000
+#define RTYPE_R5900_ACC 0x0400000
+#define RTYPE_MSA 0x0800000
+#define RWARN 0x8000000
+
+#define GENERIC_REGISTER_NUMBERS \
+ {"$0", RTYPE_NUM | 0}, \
+ {"$1", RTYPE_NUM | 1}, \
+ {"$2", RTYPE_NUM | 2}, \
+ {"$3", RTYPE_NUM | 3}, \
+ {"$4", RTYPE_NUM | 4}, \
+ {"$5", RTYPE_NUM | 5}, \
+ {"$6", RTYPE_NUM | 6}, \
+ {"$7", RTYPE_NUM | 7}, \
+ {"$8", RTYPE_NUM | 8}, \
+ {"$9", RTYPE_NUM | 9}, \
+ {"$10", RTYPE_NUM | 10}, \
+ {"$11", RTYPE_NUM | 11}, \
+ {"$12", RTYPE_NUM | 12}, \
+ {"$13", RTYPE_NUM | 13}, \
+ {"$14", RTYPE_NUM | 14}, \
+ {"$15", RTYPE_NUM | 15}, \
+ {"$16", RTYPE_NUM | 16}, \
+ {"$17", RTYPE_NUM | 17}, \
+ {"$18", RTYPE_NUM | 18}, \
+ {"$19", RTYPE_NUM | 19}, \
+ {"$20", RTYPE_NUM | 20}, \
+ {"$21", RTYPE_NUM | 21}, \
+ {"$22", RTYPE_NUM | 22}, \
+ {"$23", RTYPE_NUM | 23}, \
+ {"$24", RTYPE_NUM | 24}, \
+ {"$25", RTYPE_NUM | 25}, \
+ {"$26", RTYPE_NUM | 26}, \
+ {"$27", RTYPE_NUM | 27}, \
+ {"$28", RTYPE_NUM | 28}, \
+ {"$29", RTYPE_NUM | 29}, \
+ {"$30", RTYPE_NUM | 30}, \
+ {"$31", RTYPE_NUM | 31}
+
+#define FPU_REGISTER_NAMES \
+ {"$f0", RTYPE_FPU | 0}, \
+ {"$f1", RTYPE_FPU | 1}, \
+ {"$f2", RTYPE_FPU | 2}, \
+ {"$f3", RTYPE_FPU | 3}, \
+ {"$f4", RTYPE_FPU | 4}, \
+ {"$f5", RTYPE_FPU | 5}, \
+ {"$f6", RTYPE_FPU | 6}, \
+ {"$f7", RTYPE_FPU | 7}, \
+ {"$f8", RTYPE_FPU | 8}, \
+ {"$f9", RTYPE_FPU | 9}, \
+ {"$f10", RTYPE_FPU | 10}, \
+ {"$f11", RTYPE_FPU | 11}, \
+ {"$f12", RTYPE_FPU | 12}, \
+ {"$f13", RTYPE_FPU | 13}, \
+ {"$f14", RTYPE_FPU | 14}, \
+ {"$f15", RTYPE_FPU | 15}, \
+ {"$f16", RTYPE_FPU | 16}, \
+ {"$f17", RTYPE_FPU | 17}, \
+ {"$f18", RTYPE_FPU | 18}, \
+ {"$f19", RTYPE_FPU | 19}, \
+ {"$f20", RTYPE_FPU | 20}, \
+ {"$f21", RTYPE_FPU | 21}, \
+ {"$f22", RTYPE_FPU | 22}, \
+ {"$f23", RTYPE_FPU | 23}, \
+ {"$f24", RTYPE_FPU | 24}, \
+ {"$f25", RTYPE_FPU | 25}, \
+ {"$f26", RTYPE_FPU | 26}, \
+ {"$f27", RTYPE_FPU | 27}, \
+ {"$f28", RTYPE_FPU | 28}, \
+ {"$f29", RTYPE_FPU | 29}, \
+ {"$f30", RTYPE_FPU | 30}, \
+ {"$f31", RTYPE_FPU | 31}
+
+#define FPU_CONDITION_CODE_NAMES \
+ {"$fcc0", RTYPE_FCC | 0}, \
+ {"$fcc1", RTYPE_FCC | 1}, \
+ {"$fcc2", RTYPE_FCC | 2}, \
+ {"$fcc3", RTYPE_FCC | 3}, \
+ {"$fcc4", RTYPE_FCC | 4}, \
+ {"$fcc5", RTYPE_FCC | 5}, \
+ {"$fcc6", RTYPE_FCC | 6}, \
+ {"$fcc7", RTYPE_FCC | 7}
+
+#define COPROC_CONDITION_CODE_NAMES \
+ {"$cc0", RTYPE_FCC | RTYPE_CCC | 0}, \
+ {"$cc1", RTYPE_FCC | RTYPE_CCC | 1}, \
+ {"$cc2", RTYPE_FCC | RTYPE_CCC | 2}, \
+ {"$cc3", RTYPE_FCC | RTYPE_CCC | 3}, \
+ {"$cc4", RTYPE_FCC | RTYPE_CCC | 4}, \
+ {"$cc5", RTYPE_FCC | RTYPE_CCC | 5}, \
+ {"$cc6", RTYPE_FCC | RTYPE_CCC | 6}, \
+ {"$cc7", RTYPE_FCC | RTYPE_CCC | 7}
+
+#define N32N64_SYMBOLIC_REGISTER_NAMES \
+ {"$a4", RTYPE_GP | 8}, \
+ {"$a5", RTYPE_GP | 9}, \
+ {"$a6", RTYPE_GP | 10}, \
+ {"$a7", RTYPE_GP | 11}, \
+ {"$ta0", RTYPE_GP | 8}, /* alias for $a4 */ \
+ {"$ta1", RTYPE_GP | 9}, /* alias for $a5 */ \
+ {"$ta2", RTYPE_GP | 10}, /* alias for $a6 */ \
+ {"$ta3", RTYPE_GP | 11}, /* alias for $a7 */ \
+ {"$t0", RTYPE_GP | 12}, \
+ {"$t1", RTYPE_GP | 13}, \
+ {"$t2", RTYPE_GP | 14}, \
+ {"$t3", RTYPE_GP | 15}
+
+#define O32_SYMBOLIC_REGISTER_NAMES \
+ {"$t0", RTYPE_GP | 8}, \
+ {"$t1", RTYPE_GP | 9}, \
+ {"$t2", RTYPE_GP | 10}, \
+ {"$t3", RTYPE_GP | 11}, \
+ {"$t4", RTYPE_GP | 12}, \
+ {"$t5", RTYPE_GP | 13}, \
+ {"$t6", RTYPE_GP | 14}, \
+ {"$t7", RTYPE_GP | 15}, \
+ {"$ta0", RTYPE_GP | 12}, /* alias for $t4 */ \
+ {"$ta1", RTYPE_GP | 13}, /* alias for $t5 */ \
+ {"$ta2", RTYPE_GP | 14}, /* alias for $t6 */ \
+ {"$ta3", RTYPE_GP | 15} /* alias for $t7 */
+
+/* Remaining symbolic register names */
+#define SYMBOLIC_REGISTER_NAMES \
+ {"$zero", RTYPE_GP | 0}, \
+ {"$at", RTYPE_GP | 1}, \
+ {"$AT", RTYPE_GP | 1}, \
+ {"$v0", RTYPE_GP | 2}, \
+ {"$v1", RTYPE_GP | 3}, \
+ {"$a0", RTYPE_GP | 4}, \
+ {"$a1", RTYPE_GP | 5}, \
+ {"$a2", RTYPE_GP | 6}, \
+ {"$a3", RTYPE_GP | 7}, \
+ {"$s0", RTYPE_GP | 16}, \
+ {"$s1", RTYPE_GP | 17}, \
+ {"$s2", RTYPE_GP | 18}, \
+ {"$s3", RTYPE_GP | 19}, \
+ {"$s4", RTYPE_GP | 20}, \
+ {"$s5", RTYPE_GP | 21}, \
+ {"$s6", RTYPE_GP | 22}, \
+ {"$s7", RTYPE_GP | 23}, \
+ {"$t8", RTYPE_GP | 24}, \
+ {"$t9", RTYPE_GP | 25}, \
+ {"$k0", RTYPE_GP | 26}, \
+ {"$kt0", RTYPE_GP | 26}, \
+ {"$k1", RTYPE_GP | 27}, \
+ {"$kt1", RTYPE_GP | 27}, \
+ {"$gp", RTYPE_GP | 28}, \
+ {"$sp", RTYPE_GP | 29}, \
+ {"$s8", RTYPE_GP | 30}, \
+ {"$fp", RTYPE_GP | 30}, \
+ {"$ra", RTYPE_GP | 31}
+
+#define MIPS16_SPECIAL_REGISTER_NAMES \
+ {"$pc", RTYPE_PC | 0}
+
+#define MDMX_VECTOR_REGISTER_NAMES \
+ /* {"$v0", RTYPE_VEC | 0}, clash with REG 2 above */ \
+ /* {"$v1", RTYPE_VEC | 1}, clash with REG 3 above */ \
+ {"$v2", RTYPE_VEC | 2}, \
+ {"$v3", RTYPE_VEC | 3}, \
+ {"$v4", RTYPE_VEC | 4}, \
+ {"$v5", RTYPE_VEC | 5}, \
+ {"$v6", RTYPE_VEC | 6}, \
+ {"$v7", RTYPE_VEC | 7}, \
+ {"$v8", RTYPE_VEC | 8}, \
+ {"$v9", RTYPE_VEC | 9}, \
+ {"$v10", RTYPE_VEC | 10}, \
+ {"$v11", RTYPE_VEC | 11}, \
+ {"$v12", RTYPE_VEC | 12}, \
+ {"$v13", RTYPE_VEC | 13}, \
+ {"$v14", RTYPE_VEC | 14}, \
+ {"$v15", RTYPE_VEC | 15}, \
+ {"$v16", RTYPE_VEC | 16}, \
+ {"$v17", RTYPE_VEC | 17}, \
+ {"$v18", RTYPE_VEC | 18}, \
+ {"$v19", RTYPE_VEC | 19}, \
+ {"$v20", RTYPE_VEC | 20}, \
+ {"$v21", RTYPE_VEC | 21}, \
+ {"$v22", RTYPE_VEC | 22}, \
+ {"$v23", RTYPE_VEC | 23}, \
+ {"$v24", RTYPE_VEC | 24}, \
+ {"$v25", RTYPE_VEC | 25}, \
+ {"$v26", RTYPE_VEC | 26}, \
+ {"$v27", RTYPE_VEC | 27}, \
+ {"$v28", RTYPE_VEC | 28}, \
+ {"$v29", RTYPE_VEC | 29}, \
+ {"$v30", RTYPE_VEC | 30}, \
+ {"$v31", RTYPE_VEC | 31}
+
+#define R5900_I_NAMES \
+ {"$I", RTYPE_R5900_I | 0}
+
+#define R5900_Q_NAMES \
+ {"$Q", RTYPE_R5900_Q | 0}
+
+#define R5900_R_NAMES \
+ {"$R", RTYPE_R5900_R | 0}
+
+#define R5900_ACC_NAMES \
+ {"$ACC", RTYPE_R5900_ACC | 0 }
+
+#define MIPS_DSP_ACCUMULATOR_NAMES \
+ {"$ac0", RTYPE_ACC | 0}, \
+ {"$ac1", RTYPE_ACC | 1}, \
+ {"$ac2", RTYPE_ACC | 2}, \
+ {"$ac3", RTYPE_ACC | 3}
+
+static const struct regname reg_names[] = {
+ GENERIC_REGISTER_NUMBERS,
+ FPU_REGISTER_NAMES,
+ FPU_CONDITION_CODE_NAMES,
+ COPROC_CONDITION_CODE_NAMES,
+
+ /* The $txx registers depends on the abi,
+ these will be added later into the symbol table from
+ one of the tables below once mips_abi is set after
+ parsing of arguments from the command line. */
+ SYMBOLIC_REGISTER_NAMES,
+
+ MIPS16_SPECIAL_REGISTER_NAMES,
+ MDMX_VECTOR_REGISTER_NAMES,
+ R5900_I_NAMES,
+ R5900_Q_NAMES,
+ R5900_R_NAMES,
+ R5900_ACC_NAMES,
+ MIPS_DSP_ACCUMULATOR_NAMES,
+ {0, 0}
+};
+
+static const struct regname reg_names_o32[] = {
+ O32_SYMBOLIC_REGISTER_NAMES,
+ {0, 0}
+};
+
+static const struct regname reg_names_n32n64[] = {
+ N32N64_SYMBOLIC_REGISTER_NAMES,
+ {0, 0}
+};
+
+/* Register symbols $v0 and $v1 map to GPRs 2 and 3, but they can also be
+ interpreted as vector registers 0 and 1. If SYMVAL is the value of one
+ of these register symbols, return the associated vector register,
+ otherwise return SYMVAL itself. */
+
+static unsigned int
+mips_prefer_vec_regno (unsigned int symval)
+{
+ if ((symval & -2) == (RTYPE_GP | 2))
+ return RTYPE_VEC | (symval & 1);
+ return symval;
+}
+
+/* Return true if string [S, E) is a valid register name, storing its
+ symbol value in *SYMVAL_PTR if so. */
+
+static bfd_boolean
+mips_parse_register_1 (char *s, char *e, unsigned int *symval_ptr)
+{
+ char save_c;
+ symbolS *symbol;
+
+ /* Terminate name. */
+ save_c = *e;
+ *e = '\0';
+
+ /* Look up the name. */
+ symbol = symbol_find (s);
+ *e = save_c;
+
+ if (!symbol || S_GET_SEGMENT (symbol) != reg_section)
+ return FALSE;
+
+ *symval_ptr = S_GET_VALUE (symbol);
+ return TRUE;
+}
+
+/* Return true if the string at *SPTR is a valid register name. Allow it
+ to have a VU0-style channel suffix of the form x?y?z?w? if CHANNELS_PTR
+ is nonnull.
+
+ When returning true, move *SPTR past the register, store the
+ register's symbol value in *SYMVAL_PTR and the channel mask in
+ *CHANNELS_PTR (if nonnull). The symbol value includes the register
+ number (RNUM_MASK) and register type (RTYPE_MASK). The channel mask
+ is a 4-bit value of the form XYZW and is 0 if no suffix was given. */
+
+static bfd_boolean
+mips_parse_register (char **sptr, unsigned int *symval_ptr,
+ unsigned int *channels_ptr)
+{
+ char *s, *e, *m;
+ const char *q;
+ unsigned int channels, symval, bit;
+
+ /* Find end of name. */
+ s = e = *sptr;
+ if (is_name_beginner (*e))
+ ++e;
+ while (is_part_of_name (*e))
+ ++e;
+
+ channels = 0;
+ if (!mips_parse_register_1 (s, e, &symval))
+ {
+ if (!channels_ptr)
+ return FALSE;
+
+ /* Eat characters from the end of the string that are valid
+ channel suffixes. The preceding register must be $ACC or
+ end with a digit, so there is no ambiguity. */
+ bit = 1;
+ m = e;
+ for (q = "wzyx"; *q; q++, bit <<= 1)
+ if (m > s && m[-1] == *q)
+ {
+ --m;
+ channels |= bit;
+ }
+
+ if (channels == 0
+ || !mips_parse_register_1 (s, m, &symval)
+ || (symval & (RTYPE_VI | RTYPE_VF | RTYPE_R5900_ACC)) == 0)
+ return FALSE;
+ }
+
+ *sptr = e;
+ *symval_ptr = symval;
+ if (channels_ptr)
+ *channels_ptr = channels;
+ return TRUE;
+}
+
+/* Check if SPTR points at a valid register specifier according to TYPES.
+ If so, then return 1, advance S to consume the specifier and store
+ the register's number in REGNOP, otherwise return 0. */
+
+static int
+reg_lookup (char **s, unsigned int types, unsigned int *regnop)
+{
+ unsigned int regno;
+
+ if (mips_parse_register (s, &regno, NULL))
+ {
+ if (types & RTYPE_VEC)
+ regno = mips_prefer_vec_regno (regno);
+ if (regno & types)
+ regno &= RNUM_MASK;
+ else
+ regno = ~0;
+ }
+ else
+ {
+ if (types & RWARN)
+ as_warn (_("unrecognized register name `%s'"), *s);
+ regno = ~0;
+ }
+ if (regnop)
+ *regnop = regno;
+ return regno <= RNUM_MASK;
+}
+
+/* Parse a VU0 "x?y?z?w?" channel mask at S and store the associated
+ mask in *CHANNELS. Return a pointer to the first unconsumed character. */
+
+static char *
+mips_parse_vu0_channels (char *s, unsigned int *channels)
+{
+ unsigned int i;
+
+ *channels = 0;
+ for (i = 0; i < 4; i++)
+ if (*s == "xyzw"[i])
+ {
+ *channels |= 1 << (3 - i);
+ ++s;
+ }
+ return s;
+}
+
+/* Token types for parsed operand lists. */
+enum mips_operand_token_type {
+ /* A plain register, e.g. $f2. */
+ OT_REG,
+
+ /* A 4-bit XYZW channel mask. */
+ OT_CHANNELS,
+
+ /* A constant vector index, e.g. [1]. */
+ OT_INTEGER_INDEX,
+
+ /* A register vector index, e.g. [$2]. */
+ OT_REG_INDEX,
+
+ /* A continuous range of registers, e.g. $s0-$s4. */
+ OT_REG_RANGE,
+
+ /* A (possibly relocated) expression. */
+ OT_INTEGER,
+
+ /* A floating-point value. */
+ OT_FLOAT,
+
+ /* A single character. This can be '(', ')' or ',', but '(' only appears
+ before OT_REGs. */
+ OT_CHAR,
+
+ /* A doubled character, either "--" or "++". */
+ OT_DOUBLE_CHAR,
+
+ /* The end of the operand list. */
+ OT_END
+};
+
+/* A parsed operand token. */
+struct mips_operand_token
+{
+ /* The type of token. */
+ enum mips_operand_token_type type;
+ union
+ {
+ /* The register symbol value for an OT_REG or OT_REG_INDEX. */
+ unsigned int regno;
+
+ /* The 4-bit channel mask for an OT_CHANNEL_SUFFIX. */
+ unsigned int channels;
+
+ /* The integer value of an OT_INTEGER_INDEX. */
+ addressT index;
+
+ /* The two register symbol values involved in an OT_REG_RANGE. */
+ struct {
+ unsigned int regno1;
+ unsigned int regno2;
+ } reg_range;
+
+ /* The value of an OT_INTEGER. The value is represented as an
+ expression and the relocation operators that were applied to
+ that expression. The reloc entries are BFD_RELOC_UNUSED if no
+ relocation operators were used. */
+ struct {
+ expressionS value;
+ bfd_reloc_code_real_type relocs[3];
+ } integer;
+
+ /* The binary data for an OT_FLOAT constant, and the number of bytes
+ in the constant. */
+ struct {
+ unsigned char data[8];
+ int length;
+ } flt;
+
+ /* The character represented by an OT_CHAR or OT_DOUBLE_CHAR. */
+ char ch;
+ } u;
+};
+
+/* An obstack used to construct lists of mips_operand_tokens. */
+static struct obstack mips_operand_tokens;
+
+/* Give TOKEN type TYPE and add it to mips_operand_tokens. */
+
+static void
+mips_add_token (struct mips_operand_token *token,
+ enum mips_operand_token_type type)
+{
+ token->type = type;
+ obstack_grow (&mips_operand_tokens, token, sizeof (*token));
+}
+
+/* Check whether S is '(' followed by a register name. Add OT_CHAR
+ and OT_REG tokens for them if so, and return a pointer to the first
+ unconsumed character. Return null otherwise. */
+
+static char *
+mips_parse_base_start (char *s)
+{
+ struct mips_operand_token token;
+ unsigned int regno, channels;
+ bfd_boolean decrement_p;
+
+ if (*s != '(')
+ return 0;
+
+ ++s;
+ SKIP_SPACE_TABS (s);
+
+ /* Only match "--" as part of a base expression. In other contexts "--X"
+ is a double negative. */
+ decrement_p = (s[0] == '-' && s[1] == '-');
+ if (decrement_p)
+ {
+ s += 2;
+ SKIP_SPACE_TABS (s);
+ }
+
+ /* Allow a channel specifier because that leads to better error messages
+ than treating something like "$vf0x++" as an expression. */
+ if (!mips_parse_register (&s, &regno, &channels))
+ return 0;
+
+ token.u.ch = '(';
+ mips_add_token (&token, OT_CHAR);
+
+ if (decrement_p)
+ {
+ token.u.ch = '-';
+ mips_add_token (&token, OT_DOUBLE_CHAR);
+ }
+
+ token.u.regno = regno;
+ mips_add_token (&token, OT_REG);
+
+ if (channels)
+ {
+ token.u.channels = channels;
+ mips_add_token (&token, OT_CHANNELS);
+ }
+
+ /* For consistency, only match "++" as part of base expressions too. */
+ SKIP_SPACE_TABS (s);
+ if (s[0] == '+' && s[1] == '+')
+ {
+ s += 2;
+ token.u.ch = '+';
+ mips_add_token (&token, OT_DOUBLE_CHAR);
+ }
+
+ return s;
+}
+
+/* Parse one or more tokens from S. Return a pointer to the first
+ unconsumed character on success. Return null if an error was found
+ and store the error text in insn_error. FLOAT_FORMAT is as for
+ mips_parse_arguments. */
+
+static char *
+mips_parse_argument_token (char *s, char float_format)
+{
+ char *end, *save_in, *err;
+ unsigned int regno1, regno2, channels;
+ struct mips_operand_token token;
+
+ /* First look for "($reg", since we want to treat that as an
+ OT_CHAR and OT_REG rather than an expression. */
+ end = mips_parse_base_start (s);
+ if (end)
+ return end;
+
+ /* Handle other characters that end up as OT_CHARs. */
+ if (*s == ')' || *s == ',')
+ {
+ token.u.ch = *s;
+ mips_add_token (&token, OT_CHAR);
+ ++s;
+ return s;
+ }
+
+ /* Handle tokens that start with a register. */
+ if (mips_parse_register (&s, &regno1, &channels))
+ {
+ if (channels)
+ {
+ /* A register and a VU0 channel suffix. */
+ token.u.regno = regno1;
+ mips_add_token (&token, OT_REG);
+
+ token.u.channels = channels;
+ mips_add_token (&token, OT_CHANNELS);
+ return s;
+ }
+
+ SKIP_SPACE_TABS (s);
+ if (*s == '-')
+ {
+ /* A register range. */
+ ++s;
+ SKIP_SPACE_TABS (s);
+ if (!mips_parse_register (&s, &regno2, NULL))
+ {
+ set_insn_error (0, _("invalid register range"));
+ return 0;
+ }
+
+ token.u.reg_range.regno1 = regno1;
+ token.u.reg_range.regno2 = regno2;
+ mips_add_token (&token, OT_REG_RANGE);
+ return s;
+ }
+
+ /* Add the register itself. */
+ token.u.regno = regno1;
+ mips_add_token (&token, OT_REG);
+
+ /* Check for a vector index. */
+ if (*s == '[')
+ {
+ ++s;
+ SKIP_SPACE_TABS (s);
+ if (mips_parse_register (&s, &token.u.regno, NULL))
+ mips_add_token (&token, OT_REG_INDEX);
+ else
+ {
+ expressionS element;
+
+ my_getExpression (&element, s);
+ if (element.X_op != O_constant)
+ {
+ set_insn_error (0, _("vector element must be constant"));
+ return 0;
+ }
+ s = expr_end;
+ token.u.index = element.X_add_number;
+ mips_add_token (&token, OT_INTEGER_INDEX);
+ }
+ SKIP_SPACE_TABS (s);
+ if (*s != ']')
+ {
+ set_insn_error (0, _("missing `]'"));
+ return 0;
+ }
+ ++s;
+ }
+ return s;
+ }
+
+ if (float_format)
+ {
+ /* First try to treat expressions as floats. */
+ save_in = input_line_pointer;
+ input_line_pointer = s;
+ err = md_atof (float_format, (char *) token.u.flt.data,
+ &token.u.flt.length);
+ end = input_line_pointer;
+ input_line_pointer = save_in;
+ if (err && *err)
+ {
+ set_insn_error (0, err);
+ return 0;
+ }
+ if (s != end)
+ {
+ mips_add_token (&token, OT_FLOAT);
+ return end;
+ }
+ }
+
+ /* Treat everything else as an integer expression. */
+ token.u.integer.relocs[0] = BFD_RELOC_UNUSED;
+ token.u.integer.relocs[1] = BFD_RELOC_UNUSED;
+ token.u.integer.relocs[2] = BFD_RELOC_UNUSED;
+ my_getSmallExpression (&token.u.integer.value, token.u.integer.relocs, s);
+ s = expr_end;
+ mips_add_token (&token, OT_INTEGER);
+ return s;
+}
+
+/* S points to the operand list for an instruction. FLOAT_FORMAT is 'f'
+ if expressions should be treated as 32-bit floating-point constants,
+ 'd' if they should be treated as 64-bit floating-point constants,
+ or 0 if they should be treated as integer expressions (the usual case).
+
+ Return a list of tokens on success, otherwise return 0. The caller
+ must obstack_free the list after use. */
+
+static struct mips_operand_token *
+mips_parse_arguments (char *s, char float_format)
+{
+ struct mips_operand_token token;
+
+ SKIP_SPACE_TABS (s);
+ while (*s)
+ {
+ s = mips_parse_argument_token (s, float_format);
+ if (!s)
+ {
+ obstack_free (&mips_operand_tokens,
+ obstack_finish (&mips_operand_tokens));
+ return 0;
+ }
+ SKIP_SPACE_TABS (s);
+ }
+ mips_add_token (&token, OT_END);
+ return (struct mips_operand_token *) obstack_finish (&mips_operand_tokens);
+}
+
+/* Return TRUE if opcode MO is valid on the currently selected ISA, ASE
+ and architecture. Use is_opcode_valid_16 for MIPS16 opcodes. */
+
+static bfd_boolean
+is_opcode_valid (const struct mips_opcode *mo)
+{
+ int isa = mips_opts.isa;
+ int ase = mips_opts.ase;
+ int fp_s, fp_d;
+ unsigned int i;
+
+ if (ISA_HAS_64BIT_REGS (mips_opts.isa))
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ if ((ase & mips_ases[i].flags) == mips_ases[i].flags)
+ ase |= mips_ases[i].flags64;
+
+ if (!opcode_is_member (mo, isa, ase, mips_opts.arch))
+ return FALSE;
+
+ /* Check whether the instruction or macro requires single-precision or
+ double-precision floating-point support. Note that this information is
+ stored differently in the opcode table for insns and macros. */
+ if (mo->pinfo == INSN_MACRO)
+ {
+ fp_s = mo->pinfo2 & INSN2_M_FP_S;
+ fp_d = mo->pinfo2 & INSN2_M_FP_D;
+ }
+ else
+ {
+ fp_s = mo->pinfo & FP_S;
+ fp_d = mo->pinfo & FP_D;
+ }
+
+ if (fp_d && (mips_opts.soft_float || mips_opts.single_float))
+ return FALSE;
+
+ if (fp_s && mips_opts.soft_float)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Return TRUE if the MIPS16 opcode MO is valid on the currently
+ selected ISA and architecture. */
+
+static bfd_boolean
+is_opcode_valid_16 (const struct mips_opcode *mo)
+{
+ return opcode_is_member (mo, mips_opts.isa, 0, mips_opts.arch);
+}
+
+/* Return TRUE if the size of the microMIPS opcode MO matches one
+ explicitly requested. Always TRUE in the standard MIPS mode. */
+
+static bfd_boolean
+is_size_valid (const struct mips_opcode *mo)
+{
+ if (!mips_opts.micromips)
+ return TRUE;
+
+ if (mips_opts.insn32)
+ {
+ if (mo->pinfo != INSN_MACRO && micromips_insn_length (mo) != 4)
+ return FALSE;
+ if ((mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0)
+ return FALSE;
+ }
+ if (!forced_insn_length)
+ return TRUE;
+ if (mo->pinfo == INSN_MACRO)
+ return FALSE;
+ return forced_insn_length == micromips_insn_length (mo);
+}
+
+/* Return TRUE if the microMIPS opcode MO is valid for the delay slot
+ of the preceding instruction. Always TRUE in the standard MIPS mode.
+
+ We don't accept macros in 16-bit delay slots to avoid a case where
+ a macro expansion fails because it relies on a preceding 32-bit real
+ instruction to have matched and does not handle the operands correctly.
+ The only macros that may expand to 16-bit instructions are JAL that
+ cannot be placed in a delay slot anyway, and corner cases of BALIGN
+ and BGT (that likewise cannot be placed in a delay slot) that decay to
+ a NOP. In all these cases the macros precede any corresponding real
+ instruction definitions in the opcode table, so they will match in the
+ second pass where the size of the delay slot is ignored and therefore
+ produce correct code. */
+
+static bfd_boolean
+is_delay_slot_valid (const struct mips_opcode *mo)
+{
+ if (!mips_opts.micromips)
+ return TRUE;
+
+ if (mo->pinfo == INSN_MACRO)
+ return (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) == 0;
+ if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
+ && micromips_insn_length (mo) != 4)
+ return FALSE;
+ if ((history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
+ && micromips_insn_length (mo) != 2)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* For consistency checking, verify that all bits of OPCODE are specified
+ either by the match/mask part of the instruction definition, or by the
+ operand list. Also build up a list of operands in OPERANDS.
+
+ INSN_BITS says which bits of the instruction are significant.
+ If OPCODE is a standard or microMIPS instruction, DECODE_OPERAND
+ provides the mips_operand description of each operand. DECODE_OPERAND
+ is null for MIPS16 instructions. */
+
+static int
+validate_mips_insn (const struct mips_opcode *opcode,
+ unsigned long insn_bits,
+ const struct mips_operand *(*decode_operand) (const char *),
+ struct mips_operand_array *operands)
+{
+ const char *s;
+ unsigned long used_bits, doubled, undefined, opno, mask;
+ const struct mips_operand *operand;
+
+ mask = (opcode->pinfo == INSN_MACRO ? 0 : opcode->mask);
+ if ((mask & opcode->match) != opcode->match)
+ {
+ as_bad (_("internal: bad mips opcode (mask error): %s %s"),
+ opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits = 0;
+ opno = 0;
+ if (opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX)
+ used_bits = mips_insert_operand (&mips_vu0_channel_mask, used_bits, -1);
+ for (s = opcode->args; *s; ++s)
+ switch (*s)
+ {
+ case ',':
+ case '(':
+ case ')':
+ break;
+
+ case '#':
+ s++;
+ break;
+
+ default:
+ if (!decode_operand)
+ operand = decode_mips16_operand (*s, FALSE);
+ else
+ operand = decode_operand (s);
+ if (!operand && opcode->pinfo != INSN_MACRO)
+ {
+ as_bad (_("internal: unknown operand type: %s %s"),
+ opcode->name, opcode->args);
+ return 0;
+ }
+ gas_assert (opno < MAX_OPERANDS);
+ operands->operand[opno] = operand;
+ if (operand && operand->type != OP_VU0_MATCH_SUFFIX)
+ {
+ used_bits = mips_insert_operand (operand, used_bits, -1);
+ if (operand->type == OP_MDMX_IMM_REG)
+ /* Bit 5 is the format selector (OB vs QH). The opcode table
+ has separate entries for each format. */
+ used_bits &= ~(1 << (operand->lsb + 5));
+ if (operand->type == OP_ENTRY_EXIT_LIST)
+ used_bits &= ~(mask & 0x700);
+ }
+ /* Skip prefix characters. */
+ if (decode_operand && (*s == '+' || *s == 'm' || *s == '-'))
+ ++s;
+ opno += 1;
+ break;
+ }
+ doubled = used_bits & mask & insn_bits;
+ if (doubled)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx doubly defined):"
+ " %s %s"), doubled, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits |= mask;
+ undefined = ~used_bits & insn_bits;
+ if (opcode->pinfo != INSN_MACRO && undefined)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx undefined): %s %s"),
+ undefined, opcode->name, opcode->args);
+ return 0;
+ }
+ used_bits &= ~insn_bits;
+ if (used_bits)
+ {
+ as_bad (_("internal: bad mips opcode (bits 0x%08lx defined): %s %s"),
+ used_bits, opcode->name, opcode->args);
+ return 0;
+ }
+ return 1;
+}
+
+/* The MIPS16 version of validate_mips_insn. */
+
+static int
+validate_mips16_insn (const struct mips_opcode *opcode,
+ struct mips_operand_array *operands)
+{
+ if (opcode->args[0] == 'a' || opcode->args[0] == 'i')
+ {
+ /* In this case OPCODE defines the first 16 bits in a 32-bit jump
+ instruction. Use TMP to describe the full instruction. */
+ struct mips_opcode tmp;
+
+ tmp = *opcode;
+ tmp.match <<= 16;
+ tmp.mask <<= 16;
+ return validate_mips_insn (&tmp, 0xffffffff, 0, operands);
+ }
+ return validate_mips_insn (opcode, 0xffff, 0, operands);
+}
+
+/* The microMIPS version of validate_mips_insn. */
+
+static int
+validate_micromips_insn (const struct mips_opcode *opc,
+ struct mips_operand_array *operands)
+{
+ unsigned long insn_bits;
+ unsigned long major;
+ unsigned int length;
+
+ if (opc->pinfo == INSN_MACRO)
+ return validate_mips_insn (opc, 0xffffffff, decode_micromips_operand,
+ operands);
+
+ length = micromips_insn_length (opc);
+ if (length != 2 && length != 4)
+ {
+ as_bad (_("internal error: bad microMIPS opcode (incorrect length: %u): "
+ "%s %s"), length, opc->name, opc->args);
+ return 0;
+ }
+ major = opc->match >> (10 + 8 * (length - 2));
+ if ((length == 2 && (major & 7) != 1 && (major & 6) != 2)
+ || (length == 4 && (major & 7) != 0 && (major & 4) != 4))
+ {
+ as_bad (_("internal error: bad microMIPS opcode "
+ "(opcode/length mismatch): %s %s"), opc->name, opc->args);
+ return 0;
+ }
+
+ /* Shift piecewise to avoid an overflow where unsigned long is 32-bit. */
+ insn_bits = 1 << 4 * length;
+ insn_bits <<= 4 * length;
+ insn_bits -= 1;
+ return validate_mips_insn (opc, insn_bits, decode_micromips_operand,
+ operands);
+}
+
+/* This function is called once, at assembler startup time. It should set up
+ all the tables, etc. that the MD part of the assembler will need. */
+
+void
+md_begin (void)
+{
+ const char *retval = NULL;
+ int i = 0;
+ int broken = 0;
+
+ if (mips_pic != NO_PIC)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_bad (_("-G may not be used in position-independent code"));
+ g_switch_value = 0;
+ }
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+ as_warn (_("could not set architecture and machine"));
+
+ op_hash = hash_new ();
+
+ mips_operands = XCNEWVEC (struct mips_operand_array, NUMOPCODES);
+ for (i = 0; i < NUMOPCODES;)
+ {
+ const char *name = mips_opcodes[i].name;
+
+ retval = hash_insert (op_hash, name, (void *) &mips_opcodes[i]);
+ if (retval != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ mips_opcodes[i].name, retval);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("broken assembler, no assembly attempted"));
+ }
+ do
+ {
+ if (!validate_mips_insn (&mips_opcodes[i], 0xffffffff,
+ decode_mips_operand, &mips_operands[i]))
+ broken = 1;
+ if (nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
+ {
+ create_insn (&nop_insn, mips_opcodes + i);
+ if (mips_fix_loongson2f_nop)
+ nop_insn.insn_opcode = LOONGSON2F_NOP_INSN;
+ nop_insn.fixed_p = 1;
+ }
+ ++i;
+ }
+ while ((i < NUMOPCODES) && !strcmp (mips_opcodes[i].name, name));
+ }
+
+ mips16_op_hash = hash_new ();
+ mips16_operands = XCNEWVEC (struct mips_operand_array,
+ bfd_mips16_num_opcodes);
+
+ i = 0;
+ while (i < bfd_mips16_num_opcodes)
+ {
+ const char *name = mips16_opcodes[i].name;
+
+ retval = hash_insert (mips16_op_hash, name, (void *) &mips16_opcodes[i]);
+ if (retval != NULL)
+ as_fatal (_("internal: can't hash `%s': %s"),
+ mips16_opcodes[i].name, retval);
+ do
+ {
+ if (!validate_mips16_insn (&mips16_opcodes[i], &mips16_operands[i]))
+ broken = 1;
+ if (mips16_nop_insn.insn_mo == NULL && strcmp (name, "nop") == 0)
+ {
+ create_insn (&mips16_nop_insn, mips16_opcodes + i);
+ mips16_nop_insn.fixed_p = 1;
+ }
+ ++i;
+ }
+ while (i < bfd_mips16_num_opcodes
+ && strcmp (mips16_opcodes[i].name, name) == 0);
+ }
+
+ micromips_op_hash = hash_new ();
+ micromips_operands = XCNEWVEC (struct mips_operand_array,
+ bfd_micromips_num_opcodes);
+
+ i = 0;
+ while (i < bfd_micromips_num_opcodes)
+ {
+ const char *name = micromips_opcodes[i].name;
+
+ retval = hash_insert (micromips_op_hash, name,
+ (void *) &micromips_opcodes[i]);
+ if (retval != NULL)
+ as_fatal (_("internal: can't hash `%s': %s"),
+ micromips_opcodes[i].name, retval);
+ do
+ {
+ struct mips_cl_insn *micromips_nop_insn;
+
+ if (!validate_micromips_insn (&micromips_opcodes[i],
+ &micromips_operands[i]))
+ broken = 1;
+
+ if (micromips_opcodes[i].pinfo != INSN_MACRO)
+ {
+ if (micromips_insn_length (micromips_opcodes + i) == 2)
+ micromips_nop_insn = &micromips_nop16_insn;
+ else if (micromips_insn_length (micromips_opcodes + i) == 4)
+ micromips_nop_insn = &micromips_nop32_insn;
+ else
+ continue;
+
+ if (micromips_nop_insn->insn_mo == NULL
+ && strcmp (name, "nop") == 0)
+ {
+ create_insn (micromips_nop_insn, micromips_opcodes + i);
+ micromips_nop_insn->fixed_p = 1;
+ }
+ }
+ }
+ while (++i < bfd_micromips_num_opcodes
+ && strcmp (micromips_opcodes[i].name, name) == 0);
+ }
+
+ if (broken)
+ as_fatal (_("broken assembler, no assembly attempted"));
+
+ /* We add all the general register names to the symbol table. This
+ helps us detect invalid uses of them. */
+ for (i = 0; reg_names[i].name; i++)
+ symbol_table_insert (symbol_new (reg_names[i].name, reg_section,
+ reg_names[i].num, /* & RNUM_MASK, */
+ &zero_address_frag));
+ if (HAVE_NEWABI)
+ for (i = 0; reg_names_n32n64[i].name; i++)
+ symbol_table_insert (symbol_new (reg_names_n32n64[i].name, reg_section,
+ reg_names_n32n64[i].num, /* & RNUM_MASK, */
+ &zero_address_frag));
+ else
+ for (i = 0; reg_names_o32[i].name; i++)
+ symbol_table_insert (symbol_new (reg_names_o32[i].name, reg_section,
+ reg_names_o32[i].num, /* & RNUM_MASK, */
+ &zero_address_frag));
+
+ for (i = 0; i < 32; i++)
+ {
+ char regname[7];
+
+ /* R5900 VU0 floating-point register. */
+ regname[sizeof (rename) - 1] = 0;
+ snprintf (regname, sizeof (regname) - 1, "$vf%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_VF | i, &zero_address_frag));
+
+ /* R5900 VU0 integer register. */
+ snprintf (regname, sizeof (regname) - 1, "$vi%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_VI | i, &zero_address_frag));
+
+ /* MSA register. */
+ snprintf (regname, sizeof (regname) - 1, "$w%d", i);
+ symbol_table_insert (symbol_new (regname, reg_section,
+ RTYPE_MSA | i, &zero_address_frag));
+ }
+
+ obstack_init (&mips_operand_tokens);
+
+ mips_no_prev_insn ();
+
+ mips_gprmask = 0;
+ mips_cprmask[0] = 0;
+ mips_cprmask[1] = 0;
+ mips_cprmask[2] = 0;
+ mips_cprmask[3] = 0;
+
+ /* set the default alignment for the text section (2**2) */
+ record_alignment (text_section, 2);
+
+ bfd_set_gp_size (stdoutput, g_switch_value);
+
+ /* On a native system other than VxWorks, sections must be aligned
+ to 16 byte boundaries. When configured for an embedded ELF
+ target, we don't bother. */
+ if (strncmp (TARGET_OS, "elf", 3) != 0
+ && strncmp (TARGET_OS, "vxworks", 7) != 0)
+ {
+ (void) bfd_set_section_alignment (stdoutput, text_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, data_section, 4);
+ (void) bfd_set_section_alignment (stdoutput, bss_section, 4);
+ }
+
+ /* Create a .reginfo section for register masks and a .mdebug
+ section for debugging information. */
+ {
+ segT seg;
+ subsegT subseg;
+ flagword flags;
+ segT sec;
+
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The ABI says this section should be loaded so that the
+ running program can access it. However, we don't load it
+ if we are configured for an embedded target */
+ flags = SEC_READONLY | SEC_DATA;
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ flags |= SEC_ALLOC | SEC_LOAD;
+
+ if (mips_abi != N64_ABI)
+ {
+ sec = subseg_new (".reginfo", (subsegT) 0);
+
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, HAVE_NEWABI ? 3 : 2);
+
+ mips_regmask_frag = frag_more (sizeof (Elf32_External_RegInfo));
+ }
+ else
+ {
+ /* The 64-bit ABI uses a .MIPS.options section rather than
+ .reginfo section. */
+ sec = subseg_new (".MIPS.options", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec, flags);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+
+ /* Set up the option header. */
+ {
+ Elf_Internal_Options opthdr;
+ char *f;
+
+ opthdr.kind = ODK_REGINFO;
+ opthdr.size = (sizeof (Elf_External_Options)
+ + sizeof (Elf64_External_RegInfo));
+ opthdr.section = 0;
+ opthdr.info = 0;
+ f = frag_more (sizeof (Elf_External_Options));
+ bfd_mips_elf_swap_options_out (stdoutput, &opthdr,
+ (Elf_External_Options *) f);
+
+ mips_regmask_frag = frag_more (sizeof (Elf64_External_RegInfo));
+ }
+ }
+
+ sec = subseg_new (".MIPS.abiflags", (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec,
+ SEC_READONLY | SEC_DATA | SEC_ALLOC | SEC_LOAD);
+ bfd_set_section_alignment (stdoutput, sec, 3);
+ mips_flags_frag = frag_more (sizeof (Elf_External_ABIFlags_v0));
+
+ if (ECOFF_DEBUGGING)
+ {
+ sec = subseg_new (".mdebug", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, sec,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+ (void) bfd_set_section_alignment (stdoutput, sec, 2);
+ }
+ else if (mips_flag_pdr)
+ {
+ pdr_seg = subseg_new (".pdr", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, pdr_seg,
+ SEC_READONLY | SEC_RELOC
+ | SEC_DEBUGGING);
+ (void) bfd_set_section_alignment (stdoutput, pdr_seg, 2);
+ }
+
+ subseg_set (seg, subseg);
+ }
+
+ if (mips_fix_vr4120)
+ init_vr4120_conflicts ();
+}
+
+static inline void
+fpabi_incompatible_with (int fpabi, const char *what)
+{
+ as_warn (_(".gnu_attribute %d,%d is incompatible with `%s'"),
+ Tag_GNU_MIPS_ABI_FP, fpabi, what);
+}
+
+static inline void
+fpabi_requires (int fpabi, const char *what)
+{
+ as_warn (_(".gnu_attribute %d,%d requires `%s'"),
+ Tag_GNU_MIPS_ABI_FP, fpabi, what);
+}
+
+/* Check -mabi and register sizes against the specified FP ABI. */
+static void
+check_fpabi (int fpabi)
+{
+ switch (fpabi)
+ {
+ case Val_GNU_MIPS_ABI_FP_DOUBLE:
+ if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (file_mips_opts.single_float)
+ fpabi_incompatible_with (fpabi, "singlefloat");
+ if (file_mips_opts.gp == 64 && file_mips_opts.fp == 32)
+ fpabi_incompatible_with (fpabi, "gp=64 fp=32");
+ else if (file_mips_opts.gp == 32 && file_mips_opts.fp == 64)
+ fpabi_incompatible_with (fpabi, "gp=32 fp=64");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_XX:
+ if (mips_abi != O32_ABI)
+ fpabi_requires (fpabi, "-mabi=32");
+ else if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (file_mips_opts.single_float)
+ fpabi_incompatible_with (fpabi, "singlefloat");
+ else if (file_mips_opts.fp != 0)
+ fpabi_requires (fpabi, "fp=xx");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_64A:
+ case Val_GNU_MIPS_ABI_FP_64:
+ if (mips_abi != O32_ABI)
+ fpabi_requires (fpabi, "-mabi=32");
+ else if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (file_mips_opts.single_float)
+ fpabi_incompatible_with (fpabi, "singlefloat");
+ else if (file_mips_opts.fp != 64)
+ fpabi_requires (fpabi, "fp=64");
+ else if (fpabi == Val_GNU_MIPS_ABI_FP_64 && !file_mips_opts.oddspreg)
+ fpabi_incompatible_with (fpabi, "nooddspreg");
+ else if (fpabi == Val_GNU_MIPS_ABI_FP_64A && file_mips_opts.oddspreg)
+ fpabi_requires (fpabi, "nooddspreg");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_SINGLE:
+ if (file_mips_opts.soft_float)
+ fpabi_incompatible_with (fpabi, "softfloat");
+ else if (!file_mips_opts.single_float)
+ fpabi_requires (fpabi, "singlefloat");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_SOFT:
+ if (!file_mips_opts.soft_float)
+ fpabi_requires (fpabi, "softfloat");
+ break;
+
+ case Val_GNU_MIPS_ABI_FP_OLD_64:
+ as_warn (_(".gnu_attribute %d,%d is no longer supported"),
+ Tag_GNU_MIPS_ABI_FP, fpabi);
+ break;
+
+ default:
+ as_warn (_(".gnu_attribute %d,%d is not a recognized"
+ " floating-point ABI"), Tag_GNU_MIPS_ABI_FP, fpabi);
+ break;
+ }
+}
+
+/* Perform consistency checks on the current options. */
+
+static void
+mips_check_options (struct mips_set_options *opts, bfd_boolean abi_checks)
+{
+ /* Check the size of integer registers agrees with the ABI and ISA. */
+ if (opts->gp == 64 && !ISA_HAS_64BIT_REGS (opts->isa))
+ as_bad (_("`gp=64' used with a 32-bit processor"));
+ else if (abi_checks
+ && opts->gp == 32 && ABI_NEEDS_64BIT_REGS (mips_abi))
+ as_bad (_("`gp=32' used with a 64-bit ABI"));
+ else if (abi_checks
+ && opts->gp == 64 && ABI_NEEDS_32BIT_REGS (mips_abi))
+ as_bad (_("`gp=64' used with a 32-bit ABI"));
+
+ /* Check the size of the float registers agrees with the ABI and ISA. */
+ switch (opts->fp)
+ {
+ case 0:
+ if (!CPU_HAS_LDC1_SDC1 (opts->arch))
+ as_bad (_("`fp=xx' used with a cpu lacking ldc1/sdc1 instructions"));
+ else if (opts->single_float == 1)
+ as_bad (_("`fp=xx' cannot be used with `singlefloat'"));
+ break;
+ case 64:
+ if (!ISA_HAS_64BIT_FPRS (opts->isa))
+ as_bad (_("`fp=64' used with a 32-bit fpu"));
+ else if (abi_checks
+ && ABI_NEEDS_32BIT_REGS (mips_abi)
+ && !ISA_HAS_MXHC1 (opts->isa))
+ as_warn (_("`fp=64' used with a 32-bit ABI"));
+ break;
+ case 32:
+ if (abi_checks
+ && ABI_NEEDS_64BIT_REGS (mips_abi))
+ as_warn (_("`fp=32' used with a 64-bit ABI"));
+ if (ISA_IS_R6 (mips_opts.isa) && opts->single_float == 0)
+ as_bad (_("`fp=32' used with a MIPS R6 cpu"));
+ break;
+ default:
+ as_bad (_("Unknown size of floating point registers"));
+ break;
+ }
+
+ if (ABI_NEEDS_64BIT_REGS (mips_abi) && !opts->oddspreg)
+ as_bad (_("`nooddspreg` cannot be used with a 64-bit ABI"));
+
+ if (opts->micromips == 1 && opts->mips16 == 1)
+ as_bad (_("`mips16' cannot be used with `micromips'"));
+ else if (ISA_IS_R6 (mips_opts.isa)
+ && (opts->micromips == 1
+ || opts->mips16 == 1))
+ as_fatal (_("`%s' can not be used with `%s'"),
+ opts->micromips ? "micromips" : "mips16",
+ mips_cpu_info_from_isa (mips_opts.isa)->name);
+
+ if (ISA_IS_R6 (opts->isa) && mips_relax_branch)
+ as_fatal (_("branch relaxation is not supported in `%s'"),
+ mips_cpu_info_from_isa (opts->isa)->name);
+}
+
+/* Perform consistency checks on the module level options exactly once.
+ This is a deferred check that happens:
+ at the first .set directive
+ or, at the first pseudo op that generates code (inc .dc.a)
+ or, at the first instruction
+ or, at the end. */
+
+static void
+file_mips_check_options (void)
+{
+ const struct mips_cpu_info *arch_info = 0;
+
+ if (file_mips_opts_checked)
+ return;
+
+ /* The following code determines the register size.
+ Similar code was added to GCC 3.3 (see override_options() in
+ config/mips/mips.c). The GAS and GCC code should be kept in sync
+ as much as possible. */
+
+ if (file_mips_opts.gp < 0)
+ {
+ /* Infer the integer register size from the ABI and processor.
+ Restrict ourselves to 32-bit registers if that's all the
+ processor has, or if the ABI cannot handle 64-bit registers. */
+ file_mips_opts.gp = (ABI_NEEDS_32BIT_REGS (mips_abi)
+ || !ISA_HAS_64BIT_REGS (file_mips_opts.isa))
+ ? 32 : 64;
+ }
+
+ if (file_mips_opts.fp < 0)
+ {
+ /* No user specified float register size.
+ ??? GAS treats single-float processors as though they had 64-bit
+ float registers (although it complains when double-precision
+ instructions are used). As things stand, saying they have 32-bit
+ registers would lead to spurious "register must be even" messages.
+ So here we assume float registers are never smaller than the
+ integer ones. */
+ if (file_mips_opts.gp == 64)
+ /* 64-bit integer registers implies 64-bit float registers. */
+ file_mips_opts.fp = 64;
+ else if ((file_mips_opts.ase & FP64_ASES)
+ && ISA_HAS_64BIT_FPRS (file_mips_opts.isa))
+ /* Handle ASEs that require 64-bit float registers, if possible. */
+ file_mips_opts.fp = 64;
+ else if (ISA_IS_R6 (mips_opts.isa))
+ /* R6 implies 64-bit float registers. */
+ file_mips_opts.fp = 64;
+ else
+ /* 32-bit float registers. */
+ file_mips_opts.fp = 32;
+ }
+
+ arch_info = mips_cpu_info_from_arch (file_mips_opts.arch);
+
+ /* Disable operations on odd-numbered floating-point registers by default
+ when using the FPXX ABI. */
+ if (file_mips_opts.oddspreg < 0)
+ {
+ if (file_mips_opts.fp == 0)
+ file_mips_opts.oddspreg = 0;
+ else
+ file_mips_opts.oddspreg = 1;
+ }
+
+ /* End of GCC-shared inference code. */
+
+ /* This flag is set when we have a 64-bit capable CPU but use only
+ 32-bit wide registers. Note that EABI does not use it. */
+ if (ISA_HAS_64BIT_REGS (file_mips_opts.isa)
+ && ((mips_abi == NO_ABI && file_mips_opts.gp == 32)
+ || mips_abi == O32_ABI))
+ mips_32bitmode = 1;
+
+ if (file_mips_opts.isa == ISA_MIPS1 && mips_trap)
+ as_bad (_("trap exception not supported at ISA 1"));
+
+ /* If the selected architecture includes support for ASEs, enable
+ generation of code for them. */
+ if (file_mips_opts.mips16 == -1)
+ file_mips_opts.mips16 = (CPU_HAS_MIPS16 (file_mips_opts.arch)) ? 1 : 0;
+ if (file_mips_opts.micromips == -1)
+ file_mips_opts.micromips = (CPU_HAS_MICROMIPS (file_mips_opts.arch))
+ ? 1 : 0;
+
+ if (mips_nan2008 == -1)
+ mips_nan2008 = (ISA_HAS_LEGACY_NAN (file_mips_opts.isa)) ? 0 : 1;
+ else if (!ISA_HAS_LEGACY_NAN (file_mips_opts.isa) && mips_nan2008 == 0)
+ as_fatal (_("`%s' does not support legacy NaN"),
+ mips_cpu_info_from_arch (file_mips_opts.arch)->name);
+
+ /* Some ASEs require 64-bit FPRs, so -mfp32 should stop those ASEs from
+ being selected implicitly. */
+ if (file_mips_opts.fp != 64)
+ file_ase_explicit |= ASE_MIPS3D | ASE_MDMX | ASE_MSA;
+
+ /* If the user didn't explicitly select or deselect a particular ASE,
+ use the default setting for the CPU. */
+ file_mips_opts.ase |= (arch_info->ase & ~file_ase_explicit);
+
+ /* Set up the current options. These may change throughout assembly. */
+ mips_opts = file_mips_opts;
+
+ mips_check_isa_supports_ases ();
+ mips_check_options (&file_mips_opts, TRUE);
+ file_mips_opts_checked = TRUE;
+
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mips, file_mips_opts.arch))
+ as_warn (_("could not set architecture and machine"));
+}
+
+void
+md_assemble (char *str)
+{
+ struct mips_cl_insn insn;
+ bfd_reloc_code_real_type unused_reloc[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ file_mips_check_options ();
+
+ imm_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+ clear_insn_error ();
+
+ if (mips_opts.mips16)
+ mips16_ip (str, &insn);
+ else
+ {
+ mips_ip (str, &insn);
+ DBG ((_("returned from mips_ip(%s) insn_opcode = 0x%x\n"),
+ str, insn.insn_opcode));
+ }
+
+ if (insn_error.msg)
+ report_insn_error (str);
+ else if (insn.insn_mo->pinfo == INSN_MACRO)
+ {
+ macro_start ();
+ if (mips_opts.mips16)
+ mips16_macro (&insn);
+ else
+ macro (&insn, str);
+ macro_end ();
+ }
+ else
+ {
+ if (offset_expr.X_op != O_absent)
+ append_insn (&insn, &offset_expr, offset_reloc, FALSE);
+ else
+ append_insn (&insn, NULL, unused_reloc, FALSE);
+ }
+
+ mips_assembling_insn = FALSE;
+}
+
+/* Convenience functions for abstracting away the differences between
+ MIPS16 and non-MIPS16 relocations. */
+
+static inline bfd_boolean
+mips16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_MIPS16_JMP:
+ case BFD_RELOC_MIPS16_GPREL:
+ case BFD_RELOC_MIPS16_GOT16:
+ case BFD_RELOC_MIPS16_CALL16:
+ case BFD_RELOC_MIPS16_HI16_S:
+ case BFD_RELOC_MIPS16_HI16:
+ case BFD_RELOC_MIPS16_LO16:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static inline bfd_boolean
+micromips_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_GPREL16:
+ case BFD_RELOC_MICROMIPS_JMP:
+ case BFD_RELOC_MICROMIPS_HI16:
+ case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MICROMIPS_LO16:
+ case BFD_RELOC_MICROMIPS_LITERAL:
+ case BFD_RELOC_MICROMIPS_GOT16:
+ case BFD_RELOC_MICROMIPS_CALL16:
+ case BFD_RELOC_MICROMIPS_GOT_HI16:
+ case BFD_RELOC_MICROMIPS_GOT_LO16:
+ case BFD_RELOC_MICROMIPS_CALL_HI16:
+ case BFD_RELOC_MICROMIPS_CALL_LO16:
+ case BFD_RELOC_MICROMIPS_SUB:
+ case BFD_RELOC_MICROMIPS_GOT_PAGE:
+ case BFD_RELOC_MICROMIPS_GOT_OFST:
+ case BFD_RELOC_MICROMIPS_GOT_DISP:
+ case BFD_RELOC_MICROMIPS_HIGHEST:
+ case BFD_RELOC_MICROMIPS_HIGHER:
+ case BFD_RELOC_MICROMIPS_SCN_DISP:
+ case BFD_RELOC_MICROMIPS_JALR:
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+static inline bfd_boolean
+jmp_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return reloc == BFD_RELOC_MIPS_JMP || reloc == BFD_RELOC_MICROMIPS_JMP;
+}
+
+static inline bfd_boolean
+got16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_MIPS_GOT16 || reloc == BFD_RELOC_MIPS16_GOT16
+ || reloc == BFD_RELOC_MICROMIPS_GOT16);
+}
+
+static inline bfd_boolean
+hi16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_HI16_S || reloc == BFD_RELOC_MIPS16_HI16_S
+ || reloc == BFD_RELOC_MICROMIPS_HI16_S);
+}
+
+static inline bfd_boolean
+lo16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_LO16 || reloc == BFD_RELOC_MIPS16_LO16
+ || reloc == BFD_RELOC_MICROMIPS_LO16);
+}
+
+static inline bfd_boolean
+jalr_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return reloc == BFD_RELOC_MIPS_JALR || reloc == BFD_RELOC_MICROMIPS_JALR;
+}
+
+static inline bfd_boolean
+gprel16_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ return (reloc == BFD_RELOC_GPREL16 || reloc == BFD_RELOC_MIPS16_GPREL
+ || reloc == BFD_RELOC_MICROMIPS_GPREL16);
+}
+
+/* Return true if RELOC is a PC-relative relocation that does not have
+ full address range. */
+
+static inline bfd_boolean
+limited_pcrel_reloc_p (bfd_reloc_code_real_type reloc)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ case BFD_RELOC_MIPS_19_PCREL_S2:
+ return TRUE;
+
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_LO16_PCREL:
+ return HAVE_64BIT_ADDRESSES;
+
+ default:
+ return FALSE;
+ }
+}
+
+/* Return true if the given relocation might need a matching %lo().
+ This is only "might" because SVR4 R_MIPS_GOT16 relocations only
+ need a matching %lo() when applied to local symbols. */
+
+static inline bfd_boolean
+reloc_needs_lo_p (bfd_reloc_code_real_type reloc)
+{
+ return (HAVE_IN_PLACE_ADDENDS
+ && (hi16_reloc_p (reloc)
+ /* VxWorks R_MIPS_GOT16 relocs never need a matching %lo();
+ all GOT16 relocations evaluate to "G". */
+ || (got16_reloc_p (reloc) && mips_pic != VXWORKS_PIC)));
+}
+
+/* Return the type of %lo() reloc needed by RELOC, given that
+ reloc_needs_lo_p. */
+
+static inline bfd_reloc_code_real_type
+matching_lo_reloc (bfd_reloc_code_real_type reloc)
+{
+ return (mips16_reloc_p (reloc) ? BFD_RELOC_MIPS16_LO16
+ : (micromips_reloc_p (reloc) ? BFD_RELOC_MICROMIPS_LO16
+ : BFD_RELOC_LO16));
+}
+
+/* Return true if the given fixup is followed by a matching R_MIPS_LO16
+ relocation. */
+
+static inline bfd_boolean
+fixup_has_matching_lo_p (fixS *fixp)
+{
+ return (fixp->fx_next != NULL
+ && fixp->fx_next->fx_r_type == matching_lo_reloc (fixp->fx_r_type)
+ && fixp->fx_addsy == fixp->fx_next->fx_addsy
+ && fixp->fx_offset == fixp->fx_next->fx_offset);
+}
+
+/* Move all labels in LABELS to the current insertion point. TEXT_P
+ says whether the labels refer to text or data. */
+
+static void
+mips_move_labels (struct insn_label_list *labels, bfd_boolean text_p)
+{
+ struct insn_label_list *l;
+ valueT val;
+
+ for (l = labels; l != NULL; l = l->next)
+ {
+ gas_assert (S_GET_SEGMENT (l->label) == now_seg);
+ symbol_set_frag (l->label, frag_now);
+ val = (valueT) frag_now_fix ();
+ /* MIPS16/microMIPS text labels are stored as odd. */
+ if (text_p && HAVE_CODE_COMPRESSION)
+ ++val;
+ S_SET_VALUE (l->label, val);
+ }
+}
+
+/* Move all labels in insn_labels to the current insertion point
+ and treat them as text labels. */
+
+static void
+mips_move_text_labels (void)
+{
+ mips_move_labels (seg_info (now_seg)->label_list, TRUE);
+}
+
+static bfd_boolean
+s_is_linkonce (symbolS *sym, segT from_seg)
+{
+ bfd_boolean linkonce = FALSE;
+ segT symseg = S_GET_SEGMENT (sym);
+
+ if (symseg != from_seg && !S_IS_LOCAL (sym))
+ {
+ if ((bfd_get_section_flags (stdoutput, symseg) & SEC_LINK_ONCE))
+ linkonce = TRUE;
+ /* The GNU toolchain uses an extension for ELF: a section
+ beginning with the magic string .gnu.linkonce is a
+ linkonce section. */
+ if (strncmp (segment_name (symseg), ".gnu.linkonce",
+ sizeof ".gnu.linkonce" - 1) == 0)
+ linkonce = TRUE;
+ }
+ return linkonce;
+}
+
+/* Mark MIPS16 or microMIPS instruction label LABEL. This permits the
+ linker to handle them specially, such as generating jalx instructions
+ when needed. We also make them odd for the duration of the assembly,
+ in order to generate the right sort of code. We will make them even
+ in the adjust_symtab routine, while leaving them marked. This is
+ convenient for the debugger and the disassembler. The linker knows
+ to make them odd again. */
+
+static void
+mips_compressed_mark_label (symbolS *label)
+{
+ gas_assert (HAVE_CODE_COMPRESSION);
+
+ if (mips_opts.mips16)
+ S_SET_OTHER (label, ELF_ST_SET_MIPS16 (S_GET_OTHER (label)));
+ else
+ S_SET_OTHER (label, ELF_ST_SET_MICROMIPS (S_GET_OTHER (label)));
+ if ((S_GET_VALUE (label) & 1) == 0
+ /* Don't adjust the address if the label is global or weak, or
+ in a link-once section, since we'll be emitting symbol reloc
+ references to it which will be patched up by the linker, and
+ the final value of the symbol may or may not be MIPS16/microMIPS. */
+ && !S_IS_WEAK (label)
+ && !S_IS_EXTERNAL (label)
+ && !s_is_linkonce (label, now_seg))
+ S_SET_VALUE (label, S_GET_VALUE (label) | 1);
+}
+
+/* Mark preceding MIPS16 or microMIPS instruction labels. */
+
+static void
+mips_compressed_mark_labels (void)
+{
+ struct insn_label_list *l;
+
+ for (l = seg_info (now_seg)->label_list; l != NULL; l = l->next)
+ mips_compressed_mark_label (l->label);
+}
+
+/* End the current frag. Make it a variant frag and record the
+ relaxation info. */
+
+static void
+relax_close_frag (void)
+{
+ mips_macro_warning.first_frag = frag_now;
+ frag_var (rs_machine_dependent, 0, 0,
+ RELAX_ENCODE (mips_relax.sizes[0], mips_relax.sizes[1]),
+ mips_relax.symbol, 0, (char *) mips_relax.first_fixup);
+
+ memset (&mips_relax.sizes, 0, sizeof (mips_relax.sizes));
+ mips_relax.first_fixup = 0;
+}
+
+/* Start a new relaxation sequence whose expansion depends on SYMBOL.
+ See the comment above RELAX_ENCODE for more details. */
+
+static void
+relax_start (symbolS *symbol)
+{
+ gas_assert (mips_relax.sequence == 0);
+ mips_relax.sequence = 1;
+ mips_relax.symbol = symbol;
+}
+
+/* Start generating the second version of a relaxable sequence.
+ See the comment above RELAX_ENCODE for more details. */
+
+static void
+relax_switch (void)
+{
+ gas_assert (mips_relax.sequence == 1);
+ mips_relax.sequence = 2;
+}
+
+/* End the current relaxable sequence. */
+
+static void
+relax_end (void)
+{
+ gas_assert (mips_relax.sequence == 2);
+ relax_close_frag ();
+ mips_relax.sequence = 0;
+}
+
+/* Return true if IP is a delayed branch or jump. */
+
+static inline bfd_boolean
+delayed_branch_p (const struct mips_cl_insn *ip)
+{
+ return (ip->insn_mo->pinfo & (INSN_UNCOND_BRANCH_DELAY
+ | INSN_COND_BRANCH_DELAY
+ | INSN_COND_BRANCH_LIKELY)) != 0;
+}
+
+/* Return true if IP is a compact branch or jump. */
+
+static inline bfd_boolean
+compact_branch_p (const struct mips_cl_insn *ip)
+{
+ return (ip->insn_mo->pinfo2 & (INSN2_UNCOND_BRANCH
+ | INSN2_COND_BRANCH)) != 0;
+}
+
+/* Return true if IP is an unconditional branch or jump. */
+
+static inline bfd_boolean
+uncond_branch_p (const struct mips_cl_insn *ip)
+{
+ return ((ip->insn_mo->pinfo & INSN_UNCOND_BRANCH_DELAY) != 0
+ || (ip->insn_mo->pinfo2 & INSN2_UNCOND_BRANCH) != 0);
+}
+
+/* Return true if IP is a branch-likely instruction. */
+
+static inline bfd_boolean
+branch_likely_p (const struct mips_cl_insn *ip)
+{
+ return (ip->insn_mo->pinfo & INSN_COND_BRANCH_LIKELY) != 0;
+}
+
+/* Return the type of nop that should be used to fill the delay slot
+ of delayed branch IP. */
+
+static struct mips_cl_insn *
+get_delay_slot_nop (const struct mips_cl_insn *ip)
+{
+ if (mips_opts.micromips
+ && (ip->insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ return &micromips_nop32_insn;
+ return NOP_INSN;
+}
+
+/* Return a mask that has bit N set if OPCODE reads the register(s)
+ in operand N. */
+
+static unsigned int
+insn_read_mask (const struct mips_opcode *opcode)
+{
+ return (opcode->pinfo & INSN_READ_ALL) >> INSN_READ_SHIFT;
+}
+
+/* Return a mask that has bit N set if OPCODE writes to the register(s)
+ in operand N. */
+
+static unsigned int
+insn_write_mask (const struct mips_opcode *opcode)
+{
+ return (opcode->pinfo & INSN_WRITE_ALL) >> INSN_WRITE_SHIFT;
+}
+
+/* Return a mask of the registers specified by operand OPERAND of INSN.
+ Ignore registers of type OP_REG_<t> unless bit OP_REG_<t> of TYPE_MASK
+ is set. */
+
+static unsigned int
+operand_reg_mask (const struct mips_cl_insn *insn,
+ const struct mips_operand *operand,
+ unsigned int type_mask)
+{
+ unsigned int uval, vsel;
+
+ switch (operand->type)
+ {
+ case OP_INT:
+ case OP_MAPPED_INT:
+ case OP_MSB:
+ case OP_PCREL:
+ case OP_PERF_REG:
+ case OP_ADDIUSP_INT:
+ case OP_ENTRY_EXIT_LIST:
+ case OP_REPEAT_DEST_REG:
+ case OP_REPEAT_PREV_REG:
+ case OP_PC:
+ case OP_VU0_SUFFIX:
+ case OP_VU0_MATCH_SUFFIX:
+ case OP_IMM_INDEX:
+ abort ();
+
+ case OP_REG:
+ case OP_OPTIONAL_REG:
+ {
+ const struct mips_reg_operand *reg_op;
+
+ reg_op = (const struct mips_reg_operand *) operand;
+ if (!(type_mask & (1 << reg_op->reg_type)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return 1 << mips_decode_reg_operand (reg_op, uval);
+ }
+
+ case OP_REG_PAIR:
+ {
+ const struct mips_reg_pair_operand *pair_op;
+
+ pair_op = (const struct mips_reg_pair_operand *) operand;
+ if (!(type_mask & (1 << pair_op->reg_type)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return (1 << pair_op->reg1_map[uval]) | (1 << pair_op->reg2_map[uval]);
+ }
+
+ case OP_CLO_CLZ_DEST:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return (1 << (uval & 31)) | (1 << (uval >> 5));
+
+ case OP_SAME_RS_RT:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ gas_assert ((uval & 31) == (uval >> 5));
+ return 1 << (uval & 31);
+
+ case OP_CHECK_PREV:
+ case OP_NON_ZERO_REG:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ return 1 << (uval & 31);
+
+ case OP_LWM_SWM_LIST:
+ abort ();
+
+ case OP_SAVE_RESTORE_LIST:
+ abort ();
+
+ case OP_MDMX_IMM_REG:
+ if (!(type_mask & (1 << OP_REG_VEC)))
+ return 0;
+ uval = insn_extract_operand (insn, operand);
+ vsel = uval >> 5;
+ if ((vsel & 0x18) == 0x18)
+ return 0;
+ return 1 << (uval & 31);
+
+ case OP_REG_INDEX:
+ if (!(type_mask & (1 << OP_REG_GP)))
+ return 0;
+ return 1 << insn_extract_operand (insn, operand);
+ }
+ abort ();
+}
+
+/* Return a mask of the registers specified by operands OPNO_MASK of INSN,
+ where bit N of OPNO_MASK is set if operand N should be included.
+ Ignore registers of type OP_REG_<t> unless bit OP_REG_<t> of TYPE_MASK
+ is set. */
+
+static unsigned int
+insn_reg_mask (const struct mips_cl_insn *insn,
+ unsigned int type_mask, unsigned int opno_mask)
+{
+ unsigned int opno, reg_mask;
+
+ opno = 0;
+ reg_mask = 0;
+ while (opno_mask != 0)
+ {
+ if (opno_mask & 1)
+ reg_mask |= operand_reg_mask (insn, insn_opno (insn, opno), type_mask);
+ opno_mask >>= 1;
+ opno += 1;
+ }
+ return reg_mask;
+}
+
+/* Return the mask of core registers that IP reads. */
+
+static unsigned int
+gpr_read_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = insn_reg_mask (ip, 1 << OP_REG_GP, insn_read_mask (ip->insn_mo));
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (pinfo & INSN_UDI)
+ {
+ /* UDI instructions have traditionally been assumed to read RS
+ and RT. */
+ mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RT, *ip);
+ mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RS, *ip);
+ }
+ if (pinfo & INSN_READ_GPR_24)
+ mask |= 1 << 24;
+ if (pinfo2 & INSN2_READ_GPR_16)
+ mask |= 1 << 16;
+ if (pinfo2 & INSN2_READ_SP)
+ mask |= 1 << SP;
+ if (pinfo2 & INSN2_READ_GPR_31)
+ mask |= 1 << 31;
+ /* Don't include register 0. */
+ return mask & ~1;
+}
+
+/* Return the mask of core registers that IP writes. */
+
+static unsigned int
+gpr_write_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo, pinfo2;
+ unsigned int mask;
+
+ mask = insn_reg_mask (ip, 1 << OP_REG_GP, insn_write_mask (ip->insn_mo));
+ pinfo = ip->insn_mo->pinfo;
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (pinfo & INSN_WRITE_GPR_24)
+ mask |= 1 << 24;
+ if (pinfo & INSN_WRITE_GPR_31)
+ mask |= 1 << 31;
+ if (pinfo & INSN_UDI)
+ /* UDI instructions have traditionally been assumed to write to RD. */
+ mask |= 1 << EXTRACT_OPERAND (mips_opts.micromips, RD, *ip);
+ if (pinfo2 & INSN2_WRITE_SP)
+ mask |= 1 << SP;
+ /* Don't include register 0. */
+ return mask & ~1;
+}
+
+/* Return the mask of floating-point registers that IP reads. */
+
+static unsigned int
+fpr_read_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo;
+ unsigned int mask;
+
+ mask = insn_reg_mask (ip, ((1 << OP_REG_FP) | (1 << OP_REG_VEC)
+ | (1 << OP_REG_MSA)),
+ insn_read_mask (ip->insn_mo));
+ pinfo = ip->insn_mo->pinfo;
+ /* Conservatively treat all operands to an FP_D instruction are doubles.
+ (This is overly pessimistic for things like cvt.d.s.) */
+ if (FPR_SIZE != 64 && (pinfo & FP_D))
+ mask |= mask << 1;
+ return mask;
+}
+
+/* Return the mask of floating-point registers that IP writes. */
+
+static unsigned int
+fpr_write_mask (const struct mips_cl_insn *ip)
+{
+ unsigned long pinfo;
+ unsigned int mask;
+
+ mask = insn_reg_mask (ip, ((1 << OP_REG_FP) | (1 << OP_REG_VEC)
+ | (1 << OP_REG_MSA)),
+ insn_write_mask (ip->insn_mo));
+ pinfo = ip->insn_mo->pinfo;
+ /* Conservatively treat all operands to an FP_D instruction are doubles.
+ (This is overly pessimistic for things like cvt.s.d.) */
+ if (FPR_SIZE != 64 && (pinfo & FP_D))
+ mask |= mask << 1;
+ return mask;
+}
+
+/* Operand OPNUM of INSN is an odd-numbered floating-point register.
+ Check whether that is allowed. */
+
+static bfd_boolean
+mips_oddfpreg_ok (const struct mips_opcode *insn, int opnum)
+{
+ const char *s = insn->name;
+ bfd_boolean oddspreg = (ISA_HAS_ODD_SINGLE_FPR (mips_opts.isa, mips_opts.arch)
+ || FPR_SIZE == 64)
+ && mips_opts.oddspreg;
+
+ if (insn->pinfo == INSN_MACRO)
+ /* Let a macro pass, we'll catch it later when it is expanded. */
+ return TRUE;
+
+ /* Single-precision coprocessor loads and moves are OK for 32-bit registers,
+ otherwise it depends on oddspreg. */
+ if ((insn->pinfo & FP_S)
+ && (insn->pinfo & (INSN_LOAD_MEMORY | INSN_STORE_MEMORY
+ | INSN_LOAD_COPROC | INSN_COPROC_MOVE)))
+ return FPR_SIZE == 32 || oddspreg;
+
+ /* Allow odd registers for single-precision ops and double-precision if the
+ floating-point registers are 64-bit wide. */
+ switch (insn->pinfo & (FP_S | FP_D))
+ {
+ case FP_S:
+ case 0:
+ return oddspreg;
+ case FP_D:
+ return FPR_SIZE == 64;
+ default:
+ break;
+ }
+
+ /* Cvt.w.x and cvt.x.w allow an odd register for a 'w' or 's' operand. */
+ s = strchr (insn->name, '.');
+ if (s != NULL && opnum == 2)
+ s = strchr (s + 1, '.');
+ if (s != NULL && (s[1] == 'w' || s[1] == 's'))
+ return oddspreg;
+
+ return FPR_SIZE == 64;
+}
+
+/* Information about an instruction argument that we're trying to match. */
+struct mips_arg_info
+{
+ /* The instruction so far. */
+ struct mips_cl_insn *insn;
+
+ /* The first unconsumed operand token. */
+ struct mips_operand_token *token;
+
+ /* The 1-based operand number, in terms of insn->insn_mo->args. */
+ int opnum;
+
+ /* The 1-based argument number, for error reporting. This does not
+ count elided optional registers, etc.. */
+ int argnum;
+
+ /* The last OP_REG operand seen, or ILLEGAL_REG if none. */
+ unsigned int last_regno;
+
+ /* If the first operand was an OP_REG, this is the register that it
+ specified, otherwise it is ILLEGAL_REG. */
+ unsigned int dest_regno;
+
+ /* The value of the last OP_INT operand. Only used for OP_MSB,
+ where it gives the lsb position. */
+ unsigned int last_op_int;
+
+ /* If true, match routines should assume that no later instruction
+ alternative matches and should therefore be as accomodating as
+ possible. Match routines should not report errors if something
+ is only invalid for !LAX_MATCH. */
+ bfd_boolean lax_match;
+
+ /* True if a reference to the current AT register was seen. */
+ bfd_boolean seen_at;
+};
+
+/* Record that the argument is out of range. */
+
+static void
+match_out_of_range (struct mips_arg_info *arg)
+{
+ set_insn_error_i (arg->argnum, _("operand %d out of range"), arg->argnum);
+}
+
+/* Record that the argument isn't constant but needs to be. */
+
+static void
+match_not_constant (struct mips_arg_info *arg)
+{
+ set_insn_error_i (arg->argnum, _("operand %d must be constant"),
+ arg->argnum);
+}
+
+/* Try to match an OT_CHAR token for character CH. Consume the token
+ and return true on success, otherwise return false. */
+
+static bfd_boolean
+match_char (struct mips_arg_info *arg, char ch)
+{
+ if (arg->token->type == OT_CHAR && arg->token->u.ch == ch)
+ {
+ ++arg->token;
+ if (ch == ',')
+ arg->argnum += 1;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Try to get an expression from the next tokens in ARG. Consume the
+ tokens and return true on success, storing the expression value in
+ VALUE and relocation types in R. */
+
+static bfd_boolean
+match_expression (struct mips_arg_info *arg, expressionS *value,
+ bfd_reloc_code_real_type *r)
+{
+ /* If the next token is a '(' that was parsed as being part of a base
+ expression, assume we have an elided offset. The later match will fail
+ if this turns out to be wrong. */
+ if (arg->token->type == OT_CHAR && arg->token->u.ch == '(')
+ {
+ value->X_op = O_constant;
+ value->X_add_number = 0;
+ r[0] = r[1] = r[2] = BFD_RELOC_UNUSED;
+ return TRUE;
+ }
+
+ /* Reject register-based expressions such as "0+$2" and "(($2))".
+ For plain registers the default error seems more appropriate. */
+ if (arg->token->type == OT_INTEGER
+ && arg->token->u.integer.value.X_op == O_register)
+ {
+ set_insn_error (arg->argnum, _("register value used as expression"));
+ return FALSE;
+ }
+
+ if (arg->token->type == OT_INTEGER)
+ {
+ *value = arg->token->u.integer.value;
+ memcpy (r, arg->token->u.integer.relocs, 3 * sizeof (*r));
+ ++arg->token;
+ return TRUE;
+ }
+
+ set_insn_error_i
+ (arg->argnum, _("operand %d must be an immediate expression"),
+ arg->argnum);
+ return FALSE;
+}
+
+/* Try to get a constant expression from the next tokens in ARG. Consume
+ the tokens and return return true on success, storing the constant value
+ in *VALUE. Use FALLBACK as the value if the match succeeded with an
+ error. */
+
+static bfd_boolean
+match_const_int (struct mips_arg_info *arg, offsetT *value)
+{
+ expressionS ex;
+ bfd_reloc_code_real_type r[3];
+
+ if (!match_expression (arg, &ex, r))
+ return FALSE;
+
+ if (r[0] == BFD_RELOC_UNUSED && ex.X_op == O_constant)
+ *value = ex.X_add_number;
+ else
+ {
+ match_not_constant (arg);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Return the RTYPE_* flags for a register operand of type TYPE that
+ appears in instruction OPCODE. */
+
+static unsigned int
+convert_reg_type (const struct mips_opcode *opcode,
+ enum mips_reg_operand_type type)
+{
+ switch (type)
+ {
+ case OP_REG_GP:
+ return RTYPE_NUM | RTYPE_GP;
+
+ case OP_REG_FP:
+ /* Allow vector register names for MDMX if the instruction is a 64-bit
+ FPR load, store or move (including moves to and from GPRs). */
+ if ((mips_opts.ase & ASE_MDMX)
+ && (opcode->pinfo & FP_D)
+ && (opcode->pinfo & (INSN_COPROC_MOVE
+ | INSN_COPROC_MEMORY_DELAY
+ | INSN_LOAD_COPROC
+ | INSN_LOAD_MEMORY
+ | INSN_STORE_MEMORY)))
+ return RTYPE_FPU | RTYPE_VEC;
+ return RTYPE_FPU;
+
+ case OP_REG_CCC:
+ if (opcode->pinfo & (FP_D | FP_S))
+ return RTYPE_CCC | RTYPE_FCC;
+ return RTYPE_CCC;
+
+ case OP_REG_VEC:
+ if (opcode->membership & INSN_5400)
+ return RTYPE_FPU;
+ return RTYPE_FPU | RTYPE_VEC;
+
+ case OP_REG_ACC:
+ return RTYPE_ACC;
+
+ case OP_REG_COPRO:
+ if (opcode->name[strlen (opcode->name) - 1] == '0')
+ return RTYPE_NUM | RTYPE_CP0;
+ return RTYPE_NUM;
+
+ case OP_REG_HW:
+ return RTYPE_NUM;
+
+ case OP_REG_VI:
+ return RTYPE_NUM | RTYPE_VI;
+
+ case OP_REG_VF:
+ return RTYPE_NUM | RTYPE_VF;
+
+ case OP_REG_R5900_I:
+ return RTYPE_R5900_I;
+
+ case OP_REG_R5900_Q:
+ return RTYPE_R5900_Q;
+
+ case OP_REG_R5900_R:
+ return RTYPE_R5900_R;
+
+ case OP_REG_R5900_ACC:
+ return RTYPE_R5900_ACC;
+
+ case OP_REG_MSA:
+ return RTYPE_MSA;
+
+ case OP_REG_MSA_CTRL:
+ return RTYPE_NUM;
+ }
+ abort ();
+}
+
+/* ARG is register REGNO, of type TYPE. Warn about any dubious registers. */
+
+static void
+check_regno (struct mips_arg_info *arg,
+ enum mips_reg_operand_type type, unsigned int regno)
+{
+ if (AT && type == OP_REG_GP && regno == AT)
+ arg->seen_at = TRUE;
+
+ if (type == OP_REG_FP
+ && (regno & 1) != 0
+ && !mips_oddfpreg_ok (arg->insn->insn_mo, arg->opnum))
+ {
+ /* This was a warning prior to introducing O32 FPXX and FP64 support
+ so maintain a warning for FP32 but raise an error for the new
+ cases. */
+ if (FPR_SIZE == 32)
+ as_warn (_("float register should be even, was %d"), regno);
+ else
+ as_bad (_("float register should be even, was %d"), regno);
+ }
+
+ if (type == OP_REG_CCC)
+ {
+ const char *name;
+ size_t length;
+
+ name = arg->insn->insn_mo->name;
+ length = strlen (name);
+ if ((regno & 1) != 0
+ && ((length >= 3 && strcmp (name + length - 3, ".ps") == 0)
+ || (length >= 5 && strncmp (name + length - 5, "any2", 4) == 0)))
+ as_warn (_("condition code register should be even for %s, was %d"),
+ name, regno);
+
+ if ((regno & 3) != 0
+ && (length >= 5 && strncmp (name + length - 5, "any4", 4) == 0))
+ as_warn (_("condition code register should be 0 or 4 for %s, was %d"),
+ name, regno);
+ }
+}
+
+/* ARG is a register with symbol value SYMVAL. Try to interpret it as
+ a register of type TYPE. Return true on success, storing the register
+ number in *REGNO and warning about any dubious uses. */
+
+static bfd_boolean
+match_regno (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int symval, unsigned int *regno)
+{
+ if (type == OP_REG_VEC)
+ symval = mips_prefer_vec_regno (symval);
+ if (!(symval & convert_reg_type (arg->insn->insn_mo, type)))
+ return FALSE;
+
+ *regno = symval & RNUM_MASK;
+ check_regno (arg, type, *regno);
+ return TRUE;
+}
+
+/* Try to interpret the next token in ARG as a register of type TYPE.
+ Consume the token and return true on success, storing the register
+ number in *REGNO. Return false on failure. */
+
+static bfd_boolean
+match_reg (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int *regno)
+{
+ if (arg->token->type == OT_REG
+ && match_regno (arg, type, arg->token->u.regno, regno))
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Try to interpret the next token in ARG as a range of registers of type TYPE.
+ Consume the token and return true on success, storing the register numbers
+ in *REGNO1 and *REGNO2. Return false on failure. */
+
+static bfd_boolean
+match_reg_range (struct mips_arg_info *arg, enum mips_reg_operand_type type,
+ unsigned int *regno1, unsigned int *regno2)
+{
+ if (match_reg (arg, type, regno1))
+ {
+ *regno2 = *regno1;
+ return TRUE;
+ }
+ if (arg->token->type == OT_REG_RANGE
+ && match_regno (arg, type, arg->token->u.reg_range.regno1, regno1)
+ && match_regno (arg, type, arg->token->u.reg_range.regno2, regno2)
+ && *regno1 <= *regno2)
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* OP_INT matcher. */
+
+static bfd_boolean
+match_int_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_int_operand *operand;
+ unsigned int uval;
+ int min_val, max_val, factor;
+ offsetT sval;
+
+ operand = (const struct mips_int_operand *) operand_base;
+ factor = 1 << operand->shift;
+ min_val = mips_int_operand_min (operand);
+ max_val = mips_int_operand_max (operand);
+
+ if (operand_base->lsb == 0
+ && operand_base->size == 16
+ && operand->shift == 0
+ && operand->bias == 0
+ && (operand->max_val == 32767 || operand->max_val == 65535))
+ {
+ /* The operand can be relocated. */
+ if (!match_expression (arg, &offset_expr, offset_reloc))
+ return FALSE;
+
+ if (offset_reloc[0] != BFD_RELOC_UNUSED)
+ /* Relocation operators were used. Accept the arguent and
+ leave the relocation value in offset_expr and offset_relocs
+ for the caller to process. */
+ return TRUE;
+
+ if (offset_expr.X_op != O_constant)
+ {
+ /* Accept non-constant operands if no later alternative matches,
+ leaving it for the caller to process. */
+ if (!arg->lax_match)
+ return FALSE;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ return TRUE;
+ }
+
+ /* Clear the global state; we're going to install the operand
+ ourselves. */
+ sval = offset_expr.X_add_number;
+ offset_expr.X_op = O_absent;
+
+ /* For compatibility with older assemblers, we accept
+ 0x8000-0xffff as signed 16-bit numbers when only
+ signed numbers are allowed. */
+ if (sval > max_val)
+ {
+ max_val = ((1 << operand_base->size) - 1) << operand->shift;
+ if (!arg->lax_match && sval <= max_val)
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+ }
+
+ arg->last_op_int = sval;
+
+ if (sval < min_val || sval > max_val || sval % factor)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+
+ uval = (unsigned int) sval >> operand->shift;
+ uval -= operand->bias;
+
+ /* Handle -mfix-cn63xxp1. */
+ if (arg->opnum == 1
+ && mips_fix_cn63xxp1
+ && !mips_opts.micromips
+ && strcmp ("pref", arg->insn->insn_mo->name) == 0)
+ switch (uval)
+ {
+ case 5:
+ case 25:
+ case 26:
+ case 27:
+ case 28:
+ case 29:
+ case 30:
+ case 31:
+ /* These are ok. */
+ break;
+
+ default:
+ /* The rest must be changed to 28. */
+ uval = 28;
+ break;
+ }
+
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
+
+/* OP_MAPPED_INT matcher. */
+
+static bfd_boolean
+match_mapped_int_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_mapped_int_operand *operand;
+ unsigned int uval, num_vals;
+ offsetT sval;
+
+ operand = (const struct mips_mapped_int_operand *) operand_base;
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+
+ num_vals = 1 << operand_base->size;
+ for (uval = 0; uval < num_vals; uval++)
+ if (operand->int_map[uval] == sval)
+ break;
+ if (uval == num_vals)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
+
+/* OP_MSB matcher. */
+
+static bfd_boolean
+match_msb_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_msb_operand *operand;
+ int min_val, max_val, max_high;
+ offsetT size, sval, high;
+
+ operand = (const struct mips_msb_operand *) operand_base;
+ min_val = operand->bias;
+ max_val = min_val + (1 << operand_base->size) - 1;
+ max_high = operand->opsize;
+
+ if (!match_const_int (arg, &size))
+ return FALSE;
+
+ high = size + arg->last_op_int;
+ sval = operand->add_lsb ? high : size;
+
+ if (size < 0 || high > max_high || sval < min_val || sval > max_val)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+ insn_insert_operand (arg->insn, operand_base, sval - min_val);
+ return TRUE;
+}
+
+/* OP_REG matcher. */
+
+static bfd_boolean
+match_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_reg_operand *operand;
+ unsigned int regno, uval, num_vals;
+
+ operand = (const struct mips_reg_operand *) operand_base;
+ if (!match_reg (arg, operand->reg_type, &regno))
+ return FALSE;
+
+ if (operand->reg_map)
+ {
+ num_vals = 1 << operand->root.size;
+ for (uval = 0; uval < num_vals; uval++)
+ if (operand->reg_map[uval] == regno)
+ break;
+ if (num_vals == uval)
+ return FALSE;
+ }
+ else
+ uval = regno;
+
+ arg->last_regno = regno;
+ if (arg->opnum == 1)
+ arg->dest_regno = regno;
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
+
+/* OP_REG_PAIR matcher. */
+
+static bfd_boolean
+match_reg_pair_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_reg_pair_operand *operand;
+ unsigned int regno1, regno2, uval, num_vals;
+
+ operand = (const struct mips_reg_pair_operand *) operand_base;
+ if (!match_reg (arg, operand->reg_type, &regno1)
+ || !match_char (arg, ',')
+ || !match_reg (arg, operand->reg_type, &regno2))
+ return FALSE;
+
+ num_vals = 1 << operand_base->size;
+ for (uval = 0; uval < num_vals; uval++)
+ if (operand->reg1_map[uval] == regno1 && operand->reg2_map[uval] == regno2)
+ break;
+ if (uval == num_vals)
+ return FALSE;
+
+ insn_insert_operand (arg->insn, operand_base, uval);
+ return TRUE;
+}
+
+/* OP_PCREL matcher. The caller chooses the relocation type. */
+
+static bfd_boolean
+match_pcrel_operand (struct mips_arg_info *arg)
+{
+ bfd_reloc_code_real_type r[3];
+
+ return match_expression (arg, &offset_expr, r) && r[0] == BFD_RELOC_UNUSED;
+}
+
+/* OP_PERF_REG matcher. */
+
+static bfd_boolean
+match_perf_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ offsetT sval;
+
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+
+ if (sval != 0
+ && (sval != 1
+ || (mips_opts.arch == CPU_R5900
+ && (strcmp (arg->insn->insn_mo->name, "mfps") == 0
+ || strcmp (arg->insn->insn_mo->name, "mtps") == 0))))
+ {
+ set_insn_error (arg->argnum, _("invalid performance register"));
+ return FALSE;
+ }
+
+ insn_insert_operand (arg->insn, operand, sval);
+ return TRUE;
+}
+
+/* OP_ADDIUSP matcher. */
+
+static bfd_boolean
+match_addiusp_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ offsetT sval;
+ unsigned int uval;
+
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+
+ if (sval % 4)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+
+ sval /= 4;
+ if (!(sval >= -258 && sval <= 257) || (sval >= -2 && sval <= 1))
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+
+ uval = (unsigned int) sval;
+ uval = ((uval >> 1) & ~0xff) | (uval & 0xff);
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
+}
+
+/* OP_CLO_CLZ_DEST matcher. */
+
+static bfd_boolean
+match_clo_clz_dest_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
+
+ if (!match_reg (arg, OP_REG_GP, &regno))
+ return FALSE;
+
+ insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+ return TRUE;
+}
+
+/* OP_CHECK_PREV matcher. */
+
+static bfd_boolean
+match_check_prev_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand_base)
+{
+ const struct mips_check_prev_operand *operand;
+ unsigned int regno;
+
+ operand = (const struct mips_check_prev_operand *) operand_base;
+
+ if (!match_reg (arg, OP_REG_GP, &regno))
+ return FALSE;
+
+ if (!operand->zero_ok && regno == 0)
+ return FALSE;
+
+ if ((operand->less_than_ok && regno < arg->last_regno)
+ || (operand->greater_than_ok && regno > arg->last_regno)
+ || (operand->equal_ok && regno == arg->last_regno))
+ {
+ arg->last_regno = regno;
+ insn_insert_operand (arg->insn, operand_base, regno);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* OP_SAME_RS_RT matcher. */
+
+static bfd_boolean
+match_same_rs_rt_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
+
+ if (!match_reg (arg, OP_REG_GP, &regno))
+ return FALSE;
+
+ if (regno == 0)
+ {
+ set_insn_error (arg->argnum, _("the source register must not be $0"));
+ return FALSE;
+ }
+
+ arg->last_regno = regno;
+
+ insn_insert_operand (arg->insn, operand, regno | (regno << 5));
+ return TRUE;
+}
+
+/* OP_LWM_SWM_LIST matcher. */
+
+static bfd_boolean
+match_lwm_swm_list_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int reglist, sregs, ra, regno1, regno2;
+ struct mips_arg_info reset;
+
+ reglist = 0;
+ if (!match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+ return FALSE;
+ do
+ {
+ if (regno2 == FP && regno1 >= S0 && regno1 <= S7)
+ {
+ reglist |= 1 << FP;
+ regno2 = S7;
+ }
+ reglist |= ((1U << regno2 << 1) - 1) & -(1U << regno1);
+ reset = *arg;
+ }
+ while (match_char (arg, ',')
+ && match_reg_range (arg, OP_REG_GP, &regno1, &regno2));
+ *arg = reset;
+
+ if (operand->size == 2)
+ {
+ /* The list must include both ra and s0-sN, for 0 <= N <= 3. E.g.:
+
+ s0, ra
+ s0, s1, ra, s2, s3
+ s0-s2, ra
+
+ and any permutations of these. */
+ if ((reglist & 0xfff1ffff) != 0x80010000)
+ return FALSE;
+
+ sregs = (reglist >> 17) & 7;
+ ra = 0;
+ }
+ else
+ {
+ /* The list must include at least one of ra and s0-sN,
+ for 0 <= N <= 8. (Note that there is a gap between s7 and s8,
+ which are $23 and $30 respectively.) E.g.:
+
+ ra
+ s0
+ ra, s0, s1, s2
+ s0-s8
+ s0-s5, ra
+
+ and any permutations of these. */
+ if ((reglist & 0x3f00ffff) != 0)
+ return FALSE;
+
+ ra = (reglist >> 27) & 0x10;
+ sregs = ((reglist >> 22) & 0x100) | ((reglist >> 16) & 0xff);
+ }
+ sregs += 1;
+ if ((sregs & -sregs) != sregs)
+ return FALSE;
+
+ insn_insert_operand (arg->insn, operand, (ffs (sregs) - 1) | ra);
+ return TRUE;
+}
+
+/* OP_ENTRY_EXIT_LIST matcher. */
+
+static unsigned int
+match_entry_exit_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int mask;
+ bfd_boolean is_exit;
+
+ /* The format is the same for both ENTRY and EXIT, but the constraints
+ are different. */
+ is_exit = strcmp (arg->insn->insn_mo->name, "exit") == 0;
+ mask = (is_exit ? 7 << 3 : 0);
+ do
+ {
+ unsigned int regno1, regno2;
+ bfd_boolean is_freg;
+
+ if (match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+ is_freg = FALSE;
+ else if (match_reg_range (arg, OP_REG_FP, &regno1, &regno2))
+ is_freg = TRUE;
+ else
+ return FALSE;
+
+ if (is_exit && is_freg && regno1 == 0 && regno2 < 2)
+ {
+ mask &= ~(7 << 3);
+ mask |= (5 + regno2) << 3;
+ }
+ else if (!is_exit && regno1 == 4 && regno2 >= 4 && regno2 <= 7)
+ mask |= (regno2 - 3) << 3;
+ else if (regno1 == 16 && regno2 >= 16 && regno2 <= 17)
+ mask |= (regno2 - 15) << 1;
+ else if (regno1 == RA && regno2 == RA)
+ mask |= 1;
+ else
+ return FALSE;
+ }
+ while (match_char (arg, ','));
+
+ insn_insert_operand (arg->insn, operand, mask);
+ return TRUE;
+}
+
+/* OP_SAVE_RESTORE_LIST matcher. */
+
+static bfd_boolean
+match_save_restore_list_operand (struct mips_arg_info *arg)
+{
+ unsigned int opcode, args, statics, sregs;
+ unsigned int num_frame_sizes, num_args, num_statics, num_sregs;
+ offsetT frame_size;
+
+ opcode = arg->insn->insn_opcode;
+ frame_size = 0;
+ num_frame_sizes = 0;
+ args = 0;
+ statics = 0;
+ sregs = 0;
+ do
+ {
+ unsigned int regno1, regno2;
+
+ if (arg->token->type == OT_INTEGER)
+ {
+ /* Handle the frame size. */
+ if (!match_const_int (arg, &frame_size))
+ return FALSE;
+ num_frame_sizes += 1;
+ }
+ else
+ {
+ if (!match_reg_range (arg, OP_REG_GP, &regno1, &regno2))
+ return FALSE;
+
+ while (regno1 <= regno2)
+ {
+ if (regno1 >= 4 && regno1 <= 7)
+ {
+ if (num_frame_sizes == 0)
+ /* args $a0-$a3 */
+ args |= 1 << (regno1 - 4);
+ else
+ /* statics $a0-$a3 */
+ statics |= 1 << (regno1 - 4);
+ }
+ else if (regno1 >= 16 && regno1 <= 23)
+ /* $s0-$s7 */
+ sregs |= 1 << (regno1 - 16);
+ else if (regno1 == 30)
+ /* $s8 */
+ sregs |= 1 << 8;
+ else if (regno1 == 31)
+ /* Add $ra to insn. */
+ opcode |= 0x40;
+ else
+ return FALSE;
+ regno1 += 1;
+ if (regno1 == 24)
+ regno1 = 30;
+ }
+ }
+ }
+ while (match_char (arg, ','));
+
+ /* Encode args/statics combination. */
+ if (args & statics)
+ return FALSE;
+ else if (args == 0xf)
+ /* All $a0-$a3 are args. */
+ opcode |= MIPS16_ALL_ARGS << 16;
+ else if (statics == 0xf)
+ /* All $a0-$a3 are statics. */
+ opcode |= MIPS16_ALL_STATICS << 16;
+ else
+ {
+ /* Count arg registers. */
+ num_args = 0;
+ while (args & 0x1)
+ {
+ args >>= 1;
+ num_args += 1;
+ }
+ if (args != 0)
+ return FALSE;
+
+ /* Count static registers. */
+ num_statics = 0;
+ while (statics & 0x8)
+ {
+ statics = (statics << 1) & 0xf;
+ num_statics += 1;
+ }
+ if (statics != 0)
+ return FALSE;
+
+ /* Encode args/statics. */
+ opcode |= ((num_args << 2) | num_statics) << 16;
+ }
+
+ /* Encode $s0/$s1. */
+ if (sregs & (1 << 0)) /* $s0 */
+ opcode |= 0x20;
+ if (sregs & (1 << 1)) /* $s1 */
+ opcode |= 0x10;
+ sregs >>= 2;
+
+ /* Encode $s2-$s8. */
+ num_sregs = 0;
+ while (sregs & 1)
+ {
+ sregs >>= 1;
+ num_sregs += 1;
+ }
+ if (sregs != 0)
+ return FALSE;
+ opcode |= num_sregs << 24;
+
+ /* Encode frame size. */
+ if (num_frame_sizes == 0)
+ {
+ set_insn_error (arg->argnum, _("missing frame size"));
+ return FALSE;
+ }
+ if (num_frame_sizes > 1)
+ {
+ set_insn_error (arg->argnum, _("frame size specified twice"));
+ return FALSE;
+ }
+ if ((frame_size & 7) != 0 || frame_size < 0 || frame_size > 0xff * 8)
+ {
+ set_insn_error (arg->argnum, _("invalid frame size"));
+ return FALSE;
+ }
+ if (frame_size != 128 || (opcode >> 16) != 0)
+ {
+ frame_size /= 8;
+ opcode |= (((frame_size & 0xf0) << 16)
+ | (frame_size & 0x0f));
+ }
+
+ /* Finally build the instruction. */
+ if ((opcode >> 16) != 0 || frame_size == 0)
+ opcode |= MIPS16_EXTEND;
+ arg->insn->insn_opcode = opcode;
+ return TRUE;
+}
+
+/* OP_MDMX_IMM_REG matcher. */
+
+static bfd_boolean
+match_mdmx_imm_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno, uval;
+ bfd_boolean is_qh;
+ const struct mips_opcode *opcode;
+
+ /* The mips_opcode records whether this is an octobyte or quadhalf
+ instruction. Start out with that bit in place. */
+ opcode = arg->insn->insn_mo;
+ uval = mips_extract_operand (operand, opcode->match);
+ is_qh = (uval != 0);
+
+ if (arg->token->type == OT_REG)
+ {
+ if ((opcode->membership & INSN_5400)
+ && strcmp (opcode->name, "rzu.ob") == 0)
+ {
+ set_insn_error_i (arg->argnum, _("operand %d must be an immediate"),
+ arg->argnum);
+ return FALSE;
+ }
+
+ if (!match_regno (arg, OP_REG_VEC, arg->token->u.regno, &regno))
+ return FALSE;
+ ++arg->token;
+
+ /* Check whether this is a vector register or a broadcast of
+ a single element. */
+ if (arg->token->type == OT_INTEGER_INDEX)
+ {
+ if (arg->token->u.index > (is_qh ? 3 : 7))
+ {
+ set_insn_error (arg->argnum, _("invalid element selector"));
+ return FALSE;
+ }
+ uval |= arg->token->u.index << (is_qh ? 2 : 1) << 5;
+ ++arg->token;
+ }
+ else
+ {
+ /* A full vector. */
+ if ((opcode->membership & INSN_5400)
+ && (strcmp (opcode->name, "sll.ob") == 0
+ || strcmp (opcode->name, "srl.ob") == 0))
+ {
+ set_insn_error_i (arg->argnum, _("operand %d must be scalar"),
+ arg->argnum);
+ return FALSE;
+ }
+
+ if (is_qh)
+ uval |= MDMX_FMTSEL_VEC_QH << 5;
+ else
+ uval |= MDMX_FMTSEL_VEC_OB << 5;
+ }
+ uval |= regno;
+ }
+ else
+ {
+ offsetT sval;
+
+ if (!match_const_int (arg, &sval))
+ return FALSE;
+ if (sval < 0 || sval > 31)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+ uval |= (sval & 31);
+ if (is_qh)
+ uval |= MDMX_FMTSEL_IMM_QH << 5;
+ else
+ uval |= MDMX_FMTSEL_IMM_OB << 5;
+ }
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
+}
+
+/* OP_IMM_INDEX matcher. */
+
+static bfd_boolean
+match_imm_index_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int max_val;
+
+ if (arg->token->type != OT_INTEGER_INDEX)
+ return FALSE;
+
+ max_val = (1 << operand->size) - 1;
+ if (arg->token->u.index > max_val)
+ {
+ match_out_of_range (arg);
+ return FALSE;
+ }
+ insn_insert_operand (arg->insn, operand, arg->token->u.index);
+ ++arg->token;
+ return TRUE;
+}
+
+/* OP_REG_INDEX matcher. */
+
+static bfd_boolean
+match_reg_index_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
+
+ if (arg->token->type != OT_REG_INDEX)
+ return FALSE;
+
+ if (!match_regno (arg, OP_REG_GP, arg->token->u.regno, &regno))
+ return FALSE;
+
+ insn_insert_operand (arg->insn, operand, regno);
+ ++arg->token;
+ return TRUE;
+}
+
+/* OP_PC matcher. */
+
+static bfd_boolean
+match_pc_operand (struct mips_arg_info *arg)
+{
+ if (arg->token->type == OT_REG && (arg->token->u.regno & RTYPE_PC))
+ {
+ ++arg->token;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* OP_NON_ZERO_REG matcher. */
+
+static bfd_boolean
+match_non_zero_reg_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ unsigned int regno;
+
+ if (!match_reg (arg, OP_REG_GP, &regno))
+ return FALSE;
+
+ if (regno == 0)
+ return FALSE;
+
+ arg->last_regno = regno;
+ insn_insert_operand (arg->insn, operand, regno);
+ return TRUE;
+}
+
+/* OP_REPEAT_DEST_REG and OP_REPEAT_PREV_REG matcher. OTHER_REGNO is the
+ register that we need to match. */
+
+static bfd_boolean
+match_tied_reg_operand (struct mips_arg_info *arg, unsigned int other_regno)
+{
+ unsigned int regno;
+
+ return match_reg (arg, OP_REG_GP, &regno) && regno == other_regno;
+}
+
+/* Read a floating-point constant from S for LI.S or LI.D. LENGTH is
+ the length of the value in bytes (4 for float, 8 for double) and
+ USING_GPRS says whether the destination is a GPR rather than an FPR.
+
+ Return the constant in IMM and OFFSET as follows:
+
+ - If the constant should be loaded via memory, set IMM to O_absent and
+ OFFSET to the memory address.
+
+ - Otherwise, if the constant should be loaded into two 32-bit registers,
+ set IMM to the O_constant to load into the high register and OFFSET
+ to the corresponding value for the low register.
+
+ - Otherwise, set IMM to the full O_constant and set OFFSET to O_absent.
+
+ These constants only appear as the last operand in an instruction,
+ and every instruction that accepts them in any variant accepts them
+ in all variants. This means we don't have to worry about backing out
+ any changes if the instruction does not match. We just match
+ unconditionally and report an error if the constant is invalid. */
+
+static bfd_boolean
+match_float_constant (struct mips_arg_info *arg, expressionS *imm,
+ expressionS *offset, int length, bfd_boolean using_gprs)
+{
+ char *p;
+ segT seg, new_seg;
+ subsegT subseg;
+ const char *newname;
+ unsigned char *data;
+
+ /* Where the constant is placed is based on how the MIPS assembler
+ does things:
+
+ length == 4 && using_gprs -- immediate value only
+ length == 8 && using_gprs -- .rdata or immediate value
+ length == 4 && !using_gprs -- .lit4 or immediate value
+ length == 8 && !using_gprs -- .lit8 or immediate value
+
+ The .lit4 and .lit8 sections are only used if permitted by the
+ -G argument. */
+ if (arg->token->type != OT_FLOAT)
+ {
+ set_insn_error (arg->argnum, _("floating-point expression required"));
+ return FALSE;
+ }
+
+ gas_assert (arg->token->u.flt.length == length);
+ data = arg->token->u.flt.data;
+ ++arg->token;
+
+ /* Handle 32-bit constants for which an immediate value is best. */
+ if (length == 4
+ && (using_gprs
+ || g_switch_value < 4
+ || (data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0)))
+ {
+ imm->X_op = O_constant;
+ if (!target_big_endian)
+ imm->X_add_number = bfd_getl32 (data);
+ else
+ imm->X_add_number = bfd_getb32 (data);
+ offset->X_op = O_absent;
+ return TRUE;
+ }
+
+ /* Handle 64-bit constants for which an immediate value is best. */
+ if (length == 8
+ && !mips_disable_float_construction
+ /* Constants can only be constructed in GPRs and copied to FPRs if the
+ GPRs are at least as wide as the FPRs or MTHC1 is available.
+ Unlike most tests for 32-bit floating-point registers this check
+ specifically looks for GPR_SIZE == 32 as the FPXX ABI does not
+ permit 64-bit moves without MXHC1.
+ Force the constant into memory otherwise. */
+ && (using_gprs
+ || GPR_SIZE == 64
+ || ISA_HAS_MXHC1 (mips_opts.isa)
+ || FPR_SIZE == 32)
+ && ((data[0] == 0 && data[1] == 0)
+ || (data[2] == 0 && data[3] == 0))
+ && ((data[4] == 0 && data[5] == 0)
+ || (data[6] == 0 && data[7] == 0)))
+ {
+ /* The value is simple enough to load with a couple of instructions.
+ If using 32-bit registers, set IMM to the high order 32 bits and
+ OFFSET to the low order 32 bits. Otherwise, set IMM to the entire
+ 64 bit constant. */
+ if (GPR_SIZE == 32 || (!using_gprs && FPR_SIZE != 64))
+ {
+ imm->X_op = O_constant;
+ offset->X_op = O_constant;
+ if (!target_big_endian)
+ {
+ imm->X_add_number = bfd_getl32 (data + 4);
+ offset->X_add_number = bfd_getl32 (data);
+ }
+ else
+ {
+ imm->X_add_number = bfd_getb32 (data);
+ offset->X_add_number = bfd_getb32 (data + 4);
+ }
+ if (offset->X_add_number == 0)
+ offset->X_op = O_absent;
+ }
+ else
+ {
+ imm->X_op = O_constant;
+ if (!target_big_endian)
+ imm->X_add_number = bfd_getl64 (data);
+ else
+ imm->X_add_number = bfd_getb64 (data);
+ offset->X_op = O_absent;
+ }
+ return TRUE;
+ }
+
+ /* Switch to the right section. */
+ seg = now_seg;
+ subseg = now_subseg;
+ if (length == 4)
+ {
+ gas_assert (!using_gprs && g_switch_value >= 4);
+ newname = ".lit4";
+ }
+ else
+ {
+ if (using_gprs || g_switch_value < 8)
+ newname = RDATA_SECTION_NAME;
+ else
+ newname = ".lit8";
+ }
+
+ new_seg = subseg_new (newname, (subsegT) 0);
+ bfd_set_section_flags (stdoutput, new_seg,
+ SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA);
+ frag_align (length == 4 ? 2 : 3, 0, 0);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (new_seg, 4);
+ else
+ record_alignment (new_seg, length == 4 ? 2 : 3);
+ if (seg == now_seg)
+ as_bad (_("cannot use `%s' in this section"), arg->insn->insn_mo->name);
+
+ /* Set the argument to the current address in the section. */
+ imm->X_op = O_absent;
+ offset->X_op = O_symbol;
+ offset->X_add_symbol = symbol_temp_new_now ();
+ offset->X_add_number = 0;
+
+ /* Put the floating point number into the section. */
+ p = frag_more (length);
+ memcpy (p, data, length);
+
+ /* Switch back to the original section. */
+ subseg_set (seg, subseg);
+ return TRUE;
+}
+
+/* OP_VU0_SUFFIX and OP_VU0_MATCH_SUFFIX matcher; MATCH_P selects between
+ them. */
+
+static bfd_boolean
+match_vu0_suffix_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand,
+ bfd_boolean match_p)
+{
+ unsigned int uval;
+
+ /* The operand can be an XYZW mask or a single 2-bit channel index
+ (with X being 0). */
+ gas_assert (operand->size == 2 || operand->size == 4);
+
+ /* The suffix can be omitted when it is already part of the opcode. */
+ if (arg->token->type != OT_CHANNELS)
+ return match_p;
+
+ uval = arg->token->u.channels;
+ if (operand->size == 2)
+ {
+ /* Check that a single bit is set and convert it into a 2-bit index. */
+ if ((uval & -uval) != uval)
+ return FALSE;
+ uval = 4 - ffs (uval);
+ }
+
+ if (match_p && insn_extract_operand (arg->insn, operand) != uval)
+ return FALSE;
+
+ ++arg->token;
+ if (!match_p)
+ insn_insert_operand (arg->insn, operand, uval);
+ return TRUE;
+}
+
+/* S is the text seen for ARG. Match it against OPERAND. Return the end
+ of the argument text if the match is successful, otherwise return null. */
+
+static bfd_boolean
+match_operand (struct mips_arg_info *arg,
+ const struct mips_operand *operand)
+{
+ switch (operand->type)
+ {
+ case OP_INT:
+ return match_int_operand (arg, operand);
+
+ case OP_MAPPED_INT:
+ return match_mapped_int_operand (arg, operand);
+
+ case OP_MSB:
+ return match_msb_operand (arg, operand);
+
+ case OP_REG:
+ case OP_OPTIONAL_REG:
+ return match_reg_operand (arg, operand);
+
+ case OP_REG_PAIR:
+ return match_reg_pair_operand (arg, operand);
+
+ case OP_PCREL:
+ return match_pcrel_operand (arg);
+
+ case OP_PERF_REG:
+ return match_perf_reg_operand (arg, operand);
+
+ case OP_ADDIUSP_INT:
+ return match_addiusp_operand (arg, operand);
+
+ case OP_CLO_CLZ_DEST:
+ return match_clo_clz_dest_operand (arg, operand);
+
+ case OP_LWM_SWM_LIST:
+ return match_lwm_swm_list_operand (arg, operand);
+
+ case OP_ENTRY_EXIT_LIST:
+ return match_entry_exit_operand (arg, operand);
+
+ case OP_SAVE_RESTORE_LIST:
+ return match_save_restore_list_operand (arg);
+
+ case OP_MDMX_IMM_REG:
+ return match_mdmx_imm_reg_operand (arg, operand);
+
+ case OP_REPEAT_DEST_REG:
+ return match_tied_reg_operand (arg, arg->dest_regno);
+
+ case OP_REPEAT_PREV_REG:
+ return match_tied_reg_operand (arg, arg->last_regno);
+
+ case OP_PC:
+ return match_pc_operand (arg);
+
+ case OP_VU0_SUFFIX:
+ return match_vu0_suffix_operand (arg, operand, FALSE);
+
+ case OP_VU0_MATCH_SUFFIX:
+ return match_vu0_suffix_operand (arg, operand, TRUE);
+
+ case OP_IMM_INDEX:
+ return match_imm_index_operand (arg, operand);
+
+ case OP_REG_INDEX:
+ return match_reg_index_operand (arg, operand);
+
+ case OP_SAME_RS_RT:
+ return match_same_rs_rt_operand (arg, operand);
+
+ case OP_CHECK_PREV:
+ return match_check_prev_operand (arg, operand);
+
+ case OP_NON_ZERO_REG:
+ return match_non_zero_reg_operand (arg, operand);
+ }
+ abort ();
+}
+
+/* ARG is the state after successfully matching an instruction.
+ Issue any queued-up warnings. */
+
+static void
+check_completed_insn (struct mips_arg_info *arg)
+{
+ if (arg->seen_at)
+ {
+ if (AT == ATREG)
+ as_warn (_("used $at without \".set noat\""));
+ else
+ as_warn (_("used $%u with \".set at=$%u\""), AT, AT);
+ }
+}
+
+/* Return true if modifying general-purpose register REG needs a delay. */
+
+static bfd_boolean
+reg_needs_delay (unsigned int reg)
+{
+ unsigned long prev_pinfo;
+
+ prev_pinfo = history[0].insn_mo->pinfo;
+ if (!mips_opts.noreorder
+ && (((prev_pinfo & INSN_LOAD_MEMORY) && !gpr_interlocks)
+ || ((prev_pinfo & INSN_LOAD_COPROC) && !cop_interlocks))
+ && (gpr_write_mask (&history[0]) & (1 << reg)))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Classify an instruction according to the FIX_VR4120_* enumeration.
+ Return NUM_FIX_VR4120_CLASSES if the instruction isn't affected
+ by VR4120 errata. */
+
+static unsigned int
+classify_vr4120_insn (const char *name)
+{
+ if (strncmp (name, "macc", 4) == 0)
+ return FIX_VR4120_MACC;
+ if (strncmp (name, "dmacc", 5) == 0)
+ return FIX_VR4120_DMACC;
+ if (strncmp (name, "mult", 4) == 0)
+ return FIX_VR4120_MULT;
+ if (strncmp (name, "dmult", 5) == 0)
+ return FIX_VR4120_DMULT;
+ if (strstr (name, "div"))
+ return FIX_VR4120_DIV;
+ if (strcmp (name, "mtlo") == 0 || strcmp (name, "mthi") == 0)
+ return FIX_VR4120_MTHILO;
+ return NUM_FIX_VR4120_CLASSES;
+}
+
+#define INSN_ERET 0x42000018
+#define INSN_DERET 0x4200001f
+#define INSN_DMULT 0x1c
+#define INSN_DMULTU 0x1d
+
+/* Return the number of instructions that must separate INSN1 and INSN2,
+ where INSN1 is the earlier instruction. Return the worst-case value
+ for any INSN2 if INSN2 is null. */
+
+static unsigned int
+insns_between (const struct mips_cl_insn *insn1,
+ const struct mips_cl_insn *insn2)
+{
+ unsigned long pinfo1, pinfo2;
+ unsigned int mask;
+
+ /* If INFO2 is null, pessimistically assume that all flags are set for
+ the second instruction. */
+ pinfo1 = insn1->insn_mo->pinfo;
+ pinfo2 = insn2 ? insn2->insn_mo->pinfo : ~0U;
+
+ /* For most targets, write-after-read dependencies on the HI and LO
+ registers must be separated by at least two instructions. */
+ if (!hilo_interlocks)
+ {
+ if ((pinfo1 & INSN_READ_LO) && (pinfo2 & INSN_WRITE_LO))
+ return 2;
+ if ((pinfo1 & INSN_READ_HI) && (pinfo2 & INSN_WRITE_HI))
+ return 2;
+ }
+
+ /* If we're working around r7000 errata, there must be two instructions
+ between an mfhi or mflo and any instruction that uses the result. */
+ if (mips_7000_hilo_fix
+ && !mips_opts.micromips
+ && MF_HILO_INSN (pinfo1)
+ && (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1))))
+ return 2;
+
+ /* If we're working around 24K errata, one instruction is required
+ if an ERET or DERET is followed by a branch instruction. */
+ if (mips_fix_24k && !mips_opts.micromips)
+ {
+ if (insn1->insn_opcode == INSN_ERET
+ || insn1->insn_opcode == INSN_DERET)
+ {
+ if (insn2 == NULL
+ || insn2->insn_opcode == INSN_ERET
+ || insn2->insn_opcode == INSN_DERET
+ || delayed_branch_p (insn2))
+ return 1;
+ }
+ }
+
+ /* If we're working around PMC RM7000 errata, there must be three
+ nops between a dmult and a load instruction. */
+ if (mips_fix_rm7000 && !mips_opts.micromips)
+ {
+ if ((insn1->insn_opcode & insn1->insn_mo->mask) == INSN_DMULT
+ || (insn1->insn_opcode & insn1->insn_mo->mask) == INSN_DMULTU)
+ {
+ if (pinfo2 & INSN_LOAD_MEMORY)
+ return 3;
+ }
+ }
+
+ /* If working around VR4120 errata, check for combinations that need
+ a single intervening instruction. */
+ if (mips_fix_vr4120 && !mips_opts.micromips)
+ {
+ unsigned int class1, class2;
+
+ class1 = classify_vr4120_insn (insn1->insn_mo->name);
+ if (class1 != NUM_FIX_VR4120_CLASSES && vr4120_conflicts[class1] != 0)
+ {
+ if (insn2 == NULL)
+ return 1;
+ class2 = classify_vr4120_insn (insn2->insn_mo->name);
+ if (vr4120_conflicts[class1] & (1 << class2))
+ return 1;
+ }
+ }
+
+ if (!HAVE_CODE_COMPRESSION)
+ {
+ /* Check for GPR or coprocessor load delays. All such delays
+ are on the RT register. */
+ /* Itbl support may require additional care here. */
+ if ((!gpr_interlocks && (pinfo1 & INSN_LOAD_MEMORY))
+ || (!cop_interlocks && (pinfo1 & INSN_LOAD_COPROC)))
+ {
+ if (insn2 == NULL || (gpr_read_mask (insn2) & gpr_write_mask (insn1)))
+ return 1;
+ }
+
+ /* Check for generic coprocessor hazards.
+
+ This case is not handled very well. There is no special
+ knowledge of CP0 handling, and the coprocessors other than
+ the floating point unit are not distinguished at all. */
+ /* Itbl support may require additional care here. FIXME!
+ Need to modify this to include knowledge about
+ user specified delays! */
+ else if ((!cop_interlocks && (pinfo1 & INSN_COPROC_MOVE))
+ || (!cop_mem_interlocks && (pinfo1 & INSN_COPROC_MEMORY_DELAY)))
+ {
+ /* Handle cases where INSN1 writes to a known general coprocessor
+ register. There must be a one instruction delay before INSN2
+ if INSN2 reads that register, otherwise no delay is needed. */
+ mask = fpr_write_mask (insn1);
+ if (mask != 0)
+ {
+ if (!insn2 || (mask & fpr_read_mask (insn2)) != 0)
+ return 1;
+ }
+ else
+ {
+ /* Read-after-write dependencies on the control registers
+ require a two-instruction gap. */
+ if ((pinfo1 & INSN_WRITE_COND_CODE)
+ && (pinfo2 & INSN_READ_COND_CODE))
+ return 2;
+
+ /* We don't know exactly what INSN1 does. If INSN2 is
+ also a coprocessor instruction, assume there must be
+ a one instruction gap. */
+ if (pinfo2 & INSN_COP)
+ return 1;
+ }
+ }
+
+ /* Check for read-after-write dependencies on the coprocessor
+ control registers in cases where INSN1 does not need a general
+ coprocessor delay. This means that INSN1 is a floating point
+ comparison instruction. */
+ /* Itbl support may require additional care here. */
+ else if (!cop_interlocks
+ && (pinfo1 & INSN_WRITE_COND_CODE)
+ && (pinfo2 & INSN_READ_COND_CODE))
+ return 1;
+ }
+
+ /* Forbidden slots can not contain Control Transfer Instructions (CTIs)
+ CTIs include all branches and jumps, nal, eret, eretnc, deret, wait
+ and pause. */
+ if ((insn1->insn_mo->pinfo2 & INSN2_FORBIDDEN_SLOT)
+ && ((pinfo2 & INSN_NO_DELAY_SLOT)
+ || (insn2 && delayed_branch_p (insn2))))
+ return 1;
+
+ return 0;
+}
+
+/* Return the number of nops that would be needed to work around the
+ VR4130 mflo/mfhi errata if instruction INSN immediately followed
+ the MAX_VR4130_NOPS instructions described by HIST. Ignore hazards
+ that are contained within the first IGNORE instructions of HIST. */
+
+static int
+nops_for_vr4130 (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int i, j;
+ unsigned int mask;
+
+ /* Check if the instruction writes to HI or LO. MTHI and MTLO
+ are not affected by the errata. */
+ if (insn != 0
+ && ((insn->insn_mo->pinfo & (INSN_WRITE_HI | INSN_WRITE_LO)) == 0
+ || strcmp (insn->insn_mo->name, "mtlo") == 0
+ || strcmp (insn->insn_mo->name, "mthi") == 0))
+ return 0;
+
+ /* Search for the first MFLO or MFHI. */
+ for (i = 0; i < MAX_VR4130_NOPS; i++)
+ if (MF_HILO_INSN (hist[i].insn_mo->pinfo))
+ {
+ /* Extract the destination register. */
+ mask = gpr_write_mask (&hist[i]);
+
+ /* No nops are needed if INSN reads that register. */
+ if (insn != NULL && (gpr_read_mask (insn) & mask) != 0)
+ return 0;
+
+ /* ...or if any of the intervening instructions do. */
+ for (j = 0; j < i; j++)
+ if (gpr_read_mask (&hist[j]) & mask)
+ return 0;
+
+ if (i >= ignore)
+ return MAX_VR4130_NOPS - i;
+ }
+ return 0;
+}
+
+#define BASE_REG_EQ(INSN1, INSN2) \
+ ((((INSN1) >> OP_SH_RS) & OP_MASK_RS) \
+ == (((INSN2) >> OP_SH_RS) & OP_MASK_RS))
+
+/* Return the minimum alignment for this store instruction. */
+
+static int
+fix_24k_align_to (const struct mips_opcode *mo)
+{
+ if (strcmp (mo->name, "sh") == 0)
+ return 2;
+
+ if (strcmp (mo->name, "swc1") == 0
+ || strcmp (mo->name, "swc2") == 0
+ || strcmp (mo->name, "sw") == 0
+ || strcmp (mo->name, "sc") == 0
+ || strcmp (mo->name, "s.s") == 0)
+ return 4;
+
+ if (strcmp (mo->name, "sdc1") == 0
+ || strcmp (mo->name, "sdc2") == 0
+ || strcmp (mo->name, "s.d") == 0)
+ return 8;
+
+ /* sb, swl, swr */
+ return 1;
+}
+
+struct fix_24k_store_info
+ {
+ /* Immediate offset, if any, for this store instruction. */
+ short off;
+ /* Alignment required by this store instruction. */
+ int align_to;
+ /* True for register offsets. */
+ int register_offset;
+ };
+
+/* Comparison function used by qsort. */
+
+static int
+fix_24k_sort (const void *a, const void *b)
+{
+ const struct fix_24k_store_info *pos1 = a;
+ const struct fix_24k_store_info *pos2 = b;
+
+ return (pos1->off - pos2->off);
+}
+
+/* INSN is a store instruction. Try to record the store information
+ in STINFO. Return false if the information isn't known. */
+
+static bfd_boolean
+fix_24k_record_store_info (struct fix_24k_store_info *stinfo,
+ const struct mips_cl_insn *insn)
+{
+ /* The instruction must have a known offset. */
+ if (!insn->complete_p || !strstr (insn->insn_mo->args, "o("))
+ return FALSE;
+
+ stinfo->off = (insn->insn_opcode >> OP_SH_IMMEDIATE) & OP_MASK_IMMEDIATE;
+ stinfo->align_to = fix_24k_align_to (insn->insn_mo);
+ return TRUE;
+}
+
+/* Return the number of nops that would be needed to work around the 24k
+ "lost data on stores during refill" errata if instruction INSN
+ immediately followed the 2 instructions described by HIST.
+ Ignore hazards that are contained within the first IGNORE
+ instructions of HIST.
+
+ Problem: The FSB (fetch store buffer) acts as an intermediate buffer
+ for the data cache refills and store data. The following describes
+ the scenario where the store data could be lost.
+
+ * A data cache miss, due to either a load or a store, causing fill
+ data to be supplied by the memory subsystem
+ * The first three doublewords of fill data are returned and written
+ into the cache
+ * A sequence of four stores occurs in consecutive cycles around the
+ final doubleword of the fill:
+ * Store A
+ * Store B
+ * Store C
+ * Zero, One or more instructions
+ * Store D
+
+ The four stores A-D must be to different doublewords of the line that
+ is being filled. The fourth instruction in the sequence above permits
+ the fill of the final doubleword to be transferred from the FSB into
+ the cache. In the sequence above, the stores may be either integer
+ (sb, sh, sw, swr, swl, sc) or coprocessor (swc1/swc2, sdc1/sdc2,
+ swxc1, sdxc1, suxc1) stores, as long as the four stores are to
+ different doublewords on the line. If the floating point unit is
+ running in 1:2 mode, it is not possible to create the sequence above
+ using only floating point store instructions.
+
+ In this case, the cache line being filled is incorrectly marked
+ invalid, thereby losing the data from any store to the line that
+ occurs between the original miss and the completion of the five
+ cycle sequence shown above.
+
+ The workarounds are:
+
+ * Run the data cache in write-through mode.
+ * Insert a non-store instruction between
+ Store A and Store B or Store B and Store C. */
+
+static int
+nops_for_24k (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ struct fix_24k_store_info pos[3];
+ int align, i, base_offset;
+
+ if (ignore >= 2)
+ return 0;
+
+ /* If the previous instruction wasn't a store, there's nothing to
+ worry about. */
+ if ((hist[0].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
+
+ /* If the instructions after the previous one are unknown, we have
+ to assume the worst. */
+ if (!insn)
+ return 1;
+
+ /* Check whether we are dealing with three consecutive stores. */
+ if ((insn->insn_mo->pinfo & INSN_STORE_MEMORY) == 0
+ || (hist[1].insn_mo->pinfo & INSN_STORE_MEMORY) == 0)
+ return 0;
+
+ /* If we don't know the relationship between the store addresses,
+ assume the worst. */
+ if (!BASE_REG_EQ (insn->insn_opcode, hist[0].insn_opcode)
+ || !BASE_REG_EQ (insn->insn_opcode, hist[1].insn_opcode))
+ return 1;
+
+ if (!fix_24k_record_store_info (&pos[0], insn)
+ || !fix_24k_record_store_info (&pos[1], &hist[0])
+ || !fix_24k_record_store_info (&pos[2], &hist[1]))
+ return 1;
+
+ qsort (&pos, 3, sizeof (struct fix_24k_store_info), fix_24k_sort);
+
+ /* Pick a value of ALIGN and X such that all offsets are adjusted by
+ X bytes and such that the base register + X is known to be aligned
+ to align bytes. */
+
+ if (((insn->insn_opcode >> OP_SH_RS) & OP_MASK_RS) == SP)
+ align = 8;
+ else
+ {
+ align = pos[0].align_to;
+ base_offset = pos[0].off;
+ for (i = 1; i < 3; i++)
+ if (align < pos[i].align_to)
+ {
+ align = pos[i].align_to;
+ base_offset = pos[i].off;
+ }
+ for (i = 0; i < 3; i++)
+ pos[i].off -= base_offset;
+ }
+
+ pos[0].off &= ~align + 1;
+ pos[1].off &= ~align + 1;
+ pos[2].off &= ~align + 1;
+
+ /* If any two stores write to the same chunk, they also write to the
+ same doubleword. The offsets are still sorted at this point. */
+ if (pos[0].off == pos[1].off || pos[1].off == pos[2].off)
+ return 0;
+
+ /* A range of at least 9 bytes is needed for the stores to be in
+ non-overlapping doublewords. */
+ if (pos[2].off - pos[0].off <= 8)
+ return 0;
+
+ if (pos[2].off - pos[1].off >= 24
+ || pos[1].off - pos[0].off >= 24
+ || pos[2].off - pos[0].off >= 32)
+ return 0;
+
+ return 1;
+}
+
+/* Return the number of nops that would be needed if instruction INSN
+ immediately followed the MAX_NOPS instructions given by HIST,
+ where HIST[0] is the most recent instruction. Ignore hazards
+ between INSN and the first IGNORE instructions in HIST.
+
+ If INSN is null, return the worse-case number of nops for any
+ instruction. */
+
+static int
+nops_for_insn (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int i, nops, tmp_nops;
+
+ nops = 0;
+ for (i = ignore; i < MAX_DELAY_NOPS; i++)
+ {
+ tmp_nops = insns_between (hist + i, insn) - i;
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+
+ if (mips_fix_vr4130 && !mips_opts.micromips)
+ {
+ tmp_nops = nops_for_vr4130 (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+
+ if (mips_fix_24k && !mips_opts.micromips)
+ {
+ tmp_nops = nops_for_24k (ignore, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+
+ return nops;
+}
+
+/* The variable arguments provide NUM_INSNS extra instructions that
+ might be added to HIST. Return the largest number of nops that
+ would be needed after the extended sequence, ignoring hazards
+ in the first IGNORE instructions. */
+
+static int
+nops_for_sequence (int num_insns, int ignore,
+ const struct mips_cl_insn *hist, ...)
+{
+ va_list args;
+ struct mips_cl_insn buffer[MAX_NOPS];
+ struct mips_cl_insn *cursor;
+ int nops;
+
+ va_start (args, hist);
+ cursor = buffer + num_insns;
+ memcpy (cursor, hist, (MAX_NOPS - num_insns) * sizeof (*cursor));
+ while (cursor > buffer)
+ *--cursor = *va_arg (args, const struct mips_cl_insn *);
+
+ nops = nops_for_insn (ignore, buffer, NULL);
+ va_end (args);
+ return nops;
+}
+
+/* Like nops_for_insn, but if INSN is a branch, take into account the
+ worst-case delay for the branch target. */
+
+static int
+nops_for_insn_or_target (int ignore, const struct mips_cl_insn *hist,
+ const struct mips_cl_insn *insn)
+{
+ int nops, tmp_nops;
+
+ nops = nops_for_insn (ignore, hist, insn);
+ if (delayed_branch_p (insn))
+ {
+ tmp_nops = nops_for_sequence (2, ignore ? ignore + 2 : 0,
+ hist, insn, get_delay_slot_nop (insn));
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+ else if (compact_branch_p (insn))
+ {
+ tmp_nops = nops_for_sequence (1, ignore ? ignore + 1 : 0, hist, insn);
+ if (tmp_nops > nops)
+ nops = tmp_nops;
+ }
+ return nops;
+}
+
+/* Fix NOP issue: Replace nops by "or at,at,zero". */
+
+static void
+fix_loongson2f_nop (struct mips_cl_insn * ip)
+{
+ gas_assert (!HAVE_CODE_COMPRESSION);
+ if (strcmp (ip->insn_mo->name, "nop") == 0)
+ ip->insn_opcode = LOONGSON2F_NOP_INSN;
+}
+
+/* Fix Jump Issue: Eliminate instruction fetch from outside 256M region
+ jr target pc &= 'hffff_ffff_cfff_ffff. */
+
+static void
+fix_loongson2f_jump (struct mips_cl_insn * ip)
+{
+ gas_assert (!HAVE_CODE_COMPRESSION);
+ if (strcmp (ip->insn_mo->name, "j") == 0
+ || strcmp (ip->insn_mo->name, "jr") == 0
+ || strcmp (ip->insn_mo->name, "jalr") == 0)
+ {
+ int sreg;
+ expressionS ep;
+
+ if (! mips_opts.at)
+ return;
+
+ sreg = EXTRACT_OPERAND (0, RS, *ip);
+ if (sreg == ZERO || sreg == KT0 || sreg == KT1 || sreg == ATREG)
+ return;
+
+ ep.X_op = O_constant;
+ ep.X_add_number = 0xcfff0000;
+ macro_build (&ep, "lui", "t,u", ATREG, BFD_RELOC_HI16);
+ ep.X_add_number = 0xffff;
+ macro_build (&ep, "ori", "t,r,i", ATREG, ATREG, BFD_RELOC_LO16);
+ macro_build (NULL, "and", "d,v,t", sreg, sreg, ATREG);
+ }
+}
+
+static void
+fix_loongson2f (struct mips_cl_insn * ip)
+{
+ if (mips_fix_loongson2f_nop)
+ fix_loongson2f_nop (ip);
+
+ if (mips_fix_loongson2f_jump)
+ fix_loongson2f_jump (ip);
+}
+
+/* IP is a branch that has a delay slot, and we need to fill it
+ automatically. Return true if we can do that by swapping IP
+ with the previous instruction.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
+
+static bfd_boolean
+can_swap_branch_p (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
+{
+ unsigned long pinfo, pinfo2, prev_pinfo, prev_pinfo2;
+ unsigned int gpr_read, gpr_write, prev_gpr_read, prev_gpr_write;
+ unsigned int fpr_read, prev_fpr_write;
+
+ /* -O2 and above is required for this optimization. */
+ if (mips_optimize < 2)
+ return FALSE;
+
+ /* If we have seen .set volatile or .set nomove, don't optimize. */
+ if (mips_opts.nomove)
+ return FALSE;
+
+ /* We can't swap if the previous instruction's position is fixed. */
+ if (history[0].fixed_p)
+ return FALSE;
+
+ /* If the previous previous insn was in a .set noreorder, we can't
+ swap. Actually, the MIPS assembler will swap in this situation.
+ However, gcc configured -with-gnu-as will generate code like
+
+ .set noreorder
+ lw $4,XXX
+ .set reorder
+ INSN
+ bne $4,$0,foo
+
+ in which we can not swap the bne and INSN. If gcc is not configured
+ -with-gnu-as, it does not output the .set pseudo-ops. */
+ if (history[1].noreorder_p)
+ return FALSE;
+
+ /* If the previous instruction had a fixup in mips16 mode, we can not swap.
+ This means that the previous instruction was a 4-byte one anyhow. */
+ if (mips_opts.mips16 && history[0].fixp[0])
+ return FALSE;
+
+ /* If the branch is itself the target of a branch, we can not swap.
+ We cheat on this; all we check for is whether there is a label on
+ this instruction. If there are any branches to anything other than
+ a label, users must use .set noreorder. */
+ if (seg_info (now_seg)->label_list)
+ return FALSE;
+
+ /* If the previous instruction is in a variant frag other than this
+ branch's one, we cannot do the swap. This does not apply to
+ MIPS16 code, which uses variant frags for different purposes. */
+ if (!mips_opts.mips16
+ && history[0].frag
+ && history[0].frag->fr_type == rs_machine_dependent)
+ return FALSE;
+
+ /* We do not swap with instructions that cannot architecturally
+ be placed in a branch delay slot, such as SYNC or ERET. We
+ also refrain from swapping with a trap instruction, since it
+ complicates trap handlers to have the trap instruction be in
+ a delay slot. */
+ prev_pinfo = history[0].insn_mo->pinfo;
+ if (prev_pinfo & INSN_NO_DELAY_SLOT)
+ return FALSE;
+
+ /* Check for conflicts between the branch and the instructions
+ before the candidate delay slot. */
+ if (nops_for_insn (0, history + 1, ip) > 0)
+ return FALSE;
+
+ /* Check for conflicts between the swapped sequence and the
+ target of the branch. */
+ if (nops_for_sequence (2, 0, history + 1, ip, history) > 0)
+ return FALSE;
+
+ /* If the branch reads a register that the previous
+ instruction sets, we can not swap. */
+ gpr_read = gpr_read_mask (ip);
+ prev_gpr_write = gpr_write_mask (&history[0]);
+ if (gpr_read & prev_gpr_write)
+ return FALSE;
+
+ fpr_read = fpr_read_mask (ip);
+ prev_fpr_write = fpr_write_mask (&history[0]);
+ if (fpr_read & prev_fpr_write)
+ return FALSE;
+
+ /* If the branch writes a register that the previous
+ instruction sets, we can not swap. */
+ gpr_write = gpr_write_mask (ip);
+ if (gpr_write & prev_gpr_write)
+ return FALSE;
+
+ /* If the branch writes a register that the previous
+ instruction reads, we can not swap. */
+ prev_gpr_read = gpr_read_mask (&history[0]);
+ if (gpr_write & prev_gpr_read)
+ return FALSE;
+
+ /* If one instruction sets a condition code and the
+ other one uses a condition code, we can not swap. */
+ pinfo = ip->insn_mo->pinfo;
+ if ((pinfo & INSN_READ_COND_CODE)
+ && (prev_pinfo & INSN_WRITE_COND_CODE))
+ return FALSE;
+ if ((pinfo & INSN_WRITE_COND_CODE)
+ && (prev_pinfo & INSN_READ_COND_CODE))
+ return FALSE;
+
+ /* If the previous instruction uses the PC, we can not swap. */
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
+ if (prev_pinfo2 & INSN2_READ_PC)
+ return FALSE;
+
+ /* If the previous instruction has an incorrect size for a fixed
+ branch delay slot in microMIPS mode, we cannot swap. */
+ pinfo2 = ip->insn_mo->pinfo2;
+ if (mips_opts.micromips
+ && (pinfo2 & INSN2_BRANCH_DELAY_16BIT)
+ && insn_length (history) != 2)
+ return FALSE;
+ if (mips_opts.micromips
+ && (pinfo2 & INSN2_BRANCH_DELAY_32BIT)
+ && insn_length (history) != 4)
+ return FALSE;
+
+ /* On R5900 short loops need to be fixed by inserting a nop in
+ the branch delay slots.
+ A short loop can be terminated too early. */
+ if (mips_opts.arch == CPU_R5900
+ /* Check if instruction has a parameter, ignore "j $31". */
+ && (address_expr != NULL)
+ /* Parameter must be 16 bit. */
+ && (*reloc_type == BFD_RELOC_16_PCREL_S2)
+ /* Branch to same segment. */
+ && (S_GET_SEGMENT(address_expr->X_add_symbol) == now_seg)
+ /* Branch to same code fragment. */
+ && (symbol_get_frag(address_expr->X_add_symbol) == frag_now)
+ /* Can only calculate branch offset if value is known. */
+ && symbol_constant_p(address_expr->X_add_symbol)
+ /* Check if branch is really conditional. */
+ && !((ip->insn_opcode & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (ip->insn_opcode & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ int distance;
+ /* Check if loop is shorter than 6 instructions including
+ branch and delay slot. */
+ distance = frag_now_fix() - S_GET_VALUE(address_expr->X_add_symbol);
+ if (distance <= 20)
+ {
+ int i;
+ int rv;
+
+ rv = FALSE;
+ /* When the loop includes branches or jumps,
+ it is not a short loop. */
+ for (i = 0; i < (distance / 4); i++)
+ {
+ if ((history[i].cleared_p)
+ || delayed_branch_p(&history[i]))
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ if (rv == FALSE)
+ {
+ /* Insert nop after branch to fix short loop. */
+ return FALSE;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+/* Decide how we should add IP to the instruction stream.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. */
+
+static enum append_method
+get_append_method (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type)
+{
+ /* The relaxed version of a macro sequence must be inherently
+ hazard-free. */
+ if (mips_relax.sequence == 2)
+ return APPEND_ADD;
+
+ /* We must not dabble with instructions in a ".set norerorder" block. */
+ if (mips_opts.noreorder)
+ return APPEND_ADD;
+
+ /* Otherwise, it's our responsibility to fill branch delay slots. */
+ if (delayed_branch_p (ip))
+ {
+ if (!branch_likely_p (ip)
+ && can_swap_branch_p (ip, address_expr, reloc_type))
+ return APPEND_SWAP;
+
+ if (mips_opts.mips16
+ && ISA_SUPPORTS_MIPS16E
+ && gpr_read_mask (ip) != 0)
+ return APPEND_ADD_COMPACT;
+
+ return APPEND_ADD_WITH_NOP;
+ }
+
+ return APPEND_ADD;
+}
+
+/* IP is a MIPS16 instruction whose opcode we have just changed.
+ Point IP->insn_mo to the new opcode's definition. */
+
+static void
+find_altered_mips16_opcode (struct mips_cl_insn *ip)
+{
+ const struct mips_opcode *mo, *end;
+
+ end = &mips16_opcodes[bfd_mips16_num_opcodes];
+ for (mo = ip->insn_mo; mo < end; mo++)
+ if ((ip->insn_opcode & mo->mask) == mo->match)
+ {
+ ip->insn_mo = mo;
+ return;
+ }
+ abort ();
+}
+
+/* For microMIPS macros, we need to generate a local number label
+ as the target of branches. */
+#define MICROMIPS_LABEL_CHAR '\037'
+static unsigned long micromips_target_label;
+static char micromips_target_name[32];
+
+static char *
+micromips_label_name (void)
+{
+ char *p = micromips_target_name;
+ char symbol_name_temporary[24];
+ unsigned long l;
+ int i;
+
+ if (*p)
+ return p;
+
+ i = 0;
+ l = micromips_target_label;
+#ifdef LOCAL_LABEL_PREFIX
+ *p++ = LOCAL_LABEL_PREFIX;
+#endif
+ *p++ = 'L';
+ *p++ = MICROMIPS_LABEL_CHAR;
+ do
+ {
+ symbol_name_temporary[i++] = l % 10 + '0';
+ l /= 10;
+ }
+ while (l != 0);
+ while (i > 0)
+ *p++ = symbol_name_temporary[--i];
+ *p = '\0';
+
+ return micromips_target_name;
+}
+
+static void
+micromips_label_expr (expressionS *label_expr)
+{
+ label_expr->X_op = O_symbol;
+ label_expr->X_add_symbol = symbol_find_or_make (micromips_label_name ());
+ label_expr->X_add_number = 0;
+}
+
+static void
+micromips_label_inc (void)
+{
+ micromips_target_label++;
+ *micromips_target_name = '\0';
+}
+
+static void
+micromips_add_label (void)
+{
+ symbolS *s;
+
+ s = colon (micromips_label_name ());
+ micromips_label_inc ();
+ S_SET_OTHER (s, ELF_ST_SET_MICROMIPS (S_GET_OTHER (s)));
+}
+
+/* If assembling microMIPS code, then return the microMIPS reloc
+ corresponding to the requested one if any. Otherwise return
+ the reloc unchanged. */
+
+static bfd_reloc_code_real_type
+micromips_map_reloc (bfd_reloc_code_real_type reloc)
+{
+ static const bfd_reloc_code_real_type relocs[][2] =
+ {
+ /* Keep sorted incrementally by the left-hand key. */
+ { BFD_RELOC_16_PCREL_S2, BFD_RELOC_MICROMIPS_16_PCREL_S1 },
+ { BFD_RELOC_GPREL16, BFD_RELOC_MICROMIPS_GPREL16 },
+ { BFD_RELOC_MIPS_JMP, BFD_RELOC_MICROMIPS_JMP },
+ { BFD_RELOC_HI16, BFD_RELOC_MICROMIPS_HI16 },
+ { BFD_RELOC_HI16_S, BFD_RELOC_MICROMIPS_HI16_S },
+ { BFD_RELOC_LO16, BFD_RELOC_MICROMIPS_LO16 },
+ { BFD_RELOC_MIPS_LITERAL, BFD_RELOC_MICROMIPS_LITERAL },
+ { BFD_RELOC_MIPS_GOT16, BFD_RELOC_MICROMIPS_GOT16 },
+ { BFD_RELOC_MIPS_CALL16, BFD_RELOC_MICROMIPS_CALL16 },
+ { BFD_RELOC_MIPS_GOT_HI16, BFD_RELOC_MICROMIPS_GOT_HI16 },
+ { BFD_RELOC_MIPS_GOT_LO16, BFD_RELOC_MICROMIPS_GOT_LO16 },
+ { BFD_RELOC_MIPS_CALL_HI16, BFD_RELOC_MICROMIPS_CALL_HI16 },
+ { BFD_RELOC_MIPS_CALL_LO16, BFD_RELOC_MICROMIPS_CALL_LO16 },
+ { BFD_RELOC_MIPS_SUB, BFD_RELOC_MICROMIPS_SUB },
+ { BFD_RELOC_MIPS_GOT_PAGE, BFD_RELOC_MICROMIPS_GOT_PAGE },
+ { BFD_RELOC_MIPS_GOT_OFST, BFD_RELOC_MICROMIPS_GOT_OFST },
+ { BFD_RELOC_MIPS_GOT_DISP, BFD_RELOC_MICROMIPS_GOT_DISP },
+ { BFD_RELOC_MIPS_HIGHEST, BFD_RELOC_MICROMIPS_HIGHEST },
+ { BFD_RELOC_MIPS_HIGHER, BFD_RELOC_MICROMIPS_HIGHER },
+ { BFD_RELOC_MIPS_SCN_DISP, BFD_RELOC_MICROMIPS_SCN_DISP },
+ { BFD_RELOC_MIPS_TLS_GD, BFD_RELOC_MICROMIPS_TLS_GD },
+ { BFD_RELOC_MIPS_TLS_LDM, BFD_RELOC_MICROMIPS_TLS_LDM },
+ { BFD_RELOC_MIPS_TLS_DTPREL_HI16, BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_DTPREL_LO16, BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16 },
+ { BFD_RELOC_MIPS_TLS_GOTTPREL, BFD_RELOC_MICROMIPS_TLS_GOTTPREL },
+ { BFD_RELOC_MIPS_TLS_TPREL_HI16, BFD_RELOC_MICROMIPS_TLS_TPREL_HI16 },
+ { BFD_RELOC_MIPS_TLS_TPREL_LO16, BFD_RELOC_MICROMIPS_TLS_TPREL_LO16 }
+ };
+ bfd_reloc_code_real_type r;
+ size_t i;
+
+ if (!mips_opts.micromips)
+ return reloc;
+ for (i = 0; i < ARRAY_SIZE (relocs); i++)
+ {
+ r = relocs[i][0];
+ if (r > reloc)
+ return reloc;
+ if (r == reloc)
+ return relocs[i][1];
+ }
+ return reloc;
+}
+
+/* Try to resolve relocation RELOC against constant OPERAND at assembly time.
+ Return true on success, storing the resolved value in RESULT. */
+
+static bfd_boolean
+calculate_reloc (bfd_reloc_code_real_type reloc, offsetT operand,
+ offsetT *result)
+{
+ switch (reloc)
+ {
+ case BFD_RELOC_MIPS_HIGHEST:
+ case BFD_RELOC_MICROMIPS_HIGHEST:
+ *result = ((operand + 0x800080008000ull) >> 48) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_MIPS_HIGHER:
+ case BFD_RELOC_MICROMIPS_HIGHER:
+ *result = ((operand + 0x80008000ull) >> 32) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MIPS16_HI16_S:
+ *result = ((operand + 0x8000) >> 16) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_MICROMIPS_HI16:
+ case BFD_RELOC_MIPS16_HI16:
+ *result = (operand >> 16) & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_MICROMIPS_LO16:
+ case BFD_RELOC_MIPS16_LO16:
+ *result = operand & 0xffff;
+ return TRUE;
+
+ case BFD_RELOC_UNUSED:
+ *result = operand;
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+/* Output an instruction. IP is the instruction information.
+ ADDRESS_EXPR is an operand of the instruction to be used with
+ RELOC_TYPE. EXPANSIONP is true if the instruction is part of
+ a macro expansion. */
+
+static void
+append_insn (struct mips_cl_insn *ip, expressionS *address_expr,
+ bfd_reloc_code_real_type *reloc_type, bfd_boolean expansionp)
+{
+ unsigned long prev_pinfo2, pinfo;
+ bfd_boolean relaxed_branch = FALSE;
+ enum append_method method;
+ bfd_boolean relax32;
+ int branch_disp;
+
+ if (mips_fix_loongson2f && !HAVE_CODE_COMPRESSION)
+ fix_loongson2f (ip);
+
+ file_ase_mips16 |= mips_opts.mips16;
+ file_ase_micromips |= mips_opts.micromips;
+
+ prev_pinfo2 = history[0].insn_mo->pinfo2;
+ pinfo = ip->insn_mo->pinfo;
+
+ if (mips_opts.micromips
+ && !expansionp
+ && (((prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0
+ && micromips_insn_length (ip->insn_mo) != 2)
+ || ((prev_pinfo2 & INSN2_BRANCH_DELAY_32BIT) != 0
+ && micromips_insn_length (ip->insn_mo) != 4)))
+ as_warn (_("wrong size instruction in a %u-bit branch delay slot"),
+ (prev_pinfo2 & INSN2_BRANCH_DELAY_16BIT) != 0 ? 16 : 32);
+
+ if (address_expr == NULL)
+ ip->complete_p = 1;
+ else if (reloc_type[0] <= BFD_RELOC_UNUSED
+ && reloc_type[1] == BFD_RELOC_UNUSED
+ && reloc_type[2] == BFD_RELOC_UNUSED
+ && address_expr->X_op == O_constant)
+ {
+ switch (*reloc_type)
+ {
+ case BFD_RELOC_MIPS_JMP:
+ {
+ int shift;
+
+ shift = mips_opts.micromips ? 1 : 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x3ffffff);
+ ip->complete_p = 1;
+ }
+ break;
+
+ case BFD_RELOC_MIPS16_JMP:
+ if ((address_expr->X_add_number & 3) != 0)
+ as_bad (_("jump to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |=
+ (((address_expr->X_add_number & 0x7c0000) << 3)
+ | ((address_expr->X_add_number & 0xf800000) >> 7)
+ | ((address_expr->X_add_number & 0x3fffc) >> 2));
+ ip->complete_p = 1;
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ {
+ int shift;
+
+ shift = mips_opts.micromips ? 1 : 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if (!mips_relax_branch)
+ {
+ if ((address_expr->X_add_number + (1 << (shift + 15)))
+ & ~((1 << (shift + 16)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0xffff);
+ }
+ }
+ break;
+
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ {
+ int shift;
+
+ shift = 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if ((address_expr->X_add_number + (1 << (shift + 20)))
+ & ~((1 << (shift + 21)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x1fffff);
+ }
+ break;
+
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ {
+ int shift;
+
+ shift = 2;
+ if ((address_expr->X_add_number & ((1 << shift) - 1)) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ if ((address_expr->X_add_number + (1 << (shift + 25)))
+ & ~((1 << (shift + 26)) - 1))
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) address_expr->X_add_number);
+ ip->insn_opcode |= ((address_expr->X_add_number >> shift)
+ & 0x3ffffff);
+ }
+ break;
+
+ default:
+ {
+ offsetT value;
+
+ if (calculate_reloc (*reloc_type, address_expr->X_add_number,
+ &value))
+ {
+ ip->insn_opcode |= value & 0xffff;
+ ip->complete_p = 1;
+ }
+ }
+ break;
+ }
+ }
+
+ if (mips_relax.sequence != 2 && !mips_opts.noreorder)
+ {
+ /* There are a lot of optimizations we could do that we don't.
+ In particular, we do not, in general, reorder instructions.
+ If you use gcc with optimization, it will reorder
+ instructions and generally do much more optimization then we
+ do here; repeating all that work in the assembler would only
+ benefit hand written assembly code, and does not seem worth
+ it. */
+ int nops = (mips_optimize == 0
+ ? nops_for_insn (0, history, NULL)
+ : nops_for_insn_or_target (0, history, ip));
+ if (nops > 0)
+ {
+ fragS *old_frag;
+ unsigned long old_frag_offset;
+ int i;
+
+ old_frag = frag_now;
+ old_frag_offset = frag_now_fix ();
+
+ for (i = 0; i < nops; i++)
+ add_fixed_insn (NOP_INSN);
+ insert_into_history (0, nops, NOP_INSN);
+
+ if (listing)
+ {
+ listing_prev_line ();
+ /* We may be at the start of a variant frag. In case we
+ are, make sure there is enough space for the frag
+ after the frags created by listing_prev_line. The
+ argument to frag_grow here must be at least as large
+ as the argument to all other calls to frag_grow in
+ this file. We don't have to worry about being in the
+ middle of a variant frag, because the variants insert
+ all needed nop instructions themselves. */
+ frag_grow (40);
+ }
+
+ mips_move_text_labels ();
+
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING)
+ ecoff_fix_loc (old_frag, old_frag_offset);
+#endif
+ }
+ }
+ else if (mips_relax.sequence != 2 && prev_nop_frag != NULL)
+ {
+ int nops;
+
+ /* Work out how many nops in prev_nop_frag are needed by IP,
+ ignoring hazards generated by the first prev_nop_frag_since
+ instructions. */
+ nops = nops_for_insn_or_target (prev_nop_frag_since, history, ip);
+ gas_assert (nops <= prev_nop_frag_holds);
+
+ /* Enforce NOPS as a minimum. */
+ if (nops > prev_nop_frag_required)
+ prev_nop_frag_required = nops;
+
+ if (prev_nop_frag_holds == prev_nop_frag_required)
+ {
+ /* Settle for the current number of nops. Update the history
+ accordingly (for the benefit of any future .set reorder code). */
+ prev_nop_frag = NULL;
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_holds, NOP_INSN);
+ }
+ else
+ {
+ /* Allow this instruction to replace one of the nops that was
+ tentatively added to prev_nop_frag. */
+ prev_nop_frag->fr_fix -= NOP_INSN_SIZE;
+ prev_nop_frag_holds--;
+ prev_nop_frag_since++;
+ }
+ }
+
+ method = get_append_method (ip, address_expr, reloc_type);
+ branch_disp = method == APPEND_SWAP ? insn_length (history) : 0;
+
+ dwarf2_emit_insn (0);
+ /* We want MIPS16 and microMIPS debug info to use ISA-encoded addresses,
+ so "move" the instruction address accordingly.
+
+ Also, it doesn't seem appropriate for the assembler to reorder .loc
+ entries. If this instruction is a branch that we are going to swap
+ with the previous instruction, the two instructions should be
+ treated as a unit, and the debug information for both instructions
+ should refer to the start of the branch sequence. Using the
+ current position is certainly wrong when swapping a 32-bit branch
+ and a 16-bit delay slot, since the current position would then be
+ in the middle of a branch. */
+ dwarf2_move_insn ((HAVE_CODE_COMPRESSION ? 1 : 0) - branch_disp);
+
+ relax32 = (mips_relax_branch
+ /* Don't try branch relaxation within .set nomacro, or within
+ .set noat if we use $at for PIC computations. If it turns
+ out that the branch was out-of-range, we'll get an error. */
+ && !mips_opts.warn_about_macros
+ && (mips_opts.at || mips_pic == NO_PIC)
+ /* Don't relax BPOSGE32/64 or BC1ANY2T/F and BC1ANY4T/F
+ as they have no complementing branches. */
+ && !(ip->insn_mo->ase & (ASE_MIPS3D | ASE_DSP64 | ASE_DSP)));
+
+ if (!HAVE_CODE_COMPRESSION
+ && address_expr
+ && relax32
+ && *reloc_type == BFD_RELOC_16_PCREL_S2
+ && delayed_branch_p (ip))
+ {
+ relaxed_branch = TRUE;
+ add_relaxed_insn (ip, (relaxed_branch_length
+ (NULL, NULL,
+ uncond_branch_p (ip) ? -1
+ : branch_likely_p (ip) ? 1
+ : 0)), 4,
+ RELAX_BRANCH_ENCODE
+ (AT,
+ uncond_branch_p (ip),
+ branch_likely_p (ip),
+ pinfo & INSN_WRITE_GPR_31,
+ 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
+ }
+ else if (mips_opts.micromips
+ && address_expr
+ && ((relax32 && *reloc_type == BFD_RELOC_16_PCREL_S2)
+ || *reloc_type > BFD_RELOC_UNUSED)
+ && (delayed_branch_p (ip) || compact_branch_p (ip))
+ /* Don't try branch relaxation when users specify
+ 16-bit/32-bit instructions. */
+ && !forced_insn_length)
+ {
+ bfd_boolean relax16 = *reloc_type > BFD_RELOC_UNUSED;
+ int type = relax16 ? *reloc_type - BFD_RELOC_UNUSED : 0;
+ int uncond = uncond_branch_p (ip) ? -1 : 0;
+ int compact = compact_branch_p (ip);
+ int al = pinfo & INSN_WRITE_GPR_31;
+ int length32;
+
+ gas_assert (address_expr != NULL);
+ gas_assert (!mips_relax.sequence);
+
+ relaxed_branch = TRUE;
+ length32 = relaxed_micromips_32bit_branch_length (NULL, NULL, uncond);
+ add_relaxed_insn (ip, relax32 ? length32 : 4, relax16 ? 2 : 4,
+ RELAX_MICROMIPS_ENCODE (type, AT, uncond, compact, al,
+ relax32, 0, 0),
+ address_expr->X_add_symbol,
+ address_expr->X_add_number);
+ *reloc_type = BFD_RELOC_UNUSED;
+ }
+ else if (mips_opts.mips16 && *reloc_type > BFD_RELOC_UNUSED)
+ {
+ /* We need to set up a variant frag. */
+ gas_assert (address_expr != NULL);
+ add_relaxed_insn (ip, 4, 0,
+ RELAX_MIPS16_ENCODE
+ (*reloc_type - BFD_RELOC_UNUSED,
+ forced_insn_length == 2, forced_insn_length == 4,
+ delayed_branch_p (&history[0]),
+ history[0].mips16_absolute_jump_p),
+ make_expr_symbol (address_expr), 0);
+ }
+ else if (mips_opts.mips16 && insn_length (ip) == 2)
+ {
+ if (!delayed_branch_p (ip))
+ /* Make sure there is enough room to swap this instruction with
+ a following jump instruction. */
+ frag_grow (6);
+ add_fixed_insn (ip);
+ }
+ else
+ {
+ if (mips_opts.mips16
+ && mips_opts.noreorder
+ && delayed_branch_p (&history[0]))
+ as_warn (_("extended instruction in delay slot"));
+
+ if (mips_relax.sequence)
+ {
+ /* If we've reached the end of this frag, turn it into a variant
+ frag and record the information for the instructions we've
+ written so far. */
+ if (frag_room () < 4)
+ relax_close_frag ();
+ mips_relax.sizes[mips_relax.sequence - 1] += insn_length (ip);
+ }
+
+ if (mips_relax.sequence != 2)
+ {
+ if (mips_macro_warning.first_insn_sizes[0] == 0)
+ mips_macro_warning.first_insn_sizes[0] = insn_length (ip);
+ mips_macro_warning.sizes[0] += insn_length (ip);
+ mips_macro_warning.insns[0]++;
+ }
+ if (mips_relax.sequence != 1)
+ {
+ if (mips_macro_warning.first_insn_sizes[1] == 0)
+ mips_macro_warning.first_insn_sizes[1] = insn_length (ip);
+ mips_macro_warning.sizes[1] += insn_length (ip);
+ mips_macro_warning.insns[1]++;
+ }
+
+ if (mips_opts.mips16)
+ {
+ ip->fixed_p = 1;
+ ip->mips16_absolute_jump_p = (*reloc_type == BFD_RELOC_MIPS16_JMP);
+ }
+ add_fixed_insn (ip);
+ }
+
+ if (!ip->complete_p && *reloc_type < BFD_RELOC_UNUSED)
+ {
+ bfd_reloc_code_real_type final_type[3];
+ reloc_howto_type *howto0;
+ reloc_howto_type *howto;
+ int i;
+
+ /* Perform any necessary conversion to microMIPS relocations
+ and find out how many relocations there actually are. */
+ for (i = 0; i < 3 && reloc_type[i] != BFD_RELOC_UNUSED; i++)
+ final_type[i] = micromips_map_reloc (reloc_type[i]);
+
+ /* In a compound relocation, it is the final (outermost)
+ operator that determines the relocated field. */
+ howto = howto0 = bfd_reloc_type_lookup (stdoutput, final_type[i - 1]);
+ if (!howto)
+ abort ();
+
+ if (i > 1)
+ howto0 = bfd_reloc_type_lookup (stdoutput, final_type[0]);
+ ip->fixp[0] = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ address_expr,
+ howto0 && howto0->pc_relative,
+ final_type[0]);
+
+ /* Tag symbols that have a R_MIPS16_26 relocation against them. */
+ if (final_type[0] == BFD_RELOC_MIPS16_JMP && ip->fixp[0]->fx_addsy)
+ *symbol_get_tc (ip->fixp[0]->fx_addsy) = 1;
+
+ /* These relocations can have an addend that won't fit in
+ 4 octets for 64bit assembly. */
+ if (GPR_SIZE == 64
+ && ! howto->partial_inplace
+ && (reloc_type[0] == BFD_RELOC_16
+ || reloc_type[0] == BFD_RELOC_32
+ || reloc_type[0] == BFD_RELOC_MIPS_JMP
+ || reloc_type[0] == BFD_RELOC_GPREL16
+ || reloc_type[0] == BFD_RELOC_MIPS_LITERAL
+ || reloc_type[0] == BFD_RELOC_GPREL32
+ || reloc_type[0] == BFD_RELOC_64
+ || reloc_type[0] == BFD_RELOC_CTOR
+ || reloc_type[0] == BFD_RELOC_MIPS_SUB
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHEST
+ || reloc_type[0] == BFD_RELOC_MIPS_HIGHER
+ || reloc_type[0] == BFD_RELOC_MIPS_SCN_DISP
+ || reloc_type[0] == BFD_RELOC_MIPS_REL16
+ || reloc_type[0] == BFD_RELOC_MIPS_RELGOT
+ || reloc_type[0] == BFD_RELOC_MIPS16_GPREL
+ || hi16_reloc_p (reloc_type[0])
+ || lo16_reloc_p (reloc_type[0])))
+ ip->fixp[0]->fx_no_overflow = 1;
+
+ /* These relocations can have an addend that won't fit in 2 octets. */
+ if (reloc_type[0] == BFD_RELOC_MICROMIPS_7_PCREL_S1
+ || reloc_type[0] == BFD_RELOC_MICROMIPS_10_PCREL_S1)
+ ip->fixp[0]->fx_no_overflow = 1;
+
+ if (mips_relax.sequence)
+ {
+ if (mips_relax.first_fixup == 0)
+ mips_relax.first_fixup = ip->fixp[0];
+ }
+ else if (reloc_needs_lo_p (*reloc_type))
+ {
+ struct mips_hi_fixup *hi_fixup;
+
+ /* Reuse the last entry if it already has a matching %lo. */
+ hi_fixup = mips_hi_fixup_list;
+ if (hi_fixup == 0
+ || !fixup_has_matching_lo_p (hi_fixup->fixp))
+ {
+ hi_fixup = ((struct mips_hi_fixup *)
+ xmalloc (sizeof (struct mips_hi_fixup)));
+ hi_fixup->next = mips_hi_fixup_list;
+ mips_hi_fixup_list = hi_fixup;
+ }
+ hi_fixup->fixp = ip->fixp[0];
+ hi_fixup->seg = now_seg;
+ }
+
+ /* Add fixups for the second and third relocations, if given.
+ Note that the ABI allows the second relocation to be
+ against RSS_UNDEF, RSS_GP, RSS_GP0 or RSS_LOC. At the
+ moment we only use RSS_UNDEF, but we could add support
+ for the others if it ever becomes necessary. */
+ for (i = 1; i < 3; i++)
+ if (reloc_type[i] != BFD_RELOC_UNUSED)
+ {
+ ip->fixp[i] = fix_new (ip->frag, ip->where,
+ ip->fixp[0]->fx_size, NULL, 0,
+ FALSE, final_type[i]);
+
+ /* Use fx_tcbit to mark compound relocs. */
+ ip->fixp[0]->fx_tcbit = 1;
+ ip->fixp[i]->fx_tcbit = 1;
+ }
+ }
+ install_insn (ip);
+
+ /* Update the register mask information. */
+ mips_gprmask |= gpr_read_mask (ip) | gpr_write_mask (ip);
+ mips_cprmask[1] |= fpr_read_mask (ip) | fpr_write_mask (ip);
+
+ switch (method)
+ {
+ case APPEND_ADD:
+ insert_into_history (0, 1, ip);
+ break;
+
+ case APPEND_ADD_WITH_NOP:
+ {
+ struct mips_cl_insn *nop;
+
+ insert_into_history (0, 1, ip);
+ nop = get_delay_slot_nop (ip);
+ add_fixed_insn (nop);
+ insert_into_history (0, 1, nop);
+ if (mips_relax.sequence)
+ mips_relax.sizes[mips_relax.sequence - 1] += insn_length (nop);
+ }
+ break;
+
+ case APPEND_ADD_COMPACT:
+ /* Convert MIPS16 jr/jalr into a "compact" jump. */
+ gas_assert (mips_opts.mips16);
+ ip->insn_opcode |= 0x0080;
+ find_altered_mips16_opcode (ip);
+ install_insn (ip);
+ insert_into_history (0, 1, ip);
+ break;
+
+ case APPEND_SWAP:
+ {
+ struct mips_cl_insn delay = history[0];
+ if (mips_opts.mips16)
+ {
+ know (delay.frag == ip->frag);
+ move_insn (ip, delay.frag, delay.where);
+ move_insn (&delay, ip->frag, ip->where + insn_length (ip));
+ }
+ else if (relaxed_branch || delay.frag != ip->frag)
+ {
+ /* Add the delay slot instruction to the end of the
+ current frag and shrink the fixed part of the
+ original frag. If the branch occupies the tail of
+ the latter, move it backwards to cover the gap. */
+ delay.frag->fr_fix -= branch_disp;
+ if (delay.frag == ip->frag)
+ move_insn (ip, ip->frag, ip->where - branch_disp);
+ add_fixed_insn (&delay);
+ }
+ else
+ {
+ move_insn (&delay, ip->frag,
+ ip->where - branch_disp + insn_length (ip));
+ move_insn (ip, history[0].frag, history[0].where);
+ }
+ history[0] = *ip;
+ delay.fixed_p = 1;
+ insert_into_history (0, 1, &delay);
+ }
+ break;
+ }
+
+ /* If we have just completed an unconditional branch, clear the history. */
+ if ((delayed_branch_p (&history[1]) && uncond_branch_p (&history[1]))
+ || (compact_branch_p (&history[0]) && uncond_branch_p (&history[0])))
+ {
+ unsigned int i;
+
+ mips_no_prev_insn ();
+
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].cleared_p = 1;
+ }
+
+ /* We need to emit a label at the end of branch-likely macros. */
+ if (emit_branch_likely_macro)
+ {
+ emit_branch_likely_macro = FALSE;
+ micromips_add_label ();
+ }
+
+ /* We just output an insn, so the next one doesn't have a label. */
+ mips_clear_insn_labels ();
+}
+
+/* Forget that there was any previous instruction or label.
+ When BRANCH is true, the branch history is also flushed. */
+
+static void
+mips_no_prev_insn (void)
+{
+ prev_nop_frag = NULL;
+ insert_into_history (0, ARRAY_SIZE (history), NOP_INSN);
+ mips_clear_insn_labels ();
+}
+
+/* This function must be called before we emit something other than
+ instructions. It is like mips_no_prev_insn except that it inserts
+ any NOPS that might be needed by previous instructions. */
+
+void
+mips_emit_delays (void)
+{
+ if (! mips_opts.noreorder)
+ {
+ int nops = nops_for_insn (0, history, NULL);
+ if (nops > 0)
+ {
+ while (nops-- > 0)
+ add_fixed_insn (NOP_INSN);
+ mips_move_text_labels ();
+ }
+ }
+ mips_no_prev_insn ();
+}
+
+/* Start a (possibly nested) noreorder block. */
+
+static void
+start_noreorder (void)
+{
+ if (mips_opts.noreorder == 0)
+ {
+ unsigned int i;
+ int nops;
+
+ /* None of the instructions before the .set noreorder can be moved. */
+ for (i = 0; i < ARRAY_SIZE (history); i++)
+ history[i].fixed_p = 1;
+
+ /* Insert any nops that might be needed between the .set noreorder
+ block and the previous instructions. We will later remove any
+ nops that turn out not to be needed. */
+ nops = nops_for_insn (0, history, NULL);
+ if (nops > 0)
+ {
+ if (mips_optimize != 0)
+ {
+ /* Record the frag which holds the nop instructions, so
+ that we can remove them if we don't need them. */
+ frag_grow (nops * NOP_INSN_SIZE);
+ prev_nop_frag = frag_now;
+ prev_nop_frag_holds = nops;
+ prev_nop_frag_required = 0;
+ prev_nop_frag_since = 0;
+ }
+
+ for (; nops > 0; --nops)
+ add_fixed_insn (NOP_INSN);
+
+ /* Move on to a new frag, so that it is safe to simply
+ decrease the size of prev_nop_frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ mips_move_text_labels ();
+ }
+ mips_mark_labels ();
+ mips_clear_insn_labels ();
+ }
+ mips_opts.noreorder++;
+ mips_any_noreorder = 1;
+}
+
+/* End a nested noreorder block. */
+
+static void
+end_noreorder (void)
+{
+ mips_opts.noreorder--;
+ if (mips_opts.noreorder == 0 && prev_nop_frag != NULL)
+ {
+ /* Commit to inserting prev_nop_frag_required nops and go back to
+ handling nop insertion the .set reorder way. */
+ prev_nop_frag->fr_fix -= ((prev_nop_frag_holds - prev_nop_frag_required)
+ * NOP_INSN_SIZE);
+ insert_into_history (prev_nop_frag_since,
+ prev_nop_frag_required, NOP_INSN);
+ prev_nop_frag = NULL;
+ }
+}
+
+/* Sign-extend 32-bit mode constants that have bit 31 set and all
+ higher bits unset. */
+
+static void
+normalize_constant_expr (expressionS *ex)
+{
+ if (ex->X_op == O_constant
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
+/* Sign-extend 32-bit mode address offsets that have bit 31 set and
+ all higher bits unset. */
+
+static void
+normalize_address_expr (expressionS *ex)
+{
+ if (((ex->X_op == O_constant && HAVE_32BIT_ADDRESSES)
+ || (ex->X_op == O_symbol && HAVE_32BIT_SYMBOLS))
+ && IS_ZEXT_32BIT_NUM (ex->X_add_number))
+ ex->X_add_number = (((ex->X_add_number & 0xffffffff) ^ 0x80000000)
+ - 0x80000000);
+}
+
+/* Try to match TOKENS against OPCODE, storing the result in INSN.
+ Return true if the match was successful.
+
+ OPCODE_EXTRA is a value that should be ORed into the opcode
+ (used for VU0 channel suffixes, etc.). MORE_ALTS is true if
+ there are more alternatives after OPCODE and SOFT_MATCH is
+ as for mips_arg_info. */
+
+static bfd_boolean
+match_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
+ struct mips_operand_token *tokens, unsigned int opcode_extra,
+ bfd_boolean lax_match, bfd_boolean complete_p)
+{
+ const char *args;
+ struct mips_arg_info arg;
+ const struct mips_operand *operand;
+ char c;
+
+ imm_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+
+ create_insn (insn, opcode);
+ /* When no opcode suffix is specified, assume ".xyzw". */
+ if ((opcode->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0 && opcode_extra == 0)
+ insn->insn_opcode |= 0xf << mips_vu0_channel_mask.lsb;
+ else
+ insn->insn_opcode |= opcode_extra;
+ memset (&arg, 0, sizeof (arg));
+ arg.insn = insn;
+ arg.token = tokens;
+ arg.argnum = 1;
+ arg.last_regno = ILLEGAL_REG;
+ arg.dest_regno = ILLEGAL_REG;
+ arg.lax_match = lax_match;
+ for (args = opcode->args;; ++args)
+ {
+ if (arg.token->type == OT_END)
+ {
+ /* Handle unary instructions in which only one operand is given.
+ The source is then the same as the destination. */
+ if (arg.opnum == 1 && *args == ',')
+ {
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (args + 1)
+ : decode_mips_operand (args + 1));
+ if (operand && mips_optional_operand_p (operand))
+ {
+ arg.token = tokens;
+ arg.argnum = 1;
+ continue;
+ }
+ }
+
+ /* Treat elided base registers as $0. */
+ if (strcmp (args, "(b)") == 0)
+ args += 3;
+
+ if (args[0] == '+')
+ switch (args[1])
+ {
+ case 'K':
+ case 'N':
+ /* The register suffix is optional. */
+ args += 2;
+ break;
+ }
+
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ return FALSE;
+
+ /* Successful match. */
+ if (!complete_p)
+ return TRUE;
+ clear_insn_error ();
+ if (arg.dest_regno == arg.last_regno
+ && strncmp (insn->insn_mo->name, "jalr", 4) == 0)
+ {
+ if (arg.opnum == 2)
+ set_insn_error
+ (0, _("source and destination must be different"));
+ else if (arg.last_regno == 31)
+ set_insn_error
+ (0, _("a destination register must be supplied"));
+ }
+ else if (arg.last_regno == 31
+ && (strncmp (insn->insn_mo->name, "bltzal", 6) == 0
+ || strncmp (insn->insn_mo->name, "bgezal", 6) == 0))
+ set_insn_error (0, _("the source register must not be $31"));
+ check_completed_insn (&arg);
+ return TRUE;
+ }
+
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ return FALSE;
+
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
+ {
+ if (match_char (&arg, *args))
+ continue;
+ return FALSE;
+ }
+ if (*args == '#')
+ {
+ ++args;
+ if (arg.token->type == OT_DOUBLE_CHAR
+ && arg.token->u.ch == *args)
+ {
+ ++arg.token;
+ continue;
+ }
+ return FALSE;
+ }
+
+ /* Handle special macro operands. Work out the properties of
+ other operands. */
+ arg.opnum += 1;
+ switch (*args)
+ {
+ case '-':
+ switch (args[1])
+ {
+ case 'A':
+ *offset_reloc = BFD_RELOC_MIPS_19_PCREL_S2;
+ break;
+
+ case 'B':
+ *offset_reloc = BFD_RELOC_MIPS_18_PCREL_S3;
+ break;
+ }
+ break;
+
+ case '+':
+ switch (args[1])
+ {
+ case 'i':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
+
+ case '\'':
+ *offset_reloc = BFD_RELOC_MIPS_26_PCREL_S2;
+ break;
+
+ case '\"':
+ *offset_reloc = BFD_RELOC_MIPS_21_PCREL_S2;
+ break;
+ }
+ break;
+
+ case 'I':
+ if (!match_const_int (&arg, &imm_expr.X_add_number))
+ return FALSE;
+ imm_expr.X_op = O_constant;
+ if (GPR_SIZE == 32)
+ normalize_constant_expr (&imm_expr);
+ continue;
+
+ case 'A':
+ if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+ {
+ /* Assume that the offset has been elided and that what
+ we saw was a base register. The match will fail later
+ if that assumption turns out to be wrong. */
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else
+ {
+ if (!match_expression (&arg, &offset_expr, offset_reloc))
+ return FALSE;
+ normalize_address_expr (&offset_expr);
+ }
+ continue;
+
+ case 'F':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, TRUE))
+ return FALSE;
+ continue;
+
+ case 'L':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 8, FALSE))
+ return FALSE;
+ continue;
+
+ case 'f':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, TRUE))
+ return FALSE;
+ continue;
+
+ case 'l':
+ if (!match_float_constant (&arg, &imm_expr, &offset_expr,
+ 4, FALSE))
+ return FALSE;
+ continue;
+
+ case 'p':
+ *offset_reloc = BFD_RELOC_16_PCREL_S2;
+ break;
+
+ case 'a':
+ *offset_reloc = BFD_RELOC_MIPS_JMP;
+ break;
+
+ case 'm':
+ gas_assert (mips_opts.micromips);
+ c = args[1];
+ switch (c)
+ {
+ case 'D':
+ case 'E':
+ if (!forced_insn_length)
+ *offset_reloc = (int) BFD_RELOC_UNUSED + c;
+ else if (c == 'D')
+ *offset_reloc = BFD_RELOC_MICROMIPS_10_PCREL_S1;
+ else
+ *offset_reloc = BFD_RELOC_MICROMIPS_7_PCREL_S1;
+ break;
+ }
+ break;
+ }
+
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (args)
+ : decode_mips_operand (args));
+ if (!operand)
+ abort ();
+
+ /* Skip prefixes. */
+ if (*args == '+' || *args == 'm' || *args == '-')
+ args++;
+
+ if (mips_optional_operand_p (operand)
+ && args[1] == ','
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
+ {
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
+ }
+
+ if (!match_operand (&arg, operand))
+ return FALSE;
+ }
+}
+
+/* Like match_insn, but for MIPS16. */
+
+static bfd_boolean
+match_mips16_insn (struct mips_cl_insn *insn, const struct mips_opcode *opcode,
+ struct mips_operand_token *tokens)
+{
+ const char *args;
+ const struct mips_operand *operand;
+ const struct mips_operand *ext_operand;
+ struct mips_arg_info arg;
+ int relax_char;
+
+ create_insn (insn, opcode);
+ imm_expr.X_op = O_absent;
+ offset_expr.X_op = O_absent;
+ offset_reloc[0] = BFD_RELOC_UNUSED;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ relax_char = 0;
+
+ memset (&arg, 0, sizeof (arg));
+ arg.insn = insn;
+ arg.token = tokens;
+ arg.argnum = 1;
+ arg.last_regno = ILLEGAL_REG;
+ arg.dest_regno = ILLEGAL_REG;
+ relax_char = 0;
+ for (args = opcode->args;; ++args)
+ {
+ int c;
+
+ if (arg.token->type == OT_END)
+ {
+ offsetT value;
+
+ /* Handle unary instructions in which only one operand is given.
+ The source is then the same as the destination. */
+ if (arg.opnum == 1 && *args == ',')
+ {
+ operand = decode_mips16_operand (args[1], FALSE);
+ if (operand && mips_optional_operand_p (operand))
+ {
+ arg.token = tokens;
+ arg.argnum = 1;
+ continue;
+ }
+ }
+
+ /* Fail the match if there were too few operands. */
+ if (*args)
+ return FALSE;
+
+ /* Successful match. Stuff the immediate value in now, if
+ we can. */
+ clear_insn_error ();
+ if (opcode->pinfo == INSN_MACRO)
+ {
+ gas_assert (relax_char == 0 || relax_char == 'p');
+ gas_assert (*offset_reloc == BFD_RELOC_UNUSED);
+ }
+ else if (relax_char
+ && offset_expr.X_op == O_constant
+ && calculate_reloc (*offset_reloc,
+ offset_expr.X_add_number,
+ &value))
+ {
+ mips16_immed (NULL, 0, relax_char, *offset_reloc, value,
+ forced_insn_length, &insn->insn_opcode);
+ offset_expr.X_op = O_absent;
+ *offset_reloc = BFD_RELOC_UNUSED;
+ }
+ else if (relax_char && *offset_reloc != BFD_RELOC_UNUSED)
+ {
+ if (forced_insn_length == 2)
+ set_insn_error (0, _("invalid unextended operand value"));
+ forced_insn_length = 4;
+ insn->insn_opcode |= MIPS16_EXTEND;
+ }
+ else if (relax_char)
+ *offset_reloc = (int) BFD_RELOC_UNUSED + relax_char;
+
+ check_completed_insn (&arg);
+ return TRUE;
+ }
+
+ /* Fail the match if the line has too many operands. */
+ if (*args == 0)
+ return FALSE;
+
+ /* Handle characters that need to match exactly. */
+ if (*args == '(' || *args == ')' || *args == ',')
+ {
+ if (match_char (&arg, *args))
+ continue;
+ return FALSE;
+ }
+
+ arg.opnum += 1;
+ c = *args;
+ switch (c)
+ {
+ case 'p':
+ case 'q':
+ case 'A':
+ case 'B':
+ case 'E':
+ relax_char = c;
+ break;
+
+ case 'I':
+ if (!match_const_int (&arg, &imm_expr.X_add_number))
+ return FALSE;
+ imm_expr.X_op = O_constant;
+ if (GPR_SIZE == 32)
+ normalize_constant_expr (&imm_expr);
+ continue;
+
+ case 'a':
+ case 'i':
+ *offset_reloc = BFD_RELOC_MIPS16_JMP;
+ insn->insn_opcode <<= 16;
+ break;
+ }
+
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
+
+ /* '6' is a special case. It is used for BREAK and SDBBP,
+ whose operands are only meaningful to the software that decodes
+ them. This means that there is no architectural reason why
+ they cannot be prefixed by EXTEND, but in practice,
+ exception handlers will only look at the instruction
+ itself. We therefore allow '6' to be extended when
+ disassembling but not when assembling. */
+ if (operand->type != OP_PCREL && c != '6')
+ {
+ ext_operand = decode_mips16_operand (c, TRUE);
+ if (operand != ext_operand)
+ {
+ if (arg.token->type == OT_CHAR && arg.token->u.ch == '(')
+ {
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ relax_char = c;
+ continue;
+ }
+
+ /* We need the OT_INTEGER check because some MIPS16
+ immediate variants are listed before the register ones. */
+ if (arg.token->type != OT_INTEGER
+ || !match_expression (&arg, &offset_expr, offset_reloc))
+ return FALSE;
+
+ /* '8' is used for SLTI(U) and has traditionally not
+ been allowed to take relocation operators. */
+ if (offset_reloc[0] != BFD_RELOC_UNUSED
+ && (ext_operand->size != 16 || c == '8'))
+ return FALSE;
+
+ relax_char = c;
+ continue;
+ }
+ }
+
+ if (mips_optional_operand_p (operand)
+ && args[1] == ','
+ && (arg.token[0].type != OT_REG
+ || arg.token[1].type == OT_END))
+ {
+ /* Assume that the register has been elided and is the
+ same as the first operand. */
+ arg.token = tokens;
+ arg.argnum = 1;
+ }
+
+ if (!match_operand (&arg, operand))
+ return FALSE;
+ }
+}
+
+/* Record that the current instruction is invalid for the current ISA. */
+
+static void
+match_invalid_for_isa (void)
+{
+ set_insn_error_ss
+ (0, _("opcode not supported on this processor: %s (%s)"),
+ mips_cpu_info_from_arch (mips_opts.arch)->name,
+ mips_cpu_info_from_isa (mips_opts.isa)->name);
+}
+
+/* Try to match TOKENS against a series of opcode entries, starting at FIRST.
+ Return true if a definite match or failure was found, storing any match
+ in INSN. OPCODE_EXTRA is a value that should be ORed into the opcode
+ (to handle things like VU0 suffixes). LAX_MATCH is true if we have already
+ tried and failed to match under normal conditions and now want to try a
+ more relaxed match. */
+
+static bfd_boolean
+match_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+ const struct mips_opcode *past, struct mips_operand_token *tokens,
+ int opcode_extra, bfd_boolean lax_match)
+{
+ const struct mips_opcode *opcode;
+ const struct mips_opcode *invalid_delay_slot;
+ bfd_boolean seen_valid_for_isa, seen_valid_for_size;
+
+ /* Search for a match, ignoring alternatives that don't satisfy the
+ current ISA or forced_length. */
+ invalid_delay_slot = 0;
+ seen_valid_for_isa = FALSE;
+ seen_valid_for_size = FALSE;
+ opcode = first;
+ do
+ {
+ gas_assert (strcmp (opcode->name, first->name) == 0);
+ if (is_opcode_valid (opcode))
+ {
+ seen_valid_for_isa = TRUE;
+ if (is_size_valid (opcode))
+ {
+ bfd_boolean delay_slot_ok;
+
+ seen_valid_for_size = TRUE;
+ delay_slot_ok = is_delay_slot_valid (opcode);
+ if (match_insn (insn, opcode, tokens, opcode_extra,
+ lax_match, delay_slot_ok))
+ {
+ if (!delay_slot_ok)
+ {
+ if (!invalid_delay_slot)
+ invalid_delay_slot = opcode;
+ }
+ else
+ return TRUE;
+ }
+ }
+ }
+ ++opcode;
+ }
+ while (opcode < past && strcmp (opcode->name, first->name) == 0);
+
+ /* If the only matches we found had the wrong length for the delay slot,
+ pick the first such match. We'll issue an appropriate warning later. */
+ if (invalid_delay_slot)
+ {
+ if (match_insn (insn, invalid_delay_slot, tokens, opcode_extra,
+ lax_match, TRUE))
+ return TRUE;
+ abort ();
+ }
+
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were incompatible with the current ISA. */
+ if (!seen_valid_for_isa)
+ {
+ match_invalid_for_isa ();
+ return TRUE;
+ }
+
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were of the wrong size. */
+ if (!seen_valid_for_size)
+ {
+ if (mips_opts.insn32)
+ set_insn_error (0, _("opcode not supported in the `insn32' mode"));
+ else
+ set_insn_error_i
+ (0, _("unrecognized %d-bit version of microMIPS opcode"),
+ 8 * forced_insn_length);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Like match_insns, but for MIPS16. */
+
+static bfd_boolean
+match_mips16_insns (struct mips_cl_insn *insn, const struct mips_opcode *first,
+ struct mips_operand_token *tokens)
+{
+ const struct mips_opcode *opcode;
+ bfd_boolean seen_valid_for_isa;
+
+ /* Search for a match, ignoring alternatives that don't satisfy the
+ current ISA. There are no separate entries for extended forms so
+ we deal with forced_length later. */
+ seen_valid_for_isa = FALSE;
+ opcode = first;
+ do
+ {
+ gas_assert (strcmp (opcode->name, first->name) == 0);
+ if (is_opcode_valid_16 (opcode))
+ {
+ seen_valid_for_isa = TRUE;
+ if (match_mips16_insn (insn, opcode, tokens))
+ return TRUE;
+ }
+ ++opcode;
+ }
+ while (opcode < &mips16_opcodes[bfd_mips16_num_opcodes]
+ && strcmp (opcode->name, first->name) == 0);
+
+ /* Handle the case where we didn't try to match an instruction because
+ all the alternatives were incompatible with the current ISA. */
+ if (!seen_valid_for_isa)
+ {
+ match_invalid_for_isa ();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/* Set up global variables for the start of a new macro. */
+
+static void
+macro_start (void)
+{
+ memset (&mips_macro_warning.sizes, 0, sizeof (mips_macro_warning.sizes));
+ memset (&mips_macro_warning.first_insn_sizes, 0,
+ sizeof (mips_macro_warning.first_insn_sizes));
+ memset (&mips_macro_warning.insns, 0, sizeof (mips_macro_warning.insns));
+ mips_macro_warning.delay_slot_p = (mips_opts.noreorder
+ && delayed_branch_p (&history[0]));
+ switch (history[0].insn_mo->pinfo2
+ & (INSN2_BRANCH_DELAY_32BIT | INSN2_BRANCH_DELAY_16BIT))
+ {
+ case INSN2_BRANCH_DELAY_32BIT:
+ mips_macro_warning.delay_slot_length = 4;
+ break;
+ case INSN2_BRANCH_DELAY_16BIT:
+ mips_macro_warning.delay_slot_length = 2;
+ break;
+ default:
+ mips_macro_warning.delay_slot_length = 0;
+ break;
+ }
+ mips_macro_warning.first_frag = NULL;
+}
+
+/* Given that a macro is longer than one instruction or of the wrong size,
+ return the appropriate warning for it. Return null if no warning is
+ needed. SUBTYPE is a bitmask of RELAX_DELAY_SLOT, RELAX_DELAY_SLOT_16BIT,
+ RELAX_DELAY_SLOT_SIZE_FIRST, RELAX_DELAY_SLOT_SIZE_SECOND,
+ and RELAX_NOMACRO. */
+
+static const char *
+macro_warning (relax_substateT subtype)
+{
+ if (subtype & RELAX_DELAY_SLOT)
+ return _("macro instruction expanded into multiple instructions"
+ " in a branch delay slot");
+ else if (subtype & RELAX_NOMACRO)
+ return _("macro instruction expanded into multiple instructions");
+ else if (subtype & (RELAX_DELAY_SLOT_SIZE_FIRST
+ | RELAX_DELAY_SLOT_SIZE_SECOND))
+ return ((subtype & RELAX_DELAY_SLOT_16BIT)
+ ? _("macro instruction expanded into a wrong size instruction"
+ " in a 16-bit branch delay slot")
+ : _("macro instruction expanded into a wrong size instruction"
+ " in a 32-bit branch delay slot"));
+ else
+ return 0;
+}
+
+/* Finish up a macro. Emit warnings as appropriate. */
+
+static void
+macro_end (void)
+{
+ /* Relaxation warning flags. */
+ relax_substateT subtype = 0;
+
+ /* Check delay slot size requirements. */
+ if (mips_macro_warning.delay_slot_length == 2)
+ subtype |= RELAX_DELAY_SLOT_16BIT;
+ if (mips_macro_warning.delay_slot_length != 0)
+ {
+ if (mips_macro_warning.delay_slot_length
+ != mips_macro_warning.first_insn_sizes[0])
+ subtype |= RELAX_DELAY_SLOT_SIZE_FIRST;
+ if (mips_macro_warning.delay_slot_length
+ != mips_macro_warning.first_insn_sizes[1])
+ subtype |= RELAX_DELAY_SLOT_SIZE_SECOND;
+ }
+
+ /* Check instruction count requirements. */
+ if (mips_macro_warning.insns[0] > 1 || mips_macro_warning.insns[1] > 1)
+ {
+ if (mips_macro_warning.insns[1] > mips_macro_warning.insns[0])
+ subtype |= RELAX_SECOND_LONGER;
+ if (mips_opts.warn_about_macros)
+ subtype |= RELAX_NOMACRO;
+ if (mips_macro_warning.delay_slot_p)
+ subtype |= RELAX_DELAY_SLOT;
+ }
+
+ /* If both alternatives fail to fill a delay slot correctly,
+ emit the warning now. */
+ if ((subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0
+ && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0)
+ {
+ relax_substateT s;
+ const char *msg;
+
+ s = subtype & (RELAX_DELAY_SLOT_16BIT
+ | RELAX_DELAY_SLOT_SIZE_FIRST
+ | RELAX_DELAY_SLOT_SIZE_SECOND);
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn ("%s", msg);
+ subtype &= ~s;
+ }
+
+ /* If both implementations are longer than 1 instruction, then emit the
+ warning now. */
+ if (mips_macro_warning.insns[0] > 1 && mips_macro_warning.insns[1] > 1)
+ {
+ relax_substateT s;
+ const char *msg;
+
+ s = subtype & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT);
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn ("%s", msg);
+ subtype &= ~s;
+ }
+
+ /* If any flags still set, then one implementation might need a warning
+ and the other either will need one of a different kind or none at all.
+ Pass any remaining flags over to relaxation. */
+ if (mips_macro_warning.first_frag != NULL)
+ mips_macro_warning.first_frag->fr_subtype |= subtype;
+}
+
+/* Instruction operand formats used in macros that vary between
+ standard MIPS and microMIPS code. */
+
+static const char * const brk_fmt[2][2] = { { "c", "c" }, { "mF", "c" } };
+static const char * const cop12_fmt[2] = { "E,o(b)", "E,~(b)" };
+static const char * const jalr_fmt[2] = { "d,s", "t,s" };
+static const char * const lui_fmt[2] = { "t,u", "s,u" };
+static const char * const mem12_fmt[2] = { "t,o(b)", "t,~(b)" };
+static const char * const mfhl_fmt[2][2] = { { "d", "d" }, { "mj", "s" } };
+static const char * const shft_fmt[2] = { "d,w,<", "t,r,<" };
+static const char * const trap_fmt[2] = { "s,t,q", "s,t,|" };
+
+#define BRK_FMT (brk_fmt[mips_opts.micromips][mips_opts.insn32])
+#define COP12_FMT (ISA_IS_R6 (mips_opts.isa) ? "E,+:(d)" \
+ : cop12_fmt[mips_opts.micromips])
+#define JALR_FMT (jalr_fmt[mips_opts.micromips])
+#define LUI_FMT (lui_fmt[mips_opts.micromips])
+#define MEM12_FMT (mem12_fmt[mips_opts.micromips])
+#define LL_SC_FMT (ISA_IS_R6 (mips_opts.isa) ? "t,+j(b)" \
+ : mem12_fmt[mips_opts.micromips])
+#define MFHL_FMT (mfhl_fmt[mips_opts.micromips][mips_opts.insn32])
+#define SHFT_FMT (shft_fmt[mips_opts.micromips])
+#define TRAP_FMT (trap_fmt[mips_opts.micromips])
+
+/* Read a macro's relocation codes from *ARGS and store them in *R.
+ The first argument in *ARGS will be either the code for a single
+ relocation or -1 followed by the three codes that make up a
+ composite relocation. */
+
+static void
+macro_read_relocs (va_list *args, bfd_reloc_code_real_type *r)
+{
+ int i, next;
+
+ next = va_arg (*args, int);
+ if (next >= 0)
+ r[0] = (bfd_reloc_code_real_type) next;
+ else
+ {
+ for (i = 0; i < 3; i++)
+ r[i] = (bfd_reloc_code_real_type) va_arg (*args, int);
+ /* This function is only used for 16-bit relocation fields.
+ To make the macro code simpler, treat an unrelocated value
+ in the same way as BFD_RELOC_LO16. */
+ if (r[0] == BFD_RELOC_UNUSED)
+ r[0] = BFD_RELOC_LO16;
+ }
+}
+
+/* Build an instruction created by a macro expansion. This is passed
+ a pointer to the count of instructions created so far, an
+ expression, the name of the instruction to build, an operand format
+ string, and corresponding arguments. */
+
+static void
+macro_build (expressionS *ep, const char *name, const char *fmt, ...)
+{
+ const struct mips_opcode *mo = NULL;
+ bfd_reloc_code_real_type r[3];
+ const struct mips_opcode *amo;
+ const struct mips_operand *operand;
+ struct hash_control *hash;
+ struct mips_cl_insn insn;
+ va_list args;
+ unsigned int uval;
+
+ va_start (args, fmt);
+
+ if (mips_opts.mips16)
+ {
+ mips16_macro_build (ep, name, fmt, &args);
+ va_end (args);
+ return;
+ }
+
+ r[0] = BFD_RELOC_UNUSED;
+ r[1] = BFD_RELOC_UNUSED;
+ r[2] = BFD_RELOC_UNUSED;
+ hash = mips_opts.micromips ? micromips_op_hash : op_hash;
+ amo = (struct mips_opcode *) hash_find (hash, name);
+ gas_assert (amo);
+ gas_assert (strcmp (name, amo->name) == 0);
+
+ do
+ {
+ /* Search until we get a match for NAME. It is assumed here that
+ macros will never generate MDMX, MIPS-3D, or MT instructions.
+ We try to match an instruction that fulfils the branch delay
+ slot instruction length requirement (if any) of the previous
+ instruction. While doing this we record the first instruction
+ seen that matches all the other conditions and use it anyway
+ if the requirement cannot be met; we will issue an appropriate
+ warning later on. */
+ if (strcmp (fmt, amo->args) == 0
+ && amo->pinfo != INSN_MACRO
+ && is_opcode_valid (amo)
+ && is_size_valid (amo))
+ {
+ if (is_delay_slot_valid (amo))
+ {
+ mo = amo;
+ break;
+ }
+ else if (!mo)
+ mo = amo;
+ }
+
+ ++amo;
+ gas_assert (amo->name);
+ }
+ while (strcmp (name, amo->name) == 0);
+
+ gas_assert (mo);
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
+ {
+ switch (*fmt)
+ {
+ case ',':
+ case '(':
+ case ')':
+ case 'z':
+ break;
+
+ case 'i':
+ case 'j':
+ macro_read_relocs (&args, r);
+ gas_assert (*r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_HIGHER
+ || *r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_LO16
+ || *r == BFD_RELOC_MIPS_GOT_OFST);
+ break;
+
+ case 'o':
+ macro_read_relocs (&args, r);
+ break;
+
+ case 'u':
+ macro_read_relocs (&args, r);
+ gas_assert (ep != NULL
+ && (ep->X_op == O_constant
+ || (ep->X_op == O_symbol
+ && (*r == BFD_RELOC_MIPS_HIGHEST
+ || *r == BFD_RELOC_HI16_S
+ || *r == BFD_RELOC_HI16
+ || *r == BFD_RELOC_GPREL16
+ || *r == BFD_RELOC_MIPS_GOT_HI16
+ || *r == BFD_RELOC_MIPS_CALL_HI16))));
+ break;
+
+ case 'p':
+ gas_assert (ep != NULL);
+
+ /*
+ * This allows macro() to pass an immediate expression for
+ * creating short branches without creating a symbol.
+ *
+ * We don't allow branch relaxation for these branches, as
+ * they should only appear in ".set nomacro" anyway.
+ */
+ if (ep->X_op == O_constant)
+ {
+ /* For microMIPS we always use relocations for branches.
+ So we should not resolve immediate values. */
+ gas_assert (!mips_opts.micromips);
+
+ if ((ep->X_add_number & 3) != 0)
+ as_bad (_("branch to misaligned address (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ if ((ep->X_add_number + 0x20000) & ~0x3ffff)
+ as_bad (_("branch address range overflow (0x%lx)"),
+ (unsigned long) ep->X_add_number);
+ insn.insn_opcode |= (ep->X_add_number >> 2) & 0xffff;
+ ep = NULL;
+ }
+ else
+ *r = BFD_RELOC_16_PCREL_S2;
+ break;
+
+ case 'a':
+ gas_assert (ep != NULL);
+ *r = BFD_RELOC_MIPS_JMP;
+ break;
+
+ default:
+ operand = (mips_opts.micromips
+ ? decode_micromips_operand (fmt)
+ : decode_mips_operand (fmt));
+ if (!operand)
+ abort ();
+
+ uval = va_arg (args, int);
+ if (operand->type == OP_CLO_CLZ_DEST)
+ uval |= (uval << 5);
+ insn_insert_operand (&insn, operand, uval);
+
+ if (*fmt == '+' || *fmt == 'm' || *fmt == '-')
+ ++fmt;
+ break;
+ }
+ }
+ va_end (args);
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (&insn, ep, r, TRUE);
+}
+
+static void
+mips16_macro_build (expressionS *ep, const char *name, const char *fmt,
+ va_list *args)
+{
+ struct mips_opcode *mo;
+ struct mips_cl_insn insn;
+ const struct mips_operand *operand;
+ bfd_reloc_code_real_type r[3]
+ = {BFD_RELOC_UNUSED, BFD_RELOC_UNUSED, BFD_RELOC_UNUSED};
+
+ mo = (struct mips_opcode *) hash_find (mips16_op_hash, name);
+ gas_assert (mo);
+ gas_assert (strcmp (name, mo->name) == 0);
+
+ while (strcmp (fmt, mo->args) != 0 || mo->pinfo == INSN_MACRO)
+ {
+ ++mo;
+ gas_assert (mo->name);
+ gas_assert (strcmp (name, mo->name) == 0);
+ }
+
+ create_insn (&insn, mo);
+ for (; *fmt; ++fmt)
+ {
+ int c;
+
+ c = *fmt;
+ switch (c)
+ {
+ case ',':
+ case '(':
+ case ')':
+ break;
+
+ case '0':
+ case 'S':
+ case 'P':
+ case 'R':
+ break;
+
+ case '<':
+ case '>':
+ case '4':
+ case '5':
+ case 'H':
+ case 'W':
+ case 'D':
+ case 'j':
+ case '8':
+ case 'V':
+ case 'C':
+ case 'U':
+ case 'k':
+ case 'K':
+ case 'p':
+ case 'q':
+ {
+ offsetT value;
+
+ gas_assert (ep != NULL);
+
+ if (ep->X_op != O_constant)
+ *r = (int) BFD_RELOC_UNUSED + c;
+ else if (calculate_reloc (*r, ep->X_add_number, &value))
+ {
+ mips16_immed (NULL, 0, c, *r, value, 0, &insn.insn_opcode);
+ ep = NULL;
+ *r = BFD_RELOC_UNUSED;
+ }
+ }
+ break;
+
+ default:
+ operand = decode_mips16_operand (c, FALSE);
+ if (!operand)
+ abort ();
+
+ insn_insert_operand (&insn, operand, va_arg (*args, int));
+ break;
+ }
+ }
+
+ gas_assert (*r == BFD_RELOC_UNUSED ? ep == NULL : ep != NULL);
+
+ append_insn (&insn, ep, r, TRUE);
+}
+
+/*
+ * Generate a "jalr" instruction with a relocation hint to the called
+ * function. This occurs in NewABI PIC code.
+ */
+static void
+macro_build_jalr (expressionS *ep, int cprestore)
+{
+ static const bfd_reloc_code_real_type jalr_relocs[2]
+ = { BFD_RELOC_MIPS_JALR, BFD_RELOC_MICROMIPS_JALR };
+ bfd_reloc_code_real_type jalr_reloc = jalr_relocs[mips_opts.micromips];
+ const char *jalr;
+ char *f = NULL;
+
+ if (MIPS_JALR_HINT_P (ep))
+ {
+ frag_grow (8);
+ f = frag_more (0);
+ }
+ if (mips_opts.micromips)
+ {
+ jalr = ((mips_opts.noreorder && !cprestore) || mips_opts.insn32
+ ? "jalr" : "jalrs");
+ if (MIPS_JALR_HINT_P (ep)
+ || mips_opts.insn32
+ || (history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, jalr, "t,s", RA, PIC_CALL_REG);
+ else
+ macro_build (NULL, jalr, "mj", PIC_CALL_REG);
+ }
+ else
+ macro_build (NULL, "jalr", "d,s", RA, PIC_CALL_REG);
+ if (MIPS_JALR_HINT_P (ep))
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4, ep, FALSE, jalr_reloc);
+}
+
+/*
+ * Generate a "lui" instruction.
+ */
+static void
+macro_build_lui (expressionS *ep, int regnum)
+{
+ gas_assert (! mips_opts.mips16);
+
+ if (ep->X_op != O_constant)
+ {
+ gas_assert (ep->X_op == O_symbol);
+ /* _gp_disp is a special case, used from s_cpload.
+ __gnu_local_gp is used if mips_no_shared. */
+ gas_assert (mips_pic == NO_PIC
+ || (! HAVE_NEWABI
+ && strcmp (S_GET_NAME (ep->X_add_symbol), "_gp_disp") == 0)
+ || (! mips_in_shared
+ && strcmp (S_GET_NAME (ep->X_add_symbol),
+ "__gnu_local_gp") == 0));
+ }
+
+ macro_build (ep, "lui", LUI_FMT, regnum, BFD_RELOC_HI16_S);
+}
+
+/* Generate a sequence of instructions to do a load or store from a constant
+ offset off of a base register (breg) into/from a target register (treg),
+ using AT if necessary. */
+static void
+macro_build_ldst_constoffset (expressionS *ep, const char *op,
+ int treg, int breg, int dbl)
+{
+ gas_assert (ep->X_op == O_constant);
+
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
+
+ /* Right now, this routine can only handle signed 32-bit constants. */
+ if (! IS_SEXT_32BIT_NUM(ep->X_add_number + 0x8000))
+ as_warn (_("operand overflow"));
+
+ if (IS_SEXT_16BIT_NUM(ep->X_add_number))
+ {
+ /* Signed 16-bit offset will fit in the op. Easy! */
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, breg);
+ }
+ else
+ {
+ /* 32-bit offset, need multiple instructions and AT, like:
+ lui $tempreg,const_hi (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> $treg,const_lo($tempreg) (BFD_RELOC_LO16)
+ to handle the complete offset. */
+ macro_build_lui (ep, AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, AT, breg);
+ macro_build (ep, op, "t,o(b)", treg, BFD_RELOC_LO16, AT);
+
+ if (!mips_opts.at)
+ as_bad (_("macro used $at after \".set noat\""));
+ }
+}
+
+/* set_at()
+ * Generates code to set the $at register to true (one)
+ * if reg is less than the immediate expression.
+ */
+static void
+set_at (int reg, int unsignedp)
+{
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ macro_build (&imm_expr, unsignedp ? "sltiu" : "slti", "t,r,j",
+ AT, reg, BFD_RELOC_LO16);
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, unsignedp ? "sltu" : "slt", "d,v,t", AT, reg, AT);
+ }
+}
+
+/* Count the leading zeroes by performing a binary chop. This is a
+ bulky bit of source, but performance is a LOT better for the
+ majority of values than a simple loop to count the bits:
+ for (lcnt = 0; (lcnt < 32); lcnt++)
+ if ((v) & (1 << (31 - lcnt)))
+ break;
+ However it is not code size friendly, and the gain will drop a bit
+ on certain cached systems.
+*/
+#define COUNT_TOP_ZEROES(v) \
+ (((v) & ~0xffff) == 0 \
+ ? ((v) & ~0xff) == 0 \
+ ? ((v) & ~0xf) == 0 \
+ ? ((v) & ~0x3) == 0 \
+ ? ((v) & ~0x1) == 0 \
+ ? !(v) \
+ ? 32 \
+ : 31 \
+ : 30 \
+ : ((v) & ~0x7) == 0 \
+ ? 29 \
+ : 28 \
+ : ((v) & ~0x3f) == 0 \
+ ? ((v) & ~0x1f) == 0 \
+ ? 27 \
+ : 26 \
+ : ((v) & ~0x7f) == 0 \
+ ? 25 \
+ : 24 \
+ : ((v) & ~0xfff) == 0 \
+ ? ((v) & ~0x3ff) == 0 \
+ ? ((v) & ~0x1ff) == 0 \
+ ? 23 \
+ : 22 \
+ : ((v) & ~0x7ff) == 0 \
+ ? 21 \
+ : 20 \
+ : ((v) & ~0x3fff) == 0 \
+ ? ((v) & ~0x1fff) == 0 \
+ ? 19 \
+ : 18 \
+ : ((v) & ~0x7fff) == 0 \
+ ? 17 \
+ : 16 \
+ : ((v) & ~0xffffff) == 0 \
+ ? ((v) & ~0xfffff) == 0 \
+ ? ((v) & ~0x3ffff) == 0 \
+ ? ((v) & ~0x1ffff) == 0 \
+ ? 15 \
+ : 14 \
+ : ((v) & ~0x7ffff) == 0 \
+ ? 13 \
+ : 12 \
+ : ((v) & ~0x3fffff) == 0 \
+ ? ((v) & ~0x1fffff) == 0 \
+ ? 11 \
+ : 10 \
+ : ((v) & ~0x7fffff) == 0 \
+ ? 9 \
+ : 8 \
+ : ((v) & ~0xfffffff) == 0 \
+ ? ((v) & ~0x3ffffff) == 0 \
+ ? ((v) & ~0x1ffffff) == 0 \
+ ? 7 \
+ : 6 \
+ : ((v) & ~0x7ffffff) == 0 \
+ ? 5 \
+ : 4 \
+ : ((v) & ~0x3fffffff) == 0 \
+ ? ((v) & ~0x1fffffff) == 0 \
+ ? 3 \
+ : 2 \
+ : ((v) & ~0x7fffffff) == 0 \
+ ? 1 \
+ : 0)
+
+/* load_register()
+ * This routine generates the least number of instructions necessary to load
+ * an absolute expression value into a register.
+ */
+static void
+load_register (int reg, expressionS *ep, int dbl)
+{
+ int freg;
+ expressionS hi32, lo32;
+
+ if (ep->X_op != O_big)
+ {
+ gas_assert (ep->X_op == O_constant);
+
+ /* Sign-extending 32-bit constants makes their handling easier. */
+ if (!dbl)
+ normalize_constant_expr (ep);
+
+ if (IS_SEXT_16BIT_NUM (ep->X_add_number))
+ {
+ /* We can handle 16 bit signed values with an addiu to
+ $zero. No need to ever use daddiu here, since $zero and
+ the result are always correct in 32 bit mode. */
+ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ else if (ep->X_add_number >= 0 && ep->X_add_number < 0x10000)
+ {
+ /* We can handle 16 bit unsigned values with an ori to
+ $zero. */
+ macro_build (ep, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ else if ((IS_SEXT_32BIT_NUM (ep->X_add_number)))
+ {
+ /* 32 bit values require an lui. */
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ if ((ep->X_add_number & 0xffff) != 0)
+ macro_build (ep, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
+ }
+ }
+
+ /* The value is larger than 32 bits. */
+
+ if (!dbl || GPR_SIZE == 32)
+ {
+ char value[32];
+
+ sprintf_vma (value, ep->X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
+ macro_build (ep, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+
+ if (ep->X_op != O_big)
+ {
+ hi32 = *ep;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number = (valueT) hi32.X_add_number >> 16;
+ hi32.X_add_number &= 0xffffffff;
+ lo32 = *ep;
+ lo32.X_add_number &= 0xffffffff;
+ }
+ else
+ {
+ gas_assert (ep->X_add_number > 2);
+ if (ep->X_add_number == 3)
+ generic_bignum[3] = 0;
+ else if (ep->X_add_number > 4)
+ as_bad (_("number larger than 64 bits"));
+ lo32.X_op = O_constant;
+ lo32.X_add_number = generic_bignum[0] + (generic_bignum[1] << 16);
+ hi32.X_op = O_constant;
+ hi32.X_add_number = generic_bignum[2] + (generic_bignum[3] << 16);
+ }
+
+ if (hi32.X_add_number == 0)
+ freg = 0;
+ else
+ {
+ int shift, bit;
+ unsigned long hi, lo;
+
+ if (hi32.X_add_number == (offsetT) 0xffffffff)
+ {
+ if ((lo32.X_add_number & 0xffff8000) == 0xffff8000)
+ {
+ macro_build (&lo32, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ return;
+ }
+ if (lo32.X_add_number & 0x80000000)
+ {
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ if (lo32.X_add_number & 0xffff)
+ macro_build (&lo32, "ori", "t,r,i", reg, reg, BFD_RELOC_LO16);
+ return;
+ }
+ }
+
+ /* Check for 16bit shifted constant. We know that hi32 is
+ non-zero, so start the mask on the first bit of the hi32
+ value. */
+ shift = 17;
+ do
+ {
+ unsigned long himask, lomask;
+
+ if (shift < 32)
+ {
+ himask = 0xffff >> (32 - shift);
+ lomask = (0xffff << shift) & 0xffffffff;
+ }
+ else
+ {
+ himask = 0xffff << (shift - 32);
+ lomask = 0;
+ }
+ if ((hi32.X_add_number & ~(offsetT) himask) == 0
+ && (lo32.X_add_number & ~(offsetT) lomask) == 0)
+ {
+ expressionS tmp;
+
+ tmp.X_op = O_constant;
+ if (shift < 32)
+ tmp.X_add_number = ((hi32.X_add_number << (32 - shift))
+ | (lo32.X_add_number >> shift));
+ else
+ tmp.X_add_number = hi32.X_add_number >> (shift - 32);
+ macro_build (&tmp, "ori", "t,r,i", reg, 0, BFD_RELOC_LO16);
+ macro_build (NULL, (shift >= 32) ? "dsll32" : "dsll", SHFT_FMT,
+ reg, reg, (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ ++shift;
+ }
+ while (shift <= (64 - 16));
+
+ /* Find the bit number of the lowest one bit, and store the
+ shifted value in hi/lo. */
+ hi = (unsigned long) (hi32.X_add_number & 0xffffffff);
+ lo = (unsigned long) (lo32.X_add_number & 0xffffffff);
+ if (lo != 0)
+ {
+ bit = 0;
+ while ((lo & 1) == 0)
+ {
+ lo >>= 1;
+ ++bit;
+ }
+ lo |= (hi & (((unsigned long) 1 << bit) - 1)) << (32 - bit);
+ hi >>= bit;
+ }
+ else
+ {
+ bit = 32;
+ while ((hi & 1) == 0)
+ {
+ hi >>= 1;
+ ++bit;
+ }
+ lo = hi;
+ hi = 0;
+ }
+
+ /* Optimize if the shifted value is a (power of 2) - 1. */
+ if ((hi == 0 && ((lo + 1) & lo) == 0)
+ || (lo == 0xffffffff && ((hi + 1) & hi) == 0))
+ {
+ shift = COUNT_TOP_ZEROES ((unsigned int) hi32.X_add_number);
+ if (shift != 0)
+ {
+ expressionS tmp;
+
+ /* This instruction will set the register to be all
+ ones. */
+ tmp.X_op = O_constant;
+ tmp.X_add_number = (offsetT) -1;
+ macro_build (&tmp, "addiu", "t,r,j", reg, 0, BFD_RELOC_LO16);
+ if (bit != 0)
+ {
+ bit += shift;
+ macro_build (NULL, (bit >= 32) ? "dsll32" : "dsll", SHFT_FMT,
+ reg, reg, (bit >= 32) ? bit - 32 : bit);
+ }
+ macro_build (NULL, (shift >= 32) ? "dsrl32" : "dsrl", SHFT_FMT,
+ reg, reg, (shift >= 32) ? shift - 32 : shift);
+ return;
+ }
+ }
+
+ /* Sign extend hi32 before calling load_register, because we can
+ generally get better code when we load a sign extended value. */
+ if ((hi32.X_add_number & 0x80000000) != 0)
+ hi32.X_add_number |= ~(offsetT) 0xffffffff;
+ load_register (reg, &hi32, 0);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff0000) == 0)
+ {
+ if (freg != 0)
+ {
+ macro_build (NULL, "dsll32", SHFT_FMT, reg, freg, 0);
+ freg = reg;
+ }
+ }
+ else
+ {
+ expressionS mid16;
+
+ if ((freg == 0) && (lo32.X_add_number == (offsetT) 0xffffffff))
+ {
+ macro_build (&lo32, "lui", LUI_FMT, reg, BFD_RELOC_HI16);
+ macro_build (NULL, "dsrl32", SHFT_FMT, reg, reg, 0);
+ return;
+ }
+
+ if (freg != 0)
+ {
+ macro_build (NULL, "dsll", SHFT_FMT, reg, freg, 16);
+ freg = reg;
+ }
+ mid16 = lo32;
+ mid16.X_add_number >>= 16;
+ macro_build (&mid16, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ freg = reg;
+ }
+ if ((lo32.X_add_number & 0xffff) != 0)
+ macro_build (&lo32, "ori", "t,r,i", reg, freg, BFD_RELOC_LO16);
+}
+
+static inline void
+load_delay_nop (void)
+{
+ if (!gpr_interlocks)
+ macro_build (NULL, "nop", "");
+}
+
+/* Load an address into a register. */
+
+static void
+load_address (int reg, expressionS *ep, int *used_at)
+{
+ if (ep->X_op != O_constant
+ && ep->X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ ep->X_op = O_constant;
+ }
+
+ if (ep->X_op == O_constant)
+ {
+ load_register (reg, ep, HAVE_64BIT_ADDRESSES);
+ return;
+ }
+
+ if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $reg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $reg,<sym> (BFD_RELOC_HI16_S)
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If we have an addend, we always use the latter form.
+
+ With 64bit address space and a usable $at we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddiu $at,<sym> (BFD_RELOC_LO16)
+ dsll32 $reg,0
+ daddu $reg,$reg,$at
+
+ If $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $reg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $reg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $reg,16
+ daddiu $reg,<sym> (BFD_RELOC_HI16_S)
+ dsll $reg,16
+ daddiu $reg,<sym> (BFD_RELOC_LO16)
+
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
+ {
+ if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (ep->X_add_symbol, 1))
+ {
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+
+ if (*used_at == 0 && mips_opts.at)
+ {
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "lui", LUI_FMT, AT, BFD_RELOC_HI16_S);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (ep, "daddiu", "t,r,j", AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll32", SHFT_FMT, reg, reg, 0);
+ macro_build (NULL, "daddu", "d,v,t", reg, reg, AT);
+ *used_at = 1;
+ }
+ else
+ {
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, reg, reg, 16);
+ macro_build (ep, "daddiu", "t,r,j", reg, reg, BFD_RELOC_LO16);
+ }
+
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ if ((valueT) ep->X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (ep->X_add_symbol, 1))
+ {
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg,
+ mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+ macro_build_lui (ep, reg);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (!mips_big_got)
+ {
+ expressionS ex;
+
+ /* If this is a reference to an external symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ Otherwise we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
+
+ If we have NewABI, we want
+ lw $reg,<sym+cst>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ unless we're referencing a global symbol with a non-zero
+ offset, in which case cst must be added separately. */
+ if (HAVE_NEWABI)
+ {
+ if (ep->X_add_number)
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ ep->X_add_number = ex.X_add_number;
+ relax_switch ();
+ }
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ relax_start (ep->X_add_symbol);
+ relax_switch ();
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
+
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j",
+ reg, reg, BFD_RELOC_LO16);
+ }
+ }
+ }
+ else if (mips_big_got)
+ {
+ expressionS ex;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, we want
+ lui $reg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $reg,$reg,$gp
+ lw $reg,<sym>($reg) (BFD_RELOC_MIPS_GOT_LO16)
+
+ Otherwise, for a reference to a local symbol in old ABI, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $reg,$reg,<sym> (BFD_RELOC_LO16)
+ If there is a constant, it must be added in after.
+
+ In the NewABI, for local symbols, with or without offsets, we want:
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
+ */
+ if (HAVE_NEWABI)
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ reg, reg, mips_gp_register);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+ reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ else if (ex.X_add_number)
+ {
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ }
+
+ ep->X_add_number = ex.X_add_number;
+ relax_switch ();
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_MIPS_GOT_OFST);
+ relax_end ();
+ }
+ else
+ {
+ ex.X_add_number = ep->X_add_number;
+ ep->X_add_number = 0;
+ relax_start (ep->X_add_symbol);
+ macro_build (ep, "lui", LUI_FMT, reg, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ reg, reg, mips_gp_register);
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)",
+ reg, BFD_RELOC_MIPS_GOT_LO16, reg);
+ relax_switch ();
+ if (reg_needs_delay (mips_gp_register))
+ {
+ /* We need a nop before loading from $gp. This special
+ check is required because the lui which starts the main
+ instruction stream does not refer to $gp, and so will not
+ insert the nop which may be required. */
+ macro_build (NULL, "nop", "");
+ }
+ macro_build (ep, ADDRESS_LOAD_INSN, "t,o(b)", reg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ relax_end ();
+
+ if (ex.X_add_number != 0)
+ {
+ if (ex.X_add_number < -0x8000 || ex.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ ex.X_op = O_constant;
+ macro_build (&ex, ADDRESS_ADDI_INSN, "t,r,j", reg, reg,
+ BFD_RELOC_LO16);
+ }
+ }
+ }
+ else
+ abort ();
+
+ if (!mips_opts.at && *used_at == 1)
+ as_bad (_("macro used $at after \".set noat\""));
+}
+
+/* Move the contents of register SOURCE into register DEST. */
+
+static void
+move_register (int dest, int source)
+{
+ /* Prefer to use a 16-bit microMIPS instruction unless the previous
+ instruction specifically requires a 32-bit one. */
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, "move", "mp,mj", dest, source);
+ else
+ macro_build (NULL, GPR_SIZE == 32 ? "addu" : "daddu", "d,v,t",
+ dest, source, 0);
+}
+
+/* Emit an SVR4 PIC sequence to load address LOCAL into DEST, where
+ LOCAL is the sum of a symbol and a 16-bit or 32-bit displacement.
+ The two alternatives are:
+
+ Global symbol Local sybmol
+ ------------- ------------
+ lw DEST,%got(SYMBOL) lw DEST,%got(SYMBOL + OFFSET)
+ ... ...
+ addiu DEST,DEST,OFFSET addiu DEST,DEST,%lo(SYMBOL + OFFSET)
+
+ load_got_offset emits the first instruction and add_got_offset
+ emits the second for a 16-bit offset or add_got_offset_hilo emits
+ a sequence to add a 32-bit offset using a scratch register. */
+
+static void
+load_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
+
+ global = *local;
+ global.X_add_number = 0;
+
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ relax_switch ();
+ macro_build (local, ADDRESS_LOAD_INSN, "t,o(b)", dest,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ relax_end ();
+}
+
+static void
+add_got_offset (int dest, expressionS *local)
+{
+ expressionS global;
+
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
+
+ relax_start (local->X_add_symbol);
+ macro_build (&global, ADDRESS_ADDI_INSN, "t,r,j",
+ dest, dest, BFD_RELOC_LO16);
+ relax_switch ();
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", dest, dest, BFD_RELOC_LO16);
+ relax_end ();
+}
+
+static void
+add_got_offset_hilo (int dest, expressionS *local, int tmp)
+{
+ expressionS global;
+ int hold_mips_optimize;
+
+ global.X_op = O_constant;
+ global.X_op_symbol = NULL;
+ global.X_add_symbol = NULL;
+ global.X_add_number = local->X_add_number;
+
+ relax_start (local->X_add_symbol);
+ load_register (tmp, &global, HAVE_64BIT_ADDRESSES);
+ relax_switch ();
+ /* Set mips_optimize around the lui instruction to avoid
+ inserting an unnecessary nop after the lw. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ macro_build_lui (&global, tmp);
+ mips_optimize = hold_mips_optimize;
+ macro_build (local, ADDRESS_ADDI_INSN, "t,r,j", tmp, tmp, BFD_RELOC_LO16);
+ relax_end ();
+
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dest, dest, tmp);
+}
+
+/* Emit a sequence of instructions to emulate a branch likely operation.
+ BR is an ordinary branch corresponding to one to be emulated. BRNEG
+ is its complementing branch with the original condition negated.
+ CALL is set if the original branch specified the link operation.
+ EP, FMT, SREG and TREG specify the usual macro_build() parameters.
+
+ Code like this is produced in the noreorder mode:
+
+ BRNEG <args>, 1f
+ nop
+ b <sym>
+ delay slot (executed only if branch taken)
+ 1:
+
+ or, if CALL is set:
+
+ BRNEG <args>, 1f
+ nop
+ bal <sym>
+ delay slot (executed only if branch taken)
+ 1:
+
+ In the reorder mode the delay slot would be filled with a nop anyway,
+ so code produced is simply:
+
+ BR <args>, <sym>
+ nop
+
+ This function is used when producing code for the microMIPS ASE that
+ does not implement branch likely instructions in hardware. */
+
+static void
+macro_build_branch_likely (const char *br, const char *brneg,
+ int call, expressionS *ep, const char *fmt,
+ unsigned int sreg, unsigned int treg)
+{
+ int noreorder = mips_opts.noreorder;
+ expressionS expr1;
+
+ gas_assert (mips_opts.micromips);
+ start_noreorder ();
+ if (noreorder)
+ {
+ micromips_label_expr (&expr1);
+ macro_build (&expr1, brneg, fmt, sreg, treg);
+ macro_build (NULL, "nop", "");
+ macro_build (ep, call ? "bal" : "b", "p");
+
+ /* Set to true so that append_insn adds a label. */
+ emit_branch_likely_macro = TRUE;
+ }
+ else
+ {
+ macro_build (ep, br, fmt, sreg, treg);
+ macro_build (NULL, "nop", "");
+ }
+ end_noreorder ();
+}
+
+/* Emit a coprocessor branch-likely macro specified by TYPE, using CC as
+ the condition code tested. EP specifies the branch target. */
+
+static void
+macro_build_branch_ccl (int type, expressionS *ep, unsigned int cc)
+{
+ const int call = 0;
+ const char *brneg;
+ const char *br;
+
+ switch (type)
+ {
+ case M_BC1FL:
+ br = "bc1f";
+ brneg = "bc1t";
+ break;
+ case M_BC1TL:
+ br = "bc1t";
+ brneg = "bc1f";
+ break;
+ case M_BC2FL:
+ br = "bc2f";
+ brneg = "bc2t";
+ break;
+ case M_BC2TL:
+ br = "bc2t";
+ brneg = "bc2f";
+ break;
+ default:
+ abort ();
+ }
+ macro_build_branch_likely (br, brneg, call, ep, "N,p", cc, ZERO);
+}
+
+/* Emit a two-argument branch macro specified by TYPE, using SREG as
+ the register tested. EP specifies the branch target. */
+
+static void
+macro_build_branch_rs (int type, expressionS *ep, unsigned int sreg)
+{
+ const char *brneg = NULL;
+ const char *br;
+ int call = 0;
+
+ switch (type)
+ {
+ case M_BGEZ:
+ br = "bgez";
+ break;
+ case M_BGEZL:
+ br = mips_opts.micromips ? "bgez" : "bgezl";
+ brneg = "bltz";
+ break;
+ case M_BGEZALL:
+ gas_assert (mips_opts.micromips);
+ br = mips_opts.insn32 ? "bgezal" : "bgezals";
+ brneg = "bltz";
+ call = 1;
+ break;
+ case M_BGTZ:
+ br = "bgtz";
+ break;
+ case M_BGTZL:
+ br = mips_opts.micromips ? "bgtz" : "bgtzl";
+ brneg = "blez";
+ break;
+ case M_BLEZ:
+ br = "blez";
+ break;
+ case M_BLEZL:
+ br = mips_opts.micromips ? "blez" : "blezl";
+ brneg = "bgtz";
+ break;
+ case M_BLTZ:
+ br = "bltz";
+ break;
+ case M_BLTZL:
+ br = mips_opts.micromips ? "bltz" : "bltzl";
+ brneg = "bgez";
+ break;
+ case M_BLTZALL:
+ gas_assert (mips_opts.micromips);
+ br = mips_opts.insn32 ? "bltzal" : "bltzals";
+ brneg = "bgez";
+ call = 1;
+ break;
+ default:
+ abort ();
+ }
+ if (mips_opts.micromips && brneg)
+ macro_build_branch_likely (br, brneg, call, ep, "s,p", sreg, ZERO);
+ else
+ macro_build (ep, br, "s,p", sreg);
+}
+
+/* Emit a three-argument branch macro specified by TYPE, using SREG and
+ TREG as the registers tested. EP specifies the branch target. */
+
+static void
+macro_build_branch_rsrt (int type, expressionS *ep,
+ unsigned int sreg, unsigned int treg)
+{
+ const char *brneg = NULL;
+ const int call = 0;
+ const char *br;
+
+ switch (type)
+ {
+ case M_BEQ:
+ case M_BEQ_I:
+ br = "beq";
+ break;
+ case M_BEQL:
+ case M_BEQL_I:
+ br = mips_opts.micromips ? "beq" : "beql";
+ brneg = "bne";
+ break;
+ case M_BNE:
+ case M_BNE_I:
+ br = "bne";
+ break;
+ case M_BNEL:
+ case M_BNEL_I:
+ br = mips_opts.micromips ? "bne" : "bnel";
+ brneg = "beq";
+ break;
+ default:
+ abort ();
+ }
+ if (mips_opts.micromips && brneg)
+ macro_build_branch_likely (br, brneg, call, ep, "s,t,p", sreg, treg);
+ else
+ macro_build (ep, br, "s,t,p", sreg, treg);
+}
+
+/* Return the high part that should be loaded in order to make the low
+ part of VALUE accessible using an offset of OFFBITS bits. */
+
+static offsetT
+offset_high_part (offsetT value, unsigned int offbits)
+{
+ offsetT bias;
+ addressT low_mask;
+
+ if (offbits == 0)
+ return value;
+ bias = 1 << (offbits - 1);
+ low_mask = bias * 2 - 1;
+ return (value + bias) & ~low_mask;
+}
+
+/* Return true if the value stored in offset_expr and offset_reloc
+ fits into a signed offset of OFFBITS bits. RANGE is the maximum
+ amount that the caller wants to add without inducing overflow
+ and ALIGN is the known alignment of the value in bytes. */
+
+static bfd_boolean
+small_offset_p (unsigned int range, unsigned int align, unsigned int offbits)
+{
+ if (offbits == 16)
+ {
+ /* Accept any relocation operator if overflow isn't a concern. */
+ if (range < align && *offset_reloc != BFD_RELOC_UNUSED)
+ return TRUE;
+
+ /* These relocations are guaranteed not to overflow in correct links. */
+ if (*offset_reloc == BFD_RELOC_MIPS_LITERAL
+ || gprel16_reloc_p (*offset_reloc))
+ return TRUE;
+ }
+ if (offset_expr.X_op == O_constant
+ && offset_high_part (offset_expr.X_add_number, offbits) == 0
+ && offset_high_part (offset_expr.X_add_number + range, offbits) == 0)
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Build macros
+ * This routine implements the seemingly endless macro or synthesized
+ * instructions and addressing modes in the mips assembly language. Many
+ * of these macros are simple and are similar to each other. These could
+ * probably be handled by some kind of table or grammar approach instead of
+ * this verbose method. Others are not simple macros but are more like
+ * optimizing code generation.
+ * One interesting optimization is when several store macros appear
+ * consecutively that would load AT with the upper half of the same address.
+ * The ensuing load upper instructions are ommited. This implies some kind
+ * of global optimization. We currently only optimize within a single macro.
+ * For many of the load and store macros if the address is specified as a
+ * constant expression in the first 64k of memory (ie ld $2,0x4000c) we
+ * first load register 'at' with zero and use it as the base register. The
+ * mips assembler simply uses register $zero. Just one tiny optimization
+ * we're missing.
+ */
+static void
+macro (struct mips_cl_insn *ip, char *str)
+{
+ const struct mips_operand_array *operands;
+ unsigned int breg, i;
+ unsigned int tempreg;
+ int mask;
+ int used_at = 0;
+ expressionS label_expr;
+ expressionS expr1;
+ expressionS *ep;
+ const char *s;
+ const char *s2;
+ const char *fmt;
+ int likely = 0;
+ int coproc = 0;
+ int offbits = 16;
+ int call = 0;
+ int jals = 0;
+ int dbl = 0;
+ int imm = 0;
+ int ust = 0;
+ int lp = 0;
+ bfd_boolean large_offset;
+ int off;
+ int hold_mips_optimize;
+ unsigned int align;
+ unsigned int op[MAX_OPERANDS];
+
+ gas_assert (! mips_opts.mips16);
+
+ operands = insn_operands (ip);
+ for (i = 0; i < MAX_OPERANDS; i++)
+ if (operands->operand[i])
+ op[i] = insn_extract_operand (ip, operands->operand[i]);
+ else
+ op[i] = -1;
+
+ mask = ip->insn_mo->mask;
+
+ label_expr.X_op = O_constant;
+ label_expr.X_op_symbol = NULL;
+ label_expr.X_add_symbol = NULL;
+ label_expr.X_add_number = 0;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+ align = 1;
+
+ switch (mask)
+ {
+ case M_DABS:
+ dbl = 1;
+ case M_ABS:
+ /* bgez $a0,1f
+ move v0,$a0
+ sub v0,$zero,$a0
+ 1:
+ */
+
+ start_noreorder ();
+
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bgez", "s,p", op[1]);
+ if (op[0] == op[1])
+ macro_build (NULL, "nop", "");
+ else
+ move_register (op[0], op[1]);
+ macro_build (NULL, dbl ? "dsub" : "sub", "d,v,t", op[0], 0, op[1]);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+
+ end_noreorder ();
+ break;
+
+ case M_ADD_I:
+ s = "addi";
+ s2 = "add";
+ goto do_addi;
+ case M_ADDU_I:
+ s = "addiu";
+ s2 = "addu";
+ goto do_addi;
+ case M_DADD_I:
+ dbl = 1;
+ s = "daddi";
+ s2 = "dadd";
+ if (!mips_opts.micromips)
+ goto do_addi;
+ if (imm_expr.X_add_number >= -0x200
+ && imm_expr.X_add_number < 0x200)
+ {
+ macro_build (NULL, s, "t,r,.", op[0], op[1],
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ goto do_addi_i;
+ case M_DADDU_I:
+ dbl = 1;
+ s = "daddiu";
+ s2 = "daddu";
+ do_addi:
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, s, "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ break;
+ }
+ do_addi_i:
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+ break;
+
+ case M_AND_I:
+ s = "andi";
+ s2 = "and";
+ goto do_bit;
+ case M_OR_I:
+ s = "ori";
+ s2 = "or";
+ goto do_bit;
+ case M_NOR_I:
+ s = "";
+ s2 = "nor";
+ goto do_bit;
+ case M_XOR_I:
+ s = "xori";
+ s2 = "xor";
+ do_bit:
+ if (imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ if (mask != M_NOR_I)
+ macro_build (&imm_expr, s, "t,r,i", op[0], op[1], BFD_RELOC_LO16);
+ else
+ {
+ macro_build (&imm_expr, "ori", "t,r,i",
+ op[0], op[1], BFD_RELOC_LO16);
+ macro_build (NULL, "nor", "d,v,t", op[0], op[0], 0);
+ }
+ break;
+ }
+
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+ break;
+
+ case M_BALIGN:
+ switch (imm_expr.X_add_number)
+ {
+ case 0:
+ macro_build (NULL, "nop", "");
+ break;
+ case 2:
+ macro_build (NULL, "packrl.ph", "d,s,t", op[0], op[0], op[1]);
+ break;
+ case 1:
+ case 3:
+ macro_build (NULL, "balign", "t,s,2", op[0], op[1],
+ (int) imm_expr.X_add_number);
+ break;
+ default:
+ as_bad (_("BALIGN immediate not 0, 1, 2 or 3 (%lu)"),
+ (unsigned long) imm_expr.X_add_number);
+ break;
+ }
+ break;
+
+ case M_BC1FL:
+ case M_BC1TL:
+ case M_BC2FL:
+ case M_BC2TL:
+ gas_assert (mips_opts.micromips);
+ macro_build_branch_ccl (mask, &offset_expr,
+ EXTRACT_OPERAND (1, BCC, *ip));
+ break;
+
+ case M_BEQ_I:
+ case M_BEQL_I:
+ case M_BNE_I:
+ case M_BNEL_I:
+ if (imm_expr.X_add_number == 0)
+ op[1] = 0;
+ else
+ {
+ op[1] = AT;
+ used_at = 1;
+ load_register (op[1], &imm_expr, GPR_SIZE == 64);
+ }
+ /* Fall through. */
+ case M_BEQL:
+ case M_BNEL:
+ macro_build_branch_rsrt (mask, &offset_expr, op[0], op[1]);
+ break;
+
+ case M_BGEL:
+ likely = 1;
+ case M_BGE:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BGEZL:
+ case M_BGEZALL:
+ case M_BGTZL:
+ case M_BLEZL:
+ case M_BLTZL:
+ case M_BLTZALL:
+ macro_build_branch_rs (mask, &offset_expr, op[0]);
+ break;
+
+ case M_BGTL_I:
+ likely = 1;
+ case M_BGT_I:
+ /* Check for > max integer. */
+ if (imm_expr.X_add_number >= GPR_SMAX)
+ {
+ do_false:
+ /* Result is always false. */
+ if (! likely)
+ macro_build (NULL, "nop", "");
+ else
+ macro_build_branch_rsrt (M_BNEL, &offset_expr, ZERO, ZERO);
+ break;
+ }
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BGE_I:
+ case M_BGEL_I:
+ if (mask == M_BGEL_I)
+ likely = 1;
+ if (imm_expr.X_add_number == 0)
+ {
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ,
+ &offset_expr, op[0]);
+ break;
+ }
+ if (imm_expr.X_add_number == 1)
+ {
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ,
+ &offset_expr, op[0]);
+ break;
+ }
+ if (imm_expr.X_add_number <= GPR_SMIN)
+ {
+ do_true:
+ /* result is always true */
+ as_warn (_("branch %s is always true"), ip->insn_mo->name);
+ macro_build (&offset_expr, "b", "p");
+ break;
+ }
+ used_at = 1;
+ set_at (op[0], 0);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ break;
+
+ case M_BGEUL:
+ likely = 1;
+ case M_BGEU:
+ if (op[1] == 0)
+ goto do_true;
+ else if (op[0] == 0)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, ZERO, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BGTUL_I:
+ likely = 1;
+ case M_BGTU_I:
+ if (op[0] == 0
+ || (GPR_SIZE == 32
+ && imm_expr.X_add_number == -1))
+ goto do_false;
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BGEU_I:
+ case M_BGEUL_I:
+ if (mask == M_BGEUL_I)
+ likely = 1;
+ if (imm_expr.X_add_number == 0)
+ goto do_true;
+ else if (imm_expr.X_add_number == 1)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, op[0], ZERO);
+ else
+ {
+ used_at = 1;
+ set_at (op[0], 1);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BGTL:
+ likely = 1;
+ case M_BGT:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BGTUL:
+ likely = 1;
+ case M_BGTU:
+ if (op[1] == 0)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, op[0], ZERO);
+ else if (op[0] == 0)
+ goto do_false;
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLEL:
+ likely = 1;
+ case M_BLE:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BGEZL : M_BGEZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLEL_I:
+ likely = 1;
+ case M_BLE_I:
+ if (imm_expr.X_add_number >= GPR_SMAX)
+ goto do_true;
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BLT_I:
+ case M_BLTL_I:
+ if (mask == M_BLTL_I)
+ likely = 1;
+ if (imm_expr.X_add_number == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
+ else if (imm_expr.X_add_number == 1)
+ macro_build_branch_rs (likely ? M_BLEZL : M_BLEZ, &offset_expr, op[0]);
+ else
+ {
+ used_at = 1;
+ set_at (op[0], 0);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLEUL:
+ likely = 1;
+ case M_BLEU:
+ if (op[1] == 0)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, op[0], ZERO);
+ else if (op[0] == 0)
+ goto do_true;
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[1], op[0]);
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLEUL_I:
+ likely = 1;
+ case M_BLEU_I:
+ if (op[0] == 0
+ || (GPR_SIZE == 32
+ && imm_expr.X_add_number == -1))
+ goto do_true;
+ ++imm_expr.X_add_number;
+ /* FALLTHROUGH */
+ case M_BLTU_I:
+ case M_BLTUL_I:
+ if (mask == M_BLTUL_I)
+ likely = 1;
+ if (imm_expr.X_add_number == 0)
+ goto do_false;
+ else if (imm_expr.X_add_number == 1)
+ macro_build_branch_rsrt (likely ? M_BEQL : M_BEQ,
+ &offset_expr, op[0], ZERO);
+ else
+ {
+ used_at = 1;
+ set_at (op[0], 1);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLTL:
+ likely = 1;
+ case M_BLT:
+ if (op[1] == 0)
+ macro_build_branch_rs (likely ? M_BLTZL : M_BLTZ, &offset_expr, op[0]);
+ else if (op[0] == 0)
+ macro_build_branch_rs (likely ? M_BGTZL : M_BGTZ, &offset_expr, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "slt", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_BLTUL:
+ likely = 1;
+ case M_BLTU:
+ if (op[1] == 0)
+ goto do_false;
+ else if (op[0] == 0)
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, ZERO, op[1]);
+ else
+ {
+ used_at = 1;
+ macro_build (NULL, "sltu", "d,v,t", AT, op[0], op[1]);
+ macro_build_branch_rsrt (likely ? M_BNEL : M_BNE,
+ &offset_expr, AT, ZERO);
+ }
+ break;
+
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ if (op[2] == 0)
+ {
+ as_warn (_("divide by zero"));
+ if (mips_trap)
+ macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+ else
+ macro_build (NULL, "break", BRK_FMT, 7);
+ break;
+ }
+
+ start_noreorder ();
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", TRAP_FMT, op[2], ZERO, 7);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", op[1], op[2]);
+ }
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", op[2], ZERO);
+ macro_build (NULL, dbl ? "ddiv" : "div", "z,s,t", op[1], op[2]);
+ macro_build (NULL, "break", BRK_FMT, 7);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ expr1.X_add_number = -1;
+ used_at = 1;
+ load_register (AT, &expr1, dbl);
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = mips_trap ? (dbl ? 12 : 8) : (dbl ? 20 : 16);
+ macro_build (&label_expr, "bne", "s,t,p", op[2], AT);
+ if (dbl)
+ {
+ expr1.X_add_number = 1;
+ load_register (AT, &expr1, dbl);
+ macro_build (NULL, "dsll32", SHFT_FMT, AT, AT, 31);
+ }
+ else
+ {
+ expr1.X_add_number = 0x80000000;
+ macro_build (&expr1, "lui", LUI_FMT, AT, BFD_RELOC_HI16);
+ }
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", TRAP_FMT, op[1], AT, 6);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+ }
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", op[1], AT);
+ macro_build (NULL, "nop", "");
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+
+ macro_build (NULL, "break", BRK_FMT, 6);
+ }
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ macro_build (NULL, s, MFHL_FMT, op[0]);
+ break;
+
+ case M_DIV_3I:
+ s = "div";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DIVU_3I:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_REM_3I:
+ s = "div";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_REMU_3I:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DDIV_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DDIVU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divi;
+ case M_DREM_3I:
+ dbl = 1;
+ s = "ddiv";
+ s2 = "mfhi";
+ goto do_divi;
+ case M_DREMU_3I:
+ dbl = 1;
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divi:
+ if (imm_expr.X_add_number == 0)
+ {
+ as_warn (_("divide by zero"));
+ if (mips_trap)
+ macro_build (NULL, "teq", TRAP_FMT, ZERO, ZERO, 7);
+ else
+ macro_build (NULL, "break", BRK_FMT, 7);
+ break;
+ }
+ if (imm_expr.X_add_number == 1)
+ {
+ if (strcmp (s2, "mflo") == 0)
+ move_register (op[0], op[1]);
+ else
+ move_register (op[0], ZERO);
+ break;
+ }
+ if (imm_expr.X_add_number == -1 && s[strlen (s) - 1] != 'u')
+ {
+ if (strcmp (s2, "mflo") == 0)
+ macro_build (NULL, dbl ? "dneg" : "neg", "d,w", op[0], op[1]);
+ else
+ move_register (op[0], ZERO);
+ break;
+ }
+
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s, "z,s,t", op[1], AT);
+ macro_build (NULL, s2, MFHL_FMT, op[0]);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ start_noreorder ();
+ if (mips_trap)
+ {
+ macro_build (NULL, "teq", TRAP_FMT, op[2], ZERO, 7);
+ macro_build (NULL, s, "z,s,t", op[1], op[2]);
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+ }
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "bne", "s,t,p", op[2], ZERO);
+ macro_build (NULL, s, "z,s,t", op[1], op[2]);
+
+ /* We want to close the noreorder block as soon as possible, so
+ that later insns are available for delay slot filling. */
+ end_noreorder ();
+ macro_build (NULL, "break", BRK_FMT, 7);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ macro_build (NULL, s2, MFHL_FMT, op[0]);
+ break;
+
+ case M_DLCA_AB:
+ dbl = 1;
+ case M_LCA_AB:
+ call = 1;
+ goto do_la;
+ case M_DLA_AB:
+ dbl = 1;
+ case M_LA_AB:
+ do_la:
+ /* Load the address of a symbol into a register. If breg is not
+ zero, we then add a base register to it. */
+
+ breg = op[2];
+ if (dbl && GPR_SIZE == 32)
+ as_warn (_("dla used to load 32-bit register"));
+
+ if (!dbl && HAVE_64BIT_OBJECTS)
+ as_warn (_("la used to load 64-bit address"));
+
+ if (small_offset_p (0, align, 16))
+ {
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", op[0], breg,
+ -1, offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ break;
+ }
+
+ if (mips_opts.at && (op[0] == breg))
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ tempreg = op[0];
+
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ if (offset_expr.X_op == O_constant)
+ load_register (tempreg, &offset_expr, HAVE_64BIT_ADDRESSES);
+ else if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ addiu $tempreg,$gp,<sym> (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we may as well always use the latter form.
+
+ With 64bit address space and a usable $at we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddiu $at,<sym> (BFD_RELOC_LO16)
+ dsll32 $tempreg,0
+ daddu $tempreg,$tempreg,$at
+
+ If $at is already in use, we use a path which is suboptimal
+ on superscalar processors.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_LO16)
+
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+
+ if (used_at == 0 && mips_opts.at)
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ tempreg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ AT, BFD_RELOC_HI16_S);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
+ macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ tempreg, BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, mips_gp_register, BFD_RELOC_GPREL16);
+ relax_switch ();
+ }
+ if (!IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ as_bad (_("offset too large"));
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (!mips_big_got && !HAVE_NEWABI)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* If this is a reference to an external symbol, and there
+ is no constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want the same instruction
+ sequence, but we output a BFD_RELOC_LO16 reloc on the
+ addiu instruction.
+ */
+
+ if (offset_expr.X_add_number == 0)
+ {
+ if (mips_pic == SVR4_PIC
+ && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL16;
+
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ lw_reloc_type, mips_gp_register);
+ if (breg != 0)
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ load_delay_nop ();
+ }
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ relax_end ();
+ /* FIXME: If breg == 0, and the next instruction uses
+ $tempreg, then if this variant case is used an extra
+ nop will be generated. */
+ }
+ else if (offset_expr.X_add_number >= -0x8000
+ && offset_expr.X_add_number < 0x8000)
+ {
+ load_got_offset (tempreg, &offset_expr);
+ load_delay_nop ();
+ add_got_offset (tempreg, &offset_expr);
+ }
+ else
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number =
+ SEXT_16BIT (offset_expr.X_add_number);
+ load_got_offset (tempreg, &offset_expr);
+ offset_expr.X_add_number = expr1.X_add_number;
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg == op[0])
+ {
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ add_got_offset_hilo (tempreg, &offset_expr, AT);
+ used_at = 1;
+ }
+ }
+ else if (!mips_big_got && HAVE_NEWABI)
+ {
+ int add_breg_early = 0;
+
+ /* If this is a reference to an external, and there is no
+ constant, or local symbol (*), with or without a
+ constant, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ or for lca or if tempreg is PIC_CALL_REG
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ addiu $tempreg,$tempreg,<constant>
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_DISP)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+
+ (*) Other assemblers seem to prefer GOT_PAGE/GOT_OFST for
+ local symbols, even though it introduces an additional
+ instruction. */
+
+ if (offset_expr.X_add_number)
+ {
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ unsigned int dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != op[0])
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ dreg = op[0];
+ add_breg_early = 1;
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ if (add_breg_early)
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], tempreg, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ relax_end ();
+ }
+ else if (breg == 0 && (call || tempreg == PIC_CALL_REG))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_CALL16, mips_gp_register);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ relax_end ();
+ }
+ else
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_DISP, mips_gp_register);
+ }
+ }
+ else if (mips_big_got && !HAVE_NEWABI)
+ {
+ int gpdelay;
+ int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+ int local_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ addiu $tempreg,$tempreg,<constant>
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<constant> (BFD_RELOC_LO16)
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant>
+ addu $tempreg,$tempreg,$at
+ For a local symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ lui $at,<hiconstant>
+ addiu $at,$at,<loconstant> (BFD_RELOC_LO16)
+ addu $tempreg,$tempreg,$at
+ */
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ relax_start (offset_expr.X_add_symbol);
+ gpdelay = reg_needs_delay (mips_gp_register);
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ {
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+ }
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, lw_reloc_type, tempreg);
+ if (expr1.X_add_number == 0)
+ {
+ if (breg != 0)
+ {
+ /* We're going to put in an addu instruction using
+ tempreg, so we may as well insert the nop right
+ now. */
+ load_delay_nop ();
+ }
+ }
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ load_delay_nop ();
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else
+ {
+ unsigned int dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != op[0])
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ dreg = op[0];
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ offset_expr.X_add_number = SEXT_16BIT (expr1.X_add_number);
+ relax_switch ();
+
+ if (gpdelay)
+ {
+ /* This is needed because this instruction uses $gp, but
+ the first instruction on the main stream does not. */
+ macro_build (NULL, "nop", "");
+ }
+
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ local_reloc_type, mips_gp_register);
+ if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ /* FIXME: If add_number is 0, and there was no base
+ register, the external symbol case ended with a load,
+ so if the symbol turns out to not be external, and
+ the next instruction uses tempreg, an unnecessary nop
+ will be inserted. */
+ }
+ else
+ {
+ if (breg == op[0])
+ {
+ /* We must add in the base register now, as in the
+ external symbol case. */
+ gas_assert (tempreg == AT);
+ load_delay_nop ();
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ tempreg = op[0];
+ /* We set breg to 0 because we have arranged to add
+ it in in both cases. */
+ breg = 0;
+ }
+
+ macro_build_lui (&expr1, AT);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, AT);
+ used_at = 1;
+ }
+ relax_end ();
+ }
+ else if (mips_big_got && HAVE_NEWABI)
+ {
+ int lui_reloc_type = (int) BFD_RELOC_MIPS_GOT_HI16;
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT_LO16;
+ int add_breg_early = 0;
+
+ /* This is the large GOT case. If this is a reference to an
+ external symbol, and there is no constant, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ or for lca or if tempreg is PIC_CALL_REG
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_CALL_LO16)
+
+ If we have a small constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ addi $tempreg,$tempreg,<constant>
+
+ If we have a large constant, and this is a reference to
+ an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ lui $at,<hiconstant>
+ addi $at,$at,<loconstant>
+ add $tempreg,$tempreg,$at
+
+ If we have NewABI, and we know it's a local symbol, we want
+ lw $reg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ addiu $reg,$reg,<sym> (BFD_RELOC_MIPS_GOT_OFST)
+ otherwise we have to resort to GOT_HI16/GOT_LO16. */
+
+ relax_start (offset_expr.X_add_symbol);
+
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+
+ if (expr1.X_add_number == 0 && breg == 0
+ && (call || tempreg == PIC_CALL_REG))
+ {
+ lui_reloc_type = (int) BFD_RELOC_MIPS_CALL_HI16;
+ lw_reloc_type = (int) BFD_RELOC_MIPS_CALL_LO16;
+ }
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg, lui_reloc_type);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ tempreg, lw_reloc_type, tempreg);
+
+ if (expr1.X_add_number == 0)
+ ;
+ else if (expr1.X_add_number >= -0x8000
+ && expr1.X_add_number < 0x8000)
+ {
+ macro_build (&expr1, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, tempreg, BFD_RELOC_LO16);
+ }
+ else if (IS_SEXT_32BIT_NUM (expr1.X_add_number + 0x8000))
+ {
+ unsigned int dreg;
+
+ /* If we are going to add in a base register, and the
+ target register and the base register are the same,
+ then we are using AT as a temporary register. Since
+ we want to load the constant into AT, we add our
+ current AT (from the global offset table) and the
+ register into the register now, and pretend we were
+ not using a base register. */
+ if (breg != op[0])
+ dreg = tempreg;
+ else
+ {
+ gas_assert (tempreg == AT);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], AT, breg);
+ dreg = op[0];
+ add_breg_early = 1;
+ }
+
+ load_register (AT, &expr1, HAVE_64BIT_ADDRESSES);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", dreg, dreg, AT);
+
+ used_at = 1;
+ }
+ else
+ as_bad (_("PIC code offset overflow (max 32 signed bits)"));
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_GOT_OFST);
+ if (add_breg_early)
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ op[0], tempreg, breg);
+ breg = 0;
+ tempreg = op[0];
+ }
+ relax_end ();
+ }
+ else
+ abort ();
+
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", op[0], tempreg, breg);
+ break;
+
+ case M_MSGSND:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x01);
+ break;
+
+ case M_MSGLD:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 0x02);
+ break;
+
+ case M_MSGLD_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x02);
+ break;
+
+ case M_MSGWAIT:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", 3);
+ break;
+
+ case M_MSGWAIT_T:
+ gas_assert (!mips_opts.micromips);
+ macro_build (NULL, "c2", "C", (op[0] << 16) | 0x03);
+ break;
+
+ case M_J_A:
+ /* The j instruction may not be used in PIC code, since it
+ requires an absolute address. We convert it to a b
+ instruction. */
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, "j", "a");
+ else
+ macro_build (&offset_expr, "b", "p");
+ break;
+
+ /* The jal instructions must be handled as macros because when
+ generating PIC code they expand to multi-instruction
+ sequences. Normally they are simple instructions. */
+ case M_JALS_1:
+ op[1] = op[0];
+ op[0] = RA;
+ /* Fall through. */
+ case M_JALS_2:
+ gas_assert (mips_opts.micromips);
+ if (mips_opts.insn32)
+ {
+ as_bad (_("opcode not supported in the `insn32' mode `%s'"), str);
+ break;
+ }
+ jals = 1;
+ goto jal;
+ case M_JAL_1:
+ op[1] = op[0];
+ op[0] = RA;
+ /* Fall through. */
+ case M_JAL_2:
+ jal:
+ if (mips_pic == NO_PIC)
+ {
+ s = jals ? "jalrs" : "jalr";
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && op[0] == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, s, "mj", op[1]);
+ else
+ macro_build (NULL, s, JALR_FMT, op[0], op[1]);
+ }
+ else
+ {
+ int cprestore = (mips_pic == SVR4_PIC && !HAVE_NEWABI
+ && mips_cprestore_offset >= 0);
+
+ if (op[1] != PIC_CALL_REG)
+ as_warn (_("MIPS PIC call to register other than $25"));
+
+ s = ((mips_opts.micromips
+ && !mips_opts.insn32
+ && (!mips_opts.noreorder || cprestore))
+ ? "jalrs" : "jalr");
+ if (mips_opts.micromips
+ && !mips_opts.insn32
+ && op[0] == RA
+ && !(history[0].insn_mo->pinfo2 & INSN2_BRANCH_DELAY_32BIT))
+ macro_build (NULL, s, "mj", op[1]);
+ else
+ macro_build (NULL, s, JALR_FMT, op[0], op[1]);
+ if (mips_pic == SVR4_PIC && !HAVE_NEWABI)
+ {
+ if (mips_cprestore_offset < 0)
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (!mips_frame_reg_valid)
+ {
+ as_warn (_("no .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (!mips_cprestore_valid)
+ {
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
+ }
+
+ break;
+
+ case M_JALS_A:
+ gas_assert (mips_opts.micromips);
+ if (mips_opts.insn32)
+ {
+ as_bad (_("opcode not supported in the `insn32' mode `%s'"), str);
+ break;
+ }
+ jals = 1;
+ /* Fall through. */
+ case M_JAL_A:
+ if (mips_pic == NO_PIC)
+ macro_build (&offset_expr, jals ? "jals" : "jal", "a");
+ else if (mips_pic == SVR4_PIC)
+ {
+ /* If this is a reference to an external symbol, and we are
+ using a small GOT, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_CALL16)
+ nop
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+ The cprestore value is set using the .cprestore
+ pseudo-op. If we are using a big GOT, we want
+ lui $25,<sym> (BFD_RELOC_MIPS_CALL_HI16)
+ addu $25,$25,$gp
+ lw $25,<sym>($25) (BFD_RELOC_MIPS_CALL_LO16)
+ nop
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+ If the symbol is not external, we want
+ lw $25,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $25,$25,<sym> (BFD_RELOC_LO16)
+ jalr $ra,$25
+ nop
+ lw $gp,cprestore($sp)
+
+ For NewABI, we use the same CALL16 or CALL_HI16/CALL_LO16
+ sequences above, minus nops, unless the symbol is local,
+ which enables us to use GOT_PAGE/GOT_OFST (big got) or
+ GOT_DISP. */
+ if (HAVE_NEWABI)
+ {
+ if (!mips_big_got)
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+ mips_gp_register);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT_DISP,
+ mips_gp_register);
+ relax_end ();
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+ BFD_RELOC_MIPS_CALL_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+ PIC_CALL_REG, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+ PIC_CALL_REG);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT_PAGE,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ PIC_CALL_REG, PIC_CALL_REG,
+ BFD_RELOC_MIPS_GOT_OFST);
+ relax_end ();
+ }
+
+ macro_build_jalr (&offset_expr, 0);
+ }
+ else
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (!mips_big_got)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL16,
+ mips_gp_register);
+ load_delay_nop ();
+ relax_switch ();
+ }
+ else
+ {
+ int gpdelay;
+
+ gpdelay = reg_needs_delay (mips_gp_register);
+ macro_build (&offset_expr, "lui", LUI_FMT, PIC_CALL_REG,
+ BFD_RELOC_MIPS_CALL_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", PIC_CALL_REG,
+ PIC_CALL_REG, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_CALL_LO16,
+ PIC_CALL_REG);
+ load_delay_nop ();
+ relax_switch ();
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ }
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ PIC_CALL_REG, BFD_RELOC_MIPS_GOT16,
+ mips_gp_register);
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ PIC_CALL_REG, PIC_CALL_REG, BFD_RELOC_LO16);
+ relax_end ();
+ macro_build_jalr (&offset_expr, mips_cprestore_offset >= 0);
+
+ if (mips_cprestore_offset < 0)
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ else
+ {
+ if (!mips_frame_reg_valid)
+ {
+ as_warn (_("no .frame pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_frame_reg_valid = 1;
+ }
+ if (!mips_cprestore_valid)
+ {
+ as_warn (_("no .cprestore pseudo-op used in PIC code"));
+ /* Quiet this warning. */
+ mips_cprestore_valid = 1;
+ }
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = mips_cprestore_offset;
+ macro_build_ldst_constoffset (&expr1, ADDRESS_LOAD_INSN,
+ mips_gp_register,
+ mips_frame_reg,
+ HAVE_64BIT_ADDRESSES);
+ }
+ }
+ }
+ else if (mips_pic == VXWORKS_PIC)
+ as_bad (_("non-PIC jump used in PIC library"));
+ else
+ abort ();
+
+ break;
+
+ case M_LBUE_AB:
+ s = "lbue";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LHUE_AB:
+ s = "lhue";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LBE_AB:
+ s = "lbe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LHE_AB:
+ s = "lhe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LLE_AB:
+ s = "lle";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LWE_AB:
+ s = "lwe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LWLE_AB:
+ s = "lwle";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_LWRE_AB:
+ s = "lwre";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SBE_AB:
+ s = "sbe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SCE_AB:
+ s = "sce";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SHE_AB:
+ s = "she";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SWE_AB:
+ s = "swe";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SWLE_AB:
+ s = "swle";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SWRE_AB:
+ s = "swre";
+ fmt = "t,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_ACLR_AB:
+ s = "aclr";
+ fmt = "\\,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_ASET_AB:
+ s = "aset";
+ fmt = "\\,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_LB_AB:
+ s = "lb";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LBU_AB:
+ s = "lbu";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LH_AB:
+ s = "lh";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LHU_AB:
+ s = "lhu";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LW_AB:
+ s = "lw";
+ fmt = "t,o(b)";
+ goto ld;
+ case M_LWC0_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "lwc0";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWC1_AB:
+ s = "lwc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWC2_AB:
+ s = "lwc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWC3_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "lwc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LWL_AB:
+ s = "lwl";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_LWR_AB:
+ s = "lwr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_LDC1_AB:
+ s = "ldc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LDC2_AB:
+ s = "ldc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LQC2_AB:
+ s = "lqc2";
+ fmt = "+7,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LDC3_AB:
+ s = "ldc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_LDL_AB:
+ s = "ldl";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_LDR_AB:
+ s = "ldr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_LL_AB:
+ s = "ll";
+ fmt = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld;
+ case M_LLD_AB:
+ s = "lld";
+ fmt = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld;
+ case M_LWU_AB:
+ s = "lwu";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld;
+ case M_LWP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "lwp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ lp = 1;
+ goto ld;
+ case M_LDP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "ldp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ lp = 1;
+ goto ld;
+ case M_LWM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "lwm";
+ fmt = "n,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_LDM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "ldm";
+ fmt = "n,~(b)";
+ offbits = 12;
+ goto ld_st;
+
+ ld:
+ /* We don't want to use $0 as tempreg. */
+ if (op[2] == op[0] + lp || op[0] + lp == ZERO)
+ goto ld_st;
+ else
+ tempreg = op[0] + lp;
+ goto ld_noat;
+
+ case M_SB_AB:
+ s = "sb";
+ fmt = "t,o(b)";
+ goto ld_st;
+ case M_SH_AB:
+ s = "sh";
+ fmt = "t,o(b)";
+ goto ld_st;
+ case M_SW_AB:
+ s = "sw";
+ fmt = "t,o(b)";
+ goto ld_st;
+ case M_SWC0_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "swc0";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWC1_AB:
+ s = "swc1";
+ fmt = "T,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWC2_AB:
+ s = "swc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWC3_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "swc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SWL_AB:
+ s = "swl";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SWR_AB:
+ s = "swr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SC_AB:
+ s = "sc";
+ fmt = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld_st;
+ case M_SCD_AB:
+ s = "scd";
+ fmt = LL_SC_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld_st;
+ case M_CACHE_AB:
+ s = "cache";
+ fmt = (mips_opts.micromips ? "k,~(b)"
+ : ISA_IS_R6 (mips_opts.isa) ? "k,+j(b)"
+ : "k,o(b)");
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld_st;
+ case M_CACHEE_AB:
+ s = "cachee";
+ fmt = "k,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_PREF_AB:
+ s = "pref";
+ fmt = (mips_opts.micromips ? "k,~(b)"
+ : ISA_IS_R6 (mips_opts.isa) ? "k,+j(b)"
+ : "k,o(b)");
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 9
+ : 16);
+ goto ld_st;
+ case M_PREFE_AB:
+ s = "prefe";
+ fmt = "k,+j(b)";
+ offbits = 9;
+ goto ld_st;
+ case M_SDC1_AB:
+ s = "sdc1";
+ fmt = "T,o(b)";
+ coproc = 1;
+ /* Itbl support may require additional care here. */
+ goto ld_st;
+ case M_SDC2_AB:
+ s = "sdc2";
+ fmt = COP12_FMT;
+ offbits = (mips_opts.micromips ? 12
+ : ISA_IS_R6 (mips_opts.isa) ? 11
+ : 16);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SQC2_AB:
+ s = "sqc2";
+ fmt = "+7,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SDC3_AB:
+ gas_assert (!mips_opts.micromips);
+ s = "sdc3";
+ fmt = "E,o(b)";
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ goto ld_st;
+ case M_SDL_AB:
+ s = "sdl";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SDR_AB:
+ s = "sdr";
+ fmt = MEM12_FMT;
+ offbits = (mips_opts.micromips ? 12 : 16);
+ goto ld_st;
+ case M_SWP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "swp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_SDP_AB:
+ gas_assert (mips_opts.micromips);
+ s = "sdp";
+ fmt = "t,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_SWM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "swm";
+ fmt = "n,~(b)";
+ offbits = 12;
+ goto ld_st;
+ case M_SDM_AB:
+ gas_assert (mips_opts.micromips);
+ s = "sdm";
+ fmt = "n,~(b)";
+ offbits = 12;
+
+ ld_st:
+ tempreg = AT;
+ ld_noat:
+ breg = op[2];
+ if (small_offset_p (0, align, 16))
+ {
+ /* The first case exists for M_LD_AB and M_SD_AB, which are
+ macros for o32 but which should act like normal instructions
+ otherwise. */
+ if (offbits == 16)
+ macro_build (&offset_expr, s, fmt, op[0], -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ else if (small_offset_p (0, align, offbits))
+ {
+ if (offbits == 0)
+ macro_build (NULL, s, fmt, op[0], breg);
+ else
+ macro_build (NULL, s, fmt, op[0],
+ (int) offset_expr.X_add_number, breg);
+ }
+ else
+ {
+ if (tempreg == AT)
+ used_at = 1;
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j",
+ tempreg, breg, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2]);
+ if (offbits == 0)
+ macro_build (NULL, s, fmt, op[0], tempreg);
+ else
+ macro_build (NULL, s, fmt, op[0], 0, tempreg);
+ }
+ break;
+ }
+
+ if (tempreg == AT)
+ used_at = 1;
+
+ if (offset_expr.X_op != O_constant
+ && offset_expr.X_op != O_symbol)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
+
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
+ }
+
+ /* A constant expression in PIC code can be handled just as it
+ is in non PIC code. */
+ if (offset_expr.X_op == O_constant)
+ {
+ expr1.X_add_number = offset_high_part (offset_expr.X_add_number,
+ offbits == 0 ? 16 : offbits);
+ offset_expr.X_add_number -= expr1.X_add_number;
+
+ load_register (tempreg, &expr1, HAVE_64BIT_ADDRESSES);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ if (offbits == 0)
+ {
+ if (offset_expr.X_add_number != 0)
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN,
+ "t,r,j", tempreg, tempreg, BFD_RELOC_LO16);
+ macro_build (NULL, s, fmt, op[0], tempreg);
+ }
+ else if (offbits == 16)
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
+ else
+ macro_build (NULL, s, fmt, op[0],
+ (int) offset_expr.X_add_number, tempreg);
+ }
+ else if (offbits != 16)
+ {
+ /* The offset field is too narrow to be used for a low-part
+ relocation, so load the whole address into the auxillary
+ register. */
+ load_address (tempreg, &offset_expr, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ if (offbits == 0)
+ macro_build (NULL, s, fmt, op[0], tempreg);
+ else
+ macro_build (NULL, s, fmt, op[0], 0, tempreg);
+ }
+ else if (mips_pic == NO_PIC)
+ {
+ /* If this is a reference to a GP relative symbol, and there
+ is no base register, we want
+ <op> op[0],<sym>($gp) (BFD_RELOC_GPREL16)
+ Otherwise, if there is no base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a constant, we need two instructions anyhow,
+ so we always use the latter form.
+
+ If we have a base register, and this is a reference to a
+ GP relative symbol, we want
+ addu $tempreg,$breg,$gp
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_GPREL16)
+ Otherwise we want
+ lui $tempreg,<sym> (BFD_RELOC_HI16_S)
+ addu $tempreg,$tempreg,$breg
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+ With a constant we always use the latter case.
+
+ With 64bit address space and no base register and $at usable,
+ we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll32 $tempreg,0
+ daddu $tempreg,$at
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ daddu $at,$breg
+ dsll32 $tempreg,0
+ daddu $tempreg,$at
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+
+ Without $at we can't generate the optimal path for superscalar
+ processors here since this would require two temporary registers.
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+ If we have a base register, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_HIGHEST)
+ daddiu $tempreg,<sym> (BFD_RELOC_MIPS_HIGHER)
+ dsll $tempreg,16
+ daddiu $tempreg,<sym> (BFD_RELOC_HI16_S)
+ dsll $tempreg,16
+ daddu $tempreg,$tempreg,$breg
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_LO16)
+
+ For GP relative symbols in 64bit address space we can use
+ the same sequence as in 32bit address space. */
+ if (HAVE_64BIT_SYMBOLS)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (breg == 0)
+ {
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_GPREL16, mips_gp_register);
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_GPREL16, tempreg);
+ }
+ relax_switch ();
+ }
+
+ if (used_at == 0 && mips_opts.at)
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "lui", LUI_FMT, AT,
+ BFD_RELOC_HI16_S);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_HIGHER);
+ if (breg != 0)
+ macro_build (NULL, "daddu", "d,v,t", AT, AT, breg);
+ macro_build (NULL, "dsll32", SHFT_FMT, tempreg, tempreg, 0);
+ macro_build (NULL, "daddu", "d,v,t", tempreg, tempreg, AT);
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_LO16,
+ tempreg);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_HIGHEST);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_MIPS_HIGHER);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ macro_build (&offset_expr, "daddiu", "t,r,j", tempreg,
+ tempreg, BFD_RELOC_HI16_S);
+ macro_build (NULL, "dsll", SHFT_FMT, tempreg, tempreg, 16);
+ if (breg != 0)
+ macro_build (NULL, "daddu", "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_LO16, tempreg);
+ }
+
+ if (mips_relax.sequence)
+ relax_end ();
+ break;
+ }
+
+ if (breg == 0)
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, s, fmt, op[0], BFD_RELOC_GPREL16,
+ mips_gp_register);
+ relax_switch ();
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else
+ {
+ if ((valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, breg, mips_gp_register);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_GPREL16, tempreg);
+ relax_switch ();
+ }
+ macro_build_lui (&offset_expr, tempreg);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_LO16, tempreg);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ }
+ else if (!mips_big_got)
+ {
+ int lw_reloc_type = (int) BFD_RELOC_MIPS_GOT16;
+
+ /* If this is a reference to an external symbol, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> op[0],0($tempreg)
+
+ For NewABI, we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST)
+
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ gas_assert (offset_expr.X_op == O_symbol);
+ if (HAVE_NEWABI)
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+ break;
+ }
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ lw_reloc_type, mips_gp_register);
+ load_delay_nop ();
+ relax_start (offset_expr.X_add_symbol);
+ relax_switch ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_LO16);
+ relax_end ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_big_got && !HAVE_NEWABI)
+ {
+ int gpdelay;
+
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> op[0],0($tempreg)
+ Otherwise we want
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ addiu $tempreg,$tempreg,<sym> (BFD_RELOC_LO16)
+ <op> op[0],0($tempreg)
+ If there is a base register, we add it to $tempreg before
+ the <op>. If there is a constant, we stick it in the
+ <op> instruction. We don't handle constants larger than
+ 16 bits, because we have no way to load the upper 16 bits
+ (actually, we could handle them for the subset of cases
+ in which we are not using $at). */
+ gas_assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ gpdelay = reg_needs_delay (mips_gp_register);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_LO16, tempreg);
+ relax_switch ();
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", tempreg,
+ tempreg, BFD_RELOC_LO16);
+ relax_end ();
+
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
+ }
+ else if (mips_big_got && HAVE_NEWABI)
+ {
+ /* If this is a reference to an external symbol, we want
+ lui $tempreg,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ add $tempreg,$tempreg,$gp
+ lw $tempreg,<sym>($tempreg) (BFD_RELOC_MIPS_GOT_LO16)
+ <op> op[0],<ofst>($tempreg)
+ Otherwise, for local symbols, we want:
+ lw $tempreg,<sym>($gp) (BFD_RELOC_MIPS_GOT_PAGE)
+ <op> op[0],<sym>($tempreg) (BFD_RELOC_MIPS_GOT_OFST) */
+ gas_assert (offset_expr.X_op == O_symbol);
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT, tempreg,
+ BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", tempreg, tempreg,
+ mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_LO16, tempreg);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&expr1, s, fmt, op[0], BFD_RELOC_LO16, tempreg);
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", tempreg,
+ BFD_RELOC_MIPS_GOT_PAGE, mips_gp_register);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ macro_build (&offset_expr, s, fmt, op[0],
+ BFD_RELOC_MIPS_GOT_OFST, tempreg);
+ relax_end ();
+ }
+ else
+ abort ();
+
+ break;
+
+ case M_JRADDIUSP:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ start_noreorder ();
+ macro_build (NULL, "jr", "s", RA);
+ expr1.X_add_number = op[0] << 2;
+ macro_build (&expr1, "addiu", "t,r,j", SP, SP, BFD_RELOC_LO16);
+ end_noreorder ();
+ break;
+
+ case M_JRC:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ macro_build (NULL, "jr", "s", op[0]);
+ if (mips_opts.noreorder)
+ macro_build (NULL, "nop", "");
+ break;
+
+ case M_LI:
+ case M_LI_S:
+ load_register (op[0], &imm_expr, 0);
+ break;
+
+ case M_DLI:
+ load_register (op[0], &imm_expr, 1);
+ break;
+
+ case M_LI_SS:
+ if (imm_expr.X_op == O_constant)
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, op[0]);
+ break;
+ }
+ else
+ {
+ gas_assert (imm_expr.X_op == O_absent
+ && offset_expr.X_op == O_symbol
+ && strcmp (segment_name (S_GET_SEGMENT
+ (offset_expr.X_add_symbol)),
+ ".lit4") == 0
+ && offset_expr.X_add_number == 0);
+ macro_build (&offset_expr, "lwc1", "T,o(b)", op[0],
+ BFD_RELOC_MIPS_LITERAL, mips_gp_register);
+ break;
+ }
+
+ case M_LI_D:
+ /* Check if we have a constant in IMM_EXPR. If the GPRs are 64 bits
+ wide, IMM_EXPR is the entire value. Otherwise IMM_EXPR is the high
+ order 32 bits of the value and the low order 32 bits are either
+ zero or in OFFSET_EXPR. */
+ if (imm_expr.X_op == O_constant)
+ {
+ if (GPR_SIZE == 64)
+ load_register (op[0], &imm_expr, 1);
+ else
+ {
+ int hreg, lreg;
+
+ if (target_big_endian)
+ {
+ hreg = op[0];
+ lreg = op[0] + 1;
+ }
+ else
+ {
+ hreg = op[0] + 1;
+ lreg = op[0];
+ }
+
+ if (hreg <= 31)
+ load_register (hreg, &imm_expr, 0);
+ if (lreg <= 31)
+ {
+ if (offset_expr.X_op == O_absent)
+ move_register (lreg, 0);
+ else
+ {
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (lreg, &offset_expr, 0);
+ }
+ }
+ }
+ break;
+ }
+ gas_assert (imm_expr.X_op == O_absent);
+
+ /* We know that sym is in the .rdata section. First we get the
+ upper 16 bits of the address. */
+ if (mips_pic == NO_PIC)
+ {
+ macro_build_lui (&offset_expr, AT);
+ used_at = 1;
+ }
+ else
+ {
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ used_at = 1;
+ }
+
+ /* Now we load the register(s). */
+ if (GPR_SIZE == 64)
+ {
+ used_at = 1;
+ macro_build (&offset_expr, "ld", "t,o(b)", op[0],
+ BFD_RELOC_LO16, AT);
+ }
+ else
+ {
+ used_at = 1;
+ macro_build (&offset_expr, "lw", "t,o(b)", op[0],
+ BFD_RELOC_LO16, AT);
+ if (op[0] != RA)
+ {
+ /* FIXME: How in the world do we deal with the possible
+ overflow here? */
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, "lw", "t,o(b)",
+ op[0] + 1, BFD_RELOC_LO16, AT);
+ }
+ }
+ break;
+
+ case M_LI_DD:
+ /* Check if we have a constant in IMM_EXPR. If the FPRs are 64 bits
+ wide, IMM_EXPR is the entire value and the GPRs are known to be 64
+ bits wide as well. Otherwise IMM_EXPR is the high order 32 bits of
+ the value and the low order 32 bits are either zero or in
+ OFFSET_EXPR. */
+ if (imm_expr.X_op == O_constant)
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, FPR_SIZE == 64);
+ if (FPR_SIZE == 64 && GPR_SIZE == 64)
+ macro_build (NULL, "dmtc1", "t,S", AT, op[0]);
+ else
+ {
+ if (ISA_HAS_MXHC1 (mips_opts.isa))
+ macro_build (NULL, "mthc1", "t,G", AT, op[0]);
+ else if (FPR_SIZE != 32)
+ as_bad (_("Unable to generate `%s' compliant code "
+ "without mthc1"),
+ (FPR_SIZE == 64) ? "fp64" : "fpxx");
+ else
+ macro_build (NULL, "mtc1", "t,G", AT, op[0] + 1);
+ if (offset_expr.X_op == O_absent)
+ macro_build (NULL, "mtc1", "t,G", 0, op[0]);
+ else
+ {
+ gas_assert (offset_expr.X_op == O_constant);
+ load_register (AT, &offset_expr, 0);
+ macro_build (NULL, "mtc1", "t,G", AT, op[0]);
+ }
+ }
+ break;
+ }
+
+ gas_assert (imm_expr.X_op == O_absent
+ && offset_expr.X_op == O_symbol
+ && offset_expr.X_add_number == 0);
+ s = segment_name (S_GET_SEGMENT (offset_expr.X_add_symbol));
+ if (strcmp (s, ".lit8") == 0)
+ {
+ op[2] = mips_gp_register;
+ offset_reloc[0] = BFD_RELOC_MIPS_LITERAL;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ }
+ else
+ {
+ gas_assert (strcmp (s, RDATA_SECTION_NAME) == 0);
+ used_at = 1;
+ if (mips_pic != NO_PIC)
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ else
+ {
+ /* FIXME: This won't work for a 64 bit address. */
+ macro_build_lui (&offset_expr, AT);
+ }
+
+ op[2] = AT;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ }
+ align = 8;
+ /* Fall through */
+
+ case M_L_DAB:
+ /*
+ * The MIPS assembler seems to check for X_add_number not
+ * being double aligned and generating:
+ * lui at,%hi(foo+1)
+ * addu at,at,v1
+ * addiu at,at,%lo(foo+1)
+ * lwc1 f2,0(at)
+ * lwc1 f3,4(at)
+ * But, the resulting address is the same after relocation so why
+ * generate the extra instruction?
+ */
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ fmt = "T,o(b)";
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
+ {
+ s = "ldc1";
+ goto ld_st;
+ }
+ s = "lwc1";
+ goto ldd_std;
+
+ case M_S_DAB:
+ gas_assert (!mips_opts.micromips);
+ /* Itbl support may require additional care here. */
+ coproc = 1;
+ fmt = "T,o(b)";
+ if (CPU_HAS_LDC1_SDC1 (mips_opts.arch))
+ {
+ s = "sdc1";
+ goto ld_st;
+ }
+ s = "swc1";
+ goto ldd_std;
+
+ case M_LQ_AB:
+ fmt = "t,o(b)";
+ s = "lq";
+ goto ld;
+
+ case M_SQ_AB:
+ fmt = "t,o(b)";
+ s = "sq";
+ goto ld_st;
+
+ case M_LD_AB:
+ fmt = "t,o(b)";
+ if (GPR_SIZE == 64)
+ {
+ s = "ld";
+ goto ld;
+ }
+ s = "lw";
+ goto ldd_std;
+
+ case M_SD_AB:
+ fmt = "t,o(b)";
+ if (GPR_SIZE == 64)
+ {
+ s = "sd";
+ goto ld_st;
+ }
+ s = "sw";
+
+ ldd_std:
+ /* Even on a big endian machine $fn comes before $fn+1. We have
+ to adjust when loading from memory. We set coproc if we must
+ load $fn+1 first. */
+ /* Itbl support may require additional care here. */
+ if (!target_big_endian)
+ coproc = 0;
+
+ breg = op[2];
+ if (small_offset_p (0, align, 16))
+ {
+ ep = &offset_expr;
+ if (!small_offset_p (4, align, 16))
+ {
+ macro_build (&offset_expr, ADDRESS_ADDI_INSN, "t,r,j", AT, breg,
+ -1, offset_reloc[0], offset_reloc[1],
+ offset_reloc[2]);
+ expr1.X_add_number = 0;
+ ep = &expr1;
+ breg = AT;
+ used_at = 1;
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ }
+ if (strcmp (s, "lw") == 0 && op[0] == breg)
+ {
+ ep->X_add_number += 4;
+ macro_build (ep, s, fmt, op[0] + 1, -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ ep->X_add_number -= 4;
+ macro_build (ep, s, fmt, op[0], -1, offset_reloc[0],
+ offset_reloc[1], offset_reloc[2], breg);
+ }
+ else
+ {
+ macro_build (ep, s, fmt, coproc ? op[0] + 1 : op[0], -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2],
+ breg);
+ ep->X_add_number += 4;
+ macro_build (ep, s, fmt, coproc ? op[0] : op[0] + 1, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2],
+ breg);
+ }
+ break;
+ }
+
+ if (offset_expr.X_op != O_symbol
+ && offset_expr.X_op != O_constant)
+ {
+ as_bad (_("expression too complex"));
+ offset_expr.X_op = O_constant;
+ }
+
+ if (HAVE_32BIT_ADDRESSES
+ && !IS_SEXT_32BIT_NUM (offset_expr.X_add_number))
+ {
+ char value [32];
+
+ sprintf_vma (value, offset_expr.X_add_number);
+ as_bad (_("number (0x%s) larger than 32 bits"), value);
+ }
+
+ if (mips_pic == NO_PIC || offset_expr.X_op == O_constant)
+ {
+ /* If this is a reference to a GP relative symbol, we want
+ <op> op[0],<sym>($gp) (BFD_RELOC_GPREL16)
+ <op> op[0]+1,<sym>+4($gp) (BFD_RELOC_GPREL16)
+ If we have a base register, we use this
+ addu $at,$breg,$gp
+ <op> op[0],<sym>($at) (BFD_RELOC_GPREL16)
+ <op> op[0]+1,<sym>+4($at) (BFD_RELOC_GPREL16)
+ If this is not a GP relative symbol, we want
+ lui $at,<sym> (BFD_RELOC_HI16_S)
+ <op> op[0],<sym>($at) (BFD_RELOC_LO16)
+ <op> op[0]+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register, we add it to $at after the
+ lui instruction. If there is a constant, we always use
+ the last case. */
+ if (offset_expr.X_op == O_symbol
+ && (valueT) offset_expr.X_add_number <= MAX_GPREL_OFFSET
+ && !nopic_need_relax (offset_expr.X_add_symbol, 1))
+ {
+ relax_start (offset_expr.X_add_symbol);
+ if (breg == 0)
+ {
+ tempreg = mips_gp_register;
+ }
+ else
+ {
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, breg, mips_gp_register);
+ tempreg = AT;
+ used_at = 1;
+ }
+
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_GPREL16, tempreg);
+ offset_expr.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an
+ undesired nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_GPREL16, tempreg);
+ mips_optimize = hold_mips_optimize;
+
+ relax_switch ();
+
+ offset_expr.X_add_number -= 4;
+ }
+ used_at = 1;
+ if (offset_high_part (offset_expr.X_add_number, 16)
+ != offset_high_part (offset_expr.X_add_number + 4, 16))
+ {
+ load_address (AT, &offset_expr, &used_at);
+ offset_expr.X_op = O_constant;
+ offset_expr.X_add_number = 0;
+ }
+ else
+ macro_build_lui (&offset_expr, AT);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ /* FIXME: How do we handle overflow here? */
+ offset_expr.X_add_number += 4;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ if (mips_relax.sequence)
+ relax_end ();
+ }
+ else if (!mips_big_got)
+ {
+ /* If this is a reference to an external symbol, we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],0($at)
+ <op> op[0]+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],<sym>($at) (BFD_RELOC_LO16)
+ <op> op[0]+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ load_got_offset (AT, &offset_expr);
+ load_delay_nop ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+
+ /* Itbl support may require additional care here. */
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&expr1, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+ macro_build (&expr1, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ relax_switch ();
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ relax_end ();
+
+ mips_optimize = hold_mips_optimize;
+ }
+ else if (mips_big_got)
+ {
+ int gpdelay;
+
+ /* If this is a reference to an external symbol, we want
+ lui $at,<sym> (BFD_RELOC_MIPS_GOT_HI16)
+ addu $at,$at,$gp
+ lw $at,<sym>($at) (BFD_RELOC_MIPS_GOT_LO16)
+ nop
+ <op> op[0],0($at)
+ <op> op[0]+1,4($at)
+ Otherwise we want
+ lw $at,<sym>($gp) (BFD_RELOC_MIPS_GOT16)
+ nop
+ <op> op[0],<sym>($at) (BFD_RELOC_LO16)
+ <op> op[0]+1,<sym>+4($at) (BFD_RELOC_LO16)
+ If there is a base register we add it to $at before the
+ lwc1 instructions. If there is a constant we include it
+ in the lwc1 instructions. */
+ used_at = 1;
+ expr1.X_add_number = offset_expr.X_add_number;
+ offset_expr.X_add_number = 0;
+ if (expr1.X_add_number < -0x8000
+ || expr1.X_add_number >= 0x8000 - 4)
+ as_bad (_("PIC code offset overflow (max 16 signed bits)"));
+ gpdelay = reg_needs_delay (mips_gp_register);
+ relax_start (offset_expr.X_add_symbol);
+ macro_build (&offset_expr, "lui", LUI_FMT,
+ AT, BFD_RELOC_MIPS_GOT_HI16);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ AT, AT, mips_gp_register);
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)",
+ AT, BFD_RELOC_MIPS_GOT_LO16, AT);
+ load_delay_nop ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&expr1, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ expr1.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&expr1, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ expr1.X_add_number -= 4;
+
+ relax_switch ();
+ offset_expr.X_add_number = expr1.X_add_number;
+ if (gpdelay)
+ macro_build (NULL, "nop", "");
+ macro_build (&offset_expr, ADDRESS_LOAD_INSN, "t,o(b)", AT,
+ BFD_RELOC_MIPS_GOT16, mips_gp_register);
+ load_delay_nop ();
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", AT, breg, AT);
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] + 1 : op[0],
+ BFD_RELOC_LO16, AT);
+ offset_expr.X_add_number += 4;
+
+ /* Set mips_optimize to 2 to avoid inserting an undesired
+ nop. */
+ hold_mips_optimize = mips_optimize;
+ mips_optimize = 2;
+ /* Itbl support may require additional care here. */
+ macro_build (&offset_expr, s, fmt, coproc ? op[0] : op[0] + 1,
+ BFD_RELOC_LO16, AT);
+ mips_optimize = hold_mips_optimize;
+ relax_end ();
+ }
+ else
+ abort ();
+
+ break;
+
+ case M_SAA_AB:
+ s = "saa";
+ goto saa_saad;
+ case M_SAAD_AB:
+ s = "saad";
+ saa_saad:
+ gas_assert (!mips_opts.micromips);
+ offbits = 0;
+ fmt = "t,(b)";
+ goto ld_st;
+
+ /* New code added to support COPZ instructions.
+ This code builds table entries out of the macros in mip_opcodes.
+ R4000 uses interlocks to handle coproc delays.
+ Other chips (like the R3000) require nops to be inserted for delays.
+
+ FIXME: Currently, we require that the user handle delays.
+ In order to fill delay slots for non-interlocked chips,
+ we must have a way to specify delays based on the coprocessor.
+ Eg. 4 cycles if load coproc reg from memory, 1 if in cache, etc.
+ What are the side-effects of the cop instruction?
+ What cache support might we have and what are its effects?
+ Both coprocessor & memory require delays. how long???
+ What registers are read/set/modified?
+
+ If an itbl is provided to interpret cop instructions,
+ this knowledge can be encoded in the itbl spec. */
+
+ case M_COP0:
+ s = "c0";
+ goto copz;
+ case M_COP1:
+ s = "c1";
+ goto copz;
+ case M_COP2:
+ s = "c2";
+ goto copz;
+ case M_COP3:
+ s = "c3";
+ copz:
+ gas_assert (!mips_opts.micromips);
+ /* For now we just do C (same as Cz). The parameter will be
+ stored in insn_opcode by mips_ip. */
+ macro_build (NULL, s, "C", (int) ip->insn_opcode);
+ break;
+
+ case M_MOVE:
+ move_register (op[0], op[1]);
+ break;
+
+ case M_MOVEP:
+ gas_assert (mips_opts.micromips);
+ gas_assert (mips_opts.insn32);
+ move_register (micromips_to_32_reg_h_map1[op[0]],
+ micromips_to_32_reg_m_map[op[1]]);
+ move_register (micromips_to_32_reg_h_map2[op[0]],
+ micromips_to_32_reg_n_map[op[2]]);
+ break;
+
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ if (mips_opts.arch == CPU_R5900)
+ macro_build (NULL, dbl ? "dmultu" : "multu", "d,s,t", op[0], op[1],
+ op[2]);
+ else
+ {
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t", op[1], op[2]);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ }
+ break;
+
+ case M_DMUL_I:
+ dbl = 1;
+ case M_MUL_I:
+ /* The MIPS assembler some times generates shifts and adds. I'm
+ not trying to be that fancy. GCC should do this for us
+ anyway. */
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmult" : "mult", "s,t", op[1], AT);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ break;
+
+ case M_DMULO_I:
+ dbl = 1;
+ case M_MULO_I:
+ imm = 1;
+ goto do_mulo;
+
+ case M_DMULO:
+ dbl = 1;
+ case M_MULO:
+ do_mulo:
+ start_noreorder ();
+ used_at = 1;
+ if (imm)
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmult" : "mult", "s,t",
+ op[1], imm ? AT : op[2]);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ macro_build (NULL, dbl ? "dsra32" : "sra", SHFT_FMT, op[0], op[0], 31);
+ macro_build (NULL, "mfhi", MFHL_FMT, AT);
+ if (mips_trap)
+ macro_build (NULL, "tne", TRAP_FMT, op[0], AT, 6);
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "beq", "s,t,p", op[0], AT);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, "break", BRK_FMT, 6);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ end_noreorder ();
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ break;
+
+ case M_DMULOU_I:
+ dbl = 1;
+ case M_MULOU_I:
+ imm = 1;
+ goto do_mulou;
+
+ case M_DMULOU:
+ dbl = 1;
+ case M_MULOU:
+ do_mulou:
+ start_noreorder ();
+ used_at = 1;
+ if (imm)
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, dbl ? "dmultu" : "multu", "s,t",
+ op[1], imm ? AT : op[2]);
+ macro_build (NULL, "mfhi", MFHL_FMT, AT);
+ macro_build (NULL, "mflo", MFHL_FMT, op[0]);
+ if (mips_trap)
+ macro_build (NULL, "tne", TRAP_FMT, AT, ZERO, 6);
+ else
+ {
+ if (mips_opts.micromips)
+ micromips_label_expr (&label_expr);
+ else
+ label_expr.X_add_number = 8;
+ macro_build (&label_expr, "beq", "s,t,p", AT, ZERO);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, "break", BRK_FMT, 6);
+ if (mips_opts.micromips)
+ micromips_add_label ();
+ }
+ end_noreorder ();
+ break;
+
+ case M_DROL:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (op[0] == op[1])
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ tempreg = op[0];
+ macro_build (NULL, "dnegu", "d,w", tempreg, op[2]);
+ macro_build (NULL, "drorv", "d,t,s", op[0], op[1], tempreg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "dsrlv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "dsllv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
+
+ case M_ROL:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ if (op[0] == op[1])
+ {
+ tempreg = AT;
+ used_at = 1;
+ }
+ else
+ tempreg = op[0];
+ macro_build (NULL, "negu", "d,w", tempreg, op[2]);
+ macro_build (NULL, "rorv", "d,t,s", op[0], op[1], tempreg);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "subu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "srlv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "sllv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
+
+ case M_DROL_I:
+ {
+ unsigned int rot;
+ char *l;
+ char *rr;
+
+ rot = imm_expr.X_add_number & 0x3f;
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ rot = (64 - rot) & 0x3f;
+ if (rot >= 32)
+ macro_build (NULL, "dror32", SHFT_FMT, op[0], op[1], rot - 32);
+ else
+ macro_build (NULL, "dror", SHFT_FMT, op[0], op[1], rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", SHFT_FMT, op[0], op[1], 0);
+ break;
+ }
+ l = (rot < 0x20) ? "dsll" : "dsll32";
+ rr = ((0x40 - rot) < 0x20) ? "dsrl" : "dsrl32";
+ rot &= 0x1f;
+ used_at = 1;
+ macro_build (NULL, l, SHFT_FMT, AT, op[1], rot);
+ macro_build (NULL, rr, SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
+
+ case M_ROL_I:
+ {
+ unsigned int rot;
+
+ rot = imm_expr.X_add_number & 0x1f;
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "ror", SHFT_FMT, op[0], op[1],
+ (32 - rot) & 0x1f);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], 0);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "sll", SHFT_FMT, AT, op[1], rot);
+ macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
+
+ case M_DROR:
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ macro_build (NULL, "drorv", "d,t,s", op[0], op[1], op[2]);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "dsubu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "dsllv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "dsrlv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
+
+ case M_ROR:
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "rorv", "d,t,s", op[0], op[1], op[2]);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "subu", "d,v,t", AT, ZERO, op[2]);
+ macro_build (NULL, "sllv", "d,t,s", AT, op[1], AT);
+ macro_build (NULL, "srlv", "d,t,s", op[0], op[1], op[2]);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ break;
+
+ case M_DROR_I:
+ {
+ unsigned int rot;
+ char *l;
+ char *rr;
+
+ rot = imm_expr.X_add_number & 0x3f;
+ if (ISA_HAS_DROR (mips_opts.isa) || CPU_HAS_DROR (mips_opts.arch))
+ {
+ if (rot >= 32)
+ macro_build (NULL, "dror32", SHFT_FMT, op[0], op[1], rot - 32);
+ else
+ macro_build (NULL, "dror", SHFT_FMT, op[0], op[1], rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "dsrl", SHFT_FMT, op[0], op[1], 0);
+ break;
+ }
+ rr = (rot < 0x20) ? "dsrl" : "dsrl32";
+ l = ((0x40 - rot) < 0x20) ? "dsll" : "dsll32";
+ rot &= 0x1f;
+ used_at = 1;
+ macro_build (NULL, rr, SHFT_FMT, AT, op[1], rot);
+ macro_build (NULL, l, SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
+
+ case M_ROR_I:
+ {
+ unsigned int rot;
+
+ rot = imm_expr.X_add_number & 0x1f;
+ if (ISA_HAS_ROR (mips_opts.isa) || CPU_HAS_ROR (mips_opts.arch))
+ {
+ macro_build (NULL, "ror", SHFT_FMT, op[0], op[1], rot);
+ break;
+ }
+ if (rot == 0)
+ {
+ macro_build (NULL, "srl", SHFT_FMT, op[0], op[1], 0);
+ break;
+ }
+ used_at = 1;
+ macro_build (NULL, "srl", SHFT_FMT, AT, op[1], rot);
+ macro_build (NULL, "sll", SHFT_FMT, op[0], op[1], (0x20 - rot) & 0x1f);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
+
+ case M_SEQ:
+ if (op[1] == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[2], BFD_RELOC_LO16);
+ else if (op[2] == 0)
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[0], BFD_RELOC_LO16);
+ }
+ break;
+
+ case M_SEQ_I:
+ if (imm_expr.X_add_number == 0)
+ {
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ break;
+ }
+ if (op[1] == 0)
+ {
+ as_warn (_("instruction %s: result is always false"),
+ ip->insn_mo->name);
+ move_register (op[0], 0);
+ break;
+ }
+ if (CPU_HAS_SEQ (mips_opts.arch)
+ && -512 <= imm_expr.X_add_number
+ && imm_expr.X_add_number < 512)
+ {
+ macro_build (NULL, "seqi", "t,r,+Q", op[0], op[1],
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ if (imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ macro_build (&imm_expr, "xori", "t,r,i", op[0], op[1], BFD_RELOC_LO16);
+ else if (imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, GPR_SIZE == 32 ? "addiu" : "daddiu",
+ "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ }
+ else if (CPU_HAS_SEQ (mips_opts.arch))
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "seq", "d,v,t", op[0], op[1], AT);
+ break;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "sltiu", "t,r,j", op[0], op[0], BFD_RELOC_LO16);
+ break;
+
+ case M_SGE: /* X >= Y <==> not (X < Y) */
+ s = "slt";
+ goto sge;
+ case M_SGEU:
+ s = "sltu";
+ sge:
+ macro_build (NULL, s, "d,v,t", op[0], op[1], op[2]);
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
+
+ case M_SGE_I: /* X >= I <==> not (X < I) */
+ case M_SGEU_I:
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ macro_build (&imm_expr, mask == M_SGE_I ? "slti" : "sltiu", "t,r,j",
+ op[0], op[1], BFD_RELOC_LO16);
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, mask == M_SGE_I ? "slt" : "sltu", "d,v,t",
+ op[0], op[1], AT);
+ used_at = 1;
+ }
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
+
+ case M_SGT: /* X > Y <==> Y < X */
+ s = "slt";
+ goto sgt;
+ case M_SGTU:
+ s = "sltu";
+ sgt:
+ macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+ break;
+
+ case M_SGT_I: /* X > I <==> I < X */
+ s = "slt";
+ goto sgti;
+ case M_SGTU_I:
+ s = "sltu";
+ sgti:
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+ break;
+
+ case M_SLE: /* X <= Y <==> Y >= X <==> not (Y < X) */
+ s = "slt";
+ goto sle;
+ case M_SLEU:
+ s = "sltu";
+ sle:
+ macro_build (NULL, s, "d,v,t", op[0], op[2], op[1]);
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
+
+ case M_SLE_I: /* X <= I <==> I >= X <==> not (I < X) */
+ s = "slt";
+ goto slei;
+ case M_SLEU_I:
+ s = "sltu";
+ slei:
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s, "d,v,t", op[0], AT, op[1]);
+ macro_build (&expr1, "xori", "t,r,i", op[0], op[0], BFD_RELOC_LO16);
+ break;
+
+ case M_SLT_I:
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "slti", "t,r,j", op[0], op[1],
+ BFD_RELOC_LO16);
+ break;
+ }
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "slt", "d,v,t", op[0], op[1], AT);
+ break;
+
+ case M_SLTU_I:
+ if (imm_expr.X_add_number >= -0x8000
+ && imm_expr.X_add_number < 0x8000)
+ {
+ macro_build (&imm_expr, "sltiu", "t,r,j", op[0], op[1],
+ BFD_RELOC_LO16);
+ break;
+ }
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "sltu", "d,v,t", op[0], op[1], AT);
+ break;
+
+ case M_SNE:
+ if (op[1] == 0)
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[2]);
+ else if (op[2] == 0)
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
+ else
+ {
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], op[2]);
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
+ }
+ break;
+
+ case M_SNE_I:
+ if (imm_expr.X_add_number == 0)
+ {
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[1]);
+ break;
+ }
+ if (op[1] == 0)
+ {
+ as_warn (_("instruction %s: result is always true"),
+ ip->insn_mo->name);
+ macro_build (&expr1, GPR_SIZE == 32 ? "addiu" : "daddiu", "t,r,j",
+ op[0], 0, BFD_RELOC_LO16);
+ break;
+ }
+ if (CPU_HAS_SEQ (mips_opts.arch)
+ && -512 <= imm_expr.X_add_number
+ && imm_expr.X_add_number < 512)
+ {
+ macro_build (NULL, "snei", "t,r,+Q", op[0], op[1],
+ (int) imm_expr.X_add_number);
+ break;
+ }
+ if (imm_expr.X_add_number >= 0
+ && imm_expr.X_add_number < 0x10000)
+ {
+ macro_build (&imm_expr, "xori", "t,r,i", op[0], op[1],
+ BFD_RELOC_LO16);
+ }
+ else if (imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number < 0)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, GPR_SIZE == 32 ? "addiu" : "daddiu",
+ "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ }
+ else if (CPU_HAS_SEQ (mips_opts.arch))
+ {
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "sne", "d,v,t", op[0], op[1], AT);
+ break;
+ }
+ else
+ {
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, "xor", "d,v,t", op[0], op[1], AT);
+ used_at = 1;
+ }
+ macro_build (NULL, "sltu", "d,v,t", op[0], 0, op[0]);
+ break;
+
+ case M_SUB_I:
+ s = "addi";
+ s2 = "sub";
+ goto do_subi;
+ case M_SUBU_I:
+ s = "addiu";
+ s2 = "subu";
+ goto do_subi;
+ case M_DSUB_I:
+ dbl = 1;
+ s = "daddi";
+ s2 = "dsub";
+ if (!mips_opts.micromips)
+ goto do_subi;
+ if (imm_expr.X_add_number > -0x200
+ && imm_expr.X_add_number <= 0x200)
+ {
+ macro_build (NULL, s, "t,r,.", op[0], op[1],
+ (int) -imm_expr.X_add_number);
+ break;
+ }
+ goto do_subi_i;
+ case M_DSUBU_I:
+ dbl = 1;
+ s = "daddiu";
+ s2 = "dsubu";
+ do_subi:
+ if (imm_expr.X_add_number > -0x8000
+ && imm_expr.X_add_number <= 0x8000)
+ {
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, s, "t,r,j", op[0], op[1], BFD_RELOC_LO16);
+ break;
+ }
+ do_subi_i:
+ used_at = 1;
+ load_register (AT, &imm_expr, dbl);
+ macro_build (NULL, s2, "d,v,t", op[0], op[1], AT);
+ break;
+
+ case M_TEQ_I:
+ s = "teq";
+ goto trap;
+ case M_TGE_I:
+ s = "tge";
+ goto trap;
+ case M_TGEU_I:
+ s = "tgeu";
+ goto trap;
+ case M_TLT_I:
+ s = "tlt";
+ goto trap;
+ case M_TLTU_I:
+ s = "tltu";
+ goto trap;
+ case M_TNE_I:
+ s = "tne";
+ trap:
+ used_at = 1;
+ load_register (AT, &imm_expr, GPR_SIZE == 64);
+ macro_build (NULL, s, "s,t", op[0], AT);
+ break;
+
+ case M_TRUNCWS:
+ case M_TRUNCWD:
+ gas_assert (!mips_opts.micromips);
+ gas_assert (mips_opts.isa == ISA_MIPS1);
+ used_at = 1;
+
+ /*
+ * Is the double cfc1 instruction a bug in the mips assembler;
+ * or is there a reason for it?
+ */
+ start_noreorder ();
+ macro_build (NULL, "cfc1", "t,G", op[2], RA);
+ macro_build (NULL, "cfc1", "t,G", op[2], RA);
+ macro_build (NULL, "nop", "");
+ expr1.X_add_number = 3;
+ macro_build (&expr1, "ori", "t,r,i", AT, op[2], BFD_RELOC_LO16);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "xori", "t,r,i", AT, AT, BFD_RELOC_LO16);
+ macro_build (NULL, "ctc1", "t,G", AT, RA);
+ macro_build (NULL, "nop", "");
+ macro_build (NULL, mask == M_TRUNCWD ? "cvt.w.d" : "cvt.w.s", "D,S",
+ op[0], op[1]);
+ macro_build (NULL, "ctc1", "t,G", op[2], RA);
+ macro_build (NULL, "nop", "");
+ end_noreorder ();
+ break;
+
+ case M_ULH_AB:
+ s = "lb";
+ s2 = "lbu";
+ off = 1;
+ goto uld_st;
+ case M_ULHU_AB:
+ s = "lbu";
+ s2 = "lbu";
+ off = 1;
+ goto uld_st;
+ case M_ULW_AB:
+ s = "lwl";
+ s2 = "lwr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 3;
+ goto uld_st;
+ case M_ULD_AB:
+ s = "ldl";
+ s2 = "ldr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 7;
+ goto uld_st;
+ case M_USH_AB:
+ s = "sb";
+ s2 = "sb";
+ off = 1;
+ ust = 1;
+ goto uld_st;
+ case M_USW_AB:
+ s = "swl";
+ s2 = "swr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 3;
+ ust = 1;
+ goto uld_st;
+ case M_USD_AB:
+ s = "sdl";
+ s2 = "sdr";
+ offbits = (mips_opts.micromips ? 12 : 16);
+ off = 7;
+ ust = 1;
+
+ uld_st:
+ breg = op[2];
+ large_offset = !small_offset_p (off, align, offbits);
+ ep = &offset_expr;
+ expr1.X_add_number = 0;
+ if (large_offset)
+ {
+ used_at = 1;
+ tempreg = AT;
+ if (small_offset_p (0, align, 16))
+ macro_build (ep, ADDRESS_ADDI_INSN, "t,r,j", tempreg, breg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2]);
+ else
+ {
+ load_address (tempreg, ep, &used_at);
+ if (breg != 0)
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t",
+ tempreg, tempreg, breg);
+ }
+ offset_reloc[0] = BFD_RELOC_LO16;
+ offset_reloc[1] = BFD_RELOC_UNUSED;
+ offset_reloc[2] = BFD_RELOC_UNUSED;
+ breg = tempreg;
+ tempreg = op[0];
+ ep = &expr1;
+ }
+ else if (!ust && op[0] == breg)
+ {
+ used_at = 1;
+ tempreg = AT;
+ }
+ else
+ tempreg = op[0];
+
+ if (off == 1)
+ goto ulh_sh;
+
+ if (!target_big_endian)
+ ep->X_add_number += off;
+ if (offbits == 12)
+ macro_build (NULL, s, "t,~(b)", tempreg, (int) ep->X_add_number, breg);
+ else
+ macro_build (ep, s, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
+
+ if (!target_big_endian)
+ ep->X_add_number -= off;
+ else
+ ep->X_add_number += off;
+ if (offbits == 12)
+ macro_build (NULL, s2, "t,~(b)",
+ tempreg, (int) ep->X_add_number, breg);
+ else
+ macro_build (ep, s2, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
+
+ /* If necessary, move the result in tempreg to the final destination. */
+ if (!ust && op[0] != tempreg)
+ {
+ /* Protect second load's delay slot. */
+ load_delay_nop ();
+ move_register (op[0], tempreg);
+ }
+ break;
+
+ ulh_sh:
+ used_at = 1;
+ if (target_big_endian == ust)
+ ep->X_add_number += off;
+ tempreg = ust || large_offset ? op[0] : AT;
+ macro_build (ep, s, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
+
+ /* For halfword transfers we need a temporary register to shuffle
+ bytes. Unfortunately for M_USH_A we have none available before
+ the next store as AT holds the base address. We deal with this
+ case by clobbering TREG and then restoring it as with ULH. */
+ tempreg = ust == large_offset ? op[0] : AT;
+ if (ust)
+ macro_build (NULL, "srl", SHFT_FMT, tempreg, op[0], 8);
+
+ if (target_big_endian == ust)
+ ep->X_add_number -= off;
+ else
+ ep->X_add_number += off;
+ macro_build (ep, s2, "t,o(b)", tempreg, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], breg);
+
+ /* For M_USH_A re-retrieve the LSB. */
+ if (ust && large_offset)
+ {
+ if (target_big_endian)
+ ep->X_add_number += off;
+ else
+ ep->X_add_number -= off;
+ macro_build (&expr1, "lbu", "t,o(b)", AT, -1,
+ offset_reloc[0], offset_reloc[1], offset_reloc[2], AT);
+ }
+ /* For ULH and M_USH_A OR the LSB in. */
+ if (!ust || large_offset)
+ {
+ tempreg = !large_offset ? AT : op[0];
+ macro_build (NULL, "sll", SHFT_FMT, tempreg, tempreg, 8);
+ macro_build (NULL, "or", "d,v,t", op[0], op[0], AT);
+ }
+ break;
+
+ default:
+ /* FIXME: Check if this is one of the itbl macros, since they
+ are added dynamically. */
+ as_bad (_("macro %s not implemented yet"), ip->insn_mo->name);
+ break;
+ }
+ if (!mips_opts.at && used_at)
+ as_bad (_("macro used $at after \".set noat\""));
+}
+
+/* Implement macros in mips16 mode. */
+
+static void
+mips16_macro (struct mips_cl_insn *ip)
+{
+ const struct mips_operand_array *operands;
+ int mask;
+ int tmp;
+ expressionS expr1;
+ int dbl;
+ const char *s, *s2, *s3;
+ unsigned int op[MAX_OPERANDS];
+ unsigned int i;
+
+ mask = ip->insn_mo->mask;
+
+ operands = insn_operands (ip);
+ for (i = 0; i < MAX_OPERANDS; i++)
+ if (operands->operand[i])
+ op[i] = insn_extract_operand (ip, operands->operand[i]);
+ else
+ op[i] = -1;
+
+ expr1.X_op = O_constant;
+ expr1.X_op_symbol = NULL;
+ expr1.X_add_symbol = NULL;
+ expr1.X_add_number = 1;
+
+ dbl = 0;
+
+ switch (mask)
+ {
+ default:
+ abort ();
+
+ case M_DDIV_3:
+ dbl = 1;
+ case M_DIV_3:
+ s = "mflo";
+ goto do_div3;
+ case M_DREM_3:
+ dbl = 1;
+ case M_REM_3:
+ s = "mfhi";
+ do_div3:
+ start_noreorder ();
+ macro_build (NULL, dbl ? "ddiv" : "div", "0,x,y", op[1], op[2]);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", op[2]);
+ macro_build (NULL, "break", "6", 7);
+
+ /* FIXME: The normal code checks for of -1 / -0x80000000 here,
+ since that causes an overflow. We should do that as well,
+ but I don't see how to do the comparisons without a temporary
+ register. */
+ end_noreorder ();
+ macro_build (NULL, s, "x", op[0]);
+ break;
+
+ case M_DIVU_3:
+ s = "divu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_REMU_3:
+ s = "divu";
+ s2 = "mfhi";
+ goto do_divu3;
+ case M_DDIVU_3:
+ s = "ddivu";
+ s2 = "mflo";
+ goto do_divu3;
+ case M_DREMU_3:
+ s = "ddivu";
+ s2 = "mfhi";
+ do_divu3:
+ start_noreorder ();
+ macro_build (NULL, s, "0,x,y", op[1], op[2]);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bnez", "x,p", op[2]);
+ macro_build (NULL, "break", "6", 7);
+ end_noreorder ();
+ macro_build (NULL, s2, "x", op[0]);
+ break;
+
+ case M_DMUL:
+ dbl = 1;
+ case M_MUL:
+ macro_build (NULL, dbl ? "dmultu" : "multu", "x,y", op[1], op[2]);
+ macro_build (NULL, "mflo", "x", op[0]);
+ break;
+
+ case M_DSUBU_I:
+ dbl = 1;
+ goto do_subu;
+ case M_SUBU_I:
+ do_subu:
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, dbl ? "daddiu" : "addiu", "y,x,4", op[0], op[1]);
+ break;
+
+ case M_SUBU_I_2:
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "addiu", "x,k", op[0]);
+ break;
+
+ case M_DSUBU_I_2:
+ imm_expr.X_add_number = -imm_expr.X_add_number;
+ macro_build (&imm_expr, "daddiu", "y,j", op[0]);
+ break;
+
+ case M_BEQ:
+ s = "cmp";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BNE:
+ s = "cmp";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLTU:
+ s = "sltu";
+ s2 = "btnez";
+ goto do_branch;
+ case M_BLE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BLEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_reverse_branch;
+ case M_BGE:
+ s = "slt";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGEU:
+ s = "sltu";
+ s2 = "bteqz";
+ goto do_branch;
+ case M_BGT:
+ s = "slt";
+ s2 = "btnez";
+ goto do_reverse_branch;
+ case M_BGTU:
+ s = "sltu";
+ s2 = "btnez";
+
+ do_reverse_branch:
+ tmp = op[1];
+ op[1] = op[0];
+ op[0] = tmp;
+
+ do_branch:
+ macro_build (NULL, s, "x,y", op[0], op[1]);
+ macro_build (&offset_expr, s2, "p");
+ break;
+
+ case M_BEQ_I:
+ s = "cmpi";
+ s2 = "bteqz";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BNE_I:
+ s = "cmpi";
+ s2 = "btnez";
+ s3 = "x,U";
+ goto do_branch_i;
+ case M_BLT_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLTU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BLE_I:
+ s = "slti";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BLEU_I:
+ s = "sltiu";
+ s2 = "btnez";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGE_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGEU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_branch_i;
+ case M_BGT_I:
+ s = "slti";
+ s2 = "bteqz";
+ s3 = "x,8";
+ goto do_addone_branch_i;
+ case M_BGTU_I:
+ s = "sltiu";
+ s2 = "bteqz";
+ s3 = "x,8";
+
+ do_addone_branch_i:
+ ++imm_expr.X_add_number;
+
+ do_branch_i:
+ macro_build (&imm_expr, s, s3, op[0]);
+ macro_build (&offset_expr, s2, "p");
+ break;
+
+ case M_ABS:
+ expr1.X_add_number = 0;
+ macro_build (&expr1, "slti", "x,8", op[1]);
+ if (op[0] != op[1])
+ macro_build (NULL, "move", "y,X", op[0], mips16_to_32_reg_map[op[1]]);
+ expr1.X_add_number = 2;
+ macro_build (&expr1, "bteqz", "p");
+ macro_build (NULL, "neg", "x,w", op[0], op[0]);
+ break;
+ }
+}
+
+/* Look up instruction [START, START + LENGTH) in HASH. Record any extra
+ opcode bits in *OPCODE_EXTRA. */
+
+static struct mips_opcode *
+mips_lookup_insn (struct hash_control *hash, const char *start,
+ ssize_t length, unsigned int *opcode_extra)
+{
+ char *name, *dot, *p;
+ unsigned int mask, suffix;
+ ssize_t opend;
+ struct mips_opcode *insn;
+
+ /* Make a copy of the instruction so that we can fiddle with it. */
+ name = alloca (length + 1);
+ memcpy (name, start, length);
+ name[length] = '\0';
+
+ /* Look up the instruction as-is. */
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ if (insn)
+ return insn;
+
+ dot = strchr (name, '.');
+ if (dot && dot[1])
+ {
+ /* Try to interpret the text after the dot as a VU0 channel suffix. */
+ p = mips_parse_vu0_channels (dot + 1, &mask);
+ if (*p == 0 && mask != 0)
+ {
+ *dot = 0;
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ *dot = '.';
+ if (insn && (insn->pinfo2 & INSN2_VU0_CHANNEL_SUFFIX) != 0)
+ {
+ *opcode_extra |= mask << mips_vu0_channel_mask.lsb;
+ return insn;
+ }
+ }
+ }
+
+ if (mips_opts.micromips)
+ {
+ /* See if there's an instruction size override suffix,
+ either `16' or `32', at the end of the mnemonic proper,
+ that defines the operation, i.e. before the first `.'
+ character if any. Strip it and retry. */
+ opend = dot != NULL ? dot - name : length;
+ if (opend >= 3 && name[opend - 2] == '1' && name[opend - 1] == '6')
+ suffix = 2;
+ else if (name[opend - 2] == '3' && name[opend - 1] == '2')
+ suffix = 4;
+ else
+ suffix = 0;
+ if (suffix)
+ {
+ memcpy (name + opend - 2, name + opend, length - opend + 1);
+ insn = (struct mips_opcode *) hash_find (hash, name);
+ if (insn)
+ {
+ forced_insn_length = suffix;
+ return insn;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/* Assemble an instruction into its binary format. If the instruction
+ is a macro, set imm_expr and offset_expr to the values associated
+ with "I" and "A" operands respectively. Otherwise store the value
+ of the relocatable field (if any) in offset_expr. In both cases
+ set offset_reloc to the relocation operators applied to offset_expr. */
+
+static void
+mips_ip (char *str, struct mips_cl_insn *insn)
+{
+ const struct mips_opcode *first, *past;
+ struct hash_control *hash;
+ char format;
+ size_t end;
+ struct mips_operand_token *tokens;
+ unsigned int opcode_extra;
+
+ if (mips_opts.micromips)
+ {
+ hash = micromips_op_hash;
+ past = &micromips_opcodes[bfd_micromips_num_opcodes];
+ }
+ else
+ {
+ hash = op_hash;
+ past = &mips_opcodes[NUMOPCODES];
+ }
+ forced_insn_length = 0;
+ opcode_extra = 0;
+
+ /* We first try to match an instruction up to a space or to the end. */
+ for (end = 0; str[end] != '\0' && !ISSPACE (str[end]); end++)
+ continue;
+
+ first = mips_lookup_insn (hash, str, end, &opcode_extra);
+ if (first == NULL)
+ {
+ set_insn_error (0, _("unrecognized opcode"));
+ return;
+ }
+
+ if (strcmp (first->name, "li.s") == 0)
+ format = 'f';
+ else if (strcmp (first->name, "li.d") == 0)
+ format = 'd';
+ else
+ format = 0;
+ tokens = mips_parse_arguments (str + end, format);
+ if (!tokens)
+ return;
+
+ if (!match_insns (insn, first, past, tokens, opcode_extra, FALSE)
+ && !match_insns (insn, first, past, tokens, opcode_extra, TRUE))
+ set_insn_error (0, _("invalid operands"));
+
+ obstack_free (&mips_operand_tokens, tokens);
+}
+
+/* As for mips_ip, but used when assembling MIPS16 code.
+ Also set forced_insn_length to the resulting instruction size in
+ bytes if the user explicitly requested a small or extended instruction. */
+
+static void
+mips16_ip (char *str, struct mips_cl_insn *insn)
+{
+ char *end, *s, c;
+ struct mips_opcode *first;
+ struct mips_operand_token *tokens;
+
+ forced_insn_length = 0;
+
+ for (s = str; ISLOWER (*s); ++s)
+ ;
+ end = s;
+ c = *end;
+ switch (c)
+ {
+ case '\0':
+ break;
+
+ case ' ':
+ s++;
+ break;
+
+ case '.':
+ if (s[1] == 't' && s[2] == ' ')
+ {
+ forced_insn_length = 2;
+ s += 3;
+ break;
+ }
+ else if (s[1] == 'e' && s[2] == ' ')
+ {
+ forced_insn_length = 4;
+ s += 3;
+ break;
+ }
+ /* Fall through. */
+ default:
+ set_insn_error (0, _("unrecognized opcode"));
+ return;
+ }
+
+ if (mips_opts.noautoextend && !forced_insn_length)
+ forced_insn_length = 2;
+
+ *end = 0;
+ first = (struct mips_opcode *) hash_find (mips16_op_hash, str);
+ *end = c;
+
+ if (!first)
+ {
+ set_insn_error (0, _("unrecognized opcode"));
+ return;
+ }
+
+ tokens = mips_parse_arguments (s, 0);
+ if (!tokens)
+ return;
+
+ if (!match_mips16_insns (insn, first, tokens))
+ set_insn_error (0, _("invalid operands"));
+
+ obstack_free (&mips_operand_tokens, tokens);
+}
+
+/* Marshal immediate value VAL for an extended MIPS16 instruction.
+ NBITS is the number of significant bits in VAL. */
+
+static unsigned long
+mips16_immed_extend (offsetT val, unsigned int nbits)
+{
+ int extval;
+ if (nbits == 16)
+ {
+ extval = ((val >> 11) & 0x1f) | (val & 0x7e0);
+ val &= 0x1f;
+ }
+ else if (nbits == 15)
+ {
+ extval = ((val >> 11) & 0xf) | (val & 0x7f0);
+ val &= 0xf;
+ }
+ else
+ {
+ extval = ((val & 0x1f) << 6) | (val & 0x20);
+ val = 0;
+ }
+ return (extval << 16) | val;
+}
+
+/* Like decode_mips16_operand, but require the operand to be defined and
+ require it to be an integer. */
+
+static const struct mips_int_operand *
+mips16_immed_operand (int type, bfd_boolean extended_p)
+{
+ const struct mips_operand *operand;
+
+ operand = decode_mips16_operand (type, extended_p);
+ if (!operand || (operand->type != OP_INT && operand->type != OP_PCREL))
+ abort ();
+ return (const struct mips_int_operand *) operand;
+}
+
+/* Return true if SVAL fits OPERAND. RELOC is as for mips16_immed. */
+
+static bfd_boolean
+mips16_immed_in_range_p (const struct mips_int_operand *operand,
+ bfd_reloc_code_real_type reloc, offsetT sval)
+{
+ int min_val, max_val;
+
+ min_val = mips_int_operand_min (operand);
+ max_val = mips_int_operand_max (operand);
+ if (reloc != BFD_RELOC_UNUSED)
+ {
+ if (min_val < 0)
+ sval = SEXT_16BIT (sval);
+ else
+ sval &= 0xffff;
+ }
+
+ return (sval >= min_val
+ && sval <= max_val
+ && (sval & ((1 << operand->shift) - 1)) == 0);
+}
+
+/* Install immediate value VAL into MIPS16 instruction *INSN,
+ extending it if necessary. The instruction in *INSN may
+ already be extended.
+
+ RELOC is the relocation that produced VAL, or BFD_RELOC_UNUSED
+ if none. In the former case, VAL is a 16-bit number with no
+ defined signedness.
+
+ TYPE is the type of the immediate field. USER_INSN_LENGTH
+ is the length that the user requested, or 0 if none. */
+
+static void
+mips16_immed (char *file, unsigned int line, int type,
+ bfd_reloc_code_real_type reloc, offsetT val,
+ unsigned int user_insn_length, unsigned long *insn)
+{
+ const struct mips_int_operand *operand;
+ unsigned int uval, length;
+
+ operand = mips16_immed_operand (type, FALSE);
+ if (!mips16_immed_in_range_p (operand, reloc, val))
+ {
+ /* We need an extended instruction. */
+ if (user_insn_length == 2)
+ as_bad_where (file, line, _("invalid unextended operand value"));
+ else
+ *insn |= MIPS16_EXTEND;
+ }
+ else if (user_insn_length == 4)
+ {
+ /* The operand doesn't force an unextended instruction to be extended.
+ Warn if the user wanted an extended instruction anyway. */
+ *insn |= MIPS16_EXTEND;
+ as_warn_where (file, line,
+ _("extended operand requested but not required"));
+ }
+
+ length = mips16_opcode_length (*insn);
+ if (length == 4)
+ {
+ operand = mips16_immed_operand (type, TRUE);
+ if (!mips16_immed_in_range_p (operand, reloc, val))
+ as_bad_where (file, line,
+ _("operand value out of range for instruction"));
+ }
+ uval = ((unsigned int) val >> operand->shift) - operand->bias;
+ if (length == 2)
+ *insn = mips_insert_operand (&operand->root, *insn, uval);
+ else
+ *insn |= mips16_immed_extend (uval, operand->root.size);
+}
+
+struct percent_op_match
+{
+ const char *str;
+ bfd_reloc_code_real_type reloc;
+};
+
+static const struct percent_op_match mips_percent_op[] =
+{
+ {"%lo", BFD_RELOC_LO16},
+ {"%call_hi", BFD_RELOC_MIPS_CALL_HI16},
+ {"%call_lo", BFD_RELOC_MIPS_CALL_LO16},
+ {"%call16", BFD_RELOC_MIPS_CALL16},
+ {"%got_disp", BFD_RELOC_MIPS_GOT_DISP},
+ {"%got_page", BFD_RELOC_MIPS_GOT_PAGE},
+ {"%got_ofst", BFD_RELOC_MIPS_GOT_OFST},
+ {"%got_hi", BFD_RELOC_MIPS_GOT_HI16},
+ {"%got_lo", BFD_RELOC_MIPS_GOT_LO16},
+ {"%got", BFD_RELOC_MIPS_GOT16},
+ {"%gp_rel", BFD_RELOC_GPREL16},
+ {"%half", BFD_RELOC_16},
+ {"%highest", BFD_RELOC_MIPS_HIGHEST},
+ {"%higher", BFD_RELOC_MIPS_HIGHER},
+ {"%neg", BFD_RELOC_MIPS_SUB},
+ {"%tlsgd", BFD_RELOC_MIPS_TLS_GD},
+ {"%tlsldm", BFD_RELOC_MIPS_TLS_LDM},
+ {"%dtprel_hi", BFD_RELOC_MIPS_TLS_DTPREL_HI16},
+ {"%dtprel_lo", BFD_RELOC_MIPS_TLS_DTPREL_LO16},
+ {"%tprel_hi", BFD_RELOC_MIPS_TLS_TPREL_HI16},
+ {"%tprel_lo", BFD_RELOC_MIPS_TLS_TPREL_LO16},
+ {"%gottprel", BFD_RELOC_MIPS_TLS_GOTTPREL},
+ {"%hi", BFD_RELOC_HI16_S},
+ {"%pcrel_hi", BFD_RELOC_HI16_S_PCREL},
+ {"%pcrel_lo", BFD_RELOC_LO16_PCREL}
+};
+
+static const struct percent_op_match mips16_percent_op[] =
+{
+ {"%lo", BFD_RELOC_MIPS16_LO16},
+ {"%gprel", BFD_RELOC_MIPS16_GPREL},
+ {"%got", BFD_RELOC_MIPS16_GOT16},
+ {"%call16", BFD_RELOC_MIPS16_CALL16},
+ {"%hi", BFD_RELOC_MIPS16_HI16_S},
+ {"%tlsgd", BFD_RELOC_MIPS16_TLS_GD},
+ {"%tlsldm", BFD_RELOC_MIPS16_TLS_LDM},
+ {"%dtprel_hi", BFD_RELOC_MIPS16_TLS_DTPREL_HI16},
+ {"%dtprel_lo", BFD_RELOC_MIPS16_TLS_DTPREL_LO16},
+ {"%tprel_hi", BFD_RELOC_MIPS16_TLS_TPREL_HI16},
+ {"%tprel_lo", BFD_RELOC_MIPS16_TLS_TPREL_LO16},
+ {"%gottprel", BFD_RELOC_MIPS16_TLS_GOTTPREL}
+};
+
+
+/* Return true if *STR points to a relocation operator. When returning true,
+ move *STR over the operator and store its relocation code in *RELOC.
+ Leave both *STR and *RELOC alone when returning false. */
+
+static bfd_boolean
+parse_relocation (char **str, bfd_reloc_code_real_type *reloc)
+{
+ const struct percent_op_match *percent_op;
+ size_t limit, i;
+
+ if (mips_opts.mips16)
+ {
+ percent_op = mips16_percent_op;
+ limit = ARRAY_SIZE (mips16_percent_op);
+ }
+ else
+ {
+ percent_op = mips_percent_op;
+ limit = ARRAY_SIZE (mips_percent_op);
+ }
+
+ for (i = 0; i < limit; i++)
+ if (strncasecmp (*str, percent_op[i].str, strlen (percent_op[i].str)) == 0)
+ {
+ int len = strlen (percent_op[i].str);
+
+ if (!ISSPACE ((*str)[len]) && (*str)[len] != '(')
+ continue;
+
+ *str += strlen (percent_op[i].str);
+ *reloc = percent_op[i].reloc;
+
+ /* Check whether the output BFD supports this relocation.
+ If not, issue an error and fall back on something safe. */
+ if (!bfd_reloc_type_lookup (stdoutput, percent_op[i].reloc))
+ {
+ as_bad (_("relocation %s isn't supported by the current ABI"),
+ percent_op[i].str);
+ *reloc = BFD_RELOC_UNUSED;
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* Parse string STR as a 16-bit relocatable operand. Store the
+ expression in *EP and the relocations in the array starting
+ at RELOC. Return the number of relocation operators used.
+
+ On exit, EXPR_END points to the first character after the expression. */
+
+static size_t
+my_getSmallExpression (expressionS *ep, bfd_reloc_code_real_type *reloc,
+ char *str)
+{
+ bfd_reloc_code_real_type reversed_reloc[3];
+ size_t reloc_index, i;
+ int crux_depth, str_depth;
+ char *crux;
+
+ /* Search for the start of the main expression, recoding relocations
+ in REVERSED_RELOC. End the loop with CRUX pointing to the start
+ of the main expression and with CRUX_DEPTH containing the number
+ of open brackets at that point. */
+ reloc_index = -1;
+ str_depth = 0;
+ do
+ {
+ reloc_index++;
+ crux = str;
+ crux_depth = str_depth;
+
+ /* Skip over whitespace and brackets, keeping count of the number
+ of brackets. */
+ while (*str == ' ' || *str == '\t' || *str == '(')
+ if (*str++ == '(')
+ str_depth++;
+ }
+ while (*str == '%'
+ && reloc_index < (HAVE_NEWABI ? 3 : 1)
+ && parse_relocation (&str, &reversed_reloc[reloc_index]));
+
+ my_getExpression (ep, crux);
+ str = expr_end;
+
+ /* Match every open bracket. */
+ while (crux_depth > 0 && (*str == ')' || *str == ' ' || *str == '\t'))
+ if (*str++ == ')')
+ crux_depth--;
+
+ if (crux_depth > 0)
+ as_bad (_("unclosed '('"));
+
+ expr_end = str;
+
+ if (reloc_index != 0)
+ {
+ prev_reloc_op_frag = frag_now;
+ for (i = 0; i < reloc_index; i++)
+ reloc[i] = reversed_reloc[reloc_index - 1 - i];
+ }
+
+ return reloc_index;
+}
+
+static void
+my_getExpression (expressionS *ep, char *str)
+{
+ char *save_in;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ expression (ep);
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+static int support_64bit_objects(void)
+{
+ const char **list, **l;
+ int yes;
+
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+ if (strcmp (*l, ELF_TARGET ("elf64-", "big")) == 0
+ || strcmp (*l, ELF_TARGET ("elf64-", "little")) == 0)
+ break;
+ yes = (*l != NULL);
+ free (list);
+ return yes;
+}
+
+/* Set STRING_PTR (either &mips_arch_string or &mips_tune_string) to
+ NEW_VALUE. Warn if another value was already specified. Note:
+ we have to defer parsing the -march and -mtune arguments in order
+ to handle 'from-abi' correctly, since the ABI might be specified
+ in a later argument. */
+
+static void
+mips_set_option_string (const char **string_ptr, const char *new_value)
+{
+ if (*string_ptr != 0 && strcasecmp (*string_ptr, new_value) != 0)
+ as_warn (_("a different %s was already specified, is now %s"),
+ string_ptr == &mips_arch_string ? "-march" : "-mtune",
+ new_value);
+
+ *string_ptr = new_value;
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (mips_ases); i++)
+ if (c == mips_ases[i].option_on || c == mips_ases[i].option_off)
+ {
+ file_ase_explicit |= mips_set_ase (&mips_ases[i], &file_mips_opts,
+ c == mips_ases[i].option_on);
+ return 1;
+ }
+
+ switch (c)
+ {
+ case OPTION_CONSTRUCT_FLOATS:
+ mips_disable_float_construction = 0;
+ break;
+
+ case OPTION_NO_CONSTRUCT_FLOATS:
+ mips_disable_float_construction = 1;
+ break;
+
+ case OPTION_TRAP:
+ mips_trap = 1;
+ break;
+
+ case OPTION_BREAK:
+ mips_trap = 0;
+ break;
+
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+
+ case 'O':
+ if (arg == NULL)
+ mips_optimize = 1;
+ else if (arg[0] == '0')
+ mips_optimize = 0;
+ else if (arg[0] == '1')
+ mips_optimize = 1;
+ else
+ mips_optimize = 2;
+ break;
+
+ case 'g':
+ if (arg == NULL)
+ mips_debug = 2;
+ else
+ mips_debug = atoi (arg);
+ break;
+
+ case OPTION_MIPS1:
+ file_mips_opts.isa = ISA_MIPS1;
+ break;
+
+ case OPTION_MIPS2:
+ file_mips_opts.isa = ISA_MIPS2;
+ break;
+
+ case OPTION_MIPS3:
+ file_mips_opts.isa = ISA_MIPS3;
+ break;
+
+ case OPTION_MIPS4:
+ file_mips_opts.isa = ISA_MIPS4;
+ break;
+
+ case OPTION_MIPS5:
+ file_mips_opts.isa = ISA_MIPS5;
+ break;
+
+ case OPTION_MIPS32:
+ file_mips_opts.isa = ISA_MIPS32;
+ break;
+
+ case OPTION_MIPS32R2:
+ file_mips_opts.isa = ISA_MIPS32R2;
+ break;
+
+ case OPTION_MIPS32R3:
+ file_mips_opts.isa = ISA_MIPS32R3;
+ break;
+
+ case OPTION_MIPS32R5:
+ file_mips_opts.isa = ISA_MIPS32R5;
+ break;
+
+ case OPTION_MIPS32R6:
+ file_mips_opts.isa = ISA_MIPS32R6;
+ break;
+
+ case OPTION_MIPS64R2:
+ file_mips_opts.isa = ISA_MIPS64R2;
+ break;
+
+ case OPTION_MIPS64R3:
+ file_mips_opts.isa = ISA_MIPS64R3;
+ break;
+
+ case OPTION_MIPS64R5:
+ file_mips_opts.isa = ISA_MIPS64R5;
+ break;
+
+ case OPTION_MIPS64R6:
+ file_mips_opts.isa = ISA_MIPS64R6;
+ break;
+
+ case OPTION_MIPS64:
+ file_mips_opts.isa = ISA_MIPS64;
+ break;
+
+ case OPTION_MTUNE:
+ mips_set_option_string (&mips_tune_string, arg);
+ break;
+
+ case OPTION_MARCH:
+ mips_set_option_string (&mips_arch_string, arg);
+ break;
+
+ case OPTION_M4650:
+ mips_set_option_string (&mips_arch_string, "4650");
+ mips_set_option_string (&mips_tune_string, "4650");
+ break;
+
+ case OPTION_NO_M4650:
+ break;
+
+ case OPTION_M4010:
+ mips_set_option_string (&mips_arch_string, "4010");
+ mips_set_option_string (&mips_tune_string, "4010");
+ break;
+
+ case OPTION_NO_M4010:
+ break;
+
+ case OPTION_M4100:
+ mips_set_option_string (&mips_arch_string, "4100");
+ mips_set_option_string (&mips_tune_string, "4100");
+ break;
+
+ case OPTION_NO_M4100:
+ break;
+
+ case OPTION_M3900:
+ mips_set_option_string (&mips_arch_string, "3900");
+ mips_set_option_string (&mips_tune_string, "3900");
+ break;
+
+ case OPTION_NO_M3900:
+ break;
+
+ case OPTION_MICROMIPS:
+ if (file_mips_opts.mips16 == 1)
+ {
+ as_bad (_("-mmicromips cannot be used with -mips16"));
+ return 0;
+ }
+ file_mips_opts.micromips = 1;
+ mips_no_prev_insn ();
+ break;
+
+ case OPTION_NO_MICROMIPS:
+ file_mips_opts.micromips = 0;
+ mips_no_prev_insn ();
+ break;
+
+ case OPTION_MIPS16:
+ if (file_mips_opts.micromips == 1)
+ {
+ as_bad (_("-mips16 cannot be used with -micromips"));
+ return 0;
+ }
+ file_mips_opts.mips16 = 1;
+ mips_no_prev_insn ();
+ break;
+
+ case OPTION_NO_MIPS16:
+ file_mips_opts.mips16 = 0;
+ mips_no_prev_insn ();
+ break;
+
+ case OPTION_FIX_24K:
+ mips_fix_24k = 1;
+ break;
+
+ case OPTION_NO_FIX_24K:
+ mips_fix_24k = 0;
+ break;
+
+ case OPTION_FIX_RM7000:
+ mips_fix_rm7000 = 1;
+ break;
+
+ case OPTION_NO_FIX_RM7000:
+ mips_fix_rm7000 = 0;
+ break;
+
+ case OPTION_FIX_LOONGSON2F_JUMP:
+ mips_fix_loongson2f_jump = TRUE;
+ break;
+
+ case OPTION_NO_FIX_LOONGSON2F_JUMP:
+ mips_fix_loongson2f_jump = FALSE;
+ break;
+
+ case OPTION_FIX_LOONGSON2F_NOP:
+ mips_fix_loongson2f_nop = TRUE;
+ break;
+
+ case OPTION_NO_FIX_LOONGSON2F_NOP:
+ mips_fix_loongson2f_nop = FALSE;
+ break;
+
+ case OPTION_FIX_VR4120:
+ mips_fix_vr4120 = 1;
+ break;
+
+ case OPTION_NO_FIX_VR4120:
+ mips_fix_vr4120 = 0;
+ break;
+
+ case OPTION_FIX_VR4130:
+ mips_fix_vr4130 = 1;
+ break;
+
+ case OPTION_NO_FIX_VR4130:
+ mips_fix_vr4130 = 0;
+ break;
+
+ case OPTION_FIX_CN63XXP1:
+ mips_fix_cn63xxp1 = TRUE;
+ break;
+
+ case OPTION_NO_FIX_CN63XXP1:
+ mips_fix_cn63xxp1 = FALSE;
+ break;
+
+ case OPTION_RELAX_BRANCH:
+ mips_relax_branch = 1;
+ break;
+
+ case OPTION_NO_RELAX_BRANCH:
+ mips_relax_branch = 0;
+ break;
+
+ case OPTION_INSN32:
+ file_mips_opts.insn32 = TRUE;
+ break;
+
+ case OPTION_NO_INSN32:
+ file_mips_opts.insn32 = FALSE;
+ break;
+
+ case OPTION_MSHARED:
+ mips_in_shared = TRUE;
+ break;
+
+ case OPTION_MNO_SHARED:
+ mips_in_shared = FALSE;
+ break;
+
+ case OPTION_MSYM32:
+ file_mips_opts.sym32 = TRUE;
+ break;
+
+ case OPTION_MNO_SYM32:
+ file_mips_opts.sym32 = FALSE;
+ break;
+
+ /* When generating ELF code, we permit -KPIC and -call_shared to
+ select SVR4_PIC, and -non_shared to select no PIC. This is
+ intended to be compatible with Irix 5. */
+ case OPTION_CALL_SHARED:
+ mips_pic = SVR4_PIC;
+ mips_abicalls = TRUE;
+ break;
+
+ case OPTION_CALL_NONPIC:
+ mips_pic = NO_PIC;
+ mips_abicalls = TRUE;
+ break;
+
+ case OPTION_NON_SHARED:
+ mips_pic = NO_PIC;
+ mips_abicalls = FALSE;
+ break;
+
+ /* The -xgot option tells the assembler to use 32 bit offsets
+ when accessing the got in SVR4_PIC mode. It is for Irix
+ compatibility. */
+ case OPTION_XGOT:
+ mips_big_got = 1;
+ break;
+
+ case 'G':
+ g_switch_value = atoi (arg);
+ g_switch_seen = 1;
+ break;
+
+ /* The -32, -n32 and -64 options are shortcuts for -mabi=32, -mabi=n32
+ and -mabi=64. */
+ case OPTION_32:
+ mips_abi = O32_ABI;
+ break;
+
+ case OPTION_N32:
+ mips_abi = N32_ABI;
+ break;
+
+ case OPTION_64:
+ mips_abi = N64_ABI;
+ if (!support_64bit_objects())
+ as_fatal (_("no compiled in support for 64 bit object file format"));
+ break;
+
+ case OPTION_GP32:
+ file_mips_opts.gp = 32;
+ break;
+
+ case OPTION_GP64:
+ file_mips_opts.gp = 64;
+ break;
+
+ case OPTION_FP32:
+ file_mips_opts.fp = 32;
+ break;
+
+ case OPTION_FPXX:
+ file_mips_opts.fp = 0;
+ break;
+
+ case OPTION_FP64:
+ file_mips_opts.fp = 64;
+ break;
+
+ case OPTION_ODD_SPREG:
+ file_mips_opts.oddspreg = 1;
+ break;
+
+ case OPTION_NO_ODD_SPREG:
+ file_mips_opts.oddspreg = 0;
+ break;
+
+ case OPTION_SINGLE_FLOAT:
+ file_mips_opts.single_float = 1;
+ break;
+
+ case OPTION_DOUBLE_FLOAT:
+ file_mips_opts.single_float = 0;
+ break;
+
+ case OPTION_SOFT_FLOAT:
+ file_mips_opts.soft_float = 1;
+ break;
+
+ case OPTION_HARD_FLOAT:
+ file_mips_opts.soft_float = 0;
+ break;
+
+ case OPTION_MABI:
+ if (strcmp (arg, "32") == 0)
+ mips_abi = O32_ABI;
+ else if (strcmp (arg, "o64") == 0)
+ mips_abi = O64_ABI;
+ else if (strcmp (arg, "n32") == 0)
+ mips_abi = N32_ABI;
+ else if (strcmp (arg, "64") == 0)
+ {
+ mips_abi = N64_ABI;
+ if (! support_64bit_objects())
+ as_fatal (_("no compiled in support for 64 bit object file "
+ "format"));
+ }
+ else if (strcmp (arg, "eabi") == 0)
+ mips_abi = EABI_ABI;
+ else
+ {
+ as_fatal (_("invalid abi -mabi=%s"), arg);
+ return 0;
+ }
+ break;
+
+ case OPTION_M7000_HILO_FIX:
+ mips_7000_hilo_fix = TRUE;
+ break;
+
+ case OPTION_MNO_7000_HILO_FIX:
+ mips_7000_hilo_fix = FALSE;
+ break;
+
+ case OPTION_MDEBUG:
+ mips_flag_mdebug = TRUE;
+ break;
+
+ case OPTION_NO_MDEBUG:
+ mips_flag_mdebug = FALSE;
+ break;
+
+ case OPTION_PDR:
+ mips_flag_pdr = TRUE;
+ break;
+
+ case OPTION_NO_PDR:
+ mips_flag_pdr = FALSE;
+ break;
+
+ case OPTION_MVXWORKS_PIC:
+ mips_pic = VXWORKS_PIC;
+ break;
+
+ case OPTION_NAN:
+ if (strcmp (arg, "2008") == 0)
+ mips_nan2008 = 1;
+ else if (strcmp (arg, "legacy") == 0)
+ mips_nan2008 = 0;
+ else
+ {
+ as_fatal (_("invalid NaN setting -mnan=%s"), arg);
+ return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ mips_fix_loongson2f = mips_fix_loongson2f_nop || mips_fix_loongson2f_jump;
+
+ return 1;
+}
+
+/* Set up globals to tune for the ISA or processor described by INFO. */
+
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+ if (info != 0)
+ mips_tune = info->cpu;
+}
+
+
+void
+mips_after_parse_args (void)
+{
+ const struct mips_cpu_info *arch_info = 0;
+ const struct mips_cpu_info *tune_info = 0;
+
+ /* GP relative stuff not working for PE */
+ if (strncmp (TARGET_OS, "pe", 2) == 0)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_bad (_("-G not supported in this configuration"));
+ g_switch_value = 0;
+ }
+
+ if (mips_abi == NO_ABI)
+ mips_abi = MIPS_DEFAULT_ABI;
+
+ /* The following code determines the architecture.
+ Similar code was added to GCC 3.3 (see override_options() in
+ config/mips/mips.c). The GAS and GCC code should be kept in sync
+ as much as possible. */
+
+ if (mips_arch_string != 0)
+ arch_info = mips_parse_cpu ("-march", mips_arch_string);
+
+ if (file_mips_opts.isa != ISA_UNKNOWN)
+ {
+ /* Handle -mipsN. At this point, file_mips_opts.isa contains the
+ ISA level specified by -mipsN, while arch_info->isa contains
+ the -march selection (if any). */
+ if (arch_info != 0)
+ {
+ /* -march takes precedence over -mipsN, since it is more descriptive.
+ There's no harm in specifying both as long as the ISA levels
+ are the same. */
+ if (file_mips_opts.isa != arch_info->isa)
+ as_bad (_("-%s conflicts with the other architecture options,"
+ " which imply -%s"),
+ mips_cpu_info_from_isa (file_mips_opts.isa)->name,
+ mips_cpu_info_from_isa (arch_info->isa)->name);
+ }
+ else
+ arch_info = mips_cpu_info_from_isa (file_mips_opts.isa);
+ }
+
+ if (arch_info == 0)
+ {
+ arch_info = mips_parse_cpu ("default CPU", MIPS_CPU_STRING_DEFAULT);
+ gas_assert (arch_info);
+ }
+
+ if (ABI_NEEDS_64BIT_REGS (mips_abi) && !ISA_HAS_64BIT_REGS (arch_info->isa))
+ as_bad (_("-march=%s is not compatible with the selected ABI"),
+ arch_info->name);
+
+ file_mips_opts.arch = arch_info->cpu;
+ file_mips_opts.isa = arch_info->isa;
+
+ /* Set up initial mips_opts state. */
+ mips_opts = file_mips_opts;
+
+ /* The register size inference code is now placed in
+ file_mips_check_options. */
+
+ /* Optimize for file_mips_opts.arch, unless -mtune selects a different
+ processor. */
+ if (mips_tune_string != 0)
+ tune_info = mips_parse_cpu ("-mtune", mips_tune_string);
+
+ if (tune_info == 0)
+ mips_set_tune (arch_info);
+ else
+ mips_set_tune (tune_info);
+
+ if (mips_flag_mdebug < 0)
+ mips_flag_mdebug = 0;
+}
+
+void
+mips_init_after_args (void)
+{
+ /* initialize opcodes */
+ bfd_mips_num_opcodes = bfd_mips_num_builtin_opcodes;
+ mips_opcodes = (struct mips_opcode *) mips_builtin_opcodes;
+}
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ /* Return the address of the delay slot. */
+ return addr + 2;
+
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_JMP:
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ case BFD_RELOC_MIPS_JMP:
+ /* Return the address of the delay slot. */
+ return addr + 4;
+
+ default:
+ return addr;
+ }
+}
+
+/* This is called before the symbol table is processed. In order to
+ work with gcc when using mips-tfile, we must keep all local labels.
+ However, in other cases, we want to discard them. If we were
+ called with -g, but we didn't see any debugging information, it may
+ mean that gcc is smuggling debugging information through to
+ mips-tfile, in which case we must generate all local labels. */
+
+void
+mips_frob_file_before_adjust (void)
+{
+#ifndef NO_ECOFF_DEBUGGING
+ if (ECOFF_DEBUGGING
+ && mips_debug != 0
+ && ! ecoff_debugging_seen)
+ flag_keep_locals = 1;
+#endif
+}
+
+/* Sort any unmatched HI16 and GOT16 relocs so that they immediately precede
+ the corresponding LO16 reloc. This is called before md_apply_fix and
+ tc_gen_reloc. Unmatched relocs can only be generated by use of explicit
+ relocation operators.
+
+ For our purposes, a %lo() expression matches a %got() or %hi()
+ expression if:
+
+ (a) it refers to the same symbol; and
+ (b) the offset applied in the %lo() expression is no lower than
+ the offset applied in the %got() or %hi().
+
+ (b) allows us to cope with code like:
+
+ lui $4,%hi(foo)
+ lh $4,%lo(foo+2)($4)
+
+ ...which is legal on RELA targets, and has a well-defined behaviour
+ if the user knows that adding 2 to "foo" will not induce a carry to
+ the high 16 bits.
+
+ When several %lo()s match a particular %got() or %hi(), we use the
+ following rules to distinguish them:
+
+ (1) %lo()s with smaller offsets are a better match than %lo()s with
+ higher offsets.
+
+ (2) %lo()s with no matching %got() or %hi() are better than those
+ that already have a matching %got() or %hi().
+
+ (3) later %lo()s are better than earlier %lo()s.
+
+ These rules are applied in order.
+
+ (1) means, among other things, that %lo()s with identical offsets are
+ chosen if they exist.
+
+ (2) means that we won't associate several high-part relocations with
+ the same low-part relocation unless there's no alternative. Having
+ several high parts for the same low part is a GNU extension; this rule
+ allows careful users to avoid it.
+
+ (3) is purely cosmetic. mips_hi_fixup_list is is in reverse order,
+ with the last high-part relocation being at the front of the list.
+ It therefore makes sense to choose the last matching low-part
+ relocation, all other things being equal. It's also easier
+ to code that way. */
+
+void
+mips_frob_file (void)
+{
+ struct mips_hi_fixup *l;
+ bfd_reloc_code_real_type looking_for_rtype = BFD_RELOC_UNUSED;
+
+ for (l = mips_hi_fixup_list; l != NULL; l = l->next)
+ {
+ segment_info_type *seginfo;
+ bfd_boolean matched_lo_p;
+ fixS **hi_pos, **lo_pos, **pos;
+
+ gas_assert (reloc_needs_lo_p (l->fixp->fx_r_type));
+
+ /* If a GOT16 relocation turns out to be against a global symbol,
+ there isn't supposed to be a matching LO. Ignore %gots against
+ constants; we'll report an error for those later. */
+ if (got16_reloc_p (l->fixp->fx_r_type)
+ && !(l->fixp->fx_addsy
+ && pic_need_relax (l->fixp->fx_addsy, l->seg)))
+ continue;
+
+ /* Check quickly whether the next fixup happens to be a matching %lo. */
+ if (fixup_has_matching_lo_p (l->fixp))
+ continue;
+
+ seginfo = seg_info (l->seg);
+
+ /* Set HI_POS to the position of this relocation in the chain.
+ Set LO_POS to the position of the chosen low-part relocation.
+ MATCHED_LO_P is true on entry to the loop if *POS is a low-part
+ relocation that matches an immediately-preceding high-part
+ relocation. */
+ hi_pos = NULL;
+ lo_pos = NULL;
+ matched_lo_p = FALSE;
+ looking_for_rtype = matching_lo_reloc (l->fixp->fx_r_type);
+
+ for (pos = &seginfo->fix_root; *pos != NULL; pos = &(*pos)->fx_next)
+ {
+ if (*pos == l->fixp)
+ hi_pos = pos;
+
+ if ((*pos)->fx_r_type == looking_for_rtype
+ && symbol_same_p ((*pos)->fx_addsy, l->fixp->fx_addsy)
+ && (*pos)->fx_offset >= l->fixp->fx_offset
+ && (lo_pos == NULL
+ || (*pos)->fx_offset < (*lo_pos)->fx_offset
+ || (!matched_lo_p
+ && (*pos)->fx_offset == (*lo_pos)->fx_offset)))
+ lo_pos = pos;
+
+ matched_lo_p = (reloc_needs_lo_p ((*pos)->fx_r_type)
+ && fixup_has_matching_lo_p (*pos));
+ }
+
+ /* If we found a match, remove the high-part relocation from its
+ current position and insert it before the low-part relocation.
+ Make the offsets match so that fixup_has_matching_lo_p()
+ will return true.
+
+ We don't warn about unmatched high-part relocations since some
+ versions of gcc have been known to emit dead "lui ...%hi(...)"
+ instructions. */
+ if (lo_pos != NULL)
+ {
+ l->fixp->fx_offset = (*lo_pos)->fx_offset;
+ if (l->fixp->fx_next != *lo_pos)
+ {
+ *hi_pos = l->fixp->fx_next;
+ l->fixp->fx_next = *lo_pos;
+ *lo_pos = l->fixp;
+ }
+ }
+ }
+}
+
+int
+mips_force_relocation (fixS *fixp)
+{
+ if (generic_force_reloc (fixp))
+ return 1;
+
+ /* We want to keep BFD_RELOC_MICROMIPS_*_PCREL_S1 relocation,
+ so that the linker relaxation can update targets. */
+ if (fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1)
+ return 1;
+
+ /* We want all PC-relative relocations to be kept for R6 relaxation. */
+ if (ISA_IS_R6 (mips_opts.isa)
+ && (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
+ || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
+ || fixp->fx_r_type == BFD_RELOC_LO16_PCREL))
+ return 1;
+
+ return 0;
+}
+
+/* Read the instruction associated with RELOC from BUF. */
+
+static unsigned int
+read_reloc_insn (char *buf, bfd_reloc_code_real_type reloc)
+{
+ if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+ return read_compressed_insn (buf, 4);
+ else
+ return read_insn (buf);
+}
+
+/* Write instruction INSN to BUF, given that it has been relocated
+ by RELOC. */
+
+static void
+write_reloc_insn (char *buf, bfd_reloc_code_real_type reloc,
+ unsigned long insn)
+{
+ if (mips16_reloc_p (reloc) || micromips_reloc_p (reloc))
+ write_compressed_insn (buf, insn, 4);
+ else
+ write_insn (buf, insn);
+}
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf;
+ unsigned long insn;
+ reloc_howto_type *howto;
+
+ if (fixP->fx_pcrel)
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_16_PCREL_S2:
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ case BFD_RELOC_MIPS_19_PCREL_S2:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_LO16_PCREL:
+ break;
+
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative reference to a different section"));
+ break;
+ }
+
+ /* Handle BFD_RELOC_8, since it's easy. Punt on other bfd relocations
+ that have no MIPS ELF equivalent. */
+ if (fixP->fx_r_type != BFD_RELOC_8)
+ {
+ howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (!howto)
+ return;
+ }
+
+ gas_assert (fixP->fx_size == 2
+ || fixP->fx_size == 4
+ || fixP->fx_r_type == BFD_RELOC_8
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_64
+ || fixP->fx_r_type == BFD_RELOC_CTOR
+ || fixP->fx_r_type == BFD_RELOC_MIPS_SUB
+ || fixP->fx_r_type == BFD_RELOC_MICROMIPS_SUB
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_MIPS_TLS_DTPREL64);
+
+ buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ /* Don't treat parts of a composite relocation as done. There are two
+ reasons for this:
+
+ (1) The second and third parts will be against 0 (RSS_UNDEF) but
+ should nevertheless be emitted if the first part is.
+
+ (2) In normal usage, composite relocations are never assembly-time
+ constants. The easiest way of dealing with the pathological
+ exceptions is to generate a relocation against STN_UNDEF and
+ leave everything up to the linker. */
+ if (fixP->fx_addsy == NULL && !fixP->fx_pcrel && fixP->fx_tcbit == 0)
+ fixP->fx_done = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_MIPS_TLS_GD:
+ case BFD_RELOC_MIPS_TLS_LDM:
+ case BFD_RELOC_MIPS_TLS_DTPREL32:
+ case BFD_RELOC_MIPS_TLS_DTPREL64:
+ case BFD_RELOC_MIPS_TLS_DTPREL_HI16:
+ case BFD_RELOC_MIPS_TLS_DTPREL_LO16:
+ case BFD_RELOC_MIPS_TLS_GOTTPREL:
+ case BFD_RELOC_MIPS_TLS_TPREL32:
+ case BFD_RELOC_MIPS_TLS_TPREL64:
+ case BFD_RELOC_MIPS_TLS_TPREL_HI16:
+ case BFD_RELOC_MIPS_TLS_TPREL_LO16:
+ case BFD_RELOC_MICROMIPS_TLS_GD:
+ case BFD_RELOC_MICROMIPS_TLS_LDM:
+ case BFD_RELOC_MICROMIPS_TLS_DTPREL_HI16:
+ case BFD_RELOC_MICROMIPS_TLS_DTPREL_LO16:
+ case BFD_RELOC_MICROMIPS_TLS_GOTTPREL:
+ case BFD_RELOC_MICROMIPS_TLS_TPREL_HI16:
+ case BFD_RELOC_MICROMIPS_TLS_TPREL_LO16:
+ case BFD_RELOC_MIPS16_TLS_GD:
+ case BFD_RELOC_MIPS16_TLS_LDM:
+ case BFD_RELOC_MIPS16_TLS_DTPREL_HI16:
+ case BFD_RELOC_MIPS16_TLS_DTPREL_LO16:
+ case BFD_RELOC_MIPS16_TLS_GOTTPREL:
+ case BFD_RELOC_MIPS16_TLS_TPREL_HI16:
+ case BFD_RELOC_MIPS16_TLS_TPREL_LO16:
+ if (!fixP->fx_addsy)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("TLS relocation against a constant"));
+ break;
+ }
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* fall through */
+
+ case BFD_RELOC_MIPS_JMP:
+ case BFD_RELOC_MIPS_SHIFT5:
+ case BFD_RELOC_MIPS_SHIFT6:
+ case BFD_RELOC_MIPS_GOT_DISP:
+ case BFD_RELOC_MIPS_GOT_PAGE:
+ case BFD_RELOC_MIPS_GOT_OFST:
+ case BFD_RELOC_MIPS_SUB:
+ case BFD_RELOC_MIPS_INSERT_A:
+ case BFD_RELOC_MIPS_INSERT_B:
+ case BFD_RELOC_MIPS_DELETE:
+ case BFD_RELOC_MIPS_HIGHEST:
+ case BFD_RELOC_MIPS_HIGHER:
+ case BFD_RELOC_MIPS_SCN_DISP:
+ case BFD_RELOC_MIPS_REL16:
+ case BFD_RELOC_MIPS_RELGOT:
+ case BFD_RELOC_MIPS_JALR:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_MIPS_LITERAL:
+ case BFD_RELOC_MIPS_CALL16:
+ case BFD_RELOC_MIPS_GOT16:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_MIPS_GOT_HI16:
+ case BFD_RELOC_MIPS_GOT_LO16:
+ case BFD_RELOC_MIPS_CALL_HI16:
+ case BFD_RELOC_MIPS_CALL_LO16:
+ case BFD_RELOC_MIPS16_GPREL:
+ case BFD_RELOC_MIPS16_GOT16:
+ case BFD_RELOC_MIPS16_CALL16:
+ case BFD_RELOC_MIPS16_HI16:
+ case BFD_RELOC_MIPS16_HI16_S:
+ case BFD_RELOC_MIPS16_LO16:
+ case BFD_RELOC_MIPS16_JMP:
+ case BFD_RELOC_MICROMIPS_JMP:
+ case BFD_RELOC_MICROMIPS_GOT_DISP:
+ case BFD_RELOC_MICROMIPS_GOT_PAGE:
+ case BFD_RELOC_MICROMIPS_GOT_OFST:
+ case BFD_RELOC_MICROMIPS_SUB:
+ case BFD_RELOC_MICROMIPS_HIGHEST:
+ case BFD_RELOC_MICROMIPS_HIGHER:
+ case BFD_RELOC_MICROMIPS_SCN_DISP:
+ case BFD_RELOC_MICROMIPS_JALR:
+ case BFD_RELOC_MICROMIPS_HI16:
+ case BFD_RELOC_MICROMIPS_HI16_S:
+ case BFD_RELOC_MICROMIPS_LO16:
+ case BFD_RELOC_MICROMIPS_GPREL16:
+ case BFD_RELOC_MICROMIPS_LITERAL:
+ case BFD_RELOC_MICROMIPS_CALL16:
+ case BFD_RELOC_MICROMIPS_GOT16:
+ case BFD_RELOC_MICROMIPS_GOT_HI16:
+ case BFD_RELOC_MICROMIPS_GOT_LO16:
+ case BFD_RELOC_MICROMIPS_CALL_HI16:
+ case BFD_RELOC_MICROMIPS_CALL_LO16:
+ case BFD_RELOC_MIPS_EH:
+ if (fixP->fx_done)
+ {
+ offsetT value;
+
+ if (calculate_reloc (fixP->fx_r_type, *valP, &value))
+ {
+ insn = read_reloc_insn (buf, fixP->fx_r_type);
+ if (mips16_reloc_p (fixP->fx_r_type))
+ insn |= mips16_immed_extend (value, 16);
+ else
+ insn |= (value & 0xffff);
+ write_reloc_insn (buf, fixP->fx_r_type, insn);
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsupported constant in relocation"));
+ }
+ break;
+
+ case BFD_RELOC_64:
+ /* This is handled like BFD_RELOC_32, but we output a sign
+ extended value if we are only 32 bits. */
+ if (fixP->fx_done)
+ {
+ if (8 <= sizeof (valueT))
+ md_number_to_chars (buf, *valP, 8);
+ else
+ {
+ valueT hiv;
+
+ if ((*valP & 0x80000000) != 0)
+ hiv = 0xffffffff;
+ else
+ hiv = 0;
+ md_number_to_chars (buf + (target_big_endian ? 4 : 0), *valP, 4);
+ md_number_to_chars (buf + (target_big_endian ? 0 : 4), hiv, 4);
+ }
+ }
+ break;
+
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ /* If we are deleting this reloc entry, we must fill in the
+ value now. This can happen if we have a .word which is not
+ resolved when it appears but is later defined. */
+ if (fixP->fx_done)
+ md_number_to_chars (buf, *valP, fixP->fx_size);
+ break;
+
+ case BFD_RELOC_MIPS_21_PCREL_S2:
+ case BFD_RELOC_MIPS_26_PCREL_S2:
+ if ((*valP & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to misaligned address (%lx)"), (long) *valP);
+
+ gas_assert (!fixP->fx_done);
+ break;
+
+ case BFD_RELOC_MIPS_18_PCREL_S3:
+ if ((S_GET_VALUE (fixP->fx_addsy) & 0x7) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access using misaligned symbol (%lx)"),
+ (long) S_GET_VALUE (fixP->fx_addsy));
+ if ((fixP->fx_offset & 0x7) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access using misaligned offset (%lx)"),
+ (long) fixP->fx_offset);
+
+ gas_assert (!fixP->fx_done);
+ break;
+
+ case BFD_RELOC_MIPS_19_PCREL_S2:
+ if ((*valP & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative access to misaligned address (%lx)"),
+ (long) (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset));
+
+ gas_assert (!fixP->fx_done);
+ break;
+
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_LO16_PCREL:
+ gas_assert (!fixP->fx_done);
+ break;
+
+ case BFD_RELOC_16_PCREL_S2:
+ if ((*valP & 0x3) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch to misaligned address (%lx)"), (long) *valP);
+
+ /* We need to save the bits in the instruction since fixup_segment()
+ might be deleting the relocation entry (i.e., a branch within
+ the current segment). */
+ if (! fixP->fx_done)
+ break;
+
+ /* Update old instruction data. */
+ insn = read_insn (buf);
+
+ if (*valP + 0x20000 <= 0x3ffff)
+ {
+ insn |= (*valP >> 2) & 0xffff;
+ write_insn (buf, insn);
+ }
+ else if (mips_pic == NO_PIC
+ && fixP->fx_done
+ && fixP->fx_frag->fr_address >= text_section->vma
+ && (fixP->fx_frag->fr_address
+ < text_section->vma + bfd_get_section_size (text_section))
+ && ((insn & 0xffff0000) == 0x10000000 /* beq $0,$0 */
+ || (insn & 0xffff0000) == 0x04010000 /* bgez $0 */
+ || (insn & 0xffff0000) == 0x04110000)) /* bgezal $0 */
+ {
+ /* The branch offset is too large. If this is an
+ unconditional branch, and we are not generating PIC code,
+ we can convert it to an absolute jump instruction. */
+ if ((insn & 0xffff0000) == 0x04110000) /* bgezal $0 */
+ insn = 0x0c000000; /* jal */
+ else
+ insn = 0x08000000; /* j */
+ fixP->fx_r_type = BFD_RELOC_MIPS_JMP;
+ fixP->fx_done = 0;
+ fixP->fx_addsy = section_symbol (text_section);
+ *valP += md_pcrel_from (fixP);
+ write_insn (buf, insn);
+ }
+ else
+ {
+ /* If we got here, we have branch-relaxation disabled,
+ and there's nothing we can do to fix this instruction
+ without turning it into a longer sequence. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
+ }
+ break;
+
+ case BFD_RELOC_MICROMIPS_7_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_10_PCREL_S1:
+ case BFD_RELOC_MICROMIPS_16_PCREL_S1:
+ /* We adjust the offset back to even. */
+ if ((*valP & 0x1) != 0)
+ --(*valP);
+
+ if (! fixP->fx_done)
+ break;
+
+ /* Should never visit here, because we keep the relocation. */
+ abort ();
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy)
+ && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ break;
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ abort ();
+ }
+
+ /* Remember value for tc_gen_reloc. */
+ fixP->fx_addnumber = *valP;
+}
+
+static symbolS *
+get_symbol (void)
+{
+ int c;
+ char *name;
+ symbolS *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = (symbolS *) symbol_find_or_make (name);
+ *input_line_pointer = c;
+ return p;
+}
+
+/* Align the current frag to a given power of two. If a particular
+ fill byte should be used, FILL points to an integer that contains
+ that byte, otherwise FILL is null.
+
+ This function used to have the comment:
+
+ The MIPS assembler also automatically adjusts any preceding label.
+
+ The implementation therefore applied the adjustment to a maximum of
+ one label. However, other label adjustments are applied to batches
+ of labels, and adjusting just one caused problems when new labels
+ were added for the sake of debugging or unwind information.
+ We therefore adjust all preceding labels (given as LABELS) instead. */
+
+static void
+mips_align (int to, int *fill, struct insn_label_list *labels)
+{
+ mips_emit_delays ();
+ mips_record_compressed_mode ();
+ if (fill == NULL && subseg_text_p (now_seg))
+ frag_align_code (to, 0);
+ else
+ frag_align (to, fill ? *fill : 0, 0);
+ record_alignment (now_seg, to);
+ mips_move_labels (labels, FALSE);
+}
+
+/* Align to a given power of two. .align 0 turns off the automatic
+ alignment used by the data creating pseudo-ops. */
+
+static void
+s_align (int x ATTRIBUTE_UNUSED)
+{
+ int temp, fill_value, *fill_ptr;
+ long max_alignment = 28;
+
+ /* o Note that the assembler pulls down any immediately preceding label
+ to the aligned address.
+ o It's not documented but auto alignment is reinstated by
+ a .align pseudo instruction.
+ o Note also that after auto alignment is turned off the mips assembler
+ issues an error on attempt to assemble an improperly aligned data item.
+ We don't. */
+
+ temp = get_absolute_expression ();
+ if (temp > max_alignment)
+ as_bad (_("alignment too large, %d assumed"), temp = max_alignment);
+ else if (temp < 0)
+ {
+ as_warn (_("alignment negative, 0 assumed"));
+ temp = 0;
+ }
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ fill_value = get_absolute_expression ();
+ fill_ptr = &fill_value;
+ }
+ else
+ fill_ptr = 0;
+ if (temp)
+ {
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l = si->label_list;
+ /* Auto alignment should be switched on by next section change. */
+ auto_align = 1;
+ mips_align (temp, fill_ptr, l);
+ }
+ else
+ {
+ auto_align = 0;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_change_sec (int sec)
+{
+ segT seg;
+
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for an obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ obj_elf_section_change_hook ();
+
+ mips_emit_delays ();
+
+ switch (sec)
+ {
+ case 't':
+ s_text (0);
+ break;
+ case 'd':
+ s_data (0);
+ break;
+ case 'b':
+ subseg_set (bss_section, (subsegT) get_absolute_expression ());
+ demand_empty_rest_of_line ();
+ break;
+
+ case 'r':
+ seg = subseg_new (RDATA_SECTION_NAME,
+ (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD
+ | SEC_READONLY | SEC_RELOC
+ | SEC_DATA));
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+
+ case 's':
+ seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg,
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+
+ case 'B':
+ seg = subseg_new (".sbss", (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg, SEC_ALLOC);
+ if (strncmp (TARGET_OS, "elf", 3) != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+ }
+
+ auto_align = 1;
+}
+
+void
+s_change_section (int ignore ATTRIBUTE_UNUSED)
+{
+ char *section_name;
+ char c;
+ char next_c = 0;
+ int section_type;
+ int section_flag;
+ int section_entry_size;
+ int section_alignment;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+ if (c)
+ next_c = *(input_line_pointer + 1);
+
+ /* Do we have .section Name<,"flags">? */
+ if (c != ',' || (c == ',' && next_c == '"'))
+ {
+ /* just after name is now '\0'. */
+ *input_line_pointer = c;
+ input_line_pointer = section_name;
+ obj_elf_section (ignore);
+ return;
+ }
+ input_line_pointer++;
+
+ /* Do we have .section Name<,type><,flag><,entry_size><,alignment> */
+ if (c == ',')
+ section_type = get_absolute_expression ();
+ else
+ section_type = 0;
+ if (*input_line_pointer++ == ',')
+ section_flag = get_absolute_expression ();
+ else
+ section_flag = 0;
+ if (*input_line_pointer++ == ',')
+ section_entry_size = get_absolute_expression ();
+ else
+ section_entry_size = 0;
+ if (*input_line_pointer++ == ',')
+ section_alignment = get_absolute_expression ();
+ else
+ section_alignment = 0;
+ /* FIXME: really ignore? */
+ (void) section_alignment;
+
+ section_name = xstrdup (section_name);
+
+ /* When using the generic form of .section (as implemented by obj-elf.c),
+ there's no way to set the section type to SHT_MIPS_DWARF. Users have
+ traditionally had to fall back on the more common @progbits instead.
+
+ There's nothing really harmful in this, since bfd will correct
+ SHT_PROGBITS to SHT_MIPS_DWARF before writing out the file. But it
+ means that, for backwards compatibility, the special_section entries
+ for dwarf sections must use SHT_PROGBITS rather than SHT_MIPS_DWARF.
+
+ Even so, we shouldn't force users of the MIPS .section syntax to
+ incorrectly label the sections as SHT_PROGBITS. The best compromise
+ seems to be to map SHT_MIPS_DWARF to SHT_PROGBITS before calling the
+ generic type-checking code. */
+ if (section_type == SHT_MIPS_DWARF)
+ section_type = SHT_PROGBITS;
+
+ obj_elf_change_section (section_name, section_type, section_flag,
+ section_entry_size, 0, 0, 0);
+
+ if (now_seg->name != section_name)
+ free (section_name);
+}
+
+void
+mips_enable_auto_align (void)
+{
+ auto_align = 1;
+}
+
+static void
+s_cons (int log_size)
+{
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l = si->label_list;
+
+ mips_emit_delays ();
+ if (log_size > 0 && auto_align)
+ mips_align (log_size, 0, l);
+ cons (1 << log_size);
+ mips_clear_insn_labels ();
+}
+
+static void
+s_float_cons (int type)
+{
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l = si->label_list;
+
+ mips_emit_delays ();
+
+ if (auto_align)
+ {
+ if (type == 'd')
+ mips_align (3, 0, l);
+ else
+ mips_align (2, 0, l);
+ }
+
+ float_cons (type);
+ mips_clear_insn_labels ();
+}
+
+/* Handle .globl. We need to override it because on Irix 5 you are
+ permitted to say
+ .globl foo .text
+ where foo is an undefined symbol, to mean that foo should be
+ considered to be the address of a function. */
+
+static void
+s_mips_globl (int x ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ flagword flag;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_EXTERNAL (symbolP);
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+
+ /* On Irix 5, every global symbol that is not explicitly labelled as
+ being a function is apparently labelled as being an object. */
+ flag = BSF_OBJECT;
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer]
+ && (*input_line_pointer != ','))
+ {
+ char *secname;
+ asection *sec;
+
+ secname = input_line_pointer;
+ c = get_symbol_end ();
+ sec = bfd_get_section_by_name (stdoutput, secname);
+ if (sec == NULL)
+ as_bad (_("%s: no such section"), secname);
+ *input_line_pointer = c;
+
+ if (sec != NULL && (sec->flags & SEC_CODE) != 0)
+ flag = BSF_FUNCTION;
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= flag;
+
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_option (int x ATTRIBUTE_UNUSED)
+{
+ char *opt;
+ char c;
+
+ opt = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (*opt == 'O')
+ {
+ /* FIXME: What does this mean? */
+ }
+ else if (strncmp (opt, "pic", 3) == 0)
+ {
+ int i;
+
+ i = atoi (opt + 3);
+ if (i == 0)
+ mips_pic = NO_PIC;
+ else if (i == 2)
+ {
+ mips_pic = SVR4_PIC;
+ mips_abicalls = TRUE;
+ }
+ else
+ as_bad (_(".option pic%d not supported"), i);
+
+ if (mips_pic == SVR4_PIC)
+ {
+ if (g_switch_seen && g_switch_value != 0)
+ as_warn (_("-G may not be used with SVR4 PIC code"));
+ g_switch_value = 0;
+ bfd_set_gp_size (stdoutput, 0);
+ }
+ }
+ else
+ as_warn (_("unrecognized option \"%s\""), opt);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* This structure is used to hold a stack of .set values. */
+
+struct mips_option_stack
+{
+ struct mips_option_stack *next;
+ struct mips_set_options options;
+};
+
+static struct mips_option_stack *mips_opts_stack;
+
+static bfd_boolean
+parse_code_option (char * name)
+{
+ const struct mips_ase *ase;
+ if (strncmp (name, "at=", 3) == 0)
+ {
+ char *s = name + 3;
+
+ if (!reg_lookup (&s, RTYPE_NUM | RTYPE_GP, &mips_opts.at))
+ as_bad (_("unrecognized register name `%s'"), s);
+ }
+ else if (strcmp (name, "at") == 0)
+ mips_opts.at = ATREG;
+ else if (strcmp (name, "noat") == 0)
+ mips_opts.at = ZERO;
+ else if (strcmp (name, "move") == 0 || strcmp (name, "novolatile") == 0)
+ mips_opts.nomove = 0;
+ else if (strcmp (name, "nomove") == 0 || strcmp (name, "volatile") == 0)
+ mips_opts.nomove = 1;
+ else if (strcmp (name, "bopt") == 0)
+ mips_opts.nobopt = 0;
+ else if (strcmp (name, "nobopt") == 0)
+ mips_opts.nobopt = 1;
+ else if (strcmp (name, "gp=32") == 0)
+ mips_opts.gp = 32;
+ else if (strcmp (name, "gp=64") == 0)
+ mips_opts.gp = 64;
+ else if (strcmp (name, "fp=32") == 0)
+ mips_opts.fp = 32;
+ else if (strcmp (name, "fp=xx") == 0)
+ mips_opts.fp = 0;
+ else if (strcmp (name, "fp=64") == 0)
+ mips_opts.fp = 64;
+ else if (strcmp (name, "softfloat") == 0)
+ mips_opts.soft_float = 1;
+ else if (strcmp (name, "hardfloat") == 0)
+ mips_opts.soft_float = 0;
+ else if (strcmp (name, "singlefloat") == 0)
+ mips_opts.single_float = 1;
+ else if (strcmp (name, "doublefloat") == 0)
+ mips_opts.single_float = 0;
+ else if (strcmp (name, "nooddspreg") == 0)
+ mips_opts.oddspreg = 0;
+ else if (strcmp (name, "oddspreg") == 0)
+ mips_opts.oddspreg = 1;
+ else if (strcmp (name, "mips16") == 0
+ || strcmp (name, "MIPS-16") == 0)
+ mips_opts.mips16 = 1;
+ else if (strcmp (name, "nomips16") == 0
+ || strcmp (name, "noMIPS-16") == 0)
+ mips_opts.mips16 = 0;
+ else if (strcmp (name, "micromips") == 0)
+ mips_opts.micromips = 1;
+ else if (strcmp (name, "nomicromips") == 0)
+ mips_opts.micromips = 0;
+ else if (name[0] == 'n'
+ && name[1] == 'o'
+ && (ase = mips_lookup_ase (name + 2)))
+ mips_set_ase (ase, &mips_opts, FALSE);
+ else if ((ase = mips_lookup_ase (name)))
+ mips_set_ase (ase, &mips_opts, TRUE);
+ else if (strncmp (name, "mips", 4) == 0 || strncmp (name, "arch=", 5) == 0)
+ {
+ /* Permit the user to change the ISA and architecture on the fly.
+ Needless to say, misuse can cause serious problems. */
+ if (strncmp (name, "arch=", 5) == 0)
+ {
+ const struct mips_cpu_info *p;
+
+ p = mips_parse_cpu ("internal use", name + 5);
+ if (!p)
+ as_bad (_("unknown architecture %s"), name + 5);
+ else
+ {
+ mips_opts.arch = p->cpu;
+ mips_opts.isa = p->isa;
+ }
+ }
+ else if (strncmp (name, "mips", 4) == 0)
+ {
+ const struct mips_cpu_info *p;
+
+ p = mips_parse_cpu ("internal use", name);
+ if (!p)
+ as_bad (_("unknown ISA level %s"), name + 4);
+ else
+ {
+ mips_opts.arch = p->cpu;
+ mips_opts.isa = p->isa;
+ }
+ }
+ else
+ as_bad (_("unknown ISA or architecture %s"), name);
+ }
+ else if (strcmp (name, "autoextend") == 0)
+ mips_opts.noautoextend = 0;
+ else if (strcmp (name, "noautoextend") == 0)
+ mips_opts.noautoextend = 1;
+ else if (strcmp (name, "insn32") == 0)
+ mips_opts.insn32 = TRUE;
+ else if (strcmp (name, "noinsn32") == 0)
+ mips_opts.insn32 = FALSE;
+ else if (strcmp (name, "sym32") == 0)
+ mips_opts.sym32 = TRUE;
+ else if (strcmp (name, "nosym32") == 0)
+ mips_opts.sym32 = FALSE;
+ else
+ return FALSE;
+ return TRUE;
+}
+
+/* Handle the .set pseudo-op. */
+
+static void
+s_mipsset (int x ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+ int prev_isa = mips_opts.isa;
+
+ file_mips_check_options ();
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (strchr (name, ','))
+ {
+ /* Generic ".set" directive; use the generic handler. */
+ *input_line_pointer = ch;
+ input_line_pointer = name;
+ s_set (0);
+ return;
+ }
+
+ if (strcmp (name, "reorder") == 0)
+ {
+ if (mips_opts.noreorder)
+ end_noreorder ();
+ }
+ else if (strcmp (name, "noreorder") == 0)
+ {
+ if (!mips_opts.noreorder)
+ start_noreorder ();
+ }
+ else if (strcmp (name, "macro") == 0)
+ mips_opts.warn_about_macros = 0;
+ else if (strcmp (name, "nomacro") == 0)
+ {
+ if (mips_opts.noreorder == 0)
+ as_bad (_("`noreorder' must be set before `nomacro'"));
+ mips_opts.warn_about_macros = 1;
+ }
+ else if (strcmp (name, "gp=default") == 0)
+ mips_opts.gp = file_mips_opts.gp;
+ else if (strcmp (name, "fp=default") == 0)
+ mips_opts.fp = file_mips_opts.fp;
+ else if (strcmp (name, "mips0") == 0 || strcmp (name, "arch=default") == 0)
+ {
+ mips_opts.isa = file_mips_opts.isa;
+ mips_opts.arch = file_mips_opts.arch;
+ mips_opts.gp = file_mips_opts.gp;
+ mips_opts.fp = file_mips_opts.fp;
+ }
+ else if (strcmp (name, "push") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = (struct mips_option_stack *) xmalloc (sizeof *s);
+ s->next = mips_opts_stack;
+ s->options = mips_opts;
+ mips_opts_stack = s;
+ }
+ else if (strcmp (name, "pop") == 0)
+ {
+ struct mips_option_stack *s;
+
+ s = mips_opts_stack;
+ if (s == NULL)
+ as_bad (_(".set pop with no .set push"));
+ else
+ {
+ /* If we're changing the reorder mode we need to handle
+ delay slots correctly. */
+ if (s->options.noreorder && ! mips_opts.noreorder)
+ start_noreorder ();
+ else if (! s->options.noreorder && mips_opts.noreorder)
+ end_noreorder ();
+
+ mips_opts = s->options;
+ mips_opts_stack = s->next;
+ free (s);
+ }
+ }
+ else if (!parse_code_option (name))
+ as_warn (_("tried to set unrecognized symbol: %s\n"), name);
+
+ /* The use of .set [arch|cpu]= historically 'fixes' the width of gp and fp
+ registers based on what is supported by the arch/cpu. */
+ if (mips_opts.isa != prev_isa)
+ {
+ switch (mips_opts.isa)
+ {
+ case 0:
+ break;
+ case ISA_MIPS1:
+ /* MIPS I cannot support FPXX. */
+ mips_opts.fp = 32;
+ /* fall-through. */
+ case ISA_MIPS2:
+ case ISA_MIPS32:
+ case ISA_MIPS32R2:
+ case ISA_MIPS32R3:
+ case ISA_MIPS32R5:
+ mips_opts.gp = 32;
+ if (mips_opts.fp != 0)
+ mips_opts.fp = 32;
+ break;
+ case ISA_MIPS32R6:
+ mips_opts.gp = 32;
+ mips_opts.fp = 64;
+ break;
+ case ISA_MIPS3:
+ case ISA_MIPS4:
+ case ISA_MIPS5:
+ case ISA_MIPS64:
+ case ISA_MIPS64R2:
+ case ISA_MIPS64R3:
+ case ISA_MIPS64R5:
+ case ISA_MIPS64R6:
+ mips_opts.gp = 64;
+ if (mips_opts.fp != 0)
+ {
+ if (mips_opts.arch == CPU_R5900)
+ mips_opts.fp = 32;
+ else
+ mips_opts.fp = 64;
+ }
+ break;
+ default:
+ as_bad (_("unknown ISA level %s"), name + 4);
+ break;
+ }
+ }
+
+ mips_check_options (&mips_opts, FALSE);
+
+ mips_check_isa_supports_ases ();
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .module pseudo-op. */
+
+static void
+s_module (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ if (!file_mips_opts_checked)
+ {
+ if (!parse_code_option (name))
+ as_bad (_(".module used with unrecognized symbol: %s\n"), name);
+
+ /* Update module level settings from mips_opts. */
+ file_mips_opts = mips_opts;
+ }
+ else
+ as_bad (_(".module is not permitted after generating code"));
+
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .abicalls pseudo-op. I believe this is equivalent to
+ .option pic2. It means to generate SVR4 PIC calls. */
+
+static void
+s_abicalls (int ignore ATTRIBUTE_UNUSED)
+{
+ mips_pic = SVR4_PIC;
+ mips_abicalls = TRUE;
+
+ if (g_switch_seen && g_switch_value != 0)
+ as_warn (_("-G may not be used with SVR4 PIC code"));
+ g_switch_value = 0;
+
+ bfd_set_gp_size (stdoutput, 0);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpload pseudo-op. This is used when generating SVR4
+ PIC code. It sets the $gp register for the function based on the
+ function address, which is in the register named in the argument.
+ This uses a relocation against _gp_disp, which is handled specially
+ by the linker. The result is:
+ lui $gp,%hi(_gp_disp)
+ addiu $gp,$gp,%lo(_gp_disp)
+ addu $gp,$gp,.cpload argument
+ The .cpload argument is normally $25 == $t9.
+
+ The -mno-shared option changes this to:
+ lui $gp,%hi(__gnu_local_gp)
+ addiu $gp,$gp,%lo(__gnu_local_gp)
+ and the argument is ignored. This saves an instruction, but the
+ resulting code is not position independent; it uses an absolute
+ address for __gnu_local_gp. Thus code assembled with -mno-shared
+ can go into an ordinary executable, but not into a shared library. */
+
+static void
+s_cpload (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+ int reg;
+ int in_shared;
+
+ file_mips_check_options ();
+
+ /* If we are not generating SVR4 PIC code, or if this is NewABI code,
+ .cpload is ignored. */
+ if (mips_pic != SVR4_PIC || HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cpload");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* .cpload should be in a .set noreorder section. */
+ if (mips_opts.noreorder == 0)
+ as_warn (_(".cpload not in noreorder section"));
+
+ reg = tc_get_register (0);
+
+ /* If we need to produce a 64-bit address, we are better off using
+ the default instruction sequence. */
+ in_shared = mips_in_shared || HAVE_64BIT_SYMBOLS;
+
+ ex.X_op = O_symbol;
+ ex.X_add_symbol = symbol_find_or_make (in_shared ? "_gp_disp" :
+ "__gnu_local_gp");
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = 0;
+
+ /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */
+ symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
+ macro_start ();
+ macro_build_lui (&ex, mips_gp_register);
+ macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
+ mips_gp_register, BFD_RELOC_LO16);
+ if (in_shared)
+ macro_build (NULL, "addu", "d,v,t", mips_gp_register,
+ mips_gp_register, reg);
+ macro_end ();
+
+ mips_assembling_insn = FALSE;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpsetup pseudo-op defined for NewABI PIC code. The syntax is:
+ .cpsetup $reg1, offset|$reg2, label
+
+ If offset is given, this results in:
+ sd $gp, offset($sp)
+ lui $gp, %hi(%neg(%gp_rel(label)))
+ addiu $gp, $gp, %lo(%neg(%gp_rel(label)))
+ daddu $gp, $gp, $reg1
+
+ If $reg2 is given, this results in:
+ daddu $reg2, $gp, $0
+ lui $gp, %hi(%neg(%gp_rel(label)))
+ addiu $gp, $gp, %lo(%neg(%gp_rel(label)))
+ daddu $gp, $gp, $reg1
+ $reg1 is normally $25 == $t9.
+
+ The -mno-shared option replaces the last three instructions with
+ lui $gp,%hi(_gp)
+ addiu $gp,$gp,%lo(_gp) */
+
+static void
+s_cpsetup (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex_off;
+ expressionS ex_sym;
+ int reg1;
+
+ file_mips_check_options ();
+
+ /* If we are not generating SVR4 PIC code, .cpsetup is ignored.
+ We also need NewABI support. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cpsetup");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ reg1 = tc_get_register (0);
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing argument separator ',' for .cpsetup"));
+ return;
+ }
+ else
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '$')
+ {
+ mips_cpreturn_register = tc_get_register (0);
+ mips_cpreturn_offset = -1;
+ }
+ else
+ {
+ mips_cpreturn_offset = get_absolute_expression ();
+ mips_cpreturn_register = -1;
+ }
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing argument separator ',' for .cpsetup"));
+ return;
+ }
+ else
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ expression (&ex_sym);
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
+ macro_start ();
+ if (mips_cpreturn_register == -1)
+ {
+ ex_off.X_op = O_constant;
+ ex_off.X_add_symbol = NULL;
+ ex_off.X_op_symbol = NULL;
+ ex_off.X_add_number = mips_cpreturn_offset;
+
+ macro_build (&ex_off, "sd", "t,o(b)", mips_gp_register,
+ BFD_RELOC_LO16, SP);
+ }
+ else
+ macro_build (NULL, "daddu", "d,v,t", mips_cpreturn_register,
+ mips_gp_register, 0);
+
+ if (mips_in_shared || HAVE_64BIT_SYMBOLS)
+ {
+ macro_build (&ex_sym, "lui", LUI_FMT, mips_gp_register,
+ -1, BFD_RELOC_GPREL16, BFD_RELOC_MIPS_SUB,
+ BFD_RELOC_HI16_S);
+
+ macro_build (&ex_sym, "addiu", "t,r,j", mips_gp_register,
+ mips_gp_register, -1, BFD_RELOC_GPREL16,
+ BFD_RELOC_MIPS_SUB, BFD_RELOC_LO16);
+
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", mips_gp_register,
+ mips_gp_register, reg1);
+ }
+ else
+ {
+ expressionS ex;
+
+ ex.X_op = O_symbol;
+ ex.X_add_symbol = symbol_find_or_make ("__gnu_local_gp");
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = 0;
+
+ /* In ELF, this symbol is implicitly an STT_OBJECT symbol. */
+ symbol_get_bfdsym (ex.X_add_symbol)->flags |= BSF_OBJECT;
+
+ macro_build_lui (&ex, mips_gp_register);
+ macro_build (&ex, "addiu", "t,r,j", mips_gp_register,
+ mips_gp_register, BFD_RELOC_LO16);
+ }
+
+ macro_end ();
+
+ mips_assembling_insn = FALSE;
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_cplocal (int ignore ATTRIBUTE_UNUSED)
+{
+ file_mips_check_options ();
+
+ /* If we are not generating SVR4 PIC code, or if this is not NewABI code,
+ .cplocal is ignored. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cplocal");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ mips_gp_register = tc_get_register (0);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cprestore pseudo-op. This stores $gp into a given
+ offset from $sp. The offset is remembered, and after making a PIC
+ call $gp is restored from that location. */
+
+static void
+s_cprestore (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ file_mips_check_options ();
+
+ /* If we are not generating SVR4 PIC code, or if this is NewABI code,
+ .cprestore is ignored. */
+ if (mips_pic != SVR4_PIC || HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cprestore");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ mips_cprestore_offset = get_absolute_expression ();
+ mips_cprestore_valid = 1;
+
+ ex.X_op = O_constant;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = mips_cprestore_offset;
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
+ macro_start ();
+ macro_build_ldst_constoffset (&ex, ADDRESS_STORE_INSN, mips_gp_register,
+ SP, HAVE_64BIT_ADDRESSES);
+ macro_end ();
+
+ mips_assembling_insn = FALSE;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpreturn pseudo-op defined for NewABI PIC code. If an offset
+ was given in the preceding .cpsetup, it results in:
+ ld $gp, offset($sp)
+
+ If a register $reg2 was given there, it results in:
+ daddu $gp, $reg2, $0 */
+
+static void
+s_cpreturn (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ file_mips_check_options ();
+
+ /* If we are not generating SVR4 PIC code, .cpreturn is ignored.
+ We also need NewABI support. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if (mips_opts.mips16)
+ {
+ as_bad (_("%s not supported in MIPS16 mode"), ".cpreturn");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
+ macro_start ();
+ if (mips_cpreturn_register == -1)
+ {
+ ex.X_op = O_constant;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ ex.X_add_number = mips_cpreturn_offset;
+
+ macro_build (&ex, "ld", "t,o(b)", mips_gp_register, BFD_RELOC_LO16, SP);
+ }
+ else
+ macro_build (NULL, "daddu", "d,v,t", mips_gp_register,
+ mips_cpreturn_register, 0);
+ macro_end ();
+
+ mips_assembling_insn = FALSE;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .dtprelword, .dtpreldword, .tprelword, or .tpreldword
+ pseudo-op; DIRSTR says which. The pseudo-op generates a BYTES-size
+ DTP- or TP-relative relocation of type RTYPE, for use in either DWARF
+ debug information or MIPS16 TLS. */
+
+static void
+s_tls_rel_directive (const size_t bytes, const char *dirstr,
+ bfd_reloc_code_real_type rtype)
+{
+ expressionS ex;
+ char *p;
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol)
+ {
+ as_bad (_("unsupported use of %s"), dirstr);
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (bytes);
+ md_number_to_chars (p, 0, bytes);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, rtype);
+ demand_empty_rest_of_line ();
+ mips_clear_insn_labels ();
+}
+
+/* Handle .dtprelword. */
+
+static void
+s_dtprelword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_tls_rel_directive (4, ".dtprelword", BFD_RELOC_MIPS_TLS_DTPREL32);
+}
+
+/* Handle .dtpreldword. */
+
+static void
+s_dtpreldword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_tls_rel_directive (8, ".dtpreldword", BFD_RELOC_MIPS_TLS_DTPREL64);
+}
+
+/* Handle .tprelword. */
+
+static void
+s_tprelword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_tls_rel_directive (4, ".tprelword", BFD_RELOC_MIPS_TLS_TPREL32);
+}
+
+/* Handle .tpreldword. */
+
+static void
+s_tpreldword (int ignore ATTRIBUTE_UNUSED)
+{
+ s_tls_rel_directive (8, ".tpreldword", BFD_RELOC_MIPS_TLS_TPREL64);
+}
+
+/* Handle the .gpvalue pseudo-op. This is used when generating NewABI PIC
+ code. It sets the offset to use in gp_rel relocations. */
+
+static void
+s_gpvalue (int ignore ATTRIBUTE_UNUSED)
+{
+ /* If we are not generating SVR4 PIC code, .gpvalue is ignored.
+ We also need NewABI support. */
+ if (mips_pic != SVR4_PIC || ! HAVE_NEWABI)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ mips_gprel_offset = get_absolute_expression ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpword pseudo-op. This is used when generating PIC
+ code. It generates a 32 bit GP relative reloc. */
+
+static void
+s_gpword (int ignore ATTRIBUTE_UNUSED)
+{
+ segment_info_type *si;
+ struct insn_label_list *l;
+ expressionS ex;
+ char *p;
+
+ /* When not generating PIC code, this is treated as .word. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_cons (2);
+ return;
+ }
+
+ si = seg_info (now_seg);
+ l = si->label_list;
+ mips_emit_delays ();
+ if (auto_align)
+ mips_align (2, 0, l);
+
+ expression (&ex);
+ mips_clear_insn_labels ();
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("unsupported use of .gpword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (4);
+ md_number_to_chars (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+ BFD_RELOC_GPREL32);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_gpdword (int ignore ATTRIBUTE_UNUSED)
+{
+ segment_info_type *si;
+ struct insn_label_list *l;
+ expressionS ex;
+ char *p;
+
+ /* When not generating PIC code, this is treated as .dword. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_cons (3);
+ return;
+ }
+
+ si = seg_info (now_seg);
+ l = si->label_list;
+ mips_emit_delays ();
+ if (auto_align)
+ mips_align (3, 0, l);
+
+ expression (&ex);
+ mips_clear_insn_labels ();
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("unsupported use of .gpdword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (8);
+ md_number_to_chars (p, 0, 8);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+ BFD_RELOC_GPREL32)->fx_tcbit = 1;
+
+ /* GPREL32 composed with 64 gives a 64-bit GP offset. */
+ fix_new (frag_now, p - frag_now->fr_literal, 8, NULL, 0,
+ FALSE, BFD_RELOC_64)->fx_tcbit = 1;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .ehword pseudo-op. This is used when generating unwinding
+ tables. It generates a R_MIPS_EH reloc. */
+
+static void
+s_ehword (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+ char *p;
+
+ mips_emit_delays ();
+
+ expression (&ex);
+ mips_clear_insn_labels ();
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("unsupported use of .ehword"));
+ ignore_rest_of_line ();
+ }
+
+ p = frag_more (4);
+ md_number_to_chars (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE,
+ BFD_RELOC_MIPS_EH);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpadd pseudo-op. This is used when dealing with switch
+ tables in SVR4 PIC code. */
+
+static void
+s_cpadd (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+
+ file_mips_check_options ();
+
+ /* This is ignored when not generating SVR4 PIC code. */
+ if (mips_pic != SVR4_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ mips_mark_labels ();
+ mips_assembling_insn = TRUE;
+
+ /* Add $gp to the register named as an argument. */
+ macro_start ();
+ reg = tc_get_register (0);
+ macro_build (NULL, ADDRESS_ADD_INSN, "d,v,t", reg, reg, mips_gp_register);
+ macro_end ();
+
+ mips_assembling_insn = FALSE;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .insn pseudo-op. This marks instruction labels in
+ mips16/micromips mode. This permits the linker to handle them specially,
+ such as generating jalx instructions when needed. We also make
+ them odd for the duration of the assembly, in order to generate the
+ right sort of code. We will make them even in the adjust_symtab
+ routine, while leaving them marked. This is convenient for the
+ debugger and the disassembler. The linker knows to make them odd
+ again. */
+
+static void
+s_insn (int ignore ATTRIBUTE_UNUSED)
+{
+ file_mips_check_options ();
+ file_ase_mips16 |= mips_opts.mips16;
+ file_ase_micromips |= mips_opts.micromips;
+
+ mips_mark_labels ();
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .nan pseudo-op. */
+
+static void
+s_nan (int ignore ATTRIBUTE_UNUSED)
+{
+ static const char str_legacy[] = "legacy";
+ static const char str_2008[] = "2008";
+ size_t i;
+
+ for (i = 0; !is_end_of_line[(unsigned char) input_line_pointer[i]]; i++);
+
+ if (i == sizeof (str_2008) - 1
+ && memcmp (input_line_pointer, str_2008, i) == 0)
+ mips_nan2008 = 1;
+ else if (i == sizeof (str_legacy) - 1
+ && memcmp (input_line_pointer, str_legacy, i) == 0)
+ {
+ if (ISA_HAS_LEGACY_NAN (file_mips_opts.isa))
+ mips_nan2008 = 0;
+ else
+ as_bad (_("`%s' does not support legacy NaN"),
+ mips_cpu_info_from_isa (file_mips_opts.isa)->name);
+ }
+ else
+ as_bad (_("bad .nan directive"));
+
+ input_line_pointer += i;
+ demand_empty_rest_of_line ();
+}
+
+/* Handle a .stab[snd] directive. Ideally these directives would be
+ implemented in a transparent way, so that removing them would not
+ have any effect on the generated instructions. However, s_stab
+ internally changes the section, so in practice we need to decide
+ now whether the preceding label marks compressed code. We do not
+ support changing the compression mode of a label after a .stab*
+ directive, such as in:
+
+ foo:
+ .stabs ...
+ .set mips16
+
+ so the current mode wins. */
+
+static void
+s_mips_stab (int type)
+{
+ mips_mark_labels ();
+ s_stab (type);
+}
+
+/* Handle the .weakext pseudo-op as defined in Kane and Heinrich. */
+
+static void
+s_mips_weakext (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+ expressionS exp;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ S_SET_WEAK (symbolP);
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ if (! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ if (S_IS_DEFINED (symbolP))
+ {
+ as_bad (_("ignoring attempt to redefine symbol %s"),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ expression (&exp);
+ if (exp.X_op != O_symbol)
+ {
+ as_bad (_("bad .weakext directive"));
+ ignore_rest_of_line ();
+ return;
+ }
+ symbol_set_value_expression (symbolP, &exp);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a register string into a number. Called from the ECOFF code
+ to parse .frame. The argument is non-zero if this is the frame
+ register, so that we can record it in mips_frame_reg. */
+
+int
+tc_get_register (int frame)
+{
+ unsigned int reg;
+
+ SKIP_WHITESPACE ();
+ if (! reg_lookup (&input_line_pointer, RWARN | RTYPE_NUM | RTYPE_GP, &reg))
+ reg = 0;
+ if (frame)
+ {
+ mips_frame_reg = reg != 0 ? reg : SP;
+ mips_frame_reg_valid = 1;
+ mips_cprestore_valid = 0;
+ }
+ return reg;
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ /* We don't need to align ELF sections to the full alignment.
+ However, Irix 5 may prefer that we align them at least to a 16
+ byte boundary. We don't bother to align the sections if we
+ are targeted for an embedded system. */
+ if (strncmp (TARGET_OS, "elf", 3) == 0)
+ return addr;
+ if (align > 4)
+ align = 4;
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* Utility routine, called from above as well. If called while the
+ input file is still being read, it's only an approximation. (For
+ example, a symbol may later become defined which appeared to be
+ undefined earlier.) */
+
+static int
+nopic_need_relax (symbolS *sym, int before_relaxing)
+{
+ if (sym == 0)
+ return 0;
+
+ if (g_switch_value > 0)
+ {
+ const char *symname;
+ int change;
+
+ /* Find out whether this symbol can be referenced off the $gp
+ register. It can be if it is smaller than the -G size or if
+ it is in the .sdata or .sbss section. Certain symbols can
+ not be referenced off the $gp, although it appears as though
+ they can. */
+ symname = S_GET_NAME (sym);
+ if (symname != (const char *) NULL
+ && (strcmp (symname, "eprol") == 0
+ || strcmp (symname, "etext") == 0
+ || strcmp (symname, "_gp") == 0
+ || strcmp (symname, "edata") == 0
+ || strcmp (symname, "_fbss") == 0
+ || strcmp (symname, "_fdata") == 0
+ || strcmp (symname, "_ftext") == 0
+ || strcmp (symname, "end") == 0
+ || strcmp (symname, "_gp_disp") == 0))
+ change = 1;
+ else if ((! S_IS_DEFINED (sym) || S_IS_COMMON (sym))
+ && (0
+#ifndef NO_ECOFF_DEBUGGING
+ || (symbol_get_obj (sym)->ecoff_extern_size != 0
+ && (symbol_get_obj (sym)->ecoff_extern_size
+ <= g_switch_value))
+#endif
+ /* We must defer this decision until after the whole
+ file has been read, since there might be a .extern
+ after the first use of this symbol. */
+ || (before_relaxing
+#ifndef NO_ECOFF_DEBUGGING
+ && symbol_get_obj (sym)->ecoff_extern_size == 0
+#endif
+ && S_GET_VALUE (sym) == 0)
+ || (S_GET_VALUE (sym) != 0
+ && S_GET_VALUE (sym) <= g_switch_value)))
+ change = 0;
+ else
+ {
+ const char *segname;
+
+ segname = segment_name (S_GET_SEGMENT (sym));
+ gas_assert (strcmp (segname, ".lit8") != 0
+ && strcmp (segname, ".lit4") != 0);
+ change = (strcmp (segname, ".sdata") != 0
+ && strcmp (segname, ".sbss") != 0
+ && strncmp (segname, ".sdata.", 7) != 0
+ && strncmp (segname, ".sbss.", 6) != 0
+ && strncmp (segname, ".gnu.linkonce.sb.", 17) != 0
+ && strncmp (segname, ".gnu.linkonce.s.", 16) != 0);
+ }
+ return change;
+ }
+ else
+ /* We are not optimizing for the $gp register. */
+ return 1;
+}
+
+
+/* Return true if the given symbol should be considered local for SVR4 PIC. */
+
+static bfd_boolean
+pic_need_relax (symbolS *sym, asection *segtype)
+{
+ asection *symsec;
+
+ /* Handle the case of a symbol equated to another symbol. */
+ while (symbol_equated_reloc_p (sym))
+ {
+ symbolS *n;
+
+ /* It's possible to get a loop here in a badly written program. */
+ n = symbol_get_value_expression (sym)->X_add_symbol;
+ if (n == sym)
+ break;
+ sym = n;
+ }
+
+ if (symbol_section_p (sym))
+ return TRUE;
+
+ symsec = S_GET_SEGMENT (sym);
+
+ /* This must duplicate the test in adjust_reloc_syms. */
+ return (!bfd_is_und_section (symsec)
+ && !bfd_is_abs_section (symsec)
+ && !bfd_is_com_section (symsec)
+ && !s_is_linkonce (sym, segtype)
+ /* A global or weak symbol is treated as external. */
+ && (!S_IS_WEAK (sym) && !S_IS_EXTERNAL (sym)));
+}
+
+
+/* Given a mips16 variant frag FRAGP, return non-zero if it needs an
+ extended opcode. SEC is the section the frag is in. */
+
+static int
+mips16_extended_frag (fragS *fragp, asection *sec, long stretch)
+{
+ int type;
+ const struct mips_int_operand *operand;
+ offsetT val;
+ segT symsec;
+ fragS *sym_frag;
+
+ if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+ return 0;
+ if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+ return 1;
+
+ type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+ operand = mips16_immed_operand (type, FALSE);
+
+ sym_frag = symbol_get_frag (fragp->fr_symbol);
+ val = S_GET_VALUE (fragp->fr_symbol);
+ symsec = S_GET_SEGMENT (fragp->fr_symbol);
+
+ if (operand->root.type == OP_PCREL)
+ {
+ const struct mips_pcrel_operand *pcrel_op;
+ addressT addr;
+ offsetT maxtiny;
+
+ /* We won't have the section when we are called from
+ mips_relax_frag. However, we will always have been called
+ from md_estimate_size_before_relax first. If this is a
+ branch to a different section, we mark it as such. If SEC is
+ NULL, and the frag is not marked, then it must be a branch to
+ the same section. */
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
+ if (sec == NULL)
+ {
+ if (RELAX_MIPS16_LONG_BRANCH (fragp->fr_subtype))
+ return 1;
+ }
+ else
+ {
+ /* Must have been called from md_estimate_size_before_relax. */
+ if (symsec != sec)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+
+ /* FIXME: We should support this, and let the linker
+ catch branches and loads that are out of range. */
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("unsupported PC relative reference to different section"));
+
+ return 1;
+ }
+ if (fragp != sym_frag && sym_frag->fr_address == 0)
+ /* Assume non-extended on the first relaxation pass.
+ The address we have calculated will be bogus if this is
+ a forward branch to another frag, as the forward frag
+ will have fr_address == 0. */
+ return 0;
+ }
+
+ /* In this case, we know for sure that the symbol fragment is in
+ the same section. If the relax_marker of the symbol fragment
+ differs from the relax_marker of this fragment, we have not
+ yet adjusted the symbol fragment fr_address. We want to add
+ in STRETCH in order to get a better estimate of the address.
+ This particularly matters because of the shift bits. */
+ if (stretch != 0
+ && sym_frag->relax_marker != fragp->relax_marker)
+ {
+ fragS *f;
+
+ /* Adjust stretch for any alignment frag. Note that if have
+ been expanding the earlier code, the symbol may be
+ defined in what appears to be an earlier frag. FIXME:
+ This doesn't handle the fr_subtype field, which specifies
+ a maximum number of bytes to skip when doing an
+ alignment. */
+ for (f = fragp; f != NULL && f != sym_frag; f = f->fr_next)
+ {
+ if (f->fr_type == rs_align || f->fr_type == rs_align_code)
+ {
+ if (stretch < 0)
+ stretch = - ((- stretch)
+ & ~ ((1 << (int) f->fr_offset) - 1));
+ else
+ stretch &= ~ ((1 << (int) f->fr_offset) - 1);
+ if (stretch == 0)
+ break;
+ }
+ }
+ if (f != NULL)
+ val += stretch;
+ }
+
+ addr = fragp->fr_address + fragp->fr_fix;
+
+ /* The base address rules are complicated. The base address of
+ a branch is the following instruction. The base address of a
+ PC relative load or add is the instruction itself, but if it
+ is in a delay slot (in which case it can not be extended) use
+ the address of the instruction whose delay slot it is in. */
+ if (pcrel_op->include_isa_bit)
+ {
+ addr += 2;
+
+ /* If we are currently assuming that this frag should be
+ extended, then, the current address is two bytes
+ higher. */
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ addr += 2;
+
+ /* Ignore the low bit in the target, since it will be set
+ for a text label. */
+ val &= -2;
+ }
+ else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+ addr -= 4;
+ else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+ addr -= 2;
+
+ val -= addr & -(1 << pcrel_op->align_log2);
+
+ /* If any of the shifted bits are set, we must use an extended
+ opcode. If the address depends on the size of this
+ instruction, this can lead to a loop, so we arrange to always
+ use an extended opcode. We only check this when we are in
+ the main relaxation loop, when SEC is NULL. */
+ if ((val & ((1 << operand->shift) - 1)) != 0 && sec == NULL)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+ return 1;
+ }
+
+ /* If we are about to mark a frag as extended because the value
+ is precisely the next value above maxtiny, then there is a
+ chance of an infinite loop as in the following code:
+ la $4,foo
+ .skip 1020
+ .align 2
+ foo:
+ In this case when the la is extended, foo is 0x3fc bytes
+ away, so the la can be shrunk, but then foo is 0x400 away, so
+ the la must be extended. To avoid this loop, we mark the
+ frag as extended if it was small, and is about to become
+ extended with the next value above maxtiny. */
+ maxtiny = mips_int_operand_max (operand);
+ if (val == maxtiny + (1 << operand->shift)
+ && ! RELAX_MIPS16_EXTENDED (fragp->fr_subtype)
+ && sec == NULL)
+ {
+ fragp->fr_subtype =
+ RELAX_MIPS16_MARK_LONG_BRANCH (fragp->fr_subtype);
+ return 1;
+ }
+ }
+ else if (symsec != absolute_section && sec != NULL)
+ as_bad_where (fragp->fr_file, fragp->fr_line, _("unsupported relocation"));
+
+ return !mips16_immed_in_range_p (operand, BFD_RELOC_UNUSED, val);
+}
+
+/* Compute the length of a branch sequence, and adjust the
+ RELAX_BRANCH_TOOFAR bit accordingly. If FRAGP is NULL, the
+ worst-case length is computed, with UPDATE being used to indicate
+ whether an unconditional (-1), branch-likely (+1) or regular (0)
+ branch is to be computed. */
+static int
+relaxed_branch_length (fragS *fragp, asection *sec, int update)
+{
+ bfd_boolean toofar;
+ int length;
+
+ if (fragp
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ addressT addr;
+ offsetT val;
+
+ val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+ addr = fragp->fr_address + fragp->fr_fix + 4;
+
+ val -= addr;
+
+ toofar = val < - (0x8000 << 2) || val >= (0x8000 << 2);
+ }
+ else if (fragp)
+ /* If the symbol is not defined or it's in a different segment,
+ assume the user knows what's going on and emit a short
+ branch. */
+ toofar = FALSE;
+ else
+ toofar = TRUE;
+
+ if (fragp && update && toofar != RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+ fragp->fr_subtype
+ = RELAX_BRANCH_ENCODE (RELAX_BRANCH_AT (fragp->fr_subtype),
+ RELAX_BRANCH_UNCOND (fragp->fr_subtype),
+ RELAX_BRANCH_LIKELY (fragp->fr_subtype),
+ RELAX_BRANCH_LINK (fragp->fr_subtype),
+ toofar);
+
+ length = 4;
+ if (toofar)
+ {
+ if (fragp ? RELAX_BRANCH_LIKELY (fragp->fr_subtype) : (update > 0))
+ length += 8;
+
+ if (mips_pic != NO_PIC)
+ {
+ /* Additional space for PIC loading of target address. */
+ length += 8;
+ if (mips_opts.isa == ISA_MIPS1)
+ /* Additional space for $at-stabilizing nop. */
+ length += 4;
+ }
+
+ /* If branch is conditional. */
+ if (fragp ? !RELAX_BRANCH_UNCOND (fragp->fr_subtype) : (update >= 0))
+ length += 8;
+ }
+
+ return length;
+}
+
+/* Compute the length of a branch sequence, and adjust the
+ RELAX_MICROMIPS_TOOFAR32 bit accordingly. If FRAGP is NULL, the
+ worst-case length is computed, with UPDATE being used to indicate
+ whether an unconditional (-1), or regular (0) branch is to be
+ computed. */
+
+static int
+relaxed_micromips_32bit_branch_length (fragS *fragp, asection *sec, int update)
+{
+ bfd_boolean toofar;
+ int length;
+
+ if (fragp
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ addressT addr;
+ offsetT val;
+
+ val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+ /* Ignore the low bit in the target, since it will be set
+ for a text label. */
+ if ((val & 1) != 0)
+ --val;
+
+ addr = fragp->fr_address + fragp->fr_fix + 4;
+
+ val -= addr;
+
+ toofar = val < - (0x8000 << 1) || val >= (0x8000 << 1);
+ }
+ else if (fragp)
+ /* If the symbol is not defined or it's in a different segment,
+ assume the user knows what's going on and emit a short
+ branch. */
+ toofar = FALSE;
+ else
+ toofar = TRUE;
+
+ if (fragp && update
+ && toofar != RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
+ fragp->fr_subtype = (toofar
+ ? RELAX_MICROMIPS_MARK_TOOFAR32 (fragp->fr_subtype)
+ : RELAX_MICROMIPS_CLEAR_TOOFAR32 (fragp->fr_subtype));
+
+ length = 4;
+ if (toofar)
+ {
+ bfd_boolean compact_known = fragp != NULL;
+ bfd_boolean compact = FALSE;
+ bfd_boolean uncond;
+
+ if (compact_known)
+ compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+ if (fragp)
+ uncond = RELAX_MICROMIPS_UNCOND (fragp->fr_subtype);
+ else
+ uncond = update < 0;
+
+ /* If label is out of range, we turn branch <br>:
+
+ <br> label # 4 bytes
+ 0:
+
+ into:
+
+ j label # 4 bytes
+ nop # 2 bytes if compact && !PIC
+ 0:
+ */
+ if (mips_pic == NO_PIC && (!compact_known || compact))
+ length += 2;
+
+ /* If assembling PIC code, we further turn:
+
+ j label # 4 bytes
+
+ into:
+
+ lw/ld at, %got(label)(gp) # 4 bytes
+ d/addiu at, %lo(label) # 4 bytes
+ jr/c at # 2 bytes
+ */
+ if (mips_pic != NO_PIC)
+ length += 6;
+
+ /* If branch <br> is conditional, we prepend negated branch <brneg>:
+
+ <brneg> 0f # 4 bytes
+ nop # 2 bytes if !compact
+ */
+ if (!uncond)
+ length += (compact_known && compact) ? 4 : 6;
+ }
+
+ return length;
+}
+
+/* Compute the length of a branch, and adjust the RELAX_MICROMIPS_TOOFAR16
+ bit accordingly. */
+
+static int
+relaxed_micromips_16bit_branch_length (fragS *fragp, asection *sec, int update)
+{
+ bfd_boolean toofar;
+
+ if (fragp
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ addressT addr;
+ offsetT val;
+ int type;
+
+ val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+ /* Ignore the low bit in the target, since it will be set
+ for a text label. */
+ if ((val & 1) != 0)
+ --val;
+
+ /* Assume this is a 2-byte branch. */
+ addr = fragp->fr_address + fragp->fr_fix + 2;
+
+ /* We try to avoid the infinite loop by not adding 2 more bytes for
+ long branches. */
+
+ val -= addr;
+
+ type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
+ if (type == 'D')
+ toofar = val < - (0x200 << 1) || val >= (0x200 << 1);
+ else if (type == 'E')
+ toofar = val < - (0x40 << 1) || val >= (0x40 << 1);
+ else
+ abort ();
+ }
+ else
+ /* If the symbol is not defined or it's in a different segment,
+ we emit a normal 32-bit branch. */
+ toofar = TRUE;
+
+ if (fragp && update
+ && toofar != RELAX_MICROMIPS_TOOFAR16 (fragp->fr_subtype))
+ fragp->fr_subtype
+ = toofar ? RELAX_MICROMIPS_MARK_TOOFAR16 (fragp->fr_subtype)
+ : RELAX_MICROMIPS_CLEAR_TOOFAR16 (fragp->fr_subtype);
+
+ if (toofar)
+ return 4;
+
+ return 2;
+}
+
+/* Estimate the size of a frag before relaxing. Unless this is the
+ mips16, we are not really relaxing here, and the final size is
+ encoded in the subtype information. For the mips16, we have to
+ decide whether we are using an extended opcode or not. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *segtype)
+{
+ int change;
+
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+
+ fragp->fr_var = relaxed_branch_length (fragp, segtype, FALSE);
+
+ return fragp->fr_var;
+ }
+
+ if (RELAX_MIPS16_P (fragp->fr_subtype))
+ /* We don't want to modify the EXTENDED bit here; it might get us
+ into infinite loops. We change it only in mips_relax_frag(). */
+ return (RELAX_MIPS16_EXTENDED (fragp->fr_subtype) ? 4 : 2);
+
+ if (RELAX_MICROMIPS_P (fragp->fr_subtype))
+ {
+ int length = 4;
+
+ if (RELAX_MICROMIPS_TYPE (fragp->fr_subtype) != 0)
+ length = relaxed_micromips_16bit_branch_length (fragp, segtype, FALSE);
+ if (length == 4 && RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype))
+ length = relaxed_micromips_32bit_branch_length (fragp, segtype, FALSE);
+ fragp->fr_var = length;
+
+ return length;
+ }
+
+ if (mips_pic == NO_PIC)
+ change = nopic_need_relax (fragp->fr_symbol, 0);
+ else if (mips_pic == SVR4_PIC)
+ change = pic_need_relax (fragp->fr_symbol, segtype);
+ else if (mips_pic == VXWORKS_PIC)
+ /* For vxworks, GOT16 relocations never have a corresponding LO16. */
+ change = 0;
+ else
+ abort ();
+
+ if (change)
+ {
+ fragp->fr_subtype |= RELAX_USE_SECOND;
+ return -RELAX_FIRST (fragp->fr_subtype);
+ }
+ else
+ return -RELAX_SECOND (fragp->fr_subtype);
+}
+
+/* This is called to see whether a reloc against a defined symbol
+ should be converted into a reloc against a section. */
+
+int
+mips_fix_adjustable (fixS *fixp)
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ if (fixp->fx_addsy == NULL)
+ return 1;
+
+ /* If symbol SYM is in a mergeable section, relocations of the form
+ SYM + 0 can usually be made section-relative. The mergeable data
+ is then identified by the section offset rather than by the symbol.
+
+ However, if we're generating REL LO16 relocations, the offset is split
+ between the LO16 and parterning high part relocation. The linker will
+ need to recalculate the complete offset in order to correctly identify
+ the merge data.
+
+ The linker has traditionally not looked for the parterning high part
+ relocation, and has thus allowed orphaned R_MIPS_LO16 relocations to be
+ placed anywhere. Rather than break backwards compatibility by changing
+ this, it seems better not to force the issue, and instead keep the
+ original symbol. This will work with either linker behavior. */
+ if ((lo16_reloc_p (fixp->fx_r_type)
+ || reloc_needs_lo_p (fixp->fx_r_type))
+ && HAVE_IN_PLACE_ADDENDS
+ && (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE) != 0)
+ return 0;
+
+ /* There is no place to store an in-place offset for JALR relocations.
+ Likewise an in-range offset of limited PC-relative relocations may
+ overflow the in-place relocatable field if recalculated against the
+ start address of the symbol's containing section.
+
+ Also, PC relative relocations for MIPS R6 need to be symbol rather than
+ section relative to allow linker relaxations to be performed later on. */
+ if ((HAVE_IN_PLACE_ADDENDS || ISA_IS_R6 (mips_opts.isa))
+ && (limited_pcrel_reloc_p (fixp->fx_r_type)
+ || jalr_reloc_p (fixp->fx_r_type)))
+ return 0;
+
+ /* R_MIPS16_26 relocations against non-MIPS16 functions might resolve
+ to a floating-point stub. The same is true for non-R_MIPS16_26
+ relocations against MIPS16 functions; in this case, the stub becomes
+ the function's canonical address.
+
+ Floating-point stubs are stored in unique .mips16.call.* or
+ .mips16.fn.* sections. If a stub T for function F is in section S,
+ the first relocation in section S must be against F; this is how the
+ linker determines the target function. All relocations that might
+ resolve to T must also be against F. We therefore have the following
+ restrictions, which are given in an intentionally-redundant way:
+
+ 1. We cannot reduce R_MIPS16_26 relocations against non-MIPS16
+ symbols.
+
+ 2. We cannot reduce a stub's relocations against non-MIPS16 symbols
+ if that stub might be used.
+
+ 3. We cannot reduce non-R_MIPS16_26 relocations against MIPS16
+ symbols.
+
+ 4. We cannot reduce a stub's relocations against MIPS16 symbols if
+ that stub might be used.
+
+ There is a further restriction:
+
+ 5. We cannot reduce jump relocations (R_MIPS_26, R_MIPS16_26 or
+ R_MICROMIPS_26_S1) against MIPS16 or microMIPS symbols on
+ targets with in-place addends; the relocation field cannot
+ encode the low bit.
+
+ For simplicity, we deal with (3)-(4) by not reducing _any_ relocation
+ against a MIPS16 symbol. We deal with (5) by by not reducing any
+ such relocations on REL targets.
+
+ We deal with (1)-(2) by saying that, if there's a R_MIPS16_26
+ relocation against some symbol R, no relocation against R may be
+ reduced. (Note that this deals with (2) as well as (1) because
+ relocations against global symbols will never be reduced on ELF
+ targets.) This approach is a little simpler than trying to detect
+ stub sections, and gives the "all or nothing" per-symbol consistency
+ that we have for MIPS16 symbols. */
+ if (fixp->fx_subsy == NULL
+ && (ELF_ST_IS_MIPS16 (S_GET_OTHER (fixp->fx_addsy))
+ || *symbol_get_tc (fixp->fx_addsy)
+ || (HAVE_IN_PLACE_ADDENDS
+ && ELF_ST_IS_MICROMIPS (S_GET_OTHER (fixp->fx_addsy))
+ && jmp_reloc_p (fixp->fx_r_type))))
+ return 0;
+
+ return 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ static arelent *retval[4];
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ memset (retval, 0, sizeof(retval));
+ reloc = retval[0] = (arelent *) xcalloc (1, sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_pcrel)
+ {
+ gas_assert (fixp->fx_r_type == BFD_RELOC_16_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MICROMIPS_7_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_MICROMIPS_10_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_MICROMIPS_16_PCREL_S1
+ || fixp->fx_r_type == BFD_RELOC_32_PCREL
+ || fixp->fx_r_type == BFD_RELOC_MIPS_21_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_26_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_MIPS_18_PCREL_S3
+ || fixp->fx_r_type == BFD_RELOC_MIPS_19_PCREL_S2
+ || fixp->fx_r_type == BFD_RELOC_HI16_S_PCREL
+ || fixp->fx_r_type == BFD_RELOC_LO16_PCREL);
+
+ /* At this point, fx_addnumber is "symbol offset - pcrel address".
+ Relocations want only the symbol offset. */
+ reloc->addend = fixp->fx_addnumber + reloc->address;
+ }
+ else
+ reloc->addend = fixp->fx_addnumber;
+
+ /* Since the old MIPS ELF ABI uses Rel instead of Rela, encode the vtable
+ entry to be used in the relocation's section offset. */
+ if (! HAVE_NEWABI && fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ reloc->address = reloc->addend;
+ reloc->addend = 0;
+ }
+
+ code = fixp->fx_r_type;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file"
+ " format"),
+ bfd_get_reloc_code_name (code));
+ retval[0] = NULL;
+ }
+
+ return retval;
+}
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+int
+mips_relax_frag (asection *sec, fragS *fragp, long stretch)
+{
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+ offsetT old_var = fragp->fr_var;
+
+ fragp->fr_var = relaxed_branch_length (fragp, sec, TRUE);
+
+ return fragp->fr_var - old_var;
+ }
+
+ if (RELAX_MICROMIPS_P (fragp->fr_subtype))
+ {
+ offsetT old_var = fragp->fr_var;
+ offsetT new_var = 4;
+
+ if (RELAX_MICROMIPS_TYPE (fragp->fr_subtype) != 0)
+ new_var = relaxed_micromips_16bit_branch_length (fragp, sec, TRUE);
+ if (new_var == 4 && RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype))
+ new_var = relaxed_micromips_32bit_branch_length (fragp, sec, TRUE);
+ fragp->fr_var = new_var;
+
+ return new_var - old_var;
+ }
+
+ if (! RELAX_MIPS16_P (fragp->fr_subtype))
+ return 0;
+
+ if (mips16_extended_frag (fragp, NULL, stretch))
+ {
+ if (RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ return 0;
+ fragp->fr_subtype = RELAX_MIPS16_MARK_EXTENDED (fragp->fr_subtype);
+ return 2;
+ }
+ else
+ {
+ if (! RELAX_MIPS16_EXTENDED (fragp->fr_subtype))
+ return 0;
+ fragp->fr_subtype = RELAX_MIPS16_CLEAR_EXTENDED (fragp->fr_subtype);
+ return -2;
+ }
+
+ return 0;
+}
+
+/* Convert a machine dependent frag. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec, fragS *fragp)
+{
+ if (RELAX_BRANCH_P (fragp->fr_subtype))
+ {
+ char *buf;
+ unsigned long insn;
+ expressionS exp;
+ fixS *fixp;
+
+ buf = fragp->fr_literal + fragp->fr_fix;
+ insn = read_insn (buf);
+
+ if (!RELAX_BRANCH_TOOFAR (fragp->fr_subtype))
+ {
+ /* We generate a fixup instead of applying it right now
+ because, if there are linker relaxations, we're going to
+ need the relocations. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
+ BFD_RELOC_16_PCREL_S2);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_insn (buf, insn);
+ }
+ else
+ {
+ int i;
+
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ _("relaxed out-of-range branch into a jump"));
+
+ if (RELAX_BRANCH_UNCOND (fragp->fr_subtype))
+ goto uncond;
+
+ if (!RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+ {
+ /* Reverse the branch. */
+ switch ((insn >> 28) & 0xf)
+ {
+ case 4:
+ if ((insn & 0xff000000) == 0x47000000
+ || (insn & 0xff600000) == 0x45600000)
+ {
+ /* BZ.df/BNZ.df, BZ.V/BNZ.V can have the condition
+ reversed by tweaking bit 23. */
+ insn ^= 0x00800000;
+ }
+ else
+ {
+ /* bc[0-3][tf]l? instructions can have the condition
+ reversed by tweaking a single TF bit, and their
+ opcodes all have 0x4???????. */
+ gas_assert ((insn & 0xf3e00000) == 0x41000000);
+ insn ^= 0x00010000;
+ }
+ break;
+
+ case 0:
+ /* bltz 0x04000000 bgez 0x04010000
+ bltzal 0x04100000 bgezal 0x04110000 */
+ gas_assert ((insn & 0xfc0e0000) == 0x04000000);
+ insn ^= 0x00010000;
+ break;
+
+ case 1:
+ /* beq 0x10000000 bne 0x14000000
+ blez 0x18000000 bgtz 0x1c000000 */
+ insn ^= 0x04000000;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ if (RELAX_BRANCH_LINK (fragp->fr_subtype))
+ {
+ /* Clear the and-link bit. */
+ gas_assert ((insn & 0xfc1c0000) == 0x04100000);
+
+ /* bltzal 0x04100000 bgezal 0x04110000
+ bltzall 0x04120000 bgezall 0x04130000 */
+ insn &= ~0x00100000;
+ }
+
+ /* Branch over the branch (if the branch was likely) or the
+ full jump (not likely case). Compute the offset from the
+ current instruction to branch to. */
+ if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+ i = 16;
+ else
+ {
+ /* How many bytes in instructions we've already emitted? */
+ i = buf - fragp->fr_literal - fragp->fr_fix;
+ /* How many bytes in instructions from here to the end? */
+ i = fragp->fr_var - i;
+ }
+ /* Convert to instruction count. */
+ i >>= 2;
+ /* Branch counts from the next instruction. */
+ i--;
+ insn |= i;
+ /* Branch over the jump. */
+ buf = write_insn (buf, insn);
+
+ /* nop */
+ buf = write_insn (buf, 0);
+
+ if (RELAX_BRANCH_LIKELY (fragp->fr_subtype))
+ {
+ /* beql $0, $0, 2f */
+ insn = 0x50000000;
+ /* Compute the PC offset from the current instruction to
+ the end of the variable frag. */
+ /* How many bytes in instructions we've already emitted? */
+ i = buf - fragp->fr_literal - fragp->fr_fix;
+ /* How many bytes in instructions from here to the end? */
+ i = fragp->fr_var - i;
+ /* Convert to instruction count. */
+ i >>= 2;
+ /* Don't decrement i, because we want to branch over the
+ delay slot. */
+ insn |= i;
+
+ buf = write_insn (buf, insn);
+ buf = write_insn (buf, 0);
+ }
+
+ uncond:
+ if (mips_pic == NO_PIC)
+ {
+ /* j or jal. */
+ insn = (RELAX_BRANCH_LINK (fragp->fr_subtype)
+ ? 0x0c000000 : 0x08000000);
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+ FALSE, BFD_RELOC_MIPS_JMP);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_insn (buf, insn);
+ }
+ else
+ {
+ unsigned long at = RELAX_BRANCH_AT (fragp->fr_subtype);
+
+ /* lw/ld $at, <sym>($gp) R_MIPS_GOT16 */
+ insn = HAVE_64BIT_ADDRESSES ? 0xdf800000 : 0x8f800000;
+ insn |= at << OP_SH_RT;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ if (fragp->fr_offset)
+ {
+ exp.X_add_symbol = make_expr_symbol (&exp);
+ exp.X_add_number = 0;
+ }
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+ FALSE, BFD_RELOC_MIPS_GOT16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_insn (buf, insn);
+
+ if (mips_opts.isa == ISA_MIPS1)
+ /* nop */
+ buf = write_insn (buf, 0);
+
+ /* d/addiu $at, $at, <sym> R_MIPS_LO16 */
+ insn = HAVE_64BIT_ADDRESSES ? 0x64000000 : 0x24000000;
+ insn |= at << OP_SH_RS | at << OP_SH_RT;
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp,
+ FALSE, BFD_RELOC_LO16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_insn (buf, insn);
+
+ /* j(al)r $at. */
+ if (RELAX_BRANCH_LINK (fragp->fr_subtype))
+ insn = 0x0000f809;
+ else
+ insn = 0x00000008;
+ insn |= at << OP_SH_RS;
+
+ buf = write_insn (buf, insn);
+ }
+ }
+
+ fragp->fr_fix += fragp->fr_var;
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+ return;
+ }
+
+ /* Relax microMIPS branches. */
+ if (RELAX_MICROMIPS_P (fragp->fr_subtype))
+ {
+ char *buf = fragp->fr_literal + fragp->fr_fix;
+ bfd_boolean compact = RELAX_MICROMIPS_COMPACT (fragp->fr_subtype);
+ bfd_boolean al = RELAX_MICROMIPS_LINK (fragp->fr_subtype);
+ int type = RELAX_MICROMIPS_TYPE (fragp->fr_subtype);
+ bfd_boolean short_ds;
+ unsigned long insn;
+ expressionS exp;
+ fixS *fixp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ fragp->fr_fix += fragp->fr_var;
+
+ /* Handle 16-bit branches that fit or are forced to fit. */
+ if (type != 0 && !RELAX_MICROMIPS_TOOFAR16 (fragp->fr_subtype))
+ {
+ /* We generate a fixup instead of applying it right now,
+ because if there is linker relaxation, we're going to
+ need the relocations. */
+ if (type == 'D')
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
+ BFD_RELOC_MICROMIPS_10_PCREL_S1);
+ else if (type == 'E')
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 2, &exp, TRUE,
+ BFD_RELOC_MICROMIPS_7_PCREL_S1);
+ else
+ abort ();
+
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ /* These relocations can have an addend that won't fit in
+ 2 octets. */
+ fixp->fx_no_overflow = 1;
+
+ return;
+ }
+
+ /* Handle 32-bit branches that fit or are forced to fit. */
+ if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
+ || !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
+ {
+ /* We generate a fixup instead of applying it right now,
+ because if there is linker relaxation, we're going to
+ need the relocations. */
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, TRUE,
+ BFD_RELOC_MICROMIPS_16_PCREL_S1);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ if (type == 0)
+ return;
+ }
+
+ /* Relax 16-bit branches to 32-bit branches. */
+ if (type != 0)
+ {
+ insn = read_compressed_insn (buf, 2);
+
+ if ((insn & 0xfc00) == 0xcc00) /* b16 */
+ insn = 0x94000000; /* beq */
+ else if ((insn & 0xdc00) == 0x8c00) /* beqz16/bnez16 */
+ {
+ unsigned long regno;
+
+ regno = (insn >> MICROMIPSOP_SH_MD) & MICROMIPSOP_MASK_MD;
+ regno = micromips_to_32_reg_d_map [regno];
+ insn = ((insn & 0x2000) << 16) | 0x94000000; /* beq/bne */
+ insn |= regno << MICROMIPSOP_SH_RS;
+ }
+ else
+ abort ();
+
+ /* Nothing else to do, just write it out. */
+ if (!RELAX_MICROMIPS_RELAX32 (fragp->fr_subtype)
+ || !RELAX_MICROMIPS_TOOFAR32 (fragp->fr_subtype))
+ {
+ buf = write_compressed_insn (buf, insn, 4);
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+ return;
+ }
+ }
+ else
+ insn = read_compressed_insn (buf, 4);
+
+ /* Relax 32-bit branches to a sequence of instructions. */
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ _("relaxed out-of-range branch into a jump"));
+
+ /* Set the short-delay-slot bit. */
+ short_ds = al && (insn & 0x02000000) != 0;
+
+ if (!RELAX_MICROMIPS_UNCOND (fragp->fr_subtype))
+ {
+ symbolS *l;
+
+ /* Reverse the branch. */
+ if ((insn & 0xfc000000) == 0x94000000 /* beq */
+ || (insn & 0xfc000000) == 0xb4000000) /* bne */
+ insn ^= 0x20000000;
+ else if ((insn & 0xffe00000) == 0x40000000 /* bltz */
+ || (insn & 0xffe00000) == 0x40400000 /* bgez */
+ || (insn & 0xffe00000) == 0x40800000 /* blez */
+ || (insn & 0xffe00000) == 0x40c00000 /* bgtz */
+ || (insn & 0xffe00000) == 0x40a00000 /* bnezc */
+ || (insn & 0xffe00000) == 0x40e00000 /* beqzc */
+ || (insn & 0xffe00000) == 0x40200000 /* bltzal */
+ || (insn & 0xffe00000) == 0x40600000 /* bgezal */
+ || (insn & 0xffe00000) == 0x42200000 /* bltzals */
+ || (insn & 0xffe00000) == 0x42600000) /* bgezals */
+ insn ^= 0x00400000;
+ else if ((insn & 0xffe30000) == 0x43800000 /* bc1f */
+ || (insn & 0xffe30000) == 0x43a00000 /* bc1t */
+ || (insn & 0xffe30000) == 0x42800000 /* bc2f */
+ || (insn & 0xffe30000) == 0x42a00000) /* bc2t */
+ insn ^= 0x00200000;
+ else if ((insn & 0xff000000) == 0x83000000 /* BZ.df
+ BNZ.df */
+ || (insn & 0xff600000) == 0x81600000) /* BZ.V
+ BNZ.V */
+ insn ^= 0x00800000;
+ else
+ abort ();
+
+ if (al)
+ {
+ /* Clear the and-link and short-delay-slot bits. */
+ gas_assert ((insn & 0xfda00000) == 0x40200000);
+
+ /* bltzal 0x40200000 bgezal 0x40600000 */
+ /* bltzals 0x42200000 bgezals 0x42600000 */
+ insn &= ~0x02200000;
+ }
+
+ /* Make a label at the end for use with the branch. */
+ l = symbol_new (micromips_label_name (), asec, fragp->fr_fix, fragp);
+ micromips_label_inc ();
+ S_SET_OTHER (l, ELF_ST_SET_MICROMIPS (S_GET_OTHER (l)));
+
+ /* Refer to it. */
+ fixp = fix_new (fragp, buf - fragp->fr_literal, 4, l, 0, TRUE,
+ BFD_RELOC_MICROMIPS_16_PCREL_S1);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ /* Branch over the jump. */
+ buf = write_compressed_insn (buf, insn, 4);
+ if (!compact)
+ /* nop */
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ }
+
+ if (mips_pic == NO_PIC)
+ {
+ unsigned long jal = short_ds ? 0x74000000 : 0xf4000000; /* jal/s */
+
+ /* j/jal/jals <sym> R_MICROMIPS_26_S1 */
+ insn = al ? jal : 0xd4000000;
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+ BFD_RELOC_MICROMIPS_JMP);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_compressed_insn (buf, insn, 4);
+ if (compact)
+ /* nop */
+ buf = write_compressed_insn (buf, 0x0c00, 2);
+ }
+ else
+ {
+ unsigned long at = RELAX_MICROMIPS_AT (fragp->fr_subtype);
+ unsigned long jalr = short_ds ? 0x45e0 : 0x45c0; /* jalr/s */
+ unsigned long jr = compact ? 0x45a0 : 0x4580; /* jr/c */
+
+ /* lw/ld $at, <sym>($gp) R_MICROMIPS_GOT16 */
+ insn = HAVE_64BIT_ADDRESSES ? 0xdc1c0000 : 0xfc1c0000;
+ insn |= at << MICROMIPSOP_SH_RT;
+
+ if (exp.X_add_number)
+ {
+ exp.X_add_symbol = make_expr_symbol (&exp);
+ exp.X_add_number = 0;
+ }
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+ BFD_RELOC_MICROMIPS_GOT16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_compressed_insn (buf, insn, 4);
+
+ /* d/addiu $at, $at, <sym> R_MICROMIPS_LO16 */
+ insn = HAVE_64BIT_ADDRESSES ? 0x5c000000 : 0x30000000;
+ insn |= at << MICROMIPSOP_SH_RT | at << MICROMIPSOP_SH_RS;
+
+ fixp = fix_new_exp (fragp, buf - fragp->fr_literal, 4, &exp, FALSE,
+ BFD_RELOC_MICROMIPS_LO16);
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ buf = write_compressed_insn (buf, insn, 4);
+
+ /* jr/jrc/jalr/jalrs $at */
+ insn = al ? jalr : jr;
+ insn |= at << MICROMIPSOP_SH_MJ;
+
+ buf = write_compressed_insn (buf, insn, 2);
+ }
+
+ gas_assert (buf == fragp->fr_literal + fragp->fr_fix);
+ return;
+ }
+
+ if (RELAX_MIPS16_P (fragp->fr_subtype))
+ {
+ int type;
+ const struct mips_int_operand *operand;
+ offsetT val;
+ char *buf;
+ unsigned int user_length, length;
+ unsigned long insn;
+ bfd_boolean ext;
+
+ type = RELAX_MIPS16_TYPE (fragp->fr_subtype);
+ operand = mips16_immed_operand (type, FALSE);
+
+ ext = RELAX_MIPS16_EXTENDED (fragp->fr_subtype);
+ val = resolve_symbol_value (fragp->fr_symbol);
+ if (operand->root.type == OP_PCREL)
+ {
+ const struct mips_pcrel_operand *pcrel_op;
+ addressT addr;
+
+ pcrel_op = (const struct mips_pcrel_operand *) operand;
+ addr = fragp->fr_address + fragp->fr_fix;
+
+ /* The rules for the base address of a PC relative reloc are
+ complicated; see mips16_extended_frag. */
+ if (pcrel_op->include_isa_bit)
+ {
+ addr += 2;
+ if (ext)
+ addr += 2;
+ /* Ignore the low bit in the target, since it will be
+ set for a text label. */
+ val &= -2;
+ }
+ else if (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype))
+ addr -= 4;
+ else if (RELAX_MIPS16_DSLOT (fragp->fr_subtype))
+ addr -= 2;
+
+ addr &= -(1 << pcrel_op->align_log2);
+ val -= addr;
+
+ /* Make sure the section winds up with the alignment we have
+ assumed. */
+ if (operand->shift > 0)
+ record_alignment (asec, operand->shift);
+ }
+
+ if (ext
+ && (RELAX_MIPS16_JAL_DSLOT (fragp->fr_subtype)
+ || RELAX_MIPS16_DSLOT (fragp->fr_subtype)))
+ as_warn_where (fragp->fr_file, fragp->fr_line,
+ _("extended instruction in delay slot"));
+
+ buf = fragp->fr_literal + fragp->fr_fix;
+
+ insn = read_compressed_insn (buf, 2);
+ if (ext)
+ insn |= MIPS16_EXTEND;
+
+ if (RELAX_MIPS16_USER_EXT (fragp->fr_subtype))
+ user_length = 4;
+ else if (RELAX_MIPS16_USER_SMALL (fragp->fr_subtype))
+ user_length = 2;
+ else
+ user_length = 0;
+
+ mips16_immed (fragp->fr_file, fragp->fr_line, type,
+ BFD_RELOC_UNUSED, val, user_length, &insn);
+
+ length = (ext ? 4 : 2);
+ gas_assert (mips16_opcode_length (insn) == length);
+ write_compressed_insn (buf, insn, length);
+ fragp->fr_fix += length;
+ }
+ else
+ {
+ relax_substateT subtype = fragp->fr_subtype;
+ bfd_boolean second_longer = (subtype & RELAX_SECOND_LONGER) != 0;
+ bfd_boolean use_second = (subtype & RELAX_USE_SECOND) != 0;
+ int first, second;
+ fixS *fixp;
+
+ first = RELAX_FIRST (subtype);
+ second = RELAX_SECOND (subtype);
+ fixp = (fixS *) fragp->fr_opcode;
+
+ /* If the delay slot chosen does not match the size of the instruction,
+ then emit a warning. */
+ if ((!use_second && (subtype & RELAX_DELAY_SLOT_SIZE_FIRST) != 0)
+ || (use_second && (subtype & RELAX_DELAY_SLOT_SIZE_SECOND) != 0))
+ {
+ relax_substateT s;
+ const char *msg;
+
+ s = subtype & (RELAX_DELAY_SLOT_16BIT
+ | RELAX_DELAY_SLOT_SIZE_FIRST
+ | RELAX_DELAY_SLOT_SIZE_SECOND);
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn_where (fragp->fr_file, fragp->fr_line, "%s", msg);
+ subtype &= ~s;
+ }
+
+ /* Possibly emit a warning if we've chosen the longer option. */
+ if (use_second == second_longer)
+ {
+ relax_substateT s;
+ const char *msg;
+
+ s = (subtype
+ & (RELAX_SECOND_LONGER | RELAX_NOMACRO | RELAX_DELAY_SLOT));
+ msg = macro_warning (s);
+ if (msg != NULL)
+ as_warn_where (fragp->fr_file, fragp->fr_line, "%s", msg);
+ subtype &= ~s;
+ }
+
+ /* Go through all the fixups for the first sequence. Disable them
+ (by marking them as done) if we're going to use the second
+ sequence instead. */
+ while (fixp
+ && fixp->fx_frag == fragp
+ && fixp->fx_where < fragp->fr_fix - second)
+ {
+ if (subtype & RELAX_USE_SECOND)
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+
+ /* Go through the fixups for the second sequence. Disable them if
+ we're going to use the first sequence, otherwise adjust their
+ addresses to account for the relaxation. */
+ while (fixp && fixp->fx_frag == fragp)
+ {
+ if (subtype & RELAX_USE_SECOND)
+ fixp->fx_where -= first;
+ else
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+
+ /* Now modify the frag contents. */
+ if (subtype & RELAX_USE_SECOND)
+ {
+ char *start;
+
+ start = fragp->fr_literal + fragp->fr_fix - first - second;
+ memmove (start, start + first, second);
+ fragp->fr_fix -= first;
+ }
+ else
+ fragp->fr_fix -= second;
+ }
+}
+
+/* This function is called after the relocs have been generated.
+ We've been storing mips16 text labels as odd. Here we convert them
+ back to even for the convenience of the debugger. */
+
+void
+mips_frob_file_after_relocs (void)
+{
+ asymbol **syms;
+ unsigned int count, i;
+
+ syms = bfd_get_outsymbols (stdoutput);
+ count = bfd_get_symcount (stdoutput);
+ for (i = 0; i < count; i++, syms++)
+ if (ELF_ST_IS_COMPRESSED (elf_symbol (*syms)->internal_elf_sym.st_other)
+ && ((*syms)->value & 1) != 0)
+ {
+ (*syms)->value &= ~1;
+ /* If the symbol has an odd size, it was probably computed
+ incorrectly, so adjust that as well. */
+ if ((elf_symbol (*syms)->internal_elf_sym.st_size & 1) != 0)
+ ++elf_symbol (*syms)->internal_elf_sym.st_size;
+ }
+}
+
+/* This function is called whenever a label is defined, including fake
+ labels instantiated off the dot special symbol. It is used when
+ handling branch delays; if a branch has a label, we assume we cannot
+ move it. This also bumps the value of the symbol by 1 in compressed
+ code. */
+
+static void
+mips_record_label (symbolS *sym)
+{
+ segment_info_type *si = seg_info (now_seg);
+ struct insn_label_list *l;
+
+ if (free_insn_labels == NULL)
+ l = (struct insn_label_list *) xmalloc (sizeof *l);
+ else
+ {
+ l = free_insn_labels;
+ free_insn_labels = l->next;
+ }
+
+ l->label = sym;
+ l->next = si->label_list;
+ si->label_list = l;
+}
+
+/* This function is called as tc_frob_label() whenever a label is defined
+ and adds a DWARF-2 record we only want for true labels. */
+
+void
+mips_define_label (symbolS *sym)
+{
+ mips_record_label (sym);
+ dwarf2_emit_label (sym);
+}
+
+/* This function is called by tc_new_dot_label whenever a new dot symbol
+ is defined. */
+
+void
+mips_add_dot_label (symbolS *sym)
+{
+ mips_record_label (sym);
+ if (mips_assembling_insn && HAVE_CODE_COMPRESSION)
+ mips_compressed_mark_label (sym);
+}
+
+/* Converting ASE flags from internal to .MIPS.abiflags values. */
+static unsigned int
+mips_convert_ase_flags (int ase)
+{
+ unsigned int ext_ases = 0;
+
+ if (ase & ASE_DSP)
+ ext_ases |= AFL_ASE_DSP;
+ if (ase & ASE_DSPR2)
+ ext_ases |= AFL_ASE_DSPR2;
+ if (ase & ASE_EVA)
+ ext_ases |= AFL_ASE_EVA;
+ if (ase & ASE_MCU)
+ ext_ases |= AFL_ASE_MCU;
+ if (ase & ASE_MDMX)
+ ext_ases |= AFL_ASE_MDMX;
+ if (ase & ASE_MIPS3D)
+ ext_ases |= AFL_ASE_MIPS3D;
+ if (ase & ASE_MT)
+ ext_ases |= AFL_ASE_MT;
+ if (ase & ASE_SMARTMIPS)
+ ext_ases |= AFL_ASE_SMARTMIPS;
+ if (ase & ASE_VIRT)
+ ext_ases |= AFL_ASE_VIRT;
+ if (ase & ASE_MSA)
+ ext_ases |= AFL_ASE_MSA;
+ if (ase & ASE_XPA)
+ ext_ases |= AFL_ASE_XPA;
+
+ return ext_ases;
+}
+/* Some special processing for a MIPS ELF file. */
+
+void
+mips_elf_final_processing (void)
+{
+ int fpabi;
+ Elf_Internal_ABIFlags_v0 flags;
+
+ flags.version = 0;
+ flags.isa_rev = 0;
+ switch (file_mips_opts.isa)
+ {
+ case INSN_ISA1:
+ flags.isa_level = 1;
+ break;
+ case INSN_ISA2:
+ flags.isa_level = 2;
+ break;
+ case INSN_ISA3:
+ flags.isa_level = 3;
+ break;
+ case INSN_ISA4:
+ flags.isa_level = 4;
+ break;
+ case INSN_ISA5:
+ flags.isa_level = 5;
+ break;
+ case INSN_ISA32:
+ flags.isa_level = 32;
+ flags.isa_rev = 1;
+ break;
+ case INSN_ISA32R2:
+ flags.isa_level = 32;
+ flags.isa_rev = 2;
+ break;
+ case INSN_ISA32R3:
+ flags.isa_level = 32;
+ flags.isa_rev = 3;
+ break;
+ case INSN_ISA32R5:
+ flags.isa_level = 32;
+ flags.isa_rev = 5;
+ break;
+ case INSN_ISA32R6:
+ flags.isa_level = 32;
+ flags.isa_rev = 6;
+ break;
+ case INSN_ISA64:
+ flags.isa_level = 64;
+ flags.isa_rev = 1;
+ break;
+ case INSN_ISA64R2:
+ flags.isa_level = 64;
+ flags.isa_rev = 2;
+ break;
+ case INSN_ISA64R3:
+ flags.isa_level = 64;
+ flags.isa_rev = 3;
+ break;
+ case INSN_ISA64R5:
+ flags.isa_level = 64;
+ flags.isa_rev = 5;
+ break;
+ case INSN_ISA64R6:
+ flags.isa_level = 64;
+ flags.isa_rev = 6;
+ break;
+ }
+
+ flags.gpr_size = file_mips_opts.gp == 32 ? AFL_REG_32 : AFL_REG_64;
+ flags.cpr1_size = file_mips_opts.soft_float ? AFL_REG_NONE
+ : (file_mips_opts.ase & ASE_MSA) ? AFL_REG_128
+ : (file_mips_opts.fp == 64) ? AFL_REG_64
+ : AFL_REG_32;
+ flags.cpr2_size = AFL_REG_NONE;
+ flags.fp_abi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP);
+ flags.isa_ext = bfd_mips_isa_ext (stdoutput);
+ flags.ases = mips_convert_ase_flags (file_mips_opts.ase);
+ if (file_ase_mips16)
+ flags.ases |= AFL_ASE_MIPS16;
+ if (file_ase_micromips)
+ flags.ases |= AFL_ASE_MICROMIPS;
+ flags.flags1 = 0;
+ if ((ISA_HAS_ODD_SINGLE_FPR (file_mips_opts.isa, file_mips_opts.arch)
+ || file_mips_opts.fp == 64)
+ && file_mips_opts.oddspreg)
+ flags.flags1 |= AFL_FLAGS1_ODDSPREG;
+ flags.flags2 = 0;
+
+ bfd_mips_elf_swap_abiflags_v0_out (stdoutput, &flags,
+ ((Elf_External_ABIFlags_v0 *)
+ mips_flags_frag));
+
+ /* Write out the register information. */
+ if (mips_abi != N64_ABI)
+ {
+ Elf32_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_cprmask[0] = mips_cprmask[0];
+ s.ri_cprmask[1] = mips_cprmask[1];
+ s.ri_cprmask[2] = mips_cprmask[2];
+ s.ri_cprmask[3] = mips_cprmask[3];
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_mips_elf32_swap_reginfo_out (stdoutput, &s,
+ ((Elf32_External_RegInfo *)
+ mips_regmask_frag));
+ }
+ else
+ {
+ Elf64_Internal_RegInfo s;
+
+ s.ri_gprmask = mips_gprmask;
+ s.ri_pad = 0;
+ s.ri_cprmask[0] = mips_cprmask[0];
+ s.ri_cprmask[1] = mips_cprmask[1];
+ s.ri_cprmask[2] = mips_cprmask[2];
+ s.ri_cprmask[3] = mips_cprmask[3];
+ /* The gp_value field is set by the MIPS ELF backend. */
+
+ bfd_mips_elf64_swap_reginfo_out (stdoutput, &s,
+ ((Elf64_External_RegInfo *)
+ mips_regmask_frag));
+ }
+
+ /* Set the MIPS ELF flag bits. FIXME: There should probably be some
+ sort of BFD interface for this. */
+ if (mips_any_noreorder)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NOREORDER;
+ if (mips_pic != NO_PIC)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_PIC;
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
+ }
+ if (mips_abicalls)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_CPIC;
+
+ /* Set MIPS ELF flags for ASEs. Note that not all ASEs have flags
+ defined at present; this might need to change in future. */
+ if (file_ase_mips16)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_M16;
+ if (file_ase_micromips)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MICROMIPS;
+ if (file_mips_opts.ase & ASE_MDMX)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ARCH_ASE_MDMX;
+
+ /* Set the MIPS ELF ABI flags. */
+ if (mips_abi == O32_ABI && USE_E_MIPS_ABI_O32)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O32;
+ else if (mips_abi == O64_ABI)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_O64;
+ else if (mips_abi == EABI_ABI)
+ {
+ if (file_mips_opts.gp == 64)
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI64;
+ else
+ elf_elfheader (stdoutput)->e_flags |= E_MIPS_ABI_EABI32;
+ }
+ else if (mips_abi == N32_ABI)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_ABI2;
+
+ /* Nothing to do for N64_ABI. */
+
+ if (mips_32bitmode)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_32BITMODE;
+
+ if (mips_nan2008 == 1)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_NAN2008;
+
+ /* 32 bit code with 64 bit FP registers. */
+ fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP);
+ if (fpabi == Val_GNU_MIPS_ABI_FP_OLD_64)
+ elf_elfheader (stdoutput)->e_flags |= EF_MIPS_FP64;
+}
+
+typedef struct proc {
+ symbolS *func_sym;
+ symbolS *func_end_sym;
+ unsigned long reg_mask;
+ unsigned long reg_offset;
+ unsigned long fpreg_mask;
+ unsigned long fpreg_offset;
+ unsigned long frame_offset;
+ unsigned long frame_reg;
+ unsigned long pc_reg;
+} procS;
+
+static procS cur_proc;
+static procS *cur_proc_ptr;
+static int numprocs;
+
+/* Implement NOP_OPCODE. We encode a MIPS16 nop as "1", a microMIPS nop
+ as "2", and a normal nop as "0". */
+
+#define NOP_OPCODE_MIPS 0
+#define NOP_OPCODE_MIPS16 1
+#define NOP_OPCODE_MICROMIPS 2
+
+char
+mips_nop_opcode (void)
+{
+ if (seg_info (now_seg)->tc_segment_info_data.micromips)
+ return NOP_OPCODE_MICROMIPS;
+ else if (seg_info (now_seg)->tc_segment_info_data.mips16)
+ return NOP_OPCODE_MIPS16;
+ else
+ return NOP_OPCODE_MIPS;
+}
+
+/* Fill in an rs_align_code fragment. Unlike elsewhere we want to use
+ 32-bit microMIPS NOPs here (if applicable). */
+
+void
+mips_handle_align (fragS *fragp)
+{
+ char nop_opcode;
+ char *p;
+ int bytes, size, excess;
+ valueT opcode;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ p = fragp->fr_literal + fragp->fr_fix;
+ nop_opcode = *p;
+ switch (nop_opcode)
+ {
+ case NOP_OPCODE_MICROMIPS:
+ opcode = micromips_nop32_insn.insn_opcode;
+ size = 4;
+ break;
+ case NOP_OPCODE_MIPS16:
+ opcode = mips16_nop_insn.insn_opcode;
+ size = 2;
+ break;
+ case NOP_OPCODE_MIPS:
+ default:
+ opcode = nop_insn.insn_opcode;
+ size = 4;
+ break;
+ }
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ excess = bytes % size;
+
+ /* Handle the leading part if we're not inserting a whole number of
+ instructions, and make it the end of the fixed part of the frag.
+ Try to fit in a short microMIPS NOP if applicable and possible,
+ and use zeroes otherwise. */
+ gas_assert (excess < 4);
+ fragp->fr_fix += excess;
+ switch (excess)
+ {
+ case 3:
+ *p++ = '\0';
+ /* Fall through. */
+ case 2:
+ if (nop_opcode == NOP_OPCODE_MICROMIPS && !mips_opts.insn32)
+ {
+ p = write_compressed_insn (p, micromips_nop16_insn.insn_opcode, 2);
+ break;
+ }
+ *p++ = '\0';
+ /* Fall through. */
+ case 1:
+ *p++ = '\0';
+ /* Fall through. */
+ case 0:
+ break;
+ }
+
+ md_number_to_chars (p, opcode, size);
+ fragp->fr_var = size;
+}
+
+static long
+get_number (void)
+{
+ int negative = 0;
+ long val = 0;
+
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ negative = 1;
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ as_bad (_("expected simple number"));
+ if (input_line_pointer[0] == '0')
+ {
+ if (input_line_pointer[1] == 'x')
+ {
+ input_line_pointer += 2;
+ while (ISXDIGIT (*input_line_pointer))
+ {
+ val <<= 4;
+ val |= hex_value (*input_line_pointer++);
+ }
+ return negative ? -val : val;
+ }
+ else
+ {
+ ++input_line_pointer;
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val <<= 3;
+ val |= *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+ }
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ {
+ printf (_(" *input_line_pointer == '%c' 0x%02x\n"),
+ *input_line_pointer, *input_line_pointer);
+ as_warn (_("invalid number"));
+ return -1;
+ }
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val *= 10;
+ val += *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+}
+
+/* The .file directive; just like the usual .file directive, but there
+ is an initial number which is the ECOFF file index. In the non-ECOFF
+ case .file implies DWARF-2. */
+
+static void
+s_mips_file (int x ATTRIBUTE_UNUSED)
+{
+ static int first_file_directive = 0;
+
+ if (ECOFF_DEBUGGING)
+ {
+ get_number ();
+ s_app_file (0);
+ }
+ else
+ {
+ char *filename;
+
+ filename = dwarf2_directive_file (0);
+
+ /* Versions of GCC up to 3.1 start files with a ".file"
+ directive even for stabs output. Make sure that this
+ ".file" is handled. Note that you need a version of GCC
+ after 3.1 in order to support DWARF-2 on MIPS. */
+ if (filename != NULL && ! first_file_directive)
+ {
+ (void) new_logical_line (filename, -1);
+ s_app_file_string (filename, 0);
+ }
+ first_file_directive = 1;
+ }
+}
+
+/* The .loc directive, implying DWARF-2. */
+
+static void
+s_mips_loc (int x ATTRIBUTE_UNUSED)
+{
+ if (!ECOFF_DEBUGGING)
+ dwarf2_directive_loc (0);
+}
+
+/* The .end directive. */
+
+static void
+s_mips_end (int x ATTRIBUTE_UNUSED)
+{
+ symbolS *p;
+
+ /* Following functions need their own .frame and .cprestore directives. */
+ mips_frame_reg_valid = 0;
+ mips_cprestore_valid = 0;
+
+ if (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ p = get_symbol ();
+ demand_empty_rest_of_line ();
+ }
+ else
+ p = NULL;
+
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
+ as_warn (_(".end not in text section"));
+
+ if (!cur_proc_ptr)
+ {
+ as_warn (_(".end directive without a preceding .ent directive"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (p != NULL)
+ {
+ gas_assert (S_GET_NAME (p));
+ if (strcmp (S_GET_NAME (p), S_GET_NAME (cur_proc_ptr->func_sym)))
+ as_warn (_(".end symbol does not match .ent symbol"));
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_endfunc (S_GET_NAME (p),
+ S_GET_NAME (p));
+ }
+ else
+ as_warn (_(".end directive missing or unknown symbol"));
+
+ /* Create an expression to calculate the size of the function. */
+ if (p && cur_proc_ptr)
+ {
+ OBJ_SYMFIELD_TYPE *obj = symbol_get_obj (p);
+ expressionS *exp = xmalloc (sizeof (expressionS));
+
+ obj->size = exp;
+ exp->X_op = O_subtract;
+ exp->X_add_symbol = symbol_temp_new_now ();
+ exp->X_op_symbol = p;
+ exp->X_add_number = 0;
+
+ cur_proc_ptr->func_end_sym = exp->X_add_symbol;
+ }
+
+ /* Generate a .pdr section. */
+ if (!ECOFF_DEBUGGING && mips_flag_pdr)
+ {
+ segT saved_seg = now_seg;
+ subsegT saved_subseg = now_subseg;
+ expressionS exp;
+ char *fragp;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ gas_assert (pdr_seg);
+ subseg_set (pdr_seg, 0);
+
+ /* Write the symbol. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = p;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 4);
+
+ fragp = frag_more (7 * 4);
+
+ md_number_to_chars (fragp, cur_proc_ptr->reg_mask, 4);
+ md_number_to_chars (fragp + 4, cur_proc_ptr->reg_offset, 4);
+ md_number_to_chars (fragp + 8, cur_proc_ptr->fpreg_mask, 4);
+ md_number_to_chars (fragp + 12, cur_proc_ptr->fpreg_offset, 4);
+ md_number_to_chars (fragp + 16, cur_proc_ptr->frame_offset, 4);
+ md_number_to_chars (fragp + 20, cur_proc_ptr->frame_reg, 4);
+ md_number_to_chars (fragp + 24, cur_proc_ptr->pc_reg, 4);
+
+ subseg_set (saved_seg, saved_subseg);
+ }
+
+ cur_proc_ptr = NULL;
+}
+
+/* The .aent and .ent directives. */
+
+static void
+s_mips_ent (int aent)
+{
+ symbolS *symbolP;
+
+ symbolP = get_symbol ();
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (*input_line_pointer)
+ || *input_line_pointer == '-')
+ get_number ();
+
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) == 0)
+ as_warn (_(".ent or .aent not in text section"));
+
+ if (!aent && cur_proc_ptr)
+ as_warn (_("missing .end"));
+
+ if (!aent)
+ {
+ /* This function needs its own .frame and .cprestore directives. */
+ mips_frame_reg_valid = 0;
+ mips_cprestore_valid = 0;
+
+ cur_proc_ptr = &cur_proc;
+ memset (cur_proc_ptr, '\0', sizeof (procS));
+
+ cur_proc_ptr->func_sym = symbolP;
+
+ ++numprocs;
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_func (S_GET_NAME (symbolP),
+ S_GET_NAME (symbolP));
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .frame directive. If the mdebug section is present (IRIX 5 native)
+ then ecoff.c (ecoff_directive_frame) is used. For embedded targets,
+ s_mips_frame is used so that we can set the PDR information correctly.
+ We can't use the ecoff routines because they make reference to the ecoff
+ symbol table (in the mdebug section). */
+
+static void
+s_mips_frame (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ECOFF_DEBUGGING)
+ s_ignore (ignore);
+ else
+ {
+ long val;
+
+ if (cur_proc_ptr == (procS *) NULL)
+ {
+ as_warn (_(".frame outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ cur_proc_ptr->frame_reg = tc_get_register (1);
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer++ != ','
+ || get_absolute_expression_and_terminator (&val) != ',')
+ {
+ as_warn (_("bad .frame directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ cur_proc_ptr->frame_offset = val;
+ cur_proc_ptr->pc_reg = tc_get_register (0);
+
+ demand_empty_rest_of_line ();
+ }
+}
+
+/* The .fmask and .mask directives. If the mdebug section is present
+ (IRIX 5 native) then ecoff.c (ecoff_directive_mask) is used. For
+ embedded targets, s_mips_mask is used so that we can set the PDR
+ information correctly. We can't use the ecoff routines because they
+ make reference to the ecoff symbol table (in the mdebug section). */
+
+static void
+s_mips_mask (int reg_type)
+{
+ if (ECOFF_DEBUGGING)
+ s_ignore (reg_type);
+ else
+ {
+ long mask, off;
+
+ if (cur_proc_ptr == (procS *) NULL)
+ {
+ as_warn (_(".mask/.fmask outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ if (get_absolute_expression_and_terminator (&mask) != ',')
+ {
+ as_warn (_("bad .mask/.fmask directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ off = get_absolute_expression ();
+
+ if (reg_type == 'F')
+ {
+ cur_proc_ptr->fpreg_mask = mask;
+ cur_proc_ptr->fpreg_offset = off;
+ }
+ else
+ {
+ cur_proc_ptr->reg_mask = mask;
+ cur_proc_ptr->reg_offset = off;
+ }
+
+ demand_empty_rest_of_line ();
+ }
+}
+
+/* A table describing all the processors gas knows about. Names are
+ matched in the order listed.
+
+ To ease comparison, please keep this table in the same order as
+ gcc's mips_cpu_info_table[]. */
+static const struct mips_cpu_info mips_cpu_info_table[] =
+{
+ /* Entries for generic ISAs */
+ { "mips1", MIPS_CPU_IS_ISA, 0, ISA_MIPS1, CPU_R3000 },
+ { "mips2", MIPS_CPU_IS_ISA, 0, ISA_MIPS2, CPU_R6000 },
+ { "mips3", MIPS_CPU_IS_ISA, 0, ISA_MIPS3, CPU_R4000 },
+ { "mips4", MIPS_CPU_IS_ISA, 0, ISA_MIPS4, CPU_R8000 },
+ { "mips5", MIPS_CPU_IS_ISA, 0, ISA_MIPS5, CPU_MIPS5 },
+ { "mips32", MIPS_CPU_IS_ISA, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "mips32r2", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "mips32r3", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R3, CPU_MIPS32R3 },
+ { "mips32r5", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R5, CPU_MIPS32R5 },
+ { "mips32r6", MIPS_CPU_IS_ISA, 0, ISA_MIPS32R6, CPU_MIPS32R6 },
+ { "mips64", MIPS_CPU_IS_ISA, 0, ISA_MIPS64, CPU_MIPS64 },
+ { "mips64r2", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R2, CPU_MIPS64R2 },
+ { "mips64r3", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R3, CPU_MIPS64R3 },
+ { "mips64r5", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R5, CPU_MIPS64R5 },
+ { "mips64r6", MIPS_CPU_IS_ISA, 0, ISA_MIPS64R6, CPU_MIPS64R6 },
+
+ /* MIPS I */
+ { "r3000", 0, 0, ISA_MIPS1, CPU_R3000 },
+ { "r2000", 0, 0, ISA_MIPS1, CPU_R3000 },
+ { "r3900", 0, 0, ISA_MIPS1, CPU_R3900 },
+
+ /* MIPS II */
+ { "r6000", 0, 0, ISA_MIPS2, CPU_R6000 },
+
+ /* MIPS III */
+ { "r4000", 0, 0, ISA_MIPS3, CPU_R4000 },
+ { "r4010", 0, 0, ISA_MIPS2, CPU_R4010 },
+ { "vr4100", 0, 0, ISA_MIPS3, CPU_VR4100 },
+ { "vr4111", 0, 0, ISA_MIPS3, CPU_R4111 },
+ { "vr4120", 0, 0, ISA_MIPS3, CPU_VR4120 },
+ { "vr4130", 0, 0, ISA_MIPS3, CPU_VR4120 },
+ { "vr4181", 0, 0, ISA_MIPS3, CPU_R4111 },
+ { "vr4300", 0, 0, ISA_MIPS3, CPU_R4300 },
+ { "r4400", 0, 0, ISA_MIPS3, CPU_R4400 },
+ { "r4600", 0, 0, ISA_MIPS3, CPU_R4600 },
+ { "orion", 0, 0, ISA_MIPS3, CPU_R4600 },
+ { "r4650", 0, 0, ISA_MIPS3, CPU_R4650 },
+ { "r5900", 0, 0, ISA_MIPS3, CPU_R5900 },
+ /* ST Microelectronics Loongson 2E and 2F cores */
+ { "loongson2e", 0, 0, ISA_MIPS3, CPU_LOONGSON_2E },
+ { "loongson2f", 0, 0, ISA_MIPS3, CPU_LOONGSON_2F },
+
+ /* MIPS IV */
+ { "r8000", 0, 0, ISA_MIPS4, CPU_R8000 },
+ { "r10000", 0, 0, ISA_MIPS4, CPU_R10000 },
+ { "r12000", 0, 0, ISA_MIPS4, CPU_R12000 },
+ { "r14000", 0, 0, ISA_MIPS4, CPU_R14000 },
+ { "r16000", 0, 0, ISA_MIPS4, CPU_R16000 },
+ { "vr5000", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "vr5400", 0, 0, ISA_MIPS4, CPU_VR5400 },
+ { "vr5500", 0, 0, ISA_MIPS4, CPU_VR5500 },
+ { "rm5200", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5230", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5231", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5261", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm5721", 0, 0, ISA_MIPS4, CPU_R5000 },
+ { "rm7000", 0, 0, ISA_MIPS4, CPU_RM7000 },
+ { "rm9000", 0, 0, ISA_MIPS4, CPU_RM9000 },
+
+ /* MIPS 32 */
+ { "4kc", 0, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4km", 0, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4kp", 0, 0, ISA_MIPS32, CPU_MIPS32 },
+ { "4ksc", 0, ASE_SMARTMIPS, ISA_MIPS32, CPU_MIPS32 },
+
+ /* MIPS 32 Release 2 */
+ { "4kec", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4kem", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4kep", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "4ksd", 0, ASE_SMARTMIPS, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m4k", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m4kp", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14k", 0, ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14kc", 0, ASE_MCU, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14ke", 0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+ ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "m14kec", 0, ASE_DSP | ASE_DSPR2 | ASE_MCU,
+ ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kc", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf2_1", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kf1_1", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* Deprecated forms of the above. */
+ { "24kfx", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kx", 0, 0, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* 24KE is a 24K with DSP ASE, other ASEs are optional. */
+ { "24kec", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kef2_1", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kef", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kef1_1", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* Deprecated forms of the above. */
+ { "24kefx", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "24kex", 0, ASE_DSP, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* 34K is a 24K with DSP and MT ASE, other ASEs are optional. */
+ { "34kc", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kf2_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kf", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kf1_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* Deprecated forms of the above. */
+ { "34kfx", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "34kx", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* 34Kn is a 34kc without DSP. */
+ { "34kn", 0, ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* 74K with DSP and DSPR2 ASE, other ASEs are optional. */
+ { "74kc", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf2_1", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf1_1", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kf3_2", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* Deprecated forms of the above. */
+ { "74kfx", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "74kx", 0, ASE_DSP | ASE_DSPR2, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* 1004K cores are multiprocessor versions of the 34K. */
+ { "1004kc", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "1004kf2_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "1004kf", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ { "1004kf1_1", 0, ASE_DSP | ASE_MT, ISA_MIPS32R2, CPU_MIPS32R2 },
+ /* P5600 with EVA and Virtualization ASEs, other ASEs are optional. */
+ { "p5600", 0, ASE_VIRT | ASE_EVA | ASE_XPA, ISA_MIPS32R5, CPU_MIPS32R5 },
+
+ /* MIPS 64 */
+ { "5kc", 0, 0, ISA_MIPS64, CPU_MIPS64 },
+ { "5kf", 0, 0, ISA_MIPS64, CPU_MIPS64 },
+ { "20kc", 0, ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 },
+ { "25kf", 0, ASE_MIPS3D, ISA_MIPS64, CPU_MIPS64 },
+
+ /* Broadcom SB-1 CPU core */
+ { "sb1", 0, ASE_MIPS3D | ASE_MDMX, ISA_MIPS64, CPU_SB1 },
+ /* Broadcom SB-1A CPU core */
+ { "sb1a", 0, ASE_MIPS3D | ASE_MDMX, ISA_MIPS64, CPU_SB1 },
+
+ { "loongson3a", 0, 0, ISA_MIPS64R2, CPU_LOONGSON_3A },
+
+ /* MIPS 64 Release 2 */
+
+ /* Cavium Networks Octeon CPU core */
+ { "octeon", 0, 0, ISA_MIPS64R2, CPU_OCTEON },
+ { "octeon+", 0, 0, ISA_MIPS64R2, CPU_OCTEONP },
+ { "octeon2", 0, 0, ISA_MIPS64R2, CPU_OCTEON2 },
+
+ /* RMI Xlr */
+ { "xlr", 0, 0, ISA_MIPS64, CPU_XLR },
+
+ /* Broadcom XLP.
+ XLP is mostly like XLR, with the prominent exception that it is
+ MIPS64R2 rather than MIPS64. */
+ { "xlp", 0, 0, ISA_MIPS64R2, CPU_XLR },
+
+ /* End marker */
+ { NULL, 0, 0, 0, 0 }
+};
+
+
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+ with a final "000" replaced by "k". Ignore case.
+
+ Note: this function is shared between GCC and GAS. */
+
+static bfd_boolean
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+ given++, canonical++;
+
+ return ((*given == 0 && *canonical == 0)
+ || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
+
+
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+ CPU name. We've traditionally allowed a lot of variation here.
+
+ Note: this function is shared between GCC and GAS. */
+
+static bfd_boolean
+mips_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ /* First see if the name matches exactly, or with a final "000"
+ turned into "k". */
+ if (mips_strict_matching_cpu_name_p (canonical, given))
+ return TRUE;
+
+ /* If not, try comparing based on numerical designation alone.
+ See if GIVEN is an unadorned number, or 'r' followed by a number. */
+ if (TOLOWER (*given) == 'r')
+ given++;
+ if (!ISDIGIT (*given))
+ return FALSE;
+
+ /* Skip over some well-known prefixes in the canonical name,
+ hoping to find a number there too. */
+ if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r')
+ canonical += 1;
+
+ return mips_strict_matching_cpu_name_p (canonical, given);
+}
+
+
+/* Parse an option that takes the name of a processor as its argument.
+ OPTION is the name of the option and CPU_STRING is the argument.
+ Return the corresponding processor enumeration if the CPU_STRING is
+ recognized, otherwise report an error and return null.
+
+ A similar function exists in GCC. */
+
+static const struct mips_cpu_info *
+mips_parse_cpu (const char *option, const char *cpu_string)
+{
+ const struct mips_cpu_info *p;
+
+ /* 'from-abi' selects the most compatible architecture for the given
+ ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs. For the
+ EABIs, we have to decide whether we're using the 32-bit or 64-bit
+ version. Look first at the -mgp options, if given, otherwise base
+ the choice on MIPS_DEFAULT_64BIT.
+
+ Treat NO_ABI like the EABIs. One reason to do this is that the
+ plain 'mips' and 'mips64' configs have 'from-abi' as their default
+ architecture. This code picks MIPS I for 'mips' and MIPS III for
+ 'mips64', just as we did in the days before 'from-abi'. */
+ if (strcasecmp (cpu_string, "from-abi") == 0)
+ {
+ if (ABI_NEEDS_32BIT_REGS (mips_abi))
+ return mips_cpu_info_from_isa (ISA_MIPS1);
+
+ if (ABI_NEEDS_64BIT_REGS (mips_abi))
+ return mips_cpu_info_from_isa (ISA_MIPS3);
+
+ if (file_mips_opts.gp >= 0)
+ return mips_cpu_info_from_isa (file_mips_opts.gp == 32
+ ? ISA_MIPS1 : ISA_MIPS3);
+
+ return mips_cpu_info_from_isa (MIPS_DEFAULT_64BIT
+ ? ISA_MIPS3
+ : ISA_MIPS1);
+ }
+
+ /* 'default' has traditionally been a no-op. Probably not very useful. */
+ if (strcasecmp (cpu_string, "default") == 0)
+ return 0;
+
+ for (p = mips_cpu_info_table; p->name != 0; p++)
+ if (mips_matching_cpu_name_p (p->name, cpu_string))
+ return p;
+
+ as_bad (_("bad value (%s) for %s"), cpu_string, option);
+ return 0;
+}
+
+/* Return the canonical processor information for ISA (a member of the
+ ISA_MIPS* enumeration). */
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (int isa)
+{
+ int i;
+
+ for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+ if ((mips_cpu_info_table[i].flags & MIPS_CPU_IS_ISA)
+ && isa == mips_cpu_info_table[i].isa)
+ return (&mips_cpu_info_table[i]);
+
+ return NULL;
+}
+
+static const struct mips_cpu_info *
+mips_cpu_info_from_arch (int arch)
+{
+ int i;
+
+ for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+ if (arch == mips_cpu_info_table[i].cpu)
+ return (&mips_cpu_info_table[i]);
+
+ return NULL;
+}
+
+static void
+show (FILE *stream, const char *string, int *col_p, int *first_p)
+{
+ if (*first_p)
+ {
+ fprintf (stream, "%24s", "");
+ *col_p = 24;
+ }
+ else
+ {
+ fprintf (stream, ", ");
+ *col_p += 2;
+ }
+
+ if (*col_p + strlen (string) > 72)
+ {
+ fprintf (stream, "\n%24s", "");
+ *col_p = 24;
+ }
+
+ fprintf (stream, "%s", string);
+ *col_p += strlen (string);
+
+ *first_p = 0;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ int column, first;
+ size_t i;
+
+ fprintf (stream, _("\
+MIPS options:\n\
+-EB generate big endian output\n\
+-EL generate little endian output\n\
+-g, -g2 do not remove unneeded NOPs or swap branches\n\
+-G NUM allow referencing objects up to NUM bytes\n\
+ implicitly with the gp register [default 8]\n"));
+ fprintf (stream, _("\
+-mips1 generate MIPS ISA I instructions\n\
+-mips2 generate MIPS ISA II instructions\n\
+-mips3 generate MIPS ISA III instructions\n\
+-mips4 generate MIPS ISA IV instructions\n\
+-mips5 generate MIPS ISA V instructions\n\
+-mips32 generate MIPS32 ISA instructions\n\
+-mips32r2 generate MIPS32 release 2 ISA instructions\n\
+-mips32r3 generate MIPS32 release 3 ISA instructions\n\
+-mips32r5 generate MIPS32 release 5 ISA instructions\n\
+-mips32r6 generate MIPS32 release 6 ISA instructions\n\
+-mips64 generate MIPS64 ISA instructions\n\
+-mips64r2 generate MIPS64 release 2 ISA instructions\n\
+-mips64r3 generate MIPS64 release 3 ISA instructions\n\
+-mips64r5 generate MIPS64 release 5 ISA instructions\n\
+-mips64r6 generate MIPS64 release 6 ISA instructions\n\
+-march=CPU/-mtune=CPU generate code/schedule for CPU, where CPU is one of:\n"));
+
+ first = 1;
+
+ for (i = 0; mips_cpu_info_table[i].name != NULL; i++)
+ show (stream, mips_cpu_info_table[i].name, &column, &first);
+ show (stream, "from-abi", &column, &first);
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-mCPU equivalent to -march=CPU -mtune=CPU. Deprecated.\n\
+-no-mCPU don't generate code specific to CPU.\n\
+ For -mCPU and -no-mCPU, CPU must be one of:\n"));
+
+ first = 1;
+
+ show (stream, "3900", &column, &first);
+ show (stream, "4010", &column, &first);
+ show (stream, "4100", &column, &first);
+ show (stream, "4650", &column, &first);
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-mips16 generate mips16 instructions\n\
+-no-mips16 do not generate mips16 instructions\n"));
+ fprintf (stream, _("\
+-mmicromips generate microMIPS instructions\n\
+-mno-micromips do not generate microMIPS instructions\n"));
+ fprintf (stream, _("\
+-msmartmips generate smartmips instructions\n\
+-mno-smartmips do not generate smartmips instructions\n"));
+ fprintf (stream, _("\
+-mdsp generate DSP instructions\n\
+-mno-dsp do not generate DSP instructions\n"));
+ fprintf (stream, _("\
+-mdspr2 generate DSP R2 instructions\n\
+-mno-dspr2 do not generate DSP R2 instructions\n"));
+ fprintf (stream, _("\
+-mmt generate MT instructions\n\
+-mno-mt do not generate MT instructions\n"));
+ fprintf (stream, _("\
+-mmcu generate MCU instructions\n\
+-mno-mcu do not generate MCU instructions\n"));
+ fprintf (stream, _("\
+-mmsa generate MSA instructions\n\
+-mno-msa do not generate MSA instructions\n"));
+ fprintf (stream, _("\
+-mxpa generate eXtended Physical Address (XPA) instructions\n\
+-mno-xpa do not generate eXtended Physical Address (XPA) instructions\n"));
+ fprintf (stream, _("\
+-mvirt generate Virtualization instructions\n\
+-mno-virt do not generate Virtualization instructions\n"));
+ fprintf (stream, _("\
+-minsn32 only generate 32-bit microMIPS instructions\n\
+-mno-insn32 generate all microMIPS instructions\n"));
+ fprintf (stream, _("\
+-mfix-loongson2f-jump work around Loongson2F JUMP instructions\n\
+-mfix-loongson2f-nop work around Loongson2F NOP errata\n\
+-mfix-vr4120 work around certain VR4120 errata\n\
+-mfix-vr4130 work around VR4130 mflo/mfhi errata\n\
+-mfix-24k insert a nop after ERET and DERET instructions\n\
+-mfix-cn63xxp1 work around CN63XXP1 PREF errata\n\
+-mgp32 use 32-bit GPRs, regardless of the chosen ISA\n\
+-mfp32 use 32-bit FPRs, regardless of the chosen ISA\n\
+-msym32 assume all symbols have 32-bit values\n\
+-O0 remove unneeded NOPs, do not swap branches\n\
+-O remove unneeded NOPs and swap branches\n\
+--trap, --no-break trap exception on div by 0 and mult overflow\n\
+--break, --no-trap break exception on div by 0 and mult overflow\n"));
+ fprintf (stream, _("\
+-mhard-float allow floating-point instructions\n\
+-msoft-float do not allow floating-point instructions\n\
+-msingle-float only allow 32-bit floating-point operations\n\
+-mdouble-float allow 32-bit and 64-bit floating-point operations\n\
+--[no-]construct-floats [dis]allow floating point values to be constructed\n\
+--[no-]relax-branch [dis]allow out-of-range branches to be relaxed\n\
+-mnan=ENCODING select an IEEE 754 NaN encoding convention, either of:\n"));
+
+ first = 1;
+
+ show (stream, "legacy", &column, &first);
+ show (stream, "2008", &column, &first);
+
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-KPIC, -call_shared generate SVR4 position independent code\n\
+-call_nonpic generate non-PIC code that can operate with DSOs\n\
+-mvxworks-pic generate VxWorks position independent code\n\
+-non_shared do not generate code that can operate with DSOs\n\
+-xgot assume a 32 bit GOT\n\
+-mpdr, -mno-pdr enable/disable creation of .pdr sections\n\
+-mshared, -mno-shared disable/enable .cpload optimization for\n\
+ position dependent (non shared) code\n\
+-mabi=ABI create ABI conformant object file for:\n"));
+
+ first = 1;
+
+ show (stream, "32", &column, &first);
+ show (stream, "o64", &column, &first);
+ show (stream, "n32", &column, &first);
+ show (stream, "64", &column, &first);
+ show (stream, "eabi", &column, &first);
+
+ fputc ('\n', stream);
+
+ fprintf (stream, _("\
+-32 create o32 ABI object file (default)\n\
+-n32 create n32 ABI object file\n\
+-64 create 64 ABI object file\n"));
+}
+
+#ifdef TE_IRIX
+enum dwarf2_format
+mips_dwarf2_format (asection *sec ATTRIBUTE_UNUSED)
+{
+ if (HAVE_64BIT_SYMBOLS)
+ return dwarf2_format_64bit_irix;
+ else
+ return dwarf2_format_32bit;
+}
+#endif
+
+int
+mips_dwarf2_addr_size (void)
+{
+ if (HAVE_64BIT_OBJECTS)
+ return 8;
+ else
+ return 4;
+}
+
+/* Standard calling conventions leave the CFA at SP on entry. */
+void
+mips_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (SP);
+}
+
+int
+tc_mips_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int reg;
+
+ if (reg_lookup (&regname, RTYPE_GP | RTYPE_NUM, &reg))
+ regnum = reg;
+
+ return regnum;
+}
+
+/* Implement CONVERT_SYMBOLIC_ATTRIBUTE.
+ Given a symbolic attribute NAME, return the proper integer value.
+ Returns -1 if the attribute is not known. */
+
+int
+mips_convert_symbolic_attribute (const char *name)
+{
+ static const struct
+ {
+ const char * name;
+ const int tag;
+ }
+ attribute_table[] =
+ {
+#define T(tag) {#tag, tag}
+ T (Tag_GNU_MIPS_ABI_FP),
+ T (Tag_GNU_MIPS_ABI_MSA),
+#undef T
+ };
+ unsigned int i;
+
+ if (name == NULL)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE (attribute_table); i++)
+ if (streq (name, attribute_table[i].name))
+ return attribute_table[i].tag;
+
+ return -1;
+}
+
+void
+md_mips_end (void)
+{
+ int fpabi = Val_GNU_MIPS_ABI_FP_ANY;
+
+ mips_emit_delays ();
+ if (cur_proc_ptr)
+ as_warn (_("missing .end at end of assembly"));
+
+ /* Just in case no code was emitted, do the consistency check. */
+ file_mips_check_options ();
+
+ /* Set a floating-point ABI if the user did not. */
+ if (obj_elf_seen_attribute (OBJ_ATTR_GNU, Tag_GNU_MIPS_ABI_FP))
+ {
+ /* Perform consistency checks on the floating-point ABI. */
+ fpabi = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP);
+ if (fpabi != Val_GNU_MIPS_ABI_FP_ANY)
+ check_fpabi (fpabi);
+ }
+ else
+ {
+ /* Soft-float gets precedence over single-float, the two options should
+ not be used together so this should not matter. */
+ if (file_mips_opts.soft_float == 1)
+ fpabi = Val_GNU_MIPS_ABI_FP_SOFT;
+ /* Single-float gets precedence over all double_float cases. */
+ else if (file_mips_opts.single_float == 1)
+ fpabi = Val_GNU_MIPS_ABI_FP_SINGLE;
+ else
+ {
+ switch (file_mips_opts.fp)
+ {
+ case 32:
+ if (file_mips_opts.gp == 32)
+ fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+ break;
+ case 0:
+ fpabi = Val_GNU_MIPS_ABI_FP_XX;
+ break;
+ case 64:
+ if (file_mips_opts.gp == 32 && !file_mips_opts.oddspreg)
+ fpabi = Val_GNU_MIPS_ABI_FP_64A;
+ else if (file_mips_opts.gp == 32)
+ fpabi = Val_GNU_MIPS_ABI_FP_64;
+ else
+ fpabi = Val_GNU_MIPS_ABI_FP_DOUBLE;
+ break;
+ }
+ }
+
+ bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU,
+ Tag_GNU_MIPS_ABI_FP, fpabi);
+ }
+}
diff --git a/gas/config/tc-mips.h b/gas/config/tc-mips.h
new file mode 100644
index 0000000..0b8e607
--- /dev/null
+++ b/gas/config/tc-mips.h
@@ -0,0 +1,200 @@
+/* tc-mips.h -- header file for tc-mips.c.
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+ Contributed by the OSF and Ralph Campbell.
+ Written by Keith Knowles and Ralph Campbell, working independently.
+ Modified for ECOFF support by Ian Lance Taylor of Cygnus Support.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_MIPS
+#define TC_MIPS
+
+struct frag;
+struct expressionS;
+
+/* Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+#define TARGET_ARCH bfd_arch_mips
+
+#define WORKING_DOT_WORD 1
+#define OLD_FLOAT_READS
+#define REPEAT_CONS_EXPRESSIONS
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 3
+#define LOCAL_LABELS_FB 1
+
+#define TC_ADDRESS_BYTES mips_address_bytes
+extern int mips_address_bytes (void);
+
+/* Maximum symbol offset that can be encoded in a BFD_RELOC_GPREL16
+ relocation. */
+#define MAX_GPREL_OFFSET (0x7FF0)
+
+#define md_relax_frag(segment, fragp, stretch) \
+ mips_relax_frag(segment, fragp, stretch)
+extern int mips_relax_frag (asection *, struct frag *, long);
+
+#define md_undefined_symbol(name) (0)
+#define md_operand(x)
+
+extern char mips_nop_opcode (void);
+#define NOP_OPCODE (mips_nop_opcode ())
+
+extern void mips_handle_align (struct frag *);
+#define HANDLE_ALIGN(fragp) mips_handle_align (fragp)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4)
+
+struct insn_label_list;
+struct mips_segment_info {
+ struct insn_label_list *labels;
+ unsigned int mips16 : 1;
+ unsigned int micromips : 1;
+};
+#define TC_SEGMENT_INFO_TYPE struct mips_segment_info
+
+/* This field is nonzero if the symbol is the target of a MIPS16 jump. */
+#define TC_SYMFIELD_TYPE int
+
+/* Tell assembler that we have an itbl_mips.h header file to include. */
+#define HAVE_ITBL_CPU
+
+/* The endianness of the target format may change based on command
+ line arguments. */
+#define TARGET_FORMAT mips_target_format()
+extern const char *mips_target_format (void);
+
+/* MIPS PIC level. */
+
+enum mips_pic_level
+{
+ /* Do not generate PIC code. */
+ NO_PIC,
+
+ /* Generate PIC code as in the SVR4 MIPS ABI. */
+ SVR4_PIC,
+
+ /* VxWorks's PIC model. */
+ VXWORKS_PIC
+};
+
+extern enum mips_pic_level mips_pic;
+
+extern int tc_get_register (int frame);
+
+#define md_after_parse_args() mips_after_parse_args()
+extern void mips_after_parse_args (void);
+
+#define tc_init_after_args() mips_init_after_args()
+extern void mips_init_after_args (void);
+
+#define md_parse_long_option(arg) mips_parse_long_option (arg)
+extern int mips_parse_long_option (const char *);
+
+#define tc_frob_label(sym) mips_define_label (sym)
+extern void mips_define_label (symbolS *);
+
+#define tc_new_dot_label(sym) mips_add_dot_label (sym)
+extern void mips_add_dot_label (symbolS *);
+
+#define tc_frob_file_before_adjust() mips_frob_file_before_adjust ()
+extern void mips_frob_file_before_adjust (void);
+
+#define tc_frob_file_before_fix() mips_frob_file ()
+extern void mips_frob_file (void);
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+#define tc_frob_file_after_relocs mips_frob_file_after_relocs
+extern void mips_frob_file_after_relocs (void);
+#endif
+
+#define tc_fix_adjustable(fixp) mips_fix_adjustable (fixp)
+extern int mips_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* Global syms must not be resolved, to support ELF shared libraries. */
+#define EXTERN_FORCE_RELOC \
+ (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+
+#define TC_FORCE_RELOCATION(FIX) mips_force_relocation (FIX)
+extern int mips_force_relocation (struct fix *);
+
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+ (! SEG_NORMAL (SEG) || mips_force_relocation (FIX))
+
+/* Register mask variables. These are set by the MIPS assembly code
+ and used by ECOFF and possibly other object file formats. */
+extern unsigned long mips_gprmask;
+extern unsigned long mips_cprmask[4];
+
+#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
+
+#define elf_tc_final_processing mips_elf_final_processing
+extern void mips_elf_final_processing (void);
+
+#endif
+
+extern void md_mips_end (void);
+#define md_end() md_mips_end()
+
+extern void mips_pop_insert (void);
+#define md_pop_insert() mips_pop_insert()
+
+extern void mips_emit_delays (void);
+#define md_flush_pending_output mips_emit_delays
+
+extern void mips_enable_auto_align (void);
+#define md_elf_section_change_hook() mips_enable_auto_align()
+
+#ifdef TE_IRIX
+enum dwarf2_format;
+extern enum dwarf2_format mips_dwarf2_format (asection *);
+# define DWARF2_FORMAT(SEC) mips_dwarf2_format (SEC)
+#else
+/* Use GAS' defaults. */
+#endif
+
+extern int mips_dwarf2_addr_size (void);
+#define DWARF2_ADDR_SIZE(bfd) mips_dwarf2_addr_size ()
+#define DWARF2_FDE_RELOC_SIZE mips_dwarf2_addr_size ()
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions mips_cfi_frame_initial_instructions
+extern void mips_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_mips_regname_to_dw2regnum
+extern int tc_mips_regname_to_dw2regnum (char *regname);
+
+#define DWARF2_DEFAULT_RETURN_COLUMN 31
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+#define DIFF_EXPR_OK
+/* We define DIFF_EXPR_OK because of R_MIPS_PC32, but we have no
+ 64-bit form for n64 CFIs. */
+#define CFI_DIFF_EXPR_OK 0
+
+#define CONVERT_SYMBOLIC_ATTRIBUTE(name) mips_convert_symbolic_attribute (name)
+extern int mips_convert_symbolic_attribute (const char *);
+
+#endif /* TC_MIPS */
diff --git a/gas/config/tc-mmix.c b/gas/config/tc-mmix.c
new file mode 100644
index 0000000..82f48ae
--- /dev/null
+++ b/gas/config/tc-mmix.c
@@ -0,0 +1,4332 @@
+/* tc-mmix.c -- Assembler for Don Knuth's MMIX.
+ Copyright (C) 2001-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* Knuth's assembler mmixal does not provide a relocatable format; mmo is
+ to be considered a final link-format. In the final link, we make mmo,
+ but for relocatable files, we use ELF.
+
+ One goal is to provide a superset of what mmixal does, including
+ compatible syntax, but the main purpose is to serve GCC. */
+
+
+#include "as.h"
+#include <limits.h>
+#include "subsegs.h"
+#include "elf/mmix.h"
+#include "opcode/mmix.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "obstack.h"
+
+/* Something to describe what we need to do with a fixup before output,
+ for example assert something of what it became or make a relocation. */
+
+enum mmix_fixup_action
+{
+ mmix_fixup_byte,
+ mmix_fixup_register,
+ mmix_fixup_register_or_adjust_for_byte
+};
+
+static int get_spec_regno (char *);
+static int get_operands (int, char *, expressionS *);
+static int get_putget_operands (struct mmix_opcode *, char *, expressionS *);
+static void s_prefix (int);
+static void s_greg (int);
+static void s_loc (int);
+static void s_bspec (int);
+static void s_espec (int);
+static void mmix_s_local (int);
+static void mmix_greg_internal (char *);
+static void mmix_set_geta_branch_offset (char *, offsetT);
+static void mmix_set_jmp_offset (char *, offsetT);
+static void mmix_fill_nops (char *, int);
+static int cmp_greg_symbol_fixes (const void *, const void *);
+static int cmp_greg_val_greg_symbol_fixes (const void *, const void *);
+static void mmix_handle_rest_of_empty_line (void);
+static void mmix_discard_rest_of_line (void);
+static void mmix_byte (void);
+static void mmix_cons (int);
+
+/* Continue the tradition of symbols.c; use control characters to enforce
+ magic. These are used when replacing e.g. 8F and 8B so we can handle
+ such labels correctly with the common parser hooks. */
+#define MAGIC_FB_BACKWARD_CHAR '\003'
+#define MAGIC_FB_FORWARD_CHAR '\004'
+
+/* Copy the location of a frag to a fix. */
+#define COPY_FR_WHERE_TO_FX(FRAG, FIX) \
+ do \
+ { \
+ (FIX)->fx_file = (FRAG)->fr_file; \
+ (FIX)->fx_line = (FRAG)->fr_line; \
+ } \
+ while (0)
+
+const char *md_shortopts = "x";
+static int current_fb_label = -1;
+static char *pending_label = NULL;
+
+static bfd_vma lowest_text_loc = (bfd_vma) -1;
+static int text_has_contents = 0;
+
+/* The alignment of the previous instruction, and a boolean for whether we
+ want to avoid aligning the next WYDE, TETRA, OCTA or insn. */
+static int last_alignment = 0;
+static int want_unaligned = 0;
+
+static bfd_vma lowest_data_loc = (bfd_vma) -1;
+static int data_has_contents = 0;
+
+/* The fragS of the instruction being assembled. Only valid from within
+ md_assemble. */
+fragS *mmix_opcode_frag = NULL;
+
+/* Raw GREGs as appearing in input. These may be fewer than the number
+ after relaxing. */
+static int n_of_raw_gregs = 0;
+static struct
+ {
+ char *label;
+ expressionS exp;
+ } mmix_raw_gregs[MAX_GREGS];
+
+static struct loc_assert_s
+ {
+ segT old_seg;
+ symbolS *loc_sym;
+ fragS *frag;
+ struct loc_assert_s *next;
+ } *loc_asserts = NULL;
+
+/* Fixups for all unique GREG registers. We store the fixups here in
+ md_convert_frag, then we use the array to convert
+ BFD_RELOC_MMIX_BASE_PLUS_OFFSET fixups in tc_gen_reloc. The index is
+ just a running number and is not supposed to be correlated to a
+ register number. */
+static fixS *mmix_gregs[MAX_GREGS];
+static int n_of_cooked_gregs = 0;
+
+/* Pointing to the register section we use for output. */
+static asection *real_reg_section;
+
+/* For each symbol; unknown or section symbol, we keep a list of GREG
+ definitions sorted on increasing offset. It seems no use keeping count
+ to allocate less room than the maximum number of gregs when we've found
+ one for a section or symbol. */
+struct mmix_symbol_gregs
+ {
+ int n_gregs;
+ struct mmix_symbol_greg_fixes
+ {
+ fixS *fix;
+
+ /* A signed type, since we may have GREGs pointing slightly before the
+ contents of a section. */
+ offsetT offs;
+ } greg_fixes[MAX_GREGS];
+ };
+
+/* Should read insert a colon on something that starts in column 0 on
+ this line? */
+static int label_without_colon_this_line = 1;
+
+/* Should we automatically expand instructions into multiple insns in
+ order to generate working code? */
+static int expand_op = 1;
+
+/* Should we warn when expanding operands? FIXME: test-cases for when -x
+ is absent. */
+static int warn_on_expansion = 1;
+
+/* Should we merge non-zero GREG register definitions? */
+static int merge_gregs = 1;
+
+/* Should we pass on undefined BFD_RELOC_MMIX_BASE_PLUS_OFFSET relocs
+ (missing suitable GREG definitions) to the linker? */
+static int allocate_undefined_gregs_in_linker = 0;
+
+/* Should we emit built-in symbols? */
+static int predefined_syms = 1;
+
+/* Should we allow anything but the listed special register name
+ (e.g. equated symbols)? */
+static int equated_spec_regs = 1;
+
+/* Do we require standard GNU syntax? */
+int mmix_gnu_syntax = 0;
+
+/* Do we globalize all symbols? */
+int mmix_globalize_symbols = 0;
+
+/* When expanding insns, do we want to expand PUSHJ as a call to a stub
+ (or else as a series of insns)? */
+int pushj_stubs = 1;
+
+/* Do we know that the next semicolon is at the end of the operands field
+ (in mmixal mode; constant 1 in GNU mode)? */
+int mmix_next_semicolon_is_eoln = 1;
+
+/* Do we have a BSPEC in progress? */
+static int doing_bspec = 0;
+static char *bspec_file;
+static unsigned int bspec_line;
+
+struct option md_longopts[] =
+ {
+#define OPTION_RELAX (OPTION_MD_BASE)
+#define OPTION_NOEXPAND (OPTION_RELAX + 1)
+#define OPTION_NOMERGEGREG (OPTION_NOEXPAND + 1)
+#define OPTION_NOSYMS (OPTION_NOMERGEGREG + 1)
+#define OPTION_GNU_SYNTAX (OPTION_NOSYMS + 1)
+#define OPTION_GLOBALIZE_SYMBOLS (OPTION_GNU_SYNTAX + 1)
+#define OPTION_FIXED_SPEC_REGS (OPTION_GLOBALIZE_SYMBOLS + 1)
+#define OPTION_LINKER_ALLOCATED_GREGS (OPTION_FIXED_SPEC_REGS + 1)
+#define OPTION_NOPUSHJSTUBS (OPTION_LINKER_ALLOCATED_GREGS + 1)
+ {"linkrelax", no_argument, NULL, OPTION_RELAX},
+ {"no-expand", no_argument, NULL, OPTION_NOEXPAND},
+ {"no-merge-gregs", no_argument, NULL, OPTION_NOMERGEGREG},
+ {"no-predefined-syms", no_argument, NULL, OPTION_NOSYMS},
+ {"gnu-syntax", no_argument, NULL, OPTION_GNU_SYNTAX},
+ {"globalize-symbols", no_argument, NULL, OPTION_GLOBALIZE_SYMBOLS},
+ {"fixed-special-register-names", no_argument, NULL,
+ OPTION_FIXED_SPEC_REGS},
+ {"linker-allocated-gregs", no_argument, NULL,
+ OPTION_LINKER_ALLOCATED_GREGS},
+ {"no-pushj-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
+ {"no-stubs", no_argument, NULL, OPTION_NOPUSHJSTUBS},
+ {NULL, no_argument, NULL, 0}
+ };
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+static struct hash_control *mmix_opcode_hash;
+
+/* We use these when implementing the PREFIX pseudo. */
+char *mmix_current_prefix;
+struct obstack mmix_sym_obstack;
+
+
+/* For MMIX, we encode the relax_substateT:s (in e.g. fr_substate) as one
+ bit length, and the relax-type shifted on top of that. There seems to
+ be no point in making the relaxation more fine-grained; the linker does
+ that better and we might interfere by changing non-optimal relaxations
+ into other insns that cannot be relaxed as easily.
+
+ Groups for MMIX relaxing:
+
+ 1. GETA
+ extra length: zero or three insns.
+
+ 2. Bcc
+ extra length: zero or five insns.
+
+ 3. PUSHJ
+ extra length: zero or four insns.
+ Special handling to deal with transition to PUSHJSTUB.
+
+ 4. JMP
+ extra length: zero or four insns.
+
+ 5. GREG
+ special handling, allocates a named global register unless another
+ is within reach for all uses.
+
+ 6. PUSHJSTUB
+ special handling (mostly) for external references; assumes the
+ linker will generate a stub if target is no longer than 256k from
+ the end of the section plus max size of previous stubs. Zero or
+ four insns. */
+
+#define STATE_GETA (1)
+#define STATE_BCC (2)
+#define STATE_PUSHJ (3)
+#define STATE_JMP (4)
+#define STATE_GREG (5)
+#define STATE_PUSHJSTUB (6)
+
+/* No fine-grainedness here. */
+#define STATE_LENGTH_MASK (1)
+
+#define STATE_ZERO (0)
+#define STATE_MAX (1)
+
+/* More descriptive name for convenience. */
+/* FIXME: We should start on something different, not MAX. */
+#define STATE_UNDF STATE_MAX
+
+/* FIXME: For GREG, we must have other definitions; UNDF == MAX isn't
+ appropriate; we need it the other way round. This value together with
+ fragP->tc_frag_data shows what state the frag is in: tc_frag_data
+ non-NULL means 0, NULL means 8 bytes. */
+#define STATE_GREG_UNDF ENCODE_RELAX (STATE_GREG, STATE_ZERO)
+#define STATE_GREG_DEF ENCODE_RELAX (STATE_GREG, STATE_MAX)
+
+/* These displacements are relative to the address following the opcode
+ word of the instruction. The catch-all states have zero for "reach"
+ and "next" entries. */
+
+#define GETA_0F (65536 * 4 - 8)
+#define GETA_0B (-65536 * 4 - 4)
+
+#define GETA_MAX_LEN 4 * 4
+#define GETA_3F 0
+#define GETA_3B 0
+
+#define BCC_0F GETA_0F
+#define BCC_0B GETA_0B
+
+#define BCC_MAX_LEN 6 * 4
+#define BCC_5F GETA_3F
+#define BCC_5B GETA_3B
+
+#define PUSHJ_0F GETA_0F
+#define PUSHJ_0B GETA_0B
+
+#define PUSHJ_MAX_LEN 5 * 4
+#define PUSHJ_4F GETA_3F
+#define PUSHJ_4B GETA_3B
+
+/* We'll very rarely have sections longer than LONG_MAX, but we'll make a
+ feeble attempt at getting 64-bit values. */
+#define PUSHJSTUB_MAX ((offsetT) (((addressT) -1) >> 1))
+#define PUSHJSTUB_MIN (-PUSHJSTUB_MAX - 1)
+
+#define JMP_0F (65536 * 256 * 4 - 8)
+#define JMP_0B (-65536 * 256 * 4 - 4)
+
+#define JMP_MAX_LEN 5 * 4
+#define JMP_4F 0
+#define JMP_4B 0
+
+#define RELAX_ENCODE_SHIFT 1
+#define ENCODE_RELAX(what, length) (((what) << RELAX_ENCODE_SHIFT) + (length))
+
+const relax_typeS mmix_relax_table[] =
+ {
+ /* Error sentinel (0, 0). */
+ {1, 1, 0, 0},
+
+ /* Unused (0, 1). */
+ {1, 1, 0, 0},
+
+ /* GETA (1, 0). */
+ {GETA_0F, GETA_0B, 0, ENCODE_RELAX (STATE_GETA, STATE_MAX)},
+
+ /* GETA (1, 1). */
+ {GETA_3F, GETA_3B,
+ GETA_MAX_LEN - 4, 0},
+
+ /* BCC (2, 0). */
+ {BCC_0F, BCC_0B, 0, ENCODE_RELAX (STATE_BCC, STATE_MAX)},
+
+ /* BCC (2, 1). */
+ {BCC_5F, BCC_5B,
+ BCC_MAX_LEN - 4, 0},
+
+ /* PUSHJ (3, 0). Next state is actually PUSHJSTUB (6, 0). */
+ {PUSHJ_0F, PUSHJ_0B, 0, ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO)},
+
+ /* PUSHJ (3, 1). */
+ {PUSHJ_4F, PUSHJ_4B,
+ PUSHJ_MAX_LEN - 4, 0},
+
+ /* JMP (4, 0). */
+ {JMP_0F, JMP_0B, 0, ENCODE_RELAX (STATE_JMP, STATE_MAX)},
+
+ /* JMP (4, 1). */
+ {JMP_4F, JMP_4B,
+ JMP_MAX_LEN - 4, 0},
+
+ /* GREG (5, 0), (5, 1), though the table entry isn't used. */
+ {0, 0, 0, 0}, {0, 0, 0, 0},
+
+ /* PUSHJSTUB (6, 0). PUSHJ (3, 0) uses the range, so we set it to infinite. */
+ {PUSHJSTUB_MAX, PUSHJSTUB_MIN,
+ 0, ENCODE_RELAX (STATE_PUSHJ, STATE_MAX)},
+ /* PUSHJSTUB (6, 1) isn't used. */
+ {0, 0, PUSHJ_MAX_LEN, 0}
+};
+
+const pseudo_typeS md_pseudo_table[] =
+ {
+ /* Support " .greg sym,expr" syntax. */
+ {"greg", s_greg, 0},
+
+ /* Support " .bspec expr" syntax. */
+ {"bspec", s_bspec, 1},
+
+ /* Support " .espec" syntax. */
+ {"espec", s_espec, 1},
+
+ /* Support " .local $45" syntax. */
+ {"local", mmix_s_local, 1},
+
+ {NULL, 0, 0}
+ };
+
+const char mmix_comment_chars[] = "%!";
+
+/* A ':' is a valid symbol character in mmixal. It's the prefix
+ delimiter, but other than that, it works like a symbol character,
+ except that we strip one off at the beginning of symbols. An '@' is a
+ symbol by itself (for the current location); space around it must not
+ be stripped. */
+const char mmix_symbol_chars[] = ":@";
+
+const char line_comment_chars[] = "*#";
+
+const char line_separator_chars[] = ";";
+
+const char mmix_exp_chars[] = "eE";
+
+const char mmix_flt_chars[] = "rf";
+
+
+/* Fill in the offset-related part of GETA or Bcc. */
+
+static void
+mmix_set_geta_branch_offset (char *opcodep, offsetT value)
+{
+ if (value < 0)
+ {
+ value += 65536 * 4;
+ opcodep[0] |= 1;
+ }
+
+ value /= 4;
+ md_number_to_chars (opcodep + 2, value, 2);
+}
+
+/* Fill in the offset-related part of JMP. */
+
+static void
+mmix_set_jmp_offset (char *opcodep, offsetT value)
+{
+ if (value < 0)
+ {
+ value += 65536 * 256 * 4;
+ opcodep[0] |= 1;
+ }
+
+ value /= 4;
+ md_number_to_chars (opcodep + 1, value, 3);
+}
+
+/* Fill in NOP:s for the expanded part of GETA/JMP/Bcc/PUSHJ. */
+
+static void
+mmix_fill_nops (char *opcodep, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ md_number_to_chars (opcodep + i * 4, SWYM_INSN_BYTE << 24, 4);
+}
+
+/* See macro md_parse_name in tc-mmix.h. */
+
+int
+mmix_current_location (void (*fn) (expressionS *), expressionS *exp)
+{
+ (*fn) (exp);
+
+ return 1;
+}
+
+/* Get up to three operands, filling them into the exp array.
+ General idea and code stolen from the tic80 port. */
+
+static int
+get_operands (int max_operands, char *s, expressionS *exp)
+{
+ char *p = s;
+ int numexp = 0;
+ int nextchar = ',';
+
+ while (nextchar == ',')
+ {
+ /* Skip leading whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* Check to see if we have any operands left to parse */
+ if (*p == 0 || *p == '\n' || *p == '\r')
+ {
+ break;
+ }
+ else if (numexp == max_operands)
+ {
+ /* This seems more sane than saying "too many operands". We'll
+ get here only if the trailing trash starts with a comma. */
+ as_bad (_("invalid operands"));
+ mmix_discard_rest_of_line ();
+ return 0;
+ }
+
+ /* Begin operand parsing at the current scan point. */
+
+ input_line_pointer = p;
+ expression (&exp[numexp]);
+
+ if (exp[numexp].X_op == O_illegal)
+ {
+ as_bad (_("invalid operands"));
+ }
+ else if (exp[numexp].X_op == O_absent)
+ {
+ as_bad (_("missing operand"));
+ }
+
+ numexp++;
+ p = input_line_pointer;
+
+ /* Skip leading whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ nextchar = *p++;
+ }
+
+ /* If we allow "naked" comments, ignore the rest of the line. */
+ if (nextchar != ',')
+ {
+ mmix_handle_rest_of_empty_line ();
+ input_line_pointer--;
+ }
+
+ /* Mark the end of the valid operands with an illegal expression. */
+ exp[numexp].X_op = O_illegal;
+
+ return (numexp);
+}
+
+/* Get the value of a special register, or -1 if the name does not match
+ one. NAME is a null-terminated string. */
+
+static int
+get_spec_regno (char *name)
+{
+ int i;
+
+ if (name == NULL)
+ return -1;
+
+ if (*name == ':')
+ name++;
+
+ /* Well, it's a short array and we'll most often just match the first
+ entry, rJ. */
+ for (i = 0; mmix_spec_regs[i].name != NULL; i++)
+ if (strcmp (name, mmix_spec_regs[i].name) == 0)
+ return mmix_spec_regs[i].number;
+
+ return -1;
+}
+
+/* For GET and PUT, parse the register names "manually", so we don't use
+ user labels. */
+static int
+get_putget_operands (struct mmix_opcode *insn, char *operands,
+ expressionS *exp)
+{
+ expressionS *expp_reg;
+ expressionS *expp_sreg;
+ char *sregp = NULL;
+ char *sregend = operands;
+ char *p = operands;
+ char c = *sregend;
+ int regno;
+
+ /* Skip leading whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ input_line_pointer = p;
+
+ /* Initialize both possible operands to error state, in case we never
+ get further. */
+ exp[0].X_op = O_illegal;
+ exp[1].X_op = O_illegal;
+
+ if (insn->operands == mmix_operands_get)
+ {
+ expp_reg = &exp[0];
+ expp_sreg = &exp[1];
+
+ expression (expp_reg);
+
+ p = input_line_pointer;
+
+ /* Skip whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p == ',')
+ {
+ p++;
+
+ /* Skip whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+ sregp = p;
+ input_line_pointer = sregp;
+ c = get_symbol_end ();
+ sregend = input_line_pointer;
+ }
+ }
+ else
+ {
+ expp_sreg = &exp[0];
+ expp_reg = &exp[1];
+
+ sregp = p;
+ c = get_symbol_end ();
+ sregend = p = input_line_pointer;
+ *p = c;
+
+ /* Skip whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ if (*p == ',')
+ {
+ p++;
+
+ /* Skip whitespace */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ input_line_pointer = p;
+ expression (expp_reg);
+ }
+ *sregend = 0;
+ }
+
+ regno = get_spec_regno (sregp);
+ *sregend = c;
+
+ /* Let the caller issue errors; we've made sure the operands are
+ invalid. */
+ if (expp_reg->X_op != O_illegal
+ && expp_reg->X_op != O_absent
+ && regno != -1)
+ {
+ expp_sreg->X_op = O_register;
+ expp_sreg->X_add_number = regno + 256;
+ }
+
+ return 2;
+}
+
+/* Handle MMIX-specific option. */
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case 'x':
+ warn_on_expansion = 0;
+ allocate_undefined_gregs_in_linker = 1;
+ break;
+
+ case OPTION_RELAX:
+ linkrelax = 1;
+ break;
+
+ case OPTION_NOEXPAND:
+ expand_op = 0;
+ break;
+
+ case OPTION_NOMERGEGREG:
+ merge_gregs = 0;
+ break;
+
+ case OPTION_NOSYMS:
+ predefined_syms = 0;
+ equated_spec_regs = 0;
+ break;
+
+ case OPTION_GNU_SYNTAX:
+ mmix_gnu_syntax = 1;
+ label_without_colon_this_line = 0;
+ break;
+
+ case OPTION_GLOBALIZE_SYMBOLS:
+ mmix_globalize_symbols = 1;
+ break;
+
+ case OPTION_FIXED_SPEC_REGS:
+ equated_spec_regs = 0;
+ break;
+
+ case OPTION_LINKER_ALLOCATED_GREGS:
+ allocate_undefined_gregs_in_linker = 1;
+ break;
+
+ case OPTION_NOPUSHJSTUBS:
+ pushj_stubs = 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Display MMIX-specific help text. */
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _(" MMIX-specific command line options:\n"));
+ fprintf (stream, _("\
+ -fixed-special-register-names\n\
+ Allow only the original special register names.\n"));
+ fprintf (stream, _("\
+ -globalize-symbols Make all symbols global.\n"));
+ fprintf (stream, _("\
+ -gnu-syntax Turn off mmixal syntax compatibility.\n"));
+ fprintf (stream, _("\
+ -relax Create linker relaxable code.\n"));
+ fprintf (stream, _("\
+ -no-predefined-syms Do not provide mmixal built-in constants.\n\
+ Implies -fixed-special-register-names.\n"));
+ fprintf (stream, _("\
+ -no-expand Do not expand GETA, branches, PUSHJ or JUMP\n\
+ into multiple instructions.\n"));
+ fprintf (stream, _("\
+ -no-merge-gregs Do not merge GREG definitions with nearby values.\n"));
+ fprintf (stream, _("\
+ -linker-allocated-gregs If there's no suitable GREG definition for the\
+ operands of an instruction, let the linker resolve.\n"));
+ fprintf (stream, _("\
+ -x Do not warn when an operand to GETA, a branch,\n\
+ PUSHJ or JUMP is not known to be within range.\n\
+ The linker will catch any errors. Implies\n\
+ -linker-allocated-gregs."));
+}
+
+/* Step to end of line, but don't step over the end of the line. */
+
+static void
+mmix_discard_rest_of_line (void)
+{
+ while (*input_line_pointer
+ && (! is_end_of_line[(unsigned char) *input_line_pointer]
+ || TC_EOL_IN_INSN (input_line_pointer)))
+ input_line_pointer++;
+}
+
+/* Act as demand_empty_rest_of_line if we're in strict GNU syntax mode,
+ otherwise just ignore the rest of the line (and skip the end-of-line
+ delimiter). */
+
+static void
+mmix_handle_rest_of_empty_line (void)
+{
+ if (mmix_gnu_syntax)
+ demand_empty_rest_of_line ();
+ else
+ {
+ mmix_discard_rest_of_line ();
+ input_line_pointer++;
+ }
+}
+
+/* Initialize GAS MMIX specifics. */
+
+void
+mmix_md_begin (void)
+{
+ int i;
+ const struct mmix_opcode *opcode;
+
+ /* We assume nobody will use this, so don't allocate any room. */
+ obstack_begin (&mmix_sym_obstack, 0);
+
+ /* This will break the day the "lex" thingy changes. For now, it's the
+ only way to make ':' part of a name, and a name beginner. */
+ lex_type[':'] = (LEX_NAME | LEX_BEGIN_NAME);
+
+ mmix_opcode_hash = hash_new ();
+
+ real_reg_section
+ = bfd_make_section_old_way (stdoutput, MMIX_REG_SECTION_NAME);
+
+ for (opcode = mmix_opcodes; opcode->name; opcode++)
+ hash_insert (mmix_opcode_hash, opcode->name, (char *) opcode);
+
+ /* We always insert the ordinary registers 0..255 as registers. */
+ for (i = 0; i < 256; i++)
+ {
+ char buf[5];
+
+ /* Alternatively, we could diddle with '$' and the following number,
+ but keeping the registers as symbols helps keep parsing simple. */
+ sprintf (buf, "$%d", i);
+ symbol_table_insert (symbol_new (buf, reg_section, i,
+ &zero_address_frag));
+ }
+
+ /* Insert mmixal built-in names if allowed. */
+ if (predefined_syms)
+ {
+ for (i = 0; mmix_spec_regs[i].name != NULL; i++)
+ symbol_table_insert (symbol_new (mmix_spec_regs[i].name,
+ reg_section,
+ mmix_spec_regs[i].number + 256,
+ &zero_address_frag));
+
+ /* FIXME: Perhaps these should be recognized as specials; as field
+ names for those instructions. */
+ symbol_table_insert (symbol_new ("ROUND_CURRENT", reg_section, 512,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("ROUND_OFF", reg_section, 512 + 1,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("ROUND_UP", reg_section, 512 + 2,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("ROUND_DOWN", reg_section, 512 + 3,
+ &zero_address_frag));
+ symbol_table_insert (symbol_new ("ROUND_NEAR", reg_section, 512 + 4,
+ &zero_address_frag));
+ }
+}
+
+/* Assemble one insn in STR. */
+
+void
+md_assemble (char *str)
+{
+ char *operands = str;
+ char modified_char = 0;
+ struct mmix_opcode *instruction;
+ fragS *opc_fragP = NULL;
+ int max_operands = 3;
+
+ /* Note that the struct frag member fr_literal in frags.h is char[], so
+ I have to make this a plain char *. */
+ /* unsigned */ char *opcodep = NULL;
+
+ expressionS exp[4];
+ int n_operands = 0;
+
+ /* Move to end of opcode. */
+ for (operands = str;
+ is_part_of_name (*operands);
+ ++operands)
+ ;
+
+ if (ISSPACE (*operands))
+ {
+ modified_char = *operands;
+ *operands++ = '\0';
+ }
+
+ instruction = (struct mmix_opcode *) hash_find (mmix_opcode_hash, str);
+ if (instruction == NULL)
+ {
+ as_bad (_("unknown opcode: `%s'"), str);
+
+ /* Avoid "unhandled label" errors. */
+ pending_label = NULL;
+ return;
+ }
+
+ /* Put back the character after the opcode. */
+ if (modified_char != 0)
+ operands[-1] = modified_char;
+
+ input_line_pointer = operands;
+
+ /* Is this a mmixal pseudodirective? */
+ if (instruction->type == mmix_type_pseudo)
+ {
+ /* For mmixal compatibility, a label for an instruction (and
+ emitting pseudo) refers to the _aligned_ address. We emit the
+ label here for the pseudos that don't handle it themselves. When
+ having an fb-label, emit it here, and increment the counter after
+ the pseudo. */
+ switch (instruction->operands)
+ {
+ case mmix_operands_loc:
+ case mmix_operands_byte:
+ case mmix_operands_prefix:
+ case mmix_operands_local:
+ case mmix_operands_bspec:
+ case mmix_operands_espec:
+ if (current_fb_label >= 0)
+ colon (fb_label_name (current_fb_label, 1));
+ else if (pending_label != NULL)
+ {
+ colon (pending_label);
+ pending_label = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Some of the pseudos emit contents, others don't. Set a
+ contents-emitted flag when we emit something into .text */
+ switch (instruction->operands)
+ {
+ case mmix_operands_loc:
+ /* LOC */
+ s_loc (0);
+ break;
+
+ case mmix_operands_byte:
+ /* BYTE */
+ mmix_byte ();
+ break;
+
+ case mmix_operands_wyde:
+ /* WYDE */
+ mmix_cons (2);
+ break;
+
+ case mmix_operands_tetra:
+ /* TETRA */
+ mmix_cons (4);
+ break;
+
+ case mmix_operands_octa:
+ /* OCTA */
+ mmix_cons (8);
+ break;
+
+ case mmix_operands_prefix:
+ /* PREFIX */
+ s_prefix (0);
+ break;
+
+ case mmix_operands_local:
+ /* LOCAL */
+ mmix_s_local (0);
+ break;
+
+ case mmix_operands_bspec:
+ /* BSPEC */
+ s_bspec (0);
+ break;
+
+ case mmix_operands_espec:
+ /* ESPEC */
+ s_espec (0);
+ break;
+
+ default:
+ BAD_CASE (instruction->operands);
+ }
+
+ /* These are all working like the pseudo functions in read.c:s_...,
+ in that they step over the end-of-line marker at the end of the
+ line. We don't want that here. */
+ input_line_pointer--;
+
+ /* Step up the fb-label counter if there was a definition on this
+ line. */
+ if (current_fb_label >= 0)
+ {
+ fb_label_instance_inc (current_fb_label);
+ current_fb_label = -1;
+ }
+
+ /* Reset any don't-align-next-datum request, unless this was a LOC
+ directive. */
+ if (instruction->operands != mmix_operands_loc)
+ want_unaligned = 0;
+
+ return;
+ }
+
+ /* Not a pseudo; we *will* emit contents. */
+ if (now_seg == data_section)
+ {
+ if (lowest_data_loc != (bfd_vma) -1 && (lowest_data_loc & 3) != 0)
+ {
+ if (data_has_contents)
+ as_bad (_("specified location wasn't TETRA-aligned"));
+ else if (want_unaligned)
+ as_bad (_("unaligned data at an absolute location is not supported"));
+
+ lowest_data_loc &= ~(bfd_vma) 3;
+ lowest_data_loc += 4;
+ }
+
+ data_has_contents = 1;
+ }
+ else if (now_seg == text_section)
+ {
+ if (lowest_text_loc != (bfd_vma) -1 && (lowest_text_loc & 3) != 0)
+ {
+ if (text_has_contents)
+ as_bad (_("specified location wasn't TETRA-aligned"));
+ else if (want_unaligned)
+ as_bad (_("unaligned data at an absolute location is not supported"));
+
+ lowest_text_loc &= ~(bfd_vma) 3;
+ lowest_text_loc += 4;
+ }
+
+ text_has_contents = 1;
+ }
+
+ /* After a sequence of BYTEs or WYDEs, we need to get to instruction
+ alignment. For other pseudos, a ".p2align 2" is supposed to be
+ inserted by the user. */
+ if (last_alignment < 2 && ! want_unaligned)
+ {
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+ last_alignment = 2;
+ }
+ else
+ /* Reset any don't-align-next-datum request. */
+ want_unaligned = 0;
+
+ /* For mmixal compatibility, a label for an instruction (and emitting
+ pseudo) refers to the _aligned_ address. So we have to emit the
+ label here. */
+ if (pending_label != NULL)
+ {
+ colon (pending_label);
+ pending_label = NULL;
+ }
+
+ /* We assume that mmix_opcodes keeps having unique mnemonics for each
+ opcode, so we don't have to iterate over more than one opcode; if the
+ syntax does not match, then there's a syntax error. */
+
+ /* Operands have little or no context and are all comma-separated; it is
+ easier to parse each expression first. */
+ switch (instruction->operands)
+ {
+ case mmix_operands_reg_yz:
+ case mmix_operands_pop:
+ case mmix_operands_regaddr:
+ case mmix_operands_pushj:
+ case mmix_operands_get:
+ case mmix_operands_put:
+ case mmix_operands_set:
+ case mmix_operands_save:
+ case mmix_operands_unsave:
+ max_operands = 2;
+ break;
+
+ case mmix_operands_sync:
+ case mmix_operands_jmp:
+ case mmix_operands_resume:
+ max_operands = 1;
+ break;
+
+ /* The original 3 is fine for the rest. */
+ default:
+ break;
+ }
+
+ /* If this is GET or PUT, and we don't do allow those names to be
+ equated, we need to parse the names ourselves, so we don't pick up a
+ user label instead of the special register. */
+ if (! equated_spec_regs
+ && (instruction->operands == mmix_operands_get
+ || instruction->operands == mmix_operands_put))
+ n_operands = get_putget_operands (instruction, operands, exp);
+ else
+ n_operands = get_operands (max_operands, operands, exp);
+
+ /* If there's a fb-label on the current line, set that label. This must
+ be done *after* evaluating expressions of operands, since neither a
+ "1B" nor a "1F" refers to "1H" on the same line. */
+ if (current_fb_label >= 0)
+ {
+ fb_label_instance_inc (current_fb_label);
+ colon (fb_label_name (current_fb_label, 0));
+ current_fb_label = -1;
+ }
+
+ /* We also assume that the length of the instruction is at least 4, the
+ size of an unexpanded instruction. We need a self-contained frag
+ since we want the relocation to point to the instruction, not the
+ variant part. */
+
+ opcodep = frag_more (4);
+ mmix_opcode_frag = opc_fragP = frag_now;
+ frag_now->fr_opcode = opcodep;
+
+ /* Mark start of insn for DWARF2 debug features. */
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ dwarf2_emit_insn (4);
+
+ md_number_to_chars (opcodep, instruction->match, 4);
+
+ switch (instruction->operands)
+ {
+ case mmix_operands_jmp:
+ if (n_operands == 0 && ! mmix_gnu_syntax)
+ /* Zeros are in place - nothing needs to be done when we have no
+ operands. */
+ break;
+
+ /* Add a frag for a JMP relaxation; we need room for max four
+ extra instructions. We don't do any work around here to check if
+ we can determine the offset right away. */
+ if (n_operands != 1 || exp[0].X_op == O_register)
+ {
+ as_bad (_("invalid operand to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (expand_op)
+ frag_var (rs_machine_dependent, 4 * 4, 0,
+ ENCODE_RELAX (STATE_JMP, STATE_UNDF),
+ exp[0].X_add_symbol,
+ exp[0].X_add_number,
+ opcodep);
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ exp + 0, 1, BFD_RELOC_MMIX_ADDR27);
+ break;
+
+ case mmix_operands_pushj:
+ /* We take care of PUSHJ in full here. */
+ if (n_operands != 2
+ || ((exp[0].X_op == O_constant || exp[0].X_op == O_register)
+ && (exp[0].X_add_number > 255 || exp[0].X_add_number < 0)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[0].X_op == O_register || exp[0].X_op == O_constant)
+ opcodep[1] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp + 0, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+
+ if (expand_op)
+ frag_var (rs_machine_dependent, PUSHJ_MAX_LEN - 4, 0,
+ ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF),
+ exp[1].X_add_symbol,
+ exp[1].X_add_number,
+ opcodep);
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ exp + 1, 1, BFD_RELOC_MMIX_ADDR19);
+ break;
+
+ case mmix_operands_regaddr:
+ /* GETA/branch: Add a frag for relaxation. We don't do any work
+ around here to check if we can determine the offset right away. */
+ if (n_operands != 2 || exp[1].X_op == O_register)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (! expand_op)
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ exp + 1, 1, BFD_RELOC_MMIX_ADDR19);
+ else if (instruction->type == mmix_type_condbranch)
+ frag_var (rs_machine_dependent, BCC_MAX_LEN - 4, 0,
+ ENCODE_RELAX (STATE_BCC, STATE_UNDF),
+ exp[1].X_add_symbol,
+ exp[1].X_add_number,
+ opcodep);
+ else
+ frag_var (rs_machine_dependent, GETA_MAX_LEN - 4, 0,
+ ENCODE_RELAX (STATE_GETA, STATE_UNDF),
+ exp[1].X_add_symbol,
+ exp[1].X_add_number,
+ opcodep);
+ break;
+
+ default:
+ break;
+ }
+
+ switch (instruction->operands)
+ {
+ case mmix_operands_regs:
+ /* We check the number of operands here, since we're in a
+ FALLTHROUGH sequence in the next switch. */
+ if (n_operands != 3 || exp[2].X_op == O_constant)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ /* FALLTHROUGH. */
+ case mmix_operands_regs_z:
+ if (n_operands != 3)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ /* FALLTHROUGH. */
+ case mmix_operands_reg_yz:
+ case mmix_operands_roundregs_z:
+ case mmix_operands_roundregs:
+ case mmix_operands_regs_z_opt:
+ case mmix_operands_neg:
+ case mmix_operands_regaddr:
+ case mmix_operands_get:
+ case mmix_operands_set:
+ case mmix_operands_save:
+ if (n_operands < 1
+ || (exp[0].X_op == O_register && exp[0].X_add_number > 255))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[0].X_op == O_register)
+ opcodep[1] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp + 0, 0, BFD_RELOC_MMIX_REG);
+ break;
+
+ default:
+ ;
+ }
+
+ /* A corresponding once-over for those who take an 8-bit constant as
+ their first operand. */
+ switch (instruction->operands)
+ {
+ case mmix_operands_pushgo:
+ /* PUSHGO: X is a constant, but can be expressed as a register.
+ We handle X here and use the common machinery of T,X,3,$ for
+ the rest of the operands. */
+ if (n_operands < 2
+ || ((exp[0].X_op == O_constant || exp[0].X_op == O_register)
+ && (exp[0].X_add_number > 255 || exp[0].X_add_number < 0)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else if (exp[0].X_op == O_constant || exp[0].X_op == O_register)
+ opcodep[1] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp + 0, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ break;
+
+ case mmix_operands_pop:
+ if ((n_operands == 0 || n_operands == 1) && ! mmix_gnu_syntax)
+ break;
+ /* FALLTHROUGH. */
+ case mmix_operands_x_regs_z:
+ if (n_operands < 1
+ || (exp[0].X_op == O_constant
+ && (exp[0].X_add_number > 255
+ || exp[0].X_add_number < 0)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[0].X_op == O_constant)
+ opcodep[1] = exp[0].X_add_number;
+ else
+ /* FIXME: This doesn't bring us unsignedness checking. */
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp + 0, 0, BFD_RELOC_8);
+ default:
+ ;
+ }
+
+ /* Handle the rest. */
+ switch (instruction->operands)
+ {
+ case mmix_operands_set:
+ /* SET: Either two registers, "$X,$Y", with Z field as zero, or
+ "$X,YZ", meaning change the opcode to SETL. */
+ if (n_operands != 2
+ || (exp[1].X_op == O_constant
+ && (exp[1].X_add_number > 0xffff || exp[1].X_add_number < 0)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[1].X_op == O_constant)
+ {
+ /* There's an ambiguity with "SET $0,Y" when Y isn't defined
+ yet. To keep things simple, we assume that Y is then a
+ register, and only change the opcode if Y is defined at this
+ point.
+
+ There's no compatibility problem with mmixal, since it emits
+ errors if the field is not defined at this point. */
+ md_number_to_chars (opcodep, SETL_INSN_BYTE, 1);
+
+ opcodep[2] = (exp[1].X_add_number >> 8) & 255;
+ opcodep[3] = exp[1].X_add_number & 255;
+ break;
+ }
+ /* FALLTHROUGH. */
+ case mmix_operands_x_regs_z:
+ /* SYNCD: "X,$Y,$Z|Z". */
+ /* FALLTHROUGH. */
+ case mmix_operands_regs:
+ /* Three registers, $X,$Y,$Z. */
+ /* FALLTHROUGH. */
+ case mmix_operands_regs_z:
+ /* Operands "$X,$Y,$Z|Z", number of arguments checked above. */
+ /* FALLTHROUGH. */
+ case mmix_operands_pushgo:
+ /* Operands "$X|X,$Y,$Z|Z", optional Z. */
+ /* FALLTHROUGH. */
+ case mmix_operands_regs_z_opt:
+ /* Operands "$X,$Y,$Z|Z", with $Z|Z being optional, default 0. Any
+ operands not completely decided yet are postponed to later in
+ assembly (but not until link-time yet). */
+
+ if ((n_operands != 2 && n_operands != 3)
+ || (exp[1].X_op == O_register && exp[1].X_add_number > 255)
+ || (n_operands == 3
+ && ((exp[2].X_op == O_register
+ && exp[2].X_add_number > 255
+ && mmix_gnu_syntax)
+ || (exp[2].X_op == O_constant
+ && (exp[2].X_add_number > 255
+ || exp[2].X_add_number < 0)))))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (n_operands == 2)
+ {
+ symbolS *sym;
+
+ /* The last operand is immediate whenever we see just two
+ operands. */
+ opcodep[0] |= IMM_OFFSET_BIT;
+
+ /* Now, we could either have an implied "0" as the Z operand, or
+ it could be the constant of a "base address plus offset". It
+ depends on whether it is allowed; only memory operations, as
+ signified by instruction->type and "T" and "X" operand types,
+ and it depends on whether we find a register in the second
+ operand, exp[1]. */
+ if (exp[1].X_op == O_register && exp[1].X_add_number <= 255)
+ {
+ /* A zero then; all done. */
+ opcodep[2] = exp[1].X_add_number;
+ break;
+ }
+
+ /* Not known as a register. Is base address plus offset
+ allowed, or can we assume that it is a register anyway? */
+ if ((instruction->operands != mmix_operands_regs_z_opt
+ && instruction->operands != mmix_operands_x_regs_z
+ && instruction->operands != mmix_operands_pushgo)
+ || (instruction->type != mmix_type_memaccess_octa
+ && instruction->type != mmix_type_memaccess_tetra
+ && instruction->type != mmix_type_memaccess_wyde
+ && instruction->type != mmix_type_memaccess_byte
+ && instruction->type != mmix_type_memaccess_block
+ && instruction->type != mmix_type_jsr
+ && instruction->type != mmix_type_branch))
+ {
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 1, exp + 1, 0, BFD_RELOC_MMIX_REG);
+ break;
+ }
+
+ /* To avoid getting a NULL add_symbol for constants and then
+ catching a SEGV in write_relocs since it doesn't handle
+ constants well for relocs other than PC-relative, we need to
+ pass expressions as symbols and use fix_new, not fix_new_exp. */
+ sym = make_expr_symbol (exp + 1);
+
+ /* Mark the symbol as being OK for a reloc. */
+ symbol_get_bfdsym (sym)->flags |= BSF_KEEP;
+
+ /* Now we know it can be a "base address plus offset". Add
+ proper fixup types so we can handle this later, when we've
+ parsed everything. */
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 8, sym, 0, 0, BFD_RELOC_MMIX_BASE_PLUS_OFFSET);
+ break;
+ }
+
+ if (exp[1].X_op == O_register)
+ opcodep[2] = exp[1].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 1, exp + 1, 0, BFD_RELOC_MMIX_REG);
+
+ /* In mmixal compatibility mode, we allow special registers as
+ constants for the Z operand. They have 256 added to their
+ register numbers, so the right thing will happen if we just treat
+ those as constants. */
+ if (exp[2].X_op == O_register && exp[2].X_add_number <= 255)
+ opcodep[3] = exp[2].X_add_number;
+ else if (exp[2].X_op == O_constant
+ || (exp[2].X_op == O_register && exp[2].X_add_number > 255))
+ {
+ opcodep[3] = exp[2].X_add_number;
+ opcodep[0] |= IMM_OFFSET_BIT;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 2, 0,
+ (instruction->operands == mmix_operands_set
+ || instruction->operands == mmix_operands_regs)
+ ? BFD_RELOC_MMIX_REG : BFD_RELOC_MMIX_REG_OR_BYTE);
+ break;
+
+ case mmix_operands_pop:
+ /* POP, one eight and one 16-bit operand. */
+ if (n_operands == 0 && ! mmix_gnu_syntax)
+ break;
+ if (n_operands == 1 && ! mmix_gnu_syntax)
+ goto a_single_24_bit_number_operand;
+ /* FALLTHROUGH. */
+ case mmix_operands_reg_yz:
+ /* A register and a 16-bit unsigned number. */
+ if (n_operands != 2
+ || exp[1].X_op == O_register
+ || (exp[1].X_op == O_constant
+ && (exp[1].X_add_number > 0xffff || exp[1].X_add_number < 0)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[1].X_op == O_constant)
+ {
+ opcodep[2] = (exp[1].X_add_number >> 8) & 255;
+ opcodep[3] = exp[1].X_add_number & 255;
+ }
+ else
+ /* FIXME: This doesn't bring us unsignedness checking. */
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 2, exp + 1, 0, BFD_RELOC_16);
+ break;
+
+ case mmix_operands_jmp:
+ /* A JMP. Everything is already done. */
+ break;
+
+ case mmix_operands_roundregs:
+ /* Two registers with optional rounding mode or constant in between. */
+ if ((n_operands == 3 && exp[2].X_op == O_constant)
+ || (n_operands == 2 && exp[1].X_op == O_constant))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ /* FALLTHROUGH. */
+ case mmix_operands_roundregs_z:
+ /* Like FLOT, "$X,ROUND_MODE,$Z|Z", but the rounding mode is
+ optional and can be the corresponding constant. */
+ {
+ /* Which exp index holds the second operand (not the rounding
+ mode). */
+ int op2no = n_operands - 1;
+
+ if ((n_operands != 2 && n_operands != 3)
+ || ((exp[op2no].X_op == O_register
+ && exp[op2no].X_add_number > 255)
+ || (exp[op2no].X_op == O_constant
+ && (exp[op2no].X_add_number > 255
+ || exp[op2no].X_add_number < 0)))
+ || (n_operands == 3
+ /* We don't allow for the rounding mode to be deferred; it
+ must be determined in the "first pass". It cannot be a
+ symbol equated to a rounding mode, but defined after
+ the first use. */
+ && ((exp[1].X_op == O_register
+ && exp[1].X_add_number < 512)
+ || (exp[1].X_op == O_constant
+ && exp[1].X_add_number < 0
+ && exp[1].X_add_number > 4)
+ || (exp[1].X_op != O_register
+ && exp[1].X_op != O_constant))))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ /* Add rounding mode if present. */
+ if (n_operands == 3)
+ opcodep[2] = exp[1].X_add_number & 255;
+
+ if (exp[op2no].X_op == O_register)
+ opcodep[3] = exp[op2no].X_add_number;
+ else if (exp[op2no].X_op == O_constant)
+ {
+ opcodep[3] = exp[op2no].X_add_number;
+ opcodep[0] |= IMM_OFFSET_BIT;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + op2no, 0,
+ instruction->operands == mmix_operands_roundregs
+ ? BFD_RELOC_MMIX_REG
+ : BFD_RELOC_MMIX_REG_OR_BYTE);
+ break;
+ }
+
+ case mmix_operands_sync:
+ a_single_24_bit_number_operand:
+ if (n_operands != 1
+ || exp[0].X_op == O_register
+ || (exp[0].X_op == O_constant
+ && (exp[0].X_add_number > 0xffffff || exp[0].X_add_number < 0)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[0].X_op == O_constant)
+ {
+ opcodep[1] = (exp[0].X_add_number >> 16) & 255;
+ opcodep[2] = (exp[0].X_add_number >> 8) & 255;
+ opcodep[3] = exp[0].X_add_number & 255;
+ }
+ else
+ /* FIXME: This doesn't bring us unsignedness checking. */
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 3, exp + 0, 0, BFD_RELOC_24);
+ break;
+
+ case mmix_operands_neg:
+ /* Operands "$X,Y,$Z|Z"; NEG or NEGU. Y is optional, 0 is default. */
+
+ if ((n_operands != 3 && n_operands != 2)
+ || (n_operands == 3 && exp[1].X_op == O_register)
+ || ((exp[1].X_op == O_constant || exp[1].X_op == O_register)
+ && (exp[1].X_add_number > 255 || exp[1].X_add_number < 0))
+ || (n_operands == 3
+ && ((exp[2].X_op == O_register && exp[2].X_add_number > 255)
+ || (exp[2].X_op == O_constant
+ && (exp[2].X_add_number > 255
+ || exp[2].X_add_number < 0)))))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (n_operands == 2)
+ {
+ if (exp[1].X_op == O_register)
+ opcodep[3] = exp[1].X_add_number;
+ else if (exp[1].X_op == O_constant)
+ {
+ opcodep[3] = exp[1].X_add_number;
+ opcodep[0] |= IMM_OFFSET_BIT;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ break;
+ }
+
+ if (exp[1].X_op == O_constant)
+ opcodep[2] = exp[1].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 1, exp + 1, 0, BFD_RELOC_8);
+
+ if (exp[2].X_op == O_register)
+ opcodep[3] = exp[2].X_add_number;
+ else if (exp[2].X_op == O_constant)
+ {
+ opcodep[3] = exp[2].X_add_number;
+ opcodep[0] |= IMM_OFFSET_BIT;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 2, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ break;
+
+ case mmix_operands_regaddr:
+ /* A GETA/branch-type. */
+ break;
+
+ case mmix_operands_get:
+ /* "$X,spec_reg"; GET.
+ Like with rounding modes, we demand that the special register or
+ symbol is already defined when we get here at the point of use. */
+ if (n_operands != 2
+ || (exp[1].X_op == O_register
+ && (exp[1].X_add_number < 256 || exp[1].X_add_number >= 512))
+ || (exp[1].X_op == O_constant
+ && (exp[1].X_add_number < 0 || exp[1].X_add_number > 256))
+ || (exp[1].X_op != O_constant && exp[1].X_op != O_register))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ opcodep[3] = exp[1].X_add_number - 256;
+ break;
+
+ case mmix_operands_put:
+ /* "spec_reg,$Z|Z"; PUT. */
+ if (n_operands != 2
+ || (exp[0].X_op == O_register
+ && (exp[0].X_add_number < 256 || exp[0].X_add_number >= 512))
+ || (exp[0].X_op == O_constant
+ && (exp[0].X_add_number < 0 || exp[0].X_add_number > 256))
+ || (exp[0].X_op != O_constant && exp[0].X_op != O_register))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ opcodep[1] = exp[0].X_add_number - 256;
+
+ /* Note that the Y field is zero. */
+
+ if (exp[1].X_op == O_register)
+ opcodep[3] = exp[1].X_add_number;
+ else if (exp[1].X_op == O_constant)
+ {
+ opcodep[3] = exp[1].X_add_number;
+ opcodep[0] |= IMM_OFFSET_BIT;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 1, 0, BFD_RELOC_MMIX_REG_OR_BYTE);
+ break;
+
+ case mmix_operands_save:
+ /* "$X,0"; SAVE. */
+ if (n_operands != 2
+ || exp[1].X_op != O_constant
+ || exp[1].X_add_number != 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ break;
+
+ case mmix_operands_unsave:
+ if (n_operands < 2 && ! mmix_gnu_syntax)
+ {
+ if (n_operands == 1)
+ {
+ if (exp[0].X_op == O_register)
+ opcodep[3] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp, 0, BFD_RELOC_MMIX_REG);
+ }
+ break;
+ }
+
+ /* "0,$Z"; UNSAVE. */
+ if (n_operands != 2
+ || exp[0].X_op != O_constant
+ || exp[0].X_add_number != 0
+ || exp[1].X_op == O_constant
+ || (exp[1].X_op == O_register
+ && exp[1].X_add_number > 255))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[1].X_op == O_register)
+ opcodep[3] = exp[1].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 1, 0, BFD_RELOC_MMIX_REG);
+ break;
+
+ case mmix_operands_xyz_opt:
+ /* SWYM, TRIP, TRAP: zero, one, two or three operands. It's
+ unspecified whether operands are registers or constants, but
+ when we find register syntax, we require operands to be literal and
+ within 0..255. */
+ if (n_operands == 0 && ! mmix_gnu_syntax)
+ /* Zeros are in place - nothing needs to be done for zero
+ operands. We don't allow this in GNU syntax mode, because it
+ was believed that the risk of missing to supply an operand is
+ higher than the benefit of not having to specify a zero. */
+ ;
+ else if (n_operands == 1 && exp[0].X_op != O_register)
+ {
+ if (exp[0].X_op == O_constant)
+ {
+ if (exp[0].X_add_number > 255*256*256
+ || exp[0].X_add_number < 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else
+ {
+ opcodep[1] = (exp[0].X_add_number >> 16) & 255;
+ opcodep[2] = (exp[0].X_add_number >> 8) & 255;
+ opcodep[3] = exp[0].X_add_number & 255;
+ }
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 3, exp, 0, BFD_RELOC_24);
+ }
+ else if (n_operands == 2
+ && exp[0].X_op != O_register
+ && exp[1].X_op != O_register)
+ {
+ /* Two operands. */
+
+ if (exp[0].X_op == O_constant)
+ {
+ if (exp[0].X_add_number > 255
+ || exp[0].X_add_number < 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else
+ opcodep[1] = exp[0].X_add_number & 255;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp, 0, BFD_RELOC_8);
+
+ if (exp[1].X_op == O_constant)
+ {
+ if (exp[1].X_add_number > 255*256
+ || exp[1].X_add_number < 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else
+ {
+ opcodep[2] = (exp[1].X_add_number >> 8) & 255;
+ opcodep[3] = exp[1].X_add_number & 255;
+ }
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 2, exp + 1, 0, BFD_RELOC_16);
+ }
+ else if (n_operands == 3
+ && exp[0].X_op != O_register
+ && exp[1].X_op != O_register
+ && exp[2].X_op != O_register)
+ {
+ /* Three operands. */
+
+ if (exp[0].X_op == O_constant)
+ {
+ if (exp[0].X_add_number > 255
+ || exp[0].X_add_number < 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else
+ opcodep[1] = exp[0].X_add_number & 255;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp, 0, BFD_RELOC_8);
+
+ if (exp[1].X_op == O_constant)
+ {
+ if (exp[1].X_add_number > 255
+ || exp[1].X_add_number < 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else
+ opcodep[2] = exp[1].X_add_number & 255;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 1, exp + 1, 0, BFD_RELOC_8);
+
+ if (exp[2].X_op == O_constant)
+ {
+ if (exp[2].X_add_number > 255
+ || exp[2].X_add_number < 0)
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+ else
+ opcodep[3] = exp[2].X_add_number & 255;
+ }
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 2, 0, BFD_RELOC_8);
+ }
+ else
+ {
+ /* We can't get here for other cases. */
+ gas_assert (n_operands <= 3);
+
+ /* The meaning of operands to TRIP and TRAP is not defined (and
+ SWYM operands aren't enforced in mmixal, so let's avoid
+ that). We add combinations not handled above here as we find
+ them and as they're reported. */
+ if (n_operands == 3)
+ {
+ /* Don't require non-register operands. Always generate
+ fixups, so we don't have to copy lots of code and create
+ maintenance problems. TRIP is supposed to be a rare
+ instruction, so the overhead should not matter. We
+ aren't allowed to fix_new_exp for an expression which is
+ an O_register at this point, however.
+
+ Don't use BFD_RELOC_MMIX_REG_OR_BYTE as that modifies
+ the insn for a register in the Z field and we want
+ consistency. */
+ if (exp[0].X_op == O_register)
+ opcodep[1] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp, 0, BFD_RELOC_8);
+ if (exp[1].X_op == O_register)
+ opcodep[2] = exp[1].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 1, exp + 1, 0, BFD_RELOC_8);
+ if (exp[2].X_op == O_register)
+ opcodep[3] = exp[2].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 2, 0, BFD_RELOC_8);
+ }
+ else if (n_operands == 2)
+ {
+ if (exp[0].X_op == O_register)
+ opcodep[1] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 1,
+ 1, exp, 0, BFD_RELOC_8);
+ if (exp[1].X_op == O_register)
+ opcodep[3] = exp[1].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 2,
+ 2, exp + 1, 0, BFD_RELOC_16);
+ }
+ else
+ {
+ /* We can't get here for other cases. */
+ gas_assert (n_operands == 1 && exp[0].X_op == O_register);
+
+ opcodep[3] = exp[0].X_add_number;
+ }
+ }
+ break;
+
+ case mmix_operands_resume:
+ if (n_operands == 0 && ! mmix_gnu_syntax)
+ break;
+
+ if (n_operands != 1
+ || exp[0].X_op == O_register
+ || (exp[0].X_op == O_constant
+ && (exp[0].X_add_number < 0
+ || exp[0].X_add_number > 255)))
+ {
+ as_bad (_("invalid operands to opcode %s: `%s'"),
+ instruction->name, operands);
+ return;
+ }
+
+ if (exp[0].X_op == O_constant)
+ opcodep[3] = exp[0].X_add_number;
+ else
+ fix_new_exp (opc_fragP, opcodep - opc_fragP->fr_literal + 3,
+ 1, exp + 0, 0, BFD_RELOC_8);
+ break;
+
+ case mmix_operands_pushj:
+ /* All is done for PUSHJ already. */
+ break;
+
+ default:
+ BAD_CASE (instruction->operands);
+ }
+}
+
+/* For the benefit of insns that start with a digit, we assemble by way of
+ tc_unrecognized_line too, through this function. */
+
+int
+mmix_assemble_return_nonzero (char *str)
+{
+ int last_error_count = had_errors ();
+ char *s2 = str;
+ char c;
+
+ /* Normal instruction handling downcases, so we must too. */
+ while (ISALNUM (*s2))
+ {
+ if (ISUPPER ((unsigned char) *s2))
+ *s2 = TOLOWER (*s2);
+ s2++;
+ }
+
+ /* Cut the line for sake of the assembly. */
+ for (s2 = str; *s2 && *s2 != '\n'; s2++)
+ ;
+
+ c = *s2;
+ *s2 = 0;
+ md_assemble (str);
+ *s2 = c;
+
+ return had_errors () == last_error_count;
+}
+
+/* The PREFIX pseudo. */
+
+static void
+s_prefix (int unused ATTRIBUTE_UNUSED)
+{
+ char *p;
+ int c;
+
+ SKIP_WHITESPACE ();
+
+ p = input_line_pointer;
+
+ c = get_symbol_end ();
+
+ /* Reseting prefix? */
+ if (*p == ':' && p[1] == 0)
+ mmix_current_prefix = NULL;
+ else
+ {
+ /* Put this prefix on the mmix symbols obstack. We could malloc and
+ free it separately, but then we'd have to worry about that.
+ People using up memory on prefixes have other problems. */
+ obstack_grow (&mmix_sym_obstack, p, strlen (p) + 1);
+ p = obstack_finish (&mmix_sym_obstack);
+
+ /* Accumulate prefixes, and strip a leading ':'. */
+ if (mmix_current_prefix != NULL || *p == ':')
+ p = mmix_prefix_name (p);
+
+ mmix_current_prefix = p;
+ }
+
+ *input_line_pointer = c;
+
+ mmix_handle_rest_of_empty_line ();
+}
+
+/* We implement prefixes by using the tc_canonicalize_symbol_name hook,
+ and store each prefixed name on a (separate) obstack. This means that
+ the name is on the "notes" obstack in non-prefixed form and on the
+ mmix_sym_obstack in prefixed form, but currently it is not worth
+ rewriting the whole GAS symbol handling to improve "hooking" to avoid
+ that. (It might be worth a rewrite for other reasons, though). */
+
+char *
+mmix_prefix_name (char *shortname)
+{
+ if (*shortname == ':')
+ return shortname + 1;
+
+ if (mmix_current_prefix == NULL)
+ as_fatal (_("internal: mmix_prefix_name but empty prefix"));
+
+ if (*shortname == '$')
+ return shortname;
+
+ obstack_grow (&mmix_sym_obstack, mmix_current_prefix,
+ strlen (mmix_current_prefix));
+ obstack_grow (&mmix_sym_obstack, shortname, strlen (shortname) + 1);
+ return obstack_finish (&mmix_sym_obstack);
+}
+
+/* The GREG pseudo. At LABEL, we have the name of a symbol that we
+ want to make a register symbol, and which should be initialized with
+ the value in the expression at INPUT_LINE_POINTER (defaulting to 0).
+ Either and (perhaps less meaningful) both may be missing. LABEL must
+ be persistent, perhaps allocated on an obstack. */
+
+static void
+mmix_greg_internal (char *label)
+{
+ expressionS *expP = &mmix_raw_gregs[n_of_raw_gregs].exp;
+ segT section;
+
+ /* Don't set the section to register contents section before the
+ expression has been parsed; it may refer to the current position. */
+ section = expression (expP);
+
+ /* FIXME: Check that no expression refers to the register contents
+ section. May need to be done in elf64-mmix.c. */
+ if (expP->X_op == O_absent)
+ {
+ /* Default to zero if the expression was absent. */
+ expP->X_op = O_constant;
+ expP->X_add_number = 0;
+ expP->X_unsigned = 0;
+ expP->X_add_symbol = NULL;
+ expP->X_op_symbol = NULL;
+ }
+
+ if (section == undefined_section)
+ {
+ /* This is an error or a LOC with an expression involving
+ forward references. For the expression to be correctly
+ evaluated, we need to force a proper symbol; gas loses track
+ of the segment for "local symbols". */
+ if (expP->X_op == O_add)
+ {
+ symbol_get_value_expression (expP->X_op_symbol);
+ symbol_get_value_expression (expP->X_add_symbol);
+ }
+ else
+ {
+ gas_assert (expP->X_op == O_symbol);
+ symbol_get_value_expression (expP->X_add_symbol);
+ }
+ }
+
+ /* We must handle prefixes here, as we save the labels and expressions
+ to be output later. */
+ mmix_raw_gregs[n_of_raw_gregs].label
+ = mmix_current_prefix == NULL ? label : mmix_prefix_name (label);
+
+ if (n_of_raw_gregs == MAX_GREGS - 1)
+ as_bad (_("too many GREG registers allocated (max %d)"), MAX_GREGS);
+ else
+ n_of_raw_gregs++;
+
+ mmix_handle_rest_of_empty_line ();
+}
+
+/* The ".greg label,expr" worker. */
+
+static void
+s_greg (int unused ATTRIBUTE_UNUSED)
+{
+ char *p;
+ char c;
+ p = input_line_pointer;
+
+ /* This will skip over what can be a symbol and zero out the next
+ character, which we assume is a ',' or other meaningful delimiter.
+ What comes after that is the initializer expression for the
+ register. */
+ c = get_symbol_end ();
+
+ if (! is_end_of_line[(unsigned char) c])
+ input_line_pointer++;
+
+ if (*p)
+ {
+ /* The label must be persistent; it's not used until after all input
+ has been seen. */
+ obstack_grow (&mmix_sym_obstack, p, strlen (p) + 1);
+ mmix_greg_internal (obstack_finish (&mmix_sym_obstack));
+ }
+ else
+ mmix_greg_internal (NULL);
+}
+
+/* The "BSPEC expr" worker. */
+
+static void
+s_bspec (int unused ATTRIBUTE_UNUSED)
+{
+ asection *expsec;
+ asection *sec;
+ char secname[sizeof (MMIX_OTHER_SPEC_SECTION_PREFIX) + 20]
+ = MMIX_OTHER_SPEC_SECTION_PREFIX;
+ expressionS exp;
+ int n;
+
+ /* Get a constant expression which we can evaluate *now*. Supporting
+ more complex (though assembly-time computable) expressions is
+ feasible but Too Much Work for something of unknown usefulness like
+ BSPEC-ESPEC. */
+ expsec = expression (&exp);
+ mmix_handle_rest_of_empty_line ();
+
+ /* Check that we don't have another BSPEC in progress. */
+ if (doing_bspec)
+ {
+ as_bad (_("BSPEC already active. Nesting is not supported."));
+ return;
+ }
+
+ if (exp.X_op != O_constant
+ || expsec != absolute_section
+ || exp.X_add_number < 0
+ || exp.X_add_number > 65535)
+ {
+ as_bad (_("invalid BSPEC expression"));
+ exp.X_add_number = 0;
+ }
+
+ n = (int) exp.X_add_number;
+
+ sprintf (secname + strlen (MMIX_OTHER_SPEC_SECTION_PREFIX), "%d", n);
+ sec = bfd_get_section_by_name (stdoutput, secname);
+ if (sec == NULL)
+ {
+ /* We need a non-volatile name as it will be stored in the section
+ struct. */
+ char *newsecname = xstrdup (secname);
+ sec = bfd_make_section (stdoutput, newsecname);
+
+ if (sec == NULL)
+ as_fatal (_("can't create section %s"), newsecname);
+
+ if (!bfd_set_section_flags (stdoutput, sec,
+ bfd_get_section_flags (stdoutput, sec)
+ | SEC_READONLY))
+ as_fatal (_("can't set section flags for section %s"), newsecname);
+ }
+
+ /* Tell ELF about the pending section change. */
+ obj_elf_section_change_hook ();
+ subseg_set (sec, 0);
+
+ /* Save position for missing ESPEC. */
+ as_where (&bspec_file, &bspec_line);
+
+ doing_bspec = 1;
+}
+
+/* The "ESPEC" worker. */
+
+static void
+s_espec (int unused ATTRIBUTE_UNUSED)
+{
+ /* First, check that we *do* have a BSPEC in progress. */
+ if (! doing_bspec)
+ {
+ as_bad (_("ESPEC without preceding BSPEC"));
+ return;
+ }
+
+ mmix_handle_rest_of_empty_line ();
+ doing_bspec = 0;
+
+ /* When we told ELF about the section change in s_bspec, it stored the
+ previous section for us so we can get at it with the equivalent of a
+ .previous pseudo. */
+ obj_elf_previous (0);
+}
+
+/* The " .local expr" and " local expr" worker. We make a BFD_MMIX_LOCAL
+ relocation against the current position against the expression.
+ Implementing this by means of contents in a section lost. */
+
+static void
+mmix_s_local (int unused ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ /* Don't set the section to register contents section before the
+ expression has been parsed; it may refer to the current position in
+ some contorted way. */
+ expression (&exp);
+
+ if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing local expression"));
+ return;
+ }
+ else if (exp.X_op == O_register)
+ {
+ /* fix_new_exp doesn't like O_register. Should be configurable.
+ We're fine with a constant here, though. */
+ exp.X_op = O_constant;
+ }
+
+ fix_new_exp (frag_now, 0, 0, &exp, 0, BFD_RELOC_MMIX_LOCAL);
+ mmix_handle_rest_of_empty_line ();
+}
+
+/* Set fragP->fr_var to the initial guess of the size of a relaxable insn
+ and return it. Sizes of other instructions are not known. This
+ function may be called multiple times. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment)
+{
+ int length;
+
+#define HANDLE_RELAXABLE(state) \
+ case ENCODE_RELAX (state, STATE_UNDF): \
+ if (fragP->fr_symbol != NULL \
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment \
+ && !S_IS_WEAK (fragP->fr_symbol)) \
+ { \
+ /* The symbol lies in the same segment - a relaxable case. */ \
+ fragP->fr_subtype \
+ = ENCODE_RELAX (state, STATE_ZERO); \
+ } \
+ break;
+
+ switch (fragP->fr_subtype)
+ {
+ HANDLE_RELAXABLE (STATE_GETA);
+ HANDLE_RELAXABLE (STATE_BCC);
+ HANDLE_RELAXABLE (STATE_JMP);
+
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_UNDF):
+ if (fragP->fr_symbol != NULL
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment
+ && !S_IS_WEAK (fragP->fr_symbol))
+ /* The symbol lies in the same segment - a relaxable case. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO);
+ else if (pushj_stubs)
+ /* If we're to generate stubs, assume we can reach a stub after
+ the section. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
+ /* FALLTHROUGH. */
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+ case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+ /* We need to distinguish different relaxation rounds. */
+ seg_info (segment)->tc_segment_info_data.last_stubfrag = fragP;
+ break;
+
+ case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
+ case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
+ case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
+ /* When relaxing a section for the second time, we don't need to do
+ anything except making sure that fr_var is set right. */
+ break;
+
+ case STATE_GREG_DEF:
+ length = fragP->tc_frag_data != NULL ? 0 : 8;
+ fragP->fr_var = length;
+
+ /* Don't consult the relax_table; it isn't valid for this
+ relaxation. */
+ return length;
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ }
+
+ length = mmix_relax_table[fragP->fr_subtype].rlx_length;
+ fragP->fr_var = length;
+
+ return length;
+}
+
+/* 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. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ if (type == 'r')
+ type = 'f';
+ /* FIXME: Having 'f' in mmix_flt_chars (and here) makes it
+ problematic to also have a forward reference in an expression.
+ The testsuite wants it, and it's customary.
+ We'll deal with the real problems when they come; we share the
+ problem with most other ports. */
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+/* Convert variable-sized frags into one or more fixups. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ /* Pointer to first byte in variable-sized part of the frag. */
+ char *var_partp;
+
+ /* Pointer to first opcode byte in frag. */
+ char *opcodep;
+
+ /* Size in bytes of variable-sized part of frag. */
+ int var_part_size = 0;
+
+ /* This is part of *fragP. It contains all information about addresses
+ and offsets to varying parts. */
+ symbolS *symbolP;
+ unsigned long var_part_offset;
+
+ /* This is the frag for the opcode. It, rather than fragP, must be used
+ when emitting a frag for the opcode. */
+ fragS *opc_fragP = fragP->tc_frag_data;
+ fixS *tmpfixP;
+
+ /* Where, in file space, does addr point? */
+ bfd_vma target_address;
+ bfd_vma opcode_address;
+
+ know (fragP->fr_type == rs_machine_dependent);
+
+ 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);
+
+ /* The opcode that would be extended is the last four "fixed" bytes. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - 4;
+
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+ /* Setting the unknown bits to 0 seems the most appropriate. */
+ mmix_set_geta_branch_offset (opcodep, 0);
+ tmpfixP = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 8,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_MMIX_PUSHJ_STUBBABLE);
+ COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+ var_part_size = 0;
+ break;
+
+ case ENCODE_RELAX (STATE_GETA, STATE_ZERO):
+ case ENCODE_RELAX (STATE_BCC, STATE_ZERO):
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+ mmix_set_geta_branch_offset (opcodep, target_address - opcode_address);
+ if (linkrelax)
+ {
+ tmpfixP
+ = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_MMIX_ADDR19);
+ COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+ }
+ var_part_size = 0;
+ break;
+
+ case ENCODE_RELAX (STATE_JMP, STATE_ZERO):
+ mmix_set_jmp_offset (opcodep, target_address - opcode_address);
+ if (linkrelax)
+ {
+ tmpfixP
+ = fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_MMIX_ADDR27);
+ COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+ }
+ var_part_size = 0;
+ break;
+
+ case STATE_GREG_DEF:
+ if (fragP->tc_frag_data == NULL)
+ {
+ /* We must initialize data that's supposed to be "fixed up" to
+ avoid emitting garbage, because md_apply_fix won't do
+ anything for undefined symbols. */
+ md_number_to_chars (var_partp, 0, 8);
+ tmpfixP
+ = fix_new (fragP, var_partp - fragP->fr_literal, 8,
+ fragP->fr_symbol, fragP->fr_offset, 0, BFD_RELOC_64);
+ COPY_FR_WHERE_TO_FX (fragP, tmpfixP);
+ mmix_gregs[n_of_cooked_gregs++] = tmpfixP;
+ var_part_size = 8;
+ }
+ else
+ var_part_size = 0;
+ break;
+
+#define HANDLE_MAX_RELOC(state, reloc) \
+ case ENCODE_RELAX (state, STATE_MAX): \
+ var_part_size \
+ = mmix_relax_table[ENCODE_RELAX (state, STATE_MAX)].rlx_length; \
+ mmix_fill_nops (var_partp, var_part_size / 4); \
+ if (warn_on_expansion) \
+ as_warn_where (fragP->fr_file, fragP->fr_line, \
+ _("operand out of range, instruction expanded")); \
+ tmpfixP = fix_new (fragP, var_partp - fragP->fr_literal - 4, 8, \
+ fragP->fr_symbol, fragP->fr_offset, 1, reloc); \
+ COPY_FR_WHERE_TO_FX (fragP, tmpfixP); \
+ break
+
+ HANDLE_MAX_RELOC (STATE_GETA, BFD_RELOC_MMIX_GETA);
+ HANDLE_MAX_RELOC (STATE_BCC, BFD_RELOC_MMIX_CBRANCH);
+ HANDLE_MAX_RELOC (STATE_PUSHJ, BFD_RELOC_MMIX_PUSHJ);
+ HANDLE_MAX_RELOC (STATE_JMP, BFD_RELOC_MMIX_JMP);
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+
+ fragP->fr_fix += var_part_size;
+ fragP->fr_var = 0;
+}
+
+/* Applies the desired value to the specified location.
+ Also sets up addends for RELA type relocations.
+ Stolen from tc-mcore.c.
+
+ Note that this function isn't called when linkrelax != 0. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT segment)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ /* Note: use offsetT because it is signed, valueT is unsigned. */
+ offsetT val = (offsetT) * valP;
+ segT symsec
+ = (fixP->fx_addsy == NULL
+ ? absolute_section : S_GET_SEGMENT (fixP->fx_addsy));
+
+ /* If the fix is relative to a symbol which is not defined, or, (if
+ pcrel), not in the same segment as the fix, we cannot resolve it
+ here. */
+ if (fixP->fx_addsy != NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)
+ || (fixP->fx_pcrel && symsec != segment)
+ || (! fixP->fx_pcrel
+ && symsec != absolute_section
+ && ((fixP->fx_r_type != BFD_RELOC_MMIX_REG
+ && fixP->fx_r_type != BFD_RELOC_MMIX_REG_OR_BYTE)
+ || symsec != reg_section))))
+ {
+ fixP->fx_done = 0;
+ return;
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ /* These are never "fixed". */
+ fixP->fx_done = 0;
+ return;
+ }
+ else
+ /* We assume every other relocation is "fixed". */
+ fixP->fx_done = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_64:
+ case BFD_RELOC_32:
+ case BFD_RELOC_24:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_24_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_8_PCREL:
+ md_number_to_chars (buf, val, fixP->fx_size);
+ break;
+
+ case BFD_RELOC_MMIX_ADDR19:
+ if (expand_op)
+ {
+ /* This shouldn't happen. */
+ BAD_CASE (fixP->fx_r_type);
+ break;
+ }
+ /* FALLTHROUGH. */
+ case BFD_RELOC_MMIX_GETA:
+ case BFD_RELOC_MMIX_CBRANCH:
+ case BFD_RELOC_MMIX_PUSHJ:
+ case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
+ /* If this fixup is out of range, punt to the linker to emit an
+ error. This should only happen with -no-expand. */
+ if (val < -(((offsetT) 1 << 19)/2)
+ || val >= ((offsetT) 1 << 19)/2 - 1
+ || (val & 3) != 0)
+ {
+ if (warn_on_expansion)
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range"));
+ fixP->fx_done = 0;
+ val = 0;
+ }
+ mmix_set_geta_branch_offset (buf, val);
+ break;
+
+ case BFD_RELOC_MMIX_ADDR27:
+ if (expand_op)
+ {
+ /* This shouldn't happen. */
+ BAD_CASE (fixP->fx_r_type);
+ break;
+ }
+ /* FALLTHROUGH. */
+ case BFD_RELOC_MMIX_JMP:
+ /* If this fixup is out of range, punt to the linker to emit an
+ error. This should only happen with -no-expand. */
+ if (val < -(((offsetT) 1 << 27)/2)
+ || val >= ((offsetT) 1 << 27)/2 - 1
+ || (val & 3) != 0)
+ {
+ if (warn_on_expansion)
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("operand out of range"));
+ fixP->fx_done = 0;
+ val = 0;
+ }
+ mmix_set_jmp_offset (buf, val);
+ break;
+
+ case BFD_RELOC_MMIX_REG_OR_BYTE:
+ if (fixP->fx_addsy != NULL
+ && (S_GET_SEGMENT (fixP->fx_addsy) != reg_section
+ || S_GET_VALUE (fixP->fx_addsy) > 255)
+ && S_GET_SEGMENT (fixP->fx_addsy) != absolute_section)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid operands"));
+ /* We don't want this "symbol" appearing in output, because
+ that will fail. */
+ fixP->fx_done = 1;
+ }
+
+ buf[0] = val;
+
+ /* If this reloc is for a Z field, we need to adjust
+ the opcode if we got a constant here.
+ FIXME: Can we make this more robust? */
+
+ if ((fixP->fx_where & 3) == 3
+ && (fixP->fx_addsy == NULL
+ || S_GET_SEGMENT (fixP->fx_addsy) == absolute_section))
+ buf[-3] |= IMM_OFFSET_BIT;
+ break;
+
+ case BFD_RELOC_MMIX_REG:
+ if (fixP->fx_addsy == NULL
+ || S_GET_SEGMENT (fixP->fx_addsy) != reg_section
+ || S_GET_VALUE (fixP->fx_addsy) > 255)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid operands"));
+ fixP->fx_done = 1;
+ }
+
+ *buf = val;
+ break;
+
+ case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
+ /* These are never "fixed". */
+ fixP->fx_done = 0;
+ return;
+
+ case BFD_RELOC_MMIX_PUSHJ_1:
+ case BFD_RELOC_MMIX_PUSHJ_2:
+ case BFD_RELOC_MMIX_PUSHJ_3:
+ case BFD_RELOC_MMIX_CBRANCH_J:
+ case BFD_RELOC_MMIX_CBRANCH_1:
+ case BFD_RELOC_MMIX_CBRANCH_2:
+ case BFD_RELOC_MMIX_CBRANCH_3:
+ case BFD_RELOC_MMIX_GETA_1:
+ case BFD_RELOC_MMIX_GETA_2:
+ case BFD_RELOC_MMIX_GETA_3:
+ case BFD_RELOC_MMIX_JMP_1:
+ case BFD_RELOC_MMIX_JMP_2:
+ case BFD_RELOC_MMIX_JMP_3:
+ default:
+ BAD_CASE (fixP->fx_r_type);
+ break;
+ }
+
+ if (fixP->fx_done)
+ /* Make sure that for completed fixups we have the value around for
+ use by e.g. mmix_frob_file. */
+ fixP->fx_offset = val;
+}
+
+/* A bsearch function for looking up a value against offsets for GREG
+ definitions. */
+
+static int
+cmp_greg_val_greg_symbol_fixes (const void *p1, const void *p2)
+{
+ offsetT val1 = *(offsetT *) p1;
+ offsetT val2 = ((struct mmix_symbol_greg_fixes *) p2)->offs;
+
+ if (val1 >= val2 && val1 < val2 + 255)
+ return 0;
+
+ if (val1 > val2)
+ return 1;
+
+ return -1;
+}
+
+/* Generate a machine-dependent relocation. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
+{
+ bfd_signed_vma val
+ = fixP->fx_offset
+ + (fixP->fx_addsy != NULL
+ && !S_IS_WEAK (fixP->fx_addsy)
+ && !S_IS_COMMON (fixP->fx_addsy)
+ ? S_GET_VALUE (fixP->fx_addsy) : 0);
+ arelent *relP;
+ bfd_reloc_code_real_type code = BFD_RELOC_NONE;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ symbolS *addsy = fixP->fx_addsy;
+ asection *addsec = addsy == NULL ? NULL : S_GET_SEGMENT (addsy);
+ asymbol *baddsy = addsy != NULL ? symbol_get_bfdsym (addsy) : NULL;
+ bfd_vma addend
+ = val - (baddsy == NULL || S_IS_COMMON (addsy) || S_IS_WEAK (addsy)
+ ? 0 : bfd_asymbol_value (baddsy));
+
+ /* A single " LOCAL expression" in the wrong section will not work when
+ linking to MMO; relocations for zero-content sections are then
+ ignored. Normally, relocations would modify section contents, and
+ you'd never think or be able to do something like that. The
+ relocation resulting from a LOCAL directive doesn't have an obvious
+ and mandatory location. I can't figure out a way to do this better
+ than just helping the user around this limitation here; hopefully the
+ code using the local expression is around. Putting the LOCAL
+ semantics in a relocation still seems right; a section didn't do. */
+ if (bfd_section_size (section->owner, section) == 0)
+ as_bad_where
+ (fixP->fx_file, fixP->fx_line,
+ fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
+ /* The BFD_RELOC_MMIX_LOCAL-specific message is supposed to be
+ user-friendly, though a little bit non-substantial. */
+ ? _("directive LOCAL must be placed in code or data")
+ : _("internal confusion: relocation in a section without contents"));
+
+ /* FIXME: Range tests for all these. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_64:
+ case BFD_RELOC_32:
+ case BFD_RELOC_24:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ code = fixP->fx_r_type;
+
+ if (addsy == NULL || bfd_is_abs_section (addsec))
+ {
+ /* Resolve this reloc now, as md_apply_fix would have done (not
+ called if -linkrelax). There is no point in keeping a reloc
+ to an absolute symbol. No reloc that is subject to
+ relaxation must be to an absolute symbol; difference
+ involving symbols in a specific section must be signalled as
+ an error if the relaxing cannot be expressed; having a reloc
+ to the resolved (now absolute) value does not help. */
+ md_number_to_chars (buf, val, fixP->fx_size);
+ return NULL;
+ }
+ break;
+
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_24_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_MMIX_LOCAL:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_MMIX_GETA:
+ case BFD_RELOC_MMIX_GETA_1:
+ case BFD_RELOC_MMIX_GETA_2:
+ case BFD_RELOC_MMIX_GETA_3:
+ case BFD_RELOC_MMIX_CBRANCH:
+ case BFD_RELOC_MMIX_CBRANCH_J:
+ case BFD_RELOC_MMIX_CBRANCH_1:
+ case BFD_RELOC_MMIX_CBRANCH_2:
+ case BFD_RELOC_MMIX_CBRANCH_3:
+ case BFD_RELOC_MMIX_PUSHJ:
+ case BFD_RELOC_MMIX_PUSHJ_1:
+ case BFD_RELOC_MMIX_PUSHJ_2:
+ case BFD_RELOC_MMIX_PUSHJ_3:
+ case BFD_RELOC_MMIX_PUSHJ_STUBBABLE:
+ case BFD_RELOC_MMIX_JMP:
+ case BFD_RELOC_MMIX_JMP_1:
+ case BFD_RELOC_MMIX_JMP_2:
+ case BFD_RELOC_MMIX_JMP_3:
+ case BFD_RELOC_MMIX_ADDR19:
+ case BFD_RELOC_MMIX_ADDR27:
+ code = fixP->fx_r_type;
+ break;
+
+ case BFD_RELOC_MMIX_REG_OR_BYTE:
+ /* If we have this kind of relocation to an unknown symbol or to the
+ register contents section (that is, to a register), then we can't
+ resolve the relocation here. */
+ if (addsy != NULL
+ && (bfd_is_und_section (addsec)
+ || strcmp (bfd_get_section_name (addsec->owner, addsec),
+ MMIX_REG_CONTENTS_SECTION_NAME) == 0))
+ {
+ code = fixP->fx_r_type;
+ break;
+ }
+
+ /* If the relocation is not to the register section or to the
+ absolute section (a numeric value), then we have an error. */
+ if (addsy != NULL
+ && (S_GET_SEGMENT (addsy) != real_reg_section
+ || val > 255
+ || val < 0)
+ && ! bfd_is_abs_section (addsec))
+ goto badop;
+
+ /* Set the "immediate" bit of the insn if this relocation is to Z
+ field when the value is a numeric value, i.e. not a register. */
+ if ((fixP->fx_where & 3) == 3
+ && (addsy == NULL || bfd_is_abs_section (addsec)))
+ buf[-3] |= IMM_OFFSET_BIT;
+
+ buf[0] = val;
+ return NULL;
+
+ case BFD_RELOC_MMIX_BASE_PLUS_OFFSET:
+ if (addsy != NULL
+ && strcmp (bfd_get_section_name (addsec->owner, addsec),
+ MMIX_REG_CONTENTS_SECTION_NAME) == 0)
+ {
+ /* This changed into a register; the relocation is for the
+ register-contents section. The constant part remains zero. */
+ code = BFD_RELOC_MMIX_REG;
+ break;
+ }
+
+ /* If we've found out that this was indeed a register, then replace
+ with the register number. The constant part is already zero.
+
+ If we encounter any other defined symbol, then we must find a
+ suitable register and emit a reloc. */
+ if (addsy == NULL || addsec != real_reg_section)
+ {
+ struct mmix_symbol_gregs *gregs;
+ struct mmix_symbol_greg_fixes *fix;
+
+ if (S_IS_DEFINED (addsy)
+ && !bfd_is_com_section (addsec)
+ && !S_IS_WEAK (addsy))
+ {
+ if (! symbol_section_p (addsy) && ! bfd_is_abs_section (addsec))
+ as_fatal (_("internal: BFD_RELOC_MMIX_BASE_PLUS_OFFSET not resolved to section"));
+
+ /* If this is an absolute symbol sufficiently near
+ lowest_data_loc, then we canonicalize on the data
+ section. Note that val is signed here; we may subtract
+ lowest_data_loc which is unsigned. Careful with those
+ comparisons. */
+ if (lowest_data_loc != (bfd_vma) -1
+ && (bfd_vma) val + 256 > lowest_data_loc
+ && bfd_is_abs_section (addsec))
+ {
+ val -= (offsetT) lowest_data_loc;
+ addsy = section_symbol (data_section);
+ }
+ /* Likewise text section. */
+ else if (lowest_text_loc != (bfd_vma) -1
+ && (bfd_vma) val + 256 > lowest_text_loc
+ && bfd_is_abs_section (addsec))
+ {
+ val -= (offsetT) lowest_text_loc;
+ addsy = section_symbol (text_section);
+ }
+ }
+
+ gregs = *symbol_get_tc (addsy);
+
+ /* If that symbol does not have any associated GREG definitions,
+ we can't do anything. */
+ if (gregs == NULL
+ || (fix = bsearch (&val, gregs->greg_fixes, gregs->n_gregs,
+ sizeof (gregs->greg_fixes[0]),
+ cmp_greg_val_greg_symbol_fixes)) == NULL
+ /* The register must not point *after* the address we want. */
+ || fix->offs > val
+ /* Neither must the register point more than 255 bytes
+ before the address we want. */
+ || fix->offs + 255 < val)
+ {
+ /* We can either let the linker allocate GREGs
+ automatically, or emit an error. */
+ if (allocate_undefined_gregs_in_linker)
+ {
+ /* The values in baddsy and addend are right. */
+ code = fixP->fx_r_type;
+ break;
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("no suitable GREG definition for operands"));
+ return NULL;
+ }
+ else
+ {
+ /* Transform the base-plus-offset reloc for the actual area
+ to a reloc for the register with the address of the area.
+ Put addend for register in Z operand. */
+ buf[1] = val - fix->offs;
+ code = BFD_RELOC_MMIX_REG;
+ baddsy
+ = (bfd_get_section_by_name (stdoutput,
+ MMIX_REG_CONTENTS_SECTION_NAME)
+ ->symbol);
+
+ addend = fix->fix->fx_frag->fr_address + fix->fix->fx_where;
+ }
+ }
+ else if (S_GET_VALUE (addsy) > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid operands"));
+ else
+ {
+ *buf = val;
+ return NULL;
+ }
+ break;
+
+ case BFD_RELOC_MMIX_REG:
+ if (addsy != NULL
+ && (bfd_is_und_section (addsec)
+ || strcmp (bfd_get_section_name (addsec->owner, addsec),
+ MMIX_REG_CONTENTS_SECTION_NAME) == 0))
+ {
+ code = fixP->fx_r_type;
+ break;
+ }
+
+ if (addsy != NULL
+ && (addsec != real_reg_section
+ || val > 255
+ || val < 0)
+ && ! bfd_is_und_section (addsec))
+ /* Drop through to error message. */
+ ;
+ else
+ {
+ buf[0] = val;
+ return NULL;
+ }
+ /* FALLTHROUGH. */
+
+ /* The others are supposed to be handled by md_apply_fix.
+ FIXME: ... which isn't called when -linkrelax. Move over
+ md_apply_fix code here for everything reasonable. */
+ badop:
+ default:
+ as_bad_where
+ (fixP->fx_file, fixP->fx_line,
+ _("operands were not reducible at assembly-time"));
+
+ /* Unmark this symbol as used in a reloc, so we don't bump into a BFD
+ assert when trying to output reg_section. FIXME: A gas bug. */
+ fixP->fx_addsy = NULL;
+ return NULL;
+ }
+
+ relP = (arelent *) xmalloc (sizeof (arelent));
+ gas_assert (relP != 0);
+ relP->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *relP->sym_ptr_ptr = baddsy;
+ relP->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ relP->addend = addend;
+
+ /* If this had been a.out, we would have had a kludge for weak symbols
+ here. */
+
+ relP->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (! relP->howto)
+ {
+ const char *name;
+
+ name = S_GET_NAME (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;
+}
+
+/* Do some reformatting of a line. FIXME: We could transform a mmixal
+ line into traditional (GNU?) format, unless #NO_APP, and get rid of all
+ ugly labels_without_colons etc. */
+
+void
+mmix_handle_mmixal (void)
+{
+ char *insn;
+ char *s = input_line_pointer;
+ char *label = NULL;
+ char c;
+
+ if (pending_label != NULL)
+ as_fatal (_("internal: unhandled label %s"), pending_label);
+
+ if (mmix_gnu_syntax)
+ return;
+
+ /* If we're on a line with a label, check if it's a mmixal fb-label.
+ Save an indicator and skip the label; it must be set only after all
+ fb-labels of expressions are evaluated. */
+ if (ISDIGIT (s[0]) && s[1] == 'H' && ISSPACE (s[2]))
+ {
+ current_fb_label = s[0] - '0';
+
+ /* We have to skip the label, but also preserve the newlineness of
+ the previous character, since the caller checks that. It's a
+ mess we blame on the caller. */
+ s[1] = s[-1];
+ s += 2;
+ input_line_pointer = s;
+
+ while (*s && ISSPACE (*s) && ! is_end_of_line[(unsigned int) *s])
+ s++;
+
+ /* For errors emitted here, the book-keeping is off by one; the
+ caller is about to bump the counters. Adjust the error messages. */
+ if (is_end_of_line[(unsigned int) *s])
+ {
+ char *name;
+ unsigned int line;
+ as_where (&name, &line);
+ as_bad_where (name, line + 1,
+ _("[0-9]H labels may not appear alone on a line"));
+ current_fb_label = -1;
+ }
+ if (*s == '.')
+ {
+ char *name;
+ unsigned int line;
+ as_where (&name, &line);
+ as_bad_where (name, line + 1,
+ _("[0-9]H labels do not mix with dot-pseudos"));
+ current_fb_label = -1;
+ }
+
+ /* Back off to the last space before the opcode so we don't handle
+ the opcode as a label. */
+ s--;
+ }
+ else
+ current_fb_label = -1;
+
+ if (*s == '.')
+ {
+ /* If the first character is a '.', then it's a pseudodirective, not a
+ label. Make GAS not handle label-without-colon on this line. We
+ also don't do mmixal-specific stuff on this line. */
+ label_without_colon_this_line = 0;
+ return;
+ }
+
+ if (*s == 0 || is_end_of_line[(unsigned int) *s])
+ /* We avoid handling empty lines here. */
+ return;
+
+ if (is_name_beginner (*s))
+ label = s;
+
+ /* If there is a label, skip over it. */
+ while (*s && is_part_of_name (*s))
+ s++;
+
+ /* Find the start of the instruction or pseudo following the label,
+ if there is one. */
+ for (insn = s;
+ *insn && ISSPACE (*insn) && ! is_end_of_line[(unsigned int) *insn];
+ insn++)
+ /* Empty */
+ ;
+
+ /* Remove a trailing ":" off labels, as they'd otherwise be considered
+ part of the name. But don't do this for local labels. */
+ if (s != input_line_pointer && s[-1] == ':'
+ && (s - 2 != input_line_pointer
+ || ! ISDIGIT (s[-2])))
+ s[-1] = ' ';
+ else if (label != NULL
+ /* For a lone label on a line, we don't attach it to the next
+ instruction or MMIXAL-pseudo (getting its alignment). Thus
+ is acts like a "normal" :-ended label. Ditto if it's
+ followed by a non-MMIXAL pseudo. */
+ && !is_end_of_line[(unsigned int) *insn]
+ && *insn != '.')
+ {
+ /* For labels that don't end in ":", we save it so we can later give
+ it the same alignment and address as the associated instruction. */
+
+ /* Make room for the label including the ending nul. */
+ size_t len_0 = s - label + 1;
+
+ /* Save this label on the MMIX symbol obstack. Saving it on an
+ obstack is needless for "IS"-pseudos, but it's harmless and we
+ avoid a little code-cluttering. */
+ obstack_grow (&mmix_sym_obstack, label, len_0);
+ pending_label = obstack_finish (&mmix_sym_obstack);
+ pending_label[len_0 - 1] = 0;
+ }
+
+ /* If we have a non-MMIXAL pseudo, we have not business with the rest of
+ the line. */
+ if (*insn == '.')
+ return;
+
+ /* Find local labels of operands. Look for "[0-9][FB]" where the
+ characters before and after are not part of words. Break if a single
+ or double quote is seen anywhere. It means we can't have local
+ labels as part of list with mixed quoted and unquoted members for
+ mmixal compatibility but we can't have it all. For the moment.
+ Replace the '<N>B' or '<N>F' with MAGIC_FB_BACKWARD_CHAR<N> and
+ MAGIC_FB_FORWARD_CHAR<N> respectively. */
+
+ /* First make sure we don't have any of the magic characters on the line
+ appearing as input. */
+ while (*s)
+ {
+ c = *s++;
+ if (is_end_of_line[(unsigned int) c])
+ break;
+ if (c == MAGIC_FB_BACKWARD_CHAR || c == MAGIC_FB_FORWARD_CHAR)
+ as_bad (_("invalid characters in input"));
+ }
+
+ /* Scan again, this time looking for ';' after operands. */
+ s = insn;
+
+ /* Skip the insn. */
+ while (*s
+ && ! ISSPACE (*s)
+ && *s != ';'
+ && ! is_end_of_line[(unsigned int) *s])
+ s++;
+
+ /* Skip the spaces after the insn. */
+ while (*s
+ && ISSPACE (*s)
+ && *s != ';'
+ && ! is_end_of_line[(unsigned int) *s])
+ s++;
+
+ /* Skip the operands. While doing this, replace [0-9][BF] with
+ (MAGIC_FB_BACKWARD_CHAR|MAGIC_FB_FORWARD_CHAR)[0-9]. */
+ while ((c = *s) != 0
+ && ! ISSPACE (c)
+ && c != ';'
+ && ! is_end_of_line[(unsigned int) c])
+ {
+ if (c == '"')
+ {
+ s++;
+
+ /* FIXME: Test-case for semi-colon in string. */
+ while (*s
+ && *s != '"'
+ && (! is_end_of_line[(unsigned int) *s] || *s == ';'))
+ s++;
+
+ if (*s == '"')
+ s++;
+ }
+ else if (ISDIGIT (c))
+ {
+ if ((s[1] != 'B' && s[1] != 'F')
+ || is_part_of_name (s[-1])
+ || is_part_of_name (s[2])
+ /* Don't treat e.g. #1F as a local-label reference. */
+ || (s != input_line_pointer && s[-1] == '#'))
+ s++;
+ else
+ {
+ s[0] = (s[1] == 'B'
+ ? MAGIC_FB_BACKWARD_CHAR : MAGIC_FB_FORWARD_CHAR);
+ s[1] = c;
+ }
+ }
+ else
+ s++;
+ }
+
+ /* Skip any spaces after the operands. */
+ while (*s
+ && ISSPACE (*s)
+ && *s != ';'
+ && !is_end_of_line[(unsigned int) *s])
+ s++;
+
+ /* If we're now looking at a semi-colon, then it's an end-of-line
+ delimiter. */
+ mmix_next_semicolon_is_eoln = (*s == ';');
+
+ /* Make IS into an EQU by replacing it with "= ". Only match upper-case
+ though; let lower-case be a syntax error. */
+ s = insn;
+ if (s[0] == 'I' && s[1] == 'S' && ISSPACE (s[2]))
+ {
+ *s = '=';
+ s[1] = ' ';
+
+ /* Since labels can start without ":", we have to handle "X IS 42"
+ in full here, or "X" will be parsed as a label to be set at ".". */
+ input_line_pointer = s;
+
+ /* Right after this function ends, line numbers will be bumped if
+ input_line_pointer[-1] = '\n'. We want accurate line numbers for
+ the equals call, so we bump them before the call, and make sure
+ they aren't bumped afterwards. */
+ bump_line_counters ();
+
+ /* A fb-label is valid as an IS-label. */
+ if (current_fb_label >= 0)
+ {
+ char *fb_name;
+
+ /* We need to save this name on our symbol obstack, since the
+ string we got in fb_label_name is volatile and will change
+ with every call to fb_label_name, like those resulting from
+ parsing the IS-operand. */
+ fb_name = fb_label_name (current_fb_label, 1);
+ obstack_grow (&mmix_sym_obstack, fb_name, strlen (fb_name) + 1);
+ equals (obstack_finish (&mmix_sym_obstack), 0);
+ fb_label_instance_inc (current_fb_label);
+ current_fb_label = -1;
+ }
+ else
+ {
+ if (pending_label == NULL)
+ as_bad (_("empty label field for IS"));
+ else
+ equals (pending_label, 0);
+ pending_label = NULL;
+ }
+
+ /* For mmixal, we can have comments without a comment-start
+ character. */
+ mmix_handle_rest_of_empty_line ();
+ input_line_pointer--;
+
+ input_line_pointer[-1] = ' ';
+ }
+ else if (s[0] == 'G'
+ && s[1] == 'R'
+ && strncmp (s, "GREG", 4) == 0
+ && (ISSPACE (s[4]) || is_end_of_line[(unsigned char) s[4]]))
+ {
+ input_line_pointer = s + 4;
+
+ /* Right after this function ends, line numbers will be bumped if
+ input_line_pointer[-1] = '\n'. We want accurate line numbers for
+ the s_greg call, so we bump them before the call, and make sure
+ they aren't bumped afterwards. */
+ bump_line_counters ();
+
+ /* A fb-label is valid as a GREG-label. */
+ if (current_fb_label >= 0)
+ {
+ char *fb_name;
+
+ /* We need to save this name on our symbol obstack, since the
+ string we got in fb_label_name is volatile and will change
+ with every call to fb_label_name, like those resulting from
+ parsing the IS-operand. */
+ fb_name = fb_label_name (current_fb_label, 1);
+
+ /* Make sure we save the canonical name and don't get bitten by
+ prefixes. */
+ obstack_1grow (&mmix_sym_obstack, ':');
+ obstack_grow (&mmix_sym_obstack, fb_name, strlen (fb_name) + 1);
+ mmix_greg_internal (obstack_finish (&mmix_sym_obstack));
+ fb_label_instance_inc (current_fb_label);
+ current_fb_label = -1;
+ }
+ else
+ mmix_greg_internal (pending_label);
+
+ /* Back up before the end-of-line marker that was skipped in
+ mmix_greg_internal. */
+ input_line_pointer--;
+ input_line_pointer[-1] = ' ';
+
+ pending_label = NULL;
+ }
+ else if (pending_label != NULL)
+ {
+ input_line_pointer += strlen (pending_label);
+
+ /* See comment above about getting line numbers bumped. */
+ input_line_pointer[-1] = '\n';
+ }
+}
+
+/* Give the value of an fb-label rewritten as in mmix_handle_mmixal, when
+ parsing an expression.
+
+ On valid calls, input_line_pointer points at a MAGIC_FB_BACKWARD_CHAR
+ or MAGIC_FB_BACKWARD_CHAR, followed by an ascii digit for the label.
+ We fill in the label as an expression. */
+
+void
+mmix_fb_label (expressionS *expP)
+{
+ symbolS *sym;
+ char *fb_internal_name;
+
+ /* This doesn't happen when not using mmixal syntax. */
+ if (mmix_gnu_syntax
+ || (input_line_pointer[0] != MAGIC_FB_BACKWARD_CHAR
+ && input_line_pointer[0] != MAGIC_FB_FORWARD_CHAR))
+ return;
+
+ /* The current backward reference has augmentation 0. A forward
+ reference has augmentation 1, unless it's the same as a fb-label on
+ _this_ line, in which case we add one more so we don't refer to it.
+ This is the semantics of mmixal; it differs to that of common
+ fb-labels which refer to a here-label on the current line as a
+ backward reference. */
+ fb_internal_name
+ = fb_label_name (input_line_pointer[1] - '0',
+ (input_line_pointer[0] == MAGIC_FB_FORWARD_CHAR ? 1 : 0)
+ + ((input_line_pointer[1] - '0' == current_fb_label
+ && input_line_pointer[0] == MAGIC_FB_FORWARD_CHAR)
+ ? 1 : 0));
+
+ input_line_pointer += 2;
+ sym = symbol_find_or_make (fb_internal_name);
+
+ /* We don't have to clean up unrelated fields here; we just do what the
+ expr machinery does, but *not* just what it does for [0-9][fb], since
+ we need to treat those as ordinary symbols sometimes; see testcases
+ err-byte2.s and fb-2.s. */
+ if (S_GET_SEGMENT (sym) == absolute_section)
+ {
+ expP->X_op = O_constant;
+ expP->X_add_number = S_GET_VALUE (sym);
+ }
+ else
+ {
+ expP->X_op = O_symbol;
+ expP->X_add_symbol = sym;
+ expP->X_add_number = 0;
+ }
+}
+
+/* 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
+mmix_force_relocation (fixS *fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_MMIX_LOCAL
+ || fixP->fx_r_type == BFD_RELOC_MMIX_BASE_PLUS_OFFSET)
+ return 1;
+
+ if (linkrelax)
+ return 1;
+
+ /* All our pcrel relocations are must-keep. Note that md_apply_fix is
+ called *after* this, and will handle getting rid of the presumed
+ reloc; a relocation isn't *forced* other than to be handled by
+ md_apply_fix (or tc_gen_reloc if linkrelax). */
+ if (fixP->fx_pcrel)
+ return 1;
+
+ return generic_force_reloc (fixP);
+}
+
+/* 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);
+}
+
+/* Adjust the symbol table. We make reg_section relative to the real
+ register section. */
+
+void
+mmix_adjust_symtab (void)
+{
+ symbolS *sym;
+ symbolS *regsec = section_symbol (reg_section);
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ if (S_GET_SEGMENT (sym) == reg_section)
+ {
+ if (sym == regsec)
+ {
+ if (S_IS_EXTERNAL (sym) || symbol_used_in_reloc_p (sym))
+ abort ();
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ }
+ else
+ /* Change section to the *real* register section, so it gets
+ proper treatment when writing it out. Only do this for
+ global symbols. This also means we don't have to check for
+ $0..$255. */
+ S_SET_SEGMENT (sym, real_reg_section);
+ }
+}
+
+/* This is the expansion of LABELS_WITHOUT_COLONS.
+ We let md_start_line_hook tweak label_without_colon_this_line, and then
+ this function returns the tweaked value, and sets it to 1 for the next
+ line. FIXME: Very, very brittle. Not sure it works the way I
+ thought at the time I first wrote this. */
+
+int
+mmix_label_without_colon_this_line (void)
+{
+ int retval = label_without_colon_this_line;
+
+ if (! mmix_gnu_syntax)
+ label_without_colon_this_line = 1;
+
+ return retval;
+}
+
+/* This is the expansion of md_relax_frag. We go through the ordinary
+ relax table function except when the frag is for a GREG. Then we have
+ to check whether there's another GREG by the same value that we can
+ join with. */
+
+long
+mmix_md_relax_frag (segT seg, fragS *fragP, long stretch)
+{
+ switch (fragP->fr_subtype)
+ {
+ /* Growth for this type has been handled by mmix_md_end and
+ correctly estimated, so there's nothing more to do here. */
+ case STATE_GREG_DEF:
+ return 0;
+
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_ZERO):
+ {
+ /* We need to handle relaxation type ourselves, since relax_frag
+ doesn't update fr_subtype if there's no size increase in the
+ current section; when going from plain PUSHJ to a stub. This
+ is otherwise functionally the same as relax_frag in write.c,
+ simplified for this case. */
+ offsetT aim;
+ addressT target;
+ addressT address;
+ symbolS *symbolP;
+ target = fragP->fr_offset;
+ address = fragP->fr_address;
+ symbolP = fragP->fr_symbol;
+
+ if (symbolP)
+ {
+ fragS *sym_frag;
+
+ sym_frag = symbol_get_frag (symbolP);
+ know (S_GET_SEGMENT (symbolP) != absolute_section
+ || sym_frag == &zero_address_frag);
+ target += S_GET_VALUE (symbolP);
+
+ /* If frag has yet to be reached on this pass, assume it will
+ move by STRETCH just as we did. If this is not so, it will
+ be because some frag between grows, and that will force
+ another pass. */
+
+ if (stretch != 0
+ && sym_frag->relax_marker != fragP->relax_marker
+ && S_GET_SEGMENT (symbolP) == seg)
+ target += stretch;
+ }
+
+ aim = target - address - fragP->fr_fix;
+ if (aim >= PUSHJ_0B && aim <= PUSHJ_0F)
+ {
+ /* Target is reachable with a PUSHJ. */
+ segment_info_type *seginfo = seg_info (seg);
+
+ /* If we're at the end of a relaxation round, clear the stub
+ counter as initialization for the next round. */
+ if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+ seginfo->tc_segment_info_data.nstubs = 0;
+ return 0;
+ }
+
+ /* Not reachable. Try a stub. */
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO);
+ }
+ /* FALLTHROUGH. */
+
+ /* See if this PUSHJ is redirectable to a stub. */
+ case ENCODE_RELAX (STATE_PUSHJSTUB, STATE_ZERO):
+ {
+ segment_info_type *seginfo = seg_info (seg);
+ fragS *lastfrag = seginfo->frchainP->frch_last;
+ relax_substateT prev_type = fragP->fr_subtype;
+
+ /* The last frag is always an empty frag, so it suffices to look
+ at its address to know the ending address of this section. */
+ know (lastfrag->fr_type == rs_fill
+ && lastfrag->fr_fix == 0
+ && lastfrag->fr_var == 0);
+
+ /* For this PUSHJ to be relaxable into a call to a stub, the
+ distance must be no longer than 256k bytes from the PUSHJ to
+ the end of the section plus the maximum size of stubs so far. */
+ if ((lastfrag->fr_address
+ + stretch
+ + PUSHJ_MAX_LEN * seginfo->tc_segment_info_data.nstubs)
+ - (fragP->fr_address + fragP->fr_fix)
+ > GETA_0F
+ || !pushj_stubs)
+ fragP->fr_subtype = mmix_relax_table[prev_type].rlx_more;
+ else
+ seginfo->tc_segment_info_data.nstubs++;
+
+ /* If we're at the end of a relaxation round, clear the stub
+ counter as initialization for the next round. */
+ if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+ seginfo->tc_segment_info_data.nstubs = 0;
+
+ return
+ (mmix_relax_table[fragP->fr_subtype].rlx_length
+ - mmix_relax_table[prev_type].rlx_length);
+ }
+
+ case ENCODE_RELAX (STATE_PUSHJ, STATE_MAX):
+ {
+ segment_info_type *seginfo = seg_info (seg);
+
+ /* Need to cover all STATE_PUSHJ states to act on the last stub
+ frag (the end of this relax round; initialization for the
+ next). */
+ if (fragP == seginfo->tc_segment_info_data.last_stubfrag)
+ seginfo->tc_segment_info_data.nstubs = 0;
+
+ return 0;
+ }
+
+ default:
+ return relax_frag (seg, fragP, stretch);
+
+ case STATE_GREG_UNDF:
+ BAD_CASE (fragP->fr_subtype);
+ }
+
+ as_fatal (_("internal: unexpected relax type %d:%d"),
+ fragP->fr_type, fragP->fr_subtype);
+ return 0;
+}
+
+/* Various things we punt until all input is seen. */
+
+void
+mmix_md_end (void)
+{
+ fragS *fragP;
+ symbolS *mainsym;
+ asection *regsec;
+ struct loc_assert_s *loc_assert;
+ int i;
+
+ /* The first frag of GREG:s going into the register contents section. */
+ fragS *mmix_reg_contents_frags = NULL;
+
+ /* Reset prefix. All labels reachable at this point must be
+ canonicalized. */
+ mmix_current_prefix = NULL;
+
+ if (doing_bspec)
+ as_bad_where (bspec_file, bspec_line, _("BSPEC without ESPEC."));
+
+ /* Emit the low LOC setting of .text. */
+ if (text_has_contents && lowest_text_loc != (bfd_vma) -1)
+ {
+ symbolS *symbolP;
+ char locsymbol[sizeof (":") - 1
+ + sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1
+ + sizeof (".text")];
+
+ /* An exercise in non-ISO-C-ness, this one. */
+ sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
+ ".text");
+ symbolP
+ = symbol_new (locsymbol, absolute_section, lowest_text_loc,
+ &zero_address_frag);
+ S_SET_EXTERNAL (symbolP);
+ }
+
+ /* Ditto .data. */
+ if (data_has_contents && lowest_data_loc != (bfd_vma) -1)
+ {
+ symbolS *symbolP;
+ char locsymbol[sizeof (":") - 1
+ + sizeof (MMIX_LOC_SECTION_START_SYMBOL_PREFIX) - 1
+ + sizeof (".data")];
+
+ sprintf (locsymbol, ":%s%s", MMIX_LOC_SECTION_START_SYMBOL_PREFIX,
+ ".data");
+ symbolP
+ = symbol_new (locsymbol, absolute_section, lowest_data_loc,
+ &zero_address_frag);
+ S_SET_EXTERNAL (symbolP);
+ }
+
+ /* Unless GNU syntax mode, set "Main" to be a function, so the
+ disassembler doesn't get confused when we write truly
+ mmixal-compatible code (and don't use .type). Similarly set it
+ global (regardless of -globalize-symbols), so the linker sees it as
+ the start symbol in ELF mode. */
+ mainsym = symbol_find (MMIX_START_SYMBOL_NAME);
+ if (mainsym != NULL && ! mmix_gnu_syntax)
+ {
+ symbol_get_bfdsym (mainsym)->flags |= BSF_FUNCTION;
+ S_SET_EXTERNAL (mainsym);
+ }
+
+ /* Check that we didn't LOC into the unknown, or rather that when it
+ was unknown, we actually change sections. */
+ for (loc_assert = loc_asserts;
+ loc_assert != NULL;
+ loc_assert = loc_assert->next)
+ {
+ segT actual_seg;
+
+ resolve_symbol_value (loc_assert->loc_sym);
+ actual_seg = S_GET_SEGMENT (loc_assert->loc_sym);
+ if (actual_seg != loc_assert->old_seg)
+ {
+ char *fnam;
+ unsigned int line;
+ int e_valid = expr_symbol_where (loc_assert->loc_sym, &fnam, &line);
+
+ gas_assert (e_valid == 1);
+ as_bad_where (fnam, line,
+ _("LOC to section unknown or indeterminable "
+ "at first pass"));
+
+ /* Patch up the generic location data to avoid cascading
+ error messages from later passes. (See original in
+ write.c:relax_segment.) */
+ fragP = loc_assert->frag;
+ fragP->fr_type = rs_align;
+ fragP->fr_subtype = 0;
+ fragP->fr_offset = 0;
+ fragP->fr_fix = 0;
+ }
+ }
+
+ if (n_of_raw_gregs != 0)
+ {
+ /* Emit GREGs. They are collected in order of appearance, but must
+ be emitted in opposite order to both have section address regno*8
+ and the same allocation order (within a file) as mmixal. */
+ segT this_segment = now_seg;
+ subsegT this_subsegment = now_subseg;
+
+ regsec = bfd_make_section_old_way (stdoutput,
+ MMIX_REG_CONTENTS_SECTION_NAME);
+ subseg_set (regsec, 0);
+
+ /* Finally emit the initialization-value. Emit a variable frag, which
+ we'll fix in md_estimate_size_before_relax. We set the initializer
+ for the tc_frag_data field to NULL, so we can use that field for
+ relaxation purposes. */
+ mmix_opcode_frag = NULL;
+
+ frag_grow (0);
+ mmix_reg_contents_frags = frag_now;
+
+ for (i = n_of_raw_gregs - 1; i >= 0; i--)
+ {
+ if (mmix_raw_gregs[i].label != NULL)
+ /* There's a symbol. Let it refer to this location in the
+ register contents section. The symbol must be globalized
+ separately. */
+ colon (mmix_raw_gregs[i].label);
+
+ frag_var (rs_machine_dependent, 8, 0, STATE_GREG_UNDF,
+ make_expr_symbol (&mmix_raw_gregs[i].exp), 0, NULL);
+ }
+
+ subseg_set (this_segment, this_subsegment);
+ }
+
+ regsec = bfd_get_section_by_name (stdoutput, MMIX_REG_CONTENTS_SECTION_NAME);
+ /* Mark the section symbol as being OK for a reloc. */
+ if (regsec != NULL)
+ regsec->symbol->flags |= BSF_KEEP;
+
+ /* Iterate over frags resulting from GREGs and move those that evidently
+ have the same value together and point one to another.
+
+ This works in time O(N^2) but since the upper bound for non-error use
+ is 223, it's best to keep this simpler algorithm. */
+ for (fragP = mmix_reg_contents_frags; fragP != NULL; fragP = fragP->fr_next)
+ {
+ fragS **fpp;
+ fragS *fp = NULL;
+ fragS *osymfrag;
+ offsetT osymval;
+ expressionS *oexpP;
+ symbolS *symbolP = fragP->fr_symbol;
+
+ if (fragP->fr_type != rs_machine_dependent
+ || fragP->fr_subtype != STATE_GREG_UNDF)
+ continue;
+
+ /* Whatever the outcome, we will have this GREG judged merged or
+ non-merged. Since the tc_frag_data is NULL at this point, we
+ default to non-merged. */
+ fragP->fr_subtype = STATE_GREG_DEF;
+
+ /* If we're not supposed to merge GREG definitions, then just don't
+ look for equivalents. */
+ if (! merge_gregs)
+ continue;
+
+ osymval = (offsetT) S_GET_VALUE (symbolP);
+ osymfrag = symbol_get_frag (symbolP);
+
+ /* If the symbol isn't defined, we can't say that another symbol
+ equals this frag, then. FIXME: We can look at the "deepest"
+ defined name; if a = c and b = c then obviously a == b. */
+ if (! S_IS_DEFINED (symbolP))
+ continue;
+
+ oexpP = symbol_get_value_expression (fragP->fr_symbol);
+
+ /* If the initialization value is zero, then we must not merge them. */
+ if (oexpP->X_op == O_constant && osymval == 0)
+ continue;
+
+ /* Iterate through the frags downward this one. If we find one that
+ has the same non-zero value, move it to after this one and point
+ to it as the equivalent. */
+ for (fpp = &fragP->fr_next; *fpp != NULL; fpp = &fpp[0]->fr_next)
+ {
+ fp = *fpp;
+
+ if (fp->fr_type != rs_machine_dependent
+ || fp->fr_subtype != STATE_GREG_UNDF)
+ continue;
+
+ /* Calling S_GET_VALUE may simplify the symbol, changing from
+ expr_section etc. so call it first. */
+ if ((offsetT) S_GET_VALUE (fp->fr_symbol) == osymval
+ && symbol_get_frag (fp->fr_symbol) == osymfrag)
+ {
+ /* Move the frag links so the one we found equivalent comes
+ after the current one, carefully considering that
+ sometimes fpp == &fragP->fr_next and the moves must be a
+ NOP then. */
+ *fpp = fp->fr_next;
+ fp->fr_next = fragP->fr_next;
+ fragP->fr_next = fp;
+ break;
+ }
+ }
+
+ if (*fpp != NULL)
+ fragP->tc_frag_data = fp;
+ }
+}
+
+/* qsort function for mmix_symbol_gregs. */
+
+static int
+cmp_greg_symbol_fixes (const void *parg, const void *qarg)
+{
+ const struct mmix_symbol_greg_fixes *p
+ = (const struct mmix_symbol_greg_fixes *) parg;
+ const struct mmix_symbol_greg_fixes *q
+ = (const struct mmix_symbol_greg_fixes *) qarg;
+
+ return p->offs > q->offs ? 1 : p->offs < q->offs ? -1 : 0;
+}
+
+/* Collect GREG definitions from mmix_gregs and hang them as lists sorted
+ on increasing offsets onto each section symbol or undefined symbol.
+
+ Also, remove the register convenience section so it doesn't get output
+ as an ELF section. */
+
+void
+mmix_frob_file (void)
+{
+ int i;
+ struct mmix_symbol_gregs *all_greg_symbols[MAX_GREGS];
+ int n_greg_symbols = 0;
+
+ /* Collect all greg fixups and decorate each corresponding symbol with
+ the greg fixups for it. */
+ for (i = 0; i < n_of_cooked_gregs; i++)
+ {
+ offsetT offs;
+ symbolS *sym;
+ struct mmix_symbol_gregs *gregs;
+ fixS *fixP;
+
+ fixP = mmix_gregs[i];
+ know (fixP->fx_r_type == BFD_RELOC_64);
+
+ /* This case isn't doable in general anyway, methinks. */
+ if (fixP->fx_subsy != NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("GREG expression too complicated"));
+ continue;
+ }
+
+ sym = fixP->fx_addsy;
+ offs = (offsetT) fixP->fx_offset;
+
+ /* If the symbol is defined, then it must be resolved to a section
+ symbol at this time, or else we don't know how to handle it. */
+ if (S_IS_DEFINED (sym)
+ && !bfd_is_com_section (S_GET_SEGMENT (sym))
+ && !S_IS_WEAK (sym))
+ {
+ if (! symbol_section_p (sym)
+ && ! bfd_is_abs_section (S_GET_SEGMENT (sym)))
+ as_fatal (_("internal: GREG expression not resolved to section"));
+
+ offs += S_GET_VALUE (sym);
+ }
+
+ /* If this is an absolute symbol sufficiently near lowest_data_loc,
+ then we canonicalize on the data section. Note that offs is
+ signed here; we may subtract lowest_data_loc which is unsigned.
+ Careful with those comparisons. */
+ if (lowest_data_loc != (bfd_vma) -1
+ && (bfd_vma) offs + 256 > lowest_data_loc
+ && bfd_is_abs_section (S_GET_SEGMENT (sym)))
+ {
+ offs -= (offsetT) lowest_data_loc;
+ sym = section_symbol (data_section);
+ }
+ /* Likewise text section. */
+ else if (lowest_text_loc != (bfd_vma) -1
+ && (bfd_vma) offs + 256 > lowest_text_loc
+ && bfd_is_abs_section (S_GET_SEGMENT (sym)))
+ {
+ offs -= (offsetT) lowest_text_loc;
+ sym = section_symbol (text_section);
+ }
+
+ gregs = *symbol_get_tc (sym);
+
+ if (gregs == NULL)
+ {
+ gregs = xmalloc (sizeof (*gregs));
+ gregs->n_gregs = 0;
+ symbol_set_tc (sym, &gregs);
+ all_greg_symbols[n_greg_symbols++] = gregs;
+ }
+
+ gregs->greg_fixes[gregs->n_gregs].fix = fixP;
+ gregs->greg_fixes[gregs->n_gregs++].offs = offs;
+ }
+
+ /* For each symbol having a GREG definition, sort those definitions on
+ offset. */
+ for (i = 0; i < n_greg_symbols; i++)
+ qsort (all_greg_symbols[i]->greg_fixes, all_greg_symbols[i]->n_gregs,
+ sizeof (all_greg_symbols[i]->greg_fixes[0]), cmp_greg_symbol_fixes);
+
+ if (real_reg_section != NULL)
+ {
+ /* FIXME: Pass error state gracefully. */
+ if (bfd_get_section_flags (stdoutput, real_reg_section) & SEC_HAS_CONTENTS)
+ as_fatal (_("register section has contents\n"));
+
+ bfd_section_list_remove (stdoutput, real_reg_section);
+ --stdoutput->section_count;
+ }
+
+}
+
+/* Provide an expression for a built-in name provided when-used.
+ Either a symbol that is a handler; living in 0x10*[1..8] and having
+ name [DVWIOUZX]_Handler, or a mmixal built-in symbol.
+
+ If the name isn't a built-in name and parsed into *EXPP, return zero. */
+
+int
+mmix_parse_predefined_name (char *name, expressionS *expP)
+{
+ char *canon_name;
+ char *handler_charp;
+ const char handler_chars[] = "DVWIOUZX";
+ symbolS *symp;
+
+ if (! predefined_syms)
+ return 0;
+
+ canon_name = tc_canonicalize_symbol_name (name);
+
+ if (canon_name[1] == '_'
+ && strcmp (canon_name + 2, "Handler") == 0
+ && (handler_charp = strchr (handler_chars, *canon_name)) != NULL)
+ {
+ /* If the symbol doesn't exist, provide one relative to the .text
+ section.
+
+ FIXME: We should provide separate sections, mapped in the linker
+ script. */
+ symp = symbol_find (name);
+ if (symp == NULL)
+ symp = symbol_new (name, text_section,
+ 0x10 * (handler_charp + 1 - handler_chars),
+ &zero_address_frag);
+ }
+ else
+ {
+ /* These symbols appear when referenced; needed for
+ mmixal-compatible programs. */
+ unsigned int i;
+
+ static const struct
+ {
+ const char *name;
+ valueT val;
+ } predefined_abs_syms[] =
+ {
+ {"Data_Segment", (valueT) 0x20 << 56},
+ {"Pool_Segment", (valueT) 0x40 << 56},
+ {"Stack_Segment", (valueT) 0x60 << 56},
+ {"StdIn", 0},
+ {"StdOut", 1},
+ {"StdErr", 2},
+ {"TextRead", 0},
+ {"TextWrite", 1},
+ {"BinaryRead", 2},
+ {"BinaryWrite", 3},
+ {"BinaryReadWrite", 4},
+ {"Halt", 0},
+ {"Fopen", 1},
+ {"Fclose", 2},
+ {"Fread", 3},
+ {"Fgets", 4},
+ {"Fgetws", 5},
+ {"Fwrite", 6},
+ {"Fputs", 7},
+ {"Fputws", 8},
+ {"Fseek", 9},
+ {"Ftell", 10},
+ {"D_BIT", 0x80},
+ {"V_BIT", 0x40},
+ {"W_BIT", 0x20},
+ {"I_BIT", 0x10},
+ {"O_BIT", 0x08},
+ {"U_BIT", 0x04},
+ {"Z_BIT", 0x02},
+ {"X_BIT", 0x01},
+ {"Inf", 0x7ff00000}
+ };
+
+ /* If it's already in the symbol table, we shouldn't do anything. */
+ symp = symbol_find (name);
+ if (symp != NULL)
+ return 0;
+
+ for (i = 0;
+ i < sizeof (predefined_abs_syms) / sizeof (predefined_abs_syms[0]);
+ i++)
+ if (strcmp (canon_name, predefined_abs_syms[i].name) == 0)
+ {
+ symbol_table_insert (symbol_new (predefined_abs_syms[i].name,
+ absolute_section,
+ predefined_abs_syms[i].val,
+ &zero_address_frag));
+
+ /* Let gas find the symbol we just created, through its
+ ordinary lookup. */
+ return 0;
+ }
+
+ /* Not one of those symbols. Let gas handle it. */
+ return 0;
+ }
+
+ expP->X_op = O_symbol;
+ expP->X_add_number = 0;
+ expP->X_add_symbol = symp;
+ expP->X_op_symbol = NULL;
+
+ return 1;
+}
+
+/* Just check that we don't have a BSPEC/ESPEC pair active when changing
+ sections "normally", and get knowledge about alignment from the new
+ section. */
+
+void
+mmix_md_elf_section_change_hook (void)
+{
+ if (doing_bspec)
+ as_bad (_("section change from within a BSPEC/ESPEC pair is not supported"));
+
+ last_alignment = bfd_get_section_alignment (now_seg->owner, now_seg);
+ want_unaligned = 0;
+}
+
+/* The LOC worker. This is like s_org, but we have to support changing
+ section too. */
+
+static void
+s_loc (int ignore ATTRIBUTE_UNUSED)
+{
+ segT section;
+ expressionS exp;
+ char *p;
+ symbolS *sym;
+ offsetT off;
+
+ /* Must not have a BSPEC in progress. */
+ if (doing_bspec)
+ {
+ as_bad (_("directive LOC from within a BSPEC/ESPEC pair is not supported"));
+ return;
+ }
+
+ section = expression (&exp);
+
+ if (exp.X_op == O_illegal
+ || exp.X_op == O_absent
+ || exp.X_op == O_big)
+ {
+ as_bad (_("invalid LOC expression"));
+ return;
+ }
+
+ if (section == undefined_section)
+ {
+ /* This is an error or a LOC with an expression involving
+ forward references. For the expression to be correctly
+ evaluated, we need to force a proper symbol; gas loses track
+ of the segment for "local symbols". */
+ if (exp.X_op == O_add)
+ {
+ symbol_get_value_expression (exp.X_op_symbol);
+ symbol_get_value_expression (exp.X_add_symbol);
+ }
+ else
+ {
+ gas_assert (exp.X_op == O_symbol);
+ symbol_get_value_expression (exp.X_add_symbol);
+ }
+ }
+
+ if (section == absolute_section)
+ {
+ /* Translate a constant into a suitable section. */
+
+ if (exp.X_add_number < ((offsetT) 0x20 << 56))
+ {
+ /* Lower than Data_Segment or in the reserved area (the
+ segment number is >= 0x80, appearing negative) - assume
+ it's .text. */
+ section = text_section;
+
+ /* Save the lowest seen location, so we can pass on this
+ information to the linker. We don't actually org to this
+ location here, we just pass on information to the linker so
+ it can put the code there for us. */
+
+ /* If there was already a loc (that has to be set lower than
+ this one), we org at (this - lower). There's an implicit
+ "LOC 0" before any entered code. FIXME: handled by spurious
+ settings of text_has_contents. */
+ if (lowest_text_loc != (bfd_vma) -1
+ && (bfd_vma) exp.X_add_number < lowest_text_loc)
+ {
+ as_bad (_("LOC expression stepping backwards is not supported"));
+ exp.X_op = O_absent;
+ }
+ else
+ {
+ if (text_has_contents && lowest_text_loc == (bfd_vma) -1)
+ lowest_text_loc = 0;
+
+ if (lowest_text_loc == (bfd_vma) -1)
+ {
+ lowest_text_loc = exp.X_add_number;
+
+ /* We want only to change the section, not set an offset. */
+ exp.X_op = O_absent;
+ }
+ else
+ exp.X_add_number -= lowest_text_loc;
+ }
+ }
+ else
+ {
+ /* Do the same for the .data section, except we don't have
+ to worry about exp.X_add_number carrying a sign. */
+ section = data_section;
+
+ if (exp.X_add_number < (offsetT) lowest_data_loc)
+ {
+ as_bad (_("LOC expression stepping backwards is not supported"));
+ exp.X_op = O_absent;
+ }
+ else
+ {
+ if (data_has_contents && lowest_data_loc == (bfd_vma) -1)
+ lowest_data_loc = (bfd_vma) 0x20 << 56;
+
+ if (lowest_data_loc == (bfd_vma) -1)
+ {
+ lowest_data_loc = exp.X_add_number;
+
+ /* We want only to change the section, not set an offset. */
+ exp.X_op = O_absent;
+ }
+ else
+ exp.X_add_number -= lowest_data_loc;
+ }
+ }
+ }
+
+ /* If we can't deduce the section, it must be the current one.
+ Below, we arrange to assert this. */
+ if (section != now_seg && section != undefined_section)
+ {
+ obj_elf_section_change_hook ();
+ subseg_set (section, 0);
+
+ /* Call our section change hooks using the official hook. */
+ md_elf_section_change_hook ();
+ }
+
+ if (exp.X_op != O_absent)
+ {
+ symbolS *esym = NULL;
+
+ if (exp.X_op != O_constant && exp.X_op != O_symbol)
+ {
+ /* Handle complex expressions. */
+ esym = sym = make_expr_symbol (&exp);
+ off = 0;
+ }
+ else
+ {
+ sym = exp.X_add_symbol;
+ off = exp.X_add_number;
+
+ if (section == undefined_section)
+ {
+ /* We need an expr_symbol when tracking sections. In
+ order to make this an expr_symbol with file and line
+ tracked, we have to make the exp non-trivial; not an
+ O_symbol with .X_add_number == 0. The constant part
+ is unused. */
+ exp.X_add_number = 1;
+ esym = make_expr_symbol (&exp);
+ }
+ }
+
+ /* Track the LOC's where we couldn't deduce the section: assert
+ that we weren't supposed to change section. */
+ if (section == undefined_section)
+ {
+ struct loc_assert_s *next = loc_asserts;
+ loc_asserts
+ = (struct loc_assert_s *) xmalloc (sizeof (*loc_asserts));
+ loc_asserts->next = next;
+ loc_asserts->old_seg = now_seg;
+ loc_asserts->loc_sym = esym;
+ loc_asserts->frag = frag_now;
+ }
+
+ p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0);
+ *p = 0;
+ }
+
+ mmix_handle_rest_of_empty_line ();
+}
+
+/* The BYTE worker. We have to support sequences of mixed "strings",
+ numbers and other constant "first-pass" reducible expressions separated
+ by comma. */
+
+static void
+mmix_byte (void)
+{
+ unsigned int c;
+
+ if (now_seg == text_section)
+ text_has_contents = 1;
+ else if (now_seg == data_section)
+ data_has_contents = 1;
+
+ do
+ {
+ SKIP_WHITESPACE ();
+ switch (*input_line_pointer)
+ {
+ case '\"':
+ ++input_line_pointer;
+ while (is_a_char (c = next_char_of_string ()))
+ {
+ FRAG_APPEND_1_CHAR (c);
+ }
+
+ if (input_line_pointer[-1] != '\"')
+ {
+ /* We will only get here in rare cases involving #NO_APP,
+ where the unterminated string is not recognized by the
+ preformatting pass. */
+ as_bad (_("unterminated string"));
+ mmix_discard_rest_of_line ();
+ return;
+ }
+ break;
+
+ default:
+ {
+ expressionS exp;
+ segT expseg = expression (&exp);
+
+ /* We have to allow special register names as constant numbers. */
+ if ((expseg != absolute_section && expseg != reg_section)
+ || (exp.X_op != O_constant
+ && (exp.X_op != O_register
+ || exp.X_add_number <= 255)))
+ {
+ as_bad (_("BYTE expression not a pure number"));
+ mmix_discard_rest_of_line ();
+ return;
+ }
+ else if ((exp.X_add_number > 255 && exp.X_op != O_register)
+ || exp.X_add_number < 0)
+ {
+ /* Note that mmixal does not allow negative numbers in
+ BYTE sequences, so neither should we. */
+ as_bad (_("BYTE expression not in the range 0..255"));
+ mmix_discard_rest_of_line ();
+ return;
+ }
+
+ FRAG_APPEND_1_CHAR (exp.X_add_number);
+ }
+ break;
+ }
+
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer++;
+ }
+ while (c == ',');
+
+ input_line_pointer--;
+
+ if (mmix_gnu_syntax)
+ demand_empty_rest_of_line ();
+ else
+ {
+ mmix_discard_rest_of_line ();
+ /* Do like demand_empty_rest_of_line and step over the end-of-line
+ boundary. */
+ input_line_pointer++;
+ }
+
+ /* Make sure we align for the next instruction. */
+ last_alignment = 0;
+}
+
+/* Like cons_worker, but we have to ignore "naked comments", not barf on
+ them. Implements WYDE, TETRA and OCTA. We're a little bit more
+ lenient than mmix_byte but FIXME: they should eventually merge. */
+
+static void
+mmix_cons (int nbytes)
+{
+ expressionS exp;
+
+ /* If we don't have any contents, then it's ok to have a specified start
+ address that is not a multiple of the max data size. We will then
+ align it as necessary when we get here. Otherwise, it's a fatal sin. */
+ if (now_seg == text_section)
+ {
+ if (lowest_text_loc != (bfd_vma) -1
+ && (lowest_text_loc & (nbytes - 1)) != 0)
+ {
+ if (text_has_contents)
+ as_bad (_("data item with alignment larger than location"));
+ else if (want_unaligned)
+ as_bad (_("unaligned data at an absolute location is not supported"));
+
+ lowest_text_loc &= ~((bfd_vma) nbytes - 1);
+ lowest_text_loc += (bfd_vma) nbytes;
+ }
+
+ text_has_contents = 1;
+ }
+ else if (now_seg == data_section)
+ {
+ if (lowest_data_loc != (bfd_vma) -1
+ && (lowest_data_loc & (nbytes - 1)) != 0)
+ {
+ if (data_has_contents)
+ as_bad (_("data item with alignment larger than location"));
+ else if (want_unaligned)
+ as_bad (_("unaligned data at an absolute location is not supported"));
+
+ lowest_data_loc &= ~((bfd_vma) nbytes - 1);
+ lowest_data_loc += (bfd_vma) nbytes;
+ }
+
+ data_has_contents = 1;
+ }
+
+ /* Always align these unless asked not to (valid for the current pseudo). */
+ if (! want_unaligned)
+ {
+ last_alignment = nbytes == 2 ? 1 : (nbytes == 4 ? 2 : 3);
+ frag_align (last_alignment, 0, 0);
+ record_alignment (now_seg, last_alignment);
+ }
+
+ /* For mmixal compatibility, a label for an instruction (and emitting
+ pseudo) refers to the _aligned_ address. So we have to emit the
+ label here. */
+ if (current_fb_label >= 0)
+ colon (fb_label_name (current_fb_label, 1));
+ else if (pending_label != NULL)
+ {
+ colon (pending_label);
+ pending_label = NULL;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (is_end_of_line[(unsigned int) *input_line_pointer])
+ {
+ /* Default to zero if the expression was absent. */
+
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ exp.X_unsigned = 0;
+ exp.X_add_symbol = NULL;
+ exp.X_op_symbol = NULL;
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ else
+ do
+ {
+ unsigned int c;
+
+ switch (*input_line_pointer)
+ {
+ /* We support strings here too; each character takes up nbytes
+ bytes. */
+ case '\"':
+ ++input_line_pointer;
+ while (is_a_char (c = next_char_of_string ()))
+ {
+ exp.X_op = O_constant;
+ exp.X_add_number = c;
+ exp.X_unsigned = 1;
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+
+ if (input_line_pointer[-1] != '\"')
+ {
+ /* We will only get here in rare cases involving #NO_APP,
+ where the unterminated string is not recognized by the
+ preformatting pass. */
+ as_bad (_("unterminated string"));
+ mmix_discard_rest_of_line ();
+ return;
+ }
+ break;
+
+ default:
+ {
+ expression (&exp);
+ emit_expr (&exp, (unsigned int) nbytes);
+ SKIP_WHITESPACE ();
+ }
+ break;
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+
+ mmix_handle_rest_of_empty_line ();
+
+ /* We don't need to step up the counter for the current_fb_label here;
+ that's handled by the caller. */
+}
+
+/* The md_do_align worker. At present, we just record an alignment to
+ nullify the automatic alignment we do for WYDE, TETRA and OCTA, as gcc
+ does not use the unaligned macros when attribute packed is used.
+ Arguably this is a GCC bug. */
+
+void
+mmix_md_do_align (int n, char *fill ATTRIBUTE_UNUSED,
+ int len ATTRIBUTE_UNUSED, int max ATTRIBUTE_UNUSED)
+{
+ last_alignment = n;
+ want_unaligned = n == 0;
+}
diff --git a/gas/config/tc-mmix.h b/gas/config/tc-mmix.h
new file mode 100644
index 0000000..78d8adf
--- /dev/null
+++ b/gas/config/tc-mmix.h
@@ -0,0 +1,240 @@
+/* tc-mmix.h -- Header file for tc-mmix.c.
+ Copyright (C) 2001-2014 Free Software Foundation, Inc.
+ Written by Hans-Peter Nilsson (hp@bitrange.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_MMIX
+
+/* See gas/doc/internals.texi for explanation of these macros. */
+#define TARGET_FORMAT "elf64-mmix"
+#define TARGET_ARCH bfd_arch_mmix
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+extern const char mmix_comment_chars[];
+#define tc_comment_chars mmix_comment_chars
+
+extern const char mmix_symbol_chars[];
+#define tc_symbol_chars mmix_symbol_chars
+
+extern const char mmix_exp_chars[];
+#define EXP_CHARS mmix_exp_chars
+
+extern const char mmix_flt_chars[];
+#define FLT_CHARS mmix_flt_chars
+
+/* "@" is a synonym for ".". */
+#define LEX_AT (LEX_BEGIN_NAME)
+
+extern int mmix_label_without_colon_this_line (void);
+#define LABELS_WITHOUT_COLONS mmix_label_without_colon_this_line ()
+
+extern int mmix_next_semicolon_is_eoln;
+#define TC_EOL_IN_INSN(p) (*(p) == ';' && ! mmix_next_semicolon_is_eoln)
+
+/* This is one direction we can get mmixal compatibility. */
+extern void mmix_handle_mmixal (void);
+#define md_start_line_hook mmix_handle_mmixal
+
+extern void mmix_md_begin (void);
+#define md_begin mmix_md_begin
+
+extern void mmix_md_end (void);
+#define md_end mmix_md_end
+
+extern int mmix_current_location \
+ (void (*fn) (expressionS *), expressionS *);
+extern int mmix_parse_predefined_name (char *, expressionS *);
+
+extern char *mmix_current_prefix;
+
+/* A bit ugly, since we "know" that there's a static function
+ current_location that does what we want. We also strip off a leading
+ ':' in another ugly way.
+
+ The [DVWIOUZX]_Handler symbols are provided when-used. */
+
+extern int mmix_gnu_syntax;
+#define md_parse_name(name, exp, mode, cpos) \
+ (! mmix_gnu_syntax \
+ && (name[0] == '@' \
+ ? (! is_part_of_name (name[1]) \
+ && mmix_current_location (current_location, exp)) \
+ : ((name[0] == ':' || ISUPPER (name[0])) \
+ && mmix_parse_predefined_name (name, exp))))
+
+extern char *mmix_prefix_name (char *);
+
+/* We implement when *creating* a symbol, we also need to strip a ':' or
+ prepend a prefix. */
+#define tc_canonicalize_symbol_name(x) \
+ (mmix_current_prefix == NULL && (x)[0] != ':' ? (x) : mmix_prefix_name (x))
+
+#define md_undefined_symbol(x) NULL
+
+extern void mmix_fb_label (expressionS *);
+
+/* Since integer_constant is local to expr.c, we have to make this a
+ macro. FIXME: Do it cleaner. */
+#define md_operand(exp) \
+ do \
+ { \
+ if (input_line_pointer[0] == '#') \
+ { \
+ input_line_pointer++; \
+ integer_constant (16, (exp)); \
+ } \
+ else if (input_line_pointer[0] == '&' \
+ && input_line_pointer[1] != '&') \
+ as_bad (_("`&' serial number operator is not supported")); \
+ else \
+ mmix_fb_label (exp); \
+ } \
+ while (0)
+
+/* Gas dislikes the 2ADD, 8ADD etc. insns, so we have to assemble them in
+ the error-recovery loop. Hopefully there are no significant
+ differences. Also, space on a line isn't gracefully handled. */
+extern int mmix_assemble_return_nonzero (char *);
+#define tc_unrecognized_line(c) \
+ ((c) == ' ' \
+ || (((c) == '1' || (c) == '2' || (c) == '4' || (c) == '8') \
+ && mmix_assemble_return_nonzero (input_line_pointer - 1)))
+
+#define md_number_to_chars number_to_chars_bigendian
+
+#define WORKING_DOT_WORD
+
+extern const struct relax_type mmix_relax_table[];
+#define TC_GENERIC_RELAX_TABLE mmix_relax_table
+
+/* We use the relax table for everything except the GREG frags and PUSHJ. */
+extern long mmix_md_relax_frag (segT, fragS *, long);
+#define md_relax_frag mmix_md_relax_frag
+
+#define tc_fix_adjustable(FIX) \
+ (((FIX)->fx_addsy == NULL \
+ || S_GET_SEGMENT ((FIX)->fx_addsy) != reg_section) \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_INHERIT \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_ENTRY \
+ && (FIX)->fx_r_type != BFD_RELOC_MMIX_LOCAL)
+
+/* Adjust symbols which are registers. */
+#define tc_adjust_symtab() mmix_adjust_symtab ()
+extern void mmix_adjust_symtab (void);
+
+/* Here's where we make all symbols global, when so requested.
+ We must avoid doing that for expression symbols or section symbols,
+ though. */
+extern int mmix_globalize_symbols;
+#define tc_frob_symbol(sym, punt) \
+ do \
+ { \
+ if (S_GET_SEGMENT (sym) == reg_section) \
+ { \
+ if (S_GET_NAME (sym)[0] != '$' \
+ && S_GET_VALUE (sym) < 256) \
+ { \
+ if (mmix_globalize_symbols) \
+ S_SET_EXTERNAL (sym); \
+ else \
+ symbol_mark_used_in_reloc (sym); \
+ } \
+ } \
+ else if (mmix_globalize_symbols \
+ && ! symbol_section_p (sym) \
+ && sym != section_symbol (absolute_section) \
+ && ! S_IS_LOCAL (sym)) \
+ S_SET_EXTERNAL (sym); \
+ } \
+ while (0)
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* When relaxing, we need to emit various relocs we otherwise wouldn't. */
+#define TC_FORCE_RELOCATION(fix) mmix_force_relocation (fix)
+extern int mmix_force_relocation (struct fix *);
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define md_section_align(seg, size) (size)
+
+#define LISTING_HEADER "GAS for MMIX"
+
+/* The default of 4 means Bcc expansion looks like it's missing a line. */
+#define LISTING_LHS_CONT_LINES 5
+
+extern fragS *mmix_opcode_frag;
+#define TC_FRAG_TYPE fragS *
+#define TC_FRAG_INIT(frag) (frag)->tc_frag_data = mmix_opcode_frag
+
+/* We need to associate each section symbol with a list of GREGs defined
+ for that section/segment and sorted on offset, between the point where
+ all symbols have been evaluated and all frags mapped, and when the
+ fixups are done and relocs are output. Similarly for each unknown
+ symbol. */
+extern void mmix_frob_file (void);
+#define tc_frob_file_before_fix() \
+ do \
+ { \
+ int i = 0; \
+ \
+ /* It's likely mmix_frob_file changed (removed) sections, so make \
+ sure sections are correctly numbered as per renumber_sections, \
+ (static to write.c where this macro is called). */ \
+ mmix_frob_file (); \
+ bfd_map_over_sections (stdoutput, renumber_sections, &i); \
+ } \
+ while (0)
+
+/* Used by mmix_frob_file. Hangs on section symbols and unknown symbols. */
+struct mmix_symbol_gregs;
+#define TC_SYMFIELD_TYPE struct mmix_symbol_gregs *
+
+/* Used by relaxation, counting maximum needed PUSHJ stubs for a section. */
+struct mmix_segment_info_type
+ {
+ /* We only need to keep track of the last stubbable frag because
+ there's no less hackish way to keep track of different relaxation
+ rounds. */
+ fragS *last_stubfrag;
+ bfd_size_type nstubs;
+ };
+#define TC_SEGMENT_INFO_TYPE struct mmix_segment_info_type
+
+extern void mmix_md_elf_section_change_hook (void);
+#define md_elf_section_change_hook mmix_md_elf_section_change_hook
+
+extern void mmix_md_do_align (int, char *, int, int);
+#define md_do_align(n, fill, len, max, label) \
+ mmix_md_do_align (n, fill, len, max)
+
+/* Each insn is a tetrabyte (4 bytes) long, but if there are BYTE
+ sequences sprinkled in, we can get unaligned DWARF2 offsets, so let's
+ explicitly say one byte. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+/* This target is buggy, and sets fix size too large. */
+#define TC_FX_SIZE_SLACK(FIX) 6
+
+/* MMIX has global register symbols. */
+#define TC_GLOBAL_REGISTER_SYMBOL_OK
diff --git a/gas/config/tc-mn10200.c b/gas/config/tc-mn10200.c
new file mode 100644
index 0000000..f76fb08
--- /dev/null
+++ b/gas/config/tc-mn10200.c
@@ -0,0 +1,1340 @@
+/* tc-mn10200.c -- Assembler code for the Matsushita 10200
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/mn10200.h"
+
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char *name;
+ int value;
+};
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+const relax_typeS md_relax_table[] =
+ {
+ /* bCC relaxing */
+ {0x81, -0x7e, 2, 1},
+ {0x8004, -0x7ffb, 5, 2},
+ {0x800006, -0x7ffff9, 7, 0},
+ /* bCCx relaxing */
+ {0x81, -0x7e, 3, 4},
+ {0x8004, -0x7ffb, 6, 5},
+ {0x800006, -0x7ffff9, 8, 0},
+ /* jsr relaxing */
+ {0x8004, -0x7ffb, 3, 7},
+ {0x800006, -0x7ffff9, 5, 0},
+ /* jmp relaxing */
+ {0x81, -0x7e, 2, 9},
+ {0x8004, -0x7ffb, 3, 10},
+ {0x800006, -0x7ffff9, 5, 0},
+
+};
+
+
+/* Fixups. */
+#define MAX_INSN_FIXUPS 5
+
+struct mn10200_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+struct mn10200_fixup fixups[MAX_INSN_FIXUPS];
+static int fc;
+
+const char *md_shortopts = "";
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *mn10200_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name data_registers[] =
+{
+ { "d0", 0 },
+ { "d1", 1 },
+ { "d2", 2 },
+ { "d3", 3 },
+};
+#define DATA_REG_NAME_CNT \
+ (sizeof (data_registers) / sizeof (struct reg_name))
+
+static const struct reg_name address_registers[] =
+{
+ { "a0", 0 },
+ { "a1", 1 },
+ { "a2", 2 },
+ { "a3", 3 },
+};
+#define ADDRESS_REG_NAME_CNT \
+ (sizeof (address_registers) / sizeof (struct reg_name))
+
+static const struct reg_name other_registers[] =
+{
+ { "mdr", 0 },
+ { "psw", 0 },
+};
+#define OTHER_REG_NAME_CNT \
+ (sizeof (other_registers) / sizeof (struct reg_name))
+
+/* reg_name_search does a binary search of the given register table
+ to see if "name" is a valid regiter name. Returns the register
+ number from the array on success, or -1 on failure. */
+
+static int
+reg_name_search (const struct reg_name *regs,
+ int regcount,
+ const char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+ return -1;
+}
+
+/* Summary of register_name().
+
+ in: Input_line_pointer points to 1st char of operand.
+
+ out: An expressionS.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in
+ its original state. */
+
+static bfd_boolean
+data_register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (data_registers, DATA_REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+/* Summary of register_name().
+
+ in: Input_line_pointer points to 1st char of operand.
+
+ out: An expressionS.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in
+ its original state. */
+
+static bfd_boolean
+address_register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (address_registers, ADDRESS_REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+/* Summary of register_name().
+
+ in: Input_line_pointer points to 1st char of operand.
+
+ out: An expressionS.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in
+ its original state. */
+
+static bfd_boolean
+other_register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (other_registers, OTHER_REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("MN10200 options:\n\
+none yet\n"));
+}
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ return ieee_md_atof (type, litp, sizep, FALSE);
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ fragS *fragP)
+{
+ static unsigned long label_count = 0;
+ char buf[40];
+
+ subseg_change (sec, 0);
+ if (fragP->fr_subtype == 0)
+ {
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 1)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xfc;
+ fix_new (fragP, fragP->fr_fix + 3, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 2)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xf4;
+ fragP->fr_literal[offset + 3] = 0xe0;
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 7;
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 4)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xfc:
+ opcode = 0xfd;
+ break;
+ case 0xfd:
+ opcode = 0xfc;
+ break;
+ case 0xfe:
+ opcode = 0xff;
+ break;
+ case 0xff:
+ opcode = 0xfe;
+ break;
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ case 0xec:
+ opcode = 0xed;
+ break;
+ case 0xed:
+ opcode = 0xec;
+ break;
+ case 0xee:
+ opcode = 0xef;
+ break;
+ case 0xef:
+ opcode = 0xee;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xfc;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 5)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xfc:
+ opcode = 0xfd;
+ break;
+ case 0xfd:
+ opcode = 0xfc;
+ break;
+ case 0xfe:
+ opcode = 0xff;
+ break;
+ case 0xff:
+ opcode = 0xfe;
+ break;
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xe0:
+ opcode = 0xe2;
+ break;
+ case 0xe2:
+ opcode = 0xe0;
+ break;
+ case 0xe3:
+ opcode = 0xe1;
+ break;
+ case 0xe1:
+ opcode = 0xe3;
+ break;
+ case 0xe4:
+ opcode = 0xe6;
+ break;
+ case 0xe6:
+ opcode = 0xe4;
+ break;
+ case 0xe7:
+ opcode = 0xe5;
+ break;
+ case 0xe5:
+ opcode = 0xe7;
+ break;
+ case 0xec:
+ opcode = 0xed;
+ break;
+ case 0xed:
+ opcode = 0xec;
+ break;
+ case 0xee:
+ opcode = 0xef;
+ break;
+ case 0xef:
+ opcode = 0xee;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xf4;
+ fragP->fr_literal[offset + 4] = 0xe0;
+ fix_new (fragP, fragP->fr_fix + 5, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 8;
+ }
+ else if (fragP->fr_subtype == 6)
+ {
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 7)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xf4;
+ fragP->fr_literal[offset + 1] = 0xe1;
+
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 8)
+ {
+ fragP->fr_literal[fragP->fr_fix] = 0xea;
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 9)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xfc;
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 10)
+ {
+ int offset = fragP->fr_fix;
+ fragP->fr_literal[offset] = 0xf4;
+ fragP->fr_literal[offset + 1] = 0xe0;
+
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_24_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else
+ abort ();
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin (void)
+{
+ char *prev_name = "";
+ register const struct mn10200_opcode *op;
+
+ mn10200_hash = hash_new ();
+
+ /* Insert unique names into hash table. The MN10200 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ op = mn10200_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (mn10200_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ /* This is both a simplification (we don't have to write md_apply_fix)
+ and support for future optimizations (branch shortening and similar
+ stuff in the linker. */
+ linkrelax = 1;
+}
+
+static unsigned long
+check_operand (unsigned long insn ATTRIBUTE_UNUSED,
+ const struct mn10200_operand *operand,
+ offsetT val)
+{
+ /* No need to check 24bit or 32bit operands for a bit. */
+ if (operand->bits < 24
+ && (operand->flags & MN10200_OPERAND_NOCHECK) == 0)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & MN10200_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ return 0;
+ else
+ return 1;
+ }
+ return 1;
+}
+/* If while processing a fixup, a reloc really needs to be created
+ Then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ reloc = xmalloc (sizeof (arelent));
+
+ if (fixp->fx_subsy != NULL)
+ {
+ if (S_GET_SEGMENT (fixp->fx_addsy) == S_GET_SEGMENT (fixp->fx_subsy)
+ && S_IS_DEFINED (fixp->fx_subsy))
+ {
+ fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_subsy = NULL;
+ }
+ else
+ /* FIXME: We should try more ways to resolve difference expressions
+ here. At least this is better than silently ignoring the
+ subtrahend. */
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("can't resolve `%s' {%s section} - `%s' {%s section}"),
+ fixp->fx_addsy ? S_GET_NAME (fixp->fx_addsy) : "0",
+ segment_name (fixp->fx_addsy
+ ? S_GET_SEGMENT (fixp->fx_addsy)
+ : absolute_section),
+ S_GET_NAME (fixp->fx_subsy),
+ segment_name (S_GET_SEGMENT (fixp->fx_addsy)));
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->addend = fixp->fx_offset;
+ return reloc;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *seg)
+{
+ if (fragp->fr_subtype == 6
+ && (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol)))
+ fragp->fr_subtype = 7;
+ else if (fragp->fr_subtype == 8
+ && (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol)))
+ fragp->fr_subtype = 10;
+
+ if (fragp->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
+}
+
+long
+md_pcrel_from (fixS *fixp)
+{
+ return fixp->fx_frag->fr_address;
+}
+
+void
+md_apply_fix (fixS * fixP, valueT * valP ATTRIBUTE_UNUSED, segT seg ATTRIBUTE_UNUSED)
+{
+ /* We shouldn't ever get here because linkrelax is nonzero. */
+ abort ();
+ fixP->fx_done = 1;
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+mn10200_insert_operand (unsigned long *insnp,
+ unsigned long *extensionp,
+ const struct mn10200_operand *operand,
+ offsetT val,
+ char *file,
+ unsigned int line,
+ unsigned int shift)
+{
+ /* No need to check 24 or 32bit operands for a bit. */
+ if (operand->bits < 24
+ && (operand->flags & MN10200_OPERAND_NOCHECK) == 0)
+ {
+ long min, max;
+ offsetT test;
+
+ if ((operand->flags & MN10200_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (operand->bits - 1)) - 1;
+ min = - (1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
+ }
+
+ if ((operand->flags & MN10200_OPERAND_EXTENDED) == 0)
+ {
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10200_OPERAND_REPEATED) != 0)
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + 2));
+ }
+ else
+ {
+ *extensionp |= (val >> 16) & 0xff;
+ *insnp |= val & 0xffff;
+ }
+}
+
+void
+md_assemble (char *str)
+{
+ char *s;
+ struct mn10200_opcode *opcode;
+ struct mn10200_opcode *next_opcode;
+ const unsigned char *opindex_ptr;
+ int next_opindex, relaxable;
+ unsigned long insn, extension, size = 0;
+ char *f;
+ int i;
+ int match;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && !ISSPACE (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Find the first opcode with the proper name. */
+ opcode = (struct mn10200_opcode *) hash_find (mn10200_hash, str);
+ if (opcode == NULL)
+ {
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ return;
+ }
+
+ str = s;
+ while (ISSPACE (*str))
+ ++str;
+
+ input_line_pointer = str;
+
+ for (;;)
+ {
+ const char *errmsg = NULL;
+ int op_idx;
+ char *hold;
+ int extra_shift = 0;
+
+ relaxable = 0;
+ fc = 0;
+ match = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extension = 0;
+ for (op_idx = 1, opindex_ptr = opcode->operands;
+ *opindex_ptr != 0;
+ opindex_ptr++, op_idx++)
+ {
+ const struct mn10200_operand *operand;
+ expressionS ex;
+
+ if (next_opindex == 0)
+ {
+ operand = &mn10200_operands[*opindex_ptr];
+ }
+ else
+ {
+ operand = &mn10200_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ errmsg = NULL;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ if (operand->flags & MN10200_OPERAND_RELAX)
+ relaxable = 1;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ if (operand->flags & MN10200_OPERAND_PAREN)
+ {
+ if (*input_line_pointer != ')' && *input_line_pointer != '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ input_line_pointer++;
+ goto keep_going;
+ }
+ /* See if we can match the operands. */
+ else if (operand->flags & MN10200_OPERAND_DREG)
+ {
+ if (!data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10200_OPERAND_AREG)
+ {
+ if (!address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10200_OPERAND_PSW)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "psw") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10200_OPERAND_MDR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "mdr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (other_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (*str == ')' || *str == '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else
+ {
+ expression (&ex);
+ }
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+ case O_register:
+ if ((operand->flags
+ & (MN10200_OPERAND_DREG | MN10200_OPERAND_AREG)) == 0)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ if (opcode->format == FMT_2 || opcode->format == FMT_5)
+ extra_shift = 8;
+ else if (opcode->format == FMT_3 || opcode->format == FMT_6
+ || opcode->format == FMT_7)
+ extra_shift = 16;
+ else
+ extra_shift = 0;
+
+ mn10200_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, NULL,
+ 0, extra_shift);
+
+ break;
+
+ case O_constant:
+ /* If this operand can be promoted, and it doesn't
+ fit into the allocated bitfield for this insn,
+ then promote it (ie this opcode does not match). */
+ if (operand->flags
+ & (MN10200_OPERAND_PROMOTE | MN10200_OPERAND_RELAX)
+ && !check_operand (insn, operand, ex.X_add_number))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ mn10200_insert_operand (&insn, &extension, operand,
+ ex.X_add_number, NULL,
+ 0, 0);
+ break;
+
+ default:
+ /* If this operand can be promoted, then this opcode didn't
+ match since we can't know if it needed promotion! */
+ if (operand->flags & MN10200_OPERAND_PROMOTE)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ break;
+ }
+
+keep_going:
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ }
+
+ /* Make sure we used all the operands! */
+ if (*str != ',')
+ match = 1;
+
+ error:
+ if (match == 0)
+ {
+ next_opcode = opcode + 1;
+ if (!strcmp (next_opcode->name, opcode->name))
+ {
+ opcode = next_opcode;
+ continue;
+ }
+
+ as_bad ("%s", errmsg);
+ return;
+ }
+ break;
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ input_line_pointer = str;
+
+ if (opcode->format == FMT_1)
+ size = 1;
+ else if (opcode->format == FMT_2 || opcode->format == FMT_4)
+ size = 2;
+ else if (opcode->format == FMT_3 || opcode->format == FMT_5)
+ size = 3;
+ else if (opcode->format == FMT_6)
+ size = 4;
+ else if (opcode->format == FMT_7)
+ size = 5;
+ else
+ abort ();
+
+ /* Write out the instruction. */
+ dwarf2_emit_insn (0);
+ if (relaxable && fc > 0)
+ {
+ /* On a 64-bit host the size of an 'int' is not the same
+ as the size of a pointer, so we need a union to convert
+ the opindex field of the fr_cgen structure into a char *
+ so that it can be stored in the frag. We do not have
+ to worry about loosing accuracy as we are not going to
+ be even close to the 32bit limit of the int. */
+ union
+ {
+ int opindex;
+ char * ptr;
+ }
+ opindex_converter;
+ int type;
+
+ /* bCC */
+ if (size == 2 && opcode->opcode != 0xfc0000)
+ {
+ /* Handle bra specially. Basically treat it like jmp so
+ that we automatically handle 8, 16 and 32 bit offsets
+ correctly as well as jumps to an undefined address.
+
+ It is also important to not treat it like other bCC
+ instructions since the long forms of bra is different
+ from other bCC instructions. */
+ if (opcode->opcode == 0xea00)
+ type = 8;
+ else
+ type = 0;
+ }
+ /* jsr */
+ else if (size == 3 && opcode->opcode == 0xfd0000)
+ type = 6;
+ /* jmp */
+ else if (size == 3 && opcode->opcode == 0xfc0000)
+ type = 8;
+ /* bCCx */
+ else
+ type = 3;
+
+ opindex_converter.opindex = fixups[0].opindex;
+ f = frag_var (rs_machine_dependent, 8, 8 - size, type,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ opindex_converter.ptr);
+ number_to_chars_bigendian (f, insn, size);
+ if (8 - size > 4)
+ {
+ number_to_chars_bigendian (f + size, 0, 4);
+ number_to_chars_bigendian (f + size + 4, 0, 8 - size - 4);
+ }
+ else
+ number_to_chars_bigendian (f + size, 0, 8 - size);
+ }
+ else
+ {
+ f = frag_more (size);
+
+ /* Oh, what a mess. The instruction is in big endian format, but
+ 16 and 24bit immediates are little endian! */
+ if (opcode->format == FMT_3)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_6)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_7)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ number_to_chars_littleendian (f + 4, extension & 0xff, 1);
+ }
+ else
+ number_to_chars_bigendian (f, insn, size > 4 ? 4 : size);
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct mn10200_operand *operand;
+ int reloc_size;
+
+ operand = &mn10200_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ int offset;
+ fixS *fixP;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput,
+ fixups[i].reloc);
+
+ if (!reloc_howto)
+ abort ();
+
+ reloc_size = bfd_get_reloc_size (reloc_howto);
+
+ if (reloc_size < 1 || reloc_size > 4)
+ abort ();
+
+ offset = 4 - reloc_size;
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ reloc_size,
+ &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+
+ /* PC-relative offsets are from the first byte of the
+ next instruction, not from the start of the current
+ instruction. */
+ if (reloc_howto->pc_relative)
+ fixP->fx_offset += reloc_size;
+ }
+ else
+ {
+ int reloc, pcrel, offset;
+ fixS *fixP;
+
+ reloc = BFD_RELOC_NONE;
+ /* How big is the reloc? Remember SPLIT relocs are
+ implicitly 32bits. */
+ reloc_size = operand->bits;
+
+ offset = size - reloc_size / 8;
+
+ /* Is the reloc pc-relative? */
+ pcrel = (operand->flags & MN10200_OPERAND_PCREL) != 0;
+
+ /* Choose a proper BFD relocation type. */
+ if (pcrel)
+ {
+ if (reloc_size == 8)
+ reloc = BFD_RELOC_8_PCREL;
+ else if (reloc_size == 24)
+ reloc = BFD_RELOC_24_PCREL;
+ else
+ abort ();
+ }
+ else
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_32;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8;
+ else if (reloc_size == 24)
+ reloc = BFD_RELOC_24;
+ else
+ abort ();
+ }
+
+ /* Convert the size of the reloc into what fix_new_exp
+ wants. */
+ reloc_size = reloc_size / 8;
+ if (reloc_size == 8)
+ reloc_size = 0;
+ else if (reloc_size == 16)
+ reloc_size = 1;
+ else if (reloc_size == 32 || reloc_size == 24)
+ reloc_size = 2;
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ reloc_size, &fixups[i].exp, pcrel,
+ ((bfd_reloc_code_real_type) reloc));
+
+ /* PC-relative offsets are from the first byte of the
+ next instruction, not from the start of the current
+ instruction. */
+ if (pcrel)
+ fixP->fx_offset += size;
+ }
+ }
+ }
+}
+
diff --git a/gas/config/tc-mn10200.h b/gas/config/tc-mn10200.h
new file mode 100644
index 0000000..6eb0418
--- /dev/null
+++ b/gas/config/tc-mn10200.h
@@ -0,0 +1,46 @@
+/* tc-mn10200.h -- Header file for tc-mn10200.c.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_MN10200
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_mn10200
+
+#define TARGET_FORMAT "elf32-mn10200"
+
+#define md_operand(x)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* Don't bother to adjust relocs. */
+#define tc_fix_adjustable(FIX) 0
+
+/* We do relaxing in the assembler as well as the linker. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
diff --git a/gas/config/tc-mn10300.c b/gas/config/tc-mn10300.c
new file mode 100644
index 0000000..3d159b1
--- /dev/null
+++ b/gas/config/tc-mn10300.c
@@ -0,0 +1,2639 @@
+/* tc-mn10300.c -- Assembler code for the Matsushita 10300
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/mn10300.h"
+#include "dwarf2dbg.h"
+#include "libiberty.h"
+
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char *name;
+ int value;
+};
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+const relax_typeS md_relax_table[] =
+{
+ /* The plus values for the bCC and fBCC instructions in the table below
+ are because the branch instruction is translated into a jump
+ instruction that is now +2 or +3 bytes further on in memory, and the
+ correct size of jump instruction must be selected. */
+ /* bCC relaxing. */
+ {0x7f, -0x80, 2, 1},
+ {0x7fff + 2, -0x8000 + 2, 5, 2},
+ {0x7fffffff, -0x80000000, 7, 0},
+
+ /* bCC relaxing (uncommon cases for 3byte length instructions) */
+ {0x7f, -0x80, 3, 4},
+ {0x7fff + 3, -0x8000 + 3, 6, 5},
+ {0x7fffffff, -0x80000000, 8, 0},
+
+ /* call relaxing. */
+ {0x7fff, -0x8000, 5, 7},
+ {0x7fffffff, -0x80000000, 7, 0},
+
+ /* calls relaxing. */
+ {0x7fff, -0x8000, 4, 9},
+ {0x7fffffff, -0x80000000, 6, 0},
+
+ /* jmp relaxing. */
+ {0x7f, -0x80, 2, 11},
+ {0x7fff, -0x8000, 3, 12},
+ {0x7fffffff, -0x80000000, 5, 0},
+
+ /* fbCC relaxing. */
+ {0x7f, -0x80, 3, 14},
+ {0x7fff + 3, -0x8000 + 3, 6, 15},
+ {0x7fffffff, -0x80000000, 8, 0},
+
+};
+
+/* Set linkrelax here to avoid fixups in most sections. */
+int linkrelax = 1;
+
+static int current_machine;
+
+/* Fixups. */
+#define MAX_INSN_FIXUPS 5
+
+struct mn10300_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+struct mn10300_fixup fixups[MAX_INSN_FIXUPS];
+static int fc;
+
+/* We must store the value of each register operand so that we can
+ verify that certain registers do not match. */
+int mn10300_reg_operands[MN10300_MAX_OPERANDS];
+
+const char *md_shortopts = "";
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+#define HAVE_AM33_2 (current_machine == AM33_2)
+#define HAVE_AM33 (current_machine == AM33 || HAVE_AM33_2)
+#define HAVE_AM30 (current_machine == AM30)
+
+/* Opcode hash table. */
+static struct hash_control *mn10300_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name data_registers[] =
+{
+ { "d0", 0 },
+ { "d1", 1 },
+ { "d2", 2 },
+ { "d3", 3 },
+};
+
+static const struct reg_name address_registers[] =
+{
+ { "a0", 0 },
+ { "a1", 1 },
+ { "a2", 2 },
+ { "a3", 3 },
+};
+
+static const struct reg_name r_registers[] =
+{
+ { "a0", 8 },
+ { "a1", 9 },
+ { "a2", 10 },
+ { "a3", 11 },
+ { "d0", 12 },
+ { "d1", 13 },
+ { "d2", 14 },
+ { "d3", 15 },
+ { "e0", 0 },
+ { "e1", 1 },
+ { "e10", 10 },
+ { "e11", 11 },
+ { "e12", 12 },
+ { "e13", 13 },
+ { "e14", 14 },
+ { "e15", 15 },
+ { "e2", 2 },
+ { "e3", 3 },
+ { "e4", 4 },
+ { "e5", 5 },
+ { "e6", 6 },
+ { "e7", 7 },
+ { "e8", 8 },
+ { "e9", 9 },
+ { "r0", 0 },
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r2", 2 },
+ { "r3", 3 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+};
+
+static const struct reg_name xr_registers[] =
+{
+ { "mcrh", 2 },
+ { "mcrl", 3 },
+ { "mcvf", 4 },
+ { "mdrq", 1 },
+ { "sp", 0 },
+ { "xr0", 0 },
+ { "xr1", 1 },
+ { "xr10", 10 },
+ { "xr11", 11 },
+ { "xr12", 12 },
+ { "xr13", 13 },
+ { "xr14", 14 },
+ { "xr15", 15 },
+ { "xr2", 2 },
+ { "xr3", 3 },
+ { "xr4", 4 },
+ { "xr5", 5 },
+ { "xr6", 6 },
+ { "xr7", 7 },
+ { "xr8", 8 },
+ { "xr9", 9 },
+};
+
+static const struct reg_name float_registers[] =
+{
+ { "fs0", 0 },
+ { "fs1", 1 },
+ { "fs10", 10 },
+ { "fs11", 11 },
+ { "fs12", 12 },
+ { "fs13", 13 },
+ { "fs14", 14 },
+ { "fs15", 15 },
+ { "fs16", 16 },
+ { "fs17", 17 },
+ { "fs18", 18 },
+ { "fs19", 19 },
+ { "fs2", 2 },
+ { "fs20", 20 },
+ { "fs21", 21 },
+ { "fs22", 22 },
+ { "fs23", 23 },
+ { "fs24", 24 },
+ { "fs25", 25 },
+ { "fs26", 26 },
+ { "fs27", 27 },
+ { "fs28", 28 },
+ { "fs29", 29 },
+ { "fs3", 3 },
+ { "fs30", 30 },
+ { "fs31", 31 },
+ { "fs4", 4 },
+ { "fs5", 5 },
+ { "fs6", 6 },
+ { "fs7", 7 },
+ { "fs8", 8 },
+ { "fs9", 9 },
+};
+
+static const struct reg_name double_registers[] =
+{
+ { "fd0", 0 },
+ { "fd10", 10 },
+ { "fd12", 12 },
+ { "fd14", 14 },
+ { "fd16", 16 },
+ { "fd18", 18 },
+ { "fd2", 2 },
+ { "fd20", 20 },
+ { "fd22", 22 },
+ { "fd24", 24 },
+ { "fd26", 26 },
+ { "fd28", 28 },
+ { "fd30", 30 },
+ { "fd4", 4 },
+ { "fd6", 6 },
+ { "fd8", 8 },
+};
+
+/* We abuse the `value' field, that would be otherwise unused, to
+ encode the architecture on which (access to) the register was
+ introduced. FIXME: we should probably warn when we encounter a
+ register name when assembling for an architecture that doesn't
+ support it, before parsing it as a symbol name. */
+static const struct reg_name other_registers[] =
+{
+ { "epsw", AM33 },
+ { "mdr", 0 },
+ { "pc", AM33 },
+ { "psw", 0 },
+ { "sp", 0 },
+ { "ssp", 0 },
+ { "usp", 0 },
+};
+
+#define OTHER_REG_NAME_CNT ARRAY_SIZE (other_registers)
+
+/* Perform a binary search of the given register table REGS to see
+ if NAME is a valid regiter name. Returns the register number from
+ the array on success, or -1 on failure. */
+
+static int
+reg_name_search (const struct reg_name *regs,
+ int regcount,
+ const char *name)
+{
+ int low, high;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ int cmp, middle;
+
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/* Looks at the current position in the input line to see if it is
+ the name of a register in TABLE. If it is, then the name is
+ converted into an expression returned in EXPRESSIONP (with X_op
+ set to O_register and X_add_number set to the register number), the
+ input pointer is left pointing at the first non-blank character after
+ the name and the function returns TRUE. Otherwise the input pointer
+ is left alone and the function returns FALSE. */
+
+static bfd_boolean
+get_register_name (expressionS * expressionP,
+ const struct reg_name * table,
+ size_t table_length)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (table, table_length, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+static bfd_boolean
+r_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, r_registers, ARRAY_SIZE (r_registers));
+}
+
+
+static bfd_boolean
+xr_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, xr_registers, ARRAY_SIZE (xr_registers));
+}
+
+static bfd_boolean
+data_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, data_registers, ARRAY_SIZE (data_registers));
+}
+
+static bfd_boolean
+address_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, address_registers, ARRAY_SIZE (address_registers));
+}
+
+static bfd_boolean
+float_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, float_registers, ARRAY_SIZE (float_registers));
+}
+
+static bfd_boolean
+double_register_name (expressionS *expressionP)
+{
+ return get_register_name (expressionP, double_registers, ARRAY_SIZE (double_registers));
+}
+
+static bfd_boolean
+other_register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (other_registers, ARRAY_SIZE (other_registers), name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number == 0
+ || (reg_number == AM33 && HAVE_AM33))
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = 0;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("MN10300 assembler options:\n\
+none yet\n"));
+}
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ return ieee_md_atof (type, litp, sizep, FALSE);
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ fragS *fragP)
+{
+ static unsigned long label_count = 0;
+ char buf[40];
+
+ subseg_change (sec, 0);
+ if (fragP->fr_subtype == 0)
+ {
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 1)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xc8:
+ opcode = 0xc9;
+ break;
+ case 0xc9:
+ opcode = 0xc8;
+ break;
+ case 0xc0:
+ opcode = 0xc2;
+ break;
+ case 0xc2:
+ opcode = 0xc0;
+ break;
+ case 0xc3:
+ opcode = 0xc1;
+ break;
+ case 0xc1:
+ opcode = 0xc3;
+ break;
+ case 0xc4:
+ opcode = 0xc6;
+ break;
+ case 0xc6:
+ opcode = 0xc4;
+ break;
+ case 0xc7:
+ opcode = 0xc5;
+ break;
+ case 0xc5:
+ opcode = 0xc7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xcc;
+ fix_new (fragP, fragP->fr_fix + 3, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 2)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xc8:
+ opcode = 0xc9;
+ break;
+ case 0xc9:
+ opcode = 0xc8;
+ break;
+ case 0xc0:
+ opcode = 0xc2;
+ break;
+ case 0xc2:
+ opcode = 0xc0;
+ break;
+ case 0xc3:
+ opcode = 0xc1;
+ break;
+ case 0xc1:
+ opcode = 0xc3;
+ break;
+ case 0xc4:
+ opcode = 0xc6;
+ break;
+ case 0xc6:
+ opcode = 0xc4;
+ break;
+ case 0xc7:
+ opcode = 0xc5;
+ break;
+ case 0xc5:
+ opcode = 0xc7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 1, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 2] = 0xdc;
+ fix_new (fragP, fragP->fr_fix + 3, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 7;
+ }
+ else if (fragP->fr_subtype == 3)
+ {
+ fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 4)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xe9:
+ opcode = 0xe8;
+ break;
+ case 0xea:
+ opcode = 0xeb;
+ break;
+ case 0xeb:
+ opcode = 0xea;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xcc;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 5)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xe8:
+ opcode = 0xe9;
+ break;
+ case 0xea:
+ opcode = 0xeb;
+ break;
+ case 0xeb:
+ opcode = 0xea;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xdc;
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 8;
+ }
+ else if (fragP->fr_subtype == 6)
+ {
+ int offset = fragP->fr_fix;
+
+ fragP->fr_literal[offset] = 0xcd;
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 7)
+ {
+ int offset = fragP->fr_fix;
+
+ fragP->fr_literal[offset] = 0xdd;
+ fragP->fr_literal[offset + 5] = fragP->fr_literal[offset + 3];
+ fragP->fr_literal[offset + 6] = fragP->fr_literal[offset + 4];
+ fragP->fr_literal[offset + 3] = 0;
+ fragP->fr_literal[offset + 4] = 0;
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 7;
+ }
+ else if (fragP->fr_subtype == 8)
+ {
+ int offset = fragP->fr_fix;
+
+ fragP->fr_literal[offset] = 0xfa;
+ fragP->fr_literal[offset + 1] = 0xff;
+ fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 4;
+ }
+ else if (fragP->fr_subtype == 9)
+ {
+ int offset = fragP->fr_fix;
+
+ fragP->fr_literal[offset] = 0xfc;
+ fragP->fr_literal[offset + 1] = 0xff;
+
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 10)
+ {
+ fragP->fr_literal[fragP->fr_fix] = 0xca;
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 2;
+ }
+ else if (fragP->fr_subtype == 11)
+ {
+ int offset = fragP->fr_fix;
+
+ fragP->fr_literal[offset] = 0xcc;
+
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 12)
+ {
+ int offset = fragP->fr_fix;
+
+ fragP->fr_literal[offset] = 0xdc;
+
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 5;
+ }
+ else if (fragP->fr_subtype == 13)
+ {
+ fix_new (fragP, fragP->fr_fix + 2, 1, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 3;
+ }
+ else if (fragP->fr_subtype == 14)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xd0:
+ opcode = 0xd1;
+ break;
+ case 0xd1:
+ opcode = 0xd0;
+ break;
+ case 0xd2:
+ opcode = 0xdc;
+ break;
+ case 0xd3:
+ opcode = 0xdb;
+ break;
+ case 0xd4:
+ opcode = 0xda;
+ break;
+ case 0xd5:
+ opcode = 0xd9;
+ break;
+ case 0xd6:
+ opcode = 0xd8;
+ break;
+ case 0xd7:
+ opcode = 0xdd;
+ break;
+ case 0xd8:
+ opcode = 0xd6;
+ break;
+ case 0xd9:
+ opcode = 0xd5;
+ break;
+ case 0xda:
+ opcode = 0xd4;
+ break;
+ case 0xdb:
+ opcode = 0xd3;
+ break;
+ case 0xdc:
+ opcode = 0xd2;
+ break;
+ case 0xdd:
+ opcode = 0xd7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xcc;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_16_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 6;
+ }
+ else if (fragP->fr_subtype == 15)
+ {
+ /* Reverse the condition of the first branch. */
+ int offset = fragP->fr_fix;
+ int opcode = fragP->fr_literal[offset + 1] & 0xff;
+
+ switch (opcode)
+ {
+ case 0xd0:
+ opcode = 0xd1;
+ break;
+ case 0xd1:
+ opcode = 0xd0;
+ break;
+ case 0xd2:
+ opcode = 0xdc;
+ break;
+ case 0xd3:
+ opcode = 0xdb;
+ break;
+ case 0xd4:
+ opcode = 0xda;
+ break;
+ case 0xd5:
+ opcode = 0xd9;
+ break;
+ case 0xd6:
+ opcode = 0xd8;
+ break;
+ case 0xd7:
+ opcode = 0xdd;
+ break;
+ case 0xd8:
+ opcode = 0xd6;
+ break;
+ case 0xd9:
+ opcode = 0xd5;
+ break;
+ case 0xda:
+ opcode = 0xd4;
+ break;
+ case 0xdb:
+ opcode = 0xd3;
+ break;
+ case 0xdc:
+ opcode = 0xd2;
+ break;
+ case 0xdd:
+ opcode = 0xd7;
+ break;
+ default:
+ abort ();
+ }
+ fragP->fr_literal[offset + 1] = opcode;
+
+ /* Create a fixup for the reversed conditional branch. */
+ sprintf (buf, ".%s_%ld", FAKE_LABEL_NAME, label_count++);
+ fix_new (fragP, fragP->fr_fix + 2, 1,
+ symbol_new (buf, sec, 0, fragP->fr_next),
+ fragP->fr_offset + 2, 1, BFD_RELOC_8_PCREL);
+
+ /* Now create the unconditional branch + fixup to the
+ final target. */
+ fragP->fr_literal[offset + 3] = 0xdc;
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 1, 1, BFD_RELOC_32_PCREL);
+ fragP->fr_var = 0;
+ fragP->fr_fix += 8;
+ }
+ else
+ abort ();
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin (void)
+{
+ char *prev_name = "";
+ const struct mn10300_opcode *op;
+
+ mn10300_hash = hash_new ();
+
+ /* Insert unique names into hash table. The MN10300 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+
+ op = mn10300_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (mn10300_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ /* Set the default machine type. */
+#ifdef TE_LINUX
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, AM33_2))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = AM33_2;
+#else
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, MN103))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = MN103;
+#endif
+}
+
+static symbolS *GOT_symbol;
+
+static inline int
+mn10300_PIC_related_p (symbolS *sym)
+{
+ expressionS *exp;
+
+ if (! sym)
+ return 0;
+
+ if (sym == GOT_symbol)
+ return 1;
+
+ exp = symbol_get_value_expression (sym);
+
+ return (exp->X_op == O_PIC_reloc
+ || mn10300_PIC_related_p (exp->X_add_symbol)
+ || mn10300_PIC_related_p (exp->X_op_symbol));
+}
+
+static inline int
+mn10300_check_fixup (struct mn10300_fixup *fixup)
+{
+ expressionS *exp = &fixup->exp;
+
+ repeat:
+ switch (exp->X_op)
+ {
+ case O_add:
+ case O_subtract: /* If we're sufficiently unlucky that the label
+ and the expression that references it happen
+ to end up in different frags, the subtract
+ won't be simplified within expression(). */
+ /* The PIC-related operand must be the first operand of a sum. */
+ if (exp != &fixup->exp || mn10300_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+ fixup->reloc = BFD_RELOC_32_GOT_PCREL;
+
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+ goto repeat;
+
+ case O_symbol:
+ if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+ fixup->reloc = BFD_RELOC_32_GOT_PCREL;
+ break;
+
+ case O_PIC_reloc:
+ fixup->reloc = exp->X_md;
+ exp->X_op = O_symbol;
+ if (fixup->reloc == BFD_RELOC_32_PLT_PCREL
+ && fixup->opindex >= 0
+ && (mn10300_operands[fixup->opindex].flags
+ & MN10300_OPERAND_RELAX))
+ return 1;
+ break;
+
+ default:
+ return (mn10300_PIC_related_p (exp->X_add_symbol)
+ || mn10300_PIC_related_p (exp->X_op_symbol));
+ }
+
+ return 0;
+}
+
+void
+mn10300_cons_fix_new (fragS *frag, int off, int size, expressionS *exp,
+ bfd_reloc_code_real_type r ATTRIBUTE_UNUSED)
+{
+ struct mn10300_fixup fixup;
+
+ fixup.opindex = -1;
+ fixup.exp = *exp;
+ fixup.reloc = BFD_RELOC_UNUSED;
+
+ mn10300_check_fixup (&fixup);
+
+ if (fixup.reloc == BFD_RELOC_MN10300_GOT32)
+ switch (size)
+ {
+ case 2:
+ fixup.reloc = BFD_RELOC_MN10300_GOT16;
+ break;
+
+ case 3:
+ fixup.reloc = BFD_RELOC_MN10300_GOT24;
+ break;
+
+ case 4:
+ break;
+
+ default:
+ goto error;
+ }
+ else if (fixup.reloc == BFD_RELOC_UNUSED)
+ switch (size)
+ {
+ case 1:
+ fixup.reloc = BFD_RELOC_8;
+ break;
+
+ case 2:
+ fixup.reloc = BFD_RELOC_16;
+ break;
+
+ case 3:
+ fixup.reloc = BFD_RELOC_24;
+ break;
+
+ case 4:
+ fixup.reloc = BFD_RELOC_32;
+ break;
+
+ default:
+ goto error;
+ }
+ else if (size != 4)
+ {
+ error:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ fixup.reloc = BFD_RELOC_UNUSED;
+ }
+
+ fix_new_exp (frag, off, size, &fixup.exp, 0, fixup.reloc);
+}
+
+static bfd_boolean
+check_operand (const struct mn10300_operand *operand,
+ offsetT val)
+{
+ /* No need to check 32bit operands for a bit. Note that
+ MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
+ if (operand->bits != 32
+ && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
+ {
+ long min, max;
+ offsetT test;
+ int bits;
+
+ bits = operand->bits;
+ if (operand->flags & MN10300_OPERAND_24BIT)
+ bits = 24;
+
+ if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+mn10300_insert_operand (unsigned long *insnp,
+ unsigned long *extensionp,
+ const struct mn10300_operand *operand,
+ offsetT val,
+ char *file,
+ unsigned int line,
+ unsigned int shift)
+{
+ /* No need to check 32bit operands for a bit. Note that
+ MN10300_OPERAND_SPLIT is an implicit 32bit operand. */
+ if (operand->bits != 32
+ && (operand->flags & MN10300_OPERAND_SPLIT) == 0)
+ {
+ long min, max;
+ offsetT test;
+ int bits;
+
+ bits = operand->bits;
+ if (operand->flags & MN10300_OPERAND_24BIT)
+ bits = 24;
+
+ if ((operand->flags & MN10300_OPERAND_SIGNED) != 0)
+ {
+ max = (1 << (bits - 1)) - 1;
+ min = - (1 << (bits - 1));
+ }
+ else
+ {
+ max = (1 << bits) - 1;
+ min = 0;
+ }
+
+ test = val;
+
+ if (test < (offsetT) min || test > (offsetT) max)
+ as_warn_value_out_of_range (_("operand"), test, (offsetT) min, (offsetT) max, file, line);
+ }
+
+ if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
+ {
+ *insnp |= (val >> (32 - operand->bits)) & ((1 << operand->bits) - 1);
+ *extensionp |= ((val & ((1 << (32 - operand->bits)) - 1))
+ << operand->shift);
+ }
+ else if ((operand->flags & MN10300_OPERAND_24BIT) != 0)
+ {
+ *insnp |= (val >> (24 - operand->bits)) & ((1 << operand->bits) - 1);
+ *extensionp |= ((val & ((1 << (24 - operand->bits)) - 1))
+ << operand->shift);
+ }
+ else if ((operand->flags & (MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG)))
+ {
+ /* See devo/opcodes/m10300-opc.c just before #define FSM0 for an
+ explanation of these variables. Note that FMT-implied shifts
+ are not taken into account for FP registers. */
+ unsigned long mask_low, mask_high;
+ int shl_low, shr_high, shl_high;
+
+ switch (operand->bits)
+ {
+ case 5:
+ /* Handle regular FP registers. */
+ if (operand->shift >= 0)
+ {
+ /* This is an `m' register. */
+ shl_low = operand->shift;
+ shl_high = 8 + (8 & shl_low) + (shl_low & 4) / 4;
+ }
+ else
+ {
+ /* This is an `n' register. */
+ shl_low = -operand->shift;
+ shl_high = shl_low / 4;
+ }
+
+ mask_low = 0x0f;
+ mask_high = 0x10;
+ shr_high = 4;
+ break;
+
+ case 3:
+ /* Handle accumulators. */
+ shl_low = -operand->shift;
+ shl_high = 0;
+ mask_low = 0x03;
+ mask_high = 0x04;
+ shr_high = 2;
+ break;
+
+ default:
+ abort ();
+ }
+ *insnp |= ((((val & mask_high) >> shr_high) << shl_high)
+ | ((val & mask_low) << shl_low));
+ }
+ else if ((operand->flags & MN10300_OPERAND_EXTENDED) == 0)
+ {
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
+ *insnp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + operand->bits));
+ }
+ else
+ {
+ *extensionp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift));
+
+ if ((operand->flags & MN10300_OPERAND_REPEATED) != 0)
+ *extensionp |= (((long) val & ((1 << operand->bits) - 1))
+ << (operand->shift + shift + operand->bits));
+ }
+}
+
+void
+md_assemble (char *str)
+{
+ char *s;
+ struct mn10300_opcode *opcode;
+ struct mn10300_opcode *next_opcode;
+ const unsigned char *opindex_ptr;
+ int next_opindex, relaxable;
+ unsigned long insn, extension, size = 0;
+ char *f;
+ int i;
+ int match;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && !ISSPACE (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Find the first opcode with the proper name. */
+ opcode = (struct mn10300_opcode *) hash_find (mn10300_hash, str);
+ if (opcode == NULL)
+ {
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ return;
+ }
+
+ str = s;
+ while (ISSPACE (*str))
+ ++str;
+
+ input_line_pointer = str;
+
+ for (;;)
+ {
+ const char *errmsg;
+ int op_idx;
+ char *hold;
+ int extra_shift = 0;
+
+ errmsg = _("Invalid opcode/operands");
+
+ /* Reset the array of register operands. */
+ memset (mn10300_reg_operands, -1, sizeof (mn10300_reg_operands));
+
+ relaxable = 0;
+ fc = 0;
+ match = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extension = 0;
+
+ /* If the instruction is not available on the current machine
+ then it can not possibly match. */
+ if (opcode->machine
+ && !(opcode->machine == AM33_2 && HAVE_AM33_2)
+ && !(opcode->machine == AM33 && HAVE_AM33)
+ && !(opcode->machine == AM30 && HAVE_AM30))
+ goto error;
+
+ for (op_idx = 1, opindex_ptr = opcode->operands;
+ *opindex_ptr != 0;
+ opindex_ptr++, op_idx++)
+ {
+ const struct mn10300_operand *operand;
+ expressionS ex;
+
+ if (next_opindex == 0)
+ {
+ operand = &mn10300_operands[*opindex_ptr];
+ }
+ else
+ {
+ operand = &mn10300_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+
+ if (operand->flags & MN10300_OPERAND_RELAX)
+ relaxable = 1;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ if (operand->flags & MN10300_OPERAND_PAREN)
+ {
+ if (*input_line_pointer != ')' && *input_line_pointer != '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ input_line_pointer++;
+ goto keep_going;
+ }
+ /* See if we can match the operands. */
+ else if (operand->flags & MN10300_OPERAND_DREG)
+ {
+ if (!data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_AREG)
+ {
+ if (!address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_SP)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "sp") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_RREG)
+ {
+ if (!r_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_XRREG)
+ {
+ if (!xr_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_FSREG)
+ {
+ if (!float_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_FDREG)
+ {
+ if (!double_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ else if (operand->flags & MN10300_OPERAND_FPCR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "fpcr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_USP)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "usp") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_SSP)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "ssp") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_MSP)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "msp") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_PC)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "pc") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_EPSW)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "epsw") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_PLUS)
+ {
+ if (*input_line_pointer != '+')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ input_line_pointer++;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_PSW)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "psw") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_MDR)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcasecmp (start, "mdr") != 0)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ *input_line_pointer = c;
+ goto keep_going;
+ }
+ else if (operand->flags & MN10300_OPERAND_REG_LIST)
+ {
+ unsigned int value = 0;
+ if (*input_line_pointer != '[')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* Eat the '['. */
+ input_line_pointer++;
+
+ /* We used to reject a null register list here; however,
+ we accept it now so the compiler can emit "call"
+ instructions for all calls to named functions.
+
+ The linker can then fill in the appropriate bits for the
+ register list and stack size or change the instruction
+ into a "calls" if using "call" is not profitable. */
+ while (*input_line_pointer != ']')
+ {
+ char *start;
+ char c;
+
+ if (*input_line_pointer == ',')
+ input_line_pointer++;
+
+ start = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcasecmp (start, "d2") == 0)
+ {
+ value |= 0x80;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "d3") == 0)
+ {
+ value |= 0x40;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "a2") == 0)
+ {
+ value |= 0x20;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "a3") == 0)
+ {
+ value |= 0x10;
+ *input_line_pointer = c;
+ }
+ else if (strcasecmp (start, "other") == 0)
+ {
+ value |= 0x08;
+ *input_line_pointer = c;
+ }
+ else if (HAVE_AM33
+ && strcasecmp (start, "exreg0") == 0)
+ {
+ value |= 0x04;
+ *input_line_pointer = c;
+ }
+ else if (HAVE_AM33
+ && strcasecmp (start, "exreg1") == 0)
+ {
+ value |= 0x02;
+ *input_line_pointer = c;
+ }
+ else if (HAVE_AM33
+ && strcasecmp (start, "exother") == 0)
+ {
+ value |= 0x01;
+ *input_line_pointer = c;
+ }
+ else if (HAVE_AM33
+ && strcasecmp (start, "all") == 0)
+ {
+ value |= 0xff;
+ *input_line_pointer = c;
+ }
+ else
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ }
+ input_line_pointer++;
+ mn10300_insert_operand (& insn, & extension, operand,
+ value, NULL, 0, 0);
+ goto keep_going;
+
+ }
+ else if (data_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (address_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (other_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (HAVE_AM33 && r_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (HAVE_AM33 && xr_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (HAVE_AM33_2 && float_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (HAVE_AM33_2 && double_register_name (&ex))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else if (*str == ')' || *str == '(')
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+ else
+ {
+ expression (&ex);
+ }
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+ case O_register:
+ {
+ int mask;
+
+ mask = MN10300_OPERAND_DREG | MN10300_OPERAND_AREG;
+ if (HAVE_AM33)
+ mask |= MN10300_OPERAND_RREG | MN10300_OPERAND_XRREG;
+ if (HAVE_AM33_2)
+ mask |= MN10300_OPERAND_FSREG | MN10300_OPERAND_FDREG;
+ if ((operand->flags & mask) == 0)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ if (opcode->format == FMT_D1 || opcode->format == FMT_S1)
+ extra_shift = 8;
+ else if (opcode->format == FMT_D2
+ || opcode->format == FMT_D4
+ || opcode->format == FMT_S2
+ || opcode->format == FMT_S4
+ || opcode->format == FMT_S6
+ || opcode->format == FMT_D5)
+ extra_shift = 16;
+ else if (opcode->format == FMT_D7)
+ extra_shift = 8;
+ else if (opcode->format == FMT_D8 || opcode->format == FMT_D9)
+ extra_shift = 8;
+ else
+ extra_shift = 0;
+
+ mn10300_insert_operand (& insn, & extension, operand,
+ ex.X_add_number, NULL,
+ 0, extra_shift);
+
+ /* And note the register number in the register array. */
+ mn10300_reg_operands[op_idx - 1] = ex.X_add_number;
+ break;
+ }
+
+ case O_constant:
+ /* If this operand can be promoted, and it doesn't
+ fit into the allocated bitfield for this insn,
+ then promote it (ie this opcode does not match). */
+ if (operand->flags
+ & (MN10300_OPERAND_PROMOTE | MN10300_OPERAND_RELAX)
+ && !check_operand (operand, ex.X_add_number))
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ mn10300_insert_operand (& insn, & extension, operand,
+ ex.X_add_number, NULL, 0, 0);
+ break;
+
+ default:
+ /* If this operand can be promoted, then this opcode didn't
+ match since we can't know if it needed promotion! */
+ if (operand->flags & MN10300_OPERAND_PROMOTE)
+ {
+ input_line_pointer = hold;
+ str = hold;
+ goto error;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ if (mn10300_check_fixup (& fixups[fc]))
+ goto error;
+ ++fc;
+ break;
+ }
+
+keep_going:
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',')
+ ++str;
+ }
+
+ /* Make sure we used all the operands! */
+ if (*str != ',')
+ match = 1;
+
+ /* If this instruction has registers that must not match, verify
+ that they do indeed not match. */
+ if (opcode->no_match_operands)
+ {
+ /* Look at each operand to see if it's marked. */
+ for (i = 0; i < MN10300_MAX_OPERANDS; i++)
+ {
+ if ((1 << i) & opcode->no_match_operands)
+ {
+ int j;
+
+ /* operand I is marked. Check that it does not match any
+ operands > I which are marked. */
+ for (j = i + 1; j < MN10300_MAX_OPERANDS; j++)
+ {
+ if (((1 << j) & opcode->no_match_operands)
+ && mn10300_reg_operands[i] == mn10300_reg_operands[j])
+ {
+ errmsg = _("Invalid register specification.");
+ match = 0;
+ goto error;
+ }
+ }
+ }
+ }
+ }
+
+ error:
+ if (match == 0)
+ {
+ next_opcode = opcode + 1;
+ if (!strcmp (next_opcode->name, opcode->name))
+ {
+ opcode = next_opcode;
+ continue;
+ }
+
+ as_bad ("%s", errmsg);
+ return;
+ }
+ break;
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+ input_line_pointer = str;
+
+ /* Determine the size of the instruction. */
+ if (opcode->format == FMT_S0)
+ size = 1;
+
+ if (opcode->format == FMT_S1 || opcode->format == FMT_D0)
+ size = 2;
+
+ if (opcode->format == FMT_S2 || opcode->format == FMT_D1)
+ size = 3;
+
+ if (opcode->format == FMT_D6)
+ size = 3;
+
+ if (opcode->format == FMT_D7 || opcode->format == FMT_D10)
+ size = 4;
+
+ if (opcode->format == FMT_D8)
+ size = 6;
+
+ if (opcode->format == FMT_D9)
+ size = 7;
+
+ if (opcode->format == FMT_S4)
+ size = 5;
+
+ if (opcode->format == FMT_S6 || opcode->format == FMT_D5)
+ size = 7;
+
+ if (opcode->format == FMT_D2)
+ size = 4;
+
+ if (opcode->format == FMT_D3)
+ size = 5;
+
+ if (opcode->format == FMT_D4)
+ size = 6;
+
+ if (relaxable && fc > 0)
+ {
+ /* On a 64-bit host the size of an 'int' is not the same
+ as the size of a pointer, so we need a union to convert
+ the opindex field of the fr_cgen structure into a char *
+ so that it can be stored in the frag. We do not have
+ to worry about loosing accuracy as we are not going to
+ be even close to the 32bit limit of the int. */
+ union
+ {
+ int opindex;
+ char * ptr;
+ }
+ opindex_converter;
+ int type;
+
+ /* We want to anchor the line info to the previous frag (if
+ there isn't one, create it), so that, when the insn is
+ resized, we still get the right address for the beginning of
+ the region. */
+ f = frag_more (0);
+ dwarf2_emit_insn (0);
+
+ /* bCC */
+ if (size == 2)
+ {
+ /* Handle bra specially. Basically treat it like jmp so
+ that we automatically handle 8, 16 and 32 bit offsets
+ correctly as well as jumps to an undefined address.
+
+ It is also important to not treat it like other bCC
+ instructions since the long forms of bra is different
+ from other bCC instructions. */
+ if (opcode->opcode == 0xca00)
+ type = 10;
+ else
+ type = 0;
+ }
+ /* call */
+ else if (size == 5)
+ type = 6;
+ /* calls */
+ else if (size == 4)
+ type = 8;
+ /* jmp */
+ else if (size == 3 && opcode->opcode == 0xcc0000)
+ type = 10;
+ else if (size == 3 && (opcode->opcode & 0xfff000) == 0xf8d000)
+ type = 13;
+ /* bCC (uncommon cases) */
+ else
+ type = 3;
+
+ opindex_converter.opindex = fixups[0].opindex;
+ f = frag_var (rs_machine_dependent, 8, 8 - size, type,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ opindex_converter.ptr);
+
+ /* This is pretty hokey. We basically just care about the
+ opcode, so we have to write out the first word big endian.
+
+ The exception is "call", which has two operands that we
+ care about.
+
+ The first operand (the register list) happens to be in the
+ first instruction word, and will be in the right place if
+ we output the first word in big endian mode.
+
+ The second operand (stack size) is in the extension word,
+ and we want it to appear as the first character in the extension
+ word (as it appears in memory). Luckily, writing the extension
+ word in big endian format will do what we want. */
+ number_to_chars_bigendian (f, insn, size > 4 ? 4 : size);
+ if (size > 8)
+ {
+ number_to_chars_bigendian (f + 4, extension, 4);
+ number_to_chars_bigendian (f + 8, 0, size - 8);
+ }
+ else if (size > 4)
+ number_to_chars_bigendian (f + 4, extension, size - 4);
+ }
+ else
+ {
+ /* Allocate space for the instruction. */
+ f = frag_more (size);
+
+ /* Fill in bytes for the instruction. Note that opcode fields
+ are written big-endian, 16 & 32bit immediates are written
+ little endian. Egad. */
+ if (opcode->format == FMT_S0
+ || opcode->format == FMT_S1
+ || opcode->format == FMT_D0
+ || opcode->format == FMT_D6
+ || opcode->format == FMT_D7
+ || opcode->format == FMT_D10
+ || opcode->format == FMT_D1)
+ {
+ number_to_chars_bigendian (f, insn, size);
+ }
+ else if (opcode->format == FMT_S2
+ && opcode->opcode != 0xdf0000
+ && opcode->opcode != 0xde0000)
+ {
+ /* A format S2 instruction that is _not_ "ret" and "retf". */
+ number_to_chars_bigendian (f, (insn >> 16) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_S2)
+ {
+ /* This must be a ret or retf, which is written entirely in
+ big-endian format. */
+ number_to_chars_bigendian (f, insn, 3);
+ }
+ else if (opcode->format == FMT_S4
+ && opcode->opcode != 0xdc000000)
+ {
+ /* This must be a format S4 "call" instruction. What a pain. */
+ unsigned long temp = (insn >> 8) & 0xffff;
+ number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, temp, 2);
+ number_to_chars_bigendian (f + 3, insn & 0xff, 1);
+ number_to_chars_bigendian (f + 4, extension & 0xff, 1);
+ }
+ else if (opcode->format == FMT_S4)
+ {
+ /* This must be a format S4 "jmp" instruction. */
+ unsigned long temp = ((insn & 0xffffff) << 8) | (extension & 0xff);
+ number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, temp, 4);
+ }
+ else if (opcode->format == FMT_S6)
+ {
+ unsigned long temp = ((insn & 0xffffff) << 8)
+ | ((extension >> 16) & 0xff);
+ number_to_chars_bigendian (f, (insn >> 24) & 0xff, 1);
+ number_to_chars_littleendian (f + 1, temp, 4);
+ number_to_chars_bigendian (f + 5, (extension >> 8) & 0xff, 1);
+ number_to_chars_bigendian (f + 6, extension & 0xff, 1);
+ }
+ else if (opcode->format == FMT_D2
+ && opcode->opcode != 0xfaf80000
+ && opcode->opcode != 0xfaf00000
+ && opcode->opcode != 0xfaf40000)
+ {
+ /* A format D2 instruction where the 16bit immediate is
+ really a single 16bit value, not two 8bit values. */
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ }
+ else if (opcode->format == FMT_D2)
+ {
+ /* A format D2 instruction where the 16bit immediate
+ is really two 8bit immediates. */
+ number_to_chars_bigendian (f, insn, 4);
+ }
+ else if (opcode->format == FMT_D3)
+ {
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, insn & 0xffff, 2);
+ number_to_chars_bigendian (f + 4, extension & 0xff, 1);
+ }
+ else if (opcode->format == FMT_D4)
+ {
+ unsigned long temp = ((insn & 0xffff) << 16) | (extension & 0xffff);
+
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, temp, 4);
+ }
+ else if (opcode->format == FMT_D5)
+ {
+ unsigned long temp = (((insn & 0xffff) << 16)
+ | ((extension >> 8) & 0xffff));
+
+ number_to_chars_bigendian (f, (insn >> 16) & 0xffff, 2);
+ number_to_chars_littleendian (f + 2, temp, 4);
+ number_to_chars_bigendian (f + 6, extension & 0xff, 1);
+ }
+ else if (opcode->format == FMT_D8)
+ {
+ unsigned long temp = ((insn & 0xff) << 16) | (extension & 0xffff);
+
+ number_to_chars_bigendian (f, (insn >> 8) & 0xffffff, 3);
+ number_to_chars_bigendian (f + 3, (temp & 0xff), 1);
+ number_to_chars_littleendian (f + 4, temp >> 8, 2);
+ }
+ else if (opcode->format == FMT_D9)
+ {
+ unsigned long temp = ((insn & 0xff) << 24) | (extension & 0xffffff);
+
+ number_to_chars_bigendian (f, (insn >> 8) & 0xffffff, 3);
+ number_to_chars_littleendian (f + 3, temp, 4);
+ }
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct mn10300_operand *operand;
+ int reloc_size;
+
+ operand = &mn10300_operands[fixups[i].opindex];
+ if (fixups[i].reloc != BFD_RELOC_UNUSED
+ && fixups[i].reloc != BFD_RELOC_32_GOT_PCREL
+ && fixups[i].reloc != BFD_RELOC_32_GOTOFF
+ && fixups[i].reloc != BFD_RELOC_32_PLT_PCREL
+ && fixups[i].reloc != BFD_RELOC_MN10300_TLS_GD
+ && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LD
+ && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LDO
+ && fixups[i].reloc != BFD_RELOC_MN10300_TLS_GOTIE
+ && fixups[i].reloc != BFD_RELOC_MN10300_TLS_IE
+ && fixups[i].reloc != BFD_RELOC_MN10300_TLS_LE
+ && fixups[i].reloc != BFD_RELOC_MN10300_GOT32)
+ {
+ reloc_howto_type *reloc_howto;
+ int offset;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput,
+ fixups[i].reloc);
+
+ if (!reloc_howto)
+ abort ();
+
+ reloc_size = bfd_get_reloc_size (reloc_howto);
+
+ if (reloc_size < 1 || reloc_size > 4)
+ abort ();
+
+ offset = 4 - size;
+ fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ reloc_size, &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+ }
+ else
+ {
+ int reloc, pcrel, offset;
+ fixS *fixP;
+
+ reloc = BFD_RELOC_NONE;
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ reloc = fixups[i].reloc;
+ /* How big is the reloc? Remember SPLIT relocs are
+ implicitly 32bits. */
+ if ((operand->flags & MN10300_OPERAND_SPLIT) != 0)
+ reloc_size = 32;
+ else if ((operand->flags & MN10300_OPERAND_24BIT) != 0)
+ reloc_size = 24;
+ else
+ reloc_size = operand->bits;
+
+ /* Is the reloc pc-relative? */
+ pcrel = (operand->flags & MN10300_OPERAND_PCREL) != 0;
+ if (reloc != BFD_RELOC_NONE)
+ pcrel = bfd_reloc_type_lookup (stdoutput, reloc)->pc_relative;
+
+ offset = size - (reloc_size + operand->shift) / 8;
+
+ /* Choose a proper BFD relocation type. */
+ if (reloc != BFD_RELOC_NONE)
+ ;
+ else if (pcrel)
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_32_PCREL;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16_PCREL;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8_PCREL;
+ else
+ abort ();
+ }
+ else
+ {
+ if (reloc_size == 32)
+ reloc = BFD_RELOC_32;
+ else if (reloc_size == 16)
+ reloc = BFD_RELOC_16;
+ else if (reloc_size == 8)
+ reloc = BFD_RELOC_8;
+ else
+ abort ();
+ }
+
+ fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset,
+ reloc_size / 8, &fixups[i].exp, pcrel,
+ ((bfd_reloc_code_real_type) reloc));
+
+ if (pcrel)
+ fixP->fx_offset += offset;
+ }
+ }
+
+ dwarf2_emit_insn (size);
+ }
+
+ /* Label this frag as one that contains instructions. */
+ frag_now->tc_frag_data = TRUE;
+}
+
+/* If while processing a fixup, a reloc really needs to be created
+ then it is done here. */
+
+arelent **
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ static arelent * no_relocs = NULL;
+ static arelent * relocs[MAX_RELOC_EXPANSION + 1];
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ free (reloc);
+ return & no_relocs;
+ }
+
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ relocs[0] = reloc;
+ relocs[1] = NULL;
+
+ if (fixp->fx_subsy
+ && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ {
+ fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_subsy = NULL;
+ }
+
+ if (fixp->fx_addsy && fixp->fx_subsy)
+ {
+ asection *asec, *ssec;
+
+ asec = S_GET_SEGMENT (fixp->fx_addsy);
+ ssec = S_GET_SEGMENT (fixp->fx_subsy);
+
+ /* If we have a difference between two (non-absolute) symbols we must
+ generate two relocs (one for each symbol) and allow the linker to
+ resolve them - relaxation may change the distances between symbols,
+ even local symbols defined in the same section. */
+ if (ssec != absolute_section || asec != absolute_section)
+ {
+ arelent * reloc2 = xmalloc (sizeof * reloc);
+
+ relocs[0] = reloc2;
+ relocs[1] = reloc;
+
+ reloc2->address = reloc->address;
+ reloc2->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_MN10300_SYM_DIFF);
+ reloc2->addend = - S_GET_VALUE (fixp->fx_subsy);
+ reloc2->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+
+ reloc->addend = fixp->fx_offset;
+ if (asec == absolute_section)
+ {
+ reloc->addend += S_GET_VALUE (fixp->fx_addsy);
+ reloc->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ }
+ else
+ {
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ }
+
+ fixp->fx_pcrel = 0;
+ fixp->fx_done = 1;
+ return relocs;
+ }
+ else
+ {
+ char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;
+
+ reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
+ - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (fixpos, reloc->addend, 1);
+ break;
+
+ case BFD_RELOC_16:
+ md_number_to_chars (fixpos, reloc->addend, 2);
+ break;
+
+ case BFD_RELOC_24:
+ md_number_to_chars (fixpos, reloc->addend, 3);
+ break;
+
+ case BFD_RELOC_32:
+ md_number_to_chars (fixpos, reloc->addend, 4);
+ break;
+
+ default:
+ reloc->sym_ptr_ptr
+ = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
+ return relocs;
+ }
+
+ free (reloc);
+ return & no_relocs;
+ }
+ }
+ else
+ {
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->addend = fixp->fx_offset;
+ }
+ return relocs;
+}
+
+/* Returns true iff the symbol attached to the frag is at a known location
+ in the given section, (and hence the relocation to it can be relaxed by
+ the assembler). */
+static inline bfd_boolean
+has_known_symbol_location (fragS * fragp, asection * sec)
+{
+ symbolS * sym = fragp->fr_symbol;
+
+ return sym != NULL
+ && S_IS_DEFINED (sym)
+ && ! S_IS_WEAK (sym)
+ && S_GET_SEGMENT (sym) == sec;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *seg)
+{
+ if (fragp->fr_subtype == 6
+ && ! has_known_symbol_location (fragp, seg))
+ fragp->fr_subtype = 7;
+ else if (fragp->fr_subtype == 8
+ && ! has_known_symbol_location (fragp, seg))
+ fragp->fr_subtype = 9;
+ else if (fragp->fr_subtype == 10
+ && ! has_known_symbol_location (fragp, seg))
+ fragp->fr_subtype = 12;
+
+ if (fragp->fr_subtype == 13)
+ return 3;
+
+ if (fragp->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
+}
+
+long
+md_pcrel_from (fixS *fixp)
+{
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (!S_IS_DEFINED (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)))
+ /* The symbol is undefined or weak. Let the linker figure it out. */
+ return 0;
+
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+void
+md_apply_fix (fixS * fixP, valueT * valP, segT seg)
+{
+ char * fixpos = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int size = 0;
+ int value = (int) * valP;
+
+ gas_assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+
+ /* This should never happen. */
+ if (seg->flags & SEC_ALLOC)
+ abort ();
+
+ /* The value we are passed in *valuep includes the symbol values.
+ If we are doing this relocation the code in write.c is going to
+ call bfd_install_relocation, which is also going to use the symbol
+ value. That means that if the reloc is fully resolved we want to
+ use *valuep since bfd_install_relocation is not being used.
+
+ However, if the reloc is not fully resolved we do not want to use
+ *valuep, and must use fx_offset instead. However, if the reloc
+ is PC relative, we do want to use *valuep since it includes the
+ result of md_pcrel_from. */
+ if (fixP->fx_addsy != NULL && ! fixP->fx_pcrel)
+ value = fixP->fx_offset;
+
+ /* If the fix is relative to a symbol which is not defined, or not
+ in the same segment as the fix, we cannot resolve it here. */
+ if (fixP->fx_addsy != NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || (S_GET_SEGMENT (fixP->fx_addsy) != seg)))
+ {
+ fixP->fx_done = 0;
+ return;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ case BFD_RELOC_8_PCREL:
+ size = 1;
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ size = 2;
+ break;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ size = 4;
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ case BFD_RELOC_MN10300_ALIGN:
+ fixP->fx_done = 1;
+ return;
+
+ case BFD_RELOC_NONE:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Bad relocation fixup type (%d)"), fixP->fx_r_type);
+ }
+
+ md_number_to_chars (fixpos, value, size);
+
+ /* If a symbol remains, pass the fixup, as a reloc, onto the linker. */
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+}
+
+/* Return zero if the fixup in fixp should be left alone and not
+ adjusted. */
+
+bfd_boolean
+mn10300_fix_adjustable (struct fix *fixp)
+{
+ if (fixp->fx_pcrel)
+ {
+ if (TC_FORCE_RELOCATION_LOCAL (fixp))
+ return FALSE;
+ }
+ /* Non-relative relocs can (and must) be adjusted if they do
+ not meet the criteria below, or the generic criteria. */
+ else if (TC_FORCE_RELOCATION (fixp))
+ return FALSE;
+
+ /* Do not adjust relocations involving symbols in code sections,
+ because it breaks linker relaxations. This could be fixed in the
+ linker, but this fix is simpler, and it pretty much only affects
+ object size a little bit. */
+ if (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE)
+ return FALSE;
+
+ /* Likewise, do not adjust symbols that won't be merged, or debug
+ symbols, because they too break relaxation. We do want to adjust
+ other mergable symbols, like .rodata, because code relaxations
+ need section-relative symbols to properly relax them. */
+ if (! (S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_MERGE))
+ return FALSE;
+
+ if (strncmp (S_GET_SEGMENT (fixp->fx_addsy)->name, ".debug", 6) == 0)
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+set_arch_mach (int mach)
+{
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_mn10300, mach))
+ as_warn (_("could not set architecture and machine"));
+
+ current_machine = mach;
+}
+
+static inline char *
+mn10300_end_of_match (char *cont, char *what)
+{
+ int len = strlen (what);
+
+ if (strncmp (cont, what, strlen (what)) == 0
+ && ! is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+int
+mn10300_parse_name (char const *name,
+ expressionS *exprP,
+ enum expr_mode mode,
+ char *nextcharP)
+{
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ segT segment;
+
+ exprP->X_op_symbol = NULL;
+
+ if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (! GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+ no_suffix:
+ /* If we have an absolute symbol or a reg,
+ then we know its value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (mode != expr_defer && segment == absolute_section)
+ {
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else if (mode != expr_defer && segment == reg_section)
+ {
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else
+ {
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+ }
+
+ return 1;
+ }
+
+ exprP->X_add_symbol = symbol_find_or_make (name);
+
+ if (*nextcharP != '@')
+ goto no_suffix;
+ else if ((next_end = mn10300_end_of_match (next + 1, "GOTOFF")))
+ reloc_type = BFD_RELOC_32_GOTOFF;
+ else if ((next_end = mn10300_end_of_match (next + 1, "GOT")))
+ reloc_type = BFD_RELOC_MN10300_GOT32;
+ else if ((next_end = mn10300_end_of_match (next + 1, "PLT")))
+ reloc_type = BFD_RELOC_32_PLT_PCREL;
+ else if ((next_end = mn10300_end_of_match (next + 1, "tlsgd")))
+ reloc_type = BFD_RELOC_MN10300_TLS_GD;
+ else if ((next_end = mn10300_end_of_match (next + 1, "tlsldm")))
+ reloc_type = BFD_RELOC_MN10300_TLS_LD;
+ else if ((next_end = mn10300_end_of_match (next + 1, "dtpoff")))
+ reloc_type = BFD_RELOC_MN10300_TLS_LDO;
+ else if ((next_end = mn10300_end_of_match (next + 1, "gotntpoff")))
+ reloc_type = BFD_RELOC_MN10300_TLS_GOTIE;
+ else if ((next_end = mn10300_end_of_match (next + 1, "indntpoff")))
+ reloc_type = BFD_RELOC_MN10300_TLS_IE;
+ else if ((next_end = mn10300_end_of_match (next + 1, "tpoff")))
+ reloc_type = BFD_RELOC_MN10300_TLS_LE;
+ else
+ goto no_suffix;
+
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = O_PIC_reloc;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "am30", set_arch_mach, AM30 },
+ { "am33", set_arch_mach, AM33 },
+ { "am33_2", set_arch_mach, AM33_2 },
+ { "mn10300", set_arch_mach, MN103 },
+ {NULL, 0, 0}
+};
+
+/* Returns FALSE if there is some mn10300 specific reason why the
+ subtraction of two same-section symbols cannot be computed by
+ the assembler. */
+
+bfd_boolean
+mn10300_allow_local_subtract (expressionS * left, expressionS * right, segT section)
+{
+ bfd_boolean result;
+ fragS * left_frag;
+ fragS * right_frag;
+ fragS * frag;
+
+ /* If we are not performing linker relaxation then we have nothing
+ to worry about. */
+ if (linkrelax == 0)
+ return TRUE;
+
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ /* Otherwise we have to scan the fragments between the two symbols.
+ If any instructions are found then we have to assume that linker
+ relaxation may change their size and so we must delay resolving
+ the subtraction until the final link. */
+ left_frag = symbol_get_frag (left->X_add_symbol);
+ right_frag = symbol_get_frag (right->X_add_symbol);
+
+ if (left_frag == right_frag)
+ return ! left_frag->tc_frag_data;
+
+ result = TRUE;
+ for (frag = left_frag; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->tc_frag_data)
+ result = FALSE;
+ if (frag == right_frag)
+ break;
+ }
+
+ if (frag == NULL)
+ for (frag = right_frag; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->tc_frag_data)
+ result = FALSE;
+ if (frag == left_frag)
+ break;
+ }
+
+ if (frag == NULL)
+ /* The two symbols are on disjoint fragment chains
+ - we cannot possibly compute their difference. */
+ return FALSE;
+
+ return result;
+}
+
+/* When relaxing, we need to output a reloc for any .align directive
+ that requests alignment to a two byte boundary or larger. */
+
+void
+mn10300_handle_align (fragS *frag)
+{
+ if (linkrelax
+ && (frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 1
+ && now_seg != bss_section
+ /* Do not create relocs for the merging sections - such
+ relocs will prevent the contents from being merged. */
+ && (bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE) == 0)
+ /* Create a new fixup to record the alignment request. The symbol is
+ irrelevent but must be present so we use the absolute section symbol.
+ The offset from the symbol is used to record the power-of-two alignment
+ value. The size is set to 0 because the frag may already be aligned,
+ thus causing cvt_frag_to_fill to reduce the size of the frag to zero. */
+ fix_new (frag, frag->fr_fix, 0, & abs_symbol, frag->fr_offset, FALSE,
+ BFD_RELOC_MN10300_ALIGN);
+}
+
+bfd_boolean
+mn10300_force_relocation (struct fix * fixp)
+{
+ if (linkrelax
+ && (fixp->fx_pcrel
+ || fixp->fx_r_type == BFD_RELOC_MN10300_ALIGN))
+ return TRUE;
+
+ return generic_force_reloc (fixp);
+}
diff --git a/gas/config/tc-mn10300.h b/gas/config/tc-mn10300.h
new file mode 100644
index 0000000..d502430
--- /dev/null
+++ b/gas/config/tc-mn10300.h
@@ -0,0 +1,126 @@
+/* tc-mn10300.h -- Header file for tc-mn10300.c.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_MN10300
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define DIFF_EXPR_OK
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+
+#define TC_FORCE_RELOCATION(FIX) mn10300_force_relocation (FIX)
+extern bfd_boolean mn10300_force_relocation (struct fix *);
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
+ || (FIX)->fx_r_type == BFD_RELOC_MN10300_GOT32 \
+ || (FIX)->fx_r_type == BFD_RELOC_32_GOT_PCREL \
+ || TC_FORCE_RELOCATION (FIX))
+
+#define md_parse_name(NAME, EXPRP, MODE, NEXTCHARP) \
+ mn10300_parse_name ((NAME), (EXPRP), (MODE), (NEXTCHARP))
+int mn10300_parse_name (char const *, expressionS *, enum expr_mode, char *);
+
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP, RELOC) \
+ mn10300_cons_fix_new ((FRAG), (OFF), (LEN), (EXP), (RELOC))
+void mn10300_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+ symbols. The relocation type is stored in X_md. */
+#define O_PIC_reloc O_md1
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_mn10300
+
+#ifdef TE_LINUX
+#define TARGET_FORMAT "elf32-am33lin"
+#else
+#define TARGET_FORMAT "elf32-mn10300"
+#endif
+
+
+/* Do not adjust relocations involving symbols in code sections,
+ because it breaks linker relaxations. This could be fixed in the
+ linker, but this fix is simpler, and it pretty much only affects
+ object size a little bit. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \
+ (((SEC)->flags & SEC_CODE) != 0 \
+ || ! SEG_NORMAL (SEC) \
+ || (FIX)->fx_r_type == BFD_RELOC_MN10300_ALIGN \
+ || TC_FORCE_RELOCATION (FIX))
+
+/* We validate subtract arguments within tc_gen_reloc(), so don't
+ report errors at this point. */
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1
+
+/* Fixup debug sections since we will never relax them. Ideally, we
+ could do away with this and instead check every single fixup with
+ TC_FORCE_RELOCATION and TC_FORCE_RELOCATION_SUB_NAME, verifying
+ that the sections of the referenced symbols (and not the sections
+ in which the fixup appears) may be subject to relaxation. We'd
+ still have to check the section in which the fixup appears, because
+ we want to do some simplifications in debugging info that might
+ break in real code.
+
+ Using the infrastructure in write.c to simplify subtraction fixups
+ would enable us to remove a lot of code from tc_gen_reloc(), but
+ this is simpler, faster, and produces almost the same effect.
+ Also, in the macros above, we can't check whether the fixup is in a
+ debugging section or not, so we have to use this for now. */
+#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_ALLOC)
+
+#define md_operand(x)
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_littleendian
+
+#define tc_fix_adjustable(FIX) mn10300_fix_adjustable (FIX)
+extern bfd_boolean mn10300_fix_adjustable (struct fix *);
+
+/* We do relaxing in the assembler as well as the linker. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+/* The difference between same-section symbols may be affected by linker
+ relaxation, so do not resolve such expressions in the assembler. */
+#define md_allow_local_subtract(l,r,s) mn10300_allow_local_subtract (l, r, s)
+extern bfd_boolean mn10300_allow_local_subtract (expressionS *, expressionS *, segT);
+
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 2
+
+#define TC_FRAG_TYPE bfd_boolean
+
+#define HANDLE_ALIGN(frag) mn10300_handle_align (frag)
+extern void mn10300_handle_align (fragS *);
+
+/* Only allow call frame debug info optimization when linker relaxation is
+ not enabled as otherwise we could generate the DWARF directives without
+ the relocs necessary to patch them up. */
+#define md_allow_eh_opt (linkrelax == 0)
diff --git a/gas/config/tc-moxie.c b/gas/config/tc-moxie.c
new file mode 100644
index 0000000..02a59b7
--- /dev/null
+++ b/gas/config/tc-moxie.c
@@ -0,0 +1,844 @@
+/* tc-moxie.c -- Assemble code for moxie
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* Contributed by Anthony Green <green@moxielogic.com>. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcode/moxie.h"
+#include "elf/moxie.h"
+
+extern const moxie_opc_info_t moxie_opc_info[128];
+
+const char comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "#";
+
+static int pending_reloc;
+static struct hash_control *opcode_hash_control;
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {0, 0, 0}
+};
+
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+const char EXP_CHARS[] = "eE";
+
+static valueT md_chars_to_number (char * buf, int n);
+
+/* Byte order. */
+extern int target_big_endian;
+
+void
+md_operand (expressionS *op __attribute__((unused)))
+{
+ /* Empty for now. */
+}
+
+/* This function is called once, at assembler startup time. It sets
+ up the hash table with all the opcodes in it, and also initializes
+ some aliases for compatibility with other assemblers. */
+
+void
+md_begin (void)
+{
+ int count;
+ const moxie_opc_info_t *opcode;
+ opcode_hash_control = hash_new ();
+
+ /* Insert names into hash table. */
+ for (count = 0, opcode = moxie_form1_opc_info; count++ < 64; opcode++)
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+
+ for (count = 0, opcode = moxie_form2_opc_info; count++ < 4; opcode++)
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+
+ for (count = 0, opcode = moxie_form3_opc_info; count++ < 10; opcode++)
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+
+ target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+}
+
+/* Parse an expression and then restore the input line pointer. */
+
+static char *
+parse_exp_save_ilp (char *s, expressionS *op)
+{
+ char *save = input_line_pointer;
+
+ input_line_pointer = s;
+ expression (op);
+ s = input_line_pointer;
+ input_line_pointer = save;
+ return s;
+}
+
+static int
+parse_register_operand (char **ptr)
+{
+ int reg;
+ char *s = *ptr;
+
+ if (*s != '$')
+ {
+ as_bad (_("expecting register"));
+ ignore_rest_of_line ();
+ return -1;
+ }
+ if (s[1] == 'f' && s[2] == 'p')
+ {
+ *ptr += 3;
+ return 0;
+ }
+ if (s[1] == 's' && s[2] == 'p')
+ {
+ *ptr += 3;
+ return 1;
+ }
+ if (s[1] == 'r')
+ {
+ reg = s[2] - '0';
+ if ((reg < 0) || (reg > 9))
+ {
+ as_bad (_("illegal register number"));
+ ignore_rest_of_line ();
+ return -1;
+ }
+ if (reg == 1)
+ {
+ int r2 = s[3] - '0';
+ if ((r2 >= 0) && (r2 <= 3))
+ {
+ reg = 10 + r2;
+ *ptr += 1;
+ }
+ }
+ }
+ else
+ {
+ as_bad (_("illegal register number"));
+ ignore_rest_of_line ();
+ return -1;
+ }
+
+ *ptr += 3;
+
+ return reg + 2;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to
+ a machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *str)
+{
+ char *op_start;
+ char *op_end;
+
+ moxie_opc_info_t *opcode;
+ char *p;
+ char pend;
+
+ unsigned short iword = 0;
+
+ int nlen = 0;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the op code end. */
+ op_start = str;
+ for (op_end = str;
+ *op_end && !is_end_of_line[*op_end & 0xff] && *op_end != ' ';
+ op_end++)
+ nlen++;
+
+ pend = *op_end;
+ *op_end = 0;
+
+ if (nlen == 0)
+ as_bad (_("can't find opcode "));
+ opcode = (moxie_opc_info_t *) hash_find (opcode_hash_control, op_start);
+ *op_end = pend;
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode %s"), op_start);
+ return;
+ }
+
+ p = frag_more (2);
+
+ switch (opcode->itype)
+ {
+ case MOXIE_F2_A8V:
+ iword = (1<<15) | (opcode->opcode << 12);
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+ int reg;
+ reg = parse_register_operand (&op_end);
+ iword += (reg << 8);
+ if (*op_end != ',')
+ as_warn (_("expecting comma delimited register operands"));
+ op_end++;
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fix_new_exp (frag_now,
+ ((p + (target_big_endian ? 1 : 0)) - frag_now->fr_literal),
+ 1,
+ &arg,
+ 0,
+ BFD_RELOC_8);
+ }
+ break;
+ case MOXIE_F1_AB:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ int dest, src;
+ dest = parse_register_operand (&op_end);
+ if (*op_end != ',')
+ as_warn (_("expecting comma delimited register operands"));
+ op_end++;
+ src = parse_register_operand (&op_end);
+ iword += (dest << 4) + src;
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+ }
+ break;
+ case MOXIE_F1_A4:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+ char *where;
+ int regnum;
+
+ regnum = parse_register_operand (&op_end);
+ while (ISSPACE (*op_end))
+ op_end++;
+
+ iword += (regnum << 4);
+
+ if (*op_end != ',')
+ {
+ as_bad (_("expecting comma delimited operands"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ where = frag_more (4);
+ fix_new_exp (frag_now,
+ (where - frag_now->fr_literal),
+ 4,
+ &arg,
+ 0,
+ BFD_RELOC_32);
+ }
+ break;
+ case MOXIE_F1_M:
+ case MOXIE_F1_4:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+ char *where;
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ where = frag_more (4);
+ fix_new_exp (frag_now,
+ (where - frag_now->fr_literal),
+ 4,
+ &arg,
+ 0,
+ BFD_RELOC_32);
+ }
+ break;
+ case MOXIE_F1_NARG:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+ break;
+ case MOXIE_F1_A:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ int reg;
+ reg = parse_register_operand (&op_end);
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+ iword += (reg << 4);
+ }
+ break;
+ case MOXIE_F1_ABi:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ int a, b;
+ a = parse_register_operand (&op_end);
+ if (*op_end != ',')
+ as_warn (_("expecting comma delimited register operands"));
+ op_end++;
+ if (*op_end != '(')
+ {
+ as_bad (_("expecting indirect register `($rA)'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+ b = parse_register_operand (&op_end);
+ if (*op_end != ')')
+ {
+ as_bad (_("missing closing parenthesis"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+ iword += (a << 4) + b;
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+ }
+ break;
+ case MOXIE_F1_AiB:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ int a, b;
+ if (*op_end != '(')
+ {
+ as_bad (_("expecting indirect register `($rA)'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+ a = parse_register_operand (&op_end);
+ if (*op_end != ')')
+ {
+ as_bad (_("missing closing parenthesis"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+ if (*op_end != ',')
+ as_warn (_("expecting comma delimited register operands"));
+ op_end++;
+ b = parse_register_operand (&op_end);
+ iword += (a << 4) + b;
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+ }
+ break;
+ case MOXIE_F1_4A:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+ char *where;
+ int a;
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ where = frag_more (4);
+ fix_new_exp (frag_now,
+ (where - frag_now->fr_literal),
+ 4,
+ &arg,
+ 0,
+ BFD_RELOC_32);
+
+ if (*op_end != ',')
+ {
+ as_bad (_("expecting comma delimited operands"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+
+ a = parse_register_operand (&op_end);
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+
+ iword += (a << 4);
+ }
+ break;
+ case MOXIE_F1_ABi4:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+ char *offset;
+ int a, b;
+
+ a = parse_register_operand (&op_end);
+ while (ISSPACE (*op_end))
+ op_end++;
+
+ if (*op_end != ',')
+ {
+ as_bad (_("expecting comma delimited operands"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ offset = frag_more (4);
+ fix_new_exp (frag_now,
+ (offset - frag_now->fr_literal),
+ 4,
+ &arg,
+ 0,
+ BFD_RELOC_32);
+
+ if (*op_end != '(')
+ {
+ as_bad (_("expecting indirect register `($rX)'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+ b = parse_register_operand (&op_end);
+ if (*op_end != ')')
+ {
+ as_bad (_("missing closing parenthesis"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+
+ iword += (a << 4) + b;
+ }
+ break;
+ case MOXIE_F1_AiB4:
+ iword = opcode->opcode << 8;
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+ char *offset;
+ int a, b;
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ offset = frag_more (4);
+ fix_new_exp (frag_now,
+ (offset - frag_now->fr_literal),
+ 4,
+ &arg,
+ 0,
+ BFD_RELOC_32);
+
+ if (*op_end != '(')
+ {
+ as_bad (_("expecting indirect register `($rX)'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+ a = parse_register_operand (&op_end);
+ if (*op_end != ')')
+ {
+ as_bad (_("missing closing parenthesis"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+
+ if (*op_end != ',')
+ {
+ as_bad (_("expecting comma delimited operands"));
+ ignore_rest_of_line ();
+ return;
+ }
+ op_end++;
+
+ b = parse_register_operand (&op_end);
+ while (ISSPACE (*op_end))
+ op_end++;
+
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+
+ iword += (a << 4) + b;
+ }
+ break;
+ case MOXIE_F2_NARG:
+ iword = opcode->opcode << 12;
+ while (ISSPACE (*op_end))
+ op_end++;
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+ break;
+ case MOXIE_F3_PCREL:
+ iword = (3<<14) | (opcode->opcode << 10);
+ while (ISSPACE (*op_end))
+ op_end++;
+ {
+ expressionS arg;
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+ fix_new_exp (frag_now,
+ (p - frag_now->fr_literal),
+ 2,
+ &arg,
+ TRUE,
+ BFD_RELOC_MOXIE_10_PCREL);
+ }
+ break;
+ default:
+ abort ();
+ }
+
+ md_number_to_chars (p, iword, 2);
+
+ while (ISSPACE (*op_end))
+ op_end++;
+
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+
+ if (pending_reloc)
+ as_bad (_("Something forgot to clean up\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. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+
+ case 'd':
+ prec = 4;
+ break;
+
+ 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 * 2;
+
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+
+ return NULL;
+}
+
+enum options
+{
+ OPTION_EB = OPTION_MD_BASE,
+ OPTION_EL,
+};
+
+struct option md_longopts[] =
+{
+ { "EB", no_argument, NULL, OPTION_EB},
+ { "EL", no_argument, NULL, OPTION_EL},
+ { NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+const char *md_shortopts = "";
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+ fprintf (stream, _("\
+ -EB assemble for a big endian system (default)\n\
+ -EL assemble for a little endian system\n"));
+}
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix (fixS *fixP ATTRIBUTE_UNUSED,
+ valueT * valP ATTRIBUTE_UNUSED, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ long val = *valP;
+ long newval;
+ long max, min;
+
+ max = min = 0;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ if (target_big_endian)
+ {
+ buf[0] = val >> 24;
+ buf[1] = val >> 16;
+ buf[2] = val >> 8;
+ buf[3] = val >> 0;
+ }
+ else
+ {
+ buf[3] = val >> 24;
+ buf[2] = val >> 16;
+ buf[1] = val >> 8;
+ buf[0] = val >> 0;
+ }
+ buf += 4;
+ break;
+
+ case BFD_RELOC_16:
+ if (target_big_endian)
+ {
+ buf[0] = val >> 8;
+ buf[1] = val >> 0;
+ }
+ else
+ {
+ buf[1] = val >> 8;
+ buf[0] = val >> 0;
+ }
+ buf += 2;
+ break;
+
+ case BFD_RELOC_8:
+ *buf++ = val;
+ break;
+
+ case BFD_RELOC_MOXIE_10_PCREL:
+ if (!val)
+ break;
+ if (val < -1024 || val > 1022)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("pcrel too far BFD_RELOC_MOXIE_10"));
+ /* 11 bit offset even numbered, so we remove right bit. */
+ val >>= 1;
+ newval = md_chars_to_number (buf, 2);
+ newval |= val & 0x03ff;
+ md_number_to_chars (buf, newval, 2);
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (max != 0 && (val < min || val > max))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+/* Put number into target byte order. */
+
+void
+md_number_to_chars (char * ptr, valueT use, int nbytes)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (ptr, use, nbytes);
+ else
+ number_to_chars_littleendian (ptr, use, nbytes);
+}
+
+/* Convert from target byte order to host byte order. */
+
+static valueT
+md_chars_to_number (char * buf, int n)
+{
+ valueT result = 0;
+ unsigned char * where = (unsigned char *) buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
+ }
+
+ return result;
+}
+
+/* Generate a machine-dependent relocation. */
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
+{
+ arelent *relP;
+ bfd_reloc_code_real_type code;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ code = fixP->fx_r_type;
+ break;
+ case BFD_RELOC_MOXIE_10_PCREL:
+ 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 = xmalloc (sizeof (arelent));
+ gas_assert (relP != 0);
+ relP->sym_ptr_ptr = 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;
+
+ /* This is the standard place for KLUDGEs to work around bugs in
+ bfd_install_relocation (first such note in the documentation
+ appears with binutils-2.8).
+
+ That function bfd_install_relocation does the wrong thing with
+ putting stuff into the addend of a reloc (it should stay out) for a
+ weak symbol. The really bad thing is that it adds the
+ "segment-relative offset" of the symbol into the reloc. In this
+ case, the reloc should instead be relative to the symbol with no
+ other offset than the assembly code shows; and since the symbol is
+ weak, any local definition should be ignored until link time (or
+ thereafter).
+ To wit: weaksym+42 should be weaksym+42 in the reloc,
+ not weaksym+(offset_from_segment_of_local_weaksym_definition)
+
+ To "work around" this, we subtract the segment-relative offset of
+ "known" weak symbols. This evens out the extra offset.
+
+ That happens for a.out but not for ELF, since for ELF,
+ bfd_install_relocation uses the "special function" field of the
+ howto, and does not execute the code that needs to be undone. */
+
+ 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;
+}
+
+/* Decide from what point a pc-relative relocation is relative to,
+ relative to the pc-relative fixup. Er, relatively speaking. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ return addr + 4;
+ case BFD_RELOC_MOXIE_10_PCREL:
+ /* Offset is from the end of the instruction. */
+ return addr + 2;
+ default:
+ abort ();
+ return addr;
+ }
+}
diff --git a/gas/config/tc-moxie.h b/gas/config/tc-moxie.h
new file mode 100644
index 0000000..0a88813
--- /dev/null
+++ b/gas/config/tc-moxie.h
@@ -0,0 +1,47 @@
+/* tc-moxie.h -- Header file for tc-moxie.c.
+
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with GAS; see the file COPYING. If not, write to the Free Software
+ Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#define TC_MOXIE 1
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+#define WORKING_DOT_WORD
+
+/* This macro is the BFD architecture to pass to `bfd_set_arch_mach'. */
+#define TARGET_FORMAT (target_big_endian ? "elf32-bigmoxie" : "elf32-littlemoxie")
+
+#define TARGET_ARCH bfd_arch_moxie
+
+#define md_undefined_symbol(NAME) 0
+
+/* These macros must be defined, but is will be a fatal assembler
+ error if we ever hit them. */
+#define md_estimate_size_before_relax(A, B) (as_fatal (_("estimate size\n")), 0)
+#define md_convert_frag(B, S, F) as_fatal (_("convert_frag\n"))
+
+/* If you define this macro, it should return the offset between the
+ address of a PC relative fixup and the position from which the PC
+ relative adjustment should be made. On many processors, the base
+ of a PC relative instruction is the next instruction, so this
+ macro would return the length of an instruction. */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from (FIX)
+extern long md_pcrel_from (struct fix *);
+
+#define md_section_align(SEGMENT, SIZE) (SIZE)
diff --git a/gas/config/tc-msp430.c b/gas/config/tc-msp430.c
new file mode 100644
index 0000000..25ec0ee
--- /dev/null
+++ b/gas/config/tc-msp430.c
@@ -0,0 +1,3936 @@
+/* tc-msp430.c -- Assembler code for the Texas Instruments MSP430
+
+ Copyright (C) 2002-2014 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 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include <limits.h>
+#define PUSH_1X_WORKAROUND
+#include "subsegs.h"
+#include "opcode/msp430.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "elf/msp430.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' is within +-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;
+
+/* Set linkrelax here to avoid fixups in most sections. */
+int linkrelax = 1;
+
+/* GCC uses the some condition codes which we'll
+ implement as new polymorph instructions.
+
+ COND EXPL SHORT JUMP LONG JUMP
+ ===============================================
+ eq == jeq jne +4; br lab
+ ne != jne jeq +4; br lab
+
+ ltn honours no-overflow flag
+ ltn < jn jn +2; jmp +4; br lab
+
+ lt < jl jge +4; br lab
+ ltu < jlo lhs +4; br lab
+ le <= see below
+ leu <= see below
+
+ gt > see below
+ gtu > see below
+ ge >= jge jl +4; br lab
+ geu >= jhs jlo +4; br lab
+ ===============================================
+
+ Therefore, new opcodes are (BranchEQ -> beq; and so on...)
+ beq,bne,blt,bltn,bltu,bge,bgeu
+ 'u' means unsigned compares
+
+ Also, we add 'jump' instruction:
+ jump UNCOND -> jmp br lab
+
+ They will have fmt == 4, and insn_opnumb == number of instruction. */
+
+struct rcodes_s
+{
+ char * name;
+ int index; /* Corresponding insn_opnumb. */
+ int sop; /* Opcode if jump length is short. */
+ long lpos; /* Label position. */
+ long lop0; /* Opcode 1 _word_ (16 bits). */
+ long lop1; /* Opcode second word. */
+ long lop2; /* Opcode third word. */
+};
+
+#define MSP430_RLC(n,i,sop,o1) \
+ {#n, i, sop, 2, (o1 + 2), 0x4010, 0}
+
+static struct rcodes_s msp430_rcodes[] =
+{
+ MSP430_RLC (beq, 0, 0x2400, 0x2000),
+ MSP430_RLC (bne, 1, 0x2000, 0x2400),
+ MSP430_RLC (blt, 2, 0x3800, 0x3400),
+ MSP430_RLC (bltu, 3, 0x2800, 0x2c00),
+ MSP430_RLC (bge, 4, 0x3400, 0x3800),
+ MSP430_RLC (bgeu, 5, 0x2c00, 0x2800),
+ {"bltn", 6, 0x3000, 3, 0x3000 + 1, 0x3c00 + 2,0x4010},
+ {"jump", 7, 0x3c00, 1, 0x4010, 0, 0},
+ {0,0,0,0,0,0,0}
+};
+
+#undef MSP430_RLC
+#define MSP430_RLC(n,i,sop,o1) \
+ {#n, i, sop, 2, (o1 + 2), 0x0030, 0}
+
+static struct rcodes_s msp430x_rcodes[] =
+{
+ MSP430_RLC (beq, 0, 0x2400, 0x2000),
+ MSP430_RLC (bne, 1, 0x2000, 0x2400),
+ MSP430_RLC (blt, 2, 0x3800, 0x3400),
+ MSP430_RLC (bltu, 3, 0x2800, 0x2c00),
+ MSP430_RLC (bge, 4, 0x3400, 0x3800),
+ MSP430_RLC (bgeu, 5, 0x2c00, 0x2800),
+ {"bltn", 6, 0x3000, 3, 0x0030 + 1, 0x3c00 + 2, 0x3000},
+ {"jump", 7, 0x3c00, 1, 0x0030, 0, 0},
+ {0,0,0,0,0,0,0}
+};
+#undef MSP430_RLC
+
+/* More difficult than above and they have format 5.
+
+ COND EXPL SHORT LONG
+ =================================================================
+ gt > jeq +2; jge label jeq +6; jl +4; br label
+ gtu > jeq +2; jhs label jeq +6; jlo +4; br label
+ leu <= jeq label; jlo label jeq +2; jhs +4; br label
+ le <= jeq label; jl label jeq +2; jge +4; br label
+ ================================================================= */
+
+struct hcodes_s
+{
+ char * name;
+ int index; /* Corresponding insn_opnumb. */
+ int tlab; /* Number of labels in short mode. */
+ int op0; /* Opcode for first word of short jump. */
+ int op1; /* Opcode for second word of short jump. */
+ int lop0; /* Opcodes for long jump mode. */
+ int lop1;
+ int lop2;
+};
+
+static struct hcodes_s msp430_hcodes[] =
+{
+ {"bgt", 0, 1, 0x2401, 0x3400, 0x2403, 0x3802, 0x4010 },
+ {"bgtu", 1, 1, 0x2401, 0x2c00, 0x2403, 0x2802, 0x4010 },
+ {"bleu", 2, 2, 0x2400, 0x2800, 0x2401, 0x2c02, 0x4010 },
+ {"ble", 3, 2, 0x2400, 0x3800, 0x2401, 0x3402, 0x4010 },
+ {0,0,0,0,0,0,0,0}
+};
+
+static struct hcodes_s msp430x_hcodes[] =
+{
+ {"bgt", 0, 1, 0x2401, 0x3400, 0x2403, 0x3802, 0x0030 },
+ {"bgtu", 1, 1, 0x2401, 0x2c00, 0x2403, 0x2802, 0x0030 },
+ {"bleu", 2, 2, 0x2400, 0x2800, 0x2401, 0x2c02, 0x0030 },
+ {"ble", 3, 2, 0x2400, 0x3800, 0x2401, 0x3402, 0x0030 },
+ {0,0,0,0,0,0,0,0}
+};
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "{";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* Handle long expressions. */
+extern LITTLENUM_TYPE generic_bignum[];
+
+static struct hash_control *msp430_hash;
+
+/* Relaxations. */
+#define STATE_UNCOND_BRANCH 1 /* jump */
+#define STATE_NOOV_BRANCH 3 /* bltn */
+#define STATE_SIMPLE_BRANCH 2 /* bne, beq, etc... */
+#define STATE_EMUL_BRANCH 4
+
+#define CNRL 2
+#define CUBL 4
+#define CNOL 8
+#define CSBL 6
+#define CEBL 4
+
+/* Length. */
+#define STATE_BITS10 1 /* wild guess. short jump */
+#define STATE_WORD 2 /* 2 bytes pc rel. addr. more */
+#define STATE_UNDEF 3 /* cannot handle this yet. convert to word mode */
+
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+#define RELAX_STATE(s) ((s) & 3)
+#define RELAX_LEN(s) ((s) >> 2)
+#define RELAX_NEXT(a,b) ENCODE_RELAX (a, b + 1)
+
+relax_typeS md_relax_table[] =
+{
+ /* Unused. */
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ /* Unconditional jump. */
+ {1, 1, 8, 5},
+ {1024, -1024, CNRL, RELAX_NEXT (STATE_UNCOND_BRANCH, STATE_BITS10)}, /* state 10 bits displ */
+ {0, 0, CUBL, RELAX_NEXT (STATE_UNCOND_BRANCH, STATE_WORD)}, /* state word */
+ {1, 1, CUBL, 0}, /* state undef */
+
+ /* Simple branches. */
+ {0, 0, 8, 9},
+ {1024, -1024, CNRL, RELAX_NEXT (STATE_SIMPLE_BRANCH, STATE_BITS10)}, /* state 10 bits displ */
+ {0, 0, CSBL, RELAX_NEXT (STATE_SIMPLE_BRANCH, STATE_WORD)}, /* state word */
+ {1, 1, CSBL, 0},
+
+ /* blt no overflow branch. */
+ {1, 1, 8, 13},
+ {1024, -1024, CNRL, RELAX_NEXT (STATE_NOOV_BRANCH, STATE_BITS10)}, /* state 10 bits displ */
+ {0, 0, CNOL, RELAX_NEXT (STATE_NOOV_BRANCH, STATE_WORD)}, /* state word */
+ {1, 1, CNOL, 0},
+
+ /* Emulated branches. */
+ {1, 1, 8, 17},
+ {1020, -1020, CEBL, RELAX_NEXT (STATE_EMUL_BRANCH, STATE_BITS10)}, /* state 10 bits displ */
+ {0, 0, CNOL, RELAX_NEXT (STATE_EMUL_BRANCH, STATE_WORD)}, /* state word */
+ {1, 1, CNOL, 0}
+};
+
+
+#define MAX_OP_LEN 256
+
+typedef enum msp_isa
+{
+ MSP_ISA_430,
+ MSP_ISA_430X,
+ MSP_ISA_430Xv2
+} msp_isa;
+
+static enum msp_isa selected_isa = MSP_ISA_430Xv2;
+
+static inline bfd_boolean
+target_is_430x (void)
+{
+ return selected_isa >= MSP_ISA_430X;
+}
+
+static inline bfd_boolean
+target_is_430xv2 (void)
+{
+ return selected_isa == MSP_ISA_430Xv2;
+}
+
+/* Generate an absolute 16-bit relocation.
+ For the 430X we generate a relocation without linker range checking
+ if the value is being used in an extended (ie 20-bit) instruction,
+ otherwise if have a shifted expression we use a HI reloc.
+ For the 430 we generate a relocation without assembler range checking
+ if we are handling an immediate value or a byte-width instruction. */
+
+#undef CHECK_RELOC_MSP430
+#define CHECK_RELOC_MSP430(OP) \
+ (target_is_430x () \
+ ? (extended_op \
+ ? BFD_RELOC_16 \
+ : ((OP).vshift == 1) \
+ ? BFD_RELOC_MSP430_ABS_HI16 \
+ : BFD_RELOC_MSP430X_ABS16) \
+ : ((imm_op || byte_op) \
+ ? BFD_RELOC_MSP430_16_BYTE : BFD_RELOC_MSP430_16))
+
+/* Generate a 16-bit pc-relative relocation.
+ For the 430X we generate a relocation without linkwer range checking.
+ For the 430 we generate a relocation without assembler range checking
+ if we are handling an immediate value or a byte-width instruction. */
+#undef CHECK_RELOC_MSP430_PCREL
+#define CHECK_RELOC_MSP430_PCREL \
+ (target_is_430x () \
+ ? BFD_RELOC_MSP430X_PCR16 \
+ : (imm_op || byte_op) \
+ ? BFD_RELOC_MSP430_16_PCREL_BYTE : BFD_RELOC_MSP430_16_PCREL)
+
+/* Profiling capability:
+ It is a performance hit to use gcc's profiling approach for this tiny target.
+ Even more -- jtag hardware facility does not perform any profiling functions.
+ However we've got gdb's built-in simulator where we can do anything.
+ Therefore my suggestion is:
+
+ We define new section ".profiler" which holds all profiling information.
+ We define new pseudo operation .profiler which will instruct assembler to
+ add new profile entry to the object file. Profile should take place at the
+ present address.
+
+ Pseudo-op format:
+
+ .profiler flags,function_to_profile [, cycle_corrector, extra]
+
+ where 'flags' is a combination of the following chars:
+ s - function Start
+ x - function eXit
+ i - function is in Init section
+ f - function is in Fini section
+ l - Library call
+ c - libC standard call
+ d - stack value Demand (saved at run-time in simulator)
+ I - Interrupt service routine
+ P - Prologue start
+ p - Prologue end
+ E - Epilogue start
+ e - Epilogue end
+ j - long Jump/ sjlj unwind
+ a - an Arbitrary code fragment
+ t - exTra parameter saved (constant value like frame size)
+ '""' optional: "sil" == sil
+
+ function_to_profile - function's address
+ cycle_corrector - a value which should be added to the cycle
+ counter, zero if omitted
+ extra - some extra parameter, zero if omitted.
+
+ For example:
+ ------------------------------
+ .global fxx
+ .type fxx,@function
+ fxx:
+ .LFrameOffset_fxx=0x08
+ .profiler "scdP", fxx ; function entry.
+ ; we also demand stack value to be displayed
+ push r11
+ push r10
+ push r9
+ push r8
+ .profiler "cdp",fxx,0, .LFrameOffset_fxx ; check stack value at this point
+ ; (this is a prologue end)
+ ; note, that spare var filled with the frame size
+ mov r15,r8
+ ....
+ .profiler cdE,fxx ; check stack
+ pop r8
+ pop r9
+ pop r10
+ pop r11
+ .profiler xcde,fxx,3 ; exit adds 3 to the cycle counter
+ ret ; cause 'ret' insn takes 3 cycles
+ -------------------------------
+
+ This profiling approach does not produce any overhead and
+ absolutely harmless.
+ So, even profiled code can be uploaded to the MCU. */
+#define MSP430_PROFILER_FLAG_ENTRY 1 /* s */
+#define MSP430_PROFILER_FLAG_EXIT 2 /* x */
+#define MSP430_PROFILER_FLAG_INITSECT 4 /* i */
+#define MSP430_PROFILER_FLAG_FINISECT 8 /* f */
+#define MSP430_PROFILER_FLAG_LIBCALL 0x10 /* l */
+#define MSP430_PROFILER_FLAG_STDCALL 0x20 /* c */
+#define MSP430_PROFILER_FLAG_STACKDMD 0x40 /* d */
+#define MSP430_PROFILER_FLAG_ISR 0x80 /* I */
+#define MSP430_PROFILER_FLAG_PROLSTART 0x100 /* P */
+#define MSP430_PROFILER_FLAG_PROLEND 0x200 /* p */
+#define MSP430_PROFILER_FLAG_EPISTART 0x400 /* E */
+#define MSP430_PROFILER_FLAG_EPIEND 0x800 /* e */
+#define MSP430_PROFILER_FLAG_JUMP 0x1000 /* j */
+#define MSP430_PROFILER_FLAG_FRAGMENT 0x2000 /* a */
+#define MSP430_PROFILER_FLAG_EXTRA 0x4000 /* t */
+#define MSP430_PROFILER_FLAG_notyet 0x8000 /* ? */
+
+static int
+pow2value (int y)
+{
+ int n = 0;
+ unsigned int x;
+
+ x = y;
+
+ if (!x)
+ return 1;
+
+ for (; x; x = x >> 1)
+ if (x & 1)
+ n++;
+
+ return n == 1;
+}
+
+/* Parse ordinary expression. */
+
+static char *
+parse_exp (char * s, expressionS * op)
+{
+ input_line_pointer = s;
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ return input_line_pointer;
+}
+
+
+/* Delete spaces from s: X ( r 1 2) => X(r12). */
+
+static void
+del_spaces (char * s)
+{
+ while (*s)
+ {
+ if (ISSPACE (*s))
+ {
+ char *m = s + 1;
+
+ while (ISSPACE (*m) && *m)
+ m++;
+ memmove (s, m, strlen (m) + 1);
+ }
+ else
+ s++;
+ }
+}
+
+static inline char *
+skip_space (char * s)
+{
+ while (ISSPACE (*s))
+ ++s;
+ return s;
+}
+
+/* Extract one word from FROM and copy it to TO. Delimiters are ",;\n" */
+
+static char *
+extract_operand (char * from, char * to, int limit)
+{
+ int size = 0;
+
+ /* Drop leading whitespace. */
+ from = skip_space (from);
+
+ while (size < limit && *from)
+ {
+ *(to + size) = *from;
+ if (*from == ',' || *from == ';' || *from == '\n')
+ break;
+ from++;
+ size++;
+ }
+
+ *(to + size) = 0;
+ del_spaces (to);
+
+ from++;
+
+ return from;
+}
+
+static void
+msp430_profiler (int dummy ATTRIBUTE_UNUSED)
+{
+ char buffer[1024];
+ char f[32];
+ char * str = buffer;
+ char * flags = f;
+ int p_flags = 0;
+ char * halt;
+ int ops = 0;
+ int left;
+ char * s;
+ segT seg;
+ int subseg;
+ char * end = 0;
+ expressionS exp;
+ expressionS exp1;
+
+ s = input_line_pointer;
+ end = input_line_pointer;
+
+ while (*end && *end != '\n')
+ end++;
+
+ while (*s && *s != '\n')
+ {
+ if (*s == ',')
+ ops++;
+ s++;
+ }
+
+ left = 3 - ops;
+
+ if (ops < 1)
+ {
+ as_bad (_(".profiler pseudo requires at least two operands."));
+ input_line_pointer = end;
+ return;
+ }
+
+ input_line_pointer = extract_operand (input_line_pointer, flags, 32);
+
+ while (*flags)
+ {
+ switch (*flags)
+ {
+ case '"':
+ break;
+ case 'a':
+ p_flags |= MSP430_PROFILER_FLAG_FRAGMENT;
+ break;
+ case 'j':
+ p_flags |= MSP430_PROFILER_FLAG_JUMP;
+ break;
+ case 'P':
+ p_flags |= MSP430_PROFILER_FLAG_PROLSTART;
+ break;
+ case 'p':
+ p_flags |= MSP430_PROFILER_FLAG_PROLEND;
+ break;
+ case 'E':
+ p_flags |= MSP430_PROFILER_FLAG_EPISTART;
+ break;
+ case 'e':
+ p_flags |= MSP430_PROFILER_FLAG_EPIEND;
+ break;
+ case 's':
+ p_flags |= MSP430_PROFILER_FLAG_ENTRY;
+ break;
+ case 'x':
+ p_flags |= MSP430_PROFILER_FLAG_EXIT;
+ break;
+ case 'i':
+ p_flags |= MSP430_PROFILER_FLAG_INITSECT;
+ break;
+ case 'f':
+ p_flags |= MSP430_PROFILER_FLAG_FINISECT;
+ break;
+ case 'l':
+ p_flags |= MSP430_PROFILER_FLAG_LIBCALL;
+ break;
+ case 'c':
+ p_flags |= MSP430_PROFILER_FLAG_STDCALL;
+ break;
+ case 'd':
+ p_flags |= MSP430_PROFILER_FLAG_STACKDMD;
+ break;
+ case 'I':
+ p_flags |= MSP430_PROFILER_FLAG_ISR;
+ break;
+ case 't':
+ p_flags |= MSP430_PROFILER_FLAG_EXTRA;
+ break;
+ default:
+ as_warn (_("unknown profiling flag - ignored."));
+ break;
+ }
+ flags++;
+ }
+
+ if (p_flags
+ && ( ! pow2value (p_flags & ( MSP430_PROFILER_FLAG_ENTRY
+ | MSP430_PROFILER_FLAG_EXIT))
+ || ! pow2value (p_flags & ( MSP430_PROFILER_FLAG_PROLSTART
+ | MSP430_PROFILER_FLAG_PROLEND
+ | MSP430_PROFILER_FLAG_EPISTART
+ | MSP430_PROFILER_FLAG_EPIEND))
+ || ! pow2value (p_flags & ( MSP430_PROFILER_FLAG_INITSECT
+ | MSP430_PROFILER_FLAG_FINISECT))))
+ {
+ as_bad (_("ambiguous flags combination - '.profiler' directive ignored."));
+ input_line_pointer = end;
+ return;
+ }
+
+ /* Generate temp symbol which denotes current location. */
+ if (now_seg == absolute_section) /* Paranoia ? */
+ {
+ exp1.X_op = O_constant;
+ exp1.X_add_number = abs_section_offset;
+ as_warn (_("profiling in absolute section?"));
+ }
+ else
+ {
+ exp1.X_op = O_symbol;
+ exp1.X_add_symbol = symbol_temp_new_now ();
+ exp1.X_add_number = 0;
+ }
+
+ /* Generate a symbol which holds flags value. */
+ exp.X_op = O_constant;
+ exp.X_add_number = p_flags;
+
+ /* Save current section. */
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* Now go to .profiler section. */
+ obj_elf_change_section (".profiler", SHT_PROGBITS, 0, 0, 0, 0, 0);
+
+ /* Save flags. */
+ emit_expr (& exp, 2);
+
+ /* Save label value. */
+ emit_expr (& exp1, 2);
+
+ while (ops--)
+ {
+ /* Now get profiling info. */
+ halt = extract_operand (input_line_pointer, str, 1024);
+ /* Process like ".word xxx" directive. */
+ parse_exp (str, & exp);
+ emit_expr (& exp, 2);
+ input_line_pointer = halt;
+ }
+
+ /* Fill the rest with zeros. */
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ while (left--)
+ emit_expr (& exp, 2);
+
+ /* Return to current section. */
+ subseg_set (seg, subseg);
+}
+
+static char *
+extract_word (char * from, char * to, int limit)
+{
+ char *op_end;
+ int size = 0;
+
+ /* Drop leading whitespace. */
+ from = skip_space (from);
+ *to = 0;
+
+ /* Find the op code end. */
+ for (op_end = from; *op_end != 0 && is_part_of_name (*op_end);)
+ {
+ to[size++] = *op_end++;
+ if (size + 1 >= limit)
+ break;
+ }
+
+ to[size] = 0;
+ return op_end;
+}
+
+#define OPTION_MMCU 'm'
+#define OPTION_RELAX 'Q'
+#define OPTION_POLYMORPHS 'P'
+#define OPTION_LARGE 'l'
+static bfd_boolean large_model = FALSE;
+#define OPTION_NO_INTR_NOPS 'N'
+#define OPTION_INTR_NOPS 'n'
+static bfd_boolean gen_interrupt_nops = FALSE;
+#define OPTION_WARN_INTR_NOPS 'y'
+#define OPTION_NO_WARN_INTR_NOPS 'Y'
+static bfd_boolean warn_interrupt_nops = TRUE;
+#define OPTION_MCPU 'c'
+#define OPTION_MOVE_DATA 'd'
+static bfd_boolean move_data = FALSE;
+
+static void
+msp430_set_arch (int option)
+{
+ char *str = (char *) alloca (32); /* 32 for good measure. */
+
+ input_line_pointer = extract_word (input_line_pointer, str, 32);
+
+ md_parse_option (option, str);
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH,
+ target_is_430x () ? bfd_mach_msp430x : bfd_mach_msp11);
+}
+
+/* This is the full list of MCU names that are known to only
+ support the 430 ISA. */
+static const char * msp430_mcu_names [] =
+{
+"msp430afe221", "msp430afe222", "msp430afe223", "msp430afe231",
+"msp430afe232", "msp430afe233", "msp430afe251", "msp430afe252",
+"msp430afe253", "msp430c091", "msp430c092", "msp430c111",
+"msp430c1111", "msp430c112", "msp430c1121", "msp430c1331",
+"msp430c1351", "msp430c311s", "msp430c312", "msp430c313",
+"msp430c314", "msp430c315", "msp430c323", "msp430c325",
+"msp430c336", "msp430c337", "msp430c412", "msp430c413",
+"msp430e112", "msp430e313", "msp430e315", "msp430e325",
+"msp430e337", "msp430f110", "msp430f1101", "msp430f1101a",
+"msp430f1111", "msp430f1111a", "msp430f112", "msp430f1121",
+"msp430f1121a", "msp430f1122", "msp430f1132", "msp430f122",
+"msp430f1222", "msp430f123", "msp430f1232", "msp430f133",
+"msp430f135", "msp430f147", "msp430f1471", "msp430f148",
+"msp430f1481", "msp430f149", "msp430f1491", "msp430f155",
+"msp430f156", "msp430f157", "msp430f1610", "msp430f1611",
+"msp430f1612", "msp430f167", "msp430f168", "msp430f169",
+"msp430f2001", "msp430f2002", "msp430f2003", "msp430f2011",
+"msp430f2012", "msp430f2013", "msp430f2101", "msp430f2111",
+"msp430f2112", "msp430f2121", "msp430f2122", "msp430f2131",
+"msp430f2132", "msp430f2232", "msp430f2234", "msp430f2252",
+"msp430f2254", "msp430f2272", "msp430f2274", "msp430f233",
+"msp430f2330", "msp430f235", "msp430f2350", "msp430f2370",
+"msp430f2410", "msp430f247", "msp430f2471", "msp430f248",
+"msp430f2481", "msp430f249", "msp430f2491", "msp430f412",
+"msp430f413", "msp430f4132", "msp430f415", "msp430f4152",
+"msp430f417", "msp430f423", "msp430f423a", "msp430f425",
+"msp430f4250", "msp430f425a", "msp430f4260", "msp430f427",
+"msp430f4270", "msp430f427a", "msp430f435", "msp430f4351",
+"msp430f436", "msp430f4361", "msp430f437", "msp430f4371",
+"msp430f438", "msp430f439", "msp430f447", "msp430f448",
+"msp430f4481", "msp430f449", "msp430f4491", "msp430f477",
+"msp430f478", "msp430f4783", "msp430f4784", "msp430f479",
+"msp430f4793", "msp430f4794", "msp430fe423", "msp430fe4232",
+"msp430fe423a", "msp430fe4242", "msp430fe425", "msp430fe4252",
+"msp430fe425a", "msp430fe427", "msp430fe4272", "msp430fe427a",
+"msp430fg4250", "msp430fg4260", "msp430fg4270", "msp430fg437",
+"msp430fg438", "msp430fg439", "msp430fg477", "msp430fg478",
+"msp430fg479", "msp430fw423", "msp430fw425", "msp430fw427",
+"msp430fw428", "msp430fw429", "msp430g2001", "msp430g2101",
+"msp430g2102", "msp430g2111", "msp430g2112", "msp430g2113",
+"msp430g2121", "msp430g2131", "msp430g2132", "msp430g2152",
+"msp430g2153", "msp430g2201", "msp430g2202", "msp430g2203",
+"msp430g2210", "msp430g2211", "msp430g2212", "msp430g2213",
+"msp430g2221", "msp430g2230", "msp430g2231", "msp430g2232",
+"msp430g2233", "msp430g2252", "msp430g2253", "msp430g2302",
+"msp430g2303", "msp430g2312", "msp430g2313", "msp430g2332",
+"msp430g2333", "msp430g2352", "msp430g2353", "msp430g2402",
+"msp430g2403", "msp430g2412", "msp430g2413", "msp430g2432",
+"msp430g2433", "msp430g2444", "msp430g2452", "msp430g2453",
+"msp430g2513", "msp430g2533", "msp430g2544", "msp430g2553",
+"msp430g2744", "msp430g2755", "msp430g2855", "msp430g2955",
+"msp430i2020", "msp430i2021", "msp430i2030", "msp430i2031",
+"msp430i2040", "msp430i2041", "msp430l092", "msp430p112",
+"msp430p313", "msp430p315", "msp430p315s", "msp430p325",
+"msp430p337", "msp430tch5e"
+};
+
+int
+md_parse_option (int c, char * arg)
+{
+ switch (c)
+ {
+ case OPTION_MMCU:
+ if (arg == NULL)
+ as_fatal (_("MCU option requires a name\n"));
+
+ if (strcasecmp ("msp430", arg) == 0)
+ selected_isa = MSP_ISA_430;
+ else if (strcasecmp ("msp430xv2", arg) == 0)
+ selected_isa = MSP_ISA_430Xv2;
+ else if (strcasecmp ("msp430x", arg) == 0)
+ selected_isa = MSP_ISA_430X;
+ else
+ {
+ int i;
+
+ for (i = sizeof msp430_mcu_names / sizeof msp430_mcu_names[0]; i--;)
+ if (strcasecmp (msp430_mcu_names[i], arg) == 0)
+ {
+ selected_isa = MSP_ISA_430;
+ break;
+ }
+ }
+ /* It is not an error if we do not match the MCU name. */
+ return 1;
+
+ case OPTION_MCPU:
+ if (strcmp (arg, "430") == 0
+ || strcasecmp (arg, "msp430") == 0)
+ selected_isa = MSP_ISA_430;
+ else if (strcasecmp (arg, "430x") == 0
+ || strcasecmp (arg, "msp430x") == 0)
+ selected_isa = MSP_ISA_430X;
+ else if (strcasecmp (arg, "430xv2") == 0
+ || strcasecmp (arg, "msp430xv2") == 0)
+ selected_isa = MSP_ISA_430Xv2;
+ else
+ as_fatal (_("unrecognised argument to -mcpu option '%s'"), arg);
+ return 1;
+
+ case OPTION_RELAX:
+ msp430_enable_relax = 1;
+ return 1;
+
+ case OPTION_POLYMORPHS:
+ msp430_enable_polys = 1;
+ return 1;
+
+ case OPTION_LARGE:
+ large_model = TRUE;
+ return 1;
+
+ case OPTION_NO_INTR_NOPS:
+ gen_interrupt_nops = FALSE;
+ return 1;
+ case OPTION_INTR_NOPS:
+ gen_interrupt_nops = TRUE;
+ return 1;
+
+ case OPTION_WARN_INTR_NOPS:
+ warn_interrupt_nops = TRUE;
+ return 1;
+ case OPTION_NO_WARN_INTR_NOPS:
+ warn_interrupt_nops = FALSE;
+ return 1;
+
+ case OPTION_MOVE_DATA:
+ move_data = TRUE;
+ return 1;
+ }
+
+ return 0;
+}
+
+/* The intention here is to have the mere presence of these sections
+ cause the object to have a reference to a well-known symbol. This
+ reference pulls in the bits of the runtime (crt0) that initialize
+ these sections. Thus, for example, the startup code to call
+ memset() to initialize .bss will only be linked in when there is a
+ non-empty .bss section. Otherwise, the call would exist but have a
+ zero length parameter, which is a waste of memory and cycles.
+
+ The code which initializes these sections should have a global
+ label for these symbols, and should be marked with KEEP() in the
+ linker script.
+ */
+static void
+msp430_section (int arg)
+{
+ char * saved_ilp = input_line_pointer;
+ char * name = obj_elf_section_name ();
+
+ if (strncmp (name, ".bss", 4) == 0
+ || strncmp (name, ".gnu.linkonce.b.", 16) == 0)
+ (void) symbol_find_or_make ("__crt0_init_bss");
+
+ if (strncmp (name, ".data", 5) == 0
+ || strncmp (name, ".gnu.linkonce.d.", 16) == 0)
+ (void) symbol_find_or_make ("__crt0_movedata");
+
+ input_line_pointer = saved_ilp;
+ obj_elf_section (arg);
+}
+
+void
+msp430_frob_section (asection *sec)
+{
+ const char *name = sec->name;
+
+ if (sec->size == 0)
+ return;
+
+ if (strncmp (name, ".bss", 4) == 0
+ || strncmp (name, ".gnu.linkonce.b.", 16) == 0)
+ (void) symbol_find_or_make ("__crt0_init_bss");
+
+ if (strncmp (name, ".data", 5) == 0
+ || strncmp (name, ".gnu.linkonce.d.", 16) == 0)
+ (void) symbol_find_or_make ("__crt0_movedata");
+}
+
+static void
+msp430_lcomm (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP = s_comm_internal (0, s_lcomm_internal);
+
+ if (symbolP)
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+ (void) symbol_find_or_make ("__crt0_init_bss");
+}
+
+static void
+msp430_comm (int needs_align)
+{
+ s_comm_internal (needs_align, elf_common_parse);
+ (void) symbol_find_or_make ("__crt0_init_bss");
+}
+
+static void
+msp430_refsym (int arg ATTRIBUTE_UNUSED)
+{
+ char sym_name[1024];
+ input_line_pointer = extract_word (input_line_pointer, sym_name, 1024);
+
+ (void) symbol_find_or_make (sym_name);
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"arch", msp430_set_arch, OPTION_MMCU},
+ {"cpu", msp430_set_arch, OPTION_MCPU},
+ {"profiler", msp430_profiler, 0},
+ {"section", msp430_section, 0},
+ {"section.s", msp430_section, 0},
+ {"sect", msp430_section, 0},
+ {"sect.s", msp430_section, 0},
+ {"pushsection", msp430_section, 1},
+ {"refsym", msp430_refsym, 0},
+ {"comm", msp430_comm, 0},
+ {"lcomm", msp430_lcomm, 0},
+ {NULL, NULL, 0}
+};
+
+const char *md_shortopts = "mm:,mP,mQ,ml,mN,mn,my,mY";
+
+struct option md_longopts[] =
+{
+ {"mmcu", required_argument, NULL, OPTION_MMCU},
+ {"mcpu", required_argument, NULL, OPTION_MCPU},
+ {"mP", no_argument, NULL, OPTION_POLYMORPHS},
+ {"mQ", no_argument, NULL, OPTION_RELAX},
+ {"ml", no_argument, NULL, OPTION_LARGE},
+ {"mN", no_argument, NULL, OPTION_NO_INTR_NOPS},
+ {"mn", no_argument, NULL, OPTION_INTR_NOPS},
+ {"mY", no_argument, NULL, OPTION_NO_WARN_INTR_NOPS},
+ {"my", no_argument, NULL, OPTION_WARN_INTR_NOPS},
+ {"md", no_argument, NULL, OPTION_MOVE_DATA},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream,
+ _("MSP430 options:\n"
+ " -mmcu=<msp430-name> - select microcontroller type\n"
+ " -mcpu={430|430x|430xv2} - select microcontroller architecture\n"));
+ fprintf (stream,
+ _(" -mQ - enable relaxation at assembly time. DANGEROUS!\n"
+ " -mP - enable polymorph instructions\n"));
+ fprintf (stream,
+ _(" -ml - enable large code model\n"));
+ fprintf (stream,
+ _(" -mN - do not insert NOPs after changing interrupts (default)\n"));
+ fprintf (stream,
+ _(" -mn - insert a NOP after changing interrupts\n"));
+ fprintf (stream,
+ _(" -mY - do not warn about missing NOPs after changing interrupts\n"));
+ fprintf (stream,
+ _(" -my - warn about missing NOPs after changing interrupts (default)\n"));
+ fprintf (stream,
+ _(" -md - Force copying of data from ROM to RAM at startup\n"));
+}
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+static char *
+extract_cmd (char * from, char * to, int limit)
+{
+ int size = 0;
+
+ while (*from && ! ISSPACE (*from) && *from != '.' && limit > size)
+ {
+ *(to + size) = *from;
+ from++;
+ size++;
+ }
+
+ *(to + size) = 0;
+
+ return from;
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+void
+md_begin (void)
+{
+ struct msp430_opcode_s * opcode;
+ msp430_hash = hash_new ();
+
+ for (opcode = msp430_opcodes; opcode->name; opcode++)
+ hash_insert (msp430_hash, opcode->name, (char *) opcode);
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH,
+ target_is_430x () ? bfd_mach_msp430x : bfd_mach_msp11);
+}
+
+/* Returns the register number equivalent to the string T.
+ Returns -1 if there is no such register.
+ Skips a leading 'r' or 'R' character if there is one.
+ Handles the register aliases PC and SP. */
+
+static signed int
+check_reg (char * t)
+{
+ signed int val;
+
+ if (t == NULL)
+ return -1;
+
+ if (*t == 'r' || *t == 'R')
+ ++t;
+
+ if (strncasecmp (t, "pc", 2) == 0)
+ return 0;
+
+ if (strncasecmp (t, "sp", 2) == 0)
+ return 1;
+
+ if (strncasecmp (t, "sr", 2) == 0)
+ return 2;
+
+ if (*t == '0')
+ return 0;
+
+ val = atoi (t);
+
+ if (val < 1 || val > 15)
+ return -1;
+
+ return val;
+}
+
+static int
+msp430_srcoperand (struct msp430_operand_s * op,
+ char * l,
+ int bin,
+ bfd_boolean * imm_op,
+ bfd_boolean allow_20bit_values,
+ bfd_boolean constants_allowed)
+{
+ char *__tl = l;
+
+ /* Check if an immediate #VALUE. The hash sign should be only at the beginning! */
+ if (*l == '#')
+ {
+ char *h = l;
+ int vshift = -1;
+ int rval = 0;
+
+ /* Check if there is:
+ llo(x) - least significant 16 bits, x &= 0xffff
+ lhi(x) - x = (x >> 16) & 0xffff,
+ hlo(x) - x = (x >> 32) & 0xffff,
+ hhi(x) - x = (x >> 48) & 0xffff
+ The value _MUST_ be constant expression: #hlo(1231231231). */
+
+ *imm_op = TRUE;
+
+ if (strncasecmp (h, "#llo(", 5) == 0)
+ {
+ vshift = 0;
+ rval = 3;
+ }
+ else if (strncasecmp (h, "#lhi(", 5) == 0)
+ {
+ vshift = 1;
+ rval = 3;
+ }
+ else if (strncasecmp (h, "#hlo(", 5) == 0)
+ {
+ vshift = 2;
+ rval = 3;
+ }
+ else if (strncasecmp (h, "#hhi(", 5) == 0)
+ {
+ vshift = 3;
+ rval = 3;
+ }
+ else if (strncasecmp (h, "#lo(", 4) == 0)
+ {
+ vshift = 0;
+ rval = 2;
+ }
+ else if (strncasecmp (h, "#hi(", 4) == 0)
+ {
+ vshift = 1;
+ rval = 2;
+ }
+
+ op->reg = 0; /* Reg PC. */
+ op->am = 3;
+ op->ol = 1; /* Immediate will follow an instruction. */
+ __tl = h + 1 + rval;
+ op->mode = OP_EXP;
+ op->vshift = vshift;
+
+ parse_exp (__tl, &(op->exp));
+ if (op->exp.X_op == O_constant)
+ {
+ int x = op->exp.X_add_number;
+
+ if (vshift == 0)
+ {
+ x = x & 0xffff;
+ op->exp.X_add_number = x;
+ }
+ else if (vshift == 1)
+ {
+ x = (x >> 16) & 0xffff;
+ op->exp.X_add_number = x;
+ op->vshift = 0;
+ }
+ else if (vshift > 1)
+ {
+ if (x < 0)
+ op->exp.X_add_number = -1;
+ else
+ op->exp.X_add_number = 0; /* Nothing left. */
+ x = op->exp.X_add_number;
+ op->vshift = 0;
+ }
+
+ if (allow_20bit_values)
+ {
+ if (op->exp.X_add_number > 0xfffff || op->exp.X_add_number < -524288)
+ {
+ as_bad (_("value 0x%x out of extended range."), x);
+ return 1;
+ }
+ }
+ else if (op->exp.X_add_number > 65535 || op->exp.X_add_number < -32768)
+ {
+ as_bad (_("value %d out of range. Use #lo() or #hi()"), x);
+ return 1;
+ }
+
+ /* Now check constants. */
+ /* Substitute register mode with a constant generator if applicable. */
+
+ if (!allow_20bit_values)
+ x = (short) x; /* Extend sign. */
+
+ if (! constants_allowed)
+ ;
+ else if (x == 0)
+ {
+ op->reg = 3;
+ op->am = 0;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 1)
+ {
+ op->reg = 3;
+ op->am = 1;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 2)
+ {
+ op->reg = 3;
+ op->am = 2;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == -1)
+ {
+ op->reg = 3;
+ op->am = 3;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 4)
+ {
+#ifdef PUSH_1X_WORKAROUND
+ if (bin == 0x1200)
+ {
+ /* Remove warning as confusing.
+ as_warn (_("Hardware push bug workaround")); */
+ }
+ else
+#endif
+ {
+ op->reg = 2;
+ op->am = 2;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ }
+ else if (x == 8)
+ {
+#ifdef PUSH_1X_WORKAROUND
+ if (bin == 0x1200)
+ {
+ /* Remove warning as confusing.
+ as_warn (_("Hardware push bug workaround")); */
+ }
+ else
+#endif
+ {
+ op->reg = 2;
+ op->am = 3;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ }
+ }
+ else if (op->exp.X_op == O_symbol)
+ {
+ if (vshift > 1)
+ as_bad (_("error: unsupported #foo() directive used on symbol"));
+ op->mode = OP_EXP;
+ }
+ else if (op->exp.X_op == O_big)
+ {
+ short x;
+
+ if (vshift != -1)
+ {
+ op->exp.X_op = O_constant;
+ op->exp.X_add_number = 0xffff & generic_bignum[vshift];
+ x = op->exp.X_add_number;
+ op->vshift = 0;
+ }
+ else
+ {
+ as_bad (_
+ ("unknown expression in operand %s. use #llo() #lhi() #hlo() #hhi() "),
+ l);
+ return 1;
+ }
+
+ if (x == 0)
+ {
+ op->reg = 3;
+ op->am = 0;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 1)
+ {
+ op->reg = 3;
+ op->am = 1;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 2)
+ {
+ op->reg = 3;
+ op->am = 2;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == -1)
+ {
+ op->reg = 3;
+ op->am = 3;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 4)
+ {
+ op->reg = 2;
+ op->am = 2;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ else if (x == 8)
+ {
+ op->reg = 2;
+ op->am = 3;
+ op->ol = 0;
+ op->mode = OP_REG;
+ }
+ }
+ /* Redundant (yet) check. */
+ else if (op->exp.X_op == O_register)
+ as_bad
+ (_("Registers cannot be used within immediate expression [%s]"), l);
+ else
+ as_bad (_("unknown operand %s"), l);
+
+ return 0;
+ }
+
+ /* Check if absolute &VALUE (assume that we can construct something like ((a&b)<<7 + 25). */
+ if (*l == '&')
+ {
+ char *h = l;
+
+ op->reg = 2; /* reg 2 in absolute addr mode. */
+ op->am = 1; /* mode As == 01 bin. */
+ op->ol = 1; /* Immediate value followed by instruction. */
+ __tl = h + 1;
+ parse_exp (__tl, &(op->exp));
+ op->mode = OP_EXP;
+ op->vshift = 0;
+ if (op->exp.X_op == O_constant)
+ {
+ int x = op->exp.X_add_number;
+
+ if (allow_20bit_values)
+ {
+ if (x > 0xfffff || x < -(0x7ffff))
+ {
+ as_bad (_("value 0x%x out of extended range."), x);
+ return 1;
+ }
+ }
+ else if (x > 65535 || x < -32768)
+ {
+ as_bad (_("value out of range: 0x%x"), x);
+ return 1;
+ }
+ }
+ else if (op->exp.X_op == O_symbol)
+ ;
+ else
+ {
+ /* Redundant (yet) check. */
+ if (op->exp.X_op == O_register)
+ as_bad
+ (_("Registers cannot be used within absolute expression [%s]"), l);
+ else
+ as_bad (_("unknown expression in operand %s"), l);
+ return 1;
+ }
+ return 0;
+ }
+
+ /* Check if indirect register mode @Rn / postincrement @Rn+. */
+ if (*l == '@')
+ {
+ char *t = l;
+ char *m = strchr (l, '+');
+
+ if (t != l)
+ {
+ as_bad (_("unknown addressing mode %s"), l);
+ return 1;
+ }
+
+ t++;
+
+ if ((op->reg = check_reg (t)) == -1)
+ {
+ as_bad (_("Bad register name %s"), t);
+ return 1;
+ }
+
+ op->mode = OP_REG;
+ op->am = m ? 3 : 2;
+ op->ol = 0;
+
+ /* PC cannot be used in indirect addressing. */
+ if (target_is_430xv2 () && op->reg == 0)
+ {
+ as_bad (_("cannot use indirect addressing with the PC"));
+ return 1;
+ }
+
+ return 0;
+ }
+
+ /* Check if register indexed X(Rn). */
+ do
+ {
+ char *h = strrchr (l, '(');
+ char *m = strrchr (l, ')');
+ char *t;
+
+ *imm_op = TRUE;
+
+ if (!h)
+ break;
+ if (!m)
+ {
+ as_bad (_("')' required"));
+ return 1;
+ }
+
+ t = h;
+ op->am = 1;
+ op->ol = 1;
+
+ /* Extract a register. */
+ if ((op->reg = check_reg (t + 1)) == -1)
+ {
+ as_bad (_
+ ("unknown operator %s. Did you mean X(Rn) or #[hl][hl][oi](CONST) ?"),
+ l);
+ return 1;
+ }
+
+ if (op->reg == 2)
+ {
+ as_bad (_("r2 should not be used in indexed addressing mode"));
+ return 1;
+ }
+
+ /* Extract constant. */
+ __tl = l;
+ *h = 0;
+ op->mode = OP_EXP;
+ op->vshift = 0;
+ parse_exp (__tl, &(op->exp));
+ if (op->exp.X_op == O_constant)
+ {
+ int x = op->exp.X_add_number;
+
+ if (allow_20bit_values)
+ {
+ if (x > 0xfffff || x < - (0x7ffff))
+ {
+ as_bad (_("value 0x%x out of extended range."), x);
+ return 1;
+ }
+ }
+ else if (x > 65535 || x < -32768)
+ {
+ as_bad (_("value out of range: 0x%x"), x);
+ return 1;
+ }
+
+ if (x == 0)
+ {
+ op->mode = OP_REG;
+ op->am = 2;
+ op->ol = 0;
+ return 0;
+ }
+ }
+ else if (op->exp.X_op == O_symbol)
+ ;
+ else
+ {
+ /* Redundant (yet) check. */
+ if (op->exp.X_op == O_register)
+ as_bad
+ (_("Registers cannot be used as a prefix of indexed expression [%s]"), l);
+ else
+ as_bad (_("unknown expression in operand %s"), l);
+ return 1;
+ }
+
+ return 0;
+ }
+ while (0);
+
+ /* Possibly register mode 'mov r1,r2'. */
+ if ((op->reg = check_reg (l)) != -1)
+ {
+ op->mode = OP_REG;
+ op->am = 0;
+ op->ol = 0;
+ return 0;
+ }
+
+ /* Symbolic mode 'mov a, b' == 'mov x(pc), y(pc)'. */
+ do
+ {
+ op->mode = OP_EXP;
+ op->reg = 0; /* PC relative... be careful. */
+ /* An expression starting with a minus sign is a constant, not an address. */
+ op->am = (*l == '-' ? 3 : 1);
+ op->ol = 1;
+ op->vshift = 0;
+ __tl = l;
+ parse_exp (__tl, &(op->exp));
+ return 0;
+ }
+ while (0);
+
+ /* Unreachable. */
+ as_bad (_("unknown addressing mode for operand %s"), l);
+ return 1;
+}
+
+
+static int
+msp430_dstoperand (struct msp430_operand_s * op,
+ char * l,
+ int bin,
+ bfd_boolean allow_20bit_values,
+ bfd_boolean constants_allowed)
+{
+ int dummy;
+ int ret = msp430_srcoperand (op, l, bin, & dummy,
+ allow_20bit_values,
+ constants_allowed);
+
+ if (ret)
+ return ret;
+
+ if (op->am == 2)
+ {
+ char *__tl = "0";
+
+ op->mode = OP_EXP;
+ op->am = 1;
+ op->ol = 1;
+ op->vshift = 0;
+ parse_exp (__tl, &(op->exp));
+
+ if (op->exp.X_op != O_constant || op->exp.X_add_number != 0)
+ {
+ as_bad (_("Internal bug. Try to use 0(r%d) instead of @r%d"),
+ op->reg, op->reg);
+ return 1;
+ }
+ return 0;
+ }
+
+ if (op->am > 1)
+ {
+ as_bad (_
+ ("this addressing mode is not applicable for destination operand"));
+ return 1;
+ }
+ return 0;
+}
+
+/* Attempt to encode a MOVA instruction with the given operands.
+ Returns the length of the encoded instruction if successful
+ or 0 upon failure. If the encoding fails, an error message
+ will be returned if a pointer is provided. */
+
+static int
+try_encode_mova (bfd_boolean imm_op,
+ int bin,
+ struct msp430_operand_s * op1,
+ struct msp430_operand_s * op2,
+ const char ** error_message_return)
+{
+ short ZEROS = 0;
+ char *frag;
+ int where;
+
+ /* Only a restricted subset of the normal MSP430 addressing modes
+ are supported here, so check for the ones that are allowed. */
+ if (imm_op)
+ {
+ if (op1->mode == OP_EXP)
+ {
+ if (op2->mode != OP_REG)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("expected register as second argument of %s");
+ return 0;
+ }
+
+ if (op1->am == 3)
+ {
+ /* MOVA #imm20, Rdst. */
+ bin |= 0x80 | op2->reg;
+ frag = frag_more (4);
+ where = frag - frag_now->fr_literal;
+ if (op1->exp.X_op == O_constant)
+ {
+ bin |= ((op1->exp.X_add_number >> 16) & 0xf) << 8;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) bin, frag);
+ fix_new_exp (frag_now, where, 4, &(op1->exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_ADR_SRC);
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+ }
+
+ return 4;
+ }
+ else if (op1->am == 1)
+ {
+ /* MOVA z16(Rsrc), Rdst. */
+ bin |= 0x30 | (op1->reg << 8) | op2->reg;
+ frag = frag_more (4);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ if (op1->exp.X_op == O_constant)
+ {
+ if (op1->exp.X_add_number > 0xffff
+ || op1->exp.X_add_number < -(0x7fff))
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("index value too big for %s");
+ return 0;
+ }
+ bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+ fix_new_exp (frag_now, where + 2, 2, &(op1->exp), FALSE,
+ op1->reg == 0 ?
+ BFD_RELOC_MSP430X_PCR16 :
+ BFD_RELOC_MSP430X_ABS16);
+ }
+ return 4;
+ }
+
+ if (error_message_return != NULL)
+ * error_message_return = _("unexpected addressing mode for %s");
+ return 0;
+ }
+ else if (op1->am == 0)
+ {
+ /* MOVA Rsrc, ... */
+ if (op2->mode == OP_REG)
+ {
+ bin |= 0xc0 | (op1->reg << 8) | op2->reg;
+ frag = frag_more (2);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ return 2;
+ }
+ else if (op2->am == 1)
+ {
+ if (op2->reg == 2)
+ {
+ /* MOVA Rsrc, &abs20. */
+ bin |= 0x60 | (op1->reg << 8);
+ frag = frag_more (4);
+ where = frag - frag_now->fr_literal;
+ if (op2->exp.X_op == O_constant)
+ {
+ bin |= (op2->exp.X_add_number >> 16) & 0xf;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ bfd_putl16 (op2->exp.X_add_number & 0xffff, frag + 2);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) bin, frag);
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+ fix_new_exp (frag_now, where, 4, &(op2->exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_ADR_DST);
+ }
+ return 4;
+ }
+
+ /* MOVA Rsrc, z16(Rdst). */
+ bin |= 0x70 | (op1->reg << 8) | op2->reg;
+ frag = frag_more (4);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ if (op2->exp.X_op == O_constant)
+ {
+ if (op2->exp.X_add_number > 0xffff
+ || op2->exp.X_add_number < -(0x7fff))
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("index value too big for %s");
+ return 0;
+ }
+ bfd_putl16 (op2->exp.X_add_number & 0xffff, frag + 2);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+ fix_new_exp (frag_now, where + 2, 2, &(op2->exp), FALSE,
+ op2->reg == 0 ?
+ BFD_RELOC_MSP430X_PCR16 :
+ BFD_RELOC_MSP430X_ABS16);
+ }
+ return 4;
+ }
+
+ if (error_message_return != NULL)
+ * error_message_return = _("unexpected addressing mode for %s");
+ return 0;
+ }
+ }
+
+ /* imm_op == FALSE. */
+
+ if (op1->reg == 2 && op1->am == 1 && op1->mode == OP_EXP)
+ {
+ /* MOVA &abs20, Rdst. */
+ if (op2->mode != OP_REG)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("expected register as second argument of %s");
+ return 0;
+ }
+
+ if (op2->reg == 2 || op2->reg == 3)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("constant generator destination register found in %s");
+ return 0;
+ }
+
+ bin |= 0x20 | op2->reg;
+ frag = frag_more (4);
+ where = frag - frag_now->fr_literal;
+ if (op1->exp.X_op == O_constant)
+ {
+ bin |= ((op1->exp.X_add_number >> 16) & 0xf) << 8;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ bfd_putl16 (op1->exp.X_add_number & 0xffff, frag + 2);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) bin, frag);
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+ fix_new_exp (frag_now, where, 4, &(op1->exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_ADR_SRC);
+ }
+ return 4;
+ }
+ else if (op1->mode == OP_REG)
+ {
+ if (op1->am == 3)
+ {
+ /* MOVA @Rsrc+, Rdst. */
+ if (op2->mode != OP_REG)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("expected register as second argument of %s");
+ return 0;
+ }
+
+ if (op2->reg == 2 || op2->reg == 3)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("constant generator destination register found in %s");
+ return 0;
+ }
+
+ if (op1->reg == 2 || op1->reg == 3)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("constant generator source register found in %s");
+ return 0;
+ }
+
+ bin |= 0x10 | (op1->reg << 8) | op2->reg;
+ frag = frag_more (2);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ return 2;
+ }
+ else if (op1->am == 2)
+ {
+ /* MOVA @Rsrc,Rdst */
+ if (op2->mode != OP_REG)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("expected register as second argument of %s");
+ return 0;
+ }
+
+ if (op2->reg == 2 || op2->reg == 3)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("constant generator destination register found in %s");
+ return 0;
+ }
+
+ if (op1->reg == 2 || op1->reg == 3)
+ {
+ if (error_message_return != NULL)
+ * error_message_return = _("constant generator source register found in %s");
+ return 0;
+ }
+
+ bin |= (op1->reg << 8) | op2->reg;
+ frag = frag_more (2);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ return 2;
+ }
+ }
+
+ if (error_message_return != NULL)
+ * error_message_return = _("unexpected addressing mode for %s");
+
+ return 0;
+}
+
+static bfd_boolean check_for_nop = FALSE;
+
+#define is_opcode(NAME) (strcmp (opcode->name, NAME) == 0)
+
+/* Parse instruction operands.
+ Return binary opcode. */
+
+static unsigned int
+msp430_operands (struct msp430_opcode_s * opcode, char * line)
+{
+ int bin = opcode->bin_opcode; /* Opcode mask. */
+ int insn_length = 0;
+ char l1[MAX_OP_LEN], l2[MAX_OP_LEN];
+ char *frag;
+ int where;
+ struct msp430_operand_s op1, op2;
+ int res = 0;
+ static short ZEROS = 0;
+ bfd_boolean byte_op, imm_op;
+ int op_length = 0;
+ int fmt;
+ int extended = 0x1800;
+ bfd_boolean extended_op = FALSE;
+ bfd_boolean addr_op;
+ const char * error_message;
+ static signed int repeat_count = 0;
+ bfd_boolean fix_emitted;
+ bfd_boolean nop_check_needed = FALSE;
+
+ /* Opcode is the one from opcodes table
+ line contains something like
+ [.w] @r2+, 5(R1)
+ or
+ .b @r2+, 5(R1). */
+
+ byte_op = FALSE;
+ addr_op = FALSE;
+ if (*line == '.')
+ {
+ bfd_boolean check = FALSE;
+ ++ line;
+
+ switch (TOLOWER (* line))
+ {
+ case 'b':
+ /* Byte operation. */
+ bin |= BYTE_OPERATION;
+ byte_op = TRUE;
+ check = TRUE;
+ break;
+
+ case 'a':
+ /* "Address" ops work on 20-bit values. */
+ addr_op = TRUE;
+ bin |= BYTE_OPERATION;
+ check = TRUE;
+ break;
+
+ case 'w':
+ /* Word operation - this is the default. */
+ check = TRUE;
+ break;
+
+ case 0:
+ case ' ':
+ case '\n':
+ case '\r':
+ as_warn (_("no size modifier after period, .w assumed"));
+ break;
+
+ default:
+ as_bad (_("unrecognised instruction size modifier .%c"),
+ * line);
+ return 0;
+ }
+
+ if (check)
+ {
+ ++ line;
+
+ }
+ }
+
+ if (*line && ! ISSPACE (*line))
+ {
+ as_bad (_("junk found after instruction: %s.%s"),
+ opcode->name, line);
+ return 0;
+ }
+
+ /* Catch the case where the programmer has used a ".a" size modifier on an
+ instruction that does not support it. Look for an alternative extended
+ instruction that has the same name without the period. Eg: "add.a"
+ becomes "adda". Although this not an officially supported way of
+ specifing instruction aliases other MSP430 assemblers allow it. So we
+ support it for compatibility purposes. */
+ if (addr_op && opcode->fmt >= 0)
+ {
+ char * old_name = opcode->name;
+ char real_name[32];
+
+ sprintf (real_name, "%sa", old_name);
+ opcode = hash_find (msp430_hash, real_name);
+ if (opcode == NULL)
+ {
+ as_bad (_("instruction %s.a does not exist"), old_name);
+ return 0;
+ }
+#if 0 /* Enable for debugging. */
+ as_warn ("treating %s.a as %s", old_name, real_name);
+#endif
+ addr_op = FALSE;
+ bin = opcode->bin_opcode;
+ }
+
+ if (opcode->fmt != -1
+ && opcode->insn_opnumb
+ && (!*line || *line == '\n'))
+ {
+ as_bad (_("instruction %s requires %d operand(s)"),
+ opcode->name, opcode->insn_opnumb);
+ return 0;
+ }
+
+ memset (l1, 0, sizeof (l1));
+ memset (l2, 0, sizeof (l2));
+ memset (&op1, 0, sizeof (op1));
+ memset (&op2, 0, sizeof (op2));
+
+ imm_op = FALSE;
+
+ if ((fmt = opcode->fmt) < 0)
+ {
+ if (! target_is_430x ())
+ {
+ as_bad (_("instruction %s requires MSP430X mcu"),
+ opcode->name);
+ return 0;
+ }
+
+ fmt = (-fmt) - 1;
+ extended_op = TRUE;
+ }
+
+ if (repeat_count)
+ {
+ /* If requested set the extended instruction repeat count. */
+ if (extended_op)
+ {
+ if (repeat_count > 0)
+ extended |= (repeat_count - 1);
+ else
+ extended |= (1 << 7) | (- repeat_count);
+ }
+ else
+ as_bad (_("unable to repeat %s insn"), opcode->name);
+
+ repeat_count = 0;
+ }
+
+ if (check_for_nop && is_opcode ("nop"))
+ check_for_nop = FALSE;
+
+ switch (fmt)
+ {
+ case 0: /* Emulated. */
+ switch (opcode->insn_opnumb)
+ {
+ case 0:
+ if (is_opcode ("eint") || is_opcode ("dint"))
+ {
+ if (check_for_nop)
+ {
+ if (warn_interrupt_nops)
+ {
+ if (gen_interrupt_nops)
+ as_warn (_("NOP inserted between two instructions that change interrupt state"));
+ else
+ as_warn (_("a NOP might be needed here because of successive changes in interrupt state"));
+ }
+
+ if (gen_interrupt_nops)
+ {
+ /* Emit a NOP between interrupt enable/disable.
+ See 1.3.4.1 of the MSP430x5xx User Guide. */
+ insn_length += 2;
+ frag = frag_more (2);
+ bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag);
+ }
+ }
+
+ nop_check_needed = TRUE;
+ }
+
+ /* Set/clear bits instructions. */
+ if (extended_op)
+ {
+ if (!addr_op)
+ extended |= BYTE_OPERATION;
+
+ /* Emit the extension word. */
+ insn_length += 2;
+ frag = frag_more (2);
+ bfd_putl16 (extended, frag);
+ }
+
+ insn_length += 2;
+ frag = frag_more (2);
+ bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (insn_length);
+ break;
+
+ case 1:
+ /* Something which works with destination operand. */
+ line = extract_operand (line, l1, sizeof (l1));
+ res = msp430_dstoperand (&op1, l1, opcode->bin_opcode, extended_op, TRUE);
+ if (res)
+ break;
+
+ bin |= (op1.reg | (op1.am << 7));
+
+ if (is_opcode ("clr") && bin == 0x4302 /* CLR R2*/)
+ {
+ if (check_for_nop)
+ {
+ if (warn_interrupt_nops)
+ {
+ if (gen_interrupt_nops)
+ as_warn (_("NOP inserted between two instructions that change interrupt state"));
+ else
+ as_warn (_("a NOP might be needed here because of successive changes in interrupt state"));
+ }
+
+ if (gen_interrupt_nops)
+ {
+ /* Emit a NOP between interrupt enable/disable.
+ See 1.3.4.1 of the MSP430x5xx User Guide. */
+ insn_length += 2;
+ frag = frag_more (2);
+ bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag);
+ }
+ }
+
+ nop_check_needed = TRUE;
+ }
+
+ /* Compute the entire instruction length, in bytes. */
+ op_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2);
+ insn_length += op_length;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+
+ if (extended_op)
+ {
+ if (!addr_op)
+ extended |= BYTE_OPERATION;
+
+ if (op1.ol != 0 && ((extended & 0xf) != 0))
+ {
+ as_bad (_("repeat instruction used with non-register mode instruction"));
+ extended &= ~ 0xf;
+ }
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;
+
+ else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_EXT_SRC);
+ else
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_PCR20_EXT_SRC);
+ }
+
+ /* Emit the extension word. */
+ bfd_putl16 (extended, frag);
+ frag += 2;
+ where += 2;
+ }
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+ frag += 2;
+ where += 2;
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag);
+
+ if (!extended_op)
+ {
+ if (op1.reg)
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+ }
+
+ dwarf2_emit_insn (insn_length);
+ break;
+
+ case 2:
+ /* Shift instruction. */
+ line = extract_operand (line, l1, sizeof (l1));
+ strncpy (l2, l1, sizeof (l2));
+ l2[sizeof (l2) - 1] = '\0';
+ res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, TRUE);
+ res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, extended_op, TRUE);
+
+ if (res)
+ break; /* An error occurred. All warnings were done before. */
+
+ insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2) + (op2.ol * 2);
+ frag = frag_more (insn_length);
+ where = frag - frag_now->fr_literal;
+
+ if (target_is_430xv2 ()
+ && op1.mode == OP_REG
+ && op1.reg == 0
+ && (is_opcode ("rlax")
+ || is_opcode ("rlcx")
+ || is_opcode ("rla")
+ || is_opcode ("rlc")))
+ {
+ as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
+ break;
+ }
+
+ if (extended_op)
+ {
+ if (!addr_op)
+ extended |= BYTE_OPERATION;
+
+ if ((op1.ol != 0 || op2.ol != 0) && ((extended & 0xf) != 0))
+ {
+ as_bad (_("repeat instruction used with non-register mode instruction"));
+ extended &= ~ 0xf;
+ }
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;
+
+ else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_EXT_SRC);
+ else
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_PCR20_EXT_SRC);
+ }
+
+ if (op2.mode == OP_EXP)
+ {
+ if (op2.exp.X_op == O_constant)
+ extended |= (op2.exp.X_add_number >> 16) & 0xf;
+
+ else if (op1.mode == OP_EXP)
+ fix_new_exp (frag_now, where, 8, &(op2.exp), FALSE,
+ op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_ODST
+ : BFD_RELOC_MSP430X_PCR20_EXT_ODST);
+ else
+ fix_new_exp (frag_now, where, 6, &(op2.exp), FALSE,
+ op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_DST
+ : BFD_RELOC_MSP430X_PCR20_EXT_DST);
+ }
+
+ /* Emit the extension word. */
+ bfd_putl16 (extended, frag);
+ frag += 2;
+ where += 2;
+ }
+
+ bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7));
+ bfd_putl16 ((bfd_vma) bin, frag);
+ frag += 2;
+ where += 2;
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag);
+
+ if (!extended_op)
+ {
+ if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+ frag += 2;
+ where += 2;
+ }
+
+ if (op2.mode == OP_EXP)
+ {
+ if (op2.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op2.exp.X_add_number & 0xffff, frag);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag);
+
+ if (!extended_op)
+ {
+ if (op2.reg) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 2,
+ &(op2.exp), FALSE, CHECK_RELOC_MSP430 (op2));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+ }
+
+ dwarf2_emit_insn (insn_length);
+ break;
+
+ case 3:
+ /* Branch instruction => mov dst, r0. */
+ if (extended_op)
+ {
+ as_bad ("Internal error: state 0/3 not coded for extended instructions");
+ break;
+ }
+
+ line = extract_operand (line, l1, sizeof (l1));
+ res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, FALSE);
+ if (res)
+ break;
+
+ byte_op = FALSE;
+ imm_op = FALSE;
+ bin |= ((op1.reg << 8) | (op1.am << 4));
+ op_length = 2 + 2 * op1.ol;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op1.exp.X_add_number & 0xffff, frag + 2);
+ }
+ else
+ {
+ where += 2;
+
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+
+ if (op1.reg || (op1.reg == 0 && op1.am == 3))
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+
+ dwarf2_emit_insn (insn_length + op_length);
+ break;
+
+ case 4:
+ /* CALLA instructions. */
+ fix_emitted = FALSE;
+
+ line = extract_operand (line, l1, sizeof (l1));
+ imm_op = FALSE;
+
+ res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op,
+ extended_op, FALSE);
+ if (res)
+ break;
+
+ byte_op = FALSE;
+
+ op_length = 2 + 2 * op1.ol;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+
+ if (imm_op)
+ {
+ if (op1.am == 3)
+ {
+ bin |= 0xb0;
+
+ fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_ADR_DST);
+ fix_emitted = TRUE;
+ }
+ else if (op1.am == 1)
+ {
+ if (op1.reg == 0)
+ {
+ bin |= 0x90;
+
+ fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_PCR20_CALL);
+ fix_emitted = TRUE;
+ }
+ else
+ bin |= 0x50 | op1.reg;
+ }
+ else if (op1.am == 0)
+ bin |= 0x40 | op1.reg;
+ }
+ else if (op1.am == 1)
+ {
+ bin |= 0x80;
+
+ fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_ADR_DST);
+ fix_emitted = TRUE;
+ }
+ else if (op1.am == 2)
+ bin |= 0x60 | op1.reg;
+ else if (op1.am == 3)
+ bin |= 0x70 | op1.reg;
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.ol != 1)
+ {
+ as_bad ("Internal error: unexpected CALLA instruction length: %d\n", op1.ol);
+ break;
+ }
+
+ bfd_putl16 ((bfd_vma) ZEROS, frag + 2);
+
+ if (! fix_emitted)
+ fix_new_exp (frag_now, where + 2, 2,
+ &(op1.exp), FALSE, BFD_RELOC_16);
+ }
+
+ dwarf2_emit_insn (insn_length + op_length);
+ break;
+
+ case 5:
+ {
+ int n;
+ int reg;
+
+ /* [POP|PUSH]M[.A] #N, Rd */
+ line = extract_operand (line, l1, sizeof (l1));
+ line = extract_operand (line, l2, sizeof (l2));
+
+ if (*l1 != '#')
+ {
+ as_bad (_("expected #n as first argument of %s"), opcode->name);
+ break;
+ }
+ parse_exp (l1 + 1, &(op1.exp));
+ if (op1.exp.X_op != O_constant)
+ {
+ as_bad (_("expected constant expression for first argument of %s"),
+ opcode->name);
+ break;
+ }
+
+ if ((reg = check_reg (l2)) == -1)
+ {
+ as_bad (_("expected register as second argument of %s"),
+ opcode->name);
+ break;
+ }
+
+ op_length = 2;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+ bin = opcode->bin_opcode;
+ if (! addr_op)
+ bin |= 0x100;
+ n = op1.exp.X_add_number;
+ bin |= (n - 1) << 4;
+ if (is_opcode ("pushm"))
+ bin |= reg;
+ else
+ {
+ if (reg - n + 1 < 0)
+ {
+ as_bad (_("Too many registers popped"));
+ break;
+ }
+
+ /* CPU21 errata: cannot use POPM to restore the SR register. */
+ if (target_is_430xv2 ()
+ && (reg - n + 1 < 3)
+ && reg >= 2
+ && is_opcode ("popm"))
+ {
+ as_bad (_("Cannot use POPM to restore the SR register"));
+ break;
+ }
+
+ bin |= (reg - n + 1);
+ }
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (op_length);
+ break;
+ }
+
+ case 6:
+ {
+ int n;
+ int reg;
+
+ /* Bit rotation instructions. RRCM, RRAM, RRUM, RLAM. */
+ if (extended & 0xff)
+ {
+ as_bad (_("repeat count cannot be used with %s"), opcode->name);
+ break;
+ }
+
+ line = extract_operand (line, l1, sizeof (l1));
+ line = extract_operand (line, l2, sizeof (l2));
+
+ if (*l1 != '#')
+ {
+ as_bad (_("expected #n as first argument of %s"), opcode->name);
+ break;
+ }
+ parse_exp (l1 + 1, &(op1.exp));
+ if (op1.exp.X_op != O_constant)
+ {
+ as_bad (_("expected constant expression for first argument of %s"),
+ opcode->name);
+ break;
+ }
+ n = op1.exp.X_add_number;
+ if (n > 4 || n < 1)
+ {
+ as_bad (_("expected first argument of %s to be in the range 1-4"),
+ opcode->name);
+ break;
+ }
+
+ if ((reg = check_reg (l2)) == -1)
+ {
+ as_bad (_("expected register as second argument of %s"),
+ opcode->name);
+ break;
+ }
+
+ if (target_is_430xv2 () && reg == 0)
+ {
+ as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
+ break;
+ }
+
+ op_length = 2;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+
+ bin = opcode->bin_opcode;
+ if (! addr_op)
+ bin |= 0x10;
+ bin |= (n - 1) << 10;
+ bin |= reg;
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (op_length);
+ break;
+ }
+
+ case 7:
+ {
+ int reg;
+
+ /* RRUX: Synthetic unsigned right shift of a register by one bit. */
+ if (extended & 0xff)
+ {
+ as_bad (_("repeat count cannot be used with %s"), opcode->name);
+ break;
+ }
+
+ line = extract_operand (line, l1, sizeof (l1));
+ if ((reg = check_reg (l1)) == -1)
+ {
+ as_bad (_("expected register as argument of %s"),
+ opcode->name);
+ break;
+ }
+
+ if (target_is_430xv2 () && reg == 0)
+ {
+ as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
+ break;
+ }
+
+ if (byte_op)
+ {
+ /* Tricky - there is no single instruction that will do this.
+ Encode as: RRA.B rN { BIC.B #0x80, rN */
+ op_length = 6;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+ bin = 0x1140 | reg;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (2);
+ bin = 0xc070 | reg;
+ bfd_putl16 ((bfd_vma) bin, frag + 2);
+ bin = 0x0080;
+ bfd_putl16 ((bfd_vma) bin, frag + 4);
+ dwarf2_emit_insn (4);
+ }
+ else
+ {
+ /* Encode as RRUM[.A] rN. */
+ bin = opcode->bin_opcode;
+ if (! addr_op)
+ bin |= 0x10;
+ bin |= reg;
+ op_length = 2;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (op_length);
+ }
+ break;
+ }
+
+ case 8:
+ {
+ bfd_boolean need_reloc = FALSE;
+ int n;
+ int reg;
+
+ /* ADDA, CMPA and SUBA address instructions. */
+ if (extended & 0xff)
+ {
+ as_bad (_("repeat count cannot be used with %s"), opcode->name);
+ break;
+ }
+
+ line = extract_operand (line, l1, sizeof (l1));
+ line = extract_operand (line, l2, sizeof (l2));
+
+ bin = opcode->bin_opcode;
+
+ if (*l1 == '#')
+ {
+ parse_exp (l1 + 1, &(op1.exp));
+
+ if (op1.exp.X_op == O_constant)
+ {
+ n = op1.exp.X_add_number;
+ if (n > 0xfffff || n < - (0x7ffff))
+ {
+ as_bad (_("expected value of first argument of %s to fit into 20-bits"),
+ opcode->name);
+ break;
+ }
+
+ bin |= ((n >> 16) & 0xf) << 8;
+ }
+ else
+ {
+ n = 0;
+ need_reloc = TRUE;
+ }
+
+ op_length = 4;
+ }
+ else
+ {
+ if ((n = check_reg (l1)) == -1)
+ {
+ as_bad (_("expected register name or constant as first argument of %s"),
+ opcode->name);
+ break;
+ }
+
+ bin |= (n << 8) | (1 << 6);
+ op_length = 2;
+ }
+
+ if ((reg = check_reg (l2)) == -1)
+ {
+ as_bad (_("expected register as second argument of %s"),
+ opcode->name);
+ break;
+ }
+
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+ bin |= reg;
+ if (need_reloc)
+ fix_new_exp (frag_now, where, 4, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_ADR_SRC);
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+ if (op_length == 4)
+ bfd_putl16 ((bfd_vma) (n & 0xffff), frag + 2);
+ dwarf2_emit_insn (op_length);
+ break;
+ }
+
+ case 9: /* MOVA, BRA, RETA. */
+ imm_op = FALSE;
+ bin = opcode->bin_opcode;
+
+ if (is_opcode ("reta"))
+ {
+ /* The RETA instruction does not take any arguments.
+ The implicit first argument is @SP+.
+ The implicit second argument is PC. */
+ op1.mode = OP_REG;
+ op1.am = 3;
+ op1.reg = 1;
+
+ op2.mode = OP_REG;
+ op2.reg = 0;
+ }
+ else
+ {
+ line = extract_operand (line, l1, sizeof (l1));
+ res = msp430_srcoperand (&op1, l1, opcode->bin_opcode,
+ &imm_op, extended_op, FALSE);
+
+ if (is_opcode ("bra"))
+ {
+ /* This is the BRA synthetic instruction.
+ The second argument is always PC. */
+ op2.mode = OP_REG;
+ op2.reg = 0;
+ }
+ else
+ {
+ line = extract_operand (line, l2, sizeof (l2));
+ res += msp430_dstoperand (&op2, l2, opcode->bin_opcode,
+ extended_op, TRUE);
+ }
+
+ if (res)
+ break; /* Error occurred. All warnings were done before. */
+ }
+
+ /* Only a restricted subset of the normal MSP430 addressing modes
+ are supported here, so check for the ones that are allowed. */
+ if ((op_length = try_encode_mova (imm_op, bin, & op1, & op2,
+ & error_message)) == 0)
+ {
+ as_bad (error_message, opcode->name);
+ break;
+ }
+ dwarf2_emit_insn (op_length);
+ break;
+
+ case 10: /* RPT */
+ line = extract_operand (line, l1, sizeof l1);
+ /* The RPT instruction only accepted immediates and registers. */
+ if (*l1 == '#')
+ {
+ parse_exp (l1 + 1, &(op1.exp));
+ if (op1.exp.X_op != O_constant)
+ {
+ as_bad (_("expected constant value as argument to RPT"));
+ break;
+ }
+ if (op1.exp.X_add_number < 1
+ || op1.exp.X_add_number > (1 << 4))
+ {
+ as_bad (_("expected constant in the range 2..16"));
+ break;
+ }
+
+ /* We silently accept and ignore a repeat count of 1. */
+ if (op1.exp.X_add_number > 1)
+ repeat_count = op1.exp.X_add_number;
+ }
+ else
+ {
+ int reg;
+
+ if ((reg = check_reg (l1)) != -1)
+ {
+ if (reg == 0)
+ as_warn (_("PC used as an argument to RPT"));
+ else
+ repeat_count = - reg;
+ }
+ else
+ {
+ as_bad (_("expected constant or register name as argument to RPT insn"));
+ break;
+ }
+ }
+ break;
+
+ default:
+ as_bad (_("Illegal emulated instruction "));
+ break;
+ }
+ break;
+
+ case 1: /* Format 1, double operand. */
+ line = extract_operand (line, l1, sizeof (l1));
+ line = extract_operand (line, l2, sizeof (l2));
+ res = msp430_srcoperand (&op1, l1, opcode->bin_opcode, &imm_op, extended_op, TRUE);
+ res += msp430_dstoperand (&op2, l2, opcode->bin_opcode, extended_op, TRUE);
+
+ if (res)
+ break; /* Error occurred. All warnings were done before. */
+
+ if (extended_op
+ && is_opcode ("movx")
+ && addr_op
+ && msp430_enable_relax)
+ {
+ /* This is the MOVX.A instruction. See if we can convert
+ it into the MOVA instruction instead. This saves 2 bytes. */
+ if ((op_length = try_encode_mova (imm_op, 0x0000, & op1, & op2,
+ NULL)) != 0)
+ {
+ dwarf2_emit_insn (op_length);
+ break;
+ }
+ }
+
+ bin |= (op2.reg | (op1.reg << 8) | (op1.am << 4) | (op2.am << 7));
+
+ if ( (is_opcode ("bic") && bin == 0xc232)
+ || (is_opcode ("bis") && bin == 0xd232)
+ || (is_opcode ("mov") && op2.mode == OP_REG && op2.reg == 2))
+ {
+ if (check_for_nop)
+ {
+ if (warn_interrupt_nops)
+ {
+ if (gen_interrupt_nops)
+ as_warn (_("NOP inserted between two instructions that change interrupt state"));
+ else
+ as_warn (_("a NOP might be needed here because of successive changes in interrupt state"));
+ }
+
+ if (gen_interrupt_nops)
+ {
+ /* Emit a NOP between interrupt enable/disable.
+ See 1.3.4.1 of the MSP430x5xx User Guide. */
+ insn_length += 2;
+ frag = frag_more (2);
+ bfd_putl16 ((bfd_vma) 0x4303 /* NOP */, frag);
+ }
+ }
+
+ nop_check_needed = TRUE;
+ }
+
+ /* Compute the entire length of the instruction in bytes. */
+ op_length = (extended_op ? 2 : 0) /* The extension word. */
+ + 2 /* The opcode */
+ + (2 * op1.ol) /* The first operand. */
+ + (2 * op2.ol); /* The second operand. */
+
+ insn_length += op_length;
+ frag = frag_more (op_length);
+ where = frag - frag_now->fr_literal;
+
+ if (extended_op)
+ {
+ if (!addr_op)
+ extended |= BYTE_OPERATION;
+
+ if ((op1.ol != 0 || op2.ol != 0) && ((extended & 0xf) != 0))
+ {
+ as_bad (_("repeat instruction used with non-register mode instruction"));
+ extended &= ~ 0xf;
+ }
+
+ /* If necessary, emit a reloc to update the extension word. */
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;
+
+ else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_EXT_SRC);
+ else
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_PCR20_EXT_SRC);
+ }
+
+ if (op2.mode == OP_EXP)
+ {
+ if (op2.exp.X_op == O_constant)
+ extended |= (op2.exp.X_add_number >> 16) & 0xf;
+
+ else if (op1.mode == OP_EXP)
+ fix_new_exp (frag_now, where, 8, &(op2.exp), FALSE,
+ op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_ODST
+ : BFD_RELOC_MSP430X_PCR20_EXT_ODST);
+
+ else
+ fix_new_exp (frag_now, where, 6, &(op2.exp), FALSE,
+ op2.reg ? BFD_RELOC_MSP430X_ABS20_EXT_DST
+ : BFD_RELOC_MSP430X_PCR20_EXT_DST);
+ }
+
+ /* Emit the extension word. */
+ bfd_putl16 (extended, frag);
+ where += 2;
+ frag += 2;
+ }
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+ where += 2;
+ frag += 2;
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag);
+
+ if (!extended_op)
+ {
+ if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+
+ where += 2;
+ frag += 2;
+ }
+
+ if (op2.mode == OP_EXP)
+ {
+ if (op2.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op2.exp.X_add_number & 0xffff, frag);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag);
+
+ if (!extended_op)
+ {
+ if (op2.reg) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 2,
+ &(op2.exp), FALSE, CHECK_RELOC_MSP430 (op2));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op2.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+ }
+
+ dwarf2_emit_insn (insn_length);
+ break;
+
+ case 2: /* Single-operand mostly instr. */
+ if (opcode->insn_opnumb == 0)
+ {
+ /* reti instruction. */
+ insn_length += 2;
+ frag = frag_more (2);
+ bfd_putl16 ((bfd_vma) bin, frag);
+ dwarf2_emit_insn (insn_length);
+ break;
+ }
+
+ line = extract_operand (line, l1, sizeof (l1));
+ res = msp430_srcoperand (&op1, l1, opcode->bin_opcode,
+ &imm_op, extended_op, TRUE);
+ if (res)
+ break; /* Error in operand. */
+
+ if (target_is_430xv2 ()
+ && op1.mode == OP_REG
+ && op1.reg == 0
+ && (is_opcode ("rrax")
+ || is_opcode ("rrcx")
+ || is_opcode ("rra")
+ || is_opcode ("rrc")))
+ {
+ as_bad (_("%s: attempt to rotate the PC register"), opcode->name);
+ break;
+ }
+
+ insn_length = (extended_op ? 2 : 0) + 2 + (op1.ol * 2);
+ frag = frag_more (insn_length);
+ where = frag - frag_now->fr_literal;
+
+ if (extended_op)
+ {
+ if (is_opcode ("swpbx") || is_opcode ("sxtx"))
+ {
+ /* These two instructions use a special
+ encoding of the A/L and B/W bits. */
+ bin &= ~ BYTE_OPERATION;
+
+ if (byte_op)
+ {
+ as_bad (_("%s instruction does not accept a .b suffix"),
+ opcode->name);
+ break;
+ }
+ else if (! addr_op)
+ extended |= BYTE_OPERATION;
+ }
+ else if (! addr_op)
+ extended |= BYTE_OPERATION;
+
+ if (op1.ol != 0 && ((extended & 0xf) != 0))
+ {
+ as_bad (_("repeat instruction used with non-register mode instruction"));
+ extended &= ~ 0xf;
+ }
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ extended |= ((op1.exp.X_add_number >> 16) & 0xf) << 7;
+
+ else if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_ABS20_EXT_SRC);
+ else
+ fix_new_exp (frag_now, where, 6, &(op1.exp), FALSE,
+ BFD_RELOC_MSP430X_PCR20_EXT_SRC);
+ }
+
+ /* Emit the extension word. */
+ bfd_putl16 (extended, frag);
+ frag += 2;
+ where += 2;
+ }
+
+ bin |= op1.reg | (op1.am << 4);
+ bfd_putl16 ((bfd_vma) bin, frag);
+ frag += 2;
+ where += 2;
+
+ if (op1.mode == OP_EXP)
+ {
+ if (op1.exp.X_op == O_constant)
+ {
+ bfd_putl16 (op1.exp.X_add_number & 0xffff, frag);
+ }
+ else
+ {
+ bfd_putl16 ((bfd_vma) ZEROS, frag);
+
+ if (!extended_op)
+ {
+ if (op1.reg || (op1.reg == 0 && op1.am == 3)) /* Not PC relative. */
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), FALSE, CHECK_RELOC_MSP430 (op1));
+ else
+ fix_new_exp (frag_now, where, 2,
+ &(op1.exp), TRUE, CHECK_RELOC_MSP430_PCREL);
+ }
+ }
+ }
+
+ dwarf2_emit_insn (insn_length);
+ break;
+
+ case 3: /* Conditional jumps instructions. */
+ line = extract_operand (line, l1, sizeof (l1));
+ /* l1 is a label. */
+ if (l1[0])
+ {
+ char *m = l1;
+ expressionS exp;
+
+ if (*m == '$')
+ m++;
+
+ parse_exp (m, &exp);
+
+ /* In order to handle something like:
+
+ and #0x8000, r5
+ tst r5
+ jz 4 ; skip next 4 bytes
+ inv r5
+ inc r5
+ nop ; will jump here if r5 positive or zero
+
+ jCOND -n ;assumes jump n bytes backward:
+
+ mov r5,r6
+ jmp -2
+
+ is equal to:
+ lab:
+ mov r5,r6
+ jmp lab
+
+ jCOND $n ; jump from PC in either direction. */
+
+ if (exp.X_op == O_constant)
+ {
+ int x = exp.X_add_number;
+
+ if (x & 1)
+ {
+ as_warn (_("Even number required. Rounded to %d"), x + 1);
+ x++;
+ }
+
+ if ((*l1 == '$' && x > 0) || x < 0)
+ x -= 2;
+
+ x >>= 1;
+
+ if (x > 512 || x < -511)
+ {
+ as_bad (_("Wrong displacement %d"), x << 1);
+ break;
+ }
+
+ insn_length += 2;
+ frag = frag_more (2); /* Instr size is 1 word. */
+
+ bin |= x & 0x3ff;
+ bfd_putl16 ((bfd_vma) bin, frag);
+ }
+ else if (exp.X_op == O_symbol && *l1 != '$')
+ {
+ insn_length += 2;
+ frag = frag_more (2); /* Instr size is 1 word. */
+ where = frag - frag_now->fr_literal;
+ fix_new_exp (frag_now, where, 2,
+ &exp, TRUE, BFD_RELOC_MSP430_10_PCREL);
+
+ bfd_putl16 ((bfd_vma) bin, frag);
+ }
+ else if (*l1 == '$')
+ {
+ as_bad (_("instruction requires label sans '$'"));
+ }
+ else
+ as_bad (_
+ ("instruction requires label or value in range -511:512"));
+ dwarf2_emit_insn (insn_length);
+ break;
+ }
+ else
+ {
+ as_bad (_("instruction requires label"));
+ break;
+ }
+ 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])
+ {
+ char *m = l1;
+ expressionS exp;
+
+ /* Ignore absolute addressing. make it PC relative anyway. */
+ if (*m == '#' || *m == '$')
+ m++;
+
+ parse_exp (m, & exp);
+ if (exp.X_op == O_symbol)
+ {
+ /* Relaxation required. */
+ struct rcodes_s rc = msp430_rcodes[opcode->insn_opnumb];
+
+ if (target_is_430x ())
+ rc = msp430x_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. */
+ insn_length += 8;
+ frag = frag_more (8);
+ dwarf2_emit_insn (0);
+ bfd_putl16 ((bfd_vma) rc.sop, frag);
+ frag = frag_variant (rs_machine_dependent, 8, 2,
+ /* Wild guess. */
+ ENCODE_RELAX (rc.lpos, STATE_BITS10),
+ exp.X_add_symbol,
+ 0, /* Offset is zero if jump dist less than 1K. */
+ (char *) frag);
+ break;
+ }
+ }
+
+ as_bad (_("instruction requires label"));
+ 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])
+ {
+ char * m = l1;
+ expressionS exp;
+
+ /* Ignore absolute addressing. make it PC relative anyway. */
+ if (*m == '#' || *m == '$')
+ m++;
+
+ parse_exp (m, & exp);
+ if (exp.X_op == O_symbol)
+ {
+ /* Relaxation required. */
+ struct hcodes_s hc = msp430_hcodes[opcode->insn_opnumb];
+
+ if (target_is_430x ())
+ hc = msp430x_hcodes[opcode->insn_opnumb];
+
+ insn_length += 8;
+ 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,
+ 0, /* Offset is zero if jump dist less than 1K. */
+ (char *) frag);
+ break;
+ }
+ }
+
+ as_bad (_("instruction requires label"));
+ break;
+
+ default:
+ as_bad (_("Illegal instruction or not implemented opcode."));
+ }
+
+ input_line_pointer = line;
+ check_for_nop = nop_check_needed;
+ return 0;
+}
+
+void
+md_assemble (char * str)
+{
+ struct msp430_opcode_s * opcode;
+ char cmd[32];
+ unsigned int i = 0;
+
+ str = skip_space (str); /* Skip leading spaces. */
+ str = extract_cmd (str, cmd, sizeof (cmd));
+
+ while (cmd[i] && i < sizeof (cmd))
+ {
+ char a = TOLOWER (cmd[i]);
+ cmd[i] = a;
+ i++;
+ }
+
+ if (!cmd[0])
+ {
+ as_bad (_("can't find opcode "));
+ return;
+ }
+
+ opcode = (struct msp430_opcode_s *) hash_find (msp430_hash, cmd);
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode `%s'"), cmd);
+ return;
+ }
+
+ {
+ char *__t = input_line_pointer;
+
+ msp430_operands (opcode, str);
+ input_line_pointer = __t;
+ }
+}
+
+/* GAS will call this function for each section at the end of the assembly,
+ to permit the CPU backend to adjust the alignment of a section. */
+
+valueT
+md_section_align (asection * seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* If you define this macro, it should return the offset between the
+ address of a PC relative fixup and the position from which the PC
+ relative adjustment should be made. On many processors, the base
+ of a PC relative instruction is the next instruction, so this
+ macro would return the length of an instruction. */
+
+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)))
+ return 0;
+
+ 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 (fixp->fx_r_type == BFD_RELOC_MSP430_10_PCREL)
+ return 1;
+ if (fixp->fx_pcrel)
+ return 1;
+ if (msp430_enable_polys
+ && !msp430_enable_relax)
+ return 1;
+
+ 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)
+{
+ unsigned char * where;
+ unsigned long insn;
+ long value;
+
+ if (fixp->fx_addsy == (symbolS *) NULL)
+ {
+ value = *valuep;
+ fixp->fx_done = 1;
+ }
+ else if (fixp->fx_pcrel)
+ {
+ segT s = S_GET_SEGMENT (fixp->fx_addsy);
+
+ if (fixp->fx_addsy && (s == seg || s == absolute_section))
+ {
+ /* FIXME: We can appear here only in case if we perform a pc
+ relative jump to the label which is i) global, ii) locally
+ defined or this is a jump to an absolute symbol.
+ If this is an absolute symbol -- everything is OK.
+ If this is a global label, we've got a symbol value defined
+ twice:
+ 1. S_GET_VALUE (fixp->fx_addsy) will contain a symbol offset
+ from this section start
+ 2. *valuep will contain the real offset from jump insn to the
+ label
+ So, the result of S_GET_VALUE (fixp->fx_addsy) + (* valuep);
+ will be incorrect. Therefore remove s_get_value. */
+ value = /* S_GET_VALUE (fixp->fx_addsy) + */ * valuep;
+ fixp->fx_done = 1;
+ }
+ else
+ value = *valuep;
+ }
+ else
+ {
+ value = fixp->fx_offset;
+
+ if (fixp->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ {
+ value -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_done = 1;
+ }
+ }
+ }
+
+ 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)
+ {
+ 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)
+ {
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = (unsigned char *) fixp->fx_frag->fr_literal + fixp->fx_where;
+
+ insn = bfd_getl16 (where);
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_MSP430_10_PCREL:
+ if (value & 1)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("odd address operand: %ld"), value);
+
+ /* Jumps are in words. */
+ value >>= 1;
+ --value; /* Correct PC. */
+
+ if (value < -512 || value > 511)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("operand out of range: %ld"), value);
+
+ value &= 0x3ff; /* get rid of extended sign */
+ bfd_putl16 ((bfd_vma) (value | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_PCR16:
+ case BFD_RELOC_MSP430_RL_PCREL:
+ case BFD_RELOC_MSP430_16_PCREL:
+ if (value & 1)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("odd address operand: %ld"), value);
+ /* Fall through. */
+
+ case BFD_RELOC_MSP430_16_PCREL_BYTE:
+ /* Nothing to be corrected here. */
+ if (value < -32768 || value > 65536)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("operand out of range: %ld"), value);
+ /* Fall through. */
+
+ case BFD_RELOC_MSP430X_ABS16:
+ case BFD_RELOC_MSP430_16:
+ case BFD_RELOC_16:
+ case BFD_RELOC_MSP430_16_BYTE:
+ value &= 0xffff; /* Get rid of extended sign. */
+ bfd_putl16 ((bfd_vma) value, where);
+ break;
+
+ case BFD_RELOC_MSP430_ABS_HI16:
+ value >>= 16;
+ value &= 0xffff; /* Get rid of extended sign. */
+ bfd_putl16 ((bfd_vma) value, where);
+ break;
+
+ case BFD_RELOC_32:
+ bfd_putl16 ((bfd_vma) value, where);
+ break;
+
+ case BFD_RELOC_MSP430_ABS8:
+ case BFD_RELOC_8:
+ bfd_put_8 (NULL, (bfd_vma) value, where);
+ break;
+
+ case BFD_RELOC_MSP430X_ABS20_EXT_SRC:
+ case BFD_RELOC_MSP430X_PCR20_EXT_SRC:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 4);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) (((value & 0xf) << 7) | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_ABS20_ADR_SRC:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) (((value & 0xf) << 8) | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_ABS20_EXT_ODST:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 6);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_PCR20_CALL:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_ABS20_EXT_DST:
+ case BFD_RELOC_MSP430X_PCR20_EXT_DST:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 4);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_PCR20_EXT_ODST:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 6);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
+ break;
+
+ case BFD_RELOC_MSP430X_ABS20_ADR_DST:
+ bfd_putl16 ((bfd_vma) (value & 0xffff), where + 2);
+ value >>= 16;
+ bfd_putl16 ((bfd_vma) ((value & 0xf) | insn), where);
+ break;
+
+ default:
+ as_fatal (_("line %d: unknown relocation type: 0x%x"),
+ fixp->fx_line, fixp->fx_r_type);
+ break;
+ }
+ }
+ else
+ {
+ fixp->fx_addnumber = value;
+ }
+}
+
+static bfd_boolean
+S_IS_GAS_LOCAL (symbolS * s)
+{
+ const char * name;
+ unsigned int len;
+
+ if (s == NULL)
+ return FALSE;
+ name = S_GET_NAME (s);
+ len = strlen (name) - 1;
+
+ return name[len] == 1 || name[len] == 2;
+}
+
+/* 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. */
+
+arelent **
+tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ static arelent * no_relocs = NULL;
+ static arelent * relocs[MAX_RELOC_EXPANSION + 1];
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ free (reloc);
+ return & no_relocs;
+ }
+
+ relocs[0] = reloc;
+ relocs[1] = NULL;
+
+ if (fixp->fx_subsy
+ && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ {
+ fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_subsy = NULL;
+ }
+
+ if (fixp->fx_addsy && fixp->fx_subsy)
+ {
+ asection *asec, *ssec;
+
+ asec = S_GET_SEGMENT (fixp->fx_addsy);
+ ssec = S_GET_SEGMENT (fixp->fx_subsy);
+
+ /* If we have a difference between two different, non-absolute symbols
+ we must generate two relocs (one for each symbol) and allow the
+ linker to resolve them - relaxation may change the distances between
+ symbols, even local symbols defined in the same section.
+
+ Unfortunately we cannot do this with assembler generated local labels
+ because there can be multiple incarnations of the same label, with
+ exactly the same name, in any given section and the linker will have
+ no way to identify the correct one. Instead we just have to hope
+ that no relaxtion will occur between the local label and the other
+ symbol in the expression.
+
+ Similarly we have to compute differences between symbols in the .eh_frame
+ section as the linker is not smart enough to apply relocations there
+ before attempting to process it. */
+ if ((ssec != absolute_section || asec != absolute_section)
+ && (fixp->fx_addsy != fixp->fx_subsy)
+ && strcmp (ssec->name, ".eh_frame") != 0
+ && ! S_IS_GAS_LOCAL (fixp->fx_addsy)
+ && ! S_IS_GAS_LOCAL (fixp->fx_subsy))
+ {
+ arelent * reloc2 = xmalloc (sizeof * reloc);
+
+ relocs[0] = reloc2;
+ relocs[1] = reloc;
+
+ reloc2->address = reloc->address;
+ reloc2->howto = bfd_reloc_type_lookup (stdoutput,
+ BFD_RELOC_MSP430_SYM_DIFF);
+ reloc2->addend = - S_GET_VALUE (fixp->fx_subsy);
+
+ if (ssec == absolute_section)
+ reloc2->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ else
+ {
+ reloc2->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc2->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ }
+
+ reloc->addend = fixp->fx_offset;
+ if (asec == absolute_section)
+ {
+ reloc->addend += S_GET_VALUE (fixp->fx_addsy);
+ reloc->sym_ptr_ptr = bfd_abs_section_ptr->symbol_ptr_ptr;
+ }
+ else
+ {
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ }
+
+ fixp->fx_pcrel = 0;
+ fixp->fx_done = 1;
+ return relocs;
+ }
+ else
+ {
+ char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;
+
+ reloc->addend = (S_GET_VALUE (fixp->fx_addsy)
+ - S_GET_VALUE (fixp->fx_subsy) + fixp->fx_offset);
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (fixpos, reloc->addend, 1);
+ break;
+
+ case BFD_RELOC_16:
+ md_number_to_chars (fixpos, reloc->addend, 2);
+ break;
+
+ case BFD_RELOC_24:
+ md_number_to_chars (fixpos, reloc->addend, 3);
+ break;
+
+ case BFD_RELOC_32:
+ md_number_to_chars (fixpos, reloc->addend, 4);
+ break;
+
+ default:
+ reloc->sym_ptr_ptr
+ = (asymbol **) bfd_abs_section_ptr->symbol_ptr_ptr;
+ return relocs;
+ }
+
+ free (reloc);
+ return & no_relocs;
+ }
+ }
+ else
+ {
+#if 0
+ if (fixp->fx_r_type == BFD_RELOC_MSP430X_ABS16
+ && S_GET_SEGMENT (fixp->fx_addsy) == absolute_section)
+ {
+ bfd_vma amount = S_GET_VALUE (fixp->fx_addsy);
+ char *fixpos = fixp->fx_where + fixp->fx_frag->fr_literal;
+
+ md_number_to_chars (fixpos, amount, 2);
+ free (reloc);
+ return & no_relocs;
+ }
+#endif
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->addend = fixp->fx_offset;
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+ }
+
+ return relocs;
+}
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+ asection * segment_type ATTRIBUTE_UNUSED)
+{
+ if (fragP->fr_symbol && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ /* This is a jump -> pcrel mode. Nothing to do much here.
+ Return value == 2. */
+ fragP->fr_subtype =
+ ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_BITS10);
+ }
+ else if (fragP->fr_symbol)
+ {
+ /* Its got a segment, but its not ours. Even if fr_symbol is in
+ an absolute segment, we don't know a displacement until we link
+ object files. So it will always be long. This also applies to
+ labels in a subsegment of current. Liker may relax it to short
+ jump later. Return value == 8. */
+ fragP->fr_subtype =
+ ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_WORD);
+ }
+ else
+ {
+ /* We know the abs value. may be it is a jump to fixed address.
+ Impossible in our case, cause all constants already handled. */
+ fragP->fr_subtype =
+ ENCODE_RELAX (RELAX_LEN (fragP->fr_subtype), STATE_UNDEF);
+ }
+
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ asection * sec ATTRIBUTE_UNUSED,
+ fragS * fragP)
+{
+ char * where = 0;
+ int rela = -1;
+ int i;
+ struct rcodes_s * cc = NULL;
+ struct hcodes_s * hc = NULL;
+
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_BITS10):
+ case ENCODE_RELAX (STATE_SIMPLE_BRANCH, STATE_BITS10):
+ case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_BITS10):
+ /* We do not have to convert anything here.
+ Just apply a fix. */
+ rela = BFD_RELOC_MSP430_10_PCREL;
+ break;
+
+ case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_WORD):
+ case ENCODE_RELAX (STATE_UNCOND_BRANCH, STATE_UNDEF):
+ /* Convert uncond branch jmp lab -> br lab. */
+ if (target_is_430x ())
+ cc = msp430x_rcodes + 7;
+ else
+ cc = msp430_rcodes + 7;
+ where = fragP->fr_literal + fragP->fr_fix;
+ bfd_putl16 (cc->lop0, where);
+ rela = BFD_RELOC_MSP430_RL_PCREL;
+ fragP->fr_fix += 2;
+ break;
+
+ case ENCODE_RELAX (STATE_SIMPLE_BRANCH, STATE_WORD):
+ case ENCODE_RELAX (STATE_SIMPLE_BRANCH, STATE_UNDEF):
+ {
+ /* Other simple branches. */
+ int insn = bfd_getl16 (fragP->fr_opcode);
+
+ insn &= 0xffff;
+ /* Find actual instruction. */
+ if (target_is_430x ())
+ {
+ for (i = 0; i < 7 && !cc; i++)
+ if (msp430x_rcodes[i].sop == insn)
+ cc = msp430x_rcodes + i;
+ }
+ else
+ {
+ for (i = 0; i < 7 && !cc; i++)
+ if (msp430_rcodes[i].sop == insn)
+ cc = & msp430_rcodes[i];
+ }
+
+ if (!cc || !cc->name)
+ as_fatal (_("internal inconsistency problem in %s: insn %04lx"),
+ __FUNCTION__, (long) insn);
+ where = fragP->fr_literal + fragP->fr_fix;
+ bfd_putl16 (cc->lop0, where);
+ bfd_putl16 (cc->lop1, where + 2);
+ rela = BFD_RELOC_MSP430_RL_PCREL;
+ fragP->fr_fix += 4;
+ }
+ break;
+
+ case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_WORD):
+ case ENCODE_RELAX (STATE_NOOV_BRANCH, STATE_UNDEF):
+ if (target_is_430x ())
+ cc = msp430x_rcodes + 6;
+ else
+ cc = msp430_rcodes + 6;
+ where = fragP->fr_literal + fragP->fr_fix;
+ bfd_putl16 (cc->lop0, where);
+ bfd_putl16 (cc->lop1, where + 2);
+ bfd_putl16 (cc->lop2, where + 4);
+ rela = BFD_RELOC_MSP430_RL_PCREL;
+ fragP->fr_fix += 6;
+ break;
+
+ case ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_BITS10):
+ {
+ int insn = bfd_getl16 (fragP->fr_opcode + 2);
+
+ insn &= 0xffff;
+ if (target_is_430x ())
+ {
+ for (i = 0; i < 4 && !hc; i++)
+ if (msp430x_hcodes[i].op1 == insn)
+ hc = msp430x_hcodes + i;
+ }
+ else
+ {
+ for (i = 0; i < 4 && !hc; i++)
+ if (msp430_hcodes[i].op1 == insn)
+ hc = &msp430_hcodes[i];
+ }
+ if (!hc || !hc->name)
+ as_fatal (_("internal inconsistency problem in %s: ext. insn %04lx"),
+ __FUNCTION__, (long) insn);
+ rela = BFD_RELOC_MSP430_10_PCREL;
+ /* Apply a fix for a first label if necessary.
+ another fix will be applied to the next word of insn anyway. */
+ if (hc->tlab == 2)
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, rela);
+ fragP->fr_fix += 2;
+ }
+
+ break;
+
+ case ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_WORD):
+ case ENCODE_RELAX (STATE_EMUL_BRANCH, STATE_UNDEF):
+ {
+ int insn = bfd_getl16 (fragP->fr_opcode + 2);
+
+ insn &= 0xffff;
+ if (target_is_430x ())
+ {
+ for (i = 0; i < 4 && !hc; i++)
+ if (msp430x_hcodes[i].op1 == insn)
+ hc = msp430x_hcodes + i;
+ }
+ else
+ {
+ for (i = 0; i < 4 && !hc; i++)
+ if (msp430_hcodes[i].op1 == insn)
+ hc = & msp430_hcodes[i];
+ }
+ if (!hc || !hc->name)
+ as_fatal (_("internal inconsistency problem in %s: ext. insn %04lx"),
+ __FUNCTION__, (long) insn);
+ rela = BFD_RELOC_MSP430_RL_PCREL;
+ where = fragP->fr_literal + fragP->fr_fix;
+ bfd_putl16 (hc->lop0, where);
+ bfd_putl16 (hc->lop1, where + 2);
+ bfd_putl16 (hc->lop2, where + 4);
+ fragP->fr_fix += 6;
+ }
+ break;
+
+ default:
+ as_fatal (_("internal inconsistency problem in %s: %lx"),
+ __FUNCTION__, (long) fragP->fr_subtype);
+ break;
+ }
+
+ /* Now apply fix. */
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, TRUE, rela);
+ /* Just fixed 2 bytes. */
+ fragP->fr_fix += 2;
+}
+
+/* Relax fragment. Mostly stolen from hc11 and mcore
+ which arches I think I know. */
+
+long
+msp430_relax_frag (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 = md_relax_table;
+
+ /* Nothing to be done if the frag has already max size. */
+ if (RELAX_STATE (fragP->fr_subtype) == STATE_UNDEF
+ || RELAX_STATE (fragP->fr_subtype) == STATE_WORD)
+ return 0;
+
+ if (RELAX_STATE (fragP->fr_subtype) == STATE_BITS10)
+ {
+ symbolP = fragP->fr_symbol;
+ if (symbol_resolved_p (symbolP))
+ as_fatal (_("internal inconsistency problem in %s: resolved symbol"),
+ __FUNCTION__);
+ /* We know the offset. calculate a distance. */
+ 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;
+
+ if (aim < 0)
+ {
+ /* Look backwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim >= this_type->rlx_backward || !this_type->rlx_backward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ this_state = next_state;
+ this_type = table + this_state;
+ next_state = this_type->rlx_more;
+ }
+ }
+ else
+ {
+ /* Look forwards. */
+ for (next_state = this_type->rlx_more; next_state;)
+ if (aim <= this_type->rlx_forward || !this_type->rlx_forward)
+ next_state = 0;
+ else
+ {
+ /* Grow to next state. */
+ 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;
+}
+
+/* Return FALSE if the fixup in fixp should be left alone and not
+ adjusted. We return FALSE here so that linker relaxation will
+ work. */
+
+bfd_boolean
+msp430_fix_adjustable (struct fix *fixp ATTRIBUTE_UNUSED)
+{
+ /* If the symbol is in a non-code section then it should be OK. */
+ if (fixp->fx_addsy
+ && ((S_GET_SEGMENT (fixp->fx_addsy)->flags & SEC_CODE) == 0))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Set the contents of the .MSP430.attributes section. */
+
+void
+msp430_md_end (void)
+{
+ if (check_for_nop == TRUE && warn_interrupt_nops)
+ as_warn ("assembly finished with the last instruction changing interrupt state - a NOP might be needed");
+
+ bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_ISA,
+ target_is_430x () ? 2 : 1);
+
+ bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Code_Model,
+ large_model ? 2 : 1);
+
+ bfd_elf_add_proc_attr_int (stdoutput, OFBA_MSPABI_Tag_Data_Model,
+ large_model ? 2 : 1);
+}
+
+/* Returns FALSE if there is a msp430 specific reason why the
+ subtraction of two same-section symbols cannot be computed by
+ the assembler. */
+
+bfd_boolean
+msp430_allow_local_subtract (expressionS * left,
+ expressionS * right,
+ segT section)
+{
+ /* If the symbols are not in a code section then they are OK. */
+ if ((section->flags & SEC_CODE) == 0)
+ return TRUE;
+
+ if (S_IS_GAS_LOCAL (left->X_add_symbol) || S_IS_GAS_LOCAL (right->X_add_symbol))
+ return TRUE;
+
+ if (left->X_add_symbol == right->X_add_symbol)
+ return TRUE;
+
+ /* We have to assume that there may be instructions between the
+ two symbols and that relaxation may increase the distance between
+ them. */
+ return FALSE;
+}
diff --git a/gas/config/tc-msp430.h b/gas/config/tc-msp430.h
new file mode 100644
index 0000000..72f6dd8
--- /dev/null
+++ b/gas/config/tc-msp430.h
@@ -0,0 +1,172 @@
+/* This file is tc-msp430.h
+ Copyright (C) 2002-2014 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 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_MSP430
+/* By convention, you should define this macro in the `.h' file. For
+ example, `tc-m68k.h' defines `TC_M68K'. You might have to use this
+ if it is necessary to add CPU specific code to the object format
+ file. */
+
+#define TARGET_FORMAT "elf32-msp430"
+/* This macro is the BFD target name to use when creating the output
+ file. This will normally depend upon the `OBJ_FMT' macro. */
+
+#define TARGET_ARCH bfd_arch_msp430
+/* This macro is the BFD architecture to pass to `bfd_set_arch_mach'. */
+
+#define TARGET_MACH 0
+/* This macro is the BFD machine number to pass to
+ `bfd_set_arch_mach'. If it is not defined, GAS will use 0. */
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+/* You should define this macro to be non-zero if the target is big
+ endian, and zero if the target is little endian. */
+
+#define ONLY_STANDARD_ESCAPES
+/* If you define this macro, GAS will warn about the use of
+ nonstandard escape sequences in a string. */
+
+#define md_operand(x)
+/* GAS will call this function for any expression that can not be
+ recognized. When the function is called, `input_line_pointer'
+ will point to the start of the expression. */
+
+#define md_number_to_chars number_to_chars_littleendian
+/* This should just call either `number_to_chars_bigendian' or
+ `number_to_chars_littleendian', whichever is appropriate. On
+ targets like the MIPS which support options to change the
+ endianness, which function to call is a runtime decision. On
+ other targets, `md_number_to_chars' can be a simple macro. */
+
+#define WORKING_DOT_WORD
+/*
+`md_short_jump_size'
+`md_long_jump_size'
+`md_create_short_jump'
+`md_create_long_jump'
+ If `WORKING_DOT_WORD' is defined, GAS will not do broken word
+ processing (*note Broken words::.). Otherwise, you should set
+ `md_short_jump_size' to the size of a short jump (a jump that is
+ just long enough to jump around a long jmp) and
+ `md_long_jump_size' to the size of a long jump (a jump that can go
+ anywhere in the function), You should define
+ `md_create_short_jump' to create a short jump around a long jump,
+ and define `md_create_long_jump' to create a long jump. */
+
+#define MD_APPLY_FIX3
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define TC_HANDLES_FX_DONE
+
+#undef RELOC_EXPANSION_POSSIBLE
+/* If you define this macro, it means that `tc_gen_reloc' may return
+ multiple relocation entries for a single fixup. In this case, the
+ return value of `tc_gen_reloc' is a pointer to a null terminated
+ array. */
+
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section(FIXP, SEC)
+/* If you define this macro, it should return the offset between the
+ address of a PC relative fixup and the position from which the PC
+ relative adjustment should be made. On many processors, the base
+ of a PC relative instruction is the next instruction, so this
+ macro would return the length of an instruction. */
+
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define LISTING_WORD_SIZE 2
+/* The number of bytes to put into a word in a listing. This affects
+ the way the bytes are clumped together in the listing. For
+ example, a value of 2 might print `1234 5678' where a value of 1
+ would print `12 34 56 78'. The default value is 4. */
+
+/* Support symbols like: C$$IO$$. */
+#undef LEX_DOLLAR
+#define LEX_DOLLAR 1
+
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 0
+/* An `.lcomm' directive with no explicit alignment parameter will
+ use this macro to set P2VAR to the alignment that a request for
+ SIZE bytes will have. The alignment is expressed as a power of
+ two. If no alignment should take place, the macro definition
+ should do nothing. Some targets define a `.bss' directive that is
+ also affected by this macro. The default definition will set
+ P2VAR to the truncated power of two of sizes up to eight bytes. */
+
+#define md_relax_frag(SEG, FRAGP, STRETCH) \
+ msp430_relax_frag (SEG, FRAGP, STRETCH)
+extern long msp430_relax_frag (segT, fragS *, long);
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ msp430_force_relocation_local (FIX)
+extern int msp430_force_relocation_local (struct fix *);
+
+/* We need to add reference symbols for .data/.bss. */
+#define tc_frob_section(sec) msp430_frob_section (sec)
+extern void msp430_frob_section (asection *);
+
+extern int msp430_enable_relax;
+extern int msp430_enable_polys;
+
+#define tc_fix_adjustable(FIX) msp430_fix_adjustable (FIX)
+extern bfd_boolean msp430_fix_adjustable (struct fix *);
+
+/* Allow hexadeciaml numbers with 'h' suffix. Note that if the number
+ starts with a letter it will be interpreted as a symbol name not a
+ constant. Thus "beach" is a symbol not the hex value 0xbeac. So
+ is A5A5h... */
+#define NUMBERS_WITH_SUFFIX 1
+
+#define md_end msp430_md_end
+extern void msp430_md_end (void);
+
+/* Do not allow call frame debug info optimization as otherwise we could
+ generate the DWARF directives without the relocs necessary to patch
+ them up. */
+#define md_allow_eh_opt 0
+
+/* The difference between same-section symbols may be affected by linker
+ relaxation, so do not resolve such expressions in the assembler. */
+#define md_allow_local_subtract(l,r,s) msp430_allow_local_subtract (l, r, s)
+extern bfd_boolean msp430_allow_local_subtract (expressionS *, expressionS *, segT);
+
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 2
+
+#define DIFF_EXPR_OK
+
+/* Do not adjust relocations involving symbols in code sections,
+ because it breaks linker relaxations. This could be fixed in the
+ linker, but this fix is simpler, and it pretty much only affects
+ object size a little bit. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \
+ (((SEC)->flags & SEC_CODE) != 0 \
+ || ! SEG_NORMAL (SEC) \
+ || TC_FORCE_RELOCATION (FIX))
+
+/* We validate subtract arguments within tc_gen_reloc(),
+ so don't report errors at this point. */
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1
+
+#define DWARF2_USE_FIXED_ADVANCE_PC 1
+
+#define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_CODE)
diff --git a/gas/config/tc-mt.c b/gas/config/tc-mt.c
new file mode 100644
index 0000000..d832297
--- /dev/null
+++ b/gas/config/tc-mt.c
@@ -0,0 +1,483 @@
+/* tc-mt.c -- Assembler for the Morpho Technologies mt .
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#include "as.h"
+#include "dwarf2dbg.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/mt-desc.h"
+#include "opcodes/mt-opc.h"
+#include "cgen.h"
+#include "elf/common.h"
+#include "elf/mt.h"
+#include "libbfd.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];
+}
+mt_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";
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { NULL, NULL, 0 }
+};
+
+
+
+static int no_scheduling_restrictions = 0;
+
+struct option md_longopts[] =
+{
+#define OPTION_NO_SCHED_REST (OPTION_MD_BASE)
+ { "nosched", no_argument, NULL, OPTION_NO_SCHED_REST },
+#define OPTION_MARCH (OPTION_MD_BASE + 1)
+ { "march", required_argument, NULL, OPTION_MARCH},
+ { NULL, no_argument, NULL, 0 },
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+const char * md_shortopts = "";
+
+/* Mach selected from command line. */
+static int mt_mach = bfd_mach_ms1;
+static unsigned mt_mach_bitmask = 1 << MACH_MS1;
+
+/* Flags to set in the elf header */
+static flagword mt_flags = EF_MT_CPU_MRISC;
+
+/* The architecture to use. */
+enum mt_architectures
+ {
+ ms1_64_001,
+ ms1_16_002,
+ ms1_16_003,
+ ms2
+ };
+
+/* MT architecture we are using for this output file. */
+static enum mt_architectures mt_arch = ms1_16_002;
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char * arg)
+{
+ switch (c)
+ {
+ case OPTION_MARCH:
+ if (strcmp (arg, "ms1-64-001") == 0)
+ {
+ mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC;
+ mt_mach = bfd_mach_ms1;
+ mt_mach_bitmask = 1 << MACH_MS1;
+ mt_arch = ms1_64_001;
+ }
+ else if (strcmp (arg, "ms1-16-002") == 0)
+ {
+ mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC;
+ mt_mach = bfd_mach_ms1;
+ mt_mach_bitmask = 1 << MACH_MS1;
+ mt_arch = ms1_16_002;
+ }
+ else if (strcmp (arg, "ms1-16-003") == 0)
+ {
+ mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MRISC2;
+ mt_mach = bfd_mach_mrisc2;
+ mt_mach_bitmask = 1 << MACH_MS1_003;
+ mt_arch = ms1_16_003;
+ }
+ else if (strcmp (arg, "ms2") == 0)
+ {
+ mt_flags = (mt_flags & ~EF_MT_CPU_MASK) | EF_MT_CPU_MS2;
+ mt_mach = bfd_mach_mrisc2;
+ mt_mach_bitmask = 1 << MACH_MS2;
+ mt_arch = ms2;
+ }
+ case OPTION_NO_SCHED_REST:
+ no_scheduling_restrictions = 1;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _("MT specific command line options:\n"));
+ fprintf (stream, _(" -march=ms1-64-001 allow ms1-64-001 instructions\n"));
+ fprintf (stream, _(" -march=ms1-16-002 allow ms1-16-002 instructions (default)\n"));
+ fprintf (stream, _(" -march=ms1-16-003 allow ms1-16-003 instructions\n"));
+ fprintf (stream, _(" -march=ms2 allow ms2 instructions \n"));
+ fprintf (stream, _(" -nosched disable scheduling restrictions\n"));
+}
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = mt_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, mt_mach_bitmask,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ mt_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 (mt_flags)
+ bfd_set_private_flags (stdoutput, mt_flags);
+
+ /* Set the machine type. */
+ bfd_default_set_arch_mach (stdoutput, bfd_arch_mt, mt_mach);
+}
+
+void
+md_assemble (char * str)
+{
+ static long delayed_load_register = 0;
+ static long prev_delayed_load_register = 0;
+ static int last_insn_had_delay_slot = 0;
+ static int last_insn_in_noncond_delay_slot = 0;
+ static int last_insn_has_load_delay = 0;
+ static int last_insn_was_memory_access = 0;
+ static int last_insn_was_io_insn = 0;
+ static int last_insn_was_arithmetic_or_logic = 0;
+ static int last_insn_was_branch_insn = 0;
+ static int last_insn_was_conditional_branch_insn = 0;
+
+ mt_insn insn;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = mt_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", 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);
+
+
+ /* Handle Scheduling Restrictions. */
+ if (!no_scheduling_restrictions)
+ {
+ /* Detect consecutive Memory Accesses. */
+ if (last_insn_was_memory_access
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS)
+ && mt_mach == ms1_64_001)
+ as_warn (_("instruction %s may not follow another memory access instruction."),
+ CGEN_INSN_NAME (insn.insn));
+
+ /* Detect consecutive I/O Instructions. */
+ else if (last_insn_was_io_insn
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN))
+ as_warn (_("instruction %s may not follow another I/O instruction."),
+ CGEN_INSN_NAME (insn.insn));
+
+ /* Detect consecutive branch instructions. */
+ else if (last_insn_was_branch_insn
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN))
+ as_warn (_("%s may not occupy the delay slot of another branch insn."),
+ CGEN_INSN_NAME (insn.insn));
+
+ /* Detect data dependencies on delayed loads: memory and input insns. */
+ if (last_insn_has_load_delay && delayed_load_register)
+ {
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
+ && insn.fields.f_sr1 == delayed_load_register)
+ as_warn (_("operand references R%ld of previous load."),
+ insn.fields.f_sr1);
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
+ && insn.fields.f_sr2 == delayed_load_register)
+ as_warn (_("operand references R%ld of previous load."),
+ insn.fields.f_sr2);
+ }
+
+ /* Detect JAL/RETI hazard */
+ if (mt_mach == ms2
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_JAL_HAZARD))
+ {
+ if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
+ && insn.fields.f_sr1 == delayed_load_register)
+ || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
+ && insn.fields.f_sr2 == delayed_load_register))
+ as_warn (_("operand references R%ld of previous instruction."),
+ delayed_load_register);
+ else if ((CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
+ && insn.fields.f_sr1 == prev_delayed_load_register)
+ || (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
+ && insn.fields.f_sr2 == prev_delayed_load_register))
+ as_warn (_("operand references R%ld of instruction before previous."),
+ prev_delayed_load_register);
+ }
+
+ /* Detect data dependency between conditional branch instruction
+ and an immediately preceding arithmetic or logical instruction. */
+ if (last_insn_was_arithmetic_or_logic
+ && !last_insn_in_noncond_delay_slot
+ && (delayed_load_register != 0)
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
+ && mt_arch == ms1_64_001)
+ {
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR1)
+ && insn.fields.f_sr1 == delayed_load_register)
+ as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
+ insn.fields.f_sr1);
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2)
+ && insn.fields.f_sr2 == delayed_load_register)
+ as_warn (_("conditional branch or jal insn's operand references R%ld of previous arithmetic or logic insn."),
+ insn.fields.f_sr2);
+ }
+ }
+
+ /* Keep track of details of this insn for processing next insn. */
+ last_insn_in_noncond_delay_slot = last_insn_was_branch_insn
+ && !last_insn_was_conditional_branch_insn;
+
+ last_insn_had_delay_slot =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_DELAY_SLOT);
+ (void) last_insn_had_delay_slot;
+
+ last_insn_has_load_delay =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_LOAD_DELAY);
+
+ last_insn_was_memory_access =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_MEMORY_ACCESS);
+
+ last_insn_was_io_insn =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_IO_INSN);
+
+ last_insn_was_arithmetic_or_logic =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_AL_INSN);
+
+ last_insn_was_branch_insn =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN);
+
+ last_insn_was_conditional_branch_insn =
+ CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_BR_INSN)
+ && CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRSR2);
+
+ prev_delayed_load_register = delayed_load_register;
+
+ if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDR))
+ delayed_load_register = insn.fields.f_dr;
+ else if (CGEN_INSN_ATTR_VALUE (insn.insn, CGEN_INSN_USES_FRDRRR))
+ delayed_load_register = insn.fields.f_drrr;
+ else /* Insns has no destination register. */
+ delayed_load_register = 0;
+
+ /* Generate dwarf2 line numbers. */
+ dwarf2_emit_insn (4);
+}
+
+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 NULL;
+}
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("md_estimate_size_before_relax\n"));
+ return 1;
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+}
+
+
+/* Functions concerning relocs. */
+
+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 the address of the opcode - cgen adjusts for opcode size
+ itself, to be consistent with the disassembler, which must do
+ so. */
+ return fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+
+/* 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)
+{
+ bfd_reloc_code_real_type result;
+
+ result = BFD_RELOC_NONE;
+
+ switch (operand->type)
+ {
+ case MT_OPERAND_IMM16O:
+ result = BFD_RELOC_16_PCREL;
+ fixP->fx_pcrel = 1;
+ /* fixP->fx_no_overflow = 1; */
+ break;
+ case MT_OPERAND_IMM16:
+ case MT_OPERAND_IMM16Z:
+ /* These may have been processed at parse time. */
+ if (fixP->fx_cgen.opinfo != 0)
+ result = fixP->fx_cgen.opinfo;
+ fixP->fx_no_overflow = 1;
+ break;
+ case MT_OPERAND_LOOPSIZE:
+ result = BFD_RELOC_MT_PCINSN8;
+ fixP->fx_pcrel = 1;
+ /* Adjust for the delay slot, which is not part of the loop */
+ fixP->fx_offset -= 8;
+ break;
+ default:
+ result = BFD_RELOC_NONE;
+ break;
+ }
+
+ return result;
+}
+
+/* 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_bigendian (buf, val, n);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+/* See whether we need to force a relocation into the output file. */
+
+int
+mt_force_relocation (fixS * fixp ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+mt_apply_fix (fixS *fixP, valueT *valueP, segT seg)
+{
+ if ((fixP->fx_pcrel != 0) && (fixP->fx_r_type == BFD_RELOC_32))
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+
+ gas_cgen_md_apply_fix (fixP, valueP, seg);
+}
+
+bfd_boolean
+mt_fix_adjustable (fixS * fixP)
+{
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const CGEN_INSN *insn = NULL;
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand;
+
+ operand = cgen_operand_lookup_by_num(gas_cgen_cpu_desc, opindex);
+ md_cgen_lookup_reloc (insn, operand, fixP);
+ }
+
+ if (fixP->fx_addsy == NULL)
+ return TRUE;
+
+ /* Prevent all adjustments to global symbols. */
+ if (S_IS_EXTERNAL (fixP->fx_addsy))
+ return FALSE;
+
+ if (S_IS_WEAK (fixP->fx_addsy))
+ return FALSE;
+
+ return 1;
+}
diff --git a/gas/config/tc-mt.h b/gas/config/tc-mt.h
new file mode 100644
index 0000000..1d3a827
--- /dev/null
+++ b/gas/config/tc-mt.h
@@ -0,0 +1,70 @@
+/* tc-mt.h -- Header file for tc-mt.c.
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA. */
+
+#define TC_MT
+
+#define LISTING_HEADER "MT GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_mt
+
+#define TARGET_FORMAT "elf32-mt"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* All mt instructions are multiples of 32 bits. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+
+#define LITERAL_PREFIXDOLLAR_HEX
+#define LITERAL_PREFIXPERCENT_BIN
+
+#define md_apply_fix mt_apply_fix
+extern void mt_apply_fix (struct fix *, valueT *, segT);
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define obj_fix_adjustable(fixP) iq2000_fix_adjustable (fixP)
+extern bfd_boolean mt_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+#define md_operand(x) gas_cgen_md_operand (x)
+extern void gas_cgen_md_operand (expressionS *);
+
+#define TC_FORCE_RELOCATION(fixp) mt_force_relocation (fixp)
+extern int mt_force_relocation (struct fix *);
+
+#define tc_fix_adjustable(fixP) mt_fix_adjustable (fixP)
+extern bfd_boolean mt_fix_adjustable (struct fix *);
+
diff --git a/gas/config/tc-nds32.c b/gas/config/tc-nds32.c
new file mode 100644
index 0000000..353a165
--- /dev/null
+++ b/gas/config/tc-nds32.c
@@ -0,0 +1,6600 @@
+/* tc-nds32.c -- Assemble for the nds32
+ Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Contributed by Andes Technology Corporation.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "opcodes/nds32-asm.h"
+#include "elf/nds32.h"
+#include "bfd/elf32-nds32.h"
+#include "hash.h"
+#include "sb.h"
+#include "macro.h"
+#include "struc-symbol.h"
+#include "opcode/nds32.h"
+
+#include <stdio.h>
+
+/* GAS definitions. */
+
+/* Characters which start a comment. */
+const char comment_chars[] = "!";
+/* Characters which start a comment when they appear at the start of a line. */
+const char line_comment_chars[] = "#!";
+/* Characters which separate lines (null and newline are by default). */
+const char line_separator_chars[] = ";";
+/* Characters which may be used as the exponent character
+ in a floating point number. */
+const char EXP_CHARS[] = "eE";
+/* Characters which may be used to indicate a floating point constant. */
+const char FLT_CHARS[] = "dDfF";
+
+static int enable_16bit = 1;
+/* Save for md_assemble to distinguish if this instruction is
+ expanded from the pseudo instruction. */
+static bfd_boolean pseudo_opcode = FALSE;
+static struct nds32_relocs_pattern *relocs_list = NULL;
+/* Save instruction relation to inserting relaxation relocation. */
+struct nds32_relocs_pattern
+{
+ segT seg;
+ fragS *frag;
+ frchainS *frchain;
+ symbolS *sym;
+ fixS* fixP;
+ struct nds32_opcode *opcode;
+ char *where;
+ struct nds32_relocs_pattern *next;
+};
+
+/* Suffix name and relocation. */
+struct suffix_name
+{
+ char *suffix;
+ short unsigned int reloc;
+ int pic;
+};
+static int vec_size = 0;
+/* If the assembly code is generated by compiler, it is supposed to have
+ ".flag verbatim" at beginning of the content. We have
+ 'nds32_flag' to parse it and set this field to be non-zero. */
+static int verbatim = 0;
+static struct hash_control *nds32_gprs_hash;
+static struct hash_control *nds32_hint_hash;
+#define TLS_REG "$r27"
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+
+/* Generate relocation for relax or not, and the default is true. */
+static int enable_relax_relocs = 1;
+/* The value will be used in RELAX_ENTRY. */
+static int enable_relax_ex9 = 0;
+/* The value will be used in RELAX_ENTRY. */
+static int enable_relax_ifc = 0;
+/* Save option -O for perfomance. */
+static int optimize = 0;
+/* Save option -Os for code size. */
+static int optimize_for_space = 0;
+/* Flag to save label exist. */
+static int label_exist = 0;
+/* Flag to save state in omit_fp region. */
+static int in_omit_fp = 0;
+extern struct nds32_keyword keyword_gpr[];
+/* Tag there is relax relocation having to link. */
+static bfd_boolean relaxing = FALSE;
+
+static struct hash_control *nds32_relax_info_hash;
+static relax_info_t relax_table[] =
+{
+ {
+ "jal", /* opcode */
+ BR_RANGE_S16M, /* br_range */
+ {{0, 0, 0, FALSE}}, /* cond_field */
+ {
+ {
+ INSN_JAL /* jal label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_JAL /* jal label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_JAL /* jal label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_JAL /* jal label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JRAL_TA
+ }, /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S256 */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S64K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16M */
+ {{0, 0, 0, FALSE}} /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 4, 12}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_HI20},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL4},
+ {4, 4, NDS32_HINT | NDS32_FIX, BFD_RELOC_NDS32_LO12S0_ORI},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {8, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {8, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bltzal", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BLTZAL /* bltzal $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BLTZAL /* bltzal $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BLTZAL /* bltzal $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BGEZ, /* bgez $rt, $1 */
+ INSN_JAL /* jal label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BGEZ, /* bgez $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JRAL_TA /* jral $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_17_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_17_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bgezal", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BGEZAL /* bgezal $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BGEZAL /* bgezal $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BGEZAL /* bgezal $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BLTZ, /* bltz $rt, $1 */
+ INSN_JAL /* jal label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BLTZ, /* bltz $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JRAL_TA /* jral $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_17_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_17_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGCALL6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "j", /* opcode */
+ BR_RANGE_S16M, /* br_range */
+ {{0, 0, 0, FALSE}}, /* cond_field */
+ {
+ {
+ (INSN_J8 << 16) /* j8 label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ }, /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S256 */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S64K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16M */
+ {{0, 0, 0, FALSE}} /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 4, 4, 12}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_HI20},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP4},
+ {4, 4, NDS32_HINT | NDS32_FIX, BFD_RELOC_NDS32_LO12S0_ORI},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {8, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {8, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "j8", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {{0, 0, 0, FALSE}}, /* cond_field */
+ {
+ {
+ (INSN_J8 << 16) /* j8 label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ }, /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S256 */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S64K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16M */
+ {{0, 0, 0, FALSE}} /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 4, 4, 12}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_HI20},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP4},
+ {4, 4, NDS32_HINT | NDS32_FIX, BFD_RELOC_NDS32_LO12S0_ORI},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {8, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {8, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "beqz", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BEQZ /* beqz $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BEQZ /* beqz $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BEQZ /* beqz $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BNEZ, /* bnez $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BNEZ, /* bnez $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 4, NDS32_INSN16 , BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bgez", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BGEZ /* bgez $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BGEZ /* bgez $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BGEZ /* bgez $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BLTZ, /* bltz $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BLTZ, /* bltz $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bnez", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BNEZ /* bnez $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BNEZ /* bnez $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BNEZ /* bnez $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BEQZ, /* beqz $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BEQZ, /* beqz $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 4, NDS32_INSN16 , BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bgtz", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BGTZ /* bgtz $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BGTZ /* bgtz $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BGTZ /* bgtz $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BLEZ, /* blez $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BLEZ, /* blez $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "blez", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BLEZ /* blez $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BLEZ /* blez $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BLEZ /* blez $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BGTZ, /* bgtz $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BGTZ, /* bgtz $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bltz", /* opcode */
+ BR_RANGE_S64K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BLTZ /* bltz $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BLTZ /* bltz $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BLTZ /* bltz $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BGEZ, /* bgez $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BGEZ, /* bgez $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 4, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "beq", /* opcode */
+ BR_RANGE_S16K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BEQ /* beq $rt, $ra, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BEQ /* beq $rt, $ra, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BNE, /* bne $rt, $ra, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BNE, /* bne $rt, $ra, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BNE, /* bne $rt, $ra, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 8, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_ABS, BFD_RELOC_NDS32_EMPTY},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bne", /* opcode */
+ BR_RANGE_S16K, /* br_range */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BNE /* bne $rt, $ra, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BNE /* bne $rt, $ra, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BEQ, /* beq $rt, $ra, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BEQ, /* beq $rt, $ra, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BEQ, /* beq $rt, $ra, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 15, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 4, 8, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "beqz38", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BEQZ38 << 16 /* beqz $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BEQZ /* beqz $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BEQZ /* beqz $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BNEZ, /* bnez $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BNEZ, /* bnez $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 4, 8, 16}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bnez38", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BNEZ38 << 16 /* bnez $rt, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BNEZ /* bnez $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BNEZ /* bnez $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BEQZ, /* beqz $rt, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BEQZ, /* beqz $rt, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 4, 8, 16}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "beqzs8", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {{0, 0, 0, FALSE}}, /* cond_field */
+ {
+ {
+ INSN_BEQZS8 << 16 /* beqz $r15, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BEQZ_TA /* bnez $rt, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BEQZ_TA /* bnez $rt, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BNEZ_TA, /* bnez $r15, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BNEZ_TA, /* bnez $r15, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S256 */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S64K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16M */
+ {{0, 0, 0, FALSE}} /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 4, 8, 16}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bnezs8", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {{0, 0, 0, FALSE}}, /* cond_field */
+ {
+ {
+ INSN_BNEZS8 << 16 /* bnez $r15, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BNEZ_TA /* bnez $r15, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BNEZ_TA /* bnez $r15, label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BEQZ_TA, /* beqz $r15, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BEQZ_TA, /* beqz $r15, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S256 */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S64K */
+ {{0, 0, 0, FALSE}}, /* BR_RANGE_S16M */
+ {{0, 0, 0, FALSE}} /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 4, 8, 16}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_17_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bnes38", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BNES38 << 16 /* bne $rt, $R5, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BNE_R5 /* bne $rt, $R5, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BEQ_R5, /* beq $rt, $R5, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BEQ_R5, /* beq $rt, $R5, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BEQ_R5, /* beq $rt, $R5, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 8, 8, 16}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "beqs38", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BEQS38 << 16 /* beq $rt, $R5, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_BEQ_R5 /* beq $rt, $R5, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BNE_R5, /* bne $rt, $R5, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BNE_R5, /* bne $rt, $R5, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BNE_R5, /* bne $rt, $R5, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 8, 0x7, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {2, 4, 8, 8, 16}, /* relax_code_size */
+ {2, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 2, 0, BFD_RELOC_NDS32_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP5},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {4, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_15_PCREL},
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP6},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {4, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {8, 4, NDS32_FIX | NDS32_HINT, BFD_RELOC_NDS32_LO12S0_ORI},
+ {8, 4, NDS32_PTR |NDS32_HINT, BFD_RELOC_NDS32_PTR},
+ {12, 4, NDS32_ABS | NDS32_HINT, BFD_RELOC_NDS32_PTR_RESOLVED},
+ {12, 4, NDS32_SYM | NDS32_HINT, BFD_RELOC_NDS32_EMPTY},
+ {12, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "beqc", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {
+ {0, 8, 0x7FF, TRUE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BEQC /* beqc $rt, imm11s, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_MOVI_TA, /* movi $ta, imm11s */
+ INSN_BEQ_TA /* beq $rt, $ta, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BNEC, /* bnec $rt, imm11s, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BNEC, /* bnec $rt, imm11s, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BNEC, /* bnec $rt, imm11s, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 8, 0x7FF, TRUE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 0, 0xFFFFF, FALSE},
+ {4, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 8, 0x7FF, FALSE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 8, 0x7FF, FALSE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 8, 0x7FF, FALSE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 8, 8, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP7},
+ {4, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI},
+ {12, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ "bnec", /* opcode */
+ BR_RANGE_S256, /* br_range */
+ {
+ {0, 8, 0x7FF, TRUE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* cond_field */
+ {
+ {
+ INSN_BNEC /* bnec $rt, imm11s, label */
+ }, /* BR_RANGE_S256 */
+ {
+ INSN_MOVI_TA, /* movi $ta, imm11s */
+ INSN_BNE_TA /* bne $rt, $ta, label */
+ }, /* BR_RANGE_S16K */
+ {
+ INSN_BEQC, /* beqc $rt, imm11s, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S64K */
+ {
+ INSN_BEQC, /* beqc $rt, imm11s, $1 */
+ INSN_J /* j label */
+ }, /* BR_RANGE_S16M */
+ {
+ INSN_BEQC, /* beqc $rt, imm11s, $1 */
+ INSN_SETHI_TA, /* sethi $ta, label */
+ INSN_ORI_TA, /* ori $ta, $ta, label */
+ INSN_JR_TA /* jr $ta */
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_seq */
+ {
+ {
+ {0, 8, 0x7FF, TRUE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 0, 0xFFFFF, FALSE},
+ {4, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 8, 0x7FF, FALSE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 8, 0x7FF, FALSE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 8, 0x7FF, FALSE},
+ {0, 20, 0x1F, FALSE},
+ {0, 0, 0, FALSE}
+ } /* BR_RANGE_U4G */
+ }, /* relax_code_condition */
+ {4, 8, 8, 8, 16}, /* relax_code_size */
+ {4, 4, 4, 4, 4}, /* relax_branch_isize */
+ {
+ {
+ {0, 4, 0, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S256 */
+ {
+ {0, 4, NDS32_INSN16 | NDS32_HINT, BFD_RELOC_NDS32_INSN16},
+ {0, 4, NDS32_PTR | NDS32_HINT, BFD_RELOC_NDS32_LONGJUMP7},
+ {4, 4, 0, BFD_RELOC_NDS32_15_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S64K */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {4, 4, 0, BFD_RELOC_NDS32_25_PCREL},
+ {0, 0, 0, 0}
+ }, /* BR_RANGE_S16M */
+ {
+ {0, 4, NDS32_CREATE_LABEL, BFD_RELOC_NDS32_WORD_9_PCREL},
+ {4, 4, 0, BFD_RELOC_NDS32_HI20},
+ {8, 4, 0, BFD_RELOC_NDS32_LO12S0_ORI},
+ {12, 4, NDS32_INSN16, BFD_RELOC_NDS32_INSN16},
+ {0, 0, 0, 0}
+ } /* BR_RANGE_U4G */
+ } /* relax_fixup */
+ },
+ {
+ NULL, /* opcode */
+ 0, /* br_range */
+ {{0, 0, 0, FALSE}}, /* cond_field */
+ {{0}}, /* relax_code_seq */
+ {{{0, 0, 0, FALSE}}}, /* relax_code_condition */
+ {0}, /* relax_code_size */
+ {0}, /* relax_branch_isize */
+ {{{0, 0, 0, 0}}}, /* relax_fixup */
+ },
+};
+
+/* GAS definitions for command-line options. */
+enum options
+{
+ OPTION_BIG = OPTION_MD_BASE,
+ OPTION_LITTLE,
+ OPTION_TURBO,
+ OPTION_PIC,
+ OPTION_RELAX_FP_AS_GP_OFF,
+ OPTION_RELAX_B2BB_ON,
+ OPTION_RELAX_ALL_OFF,
+ OPTION_OPTIMIZE,
+ OPTION_OPTIMIZE_SPACE
+};
+
+const char *md_shortopts = "m:O:";
+struct option md_longopts[] =
+{
+ {"O1", no_argument, NULL, OPTION_OPTIMIZE},
+ {"Os", no_argument, NULL, OPTION_OPTIMIZE_SPACE},
+ {"big", no_argument, NULL, OPTION_BIG},
+ {"little", no_argument, NULL, OPTION_LITTLE},
+ {"EB", no_argument, NULL, OPTION_BIG},
+ {"EL", no_argument, NULL, OPTION_LITTLE},
+ {"meb", no_argument, NULL, OPTION_BIG},
+ {"mel", no_argument, NULL, OPTION_LITTLE},
+ {"mall-ext", no_argument, NULL, OPTION_TURBO},
+ {"mext-all", no_argument, NULL, OPTION_TURBO},
+ {"mpic", no_argument, NULL, OPTION_PIC},
+ /* Relaxation related options. */
+ {"mno-fp-as-gp-relax", no_argument, NULL, OPTION_RELAX_FP_AS_GP_OFF},
+ {"mb2bb", no_argument, NULL, OPTION_RELAX_B2BB_ON},
+ {"mno-all-relax", no_argument, NULL, OPTION_RELAX_ALL_OFF},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+struct nds32_parse_option_table
+{
+ const char *name; /* Option string. */
+ char *help; /* Help description. */
+ int (*func) (char *arg); /* How to parse it. */
+};
+
+
+/* The value `-1' represents this option has *NOT* been set. */
+#ifdef NDS32_DEFAULT_ARCH_NAME
+static char* nds32_arch_name = NDS32_DEFAULT_ARCH_NAME;
+#else
+static char* nds32_arch_name = "v3";
+#endif
+static int nds32_baseline = -1;
+static int nds32_gpr16 = -1;
+static int nds32_fpu_sp_ext = -1;
+static int nds32_fpu_dp_ext = -1;
+static int nds32_freg = -1;
+static int nds32_abi = -1;
+
+/* Record ELF flags */
+static int nds32_elf_flags = 0;
+static int nds32_fpu_com = 0;
+
+static int nds32_parse_arch (char *str);
+static int nds32_parse_baseline (char *str);
+static int nds32_parse_freg (char *str);
+static int nds32_parse_abi (char *str);
+
+static struct nds32_parse_option_table parse_opts [] =
+{
+ {"arch=", N_("<arch name>\t Assemble for architecture <arch name>\n\
+ <arch name> could be\n\
+ v3, v3j, v3m, v3f, v3s, "\
+ "v2, v2j, v2f, v2s"), nds32_parse_arch},
+ {"baseline=", N_("<baseline>\t Assemble for baseline <baseline>\n\
+ <baseline> could be v2, v3, v3m"),
+ nds32_parse_baseline},
+ {"fpu-freg=", N_("<freg>\t Specify a FPU configuration\n\
+ <freg>\n\
+ 0: 8 SP / 4 DP registers\n\
+ 1: 16 SP / 8 DP registers\n\
+ 2: 32 SP / 16 DP registers\n\
+ 3: 32 SP / 32 DP registers"), nds32_parse_freg},
+ {"abi=", N_("<abi>\t Specify a abi version\n\
+ <abi> could be v1, v2, v2fp, v2fpp"), nds32_parse_abi},
+ {NULL, NULL, NULL}
+};
+
+static int nds32_mac = 1;
+static int nds32_div = 1;
+static int nds32_16bit_ext = 1;
+static int nds32_dx_regs = 1;
+static int nds32_perf_ext = 1;
+static int nds32_perf_ext2 = 1;
+static int nds32_string_ext = 1;
+static int nds32_audio_ext = 1;
+static int nds32_fpu_fma = 0;
+static int nds32_pic = 0;
+static int nds32_relax_fp_as_gp = 1;
+static int nds32_relax_b2bb = 0;
+static int nds32_relax_all = 1;
+struct nds32_set_option_table
+{
+ const char *name; /* Option string. */
+ char *help; /* Help description. */
+ int *var; /* Variable to be set. */
+ int value; /* Value to set. */
+};
+
+/* The option in this group has both Enable/Disable settings.
+ Just list on here. */
+
+static struct nds32_set_option_table toggle_opts [] =
+{
+ {"mac", N_("Multiply instructions support"), &nds32_mac, 1},
+ {"div", N_("Divide instructions support"), &nds32_div, 1},
+ {"16bit-ext", N_("16-bit extension"), &nds32_16bit_ext, 1},
+ {"dx-regs", N_("d0/d1 registers"), &nds32_dx_regs, 1},
+ {"perf-ext", N_("Performance extension"), &nds32_perf_ext, 1},
+ {"perf2-ext", N_("Performance extension 2"), &nds32_perf_ext2, 1},
+ {"string-ext", N_("String extension"), &nds32_string_ext, 1},
+ {"reduced-regs", N_("Reduced Register configuration (GPR16) option"), &nds32_gpr16, 1},
+ {"audio-isa-ext", N_("AUDIO ISA extension"), &nds32_audio_ext, 1},
+ {"fpu-sp-ext", N_("FPU SP extension"), &nds32_fpu_sp_ext, 1},
+ {"fpu-dp-ext", N_("FPU DP extension"), &nds32_fpu_dp_ext, 1},
+ {"fpu-fma", N_("FPU fused-multiply-add instructions"), &nds32_fpu_fma, 1},
+ {NULL, NULL, NULL, 0}
+};
+
+
+/* GAS declarations. */
+
+/* This is the callback for nds32-asm.c to parse operands. */
+int
+nds32_asm_parse_operand (struct nds32_asm_desc *pdesc,
+ struct nds32_asm_insn *pinsn,
+ char **pstr, int64_t *value);
+
+
+struct nds32_asm_desc asm_desc;
+
+/* md_after_parse_args ()
+
+ GAS will call md_after_parse_args whenever it is defined.
+ This function checks any conflicting options specified. */
+
+void
+nds32_after_parse_args (void)
+{
+ /* If -march option is not used in command-line, set the value of option
+ variable according to NDS32_DEFAULT_ARCH_NAME. */
+ nds32_parse_arch (nds32_arch_name);
+}
+
+/* This function is called when printing usage message (--help). */
+
+void
+md_show_usage (FILE *stream)
+{
+ struct nds32_parse_option_table *coarse_tune;
+ struct nds32_set_option_table *fine_tune;
+
+ fprintf (stream, _("\n NDS32-specific assembler options:\n"));
+ fprintf (stream, _("\
+ -O1, Optimize for performance\n\
+ -Os Optimize for space\n"));
+ fprintf (stream, _("\
+ -EL, -mel or -little Produce little endian output\n\
+ -EB, -meb or -big Produce big endian output\n\
+ -mpic Generate PIC\n\
+ -mno-fp-as-gp-relax Suppress fp-as-gp relaxation for this file\n\
+ -mb2bb-relax Back-to-back branch optimization\n\
+ -mno-all-relax Suppress all relaxation for this file\n"));
+
+ for (coarse_tune = parse_opts; coarse_tune->name != NULL; coarse_tune++)
+ {
+ if (coarse_tune->help != NULL)
+ fprintf (stream, _(" -m%s%s\n"),
+ coarse_tune->name, _(coarse_tune->help));
+ }
+
+ for (fine_tune = toggle_opts; fine_tune->name != NULL; fine_tune++)
+ {
+ if (fine_tune->help != NULL)
+ fprintf (stream, _(" -m[no-]%-17sEnable/Disable %s\n"),
+ fine_tune->name, _(fine_tune->help));
+ }
+
+ fprintf (stream, _("\
+ -mall-ext Turn on all extensions and instructions support\n"));
+}
+
+void
+nds32_frag_init (fragS *fragp)
+{
+ fragp->tc_frag_data.flag = 0;
+ fragp->tc_frag_data.opcode = NULL;
+ fragp->tc_frag_data.fixup = NULL;
+}
+
+
+
+/* This function reads an expression from a C string and returns a pointer past
+ the end of the expression. */
+
+static char *
+parse_expression (char *str, expressionS *exp)
+{
+ char *s;
+ char *tmp;
+
+ tmp = input_line_pointer; /* Save line pointer. */
+ input_line_pointer = str;
+ expression (exp);
+ s = input_line_pointer;
+ input_line_pointer = tmp; /* Restore line pointer. */
+
+ return s; /* Return pointer to where parsing stopped. */
+}
+
+void
+nds32_start_line_hook (void)
+{
+}
+
+/*
+ * Pseudo opcodes
+ */
+
+typedef void (*nds32_pseudo_opcode_func) (int argc, char *argv[], int pv);
+struct nds32_pseudo_opcode
+{
+ const char *opcode;
+ int argc;
+ nds32_pseudo_opcode_func proc;
+ int pseudo_val;
+
+ /* Some instructions are not pseudo opcode, but they might still be
+ expanded or changed with other instruction combination for some
+ conditions. We also apply this structure to assist such work.
+
+ For example, if the distance of branch target '.L0' is larger than
+ imm8s<<1 range,
+
+ the instruction:
+
+ beqzs8 .L0
+
+ will be transformed into:
+
+ bnezs8 .LCB0
+ j .L0
+ .LCB0:
+
+ However, sometimes we do not want assembler to do such changes
+ because compiler knows how to generate corresponding instruction sequence.
+ Use this field to indicate that this opcode is also a physical instruction.
+ If the flag 'verbatim' is nozero and this opcode
+ is a physical instruction, we should not expand it. */
+ int physical_op;
+};
+#define PV_DONT_CARE 0
+
+static struct hash_control *nds32_pseudo_opcode_hash = NULL;
+
+static int
+builtin_isreg (const char *s, const char *x ATTRIBUTE_UNUSED)
+{
+ return s[0] == '$';
+}
+
+static int
+builtin_regnum (const char *s, const char *x ATTRIBUTE_UNUSED)
+{
+ struct nds32_keyword *k;
+ if (*s != '$')
+ return -1;
+ s++;
+ k = hash_find (nds32_gprs_hash, s);
+
+ if (k == NULL)
+ return -1;
+
+ return k->value;
+}
+
+static int
+builtin_addend (const char *s, char *x ATTRIBUTE_UNUSED)
+{
+ const char *ptr = s;
+
+ while (*ptr != '+' && *ptr != '-' && *ptr)
+ ++ptr;
+
+ if (*ptr == 0)
+ return 0;
+ else
+ return strtol (ptr, NULL, 0);
+}
+
+static void
+md_assemblef (char *format, ...)
+{
+ /* FIXME: hope this is long enough. */
+ char line[1024];
+ va_list ap;
+ unsigned int r;
+
+ va_start (ap, format);
+ r = vsnprintf (line, sizeof (line), format, ap);
+ md_assemble (line);
+
+ gas_assert (r < sizeof (line));
+}
+
+/* Some prototypes here, since some op may use another op. */
+static void do_pseudo_li_internal (char *rt, int imm32s);
+static void do_pseudo_move_reg_internal (char *dst, char *src);
+
+static void
+do_pseudo_b (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ char *arg_label = argv[0];
+ relaxing = TRUE;
+ /* b label */
+ if (nds32_pic && strstr (arg_label, "@PLT"))
+ {
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemble ("add $ta,$ta,$gp");
+ md_assemble ("jr $ta");
+ }
+ else
+ {
+ md_assemblef ("j %s", arg_label);
+ }
+ relaxing = FALSE;
+}
+
+static void
+do_pseudo_bal (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ char *arg_label = argv[0];
+ relaxing = TRUE;
+ /* bal|call label */
+ if (nds32_pic
+ && (strstr (arg_label, "@GOT") || strstr (arg_label, "@PLT")))
+ {
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemble ("add $ta,$ta,$gp");
+ md_assemble ("jral $ta");
+ }
+ else
+ {
+ md_assemblef ("jal %s", arg_label);
+ }
+ relaxing = FALSE;
+}
+
+static void
+do_pseudo_bge (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("beqz $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_bges (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("beqz $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_bgt (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("bnez $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_bgts (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("bnez $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_ble (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ md_assemblef ("slt $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("beqz $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_bles (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* bgt rt5, ra5, label */
+ md_assemblef ("slts $ta,%s,%s", argv[1], argv[0]);
+ md_assemblef ("beqz $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_blt (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ md_assemblef ("slt $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("bnez $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_blts (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* rt5, ra5, label */
+ md_assemblef ("slts $ta,%s,%s", argv[0], argv[1]);
+ md_assemblef ("bnez $ta,%s", argv[2]);
+}
+
+static void
+do_pseudo_br (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("jr %s", argv[0]);
+}
+
+static void
+do_pseudo_bral (int argc, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ if (argc == 1)
+ md_assemblef ("jral $lp,%s", argv[0]);
+ else
+ md_assemblef ("jral %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_la_internal (const char *arg_reg, const char *arg_label,
+ const char *line)
+{
+ relaxing = TRUE;
+ /* rt, label */
+ if (!nds32_pic && !strstr(arg_label, "@"))
+ {
+ md_assemblef ("sethi %s,hi20(%s)", arg_reg, arg_label);
+ md_assemblef ("ori %s,%s,lo12(%s)", arg_reg, arg_reg, arg_label);
+ }
+ else if (strstr (arg_label, "@TPOFF"))
+ {
+ /* la $rt, sym@TPOFF */
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemblef ("add %s,$ta,%s", arg_reg, TLS_REG);
+ }
+ else if (strstr(arg_label, "@GOTTPOFF"))
+ {
+ /* la $rt, sym@GOTTPOFF*/
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("lwi $ta,[$ta+lo12(%s)]", arg_label);
+ md_assemblef ("add %s,$ta,%s", arg_reg, TLS_REG);
+ }
+ else if (nds32_pic && ((strstr (arg_label, "@PLT")
+ || strstr (arg_label, "@GOTOFF"))))
+ {
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemblef ("add %s,$ta,$gp", arg_reg);
+ }
+ else if (nds32_pic && strstr (arg_label, "@GOT"))
+ {
+ long addend = builtin_addend (arg_label, NULL);
+
+ md_assemblef ("sethi $ta,hi20(%s)", arg_label);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", arg_label);
+ md_assemblef ("lw %s,[$gp+$ta]", arg_reg);
+ if (addend != 0)
+ {
+ if (addend < 0x4000 && addend >= -0x4000)
+ {
+ md_assemblef ("addi %s,%s,%d", arg_reg, arg_reg, addend);
+ }
+ else
+ {
+ do_pseudo_li_internal ("$ta", addend);
+ md_assemblef ("add %s,$ta,%s", arg_reg, arg_reg);
+ }
+ }
+ }
+ else
+ as_bad (_("need PIC qualifier with symbol. '%s'"), line);
+ relaxing = FALSE;
+}
+
+static void
+do_pseudo_la (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ do_pseudo_la_internal (argv[0], argv[1], argv[argc]);
+}
+
+static void
+do_pseudo_li_internal (char *rt, int imm32s)
+{
+ if (enable_16bit && imm32s <= 0xf && imm32s >= -0x10)
+ md_assemblef ("movi55 %s,%d", rt, imm32s);
+ else if (imm32s <= 0x7ffff && imm32s >= -0x80000)
+ md_assemblef ("movi %s,%d", rt, imm32s);
+ else if ((imm32s & 0xfff) == 0)
+ md_assemblef ("sethi %s,hi20(%d)", rt, imm32s);
+ else
+ {
+ md_assemblef ("sethi %s,hi20(%d)", rt, imm32s);
+ md_assemblef ("ori %s,%s,lo12(%d)", rt, rt, imm32s);
+ }
+}
+
+static void
+do_pseudo_li (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* Validate argv[1] for constant expression. */
+ expressionS exp;
+
+ parse_expression (argv[1], &exp);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("Operand is not a constant. `%s'"), argv[argc]);
+ return;
+ }
+
+ do_pseudo_li_internal (argv[0], exp.X_add_number);
+}
+
+static void
+do_pseudo_ls_bhw (int argc ATTRIBUTE_UNUSED, char *argv[], int pv)
+{
+ char ls = 'r';
+ char size = 'x';
+ const char *sign = "";
+
+ /* Prepare arguments for various load/store. */
+ sign = (pv & 0x10) ? "s" : "";
+ ls = (pv & 0x80000000) ? 's' : 'l';
+ switch (pv & 0x3)
+ {
+ case 0: size = 'b'; break;
+ case 1: size = 'h'; break;
+ case 2: size = 'w'; break;
+ }
+
+ if (ls == 's' || size == 'w')
+ sign = "";
+
+ if (builtin_isreg (argv[1], NULL))
+ {
+ /* lwi */
+ md_assemblef ("%c%ci %s,[%s]", ls, size, argv[0], argv[1]);
+ }
+ else if (!nds32_pic)
+ {
+ relaxing = TRUE;
+ if (strstr (argv[1], "@TPOFF"))
+ {
+ /* ls.w $rt, sym@TPOFF */
+ md_assemblef ("sethi $ta,hi20(%s)", argv[1]);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]);
+ md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], TLS_REG);
+ }
+ else if (strstr (argv[1], "@GOTTPOFF"))
+ {
+ /* ls.w $rt, sym@GOTTPOFF */
+ md_assemblef ("sethi $ta,hi20(%s)", argv[1]);
+ md_assemblef ("lwi $ta,[$ta+lo12(%s)]", argv[1]);
+ md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], TLS_REG);
+ }
+ else
+ {
+ /* lwi */
+ md_assemblef ("sethi $ta,hi20(%s)", argv[1]);
+ md_assemblef ("%c%c%si %s,[$ta+lo12(%s)]", ls, size, sign, argv[0], argv[1]);
+ }
+ relaxing = FALSE;
+ }
+ else
+ {
+ relaxing = TRUE;
+ /* PIC code. */
+ if (strstr (argv[1], "@GOTOFF"))
+ {
+ /* lw */
+ md_assemblef ("sethi $ta,hi20(%s)", argv[1]);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]);
+ md_assemblef ("%c%c%s %s,[$ta+$gp]", ls, size, sign, argv[0]);
+ }
+ else if (strstr (argv[1], "@GOT"))
+ {
+ long addend = builtin_addend (argv[1], NULL);
+ /* lw */
+ md_assemblef ("sethi $ta,hi20(%s)", argv[1]);
+ md_assemblef ("ori $ta,$ta,lo12(%s)", argv[1]);
+ md_assemble ("lw $ta,[$gp+$ta]"); /* Load address word. */
+ if (addend < 0x10000 && addend >= -0x10000)
+ {
+ md_assemblef ("%c%c%si %s,[$ta+(%d)]", ls, size, sign, argv[0], addend);
+ }
+ else
+ {
+ /* lw */
+ do_pseudo_li_internal (argv[0], addend);
+ md_assemblef ("%c%c%s %s,[$ta+%s]", ls, size, sign, argv[0], argv[0]);
+ }
+ }
+ else
+ {
+ as_bad (_("needs @GOT or @GOTOFF. %s"), argv[argc]);
+ }
+ relaxing = FALSE;
+ }
+}
+
+static void
+do_pseudo_ls_bhwp (int argc ATTRIBUTE_UNUSED, char *argv[], int pv)
+{
+ char *arg_rt = argv[0];
+ char *arg_label = argv[1];
+ char *arg_inc = argv[2];
+ char ls = 'r';
+ char size = 'x';
+ const char *sign = "";
+
+ /* Prepare arguments for various load/store. */
+ sign = (pv & 0x10) ? "s" : "";
+ ls = (pv & 0x80000000) ? 's' : 'l';
+ switch (pv & 0x3)
+ {
+ case 0: size = 'b'; break;
+ case 1: size = 'h'; break;
+ case 2: size = 'w'; break;
+ }
+
+ if (ls == 's' || size == 'w')
+ sign = "";
+
+ do_pseudo_la_internal ("$ta", arg_label, argv[argc]);
+ md_assemblef ("%c%c%si.bi %s,[$ta],%s", ls, size, sign, arg_rt, arg_inc);
+}
+
+static void
+do_pseudo_ls_bhwpc (int argc ATTRIBUTE_UNUSED, char *argv[], int pv)
+{
+ char *arg_rt = argv[0];
+ char *arg_inc = argv[1];
+ char ls = 'r';
+ char size = 'x';
+ const char *sign = "";
+
+ /* Prepare arguments for various load/store. */
+ sign = (pv & 0x10) ? "s" : "";
+ ls = (pv & 0x80000000) ? 's' : 'l';
+ switch (pv & 0x3)
+ {
+ case 0: size = 'b'; break;
+ case 1: size = 'h'; break;
+ case 2: size = 'w'; break;
+ }
+
+ if (ls == 's' || size == 'w')
+ sign = "";
+
+ md_assemblef ("%c%c%si.bi %s,[$ta],%s", ls, size, sign, arg_rt, arg_inc);
+}
+
+static void
+do_pseudo_ls_bhwi (int argc ATTRIBUTE_UNUSED, char *argv[], int pv)
+{
+ char ls = 'r';
+ char size = 'x';
+ const char *sign = "";
+
+ /* Prepare arguments for various load/store. */
+ sign = (pv & 0x10) ? "s" : "";
+ ls = (pv & 0x80000000) ? 's' : 'l';
+ switch (pv & 0x3)
+ {
+ case 0: size = 'b'; break;
+ case 1: size = 'h'; break;
+ case 2: size = 'w'; break;
+ }
+
+ if (ls == 's' || size == 'w')
+ sign = "";
+
+ md_assemblef ("%c%c%si.bi %s,%s,%s",
+ ls, size, sign, argv[0], argv[1], argv[2]);
+}
+
+static void
+do_pseudo_move_reg_internal (char *dst, char *src)
+{
+ if (enable_16bit)
+ md_assemblef ("mov55 %s,%s", dst, src);
+ else
+ md_assemblef ("ori %s,%s,0", dst, src);
+}
+
+static void
+do_pseudo_move (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ parse_expression (argv[1], &exp);
+
+ if (builtin_isreg (argv[1], NULL))
+ do_pseudo_move_reg_internal (argv[0], argv[1]);
+ else if (exp.X_op == O_constant)
+ /* move $rt, imm -> li $rt, imm */
+ do_pseudo_li_internal (argv[0], exp.X_add_number);
+ else
+ /* l.w $rt, var -> l.w $rt, var */
+ do_pseudo_ls_bhw (argc, argv, 2);
+}
+
+static void
+do_pseudo_neg (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* Instead of "subri". */
+ md_assemblef ("subri %s,%s,0", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_not (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("nor %s,%s,%s", argv[0], argv[1], argv[1]);
+}
+
+static void
+do_pseudo_pushpopm (int argc, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* posh/pop $ra, $rb */
+ /* SMW.{b | a}{i | d}{m?} Rb, [Ra], Re, Enable4 */
+ int rb, re, ra, en4;
+ int i;
+ char *opc = "pushpopm";
+
+ if (argc == 3)
+ as_bad ("'pushm/popm $ra5, $rb5, $label' is deprecated. "
+ "Only 'pushm/popm $ra5' is supported now. %s", argv[argc]);
+ else if (argc == 1)
+ as_bad ("'pushm/popm $ra5, $rb5'. %s\n", argv[argc]);
+
+ if (strstr (argv[argc], "pop") == argv[argc])
+ opc = "lmw.bim";
+ else if (strstr (argv[argc], "push") == argv[argc])
+ opc = "smw.adm";
+ else
+ as_fatal ("nds32-as internal error. %s", argv[argc]);
+
+ rb = builtin_regnum (argv[0], NULL);
+ re = builtin_regnum (argv[1], NULL);
+
+ if (re < rb)
+ {
+ as_warn ("$rb should not be smaller than $ra. %s", argv[argc]);
+ /* Swap to right order. */
+ ra = re;
+ re = rb;
+ rb = ra;
+ }
+
+ /* Build enable4 mask. */
+ en4 = 0;
+ if (re >= 28 || rb >= 28)
+ {
+ for (i = (rb >= 28? rb: 28); i <= re; i++)
+ en4 |= 1 << (3 - (i - 28));
+ }
+
+ /* Adjust $re, $rb. */
+ if (rb >= 28)
+ rb = re = 31;
+ else if (nds32_gpr16 != 1 && re >= 28)
+ re = 27;
+
+ /* Reduce register. */
+ if (nds32_gpr16 && re > 10 && !(rb == 31 && re == 31))
+ {
+ if (re >= 15 && strstr(opc, "smw") != NULL)
+ md_assemblef ("%s $r15,[$sp],$r15,%d", opc, en4);
+ if (rb <= 10)
+ md_assemblef ("%s $r%d,[$sp],$r10, 0x0", opc, rb);
+ if (re >= 15 && strstr(opc, "lmw") != NULL)
+ md_assemblef ("%s $r15,[$sp],$r15,%d", opc, en4);
+ }
+ else
+ md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4);
+}
+
+static void
+do_pseudo_pushpop (int argc, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* push/pop $ra5, $label=$sp */
+ char *argvm[3];
+
+ if (argc == 2)
+ as_bad ("'push/pop $ra5, rb5' is deprecated. "
+ "Only 'push/pop $ra5' is supported now. %s", argv[argc]);
+
+ argvm[0] = argv[0];
+ argvm[1] = argv[0];
+ argvm[2] = argv[argc];
+ do_pseudo_pushpopm (2, argvm, PV_DONT_CARE);
+}
+
+static void
+do_pseudo_v3push (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("push25 %s,%s", argv[0], argv[1]);
+}
+
+static void
+do_pseudo_v3pop (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ md_assemblef ("pop25 %s,%s", argv[0], argv[1]);
+}
+
+/* pv == 0, parsing "push.s" pseudo instruction operands.
+ pv != 0, parsing "pop.s" pseudo instruction operands. */
+
+static void
+do_pseudo_pushpop_stack (int argc, char *argv[], int pv)
+{
+ /* push.s Rb,Re,{$fp $gp $lp $sp} ==> smw.adm Rb,[$sp],Re,Eable4 */
+ /* pop.s Rb,Re,{$fp $gp $lp $sp} ==> lmw.bim Rb,[$sp],Re,Eable4 */
+
+ int rb, re;
+ int en4;
+ int last_arg_index;
+ char *opc = (pv == 0) ? "smw.adm" : "lmw.bim";
+
+ rb = re = 0;
+
+ if (argc == 1)
+ {
+ /* argc=1, operands pattern: { $fp $gp $lp $sp } */
+
+ /* Set register number Rb = Re = $sp = $r31. */
+ rb = re = 31;
+ }
+ else if (argc == 2 || argc == 3)
+ {
+ /* argc=2, operands pattern: Rb, Re */
+ /* argc=3, operands pattern: Rb, Re, { $fp $gp $lp $sp } */
+
+ /* Get register number in integer. */
+ rb = builtin_regnum (argv[0], NULL);
+ re = builtin_regnum (argv[1], NULL);
+
+ /* Rb should be equal/less than Re. */
+ if (rb > re)
+ as_bad ("The first operand (%s) should be equal to or smaller than "
+ "second operand (%s).", argv[0], argv[1]);
+
+ /* forbid using $fp|$gp|$lp|$sp in Rb or Re
+ r28 r29 r30 r31 */
+ if (rb >= 28)
+ as_bad ("Cannot use $fp, $gp, $lp, or $sp at first operand !!");
+ if (re >= 28)
+ as_bad ("Cannot use $fp, $gp, $lp, or $sp at second operand !!");
+ }
+ else
+ {
+ as_bad ("Invalid operands pattern !!");
+ }
+
+ /* Build Enable4 mask. */
+ /* Using last_arg_index for argc=1|2|3 is safe, because $fp, $gp, $lp,
+ and $sp only appear in argc=1 or argc=3 if argc=2, en4 remains 0,
+ which is also valid for code generation. */
+ en4 = 0;
+ last_arg_index = argc - 1;
+ if (strstr (argv[last_arg_index], "$fp"))
+ en4 |= 8;
+ if (strstr (argv[last_arg_index], "$gp"))
+ en4 |= 4;
+ if (strstr (argv[last_arg_index], "$lp"))
+ en4 |= 2;
+ if (strstr (argv[last_arg_index], "$sp"))
+ en4 |= 1;
+
+ md_assemblef ("%s $r%d,[$sp],$r%d,%d", opc, rb, re, en4);
+}
+
+static void
+do_pseudo_push_bhwd (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ char size = 'x';
+ /* If users omit push location, use $sp as default value. */
+ char location[8] = "$sp"; /* 8 is enough for register name. */
+
+ switch (pv & 0x3)
+ {
+ case 0: size = 'b'; break;
+ case 1: size = 'h'; break;
+ case 2: size = 'w'; break;
+ case 3: size = 'w'; break;
+ }
+
+ if (argc == 2)
+ {
+ strncpy (location, argv[1], 8);
+ location[7] = '\0';
+ }
+
+ md_assemblef ("l.%c $ta,%s", size, argv[0]);
+ md_assemblef ("smw.adm $ta,[%s],$ta", location);
+
+ if ((pv & 0x3) == 0x3) /* double-word */
+ {
+ md_assemblef ("l.w $ta,%s+4", argv[0]);
+ md_assemblef ("smw.adm $ta,[%s],$ta", location);
+ }
+}
+
+static void
+do_pseudo_pop_bhwd (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ char size = 'x';
+ /* If users omit pop location, use $sp as default value. */
+ char location[8] = "$sp"; /* 8 is enough for register name. */
+
+ switch (pv & 0x3)
+ {
+ case 0: size = 'b'; break;
+ case 1: size = 'h'; break;
+ case 2: size = 'w'; break;
+ case 3: size = 'w'; break;
+ }
+
+ if (argc == 3)
+ {
+ strncpy (location, argv[2], 8);
+ location[7] = '\0';
+ }
+
+ if ((pv & 0x3) == 0x3) /* double-word */
+ {
+ md_assemblef ("lmw.bim %s,[%s],%s", argv[1], location, argv[1]);
+ md_assemblef ("s.w %s,%s+4", argv[1], argv[0]);
+ }
+
+ md_assemblef ("lmw.bim %s,[%s],%s", argv[1], location, argv[1]);
+ md_assemblef ("s.%c %s,%s", size, argv[1], argv[0]);
+}
+
+static void
+do_pseudo_pusha (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* If users omit push location, use $sp as default value. */
+ char location[8] = "$sp"; /* 8 is enough for register name. */
+
+ if (argc == 2)
+ {
+ strncpy (location, argv[1], 8);
+ location[7] = '\0';
+ }
+
+ md_assemblef ("la $ta,%s", argv[0]);
+ md_assemblef ("smw.adm $ta,[%s],$ta", location);
+}
+
+static void
+do_pseudo_pushi (int argc ATTRIBUTE_UNUSED, char *argv[], int pv ATTRIBUTE_UNUSED)
+{
+ /* If users omit push location, use $sp as default value. */
+ char location[8] = "$sp"; /* 8 is enough for register name. */
+
+ if (argc == 2)
+ {
+ strncpy (location, argv[1], 8);
+ location[7] = '\0';
+ }
+
+ md_assemblef ("li $ta,%s", argv[0]);
+ md_assemblef ("smw.adm $ta,[%s],$ta", location);
+}
+
+struct nds32_pseudo_opcode nds32_pseudo_opcode_table[] =
+{
+ {"b", 1, do_pseudo_b, 0, 0},
+ {"bal", 1, do_pseudo_bal, 0, 0},
+
+ {"bge", 3, do_pseudo_bge, 0, 0},
+ {"bges", 3, do_pseudo_bges, 0, 0},
+
+ {"bgt", 3, do_pseudo_bgt, 0, 0},
+ {"bgts", 3, do_pseudo_bgts, 0, 0},
+
+ {"ble", 3, do_pseudo_ble, 0, 0},
+ {"bles", 3, do_pseudo_bles, 0, 0},
+
+ {"blt", 3, do_pseudo_blt, 0, 0},
+ {"blts", 3, do_pseudo_blts, 0, 0},
+
+ {"br", 1, do_pseudo_br, 0, 0},
+ {"bral", 1, do_pseudo_bral, 0, 0},
+
+ {"call", 1, do_pseudo_bal, 0, 0},
+
+ {"la", 2, do_pseudo_la, 0, 0},
+ {"li", 2, do_pseudo_li, 0, 0},
+
+ {"l.b", 2, do_pseudo_ls_bhw, 0, 0},
+ {"l.h", 2, do_pseudo_ls_bhw, 1, 0},
+ {"l.w", 2, do_pseudo_ls_bhw, 2, 0},
+ {"l.bs", 2, do_pseudo_ls_bhw, 0 | 0x10, 0},
+ {"l.hs", 2, do_pseudo_ls_bhw, 1 | 0x10, 0},
+ {"s.b", 2, do_pseudo_ls_bhw, 0 | 0x80000000, 0},
+ {"s.h", 2, do_pseudo_ls_bhw, 1 | 0x80000000, 0},
+ {"s.w", 2, do_pseudo_ls_bhw, 2 | 0x80000000, 0},
+
+ {"l.bp", 3, do_pseudo_ls_bhwp, 0, 0},
+ {"l.bpc", 3, do_pseudo_ls_bhwpc, 0, 0},
+ {"l.hp", 3, do_pseudo_ls_bhwp, 1, 0},
+ {"l.hpc", 3, do_pseudo_ls_bhwpc, 1, 0},
+ {"l.wp", 3, do_pseudo_ls_bhwp, 2, 0},
+ {"l.wpc", 3, do_pseudo_ls_bhwpc, 2, 0},
+ {"l.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x10, 0},
+ {"l.bspc", 3, do_pseudo_ls_bhwpc, 0 | 0x10, 0},
+ {"l.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x10, 0},
+ {"l.hspc", 3, do_pseudo_ls_bhwpc, 1 | 0x10, 0},
+ {"s.bp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000, 0},
+ {"s.bpc", 3, do_pseudo_ls_bhwpc, 0 | 0x80000000, 0},
+ {"s.hp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000, 0},
+ {"s.hpc", 3, do_pseudo_ls_bhwpc, 1 | 0x80000000, 0},
+ {"s.wp", 3, do_pseudo_ls_bhwp, 2 | 0x80000000, 0},
+ {"s.wpc", 3, do_pseudo_ls_bhwpc, 2 | 0x80000000, 0},
+ {"s.bsp", 3, do_pseudo_ls_bhwp, 0 | 0x80000000 | 0x10, 0},
+ {"s.hsp", 3, do_pseudo_ls_bhwp, 1 | 0x80000000 | 0x10, 0},
+
+ {"lbi.p", 3, do_pseudo_ls_bhwi, 0, 0},
+ {"lhi.p", 3, do_pseudo_ls_bhwi, 1, 0},
+ {"lwi.p", 3, do_pseudo_ls_bhwi, 2, 0},
+ {"sbi.p", 3, do_pseudo_ls_bhwi, 0 | 0x80000000, 0},
+ {"shi.p", 3, do_pseudo_ls_bhwi, 1 | 0x80000000, 0},
+ {"swi.p", 3, do_pseudo_ls_bhwi, 2 | 0x80000000, 0},
+ {"lbsi.p", 3, do_pseudo_ls_bhwi, 0 | 0x10, 0},
+ {"lhsi.p", 3, do_pseudo_ls_bhwi, 1 | 0x10, 0},
+ {"lwsi.p", 3, do_pseudo_ls_bhwi, 2 | 0x10, 0},
+
+ {"move", 2, do_pseudo_move, 0, 0},
+ {"neg", 2, do_pseudo_neg, 0, 0},
+ {"not", 2, do_pseudo_not, 0, 0},
+
+ {"pop", 2, do_pseudo_pushpop, 0, 0},
+ {"push", 2, do_pseudo_pushpop, 0, 0},
+ {"popm", 2, do_pseudo_pushpopm, 0, 0},
+ {"pushm", 3, do_pseudo_pushpopm, 0, 0},
+
+ {"v3push", 2, do_pseudo_v3push, 0, 0},
+ {"v3pop", 2, do_pseudo_v3pop, 0, 0},
+
+ /* Support pseudo instructions of pushing/poping registers into/from stack
+ push.s Rb, Re, { $fp $gp $lp $sp } ==> smw.adm Rb,[$sp],Re,Enable4
+ pop.s Rb, Re, { $fp $gp $lp $sp } ==> lmw.bim Rb,[$sp],Re,Enable4 */
+ { "push.s", 3, do_pseudo_pushpop_stack, 0, 0 },
+ { "pop.s", 3, do_pseudo_pushpop_stack, 1, 0 },
+ { "push.b", 2, do_pseudo_push_bhwd, 0, 0 },
+ { "push.h", 2, do_pseudo_push_bhwd, 1, 0 },
+ { "push.w", 2, do_pseudo_push_bhwd, 2, 0 },
+ { "push.d", 2, do_pseudo_push_bhwd, 3, 0 },
+ { "pop.b", 3, do_pseudo_pop_bhwd, 0, 0 },
+ { "pop.h", 3, do_pseudo_pop_bhwd, 1, 0 },
+ { "pop.w", 3, do_pseudo_pop_bhwd, 2, 0 },
+ { "pop.d", 3, do_pseudo_pop_bhwd, 3, 0 },
+ { "pusha", 2, do_pseudo_pusha, 0, 0 },
+ { "pushi", 2, do_pseudo_pushi, 0, 0 },
+
+ {NULL, 0, NULL, 0, 0}
+};
+
+static void
+nds32_init_nds32_pseudo_opcodes (void)
+{
+ struct nds32_pseudo_opcode *opcode = nds32_pseudo_opcode_table;
+
+ nds32_pseudo_opcode_hash = hash_new ();
+ for ( ; opcode->opcode; opcode++)
+ {
+ void *op;
+
+ op = hash_find (nds32_pseudo_opcode_hash, opcode->opcode);
+ if (op != NULL)
+ {
+ as_warn (_("Duplicated pseudo-opcode %s."), opcode->opcode);
+ continue;
+ }
+ hash_insert (nds32_pseudo_opcode_hash, opcode->opcode, opcode);
+ }
+}
+
+static struct nds32_pseudo_opcode *
+nds32_lookup_pseudo_opcode (char *str)
+{
+ int i = 0;
+ /* Assume pseudo-opcode are less than 16-char in length. */
+ char op[16] = {0};
+
+ for (i = 0; i < (int)ARRAY_SIZE (op); i++)
+ {
+ if (ISSPACE (op[i] = str[i]))
+ break;
+ }
+
+ if (i >= (int)ARRAY_SIZE (op))
+ return NULL;
+
+ op[i] = '\0';
+
+ return hash_find (nds32_pseudo_opcode_hash, op);
+}
+
+static void
+nds32_pseudo_opcode_wrapper (char *line, struct nds32_pseudo_opcode *opcode)
+{
+ int argc = 0;
+ char *argv[8] = {NULL};
+ char *s;
+ char *str = xstrdup (line);
+
+ /* Parse arguments for opcode. */
+ s = str + strlen (opcode->opcode);
+
+ if (!s[0])
+ goto end;
+
+ /* Dummy comma to ease separate arguments as below. */
+ s[0] = ',';
+ do
+ {
+ if (s[0] == ',')
+ {
+ if (argc >= opcode->argc
+ || (argc >= (int)ARRAY_SIZE (argv) - 1))
+ as_bad (_("Too many argument. `%s'"), line);
+
+ argv[argc] = s + 1;
+ argc ++;
+ s[0] = '\0';
+ }
+ ++s;
+ } while (s[0] != '\0');
+end:
+ /* Put the origin line for debugging. */
+ argv[argc] = line;
+ opcode->proc (argc, argv, opcode->pseudo_val);
+ free (str);
+}
+
+/* This function will be invoked from function `nds32_after_parse_args'.
+ Thus, if the value of option has been set, keep the value the way it is. */
+
+static int
+nds32_parse_arch (char *str)
+{
+ static const struct nds32_arch
+ {
+ const char *name;
+ int baseline;
+ int reduced_reg;
+ int fpu_sp_ext;
+ int fpu_dp_ext;
+ int fpu_freg;
+ int abi;
+ } archs[] =
+ {
+ {"v3m", ISA_V3M, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI},
+ {"v3j", ISA_V3, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI},
+ {"v3s", ISA_V3, 0, 1, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS},
+ {"v3f", ISA_V3, 0, 1, 1, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS},
+ {"v3", ISA_V3, 0, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI},
+ {"v2j", ISA_V2, 1, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI},
+ {"v2s", ISA_V2, 0, 1, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS},
+ {"v2f", ISA_V2, 0, 1, 1, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_V2FP_PLUS},
+ {"v2", ISA_V2, 0, 0, 0, E_NDS32_FPU_REG_32SP_16DP, E_NDS_ABI_AABI},
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE (archs); i++)
+ {
+ if (strcmp (str, archs[i].name) != 0)
+ continue;
+
+ /* The value `-1' represents this option has *NOT* been set. */
+ nds32_baseline = (-1 != nds32_baseline) ? nds32_baseline : archs[i].baseline;
+ nds32_gpr16 = (-1 != nds32_gpr16) ? nds32_gpr16 : archs[i].reduced_reg;
+ nds32_fpu_sp_ext = (-1 != nds32_fpu_sp_ext) ? nds32_fpu_sp_ext : archs[i].fpu_sp_ext;
+ nds32_fpu_dp_ext = (-1 != nds32_fpu_dp_ext) ? nds32_fpu_dp_ext : archs[i].fpu_dp_ext;
+ nds32_freg = (-1 != nds32_freg) ? nds32_freg : archs[i].fpu_freg;
+ nds32_abi = (-1 != nds32_abi) ? nds32_abi : archs[i].abi;
+
+ return 1;
+ }
+
+ /* Logic here rejects the input arch name. */
+ as_bad (_("unknown arch name `%s'\n"), str);
+
+ return 1;
+}
+
+/* This function parses "baseline" specified. */
+
+static int
+nds32_parse_baseline (char *str)
+{
+ if (strcmp (str, "v3") == 0)
+ nds32_baseline = ISA_V3;
+ else if (strcmp (str, "v3m") == 0)
+ nds32_baseline = ISA_V3M;
+ else if (strcmp (str, "v2") == 0)
+ nds32_baseline = ISA_V2;
+ else
+ {
+ /* Logic here rejects the input baseline. */
+ as_bad (_("unknown baseline `%s'\n"), str);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* This function parses "fpu-freg" specified. */
+
+static int
+nds32_parse_freg (char *str)
+{
+ if (strcmp (str, "2") == 0)
+ nds32_freg = E_NDS32_FPU_REG_32SP_16DP;
+ else if (strcmp (str, "3") == 0)
+ nds32_freg = E_NDS32_FPU_REG_32SP_32DP;
+ else if (strcmp (str, "1") == 0)
+ nds32_freg = E_NDS32_FPU_REG_16SP_8DP;
+ else if (strcmp (str, "0") == 0)
+ nds32_freg = E_NDS32_FPU_REG_8SP_4DP;
+ else
+ {
+ /* Logic here rejects the input FPU configuration. */
+ as_bad (_("unknown FPU configuration `%s'\n"), str);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* This function parse "abi=" specified. */
+
+static int
+nds32_parse_abi (char *str)
+{
+ if (strcmp (str, "v2") == 0)
+ nds32_abi = E_NDS_ABI_AABI;
+ /* Obsolete. */
+ else if (strcmp (str, "v2fp") == 0)
+ nds32_abi = E_NDS_ABI_V2FP;
+ else if (strcmp (str, "v1") == 0)
+ nds32_abi = E_NDS_ABI_V1;
+ else if (strcmp (str,"v2fpp") == 0)
+ nds32_abi = E_NDS_ABI_V2FP_PLUS;
+ else
+ {
+ /* Logic here rejects the input abi version. */
+ as_bad (_("unknown ABI version`%s'\n"), str);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* This function turn on all extensions and instructions support. */
+
+static int
+nds32_all_ext (void)
+{
+ nds32_mac = 1;
+ nds32_div = 1;
+ nds32_dx_regs = 1;
+ nds32_16bit_ext = 1;
+ nds32_perf_ext = 1;
+ nds32_perf_ext2 = 1;
+ nds32_string_ext = 1;
+ nds32_audio_ext = 1;
+ nds32_fpu_fma = 1;
+ nds32_fpu_sp_ext = 1;
+ nds32_fpu_dp_ext = 1;
+
+ return 1;
+}
+
+/* GAS will call md_parse_option whenever getopt returns an unrecognized code,
+ presumably indicating a special code value which appears in md_longopts.
+ This function should return non-zero if it handled the option and zero
+ otherwise. There is no need to print a message about an option not being
+ recognized. This will be handled by the generic code. */
+
+int
+nds32_parse_option (int c, char *arg)
+{
+ struct nds32_parse_option_table *coarse_tune;
+ struct nds32_set_option_table *fine_tune;
+ char *ptr_arg = NULL;
+
+ switch (c)
+ {
+ case OPTION_OPTIMIZE:
+ optimize = 1;
+ optimize_for_space = 0;
+ break;
+ case OPTION_OPTIMIZE_SPACE:
+ optimize = 0;
+ optimize_for_space = 1;
+ break;
+ case OPTION_BIG:
+ target_big_endian = 1;
+ break;
+ case OPTION_LITTLE:
+ target_big_endian = 0;
+ break;
+ case OPTION_TURBO:
+ nds32_all_ext ();
+ break;
+ case OPTION_PIC:
+ nds32_pic = 1;
+ break;
+ case OPTION_RELAX_FP_AS_GP_OFF:
+ nds32_relax_fp_as_gp = 0;
+ break;
+ case OPTION_RELAX_B2BB_ON:
+ nds32_relax_b2bb = 1;
+ break;
+ case OPTION_RELAX_ALL_OFF:
+ nds32_relax_all = 0;
+ break;
+ default:
+ /* Determination of which option table to search for to save time. */
+ if (!arg)
+ return 0;
+
+ ptr_arg = strchr (arg, '=');
+
+ if (ptr_arg)
+ {
+ /* Find the value after '='. */
+ if (ptr_arg != NULL)
+ ptr_arg++;
+ for (coarse_tune = parse_opts; coarse_tune->name != NULL; coarse_tune++)
+ {
+ if (strncmp (arg, coarse_tune->name, (ptr_arg - arg)) == 0)
+ {
+ coarse_tune->func (ptr_arg);
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ int disable = 0;
+
+ /* Filter out the Disable option first. */
+ if (strncmp (arg, "no-", 3) == 0)
+ {
+ disable = 1;
+ arg += 3;
+ }
+
+ for (fine_tune = toggle_opts; fine_tune->name != NULL; fine_tune++)
+ {
+ if (strcmp (arg, fine_tune->name) == 0)
+ {
+ if (fine_tune->var != NULL)
+ *fine_tune->var = (disable) ? 0 : 1;
+ return 1;
+ }
+ }
+ }
+ /* Nothing match. */
+ return 0;
+ }
+
+ return 1;
+}
+
+/* tc_check_label */
+
+void
+nds32_check_label (symbolS *label ATTRIBUTE_UNUSED)
+{
+ /* The code used to create BB is move to frob_label.
+ They should go there. */
+}
+
+static void
+set_endian_little (int on)
+{
+ target_big_endian = !on;
+}
+
+/* These functions toggles the generation of 16-bit. First encounter signals
+ the beginning of not generating 16-bit instructions and next encounter
+ signals the restoring back to default behavior. */
+
+static void
+trigger_16bit (int trigger)
+{
+ enable_16bit = trigger;
+}
+
+static int backup_16bit_mode;
+static void
+restore_16bit (int no_use ATTRIBUTE_UNUSED)
+{
+ enable_16bit = backup_16bit_mode;
+}
+
+static void
+off_16bit (int no_use ATTRIBUTE_UNUSED)
+{
+ backup_16bit_mode = enable_16bit;
+ enable_16bit = 0;
+}
+
+/* Built-in segments for small object. */
+typedef struct nds32_seg_entryT
+{
+ segT s;
+ const char *name;
+ flagword flags;
+} nds32_seg_entry;
+
+nds32_seg_entry nds32_seg_table[] =
+{
+ {NULL, ".sdata_f", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS | SEC_SMALL_DATA},
+ {NULL, ".sdata_b", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS | SEC_SMALL_DATA},
+ {NULL, ".sdata_h", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS | SEC_SMALL_DATA},
+ {NULL, ".sdata_w", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS | SEC_SMALL_DATA},
+ {NULL, ".sdata_d", SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA
+ | SEC_HAS_CONTENTS | SEC_SMALL_DATA},
+ {NULL, ".sbss_f", SEC_ALLOC | SEC_SMALL_DATA},
+ {NULL, ".sbss_b", SEC_ALLOC | SEC_SMALL_DATA},
+ {NULL, ".sbss_h", SEC_ALLOC | SEC_SMALL_DATA},
+ {NULL, ".sbss_w", SEC_ALLOC | SEC_SMALL_DATA},
+ {NULL, ".sbss_d", SEC_ALLOC | SEC_SMALL_DATA}
+};
+
+/* Indexes to nds32_seg_table[]. */
+enum NDS32_SECTIONS_ENUM
+{
+ SDATA_F_SECTION = 0,
+ SDATA_B_SECTION = 1,
+ SDATA_H_SECTION = 2,
+ SDATA_W_SECTION = 3,
+ SDATA_D_SECTION = 4,
+ SBSS_F_SECTION = 5,
+ SBSS_B_SECTION = 6,
+ SBSS_H_SECTION = 7,
+ SBSS_W_SECTION = 8,
+ SBSS_D_SECTION = 9
+};
+
+/* The following code is borrowed from v850_seg. Revise this is needed. */
+
+static void
+do_nds32_seg (int i, subsegT sub)
+{
+ nds32_seg_entry *seg = nds32_seg_table + i;
+
+ obj_elf_section_change_hook ();
+
+ if (seg->s != NULL)
+ subseg_set (seg->s, sub);
+ else
+ {
+ seg->s = subseg_new (seg->name, sub);
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ bfd_set_section_flags (stdoutput, seg->s, seg->flags);
+ if ((seg->flags & SEC_LOAD) == 0)
+ seg_info (seg->s)->bss = 1;
+ }
+ }
+}
+
+static void
+nds32_seg (int i)
+{
+ subsegT sub = get_absolute_expression ();
+
+ do_nds32_seg (i, sub);
+ demand_empty_rest_of_line ();
+}
+
+/* Set if label adjustment is needed. I should not adjust .xbyte in dwarf. */
+static symbolS *nds32_last_label; /* Last label for aligment. */
+
+/* This code is referred from D30V for adjust label to be with pedning
+ aligment. For example,
+ LBYTE: .byte 0x12
+ LHALF: .half 0x12
+ LWORD: .word 0x12
+ Without this, the above label will not attatch to incoming data. */
+
+static void
+nds32_adjust_label (int n)
+{
+ /* FIXME: I think adjust lable and alignment is
+ the programmer's obligation. Saddly, VLSI team doesn't
+ properly use .align for their test cases.
+ So I re-implement cons_align and auto adjust labels, again.
+
+ I think d30v's implmentation is simple and good enough. */
+
+ symbolS *label = nds32_last_label;
+ nds32_last_label = NULL;
+
+ /* SEC_ALLOC is used to eliminate .debug_ sections.
+ SEC_CODE is used to include section for ILM. */
+ if (((now_seg->flags & SEC_ALLOC) == 0 && (now_seg->flags & SEC_CODE) == 0)
+ || strcmp (now_seg->name, ".eh_frame") == 0
+ || strcmp (now_seg->name, ".gcc_except_table") == 0)
+ return;
+
+ /* Only frag by alignment when needed.
+ Otherwise, it will fail to optimize labels on 4-byte boundary. (bug8454)
+ See md_convert_frag () and RELAX_SET_RELAXABLE (frag) for details. */
+ if (frag_now_fix () & ((1 << n) -1 ))
+ {
+ if (subseg_text_p (now_seg))
+ frag_align_code (n, 0);
+ else
+ frag_align (n, 0, 0);
+
+ /* Record the minimum alignment for this segment. */
+ record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER);
+ }
+
+ if (label != NULL)
+ {
+ symbolS *sym;
+ int label_seen = FALSE;
+ struct frag *old_frag;
+ valueT old_value, new_value;
+
+ gas_assert (S_GET_SEGMENT (label) == now_seg);
+
+ old_frag = symbol_get_frag (label);
+ old_value = S_GET_VALUE (label);
+ new_value = (valueT) frag_now_fix ();
+
+ /* Multiple labels may be on the same address. And the last symbol
+ may not be a label at all, e.g., register name, external function names,
+ so I have to track the last label in tc_frob_label instead of
+ just using symbol_lastP. */
+ for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym))
+ {
+ if (symbol_get_frag (sym) == old_frag
+ && S_GET_VALUE (sym) == old_value)
+ {
+ /* Warning HERE! */
+ label_seen = TRUE;
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, new_value);
+ }
+ else if (label_seen && symbol_get_frag (sym) != old_frag)
+ break;
+ }
+ }
+}
+
+void
+nds32_cons_align (int size ATTRIBUTE_UNUSED)
+{
+ /* Do nothing here.
+ This is called before `md_flush_pending_output' is called by `cons'.
+
+ There are two things should be done for auto-adjust-label.
+ 1. Align data/instructions and adjust label to be attached to them.
+ 2. Clear auto-adjust state, so incommng data/instructions will not
+ adjust the label.
+
+ For example,
+ .byte 0x1
+ .L0:
+ .word 0x2
+ .word 0x3
+ in this case, '.word 0x2' will adjust the label, .L0, but '.word 0x3' should not.
+
+ I think `md_flush_pending_output' is a good place to clear the auto-adjust state,
+ but it is also called by `cons' before this function.
+ To simplify the code, instead of overriding .zero, .fill, .space, etc,
+ I think we should just adjust label in `nds32_aligned_X_cons' instead of here. */
+}
+
+static void
+nds32_aligned_cons (int idx)
+{
+ nds32_adjust_label (idx);
+ /* Call default handler. */
+ cons (1 << idx);
+ if (now_seg->flags & SEC_CODE
+ && now_seg->flags & SEC_ALLOC && now_seg->flags & SEC_RELOC)
+ {
+ /* Use BFD_RELOC_NDS32_DATA to avoid EX9 optimization replacing data. */
+ expressionS exp;
+
+ exp.X_add_number = 0;
+ exp.X_op = O_constant;
+ fix_new_exp (frag_now, frag_now_fix () - (1 << idx), 1 << idx,
+ &exp, 0, BFD_RELOC_NDS32_DATA);
+ }
+}
+
+/* `.double' directive. */
+
+static void
+nds32_aligned_float_cons (int type)
+{
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ nds32_adjust_label (2);
+ break;
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ nds32_adjust_label (4);
+ break;
+ default:
+ as_bad ("Unrecognized float type, %c\n", (char)type);
+ }
+ /* Call default handler. */
+ float_cons (type);
+}
+
+static void
+nds32_enable_pic (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Another way to do -mpic.
+ This is for GCC internal use and should always be first line
+ of code, otherwise, the effect is not determined. */
+ nds32_pic = 1;
+}
+
+static void
+nds32_set_abi (int ver)
+{
+ nds32_abi = ver;
+}
+
+/* Relax directive to set relocation R_NDS32_RELAX_ENTRY value. */
+
+static void
+nds32_relax_relocs (int relax)
+{
+ char saved_char;
+ char *name;
+ int i;
+ char *subtype_relax[] =
+ {"", "", "ex9", "ifc"};
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ for (i = 0; i < (int) ARRAY_SIZE (subtype_relax); i++)
+ {
+ if (strcmp (name, subtype_relax[i]) == 0)
+ {
+ switch (i)
+ {
+ case 0:
+ case 1:
+ enable_relax_relocs = relax & enable_relax_relocs;
+ enable_relax_ex9 = relax & enable_relax_ex9;
+ enable_relax_ifc = relax & enable_relax_ifc;
+ break;
+ case 2:
+ enable_relax_ex9 = relax;
+ break;
+ case 3:
+ enable_relax_ifc = relax;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+/* Record which arguments register($r0 ~ $r5) is not used in callee.
+ bit[i] for $ri */
+
+static void
+nds32_set_hint_func_args (int ignore ATTRIBUTE_UNUSED)
+{
+ ignore_rest_of_line ();
+}
+
+/* Insert relocations to mark the begin and end of a fp-omitted function,
+ for further relaxation use.
+ bit[i] for $ri */
+
+static void
+nds32_omit_fp_begin (int mode)
+{
+ expressionS exp;
+
+ if (nds32_relax_fp_as_gp == 0)
+ return;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ if (mode == 1)
+ {
+ in_omit_fp = 1;
+ exp.X_add_number = R_NDS32_RELAX_REGION_OMIT_FP_FLAG;
+ fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_RELAX_REGION_BEGIN);
+ }
+ else
+ {
+ in_omit_fp = 0;
+ exp.X_add_number = R_NDS32_RELAX_REGION_OMIT_FP_FLAG;
+ fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_RELAX_REGION_END);
+ }
+}
+
+/* Insert relocations to mark the begin and end of ex9 region,
+ for further relaxation use.
+ bit[i] for $ri */
+
+static void
+nds32_no_ex9_begin (int mode)
+{
+ expressionS exp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ if (mode == 1)
+ {
+ exp.X_add_number = R_NDS32_RELAX_REGION_NO_EX9_FLAG;
+ fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_RELAX_REGION_BEGIN);
+ }
+ else
+ {
+ exp.X_add_number = R_NDS32_RELAX_REGION_NO_EX9_FLAG;
+ fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_RELAX_REGION_END);
+ }
+}
+
+static void
+nds32_loop_begin (int mode)
+{
+ /* Insert loop region relocation here. */
+ expressionS exp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ if (mode == 1)
+ {
+ exp.X_add_number = R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG;
+ fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_RELAX_REGION_BEGIN);
+ }
+ else
+ {
+ exp.X_add_number = R_NDS32_RELAX_REGION_INNERMOST_LOOP_FLAG;
+ fix_new_exp (frag_now, frag_now_fix (), 0, &exp, 0,
+ BFD_RELOC_NDS32_RELAX_REGION_END);
+ }
+}
+
+struct nds32_relocs_group
+{
+ struct nds32_relocs_pattern *pattern;
+ struct nds32_relocs_group *next;
+};
+
+static struct nds32_relocs_group *nds32_relax_hint_current = NULL;
+
+/* Insert a relax hint. */
+
+static void
+nds32_relax_hint (int mode ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char saved_char;
+ struct nds32_relocs_pattern *relocs = NULL;
+ struct nds32_relocs_group *group, *new;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+ name = strdup (name);
+
+ /* Find relax hint entry for next instruction, and all member will be
+ initialized at that time. */
+ relocs = hash_find (nds32_hint_hash, name);
+ if (relocs == NULL)
+ {
+ relocs = malloc (sizeof (struct nds32_relocs_pattern));
+ hash_insert (nds32_hint_hash, name, relocs);
+ }
+ else
+ {
+ while (relocs->next)
+ relocs=relocs->next;
+ relocs->next = malloc (sizeof (struct nds32_relocs_pattern));
+ relocs = relocs->next;
+ }
+
+ relocs->next = NULL;
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+
+ /* Get the final one of relax hint series. */
+
+ /* It has to build this list because there are maybe more than one
+ instructions relative to the same instruction. It to connect to
+ next instruction after md_assemble. */
+ new = malloc (sizeof (struct nds32_relocs_group));
+ new->pattern = relocs;
+ new->next = NULL;
+ group = nds32_relax_hint_current;
+ if (!group)
+ nds32_relax_hint_current = new;
+ else
+ {
+ while (group->next != NULL)
+ group = group->next;
+ group->next = new;
+ }
+ relaxing = TRUE;
+}
+
+/* Decide the size of vector entries, only accepts 4 or 16 now. */
+
+static void
+nds32_vec_size (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ expression (&exp);
+
+ if (exp.X_op == O_constant)
+ {
+ if (exp.X_add_number == 4 || exp.X_add_number == 16)
+ {
+ if (vec_size == 0)
+ vec_size = exp.X_add_number;
+ else if (vec_size != exp.X_add_number)
+ as_warn (_("Different arguments of .vec_size are found, "
+ "previous %d, current %d"),
+ (int) vec_size, (int) exp.X_add_number);
+ }
+ else
+ as_warn (_("Argument of .vec_size is expected 4 or 16, actual: %d."),
+ (int) exp.X_add_number);
+ }
+ else
+ as_warn (_("Argument of .vec_size is not a constant."));
+}
+
+/* The behavior of ".flag" directive varies depending on the target.
+ In nds32 target, we use it to recognize whether this assembly content is
+ generated by compiler. Other features can also be added in this function
+ in the future. */
+
+static void
+nds32_flag (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char saved_char;
+ int i;
+ char *possible_flags[] = { "verbatim" };
+
+ /* Skip whitespaces. */
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ for (i = 0; i < (int) ARRAY_SIZE (possible_flags); i++)
+ {
+ if (strcmp (name, possible_flags[i]) == 0)
+ {
+ switch (i)
+ {
+ case 0:
+ /* flag: verbatim */
+ verbatim = 1;
+ break;
+ default:
+ break;
+ }
+ /* Already found the flag, no need to continue next loop. */
+ break;
+ }
+ }
+
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+static void
+nds32_n12hc (int ignore ATTRIBUTE_UNUSED)
+{
+ /* N1213HC core is used. */
+}
+
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Forced alignment if declared these ways. */
+ {"ascii", stringer, 8 + 0},
+ {"asciz", stringer, 8 + 1},
+ {"double", nds32_aligned_float_cons, 'd'},
+ {"dword", nds32_aligned_cons, 3},
+ {"float", nds32_aligned_float_cons, 'f'},
+ {"half", nds32_aligned_cons, 1},
+ {"hword", nds32_aligned_cons, 1},
+ {"int", nds32_aligned_cons, 2},
+ {"long", nds32_aligned_cons, 2},
+ {"octa", nds32_aligned_cons, 4},
+ {"quad", nds32_aligned_cons, 3},
+ {"qword", nds32_aligned_cons, 4},
+ {"short", nds32_aligned_cons, 1},
+ {"byte", nds32_aligned_cons, 0},
+ {"single", nds32_aligned_float_cons, 'f'},
+ {"string", stringer, 8 + 1},
+ {"word", nds32_aligned_cons, 2},
+
+ {"little", set_endian_little, 1},
+ {"big", set_endian_little, 0},
+ {"16bit_on", trigger_16bit, 1},
+ {"16bit_off", trigger_16bit, 0},
+ {"restore_16bit", restore_16bit, 0},
+ {"off_16bit", off_16bit, 0},
+
+ {"sdata_d", nds32_seg, SDATA_D_SECTION},
+ {"sdata_w", nds32_seg, SDATA_W_SECTION},
+ {"sdata_h", nds32_seg, SDATA_H_SECTION},
+ {"sdata_b", nds32_seg, SDATA_B_SECTION},
+ {"sdata_f", nds32_seg, SDATA_F_SECTION},
+
+ {"sbss_d", nds32_seg, SBSS_D_SECTION},
+ {"sbss_w", nds32_seg, SBSS_W_SECTION},
+ {"sbss_h", nds32_seg, SBSS_H_SECTION},
+ {"sbss_b", nds32_seg, SBSS_B_SECTION},
+ {"sbss_f", nds32_seg, SBSS_F_SECTION},
+
+ {"pic", nds32_enable_pic, 0},
+ {"n12_hc", nds32_n12hc, 0},
+ {"abi_1", nds32_set_abi, E_NDS_ABI_V1},
+ {"abi_2", nds32_set_abi, E_NDS_ABI_AABI},
+ /* Obsolete. */
+ {"abi_2fp", nds32_set_abi, E_NDS_ABI_V2FP},
+ {"abi_2fp_plus", nds32_set_abi, E_NDS_ABI_V2FP_PLUS},
+ {"relax", nds32_relax_relocs, 1},
+ {"no_relax", nds32_relax_relocs, 0},
+ {"hint_func_args", nds32_set_hint_func_args, 0}, /* Abandon?? */
+ {"omit_fp_begin", nds32_omit_fp_begin, 1},
+ {"omit_fp_end", nds32_omit_fp_begin, 0},
+ {"no_ex9_begin", nds32_no_ex9_begin, 1},
+ {"no_ex9_end", nds32_no_ex9_begin, 0},
+ {"vec_size", nds32_vec_size, 0},
+ {"flag", nds32_flag, 0},
+ {"innermost_loop_begin", nds32_loop_begin, 1},
+ {"innermost_loop_end", nds32_loop_begin, 0},
+ {"relax_hint", nds32_relax_hint, 0},
+ {NULL, NULL, 0}
+};
+
+void
+nds32_pre_do_align (int n, char *fill, int len, int max)
+{
+ /* Only make a frag if we HAVE to... */
+ fragS *fragP;
+ if (n != 0 && !need_pass_2)
+ {
+ if (fill == NULL)
+ {
+ if (subseg_text_p (now_seg))
+ {
+ fragP = frag_now;
+ frag_align_code (n, max);
+
+ /* Tag this alignment when there is a lable before it. */
+ if (label_exist)
+ {
+ fragP->tc_frag_data.flag = NDS32_FRAG_LABEL;
+ label_exist = 0;
+ }
+ }
+ else
+ frag_align (n, 0, max);
+ }
+ else if (len <= 1)
+ frag_align (n, *fill, max);
+ else
+ frag_align_pattern (n, fill, len, max);
+ }
+}
+
+void
+nds32_do_align (int n)
+{
+ /* Optimize for space and label exists. */
+ expressionS exp;
+
+ /* FIXME:I think this will break debug info sections and except_table. */
+ if (!enable_relax_relocs || !subseg_text_p (now_seg))
+ return;
+
+ /* Create and attach a BFD_RELOC_NDS32_LABEL fixup
+ the size of instruction may not be correct because
+ it could be relaxable. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = section_symbol (now_seg);
+ exp.X_add_number = n;
+ fix_new_exp (frag_now,
+ frag_now_fix (), 0, &exp, 0, BFD_RELOC_NDS32_LABEL);
+}
+
+/* Supported Andes machines. */
+struct nds32_machs
+{
+ enum bfd_architecture bfd_mach;
+ int mach_flags;
+};
+
+/* This is the callback for nds32-asm.c to parse operands. */
+
+int
+nds32_asm_parse_operand (struct nds32_asm_desc *pdesc ATTRIBUTE_UNUSED,
+ struct nds32_asm_insn *pinsn,
+ char **pstr, int64_t *value)
+{
+ char *hold;
+ expressionS *pexp = pinsn->info;
+
+ hold = input_line_pointer;
+ input_line_pointer = *pstr;
+ expression (pexp);
+ *pstr = input_line_pointer;
+ input_line_pointer = hold;
+
+ switch (pexp->X_op)
+ {
+ case O_symbol:
+ *value = 0;
+ return NASM_R_SYMBOL;
+ case O_constant:
+ *value = pexp->X_add_number;
+ return NASM_R_CONST;
+ case O_illegal:
+ case O_absent:
+ case O_register:
+ default:
+ return NASM_R_ILLEGAL;
+ }
+}
+
+/* GAS will call this function at the start of the assembly, after the command
+ line arguments have been parsed and all the machine independent
+ initializations have been completed. */
+
+void
+md_begin (void)
+{
+ struct nds32_keyword *k;
+ relax_info_t *relax_info;
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, nds32_baseline);
+
+ nds32_init_nds32_pseudo_opcodes ();
+ asm_desc.parse_operand = nds32_asm_parse_operand;
+ nds32_asm_init (&asm_desc, 0);
+
+ /* Initial general pupose registers hash table. */
+ nds32_gprs_hash = hash_new ();
+ for (k = keyword_gpr; k->name; k++)
+ hash_insert (nds32_gprs_hash, k->name, k);
+
+ /* Initial branch hash table. */
+ nds32_relax_info_hash = hash_new ();
+ for (relax_info = relax_table; relax_info->opcode; relax_info++)
+ hash_insert (nds32_relax_info_hash, relax_info->opcode, relax_info);
+
+ /* Initial relax hint hash table. */
+ nds32_hint_hash = hash_new ();
+ enable_16bit = nds32_16bit_ext;
+}
+
+/* HANDLE_ALIGN in write.c. */
+
+void
+nds32_handle_align (fragS *fragp)
+{
+ static const unsigned char nop16[] = { 0x92, 0x00 };
+ static const unsigned char nop32[] = { 0x40, 0x00, 0x00, 0x09 };
+ int bytes;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+
+ if (bytes & 1)
+ {
+ *p++ = 0;
+ bytes--;
+ }
+
+ if (bytes & 2)
+ {
+ expressionS exp_t;
+ exp_t.X_op = O_symbol;
+ exp_t.X_add_symbol = abs_section_sym;
+ exp_t.X_add_number = R_NDS32_INSN16_CONVERT_FLAG;
+ fix_new_exp (fragp, fragp->fr_fix, 2, &exp_t, 0,
+ BFD_RELOC_NDS32_INSN16);
+ memcpy (p, nop16, 2);
+ p += 2;
+ bytes -= 2;
+ }
+
+ while (bytes >= 4)
+ {
+ memcpy (p, nop32, 4);
+ p += 4;
+ bytes -= 4;
+ }
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ fragp->fr_fix += bytes;
+}
+
+/* md_flush_pending_output */
+
+void
+nds32_flush_pending_output (void)
+{
+ nds32_last_label = NULL;
+}
+
+void
+nds32_frob_label (symbolS *label)
+{
+ dwarf2_emit_label (label);
+}
+
+/* TC_START_LABEL */
+
+int
+nds32_start_label (int asmdone ATTRIBUTE_UNUSED, int secdone ATTRIBUTE_UNUSED)
+{
+ if (optimize && subseg_text_p (now_seg))
+ label_exist = 1;
+ return 1;
+}
+
+/* TARGET_FORMAT */
+
+const char *
+nds32_target_format (void)
+{
+#ifdef TE_LINUX
+ if (target_big_endian)
+ return "elf32-nds32be-linux";
+ else
+ return "elf32-nds32le-linux";
+#else
+ if (target_big_endian)
+ return "elf32-nds32be";
+ else
+ return "elf32-nds32le";
+#endif
+}
+
+static enum nds32_br_range
+get_range_type (const struct nds32_field *field)
+{
+ gas_assert (field != NULL);
+
+ if (field->bitpos != 0)
+ return BR_RANGE_U4G;
+
+ if (field->bitsize == 24 && field->shift == 1)
+ return BR_RANGE_S16M;
+ else if (field->bitsize == 16 && field->shift == 1)
+ return BR_RANGE_S64K;
+ else if (field->bitsize == 14 && field->shift == 1)
+ return BR_RANGE_S16K;
+ else if (field->bitsize == 8 && field->shift == 1)
+ return BR_RANGE_S256;
+ else
+ return BR_RANGE_U4G;
+}
+
+/* Save pseudo instruction relocation list. */
+
+static struct nds32_relocs_pattern*
+nds32_elf_save_pseudo_pattern (fixS* fixP, struct nds32_opcode *opcode,
+ char *out, symbolS *sym,
+ struct nds32_relocs_pattern *reloc_ptr,
+ fragS *fragP)
+{
+ if (!reloc_ptr)
+ reloc_ptr = malloc (sizeof (struct nds32_relocs_pattern));
+ reloc_ptr->seg = now_seg;
+ reloc_ptr->sym = sym;
+ reloc_ptr->frag = fragP;
+ reloc_ptr->frchain = frchain_now;
+ reloc_ptr->fixP = fixP;
+ reloc_ptr->opcode = opcode;
+ reloc_ptr->where = out;
+ reloc_ptr->next = NULL;
+ return reloc_ptr;
+}
+
+/* Check X_md to transform relocation. */
+
+static fixS*
+nds32_elf_record_fixup_exp (fragS *fragP, char *str,
+ const struct nds32_field *fld,
+ expressionS *pexp, char* out,
+ struct nds32_asm_insn *insn)
+{
+ int reloc = -1;
+ expressionS exp;
+ fixS *fixP = NULL;
+
+ /* Handle instruction relocation. */
+ if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_HI20))
+ {
+ /* Relocation for hi20 modifier. */
+ switch (pexp->X_md)
+ {
+ case BFD_RELOC_NDS32_GOTOFF: /* @GOTOFF */
+ reloc = BFD_RELOC_NDS32_GOTOFF_HI20;
+ break;
+ case BFD_RELOC_NDS32_GOT20: /* @GOT */
+ reloc = BFD_RELOC_NDS32_GOT_HI20;
+ break;
+ case BFD_RELOC_NDS32_25_PLTREL: /* @PLT */
+ if (!nds32_pic)
+ as_bad (_("Invalid PIC expression."));
+ else
+ reloc = BFD_RELOC_NDS32_PLT_GOTREL_HI20;
+ break;
+ case BFD_RELOC_NDS32_GOTPC20: /* _GLOBAL_OFFSET_TABLE_ */
+ reloc = BFD_RELOC_NDS32_GOTPC_HI20;
+ break;
+ case BFD_RELOC_NDS32_TPOFF: /* @TPOFF */
+ reloc = BFD_RELOC_NDS32_TLS_LE_HI20;
+ break;
+ case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */
+ reloc = BFD_RELOC_NDS32_TLS_IE_HI20;
+ break;
+ default: /* No suffix. */
+ reloc = BFD_RELOC_NDS32_HI20;
+ break;
+ }
+ fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize,
+ insn->info, 0 /* pcrel */, reloc);
+ }
+ else if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_LO12))
+ {
+ /* Relocation for lo12 modifier. */
+ if (fld->bitsize == 15 && fld->shift == 0)
+ {
+ /* [ls]bi || ori */
+ switch (pexp->X_md)
+ {
+ case BFD_RELOC_NDS32_GOTOFF: /* @GOTOFF */
+ reloc = BFD_RELOC_NDS32_GOTOFF_LO12;
+ break;
+ case BFD_RELOC_NDS32_GOT20: /* @GOT */
+ reloc = BFD_RELOC_NDS32_GOT_LO12;
+ break;
+ case BFD_RELOC_NDS32_25_PLTREL: /* @PLT */
+ if (!nds32_pic)
+ as_bad (_("Invalid PIC expression."));
+ else
+ reloc = BFD_RELOC_NDS32_PLT_GOTREL_LO12;
+ break;
+ case BFD_RELOC_NDS32_GOTPC20: /* _GLOBAL_OFFSET_TABLE_ */
+ reloc = BFD_RELOC_NDS32_GOTPC_LO12;
+ break;
+ case BFD_RELOC_NDS32_TPOFF: /* @TPOFF */
+ reloc = BFD_RELOC_NDS32_TLS_LE_LO12;
+ break;
+ default: /* No suffix. */
+ reloc = BFD_RELOC_NDS32_LO12S0;
+ break;
+ }
+ }
+ else if (fld->bitsize == 15 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_LO12S1; /* [ls]hi */
+ else if (fld->bitsize == 15 && fld->shift == 2)
+ {
+ /* [ls]wi */
+ switch (pexp->X_md)
+ {
+ case BFD_RELOC_NDS32_GOTTPOFF: /* @GOTTPOFF */
+ reloc = BFD_RELOC_NDS32_TLS_IE_LO12S2;
+ break;
+ default: /* No suffix. */
+ reloc = BFD_RELOC_NDS32_LO12S2;
+ break;
+ }
+ }
+ else if (fld->bitsize == 15 && fld->shift == 3)
+ reloc = BFD_RELOC_NDS32_LO12S3; /* [ls]di */
+ else if (fld->bitsize == 12 && fld->shift == 2)
+ reloc = R_NDS32_LO12S2_SP_RELA; /* f[ls][sd]i */
+
+ fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize,
+ insn->info, 0 /* pcrel */, reloc);
+ }
+ else if (fld && fld->bitpos == 0 && insn->opcode->isize == 4
+ && (insn->attr & NASM_ATTR_PCREL))
+ {
+ /* Relocation for 32-bit branch instructions. */
+ if (fld->bitsize == 24 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_25_PCREL;
+ else if (fld->bitsize == 16 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_17_PCREL;
+ else if (fld->bitsize == 14 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_15_PCREL;
+ else if (fld->bitsize == 8 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_WORD_9_PCREL;
+ else
+ abort ();
+
+ fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize,
+ insn->info, 1 /* pcrel */, reloc);
+ }
+ else if (fld && fld->bitpos == 0 && insn->opcode->isize == 4
+ && (insn->attr & NASM_ATTR_GPREL))
+ {
+ /* Relocation for 32-bit gp-relative instructions. */
+ if (fld->bitsize == 19 && fld->shift == 0)
+ reloc = BFD_RELOC_NDS32_SDA19S0;
+ else if (fld->bitsize == 18 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_SDA18S1;
+ else if (fld->bitsize == 17 && fld->shift == 2)
+ reloc = BFD_RELOC_NDS32_SDA17S2;
+ else
+ abort ();
+
+ fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize,
+ insn->info, 0 /* pcrel */, reloc);
+ /* Insert INSN16 for converting fp_as_gp. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ if (in_omit_fp && reloc == BFD_RELOC_NDS32_SDA17S2)
+ fix_new_exp (fragP, out - fragP->fr_literal,
+ insn->opcode->isize, &exp, 0 /* pcrel */,
+ BFD_RELOC_NDS32_INSN16);
+ }
+ else if (fld && fld->bitpos == 0 && insn->opcode->isize == 2
+ && (insn->attr & NASM_ATTR_PCREL))
+ {
+ /* Relocation for 16-bit branch instructions. */
+ if (fld->bitsize == 8 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_9_PCREL;
+ else
+ abort ();
+
+ fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize,
+ insn->info, 1 /* pcrel */, reloc);
+ }
+ else if (fld && fld->bitpos == 0 && (insn->attr & NASM_ATTR_IFC_EXT))
+ {
+ /* Relocation for ifcall instruction. */
+ if (insn->opcode->isize == 2 && fld->bitsize == 9 && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_10IFCU_PCREL;
+ else if (insn->opcode->isize == 4 && fld->bitsize == 16
+ && fld->shift == 1)
+ reloc = BFD_RELOC_NDS32_17IFC_PCREL;
+ else
+ abort ();
+
+ fixP = fix_new_exp (fragP, out - fragP->fr_literal, insn->opcode->isize,
+ insn->info, 1 /* pcrel */, reloc);
+ }
+ else if (fld)
+ as_bad (_("Don't know how to handle this field. %s"), str);
+
+ return fixP;
+}
+
+/* Build instruction pattern to relax. There are two type group pattern
+ including pseudo instruction and relax hint. */
+
+static void
+nds32_elf_build_relax_relation (fixS *fixP, expressionS *pexp, char* out,
+ struct nds32_opcode *opcode, fragS *fragP,
+ const struct nds32_field *fld)
+{
+ struct nds32_relocs_pattern *reloc_ptr;
+ struct nds32_relocs_group *group;
+ symbolS *sym = NULL;
+
+ /* The expression may be used uninitialized. */
+ if (fld)
+ sym = pexp->X_add_symbol;
+
+ if (pseudo_opcode)
+ {
+ /* Save instruction relation for pseudo instruction expanding pattern. */
+ reloc_ptr = nds32_elf_save_pseudo_pattern (fixP, opcode, out, sym,
+ NULL, fragP);
+ if (!relocs_list)
+ relocs_list = reloc_ptr;
+ else
+ {
+ struct nds32_relocs_pattern *temp = relocs_list;
+ while (temp->next)
+ temp = temp->next;
+ temp->next = reloc_ptr;
+ }
+ }
+ else if (nds32_relax_hint_current)
+ {
+ /* Save instruction relation by relax hint. */
+ group = nds32_relax_hint_current;
+ while (group)
+ {
+ nds32_elf_save_pseudo_pattern (fixP, opcode, out, sym,
+ group->pattern, fragP);
+ group = group->next;
+ free (nds32_relax_hint_current);
+ nds32_relax_hint_current = group;
+ }
+ }
+
+ /* Set relaxing false only for relax_hint trigger it. */
+ if (!pseudo_opcode)
+ relaxing = FALSE;
+}
+
+#define N32_MEM_EXT(insn) ((N32_OP6_MEM << 25) | insn)
+
+/* Relax pattern for link time relaxation. */
+
+static struct nds32_relax_hint_table relax_ls_table[] =
+{
+ {
+ /* Set address: la -> sethi ori. */
+ NDS32_RELAX_HINT_LA, /* main_type */
+ 8, /* relax_code_size */
+ {
+ OP6 (SETHI),
+ OP6 (ORI),
+ }, /* relax_code_seq */
+ {
+ {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE},
+ {4, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_INSN16}
+ } /* relax_fixup */
+ },
+ {
+ /* Set address: l.w -> sethi ori. */
+ NDS32_RELAX_HINT_LS, /* main_type */
+ 8, /* relax_code_size */
+ {
+ OP6 (SETHI),
+ OP6 (LBI),
+ }, /* relax_code_seq */
+ {
+ {0, 4, NDS32_HINT | NDS32_ADDEND, BFD_RELOC_NDS32_LOADSTORE},
+ {4, 4, NDS32_HINT | NDS32_ABS, BFD_RELOC_NDS32_INSN16}
+ } /* relax_fixup */
+ },
+ {
+ 0,
+ 0,
+ {0},
+ {{0, 0 , 0, 0}}
+ }
+};
+
+/* Since sethi loadstore relocation has to using next instruction to determine
+ elimination itself or not, we have to return the next instruction range. */
+
+static int
+nds32_elf_sethi_range (struct nds32_relocs_pattern *pattern)
+{
+ int range = 0;
+ while (pattern)
+ {
+ switch (pattern->opcode->value)
+ {
+ case INSN_LBI:
+ case INSN_SBI:
+ case INSN_LBSI:
+ case N32_MEM_EXT (N32_MEM_LB):
+ case N32_MEM_EXT (N32_MEM_LBS):
+ case N32_MEM_EXT (N32_MEM_SB):
+ range = NDS32_LOADSTORE_BYTE;
+ break;
+ case INSN_LHI:
+ case INSN_SHI:
+ case INSN_LHSI:
+ case N32_MEM_EXT (N32_MEM_LH):
+ case N32_MEM_EXT (N32_MEM_LHS):
+ case N32_MEM_EXT (N32_MEM_SH):
+ range = NDS32_LOADSTORE_HALF;
+ break;
+ case INSN_LWI:
+ case INSN_SWI:
+ case N32_MEM_EXT (N32_MEM_LW):
+ case N32_MEM_EXT (N32_MEM_SW):
+ range = NDS32_LOADSTORE_WORD;
+ break;
+ case INSN_FLSI:
+ case INSN_FSSI:
+ range = NDS32_LOADSTORE_FLOAT_S;
+ break;
+ case INSN_FLDI:
+ case INSN_FSDI:
+ range = NDS32_LOADSTORE_FLOAT_D;
+ break;
+ case INSN_ORI:
+ range = NDS32_LOADSTORE_IMM;
+ break;
+ default:
+ range = NDS32_LOADSTORE_NONE;
+ break;
+ }
+ if (range != NDS32_LOADSTORE_NONE)
+ break;
+ pattern = pattern->next;
+ }
+ return range;
+}
+
+/* The args means: instruction size, the 1st instruction is converted to 16 or
+ not, optimize option, 16 bit instruction is enable. */
+#define SET_ADDEND(size, convertible, optimize, insn16_on) \
+ (((size) & 0xff) | ((convertible) ? 1 << 31 : 0) \
+ | ((optimize) ? 1<< 30 : 0) | (insn16_on ? 1 << 29 : 0))
+
+static void
+nds32_set_elf_flags_by_insn (struct nds32_asm_insn * insn)
+{
+ /* Set E_NDS32_HAS_EXT_INST. */
+ if (insn->opcode->attr & NASM_ATTR_PERF_EXT)
+ {
+ if (nds32_perf_ext)
+ nds32_elf_flags |= E_NDS32_HAS_EXT_INST;
+ else
+ as_bad (_("instruction %s requires enabling performance extension"),
+ insn->opcode->opcode);
+ }
+ else if (insn->opcode->attr & NASM_ATTR_PERF2_EXT)
+ {
+ if (nds32_perf_ext2)
+ nds32_elf_flags |= E_NDS32_HAS_EXT2_INST;
+ else
+ as_bad (_("instruction %s requires enabling performance extension II"),
+ insn->opcode->opcode);
+ }
+ else if (insn->opcode->attr & NASM_ATTR_AUDIO_ISAEXT)
+ {
+ if (nds32_audio_ext)
+ nds32_elf_flags |= E_NDS32_HAS_AUDIO_INST;
+ else
+ as_bad (_("instruction %s requires enabling AUDIO extension"),
+ insn->opcode->opcode);
+ }
+ else if (insn->opcode->attr & NASM_ATTR_STR_EXT)
+ {
+ if (nds32_string_ext)
+ nds32_elf_flags |= E_NDS32_HAS_STRING_INST;
+ else
+ as_bad (_("instruction %s requires enabling STRING extension"),
+ insn->opcode->opcode);
+ }
+ else if ((insn->opcode->attr & NASM_ATTR_DIV)
+ && (insn->opcode->attr & NASM_ATTR_DXREG))
+ {
+ if (nds32_div && nds32_dx_regs)
+ nds32_elf_flags |= E_NDS32_HAS_DIV_DX_INST;
+ else
+ as_bad (_("instruction %s requires enabling DIV & DX_REGS extension"),
+ insn->opcode->opcode);
+ }
+ else if (insn->opcode->attr & NASM_ATTR_FPU)
+ {
+ if (nds32_fpu_sp_ext || nds32_fpu_dp_ext)
+ {
+ if (!(nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST)))
+ nds32_fpu_com = 1;
+ }
+ else
+ as_bad (_("instruction %s requires enabling FPU extension"),
+ insn->opcode->opcode);
+ }
+ else if (insn->opcode->attr & NASM_ATTR_FPU_SP_EXT)
+ {
+ if (nds32_fpu_sp_ext)
+ nds32_elf_flags |= E_NDS32_HAS_FPU_INST;
+ else
+ as_bad (_("instruction %s requires enabling FPU_SP extension"),
+ insn->opcode->opcode);
+ }
+ else if ((insn->opcode->attr & NASM_ATTR_FPU_SP_EXT)
+ && (insn->opcode->attr & NASM_ATTR_MAC))
+ {
+ if (nds32_fpu_sp_ext && nds32_mac)
+ {
+ nds32_elf_flags |= E_NDS32_HAS_FPU_MAC_INST;
+ nds32_elf_flags |= E_NDS32_HAS_FPU_INST;
+ }
+ else
+ as_bad (_("instruction %s requires enabling FPU_MAC extension"),
+ insn->opcode->opcode);
+ }
+ else if (insn->opcode->attr & NASM_ATTR_FPU_DP_EXT)
+ {
+ if (nds32_fpu_dp_ext)
+ nds32_elf_flags |= E_NDS32_HAS_FPU_DP_INST;
+ else
+ as_bad (_("instruction %s requires enabling FPU_DP extension"),
+ insn->opcode->opcode);
+ }
+ else if ((insn->opcode->attr & NASM_ATTR_FPU_DP_EXT)
+ && (insn->opcode->attr & NASM_ATTR_MAC))
+ {
+ if (nds32_fpu_dp_ext && nds32_mac)
+ {
+ nds32_elf_flags |= E_NDS32_HAS_FPU_MAC_INST;
+ nds32_elf_flags |= E_NDS32_HAS_FPU_DP_INST;
+ }
+ else
+ as_bad (_("instruction %s requires enabling FPU_MAC extension"),
+ insn->opcode->opcode);
+ }
+ /* TODO: FPU_BOTH */
+ else if ((insn->opcode->attr & NASM_ATTR_MAC)
+ && (insn->opcode->attr & NASM_ATTR_DXREG))
+ {
+ if (nds32_mac && nds32_dx_regs)
+ nds32_elf_flags |= E_NDS32_HAS_MAC_DX_INST;
+ else
+ as_bad (_("instruction %s requires enabling DX_REGS extension"),
+ insn->opcode->opcode);
+ }
+ /* TODO: for DX_REG set but not for MAC, DIV, AUDIO */
+ else if (insn->opcode->attr & NASM_ATTR_IFC_EXT)
+ {
+ nds32_elf_flags |= E_NDS32_HAS_IFC_INST;
+ }
+ /* TODO: E_NDS32_HAS_SATURATION_INST */
+}
+
+/* Flag for analysis relaxation type. */
+
+enum nds32_insn_type
+{
+ N32_RELAX_SETHI = 1,
+ N32_RELAX_BR = (1 << 1),
+ N32_RELAX_LSI = (1 << 2),
+ N32_RELAX_JUMP = (1 << 3),
+ N32_RELAX_CALL = (1 << 4),
+ N32_RELAX_ORI = (1 << 5),
+ N32_RELAX_MEM = (1 << 6),
+ N32_RELAX_MOVI = (1 << 7),
+};
+
+struct nds32_hint_map
+{
+ bfd_reloc_code_real_type hi_type;
+ char *opc;
+ enum nds32_relax_hint_type hint_type;
+ enum nds32_br_range range;
+ enum nds32_insn_type insn_list;
+};
+
+/* Table to match instructions with hint and relax pattern. */
+
+static struct nds32_hint_map hint_map [] =
+{
+ {
+ /* LONGCALL4. */
+ BFD_RELOC_NDS32_HI20,
+ "jal",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_U4G,
+ N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_CALL
+ },
+ {
+ /* LONGCALL5. */
+ _dummy_first_bfd_reloc_code_real,
+ "bgezal",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_S16M,
+ N32_RELAX_BR | N32_RELAX_CALL
+ },
+ {
+ /* LONGCALL6. */
+ BFD_RELOC_NDS32_HI20,
+ "bgezal",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_U4G,
+ N32_RELAX_BR | N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_CALL
+ },
+ {
+ /* LONGJUMP4. */
+ BFD_RELOC_NDS32_HI20,
+ "j",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_U4G,
+ N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_JUMP
+ },
+ {
+ /* LONGJUMP5. */
+ /* There is two kinds of veriation of LONGJUMP5. One of them
+ generate EMPTY relocation for converted INSN16 if needed.
+ But we don't distinguish them here. */
+ _dummy_first_bfd_reloc_code_real,
+ "beq",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_S16M,
+ N32_RELAX_BR | N32_RELAX_JUMP
+ },
+ {
+ /* LONGJUMP6. */
+ BFD_RELOC_NDS32_HI20,
+ "beq",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_U4G,
+ N32_RELAX_SETHI | N32_RELAX_ORI | N32_RELAX_BR | N32_RELAX_JUMP
+ },
+ {
+ /* LONGJUMP7. */
+ _dummy_first_bfd_reloc_code_real,
+ "beqc",
+ NDS32_RELAX_HINT_NONE,
+ BR_RANGE_S16K,
+ N32_RELAX_MOVI | N32_RELAX_BR
+ },
+ {
+ /* LOADSTORE ADDRESS. */
+ BFD_RELOC_NDS32_HI20,
+ NULL,
+ NDS32_RELAX_HINT_LA,
+ BR_RANGE_U4G,
+ N32_RELAX_SETHI | N32_RELAX_ORI
+ },
+ {
+ /* LOADSTORE ADDRESS. */
+ BFD_RELOC_NDS32_HI20,
+ NULL,
+ NDS32_RELAX_HINT_LS,
+ BR_RANGE_U4G,
+ N32_RELAX_SETHI | N32_RELAX_LSI
+ },
+ {0, NULL, 0, 0 ,0}
+};
+
+/* Find the relaxation pattern according to instructions. */
+
+static bfd_boolean
+nds32_find_reloc_table (struct nds32_relocs_pattern *relocs_pattern,
+ struct nds32_relax_hint_table *hint_info)
+{
+ unsigned int opcode, seq_size;
+ enum nds32_br_range range;
+ struct nds32_relocs_pattern *pattern, *hi_pattern = NULL;
+ char *opc = NULL;
+ relax_info_t *relax_info = NULL;
+ nds32_relax_fixup_info_t *fixup_info, *hint_fixup;
+ enum nds32_relax_hint_type hint_type = NDS32_RELAX_HINT_NONE;
+ struct nds32_relax_hint_table *table_ptr;
+ uint32_t *code_seq, *hint_code;
+ enum nds32_insn_type relax_type = 0;
+ struct nds32_hint_map *map_ptr = hint_map;
+ unsigned int i;
+ char *check_insn[] =
+ { "bnes38", "beqs38", "bnez38", "bnezs8", "beqz38", "beqzs8" };
+
+ /* TODO: PLT GOT. */
+ /* Traverse all pattern instruction and set flag. */
+ pattern = relocs_pattern;
+ while (pattern)
+ {
+ if (pattern->opcode->isize == 4)
+ {
+ /* 4 byte instruction. */
+ opcode = N32_OP6 (pattern->opcode->value);
+ switch (opcode)
+ {
+ case N32_OP6_SETHI:
+ hi_pattern = pattern;
+ relax_type |= N32_RELAX_SETHI;
+ break;
+ case N32_OP6_MEM:
+ relax_type |= N32_RELAX_MEM;
+ break;
+ case N32_OP6_ORI:
+ relax_type |= N32_RELAX_ORI;
+ break;
+ case N32_OP6_BR1:
+ case N32_OP6_BR2:
+ case N32_OP6_BR3:
+ relax_type |= N32_RELAX_BR;
+ break;
+ case N32_OP6_MOVI:
+ relax_type |= N32_RELAX_MOVI;
+ break;
+ case N32_OP6_LBI:
+ case N32_OP6_SBI:
+ case N32_OP6_LBSI:
+ case N32_OP6_LHI:
+ case N32_OP6_SHI:
+ case N32_OP6_LHSI:
+ case N32_OP6_LWI:
+ case N32_OP6_SWI:
+ case N32_OP6_LWC:
+ case N32_OP6_SWC:
+ relax_type |= N32_RELAX_LSI;
+ break;
+ case N32_OP6_JREG:
+ if (__GF (pattern->opcode->value, 0, 1) == 1)
+ relax_type |= N32_RELAX_CALL;
+ else
+ relax_type |= N32_RELAX_JUMP;
+ break;
+ case N32_OP6_JI:
+ if (__GF (pattern->opcode->value, 24, 1) == 1)
+ relax_type |= N32_RELAX_CALL;
+ else
+ relax_type |= N32_RELAX_JUMP;
+ break;
+ default:
+ as_warn (_("relax hint unrecognized instruction: line %d."),
+ pattern->frag->fr_line);
+ return FALSE;
+ }
+ }
+ else
+ {
+ /* 2 byte instruction. Compare by opcode name because the opcode of
+ 2byte instruction is not regular. */
+ for (i = 0; i < sizeof (check_insn) / sizeof (check_insn[0]); i++)
+ {
+ if (strcmp (pattern->opcode->opcode, check_insn[i]) == 0)
+ {
+ relax_type |= N32_RELAX_BR;
+ break;
+ }
+ }
+ if (strcmp (pattern->opcode->opcode, "movi55") == 0)
+ relax_type |= N32_RELAX_MOVI;
+ }
+ pattern = pattern->next;
+ }
+
+ /* Analysis instruction flag to choose relaxation table. */
+ while (map_ptr->insn_list != 0)
+ {
+ if (map_ptr->insn_list == relax_type
+ && (!hi_pattern
+ || (hi_pattern->fixP
+ && hi_pattern->fixP->fx_r_type == map_ptr->hi_type)))
+ {
+ opc = map_ptr->opc;
+ hint_type = map_ptr->hint_type;
+ range = map_ptr->range;
+ break;
+ }
+ map_ptr++;
+ }
+
+ if (map_ptr->insn_list == 0)
+ {
+ as_warn (_("Can not find match relax hint. line : %d"),
+ relocs_pattern->frag->fr_line);
+ return FALSE;
+ }
+
+ /* Get the match table. */
+ if (opc)
+ {
+ /* Branch relax pattern. */
+ relax_info = hash_find (nds32_relax_info_hash, opc);
+ if (!relax_info)
+ return FALSE;
+ fixup_info = relax_info->relax_fixup[range];
+ code_seq = relax_info->relax_code_seq[range];
+ seq_size = relax_info->relax_code_size[range];
+ }
+ else if (hint_type)
+ {
+ /* Load-store relax pattern. */
+ table_ptr = relax_ls_table;
+ while (table_ptr->main_type != 0)
+ {
+ if (table_ptr->main_type == hint_type)
+ {
+ fixup_info = table_ptr->relax_fixup;
+ code_seq = table_ptr->relax_code_seq;
+ seq_size = table_ptr->relax_code_size;
+ break;
+ }
+ table_ptr++;
+ }
+ if (table_ptr->main_type == 0)
+ return FALSE;
+ }
+ else
+ return FALSE;
+
+ hint_fixup = hint_info->relax_fixup;
+ hint_code = hint_info->relax_code_seq;
+ hint_info->relax_code_size = seq_size;
+
+ while (fixup_info->size != 0)
+ {
+ if (fixup_info->ramp & NDS32_HINT)
+ {
+ memcpy (hint_fixup, fixup_info, sizeof (nds32_relax_fixup_info_t));
+ hint_fixup++;
+ }
+ fixup_info++;
+ }
+ /* Clear final relocation. */
+ memset (hint_fixup, 0, sizeof (nds32_relax_fixup_info_t));
+ /* Copy code sequance. */
+ memcpy (hint_code, code_seq, seq_size);
+ return TRUE;
+}
+
+/* Because there are a lot of variant of load-store, check
+ all these type here. */
+
+#define CLEAN_REG(insn) ((insn) & 0xff0003ff)
+static bfd_boolean
+nds32_match_hint_insn (struct nds32_opcode *opcode, uint32_t seq)
+{
+ char *check_insn[] =
+ { "bnes38", "beqs38", "bnez38", "bnezs8", "beqz38", "beqzs8" };
+ uint32_t insn = opcode->value;
+ unsigned int i;
+
+ insn = CLEAN_REG (opcode->value);
+ if (insn == seq)
+ return TRUE;
+
+ switch (seq)
+ {
+ case OP6 (LBI):
+ /* In relocation_table, it regards instruction LBI as representation
+ of all the NDS32_RELAX_HINT_LS pattern. */
+ if (insn == OP6 (LBI) || insn == OP6 (SBI) || insn == OP6 (LBSI)
+ || insn == OP6 (LHI) || insn == OP6 (SHI) || insn == OP6 (LHSI)
+ || insn == OP6 (LWI) || insn == OP6 (SWI)
+ || insn == OP6 (LWC) || insn == OP6 (SWC))
+ return TRUE;
+ break;
+ case OP6 (BR2):
+ /* This is for LONGCALL5 and LONGCALL6. */
+ if (insn == OP6 (BR2))
+ return TRUE;
+ break;
+ case OP6 (BR1):
+ /* This is for LONGJUMP5 and LONGJUMP6. */
+ if (opcode->isize == 4
+ && (insn == OP6 (BR1) || insn == OP6 (BR2) || insn == OP6 (BR3)))
+ return TRUE;
+ else if (opcode->isize == 2)
+ {
+ for (i = 0; i < sizeof (check_insn) / sizeof (check_insn[0]); i++)
+ if (strcmp (opcode->opcode, check_insn[i]) == 0)
+ return TRUE;
+ }
+ break;
+ case OP6 (MOVI):
+ /* This is for LONGJUMP7. */
+ if (opcode->isize == 2 && strcmp (opcode->opcode, "movi55") == 0)
+ return TRUE;
+ break;
+ }
+ return FALSE;
+}
+
+/* Append relax relocation for link time relaxing. */
+
+static void
+nds32_elf_append_relax_relocs (const char *key ATTRIBUTE_UNUSED, void *value)
+{
+ struct nds32_relocs_pattern *relocs_pattern =
+ (struct nds32_relocs_pattern *) value;
+ struct nds32_relocs_pattern *pattern_temp, *pattern_now;
+ symbolS *sym, *hi_sym = NULL;
+ expressionS exp;
+ fragS *fragP;
+ segT seg_bak = now_seg;
+ frchainS *frchain_bak = frchain_now;
+ struct nds32_relax_hint_table hint_info;
+ nds32_relax_fixup_info_t *hint_fixup, *fixup_now;
+ size_t fixup_size;
+ offsetT branch_offset;
+ fixS *fixP;
+ int range, offset;
+ unsigned int ptr_offset, hint_count, relax_code_size, count = 0;
+ uint32_t *code_seq, code_insn;
+ char *where;
+
+ if (!relocs_pattern)
+ return;
+
+ if (!nds32_find_reloc_table (relocs_pattern, &hint_info))
+ return;
+
+ /* Save symbol for some EMPTY relocation using. */
+ pattern_now = relocs_pattern;
+ while (pattern_now)
+ {
+ if (pattern_now->opcode->value == OP6 (SETHI))
+ {
+ hi_sym = pattern_now->sym;
+ break;
+ }
+ pattern_now = pattern_now->next;
+ }
+
+ /* Inserting fix up must specify now_seg or frchain_now. */
+ now_seg = relocs_pattern->seg;
+ frchain_now = relocs_pattern->frchain;
+ fragP = relocs_pattern->frag;
+ branch_offset = fragP->fr_offset;
+
+ hint_fixup = hint_info.relax_fixup;
+ code_seq = hint_info.relax_code_seq;
+ relax_code_size = hint_info.relax_code_size;
+ pattern_now = relocs_pattern;
+
+ /* Insert relaxation. */
+ exp.X_op = O_symbol;
+
+ while (pattern_now)
+ {
+ /* Choose the match fixup by instruction. */
+ code_insn = CLEAN_REG (*(code_seq + count));
+ if (!nds32_match_hint_insn (pattern_now->opcode, code_insn))
+ {
+ count = 0;
+ code_insn = CLEAN_REG (*(code_seq + count));
+
+ while (!nds32_match_hint_insn (pattern_now->opcode, code_insn))
+ {
+ count++;
+ if (count >= relax_code_size / 4)
+ {
+ as_bad (_("Internal error: Relax hint error. %s: %x"),
+ now_seg->name, pattern_now->opcode->value);
+ goto restore;
+ }
+ code_insn = CLEAN_REG (*(code_seq + count));
+ }
+ }
+ fragP = pattern_now->frag;
+ sym = pattern_now->sym;
+ branch_offset = fragP->fr_offset;
+ offset = count * 4;
+ where = pattern_now->where;
+ /* Find the instruction map fix. */
+ fixup_now = hint_fixup;
+ while (fixup_now->offset != offset)
+ {
+ fixup_now++;
+ if (fixup_now->size == 0)
+ break;
+ }
+ /* This element is without relaxation relocation. */
+ if (fixup_now->size == 0)
+ {
+ pattern_now = pattern_now->next;
+ continue;
+ }
+ fixup_size = fixup_now->size;
+
+ /* Insert all fixup. */
+ while (fixup_size != 0 && fixup_now->offset == offset)
+ {
+ /* Set the real instruction size in element. */
+ fixup_size = pattern_now->opcode->isize;
+ if (fixup_now->ramp & NDS32_FIX)
+ {
+ /* Convert original relocation. */
+ pattern_now->fixP->fx_r_type = fixup_now->r_type ;
+ fixup_size = 0;
+ }
+ else if ((fixup_now->ramp & NDS32_PTR) != 0)
+ {
+ /* This relocation has to point to another instruction. Make
+ sure each resolved relocation has to be pointed. */
+ pattern_temp = relocs_pattern;
+ /* All instruction in relax_table should be 32-bit. */
+ hint_count = hint_info.relax_code_size / 4;
+ code_insn = CLEAN_REG (*(code_seq + hint_count - 1));
+ while (pattern_temp)
+ {
+ /* Point to every resolved relocation. */
+ if (nds32_match_hint_insn (pattern_temp->opcode, code_insn))
+ {
+ ptr_offset =
+ pattern_temp->where - pattern_temp->frag->fr_literal;
+ exp.X_add_symbol = symbol_temp_new (now_seg, ptr_offset,
+ pattern_temp->frag);
+ exp.X_add_number = 0;
+ fixP =
+ fix_new_exp (fragP, where - fragP->fr_literal,
+ fixup_size, &exp, 0, fixup_now->r_type);
+ fixP->fx_addnumber = fixP->fx_offset;
+ }
+ pattern_temp = pattern_temp->next;
+ }
+ fixup_size = 0;
+ }
+ else if (fixup_now->ramp & NDS32_ADDEND)
+ {
+ range = nds32_elf_sethi_range (relocs_pattern);
+ if (range == NDS32_LOADSTORE_NONE)
+ {
+ as_bad (_("Internal error: Range error. %s"), now_seg->name);
+ return;
+ }
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = SET_ADDEND (4, 0, optimize, enable_16bit);
+ exp.X_add_number |= ((range & 0x3f) << 8);
+ }
+ else if ((fixup_now->ramp & NDS32_ABS) != 0)
+ {
+ /* This is a tag relocation. */
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ }
+ else if ((fixup_now->ramp & NDS32_INSN16) != 0)
+ {
+ if (!enable_16bit)
+ fixup_size = 0;
+ /* This is a tag relocation. */
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ }
+ else if ((fixup_now->ramp & NDS32_SYM) != 0)
+ {
+ /* For EMPTY relocation save the true symbol. */
+ exp.X_add_symbol = hi_sym;
+ exp.X_add_number = branch_offset;
+ }
+ else
+ {
+ exp.X_add_symbol = sym;
+ exp.X_add_number = branch_offset;
+ }
+
+ if (fixup_size != 0)
+ {
+ fixP = fix_new_exp (fragP, where - fragP->fr_literal,
+ fixup_size, &exp, 0, fixup_now->r_type);
+ fixP->fx_addnumber = fixP->fx_offset;
+ }
+ fixup_now++;
+ fixup_size = fixup_now->size;
+ }
+ if (count < relax_code_size / 4)
+ count++;
+ pattern_now = pattern_now->next;
+ }
+
+restore:
+ now_seg = seg_bak;
+ frchain_now = frchain_bak;
+}
+
+/* Check instruction if it can be used for the baseline. */
+
+static bfd_boolean
+nds32_check_insn_available (struct nds32_asm_insn insn, char *str)
+{
+ int attr = insn.attr & ATTR_ALL;
+ static int baseline_isa = 0;
+ /* No isa setting or all isa can use. */
+ if (attr == 0 || attr == ATTR_ALL)
+ return TRUE;
+
+ if (baseline_isa == 0)
+ {
+ /* Map option baseline and instruction attribute. */
+ switch (nds32_baseline)
+ {
+ case ISA_V2:
+ baseline_isa = ATTR (ISA_V2);
+ break;
+ case ISA_V3:
+ baseline_isa = ATTR (ISA_V3);
+ break;
+ case ISA_V3M:
+ baseline_isa = ATTR (ISA_V3M);
+ break;
+ }
+ }
+
+ if ((baseline_isa & attr) == 0)
+ {
+ as_bad (_("Not support instrcution %s in the baseline."), str);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Stub of machine dependent. */
+
+void
+md_assemble (char *str)
+{
+ struct nds32_asm_insn insn;
+ char *out;
+ struct nds32_pseudo_opcode *popcode;
+ const struct nds32_field *fld = NULL;
+ fixS *fixP;
+ uint16_t insn_16;
+ struct nds32_relocs_pattern *relocs_temp;
+ expressionS *pexp;
+ fragS *fragP;
+ int label = label_exist;
+
+ popcode = nds32_lookup_pseudo_opcode (str);
+ /* Note that we need to check 'verbatim' and
+ 'opcode->physical_op'. If the assembly content is generated by
+ compiler and this opcode is a physical instruction, there is no
+ need to perform pseudo instruction expansion/transformation. */
+ if (popcode && !(verbatim && popcode->physical_op))
+ {
+ pseudo_opcode = TRUE;
+ nds32_pseudo_opcode_wrapper (str, popcode);
+ pseudo_opcode = FALSE;
+ nds32_elf_append_relax_relocs (NULL, relocs_list);
+
+ /* Free pseudo list. */
+ relocs_temp = relocs_list;
+ while (relocs_temp)
+ {
+ relocs_list = relocs_list->next;
+ free (relocs_temp);
+ relocs_temp = relocs_list;
+ }
+
+ return;
+ }
+
+ label_exist = 0;
+ insn.info = (expressionS *) alloca (sizeof (expressionS));
+ nds32_assemble (&asm_desc, &insn, str);
+
+ switch (asm_desc.result)
+ {
+ case NASM_ERR_UNKNOWN_OP:
+ as_bad (_("Unrecognized opcode, %s."), str);
+ return;
+ case NASM_ERR_SYNTAX:
+ as_bad (_("Incorrect syntax, %s."), str);
+ return;
+ case NASM_ERR_OPERAND:
+ as_bad (_("Unrecognized operand, %s."), str);
+ return;
+ case NASM_ERR_OUT_OF_RANGE:
+ as_bad (_("Operand out of range, %s."), str);
+ return;
+ case NASM_ERR_REG_REDUCED:
+ as_bad (_("Prohibited register used for reduced-register, %s."), str);
+ return;
+ case NASM_ERR_JUNK_EOL:
+ as_bad (_("Junk at end of line, %s."), str);
+ return;
+ }
+
+ gas_assert (insn.opcode);
+
+ nds32_set_elf_flags_by_insn (&insn);
+
+ gas_assert (insn.opcode->isize == 4 || insn.opcode->isize == 2);
+
+ if (!nds32_check_insn_available (insn, str))
+ return;
+
+ /* Make sure the begining of text being 2-byte align. */
+ nds32_adjust_label (1);
+ fld = insn.field;
+ /* Try to allocate the max size to guarantee relaxable same branch
+ instructions in the same fragment. */
+ frag_grow (NDS32_MAXCHAR);
+ fragP = frag_now;
+ if (fld && (insn.attr & NASM_ATTR_BRANCH)
+ && (pseudo_opcode || (insn.opcode->value != INSN_JAL
+ && insn.opcode->value != INSN_J))
+ && (!verbatim || pseudo_opcode))
+ {
+ /* User assembly code branch relax for it. */
+ /* If fld is not NULL, it is a symbol. */
+ /* Branch msut relax to proper pattern in user assembly code exclude
+ J and JAL. Keep these two in original type for users which wants
+ to keep their size be fixed. In general, assembler does not convert
+ instruction generated by compiler. But jump instruction may be
+ truncated in text virtual model. For workaround, compiler generate
+ pseudo jump to fix this issue currently. */
+
+ /* Get branch range type. */
+ dwarf2_emit_insn (0);
+ enum nds32_br_range range_type;
+
+ pexp = insn.info;
+ range_type = get_range_type (fld);
+
+ out = frag_var (rs_machine_dependent, NDS32_MAXCHAR,
+ 0, /* VAR is un-used. */
+ range_type, /* SUBTYPE is used as range type. */
+ pexp->X_add_symbol, pexp->X_add_number, 0);
+
+ fragP->fr_fix += insn.opcode->isize;
+ fragP->tc_frag_data.opcode = insn.opcode;
+ fragP->tc_frag_data.insn = insn.insn;
+ if (insn.opcode->isize == 4)
+ bfd_putb32 (insn.insn, out);
+ else if (insn.opcode->isize == 2)
+ bfd_putb16 (insn.insn, out);
+ fragP->tc_frag_data.flag |= NDS32_FRAG_BRANCH;
+ return;
+ /* md_convert_frag will insert relocations. */
+ }
+ else if (!fld && !relaxing && enable_16bit && (optimize || optimize_for_space)
+ && ((!verbatim && insn.opcode->isize == 4
+ && nds32_convert_32_to_16 (stdoutput, insn.insn, &insn_16, NULL))
+ || (insn.opcode->isize == 2
+ && nds32_convert_16_to_32 (stdoutput, insn.insn, NULL))))
+ {
+ /* Record this one is relaxable. */
+ dwarf2_emit_insn (0);
+ out = frag_var (rs_machine_dependent,
+ 4, /* Max size is 32-bit instruction. */
+ 0, /* VAR is un-used. */
+ 0, NULL, 0, NULL);
+ fragP->tc_frag_data.flag |= NDS32_FRAG_RELAXABLE;
+ fragP->tc_frag_data.opcode = insn.opcode;
+ fragP->tc_frag_data.insn = insn.insn;
+ fragP->fr_fix += 2;
+
+ /* In original, we don't relax the instrucion with label on it,
+ but this may cause some redundant nop16. Therefore, tag this
+ relaxable instruction and relax it carefully. */
+ if (label)
+ fragP->tc_frag_data.flag |= NDS32_FRAG_LABEL;
+
+ if (insn.opcode->isize == 4)
+ bfd_putb16 (insn_16, out);
+ else if (insn.opcode->isize == 2)
+ bfd_putb16 (insn.insn, out);
+ return;
+ }
+ else if ((verbatim || !relaxing) && optimize && label)
+ {
+ /* This instruction is with label. */
+ expressionS exp;
+ out = frag_var (rs_machine_dependent, insn.opcode->isize,
+ 0, 0, NULL, 0, NULL);
+ /* If this insturction is branch target, it is not relaxable. */
+ fragP->tc_frag_data.flag = NDS32_FRAG_LABEL;
+ fragP->tc_frag_data.opcode = insn.opcode;
+ fragP->tc_frag_data.insn = insn.insn;
+ fragP->fr_fix += insn.opcode->isize;
+ if (insn.opcode->isize == 4)
+ {
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ fixP = fix_new_exp (fragP, 0, 0, &exp, 0, BFD_RELOC_NDS32_LABEL);
+ }
+ }
+ else
+ out = frag_more (insn.opcode->isize);
+
+ if (insn.opcode->isize == 4)
+ bfd_putb32 (insn.insn, out);
+ if (insn.opcode->isize == 2)
+ bfd_putb16 (insn.insn, out);
+
+ dwarf2_emit_insn (insn.opcode->isize);
+
+ /* Compiler generating code and user assembly pseudo load-store, insert
+ fixup here. */
+ pexp = insn.info;
+ fixP = nds32_elf_record_fixup_exp (fragP, str, fld, pexp, out, &insn);
+ /* Build relaxation pattern when relaxing is enable. */
+ if (relaxing)
+ nds32_elf_build_relax_relation (fixP, pexp, out, insn.opcode, fragP, fld);
+}
+
+/* md_macro_start */
+
+void
+nds32_macro_start (void)
+{
+}
+
+/* md_macro_info */
+
+void
+nds32_macro_info (void *info ATTRIBUTE_UNUSED)
+{
+}
+
+/* md_macro_end */
+
+void
+nds32_macro_end (void)
+{
+}
+
+/* GAS will call this function with one argument, an expressionS pointer, for
+ any expression that can not be recognized. When the function is called,
+ input_line_pointer will point to the start of the expression. */
+
+void
+md_operand (expressionS *expressionP)
+{
+ if (*input_line_pointer == '#')
+ {
+ input_line_pointer++;
+ expression (expressionP);
+ }
+}
+
+/* GAS will call this function for each section at the end of the assembly, to
+ permit the CPU back end to adjust the alignment of a section. The function
+ must take two arguments, a segT for the section and a valueT for the size of
+ the section, and return a valueT for the rounded size. */
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+/* GAS will call this function when a symbol table lookup fails, before it
+ creates a new symbol. Typically this would be used to supply symbols whose
+ name or value changes dynamically, possibly in a context sensitive way.
+ Predefined symbols with fixed values, such as register names or condition
+ codes, are typically entered directly into the symbol table when md_begin
+ is called. One argument is passed, a char * for the symbol. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+static long
+nds32_calc_branch_offset (segT segment, fragS *fragP,
+ long stretch ATTRIBUTE_UNUSED,
+ relax_info_t *relax_info,
+ enum nds32_br_range branch_range_type)
+{
+ struct nds32_opcode *opcode = fragP->tc_frag_data.opcode;
+ symbolS *branch_symbol = fragP->fr_symbol;
+ offsetT branch_offset = fragP->fr_offset;
+ offsetT branch_target_address;
+ offsetT branch_insn_address;
+ long offset = 0;
+
+ if ((S_GET_SEGMENT (branch_symbol) != segment)
+ || S_IS_WEAK (branch_symbol))
+ {
+ /* The symbol is not in the SEGMENT. It could be far far away. */
+ offset = 0x80000000;
+ }
+ else
+ {
+ /* Calculate symbol-to-instruction offset. */
+ branch_target_address = S_GET_VALUE (branch_symbol) + branch_offset;
+ /* If the destination symbol is beyond current frag address,
+ STRETCH will take effect to symbol's position. */
+ if (S_GET_VALUE (branch_symbol) > fragP->fr_address)
+ branch_target_address += stretch;
+
+ branch_insn_address = fragP->fr_address + fragP->fr_fix;
+ branch_insn_address -= opcode->isize;
+
+ /* Update BRANCH_INSN_ADDRESS to relaxed position. */
+ branch_insn_address += (relax_info->relax_code_size[branch_range_type]
+ - relax_info->relax_branch_isize[branch_range_type]);
+
+ offset = branch_target_address - branch_insn_address;
+ }
+
+ return offset;
+}
+
+static enum nds32_br_range
+nds32_convert_to_range_type (long offset)
+{
+ enum nds32_br_range range_type;
+
+ if (-(0x100) <= offset && offset < 0x100) /* 256 bytes */
+ range_type = BR_RANGE_S256;
+ else if (-(0x4000) <= offset && offset < 0x4000) /* 16K bytes */
+ range_type = BR_RANGE_S16K;
+ else if (-(0x10000) <= offset && offset < 0x10000) /* 64K bytes */
+ range_type = BR_RANGE_S64K;
+ else if (-(0x1000000) <= offset && offset < 0x1000000) /* 16M bytes */
+ range_type = BR_RANGE_S16M;
+ else /* 4G bytes */
+ range_type = BR_RANGE_U4G;
+
+ return range_type;
+}
+
+/* Set insntruction register mask. */
+
+static void
+nds32_elf_get_set_cond (relax_info_t *relax_info, int offset, uint32_t *insn,
+ uint32_t ori_insn, int range)
+{
+ nds32_cond_field_t *cond_fields = relax_info->cond_field;
+ nds32_cond_field_t *code_seq_cond = relax_info->relax_code_condition[range];
+ uint32_t mask;
+ int i = 0;
+
+ /* The instruction has conditions. Collect condition values. */
+ while (code_seq_cond[i].bitmask != 0)
+ {
+ if (offset == code_seq_cond[i].offset)
+ {
+ mask = (ori_insn >> cond_fields[i].bitpos) & cond_fields[i].bitmask;
+ /* Sign extend. */
+ if (cond_fields[i].signed_extend)
+ mask = (mask ^ ((cond_fields[i].bitmask + 1) >> 1)) -
+ ((cond_fields[i].bitmask + 1) >> 1);
+ *insn |= (mask & code_seq_cond[i].bitmask) << code_seq_cond[i].bitpos;
+ }
+ i++;
+ }
+}
+
+
+static int
+nds32_relax_branch_instructions (segT segment, fragS *fragP,
+ long stretch ATTRIBUTE_UNUSED,
+ int init)
+{
+ enum nds32_br_range branch_range_type;
+ struct nds32_opcode *opcode = fragP->tc_frag_data.opcode;
+ long offset = 0;
+ enum nds32_br_range real_range_type;
+ int adjust = 0;
+ relax_info_t *relax_info;
+ int diff = 0;
+ int i, j, k;
+ int code_seq_size;
+ uint32_t *code_seq;
+ uint32_t insn;
+ int insn_size;
+ int code_seq_offset;
+
+ /* Replace with gas_assert (fragP->fr_symbol != NULL); */
+ if (fragP->fr_symbol == NULL)
+ return adjust;
+
+ /* If frag_var is not enough room, the previos frag is fr_full and with
+ opcode. The new one is rs_dependent but without opcode. */
+ if (opcode == NULL)
+ return adjust;
+
+ relax_info = hash_find (nds32_relax_info_hash, opcode->opcode);
+
+ if (relax_info == NULL)
+ return adjust;
+
+ if (init)
+ branch_range_type = relax_info->br_range;
+ else
+ branch_range_type = fragP->fr_subtype;
+
+ offset = nds32_calc_branch_offset (segment, fragP, stretch,
+ relax_info, branch_range_type);
+
+ real_range_type = nds32_convert_to_range_type (offset);
+
+ /* If actual range is equal to instruction jump range, do nothing. */
+ if (real_range_type == branch_range_type)
+ return adjust;
+
+ /* Find out proper relaxation code sequence. */
+ for (i = BR_RANGE_S256; i < BR_RANGE_NUM; i++)
+ {
+ if (real_range_type <= (unsigned int) i)
+ {
+ if (init)
+ diff = relax_info->relax_code_size[i] - opcode->isize;
+ else
+ diff = relax_info->relax_code_size[i]
+ - relax_info->relax_code_size[branch_range_type];
+
+ /* If the instruction could be converted to 16-bits,
+ minus the difference. */
+ code_seq_offset = 0;
+ j = 0;
+ k = 0;
+ code_seq_size = relax_info->relax_code_size[i];
+ code_seq = relax_info->relax_code_seq[i];
+ while (code_seq_offset < code_seq_size)
+ {
+ insn = code_seq[j];
+ if (insn & 0x80000000) /* 16-bits instruction. */
+ {
+ insn_size = 2;
+ }
+ else /* 32-bits instruction. */
+ {
+ insn_size = 4;
+
+ while (relax_info->relax_fixup[i][k].size !=0
+ && relax_info->relax_fixup[i][k].offset < code_seq_offset)
+ k++;
+ }
+
+ code_seq_offset += insn_size;
+ j++;
+ }
+
+ /* Update fr_subtype to new NDS32_BR_RANGE. */
+ fragP->fr_subtype = i;
+ break;
+ }
+ }
+
+ return diff + adjust;
+}
+
+/* Adjust relaxable frag till current frag. */
+
+static int
+nds32_adjust_relaxable_frag (fragS *startP, fragS *fragP)
+{
+ int adj;
+ if (startP->tc_frag_data.flag & NDS32_FRAG_RELAXED)
+ adj = -2;
+ else
+ adj = 2;
+
+ startP->tc_frag_data.flag ^= NDS32_FRAG_RELAXED;
+
+ while (startP)
+ {
+ startP = startP->fr_next;
+ if (startP)
+ {
+ startP->fr_address += adj;
+ if (startP == fragP)
+ break;
+ }
+ }
+ return adj;
+}
+
+static addressT
+nds32_get_align (addressT address, int align)
+{
+ addressT mask, new_address;
+
+ mask = ~((~0) << align);
+ new_address = (address + mask) & (~mask);
+ return (new_address - address);
+}
+
+/* Check the prev_frag is legal. */
+static void
+invalid_prev_frag (fragS * fragP, fragS **prev_frag)
+{
+ addressT address;
+ fragS *frag_start = *prev_frag;
+
+ if (!frag_start)
+ return;
+
+ if (frag_start->last_fr_address >= fragP->last_fr_address)
+ {
+ *prev_frag = NULL;
+ return;
+ }
+
+ fragS *frag_t = *prev_frag;
+ while (frag_t != fragP)
+ {
+ if (frag_t->fr_type == rs_align
+ || frag_t->fr_type == rs_align_code
+ || frag_t->fr_type == rs_align_test)
+ {
+ /* Relax instruction can not walk across lable. */
+ if (frag_t->tc_frag_data.flag & NDS32_FRAG_LABEL)
+ {
+ prev_frag = NULL;
+ return;
+ }
+ /* Relax previos relaxable to align rs_align frag. */
+ address = frag_t->fr_address + frag_t->fr_fix;
+ addressT offset = nds32_get_align (address, (int) frag_t->fr_offset);
+ if (offset & 0x2)
+ {
+ /* If there is label on the prev_frag, check if it is aligned. */
+ if (!((*prev_frag)->tc_frag_data.flag & NDS32_FRAG_LABEL)
+ || (((*prev_frag)->fr_address + (*prev_frag)->fr_fix - 2 )
+ & 0x2) == 0)
+ nds32_adjust_relaxable_frag (*prev_frag, frag_t);
+ }
+ *prev_frag = NULL;
+ return;
+ }
+ frag_t = frag_t->fr_next;
+ }
+}
+
+/* md_relax_frag */
+
+int
+nds32_relax_frag (segT segment, fragS *fragP, long stretch ATTRIBUTE_UNUSED)
+{
+ /* Currently, there are two kinds of relaxation in nds32 assembler.
+ 1. relax for branch
+ 2. relax for 32-bits to 16-bits */
+
+ static fragS *prev_frag = NULL;
+ int adjust = 0;
+
+ invalid_prev_frag (fragP, &prev_frag);
+
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_BRANCH)
+ adjust = nds32_relax_branch_instructions (segment, fragP, stretch, 0);
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_LABEL)
+ prev_frag = NULL;
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXABLE
+ && (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED) == 0)
+ /* Here is considered relaxed case originally. But it may cause
+ unendless loop when relaxing. Once the instruction is relaxed,
+ it can not be undo. */
+ prev_frag = fragP;
+
+ return adjust;
+}
+
+/* This function returns 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.
+
+ It is 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)
+{
+ /* Currently, there are two kinds of relaxation in nds32 assembler.
+ 1. relax for branch
+ 2. relax for 32-bits to 16-bits */
+
+ /* Save previos relaxable frag. */
+ static fragS *prev_frag = NULL;
+ int adjust = 0;
+
+ invalid_prev_frag (fragP, &prev_frag);
+
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_BRANCH)
+ adjust = nds32_relax_branch_instructions (segment, fragP, 0, 1);
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_LABEL)
+ prev_frag = NULL;
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED)
+ adjust = 2;
+ else if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXABLE)
+ prev_frag = fragP;
+
+ return adjust;
+}
+
+/* GAS will call this for each rs_machine_dependent fragment. The instruction
+ is completed using the data from the relaxation pass. It may also create any
+ necessary relocations.
+
+ *FRAGP has been relaxed to its final size, and now needs to have the bytes
+ inside it modified to conform to the new size. It is called after relaxation
+ is finished.
+
+ fragP->fr_type == rs_machine_dependent.
+ fragP->fr_subtype is the subtype of what the address relaxed to. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragP)
+{
+ /* Convert branch relaxation instructions. */
+ symbolS *branch_symbol = fragP->fr_symbol;
+ offsetT branch_offset = fragP->fr_offset;
+ enum nds32_br_range branch_range_type = fragP->fr_subtype;
+ struct nds32_opcode *opcode = fragP->tc_frag_data.opcode;
+ uint32_t origin_insn = fragP->tc_frag_data.insn;
+ int backup_endian;
+ relax_info_t *relax_info;
+ char *fr_buffer;
+ int fr_where;
+ int addend ATTRIBUTE_UNUSED;
+ offsetT branch_target_address, branch_insn_address;
+ expressionS exp;
+ fixS *fixP;
+ uint32_t *code_seq;
+ uint32_t insn;
+ int code_size, insn_size, offset, fixup_size;
+ int buf_offset;
+ int i, k;
+ uint16_t insn_16;
+ nds32_relax_fixup_info_t fixup_info[MAX_RELAX_FIX];
+ /* Save the 1st instruction is converted to 16 bit or not. */
+ unsigned int branch_size;
+
+ /* Replace with gas_assert (branch_symbol != NULL); */
+ if (branch_symbol == NULL && !(fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED))
+ return;
+
+ /* If frag_var is not enough room, the previos frag is fr_full and with
+ opcode. The new one is rs_dependent but without opcode. */
+ if (opcode == NULL)
+ return;
+
+ /* Relax the insntruction. */
+ if (fragP->tc_frag_data.flag & NDS32_FRAG_RELAXED)
+ {
+ expressionS exp_t;
+ if (fragP->tc_frag_data.opcode->isize == 2)
+ {
+ insn_16 = fragP->tc_frag_data.insn;
+ nds32_convert_16_to_32 (stdoutput, insn_16, &insn);
+ }
+ else
+ insn = fragP->tc_frag_data.insn;
+ fragP->fr_fix += 2;
+ fr_where = fragP->fr_fix - 4;
+ fr_buffer = fragP->fr_literal + fr_where;
+ exp_t.X_op = O_symbol;
+ exp_t.X_add_symbol = abs_section_sym;
+ exp_t.X_add_number = 0;
+ fix_new_exp (fragP, fr_where, 4, &exp_t, 0,
+ BFD_RELOC_NDS32_INSN16);
+ number_to_chars_bigendian (fr_buffer, insn, 4);
+ }
+ else
+ {
+ /* Branch instruction adjust and append relocations. */
+ relax_info = hash_find (nds32_relax_info_hash, opcode->opcode);
+
+ if (relax_info == NULL)
+ return;
+
+ backup_endian = target_big_endian;
+ target_big_endian = 1;
+
+ fr_where = fragP->fr_fix - opcode->isize;
+ fr_buffer = fragP->fr_literal + fr_where;
+
+ if ((S_GET_SEGMENT (branch_symbol) != sec)
+ || S_IS_WEAK (branch_symbol))
+ {
+ if (fragP->fr_offset & 3)
+ as_warn (_("Addend to unresolved symbol is not on word boundary."));
+ addend = 0;
+ }
+ else
+ {
+ /* Calculate symbol-to-instruction offset. */
+ branch_target_address = S_GET_VALUE (branch_symbol) + branch_offset;
+ branch_insn_address = fragP->fr_address + fr_where;
+ addend = (branch_target_address - branch_insn_address) >> 1;
+ }
+
+ code_size = relax_info->relax_code_size[branch_range_type];
+ code_seq = relax_info->relax_code_seq[branch_range_type];
+
+ memcpy (fixup_info, relax_info->relax_fixup[branch_range_type],
+ sizeof (fixup_info));
+
+ /* Fill in frag. */
+ i = 0;
+ k = 0;
+ offset = 0; /* code_seq offset */
+ buf_offset = 0; /* fr_buffer offset */
+ while (offset < code_size)
+ {
+ insn = code_seq[i];
+ if (insn & 0x80000000) /* 16-bits instruction. */
+ {
+ insn = (insn >> 16) & 0xFFFF;
+ insn_size = 2;
+ }
+ else /* 32-bits instruction. */
+ {
+ insn_size = 4;
+ }
+
+ nds32_elf_get_set_cond (relax_info, offset, &insn,
+ origin_insn, branch_range_type);
+
+ /* Try to convert to 16-bits instruction. Currently, only the first
+ insntruction in pattern can be converted. EX: bnez sethi ori jr,
+ only bnez can be converted to 16 bit and ori can't. */
+
+ while (fixup_info[k].size != 0
+ && relax_info->relax_fixup[branch_range_type][k].offset < offset)
+ k++;
+
+ md_number_to_chars (fr_buffer + buf_offset, insn, insn_size);
+ buf_offset += insn_size;
+
+ offset += insn_size;
+ i++;
+ }
+
+ /* Set up fixup. */
+ exp.X_op = O_symbol;
+
+ for (i = 0; fixup_info[i].size != 0; i++)
+ {
+ fixup_size = fixup_info[i].size;
+
+ if ((fixup_info[i].ramp & NDS32_CREATE_LABEL) != 0)
+ {
+ /* This is a reverse branch. */
+ exp.X_add_symbol = symbol_temp_new (sec, 0, fragP->fr_next);
+ exp.X_add_number = 0;
+ }
+ else if ((fixup_info[i].ramp & NDS32_PTR) != 0)
+ {
+ /* This relocation has to point to another instruction. */
+ branch_size = fr_where + code_size - 4;
+ exp.X_add_symbol = symbol_temp_new (sec, branch_size, fragP);
+ exp.X_add_number = 0;
+ }
+ else if ((fixup_info[i].ramp & NDS32_ABS) != 0)
+ {
+ /* This is a tag relocation. */
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ }
+ else if ((fixup_info[i].ramp & NDS32_INSN16) != 0)
+ {
+ if (!enable_16bit)
+ continue;
+ /* This is a tag relocation. */
+ exp.X_add_symbol = abs_section_sym;
+ exp.X_add_number = 0;
+ }
+ else
+ {
+ exp.X_add_symbol = branch_symbol;
+ exp.X_add_number = branch_offset;
+ }
+
+ if (fixup_info[i].r_type != 0)
+ {
+ fixP = fix_new_exp (fragP, fr_where + fixup_info[i].offset,
+ fixup_size, &exp, 0, fixup_info[i].r_type);
+ fixP->fx_addnumber = fixP->fx_offset;
+ }
+ }
+
+ fragP->fr_fix = fr_where + buf_offset;
+
+ target_big_endian = backup_endian;
+ }
+}
+
+/* tc_frob_file_before_fix */
+
+void
+nds32_frob_file_before_fix (void)
+{
+}
+
+static bfd_boolean
+nds32_relaxable_section (asection *sec)
+{
+ return ((sec->flags & SEC_DEBUGGING) == 0
+ && strcmp (sec->name, ".eh_frame") != 0);
+}
+
+/* TC_FORCE_RELOCATION */
+int
+nds32_force_relocation (fixS * fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_NDS32_INSN16:
+ case BFD_RELOC_NDS32_LABEL:
+ case BFD_RELOC_NDS32_LONGCALL1:
+ case BFD_RELOC_NDS32_LONGCALL2:
+ case BFD_RELOC_NDS32_LONGCALL3:
+ case BFD_RELOC_NDS32_LONGJUMP1:
+ case BFD_RELOC_NDS32_LONGJUMP2:
+ case BFD_RELOC_NDS32_LONGJUMP3:
+ case BFD_RELOC_NDS32_LOADSTORE:
+ case BFD_RELOC_NDS32_9_FIXED:
+ case BFD_RELOC_NDS32_15_FIXED:
+ case BFD_RELOC_NDS32_17_FIXED:
+ case BFD_RELOC_NDS32_25_FIXED:
+ case BFD_RELOC_NDS32_9_PCREL:
+ case BFD_RELOC_NDS32_15_PCREL:
+ case BFD_RELOC_NDS32_17_PCREL:
+ case BFD_RELOC_NDS32_WORD_9_PCREL:
+ case BFD_RELOC_NDS32_10_UPCREL:
+ case BFD_RELOC_NDS32_25_PCREL:
+ case BFD_RELOC_NDS32_MINUEND:
+ case BFD_RELOC_NDS32_SUBTRAHEND:
+ return 1;
+
+ case BFD_RELOC_8:
+ case BFD_RELOC_16:
+ case BFD_RELOC_32:
+ case BFD_RELOC_NDS32_DIFF_ULEB128:
+ /* Linker should handle difference between two symbol. */
+ return fix->fx_subsy != NULL
+ && nds32_relaxable_section (S_GET_SEGMENT (fix->fx_addsy));
+ case BFD_RELOC_64:
+ if (fix->fx_subsy)
+ as_bad ("Double word for difference between two symbols "
+ "is not supported across relaxation.");
+ default:
+ ;
+ }
+
+ if (generic_force_reloc (fix))
+ return 1;
+
+ return fix->fx_pcrel;
+}
+
+/* TC_VALIDATE_FIX_SUB */
+
+int
+nds32_validate_fix_sub (fixS *fix, segT add_symbol_segment)
+{
+ segT sub_symbol_segment;
+
+ /* This code is referred from Xtensa. Check their implementation for
+ details. */
+
+ /* Make sure both symbols are in the same segment, and that segment is
+ "normal" and relaxable. */
+ sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+ return (sub_symbol_segment == add_symbol_segment
+ && add_symbol_segment != undefined_section);
+}
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Equal to MAX_PRECISION in atof-ieee.c. */
+#define MAX_LITTLENUMS 6
+
+/* This function is called to convert an ASCII string into a floating point
+ value in format used by the CPU. */
+
+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;
+ 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);
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+ else
+ {
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i],
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ }
+
+ return 0;
+}
+
+/* md_elf_section_change_hook */
+
+void
+nds32_elf_section_change_hook (void)
+{
+}
+
+/* md_cleanup */
+
+void
+nds32_cleanup (void)
+{
+}
+
+/* This function is used to scan leb128 subtraction expressions,
+ and insert fixups for them.
+
+ e.g., .leb128 .L1 - .L0
+
+ These expressions are heavily used in debug information or
+ exception tables. Because relaxation will change code size,
+ we must resolve them in link time. */
+
+static void
+nds32_insert_leb128_fixes (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec, void *xxx ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ struct frag *fragP;
+
+ subseg_set (sec, 0);
+
+ for (fragP = seginfo->frchainP->frch_root;
+ fragP; fragP = fragP->fr_next)
+ {
+ expressionS *exp;
+
+ /* Only unsigned leb128 can be handle. */
+ if (fragP->fr_type != rs_leb128 || fragP->fr_subtype != 0
+ || fragP->fr_symbol == NULL)
+ continue;
+
+ exp = symbol_get_value_expression (fragP->fr_symbol);
+
+ if (exp->X_op != O_subtract)
+ continue;
+
+ fix_new_exp (fragP, fragP->fr_fix, 0,
+ exp, 0, BFD_RELOC_NDS32_DIFF_ULEB128);
+ }
+}
+
+static void
+nds32_insert_relax_entry (bfd *abfd ATTRIBUTE_UNUSED, asection *sec,
+ void *xxx ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo;
+ fragS *fragP;
+ fixS *fixP;
+ expressionS exp;
+ fixS *fixp;
+
+ seginfo = seg_info (sec);
+ if (!seginfo || !symbol_rootP || !subseg_text_p (sec) || sec->size == 0)
+ return;
+ /* If there is no relocation and relax is disabled, it is not necessary to
+ insert R_NDS32_RELAX_ENTRY for linker do EX9 or IFC optimization. */
+ for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next)
+ if (!fixp->fx_done)
+ break;
+ if (!fixp && !enable_relax_ex9 && !verbatim)
+ return;
+
+ subseg_change (sec, 0);
+
+ /* Set RELAX_ENTRY flags for linker. */
+ fragP = seginfo->frchainP->frch_root;
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = section_symbol (sec);
+ exp.X_add_number = 0;
+ if (!enable_relax_relocs)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG;
+ else
+ {
+ /* These flags are only enabled when global relax is enabled.
+ Maybe we can check DISABLE_RELAX_FLAG at linke-time,
+ so we set them anyway. */
+ if (enable_relax_ex9)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_EX9_FLAG;
+ if (enable_relax_ifc)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_IFC_FLAG;
+ if (verbatim)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_VERBATIM_FLAG;
+ }
+ if (optimize)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG;
+ if (optimize_for_space)
+ exp.X_add_number |= R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG;
+
+ fixP = fix_new_exp (fragP, 0, 0, &exp, 0, BFD_RELOC_NDS32_RELAX_ENTRY);
+ fixP->fx_no_overflow = 1;
+}
+
+/* Analysis relax hint and insert suitable relocation pattern. */
+
+static void
+nds32_elf_analysis_relax_hint (void)
+{
+ hash_traverse (nds32_hint_hash, nds32_elf_append_relax_relocs);
+}
+
+void
+md_end (void)
+{
+ nds32_elf_analysis_relax_hint ();
+ bfd_map_over_sections (stdoutput, nds32_insert_leb128_fixes, NULL);
+}
+
+/* Implement md_allow_local_subtract. */
+
+bfd_boolean
+nds32_allow_local_subtract (expressionS *expr_l ATTRIBUTE_UNUSED,
+ expressionS *expr_r ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED)
+{
+ /* Don't allow any subtraction, because relax may change the code. */
+ return FALSE;
+}
+
+/* Sort relocation by address.
+
+ We didn't use qsort () in stdlib, because quick-sort is not a stable
+ sorting algorithm. Relocations at the same address (r_offset) must keep
+ their relative order. For example, RELAX_ENTRY must be the very first
+ relocation entry.
+
+ Currently, this function implements insertion-sort. */
+
+static int
+compar_relent (const void *lhs, const void *rhs)
+{
+ const arelent **l = (const arelent **) lhs;
+ const arelent **r = (const arelent **) rhs;
+
+ if ((*l)->address > (*r)->address)
+ return 1;
+ else if ((*l)->address == (*r)->address)
+ return 0;
+ else
+ return -1;
+}
+
+/* SET_SECTION_RELOCS ()
+
+ Although this macro is originally used to set a relocation for each section,
+ we use it to sort relocations in the same section by the address of the
+ relocation. */
+
+void
+nds32_set_section_relocs (asection *sec, arelent ** relocs ATTRIBUTE_UNUSED,
+ unsigned int n ATTRIBUTE_UNUSED)
+{
+ bfd *abfd ATTRIBUTE_UNUSED = sec->owner;
+ if (bfd_get_section_flags (abfd, sec) & (flagword) SEC_RELOC)
+ nds32_insertion_sort (sec->orelocation, sec->reloc_count,
+ sizeof (arelent**), compar_relent);
+}
+
+long
+nds32_pcrel_from_section (fixS *fixP, segT sec ATTRIBUTE_UNUSED)
+{
+ if (fixP->fx_addsy == NULL || !S_IS_DEFINED (fixP->fx_addsy)
+ || S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy))
+ {
+ /* Let linker resolve undefined symbols. */
+ return 0;
+ }
+
+ return fixP->fx_frag->fr_address + fixP->fx_where;
+}
+
+/* md_post_relax_hook ()
+ Insert relax entry relocation into sections. */
+
+void
+nds32_post_relax_hook (void)
+{
+ bfd_map_over_sections (stdoutput, nds32_insert_relax_entry, NULL);
+}
+
+/* tc_fix_adjustable ()
+
+ Return whether this symbol (fixup) can be replaced with
+ section symbols. */
+
+bfd_boolean
+nds32_fix_adjustable (fixS *fixP)
+{
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NDS32_WORD_9_PCREL:
+ case BFD_RELOC_NDS32_9_PCREL:
+ case BFD_RELOC_NDS32_15_PCREL:
+ case BFD_RELOC_NDS32_17_PCREL:
+ case BFD_RELOC_NDS32_25_PCREL:
+ case BFD_RELOC_NDS32_HI20:
+ case BFD_RELOC_NDS32_LO12S0:
+ case BFD_RELOC_8:
+ case BFD_RELOC_16:
+ case BFD_RELOC_32:
+ case BFD_RELOC_NDS32_PTR:
+ case BFD_RELOC_NDS32_LONGCALL4:
+ case BFD_RELOC_NDS32_LONGCALL5:
+ case BFD_RELOC_NDS32_LONGCALL6:
+ case BFD_RELOC_NDS32_LONGJUMP4:
+ case BFD_RELOC_NDS32_LONGJUMP5:
+ case BFD_RELOC_NDS32_LONGJUMP6:
+ case BFD_RELOC_NDS32_LONGJUMP7:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/* elf_tc_final_processing */
+
+void
+elf_nds32_final_processing (void)
+{
+ /* An FPU_COM instruction is found without previous non-FPU_COM
+ instruction. */
+ if (nds32_fpu_com
+ && !(nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST)))
+ {
+ /* Since only FPU_COM instructions are used and no other FPU instructions
+ are used. The nds32_elf_flags will be decided by the enabled options
+ by command line or default configuration. */
+ if (nds32_fpu_dp_ext || nds32_fpu_sp_ext)
+ {
+ nds32_elf_flags |= nds32_fpu_dp_ext ? E_NDS32_HAS_FPU_DP_INST : 0;
+ nds32_elf_flags |= nds32_fpu_sp_ext ? E_NDS32_HAS_FPU_INST : 0;
+ }
+ else
+ {
+ /* Should never here. */
+ as_bad (_("Used FPU instructions requires enabling FPU extension"));
+ }
+ }
+
+ if (nds32_elf_flags & (E_NDS32_HAS_FPU_INST | E_NDS32_HAS_FPU_DP_INST))
+ {
+ /* Single/double FPU has been used, set FPU register config. */
+ /* We did not check the actual number of register used. We may
+ want to do it while assemble. */
+ nds32_elf_flags &= ~E_NDS32_FPU_REG_CONF;
+ nds32_elf_flags |= (nds32_freg << E_NDS32_FPU_REG_CONF_SHIFT);
+ }
+
+ if (nds32_pic)
+ nds32_elf_flags |= E_NDS32_HAS_PIC;
+
+ if (nds32_gpr16)
+ nds32_elf_flags |= E_NDS32_HAS_REDUCED_REGS;
+
+ nds32_elf_flags |= (E_NDS32_ELF_VER_1_4 | nds32_abi);
+ elf_elfheader (stdoutput)->e_flags |= nds32_elf_flags;
+}
+
+/* Implement md_apply_fix. Apply the fix-up or tranform the fix-up for
+ later relocation generation. */
+
+void
+nds32_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ bfd_vma value = *valP;
+
+ if (fixP->fx_r_type < BFD_RELOC_UNUSED
+ && fixP->fx_r_type > BFD_RELOC_NONE
+ && fixP->fx_r_type != BFD_RELOC_NDS32_DIFF_ULEB128)
+ {
+ /* In our old nds32 binutils, it must convert relocations which is
+ generated by CGEN. However, it does not have to consider this anymore.
+ In current, it only deal with data relocations which enum
+ is smaller than BFD_RELOC_NONE and BFD_RELOC_NDS32_DIFF_ULEB128.
+ It is believed that we can construct a better mechanism to
+ deal with the whole relocation issue in nds32 target
+ without using CGEN. */
+ fixP->fx_addnumber = value;
+ fixP->tc_fix_data = NULL;
+
+ /* Tranform specific relocations here for later relocation generation.
+ Tag data here for ex9 relaxtion and tag tls data for linker. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NDS32_DATA:
+ if (!enable_relax_ex9)
+ fixP->fx_done = 1;
+ break;
+ case BFD_RELOC_NDS32_TPOFF:
+ case BFD_RELOC_NDS32_TLS_LE_HI20:
+ case BFD_RELOC_NDS32_TLS_LE_LO12:
+ case BFD_RELOC_NDS32_TLS_LE_ADD:
+ case BFD_RELOC_NDS32_TLS_LE_LS:
+ case BFD_RELOC_NDS32_GOTTPOFF:
+ case BFD_RELOC_NDS32_TLS_IE_HI20:
+ case BFD_RELOC_NDS32_TLS_IE_LO12S2:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+ default:
+ break;
+ }
+ return;
+ }
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ /* HOW DIFF RELOCATION WORKS.
+
+ First of all, this relocation is used to calculate the distance
+ between two symbols in the SAME section. It is used for jump-
+ table, debug information, exception table, et al. Therefore,
+ it is a unsigned positive value. It is NOT used for general-
+ purpose arithmetic.
+
+ Consider this example, the distance between .LEND and .LBEGIN
+ is stored at the address of foo.
+
+ ---- >8 ---- >8 ---- >8 ---- >8 ----
+ .data
+ foo:
+ .word .LBEGIN - .LEND
+
+ .text
+ [before]
+ .LBEGIN
+ \
+ [between] distance
+ /
+ .LEND
+ [after]
+ ---- 8< ---- 8< ---- 8< ---- 8< ----
+
+ We use a single relocation entry for this expression.
+ * The initial distance value is stored direcly in that location
+ specified by r_offset (i.e., foo in this example.)
+ * The begin of the region, i.e., .LBEGIN, is specified by
+ r_info/R_SYM and r_addend, e.g., .text + 0x32.
+ * The end of region, i.e., .LEND, is represented by
+ .LBEGIN + distance instead of .LEND, so we only need
+ a single relocation entry instead of two.
+
+ When an instruction is relaxed, we adjust the relocation entry
+ depending on where the instruction locates. There are three
+ cases, before, after and between the region.
+ * between: Distance value is read from r_offset, adjusted and
+ written back into r_offset.
+ * before: Only r_addend is adjust.
+ * after: We don't care about it.
+
+ Hereby, there are some limitation.
+
+ `(.LEND - 1) - .LBEGIN' and `(.LEND - .LBEGIN) - 1'
+ are semantically different, and we cannot handle latter case
+ when relaxation.
+
+ The latter expression means subtracting 1 from the distance
+ between .LEND and .LBEGIN. And the former expression means
+ the distance between (.LEND - 1) and .LBEGIN.
+
+ The nuance affects whether to adjust distance value when relax
+ an instruction. In another words, whether the instruction
+ locates in the region. Because we use a single relocation entry,
+ there is no field left for .LEND and the subtrahend.
+
+ Since GCC-4.5, GCC may produce debug information in such expression
+ .long .L1-1-.L0
+ in order to describe register clobbering during an function-call.
+ .L0:
+ call foo
+ .L1:
+
+ Check http://gcc.gnu.org/ml/gcc-patches/2009-06/msg01317.html
+ for details. */
+
+ value -= S_GET_VALUE (fixP->fx_subsy);
+ *valP = value;
+ fixP->fx_subsy = NULL;
+ fixP->fx_offset -= value;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_NDS32_DIFF8;
+ md_number_to_chars (where, value, 1);
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_NDS32_DIFF16;
+ md_number_to_chars (where, value, 2);
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_NDS32_DIFF32;
+ md_number_to_chars (where, value, 4);
+ break;
+ case BFD_RELOC_NDS32_DIFF_ULEB128:
+ /* cvt_frag_to_fill () has called output_leb128 () for us. */
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("expression too complex"));
+ return;
+ }
+ }
+ else if (fixP->fx_done)
+ {
+ /* We're finished with this fixup. Install it because
+ bfd_install_relocation won't be called to do it. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (where, value, 1);
+ break;
+ case BFD_RELOC_16:
+ md_number_to_chars (where, value, 2);
+ break;
+ case BFD_RELOC_32:
+ md_number_to_chars (where, value, 4);
+ break;
+ case BFD_RELOC_64:
+ md_number_to_chars (where, value, 8);
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Internal error: Unknown fixup type %d (`%s')"),
+ fixP->fx_r_type,
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
+ }
+ }
+}
+
+/* Implement tc_gen_reloc. Generate ELF relocation for a fix-up. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+
+ code = fixP->fx_r_type;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixP->fx_r_type, bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ /* Add relocation handling here. */
+
+ switch (fixP->fx_r_type)
+ {
+ default:
+ /* In general, addend of a relocation is the offset to the
+ associated symbol. */
+ reloc->addend = fixP->fx_offset;
+ break;
+
+ case BFD_RELOC_NDS32_DATA:
+ /* Prevent linker from optimizing data in text sections.
+ For example, jump table. */
+ reloc->addend = fixP->fx_size;
+ break;
+ }
+
+ return reloc;
+}
+
+struct suffix_name suffix_table[] =
+{
+ {"GOTOFF", BFD_RELOC_NDS32_GOTOFF, 1},
+ {"GOT", BFD_RELOC_NDS32_GOT20, 1},
+ {"TPOFF", BFD_RELOC_NDS32_TPOFF, 0},
+ {"PLT", BFD_RELOC_NDS32_25_PLTREL, 1},
+ {"GOTTPOFF", BFD_RELOC_NDS32_GOTTPOFF, 0}
+};
+
+/* Implement md_parse_name. */
+
+int
+nds32_parse_name (char const *name, expressionS *exprP,
+ enum expr_mode mode ATTRIBUTE_UNUSED,
+ char *nextcharP ATTRIBUTE_UNUSED)
+{
+ exprP->X_op_symbol = NULL;
+ exprP->X_md = BFD_RELOC_UNUSED;
+
+ exprP->X_add_symbol = symbol_find_or_make (name);
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+
+ if (strcmp (name, GOT_NAME) == 0 && *nextcharP != '@')
+ {
+ /* Set for _GOT_OFFSET_TABLE_. */
+ exprP->X_md = BFD_RELOC_NDS32_GOTPC20;
+ }
+ else if (*nextcharP == '@')
+ {
+ size_t i;
+ char *next;
+ for (i = 0; i < ARRAY_SIZE (suffix_table); i++)
+ {
+ next = input_line_pointer + 1 + strlen(suffix_table[i].suffix);
+ if (strncasecmp (input_line_pointer + 1, suffix_table[i].suffix,
+ strlen (suffix_table[i].suffix)) == 0
+ && !is_part_of_name (*next))
+ {
+ if (!nds32_pic && suffix_table[i].pic)
+ as_bad (_("need PIC qualifier with symbol."));
+ exprP->X_md = suffix_table[i].reloc;
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+ break;
+ }
+ }
+ }
+ return 1;
+}
+
+/* Implement tc_regname_to_dw2regnum. */
+
+int
+tc_nds32_regname_to_dw2regnum (char *regname)
+{
+ struct nds32_keyword *sym = hash_find (nds32_gprs_hash, regname);
+
+ if (!sym)
+ return -1;
+
+ return sym->value;
+}
+
+void
+tc_nds32_frame_initial_instructions (void)
+{
+ /* CIE */
+ /* Default cfa is register-31/sp. */
+ cfi_add_CFA_def_cfa (31, 0);
+}
diff --git a/gas/config/tc-nds32.h b/gas/config/tc-nds32.h
new file mode 100644
index 0000000..1483d51
--- /dev/null
+++ b/gas/config/tc-nds32.h
@@ -0,0 +1,281 @@
+/* tc-nds32.h -- Header file for tc-nds32.c.
+ Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Contributed by Andes Technology Corporation.
+
+ This file is part of GAS.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_NDS32
+#define TC_NDS32
+
+#include "bfd_stdint.h"
+
+#define LISTING_HEADER \
+ (target_big_endian ? "NDS32 GAS" : "NDS32 GAS Little Endian")
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_nds32
+
+/* mapping to mach_table[5] */
+#define ISA_V1 bfd_mach_n1h
+#define ISA_V2 bfd_mach_n1h_v2
+#define ISA_V3 bfd_mach_n1h_v3
+#define ISA_V3M bfd_mach_n1h_v3m
+
+/* Default to big endian. Please note that for Andes architecture,
+ instructions are always in big-endian format. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+/* as.c. */
+/* Extend GAS command line option handling capability. */
+extern int nds32_parse_option (int, char *);
+extern void nds32_after_parse_args (void);
+/* The endianness of the target format may change based on command
+ line arguments. */
+extern const char * nds32_target_format (void);
+
+#define md_parse_option(optc, optarg) nds32_parse_option (optc, optarg)
+#define md_after_parse_args() nds32_after_parse_args ()
+#define TARGET_FORMAT nds32_target_format()
+
+/* expr.c */
+extern int nds32_parse_name (char const *, expressionS *, enum expr_mode, char *);
+extern bfd_boolean nds32_allow_local_subtract (expressionS *, expressionS *, segT);
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ nds32_parse_name (name, exprP, mode, nextcharP)
+#define md_allow_local_subtract(lhs,rhs,sect) nds32_allow_local_subtract (lhs, rhs, sect)
+
+/* dwarf2dbg.c. */
+#define DWARF2_USE_FIXED_ADVANCE_PC 1
+
+/* write.c. */
+extern long nds32_pcrel_from_section (struct fix *, segT);
+extern bfd_boolean nds32_fix_adjustable (struct fix *);
+extern void nds32_frob_file (void);
+extern void nds32_post_relax_hook (void);
+extern void nds32_frob_file_before_fix (void);
+extern void elf_nds32_final_processing (void);
+extern int nds32_validate_fix_sub (struct fix *, segT);
+extern int nds32_force_relocation (struct fix *);
+extern void nds32_set_section_relocs (asection *, arelent ** , unsigned int);
+
+/* Fill in rs_align_code fragments. TODO: Review this. */
+extern void nds32_handle_align (fragS *);
+extern int nds32_relax_frag (segT, fragS *, long);
+extern int tc_nds32_regname_to_dw2regnum (char *);
+extern void tc_nds32_frame_initial_instructions (void);
+
+#define MD_PCREL_FROM_SECTION(fix, sect) nds32_pcrel_from_section (fix, sect)
+#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 0
+#define tc_fix_adjustable(FIX) nds32_fix_adjustable (FIX)
+#define md_apply_fix(fixP, addn, seg) nds32_apply_fix (fixP, addn, seg)
+#define md_post_relax_hook nds32_post_relax_hook ()
+#define tc_frob_file_before_fix() nds32_frob_file_before_fix ()
+#define elf_tc_final_processing() elf_nds32_final_processing ()
+/* For DIFF relocations. The default behavior is inconsistent with the
+ asm internal document. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \
+ (! SEG_NORMAL (SEC) || TC_FORCE_RELOCATION (FIX))
+#define TC_FORCE_RELOCATION(fix) nds32_force_relocation (fix)
+#define TC_VALIDATE_FIX_SUB(FIX,SEG) nds32_validate_fix_sub (FIX,SEG)
+#define SET_SECTION_RELOCS(sec, relocs, n) nds32_set_section_relocs (sec, relocs, n)
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#define HANDLE_ALIGN(f) nds32_handle_align (f)
+#undef DIFF_EXPR_OK /* They should be fixed in linker. */
+#define md_relax_frag(segment, fragP, stretch) nds32_relax_frag (segment, fragP, stretch)
+#define WORKING_DOT_WORD /* We don't need to handle .word strangely. */
+/* Using to chain fixup with previous fixup. */
+#define TC_FIX_TYPE struct fix *
+#define TC_INIT_FIX_DATA(fixP) \
+ do \
+ { \
+ fixP->tc_fix_data = NULL; \
+ } \
+ while (0)
+
+/* read.c. */
+/* Extend GAS macro handling capability. */
+extern void nds32_macro_start (void);
+extern void nds32_macro_end (void);
+extern void nds32_macro_info (void *);
+extern void nds32_start_line_hook (void);
+extern void nds32_elf_section_change_hook (void);
+extern void md_begin (void);
+extern void md_end (void);
+extern int nds32_start_label (int, int);
+extern void nds32_cleanup (void);
+extern void nds32_flush_pending_output (void);
+extern void nds32_cons_align (int);
+extern void nds32_check_label (symbolS *);
+extern void nds32_frob_label (symbolS *);
+extern void nds32_pre_do_align (int, char *, int, int);
+extern void nds32_do_align (int);
+
+#define md_macro_start() nds32_macro_start ()
+#define md_macro_end() nds32_macro_end ()
+#define md_macro_info(args) nds32_macro_info (args)
+#define TC_START_LABEL(C, S, STR) (C == ':' && nds32_start_label (0, 0))
+#define tc_check_label(label) nds32_check_label (label)
+#define tc_frob_label(label) nds32_frob_label (label)
+#define md_end md_end
+#define md_start_line_hook() nds32_start_line_hook ()
+#define md_cons_align(n) nds32_cons_align (n)
+/* COLE: TODO: Review md_do_align. */
+#define md_do_align(N, FILL, LEN, MAX, LABEL) \
+ nds32_pre_do_align (N, FILL, LEN, MAX); \
+ if ((N) > 1 && (subseg_text_p (now_seg) \
+ || strncmp (now_seg->name, ".gcc_except_table", sizeof(".gcc_except_table") - 1) == 0)) \
+ nds32_do_align (N); \
+ goto LABEL;
+#define md_elf_section_change_hook() nds32_elf_section_change_hook ()
+#define md_flush_pending_output() nds32_flush_pending_output ()
+#define md_cleanup() nds32_cleanup ()
+#define LOCAL_LABELS_FB 1 /* Permit temporary numeric labels. */
+
+/* frags.c. */
+
+#define NDS32_FRAG_RELAXABLE 0x1
+#define NDS32_FRAG_RELAXED 0x2
+#define NDS32_FRAG_BRANCH 0x4
+#define NDS32_FRAG_LABEL 0x8
+
+struct nds32_frag_type
+{
+ relax_substateT flag;
+ struct nds32_opcode *opcode;
+ uint32_t insn;
+ /* To Save previos label fixup if existence. */
+ struct fix *fixup;
+};
+
+extern void nds32_frag_init (fragS *);
+
+#define TC_FRAG_TYPE struct nds32_frag_type
+#define TC_FRAG_INIT(fragP) nds32_frag_init (fragP)
+
+/* CFI directive. */
+extern void nds32_elf_frame_initial_instructions (void);
+extern int tc_nds32_regname_to_dw2regnum (char *);
+
+#define TARGET_USE_CFIPOP 1
+#define DWARF2_DEFAULT_RETURN_COLUMN 30
+#define DWARF2_CIE_DATA_ALIGNMENT -4
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+#define tc_regname_to_dw2regnum tc_nds32_regname_to_dw2regnum
+#define tc_cfi_frame_initial_instructions tc_nds32_frame_initial_instructions
+
+/* COLE: TODO: Review These. They seem to be obsoleted. */
+#if 1
+#define TC_RELOC_RTSYM_LOC_FIXUP(FIX) \
+ ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy) \
+ && S_IS_DEFINED ((FIX)->fx_addsy) \
+ && ! S_IS_COMMON ((FIX)->fx_addsy)))
+#define TC_HANDLES_FX_DONE
+/* This arranges for gas/write.c to not apply a relocation if
+ obj_fix_adjustable() says it is not adjustable. */
+#define TC_FIX_ADJUSTABLE(fixP) obj_fix_adjustable (fixP)
+#endif
+
+/* Because linker may relax the code, assemble-time expression
+ optimization is not allowed. */
+#define md_allow_eh_opt 0
+
+/* For nds32 relax. */
+enum nds32_br_range
+{
+ BR_RANGE_S256 = 0,
+ BR_RANGE_S16K,
+ BR_RANGE_S64K,
+ BR_RANGE_S16M,
+ BR_RANGE_U4G,
+ BR_RANGE_NUM
+};
+
+enum nds32_ramp
+{
+ NDS32_CREATE_LABEL = 1,
+ NDS32_RELAX = (1 << 1), /* Obsolete in the future. */
+ NDS32_ORIGIN = (1 << 2),
+ NDS32_INSN16 = (1 << 3),
+ NDS32_PTR = (1 << 4),
+ NDS32_ABS = (1 << 5),
+ NDS32_HINT = (1 << 6),
+ NDS32_FIX = (1 << 7),
+ NDS32_ADDEND = (1 << 8),
+ NDS32_SYM = (1 << 9)
+};
+
+typedef struct nds32_relax_fixup_info
+{
+ int offset;
+ int size;
+ /* Reverse branch has to jump to the end of instruction pattern. */
+ int ramp;
+ enum bfd_reloc_code_real r_type;
+} nds32_relax_fixup_info_t;
+
+typedef struct nds32_cond_field
+{
+ int offset;
+ int bitpos; /* Register position. */
+ int bitmask; /* Number of register bits. */
+ bfd_boolean signed_extend;
+} nds32_cond_field_t;
+
+/* The max relaxation pattern is 20-bytes including the nop. */
+#define NDS32_MAXCHAR 20
+/* In current, the max entend number of instruction for one pseudo instruction
+ is 4, but its number of relocation may be 12. */
+#define MAX_RELAX_NUM 4
+#define MAX_RELAX_FIX 12
+
+typedef struct nds32_relax_info
+{
+ /* Opcode for the instruction. */
+ const char *opcode;
+ enum nds32_br_range br_range;
+ nds32_cond_field_t cond_field[MAX_RELAX_NUM]; /* TODO: Reuse nds32_field? */
+ /* Code sequences for different branch range. */
+ uint32_t relax_code_seq[BR_RANGE_NUM][MAX_RELAX_NUM];
+ nds32_cond_field_t relax_code_condition[BR_RANGE_NUM][MAX_RELAX_NUM];
+ unsigned int relax_code_size[BR_RANGE_NUM];
+ int relax_branch_isize[BR_RANGE_NUM];
+ nds32_relax_fixup_info_t relax_fixup[BR_RANGE_NUM][MAX_RELAX_FIX];
+} relax_info_t;
+
+enum nds32_relax_hint_type
+{
+ NDS32_RELAX_HINT_NONE = 0,
+ NDS32_RELAX_HINT_LA,
+ NDS32_RELAX_HINT_LS
+};
+
+struct nds32_relax_hint_table
+{
+ enum nds32_relax_hint_type main_type;
+ unsigned int relax_code_size;
+ uint32_t relax_code_seq[MAX_RELAX_NUM];
+ nds32_relax_fixup_info_t relax_fixup[MAX_RELAX_FIX];
+};
+
+#endif /* TC_NDS32 */
diff --git a/gas/config/tc-nios2.c b/gas/config/tc-nios2.c
new file mode 100644
index 0000000..21f4288
--- /dev/null
+++ b/gas/config/tc-nios2.c
@@ -0,0 +1,3105 @@
+/* Altera Nios II assembler.
+ Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Contributed by Nigel Gray (ngray@altera.com).
+ Contributed by Mentor Graphics, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "opcode/nios2.h"
+#include "elf/nios2.h"
+#include "tc-nios2.h"
+#include "bfd.h"
+#include "dwarf2dbg.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+#include "dw2gencfi.h"
+
+#ifndef OBJ_ELF
+/* We are not supporting any other target so we throw a compile time error. */
+OBJ_ELF not defined
+#endif
+
+/* We can choose our endianness at run-time, regardless of configuration. */
+extern int target_big_endian;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "#";
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that C style comments are always supported. */
+const char line_comment_chars[] = "#";
+
+/* This array holds machine specific line separator characters. */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant. */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+/* Machine-dependent command-line options. */
+
+const char *md_shortopts = "r";
+
+struct option md_longopts[] = {
+#define OPTION_RELAX_ALL (OPTION_MD_BASE + 0)
+ {"relax-all", no_argument, NULL, OPTION_RELAX_ALL},
+#define OPTION_NORELAX (OPTION_MD_BASE + 1)
+ {"no-relax", no_argument, NULL, OPTION_NORELAX},
+#define OPTION_RELAX_SECTION (OPTION_MD_BASE + 2)
+ {"relax-section", no_argument, NULL, OPTION_RELAX_SECTION},
+#define OPTION_EB (OPTION_MD_BASE + 3)
+ {"EB", no_argument, NULL, OPTION_EB},
+#define OPTION_EL (OPTION_MD_BASE + 4)
+ {"EL", no_argument, NULL, OPTION_EL}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* The assembler supports three different relaxation modes, controlled by
+ command-line options. */
+typedef enum
+{
+ relax_section = 0,
+ relax_none,
+ relax_all
+} relax_optionT;
+
+/* Struct contains all assembler options set with .set. */
+struct
+{
+ /* .set noat -> noat = 1 allows assembly code to use at without warning
+ and macro expansions generate a warning.
+ .set at -> noat = 0, assembly code using at warn but macro expansions
+ do not generate warnings. */
+ bfd_boolean noat;
+
+ /* .set nobreak -> nobreak = 1 allows assembly code to use ba,bt without
+ warning.
+ .set break -> nobreak = 0, assembly code using ba,bt warns. */
+ bfd_boolean nobreak;
+
+ /* .cmd line option -relax-all allows all branches and calls to be replaced
+ with longer versions.
+ -no-relax inhibits branch/call conversion.
+ The default value is relax_section, which relaxes branches within
+ a section. */
+ relax_optionT relax;
+
+} nios2_as_options = {FALSE, FALSE, relax_section};
+
+
+typedef struct nios2_insn_reloc
+{
+ /* Any expression in the instruction is parsed into this field,
+ which is passed to fix_new_exp() to generate a fixup. */
+ expressionS reloc_expression;
+
+ /* The type of the relocation to be applied. */
+ bfd_reloc_code_real_type reloc_type;
+
+ /* PC-relative. */
+ unsigned int reloc_pcrel;
+
+ /* The next relocation to be applied to the instruction. */
+ struct nios2_insn_reloc *reloc_next;
+} nios2_insn_relocS;
+
+/* This struct is used to hold state when assembling instructions. */
+typedef struct nios2_insn_info
+{
+ /* Assembled instruction. */
+ unsigned long insn_code;
+ /* Pointer to the relevant bit of the opcode table. */
+ const struct nios2_opcode *insn_nios2_opcode;
+ /* After parsing ptrs to the tokens in the instruction fill this array
+ it is terminated with a null pointer (hence the first +1).
+ The second +1 is because in some parts of the code the opcode
+ is not counted as a token, but still placed in this array. */
+ const char *insn_tokens[NIOS2_MAX_INSN_TOKENS + 1 + 1];
+
+ /* This holds information used to generate fixups
+ and eventually relocations if it is not null. */
+ nios2_insn_relocS *insn_reloc;
+} nios2_insn_infoS;
+
+/* This struct associates an argument assemble function with
+ an argument syntax string. Used by the assembler to find out
+ how to parse and assemble a set of instruction operands and
+ return the instruction field values. */
+typedef struct nios2_arg_info
+{
+ const char *args;
+ void (*assemble_args_func) (nios2_insn_infoS *insn_info);
+} nios2_arg_infoS;
+
+/* This struct is used to convert Nios II pseudo-ops into the
+ corresponding real op. */
+typedef struct nios2_ps_insn_info
+{
+ /* Map this pseudo_op... */
+ const char *pseudo_insn;
+
+ /* ...to this real instruction. */
+ const char *insn;
+
+ /* Call this function to modify the operands.... */
+ void (*arg_modifer_func) (char ** parsed_args, const char *arg, int num,
+ int start);
+
+ /* ...with these arguments. */
+ const char *arg_modifier;
+ int num;
+ int index;
+
+ /* If arg_modifier_func allocates new memory, provide this function
+ to free it afterwards. */
+ void (*arg_cleanup_func) (char **parsed_args, int num, int start);
+} nios2_ps_insn_infoS;
+
+/* Opcode hash table. */
+static struct hash_control *nios2_opcode_hash = NULL;
+#define nios2_opcode_lookup(NAME) \
+ ((struct nios2_opcode *) hash_find (nios2_opcode_hash, (NAME)))
+
+/* Register hash table. */
+static struct hash_control *nios2_reg_hash = NULL;
+#define nios2_reg_lookup(NAME) \
+ ((struct nios2_reg *) hash_find (nios2_reg_hash, (NAME)))
+
+/* Parse args hash table. */
+static struct hash_control *nios2_arg_hash = NULL;
+#define nios2_arg_lookup(NAME) \
+ ((nios2_arg_infoS *) hash_find (nios2_arg_hash, (NAME)))
+
+/* Pseudo-op hash table. */
+static struct hash_control *nios2_ps_hash = NULL;
+#define nios2_ps_lookup(NAME) \
+ ((nios2_ps_insn_infoS *) hash_find (nios2_ps_hash, (NAME)))
+
+/* The known current alignment of the current section. */
+static int nios2_current_align;
+static segT nios2_current_align_seg;
+
+static int nios2_auto_align_on = 1;
+
+/* The last seen label in the current section. This is used to auto-align
+ labels preceeding instructions. */
+static symbolS *nios2_last_label;
+
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+symbolS *GOT_symbol;
+#endif
+
+
+/** Utility routines. */
+/* Function md_chars_to_number takes the sequence of
+ bytes in buf and returns the corresponding value
+ in an int. n must be 1, 2 or 4. */
+static valueT
+md_chars_to_number (char *buf, int n)
+{
+ int i;
+ valueT val;
+
+ gas_assert (n == 1 || n == 2 || n == 4);
+
+ val = 0;
+ if (target_big_endian)
+ for (i = 0; i < n; ++i)
+ val = val | ((buf[i] & 0xff) << 8 * (n - (i + 1)));
+ else
+ for (i = 0; i < n; ++i)
+ val = val | ((buf[i] & 0xff) << 8 * i);
+ return val;
+}
+
+
+/* This function turns a C long int, short int or char
+ into the series of bytes that represent the number
+ on the target machine. */
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ gas_assert (n == 1 || n == 2 || n == 4 || n == 8);
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ 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. */
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[4];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ prec = 2;
+ break;
+ case 'd':
+ prec = 4;
+ break;
+ 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 * 2;
+
+ if (! target_big_endian)
+ for (i = prec - 1; i >= 0; i--, litP += 2)
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ else
+ for (i = 0; i < prec; i++, litP += 2)
+ md_number_to_chars (litP, (valueT) words[i], 2);
+
+ return NULL;
+}
+
+/* Return true if STR starts with PREFIX, which should be a string literal. */
+#define strprefix(STR, PREFIX) \
+ (strncmp ((STR), PREFIX, strlen (PREFIX)) == 0)
+
+/* Return true if STR is prefixed with a control register name. */
+static int
+nios2_control_register_arg_p (const char *str)
+{
+ return (strprefix (str, "ctl")
+ || strprefix (str, "cpuid")
+ || strprefix (str, "status")
+ || strprefix (str, "estatus")
+ || strprefix (str, "bstatus")
+ || strprefix (str, "ienable")
+ || strprefix (str, "ipending")
+ || strprefix (str, "exception")
+ || strprefix (str, "pteaddr")
+ || strprefix (str, "tlbacc")
+ || strprefix (str, "tlbmisc")
+ || strprefix (str, "eccinj")
+ || strprefix (str, "config")
+ || strprefix (str, "mpubase")
+ || strprefix (str, "mpuacc")
+ || strprefix (str, "badaddr"));
+}
+
+/* Return true if STR is prefixed with a special relocation operator. */
+static int
+nios2_special_relocation_p (const char *str)
+{
+ return (strprefix (str, "%lo")
+ || strprefix (str, "%hi")
+ || strprefix (str, "%hiadj")
+ || strprefix (str, "%gprel")
+ || strprefix (str, "%got")
+ || strprefix (str, "%call")
+ || strprefix (str, "%gotoff_lo")
+ || strprefix (str, "%gotoff_hiadj")
+ || strprefix (str, "%tls_gd")
+ || strprefix (str, "%tls_ldm")
+ || strprefix (str, "%tls_ldo")
+ || strprefix (str, "%tls_ie")
+ || strprefix (str, "%tls_le")
+ || strprefix (str, "%gotoff"));
+}
+
+/* Checks whether the register name is a coprocessor
+ register - returns TRUE if it is, FALSE otherwise. */
+static bfd_boolean
+nios2_coproc_reg (const char *reg_name)
+{
+ gas_assert (reg_name != NULL);
+
+ /* Check that we do have a valid register name and that it is a
+ coprocessor register.
+ It must begin with c, not be a control register, and be a valid
+ register name. */
+ if (strprefix (reg_name, "c")
+ && !strprefix (reg_name, "ctl")
+ && hash_find (nios2_reg_hash, reg_name) != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* nop fill pattern for text section. */
+static char const nop[4] = { 0x3a, 0x88, 0x01, 0x00 };
+
+/* Handles all machine-dependent alignment needs. */
+static void
+nios2_align (int log_size, const char *pfill, symbolS *label)
+{
+ int align;
+ long max_alignment = 15;
+
+ /* The front end is prone to changing segments out from under us
+ temporarily when -g is in effect. */
+ int switched_seg_p = (nios2_current_align_seg != now_seg);
+
+ align = log_size;
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_bad (_("Alignment too large: %d. assumed"), align);
+ }
+ else if (align < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed"));
+ align = 0;
+ }
+
+ if (align != 0)
+ {
+ if (subseg_text_p (now_seg) && align >= 2)
+ {
+ /* First, make sure we're on a four-byte boundary, in case
+ someone has been putting .byte values the text section. */
+ if (nios2_current_align < 2 || switched_seg_p)
+ frag_align (2, 0, 0);
+
+ /* Now fill in the alignment pattern. */
+ if (pfill != NULL)
+ frag_align_pattern (align, pfill, sizeof nop, 0);
+ else
+ frag_align (align, 0, 0);
+ }
+ else
+ frag_align (align, 0, 0);
+
+ if (!switched_seg_p)
+ nios2_current_align = align;
+
+ /* If the last label was in a different section we can't align it. */
+ if (label != NULL && !switched_seg_p)
+ {
+ symbolS *sym;
+ int label_seen = FALSE;
+ struct frag *old_frag;
+ valueT old_value;
+ valueT new_value;
+
+ gas_assert (S_GET_SEGMENT (label) == now_seg);
+
+ old_frag = symbol_get_frag (label);
+ old_value = S_GET_VALUE (label);
+ new_value = (valueT) frag_now_fix ();
+
+ /* It is possible to have more than one label at a particular
+ address, especially if debugging is enabled, so we must
+ take care to adjust all the labels at this address in this
+ fragment. To save time we search from the end of the symbol
+ list, backwards, since the symbols we are interested in are
+ almost certainly the ones that were most recently added.
+ Also to save time we stop searching once we have seen at least
+ one matching label, and we encounter a label that is no longer
+ in the target fragment. Note, this search is guaranteed to
+ find at least one match when sym == label, so no special case
+ code is necessary. */
+ for (sym = symbol_lastP; sym != NULL; sym = symbol_previous (sym))
+ if (symbol_get_frag (sym) == old_frag
+ && S_GET_VALUE (sym) == old_value)
+ {
+ label_seen = TRUE;
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, new_value);
+ }
+ else if (label_seen && symbol_get_frag (sym) != old_frag)
+ break;
+ }
+ record_alignment (now_seg, align);
+ }
+}
+
+
+/** Support for self-check mode. */
+
+/* Mode of the assembler. */
+typedef enum
+{
+ NIOS2_MODE_ASSEMBLE, /* Ordinary operation. */
+ NIOS2_MODE_TEST /* Hidden mode used for self testing. */
+} NIOS2_MODE;
+
+static NIOS2_MODE nios2_mode = NIOS2_MODE_ASSEMBLE;
+
+/* This function is used to in self-checking mode
+ to check the assembled instruction
+ opcode should be the assembled opcode, and exp_opcode
+ the parsed string representing the expected opcode. */
+static void
+nios2_check_assembly (unsigned int opcode, const char *exp_opcode)
+{
+ if (nios2_mode == NIOS2_MODE_TEST)
+ {
+ if (exp_opcode == NULL)
+ as_bad (_("expecting opcode string in self test mode"));
+ else if (opcode != strtoul (exp_opcode, NULL, 16))
+ as_bad (_("assembly 0x%08x, expected %s"), opcode, exp_opcode);
+ }
+}
+
+
+/** Support for machine-dependent assembler directives. */
+/* Handle the .align pseudo-op. This aligns to a power of two. It
+ also adjusts any current instruction label. We treat this the same
+ way the MIPS port does: .align 0 turns off auto alignment. */
+static void
+s_nios2_align (int ignore ATTRIBUTE_UNUSED)
+{
+ int align;
+ char fill;
+ const char *pfill = NULL;
+ long max_alignment = 15;
+
+ align = get_absolute_expression ();
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_bad (_("Alignment too large: %d. assumed"), align);
+ }
+ else if (align < 0)
+ {
+ as_warn (_("Alignment negative: 0 assumed"));
+ align = 0;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ fill = get_absolute_expression ();
+ pfill = (const char *) &fill;
+ }
+ else if (subseg_text_p (now_seg))
+ pfill = (const char *) &nop;
+ else
+ {
+ pfill = NULL;
+ nios2_last_label = NULL;
+ }
+
+ if (align != 0)
+ {
+ nios2_auto_align_on = 1;
+ nios2_align (align, pfill, nios2_last_label);
+ nios2_last_label = NULL;
+ }
+ else
+ nios2_auto_align_on = 0;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .text pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+static void
+s_nios2_text (int i)
+{
+ s_text (i);
+ nios2_last_label = NULL;
+ nios2_current_align = 0;
+ nios2_current_align_seg = now_seg;
+}
+
+/* Handle the .data pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+static void
+s_nios2_data (int i)
+{
+ s_data (i);
+ nios2_last_label = NULL;
+ nios2_current_align = 0;
+ nios2_current_align_seg = now_seg;
+}
+
+/* Handle the .section pseudo-op. This is like the usual one, but it
+ clears the saved last label and resets known alignment. */
+static void
+s_nios2_section (int ignore)
+{
+ obj_elf_section (ignore);
+ nios2_last_label = NULL;
+ nios2_current_align = 0;
+ nios2_current_align_seg = now_seg;
+}
+
+/* Explicitly unaligned cons. */
+static void
+s_nios2_ucons (int nbytes)
+{
+ int hold;
+ hold = nios2_auto_align_on;
+ nios2_auto_align_on = 0;
+ cons (nbytes);
+ nios2_auto_align_on = hold;
+}
+
+/* Handle the .sdata directive. */
+static void
+s_nios2_sdata (int ignore ATTRIBUTE_UNUSED)
+{
+ get_absolute_expression (); /* Ignored. */
+ subseg_new (".sdata", 0);
+ demand_empty_rest_of_line ();
+}
+
+/* .set sets assembler options eg noat/at and is also used
+ to set symbol values (.equ, .equiv ). */
+static void
+s_nios2_set (int equiv)
+{
+ char *directive = input_line_pointer;
+ char delim = get_symbol_end ();
+ char *endline = input_line_pointer;
+ *endline = delim;
+
+ /* We only want to handle ".set XXX" if the
+ user has tried ".set XXX, YYY" they are not
+ trying a directive. This prevents
+ us from polluting the name space. */
+ SKIP_WHITESPACE ();
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ bfd_boolean done = TRUE;
+ *endline = 0;
+
+ if (!strcmp (directive, "noat"))
+ nios2_as_options.noat = TRUE;
+ else if (!strcmp (directive, "at"))
+ nios2_as_options.noat = FALSE;
+ else if (!strcmp (directive, "nobreak"))
+ nios2_as_options.nobreak = TRUE;
+ else if (!strcmp (directive, "break"))
+ nios2_as_options.nobreak = FALSE;
+ else if (!strcmp (directive, "norelax"))
+ nios2_as_options.relax = relax_none;
+ else if (!strcmp (directive, "relaxsection"))
+ nios2_as_options.relax = relax_section;
+ else if (!strcmp (directive, "relaxall"))
+ nios2_as_options.relax = relax_all;
+ else
+ done = FALSE;
+
+ if (done)
+ {
+ *endline = delim;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ }
+
+ /* If we fall through to here, either we have ".set XXX, YYY"
+ or we have ".set XXX" where XXX is unknown or we have
+ a syntax error. */
+ input_line_pointer = directive;
+ *endline = delim;
+ s_set (equiv);
+}
+
+/* Machine-dependent assembler directives.
+ Format of each entry is:
+ { "directive", handler_func, param } */
+const pseudo_typeS md_pseudo_table[] = {
+ {"align", s_nios2_align, 0},
+ {"text", s_nios2_text, 0},
+ {"data", s_nios2_data, 0},
+ {"section", s_nios2_section, 0},
+ {"section.s", s_nios2_section, 0},
+ {"sect", s_nios2_section, 0},
+ {"sect.s", s_nios2_section, 0},
+ /* .dword and .half are included for compatibility with MIPS. */
+ {"dword", cons, 8},
+ {"half", cons, 2},
+ /* NIOS2 native word size is 4 bytes, so we override
+ the GAS default of 2. */
+ {"word", cons, 4},
+ /* Explicitly unaligned directives. */
+ {"2byte", s_nios2_ucons, 2},
+ {"4byte", s_nios2_ucons, 4},
+ {"8byte", s_nios2_ucons, 8},
+ {"16byte", s_nios2_ucons, 16},
+#ifdef OBJ_ELF
+ {"sdata", s_nios2_sdata, 0},
+#endif
+ {"set", s_nios2_set, 0},
+ {NULL, NULL, 0}
+};
+
+
+/** Relaxation support. */
+
+/* We support two relaxation modes: a limited PC-relative mode with
+ -relax-section (the default), and an absolute jump mode with -relax-all.
+
+ Nios II PC-relative branch instructions only support 16-bit offsets.
+ And, there's no good way to add a 32-bit constant to the PC without
+ using two registers.
+
+ To deal with this, for the pc-relative relaxation mode we convert
+ br label
+ into a series of 16-bit adds, like:
+ nextpc at
+ addi at, at, 32767
+ ...
+ addi at, at, remainder
+ jmp at
+
+ Similarly, conditional branches are converted from
+ b(condition) r, s, label
+ into a series like:
+ b(opposite condition) r, s, skip
+ nextpc at
+ addi at, at, 32767
+ ...
+ addi at, at, remainder
+ jmp at
+ skip:
+
+ The compiler can do a better job, either by converting the branch
+ directly into a JMP (going through the GOT for PIC) or by allocating
+ a second register for the 32-bit displacement.
+
+ For the -relax-all relaxation mode, the conversions are
+ movhi at, %hi(symbol+offset)
+ ori at, %lo(symbol+offset)
+ jmp at
+ and
+ b(opposite condition), r, s, skip
+ movhi at, %hi(symbol+offset)
+ ori at, %lo(symbol+offset)
+ jmp at
+ skip:
+ respectively.
+*/
+
+/* Arbitrarily limit the number of addis we can insert; we need to be able
+ to specify the maximum growth size for each frag that contains a
+ relaxable branch. There's no point in specifying a huge number here
+ since that means the assembler needs to allocate that much extra
+ memory for every branch, and almost no real code will ever need it.
+ Plus, as already noted a better solution is to just use a jmp, or
+ allocate a second register to hold a 32-bit displacement.
+ FIXME: Rather than making this a constant, it could be controlled by
+ a command-line argument. */
+#define RELAX_MAX_ADDI 32
+
+/* The fr_subtype field represents the target-specific relocation state.
+ It has type relax_substateT (unsigned int). We use it to track the
+ number of addis necessary, plus a bit to track whether this is a
+ conditional branch.
+ Regardless of the smaller RELAX_MAX_ADDI limit, we reserve 16 bits
+ in the fr_subtype to encode the number of addis so that the whole
+ theoretically-valid range is representable.
+ For the -relax-all mode, N = 0 represents an in-range branch and N = 1
+ represents a branch that needs to be relaxed. */
+#define UBRANCH (0 << 16)
+#define CBRANCH (1 << 16)
+#define IS_CBRANCH(SUBTYPE) ((SUBTYPE) & CBRANCH)
+#define IS_UBRANCH(SUBTYPE) (!IS_CBRANCH (SUBTYPE))
+#define UBRANCH_SUBTYPE(N) (UBRANCH | (N))
+#define CBRANCH_SUBTYPE(N) (CBRANCH | (N))
+#define SUBTYPE_ADDIS(SUBTYPE) ((SUBTYPE) & 0xffff)
+
+/* For the -relax-section mode, unconditional branches require 2 extra i
+ nstructions besides the addis, conditional branches require 3. */
+#define UBRANCH_ADDIS_TO_SIZE(N) (((N) + 2) * 4)
+#define CBRANCH_ADDIS_TO_SIZE(N) (((N) + 3) * 4)
+
+/* For the -relax-all mode, unconditional branches require 3 instructions
+ and conditional branches require 4. */
+#define UBRANCH_JUMP_SIZE 12
+#define CBRANCH_JUMP_SIZE 16
+
+/* Maximum sizes of relaxation sequences. */
+#define UBRANCH_MAX_SIZE \
+ (nios2_as_options.relax == relax_all \
+ ? UBRANCH_JUMP_SIZE \
+ : UBRANCH_ADDIS_TO_SIZE (RELAX_MAX_ADDI))
+#define CBRANCH_MAX_SIZE \
+ (nios2_as_options.relax == relax_all \
+ ? CBRANCH_JUMP_SIZE \
+ : CBRANCH_ADDIS_TO_SIZE (RELAX_MAX_ADDI))
+
+/* Register number of AT, the assembler temporary. */
+#define AT_REGNUM 1
+
+/* Determine how many bytes are required to represent the sequence
+ indicated by SUBTYPE. */
+static int
+nios2_relax_subtype_size (relax_substateT subtype)
+{
+ int n = SUBTYPE_ADDIS (subtype);
+ if (n == 0)
+ /* Regular conditional/unconditional branch instruction. */
+ return 4;
+ else if (nios2_as_options.relax == relax_all)
+ return (IS_CBRANCH (subtype) ? CBRANCH_JUMP_SIZE : UBRANCH_JUMP_SIZE);
+ else if (IS_CBRANCH (subtype))
+ return CBRANCH_ADDIS_TO_SIZE (n);
+ else
+ return UBRANCH_ADDIS_TO_SIZE (n);
+}
+
+/* Estimate size of fragp before relaxation.
+ This could also examine the offset in fragp and adjust
+ fragp->fr_subtype, but we will do that in nios2_relax_frag anyway. */
+int
+md_estimate_size_before_relax (fragS *fragp, segT segment ATTRIBUTE_UNUSED)
+{
+ return nios2_relax_subtype_size (fragp->fr_subtype);
+}
+
+/* Implement md_relax_frag, returning the change in size of the frag. */
+long
+nios2_relax_frag (segT segment, fragS *fragp, long stretch)
+{
+ addressT target = fragp->fr_offset;
+ relax_substateT subtype = fragp->fr_subtype;
+ symbolS *symbolp = fragp->fr_symbol;
+
+ if (symbolp)
+ {
+ fragS *sym_frag = symbol_get_frag (symbolp);
+ offsetT offset;
+ int n;
+
+ target += S_GET_VALUE (symbolp);
+
+ /* See comments in write.c:relax_frag about handling of stretch. */
+ if (stretch != 0
+ && sym_frag->relax_marker != fragp->relax_marker)
+ {
+ if (stretch < 0 || sym_frag->region == fragp->region)
+ target += stretch;
+ else if (target < fragp->fr_address)
+ target = fragp->fr_next->fr_address + stretch;
+ }
+
+ /* We subtract 4 because all pc relative branches are
+ from the next instruction. */
+ offset = target - fragp->fr_address - fragp->fr_fix - 4;
+ if (offset >= -32768 && offset <= 32764)
+ /* Fits in PC-relative branch. */
+ n = 0;
+ else if (nios2_as_options.relax == relax_all)
+ /* Convert to jump. */
+ n = 1;
+ else if (nios2_as_options.relax == relax_section
+ && S_GET_SEGMENT (symbolp) == segment
+ && S_IS_DEFINED (symbolp))
+ /* Attempt a PC-relative relaxation on a branch to a defined
+ symbol in the same segment. */
+ {
+ /* The relaxation for conditional branches is offset by 4
+ bytes because we insert the inverted branch around the
+ sequence. */
+ if (IS_CBRANCH (subtype))
+ offset = offset - 4;
+ if (offset > 0)
+ n = offset / 32767 + 1;
+ else
+ n = offset / -32768 + 1;
+
+ /* Bail out immediately if relaxation has failed. If we try to
+ defer the diagnostic to md_convert_frag, some pathological test
+ cases (e.g. gcc/testsuite/gcc.c-torture/compile/20001226-1.c)
+ apparently never converge. By returning 0 here we could pretend
+ to the caller that nothing has changed, but that leaves things
+ in an inconsistent state when we get to md_convert_frag. */
+ if (n > RELAX_MAX_ADDI)
+ {
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("branch offset out of range\n"));
+ as_fatal (_("branch relaxation failed\n"));
+ }
+ }
+ else
+ /* We cannot handle this case, diagnose overflow later. */
+ return 0;
+
+ if (IS_CBRANCH (subtype))
+ fragp->fr_subtype = CBRANCH_SUBTYPE (n);
+ else
+ fragp->fr_subtype = UBRANCH_SUBTYPE (n);
+
+ return (nios2_relax_subtype_size (fragp->fr_subtype)
+ - nios2_relax_subtype_size (subtype));
+ }
+
+ /* If we got here, it's probably an error. */
+ return 0;
+}
+
+
+/* Complete fragp using the data from the relaxation pass. */
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED,
+ fragS *fragp)
+{
+ char *buffer = fragp->fr_literal + fragp->fr_fix;
+ relax_substateT subtype = fragp->fr_subtype;
+ int n = SUBTYPE_ADDIS (subtype);
+ addressT target = fragp->fr_offset;
+ symbolS *symbolp = fragp->fr_symbol;
+ offsetT offset;
+ unsigned int addend_mask, addi_mask;
+ offsetT addend, remainder;
+ int i;
+
+ /* If we didn't or can't relax, this is a regular branch instruction.
+ We just need to generate the fixup for the symbol and offset. */
+ if (n == 0)
+ {
+ fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset, 1,
+ BFD_RELOC_16_PCREL);
+ fragp->fr_fix += 4;
+ return;
+ }
+
+ /* Replace the cbranch at fr_fix with one that has the opposite condition
+ in order to jump around the block of instructions we'll be adding. */
+ if (IS_CBRANCH (subtype))
+ {
+ unsigned int br_opcode;
+ int nbytes;
+
+ /* Account for the nextpc and jmp in the pc-relative case, or the two
+ load instructions and jump in the absolute case. */
+ if (nios2_as_options.relax == relax_section)
+ nbytes = (n + 2) * 4;
+ else
+ nbytes = 12;
+
+ br_opcode = md_chars_to_number (buffer, 4);
+ switch (br_opcode & OP_MASK_OP)
+ {
+ case OP_MATCH_BEQ:
+ br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BNE;
+ break;
+ case OP_MATCH_BNE:
+ br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BEQ ;
+ break;
+ case OP_MATCH_BGE:
+ br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BLT ;
+ break;
+ case OP_MATCH_BGEU:
+ br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BLTU ;
+ break;
+ case OP_MATCH_BLT:
+ br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BGE ;
+ break;
+ case OP_MATCH_BLTU:
+ br_opcode = (br_opcode & ~OP_MASK_OP) | OP_MATCH_BGEU ;
+ break;
+ default:
+ as_bad_where (fragp->fr_file, fragp->fr_line,
+ _("expecting conditional branch for relaxation\n"));
+ abort ();
+ }
+
+ br_opcode = br_opcode | (nbytes << OP_SH_IMM16);
+ md_number_to_chars (buffer, br_opcode, 4);
+ fragp->fr_fix += 4;
+ buffer += 4;
+ }
+
+ /* Load at for the PC-relative case. */
+ if (nios2_as_options.relax == relax_section)
+ {
+ /* Insert the nextpc instruction. */
+ md_number_to_chars (buffer,
+ OP_MATCH_NEXTPC | (AT_REGNUM << OP_SH_RRD), 4);
+ fragp->fr_fix += 4;
+ buffer += 4;
+
+ /* We need to know whether the offset is positive or negative. */
+ target += S_GET_VALUE (symbolp);
+ offset = target - fragp->fr_address - fragp->fr_fix;
+ if (offset > 0)
+ addend = 32767;
+ else
+ addend = -32768;
+ addend_mask = (((unsigned int)addend) & 0xffff) << OP_SH_IMM16;
+
+ /* Insert n-1 addi instructions. */
+ addi_mask = (OP_MATCH_ADDI
+ | (AT_REGNUM << OP_SH_IRD)
+ | (AT_REGNUM << OP_SH_IRS));
+ for (i = 0; i < n - 1; i ++)
+ {
+ md_number_to_chars (buffer, addi_mask | addend_mask, 4);
+ fragp->fr_fix += 4;
+ buffer += 4;
+ }
+
+ /* Insert the last addi instruction to hold the remainder. */
+ remainder = offset - addend * (n - 1);
+ gas_assert (remainder >= -32768 && remainder <= 32767);
+ addend_mask = (((unsigned int)remainder) & 0xffff) << OP_SH_IMM16;
+ md_number_to_chars (buffer, addi_mask | addend_mask, 4);
+ fragp->fr_fix += 4;
+ buffer += 4;
+ }
+
+ /* Load at for the absolute case. */
+ else
+ {
+ md_number_to_chars (buffer, OP_MATCH_ORHI | 0x00400000, 4);
+ fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset,
+ 0, BFD_RELOC_NIOS2_HI16);
+ fragp->fr_fix += 4;
+ buffer += 4;
+ md_number_to_chars (buffer, OP_MATCH_ORI | 0x08400000, 4);
+ fix_new (fragp, fragp->fr_fix, 4, fragp->fr_symbol, fragp->fr_offset,
+ 0, BFD_RELOC_NIOS2_LO16);
+ fragp->fr_fix += 4;
+ buffer += 4;
+ }
+
+ /* Insert the jmp instruction. */
+ md_number_to_chars (buffer, OP_MATCH_JMP | (AT_REGNUM << OP_SH_RRS), 4);
+ fragp->fr_fix += 4;
+ buffer += 4;
+}
+
+
+/** Fixups and overflow checking. */
+
+/* Check a fixup for overflow. */
+static bfd_boolean
+nios2_check_overflow (valueT fixup, reloc_howto_type *howto)
+{
+ /* Apply the rightshift before checking for overflow. */
+ fixup = ((signed)fixup) >> howto->rightshift;
+
+ /* Check for overflow - return TRUE if overflow, FALSE if not. */
+ switch (howto->complain_on_overflow)
+ {
+ case complain_overflow_dont:
+ break;
+ case complain_overflow_bitfield:
+ if ((fixup >> howto->bitsize) != 0
+ && ((signed) fixup >> howto->bitsize) != -1)
+ return TRUE;
+ break;
+ case complain_overflow_signed:
+ if ((fixup & 0x80000000) > 0)
+ {
+ /* Check for negative overflow. */
+ if ((signed) fixup < ((signed) 0x80000000 >> howto->bitsize))
+ return TRUE;
+ }
+ else
+ {
+ /* Check for positive overflow. */
+ if (fixup >= ((unsigned) 1 << (howto->bitsize - 1)))
+ return TRUE;
+ }
+ break;
+ case complain_overflow_unsigned:
+ if ((fixup >> howto->bitsize) != 0)
+ return TRUE;
+ break;
+ default:
+ as_bad (_("error checking for overflow - broken assembler"));
+ break;
+ }
+ return FALSE;
+}
+
+/* Emit diagnostic for fixup overflow. */
+static void
+nios2_diagnose_overflow (valueT fixup, reloc_howto_type *howto,
+ fixS *fixP, valueT value)
+{
+ if (fixP->fx_r_type == BFD_RELOC_8
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_32)
+ /* These relocs are against data, not instructions. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value 0x%x truncated to 0x%x"),
+ (unsigned int) fixup,
+ (unsigned int) (~(~(valueT) 0 << howto->bitsize) & fixup));
+ else
+ {
+ /* What opcode is the instruction? This will determine
+ whether we check for overflow in immediate values
+ and what error message we get. */
+ const struct nios2_opcode *opcode;
+ enum overflow_type overflow_msg_type;
+ unsigned int range_min;
+ unsigned int range_max;
+ unsigned int address;
+ gas_assert (fixP->fx_size == 4);
+ opcode = nios2_find_opcode_hash (value);
+ gas_assert (opcode);
+ overflow_msg_type = opcode->overflow_msg;
+ switch (overflow_msg_type)
+ {
+ case call_target_overflow:
+ range_min
+ = ((fixP->fx_frag->fr_address + fixP->fx_where) & 0xf0000000);
+ range_max = range_min + 0x0fffffff;
+ address = fixup | range_min;
+
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("call target address 0x%08x out of range 0x%08x to 0x%08x"),
+ address, range_min, range_max);
+ break;
+ case branch_target_overflow:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch offset %d out of range %d to %d"),
+ (int)fixup, -32768, 32767);
+ break;
+ case address_offset_overflow:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("%s offset %d out of range %d to %d"),
+ opcode->name, (int)fixup, -32768, 32767);
+ break;
+ case signed_immed16_overflow:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value %d out of range %d to %d"),
+ (int)fixup, -32768, 32767);
+ break;
+ case unsigned_immed16_overflow:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value %u out of range %u to %u"),
+ (unsigned int)fixup, 0, 65535);
+ break;
+ case unsigned_immed5_overflow:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value %u out of range %u to %u"),
+ (unsigned int)fixup, 0, 31);
+ break;
+ case custom_opcode_overflow:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("custom instruction opcode %u out of range %u to %u"),
+ (unsigned int)fixup, 0, 255);
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("overflow in immediate argument"));
+ break;
+ }
+ }
+}
+
+/* Apply a fixup to the object file. */
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ /* Assert that the fixup is one we can handle. */
+ gas_assert (fixP != NULL && valP != NULL
+ && (fixP->fx_r_type == BFD_RELOC_8
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_32
+ || fixP->fx_r_type == BFD_RELOC_64
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_S16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_U16
+ || fixP->fx_r_type == BFD_RELOC_16_PCREL
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL26
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_IMM5
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CACHE_OPX
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_IMM6
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_IMM8
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_HI16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_LO16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_HIADJ16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GPREL
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_UJMP
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CJMP
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CALLR
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_ALIGN
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GOT16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_LO
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_HA
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_GD16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LDM16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LDO16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_IE16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_LE16
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPREL
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL26_NOAT
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GOT_LO
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_GOT_HA
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL_LO
+ || fixP->fx_r_type == BFD_RELOC_NIOS2_CALL_HA
+ /* Add other relocs here as we generate them. */
+ ));
+
+ if (fixP->fx_r_type == BFD_RELOC_64)
+ {
+ /* We may reach here due to .8byte directives, but we never output
+ BFD_RELOC_64; it must be resolved. */
+ if (fixP->fx_addsy != NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot create 64-bit relocation"));
+ else
+ {
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ *valP, 8);
+ fixP->fx_done = 1;
+ }
+ return;
+ }
+
+ /* The value passed in valP can be the value of a fully
+ resolved expression, or it can be the value of a partially
+ resolved expression. In the former case, both fixP->fx_addsy
+ and fixP->fx_subsy are NULL, and fixP->fx_offset == *valP, and
+ we can fix up the instruction that fixP relates to.
+ In the latter case, one or both of fixP->fx_addsy and
+ fixP->fx_subsy are not NULL, and fixP->fx_offset may or may not
+ equal *valP. We don't need to check for fixP->fx_subsy being null
+ because the generic part of the assembler generates an error if
+ it is not an absolute symbol. */
+ if (fixP->fx_addsy != NULL)
+ /* Partially resolved expression. */
+ {
+ fixP->fx_addnumber = fixP->fx_offset;
+ fixP->fx_done = 0;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NIOS2_TLS_GD16:
+ case BFD_RELOC_NIOS2_TLS_LDM16:
+ case BFD_RELOC_NIOS2_TLS_LDO16:
+ case BFD_RELOC_NIOS2_TLS_IE16:
+ case BFD_RELOC_NIOS2_TLS_LE16:
+ case BFD_RELOC_NIOS2_TLS_DTPMOD:
+ case BFD_RELOC_NIOS2_TLS_DTPREL:
+ case BFD_RELOC_NIOS2_TLS_TPREL:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ /* Fully resolved fixup. */
+ {
+ reloc_howto_type *howto
+ = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+
+ if (howto == NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation is not supported"));
+ else
+ {
+ valueT fixup = *valP;
+ valueT value;
+ char *buf;
+
+ /* If this is a pc-relative relocation, we need to
+ subtract the current offset within the object file
+ FIXME : for some reason fixP->fx_pcrel isn't 1 when it should be
+ so I'm using the howto structure instead to determine this. */
+ if (howto->pc_relative == 1)
+ fixup = fixup - (fixP->fx_frag->fr_address + fixP->fx_where + 4);
+
+ /* Get the instruction or data to be fixed up. */
+ buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+ value = md_chars_to_number (buf, fixP->fx_size);
+
+ /* Check for overflow, emitting a diagnostic if necessary. */
+ if (nios2_check_overflow (fixup, howto))
+ nios2_diagnose_overflow (fixup, howto, fixP, value);
+
+ /* Apply the right shift. */
+ fixup = ((signed)fixup) >> howto->rightshift;
+
+ /* Truncate the fixup to right size. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NIOS2_HI16:
+ fixup = (fixup >> 16) & 0xFFFF;
+ break;
+ case BFD_RELOC_NIOS2_LO16:
+ fixup = fixup & 0xFFFF;
+ break;
+ case BFD_RELOC_NIOS2_HIADJ16:
+ fixup = ((((fixup >> 16) & 0xFFFF) + ((fixup >> 15) & 0x01))
+ & 0xFFFF);
+ break;
+ default:
+ {
+ int n = sizeof (fixup) * 8 - howto->bitsize;
+ fixup = (fixup << n) >> n;
+ break;
+ }
+ }
+
+ /* Fix up the instruction. */
+ value = (value & ~howto->dst_mask) | (fixup << howto->bitpos);
+ md_number_to_chars (buf, value, fixP->fx_size);
+ }
+
+ fixP->fx_done = 1;
+ }
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT)
+ {
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy
+ && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ fixP->fx_done = 0;
+}
+
+
+
+/** Instruction parsing support. */
+
+/* Special relocation directive strings. */
+
+struct nios2_special_relocS
+{
+ const char *string;
+ bfd_reloc_code_real_type reloc_type;
+};
+
+/* This table is sorted so that prefix strings are listed after the longer
+ strings that include them -- e.g., %got after %got_hiadj, etc. */
+
+struct nios2_special_relocS nios2_special_reloc[] = {
+ {"%hiadj", BFD_RELOC_NIOS2_HIADJ16},
+ {"%hi", BFD_RELOC_NIOS2_HI16},
+ {"%lo", BFD_RELOC_NIOS2_LO16},
+ {"%gprel", BFD_RELOC_NIOS2_GPREL},
+ {"%call_lo", BFD_RELOC_NIOS2_CALL_LO},
+ {"%call_hiadj", BFD_RELOC_NIOS2_CALL_HA},
+ {"%call", BFD_RELOC_NIOS2_CALL16},
+ {"%gotoff_lo", BFD_RELOC_NIOS2_GOTOFF_LO},
+ {"%gotoff_hiadj", BFD_RELOC_NIOS2_GOTOFF_HA},
+ {"%gotoff", BFD_RELOC_NIOS2_GOTOFF},
+ {"%got_hiadj", BFD_RELOC_NIOS2_GOT_HA},
+ {"%got_lo", BFD_RELOC_NIOS2_GOT_LO},
+ {"%got", BFD_RELOC_NIOS2_GOT16},
+ {"%tls_gd", BFD_RELOC_NIOS2_TLS_GD16},
+ {"%tls_ldm", BFD_RELOC_NIOS2_TLS_LDM16},
+ {"%tls_ldo", BFD_RELOC_NIOS2_TLS_LDO16},
+ {"%tls_ie", BFD_RELOC_NIOS2_TLS_IE16},
+ {"%tls_le", BFD_RELOC_NIOS2_TLS_LE16},
+};
+
+#define NIOS2_NUM_SPECIAL_RELOCS \
+ (sizeof(nios2_special_reloc)/sizeof(nios2_special_reloc[0]))
+const int nios2_num_special_relocs = NIOS2_NUM_SPECIAL_RELOCS;
+
+/* Creates a new nios2_insn_relocS and returns a pointer to it. */
+static nios2_insn_relocS *
+nios2_insn_reloc_new (bfd_reloc_code_real_type reloc_type, unsigned int pcrel)
+{
+ nios2_insn_relocS *retval;
+ retval = (nios2_insn_relocS *) malloc (sizeof (nios2_insn_relocS));
+ if (retval == NULL)
+ {
+ as_bad (_("can't create relocation"));
+ abort ();
+ }
+
+ /* Fill out the fields with default values. */
+ retval->reloc_next = NULL;
+ retval->reloc_type = reloc_type;
+ retval->reloc_pcrel = pcrel;
+ return retval;
+}
+
+/* Frees up memory previously allocated by nios2_insn_reloc_new(). */
+/* FIXME: this is never called; memory leak? */
+#if 0
+static void
+nios2_insn_reloc_destroy (nios2_insn_relocS *reloc)
+{
+ gas_assert (reloc != NULL);
+ free (reloc);
+}
+#endif
+
+/* The various nios2_assemble_* functions call this
+ function to generate an expression from a string representing an expression.
+ It then tries to evaluate the expression, and if it can, returns its value.
+ If not, it creates a new nios2_insn_relocS and stores the expression and
+ reloc_type for future use. */
+static unsigned long
+nios2_assemble_expression (const char *exprstr,
+ nios2_insn_infoS *insn,
+ nios2_insn_relocS *prev_reloc,
+ bfd_reloc_code_real_type reloc_type,
+ unsigned int pcrel)
+{
+ nios2_insn_relocS *reloc;
+ char *saved_line_ptr;
+ unsigned short value;
+ int i;
+
+ gas_assert (exprstr != NULL);
+ gas_assert (insn != NULL);
+
+ /* Check for relocation operators.
+ Change the relocation type and advance the ptr to the start of
+ the expression proper. */
+ for (i = 0; i < nios2_num_special_relocs; i++)
+ if (strstr (exprstr, nios2_special_reloc[i].string) != NULL)
+ {
+ reloc_type = nios2_special_reloc[i].reloc_type;
+ exprstr += strlen (nios2_special_reloc[i].string) + 1;
+
+ /* %lo and %hiadj have different meanings for PC-relative
+ expressions. */
+ if (pcrel)
+ {
+ if (reloc_type == BFD_RELOC_NIOS2_LO16)
+ reloc_type = BFD_RELOC_NIOS2_PCREL_LO;
+ if (reloc_type == BFD_RELOC_NIOS2_HIADJ16)
+ reloc_type = BFD_RELOC_NIOS2_PCREL_HA;
+ }
+
+ break;
+ }
+
+ /* We potentially have a relocation. */
+ reloc = nios2_insn_reloc_new (reloc_type, pcrel);
+ if (prev_reloc != NULL)
+ prev_reloc->reloc_next = reloc;
+ else
+ insn->insn_reloc = reloc;
+
+ /* Parse the expression string. */
+ saved_line_ptr = input_line_pointer;
+ input_line_pointer = (char *) exprstr;
+ expression (&reloc->reloc_expression);
+ input_line_pointer = saved_line_ptr;
+
+ /* This is redundant as the fixup will put this into
+ the instruction, but it is included here so that
+ self-test mode (-r) works. */
+ value = 0;
+ if (nios2_mode == NIOS2_MODE_TEST
+ && reloc->reloc_expression.X_op == O_constant)
+ value = reloc->reloc_expression.X_add_number;
+
+ return (unsigned long) value;
+}
+
+/* Argument assemble functions.
+ All take an instruction argument string, and a pointer
+ to an instruction opcode. Upon return the insn_opcode
+ has the relevant fields filled in to represent the arg
+ string. The return value is NULL if successful, or
+ an error message if an error was detected.
+
+ The naming conventions for these functions match the args template
+ in the nios2_opcode structure, as documented in include/opcode/nios2.h.
+ For example, nios2_assemble_args_dst is used for instructions with
+ "d,s,t" args.
+ See nios2_arg_info_structs below for the exact correspondence. */
+
+static void
+nios2_assemble_args_dst (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL
+ && insn_info->insn_tokens[2] != NULL
+ && insn_info->insn_tokens[3] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ struct nios2_reg *src2 = nios2_reg_lookup (insn_info->insn_tokens[3]);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index);
+
+ if (src1 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, src1->index);
+
+ if (src2 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[3]);
+ else
+ SET_INSN_FIELD (RRT, insn_info->insn_code, src2->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]);
+ }
+}
+
+static void
+nios2_assemble_args_tsi (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL &&
+ insn_info->insn_tokens[2] != NULL && insn_info->insn_tokens[3] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ unsigned int src2
+ = nios2_assemble_expression (insn_info->insn_tokens[3], insn_info,
+ insn_info->insn_reloc, BFD_RELOC_NIOS2_S16,
+ 0);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (IRT, insn_info->insn_code, dst->index);
+
+ if (src1 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (IRS, insn_info->insn_code, src1->index);
+
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, src2);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_tsu (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL
+ && insn_info->insn_tokens[2] != NULL
+ && insn_info->insn_tokens[3] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ unsigned int src2
+ = nios2_assemble_expression (insn_info->insn_tokens[3], insn_info,
+ insn_info->insn_reloc, BFD_RELOC_NIOS2_U16,
+ 0);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (IRT, insn_info->insn_code, dst->index);
+
+ if (src1 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (IRS, insn_info->insn_code, src1->index);
+
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, src2);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_sto (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL
+ && insn_info->insn_tokens[2] != NULL
+ && insn_info->insn_tokens[3] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ unsigned int src2
+ = nios2_assemble_expression (insn_info->insn_tokens[3], insn_info,
+ insn_info->insn_reloc, BFD_RELOC_16_PCREL,
+ 1);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (IRS, insn_info->insn_code, dst->index);
+
+ if (src1 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (IRT, insn_info->insn_code, src1->index);
+
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, src2);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_o (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL)
+ {
+ unsigned long immed
+ = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info,
+ insn_info->insn_reloc, BFD_RELOC_16_PCREL,
+ 1);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, immed);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_is (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL)
+ {
+ struct nios2_reg *addr_src = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ unsigned long immed
+ = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info,
+ insn_info->insn_reloc, BFD_RELOC_NIOS2_S16,
+ 0);
+
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, immed);
+
+ if (addr_src == NULL)
+ as_bad (_("unknown base register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, addr_src->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_m (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL)
+ {
+ unsigned long immed
+ = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info,
+ insn_info->insn_reloc,
+ (nios2_as_options.noat
+ ? BFD_RELOC_NIOS2_CALL26_NOAT
+ : BFD_RELOC_NIOS2_CALL26),
+ 0);
+
+ SET_INSN_FIELD (IMM26, insn_info->insn_code, immed);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]);
+ SET_INSN_FIELD (IMM26, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_s (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL)
+ {
+ struct nios2_reg *src = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ if (src == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, src->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]);
+ }
+}
+
+static void
+nios2_assemble_args_tis (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL
+ && insn_info->insn_tokens[2] != NULL
+ && insn_info->insn_tokens[3] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *addr_src = nios2_reg_lookup (insn_info->insn_tokens[3]);
+ unsigned long immed
+ = nios2_assemble_expression (insn_info->insn_tokens[2], insn_info,
+ insn_info->insn_reloc, BFD_RELOC_NIOS2_S16,
+ 0);
+
+ if (addr_src == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[3]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, addr_src->index);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RRT, insn_info->insn_code, dst->index);
+
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, immed);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]);
+ SET_INSN_FIELD (IMM16, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_dc (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL)
+ {
+ struct nios2_reg *ctl = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+
+ if (ctl == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RCTL, insn_info->insn_code, ctl->index);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]);
+ }
+}
+
+static void
+nios2_assemble_args_cs (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL)
+ {
+ struct nios2_reg *ctl = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src = nios2_reg_lookup (insn_info->insn_tokens[2]);
+
+ if (ctl == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else if (ctl->index == 4)
+ as_bad (_("ipending control register (ctl4) is read-only\n"));
+ else
+ SET_INSN_FIELD (RCTL, insn_info->insn_code, ctl->index);
+
+ if (src == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, src->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]);
+ }
+}
+
+static void
+nios2_assemble_args_ds (nios2_insn_infoS * insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL && insn_info->insn_tokens[2] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src = nios2_reg_lookup (insn_info->insn_tokens[2]);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index);
+
+ if (src == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, src->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[3]);
+ }
+}
+
+static void
+nios2_assemble_args_ldst (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL
+ && insn_info->insn_tokens[2] != NULL
+ && insn_info->insn_tokens[3] != NULL
+ && insn_info->insn_tokens[4] != NULL)
+ {
+ unsigned long custom_n
+ = nios2_assemble_expression (insn_info->insn_tokens[1], insn_info,
+ insn_info->insn_reloc,
+ BFD_RELOC_NIOS2_IMM8, 0);
+
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[2]);
+ struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[3]);
+ struct nios2_reg *src2 = nios2_reg_lookup (insn_info->insn_tokens[4]);
+
+ SET_INSN_FIELD (CUSTOM_N, insn_info->insn_code, custom_n);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index);
+
+ if (src1 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[3]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, src1->index);
+
+ if (src2 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[4]);
+ else
+ SET_INSN_FIELD (RRT, insn_info->insn_code, src2->index);
+
+ /* Set or clear the bits to indicate whether coprocessor registers are
+ used. */
+ if (nios2_coproc_reg (insn_info->insn_tokens[2]))
+ SET_INSN_FIELD (CUSTOM_C, insn_info->insn_code, 0);
+ else
+ SET_INSN_FIELD (CUSTOM_C, insn_info->insn_code, 1);
+
+ if (nios2_coproc_reg (insn_info->insn_tokens[3]))
+ SET_INSN_FIELD (CUSTOM_A, insn_info->insn_code, 0);
+ else
+ SET_INSN_FIELD (CUSTOM_A, insn_info->insn_code, 1);
+
+ if (nios2_coproc_reg (insn_info->insn_tokens[4]))
+ SET_INSN_FIELD (CUSTOM_B, insn_info->insn_code, 0);
+ else
+ SET_INSN_FIELD (CUSTOM_B, insn_info->insn_code, 1);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[5]);
+ }
+}
+
+static void
+nios2_assemble_args_none (nios2_insn_infoS *insn_info ATTRIBUTE_UNUSED)
+{
+ /* Nothing to do. */
+}
+
+static void
+nios2_assemble_args_dsj (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL
+ && insn_info->insn_tokens[2] != NULL
+ && insn_info->insn_tokens[3] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+ struct nios2_reg *src1 = nios2_reg_lookup (insn_info->insn_tokens[2]);
+
+ /* A 5-bit constant expression. */
+ unsigned int src2 =
+ nios2_assemble_expression (insn_info->insn_tokens[3], insn_info,
+ insn_info->insn_reloc,
+ BFD_RELOC_NIOS2_IMM5, 0);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index);
+
+ if (src1 == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[2]);
+ else
+ SET_INSN_FIELD (RRS, insn_info->insn_code, src1->index);
+
+ SET_INSN_FIELD (IMM5, insn_info->insn_code, src2);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[4]);
+ SET_INSN_FIELD (IMM5, insn_info->insn_code, 0);
+ }
+}
+
+static void
+nios2_assemble_args_d (nios2_insn_infoS *insn_info)
+{
+ if (insn_info->insn_tokens[1] != NULL)
+ {
+ struct nios2_reg *dst = nios2_reg_lookup (insn_info->insn_tokens[1]);
+
+ if (dst == NULL)
+ as_bad (_("unknown register %s"), insn_info->insn_tokens[1]);
+ else
+ SET_INSN_FIELD (RRD, insn_info->insn_code, dst->index);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]);
+ }
+}
+
+static void
+nios2_assemble_args_b (nios2_insn_infoS *insn_info)
+{
+ unsigned int imm5 = 0;
+
+ if (insn_info->insn_tokens[1] != NULL)
+ {
+ /* A 5-bit constant expression. */
+ imm5 = nios2_assemble_expression (insn_info->insn_tokens[1],
+ insn_info, insn_info->insn_reloc,
+ BFD_RELOC_NIOS2_IMM5, 0);
+ SET_INSN_FIELD (TRAP_IMM5, insn_info->insn_code, imm5);
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]);
+ }
+
+ SET_INSN_FIELD (TRAP_IMM5, insn_info->insn_code, imm5);
+
+ nios2_check_assembly (insn_info->insn_code, insn_info->insn_tokens[2]);
+}
+
+/* This table associates pointers to functions that parse the arguments to an
+ instruction and fill in the relevant fields of the instruction. */
+const nios2_arg_infoS nios2_arg_info_structs[] = {
+ /* args, assemble_args_func */
+ {"d,s,t", nios2_assemble_args_dst},
+ {"d,s,t,E", nios2_assemble_args_dst},
+ {"t,s,i", nios2_assemble_args_tsi},
+ {"t,s,i,E", nios2_assemble_args_tsi},
+ {"t,s,u", nios2_assemble_args_tsu},
+ {"t,s,u,E", nios2_assemble_args_tsu},
+ {"s,t,o", nios2_assemble_args_sto},
+ {"s,t,o,E", nios2_assemble_args_sto},
+ {"o", nios2_assemble_args_o},
+ {"o,E", nios2_assemble_args_o},
+ {"s", nios2_assemble_args_s},
+ {"s,E", nios2_assemble_args_s},
+ {"", nios2_assemble_args_none},
+ {"E", nios2_assemble_args_none},
+ {"i(s)", nios2_assemble_args_is},
+ {"i(s)E", nios2_assemble_args_is},
+ {"m", nios2_assemble_args_m},
+ {"m,E", nios2_assemble_args_m},
+ {"t,i(s)", nios2_assemble_args_tis},
+ {"t,i(s)E", nios2_assemble_args_tis},
+ {"d,c", nios2_assemble_args_dc},
+ {"d,c,E", nios2_assemble_args_dc},
+ {"c,s", nios2_assemble_args_cs},
+ {"c,s,E", nios2_assemble_args_cs},
+ {"d,s", nios2_assemble_args_ds},
+ {"d,s,E", nios2_assemble_args_ds},
+ {"l,d,s,t", nios2_assemble_args_ldst},
+ {"l,d,s,t,E", nios2_assemble_args_ldst},
+ {"d,s,j", nios2_assemble_args_dsj},
+ {"d,s,j,E", nios2_assemble_args_dsj},
+ {"d", nios2_assemble_args_d},
+ {"d,E", nios2_assemble_args_d},
+ {"b", nios2_assemble_args_b},
+ {"b,E", nios2_assemble_args_b}
+};
+
+#define NIOS2_NUM_ARGS \
+ ((sizeof(nios2_arg_info_structs)/sizeof(nios2_arg_info_structs[0])))
+const int nios2_num_arg_info_structs = NIOS2_NUM_ARGS;
+
+/* The function consume_arg takes a pointer into a string
+ of instruction tokens (args) and a pointer into a string
+ representing the expected sequence of tokens and separators.
+ It checks whether the first argument in argstr is of the
+ expected type, throwing an error if it is not, and returns
+ the pointer argstr. */
+static char *
+nios2_consume_arg (nios2_insn_infoS *insn, char *argstr, const char *parsestr)
+{
+ char *temp;
+ int regno = -1;
+
+ switch (*parsestr)
+ {
+ case 'c':
+ if (!nios2_control_register_arg_p (argstr))
+ as_bad (_("expecting control register"));
+ break;
+ case 'd':
+ case 's':
+ case 't':
+
+ /* We check to make sure we don't have a control register. */
+ if (nios2_control_register_arg_p (argstr))
+ as_bad (_("illegal use of control register"));
+
+ /* And whether coprocessor registers are valid here. */
+ if (nios2_coproc_reg (argstr)
+ && insn->insn_nios2_opcode->match != OP_MATCH_CUSTOM)
+ as_bad (_("illegal use of coprocessor register\n"));
+
+ /* Extract a register number if the register is of the
+ form r[0-9]+, if it is a normal register, set
+ regno to its number (0-31), else set regno to -1. */
+ if (argstr[0] == 'r' && ISDIGIT (argstr[1]))
+ {
+ char *p = argstr;
+
+ ++p;
+ regno = 0;
+ do
+ {
+ regno *= 10;
+ regno += *p - '0';
+ ++p;
+ }
+ while (ISDIGIT (*p));
+ }
+ else
+ regno = -1;
+
+ /* And whether we are using at. */
+ if (!nios2_as_options.noat
+ && (regno == 1 || strprefix (argstr, "at")))
+ as_warn (_("Register at (r1) can sometimes be corrupted by assembler "
+ "optimizations.\n"
+ "Use .set noat to turn off those optimizations (and this "
+ "warning)."));
+
+ /* And whether we are using oci registers. */
+ if (!nios2_as_options.nobreak
+ && (regno == 25 || strprefix (argstr, "bt")))
+ as_warn (_("The debugger will corrupt bt (r25).\n"
+ "If you don't need to debug this "
+ "code use .set nobreak to turn off this warning."));
+
+ if (!nios2_as_options.nobreak
+ && (regno == 30
+ || strprefix (argstr, "ba")
+ || strprefix (argstr, "sstatus")))
+ as_warn (_("The debugger will corrupt sstatus/ba (r30).\n"
+ "If you don't need to debug this "
+ "code use .set nobreak to turn off this warning."));
+ break;
+ case 'i':
+ case 'u':
+ if (*argstr == '%')
+ {
+ if (nios2_special_relocation_p (argstr))
+ {
+ /* We zap the parentheses because we don't want them confused
+ with separators. */
+ temp = strchr (argstr, '(');
+ if (temp != NULL)
+ *temp = ' ';
+ temp = strchr (argstr, ')');
+ if (temp != NULL)
+ *temp = ' ';
+ }
+ else
+ as_bad (_("badly formed expression near %s"), argstr);
+ }
+ break;
+ case 'm':
+ case 'j':
+ case 'l':
+ case 'b':
+ /* We can't have %hi, %lo or %hiadj here. */
+ if (*argstr == '%')
+ as_bad (_("badly formed expression near %s"), argstr);
+ break;
+ case 'o':
+ case 'E':
+ break;
+ default:
+ BAD_CASE (*parsestr);
+ break;
+ }
+
+ return argstr;
+}
+
+/* The function consume_separator takes a pointer into a string
+ of instruction tokens (args) and a pointer into a string representing
+ the expected sequence of tokens and separators. It finds the first
+ instance of the character pointed to by separator in argstr, and
+ returns a pointer to the next element of argstr, which is the
+ following token in the sequence. */
+static char *
+nios2_consume_separator (char *argstr, const char *separator)
+{
+ char *p;
+
+ /* If we have a opcode reg, expr(reg) type instruction, and
+ * we are separating the expr from the (reg), we find the last
+ * (, just in case the expression has parentheses. */
+
+ if (*separator == '(')
+ p = strrchr (argstr, *separator);
+ else
+ p = strchr (argstr, *separator);
+
+ if (p != NULL)
+ *p++ = 0;
+ else
+ as_bad (_("expecting %c near %s"), *separator, argstr);
+ return p;
+}
+
+
+/* The principal argument parsing function which takes a string argstr
+ representing the instruction arguments for insn, and extracts the argument
+ tokens matching parsestr into parsed_args. */
+static void
+nios2_parse_args (nios2_insn_infoS *insn, char *argstr,
+ const char *parsestr, char **parsed_args)
+{
+ char *p;
+ char *end = NULL;
+ int i;
+ p = argstr;
+ i = 0;
+ bfd_boolean terminate = FALSE;
+
+ /* This rest of this function is it too fragile and it mostly works,
+ therefore special case this one. */
+ if (*parsestr == 0 && argstr != 0)
+ {
+ as_bad (_("too many arguments"));
+ parsed_args[0] = NULL;
+ return;
+ }
+
+ while (p != NULL && !terminate && i < NIOS2_MAX_INSN_TOKENS)
+ {
+ parsed_args[i] = nios2_consume_arg (insn, p, parsestr);
+ ++parsestr;
+ if (*parsestr != '\0')
+ {
+ p = nios2_consume_separator (p, parsestr);
+ ++parsestr;
+ }
+ else
+ {
+ /* Check that the argument string has no trailing arguments. */
+ /* If we've got a %lo etc relocation, we've zapped the parens with
+ spaces. */
+ if (nios2_special_relocation_p (p))
+ end = strpbrk (p, ",");
+ else
+ end = strpbrk (p, " ,");
+
+ if (end != NULL)
+ as_bad (_("too many arguments"));
+ }
+
+ if (*parsestr == '\0' || (p != NULL && *p == '\0'))
+ terminate = TRUE;
+ ++i;
+ }
+
+ parsed_args[i] = NULL;
+
+ /* The argument to break and trap instructions is optional; complain
+ for other cases of missing arguments. */
+ if (*parsestr != '\0'
+ && insn->insn_nios2_opcode->match != OP_MATCH_BREAK
+ && insn->insn_nios2_opcode->match != OP_MATCH_TRAP)
+ as_bad (_("missing argument"));
+}
+
+
+
+/** Support for pseudo-op parsing. These are macro-like opcodes that
+ expand into real insns by suitable fiddling with the operands. */
+
+/* Append the string modifier to the string contained in the argument at
+ parsed_args[ndx]. */
+static void
+nios2_modify_arg (char **parsed_args, const char *modifier,
+ int unused ATTRIBUTE_UNUSED, int ndx)
+{
+ char *tmp = parsed_args[ndx];
+
+ parsed_args[ndx]
+ = (char *) malloc (strlen (parsed_args[ndx]) + strlen (modifier) + 1);
+ strcpy (parsed_args[ndx], tmp);
+ strcat (parsed_args[ndx], modifier);
+}
+
+/* Modify parsed_args[ndx] by negating that argument. */
+static void
+nios2_negate_arg (char **parsed_args, const char *modifier ATTRIBUTE_UNUSED,
+ int unused ATTRIBUTE_UNUSED, int ndx)
+{
+ char *tmp = parsed_args[ndx];
+
+ parsed_args[ndx]
+ = (char *) malloc (strlen ("~(") + strlen (parsed_args[ndx]) +
+ strlen (")+1") + 1);
+
+ strcpy (parsed_args[ndx], "~(");
+ strcat (parsed_args[ndx], tmp);
+ strcat (parsed_args[ndx], ")+1");
+}
+
+/* The function nios2_swap_args swaps the pointers at indices index_1 and
+ index_2 in the array parsed_args[] - this is used for operand swapping
+ for comparison operations. */
+static void
+nios2_swap_args (char **parsed_args, const char *unused ATTRIBUTE_UNUSED,
+ int index_1, int index_2)
+{
+ char *tmp;
+ gas_assert (index_1 < NIOS2_MAX_INSN_TOKENS
+ && index_2 < NIOS2_MAX_INSN_TOKENS);
+ tmp = parsed_args[index_1];
+ parsed_args[index_1] = parsed_args[index_2];
+ parsed_args[index_2] = tmp;
+}
+
+/* This function appends the string appnd to the array of strings in
+ parsed_args num times starting at index start in the array. */
+static void
+nios2_append_arg (char **parsed_args, const char *appnd, int num,
+ int start)
+{
+ int i, count;
+ char *tmp;
+
+ gas_assert ((start + num) < NIOS2_MAX_INSN_TOKENS);
+
+ if (nios2_mode == NIOS2_MODE_TEST)
+ tmp = parsed_args[start];
+ else
+ tmp = NULL;
+
+ for (i = start, count = num; count > 0; ++i, --count)
+ parsed_args[i] = (char *) appnd;
+
+ gas_assert (i == (start + num));
+ parsed_args[i] = tmp;
+ parsed_args[i + 1] = NULL;
+}
+
+/* This function inserts the string insert num times in the array
+ parsed_args, starting at the index start. */
+static void
+nios2_insert_arg (char **parsed_args, const char *insert, int num,
+ int start)
+{
+ int i, count;
+
+ gas_assert ((start + num) < NIOS2_MAX_INSN_TOKENS);
+
+ /* Move the existing arguments up to create space. */
+ for (i = NIOS2_MAX_INSN_TOKENS; i - num >= start; --i)
+ parsed_args[i] = parsed_args[i - num];
+
+ for (i = start, count = num; count > 0; ++i, --count)
+ parsed_args[i] = (char *) insert;
+}
+
+/* Cleanup function to free malloc'ed arg strings. */
+static void
+nios2_free_arg (char **parsed_args, int num ATTRIBUTE_UNUSED, int start)
+{
+ if (parsed_args[start])
+ {
+ free (parsed_args[start]);
+ parsed_args[start] = NULL;
+ }
+}
+
+/* This function swaps the pseudo-op for a real op. */
+static nios2_ps_insn_infoS*
+nios2_translate_pseudo_insn (nios2_insn_infoS *insn)
+{
+
+ nios2_ps_insn_infoS *ps_insn;
+
+ /* Find which real insn the pseudo-op transates to and
+ switch the insn_info ptr to point to it. */
+ ps_insn = nios2_ps_lookup (insn->insn_nios2_opcode->name);
+
+ if (ps_insn != NULL)
+ {
+ insn->insn_nios2_opcode = nios2_opcode_lookup (ps_insn->insn);
+ insn->insn_tokens[0] = insn->insn_nios2_opcode->name;
+ /* Modify the args so they work with the real insn. */
+ ps_insn->arg_modifer_func ((char **) insn->insn_tokens,
+ ps_insn->arg_modifier, ps_insn->num,
+ ps_insn->index);
+ }
+ else
+ /* we cannot recover from this. */
+ as_fatal (_("unrecognized pseudo-instruction %s"),
+ ps_insn->pseudo_insn);
+ return ps_insn;
+}
+
+/* Invoke the cleanup handler for pseudo-insn ps_insn on insn. */
+static void
+nios2_cleanup_pseudo_insn (nios2_insn_infoS *insn,
+ nios2_ps_insn_infoS *ps_insn)
+{
+ if (ps_insn->arg_cleanup_func)
+ (ps_insn->arg_cleanup_func) ((char **) insn->insn_tokens,
+ ps_insn->num, ps_insn->index);
+}
+
+const nios2_ps_insn_infoS nios2_ps_insn_info_structs[] = {
+ /* pseudo-op, real-op, arg, arg_modifier_func, num, index, arg_cleanup_func */
+ {"mov", "add", nios2_append_arg, "zero", 1, 3, NULL},
+ {"movi", "addi", nios2_insert_arg, "zero", 1, 2, NULL},
+ {"movhi", "orhi", nios2_insert_arg, "zero", 1, 2, NULL},
+ {"movui", "ori", nios2_insert_arg, "zero", 1, 2, NULL},
+ {"movia", "orhi", nios2_insert_arg, "zero", 1, 2, NULL},
+ {"nop", "add", nios2_append_arg, "zero", 3, 1, NULL},
+ {"bgt", "blt", nios2_swap_args, "", 1, 2, NULL},
+ {"bgtu", "bltu", nios2_swap_args, "", 1, 2, NULL},
+ {"ble", "bge", nios2_swap_args, "", 1, 2, NULL},
+ {"bleu", "bgeu", nios2_swap_args, "", 1, 2, NULL},
+ {"cmpgt", "cmplt", nios2_swap_args, "", 2, 3, NULL},
+ {"cmpgtu", "cmpltu", nios2_swap_args, "", 2, 3, NULL},
+ {"cmple", "cmpge", nios2_swap_args, "", 2, 3, NULL},
+ {"cmpleu", "cmpgeu", nios2_swap_args, "", 2, 3, NULL},
+ {"cmpgti", "cmpgei", nios2_modify_arg, "+1", 0, 3, nios2_free_arg},
+ {"cmpgtui", "cmpgeui", nios2_modify_arg, "+1", 0, 3, nios2_free_arg},
+ {"cmplei", "cmplti", nios2_modify_arg, "+1", 0, 3, nios2_free_arg},
+ {"cmpleui", "cmpltui", nios2_modify_arg, "+1", 0, 3, nios2_free_arg},
+ {"subi", "addi", nios2_negate_arg, "", 0, 3, nios2_free_arg}
+ /* Add further pseudo-ops here. */
+};
+
+#define NIOS2_NUM_PSEUDO_INSNS \
+ ((sizeof(nios2_ps_insn_info_structs)/ \
+ sizeof(nios2_ps_insn_info_structs[0])))
+const int nios2_num_ps_insn_info_structs = NIOS2_NUM_PSEUDO_INSNS;
+
+
+/** Assembler output support. */
+
+static int
+can_evaluate_expr (nios2_insn_infoS *insn)
+{
+ /* Remove this check for null and the invalid insn "ori r9, 1234" seg faults. */
+ if (!insn->insn_reloc)
+ /* ??? Ideally we should do something other than as_fatal here as we can
+ continue to assemble.
+ However this function (actually the output_* functions) should not
+ have been called in the first place once an illegal instruction had
+ been encountered. */
+ as_fatal (_("Invalid instruction encountered, cannot recover. No assembly attempted."));
+
+ if (insn->insn_reloc->reloc_expression.X_op == O_constant)
+ return 1;
+
+ return 0;
+}
+
+static int
+get_expr_value (nios2_insn_infoS *insn)
+{
+ int value = 0;
+
+ if (insn->insn_reloc->reloc_expression.X_op == O_constant)
+ value = insn->insn_reloc->reloc_expression.X_add_number;
+ return value;
+}
+
+/* Output a normal instruction. */
+static void
+output_insn (nios2_insn_infoS *insn)
+{
+ char *f;
+ nios2_insn_relocS *reloc;
+
+ f = frag_more (4);
+ /* This allocates enough space for the instruction
+ and puts it in the current frag. */
+ md_number_to_chars (f, insn->insn_code, 4);
+ /* Emit debug info. */
+ dwarf2_emit_insn (4);
+ /* Create any fixups to be acted on later. */
+ for (reloc = insn->insn_reloc; reloc != NULL; reloc = reloc->reloc_next)
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+ &reloc->reloc_expression, reloc->reloc_pcrel,
+ reloc->reloc_type);
+}
+
+/* Output an unconditional branch. */
+static void
+output_ubranch (nios2_insn_infoS *insn)
+{
+ nios2_insn_relocS *reloc = insn->insn_reloc;
+
+ /* If the reloc is NULL, there was an error assembling the branch. */
+ if (reloc != NULL)
+ {
+ symbolS *symp = reloc->reloc_expression.X_add_symbol;
+ offsetT offset = reloc->reloc_expression.X_add_number;
+ char *f;
+
+ /* Tag dwarf2 debug info to the address at the start of the insn.
+ We must do it before frag_var() below closes off the frag. */
+ dwarf2_emit_insn (0);
+
+ /* We create a machine dependent frag which can grow
+ to accommodate the largest possible instruction sequence
+ this may generate. */
+ f = frag_var (rs_machine_dependent,
+ UBRANCH_MAX_SIZE, 4, UBRANCH_SUBTYPE (0),
+ symp, offset, NULL);
+
+ md_number_to_chars (f, insn->insn_code, 4);
+
+ /* We leave fixup generation to md_convert_frag. */
+ }
+}
+
+/* Output a conditional branch. */
+static void
+output_cbranch (nios2_insn_infoS *insn)
+{
+ nios2_insn_relocS *reloc = insn->insn_reloc;
+
+ /* If the reloc is NULL, there was an error assembling the branch. */
+ if (reloc != NULL)
+ {
+ symbolS *symp = reloc->reloc_expression.X_add_symbol;
+ offsetT offset = reloc->reloc_expression.X_add_number;
+ char *f;
+
+ /* Tag dwarf2 debug info to the address at the start of the insn.
+ We must do it before frag_var() below closes off the frag. */
+ dwarf2_emit_insn (0);
+
+ /* We create a machine dependent frag which can grow
+ to accommodate the largest possible instruction sequence
+ this may generate. */
+ f = frag_var (rs_machine_dependent,
+ CBRANCH_MAX_SIZE, 4, CBRANCH_SUBTYPE (0),
+ symp, offset, NULL);
+
+ md_number_to_chars (f, insn->insn_code, 4);
+
+ /* We leave fixup generation to md_convert_frag. */
+ }
+}
+
+/* Output a call sequence. Since calls are not pc-relative for NIOS2,
+ but are page-relative, we cannot tell at any stage in assembly
+ whether a call will be out of range since a section may be linked
+ at any address. So if we are relaxing, we convert all call instructions
+ to long call sequences, and rely on the linker to relax them back to
+ short calls. */
+static void
+output_call (nios2_insn_infoS *insn)
+{
+ /* This allocates enough space for the instruction
+ and puts it in the current frag. */
+ char *f = frag_more (12);
+ nios2_insn_relocS *reloc = insn->insn_reloc;
+
+ md_number_to_chars (f, OP_MATCH_ORHI | 0x00400000, 4);
+ dwarf2_emit_insn (4);
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4,
+ &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_HI16);
+ md_number_to_chars (f + 4, OP_MATCH_ORI | 0x08400000, 4);
+ dwarf2_emit_insn (4);
+ fix_new_exp (frag_now, f - frag_now->fr_literal + 4, 4,
+ &reloc->reloc_expression, 0, BFD_RELOC_NIOS2_LO16);
+ md_number_to_chars (f + 8, OP_MATCH_CALLR | 0x08000000, 4);
+ dwarf2_emit_insn (4);
+}
+
+/* Output an addi - will silently convert to
+ orhi if rA = r0 and (expr & 0xffff0000) == 0. */
+static void
+output_addi (nios2_insn_infoS *insn)
+{
+ if (can_evaluate_expr (insn))
+ {
+ int expr_val = get_expr_value (insn);
+ if (GET_INSN_FIELD (RRS, insn->insn_code) == 0
+ && (expr_val & 0xffff) == 0
+ && expr_val != 0)
+ {
+ /* We really want a movhi (orhi) here. */
+ insn->insn_code = (insn->insn_code & ~OP_MATCH_ADDI) | OP_MATCH_ORHI;
+ insn->insn_reloc->reloc_expression.X_add_number =
+ (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff;
+ insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16;
+ }
+ }
+
+ /* Output an instruction. */
+ output_insn (insn);
+}
+
+static void
+output_andi (nios2_insn_infoS *insn)
+{
+ if (can_evaluate_expr (insn))
+ {
+ int expr_val = get_expr_value (insn);
+ if (expr_val != 0 && (expr_val & 0xffff) == 0)
+ {
+ /* We really want a movhi (orhi) here. */
+ insn->insn_code = (insn->insn_code & ~OP_MATCH_ANDI) | OP_MATCH_ANDHI;
+ insn->insn_reloc->reloc_expression.X_add_number =
+ (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff;
+ insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16;
+ }
+ }
+
+ /* Output an instruction. */
+ output_insn (insn);
+}
+
+static void
+output_ori (nios2_insn_infoS *insn)
+{
+ if (can_evaluate_expr (insn))
+ {
+ int expr_val = get_expr_value (insn);
+ if (expr_val != 0 && (expr_val & 0xffff) == 0)
+ {
+ /* We really want a movhi (orhi) here. */
+ insn->insn_code = (insn->insn_code & ~OP_MATCH_ORI) | OP_MATCH_ORHI;
+ insn->insn_reloc->reloc_expression.X_add_number =
+ (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff;
+ insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16;
+ }
+ }
+
+ /* Output an instruction. */
+ output_insn (insn);
+}
+
+static void
+output_xori (nios2_insn_infoS *insn)
+{
+ if (can_evaluate_expr (insn))
+ {
+ int expr_val = get_expr_value (insn);
+ if (expr_val != 0 && (expr_val & 0xffff) == 0)
+ {
+ /* We really want a movhi (orhi) here. */
+ insn->insn_code = (insn->insn_code & ~OP_MATCH_XORI) | OP_MATCH_XORHI;
+ insn->insn_reloc->reloc_expression.X_add_number =
+ (insn->insn_reloc->reloc_expression.X_add_number >> 16) & 0xffff;
+ insn->insn_reloc->reloc_type = BFD_RELOC_NIOS2_U16;
+ }
+ }
+
+ /* Output an instruction. */
+ output_insn (insn);
+}
+
+
+/* Output a movhi/addi pair for the movia pseudo-op. */
+static void
+output_movia (nios2_insn_infoS *insn)
+{
+ /* This allocates enough space for the instruction
+ and puts it in the current frag. */
+ char *f = frag_more (8);
+ nios2_insn_relocS *reloc = insn->insn_reloc;
+ unsigned long reg_index = GET_INSN_FIELD (IRT, insn->insn_code);
+
+ /* If the reloc is NULL, there was an error assembling the movia. */
+ if (reloc != NULL)
+ {
+ md_number_to_chars (f, insn->insn_code, 4);
+ dwarf2_emit_insn (4);
+ md_number_to_chars (f + 4,
+ (OP_MATCH_ADDI | (reg_index << OP_SH_IRT)
+ | (reg_index << OP_SH_IRS)),
+ 4);
+ dwarf2_emit_insn (4);
+ fix_new (frag_now, f - frag_now->fr_literal, 4,
+ reloc->reloc_expression.X_add_symbol,
+ reloc->reloc_expression.X_add_number, 0,
+ BFD_RELOC_NIOS2_HIADJ16);
+ fix_new (frag_now, f + 4 - frag_now->fr_literal, 4,
+ reloc->reloc_expression.X_add_symbol,
+ reloc->reloc_expression.X_add_number, 0, BFD_RELOC_NIOS2_LO16);
+ }
+}
+
+
+
+/** External interfaces. */
+
+/* The following functions are called by machine-independent parts of
+ the assembler. */
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case 'r':
+ /* Hidden option for self-test mode. */
+ nios2_mode = NIOS2_MODE_TEST;
+ break;
+ case OPTION_RELAX_ALL:
+ nios2_as_options.relax = relax_all;
+ break;
+ case OPTION_NORELAX:
+ nios2_as_options.relax = relax_none;
+ break;
+ case OPTION_RELAX_SECTION:
+ nios2_as_options.relax = relax_section;
+ break;
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+ default:
+ return 0;
+ break;
+ }
+
+ return 1;
+}
+
+/* Implement TARGET_FORMAT. We can choose to be big-endian or
+ little-endian at runtime based on a switch. */
+const char *
+nios2_target_format (void)
+{
+ return target_big_endian ? "elf32-bignios2" : "elf32-littlenios2";
+}
+
+/* Machine-dependent usage message. */
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, " NIOS2 options:\n"
+ " -relax-all replace all branch and call "
+ "instructions with jmp and callr sequences\n"
+ " -relax-section replace identified out of range "
+ "branches with jmp sequences (default)\n"
+ " -no-relax do not replace any branches or calls\n"
+ " -EB force big-endian byte ordering\n"
+ " -EL force little-endian byte ordering\n");
+}
+
+/* This function is called once, at assembler startup time.
+ It should set up all the tables, etc. that the MD part of the
+ assembler will need. */
+void
+md_begin (void)
+{
+ int i;
+ const char *inserted;
+
+ /* Create and fill a hashtable for the Nios II opcodes, registers and
+ arguments. */
+ nios2_opcode_hash = hash_new ();
+ nios2_reg_hash = hash_new ();
+ nios2_arg_hash = hash_new ();
+ nios2_ps_hash = hash_new ();
+
+ for (i = 0; i < NUMOPCODES; ++i)
+ {
+ inserted
+ = hash_insert (nios2_opcode_hash, nios2_opcodes[i].name,
+ (PTR) & nios2_opcodes[i]);
+ if (inserted != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ nios2_opcodes[i].name, inserted);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ }
+
+ for (i = 0; i < nios2_num_regs; ++i)
+ {
+ inserted
+ = hash_insert (nios2_reg_hash, nios2_regs[i].name,
+ (PTR) & nios2_regs[i]);
+ if (inserted != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ nios2_regs[i].name, inserted);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+
+ }
+
+ for (i = 0; i < nios2_num_arg_info_structs; ++i)
+ {
+ inserted
+ = hash_insert (nios2_arg_hash, nios2_arg_info_structs[i].args,
+ (PTR) & nios2_arg_info_structs[i]);
+ if (inserted != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ nios2_arg_info_structs[i].args, inserted);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ }
+
+ for (i = 0; i < nios2_num_ps_insn_info_structs; ++i)
+ {
+ inserted
+ = hash_insert (nios2_ps_hash, nios2_ps_insn_info_structs[i].pseudo_insn,
+ (PTR) & nios2_ps_insn_info_structs[i]);
+ if (inserted != NULL)
+ {
+ fprintf (stderr, _("internal error: can't hash `%s': %s\n"),
+ nios2_ps_insn_info_structs[i].pseudo_insn, inserted);
+ /* Probably a memory allocation problem? Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ }
+
+ /* Assembler option defaults. */
+ nios2_as_options.noat = FALSE;
+ nios2_as_options.nobreak = FALSE;
+
+ /* Debug information is incompatible with relaxation. */
+ if (debug_type != DEBUG_UNSPECIFIED)
+ nios2_as_options.relax = relax_none;
+
+ /* Initialize the alignment data. */
+ nios2_current_align_seg = now_seg;
+ nios2_last_label = NULL;
+ nios2_current_align = 0;
+}
+
+
+/* Assembles a single line of Nios II assembly language. */
+void
+md_assemble (char *op_str)
+{
+ char *argstr;
+ char *op_strdup = NULL;
+ nios2_arg_infoS *arg_info;
+ unsigned long saved_pinfo = 0;
+ nios2_insn_infoS thisinsn;
+ nios2_insn_infoS *insn = &thisinsn;
+
+ /* Make sure we are aligned on a 4-byte boundary. */
+ if (nios2_current_align < 2)
+ nios2_align (2, NULL, nios2_last_label);
+ else if (nios2_current_align > 2)
+ nios2_current_align = 2;
+ nios2_last_label = NULL;
+
+ /* We don't want to clobber to op_str
+ because we want to be able to use it in messages. */
+ op_strdup = strdup (op_str);
+ insn->insn_tokens[0] = strtok (op_strdup, " ");
+ argstr = strtok (NULL, "");
+
+ /* Assemble the opcode. */
+ insn->insn_nios2_opcode = nios2_opcode_lookup (insn->insn_tokens[0]);
+ insn->insn_reloc = NULL;
+
+ if (insn->insn_nios2_opcode != NULL)
+ {
+ nios2_ps_insn_infoS *ps_insn = NULL;
+ /* Set the opcode for the instruction. */
+ insn->insn_code = insn->insn_nios2_opcode->match;
+
+ /* Parse the arguments pointed to by argstr. */
+ if (nios2_mode == NIOS2_MODE_ASSEMBLE)
+ nios2_parse_args (insn, argstr, insn->insn_nios2_opcode->args,
+ (char **) &insn->insn_tokens[1]);
+ else
+ nios2_parse_args (insn, argstr, insn->insn_nios2_opcode->args_test,
+ (char **) &insn->insn_tokens[1]);
+
+ /* We need to preserve the MOVIA macro as this is clobbered by
+ translate_pseudo_insn. */
+ if (insn->insn_nios2_opcode->pinfo == NIOS2_INSN_MACRO_MOVIA)
+ saved_pinfo = NIOS2_INSN_MACRO_MOVIA;
+ /* If the instruction is an pseudo-instruction, we want to replace it
+ with its real equivalent, and then continue. */
+ if ((insn->insn_nios2_opcode->pinfo & NIOS2_INSN_MACRO)
+ == NIOS2_INSN_MACRO)
+ ps_insn = nios2_translate_pseudo_insn (insn);
+
+ /* Find the assemble function, and call it. */
+ arg_info = nios2_arg_lookup (insn->insn_nios2_opcode->args);
+ if (arg_info != NULL)
+ {
+ arg_info->assemble_args_func (insn);
+
+ if (nios2_as_options.relax != relax_none
+ && !nios2_as_options.noat
+ && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_UBRANCH)
+ output_ubranch (insn);
+ else if (nios2_as_options.relax != relax_none
+ && !nios2_as_options.noat
+ && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CBRANCH)
+ output_cbranch (insn);
+ else if (nios2_as_options.relax == relax_all
+ && !nios2_as_options.noat
+ && insn->insn_nios2_opcode->pinfo & NIOS2_INSN_CALL
+ && insn->insn_reloc
+ && ((insn->insn_reloc->reloc_type
+ == BFD_RELOC_NIOS2_CALL26)
+ || (insn->insn_reloc->reloc_type
+ == BFD_RELOC_NIOS2_CALL26_NOAT)))
+ output_call (insn);
+ else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_ANDI)
+ output_andi (insn);
+ else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_ORI)
+ output_ori (insn);
+ else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_XORI)
+ output_xori (insn);
+ else if (insn->insn_nios2_opcode->pinfo & NIOS2_INSN_ADDI)
+ output_addi (insn);
+ else if (saved_pinfo == NIOS2_INSN_MACRO_MOVIA)
+ output_movia (insn);
+ else
+ output_insn (insn);
+ if (ps_insn)
+ nios2_cleanup_pseudo_insn (insn, ps_insn);
+ }
+ else
+ {
+ /* The assembler is broken. */
+ fprintf (stderr,
+ _("internal error: %s is not a valid argument syntax\n"),
+ insn->insn_nios2_opcode->args);
+ /* Probably a memory allocation problem. Give up now. */
+ as_fatal (_("Broken assembler. No assembly attempted."));
+ }
+ }
+ else
+ /* Unrecognised instruction - error. */
+ as_bad (_("unrecognised instruction %s"), insn->insn_tokens[0]);
+
+ /* Don't leak memory. */
+ free (op_strdup);
+}
+
+/* Round up section size. */
+valueT
+md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT size)
+{
+ /* I think byte alignment is fine here. */
+ return size;
+}
+
+/* Implement TC_FORCE_RELOCATION. */
+int
+nios2_force_relocation (fixS *fixp)
+{
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_ALIGN)
+ return 1;
+
+ return generic_force_reloc (fixp);
+}
+
+/* Implement tc_fix_adjustable. */
+int
+nios2_fix_adjustable (fixS *fixp)
+{
+ if (fixp->fx_addsy == NULL)
+ return 1;
+
+#ifdef OBJ_ELF
+ /* Prevent all adjustments to global symbols. */
+ if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && (S_IS_EXTERNAL (fixp->fx_addsy) || S_IS_WEAK (fixp->fx_addsy)))
+ return 0;
+#endif
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ /* Preserve relocations against symbols with function type. */
+ if (symbol_get_bfdsym (fixp->fx_addsy)->flags & BSF_FUNCTION)
+ return 0;
+
+ /* Don't allow symbols to be discarded on GOT related relocs. */
+ if (fixp->fx_r_type == BFD_RELOC_NIOS2_GOT16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_CALL16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_LO
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF_HA
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_GD16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LDM16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LDO16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_IE16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_LE16
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPMOD
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_DTPREL
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_TLS_TPREL
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOTOFF
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOT_LO
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_GOT_HA
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_CALL_LO
+ || fixp->fx_r_type == BFD_RELOC_NIOS2_CALL_HA
+ )
+ return 0;
+
+ return 1;
+}
+
+/* Implement tc_frob_symbol. This is called in adjust_reloc_syms;
+ it is used to remove *ABS* references from the symbol table. */
+int
+nios2_frob_symbol (symbolS *symp)
+{
+ if ((OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && symp == section_symbol (absolute_section))
+ || !S_IS_DEFINED (symp))
+ return 1;
+ else
+ return 0;
+}
+
+/* The function tc_gen_reloc creates a relocation structure for the
+ fixup fixp, and returns a pointer to it. This structure is passed
+ to bfd_install_relocation so that it can be written to the object
+ file for linking. */
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = fixp->fx_offset; /* fixp->fx_addnumber; */
+
+ if (fixp->fx_pcrel)
+ {
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_16:
+ fixp->fx_r_type = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_NIOS2_LO16:
+ fixp->fx_r_type = BFD_RELOC_NIOS2_PCREL_LO;
+ break;
+ case BFD_RELOC_NIOS2_HIADJ16:
+ fixp->fx_r_type = BFD_RELOC_NIOS2_PCREL_HA;
+ break;
+ default:
+ break;
+ }
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("can't represent relocation type %s"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+
+ /* Set howto to a garbage value so that we can keep going. */
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (reloc->howto != NULL);
+ }
+ return reloc;
+}
+
+long
+md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Called just before the assembler exits. */
+void
+md_end ()
+{
+ /* FIXME - not yet implemented */
+}
+
+/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
+ Otherwise we have no need to default values of symbols. */
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_ELF
+ if (name[0] == '_' && name[1] == 'G'
+ && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad ("GOT already in the symbol table");
+
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ }
+
+ return GOT_symbol;
+ }
+#endif
+
+ return 0;
+}
+
+/* Implement tc_frob_label. */
+void
+nios2_frob_label (symbolS *lab)
+{
+ /* Emit dwarf information. */
+ dwarf2_emit_label (lab);
+
+ /* Update the label's address with the current output pointer. */
+ symbol_set_frag (lab, frag_now);
+ S_SET_VALUE (lab, (valueT) frag_now_fix ());
+
+ /* Record this label for future adjustment after we find out what
+ kind of data it references, and the required alignment therewith. */
+ nios2_last_label = lab;
+}
+
+/* Implement md_cons_align. */
+void
+nios2_cons_align (int size)
+{
+ int log_size = 0;
+ const char *pfill = NULL;
+
+ while ((size >>= 1) != 0)
+ ++log_size;
+
+ if (subseg_text_p (now_seg))
+ pfill = (const char *) &nop;
+ else
+ pfill = NULL;
+
+ if (nios2_auto_align_on)
+ nios2_align (log_size, pfill, NULL);
+
+ nios2_last_label = NULL;
+}
+
+/* Map 's' to SHF_NIOS2_GPREL. */
+/* This is from the Alpha code tc-alpha.c. */
+int
+nios2_elf_section_letter (int letter, char **ptr_msg)
+{
+ if (letter == 's')
+ return SHF_NIOS2_GPREL;
+
+ *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string");
+ return -1;
+}
+
+/* Map SHF_ALPHA_GPREL to SEC_SMALL_DATA. */
+/* This is from the Alpha code tc-alpha.c. */
+flagword
+nios2_elf_section_flags (flagword flags, int attr, int type ATTRIBUTE_UNUSED)
+{
+ if (attr & SHF_NIOS2_GPREL)
+ flags |= SEC_SMALL_DATA;
+ return flags;
+}
+
+/* Implement TC_PARSE_CONS_EXPRESSION to handle %tls_ldo(...) */
+bfd_reloc_code_real_type
+nios2_cons (expressionS *exp, int size)
+{
+ bfd_reloc_code_real_type nios2_tls_ldo_reloc = BFD_RELOC_NONE;
+
+ SKIP_WHITESPACE ();
+ if (input_line_pointer[0] == '%')
+ {
+ if (strprefix (input_line_pointer + 1, "tls_ldo"))
+ {
+ if (size != 4)
+ as_bad (_("Illegal operands: %%tls_ldo in %d-byte data field"),
+ size);
+ else
+ {
+ input_line_pointer += 8;
+ nios2_tls_ldo_reloc = BFD_RELOC_NIOS2_TLS_DTPREL;
+ }
+ }
+ if (nios2_tls_ldo_reloc != BFD_RELOC_NONE)
+ {
+ SKIP_WHITESPACE ();
+ if (input_line_pointer[0] != '(')
+ as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()"));
+ else
+ {
+ int c;
+ char *end = ++input_line_pointer;
+ int npar = 0;
+
+ for (c = *end; !is_end_of_line[c]; end++, c = *end)
+ if (c == '(')
+ npar++;
+ else if (c == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+
+ if (c != ')')
+ as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()"));
+ else
+ {
+ *end = '\0';
+ expression (exp);
+ *end = c;
+ if (input_line_pointer != end)
+ as_bad (_("Illegal operands: %%tls_ldo requires arguments in ()"));
+ else
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (! is_end_of_line[c] && c != ',')
+ as_bad (_("Illegal operands: garbage after %%tls_ldo()"));
+ }
+ }
+ }
+ }
+ }
+ if (nios2_tls_ldo_reloc == BFD_RELOC_NONE)
+ expression (exp);
+ return nios2_tls_ldo_reloc;
+}
+
+/* Implement HANDLE_ALIGN. */
+void
+nios2_handle_align (fragS *fragp)
+{
+ /* If we are expecting to relax in the linker, then we must output a
+ relocation to tell the linker we are aligning code. */
+ if (nios2_as_options.relax == relax_all
+ && (fragp->fr_type == rs_align || fragp->fr_type == rs_align_code)
+ && fragp->fr_address + fragp->fr_fix > 0
+ && fragp->fr_offset > 1
+ && now_seg != bss_section)
+ fix_new (fragp, fragp->fr_fix, 0, &abs_symbol, fragp->fr_offset, 0,
+ BFD_RELOC_NIOS2_ALIGN);
+}
+
+/* Implement tc_regname_to_dw2regnum, to convert REGNAME to a DWARF-2
+ register number. */
+int
+nios2_regname_to_dw2regnum (char *regname)
+{
+ struct nios2_reg *r = nios2_reg_lookup (regname);
+ if (r == NULL)
+ return -1;
+ return r->index;
+}
+
+/* Implement tc_cfi_frame_initial_instructions, to initialize the DWARF-2
+ unwind information for this procedure. */
+void
+nios2_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (27, 0);
+}
diff --git a/gas/config/tc-nios2.h b/gas/config/tc-nios2.h
new file mode 100644
index 0000000..82bb624
--- /dev/null
+++ b/gas/config/tc-nios2.h
@@ -0,0 +1,121 @@
+/* Definitions for Altera Nios II assembler.
+ Copyright (C) 2012-2014 Free Software Foundation, Inc.
+ Contributed by Nigel Gray (ngray@altera.com).
+ Contributed by Mentor Graphics, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_NIOS2
+#define TC_NIOS2
+
+/* If unspecified, default to little endian. We can explicitly specify
+ * a big-endian default by configuring with --target=nios2eb-elf. We
+ * can override the default with the -EB and -EL options. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 0
+#endif
+
+/* Words are big enough to hold addresses. */
+#define WORKING_DOT_WORD 1
+
+#ifdef OBJ_ELF
+extern const char *nios2_target_format (void);
+#define TARGET_FORMAT nios2_target_format ()
+#define TARGET_ARCH bfd_arch_nios2
+#endif
+
+/* A NIOS2 instruction consists of tokens and separator characters
+ the tokens are things like the instruction name (add, or jmp etc),
+ the register indices ($5, $7 etc), and constant expressions. The
+ separator characters are commas, brackets and space.
+ The instruction name is always separated from other tokens by a space
+ The maximum number of tokens in an instruction is 5 (the instruction name,
+ 3 arguments, and a 4th string representing the expected instructin opcode
+ after assembly. The latter is only used when the assemble is running in
+ self test mode, otherwise its presence will generate an error. */
+#define NIOS2_MAX_INSN_TOKENS 6
+
+/* There are no machine-specific operands so we #define this to nothing. */
+#define md_operand(x)
+
+/* Function prototypes exported to rest of GAS. */
+extern void md_assemble (char *op_str);
+extern void md_end (void);
+extern void md_begin (void);
+
+#define TC_FORCE_RELOCATION(fixp) nios2_force_relocation (fixp)
+extern int nios2_force_relocation (struct fix *);
+
+#define tc_fix_adjustable(fixp) nios2_fix_adjustable (fixp)
+extern int nios2_fix_adjustable (struct fix *);
+
+#define tc_frob_label(lab) nios2_frob_label (lab)
+extern void nios2_frob_label (symbolS *);
+
+#define tc_frob_symbol(symp, punt) punt = nios2_frob_symbol (symp) ? 1 : punt
+extern int nios2_frob_symbol (symbolS * symp);
+
+#define md_cons_align(nbytes) nios2_cons_align (nbytes)
+extern void nios2_cons_align (int);
+
+extern void md_convert_frag (bfd * headers, segT sec, fragS * fragP);
+
+/* When relaxing, we need to generate relocations for alignment
+ directives. */
+#define HANDLE_ALIGN(frag) nios2_handle_align (frag)
+extern void nios2_handle_align (fragS *);
+
+#define md_relax_frag nios2_relax_frag
+extern long nios2_relax_frag (segT segment, fragS * fragP, long stretch);
+
+#ifdef OBJ_ELF
+#define ELF_TC_SPECIAL_SECTIONS \
+ { ".sdata", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \
+ { ".sbss", SHT_NOBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \
+ { ".lit4", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL }, \
+ { ".lit8", SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_NIOS2_GPREL },
+
+/* Processor-specific section directives. */
+#define md_elf_section_letter nios2_elf_section_letter
+extern int nios2_elf_section_letter (int, char **);
+#define md_elf_section_flags nios2_elf_section_flags
+extern flagword nios2_elf_section_flags (flagword, int, int);
+#endif
+
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+
+#define DIFF_EXPR_OK
+
+/* Nios2 ABI doesn't have 32-bit PCREL relocation, and, as relocations for
+ CFI information will be in section other than .text, we can't use PC-biased
+ relocs. */
+#define CFI_DIFF_EXPR_OK 0
+
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) nios2_cons (EXP, NBYTES)
+extern bfd_reloc_code_real_type nios2_cons (expressionS *exp, int size);
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+#define DWARF2_DEFAULT_RETURN_COLUMN 31
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+#define tc_regname_to_dw2regnum nios2_regname_to_dw2regnum
+extern int nios2_regname_to_dw2regnum (char *regname);
+#define tc_cfi_frame_initial_instructions nios2_frame_initial_instructions
+extern void nios2_frame_initial_instructions (void);
+
+#endif /* TC_NIOS2 */
diff --git a/gas/config/tc-ns32k.c b/gas/config/tc-ns32k.c
new file mode 100644
index 0000000..1c97d43
--- /dev/null
+++ b/gas/config/tc-ns32k.c
@@ -0,0 +1,2253 @@
+/* ns32k.c -- Assemble on the National Semiconductor 32k series
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/*#define SHOW_NUM 1*//* Uncomment for debugging. */
+
+#include "as.h"
+#include "opcode/ns32k.h"
+
+#include "obstack.h"
+
+/* Macros. */
+#define IIF_ENTRIES 13 /* Number of entries in iif. */
+#define PRIVATE_SIZE 256 /* Size of my garbage memory. */
+#define MAX_ARGS 4
+#define DEFAULT -1 /* addr_mode returns this value when
+ plain constant or label is
+ encountered. */
+
+#define IIF(ptr,a1,c1,e1,g1,i1,k1,m1,o1,q1,s1,u1) \
+ iif.iifP[ptr].type = a1; \
+ iif.iifP[ptr].size = c1; \
+ iif.iifP[ptr].object = e1; \
+ iif.iifP[ptr].object_adjust = g1; \
+ iif.iifP[ptr].pcrel = i1; \
+ iif.iifP[ptr].pcrel_adjust = k1; \
+ iif.iifP[ptr].im_disp = m1; \
+ iif.iifP[ptr].relax_substate = o1; \
+ iif.iifP[ptr].bit_fixP = q1; \
+ iif.iifP[ptr].addr_mode = s1; \
+ iif.iifP[ptr].bsr = u1;
+
+#ifdef SEQUENT_COMPATABILITY
+#define LINE_COMMENT_CHARS "|"
+#define ABSOLUTE_PREFIX '@'
+#define IMMEDIATE_PREFIX '#'
+#endif
+
+#ifndef LINE_COMMENT_CHARS
+#define LINE_COMMENT_CHARS "#"
+#endif
+
+const char comment_chars[] = "#";
+const char line_comment_chars[] = LINE_COMMENT_CHARS;
+const char line_separator_chars[] = ";";
+static int default_disp_size = 4; /* Displacement size for external refs. */
+
+#if !defined(ABSOLUTE_PREFIX) && !defined(IMMEDIATE_PREFIX)
+#define ABSOLUTE_PREFIX '@' /* One or the other MUST be defined. */
+#endif
+
+struct addr_mode
+{
+ signed char mode; /* Addressing mode of operand (0-31). */
+ signed char scaled_mode; /* Mode combined with scaled mode. */
+ char scaled_reg; /* Register used in scaled+1 (1-8). */
+ char float_flag; /* Set if R0..R7 was F0..F7 ie a
+ floating-point-register. */
+ char am_size; /* Estimated max size of general addr-mode
+ parts. */
+ char im_disp; /* If im_disp==1 we have a displacement. */
+ char pcrel; /* 1 if pcrel, this is really redundant info. */
+ char disp_suffix[2]; /* Length of displacement(s), 0=undefined. */
+ char *disp[2]; /* Pointer(s) at displacement(s)
+ or immediates(s) (ascii). */
+ char index_byte; /* Index byte. */
+};
+typedef struct addr_mode addr_modeS;
+
+char *freeptr, *freeptr_static; /* Points at some number of free bytes. */
+struct hash_control *inst_hash_handle;
+
+struct ns32k_opcode *desc; /* Pointer at description of instruction. */
+addr_modeS addr_modeP;
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "fd"; /* We don't want to support lowercase,
+ do we? */
+
+/* UPPERCASE denotes live names when an instruction is built, IIF is
+ used as an intermediate form to store the actual parts of the
+ instruction. A ns32k machine instruction can be divided into a
+ couple of sub PARTs. When an instruction is assembled the
+ appropriate PART get an assignment. When an IIF has been completed
+ it is converted to a FRAGment as specified in AS.H. */
+
+/* Internal structs. */
+struct ns32k_option
+{
+ char *pattern;
+ unsigned long or;
+ unsigned long and;
+};
+
+typedef struct
+{
+ int type; /* How to interpret object. */
+ int size; /* Estimated max size of object. */
+ unsigned long object; /* Binary data. */
+ int object_adjust; /* Number added to object. */
+ int pcrel; /* True if object is pcrel. */
+ int pcrel_adjust; /* Length in bytes from the instruction
+ start to the displacement. */
+ int im_disp; /* True if the object is a displacement. */
+ relax_substateT relax_substate;/*Initial relaxsubstate. */
+ bit_fixS *bit_fixP; /* Pointer at bit_fix struct. */
+ int addr_mode; /* What addrmode do we associate with this
+ iif-entry. */
+ char bsr; /* Sequent hack. */
+} iif_entryT; /* Internal Instruction Format. */
+
+struct int_ins_form
+{
+ int instr_size; /* Max size of instruction in bytes. */
+ iif_entryT iifP[IIF_ENTRIES + 1];
+};
+
+struct int_ins_form iif;
+expressionS exprP;
+char *input_line_pointer;
+
+/* Description of the PARTs in IIF
+ object[n]:
+ 0 total length in bytes of entries in iif
+ 1 opcode
+ 2 index_byte_a
+ 3 index_byte_b
+ 4 disp_a_1
+ 5 disp_a_2
+ 6 disp_b_1
+ 7 disp_b_2
+ 8 imm_a
+ 9 imm_b
+ 10 implied1
+ 11 implied2
+
+ For every entry there is a datalength in bytes. This is stored in size[n].
+ 0, the objectlength is not explicitly given by the instruction
+ and the operand is undefined. This is a case for relaxation.
+ Reserve 4 bytes for the final object.
+
+ 1, the entry contains one byte
+ 2, the entry contains two bytes
+ 3, the entry contains three bytes
+ 4, the entry contains four bytes
+ etc
+
+ Furthermore, every entry has a data type identifier in type[n].
+
+ 0, the entry is void, ignore it.
+ 1, the entry is a binary number.
+ 2, the entry is a pointer at an expression.
+ Where expression may be as simple as a single '1',
+ and as complicated as foo-bar+12,
+ foo and bar may be undefined but suffixed by :{b|w|d} to
+ control the length of the object.
+
+ 3, the entry is a pointer at a bignum struct
+
+ The low-order-byte corresponds to low physical memory.
+ Obviously a FRAGment must be created for each valid disp in PART whose
+ datalength is undefined (to bad) .
+ The case where just the expression is undefined is less severe and is
+ handled by fix. Here the number of bytes in the objectfile is known.
+ With this representation we simplify the assembly and separates the
+ machine dependent/independent parts in a more clean way (said OE). */
+
+struct ns32k_option opt1[] = /* restore, exit. */
+{
+ {"r0", 0x80, 0xff},
+ {"r1", 0x40, 0xff},
+ {"r2", 0x20, 0xff},
+ {"r3", 0x10, 0xff},
+ {"r4", 0x08, 0xff},
+ {"r5", 0x04, 0xff},
+ {"r6", 0x02, 0xff},
+ {"r7", 0x01, 0xff},
+ {0, 0x00, 0xff}
+};
+struct ns32k_option opt2[] = /* save, enter. */
+{
+ {"r0", 0x01, 0xff},
+ {"r1", 0x02, 0xff},
+ {"r2", 0x04, 0xff},
+ {"r3", 0x08, 0xff},
+ {"r4", 0x10, 0xff},
+ {"r5", 0x20, 0xff},
+ {"r6", 0x40, 0xff},
+ {"r7", 0x80, 0xff},
+ {0, 0x00, 0xff}
+};
+struct ns32k_option opt3[] = /* setcfg. */
+{
+ {"c", 0x8, 0xff},
+ {"m", 0x4, 0xff},
+ {"f", 0x2, 0xff},
+ {"i", 0x1, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option opt4[] = /* cinv. */
+{
+ {"a", 0x4, 0xff},
+ {"i", 0x2, 0xff},
+ {"d", 0x1, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option opt5[] = /* String inst. */
+{
+ {"b", 0x2, 0xff},
+ {"u", 0xc, 0xff},
+ {"w", 0x4, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option opt6[] = /* Plain reg ext,cvtp etc. */
+{
+ {"r0", 0x00, 0xff},
+ {"r1", 0x01, 0xff},
+ {"r2", 0x02, 0xff},
+ {"r3", 0x03, 0xff},
+ {"r4", 0x04, 0xff},
+ {"r5", 0x05, 0xff},
+ {"r6", 0x06, 0xff},
+ {"r7", 0x07, 0xff},
+ {0, 0x00, 0xff}
+};
+
+#if !defined(NS32032) && !defined(NS32532)
+#define NS32532
+#endif
+
+struct ns32k_option cpureg_532[] = /* lpr spr. */
+{
+ {"us", 0x0, 0xff},
+ {"dcr", 0x1, 0xff},
+ {"bpc", 0x2, 0xff},
+ {"dsr", 0x3, 0xff},
+ {"car", 0x4, 0xff},
+ {"fp", 0x8, 0xff},
+ {"sp", 0x9, 0xff},
+ {"sb", 0xa, 0xff},
+ {"usp", 0xb, 0xff},
+ {"cfg", 0xc, 0xff},
+ {"psr", 0xd, 0xff},
+ {"intbase", 0xe, 0xff},
+ {"mod", 0xf, 0xff},
+ {0, 0x00, 0xff}
+};
+struct ns32k_option mmureg_532[] = /* lmr smr. */
+{
+ {"mcr", 0x9, 0xff},
+ {"msr", 0xa, 0xff},
+ {"tear", 0xb, 0xff},
+ {"ptb0", 0xc, 0xff},
+ {"ptb1", 0xd, 0xff},
+ {"ivar0", 0xe, 0xff},
+ {"ivar1", 0xf, 0xff},
+ {0, 0x0, 0xff}
+};
+
+struct ns32k_option cpureg_032[] = /* lpr spr. */
+{
+ {"upsr", 0x0, 0xff},
+ {"fp", 0x8, 0xff},
+ {"sp", 0x9, 0xff},
+ {"sb", 0xa, 0xff},
+ {"psr", 0xd, 0xff},
+ {"intbase", 0xe, 0xff},
+ {"mod", 0xf, 0xff},
+ {0, 0x0, 0xff}
+};
+struct ns32k_option mmureg_032[] = /* lmr smr. */
+{
+ {"bpr0", 0x0, 0xff},
+ {"bpr1", 0x1, 0xff},
+ {"pf0", 0x4, 0xff},
+ {"pf1", 0x5, 0xff},
+ {"sc", 0x8, 0xff},
+ {"msr", 0xa, 0xff},
+ {"bcnt", 0xb, 0xff},
+ {"ptb0", 0xc, 0xff},
+ {"ptb1", 0xd, 0xff},
+ {"eia", 0xf, 0xff},
+ {0, 0x0, 0xff}
+};
+
+#if defined(NS32532)
+struct ns32k_option *cpureg = cpureg_532;
+struct ns32k_option *mmureg = mmureg_532;
+#else
+struct ns32k_option *cpureg = cpureg_032;
+struct ns32k_option *mmureg = mmureg_032;
+#endif
+
+
+const pseudo_typeS md_pseudo_table[] =
+{ /* So far empty. */
+ {0, 0, 0}
+};
+
+#define IND(x,y) (((x)<<2)+(y))
+
+/* Those are index's to relax groups in md_relax_table ie it must be
+ multiplied by 4 to point at a group start. Viz IND(x,y) Se function
+ relax_segment in write.c for more info. */
+
+#define BRANCH 1
+#define PCREL 2
+
+/* Those are index's to entries in a relax group. */
+
+#define BYTE 0
+#define WORD 1
+#define DOUBLE 2
+#define UNDEF 3
+/* Those limits are calculated from the displacement start in memory.
+ The ns32k uses the beginning of the instruction as displacement
+ base. This type of displacements could be handled here by moving
+ the limit window up or down. I choose to use an internal
+ displacement base-adjust as there are other routines that must
+ consider this. Also, as we have two various offset-adjusts in the
+ ns32k (acb versus br/brs/jsr/bcond), two set of limits would have
+ had to be used. Now we dont have to think about that. */
+
+const relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+ {1, 1, 0, 0},
+
+ {(63), (-64), 1, IND (BRANCH, WORD)},
+ {(8192), (-8192), 2, IND (BRANCH, DOUBLE)},
+ {0, 0, 4, 0},
+ {1, 1, 0, 0}
+};
+
+/* Array used to test if mode contains displacements.
+ Value is true if mode contains displacement. */
+
+char disp_test[] =
+{0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 0, 0, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1};
+
+/* Array used to calculate max size of displacements. */
+
+char disp_size[] =
+{4, 1, 2, 0, 4};
+
+/* Parse a general operand into an addressingmode struct
+
+ In: pointer at operand in ascii form
+ pointer at addr_mode struct for result
+ the level of recursion. (always 0 or 1)
+
+ Out: data in addr_mode struct. */
+
+static int
+addr_mode (char *operand,
+ addr_modeS *addrmodeP,
+ int recursive_level)
+{
+ char *str;
+ int i;
+ int strl;
+ int mode;
+ int j;
+
+ mode = DEFAULT; /* Default. */
+ addrmodeP->scaled_mode = 0; /* Why not. */
+ addrmodeP->scaled_reg = 0; /* If 0, not scaled index. */
+ addrmodeP->float_flag = 0;
+ addrmodeP->am_size = 0;
+ addrmodeP->im_disp = 0;
+ addrmodeP->pcrel = 0; /* Not set in this function. */
+ addrmodeP->disp_suffix[0] = 0;
+ addrmodeP->disp_suffix[1] = 0;
+ addrmodeP->disp[0] = NULL;
+ addrmodeP->disp[1] = NULL;
+ str = operand;
+
+ if (str[0] == 0)
+ return 0;
+
+ strl = strlen (str);
+
+ switch (str[0])
+ {
+ /* The following three case statements controls the mode-chars
+ this is the place to ed if you want to change them. */
+#ifdef ABSOLUTE_PREFIX
+ case ABSOLUTE_PREFIX:
+ if (str[strl - 1] == ']')
+ break;
+ addrmodeP->mode = 21; /* absolute */
+ addrmodeP->disp[0] = str + 1;
+ return -1;
+#endif
+#ifdef IMMEDIATE_PREFIX
+ case IMMEDIATE_PREFIX:
+ if (str[strl - 1] == ']')
+ break;
+ addrmodeP->mode = 20; /* immediate */
+ addrmodeP->disp[0] = str + 1;
+ return -1;
+#endif
+ case '.':
+ if (str[strl - 1] != ']')
+ {
+ switch (str[1])
+ {
+ case '-':
+ case '+':
+ if (str[2] != '\000')
+ {
+ addrmodeP->mode = 27; /* pc-relative */
+ addrmodeP->disp[0] = str + 2;
+ return -1;
+ }
+ default:
+ as_bad (_("Invalid syntax in PC-relative addressing mode"));
+ return 0;
+ }
+ }
+ break;
+ case 'e':
+ if (str[strl - 1] != ']')
+ {
+ if ((!strncmp (str, "ext(", 4)) && strl > 7)
+ { /* external */
+ addrmodeP->disp[0] = str + 4;
+ i = 0;
+ j = 2;
+ do
+ { /* disp[0]'s termination point. */
+ j += 1;
+ if (str[j] == '(')
+ i++;
+ if (str[j] == ')')
+ i--;
+ }
+ while (j < strl && i != 0);
+ if (i != 0 || !(str[j + 1] == '-' || str[j + 1] == '+'))
+ {
+ as_bad (_("Invalid syntax in External addressing mode"));
+ return (0);
+ }
+ str[j] = '\000'; /* null terminate disp[0] */
+ addrmodeP->disp[1] = str + j + 2;
+ addrmodeP->mode = 22;
+ return -1;
+ }
+ }
+ break;
+
+ default:
+ ;
+ }
+
+ strl = strlen (str);
+
+ switch (strl)
+ {
+ case 2:
+ switch (str[0])
+ {
+ case 'f':
+ addrmodeP->float_flag = 1;
+ /* Drop through. */
+ case 'r':
+ if (str[1] >= '0' && str[1] < '8')
+ {
+ addrmodeP->mode = str[1] - '0';
+ return -1;
+ }
+ break;
+ default:
+ break;
+ }
+ /* Drop through. */
+
+ case 3:
+ if (!strncmp (str, "tos", 3))
+ {
+ addrmodeP->mode = 23; /* TopOfStack */
+ return -1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (strl > 4)
+ {
+ if (str[strl - 1] == ')')
+ {
+ if (str[strl - 2] == ')')
+ {
+ if (!strncmp (&str[strl - 5], "(fp", 3))
+ mode = 16; /* Memory Relative. */
+ else if (!strncmp (&str[strl - 5], "(sp", 3))
+ mode = 17;
+ else if (!strncmp (&str[strl - 5], "(sb", 3))
+ mode = 18;
+
+ if (mode != DEFAULT)
+ {
+ /* Memory relative. */
+ addrmodeP->mode = mode;
+ j = strl - 5; /* Temp for end of disp[0]. */
+ i = 0;
+
+ do
+ {
+ strl -= 1;
+ if (str[strl] == ')')
+ i++;
+ if (str[strl] == '(')
+ i--;
+ }
+ while (strl > -1 && i != 0);
+
+ if (i != 0)
+ {
+ as_bad (_("Invalid syntax in Memory Relative addressing mode"));
+ return (0);
+ }
+
+ addrmodeP->disp[1] = str;
+ addrmodeP->disp[0] = str + strl + 1;
+ str[j] = '\000'; /* Null terminate disp[0] . */
+ str[strl] = '\000'; /* Null terminate disp[1]. */
+
+ return -1;
+ }
+ }
+
+ switch (str[strl - 3])
+ {
+ case 'r':
+ case 'R':
+ if (str[strl - 2] >= '0'
+ && str[strl - 2] < '8'
+ && str[strl - 4] == '(')
+ {
+ addrmodeP->mode = str[strl - 2] - '0' + 8;
+ addrmodeP->disp[0] = str;
+ str[strl - 4] = 0;
+ return -1; /* reg rel */
+ }
+ /* Drop through. */
+
+ default:
+ if (!strncmp (&str[strl - 4], "(fp", 3))
+ mode = 24;
+ else if (!strncmp (&str[strl - 4], "(sp", 3))
+ mode = 25;
+ else if (!strncmp (&str[strl - 4], "(sb", 3))
+ mode = 26;
+ else if (!strncmp (&str[strl - 4], "(pc", 3))
+ mode = 27;
+
+ if (mode != DEFAULT)
+ {
+ addrmodeP->mode = mode;
+ addrmodeP->disp[0] = str;
+ str[strl - 4] = '\0';
+
+ return -1; /* Memory space. */
+ }
+ }
+ }
+
+ /* No trailing ')' do we have a ']' ? */
+ if (str[strl - 1] == ']')
+ {
+ switch (str[strl - 2])
+ {
+ case 'b':
+ mode = 28;
+ break;
+ case 'w':
+ mode = 29;
+ break;
+ case 'd':
+ mode = 30;
+ break;
+ case 'q':
+ mode = 31;
+ break;
+ default:
+ as_bad (_("Invalid scaled-indexed mode, use (b,w,d,q)"));
+
+ if (str[strl - 3] != ':' || str[strl - 6] != '['
+ || str[strl - 5] == 'r' || str[strl - 4] < '0'
+ || str[strl - 4] > '7')
+ as_bad (_("Syntax in scaled-indexed mode, use [Rn:m] where n=[0..7] m={b,w,d,q}"));
+ } /* Scaled index. */
+
+ if (recursive_level > 0)
+ {
+ as_bad (_("Scaled-indexed addressing mode combined with scaled-index"));
+ return 0;
+ }
+
+ addrmodeP->am_size += 1; /* scaled index byte. */
+ j = str[strl - 4] - '0'; /* store temporary. */
+ str[strl - 6] = '\000'; /* nullterminate for recursive call. */
+ i = addr_mode (str, addrmodeP, 1);
+
+ if (!i || addrmodeP->mode == 20)
+ {
+ as_bad (_("Invalid or illegal addressing mode combined with scaled-index"));
+ return 0;
+ }
+
+ addrmodeP->scaled_mode = addrmodeP->mode; /* Store the inferior mode. */
+ addrmodeP->mode = mode;
+ addrmodeP->scaled_reg = j + 1;
+
+ return -1;
+ }
+ }
+
+ addrmodeP->mode = DEFAULT; /* Default to whatever. */
+ addrmodeP->disp[0] = str;
+
+ return -1;
+}
+
+static void
+evaluate_expr (expressionS *resultP, char *ptr)
+{
+ char *tmp_line;
+
+ tmp_line = input_line_pointer;
+ input_line_pointer = ptr;
+ expression (resultP);
+ input_line_pointer = tmp_line;
+}
+
+/* ptr points at string addr_modeP points at struct with result This
+ routine calls addr_mode to determine the general addr.mode of the
+ operand. When this is ready it parses the displacements for size
+ specifying suffixes and determines size of immediate mode via
+ ns32k-opcode. Also builds index bytes if needed. */
+
+static int
+get_addr_mode (char *ptr, addr_modeS *addrmodeP)
+{
+ int tmp;
+
+ addr_mode (ptr, addrmodeP, 0);
+
+ if (addrmodeP->mode == DEFAULT || addrmodeP->scaled_mode == -1)
+ {
+ /* Resolve ambiguous operands, this shouldn't be necessary if
+ one uses standard NSC operand syntax. But the sequent
+ compiler doesn't!!! This finds a proper addressing mode
+ if it is implicitly stated. See ns32k-opcode.h. */
+ (void) evaluate_expr (&exprP, ptr); /* This call takes time Sigh! */
+
+ if (addrmodeP->mode == DEFAULT)
+ {
+ if (exprP.X_add_symbol || exprP.X_op_symbol)
+ addrmodeP->mode = desc->default_model; /* We have a label. */
+ else
+ addrmodeP->mode = desc->default_modec; /* We have a constant. */
+ }
+ else
+ {
+ if (exprP.X_add_symbol || exprP.X_op_symbol)
+ addrmodeP->scaled_mode = desc->default_model;
+ else
+ addrmodeP->scaled_mode = desc->default_modec;
+ }
+
+ /* Must put this mess down in addr_mode to handle the scaled
+ case better. */
+ }
+
+ /* It appears as the sequent compiler wants an absolute when we have
+ a label without @. Constants becomes immediates besides the addr
+ case. Think it does so with local labels too, not optimum, pcrel
+ is better. When I have time I will make gas check this and
+ select pcrel when possible Actually that is trivial. */
+ if ((tmp = addrmodeP->scaled_reg))
+ { /* Build indexbyte. */
+ tmp--; /* Remember regnumber comes incremented for
+ flagpurpose. */
+ tmp |= addrmodeP->scaled_mode << 3;
+ addrmodeP->index_byte = (char) tmp;
+ addrmodeP->am_size += 1;
+ }
+
+ gas_assert (addrmodeP->mode >= 0);
+ if (disp_test[(unsigned int) addrmodeP->mode])
+ {
+ char c;
+ char suffix;
+ char suffix_sub;
+ int i;
+ char *toP;
+ char *fromP;
+
+ /* There was a displacement, probe for length specifying suffix. */
+ addrmodeP->pcrel = 0;
+
+ gas_assert (addrmodeP->mode >= 0);
+ if (disp_test[(unsigned int) addrmodeP->mode])
+ {
+ /* There is a displacement. */
+ if (addrmodeP->mode == 27 || addrmodeP->scaled_mode == 27)
+ /* Do we have pcrel. mode. */
+ addrmodeP->pcrel = 1;
+
+ addrmodeP->im_disp = 1;
+
+ for (i = 0; i < 2; i++)
+ {
+ suffix_sub = suffix = 0;
+
+ if ((toP = addrmodeP->disp[i]))
+ {
+ /* Suffix of expression, the largest size rules. */
+ fromP = toP;
+
+ while ((c = *fromP++))
+ {
+ *toP++ = c;
+ if (c == ':')
+ {
+ switch (*fromP)
+ {
+ case '\0':
+ as_warn (_("Premature end of suffix -- Defaulting to d"));
+ suffix = 4;
+ continue;
+ case 'b':
+ suffix_sub = 1;
+ break;
+ case 'w':
+ suffix_sub = 2;
+ break;
+ case 'd':
+ suffix_sub = 4;
+ break;
+ default:
+ as_warn (_("Bad suffix after ':' use {b|w|d} Defaulting to d"));
+ suffix = 4;
+ }
+
+ fromP ++;
+ toP --; /* So we write over the ':' */
+
+ if (suffix < suffix_sub)
+ suffix = suffix_sub;
+ }
+ }
+
+ *toP = '\0'; /* Terminate properly. */
+ addrmodeP->disp_suffix[i] = suffix;
+ addrmodeP->am_size += suffix ? suffix : 4;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (addrmodeP->mode == 20)
+ {
+ /* Look in ns32k_opcode for size. */
+ addrmodeP->disp_suffix[0] = addrmodeP->am_size = desc->im_size;
+ addrmodeP->im_disp = 0;
+ }
+ }
+
+ return addrmodeP->mode;
+}
+
+/* Read an optionlist. */
+
+static void
+optlist (char *str, /* The string to extract options from. */
+ struct ns32k_option *optionP, /* How to search the string. */
+ unsigned long *default_map) /* Default pattern and output. */
+{
+ int i, j, k, strlen1, strlen2;
+ char *patternP, *strP;
+
+ strlen1 = strlen (str);
+
+ if (strlen1 < 1)
+ as_fatal (_("Very short instr to option, ie you can't do it on a NULLstr"));
+
+ for (i = 0; optionP[i].pattern != 0; i++)
+ {
+ strlen2 = strlen (optionP[i].pattern);
+
+ for (j = 0; j < strlen1; j++)
+ {
+ patternP = optionP[i].pattern;
+ strP = &str[j];
+
+ for (k = 0; k < strlen2; k++)
+ {
+ if (*(strP++) != *(patternP++))
+ break;
+ }
+
+ if (k == strlen2)
+ { /* match */
+ *default_map |= optionP[i].or;
+ *default_map &= optionP[i].and;
+ }
+ }
+ }
+}
+
+/* Search struct for symbols.
+ This function is used to get the short integer form of reg names in
+ the instructions lmr, smr, lpr, spr return true if str is found in
+ list. */
+
+static int
+list_search (char *str, /* The string to match. */
+ struct ns32k_option *optionP, /* List to search. */
+ unsigned long *default_map) /* Default pattern and output. */
+{
+ int i;
+
+ for (i = 0; optionP[i].pattern != 0; i++)
+ {
+ if (!strncmp (optionP[i].pattern, str, 20))
+ {
+ /* Use strncmp to be safe. */
+ *default_map |= optionP[i].or;
+ *default_map &= optionP[i].and;
+
+ return -1;
+ }
+ }
+
+ as_bad (_("No such entry in list. (cpu/mmu register)"));
+ return 0;
+}
+
+/* Create a bit_fixS in obstack 'notes'.
+ This struct is used to profile the normal fix. If the bit_fixP is a
+ valid pointer (not NULL) the bit_fix data will be used to format
+ the fix. */
+
+static bit_fixS *
+bit_fix_new (int size, /* Length of bitfield. */
+ int offset, /* Bit offset to bitfield. */
+ long min, /* Signextended min for bitfield. */
+ long max, /* Signextended max for bitfield. */
+ long add, /* Add mask, used for huffman prefix. */
+ long base_type, /* 0 or 1, if 1 it's exploded to opcode ptr. */
+ long base_adj)
+{
+ bit_fixS *bit_fixP;
+
+ bit_fixP = obstack_alloc (&notes, sizeof (bit_fixS));
+
+ bit_fixP->fx_bit_size = size;
+ bit_fixP->fx_bit_offset = offset;
+ bit_fixP->fx_bit_base = base_type;
+ bit_fixP->fx_bit_base_adj = base_adj;
+ bit_fixP->fx_bit_max = max;
+ bit_fixP->fx_bit_min = min;
+ bit_fixP->fx_bit_add = add;
+
+ return bit_fixP;
+}
+
+/* Convert operands to iif-format and adds bitfields to the opcode.
+ Operands are parsed in such an order that the opcode is updated from
+ its most significant bit, that is when the operand need to alter the
+ opcode.
+ Be careful not to put to objects in the same iif-slot. */
+
+static void
+encode_operand (int argc,
+ char **argv,
+ const char *operandsP,
+ const char *suffixP,
+ char im_size ATTRIBUTE_UNUSED,
+ char opcode_bit_ptr)
+{
+ int i, j;
+ char d;
+ int pcrel, b, loop, pcrel_adjust;
+ unsigned long tmp;
+
+ for (loop = 0; loop < argc; loop++)
+ {
+ /* What operand are we supposed to work on. */
+ i = operandsP[loop << 1] - '1';
+ if (i > 3)
+ as_fatal (_("Internal consistency error. check ns32k-opcode.h"));
+
+ pcrel = 0;
+ pcrel_adjust = 0;
+ tmp = 0;
+
+ switch ((d = operandsP[(loop << 1) + 1]))
+ {
+ case 'f': /* Operand of sfsr turns out to be a nasty
+ specialcase. */
+ opcode_bit_ptr -= 5;
+ case 'Z': /* Float not immediate. */
+ case 'F': /* 32 bit float general form. */
+ case 'L': /* 64 bit float. */
+ case 'I': /* Integer not immediate. */
+ case 'B': /* Byte */
+ case 'W': /* Word */
+ case 'D': /* Double-word. */
+ case 'A': /* Double-word gen-address-form ie no regs
+ allowed. */
+ get_addr_mode (argv[i], &addr_modeP);
+
+ if ((addr_modeP.mode == 20) &&
+ (d == 'I' || d == 'Z' || d == 'A'))
+ as_fatal (d == 'A'? _("Address of immediate operand"):
+ _("Invalid immediate write operand."));
+
+ if (opcode_bit_ptr == desc->opcode_size)
+ b = 4;
+ else
+ b = 6;
+
+ for (j = b; j < (b + 2); j++)
+ {
+ if (addr_modeP.disp[j - b])
+ {
+ IIF (j,
+ 2,
+ addr_modeP.disp_suffix[j - b],
+ (unsigned long) addr_modeP.disp[j - b],
+ 0,
+ addr_modeP.pcrel,
+ iif.instr_size,
+ addr_modeP.im_disp,
+ IND (BRANCH, BYTE),
+ NULL,
+ (addr_modeP.scaled_reg ? addr_modeP.scaled_mode
+ : addr_modeP.mode),
+ 0);
+ }
+ }
+
+ opcode_bit_ptr -= 5;
+ iif.iifP[1].object |= ((long) addr_modeP.mode) << opcode_bit_ptr;
+
+ if (addr_modeP.scaled_reg)
+ {
+ j = b / 2;
+ IIF (j, 1, 1, (unsigned long) addr_modeP.index_byte,
+ 0, 0, 0, 0, 0, NULL, -1, 0);
+ }
+ break;
+
+ case 'b': /* Multiple instruction disp. */
+ freeptr++; /* OVE:this is an useful hack. */
+ sprintf (freeptr, "((%s-1)*%d)", argv[i], desc->im_size);
+ argv[i] = freeptr;
+ pcrel -= 1; /* Make pcrel 0 in spite of what case 'p':
+ wants. */
+ /* fall thru */
+ case 'p': /* Displacement - pc relative addressing. */
+ pcrel += 1;
+ /* fall thru */
+ case 'd': /* Displacement. */
+ iif.instr_size += suffixP[i] ? suffixP[i] : 4;
+ IIF (12, 2, suffixP[i], (unsigned long) argv[i], 0,
+ pcrel, pcrel_adjust, 1, IND (BRANCH, BYTE), NULL, -1, 0);
+ break;
+ case 'H': /* Sequent-hack: the linker wants a bit set
+ when bsr. */
+ pcrel = 1;
+ iif.instr_size += suffixP[i] ? suffixP[i] : 4;
+ IIF (12, 2, suffixP[i], (unsigned long) argv[i], 0,
+ pcrel, pcrel_adjust, 1, IND (BRANCH, BYTE), NULL, -1, 1);
+ break;
+ case 'q': /* quick */
+ opcode_bit_ptr -= 4;
+ IIF (11, 2, 42, (unsigned long) argv[i], 0, 0, 0, 0, 0,
+ bit_fix_new (4, opcode_bit_ptr, -8, 7, 0, 1, 0), -1, 0);
+ break;
+ case 'r': /* Register number (3 bits). */
+ list_search (argv[i], opt6, &tmp);
+ opcode_bit_ptr -= 3;
+ iif.iifP[1].object |= tmp << opcode_bit_ptr;
+ break;
+ case 'O': /* Setcfg instruction optionslist. */
+ optlist (argv[i], opt3, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << 15;
+ break;
+ case 'C': /* Cinv instruction optionslist. */
+ optlist (argv[i], opt4, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << 15; /* Insert the regtype in opcode. */
+ break;
+ case 'S': /* String instruction options list. */
+ optlist (argv[i], opt5, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << 15;
+ break;
+ case 'u':
+ case 'U': /* Register list. */
+ IIF (10, 1, 1, 0, 0, 0, 0, 0, 0, NULL, -1, 0);
+ switch (operandsP[(i << 1) + 1])
+ {
+ case 'u': /* Restore, exit. */
+ optlist (argv[i], opt1, &iif.iifP[10].object);
+ break;
+ case 'U': /* Save, enter. */
+ optlist (argv[i], opt2, &iif.iifP[10].object);
+ break;
+ }
+ iif.instr_size += 1;
+ break;
+ case 'M': /* MMU register. */
+ list_search (argv[i], mmureg, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << opcode_bit_ptr;
+ break;
+ case 'P': /* CPU register. */
+ list_search (argv[i], cpureg, &tmp);
+ opcode_bit_ptr -= 4;
+ iif.iifP[1].object |= tmp << opcode_bit_ptr;
+ break;
+ case 'g': /* Inss exts. */
+ iif.instr_size += 1; /* 1 byte is allocated after the opcode. */
+ IIF (10, 2, 1,
+ (unsigned long) argv[i], /* i always 2 here. */
+ 0, 0, 0, 0, 0,
+ bit_fix_new (3, 5, 0, 7, 0, 0, 0), /* A bit_fix is targeted to
+ the byte. */
+ -1, 0);
+ break;
+ case 'G':
+ IIF (11, 2, 42,
+ (unsigned long) argv[i], /* i always 3 here. */
+ 0, 0, 0, 0, 0,
+ bit_fix_new (5, 0, 1, 32, -1, 0, -1), -1, 0);
+ break;
+ case 'i':
+ iif.instr_size += 1;
+ b = 2 + i; /* Put the extension byte after opcode. */
+ IIF (b, 2, 1, 0, 0, 0, 0, 0, 0, 0, -1, 0);
+ break;
+ default:
+ as_fatal (_("Bad opcode-table-option, check in file ns32k-opcode.h"));
+ }
+ }
+}
+
+/* in: instruction line
+ out: internal structure of instruction
+ that has been prepared for direct conversion to fragment(s) and
+ fixes in a systematical fashion
+ Return-value = recursive_level. */
+/* Build iif of one assembly text line. */
+
+static int
+parse (const char *line, int recursive_level)
+{
+ const char *lineptr;
+ char c, suffix_separator;
+ int i;
+ unsigned int argc;
+ int arg_type;
+ char sqr, sep;
+ char suffix[MAX_ARGS], *argv[MAX_ARGS]; /* No more than 4 operands. */
+
+ if (recursive_level <= 0)
+ {
+ /* Called from md_assemble. */
+ for (lineptr = line; (*lineptr) != '\0' && (*lineptr) != ' '; lineptr++)
+ continue;
+
+ c = *lineptr;
+ *(char *) lineptr = '\0';
+
+ if (!(desc = (struct ns32k_opcode *) hash_find (inst_hash_handle, line)))
+ as_fatal (_("No such opcode"));
+
+ *(char *) lineptr = c;
+ }
+ else
+ lineptr = line;
+
+ argc = 0;
+
+ if (*desc->operands)
+ {
+ if (*lineptr++ != '\0')
+ {
+ sqr = '[';
+ sep = ',';
+
+ while (*lineptr != '\0')
+ {
+ if (desc->operands[argc << 1])
+ {
+ suffix[argc] = 0;
+ arg_type = desc->operands[(argc << 1) + 1];
+
+ switch (arg_type)
+ {
+ case 'd':
+ case 'b':
+ case 'p':
+ case 'H':
+ /* The operand is supposed to be a displacement. */
+ /* Hackwarning: do not forget to update the 4
+ cases above when editing ns32k-opcode.h. */
+ suffix_separator = ':';
+ break;
+ default:
+ /* If this char occurs we loose. */
+ suffix_separator = '\255';
+ break;
+ }
+
+ suffix[argc] = 0; /* 0 when no ':' is encountered. */
+ argv[argc] = freeptr;
+ *freeptr = '\0';
+
+ while ((c = *lineptr) != '\0' && c != sep)
+ {
+ if (c == sqr)
+ {
+ if (sqr == '[')
+ {
+ sqr = ']';
+ sep = '\0';
+ }
+ else
+ {
+ sqr = '[';
+ sep = ',';
+ }
+ }
+
+ if (c == suffix_separator)
+ {
+ /* ':' - label/suffix separator. */
+ switch (lineptr[1])
+ {
+ case 'b':
+ suffix[argc] = 1;
+ break;
+ case 'w':
+ suffix[argc] = 2;
+ break;
+ case 'd':
+ suffix[argc] = 4;
+ break;
+ default:
+ as_warn (_("Bad suffix, defaulting to d"));
+ suffix[argc] = 4;
+ if (lineptr[1] == '\0' || lineptr[1] == sep)
+ {
+ lineptr += 1;
+ continue;
+ }
+ break;
+ }
+
+ lineptr += 2;
+ continue;
+ }
+
+ *freeptr++ = c;
+ lineptr++;
+ }
+
+ *freeptr++ = '\0';
+ argc += 1;
+
+ if (*lineptr == '\0')
+ continue;
+
+ lineptr += 1;
+ }
+ else
+ as_fatal (_("Too many operands passed to instruction"));
+ }
+ }
+ }
+
+ if (argc != strlen (desc->operands) / 2)
+ {
+ if (strlen (desc->default_args))
+ {
+ /* We can apply default, don't goof. */
+ if (parse (desc->default_args, 1) != 1)
+ /* Check error in default. */
+ as_fatal (_("Wrong numbers of operands in default, check ns32k-opcodes.h"));
+ }
+ else
+ as_fatal (_("Wrong number of operands"));
+ }
+
+ for (i = 0; i < IIF_ENTRIES; i++)
+ /* Mark all entries as void. */
+ iif.iifP[i].type = 0;
+
+ /* Build opcode iif-entry. */
+ iif.instr_size = desc->opcode_size / 8;
+ IIF (1, 1, iif.instr_size, desc->opcode_seed, 0, 0, 0, 0, 0, 0, -1, 0);
+
+ /* This call encodes operands to iif format. */
+ if (argc)
+ encode_operand (argc, argv, &desc->operands[0],
+ &suffix[0], desc->im_size, desc->opcode_size);
+
+ return recursive_level;
+}
+
+/* This functionality should really be in the bfd library. */
+
+static bfd_reloc_code_real_type
+reloc (int size, int pcrel, int type)
+{
+ int length, rel_index;
+ bfd_reloc_code_real_type relocs[] =
+ {
+ BFD_RELOC_NS32K_IMM_8,
+ BFD_RELOC_NS32K_IMM_16,
+ BFD_RELOC_NS32K_IMM_32,
+ BFD_RELOC_NS32K_IMM_8_PCREL,
+ BFD_RELOC_NS32K_IMM_16_PCREL,
+ BFD_RELOC_NS32K_IMM_32_PCREL,
+
+ /* ns32k displacements. */
+ BFD_RELOC_NS32K_DISP_8,
+ BFD_RELOC_NS32K_DISP_16,
+ BFD_RELOC_NS32K_DISP_32,
+ BFD_RELOC_NS32K_DISP_8_PCREL,
+ BFD_RELOC_NS32K_DISP_16_PCREL,
+ BFD_RELOC_NS32K_DISP_32_PCREL,
+
+ /* Normal 2's complement. */
+ BFD_RELOC_8,
+ BFD_RELOC_16,
+ BFD_RELOC_32,
+ BFD_RELOC_8_PCREL,
+ BFD_RELOC_16_PCREL,
+ BFD_RELOC_32_PCREL
+ };
+
+ switch (size)
+ {
+ case 1:
+ length = 0;
+ break;
+ case 2:
+ length = 1;
+ break;
+ case 4:
+ length = 2;
+ break;
+ default:
+ length = -1;
+ break;
+ }
+
+ rel_index = length + 3 * pcrel + 6 * type;
+
+ if (rel_index >= 0 && (unsigned int) rel_index < sizeof (relocs) / sizeof (relocs[0]))
+ return relocs[rel_index];
+
+ if (pcrel)
+ as_bad (_("Can not do %d byte pc-relative relocation for storage type %d"),
+ size, type);
+ else
+ as_bad (_("Can not do %d byte relocation for storage type %d"),
+ size, type);
+
+ return BFD_RELOC_NONE;
+
+}
+
+static void
+fix_new_ns32k (fragS *frag, /* Which frag? */
+ int where, /* Where in that frag? */
+ int size, /* 1, 2 or 4 usually. */
+ symbolS *add_symbol, /* X_add_symbol. */
+ long offset, /* X_add_number. */
+ int pcrel, /* True if PC-relative relocation. */
+ char im_disp, /* True if the value to write is a
+ displacement. */
+ bit_fixS *bit_fixP, /* Pointer at struct of bit_fix's, ignored if
+ NULL. */
+ char bsr, /* Sequent-linker-hack: 1 when relocobject is
+ a bsr. */
+ fragS *opcode_frag,
+ unsigned int opcode_offset)
+{
+ fixS *fixP = fix_new (frag, where, size, add_symbol,
+ offset, pcrel,
+ bit_fixP ? NO_RELOC : reloc (size, pcrel, im_disp)
+ );
+
+ fix_opcode_frag (fixP) = opcode_frag;
+ fix_opcode_offset (fixP) = opcode_offset;
+ fix_im_disp (fixP) = im_disp;
+ fix_bsr (fixP) = bsr;
+ fix_bit_fixP (fixP) = bit_fixP;
+ /* We have a MD overflow check for displacements. */
+ fixP->fx_no_overflow = (im_disp != 0);
+}
+
+static void
+fix_new_ns32k_exp (fragS *frag, /* Which frag? */
+ int where, /* Where in that frag? */
+ int size, /* 1, 2 or 4 usually. */
+ expressionS *exp, /* Expression. */
+ int pcrel, /* True if PC-relative relocation. */
+ char im_disp, /* True if the value to write is a
+ displacement. */
+ bit_fixS *bit_fixP, /* Pointer at struct of bit_fix's, ignored if
+ NULL. */
+ char bsr, /* Sequent-linker-hack: 1 when relocobject is
+ a bsr. */
+ fragS *opcode_frag,
+ unsigned int opcode_offset)
+{
+ fixS *fixP = fix_new_exp (frag, where, size, exp, pcrel,
+ bit_fixP ? NO_RELOC : reloc (size, pcrel, im_disp)
+ );
+
+ fix_opcode_frag (fixP) = opcode_frag;
+ fix_opcode_offset (fixP) = opcode_offset;
+ fix_im_disp (fixP) = im_disp;
+ fix_bsr (fixP) = bsr;
+ fix_bit_fixP (fixP) = bit_fixP;
+ /* We have a MD overflow check for displacements. */
+ fixP->fx_no_overflow = (im_disp != 0);
+}
+
+/* Convert number to chars in correct order. */
+
+void
+md_number_to_chars (char *buf, valueT value, int nbytes)
+{
+ number_to_chars_littleendian (buf, value, nbytes);
+}
+
+/* This is a variant of md_numbers_to_chars. The reason for its'
+ existence is the fact that ns32k uses Huffman coded
+ displacements. This implies that the bit order is reversed in
+ displacements and that they are prefixed with a size-tag.
+
+ binary: msb -> lsb
+ 0xxxxxxx byte
+ 10xxxxxx xxxxxxxx word
+ 11xxxxxx xxxxxxxx xxxxxxxx xxxxxxxx double word
+
+ This must be taken care of and we do it here! */
+
+static void
+md_number_to_disp (char *buf, long val, int n)
+{
+ switch (n)
+ {
+ case 1:
+ if (val < -64 || val > 63)
+ as_bad (_("value of %ld out of byte displacement range."), val);
+ val &= 0x7f;
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+
+ case 2:
+ if (val < -8192 || val > 8191)
+ as_bad (_("value of %ld out of word displacement range."), val);
+ val &= 0x3fff;
+ val |= 0x8000;
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+
+ case 4:
+ if (val < -0x20000000 || val >= 0x20000000)
+ as_bad (_("value of %ld out of double word displacement range."), val);
+ val |= 0xc0000000;
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 24 & 0xff);
+#endif
+ *buf++ = (val >> 24);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 16 & 0xff);
+#endif
+ *buf++ = (val >> 16);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+
+ default:
+ as_fatal (_("Internal logic error. line %d, file \"%s\""),
+ __LINE__, __FILE__);
+ }
+}
+
+static void
+md_number_to_imm (char *buf, long val, int n)
+{
+ switch (n)
+ {
+ case 1:
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+
+ case 2:
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+
+ case 4:
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 24 & 0xff);
+#endif
+ *buf++ = (val >> 24);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 16 & 0xff);
+#endif
+ *buf++ = (val >> 16);
+#ifdef SHOW_NUM
+ printf ("%x ", val >> 8 & 0xff);
+#endif
+ *buf++ = (val >> 8);
+#ifdef SHOW_NUM
+ printf ("%x ", val & 0xff);
+#endif
+ *buf++ = val;
+ break;
+
+ default:
+ as_fatal (_("Internal logic error. line %d, file \"%s\""),
+ __LINE__, __FILE__);
+ }
+}
+
+/* Fast bitfiddling support. */
+/* Mask used to zero bitfield before oring in the true field. */
+
+static unsigned long l_mask[] =
+{
+ 0xffffffff, 0xfffffffe, 0xfffffffc, 0xfffffff8,
+ 0xfffffff0, 0xffffffe0, 0xffffffc0, 0xffffff80,
+ 0xffffff00, 0xfffffe00, 0xfffffc00, 0xfffff800,
+ 0xfffff000, 0xffffe000, 0xffffc000, 0xffff8000,
+ 0xffff0000, 0xfffe0000, 0xfffc0000, 0xfff80000,
+ 0xfff00000, 0xffe00000, 0xffc00000, 0xff800000,
+ 0xff000000, 0xfe000000, 0xfc000000, 0xf8000000,
+ 0xf0000000, 0xe0000000, 0xc0000000, 0x80000000,
+};
+static unsigned long r_mask[] =
+{
+ 0x00000000, 0x00000001, 0x00000003, 0x00000007,
+ 0x0000000f, 0x0000001f, 0x0000003f, 0x0000007f,
+ 0x000000ff, 0x000001ff, 0x000003ff, 0x000007ff,
+ 0x00000fff, 0x00001fff, 0x00003fff, 0x00007fff,
+ 0x0000ffff, 0x0001ffff, 0x0003ffff, 0x0007ffff,
+ 0x000fffff, 0x001fffff, 0x003fffff, 0x007fffff,
+ 0x00ffffff, 0x01ffffff, 0x03ffffff, 0x07ffffff,
+ 0x0fffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff,
+};
+#define MASK_BITS 31
+/* Insert bitfield described by field_ptr and val at buf
+ This routine is written for modification of the first 4 bytes pointed
+ to by buf, to yield speed.
+ The ifdef stuff is for selection between a ns32k-dependent routine
+ and a general version. (My advice: use the general version!). */
+
+static void
+md_number_to_field (char *buf, long val, bit_fixS *field_ptr)
+{
+ unsigned long object;
+ unsigned long mask;
+ /* Define ENDIAN on a ns32k machine. */
+#ifdef ENDIAN
+ unsigned long *mem_ptr;
+#else
+ char *mem_ptr;
+#endif
+
+ if (field_ptr->fx_bit_min <= val && val <= field_ptr->fx_bit_max)
+ {
+#ifdef ENDIAN
+ if (field_ptr->fx_bit_base)
+ /* Override buf. */
+ mem_ptr = (unsigned long *) field_ptr->fx_bit_base;
+ else
+ mem_ptr = (unsigned long *) buf;
+
+ mem_ptr = ((unsigned long *)
+ ((char *) mem_ptr + field_ptr->fx_bit_base_adj));
+#else
+ if (field_ptr->fx_bit_base)
+ mem_ptr = (char *) field_ptr->fx_bit_base;
+ else
+ mem_ptr = buf;
+
+ mem_ptr += field_ptr->fx_bit_base_adj;
+#endif
+#ifdef ENDIAN
+ /* We have a nice ns32k machine with lowbyte at low-physical mem. */
+ object = *mem_ptr; /* get some bytes */
+#else /* OVE Goof! the machine is a m68k or dito. */
+ /* That takes more byte fiddling. */
+ object = 0;
+ object |= mem_ptr[3] & 0xff;
+ object <<= 8;
+ object |= mem_ptr[2] & 0xff;
+ object <<= 8;
+ object |= mem_ptr[1] & 0xff;
+ object <<= 8;
+ object |= mem_ptr[0] & 0xff;
+#endif
+ mask = 0;
+ mask |= (r_mask[field_ptr->fx_bit_offset]);
+ mask |= (l_mask[field_ptr->fx_bit_offset + field_ptr->fx_bit_size]);
+ object &= mask;
+ val += field_ptr->fx_bit_add;
+ object |= ((val << field_ptr->fx_bit_offset) & (mask ^ 0xffffffff));
+#ifdef ENDIAN
+ *mem_ptr = object;
+#else
+ mem_ptr[0] = (char) object;
+ object >>= 8;
+ mem_ptr[1] = (char) object;
+ object >>= 8;
+ mem_ptr[2] = (char) object;
+ object >>= 8;
+ mem_ptr[3] = (char) object;
+#endif
+ }
+ else
+ as_bad (_("Bit field out of range"));
+}
+
+/* Convert iif to fragments. From this point we start to dribble with
+ functions in other files than this one.(Except hash.c) So, if it's
+ possible to make an iif for an other CPU, you don't need to know
+ what frags, relax, obstacks, etc is in order to port this
+ assembler. You only need to know if it's possible to reduce your
+ cpu-instruction to iif-format (takes some work) and adopt the other
+ md_? parts according to given instructions Note that iif was
+ invented for the clean ns32k`s architecture. */
+
+/* GAS for the ns32k has a problem. PC relative displacements are
+ relative to the address of the opcode, not the address of the
+ operand. We used to keep track of the offset between the operand
+ and the opcode in pcrel_adjust for each frag and each fix. However,
+ we get into trouble where there are two or more pc-relative
+ operands and the size of the first one can't be determined. Then in
+ the relax phase, the size of the first operand will change and
+ pcrel_adjust will no longer be correct. The current solution is
+ keep a pointer to the frag with the opcode in it and the offset in
+ that frag for each frag and each fix. Then, when needed, we can
+ always figure out how far it is between the opcode and the pcrel
+ object. See also md_pcrel_adjust and md_fix_pcrel_adjust. For
+ objects not part of an instruction, the pointer to the opcode frag
+ is always zero. */
+
+static void
+convert_iif (void)
+{
+ int i;
+ bit_fixS *j;
+ fragS *inst_frag;
+ unsigned int inst_offset;
+ char *inst_opcode;
+ char *memP;
+ int l;
+ int k;
+ char type;
+ char size = 0;
+
+ frag_grow (iif.instr_size); /* This is important. */
+ memP = frag_more (0);
+ inst_opcode = memP;
+ inst_offset = (memP - frag_now->fr_literal);
+ inst_frag = frag_now;
+
+ for (i = 0; i < IIF_ENTRIES; i++)
+ {
+ if ((type = iif.iifP[i].type))
+ {
+ /* The object exist, so handle it. */
+ switch (size = iif.iifP[i].size)
+ {
+ case 42:
+ size = 0;
+ /* It's a bitfix that operates on an existing object. */
+ if (iif.iifP[i].bit_fixP->fx_bit_base)
+ /* Expand fx_bit_base to point at opcode. */
+ iif.iifP[i].bit_fixP->fx_bit_base = (long) inst_opcode;
+ /* Fall through. */
+
+ case 8: /* bignum or doublefloat. */
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ /* The final size in objectmemory is known. */
+ memP = frag_more (size);
+ j = iif.iifP[i].bit_fixP;
+
+ switch (type)
+ {
+ case 1: /* The object is pure binary. */
+ if (j)
+ md_number_to_field (memP, exprP.X_add_number, j);
+
+ else if (iif.iifP[i].pcrel)
+ fix_new_ns32k (frag_now,
+ (long) (memP - frag_now->fr_literal),
+ size,
+ 0,
+ iif.iifP[i].object,
+ iif.iifP[i].pcrel,
+ iif.iifP[i].im_disp,
+ 0,
+ iif.iifP[i].bsr, /* Sequent hack. */
+ inst_frag, inst_offset);
+ else
+ {
+ /* Good, just put them bytes out. */
+ switch (iif.iifP[i].im_disp)
+ {
+ case 0:
+ md_number_to_chars (memP, iif.iifP[i].object, size);
+ break;
+ case 1:
+ md_number_to_disp (memP, iif.iifP[i].object, size);
+ break;
+ default:
+ as_fatal (_("iif convert internal pcrel/binary"));
+ }
+ }
+ break;
+
+ case 2:
+ /* The object is a pointer at an expression, so
+ unpack it, note that bignums may result from the
+ expression. */
+ evaluate_expr (&exprP, (char *) iif.iifP[i].object);
+ if (exprP.X_op == O_big || size == 8)
+ {
+ if ((k = exprP.X_add_number) > 0)
+ {
+ /* We have a bignum ie a quad. This can only
+ happens in a long suffixed instruction. */
+ if (k * 2 > size)
+ as_bad (_("Bignum too big for long"));
+
+ if (k == 3)
+ memP += 2;
+
+ for (l = 0; k > 0; k--, l += 2)
+ md_number_to_chars (memP + l,
+ generic_bignum[l >> 1],
+ sizeof (LITTLENUM_TYPE));
+ }
+ else
+ {
+ /* flonum. */
+ LITTLENUM_TYPE words[4];
+
+ switch (size)
+ {
+ case 4:
+ gen_to_words (words, 2, 8);
+ md_number_to_imm (memP, (long) words[0],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm (memP + sizeof (LITTLENUM_TYPE),
+ (long) words[1],
+ sizeof (LITTLENUM_TYPE));
+ break;
+ case 8:
+ gen_to_words (words, 4, 11);
+ md_number_to_imm (memP, (long) words[0],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm (memP + sizeof (LITTLENUM_TYPE),
+ (long) words[1],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm ((memP + 2
+ * sizeof (LITTLENUM_TYPE)),
+ (long) words[2],
+ sizeof (LITTLENUM_TYPE));
+ md_number_to_imm ((memP + 3
+ * sizeof (LITTLENUM_TYPE)),
+ (long) words[3],
+ sizeof (LITTLENUM_TYPE));
+ break;
+ }
+ }
+ break;
+ }
+ if (exprP.X_add_symbol ||
+ exprP.X_op_symbol ||
+ iif.iifP[i].pcrel)
+ {
+ /* The expression was undefined due to an
+ undefined label. Create a fix so we can fix
+ the object later. */
+ exprP.X_add_number += iif.iifP[i].object_adjust;
+ fix_new_ns32k_exp (frag_now,
+ (long) (memP - frag_now->fr_literal),
+ size,
+ &exprP,
+ iif.iifP[i].pcrel,
+ iif.iifP[i].im_disp,
+ j,
+ iif.iifP[i].bsr,
+ inst_frag, inst_offset);
+ }
+ else if (j)
+ md_number_to_field (memP, exprP.X_add_number, j);
+ else
+ {
+ /* Good, just put them bytes out. */
+ switch (iif.iifP[i].im_disp)
+ {
+ case 0:
+ md_number_to_imm (memP, exprP.X_add_number, size);
+ break;
+ case 1:
+ md_number_to_disp (memP, exprP.X_add_number, size);
+ break;
+ default:
+ as_fatal (_("iif convert internal pcrel/pointer"));
+ }
+ }
+ break;
+ default:
+ as_fatal (_("Internal logic error in iif.iifP[n].type"));
+ }
+ break;
+
+ case 0:
+ /* Too bad, the object may be undefined as far as its
+ final nsize in object memory is concerned. The size
+ of the object in objectmemory is not explicitly
+ given. If the object is defined its length can be
+ determined and a fix can replace the frag. */
+ {
+ evaluate_expr (&exprP, (char *) iif.iifP[i].object);
+
+ if ((exprP.X_add_symbol || exprP.X_op_symbol) &&
+ !iif.iifP[i].pcrel)
+ {
+ /* Size is unknown until link time so have to default. */
+ size = default_disp_size; /* Normally 4 bytes. */
+ memP = frag_more (size);
+ fix_new_ns32k_exp (frag_now,
+ (long) (memP - frag_now->fr_literal),
+ size,
+ &exprP,
+ 0, /* never iif.iifP[i].pcrel, */
+ 1, /* always iif.iifP[i].im_disp */
+ (bit_fixS *) 0, 0,
+ inst_frag,
+ inst_offset);
+ break; /* Exit this absolute hack. */
+ }
+
+ if (exprP.X_add_symbol || exprP.X_op_symbol)
+ {
+ /* Frag it. */
+ if (exprP.X_op_symbol)
+ /* We cant relax this case. */
+ as_fatal (_("Can't relax difference"));
+ else
+ {
+ /* Size is not important. This gets fixed by
+ relax, but we assume 0 in what follows. */
+ memP = frag_more (4); /* Max size. */
+ size = 0;
+
+ {
+ fragS *old_frag = frag_now;
+ frag_variant (rs_machine_dependent,
+ 4, /* Max size. */
+ 0, /* Size. */
+ IND (BRANCH, UNDEF), /* Expecting
+ the worst. */
+ exprP.X_add_symbol,
+ exprP.X_add_number,
+ inst_opcode);
+ frag_opcode_frag (old_frag) = inst_frag;
+ frag_opcode_offset (old_frag) = inst_offset;
+ frag_bsr (old_frag) = iif.iifP[i].bsr;
+ }
+ }
+ }
+ else
+ {
+ /* This duplicates code in md_number_to_disp. */
+ if (-64 <= exprP.X_add_number && exprP.X_add_number <= 63)
+ size = 1;
+ else
+ {
+ if (-8192 <= exprP.X_add_number
+ && exprP.X_add_number <= 8191)
+ size = 2;
+ else
+ {
+ if (-0x20000000 <= exprP.X_add_number
+ && exprP.X_add_number<=0x1fffffff)
+ size = 4;
+ else
+ {
+ as_bad (_("Displacement too large for :d"));
+ size = 4;
+ }
+ }
+ }
+
+ memP = frag_more (size);
+ md_number_to_disp (memP, exprP.X_add_number, size);
+ }
+ }
+ break;
+
+ default:
+ as_fatal (_("Internal logic error in iif.iifP[].type"));
+ }
+ }
+ }
+}
+
+void
+md_assemble (char *line)
+{
+ freeptr = freeptr_static;
+ parse (line, 0); /* Explode line to more fix form in iif. */
+ convert_iif (); /* Convert iif to frags, fix's etc. */
+#ifdef SHOW_NUM
+ printf (" \t\t\t%s\n", line);
+#endif
+}
+
+void
+md_begin (void)
+{
+ /* Build a hashtable of the instructions. */
+ const struct ns32k_opcode *ptr;
+ const char *status;
+ const struct ns32k_opcode *endop;
+
+ inst_hash_handle = hash_new ();
+
+ endop = ns32k_opcodes + sizeof (ns32k_opcodes) / sizeof (ns32k_opcodes[0]);
+ for (ptr = ns32k_opcodes; ptr < endop; ptr++)
+ {
+ if ((status = hash_insert (inst_hash_handle, ptr->name, (char *) ptr)))
+ /* Fatal. */
+ as_fatal (_("Can't hash %s: %s"), ptr->name, status);
+ }
+
+ /* Some private space please! */
+ freeptr_static = (char *) malloc (PRIVATE_SIZE);
+}
+
+/* Turn the string pointed to by litP into a floating point constant
+ of type TYPE, and emit the appropriate bytes. The number of
+ LITTLENUMS emitted is stored in *SIZEP. An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+int
+md_pcrel_adjust (fragS *fragP)
+{
+ fragS *opcode_frag;
+ addressT opcode_address;
+ unsigned int offset;
+
+ opcode_frag = frag_opcode_frag (fragP);
+ if (opcode_frag == 0)
+ return 0;
+
+ offset = frag_opcode_offset (fragP);
+ opcode_address = offset + opcode_frag->fr_address;
+
+ return fragP->fr_address + fragP->fr_fix - opcode_address;
+}
+
+static int
+md_fix_pcrel_adjust (fixS *fixP)
+{
+ fragS *opcode_frag;
+ addressT opcode_address;
+ unsigned int offset;
+
+ opcode_frag = fix_opcode_frag (fixP);
+ if (opcode_frag == 0)
+ return 0;
+
+ offset = fix_opcode_offset (fixP);
+ opcode_address = offset + opcode_frag->fr_address;
+
+ return fixP->fx_where + fixP->fx_frag->fr_address - opcode_address;
+}
+
+/* Apply a fixS (fixup of an instruction or data that we didn't have
+ enough info to complete immediately) to the data in a frag.
+
+ On the ns32k, everything is in a different format, so we have broken
+ out separate functions for each kind of thing we could be fixing.
+ They all get called from here. */
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
+{
+ long val = * (long *) valP;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (fix_bit_fixP (fixP))
+ /* Bitfields to fix, sigh. */
+ md_number_to_field (buf, val, fix_bit_fixP (fixP));
+ else switch (fix_im_disp (fixP))
+ {
+ case 0:
+ /* Immediate field. */
+ md_number_to_imm (buf, val, fixP->fx_size);
+ break;
+
+ case 1:
+ /* Displacement field. */
+ /* Calculate offset. */
+ md_number_to_disp (buf,
+ (fixP->fx_pcrel ? val + md_fix_pcrel_adjust (fixP)
+ : val), fixP->fx_size);
+ break;
+
+ case 2:
+ /* Pointer in a data object. */
+ md_number_to_chars (buf, val, fixP->fx_size);
+ break;
+ }
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+/* Convert a relaxed displacement to ditto in final output. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ long disp;
+ long ext = 0;
+ /* Address in gas core of the place to store the displacement. */
+ char *buffer_address = fragP->fr_fix + fragP->fr_literal;
+ /* Address in object code of the displacement. */
+ int object_address;
+
+ switch (fragP->fr_subtype)
+ {
+ case IND (BRANCH, BYTE):
+ ext = 1;
+ break;
+ case IND (BRANCH, WORD):
+ ext = 2;
+ break;
+ case IND (BRANCH, DOUBLE):
+ ext = 4;
+ break;
+ }
+
+ if (ext == 0)
+ return;
+
+ know (fragP->fr_symbol);
+
+ object_address = fragP->fr_fix + fragP->fr_address;
+
+ /* The displacement of the address, from current location. */
+ disp = (S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset) - object_address;
+ disp += md_pcrel_adjust (fragP);
+
+ md_number_to_disp (buffer_address, (long) disp, (int) ext);
+ fragP->fr_fix += ext;
+}
+
+/* This function returns the estimated size a variable object will occupy,
+ one can say that we tries to guess the size of the objects before we
+ actually know it. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment)
+{
+ if (fragP->fr_subtype == IND (BRANCH, UNDEF))
+ {
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment)
+ {
+ /* We don't relax symbols defined in another segment. The
+ thing to do is to assume the object will occupy 4 bytes. */
+ fix_new_ns32k (fragP,
+ (int) (fragP->fr_fix),
+ 4,
+ fragP->fr_symbol,
+ fragP->fr_offset,
+ 1,
+ 1,
+ 0,
+ frag_bsr(fragP), /* Sequent hack. */
+ frag_opcode_frag (fragP),
+ frag_opcode_offset (fragP));
+ fragP->fr_fix += 4;
+ frag_wane (fragP);
+ return 4;
+ }
+
+ /* Relaxable case. Set up the initial guess for the variable
+ part of the frag. */
+ fragP->fr_subtype = IND (BRANCH, BYTE);
+ }
+
+ if (fragP->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ abort ();
+
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+int md_short_jump_size = 3;
+int md_long_jump_size = 5;
+
+void
+md_create_short_jump (char *ptr,
+ addressT from_addr,
+ addressT to_addr,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ valueT offset;
+
+ offset = to_addr - from_addr;
+ md_number_to_chars (ptr, (valueT) 0xEA, 1);
+ md_number_to_disp (ptr + 1, (valueT) offset, 2);
+}
+
+void
+md_create_long_jump (char *ptr,
+ addressT from_addr,
+ addressT to_addr,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ valueT offset;
+
+ offset = to_addr - from_addr;
+ md_number_to_chars (ptr, (valueT) 0xEA, 1);
+ md_number_to_disp (ptr + 1, (valueT) offset, 4);
+}
+
+const char *md_shortopts = "m:";
+
+struct option md_longopts[] =
+{
+#define OPTION_DISP_SIZE (OPTION_MD_BASE)
+ {"disp-size-default", required_argument , NULL, OPTION_DISP_SIZE},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'm':
+ if (!strcmp (arg, "32032"))
+ {
+ cpureg = cpureg_032;
+ mmureg = mmureg_032;
+ }
+ else if (!strcmp (arg, "32532"))
+ {
+ cpureg = cpureg_532;
+ mmureg = mmureg_532;
+ }
+ else
+ {
+ as_warn (_("invalid architecture option -m%s, ignored"), arg);
+ return 0;
+ }
+ break;
+ case OPTION_DISP_SIZE:
+ {
+ int size = atoi(arg);
+ switch (size)
+ {
+ case 1: case 2: case 4:
+ default_disp_size = size;
+ break;
+ default:
+ as_warn (_("invalid default displacement size \"%s\". Defaulting to %d."),
+ arg, default_disp_size);
+ }
+ break;
+ }
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+NS32K options:\n\
+-m32032 | -m32532 select variant of NS32K architecture\n\
+--disp-size-default=<1|2|4>\n"));
+}
+
+/* This is TC_CONS_FIX_NEW, called by emit_expr in read.c. */
+
+void
+cons_fix_new_ns32k (fragS *frag, /* Which frag? */
+ int where, /* Where in that frag? */
+ int size, /* 1, 2 or 4 usually. */
+ expressionS *exp, /* Expression. */
+ bfd_reloc_code_real_type r ATTRIBUTE_UNUSED)
+{
+ fix_new_ns32k_exp (frag, where, size, exp,
+ 0, 2, 0, 0, 0, 0);
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ return size; /* Byte alignment is fine. */
+}
+
+/* Exactly what point is a PC-relative offset relative TO? On the
+ ns32k, they're relative to the start of the instruction. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ long res;
+
+ res = fixP->fx_where + fixP->fx_frag->fr_address;
+#ifdef SEQUENT_COMPATABILITY
+ if (frag_bsr (fixP->fx_frag))
+ res += 0x12 /* FOO Kludge alert! */
+#endif
+ return res;
+}
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code;
+
+ code = reloc (fixp->fx_size, fixp->fx_pcrel, fix_im_disp (fixp));
+
+ rel = xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ if (fixp->fx_pcrel)
+ rel->addend = fixp->fx_addnumber;
+ else
+ rel->addend = 0;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (!rel->howto)
+ {
+ const char *name;
+
+ name = S_GET_NAME (fixp->fx_addsy);
+ if (name == NULL)
+ name = _("<unknown>");
+ as_fatal (_("Cannot find relocation type for symbol %s, code %d"),
+ name, (int) code);
+ }
+
+ return rel;
+}
diff --git a/gas/config/tc-ns32k.h b/gas/config/tc-ns32k.h
new file mode 100644
index 0000000..02d7196
--- /dev/null
+++ b/gas/config/tc-ns32k.h
@@ -0,0 +1,123 @@
+/* tc-ns32k.h -- Opcode table for National Semi 32k processor
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_NS32K
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TC_PCREL_ADJUST(F) md_pcrel_adjust(F)
+extern int md_pcrel_adjust (fragS *);
+
+#define NO_RELOC BFD_RELOC_NONE
+
+#define TARGET_ARCH bfd_arch_ns32k
+
+#ifndef TARGET_FORMAT /* Maybe defined in te-*.h. */
+#define TARGET_FORMAT "a.out-pc532-mach"
+#endif
+
+#define LOCAL_LABELS_FB 1
+
+#include "bit_fix.h"
+
+#ifdef SEQUENT_COMPATABILITY
+#define DEF_MODEC 20
+#define DEF_MODEL 21
+#endif
+
+#ifndef DEF_MODEC
+#define DEF_MODEC 20
+#endif
+
+#ifndef DEF_MODEL
+#define DEF_MODEL 20
+#endif
+
+#define MAX_ARGS 4
+#define ARG_LEN 50
+
+#define TC_CONS_FIX_NEW cons_fix_new_ns32k
+extern void cons_fix_new_ns32k (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+/* The NS32x32 has a non 0 nop instruction which should be used in aligns. */
+#define NOP_OPCODE 0xa2
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define TC_FRAG_TYPE \
+ struct \
+ { \
+ fragS * fr_opcode_fragP; \
+ unsigned int fr_opcode_offset; \
+ char fr_bsr; \
+ }
+
+#define TC_FRAG_INIT(X) \
+ do \
+ { \
+ frag_opcode_frag (X) = NULL; \
+ frag_opcode_offset (X) = 0; \
+ frag_bsr (X) = 0; \
+ } \
+ while (0)
+
+/* Accessor macros for things which may move around. */
+#define frag_opcode_frag(X) (X)->tc_frag_data.fr_opcode_fragP
+#define frag_opcode_offset(X) (X)->tc_frag_data.fr_opcode_offset
+#define frag_bsr(X) (X)->tc_frag_data.fr_bsr
+
+#define TC_FIX_TYPE \
+ struct \
+ { \
+ fragS * opcode_fragP; \
+ unsigned int opcode_offset; \
+ unsigned int bsr : 1; \
+ }
+
+/* Accessor macros for things which may move around.
+ See comments in write.h. */
+#define fix_im_disp(X) (X)->fx_im_disp
+#define fix_bit_fixP(X) (X)->fx_bit_fixP
+#define fix_opcode_frag(X) (X)->tc_fix_data.opcode_fragP
+#define fix_opcode_offset(X) (X)->tc_fix_data.opcode_offset
+#define fix_bsr(X) (X)->tc_fix_data.bsr
+
+#define TC_INIT_FIX_DATA(X) \
+ do \
+ { \
+ fix_opcode_frag(X) = NULL; \
+ fix_opcode_offset(X) = 0; \
+ fix_bsr(X) = 0; \
+ } \
+ while (0)
+
+#define TC_FIX_DATA_PRINT(FILE, FIX) \
+ do \
+ { \
+ fprintf ((FILE), "opcode_frag=%ld, operand offset=%d, bsr=%d\n", \
+ (unsigned long) fix_opcode_frag (FIX), \
+ fix_opcode_offset (FIX), \
+ fix_bsr (FIX)); \
+ } \
+ while (0)
diff --git a/gas/config/tc-or1k.c b/gas/config/tc-or1k.c
new file mode 100644
index 0000000..7b479ca
--- /dev/null
+++ b/gas/config/tc-or1k.c
@@ -0,0 +1,362 @@
+/* tc-or1k.c -- Assembler for the OpenRISC family.
+ Copyright 2001-2014 Free Software Foundation.
+ Contributed for OR32 by Johan Rydberg, jrydberg@opencores.org
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/> */
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/or1k-desc.h"
+#include "opcodes/or1k-opc.h"
+#include "cgen.h"
+#include "elf/or1k.h"
+#include "dw2gencfi.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];
+}
+or1k_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 OR1K_SHORTOPTS "m:"
+const char * md_shortopts = OR1K_SHORTOPTS;
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+unsigned long or1k_machine = 0; /* default */
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_show_usage (FILE * stream ATTRIBUTE_UNUSED)
+{
+}
+
+static void
+ignore_pseudo (int val ATTRIBUTE_UNUSED)
+{
+ discard_rest_of_line ();
+}
+
+static bfd_boolean nodelay = FALSE;
+static void
+s_nodelay (int val ATTRIBUTE_UNUSED)
+{
+ nodelay = TRUE;
+}
+
+const char or1k_comment_chars [] = ";#";
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 },
+ { "word", cons, 4 },
+ { "proc", ignore_pseudo, 0 },
+ { "endproc", ignore_pseudo, 0 },
+ { "nodelay", s_nodelay, 0 },
+ { NULL, NULL, 0 }
+};
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = or1k_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_BIG,
+ CGEN_CPU_OPEN_END);
+ or1k_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);
+}
+
+void
+md_assemble (char * str)
+{
+ static int last_insn_had_delay_slot = 0;
+ or1k_insn insn;
+ char * errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = or1k_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", 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);
+ (void) last_insn_had_delay_slot;
+}
+
+
+/* The syntax in the manual says constants begin with '#'.
+ We just ignore it. */
+
+void
+md_operand (expressionS * expressionP)
+{
+ if (* input_line_pointer == '#')
+ {
+ input_line_pointer ++;
+ expression (expressionP);
+ }
+}
+
+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;
+}
+
+
+/* Interface to relax_segment. */
+
+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 add to the size of the current frag
+ 4) which index into the table to try if we can't fit into this one. */
+
+ /* The first entry must be unused because an `rlx_more' value of zero ends
+ each list. */
+ {1, 1, 0, 0},
+
+ /* The displacement used by GAS is from the end of the 4 byte insn,
+ so we subtract 4 from the following. */
+ {(((1 << 25) - 1) << 2) - 4, -(1 << 25) - 4, 0, 0},
+};
+
+int
+md_estimate_size_before_relax (fragS * fragP, segT segment ATTRIBUTE_UNUSED)
+{
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+ /* FIXME */
+}
+
+
+/* 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)
+ || S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)))
+ {
+ /* 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)
+{
+ if (fixP->fx_cgen.opinfo)
+ return fixP->fx_cgen.opinfo;
+
+ switch (operand->type)
+ {
+ case OR1K_OPERAND_DISP26:
+ fixP->fx_pcrel = 1;
+ return BFD_RELOC_OR1K_REL_26;
+
+ default: /* avoid -Wall warning */
+ return BFD_RELOC_NONE;
+ }
+}
+
+/* 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_bigendian (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)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+bfd_boolean
+or1k_fix_adjustable (fixS * fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return FALSE;
+
+ return TRUE;
+}
+
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+
+arelent *
+tc_gen_reloc (asection *sec, fixS *fx)
+{
+ bfd_reloc_code_real_type code = fx->fx_r_type;
+
+ if (fx->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fx->fx_addsy), GOT_NAME) == 0
+ && (code == BFD_RELOC_OR1K_GOTPC_HI16
+ || code == BFD_RELOC_OR1K_GOTPC_LO16))
+ {
+ 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);
+}
+
+void
+or1k_apply_fix (struct fix *f, valueT *t, segT s)
+{
+ gas_cgen_md_apply_fix (f, t, s);
+
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_OR1K_TLS_GD_HI16:
+ case BFD_RELOC_OR1K_TLS_GD_LO16:
+ case BFD_RELOC_OR1K_TLS_LDM_HI16:
+ case BFD_RELOC_OR1K_TLS_LDM_LO16:
+ case BFD_RELOC_OR1K_TLS_LDO_HI16:
+ case BFD_RELOC_OR1K_TLS_LDO_LO16:
+ case BFD_RELOC_OR1K_TLS_IE_HI16:
+ case BFD_RELOC_OR1K_TLS_IE_LO16:
+ case BFD_RELOC_OR1K_TLS_LE_HI16:
+ case BFD_RELOC_OR1K_TLS_LE_LO16:
+ S_SET_THREAD_LOCAL (f->fx_addsy);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+or1k_elf_final_processing (void)
+{
+ if (nodelay)
+ elf_elfheader (stdoutput)->e_flags |= EF_OR1K_NODELAY;
+}
+
+/* Standard calling conventions leave the CFA at SP on entry. */
+
+void
+or1k_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (1);
+}
+
diff --git a/gas/config/tc-or1k.h b/gas/config/tc-or1k.h
new file mode 100644
index 0000000..18e22a5
--- /dev/null
+++ b/gas/config/tc-or1k.h
@@ -0,0 +1,79 @@
+/* tc-or1k.h -- Header file for tc-or1k.c.
+ Copyright 2001-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/> */
+
+#define TC_OR1K
+
+#define LISTING_HEADER "Or1k GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_or1k
+
+extern unsigned long or1k_machine;
+#define TARGET_MACH (or1k_machine)
+
+#define TARGET_FORMAT "elf32-or1k"
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+extern const char or1k_comment_chars [];
+#define tc_comment_chars or1k_comment_chars
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK 1 /* .-foo gets turned into PC relative relocs. */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_apply_fix or1k_apply_fix
+extern void or1k_apply_fix (struct fix *, valueT *, segT);
+
+extern bfd_boolean or1k_fix_adjustable (struct fix *);
+#define tc_fix_adjustable(FIX) or1k_fix_adjustable (FIX)
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+extern long md_pcrel_from_section (struct fix *, segT);
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+
+/* For 8 vs 16 vs 32 bit branch selection. */
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+#define GAS_CGEN_PCREL_R_TYPE(r_type) gas_cgen_pcrel_r_type(r_type)
+
+#define elf_tc_final_processing or1k_elf_final_processing
+void or1k_elf_final_processing (void);
+
+/* Enable cfi directives. */
+#define TARGET_USE_CFIPOP 1
+
+/* Stack grows to lower addresses and wants 4 byte boundary. */
+#define DWARF2_CIE_DATA_ALIGNMENT -4
+
+/* Define the column that represents the PC. */
+#define DWARF2_DEFAULT_RETURN_COLUMN 9
+
+/* or1k instructions are 4 bytes long. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+
+#define tc_cfi_frame_initial_instructions \
+ or1k_cfi_frame_initial_instructions
+extern void or1k_cfi_frame_initial_instructions (void);
diff --git a/gas/config/tc-pdp11.c b/gas/config/tc-pdp11.c
new file mode 100644
index 0000000..1b3df58
--- /dev/null
+++ b/gas/config/tc-pdp11.c
@@ -0,0 +1,1453 @@
+/* tc-pdp11.c - pdp11-specific -
+ Copyright (C) 2001-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcode/pdp11.h"
+
+extern int flonum_gen2vax (int, FLONUM_TYPE * f, LITTLENUM_TYPE *);
+
+#define TRUE 1
+#define FALSE 0
+
+/* A representation for PDP-11 machine code. */
+struct pdp11_code
+{
+ char *error;
+ int code;
+ int additional; /* Is there an additional word? */
+ int word; /* Additional word, if any. */
+ struct
+ {
+ bfd_reloc_code_real_type type;
+ expressionS exp;
+ int pc_rel;
+ } reloc;
+};
+
+/* Instruction set extensions.
+
+ If you change this from an array to something else, please update
+ the "PDP-11 instruction set extensions" comment in pdp11.h. */
+int pdp11_extension[PDP11_EXT_NUM];
+
+/* Assembly options. */
+
+#define ASM_OPT_PIC 1
+#define ASM_OPT_NUM 2
+
+int asm_option[ASM_OPT_NUM];
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment. */
+const char comment_chars[] = "#/";
+
+/* These chars only start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#/";
+
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant. */
+/* as in 0f123.456. */
+/* or 0H1.234E-12 (see exp chars above). */
+const char FLT_CHARS[] = "dDfF";
+
+void pseudo_even (int);
+void pseudo_bss (int);
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "bss", pseudo_bss, 0 },
+ { "even", pseudo_even, 0 },
+ { 0, 0, 0 },
+};
+
+static struct hash_control *insn_hash = NULL;
+
+static int
+set_option (char *arg)
+{
+ int yes = 1;
+
+ if (strcmp (arg, "all-extensions") == 0
+ || strcmp (arg, "all") == 0)
+ {
+ memset (pdp11_extension, ~0, sizeof pdp11_extension);
+ pdp11_extension[PDP11_NONE] = 0;
+ return 1;
+ }
+ else if (strcmp (arg, "no-extensions") == 0)
+ {
+ memset (pdp11_extension, 0, sizeof pdp11_extension);
+ pdp11_extension[PDP11_BASIC] = 1;
+ return 1;
+ }
+
+ if (strncmp (arg, "no-", 3) == 0)
+ {
+ yes = 0;
+ arg += 3;
+ }
+
+ /* Commersial instructions. */
+ if (strcmp (arg, "cis") == 0)
+ pdp11_extension[PDP11_CIS] = yes;
+ /* Call supervisor mode. */
+ else if (strcmp (arg, "csm") == 0)
+ pdp11_extension[PDP11_CSM] = yes;
+ /* Extended instruction set. */
+ else if (strcmp (arg, "eis") == 0)
+ pdp11_extension[PDP11_EIS] = pdp11_extension[PDP11_LEIS] = yes;
+ /* KEV11 floating-point. */
+ else if (strcmp (arg, "fis") == 0
+ || strcmp (arg, "kev11") == 0
+ || strcmp (arg, "kev-11") == 0)
+ pdp11_extension[PDP11_FIS] = yes;
+ /* FP-11 floating-point. */
+ else if (strcmp (arg, "fpp") == 0
+ || strcmp (arg, "fpu") == 0
+ || strcmp (arg, "fp11") == 0
+ || strcmp (arg, "fp-11") == 0
+ || strcmp (arg, "fpj11") == 0
+ || strcmp (arg, "fp-j11") == 0
+ || strcmp (arg, "fpj-11") == 0)
+ pdp11_extension[PDP11_FPP] = yes;
+ /* Limited extended insns. */
+ else if (strcmp (arg, "limited-eis") == 0)
+ {
+ pdp11_extension[PDP11_LEIS] = yes;
+ if (!pdp11_extension[PDP11_LEIS])
+ pdp11_extension[PDP11_EIS] = 0;
+ }
+ /* Move from processor type. */
+ else if (strcmp (arg, "mfpt") == 0)
+ pdp11_extension[PDP11_MFPT] = yes;
+ /* Multiprocessor insns: */
+ else if (strncmp (arg, "mproc", 5) == 0
+ /* TSTSET, WRTLCK */
+ || strncmp (arg, "multiproc", 9) == 0)
+ pdp11_extension[PDP11_MPROC] = yes;
+ /* Move from/to proc status. */
+ else if (strcmp (arg, "mxps") == 0)
+ pdp11_extension[PDP11_MXPS] = yes;
+ /* Position-independent code. */
+ else if (strcmp (arg, "pic") == 0)
+ asm_option[ASM_OPT_PIC] = yes;
+ /* Set priority level. */
+ else if (strcmp (arg, "spl") == 0)
+ pdp11_extension[PDP11_SPL] = yes;
+ /* Microcode instructions: */
+ else if (strcmp (arg, "ucode") == 0
+ /* LDUB, MED, XFC */
+ || strcmp (arg, "microcode") == 0)
+ pdp11_extension[PDP11_UCODE] = yes;
+ else
+ return 0;
+
+ return 1;
+}
+
+
+static void
+init_defaults (void)
+{
+ static int first = 1;
+
+ if (first)
+ {
+ set_option ("all-extensions");
+ set_option ("pic");
+ first = 0;
+ }
+}
+
+void
+md_begin (void)
+{
+ int i;
+
+ init_defaults ();
+
+ insn_hash = hash_new ();
+ if (insn_hash == NULL)
+ as_fatal (_("Virtual memory exhausted"));
+
+ for (i = 0; i < pdp11_num_opcodes; i++)
+ hash_insert (insn_hash, pdp11_opcodes[i].name, (void *) (pdp11_opcodes + i));
+ for (i = 0; i < pdp11_num_aliases; i++)
+ hash_insert (insn_hash, pdp11_aliases[i].name, (void *) (pdp11_aliases + i));
+}
+
+void
+md_number_to_chars (char con[], valueT value, int nbytes)
+{
+ /* On a PDP-11, 0x1234 is stored as "\x12\x34", and
+ 0x12345678 is stored as "\x56\x78\x12\x34". It's
+ anyones guess what 0x123456 would be stored like. */
+
+ switch (nbytes)
+ {
+ case 0:
+ break;
+ case 1:
+ con[0] = value & 0xff;
+ break;
+ case 2:
+ con[0] = value & 0xff;
+ con[1] = (value >> 8) & 0xff;
+ break;
+ case 4:
+ con[0] = (value >> 16) & 0xff;
+ con[1] = (value >> 24) & 0xff;
+ con[2] = value & 0xff;
+ con[3] = (value >> 8) & 0xff;
+ break;
+ default:
+ BAD_CASE (nbytes);
+ }
+}
+
+/* Fix up some data or instructions after we find out the value of a symbol
+ that they reference. Knows about order of bytes in address. */
+
+void
+md_apply_fix (fixS *fixP,
+ valueT * valP,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ valueT code;
+ valueT mask;
+ valueT val = * valP;
+ char *buf;
+ int shift;
+ int size;
+
+ buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ size = fixP->fx_size;
+ code = md_chars_to_number ((unsigned char *) buf, size);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ mask = 0xffff;
+ shift = 0;
+ break;
+ case BFD_RELOC_PDP11_DISP_8_PCREL:
+ mask = 0x00ff;
+ shift = 1;
+ break;
+ case BFD_RELOC_PDP11_DISP_6_PCREL:
+ mask = 0x003f;
+ shift = 1;
+ val = -val;
+ break;
+ default:
+ BAD_CASE (fixP->fx_r_type);
+ }
+
+ if (fixP->fx_addsy != NULL)
+ val += symbol_get_bfdsym (fixP->fx_addsy)->section->vma;
+ /* *value += fixP->fx_addsy->bsym->section->vma; */
+
+ code &= ~mask;
+ code |= (val >> shift) & mask;
+ number_to_chars_littleendian (buf, code, size);
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+long
+md_chars_to_number (con, nbytes)
+ unsigned char con[]; /* Low order byte 1st. */
+ int nbytes; /* Number of bytes in the input. */
+{
+ /* On a PDP-11, 0x1234 is stored as "\x12\x34", and
+ 0x12345678 is stored as "\x56\x78\x12\x34". It's
+ anyones guess what 0x123456 would be stored like. */
+ switch (nbytes)
+ {
+ case 0:
+ return 0;
+ case 1:
+ return con[0];
+ case 2:
+ return (con[1] << BITS_PER_CHAR) | con[0];
+ case 4:
+ return
+ (((con[1] << BITS_PER_CHAR) | con[0]) << (2 * BITS_PER_CHAR))
+ |((con[3] << BITS_PER_CHAR) | con[2]);
+ default:
+ BAD_CASE (nbytes);
+ return 0;
+ }
+}
+
+static char *
+skip_whitespace (char *str)
+{
+ while (*str == ' ' || *str == '\t')
+ str++;
+ return str;
+}
+
+static char *
+find_whitespace (char *str)
+{
+ while (*str != ' ' && *str != '\t' && *str != 0)
+ str++;
+ return str;
+}
+
+static char *
+parse_reg (char *str, struct pdp11_code *operand)
+{
+ str = skip_whitespace (str);
+ if (TOLOWER (*str) == 'r')
+ {
+ str++;
+ switch (*str)
+ {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ operand->code = *str - '0';
+ str++;
+ break;
+ default:
+ operand->error = _("Bad register name");
+ return str - 1;
+ }
+ }
+ else if (strncmp (str, "sp", 2) == 0
+ || strncmp (str, "SP", 2) == 0)
+ {
+ operand->code = 6;
+ str += 2;
+ }
+ else if (strncmp (str, "pc", 2) == 0
+ || strncmp (str, "PC", 2) == 0)
+ {
+ operand->code = 7;
+ str += 2;
+ }
+ else
+ {
+ operand->error = _("Bad register name");
+ return str;
+ }
+
+ return str;
+}
+
+static char *
+parse_ac5 (char *str, struct pdp11_code *operand)
+{
+ str = skip_whitespace (str);
+ if (strncmp (str, "fr", 2) == 0
+ || strncmp (str, "FR", 2) == 0
+ || strncmp (str, "ac", 2) == 0
+ || strncmp (str, "AC", 2) == 0)
+ {
+ str += 2;
+ switch (*str)
+ {
+ case '0': case '1': case '2': case '3':
+ case '4': case '5':
+ operand->code = *str - '0';
+ str++;
+ break;
+ default:
+ operand->error = _("Bad register name");
+ return str - 2;
+ }
+ }
+ else
+ {
+ operand->error = _("Bad register name");
+ return str;
+ }
+
+ return str;
+}
+
+static char *
+parse_ac (char *str, struct pdp11_code *operand)
+{
+ str = parse_ac5 (str, operand);
+ if (!operand->error && operand->code > 3)
+ {
+ operand->error = _("Bad register name");
+ return str - 3;
+ }
+
+ return str;
+}
+
+static char *
+parse_expression (char *str, struct pdp11_code *operand)
+{
+ char *save_input_line_pointer;
+ segT seg;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&operand->reloc.exp);
+ if (seg == NULL)
+ {
+ input_line_pointer = save_input_line_pointer;
+ operand->error = _("Error in expression");
+ return str;
+ }
+
+ str = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+
+ operand->reloc.pc_rel = 0;
+
+ return str;
+}
+
+static char *
+parse_op_no_deferred (char *str, struct pdp11_code *operand)
+{
+ LITTLENUM_TYPE literal_float[2];
+
+ str = skip_whitespace (str);
+
+ switch (*str)
+ {
+ case '(': /* (rn) and (rn)+ */
+ str = parse_reg (str + 1, operand);
+ if (operand->error)
+ return str;
+ str = skip_whitespace (str);
+ if (*str != ')')
+ {
+ operand->error = _("Missing ')'");
+ return str;
+ }
+ str++;
+ if (*str == '+')
+ {
+ operand->code |= 020;
+ str++;
+ }
+ else
+ {
+ operand->code |= 010;
+ }
+ break;
+
+ /* Immediate. */
+ case '#':
+ case '$':
+ str = parse_expression (str + 1, operand);
+ if (operand->error)
+ return str;
+ operand->additional = TRUE;
+ operand->word = operand->reloc.exp.X_add_number;
+ switch (operand->reloc.exp.X_op)
+ {
+ case O_constant:
+ break;
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ operand->reloc.type = BFD_RELOC_16;
+ operand->reloc.pc_rel = 0;
+ break;
+ case O_big:
+ if (operand->reloc.exp.X_add_number > 0)
+ {
+ operand->error = _("Error in expression");
+ break;
+ }
+ /* It's a floating literal... */
+ know (operand->reloc.exp.X_add_number < 0);
+ flonum_gen2vax ('f', &generic_floating_point_number, literal_float);
+ operand->word = literal_float[0];
+ if (literal_float[1] != 0)
+ as_warn (_("Low order bits truncated in immediate float operand"));
+ break;
+ default:
+ operand->error = _("Error in expression");
+ break;
+ }
+ operand->code = 027;
+ break;
+
+ /* label, d(rn), -(rn) */
+ default:
+ {
+ if (strncmp (str, "-(", 2) == 0) /* -(rn) */
+ {
+ str = parse_reg (str + 2, operand);
+ if (operand->error)
+ return str;
+ str = skip_whitespace (str);
+ if (*str != ')')
+ {
+ operand->error = _("Missing ')'");
+ return str;
+ }
+ operand->code |= 040;
+ str++;
+ break;
+ }
+
+ str = parse_expression (str, operand);
+ if (operand->error)
+ return str;
+
+ str = skip_whitespace (str);
+
+ if (*str != '(')
+ {
+ operand->code = 067;
+ operand->additional = 1;
+ operand->word = 0;
+ operand->reloc.type = BFD_RELOC_16_PCREL;
+ operand->reloc.pc_rel = 1;
+ break;
+ }
+
+ /* d(rn) */
+ str++;
+ str = parse_reg (str, operand);
+ if (operand->error)
+ return str;
+
+ str = skip_whitespace (str);
+
+ if (*str != ')')
+ {
+ operand->error = _("Missing ')'");
+ return str;
+ }
+
+ str++;
+ operand->additional = TRUE;
+ operand->code |= 060;
+ switch (operand->reloc.exp.X_op)
+ {
+ case O_symbol:
+ operand->reloc.type = BFD_RELOC_16;
+ operand->reloc.pc_rel = 0;
+ break;
+ case O_constant:
+ if ((operand->code & 7) == 7)
+ {
+ operand->reloc.pc_rel = 1;
+ operand->word = operand->reloc.exp.X_add_number;
+ }
+ else
+ operand->word = operand->reloc.exp.X_add_number;
+
+ break;
+ default:
+ BAD_CASE (operand->reloc.exp.X_op);
+ }
+ break;
+ }
+ }
+
+ return str;
+}
+
+static char *
+parse_op_noreg (char *str, struct pdp11_code *operand)
+{
+ str = skip_whitespace (str);
+ operand->error = NULL;
+
+ if (*str == '@' || *str == '*')
+ {
+ str = parse_op_no_deferred (str + 1, operand);
+ if (operand->error)
+ return str;
+ operand->code |= 010;
+ }
+ else
+ str = parse_op_no_deferred (str, operand);
+
+ return str;
+}
+
+static char *
+parse_op (char *str, struct pdp11_code *operand)
+{
+ str = skip_whitespace (str);
+
+ str = parse_reg (str, operand);
+ if (!operand->error)
+ return str;
+
+ operand->error = NULL;
+ parse_ac5 (str, operand);
+ if (!operand->error)
+ {
+ operand->error = _("Float AC not legal as integer operand");
+ return str;
+ }
+
+ return parse_op_noreg (str, operand);
+}
+
+static char *
+parse_fop (char *str, struct pdp11_code *operand)
+{
+ str = skip_whitespace (str);
+
+ str = parse_ac5 (str, operand);
+ if (!operand->error)
+ return str;
+
+ operand->error = NULL;
+ parse_reg (str, operand);
+ if (!operand->error)
+ {
+ operand->error = _("General register not legal as float operand");
+ return str;
+ }
+
+ return parse_op_noreg (str, operand);
+}
+
+static char *
+parse_separator (char *str, int *error)
+{
+ str = skip_whitespace (str);
+ *error = (*str != ',');
+ if (!*error)
+ str++;
+ return str;
+}
+
+void
+md_assemble (char *instruction_string)
+{
+ const struct pdp11_opcode *op;
+ struct pdp11_code insn, op1, op2;
+ int error;
+ int size;
+ char *err = NULL;
+ char *str;
+ char *p;
+ char c;
+
+ str = skip_whitespace (instruction_string);
+ p = find_whitespace (str);
+ if (p - str == 0)
+ {
+ as_bad (_("No instruction found"));
+ return;
+ }
+
+ c = *p;
+ *p = '\0';
+ op = (struct pdp11_opcode *)hash_find (insn_hash, str);
+ *p = c;
+ if (op == 0)
+ {
+ as_bad (_("Unknown instruction '%s'"), str);
+ return;
+ }
+
+ if (!pdp11_extension[op->extension])
+ {
+ as_warn (_("Unsupported instruction set extension: %s"), op->name);
+ return;
+ }
+
+ insn.error = NULL;
+ insn.code = op->opcode;
+ insn.reloc.type = BFD_RELOC_NONE;
+ op1.error = NULL;
+ op1.additional = FALSE;
+ op1.reloc.type = BFD_RELOC_NONE;
+ op2.error = NULL;
+ op2.additional = FALSE;
+ op2.reloc.type = BFD_RELOC_NONE;
+
+ str = p;
+ size = 2;
+
+ switch (op->type)
+ {
+ case PDP11_OPCODE_NO_OPS:
+ str = skip_whitespace (str);
+ if (*str == 0)
+ str = "";
+ break;
+
+ case PDP11_OPCODE_IMM3:
+ case PDP11_OPCODE_IMM6:
+ case PDP11_OPCODE_IMM8:
+ str = skip_whitespace (str);
+ if (*str == '#' || *str == '$')
+ str++;
+ str = parse_expression (str, &op1);
+ if (op1.error)
+ break;
+ if (op1.reloc.exp.X_op != O_constant || op1.reloc.type != BFD_RELOC_NONE)
+ {
+ op1.error = _("operand is not an absolute constant");
+ break;
+ }
+ switch (op->type)
+ {
+ case PDP11_OPCODE_IMM3:
+ if (op1.reloc.exp.X_add_number & ~7)
+ {
+ op1.error = _("3-bit immediate out of range");
+ break;
+ }
+ break;
+ case PDP11_OPCODE_IMM6:
+ if (op1.reloc.exp.X_add_number & ~0x3f)
+ {
+ op1.error = _("6-bit immediate out of range");
+ break;
+ }
+ break;
+ case PDP11_OPCODE_IMM8:
+ if (op1.reloc.exp.X_add_number & ~0xff)
+ {
+ op1.error = _("8-bit immediate out of range");
+ break;
+ }
+ break;
+ }
+ insn.code |= op1.reloc.exp.X_add_number;
+ break;
+
+ case PDP11_OPCODE_DISPL:
+ {
+ char *new_pointer;
+ new_pointer = parse_expression (str, &op1);
+ op1.code = 0;
+ op1.reloc.pc_rel = 1;
+ op1.reloc.type = BFD_RELOC_PDP11_DISP_8_PCREL;
+ if (op1.reloc.exp.X_op != O_symbol)
+ {
+ op1.error = _("Symbol expected");
+ break;
+ }
+ if (op1.code & ~0xff)
+ {
+ err = _("8-bit displacement out of range");
+ break;
+ }
+ str = new_pointer;
+ insn.code |= op1.code;
+ insn.reloc = op1.reloc;
+ }
+ break;
+
+ case PDP11_OPCODE_REG:
+ str = parse_reg (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ break;
+
+ case PDP11_OPCODE_OP:
+ str = parse_op (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ break;
+
+ case PDP11_OPCODE_FOP:
+ str = parse_fop (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ break;
+
+ case PDP11_OPCODE_REG_OP:
+ str = parse_reg (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op2.error = _("Missing ','");
+ break;
+ }
+ str = parse_op (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ break;
+
+ case PDP11_OPCODE_REG_OP_REV:
+ str = parse_op (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op2.error = _("Missing ','");
+ break;
+ }
+ str = parse_reg (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ break;
+
+ case PDP11_OPCODE_AC_FOP:
+ str = parse_ac (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op1.error = _("Missing ','");
+ break;
+ }
+ str = parse_fop (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ break;
+
+ case PDP11_OPCODE_FOP_AC:
+ str = parse_fop (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op1.error = _("Missing ','");
+ break;
+ }
+ str = parse_ac (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ break;
+
+ case PDP11_OPCODE_AC_OP:
+ str = parse_ac (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op1.error = _("Missing ','");
+ break;
+ }
+ str = parse_op (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ break;
+
+ case PDP11_OPCODE_OP_AC:
+ str = parse_op (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code;
+ if (op1.additional)
+ size += 2;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op1.error = _("Missing ','");
+ break;
+ }
+ str = parse_ac (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ break;
+
+ case PDP11_OPCODE_OP_OP:
+ str = parse_op (str, &op1);
+ if (op1.error)
+ break;
+ insn.code |= op1.code << 6;
+ if (op1.additional)
+ size += 2;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op2.error = _("Missing ','");
+ break;
+ }
+ str = parse_op (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code;
+ if (op2.additional)
+ size += 2;
+ break;
+
+ case PDP11_OPCODE_REG_DISPL:
+ {
+ char *new_pointer;
+ str = parse_reg (str, &op2);
+ if (op2.error)
+ break;
+ insn.code |= op2.code << 6;
+ str = parse_separator (str, &error);
+ if (error)
+ {
+ op1.error = _("Missing ','");
+ break;
+ }
+ new_pointer = parse_expression (str, &op1);
+ op1.code = 0;
+ op1.reloc.pc_rel = 1;
+ op1.reloc.type = BFD_RELOC_PDP11_DISP_6_PCREL;
+ if (op1.reloc.exp.X_op != O_symbol)
+ {
+ op1.error = _("Symbol expected");
+ break;
+ }
+ if (op1.code & ~0x3f)
+ {
+ err = _("6-bit displacement out of range");
+ break;
+ }
+ str = new_pointer;
+ insn.code |= op1.code;
+ insn.reloc = op1.reloc;
+ }
+ break;
+
+ default:
+ BAD_CASE (op->type);
+ }
+
+ if (op1.error)
+ err = op1.error;
+ else if (op2.error)
+ err = op2.error;
+ else
+ {
+ str = skip_whitespace (str);
+ if (*str)
+ err = _("Too many operands");
+ }
+
+ {
+ char *to = NULL;
+
+ if (err)
+ {
+ as_bad ("%s", err);
+ return;
+ }
+
+ to = frag_more (size);
+
+ md_number_to_chars (to, insn.code, 2);
+ if (insn.reloc.type != BFD_RELOC_NONE)
+ fix_new_exp (frag_now, to - frag_now->fr_literal, 2,
+ &insn.reloc.exp, insn.reloc.pc_rel, insn.reloc.type);
+ to += 2;
+
+ if (op1.additional)
+ {
+ md_number_to_chars (to, op1.word, 2);
+ if (op1.reloc.type != BFD_RELOC_NONE)
+ fix_new_exp (frag_now, to - frag_now->fr_literal, 2,
+ &op1.reloc.exp, op1.reloc.pc_rel, op1.reloc.type);
+ to += 2;
+ }
+
+ if (op2.additional)
+ {
+ md_number_to_chars (to, op2.word, 2);
+ if (op2.reloc.type != BFD_RELOC_NONE)
+ fix_new_exp (frag_now, to - frag_now->fr_literal, 2,
+ &op2.reloc.exp, op2.reloc.pc_rel, op2.reloc.type);
+ }
+ }
+}
+
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+}
+
+int md_short_jump_size = 2;
+int md_long_jump_size = 4;
+
+void
+md_create_short_jump (char *ptr ATTRIBUTE_UNUSED,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+}
+
+void
+md_create_long_jump (char *ptr ATTRIBUTE_UNUSED,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+}
+
+static int
+set_cpu_model (char *arg)
+{
+ char buf[4];
+ char *model = buf;
+
+ if (arg[0] == 'k')
+ arg++;
+
+ *model++ = *arg++;
+
+ if (strchr ("abdx", model[-1]) == NULL)
+ return 0;
+
+ if (model[-1] == 'd')
+ {
+ if (arg[0] == 'f' || arg[0] == 'j')
+ model[-1] = *arg++;
+ }
+ else if (model[-1] == 'x')
+ {
+ if (arg[0] == 't')
+ model[-1] = *arg++;
+ }
+
+ if (arg[0] == '-')
+ arg++;
+
+ if (strncmp (arg, "11", 2) != 0)
+ return 0;
+ arg += 2;
+
+ if (arg[0] == '-')
+ {
+ if (*++arg == 0)
+ return 0;
+ }
+
+ /* Allow up to two revision letters. */
+ if (arg[0] != 0)
+ *model++ = *arg++;
+ if (arg[0] != 0)
+ *model++ = *arg++;
+
+ *model++ = 0;
+
+ set_option ("no-extensions");
+
+ /* KA11 (11/15/20). */
+ if (strncmp (buf, "a", 1) == 0)
+ return 1; /* No extensions. */
+
+ /* KB11 (11/45/50/55/70). */
+ else if (strncmp (buf, "b", 1) == 0)
+ return set_option ("eis") && set_option ("spl");
+
+ /* KD11-A (11/35/40). */
+ else if (strncmp (buf, "da", 2) == 0)
+ return set_option ("limited-eis");
+
+ /* KD11-B (11/05/10). */
+ else if (strncmp (buf, "db", 2) == 0
+ /* KD11-D (11/04). */
+ || strncmp (buf, "dd", 2) == 0)
+ return 1; /* no extensions */
+
+ /* KD11-E (11/34). */
+ else if (strncmp (buf, "de", 2) == 0)
+ return set_option ("eis") && set_option ("mxps");
+
+ /* KD11-F (11/03). */
+ else if (strncmp (buf, "df", 2) == 0
+ /* KD11-H (11/03). */
+ || strncmp (buf, "dh", 2) == 0
+ /* KD11-Q (11/03). */
+ || strncmp (buf, "dq", 2) == 0)
+ return set_option ("limited-eis") && set_option ("mxps");
+
+ /* KD11-K (11/60). */
+ else if (strncmp (buf, "dk", 2) == 0)
+ return set_option ("eis")
+ && set_option ("mxps")
+ && set_option ("ucode");
+
+ /* KD11-Z (11/44). */
+ else if (strncmp (buf, "dz", 2) == 0)
+ return set_option ("csm")
+ && set_option ("eis")
+ && set_option ("mfpt")
+ && set_option ("mxps")
+ && set_option ("spl");
+
+ /* F11 (11/23/24). */
+ else if (strncmp (buf, "f", 1) == 0)
+ return set_option ("eis")
+ && set_option ("mfpt")
+ && set_option ("mxps");
+
+ /* J11 (11/53/73/83/84/93/94). */
+ else if (strncmp (buf, "j", 1) == 0)
+ return set_option ("csm")
+ && set_option ("eis")
+ && set_option ("mfpt")
+ && set_option ("multiproc")
+ && set_option ("mxps")
+ && set_option ("spl");
+
+ /* T11 (11/21). */
+ else if (strncmp (buf, "t", 1) == 0)
+ return set_option ("limited-eis")
+ && set_option ("mxps");
+
+ else
+ return 0;
+}
+
+static int
+set_machine_model (char *arg)
+{
+ if (strncmp (arg, "pdp-11/", 7) != 0
+ && strncmp (arg, "pdp11/", 6) != 0
+ && strncmp (arg, "11/", 3) != 0)
+ return 0;
+
+ if (strncmp (arg, "pdp", 3) == 0)
+ arg += 3;
+ if (arg[0] == '-')
+ arg++;
+ if (strncmp (arg, "11/", 3) == 0)
+ arg += 3;
+
+ if (strcmp (arg, "03") == 0)
+ return set_cpu_model ("kd11f");
+
+ else if (strcmp (arg, "04") == 0)
+ return set_cpu_model ("kd11d");
+
+ else if (strcmp (arg, "05") == 0
+ || strcmp (arg, "10") == 0)
+ return set_cpu_model ("kd11b");
+
+ else if (strcmp (arg, "15") == 0
+ || strcmp (arg, "20") == 0)
+ return set_cpu_model ("ka11");
+
+ else if (strcmp (arg, "21") == 0)
+ return set_cpu_model ("t11");
+
+ else if (strcmp (arg, "23") == 0
+ || strcmp (arg, "24") == 0)
+ return set_cpu_model ("f11");
+
+ else if (strcmp (arg, "34") == 0
+ || strcmp (arg, "34a") == 0)
+ return set_cpu_model ("kd11e");
+
+ else if (strcmp (arg, "35") == 0
+ || strcmp (arg, "40") == 0)
+ return set_cpu_model ("kd11da");
+
+ else if (strcmp (arg, "44") == 0)
+ return set_cpu_model ("kd11dz");
+
+ else if (strcmp (arg, "45") == 0
+ || strcmp (arg, "50") == 0
+ || strcmp (arg, "55") == 0
+ || strcmp (arg, "70") == 0)
+ return set_cpu_model ("kb11");
+
+ else if (strcmp (arg, "60") == 0)
+ return set_cpu_model ("kd11k");
+
+ else if (strcmp (arg, "53") == 0
+ || strcmp (arg, "73") == 0
+ || strcmp (arg, "83") == 0
+ || strcmp (arg, "84") == 0
+ || strcmp (arg, "93") == 0
+ || strcmp (arg, "94") == 0)
+ return set_cpu_model ("j11")
+ && set_option ("fpp");
+
+ else
+ return 0;
+}
+
+const char *md_shortopts = "m:";
+
+struct option md_longopts[] =
+{
+#define OPTION_CPU 257
+ { "cpu", required_argument, NULL, OPTION_CPU },
+#define OPTION_MACHINE 258
+ { "machine", required_argument, NULL, OPTION_MACHINE },
+#define OPTION_PIC 259
+ { "pic", no_argument, NULL, OPTION_PIC },
+ { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Invocation line includes a switch not recognized by the base assembler.
+ See if it's a processor-specific option. */
+
+int
+md_parse_option (int c, char *arg)
+{
+ init_defaults ();
+
+ switch (c)
+ {
+ case 'm':
+ if (set_option (arg))
+ return 1;
+ if (set_cpu_model (arg))
+ return 1;
+ if (set_machine_model (arg))
+ return 1;
+ break;
+
+ case OPTION_CPU:
+ if (set_cpu_model (arg))
+ return 1;
+ break;
+
+ case OPTION_MACHINE:
+ if (set_machine_model (arg))
+ return 1;
+ break;
+
+ case OPTION_PIC:
+ if (set_option ("pic"))
+ return 1;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, "\
+\n\
+PDP-11 instruction set extentions:\n\
+\n\
+-m(no-)cis allow (disallow) commersial instruction set\n\
+-m(no-)csm allow (disallow) CSM instruction\n\
+-m(no-)eis allow (disallow) full extended instruction set\n\
+-m(no-)fis allow (disallow) KEV11 floating-point instructions\n\
+-m(no-)fpp allow (disallow) FP-11 floating-point instructions\n\
+-m(no-)fpu allow (disallow) FP-11 floating-point instructions\n\
+-m(no-)limited-eis allow (disallow) limited extended instruction set\n\
+-m(no-)mfpt allow (disallow) processor type instruction\n\
+-m(no-)multiproc allow (disallow) multiprocessor instructions\n\
+-m(no-)mxps allow (disallow) processor status instructions\n\
+-m(no-)spl allow (disallow) SPL instruction\n\
+-m(no-)ucode allow (disallow) microcode instructions\n\
+-mall-extensions allow all instruction set extensions\n\
+ (this is the default)\n\
+-mno-extentions disallow all instruction set extensions\n\
+-pic generate position-indepenent code\n\
+\n\
+PDP-11 CPU model options:\n\
+\n\
+-mka11* KA11 CPU. base line instruction set only\n\
+-mkb11* KB11 CPU. enable full EIS and SPL\n\
+-mkd11a* KD11-A CPU. enable limited EIS\n\
+-mkd11b* KD11-B CPU. base line instruction set only\n\
+-mkd11d* KD11-D CPU. base line instruction set only\n\
+-mkd11e* KD11-E CPU. enable full EIS, MTPS, and MFPS\n\
+-mkd11f* KD11-F CPU. enable limited EIS, MTPS, and MFPS\n\
+-mkd11h* KD11-H CPU. enable limited EIS, MTPS, and MFPS\n\
+-mkd11q* KD11-Q CPU. enable limited EIS, MTPS, and MFPS\n\
+-mkd11k* KD11-K CPU. enable full EIS, MTPS, MFPS, LDUB, MED,\n\
+ XFC, and MFPT\n\
+-mkd11z* KD11-Z CPU. enable full EIS, MTPS, MFPS, MFPT, SPL,\n\
+ and CSM\n\
+-mf11* F11 CPU. enable full EIS, MFPS, MTPS, and MFPT\n\
+-mj11* J11 CPU. enable full EIS, MTPS, MFPS, MFPT, SPL,\n\
+ CSM, TSTSET, and WRTLCK\n\
+-mt11* T11 CPU. enable limited EIS, MTPS, and MFPS\n\
+\n\
+PDP-11 machine model options:\n\
+\n\
+-m11/03 same as -mkd11f\n\
+-m11/04 same as -mkd11d\n\
+-m11/05 same as -mkd11b\n\
+-m11/10 same as -mkd11b\n\
+-m11/15 same as -mka11\n\
+-m11/20 same as -mka11\n\
+-m11/21 same as -mt11\n\
+-m11/23 same as -mf11\n\
+-m11/24 same as -mf11\n\
+-m11/34 same as -mkd11e\n\
+-m11/34a same as -mkd11e -mfpp\n\
+-m11/35 same as -mkd11a\n\
+-m11/40 same as -mkd11a\n\
+-m11/44 same as -mkd11z\n\
+-m11/45 same as -mkb11\n\
+-m11/50 same as -mkb11\n\
+-m11/53 same as -mj11\n\
+-m11/55 same as -mkb11\n\
+-m11/60 same as -mkd11k\n\
+-m11/70 same as -mkb11\n\
+-m11/73 same as -mj11\n\
+-m11/83 same as -mj11\n\
+-m11/84 same as -mj11\n\
+-m11/93 same as -mj11\n\
+-m11/94 same as -mj11\n\
+");
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
+{
+ return (size + 1) & ~1;
+}
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_frag->fr_address + fixP->fx_where + fixP->fx_size;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixp)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ reloc = xmalloc (sizeof (* reloc));
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* This is taken account for in md_apply_fix(). */
+ reloc->addend = -symbol_get_bfdsym (fixp->fx_addsy)->section->vma;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_16:
+ if (fixp->fx_pcrel)
+ code = BFD_RELOC_16_PCREL;
+ else
+ code = BFD_RELOC_16;
+ break;
+
+ case BFD_RELOC_16_PCREL:
+ code = BFD_RELOC_16_PCREL;
+ break;
+
+ default:
+ BAD_CASE (fixp->fx_r_type);
+ return NULL;
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Can not represent %s relocation in this object file format"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+
+ return reloc;
+}
+
+void
+pseudo_bss (int c ATTRIBUTE_UNUSED)
+{
+ int temp;
+
+ temp = get_absolute_expression ();
+ subseg_set (bss_section, temp);
+ demand_empty_rest_of_line ();
+}
+
+void
+pseudo_even (int c ATTRIBUTE_UNUSED)
+{
+ int alignment = 1; /* 2^1 */
+ frag_align (alignment, 0, 1);
+ record_alignment (now_seg, alignment);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return vax_md_atof (type, litP, sizeP);
+}
diff --git a/gas/config/tc-pdp11.h b/gas/config/tc-pdp11.h
new file mode 100644
index 0000000..af71667
--- /dev/null
+++ b/gas/config/tc-pdp11.h
@@ -0,0 +1,33 @@
+/* tc-pdp11.h -- Header file for tc-pdp11.c.
+ Copyright (C) 2001-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_PDP11 1
+
+#define TARGET_FORMAT "a.out-pdp11"
+#define TARGET_ARCH bfd_arch_pdp11
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define LEX_TILDE (LEX_BEGIN_NAME | LEX_NAME)
+
+#define md_operand(x)
+
+long md_chars_to_number (unsigned char *, int);
+
+/* end of tc-pdp11.h */
diff --git a/gas/config/tc-pj.c b/gas/config/tc-pj.c
new file mode 100644
index 0000000..dba4cbc
--- /dev/null
+++ b/gas/config/tc-pj.c
@@ -0,0 +1,495 @@
+/* tc-pj.c -- Assemble code for Pico Java
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* Contributed by Steve Chamberlain of Transmeta <sac@pobox.com>. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcode/pj.h"
+
+extern const pj_opc_info_t pj_opc_info[512];
+
+const char comment_chars[] = "!/";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "/!#";
+
+static int pending_reloc;
+static struct hash_control *opcode_hash_control;
+
+static void
+little (int ignore ATTRIBUTE_UNUSED)
+{
+ target_big_endian = 0;
+}
+
+static void
+big (int ignore ATTRIBUTE_UNUSED)
+{
+ target_big_endian = 1;
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"ml", little, 0},
+ {"mb", big, 0},
+ {0, 0, 0}
+};
+
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+const char EXP_CHARS[] = "eE";
+
+void
+md_operand (expressionS *op)
+{
+ if (strncmp (input_line_pointer, "%hi16", 5) == 0)
+ {
+ if (pending_reloc)
+ as_bad (_("confusing relocation expressions"));
+ pending_reloc = BFD_RELOC_PJ_CODE_HI16;
+ input_line_pointer += 5;
+ expression (op);
+ }
+
+ if (strncmp (input_line_pointer, "%lo16", 5) == 0)
+ {
+ if (pending_reloc)
+ as_bad (_("confusing relocation expressions"));
+ pending_reloc = BFD_RELOC_PJ_CODE_LO16;
+ input_line_pointer += 5;
+ expression (op);
+ }
+}
+
+/* Parse an expression and then restore the input line pointer. */
+
+static char *
+parse_exp_save_ilp (char *s, expressionS *op)
+{
+ char *save = input_line_pointer;
+
+ input_line_pointer = s;
+ expression (op);
+ s = input_line_pointer;
+ input_line_pointer = save;
+ return s;
+}
+
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+ reloc for a cons. We could use the definition there, except that
+ we want to handle magic pending reloc expressions specially. */
+
+void
+pj_cons_fix_new_pj (fragS *frag, int where, int nbytes, expressionS *exp,
+ bfd_reloc_code_real_type r ATTRIBUTE_UNUSED)
+{
+ static int rv[5][2] =
+ { { 0, 0 },
+ { BFD_RELOC_8, BFD_RELOC_8 },
+ { BFD_RELOC_PJ_CODE_DIR16, BFD_RELOC_16 },
+ { 0, 0 },
+ { BFD_RELOC_PJ_CODE_DIR32, BFD_RELOC_32 }};
+
+ fix_new_exp (frag, where, nbytes, exp, 0,
+ pending_reloc ? pending_reloc
+ : rv[nbytes][(now_seg->flags & SEC_CODE) ? 0 : 1]);
+
+ pending_reloc = 0;
+}
+
+/* Turn a reloc description character from the pj-opc.h table into
+ code which BFD can handle. */
+
+static int
+c_to_r (int x)
+{
+ switch (x)
+ {
+ case O_R8:
+ return BFD_RELOC_8_PCREL;
+ case O_U8:
+ case O_8:
+ return BFD_RELOC_8;
+ case O_R16:
+ return BFD_RELOC_PJ_CODE_REL16;
+ case O_U16:
+ case O_16:
+ return BFD_RELOC_PJ_CODE_DIR16;
+ case O_R32:
+ return BFD_RELOC_PJ_CODE_REL32;
+ case O_32:
+ return BFD_RELOC_PJ_CODE_DIR32;
+ }
+ abort ();
+ return 0;
+}
+
+/* Handler for the ipush fake opcode,
+ turns ipush <foo> into sipush lo16<foo>, sethi hi16<foo>. */
+
+static void
+ipush_code (pj_opc_info_t *opcode ATTRIBUTE_UNUSED, char *str)
+{
+ char *b = frag_more (6);
+ expressionS arg;
+
+ b[0] = 0x11;
+ b[3] = 0xed;
+ parse_exp_save_ilp (str + 1, &arg);
+ if (pending_reloc)
+ {
+ as_bad (_("can't have relocation for ipush"));
+ pending_reloc = 0;
+ }
+
+ fix_new_exp (frag_now, b - frag_now->fr_literal + 1, 2,
+ &arg, 0, BFD_RELOC_PJ_CODE_DIR16);
+ fix_new_exp (frag_now, b - frag_now->fr_literal + 4, 2,
+ &arg, 0, BFD_RELOC_PJ_CODE_HI16);
+}
+
+/* Insert names into the opcode table which are really mini macros,
+ not opcodes. The fakeness is indicated with an opcode of -1. */
+
+static void
+fake_opcode (const char *name,
+ void (*func) (struct pj_opc_info_t *, char *))
+{
+ pj_opc_info_t * fake = xmalloc (sizeof (pj_opc_info_t));
+
+ fake->opcode = -1;
+ fake->opcode_next = -1;
+ fake->u.func = func;
+ hash_insert (opcode_hash_control, name, (char *) fake);
+}
+
+/* Enter another entry into the opcode hash table so the same opcode
+ can have another name. */
+
+static void
+alias (const char *new_name, const char *old)
+{
+ hash_insert (opcode_hash_control, new_name,
+ (char *) hash_find (opcode_hash_control, old));
+}
+
+/* This function is called once, at assembler startup time. It sets
+ up the hash table with all the opcodes in it, and also initializes
+ some aliases for compatibility with other assemblers. */
+
+void
+md_begin (void)
+{
+ const pj_opc_info_t *opcode;
+ opcode_hash_control = hash_new ();
+
+ /* Insert names into hash table. */
+ for (opcode = pj_opc_info; opcode->u.name; opcode++)
+ hash_insert (opcode_hash_control, opcode->u.name, (char *) opcode);
+
+ /* Insert the only fake opcode. */
+ fake_opcode ("ipush", ipush_code);
+
+ /* Add some aliases for opcode names. */
+ alias ("ifeq_s", "ifeq");
+ alias ("ifne_s", "ifne");
+ alias ("if_icmpge_s", "if_icmpge");
+ alias ("if_icmpne_s", "if_icmpne");
+ alias ("if_icmpeq_s", "if_icmpeq");
+ alias ("if_icmpgt_s", "if_icmpgt");
+ alias ("goto_s", "goto");
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to
+ a machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *str)
+{
+ char *op_start;
+ char *op_end;
+
+ pj_opc_info_t *opcode;
+ char *output;
+ int idx = 0;
+ char pend;
+
+ int nlen = 0;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the op code end. */
+ op_start = str;
+ for (op_end = str;
+ *op_end && !is_end_of_line[*op_end & 0xff] && *op_end != ' ';
+ op_end++)
+ nlen++;
+
+ pend = *op_end;
+ *op_end = 0;
+
+ if (nlen == 0)
+ as_bad (_("can't find opcode "));
+
+ opcode = (pj_opc_info_t *) hash_find (opcode_hash_control, op_start);
+ *op_end = pend;
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode %s"), op_start);
+ return;
+ }
+
+ dwarf2_emit_insn (0);
+ if (opcode->opcode == -1)
+ {
+ /* It's a fake opcode. Dig out the args and pretend that was
+ what we were passed. */
+ (*opcode->u.func) (opcode, op_end);
+ }
+ else
+ {
+ int an;
+
+ output = frag_more (opcode->len);
+ output[idx++] = opcode->opcode;
+
+ if (opcode->opcode_next != -1)
+ output[idx++] = opcode->opcode_next;
+
+ for (an = 0; opcode->arg[an]; an++)
+ {
+ expressionS arg;
+
+ if (*op_end == ',' && an != 0)
+ op_end++;
+
+ if (*op_end == 0)
+ as_bad (_("expected expression"));
+
+ op_end = parse_exp_save_ilp (op_end, &arg);
+
+ fix_new_exp (frag_now,
+ output - frag_now->fr_literal + idx,
+ ASIZE (opcode->arg[an]),
+ &arg,
+ PCREL (opcode->arg[an]),
+ pending_reloc ? pending_reloc : c_to_r (opcode->arg[an]));
+
+ idx += ASIZE (opcode->arg[an]);
+ pending_reloc = 0;
+ }
+
+ while (ISSPACE (*op_end))
+ op_end++;
+
+ if (*op_end != 0)
+ as_warn (_("extra stuff on line ignored"));
+
+ }
+
+ if (pending_reloc)
+ as_bad (_("Something forgot to clean up\n"));
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+const char *md_shortopts = "";
+
+struct option md_longopts[] =
+{
+#define OPTION_LITTLE (OPTION_MD_BASE)
+#define OPTION_BIG (OPTION_LITTLE + 1)
+
+ {"little", no_argument, NULL, OPTION_LITTLE},
+ {"big", no_argument, NULL, OPTION_BIG},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_LITTLE:
+ little (0);
+ break;
+ case OPTION_BIG:
+ big (0);
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+PJ options:\n\
+-little generate little endian code\n\
+-big generate big endian code\n"));
+}
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ long val = *valP;
+ long max, min;
+
+ max = min = 0;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ case BFD_RELOC_PJ_CODE_REL16:
+ if (val < -0x8000 || val >= 0x7fff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[0] |= (val >> 8) & 0xff;
+ buf[1] = val & 0xff;
+ break;
+
+ case BFD_RELOC_PJ_CODE_HI16:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ fixP->fx_addnumber = val & 0xffff;
+ break;
+
+ case BFD_RELOC_PJ_CODE_DIR16:
+ case BFD_RELOC_PJ_CODE_LO16:
+ *buf++ = val >> 8;
+ *buf++ = val >> 0;
+
+ max = 0xffff;
+ min = -0xffff;
+ break;
+
+ case BFD_RELOC_8:
+ max = 0xff;
+ min = -0xff;
+ *buf++ = val;
+ break;
+
+ case BFD_RELOC_PJ_CODE_DIR32:
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ *buf++ = val >> 8;
+ *buf++ = val >> 0;
+ break;
+
+ case BFD_RELOC_32:
+ if (target_big_endian)
+ {
+ *buf++ = val >> 24;
+ *buf++ = val >> 16;
+ *buf++ = val >> 8;
+ *buf++ = val >> 0;
+ }
+ else
+ {
+ *buf++ = val >> 0;
+ *buf++ = val >> 8;
+ *buf++ = val >> 16;
+ *buf++ = val >> 24;
+ }
+ break;
+
+ case BFD_RELOC_16:
+ if (target_big_endian)
+ {
+ *buf++ = val >> 8;
+ *buf++ = val >> 0;
+ }
+ else
+ {
+ *buf++ = val >> 0;
+ *buf++ = val >> 8;
+ }
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (max != 0 && (val < min || val > max))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+/* Put number into target byte order. Always put values in an
+ executable section into big endian order. */
+
+void
+md_number_to_chars (char *ptr, valueT use, int nbytes)
+{
+ if (target_big_endian || now_seg->flags & SEC_CODE)
+ number_to_chars_bigendian (ptr, use, nbytes);
+ else
+ number_to_chars_littleendian (ptr, use, nbytes);
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type r_type;
+
+ rel = xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ r_type = fixp->fx_r_type;
+ rel->addend = fixp->fx_addnumber;
+ rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (r_type));
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (rel->howto != NULL);
+ }
+
+ return rel;
+}
diff --git a/gas/config/tc-pj.h b/gas/config/tc-pj.h
new file mode 100644
index 0000000..61cd11f
--- /dev/null
+++ b/gas/config/tc-pj.h
@@ -0,0 +1,61 @@
+/* This file is tc-pj.h
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+
+ Contributed by Steve Chamberlain of Transmeta, sac@pobox.com
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* Contributed by Steve Chamberlain, of Transmeta. sac@pobox.com. */
+
+#define WORKING_DOT_WORD
+#define IGNORE_NONSTANDARD_ESCAPES
+#define TARGET_ARCH bfd_arch_pj
+#define TARGET_FORMAT (target_big_endian ? "elf32-pj" : "elf32-pjl")
+#define LISTING_HEADER \
+ (target_big_endian \
+ ? "Pico Java GAS Big Endian" \
+ : "Pico Java GAS Little Endian")
+
+void pj_cons_fix_new_pj (struct frag *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+arelent *tc_gen_reloc (asection *, struct fix *);
+
+#define md_section_align(SEGMENT, SIZE) (SIZE)
+#define md_convert_frag(B, S, F) as_fatal (_("convert_frag\n"))
+#define md_estimate_size_before_relax(A, B) (as_fatal (_("estimate size\n")),0)
+#define md_undefined_symbol(NAME) 0
+
+/* PC relative operands are relative to the start of the opcode, and
+ the operand is always one byte into the opcode. */
+
+#define md_pcrel_from(FIX) \
+ ((FIX)->fx_where + (FIX)->fx_frag->fr_address - 1)
+
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \
+ pj_cons_fix_new_pj (FRAG, WHERE, NBYTES, EXP, RELOC)
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define tc_fix_adjustable(FIX) \
+ (! ((FIX)->fx_r_type == BFD_RELOC_VTABLE_INHERIT \
+ || (FIX)->fx_r_type == BFD_RELOC_VTABLE_ENTRY))
diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c
new file mode 100644
index 0000000..189a22b
--- /dev/null
+++ b/gas/config/tc-ppc.c
@@ -0,0 +1,7193 @@
+/* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "dw2gencfi.h"
+#include "opcode/ppc.h"
+
+#ifdef OBJ_ELF
+#include "elf/ppc.h"
+#include "elf/ppc64.h"
+#include "dwarf2dbg.h"
+#endif
+
+#ifdef TE_PE
+#include "coff/pe.h"
+#endif
+
+#ifdef OBJ_XCOFF
+#include "coff/xcoff.h"
+#include "libxcoff.h"
+#endif
+
+/* This is the assembler for the PowerPC or POWER (RS/6000) chips. */
+
+/* Tell the main code what the endianness is. */
+extern int target_big_endian;
+
+/* Whether or not, we've set target_big_endian. */
+static int set_target_endian = 0;
+
+/* Whether to use user friendly register names. */
+#ifndef TARGET_REG_NAMES_P
+#ifdef TE_PE
+#define TARGET_REG_NAMES_P TRUE
+#else
+#define TARGET_REG_NAMES_P FALSE
+#endif
+#endif
+
+/* Macros for calculating LO, HI, HA, HIGHER, HIGHERA, HIGHEST,
+ HIGHESTA. */
+
+/* #lo(value) denotes the least significant 16 bits of the indicated. */
+#define PPC_LO(v) ((v) & 0xffff)
+
+/* #hi(value) denotes bits 16 through 31 of the indicated value. */
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+
+/* #ha(value) denotes the high adjusted value: bits 16 through 31 of
+ the indicated value, compensating for #lo() being treated as a
+ signed number. */
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
+/* #higher(value) denotes bits 32 through 47 of the indicated value. */
+#define PPC_HIGHER(v) (((v) >> 16 >> 16) & 0xffff)
+
+/* #highera(value) denotes bits 32 through 47 of the indicated value,
+ compensating for #lo() being treated as a signed number. */
+#define PPC_HIGHERA(v) PPC_HIGHER ((v) + 0x8000)
+
+/* #highest(value) denotes bits 48 through 63 of the indicated value. */
+#define PPC_HIGHEST(v) (((v) >> 24 >> 24) & 0xffff)
+
+/* #highesta(value) denotes bits 48 through 63 of the indicated value,
+ compensating for #lo being treated as a signed number. */
+#define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000)
+
+#define SEX16(val) (((val) ^ 0x8000) - 0x8000)
+
+/* For the time being on ppc64, don't report overflow on @h and @ha
+ applied to constants. */
+#define REPORT_OVERFLOW_HI 0
+
+static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
+
+static void ppc_macro (char *, const struct powerpc_macro *);
+static void ppc_byte (int);
+
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+static void ppc_tc (int);
+static void ppc_machine (int);
+#endif
+
+#ifdef OBJ_XCOFF
+static void ppc_comm (int);
+static void ppc_bb (int);
+static void ppc_bc (int);
+static void ppc_bf (int);
+static void ppc_biei (int);
+static void ppc_bs (int);
+static void ppc_eb (int);
+static void ppc_ec (int);
+static void ppc_ef (int);
+static void ppc_es (int);
+static void ppc_csect (int);
+static void ppc_dwsect (int);
+static void ppc_change_csect (symbolS *, offsetT);
+static void ppc_function (int);
+static void ppc_extern (int);
+static void ppc_lglobl (int);
+static void ppc_ref (int);
+static void ppc_section (int);
+static void ppc_named_section (int);
+static void ppc_stabx (int);
+static void ppc_rename (int);
+static void ppc_toc (int);
+static void ppc_xcoff_cons (int);
+static void ppc_vbyte (int);
+#endif
+
+#ifdef OBJ_ELF
+static void ppc_elf_rdata (int);
+static void ppc_elf_lcomm (int);
+static void ppc_elf_localentry (int);
+static void ppc_elf_abiversion (int);
+#endif
+
+#ifdef TE_PE
+static void ppc_previous (int);
+static void ppc_pdata (int);
+static void ppc_ydata (int);
+static void ppc_reldata (int);
+static void ppc_rdata (int);
+static void ppc_ualong (int);
+static void ppc_znop (int);
+static void ppc_pe_comm (int);
+static void ppc_pe_section (int);
+static void ppc_pe_function (int);
+static void ppc_pe_tocd (int);
+#endif
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+#ifdef OBJ_ELF
+/* This string holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. The macro
+ tc_comment_chars points to this. We use this, rather than the
+ usual comment_chars, so that we can switch for Solaris conventions. */
+static const char ppc_solaris_comment_chars[] = "#!";
+static const char ppc_eabi_comment_chars[] = "#";
+
+#ifdef TARGET_SOLARIS_COMMENT
+const char *ppc_comment_chars = ppc_solaris_comment_chars;
+#else
+const char *ppc_comment_chars = ppc_eabi_comment_chars;
+#endif
+#else
+const char comment_chars[] = "#";
+#endif
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+/* Anything that can start an operand needs to be mentioned here,
+ to stop the input scrubber eating whitespace. */
+const char ppc_symbol_chars[] = "%[";
+
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */
+int ppc_cie_data_alignment;
+
+/* The dwarf2 minimum instruction length. */
+int ppc_dwarf2_line_min_insn_length;
+
+/* More than this number of nops in an alignment op gets a branch
+ instead. */
+unsigned long nop_limit = 4;
+
+/* The type of processor we are assembling for. This is one or more
+ of the PPC_OPCODE flags defined in opcode/ppc.h. */
+ppc_cpu_t ppc_cpu = 0;
+ppc_cpu_t sticky = 0;
+
+/* Value for ELF e_flags EF_PPC64_ABI. */
+unsigned int ppc_abiversion = 0;
+
+/* Flags set on encountering toc relocs. */
+enum {
+ has_large_toc_reloc = 1,
+ has_small_toc_reloc = 2
+} toc_reloc_types;
+
+/* Warn on emitting data to code sections. */
+int warn_476;
+unsigned long last_insn;
+segT last_seg;
+subsegT last_subseg;
+
+/* The target specific pseudo-ops which we support. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Pseudo-ops which must be overridden. */
+ { "byte", ppc_byte, 0 },
+
+#ifdef OBJ_XCOFF
+ /* Pseudo-ops specific to the RS/6000 XCOFF format. Some of these
+ legitimately belong in the obj-*.c file. However, XCOFF is based
+ on COFF, and is only implemented for the RS/6000. We just use
+ obj-coff.c, and add what we need here. */
+ { "comm", ppc_comm, 0 },
+ { "lcomm", ppc_comm, 1 },
+ { "bb", ppc_bb, 0 },
+ { "bc", ppc_bc, 0 },
+ { "bf", ppc_bf, 0 },
+ { "bi", ppc_biei, 0 },
+ { "bs", ppc_bs, 0 },
+ { "csect", ppc_csect, 0 },
+ { "dwsect", ppc_dwsect, 0 },
+ { "data", ppc_section, 'd' },
+ { "eb", ppc_eb, 0 },
+ { "ec", ppc_ec, 0 },
+ { "ef", ppc_ef, 0 },
+ { "ei", ppc_biei, 1 },
+ { "es", ppc_es, 0 },
+ { "extern", ppc_extern, 0 },
+ { "function", ppc_function, 0 },
+ { "lglobl", ppc_lglobl, 0 },
+ { "ref", ppc_ref, 0 },
+ { "rename", ppc_rename, 0 },
+ { "section", ppc_named_section, 0 },
+ { "stabx", ppc_stabx, 0 },
+ { "text", ppc_section, 't' },
+ { "toc", ppc_toc, 0 },
+ { "long", ppc_xcoff_cons, 2 },
+ { "llong", ppc_xcoff_cons, 3 },
+ { "word", ppc_xcoff_cons, 1 },
+ { "short", ppc_xcoff_cons, 1 },
+ { "vbyte", ppc_vbyte, 0 },
+#endif
+
+#ifdef OBJ_ELF
+ { "llong", cons, 8 },
+ { "rdata", ppc_elf_rdata, 0 },
+ { "rodata", ppc_elf_rdata, 0 },
+ { "lcomm", ppc_elf_lcomm, 0 },
+ { "localentry", ppc_elf_localentry, 0 },
+ { "abiversion", ppc_elf_abiversion, 0 },
+#endif
+
+#ifdef TE_PE
+ /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format. */
+ { "previous", ppc_previous, 0 },
+ { "pdata", ppc_pdata, 0 },
+ { "ydata", ppc_ydata, 0 },
+ { "reldata", ppc_reldata, 0 },
+ { "rdata", ppc_rdata, 0 },
+ { "ualong", ppc_ualong, 0 },
+ { "znop", ppc_znop, 0 },
+ { "comm", ppc_pe_comm, 0 },
+ { "lcomm", ppc_pe_comm, 1 },
+ { "section", ppc_pe_section, 0 },
+ { "function", ppc_pe_function,0 },
+ { "tocd", ppc_pe_tocd, 0 },
+#endif
+
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+ { "tc", ppc_tc, 0 },
+ { "machine", ppc_machine, 0 },
+#endif
+
+ { NULL, NULL, 0 }
+};
+
+
+/* Predefined register names if -mregnames (or default for Windows NT).
+ In general, there are lots of them, in an attempt to be compatible
+ with a number of other Windows NT assemblers. */
+
+/* Structure to hold information about predefined registers. */
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* List of registers that are pre-defined:
+
+ Each general register has predefined names of the form:
+ 1. r<reg_num> which has the value <reg_num>.
+ 2. r.<reg_num> which has the value <reg_num>.
+
+ Each floating point register has predefined names of the form:
+ 1. f<reg_num> which has the value <reg_num>.
+ 2. f.<reg_num> which has the value <reg_num>.
+
+ Each vector unit register has predefined names of the form:
+ 1. v<reg_num> which has the value <reg_num>.
+ 2. v.<reg_num> which has the value <reg_num>.
+
+ Each condition register has predefined names of the form:
+ 1. cr<reg_num> which has the value <reg_num>.
+ 2. cr.<reg_num> which has the value <reg_num>.
+
+ There are individual registers as well:
+ sp or r.sp has the value 1
+ rtoc or r.toc has the value 2
+ fpscr has the value 0
+ xer has the value 1
+ lr has the value 8
+ ctr has the value 9
+ pmr has the value 0
+ dar has the value 19
+ dsisr has the value 18
+ dec has the value 22
+ sdr1 has the value 25
+ srr0 has the value 26
+ srr1 has the value 27
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ { "cr.0", 0 }, /* Condition Registers */
+ { "cr.1", 1 },
+ { "cr.2", 2 },
+ { "cr.3", 3 },
+ { "cr.4", 4 },
+ { "cr.5", 5 },
+ { "cr.6", 6 },
+ { "cr.7", 7 },
+
+ { "cr0", 0 },
+ { "cr1", 1 },
+ { "cr2", 2 },
+ { "cr3", 3 },
+ { "cr4", 4 },
+ { "cr5", 5 },
+ { "cr6", 6 },
+ { "cr7", 7 },
+
+ { "ctr", 9 },
+
+ { "dar", 19 }, /* Data Access Register */
+ { "dec", 22 }, /* Decrementer */
+ { "dsisr", 18 }, /* Data Storage Interrupt Status Register */
+
+ { "f.0", 0 }, /* Floating point registers */
+ { "f.1", 1 },
+ { "f.10", 10 },
+ { "f.11", 11 },
+ { "f.12", 12 },
+ { "f.13", 13 },
+ { "f.14", 14 },
+ { "f.15", 15 },
+ { "f.16", 16 },
+ { "f.17", 17 },
+ { "f.18", 18 },
+ { "f.19", 19 },
+ { "f.2", 2 },
+ { "f.20", 20 },
+ { "f.21", 21 },
+ { "f.22", 22 },
+ { "f.23", 23 },
+ { "f.24", 24 },
+ { "f.25", 25 },
+ { "f.26", 26 },
+ { "f.27", 27 },
+ { "f.28", 28 },
+ { "f.29", 29 },
+ { "f.3", 3 },
+ { "f.30", 30 },
+ { "f.31", 31 },
+
+ { "f.32", 32 }, /* Extended floating point scalar registers (ISA 2.06). */
+ { "f.33", 33 },
+ { "f.34", 34 },
+ { "f.35", 35 },
+ { "f.36", 36 },
+ { "f.37", 37 },
+ { "f.38", 38 },
+ { "f.39", 39 },
+ { "f.4", 4 },
+ { "f.40", 40 },
+ { "f.41", 41 },
+ { "f.42", 42 },
+ { "f.43", 43 },
+ { "f.44", 44 },
+ { "f.45", 45 },
+ { "f.46", 46 },
+ { "f.47", 47 },
+ { "f.48", 48 },
+ { "f.49", 49 },
+ { "f.5", 5 },
+ { "f.50", 50 },
+ { "f.51", 51 },
+ { "f.52", 52 },
+ { "f.53", 53 },
+ { "f.54", 54 },
+ { "f.55", 55 },
+ { "f.56", 56 },
+ { "f.57", 57 },
+ { "f.58", 58 },
+ { "f.59", 59 },
+ { "f.6", 6 },
+ { "f.60", 60 },
+ { "f.61", 61 },
+ { "f.62", 62 },
+ { "f.63", 63 },
+ { "f.7", 7 },
+ { "f.8", 8 },
+ { "f.9", 9 },
+
+ { "f0", 0 },
+ { "f1", 1 },
+ { "f10", 10 },
+ { "f11", 11 },
+ { "f12", 12 },
+ { "f13", 13 },
+ { "f14", 14 },
+ { "f15", 15 },
+ { "f16", 16 },
+ { "f17", 17 },
+ { "f18", 18 },
+ { "f19", 19 },
+ { "f2", 2 },
+ { "f20", 20 },
+ { "f21", 21 },
+ { "f22", 22 },
+ { "f23", 23 },
+ { "f24", 24 },
+ { "f25", 25 },
+ { "f26", 26 },
+ { "f27", 27 },
+ { "f28", 28 },
+ { "f29", 29 },
+ { "f3", 3 },
+ { "f30", 30 },
+ { "f31", 31 },
+
+ { "f32", 32 }, /* Extended floating point scalar registers (ISA 2.06). */
+ { "f33", 33 },
+ { "f34", 34 },
+ { "f35", 35 },
+ { "f36", 36 },
+ { "f37", 37 },
+ { "f38", 38 },
+ { "f39", 39 },
+ { "f4", 4 },
+ { "f40", 40 },
+ { "f41", 41 },
+ { "f42", 42 },
+ { "f43", 43 },
+ { "f44", 44 },
+ { "f45", 45 },
+ { "f46", 46 },
+ { "f47", 47 },
+ { "f48", 48 },
+ { "f49", 49 },
+ { "f5", 5 },
+ { "f50", 50 },
+ { "f51", 51 },
+ { "f52", 52 },
+ { "f53", 53 },
+ { "f54", 54 },
+ { "f55", 55 },
+ { "f56", 56 },
+ { "f57", 57 },
+ { "f58", 58 },
+ { "f59", 59 },
+ { "f6", 6 },
+ { "f60", 60 },
+ { "f61", 61 },
+ { "f62", 62 },
+ { "f63", 63 },
+ { "f7", 7 },
+ { "f8", 8 },
+ { "f9", 9 },
+
+ { "fpscr", 0 },
+
+ /* Quantization registers used with pair single instructions. */
+ { "gqr.0", 0 },
+ { "gqr.1", 1 },
+ { "gqr.2", 2 },
+ { "gqr.3", 3 },
+ { "gqr.4", 4 },
+ { "gqr.5", 5 },
+ { "gqr.6", 6 },
+ { "gqr.7", 7 },
+ { "gqr0", 0 },
+ { "gqr1", 1 },
+ { "gqr2", 2 },
+ { "gqr3", 3 },
+ { "gqr4", 4 },
+ { "gqr5", 5 },
+ { "gqr6", 6 },
+ { "gqr7", 7 },
+
+ { "lr", 8 }, /* Link Register */
+
+ { "pmr", 0 },
+
+ { "r.0", 0 }, /* General Purpose Registers */
+ { "r.1", 1 },
+ { "r.10", 10 },
+ { "r.11", 11 },
+ { "r.12", 12 },
+ { "r.13", 13 },
+ { "r.14", 14 },
+ { "r.15", 15 },
+ { "r.16", 16 },
+ { "r.17", 17 },
+ { "r.18", 18 },
+ { "r.19", 19 },
+ { "r.2", 2 },
+ { "r.20", 20 },
+ { "r.21", 21 },
+ { "r.22", 22 },
+ { "r.23", 23 },
+ { "r.24", 24 },
+ { "r.25", 25 },
+ { "r.26", 26 },
+ { "r.27", 27 },
+ { "r.28", 28 },
+ { "r.29", 29 },
+ { "r.3", 3 },
+ { "r.30", 30 },
+ { "r.31", 31 },
+ { "r.4", 4 },
+ { "r.5", 5 },
+ { "r.6", 6 },
+ { "r.7", 7 },
+ { "r.8", 8 },
+ { "r.9", 9 },
+
+ { "r.sp", 1 }, /* Stack Pointer */
+
+ { "r.toc", 2 }, /* Pointer to the table of contents */
+
+ { "r0", 0 }, /* More general purpose registers */
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r16", 16 },
+ { "r17", 17 },
+ { "r18", 18 },
+ { "r19", 19 },
+ { "r2", 2 },
+ { "r20", 20 },
+ { "r21", 21 },
+ { "r22", 22 },
+ { "r23", 23 },
+ { "r24", 24 },
+ { "r25", 25 },
+ { "r26", 26 },
+ { "r27", 27 },
+ { "r28", 28 },
+ { "r29", 29 },
+ { "r3", 3 },
+ { "r30", 30 },
+ { "r31", 31 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+
+ { "rtoc", 2 }, /* Table of contents */
+
+ { "sdr1", 25 }, /* Storage Description Register 1 */
+
+ { "sp", 1 },
+
+ { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */
+ { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */
+
+ { "v.0", 0 }, /* Vector (Altivec/VMX) registers */
+ { "v.1", 1 },
+ { "v.10", 10 },
+ { "v.11", 11 },
+ { "v.12", 12 },
+ { "v.13", 13 },
+ { "v.14", 14 },
+ { "v.15", 15 },
+ { "v.16", 16 },
+ { "v.17", 17 },
+ { "v.18", 18 },
+ { "v.19", 19 },
+ { "v.2", 2 },
+ { "v.20", 20 },
+ { "v.21", 21 },
+ { "v.22", 22 },
+ { "v.23", 23 },
+ { "v.24", 24 },
+ { "v.25", 25 },
+ { "v.26", 26 },
+ { "v.27", 27 },
+ { "v.28", 28 },
+ { "v.29", 29 },
+ { "v.3", 3 },
+ { "v.30", 30 },
+ { "v.31", 31 },
+ { "v.4", 4 },
+ { "v.5", 5 },
+ { "v.6", 6 },
+ { "v.7", 7 },
+ { "v.8", 8 },
+ { "v.9", 9 },
+
+ { "v0", 0 },
+ { "v1", 1 },
+ { "v10", 10 },
+ { "v11", 11 },
+ { "v12", 12 },
+ { "v13", 13 },
+ { "v14", 14 },
+ { "v15", 15 },
+ { "v16", 16 },
+ { "v17", 17 },
+ { "v18", 18 },
+ { "v19", 19 },
+ { "v2", 2 },
+ { "v20", 20 },
+ { "v21", 21 },
+ { "v22", 22 },
+ { "v23", 23 },
+ { "v24", 24 },
+ { "v25", 25 },
+ { "v26", 26 },
+ { "v27", 27 },
+ { "v28", 28 },
+ { "v29", 29 },
+ { "v3", 3 },
+ { "v30", 30 },
+ { "v31", 31 },
+ { "v4", 4 },
+ { "v5", 5 },
+ { "v6", 6 },
+ { "v7", 7 },
+ { "v8", 8 },
+ { "v9", 9 },
+
+ { "vs.0", 0 }, /* Vector Scalar (VSX) registers (ISA 2.06). */
+ { "vs.1", 1 },
+ { "vs.10", 10 },
+ { "vs.11", 11 },
+ { "vs.12", 12 },
+ { "vs.13", 13 },
+ { "vs.14", 14 },
+ { "vs.15", 15 },
+ { "vs.16", 16 },
+ { "vs.17", 17 },
+ { "vs.18", 18 },
+ { "vs.19", 19 },
+ { "vs.2", 2 },
+ { "vs.20", 20 },
+ { "vs.21", 21 },
+ { "vs.22", 22 },
+ { "vs.23", 23 },
+ { "vs.24", 24 },
+ { "vs.25", 25 },
+ { "vs.26", 26 },
+ { "vs.27", 27 },
+ { "vs.28", 28 },
+ { "vs.29", 29 },
+ { "vs.3", 3 },
+ { "vs.30", 30 },
+ { "vs.31", 31 },
+ { "vs.32", 32 },
+ { "vs.33", 33 },
+ { "vs.34", 34 },
+ { "vs.35", 35 },
+ { "vs.36", 36 },
+ { "vs.37", 37 },
+ { "vs.38", 38 },
+ { "vs.39", 39 },
+ { "vs.4", 4 },
+ { "vs.40", 40 },
+ { "vs.41", 41 },
+ { "vs.42", 42 },
+ { "vs.43", 43 },
+ { "vs.44", 44 },
+ { "vs.45", 45 },
+ { "vs.46", 46 },
+ { "vs.47", 47 },
+ { "vs.48", 48 },
+ { "vs.49", 49 },
+ { "vs.5", 5 },
+ { "vs.50", 50 },
+ { "vs.51", 51 },
+ { "vs.52", 52 },
+ { "vs.53", 53 },
+ { "vs.54", 54 },
+ { "vs.55", 55 },
+ { "vs.56", 56 },
+ { "vs.57", 57 },
+ { "vs.58", 58 },
+ { "vs.59", 59 },
+ { "vs.6", 6 },
+ { "vs.60", 60 },
+ { "vs.61", 61 },
+ { "vs.62", 62 },
+ { "vs.63", 63 },
+ { "vs.7", 7 },
+ { "vs.8", 8 },
+ { "vs.9", 9 },
+
+ { "vs0", 0 },
+ { "vs1", 1 },
+ { "vs10", 10 },
+ { "vs11", 11 },
+ { "vs12", 12 },
+ { "vs13", 13 },
+ { "vs14", 14 },
+ { "vs15", 15 },
+ { "vs16", 16 },
+ { "vs17", 17 },
+ { "vs18", 18 },
+ { "vs19", 19 },
+ { "vs2", 2 },
+ { "vs20", 20 },
+ { "vs21", 21 },
+ { "vs22", 22 },
+ { "vs23", 23 },
+ { "vs24", 24 },
+ { "vs25", 25 },
+ { "vs26", 26 },
+ { "vs27", 27 },
+ { "vs28", 28 },
+ { "vs29", 29 },
+ { "vs3", 3 },
+ { "vs30", 30 },
+ { "vs31", 31 },
+ { "vs32", 32 },
+ { "vs33", 33 },
+ { "vs34", 34 },
+ { "vs35", 35 },
+ { "vs36", 36 },
+ { "vs37", 37 },
+ { "vs38", 38 },
+ { "vs39", 39 },
+ { "vs4", 4 },
+ { "vs40", 40 },
+ { "vs41", 41 },
+ { "vs42", 42 },
+ { "vs43", 43 },
+ { "vs44", 44 },
+ { "vs45", 45 },
+ { "vs46", 46 },
+ { "vs47", 47 },
+ { "vs48", 48 },
+ { "vs49", 49 },
+ { "vs5", 5 },
+ { "vs50", 50 },
+ { "vs51", 51 },
+ { "vs52", 52 },
+ { "vs53", 53 },
+ { "vs54", 54 },
+ { "vs55", 55 },
+ { "vs56", 56 },
+ { "vs57", 57 },
+ { "vs58", 58 },
+ { "vs59", 59 },
+ { "vs6", 6 },
+ { "vs60", 60 },
+ { "vs61", 61 },
+ { "vs62", 62 },
+ { "vs63", 63 },
+ { "vs7", 7 },
+ { "vs8", 8 },
+ { "vs9", 9 },
+
+ { "xer", 1 },
+
+};
+
+#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
+
+/* Given NAME, find the register number associated with that name, return
+ the integer value associated with the given name or -1 on failure. */
+
+static int
+reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+/*
+ * Summary of register_name.
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in its
+ * original state.
+ */
+
+static bfd_boolean
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+ if (name[0] == '%' && ISALPHA (name[1]))
+ name = ++input_line_pointer;
+
+ else if (!reg_names_p || !ISALPHA (name[0]))
+ return FALSE;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+/* This function is called for each symbol seen in an expression. It
+ handles the special parsing which PowerPC assemblers are supposed
+ to use for condition codes. */
+
+/* Whether to do the special parsing. */
+static bfd_boolean cr_operand;
+
+/* Names to recognize in a condition code. This table is sorted. */
+static const struct pd_reg cr_names[] =
+{
+ { "cr0", 0 },
+ { "cr1", 1 },
+ { "cr2", 2 },
+ { "cr3", 3 },
+ { "cr4", 4 },
+ { "cr5", 5 },
+ { "cr6", 6 },
+ { "cr7", 7 },
+ { "eq", 2 },
+ { "gt", 1 },
+ { "lt", 0 },
+ { "so", 3 },
+ { "un", 3 }
+};
+
+/* Parsing function. This returns non-zero if it recognized an
+ expression. */
+
+int
+ppc_parse_name (const char *name, expressionS *exp)
+{
+ int val;
+
+ if (! cr_operand)
+ return 0;
+
+ if (*name == '%')
+ ++name;
+ val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
+ name);
+ if (val < 0)
+ return 0;
+
+ exp->X_op = O_constant;
+ exp->X_add_number = val;
+
+ return 1;
+}
+
+/* Local variables. */
+
+/* Whether to target xcoff64/elf64. */
+static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
+
+/* Opcode hash table. */
+static struct hash_control *ppc_hash;
+
+/* Macro hash table. */
+static struct hash_control *ppc_macro_hash;
+
+#ifdef OBJ_ELF
+/* What type of shared library support to use. */
+static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE;
+
+/* Flags to set in the elf header. */
+static flagword ppc_flags = 0;
+
+/* Whether this is Solaris or not. */
+#ifdef TARGET_SOLARIS_COMMENT
+#define SOLARIS_P TRUE
+#else
+#define SOLARIS_P FALSE
+#endif
+
+static bfd_boolean msolaris = SOLARIS_P;
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* The RS/6000 assembler uses the .csect pseudo-op to generate code
+ using a bunch of different sections. These assembler sections,
+ however, are all encompassed within the .text or .data sections of
+ the final output file. We handle this by using different
+ subsegments within these main segments. */
+
+/* Next subsegment to allocate within the .text segment. */
+static subsegT ppc_text_subsegment = 2;
+
+/* Linked list of csects in the text section. */
+static symbolS *ppc_text_csects;
+
+/* Next subsegment to allocate within the .data segment. */
+static subsegT ppc_data_subsegment = 2;
+
+/* Linked list of csects in the data section. */
+static symbolS *ppc_data_csects;
+
+/* The current csect. */
+static symbolS *ppc_current_csect;
+
+/* The RS/6000 assembler uses a TOC which holds addresses of functions
+ and variables. Symbols are put in the TOC with the .tc pseudo-op.
+ A special relocation is used when accessing TOC entries. We handle
+ the TOC as a subsegment within the .data segment. We set it up if
+ we see a .toc pseudo-op, and save the csect symbol here. */
+static symbolS *ppc_toc_csect;
+
+/* The first frag in the TOC subsegment. */
+static fragS *ppc_toc_frag;
+
+/* The first frag in the first subsegment after the TOC in the .data
+ segment. NULL if there are no subsegments after the TOC. */
+static fragS *ppc_after_toc_frag;
+
+/* The current static block. */
+static symbolS *ppc_current_block;
+
+/* The COFF debugging section; set by md_begin. This is not the
+ .debug section, but is instead the secret BFD section which will
+ cause BFD to set the section number of a symbol to N_DEBUG. */
+static asection *ppc_coff_debug_section;
+
+/* Structure to set the length field of the dwarf sections. */
+struct dw_subsection {
+ /* Subsections are simply linked. */
+ struct dw_subsection *link;
+
+ /* The subsection number. */
+ subsegT subseg;
+
+ /* Expression to compute the length of the section. */
+ expressionS end_exp;
+};
+
+static struct dw_section {
+ /* Corresponding section. */
+ segT sect;
+
+ /* Simply linked list of subsections with a label. */
+ struct dw_subsection *list_subseg;
+
+ /* The anonymous subsection. */
+ struct dw_subsection *anon_subseg;
+} dw_sections[XCOFF_DWSECT_NBR_NAMES];
+#endif /* OBJ_XCOFF */
+
+#ifdef TE_PE
+
+/* Various sections that we need for PE coff support. */
+static segT ydata_section;
+static segT pdata_section;
+static segT reldata_section;
+static segT rdata_section;
+static segT tocdata_section;
+
+/* The current section and the previous section. See ppc_previous. */
+static segT ppc_previous_section;
+static segT ppc_current_section;
+
+#endif /* TE_PE */
+
+#ifdef OBJ_ELF
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
+#define PPC_APUINFO_ISEL 0x40
+#define PPC_APUINFO_PMR 0x41
+#define PPC_APUINFO_RFMCI 0x42
+#define PPC_APUINFO_CACHELCK 0x43
+#define PPC_APUINFO_SPE 0x100
+#define PPC_APUINFO_EFS 0x101
+#define PPC_APUINFO_BRLOCK 0x102
+#define PPC_APUINFO_VLE 0x104
+
+/*
+ * We keep a list of APUinfo
+ */
+unsigned long *ppc_apuinfo_list;
+unsigned int ppc_apuinfo_num;
+unsigned int ppc_apuinfo_num_alloc;
+#endif /* OBJ_ELF */
+
+#ifdef OBJ_ELF
+const char *const md_shortopts = "b:l:usm:K:VQ:";
+#else
+const char *const md_shortopts = "um:";
+#endif
+#define OPTION_NOPS (OPTION_MD_BASE + 0)
+const struct option md_longopts[] = {
+ {"nops", required_argument, NULL, OPTION_NOPS},
+ {"ppc476-workaround", no_argument, &warn_476, 1},
+ {"no-ppc476-workaround", no_argument, &warn_476, 0},
+ {NULL, no_argument, NULL, 0}
+};
+const size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ ppc_cpu_t new_cpu;
+
+ switch (c)
+ {
+ case 'u':
+ /* -u means that any undefined symbols should be treated as
+ external, which is the default for gas anyhow. */
+ break;
+
+#ifdef OBJ_ELF
+ case 'l':
+ /* Solaris as takes -le (presumably for little endian). For completeness
+ sake, recognize -be also. */
+ if (strcmp (arg, "e") == 0)
+ {
+ target_big_endian = 0;
+ set_target_endian = 1;
+ if (ppc_cpu & PPC_OPCODE_VLE)
+ as_bad (_("the use of -mvle requires big endian."));
+ }
+ else
+ return 0;
+
+ break;
+
+ case 'b':
+ if (strcmp (arg, "e") == 0)
+ {
+ target_big_endian = 1;
+ set_target_endian = 1;
+ }
+ else
+ return 0;
+
+ break;
+
+ case 'K':
+ /* Recognize -K PIC. */
+ if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0)
+ {
+ shlib = SHLIB_PIC;
+ ppc_flags |= EF_PPC_RELOCATABLE_LIB;
+ }
+ else
+ return 0;
+
+ break;
+#endif
+
+ /* a64 and a32 determine whether to use XCOFF64 or XCOFF32. */
+ case 'a':
+ if (strcmp (arg, "64") == 0)
+ {
+#ifdef BFD64
+ ppc_obj64 = 1;
+ if (ppc_cpu & PPC_OPCODE_VLE)
+ as_bad (_("the use of -mvle requires -a32."));
+#else
+ as_fatal (_("%s unsupported"), "-a64");
+#endif
+ }
+ else if (strcmp (arg, "32") == 0)
+ ppc_obj64 = 0;
+ else
+ return 0;
+ break;
+
+ case 'm':
+ new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, arg);
+ if (new_cpu != 0)
+ {
+ ppc_cpu = new_cpu;
+ if (strcmp (arg, "vle") == 0)
+ {
+ if (set_target_endian && target_big_endian == 0)
+ as_bad (_("the use of -mvle requires big endian."));
+ if (ppc_obj64)
+ as_bad (_("the use of -mvle requires -a32."));
+ }
+ }
+
+ else if (strcmp (arg, "regnames") == 0)
+ reg_names_p = TRUE;
+
+ else if (strcmp (arg, "no-regnames") == 0)
+ reg_names_p = FALSE;
+
+#ifdef OBJ_ELF
+ /* -mrelocatable/-mrelocatable-lib -- warn about initializations
+ that require relocation. */
+ else if (strcmp (arg, "relocatable") == 0)
+ {
+ shlib = SHLIB_MRELOCATABLE;
+ ppc_flags |= EF_PPC_RELOCATABLE;
+ }
+
+ else if (strcmp (arg, "relocatable-lib") == 0)
+ {
+ shlib = SHLIB_MRELOCATABLE;
+ ppc_flags |= EF_PPC_RELOCATABLE_LIB;
+ }
+
+ /* -memb, set embedded bit. */
+ else if (strcmp (arg, "emb") == 0)
+ ppc_flags |= EF_PPC_EMB;
+
+ /* -mlittle/-mbig set the endianness. */
+ else if (strcmp (arg, "little") == 0
+ || strcmp (arg, "little-endian") == 0)
+ {
+ target_big_endian = 0;
+ set_target_endian = 1;
+ if (ppc_cpu & PPC_OPCODE_VLE)
+ as_bad (_("the use of -mvle requires big endian."));
+ }
+
+ else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0)
+ {
+ target_big_endian = 1;
+ set_target_endian = 1;
+ }
+
+ else if (strcmp (arg, "solaris") == 0)
+ {
+ msolaris = TRUE;
+ ppc_comment_chars = ppc_solaris_comment_chars;
+ }
+
+ else if (strcmp (arg, "no-solaris") == 0)
+ {
+ msolaris = FALSE;
+ ppc_comment_chars = ppc_eabi_comment_chars;
+ }
+#endif
+ else
+ {
+ as_bad (_("invalid switch -m%s"), arg);
+ return 0;
+ }
+ break;
+
+#ifdef OBJ_ELF
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ /* Solaris takes -s to specify that .stabs go in a .stabs section,
+ rather than .stabs.excl, which is ignored by the linker.
+ FIXME: Not implemented. */
+ case 's':
+ if (arg)
+ return 0;
+
+ break;
+#endif
+
+ case OPTION_NOPS:
+ {
+ char *end;
+ nop_limit = strtoul (optarg, &end, 0);
+ if (*end)
+ as_bad (_("--nops needs a numeric argument"));
+ }
+ break;
+
+ case 0:
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+PowerPC options:\n\
+-a32 generate ELF32/XCOFF32\n\
+-a64 generate ELF64/XCOFF64\n\
+-u ignored\n\
+-mpwrx, -mpwr2 generate code for POWER/2 (RIOS2)\n\
+-mpwr generate code for POWER (RIOS1)\n\
+-m601 generate code for PowerPC 601\n\
+-mppc, -mppc32, -m603, -m604\n\
+ generate code for PowerPC 603/604\n\
+-m403 generate code for PowerPC 403\n\
+-m405 generate code for PowerPC 405\n\
+-m440 generate code for PowerPC 440\n\
+-m464 generate code for PowerPC 464\n\
+-m476 generate code for PowerPC 476\n\
+-m7400, -m7410, -m7450, -m7455\n\
+ generate code for PowerPC 7400/7410/7450/7455\n\
+-m750cl generate code for PowerPC 750cl\n"));
+ fprintf (stream, _("\
+-mppc64, -m620 generate code for PowerPC 620/625/630\n\
+-mppc64bridge generate code for PowerPC 64, including bridge insns\n\
+-mbooke generate code for 32-bit PowerPC BookE\n\
+-ma2 generate code for A2 architecture\n\
+-mpower4, -mpwr4 generate code for Power4 architecture\n\
+-mpower5, -mpwr5, -mpwr5x\n\
+ generate code for Power5 architecture\n\
+-mpower6, -mpwr6 generate code for Power6 architecture\n\
+-mpower7, -mpwr7 generate code for Power7 architecture\n\
+-mpower8, -mpwr8 generate code for Power8 architecture\n\
+-mcell generate code for Cell Broadband Engine architecture\n\
+-mcom generate code Power/PowerPC common instructions\n\
+-many generate code for any architecture (PWR/PWRX/PPC)\n"));
+ fprintf (stream, _("\
+-maltivec generate code for AltiVec\n\
+-mvsx generate code for Vector-Scalar (VSX) instructions\n\
+-mhtm generate code for Hardware Transactional Memory\n\
+-me300 generate code for PowerPC e300 family\n\
+-me500, -me500x2 generate code for Motorola e500 core complex\n\
+-me500mc, generate code for Freescale e500mc core complex\n\
+-me500mc64, generate code for Freescale e500mc64 core complex\n\
+-me5500, generate code for Freescale e5500 core complex\n\
+-me6500, generate code for Freescale e6500 core complex\n\
+-mspe generate code for Motorola SPE instructions\n\
+-mvle generate code for Freescale VLE instructions\n\
+-mtitan generate code for AppliedMicro Titan core complex\n\
+-mregnames Allow symbolic names for registers\n\
+-mno-regnames Do not allow symbolic names for registers\n"));
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+-mrelocatable support for GCC's -mrelocatble option\n\
+-mrelocatable-lib support for GCC's -mrelocatble-lib option\n\
+-memb set PPC_EMB bit in ELF flags\n\
+-mlittle, -mlittle-endian, -le\n\
+ generate code for a little endian machine\n\
+-mbig, -mbig-endian, -be\n\
+ generate code for a big endian machine\n\
+-msolaris generate code for Solaris\n\
+-mno-solaris do not generate code for Solaris\n\
+-K PIC set EF_PPC_RELOCATABLE_LIB in ELF flags\n\
+-V print assembler version number\n\
+-Qy, -Qn ignored\n"));
+#endif
+ fprintf (stream, _("\
+-nops=count when aligning, more than COUNT nops uses a branch\n\
+-ppc476-workaround warn if emitting data to code sections\n"));
+}
+
+/* Set ppc_cpu if it is not already set. */
+
+static void
+ppc_set_cpu (void)
+{
+ const char *default_os = TARGET_OS;
+ const char *default_cpu = TARGET_CPU;
+
+ if ((ppc_cpu & ~(ppc_cpu_t) PPC_OPCODE_ANY) == 0)
+ {
+ if (ppc_obj64)
+ ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_64;
+ else if (strncmp (default_os, "aix", 3) == 0
+ && default_os[3] >= '4' && default_os[3] <= '9')
+ ppc_cpu |= PPC_OPCODE_COMMON;
+ else if (strncmp (default_os, "aix3", 4) == 0)
+ ppc_cpu |= PPC_OPCODE_POWER;
+ else if (strcmp (default_cpu, "rs6000") == 0)
+ ppc_cpu |= PPC_OPCODE_POWER;
+ else if (strncmp (default_cpu, "powerpc", 7) == 0)
+ ppc_cpu |= PPC_OPCODE_PPC;
+ else
+ as_fatal (_("unknown default cpu = %s, os = %s"),
+ default_cpu, default_os);
+ }
+}
+
+/* Figure out the BFD architecture to use. This function and ppc_mach
+ are called well before md_begin, when the output file is opened. */
+
+enum bfd_architecture
+ppc_arch (void)
+{
+ const char *default_cpu = TARGET_CPU;
+ ppc_set_cpu ();
+
+ if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
+ return bfd_arch_powerpc;
+ if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+ return bfd_arch_powerpc;
+ if ((ppc_cpu & PPC_OPCODE_POWER) != 0)
+ return bfd_arch_rs6000;
+ if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
+ {
+ if (strcmp (default_cpu, "rs6000") == 0)
+ return bfd_arch_rs6000;
+ else if (strncmp (default_cpu, "powerpc", 7) == 0)
+ return bfd_arch_powerpc;
+ }
+
+ as_fatal (_("neither Power nor PowerPC opcodes were selected."));
+ return bfd_arch_unknown;
+}
+
+unsigned long
+ppc_mach (void)
+{
+ if (ppc_obj64)
+ return bfd_mach_ppc64;
+ else if (ppc_arch () == bfd_arch_rs6000)
+ return bfd_mach_rs6k;
+ else if (ppc_cpu & PPC_OPCODE_TITAN)
+ return bfd_mach_ppc_titan;
+ else if (ppc_cpu & PPC_OPCODE_VLE)
+ return bfd_mach_ppc_vle;
+ else
+ return bfd_mach_ppc;
+}
+
+extern char*
+ppc_target_format (void)
+{
+#ifdef OBJ_COFF
+#ifdef TE_PE
+ return target_big_endian ? "pe-powerpc" : "pe-powerpcle";
+#elif TE_POWERMAC
+ return "xcoff-powermac";
+#else
+# ifdef TE_AIX5
+ return (ppc_obj64 ? "aix5coff64-rs6000" : "aixcoff-rs6000");
+# else
+ return (ppc_obj64 ? "aixcoff64-rs6000" : "aixcoff-rs6000");
+# endif
+#endif
+#endif
+#ifdef OBJ_ELF
+# ifdef TE_FreeBSD
+ return (ppc_obj64 ? "elf64-powerpc-freebsd" : "elf32-powerpc-freebsd");
+# elif defined (TE_VXWORKS)
+ return "elf32-powerpc-vxworks";
+# else
+ return (target_big_endian
+ ? (ppc_obj64 ? "elf64-powerpc" : "elf32-powerpc")
+ : (ppc_obj64 ? "elf64-powerpcle" : "elf32-powerpcle"));
+# endif
+#endif
+}
+
+/* Validate one entry in powerpc_opcodes[] or vle_opcodes[].
+ Return TRUE if there's a problem, otherwise FALSE. */
+
+static bfd_boolean
+insn_validate (const struct powerpc_opcode *op)
+{
+ const unsigned char *o;
+ unsigned long omask = op->mask;
+
+ /* The mask had better not trim off opcode bits. */
+ if ((op->opcode & omask) != op->opcode)
+ {
+ as_bad (_("mask trims opcode bits for %s"), op->name);
+ return TRUE;
+ }
+
+ /* The operands must not overlap the opcode or each other. */
+ for (o = op->operands; *o; ++o)
+ {
+ if (*o >= num_powerpc_operands)
+ {
+ as_bad (_("operand index error for %s"), op->name);
+ return TRUE;
+ }
+ else
+ {
+ const struct powerpc_operand *operand = &powerpc_operands[*o];
+ if (operand->shift != PPC_OPSHIFT_INV)
+ {
+ unsigned long mask;
+
+ if (operand->shift >= 0)
+ mask = operand->bitm << operand->shift;
+ else
+ mask = operand->bitm >> -operand->shift;
+ if (omask & mask)
+ {
+ as_bad (_("operand %d overlap in %s"),
+ (int) (o - op->operands), op->name);
+ return TRUE;
+ }
+ omask |= mask;
+ }
+ }
+ }
+ return FALSE;
+}
+
+/* Insert opcodes and macros into hash tables. Called at startup and
+ for .machine pseudo. */
+
+static void
+ppc_setup_opcodes (void)
+{
+ const struct powerpc_opcode *op;
+ const struct powerpc_opcode *op_end;
+ const struct powerpc_macro *macro;
+ const struct powerpc_macro *macro_end;
+ bfd_boolean bad_insn = FALSE;
+
+ if (ppc_hash != NULL)
+ hash_die (ppc_hash);
+ if (ppc_macro_hash != NULL)
+ hash_die (ppc_macro_hash);
+
+ /* Insert the opcodes into a hash table. */
+ ppc_hash = hash_new ();
+
+ if (ENABLE_CHECKING)
+ {
+ unsigned int i;
+
+ /* An index into powerpc_operands is stored in struct fix
+ fx_pcrel_adjust which is 8 bits wide. */
+ gas_assert (num_powerpc_operands < 256);
+
+ /* Check operand masks. Code here and in the disassembler assumes
+ all the 1's in the mask are contiguous. */
+ for (i = 0; i < num_powerpc_operands; ++i)
+ {
+ unsigned long mask = powerpc_operands[i].bitm;
+ unsigned long right_bit;
+ unsigned int j;
+
+ right_bit = mask & -mask;
+ mask += right_bit;
+ right_bit = mask & -mask;
+ if (mask != right_bit)
+ {
+ as_bad (_("powerpc_operands[%d].bitm invalid"), i);
+ bad_insn = TRUE;
+ }
+ for (j = i + 1; j < num_powerpc_operands; ++j)
+ if (memcmp (&powerpc_operands[i], &powerpc_operands[j],
+ sizeof (powerpc_operands[0])) == 0)
+ {
+ as_bad (_("powerpc_operands[%d] duplicates powerpc_operands[%d]"),
+ j, i);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ op_end = powerpc_opcodes + powerpc_num_opcodes;
+ for (op = powerpc_opcodes; op < op_end; op++)
+ {
+ if (ENABLE_CHECKING)
+ {
+ if (op != powerpc_opcodes)
+ {
+ int old_opcode = PPC_OP (op[-1].opcode);
+ int new_opcode = PPC_OP (op[0].opcode);
+
+#ifdef PRINT_OPCODE_TABLE
+ printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+ op->name, (unsigned int) (op - powerpc_opcodes),
+ (unsigned int) new_opcode, (unsigned int) op->opcode,
+ (unsigned int) op->mask, (unsigned long long) op->flags);
+#endif
+
+ /* The major opcodes had better be sorted. Code in the
+ disassembler assumes the insns are sorted according to
+ major opcode. */
+ if (new_opcode < old_opcode)
+ {
+ as_bad (_("major opcode is not sorted for %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+ bad_insn |= insn_validate (op);
+ }
+
+ if ((ppc_cpu & op->flags) != 0
+ && !(ppc_cpu & op->deprecated))
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_hash, op->name, (void *) op);
+ if (retval != NULL)
+ {
+ as_bad (_("duplicate instruction %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
+ for (op = powerpc_opcodes; op < op_end; op++)
+ hash_insert (ppc_hash, op->name, (void *) op);
+
+ op_end = vle_opcodes + vle_num_opcodes;
+ for (op = vle_opcodes; op < op_end; op++)
+ {
+ if (ENABLE_CHECKING)
+ {
+ if (op != vle_opcodes)
+ {
+ unsigned old_seg, new_seg;
+
+ old_seg = VLE_OP (op[-1].opcode, op[-1].mask);
+ old_seg = VLE_OP_TO_SEG (old_seg);
+ new_seg = VLE_OP (op[0].opcode, op[0].mask);
+ new_seg = VLE_OP_TO_SEG (new_seg);
+
+#ifdef PRINT_OPCODE_TABLE
+ printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+ op->name, (unsigned int) (op - powerpc_opcodes),
+ (unsigned int) new_seg, (unsigned int) op->opcode,
+ (unsigned int) op->mask, (unsigned long long) op->flags);
+#endif
+ /* The major opcodes had better be sorted. Code in the
+ disassembler assumes the insns are sorted according to
+ major opcode. */
+ if (new_seg < old_seg)
+ {
+ as_bad (_("major opcode is not sorted for %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+
+ bad_insn |= insn_validate (op);
+ }
+
+ if ((ppc_cpu & op->flags) != 0
+ && !(ppc_cpu & op->deprecated))
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_hash, op->name, (void *) op);
+ if (retval != NULL)
+ {
+ as_bad (_("duplicate instruction %s"),
+ op->name);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+ for (op = vle_opcodes; op < op_end; op++)
+ hash_insert (ppc_hash, op->name, (void *) op);
+
+ /* Insert the macros into a hash table. */
+ ppc_macro_hash = hash_new ();
+
+ macro_end = powerpc_macros + powerpc_num_macros;
+ for (macro = powerpc_macros; macro < macro_end; macro++)
+ {
+ if ((macro->flags & ppc_cpu) != 0 || (ppc_cpu & PPC_OPCODE_ANY) != 0)
+ {
+ const char *retval;
+
+ retval = hash_insert (ppc_macro_hash, macro->name, (void *) macro);
+ if (retval != (const char *) NULL)
+ {
+ as_bad (_("duplicate macro %s"), macro->name);
+ bad_insn = TRUE;
+ }
+ }
+ }
+
+ if (bad_insn)
+ abort ();
+}
+
+/* This function is called when the assembler starts up. It is called
+ after the options have been parsed and the output file has been
+ opened. */
+
+void
+md_begin (void)
+{
+ ppc_set_cpu ();
+
+ ppc_cie_data_alignment = ppc_obj64 ? -8 : -4;
+ ppc_dwarf2_line_min_insn_length = (ppc_cpu & PPC_OPCODE_VLE) ? 2 : 4;
+
+#ifdef OBJ_ELF
+ /* Set the ELF flags if desired. */
+ if (ppc_flags && !msolaris)
+ bfd_set_private_flags (stdoutput, ppc_flags);
+#endif
+
+ ppc_setup_opcodes ();
+
+ /* Tell the main code what the endianness is if it is not overridden
+ by the user. */
+ if (!set_target_endian)
+ {
+ set_target_endian = 1;
+ target_big_endian = PPC_BIG_ENDIAN;
+ }
+
+#ifdef OBJ_XCOFF
+ ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG);
+
+ /* Create dummy symbols to serve as initial csects. This forces the
+ text csects to precede the data csects. These symbols will not
+ be output. */
+ ppc_text_csects = symbol_make ("dummy\001");
+ symbol_get_tc (ppc_text_csects)->within = ppc_text_csects;
+ ppc_data_csects = symbol_make ("dummy\001");
+ symbol_get_tc (ppc_data_csects)->within = ppc_data_csects;
+#endif
+
+#ifdef TE_PE
+
+ ppc_current_section = text_section;
+ ppc_previous_section = 0;
+
+#endif
+}
+
+void
+ppc_cleanup (void)
+{
+#ifdef OBJ_ELF
+ if (ppc_apuinfo_list == NULL)
+ return;
+
+ /* Ok, so write the section info out. We have this layout:
+
+ byte data what
+ ---- ---- ----
+ 0 8 length of "APUinfo\0"
+ 4 (n*4) number of APU's (4 bytes each)
+ 8 2 note type 2
+ 12 "APUinfo\0" name
+ 20 APU#1 first APU's info
+ 24 APU#2 second APU's info
+ ... ...
+ */
+ {
+ char *p;
+ asection *seg = now_seg;
+ subsegT subseg = now_subseg;
+ asection *apuinfo_secp = (asection *) NULL;
+ unsigned int i;
+
+ /* Create the .PPC.EMB.apuinfo section. */
+ apuinfo_secp = subseg_new (".PPC.EMB.apuinfo", 0);
+ bfd_set_section_flags (stdoutput,
+ apuinfo_secp,
+ SEC_HAS_CONTENTS | SEC_READONLY);
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) 8, 4);
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) ppc_apuinfo_num * 4, 4);
+
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) 2, 4);
+
+ p = frag_more (8);
+ strcpy (p, "APUinfo");
+
+ for (i = 0; i < ppc_apuinfo_num; i++)
+ {
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) ppc_apuinfo_list[i], 4);
+ }
+
+ frag_align (2, 0, 0);
+
+ /* We probably can't restore the current segment, for there likely
+ isn't one yet... */
+ if (seg && subseg)
+ subseg_set (seg, subseg);
+ }
+#endif
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned long
+ppc_insert_operand (unsigned long insn,
+ const struct powerpc_operand *operand,
+ offsetT val,
+ ppc_cpu_t cpu,
+ char *file,
+ unsigned int line)
+{
+ long min, max, right;
+
+ max = operand->bitm;
+ right = max & -max;
+ min = 0;
+
+ if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0)
+ {
+ /* Extend the allowed range for addis to [-65536, 65535].
+ Similarly for some VLE high part insns. For 64-bit it
+ would be good to disable this for signed fields since the
+ value is sign extended into the high 32 bits of the register.
+ If the value is, say, an address, then we might care about
+ the high bits. However, gcc as of 2014-06 uses unsigned
+ values when loading the high part of 64-bit constants using
+ lis.
+ Use the same extended range for cmpli, to allow at least
+ [-32768, 65535]. */
+ min = ~max & -right;
+ }
+ else if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ max = (max >> 1) & -right;
+ min = ~max & -right;
+ }
+
+ if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
+ max++;
+
+ if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+ {
+ long tmp = min;
+ min = -max;
+ max = -tmp;
+ }
+
+ if (min <= max)
+ {
+ /* Some people write constants with the sign extension done by
+ hand but only up to 32 bits. This shouldn't really be valid,
+ but, to permit this code to assemble on a 64-bit host, we
+ sign extend the 32-bit value to 64 bits if so doing makes the
+ value valid. */
+ if (val > max
+ && (offsetT) (val - 0x80000000 - 0x80000000) >= min
+ && (offsetT) (val - 0x80000000 - 0x80000000) <= max
+ && ((val - 0x80000000 - 0x80000000) & (right - 1)) == 0)
+ val = val - 0x80000000 - 0x80000000;
+
+ /* Similarly, people write expressions like ~(1<<15), and expect
+ this to be OK for a 32-bit unsigned value. */
+ else if (val < min
+ && (offsetT) (val + 0x80000000 + 0x80000000) >= min
+ && (offsetT) (val + 0x80000000 + 0x80000000) <= max
+ && ((val + 0x80000000 + 0x80000000) & (right - 1)) == 0)
+ val = val + 0x80000000 + 0x80000000;
+
+ else if (val < min
+ || val > max
+ || (val & (right - 1)) != 0)
+ as_bad_value_out_of_range (_("operand"), val, min, max, file, line);
+ }
+
+ if (operand->insert)
+ {
+ const char *errmsg;
+
+ errmsg = NULL;
+ insn = (*operand->insert) (insn, (long) val, cpu, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad_where (file, line, "%s", errmsg);
+ }
+ else if (operand->shift >= 0)
+ insn |= ((long) val & operand->bitm) << operand->shift;
+ else
+ insn |= ((long) val & operand->bitm) >> -operand->shift;
+
+ return insn;
+}
+
+
+#ifdef OBJ_ELF
+/* Parse @got, etc. and return the desired relocation. */
+static bfd_reloc_code_real_type
+ppc_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ struct map_bfd {
+ char *string;
+ unsigned int length : 8;
+ unsigned int valid32 : 1;
+ unsigned int valid64 : 1;
+ unsigned int reloc;
+ };
+
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ const struct map_bfd *ptr;
+
+#define MAP(str, reloc) { str, sizeof (str) - 1, 1, 1, reloc }
+#define MAP32(str, reloc) { str, sizeof (str) - 1, 1, 0, reloc }
+#define MAP64(str, reloc) { str, sizeof (str) - 1, 0, 1, reloc }
+
+ static const struct map_bfd mapping[] = {
+ MAP ("l", BFD_RELOC_LO16),
+ MAP ("h", BFD_RELOC_HI16),
+ MAP ("ha", BFD_RELOC_HI16_S),
+ MAP ("brtaken", BFD_RELOC_PPC_B16_BRTAKEN),
+ MAP ("brntaken", BFD_RELOC_PPC_B16_BRNTAKEN),
+ MAP ("got", BFD_RELOC_16_GOTOFF),
+ MAP ("got@l", BFD_RELOC_LO16_GOTOFF),
+ MAP ("got@h", BFD_RELOC_HI16_GOTOFF),
+ MAP ("got@ha", BFD_RELOC_HI16_S_GOTOFF),
+ MAP ("plt@l", BFD_RELOC_LO16_PLTOFF),
+ MAP ("plt@h", BFD_RELOC_HI16_PLTOFF),
+ MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF),
+ MAP ("copy", BFD_RELOC_PPC_COPY),
+ MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT),
+ MAP ("sectoff", BFD_RELOC_16_BASEREL),
+ MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL),
+ MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL),
+ MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL),
+ MAP ("tls", BFD_RELOC_PPC_TLS),
+ MAP ("dtpmod", BFD_RELOC_PPC_DTPMOD),
+ MAP ("dtprel", BFD_RELOC_PPC_DTPREL),
+ MAP ("dtprel@l", BFD_RELOC_PPC_DTPREL16_LO),
+ MAP ("dtprel@h", BFD_RELOC_PPC_DTPREL16_HI),
+ MAP ("dtprel@ha", BFD_RELOC_PPC_DTPREL16_HA),
+ MAP ("tprel", BFD_RELOC_PPC_TPREL),
+ MAP ("tprel@l", BFD_RELOC_PPC_TPREL16_LO),
+ MAP ("tprel@h", BFD_RELOC_PPC_TPREL16_HI),
+ MAP ("tprel@ha", BFD_RELOC_PPC_TPREL16_HA),
+ MAP ("got@tlsgd", BFD_RELOC_PPC_GOT_TLSGD16),
+ MAP ("got@tlsgd@l", BFD_RELOC_PPC_GOT_TLSGD16_LO),
+ MAP ("got@tlsgd@h", BFD_RELOC_PPC_GOT_TLSGD16_HI),
+ MAP ("got@tlsgd@ha", BFD_RELOC_PPC_GOT_TLSGD16_HA),
+ MAP ("got@tlsld", BFD_RELOC_PPC_GOT_TLSLD16),
+ MAP ("got@tlsld@l", BFD_RELOC_PPC_GOT_TLSLD16_LO),
+ MAP ("got@tlsld@h", BFD_RELOC_PPC_GOT_TLSLD16_HI),
+ MAP ("got@tlsld@ha", BFD_RELOC_PPC_GOT_TLSLD16_HA),
+ MAP ("got@dtprel", BFD_RELOC_PPC_GOT_DTPREL16),
+ MAP ("got@dtprel@l", BFD_RELOC_PPC_GOT_DTPREL16_LO),
+ MAP ("got@dtprel@h", BFD_RELOC_PPC_GOT_DTPREL16_HI),
+ MAP ("got@dtprel@ha", BFD_RELOC_PPC_GOT_DTPREL16_HA),
+ MAP ("got@tprel", BFD_RELOC_PPC_GOT_TPREL16),
+ MAP ("got@tprel@l", BFD_RELOC_PPC_GOT_TPREL16_LO),
+ MAP ("got@tprel@h", BFD_RELOC_PPC_GOT_TPREL16_HI),
+ MAP ("got@tprel@ha", BFD_RELOC_PPC_GOT_TPREL16_HA),
+ MAP32 ("fixup", BFD_RELOC_CTOR),
+ MAP32 ("plt", BFD_RELOC_24_PLT_PCREL),
+ MAP32 ("pltrel24", BFD_RELOC_24_PLT_PCREL),
+ MAP32 ("local24pc", BFD_RELOC_PPC_LOCAL24PC),
+ MAP32 ("local", BFD_RELOC_PPC_LOCAL24PC),
+ MAP32 ("pltrel", BFD_RELOC_32_PLT_PCREL),
+ MAP32 ("sdarel", BFD_RELOC_GPREL16),
+ MAP32 ("sdarel@l", BFD_RELOC_PPC_VLE_SDAREL_LO16A),
+ MAP32 ("sdarel@h", BFD_RELOC_PPC_VLE_SDAREL_HI16A),
+ MAP32 ("sdarel@ha", BFD_RELOC_PPC_VLE_SDAREL_HA16A),
+ MAP32 ("naddr", BFD_RELOC_PPC_EMB_NADDR32),
+ MAP32 ("naddr16", BFD_RELOC_PPC_EMB_NADDR16),
+ MAP32 ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO),
+ MAP32 ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI),
+ MAP32 ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA),
+ MAP32 ("sdai16", BFD_RELOC_PPC_EMB_SDAI16),
+ MAP32 ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL),
+ MAP32 ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16),
+ MAP32 ("sda21", BFD_RELOC_PPC_EMB_SDA21),
+ MAP32 ("sda21@l", BFD_RELOC_PPC_VLE_SDA21_LO),
+ MAP32 ("mrkref", BFD_RELOC_PPC_EMB_MRKREF),
+ MAP32 ("relsect", BFD_RELOC_PPC_EMB_RELSEC16),
+ MAP32 ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO),
+ MAP32 ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI),
+ MAP32 ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA),
+ MAP32 ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD),
+ MAP32 ("relsda", BFD_RELOC_PPC_EMB_RELSDA),
+ MAP32 ("xgot", BFD_RELOC_PPC_TOC16),
+ MAP64 ("high", BFD_RELOC_PPC64_ADDR16_HIGH),
+ MAP64 ("higha", BFD_RELOC_PPC64_ADDR16_HIGHA),
+ MAP64 ("higher", BFD_RELOC_PPC64_HIGHER),
+ MAP64 ("highera", BFD_RELOC_PPC64_HIGHER_S),
+ MAP64 ("highest", BFD_RELOC_PPC64_HIGHEST),
+ MAP64 ("highesta", BFD_RELOC_PPC64_HIGHEST_S),
+ MAP64 ("tocbase", BFD_RELOC_PPC64_TOC),
+ MAP64 ("toc", BFD_RELOC_PPC_TOC16),
+ MAP64 ("toc@l", BFD_RELOC_PPC64_TOC16_LO),
+ MAP64 ("toc@h", BFD_RELOC_PPC64_TOC16_HI),
+ MAP64 ("toc@ha", BFD_RELOC_PPC64_TOC16_HA),
+ MAP64 ("dtprel@high", BFD_RELOC_PPC64_DTPREL16_HIGH),
+ MAP64 ("dtprel@higha", BFD_RELOC_PPC64_DTPREL16_HIGHA),
+ MAP64 ("dtprel@higher", BFD_RELOC_PPC64_DTPREL16_HIGHER),
+ MAP64 ("dtprel@highera", BFD_RELOC_PPC64_DTPREL16_HIGHERA),
+ MAP64 ("dtprel@highest", BFD_RELOC_PPC64_DTPREL16_HIGHEST),
+ MAP64 ("dtprel@highesta", BFD_RELOC_PPC64_DTPREL16_HIGHESTA),
+ MAP64 ("localentry", BFD_RELOC_PPC64_ADDR64_LOCAL),
+ MAP64 ("tprel@high", BFD_RELOC_PPC64_TPREL16_HIGH),
+ MAP64 ("tprel@higha", BFD_RELOC_PPC64_TPREL16_HIGHA),
+ MAP64 ("tprel@higher", BFD_RELOC_PPC64_TPREL16_HIGHER),
+ MAP64 ("tprel@highera", BFD_RELOC_PPC64_TPREL16_HIGHERA),
+ MAP64 ("tprel@highest", BFD_RELOC_PPC64_TPREL16_HIGHEST),
+ MAP64 ("tprel@highesta", BFD_RELOC_PPC64_TPREL16_HIGHESTA),
+ { (char *) 0, 0, 0, 0, BFD_RELOC_NONE }
+ };
+
+ if (*str++ != '@')
+ return BFD_RELOC_NONE;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = TOLOWER (ch);
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->string[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->string, ptr->length) == 0
+ && (ppc_obj64 ? ptr->valid64 : ptr->valid32))
+ {
+ int reloc = ptr->reloc;
+
+ if (!ppc_obj64 && exp_p->X_add_number != 0)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16_GOTOFF:
+ case BFD_RELOC_HI16_GOTOFF:
+ case BFD_RELOC_HI16_S_GOTOFF:
+ as_warn (_("identifier+constant@got means "
+ "identifier@got+constant"));
+ break;
+
+ case BFD_RELOC_PPC_GOT_TLSGD16:
+ case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+ case BFD_RELOC_PPC_GOT_TLSLD16:
+ case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16_HI:
+ case BFD_RELOC_PPC_GOT_TPREL16_HA:
+ as_bad (_("symbol+offset not supported for got tls"));
+ break;
+ }
+ }
+
+ /* Now check for identifier@suffix+constant. */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+ *str_p = str;
+
+ if (reloc == (int) BFD_RELOC_PPC64_TOC
+ && exp_p->X_op == O_symbol
+ && strcmp (S_GET_NAME (exp_p->X_add_symbol), ".TOC.") == 0)
+ {
+ /* Change the symbol so that the dummy .TOC. symbol can be
+ omitted from the object file. */
+ exp_p->X_add_symbol = &abs_symbol;
+ }
+
+ return (bfd_reloc_code_real_type) reloc;
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* Support @got, etc. on constants emitted via .short, .int etc. */
+
+bfd_reloc_code_real_type
+ppc_elf_parse_cons (expressionS *exp, unsigned int nbytes)
+{
+ expression (exp);
+ if (nbytes >= 2 && *input_line_pointer == '@')
+ return ppc_elf_suffix (&input_line_pointer, exp);
+ return BFD_RELOC_NONE;
+}
+
+/* Warn when emitting data to code sections, unless we are emitting
+ a relocation that ld --ppc476-workaround uses to recognise data
+ *and* there was an unconditional branch prior to the data. */
+
+void
+ppc_elf_cons_fix_check (expressionS *exp ATTRIBUTE_UNUSED,
+ unsigned int nbytes, fixS *fix)
+{
+ if (warn_476
+ && (now_seg->flags & SEC_CODE) != 0
+ && (nbytes != 4
+ || fix == NULL
+ || !(fix->fx_r_type == BFD_RELOC_32
+ || fix->fx_r_type == BFD_RELOC_CTOR
+ || fix->fx_r_type == BFD_RELOC_32_PCREL)
+ || !(last_seg == now_seg && last_subseg == now_subseg)
+ || !((last_insn & (0x3f << 26)) == (18u << 26)
+ || ((last_insn & (0x3f << 26)) == (16u << 26)
+ && (last_insn & (0x14 << 21)) == (0x14 << 21))
+ || ((last_insn & (0x3f << 26)) == (19u << 26)
+ && (last_insn & (0x3ff << 1)) == (16u << 1)
+ && (last_insn & (0x14 << 21)) == (0x14 << 21)))))
+ {
+ /* Flag that we've warned. */
+ if (fix != NULL)
+ fix->fx_tcbit = 1;
+
+ as_warn (_("data in executable section"));
+ }
+}
+
+/* Solaris pseduo op to change to the .rodata section. */
+static void
+ppc_elf_rdata (int xxx)
+{
+ char *save_line = input_line_pointer;
+ static char section[] = ".rodata\n";
+
+ /* Just pretend this is .section .rodata */
+ input_line_pointer = section;
+ obj_elf_section (xxx);
+
+ input_line_pointer = save_line;
+}
+
+/* Pseudo op to make file scope bss items. */
+static void
+ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+ offsetT align;
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 8;
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_bad (_("length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) size);
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Allocate_bss. */
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+ if (align)
+ {
+ /* Convert to a power of 2 alignment. */
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2);
+ if (align != 1)
+ {
+ as_bad (_("common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ record_alignment (bss_section, align2);
+ subseg_set (bss_section, 1);
+ if (align2)
+ frag_align (align2, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, bss_section);
+ subseg_set (old_sec, old_subsec);
+ demand_empty_rest_of_line ();
+}
+
+/* Pseudo op to set symbol local entry point. */
+static void
+ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer;
+ char c = get_symbol_end ();
+ char *p;
+ expressionS exp;
+ symbolS *sym;
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ *p = 0;
+ as_bad (_("expected comma after name `%s' in .localentry directive"),
+ name);
+ *p = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ expression (&exp);
+ if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing expression in .localentry directive"));
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ }
+ *p = 0;
+ sym = symbol_find_or_make (name);
+ *p = c;
+
+ if (resolve_expression (&exp)
+ && exp.X_op == O_constant)
+ {
+ unsigned char encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number);
+
+ if (exp.X_add_number != (offsetT) PPC64_LOCAL_ENTRY_OFFSET (encoded))
+ as_bad (_(".localentry expression for `%s' "
+ "is not a valid power of 2"), S_GET_NAME (sym));
+ else
+ {
+ bfdsym = symbol_get_bfdsym (sym);
+ elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ gas_assert (elfsym);
+ elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK;
+ elfsym->internal_elf_sym.st_other |= encoded;
+ if (ppc_abiversion == 0)
+ ppc_abiversion = 2;
+ }
+ }
+ else
+ as_bad (_(".localentry expression for `%s' "
+ "does not evaluate to a constant"), S_GET_NAME (sym));
+
+ demand_empty_rest_of_line ();
+}
+
+/* Pseudo op to set ABI version. */
+static void
+ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ expression (&exp);
+ if (exp.X_op == O_absent)
+ {
+ as_bad (_("missing expression in .abiversion directive"));
+ exp.X_op = O_constant;
+ exp.X_add_number = 0;
+ }
+
+ if (resolve_expression (&exp)
+ && exp.X_op == O_constant)
+ ppc_abiversion = exp.X_add_number;
+ else
+ as_bad (_(".abiversion expression does not evaluate to a constant"));
+ demand_empty_rest_of_line ();
+}
+
+/* Set ABI version in output file. */
+void
+ppc_elf_end (void)
+{
+ if (ppc_obj64 && ppc_abiversion != 0)
+ {
+ elf_elfheader (stdoutput)->e_flags &= ~EF_PPC64_ABI;
+ elf_elfheader (stdoutput)->e_flags |= ppc_abiversion & EF_PPC64_ABI;
+ }
+}
+
+/* Validate any relocations emitted for -mrelocatable, possibly adding
+ fixups for word relocations in writable segments, so we can adjust
+ them at runtime. */
+static void
+ppc_elf_validate_fix (fixS *fixp, segT seg)
+{
+ if (fixp->fx_done || fixp->fx_pcrel)
+ return;
+
+ switch (shlib)
+ {
+ case SHLIB_NONE:
+ case SHLIB_PIC:
+ return;
+
+ case SHLIB_MRELOCATABLE:
+ if (fixp->fx_r_type != BFD_RELOC_16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
+ && fixp->fx_r_type != BFD_RELOC_16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_LO16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_BASEREL
+ && fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL
+ && (seg->flags & SEC_LOAD) != 0
+ && strcmp (segment_name (seg), ".got2") != 0
+ && strcmp (segment_name (seg), ".dtors") != 0
+ && strcmp (segment_name (seg), ".ctors") != 0
+ && strcmp (segment_name (seg), ".fixup") != 0
+ && strcmp (segment_name (seg), ".gcc_except_table") != 0
+ && strcmp (segment_name (seg), ".eh_frame") != 0
+ && strcmp (segment_name (seg), ".ex_shared") != 0)
+ {
+ if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0
+ || fixp->fx_r_type != BFD_RELOC_CTOR)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("relocation cannot be done when using -mrelocatable"));
+ }
+ }
+ return;
+ }
+}
+
+/* Prevent elf_frob_file_before_adjust removing a weak undefined
+ function descriptor sym if the corresponding code sym is used. */
+
+void
+ppc_frob_file_before_adjust (void)
+{
+ symbolS *symp;
+ asection *toc;
+
+ if (!ppc_obj64)
+ return;
+
+ for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+ {
+ const char *name;
+ char *dotname;
+ symbolS *dotsym;
+ size_t len;
+
+ name = S_GET_NAME (symp);
+ if (name[0] == '.')
+ continue;
+
+ if (! S_IS_WEAK (symp)
+ || S_IS_DEFINED (symp))
+ continue;
+
+ len = strlen (name) + 1;
+ dotname = xmalloc (len + 1);
+ dotname[0] = '.';
+ memcpy (dotname + 1, name, len);
+ dotsym = symbol_find_noref (dotname, 1);
+ free (dotname);
+ if (dotsym != NULL && (symbol_used_p (dotsym)
+ || symbol_used_in_reloc_p (dotsym)))
+ symbol_mark_used (symp);
+
+ }
+
+ toc = bfd_get_section_by_name (stdoutput, ".toc");
+ if (toc != NULL
+ && toc_reloc_types != has_large_toc_reloc
+ && bfd_section_size (stdoutput, toc) > 0x10000)
+ as_warn (_("TOC section size exceeds 64k"));
+}
+
+/* .TOC. used in an opd entry as .TOC.@tocbase doesn't need to be
+ emitted. Other uses of .TOC. will cause the symbol to be marked
+ with BSF_KEEP in md_apply_fix. */
+
+void
+ppc_elf_adjust_symtab (void)
+{
+ if (ppc_obj64)
+ {
+ symbolS *symp;
+ symp = symbol_find (".TOC.");
+ if (symp != NULL)
+ {
+ asymbol *bsym = symbol_get_bfdsym (symp);
+ if ((bsym->flags & BSF_KEEP) == 0)
+ symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+ }
+ }
+}
+#endif /* OBJ_ELF */
+
+#ifdef TE_PE
+
+/*
+ * Summary of parse_toc_entry.
+ *
+ * in: Input_line_pointer points to the '[' in one of:
+ *
+ * [toc] [tocv] [toc32] [toc64]
+ *
+ * Anything else is an error of one kind or another.
+ *
+ * out:
+ * return value: success or failure
+ * toc_kind: kind of toc reference
+ * input_line_pointer:
+ * success: first char after the ']'
+ * failure: unchanged
+ *
+ * settings:
+ *
+ * [toc] - rv == success, toc_kind = default_toc
+ * [tocv] - rv == success, toc_kind = data_in_toc
+ * [toc32] - rv == success, toc_kind = must_be_32
+ * [toc64] - rv == success, toc_kind = must_be_64
+ *
+ */
+
+enum toc_size_qualifier
+{
+ default_toc, /* The toc cell constructed should be the system default size */
+ data_in_toc, /* This is a direct reference to a toc cell */
+ must_be_32, /* The toc cell constructed must be 32 bits wide */
+ must_be_64 /* The toc cell constructed must be 64 bits wide */
+};
+
+static int
+parse_toc_entry (enum toc_size_qualifier *toc_kind)
+{
+ char *start;
+ char *toc_spec;
+ char c;
+ enum toc_size_qualifier t;
+
+ /* Save the input_line_pointer. */
+ start = input_line_pointer;
+
+ /* Skip over the '[' , and whitespace. */
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ /* Find the spelling of the operand. */
+ toc_spec = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcmp (toc_spec, "toc") == 0)
+ {
+ t = default_toc;
+ }
+ else if (strcmp (toc_spec, "tocv") == 0)
+ {
+ t = data_in_toc;
+ }
+ else if (strcmp (toc_spec, "toc32") == 0)
+ {
+ t = must_be_32;
+ }
+ else if (strcmp (toc_spec, "toc64") == 0)
+ {
+ t = must_be_64;
+ }
+ else
+ {
+ as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec);
+ *input_line_pointer = c;
+ input_line_pointer = start;
+ return 0;
+ }
+
+ /* Now find the ']'. */
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE (); /* leading whitespace could be there. */
+ c = *input_line_pointer++; /* input_line_pointer->past char in c. */
+
+ if (c != ']')
+ {
+ as_bad (_("syntax error: expected `]', found `%c'"), c);
+ input_line_pointer = start;
+ return 0;
+ }
+
+ *toc_kind = t;
+ return 1;
+}
+#endif
+
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+/* See whether a symbol is in the TOC section. */
+
+static int
+ppc_is_toc_sym (symbolS *sym)
+{
+#ifdef OBJ_XCOFF
+ return (symbol_get_tc (sym)->symbol_class == XMC_TC
+ || symbol_get_tc (sym)->symbol_class == XMC_TC0);
+#endif
+#ifdef OBJ_ELF
+ const char *sname = segment_name (S_GET_SEGMENT (sym));
+ if (ppc_obj64)
+ return strcmp (sname, ".toc") == 0;
+ else
+ return strcmp (sname, ".got") == 0;
+#endif
+}
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+
+
+#ifdef OBJ_ELF
+#define APUID(a,v) ((((a) & 0xffff) << 16) | ((v) & 0xffff))
+static void
+ppc_apuinfo_section_add (unsigned int apu, unsigned int version)
+{
+ unsigned int i;
+
+ /* Check we don't already exist. */
+ for (i = 0; i < ppc_apuinfo_num; i++)
+ if (ppc_apuinfo_list[i] == APUID (apu, version))
+ return;
+
+ if (ppc_apuinfo_num == ppc_apuinfo_num_alloc)
+ {
+ if (ppc_apuinfo_num_alloc == 0)
+ {
+ ppc_apuinfo_num_alloc = 4;
+ ppc_apuinfo_list = (unsigned long *)
+ xmalloc (sizeof (unsigned long) * ppc_apuinfo_num_alloc);
+ }
+ else
+ {
+ ppc_apuinfo_num_alloc += 4;
+ ppc_apuinfo_list = (unsigned long *) xrealloc (ppc_apuinfo_list,
+ sizeof (unsigned long) * ppc_apuinfo_num_alloc);
+ }
+ }
+ ppc_apuinfo_list[ppc_apuinfo_num++] = APUID (apu, version);
+}
+#undef APUID
+#endif
+
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct ppc_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+#define MAX_INSN_FIXUPS (5)
+
+/* Form I16L. */
+#define E_OR2I_INSN 0x7000C000
+#define E_AND2I_DOT_INSN 0x7000C800
+#define E_OR2IS_INSN 0x7000D000
+#define E_LIS_INSN 0x7000E000
+#define E_AND2IS_DOT_INSN 0x7000E800
+
+/* Form I16A. */
+#define E_ADD2I_DOT_INSN 0x70008800
+#define E_ADD2IS_INSN 0x70009000
+#define E_CMP16I_INSN 0x70009800
+#define E_MULL2I_INSN 0x7000A000
+#define E_CMPL16I_INSN 0x7000A800
+#define E_CMPH16I_INSN 0x7000B000
+#define E_CMPHL16I_INSN 0x7000B800
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (char *str)
+{
+ char *s;
+ const struct powerpc_opcode *opcode;
+ unsigned long insn;
+ const unsigned char *opindex_ptr;
+ int skip_optional;
+ int need_paren;
+ int next_opindex;
+ struct ppc_fixup fixups[MAX_INSN_FIXUPS];
+ int fc;
+ char *f;
+ int addr_mod;
+ int i;
+ unsigned int insn_length;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str);
+ if (opcode == (const struct powerpc_opcode *) NULL)
+ {
+ const struct powerpc_macro *macro;
+
+ macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str);
+ if (macro == (const struct powerpc_macro *) NULL)
+ as_bad (_("unrecognized opcode: `%s'"), str);
+ else
+ ppc_macro (s, macro);
+
+ return;
+ }
+
+ insn = opcode->opcode;
+
+ str = s;
+ while (ISSPACE (*str))
+ ++str;
+
+ /* PowerPC operands are just expressions. The only real issue is
+ that a few operand types are optional. All cases which might use
+ an optional operand separate the operands only with commas (in some
+ cases parentheses are used, as in ``lwz 1,0(1)'' but such cases never
+ have optional operands). Most instructions with optional operands
+ have only one. Those that have more than one optional operand can
+ take either all their operands or none. So, before we start seriously
+ parsing the operands, we check to see if we have optional operands,
+ and if we do, we count the number of commas to see which operands
+ have been omitted. */
+ skip_optional = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct powerpc_operand *operand;
+
+ operand = &powerpc_operands[*opindex_ptr];
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
+ {
+ unsigned int opcount;
+ unsigned int num_operands_expected;
+
+ /* There is an optional operand. Count the number of
+ commas in the input line. */
+ if (*str == '\0')
+ opcount = 0;
+ else
+ {
+ opcount = 1;
+ s = str;
+ while ((s = strchr (s, ',')) != (char *) NULL)
+ {
+ ++opcount;
+ ++s;
+ }
+ }
+
+ /* Compute the number of expected operands.
+ Do not count fake operands. */
+ for (num_operands_expected = 0, i = 0; opcode->operands[i]; i ++)
+ if ((powerpc_operands [opcode->operands[i]].flags & PPC_OPERAND_FAKE) == 0)
+ ++ num_operands_expected;
+
+ /* If there are fewer operands in the line then are called
+ for by the instruction, we want to skip the optional
+ operands. */
+ if (opcount < num_operands_expected)
+ skip_optional = 1;
+
+ break;
+ }
+ }
+
+ /* Gather the operands. */
+ need_paren = 0;
+ next_opindex = 0;
+ fc = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct powerpc_operand *operand;
+ const char *errmsg;
+ char *hold;
+ expressionS ex;
+ char endc;
+
+ if (next_opindex == 0)
+ operand = &powerpc_operands[*opindex_ptr];
+ else
+ {
+ operand = &powerpc_operands[next_opindex];
+ next_opindex = 0;
+ }
+ errmsg = NULL;
+
+ /* If this is a fake operand, then we do not expect anything
+ from the input. */
+ if ((operand->flags & PPC_OPERAND_FAKE) != 0)
+ {
+ insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad ("%s", errmsg);
+ continue;
+ }
+
+ /* If this is an optional operand, and we are skipping it, just
+ insert a zero. */
+ if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
+ && skip_optional)
+ {
+ if (operand->insert)
+ {
+ insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
+ if (errmsg != (const char *) NULL)
+ as_bad ("%s", errmsg);
+ }
+ if ((operand->flags & PPC_OPERAND_NEXT) != 0)
+ next_opindex = *opindex_ptr + 1;
+ continue;
+ }
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+#ifdef TE_PE
+ if (*input_line_pointer == '[')
+ {
+ /* We are expecting something like the second argument here:
+ *
+ * lwz r4,[toc].GS.0.static_int(rtoc)
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ * The argument following the `]' must be a symbol name, and the
+ * register must be the toc register: 'rtoc' or '2'
+ *
+ * The effect is to 0 as the displacement field
+ * in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or
+ * the appropriate variation) reloc against it based on the symbol.
+ * The linker will build the toc, and insert the resolved toc offset.
+ *
+ * Note:
+ * o The size of the toc entry is currently assumed to be
+ * 32 bits. This should not be assumed to be a hard coded
+ * number.
+ * o In an effort to cope with a change from 32 to 64 bits,
+ * there are also toc entries that are specified to be
+ * either 32 or 64 bits:
+ * lwz r4,[toc32].GS.0.static_int(rtoc)
+ * lwz r4,[toc64].GS.0.static_int(rtoc)
+ * These demand toc entries of the specified size, and the
+ * instruction probably requires it.
+ */
+
+ int valid_toc;
+ enum toc_size_qualifier toc_kind;
+ bfd_reloc_code_real_type toc_reloc;
+
+ /* Go parse off the [tocXX] part. */
+ valid_toc = parse_toc_entry (&toc_kind);
+
+ if (!valid_toc)
+ {
+ ignore_rest_of_line ();
+ break;
+ }
+
+ /* Now get the symbol following the ']'. */
+ expression (&ex);
+
+ switch (toc_kind)
+ {
+ case default_toc:
+ /* In this case, we may not have seen the symbol yet,
+ since it is allowed to appear on a .extern or .globl
+ or just be a label in the .data section. */
+ toc_reloc = BFD_RELOC_PPC_TOC16;
+ break;
+ case data_in_toc:
+ /* 1. The symbol must be defined and either in the toc
+ section, or a global.
+ 2. The reloc generated must have the TOCDEFN flag set
+ in upper bit mess of the reloc type.
+ FIXME: It's a little confusing what the tocv
+ qualifier can be used for. At the very least, I've
+ seen three uses, only one of which I'm sure I can
+ explain. */
+ if (ex.X_op == O_symbol)
+ {
+ gas_assert (ex.X_add_symbol != NULL);
+ if (symbol_get_bfdsym (ex.X_add_symbol)->section
+ != tocdata_section)
+ {
+ as_bad (_("[tocv] symbol is not a toc symbol"));
+ }
+ }
+
+ toc_reloc = BFD_RELOC_PPC_TOC16;
+ break;
+ case must_be_32:
+ /* FIXME: these next two specifically specify 32/64 bit
+ toc entries. We don't support them today. Is this
+ the right way to say that? */
+ toc_reloc = BFD_RELOC_NONE;
+ as_bad (_("unimplemented toc32 expression modifier"));
+ break;
+ case must_be_64:
+ /* FIXME: see above. */
+ toc_reloc = BFD_RELOC_NONE;
+ as_bad (_("unimplemented toc64 expression modifier"));
+ break;
+ default:
+ fprintf (stderr,
+ _("Unexpected return value [%d] from parse_toc_entry!\n"),
+ toc_kind);
+ abort ();
+ break;
+ }
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].reloc = toc_reloc;
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ ++fc;
+
+ /* Ok. We've set up the fixup for the instruction. Now make it
+ look like the constant 0 was found here. */
+ ex.X_unsigned = 1;
+ ex.X_op = O_constant;
+ ex.X_add_number = 0;
+ ex.X_add_symbol = NULL;
+ ex.X_op_symbol = NULL;
+ }
+
+ else
+#endif /* TE_PE */
+ {
+ if ((reg_names_p
+ && (((operand->flags & PPC_OPERAND_CR_BIT) != 0)
+ || ((operand->flags & PPC_OPERAND_CR_REG) != 0)))
+ || !register_name (&ex))
+ {
+ char save_lex = lex_type['%'];
+
+ if (((operand->flags & PPC_OPERAND_CR_REG) != 0)
+ || (operand->flags & PPC_OPERAND_CR_BIT) != 0)
+ {
+ cr_operand = TRUE;
+ lex_type['%'] |= LEX_BEGIN_NAME;
+ }
+ expression (&ex);
+ cr_operand = FALSE;
+ lex_type['%'] = save_lex;
+ }
+ }
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ if (ex.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (ex.X_op == O_absent)
+ as_bad (_("missing operand"));
+ else if (ex.X_op == O_register)
+ {
+ insn = ppc_insert_operand (insn, operand, ex.X_add_number,
+ ppc_cpu, (char *) NULL, 0);
+ }
+ else if (ex.X_op == O_constant)
+ {
+#ifdef OBJ_ELF
+ /* Allow @HA, @L, @H on constants. */
+ bfd_reloc_code_real_type reloc;
+ char *orig_str = str;
+
+ if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
+ switch (reloc)
+ {
+ default:
+ str = orig_str;
+ break;
+
+ case BFD_RELOC_LO16:
+ ex.X_add_number &= 0xffff;
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+
+ case BFD_RELOC_HI16:
+ if (REPORT_OVERFLOW_HI && ppc_obj64)
+ {
+ /* PowerPC64 @h is tested for overflow. */
+ ex.X_add_number = (addressT) ex.X_add_number >> 16;
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
+ ex.X_add_number
+ = ((addressT) ex.X_add_number ^ sign) - sign;
+ }
+ break;
+ }
+ /* Fall thru */
+
+ case BFD_RELOC_PPC64_ADDR16_HIGH:
+ ex.X_add_number = PPC_HI (ex.X_add_number);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+
+ case BFD_RELOC_HI16_S:
+ if (REPORT_OVERFLOW_HI && ppc_obj64)
+ {
+ /* PowerPC64 @ha is tested for overflow. */
+ ex.X_add_number
+ = ((addressT) ex.X_add_number + 0x8000) >> 16;
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
+ ex.X_add_number
+ = ((addressT) ex.X_add_number ^ sign) - sign;
+ }
+ break;
+ }
+ /* Fall thru */
+
+ case BFD_RELOC_PPC64_ADDR16_HIGHA:
+ ex.X_add_number = PPC_HA (ex.X_add_number);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+
+ case BFD_RELOC_PPC64_HIGHER:
+ ex.X_add_number = PPC_HIGHER (ex.X_add_number);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+
+ case BFD_RELOC_PPC64_HIGHER_S:
+ ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+
+ case BFD_RELOC_PPC64_HIGHEST:
+ ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+
+ case BFD_RELOC_PPC64_HIGHEST_S:
+ ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
+ if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+ ex.X_add_number = SEX16 (ex.X_add_number);
+ break;
+ }
+#endif /* OBJ_ELF */
+ insn = ppc_insert_operand (insn, operand, ex.X_add_number,
+ ppc_cpu, (char *) NULL, 0);
+ }
+ else
+ {
+ bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
+#ifdef OBJ_ELF
+ if (ex.X_op == O_symbol && str[0] == '(')
+ {
+ const char *sym_name = S_GET_NAME (ex.X_add_symbol);
+ if (sym_name[0] == '.')
+ ++sym_name;
+
+ if (strcasecmp (sym_name, "__tls_get_addr") == 0)
+ {
+ expressionS tls_exp;
+
+ hold = input_line_pointer;
+ input_line_pointer = str + 1;
+ expression (&tls_exp);
+ if (tls_exp.X_op == O_symbol)
+ {
+ reloc = BFD_RELOC_NONE;
+ if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
+ {
+ reloc = BFD_RELOC_PPC_TLSGD;
+ input_line_pointer += 7;
+ }
+ else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0)
+ {
+ reloc = BFD_RELOC_PPC_TLSLD;
+ input_line_pointer += 7;
+ }
+ if (reloc != BFD_RELOC_NONE)
+ {
+ SKIP_WHITESPACE ();
+ str = input_line_pointer;
+
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = tls_exp;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+ }
+ input_line_pointer = hold;
+ }
+ }
+
+ if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
+ {
+ /* Some TLS tweaks. */
+ switch (reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_PPC_TLS:
+ if (!_bfd_elf_ppc_at_tls_transform (opcode->opcode, 0))
+ as_bad (_("@tls may not be used with \"%s\" operands"),
+ opcode->name);
+ else if (operand->shift != 11)
+ as_bad (_("@tls may only be used in last operand"));
+ else
+ insn = ppc_insert_operand (insn, operand,
+ ppc_obj64 ? 13 : 2,
+ ppc_cpu, (char *) NULL, 0);
+ break;
+
+ /* We'll only use the 32 (or 64) bit form of these relocations
+ in constants. Instructions get the 16 bit form. */
+ case BFD_RELOC_PPC_DTPREL:
+ reloc = BFD_RELOC_PPC_DTPREL16;
+ break;
+ case BFD_RELOC_PPC_TPREL:
+ reloc = BFD_RELOC_PPC_TPREL16;
+ break;
+ }
+
+ /* If VLE-mode convert LO/HI/HA relocations. */
+ if (opcode->flags & PPC_OPCODE_VLE)
+ {
+ int tmp_insn = insn & opcode->mask;
+
+ int use_d_reloc = (tmp_insn == E_OR2I_INSN
+ || tmp_insn == E_AND2I_DOT_INSN
+ || tmp_insn == E_OR2IS_INSN
+ || tmp_insn == E_LIS_INSN
+ || tmp_insn == E_AND2IS_DOT_INSN);
+
+
+ int use_a_reloc = (tmp_insn == E_ADD2I_DOT_INSN
+ || tmp_insn == E_ADD2IS_INSN
+ || tmp_insn == E_CMP16I_INSN
+ || tmp_insn == E_MULL2I_INSN
+ || tmp_insn == E_CMPL16I_INSN
+ || tmp_insn == E_CMPH16I_INSN
+ || tmp_insn == E_CMPHL16I_INSN);
+
+ switch (reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_PPC_EMB_SDA21:
+ reloc = BFD_RELOC_PPC_VLE_SDA21;
+ break;
+
+ case BFD_RELOC_LO16:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_LO16D;
+ else if (use_a_reloc)
+ reloc = BFD_RELOC_PPC_VLE_LO16A;
+ break;
+
+ case BFD_RELOC_HI16:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HI16D;
+ else if (use_a_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HI16A;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HA16D;
+ else if (use_a_reloc)
+ reloc = BFD_RELOC_PPC_VLE_HA16A;
+ break;
+
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_SDAREL_LO16D;
+ break;
+
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_SDAREL_HI16D;
+ break;
+
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+ if (use_d_reloc)
+ reloc = BFD_RELOC_PPC_VLE_SDAREL_HA16D;
+ break;
+ }
+ }
+ }
+#endif /* OBJ_ELF */
+
+ if (reloc != BFD_RELOC_NONE)
+ ;
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into
+ relocs. */
+ else if ((operand->flags & (PPC_OPERAND_RELATIVE
+ | PPC_OPERAND_ABSOLUTE)) != 0
+ && operand->bitm == 0x3fffffc
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_B26;
+ else if ((operand->flags & (PPC_OPERAND_RELATIVE
+ | PPC_OPERAND_ABSOLUTE)) != 0
+ && operand->bitm == 0xfffc
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_B16;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bitm == 0x1fe
+ && operand->shift == -1)
+ reloc = BFD_RELOC_PPC_VLE_REL8;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bitm == 0xfffe
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_VLE_REL15;
+ else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+ && operand->bitm == 0x1fffffe
+ && operand->shift == 0)
+ reloc = BFD_RELOC_PPC_VLE_REL24;
+ else if ((operand->flags & PPC_OPERAND_NEGATIVE) == 0
+ && (operand->bitm & 0xfff0) == 0xfff0
+ && operand->shift == 0)
+ {
+ reloc = BFD_RELOC_16;
+#if defined OBJ_XCOFF || defined OBJ_ELF
+ /* Note: the symbol may be not yet defined. */
+ if ((operand->flags & PPC_OPERAND_PARENS) != 0
+ && ppc_is_toc_sym (ex.X_add_symbol))
+ {
+ reloc = BFD_RELOC_PPC_TOC16;
+#ifdef OBJ_ELF
+ as_warn (_("assuming %s on symbol"),
+ ppc_obj64 ? "@toc" : "@xgot");
+#endif
+ }
+#endif
+ }
+
+ /* For the absolute forms of branches, convert the PC
+ relative form back into the absolute. */
+ if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_PPC_B26:
+ reloc = BFD_RELOC_PPC_BA26;
+ break;
+ case BFD_RELOC_PPC_B16:
+ reloc = BFD_RELOC_PPC_BA16;
+ break;
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
+ break;
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+#ifdef OBJ_ELF
+ switch (reloc)
+ {
+ case BFD_RELOC_PPC_TOC16:
+ toc_reloc_types |= has_small_toc_reloc;
+ break;
+ case BFD_RELOC_PPC64_TOC16_LO:
+ case BFD_RELOC_PPC64_TOC16_HI:
+ case BFD_RELOC_PPC64_TOC16_HA:
+ toc_reloc_types |= has_large_toc_reloc;
+ break;
+ default:
+ break;
+ }
+
+ if (ppc_obj64
+ && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_16:
+ reloc = BFD_RELOC_PPC64_ADDR16_DS;
+ break;
+ case BFD_RELOC_LO16:
+ reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
+ break;
+ case BFD_RELOC_16_GOTOFF:
+ reloc = BFD_RELOC_PPC64_GOT16_DS;
+ break;
+ case BFD_RELOC_LO16_GOTOFF:
+ reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
+ break;
+ case BFD_RELOC_LO16_PLTOFF:
+ reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
+ break;
+ case BFD_RELOC_16_BASEREL:
+ reloc = BFD_RELOC_PPC64_SECTOFF_DS;
+ break;
+ case BFD_RELOC_LO16_BASEREL:
+ reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
+ break;
+ case BFD_RELOC_PPC_TOC16:
+ reloc = BFD_RELOC_PPC64_TOC16_DS;
+ break;
+ case BFD_RELOC_PPC64_TOC16_LO:
+ reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
+ break;
+ case BFD_RELOC_PPC64_PLTGOT16:
+ reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
+ break;
+ case BFD_RELOC_PPC64_PLTGOT16_LO:
+ reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
+ break;
+ case BFD_RELOC_PPC_DTPREL16:
+ reloc = BFD_RELOC_PPC64_DTPREL16_DS;
+ break;
+ case BFD_RELOC_PPC_DTPREL16_LO:
+ reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
+ break;
+ case BFD_RELOC_PPC_TPREL16:
+ reloc = BFD_RELOC_PPC64_TPREL16_DS;
+ break;
+ case BFD_RELOC_PPC_TPREL16_LO:
+ reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
+ break;
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ break;
+ default:
+ as_bad (_("unsupported relocation for DS offset field"));
+ break;
+ }
+ }
+#endif
+
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+
+ if (need_paren)
+ {
+ endc = ')';
+ need_paren = 0;
+ /* If expecting more operands, then we want to see "),". */
+ if (*str == endc && opindex_ptr[1] != 0)
+ {
+ do
+ ++str;
+ while (ISSPACE (*str));
+ endc = ',';
+ }
+ }
+ else if ((operand->flags & PPC_OPERAND_PARENS) != 0)
+ {
+ endc = '(';
+ need_paren = 1;
+ }
+ else
+ endc = ',';
+
+ /* The call to expression should have advanced str past any
+ whitespace. */
+ if (*str != endc
+ && (endc != ',' || *str != '\0'))
+ {
+ if (*str == '\0')
+ as_bad (_("syntax error; end of line, expected `%c'"), endc);
+ else
+ as_bad (_("syntax error; found `%c', expected `%c'"), *str, endc);
+ break;
+ }
+
+ if (*str != '\0')
+ ++str;
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (*str != '\0')
+ as_bad (_("junk at end of line: `%s'"), str);
+
+#ifdef OBJ_ELF
+ /* Do we need/want an APUinfo section? */
+ if ((ppc_cpu & (PPC_OPCODE_E500 | PPC_OPCODE_E500MC | PPC_OPCODE_VLE)) != 0
+ && !ppc_obj64)
+ {
+ /* These are all version "1". */
+ if (opcode->flags & PPC_OPCODE_SPE)
+ ppc_apuinfo_section_add (PPC_APUINFO_SPE, 1);
+ if (opcode->flags & PPC_OPCODE_ISEL)
+ ppc_apuinfo_section_add (PPC_APUINFO_ISEL, 1);
+ if (opcode->flags & PPC_OPCODE_EFS)
+ ppc_apuinfo_section_add (PPC_APUINFO_EFS, 1);
+ if (opcode->flags & PPC_OPCODE_BRLOCK)
+ ppc_apuinfo_section_add (PPC_APUINFO_BRLOCK, 1);
+ if (opcode->flags & PPC_OPCODE_PMR)
+ ppc_apuinfo_section_add (PPC_APUINFO_PMR, 1);
+ if (opcode->flags & PPC_OPCODE_CACHELCK)
+ ppc_apuinfo_section_add (PPC_APUINFO_CACHELCK, 1);
+ if (opcode->flags & PPC_OPCODE_RFMCI)
+ ppc_apuinfo_section_add (PPC_APUINFO_RFMCI, 1);
+ /* Only set the VLE flag if the instruction has been pulled via
+ the VLE instruction set. This way the flag is guaranteed to
+ be set for VLE-only instructions or for VLE-only processors,
+ however it'll remain clear for dual-mode instructions on
+ dual-mode and, more importantly, standard-mode processors. */
+ if ((ppc_cpu & opcode->flags) == PPC_OPCODE_VLE)
+ ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
+ }
+#endif
+
+ /* Write out the instruction. */
+ /* Differentiate between two and four byte insns. */
+ if (ppc_mach () == bfd_mach_ppc_vle)
+ {
+ if (PPC_OP_SE_VLE (insn))
+ insn_length = 2;
+ else
+ insn_length = 4;
+ addr_mod = frag_now_fix () & 1;
+ }
+ else
+ {
+ insn_length = 4;
+ addr_mod = frag_now_fix () & 3;
+ }
+ /* All instructions can start on a 2 byte boundary for VLE. */
+ f = frag_more (insn_length);
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ {
+ if (ppc_mach() == bfd_mach_ppc_vle)
+ as_bad (_("instruction address is not a multiple of 2"));
+ else
+ as_bad (_("instruction address is not a multiple of 4"));
+ }
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+ md_number_to_chars (f, insn, insn_length);
+ last_insn = insn;
+ last_seg = now_seg;
+ last_subseg = now_subseg;
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (insn_length);
+#endif
+
+ /* Create any fixups. */
+ for (i = 0; i < fc; i++)
+ {
+ fixS *fixP;
+ if (fixups[i].reloc != BFD_RELOC_NONE)
+ {
+ reloc_howto_type *reloc_howto;
+ int size;
+ int offset;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+ offset = target_big_endian ? (insn_length - size) : 0;
+
+ if (size < 1 || size > 4)
+ abort ();
+
+ fixP = fix_new_exp (frag_now,
+ f - frag_now->fr_literal + offset,
+ size,
+ &fixups[i].exp,
+ reloc_howto->pc_relative,
+ fixups[i].reloc);
+ }
+ else
+ {
+ const struct powerpc_operand *operand;
+
+ operand = &powerpc_operands[fixups[i].opindex];
+ fixP = fix_new_exp (frag_now,
+ f - frag_now->fr_literal,
+ insn_length,
+ &fixups[i].exp,
+ (operand->flags & PPC_OPERAND_RELATIVE) != 0,
+ BFD_RELOC_NONE);
+ }
+ fixP->fx_pcrel_adjust = fixups[i].opindex;
+ }
+}
+
+/* Handle a macro. Gather all the operands, transform them as
+ described by the macro, and call md_assemble recursively. All the
+ operands are separated by commas; we don't accept parentheses
+ around operands here. */
+
+static void
+ppc_macro (char *str, const struct powerpc_macro *macro)
+{
+ char *operands[10];
+ unsigned int count;
+ char *s;
+ unsigned int len;
+ const char *format;
+ unsigned int arg;
+ char *send;
+ char *complete;
+
+ /* Gather the users operands into the operands array. */
+ count = 0;
+ s = str;
+ while (1)
+ {
+ if (count >= sizeof operands / sizeof operands[0])
+ break;
+ operands[count++] = s;
+ s = strchr (s, ',');
+ if (s == (char *) NULL)
+ break;
+ *s++ = '\0';
+ }
+
+ if (count != macro->operands)
+ {
+ as_bad (_("wrong number of operands"));
+ return;
+ }
+
+ /* Work out how large the string must be (the size is unbounded
+ because it includes user input). */
+ len = 0;
+ format = macro->format;
+ while (*format != '\0')
+ {
+ if (*format != '%')
+ {
+ ++len;
+ ++format;
+ }
+ else
+ {
+ arg = strtol (format + 1, &send, 10);
+ know (send != format && arg < count);
+ len += strlen (operands[arg]);
+ format = send;
+ }
+ }
+
+ /* Put the string together. */
+ complete = s = (char *) alloca (len + 1);
+ format = macro->format;
+ while (*format != '\0')
+ {
+ if (*format != '%')
+ *s++ = *format++;
+ else
+ {
+ arg = strtol (format + 1, &send, 10);
+ strcpy (s, operands[arg]);
+ s += strlen (s);
+ format = send;
+ }
+ }
+ *s = '\0';
+
+ /* Assemble the constructed instruction. */
+ md_assemble (complete);
+}
+
+#ifdef OBJ_ELF
+/* For ELF, add support for SHT_ORDERED. */
+
+int
+ppc_section_type (char *str, size_t len)
+{
+ if (len == 7 && strncmp (str, "ordered", 7) == 0)
+ return SHT_ORDERED;
+
+ return -1;
+}
+
+int
+ppc_section_flags (flagword flags, bfd_vma attr ATTRIBUTE_UNUSED, int type)
+{
+ if (type == SHT_ORDERED)
+ flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES;
+
+ return flags;
+}
+#endif /* OBJ_ELF */
+
+
+/* Pseudo-op handling. */
+
+/* The .byte pseudo-op. This is similar to the normal .byte
+ pseudo-op, but it can also take a single ASCII string. */
+
+static void
+ppc_byte (int ignore ATTRIBUTE_UNUSED)
+{
+ int count = 0;
+
+ if (*input_line_pointer != '\"')
+ {
+ cons (1);
+ return;
+ }
+
+ /* Gather characters. A real double quote is doubled. Unusual
+ characters are not permitted. */
+ ++input_line_pointer;
+ while (1)
+ {
+ char c;
+
+ c = *input_line_pointer++;
+
+ if (c == '\"')
+ {
+ if (*input_line_pointer != '\"')
+ break;
+ ++input_line_pointer;
+ }
+
+ FRAG_APPEND_1_CHAR (c);
+ ++count;
+ }
+
+ if (warn_476 && count != 0 && (now_seg->flags & SEC_CODE) != 0)
+ as_warn (_("data in executable section"));
+ demand_empty_rest_of_line ();
+}
+
+#ifdef OBJ_XCOFF
+
+/* XCOFF specific pseudo-op handling. */
+
+/* This is set if we are creating a .stabx symbol, since we don't want
+ to handle symbol suffixes for such symbols. */
+static bfd_boolean ppc_stab_symbol;
+
+/* The .comm and .lcomm pseudo-ops for XCOFF. XCOFF puts common
+ symbols in the .bss segment as though they were local common
+ symbols, and uses a different smclas. The native Aix 4.3.3 assembler
+ aligns .comm and .lcomm to 4 bytes. */
+
+static void
+ppc_comm (int lcomm)
+{
+ asection *current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ char *name;
+ char endc;
+ char *end_name;
+ offsetT size;
+ offsetT align;
+ symbolS *lcomm_sym = NULL;
+ symbolS *sym;
+ char *pfrag;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing size"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ size = get_absolute_expression ();
+ if (size < 0)
+ {
+ as_bad (_("negative size"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (! lcomm)
+ {
+ /* The third argument to .comm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 2;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 2;
+ }
+ }
+ }
+ else
+ {
+ char *lcomm_name;
+ char lcomm_endc;
+
+ /* The third argument to .lcomm appears to be the real local
+ common symbol to create. References to the symbol named in
+ the first argument are turned into references to the third
+ argument. */
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing real symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ lcomm_name = input_line_pointer;
+ lcomm_endc = get_symbol_end ();
+
+ lcomm_sym = symbol_find_or_make (lcomm_name);
+
+ *input_line_pointer = lcomm_endc;
+
+ /* The fourth argument to .lcomm is the alignment. */
+ if (*input_line_pointer != ',')
+ {
+ if (size <= 4)
+ align = 2;
+ else
+ align = 3;
+ }
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 2;
+ }
+ }
+ }
+
+ *end_name = '\0';
+ sym = symbol_find_or_make (name);
+ *end_name = endc;
+
+ if (S_IS_DEFINED (sym)
+ || S_GET_VALUE (sym) != 0)
+ {
+ as_bad (_("attempt to redefine symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ record_alignment (bss_section, align);
+
+ if (! lcomm
+ || ! S_IS_DEFINED (lcomm_sym))
+ {
+ symbolS *def_sym;
+ offsetT def_size;
+
+ if (! lcomm)
+ {
+ def_sym = sym;
+ def_size = size;
+ S_SET_EXTERNAL (sym);
+ }
+ else
+ {
+ symbol_get_tc (lcomm_sym)->output = 1;
+ def_sym = lcomm_sym;
+ def_size = 0;
+ }
+
+ subseg_set (bss_section, 1);
+ frag_align (align, 0, 0);
+
+ symbol_set_frag (def_sym, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym,
+ def_size, (char *) NULL);
+ *pfrag = 0;
+ S_SET_SEGMENT (def_sym, bss_section);
+ symbol_get_tc (def_sym)->align = align;
+ }
+ else if (lcomm)
+ {
+ /* Align the size of lcomm_sym. */
+ symbol_get_frag (lcomm_sym)->fr_offset =
+ ((symbol_get_frag (lcomm_sym)->fr_offset + (1 << align) - 1)
+ &~ ((1 << align) - 1));
+ if (align > symbol_get_tc (lcomm_sym)->align)
+ symbol_get_tc (lcomm_sym)->align = align;
+ }
+
+ if (lcomm)
+ {
+ /* Make sym an offset from lcomm_sym. */
+ S_SET_SEGMENT (sym, bss_section);
+ symbol_set_frag (sym, symbol_get_frag (lcomm_sym));
+ S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset);
+ symbol_get_frag (lcomm_sym)->fr_offset += size;
+ }
+
+ subseg_set (current_seg, current_subseg);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .csect pseudo-op. This switches us into a different
+ subsegment. The first argument is a symbol whose value is the
+ start of the .csect. In COFF, csect symbols get special aux
+ entries defined by the x_csect field of union internal_auxent. The
+ optional second argument is the alignment (the default is 2). */
+
+static void
+ppc_csect (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+ offsetT align;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (S_GET_NAME (sym)[0] == '\0')
+ {
+ /* An unnamed csect is assumed to be [PR]. */
+ symbol_get_tc (sym)->symbol_class = XMC_PR;
+ }
+
+ align = 2;
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ }
+
+ ppc_change_csect (sym, align);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Change to a different csect. */
+
+static void
+ppc_change_csect (symbolS *sym, offsetT align)
+{
+ if (S_IS_DEFINED (sym))
+ subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg);
+ else
+ {
+ symbolS **list_ptr;
+ int after_toc;
+ int hold_chunksize;
+ symbolS *list;
+ int is_code;
+ segT sec;
+
+ /* This is a new csect. We need to look at the symbol class to
+ figure out whether it should go in the text section or the
+ data section. */
+ after_toc = 0;
+ is_code = 0;
+ switch (symbol_get_tc (sym)->symbol_class)
+ {
+ case XMC_PR:
+ case XMC_RO:
+ case XMC_DB:
+ case XMC_GL:
+ case XMC_XO:
+ case XMC_SV:
+ case XMC_TI:
+ case XMC_TB:
+ S_SET_SEGMENT (sym, text_section);
+ symbol_get_tc (sym)->subseg = ppc_text_subsegment;
+ ++ppc_text_subsegment;
+ list_ptr = &ppc_text_csects;
+ is_code = 1;
+ break;
+ case XMC_RW:
+ case XMC_TC0:
+ case XMC_TC:
+ case XMC_DS:
+ case XMC_UA:
+ case XMC_BS:
+ case XMC_UC:
+ if (ppc_toc_csect != NULL
+ && (symbol_get_tc (ppc_toc_csect)->subseg + 1
+ == ppc_data_subsegment))
+ after_toc = 1;
+ S_SET_SEGMENT (sym, data_section);
+ symbol_get_tc (sym)->subseg = ppc_data_subsegment;
+ ++ppc_data_subsegment;
+ list_ptr = &ppc_data_csects;
+ break;
+ default:
+ abort ();
+ }
+
+ /* We set the obstack chunk size to a small value before
+ changing subsegments, so that we don't use a lot of memory
+ space for what may be a small section. */
+ hold_chunksize = chunksize;
+ chunksize = 64;
+
+ sec = subseg_new (segment_name (S_GET_SEGMENT (sym)),
+ symbol_get_tc (sym)->subseg);
+
+ chunksize = hold_chunksize;
+
+ if (after_toc)
+ ppc_after_toc_frag = frag_now;
+
+ record_alignment (sec, align);
+ if (is_code)
+ frag_align_code (align, 0);
+ else
+ frag_align (align, 0, 0);
+
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
+ symbol_get_tc (sym)->align = align;
+ symbol_get_tc (sym)->output = 1;
+ symbol_get_tc (sym)->within = sym;
+
+ for (list = *list_ptr;
+ symbol_get_tc (list)->next != (symbolS *) NULL;
+ list = symbol_get_tc (list)->next)
+ ;
+ symbol_get_tc (list)->next = sym;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP,
+ &symbol_lastP);
+ }
+
+ ppc_current_csect = sym;
+}
+
+static void
+ppc_change_debug_section (unsigned int idx, subsegT subseg)
+{
+ segT sec;
+ flagword oldflags;
+ const struct xcoff_dwsect_name *dw = &xcoff_dwsect_names[idx];
+
+ sec = subseg_new (dw->name, subseg);
+ oldflags = bfd_get_section_flags (stdoutput, sec);
+ if (oldflags == SEC_NO_FLAGS)
+ {
+ /* Just created section. */
+ gas_assert (dw_sections[idx].sect == NULL);
+
+ bfd_set_section_flags (stdoutput, sec, SEC_DEBUGGING);
+ bfd_set_section_alignment (stdoutput, sec, 0);
+ dw_sections[idx].sect = sec;
+ }
+
+ /* Not anymore in a csect. */
+ ppc_current_csect = NULL;
+}
+
+/* The .dwsect pseudo-op. Defines a DWARF section. Syntax is:
+ .dwsect flag [, opt-label ]
+*/
+
+static void
+ppc_dwsect (int ignore ATTRIBUTE_UNUSED)
+{
+ offsetT flag;
+ symbolS *opt_label;
+ const struct xcoff_dwsect_name *dw;
+ struct dw_subsection *subseg;
+ struct dw_section *dws;
+ int i;
+
+ /* Find section. */
+ flag = get_absolute_expression ();
+ dw = NULL;
+ for (i = 0; i < XCOFF_DWSECT_NBR_NAMES; i++)
+ if (xcoff_dwsect_names[i].flag == flag)
+ {
+ dw = &xcoff_dwsect_names[i];
+ break;
+ }
+
+ /* Parse opt-label. */
+ if (*input_line_pointer == ',')
+ {
+ const char *label;
+ char c;
+
+ ++input_line_pointer;
+
+ label = input_line_pointer;
+ c = get_symbol_end ();
+ opt_label = symbol_find_or_make (label);
+ *input_line_pointer = c;
+ }
+ else
+ opt_label = NULL;
+
+ demand_empty_rest_of_line ();
+
+ /* Return now in case of unknown subsection. */
+ if (dw == NULL)
+ {
+ as_bad (_("no known dwarf XCOFF section for flag 0x%08x\n"),
+ (unsigned)flag);
+ return;
+ }
+
+ /* Find the subsection. */
+ dws = &dw_sections[i];
+ subseg = NULL;
+ if (opt_label != NULL && S_IS_DEFINED (opt_label))
+ {
+ /* Sanity check (note that in theory S_GET_SEGMENT mustn't be null). */
+ if (dws->sect == NULL || S_GET_SEGMENT (opt_label) != dws->sect)
+ {
+ as_bad (_("label %s was not defined in this dwarf section"),
+ S_GET_NAME (opt_label));
+ subseg = dws->anon_subseg;
+ opt_label = NULL;
+ }
+ else
+ subseg = symbol_get_tc (opt_label)->u.dw;
+ }
+
+ if (subseg != NULL)
+ {
+ /* Switch to the subsection. */
+ ppc_change_debug_section (i, subseg->subseg);
+ }
+ else
+ {
+ /* Create a new dw subsection. */
+ subseg = (struct dw_subsection *)
+ xmalloc (sizeof (struct dw_subsection));
+
+ if (opt_label == NULL)
+ {
+ /* The anonymous one. */
+ subseg->subseg = 0;
+ subseg->link = NULL;
+ dws->anon_subseg = subseg;
+ }
+ else
+ {
+ /* A named one. */
+ if (dws->list_subseg != NULL)
+ subseg->subseg = dws->list_subseg->subseg + 1;
+ else
+ subseg->subseg = 1;
+
+ subseg->link = dws->list_subseg;
+ dws->list_subseg = subseg;
+ symbol_get_tc (opt_label)->u.dw = subseg;
+ }
+
+ ppc_change_debug_section (i, subseg->subseg);
+
+ if (dw->def_size)
+ {
+ /* Add the length field. */
+ expressionS *exp = &subseg->end_exp;
+ int sz;
+
+ if (opt_label != NULL)
+ symbol_set_value_now (opt_label);
+
+ /* Add the length field. Note that according to the AIX assembler
+ manual, the size of the length field is 4 for powerpc32 but
+ 12 for powerpc64. */
+ if (ppc_obj64)
+ {
+ /* Write the 64bit marker. */
+ md_number_to_chars (frag_more (4), -1, 4);
+ }
+
+ exp->X_op = O_subtract;
+ exp->X_op_symbol = symbol_temp_new_now ();
+ exp->X_add_symbol = symbol_temp_make ();
+
+ sz = ppc_obj64 ? 8 : 4;
+ exp->X_add_number = -sz;
+ emit_expr (exp, sz);
+ }
+ }
+}
+
+/* This function handles the .text and .data pseudo-ops. These
+ pseudo-ops aren't really used by XCOFF; we implement them for the
+ convenience of people who aren't used to XCOFF. */
+
+static void
+ppc_section (int type)
+{
+ const char *name;
+ symbolS *sym;
+
+ if (type == 't')
+ name = ".text[PR]";
+ else if (type == 'd')
+ name = ".data[RW]";
+ else
+ abort ();
+
+ sym = symbol_find_or_make (name);
+
+ ppc_change_csect (sym, 2);
+
+ demand_empty_rest_of_line ();
+}
+
+/* This function handles the .section pseudo-op. This is mostly to
+ give an error, since XCOFF only supports .text, .data and .bss, but
+ we do permit the user to name the text or data section. */
+
+static void
+ppc_named_section (int ignore ATTRIBUTE_UNUSED)
+{
+ char *user_name;
+ const char *real_name;
+ char c;
+ symbolS *sym;
+
+ user_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ if (strcmp (user_name, ".text") == 0)
+ real_name = ".text[PR]";
+ else if (strcmp (user_name, ".data") == 0)
+ real_name = ".data[RW]";
+ else
+ {
+ as_bad (_("the XCOFF file format does not support arbitrary sections"));
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *input_line_pointer = c;
+
+ sym = symbol_find_or_make (real_name);
+
+ ppc_change_csect (sym, 2);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .extern pseudo-op. We create an undefined symbol. */
+
+static void
+ppc_extern (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ (void) symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .lglobl pseudo-op. Keep the symbol in the symbol table. */
+
+static void
+ppc_lglobl (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ symbol_get_tc (sym)->output = 1;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ref pseudo-op. It takes a list of symbol names and inserts R_REF
+ relocations at the beginning of the current csect.
+
+ (In principle, there's no reason why the relocations _have_ to be at
+ the beginning. Anywhere in the csect would do. However, inserting
+ at the beginning is what the native assmebler does, and it helps to
+ deal with cases where the .ref statements follow the section contents.)
+
+ ??? .refs don't work for empty .csects. However, the native assembler
+ doesn't report an error in this case, and neither yet do we. */
+
+static void
+ppc_ref (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+
+ if (ppc_current_csect == NULL)
+ {
+ as_bad (_(".ref outside .csect"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ fix_at_start (symbol_get_frag (ppc_current_csect), 0,
+ symbol_find_or_make (name), 0, FALSE, BFD_RELOC_NONE);
+
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ as_bad (_("missing symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .rename pseudo-op. The RS/6000 assembler can rename symbols,
+ although I don't know why it bothers. */
+
+static void
+ppc_rename (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *sym;
+ int len;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing rename string"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ symbol_get_tc (sym)->real_name = demand_copy_C_string (&len);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .stabx pseudo-op. This is similar to a normal .stabs
+ pseudo-op, but slightly different. A sample is
+ .stabx "main:F-1",.main,142,0
+ The first argument is the symbol name to create. The second is the
+ value, and the third is the storage class. The fourth seems to be
+ always zero, and I am assuming it is the type. */
+
+static void
+ppc_stabx (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int len;
+ symbolS *sym;
+ expressionS exp;
+
+ name = demand_copy_C_string (&len);
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing value"));
+ return;
+ }
+ ++input_line_pointer;
+
+ ppc_stab_symbol = TRUE;
+ sym = symbol_make (name);
+ ppc_stab_symbol = FALSE;
+
+ symbol_get_tc (sym)->real_name = name;
+
+ (void) expression (&exp);
+
+ switch (exp.X_op)
+ {
+ case O_illegal:
+ case O_absent:
+ case O_big:
+ as_bad (_("illegal .stabx expression; zero assumed"));
+ exp.X_add_number = 0;
+ /* Fall through. */
+ case O_constant:
+ S_SET_VALUE (sym, (valueT) exp.X_add_number);
+ symbol_set_frag (sym, &zero_address_frag);
+ break;
+
+ case O_symbol:
+ if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section)
+ symbol_set_value_expression (sym, &exp);
+ else
+ {
+ S_SET_VALUE (sym,
+ exp.X_add_number + S_GET_VALUE (exp.X_add_symbol));
+ symbol_set_frag (sym, symbol_get_frag (exp.X_add_symbol));
+ }
+ break;
+
+ default:
+ /* The value is some complex expression. This will probably
+ fail at some later point, but this is probably the right
+ thing to do here. */
+ symbol_set_value_expression (sym, &exp);
+ break;
+ }
+
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing class"));
+ return;
+ }
+ ++input_line_pointer;
+
+ S_SET_STORAGE_CLASS (sym, get_absolute_expression ());
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing type"));
+ return;
+ }
+ ++input_line_pointer;
+
+ S_SET_DATA_TYPE (sym, get_absolute_expression ());
+
+ symbol_get_tc (sym)->output = 1;
+
+ if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+ {
+ /* In this case :
+
+ .bs name
+ .stabx "z",arrays_,133,0
+ .es
+
+ .comm arrays_,13768,3
+
+ resolve_symbol_value will copy the exp's "within" into sym's when the
+ offset is 0. Since this seems to be corner case problem,
+ only do the correction for storage class C_STSYM. A better solution
+ would be to have the tc field updated in ppc_symbol_new_hook. */
+
+ if (exp.X_op == O_symbol)
+ {
+ if (ppc_current_block == NULL)
+ as_bad (_(".stabx of storage class stsym must be within .bs/.es"));
+
+ symbol_get_tc (sym)->within = ppc_current_block;
+ symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block;
+ }
+ }
+
+ if (exp.X_op != O_symbol
+ || ! S_IS_EXTERNAL (exp.X_add_symbol)
+ || S_GET_SEGMENT (exp.X_add_symbol) != bss_section)
+ ppc_frob_label (sym);
+ else
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, exp.X_add_symbol, &symbol_rootP, &symbol_lastP);
+ if (symbol_get_tc (ppc_current_csect)->within == exp.X_add_symbol)
+ symbol_get_tc (ppc_current_csect)->within = sym;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .function pseudo-op. This takes several arguments. The first
+ argument seems to be the external name of the symbol. The second
+ argument seems to be the label for the start of the function. gcc
+ uses the same name for both. I have no idea what the third and
+ fourth arguments are meant to be. The optional fifth argument is
+ an expression for the size of the function. In COFF this symbol
+ gets an aux entry like that used for a csect. */
+
+static void
+ppc_function (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ char *s;
+ symbolS *ext_sym;
+ symbolS *lab_sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ /* Ignore any [PR] suffix. */
+ name = ppc_canonicalize_symbol_name (name);
+ s = strchr (name, '[');
+ if (s != (char *) NULL
+ && strcmp (s + 1, "PR]") == 0)
+ *s = '\0';
+
+ ext_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ lab_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (ext_sym != lab_sym)
+ {
+ expressionS exp;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = lab_sym;
+ exp.X_op_symbol = NULL;
+ exp.X_add_number = 0;
+ exp.X_unsigned = 0;
+ symbol_set_value_expression (ext_sym, &exp);
+ }
+
+ if (symbol_get_tc (ext_sym)->symbol_class == -1)
+ symbol_get_tc (ext_sym)->symbol_class = XMC_PR;
+ symbol_get_tc (ext_sym)->output = 1;
+
+ if (*input_line_pointer == ',')
+ {
+ expressionS exp;
+
+ /* Ignore the third argument. */
+ ++input_line_pointer;
+ expression (& exp);
+ if (*input_line_pointer == ',')
+ {
+ /* Ignore the fourth argument. */
+ ++input_line_pointer;
+ expression (& exp);
+ if (*input_line_pointer == ',')
+ {
+ /* The fifth argument is the function size. */
+ ++input_line_pointer;
+ symbol_get_tc (ext_sym)->u.size = symbol_new
+ ("L0\001", absolute_section,(valueT) 0, &zero_address_frag);
+ pseudo_set (symbol_get_tc (ext_sym)->u.size);
+ }
+ }
+ }
+
+ S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
+ SF_SET_FUNCTION (ext_sym);
+ SF_SET_PROCESS (ext_sym);
+ coff_add_linesym (ext_sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bf pseudo-op. This is just like a COFF C_FCN symbol named
+ ".bf". If the pseudo op .bi was seen before .bf, patch the .bi sym
+ with the correct line number */
+
+static symbolS *saved_bi_sym = 0;
+
+static void
+ppc_bf (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".bf");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_FCN);
+
+ coff_line_base = get_absolute_expression ();
+
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, coff_line_base);
+
+ /* Line number for bi. */
+ if (saved_bi_sym)
+ {
+ S_SET_VALUE (saved_bi_sym, coff_n_line_nos);
+ saved_bi_sym = 0;
+ }
+
+
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ef pseudo-op. This is just like a COFF C_FCN symbol named
+ ".ef", except that the line number is absolute, not relative to the
+ most recent ".bf" symbol. */
+
+static void
+ppc_ef (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".ef");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_FCN);
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bi and .ei pseudo-ops. These take a string argument and
+ generates a C_BINCL or C_EINCL symbol, which goes at the start of
+ the symbol list. The value of .bi will be know when the next .bf
+ is encountered. */
+
+static void
+ppc_biei (int ei)
+{
+ static symbolS *last_biei;
+
+ char *name;
+ int len;
+ symbolS *sym;
+ symbolS *look;
+
+ name = demand_copy_C_string (&len);
+
+ /* The value of these symbols is actually file offset. Here we set
+ the value to the index into the line number entries. In
+ ppc_frob_symbols we set the fix_line field, which will cause BFD
+ to do the right thing. */
+
+ sym = symbol_make (name);
+ /* obj-coff.c currently only handles line numbers correctly in the
+ .text section. */
+ S_SET_SEGMENT (sym, text_section);
+ S_SET_VALUE (sym, coff_n_line_nos);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+
+ S_SET_STORAGE_CLASS (sym, ei ? C_EINCL : C_BINCL);
+ symbol_get_tc (sym)->output = 1;
+
+ /* Save bi. */
+ if (ei)
+ saved_bi_sym = 0;
+ else
+ saved_bi_sym = sym;
+
+ for (look = last_biei ? last_biei : symbol_rootP;
+ (look != (symbolS *) NULL
+ && (S_GET_STORAGE_CLASS (look) == C_FILE
+ || S_GET_STORAGE_CLASS (look) == C_BINCL
+ || S_GET_STORAGE_CLASS (look) == C_EINCL));
+ look = symbol_next (look))
+ ;
+ if (look != (symbolS *) NULL)
+ {
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_insert (sym, look, &symbol_rootP, &symbol_lastP);
+ last_biei = sym;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bs pseudo-op. This generates a C_BSTAT symbol named ".bs".
+ There is one argument, which is a csect symbol. The value of the
+ .bs symbol is the index of this csect symbol. */
+
+static void
+ppc_bs (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *csect;
+ symbolS *sym;
+
+ if (ppc_current_block != NULL)
+ as_bad (_("nested .bs blocks"));
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ csect = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ sym = symbol_make (".bs");
+ S_SET_SEGMENT (sym, now_seg);
+ S_SET_STORAGE_CLASS (sym, C_BSTAT);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ symbol_get_tc (sym)->output = 1;
+
+ symbol_get_tc (sym)->within = csect;
+
+ ppc_frob_label (sym);
+
+ ppc_current_block = sym;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .es pseudo-op. Generate a C_ESTART symbol named .es. */
+
+static void
+ppc_es (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ if (ppc_current_block == NULL)
+ as_bad (_(".es without preceding .bs"));
+
+ sym = symbol_make (".es");
+ S_SET_SEGMENT (sym, now_seg);
+ S_SET_STORAGE_CLASS (sym, C_ESTAT);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ ppc_current_block = NULL;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bb pseudo-op. Generate a C_BLOCK symbol named .bb, with a
+ line number. */
+
+static void
+ppc_bb (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".bb");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_BLOCK);
+
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+
+ symbol_get_tc (sym)->output = 1;
+
+ SF_SET_PROCESS (sym);
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .eb pseudo-op. Generate a C_BLOCK symbol named .eb, with a
+ line number. */
+
+static void
+ppc_eb (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".eb");
+ S_SET_SEGMENT (sym, text_section);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, frag_now_fix ());
+ S_SET_STORAGE_CLASS (sym, C_BLOCK);
+ S_SET_NUMBER_AUXILIARY (sym, 1);
+ SA_SET_SYM_LNNO (sym, get_absolute_expression ());
+ symbol_get_tc (sym)->output = 1;
+
+ SF_SET_PROCESS (sym);
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .bc pseudo-op. This just creates a C_BCOMM symbol with a
+ specified name. */
+
+static void
+ppc_bc (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int len;
+ symbolS *sym;
+
+ name = demand_copy_C_string (&len);
+ sym = symbol_make (name);
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ S_SET_STORAGE_CLASS (sym, C_BCOMM);
+ S_SET_VALUE (sym, 0);
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ec pseudo-op. This just creates a C_ECOMM symbol. */
+
+static void
+ppc_ec (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *sym;
+
+ sym = symbol_make (".ec");
+ S_SET_SEGMENT (sym, ppc_coff_debug_section);
+ symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING;
+ S_SET_STORAGE_CLASS (sym, C_ECOMM);
+ S_SET_VALUE (sym, 0);
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .toc pseudo-op. Switch to the .toc subsegment. */
+
+static void
+ppc_toc (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ppc_toc_csect != (symbolS *) NULL)
+ subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg);
+ else
+ {
+ subsegT subseg;
+ symbolS *sym;
+ symbolS *list;
+
+ subseg = ppc_data_subsegment;
+ ++ppc_data_subsegment;
+
+ subseg_new (segment_name (data_section), subseg);
+ ppc_toc_frag = frag_now;
+
+ sym = symbol_find_or_make ("TOC[TC0]");
+ symbol_set_frag (sym, frag_now);
+ S_SET_SEGMENT (sym, data_section);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+ symbol_get_tc (sym)->subseg = subseg;
+ symbol_get_tc (sym)->output = 1;
+ symbol_get_tc (sym)->within = sym;
+
+ ppc_toc_csect = sym;
+
+ for (list = ppc_data_csects;
+ symbol_get_tc (list)->next != (symbolS *) NULL;
+ list = symbol_get_tc (list)->next)
+ ;
+ symbol_get_tc (list)->next = sym;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP,
+ &symbol_lastP);
+ }
+
+ ppc_current_csect = ppc_toc_csect;
+
+ demand_empty_rest_of_line ();
+}
+
+/* The AIX assembler automatically aligns the operands of a .long or
+ .short pseudo-op, and we want to be compatible. */
+
+static void
+ppc_xcoff_cons (int log_size)
+{
+ frag_align (log_size, 0, 0);
+ record_alignment (now_seg, log_size);
+ cons (1 << log_size);
+}
+
+static void
+ppc_vbyte (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ int byte_count;
+
+ (void) expression (&exp);
+
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("non-constant byte count"));
+ return;
+ }
+
+ byte_count = exp.X_add_number;
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("missing value"));
+ return;
+ }
+
+ ++input_line_pointer;
+ cons (byte_count);
+}
+
+void
+ppc_xcoff_end (void)
+{
+ int i;
+
+ for (i = 0; i < XCOFF_DWSECT_NBR_NAMES; i++)
+ {
+ struct dw_section *dws = &dw_sections[i];
+ struct dw_subsection *dwss;
+
+ if (dws->anon_subseg)
+ {
+ dwss = dws->anon_subseg;
+ dwss->link = dws->list_subseg;
+ }
+ else
+ dwss = dws->list_subseg;
+
+ for (; dwss != NULL; dwss = dwss->link)
+ if (dwss->end_exp.X_add_symbol != NULL)
+ {
+ subseg_set (dws->sect, dwss->subseg);
+ symbol_set_value_now (dwss->end_exp.X_add_symbol);
+ }
+ }
+}
+
+#endif /* OBJ_XCOFF */
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+
+/* The .tc pseudo-op. This is used when generating either XCOFF or
+ ELF. This takes two or more arguments.
+
+ When generating XCOFF output, the first argument is the name to
+ give to this location in the toc; this will be a symbol with class
+ TC. The rest of the arguments are N-byte values to actually put at
+ this location in the TOC; often there is just one more argument, a
+ relocatable symbol reference. The size of the value to store
+ depends on target word size. A 32-bit target uses 4-byte values, a
+ 64-bit target uses 8-byte values.
+
+ When not generating XCOFF output, the arguments are the same, but
+ the first argument is simply ignored. */
+
+static void
+ppc_tc (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_XCOFF
+
+ /* Define the TOC symbol name. */
+ {
+ char *name;
+ char endc;
+ symbolS *sym;
+
+ if (ppc_toc_csect == (symbolS *) NULL
+ || ppc_toc_csect != ppc_current_csect)
+ {
+ as_bad (_(".tc not in .toc section"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ if (S_IS_DEFINED (sym))
+ {
+ symbolS *label;
+
+ label = symbol_get_tc (ppc_current_csect)->within;
+ if (symbol_get_tc (label)->symbol_class != XMC_TC0)
+ {
+ as_bad (_(".tc with no label"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ S_SET_SEGMENT (label, S_GET_SEGMENT (sym));
+ symbol_set_frag (label, symbol_get_frag (sym));
+ S_SET_VALUE (label, S_GET_VALUE (sym));
+
+ while (! is_end_of_line[(unsigned char) *input_line_pointer])
+ ++input_line_pointer;
+
+ return;
+ }
+
+ S_SET_SEGMENT (sym, now_seg);
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+ symbol_get_tc (sym)->symbol_class = XMC_TC;
+ symbol_get_tc (sym)->output = 1;
+
+ ppc_frob_label (sym);
+ }
+
+#endif /* OBJ_XCOFF */
+#ifdef OBJ_ELF
+ int align;
+
+ /* Skip the TOC symbol name. */
+ while (is_part_of_name (*input_line_pointer)
+ || *input_line_pointer == ' '
+ || *input_line_pointer == '['
+ || *input_line_pointer == ']'
+ || *input_line_pointer == '{'
+ || *input_line_pointer == '}')
+ ++input_line_pointer;
+
+ /* Align to a four/eight byte boundary. */
+ align = ppc_obj64 ? 3 : 2;
+ frag_align (align, 0, 0);
+ record_alignment (now_seg, align);
+#endif /* OBJ_ELF */
+
+ if (*input_line_pointer != ',')
+ demand_empty_rest_of_line ();
+ else
+ {
+ ++input_line_pointer;
+ cons (ppc_obj64 ? 8 : 4);
+ }
+}
+
+/* Pseudo-op .machine. */
+
+static void
+ppc_machine (int ignore ATTRIBUTE_UNUSED)
+{
+ char *cpu_string;
+#define MAX_HISTORY 100
+ static ppc_cpu_t *cpu_history;
+ static int curr_hist;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ int len;
+ cpu_string = demand_copy_C_string (&len);
+ }
+ else
+ {
+ char c;
+ cpu_string = input_line_pointer;
+ c = get_symbol_end ();
+ cpu_string = xstrdup (cpu_string);
+ *input_line_pointer = c;
+ }
+
+ if (cpu_string != NULL)
+ {
+ ppc_cpu_t old_cpu = ppc_cpu;
+ ppc_cpu_t new_cpu;
+ char *p;
+
+ for (p = cpu_string; *p != 0; p++)
+ *p = TOLOWER (*p);
+
+ if (strcmp (cpu_string, "push") == 0)
+ {
+ if (cpu_history == NULL)
+ cpu_history = xmalloc (MAX_HISTORY * sizeof (*cpu_history));
+
+ if (curr_hist >= MAX_HISTORY)
+ as_bad (_(".machine stack overflow"));
+ else
+ cpu_history[curr_hist++] = ppc_cpu;
+ }
+ else if (strcmp (cpu_string, "pop") == 0)
+ {
+ if (curr_hist <= 0)
+ as_bad (_(".machine stack underflow"));
+ else
+ ppc_cpu = cpu_history[--curr_hist];
+ }
+ else if ((new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, cpu_string)) != 0)
+ ppc_cpu = new_cpu;
+ else
+ as_bad (_("invalid machine `%s'"), cpu_string);
+
+ if (ppc_cpu != old_cpu)
+ ppc_setup_opcodes ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+
+#ifdef TE_PE
+
+/* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format. */
+
+/* Set the current section. */
+static void
+ppc_set_current_section (segT new)
+{
+ ppc_previous_section = ppc_current_section;
+ ppc_current_section = new;
+}
+
+/* pseudo-op: .previous
+ behaviour: toggles the current section with the previous section.
+ errors: None
+ warnings: "No previous section" */
+
+static void
+ppc_previous (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ppc_previous_section == NULL)
+ {
+ as_warn (_("no previous section to return to, ignored."));
+ return;
+ }
+
+ subseg_set (ppc_previous_section, 0);
+
+ ppc_set_current_section (ppc_previous_section);
+}
+
+/* pseudo-op: .pdata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .pdata "adr3"
+ a - don't know -- maybe a misprint
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+
+ commentary:
+ Tag index tables (also known as the function table) for exception
+ handling, debugging, etc. */
+
+static void
+ppc_pdata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (pdata_section == 0)
+ {
+ pdata_section = subseg_new (".pdata", 0);
+
+ bfd_set_section_flags (stdoutput, pdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, pdata_section, 2);
+ }
+ else
+ {
+ pdata_section = subseg_new (".pdata", 0);
+ }
+ ppc_set_current_section (pdata_section);
+}
+
+/* pseudo-op: .ydata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .ydata "drw3"
+ a - don't know -- maybe a misprint
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary)
+ commentary:
+ Tag tables (also known as the scope table) for exception handling,
+ debugging, etc. */
+
+static void
+ppc_ydata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (ydata_section == 0)
+ {
+ ydata_section = subseg_new (".ydata", 0);
+ bfd_set_section_flags (stdoutput, ydata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, ydata_section, 3);
+ }
+ else
+ {
+ ydata_section = subseg_new (".ydata", 0);
+ }
+ ppc_set_current_section (ydata_section);
+}
+
+/* pseudo-op: .reldata
+ behaviour: predefined read write data section
+ double word aligned (4-byte)
+ FIXME: relocation is applied to it
+ FIXME: what's the difference between this and .data?
+ errors: None
+ warnings: None
+ initial: .section .reldata "drw3"
+ d - initialized data
+ r - readable
+ w - writeable
+ 3 - double word aligned (that would be 8 byte boundary)
+
+ commentary:
+ Like .data, but intended to hold data subject to relocation, such as
+ function descriptors, etc. */
+
+static void
+ppc_reldata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (reldata_section == 0)
+ {
+ reldata_section = subseg_new (".reldata", 0);
+
+ bfd_set_section_flags (stdoutput, reldata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_DATA));
+
+ bfd_set_section_alignment (stdoutput, reldata_section, 2);
+ }
+ else
+ {
+ reldata_section = subseg_new (".reldata", 0);
+ }
+ ppc_set_current_section (reldata_section);
+}
+
+/* pseudo-op: .rdata
+ behaviour: predefined read only data section
+ double word aligned
+ errors: None
+ warnings: None
+ initial: .section .rdata "dr3"
+ d - initialized data
+ r - readable
+ 3 - double word aligned (that would be 4 byte boundary) */
+
+static void
+ppc_rdata (int ignore ATTRIBUTE_UNUSED)
+{
+ if (rdata_section == 0)
+ {
+ rdata_section = subseg_new (".rdata", 0);
+ bfd_set_section_flags (stdoutput, rdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA ));
+
+ bfd_set_section_alignment (stdoutput, rdata_section, 2);
+ }
+ else
+ {
+ rdata_section = subseg_new (".rdata", 0);
+ }
+ ppc_set_current_section (rdata_section);
+}
+
+/* pseudo-op: .ualong
+ behaviour: much like .int, with the exception that no alignment is
+ performed.
+ FIXME: test the alignment statement
+ errors: None
+ warnings: None */
+
+static void
+ppc_ualong (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Try for long. */
+ cons (4);
+}
+
+/* pseudo-op: .znop <symbol name>
+ behaviour: Issue a nop instruction
+ Issue a IMAGE_REL_PPC_IFGLUE relocation against it, using
+ the supplied symbol name.
+ errors: None
+ warnings: Missing symbol name */
+
+static void
+ppc_znop (int ignore ATTRIBUTE_UNUSED)
+{
+ unsigned long insn;
+ const struct powerpc_opcode *opcode;
+ char *f;
+ symbolS *sym;
+ char *symbol_name;
+ char c;
+ char *name;
+
+ /* Strip out the symbol name. */
+ symbol_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - symbol_name + 1);
+ strcpy (name, symbol_name);
+
+ sym = symbol_find_or_make (name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ /* Look up the opcode in the hash table. */
+ opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop");
+
+ /* Stick in the nop. */
+ insn = opcode->opcode;
+
+ /* Write out the instruction. */
+ f = frag_more (4);
+ md_number_to_chars (f, insn, 4);
+ fix_new (frag_now,
+ f - frag_now->fr_literal,
+ 4,
+ sym,
+ 0,
+ 0,
+ BFD_RELOC_16_GOT_PCREL);
+
+}
+
+/* pseudo-op:
+ behaviour:
+ errors:
+ warnings: */
+
+static void
+ppc_pe_comm (int lcomm)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT temp;
+ symbolS *symbolP;
+ offsetT align;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol-name: rest of line ignored."));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++; /* skip ',' */
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (! lcomm)
+ {
+ /* The third argument to .comm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 3;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("ignoring bad alignment"));
+ align = 3;
+ }
+ }
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("ignoring attempt to re-define symbol `%s'."),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP))
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) temp)
+ as_bad (_("length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP),
+ (long) S_GET_VALUE (symbolP),
+ (long) temp);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) temp);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/*
+ * implement the .section pseudo op:
+ * .section name {, "flags"}
+ * ^ ^
+ * | +--- optional flags: 'b' for bss
+ * | 'i' for info
+ * +-- section name 'l' for lib
+ * 'n' for noload
+ * 'o' for over
+ * 'w' for data
+ * 'd' (apparently m88k for data)
+ * 'x' for text
+ * But if the argument is not a quoted string, treat it as a
+ * subsegment number.
+ *
+ * FIXME: this is a copy of the section processing from obj-coff.c, with
+ * additions/changes for the moto-pas assembler support. There are three
+ * categories:
+ *
+ * FIXME: I just noticed this. This doesn't work at all really. It it
+ * setting bits that bfd probably neither understands or uses. The
+ * correct approach (?) will have to incorporate extra fields attached
+ * to the section to hold the system specific stuff. (krk)
+ *
+ * Section Contents:
+ * 'a' - unknown - referred to in documentation, but no definition supplied
+ * 'c' - section has code
+ * 'd' - section has initialized data
+ * 'u' - section has uninitialized data
+ * 'i' - section contains directives (info)
+ * 'n' - section can be discarded
+ * 'R' - remove section at link time
+ *
+ * Section Protection:
+ * 'r' - section is readable
+ * 'w' - section is writeable
+ * 'x' - section is executable
+ * 's' - section is sharable
+ *
+ * Section Alignment:
+ * '0' - align to byte boundary
+ * '1' - align to halfword undary
+ * '2' - align to word boundary
+ * '3' - align to doubleword boundary
+ * '4' - align to quadword boundary
+ * '5' - align to 32 byte boundary
+ * '6' - align to 64 byte boundary
+ *
+ */
+
+void
+ppc_pe_section (int ignore ATTRIBUTE_UNUSED)
+{
+ /* Strip out the section name. */
+ char *section_name;
+ char c;
+ char *name;
+ unsigned int exp;
+ flagword flags;
+ segT sec;
+ int align;
+
+ section_name = input_line_pointer;
+ c = get_symbol_end ();
+
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ *input_line_pointer = c;
+
+ SKIP_WHITESPACE ();
+
+ exp = 0;
+ flags = SEC_NO_FLAGS;
+
+ if (strcmp (name, ".idata$2") == 0)
+ {
+ align = 0;
+ }
+ else if (strcmp (name, ".idata$3") == 0)
+ {
+ align = 0;
+ }
+ else if (strcmp (name, ".idata$4") == 0)
+ {
+ align = 2;
+ }
+ else if (strcmp (name, ".idata$5") == 0)
+ {
+ align = 2;
+ }
+ else if (strcmp (name, ".idata$6") == 0)
+ {
+ align = 1;
+ }
+ else
+ /* Default alignment to 16 byte boundary. */
+ align = 4;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ exp = get_absolute_expression ();
+ else
+ {
+ ++input_line_pointer;
+ while (*input_line_pointer != '"'
+ && ! is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ /* Section Contents */
+ case 'a': /* unknown */
+ as_bad (_("unsupported section attribute -- 'a'"));
+ break;
+ case 'c': /* code section */
+ flags |= SEC_CODE;
+ break;
+ case 'd': /* section has initialized data */
+ flags |= SEC_DATA;
+ break;
+ case 'u': /* section has uninitialized data */
+ /* FIXME: This is IMAGE_SCN_CNT_UNINITIALIZED_DATA
+ in winnt.h */
+ flags |= SEC_ROM;
+ break;
+ case 'i': /* section contains directives (info) */
+ /* FIXME: This is IMAGE_SCN_LNK_INFO
+ in winnt.h */
+ flags |= SEC_HAS_CONTENTS;
+ break;
+ case 'n': /* section can be discarded */
+ flags &=~ SEC_LOAD;
+ break;
+ case 'R': /* Remove section at link time */
+ flags |= SEC_NEVER_LOAD;
+ break;
+#if IFLICT_BRAIN_DAMAGE
+ /* Section Protection */
+ case 'r': /* section is readable */
+ flags |= IMAGE_SCN_MEM_READ;
+ break;
+ case 'w': /* section is writeable */
+ flags |= IMAGE_SCN_MEM_WRITE;
+ break;
+ case 'x': /* section is executable */
+ flags |= IMAGE_SCN_MEM_EXECUTE;
+ break;
+ case 's': /* section is sharable */
+ flags |= IMAGE_SCN_MEM_SHARED;
+ break;
+
+ /* Section Alignment */
+ case '0': /* align to byte boundary */
+ flags |= IMAGE_SCN_ALIGN_1BYTES;
+ align = 0;
+ break;
+ case '1': /* align to halfword boundary */
+ flags |= IMAGE_SCN_ALIGN_2BYTES;
+ align = 1;
+ break;
+ case '2': /* align to word boundary */
+ flags |= IMAGE_SCN_ALIGN_4BYTES;
+ align = 2;
+ break;
+ case '3': /* align to doubleword boundary */
+ flags |= IMAGE_SCN_ALIGN_8BYTES;
+ align = 3;
+ break;
+ case '4': /* align to quadword boundary */
+ flags |= IMAGE_SCN_ALIGN_16BYTES;
+ align = 4;
+ break;
+ case '5': /* align to 32 byte boundary */
+ flags |= IMAGE_SCN_ALIGN_32BYTES;
+ align = 5;
+ break;
+ case '6': /* align to 64 byte boundary */
+ flags |= IMAGE_SCN_ALIGN_64BYTES;
+ align = 6;
+ break;
+#endif
+ default:
+ as_bad (_("unknown section attribute '%c'"),
+ *input_line_pointer);
+ break;
+ }
+ ++input_line_pointer;
+ }
+ if (*input_line_pointer == '"')
+ ++input_line_pointer;
+ }
+ }
+
+ sec = subseg_new (name, (subsegT) exp);
+
+ ppc_set_current_section (sec);
+
+ if (flags != SEC_NO_FLAGS)
+ {
+ if (! bfd_set_section_flags (stdoutput, sec, flags))
+ as_bad (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, sec),
+ bfd_errmsg (bfd_get_error ()));
+ }
+
+ bfd_set_section_alignment (stdoutput, sec, align);
+}
+
+static void
+ppc_pe_function (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char endc;
+ symbolS *ext_sym;
+
+ name = input_line_pointer;
+ endc = get_symbol_end ();
+
+ ext_sym = symbol_find_or_make (name);
+
+ *input_line_pointer = endc;
+
+ S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
+ SF_SET_FUNCTION (ext_sym);
+ SF_SET_PROCESS (ext_sym);
+ coff_add_linesym (ext_sym);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+ppc_pe_tocd (int ignore ATTRIBUTE_UNUSED)
+{
+ if (tocdata_section == 0)
+ {
+ tocdata_section = subseg_new (".tocd", 0);
+ /* FIXME: section flags won't work. */
+ bfd_set_section_flags (stdoutput, tocdata_section,
+ (SEC_ALLOC | SEC_LOAD | SEC_RELOC
+ | SEC_READONLY | SEC_DATA));
+
+ bfd_set_section_alignment (stdoutput, tocdata_section, 2);
+ }
+ else
+ {
+ rdata_section = subseg_new (".tocd", 0);
+ }
+
+ ppc_set_current_section (tocdata_section);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Don't adjust TOC relocs to use the section symbol. */
+
+int
+ppc_pe_fix_adjustable (fixS *fix)
+{
+ return fix->fx_r_type != BFD_RELOC_PPC_TOC16;
+}
+
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* XCOFF specific symbol and file handling. */
+
+/* Canonicalize the symbol name. We use the to force the suffix, if
+ any, to use square brackets, and to be in upper case. */
+
+char *
+ppc_canonicalize_symbol_name (char *name)
+{
+ char *s;
+
+ if (ppc_stab_symbol)
+ return name;
+
+ for (s = name; *s != '\0' && *s != '{' && *s != '['; s++)
+ ;
+ if (*s != '\0')
+ {
+ char brac;
+
+ if (*s == '[')
+ brac = ']';
+ else
+ {
+ *s = '[';
+ brac = '}';
+ }
+
+ for (s++; *s != '\0' && *s != brac; s++)
+ *s = TOUPPER (*s);
+
+ if (*s == '\0' || s[1] != '\0')
+ as_bad (_("bad symbol suffix"));
+
+ *s = ']';
+ }
+
+ return name;
+}
+
+/* Set the class of a symbol based on the suffix, if any. This is
+ called whenever a new symbol is created. */
+
+void
+ppc_symbol_new_hook (symbolS *sym)
+{
+ struct ppc_tc_sy *tc;
+ const char *s;
+
+ tc = symbol_get_tc (sym);
+ tc->next = NULL;
+ tc->output = 0;
+ tc->symbol_class = -1;
+ tc->real_name = NULL;
+ tc->subseg = 0;
+ tc->align = 0;
+ tc->u.size = NULL;
+ tc->u.dw = NULL;
+ tc->within = NULL;
+
+ if (ppc_stab_symbol)
+ return;
+
+ s = strchr (S_GET_NAME (sym), '[');
+ if (s == (const char *) NULL)
+ {
+ /* There is no suffix. */
+ return;
+ }
+
+ ++s;
+
+ switch (s[0])
+ {
+ case 'B':
+ if (strcmp (s, "BS]") == 0)
+ tc->symbol_class = XMC_BS;
+ break;
+ case 'D':
+ if (strcmp (s, "DB]") == 0)
+ tc->symbol_class = XMC_DB;
+ else if (strcmp (s, "DS]") == 0)
+ tc->symbol_class = XMC_DS;
+ break;
+ case 'G':
+ if (strcmp (s, "GL]") == 0)
+ tc->symbol_class = XMC_GL;
+ break;
+ case 'P':
+ if (strcmp (s, "PR]") == 0)
+ tc->symbol_class = XMC_PR;
+ break;
+ case 'R':
+ if (strcmp (s, "RO]") == 0)
+ tc->symbol_class = XMC_RO;
+ else if (strcmp (s, "RW]") == 0)
+ tc->symbol_class = XMC_RW;
+ break;
+ case 'S':
+ if (strcmp (s, "SV]") == 0)
+ tc->symbol_class = XMC_SV;
+ break;
+ case 'T':
+ if (strcmp (s, "TC]") == 0)
+ tc->symbol_class = XMC_TC;
+ else if (strcmp (s, "TI]") == 0)
+ tc->symbol_class = XMC_TI;
+ else if (strcmp (s, "TB]") == 0)
+ tc->symbol_class = XMC_TB;
+ else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0)
+ tc->symbol_class = XMC_TC0;
+ break;
+ case 'U':
+ if (strcmp (s, "UA]") == 0)
+ tc->symbol_class = XMC_UA;
+ else if (strcmp (s, "UC]") == 0)
+ tc->symbol_class = XMC_UC;
+ break;
+ case 'X':
+ if (strcmp (s, "XO]") == 0)
+ tc->symbol_class = XMC_XO;
+ break;
+ }
+
+ if (tc->symbol_class == -1)
+ as_bad (_("unrecognized symbol suffix"));
+}
+
+/* Set the class of a label based on where it is defined. This
+ handles symbols without suffixes. Also, move the symbol so that it
+ follows the csect symbol. */
+
+void
+ppc_frob_label (symbolS *sym)
+{
+ if (ppc_current_csect != (symbolS *) NULL)
+ {
+ if (symbol_get_tc (sym)->symbol_class == -1)
+ symbol_get_tc (sym)->symbol_class = symbol_get_tc (ppc_current_csect)->symbol_class;
+
+ symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+ symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
+ &symbol_rootP, &symbol_lastP);
+ symbol_get_tc (ppc_current_csect)->within = sym;
+ symbol_get_tc (sym)->within = ppc_current_csect;
+ }
+
+#ifdef OBJ_ELF
+ dwarf2_emit_label (sym);
+#endif
+}
+
+/* This variable is set by ppc_frob_symbol if any absolute symbols are
+ seen. It tells ppc_adjust_symtab whether it needs to look through
+ the symbols. */
+
+static bfd_boolean ppc_saw_abs;
+
+/* Change the name of a symbol just before writing it out. Set the
+ real name if the .rename pseudo-op was used. Otherwise, remove any
+ class suffix. Return 1 if the symbol should not be included in the
+ symbol table. */
+
+int
+ppc_frob_symbol (symbolS *sym)
+{
+ static symbolS *ppc_last_function;
+ static symbolS *set_end;
+
+ /* Discard symbols that should not be included in the output symbol
+ table. */
+ if (! symbol_used_in_reloc_p (sym)
+ && ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0
+ || (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
+ && ! symbol_get_tc (sym)->output
+ && S_GET_STORAGE_CLASS (sym) != C_FILE)))
+ return 1;
+
+ /* This one will disappear anyway. Don't make a csect sym for it. */
+ if (sym == abs_section_sym)
+ return 1;
+
+ if (symbol_get_tc (sym)->real_name != (char *) NULL)
+ S_SET_NAME (sym, symbol_get_tc (sym)->real_name);
+ else
+ {
+ const char *name;
+ const char *s;
+
+ name = S_GET_NAME (sym);
+ s = strchr (name, '[');
+ if (s != (char *) NULL)
+ {
+ unsigned int len;
+ char *snew;
+
+ len = s - name;
+ snew = xmalloc (len + 1);
+ memcpy (snew, name, len);
+ snew[len] = '\0';
+
+ S_SET_NAME (sym, snew);
+ }
+ }
+
+ if (set_end != (symbolS *) NULL)
+ {
+ SA_SET_SYM_ENDNDX (set_end, sym);
+ set_end = NULL;
+ }
+
+ if (SF_GET_FUNCTION (sym))
+ {
+ if (ppc_last_function != (symbolS *) NULL)
+ as_bad (_("two .function pseudo-ops with no intervening .ef"));
+ ppc_last_function = sym;
+ if (symbol_get_tc (sym)->u.size != (symbolS *) NULL)
+ {
+ resolve_symbol_value (symbol_get_tc (sym)->u.size);
+ SA_SET_SYM_FSIZE (sym,
+ (long) S_GET_VALUE (symbol_get_tc (sym)->u.size));
+ }
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_FCN
+ && strcmp (S_GET_NAME (sym), ".ef") == 0)
+ {
+ if (ppc_last_function == (symbolS *) NULL)
+ as_bad (_(".ef with no preceding .function"));
+ else
+ {
+ set_end = ppc_last_function;
+ ppc_last_function = NULL;
+
+ /* We don't have a C_EFCN symbol, but we need to force the
+ COFF backend to believe that it has seen one. */
+ coff_last_function = NULL;
+ }
+ }
+
+ if (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
+ && (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) == 0
+ && S_GET_STORAGE_CLASS (sym) != C_FILE
+ && S_GET_STORAGE_CLASS (sym) != C_FCN
+ && S_GET_STORAGE_CLASS (sym) != C_BLOCK
+ && S_GET_STORAGE_CLASS (sym) != C_BSTAT
+ && S_GET_STORAGE_CLASS (sym) != C_ESTAT
+ && S_GET_STORAGE_CLASS (sym) != C_BINCL
+ && S_GET_STORAGE_CLASS (sym) != C_EINCL
+ && S_GET_SEGMENT (sym) != ppc_coff_debug_section)
+ S_SET_STORAGE_CLASS (sym, C_HIDEXT);
+
+ if (S_GET_STORAGE_CLASS (sym) == C_EXT
+ || S_GET_STORAGE_CLASS (sym) == C_AIX_WEAKEXT
+ || S_GET_STORAGE_CLASS (sym) == C_HIDEXT)
+ {
+ int i;
+ union internal_auxent *a;
+
+ /* Create a csect aux. */
+ i = S_GET_NUMBER_AUXILIARY (sym);
+ S_SET_NUMBER_AUXILIARY (sym, i + 1);
+ a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].u.auxent;
+ if (symbol_get_tc (sym)->symbol_class == XMC_TC0)
+ {
+ /* This is the TOC table. */
+ know (strcmp (S_GET_NAME (sym), "TOC") == 0);
+ a->x_csect.x_scnlen.l = 0;
+ a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ }
+ else if (symbol_get_tc (sym)->subseg != 0)
+ {
+ /* This is a csect symbol. x_scnlen is the size of the
+ csect. */
+ if (symbol_get_tc (sym)->next == (symbolS *) NULL)
+ a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
+ S_GET_SEGMENT (sym))
+ - S_GET_VALUE (sym));
+ else
+ {
+ resolve_symbol_value (symbol_get_tc (sym)->next);
+ a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next)
+ - S_GET_VALUE (sym));
+ }
+ a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD;
+ }
+ else if (S_GET_SEGMENT (sym) == bss_section)
+ {
+ /* This is a common symbol. */
+ a->x_csect.x_scnlen.l = symbol_get_frag (sym)->fr_offset;
+ a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM;
+ if (S_IS_EXTERNAL (sym))
+ symbol_get_tc (sym)->symbol_class = XMC_RW;
+ else
+ symbol_get_tc (sym)->symbol_class = XMC_BS;
+ }
+ else if (S_GET_SEGMENT (sym) == absolute_section)
+ {
+ /* This is an absolute symbol. The csect will be created by
+ ppc_adjust_symtab. */
+ ppc_saw_abs = TRUE;
+ a->x_csect.x_smtyp = XTY_LD;
+ if (symbol_get_tc (sym)->symbol_class == -1)
+ symbol_get_tc (sym)->symbol_class = XMC_XO;
+ }
+ else if (! S_IS_DEFINED (sym))
+ {
+ /* This is an external symbol. */
+ a->x_csect.x_scnlen.l = 0;
+ a->x_csect.x_smtyp = XTY_ER;
+ }
+ else if (symbol_get_tc (sym)->symbol_class == XMC_TC)
+ {
+ symbolS *next;
+
+ /* This is a TOC definition. x_scnlen is the size of the
+ TOC entry. */
+ next = symbol_next (sym);
+ while (symbol_get_tc (next)->symbol_class == XMC_TC0)
+ next = symbol_next (next);
+ if (next == (symbolS *) NULL
+ || symbol_get_tc (next)->symbol_class != XMC_TC)
+ {
+ if (ppc_after_toc_frag == (fragS *) NULL)
+ a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
+ data_section)
+ - S_GET_VALUE (sym));
+ else
+ a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address
+ - S_GET_VALUE (sym));
+ }
+ else
+ {
+ resolve_symbol_value (next);
+ a->x_csect.x_scnlen.l = (S_GET_VALUE (next)
+ - S_GET_VALUE (sym));
+ }
+ a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+ }
+ else
+ {
+ symbolS *csect;
+
+ /* This is a normal symbol definition. x_scnlen is the
+ symbol index of the containing csect. */
+ if (S_GET_SEGMENT (sym) == text_section)
+ csect = ppc_text_csects;
+ else if (S_GET_SEGMENT (sym) == data_section)
+ csect = ppc_data_csects;
+ else
+ abort ();
+
+ /* Skip the initial dummy symbol. */
+ csect = symbol_get_tc (csect)->next;
+
+ if (csect == (symbolS *) NULL)
+ {
+ as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym));
+ a->x_csect.x_scnlen.l = 0;
+ }
+ else
+ {
+ while (symbol_get_tc (csect)->next != (symbolS *) NULL)
+ {
+ resolve_symbol_value (symbol_get_tc (csect)->next);
+ if (S_GET_VALUE (symbol_get_tc (csect)->next)
+ > S_GET_VALUE (sym))
+ break;
+ csect = symbol_get_tc (csect)->next;
+ }
+
+ a->x_csect.x_scnlen.p =
+ coffsymbol (symbol_get_bfdsym (csect))->native;
+ coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].fix_scnlen =
+ 1;
+ }
+ a->x_csect.x_smtyp = XTY_LD;
+ }
+
+ a->x_csect.x_parmhash = 0;
+ a->x_csect.x_snhash = 0;
+ if (symbol_get_tc (sym)->symbol_class == -1)
+ a->x_csect.x_smclas = XMC_PR;
+ else
+ a->x_csect.x_smclas = symbol_get_tc (sym)->symbol_class;
+ a->x_csect.x_stab = 0;
+ a->x_csect.x_snstab = 0;
+
+ /* Don't let the COFF backend resort these symbols. */
+ symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END;
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_BSTAT)
+ {
+ /* We want the value to be the symbol index of the referenced
+ csect symbol. BFD will do that for us if we set the right
+ flags. */
+ asymbol *bsym = symbol_get_bfdsym (symbol_get_tc (sym)->within);
+ combined_entry_type *c = coffsymbol (bsym)->native;
+
+ S_SET_VALUE (sym, (valueT) (size_t) c);
+ coffsymbol (symbol_get_bfdsym (sym))->native->fix_value = 1;
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+ {
+ symbolS *block;
+ valueT base;
+
+ block = symbol_get_tc (sym)->within;
+ if (block)
+ {
+ /* The value is the offset from the enclosing csect. */
+ symbolS *csect;
+
+ csect = symbol_get_tc (block)->within;
+ resolve_symbol_value (csect);
+ base = S_GET_VALUE (csect);
+ }
+ else
+ base = 0;
+
+ S_SET_VALUE (sym, S_GET_VALUE (sym) - base);
+ }
+ else if (S_GET_STORAGE_CLASS (sym) == C_BINCL
+ || S_GET_STORAGE_CLASS (sym) == C_EINCL)
+ {
+ /* We want the value to be a file offset into the line numbers.
+ BFD will do that for us if we set the right flags. We have
+ already set the value correctly. */
+ coffsymbol (symbol_get_bfdsym (sym))->native->fix_line = 1;
+ }
+
+ return 0;
+}
+
+/* Adjust the symbol table. This creates csect symbols for all
+ absolute symbols. */
+
+void
+ppc_adjust_symtab (void)
+{
+ symbolS *sym;
+
+ if (! ppc_saw_abs)
+ return;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ symbolS *csect;
+ int i;
+ union internal_auxent *a;
+
+ if (S_GET_SEGMENT (sym) != absolute_section)
+ continue;
+
+ csect = symbol_create (".abs[XO]", absolute_section,
+ S_GET_VALUE (sym), &zero_address_frag);
+ symbol_get_bfdsym (csect)->value = S_GET_VALUE (sym);
+ S_SET_STORAGE_CLASS (csect, C_HIDEXT);
+ i = S_GET_NUMBER_AUXILIARY (csect);
+ S_SET_NUMBER_AUXILIARY (csect, i + 1);
+ a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1].u.auxent;
+ a->x_csect.x_scnlen.l = 0;
+ a->x_csect.x_smtyp = XTY_SD;
+ a->x_csect.x_parmhash = 0;
+ a->x_csect.x_snhash = 0;
+ a->x_csect.x_smclas = XMC_XO;
+ a->x_csect.x_stab = 0;
+ a->x_csect.x_snstab = 0;
+
+ symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP);
+
+ i = S_GET_NUMBER_AUXILIARY (sym);
+ a = &coffsymbol (symbol_get_bfdsym (sym))->native[i].u.auxent;
+ a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native;
+ coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1;
+ }
+
+ ppc_saw_abs = FALSE;
+}
+
+/* Set the VMA for a section. This is called on all the sections in
+ turn. */
+
+void
+ppc_frob_section (asection *sec)
+{
+ static bfd_vma vma = 0;
+
+ /* Dwarf sections start at 0. */
+ if (bfd_get_section_flags (NULL, sec) & SEC_DEBUGGING)
+ return;
+
+ vma = md_section_align (sec, vma);
+ bfd_set_section_vma (stdoutput, sec, vma);
+ vma += bfd_section_size (stdoutput, sec);
+}
+
+#endif /* OBJ_XCOFF */
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ return ieee_md_atof (type, litp, sizep, target_big_endian);
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Align a section (I don't know why this is machine dependent). */
+
+valueT
+md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT addr)
+{
+#ifdef OBJ_ELF
+ return addr;
+#else
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+#endif
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* 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 ATTRIBUTE_UNUSED)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+#ifdef OBJ_XCOFF
+
+/* This is called to see whether a fixup should be adjusted to use a
+ section symbol. We take the opportunity to change a fixup against
+ a symbol in the TOC subsegment into a reloc against the
+ corresponding .tc symbol. */
+
+int
+ppc_fix_adjustable (fixS *fix)
+{
+ valueT val = resolve_symbol_value (fix->fx_addsy);
+ segT symseg = S_GET_SEGMENT (fix->fx_addsy);
+ TC_SYMFIELD_TYPE *tc;
+
+ if (symseg == absolute_section)
+ return 0;
+
+ /* Always adjust symbols in debugging sections. */
+ if (bfd_get_section_flags (stdoutput, symseg) & SEC_DEBUGGING)
+ return 1;
+
+ if (ppc_toc_csect != (symbolS *) NULL
+ && fix->fx_addsy != ppc_toc_csect
+ && symseg == data_section
+ && val >= ppc_toc_frag->fr_address
+ && (ppc_after_toc_frag == (fragS *) NULL
+ || val < ppc_after_toc_frag->fr_address))
+ {
+ symbolS *sy;
+
+ for (sy = symbol_next (ppc_toc_csect);
+ sy != (symbolS *) NULL;
+ sy = symbol_next (sy))
+ {
+ TC_SYMFIELD_TYPE *sy_tc = symbol_get_tc (sy);
+
+ if (sy_tc->symbol_class == XMC_TC0)
+ continue;
+ if (sy_tc->symbol_class != XMC_TC)
+ break;
+ if (val == resolve_symbol_value (sy))
+ {
+ fix->fx_addsy = sy;
+ fix->fx_addnumber = val - ppc_toc_frag->fr_address;
+ return 0;
+ }
+ }
+
+ as_bad_where (fix->fx_file, fix->fx_line,
+ _("symbol in .toc does not match any .tc"));
+ }
+
+ /* Possibly adjust the reloc to be against the csect. */
+ tc = symbol_get_tc (fix->fx_addsy);
+ if (tc->subseg == 0
+ && tc->symbol_class != XMC_TC0
+ && tc->symbol_class != XMC_TC
+ && symseg != bss_section
+ /* Don't adjust if this is a reloc in the toc section. */
+ && (symseg != data_section
+ || ppc_toc_csect == NULL
+ || val < ppc_toc_frag->fr_address
+ || (ppc_after_toc_frag != NULL
+ && val >= ppc_after_toc_frag->fr_address)))
+ {
+ symbolS *csect = tc->within;
+
+ /* If the symbol was not declared by a label (eg: a section symbol),
+ use the section instead of the csect. This doesn't happen in
+ normal AIX assembly code. */
+ if (csect == NULL)
+ csect = seg_info (symseg)->sym;
+
+ fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
+ fix->fx_addsy = csect;
+
+ return 0;
+ }
+
+ /* Adjust a reloc against a .lcomm symbol to be against the base
+ .lcomm. */
+ if (symseg == bss_section
+ && ! S_IS_EXTERNAL (fix->fx_addsy))
+ {
+ symbolS *sy = symbol_get_frag (fix->fx_addsy)->fr_symbol;
+
+ fix->fx_offset += val - resolve_symbol_value (sy);
+ fix->fx_addsy = sy;
+ }
+
+ return 0;
+}
+
+/* A reloc from one csect to another must be kept. The assembler
+ will, of course, keep relocs between sections, and it will keep
+ absolute relocs, but we need to force it to keep PC relative relocs
+ between two csects in the same section. */
+
+int
+ppc_force_relocation (fixS *fix)
+{
+ /* At this point fix->fx_addsy should already have been converted to
+ a csect symbol. If the csect does not include the fragment, then
+ we need to force the relocation. */
+ if (fix->fx_pcrel
+ && fix->fx_addsy != NULL
+ && symbol_get_tc (fix->fx_addsy)->subseg != 0
+ && ((symbol_get_frag (fix->fx_addsy)->fr_address
+ > fix->fx_frag->fr_address)
+ || (symbol_get_tc (fix->fx_addsy)->next != NULL
+ && (symbol_get_frag (symbol_get_tc (fix->fx_addsy)->next)->fr_address
+ <= fix->fx_frag->fr_address))))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+void
+ppc_new_dot_label (symbolS *sym)
+{
+ /* Anchor this label to the current csect for relocations. */
+ symbol_get_tc (sym)->within = ppc_current_csect;
+}
+
+#endif /* OBJ_XCOFF */
+
+#ifdef OBJ_ELF
+/* If this function returns non-zero, it guarantees that a relocation
+ will be emitted for a fixup. */
+
+int
+ppc_force_relocation (fixS *fix)
+{
+ /* Branch prediction relocations must force a relocation, as must
+ the vtable description relocs. */
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ case BFD_RELOC_PPC_BA16_BRTAKEN:
+ case BFD_RELOC_PPC_BA16_BRNTAKEN:
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_PPC64_TOC:
+ return 1;
+ case BFD_RELOC_PPC_B26:
+ case BFD_RELOC_PPC_BA26:
+ case BFD_RELOC_PPC_B16:
+ case BFD_RELOC_PPC_BA16:
+ /* All branch fixups targeting a localentry symbol must
+ force a relocation. */
+ if (fix->fx_addsy)
+ {
+ asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+ elf_symbol_type *elfsym
+ = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ gas_assert (elfsym);
+ if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (fix->fx_r_type >= BFD_RELOC_PPC_TLS
+ && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA)
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+int
+ppc_fix_adjustable (fixS *fix)
+{
+ switch (fix->fx_r_type)
+ {
+ /* All branch fixups targeting a localentry symbol must
+ continue using the symbol. */
+ case BFD_RELOC_PPC_B26:
+ case BFD_RELOC_PPC_BA26:
+ case BFD_RELOC_PPC_B16:
+ case BFD_RELOC_PPC_BA16:
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ case BFD_RELOC_PPC_BA16_BRTAKEN:
+ case BFD_RELOC_PPC_BA16_BRNTAKEN:
+ if (fix->fx_addsy)
+ {
+ asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+ elf_symbol_type *elfsym
+ = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+ gas_assert (elfsym);
+ if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+ return 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return (fix->fx_r_type != BFD_RELOC_16_GOTOFF
+ && fix->fx_r_type != BFD_RELOC_LO16_GOTOFF
+ && fix->fx_r_type != BFD_RELOC_HI16_GOTOFF
+ && fix->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
+ && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_DS
+ && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_LO_DS
+ && fix->fx_r_type != BFD_RELOC_GPREL16
+ && fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT
+ && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY
+ && !(fix->fx_r_type >= BFD_RELOC_PPC_TLS
+ && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA));
+}
+#endif
+
+void
+ppc_frag_check (struct frag *fragP)
+{
+ if (!fragP->has_code)
+ return;
+
+ if (ppc_mach() == bfd_mach_ppc_vle)
+ {
+ if (((fragP->fr_address + fragP->insn_addr) & 1) != 0)
+ as_bad (_("instruction address is not a multiple of 2"));
+ }
+ else
+ {
+ if (((fragP->fr_address + fragP->insn_addr) & 3) != 0)
+ as_bad (_("instruction address is not a multiple of 4"));
+ }
+}
+
+/* Implement HANDLE_ALIGN. This writes the NOP pattern into an
+ rs_align_code frag. */
+
+void
+ppc_handle_align (struct frag *fragP)
+{
+ valueT count = (fragP->fr_next->fr_address
+ - (fragP->fr_address + fragP->fr_fix));
+
+ if (ppc_mach() == bfd_mach_ppc_vle && count != 0 && (count & 1) == 0)
+ {
+ char *dest = fragP->fr_literal + fragP->fr_fix;
+
+ fragP->fr_var = 2;
+ md_number_to_chars (dest, 0x4400, 2);
+ }
+ else if (count != 0 && (count & 3) == 0)
+ {
+ char *dest = fragP->fr_literal + fragP->fr_fix;
+
+ fragP->fr_var = 4;
+
+ if (count > 4 * nop_limit && count < 0x2000000)
+ {
+ struct frag *rest;
+
+ /* Make a branch, then follow with nops. Insert another
+ frag to handle the nops. */
+ md_number_to_chars (dest, 0x48000000 + count, 4);
+ count -= 4;
+ if (count == 0)
+ return;
+
+ rest = xmalloc (SIZEOF_STRUCT_FRAG + 4);
+ memcpy (rest, fragP, SIZEOF_STRUCT_FRAG);
+ fragP->fr_next = rest;
+ fragP = rest;
+ rest->fr_address += rest->fr_fix + 4;
+ rest->fr_fix = 0;
+ /* If we leave the next frag as rs_align_code we'll come here
+ again, resulting in a bunch of branches rather than a
+ branch followed by nops. */
+ rest->fr_type = rs_align;
+ dest = rest->fr_literal;
+ }
+
+ md_number_to_chars (dest, 0x60000000, 4);
+
+ if ((ppc_cpu & PPC_OPCODE_POWER6) != 0
+ || (ppc_cpu & PPC_OPCODE_POWER7) != 0
+ || (ppc_cpu & PPC_OPCODE_POWER8) != 0)
+ {
+ /* For power6, power7 and power8, we want the last nop to be a group
+ terminating one. Do this by inserting an rs_fill frag immediately
+ after this one, with its address set to the last nop location.
+ This will automatically reduce the number of nops in the current
+ frag by one. */
+ if (count > 4)
+ {
+ struct frag *group_nop = xmalloc (SIZEOF_STRUCT_FRAG + 4);
+
+ memcpy (group_nop, fragP, SIZEOF_STRUCT_FRAG);
+ group_nop->fr_address = group_nop->fr_next->fr_address - 4;
+ group_nop->fr_fix = 0;
+ group_nop->fr_offset = 1;
+ group_nop->fr_type = rs_fill;
+ fragP->fr_next = group_nop;
+ dest = group_nop->fr_literal;
+ }
+
+ if ((ppc_cpu & PPC_OPCODE_POWER7) != 0
+ || (ppc_cpu & PPC_OPCODE_POWER8) != 0)
+ {
+ if (ppc_cpu & PPC_OPCODE_E500MC)
+ /* e500mc group terminating nop: "ori 0,0,0". */
+ md_number_to_chars (dest, 0x60000000, 4);
+ else
+ /* power7/power8 group terminating nop: "ori 2,2,0". */
+ md_number_to_chars (dest, 0x60420000, 4);
+ }
+ else
+ /* power6 group terminating nop: "ori 1,1,0". */
+ md_number_to_chars (dest, 0x60210000, 4);
+ }
+ }
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the calls to fix_new_exp, above. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ valueT value = * valP;
+ offsetT fieldval;
+ const struct powerpc_operand *operand;
+
+#ifdef OBJ_ELF
+ if (fixP->fx_addsy != NULL)
+ {
+ /* Hack around bfd_install_relocation brain damage. */
+ if (fixP->fx_pcrel)
+ value += fixP->fx_frag->fr_address + fixP->fx_where;
+ }
+ else
+ fixP->fx_done = 1;
+#else
+ /* FIXME FIXME FIXME: The value we are passed in *valP includes
+ the symbol values. If we are doing this relocation the code in
+ write.c is going to call bfd_install_relocation, which is also
+ going to use the symbol value. That means that if the reloc is
+ fully resolved we want to use *valP since bfd_install_relocation is
+ not being used.
+ However, if the reloc is not fully resolved we do not want to
+ use *valP, and must use fx_offset instead. If the relocation
+ is PC-relative, we then need to re-apply md_pcrel_from_section
+ to this new relocation value. */
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ else
+ {
+ value = fixP->fx_offset;
+ if (fixP->fx_pcrel)
+ value -= md_pcrel_from_section (fixP, seg);
+ }
+#endif
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ }
+
+ operand = NULL;
+ if (fixP->fx_pcrel_adjust != 0)
+ {
+ /* This is a fixup on an instruction. */
+ int opindex = fixP->fx_pcrel_adjust & 0xff;
+
+ operand = &powerpc_operands[opindex];
+#ifdef OBJ_XCOFF
+ /* An instruction like `lwz 9,sym(30)' when `sym' is not a TOC symbol
+ does not generate a reloc. It uses the offset of `sym' within its
+ csect. Other usages, such as `.long sym', generate relocs. This
+ is the documented behaviour of non-TOC symbols. */
+ if ((operand->flags & PPC_OPERAND_PARENS) != 0
+ && (operand->bitm & 0xfff0) == 0xfff0
+ && operand->shift == 0
+ && (operand->insert == NULL || ppc_obj64)
+ && fixP->fx_addsy != NULL
+ && symbol_get_tc (fixP->fx_addsy)->subseg != 0
+ && symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC
+ && symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC0
+ && S_GET_SEGMENT (fixP->fx_addsy) != bss_section)
+ {
+ value = fixP->fx_offset;
+ fixP->fx_done = 1;
+ }
+
+ /* During parsing of instructions, a TOC16 reloc is generated for
+ instructions such as 'lwz RT,SYM(RB)' if SYM is a symbol defined
+ in the toc. But at parse time, SYM may be not yet defined, so
+ check again here. */
+ if (fixP->fx_r_type == BFD_RELOC_16
+ && fixP->fx_addsy != NULL
+ && ppc_is_toc_sym (fixP->fx_addsy))
+ fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
+#endif
+ }
+
+ /* Calculate value to be stored in field. */
+ fieldval = value;
+ switch (fixP->fx_r_type)
+ {
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC64_ADDR16_LO_DS:
+ case BFD_RELOC_PPC_VLE_LO16A:
+ case BFD_RELOC_PPC_VLE_LO16D:
+#endif
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_LO16_PCREL:
+ fieldval = value & 0xffff;
+ sign_extend_16:
+ if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+ fieldval = SEX16 (fieldval);
+ fixP->fx_no_overflow = 1;
+ break;
+
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_PCREL:
+#ifdef OBJ_ELF
+ if (REPORT_OVERFLOW_HI && ppc_obj64)
+ {
+ fieldval = value >> 16;
+ if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ valueT sign = (((valueT) -1 >> 16) + 1) >> 1;
+ fieldval = ((valueT) fieldval ^ sign) - sign;
+ }
+ break;
+ }
+ /* Fall thru */
+
+ case BFD_RELOC_PPC_VLE_HI16A:
+ case BFD_RELOC_PPC_VLE_HI16D:
+ case BFD_RELOC_PPC64_ADDR16_HIGH:
+#endif
+ fieldval = PPC_HI (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_HI16_S_PCREL:
+#ifdef OBJ_ELF
+ if (REPORT_OVERFLOW_HI && ppc_obj64)
+ {
+ fieldval = (value + 0x8000) >> 16;
+ if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+ {
+ valueT sign = (((valueT) -1 >> 16) + 1) >> 1;
+ fieldval = ((valueT) fieldval ^ sign) - sign;
+ }
+ break;
+ }
+ /* Fall thru */
+
+ case BFD_RELOC_PPC_VLE_HA16A:
+ case BFD_RELOC_PPC_VLE_HA16D:
+ case BFD_RELOC_PPC64_ADDR16_HIGHA:
+#endif
+ fieldval = PPC_HA (value);
+ goto sign_extend_16;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_PPC64_HIGHER:
+ fieldval = PPC_HIGHER (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_PPC64_HIGHER_S:
+ fieldval = PPC_HIGHERA (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_PPC64_HIGHEST:
+ fieldval = PPC_HIGHEST (value);
+ goto sign_extend_16;
+
+ case BFD_RELOC_PPC64_HIGHEST_S:
+ fieldval = PPC_HIGHESTA (value);
+ goto sign_extend_16;
+#endif
+
+ default:
+ break;
+ }
+
+ if (operand != NULL)
+ {
+ /* Handle relocs in an insn. */
+ char *where;
+ unsigned long insn;
+
+ switch (fixP->fx_r_type)
+ {
+#ifdef OBJ_ELF
+ /* The following relocs can't be calculated by the assembler.
+ Leave the field zero. */
+ case BFD_RELOC_PPC_TPREL16:
+ case BFD_RELOC_PPC_TPREL16_LO:
+ case BFD_RELOC_PPC_TPREL16_HI:
+ case BFD_RELOC_PPC_TPREL16_HA:
+ case BFD_RELOC_PPC_DTPREL16:
+ case BFD_RELOC_PPC_DTPREL16_LO:
+ case BFD_RELOC_PPC_DTPREL16_HI:
+ case BFD_RELOC_PPC_DTPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TLSGD16:
+ case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+ case BFD_RELOC_PPC_GOT_TLSLD16:
+ case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16_HI:
+ case BFD_RELOC_PPC_GOT_TPREL16_HA:
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+ case BFD_RELOC_PPC64_TPREL16_DS:
+ case BFD_RELOC_PPC64_TPREL16_LO_DS:
+ case BFD_RELOC_PPC64_TPREL16_HIGH:
+ case BFD_RELOC_PPC64_TPREL16_HIGHA:
+ case BFD_RELOC_PPC64_TPREL16_HIGHER:
+ case BFD_RELOC_PPC64_TPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_TPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+ case BFD_RELOC_PPC64_DTPREL16_HIGH:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHA:
+ case BFD_RELOC_PPC64_DTPREL16_DS:
+ case BFD_RELOC_PPC64_DTPREL16_LO_DS:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHER:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+ gas_assert (fixP->fx_addsy != NULL);
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ fieldval = 0;
+ break;
+
+ /* These also should leave the field zero for the same
+ reason. Note that older versions of gas wrote values
+ here. If we want to go back to the old behaviour, then
+ all _LO and _LO_DS cases will need to be treated like
+ BFD_RELOC_LO16_PCREL above. Similarly for _HI etc. */
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16_GOTOFF:
+ case BFD_RELOC_HI16_GOTOFF:
+ case BFD_RELOC_HI16_S_GOTOFF:
+ case BFD_RELOC_LO16_PLTOFF:
+ case BFD_RELOC_HI16_PLTOFF:
+ case BFD_RELOC_HI16_S_PLTOFF:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_16_BASEREL:
+ case BFD_RELOC_LO16_BASEREL:
+ case BFD_RELOC_HI16_BASEREL:
+ case BFD_RELOC_HI16_S_BASEREL:
+ case BFD_RELOC_PPC_TOC16:
+ case BFD_RELOC_PPC64_TOC16_LO:
+ case BFD_RELOC_PPC64_TOC16_HI:
+ case BFD_RELOC_PPC64_TOC16_HA:
+ case BFD_RELOC_PPC64_PLTGOT16:
+ case BFD_RELOC_PPC64_PLTGOT16_LO:
+ case BFD_RELOC_PPC64_PLTGOT16_HI:
+ case BFD_RELOC_PPC64_PLTGOT16_HA:
+ case BFD_RELOC_PPC64_GOT16_DS:
+ case BFD_RELOC_PPC64_GOT16_LO_DS:
+ case BFD_RELOC_PPC64_PLT16_LO_DS:
+ case BFD_RELOC_PPC64_SECTOFF_DS:
+ case BFD_RELOC_PPC64_SECTOFF_LO_DS:
+ case BFD_RELOC_PPC64_TOC16_DS:
+ case BFD_RELOC_PPC64_TOC16_LO_DS:
+ case BFD_RELOC_PPC64_PLTGOT16_DS:
+ case BFD_RELOC_PPC64_PLTGOT16_LO_DS:
+ case BFD_RELOC_PPC_EMB_NADDR16:
+ case BFD_RELOC_PPC_EMB_NADDR16_LO:
+ case BFD_RELOC_PPC_EMB_NADDR16_HI:
+ case BFD_RELOC_PPC_EMB_NADDR16_HA:
+ case BFD_RELOC_PPC_EMB_SDAI16:
+ case BFD_RELOC_PPC_EMB_SDA2I16:
+ case BFD_RELOC_PPC_EMB_SDA2REL:
+ case BFD_RELOC_PPC_EMB_SDA21:
+ case BFD_RELOC_PPC_EMB_MRKREF:
+ case BFD_RELOC_PPC_EMB_RELSEC16:
+ case BFD_RELOC_PPC_EMB_RELST_LO:
+ case BFD_RELOC_PPC_EMB_RELST_HI:
+ case BFD_RELOC_PPC_EMB_RELST_HA:
+ case BFD_RELOC_PPC_EMB_BIT_FLD:
+ case BFD_RELOC_PPC_EMB_RELSDA:
+ case BFD_RELOC_PPC_VLE_SDA21:
+ case BFD_RELOC_PPC_VLE_SDA21_LO:
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+ gas_assert (fixP->fx_addsy != NULL);
+ /* Fall thru */
+
+ case BFD_RELOC_PPC_TLS:
+ case BFD_RELOC_PPC_TLSGD:
+ case BFD_RELOC_PPC_TLSLD:
+ fieldval = 0;
+ break;
+#endif
+
+#ifdef OBJ_XCOFF
+ case BFD_RELOC_PPC_B16:
+ /* Adjust the offset to the instruction boundary. */
+ fieldval += 2;
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+#ifdef OBJ_ELF
+/* powerpc uses RELA style relocs, so if emitting a reloc the field
+ contents can stay at zero. */
+#define APPLY_RELOC fixP->fx_done
+#else
+#define APPLY_RELOC 1
+#endif
+ if ((fieldval != 0 && APPLY_RELOC) || operand->insert != NULL)
+ {
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again. */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ if (target_big_endian)
+ {
+ if (fixP->fx_size == 4)
+ insn = bfd_getb32 ((unsigned char *) where);
+ else
+ insn = bfd_getb16 ((unsigned char *) where);
+ }
+ else
+ {
+ if (fixP->fx_size == 4)
+ insn = bfd_getl32 ((unsigned char *) where);
+ else
+ insn = bfd_getl16 ((unsigned char *) where);
+ }
+ insn = ppc_insert_operand (insn, operand, fieldval,
+ fixP->tc_fix_data.ppc_cpu,
+ fixP->fx_file, fixP->fx_line);
+ if (target_big_endian)
+ {
+ if (fixP->fx_size == 4)
+ bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putb16 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ else
+ {
+ if (fixP->fx_size == 4)
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl16 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ }
+
+ if (fixP->fx_done)
+ /* Nothing else to do here. */
+ return;
+
+ gas_assert (fixP->fx_addsy != NULL);
+ if (fixP->fx_r_type == BFD_RELOC_NONE)
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an expression
+ symbol. */
+ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsupported relocation against %s"),
+ S_GET_NAME (fixP->fx_addsy));
+ fixP->fx_done = 1;
+ return;
+ }
+ }
+ else
+ {
+ /* Handle relocs in data. */
+ switch (fixP->fx_r_type)
+ {
+ 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);
+ /* Fall thru */
+
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+#ifdef OBJ_ELF
+ /* These can appear with @l etc. in data. */
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_LO16_PCREL:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_PCREL:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_PPC64_HIGHER:
+ case BFD_RELOC_PPC64_HIGHER_S:
+ case BFD_RELOC_PPC64_HIGHEST:
+ case BFD_RELOC_PPC64_HIGHEST_S:
+ case BFD_RELOC_PPC64_ADDR16_HIGH:
+ case BFD_RELOC_PPC64_ADDR16_HIGHA:
+ case BFD_RELOC_PPC64_ADDR64_LOCAL:
+ break;
+
+ case BFD_RELOC_PPC_DTPMOD:
+ case BFD_RELOC_PPC_TPREL:
+ case BFD_RELOC_PPC_DTPREL:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ /* Just punt all of these to the linker. */
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ case BFD_RELOC_16_GOTOFF:
+ case BFD_RELOC_LO16_GOTOFF:
+ case BFD_RELOC_HI16_GOTOFF:
+ case BFD_RELOC_HI16_S_GOTOFF:
+ case BFD_RELOC_LO16_PLTOFF:
+ case BFD_RELOC_HI16_PLTOFF:
+ case BFD_RELOC_HI16_S_PLTOFF:
+ case BFD_RELOC_PPC_COPY:
+ case BFD_RELOC_PPC_GLOB_DAT:
+ case BFD_RELOC_16_BASEREL:
+ case BFD_RELOC_LO16_BASEREL:
+ case BFD_RELOC_HI16_BASEREL:
+ case BFD_RELOC_HI16_S_BASEREL:
+ case BFD_RELOC_PPC_TLS:
+ case BFD_RELOC_PPC_DTPREL16_LO:
+ case BFD_RELOC_PPC_DTPREL16_HI:
+ case BFD_RELOC_PPC_DTPREL16_HA:
+ case BFD_RELOC_PPC_TPREL16_LO:
+ case BFD_RELOC_PPC_TPREL16_HI:
+ case BFD_RELOC_PPC_TPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TLSGD16:
+ case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+ case BFD_RELOC_PPC_GOT_TLSLD16:
+ case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+ case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+ case BFD_RELOC_PPC_GOT_DTPREL16:
+ case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+ case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+ case BFD_RELOC_PPC_GOT_TPREL16:
+ case BFD_RELOC_PPC_GOT_TPREL16_LO:
+ case BFD_RELOC_PPC_GOT_TPREL16_HI:
+ case BFD_RELOC_PPC_GOT_TPREL16_HA:
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_PPC_LOCAL24PC:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+ case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+ case BFD_RELOC_PPC_EMB_NADDR32:
+ case BFD_RELOC_PPC_EMB_NADDR16:
+ case BFD_RELOC_PPC_EMB_NADDR16_LO:
+ case BFD_RELOC_PPC_EMB_NADDR16_HI:
+ case BFD_RELOC_PPC_EMB_NADDR16_HA:
+ case BFD_RELOC_PPC_EMB_SDAI16:
+ case BFD_RELOC_PPC_EMB_SDA2REL:
+ case BFD_RELOC_PPC_EMB_SDA2I16:
+ case BFD_RELOC_PPC_EMB_SDA21:
+ case BFD_RELOC_PPC_VLE_SDA21_LO:
+ case BFD_RELOC_PPC_EMB_MRKREF:
+ case BFD_RELOC_PPC_EMB_RELSEC16:
+ case BFD_RELOC_PPC_EMB_RELST_LO:
+ case BFD_RELOC_PPC_EMB_RELST_HI:
+ case BFD_RELOC_PPC_EMB_RELST_HA:
+ case BFD_RELOC_PPC_EMB_BIT_FLD:
+ case BFD_RELOC_PPC_EMB_RELSDA:
+ case BFD_RELOC_PPC64_TOC:
+ case BFD_RELOC_PPC_TOC16:
+ case BFD_RELOC_PPC64_TOC16_LO:
+ case BFD_RELOC_PPC64_TOC16_HI:
+ case BFD_RELOC_PPC64_TOC16_HA:
+ case BFD_RELOC_PPC64_DTPREL16_HIGH:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHA:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHER:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+ case BFD_RELOC_PPC64_TPREL16_HIGH:
+ case BFD_RELOC_PPC64_TPREL16_HIGHA:
+ case BFD_RELOC_PPC64_TPREL16_HIGHER:
+ case BFD_RELOC_PPC64_TPREL16_HIGHERA:
+ case BFD_RELOC_PPC64_TPREL16_HIGHEST:
+ case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+ fixP->fx_done = 0;
+ break;
+#endif
+
+#ifdef OBJ_XCOFF
+ case BFD_RELOC_NONE:
+#endif
+ case BFD_RELOC_CTOR:
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_8:
+ break;
+
+ default:
+ fprintf (stderr,
+ _("Gas failure, reloc value %d\n"), fixP->fx_r_type);
+ fflush (stderr);
+ abort ();
+ }
+
+ if (fixP->fx_size && APPLY_RELOC)
+ md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+ fieldval, fixP->fx_size);
+ if (warn_476
+ && (seg->flags & SEC_CODE) != 0
+ && fixP->fx_size == 4
+ && fixP->fx_done
+ && !fixP->fx_tcbit
+ && (fixP->fx_r_type == BFD_RELOC_32
+ || fixP->fx_r_type == BFD_RELOC_CTOR
+ || fixP->fx_r_type == BFD_RELOC_32_PCREL))
+ as_warn_where (fixP->fx_file, fixP->fx_line,
+ _("data in executable section"));
+ }
+
+ /* We are only able to convert some relocs to pc-relative. */
+ if (!fixP->fx_done && fixP->fx_pcrel)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_LO16:
+ fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
+ break;
+
+ case BFD_RELOC_HI16:
+ fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
+ break;
+
+ case BFD_RELOC_HI16_S:
+ fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
+ break;
+
+ case BFD_RELOC_64:
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ break;
+
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ break;
+
+ /* Some of course are already pc-relative. */
+ case BFD_RELOC_LO16_PCREL:
+ case BFD_RELOC_HI16_PCREL:
+ case BFD_RELOC_HI16_S_PCREL:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_PPC_B16:
+ case BFD_RELOC_PPC_B16_BRTAKEN:
+ case BFD_RELOC_PPC_B16_BRNTAKEN:
+ case BFD_RELOC_PPC_B26:
+ case BFD_RELOC_PPC_LOCAL24PC:
+ case BFD_RELOC_24_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+ case BFD_RELOC_64_PLT_PCREL:
+ case BFD_RELOC_PPC_VLE_REL8:
+ case BFD_RELOC_PPC_VLE_REL15:
+ case BFD_RELOC_PPC_VLE_REL24:
+ break;
+
+ default:
+ if (fixP->fx_addsy)
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an
+ expression symbol. */
+ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must"
+ " be resolved"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot emit PC relative %s relocation"
+ " against %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type),
+ S_GET_NAME (fixP->fx_addsy));
+ }
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unable to resolve expression"));
+ fixP->fx_done = 1;
+ break;
+ }
+ }
+
+#ifdef OBJ_ELF
+ ppc_elf_validate_fix (fixP, seg);
+ fixP->fx_addnumber = value;
+
+ /* PowerPC uses RELA relocs, ie. the reloc addend is stored separately
+ from the section contents. If we are going to be emitting a reloc
+ then the section contents are immaterial, so don't warn if they
+ happen to overflow. Leave such warnings to ld. */
+ if (!fixP->fx_done)
+ {
+ fixP->fx_no_overflow = 1;
+
+ /* Arrange to emit .TOC. as a normal symbol if used in anything
+ but .TOC.@tocbase. */
+ if (ppc_obj64
+ && fixP->fx_r_type != BFD_RELOC_PPC64_TOC
+ && fixP->fx_addsy != NULL
+ && strcmp (S_GET_NAME (fixP->fx_addsy), ".TOC.") == 0)
+ symbol_get_bfdsym (fixP->fx_addsy)->flags |= BSF_KEEP;
+ }
+#else
+ if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16)
+ fixP->fx_addnumber = 0;
+ else
+ {
+#ifdef TE_PE
+ fixP->fx_addnumber = 0;
+#else
+ /* We want to use the offset within the toc, not the actual VMA
+ of the symbol. */
+ fixP->fx_addnumber =
+ - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy))
+ - S_GET_VALUE (ppc_toc_csect);
+ /* Set *valP to avoid errors. */
+ *valP = value;
+#endif
+ }
+#endif
+}
+
+/* Generate a reloc for a fixup. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_addnumber;
+
+ return reloc;
+}
+
+void
+ppc_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (1, 0);
+}
+
+int
+tc_ppc_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int i;
+ const char *p;
+ char *q;
+ static struct { char *name; int dw2regnum; } regnames[] =
+ {
+ { "sp", 1 }, { "r.sp", 1 }, { "rtoc", 2 }, { "r.toc", 2 },
+ { "mq", 64 }, { "lr", 65 }, { "ctr", 66 }, { "ap", 67 },
+ { "cr", 70 }, { "xer", 76 }, { "vrsave", 109 }, { "vscr", 110 },
+ { "spe_acc", 111 }, { "spefscr", 112 }
+ };
+
+ for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+ if (strcmp (regnames[i].name, regname) == 0)
+ return regnames[i].dw2regnum;
+
+ if (regname[0] == 'r' || regname[0] == 'f' || regname[0] == 'v')
+ {
+ p = regname + 1 + (regname[1] == '.');
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 32)
+ return -1;
+ if (regname[0] == 'f')
+ regnum += 32;
+ else if (regname[0] == 'v')
+ regnum += 77;
+ }
+ else if (regname[0] == 'c' && regname[1] == 'r')
+ {
+ p = regname + 2 + (regname[2] == '.');
+ if (p[0] < '0' || p[0] > '7' || p[1])
+ return -1;
+ regnum = p[0] - '0' + 68;
+ }
+ return regnum;
+}
diff --git a/gas/config/tc-ppc.h b/gas/config/tc-ppc.h
new file mode 100644
index 0000000..3cd9bf1
--- /dev/null
+++ b/gas/config/tc-ppc.h
@@ -0,0 +1,290 @@
+/* tc-ppc.h -- Header file for tc-ppc.c.
+ Copyright (C) 1994-2014 Free Software Foundation, Inc.
+ Written by Ian Lance Taylor, Cygnus Support.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_PPC
+
+#include "opcode/ppc.h"
+
+struct fix;
+
+/* Set the endianness we are using. Default to big endian. */
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 1
+#endif
+
+/* If OBJ_COFF is defined, and TE_PE is not defined, we are assembling
+ XCOFF for AIX or PowerMac. If TE_PE is defined, we are assembling
+ COFF for Windows NT. */
+
+#ifdef OBJ_COFF
+#ifndef TE_PE
+#define OBJ_XCOFF
+#endif
+#endif
+
+/* The target BFD architecture. */
+#define TARGET_ARCH (ppc_arch ())
+#define TARGET_MACH (ppc_mach ())
+extern enum bfd_architecture ppc_arch (void);
+extern unsigned long ppc_mach (void);
+
+/* Whether or not the target is big endian */
+extern int target_big_endian;
+
+/* The target BFD format. */
+#define TARGET_FORMAT (ppc_target_format ())
+extern char *ppc_target_format (void);
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* $ is used to refer to the current location. */
+#define DOLLAR_DOT
+
+/* Strings do not use backslash escapes under COFF. */
+#ifdef OBJ_COFF
+#define NO_STRING_ESCAPES
+#endif
+
+#ifdef OBJ_ELF
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+#endif
+
+#if TARGET_BYTES_BIG_ENDIAN
+#define PPC_BIG_ENDIAN 1
+#else
+#define PPC_BIG_ENDIAN 0
+#endif
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE 4
+#define HANDLE_ALIGN(FRAGP) \
+ if ((FRAGP)->fr_type == rs_align_code) \
+ ppc_handle_align (FRAGP);
+
+extern void ppc_handle_align (struct frag *);
+extern void ppc_frag_check (struct frag *);
+
+#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
+
+#define md_frag_check(FRAGP) ppc_frag_check (FRAGP)
+
+/* Arrange to store the value of ppc_cpu at the site of a fixup
+ for later use in md_apply_fix. */
+struct _ppc_fix_extra
+{
+ ppc_cpu_t ppc_cpu;
+};
+
+extern ppc_cpu_t ppc_cpu;
+
+#define TC_FIX_TYPE struct _ppc_fix_extra
+#define TC_INIT_FIX_DATA(FIXP) \
+ do { (FIXP)->tc_fix_data.ppc_cpu = ppc_cpu; } while (0)
+
+#ifdef TE_PE
+
+/* Question marks are permitted in symbol names. */
+#define LEX_QM 1
+
+/* Don't adjust TOC relocs. */
+#define tc_fix_adjustable(FIX) ppc_pe_fix_adjustable (FIX)
+extern int ppc_pe_fix_adjustable (struct fix *);
+
+#endif
+
+#ifdef OBJ_XCOFF
+
+/* Declarations needed when generating XCOFF code. XCOFF is an
+ extension of COFF, used only on the RS/6000. Rather than create an
+ obj-xcoff, we just use obj-coff, and handle the extensions here in
+ tc-ppc. */
+
+/* We need to keep some information for symbols. */
+struct ppc_tc_sy
+{
+ /* We keep a few linked lists of symbols. */
+ symbolS *next;
+ /* The real name, if the symbol was renamed. */
+ char *real_name;
+ /* Non-zero if the symbol should be output. The RS/6000 assembler
+ only outputs symbols that are external or are mentioned in a
+ .globl or .lglobl statement. */
+ unsigned char output;
+ /* The symbol class. */
+ short symbol_class;
+ /* For a csect or common symbol, the alignment to use. */
+ unsigned char align;
+ /* For a csect symbol, the subsegment we are using. This is zero
+ for symbols that are not csects. */
+ subsegT subseg;
+ /* For a csect symbol, the last symbol which has been defined in
+ this csect, or NULL if none have been defined so far.
+ For a .bs symbol, the referenced csect symbol.
+ For a label, the enclosing csect. */
+ symbolS *within;
+ union
+ {
+ /* For a function symbol, a symbol whose value is the size. The
+ field is NULL if there is no size. */
+ symbolS *size;
+ /* For a dwarf symbol, the corresponding dwarf subsection. */
+ struct dw_subsection *dw;
+ } u;
+};
+
+#define TC_SYMFIELD_TYPE struct ppc_tc_sy
+
+/* We need an additional auxent for function symbols. */
+#define OBJ_COFF_MAX_AUXENTRIES 2
+
+/* Square and curly brackets are permitted in symbol names. */
+#define LEX_BR 3
+
+/* Canonicalize the symbol name. */
+#define tc_canonicalize_symbol_name(name) ppc_canonicalize_symbol_name (name)
+extern char *ppc_canonicalize_symbol_name (char *);
+
+/* Get the symbol class from the name. */
+#define tc_symbol_new_hook(sym) ppc_symbol_new_hook (sym)
+extern void ppc_symbol_new_hook (symbolS *);
+
+/* Set the symbol class of a label based on the csect. */
+#define tc_frob_label(sym) ppc_frob_label (sym)
+extern void ppc_frob_label (symbolS *);
+
+/* TOC relocs requires special handling. */
+#define tc_fix_adjustable(FIX) ppc_fix_adjustable (FIX)
+extern int ppc_fix_adjustable (struct fix *);
+
+/* We need to set the section VMA. */
+#define tc_frob_section(sec) ppc_frob_section (sec)
+extern void ppc_frob_section (asection *);
+
+/* Finish up the symbol. */
+#define tc_frob_symbol(sym, punt) punt = ppc_frob_symbol (sym)
+extern int ppc_frob_symbol (symbolS *);
+
+/* Finish up the entire symtab. */
+#define tc_adjust_symtab() ppc_adjust_symtab ()
+extern void ppc_adjust_symtab (void);
+
+/* We also need to copy, in particular, the class of the symbol,
+ over what obj-coff would otherwise have copied. */
+#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest,src) \
+do { \
+ if (SF_GET_GET_SEGMENT (dest)) \
+ S_SET_SEGMENT (dest, S_GET_SEGMENT (src)); \
+ symbol_get_tc (dest)->u = symbol_get_tc (src)->u; \
+ symbol_get_tc (dest)->align = symbol_get_tc (src)->align; \
+ symbol_get_tc (dest)->symbol_class = symbol_get_tc (src)->symbol_class; \
+ symbol_get_tc (dest)->within = symbol_get_tc (src)->within; \
+} while (0)
+
+extern void ppc_xcoff_end (void);
+#define md_end ppc_xcoff_end
+
+#define tc_new_dot_label(sym) ppc_new_dot_label (sym)
+extern void ppc_new_dot_label (symbolS *);
+
+#endif /* OBJ_XCOFF */
+
+extern const char ppc_symbol_chars[];
+#define tc_symbol_chars ppc_symbol_chars
+
+#ifdef OBJ_ELF
+
+/* Support for SHT_ORDERED */
+extern int ppc_section_type (char *, size_t);
+extern int ppc_section_flags (flagword, bfd_vma, int);
+
+#define md_elf_section_type(STR, LEN) ppc_section_type (STR, LEN)
+#define md_elf_section_flags(FLAGS, ATTR, TYPE) ppc_section_flags (FLAGS, ATTR, TYPE)
+
+#define tc_comment_chars ppc_comment_chars
+extern const char *ppc_comment_chars;
+
+/* Keep relocations relative to the GOT, or non-PC relative. */
+#define tc_fix_adjustable(FIX) ppc_fix_adjustable (FIX)
+extern int ppc_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
+ ppc_elf_parse_cons (EXP, NBYTES)
+extern bfd_reloc_code_real_type ppc_elf_parse_cons (expressionS *,
+ unsigned int);
+#define TC_CONS_FIX_CHECK(EXP, NBYTES, FIX) \
+ ppc_elf_cons_fix_check (EXP, NBYTES, FIX)
+extern void ppc_elf_cons_fix_check (expressionS *, unsigned int, struct fix *);
+
+#define tc_frob_file_before_adjust ppc_frob_file_before_adjust
+extern void ppc_frob_file_before_adjust (void);
+
+#define tc_adjust_symtab() ppc_elf_adjust_symtab ()
+extern void ppc_elf_adjust_symtab (void);
+
+extern void ppc_elf_end (void);
+#define md_end ppc_elf_end
+
+#endif /* OBJ_ELF */
+
+#if defined (OBJ_ELF) || defined (OBJ_XCOFF)
+#define TC_FORCE_RELOCATION(FIX) ppc_force_relocation (FIX)
+extern int ppc_force_relocation (struct fix *);
+#endif
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define md_parse_name(name, exp, mode, c) ppc_parse_name (name, exp)
+extern int ppc_parse_name (const char *, struct expressionS *);
+
+#define md_operand(x)
+
+#define md_cleanup() ppc_cleanup ()
+extern void ppc_cleanup (void);
+
+/* ppc uses different register numbers between .eh_frame and .debug_frame.
+ This macro translates the .eh_frame register numbers to .debug_frame
+ register numbers. */
+#define md_reg_eh_frame_to_debug_frame(regno) \
+ ((regno) == 70 ? 64 /* cr2 */ : (regno))
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions ppc_cfi_frame_initial_instructions
+extern void ppc_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_ppc_regname_to_dw2regnum
+extern int tc_ppc_regname_to_dw2regnum (char *);
+
+extern int ppc_cie_data_alignment;
+
+extern int ppc_dwarf2_line_min_insn_length;
+
+#define DWARF2_LINE_MIN_INSN_LENGTH ppc_dwarf2_line_min_insn_length
+#define DWARF2_DEFAULT_RETURN_COLUMN 0x41
+#define DWARF2_CIE_DATA_ALIGNMENT ppc_cie_data_alignment
diff --git a/gas/config/tc-rl78.c b/gas/config/tc-rl78.c
new file mode 100644
index 0000000..1627c61
--- /dev/null
+++ b/gas/config/tc-rl78.c
@@ -0,0 +1,1411 @@
+/* tc-rl78.c -- Assembler for the Renesas RL78
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "struc-symbol.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "libbfd.h"
+#include "elf/common.h"
+#include "elf/rl78.h"
+#include "rl78-defs.h"
+#include "filenames.h"
+#include "listing.h"
+#include "sb.h"
+#include "macro.h"
+
+const char comment_chars[] = ";";
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+const char line_comment_chars[] = "#";
+/* Use something that isn't going to be needed by any expressions or
+ other syntax. */
+const char line_separator_chars[] = "@";
+
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* ELF flags to set in the output file header. */
+static int elf_flags = 0;
+
+/*------------------------------------------------------------------*/
+
+char * rl78_lex_start;
+char * rl78_lex_end;
+
+typedef struct rl78_bytesT
+{
+ char prefix[1];
+ int n_prefix;
+ char base[4];
+ int n_base;
+ char ops[8];
+ int n_ops;
+ struct
+ {
+ expressionS exp;
+ char offset;
+ char nbits;
+ char type; /* RL78REL_*. */
+ int reloc;
+ fixS * fixP;
+ } fixups[2];
+ int n_fixups;
+ struct
+ {
+ char type;
+ char field_pos;
+ char val_ofs;
+ } relax[2];
+ int n_relax;
+ int link_relax;
+ fixS *link_relax_fixP;
+ char times_grown;
+ char times_shrank;
+} rl78_bytesT;
+
+static rl78_bytesT rl78_bytes;
+
+void
+rl78_relax (int type, int pos)
+{
+ rl78_bytes.relax[rl78_bytes.n_relax].type = type;
+ rl78_bytes.relax[rl78_bytes.n_relax].field_pos = pos;
+ rl78_bytes.relax[rl78_bytes.n_relax].val_ofs = rl78_bytes.n_base + rl78_bytes.n_ops;
+ rl78_bytes.n_relax ++;
+}
+
+void
+rl78_linkrelax_addr16 (void)
+{
+ rl78_bytes.link_relax |= RL78_RELAXA_ADDR16;
+}
+
+void
+rl78_linkrelax_branch (void)
+{
+ rl78_bytes.link_relax |= RL78_RELAXA_BRA;
+}
+
+static void
+rl78_fixup (expressionS exp, int offsetbits, int nbits, int type)
+{
+ rl78_bytes.fixups[rl78_bytes.n_fixups].exp = exp;
+ rl78_bytes.fixups[rl78_bytes.n_fixups].offset = offsetbits;
+ rl78_bytes.fixups[rl78_bytes.n_fixups].nbits = nbits;
+ rl78_bytes.fixups[rl78_bytes.n_fixups].type = type;
+ rl78_bytes.fixups[rl78_bytes.n_fixups].reloc = exp.X_md;
+ rl78_bytes.n_fixups ++;
+}
+
+#define rl78_field_fixup(exp, offset, nbits, type) \
+ rl78_fixup (exp, offset + 8 * rl78_bytes.n_prefix), nbits, type)
+
+#define rl78_op_fixup(exp, offset, nbits, type) \
+ rl78_fixup (exp, offset + 8 * (rl78_bytes.n_prefix + rl78_bytes.n_base), nbits, type)
+
+void
+rl78_prefix (int p)
+{
+ rl78_bytes.prefix[0] = p;
+ rl78_bytes.n_prefix = 1;
+}
+
+int
+rl78_has_prefix ()
+{
+ return rl78_bytes.n_prefix;
+}
+
+void
+rl78_base1 (int b1)
+{
+ rl78_bytes.base[0] = b1;
+ rl78_bytes.n_base = 1;
+}
+
+void
+rl78_base2 (int b1, int b2)
+{
+ rl78_bytes.base[0] = b1;
+ rl78_bytes.base[1] = b2;
+ rl78_bytes.n_base = 2;
+}
+
+void
+rl78_base3 (int b1, int b2, int b3)
+{
+ rl78_bytes.base[0] = b1;
+ rl78_bytes.base[1] = b2;
+ rl78_bytes.base[2] = b3;
+ rl78_bytes.n_base = 3;
+}
+
+void
+rl78_base4 (int b1, int b2, int b3, int b4)
+{
+ rl78_bytes.base[0] = b1;
+ rl78_bytes.base[1] = b2;
+ rl78_bytes.base[2] = b3;
+ rl78_bytes.base[3] = b4;
+ rl78_bytes.n_base = 4;
+}
+
+#define F_PRECISION 2
+
+void
+rl78_op (expressionS exp, int nbytes, int type)
+{
+ int v = 0;
+
+ if ((exp.X_op == O_constant || exp.X_op == O_big)
+ && type != RL78REL_PCREL)
+ {
+ if (exp.X_op == O_big && exp.X_add_number <= 0)
+ {
+ LITTLENUM_TYPE w[2];
+ char * ip = rl78_bytes.ops + rl78_bytes.n_ops;
+
+ gen_to_words (w, F_PRECISION, 8);
+ ip[3] = w[0] >> 8;
+ ip[2] = w[0];
+ ip[1] = w[1] >> 8;
+ ip[0] = w[1];
+ rl78_bytes.n_ops += 4;
+ }
+ else
+ {
+ v = exp.X_add_number;
+ while (nbytes)
+ {
+ rl78_bytes.ops[rl78_bytes.n_ops++] =v & 0xff;
+ v >>= 8;
+ nbytes --;
+ }
+ }
+ }
+ else
+ {
+ if (nbytes > 2
+ && exp.X_md == BFD_RELOC_RL78_CODE)
+ exp.X_md = 0;
+
+ if (nbytes == 1
+ && (exp.X_md == BFD_RELOC_RL78_LO16
+ || exp.X_md == BFD_RELOC_RL78_HI16))
+ as_bad (_("16-bit relocation used in 8-bit operand"));
+
+ if (nbytes == 2
+ && exp.X_md == BFD_RELOC_RL78_HI8)
+ as_bad (_("8-bit relocation used in 16-bit operand"));
+
+ rl78_op_fixup (exp, rl78_bytes.n_ops * 8, nbytes * 8, type);
+ memset (rl78_bytes.ops + rl78_bytes.n_ops, 0, nbytes);
+ rl78_bytes.n_ops += nbytes;
+ }
+}
+
+/* This gets complicated when the field spans bytes, because fields
+ are numbered from the MSB of the first byte as zero, and bits are
+ stored LSB towards the LSB of the byte. Thus, a simple four-bit
+ insertion of 12 at position 4 of 0x00 yields: 0x0b. A three-bit
+ insertion of b'MXL at position 7 is like this:
+
+ - - - - - - - - - - - - - - - -
+ M X L */
+
+void
+rl78_field (int val, int pos, int sz)
+{
+ int valm;
+ int bytep, bitp;
+
+ if (sz > 0)
+ {
+ if (val < 0 || val >= (1 << sz))
+ as_bad (_("Value %d doesn't fit in unsigned %d-bit field"), val, sz);
+ }
+ else
+ {
+ sz = - sz;
+ if (val < -(1 << (sz - 1)) || val >= (1 << (sz - 1)))
+ as_bad (_("Value %d doesn't fit in signed %d-bit field"), val, sz);
+ }
+
+ /* This code points at 'M' in the above example. */
+ bytep = pos / 8;
+ bitp = pos % 8;
+
+ while (bitp + sz > 8)
+ {
+ int ssz = 8 - bitp;
+ int svalm;
+
+ svalm = val >> (sz - ssz);
+ svalm = svalm & ((1 << ssz) - 1);
+ svalm = svalm << (8 - bitp - ssz);
+ gas_assert (bytep < rl78_bytes.n_base);
+ rl78_bytes.base[bytep] |= svalm;
+
+ bitp = 0;
+ sz -= ssz;
+ bytep ++;
+ }
+ valm = val & ((1 << sz) - 1);
+ valm = valm << (8 - bitp - sz);
+ gas_assert (bytep < rl78_bytes.n_base);
+ rl78_bytes.base[bytep] |= valm;
+}
+
+/*------------------------------------------------------------------*/
+
+enum options
+{
+ OPTION_RELAX = OPTION_MD_BASE,
+ OPTION_G10,
+ OPTION_32BIT_DOUBLES,
+ OPTION_64BIT_DOUBLES,
+};
+
+#define RL78_SHORTOPTS ""
+const char * md_shortopts = RL78_SHORTOPTS;
+
+/* Assembler options. */
+struct option md_longopts[] =
+{
+ {"relax", no_argument, NULL, OPTION_RELAX},
+ {"mg10", no_argument, NULL, OPTION_G10},
+ {"m32bit-doubles", no_argument, NULL, OPTION_32BIT_DOUBLES},
+ {"m64bit-doubles", no_argument, NULL, OPTION_64BIT_DOUBLES},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_RELAX:
+ linkrelax = 1;
+ return 1;
+
+ case OPTION_G10:
+ elf_flags |= E_FLAG_RL78_G10;
+ return 1;
+
+ case OPTION_32BIT_DOUBLES:
+ elf_flags &= ~ E_FLAG_RL78_64BIT_DOUBLES;
+ return 1;
+
+ case OPTION_64BIT_DOUBLES:
+ elf_flags |= E_FLAG_RL78_64BIT_DOUBLES;
+ return 1;
+ }
+ return 0;
+}
+
+void
+md_show_usage (FILE * stream ATTRIBUTE_UNUSED)
+{
+ fprintf (stream, _(" RL78 specific command line options:\n"));
+ fprintf (stream, _(" --mg10 Enable support for G10 variant\n"));
+ fprintf (stream, _(" --m32bit-doubles [default]\n"));
+ fprintf (stream, _(" --m64bit-doubles\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 ();
+}
+
+static void
+rl78_float_cons (int ignore ATTRIBUTE_UNUSED)
+{
+ if (elf_flags & E_FLAG_RL78_64BIT_DOUBLES)
+ return float_cons ('d');
+ return float_cons ('f');
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* Our "standard" pseudos. */
+ { "double", rl78_float_cons, 'd' },
+ { "bss", s_bss, 0 },
+ { "3byte", cons, 3 },
+ { "int", cons, 4 },
+ { "word", cons, 4 },
+
+ /* End of list marker. */
+ { NULL, NULL, 0 }
+};
+
+void
+md_begin (void)
+{
+}
+
+void
+rl78_md_end (void)
+{
+}
+
+/* Set the ELF specific flags. */
+void
+rl78_elf_final_processing (void)
+{
+ elf_elfheader (stdoutput)->e_flags |= elf_flags;
+}
+
+/* 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);
+}
+
+static void
+require_end_of_expr (char *fname)
+{
+ while (* input_line_pointer == ' '
+ || * input_line_pointer == '\t')
+ input_line_pointer ++;
+
+ if (! * input_line_pointer
+ || strchr ("\n\r,", * input_line_pointer)
+ || strchr (comment_chars, * input_line_pointer)
+ || strchr (line_comment_chars, * input_line_pointer)
+ || strchr (line_separator_chars, * input_line_pointer))
+ return;
+
+ as_bad (_("%%%s() must be outermost term in expression"), fname);
+}
+
+static struct
+{
+ char * fname;
+ int reloc;
+}
+reloc_functions[] =
+{
+ { "code", BFD_RELOC_RL78_CODE },
+ { "lo16", BFD_RELOC_RL78_LO16 },
+ { "hi16", BFD_RELOC_RL78_HI16 },
+ { "hi8", BFD_RELOC_RL78_HI8 },
+ { 0, 0 }
+};
+
+void
+md_operand (expressionS * exp ATTRIBUTE_UNUSED)
+{
+ int reloc = 0;
+ int i;
+
+ for (i = 0; reloc_functions[i].fname; i++)
+ {
+ int flen = strlen (reloc_functions[i].fname);
+
+ if (input_line_pointer[0] == '%'
+ && strncasecmp (input_line_pointer + 1, reloc_functions[i].fname, flen) == 0
+ && input_line_pointer[flen + 1] == '(')
+ {
+ reloc = reloc_functions[i].reloc;
+ input_line_pointer += flen + 2;
+ break;
+ }
+ }
+ if (reloc == 0)
+ return;
+
+ expression (exp);
+ if (* input_line_pointer == ')')
+ input_line_pointer ++;
+
+ exp->X_md = reloc;
+
+ require_end_of_expr (reloc_functions[i].fname);
+}
+
+void
+rl78_frag_init (fragS * fragP)
+{
+ if (rl78_bytes.n_relax || rl78_bytes.link_relax)
+ {
+ fragP->tc_frag_data = malloc (sizeof (rl78_bytesT));
+ memcpy (fragP->tc_frag_data, & rl78_bytes, sizeof (rl78_bytesT));
+ }
+ else
+ fragP->tc_frag_data = 0;
+}
+
+/* When relaxing, we need to output a reloc for any .align directive
+ so that we can retain this alignment as we adjust opcode sizes. */
+void
+rl78_handle_align (fragS * frag)
+{
+ if (linkrelax
+ && (frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 0
+ && now_seg != bss_section)
+ {
+ fix_new (frag, frag->fr_fix, 0,
+ &abs_symbol, RL78_RELAXA_ALIGN + frag->fr_offset,
+ 0, BFD_RELOC_RL78_RELAX);
+ /* For the purposes of relaxation, this relocation is attached
+ to the byte *after* the alignment - i.e. the byte that must
+ remain aligned. */
+ fix_new (frag->fr_next, 0, 0,
+ &abs_symbol, RL78_RELAXA_ELIGN + frag->fr_offset,
+ 0, BFD_RELOC_RL78_RELAX);
+ }
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+#define APPEND(B, N_B) \
+ if (rl78_bytes.N_B) \
+ { \
+ memcpy (bytes + idx, rl78_bytes.B, rl78_bytes.N_B); \
+ idx += rl78_bytes.N_B; \
+ }
+
+
+void
+md_assemble (char * str)
+{
+ char * bytes;
+ fragS * frag_then = frag_now;
+ int idx = 0;
+ int i;
+ int rel;
+ expressionS *exp;
+
+ /*printf("\033[32mASM: %s\033[0m\n", str);*/
+
+ dwarf2_emit_insn (0);
+
+ memset (& rl78_bytes, 0, sizeof (rl78_bytes));
+
+ rl78_lex_init (str, str + strlen (str));
+
+ rl78_parse ();
+
+ /* This simplifies the relaxation code. */
+ if (rl78_bytes.n_relax || rl78_bytes.link_relax)
+ {
+ int olen = rl78_bytes.n_prefix + rl78_bytes.n_base + rl78_bytes.n_ops;
+ /* We do it this way because we want the frag to have the
+ rl78_bytes in it, which we initialize above. The extra bytes
+ are for relaxing. */
+ bytes = frag_more (olen + 3);
+ frag_then = frag_now;
+ frag_variant (rs_machine_dependent,
+ olen /* max_chars */,
+ 0 /* var */,
+ olen /* subtype */,
+ 0 /* symbol */,
+ 0 /* offset */,
+ 0 /* opcode */);
+ frag_then->fr_opcode = bytes;
+ frag_then->fr_fix = olen + (bytes - frag_then->fr_literal);
+ frag_then->fr_subtype = olen;
+ frag_then->fr_var = 0;
+ }
+ else
+ {
+ bytes = frag_more (rl78_bytes.n_prefix + rl78_bytes.n_base + rl78_bytes.n_ops);
+ frag_then = frag_now;
+ }
+
+ APPEND (prefix, n_prefix);
+ APPEND (base, n_base);
+ APPEND (ops, n_ops);
+
+ if (rl78_bytes.link_relax)
+ {
+ fixS * f;
+
+ f = fix_new (frag_then,
+ (char *) bytes - frag_then->fr_literal,
+ 0,
+ abs_section_sym,
+ rl78_bytes.link_relax | rl78_bytes.n_fixups,
+ 0,
+ BFD_RELOC_RL78_RELAX);
+ frag_then->tc_frag_data->link_relax_fixP = f;
+ }
+
+ for (i = 0; i < rl78_bytes.n_fixups; i ++)
+ {
+ /* index: [nbytes][type] */
+ static int reloc_map[5][4] =
+ {
+ { 0, 0 },
+ { BFD_RELOC_8, BFD_RELOC_8_PCREL },
+ { BFD_RELOC_16, BFD_RELOC_16_PCREL },
+ { BFD_RELOC_24, BFD_RELOC_24_PCREL },
+ { BFD_RELOC_32, BFD_RELOC_32_PCREL },
+ };
+ fixS * f;
+
+ idx = rl78_bytes.fixups[i].offset / 8;
+ rel = reloc_map [rl78_bytes.fixups[i].nbits / 8][(int) rl78_bytes.fixups[i].type];
+
+ if (rl78_bytes.fixups[i].reloc)
+ rel = rl78_bytes.fixups[i].reloc;
+
+ if (frag_then->tc_frag_data)
+ exp = & frag_then->tc_frag_data->fixups[i].exp;
+ else
+ exp = & rl78_bytes.fixups[i].exp;
+
+ f = fix_new_exp (frag_then,
+ (char *) bytes + idx - frag_then->fr_literal,
+ rl78_bytes.fixups[i].nbits / 8,
+ exp,
+ rl78_bytes.fixups[i].type == RL78REL_PCREL ? 1 : 0,
+ rel);
+ if (frag_then->tc_frag_data)
+ frag_then->tc_frag_data->fixups[i].fixP = f;
+ }
+}
+
+void
+rl78_cons_fix_new (fragS * frag,
+ int where,
+ int size,
+ expressionS * exp)
+{
+ bfd_reloc_code_real_type type;
+ fixS *fixP;
+
+ 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:
+ type = BFD_RELOC_32;
+ break;
+ default:
+ as_bad (_("unsupported constant size %d\n"), size);
+ return;
+ }
+
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_RL78_CODE:
+ if (size == 2)
+ type = exp->X_md;
+ break;
+ case BFD_RELOC_RL78_LO16:
+ case BFD_RELOC_RL78_HI16:
+ if (size != 2)
+ as_bad (_("%%hi16/%%lo16 only applies to .short or .hword"));
+ type = exp->X_md;
+ break;
+ case BFD_RELOC_RL78_HI8:
+ if (size != 1)
+ as_bad (_("%%hi8 only applies to .byte"));
+ type = exp->X_md;
+ break;
+ default:
+ break;
+ }
+
+ if (exp->X_op == O_subtract && exp->X_op_symbol)
+ {
+ if (size != 4 && size != 2 && size != 1)
+ as_bad (_("difference of two symbols only supported with .long, .short, or .byte"));
+ else
+ type = BFD_RELOC_RL78_DIFF;
+ }
+
+ fixP = fix_new_exp (frag, where, (int) size, exp, 0, type);
+ switch (exp->X_md)
+ {
+ /* These are intended to have values larger than the container,
+ since the backend puts only the portion we need in it.
+ However, we don't have a backend-specific reloc for them as
+ they're handled with complex relocations. */
+ case BFD_RELOC_RL78_LO16:
+ case BFD_RELOC_RL78_HI16:
+ case BFD_RELOC_RL78_HI8:
+ fixP->fx_no_overflow = 1;
+ break;
+ default:
+ break;
+ }
+}
+
+
+/*----------------------------------------------------------------------*/
+/* To recap: we estimate everything based on md_estimate_size, then
+ adjust based on rl78_relax_frag. When it all settles, we call
+ md_convert frag to update the bytes. The relaxation types and
+ relocations are in fragP->tc_frag_data, which is a copy of that
+ rl78_bytes.
+
+ Our scheme is as follows: fr_fix has the size of the smallest
+ opcode (like BRA.S). We store the number of total bytes we need in
+ fr_subtype. When we're done relaxing, we use fr_subtype and the
+ existing opcode bytes to figure out what actual opcode we need to
+ put in there. If the fixup isn't resolvable now, we use the
+ maximal size. */
+
+#define TRACE_RELAX 0
+#define tprintf if (TRACE_RELAX) printf
+
+
+typedef enum
+{
+ OT_other,
+ OT_bt,
+ OT_bt_sfr,
+ OT_bt_es,
+ OT_bc,
+ OT_bh
+} op_type_T;
+
+/* We're looking for these types of relaxations:
+
+ BT 00110001 sbit0cc1 addr---- (cc is 10 (BF) or 01 (BT))
+ B~T 00110001 sbit0cc1 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+
+ BT sfr 00110001 sbit0cc0 sfr----- addr----
+ BT ES: 00010001 00101110 sbit0cc1 addr----
+
+ BC 110111cc addr----
+ B~C 110111cc 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+
+ BH 01100001 110c0011 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+ B~H 01100001 110c0011 00000011 11101110 pcrel16- -------- (BR $!pcrel20)
+*/
+
+/* Given the opcode bytes at OP, figure out which opcode it is and
+ return the type of opcode. We use this to re-encode the opcode as
+ a different size later. */
+
+static op_type_T
+rl78_opcode_type (char * op)
+{
+ if (op[0] == 0x31
+ && ((op[1] & 0x0f) == 0x05
+ || (op[1] & 0x0f) == 0x03))
+ return OT_bt;
+
+ if (op[0] == 0x31
+ && ((op[1] & 0x0f) == 0x04
+ || (op[1] & 0x0f) == 0x02))
+ return OT_bt_sfr;
+
+ if (op[0] == 0x11
+ && op[1] == 0x31
+ && ((op[2] & 0x0f) == 0x05
+ || (op[2] & 0x0f) == 0x03))
+ return OT_bt_es;
+
+ if ((op[0] & 0xfc) == 0xdc)
+ return OT_bc;
+
+ if (op[0] == 0x61
+ && (op[1] & 0xef) == 0xc3)
+ return OT_bh;
+
+ return OT_other;
+}
+
+/* Returns zero if *addrP has the target address. Else returns nonzero
+ if we cannot compute the target address yet. */
+
+static int
+rl78_frag_fix_value (fragS * fragP,
+ segT segment,
+ int which,
+ addressT * addrP,
+ int need_diff,
+ addressT * sym_addr)
+{
+ addressT addr = 0;
+ rl78_bytesT * b = fragP->tc_frag_data;
+ expressionS * exp = & b->fixups[which].exp;
+
+ if (need_diff && exp->X_op != O_subtract)
+ return 1;
+
+ if (exp->X_add_symbol)
+ {
+ if (S_FORCE_RELOC (exp->X_add_symbol, 1))
+ return 1;
+ if (S_GET_SEGMENT (exp->X_add_symbol) != segment)
+ return 1;
+ addr += S_GET_VALUE (exp->X_add_symbol);
+ }
+
+ if (exp->X_op_symbol)
+ {
+ if (exp->X_op != O_subtract)
+ return 1;
+ if (S_FORCE_RELOC (exp->X_op_symbol, 1))
+ return 1;
+ if (S_GET_SEGMENT (exp->X_op_symbol) != segment)
+ return 1;
+ addr -= S_GET_VALUE (exp->X_op_symbol);
+ }
+ if (sym_addr)
+ * sym_addr = addr;
+ addr += exp->X_add_number;
+ * addrP = addr;
+ return 0;
+}
+
+/* Estimate how big the opcode is after this relax pass. The return
+ value is the difference between fr_fix and the actual size. We
+ compute the total size in rl78_relax_frag and store it in fr_subtype,
+ sowe only need to subtract fx_fix and return it. */
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED)
+{
+ int opfixsize;
+ int delta;
+
+ /* This is the size of the opcode that's accounted for in fr_fix. */
+ opfixsize = fragP->fr_fix - (fragP->fr_opcode - fragP->fr_literal);
+ /* This is the size of the opcode that isn't. */
+ delta = (fragP->fr_subtype - opfixsize);
+
+ tprintf (" -> opfixsize %d delta %d\n", opfixsize, delta);
+ return delta;
+}
+
+/* Given the new addresses for this relax pass, figure out how big
+ each opcode must be. We store the total number of bytes needed in
+ fr_subtype. The return value is the difference between the size
+ after the last pass and the size after this pass, so we use the old
+ fr_subtype to calculate the difference. */
+
+int
+rl78_relax_frag (segT segment ATTRIBUTE_UNUSED, fragS * fragP, long stretch)
+{
+ addressT addr0, sym_addr;
+ addressT mypc;
+ int disp;
+ int oldsize = fragP->fr_subtype;
+ int newsize = oldsize;
+ op_type_T optype;
+ int ri;
+
+ mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+
+ /* If we ever get more than one reloc per opcode, this is the one
+ we're relaxing. */
+ ri = 0;
+
+ optype = rl78_opcode_type (fragP->fr_opcode);
+ /* Try to get the target address. */
+ if (rl78_frag_fix_value (fragP, segment, ri, & addr0,
+ fragP->tc_frag_data->relax[ri].type != RL78_RELAX_BRANCH,
+ & sym_addr))
+ {
+ /* If we don't, we must use the maximum size for the linker. */
+ switch (fragP->tc_frag_data->relax[ri].type)
+ {
+ case RL78_RELAX_BRANCH:
+ switch (optype)
+ {
+ case OT_bt:
+ newsize = 6;
+ break;
+ case OT_bt_sfr:
+ case OT_bt_es:
+ newsize = 7;
+ break;
+ case OT_bc:
+ newsize = 5;
+ break;
+ case OT_bh:
+ newsize = 6;
+ break;
+ case OT_other:
+ newsize = oldsize;
+ break;
+ }
+ break;
+
+ }
+ fragP->fr_subtype = newsize;
+ tprintf (" -> new %d old %d delta %d (external)\n", newsize, oldsize, newsize-oldsize);
+ return newsize - oldsize;
+ }
+
+ if (sym_addr > mypc)
+ addr0 += stretch;
+
+ switch (fragP->tc_frag_data->relax[ri].type)
+ {
+ case RL78_RELAX_BRANCH:
+ disp = (int) addr0 - (int) mypc;
+
+ switch (optype)
+ {
+ case OT_bt:
+ if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+ newsize = 3;
+ else
+ newsize = 6;
+ break;
+ case OT_bt_sfr:
+ case OT_bt_es:
+ if (disp >= -128 && (disp - (oldsize-3)) <= 127)
+ newsize = 4;
+ else
+ newsize = 7;
+ break;
+ case OT_bc:
+ if (disp >= -128 && (disp - (oldsize-1)) <= 127)
+ newsize = 2;
+ else
+ newsize = 5;
+ break;
+ case OT_bh:
+ if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+ newsize = 3;
+ else
+ newsize = 6;
+ break;
+ case OT_other:
+ newsize = oldsize;
+ break;
+ }
+ break;
+ }
+
+ /* This prevents infinite loops in align-heavy sources. */
+ if (newsize < oldsize)
+ {
+ if (fragP->tc_frag_data->times_shrank > 10
+ && fragP->tc_frag_data->times_grown > 10)
+ newsize = oldsize;
+ if (fragP->tc_frag_data->times_shrank < 20)
+ fragP->tc_frag_data->times_shrank ++;
+ }
+ else if (newsize > oldsize)
+ {
+ if (fragP->tc_frag_data->times_grown < 20)
+ fragP->tc_frag_data->times_grown ++;
+ }
+
+ fragP->fr_subtype = newsize;
+ tprintf (" -> new %d old %d delta %d\n", newsize, oldsize, newsize-oldsize);
+ return newsize - oldsize;
+ }
+
+/* This lets us test for the opcode type and the desired size in a
+ switch statement. */
+#define OPCODE(type,size) ((type) * 16 + (size))
+
+/* Given the opcode stored in fr_opcode and the number of bytes we
+ think we need, encode a new opcode. We stored a pointer to the
+ fixup for this opcode in the tc_frag_data structure. If we can do
+ the fixup here, we change the relocation type to "none" (we test
+ for that in tc_gen_reloc) else we change it to the right type for
+ the new (biggest) opcode. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+ rl78_bytesT * rl78b = fragP->tc_frag_data;
+ addressT addr0, mypc;
+ int disp;
+ int reloc_type, reloc_adjust;
+ char * op = fragP->fr_opcode;
+ int keep_reloc = 0;
+ int ri;
+ int fi = (rl78b->n_fixups > 1) ? 1 : 0;
+ fixS * fix = rl78b->fixups[fi].fixP;
+
+ /* If we ever get more than one reloc per opcode, this is the one
+ we're relaxing. */
+ ri = 0;
+
+ /* We used a new frag for this opcode, so the opcode address should
+ be the frag address. */
+ mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+ tprintf("\033[32mmypc: 0x%x\033[0m\n", (int)mypc);
+
+ /* Try to get the target address. If we fail here, we just use the
+ largest format. */
+ if (rl78_frag_fix_value (fragP, segment, 0, & addr0,
+ fragP->tc_frag_data->relax[ri].type != RL78_RELAX_BRANCH, 0))
+ {
+ /* We don't know the target address. */
+ keep_reloc = 1;
+ addr0 = 0;
+ disp = 0;
+ tprintf ("unknown addr ? - %x = ?\n", (int)mypc);
+ }
+ else
+ {
+ /* We know the target address, and it's in addr0. */
+ disp = (int) addr0 - (int) mypc;
+ tprintf ("known addr %x - %x = %d\n", (int)addr0, (int)mypc, disp);
+ }
+
+ if (linkrelax)
+ keep_reloc = 1;
+
+ reloc_type = BFD_RELOC_NONE;
+ reloc_adjust = 0;
+
+ switch (fragP->tc_frag_data->relax[ri].type)
+ {
+ case RL78_RELAX_BRANCH:
+ switch (OPCODE (rl78_opcode_type (fragP->fr_opcode), fragP->fr_subtype))
+ {
+
+ case OPCODE (OT_bt, 3): /* BT A,$ - no change. */
+ disp -= 3;
+ op[2] = disp;
+ break;
+
+ case OPCODE (OT_bt, 6): /* BT A,$ - long version. */
+ disp -= 3;
+ op[1] ^= 0x06; /* toggle conditional. */
+ op[2] = 3; /* displacement over long branch. */
+ disp -= 3;
+ op[3] = 0xEE; /* BR $!addr20 */
+ op[4] = disp & 0xff;
+ op[5] = disp >> 8;
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ case OPCODE (OT_bt_sfr, 4): /* BT PSW,$ - no change. */
+ disp -= 4;
+ op[3] = disp;
+ break;
+
+ case OPCODE (OT_bt_sfr, 7): /* BT PSW,$ - long version. */
+ disp -= 4;
+ op[1] ^= 0x06; /* toggle conditional. */
+ op[3] = 3; /* displacement over long branch. */
+ disp -= 3;
+ op[4] = 0xEE; /* BR $!addr20 */
+ op[5] = disp & 0xff;
+ op[6] = disp >> 8;
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ case OPCODE (OT_bt_es, 4): /* BT ES:[HL],$ - no change. */
+ disp -= 4;
+ op[3] = disp;
+ break;
+
+ case OPCODE (OT_bt_es, 7): /* BT PSW,$ - long version. */
+ disp -= 4;
+ op[2] ^= 0x06; /* toggle conditional. */
+ op[3] = 3; /* displacement over long branch. */
+ disp -= 3;
+ op[4] = 0xEE; /* BR $!addr20 */
+ op[5] = disp & 0xff;
+ op[6] = disp >> 8;
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ case OPCODE (OT_bc, 2): /* BC $ - no change. */
+ disp -= 2;
+ op[1] = disp;
+ break;
+
+ case OPCODE (OT_bc, 5): /* BC $ - long version. */
+ disp -= 2;
+ op[0] ^= 0x02; /* toggle conditional. */
+ op[1] = 3;
+ disp -= 3;
+ op[2] = 0xEE; /* BR $!addr20 */
+ op[3] = disp & 0xff;
+ op[4] = disp >> 8;
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ case OPCODE (OT_bh, 3): /* BH $ - no change. */
+ disp -= 3;
+ op[2] = disp;
+ break;
+
+ case OPCODE (OT_bh, 6): /* BC $ - long version. */
+ disp -= 3;
+ op[1] ^= 0x10; /* toggle conditional. */
+ op[2] = 3;
+ disp -= 3;
+ op[3] = 0xEE; /* BR $!addr20 */
+ op[4] = disp & 0xff;
+ op[5] = disp >> 8;
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ default:
+ fprintf(stderr, "Missed case %d %d at 0x%lx\n",
+ rl78_opcode_type (fragP->fr_opcode), fragP->fr_subtype, mypc);
+ abort ();
+
+ }
+ break;
+
+ default:
+ if (rl78b->n_fixups)
+ {
+ reloc_type = fix->fx_r_type;
+ reloc_adjust = 0;
+ }
+ break;
+ }
+
+ if (rl78b->n_fixups)
+ {
+
+ fix->fx_r_type = reloc_type;
+ fix->fx_where += reloc_adjust;
+ switch (reloc_type)
+ {
+ case BFD_RELOC_NONE:
+ fix->fx_size = 0;
+ break;
+ case BFD_RELOC_8:
+ fix->fx_size = 1;
+ break;
+ case BFD_RELOC_16_PCREL:
+ fix->fx_size = 2;
+ break;
+ }
+ }
+
+ fragP->fr_fix = fragP->fr_subtype + (fragP->fr_opcode - fragP->fr_literal);
+ tprintf ("fragP->fr_fix now %ld (%d + (%p - %p)\n", (long) fragP->fr_fix,
+ fragP->fr_subtype, fragP->fr_opcode, fragP->fr_literal);
+ fragP->fr_var = 0;
+
+ tprintf ("compare 0x%lx vs 0x%lx - 0x%lx = 0x%lx (%p)\n",
+ (long)fragP->fr_fix,
+ (long)fragP->fr_next->fr_address, (long)fragP->fr_address,
+ (long)(fragP->fr_next->fr_address - fragP->fr_address),
+ fragP->fr_next);
+
+ if (fragP->fr_next != NULL
+ && ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address)
+ != fragP->fr_fix))
+ as_bad (_("bad frag at %p : fix %ld addr %ld %ld \n"), fragP,
+ (long) fragP->fr_fix,
+ (long) fragP->fr_address, (long) fragP->fr_next->fr_address);
+}
+
+/* End of relaxation code.
+ ----------------------------------------------------------------------*/
+
+
+arelent **
+tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ static arelent * reloc[8];
+ int rp;
+
+ if (fixp->fx_r_type == BFD_RELOC_NONE)
+ {
+ reloc[0] = NULL;
+ return reloc;
+ }
+
+ if (fixp->fx_subsy
+ && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ {
+ fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_subsy = NULL;
+ }
+
+ reloc[0] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[0]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ * reloc[0]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc[0]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc[0]->addend = fixp->fx_offset;
+
+ if (fixp->fx_r_type == BFD_RELOC_RL78_32_OP
+ && fixp->fx_subsy)
+ {
+ fixp->fx_r_type = BFD_RELOC_RL78_DIFF;
+ }
+
+#define OPX(REL,SYM,ADD) \
+ reloc[rp] = (arelent *) xmalloc (sizeof (arelent)); \
+ reloc[rp]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); \
+ reloc[rp]->howto = bfd_reloc_type_lookup (stdoutput, REL); \
+ reloc[rp]->addend = ADD; \
+ * reloc[rp]->sym_ptr_ptr = SYM; \
+ reloc[rp]->address = fixp->fx_frag->fr_address + fixp->fx_where; \
+ reloc[++rp] = NULL
+#define OPSYM(SYM) OPX(BFD_RELOC_RL78_SYM, SYM, 0)
+#define OPIMM(IMM) OPX(BFD_RELOC_RL78_SYM, abs_symbol.bsym, IMM)
+#define OP(OP) OPX(BFD_RELOC_RL78_##OP, *reloc[0]->sym_ptr_ptr, 0)
+#define SYM0() reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RL78_SYM)
+
+ rp = 1;
+
+ /* Certain BFD relocations cannot be translated directly into
+ a single (non-Red Hat) RL78 relocation, but instead need
+ multiple RL78 relocations - handle them here. */
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_RL78_DIFF:
+ SYM0 ();
+ OPSYM (symbol_get_bfdsym (fixp->fx_subsy));
+ OP(OP_SUBTRACT);
+
+ switch (fixp->fx_size)
+ {
+ case 1:
+ OP(ABS8);
+ break;
+ case 2:
+ OP (ABS16);
+ break;
+ case 4:
+ OP (ABS32);
+ break;
+ }
+ break;
+
+ case BFD_RELOC_RL78_NEG32:
+ SYM0 ();
+ OP (OP_NEG);
+ OP (ABS32);
+ break;
+
+ case BFD_RELOC_RL78_CODE:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RL78_16U);
+ reloc[1] = NULL;
+ break;
+
+ case BFD_RELOC_RL78_LO16:
+ SYM0 ();
+ OPIMM (0xffff);
+ OP (OP_AND);
+ OP (ABS16);
+ break;
+
+ case BFD_RELOC_RL78_HI16:
+ SYM0 ();
+ OPIMM (16);
+ OP (OP_SHRA);
+ OP (ABS16);
+ break;
+
+ case BFD_RELOC_RL78_HI8:
+ SYM0 ();
+ OPIMM (16);
+ OP (OP_SHRA);
+ OPIMM (0xff);
+ OP (OP_AND);
+ OP (ABS8);
+ break;
+
+ default:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ reloc[1] = NULL;
+ break;
+ }
+
+ return reloc;
+}
+
+int
+rl78_validate_fix_sub (struct fix * f)
+{
+ /* We permit the subtraction of two symbols in a few cases. */
+ /* mov #sym1-sym2, R3 */
+ if (f->fx_r_type == BFD_RELOC_RL78_32_OP)
+ return 1;
+ /* .long sym1-sym2 */
+ if (f->fx_r_type == BFD_RELOC_RL78_DIFF
+ && ! f->fx_pcrel
+ && (f->fx_size == 4 || f->fx_size == 2 || f->fx_size == 1))
+ return 1;
+ return 0;
+}
+
+long
+md_pcrel_from_section (fixS * fixP, segT sec)
+{
+ long rv;
+
+ if (fixP->fx_addsy != 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;
+
+ rv = fixP->fx_frag->fr_address + fixP->fx_where;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8_PCREL:
+ rv += 1;
+ break;
+ case BFD_RELOC_16_PCREL:
+ rv += 2;
+ break;
+ default:
+ break;
+ }
+ return rv;
+}
+
+void
+md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
+ valueT * t ATTRIBUTE_UNUSED,
+ segT s ATTRIBUTE_UNUSED)
+{
+ char * op;
+ unsigned long val;
+
+ if (f->fx_addsy && S_FORCE_RELOC (f->fx_addsy, 1))
+ return;
+ if (f->fx_subsy && S_FORCE_RELOC (f->fx_subsy, 1))
+ return;
+
+ op = f->fx_frag->fr_literal + f->fx_where;
+ val = (unsigned long) * t;
+
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_NONE:
+ break;
+
+ case BFD_RELOC_RL78_RELAX:
+ f->fx_done = 1;
+ break;
+
+ case BFD_RELOC_8_PCREL:
+ if ((long)val < -128 || (long)val > 127)
+ as_bad_where (f->fx_file, f->fx_line,
+ _("value of %ld too large for 8-bit branch"),
+ val);
+ /* Fall through. */
+ case BFD_RELOC_8:
+ op[0] = val;
+ break;
+
+ case BFD_RELOC_16_PCREL:
+ if ((long)val < -32768 || (long)val > 32767)
+ as_bad_where (f->fx_file, f->fx_line,
+ _("value of %ld too large for 16-bit branch"),
+ val);
+ /* Fall through. */
+ case BFD_RELOC_16:
+ case BFD_RELOC_RL78_CODE:
+ op[0] = val;
+ op[1] = val >> 8;
+ break;
+
+ case BFD_RELOC_24:
+ op[0] = val;
+ op[1] = val >> 8;
+ op[2] = val >> 16;
+ break;
+
+ case BFD_RELOC_32:
+ op[0] = val;
+ op[1] = val >> 8;
+ op[2] = val >> 16;
+ op[3] = val >> 24;
+ break;
+
+ case BFD_RELOC_RL78_DIFF:
+ op[0] = val;
+ if (f->fx_size > 1)
+ op[1] = val >> 8;
+ if (f->fx_size > 2)
+ op[2] = val >> 16;
+ if (f->fx_size > 3)
+ op[3] = val >> 24;
+ break;
+
+ case BFD_RELOC_RL78_HI8:
+ val = val >> 16;
+ op[0] = val;
+ break;
+
+ case BFD_RELOC_RL78_HI16:
+ val = val >> 16;
+ op[0] = val;
+ op[1] = val >> 8;
+ break;
+
+ case BFD_RELOC_RL78_LO16:
+ op[0] = val;
+ op[1] = val >> 8;
+ break;
+
+ default:
+ as_bad (_("Unknown reloc in md_apply_fix: %s"),
+ bfd_get_reloc_code_name (f->fx_r_type));
+ break;
+ }
+
+ if (f->fx_addsy == NULL)
+ f->fx_done = 1;
+}
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
diff --git a/gas/config/tc-rl78.h b/gas/config/tc-rl78.h
new file mode 100644
index 0000000..5b6a312
--- /dev/null
+++ b/gas/config/tc-rl78.h
@@ -0,0 +1,84 @@
+/* tc-rl78.h - header file for Renesas RL78
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_RL78
+
+extern int target_little_endian;
+
+#define LISTING_HEADER "RL78 GAS LE"
+#define LISTING_LHS_WIDTH 8
+#define LISTING_WORD_SIZE 1
+
+#define TARGET_ARCH bfd_arch_rl78
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_FORMAT "elf32-rl78"
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+/* But make sure that the binutils treat them as locals. */
+#define LOCAL_LABEL_PREFIX '.'
+
+/* Allow classic-style constants. */
+#define NUMBERS_WITH_SUFFIX 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+#define md_end rl78_md_end
+extern void rl78_md_end (void);
+
+#define md_relax_frag rl78_relax_frag
+extern int rl78_relax_frag (segT, fragS *, long);
+
+#define TC_FRAG_TYPE struct rl78_bytesT *
+#define TC_FRAG_INIT rl78_frag_init
+extern void rl78_frag_init (fragS *);
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* RL78 doesn't have a 32 bit PCREL relocations. */
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
+
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) \
+ rl78_validate_fix_sub (FIX)
+extern int rl78_validate_fix_sub (struct fix *);
+
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RET) \
+ rl78_cons_fix_new (FRAG, WHERE, NBYTES, EXP)
+extern void rl78_cons_fix_new (fragS *, int, int, expressionS *);
+
+#define tc_fix_adjustable(x) 0
+
+#define RELOC_EXPANSION_POSSIBLE 1
+#define MAX_RELOC_EXPANSION 8
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE 8
+#define HANDLE_ALIGN(FRAG) rl78_handle_align (FRAG)
+extern void rl78_handle_align (fragS *);
+
+#define elf_tc_final_processing rl78_elf_final_processing
+extern void rl78_elf_final_processing (void);
diff --git a/gas/config/tc-rx.c b/gas/config/tc-rx.c
new file mode 100644
index 0000000..0d7e1d5
--- /dev/null
+++ b/gas/config/tc-rx.c
@@ -0,0 +1,2688 @@
+/* tc-rx.c -- Assembler for the Renesas RX
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "struc-symbol.h"
+#include "safe-ctype.h"
+#include "dwarf2dbg.h"
+#include "libbfd.h"
+#include "elf/common.h"
+#include "elf/rx.h"
+#include "rx-defs.h"
+#include "filenames.h"
+#include "listing.h"
+#include "sb.h"
+#include "macro.h"
+
+#define RX_OPCODE_BIG_ENDIAN 0
+
+const char comment_chars[] = ";";
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = "!";
+
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* ELF flags to set in the output file header. */
+static int elf_flags = E_FLAG_RX_ABI;
+
+bfd_boolean rx_use_conventional_section_names = FALSE;
+static bfd_boolean rx_use_small_data_limit = FALSE;
+
+static bfd_boolean rx_pid_mode = FALSE;
+static int rx_num_int_regs = 0;
+int rx_pid_register;
+int rx_gp_register;
+
+enum rx_cpu_types rx_cpu = RX600;
+
+static void rx_fetchalign (int ignore ATTRIBUTE_UNUSED);
+
+enum options
+{
+ OPTION_BIG = OPTION_MD_BASE,
+ OPTION_LITTLE,
+ OPTION_32BIT_DOUBLES,
+ OPTION_64BIT_DOUBLES,
+ OPTION_CONVENTIONAL_SECTION_NAMES,
+ OPTION_RENESAS_SECTION_NAMES,
+ OPTION_SMALL_DATA_LIMIT,
+ OPTION_RELAX,
+ OPTION_PID,
+ OPTION_INT_REGS,
+ OPTION_USES_GCC_ABI,
+ OPTION_USES_RX_ABI,
+ OPTION_CPU,
+};
+
+#define RX_SHORTOPTS ""
+const char * md_shortopts = RX_SHORTOPTS;
+
+/* Assembler options. */
+struct option md_longopts[] =
+{
+ {"mbig-endian-data", no_argument, NULL, OPTION_BIG},
+ {"mlittle-endian-data", no_argument, NULL, OPTION_LITTLE},
+ /* The next two switches are here because the
+ generic parts of the linker testsuite uses them. */
+ {"EB", no_argument, NULL, OPTION_BIG},
+ {"EL", no_argument, NULL, OPTION_LITTLE},
+ {"m32bit-doubles", no_argument, NULL, OPTION_32BIT_DOUBLES},
+ {"m64bit-doubles", no_argument, NULL, OPTION_64BIT_DOUBLES},
+ /* This option is here mainly for the binutils testsuites,
+ as many of their tests assume conventional section naming. */
+ {"muse-conventional-section-names", no_argument, NULL, OPTION_CONVENTIONAL_SECTION_NAMES},
+ {"muse-renesas-section-names", no_argument, NULL, OPTION_RENESAS_SECTION_NAMES},
+ {"msmall-data-limit", no_argument, NULL, OPTION_SMALL_DATA_LIMIT},
+ {"relax", no_argument, NULL, OPTION_RELAX},
+ {"mpid", no_argument, NULL, OPTION_PID},
+ {"mint-register", required_argument, NULL, OPTION_INT_REGS},
+ {"mgcc-abi", no_argument, NULL, OPTION_USES_GCC_ABI},
+ {"mrx-abi", no_argument, NULL, OPTION_USES_RX_ABI},
+ {"mcpu",required_argument,NULL,OPTION_CPU},
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED, char * arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_BIG:
+ target_big_endian = 1;
+ return 1;
+
+ case OPTION_LITTLE:
+ target_big_endian = 0;
+ return 1;
+
+ case OPTION_32BIT_DOUBLES:
+ elf_flags &= ~ E_FLAG_RX_64BIT_DOUBLES;
+ return 1;
+
+ case OPTION_64BIT_DOUBLES:
+ elf_flags |= E_FLAG_RX_64BIT_DOUBLES;
+ return 1;
+
+ case OPTION_CONVENTIONAL_SECTION_NAMES:
+ rx_use_conventional_section_names = TRUE;
+ return 1;
+
+ case OPTION_RENESAS_SECTION_NAMES:
+ rx_use_conventional_section_names = FALSE;
+ return 1;
+
+ case OPTION_SMALL_DATA_LIMIT:
+ rx_use_small_data_limit = TRUE;
+ return 1;
+
+ case OPTION_RELAX:
+ linkrelax = 1;
+ return 1;
+
+ case OPTION_PID:
+ rx_pid_mode = TRUE;
+ elf_flags |= E_FLAG_RX_PID;
+ return 1;
+
+ case OPTION_INT_REGS:
+ rx_num_int_regs = atoi (optarg);
+ return 1;
+
+ case OPTION_USES_GCC_ABI:
+ elf_flags &= ~ E_FLAG_RX_ABI;
+ return 1;
+
+ case OPTION_USES_RX_ABI:
+ elf_flags |= E_FLAG_RX_ABI;
+ return 1;
+
+ case OPTION_CPU:
+ if (strcasecmp (arg, "rx100") == 0)
+ rx_cpu = RX100;
+ else if (strcasecmp (arg, "rx200") == 0)
+ rx_cpu = RX200;
+ else if (strcasecmp (arg, "rx600") == 0)
+ rx_cpu = RX600;
+ else if (strcasecmp (arg, "rx610") == 0)
+ rx_cpu = RX610;
+ else
+ {
+ as_warn (_("unrecognised RX CPU type %s"), arg);
+ break;
+ }
+ return 1;
+ }
+ return 0;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _(" RX specific command line options:\n"));
+ fprintf (stream, _(" --mbig-endian-data\n"));
+ fprintf (stream, _(" --mlittle-endian-data [default]\n"));
+ fprintf (stream, _(" --m32bit-doubles [default]\n"));
+ fprintf (stream, _(" --m64bit-doubles\n"));
+ fprintf (stream, _(" --muse-conventional-section-names\n"));
+ fprintf (stream, _(" --muse-renesas-section-names [default]\n"));
+ fprintf (stream, _(" --msmall-data-limit\n"));
+ fprintf (stream, _(" --mrelax\n"));
+ fprintf (stream, _(" --mpid\n"));
+ fprintf (stream, _(" --mint-register=<value>\n"));
+ fprintf (stream, _(" --mcpu=<rx100|rx200|rx600|rx610>\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 ();
+}
+
+static void
+rx_float_cons (int ignore ATTRIBUTE_UNUSED)
+{
+ if (elf_flags & E_FLAG_RX_64BIT_DOUBLES)
+ return float_cons ('d');
+ return float_cons ('f');
+}
+
+static char *
+rx_strcasestr (const char *string, const char *sub)
+{
+ int subl;
+ int strl;
+
+ if (!sub || !sub[0])
+ return (char *)string;
+
+ subl = strlen (sub);
+ strl = strlen (string);
+
+ while (strl >= subl)
+ {
+ /* strncasecmp is in libiberty. */
+ if (strncasecmp (string, sub, subl) == 0)
+ return (char *)string;
+
+ string ++;
+ strl --;
+ }
+ return NULL;
+}
+
+static void
+rx_include (int ignore)
+{
+ FILE * try;
+ char * path;
+ char * filename;
+ char * current_filename;
+ char * last_char;
+ char * p;
+ char * d;
+ char * f;
+ char end_char;
+ size_t len;
+
+ /* The RX version of the .INCLUDE pseudo-op does not
+ have to have the filename inside double quotes. */
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ {
+ /* Treat as the normal GAS .include pseudo-op. */
+ s_include (ignore);
+ return;
+ }
+
+ /* Get the filename. Spaces are allowed, NUL characters are not. */
+ filename = input_line_pointer;
+ last_char = find_end_of_line (filename, FALSE);
+ input_line_pointer = last_char;
+
+ while (last_char >= filename && (* last_char == ' ' || * last_char == '\n'))
+ -- last_char;
+ end_char = *(++ last_char);
+ * last_char = 0;
+ if (last_char == filename)
+ {
+ as_bad (_("no filename following .INCLUDE pseudo-op"));
+ * last_char = end_char;
+ return;
+ }
+
+ as_where (& current_filename, NULL);
+ f = (char *) xmalloc (strlen (current_filename) + strlen (filename) + 1);
+
+ /* Check the filename. If [@]..FILE[@] is found then replace
+ this with the current assembler source filename, stripped
+ of any directory prefixes or extensions. */
+ if ((p = rx_strcasestr (filename, "..file")) != NULL)
+ {
+ char * c;
+
+ len = 6; /* strlen ("..file"); */
+
+ if (p > filename && p[-1] == '@')
+ -- p, ++len;
+
+ if (p[len] == '@')
+ len ++;
+
+ for (d = c = current_filename; *c; c++)
+ if (IS_DIR_SEPARATOR (* c))
+ d = c + 1;
+ for (c = d; *c; c++)
+ if (*c == '.')
+ break;
+
+ sprintf (f, "%.*s%.*s%.*s", (int) (p - filename), filename,
+ (int) (c - d), d,
+ (int) (strlen (filename) - ((p + len) - filename)),
+ p + len);
+ }
+ else
+ strcpy (f, filename);
+
+ /* RX .INCLUDE semantics say that 'filename' is located by:
+
+ 1. If filename is absolute, just try that. Otherwise...
+
+ 2. If the current source file includes a directory component
+ then prepend that to the filename and try. Otherwise...
+
+ 3. Try any directories specified by the -I command line
+ option(s).
+
+ 4 .Try a directory specifed by the INC100 environment variable. */
+
+ if (IS_ABSOLUTE_PATH (f))
+ try = fopen (path = f, FOPEN_RT);
+ else
+ {
+ char * env = getenv ("INC100");
+
+ try = NULL;
+
+ len = strlen (current_filename);
+ if ((size_t) include_dir_maxlen > len)
+ len = include_dir_maxlen;
+ if (env && strlen (env) > len)
+ len = strlen (env);
+
+ path = (char *) xmalloc (strlen (f) + len + 5);
+
+ if (current_filename != NULL)
+ {
+ for (d = NULL, p = current_filename; *p; p++)
+ if (IS_DIR_SEPARATOR (* p))
+ d = p;
+
+ if (d != NULL)
+ {
+ sprintf (path, "%.*s/%s", (int) (d - current_filename), current_filename,
+ f);
+ try = fopen (path, FOPEN_RT);
+ }
+ }
+
+ if (try == NULL)
+ {
+ int i;
+
+ for (i = 0; i < include_dir_count; i++)
+ {
+ sprintf (path, "%s/%s", include_dirs[i], f);
+ if ((try = fopen (path, FOPEN_RT)) != NULL)
+ break;
+ }
+ }
+
+ if (try == NULL && env != NULL)
+ {
+ sprintf (path, "%s/%s", env, f);
+ try = fopen (path, FOPEN_RT);
+ }
+
+ free (f);
+ }
+
+ if (try == NULL)
+ {
+ as_bad (_("unable to locate include file: %s"), filename);
+ free (path);
+ }
+ else
+ {
+ fclose (try);
+ register_dependency (path);
+ input_scrub_insert_file (path);
+ }
+
+ * last_char = end_char;
+}
+
+static void
+parse_rx_section (char * name)
+{
+ asection * sec;
+ int type;
+ int attr = SHF_ALLOC | SHF_EXECINSTR;
+ int align = 2;
+ char end_char;
+
+ do
+ {
+ char * p;
+
+ SKIP_WHITESPACE ();
+ for (p = input_line_pointer; *p && strchr ("\n\t, =", *p) == NULL; p++)
+ ;
+ end_char = *p;
+ *p = 0;
+
+ if (strcasecmp (input_line_pointer, "ALIGN") == 0)
+ {
+ *p = end_char;
+
+ if (end_char == ' ')
+ while (ISSPACE (*p))
+ p++;
+
+ if (*p == '=')
+ {
+ ++ p;
+ while (ISSPACE (*p))
+ p++;
+ switch (*p)
+ {
+ case '2': align = 2; break;
+ case '4': align = 4; break;
+ case '8': align = 8; break;
+ default:
+ as_bad (_("unrecognised alignment value in .SECTION directive: %s"), p);
+ ignore_rest_of_line ();
+ return;
+ }
+ ++ p;
+ }
+
+ end_char = *p;
+ }
+ else if (strcasecmp (input_line_pointer, "CODE") == 0)
+ attr = SHF_ALLOC | SHF_EXECINSTR;
+ else if (strcasecmp (input_line_pointer, "DATA") == 0)
+ attr = SHF_ALLOC | SHF_WRITE;
+ else if (strcasecmp (input_line_pointer, "ROMDATA") == 0)
+ attr = SHF_ALLOC;
+ else
+ {
+ as_bad (_("unknown parameter following .SECTION directive: %s"),
+ input_line_pointer);
+
+ *p = end_char;
+ input_line_pointer = p + 1;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ *p = end_char;
+ input_line_pointer = p + 1;
+ }
+ while (end_char != '\n' && end_char != 0);
+
+ if ((sec = bfd_get_section_by_name (stdoutput, name)) == NULL)
+ {
+ if (strcmp (name, "B") && strcmp (name, "B_1") && strcmp (name, "B_2"))
+ type = SHT_NULL;
+ else
+ type = SHT_NOBITS;
+
+ obj_elf_change_section (name, type, attr, 0, NULL, FALSE, FALSE);
+ }
+ else /* Try not to redefine a section, especially B_1. */
+ {
+ int flags = sec->flags;
+
+ type = elf_section_type (sec);
+
+ attr = ((flags & SEC_READONLY) ? 0 : SHF_WRITE)
+ | ((flags & SEC_ALLOC) ? SHF_ALLOC : 0)
+ | ((flags & SEC_CODE) ? SHF_EXECINSTR : 0)
+ | ((flags & SEC_MERGE) ? SHF_MERGE : 0)
+ | ((flags & SEC_STRINGS) ? SHF_STRINGS : 0)
+ | ((flags & SEC_THREAD_LOCAL) ? SHF_TLS : 0);
+
+ obj_elf_change_section (name, type, attr, 0, NULL, FALSE, FALSE);
+ }
+
+ bfd_set_section_alignment (stdoutput, now_seg, align);
+}
+
+static void
+rx_section (int ignore)
+{
+ char * p;
+
+ /* The as100 assembler supports a different syntax for the .section
+ pseudo-op. So check for it and handle it here if necessary. */
+ SKIP_WHITESPACE ();
+
+ /* Peek past the section name to see if arguments follow. */
+ for (p = input_line_pointer; *p; p++)
+ if (*p == ',' || *p == '\n')
+ break;
+
+ if (*p == ',')
+ {
+ int len = p - input_line_pointer;
+
+ while (ISSPACE (*++p))
+ ;
+
+ if (*p != '"' && *p != '#')
+ {
+ char * name = (char *) xmalloc (len + 1);
+
+ strncpy (name, input_line_pointer, len);
+ name[len] = 0;
+
+ input_line_pointer = p;
+ parse_rx_section (name);
+ return;
+ }
+ }
+
+ obj_elf_section (ignore);
+}
+
+static void
+rx_list (int ignore ATTRIBUTE_UNUSED)
+{
+ SKIP_WHITESPACE ();
+
+ if (strncasecmp (input_line_pointer, "OFF", 3))
+ listing_list (0);
+ else if (strncasecmp (input_line_pointer, "ON", 2))
+ listing_list (1);
+ else
+ as_warn (_("expecting either ON or OFF after .list"));
+}
+
+/* Like the .rept pseudo op, but supports the
+ use of ..MACREP inside the repeated region. */
+
+static void
+rx_rept (int ignore ATTRIBUTE_UNUSED)
+{
+ int count = get_absolute_expression ();
+
+ do_repeat_with_expander (count, "MREPEAT", "ENDR", "..MACREP");
+}
+
+/* Like cons() accept that strings are allowed. */
+
+static void
+rx_cons (int size)
+{
+ SKIP_WHITESPACE ();
+
+ if (* input_line_pointer == '"')
+ stringer (8+0);
+ else
+ cons (size);
+}
+
+static void
+rx_nop (int ignore ATTRIBUTE_UNUSED)
+{
+ ignore_rest_of_line ();
+}
+
+static void
+rx_unimp (int idx)
+{
+ as_warn (_("The \".%s\" pseudo-op is not implemented\n"),
+ md_pseudo_table[idx].poc_name);
+ ignore_rest_of_line ();
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* These are unimplemented. They're listed first so that we can use
+ the poc_value as the index into this array, to get the name of
+ the pseudo. So, keep these (1) first, and (2) in order, with (3)
+ the poc_value's in sequence. */
+ { "btglb", rx_unimp, 0 },
+ { "call", rx_unimp, 1 },
+ { "einsf", rx_unimp, 2 },
+ { "fb", rx_unimp, 3 },
+ { "fbsym", rx_unimp, 4 },
+ { "id", rx_unimp, 5 },
+ { "initsct", rx_unimp, 6 },
+ { "insf", rx_unimp, 7 },
+ { "instr", rx_unimp, 8 },
+ { "lbba", rx_unimp, 9 },
+ { "len", rx_unimp, 10 },
+ { "optj", rx_unimp, 11 },
+ { "rvector", rx_unimp, 12 },
+ { "sb", rx_unimp, 13 },
+ { "sbbit", rx_unimp, 14 },
+ { "sbsym", rx_unimp, 15 },
+ { "sbsym16", rx_unimp, 16 },
+
+ /* These are the do-nothing pseudos. */
+ { "stk", rx_nop, 0 },
+ /* The manual documents ".stk" but the compiler emits ".stack". */
+ { "stack", rx_nop, 0 },
+
+ /* These are Renesas as100 assembler pseudo-ops that we do support. */
+ { "addr", rx_cons, 3 },
+ { "align", s_align_bytes, 2 },
+ { "byte", rx_cons, 1 },
+ { "fixed", float_cons, 'f' },
+ { "form", listing_psize, 0 },
+ { "glb", s_globl, 0 },
+ { "include", rx_include, 0 },
+ { "list", rx_list, 0 },
+ { "lword", rx_cons, 4 },
+ { "mrepeat", rx_rept, 0 },
+ { "section", rx_section, 0 },
+
+ /* FIXME: The following pseudo-ops place their values (and associated
+ label if present) in the data section, regardless of whatever
+ section we are currently in. At the moment this code does not
+ implement that part of the semantics. */
+ { "blka", s_space, 3 },
+ { "blkb", s_space, 1 },
+ { "blkd", s_space, 8 },
+ { "blkf", s_space, 4 },
+ { "blkl", s_space, 4 },
+ { "blkw", s_space, 2 },
+
+ /* Our "standard" pseudos. */
+ { "double", rx_float_cons, 0 },
+ { "bss", s_bss, 0 },
+ { "3byte", cons, 3 },
+ { "int", cons, 4 },
+ { "word", cons, 4 },
+
+ { "fetchalign", rx_fetchalign, 0 },
+
+ /* End of list marker. */
+ { NULL, NULL, 0 }
+};
+
+static asymbol * gp_symbol;
+static asymbol * rx_pid_symbol;
+
+static symbolS * rx_pidreg_symbol;
+static symbolS * rx_gpreg_symbol;
+
+void
+md_begin (void)
+{
+ /* Make the __gp and __pid_base symbols now rather
+ than after the symbol table is frozen. We only do this
+ when supporting small data limits because otherwise we
+ pollute the symbol table. */
+
+ /* The meta-registers %pidreg and %gpreg depend on what other
+ options are specified. The __rx_*_defined symbols exist so we
+ can .ifdef asm code based on what options were passed to gas,
+ without needing a preprocessor */
+
+ if (rx_pid_mode)
+ {
+ rx_pid_register = 13 - rx_num_int_regs;
+ rx_pid_symbol = symbol_get_bfdsym (symbol_find_or_make ("__pid_base"));
+ rx_pidreg_symbol = symbol_find_or_make ("__rx_pidreg_defined");
+ S_SET_VALUE (rx_pidreg_symbol, rx_pid_register);
+ S_SET_SEGMENT (rx_pidreg_symbol, absolute_section);
+ }
+
+ if (rx_use_small_data_limit)
+ {
+ if (rx_pid_mode)
+ rx_gp_register = rx_pid_register - 1;
+ else
+ rx_gp_register = 13 - rx_num_int_regs;
+ gp_symbol = symbol_get_bfdsym (symbol_find_or_make ("__gp"));
+ rx_gpreg_symbol = symbol_find_or_make ("__rx_gpreg_defined");
+ S_SET_VALUE (rx_gpreg_symbol, rx_gp_register);
+ S_SET_SEGMENT (rx_gpreg_symbol, absolute_section);
+ }
+}
+
+char * rx_lex_start;
+char * rx_lex_end;
+
+/* These negative numbers are found in rx_bytesT.n_base for non-opcode
+ md_frags */
+#define RX_NBASE_FETCHALIGN -1
+
+typedef struct rx_bytesT
+{
+ char base[4];
+ /* If this is negative, it's a special-purpose frag as per the defines above. */
+ int n_base;
+ char ops[8];
+ int n_ops;
+ struct
+ {
+ expressionS exp;
+ char offset;
+ char nbits;
+ char type; /* RXREL_*. */
+ int reloc;
+ fixS * fixP;
+ } fixups[2];
+ int n_fixups;
+ struct
+ {
+ char type;
+ char field_pos;
+ char val_ofs;
+ } relax[2];
+ int n_relax;
+ int link_relax;
+ fixS *link_relax_fixP;
+ char times_grown;
+ char times_shrank;
+} rx_bytesT;
+
+static rx_bytesT rx_bytes;
+/* We set n_ops to be "size of next opcode" if the next opcode doesn't relax. */
+static rx_bytesT *fetchalign_bytes = NULL;
+
+static void
+rx_fetchalign (int ignore ATTRIBUTE_UNUSED)
+{
+ char * bytes;
+ fragS * frag_then;
+
+ memset (& rx_bytes, 0, sizeof (rx_bytes));
+ rx_bytes.n_base = RX_NBASE_FETCHALIGN;
+
+ bytes = frag_more (8);
+ frag_then = frag_now;
+ frag_variant (rs_machine_dependent,
+ 0 /* max_chars */,
+ 0 /* var */,
+ 0 /* subtype */,
+ 0 /* symbol */,
+ 0 /* offset */,
+ 0 /* opcode */);
+ frag_then->fr_opcode = bytes;
+ frag_then->fr_subtype = 0;
+ fetchalign_bytes = frag_then->tc_frag_data;
+}
+
+void
+rx_relax (int type, int pos)
+{
+ rx_bytes.relax[rx_bytes.n_relax].type = type;
+ rx_bytes.relax[rx_bytes.n_relax].field_pos = pos;
+ rx_bytes.relax[rx_bytes.n_relax].val_ofs = rx_bytes.n_base + rx_bytes.n_ops;
+ rx_bytes.n_relax ++;
+}
+
+void
+rx_linkrelax_dsp (int pos)
+{
+ switch (pos)
+ {
+ case 4:
+ rx_bytes.link_relax |= RX_RELAXA_DSP4;
+ break;
+ case 6:
+ rx_bytes.link_relax |= RX_RELAXA_DSP6;
+ break;
+ case 14:
+ rx_bytes.link_relax |= RX_RELAXA_DSP14;
+ break;
+ }
+}
+
+void
+rx_linkrelax_imm (int pos)
+{
+ switch (pos)
+ {
+ case 6:
+ rx_bytes.link_relax |= RX_RELAXA_IMM6;
+ break;
+ case 12:
+ rx_bytes.link_relax |= RX_RELAXA_IMM12;
+ break;
+ }
+}
+
+void
+rx_linkrelax_branch (void)
+{
+ rx_bytes.link_relax |= RX_RELAXA_BRA;
+}
+
+static void
+rx_fixup (expressionS exp, int offsetbits, int nbits, int type)
+{
+ rx_bytes.fixups[rx_bytes.n_fixups].exp = exp;
+ rx_bytes.fixups[rx_bytes.n_fixups].offset = offsetbits;
+ rx_bytes.fixups[rx_bytes.n_fixups].nbits = nbits;
+ rx_bytes.fixups[rx_bytes.n_fixups].type = type;
+ rx_bytes.fixups[rx_bytes.n_fixups].reloc = exp.X_md;
+ rx_bytes.n_fixups ++;
+}
+
+#define rx_field_fixup(exp, offset, nbits, type) \
+ rx_fixup (exp, offset, nbits, type)
+
+#define rx_op_fixup(exp, offset, nbits, type) \
+ rx_fixup (exp, offset + 8 * rx_bytes.n_base, nbits, type)
+
+void
+rx_base1 (int b1)
+{
+ rx_bytes.base[0] = b1;
+ rx_bytes.n_base = 1;
+}
+
+void
+rx_base2 (int b1, int b2)
+{
+ rx_bytes.base[0] = b1;
+ rx_bytes.base[1] = b2;
+ rx_bytes.n_base = 2;
+}
+
+void
+rx_base3 (int b1, int b2, int b3)
+{
+ rx_bytes.base[0] = b1;
+ rx_bytes.base[1] = b2;
+ rx_bytes.base[2] = b3;
+ rx_bytes.n_base = 3;
+}
+
+void
+rx_base4 (int b1, int b2, int b3, int b4)
+{
+ rx_bytes.base[0] = b1;
+ rx_bytes.base[1] = b2;
+ rx_bytes.base[2] = b3;
+ rx_bytes.base[3] = b4;
+ rx_bytes.n_base = 4;
+}
+
+/* This gets complicated when the field spans bytes, because fields
+ are numbered from the MSB of the first byte as zero, and bits are
+ stored LSB towards the LSB of the byte. Thus, a simple four-bit
+ insertion of 12 at position 4 of 0x00 yields: 0x0b. A three-bit
+ insertion of b'MXL at position 7 is like this:
+
+ - - - - - - - - - - - - - - - -
+ M X L */
+
+void
+rx_field (int val, int pos, int sz)
+{
+ int valm;
+ int bytep, bitp;
+
+ if (sz > 0)
+ {
+ if (val < 0 || val >= (1 << sz))
+ as_bad (_("Value %d doesn't fit in unsigned %d-bit field"), val, sz);
+ }
+ else
+ {
+ sz = - sz;
+ if (val < -(1 << (sz - 1)) || val >= (1 << (sz - 1)))
+ as_bad (_("Value %d doesn't fit in signed %d-bit field"), val, sz);
+ }
+
+ /* This code points at 'M' in the above example. */
+ bytep = pos / 8;
+ bitp = pos % 8;
+
+ while (bitp + sz > 8)
+ {
+ int ssz = 8 - bitp;
+ int svalm;
+
+ svalm = val >> (sz - ssz);
+ svalm = svalm & ((1 << ssz) - 1);
+ svalm = svalm << (8 - bitp - ssz);
+ gas_assert (bytep < rx_bytes.n_base);
+ rx_bytes.base[bytep] |= svalm;
+
+ bitp = 0;
+ sz -= ssz;
+ bytep ++;
+ }
+ valm = val & ((1 << sz) - 1);
+ valm = valm << (8 - bitp - sz);
+ gas_assert (bytep < rx_bytes.n_base);
+ rx_bytes.base[bytep] |= valm;
+}
+
+/* Special case of the above, for 3-bit displacements of 2..9. */
+
+void
+rx_disp3 (expressionS exp, int pos)
+{
+ rx_field_fixup (exp, pos, 3, RXREL_PCREL);
+}
+
+/* Special case of the above, for split 5-bit displacements. Assumes
+ the displacement has been checked with rx_disp5op. */
+/* ---- -432 1--- 0--- */
+
+void
+rx_field5s (expressionS exp)
+{
+ int val;
+
+ val = exp.X_add_number;
+ rx_bytes.base[0] |= val >> 2;
+ rx_bytes.base[1] |= (val << 6) & 0x80;
+ rx_bytes.base[1] |= (val << 3) & 0x08;
+}
+
+/* ---- ---- 4--- 3210 */
+
+void
+rx_field5s2 (expressionS exp)
+{
+ int val;
+
+ val = exp.X_add_number;
+ rx_bytes.base[1] |= (val << 3) & 0x80;
+ rx_bytes.base[1] |= (val ) & 0x0f;
+}
+
+#define OP(x) rx_bytes.ops[rx_bytes.n_ops++] = (x)
+
+#define F_PRECISION 2
+
+void
+rx_op (expressionS exp, int nbytes, int type)
+{
+ int v = 0;
+
+ if ((exp.X_op == O_constant || exp.X_op == O_big)
+ && type != RXREL_PCREL)
+ {
+ if (exp.X_op == O_big && exp.X_add_number <= 0)
+ {
+ LITTLENUM_TYPE w[2];
+ char * ip = rx_bytes.ops + rx_bytes.n_ops;
+
+ gen_to_words (w, F_PRECISION, 8);
+#if RX_OPCODE_BIG_ENDIAN
+ ip[0] = w[0] >> 8;
+ ip[1] = w[0];
+ ip[2] = w[1] >> 8;
+ ip[3] = w[1];
+#else
+ ip[3] = w[0] >> 8;
+ ip[2] = w[0];
+ ip[1] = w[1] >> 8;
+ ip[0] = w[1];
+#endif
+ rx_bytes.n_ops += 4;
+ }
+ else
+ {
+ v = exp.X_add_number;
+ while (nbytes)
+ {
+#if RX_OPCODE_BIG_ENDIAN
+ OP ((v >> (8 * (nbytes - 1))) & 0xff);
+#else
+ OP (v & 0xff);
+ v >>= 8;
+#endif
+ nbytes --;
+ }
+ }
+ }
+ else
+ {
+ rx_op_fixup (exp, rx_bytes.n_ops * 8, nbytes * 8, type);
+ memset (rx_bytes.ops + rx_bytes.n_ops, 0, nbytes);
+ rx_bytes.n_ops += nbytes;
+ }
+}
+
+int
+rx_wrap (void)
+{
+ return 0;
+}
+
+#define APPEND(B, N_B) \
+ if (rx_bytes.N_B) \
+ { \
+ memcpy (bytes + idx, rx_bytes.B, rx_bytes.N_B); \
+ idx += rx_bytes.N_B; \
+ }
+
+void
+rx_frag_init (fragS * fragP)
+{
+ if (rx_bytes.n_relax || rx_bytes.link_relax || rx_bytes.n_base < 0)
+ {
+ fragP->tc_frag_data = malloc (sizeof (rx_bytesT));
+ memcpy (fragP->tc_frag_data, & rx_bytes, sizeof (rx_bytesT));
+ }
+ else
+ fragP->tc_frag_data = 0;
+}
+
+/* Handle the as100's version of the .equ pseudo-op. It has the syntax:
+ <symbol_name> .equ <expression> */
+
+static void
+rx_equ (char * name, char * expression)
+{
+ char saved_name_end_char;
+ char * name_end;
+ char * saved_ilp;
+
+ while (ISSPACE (* name))
+ name ++;
+
+ for (name_end = name + 1; *name_end; name_end ++)
+ if (! ISALNUM (* name_end))
+ break;
+
+ saved_name_end_char = * name_end;
+ * name_end = 0;
+
+ saved_ilp = input_line_pointer;
+ input_line_pointer = expression;
+
+ equals (name, 1);
+
+ input_line_pointer = saved_ilp;
+ * name_end = saved_name_end_char;
+}
+
+/* Look for Renesas as100 pseudo-ops that occur after a symbol name
+ rather than at the start of a line. (eg .EQU or .DEFINE). If one
+ is found, process it and return TRUE otherwise return FALSE. */
+
+static bfd_boolean
+scan_for_infix_rx_pseudo_ops (char * str)
+{
+ char * p;
+ char * pseudo_op;
+ char * dot = strchr (str, '.');
+
+ if (dot == NULL || dot == str)
+ return FALSE;
+
+ /* A real pseudo-op must be preceeded by whitespace. */
+ if (dot[-1] != ' ' && dot[-1] != '\t')
+ return FALSE;
+
+ pseudo_op = dot + 1;
+
+ if (!ISALNUM (* pseudo_op))
+ return FALSE;
+
+ for (p = pseudo_op + 1; ISALNUM (* p); p++)
+ ;
+
+ if (strncasecmp ("EQU", pseudo_op, p - pseudo_op) == 0)
+ rx_equ (str, p);
+ else if (strncasecmp ("DEFINE", pseudo_op, p - pseudo_op) == 0)
+ as_warn (_("The .DEFINE pseudo-op is not implemented"));
+ else if (strncasecmp ("MACRO", pseudo_op, p - pseudo_op) == 0)
+ as_warn (_("The .MACRO pseudo-op is not implemented"));
+ else if (strncasecmp ("BTEQU", pseudo_op, p - pseudo_op) == 0)
+ as_warn (_("The .BTEQU pseudo-op is not implemented."));
+ else
+ return FALSE;
+
+ return TRUE;
+}
+
+void
+md_assemble (char * str)
+{
+ char * bytes;
+ int idx = 0;
+ int i, rel;
+ fragS * frag_then = frag_now;
+ expressionS *exp;
+
+ memset (& rx_bytes, 0, sizeof (rx_bytes));
+
+ rx_lex_init (str, str + strlen (str));
+ if (scan_for_infix_rx_pseudo_ops (str))
+ return;
+ rx_parse ();
+
+ /* This simplifies the relaxation code. */
+ if (rx_bytes.n_relax || rx_bytes.link_relax)
+ {
+ /* We do it this way because we want the frag to have the
+ rx_bytes in it, which we initialize above. */
+ bytes = frag_more (12);
+ frag_then = frag_now;
+ frag_variant (rs_machine_dependent,
+ 0 /* max_chars */,
+ 0 /* var */,
+ 0 /* subtype */,
+ 0 /* symbol */,
+ 0 /* offset */,
+ 0 /* opcode */);
+ frag_then->fr_opcode = bytes;
+ frag_then->fr_fix += rx_bytes.n_base + rx_bytes.n_ops;
+ frag_then->fr_subtype = rx_bytes.n_base + rx_bytes.n_ops;
+ }
+ else
+ {
+ bytes = frag_more (rx_bytes.n_base + rx_bytes.n_ops);
+ frag_then = frag_now;
+ if (fetchalign_bytes)
+ fetchalign_bytes->n_ops = rx_bytes.n_base + rx_bytes.n_ops;
+ }
+
+ fetchalign_bytes = NULL;
+
+ APPEND (base, n_base);
+ APPEND (ops, n_ops);
+
+ if (rx_bytes.link_relax && rx_bytes.n_fixups)
+ {
+ fixS * f;
+
+ f = fix_new (frag_then,
+ (char *) bytes - frag_then->fr_literal,
+ 0,
+ abs_section_sym,
+ rx_bytes.link_relax | rx_bytes.n_fixups,
+ 0,
+ BFD_RELOC_RX_RELAX);
+ frag_then->tc_frag_data->link_relax_fixP = f;
+ }
+
+ for (i = 0; i < rx_bytes.n_fixups; i ++)
+ {
+ /* index: [nbytes][type] */
+ static int reloc_map[5][4] =
+ {
+ { 0, 0, 0, BFD_RELOC_RX_DIR3U_PCREL },
+ { BFD_RELOC_8, BFD_RELOC_RX_8U, BFD_RELOC_RX_NEG8, BFD_RELOC_8_PCREL },
+ { BFD_RELOC_RX_16_OP, BFD_RELOC_RX_16U, BFD_RELOC_RX_NEG16, BFD_RELOC_16_PCREL },
+ { BFD_RELOC_RX_24_OP, BFD_RELOC_RX_24U, BFD_RELOC_RX_NEG24, BFD_RELOC_24_PCREL },
+ { BFD_RELOC_RX_32_OP, BFD_RELOC_32, BFD_RELOC_RX_NEG32, BFD_RELOC_32_PCREL },
+ };
+ fixS * f;
+
+ idx = rx_bytes.fixups[i].offset / 8;
+ rel = reloc_map [rx_bytes.fixups[i].nbits / 8][(int) rx_bytes.fixups[i].type];
+
+ if (rx_bytes.fixups[i].reloc)
+ rel = rx_bytes.fixups[i].reloc;
+
+ if (frag_then->tc_frag_data)
+ exp = & frag_then->tc_frag_data->fixups[i].exp;
+ else
+ exp = & rx_bytes.fixups[i].exp;
+
+ f = fix_new_exp (frag_then,
+ (char *) bytes + idx - frag_then->fr_literal,
+ rx_bytes.fixups[i].nbits / 8,
+ exp,
+ rx_bytes.fixups[i].type == RXREL_PCREL ? 1 : 0,
+ rel);
+ if (frag_then->tc_frag_data)
+ frag_then->tc_frag_data->fixups[i].fixP = f;
+ }
+
+ dwarf2_emit_insn (idx);
+}
+
+void
+rx_md_end (void)
+{
+}
+
+/* Write a value out to the object file, using the appropriate endianness. */
+
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+static struct
+{
+ char * fname;
+ int reloc;
+}
+reloc_functions[] =
+{
+ { "gp", BFD_RELOC_GPREL16 },
+ { 0, 0 }
+};
+
+void
+md_operand (expressionS * exp ATTRIBUTE_UNUSED)
+{
+ int reloc = 0;
+ int i;
+
+ for (i = 0; reloc_functions[i].fname; i++)
+ {
+ int flen = strlen (reloc_functions[i].fname);
+
+ if (input_line_pointer[0] == '%'
+ && strncasecmp (input_line_pointer + 1, reloc_functions[i].fname, flen) == 0
+ && input_line_pointer[flen + 1] == '(')
+ {
+ reloc = reloc_functions[i].reloc;
+ input_line_pointer += flen + 2;
+ break;
+ }
+ }
+ if (reloc == 0)
+ return;
+
+ expression (exp);
+ if (* input_line_pointer == ')')
+ input_line_pointer ++;
+
+ exp->X_md = reloc;
+}
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+ /* NOP - 1 cycle */
+static unsigned char nop_1[] = { 0x03};
+ /* MOV.L R0,R0 - 1 cycle */
+static unsigned char nop_2[] = { 0xef, 0x00};
+ /* MAX R0,R0 - 1 cycle */
+static unsigned char nop_3[] = { 0xfc, 0x13, 0x00 };
+ /* MUL #1,R0 - 1 cycle */
+static unsigned char nop_4[] = { 0x76, 0x10, 0x01, 0x00 };
+ /* MUL #1,R0 - 1 cycle */
+static unsigned char nop_5[] = { 0x77, 0x10, 0x01, 0x00, 0x00 };
+ /* MUL #1,R0 - 1 cycle */
+static unsigned char nop_6[] = { 0x74, 0x10, 0x01, 0x00, 0x00, 0x00 };
+ /* BRA.S .+7 - 1 cycle */
+static unsigned char nop_7[] = { 0x0F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03 };
+
+static unsigned char *nops[] = { NULL, nop_1, nop_2, nop_3, nop_4, nop_5, nop_6, nop_7 };
+#define BIGGEST_NOP 7
+
+/* When relaxing, we need to output a reloc for any .align directive
+ so that we can retain this alignment as we adjust opcode sizes. */
+void
+rx_handle_align (fragS * frag)
+{
+ /* If handling an alignment frag, use an optimal NOP pattern.
+ Only do this if a fill value has not already been provided.
+ FIXME: This test fails if the provided fill value is zero. */
+ if ((frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
+ && subseg_text_p (now_seg))
+ {
+ int count = (frag->fr_next->fr_address
+ - frag->fr_address
+ - frag->fr_fix);
+ unsigned char *base = (unsigned char *)frag->fr_literal + frag->fr_fix;
+
+ if (* base == 0)
+ {
+ if (count > BIGGEST_NOP)
+ {
+ base[0] = 0x2e;
+ base[1] = count;
+ frag->fr_var = 2;
+ }
+ else if (count > 0)
+ {
+ memcpy (base, nops[count], count);
+ frag->fr_var = count;
+ }
+ }
+ }
+
+ if (linkrelax
+ && (frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 0
+ && now_seg != bss_section)
+ {
+ fix_new (frag, frag->fr_fix, 0,
+ &abs_symbol, RX_RELAXA_ALIGN + frag->fr_offset,
+ 0, BFD_RELOC_RX_RELAX);
+ /* For the purposes of relaxation, this relocation is attached
+ to the byte *after* the alignment - i.e. the byte that must
+ remain aligned. */
+ fix_new (frag->fr_next, 0, 0,
+ &abs_symbol, RX_RELAXA_ELIGN + frag->fr_offset,
+ 0, BFD_RELOC_RX_RELAX);
+ }
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+symbolS *
+md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/*----------------------------------------------------------------------*/
+/* To recap: we estimate everything based on md_estimate_size, then
+ adjust based on rx_relax_frag. When it all settles, we call
+ md_convert frag to update the bytes. The relaxation types and
+ relocations are in fragP->tc_frag_data, which is a copy of that
+ rx_bytes.
+
+ Our scheme is as follows: fr_fix has the size of the smallest
+ opcode (like BRA.S). We store the number of total bytes we need in
+ fr_subtype. When we're done relaxing, we use fr_subtype and the
+ existing opcode bytes to figure out what actual opcode we need to
+ put in there. If the fixup isn't resolvable now, we use the
+ maximal size. */
+
+#define TRACE_RELAX 0
+#define tprintf if (TRACE_RELAX) printf
+
+typedef enum
+{
+ OT_other,
+ OT_bra,
+ OT_beq,
+ OT_bne,
+ OT_bsr,
+ OT_bcc
+} op_type_T;
+
+/* We're looking for these types of relaxations:
+
+ BRA.S 00001dsp
+ BRA.B 00101110 dspppppp
+ BRA.W 00111000 dspppppp pppppppp
+ BRA.A 00000100 dspppppp pppppppp pppppppp
+
+ BEQ.S 00010dsp
+ BEQ.B 00100000 dspppppp
+ BEQ.W 00111010 dspppppp pppppppp
+
+ BNE.S 00011dsp
+ BNE.B 00100001 dspppppp
+ BNE.W 00111011 dspppppp pppppppp
+
+ BSR.W 00111001 dspppppp pppppppp
+ BSR.A 00000101 dspppppp pppppppp pppppppp
+
+ Bcc.B 0010cond dspppppp
+
+ Additionally, we can synthesize longer conditional branches using
+ pairs of opcodes, one with an inverted conditional (flip LSB):
+
+ Bcc.W 0010ncnd 00000110 00111000 dspppppp pppppppp
+ Bcc.A 0010ncnd 00000111 00000100 dspppppp pppppppp pppppppp
+ BEQ.A 00011100 00000100 dspppppp pppppppp pppppppp
+ BNE.A 00010100 00000100 dspppppp pppppppp pppppppp */
+
+/* Given the opcode bytes at OP, figure out which opcode it is and
+ return the type of opcode. We use this to re-encode the opcode as
+ a different size later. */
+
+static op_type_T
+rx_opcode_type (char * op)
+{
+ unsigned char b = (unsigned char) op[0];
+
+ switch (b & 0xf8)
+ {
+ case 0x08: return OT_bra;
+ case 0x10: return OT_beq;
+ case 0x18: return OT_bne;
+ }
+
+ switch (b)
+ {
+ case 0x2e: return OT_bra;
+ case 0x38: return OT_bra;
+ case 0x04: return OT_bra;
+
+ case 0x20: return OT_beq;
+ case 0x3a: return OT_beq;
+
+ case 0x21: return OT_bne;
+ case 0x3b: return OT_bne;
+
+ case 0x39: return OT_bsr;
+ case 0x05: return OT_bsr;
+ }
+
+ if ((b & 0xf0) == 0x20)
+ return OT_bcc;
+
+ return OT_other;
+}
+
+/* Returns zero if *addrP has the target address. Else returns nonzero
+ if we cannot compute the target address yet. */
+
+static int
+rx_frag_fix_value (fragS * fragP,
+ segT segment,
+ int which,
+ addressT * addrP,
+ int need_diff,
+ addressT * sym_addr)
+{
+ addressT addr = 0;
+ rx_bytesT * b = fragP->tc_frag_data;
+ expressionS * exp = & b->fixups[which].exp;
+
+ if (need_diff && exp->X_op != O_subtract)
+ return 1;
+
+ if (exp->X_add_symbol)
+ {
+ if (S_FORCE_RELOC (exp->X_add_symbol, 1))
+ return 1;
+ if (S_GET_SEGMENT (exp->X_add_symbol) != segment)
+ return 1;
+ addr += S_GET_VALUE (exp->X_add_symbol);
+ }
+
+ if (exp->X_op_symbol)
+ {
+ if (exp->X_op != O_subtract)
+ return 1;
+ if (S_FORCE_RELOC (exp->X_op_symbol, 1))
+ return 1;
+ if (S_GET_SEGMENT (exp->X_op_symbol) != segment)
+ return 1;
+ addr -= S_GET_VALUE (exp->X_op_symbol);
+ }
+ if (sym_addr)
+ * sym_addr = addr;
+ addr += exp->X_add_number;
+ * addrP = addr;
+ return 0;
+}
+
+/* Estimate how big the opcode is after this relax pass. The return
+ value is the difference between fr_fix and the actual size. We
+ compute the total size in rx_relax_frag and store it in fr_subtype,
+ sowe only need to subtract fx_fix and return it. */
+
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED, segT segment ATTRIBUTE_UNUSED)
+{
+ int opfixsize;
+ int delta;
+
+ tprintf ("\033[32m est frag: addr %08lx fix %ld var %ld ofs %ld lit %p opc %p type %d sub %d\033[0m\n",
+ (unsigned long) (fragP->fr_address
+ + (fragP->fr_opcode - fragP->fr_literal)),
+ (long) fragP->fr_fix, (long) fragP->fr_var, (long) fragP->fr_offset,
+ fragP->fr_literal, fragP->fr_opcode, fragP->fr_type, fragP->fr_subtype);
+
+ /* This is the size of the opcode that's accounted for in fr_fix. */
+ opfixsize = fragP->fr_fix - (fragP->fr_opcode - fragP->fr_literal);
+ /* This is the size of the opcode that isn't. */
+ delta = (fragP->fr_subtype - opfixsize);
+
+ tprintf (" -> opfixsize %d delta %d\n", opfixsize, delta);
+ return delta;
+}
+
+/* Given a frag FRAGP, return the "next" frag that contains an
+ opcode. Assumes the next opcode is relaxable, and thus rs_machine_dependent. */
+
+static fragS *
+rx_next_opcode (fragS *fragP)
+{
+ do {
+ fragP = fragP->fr_next;
+ } while (fragP && fragP->fr_type != rs_machine_dependent);
+ return fragP;
+}
+
+/* Given the new addresses for this relax pass, figure out how big
+ each opcode must be. We store the total number of bytes needed in
+ fr_subtype. The return value is the difference between the size
+ after the last pass and the size after this pass, so we use the old
+ fr_subtype to calculate the difference. */
+
+int
+rx_relax_frag (segT segment ATTRIBUTE_UNUSED, fragS * fragP, long stretch)
+{
+ addressT addr0, sym_addr;
+ addressT mypc;
+ int disp;
+ int oldsize = fragP->fr_subtype;
+ int newsize = oldsize;
+ op_type_T optype;
+ /* Index of relaxation we care about. */
+ int ri;
+
+ tprintf ("\033[36mrelax frag: addr %08lx fix %ld var %ld ofs %ld lit %p opc %p type %d sub %d str %ld\033[0m\n",
+ (unsigned long) (fragP->fr_address
+ + (fragP->fr_opcode - fragP->fr_literal)),
+ (long) fragP->fr_fix, (long) fragP->fr_var, (long) fragP->fr_offset,
+ fragP->fr_literal, fragP->fr_opcode, fragP->fr_type, fragP->fr_subtype, stretch);
+
+ mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+
+ if (fragP->tc_frag_data->n_base == RX_NBASE_FETCHALIGN)
+ {
+ unsigned int next_size;
+ if (fragP->fr_next == NULL)
+ return 0;
+
+ next_size = fragP->tc_frag_data->n_ops;
+ if (next_size == 0)
+ {
+ fragS *n = rx_next_opcode (fragP);
+ next_size = n->fr_subtype;
+ }
+
+ fragP->fr_subtype = (8-(mypc & 7)) & 7;
+ tprintf("subtype %u\n", fragP->fr_subtype);
+ if (fragP->fr_subtype >= next_size)
+ fragP->fr_subtype = 0;
+ tprintf ("\033[34m -> mypc %lu next_size %u new %d old %d delta %d (fetchalign)\033[0m\n",
+ mypc & 7,
+ next_size, fragP->fr_subtype, oldsize, fragP->fr_subtype-oldsize);
+
+ newsize = fragP->fr_subtype;
+
+ return newsize - oldsize;
+ }
+
+ optype = rx_opcode_type (fragP->fr_opcode);
+
+ /* In the one case where we have both a disp and imm relaxation, we want
+ the imm relaxation here. */
+ ri = 0;
+ if (fragP->tc_frag_data->n_relax > 1
+ && fragP->tc_frag_data->relax[0].type == RX_RELAX_DISP)
+ ri = 1;
+
+ /* Try to get the target address. */
+ if (rx_frag_fix_value (fragP, segment, ri, & addr0,
+ fragP->tc_frag_data->relax[ri].type != RX_RELAX_BRANCH,
+ & sym_addr))
+ {
+ /* If we don't, we must use the maximum size for the linker.
+ Note that we don't use synthetically expanded conditionals
+ for this. */
+ switch (fragP->tc_frag_data->relax[ri].type)
+ {
+ case RX_RELAX_BRANCH:
+ switch (optype)
+ {
+ case OT_bra:
+ case OT_bsr:
+ newsize = 4;
+ break;
+ case OT_beq:
+ case OT_bne:
+ newsize = 3;
+ break;
+ case OT_bcc:
+ newsize = 2;
+ break;
+ case OT_other:
+ newsize = oldsize;
+ break;
+ }
+ break;
+
+ case RX_RELAX_IMM:
+ newsize = fragP->tc_frag_data->relax[ri].val_ofs + 4;
+ break;
+ }
+ fragP->fr_subtype = newsize;
+ tprintf (" -> new %d old %d delta %d (external)\n", newsize, oldsize, newsize-oldsize);
+ return newsize - oldsize;
+ }
+
+ if (sym_addr > mypc)
+ addr0 += stretch;
+
+ switch (fragP->tc_frag_data->relax[ri].type)
+ {
+ case RX_RELAX_BRANCH:
+ tprintf ("branch, addr %08lx pc %08lx disp %ld\n",
+ (unsigned long) addr0, (unsigned long) mypc,
+ (long) (addr0 - mypc));
+ disp = (int) addr0 - (int) mypc;
+
+ switch (optype)
+ {
+ case OT_bcc:
+ if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+ /* bcc.b */
+ newsize = 2;
+ else if (disp >= -32768 && (disp - (oldsize-5)) <= 32767)
+ /* bncc.b/bra.w */
+ newsize = 5;
+ else
+ /* bncc.b/bra.a */
+ newsize = 6;
+ break;
+
+ case OT_beq:
+ case OT_bne:
+ if ((disp - (oldsize-1)) >= 3 && (disp - (oldsize-1)) <= 10 && !linkrelax)
+ /* beq.s */
+ newsize = 1;
+ else if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+ /* beq.b */
+ newsize = 2;
+ else if (disp >= -32768 && (disp - (oldsize-3)) <= 32767)
+ /* beq.w */
+ newsize = 3;
+ else
+ /* bne.s/bra.a */
+ newsize = 5;
+ break;
+
+ case OT_bra:
+ case OT_bsr:
+ if ((disp - (oldsize-1)) >= 3 && (disp - (oldsize-1)) <= 10 && !linkrelax)
+ /* bra.s */
+ newsize = 1;
+ else if (disp >= -128 && (disp - (oldsize-2)) <= 127)
+ /* bra.b */
+ newsize = 2;
+ else if (disp >= -32768 && (disp - (oldsize-3)) <= 32767)
+ /* bra.w */
+ newsize = 3;
+ else
+ /* bra.a */
+ newsize = 4;
+ break;
+
+ case OT_other:
+ break;
+ }
+ tprintf (" - newsize %d\n", newsize);
+ break;
+
+ case RX_RELAX_IMM:
+ tprintf ("other, addr %08lx pc %08lx LI %d OF %d\n",
+ (unsigned long) addr0, (unsigned long) mypc,
+ fragP->tc_frag_data->relax[ri].field_pos,
+ fragP->tc_frag_data->relax[ri].val_ofs);
+
+ newsize = fragP->tc_frag_data->relax[ri].val_ofs;
+
+ if ((long) addr0 >= -128 && (long) addr0 <= 127)
+ newsize += 1;
+ else if ((long) addr0 >= -32768 && (long) addr0 <= 32767)
+ newsize += 2;
+ else if ((long) addr0 >= -8388608 && (long) addr0 <= 8388607)
+ newsize += 3;
+ else
+ newsize += 4;
+ break;
+
+ default:
+ break;
+ }
+
+ if (fragP->tc_frag_data->relax[ri].type == RX_RELAX_BRANCH)
+ switch (optype)
+ {
+ case OT_bra:
+ case OT_bcc:
+ case OT_beq:
+ case OT_bne:
+ break;
+ case OT_bsr:
+ if (newsize < 3)
+ newsize = 3;
+ break;
+ case OT_other:
+ break;
+ }
+
+ /* This prevents infinite loops in align-heavy sources. */
+ if (newsize < oldsize)
+ {
+ if (fragP->tc_frag_data->times_shrank > 10
+ && fragP->tc_frag_data->times_grown > 10)
+ newsize = oldsize;
+ if (fragP->tc_frag_data->times_shrank < 20)
+ fragP->tc_frag_data->times_shrank ++;
+ }
+ else if (newsize > oldsize)
+ {
+ if (fragP->tc_frag_data->times_grown < 20)
+ fragP->tc_frag_data->times_grown ++;
+ }
+
+ fragP->fr_subtype = newsize;
+ tprintf (" -> new %d old %d delta %d\n", newsize, oldsize, newsize-oldsize);
+ return newsize - oldsize;
+}
+
+/* This lets us test for the opcode type and the desired size in a
+ switch statement. */
+#define OPCODE(type,size) ((type) * 16 + (size))
+
+/* Given the opcode stored in fr_opcode and the number of bytes we
+ think we need, encode a new opcode. We stored a pointer to the
+ fixup for this opcode in the tc_frag_data structure. If we can do
+ the fixup here, we change the relocation type to "none" (we test
+ for that in tc_gen_reloc) else we change it to the right type for
+ the new (biggest) opcode. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+ rx_bytesT * rxb = fragP->tc_frag_data;
+ addressT addr0, mypc;
+ int disp;
+ int reloc_type, reloc_adjust;
+ char * op = fragP->fr_opcode;
+ int keep_reloc = 0;
+ int ri;
+ int fi = (rxb->n_fixups > 1) ? 1 : 0;
+ fixS * fix = rxb->fixups[fi].fixP;
+
+ tprintf ("\033[31mconvrt frag: addr %08lx fix %ld var %ld ofs %ld lit %p opc %p type %d sub %d\033[0m\n",
+ (unsigned long) (fragP->fr_address
+ + (fragP->fr_opcode - fragP->fr_literal)),
+ (long) fragP->fr_fix, (long) fragP->fr_var, (long) fragP->fr_offset,
+ fragP->fr_literal, fragP->fr_opcode, fragP->fr_type,
+ fragP->fr_subtype);
+
+#if TRACE_RELAX
+ {
+ int i;
+
+ printf ("lit 0x%p opc 0x%p", fragP->fr_literal, fragP->fr_opcode);
+ for (i = 0; i < 10; i++)
+ printf (" %02x", (unsigned char) (fragP->fr_opcode[i]));
+ printf ("\n");
+ }
+#endif
+
+ if (fragP->tc_frag_data->n_base == RX_NBASE_FETCHALIGN)
+ {
+ int count = fragP->fr_subtype;
+ if (count == 0)
+ ;
+ else if (count > BIGGEST_NOP)
+ {
+ op[0] = 0x2e;
+ op[1] = count;
+ }
+ else if (count > 0)
+ {
+ memcpy (op, nops[count], count);
+ }
+ }
+
+ /* In the one case where we have both a disp and imm relaxation, we want
+ the imm relaxation here. */
+ ri = 0;
+ if (fragP->tc_frag_data->n_relax > 1
+ && fragP->tc_frag_data->relax[0].type == RX_RELAX_DISP)
+ ri = 1;
+
+ /* We used a new frag for this opcode, so the opcode address should
+ be the frag address. */
+ mypc = fragP->fr_address + (fragP->fr_opcode - fragP->fr_literal);
+
+ /* Try to get the target address. If we fail here, we just use the
+ largest format. */
+ if (rx_frag_fix_value (fragP, segment, 0, & addr0,
+ fragP->tc_frag_data->relax[ri].type != RX_RELAX_BRANCH, 0))
+ {
+ /* We don't know the target address. */
+ keep_reloc = 1;
+ addr0 = 0;
+ disp = 0;
+ }
+ else
+ {
+ /* We know the target address, and it's in addr0. */
+ disp = (int) addr0 - (int) mypc;
+ }
+
+ if (linkrelax)
+ keep_reloc = 1;
+
+ reloc_type = BFD_RELOC_NONE;
+ reloc_adjust = 0;
+
+ tprintf ("convert, op is %d, disp %d (%lx-%lx)\n",
+ rx_opcode_type (fragP->fr_opcode), disp,
+ (unsigned long) addr0, (unsigned long) mypc);
+ switch (fragP->tc_frag_data->relax[ri].type)
+ {
+ case RX_RELAX_BRANCH:
+ switch (OPCODE (rx_opcode_type (fragP->fr_opcode), fragP->fr_subtype))
+ {
+ case OPCODE (OT_bra, 1): /* BRA.S - no change. */
+ op[0] = 0x08 + (disp & 7);
+ break;
+ case OPCODE (OT_bra, 2): /* BRA.B - 8 bit. */
+ op[0] = 0x2e;
+ op[1] = disp;
+ reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 1;
+ break;
+ case OPCODE (OT_bra, 3): /* BRA.W - 16 bit. */
+ op[0] = 0x38;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = (disp >> 8) & 0xff;
+ op[2] = disp;
+#else
+ op[2] = (disp >> 8) & 0xff;
+ op[1] = disp;
+#endif
+ reloc_adjust = 1;
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ break;
+ case OPCODE (OT_bra, 4): /* BRA.A - 24 bit. */
+ op[0] = 0x04;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = (disp >> 16) & 0xff;
+ op[2] = (disp >> 8) & 0xff;
+ op[3] = disp;
+#else
+ op[3] = (disp >> 16) & 0xff;
+ op[2] = (disp >> 8) & 0xff;
+ op[1] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 1;
+ break;
+
+ case OPCODE (OT_beq, 1): /* BEQ.S - no change. */
+ op[0] = 0x10 + (disp & 7);
+ break;
+ case OPCODE (OT_beq, 2): /* BEQ.B - 8 bit. */
+ op[0] = 0x20;
+ op[1] = disp;
+ reloc_adjust = 1;
+ reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+ break;
+ case OPCODE (OT_beq, 3): /* BEQ.W - 16 bit. */
+ op[0] = 0x3a;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = (disp >> 8) & 0xff;
+ op[2] = disp;
+#else
+ op[2] = (disp >> 8) & 0xff;
+ op[1] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 1;
+ break;
+ case OPCODE (OT_beq, 5): /* BEQ.A - synthetic. */
+ op[0] = 0x1d; /* bne.s .+5. */
+ op[1] = 0x04; /* bra.a dsp:24. */
+ disp -= 1;
+#if RX_OPCODE_BIG_ENDIAN
+ op[2] = (disp >> 16) & 0xff;
+ op[3] = (disp >> 8) & 0xff;
+ op[4] = disp;
+#else
+ op[4] = (disp >> 16) & 0xff;
+ op[3] = (disp >> 8) & 0xff;
+ op[2] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ case OPCODE (OT_bne, 1): /* BNE.S - no change. */
+ op[0] = 0x18 + (disp & 7);
+ break;
+ case OPCODE (OT_bne, 2): /* BNE.B - 8 bit. */
+ op[0] = 0x21;
+ op[1] = disp;
+ reloc_adjust = 1;
+ reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+ break;
+ case OPCODE (OT_bne, 3): /* BNE.W - 16 bit. */
+ op[0] = 0x3b;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = (disp >> 8) & 0xff;
+ op[2] = disp;
+#else
+ op[2] = (disp >> 8) & 0xff;
+ op[1] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 1;
+ break;
+ case OPCODE (OT_bne, 5): /* BNE.A - synthetic. */
+ op[0] = 0x15; /* beq.s .+5. */
+ op[1] = 0x04; /* bra.a dsp:24. */
+ disp -= 1;
+#if RX_OPCODE_BIG_ENDIAN
+ op[2] = (disp >> 16) & 0xff;
+ op[3] = (disp >> 8) & 0xff;
+ op[4] = disp;
+#else
+ op[4] = (disp >> 16) & 0xff;
+ op[3] = (disp >> 8) & 0xff;
+ op[2] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ case OPCODE (OT_bsr, 3): /* BSR.W - 16 bit. */
+ op[0] = 0x39;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = (disp >> 8) & 0xff;
+ op[2] = disp;
+#else
+ op[2] = (disp >> 8) & 0xff;
+ op[1] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 0;
+ break;
+ case OPCODE (OT_bsr, 4): /* BSR.A - 24 bit. */
+ op[0] = 0x05;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = (disp >> 16) & 0xff;
+ op[2] = (disp >> 8) & 0xff;
+ op[3] = disp;
+#else
+ op[3] = (disp >> 16) & 0xff;
+ op[2] = (disp >> 8) & 0xff;
+ op[1] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 0;
+ break;
+
+ case OPCODE (OT_bcc, 2): /* Bcond.B - 8 bit. */
+ op[1] = disp;
+ reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
+ break;
+ case OPCODE (OT_bcc, 5): /* Bcond.W - synthetic. */
+ op[0] ^= 1; /* Invert condition. */
+ op[1] = 5; /* Displacement. */
+ op[2] = 0x38;
+ disp -= 2;
+#if RX_OPCODE_BIG_ENDIAN
+ op[3] = (disp >> 8) & 0xff;
+ op[4] = disp;
+#else
+ op[4] = (disp >> 8) & 0xff;
+ op[3] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+ case OPCODE (OT_bcc, 6): /* Bcond.S - synthetic. */
+ op[0] ^= 1; /* Invert condition. */
+ op[1] = 6; /* Displacement. */
+ op[2] = 0x04;
+ disp -= 2;
+#if RX_OPCODE_BIG_ENDIAN
+ op[3] = (disp >> 16) & 0xff;
+ op[4] = (disp >> 8) & 0xff;
+ op[5] = disp;
+#else
+ op[5] = (disp >> 16) & 0xff;
+ op[4] = (disp >> 8) & 0xff;
+ op[3] = disp;
+#endif
+ reloc_type = keep_reloc ? BFD_RELOC_24_PCREL : BFD_RELOC_NONE;
+ reloc_adjust = 2;
+ break;
+
+ default:
+ /* These are opcodes we'll relax in th linker, later. */
+ if (rxb->n_fixups)
+ reloc_type = rxb->fixups[ri].fixP->fx_r_type;
+ break;
+ }
+ break;
+
+ case RX_RELAX_IMM:
+ {
+ int nbytes = fragP->fr_subtype - fragP->tc_frag_data->relax[ri].val_ofs;
+ int li;
+ char * imm = op + fragP->tc_frag_data->relax[ri].val_ofs;
+
+ switch (nbytes)
+ {
+ case 1:
+ li = 1;
+ imm[0] = addr0;
+ reloc_type = BFD_RELOC_8;
+ break;
+ case 2:
+ li = 2;
+#if RX_OPCODE_BIG_ENDIAN
+ imm[1] = addr0;
+ imm[0] = addr0 >> 8;
+#else
+ imm[0] = addr0;
+ imm[1] = addr0 >> 8;
+#endif
+ reloc_type = BFD_RELOC_RX_16_OP;
+ break;
+ case 3:
+ li = 3;
+#if RX_OPCODE_BIG_ENDIAN
+ imm[2] = addr0;
+ imm[1] = addr0 >> 8;
+ imm[0] = addr0 >> 16;
+#else
+ imm[0] = addr0;
+ imm[1] = addr0 >> 8;
+ imm[2] = addr0 >> 16;
+#endif
+ reloc_type = BFD_RELOC_RX_24_OP;
+ break;
+ case 4:
+ li = 0;
+#if RX_OPCODE_BIG_ENDIAN
+ imm[3] = addr0;
+ imm[2] = addr0 >> 8;
+ imm[1] = addr0 >> 16;
+ imm[0] = addr0 >> 24;
+#else
+ imm[0] = addr0;
+ imm[1] = addr0 >> 8;
+ imm[2] = addr0 >> 16;
+ imm[3] = addr0 >> 24;
+#endif
+ reloc_type = BFD_RELOC_RX_32_OP;
+ break;
+ default:
+ as_bad (_("invalid immediate size"));
+ li = -1;
+ }
+
+ switch (fragP->tc_frag_data->relax[ri].field_pos)
+ {
+ case 6:
+ op[0] &= 0xfc;
+ op[0] |= li;
+ break;
+ case 12:
+ op[1] &= 0xf3;
+ op[1] |= li << 2;
+ break;
+ case 20:
+ op[2] &= 0xf3;
+ op[2] |= li << 2;
+ break;
+ default:
+ as_bad (_("invalid immediate field position"));
+ }
+ }
+ break;
+
+ default:
+ if (rxb->n_fixups)
+ {
+ reloc_type = fix->fx_r_type;
+ reloc_adjust = 0;
+ }
+ break;
+ }
+
+ if (rxb->n_fixups)
+ {
+
+ fix->fx_r_type = reloc_type;
+ fix->fx_where += reloc_adjust;
+ switch (reloc_type)
+ {
+ case BFD_RELOC_NONE:
+ fix->fx_size = 0;
+ break;
+ case BFD_RELOC_8:
+ fix->fx_size = 1;
+ break;
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_RX_16_OP:
+ fix->fx_size = 2;
+ break;
+ case BFD_RELOC_24_PCREL:
+ case BFD_RELOC_RX_24_OP:
+ fix->fx_size = 3;
+ break;
+ case BFD_RELOC_RX_32_OP:
+ fix->fx_size = 4;
+ break;
+ }
+ }
+
+ fragP->fr_fix = fragP->fr_subtype + (fragP->fr_opcode - fragP->fr_literal);
+ tprintf ("fragP->fr_fix now %ld (%d + (%p - %p)\n", (long) fragP->fr_fix,
+ fragP->fr_subtype, fragP->fr_opcode, fragP->fr_literal);
+ fragP->fr_var = 0;
+
+ if (fragP->fr_next != NULL
+ && ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address)
+ != fragP->fr_fix))
+ as_bad (_("bad frag at %p : fix %ld addr %ld %ld \n"), fragP,
+ (long) fragP->fr_fix,
+ (long) fragP->fr_address, (long) fragP->fr_next->fr_address);
+}
+
+#undef OPCODE
+
+int
+rx_validate_fix_sub (struct fix * f)
+{
+ /* We permit the subtraction of two symbols in a few cases. */
+ /* mov #sym1-sym2, R3 */
+ if (f->fx_r_type == BFD_RELOC_RX_32_OP)
+ return 1;
+ /* .long sym1-sym2 */
+ if (f->fx_r_type == BFD_RELOC_RX_DIFF
+ && ! f->fx_pcrel
+ && (f->fx_size == 4 || f->fx_size == 2 || f->fx_size == 1))
+ return 1;
+ return 0;
+}
+
+long
+md_pcrel_from_section (fixS * fixP, segT sec)
+{
+ long rv;
+
+ if (fixP->fx_addsy != 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;
+
+ rv = fixP->fx_frag->fr_address + fixP->fx_where;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_RX_DIR3U_PCREL:
+ return rv;
+ default:
+ return rv - 1;
+ }
+}
+
+void
+rx_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:
+ type = BFD_RELOC_32;
+ break;
+ default:
+ as_bad (_("unsupported constant size %d\n"), size);
+ return;
+ }
+
+ if (exp->X_op == O_subtract && exp->X_op_symbol)
+ {
+ if (size != 4 && size != 2 && size != 1)
+ as_bad (_("difference of two symbols only supported with .long, .short, or .byte"));
+ else
+ type = BFD_RELOC_RX_DIFF;
+ }
+
+ fix_new_exp (frag, where, (int) size, exp, 0, type);
+}
+
+void
+md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
+ valueT * t ATTRIBUTE_UNUSED,
+ segT s ATTRIBUTE_UNUSED)
+{
+ /* Instruction bytes are always little endian. */
+ char * op;
+ unsigned long val;
+
+ if (f->fx_addsy && S_FORCE_RELOC (f->fx_addsy, 1))
+ return;
+ if (f->fx_subsy && S_FORCE_RELOC (f->fx_subsy, 1))
+ return;
+
+#define OP2(x) op[target_big_endian ? 1-x : x]
+#define OP3(x) op[target_big_endian ? 2-x : x]
+#define OP4(x) op[target_big_endian ? 3-x : x]
+
+ op = f->fx_frag->fr_literal + f->fx_where;
+ val = (unsigned long) * t;
+
+ /* Opcode words are always the same endian. Data words are either
+ big or little endian. */
+
+ switch (f->fx_r_type)
+ {
+ case BFD_RELOC_NONE:
+ break;
+
+ case BFD_RELOC_RX_RELAX:
+ f->fx_done = 1;
+ break;
+
+ case BFD_RELOC_RX_DIR3U_PCREL:
+ if (val < 3 || val > 10)
+ as_bad_where (f->fx_file, f->fx_line,
+ _("jump not 3..10 bytes away (is %d)"), (int) val);
+ op[0] &= 0xf8;
+ op[0] |= val & 0x07;
+ break;
+
+ case BFD_RELOC_8:
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_RX_8U:
+ op[0] = val;
+ break;
+
+ case BFD_RELOC_16:
+ OP2(1) = val & 0xff;
+ OP2(0) = (val >> 8) & 0xff;
+ break;
+
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_RX_16_OP:
+ case BFD_RELOC_RX_16U:
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = val & 0xff;
+ op[0] = (val >> 8) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+#endif
+ break;
+
+ case BFD_RELOC_24:
+ OP3(0) = val & 0xff;
+ OP3(1) = (val >> 8) & 0xff;
+ OP3(2) = (val >> 16) & 0xff;
+ break;
+
+ case BFD_RELOC_24_PCREL:
+ case BFD_RELOC_RX_24_OP:
+ case BFD_RELOC_RX_24U:
+#if RX_OPCODE_BIG_ENDIAN
+ op[2] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+ op[0] = (val >> 16) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+ op[2] = (val >> 16) & 0xff;
+#endif
+ break;
+
+ case BFD_RELOC_RX_DIFF:
+ switch (f->fx_size)
+ {
+ case 1:
+ op[0] = val & 0xff;
+ break;
+ case 2:
+ OP2(0) = val & 0xff;
+ OP2(1) = (val >> 8) & 0xff;
+ break;
+ case 4:
+ OP4(0) = val & 0xff;
+ OP4(1) = (val >> 8) & 0xff;
+ OP4(2) = (val >> 16) & 0xff;
+ OP4(3) = (val >> 24) & 0xff;
+ break;
+ }
+ break;
+
+ case BFD_RELOC_32:
+ OP4(0) = val & 0xff;
+ OP4(1) = (val >> 8) & 0xff;
+ OP4(2) = (val >> 16) & 0xff;
+ OP4(3) = (val >> 24) & 0xff;
+ break;
+
+ case BFD_RELOC_RX_32_OP:
+#if RX_OPCODE_BIG_ENDIAN
+ op[3] = val & 0xff;
+ op[2] = (val >> 8) & 0xff;
+ op[1] = (val >> 16) & 0xff;
+ op[0] = (val >> 24) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+ op[2] = (val >> 16) & 0xff;
+ op[3] = (val >> 24) & 0xff;
+#endif
+ break;
+
+ case BFD_RELOC_RX_NEG8:
+ op[0] = - val;
+ break;
+
+ case BFD_RELOC_RX_NEG16:
+ val = -val;
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = val & 0xff;
+ op[0] = (val >> 8) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+#endif
+ break;
+
+ case BFD_RELOC_RX_NEG24:
+ val = -val;
+#if RX_OPCODE_BIG_ENDIAN
+ op[2] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+ op[0] = (val >> 16) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+ op[2] = (val >> 16) & 0xff;
+#endif
+ break;
+
+ case BFD_RELOC_RX_NEG32:
+ val = -val;
+#if RX_OPCODE_BIG_ENDIAN
+ op[3] = val & 0xff;
+ op[2] = (val >> 8) & 0xff;
+ op[1] = (val >> 16) & 0xff;
+ op[0] = (val >> 24) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+ op[2] = (val >> 16) & 0xff;
+ op[3] = (val >> 24) & 0xff;
+#endif
+ break;
+
+ case BFD_RELOC_RX_GPRELL:
+ val >>= 1;
+ case BFD_RELOC_RX_GPRELW:
+ val >>= 1;
+ case BFD_RELOC_RX_GPRELB:
+#if RX_OPCODE_BIG_ENDIAN
+ op[1] = val & 0xff;
+ op[0] = (val >> 8) & 0xff;
+#else
+ op[0] = val & 0xff;
+ op[1] = (val >> 8) & 0xff;
+#endif
+ break;
+
+ default:
+ as_bad (_("Unknown reloc in md_apply_fix: %s"),
+ bfd_get_reloc_code_name (f->fx_r_type));
+ break;
+ }
+
+ if (f->fx_addsy == NULL)
+ f->fx_done = 1;
+}
+
+arelent **
+tc_gen_reloc (asection * sec ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ static arelent * reloc[5];
+ bfd_boolean is_opcode = FALSE;
+
+ if (fixp->fx_r_type == BFD_RELOC_NONE)
+ {
+ reloc[0] = NULL;
+ return reloc;
+ }
+
+ if (fixp->fx_subsy
+ && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
+ {
+ fixp->fx_offset -= S_GET_VALUE (fixp->fx_subsy);
+ fixp->fx_subsy = NULL;
+ }
+
+ reloc[0] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[0]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ * reloc[0]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc[0]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc[0]->addend = fixp->fx_offset;
+
+ if (fixp->fx_r_type == BFD_RELOC_RX_32_OP
+ && fixp->fx_subsy)
+ {
+ fixp->fx_r_type = BFD_RELOC_RX_DIFF;
+ is_opcode = TRUE;
+ }
+ else if (sec)
+ is_opcode = sec->flags & SEC_CODE;
+
+ /* Certain BFD relocations cannot be translated directly into
+ a single (non-Red Hat) RX relocation, but instead need
+ multiple RX relocations - handle them here. */
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_RX_DIFF:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[1] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[1]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ * reloc[1]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ reloc[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc[1]->addend = 0;
+ reloc[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[2] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[2]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_OP_SUBTRACT);
+ reloc[2]->addend = 0;
+ reloc[2]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[3] = (arelent *) xmalloc (sizeof (arelent));
+ switch (fixp->fx_size)
+ {
+ case 1:
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS8);
+ break;
+ case 2:
+ if (!is_opcode && target_big_endian)
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS16_REV);
+ else if (is_opcode)
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS16UL);
+ else
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS16);
+ break;
+ case 4:
+ if (!is_opcode && target_big_endian)
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS32_REV);
+ else
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS32);
+ break;
+ }
+ reloc[3]->addend = 0;
+ reloc[3]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[3]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[4] = NULL;
+ break;
+
+ case BFD_RELOC_RX_GPRELL:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[1] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[1]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ if (gp_symbol == NULL)
+ {
+ if (symbol_table_frozen)
+ {
+ symbolS * gp;
+
+ gp = symbol_find ("__gp");
+ if (gp == NULL)
+ as_bad (("unable to create __gp symbol: please re-assemble with the -msmall-data-limit option specified"));
+ else
+ gp_symbol = symbol_get_bfdsym (gp);
+ }
+ else
+ gp_symbol = symbol_get_bfdsym (symbol_find_or_make ("__gp"));
+ }
+ * reloc[1]->sym_ptr_ptr = gp_symbol;
+ reloc[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc[1]->addend = 0;
+ reloc[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[2] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[2]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_OP_SUBTRACT);
+ reloc[2]->addend = 0;
+ reloc[2]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[3] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS16UL);
+ reloc[3]->addend = 0;
+ reloc[3]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[3]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[4] = NULL;
+ break;
+
+ case BFD_RELOC_RX_GPRELW:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[1] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[1]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ if (gp_symbol == NULL)
+ {
+ if (symbol_table_frozen)
+ {
+ symbolS * gp;
+
+ gp = symbol_find ("__gp");
+ if (gp == NULL)
+ as_bad (("unable to create __gp symbol: please re-assemble with the -msmall-data-limit option specified"));
+ else
+ gp_symbol = symbol_get_bfdsym (gp);
+ }
+ else
+ gp_symbol = symbol_get_bfdsym (symbol_find_or_make ("__gp"));
+ }
+ * reloc[1]->sym_ptr_ptr = gp_symbol;
+ reloc[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc[1]->addend = 0;
+ reloc[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[2] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[2]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_OP_SUBTRACT);
+ reloc[2]->addend = 0;
+ reloc[2]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[3] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS16UW);
+ reloc[3]->addend = 0;
+ reloc[3]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[3]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[4] = NULL;
+ break;
+
+ case BFD_RELOC_RX_GPRELB:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[1] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[1]->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ if (gp_symbol == NULL)
+ {
+ if (symbol_table_frozen)
+ {
+ symbolS * gp;
+
+ gp = symbol_find ("__gp");
+ if (gp == NULL)
+ as_bad (("unable to create __gp symbol: please re-assemble with the -msmall-data-limit option specified"));
+ else
+ gp_symbol = symbol_get_bfdsym (gp);
+ }
+ else
+ gp_symbol = symbol_get_bfdsym (symbol_find_or_make ("__gp"));
+ }
+ * reloc[1]->sym_ptr_ptr = gp_symbol;
+ reloc[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc[1]->addend = 0;
+ reloc[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[2] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[2]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_OP_SUBTRACT);
+ reloc[2]->addend = 0;
+ reloc[2]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[3] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[3]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS16U);
+ reloc[3]->addend = 0;
+ reloc[3]->sym_ptr_ptr = reloc[1]->sym_ptr_ptr;
+ reloc[3]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[4] = NULL;
+ break;
+
+ case BFD_RELOC_RX_NEG32:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_SYM);
+
+ reloc[1] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_OP_NEG);
+ reloc[1]->addend = 0;
+ reloc[1]->sym_ptr_ptr = reloc[0]->sym_ptr_ptr;
+ reloc[1]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[2] = (arelent *) xmalloc (sizeof (arelent));
+ reloc[2]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_RX_ABS32);
+ reloc[2]->addend = 0;
+ reloc[2]->sym_ptr_ptr = reloc[0]->sym_ptr_ptr;
+ reloc[2]->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ reloc[3] = NULL;
+ break;
+
+ default:
+ reloc[0]->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ reloc[1] = NULL;
+ break;
+ }
+
+ return reloc;
+}
+
+/* Set the ELF specific flags. */
+
+void
+rx_elf_final_processing (void)
+{
+ elf_elfheader (stdoutput)->e_flags |= elf_flags;
+}
+
+/* Scan the current input line for occurances of Renesas
+ local labels and replace them with the GAS version. */
+
+void
+rx_start_line (void)
+{
+ int in_double_quote = 0;
+ int in_single_quote = 0;
+ int done = 0;
+ char * p = input_line_pointer;
+
+ /* Scan the line looking for question marks. Skip past quote enclosed regions. */
+ do
+ {
+ switch (*p)
+ {
+ case '\n':
+ case 0:
+ done = 1;
+ break;
+
+ case '"':
+ in_double_quote = ! in_double_quote;
+ break;
+
+ case '\'':
+ in_single_quote = ! in_single_quote;
+ break;
+
+ case '?':
+ if (in_double_quote || in_single_quote)
+ break;
+
+ if (p[1] == ':')
+ *p = '1';
+ else if (p[1] == '+')
+ {
+ p[0] = '1';
+ p[1] = 'f';
+ }
+ else if (p[1] == '-')
+ {
+ p[0] = '1';
+ p[1] = 'b';
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ p ++;
+ }
+ while (! done);
+}
diff --git a/gas/config/tc-rx.h b/gas/config/tc-rx.h
new file mode 100644
index 0000000..b82812d
--- /dev/null
+++ b/gas/config/tc-rx.h
@@ -0,0 +1,105 @@
+/* tc-rx.h - header file for Renesas RX
+ Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_RX
+
+extern int target_big_endian;
+
+#define LISTING_HEADER (target_big_endian ? "RX GAS BE" : "RX GAS LE")
+#define LISTING_LHS_WIDTH 8
+#define LISTING_WORD_SIZE 1
+
+#define TARGET_ARCH bfd_arch_rx
+
+/* Instruction bytes are big endian, data bytes can be either. */
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_FORMAT (target_big_endian ? "elf32-rx-be" : "elf32-rx-le")
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+/* But make sure that the binutils treat them as locals. */
+#define LOCAL_LABEL_PREFIX '.'
+
+/* Allow classic-style constants. */
+#define NUMBERS_WITH_SUFFIX 1
+
+/* .-foo gets turned into PC relative relocs. */
+#define DIFF_EXPR_OK
+
+#define md_end rx_md_end
+extern void rx_md_end (void);
+
+#define md_relax_frag rx_relax_frag
+extern int rx_relax_frag (segT, fragS *, long);
+
+#define TC_FRAG_TYPE struct rx_bytesT *
+#define TC_FRAG_INIT rx_frag_init
+extern void rx_frag_init (fragS *);
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIXP, SEC) md_pcrel_from_section (FIXP, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* RX doesn't have a 32 bit PCREL relocations. */
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1
+
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) \
+ rx_validate_fix_sub (FIX)
+extern int rx_validate_fix_sub (struct fix *);
+
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \
+ rx_cons_fix_new (FRAG, WHERE, NBYTES, EXP, RELOC)
+extern void rx_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+#define tc_fix_adjustable(x) 0
+
+#define md_do_align(n, fill, len, max, around) \
+ if ((n) \
+ && !need_pass_2 \
+ && (!(fill) \
+ || ((char)*(fill) == (char)0x03 && (len) == 1)) \
+ && subseg_text_p (now_seg)) \
+ { \
+ frag_align_code ((n), (max)); \
+ goto around; \
+ }
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE 8
+#define HANDLE_ALIGN(FRAG) rx_handle_align (FRAG)
+extern void rx_handle_align (fragS *);
+
+#define RELOC_EXPANSION_POSSIBLE 1
+#define MAX_RELOC_EXPANSION 4
+
+#define elf_tc_final_processing rx_elf_final_processing
+extern void rx_elf_final_processing (void);
+
+extern bfd_boolean rx_use_conventional_section_names;
+#define TEXT_SECTION_NAME (rx_use_conventional_section_names ? ".text" : "P")
+#define DATA_SECTION_NAME (rx_use_conventional_section_names ? ".data" : "D_1")
+#define BSS_SECTION_NAME (rx_use_conventional_section_names ? ".bss" : "B_1")
+
+#define md_start_line_hook rx_start_line
+extern void rx_start_line (void);
diff --git a/gas/config/tc-s390.c b/gas/config/tc-s390.c
new file mode 100644
index 0000000..59f6ab6
--- /dev/null
+++ b/gas/config/tc-s390.c
@@ -0,0 +1,2518 @@
+/* tc-s390.c -- Assemble for the S390
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+ Contributed by Martin Schwidefsky (schwidefsky@de.ibm.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "struc-symbol.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+#include "opcode/s390.h"
+#include "elf/s390.h"
+
+/* The default architecture. */
+#ifndef DEFAULT_ARCH
+#define DEFAULT_ARCH "s390"
+#endif
+static char *default_arch = DEFAULT_ARCH;
+/* Either 32 or 64, selects file format. */
+static int s390_arch_size = 0;
+
+/* If no -march option was given default to the highest available CPU.
+ Since with S/390 a newer CPU always supports everything from its
+ predecessors this will accept every valid asm input. */
+static unsigned int current_cpu = S390_OPCODE_MAXCPU - 1;
+static unsigned int current_mode_mask = 0;
+
+/* Set to TRUE if the highgprs flag in the ELF header needs to be set
+ for the output file. */
+static bfd_boolean set_highgprs_p = FALSE;
+
+/* Whether to use user friendly register names. Default is TRUE. */
+#ifndef TARGET_REG_NAMES_P
+#define TARGET_REG_NAMES_P TRUE
+#endif
+
+static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
+
+/* Set to TRUE if we want to warn about zero base/index registers. */
+static bfd_boolean warn_areg_zero = FALSE;
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */
+int s390_cie_data_alignment;
+
+/* The target specific pseudo-ops which we support. */
+
+/* Define the prototypes for the pseudo-ops */
+static void s390_byte (int);
+static void s390_elf_cons (int);
+static void s390_bss (int);
+static void s390_insn (int);
+static void s390_literals (int);
+static void s390_machine (int);
+static void s390_machinemode (int);
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 },
+ /* Pseudo-ops which must be defined. */
+ { "bss", s390_bss, 0 },
+ { "insn", s390_insn, 0 },
+ /* Pseudo-ops which must be overridden. */
+ { "byte", s390_byte, 0 },
+ { "short", s390_elf_cons, 2 },
+ { "long", s390_elf_cons, 4 },
+ { "quad", s390_elf_cons, 8 },
+ { "ltorg", s390_literals, 0 },
+ { "string", stringer, 8 + 1 },
+ { "machine", s390_machine, 0 },
+ { "machinemode", s390_machinemode, 0 },
+ { NULL, NULL, 0 }
+};
+
+
+/* Structure to hold information about predefined registers. */
+struct pd_reg
+ {
+ char *name;
+ int value;
+ };
+
+/* List of registers that are pre-defined:
+
+ Each access register has a predefined name of the form:
+ a<reg_num> which has the value <reg_num>.
+
+ Each control register has a predefined name of the form:
+ c<reg_num> which has the value <reg_num>.
+
+ Each general register has a predefined name of the form:
+ r<reg_num> which has the value <reg_num>.
+
+ Each floating point register a has predefined name of the form:
+ f<reg_num> which has the value <reg_num>.
+
+ There are individual registers as well:
+ sp has the value 15
+ lit has the value 12
+
+ The table is sorted. Suitable for searching by a binary search. */
+
+static const struct pd_reg pre_defined_registers[] =
+{
+ { "a0", 0 }, /* Access registers */
+ { "a1", 1 },
+ { "a10", 10 },
+ { "a11", 11 },
+ { "a12", 12 },
+ { "a13", 13 },
+ { "a14", 14 },
+ { "a15", 15 },
+ { "a2", 2 },
+ { "a3", 3 },
+ { "a4", 4 },
+ { "a5", 5 },
+ { "a6", 6 },
+ { "a7", 7 },
+ { "a8", 8 },
+ { "a9", 9 },
+
+ { "c0", 0 }, /* Control registers */
+ { "c1", 1 },
+ { "c10", 10 },
+ { "c11", 11 },
+ { "c12", 12 },
+ { "c13", 13 },
+ { "c14", 14 },
+ { "c15", 15 },
+ { "c2", 2 },
+ { "c3", 3 },
+ { "c4", 4 },
+ { "c5", 5 },
+ { "c6", 6 },
+ { "c7", 7 },
+ { "c8", 8 },
+ { "c9", 9 },
+
+ { "f0", 0 }, /* Floating point registers */
+ { "f1", 1 },
+ { "f10", 10 },
+ { "f11", 11 },
+ { "f12", 12 },
+ { "f13", 13 },
+ { "f14", 14 },
+ { "f15", 15 },
+ { "f2", 2 },
+ { "f3", 3 },
+ { "f4", 4 },
+ { "f5", 5 },
+ { "f6", 6 },
+ { "f7", 7 },
+ { "f8", 8 },
+ { "f9", 9 },
+
+ { "lit", 13 }, /* Pointer to literal pool */
+
+ { "r0", 0 }, /* General purpose registers */
+ { "r1", 1 },
+ { "r10", 10 },
+ { "r11", 11 },
+ { "r12", 12 },
+ { "r13", 13 },
+ { "r14", 14 },
+ { "r15", 15 },
+ { "r2", 2 },
+ { "r3", 3 },
+ { "r4", 4 },
+ { "r5", 5 },
+ { "r6", 6 },
+ { "r7", 7 },
+ { "r8", 8 },
+ { "r9", 9 },
+
+ { "sp", 15 }, /* Stack pointer */
+
+};
+
+#define REG_NAME_CNT (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
+
+/* Given NAME, find the register number associated with that name, return
+ the integer value associated with the given name or -1 on failure. */
+
+static int
+reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
+{
+ int middle, low, high;
+ int cmp;
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return regs[middle].value;
+ }
+ while (low <= high);
+
+ return -1;
+}
+
+
+/*
+ * Summary of register_name().
+ *
+ * in: Input_line_pointer points to 1st char of operand.
+ *
+ * out: A expressionS.
+ * The operand may have been a register: in this case, X_op == O_register,
+ * X_add_number is set to the register number, and truth is returned.
+ * Input_line_pointer->(next non-blank) char after operand, or is in its
+ * original state.
+ */
+
+static bfd_boolean
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+ if (name[0] == '%' && ISALPHA (name[1]))
+ name = ++input_line_pointer;
+ else
+ return FALSE;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ /* Make the rest nice. */
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+ return FALSE;
+}
+
+/* Local variables. */
+
+/* Opformat hash table. */
+static struct hash_control *s390_opformat_hash;
+
+/* Opcode hash table. */
+static struct hash_control *s390_opcode_hash = NULL;
+
+/* Flags to set in the elf header */
+static flagword s390_flags = 0;
+
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+
+#ifndef WORKING_DOT_WORD
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
+#endif
+
+const char *md_shortopts = "A:m:kVQ:";
+struct option md_longopts[] = {
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Initialize the default opcode arch and word size from the default
+ architecture name if not specified by an option. */
+static void
+init_default_arch (void)
+{
+ if (strcmp (default_arch, "s390") == 0)
+ {
+ if (s390_arch_size == 0)
+ s390_arch_size = 32;
+ }
+ else if (strcmp (default_arch, "s390x") == 0)
+ {
+ if (s390_arch_size == 0)
+ s390_arch_size = 64;
+ }
+ else
+ as_fatal (_("Invalid default architecture, broken assembler."));
+
+ if (current_mode_mask == 0)
+ {
+ /* Default to z/Architecture mode if the CPU supports it. */
+ if (current_cpu < S390_OPCODE_Z900)
+ current_mode_mask = 1 << S390_OPCODE_ESA;
+ else
+ current_mode_mask = 1 << S390_OPCODE_ZARCH;
+ }
+}
+
+/* Called by TARGET_FORMAT. */
+const char *
+s390_target_format (void)
+{
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ init_default_arch ();
+
+ return s390_arch_size == 64 ? "elf64-s390" : "elf32-s390";
+}
+
+/* Map a CPU string as given with -march= or .machine to the
+ respective enum s390_opcode_cpu_val value. 0xffffffff is returned
+ in case of an error. */
+
+static unsigned int
+s390_parse_cpu (char *arg)
+{
+ if (strcmp (arg, "g5") == 0)
+ return S390_OPCODE_G5;
+ else if (strcmp (arg, "g6") == 0)
+ return S390_OPCODE_G6;
+ else if (strcmp (arg, "z900") == 0)
+ return S390_OPCODE_Z900;
+ else if (strcmp (arg, "z990") == 0)
+ return S390_OPCODE_Z990;
+ else if (strcmp (arg, "z9-109") == 0)
+ return S390_OPCODE_Z9_109;
+ else if (strcmp (arg, "z9-ec") == 0)
+ return S390_OPCODE_Z9_EC;
+ else if (strcmp (arg, "z10") == 0)
+ return S390_OPCODE_Z10;
+ else if (strcmp (arg, "z196") == 0)
+ return S390_OPCODE_Z196;
+ else if (strcmp (arg, "zEC12") == 0)
+ return S390_OPCODE_ZEC12;
+ else if (strcmp (arg, "all") == 0)
+ return S390_OPCODE_MAXCPU - 1;
+ else
+ return -1;
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ /* -k: Ignore for FreeBSD compatibility. */
+ case 'k':
+ break;
+ case 'm':
+ if (arg != NULL && strcmp (arg, "regnames") == 0)
+ reg_names_p = TRUE;
+
+ else if (arg != NULL && strcmp (arg, "no-regnames") == 0)
+ reg_names_p = FALSE;
+
+ else if (arg != NULL && strcmp (arg, "warn-areg-zero") == 0)
+ warn_areg_zero = TRUE;
+
+ else if (arg != NULL && strcmp (arg, "31") == 0)
+ s390_arch_size = 32;
+
+ else if (arg != NULL && strcmp (arg, "64") == 0)
+ s390_arch_size = 64;
+
+ else if (arg != NULL && strcmp (arg, "esa") == 0)
+ current_mode_mask = 1 << S390_OPCODE_ESA;
+
+ else if (arg != NULL && strcmp (arg, "zarch") == 0)
+ {
+ if (s390_arch_size == 32)
+ set_highgprs_p = TRUE;
+ current_mode_mask = 1 << S390_OPCODE_ZARCH;
+ }
+
+ else if (arg != NULL && strncmp (arg, "arch=", 5) == 0)
+ {
+ current_cpu = s390_parse_cpu (arg + 5);
+
+ if (current_cpu == (unsigned int)-1)
+ {
+ as_bad (_("invalid switch -m%s"), arg);
+ return 0;
+ }
+ }
+
+ else
+ {
+ as_bad (_("invalid switch -m%s"), arg);
+ return 0;
+ }
+ break;
+
+ case 'A':
+ /* Option -A is deprecated. Still available for compatibility. */
+ if (arg != NULL && strcmp (arg, "esa") == 0)
+ current_cpu = S390_OPCODE_G5;
+ else if (arg != NULL && strcmp (arg, "esame") == 0)
+ current_cpu = S390_OPCODE_Z900;
+ else
+ as_bad (_("invalid architecture -A%s"), arg);
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+ S390 options:\n\
+ -mregnames Allow symbolic names for registers\n\
+ -mwarn-areg-zero Warn about zero base/index registers\n\
+ -mno-regnames Do not allow symbolic names for registers\n\
+ -m31 Set file format to 31 bit format\n\
+ -m64 Set file format to 64 bit format\n"));
+ fprintf (stream, _("\
+ -V print assembler version number\n\
+ -Qy, -Qn ignored\n"));
+}
+
+/* Generate the hash table mapping mnemonics to struct s390_opcode.
+ This table is built at startup and whenever the CPU level is
+ changed using .machine. */
+
+static void
+s390_setup_opcodes (void)
+{
+ register const struct s390_opcode *op;
+ const struct s390_opcode *op_end;
+ bfd_boolean dup_insn = FALSE;
+ const char *retval;
+
+ if (s390_opcode_hash != NULL)
+ hash_die (s390_opcode_hash);
+
+ /* Insert the opcodes into a hash table. */
+ s390_opcode_hash = hash_new ();
+
+ op_end = s390_opcodes + s390_num_opcodes;
+ for (op = s390_opcodes; op < op_end; op++)
+ {
+ while (op < op_end - 1 && strcmp(op->name, op[1].name) == 0)
+ {
+ if (op->min_cpu <= current_cpu && (op->modes & current_mode_mask))
+ break;
+ op++;
+ }
+
+ if (op->min_cpu <= current_cpu && (op->modes & current_mode_mask))
+ {
+ retval = hash_insert (s390_opcode_hash, op->name, (void *) op);
+ if (retval != (const char *) NULL)
+ {
+ as_bad (_("Internal assembler error for instruction %s"),
+ op->name);
+ dup_insn = TRUE;
+ }
+ }
+
+ while (op < op_end - 1 && strcmp (op->name, op[1].name) == 0)
+ op++;
+ }
+
+ if (dup_insn)
+ abort ();
+}
+
+/* This function is called when the assembler starts up. It is called
+ after the options have been parsed and the output file has been
+ opened. */
+
+void
+md_begin (void)
+{
+ register const struct s390_opcode *op;
+ const struct s390_opcode *op_end;
+ const char *retval;
+
+ /* Give a warning if the combination -m64-bit and -Aesa is used. */
+ if (s390_arch_size == 64 && current_cpu < S390_OPCODE_Z900)
+ as_warn (_("The 64 bit file format is used without esame instructions."));
+
+ s390_cie_data_alignment = -s390_arch_size / 8;
+
+ /* Set the ELF flags if desired. */
+ if (s390_flags)
+ bfd_set_private_flags (stdoutput, s390_flags);
+
+ /* Insert the opcode formats into a hash table. */
+ s390_opformat_hash = hash_new ();
+
+ op_end = s390_opformats + s390_num_opformats;
+ for (op = s390_opformats; op < op_end; op++)
+ {
+ retval = hash_insert (s390_opformat_hash, op->name, (void *) op);
+ if (retval != (const char *) NULL)
+ as_bad (_("Internal assembler error for instruction format %s"),
+ op->name);
+ }
+
+ s390_setup_opcodes ();
+
+ record_alignment (text_section, 2);
+ record_alignment (data_section, 2);
+ record_alignment (bss_section, 2);
+}
+
+/* Called after all assembly has been done. */
+void
+s390_md_end (void)
+{
+ if (s390_arch_size == 64)
+ bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_64);
+ else
+ bfd_set_arch_mach (stdoutput, bfd_arch_s390, bfd_mach_s390_31);
+}
+
+/* Insert an operand value into an instruction. */
+
+static void
+s390_insert_operand (unsigned char *insn,
+ const struct s390_operand *operand,
+ offsetT val,
+ char *file,
+ unsigned int line)
+{
+ addressT uval;
+ int offset;
+
+ if (operand->flags & (S390_OPERAND_SIGNED|S390_OPERAND_PCREL))
+ {
+ offsetT min, max;
+
+ max = ((offsetT) 1 << (operand->bits - 1)) - 1;
+ min = - ((offsetT) 1 << (operand->bits - 1));
+ /* Halve PCREL operands. */
+ if (operand->flags & S390_OPERAND_PCREL)
+ val >>= 1;
+ /* Check for underflow / overflow. */
+ if (val < min || val > max)
+ {
+ const char *err =
+ _("operand out of range (%s not between %ld and %ld)");
+ char buf[100];
+
+ if (operand->flags & S390_OPERAND_PCREL)
+ {
+ val <<= 1;
+ min <<= 1;
+ max <<= 1;
+ }
+ sprint_value (buf, val);
+ if (file == (char *) NULL)
+ as_bad (err, buf, (int) min, (int) max);
+ else
+ as_bad_where (file, line, err, buf, (int) min, (int) max);
+ return;
+ }
+ /* val is ok, now restrict it to operand->bits bits. */
+ uval = (addressT) val & ((((addressT) 1 << (operand->bits-1)) << 1) - 1);
+ /* val is restrict, now check for special case. */
+ if (operand->bits == 20 && operand->shift == 20)
+ uval = (uval >> 12) | ((uval & 0xfff) << 8);
+ }
+ else
+ {
+ addressT min, max;
+
+ max = (((addressT) 1 << (operand->bits - 1)) << 1) - 1;
+ min = (offsetT) 0;
+ uval = (addressT) val;
+ /* Length x in an instructions has real length x+1. */
+ if (operand->flags & S390_OPERAND_LENGTH)
+ uval--;
+ /* Check for underflow / overflow. */
+ if (uval < min || uval > max)
+ {
+ if (operand->flags & S390_OPERAND_LENGTH)
+ {
+ uval++;
+ min++;
+ max++;
+ }
+
+ as_bad_value_out_of_range (_("operand"), uval, (offsetT) min, (offsetT) max, file, line);
+
+ return;
+ }
+ }
+
+ /* Insert fragments of the operand byte for byte. */
+ offset = operand->shift + operand->bits;
+ uval <<= (-offset) & 7;
+ insn += (offset - 1) / 8;
+ while (uval != 0)
+ {
+ *insn-- |= uval;
+ uval >>= 8;
+ }
+}
+
+struct map_tls
+ {
+ char *string;
+ int length;
+ bfd_reloc_code_real_type reloc;
+ };
+
+/* Parse tls marker and return the desired relocation. */
+static bfd_reloc_code_real_type
+s390_tls_suffix (char **str_p, expressionS *exp_p)
+{
+ static struct map_tls mapping[] =
+ {
+ { "tls_load", 8, BFD_RELOC_390_TLS_LOAD },
+ { "tls_gdcall", 10, BFD_RELOC_390_TLS_GDCALL },
+ { "tls_ldcall", 10, BFD_RELOC_390_TLS_LDCALL },
+ { NULL, 0, BFD_RELOC_UNUSED }
+ };
+ struct map_tls *ptr;
+ char *orig_line;
+ char *str;
+ char *ident;
+ int len;
+
+ str = *str_p;
+ if (*str++ != ':')
+ return BFD_RELOC_UNUSED;
+
+ ident = str;
+ while (ISIDNUM (*str))
+ str++;
+ len = str - ident;
+ if (*str++ != ':')
+ return BFD_RELOC_UNUSED;
+
+ orig_line = input_line_pointer;
+ input_line_pointer = str;
+ expression (exp_p);
+ str = input_line_pointer;
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+
+ if (exp_p->X_op != O_symbol)
+ return BFD_RELOC_UNUSED;
+
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (len == ptr->length
+ && strncasecmp (ident, ptr->string, ptr->length) == 0)
+ {
+ /* Found a matching tls suffix. */
+ *str_p = str;
+ return ptr->reloc;
+ }
+ return BFD_RELOC_UNUSED;
+}
+
+/* Structure used to hold suffixes. */
+typedef enum
+ {
+ ELF_SUFFIX_NONE = 0,
+ ELF_SUFFIX_GOT,
+ ELF_SUFFIX_PLT,
+ ELF_SUFFIX_GOTENT,
+ ELF_SUFFIX_GOTOFF,
+ ELF_SUFFIX_GOTPLT,
+ ELF_SUFFIX_PLTOFF,
+ ELF_SUFFIX_TLS_GD,
+ ELF_SUFFIX_TLS_GOTIE,
+ ELF_SUFFIX_TLS_IE,
+ ELF_SUFFIX_TLS_LDM,
+ ELF_SUFFIX_TLS_LDO,
+ ELF_SUFFIX_TLS_LE
+ }
+elf_suffix_type;
+
+struct map_bfd
+ {
+ char *string;
+ int length;
+ elf_suffix_type suffix;
+ };
+
+
+/* Parse @got/@plt/@gotoff. and return the desired relocation. */
+static elf_suffix_type
+s390_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ static struct map_bfd mapping[] =
+ {
+ { "got", 3, ELF_SUFFIX_GOT },
+ { "got12", 5, ELF_SUFFIX_GOT },
+ { "plt", 3, ELF_SUFFIX_PLT },
+ { "gotent", 6, ELF_SUFFIX_GOTENT },
+ { "gotoff", 6, ELF_SUFFIX_GOTOFF },
+ { "gotplt", 6, ELF_SUFFIX_GOTPLT },
+ { "pltoff", 6, ELF_SUFFIX_PLTOFF },
+ { "tlsgd", 5, ELF_SUFFIX_TLS_GD },
+ { "gotntpoff", 9, ELF_SUFFIX_TLS_GOTIE },
+ { "indntpoff", 9, ELF_SUFFIX_TLS_IE },
+ { "tlsldm", 6, ELF_SUFFIX_TLS_LDM },
+ { "dtpoff", 6, ELF_SUFFIX_TLS_LDO },
+ { "ntpoff", 6, ELF_SUFFIX_TLS_LE },
+ { NULL, 0, ELF_SUFFIX_NONE }
+ };
+
+ struct map_bfd *ptr;
+ char *str = *str_p;
+ char *ident;
+ int len;
+
+ if (*str++ != '@')
+ return ELF_SUFFIX_NONE;
+
+ ident = str;
+ while (ISALNUM (*str))
+ str++;
+ len = str - ident;
+
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (len == ptr->length
+ && strncasecmp (ident, ptr->string, ptr->length) == 0)
+ {
+ if (exp_p->X_add_number != 0)
+ as_warn (_("identifier+constant@%s means identifier@%s+constant"),
+ ptr->string, ptr->string);
+ /* Now check for identifier@suffix+constant. */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+
+ switch (new_exp.X_op)
+ {
+ case O_constant: /* X_add_number (a constant expression). */
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ break;
+ case O_symbol: /* X_add_symbol + X_add_number. */
+ /* this case is used for e.g. xyz@PLT+.Label. */
+ exp_p->X_add_number += new_exp.X_add_number;
+ exp_p->X_op_symbol = new_exp.X_add_symbol;
+ exp_p->X_op = O_add;
+ str = input_line_pointer;
+ break;
+ case O_uminus: /* (- X_add_symbol) + X_add_number. */
+ /* this case is used for e.g. xyz@PLT-.Label. */
+ exp_p->X_add_number += new_exp.X_add_number;
+ exp_p->X_op_symbol = new_exp.X_add_symbol;
+ exp_p->X_op = O_subtract;
+ str = input_line_pointer;
+ break;
+ default:
+ break;
+ }
+
+ /* If s390_elf_suffix has not been called with
+ &input_line_pointer as first parameter, we have
+ clobbered the input_line_pointer. We have to
+ undo that. */
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+ *str_p = str;
+ return ptr->suffix;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+/* Structure used to hold a literal pool entry. */
+struct s390_lpe
+ {
+ struct s390_lpe *next;
+ expressionS ex;
+ FLONUM_TYPE floatnum; /* used if X_op == O_big && X_add_number <= 0 */
+ LITTLENUM_TYPE bignum[4]; /* used if X_op == O_big && X_add_number > 0 */
+ int nbytes;
+ bfd_reloc_code_real_type reloc;
+ symbolS *sym;
+ };
+
+static struct s390_lpe *lpe_free_list = NULL;
+static struct s390_lpe *lpe_list = NULL;
+static struct s390_lpe *lpe_list_tail = NULL;
+static symbolS *lp_sym = NULL;
+static int lp_count = 0;
+static int lpe_count = 0;
+
+static int
+s390_exp_compare (expressionS *exp1, expressionS *exp2)
+{
+ if (exp1->X_op != exp2->X_op)
+ return 0;
+
+ switch (exp1->X_op)
+ {
+ case O_constant: /* X_add_number must be equal. */
+ case O_register:
+ return exp1->X_add_number == exp2->X_add_number;
+
+ case O_big:
+ as_bad (_("Can't handle O_big in s390_exp_compare"));
+
+ case O_symbol: /* X_add_symbol & X_add_number must be equal. */
+ case O_symbol_rva:
+ case O_uminus:
+ case O_bit_not:
+ case O_logical_not:
+ return (exp1->X_add_symbol == exp2->X_add_symbol)
+ && (exp1->X_add_number == exp2->X_add_number);
+
+ case O_multiply: /* X_add_symbol,X_op_symbol&X_add_number must be equal. */
+ case O_divide:
+ case O_modulus:
+ case O_left_shift:
+ case O_right_shift:
+ case O_bit_inclusive_or:
+ case O_bit_or_not:
+ case O_bit_exclusive_or:
+ case O_bit_and:
+ case O_add:
+ case O_subtract:
+ case O_eq:
+ case O_ne:
+ case O_lt:
+ case O_le:
+ case O_ge:
+ case O_gt:
+ case O_logical_and:
+ case O_logical_or:
+ return (exp1->X_add_symbol == exp2->X_add_symbol)
+ && (exp1->X_op_symbol == exp2->X_op_symbol)
+ && (exp1->X_add_number == exp2->X_add_number);
+ default:
+ return 0;
+ }
+}
+
+/* Test for @lit and if its present make an entry in the literal pool and
+ modify the current expression to be an offset into the literal pool. */
+static elf_suffix_type
+s390_lit_suffix (char **str_p, expressionS *exp_p, elf_suffix_type suffix)
+{
+ bfd_reloc_code_real_type reloc;
+ char tmp_name[64];
+ char *str = *str_p;
+ char *ident;
+ struct s390_lpe *lpe;
+ int nbytes, len;
+
+ if (*str++ != ':')
+ return suffix; /* No modification. */
+
+ /* We look for a suffix of the form "@lit1", "@lit2", "@lit4" or "@lit8". */
+ ident = str;
+ while (ISALNUM (*str))
+ str++;
+ len = str - ident;
+ if (len != 4 || strncasecmp (ident, "lit", 3) != 0
+ || (ident[3]!='1' && ident[3]!='2' && ident[3]!='4' && ident[3]!='8'))
+ return suffix; /* no modification */
+ nbytes = ident[3] - '0';
+
+ reloc = BFD_RELOC_UNUSED;
+ if (suffix == ELF_SUFFIX_GOT)
+ {
+ if (nbytes == 2)
+ reloc = BFD_RELOC_390_GOT16;
+ else if (nbytes == 4)
+ reloc = BFD_RELOC_32_GOT_PCREL;
+ else if (nbytes == 8)
+ reloc = BFD_RELOC_390_GOT64;
+ }
+ else if (suffix == ELF_SUFFIX_PLT)
+ {
+ if (nbytes == 4)
+ reloc = BFD_RELOC_390_PLT32;
+ else if (nbytes == 8)
+ reloc = BFD_RELOC_390_PLT64;
+ }
+
+ if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
+ as_bad (_("Invalid suffix for literal pool entry"));
+
+ /* Search the pool if the new entry is a duplicate. */
+ if (exp_p->X_op == O_big)
+ {
+ /* Special processing for big numbers. */
+ for (lpe = lpe_list; lpe != NULL; lpe = lpe->next)
+ {
+ if (lpe->ex.X_op == O_big)
+ {
+ if (exp_p->X_add_number <= 0 && lpe->ex.X_add_number <= 0)
+ {
+ if (memcmp (&generic_floating_point_number, &lpe->floatnum,
+ sizeof (FLONUM_TYPE)) == 0)
+ break;
+ }
+ else if (exp_p->X_add_number == lpe->ex.X_add_number)
+ {
+ if (memcmp (generic_bignum, lpe->bignum,
+ sizeof (LITTLENUM_TYPE)*exp_p->X_add_number) == 0)
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Processing for 'normal' data types. */
+ for (lpe = lpe_list; lpe != NULL; lpe = lpe->next)
+ if (lpe->nbytes == nbytes && lpe->reloc == reloc
+ && s390_exp_compare (exp_p, &lpe->ex) != 0)
+ break;
+ }
+
+ if (lpe == NULL)
+ {
+ /* A new literal. */
+ if (lpe_free_list != NULL)
+ {
+ lpe = lpe_free_list;
+ lpe_free_list = lpe_free_list->next;
+ }
+ else
+ {
+ lpe = (struct s390_lpe *) xmalloc (sizeof (struct s390_lpe));
+ }
+
+ lpe->ex = *exp_p;
+
+ if (exp_p->X_op == O_big)
+ {
+ if (exp_p->X_add_number <= 0)
+ lpe->floatnum = generic_floating_point_number;
+ else if (exp_p->X_add_number <= 4)
+ memcpy (lpe->bignum, generic_bignum,
+ exp_p->X_add_number * sizeof (LITTLENUM_TYPE));
+ else
+ as_bad (_("Big number is too big"));
+ }
+
+ lpe->nbytes = nbytes;
+ lpe->reloc = reloc;
+ /* Literal pool name defined ? */
+ if (lp_sym == NULL)
+ {
+ sprintf (tmp_name, ".L\001%i", lp_count);
+ lp_sym = symbol_make (tmp_name);
+ }
+
+ /* Make name for literal pool entry. */
+ sprintf (tmp_name, ".L\001%i\002%i", lp_count, lpe_count);
+ lpe_count++;
+ lpe->sym = symbol_make (tmp_name);
+
+ /* Add to literal pool list. */
+ lpe->next = NULL;
+ if (lpe_list_tail != NULL)
+ {
+ lpe_list_tail->next = lpe;
+ lpe_list_tail = lpe;
+ }
+ else
+ lpe_list = lpe_list_tail = lpe;
+ }
+
+ /* Now change exp_p to the offset into the literal pool.
+ Thats the expression: .L^Ax^By-.L^Ax */
+ exp_p->X_add_symbol = lpe->sym;
+ exp_p->X_op_symbol = lp_sym;
+ exp_p->X_op = O_subtract;
+ exp_p->X_add_number = 0;
+
+ *str_p = str;
+
+ /* We change the suffix type to ELF_SUFFIX_NONE, because
+ the difference of two local labels is just a number. */
+ return ELF_SUFFIX_NONE;
+}
+
+/* Like normal .long/.short/.word, except support @got, etc.
+ clobbers input_line_pointer, checks end-of-line. */
+static void
+s390_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long */)
+{
+ expressionS exp;
+ elf_suffix_type suffix;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && (suffix = s390_elf_suffix (&input_line_pointer, &exp)) != ELF_SUFFIX_NONE)
+ {
+ bfd_reloc_code_real_type reloc;
+ reloc_howto_type *reloc_howto;
+ int size;
+ char *where;
+
+ if (nbytes == 2)
+ {
+ static bfd_reloc_code_real_type tab2[] =
+ {
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_NONE */
+ BFD_RELOC_390_GOT16, /* ELF_SUFFIX_GOT */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_PLT */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTENT */
+ BFD_RELOC_16_GOTOFF, /* ELF_SUFFIX_GOTOFF */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTPLT */
+ BFD_RELOC_390_PLTOFF16, /* ELF_SUFFIX_PLTOFF */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_GD */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_GOTIE */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_IE */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_LDM */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_TLS_LDO */
+ BFD_RELOC_UNUSED /* ELF_SUFFIX_TLS_LE */
+ };
+ reloc = tab2[suffix];
+ }
+ else if (nbytes == 4)
+ {
+ static bfd_reloc_code_real_type tab4[] =
+ {
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_NONE */
+ BFD_RELOC_32_GOT_PCREL, /* ELF_SUFFIX_GOT */
+ BFD_RELOC_390_PLT32, /* ELF_SUFFIX_PLT */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTENT */
+ BFD_RELOC_32_GOTOFF, /* ELF_SUFFIX_GOTOFF */
+ BFD_RELOC_390_GOTPLT32, /* ELF_SUFFIX_GOTPLT */
+ BFD_RELOC_390_PLTOFF32, /* ELF_SUFFIX_PLTOFF */
+ BFD_RELOC_390_TLS_GD32, /* ELF_SUFFIX_TLS_GD */
+ BFD_RELOC_390_TLS_GOTIE32, /* ELF_SUFFIX_TLS_GOTIE */
+ BFD_RELOC_390_TLS_IE32, /* ELF_SUFFIX_TLS_IE */
+ BFD_RELOC_390_TLS_LDM32, /* ELF_SUFFIX_TLS_LDM */
+ BFD_RELOC_390_TLS_LDO32, /* ELF_SUFFIX_TLS_LDO */
+ BFD_RELOC_390_TLS_LE32 /* ELF_SUFFIX_TLS_LE */
+ };
+ reloc = tab4[suffix];
+ }
+ else if (nbytes == 8)
+ {
+ static bfd_reloc_code_real_type tab8[] =
+ {
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_NONE */
+ BFD_RELOC_390_GOT64, /* ELF_SUFFIX_GOT */
+ BFD_RELOC_390_PLT64, /* ELF_SUFFIX_PLT */
+ BFD_RELOC_UNUSED, /* ELF_SUFFIX_GOTENT */
+ BFD_RELOC_390_GOTOFF64, /* ELF_SUFFIX_GOTOFF */
+ BFD_RELOC_390_GOTPLT64, /* ELF_SUFFIX_GOTPLT */
+ BFD_RELOC_390_PLTOFF64, /* ELF_SUFFIX_PLTOFF */
+ BFD_RELOC_390_TLS_GD64, /* ELF_SUFFIX_TLS_GD */
+ BFD_RELOC_390_TLS_GOTIE64, /* ELF_SUFFIX_TLS_GOTIE */
+ BFD_RELOC_390_TLS_IE64, /* ELF_SUFFIX_TLS_IE */
+ BFD_RELOC_390_TLS_LDM64, /* ELF_SUFFIX_TLS_LDM */
+ BFD_RELOC_390_TLS_LDO64, /* ELF_SUFFIX_TLS_LDO */
+ BFD_RELOC_390_TLS_LE64 /* ELF_SUFFIX_TLS_LE */
+ };
+ reloc = tab8[suffix];
+ }
+ else
+ reloc = BFD_RELOC_UNUSED;
+
+ if (reloc != BFD_RELOC_UNUSED
+ && (reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc)))
+ {
+ size = bfd_get_reloc_size (reloc_howto);
+ if (size > nbytes)
+ as_bad (_("%s relocations do not fit in %d bytes"),
+ reloc_howto->name, nbytes);
+ where = frag_more (nbytes);
+ md_number_to_chars (where, 0, size);
+ /* To make fixup_segment do the pc relative conversion the
+ pcrel parameter on the fix_new_exp call needs to be FALSE. */
+ fix_new_exp (frag_now, where - frag_now->fr_literal,
+ size, &exp, FALSE, reloc);
+ }
+ else
+ as_bad (_("relocation not applicable"));
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+/* We need to keep a list of fixups. We can't simply generate them as
+ we go, because that would require us to first create the frag, and
+ that would screw up references to ``.''. */
+
+struct s390_fixup
+ {
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+ };
+
+#define MAX_INSN_FIXUPS (4)
+
+/* This routine is called for each instruction to be assembled. */
+
+static char *
+md_gather_operands (char *str,
+ unsigned char *insn,
+ const struct s390_opcode *opcode)
+{
+ struct s390_fixup fixups[MAX_INSN_FIXUPS];
+ const struct s390_operand *operand;
+ const unsigned char *opindex_ptr;
+ expressionS ex;
+ elf_suffix_type suffix;
+ bfd_reloc_code_real_type reloc;
+ int skip_optional;
+ char *f;
+ int fc, i;
+
+ while (ISSPACE (*str))
+ str++;
+
+ skip_optional = 0;
+
+ /* Gather the operands. */
+ fc = 0;
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ char *hold;
+
+ operand = s390_operands + *opindex_ptr;
+
+ if (skip_optional && (operand->flags & S390_OPERAND_INDEX))
+ {
+ /* We do an early skip. For D(X,B) constructions the index
+ register is skipped (X is optional). For D(L,B) the base
+ register will be the skipped operand, because L is NOT
+ optional. */
+ skip_optional = 0;
+ continue;
+ }
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ /* Parse the operand. */
+ if (! register_name (&ex))
+ expression (&ex);
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ /* Write the operand to the insn. */
+ if (ex.X_op == O_illegal)
+ as_bad (_("illegal operand"));
+ else if (ex.X_op == O_absent)
+ {
+ /* No operands, check if all operands can be skipped. */
+ while (*opindex_ptr != 0 && operand->flags & S390_OPERAND_OPTIONAL)
+ {
+ if (operand->flags & S390_OPERAND_DISP)
+ {
+ /* An optional displacement makes the whole D(X,B)
+ D(L,B) or D(B) block optional. */
+ do {
+ operand = s390_operands + *(++opindex_ptr);
+ } while (!(operand->flags & S390_OPERAND_BASE));
+ }
+ operand = s390_operands + *(++opindex_ptr);
+ }
+ if (opindex_ptr[0] == '\0')
+ break;
+ as_bad (_("missing operand"));
+ }
+ else if (ex.X_op == O_register || ex.X_op == O_constant)
+ {
+ s390_lit_suffix (&str, &ex, ELF_SUFFIX_NONE);
+
+ if (ex.X_op != O_register && ex.X_op != O_constant)
+ {
+ /* We need to generate a fixup for the
+ expression returned by s390_lit_suffix. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_UNUSED;
+ ++fc;
+ }
+ else
+ {
+ if ((operand->flags & S390_OPERAND_INDEX)
+ && ex.X_add_number == 0
+ && warn_areg_zero)
+ as_warn (_("index register specified but zero"));
+ if ((operand->flags & S390_OPERAND_BASE)
+ && ex.X_add_number == 0
+ && warn_areg_zero)
+ as_warn (_("base register specified but zero"));
+ if ((operand->flags & S390_OPERAND_GPR)
+ && (operand->flags & S390_OPERAND_REG_PAIR)
+ && (ex.X_add_number & 1))
+ as_fatal (_("odd numbered general purpose register specified as "
+ "register pair"));
+ if ((operand->flags & S390_OPERAND_FPR)
+ && (operand->flags & S390_OPERAND_REG_PAIR)
+ && ex.X_add_number != 0 && ex.X_add_number != 1
+ && ex.X_add_number != 4 && ex.X_add_number != 5
+ && ex.X_add_number != 8 && ex.X_add_number != 9
+ && ex.X_add_number != 12 && ex.X_add_number != 13)
+ as_fatal (_("invalid floating point register pair. Valid fp "
+ "register pair operands are 0, 1, 4, 5, 8, 9, "
+ "12 or 13."));
+ s390_insert_operand (insn, operand, ex.X_add_number, NULL, 0);
+ }
+ }
+ else
+ {
+ suffix = s390_elf_suffix (&str, &ex);
+ suffix = s390_lit_suffix (&str, &ex, suffix);
+ reloc = BFD_RELOC_UNUSED;
+
+ if (suffix == ELF_SUFFIX_GOT)
+ {
+ if ((operand->flags & S390_OPERAND_DISP) &&
+ (operand->bits == 12))
+ reloc = BFD_RELOC_390_GOT12;
+ else if ((operand->flags & S390_OPERAND_DISP) &&
+ (operand->bits == 20))
+ reloc = BFD_RELOC_390_GOT20;
+ else if ((operand->flags & S390_OPERAND_SIGNED)
+ && (operand->bits == 16))
+ reloc = BFD_RELOC_390_GOT16;
+ else if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 32))
+ reloc = BFD_RELOC_390_GOTENT;
+ }
+ else if (suffix == ELF_SUFFIX_PLT)
+ {
+ if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 12))
+ reloc = BFD_RELOC_390_PLT12DBL;
+ else if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 16))
+ reloc = BFD_RELOC_390_PLT16DBL;
+ else if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 24))
+ reloc = BFD_RELOC_390_PLT24DBL;
+ else if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 32))
+ reloc = BFD_RELOC_390_PLT32DBL;
+ }
+ else if (suffix == ELF_SUFFIX_GOTENT)
+ {
+ if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 32))
+ reloc = BFD_RELOC_390_GOTENT;
+ }
+ else if (suffix == ELF_SUFFIX_GOTOFF)
+ {
+ if ((operand->flags & S390_OPERAND_SIGNED)
+ && (operand->bits == 16))
+ reloc = BFD_RELOC_16_GOTOFF;
+ }
+ else if (suffix == ELF_SUFFIX_PLTOFF)
+ {
+ if ((operand->flags & S390_OPERAND_SIGNED)
+ && (operand->bits == 16))
+ reloc = BFD_RELOC_390_PLTOFF16;
+ }
+ else if (suffix == ELF_SUFFIX_GOTPLT)
+ {
+ if ((operand->flags & S390_OPERAND_DISP)
+ && (operand->bits == 12))
+ reloc = BFD_RELOC_390_GOTPLT12;
+ else if ((operand->flags & S390_OPERAND_SIGNED)
+ && (operand->bits == 16))
+ reloc = BFD_RELOC_390_GOTPLT16;
+ else if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 32))
+ reloc = BFD_RELOC_390_GOTPLTENT;
+ }
+ else if (suffix == ELF_SUFFIX_TLS_GOTIE)
+ {
+ if ((operand->flags & S390_OPERAND_DISP)
+ && (operand->bits == 12))
+ reloc = BFD_RELOC_390_TLS_GOTIE12;
+ else if ((operand->flags & S390_OPERAND_DISP)
+ && (operand->bits == 20))
+ reloc = BFD_RELOC_390_TLS_GOTIE20;
+ }
+ else if (suffix == ELF_SUFFIX_TLS_IE)
+ {
+ if ((operand->flags & S390_OPERAND_PCREL)
+ && (operand->bits == 32))
+ reloc = BFD_RELOC_390_TLS_IEENT;
+ }
+
+ if (suffix != ELF_SUFFIX_NONE && reloc == BFD_RELOC_UNUSED)
+ as_bad (_("invalid operand suffix"));
+ /* We need to generate a fixup of type 'reloc' for this
+ expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+
+ /* Check the next character. The call to expression has advanced
+ str past any whitespace. */
+ if (operand->flags & S390_OPERAND_DISP)
+ {
+ /* After a displacement a block in parentheses can start. */
+ if (*str != '(')
+ {
+ /* Check if parenthesized block can be skipped. If the next
+ operand is neiter an optional operand nor a base register
+ then we have a syntax error. */
+ operand = s390_operands + *(++opindex_ptr);
+ if (!(operand->flags & (S390_OPERAND_INDEX|S390_OPERAND_BASE)))
+ as_bad (_("syntax error; missing '(' after displacement"));
+
+ /* Ok, skip all operands until S390_OPERAND_BASE. */
+ while (!(operand->flags & S390_OPERAND_BASE))
+ operand = s390_operands + *(++opindex_ptr);
+
+ /* If there is a next operand it must be separated by a comma. */
+ if (opindex_ptr[1] != '\0')
+ {
+ if (*str != ',')
+ {
+ while (opindex_ptr[1] != '\0')
+ {
+ operand = s390_operands + *(++opindex_ptr);
+ if (operand->flags & S390_OPERAND_OPTIONAL)
+ continue;
+ as_bad (_("syntax error; expected ,"));
+ break;
+ }
+ }
+ else
+ str++;
+ }
+ }
+ else
+ {
+ /* We found an opening parentheses. */
+ str++;
+ for (f = str; *f != '\0'; f++)
+ if (*f == ',' || *f == ')')
+ break;
+ /* If there is no comma until the closing parentheses OR
+ there is a comma right after the opening parentheses,
+ we have to skip optional operands. */
+ if (*f == ',' && f == str)
+ {
+ /* comma directly after '(' ? */
+ skip_optional = 1;
+ str++;
+ }
+ else
+ skip_optional = (*f != ',');
+ }
+ }
+ else if (operand->flags & S390_OPERAND_BASE)
+ {
+ /* After the base register the parenthesed block ends. */
+ if (*str++ != ')')
+ as_bad (_("syntax error; missing ')' after base register"));
+ skip_optional = 0;
+ /* If there is a next operand it must be separated by a comma. */
+ if (opindex_ptr[1] != '\0')
+ {
+ if (*str != ',')
+ {
+ while (opindex_ptr[1] != '\0')
+ {
+ operand = s390_operands + *(++opindex_ptr);
+ if (operand->flags & S390_OPERAND_OPTIONAL)
+ continue;
+ as_bad (_("syntax error; expected ,"));
+ break;
+ }
+ }
+ else
+ str++;
+ }
+ }
+ else
+ {
+ /* We can find an 'early' closing parentheses in e.g. D(L) instead
+ of D(L,B). In this case the base register has to be skipped. */
+ if (*str == ')')
+ {
+ operand = s390_operands + *(++opindex_ptr);
+
+ if (!(operand->flags & S390_OPERAND_BASE))
+ as_bad (_("syntax error; ')' not allowed here"));
+ str++;
+ }
+ /* If there is a next operand it must be separated by a comma. */
+ if (opindex_ptr[1] != '\0')
+ {
+ if (*str != ',')
+ {
+ while (opindex_ptr[1] != '\0')
+ {
+ operand = s390_operands + *(++opindex_ptr);
+ if (operand->flags & S390_OPERAND_OPTIONAL)
+ continue;
+ as_bad (_("syntax error; expected ,"));
+ break;
+ }
+ }
+ else
+ str++;
+ }
+ }
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ /* Check for tls instruction marker. */
+ reloc = s390_tls_suffix (&str, &ex);
+ if (reloc != BFD_RELOC_UNUSED)
+ {
+ /* We need to generate a fixup of type 'reloc' for this
+ instruction. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = -1;
+ fixups[fc].reloc = reloc;
+ ++fc;
+ }
+
+ if (*str != '\0')
+ {
+ char *linefeed;
+
+ if ((linefeed = strchr (str, '\n')) != NULL)
+ *linefeed = '\0';
+ as_bad (_("junk at end of line: `%s'"), str);
+ if (linefeed != NULL)
+ *linefeed = '\n';
+ }
+
+ /* Write out the instruction. */
+ f = frag_more (opcode->oplen);
+ memcpy (f, insn, opcode->oplen);
+ dwarf2_emit_insn (opcode->oplen);
+
+ /* Create any fixups. At this point we do not use a
+ bfd_reloc_code_real_type, but instead just use the
+ BFD_RELOC_UNUSED plus the operand index. This lets us easily
+ handle fixups for any operand type, although that is admittedly
+ not a very exciting feature. We pick a BFD reloc type in
+ md_apply_fix. */
+ for (i = 0; i < fc; i++)
+ {
+
+ if (fixups[i].opindex < 0)
+ {
+ /* Create tls instruction marker relocation. */
+ fix_new_exp (frag_now, f - frag_now->fr_literal, opcode->oplen,
+ &fixups[i].exp, 0, fixups[i].reloc);
+ continue;
+ }
+
+ operand = s390_operands + fixups[i].opindex;
+
+ if (fixups[i].reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto;
+ fixS *fixP;
+ int size;
+
+ reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
+ if (!reloc_howto)
+ abort ();
+
+ size = ((reloc_howto->bitsize - 1) / 8) + 1;
+
+ if (size < 1 || size > 4)
+ abort ();
+
+ fixP = fix_new_exp (frag_now,
+ f - frag_now->fr_literal + (operand->shift/8),
+ size, &fixups[i].exp, reloc_howto->pc_relative,
+ fixups[i].reloc);
+ /* Turn off overflow checking in fixup_segment. This is necessary
+ because fixup_segment will signal an overflow for large 4 byte
+ quantities for GOT12 relocations. */
+ if ( fixups[i].reloc == BFD_RELOC_390_GOT12
+ || fixups[i].reloc == BFD_RELOC_390_GOT20
+ || fixups[i].reloc == BFD_RELOC_390_GOT16)
+ fixP->fx_no_overflow = 1;
+ }
+ else
+ fix_new_exp (frag_now, f - frag_now->fr_literal, 4, &fixups[i].exp,
+ (operand->flags & S390_OPERAND_PCREL) != 0,
+ ((bfd_reloc_code_real_type)
+ (fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
+ }
+ return str;
+}
+
+/* This routine is called for each instruction to be assembled. */
+
+void
+md_assemble (char *str)
+{
+ const struct s390_opcode *opcode;
+ unsigned char insn[6];
+ char *s;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
+ ;
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Look up the opcode in the hash table. */
+ opcode = (struct s390_opcode *) hash_find (s390_opcode_hash, str);
+ if (opcode == (const struct s390_opcode *) NULL)
+ {
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ return;
+ }
+ else if (!(opcode->modes & current_mode_mask))
+ {
+ as_bad (_("Opcode %s not available in this mode"), str);
+ return;
+ }
+ memcpy (insn, opcode->opcode, sizeof (insn));
+ md_gather_operands (s, insn, opcode);
+}
+
+#ifndef WORKING_DOT_WORD
+/* Handle long and short jumps. We don't support these */
+void
+md_create_short_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ abort ();
+}
+
+void
+md_create_long_jump (ptr, from_addr, to_addr, frag, to_symbol)
+ char *ptr;
+ addressT from_addr, to_addr;
+ fragS *frag;
+ symbolS *to_symbol;
+{
+ abort ();
+}
+#endif
+
+void
+s390_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ /* We don't support putting frags in the BSS segment, we fake it
+ by marking in_bss, then looking at s_skip for clues. */
+
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+}
+
+/* Pseudo-op handling. */
+
+void
+s390_insn (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ const struct s390_opcode *opformat;
+ unsigned char insn[6];
+ char *s;
+
+ /* Get the opcode format. */
+ s = input_line_pointer;
+ while (*s != '\0' && *s != ',' && ! ISSPACE (*s))
+ s++;
+ if (*s != ',')
+ as_bad (_("Invalid .insn format\n"));
+ *s++ = '\0';
+
+ /* Look up the opcode in the hash table. */
+ opformat = (struct s390_opcode *)
+ hash_find (s390_opformat_hash, input_line_pointer);
+ if (opformat == (const struct s390_opcode *) NULL)
+ {
+ as_bad (_("Unrecognized opcode format: `%s'"), input_line_pointer);
+ return;
+ }
+ input_line_pointer = s;
+ expression (&exp);
+ if (exp.X_op == O_constant)
+ {
+ if ( ( opformat->oplen == 6
+ && (addressT) exp.X_add_number < (1ULL << 48))
+ || ( opformat->oplen == 4
+ && (addressT) exp.X_add_number < (1ULL << 32))
+ || ( opformat->oplen == 2
+ && (addressT) exp.X_add_number < (1ULL << 16)))
+ md_number_to_chars ((char *) insn, exp.X_add_number, opformat->oplen);
+ else
+ as_bad (_("Invalid .insn format\n"));
+ }
+ else if (exp.X_op == O_big)
+ {
+ if (exp.X_add_number > 0
+ && opformat->oplen == 6
+ && generic_bignum[3] == 0)
+ {
+ md_number_to_chars ((char *) insn, generic_bignum[2], 2);
+ md_number_to_chars ((char *) &insn[2], generic_bignum[1], 2);
+ md_number_to_chars ((char *) &insn[4], generic_bignum[0], 2);
+ }
+ else
+ as_bad (_("Invalid .insn format\n"));
+ }
+ else
+ as_bad (_("second operand of .insn not a constant\n"));
+
+ if (strcmp (opformat->name, "e") != 0 && *input_line_pointer++ != ',')
+ as_bad (_("missing comma after insn constant\n"));
+
+ if ((s = strchr (input_line_pointer, '\n')) != NULL)
+ *s = '\0';
+ input_line_pointer = md_gather_operands (input_line_pointer, insn,
+ opformat);
+ if (s != NULL)
+ *s = '\n';
+ demand_empty_rest_of_line ();
+}
+
+/* The .byte pseudo-op. This is similar to the normal .byte
+ pseudo-op, but it can also take a single ASCII string. */
+
+static void
+s390_byte (int ignore ATTRIBUTE_UNUSED)
+{
+ if (*input_line_pointer != '\"')
+ {
+ cons (1);
+ return;
+ }
+
+ /* Gather characters. A real double quote is doubled. Unusual
+ characters are not permitted. */
+ ++input_line_pointer;
+ while (1)
+ {
+ char c;
+
+ c = *input_line_pointer++;
+
+ if (c == '\"')
+ {
+ if (*input_line_pointer != '\"')
+ break;
+ ++input_line_pointer;
+ }
+
+ FRAG_APPEND_1_CHAR (c);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .ltorg pseudo-op.This emits all literals defined since the last
+ .ltorg or the invocation of gas. Literals are defined with the
+ @lit suffix. */
+
+static void
+s390_literals (int ignore ATTRIBUTE_UNUSED)
+{
+ struct s390_lpe *lpe;
+
+ if (lp_sym == NULL || lpe_count == 0)
+ return; /* Nothing to be done. */
+
+ /* Emit symbol for start of literal pool. */
+ S_SET_SEGMENT (lp_sym, now_seg);
+ S_SET_VALUE (lp_sym, (valueT) frag_now_fix ());
+ lp_sym->sy_frag = frag_now;
+
+ while (lpe_list)
+ {
+ lpe = lpe_list;
+ lpe_list = lpe_list->next;
+ S_SET_SEGMENT (lpe->sym, now_seg);
+ S_SET_VALUE (lpe->sym, (valueT) frag_now_fix ());
+ lpe->sym->sy_frag = frag_now;
+
+ /* Emit literal pool entry. */
+ if (lpe->reloc != BFD_RELOC_UNUSED)
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput, lpe->reloc);
+ int size = bfd_get_reloc_size (reloc_howto);
+ char *where;
+
+ if (size > lpe->nbytes)
+ as_bad (_("%s relocations do not fit in %d bytes"),
+ reloc_howto->name, lpe->nbytes);
+ where = frag_more (lpe->nbytes);
+ md_number_to_chars (where, 0, size);
+ fix_new_exp (frag_now, where - frag_now->fr_literal,
+ size, &lpe->ex, reloc_howto->pc_relative, lpe->reloc);
+ }
+ else
+ {
+ if (lpe->ex.X_op == O_big)
+ {
+ if (lpe->ex.X_add_number <= 0)
+ generic_floating_point_number = lpe->floatnum;
+ else
+ memcpy (generic_bignum, lpe->bignum,
+ lpe->ex.X_add_number * sizeof (LITTLENUM_TYPE));
+ }
+ emit_expr (&lpe->ex, lpe->nbytes);
+ }
+
+ lpe->next = lpe_free_list;
+ lpe_free_list = lpe;
+ }
+ lpe_list_tail = NULL;
+ lp_sym = NULL;
+ lp_count++;
+ lpe_count = 0;
+}
+
+/* The .machine pseudo op allows to switch to a different CPU level in
+ the asm listing. The current CPU setting can be stored on a stack
+ with .machine push and restored with .machine pop. */
+
+static void
+s390_machine (int ignore ATTRIBUTE_UNUSED)
+{
+ char *cpu_string;
+#define MAX_HISTORY 100
+ static unsigned int *cpu_history;
+ static int curr_hist;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ int len;
+ cpu_string = demand_copy_C_string (&len);
+ }
+ else
+ {
+ char c;
+ cpu_string = input_line_pointer;
+ c = get_symbol_end ();
+ cpu_string = xstrdup (cpu_string);
+ *input_line_pointer = c;
+ }
+
+ if (cpu_string != NULL)
+ {
+ unsigned int old_cpu = current_cpu;
+ unsigned int new_cpu;
+
+ if (strcmp (cpu_string, "push") == 0)
+ {
+ if (cpu_history == NULL)
+ cpu_history = xmalloc (MAX_HISTORY * sizeof (*cpu_history));
+
+ if (curr_hist >= MAX_HISTORY)
+ as_bad (_(".machine stack overflow"));
+ else
+ cpu_history[curr_hist++] = current_cpu;
+ }
+ else if (strcmp (cpu_string, "pop") == 0)
+ {
+ if (curr_hist <= 0)
+ as_bad (_(".machine stack underflow"));
+ else
+ current_cpu = cpu_history[--curr_hist];
+ }
+ else if ((new_cpu = s390_parse_cpu (cpu_string)) != (unsigned int)-1)
+ current_cpu = new_cpu;
+ else
+ as_bad (_("invalid machine `%s'"), cpu_string);
+
+ if (current_cpu != old_cpu)
+ s390_setup_opcodes ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* The .machinemode pseudo op allows to switch to a different
+ architecture mode in the asm listing. The current architecture
+ mode setting can be stored on a stack with .machinemode push and
+ restored with .machinemode pop. */
+
+static void
+s390_machinemode (int ignore ATTRIBUTE_UNUSED)
+{
+ char *mode_string;
+#define MAX_HISTORY 100
+ static unsigned int *mode_history;
+ static int curr_hist;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ int len;
+ mode_string = demand_copy_C_string (&len);
+ }
+ else
+ {
+ char c;
+ mode_string = input_line_pointer;
+ c = get_symbol_end ();
+ mode_string = xstrdup (mode_string);
+ *input_line_pointer = c;
+ }
+
+ if (mode_string != NULL)
+ {
+ unsigned int old_mode_mask = current_mode_mask;
+ char *p;
+
+ for (p = mode_string; *p != 0; p++)
+ *p = TOLOWER (*p);
+
+ if (strcmp (mode_string, "push") == 0)
+ {
+ if (mode_history == NULL)
+ mode_history = xmalloc (MAX_HISTORY * sizeof (*mode_history));
+
+ if (curr_hist >= MAX_HISTORY)
+ as_bad (_(".machinemode stack overflow"));
+ else
+ mode_history[curr_hist++] = current_mode_mask;
+ }
+ else if (strcmp (mode_string, "pop") == 0)
+ {
+ if (curr_hist <= 0)
+ as_bad (_(".machinemode stack underflow"));
+ else
+ current_mode_mask = mode_history[--curr_hist];
+ }
+ else
+ {
+ if (strcmp (mode_string, "esa") == 0)
+ current_mode_mask = 1 << S390_OPCODE_ESA;
+ else if (strcmp (mode_string, "zarch") == 0)
+ {
+ if (s390_arch_size == 32)
+ set_highgprs_p = TRUE;
+ current_mode_mask = 1 << S390_OPCODE_ZARCH;
+ }
+ else if (strcmp (mode_string, "zarch_nohighgprs") == 0)
+ current_mode_mask = 1 << S390_OPCODE_ZARCH;
+ else
+ as_bad (_("invalid machine `%s'"), mode_string);
+ }
+
+ if (current_mode_mask != old_mode_mask)
+ s390_setup_opcodes ();
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ return ieee_md_atof (type, litp, sizep, TRUE);
+}
+
+/* Align a section (I don't know why this is machine dependent). */
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+/* We don't have any form of relaxing. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ asection *seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+ return 0;
+}
+
+/* Convert a machine dependent frag. We never generate these. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+symbolS *
+md_undefined_symbol (char *name)
+{
+ if (*name == '_' && *(name + 1) == 'G'
+ && strcmp (name, "_GLOBAL_OFFSET_TABLE_") == 0)
+ {
+ if (!GOT_symbol)
+ {
+ if (symbol_find (name))
+ as_bad (_("GOT already in symbol table"));
+ GOT_symbol = symbol_new (name, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ }
+ return GOT_symbol;
+ }
+ return 0;
+}
+
+/* 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 ATTRIBUTE_UNUSED)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+/* Here we decide which fixups can be adjusted to make them relative to
+ the beginning of the section instead of the symbol. Basically we need
+ to make sure that the dynamic relocations are done correctly, so in
+ some cases we force the original symbol to be used. */
+int
+tc_s390_fix_adjustable (fixS *fixP)
+{
+ /* Don't adjust references to merge sections. */
+ if ((S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0)
+ return 0;
+ /* adjust_reloc_syms doesn't know about the GOT. */
+ if ( fixP->fx_r_type == BFD_RELOC_16_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_32_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_390_GOTOFF64
+ || fixP->fx_r_type == BFD_RELOC_390_PLTOFF16
+ || fixP->fx_r_type == BFD_RELOC_390_PLTOFF32
+ || fixP->fx_r_type == BFD_RELOC_390_PLTOFF64
+ || fixP->fx_r_type == BFD_RELOC_390_PLT12DBL
+ || fixP->fx_r_type == BFD_RELOC_390_PLT16DBL
+ || fixP->fx_r_type == BFD_RELOC_390_PLT24DBL
+ || fixP->fx_r_type == BFD_RELOC_390_PLT32
+ || fixP->fx_r_type == BFD_RELOC_390_PLT32DBL
+ || fixP->fx_r_type == BFD_RELOC_390_PLT64
+ || fixP->fx_r_type == BFD_RELOC_390_GOT12
+ || fixP->fx_r_type == BFD_RELOC_390_GOT20
+ || fixP->fx_r_type == BFD_RELOC_390_GOT16
+ || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_390_GOT64
+ || fixP->fx_r_type == BFD_RELOC_390_GOTENT
+ || fixP->fx_r_type == BFD_RELOC_390_GOTPLT12
+ || fixP->fx_r_type == BFD_RELOC_390_GOTPLT16
+ || fixP->fx_r_type == BFD_RELOC_390_GOTPLT20
+ || fixP->fx_r_type == BFD_RELOC_390_GOTPLT32
+ || fixP->fx_r_type == BFD_RELOC_390_GOTPLT64
+ || fixP->fx_r_type == BFD_RELOC_390_GOTPLTENT
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LOAD
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GDCALL
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LDCALL
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GD32
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GD64
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE12
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE20
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE32
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_GOTIE64
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LDM32
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LDM64
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_IE32
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_IE64
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_IEENT
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LE32
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LE64
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LDO32
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_LDO64
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_DTPMOD
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_DTPOFF
+ || fixP->fx_r_type == BFD_RELOC_390_TLS_TPOFF
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+ return 1;
+}
+
+/* Return true if we must always emit a reloc for a type and false if
+ there is some hope of resolving it at assembly time. */
+int
+tc_s390_force_relocation (struct fix *fixp)
+{
+ /* Ensure we emit a relocation for every reference to the global
+ offset table or to the procedure link table. */
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_390_GOT12:
+ case BFD_RELOC_390_GOT20:
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_390_GOTOFF64:
+ case BFD_RELOC_390_PLTOFF16:
+ case BFD_RELOC_390_PLTOFF32:
+ case BFD_RELOC_390_PLTOFF64:
+ case BFD_RELOC_390_GOTPC:
+ case BFD_RELOC_390_GOT16:
+ case BFD_RELOC_390_GOTPCDBL:
+ case BFD_RELOC_390_GOT64:
+ case BFD_RELOC_390_GOTENT:
+ case BFD_RELOC_390_PLT32:
+ case BFD_RELOC_390_PLT12DBL:
+ case BFD_RELOC_390_PLT16DBL:
+ case BFD_RELOC_390_PLT24DBL:
+ case BFD_RELOC_390_PLT32DBL:
+ case BFD_RELOC_390_PLT64:
+ case BFD_RELOC_390_GOTPLT12:
+ case BFD_RELOC_390_GOTPLT16:
+ case BFD_RELOC_390_GOTPLT20:
+ case BFD_RELOC_390_GOTPLT32:
+ case BFD_RELOC_390_GOTPLT64:
+ case BFD_RELOC_390_GOTPLTENT:
+ return 1;
+ default:
+ break;
+ }
+
+ return generic_force_reloc (fixp);
+}
+
+/* Apply a fixup to the object code. This is called for all the
+ fixups we generated by the call to fix_new_exp, above. In the call
+ above we used a reloc code which was the largest legal reloc code
+ plus the operand index. Here we undo that to recover the operand
+ index. At this point all symbol values should be fully resolved,
+ and we attempt to completely resolve the reloc. If we can not do
+ that, we determine the correct reloc code and put it back in the
+ fixup. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where;
+ valueT value = *valP;
+
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (fixP->fx_subsy != NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot emit relocation %s against subsy symbol %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type),
+ S_GET_NAME (fixP->fx_subsy));
+
+ if (fixP->fx_addsy != NULL)
+ {
+ if (fixP->fx_pcrel)
+ value += fixP->fx_frag->fr_address + fixP->fx_where;
+ }
+ else
+ fixP->fx_done = 1;
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ const struct s390_operand *operand;
+ int opindex;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = &s390_operands[opindex];
+
+ if (fixP->fx_done)
+ {
+ /* Insert the fully resolved operand value. */
+ s390_insert_operand ((unsigned char *) where, operand,
+ (offsetT) value, fixP->fx_file, fixP->fx_line);
+ return;
+ }
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into
+ relocs. */
+ fixP->fx_offset = value;
+ if (operand->bits == 12 && operand->shift == 20)
+ {
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ fixP->fx_r_type = BFD_RELOC_390_12;
+ }
+ else if (operand->bits == 12 && operand->shift == 36)
+ {
+ fixP->fx_size = 2;
+ fixP->fx_where += 4;
+ fixP->fx_r_type = BFD_RELOC_390_12;
+ }
+ else if (operand->bits == 20 && operand->shift == 20)
+ {
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ fixP->fx_r_type = BFD_RELOC_390_20;
+ }
+ else if (operand->bits == 8 && operand->shift == 8)
+ {
+ fixP->fx_size = 1;
+ fixP->fx_where += 1;
+ fixP->fx_r_type = BFD_RELOC_8;
+ }
+ else if (operand->bits == 12 && operand->shift == 12
+ && (operand->flags & S390_OPERAND_PCREL))
+ {
+ fixP->fx_size = 2;
+ fixP->fx_where += 1;
+ fixP->fx_offset += 1;
+ fixP->fx_r_type = BFD_RELOC_390_PC12DBL;
+ }
+ else if (operand->bits == 16 && operand->shift == 16)
+ {
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ if (operand->flags & S390_OPERAND_PCREL)
+ {
+ fixP->fx_r_type = BFD_RELOC_390_PC16DBL;
+ fixP->fx_offset += 2;
+ }
+ else
+ fixP->fx_r_type = BFD_RELOC_16;
+ }
+ else if (operand->bits == 24 && operand->shift == 24
+ && (operand->flags & S390_OPERAND_PCREL))
+ {
+ fixP->fx_size = 3;
+ fixP->fx_where += 3;
+ fixP->fx_offset += 3;
+ fixP->fx_r_type = BFD_RELOC_390_PC24DBL;
+ }
+ else if (operand->bits == 32 && operand->shift == 16
+ && (operand->flags & S390_OPERAND_PCREL))
+ {
+ fixP->fx_size = 4;
+ fixP->fx_where += 2;
+ fixP->fx_offset += 2;
+ fixP->fx_r_type = BFD_RELOC_390_PC32DBL;
+ }
+ else
+ {
+ char *sfile;
+ unsigned int sline;
+
+ /* Use expr_symbol_where to see if this is an expression
+ symbol. */
+ if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unsupported relocation type"));
+ fixP->fx_done = 1;
+ return;
+ }
+ }
+ else
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ if (fixP->fx_pcrel)
+ abort ();
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 1);
+ break;
+ case BFD_RELOC_390_12:
+ case BFD_RELOC_390_GOT12:
+ case BFD_RELOC_390_GOTPLT12:
+ case BFD_RELOC_390_PC12DBL:
+ case BFD_RELOC_390_PLT12DBL:
+ if (fixP->fx_pcrel)
+ value++;
+
+ if (fixP->fx_done)
+ {
+ unsigned short mop;
+
+ if (fixP->fx_pcrel)
+ value >>= 1;
+
+ mop = bfd_getb16 ((unsigned char *) where);
+ mop |= (unsigned short) (value & 0xfff);
+ bfd_putb16 ((bfd_vma) mop, (unsigned char *) where);
+ }
+ break;
+
+ case BFD_RELOC_390_20:
+ case BFD_RELOC_390_GOT20:
+ case BFD_RELOC_390_GOTPLT20:
+ if (fixP->fx_done)
+ {
+ unsigned int mop;
+ mop = bfd_getb32 ((unsigned char *) where);
+ mop |= (unsigned int) ((value & 0xfff) << 8 |
+ (value & 0xff000) >> 12);
+ bfd_putb32 ((bfd_vma) mop, (unsigned char *) where);
+ }
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_GPREL16:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_16_GOTOFF:
+ if (fixP->fx_pcrel)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot emit PC relative %s relocation%s%s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type),
+ fixP->fx_addsy != NULL ? " against " : "",
+ (fixP->fx_addsy != NULL
+ ? S_GET_NAME (fixP->fx_addsy)
+ : ""));
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 2);
+ break;
+ case BFD_RELOC_390_GOT16:
+ case BFD_RELOC_390_PLTOFF16:
+ case BFD_RELOC_390_GOTPLT16:
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 2);
+ break;
+ case BFD_RELOC_390_PC16DBL:
+ case BFD_RELOC_390_PLT16DBL:
+ value += 2;
+ if (fixP->fx_done)
+ md_number_to_chars (where, (offsetT) value >> 1, 2);
+ break;
+
+ case BFD_RELOC_390_PC24DBL:
+ case BFD_RELOC_390_PLT24DBL:
+ value += 3;
+ if (fixP->fx_done)
+ {
+ unsigned int mop;
+ value >>= 1;
+
+ mop = bfd_getb32 ((unsigned char *) where - 1);
+ mop |= (unsigned int) (value & 0xffffff);
+ bfd_putb32 ((bfd_vma) mop, (unsigned char *) where - 1);
+ }
+ break;
+
+ case BFD_RELOC_32:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ else
+ fixP->fx_r_type = BFD_RELOC_32;
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 4);
+ break;
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_32_BASEREL:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 4);
+ break;
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_390_PLTOFF32:
+ case BFD_RELOC_390_PLT32:
+ case BFD_RELOC_390_GOTPLT32:
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 4);
+ break;
+ case BFD_RELOC_390_PC32DBL:
+ case BFD_RELOC_390_PLT32DBL:
+ case BFD_RELOC_390_GOTPCDBL:
+ case BFD_RELOC_390_GOTENT:
+ case BFD_RELOC_390_GOTPLTENT:
+ value += 2;
+ if (fixP->fx_done)
+ md_number_to_chars (where, (offsetT) value >> 1, 4);
+ break;
+
+ case BFD_RELOC_32_GOTOFF:
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, sizeof (int));
+ break;
+
+ case BFD_RELOC_390_GOTOFF64:
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 8);
+ break;
+
+ case BFD_RELOC_390_GOT64:
+ case BFD_RELOC_390_PLTOFF64:
+ case BFD_RELOC_390_PLT64:
+ case BFD_RELOC_390_GOTPLT64:
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 8);
+ break;
+
+ case BFD_RELOC_64:
+ if (fixP->fx_pcrel)
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ else
+ fixP->fx_r_type = BFD_RELOC_64;
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 8);
+ break;
+
+ case BFD_RELOC_64_PCREL:
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ if (fixP->fx_done)
+ md_number_to_chars (where, value, 8);
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+ case BFD_RELOC_390_TLS_LOAD:
+ case BFD_RELOC_390_TLS_GDCALL:
+ case BFD_RELOC_390_TLS_LDCALL:
+ case BFD_RELOC_390_TLS_GD32:
+ case BFD_RELOC_390_TLS_GD64:
+ case BFD_RELOC_390_TLS_GOTIE12:
+ case BFD_RELOC_390_TLS_GOTIE20:
+ case BFD_RELOC_390_TLS_GOTIE32:
+ case BFD_RELOC_390_TLS_GOTIE64:
+ case BFD_RELOC_390_TLS_LDM32:
+ case BFD_RELOC_390_TLS_LDM64:
+ case BFD_RELOC_390_TLS_IE32:
+ case BFD_RELOC_390_TLS_IE64:
+ case BFD_RELOC_390_TLS_LE32:
+ case BFD_RELOC_390_TLS_LE64:
+ case BFD_RELOC_390_TLS_LDO32:
+ case BFD_RELOC_390_TLS_LDO64:
+ case BFD_RELOC_390_TLS_DTPMOD:
+ case BFD_RELOC_390_TLS_DTPOFF:
+ case BFD_RELOC_390_TLS_TPOFF:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fully resolved at link time. */
+ break;
+ case BFD_RELOC_390_TLS_IEENT:
+ /* Fully resolved at link time. */
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ value += 2;
+ break;
+
+ default:
+ {
+ const char *reloc_name = bfd_get_reloc_code_name (fixP->fx_r_type);
+
+ if (reloc_name != NULL)
+ as_fatal (_("Gas failure, reloc type %s\n"), reloc_name);
+ else
+ as_fatal (_("Gas failure, reloc type #%i\n"), fixP->fx_r_type);
+ }
+ }
+
+ fixP->fx_offset = value;
+ }
+}
+
+/* Generate a reloc for a fixup. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ bfd_reloc_code_real_type code;
+ arelent *reloc;
+
+ code = fixp->fx_r_type;
+ if (GOT_symbol && fixp->fx_addsy == GOT_symbol)
+ {
+ if ( (s390_arch_size == 32 && code == BFD_RELOC_32_PCREL)
+ || (s390_arch_size == 64 && code == BFD_RELOC_64_PCREL))
+ code = BFD_RELOC_390_GOTPC;
+ if (code == BFD_RELOC_390_PC32DBL)
+ code = BFD_RELOC_390_GOTPCDBL;
+ }
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (code));
+ /* Set howto to a garbage value so that we can keep going. */
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (reloc->howto != NULL);
+ }
+ reloc->addend = fixp->fx_offset;
+
+ return reloc;
+}
+
+void
+s390_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (15, s390_arch_size == 64 ? 160 : 96);
+}
+
+int
+tc_s390_regname_to_dw2regnum (char *regname)
+{
+ int regnum = -1;
+
+ if (regname[0] != 'c' && regname[0] != 'a')
+ {
+ regnum = reg_name_search (pre_defined_registers, REG_NAME_CNT, regname);
+ if (regname[0] == 'f' && regnum != -1)
+ regnum += 16;
+ }
+ else if (strcmp (regname, "ap") == 0)
+ regnum = 32;
+ else if (strcmp (regname, "cc") == 0)
+ regnum = 33;
+ return regnum;
+}
+
+void
+s390_elf_final_processing (void)
+{
+ if (set_highgprs_p)
+ elf_elfheader (stdoutput)->e_flags |= EF_S390_HIGH_GPRS;
+}
diff --git a/gas/config/tc-s390.h b/gas/config/tc-s390.h
new file mode 100644
index 0000000..88c857b
--- /dev/null
+++ b/gas/config/tc-s390.h
@@ -0,0 +1,100 @@
+/* tc-s390.h -- Header file for tc-s390.c.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+ Written by Martin Schwidefsky (schwidefsky@de.ibm.com).
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_S390
+
+struct fix;
+
+#define TC_FORCE_RELOCATION(FIX) tc_s390_force_relocation(FIX)
+extern int tc_s390_force_relocation (struct fix *);
+
+/* Don't resolve foo@PLT-bar to offset@PLT. */
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \
+ (! SEG_NORMAL (SEG) || TC_FORCE_RELOCATION (FIX))
+
+#define tc_fix_adjustable(X) tc_s390_fix_adjustable(X)
+extern int tc_s390_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_s390
+extern enum bfd_architecture s390_arch (void);
+
+/* The target BFD format. */
+#define TARGET_FORMAT s390_target_format()
+extern const char *s390_target_format (void);
+
+/* Set the endianness we are using. */
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Whether or not the target is big endian */
+extern int target_big_endian;
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+/* $ is used to refer to the current location. */
+/* #define DOLLAR_DOT */
+
+/* We need to be able to make relocations involving the difference of
+ two symbols. This includes the difference of two symbols when
+ one of them is undefined (this comes up in PIC code generation).
+ */
+#define UNDEFINED_DIFFERENCE_OK
+
+/* foo-. gets turned into PC relative relocs */
+#define DIFF_EXPR_OK
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+#define NOP_OPCODE 0x07
+
+/* call md_pcrel_from_section, not md_pcrel_from */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section(FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define md_operand(x)
+
+extern void s390_md_end (void);
+#define md_end() s390_md_end ()
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions s390_cfi_frame_initial_instructions
+extern void s390_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_s390_regname_to_dw2regnum
+extern int tc_s390_regname_to_dw2regnum (char *regname);
+
+extern int s390_cie_data_alignment;
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+#define DWARF2_DEFAULT_RETURN_COLUMN 14
+#define DWARF2_CIE_DATA_ALIGNMENT s390_cie_data_alignment
+
+extern void s390_elf_final_processing (void);
+
+#define elf_tc_final_processing s390_elf_final_processing
diff --git a/gas/config/tc-score.c b/gas/config/tc-score.c
new file mode 100644
index 0000000..72597a0
--- /dev/null
+++ b/gas/config/tc-score.c
@@ -0,0 +1,7836 @@
+/* tc-score.c -- Assembler for Score
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Contributed by:
+ Brain.lin (brain.lin@sunplusct.com)
+ Mei Ligang (ligang@sunnorth.com.cn)
+ Pei-Lin Tsai (pltsai@sunplus.com)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "tc-score7.c"
+
+static void s3_s_score_bss (int ignore ATTRIBUTE_UNUSED);
+static void s3_s_score_text (int ignore);
+static void s3_score_s_section (int ignore);
+static void s3_s_change_sec (int sec);
+static void s3_s_score_mask (int reg_type ATTRIBUTE_UNUSED);
+static void s3_s_score_ent (int aent);
+static void s3_s_score_frame (int ignore ATTRIBUTE_UNUSED);
+static void s3_s_score_end (int x ATTRIBUTE_UNUSED);
+static void s3_s_score_set (int x ATTRIBUTE_UNUSED);
+static void s3_s_score_cpload (int ignore ATTRIBUTE_UNUSED);
+static void s3_s_score_cprestore (int ignore ATTRIBUTE_UNUSED);
+static void s3_s_score_gpword (int ignore ATTRIBUTE_UNUSED);
+static void s3_s_score_cpadd (int ignore ATTRIBUTE_UNUSED);
+static void s3_s_score_lcomm (int bytes_p);
+
+static void s_score_bss (int ignore ATTRIBUTE_UNUSED);
+static void s_score_text (int ignore);
+static void s_section (int ignore);
+static void s_change_sec (int sec);
+static void s_score_mask (int reg_type ATTRIBUTE_UNUSED);
+static void s_score_ent (int aent);
+static void s_score_frame (int ignore ATTRIBUTE_UNUSED);
+static void s_score_end (int x ATTRIBUTE_UNUSED);
+static void s_score_set (int x ATTRIBUTE_UNUSED);
+static void s_score_cpload (int ignore ATTRIBUTE_UNUSED);
+static void s_score_cprestore (int ignore ATTRIBUTE_UNUSED);
+static void s_score_gpword (int ignore ATTRIBUTE_UNUSED);
+static void s_score_cpadd (int ignore ATTRIBUTE_UNUSED);
+static void s_score_lcomm (int bytes_p);
+
+/* s3: hooks. */
+static void s3_md_number_to_chars (char *buf, valueT val, int n);
+static valueT s3_md_chars_to_number (char *buf, int n);
+static void s3_assemble (char *str);
+static void s3_operand (expressionS *);
+static void s3_begin (void);
+static void s3_number_to_chars (char *buf, valueT val, int n);
+static char *s3_atof (int type, char *litP, int *sizeP);
+static void s3_frag_check (fragS * fragp ATTRIBUTE_UNUSED);
+static void s3_validate_fix (fixS *fixP);
+static int s3_force_relocation (struct fix *fixp);
+static bfd_boolean s3_fix_adjustable (fixS * fixP);
+static void s3_elf_final_processing (void);
+static int s3_estimate_size_before_relax (fragS * fragp, asection * sec ATTRIBUTE_UNUSED);
+static int s3_relax_frag (asection * sec ATTRIBUTE_UNUSED, fragS * fragp, long stretch ATTRIBUTE_UNUSED);
+static void s3_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, fragS * fragp);
+static long s3_pcrel_from (fixS * fixP);
+static valueT s3_section_align (segT segment ATTRIBUTE_UNUSED, valueT size);
+static void s3_apply_fix (fixS *fixP, valueT *valP, segT seg);
+static arelent **s3_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp);
+
+/* s3: utils. */
+static void s3_do_ldst_insn (char *);
+static void s3_do_crdcrscrsimm5 (char *);
+static void s3_do_ldst_unalign (char *);
+static void s3_do_ldst_atomic (char *);
+static void s3_do_ldst_cop (char *);
+static void s3_do_macro_li_rdi32 (char *);
+static void s3_do_macro_la_rdi32 (char *);
+static void s3_do_macro_rdi32hi (char *);
+static void s3_do_macro_rdi32lo (char *);
+static void s3_do_macro_mul_rdrsrs (char *);
+static void s3_do_macro_bcmp (char *);
+static void s3_do_macro_bcmpz (char *);
+static void s3_do_macro_ldst_label (char *);
+static void s3_do_branch (char *);
+static void s3_do_jump (char *);
+static void s3_do_empty (char *);
+static void s3_do16_int (char *);
+static void s3_do_rdrsrs (char *);
+static void s3_do_rdsi16 (char *);
+static void s3_do_rdrssi14 (char *);
+static void s3_do_sub_rdsi16 (char *);
+static void s3_do_sub_rdi16 (char *);
+static void s3_do_sub_rdrssi14 (char *);
+static void s3_do_rdrsi5 (char *);
+static void s3_do_rdrsi14 (char *);
+static void s3_do_rdi16 (char *);
+static void s3_do_ldis (char *);
+static void s3_do_xrsi5 (char *);
+static void s3_do_rdrs (char *);
+static void s3_do_rdxrs (char *);
+static void s3_do_rsrs (char *);
+static void s3_do_rdcrs (char *);
+static void s3_do_rdsrs (char *);
+static void s3_do_rd (char *);
+static void s3_do16_dsp (char *);
+static void s3_do16_dsp2 (char *);
+static void s3_do_dsp (char *);
+static void s3_do_dsp2 (char *);
+static void s3_do_dsp3 (char *);
+static void s3_do_rs (char *);
+static void s3_do_i15 (char *);
+static void s3_do_xi5x (char *);
+static void s3_do_ceinst (char *);
+static void s3_do_cache (char *);
+static void s3_do16_rdrs2 (char *);
+static void s3_do16_br (char *);
+static void s3_do16_brr (char *);
+static void s3_do_ltb (char *);
+static void s3_do16_mv_cmp (char *);
+static void s3_do16_addi (char *);
+static void s3_do16_cmpi (char *);
+static void s3_do16_rdi5 (char *);
+static void s3_do16_xi5 (char *);
+static void s3_do16_ldst_insn (char *);
+static void s3_do16_slli_srli (char *);
+static void s3_do16_ldiu (char *);
+static void s3_do16_push_pop (char *);
+static void s3_do16_rpush (char *);
+static void s3_do16_rpop (char *);
+static void s3_do16_branch (char *);
+static void s3_do_lw48 (char *);
+static void s3_do_sw48 (char *);
+static void s3_do_ldi48 (char *);
+static void s3_do_sdbbp48 (char *);
+static void s3_do_and48 (char *);
+static void s3_do_or48 (char *);
+static void s3_do_mbitclr (char *);
+static void s3_do_mbitset (char *);
+static void s3_do_rdi16_pic (char *);
+static void s3_do_addi_s_pic (char *);
+static void s3_do_addi_u_pic (char *);
+static void s3_do_lw_pic (char *);
+
+#define MARCH_SCORE3 "score3"
+#define MARCH_SCORE3D "score3d"
+#define MARCH_SCORE7 "score7"
+#define MARCH_SCORE7D "score7d"
+#define MARCH_SCORE5 "score5"
+#define MARCH_SCORE5U "score5u"
+
+#define SCORE_BI_ENDIAN
+
+#ifdef SCORE_BI_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#else
+#if TARGET_BYTES_BIG_ENDIAN
+#define OPTION_EB (OPTION_MD_BASE + 0)
+#else
+#define OPTION_EL (OPTION_MD_BASE + 1)
+#endif
+#endif
+#define OPTION_FIXDD (OPTION_MD_BASE + 2)
+#define OPTION_NWARN (OPTION_MD_BASE + 3)
+#define OPTION_SCORE5 (OPTION_MD_BASE + 4)
+#define OPTION_SCORE5U (OPTION_MD_BASE + 5)
+#define OPTION_SCORE7 (OPTION_MD_BASE + 6)
+#define OPTION_R1 (OPTION_MD_BASE + 7)
+#define OPTION_O0 (OPTION_MD_BASE + 8)
+#define OPTION_SCORE_VERSION (OPTION_MD_BASE + 9)
+#define OPTION_PIC (OPTION_MD_BASE + 10)
+#define OPTION_MARCH (OPTION_MD_BASE + 11)
+#define OPTION_SCORE3 (OPTION_MD_BASE + 12)
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "#";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+/* Chars that can be used to separate mant from exp in floating point numbers. */
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
+
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+symbolS *GOT_symbol;
+#endif
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"bss", s_score_bss, 0},
+ {"text", s_score_text, 0},
+ {"word", cons, 4},
+ {"long", cons, 4},
+ {"extend", float_cons, 'x'},
+ {"ldouble", float_cons, 'x'},
+ {"packed", float_cons, 'p'},
+ {"end", s_score_end, 0},
+ {"ent", s_score_ent, 0},
+ {"frame", s_score_frame, 0},
+ {"rdata", s_change_sec, 'r'},
+ {"sdata", s_change_sec, 's'},
+ {"set", s_score_set, 0},
+ {"mask", s_score_mask, 'R'},
+ {"dword", cons, 8},
+ {"lcomm", s_score_lcomm, 1},
+ {"section", s_section, 0},
+ {"cpload", s_score_cpload, 0},
+ {"cprestore", s_score_cprestore, 0},
+ {"gpword", s_score_gpword, 0},
+ {"cpadd", s_score_cpadd, 0},
+ {0, 0, 0}
+};
+
+const char *md_shortopts = "nO::g::G:";
+struct option md_longopts[] =
+{
+#ifdef OPTION_EB
+ {"EB" , no_argument, NULL, OPTION_EB},
+#endif
+#ifdef OPTION_EL
+ {"EL" , no_argument, NULL, OPTION_EL},
+#endif
+ {"FIXDD" , no_argument, NULL, OPTION_FIXDD},
+ {"NWARN" , no_argument, NULL, OPTION_NWARN},
+ {"SCORE5" , no_argument, NULL, OPTION_SCORE5},
+ {"SCORE5U", no_argument, NULL, OPTION_SCORE5U},
+ {"SCORE7" , no_argument, NULL, OPTION_SCORE7},
+ {"USE_R1" , no_argument, NULL, OPTION_R1},
+ {"O0" , no_argument, NULL, OPTION_O0},
+ {"V" , no_argument, NULL, OPTION_SCORE_VERSION},
+ {"KPIC" , no_argument, NULL, OPTION_PIC},
+ {"march=" , required_argument, NULL, OPTION_MARCH},
+ {"SCORE3" , no_argument, NULL, OPTION_SCORE3},
+ {NULL , no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+#define s3_GP 28
+#define s3_PIC_CALL_REG 29
+#define s3_MAX_LITERAL_POOL_SIZE 1024
+#define s3_FAIL 0x80000000
+#define s3_SUCCESS 0
+#define s3_INSN48_SIZE 6
+#define s3_INSN_SIZE 4
+#define s3_INSN16_SIZE 2
+#define s3_RELAX_INST_NUM 3
+
+/* For score5u : div/mul will pop warning message, mmu/alw/asw will pop error message. */
+#define s3_BAD_ARGS _("bad arguments to instruction")
+#define s3_ERR_FOR_SCORE5U_MUL_DIV _("div / mul are reserved instructions")
+#define s3_ERR_FOR_SCORE5U_MMU _("This architecture doesn't support mmu")
+#define s3_ERR_FOR_SCORE5U_ATOMIC _("This architecture doesn't support atomic instruction")
+#define s3_BAD_SKIP_COMMA s3_BAD_ARGS
+#define s3_BAD_GARBAGE _("garbage following instruction");
+
+#define s3_skip_whitespace(str) while (*(str) == ' ') ++(str)
+
+/* The name of the readonly data section. */
+#define s3_RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
+ ? ".data" \
+ : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_coff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ ? ".rodata" \
+ : (abort (), ""))
+
+#define s3_RELAX_ENCODE(old, new, type, reloc1, reloc2, opt) \
+ ((relax_substateT) \
+ (((old) << 23) \
+ | ((new) << 16) \
+ | ((type) << 9) \
+ | ((reloc1) << 5) \
+ | ((reloc2) << 1) \
+ | ((opt) ? 1 : 0)))
+
+#define s3_RELAX_OLD(i) (((i) >> 23) & 0x7f)
+#define s3_RELAX_NEW(i) (((i) >> 16) & 0x7f)
+#define s3_RELAX_TYPE(i) (((i) >> 9) & 0x7f)
+#define s3_RELAX_RELOC1(i) ((valueT) ((i) >> 5) & 0xf)
+#define s3_RELAX_RELOC2(i) ((valueT) ((i) >> 1) & 0xf)
+#define s3_RELAX_OPT(i) ((i) & 1)
+
+#define s3_SET_INSN_ERROR(s) (s3_inst.error = (s))
+#define s3_INSN_IS_PCE_P(s) (strstr (str, "||") != NULL)
+#define s3_INSN_IS_48_P(s) (strstr (str, "48") != NULL)
+#define s3_GET_INSN_CLASS(type) (s3_get_insn_class_from_type (type))
+#define s3_GET_INSN_SIZE(type) ((s3_GET_INSN_CLASS (type) == INSN_CLASS_16) \
+ ? s3_INSN16_SIZE : (s3_GET_INSN_CLASS (type) == INSN_CLASS_48) \
+ ? s3_INSN48_SIZE : s3_INSN_SIZE)
+
+#define s3_MAX_LITTLENUMS 6
+#define s3_INSN_NAME_LEN 16
+
+/* Relax will need some padding for alignment. */
+#define s3_RELAX_PAD_BYTE 3
+
+
+#define s3_USE_GLOBAL_POINTER_OPT 1
+
+/* Enumeration matching entries in table above. */
+enum s3_score_reg_type
+{
+ s3_REG_TYPE_SCORE = 0,
+#define s3_REG_TYPE_FIRST s3_REG_TYPE_SCORE
+ s3_REG_TYPE_SCORE_SR = 1,
+ s3_REG_TYPE_SCORE_CR = 2,
+ s3_REG_TYPE_MAX = 3
+};
+
+enum s3_score_pic_level
+{
+ s3_NO_PIC,
+ s3_PIC
+};
+static enum s3_score_pic_level s3_score_pic = s3_NO_PIC;
+
+enum s3_insn_type_for_dependency
+{
+ s3_D_mtcr,
+ s3_D_all_insn
+};
+
+struct s3_insn_to_dependency
+{
+ char *insn_name;
+ enum s3_insn_type_for_dependency type;
+};
+
+struct s3_data_dependency
+{
+ enum s3_insn_type_for_dependency pre_insn_type;
+ char pre_reg[6];
+ enum s3_insn_type_for_dependency cur_insn_type;
+ char cur_reg[6];
+ int bubblenum_7;
+ int bubblenum_3;
+ int warn_or_error; /* warning - 0; error - 1 */
+};
+
+static const struct s3_insn_to_dependency s3_insn_to_dependency_table[] =
+{
+ /* move spectial instruction. */
+ {"mtcr", s3_D_mtcr},
+};
+
+static const struct s3_data_dependency s3_data_dependency_table[] =
+{
+ /* Status regiser. */
+ {s3_D_mtcr, "cr0", s3_D_all_insn, "", 5, 1, 0},
+};
+
+/* Used to contain constructed error messages. */
+static char s3_err_msg[255];
+
+static int s3_fix_data_dependency = 0;
+static int s3_warn_fix_data_dependency = 1;
+
+static int s3_in_my_get_expression = 0;
+
+/* Default, pop warning message when using r1. */
+static int s3_nor1 = 1;
+
+/* Default will do instruction relax, -O0 will set s3_g_opt = 0. */
+static unsigned int s3_g_opt = 1;
+
+/* The size of the small data section. */
+static unsigned int s3_g_switch_value = 8;
+
+static segT s3_pdr_seg;
+
+struct s3_score_it
+{
+ char name[s3_INSN_NAME_LEN];
+ bfd_vma instruction;
+ bfd_vma relax_inst;
+ int size;
+ int relax_size;
+ enum score_insn_type type;
+ char str[s3_MAX_LITERAL_POOL_SIZE];
+ const char *error;
+ int bwarn;
+ char reg[s3_INSN_NAME_LEN];
+ struct
+ {
+ bfd_reloc_code_real_type type;
+ expressionS exp;
+ int pc_rel;
+ }reloc;
+};
+static struct s3_score_it s3_inst;
+
+typedef struct s3_proc
+{
+ symbolS *isym;
+ unsigned long reg_mask;
+ unsigned long reg_offset;
+ unsigned long fpreg_mask;
+ unsigned long leaf;
+ unsigned long frame_offset;
+ unsigned long frame_reg;
+ unsigned long pc_reg;
+} s3_procS;
+static s3_procS s3_cur_proc;
+static s3_procS *s3_cur_proc_ptr;
+static int s3_numprocs;
+
+
+/* Structure for a hash table entry for a register. */
+struct s3_reg_entry
+{
+ const char *name;
+ int number;
+};
+
+static const struct s3_reg_entry s3_score_rn_table[] =
+{
+ {"r0", 0}, {"r1", 1}, {"r2", 2}, {"r3", 3},
+ {"r4", 4}, {"r5", 5}, {"r6", 6}, {"r7", 7},
+ {"r8", 8}, {"r9", 9}, {"r10", 10}, {"r11", 11},
+ {"r12", 12}, {"r13", 13}, {"r14", 14}, {"r15", 15},
+ {"r16", 16}, {"r17", 17}, {"r18", 18}, {"r19", 19},
+ {"r20", 20}, {"r21", 21}, {"r22", 22}, {"r23", 23},
+ {"r24", 24}, {"r25", 25}, {"r26", 26}, {"r27", 27},
+ {"r28", 28}, {"r29", 29}, {"r30", 30}, {"r31", 31},
+ {NULL, 0}
+};
+
+static const struct s3_reg_entry s3_score_srn_table[] =
+{
+ {"sr0", 0}, {"sr1", 1}, {"sr2", 2},
+ {NULL, 0}
+};
+
+static const struct s3_reg_entry s3_score_crn_table[] =
+{
+ {"cr0", 0}, {"cr1", 1}, {"cr2", 2}, {"cr3", 3},
+ {"cr4", 4}, {"cr5", 5}, {"cr6", 6}, {"cr7", 7},
+ {"cr8", 8}, {"cr9", 9}, {"cr10", 10}, {"cr11", 11},
+ {"cr12", 12}, {"cr13", 13}, {"cr14", 14}, {"cr15", 15},
+ {"cr16", 16}, {"cr17", 17}, {"cr18", 18}, {"cr19", 19},
+ {"cr20", 20}, {"cr21", 21}, {"cr22", 22}, {"cr23", 23},
+ {"cr24", 24}, {"cr25", 25}, {"cr26", 26}, {"cr27", 27},
+ {"cr28", 28}, {"cr29", 29}, {"cr30", 30}, {"cr31", 31},
+ {NULL, 0}
+};
+
+struct s3_reg_map
+{
+ const struct s3_reg_entry *names;
+ int max_regno;
+ struct hash_control *htab;
+ const char *expected;
+};
+
+static struct s3_reg_map s3_all_reg_maps[] =
+{
+ {s3_score_rn_table, 31, NULL, N_("S+core register expected")},
+ {s3_score_srn_table, 2, NULL, N_("S+core special-register expected")},
+ {s3_score_crn_table, 31, NULL, N_("S+core co-processor register expected")},
+};
+
+static struct hash_control *s3_score_ops_hsh = NULL;
+static struct hash_control *s3_dependency_insn_hsh = NULL;
+
+
+struct s3_datafield_range
+{
+ int data_type;
+ int bits;
+ int range[2];
+};
+
+static struct s3_datafield_range s3_score_df_range[] =
+{
+ {_IMM4, 4, {0, (1 << 4) - 1}}, /* ( 0 ~ 15 ) */
+ {_IMM5, 5, {0, (1 << 5) - 1}}, /* ( 0 ~ 31 ) */
+ {_IMM8, 8, {0, (1 << 8) - 1}}, /* ( 0 ~ 255 ) */
+ {_IMM14, 14, {0, (1 << 14) - 1}}, /* ( 0 ~ 16383) */
+ {_IMM15, 15, {0, (1 << 15) - 1}}, /* ( 0 ~ 32767) */
+ {_IMM16, 16, {0, (1 << 16) - 1}}, /* ( 0 ~ 65535) */
+ {_SIMM10, 10, {-(1 << 9), (1 << 9) - 1}}, /* ( -512 ~ 511 ) */
+ {_SIMM12, 12, {-(1 << 11), (1 << 11) - 1}}, /* ( -2048 ~ 2047 ) */
+ {_SIMM14, 14, {-(1 << 13), (1 << 13) - 1}}, /* ( -8192 ~ 8191 ) */
+ {_SIMM15, 15, {-(1 << 14), (1 << 14) - 1}}, /* (-16384 ~ 16383) */
+ {_SIMM16, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_SIMM14_NEG, 14, {-(1 << 13), (1 << 13) - 1}}, /* ( -8191 ~ 8192 ) */
+ {_IMM16_NEG, 16, {0, (1 << 16) - 1}}, /* (-65535 ~ 0 ) */
+ {_SIMM16_NEG, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM20, 20, {0, (1 << 20) - 1}},
+ {_IMM25, 25, {0, (1 << 25) - 1}},
+ {_DISP8div2, 8, {-(1 << 8), (1 << 8) - 1}}, /* ( -256 ~ 255 ) */
+ {_DISP11div2, 11, {0, 0}},
+ {_DISP19div2, 19, {-(1 << 19), (1 << 19) - 1}}, /* (-524288 ~ 524287) */
+ {_DISP24div2, 24, {0, 0}},
+ {_VALUE, 32, {0, ((unsigned int)1 << 31) - 1}},
+ {_VALUE_HI16, 16, {0, (1 << 16) - 1}},
+ {_VALUE_LO16, 16, {0, (1 << 16) - 1}},
+ {_VALUE_LDST_LO16, 16, {0, (1 << 16) - 1}},
+ {_SIMM16_LA, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM5_RSHIFT_1, 5, {0, (1 << 6) - 1}}, /* ( 0 ~ 63 ) */
+ {_IMM5_RSHIFT_2, 5, {0, (1 << 7) - 1}}, /* ( 0 ~ 127 ) */
+ {_SIMM16_LA_POS, 16, {0, (1 << 15) - 1}}, /* ( 0 ~ 32767) */
+ {_IMM5_RANGE_8_31, 5, {8, 31}}, /* But for cop0 the valid data : (8 ~ 31). */
+ {_IMM10_RSHIFT_2, 10, {-(1 << 11), (1 << 11) - 1}}, /* For ldc#, stc#. */
+ {_SIMM10, 10, {0, (1 << 10) - 1}}, /* ( -1024 ~ 1023 ) */
+ {_SIMM12, 12, {0, (1 << 12) - 1}}, /* ( -2048 ~ 2047 ) */
+ {_SIMM14, 14, {0, (1 << 14) - 1}}, /* ( -8192 ~ 8191 ) */
+ {_SIMM15, 15, {0, (1 << 15) - 1}}, /* (-16384 ~ 16383) */
+ {_SIMM16, 16, {0, (1 << 16) - 1}}, /* (-65536 ~ 65536) */
+ {_SIMM14_NEG, 14, {0, (1 << 16) - 1}}, /* ( -8191 ~ 8192 ) */
+ {_IMM16_NEG, 16, {0, (1 << 16) - 1}}, /* ( 65535 ~ 0 ) */
+ {_SIMM16_NEG, 16, {0, (1 << 16) - 1}}, /* ( 65535 ~ 0 ) */
+ {_IMM20, 20, {0, (1 << 20) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM25, 25, {0, (1 << 25) - 1}}, /* (-32768 ~ 32767) */
+ {_GP_IMM15, 15, {0, (1 << 15) - 1}}, /* ( 0 ~ 65535) */
+ {_GP_IMM14, 14, {0, (1 << 14) - 1}}, /* ( 0 ~ 65535) */
+ {_SIMM16_pic, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM16_LO16_pic, 16, {0, (1 << 16) - 1}}, /* ( 65535 ~ 0 ) */
+ {_IMM16_pic, 16, {0, (1 << 16) - 1}}, /* ( 0 ~ 65535) */
+ {_SIMM5, 5, {-(1 << 4), (1 << 4) - 1}}, /* ( -16 ~ 15 ) */
+ {_SIMM6, 6, {-(1 << 5), (1 << 5) - 1}}, /* ( -32 ~ 31 ) */
+ {_IMM32, 32, {0, 0xfffffff}},
+ {_SIMM32, 32, {-0x80000000, 0x7fffffff}},
+ {_IMM11, 11, {0, (1 << 11) - 1}},
+};
+
+struct s3_asm_opcode
+{
+ /* Instruction name. */
+ const char *template_name;
+
+ /* Instruction Opcode. */
+ bfd_vma value;
+
+ /* Instruction bit mask. */
+ bfd_vma bitmask;
+
+ /* Relax instruction opcode. 0x8000 imply no relaxation. */
+ bfd_vma relax_value;
+
+ /* Instruction type. */
+ enum score_insn_type type;
+
+ /* Function to call to parse args. */
+ void (*parms) (char *);
+};
+
+static const struct s3_asm_opcode s3_score_ldst_insns[] =
+{
+ {"lw", 0x20000000, 0x3e000000, 0x1000, Rd_rvalueRs_SI15, s3_do_ldst_insn},
+ {"lw", 0x06000000, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s3_do_ldst_insn},
+ {"lw", 0x0e000000, 0x3e000007, 0x0040, Rd_rvalueRs_postSI12, s3_do_ldst_insn},
+ {"lh", 0x22000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, s3_do_ldst_insn},
+ {"lh", 0x06000001, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s3_do_ldst_insn},
+ {"lh", 0x0e000001, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s3_do_ldst_insn},
+ {"lhu", 0x24000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, s3_do_ldst_insn},
+ {"lhu", 0x06000002, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s3_do_ldst_insn},
+ {"lhu", 0x0e000002, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s3_do_ldst_insn},
+ {"lb", 0x26000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, s3_do_ldst_insn},
+ {"lb", 0x06000003, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s3_do_ldst_insn},
+ {"lb", 0x0e000003, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s3_do_ldst_insn},
+ {"sw", 0x28000000, 0x3e000000, 0x2000, Rd_lvalueRs_SI15, s3_do_ldst_insn},
+ {"sw", 0x06000004, 0x3e000007, 0x0060, Rd_lvalueRs_preSI12, s3_do_ldst_insn},
+ {"sw", 0x0e000004, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, s3_do_ldst_insn},
+ {"sh", 0x2a000000, 0x3e000000, 0x8000, Rd_lvalueRs_SI15, s3_do_ldst_insn},
+ {"sh", 0x06000005, 0x3e000007, 0x8000, Rd_lvalueRs_preSI12, s3_do_ldst_insn},
+ {"sh", 0x0e000005, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, s3_do_ldst_insn},
+ {"lbu", 0x2c000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, s3_do_ldst_insn},
+ {"lbu", 0x06000006, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s3_do_ldst_insn},
+ {"lbu", 0x0e000006, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s3_do_ldst_insn},
+ {"sb", 0x2e000000, 0x3e000000, 0x8000, Rd_lvalueRs_SI15, s3_do_ldst_insn},
+ {"sb", 0x06000007, 0x3e000007, 0x8000, Rd_lvalueRs_preSI12, s3_do_ldst_insn},
+ {"sb", 0x0e000007, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, s3_do_ldst_insn},
+};
+
+static const struct s3_asm_opcode s3_score_insns[] =
+{
+ {"abs", 0x3800000a, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_dsp3},
+ {"abs.s", 0x3800004b, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_dsp3},
+ {"add", 0x00000010, 0x3e0003ff, 0x4800, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"add.c", 0x00000011, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"add.s", 0x38000048, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_dsp2},
+ {"addc", 0x00000012, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"addc.c", 0x00000013, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"addi", 0x02000000, 0x3e0e0001, 0x5c00, Rd_SI16, s3_do_rdsi16},
+ {"addi.c", 0x02000001, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_rdsi16},
+ {"addis", 0x0a000000, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_rdi16},
+ {"addis.c", 0x0a000001, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_rdi16},
+ {"addi!", 0x5c00, 0x7c00, 0x8000, Rd_SI6, s3_do16_addi},
+ {"addri", 0x10000000, 0x3e000001, 0x8000, Rd_Rs_SI14, s3_do_rdrssi14},
+ {"addri.c", 0x10000001, 0x3e000001, 0x8000, Rd_Rs_SI14, s3_do_rdrssi14},
+
+ /* add.c <-> add!. */
+ {"add!", 0x4800, 0x7f00, 0x8000, Rd_Rs, s3_do16_rdrs2},
+ {"subi", 0x02000000, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_sub_rdsi16},
+ {"subi.c", 0x02000001, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_sub_rdsi16},
+ {"subis", 0x0a000000, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_sub_rdi16},
+ {"subis.c", 0x0a000001, 0x3e0e0001, 0x8000, Rd_SI16, s3_do_sub_rdi16},
+ {"subri", 0x10000000, 0x3e000001, 0x8000, Rd_Rs_SI14, s3_do_sub_rdrssi14},
+ {"subri.c", 0x10000001, 0x3e000001, 0x8000, Rd_Rs_SI14, s3_do_sub_rdrssi14},
+ {"and", 0x00000020, 0x3e0003ff, 0x4b00, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"and.c", 0x00000021, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"andi", 0x02080000, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"andi.c", 0x02080001, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"andis", 0x0a080000, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"andis.c", 0x0a080001, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"andri", 0x18000000, 0x3e000001, 0x8000, Rd_Rs_I14, s3_do_rdrsi14},
+ {"andri.c", 0x18000001, 0x3e000001, 0x8000, Rd_Rs_I14, s3_do_rdrsi14},
+
+ /* and.c <-> and!. */
+ {"and!", 0x4b00, 0x7f00, 0x8000, Rd_Rs, s3_do16_rdrs2},
+ {"bcs", 0x08000000, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bcc", 0x08000400, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bcnz", 0x08003800, 0x3e007c01, 0x3200, PC_DISP19div2, s3_do_branch},
+ {"bcsl", 0x08000001, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bccl", 0x08000401, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bcnzl", 0x08003801, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bcnz!", 0x3200, 0x7f00, 0x08003800, PC_DISP8div2, s3_do16_branch},
+ {"beq", 0x08001000, 0x3e007c01, 0x3800, PC_DISP19div2, s3_do_branch},
+ {"beql", 0x08001001, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"beq!", 0x3800, 0x7e00, 0x08001000, PC_DISP8div2, s3_do16_branch},
+ {"bgtu", 0x08000800, 0x3e007c01, 0x3400, PC_DISP19div2, s3_do_branch},
+ {"bgt", 0x08001800, 0x3e007c01, 0x3c00, PC_DISP19div2, s3_do_branch},
+ {"bge", 0x08002000, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bgtul", 0x08000801, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bgtl", 0x08001801, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bgel", 0x08002001, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bgtu!", 0x3400, 0x7e00, 0x08000800, PC_DISP8div2, s3_do16_branch},
+ {"bgt!", 0x3c00, 0x7e00, 0x08001800, PC_DISP8div2, s3_do16_branch},
+ {"bitclr", 0x00000028, 0x3e0003ff, 0x5000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"bitclr.c", 0x00000029, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+
+ {"mbitclr", 0x00000064, 0x3e00007e, 0x8000, Ra_I9_I5, s3_do_mbitclr},
+ {"mbitset", 0x0000006c, 0x3e00007e, 0x8000, Ra_I9_I5, s3_do_mbitset},
+
+ {"bitrev", 0x3800000c, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_dsp2},
+ {"bitset", 0x0000002a, 0x3e0003ff, 0x5200, Rd_Rs_I5, s3_do_rdrsi5},
+ {"bitset.c", 0x0000002b, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"bittst.c", 0x0000002d, 0x3e0003ff, 0x5400, x_Rs_I5, s3_do_xrsi5},
+ {"bittgl", 0x0000002e, 0x3e0003ff, 0x5600, Rd_Rs_I5, s3_do_rdrsi5},
+ {"bittgl.c", 0x0000002f, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"bitclr!", 0x5000, 0x7e00, 0x8000, Rd_I5, s3_do16_rdi5},
+ {"bitset!", 0x5200, 0x7e00, 0x8000, Rd_I5, s3_do16_rdi5},
+ {"bittst!", 0x5400, 0x7e00, 0x8000, Rd_I5, s3_do16_rdi5},
+ {"bittgl!", 0x5600, 0x7e00, 0x8000, Rd_I5, s3_do16_rdi5},
+ {"bleu", 0x08000c00, 0x3e007c01, 0x3600, PC_DISP19div2, s3_do_branch},
+ {"ble", 0x08001c00, 0x3e007c01, 0x3e00, PC_DISP19div2, s3_do_branch},
+ {"blt", 0x08002400, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bleul", 0x08000c01, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"blel", 0x08001c01, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bltl", 0x08002401, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bl", 0x08003c01, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bleu!", 0x3600, 0x7e00, 0x08000c00, PC_DISP8div2, s3_do16_branch},
+ {"ble!", 0x3e00, 0x7e00, 0x08001c00, PC_DISP8div2, s3_do16_branch},
+ {"bmi", 0x08002800, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bmil", 0x08002801, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bne", 0x08001400, 0x3e007c01, 0x3a00, PC_DISP19div2, s3_do_branch},
+ {"bnel", 0x08001401, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bne!", 0x3a00, 0x7e00, 0x08001400, PC_DISP8div2, s3_do16_branch},
+ {"bpl", 0x08002c00, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bpll", 0x08002c01, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"brcs", 0x00000008, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brcc", 0x00000408, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brgtu", 0x00000808, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brleu", 0x00000c08, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"breq", 0x00001008, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brne", 0x00001408, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brgt", 0x00001808, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brle", 0x00001c08, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brge", 0x00002008, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brlt", 0x00002408, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brmi", 0x00002808, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brpl", 0x00002c08, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brvs", 0x00003008, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brvc", 0x00003408, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brcnz", 0x00003808, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"br", 0x00003c08, 0x3e007fff, 0x0080, x_Rs_x, s3_do_rs},
+ {"brcsl", 0x00000009, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brccl", 0x00000409, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brgtul", 0x00000809, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brleul", 0x00000c09, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"breql", 0x00001009, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brnel", 0x00001409, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brgtl", 0x00001809, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brlel", 0x00001c09, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brgel", 0x00002009, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brltl", 0x00002409, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brmil", 0x00002809, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brpll", 0x00002c09, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brvsl", 0x00003009, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brvcl", 0x00003409, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brcnzl", 0x00003809, 0x3e007fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"brl", 0x00003c09, 0x3e007fff, 0x00a0, x_Rs_x, s3_do_rs},
+ {"br!", 0x0080, 0x7fe0, 0x8000, x_Rs, s3_do16_br},
+ {"brl!", 0x00a0, 0x7fe0, 0x8000, x_Rs, s3_do16_br},
+ {"brr!", 0x00c0, 0x7fe0, 0x8000, x_Rs, s3_do16_brr},
+ {"bvs", 0x08003000, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bvc", 0x08003400, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bvsl", 0x08003001, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"bvcl", 0x08003401, 0x3e007c01, 0x8000, PC_DISP19div2, s3_do_branch},
+ {"b!", 0x3000, 0x7e00, 0x08003c00, PC_DISP8div2, s3_do16_branch},
+ {"b", 0x08003c00, 0x3e007c01, 0x3000, PC_DISP19div2, s3_do_branch},
+ {"cache", 0x30000000, 0x3ff00000, 0x8000, OP5_rvalueRs_SI15, s3_do_cache},
+ {"ceinst", 0x38000000, 0x3e000000, 0x8000, I5_Rs_Rs_I5_OP5, s3_do_ceinst},
+ {"clz", 0x0000001c, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"cmp.c", 0x00300019, 0x3ff003ff, 0x4400, x_Rs_Rs, s3_do_rsrs},
+ {"cmpz.c", 0x0030001b, 0x3ff07fff, 0x8000, x_Rs_x, s3_do_rs},
+ {"cmpi.c", 0x02040001, 0x3e0e0001, 0x6000, Rd_SI16, s3_do_rdsi16},
+
+ /* cmp.c <-> cmp!. */
+ {"cmp!", 0x4400, 0x7c00, 0x8000, Rd_Rs, s3_do16_mv_cmp},
+ {"cmpi!", 0x6000, 0x7c00, 0x8000, Rd_SI5, s3_do16_cmpi},
+ {"cop1", 0x0c00000c, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, s3_do_crdcrscrsimm5},
+ {"cop2", 0x0c000014, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, s3_do_crdcrscrsimm5},
+ {"cop3", 0x0c00001c, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, s3_do_crdcrscrsimm5},
+ {"drte", 0x0c0000a4, 0x3e0003ff, 0x8000, NO_OPD, s3_do_empty},
+ {"disint!", 0x00e0, 0xffe1, 0x8000, NO16_OPD, s3_do16_int},
+ {"enint!", 0x00e1, 0xffe1, 0x8000, NO16_OPD, s3_do16_int},
+ {"extsb", 0x00000058, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extsb.c", 0x00000059, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extsh", 0x0000005a, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extsh.c", 0x0000005b, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extzb", 0x0000005c, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extzb.c", 0x0000005d, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extzh", 0x0000005e, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"extzh.c", 0x0000005f, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"jl", 0x04000001, 0x3e000001, 0x8000, PC_DISP24div2, s3_do_jump},
+ {"j", 0x04000000, 0x3e000001, 0x8000, PC_DISP24div2, s3_do_jump},
+ {"alw", 0x0000000c, 0x3e0003ff, 0x8000, Rd_rvalue32Rs, s3_do_ldst_atomic},
+ {"lcb", 0x00000060, 0x3e0003ff, 0x8000, x_rvalueRs_post4, s3_do_ldst_unalign},
+ {"lcw", 0x00000062, 0x3e0003ff, 0x8000, Rd_rvalueRs_post4, s3_do_ldst_unalign},
+ {"lce", 0x00000066, 0x3e0003ff, 0x8000, Rd_rvalueRs_post4, s3_do_ldst_unalign},
+ {"ldc1", 0x0c00000a, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, s3_do_ldst_cop},
+ {"ldc2", 0x0c000012, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, s3_do_ldst_cop},
+ {"ldc3", 0x0c00001a, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, s3_do_ldst_cop},
+
+ /* s3_inst.relax */
+ {"ldi", 0x020c0000, 0x3e0e0000, 0x6400, Rd_SI16, s3_do_rdsi16},
+ {"ldis", 0x0a0c0000, 0x3e0e0000, 0x8000, Rd_I16, s3_do_ldis},
+
+ /* ldi <-> ldiu!. */
+ {"ldiu!", 0x6400, 0x7c00, 0x8000, Rd_I5, s3_do16_ldiu},
+
+ /*ltbb! , ltbh! ltbw! */
+ {"ltbw", 0x00000032, 0x03ff, 0x8000, Rd_Rs_Rs, s3_do_ltb},
+ {"ltbh", 0x00000132, 0x03ff, 0x8000, Rd_Rs_Rs, s3_do_ltb},
+ {"ltbb", 0x00000332, 0x03ff, 0x8000, Rd_Rs_Rs, s3_do_ltb},
+ {"lw!", 0x1000, 0x7000, 0x8000, Rd_rvalueRs, s3_do16_ldst_insn},
+ {"mfcel", 0x00000448, 0x3e007fff, 0x8000, Rd_x_x, s3_do_rd},
+ {"mfcel!", 0x7100, 0x7ff0, 0x00000448, x_Rs, s3_do16_dsp},
+ {"mad", 0x38000000, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mad.f!", 0x7400, 0x7f00, 0x38000080, Rd_Rs, s3_do16_dsp2},
+ {"madh", 0x38000203, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"madh.fs", 0x380002c3, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"madh.fs!", 0x7b00, 0x7f00, 0x380002c3, Rd_Rs, s3_do16_dsp2},
+ {"madl", 0x38000002, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"madl.fs", 0x380000c2, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"madl.fs!", 0x7a00, 0x7f00, 0x380000c2, Rd_Rs, s3_do16_dsp2},
+ {"madu", 0x38000020, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"madu!", 0x7500, 0x7f00, 0x38000020, Rd_Rs, s3_do16_dsp2},
+ {"mad.f", 0x38000080, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"max", 0x38000007, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_dsp2},
+ {"mazh", 0x38000303, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mazh.f", 0x38000383, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mazh.f!", 0x7900, 0x7f00, 0x3800038c, Rd_Rs, s3_do16_dsp2},
+ {"mazl", 0x38000102, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mazl.f", 0x38000182, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mazl.f!", 0x7800, 0x7f00, 0x38000182, Rd_Rs, s3_do16_dsp2},
+ {"mfceh", 0x00000848, 0x3e007fff, 0x8000, Rd_x_x, s3_do_rd},
+ {"mfceh!", 0x7110, 0x7ff0, 0x00000848, x_Rs, s3_do16_dsp},
+ {"mfcehl", 0x00000c48, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mfsr", 0x00000050, 0x3e0003ff, 0x8000, Rd_x_I5, s3_do_rdsrs},
+ {"mfcr", 0x0c000001, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mfc1", 0x0c000009, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mfc2", 0x0c000011, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mfc3", 0x0c000019, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mfcc1", 0x0c00000f, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mfcc2", 0x0c000017, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mfcc3", 0x0c00001f, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"min", 0x38000006, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_dsp2},
+ {"msb", 0x38000001, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"msb.f!", 0x7600, 0x7f00, 0x38000081, Rd_Rs, s3_do16_dsp2},
+ {"msbh", 0x38000205, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"msbh.fs", 0x380002c5, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"msbh.fs!", 0x7f00, 0x7f00, 0x380002c5, Rd_Rs, s3_do16_dsp2},
+ {"msbl", 0x38000004, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"msbl.fs", 0x380000c4, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"msbl.fs!", 0x7e00, 0x7f00, 0x380000c4, Rd_Rs, s3_do16_dsp2},
+ {"msbu", 0x38000021, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"msbu!", 0x7700, 0x7f00, 0x38000021, Rd_Rs, s3_do16_dsp2},
+ {"msb.f", 0x38000081, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mszh", 0x38000305, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mszh.f", 0x38000385, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mszh.f!", 0x7d00, 0x7f00, 0x38000385, Rd_Rs, s3_do16_dsp2},
+ {"mszl", 0x38000104, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mszl.f", 0x38000184, 0x3ff003ff, 0x8000, x_Rs_Rs, s3_do_dsp},
+ {"mszl.f!", 0x7c00, 0x7f00, 0x38000184, Rd_Rs, s3_do16_dsp2},
+ {"mtcel!", 0x7000, 0x7ff0, 0x0000044a, x_Rs, s3_do16_dsp},
+ {"mtcel", 0x0000044a, 0x3e007fff, 0x8000, Rd_x_x, s3_do_rd},
+ {"mtceh", 0x0000084a, 0x3e007fff, 0x8000, Rd_x_x, s3_do_rd},
+ {"mtceh!", 0x7010, 0x7ff0, 0x0000084a, x_Rs, s3_do16_dsp},
+ {"mtcehl", 0x00000c4a, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mtsr", 0x00000052, 0x3e0003ff, 0x8000, x_Rs_I5, s3_do_rdsrs},
+ {"mtcr", 0x0c000000, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mtc1", 0x0c000008, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mtc2", 0x0c000010, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mtc3", 0x0c000018, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mtcc1", 0x0c00000e, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mtcc2", 0x0c000016, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mtcc3", 0x0c00001e, 0x3e00001f, 0x8000, Rd_Rs_x, s3_do_rdcrs},
+ {"mul.f!", 0x7200, 0x7f00, 0x00000041, Rd_Rs, s3_do16_dsp2},
+ {"mulu!", 0x7300, 0x7f00, 0x00000042, Rd_Rs, s3_do16_dsp2},
+ {"mulr.l", 0x00000140, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulr.h", 0x00000240, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulr", 0x00000340, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulr.lf", 0x00000141, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulr.hf", 0x00000241, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulr.f", 0x00000341, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulur.l", 0x00000142, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulur.h", 0x00000242, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mulur", 0x00000342, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"divr.q", 0x00000144, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"divr.r", 0x00000244, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"divr", 0x00000344, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"divur.q", 0x00000146, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"divur.r", 0x00000246, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"divur", 0x00000346, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_rdrsrs},
+ {"mvcs", 0x00000056, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvcc", 0x00000456, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvgtu", 0x00000856, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvleu", 0x00000c56, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mveq", 0x00001056, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvne", 0x00001456, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvgt", 0x00001856, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvle", 0x00001c56, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvge", 0x00002056, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvlt", 0x00002456, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvmi", 0x00002856, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvpl", 0x00002c56, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvvs", 0x00003056, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"mvvc", 0x00003456, 0x3e007fff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+
+ /* mv <-> mv!. */
+ {"mv", 0x00003c56, 0x3e007fff, 0x4000, Rd_Rs_x, s3_do_rdrs},
+ {"mv!", 0x4000, 0x7c00, 0x8000, Rd_Rs, s3_do16_mv_cmp},
+ {"neg", 0x0000001e, 0x3e0003ff, 0x8000, Rd_x_Rs, s3_do_rdxrs},
+ {"neg.c", 0x0000001f, 0x3e0003ff, 0x8000, Rd_x_Rs, s3_do_rdxrs},
+ {"nop", 0x00000000, 0x3e0003ff, 0x0000, NO_OPD, s3_do_empty},
+ {"not", 0x00000024, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"not.c", 0x00000025, 0x3e0003ff, 0x8000, Rd_Rs_x, s3_do_rdrs},
+ {"nop!", 0x0000, 0x7fff, 0x8000, NO16_OPD, s3_do_empty},
+ {"or", 0x00000022, 0x3e0003ff, 0x4a00, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"or.c", 0x00000023, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"ori", 0x020a0000, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"ori.c", 0x020a0001, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"oris", 0x0a0a0000, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"oris.c", 0x0a0a0001, 0x3e0e0001, 0x8000, Rd_I16, s3_do_rdi16},
+ {"orri", 0x1a000000, 0x3e000001, 0x8000, Rd_Rs_I14, s3_do_rdrsi14},
+ {"orri.c", 0x1a000001, 0x3e000001, 0x8000, Rd_Rs_I14, s3_do_rdrsi14},
+
+ /* or.c <-> or!. */
+ {"or!", 0x4a00, 0x7f00, 0x8000, Rd_Rs, s3_do16_rdrs2},
+ {"pflush", 0x0000000a, 0x3e0003ff, 0x8000, NO_OPD, s3_do_empty},
+ {"pop!", 0x0040, 0x7fe0, 0x8000, Rd_rvalueRs, s3_do16_push_pop},
+ {"push!", 0x0060, 0x7fe0, 0x8000, Rd_lvalueRs, s3_do16_push_pop},
+
+ {"rpop!", 0x6800, 0x7c00, 0x8000, Rd_I5, s3_do16_rpop},
+ {"rpush!", 0x6c00, 0x7c00, 0x8000, Rd_I5, s3_do16_rpush},
+
+ {"ror", 0x00000038, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"ror.c", 0x00000039, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"rorc.c", 0x0000003b, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"rol", 0x0000003c, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"rol.c", 0x0000003d, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"rolc.c", 0x0000003f, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"rori", 0x00000078, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"rori.c", 0x00000079, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"roric.c", 0x0000007b, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"roli", 0x0000007c, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"roli.c", 0x0000007d, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"rolic.c", 0x0000007f, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"rte", 0x0c000084, 0x3e0003ff, 0x8000, NO_OPD, s3_do_empty},
+ {"asw", 0x0000000e, 0x3e0003ff, 0x8000, Rd_lvalue32Rs, s3_do_ldst_atomic},
+ {"scb", 0x00000068, 0x3e0003ff, 0x8000, Rd_lvalueRs_post4, s3_do_ldst_unalign},
+ {"scw", 0x0000006a, 0x3e0003ff, 0x8000, Rd_lvalueRs_post4, s3_do_ldst_unalign},
+ {"sce", 0x0000006e, 0x3e0003ff, 0x8000, x_lvalueRs_post4, s3_do_ldst_unalign},
+ {"sdbbp", 0x00000006, 0x3e0003ff, 0x0020, x_I5_x, s3_do_xi5x},
+ {"sdbbp!", 0x0020, 0x7fe0, 0x8000, Rd_I5, s3_do16_xi5},
+ {"sleep", 0x0c0000c4, 0x3e0003ff, 0x8000, NO_OPD, s3_do_empty},
+ {"rti", 0x0c0000e4, 0x3e0003ff, 0x8000, NO_OPD, s3_do_empty},
+ {"sll", 0x00000030, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"sll.c", 0x00000031, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"sll.s", 0x3800004e, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_dsp2},
+ {"slli", 0x00000070, 0x3e0003ff, 0x5800, Rd_Rs_I5, s3_do_rdrsi5},
+ {"slli.c", 0x00000071, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+
+ /* slli.c <-> slli!. */
+ {"slli!", 0x5800, 0x7e00, 0x8000, Rd_I5, s3_do16_slli_srli},
+ {"srl", 0x00000034, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"srl.c", 0x00000035, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"sra", 0x00000036, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"sra.c", 0x00000037, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"srli", 0x00000074, 0x3e0003ff, 0x5a00, Rd_Rs_I5, s3_do_rdrsi5},
+ {"srli.c", 0x00000075, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"srai", 0x00000076, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+ {"srai.c", 0x00000077, 0x3e0003ff, 0x8000, Rd_Rs_I5, s3_do_rdrsi5},
+
+ /* srli.c <-> srli!. */
+ {"srli!", 0x5a00, 0x7e00, 0x8000, Rd_Rs, s3_do16_slli_srli},
+ {"stc1", 0x0c00000b, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, s3_do_ldst_cop},
+ {"stc2", 0x0c000013, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, s3_do_ldst_cop},
+ {"stc3", 0x0c00001b, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, s3_do_ldst_cop},
+ {"sub", 0x00000014, 0x3e0003ff, 0x4900, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"sub.c", 0x00000015, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"sub.s", 0x38000049, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_dsp2},
+ {"subc", 0x00000016, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"subc.c", 0x00000017, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+
+ /* sub.c <-> sub!. */
+ {"sub!", 0x4900, 0x7f00, 0x8000, Rd_Rs, s3_do16_rdrs2},
+ {"sw!", 0x2000, 0x7000, 0x8000, Rd_lvalueRs, s3_do16_ldst_insn},
+ {"syscall", 0x00000002, 0x3e0003ff, 0x8000, I15, s3_do_i15},
+ {"trapcs", 0x00000004, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapcc", 0x00000404, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapgtu", 0x00000804, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapleu", 0x00000c04, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapeq", 0x00001004, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapne", 0x00001404, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapgt", 0x00001804, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"traple", 0x00001c04, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapge", 0x00002004, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"traplt", 0x00002404, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapmi", 0x00002804, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trappl", 0x00002c04, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapvs", 0x00003004, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trapvc", 0x00003404, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"trap", 0x00003c04, 0x3e007fff, 0x8000, x_I5_x, s3_do_xi5x},
+ {"xor", 0x00000026, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+ {"xor.c", 0x00000027, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s3_do_rdrsrs},
+
+ /* Macro instruction. */
+ {"li", 0x020c0000, 0x3e0e0000, 0x8000, Insn_Type_SYN, s3_do_macro_li_rdi32},
+
+ /* la reg, imm32 -->(1) ldi reg, simm16
+ (2) ldis reg, %HI(imm32)
+ ori reg, %LO(imm32)
+
+ la reg, symbol -->(1) lis reg, %HI(imm32)
+ ori reg, %LO(imm32) */
+ {"la", 0x020c0000, 0x3e0e0000, 0x8000, Insn_Type_SYN, s3_do_macro_la_rdi32},
+ {"bcmpeqz", 0x0000004c, 0x3e00007e, 0x8000, Insn_BCMP, s3_do_macro_bcmpz},
+ {"bcmpeq", 0x0000004c, 0x3e00007e, 0x8000, Insn_BCMP, s3_do_macro_bcmp},
+ {"bcmpnez", 0x0000004e, 0x3e00007e, 0x8000, Insn_BCMP, s3_do_macro_bcmpz},
+ {"bcmpne", 0x0000004e, 0x3e00007e, 0x8000, Insn_BCMP, s3_do_macro_bcmp},
+ {"div", 0x00000044, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"divu", 0x00000046, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"rem", 0x00000044, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"remu", 0x00000046, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"mul", 0x00000040, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"mulu", 0x00000042, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"maz", 0x00000040, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"mazu", 0x00000042, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"mul.f", 0x00000041, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"maz.f", 0x00000041, 0x3e0003ff, 0x8000, Insn_Type_SYN, s3_do_macro_mul_rdrsrs},
+ {"lb", INSN_LB, 0x00000000, 0x8000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"lbu", INSN_LBU, 0x00000000, 0x8000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"lh", INSN_LH, 0x00000000, 0x8000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"lhu", INSN_LHU, 0x00000000, 0x8000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"lw", INSN_LW, 0x00000000, 0x1000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"sb", INSN_SB, 0x00000000, 0x8000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"sh", INSN_SH, 0x00000000, 0x8000, Insn_Type_SYN, s3_do_macro_ldst_label},
+ {"sw", INSN_SW, 0x00000000, 0x2000, Insn_Type_SYN, s3_do_macro_ldst_label},
+
+ /* Assembler use internal. */
+ {"ld_i32hi", 0x0a0c0000, 0x3e0e0000, 0x8000, Insn_internal, s3_do_macro_rdi32hi},
+ {"ld_i32lo", 0x020a0000, 0x3e0e0001, 0x8000, Insn_internal, s3_do_macro_rdi32lo},
+ {"ldis_pic", 0x0a0c0000, 0x3e0e0000, 0x8000, Insn_internal, s3_do_rdi16_pic},
+ {"addi_s_pic",0x02000000, 0x3e0e0001, 0x8000, Insn_internal, s3_do_addi_s_pic},
+ {"addi_u_pic",0x02000000, 0x3e0e0001, 0x8000, Insn_internal, s3_do_addi_u_pic},
+ {"lw_pic", 0x20000000, 0x3e000000, 0x8000, Insn_internal, s3_do_lw_pic},
+
+ /* 48-bit instructions. */
+ {"sdbbp48", 0x000000000000LL, 0x1c000000001fLL, 0x8000, Rd_I32, s3_do_sdbbp48},
+ {"ldi48", 0x000000000001LL, 0x1c000000001fLL, 0x8000, Rd_I32, s3_do_ldi48},
+ {"lw48", 0x000000000002LL, 0x1c000000001fLL, 0x8000, Rd_I30, s3_do_lw48},
+ {"sw48", 0x000000000003LL, 0x1c000000001fLL, 0x8000, Rd_I30, s3_do_sw48},
+ {"andri48", 0x040000000000LL, 0x1c0000000003LL, 0x8000, Rd_I32, s3_do_and48},
+ {"andri48.c", 0x040000000001LL, 0x1c0000000003LL, 0x8000, Rd_I32, s3_do_and48},
+ {"orri48", 0x040000000002LL, 0x1c0000000003LL, 0x8000, Rd_I32, s3_do_or48},
+ {"orri48.c", 0x040000000003LL, 0x1c0000000003LL, 0x8000, Rd_I32, s3_do_or48},
+};
+
+#define s3_SCORE3_PIPELINE 3
+
+static int s3_university_version = 0;
+static int s3_vector_size = s3_SCORE3_PIPELINE;
+static struct s3_score_it s3_dependency_vector[s3_SCORE3_PIPELINE];
+
+static int s3_score3d = 1;
+
+static int
+s3_end_of_line (char *str)
+{
+ int retval = s3_SUCCESS;
+
+ s3_skip_whitespace (str);
+ if (*str != '\0')
+ {
+ retval = (int) s3_FAIL;
+
+ if (!s3_inst.error)
+ s3_inst.error = s3_BAD_GARBAGE;
+ }
+
+ return retval;
+}
+
+static int
+s3_score_reg_parse (char **ccp, struct hash_control *htab)
+{
+ char *start = *ccp;
+ char c;
+ char *p;
+ struct s3_reg_entry *reg;
+
+ p = start;
+ if (!ISALPHA (*p) || !is_name_beginner (*p))
+ return (int) s3_FAIL;
+
+ c = *p++;
+
+ while (ISALPHA (c) || ISDIGIT (c) || c == '_')
+ c = *p++;
+
+ *--p = 0;
+ reg = (struct s3_reg_entry *) hash_find (htab, start);
+ *p = c;
+
+ if (reg)
+ {
+ *ccp = p;
+ return reg->number;
+ }
+ return (int) s3_FAIL;
+}
+
+/* If shift <= 0, only return reg. */
+
+static int
+s3_reg_required_here (char **str, int shift, enum s3_score_reg_type reg_type)
+{
+ static char buff[s3_MAX_LITERAL_POOL_SIZE];
+ int reg = (int) s3_FAIL;
+ char *start = *str;
+
+ if ((reg = s3_score_reg_parse (str, s3_all_reg_maps[reg_type].htab)) != (int) s3_FAIL)
+ {
+ if (reg_type == s3_REG_TYPE_SCORE)
+ {
+ if ((reg == 1) && (s3_nor1 == 1) && (s3_inst.bwarn == 0))
+ {
+ as_warn (_("Using temp register(r1)"));
+ s3_inst.bwarn = 1;
+ }
+ }
+ if (shift >= 0)
+ {
+ if (reg_type == s3_REG_TYPE_SCORE_CR)
+ strcpy (s3_inst.reg, s3_score_crn_table[reg].name);
+ else if (reg_type == s3_REG_TYPE_SCORE_SR)
+ strcpy (s3_inst.reg, s3_score_srn_table[reg].name);
+ else
+ strcpy (s3_inst.reg, "");
+
+ s3_inst.instruction |= (bfd_vma) reg << shift;
+ }
+ }
+ else
+ {
+ *str = start;
+ sprintf (buff, _("register expected, not '%.100s'"), start);
+ s3_inst.error = buff;
+ }
+
+ return reg;
+}
+
+static int
+s3_skip_past_comma (char **str)
+{
+ char *p = *str;
+ char c;
+ int comma = 0;
+
+ while ((c = *p) == ' ' || c == ',')
+ {
+ p++;
+ if (c == ',' && comma++)
+ {
+ s3_inst.error = s3_BAD_SKIP_COMMA;
+ return (int) s3_FAIL;
+ }
+ }
+
+ if ((c == '\0') || (comma == 0))
+ {
+ s3_inst.error = s3_BAD_SKIP_COMMA;
+ return (int) s3_FAIL;
+ }
+
+ *str = p;
+ return comma ? s3_SUCCESS : (int) s3_FAIL;
+}
+
+static void
+s3_do_rdrsrs (char *str)
+{
+ int reg;
+ s3_skip_whitespace (str);
+
+ if ((reg = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ /* Check mulr, mulur rd is even number. */
+ if (((s3_inst.instruction & 0x3e0003ff) == 0x00000340
+ || (s3_inst.instruction & 0x3e0003ff) == 0x00000342)
+ && (reg % 2))
+ {
+ s3_inst.error = _("rd must be even number.");
+ return;
+ }
+
+ if ((((s3_inst.instruction >> 15) & 0x10) == 0)
+ && (((s3_inst.instruction >> 10) & 0x10) == 0)
+ && (((s3_inst.instruction >> 20) & 0x10) == 0)
+ && (s3_inst.relax_inst != 0x8000)
+ && (((s3_inst.instruction >> 20) & 0xf) == ((s3_inst.instruction >> 15) & 0xf)))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0xf) )
+ | (((s3_inst.instruction >> 15) & 0xf) << 4);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+}
+
+static int
+s3_walk_no_bignums (symbolS * sp)
+{
+ if (symbol_get_value_expression (sp)->X_op == O_big)
+ return 1;
+
+ if (symbol_get_value_expression (sp)->X_add_symbol)
+ return (s3_walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
+ || (symbol_get_value_expression (sp)->X_op_symbol
+ && s3_walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
+
+ return 0;
+}
+
+static int
+s3_my_get_expression (expressionS * ep, char **str)
+{
+ char *save_in;
+
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ s3_in_my_get_expression = 1;
+ (void) expression (ep);
+ s3_in_my_get_expression = 0;
+
+ if (ep->X_op == O_illegal)
+ {
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ s3_inst.error = _("illegal expression");
+ return (int) s3_FAIL;
+ }
+ /* Get rid of any bignums now, so that we don't generate an error for which
+ we can't establish a line number later on. Big numbers are never valid
+ in instructions, which is where this routine is always called. */
+ if (ep->X_op == O_big
+ || (ep->X_add_symbol
+ && (s3_walk_no_bignums (ep->X_add_symbol)
+ || (ep->X_op_symbol && s3_walk_no_bignums (ep->X_op_symbol)))))
+ {
+ s3_inst.error = _("invalid constant");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return (int) s3_FAIL;
+ }
+
+ if ((ep->X_add_symbol != NULL)
+ && (s3_inst.type != PC_DISP19div2)
+ && (s3_inst.type != PC_DISP8div2)
+ && (s3_inst.type != PC_DISP24div2)
+ && (s3_inst.type != PC_DISP11div2)
+ && (s3_inst.type != Insn_Type_SYN)
+ && (s3_inst.type != Rd_rvalueRs_SI15)
+ && (s3_inst.type != Rd_lvalueRs_SI15)
+ && (s3_inst.type != Insn_internal)
+ && (s3_inst.type != Rd_I30)
+ && (s3_inst.type != Rd_I32)
+ && (s3_inst.type != Insn_BCMP))
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return (int) s3_FAIL;
+ }
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return s3_SUCCESS;
+}
+
+/* Check if an immediate is valid. If so, convert it to the right format. */
+static bfd_signed_vma
+s3_validate_immediate (bfd_signed_vma val, unsigned int data_type, int hex_p)
+{
+ switch (data_type)
+ {
+ case _VALUE_HI16:
+ {
+ bfd_signed_vma val_hi = ((val & 0xffff0000) >> 16);
+
+ if (s3_score_df_range[data_type].range[0] <= val_hi
+ && val_hi <= s3_score_df_range[data_type].range[1])
+ return val_hi;
+ }
+ break;
+
+ case _VALUE_LO16:
+ {
+ bfd_signed_vma val_lo = (val & 0xffff);
+
+ if (s3_score_df_range[data_type].range[0] <= val_lo
+ && val_lo <= s3_score_df_range[data_type].range[1])
+ return val_lo;
+ }
+ break;
+
+ case _SIMM14:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x2000 && val <= 0x3fff))
+ {
+ return (int) s3_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -8192 && val <= 8191))
+ {
+ return (int) s3_FAIL;
+ }
+ }
+
+ return val;
+ break;
+
+ case _SIMM16_NEG:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x7fff && val <= 0xffff && val != 0x8000))
+ {
+ return (int) s3_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -32767 && val <= 32768))
+ {
+ return (int) s3_FAIL;
+ }
+ }
+
+ val = -val;
+ return val;
+ break;
+
+ case _IMM5_MULTI_LOAD:
+ if (val >= 2 && val <= 32)
+ {
+ if (val == 32)
+ val = 0;
+ return val;
+ }
+ return (int) s3_FAIL;
+
+ case _IMM32:
+ if (val >= 0 && val <= 0xffffffff)
+ {
+ return val;
+ }
+ else
+ {
+ return (int) s3_FAIL;
+ }
+
+ default:
+ if (data_type == _SIMM14_NEG || data_type == _IMM16_NEG)
+ val = -val;
+
+ if (s3_score_df_range[data_type].range[0] <= val
+ && val <= s3_score_df_range[data_type].range[1])
+ return val;
+
+ break;
+ }
+
+ return (int) s3_FAIL;
+}
+
+static int
+s3_data_op2 (char **str, int shift, enum score_data_type data_type)
+{
+ bfd_signed_vma value;
+ char data_exp[s3_MAX_LITERAL_POOL_SIZE];
+ char *dataptr;
+ int cnt = 0;
+ char *pp = NULL;
+
+ s3_skip_whitespace (*str);
+ s3_inst.error = NULL;
+ dataptr = * str;
+
+ /* Set hex_p to zero. */
+ int hex_p = 0;
+
+ while ((*dataptr != '\0') && (*dataptr != '|') && (cnt <= s3_MAX_LITERAL_POOL_SIZE)) /* 0x7c = ='|' */
+ {
+ data_exp[cnt] = *dataptr;
+ dataptr++;
+ cnt++;
+ }
+
+ data_exp[cnt] = '\0';
+ pp = (char *)&data_exp;
+
+ if (*dataptr == '|') /* process PCE */
+ {
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &pp) == (int) s3_FAIL)
+ return (int) s3_FAIL;
+ s3_end_of_line (pp);
+ if (s3_inst.error != 0)
+ return (int) s3_FAIL; /* to ouptut_inst to printf out the error */
+ *str = dataptr;
+ }
+ else /* process 16 bit */
+ {
+ if (s3_my_get_expression (&s3_inst.reloc.exp, str) == (int) s3_FAIL)
+ {
+ return (int) s3_FAIL;
+ }
+
+ dataptr = (char *)data_exp;
+ for (; *dataptr != '\0'; dataptr++)
+ {
+ *dataptr = TOLOWER (*dataptr);
+ if (*dataptr == '!' || *dataptr == ' ')
+ break;
+ }
+ dataptr = (char *)data_exp;
+
+ if ((dataptr != NULL)
+ && (((strstr (dataptr, "0x")) != NULL)
+ || ((strstr (dataptr, "0X")) != NULL)))
+ {
+ hex_p = 1;
+ if ((data_type != _SIMM16_LA)
+ && (data_type != _VALUE_HI16)
+ && (data_type != _VALUE_LO16)
+ && (data_type != _IMM16)
+ && (data_type != _IMM15)
+ && (data_type != _IMM14)
+ && (data_type != _IMM4)
+ && (data_type != _IMM5)
+ && (data_type != _IMM5_MULTI_LOAD)
+ && (data_type != _IMM11)
+ && (data_type != _IMM8)
+ && (data_type != _IMM5_RSHIFT_1)
+ && (data_type != _IMM5_RSHIFT_2)
+ && (data_type != _SIMM14)
+ && (data_type != _SIMM14_NEG)
+ && (data_type != _SIMM16_NEG)
+ && (data_type != _IMM10_RSHIFT_2)
+ && (data_type != _GP_IMM15)
+ && (data_type != _SIMM5)
+ && (data_type != _SIMM6)
+ && (data_type != _IMM32)
+ && (data_type != _SIMM32))
+ {
+ data_type += 24;
+ }
+ }
+
+ if ((s3_inst.reloc.exp.X_add_number == 0)
+ && (s3_inst.type != Insn_Type_SYN)
+ && (s3_inst.type != Rd_rvalueRs_SI15)
+ && (s3_inst.type != Rd_lvalueRs_SI15)
+ && (s3_inst.type != Insn_internal)
+ && (((*dataptr >= 'a') && (*dataptr <= 'z'))
+ || ((*dataptr == '0') && (*(dataptr + 1) == 'x') && (*(dataptr + 2) != '0'))
+ || ((*dataptr == '+') && (*(dataptr + 1) != '0'))
+ || ((*dataptr == '-') && (*(dataptr + 1) != '0'))))
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ return (int) s3_FAIL;
+ }
+ }
+
+ if ((s3_inst.reloc.exp.X_add_symbol)
+ && ((data_type == _SIMM16)
+ || (data_type == _SIMM16_NEG)
+ || (data_type == _IMM16_NEG)
+ || (data_type == _SIMM14)
+ || (data_type == _SIMM14_NEG)
+ || (data_type == _IMM5)
+ || (data_type == _IMM5_MULTI_LOAD)
+ || (data_type == _IMM11)
+ || (data_type == _IMM14)
+ || (data_type == _IMM20)
+ || (data_type == _IMM16)
+ || (data_type == _IMM15)
+ || (data_type == _IMM4)))
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ return (int) s3_FAIL;
+ }
+
+ if (s3_inst.reloc.exp.X_add_symbol)
+ {
+ switch (data_type)
+ {
+ case _SIMM16_LA:
+ return (int) s3_FAIL;
+ case _VALUE_HI16:
+ s3_inst.reloc.type = BFD_RELOC_HI16_S;
+ s3_inst.reloc.pc_rel = 0;
+ break;
+ case _VALUE_LO16:
+ s3_inst.reloc.type = BFD_RELOC_LO16;
+ s3_inst.reloc.pc_rel = 0;
+ break;
+ case _GP_IMM15:
+ s3_inst.reloc.type = BFD_RELOC_SCORE_GPREL15;
+ s3_inst.reloc.pc_rel = 0;
+ break;
+ case _SIMM16_pic:
+ case _IMM16_LO16_pic:
+ s3_inst.reloc.type = BFD_RELOC_SCORE_GOT_LO16;
+ s3_inst.reloc.pc_rel = 0;
+ break;
+ default:
+ s3_inst.reloc.type = BFD_RELOC_32;
+ s3_inst.reloc.pc_rel = 0;
+ break;
+ }
+ }
+ else
+ {
+ if (data_type == _IMM16_pic)
+ {
+ s3_inst.reloc.type = BFD_RELOC_SCORE_DUMMY_HI16;
+ s3_inst.reloc.pc_rel = 0;
+ }
+
+ if (data_type == _SIMM16_LA && s3_inst.reloc.exp.X_unsigned == 1)
+ {
+ value = s3_validate_immediate (s3_inst.reloc.exp.X_add_number, _SIMM16_LA_POS, hex_p);
+ if (value == (int) s3_FAIL) /* for advance to check if this is ldis */
+ if ((s3_inst.reloc.exp.X_add_number & 0xffff) == 0)
+ {
+ s3_inst.instruction |= 0x8000000;
+ s3_inst.instruction |= ((s3_inst.reloc.exp.X_add_number >> 16) << 1) & 0x1fffe;
+ return s3_SUCCESS;
+ }
+ }
+ else
+ {
+ value = s3_validate_immediate (s3_inst.reloc.exp.X_add_number, data_type, hex_p);
+ }
+
+ if (value == (int) s3_FAIL)
+ {
+ if (data_type == _IMM32)
+ {
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %u..%u"),
+ s3_score_df_range[data_type].bits,
+ 0, (unsigned)0xffffffff);
+ }
+ else if (data_type == _IMM5_MULTI_LOAD)
+ {
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %u..%u"),
+ 5, 2, 32);
+ }
+ else if ((data_type != _SIMM14_NEG) && (data_type != _SIMM16_NEG) && (data_type != _IMM16_NEG))
+ {
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type].bits,
+ s3_score_df_range[data_type].range[0], s3_score_df_range[data_type].range[1]);
+ }
+ else
+ {
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type].bits,
+ -s3_score_df_range[data_type].range[1], -s3_score_df_range[data_type].range[0]);
+ }
+
+ s3_inst.error = s3_err_msg;
+ return (int) s3_FAIL;
+ }
+
+ if (((s3_score_df_range[data_type].range[0] != 0) || (data_type == _IMM5_RANGE_8_31))
+ && data_type != _IMM5_MULTI_LOAD)
+ {
+ value &= (1 << s3_score_df_range[data_type].bits) - 1;
+ }
+
+ s3_inst.instruction |= value << shift;
+ }
+
+ if ((s3_inst.instruction & 0x3e000000) == 0x30000000)
+ {
+ if ((((s3_inst.instruction >> 20) & 0x1F) != 0)
+ && (((s3_inst.instruction >> 20) & 0x1F) != 1)
+ && (((s3_inst.instruction >> 20) & 0x1F) != 2)
+ && (((s3_inst.instruction >> 20) & 0x1F) != 0x10))
+ {
+ s3_inst.error = _("invalid constant: bit expression not defined");
+ return (int) s3_FAIL;
+ }
+ }
+
+ return s3_SUCCESS;
+}
+
+/* Handle addi/addi.c/addis.c/cmpi.c/addis.c/ldi. */
+static void
+s3_do_rdsi16 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 1, _SIMM16) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ /* ldi.->ldiu! only for imm5 */
+ if ((s3_inst.instruction & 0x20c0000) == 0x20c0000)
+ {
+ if ((s3_inst.instruction & 0x1ffc0) != 0)
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ else
+ {
+ s3_inst.relax_inst |= (s3_inst.instruction >> 1) & 0x1f;
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 20)& 0x1f) <<5);
+ s3_inst.relax_size = 2;
+ }
+ }
+ /*cmpi.c */
+ else if ((s3_inst.instruction & 0x02040001) == 0x02040001)
+ {
+ /* imm <=0x3f (5 bit<<1)*/
+ if (((s3_inst.instruction & 0x1ffe0) == 0)
+ || (((s3_inst.instruction & 0x1ffe0) == 0x1ffe0)
+ && (s3_inst.instruction & 0x003e) != 0))
+ {
+ s3_inst.relax_inst |= (s3_inst.instruction >> 1) & 0x1f;
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 20) & 0x1f) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst =0x8000;
+
+ }
+ }
+ /* addi */
+ else if (((s3_inst.instruction & 0x2000000) == 0x02000000) && (s3_inst.relax_inst!=0x8000))
+ {
+ /* rd : 0-16 ; imm <=0x7f (6 bit<<1)*/
+ if ((((s3_inst.instruction >> 20) & 0x10) != 0x10)
+ && (((s3_inst.instruction & 0x1ffc0) == 0)
+ || (((s3_inst.instruction & 0x1ffc0) == 0x1ffc0)
+ && (s3_inst.instruction & 0x007e) != 0)))
+ {
+ s3_inst.relax_inst |= (s3_inst.instruction >> 1) & 0x3f;
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 20) & 0xf) << 6);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst =0x8000;
+ }
+ }
+
+ else if (((s3_inst.instruction >> 20) & 0x10) == 0x10)
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+}
+
+static void
+s3_do_ldis (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 1, _IMM16) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+/* Handle subi/subi.c. */
+static void
+s3_do_sub_rdsi16 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _SIMM16_NEG) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle subis/subis.c. */
+static void
+s3_do_sub_rdi16 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _IMM16_NEG) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle addri/addri.c. */
+static void
+s3_do_rdrssi14 (char *str) /* -(2^13)~((2^13)-1) */
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL)
+ s3_data_op2 (&str, 1, _SIMM14);
+}
+
+/* Handle subri.c/subri. */
+static void
+s3_do_sub_rdrssi14 (char *str) /* -(2^13)~((2^13)-1) */
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _SIMM14_NEG) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle bitclr.c/bitset.c/bittgl.c/slli.c/srai.c/srli.c/roli.c/rori.c/rolic.c.
+ 0~((2^14)-1) */
+static void
+s3_do_rdrsi5 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 10, _IMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((((s3_inst.instruction >> 20) & 0x1f) == ((s3_inst.instruction >> 15) & 0x1f))
+ && (s3_inst.relax_inst != 0x8000) && (((s3_inst.instruction >> 15) & 0x10) == 0))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0x1f) ) | (((s3_inst.instruction >> 15) & 0xf) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+/* Handle andri/orri/andri.c/orri.c.
+ 0 ~ ((2^14)-1) */
+static void
+s3_do_rdrsi14 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _IMM14) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle bittst.c. */
+static void
+s3_do_xrsi5 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 10, _IMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((s3_inst.relax_inst != 0x8000) && (((s3_inst.instruction >> 15) & 0x10) == 0))
+ {
+ s3_inst.relax_inst |= ((s3_inst.instruction >> 10) & 0x1f) | (((s3_inst.instruction >> 15) & 0xf) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+/* Handle addis/andi/ori/andis/oris/ldis. */
+static void
+s3_do_rdi16 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 1, _IMM16) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ /* ldis */
+ if ((s3_inst.instruction & 0x3e0e0000) == 0x0a0c0000)
+ {
+ /* rd : 0-16 ;imm =0 -> can transform to addi!*/
+ if ((((s3_inst.instruction >> 20) & 0x10) != 0x10) && ((s3_inst.instruction & 0x1ffff)==0))
+ {
+ s3_inst.relax_inst =0x5400; /* ldiu! */
+ s3_inst.relax_inst |= (s3_inst.instruction >> 1) & 0x1f;
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 20) & 0xf) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst =0x8000;
+
+ }
+ }
+
+ /* addis */
+ else if ((s3_inst.instruction & 0x3e0e0001) == 0x0a000000)
+ {
+ /* rd : 0-16 ;imm =0 -> can transform to addi!*/
+ if ((((s3_inst.instruction >> 20) & 0x10) != 0x10) && ((s3_inst.instruction & 0x1ffff)==0))
+ {
+ s3_inst.relax_inst =0x5c00; /* addi! */
+ s3_inst.relax_inst |= (s3_inst.instruction >> 1) & 0x3f;
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 20) & 0xf) << 6);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst =0x8000;
+
+ }
+ }
+}
+
+static void
+s3_do_macro_rdi32hi (char *str)
+{
+ s3_skip_whitespace (str);
+
+ /* Do not handle s3_end_of_line(). */
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL)
+ s3_data_op2 (&str, 1, _VALUE_HI16);
+}
+
+static void
+s3_do_macro_rdi32lo (char *str)
+{
+ s3_skip_whitespace (str);
+
+ /* Do not handle s3_end_of_line(). */
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL)
+ s3_data_op2 (&str, 1, _VALUE_LO16);
+}
+
+/* Handle ldis_pic. */
+static void
+s3_do_rdi16_pic (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _IMM16_pic) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle addi_s_pic to generate R_SCORE_GOT_LO16 . */
+static void
+s3_do_addi_s_pic (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _SIMM16_pic) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle addi_u_pic to generate R_SCORE_GOT_LO16 . */
+static void
+s3_do_addi_u_pic (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_data_op2 (&str, 1, _IMM16_LO16_pic) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle mfceh/mfcel/mtceh/mtchl. */
+static void
+s3_do_rd (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle br{cond},cmpzteq.c ,cmpztmi.c ,cmpz.c */
+static void
+s3_do_rs (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((s3_inst.relax_inst != 0x8000) )
+ {
+ s3_inst.relax_inst |= ((s3_inst.instruction >> 15) &0x1f);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+static void
+s3_do_i15 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_data_op2 (&str, 10, _IMM15) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+static void
+s3_do_xi5x (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_data_op2 (&str, 15, _IMM5) == (int) s3_FAIL || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if (s3_inst.relax_inst != 0x8000)
+ {
+ s3_inst.relax_inst |= ((s3_inst.instruction >> 15) & 0x1f);
+ s3_inst.relax_size = 2;
+ }
+}
+
+static void
+s3_do_rdrs (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if (s3_inst.relax_inst != 0x8000)
+ {
+ if (((s3_inst.instruction & 0x7f) == 0x56)) /* adjust mv -> mv!*/
+ {
+ /* mv! rd : 5bit , ra : 5bit */
+ s3_inst.relax_inst |= ((s3_inst.instruction >> 15) & 0x1f) | (((s3_inst.instruction >> 20) & 0x1f) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else if ((((s3_inst.instruction >> 15) & 0x10) == 0x0) && (((s3_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 15) & 0xf) << 4)
+ | (((s3_inst.instruction >> 20) & 0xf) << 8);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+}
+
+/* Handle mfcr/mtcr. */
+static void
+s3_do_rdcrs (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE_CR) != (int) s3_FAIL)
+ s3_end_of_line (str);
+}
+
+/* Handle mfsr/mtsr. */
+static void
+s3_do_rdsrs (char *str)
+{
+ s3_skip_whitespace (str);
+
+ /* mfsr */
+ if ((s3_inst.instruction & 0xff) == 0x50)
+ {
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL
+ && s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE_SR) != (int) s3_FAIL)
+ s3_end_of_line (str);
+ }
+ else
+ {
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) != (int) s3_FAIL
+ && s3_skip_past_comma (&str) != (int) s3_FAIL)
+ s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE_SR);
+ }
+}
+
+/* Handle neg. */
+static void
+s3_do_rdxrs (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((s3_inst.relax_inst != 0x8000) && (((s3_inst.instruction >> 10) & 0x10) == 0)
+ && (((s3_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0xf) << 4) | (((s3_inst.instruction >> 20) & 0xf) << 8);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+/* Handle cmp.c/cmp<cond>. */
+static void
+s3_do_rsrs (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((s3_inst.relax_inst != 0x8000) && (((s3_inst.instruction >> 20) & 0x1f) == 3) )
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0x1f)) | (((s3_inst.instruction >> 15) & 0x1f) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+static void
+s3_do_ceinst (char *str)
+{
+ char *strbak;
+
+ strbak = str;
+ s3_skip_whitespace (str);
+
+ if (s3_data_op2 (&str, 20, _IMM5) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 5, _IMM5) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 0, _IMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ str = strbak;
+ if (s3_data_op2 (&str, 0, _IMM25) == (int) s3_FAIL)
+ return;
+ }
+}
+
+static int
+s3_reglow_required_here (char **str, int shift)
+{
+ static char buff[s3_MAX_LITERAL_POOL_SIZE];
+ int reg;
+ char *start = *str;
+
+ if ((reg = s3_score_reg_parse (str, s3_all_reg_maps[s3_REG_TYPE_SCORE].htab)) != (int) s3_FAIL)
+ {
+ if ((reg == 1) && (s3_nor1 == 1) && (s3_inst.bwarn == 0))
+ {
+ as_warn (_("Using temp register(r1)"));
+ s3_inst.bwarn = 1;
+ }
+ if (reg < 16)
+ {
+ if (shift >= 0)
+ s3_inst.instruction |= (bfd_vma) reg << shift;
+
+ return reg;
+ }
+ }
+
+ /* Restore the start point, we may have got a reg of the wrong class. */
+ *str = start;
+ sprintf (buff, _("low register(r0-r15)expected, not '%.100s'"), start);
+ s3_inst.error = buff;
+ return (int) s3_FAIL;
+}
+
+/* Handle add!/and!/or!/sub!. */
+static void
+s3_do16_rdrs2 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reglow_required_here (&str, 4) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reglow_required_here (&str, 0) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+}
+
+/* Handle br!/brl!. */
+static void
+s3_do16_br (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 0, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+}
+
+/* Handle brr!. */
+static void
+s3_do16_brr (char *str)
+{
+ int rd = 0;
+
+ s3_skip_whitespace (str);
+
+ if ((rd = s3_reg_required_here (&str, 0,s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+}
+
+/*Handle ltbw / ltbh / ltbb */
+static void
+s3_do_ltb (char *str)
+{
+ s3_skip_whitespace (str);
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ s3_skip_whitespace (str);
+ if (*str++ != '[')
+ {
+ s3_inst.error = _("missing [");
+ return;
+ }
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ s3_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+}
+
+/* We need to be able to fix up arbitrary expressions in some statements.
+ This is so that we can handle symbols that are an arbitrary distance from
+ the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+ which returns part of an address in a form which will be valid for
+ a data instruction. We do this by pushing the expression into a symbol
+ in the expr_section, and creating a fix for that. */
+static fixS *
+s3_fix_new_score (fragS * frag, int where, short int size, expressionS * exp, int pc_rel, int reloc)
+{
+ fixS *new_fix;
+
+ switch (exp->X_op)
+ {
+ case O_constant:
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+ break;
+ default:
+ new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0, pc_rel, reloc);
+ break;
+ }
+ return new_fix;
+}
+
+static void
+s3_init_dependency_vector (void)
+{
+ int i;
+
+ for (i = 0; i < s3_vector_size; i++)
+ memset (&s3_dependency_vector[i], '\0', sizeof (s3_dependency_vector[i]));
+
+ return;
+}
+
+static enum s3_insn_type_for_dependency
+s3_dependency_type_from_insn (char *insn_name)
+{
+ char name[s3_INSN_NAME_LEN];
+ const struct s3_insn_to_dependency *tmp;
+
+ strcpy (name, insn_name);
+ tmp = (const struct s3_insn_to_dependency *) hash_find (s3_dependency_insn_hsh, name);
+
+ if (tmp)
+ return tmp->type;
+
+ return s3_D_all_insn;
+}
+
+static int
+s3_check_dependency (char *pre_insn, char *pre_reg,
+ char *cur_insn, char *cur_reg, int *warn_or_error)
+{
+ int bubbles = 0;
+ unsigned int i;
+ enum s3_insn_type_for_dependency pre_insn_type;
+ enum s3_insn_type_for_dependency cur_insn_type;
+
+ pre_insn_type = s3_dependency_type_from_insn (pre_insn);
+ cur_insn_type = s3_dependency_type_from_insn (cur_insn);
+
+ for (i = 0; i < sizeof (s3_data_dependency_table) / sizeof (s3_data_dependency_table[0]); i++)
+ {
+ if ((pre_insn_type == s3_data_dependency_table[i].pre_insn_type)
+ && (s3_D_all_insn == s3_data_dependency_table[i].cur_insn_type
+ || cur_insn_type == s3_data_dependency_table[i].cur_insn_type)
+ && (strcmp (s3_data_dependency_table[i].pre_reg, "") == 0
+ || strcmp (s3_data_dependency_table[i].pre_reg, pre_reg) == 0)
+ && (strcmp (s3_data_dependency_table[i].cur_reg, "") == 0
+ || strcmp (s3_data_dependency_table[i].cur_reg, cur_reg) == 0))
+ {
+ bubbles = s3_data_dependency_table[i].bubblenum_3;
+ *warn_or_error = s3_data_dependency_table[i].warn_or_error;
+ break;
+ }
+ }
+
+ return bubbles;
+}
+
+static void
+s3_build_one_frag (struct s3_score_it one_inst)
+{
+ char *p;
+ int relaxable_p = s3_g_opt;
+ int relax_size = 0;
+
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ p = frag_more (one_inst.size);
+ s3_md_number_to_chars (p, one_inst.instruction, one_inst.size);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (one_inst.size);
+#endif
+
+ relaxable_p &= (one_inst.relax_size != 0);
+ relax_size = relaxable_p ? one_inst.relax_size : 0;
+
+ p = frag_var (rs_machine_dependent, relax_size + s3_RELAX_PAD_BYTE, 0,
+ s3_RELAX_ENCODE (one_inst.size, one_inst.relax_size,
+ one_inst.type, 0, 0, relaxable_p),
+ NULL, 0, NULL);
+
+ if (relaxable_p)
+ s3_md_number_to_chars (p, one_inst.relax_inst, relax_size);
+}
+
+static void
+s3_handle_dependency (struct s3_score_it *theinst)
+{
+ int i;
+ int warn_or_error = 0; /* warn - 0; error - 1 */
+ int bubbles = 0;
+ int remainder_bubbles = 0;
+ char cur_insn[s3_INSN_NAME_LEN];
+ char pre_insn[s3_INSN_NAME_LEN];
+ struct s3_score_it nop_inst;
+ struct s3_score_it pflush_inst;
+
+ nop_inst.instruction = 0x0000;
+ nop_inst.size = 2;
+ nop_inst.relax_inst = 0x80008000;
+ nop_inst.relax_size = 4;
+ nop_inst.type = NO16_OPD;
+
+ pflush_inst.instruction = 0x8000800a;
+ pflush_inst.size = 4;
+ pflush_inst.relax_inst = 0x8000;
+ pflush_inst.relax_size = 0;
+ pflush_inst.type = NO_OPD;
+
+ /* pflush will clear all data dependency. */
+ if (strcmp (theinst->name, "pflush") == 0)
+ {
+ s3_init_dependency_vector ();
+ return;
+ }
+
+ /* Push current instruction to s3_dependency_vector[0]. */
+ for (i = s3_vector_size - 1; i > 0; i--)
+ memcpy (&s3_dependency_vector[i], &s3_dependency_vector[i - 1], sizeof (s3_dependency_vector[i]));
+
+ memcpy (&s3_dependency_vector[0], theinst, sizeof (s3_dependency_vector[i]));
+
+ /* There is no dependency between nop and any instruction. */
+ if (strcmp (s3_dependency_vector[0].name, "nop") == 0
+ || strcmp (s3_dependency_vector[0].name, "nop!") == 0)
+ return;
+
+ strcpy (cur_insn, s3_dependency_vector[0].name);
+
+ for (i = 1; i < s3_vector_size; i++)
+ {
+ /* The element of s3_dependency_vector is NULL. */
+ if (s3_dependency_vector[i].name[0] == '\0')
+ continue;
+
+ strcpy (pre_insn, s3_dependency_vector[i].name);
+
+ bubbles = s3_check_dependency (pre_insn, s3_dependency_vector[i].reg,
+ cur_insn, s3_dependency_vector[0].reg, &warn_or_error);
+ remainder_bubbles = bubbles - i + 1;
+
+ if (remainder_bubbles > 0)
+ {
+ int j;
+
+ if (s3_fix_data_dependency == 1)
+ {
+ if (remainder_bubbles <= 2)
+ {
+ if (s3_warn_fix_data_dependency)
+ as_warn (_("Fix data dependency: %s %s -- %s %s (insert %d nop!/%d)"),
+ s3_dependency_vector[i].name, s3_dependency_vector[i].reg,
+ s3_dependency_vector[0].name, s3_dependency_vector[0].reg,
+ remainder_bubbles, bubbles);
+
+ for (j = (s3_vector_size - 1); (j - remainder_bubbles) > 0; j--)
+ memcpy (&s3_dependency_vector[j], &s3_dependency_vector[j - remainder_bubbles],
+ sizeof (s3_dependency_vector[j]));
+
+ for (j = 1; j <= remainder_bubbles; j++)
+ {
+ memset (&s3_dependency_vector[j], '\0', sizeof (s3_dependency_vector[j]));
+ /* Insert nop!. */
+ s3_build_one_frag (nop_inst);
+ }
+ }
+ else
+ {
+ if (s3_warn_fix_data_dependency)
+ as_warn (_("Fix data dependency: %s %s -- %s %s (insert 1 pflush/%d)"),
+ s3_dependency_vector[i].name, s3_dependency_vector[i].reg,
+ s3_dependency_vector[0].name, s3_dependency_vector[0].reg,
+ bubbles);
+
+ for (j = 1; j < s3_vector_size; j++)
+ memset (&s3_dependency_vector[j], '\0', sizeof (s3_dependency_vector[j]));
+
+ /* Insert pflush. */
+ s3_build_one_frag (pflush_inst);
+ }
+ }
+ else
+ {
+ if (warn_or_error)
+ {
+ as_bad (_("data dependency: %s %s -- %s %s (%d/%d bubble)"),
+ s3_dependency_vector[i].name, s3_dependency_vector[i].reg,
+ s3_dependency_vector[0].name, s3_dependency_vector[0].reg,
+ remainder_bubbles, bubbles);
+ }
+ else
+ {
+ as_warn (_("data dependency: %s %s -- %s %s (%d/%d bubble)"),
+ s3_dependency_vector[i].name, s3_dependency_vector[i].reg,
+ s3_dependency_vector[0].name, s3_dependency_vector[0].reg,
+ remainder_bubbles, bubbles);
+ }
+ }
+ }
+ }
+}
+
+static enum insn_class
+s3_get_insn_class_from_type (enum score_insn_type type)
+{
+ enum insn_class retval = (int) s3_FAIL;
+
+ switch (type)
+ {
+ case Rd_I4:
+ case Rd_I5:
+ case Rd_rvalueBP_I5:
+ case Rd_lvalueBP_I5:
+ case Rd_I8:
+ case PC_DISP8div2:
+ case PC_DISP11div2:
+ case Rd_Rs:
+ case Rd_HighRs:
+ case Rd_lvalueRs:
+ case Rd_rvalueRs:
+ case x_Rs:
+ case Rd_LowRs:
+ case NO16_OPD:
+ case Rd_SI5:
+ case Rd_SI6:
+ retval = INSN_CLASS_16;
+ break;
+ case Rd_Rs_I5:
+ case x_Rs_I5:
+ case x_I5_x:
+ case Rd_Rs_I14:
+ case I15:
+ case Rd_I16:
+ case Rd_SI16:
+ case Rd_rvalueRs_SI10:
+ case Rd_lvalueRs_SI10:
+ case Rd_rvalueRs_preSI12:
+ case Rd_rvalueRs_postSI12:
+ case Rd_lvalueRs_preSI12:
+ case Rd_lvalueRs_postSI12:
+ case Rd_Rs_SI14:
+ case Rd_rvalueRs_SI15:
+ case Rd_lvalueRs_SI15:
+ case PC_DISP19div2:
+ case PC_DISP24div2:
+ case Rd_Rs_Rs:
+ case x_Rs_x:
+ case x_Rs_Rs:
+ case Rd_Rs_x:
+ case Rd_x_Rs:
+ case Rd_x_x:
+ case OP5_rvalueRs_SI15:
+ case I5_Rs_Rs_I5_OP5:
+ case x_rvalueRs_post4:
+ case Rd_rvalueRs_post4:
+ case Rd_x_I5:
+ case Rd_lvalueRs_post4:
+ case x_lvalueRs_post4:
+ case Rd_Rs_Rs_imm:
+ case NO_OPD:
+ case Rd_lvalue32Rs:
+ case Rd_rvalue32Rs:
+ case Insn_GP:
+ case Insn_PIC:
+ case Insn_internal:
+ case Insn_BCMP:
+ case Ra_I9_I5:
+ retval = INSN_CLASS_32;
+ break;
+ case Insn_Type_PCE:
+ retval = INSN_CLASS_PCE;
+ break;
+ case Insn_Type_SYN:
+ retval = INSN_CLASS_SYN;
+ break;
+ case Rd_I30:
+ case Rd_I32:
+ retval = INSN_CLASS_48;
+ break;
+ default:
+ abort ();
+ break;
+ }
+ return retval;
+}
+
+/* Type of p-bits:
+ 48-bit instruction: 1, 1, 0.
+ 32-bit instruction: 1, 0.
+ 16-bit instruction: 0. */
+static bfd_vma
+s3_adjust_paritybit (bfd_vma m_code, enum insn_class i_class)
+{
+ bfd_vma result = 0;
+ bfd_vma m_code_high = 0;
+ unsigned long m_code_middle = 0;
+ unsigned long m_code_low = 0;
+ bfd_vma pb_high = 0;
+ unsigned long pb_middle = 0;
+ unsigned long pb_low = 0;
+
+ if (i_class == INSN_CLASS_48)
+ {
+ pb_high = 0x800000000000LL;
+ pb_middle = 0x80000000;
+ pb_low = 0x00000000;
+ m_code_high = m_code & 0x1fffc0000000LL;
+ m_code_middle = m_code & 0x3fff8000;
+ m_code_low = m_code & 0x00007fff;
+ result = pb_high | (m_code_high << 2) |
+ pb_middle | (m_code_middle << 1) |
+ pb_low | m_code_low;
+ }
+ else if (i_class == INSN_CLASS_32 || i_class == INSN_CLASS_SYN)
+ {
+ pb_high = 0x80000000;
+ pb_low = 0x00000000;
+ m_code_high = m_code & 0x3fff8000;
+ m_code_low = m_code & 0x00007fff;
+ result = pb_high | (m_code_high << 1) | pb_low | m_code_low;
+ }
+ else if (i_class == INSN_CLASS_16)
+ {
+ pb_high = 0;
+ pb_low = 0;
+ m_code_high = m_code & 0x3fff8000;
+ m_code_low = m_code & 0x00007fff;
+ result = pb_high | (m_code_high << 1) | pb_low | m_code_low;
+ }
+ else if (i_class == INSN_CLASS_PCE)
+ {
+ /* Keep original. */
+ pb_high = 0;
+ pb_low = 0x00008000;
+ m_code_high = m_code & 0x3fff8000;
+ m_code_low = m_code & 0x00007fff;
+ result = pb_high | (m_code_high << 1) | pb_low | m_code_low;
+ }
+ else
+ {
+ abort ();
+ }
+
+ return result;
+}
+
+static void
+s3_gen_insn_frag (struct s3_score_it *part_1, struct s3_score_it *part_2)
+{
+ char *p;
+ bfd_boolean pce_p = FALSE;
+ int relaxable_p = s3_g_opt;
+ int relax_size = 0;
+ struct s3_score_it *inst1 = part_1;
+ struct s3_score_it *inst2 = part_2;
+ struct s3_score_it backup_inst1;
+
+ pce_p = (inst2) ? TRUE : FALSE;
+ memcpy (&backup_inst1, inst1, sizeof (struct s3_score_it));
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ if (pce_p)
+ {
+ backup_inst1.instruction = ((backup_inst1.instruction & 0x7FFF) << 15)
+ | (inst2->instruction & 0x7FFF);
+ backup_inst1.instruction = s3_adjust_paritybit (backup_inst1.instruction, INSN_CLASS_PCE);
+ backup_inst1.relax_inst = 0x8000;
+ backup_inst1.size = s3_INSN_SIZE;
+ backup_inst1.relax_size = 0;
+ backup_inst1.type = Insn_Type_PCE;
+ }
+ else
+ {
+ backup_inst1.instruction = s3_adjust_paritybit (backup_inst1.instruction,
+ s3_GET_INSN_CLASS (backup_inst1.type));
+ }
+
+ if (backup_inst1.relax_size != 0)
+ {
+ enum insn_class tmp;
+
+ tmp = (backup_inst1.size == s3_INSN_SIZE) ? INSN_CLASS_16 : INSN_CLASS_32;
+ backup_inst1.relax_inst = s3_adjust_paritybit (backup_inst1.relax_inst, tmp);
+ }
+
+ /* Check data dependency. */
+ s3_handle_dependency (&backup_inst1);
+
+ /* Start a new frag if frag_now is not empty and is not instruction frag, maybe it contains
+ data produced by .ascii etc. Doing this is to make one instruction per frag. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+
+ /* Here, we must call frag_grow in order to keep the instruction frag type is
+ rs_machine_dependent.
+ For, frag_var may change frag_now->fr_type to rs_fill by calling frag_grow which
+ acturally will call frag_wane.
+ Calling frag_grow first will create a new frag_now which free size is 20 that is enough
+ for frag_var. */
+ frag_grow (20);
+
+ p = frag_more (backup_inst1.size);
+ s3_md_number_to_chars (p, backup_inst1.instruction, backup_inst1.size);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (backup_inst1.size);
+#endif
+
+ /* Generate fixup structure. */
+ if (pce_p)
+ {
+ if (inst1->reloc.type != BFD_RELOC_NONE)
+ s3_fix_new_score (frag_now, p - frag_now->fr_literal,
+ inst1->size, &inst1->reloc.exp,
+ inst1->reloc.pc_rel, inst1->reloc.type);
+
+ if (inst2->reloc.type != BFD_RELOC_NONE)
+ s3_fix_new_score (frag_now, p - frag_now->fr_literal + 2,
+ inst2->size, &inst2->reloc.exp, inst2->reloc.pc_rel, inst2->reloc.type);
+ }
+ else
+ {
+ if (backup_inst1.reloc.type != BFD_RELOC_NONE)
+ s3_fix_new_score (frag_now, p - frag_now->fr_literal,
+ backup_inst1.size, &backup_inst1.reloc.exp,
+ backup_inst1.reloc.pc_rel, backup_inst1.reloc.type);
+ }
+
+ /* relax_size may be 2, 4, 12 or 0, 0 indicates no relaxation. */
+ relaxable_p &= (backup_inst1.relax_size != 0);
+ relax_size = relaxable_p ? backup_inst1.relax_size : 0;
+
+ p = frag_var (rs_machine_dependent, relax_size + s3_RELAX_PAD_BYTE, 0,
+ s3_RELAX_ENCODE (backup_inst1.size, backup_inst1.relax_size,
+ backup_inst1.type, 0, 0, relaxable_p),
+ backup_inst1.reloc.exp.X_add_symbol, 0, NULL);
+
+ if (relaxable_p)
+ s3_md_number_to_chars (p, backup_inst1.relax_inst, relax_size);
+
+ memcpy (inst1, &backup_inst1, sizeof (struct s3_score_it));
+}
+
+static void
+s3_parse_16_32_inst (char *insnstr, bfd_boolean gen_frag_p)
+{
+ char c;
+ char *p;
+ char *operator = insnstr;
+ const struct s3_asm_opcode *opcode;
+
+ /* Parse operator and operands. */
+ s3_skip_whitespace (operator);
+
+ for (p = operator; *p != '\0'; p++)
+ if ((*p == ' ') || (*p == '!'))
+ break;
+
+ if (*p == '!')
+ p++;
+
+ c = *p;
+ *p = '\0';
+
+ opcode = (const struct s3_asm_opcode *) hash_find (s3_score_ops_hsh, operator);
+ *p = c;
+
+ memset (&s3_inst, '\0', sizeof (s3_inst));
+ sprintf (s3_inst.str, "%s", insnstr);
+ if (opcode)
+ {
+ s3_inst.instruction = opcode->value;
+ s3_inst.relax_inst = opcode->relax_value;
+ s3_inst.type = opcode->type;
+ s3_inst.size = s3_GET_INSN_SIZE (s3_inst.type);
+ s3_inst.relax_size = 0;
+ s3_inst.bwarn = 0;
+ sprintf (s3_inst.name, "%s", opcode->template_name);
+ strcpy (s3_inst.reg, "");
+ s3_inst.error = NULL;
+ s3_inst.reloc.type = BFD_RELOC_NONE;
+
+ (*opcode->parms) (p);
+
+ /* It indicates current instruction is a macro instruction if s3_inst.bwarn equals -1. */
+ if ((s3_inst.bwarn != -1) && (!s3_inst.error) && (gen_frag_p))
+ s3_gen_insn_frag (&s3_inst, NULL);
+ }
+ else
+ s3_inst.error = _("unrecognized opcode");
+}
+
+static void
+s3_parse_48_inst (char *insnstr, bfd_boolean gen_frag_p)
+{
+ char c;
+ char *p;
+ char *operator = insnstr;
+ const struct s3_asm_opcode *opcode;
+
+ /* Parse operator and operands. */
+ s3_skip_whitespace (operator);
+
+ for (p = operator; *p != '\0'; p++)
+ if (*p == ' ')
+ break;
+
+ c = *p;
+ *p = '\0';
+
+ opcode = (const struct s3_asm_opcode *) hash_find (s3_score_ops_hsh, operator);
+ *p = c;
+
+ memset (&s3_inst, '\0', sizeof (s3_inst));
+ sprintf (s3_inst.str, "%s", insnstr);
+ if (opcode)
+ {
+ s3_inst.instruction = opcode->value;
+ s3_inst.relax_inst = opcode->relax_value;
+ s3_inst.type = opcode->type;
+ s3_inst.size = s3_GET_INSN_SIZE (s3_inst.type);
+ s3_inst.relax_size = 0;
+ s3_inst.bwarn = 0;
+ sprintf (s3_inst.name, "%s", opcode->template_name);
+ strcpy (s3_inst.reg, "");
+ s3_inst.error = NULL;
+ s3_inst.reloc.type = BFD_RELOC_NONE;
+
+ (*opcode->parms) (p);
+
+ /* It indicates current instruction is a macro instruction if s3_inst.bwarn equals -1. */
+ if ((s3_inst.bwarn != -1) && (!s3_inst.error) && (gen_frag_p))
+ s3_gen_insn_frag (&s3_inst, NULL);
+ }
+ else
+ s3_inst.error = _("unrecognized opcode");
+}
+
+static int
+s3_append_insn (char *str, bfd_boolean gen_frag_p)
+{
+ int retval = s3_SUCCESS;
+
+ s3_parse_16_32_inst (str, gen_frag_p);
+
+ if (s3_inst.error)
+ {
+ retval = (int) s3_FAIL;
+ as_bad (_("%s -- `%s'"), s3_inst.error, s3_inst.str);
+ s3_inst.error = NULL;
+ }
+
+ return retval;
+}
+
+static void
+s3_do16_mv_cmp (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 5, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 0, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+}
+
+static void
+s3_do16_cmpi (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 5, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 0, _SIMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+}
+
+static void
+s3_do16_addi (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reglow_required_here (&str, 6) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 0, _SIMM6) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+}
+
+/* Handle bitclr! / bitset! / bittst! / bittgl! */
+static void
+s3_do16_rdi5 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reglow_required_here (&str, 5) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 0, _IMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ else
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >>5) & 0xf) << 20)
+ | (((s3_inst.instruction >> 5) & 0xf) << 15) | (((s3_inst.instruction ) & 0x1f) << 10);
+ s3_inst.relax_size = 4;
+ }
+}
+
+
+/* Handle sdbbp!. */
+static void
+s3_do16_xi5 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_data_op2 (&str, 0, _IMM5) == (int) s3_FAIL || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+/* Check that an immediate is word alignment or half word alignment.
+ If so, convert it to the right format. */
+static int
+s3_validate_immediate_align (int val, unsigned int data_type)
+{
+ if (data_type == _IMM5_RSHIFT_1)
+ {
+ if (val % 2)
+ {
+ s3_inst.error = _("address offset must be half word alignment");
+ return (int) s3_FAIL;
+ }
+ }
+ else if ((data_type == _IMM5_RSHIFT_2) || (data_type == _IMM10_RSHIFT_2))
+ {
+ if (val % 4)
+ {
+ s3_inst.error = _("address offset must be word alignment");
+ return (int) s3_FAIL;
+ }
+ }
+
+ return s3_SUCCESS;
+}
+
+static int
+s3_exp_ldst_offset (char **str, int shift, unsigned int data_type)
+{
+ char *dataptr;
+
+ dataptr = * str;
+
+ if ((*dataptr == '0') && (*(dataptr + 1) == 'x')
+ && (data_type != _SIMM16_LA)
+ && (data_type != _VALUE_HI16)
+ && (data_type != _VALUE_LO16)
+ && (data_type != _IMM16)
+ && (data_type != _IMM15)
+ && (data_type != _IMM14)
+ && (data_type != _IMM4)
+ && (data_type != _IMM5)
+ && (data_type != _IMM8)
+ && (data_type != _IMM5_RSHIFT_1)
+ && (data_type != _IMM5_RSHIFT_2)
+ && (data_type != _SIMM14_NEG)
+ && (data_type != _IMM10_RSHIFT_2))
+ {
+ data_type += 24;
+ }
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, str) == (int) s3_FAIL)
+ return (int) s3_FAIL;
+
+ if (s3_inst.reloc.exp.X_op == O_constant)
+ {
+ /* Need to check the immediate align. */
+ int value = s3_validate_immediate_align (s3_inst.reloc.exp.X_add_number, data_type);
+
+ if (value == (int) s3_FAIL)
+ return (int) s3_FAIL;
+
+ value = s3_validate_immediate (s3_inst.reloc.exp.X_add_number, data_type, 0);
+ if (value == (int) s3_FAIL)
+ {
+ if (data_type < 30)
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type].bits,
+ s3_score_df_range[data_type].range[0], s3_score_df_range[data_type].range[1]);
+ else
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type - 24].bits,
+ s3_score_df_range[data_type - 24].range[0], s3_score_df_range[data_type - 24].range[1]);
+ s3_inst.error = s3_err_msg;
+ return (int) s3_FAIL;
+ }
+
+ if (data_type == _IMM5_RSHIFT_1)
+ {
+ value >>= 1;
+ }
+ else if ((data_type == _IMM5_RSHIFT_2) || (data_type == _IMM10_RSHIFT_2))
+ {
+ value >>= 2;
+ }
+
+ if (s3_score_df_range[data_type].range[0] != 0)
+ {
+ value &= (1 << s3_score_df_range[data_type].bits) - 1;
+ }
+
+ s3_inst.instruction |= value << shift;
+ }
+ else
+ {
+ s3_inst.reloc.pc_rel = 0;
+ }
+
+ return s3_SUCCESS;
+}
+
+static void
+s3_do_ldst_insn (char *str)
+{
+ int pre_inc = 0;
+ int conflict_reg;
+ int value;
+ char * temp;
+ char *dataptr;
+ int reg;
+ int ldst_idx = 0;
+
+ s3_skip_whitespace (str);
+
+ if (((conflict_reg = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ return;
+
+ /* ld/sw rD, [rA, simm15] ld/sw rD, [rA]+, simm12 ld/sw rD, [rA, simm12]+. */
+ if (*str == '[')
+ {
+ str++;
+ s3_skip_whitespace (str);
+
+ if ((reg = s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ return;
+
+ /* Conflicts can occur on stores as well as loads. */
+ conflict_reg = (conflict_reg == reg);
+ s3_skip_whitespace (str);
+ temp = str + 1; /* The latter will process decimal/hex expression. */
+
+ /* ld/sw rD, [rA]+, simm12 ld/sw rD, [rA]+. */
+ if (*str == ']')
+ {
+ str++;
+ if (*str == '+')
+ {
+ str++;
+ /* ld/sw rD, [rA]+, simm12. */
+ if (s3_skip_past_comma (&str) == s3_SUCCESS)
+ {
+ if ((s3_exp_ldst_offset (&str, 3, _SIMM12) == (int) s3_FAIL)
+ || (s3_end_of_line (str) == (int) s3_FAIL))
+ return;
+
+ if (conflict_reg)
+ {
+ unsigned int ldst_func = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+
+ if ((ldst_func == INSN_LH)
+ || (ldst_func == INSN_LHU)
+ || (ldst_func == INSN_LW)
+ || (ldst_func == INSN_LB)
+ || (ldst_func == INSN_LBU))
+ {
+ s3_inst.error = _("register same as write-back base");
+ return;
+ }
+ }
+
+ ldst_idx = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction |= s3_score_ldst_insns[ldst_idx * 3 + LDST_POST].value;
+
+ /* lw rD, [rA]+, 4 convert to pop rD, [rA]. */
+ if ((s3_inst.instruction & 0x3e000007) == 0x0e000000)
+ {
+ /* rs = r0, offset = 4 */
+ if ((((s3_inst.instruction >> 15) & 0x1f) == 0)
+ && (((s3_inst.instruction >> 3) & 0xfff) == 4))
+ {
+ /* Relax to pop!. */
+ s3_inst.relax_inst = 0x0040 | ((s3_inst.instruction >> 20) & 0x1f);
+ s3_inst.relax_size = 2;
+ }
+ }
+ return;
+ }
+ /* ld/sw rD, [rA]+ convert to ld/sw rD, [rA, 0]+. */
+ else
+ {
+ s3_SET_INSN_ERROR (NULL);
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ pre_inc = 1;
+ value = s3_validate_immediate (s3_inst.reloc.exp.X_add_number, _SIMM12, 0);
+ value &= (1 << s3_score_df_range[_SIMM12].bits) - 1;
+ ldst_idx = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction |= s3_score_ldst_insns[ldst_idx * 3 + pre_inc].value;
+ s3_inst.instruction |= value << 3;
+ s3_inst.relax_inst = 0x8000;
+ return;
+ }
+ }
+ /* ld/sw rD, [rA] convert to ld/sw rD, [rA, simm15]. */
+ else
+ {
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ ldst_idx = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction |= s3_score_ldst_insns[ldst_idx * 3 + LDST_NOUPDATE].value;
+
+ /* lbu rd, [rs] -> lbu! rd, [rs] */
+ if (ldst_idx == INSN_LBU)
+ {
+ s3_inst.relax_inst = INSN16_LBU;
+ }
+ else if (ldst_idx == INSN_LH)
+ {
+ s3_inst.relax_inst = INSN16_LH;
+ }
+ else if (ldst_idx == INSN_LW)
+ {
+ s3_inst.relax_inst = INSN16_LW;
+ }
+ else if (ldst_idx == INSN_SB)
+ {
+ s3_inst.relax_inst = INSN16_SB;
+ }
+ else if (ldst_idx == INSN_SH)
+ {
+ s3_inst.relax_inst = INSN16_SH;
+ }
+ else if (ldst_idx == INSN_SW)
+ {
+ s3_inst.relax_inst = INSN16_SW;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+
+ /* lw/lh/lbu/sw/sh/sb, offset = 0, relax to 16 bit instruction. */
+ /* if ((ldst_idx == INSN_LBU)
+ || (ldst_idx == INSN_LH)
+ || (ldst_idx == INSN_LW)
+ || (ldst_idx == INSN_SB) || (ldst_idx == INSN_SH) || (ldst_idx == INSN_SW))*/
+ if ( (ldst_idx == INSN_LW)|| (ldst_idx == INSN_SW))
+ {
+ /* ra only 3 bit , rd only 4 bit for lw! and sw! */
+ if ((((s3_inst.instruction >> 15) & 0x18) == 0) && (((s3_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 20) & 0xf) << 8) |
+ (((s3_inst.instruction >> 15) & 0x7) << 5);
+ s3_inst.relax_size = 2;
+ }
+ }
+
+ return;
+ }
+ }
+ /* ld/sw rD, [rA, simm15] ld/sw rD, [rA, simm12]+. */
+ else
+ {
+ if (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ s3_inst.error = _("pre-indexed expression expected");
+ return;
+ }
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL)
+ return;
+
+ s3_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+
+ s3_skip_whitespace (str);
+ /* ld/sw rD, [rA, simm12]+. */
+ if (*str == '+')
+ {
+ str++;
+ pre_inc = 1;
+ if (conflict_reg)
+ {
+ unsigned int ldst_func = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+
+ if ((ldst_func == INSN_LH)
+ || (ldst_func == INSN_LHU)
+ || (ldst_func == INSN_LW)
+ || (ldst_func == INSN_LB)
+ || (ldst_func == INSN_LBU))
+ {
+ s3_inst.error = _("register same as write-back base");
+ return;
+ }
+ }
+ }
+
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if (s3_inst.reloc.exp.X_op == O_constant)
+ {
+ unsigned int data_type;
+
+ if (pre_inc == 1)
+ data_type = _SIMM12;
+ else
+ data_type = _SIMM15;
+ dataptr = temp;
+
+ if ((*dataptr == '0') && (*(dataptr + 1) == 'x')
+ && (data_type != _SIMM16_LA)
+ && (data_type != _VALUE_HI16)
+ && (data_type != _VALUE_LO16)
+ && (data_type != _IMM16)
+ && (data_type != _IMM15)
+ && (data_type != _IMM14)
+ && (data_type != _IMM4)
+ && (data_type != _IMM5)
+ && (data_type != _IMM8)
+ && (data_type != _IMM5_RSHIFT_1)
+ && (data_type != _IMM5_RSHIFT_2)
+ && (data_type != _SIMM14_NEG)
+ && (data_type != _IMM10_RSHIFT_2))
+ {
+ data_type += 24;
+ }
+
+ value = s3_validate_immediate (s3_inst.reloc.exp.X_add_number, data_type, 0);
+ if (value == (int) s3_FAIL)
+ {
+ if (data_type < 30)
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type].bits,
+ s3_score_df_range[data_type].range[0], s3_score_df_range[data_type].range[1]);
+ else
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type - 24].bits,
+ s3_score_df_range[data_type - 24].range[0],
+ s3_score_df_range[data_type - 24].range[1]);
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+
+ value &= (1 << s3_score_df_range[data_type].bits) - 1;
+ ldst_idx = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction |= s3_score_ldst_insns[ldst_idx * 3 + pre_inc].value;
+ if (pre_inc == 1)
+ s3_inst.instruction |= value << 3;
+ else
+ s3_inst.instruction |= value;
+
+ /* lw rD, [rA, simm15] */
+ if ((s3_inst.instruction & 0x3e000000) == 0x20000000)
+ {
+ /* rD in [r0 - r15]. , ra in [r0-r7] */
+ if ((((s3_inst.instruction >> 15) & 0x18) == 0)
+ && (((s3_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm = [bit 7], lw -> lw!. */
+ if (((s3_inst.instruction & 0x7f80) == 0)&&((s3_inst.instruction &0x3)==0))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 15) & 0x7) << 5)
+ | (((s3_inst.instruction >> 20) & 0xf) << 8)|(value>>2);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+ /* sw rD, [rA, simm15] */
+ else if ((s3_inst.instruction & 0x3e000000) == 0x28000000)
+ {
+ /* rD is in [r0 - r15] and ra in [r0-r7] */
+ if ((((s3_inst.instruction >> 15) & 0x18) == 0) && (((s3_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 =7 bit , sw -> sw!. */
+ if (((s3_inst.instruction & 0x7f80) == 0)&&((s3_inst.instruction &0x3)==0))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 15) & 0xf) << 5)
+ | (((s3_inst.instruction >> 20) & 0xf) << 8)|(value>>2);
+ s3_inst.relax_size = 2;
+ }
+ /* rA = r2, sw -> swp!. */
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+ /* sw rD, [rA, simm15]+ sw pre. */
+ else if ((s3_inst.instruction & 0x3e000007) == 0x06000004)
+ {
+ /* simm15 = -4. and ra==r0 */
+ if ((((s3_inst.instruction >> 15) & 0x1f) == 0)
+ && (((s3_inst.instruction >> 3) & 0xfff) == 0xffc))
+ {
+ /* sw -> push!. */
+ s3_inst.relax_inst = 0x0060 | ((s3_inst.instruction >> 20) & 0x1f);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+
+ return;
+ }
+ else
+ {
+ /* FIXME: may set error, for there is no ld/sw rD, [rA, label] */
+ s3_inst.reloc.pc_rel = 0;
+ }
+ }
+ }
+ else
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ }
+}
+
+/* Handle cache. */
+static void
+s3_do_cache (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if ((s3_data_op2 (&str, 20, _IMM5) == (int) s3_FAIL) || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ int cache_op;
+
+ cache_op = (s3_inst.instruction >> 20) & 0x1F;
+ sprintf (s3_inst.name, "cache %d", cache_op);
+ }
+
+ if (*str == '[')
+ {
+ str++;
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ return;
+
+ s3_skip_whitespace (str);
+
+ /* cache op, [rA] */
+ if (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ s3_SET_INSN_ERROR (NULL);
+ if (*str != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+ str++;
+ }
+ /* cache op, [rA, simm15] */
+ else
+ {
+ if (s3_exp_ldst_offset (&str, 0, _SIMM15) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ s3_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+ }
+
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ }
+}
+
+static void
+s3_do_crdcrscrsimm5 (char *str)
+{
+ char *strbak;
+
+ strbak = str;
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE_CR) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE_CR) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE_CR) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ str = strbak;
+ /* cop1 cop_code20. */
+ if (s3_data_op2 (&str, 5, _IMM20) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ if (s3_data_op2 (&str, 5, _IMM5) == (int) s3_FAIL)
+ return;
+ }
+
+ s3_end_of_line (str);
+}
+
+/* Handle ldc/stc. */
+static void
+s3_do_ldst_cop (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if ((s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE_CR) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ return;
+
+ if (*str == '[')
+ {
+ str++;
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ return;
+
+ s3_skip_whitespace (str);
+
+ if (*str++ != ']')
+ {
+ if (s3_exp_ldst_offset (&str, 5, _IMM10_RSHIFT_2) == (int) s3_FAIL)
+ return;
+
+ s3_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+ }
+
+ s3_end_of_line (str);
+ }
+ else
+ s3_inst.error = s3_BAD_ARGS;
+}
+
+static void
+s3_do16_ldst_insn (char *str)
+{
+ int conflict_reg = 0;
+ s3_skip_whitespace (str);
+
+ if ((s3_reglow_required_here (&str, 8) == (int) s3_FAIL) || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ return;
+
+ if (*str == '[')
+ {
+
+ str++;
+ s3_skip_whitespace (str);
+
+ if ((conflict_reg = s3_reglow_required_here (&str, 5)) == (int) s3_FAIL)
+ return;
+ if (conflict_reg&0x8)
+ {
+ sprintf (s3_err_msg, _("invalid register number: %d is not in [r0--r7]"),conflict_reg);
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if (*str == ']')
+ {
+ str++;
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ if (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ s3_inst.error = _("comma is expected");
+ return;
+ }
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL)
+ return;
+ s3_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ if (s3_inst.reloc.exp.X_op == O_constant)
+ {
+ int value;
+ unsigned int data_type;
+ data_type = _IMM5_RSHIFT_2;
+ value = s3_validate_immediate (s3_inst.reloc.exp.X_add_number, data_type, 0);
+ if (value == (int) s3_FAIL)
+ {
+ if (data_type < 30)
+ sprintf (s3_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s3_score_df_range[data_type].bits,
+ s3_score_df_range[data_type].range[0], s3_score_df_range[data_type].range[1]);
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+ if (value & 0x3)
+ {
+ sprintf (s3_err_msg, _("invalid constant: %d is not word align integer"),value);
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+
+ value >>= 2;
+ s3_inst.instruction |= value;
+ }
+ }
+ }
+ else
+ {
+ sprintf (s3_err_msg, _("missing ["));
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+}
+
+static void
+s3_do_lw48 (char *str)
+{
+ bfd_signed_vma val = 0;
+
+ s3_skip_whitespace (str);
+
+ if ((s3_reg_required_here (&str, 37, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ return;
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ /* Check word align for lw48 rd, value. */
+ if ((s3_inst.reloc.exp.X_add_symbol == NULL)
+ && ((s3_inst.reloc.exp.X_add_number & 0x3) != 0))
+ {
+ s3_inst.error = _("invalid constant: 32 bit expression not word align");
+ return;
+ }
+
+ /* Check and set offset. */
+ val = s3_inst.reloc.exp.X_add_number;
+ if ((s3_inst.reloc.exp.X_add_symbol == NULL)
+ && (!(val >= 0 && val <= 0xffffffffLL)))
+ {
+ s3_inst.error = _("invalid constant: 32 bit expression not in range [0, 0xffffffff]");
+ return;
+ }
+
+ val &= 0xffffffff;
+ val >>= 2;
+ s3_inst.instruction |= (val << 7);
+
+ /* Set reloc type. */
+ s3_inst.reloc.type = BFD_RELOC_SCORE_IMM30;
+
+}
+
+static void
+s3_do_sw48 (char *str)
+{
+ bfd_signed_vma val = 0;
+
+ s3_skip_whitespace (str);
+
+ if ((s3_reg_required_here (&str, 37, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ return;
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ /* Check word align for lw48 rd, value. */
+ if ((s3_inst.reloc.exp.X_add_symbol == NULL)
+ && ((s3_inst.reloc.exp.X_add_number & 0x3) != 0))
+ {
+ s3_inst.error = _("invalid constant: 32 bit expression not word align");
+ return;
+ }
+
+ /* Check and set offset. */
+ val = s3_inst.reloc.exp.X_add_number;
+ if ((s3_inst.reloc.exp.X_add_symbol == NULL)
+ && (!(val >= 0 && val <= 0xffffffffLL)))
+ {
+ s3_inst.error = _("invalid constant: 32 bit expression not in range [0, 0xffffffff]");
+ return;
+ }
+
+ val &= 0xffffffff;
+ val >>= 2;
+ s3_inst.instruction |= (val << 7);
+
+ /* Set reloc type. */
+ s3_inst.reloc.type = BFD_RELOC_SCORE_IMM30;
+}
+
+static void
+s3_do_ldi48 (char *str)
+{
+ bfd_signed_vma val;
+
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 37, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ return;
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ /* Check and set offset. */
+ val = s3_inst.reloc.exp.X_add_number;
+ if (!(val >= -0xffffffffLL && val <= 0xffffffffLL))
+ {
+ s3_inst.error = _("invalid constant: 32 bit expression not in range [-0x80000000, 0x7fffffff]");
+ return;
+ }
+
+ val &= 0xffffffff;
+ s3_inst.instruction |= (val << 5);
+
+ /* Set reloc type. */
+ s3_inst.reloc.type = BFD_RELOC_SCORE_IMM32;
+}
+
+static void
+s3_do_sdbbp48 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_data_op2 (&str, 5, _IMM5) == (int) s3_FAIL || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+static void
+s3_do_and48 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reglow_required_here (&str, 38) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reglow_required_here (&str, 34) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 2, _IMM32) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+static void
+s3_do_or48 (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if (s3_reglow_required_here (&str, 38) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reglow_required_here (&str, 34) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 2, _IMM32) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+static void
+s3_do_mbitclr (char *str)
+{
+ int val;
+ s3_skip_whitespace (str);
+
+ if (*str != '[')
+ {
+ sprintf (s3_err_msg, _("missing ["));
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+ str++;
+
+ s3_inst.instruction &= 0x0;
+
+ if ((s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || (s3_data_op2 (&str, 0, _IMM11) == (int) s3_FAIL))
+ return;
+
+ /* Get imm11 and refill opcode. */
+ val = s3_inst.instruction & 0x7ff;
+ val >>= 2;
+ s3_inst.instruction &= 0x000f8000;
+ s3_inst.instruction |= 0x00000064;
+
+ if (*str != ']')
+ {
+ sprintf (s3_err_msg, _("missing ]"));
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+ str++;
+
+ if ((s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || (s3_data_op2 (&str, 10, _IMM5) == (int) s3_FAIL))
+ return;
+
+ /* Set imm11 to opcode. */
+ s3_inst.instruction |= (val & 0x1)
+ | (((val >> 1 ) & 0x7) << 7)
+ | (((val >> 4 ) & 0x1f) << 20);
+}
+
+static void
+s3_do_mbitset (char *str)
+{
+ int val;
+ s3_skip_whitespace (str);
+
+ if (*str != '[')
+ {
+ sprintf (s3_err_msg, _("missing ["));
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+ str++;
+
+ s3_inst.instruction &= 0x0;
+
+ if ((s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || (s3_data_op2 (&str, 0, _IMM11) == (int) s3_FAIL))
+ return;
+
+ /* Get imm11 and refill opcode. */
+ val = s3_inst.instruction & 0x7ff;
+ val >>= 2;
+ s3_inst.instruction &= 0x000f8000;
+ s3_inst.instruction |= 0x0000006c;
+
+ if (*str != ']')
+ {
+ sprintf (s3_err_msg, _("missing ]"));
+ s3_inst.error = s3_err_msg;
+ return;
+ }
+ str++;
+
+ if ((s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || (s3_data_op2 (&str, 10, _IMM5) == (int) s3_FAIL))
+ return;
+
+ /* Set imm11 to opcode. */
+ s3_inst.instruction |= (val & 0x1)
+ | (((val >> 1 ) & 0x7) << 7)
+ | (((val >> 4 ) & 0x1f) << 20);
+}
+
+static void
+s3_do16_slli_srli (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if ((s3_reglow_required_here (&str, 5) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || s3_data_op2 (&str, 0, _IMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+static void
+s3_do16_ldiu (char *str)
+{
+ s3_skip_whitespace (str);
+
+ if ((s3_reg_required_here (&str, 5,s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || s3_data_op2 (&str, 0, _IMM5) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+static void
+s3_do16_push_pop (char *str)
+{
+ s3_skip_whitespace (str);
+ if ((s3_reg_required_here (&str, 0, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+}
+
+static void
+s3_do16_rpush (char *str)
+{
+ int reg;
+ int val;
+ s3_skip_whitespace (str);
+ if ((reg = (s3_reg_required_here (&str, 5, s3_REG_TYPE_SCORE))) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 0, _IMM5_MULTI_LOAD) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ /* 0: indicate 32.
+ 1: invalide value.
+ 2: to 31: normal value. */
+ val = s3_inst.instruction & 0x1f;
+ if (val == 1)
+ {
+ s3_inst.error = _("imm5 should >= 2");
+ return;
+ }
+ if (reg >= 32)
+ {
+ s3_inst.error = _("reg should <= 31");
+ return;
+ }
+}
+
+static void
+s3_do16_rpop (char *str)
+{
+ int reg;
+ int val;
+ s3_skip_whitespace (str);
+ if ((reg = (s3_reg_required_here (&str, 5, s3_REG_TYPE_SCORE))) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_data_op2 (&str, 0, _IMM5_MULTI_LOAD) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ /* 0: indicate 32.
+ 1: invalide value.
+ 2: to 31: normal value. */
+ val = s3_inst.instruction & 0x1f;
+ if (val == 1)
+ {
+ s3_inst.error = _("imm5 should >= 2");
+ return;
+ }
+
+ if (reg >= 32)
+ {
+ s3_inst.error = _("reg should <= 31");
+ return;
+ }
+ else
+ {
+ if ((reg + val) <= 32)
+ reg = reg + val - 1;
+ else
+ reg = reg + val - 33;
+ s3_inst.instruction &= 0x7c1f;
+ s3_inst.instruction |= (reg << 5);
+ return;
+ }
+}
+
+/* Handle lcb/lcw/lce/scb/scw/sce. */
+static void
+s3_do_ldst_unalign (char *str)
+{
+ int conflict_reg;
+
+ if (s3_university_version == 1)
+ {
+ s3_inst.error = s3_ERR_FOR_SCORE5U_ATOMIC;
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ /* lcb/scb [rA]+. */
+ if (*str == '[')
+ {
+ str++;
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ return;
+
+ if (*str++ == ']')
+ {
+ if (*str++ != '+')
+ {
+ s3_inst.error = _("missing +");
+ return;
+ }
+ }
+ else
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ }
+ /* lcw/lce/scb/sce rD, [rA]+. */
+ else
+ {
+ if (((conflict_reg = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ {
+ return;
+ }
+
+ s3_skip_whitespace (str);
+ if (*str++ == '[')
+ {
+ int reg;
+
+ s3_skip_whitespace (str);
+ if ((reg = s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ /* Conflicts can occur on stores as well as loads. */
+ conflict_reg = (conflict_reg == reg);
+ s3_skip_whitespace (str);
+ if (*str++ == ']')
+ {
+ unsigned int ldst_func = s3_inst.instruction & LDST_UNALIGN_MASK;
+
+ if (*str++ == '+')
+ {
+ if (conflict_reg)
+ {
+ as_warn (_("%s register same as write-back base"),
+ ((ldst_func & UA_LCE) || (ldst_func & UA_LCW)
+ ? _("destination") : _("source")));
+ }
+ }
+ else
+ {
+ s3_inst.error = _("missing +");
+ return;
+ }
+
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+ }
+ else
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ return;
+ }
+ }
+}
+
+/* Handle alw/asw. */
+static void
+s3_do_ldst_atomic (char *str)
+{
+ if (s3_university_version == 1)
+ {
+ s3_inst.error = s3_ERR_FOR_SCORE5U_ATOMIC;
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if ((s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else
+ {
+
+ s3_skip_whitespace (str);
+ if (*str++ == '[')
+ {
+ int reg;
+
+ s3_skip_whitespace (str);
+ if ((reg = s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ s3_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s3_inst.error = _("missing ]");
+ return;
+ }
+
+ s3_end_of_line (str);
+ }
+ else
+ s3_inst.error = s3_BAD_ARGS;
+ }
+}
+
+static void
+s3_build_relax_frag (struct s3_score_it fix_insts[s3_RELAX_INST_NUM], int fix_num ATTRIBUTE_UNUSED,
+ struct s3_score_it var_insts[s3_RELAX_INST_NUM], int var_num,
+ symbolS *add_symbol)
+{
+ int i;
+ char *p;
+ fixS *fixp = NULL;
+ fixS *cur_fixp = NULL;
+ long where;
+ struct s3_score_it inst_main;
+
+ memcpy (&inst_main, &fix_insts[0], sizeof (struct s3_score_it));
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ inst_main.instruction = s3_adjust_paritybit (inst_main.instruction, s3_GET_INSN_CLASS (inst_main.type));
+ inst_main.type = Insn_PIC;
+
+ for (i = 0; i < var_num; i++)
+ {
+ inst_main.relax_size += var_insts[i].size;
+ var_insts[i].instruction = s3_adjust_paritybit (var_insts[i].instruction,
+ s3_GET_INSN_CLASS (var_insts[i].type));
+ }
+
+ /* Check data dependency. */
+ s3_handle_dependency (&inst_main);
+
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ {
+ frag_wane (frag_now);
+ }
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ /* Write fr_fix part. */
+ p = frag_more (inst_main.size);
+ s3_md_number_to_chars (p, inst_main.instruction, inst_main.size);
+
+ if (inst_main.reloc.type != BFD_RELOC_NONE)
+ fixp = s3_fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size,
+ &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type);
+
+ frag_now->tc_frag_data.fixp = fixp;
+ cur_fixp = frag_now->tc_frag_data.fixp;
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst_main.size);
+#endif
+
+ where = p - frag_now->fr_literal + inst_main.size;
+ for (i = 0; i < var_num; i++)
+ {
+ if (i > 0)
+ where += var_insts[i - 1].size;
+
+ if (var_insts[i].reloc.type != BFD_RELOC_NONE)
+ {
+ fixp = s3_fix_new_score (frag_now, where, var_insts[i].size,
+ &var_insts[i].reloc.exp, var_insts[i].reloc.pc_rel,
+ var_insts[i].reloc.type);
+ if (fixp)
+ {
+ if (cur_fixp)
+ {
+ cur_fixp->fx_next = fixp;
+ cur_fixp = cur_fixp->fx_next;
+ }
+ else
+ {
+ frag_now->tc_frag_data.fixp = fixp;
+ cur_fixp = frag_now->tc_frag_data.fixp;
+ }
+ }
+ }
+ }
+
+ p = frag_var (rs_machine_dependent, inst_main.relax_size + s3_RELAX_PAD_BYTE, 0,
+ s3_RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type,
+ 0, inst_main.size, 0), add_symbol, 0, NULL);
+
+ /* Write fr_var part.
+ no calling s3_gen_insn_frag, no fixS will be generated. */
+ for (i = 0; i < var_num; i++)
+ {
+ s3_md_number_to_chars (p, var_insts[i].instruction, var_insts[i].size);
+ p += var_insts[i].size;
+ }
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+}
+
+/* Build a relax frag for la instruction when generating s3_PIC,
+ external symbol first and local symbol second. */
+static void
+s3_build_la_pic (int reg_rd, expressionS exp)
+{
+ symbolS *add_symbol = exp.X_add_symbol;
+ offsetT add_number = exp.X_add_number;
+ struct s3_score_it fix_insts[s3_RELAX_INST_NUM];
+ struct s3_score_it var_insts[s3_RELAX_INST_NUM];
+ int fix_num = 0;
+ int var_num = 0;
+ char tmp[s3_MAX_LITERAL_POOL_SIZE];
+ int r1_bak;
+
+ r1_bak = s3_nor1;
+ s3_nor1 = 0;
+
+ if (add_number == 0)
+ {
+ fix_num = 1;
+ var_num = 2;
+
+ /* For an external symbol, only one insn is generated;
+ For a local symbol, two insns are generated. */
+ /* Fix part
+ For an external symbol: lw rD, <sym>($gp)
+ (BFD_RELOC_SCORE_GOT15 or BFD_RELOC_SCORE_CALL15) */
+ sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ if (reg_rd == s3_PIC_CALL_REG)
+ s3_inst.reloc.type = BFD_RELOC_SCORE_CALL15;
+ memcpy (&fix_insts[0], &s3_inst, sizeof (struct s3_score_it));
+
+ /* Var part
+ For a local symbol :
+ lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15)
+ addi rD, <sym> (BFD_RELOC_GOT_LO16) */
+ s3_inst.reloc.type = BFD_RELOC_SCORE_GOT15;
+ memcpy (&var_insts[0], &s3_inst, sizeof (struct s3_score_it));
+ sprintf (tmp, "addi_s_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&var_insts[1], &s3_inst, sizeof (struct s3_score_it));
+ s3_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+ }
+ else if (add_number >= -0x8000 && add_number <= 0x7fff)
+ {
+ /* Insn 1: lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) */
+ sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s3_append_insn (tmp, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Insn 2 */
+ fix_num = 1;
+ var_num = 1;
+ /* Fix part
+ For an external symbol: addi rD, <constant> */
+ sprintf (tmp, "addi r%d, %d", reg_rd, (int)add_number);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s3_inst, sizeof (struct s3_score_it));
+
+ /* Var part
+ For a local symbol: addi rD, <sym>+<constant> (BFD_RELOC_GOT_LO16) */
+ sprintf (tmp, "addi_s_pic r%d, %s + %d", reg_rd, add_symbol->bsym->name, (int)add_number);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&var_insts[0], &s3_inst, sizeof (struct s3_score_it));
+ s3_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+ }
+ else
+ {
+ int hi = (add_number >> 16) & 0x0000FFFF;
+ int lo = add_number & 0x0000FFFF;
+
+ /* Insn 1: lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) */
+ sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s3_append_insn (tmp, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Insn 2 */
+ fix_num = 1;
+ var_num = 1;
+ /* Fix part
+ For an external symbol: ldis r1, HI%<constant> */
+ sprintf (tmp, "ldis r1, %d", hi);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s3_inst, sizeof (struct s3_score_it));
+
+ /* Var part
+ For a local symbol: ldis r1, HI%<constant>
+ but, if lo is outof 16 bit, make hi plus 1 */
+ if ((lo < -0x8000) || (lo > 0x7fff))
+ {
+ hi += 1;
+ }
+ sprintf (tmp, "ldis_pic r1, %d", hi);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&var_insts[0], &s3_inst, sizeof (struct s3_score_it));
+ s3_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+
+ /* Insn 3 */
+ fix_num = 1;
+ var_num = 1;
+ /* Fix part
+ For an external symbol: ori r1, LO%<constant> */
+ sprintf (tmp, "ori r1, %d", lo);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s3_inst, sizeof (struct s3_score_it));
+
+ /* Var part
+ For a local symbol: addi r1, <sym>+LO%<constant> (BFD_RELOC_GOT_LO16) */
+ sprintf (tmp, "addi_u_pic r1, %s + %d", add_symbol->bsym->name, lo);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&var_insts[0], &s3_inst, sizeof (struct s3_score_it));
+ s3_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+
+ /* Insn 4: add rD, rD, r1 */
+ sprintf (tmp, "add r%d, r%d, r1", reg_rd, reg_rd);
+ if (s3_append_insn (tmp, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+
+ s3_nor1 = r1_bak;
+}
+
+/* Handle la. */
+static void
+s3_do_macro_la_rdi32 (char *str)
+{
+ int reg_rd;
+
+ s3_skip_whitespace (str);
+ if ((reg_rd = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ /* Save str. */
+ char *keep_data = str;
+ char append_str[s3_MAX_LITERAL_POOL_SIZE];
+
+ /* Check immediate value. */
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL)
+ {
+ s3_inst.error = _("expression error");
+ return;
+ }
+ else if ((s3_inst.reloc.exp.X_add_symbol == NULL)
+ && (s3_validate_immediate (s3_inst.reloc.exp.X_add_number, _IMM32, 0) == (int) s3_FAIL))
+ {
+ s3_inst.error = _("value not in range [0, 0xffffffff]");
+ return;
+ }
+
+ /* Reset str. */
+ str = keep_data;
+
+ /* la rd, simm16. */
+ if (s3_data_op2 (&str, 1, _SIMM16_LA) != (int) s3_FAIL)
+ {
+ s3_end_of_line (str);
+ return;
+ }
+ /* la rd, imm32 or la rd, label. */
+ else
+ {
+ s3_SET_INSN_ERROR (NULL);
+ /* Reset str. */
+ str = keep_data;
+ if ((s3_data_op2 (&str, 1, _VALUE_HI16) == (int) s3_FAIL)
+ || (s3_end_of_line (str) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ if ((s3_score_pic == s3_NO_PIC) || (!s3_inst.reloc.exp.X_add_symbol))
+ {
+ sprintf (append_str, "ld_i32hi r%d, %s", reg_rd, keep_data);
+ if (s3_append_insn (append_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ sprintf (append_str, "ld_i32lo r%d, %s", reg_rd, keep_data);
+ if (s3_append_insn (append_str, TRUE) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ gas_assert (s3_inst.reloc.exp.X_add_symbol);
+ s3_build_la_pic (reg_rd, s3_inst.reloc.exp);
+ }
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+ }
+ }
+}
+
+/* Handle li. */
+static void
+s3_do_macro_li_rdi32 (char *str)
+{
+
+ int reg_rd;
+
+ s3_skip_whitespace (str);
+ if ((reg_rd = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ /* Save str. */
+ char *keep_data = str;
+
+ /* Check immediate value. */
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL)
+ {
+ s3_inst.error = _("expression error");
+ return;
+ }
+ else if (!(s3_inst.reloc.exp.X_add_number >= -0xffffffffLL
+ && s3_inst.reloc.exp.X_add_number <= 0xffffffffLL))
+ {
+ s3_inst.error = _("value not in range [-0xffffffff, 0xffffffff]");
+ return;
+ }
+
+ /* Reset str. */
+ str = keep_data;
+
+ /* li rd, simm16. */
+ if (s3_data_op2 (&str, 1, _SIMM16_LA) != (int) s3_FAIL)
+ {
+ s3_end_of_line (str);
+ return;
+ }
+ /* li rd, imm32. */
+ else
+ {
+ char append_str[s3_MAX_LITERAL_POOL_SIZE];
+
+ /* Reset str. */
+ str = keep_data;
+
+ if ((s3_data_op2 (&str, 1, _VALUE_HI16) == (int) s3_FAIL)
+ || (s3_end_of_line (str) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else if (s3_inst.reloc.exp.X_add_symbol)
+ {
+ s3_inst.error = _("li rd label isn't correct instruction form");
+ return;
+ }
+ else
+ {
+ sprintf (append_str, "ld_i32hi r%d, %s", reg_rd, keep_data);
+
+ if (s3_append_insn (append_str, TRUE) == (int) s3_FAIL)
+ return;
+ else
+ {
+ sprintf (append_str, "ld_i32lo r%d, %s", reg_rd, keep_data);
+ if (s3_append_insn (append_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+ }
+ }
+ }
+}
+
+/* Handle mul/mulu/div/divu/rem/remu. */
+static void
+s3_do_macro_mul_rdrsrs (char *str)
+{
+ int reg_rd;
+ int reg_rs1;
+ int reg_rs2;
+ char *backupstr;
+ char append_str[s3_MAX_LITERAL_POOL_SIZE];
+
+ if (s3_university_version == 1)
+ as_warn ("%s", s3_ERR_FOR_SCORE5U_MUL_DIV);
+
+ strcpy (append_str, str);
+ backupstr = append_str;
+ s3_skip_whitespace (backupstr);
+ if (((reg_rd = s3_reg_required_here (&backupstr, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&backupstr) == (int) s3_FAIL)
+ || ((reg_rs1 = s3_reg_required_here (&backupstr, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL))
+ {
+ s3_inst.error = s3_BAD_ARGS;
+ return;
+ }
+
+ if (s3_skip_past_comma (&backupstr) == (int) s3_FAIL)
+ {
+ /* rem/remu rA, rB is error format. */
+ if (strcmp (s3_inst.name, "rem") == 0 || strcmp (s3_inst.name, "remu") == 0)
+ {
+ s3_SET_INSN_ERROR (s3_BAD_ARGS);
+ }
+ else
+ {
+ s3_SET_INSN_ERROR (NULL);
+ s3_do_rsrs (str);
+ }
+ return;
+ }
+ else
+ {
+ s3_SET_INSN_ERROR (NULL);
+ if (((reg_rs2 = s3_reg_required_here (&backupstr, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ || (s3_end_of_line (backupstr) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ char append_str1[s3_MAX_LITERAL_POOL_SIZE];
+
+ if (strcmp (s3_inst.name, "rem") == 0)
+ {
+ sprintf (append_str, "mul r%d, r%d", reg_rs1, reg_rs2);
+ sprintf (append_str1, "mfceh r%d", reg_rd);
+ }
+ else if (strcmp (s3_inst.name, "remu") == 0)
+ {
+ sprintf (append_str, "mulu r%d, r%d", reg_rs1, reg_rs2);
+ sprintf (append_str1, "mfceh r%d", reg_rd);
+ }
+ else
+ {
+ sprintf (append_str, "%s r%d, r%d", s3_inst.name, reg_rs1, reg_rs2);
+ sprintf (append_str1, "mfcel r%d", reg_rd);
+ }
+
+ /* Output mul/mulu or div/divu or rem/remu. */
+ if (s3_append_insn (append_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Output mfcel or mfceh. */
+ if (s3_append_insn (append_str1, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+ }
+}
+
+static void
+s3_exp_macro_ldst_abs (char *str)
+{
+ int reg_rd;
+ char *backupstr, *tmp;
+ char append_str[s3_MAX_LITERAL_POOL_SIZE];
+ char verifystr[s3_MAX_LITERAL_POOL_SIZE];
+ struct s3_score_it inst_backup;
+ int r1_bak = 0;
+
+ r1_bak = s3_nor1;
+ s3_nor1 = 0;
+ memcpy (&inst_backup, &s3_inst, sizeof (struct s3_score_it));
+
+ strcpy (verifystr, str);
+ backupstr = verifystr;
+ s3_skip_whitespace (backupstr);
+ if ((reg_rd = s3_reg_required_here (&backupstr, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ return;
+
+ tmp = backupstr;
+ if (s3_skip_past_comma (&backupstr) == (int) s3_FAIL)
+ return;
+
+ backupstr = tmp;
+ sprintf (append_str, "li r1 %s", backupstr);
+ s3_append_insn (append_str, TRUE);
+
+ memcpy (&s3_inst, &inst_backup, sizeof (struct s3_score_it));
+ sprintf (append_str, " r%d, [r1,0]", reg_rd);
+ s3_do_ldst_insn (append_str);
+
+ s3_nor1 = r1_bak;
+}
+
+/* Handle bcmpeq / bcmpne */
+static void
+s3_do_macro_bcmp (char *str)
+{
+ int reg_a , reg_b;
+ char keep_data[s3_MAX_LITERAL_POOL_SIZE];
+ char* ptemp;
+ int i = 0;
+ struct s3_score_it inst_expand[2];
+ struct s3_score_it inst_main;
+
+ memset (inst_expand, 0, sizeof inst_expand);
+ s3_skip_whitespace (str);
+ if (( reg_a = s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ ||(reg_b = s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ return;
+ ptemp = str;
+ while (*ptemp != 0)
+ {
+ keep_data[i] = *ptemp;
+ i++;
+ ptemp++;
+ }
+ keep_data[i] = 0;
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ ||reg_b == 0
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ else if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s3_inst.error = _("lacking label ");
+ return;
+ }
+ else
+ {
+ char append_str[s3_MAX_LITERAL_POOL_SIZE];
+ s3_SET_INSN_ERROR (NULL);
+
+ s3_inst.reloc.type = BFD_RELOC_SCORE_BCMP;
+ s3_inst.reloc.pc_rel = 1;
+ bfd_signed_vma val = s3_inst.reloc.exp.X_add_number;
+
+ /* Branch 32 offset field : 20 bit, 16 bit branch offset field : 8 bit. */
+ s3_inst.instruction |= ((s3_inst.reloc.exp.X_add_number >> 1) & 0x1)
+ | ((s3_inst.reloc.exp.X_add_number >> 2) & 0x7) << 7
+ | ((s3_inst.reloc.exp.X_add_number >> 5) & 0x1f) << 20;
+
+ /* Check and set offset. */
+ if (((val & 0xfffffe00) != 0)
+ && ((val & 0xfffffe00) != 0xfffffe00))
+ {
+ /* support bcmp --> cmp!+beq (bne) */
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ sprintf (&append_str[0], "cmp! r%d, r%d", reg_a, reg_b);
+ if (s3_append_insn (&append_str[0], TRUE) == (int) s3_FAIL)
+ return;
+ if ((inst_main.instruction & 0x3e00007e) == 0x0000004c)
+ sprintf (&append_str[1], "beq %s", keep_data);
+ else
+ sprintf (&append_str[1], "bne %s", keep_data);
+ if (s3_append_insn (&append_str[1], TRUE) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ gas_assert (s3_inst.reloc.exp.X_add_symbol);
+ }
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ return;
+ }
+ else
+ {
+ val >>= 1;
+ s3_inst.instruction |= (val & 0x1)
+ | (((val >> 1) & 0x7) << 7)
+ | (((val >> 4) & 0x1f) << 20);
+ }
+
+ /* Backup s3_inst. */
+ memcpy (&inst_main, &s3_inst, sizeof (struct s3_score_it));
+
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ sprintf (&append_str[0], "cmp! r%d, r%d", reg_a, reg_b);
+ if (s3_append_insn (&append_str[0], FALSE) == (int) s3_FAIL)
+ return;
+ memcpy (&inst_expand[0], &s3_inst, sizeof (struct s3_score_it));
+
+ if ((inst_main.instruction & 0x3e00007e) == 0x0000004c)
+ sprintf (&append_str[1], "beq %s", keep_data);
+ else
+ sprintf (&append_str[1], "bne %s", keep_data);
+ if (s3_append_insn (&append_str[1], FALSE) == (int) s3_FAIL)
+ return;
+ memcpy (&inst_expand[1], &s3_inst, sizeof (struct s3_score_it));
+ }
+ else
+ {
+ gas_assert (s3_inst.reloc.exp.X_add_symbol);
+ }
+ inst_main.relax_size = inst_expand[0].size + inst_expand[1].size;
+ inst_main.type = Insn_BCMP;
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ inst_main.instruction = s3_adjust_paritybit (inst_main.instruction, s3_GET_INSN_CLASS (inst_main.type));
+
+ for (i = 0; i < 2; i++)
+ inst_expand[i].instruction = s3_adjust_paritybit (inst_expand[i].instruction,
+ s3_GET_INSN_CLASS (inst_expand[i].type));
+ /* Check data dependency. */
+ s3_handle_dependency (&inst_main);
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ /* Write fr_fix part. */
+ char *p;
+ p = frag_more (inst_main.size);
+ s3_md_number_to_chars (p, inst_main.instruction, inst_main.size);
+
+ if (inst_main.reloc.type != BFD_RELOC_NONE)
+ {
+ s3_fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size,
+ &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type);
+ }
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst_main.size);
+#endif
+
+ /* s3_GP instruction can not do optimization, only can do relax between
+ 1 instruction and 3 instructions. */
+ p = frag_var (rs_machine_dependent, inst_main.relax_size + s3_RELAX_PAD_BYTE, 0,
+ s3_RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type, 0, 4, 1),
+ inst_main.reloc.exp.X_add_symbol, 0, NULL);
+
+ /* Write fr_var part.
+ no calling s3_gen_insn_frag, no fixS will be generated. */
+ s3_md_number_to_chars (p, inst_expand[0].instruction, inst_expand[0].size);
+ p += inst_expand[0].size;
+ s3_md_number_to_chars (p, inst_expand[1].instruction, inst_expand[1].size);
+ p += inst_expand[1].size;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+}
+
+/* Handle bcmpeqz / bcmpnez */
+static void
+s3_do_macro_bcmpz (char *str)
+{
+ int reg_a;
+ char keep_data[s3_MAX_LITERAL_POOL_SIZE];
+ char* ptemp;
+ int i = 0;
+ struct s3_score_it inst_expand[2];
+ struct s3_score_it inst_main;
+
+ memset (inst_expand, 0, sizeof inst_expand);
+ s3_skip_whitespace (str);
+ if (( reg_a = s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL)
+ return;
+ ptemp = str;
+ while (*ptemp != 0)
+ {
+ keep_data[i] = *ptemp;
+ i++;
+ ptemp++;
+ }
+
+ keep_data[i] = 0;
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+ else if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s3_inst.error = _("lacking label ");
+ return;
+ }
+ else
+ {
+ char append_str[s3_MAX_LITERAL_POOL_SIZE];
+ s3_SET_INSN_ERROR (NULL);
+ s3_inst.reloc.type = BFD_RELOC_SCORE_BCMP;
+ s3_inst.reloc.pc_rel = 1;
+ bfd_signed_vma val = s3_inst.reloc.exp.X_add_number;
+
+ /* Branch 32 offset field : 20 bit, 16 bit branch offset field : 8 bit. */
+ s3_inst.instruction |= ((s3_inst.reloc.exp.X_add_number>>1) & 0x1) | ((s3_inst.reloc.exp.X_add_number>>2) & 0x7)<<7 |((s3_inst.reloc.exp.X_add_number>>5) & 0x1f)<<20;
+
+ /* Check and set offset. */
+ if (((val & 0xfffffe00) != 0)
+ && ((val & 0xfffffe00) != 0xfffffe00))
+ {
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ sprintf (&append_str[0], "cmpi! r%d,0", reg_a);
+ if (s3_append_insn (&append_str[0], TRUE) == (int) s3_FAIL)
+ return;
+ if ((inst_main.instruction & 0x3e00007e) == 0x0000004c)
+ sprintf (&append_str[1], "beq %s", keep_data);
+ else
+ sprintf (&append_str[1], "bne %s", keep_data);
+ if (s3_append_insn (&append_str[1], TRUE) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ gas_assert (s3_inst.reloc.exp.X_add_symbol);
+ }
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ return;
+ }
+ else
+ {
+ val >>= 1;
+ s3_inst.instruction |= (val & 0x1)
+ | (((val >> 1) & 0x7) << 7)
+ | (((val >> 4) & 0x1f) << 20);
+ }
+
+ /* Backup s3_inst. */
+ memcpy (&inst_main, &s3_inst, sizeof (struct s3_score_it));
+
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ sprintf (&append_str[0], "cmpi! r%d, 0", reg_a);
+ if (s3_append_insn (&append_str[0], FALSE) == (int) s3_FAIL)
+ return;
+ memcpy (&inst_expand[0], &s3_inst, sizeof (struct s3_score_it));
+ if ((inst_main.instruction & 0x3e00007e) == 0x0000004c)
+ sprintf (&append_str[1], "beq %s", keep_data);
+ else
+ sprintf (&append_str[1], "bne %s", keep_data);
+ if (s3_append_insn (&append_str[1], FALSE) == (int) s3_FAIL)
+ return;
+ memcpy (&inst_expand[1], &s3_inst, sizeof (struct s3_score_it));
+ }
+ else
+ {
+ gas_assert (s3_inst.reloc.exp.X_add_symbol);
+ }
+ inst_main.relax_size = inst_expand[0].size + inst_expand[1].size;
+ inst_main.type = Insn_BCMP;
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ inst_main.instruction = s3_adjust_paritybit (inst_main.instruction, s3_GET_INSN_CLASS (inst_main.type));
+
+ for (i = 0; i < 2; i++)
+ inst_expand[i].instruction = s3_adjust_paritybit (inst_expand[i].instruction ,
+ s3_GET_INSN_CLASS (inst_expand[i].type));
+ /* Check data dependency. */
+ s3_handle_dependency (&inst_main);
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ /* Write fr_fix part. */
+ char *p;
+ p = frag_more (inst_main.size);
+ s3_md_number_to_chars (p, inst_main.instruction, inst_main.size);
+
+ if (inst_main.reloc.type != BFD_RELOC_NONE)
+ {
+ s3_fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size,
+ &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type);
+ }
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst_main.size);
+#endif
+
+ /* s3_GP instruction can not do optimization, only can do relax between
+ 1 instruction and 3 instructions. */
+ p = frag_var (rs_machine_dependent, inst_main.relax_size + s3_RELAX_PAD_BYTE, 0,
+ s3_RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type, 0, 4, 1),
+ inst_main.reloc.exp.X_add_symbol, 0, NULL);
+
+ /* Write fr_var part.
+ no calling s3_gen_insn_frag, no fixS will be generated. */
+ s3_md_number_to_chars (p, inst_expand[0].instruction, inst_expand[0].size);
+ p += inst_expand[0].size;
+ s3_md_number_to_chars (p, inst_expand[1].instruction, inst_expand[1].size);
+ p += inst_expand[1].size;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+}
+
+static int
+s3_nopic_need_relax (symbolS * sym, int before_relaxing)
+{
+ if (sym == NULL)
+ return 0;
+ else if (s3_USE_GLOBAL_POINTER_OPT && s3_g_switch_value > 0)
+ {
+ const char *symname;
+ const char *segname;
+
+ /* Find out whether this symbol can be referenced off the $gp
+ register. It can be if it is smaller than the -G size or if
+ it is in the .sdata or .sbss section. Certain symbols can
+ not be referenced off the $gp, although it appears as though
+ they can. */
+ symname = S_GET_NAME (sym);
+ if (symname != (const char *)NULL
+ && (strcmp (symname, "eprol") == 0
+ || strcmp (symname, "etext") == 0
+ || strcmp (symname, "_gp") == 0
+ || strcmp (symname, "edata") == 0
+ || strcmp (symname, "_fbss") == 0
+ || strcmp (symname, "_fdata") == 0
+ || strcmp (symname, "_ftext") == 0
+ || strcmp (symname, "end") == 0
+ || strcmp (symname, GP_DISP_LABEL) == 0))
+ {
+ return 1;
+ }
+ else if ((!S_IS_DEFINED (sym) || S_IS_COMMON (sym)) && (0
+ /* We must defer this decision until after the whole file has been read,
+ since there might be a .extern after the first use of this symbol. */
+ || (before_relaxing
+ && S_GET_VALUE (sym) == 0)
+ || (S_GET_VALUE (sym) != 0
+ && S_GET_VALUE (sym) <= s3_g_switch_value)))
+ {
+ return 0;
+ }
+
+ segname = segment_name (S_GET_SEGMENT (sym));
+ return (strcmp (segname, ".sdata") != 0
+ && strcmp (segname, ".sbss") != 0
+ && strncmp (segname, ".sdata.", 7) != 0
+ && strncmp (segname, ".gnu.linkonce.s.", 16) != 0);
+ }
+ /* We are not optimizing for the $gp register. */
+ else
+ return 1;
+}
+
+/* Build a relax frag for lw/st instruction when generating s3_PIC,
+ external symbol first and local symbol second. */
+static void
+s3_build_lwst_pic (int reg_rd, expressionS exp, const char *insn_name)
+{
+ symbolS *add_symbol = exp.X_add_symbol;
+ int add_number = exp.X_add_number;
+ struct s3_score_it fix_insts[s3_RELAX_INST_NUM];
+ struct s3_score_it var_insts[s3_RELAX_INST_NUM];
+ int fix_num = 0;
+ int var_num = 0;
+ char tmp[s3_MAX_LITERAL_POOL_SIZE];
+ int r1_bak;
+
+ r1_bak = s3_nor1;
+ s3_nor1 = 0;
+
+ if ((add_number == 0) || (add_number >= -0x8000 && add_number <= 0x7fff))
+ {
+ fix_num = 1;
+ var_num = 2;
+
+ /* For an external symbol, two insns are generated;
+ For a local symbol, three insns are generated. */
+ /* Fix part
+ For an external symbol: lw rD, <sym>($gp)
+ (BFD_RELOC_SCORE_GOT15) */
+ sprintf (tmp, "lw_pic r1, %s", add_symbol->bsym->name);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s3_inst, sizeof (struct s3_score_it));
+
+ /* Var part
+ For a local symbol :
+ lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15)
+ addi rD, <sym> (BFD_RELOC_GOT_LO16) */
+ s3_inst.reloc.type = BFD_RELOC_SCORE_GOT15;
+ memcpy (&var_insts[0], &s3_inst, sizeof (struct s3_score_it));
+ sprintf (tmp, "addi_s_pic r1, %s", add_symbol->bsym->name);
+ if (s3_append_insn (tmp, FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&var_insts[1], &s3_inst, sizeof (struct s3_score_it));
+ s3_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+
+ /* Insn 2 or Insn 3: lw/st rD, [r1, constant] */
+ sprintf (tmp, "%s r%d, [r1, %d]", insn_name, reg_rd, add_number);
+ if (s3_append_insn (tmp, TRUE) == (int) s3_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+ }
+ else
+ {
+ s3_inst.error = _("s3_PIC code offset overflow (max 16 signed bits)");
+ return;
+ }
+
+ s3_nor1 = r1_bak;
+}
+
+static void
+s3_do_macro_ldst_label (char *str)
+{
+ int i;
+ int ldst_gp_p = 0;
+ int reg_rd;
+ int r1_bak;
+ char *backup_str;
+ char *label_str;
+ char *absolute_value;
+ char append_str[3][s3_MAX_LITERAL_POOL_SIZE];
+ char verifystr[s3_MAX_LITERAL_POOL_SIZE];
+ struct s3_score_it inst_backup;
+ struct s3_score_it inst_expand[3];
+ struct s3_score_it inst_main;
+
+ memcpy (&inst_backup, &s3_inst, sizeof (struct s3_score_it));
+ strcpy (verifystr, str);
+ backup_str = verifystr;
+
+ s3_skip_whitespace (backup_str);
+ if ((reg_rd = s3_reg_required_here (&backup_str, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ return;
+
+ if (s3_skip_past_comma (&backup_str) == (int) s3_FAIL)
+ return;
+
+ label_str = backup_str;
+
+ /* Ld/st rD, [rA, imm] ld/st rD, [rA]+, imm ld/st rD, [rA, imm]+. */
+ if (*backup_str == '[')
+ {
+ s3_inst.type = Rd_rvalueRs_preSI12;
+ s3_do_ldst_insn (str);
+ return;
+ }
+
+ /* Ld/st rD, imm. */
+ absolute_value = backup_str;
+ s3_inst.type = Rd_rvalueRs_SI15;
+
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &backup_str) == (int) s3_FAIL)
+ {
+ s3_inst.error = _("expression error");
+ return;
+ }
+ else if ((s3_inst.reloc.exp.X_add_symbol == NULL)
+ && (s3_validate_immediate (s3_inst.reloc.exp.X_add_number, _VALUE, 0) == (int) s3_FAIL))
+ {
+ s3_inst.error = _("value not in range [0, 0x7fffffff]");
+ return;
+ }
+ else if (s3_end_of_line (backup_str) == (int) s3_FAIL)
+ {
+ s3_inst.error = _("end on line error");
+ return;
+ }
+ else
+ {
+ if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ memcpy (&s3_inst, &inst_backup, sizeof (struct s3_score_it));
+ s3_exp_macro_ldst_abs (str);
+ return;
+ }
+ }
+
+ /* Ld/st rD, label. */
+ s3_inst.type = Rd_rvalueRs_SI15;
+ backup_str = absolute_value;
+ if ((s3_data_op2 (&backup_str, 1, _GP_IMM15) == (int) s3_FAIL)
+ || (s3_end_of_line (backup_str) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ if (!s3_inst.error)
+ s3_inst.error = s3_BAD_ARGS;
+
+ return;
+ }
+
+ if (s3_score_pic == s3_PIC)
+ {
+ int ldst_idx = 0;
+ ldst_idx = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s3_build_lwst_pic (reg_rd, s3_inst.reloc.exp,
+ s3_score_ldst_insns[ldst_idx * 3 + 0].template_name);
+ return;
+ }
+ else
+ {
+ if ((s3_inst.reloc.exp.X_add_number <= 0x3fff)
+ && (s3_inst.reloc.exp.X_add_number >= -0x4000)
+ && (!s3_nopic_need_relax (s3_inst.reloc.exp.X_add_symbol, 1)))
+ {
+ int ldst_idx = 0;
+
+ /* Assign the real opcode. */
+ ldst_idx = s3_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s3_inst.instruction |= s3_score_ldst_insns[ldst_idx * 3 + 0].value;
+ s3_inst.instruction |= reg_rd << 20;
+ s3_inst.instruction |= s3_GP << 15;
+ s3_inst.relax_inst = 0x8000;
+ s3_inst.relax_size = 0;
+ ldst_gp_p = 1;
+ }
+ }
+ }
+
+ /* Backup s3_inst. */
+ memcpy (&inst_main, &s3_inst, sizeof (struct s3_score_it));
+ r1_bak = s3_nor1;
+ s3_nor1 = 0;
+
+ /* Determine which instructions should be output. */
+ sprintf (append_str[0], "ld_i32hi r1, %s", label_str);
+ sprintf (append_str[1], "ld_i32lo r1, %s", label_str);
+ sprintf (append_str[2], "%s r%d, [r1, 0]", inst_backup.name, reg_rd);
+
+ /* Generate three instructions.
+ la r1, label
+ ld/st rd, [r1, 0] */
+ for (i = 0; i < 3; i++)
+ {
+ if (s3_append_insn (append_str[i], FALSE) == (int) s3_FAIL)
+ return;
+
+ memcpy (&inst_expand[i], &s3_inst, sizeof (struct s3_score_it));
+ }
+
+ if (ldst_gp_p)
+ {
+ char *p;
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ inst_main.instruction = s3_adjust_paritybit (inst_main.instruction, s3_GET_INSN_CLASS (inst_main.type));
+
+ /* relax lw rd, label -> ldis rs, imm16
+ ori rd, imm16
+ lw rd, [rs, imm15] or lw! rd, [rs, imm5]. */
+ if (inst_expand[2].relax_size == 0)
+ inst_main.relax_size = inst_expand[0].size + inst_expand[1].size + inst_expand[2].size;
+ else
+ inst_main.relax_size = inst_expand[0].size + inst_expand[1].size + inst_expand[2].relax_size;
+
+ inst_main.type = Insn_GP;
+
+ for (i = 0; i < 3; i++)
+ inst_expand[i].instruction = s3_adjust_paritybit (inst_expand[i].instruction,
+ s3_GET_INSN_CLASS (inst_expand[i].type));
+
+ /* Check data dependency. */
+ s3_handle_dependency (&inst_main);
+
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ /* Write fr_fix part. */
+ p = frag_more (inst_main.size);
+ s3_md_number_to_chars (p, inst_main.instruction, inst_main.size);
+
+ if (inst_main.reloc.type != BFD_RELOC_NONE)
+ {
+ s3_fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size,
+ &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type);
+ }
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst_main.size);
+#endif
+
+ /* s3_GP instruction can not do optimization, only can do relax between
+ 1 instruction and 3 instructions. */
+ p = frag_var (rs_machine_dependent, inst_main.relax_size + s3_RELAX_PAD_BYTE, 0,
+ s3_RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type, 0, 4, 0),
+ inst_main.reloc.exp.X_add_symbol, 0, NULL);
+
+ /* Write fr_var part.
+ no calling s3_gen_insn_frag, no fixS will be generated. */
+ s3_md_number_to_chars (p, inst_expand[0].instruction, inst_expand[0].size);
+ p += inst_expand[0].size;
+ s3_md_number_to_chars (p, inst_expand[1].instruction, inst_expand[1].size);
+ p += inst_expand[1].size;
+
+ /* relax lw rd, label -> ldis rs, imm16
+ ori rd, imm16
+ lw rd, [rs, imm15] or lw! rd, [rs, imm5]. */
+ if (inst_expand[2].relax_size == 0)
+ s3_md_number_to_chars (p, inst_expand[2].instruction, inst_expand[2].size);
+ else
+ s3_md_number_to_chars (p, inst_expand[2].relax_inst, inst_expand[2].relax_size);
+ }
+ else
+ {
+ s3_gen_insn_frag (&inst_expand[0], NULL);
+ s3_gen_insn_frag (&inst_expand[1], NULL);
+ s3_gen_insn_frag (&inst_expand[2], NULL);
+ }
+ s3_nor1 = r1_bak;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s3_inst.bwarn = -1;
+}
+
+static void
+s3_do_lw_pic (char *str)
+{
+ int reg_rd;
+
+ s3_skip_whitespace (str);
+ if (((reg_rd = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ || (s3_skip_past_comma (&str) == (int) s3_FAIL)
+ || (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL)
+ || (s3_end_of_line (str) == (int) s3_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ if (!s3_inst.error)
+ s3_inst.error = s3_BAD_ARGS;
+
+ return;
+ }
+
+ s3_inst.instruction |= s3_GP << 15;
+ s3_inst.reloc.type = BFD_RELOC_SCORE_GOT15;
+ }
+}
+
+static void
+s3_do_empty (char *str)
+{
+ str = str;
+ if (s3_university_version == 1)
+ {
+ if (((s3_inst.instruction & 0x3e0003ff) == 0x0c000004)
+ || ((s3_inst.instruction & 0x3e0003ff) == 0x0c000024)
+ || ((s3_inst.instruction & 0x3e0003ff) == 0x0c000044)
+ || ((s3_inst.instruction & 0x3e0003ff) == 0x0c000064))
+ {
+ s3_inst.error = s3_ERR_FOR_SCORE5U_MMU;
+ return;
+ }
+ }
+ if (s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if (s3_inst.relax_inst != 0x8000)
+ {
+ if (s3_inst.type == NO_OPD)
+ {
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_size = 4;
+ }
+ }
+}
+
+static void
+s3_do16_int (char *str)
+{
+ s3_skip_whitespace (str);
+ return;
+}
+
+static void
+s3_do_jump (char *str)
+{
+ char *save_in;
+
+ s3_skip_whitespace (str);
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s3_inst.error = _("lacking label ");
+ return;
+ }
+
+ if (!(s3_inst.reloc.exp.X_add_number >= -16777216
+ && s3_inst.reloc.exp.X_add_number <= 16777215))
+ {
+ s3_inst.error = _("invalid constant: 25 bit expression not in range [-16777216, 16777215]");
+ return;
+ }
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ s3_inst.reloc.type = BFD_RELOC_SCORE_JMP;
+ s3_inst.reloc.pc_rel = 1;
+ input_line_pointer = save_in;
+}
+
+static void
+s3_do_branch (char *str)
+{
+ if (s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s3_inst.error = _("lacking label ");
+ return;
+ }
+ else if (!(s3_inst.reloc.exp.X_add_number >= -524288
+ && s3_inst.reloc.exp.X_add_number <= 524287))
+ {
+ s3_inst.error = _("invalid constant: 20 bit expression not in range -2^19..2^19");
+ return;
+ }
+
+ s3_inst.reloc.type = BFD_RELOC_SCORE_BRANCH;
+ s3_inst.reloc.pc_rel = 1;
+
+ /* Branch 32 offset field : 20 bit, 16 bit branch offset field : 8 bit. */
+ s3_inst.instruction |= (s3_inst.reloc.exp.X_add_number & 0x3fe) | ((s3_inst.reloc.exp.X_add_number & 0xffc00) << 5);
+
+ /* Compute 16 bit branch instruction. */
+ if ((s3_inst.relax_inst != 0x8000)
+ && (s3_inst.reloc.exp.X_add_number >= -512 && s3_inst.reloc.exp.X_add_number <= 511))
+ {
+ s3_inst.relax_inst |= ((s3_inst.reloc.exp.X_add_number >> 1) & 0x1ff);/*b! :disp 9 bit */
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+}
+
+static void
+s3_do16_branch (char *str)
+{
+ if ((s3_my_get_expression (&s3_inst.reloc.exp, &str) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL))
+ {
+ ;
+ }
+ else if (s3_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s3_inst.error = _("lacking label");
+ }
+ else if (!(s3_inst.reloc.exp.X_add_number >= -512
+ && s3_inst.reloc.exp.X_add_number <= 511))
+ {
+ s3_inst.error = _("invalid constant: 10 bit expression not in range [-2^9, 2^9-1]");
+ }
+ else
+ {
+ s3_inst.reloc.type = BFD_RELOC_SCORE16_BRANCH;
+ s3_inst.reloc.pc_rel = 1;
+ s3_inst.instruction |= ((s3_inst.reloc.exp.X_add_number >> 1) & 0x1ff);
+ s3_inst.relax_inst |= ((s3_inst.reloc.exp.X_add_number ) & 0x1ff);
+ s3_inst.relax_size = 4;
+ }
+}
+
+/* Return true if the given symbol should be considered local for s3_PIC. */
+static bfd_boolean
+s3_pic_need_relax (symbolS *sym, asection *segtype)
+{
+ asection *symsec;
+ bfd_boolean linkonce;
+
+ /* Handle the case of a symbol equated to another symbol. */
+ while (symbol_equated_reloc_p (sym))
+ {
+ symbolS *n;
+
+ /* It's possible to get a loop here in a badly written
+ program. */
+ n = symbol_get_value_expression (sym)->X_add_symbol;
+ if (n == sym)
+ break;
+ sym = n;
+ }
+
+ symsec = S_GET_SEGMENT (sym);
+
+ /* duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */
+ linkonce = FALSE;
+ if (symsec != segtype && ! S_IS_LOCAL (sym))
+ {
+ if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE) != 0)
+ linkonce = TRUE;
+
+ /* The GNU toolchain uses an extension for ELF: a section
+ beginning with the magic string .gnu.linkonce is a linkonce
+ section. */
+ if (strncmp (segment_name (symsec), ".gnu.linkonce",
+ sizeof ".gnu.linkonce" - 1) == 0)
+ linkonce = TRUE;
+ }
+
+ /* This must duplicate the test in adjust_reloc_syms. */
+ return (!bfd_is_und_section (symsec)
+ && !bfd_is_abs_section (symsec)
+ && !bfd_is_com_section (symsec)
+ && !linkonce
+#ifdef OBJ_ELF
+ /* A global or weak symbol is treated as external. */
+ && (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym)))
+#endif
+ );
+}
+
+static void
+s3_parse_pce_inst (char *insnstr)
+{
+ char c;
+ char *p;
+ char first[s3_MAX_LITERAL_POOL_SIZE];
+ char second[s3_MAX_LITERAL_POOL_SIZE];
+ struct s3_score_it pec_part_1;
+
+ /* Get first part string of PCE. */
+ p = strstr (insnstr, "||");
+ c = *p;
+ *p = '\0';
+ sprintf (first, "%s", insnstr);
+
+ /* Get second part string of PCE. */
+ *p = c;
+ p += 2;
+ sprintf (second, "%s", p);
+
+ s3_parse_16_32_inst (first, FALSE);
+ if (s3_inst.error)
+ return;
+
+ memcpy (&pec_part_1, &s3_inst, sizeof (s3_inst));
+
+ s3_parse_16_32_inst (second, FALSE);
+ if (s3_inst.error)
+ return;
+
+ if ( ((pec_part_1.size == s3_INSN_SIZE) && (s3_inst.size == s3_INSN_SIZE))
+ || ((pec_part_1.size == s3_INSN_SIZE) && (s3_inst.size == s3_INSN16_SIZE))
+ || ((pec_part_1.size == s3_INSN16_SIZE) && (s3_inst.size == s3_INSN_SIZE)))
+ {
+ s3_inst.error = _("pce instruction error (16 bit || 16 bit)'");
+ sprintf (s3_inst.str, "%s", insnstr);
+ return;
+ }
+
+ if (!s3_inst.error)
+ s3_gen_insn_frag (&pec_part_1, &s3_inst);
+}
+
+/* s3: dsp. */
+static void
+s3_do16_dsp (char *str)
+{
+ int rd = 0;
+
+ /* Check 3d. */
+ if (s3_score3d == 0)
+ {
+ s3_inst.error = _("score3d instruction.");
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if ((rd = s3_reglow_required_here (&str, 0)) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ s3_inst.relax_inst |= rd << 20;
+ s3_inst.relax_size = 4;
+ }
+}
+
+static void
+s3_do16_dsp2 (char *str)
+{
+ /* Check 3d. */
+ if (s3_score3d == 0)
+ {
+ s3_inst.error = _("score3d instruction.");
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if (s3_reglow_required_here (&str, 4) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reglow_required_here (&str, 0) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 8) & 0xf) << 20)
+ | (((s3_inst.instruction >> 8) & 0xf) << 15) | (((s3_inst.instruction >> 4) & 0xf) << 10);
+ s3_inst.relax_size = 4;
+ }
+}
+
+static void
+s3_do_dsp (char *str)
+{
+ /* Check 3d. */
+ if (s3_score3d == 0)
+ {
+ s3_inst.error = _("score3d instruction.");
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((s3_inst.relax_inst != 0x8000) && (((s3_inst.instruction >> 20) & 0x1f) == 3) )
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0x1f)) | (((s3_inst.instruction >> 15) & 0x1f) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+static void
+s3_do_dsp2 (char *str)
+{
+ int reg;
+
+ /* Check 3d. */
+ if (s3_score3d == 0)
+ {
+ s3_inst.error = _("score3d instruction.");
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if ((reg = s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 10, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ /* Check mulr, mulur rd is even number. */
+ if (((s3_inst.instruction & 0x3e0003ff) == 0x00000340
+ || (s3_inst.instruction & 0x3e0003ff) == 0x00000342)
+ && (reg % 2))
+ {
+ s3_inst.error = _("rd must be even number.");
+ return;
+ }
+
+ if ((((s3_inst.instruction >> 15) & 0x10) == 0)
+ && (((s3_inst.instruction >> 10) & 0x10) == 0)
+ && (((s3_inst.instruction >> 20) & 0x10) == 0)
+ && (s3_inst.relax_inst != 0x8000)
+ && (((s3_inst.instruction >> 20) & 0xf) == ((s3_inst.instruction >> 15) & 0xf)))
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0xf) )
+ | (((s3_inst.instruction >> 15) & 0xf) << 4);
+ s3_inst.relax_size = 2;
+ }
+ else
+ {
+ s3_inst.relax_inst = 0x8000;
+ }
+ }
+}
+
+static void
+s3_do_dsp3 (char *str)
+{
+ /* Check 3d. */
+ if (s3_score3d == 0)
+ {
+ s3_inst.error = _("score3d instruction.");
+ return;
+ }
+
+ s3_skip_whitespace (str);
+
+ if (s3_reg_required_here (&str, 20, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_skip_past_comma (&str) == (int) s3_FAIL
+ || s3_reg_required_here (&str, 15, s3_REG_TYPE_SCORE) == (int) s3_FAIL
+ || s3_end_of_line (str) == (int) s3_FAIL)
+ return;
+
+ if ((s3_inst.relax_inst != 0x8000) && (((s3_inst.instruction >> 20) & 0x1f) == 3) )
+ {
+ s3_inst.relax_inst |= (((s3_inst.instruction >> 10) & 0x1f)) | (((s3_inst.instruction >> 15) & 0x1f) << 5);
+ s3_inst.relax_size = 2;
+ }
+ else
+ s3_inst.relax_inst = 0x8000;
+}
+
+
+/* If we change section we must dump the literal pool first. */
+static void
+s3_s_score_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ subseg_set (bss_section, (subsegT) get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+s3_s_score_text (int ignore)
+{
+ obj_elf_text (ignore);
+ record_alignment (now_seg, 2);
+}
+
+static void
+s3_score_s_section (int ignore)
+{
+ obj_elf_section (ignore);
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ record_alignment (now_seg, 2);
+
+}
+
+static void
+s3_s_change_sec (int sec)
+{
+ segT seg;
+
+#ifdef OBJ_ELF
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for an obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ obj_elf_section_change_hook ();
+#endif
+ switch (sec)
+ {
+ case 'r':
+ seg = subseg_new (s3_RDATA_SECTION_NAME, (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_RELOC | SEC_DATA));
+ if (strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+ case 's':
+ seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg, SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+ if (strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+ }
+}
+
+static void
+s3_s_score_mask (int reg_type ATTRIBUTE_UNUSED)
+{
+ long mask, off;
+
+ if (s3_cur_proc_ptr == (s3_procS *) NULL)
+ {
+ as_warn (_(".mask outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (get_absolute_expression_and_terminator (&mask) != ',')
+ {
+ as_warn (_("Bad .mask directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ off = get_absolute_expression ();
+ s3_cur_proc_ptr->reg_mask = mask;
+ s3_cur_proc_ptr->reg_offset = off;
+ demand_empty_rest_of_line ();
+}
+
+static symbolS *
+s3_get_symbol (void)
+{
+ int c;
+ char *name;
+ symbolS *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = (symbolS *) symbol_find_or_make (name);
+ *input_line_pointer = c;
+ return p;
+}
+
+static long
+s3_get_number (void)
+{
+ int negative = 0;
+ long val = 0;
+
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ negative = 1;
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ as_bad (_("expected simple number"));
+ if (input_line_pointer[0] == '0')
+ {
+ if (input_line_pointer[1] == 'x')
+ {
+ input_line_pointer += 2;
+ while (ISXDIGIT (*input_line_pointer))
+ {
+ val <<= 4;
+ val |= hex_value (*input_line_pointer++);
+ }
+ return negative ? -val : val;
+ }
+ else
+ {
+ ++input_line_pointer;
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val <<= 3;
+ val |= *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+ }
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ {
+ printf (_(" *input_line_pointer == '%c' 0x%02x\n"), *input_line_pointer, *input_line_pointer);
+ as_warn (_("invalid number"));
+ return -1;
+ }
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val *= 10;
+ val += *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+}
+
+/* The .aent and .ent directives. */
+static void
+s3_s_score_ent (int aent)
+{
+ symbolS *symbolP;
+ int maybe_text;
+
+ symbolP = s3_get_symbol ();
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-')
+ s3_get_number ();
+
+#ifdef BFD_ASSEMBLER
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#else
+ if (now_seg != data_section && now_seg != bss_section)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#endif
+ if (!maybe_text)
+ as_warn (_(".ent or .aent not in text section."));
+ if (!aent && s3_cur_proc_ptr)
+ as_warn (_("missing .end"));
+ if (!aent)
+ {
+ s3_cur_proc_ptr = &s3_cur_proc;
+ s3_cur_proc_ptr->reg_mask = 0xdeadbeaf;
+ s3_cur_proc_ptr->reg_offset = 0xdeadbeaf;
+ s3_cur_proc_ptr->fpreg_mask = 0xdeafbeaf;
+ s3_cur_proc_ptr->leaf = 0xdeafbeaf;
+ s3_cur_proc_ptr->frame_offset = 0xdeafbeaf;
+ s3_cur_proc_ptr->frame_reg = 0xdeafbeaf;
+ s3_cur_proc_ptr->pc_reg = 0xdeafbeaf;
+ s3_cur_proc_ptr->isym = symbolP;
+ symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
+ ++s3_numprocs;
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_func (S_GET_NAME (symbolP), S_GET_NAME (symbolP));
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+s3_s_score_frame (int ignore ATTRIBUTE_UNUSED)
+{
+ char *backupstr;
+ char str[30];
+ long val;
+ int i = 0;
+
+ backupstr = input_line_pointer;
+
+#ifdef OBJ_ELF
+ if (s3_cur_proc_ptr == (s3_procS *) NULL)
+ {
+ as_warn (_(".frame outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ s3_cur_proc_ptr->frame_reg = s3_reg_required_here ((&backupstr), 0, s3_REG_TYPE_SCORE);
+ SKIP_WHITESPACE ();
+ s3_skip_past_comma (&backupstr);
+ while (*backupstr != ',')
+ {
+ str[i] = *backupstr;
+ i++;
+ backupstr++;
+ }
+ str[i] = '\0';
+ val = atoi (str);
+
+ SKIP_WHITESPACE ();
+ s3_skip_past_comma (&backupstr);
+ s3_cur_proc_ptr->frame_offset = val;
+ s3_cur_proc_ptr->pc_reg = s3_reg_required_here ((&backupstr), 0, s3_REG_TYPE_SCORE);
+
+ SKIP_WHITESPACE ();
+ s3_skip_past_comma (&backupstr);
+ i = 0;
+ while (*backupstr != '\n')
+ {
+ str[i] = *backupstr;
+ i++;
+ backupstr++;
+ }
+ str[i] = '\0';
+ val = atoi (str);
+ s3_cur_proc_ptr->leaf = val;
+ SKIP_WHITESPACE ();
+ s3_skip_past_comma (&backupstr);
+
+#endif /* OBJ_ELF */
+ while (input_line_pointer != backupstr)
+ input_line_pointer++;
+}
+
+/* The .end directive. */
+static void
+s3_s_score_end (int x ATTRIBUTE_UNUSED)
+{
+ symbolS *p;
+ int maybe_text;
+
+ /* Generate a .pdr section. */
+ segT saved_seg = now_seg;
+ subsegT saved_subseg = now_subseg;
+ expressionS exp;
+ char *fragp;
+
+ if (!is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ p = s3_get_symbol ();
+ demand_empty_rest_of_line ();
+ }
+ else
+ p = NULL;
+
+#ifdef BFD_ASSEMBLER
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#else
+ if (now_seg != data_section && now_seg != bss_section)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#endif
+
+ if (!maybe_text)
+ as_warn (_(".end not in text section"));
+ if (!s3_cur_proc_ptr)
+ {
+ as_warn (_(".end directive without a preceding .ent directive."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (p != NULL)
+ {
+ gas_assert (S_GET_NAME (p));
+ if (strcmp (S_GET_NAME (p), S_GET_NAME (s3_cur_proc_ptr->isym)))
+ as_warn (_(".end symbol does not match .ent symbol."));
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_endfunc (S_GET_NAME (p), S_GET_NAME (p));
+ }
+ else
+ as_warn (_(".end directive missing or unknown symbol"));
+
+ if ((s3_cur_proc_ptr->reg_mask == 0xdeadbeaf) ||
+ (s3_cur_proc_ptr->reg_offset == 0xdeadbeaf) ||
+ (s3_cur_proc_ptr->leaf == 0xdeafbeaf) ||
+ (s3_cur_proc_ptr->frame_offset == 0xdeafbeaf) ||
+ (s3_cur_proc_ptr->frame_reg == 0xdeafbeaf) || (s3_cur_proc_ptr->pc_reg == 0xdeafbeaf));
+
+ else
+ {
+ (void) frag_now_fix ();
+ gas_assert (s3_pdr_seg);
+ subseg_set (s3_pdr_seg, 0);
+ /* Write the symbol. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = p;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 4);
+ fragp = frag_more (7 * 4);
+ md_number_to_chars (fragp, (valueT) s3_cur_proc_ptr->reg_mask, 4);
+ md_number_to_chars (fragp + 4, (valueT) s3_cur_proc_ptr->reg_offset, 4);
+ md_number_to_chars (fragp + 8, (valueT) s3_cur_proc_ptr->fpreg_mask, 4);
+ md_number_to_chars (fragp + 12, (valueT) s3_cur_proc_ptr->leaf, 4);
+ md_number_to_chars (fragp + 16, (valueT) s3_cur_proc_ptr->frame_offset, 4);
+ md_number_to_chars (fragp + 20, (valueT) s3_cur_proc_ptr->frame_reg, 4);
+ md_number_to_chars (fragp + 24, (valueT) s3_cur_proc_ptr->pc_reg, 4);
+ subseg_set (saved_seg, saved_subseg);
+
+ }
+ s3_cur_proc_ptr = NULL;
+}
+
+/* Handle the .set pseudo-op. */
+static void
+s3_s_score_set (int x ATTRIBUTE_UNUSED)
+{
+ int i = 0;
+ char name[s3_MAX_LITERAL_POOL_SIZE];
+ char * orig_ilp = input_line_pointer;
+
+ while (!is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ name[i] = (char) * input_line_pointer;
+ i++;
+ ++input_line_pointer;
+ }
+
+ name[i] = '\0';
+
+ if (strcmp (name, "nwarn") == 0)
+ {
+ s3_warn_fix_data_dependency = 0;
+ }
+ else if (strcmp (name, "fixdd") == 0)
+ {
+ s3_fix_data_dependency = 1;
+ }
+ else if (strcmp (name, "nofixdd") == 0)
+ {
+ s3_fix_data_dependency = 0;
+ }
+ else if (strcmp (name, "r1") == 0)
+ {
+ s3_nor1 = 0;
+ }
+ else if (strcmp (name, "nor1") == 0)
+ {
+ s3_nor1 = 1;
+ }
+ else if (strcmp (name, "optimize") == 0)
+ {
+ s3_g_opt = 1;
+ }
+ else if (strcmp (name, "volatile") == 0)
+ {
+ s3_g_opt = 0;
+ }
+ else if (strcmp (name, "pic") == 0)
+ {
+ s3_score_pic = s3_PIC;
+ }
+ else
+ {
+ input_line_pointer = orig_ilp;
+ s_set (0);
+ }
+}
+
+/* Handle the .cpload pseudo-op. This is used when generating s3_PIC code. It sets the
+ $gp register for the function based on the function address, which is in the register
+ named in the argument. This uses a relocation against GP_DISP_LABEL, which is handled
+ specially by the linker. The result is:
+ ldis gp, %hi(GP_DISP_LABEL)
+ ori gp, %low(GP_DISP_LABEL)
+ add gp, gp, .cpload argument
+ The .cpload argument is normally r29. */
+static void
+s3_s_score_cpload (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+ char insn_str[s3_MAX_LITERAL_POOL_SIZE];
+
+ /* If we are not generating s3_PIC code, .cpload is ignored. */
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if ((reg = s3_reg_required_here (&input_line_pointer, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ return;
+
+ demand_empty_rest_of_line ();
+
+ sprintf (insn_str, "ld_i32hi r%d, %s", s3_GP, GP_DISP_LABEL);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ sprintf (insn_str, "ld_i32lo r%d, %s", s3_GP, GP_DISP_LABEL);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ sprintf (insn_str, "add r%d, r%d, r%d", s3_GP, s3_GP, reg);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+}
+
+/* Handle the .cprestore pseudo-op. This stores $gp into a given
+ offset from $sp. The offset is remembered, and after making a s3_PIC
+ call $gp is restored from that location. */
+static void
+s3_s_score_cprestore (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+ int cprestore_offset;
+ char insn_str[s3_MAX_LITERAL_POOL_SIZE];
+
+ /* If we are not generating s3_PIC code, .cprestore is ignored. */
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if ((reg = s3_reg_required_here (&input_line_pointer, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL
+ || s3_skip_past_comma (&input_line_pointer) == (int) s3_FAIL)
+ {
+ return;
+ }
+
+ cprestore_offset = get_absolute_expression ();
+
+ if (cprestore_offset <= 0x3fff)
+ {
+ sprintf (insn_str, "sw r%d, [r%d, %d]", s3_GP, reg, cprestore_offset);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+ }
+ else
+ {
+ int r1_bak;
+
+ r1_bak = s3_nor1;
+ s3_nor1 = 0;
+
+ sprintf (insn_str, "li r1, %d", cprestore_offset);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ sprintf (insn_str, "add r1, r1, r%d", reg);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ sprintf (insn_str, "sw r%d, [r1]", s3_GP);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+
+ s3_nor1 = r1_bak;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpword pseudo-op. This is used when generating s3_PIC
+ code. It generates a 32 bit s3_GP relative reloc. */
+static void
+s3_s_score_gpword (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+ char *p;
+
+ /* When not generating s3_PIC code, this is treated as .word. */
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ cons (4);
+ return;
+ }
+ expression (&ex);
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("Unsupported use of .gpword"));
+ ignore_rest_of_line ();
+ }
+ p = frag_more (4);
+ s3_md_number_to_chars (p, (valueT) 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE, BFD_RELOC_GPREL32);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpadd pseudo-op. This is used when dealing with switch
+ tables in s3_PIC code. */
+static void
+s3_s_score_cpadd (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+ char insn_str[s3_MAX_LITERAL_POOL_SIZE];
+
+ /* If we are not generating s3_PIC code, .cpload is ignored. */
+ if (s3_score_pic == s3_NO_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if ((reg = s3_reg_required_here (&input_line_pointer, -1, s3_REG_TYPE_SCORE)) == (int) s3_FAIL)
+ {
+ return;
+ }
+ demand_empty_rest_of_line ();
+
+ /* Add $gp to the register named as an argument. */
+ sprintf (insn_str, "add r%d, r%d, r%d", reg, reg, s3_GP);
+ if (s3_append_insn (insn_str, TRUE) == (int) s3_FAIL)
+ return;
+}
+
+#ifndef TC_IMPLICIT_LCOMM_ALIGNMENT
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) \
+ do \
+ { \
+ if ((SIZE) >= 8) \
+ (P2VAR) = 3; \
+ else if ((SIZE) >= 4) \
+ (P2VAR) = 2; \
+ else if ((SIZE) >= 2) \
+ (P2VAR) = 1; \
+ else \
+ (P2VAR) = 0; \
+ } \
+ while (0)
+#endif
+
+static void
+s3_s_score_lcomm (int bytes_p)
+{
+ char *name;
+ char c;
+ char *p;
+ int temp;
+ symbolS *symbolP;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ const int max_alignment = 15;
+ int align = 0;
+ segT bss_seg = bss_section;
+ int needs_align = 0;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ *p = c;
+
+ if (name == p)
+ {
+ as_bad (_("expected symbol name"));
+ discard_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ /* Accept an optional comma after the name. The comma used to be
+ required, but Irix 5 cc does not generate it. */
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ if (is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ as_bad (_("missing size expression"));
+ return;
+ }
+
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_("BSS length (%d) < 0 ignored"), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+#if defined (TC_SCORE)
+ if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ /* For Score and Alpha ECOFF or ELF, small objects are put in .sbss. */
+ if ((unsigned)temp <= bfd_get_gp_size (stdoutput))
+ {
+ bss_seg = subseg_new (".sbss", 1);
+ seg_info (bss_seg)->bss = 1;
+#ifdef BFD_ASSEMBLER
+ if (!bfd_set_section_flags (stdoutput, bss_seg, SEC_ALLOC))
+ as_warn (_("error setting flags for \".sbss\": %s"), bfd_errmsg (bfd_get_error ()));
+#endif
+ }
+ }
+#endif
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ as_bad (_("missing alignment"));
+ return;
+ }
+ else
+ {
+ align = get_absolute_expression ();
+ needs_align = 1;
+ }
+ }
+
+ if (!needs_align)
+ {
+ TC_IMPLICIT_LCOMM_ALIGNMENT (temp, align);
+
+ /* Still zero unless TC_IMPLICIT_LCOMM_ALIGNMENT set it. */
+ if (align)
+ record_alignment (bss_seg, align);
+ }
+
+ if (needs_align)
+ {
+ if (bytes_p)
+ {
+ /* Convert to a power of 2. */
+ if (align != 0)
+ {
+ unsigned int i;
+
+ for (i = 0; align != 0; align >>= 1, ++i)
+ ;
+ align = i - 1;
+ }
+ }
+
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("alignment too large; %d assumed"), align);
+ }
+ else if (align < 0)
+ {
+ align = 0;
+ as_warn (_("alignment negative; 0 assumed"));
+ }
+
+ record_alignment (bss_seg, align);
+ }
+ else
+ {
+ /* Assume some objects may require alignment on some systems. */
+#if defined (TC_ALPHA) && ! defined (VMS)
+ if (temp > 1)
+ {
+ align = ffs (temp) - 1;
+ if (temp % (1 << align))
+ abort ();
+ }
+#endif
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT) \
+ || defined (OBJ_BOUT) || defined (OBJ_MAYBE_BOUT))
+#ifdef BFD_ASSEMBLER
+ (OUTPUT_FLAVOR != bfd_target_aout_flavour
+ || (S_GET_OTHER (symbolP) == 0 && S_GET_DESC (symbolP) == 0)) &&
+#else
+ (S_GET_OTHER (symbolP) == 0 && S_GET_DESC (symbolP) == 0) &&
+#endif
+#endif
+ (S_GET_SEGMENT (symbolP) == bss_seg || (!S_IS_DEFINED (symbolP) && S_GET_VALUE (symbolP) == 0)))
+ {
+ char *pfrag;
+
+ subseg_set (bss_seg, 1);
+
+ if (align)
+ frag_align (align, 0, 0);
+
+ /* Detach from old frag. */
+ if (S_GET_SEGMENT (symbolP) == bss_seg)
+ symbol_get_frag (symbolP)->fr_symbol = NULL;
+
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, (offsetT) temp, NULL);
+ *pfrag = 0;
+
+
+ S_SET_SEGMENT (symbolP, bss_seg);
+
+#ifdef OBJ_COFF
+ /* The symbol may already have been created with a preceding
+ ".globl" directive -- be careful not to step on storage class
+ in that case. Otherwise, set it to static. */
+ if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
+ {
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+ }
+#endif /* OBJ_COFF */
+
+#ifdef S_SET_SIZE
+ S_SET_SIZE (symbolP, temp);
+#endif
+ }
+ else
+ as_bad (_("symbol `%s' is already defined"), S_GET_NAME (symbolP));
+
+ subseg_set (current_seg, current_subseg);
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s3_insert_reg (const struct s3_reg_entry *r, struct hash_control *htab)
+{
+ int i = 0;
+ int len = strlen (r->name) + 2;
+ char *buf = xmalloc (len);
+ char *buf2 = xmalloc (len);
+
+ strcpy (buf + i, r->name);
+ for (i = 0; buf[i]; i++)
+ {
+ buf2[i] = TOUPPER (buf[i]);
+ }
+ buf2[i] = '\0';
+
+ hash_insert (htab, buf, (void *) r);
+ hash_insert (htab, buf2, (void *) r);
+}
+
+static void
+s3_build_reg_hsh (struct s3_reg_map *map)
+{
+ const struct s3_reg_entry *r;
+
+ if ((map->htab = hash_new ()) == NULL)
+ {
+ as_fatal (_("virtual memory exhausted"));
+ }
+ for (r = map->names; r->name != NULL; r++)
+ {
+ s3_insert_reg (r, map->htab);
+ }
+}
+
+/* Iterate over the base tables to create the instruction patterns. */
+static void
+s3_build_score_ops_hsh (void)
+{
+ unsigned int i;
+ static struct obstack insn_obstack;
+
+ obstack_begin (&insn_obstack, 4000);
+ for (i = 0; i < sizeof (s3_score_insns) / sizeof (struct s3_asm_opcode); i++)
+ {
+ const struct s3_asm_opcode *insn = s3_score_insns + i;
+ size_t len = strlen (insn->template_name);
+ struct s3_asm_opcode *new_opcode;
+ char *template_name;
+ new_opcode = (struct s3_asm_opcode *)
+ obstack_alloc (&insn_obstack, sizeof (struct s3_asm_opcode));
+ template_name = (char *) obstack_alloc (& insn_obstack, len + 1);
+
+ strcpy (template_name, insn->template_name);
+ new_opcode->template_name = template_name;
+ new_opcode->parms = insn->parms;
+ new_opcode->value = insn->value;
+ new_opcode->relax_value = insn->relax_value;
+ new_opcode->type = insn->type;
+ new_opcode->bitmask = insn->bitmask;
+ hash_insert (s3_score_ops_hsh, new_opcode->template_name,
+ (void *) new_opcode);
+ }
+}
+
+static void
+s3_build_dependency_insn_hsh (void)
+{
+ unsigned int i;
+ static struct obstack dependency_obstack;
+
+ obstack_begin (&dependency_obstack, 4000);
+ for (i = 0; i < sizeof (s3_insn_to_dependency_table) / sizeof (s3_insn_to_dependency_table[0]); i++)
+ {
+ const struct s3_insn_to_dependency *tmp = s3_insn_to_dependency_table + i;
+ size_t len = strlen (tmp->insn_name);
+ struct s3_insn_to_dependency *new_i2n;
+
+ new_i2n = (struct s3_insn_to_dependency *)
+ obstack_alloc (&dependency_obstack,
+ sizeof (struct s3_insn_to_dependency));
+ new_i2n->insn_name = (char *) obstack_alloc (&dependency_obstack,
+ len + 1);
+
+ strcpy (new_i2n->insn_name, tmp->insn_name);
+ new_i2n->type = tmp->type;
+ hash_insert (s3_dependency_insn_hsh, new_i2n->insn_name,
+ (void *) new_i2n);
+ }
+}
+
+static void
+s_score_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_bss (ignore);
+ else
+ return s7_s_score_bss (ignore);
+}
+
+static void
+s_score_text (int ignore)
+{
+ if (score3)
+ return s3_s_score_text (ignore);
+ else
+ return s7_s_score_text (ignore);
+}
+
+static void
+s_section (int ignore)
+{
+ if (score3)
+ return s3_score_s_section (ignore);
+ else
+ return s7_s_section (ignore);
+}
+
+static void
+s_change_sec (int sec)
+{
+ if (score3)
+ return s3_s_change_sec (sec);
+ else
+ return s7_s_change_sec (sec);
+}
+
+static void
+s_score_mask (int reg_type ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_mask (reg_type);
+ else
+ return s7_s_score_mask (reg_type);
+}
+
+static void
+s_score_ent (int aent)
+{
+ if (score3)
+ return s3_s_score_ent (aent);
+ else
+ return s7_s_score_ent (aent);
+}
+
+static void
+s_score_frame (int ignore ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_frame (ignore);
+ else
+ return s7_s_score_frame (ignore);
+}
+
+static void
+s_score_end (int x ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_end (x);
+ else
+ return s7_s_score_end (x);
+}
+
+static void
+s_score_set (int x ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_set (x);
+ else
+ return s7_s_score_set (x);
+}
+
+static void
+s_score_cpload (int ignore ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_cpload (ignore);
+ else
+ return s7_s_score_cpload (ignore);
+}
+
+static void
+s_score_cprestore (int ignore ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_cprestore (ignore);
+ else
+ return s7_s_score_cprestore (ignore);
+}
+
+static void
+s_score_gpword (int ignore ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_gpword (ignore);
+ else
+ return s7_s_score_gpword (ignore);
+}
+
+static void
+s_score_cpadd (int ignore ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_s_score_cpadd (ignore);
+ else
+ return s7_s_score_cpadd (ignore);
+}
+
+static void
+s_score_lcomm (int bytes_p)
+{
+ if (score3)
+ return s3_s_score_lcomm (bytes_p);
+ else
+ return s7_s_score_lcomm (bytes_p);
+}
+
+static void
+s3_assemble (char *str)
+{
+ know (str);
+ know (strlen (str) < s3_MAX_LITERAL_POOL_SIZE);
+
+ memset (&s3_inst, '\0', sizeof (s3_inst));
+ if (s3_INSN_IS_PCE_P (str))
+ s3_parse_pce_inst (str);
+ else if (s3_INSN_IS_48_P (str))
+ s3_parse_48_inst (str, TRUE);
+ else
+ s3_parse_16_32_inst (str, TRUE);
+
+ if (s3_inst.error)
+ as_bad (_("%s -- `%s'"), s3_inst.error, s3_inst.str);
+}
+
+static void
+s3_operand (expressionS * exp)
+{
+ if (s3_in_my_get_expression)
+ {
+ exp->X_op = O_illegal;
+ if (s3_inst.error == NULL)
+ {
+ s3_inst.error = _("bad expression");
+ }
+ }
+}
+
+static void
+s3_begin (void)
+{
+ unsigned int i;
+ segT seg;
+ subsegT subseg;
+
+ if ((s3_score_ops_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ s3_build_score_ops_hsh ();
+
+ if ((s3_dependency_insn_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ s3_build_dependency_insn_hsh ();
+
+ for (i = (int)s3_REG_TYPE_FIRST; i < (int)s3_REG_TYPE_MAX; i++)
+ s3_build_reg_hsh (s3_all_reg_maps + i);
+
+ /* Initialize dependency vector. */
+ s3_init_dependency_vector ();
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+ seg = now_seg;
+ subseg = now_subseg;
+ s3_pdr_seg = subseg_new (".pdr", (subsegT) 0);
+ (void)bfd_set_section_flags (stdoutput, s3_pdr_seg, SEC_READONLY | SEC_RELOC | SEC_DEBUGGING);
+ (void)bfd_set_section_alignment (stdoutput, s3_pdr_seg, 2);
+ subseg_set (seg, subseg);
+
+ if (s3_USE_GLOBAL_POINTER_OPT)
+ bfd_set_gp_size (stdoutput, s3_g_switch_value);
+}
+
+static void
+s3_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+static valueT
+s3_normal_chars_to_number (char *buf, int n)
+{
+ valueT result = 0;
+ unsigned char *where = (unsigned char *)buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
+ }
+
+ return result;
+}
+
+static void
+s3_number_to_chars_littleendian (void *p, valueT data, int n)
+{
+ char *buf = (char *) p;
+
+ switch (n)
+ {
+ case 4:
+ md_number_to_chars (buf, data >> 16, 2);
+ md_number_to_chars (buf + 2, data, 2);
+ break;
+ case 6:
+ md_number_to_chars (buf, data >> 32, 2);
+ md_number_to_chars (buf + 2, data >> 16, 2);
+ md_number_to_chars (buf + 4, data, 2);
+ break;
+ default:
+ /* Error routine. */
+ as_bad_where (__FILE__, __LINE__, _("size is not 4 or 6"));
+ break;
+ }
+}
+
+static valueT
+s3_chars_to_number_littleendian (const void *p, int n)
+{
+ char *buf = (char *) p;
+ valueT result = 0;
+
+ switch (n)
+ {
+ case 4:
+ result = s3_normal_chars_to_number (buf, 2) << 16;
+ result |= s3_normal_chars_to_number (buf + 2, 2);
+ break;
+ case 6:
+ result = s3_normal_chars_to_number (buf, 2) << 32;
+ result |= s3_normal_chars_to_number (buf + 2, 2) << 16;
+ result |= s3_normal_chars_to_number (buf + 4, 2);
+ break;
+ default:
+ /* Error routine. */
+ as_bad_where (__FILE__, __LINE__, _("size is not 4 or 6"));
+ break;
+ }
+
+ return result;
+}
+
+static void
+s3_md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (!target_big_endian && n >= 4)
+ s3_number_to_chars_littleendian (buf, val, n);
+ else
+ md_number_to_chars (buf, val, n);
+}
+
+static valueT
+s3_md_chars_to_number (char *buf, int n)
+{
+ valueT result = 0;
+
+ if (!target_big_endian && n >= 4)
+ result = s3_chars_to_number_littleendian (buf, n);
+ else
+ result = s3_normal_chars_to_number (buf, n);
+
+ return result;
+}
+
+static char *
+s3_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[s3_MAX_LITTLENUMS];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+ case 'x':
+ case 'X':
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+ 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 * 2;
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ s3_md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+ else
+ {
+ for (i = 0; i < prec; i += 2)
+ {
+ s3_md_number_to_chars (litP, (valueT) words[i + 1], 2);
+ s3_md_number_to_chars (litP + 2, (valueT) words[i], 2);
+ litP += 4;
+ }
+ }
+
+ return 0;
+}
+
+static void
+s3_frag_check (fragS * fragp ATTRIBUTE_UNUSED)
+{
+ know (fragp->insn_addr <= s3_RELAX_PAD_BYTE);
+}
+
+static void
+s3_validate_fix (fixS *fixP)
+{
+ fixP->fx_where += fixP->fx_frag->insn_addr;
+}
+
+static int
+s3_force_relocation (struct fix *fixp)
+{
+ int retval = 0;
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixp->fx_r_type == BFD_RELOC_SCORE_JMP
+ || fixp->fx_r_type == BFD_RELOC_SCORE_BRANCH
+ || fixp->fx_r_type == BFD_RELOC_SCORE16_JMP
+ || fixp->fx_r_type == BFD_RELOC_SCORE16_BRANCH
+ || fixp->fx_r_type == BFD_RELOC_SCORE_BCMP)
+ {
+ retval = 1;
+ }
+ return retval;
+}
+
+static bfd_boolean
+s3_fix_adjustable (fixS * fixP)
+{
+ if (fixP->fx_addsy == NULL)
+ {
+ return 1;
+ }
+ else if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && (S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)))
+ {
+ return 0;
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_SCORE_JMP
+ || fixP->fx_r_type == BFD_RELOC_SCORE16_JMP)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+s3_elf_final_processing (void)
+{
+ unsigned long val = 0;
+
+ if (score3)
+ val = E_SCORE_MACH_SCORE3;
+ else if (score7)
+ val = E_SCORE_MACH_SCORE7;
+
+ elf_elfheader (stdoutput)->e_machine = EM_SCORE;
+ elf_elfheader (stdoutput)->e_flags &= ~EF_SCORE_MACH;
+ elf_elfheader (stdoutput)->e_flags |= val;
+
+ if (s3_fix_data_dependency == 1)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_SCORE_FIXDEP;
+ }
+ if (s3_score_pic == s3_PIC)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_SCORE_PIC;
+ }
+}
+
+static int
+s3_judge_size_before_relax (fragS * fragp, asection *sec)
+{
+ int change = 0;
+
+ if (s3_score_pic == s3_NO_PIC)
+ change = s3_nopic_need_relax (fragp->fr_symbol, 0);
+ else
+ change = s3_pic_need_relax (fragp->fr_symbol, sec);
+
+ if (change == 1)
+ {
+ /* Only at the first time determining whether s3_GP instruction relax should be done,
+ return the difference between insntruction size and instruction relax size. */
+ if (fragp->fr_opcode == NULL)
+ {
+ fragp->fr_fix = s3_RELAX_NEW (fragp->fr_subtype);
+ fragp->fr_opcode = fragp->fr_literal + s3_RELAX_RELOC1 (fragp->fr_subtype);
+ return s3_RELAX_NEW (fragp->fr_subtype) - s3_RELAX_OLD (fragp->fr_subtype);
+ }
+ }
+
+ return 0;
+}
+
+static int
+s3_estimate_size_before_relax (fragS * fragp, asection * sec ATTRIBUTE_UNUSED)
+{
+ if ((s3_RELAX_TYPE (fragp->fr_subtype) == Insn_GP)
+ || (s3_RELAX_TYPE (fragp->fr_subtype) == Insn_PIC))
+ return s3_judge_size_before_relax (fragp, sec);
+
+ return 0;
+}
+
+static int
+s3_relax_branch_inst32 (fragS * fragp)
+{
+ fragp->fr_opcode = NULL;
+ return 0;
+}
+
+static int
+s3_relax_branch_inst16 (fragS * fragp)
+{
+ int relaxable_p = 0;
+ int frag_addr = fragp->fr_address + fragp->insn_addr;
+ addressT symbol_address = 0;
+ symbolS *s;
+ offsetT offset;
+ long value;
+ unsigned long inst_value;
+
+ relaxable_p = s3_RELAX_OPT (fragp->fr_subtype);
+
+ s = fragp->fr_symbol;
+ if (s == NULL)
+ frag_addr = 0;
+ else
+ {
+ if (s->bsym != NULL)
+ symbol_address = (addressT) symbol_get_frag (s)->fr_address;
+ }
+
+ inst_value = s3_md_chars_to_number (fragp->fr_literal, s3_INSN16_SIZE);
+ offset = (inst_value & 0x1ff) << 1;
+ if ((offset & 0x200) == 0x200)
+ offset |= 0xfffffc00;
+
+ value = offset + symbol_address - frag_addr;
+
+ if (relaxable_p
+ && (!((value & 0xfffffe00) == 0 || (value & 0xfffffe00) == 0xfffffe00))
+ && fragp->fr_fix == 2
+ && (s->bsym != NULL)
+ && (S_IS_DEFINED (s)
+ && !S_IS_COMMON (s)
+ && !S_IS_EXTERNAL (s)))
+ {
+ /* Relax branch 32 to branch 16. */
+ fragp->fr_opcode = fragp->fr_literal + s3_RELAX_RELOC1 (fragp->fr_subtype);
+ fragp->fr_fix = 4;
+ return 2;
+ }
+ else
+ return 0;
+}
+
+static int
+s3_relax_cmpbranch_inst32 (fragS * fragp)
+{
+ int relaxable_p = 0;
+ symbolS *s;
+ /* For sign bit. */
+ long offset;
+ long frag_addr = fragp->fr_address + fragp->insn_addr;
+ long symbol_address = 0;
+ long value;
+ unsigned long inst_value;
+
+ relaxable_p = s3_RELAX_OPT (fragp->fr_subtype);
+
+ s = fragp->fr_symbol;
+ if (s == NULL)
+ frag_addr = 0;
+ else
+ {
+ if (s->bsym != NULL)
+ symbol_address = (addressT) symbol_get_frag (s)->fr_address;
+ }
+
+ inst_value = s3_md_chars_to_number (fragp->fr_literal, s3_INSN_SIZE);
+ offset = (inst_value & 0x1)
+ | (((inst_value >> 7) & 0x7) << 1)
+ | (((inst_value >> 21) & 0x1f) << 4);
+ offset <<= 1;
+ if ((offset & 0x200) == 0x200)
+ offset |= 0xfffffe00;
+
+ value = offset + symbol_address - frag_addr;
+ /* change the order of judging rule is because
+ 1.not defined symbol or common sysbol or external symbol will change
+ bcmp to cmp!+beq/bne ,here need to record fragp->fr_opcode
+ 2.if the flow is as before : it will results to recursive loop
+ */
+ if (fragp->fr_fix == 6)
+ {
+ /* Have already relaxed! Just return 0 to terminate the loop. */
+ return 0;
+ }
+ /* need to translate when extern or not defind or common sysbol */
+ else if ((relaxable_p
+ && (!((value & 0xfffffe00) == 0 || (value & 0xfffffe00) == 0xfffffe00))
+ && fragp->fr_fix == 4
+ && (s->bsym != NULL))
+ || !S_IS_DEFINED (s)
+ ||S_IS_COMMON (s)
+ ||S_IS_EXTERNAL (s))
+ {
+ fragp->fr_opcode = fragp->fr_literal + s3_RELAX_RELOC1 (fragp->fr_subtype);
+ fragp->fr_fix = 6;
+ return 2;
+ }
+ else
+ {
+ /* Never relax. Modify fr_opcode to NULL to verify it's value in
+ md_apply_fix. */
+ fragp->fr_opcode = NULL;
+ return 0;
+ }
+}
+
+
+static int
+s3_relax_other_inst32 (fragS * fragp)
+{
+ int relaxable_p = s3_RELAX_OPT (fragp->fr_subtype);
+
+ if (relaxable_p
+ && fragp->fr_fix == 4)
+ {
+ fragp->fr_opcode = fragp->fr_literal + s3_RELAX_RELOC1 (fragp->fr_subtype);
+ fragp->fr_fix = 2;
+ return -2;
+ }
+ else
+ return 0;
+}
+
+static int
+s3_relax_gp_and_pic_inst32 (void)
+{
+ /* md_estimate_size_before_relax has already relaxed s3_GP and s3_PIC
+ instructions. We don't change relax size here. */
+ return 0;
+}
+
+static int
+s3_relax_frag (asection * sec ATTRIBUTE_UNUSED, fragS * fragp, long stretch ATTRIBUTE_UNUSED)
+{
+ int grows = 0;
+ int adjust_align_p = 0;
+
+ /* If the instruction address is odd, make it half word align first. */
+ if ((fragp->fr_address) % 2 != 0)
+ {
+ if ((fragp->fr_address + fragp->insn_addr) % 2 != 0)
+ {
+ fragp->insn_addr = 1;
+ grows += 1;
+ adjust_align_p = 1;
+ }
+ }
+
+ switch (s3_RELAX_TYPE (fragp->fr_subtype))
+ {
+ case PC_DISP19div2:
+ grows += s3_relax_branch_inst32 (fragp);
+ break;
+
+ case PC_DISP8div2:
+ grows += s3_relax_branch_inst16 (fragp);
+ break;
+
+ case Insn_BCMP :
+ grows += s3_relax_cmpbranch_inst32 (fragp);
+ break;
+
+ case Insn_GP:
+ case Insn_PIC:
+ grows += s3_relax_gp_and_pic_inst32 ();
+ break;
+
+ default:
+ grows += s3_relax_other_inst32 (fragp);
+ break;
+ }
+
+ /* newly added */
+ if (adjust_align_p && fragp->insn_addr)
+ {
+ fragp->fr_fix += fragp->insn_addr;
+ }
+
+ return grows;
+}
+
+static void
+s3_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, fragS * fragp)
+{
+ int r_old;
+ int r_new;
+ char backup[20];
+ fixS *fixp;
+
+ r_old = s3_RELAX_OLD (fragp->fr_subtype);
+ r_new = s3_RELAX_NEW (fragp->fr_subtype);
+
+ /* fragp->fr_opcode indicates whether this frag should be relaxed. */
+ if (fragp->fr_opcode == NULL)
+ {
+ memcpy (backup, fragp->fr_literal, r_old);
+ fragp->fr_fix = r_old;
+ }
+ else
+ {
+ memcpy (backup, fragp->fr_literal + r_old, r_new);
+ fragp->fr_fix = r_new;
+ }
+
+ fixp = fragp->tc_frag_data.fixp;
+ while (fixp && fixp->fx_frag == fragp && fixp->fx_where < r_old)
+ {
+ if (fragp->fr_opcode)
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+ while (fixp && fixp->fx_frag == fragp)
+ {
+ if (fragp->fr_opcode)
+ fixp->fx_where -= r_old + fragp->insn_addr;
+ else
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+
+ if (fragp->insn_addr)
+ {
+ s3_md_number_to_chars (fragp->fr_literal, 0x0, fragp->insn_addr);
+ }
+ memcpy (fragp->fr_literal + fragp->insn_addr, backup, fragp->fr_fix);
+ fragp->fr_fix += fragp->insn_addr;
+}
+
+static long
+s3_pcrel_from (fixS * fixP)
+{
+ long retval = 0;
+
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == undefined_section)
+ && (fixP->fx_subsy == NULL))
+ {
+ retval = 0;
+ }
+ else
+ {
+ retval = fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+
+ return retval;
+}
+
+static valueT
+s3_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+static void
+s3_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ offsetT value = *valP;
+ offsetT newval;
+ offsetT content;
+ unsigned short HI, LO;
+
+ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ gas_assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ {
+ if (fixP->fx_r_type != BFD_RELOC_SCORE_DUMMY_HI16)
+ fixP->fx_done = 1;
+ }
+
+ /* If this symbol is in a different section then we need to leave it for
+ the linker to deal with. Unfortunately, md_pcrel_from can't tell,
+ so we have to undo it's effects here. */
+ if (fixP->fx_pcrel)
+ {
+ if (fixP->fx_addsy != NULL
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ value += md_pcrel_from (fixP);
+ }
+
+ /* Remember value for emit_reloc. */
+ fixP->fx_addnumber = value;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_HI16_S:
+ if (fixP->fx_done) /* For la rd, imm32. */
+ {
+ newval = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ HI = (value) >> 16; /* mul to 2, then take the hi 16 bit. */
+ newval |= (HI & 0x3fff) << 1;
+ newval |= ((HI >> 14) & 0x3) << 16;
+ s3_md_number_to_chars (buf, newval, s3_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_LO16:
+ if (fixP->fx_done) /* For la rd, imm32. */
+ {
+ newval = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ LO = (value) & 0xffff;
+ newval |= (LO & 0x3fff) << 1; /* 16 bit: imm -> 14 bit in lo, 2 bit in hi. */
+ newval |= ((LO >> 14) & 0x3) << 16;
+ s3_md_number_to_chars (buf, newval, s3_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_SCORE_JMP:
+ {
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ value = fixP->fx_offset;
+ content = (content & ~0x3ff7ffe) | ((value << 1) & 0x3ff0000) | (value & 0x7fff);
+ s3_md_number_to_chars (buf, content, s3_INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_SCORE_IMM30:
+ {
+ content = s3_md_chars_to_number (buf, s3_INSN48_SIZE);
+ value = fixP->fx_offset;
+ value >>= 2;
+ content = (content & ~0x7f7fff7f80LL)
+ | (((value & 0xff) >> 0) << 7)
+ | (((value & 0x7fff00) >> 8) << 16)
+ | (((value & 0x3f800000) >> 23) << 32);
+ s3_md_number_to_chars (buf, content, s3_INSN48_SIZE);
+ break;
+ }
+
+ case BFD_RELOC_SCORE_IMM32:
+ {
+ content = s3_md_chars_to_number (buf, s3_INSN48_SIZE);
+ value = fixP->fx_offset;
+ content = (content & ~0x7f7fff7fe0LL)
+ | ((value & 0x3ff) << 5)
+ | (((value >> 10) & 0x7fff) << 16)
+ | (((value >> 25) & 0x7f) << 32);
+ s3_md_number_to_chars (buf, content, s3_INSN48_SIZE);
+ break;
+ }
+
+ case BFD_RELOC_SCORE_BRANCH:
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+
+ /* Don't check c-bit. */
+ if (fixP->fx_frag->fr_opcode != 0)
+ {
+ if ((value & 0xfffffe00) != 0 && (value & 0xfffffe00) != 0xfffffe00)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^9 ~ 2^9]"), (unsigned int)value);
+ return;
+ }
+ content = s3_md_chars_to_number (buf, s3_INSN16_SIZE);
+ content &= 0xfe00;
+ content = (content & 0xfe00) | ((value >> 1) & 0x1ff);
+ s3_md_number_to_chars (buf, content, s3_INSN16_SIZE);
+ fixP->fx_r_type = BFD_RELOC_SCORE16_BRANCH;
+ fixP->fx_size = 2;
+ }
+ else
+ {
+ if ((value & 0xfff80000) != 0 && (value & 0xfff80000) != 0xfff80000)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"), (unsigned int)value);
+ return;
+ }
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ content &= 0xfc00fc01;
+ content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000);
+ s3_md_number_to_chars (buf, content, s3_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_SCORE16_JMP:
+ content = s3_md_chars_to_number (buf, s3_INSN16_SIZE);
+ content &= 0xf001;
+ value = fixP->fx_offset & 0xfff;
+ content = (content & 0xfc01) | (value & 0xffe);
+ s3_md_number_to_chars (buf, content, s3_INSN16_SIZE);
+ break;
+ case BFD_RELOC_SCORE16_BRANCH:
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ /* Don't check c-bit. */
+ if (fixP->fx_frag->fr_opcode != 0)
+ {
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) ||
+ (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+ if ((value & 0xfff80000) != 0 && (value & 0xfff80000) != 0xfff80000)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"), (unsigned int)value);
+ return;
+ }
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000);
+ s3_md_number_to_chars (buf, content, s3_INSN_SIZE);
+ fixP->fx_r_type = BFD_RELOC_SCORE_BRANCH;
+ fixP->fx_size = 4;
+ break;
+ }
+ else
+ {
+ /* In differnt section. */
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) ||
+ (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ if ((value & 0xfffffe00) != 0 && (value & 0xfffffe00) != 0xfffffe00)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^9 ~ 2^9]"), (unsigned int)value);
+ return;
+ }
+
+ content = s3_md_chars_to_number (buf, s3_INSN16_SIZE);
+ content = (content & 0xfe00) | ((value >> 1) & 0x1ff);
+ s3_md_number_to_chars (buf, content, s3_INSN16_SIZE);
+ break;
+ }
+
+ break;
+
+ case BFD_RELOC_SCORE_BCMP:
+ if (fixP->fx_frag->fr_opcode != 0)
+ {
+ char *buf_ptr = buf;
+ buf_ptr += 2;
+
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ /* NOTE!!!
+ bcmp -> cmp! and branch, so value -= 2. */
+ value -= 2;
+
+ if ((value & 0xfff80000) != 0 && (value & 0xfff80000) != 0xfff80000)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"), (unsigned int)value);
+ return;
+ }
+
+ content = s3_md_chars_to_number (buf_ptr, s3_INSN_SIZE);
+ content &= 0xfc00fc01;
+ content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000);
+ s3_md_number_to_chars (buf_ptr, content, s3_INSN_SIZE);
+ /* change relocation type to BFD_RELOC_SCORE_BRANCH */
+ fixP->fx_r_type = BFD_RELOC_SCORE_BRANCH;
+ fixP->fx_where+=2; /* first insn is cmp! , the second insn is beq/bne */
+ break;
+ }
+ else
+ {
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+
+ if ((value & 0xfffffe00) != 0 && (value & 0xfffffe00) != 0xfffffe00)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^9 ~ 2^9]"), (unsigned int)value);
+ return;
+ }
+
+ value >>= 1;
+ content &= ~0x03e00381;
+ content = content
+ | (value & 0x1)
+ | (((value & 0xe) >> 1) << 7)
+ | (((value & 0x1f0) >> 4) << 21);
+
+ s3_md_number_to_chars (buf, content, s3_INSN_SIZE);
+ break;
+ }
+
+ case BFD_RELOC_8:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ s3_md_number_to_chars (buf, value, 1);
+#ifdef OBJ_ELF
+ else
+ {
+ value = fixP->fx_offset;
+ s3_md_number_to_chars (buf, value, 1);
+ }
+#endif
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ s3_md_number_to_chars (buf, value, 2);
+#ifdef OBJ_ELF
+ else
+ {
+ value = fixP->fx_offset;
+ s3_md_number_to_chars (buf, value, 2);
+ }
+#endif
+ break;
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ md_number_to_chars (buf, value, 4);
+#ifdef OBJ_ELF
+ else
+ {
+ value = fixP->fx_offset;
+ md_number_to_chars (buf, value, 4);
+ }
+#endif
+ break;
+ case BFD_RELOC_VTABLE_INHERIT:
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ break;
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+ case BFD_RELOC_SCORE_GPREL15:
+ content = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ /* c-bit. */
+ if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0xfc1c8000) != 0x94180000))
+ fixP->fx_r_type = BFD_RELOC_NONE;
+ fixP->fx_done = 0;
+ break;
+ case BFD_RELOC_SCORE_GOT15:
+ case BFD_RELOC_SCORE_DUMMY_HI16:
+ case BFD_RELOC_SCORE_GOT_LO16:
+ case BFD_RELOC_SCORE_CALL15:
+ case BFD_RELOC_GPREL32:
+ break;
+ case BFD_RELOC_NONE:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("bad relocation fixup type (%d)"), fixP->fx_r_type);
+ }
+}
+
+static arelent **
+s3_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ static arelent *retval[MAX_RELOC_EXPANSION + 1]; /* MAX_RELOC_EXPANSION equals 2. */
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+ char *type;
+
+ reloc = retval[0] = xmalloc (sizeof (arelent));
+ retval[1] = NULL;
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = fixp->fx_offset;
+
+ /* If this is a variant frag, we may need to adjust the existing
+ reloc and generate a new one. */
+ if (fixp->fx_frag->fr_opcode != NULL && (fixp->fx_r_type == BFD_RELOC_SCORE_GPREL15))
+ {
+ /* Update instruction imm bit. */
+ offsetT newval;
+ unsigned short off;
+ char *buf;
+
+ buf = fixp->fx_frag->fr_literal + fixp->fx_frag->insn_addr;
+ newval = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ off = fixp->fx_offset >> 16;
+ newval |= (off & 0x3fff) << 1;
+ newval |= ((off >> 14) & 0x3) << 16;
+ s3_md_number_to_chars (buf, newval, s3_INSN_SIZE);
+
+ buf += s3_INSN_SIZE;
+ newval = s3_md_chars_to_number (buf, s3_INSN_SIZE);
+ off = fixp->fx_offset & 0xffff;
+ newval |= ((off & 0x3fff) << 1);
+ newval |= (((off >> 14) & 0x3) << 16);
+ s3_md_number_to_chars (buf, newval, s3_INSN_SIZE);
+
+ retval[1] = xmalloc (sizeof (arelent));
+ retval[2] = NULL;
+ retval[1]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *retval[1]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ retval[1]->address = (reloc->address + s3_RELAX_RELOC2 (fixp->fx_frag->fr_subtype));
+
+ retval[1]->addend = 0;
+ retval[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16);
+ gas_assert (retval[1]->howto != NULL);
+
+ fixp->fx_r_type = BFD_RELOC_HI16_S;
+ }
+
+ code = fixp->fx_r_type;
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_32_PCREL;
+ break;
+ }
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_SCORE_JMP:
+ case BFD_RELOC_SCORE_BRANCH:
+ case BFD_RELOC_SCORE16_JMP:
+ case BFD_RELOC_SCORE16_BRANCH:
+ case BFD_RELOC_SCORE_BCMP:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_SCORE_GPREL15:
+ case BFD_RELOC_SCORE_GOT15:
+ case BFD_RELOC_SCORE_DUMMY_HI16:
+ case BFD_RELOC_SCORE_GOT_LO16:
+ case BFD_RELOC_SCORE_CALL15:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_SCORE_IMM30:
+ case BFD_RELOC_SCORE_IMM32:
+ code = fixp->fx_r_type;
+ break;
+ default:
+ type = _("<unknown>");
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file format"), type);
+ return NULL;
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file format1"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+ /* HACK: Since arm ELF uses Rel instead of Rela, encode the
+ vtable entry to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ return retval;
+}
+
+void
+md_assemble (char *str)
+{
+ if (score3)
+ s3_assemble (str);
+ else
+ s7_assemble (str);
+}
+
+/* We handle all bad expressions here, so that we can report the faulty
+ instruction in the error message. */
+void
+md_operand (expressionS * exp)
+{
+ if (score3)
+ s3_operand (exp);
+ else
+ s7_operand (exp);
+}
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least). */
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (score3)
+ s3_number_to_chars (buf, val, n);
+ else
+ s7_number_to_chars (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.
+
+ Note that fp constants aren't represent in the normal way on the ARM.
+ In big endian mode, things are as expected. However, in little endian
+ mode fp constants are big-endian word-wise, and little-endian byte-wise
+ within the words. For example, (double) 1.1 in big endian mode is
+ the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
+ the byte sequence 99 99 f1 3f 9a 99 99 99. */
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ if (score3)
+ return s3_atof (type, litP, sizeP);
+ else
+ return s7_atof (type, litP, sizeP);
+}
+
+void
+score_frag_check (fragS * fragp ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ s3_frag_check (fragp);
+ else
+ s7_frag_check (fragp);
+}
+
+/* Implementation of TC_VALIDATE_FIX.
+ Called before md_apply_fix() and after md_convert_frag(). */
+void
+score_validate_fix (fixS *fixP)
+{
+ if (score3)
+ s3_validate_fix (fixP);
+ else
+ s7_validate_fix (fixP);
+}
+
+int
+score_force_relocation (struct fix *fixp)
+{
+ if (score3)
+ return s3_force_relocation (fixp);
+ else
+ return s7_force_relocation (fixp);
+}
+
+/* Implementation of md_frag_check.
+ Called after md_convert_frag(). */
+bfd_boolean
+score_fix_adjustable (fixS * fixP)
+{
+ if (score3)
+ return s3_fix_adjustable (fixP);
+ else
+ return s7_fix_adjustable (fixP);
+}
+
+void
+score_elf_final_processing (void)
+{
+ if (score3)
+ s3_elf_final_processing ();
+ else
+ s7_elf_final_processing ();
+}
+
+/* In this function, we determine whether s3_GP instruction should do relaxation,
+ for the label being against was known now.
+ Doing this here but not in md_relax_frag() can induce iteration times
+ in stage of doing relax. */
+int
+md_estimate_size_before_relax (fragS * fragp, asection * sec ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_estimate_size_before_relax (fragp, sec);
+ else
+ return s7_estimate_size_before_relax (fragp, sec);
+}
+
+int
+score_relax_frag (asection * sec ATTRIBUTE_UNUSED, fragS * fragp, long stretch ATTRIBUTE_UNUSED)
+{
+ if (score3)
+ return s3_relax_frag (sec, fragp, stretch);
+ else
+ return s7_relax_frag (sec, fragp, stretch);
+}
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, fragS * fragp)
+{
+ if (score3)
+ return s3_convert_frag (abfd, sec, fragp);
+ else
+ return s7_convert_frag (abfd, sec, fragp);
+}
+
+long
+md_pcrel_from (fixS * fixP)
+{
+ if (score3)
+ return s3_pcrel_from (fixP);
+ else
+ return s7_pcrel_from (fixP);
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ if (score3)
+ return s3_section_align (segment, size);
+ else
+ return s7_section_align (segment, size);
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ if (score3)
+ return s3_apply_fix (fixP, valP, seg);
+ else
+ return s7_apply_fix (fixP, valP, seg);
+}
+
+/* Translate internal representation of relocation info to BFD target format. */
+arelent **
+tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ if (score3)
+ return s3_gen_reloc (section, fixp);
+ else
+ return s7_gen_reloc (section, fixp);
+}
+
+void
+md_begin (void)
+{
+ s3_begin ();
+ s7_begin ();
+}
+
+static void
+score_set_mach (const char *arg)
+{
+ if (strcmp (arg, MARCH_SCORE3) == 0)
+ {
+ score3 = 1;
+ score7 = 0;
+ s3_score3d = 1;
+ }
+ else if (strcmp (arg, MARCH_SCORE7) == 0)
+ {
+ score3 = 0;
+ score7 = 1;
+ s7_score7d = 1;
+ s7_university_version = 0;
+ s7_vector_size = s7_SCORE7_PIPELINE;
+ }
+ else if (strcmp (arg, MARCH_SCORE5) == 0)
+ {
+ score3 = 0;
+ score7 = 1;
+ s7_score7d = 1;
+ s7_university_version = 0;
+ s7_vector_size = s7_SCORE5_PIPELINE;
+ }
+ else if (strcmp (arg, MARCH_SCORE5U) == 0)
+ {
+ score3 = 0;
+ score7 = 1;
+ s7_score7d = 1;
+ s7_university_version = 1;
+ s7_vector_size = s7_SCORE5_PIPELINE;
+ }
+ else
+ {
+ as_bad (_("unknown architecture `%s'\n"), arg);
+ }
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+#ifdef OPTION_EB
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+#endif
+#ifdef OPTION_EL
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+#endif
+ case OPTION_FIXDD:
+ s3_fix_data_dependency = 1;
+ s7_fix_data_dependency = 1;
+ break;
+ case OPTION_NWARN:
+ s3_warn_fix_data_dependency = 0;
+ s7_warn_fix_data_dependency = 0;
+ break;
+ case OPTION_SCORE5:
+ score3 = 0;
+ score7 = 1;
+ s7_university_version = 0;
+ s7_vector_size = s7_SCORE5_PIPELINE;
+ break;
+ case OPTION_SCORE5U:
+ score3 = 0;
+ score7 = 1;
+ s7_university_version = 1;
+ s7_vector_size = s7_SCORE5_PIPELINE;
+ break;
+ case OPTION_SCORE7:
+ score3 = 0;
+ score7 = 1;
+ s7_score7d = 1;
+ s7_university_version = 0;
+ s7_vector_size = s7_SCORE7_PIPELINE;
+ break;
+ case OPTION_SCORE3:
+ score3 = 1;
+ score7 = 0;
+ s3_score3d = 1;
+ break;
+ case OPTION_R1:
+ s3_nor1 = 0;
+ s7_nor1 = 0;
+ break;
+ case 'G':
+ s3_g_switch_value = atoi (arg);
+ s7_g_switch_value = atoi (arg);
+ break;
+ case OPTION_O0:
+ s3_g_opt = 0;
+ s7_g_opt = 0;
+ break;
+ case OPTION_SCORE_VERSION:
+ printf (_("Sunplus-v2-0-0-20060510\n"));
+ break;
+ case OPTION_PIC:
+ s3_score_pic = s3_NO_PIC; /* Score3 doesn't support PIC now. */
+ s7_score_pic = s7_PIC;
+ s3_g_switch_value = 0; /* Must set -G num as 0 to generate s3_PIC code. */
+ s7_g_switch_value = 0; /* Must set -G num as 0 to generate s7_PIC code. */
+ break;
+ case OPTION_MARCH:
+ score_set_mach (arg);
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE * fp)
+{
+ fprintf (fp, _(" Score-specific assembler options:\n"));
+#ifdef OPTION_EB
+ fprintf (fp, _("\
+ -EB\t\tassemble code for a big-endian cpu\n"));
+#endif
+
+#ifdef OPTION_EL
+ fprintf (fp, _("\
+ -EL\t\tassemble code for a little-endian cpu\n"));
+#endif
+
+ fprintf (fp, _("\
+ -FIXDD\t\tassemble code for fix data dependency\n"));
+ fprintf (fp, _("\
+ -NWARN\t\tassemble code for no warning message for fix data dependency\n"));
+ fprintf (fp, _("\
+ -SCORE5\t\tassemble code for target is SCORE5\n"));
+ fprintf (fp, _("\
+ -SCORE5U\tassemble code for target is SCORE5U\n"));
+ fprintf (fp, _("\
+ -SCORE7\t\tassemble code for target is SCORE7, this is default setting\n"));
+ fprintf (fp, _("\
+ -SCORE3\t\tassemble code for target is SCORE3\n"));
+ fprintf (fp, _("\
+ -march=score7\tassemble code for target is SCORE7, this is default setting\n"));
+ fprintf (fp, _("\
+ -march=score3\tassemble code for target is SCORE3\n"));
+ fprintf (fp, _("\
+ -USE_R1\t\tassemble code for no warning message when using temp register r1\n"));
+ fprintf (fp, _("\
+ -KPIC\t\tassemble code for PIC\n"));
+ fprintf (fp, _("\
+ -O0\t\tassembler will not perform any optimizations\n"));
+ fprintf (fp, _("\
+ -G gpnum\tassemble code for setting gpsize and default is 8 byte\n"));
+ fprintf (fp, _("\
+ -V \t\tSunplus release version \n"));
+}
diff --git a/gas/config/tc-score.h b/gas/config/tc-score.h
new file mode 100644
index 0000000..5b11f30
--- /dev/null
+++ b/gas/config/tc-score.h
@@ -0,0 +1,78 @@
+/* tc-score.h -- Score specific file for assembler
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Contributed by:
+ Brain.lin (brain.lin@sunplusct.com)
+ Mei Ligang (ligang@sunnorth.com.cn)
+ Pei-Lin Tsai (pltsai@sunplus.com)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef TC_SCORE
+#define TC_SCORE
+
+#define TARGET_ARCH bfd_arch_score
+#define WORKING_DOT_WORD
+#define DIFF_EXPR_OK
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 2
+#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4)
+
+#define md_undefined_symbol(name) NULL
+
+#define TARGET_FORMAT (target_big_endian ? "elf32-bigscore" : "elf32-littlescore")
+
+#define md_relax_frag(segment, fragp, stretch) score_relax_frag (segment, fragp, stretch)
+extern int score_relax_frag (asection *, struct frag *, long);
+
+/* #define md_frag_check(fragp) score_frag_check (fragp) */
+extern void score_frag_check (fragS *);
+
+#define TC_VALIDATE_FIX(FIXP, SEGTYPE, SKIP) score_validate_fix (FIXP)
+extern void score_validate_fix (struct fix *);
+
+#define TC_FORCE_RELOCATION(FIXP) score_force_relocation (FIXP)
+extern int score_force_relocation (struct fix *);
+
+#define tc_fix_adjustable(fixp) score_fix_adjustable (fixp)
+extern bfd_boolean score_fix_adjustable (struct fix *);
+
+#define elf_tc_final_processing score_elf_final_processing
+extern void score_elf_final_processing (void);
+
+struct score_tc_frag_data
+{
+ unsigned int is_insn;
+ struct fix *fixp;
+};
+
+#define TC_FRAG_TYPE struct score_tc_frag_data
+
+#define TC_FRAG_INIT(FRAGP) \
+ do \
+ { \
+ (FRAGP)->tc_frag_data.is_insn = (((FRAGP)->fr_type == rs_machine_dependent) ? 1 : 0); \
+ } \
+ while (0)
+
+#ifdef OBJ_ELF
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+#else
+#define GLOBAL_OFFSET_TABLE_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+
+#endif /*TC_SCORE */
diff --git a/gas/config/tc-score7.c b/gas/config/tc-score7.c
new file mode 100644
index 0000000..ae15a04
--- /dev/null
+++ b/gas/config/tc-score7.c
@@ -0,0 +1,6972 @@
+/* tc-score7.c -- Assembler for Score7
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Contributed by:
+ Brain.lin (brain.lin@sunplusct.com)
+ Mei Ligang (ligang@sunnorth.com.cn)
+ Pei-Lin Tsai (pltsai@sunplus.com)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include "config.h"
+#include "subsegs.h"
+#include "safe-ctype.h"
+#include "opcode/score-inst.h"
+#include "struc-symbol.h"
+#include "libiberty.h"
+
+#ifdef OBJ_ELF
+#include "elf/score.h"
+#include "dwarf2dbg.h"
+#endif
+
+static void s7_do_ldst_insn (char *);
+static void s7_do_crdcrscrsimm5 (char *);
+static void s7_do_ldst_unalign (char *);
+static void s7_do_ldst_atomic (char *);
+static void s7_do_ldst_cop (char *);
+static void s7_do_macro_li_rdi32 (char *);
+static void s7_do_macro_la_rdi32 (char *);
+static void s7_do_macro_rdi32hi (char *);
+static void s7_do_macro_rdi32lo (char *);
+static void s7_do_macro_mul_rdrsrs (char *);
+static void s7_do_macro_ldst_label (char *);
+static void s7_do_branch (char *);
+static void s7_do_jump (char *);
+static void s7_do_empty (char *);
+static void s7_do_rdrsrs (char *);
+static void s7_do_rdsi16 (char *);
+static void s7_do_rdrssi14 (char *);
+static void s7_do_sub_rdsi16 (char *);
+static void s7_do_sub_rdrssi14 (char *);
+static void s7_do_rdrsi5 (char *);
+static void s7_do_rdrsi14 (char *);
+static void s7_do_rdi16 (char *);
+static void s7_do_xrsi5 (char *);
+static void s7_do_rdrs (char *);
+static void s7_do_rdxrs (char *);
+static void s7_do_rsrs (char *);
+static void s7_do_rdcrs (char *);
+static void s7_do_rdsrs (char *);
+static void s7_do_rd (char *);
+static void s7_do_rs (char *);
+static void s7_do_i15 (char *);
+static void s7_do_xi5x (char *);
+static void s7_do_ceinst (char *);
+static void s7_do_cache (char *);
+static void s7_do16_rdrs (char *);
+static void s7_do16_rs (char *);
+static void s7_do16_xrs (char *);
+static void s7_do16_mv_rdrs (char *);
+static void s7_do16_hrdrs (char *);
+static void s7_do16_rdhrs (char *);
+static void s7_do16_rdi4 (char *);
+static void s7_do16_rdi5 (char *);
+static void s7_do16_xi5 (char *);
+static void s7_do16_ldst_insn (char *);
+static void s7_do16_ldst_imm_insn (char *);
+static void s7_do16_push_pop (char *);
+static void s7_do16_branch (char *);
+static void s7_do16_jump (char *);
+static void s7_do_rdi16_pic (char *);
+static void s7_do_addi_s_pic (char *);
+static void s7_do_addi_u_pic (char *);
+static void s7_do_lw_pic (char *);
+
+#define s7_GP 28
+#define s7_PIC_CALL_REG 29
+#define s7_MAX_LITERAL_POOL_SIZE 1024
+#define s7_FAIL 0x80000000
+#define s7_SUCCESS 0
+#define s7_INSN_SIZE 4
+#define s7_INSN16_SIZE 2
+#define s7_RELAX_INST_NUM 3
+
+/* For score5u : div/mul will pop warning message, mmu/alw/asw will pop error message. */
+#define s7_BAD_ARGS _("bad arguments to instruction")
+#define s7_ERR_FOR_SCORE5U_MUL_DIV _("div / mul are reserved instructions")
+#define s7_ERR_FOR_SCORE5U_MMU _("This architecture doesn't support mmu")
+#define s7_ERR_FOR_SCORE5U_ATOMIC _("This architecture doesn't support atomic instruction")
+#define s7_BAD_SKIP_COMMA s7_BAD_ARGS
+#define s7_BAD_GARBAGE _("garbage following instruction");
+
+#define s7_skip_whitespace(str) while (*(str) == ' ') ++(str)
+
+/* The name of the readonly data section. */
+#define s7_RDATA_SECTION_NAME (OUTPUT_FLAVOR == bfd_target_aout_flavour \
+ ? ".data" \
+ : OUTPUT_FLAVOR == bfd_target_ecoff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_coff_flavour \
+ ? ".rdata" \
+ : OUTPUT_FLAVOR == bfd_target_elf_flavour \
+ ? ".rodata" \
+ : (abort (), ""))
+
+#define s7_RELAX_ENCODE(old, new, type, reloc1, reloc2, opt) \
+ ((relax_substateT) \
+ (((old) << 23) \
+ | ((new) << 16) \
+ | ((type) << 9) \
+ | ((reloc1) << 5) \
+ | ((reloc2) << 1) \
+ | ((opt) ? 1 : 0)))
+
+#define s7_RELAX_OLD(i) (((i) >> 23) & 0x7f)
+#define s7_RELAX_NEW(i) (((i) >> 16) & 0x7f)
+#define s7_RELAX_TYPE(i) (((i) >> 9) & 0x7f)
+#define s7_RELAX_RELOC1(i) ((valueT) ((i) >> 5) & 0xf)
+#define s7_RELAX_RELOC2(i) ((valueT) ((i) >> 1) & 0xf)
+#define s7_RELAX_OPT(i) ((i) & 1)
+#define s7_RELAX_OPT_CLEAR(i) ((i) & ~1)
+
+#define s7_SET_INSN_ERROR(s) (s7_inst.error = (s))
+#define s7_INSN_IS_PCE_P(s) (strstr (str, "||") != NULL)
+
+#define s7_GET_INSN_CLASS(type) (s7_get_insn_class_from_type (type))
+
+#define s7_GET_INSN_SIZE(type) ((s7_GET_INSN_CLASS (type) == INSN_CLASS_16) \
+ ? s7_INSN16_SIZE : s7_INSN_SIZE)
+
+#define s7_MAX_LITTLENUMS 6
+#define s7_INSN_NAME_LEN 16
+
+/* Relax will need some padding for alignment. */
+#define s7_RELAX_PAD_BYTE 3
+
+#define s7_USE_GLOBAL_POINTER_OPT 1
+
+
+
+/* Enumeration matching entries in table above. */
+enum s7_score_reg_type
+{
+ s7_REG_TYPE_SCORE = 0,
+#define REG_TYPE_FIRST s7_REG_TYPE_SCORE
+ s7_REG_TYPE_SCORE_SR = 1,
+ s7_REG_TYPE_SCORE_CR = 2,
+ s7_REG_TYPE_MAX = 3
+};
+
+enum s7_score_pic_level
+{
+ s7_NO_PIC,
+ s7_PIC
+};
+static enum s7_score_pic_level s7_score_pic = s7_NO_PIC;
+
+enum s7_insn_type_for_dependency
+{
+ s7_D_pce,
+ s7_D_cond_br,
+ s7_D_cond_mv,
+ s7_D_cached,
+ s7_D_cachei,
+ s7_D_ldst,
+ s7_D_ldcombine,
+ s7_D_mtcr,
+ s7_D_mfcr,
+ s7_D_mfsr,
+ s7_D_mftlb,
+ s7_D_mtptlb,
+ s7_D_mtrtlb,
+ s7_D_stlb,
+ s7_D_all_insn
+};
+
+struct s7_insn_to_dependency
+{
+ char *insn_name;
+ enum s7_insn_type_for_dependency type;
+};
+
+struct s7_data_dependency
+{
+ enum s7_insn_type_for_dependency pre_insn_type;
+ char pre_reg[6];
+ enum s7_insn_type_for_dependency cur_insn_type;
+ char cur_reg[6];
+ int bubblenum_7;
+ int bubblenum_5;
+ int warn_or_error; /* warning - 0; error - 1 */
+};
+
+static const struct s7_insn_to_dependency s7_insn_to_dependency_table[] =
+{
+ /* pce instruction. */
+ {"pce", s7_D_pce},
+ /* conditional branch instruction. */
+ {"bcs", s7_D_cond_br},
+ {"bcc", s7_D_cond_br},
+ {"bgtu", s7_D_cond_br},
+ {"bleu", s7_D_cond_br},
+ {"beq", s7_D_cond_br},
+ {"bne", s7_D_cond_br},
+ {"bgt", s7_D_cond_br},
+ {"ble", s7_D_cond_br},
+ {"bge", s7_D_cond_br},
+ {"blt", s7_D_cond_br},
+ {"bmi", s7_D_cond_br},
+ {"bpl", s7_D_cond_br},
+ {"bvs", s7_D_cond_br},
+ {"bvc", s7_D_cond_br},
+ {"bcsl", s7_D_cond_br},
+ {"bccl", s7_D_cond_br},
+ {"bgtul", s7_D_cond_br},
+ {"bleul", s7_D_cond_br},
+ {"beql", s7_D_cond_br},
+ {"bnel", s7_D_cond_br},
+ {"bgtl", s7_D_cond_br},
+ {"blel", s7_D_cond_br},
+ {"bgel", s7_D_cond_br},
+ {"bltl", s7_D_cond_br},
+ {"bmil", s7_D_cond_br},
+ {"bpll", s7_D_cond_br},
+ {"bvsl", s7_D_cond_br},
+ {"bvcl", s7_D_cond_br},
+ {"bcs!", s7_D_cond_br},
+ {"bcc!", s7_D_cond_br},
+ {"bgtu!", s7_D_cond_br},
+ {"bleu!", s7_D_cond_br},
+ {"beq!", s7_D_cond_br},
+ {"bne!", s7_D_cond_br},
+ {"bgt!", s7_D_cond_br},
+ {"ble!", s7_D_cond_br},
+ {"bge!", s7_D_cond_br},
+ {"blt!", s7_D_cond_br},
+ {"bmi!", s7_D_cond_br},
+ {"bpl!", s7_D_cond_br},
+ {"bvs!", s7_D_cond_br},
+ {"bvc!", s7_D_cond_br},
+ {"brcs", s7_D_cond_br},
+ {"brcc", s7_D_cond_br},
+ {"brgtu", s7_D_cond_br},
+ {"brleu", s7_D_cond_br},
+ {"breq", s7_D_cond_br},
+ {"brne", s7_D_cond_br},
+ {"brgt", s7_D_cond_br},
+ {"brle", s7_D_cond_br},
+ {"brge", s7_D_cond_br},
+ {"brlt", s7_D_cond_br},
+ {"brmi", s7_D_cond_br},
+ {"brpl", s7_D_cond_br},
+ {"brvs", s7_D_cond_br},
+ {"brvc", s7_D_cond_br},
+ {"brcsl", s7_D_cond_br},
+ {"brccl", s7_D_cond_br},
+ {"brgtul", s7_D_cond_br},
+ {"brleul", s7_D_cond_br},
+ {"breql", s7_D_cond_br},
+ {"brnel", s7_D_cond_br},
+ {"brgtl", s7_D_cond_br},
+ {"brlel", s7_D_cond_br},
+ {"brgel", s7_D_cond_br},
+ {"brltl", s7_D_cond_br},
+ {"brmil", s7_D_cond_br},
+ {"brpll", s7_D_cond_br},
+ {"brvsl", s7_D_cond_br},
+ {"brvcl", s7_D_cond_br},
+ {"brcs!", s7_D_cond_br},
+ {"brcc!", s7_D_cond_br},
+ {"brgtu!", s7_D_cond_br},
+ {"brleu!", s7_D_cond_br},
+ {"breq!", s7_D_cond_br},
+ {"brne!", s7_D_cond_br},
+ {"brgt!", s7_D_cond_br},
+ {"brle!", s7_D_cond_br},
+ {"brge!", s7_D_cond_br},
+ {"brlt!", s7_D_cond_br},
+ {"brmi!", s7_D_cond_br},
+ {"brpl!", s7_D_cond_br},
+ {"brvs!", s7_D_cond_br},
+ {"brvc!", s7_D_cond_br},
+ {"brcsl!", s7_D_cond_br},
+ {"brccl!", s7_D_cond_br},
+ {"brgtul!", s7_D_cond_br},
+ {"brleul!", s7_D_cond_br},
+ {"breql!", s7_D_cond_br},
+ {"brnel!", s7_D_cond_br},
+ {"brgtl!", s7_D_cond_br},
+ {"brlel!", s7_D_cond_br},
+ {"brgel!", s7_D_cond_br},
+ {"brltl!", s7_D_cond_br},
+ {"brmil!", s7_D_cond_br},
+ {"brpll!", s7_D_cond_br},
+ {"brvsl!", s7_D_cond_br},
+ {"brvcl!", s7_D_cond_br},
+ /* conditional move instruction. */
+ {"mvcs", s7_D_cond_mv},
+ {"mvcc", s7_D_cond_mv},
+ {"mvgtu", s7_D_cond_mv},
+ {"mvleu", s7_D_cond_mv},
+ {"mveq", s7_D_cond_mv},
+ {"mvne", s7_D_cond_mv},
+ {"mvgt", s7_D_cond_mv},
+ {"mvle", s7_D_cond_mv},
+ {"mvge", s7_D_cond_mv},
+ {"mvlt", s7_D_cond_mv},
+ {"mvmi", s7_D_cond_mv},
+ {"mvpl", s7_D_cond_mv},
+ {"mvvs", s7_D_cond_mv},
+ {"mvvc", s7_D_cond_mv},
+ /* move spectial instruction. */
+ {"mtcr", s7_D_mtcr},
+ {"mftlb", s7_D_mftlb},
+ {"mtptlb", s7_D_mtptlb},
+ {"mtrtlb", s7_D_mtrtlb},
+ {"stlb", s7_D_stlb},
+ {"mfcr", s7_D_mfcr},
+ {"mfsr", s7_D_mfsr},
+ /* cache instruction. */
+ {"cache 8", s7_D_cached},
+ {"cache 9", s7_D_cached},
+ {"cache 10", s7_D_cached},
+ {"cache 11", s7_D_cached},
+ {"cache 12", s7_D_cached},
+ {"cache 13", s7_D_cached},
+ {"cache 14", s7_D_cached},
+ {"cache 24", s7_D_cached},
+ {"cache 26", s7_D_cached},
+ {"cache 27", s7_D_cached},
+ {"cache 29", s7_D_cached},
+ {"cache 30", s7_D_cached},
+ {"cache 31", s7_D_cached},
+ {"cache 0", s7_D_cachei},
+ {"cache 1", s7_D_cachei},
+ {"cache 2", s7_D_cachei},
+ {"cache 3", s7_D_cachei},
+ {"cache 4", s7_D_cachei},
+ {"cache 16", s7_D_cachei},
+ {"cache 17", s7_D_cachei},
+ /* load/store instruction. */
+ {"lb", s7_D_ldst},
+ {"lbu", s7_D_ldst},
+ {"lbu!", s7_D_ldst},
+ {"lbup!", s7_D_ldst},
+ {"lh", s7_D_ldst},
+ {"lhu", s7_D_ldst},
+ {"lh!", s7_D_ldst},
+ {"lhp!", s7_D_ldst},
+ {"lw", s7_D_ldst},
+ {"lw!", s7_D_ldst},
+ {"lwp!", s7_D_ldst},
+ {"sb", s7_D_ldst},
+ {"sb!", s7_D_ldst},
+ {"sbp!", s7_D_ldst},
+ {"sh", s7_D_ldst},
+ {"sh!", s7_D_ldst},
+ {"shp!", s7_D_ldst},
+ {"sw", s7_D_ldst},
+ {"sw!", s7_D_ldst},
+ {"swp!", s7_D_ldst},
+ {"alw", s7_D_ldst},
+ {"asw", s7_D_ldst},
+ {"push!", s7_D_ldst},
+ {"pushhi!", s7_D_ldst},
+ {"pop!", s7_D_ldst},
+ {"pophi!", s7_D_ldst},
+ {"ldc1", s7_D_ldst},
+ {"ldc2", s7_D_ldst},
+ {"ldc3", s7_D_ldst},
+ {"stc1", s7_D_ldst},
+ {"stc2", s7_D_ldst},
+ {"stc3", s7_D_ldst},
+ {"scb", s7_D_ldst},
+ {"scw", s7_D_ldst},
+ {"sce", s7_D_ldst},
+ /* load combine instruction. */
+ {"lcb", s7_D_ldcombine},
+ {"lcw", s7_D_ldcombine},
+ {"lce", s7_D_ldcombine},
+};
+
+static const struct s7_data_dependency s7_data_dependency_table[] =
+{
+ /* Condition register. */
+ {s7_D_mtcr, "cr1", s7_D_pce, "", 2, 1, 0},
+ {s7_D_mtcr, "cr1", s7_D_cond_br, "", 1, 0, 1},
+ {s7_D_mtcr, "cr1", s7_D_cond_mv, "", 1, 0, 1},
+ /* Status regiser. */
+ {s7_D_mtcr, "cr0", s7_D_all_insn, "", 5, 4, 0},
+ /* CCR regiser. */
+ {s7_D_mtcr, "cr4", s7_D_all_insn, "", 6, 5, 0},
+ /* EntryHi/EntryLo register. */
+ {s7_D_mftlb, "", s7_D_mtptlb, "", 1, 1, 1},
+ {s7_D_mftlb, "", s7_D_mtrtlb, "", 1, 1, 1},
+ {s7_D_mftlb, "", s7_D_stlb, "", 1, 1,1},
+ {s7_D_mftlb, "", s7_D_mfcr, "cr11", 1, 1, 1},
+ {s7_D_mftlb, "", s7_D_mfcr, "cr12", 1, 1, 1},
+ /* Index register. */
+ {s7_D_stlb, "", s7_D_mtptlb, "", 1, 1, 1},
+ {s7_D_stlb, "", s7_D_mftlb, "", 1, 1, 1},
+ {s7_D_stlb, "", s7_D_mfcr, "cr8", 2, 2, 1},
+ /* Cache. */
+ {s7_D_cached, "", s7_D_ldst, "", 1, 1, 0},
+ {s7_D_cached, "", s7_D_ldcombine, "", 1, 1, 0},
+ {s7_D_cachei, "", s7_D_all_insn, "", 5, 4, 0},
+ /* Load combine. */
+ {s7_D_ldcombine, "", s7_D_mfsr, "sr1", 3, 3, 1},
+};
+
+
+
+/* Used to contain constructed error messages. */
+static char s7_err_msg[255];
+static int s7_fix_data_dependency = 0;
+static int s7_warn_fix_data_dependency = 1;
+
+static int s7_in_my_get_expression = 0;
+
+/* Default, pop warning message when using r1. */
+static int s7_nor1 = 1;
+
+/* Default will do instruction relax, -O0 will set s7_g_opt = 0. */
+static unsigned int s7_g_opt = 1;
+
+/* The size of the small data section. */
+static unsigned int s7_g_switch_value = 8;
+
+static segT s7_pdr_seg;
+
+struct s7_score_it
+{
+ char name[s7_INSN_NAME_LEN];
+ unsigned long instruction;
+ unsigned long relax_inst;
+ int size;
+ int relax_size;
+ enum score_insn_type type;
+ char str[s7_MAX_LITERAL_POOL_SIZE];
+ const char *error;
+ int bwarn;
+ char reg[s7_INSN_NAME_LEN];
+ struct
+ {
+ bfd_reloc_code_real_type type;
+ expressionS exp;
+ int pc_rel;
+ }reloc;
+};
+static struct s7_score_it s7_inst;
+
+typedef struct proc
+{
+ symbolS *isym;
+ unsigned long reg_mask;
+ unsigned long reg_offset;
+ unsigned long fpreg_mask;
+ unsigned long leaf;
+ unsigned long frame_offset;
+ unsigned long frame_reg;
+ unsigned long pc_reg;
+} s7_procS;
+static s7_procS s7_cur_proc;
+static s7_procS *s7_cur_proc_ptr;
+static int s7_numprocs;
+
+/* Structure for a hash table entry for a register. */
+struct s7_reg_entry
+{
+ const char *name;
+ int number;
+};
+
+static const struct s7_reg_entry s7_score_rn_table[] =
+{
+ {"r0", 0}, {"r1", 1}, {"r2", 2}, {"r3", 3},
+ {"r4", 4}, {"r5", 5}, {"r6", 6}, {"r7", 7},
+ {"r8", 8}, {"r9", 9}, {"r10", 10}, {"r11", 11},
+ {"r12", 12}, {"r13", 13}, {"r14", 14}, {"r15", 15},
+ {"r16", 16}, {"r17", 17}, {"r18", 18}, {"r19", 19},
+ {"r20", 20}, {"r21", 21}, {"r22", 22}, {"r23", 23},
+ {"r24", 24}, {"r25", 25}, {"r26", 26}, {"r27", 27},
+ {"r28", 28}, {"r29", 29}, {"r30", 30}, {"r31", 31},
+ {NULL, 0}
+};
+
+static const struct s7_reg_entry s7_score_srn_table[] =
+{
+ {"sr0", 0}, {"sr1", 1}, {"sr2", 2},
+ {NULL, 0}
+};
+
+static const struct s7_reg_entry s7_score_crn_table[] =
+{
+ {"cr0", 0}, {"cr1", 1}, {"cr2", 2}, {"cr3", 3},
+ {"cr4", 4}, {"cr5", 5}, {"cr6", 6}, {"cr7", 7},
+ {"cr8", 8}, {"cr9", 9}, {"cr10", 10}, {"cr11", 11},
+ {"cr12", 12}, {"cr13", 13}, {"cr14", 14}, {"cr15", 15},
+ {"cr16", 16}, {"cr17", 17}, {"cr18", 18}, {"cr19", 19},
+ {"cr20", 20}, {"cr21", 21}, {"cr22", 22}, {"cr23", 23},
+ {"cr24", 24}, {"cr25", 25}, {"cr26", 26}, {"cr27", 27},
+ {"cr28", 28}, {"cr29", 29}, {"cr30", 30}, {"cr31", 31},
+ {NULL, 0}
+};
+
+struct s7_reg_map
+{
+ const struct s7_reg_entry *names;
+ int max_regno;
+ struct hash_control *htab;
+ const char *expected;
+};
+
+static struct s7_reg_map s7_all_reg_maps[] =
+{
+ {s7_score_rn_table, 31, NULL, N_("S+core register expected")},
+ {s7_score_srn_table, 2, NULL, N_("S+core special-register expected")},
+ {s7_score_crn_table, 31, NULL, N_("S+core co-processor register expected")},
+};
+
+static struct hash_control *s7_score_ops_hsh = NULL;
+static struct hash_control *s7_dependency_insn_hsh = NULL;
+
+
+struct s7_datafield_range
+{
+ int data_type;
+ int bits;
+ int range[2];
+};
+
+static struct s7_datafield_range s7_score_df_range[] =
+{
+ {_IMM4, 4, {0, (1 << 4) - 1}}, /* ( 0 ~ 15 ) */
+ {_IMM5, 5, {0, (1 << 5) - 1}}, /* ( 0 ~ 31 ) */
+ {_IMM8, 8, {0, (1 << 8) - 1}}, /* ( 0 ~ 255 ) */
+ {_IMM14, 14, {0, (1 << 14) - 1}}, /* ( 0 ~ 16383) */
+ {_IMM15, 15, {0, (1 << 15) - 1}}, /* ( 0 ~ 32767) */
+ {_IMM16, 16, {0, (1 << 16) - 1}}, /* ( 0 ~ 65535) */
+ {_SIMM10, 10, {-(1 << 9), (1 << 9) - 1}}, /* ( -512 ~ 511 ) */
+ {_SIMM12, 12, {-(1 << 11), (1 << 11) - 1}}, /* ( -2048 ~ 2047 ) */
+ {_SIMM14, 14, {-(1 << 13), (1 << 13) - 1}}, /* ( -8192 ~ 8191 ) */
+ {_SIMM15, 15, {-(1 << 14), (1 << 14) - 1}}, /* (-16384 ~ 16383) */
+ {_SIMM16, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_SIMM14_NEG, 14, {-(1 << 13), (1 << 13) - 1}}, /* ( -8191 ~ 8192 ) */
+ {_IMM16_NEG, 16, {0, (1 << 16) - 1}}, /* (-65535 ~ 0 ) */
+ {_SIMM16_NEG, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM20, 20, {0, (1 << 20) - 1}},
+ {_IMM25, 25, {0, (1 << 25) - 1}},
+ {_DISP8div2, 8, {-(1 << 8), (1 << 8) - 1}}, /* ( -256 ~ 255 ) */
+ {_DISP11div2, 11, {0, 0}},
+ {_DISP19div2, 19, {-(1 << 19), (1 << 19) - 1}}, /* (-524288 ~ 524287) */
+ {_DISP24div2, 24, {0, 0}},
+ {_VALUE, 32, {0, ((unsigned int)1 << 31) - 1}},
+ {_VALUE_HI16, 16, {0, (1 << 16) - 1}},
+ {_VALUE_LO16, 16, {0, (1 << 16) - 1}},
+ {_VALUE_LDST_LO16, 16, {0, (1 << 16) - 1}},
+ {_SIMM16_LA, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM5_RSHIFT_1, 5, {0, (1 << 6) - 1}}, /* ( 0 ~ 63 ) */
+ {_IMM5_RSHIFT_2, 5, {0, (1 << 7) - 1}}, /* ( 0 ~ 127 ) */
+ {_SIMM16_LA_POS, 16, {0, (1 << 15) - 1}}, /* ( 0 ~ 32767) */
+ {_IMM5_RANGE_8_31, 5, {8, 31}}, /* But for cop0 the valid data : (8 ~ 31). */
+ {_IMM10_RSHIFT_2, 10, {-(1 << 11), (1 << 11) - 1}}, /* For ldc#, stc#. */
+ {_SIMM10, 10, {0, (1 << 10) - 1}}, /* ( -1024 ~ 1023 ) */
+ {_SIMM12, 12, {0, (1 << 12) - 1}}, /* ( -2048 ~ 2047 ) */
+ {_SIMM14, 14, {0, (1 << 14) - 1}}, /* ( -8192 ~ 8191 ) */
+ {_SIMM15, 15, {0, (1 << 15) - 1}}, /* (-16384 ~ 16383) */
+ {_SIMM16, 16, {0, (1 << 16) - 1}}, /* (-65536 ~ 65536) */
+ {_SIMM14_NEG, 14, {0, (1 << 16) - 1}}, /* ( -8191 ~ 8192 ) */
+ {_IMM16_NEG, 16, {0, (1 << 16) - 1}}, /* ( 65535 ~ 0 ) */
+ {_SIMM16_NEG, 16, {0, (1 << 16) - 1}}, /* ( 65535 ~ 0 ) */
+ {_IMM20, 20, {0, (1 << 20) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM25, 25, {0, (1 << 25) - 1}}, /* (-32768 ~ 32767) */
+ {_GP_IMM15, 15, {0, (1 << 15) - 1}}, /* ( 0 ~ 65535) */
+ {_GP_IMM14, 14, {0, (1 << 14) - 1}}, /* ( 0 ~ 65535) */
+ {_SIMM16_pic, 16, {-(1 << 15), (1 << 15) - 1}}, /* (-32768 ~ 32767) */
+ {_IMM16_LO16_pic, 16, {0, (1 << 16) - 1}}, /* ( 65535 ~ 0 ) */
+ {_IMM16_pic, 16, {0, (1 << 16) - 1}}, /* ( 0 ~ 65535) */
+};
+
+
+struct s7_asm_opcode
+{
+ /* Instruction name. */
+ const char *template_name;
+
+ /* Instruction Opcode. */
+ bfd_vma value;
+
+ /* Instruction bit mask. */
+ bfd_vma bitmask;
+
+ /* Relax instruction opcode. 0x8000 imply no relaxation. */
+ bfd_vma relax_value;
+
+ /* Instruction type. */
+ enum score_insn_type type;
+
+ /* Function to call to parse args. */
+ void (*parms) (char *);
+};
+
+static const struct s7_asm_opcode s7_score_ldst_insns[] =
+{
+ {"lw", 0x20000000, 0x3e000000, 0x2008, Rd_rvalueRs_SI15, s7_do_ldst_insn},
+ {"lw", 0x06000000, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s7_do_ldst_insn},
+ {"lw", 0x0e000000, 0x3e000007, 0x200a, Rd_rvalueRs_postSI12, s7_do_ldst_insn},
+ {"lh", 0x22000000, 0x3e000000, 0x2009, Rd_rvalueRs_SI15, s7_do_ldst_insn},
+ {"lh", 0x06000001, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s7_do_ldst_insn},
+ {"lh", 0x0e000001, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s7_do_ldst_insn},
+ {"lhu", 0x24000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, s7_do_ldst_insn},
+ {"lhu", 0x06000002, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s7_do_ldst_insn},
+ {"lhu", 0x0e000002, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s7_do_ldst_insn},
+ {"lb", 0x26000000, 0x3e000000, 0x8000, Rd_rvalueRs_SI15, s7_do_ldst_insn},
+ {"lb", 0x06000003, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s7_do_ldst_insn},
+ {"lb", 0x0e000003, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s7_do_ldst_insn},
+ {"sw", 0x28000000, 0x3e000000, 0x200c, Rd_lvalueRs_SI15, s7_do_ldst_insn},
+ {"sw", 0x06000004, 0x3e000007, 0x200e, Rd_lvalueRs_preSI12, s7_do_ldst_insn},
+ {"sw", 0x0e000004, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, s7_do_ldst_insn},
+ {"sh", 0x2a000000, 0x3e000000, 0x200d, Rd_lvalueRs_SI15, s7_do_ldst_insn},
+ {"sh", 0x06000005, 0x3e000007, 0x8000, Rd_lvalueRs_preSI12, s7_do_ldst_insn},
+ {"sh", 0x0e000005, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, s7_do_ldst_insn},
+ {"lbu", 0x2c000000, 0x3e000000, 0x200b, Rd_rvalueRs_SI15, s7_do_ldst_insn},
+ {"lbu", 0x06000006, 0x3e000007, 0x8000, Rd_rvalueRs_preSI12, s7_do_ldst_insn},
+ {"lbu", 0x0e000006, 0x3e000007, 0x8000, Rd_rvalueRs_postSI12, s7_do_ldst_insn},
+ {"sb", 0x2e000000, 0x3e000000, 0x200f, Rd_lvalueRs_SI15, s7_do_ldst_insn},
+ {"sb", 0x06000007, 0x3e000007, 0x8000, Rd_lvalueRs_preSI12, s7_do_ldst_insn},
+ {"sb", 0x0e000007, 0x3e000007, 0x8000, Rd_lvalueRs_postSI12, s7_do_ldst_insn},
+};
+
+static const struct s7_asm_opcode s7_score_insns[] =
+{
+ {"abs", 0x3800000a, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"abs.s", 0x3800004b, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"add", 0x00000010, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"add.c", 0x00000011, 0x3e0003ff, 0x2000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"add.s", 0x38000048, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"addc", 0x00000012, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"addc.c", 0x00000013, 0x3e0003ff, 0x0009, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"addi", 0x02000000, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_rdsi16},
+ {"addi.c", 0x02000001, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_rdsi16},
+ {"addis", 0x0a000000, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_rdi16},
+ {"addis.c", 0x0a000001, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_rdi16},
+ {"addri", 0x10000000, 0x3e000001, 0x8000, Rd_Rs_SI14, s7_do_rdrssi14},
+ {"addri.c", 0x10000001, 0x3e000001, 0x8000, Rd_Rs_SI14, s7_do_rdrssi14},
+ {"addc!", 0x0009, 0x700f, 0x00000013, Rd_Rs, s7_do16_rdrs},
+ {"add!", 0x2000, 0x700f, 0x00000011, Rd_Rs, s7_do16_rdrs},
+ {"addei!", 0x6000 , 0x7087, 0x02000001, Rd_I4, s7_do16_rdi4},
+ {"subi", 0x02000000, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_sub_rdsi16},
+ {"subi.c", 0x02000001, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_sub_rdsi16},
+ {"subri", 0x10000000, 0x3e000001, 0x8000, Rd_Rs_SI14, s7_do_sub_rdrssi14},
+ {"subri.c", 0x10000001, 0x3e000001, 0x8000, Rd_Rs_SI14, s7_do_sub_rdrssi14},
+ {"and", 0x00000020, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"and.c", 0x00000021, 0x3e0003ff, 0x2004, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"andi", 0x02080000, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"andi.c", 0x02080001, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"andis", 0x0a080000, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"andis.c", 0x0a080001, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"andri", 0x18000000, 0x3e000001, 0x8000, Rd_Rs_I14, s7_do_rdrsi14},
+ {"andri.c", 0x18000001, 0x3e000001, 0x8000, Rd_Rs_I14, s7_do_rdrsi14},
+ {"and!", 0x2004, 0x700f, 0x00000021, Rd_Rs, s7_do16_rdrs},
+ {"bcs", 0x08000000, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bcc", 0x08000400, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bcnz", 0x08003800, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bcsl", 0x08000001, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bccl", 0x08000401, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bcnzl", 0x08003801, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bcs!", 0x4000, 0x7f00, 0x08000000, PC_DISP8div2, s7_do16_branch},
+ {"bcc!", 0x4100, 0x7f00, 0x08000400, PC_DISP8div2, s7_do16_branch},
+ {"bcnz!", 0x4e00, 0x7f00, 0x08003800, PC_DISP8div2, s7_do16_branch},
+ {"beq", 0x08001000, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"beql", 0x08001001, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"beq!", 0x4400, 0x7f00, 0x08001000, PC_DISP8div2, s7_do16_branch},
+ {"bgtu", 0x08000800, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bgt", 0x08001800, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bge", 0x08002000, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bgtul", 0x08000801, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bgtl", 0x08001801, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bgel", 0x08002001, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bgtu!", 0x4200, 0x7f00, 0x08000800, PC_DISP8div2, s7_do16_branch},
+ {"bgt!", 0x4600, 0x7f00, 0x08001800, PC_DISP8div2, s7_do16_branch},
+ {"bge!", 0x4800, 0x7f00, 0x08002000, PC_DISP8div2, s7_do16_branch},
+ {"bitclr.c", 0x00000029, 0x3e0003ff, 0x6004, Rd_Rs_I5, s7_do_rdrsi5},
+ {"bitrev", 0x3800000c, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"bitset.c", 0x0000002b, 0x3e0003ff, 0x6005, Rd_Rs_I5, s7_do_rdrsi5},
+ {"bittst.c", 0x0000002d, 0x3e0003ff, 0x6006, x_Rs_I5, s7_do_xrsi5},
+ {"bittgl.c", 0x0000002f, 0x3e0003ff, 0x6007, Rd_Rs_I5, s7_do_rdrsi5},
+ {"bitclr!", 0x6004, 0x7007, 0x00000029, Rd_I5, s7_do16_rdi5},
+ {"bitset!", 0x6005, 0x7007, 0x0000002b, Rd_I5, s7_do16_rdi5},
+ {"bittst!", 0x6006, 0x7007, 0x0000002d, Rd_I5, s7_do16_rdi5},
+ {"bittgl!", 0x6007, 0x7007, 0x0000002f, Rd_I5, s7_do16_rdi5},
+ {"bleu", 0x08000c00, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"ble", 0x08001c00, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"blt", 0x08002400, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bleul", 0x08000c01, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"blel", 0x08001c01, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bltl", 0x08002401, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bl", 0x08003c01, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bleu!", 0x4300, 0x7f00, 0x08000c00, PC_DISP8div2, s7_do16_branch},
+ {"ble!", 0x4700, 0x7f00, 0x08001c00, PC_DISP8div2, s7_do16_branch},
+ {"blt!", 0x4900, 0x7f00, 0x08002400, PC_DISP8div2, s7_do16_branch},
+ {"bmi", 0x08002800, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bmil", 0x08002801, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bmi!", 0x00004a00, 0x00007f00, 0x08002800, PC_DISP8div2, s7_do16_branch},
+ {"bne", 0x08001400, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bnel", 0x08001401, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bne!", 0x4500, 0x7f00, 0x08001400, PC_DISP8div2, s7_do16_branch},
+ {"bpl", 0x08002c00, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bpll", 0x08002c01, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bpl!", 0x4b00, 0x7f00, 0x08002c00, PC_DISP8div2, s7_do16_branch},
+ {"brcs", 0x00000008, 0x3e007fff, 0x0004, x_Rs_x, s7_do_rs},
+ {"brcc", 0x00000408, 0x3e007fff, 0x0104, x_Rs_x, s7_do_rs},
+ {"brgtu", 0x00000808, 0x3e007fff, 0x0204, x_Rs_x, s7_do_rs},
+ {"brleu", 0x00000c08, 0x3e007fff, 0x0304, x_Rs_x, s7_do_rs},
+ {"breq", 0x00001008, 0x3e007fff, 0x0404, x_Rs_x, s7_do_rs},
+ {"brne", 0x00001408, 0x3e007fff, 0x0504, x_Rs_x, s7_do_rs},
+ {"brgt", 0x00001808, 0x3e007fff, 0x0604, x_Rs_x, s7_do_rs},
+ {"brle", 0x00001c08, 0x3e007fff, 0x0704, x_Rs_x, s7_do_rs},
+ {"brge", 0x00002008, 0x3e007fff, 0x0804, x_Rs_x, s7_do_rs},
+ {"brlt", 0x00002408, 0x3e007fff, 0x0904, x_Rs_x, s7_do_rs},
+ {"brmi", 0x00002808, 0x3e007fff, 0x0a04, x_Rs_x, s7_do_rs},
+ {"brpl", 0x00002c08, 0x3e007fff, 0x0b04, x_Rs_x, s7_do_rs},
+ {"brvs", 0x00003008, 0x3e007fff, 0x0c04, x_Rs_x, s7_do_rs},
+ {"brvc", 0x00003408, 0x3e007fff, 0x0d04, x_Rs_x, s7_do_rs},
+ {"brcnz", 0x00003808, 0x3e007fff, 0x0e04, x_Rs_x, s7_do_rs},
+ {"br", 0x00003c08, 0x3e007fff, 0x0f04, x_Rs_x, s7_do_rs},
+ {"brcsl", 0x00000009, 0x3e007fff, 0x000c, x_Rs_x, s7_do_rs},
+ {"brccl", 0x00000409, 0x3e007fff, 0x010c, x_Rs_x, s7_do_rs},
+ {"brgtul", 0x00000809, 0x3e007fff, 0x020c, x_Rs_x, s7_do_rs},
+ {"brleul", 0x00000c09, 0x3e007fff, 0x030c, x_Rs_x, s7_do_rs},
+ {"breql", 0x00001009, 0x3e007fff, 0x040c, x_Rs_x, s7_do_rs},
+ {"brnel", 0x00001409, 0x3e007fff, 0x050c, x_Rs_x, s7_do_rs},
+ {"brgtl", 0x00001809, 0x3e007fff, 0x060c, x_Rs_x, s7_do_rs},
+ {"brlel", 0x00001c09, 0x3e007fff, 0x070c, x_Rs_x, s7_do_rs},
+ {"brgel", 0x00002009, 0x3e007fff, 0x080c, x_Rs_x, s7_do_rs},
+ {"brltl", 0x00002409, 0x3e007fff, 0x090c, x_Rs_x, s7_do_rs},
+ {"brmil", 0x00002809, 0x3e007fff, 0x0a0c, x_Rs_x, s7_do_rs},
+ {"brpll", 0x00002c09, 0x3e007fff, 0x0b0c, x_Rs_x, s7_do_rs},
+ {"brvsl", 0x00003009, 0x3e007fff, 0x0c0c, x_Rs_x, s7_do_rs},
+ {"brvcl", 0x00003409, 0x3e007fff, 0x0d0c, x_Rs_x, s7_do_rs},
+ {"brcnzl", 0x00003809, 0x3e007fff, 0x0e0c, x_Rs_x, s7_do_rs},
+ {"brl", 0x00003c09, 0x3e007fff, 0x0f0c, x_Rs_x, s7_do_rs},
+ {"brcs!", 0x0004, 0x7f0f, 0x00000008, x_Rs, s7_do16_xrs},
+ {"brcc!", 0x0104, 0x7f0f, 0x00000408, x_Rs, s7_do16_xrs},
+ {"brgtu!", 0x0204, 0x7f0f, 0x00000808, x_Rs, s7_do16_xrs},
+ {"brleu!", 0x0304, 0x7f0f, 0x00000c08, x_Rs, s7_do16_xrs},
+ {"breq!", 0x0404, 0x7f0f, 0x00001008, x_Rs, s7_do16_xrs},
+ {"brne!", 0x0504, 0x7f0f, 0x00001408, x_Rs, s7_do16_xrs},
+ {"brgt!", 0x0604, 0x7f0f, 0x00001808, x_Rs, s7_do16_xrs},
+ {"brle!", 0x0704, 0x7f0f, 0x00001c08, x_Rs, s7_do16_xrs},
+ {"brge!", 0x0804, 0x7f0f, 0x00002008, x_Rs, s7_do16_xrs},
+ {"brlt!", 0x0904, 0x7f0f, 0x00002408, x_Rs, s7_do16_xrs},
+ {"brmi!", 0x0a04, 0x7f0f, 0x00002808, x_Rs, s7_do16_xrs},
+ {"brpl!", 0x0b04, 0x7f0f, 0x00002c08, x_Rs, s7_do16_xrs},
+ {"brvs!", 0x0c04, 0x7f0f, 0x00003008, x_Rs, s7_do16_xrs},
+ {"brvc!", 0x0d04, 0x7f0f, 0x00003408, x_Rs, s7_do16_xrs},
+ {"brcnz!", 0x0e04, 0x7f0f, 0x00003808, x_Rs, s7_do16_xrs},
+ {"br!", 0x0f04, 0x7f0f, 0x00003c08, x_Rs, s7_do16_xrs},
+ {"brcsl!", 0x000c, 0x7f0f, 0x00000009, x_Rs, s7_do16_xrs},
+ {"brccl!", 0x010c, 0x7f0f, 0x00000409, x_Rs, s7_do16_xrs},
+ {"brgtul!", 0x020c, 0x7f0f, 0x00000809, x_Rs, s7_do16_xrs},
+ {"brleul!", 0x030c, 0x7f0f, 0x00000c09, x_Rs, s7_do16_xrs},
+ {"breql!", 0x040c, 0x7f0f, 0x00001009, x_Rs, s7_do16_xrs},
+ {"brnel!", 0x050c, 0x7f0f, 0x00001409, x_Rs, s7_do16_xrs},
+ {"brgtl!", 0x060c, 0x7f0f, 0x00001809, x_Rs, s7_do16_xrs},
+ {"brlel!", 0x070c, 0x7f0f, 0x00001c09, x_Rs, s7_do16_xrs},
+ {"brgel!", 0x080c, 0x7f0f, 0x00002009, x_Rs, s7_do16_xrs},
+ {"brltl!", 0x090c, 0x7f0f, 0x00002409, x_Rs, s7_do16_xrs},
+ {"brmil!", 0x0a0c, 0x7f0f, 0x00002809, x_Rs, s7_do16_xrs},
+ {"brpll!", 0x0b0c, 0x7f0f, 0x00002c09, x_Rs, s7_do16_xrs},
+ {"brvsl!", 0x0c0c, 0x7f0f, 0x00003009, x_Rs, s7_do16_xrs},
+ {"brvcl!", 0x0d0c, 0x7f0f, 0x00003409, x_Rs, s7_do16_xrs},
+ {"brcnzl!", 0x0e0c, 0x7f0f, 0x00003809, x_Rs, s7_do16_xrs},
+ {"brl!", 0x0f0c, 0x7f0f, 0x00003c09, x_Rs, s7_do16_xrs},
+ {"bvs", 0x08003000, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bvc", 0x08003400, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"bvsl", 0x08003001, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bvcl", 0x08003401, 0x3e007c01, 0x8000, PC_DISP19div2, s7_do_branch},
+ {"bvs!", 0x4c00, 0x7f00, 0x08003000, PC_DISP8div2, s7_do16_branch},
+ {"bvc!", 0x4d00, 0x7f00, 0x08003400, PC_DISP8div2, s7_do16_branch},
+ {"b!", 0x4f00, 0x7f00, 0x08003c00, PC_DISP8div2, s7_do16_branch},
+ {"b", 0x08003c00, 0x3e007c01, 0x4000, PC_DISP19div2, s7_do_branch},
+ {"cache", 0x30000000, 0x3ff00000, 0x8000, OP5_rvalueRs_SI15, s7_do_cache},
+ {"ceinst", 0x38000000, 0x3e000000, 0x8000, I5_Rs_Rs_I5_OP5, s7_do_ceinst},
+ {"clz", 0x3800000d, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"cmpteq.c", 0x00000019, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"cmptmi.c", 0x00100019, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"cmp.c", 0x00300019, 0x3ff003ff, 0x2003, x_Rs_Rs, s7_do_rsrs},
+ {"cmpzteq.c", 0x0000001b, 0x3ff07fff, 0x8000, x_Rs_x, s7_do_rs},
+ {"cmpztmi.c", 0x0010001b, 0x3ff07fff, 0x8000, x_Rs_x, s7_do_rs},
+ {"cmpz.c", 0x0030001b, 0x3ff07fff, 0x8000, x_Rs_x, s7_do_rs},
+ {"cmpi.c", 0x02040001, 0x3e0e0001, 0x8000, Rd_SI16, s7_do_rdsi16},
+ {"cmp!", 0x2003, 0x700f, 0x00300019, Rd_Rs, s7_do16_rdrs},
+ {"cop1", 0x0c00000c, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, s7_do_crdcrscrsimm5},
+ {"cop2", 0x0c000014, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, s7_do_crdcrscrsimm5},
+ {"cop3", 0x0c00001c, 0x3e00001f, 0x8000, Rd_Rs_Rs_imm, s7_do_crdcrscrsimm5},
+ {"drte", 0x0c0000a4, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"extsb", 0x00000058, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extsb.c", 0x00000059, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extsh", 0x0000005a, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extsh.c", 0x0000005b, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extzb", 0x0000005c, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extzb.c", 0x0000005d, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extzh", 0x0000005e, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"extzh.c", 0x0000005f, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"jl", 0x04000001, 0x3e000001, 0x8000, PC_DISP24div2, s7_do_jump},
+ {"jl!", 0x3001, 0x7001, 0x04000001, PC_DISP11div2, s7_do16_jump},
+ {"j!", 0x3000, 0x7001, 0x04000000, PC_DISP11div2, s7_do16_jump},
+ {"j", 0x04000000, 0x3e000001, 0x8000, PC_DISP24div2, s7_do_jump},
+ {"lbu!", 0x200b, 0x0000700f, 0x2c000000, Rd_rvalueRs, s7_do16_ldst_insn},
+ {"lbup!", 0x7003, 0x7007, 0x2c000000, Rd_rvalueBP_I5, s7_do16_ldst_imm_insn},
+ {"alw", 0x0000000c, 0x3e0003ff, 0x8000, Rd_rvalue32Rs, s7_do_ldst_atomic},
+ {"lcb", 0x00000060, 0x3e0003ff, 0x8000, x_rvalueRs_post4, s7_do_ldst_unalign},
+ {"lcw", 0x00000062, 0x3e0003ff, 0x8000, Rd_rvalueRs_post4, s7_do_ldst_unalign},
+ {"lce", 0x00000066, 0x3e0003ff, 0x8000, Rd_rvalueRs_post4, s7_do_ldst_unalign},
+ {"ldc1", 0x0c00000a, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, s7_do_ldst_cop},
+ {"ldc2", 0x0c000012, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, s7_do_ldst_cop},
+ {"ldc3", 0x0c00001a, 0x3e00001f, 0x8000, Rd_rvalueRs_SI10, s7_do_ldst_cop},
+ {"lh!", 0x2009, 0x700f, 0x22000000, Rd_rvalueRs, s7_do16_ldst_insn},
+ {"lhp!", 0x7001, 0x7007, 0x22000000, Rd_rvalueBP_I5, s7_do16_ldst_imm_insn},
+ {"ldi", 0x020c0000, 0x3e0e0000, 0x5000, Rd_SI16, s7_do_rdsi16},
+ {"ldis", 0x0a0c0000, 0x3e0e0000, 0x8000, Rd_I16, s7_do_rdi16},
+ {"ldiu!", 0x5000, 0x7000, 0x020c0000, Rd_I8, s7_do16_ldst_imm_insn},
+ {"lw!", 0x2008, 0x700f, 0x20000000, Rd_rvalueRs, s7_do16_ldst_insn},
+ {"lwp!", 0x7000, 0x7007, 0x20000000, Rd_rvalueBP_I5, s7_do16_ldst_imm_insn},
+ {"mfcel", 0x00000448, 0x3e007fff, 0x8000, Rd_x_x, s7_do_rd},
+ {"mfcel!", 0x1001, 0x7f0f, 0x00000448, x_Rs, s7_do16_rs},
+ {"mad", 0x38000000, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mad.f!", 0x1004, 0x700f, 0x38000080, Rd_Rs, s7_do16_rdrs},
+ {"madh", 0x38000203, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"madh.fs", 0x380002c3, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"madh.fs!", 0x100b, 0x700f, 0x380002c3, Rd_Rs, s7_do16_rdrs},
+ {"madl", 0x38000002, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"madl.fs", 0x380000c2, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"madl.fs!", 0x100a, 0x700f, 0x380000c2, Rd_Rs, s7_do16_rdrs},
+ {"madu", 0x38000020, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"madu!", 0x1005, 0x700f, 0x38000020, Rd_Rs, s7_do16_rdrs},
+ {"mad.f", 0x38000080, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"max", 0x38000007, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"mazh", 0x38000303, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mazh.f", 0x38000383, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mazh.f!", 0x1009, 0x700f, 0x38000383, Rd_Rs, s7_do16_rdrs},
+ {"mazl", 0x38000102, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mazl.f", 0x38000182, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mazl.f!", 0x1008, 0x700f, 0x38000182, Rd_Rs, s7_do16_rdrs},
+ {"mfceh", 0x00000848, 0x3e007fff, 0x8000, Rd_x_x, s7_do_rd},
+ {"mfceh!", 0x1101, 0x7f0f, 0x00000848, x_Rs, s7_do16_rs},
+ {"mfcehl", 0x00000c48, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mfsr", 0x00000050, 0x3e0003ff, 0x8000, Rd_x_I5, s7_do_rdsrs},
+ {"mfcr", 0x0c000001, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mfc1", 0x0c000009, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mfc2", 0x0c000011, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mfc3", 0x0c000019, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mfcc1", 0x0c00000f, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mfcc2", 0x0c000017, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mfcc3", 0x0c00001f, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mhfl!", 0x0002, 0x700f, 0x00003c56, Rd_LowRs, s7_do16_hrdrs},
+ {"min", 0x38000006, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"mlfh!", 0x0001, 0x700f, 0x00003c56, Rd_HighRs, s7_do16_rdhrs},
+ {"msb", 0x38000001, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"msb.f!", 0x1006, 0x700f, 0x38000081, Rd_Rs, s7_do16_rdrs},
+ {"msbh", 0x38000205, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"msbh.fs", 0x380002c5, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"msbh.fs!", 0x100f, 0x700f, 0x380002c5, Rd_Rs, s7_do16_rdrs},
+ {"msbl", 0x38000004, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"msbl.fs", 0x380000c4, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"msbl.fs!", 0x100e, 0x700f, 0x380000c4, Rd_Rs, s7_do16_rdrs},
+ {"msbu", 0x38000021, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"msbu!", 0x1007, 0x700f, 0x38000021, Rd_Rs, s7_do16_rdrs},
+ {"msb.f", 0x38000081, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mszh", 0x38000305, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mszh.f", 0x38000385, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mszh.f!", 0x100d, 0x700f, 0x38000385, Rd_Rs, s7_do16_rdrs},
+ {"mszl", 0x38000104, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mszl.f", 0x38000184, 0x3ff003ff, 0x8000, x_Rs_Rs, s7_do_rsrs},
+ {"mszl.f!", 0x100c, 0x700f, 0x38000184, Rd_Rs, s7_do16_rdrs},
+ {"mtcel!", 0x1000, 0x7f0f, 0x0000044a, x_Rs, s7_do16_rs},
+ {"mtcel", 0x0000044a, 0x3e007fff, 0x8000, Rd_x_x, s7_do_rd},
+ {"mtceh", 0x0000084a, 0x3e007fff, 0x8000, Rd_x_x, s7_do_rd},
+ {"mtceh!", 0x1100, 0x7f0f, 0x0000084a, x_Rs, s7_do16_rs},
+ {"mtcehl", 0x00000c4a, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mtsr", 0x00000052, 0x3e0003ff, 0x8000, x_Rs_I5, s7_do_rdsrs},
+ {"mtcr", 0x0c000000, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mtc1", 0x0c000008, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mtc2", 0x0c000010, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mtc3", 0x0c000018, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mtcc1", 0x0c00000e, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mtcc2", 0x0c000016, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mtcc3", 0x0c00001e, 0x3e00001f, 0x8000, Rd_Rs_x, s7_do_rdcrs},
+ {"mul.f!", 0x1002, 0x700f, 0x00000041, Rd_Rs, s7_do16_rdrs},
+ {"mulu!", 0x1003, 0x700f, 0x00000042, Rd_Rs, s7_do16_rdrs},
+ {"mvcs", 0x00000056, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvcc", 0x00000456, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvgtu", 0x00000856, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvleu", 0x00000c56, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mveq", 0x00001056, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvne", 0x00001456, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvgt", 0x00001856, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvle", 0x00001c56, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvge", 0x00002056, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvlt", 0x00002456, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvmi", 0x00002856, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvpl", 0x00002c56, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvvs", 0x00003056, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mvvc", 0x00003456, 0x3e007fff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"mv", 0x00003c56, 0x3e007fff, 0x0003, Rd_Rs_x, s7_do_rdrs},
+ {"mv!", 0x0003, 0x700f, 0x00003c56, Rd_Rs, s7_do16_mv_rdrs},
+ {"neg", 0x0000001e, 0x3e0003ff, 0x8000, Rd_x_Rs, s7_do_rdxrs},
+ {"neg.c", 0x0000001f, 0x3e0003ff, 0x2002, Rd_x_Rs, s7_do_rdxrs},
+ {"neg!", 0x2002, 0x700f, 0x0000001f, Rd_Rs, s7_do16_rdrs},
+ {"nop", 0x00000000, 0x3e0003ff, 0x0000, NO_OPD, s7_do_empty},
+ {"not", 0x00000024, 0x3e0003ff, 0x8000, Rd_Rs_x, s7_do_rdrs},
+ {"not.c", 0x00000025, 0x3e0003ff, 0x2006, Rd_Rs_x, s7_do_rdrs},
+ {"nop!", 0x0000, 0x700f, 0x00000000, NO16_OPD, s7_do_empty},
+ {"not!", 0x2006, 0x700f, 0x00000025, Rd_Rs, s7_do16_rdrs},
+ {"or", 0x00000022, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"or.c", 0x00000023, 0x3e0003ff, 0x2005, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"ori", 0x020a0000, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"ori.c", 0x020a0001, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"oris", 0x0a0a0000, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"oris.c", 0x0a0a0001, 0x3e0e0001, 0x8000, Rd_I16, s7_do_rdi16},
+ {"orri", 0x1a000000, 0x3e000001, 0x8000, Rd_Rs_I14, s7_do_rdrsi14},
+ {"orri.c", 0x1a000001, 0x3e000001, 0x8000, Rd_Rs_I14, s7_do_rdrsi14},
+ {"or!", 0x2005, 0x700f, 0x00000023, Rd_Rs, s7_do16_rdrs},
+ {"pflush", 0x0000000a, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"pop!", 0x200a, 0x700f, 0x0e000000, Rd_rvalueRs, s7_do16_push_pop},
+ {"push!", 0x200e, 0x700f, 0x06000004, Rd_lvalueRs, s7_do16_push_pop},
+ {"ror", 0x00000038, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"ror.c", 0x00000039, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"rorc.c", 0x0000003b, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"rol", 0x0000003c, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"rol.c", 0x0000003d, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"rolc.c", 0x0000003f, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"rori", 0x00000078, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"rori.c", 0x00000079, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"roric.c", 0x0000007b, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"roli", 0x0000007c, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"roli.c", 0x0000007d, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"rolic.c", 0x0000007f, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"rte", 0x0c000084, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"sb!", 0x200f, 0x700f, 0x2e000000, Rd_lvalueRs, s7_do16_ldst_insn},
+ {"sbp!", 0x7007, 0x7007, 0x2e000000, Rd_lvalueBP_I5, s7_do16_ldst_imm_insn},
+ {"asw", 0x0000000e, 0x3e0003ff, 0x8000, Rd_lvalue32Rs, s7_do_ldst_atomic},
+ {"scb", 0x00000068, 0x3e0003ff, 0x8000, Rd_lvalueRs_post4, s7_do_ldst_unalign},
+ {"scw", 0x0000006a, 0x3e0003ff, 0x8000, Rd_lvalueRs_post4, s7_do_ldst_unalign},
+ {"sce", 0x0000006e, 0x3e0003ff, 0x8000, x_lvalueRs_post4, s7_do_ldst_unalign},
+ {"sdbbp", 0x00000006, 0x3e0003ff, 0x6002, x_I5_x, s7_do_xi5x},
+ {"sdbbp!", 0x6002, 0x7007, 0x00000006, Rd_I5, s7_do16_xi5},
+ {"sh!", 0x200d, 0x700f, 0x2a000000, Rd_lvalueRs, s7_do16_ldst_insn},
+ {"shp!", 0x7005, 0x7007, 0x2a000000, Rd_lvalueBP_I5, s7_do16_ldst_imm_insn},
+ {"sleep", 0x0c0000c4, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"sll", 0x00000030, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sll.c", 0x00000031, 0x3e0003ff, 0x0008, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sll.s", 0x3800004e, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"slli", 0x00000070, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"slli.c", 0x00000071, 0x3e0003ff, 0x6001, Rd_Rs_I5, s7_do_rdrsi5},
+ {"sll!", 0x0008, 0x700f, 0x00000031, Rd_Rs, s7_do16_rdrs},
+ {"slli!", 0x6001, 0x7007, 0x00000071, Rd_I5, s7_do16_rdi5},
+ {"srl", 0x00000034, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"srl.c", 0x00000035, 0x3e0003ff, 0x000a, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sra", 0x00000036, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sra.c", 0x00000037, 0x3e0003ff, 0x000b, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"srli", 0x00000074, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"srli.c", 0x00000075, 0x3e0003ff, 0x6003, Rd_Rs_I5, s7_do_rdrsi5},
+ {"srai", 0x00000076, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"srai.c", 0x00000077, 0x3e0003ff, 0x8000, Rd_Rs_I5, s7_do_rdrsi5},
+ {"srl!", 0x000a, 0x700f, 0x00000035, Rd_Rs, s7_do16_rdrs},
+ {"sra!", 0x000b, 0x700f, 0x00000037, Rd_Rs, s7_do16_rdrs},
+ {"srli!", 0x6003, 0x7007, 0x00000075, Rd_Rs, s7_do16_rdi5},
+ {"stc1", 0x0c00000b, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, s7_do_ldst_cop},
+ {"stc2", 0x0c000013, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, s7_do_ldst_cop},
+ {"stc3", 0x0c00001b, 0x3e00001f, 0x8000, Rd_lvalueRs_SI10, s7_do_ldst_cop},
+ {"sub", 0x00000014, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sub.c", 0x00000015, 0x3e0003ff, 0x2001, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sub.s", 0x38000049, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"subc", 0x00000016, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"subc.c", 0x00000017, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"sub!", 0x2001, 0x700f, 0x00000015, Rd_Rs, s7_do16_rdrs},
+ {"subei!", 0x6080, 0x7087, 0x02000001, Rd_I4, s7_do16_rdi4},
+ {"sw!", 0x200c, 0x700f, 0x28000000, Rd_lvalueRs, s7_do16_ldst_insn},
+ {"swp!", 0x7004, 0x7007, 0x28000000, Rd_lvalueBP_I5, s7_do16_ldst_imm_insn},
+ {"syscall", 0x00000002, 0x3e0003ff, 0x8000, I15, s7_do_i15},
+ {"tcs", 0x00000054, 0x3e007fff, 0x0005, NO_OPD, s7_do_empty},
+ {"tcc", 0x00000454, 0x3e007fff, 0x0105, NO_OPD, s7_do_empty},
+ {"tcnz", 0x00003854, 0x3e007fff, 0x0e05, NO_OPD, s7_do_empty},
+ {"tcs!", 0x0005, 0x7f0f, 0x00000054, NO16_OPD, s7_do_empty},
+ {"tcc!", 0x0105, 0x7f0f, 0x00000454, NO16_OPD, s7_do_empty},
+ {"tcnz!", 0x0e05, 0x7f0f, 0x00003854, NO16_OPD, s7_do_empty},
+ {"teq", 0x00001054, 0x3e007fff, 0x0405, NO_OPD, s7_do_empty},
+ {"teq!", 0x0405, 0x7f0f, 0x00001054, NO16_OPD, s7_do_empty},
+ {"tgtu", 0x00000854, 0x3e007fff, 0x0205, NO_OPD, s7_do_empty},
+ {"tgt", 0x00001854, 0x3e007fff, 0x0605, NO_OPD, s7_do_empty},
+ {"tge", 0x00002054, 0x3e007fff, 0x0805, NO_OPD, s7_do_empty},
+ {"tgtu!", 0x0205, 0x7f0f, 0x00000854, NO16_OPD, s7_do_empty},
+ {"tgt!", 0x0605, 0x7f0f, 0x00001854, NO16_OPD, s7_do_empty},
+ {"tge!", 0x0805, 0x7f0f, 0x00002054, NO16_OPD, s7_do_empty},
+ {"tleu", 0x00000c54, 0x3e007fff, 0x0305, NO_OPD, s7_do_empty},
+ {"tle", 0x00001c54, 0x3e007fff, 0x0705, NO_OPD, s7_do_empty},
+ {"tlt", 0x00002454, 0x3e007fff, 0x0905, NO_OPD, s7_do_empty},
+ {"stlb", 0x0c000004, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"mftlb", 0x0c000024, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"mtptlb", 0x0c000044, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"mtrtlb", 0x0c000064, 0x3e0003ff, 0x8000, NO_OPD, s7_do_empty},
+ {"tleu!", 0x0305, 0x7f0f, 0x00000c54, NO16_OPD, s7_do_empty},
+ {"tle!", 0x0705, 0x7f0f, 0x00001c54, NO16_OPD, s7_do_empty},
+ {"tlt!", 0x0905, 0x7f0f, 0x00002454, NO16_OPD, s7_do_empty},
+ {"tmi", 0x00002854, 0x3e007fff, 0x0a05, NO_OPD, s7_do_empty},
+ {"tmi!", 0x0a05, 0x7f0f, 0x00002854, NO16_OPD, s7_do_empty},
+ {"tne", 0x00001454, 0x3e007fff, 0x0505, NO_OPD, s7_do_empty},
+ {"tne!", 0x0505, 0x7f0f, 0x00001454, NO16_OPD, s7_do_empty},
+ {"tpl", 0x00002c54, 0x3e007fff, 0x0b05, NO_OPD, s7_do_empty},
+ {"tpl!", 0x0b05, 0x7f0f, 0x00002c54, NO16_OPD, s7_do_empty},
+ {"trapcs", 0x00000004, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapcc", 0x00000404, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapgtu", 0x00000804, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapleu", 0x00000c04, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapeq", 0x00001004, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapne", 0x00001404, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapgt", 0x00001804, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"traple", 0x00001c04, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapge", 0x00002004, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"traplt", 0x00002404, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapmi", 0x00002804, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trappl", 0x00002c04, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapvs", 0x00003004, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trapvc", 0x00003404, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"trap", 0x00003c04, 0x3e007fff, 0x8000, x_I5_x, s7_do_xi5x},
+ {"tset", 0x00003c54, 0x3e007fff, 0x0f05, NO_OPD, s7_do_empty},
+ {"tset!", 0x0f05, 0x00007f0f, 0x00003c54, NO16_OPD, s7_do_empty},
+ {"tvs", 0x00003054, 0x3e007fff, 0x0c05, NO_OPD, s7_do_empty},
+ {"tvc", 0x00003454, 0x3e007fff, 0x0d05, NO_OPD, s7_do_empty},
+ {"tvs!", 0x0c05, 0x7f0f, 0x00003054, NO16_OPD, s7_do_empty},
+ {"tvc!", 0x0d05, 0x7f0f, 0x00003454, NO16_OPD, s7_do_empty},
+ {"xor", 0x00000026, 0x3e0003ff, 0x8000, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"xor.c", 0x00000027, 0x3e0003ff, 0x2007, Rd_Rs_Rs, s7_do_rdrsrs},
+ {"xor!", 0x2007, 0x700f, 0x00000027, Rd_Rs, s7_do16_rdrs},
+ /* Macro instruction. */
+ {"li", 0x020c0000, 0x3e0e0000, 0x8000, Insn_Type_SYN, s7_do_macro_li_rdi32},
+ /* la reg, imm32 -->(1) ldi reg, simm16
+ (2) ldis reg, %HI(imm32)
+ ori reg, %LO(imm32)
+
+ la reg, symbol -->(1) lis reg, %HI(imm32)
+ ori reg, %LO(imm32) */
+ {"la", 0x020c0000, 0x3e0e0000, 0x8000, Insn_Type_SYN, s7_do_macro_la_rdi32},
+ {"div", 0x00000044, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"divu", 0x00000046, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"rem", 0x00000044, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"remu", 0x00000046, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"mul", 0x00000040, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"mulu", 0x00000042, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"maz", 0x00000040, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"mazu", 0x00000042, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"mul.f", 0x00000041, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"maz.f", 0x00000041, 0x3e0003ff, 0x8000, Insn_Type_SYN, s7_do_macro_mul_rdrsrs},
+ {"lb", INSN_LB, 0x00000000, 0x8000, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"lbu", INSN_LBU, 0x00000000, 0x200b, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"lh", INSN_LH, 0x00000000, 0x2009, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"lhu", INSN_LHU, 0x00000000, 0x8000, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"lw", INSN_LW, 0x00000000, 0x2008, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"sb", INSN_SB, 0x00000000, 0x200f, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"sh", INSN_SH, 0x00000000, 0x200d, Insn_Type_SYN, s7_do_macro_ldst_label},
+ {"sw", INSN_SW, 0x00000000, 0x200c, Insn_Type_SYN, s7_do_macro_ldst_label},
+ /* Assembler use internal. */
+ {"ld_i32hi", 0x0a0c0000, 0x3e0e0000, 0x8000, Insn_internal, s7_do_macro_rdi32hi},
+ {"ld_i32lo", 0x020a0000, 0x3e0e0001, 0x8000, Insn_internal, s7_do_macro_rdi32lo},
+ {"ldis_pic", 0x0a0c0000, 0x3e0e0000, 0x5000, Insn_internal, s7_do_rdi16_pic},
+ {"addi_s_pic",0x02000000, 0x3e0e0001, 0x8000, Insn_internal, s7_do_addi_s_pic},
+ {"addi_u_pic",0x02000000, 0x3e0e0001, 0x8000, Insn_internal, s7_do_addi_u_pic},
+ {"lw_pic", 0x20000000, 0x3e000000, 0x8000, Insn_internal, s7_do_lw_pic},
+};
+
+#define s7_SCORE5_PIPELINE 5
+#define s7_SCORE7_PIPELINE 7
+
+static int s7_university_version = 0;
+static int s7_vector_size = s7_SCORE7_PIPELINE;
+static struct s7_score_it s7_dependency_vector[s7_SCORE7_PIPELINE];
+
+static int s7_score7d = 1;
+
+
+
+static int
+s7_end_of_line (char *str)
+{
+ int retval = s7_SUCCESS;
+
+ s7_skip_whitespace (str);
+ if (*str != '\0')
+ {
+ retval = (int) s7_FAIL;
+
+ if (!s7_inst.error)
+ s7_inst.error = s7_BAD_GARBAGE;
+ }
+
+ return retval;
+}
+
+static int
+s7_score_reg_parse (char **ccp, struct hash_control *htab)
+{
+ char *start = *ccp;
+ char c;
+ char *p;
+ struct s7_reg_entry *reg;
+
+ p = start;
+ if (!ISALPHA (*p) || !is_name_beginner (*p))
+ return (int) s7_FAIL;
+
+ c = *p++;
+
+ while (ISALPHA (c) || ISDIGIT (c) || c == '_')
+ c = *p++;
+
+ *--p = 0;
+ reg = (struct s7_reg_entry *) hash_find (htab, start);
+ *p = c;
+
+ if (reg)
+ {
+ *ccp = p;
+ return reg->number;
+ }
+ return (int) s7_FAIL;
+}
+
+/* If shift <= 0, only return reg. */
+static int
+s7_reg_required_here (char **str, int shift, enum s7_score_reg_type reg_type)
+{
+ static char buff[s7_MAX_LITERAL_POOL_SIZE];
+ int reg = (int) s7_FAIL;
+ char *start = *str;
+
+ if ((reg = s7_score_reg_parse (str, s7_all_reg_maps[reg_type].htab)) != (int) s7_FAIL)
+ {
+ if (reg_type == s7_REG_TYPE_SCORE)
+ {
+ if ((reg == 1) && (s7_nor1 == 1) && (s7_inst.bwarn == 0))
+ {
+ as_warn (_("Using temp register(r1)"));
+ s7_inst.bwarn = 1;
+ }
+ }
+ if (shift >= 0)
+ {
+ if (reg_type == s7_REG_TYPE_SCORE_CR)
+ strcpy (s7_inst.reg, s7_score_crn_table[reg].name);
+ else if (reg_type == s7_REG_TYPE_SCORE_SR)
+ strcpy (s7_inst.reg, s7_score_srn_table[reg].name);
+ else
+ strcpy (s7_inst.reg, "");
+
+ s7_inst.instruction |= reg << shift;
+ }
+ }
+ else
+ {
+ *str = start;
+ sprintf (buff, _("register expected, not '%.100s'"), start);
+ s7_inst.error = buff;
+ }
+
+ return reg;
+}
+
+static int
+s7_skip_past_comma (char **str)
+{
+ char *p = *str;
+ char c;
+ int comma = 0;
+
+ while ((c = *p) == ' ' || c == ',')
+ {
+ p++;
+ if (c == ',' && comma++)
+ {
+ s7_inst.error = s7_BAD_SKIP_COMMA;
+ return (int) s7_FAIL;
+ }
+ }
+
+ if ((c == '\0') || (comma == 0))
+ {
+ s7_inst.error = s7_BAD_SKIP_COMMA;
+ return (int) s7_FAIL;
+ }
+
+ *str = p;
+ return comma ? s7_SUCCESS : (int) s7_FAIL;
+}
+
+static void
+s7_do_rdrsrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0)
+ && (((s7_inst.instruction >> 10) & 0x10) == 0)
+ && (((s7_inst.instruction >> 20) & 0x10) == 0)
+ && (s7_inst.relax_inst != 0x8000)
+ && (((s7_inst.instruction >> 20) & 0xf) == ((s7_inst.instruction >> 15) & 0xf)))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0xf) << 4)
+ | (((s7_inst.instruction >> 15) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+}
+
+static int
+s7_walk_no_bignums (symbolS * sp)
+{
+ if (symbol_get_value_expression (sp)->X_op == O_big)
+ return 1;
+
+ if (symbol_get_value_expression (sp)->X_add_symbol)
+ return (s7_walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
+ || (symbol_get_value_expression (sp)->X_op_symbol
+ && s7_walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
+
+ return 0;
+}
+
+static int
+s7_my_get_expression (expressionS * ep, char **str)
+{
+ char *save_in;
+
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ s7_in_my_get_expression = 1;
+
+ (void) expression (ep);
+ s7_in_my_get_expression = 0;
+
+ if (ep->X_op == O_illegal)
+ {
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ s7_inst.error = _("illegal expression");
+ return (int) s7_FAIL;
+ }
+ /* Get rid of any bignums now, so that we don't generate an error for which
+ we can't establish a line number later on. Big numbers are never valid
+ in instructions, which is where this routine is always called. */
+ if (ep->X_op == O_big
+ || (ep->X_add_symbol
+ && (s7_walk_no_bignums (ep->X_add_symbol)
+ || (ep->X_op_symbol && s7_walk_no_bignums (ep->X_op_symbol)))))
+ {
+ s7_inst.error = _("invalid constant");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return (int) s7_FAIL;
+ }
+
+ if ((ep->X_add_symbol != NULL)
+ && (s7_inst.type != PC_DISP19div2)
+ && (s7_inst.type != PC_DISP8div2)
+ && (s7_inst.type != PC_DISP24div2)
+ && (s7_inst.type != PC_DISP11div2)
+ && (s7_inst.type != Insn_Type_SYN)
+ && (s7_inst.type != Rd_rvalueRs_SI15)
+ && (s7_inst.type != Rd_lvalueRs_SI15)
+ && (s7_inst.type != Insn_internal))
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return (int) s7_FAIL;
+ }
+
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return s7_SUCCESS;
+}
+
+/* Check if an immediate is valid. If so, convert it to the right format. */
+
+static bfd_signed_vma
+s7_validate_immediate (bfd_signed_vma val, unsigned int data_type, int hex_p)
+{
+ switch (data_type)
+ {
+ case _VALUE_HI16:
+ {
+ int val_hi = ((val & 0xffff0000) >> 16);
+
+ if (s7_score_df_range[data_type].range[0] <= val_hi
+ && val_hi <= s7_score_df_range[data_type].range[1])
+ return val_hi;
+ }
+ break;
+
+ case _VALUE_LO16:
+ {
+ int val_lo = (val & 0xffff);
+
+ if (s7_score_df_range[data_type].range[0] <= val_lo
+ && val_lo <= s7_score_df_range[data_type].range[1])
+ return val_lo;
+ }
+ break;
+
+ case _SIMM12:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x800 && val <= 0xfff))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -2048 && val <= 2047))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+
+ return val;
+ break;
+
+ case _SIMM14:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x2000 && val <= 0x3fff))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -8192 && val <= 8191))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+
+ return val;
+ break;
+
+ case _SIMM15:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x4000 && val <= 0x7fff))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -16384 && val <= 16383))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+
+ return val;
+ break;
+
+ case _SIMM16:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x8000 && val <= 0xffff))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -32768 && val <= 32767))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+
+ return val;
+ break;
+
+ case _SIMM16_NEG:
+ if (hex_p == 1)
+ {
+ if (!(val >= -0x7fff && val <= 0xffff && val != 0x8000))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+ else
+ {
+ if (!(val >= -32767 && val <= 32768))
+ {
+ return (int) s7_FAIL;
+ }
+ }
+
+ val = -val;
+ return val;
+ break;
+
+ case _IMM32:
+ if (val >= 0 && val <= 0xffffffff)
+ {
+ return val;
+ }
+ else
+ {
+ return (int) s7_FAIL;
+ }
+
+ default:
+ if (data_type == _SIMM14_NEG || data_type == _IMM16_NEG)
+ val = -val;
+
+ if (s7_score_df_range[data_type].range[0] <= val
+ && val <= s7_score_df_range[data_type].range[1])
+ return val;
+
+ break;
+ }
+
+ return (int) s7_FAIL;
+}
+
+static int
+s7_data_op2 (char **str, int shift, enum score_data_type data_type)
+{
+ int value;
+ char data_exp[s7_MAX_LITERAL_POOL_SIZE];
+ char *dataptr;
+ int cnt = 0;
+ char *pp = NULL;
+
+ s7_skip_whitespace (*str);
+ s7_inst.error = NULL;
+ dataptr = * str;
+
+ /* Set hex_p to zero. */
+ int hex_p = 0;
+
+ while ((*dataptr != '\0') && (*dataptr != '|') && (cnt <= s7_MAX_LITERAL_POOL_SIZE)) /* 0x7c = ='|' */
+ {
+ data_exp[cnt] = *dataptr;
+ dataptr++;
+ cnt++;
+ }
+
+ data_exp[cnt] = '\0';
+ pp = (char *)&data_exp;
+
+ if (*dataptr == '|') /* process PCE */
+ {
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &pp) == (int) s7_FAIL)
+ return (int) s7_FAIL;
+ s7_end_of_line (pp);
+ if (s7_inst.error != 0)
+ return (int) s7_FAIL; /* to ouptut_inst to printf out the error */
+ *str = dataptr;
+ }
+ else /* process 16 bit */
+ {
+ if (s7_my_get_expression (&s7_inst.reloc.exp, str) == (int) s7_FAIL)
+ {
+ return (int) s7_FAIL;
+ }
+
+ dataptr = (char *) data_exp;
+ for (; *dataptr != '\0'; dataptr++)
+ {
+ *dataptr = TOLOWER (*dataptr);
+ if (*dataptr == '!' || *dataptr == ' ')
+ break;
+ }
+ dataptr = (char *) data_exp;
+
+ if ((dataptr != NULL)
+ && (((strstr (dataptr, "0x")) != NULL)
+ || ((strstr (dataptr, "0X")) != NULL)))
+ {
+ hex_p = 1;
+ if ((data_type != _SIMM16_LA)
+ && (data_type != _VALUE_HI16)
+ && (data_type != _VALUE_LO16)
+ && (data_type != _IMM16)
+ && (data_type != _IMM15)
+ && (data_type != _IMM14)
+ && (data_type != _IMM4)
+ && (data_type != _IMM5)
+ && (data_type != _IMM8)
+ && (data_type != _IMM5_RSHIFT_1)
+ && (data_type != _IMM5_RSHIFT_2)
+ && (data_type != _SIMM14)
+ && (data_type != _SIMM16)
+ && (data_type != _SIMM14_NEG)
+ && (data_type != _SIMM16_NEG)
+ && (data_type != _IMM10_RSHIFT_2)
+ && (data_type != _GP_IMM15))
+ {
+ data_type += 24;
+ }
+ }
+
+ if ((s7_inst.reloc.exp.X_add_number == 0)
+ /* for "addi r0,-((((((32*4)+4)+4)+4)+4)&0xf)". */
+ && (s7_inst.type != Rd_SI16)
+ && (s7_inst.type != Insn_Type_SYN)
+ && (s7_inst.type != Rd_rvalueRs_SI15)
+ && (s7_inst.type != Rd_lvalueRs_SI15)
+ && (s7_inst.type != Insn_internal)
+ && (((*dataptr >= 'a') && (*dataptr <= 'z'))
+ || ((*dataptr == '0') && (*(dataptr + 1) == 'x') && (*(dataptr + 2) != '0'))
+ || ((*dataptr == '+') && (*(dataptr + 1) != '0'))
+ || ((*dataptr == '-') && (*(dataptr + 1) != '0'))))
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ return (int) s7_FAIL;
+ }
+ }
+
+ if ((s7_inst.reloc.exp.X_add_symbol)
+ && ((data_type == _SIMM16)
+ || (data_type == _SIMM16_NEG)
+ || (data_type == _IMM16_NEG)
+ || (data_type == _SIMM14)
+ || (data_type == _SIMM14_NEG)
+ || (data_type == _IMM5)
+ || (data_type == _IMM14)
+ || (data_type == _IMM20)
+ || (data_type == _IMM16)
+ || (data_type == _IMM15)
+ || (data_type == _IMM4)))
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ return (int) s7_FAIL;
+ }
+
+ if (s7_inst.reloc.exp.X_add_symbol)
+ {
+ switch (data_type)
+ {
+ case _SIMM16_LA:
+ return (int) s7_FAIL;
+ case _VALUE_HI16:
+ s7_inst.reloc.type = BFD_RELOC_HI16_S;
+ s7_inst.reloc.pc_rel = 0;
+ break;
+ case _VALUE_LO16:
+ s7_inst.reloc.type = BFD_RELOC_LO16;
+ s7_inst.reloc.pc_rel = 0;
+ break;
+ case _GP_IMM15:
+ s7_inst.reloc.type = BFD_RELOC_SCORE_GPREL15;
+ s7_inst.reloc.pc_rel = 0;
+ break;
+ case _SIMM16_pic:
+ case _IMM16_LO16_pic:
+ s7_inst.reloc.type = BFD_RELOC_SCORE_GOT_LO16;
+ s7_inst.reloc.pc_rel = 0;
+ break;
+ default:
+ s7_inst.reloc.type = BFD_RELOC_32;
+ s7_inst.reloc.pc_rel = 0;
+ break;
+ }
+ }
+ else
+ {
+ if (data_type == _IMM16_pic)
+ {
+ s7_inst.reloc.type = BFD_RELOC_SCORE_DUMMY_HI16;
+ s7_inst.reloc.pc_rel = 0;
+ }
+
+ if (data_type == _SIMM16_LA && s7_inst.reloc.exp.X_unsigned == 1)
+ {
+ value = s7_validate_immediate (s7_inst.reloc.exp.X_add_number, _SIMM16_LA_POS, hex_p);
+ if (value == (int) s7_FAIL) /* for advance to check if this is ldis */
+ if ((s7_inst.reloc.exp.X_add_number & 0xffff) == 0)
+ {
+ s7_inst.instruction |= 0x8000000;
+ s7_inst.instruction |= ((s7_inst.reloc.exp.X_add_number >> 16) << 1) & 0x1fffe;
+ return s7_SUCCESS;
+ }
+ }
+ else
+ {
+ value = s7_validate_immediate (s7_inst.reloc.exp.X_add_number, data_type, hex_p);
+ }
+
+ if (value == (int) s7_FAIL)
+ {
+ if ((data_type != _SIMM14_NEG) && (data_type != _SIMM16_NEG) && (data_type != _IMM16_NEG))
+ {
+ sprintf (s7_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s7_score_df_range[data_type].bits,
+ s7_score_df_range[data_type].range[0], s7_score_df_range[data_type].range[1]);
+ }
+ else
+ {
+ sprintf (s7_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s7_score_df_range[data_type].bits,
+ -s7_score_df_range[data_type].range[1], -s7_score_df_range[data_type].range[0]);
+ }
+
+ s7_inst.error = s7_err_msg;
+ return (int) s7_FAIL;
+ }
+
+ if ((s7_score_df_range[data_type].range[0] != 0) || (data_type == _IMM5_RANGE_8_31))
+ {
+ value &= (1 << s7_score_df_range[data_type].bits) - 1;
+ }
+
+ s7_inst.instruction |= value << shift;
+ }
+
+ if ((s7_inst.instruction & 0x3e000000) == 0x30000000)
+ {
+ if ((((s7_inst.instruction >> 20) & 0x1F) != 0)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 1)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 2)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 3)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 4)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 8)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 9)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0xa)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0xb)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0xc)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0xd)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0xe)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x10)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x11)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x18)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x1A)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x1B)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x1d)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x1e)
+ && (((s7_inst.instruction >> 20) & 0x1F) != 0x1f))
+ {
+ s7_inst.error = _("invalid constant: bit expression not defined");
+ return (int) s7_FAIL;
+ }
+ }
+
+ return s7_SUCCESS;
+}
+
+/* Handle addi/addi.c/addis.c/cmpi.c/addis.c/ldi. */
+
+static void
+s7_do_rdsi16 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 1, _SIMM16) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ /* ldi. */
+ if ((s7_inst.instruction & 0x20c0000) == 0x20c0000)
+ {
+ if ((((s7_inst.instruction >> 20) & 0x10) == 0x10) || ((s7_inst.instruction & 0x1fe00) != 0))
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ else
+ {
+ s7_inst.relax_inst |= (s7_inst.instruction >> 1) & 0xff;
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ }
+ else if (((s7_inst.instruction >> 20) & 0x10) == 0x10)
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+}
+
+/* Handle subi/subi.c. */
+
+static void
+s7_do_sub_rdsi16 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_data_op2 (&str, 1, _SIMM16_NEG) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+
+/* Handle addri/addri.c. */
+
+static void
+s7_do_rdrssi14 (char *str) /* -(2^13)~((2^13)-1) */
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL)
+ s7_data_op2 (&str, 1, _SIMM14);
+}
+
+/* Handle subri.c/subri. */
+
+static void
+s7_do_sub_rdrssi14 (char *str) /* -(2^13)~((2^13)-1) */
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_data_op2 (&str, 1, _SIMM14_NEG) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+/* Handle bitclr.c/bitset.c/bittgl.c/slli.c/srai.c/srli.c/roli.c/rori.c/rolic.c. */
+
+static void
+s7_do_rdrsi5 (char *str) /* 0~((2^14)-1) */
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 10, _IMM5) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if ((((s7_inst.instruction >> 20) & 0x1f) == ((s7_inst.instruction >> 15) & 0x1f))
+ && (s7_inst.relax_inst != 0x8000) && (((s7_inst.instruction >> 15) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0x1f) << 3) | (((s7_inst.instruction >> 15) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ s7_inst.relax_inst = 0x8000;
+}
+
+/* Handle andri/orri/andri.c/orri.c. */
+
+static void
+s7_do_rdrsi14 (char *str) /* 0 ~ ((2^14)-1) */
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_data_op2 (&str, 1, _IMM14) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+/* Handle bittst.c. */
+
+static void
+s7_do_xrsi5 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 10, _IMM5) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if ((s7_inst.relax_inst != 0x8000) && (((s7_inst.instruction >> 15) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0x1f) << 3) | (((s7_inst.instruction >> 15) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ s7_inst.relax_inst = 0x8000;
+}
+
+/* Handle addis/andi/ori/andis/oris/ldis. */
+
+static void
+s7_do_rdi16 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 1, _IMM16) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+}
+
+static void
+s7_do_macro_rdi32hi (char *str)
+{
+ s7_skip_whitespace (str);
+
+ /* Do not handle s7_end_of_line(). */
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL)
+ s7_data_op2 (&str, 1, _VALUE_HI16);
+}
+
+static void
+s7_do_macro_rdi32lo (char *str)
+{
+ s7_skip_whitespace (str);
+
+ /* Do not handle s7_end_of_line(). */
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL)
+ s7_data_op2 (&str, 1, _VALUE_LO16);
+}
+
+/* Handle ldis_pic. */
+
+static void
+s7_do_rdi16_pic (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_data_op2 (&str, 1, _IMM16_pic) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+/* Handle addi_s_pic to generate R_SCORE_GOT_LO16 . */
+
+static void
+s7_do_addi_s_pic (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_data_op2 (&str, 1, _SIMM16_pic) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+/* Handle addi_u_pic to generate R_SCORE_GOT_LO16 . */
+
+static void
+s7_do_addi_u_pic (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_data_op2 (&str, 1, _IMM16_LO16_pic) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+/* Handle mfceh/mfcel/mtceh/mtchl. */
+
+static void
+s7_do_rd (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+static void
+s7_do_rs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if ((s7_inst.relax_inst != 0x8000) && (((s7_inst.instruction >> 15) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0xf) << 8) | (((s7_inst.instruction >> 15) & 0xf) << 4);
+ s7_inst.relax_size = 2;
+ }
+ else
+ s7_inst.relax_inst = 0x8000;
+}
+
+static void
+s7_do_i15 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_data_op2 (&str, 10, _IMM15) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+static void
+s7_do_xi5x (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_data_op2 (&str, 15, _IMM5) == (int) s7_FAIL || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if (s7_inst.relax_inst != 0x8000)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0x1f) << 3);
+ s7_inst.relax_size = 2;
+ }
+}
+
+static void
+s7_do_rdrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if (s7_inst.relax_inst != 0x8000)
+ {
+ if (((s7_inst.instruction & 0x7f) == 0x56)) /* adjust mv -> mv! / mlfh! / mhfl! */
+ {
+ /* mlfh */
+ if ((((s7_inst.instruction >> 15) & 0x10) != 0x0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s7_inst.relax_inst = 0x00000001 | (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* mhfl */
+ else if ((((s7_inst.instruction >> 15) & 0x10) == 0x0) && ((s7_inst.instruction >> 20) & 0x10) != 0)
+ {
+ s7_inst.relax_inst = 0x00000002 | (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else if ((((s7_inst.instruction >> 15) & 0x10) == 0x0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else if ((((s7_inst.instruction >> 15) & 0x10) == 0x0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+}
+
+/* Handle mfcr/mtcr. */
+static void
+s7_do_rdcrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE_CR) != (int) s7_FAIL)
+ s7_end_of_line (str);
+}
+
+/* Handle mfsr/mtsr. */
+
+static void
+s7_do_rdsrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ /* mfsr */
+ if ((s7_inst.instruction & 0xff) == 0x50)
+ {
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE_SR) != (int) s7_FAIL)
+ s7_end_of_line (str);
+ }
+ else
+ {
+ if (s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL)
+ s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE_SR);
+ }
+}
+
+/* Handle neg. */
+
+static void
+s7_do_rdxrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if ((s7_inst.relax_inst != 0x8000) && (((s7_inst.instruction >> 10) & 0x10) == 0)
+ && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0xf) << 4) | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ s7_inst.relax_inst = 0x8000;
+}
+
+/* Handle cmp.c/cmp<cond>. */
+static void
+s7_do_rsrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if ((s7_inst.relax_inst != 0x8000) && (((s7_inst.instruction >> 20) & 0x1f) == 3)
+ && (((s7_inst.instruction >> 10) & 0x10) == 0) && (((s7_inst.instruction >> 15) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0xf) << 4) | (((s7_inst.instruction >> 15) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ else
+ s7_inst.relax_inst = 0x8000;
+}
+
+static void
+s7_do_ceinst (char *str)
+{
+ char *strbak;
+
+ strbak = str;
+ s7_skip_whitespace (str);
+
+ if (s7_data_op2 (&str, 20, _IMM5) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 5, _IMM5) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 0, _IMM5) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ str = strbak;
+ if (s7_data_op2 (&str, 0, _IMM25) == (int) s7_FAIL)
+ return;
+ }
+}
+
+static int
+s7_reglow_required_here (char **str, int shift)
+{
+ static char buff[s7_MAX_LITERAL_POOL_SIZE];
+ int reg;
+ char *start = *str;
+
+ if ((reg = s7_score_reg_parse (str, s7_all_reg_maps[s7_REG_TYPE_SCORE].htab)) != (int) s7_FAIL)
+ {
+ if ((reg == 1) && (s7_nor1 == 1) && (s7_inst.bwarn == 0))
+ {
+ as_warn (_("Using temp register(r1)"));
+ s7_inst.bwarn = 1;
+ }
+ if (reg < 16)
+ {
+ if (shift >= 0)
+ s7_inst.instruction |= reg << shift;
+
+ return reg;
+ }
+ }
+
+ /* Restore the start point, we may have got a reg of the wrong class. */
+ *str = start;
+ sprintf (buff, _("low register(r0-r15)expected, not '%.100s'"), start);
+ s7_inst.error = buff;
+ return (int) s7_FAIL;
+}
+
+/* Handle addc!/add!/and!/cmp!/neg!/not!/or!/sll!/srl!/sra!/xor!/sub!. */
+
+static void
+s7_do16_rdrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reglow_required_here (&str, 8) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reglow_required_here (&str, 4) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ if ((s7_inst.instruction & 0x700f) == 0x2003) /* cmp! */
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 15)
+ | (((s7_inst.instruction >> 4) & 0xf) << 10);
+ }
+ else if ((s7_inst.instruction & 0x700f) == 0x2006) /* not! */
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 4) & 0xf) << 15);
+ }
+ else if ((s7_inst.instruction & 0x700f) == 0x1009) /* mazh.f! */
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 15)
+ | (((s7_inst.instruction >> 4) & 0xf) << 10);
+ }
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 8) & 0xf) << 15) | (((s7_inst.instruction >> 4) & 0xf) << 10);
+ }
+ s7_inst.relax_size = 4;
+ }
+}
+
+static void
+s7_do16_rs (char *str)
+{
+ int rd = 0;
+
+ s7_skip_whitespace (str);
+
+ if ((rd = s7_reglow_required_here (&str, 4)) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ s7_inst.relax_inst |= rd << 20;
+ s7_inst.relax_size = 4;
+ }
+}
+
+/* Handle br!/brl!. */
+
+static void
+s7_do16_xrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reglow_required_here (&str, 4) == (int) s7_FAIL || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 10)
+ | (((s7_inst.instruction >> 4) & 0xf) << 15);
+ s7_inst.relax_size = 4;
+ }
+}
+
+static int
+s7_reghigh_required_here (char **str, int shift)
+{
+ static char buff[s7_MAX_LITERAL_POOL_SIZE];
+ int reg;
+ char *start = *str;
+
+ if ((reg = s7_score_reg_parse (str, s7_all_reg_maps[s7_REG_TYPE_SCORE].htab)) != (int) s7_FAIL)
+ {
+ if (15 < reg && reg < 32)
+ {
+ if (shift >= 0)
+ s7_inst.instruction |= (reg & 0xf) << shift;
+
+ return reg;
+ }
+ }
+
+ *str = start;
+ sprintf (buff, _("high register(r16-r31)expected, not '%.100s'"), start);
+ s7_inst.error = buff;
+ return (int) s7_FAIL;
+}
+
+/* Handle mhfl!. */
+
+static void
+s7_do16_hrdrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reghigh_required_here (&str, 8) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reglow_required_here (&str, 4) != (int) s7_FAIL
+ && s7_end_of_line (str) != (int) s7_FAIL)
+ {
+ s7_inst.relax_inst |= ((((s7_inst.instruction >> 8) & 0xf) | 0x10) << 20)
+ | (((s7_inst.instruction >> 4) & 0xf) << 15) | (0xf << 10);
+ s7_inst.relax_size = 4;
+ }
+}
+
+/* Handle mlfh!. */
+
+static void
+s7_do16_rdhrs (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reglow_required_here (&str, 8) != (int) s7_FAIL
+ && s7_skip_past_comma (&str) != (int) s7_FAIL
+ && s7_reghigh_required_here (&str, 4) != (int) s7_FAIL
+ && s7_end_of_line (str) != (int) s7_FAIL)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | ((((s7_inst.instruction >> 4) & 0xf) | 0x10) << 15) | (0xf << 10);
+ s7_inst.relax_size = 4;
+ }
+}
+
+/* We need to be able to fix up arbitrary expressions in some statements.
+ This is so that we can handle symbols that are an arbitrary distance from
+ the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+ which returns part of an address in a form which will be valid for
+ a data instruction. We do this by pushing the expression into a symbol
+ in the expr_section, and creating a fix for that. */
+
+static fixS *
+s7_fix_new_score (fragS * frag, int where, short int size, expressionS * exp, int pc_rel, int reloc)
+{
+ fixS *new_fix;
+
+ switch (exp->X_op)
+ {
+ case O_constant:
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+ break;
+ default:
+ new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0, pc_rel, reloc);
+ break;
+ }
+ return new_fix;
+}
+
+static void
+s7_init_dependency_vector (void)
+{
+ int i;
+
+ for (i = 0; i < s7_vector_size; i++)
+ memset (&s7_dependency_vector[i], '\0', sizeof (s7_dependency_vector[i]));
+
+ return;
+}
+
+static enum s7_insn_type_for_dependency
+s7_dependency_type_from_insn (char *insn_name)
+{
+ char name[s7_INSN_NAME_LEN];
+ const struct s7_insn_to_dependency *tmp;
+
+ strcpy (name, insn_name);
+ tmp = (const struct s7_insn_to_dependency *) hash_find (s7_dependency_insn_hsh, name);
+
+ if (tmp)
+ return tmp->type;
+
+ return s7_D_all_insn;
+}
+
+static int
+s7_check_dependency (char *pre_insn, char *pre_reg,
+ char *cur_insn, char *cur_reg, int *warn_or_error)
+{
+ int bubbles = 0;
+ unsigned int i;
+ enum s7_insn_type_for_dependency pre_insn_type;
+ enum s7_insn_type_for_dependency cur_insn_type;
+
+ pre_insn_type = s7_dependency_type_from_insn (pre_insn);
+ cur_insn_type = s7_dependency_type_from_insn (cur_insn);
+
+ for (i = 0; i < sizeof (s7_data_dependency_table) / sizeof (s7_data_dependency_table[0]); i++)
+ {
+ if ((pre_insn_type == s7_data_dependency_table[i].pre_insn_type)
+ && (s7_D_all_insn == s7_data_dependency_table[i].cur_insn_type
+ || cur_insn_type == s7_data_dependency_table[i].cur_insn_type)
+ && (strcmp (s7_data_dependency_table[i].pre_reg, "") == 0
+ || strcmp (s7_data_dependency_table[i].pre_reg, pre_reg) == 0)
+ && (strcmp (s7_data_dependency_table[i].cur_reg, "") == 0
+ || strcmp (s7_data_dependency_table[i].cur_reg, cur_reg) == 0))
+ {
+ if (s7_vector_size == s7_SCORE5_PIPELINE)
+ bubbles = s7_data_dependency_table[i].bubblenum_5;
+ else
+ bubbles = s7_data_dependency_table[i].bubblenum_7;
+ *warn_or_error = s7_data_dependency_table[i].warn_or_error;
+ break;
+ }
+ }
+
+ return bubbles;
+}
+
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least). */
+
+static void
+s7_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+static void
+s7_build_one_frag (struct s7_score_it one_inst)
+{
+ char *p;
+ int relaxable_p = s7_g_opt;
+ int relax_size = 0;
+
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ p = frag_more (one_inst.size);
+ s7_number_to_chars (p, one_inst.instruction, one_inst.size);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (one_inst.size);
+#endif
+
+ relaxable_p &= (one_inst.relax_size != 0);
+ relax_size = relaxable_p ? one_inst.relax_size : 0;
+
+ p = frag_var (rs_machine_dependent, relax_size + s7_RELAX_PAD_BYTE, 0,
+ s7_RELAX_ENCODE (one_inst.size, one_inst.relax_size,
+ one_inst.type, 0, 0, relaxable_p),
+ NULL, 0, NULL);
+
+ if (relaxable_p)
+ s7_number_to_chars (p, one_inst.relax_inst, relax_size);
+}
+
+static void
+s7_handle_dependency (struct s7_score_it *theinst)
+{
+ int i;
+ int warn_or_error = 0; /* warn - 0; error - 1 */
+ int bubbles = 0;
+ int remainder_bubbles = 0;
+ char cur_insn[s7_INSN_NAME_LEN];
+ char pre_insn[s7_INSN_NAME_LEN];
+ struct s7_score_it nop_inst;
+ struct s7_score_it pflush_inst;
+
+ nop_inst.instruction = 0x0000;
+ nop_inst.size = 2;
+ nop_inst.relax_inst = 0x80008000;
+ nop_inst.relax_size = 4;
+ nop_inst.type = NO16_OPD;
+
+ pflush_inst.instruction = 0x8000800a;
+ pflush_inst.size = 4;
+ pflush_inst.relax_inst = 0x8000;
+ pflush_inst.relax_size = 0;
+ pflush_inst.type = NO_OPD;
+
+ /* pflush will clear all data dependency. */
+ if (strcmp (theinst->name, "pflush") == 0)
+ {
+ s7_init_dependency_vector ();
+ return;
+ }
+
+ /* Push current instruction to s7_dependency_vector[0]. */
+ for (i = s7_vector_size - 1; i > 0; i--)
+ memcpy (&s7_dependency_vector[i], &s7_dependency_vector[i - 1], sizeof (s7_dependency_vector[i]));
+
+ memcpy (&s7_dependency_vector[0], theinst, sizeof (s7_dependency_vector[i]));
+
+ /* There is no dependency between nop and any instruction. */
+ if (strcmp (s7_dependency_vector[0].name, "nop") == 0
+ || strcmp (s7_dependency_vector[0].name, "nop!") == 0)
+ return;
+
+ /* "pce" is defined in s7_insn_to_dependency_table. */
+#define PCE_NAME "pce"
+
+ if (s7_dependency_vector[0].type == Insn_Type_PCE)
+ strcpy (cur_insn, PCE_NAME);
+ else
+ strcpy (cur_insn, s7_dependency_vector[0].name);
+
+ for (i = 1; i < s7_vector_size; i++)
+ {
+ /* The element of s7_dependency_vector is NULL. */
+ if (s7_dependency_vector[i].name[0] == '\0')
+ continue;
+
+ if (s7_dependency_vector[i].type == Insn_Type_PCE)
+ strcpy (pre_insn, PCE_NAME);
+ else
+ strcpy (pre_insn, s7_dependency_vector[i].name);
+
+ bubbles = s7_check_dependency (pre_insn, s7_dependency_vector[i].reg,
+ cur_insn, s7_dependency_vector[0].reg, &warn_or_error);
+ remainder_bubbles = bubbles - i + 1;
+
+ if (remainder_bubbles > 0)
+ {
+ int j;
+
+ if (s7_fix_data_dependency == 1)
+ {
+ if (remainder_bubbles <= 2)
+ {
+ if (s7_warn_fix_data_dependency)
+ as_warn (_("Fix data dependency: %s %s -- %s %s (insert %d nop!/%d)"),
+ s7_dependency_vector[i].name, s7_dependency_vector[i].reg,
+ s7_dependency_vector[0].name, s7_dependency_vector[0].reg,
+ remainder_bubbles, bubbles);
+
+ for (j = (s7_vector_size - 1); (j - remainder_bubbles) > 0; j--)
+ memcpy (&s7_dependency_vector[j], &s7_dependency_vector[j - remainder_bubbles],
+ sizeof (s7_dependency_vector[j]));
+
+ for (j = 1; j <= remainder_bubbles; j++)
+ {
+ memset (&s7_dependency_vector[j], '\0', sizeof (s7_dependency_vector[j]));
+ /* Insert nop!. */
+ s7_build_one_frag (nop_inst);
+ }
+ }
+ else
+ {
+ if (s7_warn_fix_data_dependency)
+ as_warn (_("Fix data dependency: %s %s -- %s %s (insert 1 pflush/%d)"),
+ s7_dependency_vector[i].name, s7_dependency_vector[i].reg,
+ s7_dependency_vector[0].name, s7_dependency_vector[0].reg,
+ bubbles);
+
+ for (j = 1; j < s7_vector_size; j++)
+ memset (&s7_dependency_vector[j], '\0', sizeof (s7_dependency_vector[j]));
+
+ /* Insert pflush. */
+ s7_build_one_frag (pflush_inst);
+ }
+ }
+ else
+ {
+ if (warn_or_error)
+ {
+ as_bad (_("data dependency: %s %s -- %s %s (%d/%d bubble)"),
+ s7_dependency_vector[i].name, s7_dependency_vector[i].reg,
+ s7_dependency_vector[0].name, s7_dependency_vector[0].reg,
+ remainder_bubbles, bubbles);
+ }
+ else
+ {
+ as_warn (_("data dependency: %s %s -- %s %s (%d/%d bubble)"),
+ s7_dependency_vector[i].name, s7_dependency_vector[i].reg,
+ s7_dependency_vector[0].name, s7_dependency_vector[0].reg,
+ remainder_bubbles, bubbles);
+ }
+ }
+ }
+ }
+}
+
+static enum insn_class
+s7_get_insn_class_from_type (enum score_insn_type type)
+{
+ enum insn_class retval = (int) s7_FAIL;
+
+ switch (type)
+ {
+ case Rd_I4:
+ case Rd_I5:
+ case Rd_rvalueBP_I5:
+ case Rd_lvalueBP_I5:
+ case Rd_I8:
+ case PC_DISP8div2:
+ case PC_DISP11div2:
+ case Rd_Rs:
+ case Rd_HighRs:
+ case Rd_lvalueRs:
+ case Rd_rvalueRs:
+ case x_Rs:
+ case Rd_LowRs:
+ case NO16_OPD:
+ retval = INSN_CLASS_16;
+ break;
+ case Rd_Rs_I5:
+ case x_Rs_I5:
+ case x_I5_x:
+ case Rd_Rs_I14:
+ case I15:
+ case Rd_I16:
+ case Rd_SI16:
+ case Rd_rvalueRs_SI10:
+ case Rd_lvalueRs_SI10:
+ case Rd_rvalueRs_preSI12:
+ case Rd_rvalueRs_postSI12:
+ case Rd_lvalueRs_preSI12:
+ case Rd_lvalueRs_postSI12:
+ case Rd_Rs_SI14:
+ case Rd_rvalueRs_SI15:
+ case Rd_lvalueRs_SI15:
+ case PC_DISP19div2:
+ case PC_DISP24div2:
+ case Rd_Rs_Rs:
+ case x_Rs_x:
+ case x_Rs_Rs:
+ case Rd_Rs_x:
+ case Rd_x_Rs:
+ case Rd_x_x:
+ case OP5_rvalueRs_SI15:
+ case I5_Rs_Rs_I5_OP5:
+ case x_rvalueRs_post4:
+ case Rd_rvalueRs_post4:
+ case Rd_x_I5:
+ case Rd_lvalueRs_post4:
+ case x_lvalueRs_post4:
+ case Rd_Rs_Rs_imm:
+ case NO_OPD:
+ case Rd_lvalue32Rs:
+ case Rd_rvalue32Rs:
+ case Insn_GP:
+ case Insn_PIC:
+ case Insn_internal:
+ retval = INSN_CLASS_32;
+ break;
+ case Insn_Type_PCE:
+ retval = INSN_CLASS_PCE;
+ break;
+ case Insn_Type_SYN:
+ retval = INSN_CLASS_SYN;
+ break;
+ default:
+ abort ();
+ break;
+ }
+ return retval;
+}
+
+static unsigned long
+s7_adjust_paritybit (unsigned long m_code, enum insn_class i_class)
+{
+ unsigned long result = 0;
+ unsigned long m_code_high = 0;
+ unsigned long m_code_low = 0;
+ unsigned long pb_high = 0;
+ unsigned long pb_low = 0;
+
+ if (i_class == INSN_CLASS_32)
+ {
+ pb_high = 0x80000000;
+ pb_low = 0x00008000;
+ }
+ else if (i_class == INSN_CLASS_16)
+ {
+ pb_high = 0;
+ pb_low = 0;
+ }
+ else if (i_class == INSN_CLASS_PCE)
+ {
+ pb_high = 0;
+ pb_low = 0x00008000;
+ }
+ else if (i_class == INSN_CLASS_SYN)
+ {
+ /* FIXME. at this time, INSN_CLASS_SYN must be 32 bit, but, instruction type should
+ be changed if macro instruction has been expanded. */
+ pb_high = 0x80000000;
+ pb_low = 0x00008000;
+ }
+ else
+ {
+ abort ();
+ }
+
+ m_code_high = m_code & 0x3fff8000;
+ m_code_low = m_code & 0x00007fff;
+ result = pb_high | (m_code_high << 1) | pb_low | m_code_low;
+ return result;
+
+}
+
+static void
+s7_gen_insn_frag (struct s7_score_it *part_1, struct s7_score_it *part_2)
+{
+ char *p;
+ bfd_boolean pce_p = FALSE;
+ int relaxable_p = s7_g_opt;
+ int relax_size = 0;
+ struct s7_score_it *inst1 = part_1;
+ struct s7_score_it *inst2 = part_2;
+ struct s7_score_it backup_inst1;
+
+ pce_p = (inst2) ? TRUE : FALSE;
+ memcpy (&backup_inst1, inst1, sizeof (struct s7_score_it));
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ if (pce_p)
+ {
+ backup_inst1.instruction = ((backup_inst1.instruction & 0x7FFF) << 15)
+ | (inst2->instruction & 0x7FFF);
+ backup_inst1.instruction = s7_adjust_paritybit (backup_inst1.instruction, INSN_CLASS_PCE);
+ if (!target_big_endian)
+ {
+ unsigned long tmp = backup_inst1.instruction;
+ backup_inst1.instruction = ((tmp & 0xffff) << 16)
+ | (tmp >> 16);
+ }
+ backup_inst1.relax_inst = 0x8000;
+ backup_inst1.size = s7_INSN_SIZE;
+ backup_inst1.relax_size = 0;
+ backup_inst1.type = Insn_Type_PCE;
+ }
+ else
+ {
+ backup_inst1.instruction = s7_adjust_paritybit (backup_inst1.instruction,
+ s7_GET_INSN_CLASS (backup_inst1.type));
+ }
+
+ if (backup_inst1.relax_size != 0)
+ {
+ enum insn_class tmp;
+
+ tmp = (backup_inst1.size == s7_INSN_SIZE) ? INSN_CLASS_16 : INSN_CLASS_32;
+ backup_inst1.relax_inst = s7_adjust_paritybit (backup_inst1.relax_inst, tmp);
+ }
+
+ /* Check data dependency. */
+ s7_handle_dependency (&backup_inst1);
+
+ /* Start a new frag if frag_now is not empty and is not instruction frag, maybe it contains
+ data produced by .ascii etc. Doing this is to make one instruction per frag. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+
+ /* Here, we must call frag_grow in order to keep the instruction frag type is
+ rs_machine_dependent.
+ For, frag_var may change frag_now->fr_type to rs_fill by calling frag_grow which
+ acturally will call frag_wane.
+ Calling frag_grow first will create a new frag_now which free size is 20 that is enough
+ for frag_var. */
+ frag_grow (20);
+
+ p = frag_more (backup_inst1.size);
+ s7_number_to_chars (p, backup_inst1.instruction, backup_inst1.size);
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (backup_inst1.size);
+#endif
+
+ /* Generate fixup structure. */
+ if (pce_p)
+ {
+ if (inst1->reloc.type != BFD_RELOC_NONE)
+ s7_fix_new_score (frag_now, p - frag_now->fr_literal,
+ inst1->size, &inst1->reloc.exp,
+ inst1->reloc.pc_rel, inst1->reloc.type);
+
+ if (inst2->reloc.type != BFD_RELOC_NONE)
+ s7_fix_new_score (frag_now, p - frag_now->fr_literal + 2,
+ inst2->size, &inst2->reloc.exp, inst2->reloc.pc_rel, inst2->reloc.type);
+ }
+ else
+ {
+ if (backup_inst1.reloc.type != BFD_RELOC_NONE)
+ s7_fix_new_score (frag_now, p - frag_now->fr_literal,
+ backup_inst1.size, &backup_inst1.reloc.exp,
+ backup_inst1.reloc.pc_rel, backup_inst1.reloc.type);
+ }
+
+ /* relax_size may be 2, 4, 12 or 0, 0 indicates no relaxation. */
+ relaxable_p &= (backup_inst1.relax_size != 0);
+ relax_size = relaxable_p ? backup_inst1.relax_size : 0;
+
+ p = frag_var (rs_machine_dependent, relax_size + s7_RELAX_PAD_BYTE, 0,
+ s7_RELAX_ENCODE (backup_inst1.size, backup_inst1.relax_size,
+ backup_inst1.type, 0, 0, relaxable_p),
+ backup_inst1.reloc.exp.X_add_symbol, 0, NULL);
+
+ if (relaxable_p)
+ s7_number_to_chars (p, backup_inst1.relax_inst, relax_size);
+
+ memcpy (inst1, &backup_inst1, sizeof (struct s7_score_it));
+}
+
+static void
+s7_parse_16_32_inst (char *insnstr, bfd_boolean gen_frag_p)
+{
+ char c;
+ char *p;
+ char *operator = insnstr;
+ const struct s7_asm_opcode *opcode;
+
+ /* Parse operator and operands. */
+ s7_skip_whitespace (operator);
+
+ for (p = operator; *p != '\0'; p++)
+ if ((*p == ' ') || (*p == '!'))
+ break;
+
+ if (*p == '!')
+ p++;
+
+ c = *p;
+ *p = '\0';
+
+ opcode = (const struct s7_asm_opcode *) hash_find (s7_score_ops_hsh, operator);
+ *p = c;
+
+ memset (&s7_inst, '\0', sizeof (s7_inst));
+ strcpy (s7_inst.str, insnstr);
+ if (opcode)
+ {
+ s7_inst.instruction = opcode->value;
+ s7_inst.relax_inst = opcode->relax_value;
+ s7_inst.type = opcode->type;
+ s7_inst.size = s7_GET_INSN_SIZE (s7_inst.type);
+ s7_inst.relax_size = 0;
+ s7_inst.bwarn = 0;
+ strcpy (s7_inst.name, opcode->template_name);
+ strcpy (s7_inst.reg, "");
+ s7_inst.error = NULL;
+ s7_inst.reloc.type = BFD_RELOC_NONE;
+
+ (*opcode->parms) (p);
+
+ /* It indicates current instruction is a macro instruction if s7_inst.bwarn equals -1. */
+ if ((s7_inst.bwarn != -1) && (!s7_inst.error) && (gen_frag_p))
+ s7_gen_insn_frag (&s7_inst, NULL);
+ }
+ else
+ s7_inst.error = _("unrecognized opcode");
+}
+
+static int
+s7_append_insn (char *str, bfd_boolean gen_frag_p)
+{
+ int retval = s7_SUCCESS;
+
+ s7_parse_16_32_inst (str, gen_frag_p);
+
+ if (s7_inst.error)
+ {
+ retval = (int) s7_FAIL;
+ as_bad (_("%s -- `%s'"), s7_inst.error, s7_inst.str);
+ s7_inst.error = NULL;
+ }
+
+ return retval;
+}
+
+/* Handle mv! reg_high, reg_low;
+ mv! reg_low, reg_high;
+ mv! reg_low, reg_low; */
+static void
+s7_do16_mv_rdrs (char *str)
+{
+ int reg_rd;
+ int reg_rs;
+ char *backupstr = NULL;
+
+ backupstr = str;
+ s7_skip_whitespace (str);
+
+ if ((reg_rd = s7_reg_required_here (&str, 8, s7_REG_TYPE_SCORE)) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || (reg_rs = s7_reg_required_here (&str, 4, s7_REG_TYPE_SCORE)) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ /* Case 1 : mv! or mlfh!. */
+ if (reg_rd < 16)
+ {
+ if (reg_rs < 16)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 4) & 0xf) << 15) | (0xf << 10);
+ s7_inst.relax_size = 4;
+ }
+ else
+ {
+ char append_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ sprintf (append_str, "mlfh! %s", backupstr);
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+ }
+ /* Case 2 : mhfl!. */
+ else
+ {
+ if (reg_rs > 16)
+ {
+ s7_SET_INSN_ERROR (s7_BAD_ARGS);
+ return;
+ }
+ else
+ {
+ char append_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ sprintf (append_str, "mhfl! %s", backupstr);
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+ }
+ }
+}
+
+static void
+s7_do16_rdi4 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reglow_required_here (&str, 8) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 3, _IMM4) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ if (((s7_inst.instruction >> 3) & 0x10) == 0) /* for judge is addei or subei : bit 5 =0 : addei */
+ {
+ if (((s7_inst.instruction >> 3) & 0xf) != 0xf)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | ((1 << ((s7_inst.instruction >> 3) & 0xf)) << 1);
+ s7_inst.relax_size = 4;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ if (((s7_inst.instruction >> 3) & 0xf) != 0xf)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((-(1 << ((s7_inst.instruction >> 3) & 0xf))) & 0xffff) << 1);
+ s7_inst.relax_size = 4;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ }
+}
+
+static void
+s7_do16_rdi5 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_reglow_required_here (&str, 8) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_data_op2 (&str, 3, _IMM5) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 8) & 0xf) << 15) | (((s7_inst.instruction >> 3) & 0x1f) << 10);
+ s7_inst.relax_size = 4;
+ }
+}
+
+/* Handle sdbbp. */
+
+static void
+s7_do16_xi5 (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if (s7_data_op2 (&str, 3, _IMM5) == (int) s7_FAIL || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 3) & 0x1f) << 15);
+ s7_inst.relax_size = 4;
+ }
+}
+
+/* Check that an immediate is word alignment or half word alignment.
+ If so, convert it to the right format. */
+
+static int
+s7_validate_immediate_align (int val, unsigned int data_type)
+{
+ if (data_type == _IMM5_RSHIFT_1)
+ {
+ if (val % 2)
+ {
+ s7_inst.error = _("address offset must be half word alignment");
+ return (int) s7_FAIL;
+ }
+ }
+ else if ((data_type == _IMM5_RSHIFT_2) || (data_type == _IMM10_RSHIFT_2))
+ {
+ if (val % 4)
+ {
+ s7_inst.error = _("address offset must be word alignment");
+ return (int) s7_FAIL;
+ }
+ }
+
+ return s7_SUCCESS;
+}
+
+static int
+s7_exp_ldst_offset (char **str, int shift, unsigned int data_type)
+{
+ char *dataptr;
+ int hex_p = 0;
+
+ dataptr = * str;
+
+ if ((dataptr != NULL)
+ && (((strstr (dataptr, "0x")) != NULL)
+ || ((strstr (dataptr, "0X")) != NULL)))
+ {
+ hex_p = 1;
+ if ((data_type != _SIMM16_LA)
+ && (data_type != _VALUE_HI16)
+ && (data_type != _VALUE_LO16)
+ && (data_type != _IMM16)
+ && (data_type != _IMM15)
+ && (data_type != _IMM14)
+ && (data_type != _IMM4)
+ && (data_type != _IMM5)
+ && (data_type != _IMM8)
+ && (data_type != _IMM5_RSHIFT_1)
+ && (data_type != _IMM5_RSHIFT_2)
+ && (data_type != _SIMM12)
+ && (data_type != _SIMM15)
+ && (data_type != _SIMM14_NEG)
+ && (data_type != _IMM10_RSHIFT_2))
+ {
+ data_type += 24;
+ }
+ }
+
+ if (s7_my_get_expression (&s7_inst.reloc.exp, str) == (int) s7_FAIL)
+ return (int) s7_FAIL;
+
+ if (s7_inst.reloc.exp.X_op == O_constant)
+ {
+ /* Need to check the immediate align. */
+ int value = s7_validate_immediate_align (s7_inst.reloc.exp.X_add_number, data_type);
+
+ if (value == (int) s7_FAIL)
+ return (int) s7_FAIL;
+
+ value = s7_validate_immediate (s7_inst.reloc.exp.X_add_number, data_type, hex_p);
+ if (value == (int) s7_FAIL)
+ {
+ if (data_type < 30)
+ sprintf (s7_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s7_score_df_range[data_type].bits,
+ s7_score_df_range[data_type].range[0], s7_score_df_range[data_type].range[1]);
+ else
+ sprintf (s7_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s7_score_df_range[data_type - 24].bits,
+ s7_score_df_range[data_type - 24].range[0], s7_score_df_range[data_type - 24].range[1]);
+ s7_inst.error = s7_err_msg;
+ return (int) s7_FAIL;
+ }
+
+ if (data_type == _IMM5_RSHIFT_1)
+ {
+ value >>= 1;
+ }
+ else if ((data_type == _IMM5_RSHIFT_2) || (data_type == _IMM10_RSHIFT_2))
+ {
+ value >>= 2;
+ }
+
+ if (s7_score_df_range[data_type].range[0] != 0)
+ {
+ value &= (1 << s7_score_df_range[data_type].bits) - 1;
+ }
+
+ s7_inst.instruction |= value << shift;
+ }
+ else
+ {
+ s7_inst.reloc.pc_rel = 0;
+ }
+
+ return s7_SUCCESS;
+}
+
+static void
+s7_do_ldst_insn (char *str)
+{
+ int pre_inc = 0;
+ int conflict_reg;
+ int value;
+ char * temp;
+ char *dataptr;
+ int reg;
+ int ldst_idx = 0;
+
+ int hex_p = 0;
+
+ s7_skip_whitespace (str);
+
+ if (((conflict_reg = s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ return;
+
+ /* ld/sw rD, [rA, simm15] ld/sw rD, [rA]+, simm12 ld/sw rD, [rA, simm12]+. */
+ if (*str == '[')
+ {
+ str++;
+ s7_skip_whitespace (str);
+
+ if ((reg = s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ return;
+
+ /* Conflicts can occur on stores as well as loads. */
+ conflict_reg = (conflict_reg == reg);
+ s7_skip_whitespace (str);
+ temp = str + 1; /* The latter will process decimal/hex expression. */
+
+ /* ld/sw rD, [rA]+, simm12 ld/sw rD, [rA]+. */
+ if (*str == ']')
+ {
+ str++;
+ if (*str == '+')
+ {
+ str++;
+ /* ld/sw rD, [rA]+, simm12. */
+ if (s7_skip_past_comma (&str) == s7_SUCCESS)
+ {
+ if ((s7_exp_ldst_offset (&str, 3, _SIMM12) == (int) s7_FAIL)
+ || (s7_end_of_line (str) == (int) s7_FAIL))
+ return;
+
+ if (conflict_reg)
+ {
+ unsigned int ldst_func = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+
+ if ((ldst_func == INSN_LH)
+ || (ldst_func == INSN_LHU)
+ || (ldst_func == INSN_LW)
+ || (ldst_func == INSN_LB)
+ || (ldst_func == INSN_LBU))
+ {
+ s7_inst.error = _("register same as write-back base");
+ return;
+ }
+ }
+
+ ldst_idx = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction |= s7_score_ldst_insns[ldst_idx * 3 + LDST_POST].value;
+
+ /* lw rD, [rA]+, 4 convert to pop rD, [rA]. */
+ if ((s7_inst.instruction & 0x3e000007) == 0x0e000000)
+ {
+ /* rs = r0-r7, offset = 4 */
+ if ((((s7_inst.instruction >> 15) & 0x18) == 0)
+ && (((s7_inst.instruction >> 3) & 0xfff) == 4))
+ {
+ /* Relax to pophi. */
+ if ((((s7_inst.instruction >> 20) & 0x10) == 0x10))
+ {
+ s7_inst.relax_inst = 0x0000200a | (((s7_inst.instruction >> 20) & 0xf)
+ << 8) | 1 << 7 |
+ (((s7_inst.instruction >> 15) & 0x7) << 4);
+ }
+ /* Relax to pop. */
+ else
+ {
+ s7_inst.relax_inst = 0x0000200a | (((s7_inst.instruction >> 20) & 0xf)
+ << 8) | 0 << 7 |
+ (((s7_inst.instruction >> 15) & 0x7) << 4);
+ }
+ s7_inst.relax_size = 2;
+ }
+ }
+ return;
+ }
+ /* ld/sw rD, [rA]+ convert to ld/sw rD, [rA, 0]+. */
+ else
+ {
+ s7_SET_INSN_ERROR (NULL);
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+
+ pre_inc = 1;
+ value = s7_validate_immediate (s7_inst.reloc.exp.X_add_number, _SIMM12, 0);
+ value &= (1 << s7_score_df_range[_SIMM12].bits) - 1;
+ ldst_idx = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction |= s7_score_ldst_insns[ldst_idx * 3 + pre_inc].value;
+ s7_inst.instruction |= value << 3;
+ s7_inst.relax_inst = 0x8000;
+ return;
+ }
+ }
+ /* ld/sw rD, [rA] convert to ld/sw rD, [rA, simm15]. */
+ else
+ {
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ ldst_idx = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction |= s7_score_ldst_insns[ldst_idx * 3 + LDST_NOUPDATE].value;
+
+ /* lbu rd, [rs] -> lbu! rd, [rs] */
+ if (ldst_idx == INSN_LBU)
+ {
+ s7_inst.relax_inst = INSN16_LBU;
+ }
+ else if (ldst_idx == INSN_LH)
+ {
+ s7_inst.relax_inst = INSN16_LH;
+ }
+ else if (ldst_idx == INSN_LW)
+ {
+ s7_inst.relax_inst = INSN16_LW;
+ }
+ else if (ldst_idx == INSN_SB)
+ {
+ s7_inst.relax_inst = INSN16_SB;
+ }
+ else if (ldst_idx == INSN_SH)
+ {
+ s7_inst.relax_inst = INSN16_SH;
+ }
+ else if (ldst_idx == INSN_SW)
+ {
+ s7_inst.relax_inst = INSN16_SW;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+
+ /* lw/lh/lbu/sw/sh/sb, offset = 0, relax to 16 bit instruction. */
+ if ((ldst_idx == INSN_LBU)
+ || (ldst_idx == INSN_LH)
+ || (ldst_idx == INSN_LW)
+ || (ldst_idx == INSN_SB) || (ldst_idx == INSN_SH) || (ldst_idx == INSN_SW))
+ {
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ s7_inst.relax_inst |= (2 << 12) | (((s7_inst.instruction >> 20) & 0xf) << 8) |
+ (((s7_inst.instruction >> 15) & 0xf) << 4);
+ s7_inst.relax_size = 2;
+ }
+ }
+
+ return;
+ }
+ }
+ /* ld/sw rD, [rA, simm15] ld/sw rD, [rA, simm12]+. */
+ else
+ {
+ if (s7_skip_past_comma (&str) == (int) s7_FAIL)
+ {
+ s7_inst.error = _("pre-indexed expression expected");
+ return;
+ }
+
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL)
+ return;
+
+ s7_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+
+ s7_skip_whitespace (str);
+ /* ld/sw rD, [rA, simm12]+. */
+ if (*str == '+')
+ {
+ str++;
+ pre_inc = 1;
+ if (conflict_reg)
+ {
+ unsigned int ldst_func = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+
+ if ((ldst_func == INSN_LH)
+ || (ldst_func == INSN_LHU)
+ || (ldst_func == INSN_LW)
+ || (ldst_func == INSN_LB)
+ || (ldst_func == INSN_LBU))
+ {
+ s7_inst.error = _("register same as write-back base");
+ return;
+ }
+ }
+ }
+
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if (s7_inst.reloc.exp.X_op == O_constant)
+ {
+ unsigned int data_type;
+
+ if (pre_inc == 1)
+ data_type = _SIMM12;
+ else
+ data_type = _SIMM15;
+ dataptr = temp;
+
+ if ((dataptr != NULL)
+ && (((strstr (dataptr, "0x")) != NULL)
+ || ((strstr (dataptr, "0X")) != NULL)))
+ {
+ hex_p = 1;
+ if ((data_type != _SIMM16_LA)
+ && (data_type != _VALUE_HI16)
+ && (data_type != _VALUE_LO16)
+ && (data_type != _IMM16)
+ && (data_type != _IMM15)
+ && (data_type != _IMM14)
+ && (data_type != _IMM4)
+ && (data_type != _IMM5)
+ && (data_type != _IMM8)
+ && (data_type != _SIMM12)
+ && (data_type != _SIMM15)
+ && (data_type != _IMM5_RSHIFT_1)
+ && (data_type != _IMM5_RSHIFT_2)
+ && (data_type != _SIMM14_NEG)
+ && (data_type != _IMM10_RSHIFT_2))
+ {
+ data_type += 24;
+ }
+ }
+
+ value = s7_validate_immediate (s7_inst.reloc.exp.X_add_number, data_type, hex_p);
+ if (value == (int) s7_FAIL)
+ {
+ if (data_type < 30)
+ sprintf (s7_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s7_score_df_range[data_type].bits,
+ s7_score_df_range[data_type].range[0], s7_score_df_range[data_type].range[1]);
+ else
+ sprintf (s7_err_msg,
+ _("invalid constant: %d bit expression not in range %d..%d"),
+ s7_score_df_range[data_type - 24].bits,
+ s7_score_df_range[data_type - 24].range[0],
+ s7_score_df_range[data_type - 24].range[1]);
+ s7_inst.error = s7_err_msg;
+ return;
+ }
+
+ value &= (1 << s7_score_df_range[data_type].bits) - 1;
+ ldst_idx = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction |= s7_score_ldst_insns[ldst_idx * 3 + pre_inc].value;
+ if (pre_inc == 1)
+ s7_inst.instruction |= value << 3;
+ else
+ s7_inst.instruction |= value;
+
+ /* lw rD, [rA, simm15] */
+ if ((s7_inst.instruction & 0x3e000000) == 0x20000000)
+ {
+ /* Both rD and rA are in [r0 - r15]. */
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0)
+ && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 = 0, lw -> lw!. */
+ if ((s7_inst.instruction & 0x7fff) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* rA = r2, lw -> lwp!. */
+ else if ((((s7_inst.instruction >> 15) & 0xf) == 2)
+ && ((s7_inst.instruction & 0x3) == 0)
+ && ((s7_inst.instruction & 0x7fff) < 128))
+ {
+ s7_inst.relax_inst = 0x7000 | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | (((s7_inst.instruction & 0x7fff) >> 2) << 3);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ /* sw rD, [rA, simm15] */
+ else if ((s7_inst.instruction & 0x3e000000) == 0x28000000)
+ {
+ /* Both rD and rA are in [r0 - r15]. */
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 = 0, sw -> sw!. */
+ if ((s7_inst.instruction & 0x7fff) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* rA = r2, sw -> swp!. */
+ else if ((((s7_inst.instruction >> 15) & 0xf) == 2)
+ && ((s7_inst.instruction & 0x3) == 0)
+ && ((s7_inst.instruction & 0x7fff) < 128))
+ {
+ s7_inst.relax_inst = 0x7004 | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | (((s7_inst.instruction & 0x7fff) >> 2) << 3);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ /* sw rD, [rA, simm15]+ sw pre. */
+ else if ((s7_inst.instruction & 0x3e000007) == 0x06000004)
+ {
+ /* rA is in [r0 - r7], and simm15 = -4. */
+ if ((((s7_inst.instruction >> 15) & 0x18) == 0)
+ && (((s7_inst.instruction >> 3) & 0xfff) == 0xffc))
+ {
+ /* sw -> pushhi!. */
+ if ((((s7_inst.instruction >> 20) & 0x10) == 0x10))
+ {
+ s7_inst.relax_inst = 0x0000200e | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | 1 << 7 | (((s7_inst.instruction >> 15) & 0x7) << 4);
+ s7_inst.relax_size = 2;
+ }
+ /* sw -> push!. */
+ else
+ {
+ s7_inst.relax_inst = 0x0000200e | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | 0 << 7 | (((s7_inst.instruction >> 15) & 0x7) << 4);
+ s7_inst.relax_size = 2;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ /* lh rD, [rA, simm15] */
+ else if ((s7_inst.instruction & 0x3e000000) == 0x22000000)
+ {
+ /* Both rD and rA are in [r0 - r15]. */
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 = 0, lh -> lh!. */
+ if ((s7_inst.instruction & 0x7fff) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* rA = r2, lh -> lhp!. */
+ else if ((((s7_inst.instruction >> 15) & 0xf) == 2)
+ && ((s7_inst.instruction & 0x1) == 0)
+ && ((s7_inst.instruction & 0x7fff) < 64))
+ {
+ s7_inst.relax_inst = 0x7001 | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | (((s7_inst.instruction & 0x7fff) >> 1) << 3);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ /* sh rD, [rA, simm15] */
+ else if ((s7_inst.instruction & 0x3e000000) == 0x2a000000)
+ {
+ /* Both rD and rA are in [r0 - r15]. */
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 = 0, sh -> sh!. */
+ if ((s7_inst.instruction & 0x7fff) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* rA = r2, sh -> shp!. */
+ else if ((((s7_inst.instruction >> 15) & 0xf) == 2)
+ && ((s7_inst.instruction & 0x1) == 0)
+ && ((s7_inst.instruction & 0x7fff) < 64))
+ {
+ s7_inst.relax_inst = 0x7005 | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | (((s7_inst.instruction & 0x7fff) >> 1) << 3);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ /* lbu rD, [rA, simm15] */
+ else if ((s7_inst.instruction & 0x3e000000) == 0x2c000000)
+ {
+ /* Both rD and rA are in [r0 - r15]. */
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 = 0, lbu -> lbu!. */
+ if ((s7_inst.instruction & 0x7fff) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* rA = r2, lbu -> lbup!. */
+ else if ((((s7_inst.instruction >> 15) & 0xf) == 2)
+ && ((s7_inst.instruction & 0x7fff) < 32))
+ {
+ s7_inst.relax_inst = 0x7003 | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | ((s7_inst.instruction & 0x7fff) << 3);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ /* sb rD, [rA, simm15] */
+ else if ((s7_inst.instruction & 0x3e000000) == 0x2e000000)
+ {
+ /* Both rD and rA are in [r0 - r15]. */
+ if ((((s7_inst.instruction >> 15) & 0x10) == 0) && (((s7_inst.instruction >> 20) & 0x10) == 0))
+ {
+ /* simm15 = 0, sb -> sb!. */
+ if ((s7_inst.instruction & 0x7fff) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 15) & 0xf) << 4)
+ | (((s7_inst.instruction >> 20) & 0xf) << 8);
+ s7_inst.relax_size = 2;
+ }
+ /* rA = r2, sb -> sb!. */
+ else if ((((s7_inst.instruction >> 15) & 0xf) == 2)
+ && ((s7_inst.instruction & 0x7fff) < 32))
+ {
+ s7_inst.relax_inst = 0x7007 | (((s7_inst.instruction >> 20) & 0xf) << 8)
+ | ((s7_inst.instruction & 0x7fff) << 3);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+
+ return;
+ }
+ else
+ {
+ /* FIXME: may set error, for there is no ld/sw rD, [rA, label] */
+ s7_inst.reloc.pc_rel = 0;
+ }
+ }
+ }
+ else
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ }
+}
+
+/* Handle cache. */
+static void
+s7_do_cache (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if ((s7_data_op2 (&str, 20, _IMM5) == (int) s7_FAIL) || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ int cache_op;
+
+ cache_op = (s7_inst.instruction >> 20) & 0x1F;
+ sprintf (s7_inst.name, "cache %d", cache_op);
+ }
+
+ if (*str == '[')
+ {
+ str++;
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL)
+ return;
+
+ s7_skip_whitespace (str);
+
+ /* cache op, [rA] */
+ if (s7_skip_past_comma (&str) == (int) s7_FAIL)
+ {
+ s7_SET_INSN_ERROR (NULL);
+ if (*str != ']')
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+ str++;
+ }
+ /* cache op, [rA, simm15] */
+ else
+ {
+ if (s7_exp_ldst_offset (&str, 0, _SIMM15) == (int) s7_FAIL)
+ {
+ return;
+ }
+
+ s7_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+ }
+
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+ }
+ else
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ }
+}
+
+static void
+s7_do_crdcrscrsimm5 (char *str)
+{
+ char *strbak;
+
+ strbak = str;
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE_CR) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE_CR) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL
+ || s7_reg_required_here (&str, 10, s7_REG_TYPE_SCORE_CR) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL)
+ {
+ str = strbak;
+ /* cop1 cop_code20. */
+ if (s7_data_op2 (&str, 5, _IMM20) == (int) s7_FAIL)
+ return;
+ }
+ else
+ {
+ if (s7_data_op2 (&str, 5, _IMM5) == (int) s7_FAIL)
+ return;
+ }
+
+ s7_end_of_line (str);
+}
+
+/* Handle ldc/stc. */
+static void
+s7_do_ldst_cop (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if ((s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE_CR) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ return;
+
+ if (*str == '[')
+ {
+ str++;
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL)
+ return;
+
+ s7_skip_whitespace (str);
+
+ if (*str++ != ']')
+ {
+ if (s7_exp_ldst_offset (&str, 5, _IMM10_RSHIFT_2) == (int) s7_FAIL)
+ return;
+
+ s7_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+ }
+
+ s7_end_of_line (str);
+ }
+ else
+ s7_inst.error = s7_BAD_ARGS;
+}
+
+static void
+s7_do16_ldst_insn (char *str)
+{
+ s7_skip_whitespace (str);
+
+ if ((s7_reglow_required_here (&str, 8) == (int) s7_FAIL) || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ return;
+
+ if (*str == '[')
+ {
+ int reg;
+
+ str++;
+ s7_skip_whitespace (str);
+
+ if ((reg = s7_reglow_required_here (&str, 4)) == (int) s7_FAIL)
+ return;
+
+ s7_skip_whitespace (str);
+ if (*str++ == ']')
+ {
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 4) & 0xf) << 15);
+ s7_inst.relax_size = 4;
+ }
+ }
+ else
+ {
+ s7_inst.error = _("missing ]");
+ }
+ }
+ else
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ }
+}
+
+/* Handle lbup!/lhp!/ldiu!/lwp!/sbp!/shp!/swp!. */
+
+static void
+s7_do16_ldst_imm_insn (char *str)
+{
+ char data_exp[s7_MAX_LITERAL_POOL_SIZE];
+ int reg_rd;
+ char *dataptr = NULL, *pp = NULL;
+ int cnt = 0;
+ int assign_data = (int) s7_FAIL;
+ unsigned int ldst_func;
+
+ s7_skip_whitespace (str);
+
+ if (((reg_rd = s7_reglow_required_here (&str, 8)) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ return;
+
+ s7_skip_whitespace (str);
+ dataptr = str;
+
+ while ((*dataptr != '\0') && (*dataptr != '|') && (cnt <= s7_MAX_LITERAL_POOL_SIZE))
+ {
+ data_exp[cnt] = *dataptr;
+ dataptr++;
+ cnt++;
+ }
+
+ data_exp[cnt] = '\0';
+ pp = &data_exp[0];
+
+ str = dataptr;
+
+ ldst_func = s7_inst.instruction & LDST16_RI_MASK;
+ if (ldst_func == N16_LIU)
+ assign_data = s7_exp_ldst_offset (&pp, 0, _IMM8);
+ else if (ldst_func == N16_LHP || ldst_func == N16_SHP)
+ assign_data = s7_exp_ldst_offset (&pp, 3, _IMM5_RSHIFT_1);
+ else if (ldst_func == N16_LWP || ldst_func == N16_SWP)
+ assign_data = s7_exp_ldst_offset (&pp, 3, _IMM5_RSHIFT_2);
+ else
+ assign_data = s7_exp_ldst_offset (&pp, 3, _IMM5);
+
+ if ((assign_data == (int) s7_FAIL) || (s7_end_of_line (pp) == (int) s7_FAIL))
+ return;
+ else
+ {
+ if ((s7_inst.instruction & 0x7000) == N16_LIU)
+ {
+ s7_inst.relax_inst |= ((s7_inst.instruction >> 8) & 0xf) << 20
+ | ((s7_inst.instruction & 0xff) << 1);
+ }
+ else if (((s7_inst.instruction & 0x7007) == N16_LHP)
+ || ((s7_inst.instruction & 0x7007) == N16_SHP))
+ {
+ s7_inst.relax_inst |= ((s7_inst.instruction >> 8) & 0xf) << 20 | 2 << 15
+ | (((s7_inst.instruction >> 3) & 0x1f) << 1);
+ }
+ else if (((s7_inst.instruction & 0x7007) == N16_LWP)
+ || ((s7_inst.instruction & 0x7007) == N16_SWP))
+ {
+ s7_inst.relax_inst |= ((s7_inst.instruction >> 8) & 0xf) << 20 | 2 << 15
+ | (((s7_inst.instruction >> 3) & 0x1f) << 2);
+ }
+ else if (((s7_inst.instruction & 0x7007) == N16_LBUP)
+ || ((s7_inst.instruction & 0x7007) == N16_SBP))
+ {
+ s7_inst.relax_inst |= ((s7_inst.instruction >> 8) & 0xf) << 20 | 2 << 15
+ | (((s7_inst.instruction >> 3) & 0x1f));
+ }
+
+ s7_inst.relax_size = 4;
+ }
+}
+
+static void
+s7_do16_push_pop (char *str)
+{
+ int reg_rd;
+ int H_bit_mask = 0;
+
+ s7_skip_whitespace (str);
+ if (((reg_rd = s7_reg_required_here (&str, 8, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ return;
+
+ if (reg_rd >= 16)
+ H_bit_mask = 1;
+
+ /* s7_reg_required_here will change bit 12 of opcode, so we must restore bit 12. */
+ s7_inst.instruction &= ~(1 << 12);
+
+ s7_inst.instruction |= H_bit_mask << 7;
+
+ if (*str == '[')
+ {
+ int reg;
+
+ str++;
+ s7_skip_whitespace (str);
+ if ((reg = s7_reg_required_here (&str, 4, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ return;
+ else if (reg > 7)
+ {
+ if (!s7_inst.error)
+ s7_inst.error = _("base register nums are over 3 bit");
+
+ return;
+ }
+
+ s7_skip_whitespace (str);
+ if ((*str++ != ']') || (s7_end_of_line (str) == (int) s7_FAIL))
+ {
+ if (!s7_inst.error)
+ s7_inst.error = _("missing ]");
+
+ return;
+ }
+
+ /* pop! */
+ if ((s7_inst.instruction & 0xf) == 0xa)
+ {
+ if (H_bit_mask)
+ {
+ s7_inst.relax_inst |= ((((s7_inst.instruction >> 8) & 0xf) | 0x10) << 20)
+ | (((s7_inst.instruction >> 4) & 0x7) << 15) | (4 << 3);
+ }
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 4) & 0x7) << 15) | (4 << 3);
+ }
+ }
+ /* push! */
+ else
+ {
+ if (H_bit_mask)
+ {
+ s7_inst.relax_inst |= ((((s7_inst.instruction >> 8) & 0xf) | 0x10) << 20)
+ | (((s7_inst.instruction >> 4) & 0x7) << 15) | (((-4) & 0xfff) << 3);
+ }
+ else
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 8) & 0xf) << 20)
+ | (((s7_inst.instruction >> 4) & 0x7) << 15) | (((-4) & 0xfff) << 3);
+ }
+ }
+ s7_inst.relax_size = 4;
+ }
+ else
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ }
+}
+
+/* Handle lcb/lcw/lce/scb/scw/sce. */
+static void
+s7_do_ldst_unalign (char *str)
+{
+ int conflict_reg;
+
+ if (s7_university_version == 1)
+ {
+ s7_inst.error = s7_ERR_FOR_SCORE5U_ATOMIC;
+ return;
+ }
+
+ s7_skip_whitespace (str);
+
+ /* lcb/scb [rA]+. */
+ if (*str == '[')
+ {
+ str++;
+ s7_skip_whitespace (str);
+
+ if (s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE) == (int) s7_FAIL)
+ return;
+
+ if (*str++ == ']')
+ {
+ if (*str++ != '+')
+ {
+ s7_inst.error = _("missing +");
+ return;
+ }
+ }
+ else
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+ }
+ /* lcw/lce/scb/sce rD, [rA]+. */
+ else
+ {
+ if (((conflict_reg = s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ {
+ return;
+ }
+
+ s7_skip_whitespace (str);
+ if (*str++ == '[')
+ {
+ int reg;
+
+ s7_skip_whitespace (str);
+ if ((reg = s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ {
+ return;
+ }
+
+ /* Conflicts can occur on stores as well as loads. */
+ conflict_reg = (conflict_reg == reg);
+ s7_skip_whitespace (str);
+ if (*str++ == ']')
+ {
+ unsigned int ldst_func = s7_inst.instruction & LDST_UNALIGN_MASK;
+
+ if (*str++ == '+')
+ {
+ if (conflict_reg)
+ {
+ as_warn (_("%s register same as write-back base"),
+ ((ldst_func & UA_LCE) || (ldst_func & UA_LCW)
+ ? _("destination") : _("source")));
+ }
+ }
+ else
+ {
+ s7_inst.error = _("missing +");
+ return;
+ }
+
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+ }
+ else
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+ }
+ else
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ return;
+ }
+ }
+}
+
+/* Handle alw/asw. */
+
+static void
+s7_do_ldst_atomic (char *str)
+{
+ if (s7_university_version == 1)
+ {
+ s7_inst.error = s7_ERR_FOR_SCORE5U_ATOMIC;
+ return;
+ }
+
+ s7_skip_whitespace (str);
+
+ if ((s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else
+ {
+
+ s7_skip_whitespace (str);
+ if (*str++ == '[')
+ {
+ int reg;
+
+ s7_skip_whitespace (str);
+ if ((reg = s7_reg_required_here (&str, 15, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ {
+ return;
+ }
+
+ s7_skip_whitespace (str);
+ if (*str++ != ']')
+ {
+ s7_inst.error = _("missing ]");
+ return;
+ }
+
+ s7_end_of_line (str);
+ }
+ else
+ s7_inst.error = s7_BAD_ARGS;
+ }
+}
+
+static void
+s7_build_relax_frag (struct s7_score_it fix_insts[s7_RELAX_INST_NUM],
+ int fix_num ATTRIBUTE_UNUSED,
+ struct s7_score_it var_insts[s7_RELAX_INST_NUM], int var_num,
+ symbolS *add_symbol)
+{
+ int i;
+ char *p;
+ fixS *fixp = NULL;
+ fixS *cur_fixp = NULL;
+ long where;
+ struct s7_score_it inst_main;
+
+ memcpy (&inst_main, &fix_insts[0], sizeof (struct s7_score_it));
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ inst_main.instruction = s7_adjust_paritybit (inst_main.instruction, s7_GET_INSN_CLASS (inst_main.type));
+ inst_main.type = Insn_PIC;
+
+ for (i = 0; i < var_num; i++)
+ {
+ inst_main.relax_size += var_insts[i].size;
+ var_insts[i].instruction = s7_adjust_paritybit (var_insts[i].instruction,
+ s7_GET_INSN_CLASS (var_insts[i].type));
+ }
+
+ /* Check data dependency. */
+ s7_handle_dependency (&inst_main);
+
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ {
+ frag_wane (frag_now);
+ }
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ /* Write fr_fix part. */
+ p = frag_more (inst_main.size);
+ s7_number_to_chars (p, inst_main.instruction, inst_main.size);
+
+ if (inst_main.reloc.type != BFD_RELOC_NONE)
+ fixp = s7_fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size,
+ &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type);
+
+ frag_now->tc_frag_data.fixp = fixp;
+ cur_fixp = frag_now->tc_frag_data.fixp;
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst_main.size);
+#endif
+
+ where = p - frag_now->fr_literal + inst_main.size;
+ for (i = 0; i < var_num; i++)
+ {
+ if (i > 0)
+ where += var_insts[i - 1].size;
+
+ if (var_insts[i].reloc.type != BFD_RELOC_NONE)
+ {
+ fixp = s7_fix_new_score (frag_now, where, var_insts[i].size,
+ &var_insts[i].reloc.exp, var_insts[i].reloc.pc_rel,
+ var_insts[i].reloc.type);
+ if (fixp)
+ {
+ if (cur_fixp)
+ {
+ cur_fixp->fx_next = fixp;
+ cur_fixp = cur_fixp->fx_next;
+ }
+ else
+ {
+ frag_now->tc_frag_data.fixp = fixp;
+ cur_fixp = frag_now->tc_frag_data.fixp;
+ }
+ }
+ }
+ }
+
+ p = frag_var (rs_machine_dependent, inst_main.relax_size + s7_RELAX_PAD_BYTE, 0,
+ s7_RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type,
+ 0, inst_main.size, 0), add_symbol, 0, NULL);
+
+ /* Write fr_var part.
+ no calling s7_gen_insn_frag, no fixS will be generated. */
+ for (i = 0; i < var_num; i++)
+ {
+ s7_number_to_chars (p, var_insts[i].instruction, var_insts[i].size);
+ p += var_insts[i].size;
+ }
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+}
+
+/* Build a relax frag for la instruction when generating s7_PIC,
+ external symbol first and local symbol second. */
+
+static void
+s7_build_la_pic (int reg_rd, expressionS exp)
+{
+ symbolS *add_symbol = exp.X_add_symbol;
+ offsetT add_number = exp.X_add_number;
+ struct s7_score_it fix_insts[s7_RELAX_INST_NUM];
+ struct s7_score_it var_insts[s7_RELAX_INST_NUM];
+ int fix_num = 0;
+ int var_num = 0;
+ char tmp[s7_MAX_LITERAL_POOL_SIZE];
+ int r1_bak;
+
+ r1_bak = s7_nor1;
+ s7_nor1 = 0;
+
+ if (add_number == 0)
+ {
+ fix_num = 1;
+ var_num = 2;
+
+ /* For an external symbol, only one insn is generated;
+ For a local symbol, two insns are generated. */
+ /* Fix part
+ For an external symbol: lw rD, <sym>($gp)
+ (BFD_RELOC_SCORE_GOT15 or BFD_RELOC_SCORE_CALL15) */
+ sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ if (reg_rd == s7_PIC_CALL_REG)
+ s7_inst.reloc.type = BFD_RELOC_SCORE_CALL15;
+ memcpy (&fix_insts[0], &s7_inst, sizeof (struct s7_score_it));
+
+ /* Var part
+ For a local symbol :
+ lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15)
+ addi rD, <sym> (BFD_RELOC_GOT_LO16) */
+ s7_inst.reloc.type = BFD_RELOC_SCORE_GOT15;
+ memcpy (&var_insts[0], &s7_inst, sizeof (struct s7_score_it));
+ sprintf (tmp, "addi_s_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&var_insts[1], &s7_inst, sizeof (struct s7_score_it));
+ s7_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+ }
+ else if (add_number >= -0x8000 && add_number <= 0x7fff)
+ {
+ /* Insn 1: lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) */
+ sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s7_append_insn (tmp, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Insn 2 */
+ fix_num = 1;
+ var_num = 1;
+ /* Fix part
+ For an external symbol: addi rD, <constant> */
+ sprintf (tmp, "addi r%d, %d", reg_rd, (int) add_number);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s7_inst, sizeof (struct s7_score_it));
+
+ /* Var part
+ For a local symbol: addi rD, <sym>+<constant> (BFD_RELOC_GOT_LO16) */
+ sprintf (tmp, "addi_s_pic r%d, %s + %d", reg_rd, add_symbol->bsym->name, (int) add_number);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&var_insts[0], &s7_inst, sizeof (struct s7_score_it));
+ s7_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+ }
+ else
+ {
+ int hi = (add_number >> 16) & 0x0000FFFF;
+ int lo = add_number & 0x0000FFFF;
+
+ /* Insn 1: lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15) */
+ sprintf (tmp, "lw_pic r%d, %s", reg_rd, add_symbol->bsym->name);
+ if (s7_append_insn (tmp, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Insn 2 */
+ fix_num = 1;
+ var_num = 1;
+ /* Fix part
+ For an external symbol: ldis r1, HI%<constant> */
+ sprintf (tmp, "ldis r1, %d", hi);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s7_inst, sizeof (struct s7_score_it));
+
+ /* Var part
+ For a local symbol: ldis r1, HI%<constant>
+ but, if lo is outof 16 bit, make hi plus 1 */
+ if ((lo < -0x8000) || (lo > 0x7fff))
+ {
+ hi += 1;
+ }
+ sprintf (tmp, "ldis_pic r1, %d", hi);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&var_insts[0], &s7_inst, sizeof (struct s7_score_it));
+ s7_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+
+ /* Insn 3 */
+ fix_num = 1;
+ var_num = 1;
+ /* Fix part
+ For an external symbol: ori r1, LO%<constant> */
+ sprintf (tmp, "ori r1, %d", lo);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s7_inst, sizeof (struct s7_score_it));
+
+ /* Var part
+ For a local symbol: addi r1, <sym>+LO%<constant> (BFD_RELOC_GOT_LO16) */
+ sprintf (tmp, "addi_u_pic r1, %s + %d", add_symbol->bsym->name, lo);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&var_insts[0], &s7_inst, sizeof (struct s7_score_it));
+ s7_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+
+ /* Insn 4: add rD, rD, r1 */
+ sprintf (tmp, "add r%d, r%d, r1", reg_rd, reg_rd);
+ if (s7_append_insn (tmp, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+
+ s7_nor1 = r1_bak;
+}
+
+/* Handle la. */
+
+static void
+s7_do_macro_la_rdi32 (char *str)
+{
+ int reg_rd;
+
+ s7_skip_whitespace (str);
+ if ((reg_rd = s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE)) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ char append_str[s7_MAX_LITERAL_POOL_SIZE];
+ char *keep_data = str;
+
+ /* Check immediate value. */
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL)
+ {
+ s7_inst.error = _("expression error");
+ return;
+ }
+ else if ((s7_inst.reloc.exp.X_add_symbol == NULL)
+ && (s7_validate_immediate (s7_inst.reloc.exp.X_add_number, _IMM32, 0) == (int) s7_FAIL))
+ {
+ s7_inst.error = _("value not in range [0, 0xffffffff]");
+ return;
+ }
+
+ /* Reset str. */
+ str = keep_data;
+
+ /* la rd, simm16. */
+ if (s7_data_op2 (&str, 1, _SIMM16_LA) != (int) s7_FAIL)
+ {
+ s7_end_of_line (str);
+ return;
+ }
+ /* la rd, imm32 or la rd, label. */
+ else
+ {
+ s7_SET_INSN_ERROR (NULL);
+ str = keep_data;
+ if ((s7_data_op2 (&str, 1, _VALUE_HI16) == (int) s7_FAIL)
+ || (s7_end_of_line (str) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ if ((s7_score_pic == s7_NO_PIC) || (!s7_inst.reloc.exp.X_add_symbol))
+ {
+ sprintf (append_str, "ld_i32hi r%d, %s", reg_rd, keep_data);
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ sprintf (append_str, "ld_i32lo r%d, %s", reg_rd, keep_data);
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+ }
+ else
+ {
+ gas_assert (s7_inst.reloc.exp.X_add_symbol);
+ s7_build_la_pic (reg_rd, s7_inst.reloc.exp);
+ }
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+ }
+ }
+}
+
+/* Handle li. */
+
+static void
+s7_do_macro_li_rdi32 (char *str)
+{
+ int reg_rd;
+
+ s7_skip_whitespace (str);
+ if ((reg_rd = s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE)) == (int) s7_FAIL
+ || s7_skip_past_comma (&str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else
+ {
+ char *keep_data = str;
+
+ /* Check immediate value. */
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL)
+ {
+ s7_inst.error = _("expression error");
+ return;
+ }
+ else if (!(s7_inst.reloc.exp.X_add_number >= -0xffffffffLL
+ && s7_inst.reloc.exp.X_add_number <= 0xffffffffLL))
+ {
+ s7_inst.error = _("value not in range [-0xffffffff, 0xffffffff]");
+ return;
+ }
+
+ /* Reset str. */
+ str = keep_data;
+
+ /* li rd, simm16. */
+ if (s7_data_op2 (&str, 1, _SIMM16_LA) != (int) s7_FAIL)
+ {
+ s7_end_of_line (str);
+ return;
+ }
+ /* li rd, imm32. */
+ else
+ {
+ char append_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ str = keep_data;
+
+ if ((s7_data_op2 (&str, 1, _VALUE_HI16) == (int) s7_FAIL)
+ || (s7_end_of_line (str) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else if (s7_inst.reloc.exp.X_add_symbol)
+ {
+ s7_inst.error = _("li rd label isn't correct instruction form");
+ return;
+ }
+ else
+ {
+ sprintf (append_str, "ld_i32hi r%d, %s", reg_rd, keep_data);
+
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+ else
+ {
+ sprintf (append_str, "ld_i32lo r%d, %s", reg_rd, keep_data);
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+ }
+ }
+ }
+}
+
+/* Handle mul/mulu/div/divu/rem/remu. */
+
+static void
+s7_do_macro_mul_rdrsrs (char *str)
+{
+ int reg_rd;
+ int reg_rs1;
+ int reg_rs2;
+ char *backupstr;
+ char append_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ if (s7_university_version == 1)
+ as_warn ("%s", s7_ERR_FOR_SCORE5U_MUL_DIV);
+
+ strcpy (append_str, str);
+ backupstr = append_str;
+ s7_skip_whitespace (backupstr);
+ if (((reg_rd = s7_reg_required_here (&backupstr, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&backupstr) == (int) s7_FAIL)
+ || ((reg_rs1 = s7_reg_required_here (&backupstr, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL))
+ {
+ s7_inst.error = s7_BAD_ARGS;
+ return;
+ }
+
+ if (s7_skip_past_comma (&backupstr) == (int) s7_FAIL)
+ {
+ /* rem/remu rA, rB is error format. */
+ if (strcmp (s7_inst.name, "rem") == 0 || strcmp (s7_inst.name, "remu") == 0)
+ {
+ s7_SET_INSN_ERROR (s7_BAD_ARGS);
+ }
+ else
+ {
+ s7_SET_INSN_ERROR (NULL);
+ s7_do_rsrs (str);
+ }
+ return;
+ }
+ else
+ {
+ s7_SET_INSN_ERROR (NULL);
+ if (((reg_rs2 = s7_reg_required_here (&backupstr, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ || (s7_end_of_line (backupstr) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ char append_str1[s7_MAX_LITERAL_POOL_SIZE];
+
+ if (strcmp (s7_inst.name, "rem") == 0)
+ {
+ sprintf (append_str, "mul r%d, r%d", reg_rs1, reg_rs2);
+ sprintf (append_str1, "mfceh r%d", reg_rd);
+ }
+ else if (strcmp (s7_inst.name, "remu") == 0)
+ {
+ sprintf (append_str, "mulu r%d, r%d", reg_rs1, reg_rs2);
+ sprintf (append_str1, "mfceh r%d", reg_rd);
+ }
+ else
+ {
+ sprintf (append_str, "%s r%d, r%d", s7_inst.name, reg_rs1, reg_rs2);
+ sprintf (append_str1, "mfcel r%d", reg_rd);
+ }
+
+ /* Output mul/mulu or div/divu or rem/remu. */
+ if (s7_append_insn (append_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Output mfcel or mfceh. */
+ if (s7_append_insn (append_str1, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+ }
+}
+
+static void
+s7_exp_macro_ldst_abs (char *str)
+{
+ int reg_rd;
+ char *backupstr, *tmp;
+ char append_str[s7_MAX_LITERAL_POOL_SIZE];
+ char verifystr[s7_MAX_LITERAL_POOL_SIZE];
+ struct s7_score_it inst_backup;
+ int r1_bak = 0;
+
+ r1_bak = s7_nor1;
+ s7_nor1 = 0;
+ memcpy (&inst_backup, &s7_inst, sizeof (struct s7_score_it));
+
+ strcpy (verifystr, str);
+ backupstr = verifystr;
+ s7_skip_whitespace (backupstr);
+ if ((reg_rd = s7_reg_required_here (&backupstr, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ return;
+
+ tmp = backupstr;
+ if (s7_skip_past_comma (&backupstr) == (int) s7_FAIL)
+ return;
+
+ backupstr = tmp;
+ sprintf (append_str, "li r1 %s", backupstr);
+ s7_append_insn (append_str, TRUE);
+
+ memcpy (&s7_inst, &inst_backup, sizeof (struct s7_score_it));
+ sprintf (append_str, " r%d, [r1,0]", reg_rd);
+ s7_do_ldst_insn (append_str);
+
+ s7_nor1 = r1_bak;
+}
+
+static int
+s7_nopic_need_relax (symbolS * sym, int before_relaxing)
+{
+ if (sym == NULL)
+ return 0;
+ else if (s7_USE_GLOBAL_POINTER_OPT && s7_g_switch_value > 0)
+ {
+ const char *symname;
+ const char *segname;
+
+ /* Find out whether this symbol can be referenced off the $gp
+ register. It can be if it is smaller than the -G size or if
+ it is in the .sdata or .sbss section. Certain symbols can
+ not be referenced off the $gp, although it appears as though
+ they can. */
+ symname = S_GET_NAME (sym);
+ if (symname != NULL
+ && (strcmp (symname, "eprol") == 0
+ || strcmp (symname, "etext") == 0
+ || strcmp (symname, "_gp") == 0
+ || strcmp (symname, "edata") == 0
+ || strcmp (symname, "_fbss") == 0
+ || strcmp (symname, "_fdata") == 0
+ || strcmp (symname, "_ftext") == 0
+ || strcmp (symname, "end") == 0
+ || strcmp (symname, GP_DISP_LABEL) == 0))
+ {
+ return 1;
+ }
+ else if ((!S_IS_DEFINED (sym) || S_IS_COMMON (sym)) && (0
+ /* We must defer this decision until after the whole file has been read,
+ since there might be a .extern after the first use of this symbol. */
+ || (before_relaxing
+ && S_GET_VALUE (sym) == 0)
+ || (S_GET_VALUE (sym) != 0
+ && S_GET_VALUE (sym) <= s7_g_switch_value)))
+ {
+ return 0;
+ }
+
+ segname = segment_name (S_GET_SEGMENT (sym));
+ return (strcmp (segname, ".sdata") != 0
+ && strcmp (segname, ".sbss") != 0
+ && strncmp (segname, ".sdata.", 7) != 0
+ && strncmp (segname, ".gnu.linkonce.s.", 16) != 0);
+ }
+ /* We are not optimizing for the $gp register. */
+ else
+ return 1;
+}
+
+/* Build a relax frag for lw/st instruction when generating s7_PIC,
+ external symbol first and local symbol second. */
+
+static void
+s7_build_lwst_pic (int reg_rd, expressionS exp, const char *insn_name)
+{
+ symbolS *add_symbol = exp.X_add_symbol;
+ int add_number = exp.X_add_number;
+ struct s7_score_it fix_insts[s7_RELAX_INST_NUM];
+ struct s7_score_it var_insts[s7_RELAX_INST_NUM];
+ int fix_num = 0;
+ int var_num = 0;
+ char tmp[s7_MAX_LITERAL_POOL_SIZE];
+ int r1_bak;
+
+ r1_bak = s7_nor1;
+ s7_nor1 = 0;
+
+ if ((add_number == 0) || (add_number >= -0x8000 && add_number <= 0x7fff))
+ {
+ fix_num = 1;
+ var_num = 2;
+
+ /* For an external symbol, two insns are generated;
+ For a local symbol, three insns are generated. */
+ /* Fix part
+ For an external symbol: lw rD, <sym>($gp)
+ (BFD_RELOC_SCORE_GOT15) */
+ sprintf (tmp, "lw_pic r1, %s", add_symbol->bsym->name);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&fix_insts[0], &s7_inst, sizeof (struct s7_score_it));
+
+ /* Var part
+ For a local symbol :
+ lw rD, <sym>($gp) (BFD_RELOC_SCORE_GOT15)
+ addi rD, <sym> (BFD_RELOC_GOT_LO16) */
+ s7_inst.reloc.type = BFD_RELOC_SCORE_GOT15;
+ memcpy (&var_insts[0], &s7_inst, sizeof (struct s7_score_it));
+ sprintf (tmp, "addi_s_pic r1, %s", add_symbol->bsym->name);
+ if (s7_append_insn (tmp, FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&var_insts[1], &s7_inst, sizeof (struct s7_score_it));
+ s7_build_relax_frag (fix_insts, fix_num, var_insts, var_num, add_symbol);
+
+ /* Insn 2 or Insn 3: lw/st rD, [r1, constant] */
+ sprintf (tmp, "%s r%d, [r1, %d]", insn_name, reg_rd, add_number);
+ if (s7_append_insn (tmp, TRUE) == (int) s7_FAIL)
+ return;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+ }
+ else
+ {
+ s7_inst.error = _("PIC code offset overflow (max 16 signed bits)");
+ return;
+ }
+
+ s7_nor1 = r1_bak;
+}
+
+static void
+s7_do_macro_ldst_label (char *str)
+{
+ int i;
+ int ldst_gp_p = 0;
+ int reg_rd;
+ int r1_bak;
+ char *backup_str;
+ char *label_str;
+ char *absolute_value;
+ char append_str[3][s7_MAX_LITERAL_POOL_SIZE];
+ char verifystr[s7_MAX_LITERAL_POOL_SIZE];
+ struct s7_score_it inst_backup;
+ struct s7_score_it inst_expand[3];
+ struct s7_score_it inst_main;
+
+ memcpy (&inst_backup, &s7_inst, sizeof (struct s7_score_it));
+ strcpy (verifystr, str);
+ backup_str = verifystr;
+
+ s7_skip_whitespace (backup_str);
+ if ((reg_rd = s7_reg_required_here (&backup_str, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ return;
+
+ if (s7_skip_past_comma (&backup_str) == (int) s7_FAIL)
+ return;
+
+ label_str = backup_str;
+
+ /* Ld/st rD, [rA, imm] ld/st rD, [rA]+, imm ld/st rD, [rA, imm]+. */
+ if (*backup_str == '[')
+ {
+ s7_inst.type = Rd_rvalueRs_preSI12;
+ s7_do_ldst_insn (str);
+ return;
+ }
+
+ /* Ld/st rD, imm. */
+ absolute_value = backup_str;
+ s7_inst.type = Rd_rvalueRs_SI15;
+
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &backup_str) == (int) s7_FAIL)
+ {
+ s7_inst.error = _("expression error");
+ return;
+ }
+ else if ((s7_inst.reloc.exp.X_add_symbol == NULL)
+ && (s7_validate_immediate (s7_inst.reloc.exp.X_add_number, _VALUE, 0) == (int) s7_FAIL))
+ {
+ s7_inst.error = _("value not in range [0, 0x7fffffff]");
+ return;
+ }
+ else if (s7_end_of_line (backup_str) == (int) s7_FAIL)
+ {
+ s7_inst.error = _("end on line error");
+ return;
+ }
+ else
+ {
+ if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ memcpy (&s7_inst, &inst_backup, sizeof (struct s7_score_it));
+ s7_exp_macro_ldst_abs (str);
+ return;
+ }
+ }
+
+ /* Ld/st rD, label. */
+ s7_inst.type = Rd_rvalueRs_SI15;
+ backup_str = absolute_value;
+ if ((s7_data_op2 (&backup_str, 1, _GP_IMM15) == (int) s7_FAIL)
+ || (s7_end_of_line (backup_str) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ if (!s7_inst.error)
+ s7_inst.error = s7_BAD_ARGS;
+
+ return;
+ }
+
+ if (s7_score_pic == s7_PIC)
+ {
+ int ldst_idx = 0;
+ ldst_idx = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s7_build_lwst_pic (reg_rd, s7_inst.reloc.exp,
+ s7_score_ldst_insns[ldst_idx * 3 + 0].template_name);
+ return;
+ }
+ else
+ {
+ if ((s7_inst.reloc.exp.X_add_number <= 0x3fff)
+ && (s7_inst.reloc.exp.X_add_number >= -0x4000)
+ && (!s7_nopic_need_relax (s7_inst.reloc.exp.X_add_symbol, 1)))
+ {
+ int ldst_idx = 0;
+
+ /* Assign the real opcode. */
+ ldst_idx = s7_inst.instruction & OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction &= ~OPC_PSEUDOLDST_MASK;
+ s7_inst.instruction |= s7_score_ldst_insns[ldst_idx * 3 + 0].value;
+ s7_inst.instruction |= reg_rd << 20;
+ s7_inst.instruction |= s7_GP << 15;
+ s7_inst.relax_inst = 0x8000;
+ s7_inst.relax_size = 0;
+ ldst_gp_p = 1;
+ }
+ }
+ }
+
+ /* Backup s7_inst. */
+ memcpy (&inst_main, &s7_inst, sizeof (struct s7_score_it));
+ r1_bak = s7_nor1;
+ s7_nor1 = 0;
+
+ /* Determine which instructions should be output. */
+ sprintf (append_str[0], "ld_i32hi r1, %s", label_str);
+ sprintf (append_str[1], "ld_i32lo r1, %s", label_str);
+ sprintf (append_str[2], "%s r%d, [r1, 0]", inst_backup.name, reg_rd);
+
+ /* Generate three instructions.
+ la r1, label
+ ld/st rd, [r1, 0] */
+ for (i = 0; i < 3; i++)
+ {
+ if (s7_append_insn (append_str[i], FALSE) == (int) s7_FAIL)
+ return;
+
+ memcpy (&inst_expand[i], &s7_inst, sizeof (struct s7_score_it));
+ }
+
+ if (ldst_gp_p)
+ {
+ char *p;
+
+ /* Adjust instruction opcode and to be relaxed instruction opcode. */
+ inst_main.instruction = s7_adjust_paritybit (inst_main.instruction, s7_GET_INSN_CLASS (inst_main.type));
+ inst_main.relax_size = inst_expand[0].size + inst_expand[1].size + inst_expand[2].size;
+ inst_main.type = Insn_GP;
+
+ for (i = 0; i < 3; i++)
+ inst_expand[i].instruction = s7_adjust_paritybit (inst_expand[i].instruction
+ , s7_GET_INSN_CLASS (inst_expand[i].type));
+
+ /* Check data dependency. */
+ s7_handle_dependency (&inst_main);
+
+ /* Start a new frag if frag_now is not empty. */
+ if (frag_now_fix () != 0)
+ {
+ if (!frag_now->tc_frag_data.is_insn)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (20);
+
+ /* Write fr_fix part. */
+ p = frag_more (inst_main.size);
+ s7_number_to_chars (p, inst_main.instruction, inst_main.size);
+
+ if (inst_main.reloc.type != BFD_RELOC_NONE)
+ {
+ s7_fix_new_score (frag_now, p - frag_now->fr_literal, inst_main.size,
+ &inst_main.reloc.exp, inst_main.reloc.pc_rel, inst_main.reloc.type);
+ }
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst_main.size);
+#endif
+
+ /* s7_GP instruction can not do optimization, only can do relax between
+ 1 instruction and 3 instructions. */
+ p = frag_var (rs_machine_dependent, inst_main.relax_size + s7_RELAX_PAD_BYTE, 0,
+ s7_RELAX_ENCODE (inst_main.size, inst_main.relax_size, inst_main.type, 0, 4, 0),
+ inst_main.reloc.exp.X_add_symbol, 0, NULL);
+
+ /* Write fr_var part.
+ no calling s7_gen_insn_frag, no fixS will be generated. */
+ s7_number_to_chars (p, inst_expand[0].instruction, inst_expand[0].size);
+ p += inst_expand[0].size;
+ s7_number_to_chars (p, inst_expand[1].instruction, inst_expand[1].size);
+ p += inst_expand[1].size;
+ s7_number_to_chars (p, inst_expand[2].instruction, inst_expand[2].size);
+ }
+ else
+ {
+ s7_gen_insn_frag (&inst_expand[0], NULL);
+ s7_gen_insn_frag (&inst_expand[1], NULL);
+ s7_gen_insn_frag (&inst_expand[2], NULL);
+ }
+ s7_nor1 = r1_bak;
+
+ /* Set bwarn as -1, so macro instruction itself will not be generated frag. */
+ s7_inst.bwarn = -1;
+}
+
+static void
+s7_do_lw_pic (char *str)
+{
+ int reg_rd;
+
+ s7_skip_whitespace (str);
+ if (((reg_rd = s7_reg_required_here (&str, 20, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ || (s7_skip_past_comma (&str) == (int) s7_FAIL)
+ || (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL)
+ || (s7_end_of_line (str) == (int) s7_FAIL))
+ {
+ return;
+ }
+ else
+ {
+ if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ if (!s7_inst.error)
+ s7_inst.error = s7_BAD_ARGS;
+
+ return;
+ }
+
+ s7_inst.instruction |= s7_GP << 15;
+ s7_inst.reloc.type = BFD_RELOC_SCORE_GOT15;
+ }
+}
+
+static void
+s7_do_empty (char *str)
+{
+ str = str;
+ if (s7_university_version == 1)
+ {
+ if (((s7_inst.instruction & 0x3e0003ff) == 0x0c000004)
+ || ((s7_inst.instruction & 0x3e0003ff) == 0x0c000024)
+ || ((s7_inst.instruction & 0x3e0003ff) == 0x0c000044)
+ || ((s7_inst.instruction & 0x3e0003ff) == 0x0c000064))
+ {
+ s7_inst.error = s7_ERR_FOR_SCORE5U_MMU;
+ return;
+ }
+ }
+ if (s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if (s7_inst.relax_inst != 0x8000)
+ {
+ if (s7_inst.type == NO_OPD)
+ {
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_size = 4;
+ }
+ }
+}
+
+static void
+s7_do_jump (char *str)
+{
+ char *save_in;
+
+ s7_skip_whitespace (str);
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ return;
+
+ if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s7_inst.error = _("lacking label ");
+ return;
+ }
+
+ if (!(s7_inst.reloc.exp.X_add_number >= -16777216
+ && s7_inst.reloc.exp.X_add_number <= 16777215))
+ {
+ s7_inst.error = _("invalid constant: 25 bit expression not in range [-16777216, 16777215]");
+ return;
+ }
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ s7_inst.reloc.type = BFD_RELOC_SCORE_JMP;
+ s7_inst.reloc.pc_rel = 1;
+ input_line_pointer = save_in;
+}
+
+static void
+s7_do16_jump (char *str)
+{
+ s7_skip_whitespace (str);
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s7_inst.error = _("lacking label ");
+ return;
+ }
+ else if (!(s7_inst.reloc.exp.X_add_number >= 0
+ && s7_inst.reloc.exp.X_add_number <= 4095))
+ {
+ s7_inst.error = _("invalid constant: 12 bit expression not in range [0, 4095]");
+ return;
+ }
+
+ s7_inst.reloc.type = BFD_RELOC_SCORE16_JMP;
+ s7_inst.reloc.pc_rel = 1;
+}
+
+static void
+s7_do_branch (char *str)
+{
+ unsigned long abs_value = 0;
+
+ if (s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL)
+ {
+ return;
+ }
+ else if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s7_inst.error = _("lacking label ");
+ return;
+ }
+ else if (!(s7_inst.reloc.exp.X_add_number >= -524288
+ && s7_inst.reloc.exp.X_add_number <= 524287))
+ {
+ s7_inst.error = _("invalid constant: 20 bit expression not in range -2^19..2^19");
+ return;
+ }
+
+ s7_inst.reloc.type = BFD_RELOC_SCORE_BRANCH;
+ s7_inst.reloc.pc_rel = 1;
+
+ /* Branch 32 offset field : 20 bit, 16 bit branch offset field : 8 bit. */
+ s7_inst.instruction |= (s7_inst.reloc.exp.X_add_number & 0x3fe) | ((s7_inst.reloc.exp.X_add_number & 0xffc00) << 5);
+
+ /* Compute 16 bit branch instruction. */
+ if ((s7_inst.relax_inst != 0x8000) && (abs_value & 0xfffffe00) == 0)
+ {
+ s7_inst.relax_inst |= (((s7_inst.instruction >> 10) & 0xf) << 8);
+ s7_inst.relax_inst |= ((s7_inst.reloc.exp.X_add_number >> 1) & 0xff);
+ s7_inst.relax_size = 2;
+ }
+ else
+ {
+ s7_inst.relax_inst = 0x8000;
+ }
+}
+
+static void
+s7_do16_branch (char *str)
+{
+ if ((s7_my_get_expression (&s7_inst.reloc.exp, &str) == (int) s7_FAIL
+ || s7_end_of_line (str) == (int) s7_FAIL))
+ {
+ ;
+ }
+ else if (s7_inst.reloc.exp.X_add_symbol == 0)
+ {
+ s7_inst.error = _("lacking label");
+ }
+ else if (!(s7_inst.reloc.exp.X_add_number >= -512
+ && s7_inst.reloc.exp.X_add_number <= 511))
+ {
+ s7_inst.error = _("invalid constant: 10 bit expression not in range [-2^9, 2^9-1]");
+ }
+ else
+ {
+ s7_inst.reloc.type = BFD_RELOC_SCORE16_BRANCH;
+ s7_inst.reloc.pc_rel = 1;
+ s7_inst.instruction |= ((s7_inst.reloc.exp.X_add_number >> 1) & 0xff);
+ }
+}
+
+/* Iterate over the base tables to create the instruction patterns. */
+
+static void
+s7_build_score_ops_hsh (void)
+{
+ unsigned int i;
+ static struct obstack insn_obstack;
+
+ obstack_begin (&insn_obstack, 4000);
+ for (i = 0; i < sizeof (s7_score_insns) / sizeof (struct s7_asm_opcode); i++)
+ {
+ const struct s7_asm_opcode *insn = s7_score_insns + i;
+ size_t len = strlen (insn->template_name);
+ struct s7_asm_opcode *new_opcode;
+ char *template_name;
+ new_opcode = (struct s7_asm_opcode *)
+ obstack_alloc (&insn_obstack, sizeof (struct s7_asm_opcode));
+ template_name = (char *) obstack_alloc (&insn_obstack, len + 1);
+
+ strcpy (template_name, insn->template_name);
+ new_opcode->template_name = template_name;
+ new_opcode->parms = insn->parms;
+ new_opcode->value = insn->value;
+ new_opcode->relax_value = insn->relax_value;
+ new_opcode->type = insn->type;
+ new_opcode->bitmask = insn->bitmask;
+ hash_insert (s7_score_ops_hsh, new_opcode->template_name,
+ (void *) new_opcode);
+ }
+}
+
+static void
+s7_build_dependency_insn_hsh (void)
+{
+ unsigned int i;
+ static struct obstack dependency_obstack;
+
+ obstack_begin (&dependency_obstack, 4000);
+ for (i = 0; i < ARRAY_SIZE (s7_insn_to_dependency_table); i++)
+ {
+ const struct s7_insn_to_dependency *tmp = s7_insn_to_dependency_table + i;
+ size_t len = strlen (tmp->insn_name);
+ struct s7_insn_to_dependency *new_i2d;
+
+ new_i2d = (struct s7_insn_to_dependency *)
+ obstack_alloc (&dependency_obstack,
+ sizeof (struct s7_insn_to_dependency));
+ new_i2d->insn_name = (char *) obstack_alloc (&dependency_obstack,
+ len + 1);
+
+ strcpy (new_i2d->insn_name, tmp->insn_name);
+ new_i2d->type = tmp->type;
+ hash_insert (s7_dependency_insn_hsh, new_i2d->insn_name,
+ (void *) new_i2d);
+ }
+}
+
+static valueT
+s7_md_chars_to_number (char *buf, int n)
+{
+ valueT result = 0;
+ unsigned char *where = (unsigned char *) buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
+ }
+
+ return result;
+}
+
+/* Return true if the given symbol should be considered local for s7_PIC. */
+
+static bfd_boolean
+s7_pic_need_relax (symbolS *sym, asection *segtype)
+{
+ asection *symsec;
+ bfd_boolean linkonce;
+
+ /* Handle the case of a symbol equated to another symbol. */
+ while (symbol_equated_reloc_p (sym))
+ {
+ symbolS *n;
+
+ /* It's possible to get a loop here in a badly written
+ program. */
+ n = symbol_get_value_expression (sym)->X_add_symbol;
+ if (n == sym)
+ break;
+ sym = n;
+ }
+
+ symsec = S_GET_SEGMENT (sym);
+
+ /* Duplicate the test for LINK_ONCE sections as in adjust_reloc_syms */
+ linkonce = FALSE;
+ if (symsec != segtype && ! S_IS_LOCAL (sym))
+ {
+ if ((bfd_get_section_flags (stdoutput, symsec) & SEC_LINK_ONCE) != 0)
+ linkonce = TRUE;
+
+ /* The GNU toolchain uses an extension for ELF: a section
+ beginning with the magic string .gnu.linkonce is a linkonce
+ section. */
+ if (strncmp (segment_name (symsec), ".gnu.linkonce",
+ sizeof ".gnu.linkonce" - 1) == 0)
+ linkonce = TRUE;
+ }
+
+ /* This must duplicate the test in adjust_reloc_syms. */
+ return (!bfd_is_und_section (symsec)
+ && !bfd_is_abs_section (symsec)
+ && !bfd_is_com_section (symsec)
+ && !linkonce
+#ifdef OBJ_ELF
+ /* A global or weak symbol is treated as external. */
+ && (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ || (! S_IS_WEAK (sym) && ! S_IS_EXTERNAL (sym)))
+#endif
+ );
+}
+
+static int
+s7_judge_size_before_relax (fragS * fragp, asection *sec)
+{
+ int change = 0;
+
+ if (s7_score_pic == s7_NO_PIC)
+ change = s7_nopic_need_relax (fragp->fr_symbol, 0);
+ else
+ change = s7_pic_need_relax (fragp->fr_symbol, sec);
+
+ if (change == 1)
+ {
+ /* Only at the first time determining whether s7_GP instruction relax should be done,
+ return the difference between insntruction size and instruction relax size. */
+ if (fragp->fr_opcode == NULL)
+ {
+ fragp->fr_fix = s7_RELAX_NEW (fragp->fr_subtype);
+ fragp->fr_opcode = fragp->fr_literal + s7_RELAX_RELOC1 (fragp->fr_subtype);
+ return s7_RELAX_NEW (fragp->fr_subtype) - s7_RELAX_OLD (fragp->fr_subtype);
+ }
+ }
+
+ return 0;
+}
+
+static int
+s7_b32_relax_to_b16 (fragS * fragp)
+{
+ int grows = 0;
+ int relaxable_p = 0;
+ int frag_addr = fragp->fr_address + fragp->insn_addr;
+
+ addressT symbol_address = 0;
+ symbolS *s;
+ offsetT offset;
+ unsigned long value;
+ unsigned long abs_value;
+
+ /* FIXME : here may be able to modify better .
+ I don't know how to get the fragp's section ,
+ so in relax stage , it may be wrong to calculate the symbol's offset when the frag's section
+ is different from the symbol's. */
+
+ relaxable_p = s7_RELAX_OPT (fragp->fr_subtype);
+
+ s = fragp->fr_symbol;
+ /* b/bl immediate */
+ if (s == NULL)
+ frag_addr = 0;
+ else
+ {
+ if (s->bsym != NULL)
+ symbol_address = (addressT) symbol_get_frag (s)->fr_address;
+ }
+
+ value = s7_md_chars_to_number (fragp->fr_literal, s7_INSN_SIZE);
+
+ /* b 32's offset : 20 bit, b 16's tolerate field : 0xff. */
+ offset = ((value & 0x3ff0000) >> 6) | (value & 0x3fe);
+ if ((offset & 0x80000) == 0x80000)
+ offset |= 0xfff00000;
+
+ abs_value = offset + symbol_address - frag_addr;
+ if ((abs_value & 0x80000000) == 0x80000000)
+ abs_value = 0xffffffff - abs_value + 1;
+
+ /* Relax branch 32 to branch 16. */
+ if (relaxable_p && (s->bsym != NULL) && ((abs_value & 0xffffff00) == 0)
+ && (S_IS_DEFINED (s) && !S_IS_COMMON (s) && !S_IS_EXTERNAL (s)))
+ {
+ /* do nothing. */
+ }
+ else
+ {
+ /* Branch 32 can not be relaxed to b 16, so clear OPT bit. */
+ fragp->fr_opcode = NULL;
+ fragp->fr_subtype = s7_RELAX_OPT_CLEAR (fragp->fr_subtype);
+ }
+
+ return grows;
+}
+
+static void
+s7_parse_pce_inst (char *insnstr)
+{
+ char c;
+ char *p;
+ char *q;
+ char first[s7_MAX_LITERAL_POOL_SIZE];
+ char second[s7_MAX_LITERAL_POOL_SIZE];
+ struct s7_score_it pec_part_1;
+
+ /* Get first part string of PCE. */
+ p = strstr (insnstr, "||");
+ c = *p;
+ *p = '\0';
+ strcpy (first, insnstr);
+
+ /* Get second part string of PCE. */
+ *p = c;
+ p += 2;
+ strcpy (second, p);
+
+ s7_parse_16_32_inst (first, FALSE);
+ if (s7_inst.error)
+ return;
+
+ memcpy (&pec_part_1, &s7_inst, sizeof (s7_inst));
+
+ q = second;
+ while (q && *q)
+ {
+ *q = TOLOWER (*q);
+ q++;
+ }
+
+ s7_parse_16_32_inst (second, FALSE);
+ if (s7_inst.error)
+ return;
+
+ if ( ((pec_part_1.size == s7_INSN_SIZE) && (s7_inst.size == s7_INSN_SIZE))
+ || ((pec_part_1.size == s7_INSN_SIZE) && (s7_inst.size == s7_INSN16_SIZE))
+ || ((pec_part_1.size == s7_INSN16_SIZE) && (s7_inst.size == s7_INSN_SIZE)))
+ {
+ s7_inst.error = _("pce instruction error (16 bit || 16 bit)'");
+ strcpy (s7_inst.str, insnstr);
+ return;
+ }
+
+ if (!s7_inst.error)
+ s7_gen_insn_frag (&pec_part_1, &s7_inst);
+}
+
+
+
+static void
+s7_insert_reg (const struct s7_reg_entry *r, struct hash_control *htab)
+{
+ int i = 0;
+ int len = strlen (r->name) + 2;
+ char *buf = xmalloc (len);
+ char *buf2 = xmalloc (len);
+
+ strcpy (buf + i, r->name);
+ for (i = 0; buf[i]; i++)
+ {
+ buf2[i] = TOUPPER (buf[i]);
+ }
+ buf2[i] = '\0';
+
+ hash_insert (htab, buf, (void *) r);
+ hash_insert (htab, buf2, (void *) r);
+}
+
+static void
+s7_build_reg_hsh (struct s7_reg_map *map)
+{
+ const struct s7_reg_entry *r;
+
+ if ((map->htab = hash_new ()) == NULL)
+ {
+ as_fatal (_("virtual memory exhausted"));
+ }
+ for (r = map->names; r->name != NULL; r++)
+ {
+ s7_insert_reg (r, map->htab);
+ }
+}
+
+
+
+/* If we change section we must dump the literal pool first. */
+static void
+s7_s_score_bss (int ignore ATTRIBUTE_UNUSED)
+{
+ subseg_set (bss_section, (subsegT) get_absolute_expression ());
+ demand_empty_rest_of_line ();
+}
+
+static void
+s7_s_score_text (int ignore)
+{
+ obj_elf_text (ignore);
+ record_alignment (now_seg, 2);
+}
+
+static void
+s7_s_section (int ignore)
+{
+ obj_elf_section (ignore);
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ record_alignment (now_seg, 2);
+
+}
+
+static void
+s7_s_change_sec (int sec)
+{
+ segT seg;
+
+#ifdef OBJ_ELF
+ /* The ELF backend needs to know that we are changing sections, so
+ that .previous works correctly. We could do something like check
+ for an obj_section_change_hook macro, but that might be confusing
+ as it would not be appropriate to use it in the section changing
+ functions in read.c, since obj-elf.c intercepts those. FIXME:
+ This should be cleaner, somehow. */
+ obj_elf_section_change_hook ();
+#endif
+ switch (sec)
+ {
+ case 'r':
+ seg = subseg_new (s7_RDATA_SECTION_NAME, (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg, (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_RELOC | SEC_DATA));
+ if (strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+ case 's':
+ seg = subseg_new (".sdata", (subsegT) get_absolute_expression ());
+ bfd_set_section_flags (stdoutput, seg, SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA);
+ if (strcmp (TARGET_OS, "elf") != 0)
+ record_alignment (seg, 4);
+ demand_empty_rest_of_line ();
+ break;
+ }
+}
+
+static void
+s7_s_score_mask (int reg_type ATTRIBUTE_UNUSED)
+{
+ long mask, off;
+
+ if (s7_cur_proc_ptr == NULL)
+ {
+ as_warn (_(".mask outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (get_absolute_expression_and_terminator (&mask) != ',')
+ {
+ as_warn (_("Bad .mask directive"));
+ --input_line_pointer;
+ demand_empty_rest_of_line ();
+ return;
+ }
+ off = get_absolute_expression ();
+ s7_cur_proc_ptr->reg_mask = mask;
+ s7_cur_proc_ptr->reg_offset = off;
+ demand_empty_rest_of_line ();
+}
+
+static symbolS *
+s7_get_symbol (void)
+{
+ int c;
+ char *name;
+ symbolS *p;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = (symbolS *) symbol_find_or_make (name);
+ *input_line_pointer = c;
+ return p;
+}
+
+static long
+s7_get_number (void)
+{
+ int negative = 0;
+ long val = 0;
+
+ if (*input_line_pointer == '-')
+ {
+ ++input_line_pointer;
+ negative = 1;
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ as_bad (_("expected simple number"));
+ if (input_line_pointer[0] == '0')
+ {
+ if (input_line_pointer[1] == 'x')
+ {
+ input_line_pointer += 2;
+ while (ISXDIGIT (*input_line_pointer))
+ {
+ val <<= 4;
+ val |= hex_value (*input_line_pointer++);
+ }
+ return negative ? -val : val;
+ }
+ else
+ {
+ ++input_line_pointer;
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val <<= 3;
+ val |= *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+ }
+ }
+ if (!ISDIGIT (*input_line_pointer))
+ {
+ printf (_(" *input_line_pointer == '%c' 0x%02x\n"), *input_line_pointer, *input_line_pointer);
+ as_warn (_("invalid number"));
+ return -1;
+ }
+ while (ISDIGIT (*input_line_pointer))
+ {
+ val *= 10;
+ val += *input_line_pointer++ - '0';
+ }
+ return negative ? -val : val;
+}
+
+/* The .aent and .ent directives. */
+
+static void
+s7_s_score_ent (int aent)
+{
+ symbolS *symbolP;
+ int maybe_text;
+
+ symbolP = s7_get_symbol ();
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ if (ISDIGIT (*input_line_pointer) || *input_line_pointer == '-')
+ s7_get_number ();
+
+#ifdef BFD_ASSEMBLER
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#else
+ if (now_seg != data_section && now_seg != bss_section)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#endif
+ if (!maybe_text)
+ as_warn (_(".ent or .aent not in text section."));
+ if (!aent && s7_cur_proc_ptr)
+ as_warn (_("missing .end"));
+ if (!aent)
+ {
+ s7_cur_proc_ptr = &s7_cur_proc;
+ s7_cur_proc_ptr->reg_mask = 0xdeadbeaf;
+ s7_cur_proc_ptr->reg_offset = 0xdeadbeaf;
+ s7_cur_proc_ptr->fpreg_mask = 0xdeafbeaf;
+ s7_cur_proc_ptr->leaf = 0xdeafbeaf;
+ s7_cur_proc_ptr->frame_offset = 0xdeafbeaf;
+ s7_cur_proc_ptr->frame_reg = 0xdeafbeaf;
+ s7_cur_proc_ptr->pc_reg = 0xdeafbeaf;
+ s7_cur_proc_ptr->isym = symbolP;
+ symbol_get_bfdsym (symbolP)->flags |= BSF_FUNCTION;
+ ++s7_numprocs;
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_func (S_GET_NAME (symbolP), S_GET_NAME (symbolP));
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+s7_s_score_frame (int ignore ATTRIBUTE_UNUSED)
+{
+ char *backupstr;
+ char str[30];
+ long val;
+ int i = 0;
+
+ backupstr = input_line_pointer;
+
+#ifdef OBJ_ELF
+ if (s7_cur_proc_ptr == NULL)
+ {
+ as_warn (_(".frame outside of .ent"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ s7_cur_proc_ptr->frame_reg = s7_reg_required_here ((&backupstr), 0, s7_REG_TYPE_SCORE);
+ SKIP_WHITESPACE ();
+ s7_skip_past_comma (&backupstr);
+ while (*backupstr != ',')
+ {
+ str[i] = *backupstr;
+ i++;
+ backupstr++;
+ }
+ str[i] = '\0';
+ val = atoi (str);
+
+ SKIP_WHITESPACE ();
+ s7_skip_past_comma (&backupstr);
+ s7_cur_proc_ptr->frame_offset = val;
+ s7_cur_proc_ptr->pc_reg = s7_reg_required_here ((&backupstr), 0, s7_REG_TYPE_SCORE);
+
+ SKIP_WHITESPACE ();
+ s7_skip_past_comma (&backupstr);
+ i = 0;
+ while (*backupstr != '\n')
+ {
+ str[i] = *backupstr;
+ i++;
+ backupstr++;
+ }
+ str[i] = '\0';
+ val = atoi (str);
+ s7_cur_proc_ptr->leaf = val;
+ SKIP_WHITESPACE ();
+ s7_skip_past_comma (&backupstr);
+
+#endif /* OBJ_ELF */
+ while (input_line_pointer != backupstr)
+ input_line_pointer++;
+}
+
+/* The .end directive. */
+
+static void
+s7_s_score_end (int x ATTRIBUTE_UNUSED)
+{
+ symbolS *p;
+ int maybe_text;
+
+ /* Generate a .pdr section. */
+ segT saved_seg = now_seg;
+ subsegT saved_subseg = now_subseg;
+ expressionS exp;
+ char *fragp;
+
+ if (!is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ p = s7_get_symbol ();
+ demand_empty_rest_of_line ();
+ }
+ else
+ p = NULL;
+
+#ifdef BFD_ASSEMBLER
+ if ((bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#else
+ if (now_seg != data_section && now_seg != bss_section)
+ maybe_text = 1;
+ else
+ maybe_text = 0;
+#endif
+
+ if (!maybe_text)
+ as_warn (_(".end not in text section"));
+ if (!s7_cur_proc_ptr)
+ {
+ as_warn (_(".end directive without a preceding .ent directive."));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (p != NULL)
+ {
+ gas_assert (S_GET_NAME (p));
+ if (strcmp (S_GET_NAME (p), S_GET_NAME (s7_cur_proc_ptr->isym)))
+ as_warn (_(".end symbol does not match .ent symbol."));
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_endfunc (S_GET_NAME (p), S_GET_NAME (p));
+ }
+ else
+ as_warn (_(".end directive missing or unknown symbol"));
+
+ if ((s7_cur_proc_ptr->reg_mask == 0xdeadbeaf) ||
+ (s7_cur_proc_ptr->reg_offset == 0xdeadbeaf) ||
+ (s7_cur_proc_ptr->leaf == 0xdeafbeaf) ||
+ (s7_cur_proc_ptr->frame_offset == 0xdeafbeaf) ||
+ (s7_cur_proc_ptr->frame_reg == 0xdeafbeaf) || (s7_cur_proc_ptr->pc_reg == 0xdeafbeaf));
+
+ else
+ {
+ (void) frag_now_fix ();
+ gas_assert (s7_pdr_seg);
+ subseg_set (s7_pdr_seg, 0);
+ /* Write the symbol. */
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = p;
+ exp.X_add_number = 0;
+ emit_expr (&exp, 4);
+ fragp = frag_more (7 * 4);
+ s7_number_to_chars (fragp, (valueT) s7_cur_proc_ptr->reg_mask, 4);
+ s7_number_to_chars (fragp + 4, (valueT) s7_cur_proc_ptr->reg_offset, 4);
+ s7_number_to_chars (fragp + 8, (valueT) s7_cur_proc_ptr->fpreg_mask, 4);
+ s7_number_to_chars (fragp + 12, (valueT) s7_cur_proc_ptr->leaf, 4);
+ s7_number_to_chars (fragp + 16, (valueT) s7_cur_proc_ptr->frame_offset, 4);
+ s7_number_to_chars (fragp + 20, (valueT) s7_cur_proc_ptr->frame_reg, 4);
+ s7_number_to_chars (fragp + 24, (valueT) s7_cur_proc_ptr->pc_reg, 4);
+ subseg_set (saved_seg, saved_subseg);
+
+ }
+ s7_cur_proc_ptr = NULL;
+}
+
+/* Handle the .set pseudo-op. */
+
+static void
+s7_s_score_set (int x ATTRIBUTE_UNUSED)
+{
+ int i = 0;
+ char name[s7_MAX_LITERAL_POOL_SIZE];
+ char * orig_ilp = input_line_pointer;
+
+ while (!is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ name[i] = (char) * input_line_pointer;
+ i++;
+ ++input_line_pointer;
+ }
+
+ name[i] = '\0';
+
+ if (strcmp (name, "nwarn") == 0)
+ {
+ s7_warn_fix_data_dependency = 0;
+ }
+ else if (strcmp (name, "fixdd") == 0)
+ {
+ s7_fix_data_dependency = 1;
+ }
+ else if (strcmp (name, "nofixdd") == 0)
+ {
+ s7_fix_data_dependency = 0;
+ }
+ else if (strcmp (name, "r1") == 0)
+ {
+ s7_nor1 = 0;
+ }
+ else if (strcmp (name, "nor1") == 0)
+ {
+ s7_nor1 = 1;
+ }
+ else if (strcmp (name, "optimize") == 0)
+ {
+ s7_g_opt = 1;
+ }
+ else if (strcmp (name, "volatile") == 0)
+ {
+ s7_g_opt = 0;
+ }
+ else if (strcmp (name, "pic") == 0)
+ {
+ s7_score_pic = s7_PIC;
+ }
+ else
+ {
+ input_line_pointer = orig_ilp;
+ s_set (0);
+ }
+}
+
+/* Handle the .cpload pseudo-op. This is used when generating s7_PIC code. It sets the
+ $gp register for the function based on the function address, which is in the register
+ named in the argument. This uses a relocation against GP_DISP_LABEL, which is handled
+ specially by the linker. The result is:
+ ldis gp, %hi(GP_DISP_LABEL)
+ ori gp, %low(GP_DISP_LABEL)
+ add gp, gp, .cpload argument
+ The .cpload argument is normally r29. */
+
+static void
+s7_s_score_cpload (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+ char insn_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ /* If we are not generating s7_PIC code, .cpload is ignored. */
+ if (s7_score_pic == s7_NO_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if ((reg = s7_reg_required_here (&input_line_pointer, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ return;
+
+ demand_empty_rest_of_line ();
+
+ sprintf (insn_str, "ld_i32hi r%d, %s", s7_GP, GP_DISP_LABEL);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ sprintf (insn_str, "ld_i32lo r%d, %s", s7_GP, GP_DISP_LABEL);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ sprintf (insn_str, "add r%d, r%d, r%d", s7_GP, s7_GP, reg);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+}
+
+/* Handle the .cprestore pseudo-op. This stores $gp into a given
+ offset from $sp. The offset is remembered, and after making a s7_PIC
+ call $gp is restored from that location. */
+
+static void
+s7_s_score_cprestore (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+ int cprestore_offset;
+ char insn_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ /* If we are not generating s7_PIC code, .cprestore is ignored. */
+ if (s7_score_pic == s7_NO_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if ((reg = s7_reg_required_here (&input_line_pointer, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL
+ || s7_skip_past_comma (&input_line_pointer) == (int) s7_FAIL)
+ {
+ return;
+ }
+
+ cprestore_offset = get_absolute_expression ();
+
+ if (cprestore_offset <= 0x3fff)
+ {
+ sprintf (insn_str, "sw r%d, [r%d, %d]", s7_GP, reg, cprestore_offset);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+ }
+ else
+ {
+ int r1_bak;
+
+ r1_bak = s7_nor1;
+ s7_nor1 = 0;
+
+ sprintf (insn_str, "li r1, %d", cprestore_offset);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ sprintf (insn_str, "add r1, r1, r%d", reg);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ sprintf (insn_str, "sw r%d, [r1]", s7_GP);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+
+ s7_nor1 = r1_bak;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .gpword pseudo-op. This is used when generating s7_PIC
+ code. It generates a 32 bit s7_GP relative reloc. */
+
+static void
+s7_s_score_gpword (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+ char *p;
+
+ /* When not generating s7_PIC code, this is treated as .word. */
+ if (s7_score_pic == s7_NO_PIC)
+ {
+ cons (4);
+ return;
+ }
+ expression (&ex);
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("Unsupported use of .gpword"));
+ ignore_rest_of_line ();
+ }
+ p = frag_more (4);
+ s7_number_to_chars (p, (valueT) 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4, &ex, FALSE, BFD_RELOC_GPREL32);
+ demand_empty_rest_of_line ();
+}
+
+/* Handle the .cpadd pseudo-op. This is used when dealing with switch
+ tables in s7_PIC code. */
+
+static void
+s7_s_score_cpadd (int ignore ATTRIBUTE_UNUSED)
+{
+ int reg;
+ char insn_str[s7_MAX_LITERAL_POOL_SIZE];
+
+ /* If we are not generating s7_PIC code, .cpload is ignored. */
+ if (s7_score_pic == s7_NO_PIC)
+ {
+ s_ignore (0);
+ return;
+ }
+
+ if ((reg = s7_reg_required_here (&input_line_pointer, -1, s7_REG_TYPE_SCORE)) == (int) s7_FAIL)
+ {
+ return;
+ }
+ demand_empty_rest_of_line ();
+
+ /* Add $gp to the register named as an argument. */
+ sprintf (insn_str, "add r%d, r%d, r%d", reg, reg, s7_GP);
+ if (s7_append_insn (insn_str, TRUE) == (int) s7_FAIL)
+ return;
+}
+
+#ifndef TC_IMPLICIT_LCOMM_ALIGNMENT
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) \
+ do \
+ { \
+ if ((SIZE) >= 8) \
+ (P2VAR) = 3; \
+ else if ((SIZE) >= 4) \
+ (P2VAR) = 2; \
+ else if ((SIZE) >= 2) \
+ (P2VAR) = 1; \
+ else \
+ (P2VAR) = 0; \
+ } \
+ while (0)
+#endif
+
+static void
+s7_s_score_lcomm (int bytes_p)
+{
+ char *name;
+ char c;
+ char *p;
+ int temp;
+ symbolS *symbolP;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+ const int max_alignment = 15;
+ int align = 0;
+ segT bss_seg = bss_section;
+ int needs_align = 0;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ *p = c;
+
+ if (name == p)
+ {
+ as_bad (_("expected symbol name"));
+ discard_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ /* Accept an optional comma after the name. The comma used to be
+ required, but Irix 5 cc does not generate it. */
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+ }
+
+ if (is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ as_bad (_("missing size expression"));
+ return;
+ }
+
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_warn (_("BSS length (%d) < 0 ignored"), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+#if defined (TC_SCORE)
+ if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour || OUTPUT_FLAVOR == bfd_target_elf_flavour)
+ {
+ /* For Score and Alpha ECOFF or ELF, small objects are put in .sbss. */
+ if ((unsigned) temp <= bfd_get_gp_size (stdoutput))
+ {
+ bss_seg = subseg_new (".sbss", 1);
+ seg_info (bss_seg)->bss = 1;
+#ifdef BFD_ASSEMBLER
+ if (!bfd_set_section_flags (stdoutput, bss_seg, SEC_ALLOC))
+ as_warn (_("error setting flags for \".sbss\": %s"), bfd_errmsg (bfd_get_error ()));
+#endif
+ }
+ }
+#endif
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ SKIP_WHITESPACE ();
+
+ if (is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ as_bad (_("missing alignment"));
+ return;
+ }
+ else
+ {
+ align = get_absolute_expression ();
+ needs_align = 1;
+ }
+ }
+
+ if (!needs_align)
+ {
+ TC_IMPLICIT_LCOMM_ALIGNMENT (temp, align);
+
+ /* Still zero unless TC_IMPLICIT_LCOMM_ALIGNMENT set it. */
+ if (align)
+ record_alignment (bss_seg, align);
+ }
+
+ if (needs_align)
+ {
+ if (bytes_p)
+ {
+ /* Convert to a power of 2. */
+ if (align != 0)
+ {
+ unsigned int i;
+
+ for (i = 0; align != 0; align >>= 1, ++i)
+ ;
+ align = i - 1;
+ }
+ }
+
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("alignment too large; %d assumed"), align);
+ }
+ else if (align < 0)
+ {
+ align = 0;
+ as_warn (_("alignment negative; 0 assumed"));
+ }
+
+ record_alignment (bss_seg, align);
+ }
+ else
+ {
+ /* Assume some objects may require alignment on some systems. */
+#if defined (TC_ALPHA) && ! defined (VMS)
+ if (temp > 1)
+ {
+ align = ffs (temp) - 1;
+ if (temp % (1 << align))
+ abort ();
+ }
+#endif
+ }
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT) \
+ || defined (OBJ_BOUT) || defined (OBJ_MAYBE_BOUT))
+#ifdef BFD_ASSEMBLER
+ (OUTPUT_FLAVOR != bfd_target_aout_flavour
+ || (S_GET_OTHER (symbolP) == 0 && S_GET_DESC (symbolP) == 0)) &&
+#else
+ (S_GET_OTHER (symbolP) == 0 && S_GET_DESC (symbolP) == 0) &&
+#endif
+#endif
+ (S_GET_SEGMENT (symbolP) == bss_seg || (!S_IS_DEFINED (symbolP) && S_GET_VALUE (symbolP) == 0)))
+ {
+ char *pfrag;
+
+ subseg_set (bss_seg, 1);
+
+ if (align)
+ frag_align (align, 0, 0);
+
+ /* Detach from old frag. */
+ if (S_GET_SEGMENT (symbolP) == bss_seg)
+ symbol_get_frag (symbolP)->fr_symbol = NULL;
+
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, (offsetT) temp, NULL);
+ *pfrag = 0;
+
+
+ S_SET_SEGMENT (symbolP, bss_seg);
+
+#ifdef OBJ_COFF
+ /* The symbol may already have been created with a preceding
+ ".globl" directive -- be careful not to step on storage class
+ in that case. Otherwise, set it to static. */
+ if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
+ {
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+ }
+#endif /* OBJ_COFF */
+
+#ifdef S_SET_SIZE
+ S_SET_SIZE (symbolP, temp);
+#endif
+ }
+ else
+ as_bad (_("symbol `%s' is already defined"), S_GET_NAME (symbolP));
+
+ subseg_set (current_seg, current_subseg);
+
+ demand_empty_rest_of_line ();
+}
+
+
+
+static void
+s7_begin (void)
+{
+ unsigned int i;
+ segT seg;
+ subsegT subseg;
+
+ if ((s7_score_ops_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ s7_build_score_ops_hsh ();
+
+ if ((s7_dependency_insn_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ s7_build_dependency_insn_hsh ();
+
+ for (i = (int) REG_TYPE_FIRST; i < (int) s7_REG_TYPE_MAX; i++)
+ s7_build_reg_hsh (s7_all_reg_maps + i);
+
+ /* Initialize dependency vector. */
+ s7_init_dependency_vector ();
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+ seg = now_seg;
+ subseg = now_subseg;
+ s7_pdr_seg = subseg_new (".pdr", (subsegT) 0);
+ (void) bfd_set_section_flags (stdoutput, s7_pdr_seg, SEC_READONLY | SEC_RELOC | SEC_DEBUGGING);
+ (void) bfd_set_section_alignment (stdoutput, s7_pdr_seg, 2);
+ subseg_set (seg, subseg);
+
+ if (s7_USE_GLOBAL_POINTER_OPT)
+ bfd_set_gp_size (stdoutput, s7_g_switch_value);
+}
+
+static void
+s7_assemble (char *str)
+{
+ know (str);
+ know (strlen (str) < s7_MAX_LITERAL_POOL_SIZE);
+
+ memset (&s7_inst, '\0', sizeof (s7_inst));
+ if (s7_INSN_IS_PCE_P (str))
+ s7_parse_pce_inst (str);
+ else
+ s7_parse_16_32_inst (str, TRUE);
+
+ if (s7_inst.error)
+ as_bad (_("%s -- `%s'"), s7_inst.error, s7_inst.str);
+}
+
+/* We handle all bad expressions here, so that we can report the faulty
+ instruction in the error message. */
+
+static void
+s7_operand (expressionS * exp)
+{
+ if (s7_in_my_get_expression)
+ {
+ exp->X_op = O_illegal;
+ if (s7_inst.error == NULL)
+ {
+ s7_inst.error = _("bad expression");
+ }
+ }
+}
+
+/* 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.
+
+ Note that fp constants aren't represent in the normal way on the ARM.
+ In big endian mode, things are as expected. However, in little endian
+ mode fp constants are big-endian word-wise, and little-endian byte-wise
+ within the words. For example, (double) 1.1 in big endian mode is
+ the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
+ the byte sequence 99 99 f1 3f 9a 99 99 99. */
+
+static char *
+s7_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[s7_MAX_LITTLENUMS];
+ char *t;
+ int i;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+ case 'x':
+ case 'X':
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+ 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 * 2;
+
+ if (target_big_endian)
+ {
+ for (i = 0; i < prec; i++)
+ {
+ s7_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ }
+ else
+ {
+ for (i = 0; i < prec; i += 2)
+ {
+ s7_number_to_chars (litP, (valueT) words[i + 1], 2);
+ s7_number_to_chars (litP + 2, (valueT) words[i], 2);
+ litP += 4;
+ }
+ }
+
+ return 0;
+}
+
+/* Implementation of md_frag_check.
+ Called after md_convert_frag(). */
+
+static void
+s7_frag_check (fragS * fragp ATTRIBUTE_UNUSED)
+{
+ know (fragp->insn_addr <= s7_RELAX_PAD_BYTE);
+}
+
+/* Implementation of TC_VALIDATE_FIX.
+ Called before md_apply_fix() and after md_convert_frag(). */
+
+static void
+s7_validate_fix (fixS *fixP)
+{
+ fixP->fx_where += fixP->fx_frag->insn_addr;
+}
+
+static int
+s7_force_relocation (struct fix *fixp)
+{
+ int retval = 0;
+
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixp->fx_r_type == BFD_RELOC_SCORE_JMP
+ || fixp->fx_r_type == BFD_RELOC_SCORE_BRANCH
+ || fixp->fx_r_type == BFD_RELOC_SCORE16_JMP
+ || fixp->fx_r_type == BFD_RELOC_SCORE16_BRANCH)
+ {
+ retval = 1;
+ }
+
+ return retval;
+}
+
+static bfd_boolean
+s7_fix_adjustable (fixS * fixP)
+{
+ if (fixP->fx_addsy == NULL)
+ {
+ return 1;
+ }
+ else if (OUTPUT_FLAVOR == bfd_target_elf_flavour
+ && (S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)))
+ {
+ return 0;
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixP->fx_r_type == BFD_RELOC_SCORE_JMP
+ || fixP->fx_r_type == BFD_RELOC_SCORE16_JMP)
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+s7_elf_final_processing (void)
+{
+ unsigned long val = E_SCORE_MACH_SCORE7;
+
+ elf_elfheader (stdoutput)->e_machine = EM_SCORE;
+ elf_elfheader (stdoutput)->e_flags &= ~EF_SCORE_MACH;
+ elf_elfheader (stdoutput)->e_flags |= val;
+
+ if (s7_fix_data_dependency == 1)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_SCORE_FIXDEP;
+ }
+ if (s7_score_pic == s7_PIC)
+ {
+ elf_elfheader (stdoutput)->e_flags |= EF_SCORE_PIC;
+ }
+}
+
+/* In this function, we determine whether s7_GP instruction should do relaxation,
+ for the label being against was known now.
+ Doing this here but not in md_relax_frag() can induce iteration times
+ in stage of doing relax. */
+
+static int
+s7_estimate_size_before_relax (fragS * fragp, asection * sec)
+{
+ if ((s7_RELAX_TYPE (fragp->fr_subtype) == Insn_GP)
+ || (s7_RELAX_TYPE (fragp->fr_subtype) == Insn_PIC))
+ return s7_judge_size_before_relax (fragp, sec);
+
+ return 0;
+}
+
+static int
+s7_relax_frag (asection * sec ATTRIBUTE_UNUSED,
+ fragS * fragp,
+ long stretch ATTRIBUTE_UNUSED)
+{
+ int grows = 0;
+ int insn_size;
+ int do_relax_p = 0; /* Indicate doing relaxation for this frag. */
+ int relaxable_p = 0;
+ bfd_boolean word_align_p = FALSE;
+ fragS *next_fragp;
+
+ /* If the instruction address is odd, make it half word align first. */
+ if ((fragp->fr_address) % 2 != 0)
+ {
+ if ((fragp->fr_address + fragp->insn_addr) % 2 != 0)
+ {
+ fragp->insn_addr = 1;
+ grows += 1;
+ }
+ }
+
+ word_align_p = ((fragp->fr_address + fragp->insn_addr) % 4 == 0) ? TRUE : FALSE;
+
+ /* Get instruction size and relax size after the last relaxation. */
+ if (fragp->fr_opcode)
+ insn_size = s7_RELAX_NEW (fragp->fr_subtype);
+ else
+ insn_size = s7_RELAX_OLD (fragp->fr_subtype);
+
+ /* Handle specially for s7_GP instruction. for, s7_judge_size_before_relax() has already determine
+ whether the s7_GP instruction should do relax. */
+ if ((s7_RELAX_TYPE (fragp->fr_subtype) == Insn_GP)
+ || (s7_RELAX_TYPE (fragp->fr_subtype) == Insn_PIC))
+ {
+ if (!word_align_p)
+ {
+ if (fragp->insn_addr < 2)
+ {
+ fragp->insn_addr += 2;
+ grows += 2;
+ }
+ else
+ {
+ fragp->insn_addr -= 2;
+ grows -= 2;
+ }
+ }
+
+ if (fragp->fr_opcode)
+ fragp->fr_fix = s7_RELAX_NEW (fragp->fr_subtype) + fragp->insn_addr;
+ else
+ fragp->fr_fix = s7_RELAX_OLD (fragp->fr_subtype) + fragp->insn_addr;
+ }
+ else
+ {
+ if (s7_RELAX_TYPE (fragp->fr_subtype) == PC_DISP19div2)
+ s7_b32_relax_to_b16 (fragp);
+
+ relaxable_p = s7_RELAX_OPT (fragp->fr_subtype);
+ next_fragp = fragp->fr_next;
+ while ((next_fragp) && (next_fragp->fr_type != rs_machine_dependent))
+ {
+ next_fragp = next_fragp->fr_next;
+ }
+
+ if (next_fragp)
+ {
+ int n_insn_size;
+ int n_relaxable_p = 0;
+
+ if (next_fragp->fr_opcode)
+ {
+ n_insn_size = s7_RELAX_NEW (next_fragp->fr_subtype);
+ }
+ else
+ {
+ n_insn_size = s7_RELAX_OLD (next_fragp->fr_subtype);
+ }
+
+ if (s7_RELAX_TYPE (next_fragp->fr_subtype) == PC_DISP19div2)
+ s7_b32_relax_to_b16 (next_fragp);
+ n_relaxable_p = s7_RELAX_OPT (next_fragp->fr_subtype);
+
+ if (word_align_p)
+ {
+ if (insn_size == 4)
+ {
+ /* 32 -> 16. */
+ if (relaxable_p && ((n_insn_size == 2) || n_relaxable_p))
+ {
+ grows -= 2;
+ do_relax_p = 1;
+ }
+ }
+ else if (insn_size == 2)
+ {
+ /* 16 -> 32. */
+ if (relaxable_p && (((n_insn_size == 4) && !n_relaxable_p) || (n_insn_size > 4)))
+ {
+ grows += 2;
+ do_relax_p = 1;
+ }
+ }
+ else
+ {
+ abort ();
+ }
+ }
+ else
+ {
+ if (insn_size == 4)
+ {
+ /* 32 -> 16. */
+ if (relaxable_p)
+ {
+ grows -= 2;
+ do_relax_p = 1;
+ }
+ /* Make the 32 bit insturction word align. */
+ else
+ {
+ fragp->insn_addr += 2;
+ grows += 2;
+ }
+ }
+ else if (insn_size == 2)
+ {
+ /* Do nothing. */
+ }
+ else
+ {
+ abort ();
+ }
+ }
+ }
+ else
+ {
+ /* Here, try best to do relax regardless fragp->fr_next->fr_type. */
+ if (word_align_p == FALSE)
+ {
+ if (insn_size % 4 == 0)
+ {
+ /* 32 -> 16. */
+ if (relaxable_p)
+ {
+ grows -= 2;
+ do_relax_p = 1;
+ }
+ else
+ {
+ fragp->insn_addr += 2;
+ grows += 2;
+ }
+ }
+ }
+ else
+ {
+ /* Do nothing. */
+ }
+ }
+
+ /* fragp->fr_opcode indicates whether this frag should be relaxed. */
+ if (do_relax_p)
+ {
+ if (fragp->fr_opcode)
+ {
+ fragp->fr_opcode = NULL;
+ /* Guarantee estimate stage is correct. */
+ fragp->fr_fix = s7_RELAX_OLD (fragp->fr_subtype);
+ fragp->fr_fix += fragp->insn_addr;
+ }
+ else
+ {
+ fragp->fr_opcode = fragp->fr_literal + s7_RELAX_RELOC1 (fragp->fr_subtype);
+ /* Guarantee estimate stage is correct. */
+ fragp->fr_fix = s7_RELAX_NEW (fragp->fr_subtype);
+ fragp->fr_fix += fragp->insn_addr;
+ }
+ }
+ else
+ {
+ if (fragp->fr_opcode)
+ {
+ /* Guarantee estimate stage is correct. */
+ fragp->fr_fix = s7_RELAX_NEW (fragp->fr_subtype);
+ fragp->fr_fix += fragp->insn_addr;
+ }
+ else
+ {
+ /* Guarantee estimate stage is correct. */
+ fragp->fr_fix = s7_RELAX_OLD (fragp->fr_subtype);
+ fragp->fr_fix += fragp->insn_addr;
+ }
+ }
+ }
+
+ return grows;
+}
+
+static void
+s7_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragp)
+{
+ int r_old;
+ int r_new;
+ char backup[20];
+ fixS *fixp;
+
+ r_old = s7_RELAX_OLD (fragp->fr_subtype);
+ r_new = s7_RELAX_NEW (fragp->fr_subtype);
+
+ /* fragp->fr_opcode indicates whether this frag should be relaxed. */
+ if (fragp->fr_opcode == NULL)
+ {
+ memcpy (backup, fragp->fr_literal, r_old);
+ fragp->fr_fix = r_old;
+ }
+ else
+ {
+ memcpy (backup, fragp->fr_literal + r_old, r_new);
+ fragp->fr_fix = r_new;
+ }
+
+ fixp = fragp->tc_frag_data.fixp;
+ while (fixp && fixp->fx_frag == fragp && fixp->fx_where < r_old)
+ {
+ if (fragp->fr_opcode)
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+ while (fixp && fixp->fx_frag == fragp)
+ {
+ if (fragp->fr_opcode)
+ fixp->fx_where -= r_old + fragp->insn_addr;
+ else
+ fixp->fx_done = 1;
+ fixp = fixp->fx_next;
+ }
+
+ if (fragp->insn_addr)
+ {
+ s7_number_to_chars (fragp->fr_literal, 0x0, fragp->insn_addr);
+ }
+ memcpy (fragp->fr_literal + fragp->insn_addr, backup, fragp->fr_fix);
+ fragp->fr_fix += fragp->insn_addr;
+}
+
+static long
+s7_pcrel_from (fixS * fixP)
+{
+ long retval = 0;
+
+ if (fixP->fx_addsy
+ && (S_GET_SEGMENT (fixP->fx_addsy) == undefined_section)
+ && (fixP->fx_subsy == NULL))
+ {
+ retval = 0;
+ }
+ else
+ {
+ retval = fixP->fx_where + fixP->fx_frag->fr_address;
+ }
+
+ return retval;
+}
+
+/* Round up a section size to the appropriate boundary. */
+static valueT
+s7_section_align (segT segment, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, segment);
+
+ return ((size + (1 << align) - 1) & (-1 << align));
+}
+
+static void
+s7_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ offsetT value = *valP;
+ offsetT abs_value = 0;
+ offsetT newval;
+ offsetT content;
+ unsigned short HI, LO;
+
+ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ gas_assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ {
+ if (fixP->fx_r_type != BFD_RELOC_SCORE_DUMMY_HI16)
+ fixP->fx_done = 1;
+ }
+
+ /* If this symbol is in a different section then we need to leave it for
+ the linker to deal with. Unfortunately, md_pcrel_from can't tell,
+ so we have to undo it's effects here. */
+ if (fixP->fx_pcrel)
+ {
+ if (fixP->fx_addsy != NULL
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && S_GET_SEGMENT (fixP->fx_addsy) != seg)
+ value += md_pcrel_from (fixP);
+ }
+
+ /* Remember value for emit_reloc. */
+ fixP->fx_addnumber = value;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_HI16_S:
+ if (fixP->fx_done)
+ { /* For la rd, imm32. */
+ newval = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ HI = (value) >> 16; /* mul to 2, then take the hi 16 bit. */
+ newval |= (HI & 0x3fff) << 1;
+ newval |= ((HI >> 14) & 0x3) << 16;
+ s7_number_to_chars (buf, newval, s7_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_LO16:
+ if (fixP->fx_done) /* For la rd, imm32. */
+ {
+ newval = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ LO = (value) & 0xffff;
+ newval |= (LO & 0x3fff) << 1; /* 16 bit: imm -> 14 bit in lo, 2 bit in hi. */
+ newval |= ((LO >> 14) & 0x3) << 16;
+ s7_number_to_chars (buf, newval, s7_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_SCORE_JMP:
+ {
+ content = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ value = fixP->fx_offset;
+ if (!(value >= 0 && value <= 0x1ffffff))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("j or jl truncate (0x%x) [0 ~ 2^25-1]"), (unsigned int) value);
+ return;
+ }
+ content = (content & ~0x3ff7ffe) | ((value << 1) & 0x3ff0000) | (value & 0x7fff);
+ s7_number_to_chars (buf, content, s7_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_SCORE_BRANCH:
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) || (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ content = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0x80008000) != 0x80008000))
+ {
+ if ((value & 0x80000000) == 0x80000000)
+ abs_value = 0xffffffff - value + 1;
+ if ((abs_value & 0xffffff00) != 0)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^8 ~ 2^8]"), (unsigned int) value);
+ return;
+ }
+ content = s7_md_chars_to_number (buf, s7_INSN16_SIZE);
+ content &= 0xff00;
+ content = (content & 0xff00) | ((value >> 1) & 0xff);
+ s7_number_to_chars (buf, content, s7_INSN16_SIZE);
+ fixP->fx_r_type = BFD_RELOC_SCORE16_BRANCH;
+ fixP->fx_size = 2;
+ }
+ else
+ {
+ if ((value & 0x80000000) == 0x80000000)
+ abs_value = 0xffffffff - value + 1;
+ if ((abs_value & 0xfff80000) != 0)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"),
+ (unsigned int) value);
+ return;
+ }
+ content = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ content &= 0xfc00fc01;
+ content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000);
+ s7_number_to_chars (buf, content, s7_INSN_SIZE);
+ }
+ break;
+ case BFD_RELOC_SCORE16_JMP:
+ content = s7_md_chars_to_number (buf, s7_INSN16_SIZE);
+ content &= 0xf001;
+ value = fixP->fx_offset;
+ if (!(value >= 0 && value <= 0xfff))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("j! or jl! truncate (0x%x) [0 ~ 2^12-1]"), (unsigned int) value);
+ return;
+ }
+ value = fixP->fx_offset & 0xfff;
+ content = (content & 0xfc01) | (value & 0xffe);
+ s7_number_to_chars (buf, content, s7_INSN16_SIZE);
+ break;
+ case BFD_RELOC_SCORE16_BRANCH:
+ content = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0x80008000) == 0x80008000))
+ {
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) ||
+ (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ if ((value & 0xfff80000) != 0 && (value & 0xfff80000) != 0xfff80000)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^19 ~ 2^19]"),
+ (unsigned int) value);
+ return;
+ }
+ content = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ content = (content & 0xfc00fc01) | (value & 0x3fe) | ((value << 6) & 0x3ff0000);
+ s7_number_to_chars (buf, content, s7_INSN_SIZE);
+ fixP->fx_r_type = BFD_RELOC_SCORE_BRANCH;
+ fixP->fx_size = 4;
+ break;
+ }
+ else
+ {
+ /* In differnt section. */
+ if ((S_GET_SEGMENT (fixP->fx_addsy) != seg) ||
+ (fixP->fx_addsy != NULL && S_IS_EXTERNAL (fixP->fx_addsy)))
+ value = fixP->fx_offset;
+ else
+ fixP->fx_done = 1;
+
+ if ((value & 0xffffff00) != 0 && (value & 0xffffff00) != 0xffffff00)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _(" branch relocation truncate (0x%x) [-2^8 ~ 2^8]"),
+ (unsigned int) value);
+ return;
+ }
+ content = s7_md_chars_to_number (buf, s7_INSN16_SIZE);
+ content = (content & 0xff00) | ((value >> 1) & 0xff);
+ s7_number_to_chars (buf, content, s7_INSN16_SIZE);
+ break;
+ }
+ case BFD_RELOC_8:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ s7_number_to_chars (buf, value, 1);
+#ifdef OBJ_ELF
+ else
+ {
+ value = fixP->fx_offset;
+ s7_number_to_chars (buf, value, 1);
+ }
+#endif
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ s7_number_to_chars (buf, value, 2);
+#ifdef OBJ_ELF
+ else
+ {
+ value = fixP->fx_offset;
+ s7_number_to_chars (buf, value, 2);
+ }
+#endif
+ break;
+ case BFD_RELOC_RVA:
+ case BFD_RELOC_32:
+ if (fixP->fx_done || fixP->fx_pcrel)
+ s7_number_to_chars (buf, value, 4);
+#ifdef OBJ_ELF
+ else
+ {
+ value = fixP->fx_offset;
+ s7_number_to_chars (buf, value, 4);
+ }
+#endif
+ break;
+ case BFD_RELOC_VTABLE_INHERIT:
+ fixP->fx_done = 0;
+ if (fixP->fx_addsy && !S_IS_DEFINED (fixP->fx_addsy) && !S_IS_WEAK (fixP->fx_addsy))
+ S_SET_WEAK (fixP->fx_addsy);
+ break;
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+ case BFD_RELOC_SCORE_GPREL15:
+ content = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ if ((fixP->fx_frag->fr_opcode != 0) && ((content & 0xfc1c8000) != 0x94188000))
+ fixP->fx_r_type = BFD_RELOC_NONE;
+ fixP->fx_done = 0;
+ break;
+ case BFD_RELOC_SCORE_GOT15:
+ case BFD_RELOC_SCORE_DUMMY_HI16:
+ case BFD_RELOC_SCORE_GOT_LO16:
+ case BFD_RELOC_SCORE_CALL15:
+ case BFD_RELOC_GPREL32:
+ break;
+ case BFD_RELOC_NONE:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("bad relocation fixup type (%d)"), fixP->fx_r_type);
+ }
+}
+
+/* Translate internal representation of relocation info to BFD target format. */
+
+static arelent **
+s7_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ static arelent *retval[MAX_RELOC_EXPANSION + 1]; /* MAX_RELOC_EXPANSION equals 2. */
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+ char *type;
+
+ reloc = retval[0] = xmalloc (sizeof (arelent));
+ retval[1] = NULL;
+
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = fixp->fx_offset;
+
+ /* If this is a variant frag, we may need to adjust the existing
+ reloc and generate a new one. */
+ if (fixp->fx_frag->fr_opcode != NULL && (fixp->fx_r_type == BFD_RELOC_SCORE_GPREL15))
+ {
+ /* Update instruction imm bit. */
+ offsetT newval;
+ unsigned short off;
+ char *buf;
+
+ buf = fixp->fx_frag->fr_literal + fixp->fx_frag->insn_addr;
+ newval = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ off = fixp->fx_offset >> 16;
+ newval |= (off & 0x3fff) << 1;
+ newval |= ((off >> 14) & 0x3) << 16;
+ s7_number_to_chars (buf, newval, s7_INSN_SIZE);
+
+ buf += s7_INSN_SIZE;
+ newval = s7_md_chars_to_number (buf, s7_INSN_SIZE);
+ off = fixp->fx_offset & 0xffff;
+ newval |= ((off & 0x3fff) << 1);
+ newval |= (((off >> 14) & 0x3) << 16);
+ s7_number_to_chars (buf, newval, s7_INSN_SIZE);
+
+ retval[1] = xmalloc (sizeof (arelent));
+ retval[2] = NULL;
+ retval[1]->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *retval[1]->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ retval[1]->address = (reloc->address + s7_RELAX_RELOC2 (fixp->fx_frag->fr_subtype));
+
+ retval[1]->addend = 0;
+ retval[1]->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO16);
+ gas_assert (retval[1]->howto != NULL);
+
+ fixp->fx_r_type = BFD_RELOC_HI16_S;
+ }
+
+ code = fixp->fx_r_type;
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ if (fixp->fx_pcrel)
+ {
+ code = BFD_RELOC_32_PCREL;
+ break;
+ }
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_SCORE_JMP:
+ case BFD_RELOC_SCORE_BRANCH:
+ case BFD_RELOC_SCORE16_JMP:
+ case BFD_RELOC_SCORE16_BRANCH:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_SCORE_GPREL15:
+ case BFD_RELOC_SCORE_GOT15:
+ case BFD_RELOC_SCORE_DUMMY_HI16:
+ case BFD_RELOC_SCORE_GOT_LO16:
+ case BFD_RELOC_SCORE_CALL15:
+ case BFD_RELOC_GPREL32:
+ case BFD_RELOC_NONE:
+ code = fixp->fx_r_type;
+ break;
+ default:
+ type = _("<unknown>");
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file format"), type);
+ return NULL;
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent %s relocation in this object file format1"),
+ bfd_get_reloc_code_name (code));
+ return NULL;
+ }
+ /* HACK: Since arm ELF uses Rel instead of Rela, encode the
+ vtable entry to be used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+
+ return retval;
+}
diff --git a/gas/config/tc-sh.c b/gas/config/tc-sh.c
new file mode 100644
index 0000000..6e9ae92
--- /dev/null
+++ b/gas/config/tc-sh.c
@@ -0,0 +1,4641 @@
+/* tc-sh.c -- Assemble code for the Renesas / SuperH SH
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* Written By Steve Chamberlain <sac@cygnus.com> */
+
+#include "as.h"
+#include "subsegs.h"
+#define DEFINE_TABLE
+#include "opcodes/sh-opc.h"
+#include "safe-ctype.h"
+#include "struc-symbol.h"
+
+#ifdef OBJ_ELF
+#include "elf/sh.h"
+#endif
+
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+typedef struct
+ {
+ sh_arg_type type;
+ int reg;
+ expressionS immediate;
+ }
+sh_operand_info;
+
+const char comment_chars[] = "!";
+const char line_separator_chars[] = ";";
+const char line_comment_chars[] = "!#";
+
+static void s_uses (int);
+static void s_uacons (int);
+
+#ifdef OBJ_ELF
+static void sh_elf_cons (int);
+
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+#endif
+
+static void
+big (int ignore ATTRIBUTE_UNUSED)
+{
+ if (! target_big_endian)
+ as_bad (_("directive .big encountered when option -big required"));
+
+ /* Stop further messages. */
+ target_big_endian = 1;
+}
+
+static void
+little (int ignore ATTRIBUTE_UNUSED)
+{
+ if (target_big_endian)
+ as_bad (_("directive .little encountered when option -little required"));
+
+ /* Stop further messages. */
+ target_big_endian = 0;
+}
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
+
+const pseudo_typeS md_pseudo_table[] =
+{
+#ifdef OBJ_ELF
+ {"long", sh_elf_cons, 4},
+ {"int", sh_elf_cons, 4},
+ {"word", sh_elf_cons, 2},
+ {"short", sh_elf_cons, 2},
+#else
+ {"int", cons, 4},
+ {"word", cons, 2},
+#endif /* OBJ_ELF */
+ {"big", big, 0},
+ {"form", listing_psize, 0},
+ {"little", little, 0},
+ {"heading", listing_title, 0},
+ {"import", s_ignore, 0},
+ {"page", listing_eject, 0},
+ {"program", s_ignore, 0},
+ {"uses", s_uses, 0},
+ {"uaword", s_uacons, 2},
+ {"ualong", s_uacons, 4},
+ {"uaquad", s_uacons, 8},
+ {"2byte", s_uacons, 2},
+ {"4byte", s_uacons, 4},
+ {"8byte", s_uacons, 8},
+#ifdef HAVE_SH64
+ {"mode", s_sh64_mode, 0 },
+
+ /* Have the old name too. */
+ {"isa", s_sh64_mode, 0 },
+
+ /* Assert that the right ABI is used. */
+ {"abi", s_sh64_abi, 0 },
+
+ { "vtable_inherit", sh64_vtable_inherit, 0 },
+ { "vtable_entry", sh64_vtable_entry, 0 },
+#endif /* HAVE_SH64 */
+ {0, 0, 0}
+};
+
+int sh_relax; /* set if -relax seen */
+
+/* Whether -small was seen. */
+
+int sh_small;
+
+/* Flag to generate relocations against symbol values for local symbols. */
+
+static int dont_adjust_reloc_32;
+
+/* Flag to indicate that '$' is allowed as a register prefix. */
+
+static int allow_dollar_register_prefix;
+
+/* Preset architecture set, if given; zero otherwise. */
+
+static unsigned int preset_target_arch;
+
+/* The bit mask of architectures that could
+ accommodate the insns seen so far. */
+static unsigned int valid_arch;
+
+#ifdef OBJ_ELF
+/* Whether --fdpic was given. */
+static int sh_fdpic;
+#endif
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant. */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+#define C(a,b) ENCODE_RELAX(a,b)
+
+#define ENCODE_RELAX(what,length) (((what) << 4) + (length))
+#define GET_WHAT(x) ((x>>4))
+
+/* These are the three types of relaxable instruction. */
+/* These are the types of relaxable instructions; except for END which is
+ a marker. */
+#define COND_JUMP 1
+#define COND_JUMP_DELAY 2
+#define UNCOND_JUMP 3
+
+#ifdef HAVE_SH64
+
+/* A 16-bit (times four) pc-relative operand, at most expanded to 32 bits. */
+#define SH64PCREL16_32 4
+/* A 16-bit (times four) pc-relative operand, at most expanded to 64 bits. */
+#define SH64PCREL16_64 5
+
+/* Variants of the above for adjusting the insn to PTA or PTB according to
+ the label. */
+#define SH64PCREL16PT_32 6
+#define SH64PCREL16PT_64 7
+
+/* A MOVI expansion, expanding to at most 32 or 64 bits. */
+#define MOVI_IMM_32 8
+#define MOVI_IMM_32_PCREL 9
+#define MOVI_IMM_64 10
+#define MOVI_IMM_64_PCREL 11
+#define END 12
+
+#else /* HAVE_SH64 */
+
+#define END 4
+
+#endif /* HAVE_SH64 */
+
+#define UNDEF_DISP 0
+#define COND8 1
+#define COND12 2
+#define COND32 3
+#define UNDEF_WORD_DISP 4
+
+#define UNCOND12 1
+#define UNCOND32 2
+
+#ifdef HAVE_SH64
+#define UNDEF_SH64PCREL 0
+#define SH64PCREL16 1
+#define SH64PCREL32 2
+#define SH64PCREL48 3
+#define SH64PCREL64 4
+#define SH64PCRELPLT 5
+
+#define UNDEF_MOVI 0
+#define MOVI_16 1
+#define MOVI_32 2
+#define MOVI_48 3
+#define MOVI_64 4
+#define MOVI_PLT 5
+#define MOVI_GOTOFF 6
+#define MOVI_GOTPC 7
+#endif /* HAVE_SH64 */
+
+/* Branch displacements are from the address of the branch plus
+ four, thus all minimum and maximum values have 4 added to them. */
+#define COND8_F 258
+#define COND8_M -252
+#define COND8_LENGTH 2
+
+/* There is one extra instruction before the branch, so we must add
+ two more bytes to account for it. */
+#define COND12_F 4100
+#define COND12_M -4090
+#define COND12_LENGTH 6
+
+#define COND12_DELAY_LENGTH 4
+
+/* ??? The minimum and maximum values are wrong, but this does not matter
+ since this relocation type is not supported yet. */
+#define COND32_F (1<<30)
+#define COND32_M -(1<<30)
+#define COND32_LENGTH 14
+
+#define UNCOND12_F 4098
+#define UNCOND12_M -4092
+#define UNCOND12_LENGTH 2
+
+/* ??? The minimum and maximum values are wrong, but this does not matter
+ since this relocation type is not supported yet. */
+#define UNCOND32_F (1<<30)
+#define UNCOND32_M -(1<<30)
+#define UNCOND32_LENGTH 14
+
+#ifdef HAVE_SH64
+/* The trivial expansion of a SH64PCREL16 relaxation is just a "PT label,
+ TRd" as is the current insn, so no extra length. Note that the "reach"
+ is calculated from the address *after* that insn, but the offset in the
+ insn is calculated from the beginning of the insn. We also need to
+ take into account the implicit 1 coded as the "A" in PTA when counting
+ forward. If PTB reaches an odd address, we trap that as an error
+ elsewhere, so we don't have to have different relaxation entries. We
+ don't add a one to the negative range, since PTB would then have the
+ farthest backward-reaching value skipped, not generated at relaxation. */
+#define SH64PCREL16_F (32767 * 4 - 4 + 1)
+#define SH64PCREL16_M (-32768 * 4 - 4)
+#define SH64PCREL16_LENGTH 0
+
+/* The next step is to change that PT insn into
+ MOVI ((label - datalabel Ln) >> 16) & 65535, R25
+ SHORI (label - datalabel Ln) & 65535, R25
+ Ln:
+ PTREL R25,TRd
+ which means two extra insns, 8 extra bytes. This is the limit for the
+ 32-bit ABI.
+
+ The expressions look a bit bad since we have to adjust this to avoid overflow on a
+ 32-bit host. */
+#define SH64PCREL32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define SH64PCREL32_LENGTH (2 * 4)
+
+/* Similarly, we just change the MOVI and add a SHORI for the 48-bit
+ expansion. */
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+ host. */
+#define SH64PCREL32_M (((long) -1 << 30) * 2 - 4)
+#define SH64PCREL48_F ((((long) 1 << 47) - 1) - 4)
+#define SH64PCREL48_M (((long) -1 << 47) - 4)
+#define SH64PCREL48_LENGTH (3 * 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+ in reach to the 32-bit state. Note that we have a slightly incorrect
+ reach, but the correct one above will overflow a 32-bit number. */
+#define SH64PCREL32_M (((long) -1 << 30) * 2)
+#define SH64PCREL48_F SH64PCREL32_F
+#define SH64PCREL48_M SH64PCREL32_M
+#define SH64PCREL48_LENGTH (3 * 4)
+#endif /* BFD_HOST_64BIT_LONG */
+
+/* And similarly for the 64-bit expansion; a MOVI + SHORI + SHORI + SHORI
+ + PTREL sequence. */
+#define SH64PCREL64_LENGTH (4 * 4)
+
+/* For MOVI, we make the MOVI + SHORI... expansion you can see in the
+ SH64PCREL expansions. The PCREL one is similar, but the other has no
+ pc-relative reach; it must be fully expanded in
+ shmedia_md_estimate_size_before_relax. */
+#define MOVI_16_LENGTH 0
+#define MOVI_16_F (32767 - 4)
+#define MOVI_16_M (-32768 - 4)
+#define MOVI_32_LENGTH 4
+#define MOVI_32_F ((((long) 1 << 30) - 1) * 2 + 1 - 4)
+#define MOVI_48_LENGTH 8
+
+#if BFD_HOST_64BIT_LONG
+/* The "reach" type is long, so we can only do this for a 64-bit-long
+ host. */
+#define MOVI_32_M (((long) -1 << 30) * 2 - 4)
+#define MOVI_48_F ((((long) 1 << 47) - 1) - 4)
+#define MOVI_48_M (((long) -1 << 47) - 4)
+#else
+/* If the host does not have 64-bit longs, just make this state identical
+ in reach to the 32-bit state. Note that we have a slightly incorrect
+ reach, but the correct one above will overflow a 32-bit number. */
+#define MOVI_32_M (((long) -1 << 30) * 2)
+#define MOVI_48_F MOVI_32_F
+#define MOVI_48_M MOVI_32_M
+#endif /* BFD_HOST_64BIT_LONG */
+
+#define MOVI_64_LENGTH 12
+#endif /* HAVE_SH64 */
+
+#define EMPTY { 0, 0, 0, 0 }
+
+const relax_typeS md_relax_table[C (END, 0)] = {
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ EMPTY,
+ /* C (COND_JUMP, COND8) */
+ { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP, COND12) },
+ /* C (COND_JUMP, COND12) */
+ { COND12_F, COND12_M, COND12_LENGTH, C (COND_JUMP, COND32), },
+ /* C (COND_JUMP, COND32) */
+ { COND32_F, COND32_M, COND32_LENGTH, 0, },
+ /* C (COND_JUMP, UNDEF_WORD_DISP) */
+ { 0, 0, COND32_LENGTH, 0, },
+ EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ EMPTY,
+ /* C (COND_JUMP_DELAY, COND8) */
+ { COND8_F, COND8_M, COND8_LENGTH, C (COND_JUMP_DELAY, COND12) },
+ /* C (COND_JUMP_DELAY, COND12) */
+ { COND12_F, COND12_M, COND12_DELAY_LENGTH, C (COND_JUMP_DELAY, COND32), },
+ /* C (COND_JUMP_DELAY, COND32) */
+ { COND32_F, COND32_M, COND32_LENGTH, 0, },
+ /* C (COND_JUMP_DELAY, UNDEF_WORD_DISP) */
+ { 0, 0, COND32_LENGTH, 0, },
+ EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ EMPTY,
+ /* C (UNCOND_JUMP, UNCOND12) */
+ { UNCOND12_F, UNCOND12_M, UNCOND12_LENGTH, C (UNCOND_JUMP, UNCOND32), },
+ /* C (UNCOND_JUMP, UNCOND32) */
+ { UNCOND32_F, UNCOND32_M, UNCOND32_LENGTH, 0, },
+ EMPTY,
+ /* C (UNCOND_JUMP, UNDEF_WORD_DISP) */
+ { 0, 0, UNCOND32_LENGTH, 0, },
+ EMPTY, EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#ifdef HAVE_SH64
+ /* C (SH64PCREL16_32, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_32, SH64PCREL32) },
+ /* C (SH64PCREL16_32, SH64PCREL32) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ /* C (SH64PCREL16_32, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (SH64PCREL16_64, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16_64, SH64PCREL32) },
+ /* C (SH64PCREL16_64, SH64PCREL32) */
+ { SH64PCREL32_F, SH64PCREL32_M, SH64PCREL32_LENGTH, C (SH64PCREL16_64, SH64PCREL48) },
+ /* C (SH64PCREL16_64, SH64PCREL48) */
+ { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16_64, SH64PCREL64) },
+ /* C (SH64PCREL16_64, SH64PCREL64) */
+ { 0, 0, SH64PCREL64_LENGTH, 0 },
+ /* C (SH64PCREL16_64, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL64_LENGTH, 0 },
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (SH64PCREL16PT_32, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_32, SH64PCREL32) },
+ /* C (SH64PCREL16PT_32, SH64PCREL32) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ /* C (SH64PCREL16PT_32, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (SH64PCREL16PT_64, SH64PCREL16) */
+ EMPTY,
+ { SH64PCREL16_F, SH64PCREL16_M, SH64PCREL16_LENGTH, C (SH64PCREL16PT_64, SH64PCREL32) },
+ /* C (SH64PCREL16PT_64, SH64PCREL32) */
+ { SH64PCREL32_F,
+ SH64PCREL32_M,
+ SH64PCREL32_LENGTH,
+ C (SH64PCREL16PT_64, SH64PCREL48) },
+ /* C (SH64PCREL16PT_64, SH64PCREL48) */
+ { SH64PCREL48_F, SH64PCREL48_M, SH64PCREL48_LENGTH, C (SH64PCREL16PT_64, SH64PCREL64) },
+ /* C (SH64PCREL16PT_64, SH64PCREL64) */
+ { 0, 0, SH64PCREL64_LENGTH, 0 },
+ /* C (SH64PCREL16PT_64, SH64PCRELPLT) */
+ { 0, 0, SH64PCREL64_LENGTH, 0},
+ EMPTY, EMPTY,
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_32, UNDEF_MOVI) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ /* C (MOVI_IMM_32, MOVI_16) */
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32, MOVI_32) },
+ /* C (MOVI_IMM_32, MOVI_32) */
+ { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY,
+ /* C (MOVI_IMM_32, MOVI_GOTOFF) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_32_PCREL, MOVI_16) */
+ EMPTY,
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_32_PCREL, MOVI_32) },
+ /* C (MOVI_IMM_32_PCREL, MOVI_32) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY,
+ /* C (MOVI_IMM_32_PCREL, MOVI_PLT) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY,
+ /* C (MOVI_IMM_32_PCREL, MOVI_GOTPC) */
+ { 0, 0, MOVI_32_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_64, UNDEF_MOVI) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ /* C (MOVI_IMM_64, MOVI_16) */
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64, MOVI_32) },
+ /* C (MOVI_IMM_64, MOVI_32) */
+ { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64, MOVI_48) },
+ /* C (MOVI_IMM_64, MOVI_48) */
+ { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64, MOVI_64) },
+ /* C (MOVI_IMM_64, MOVI_64) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY,
+ /* C (MOVI_IMM_64, MOVI_GOTOFF) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+ /* C (MOVI_IMM_64_PCREL, MOVI_16) */
+ EMPTY,
+ { MOVI_16_F, MOVI_16_M, MOVI_16_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_32) },
+ /* C (MOVI_IMM_64_PCREL, MOVI_32) */
+ { MOVI_32_F, MOVI_32_M, MOVI_32_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_48) },
+ /* C (MOVI_IMM_64_PCREL, MOVI_48) */
+ { MOVI_48_F, MOVI_48_M, MOVI_48_LENGTH, C (MOVI_IMM_64_PCREL, MOVI_64) },
+ /* C (MOVI_IMM_64_PCREL, MOVI_64) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ /* C (MOVI_IMM_64_PCREL, MOVI_PLT) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY,
+ /* C (MOVI_IMM_64_PCREL, MOVI_GOTPC) */
+ { 0, 0, MOVI_64_LENGTH, 0 },
+ EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
+
+#endif /* HAVE_SH64 */
+
+};
+
+#undef EMPTY
+
+static struct hash_control *opcode_hash_control; /* Opcode mnemonics */
+
+
+#ifdef OBJ_ELF
+/* Determinet whether the symbol needs any kind of PIC relocation. */
+
+inline static int
+sh_PIC_related_p (symbolS *sym)
+{
+ expressionS *exp;
+
+ if (! sym)
+ return 0;
+
+ if (sym == GOT_symbol)
+ return 1;
+
+#ifdef HAVE_SH64
+ if (sh_PIC_related_p (*symbol_get_tc (sym)))
+ return 1;
+#endif
+
+ exp = symbol_get_value_expression (sym);
+
+ return (exp->X_op == O_PIC_reloc
+ || sh_PIC_related_p (exp->X_add_symbol)
+ || sh_PIC_related_p (exp->X_op_symbol));
+}
+
+/* Determine the relocation type to be used to represent the
+ expression, that may be rearranged. */
+
+static int
+sh_check_fixup (expressionS *main_exp, bfd_reloc_code_real_type *r_type_p)
+{
+ expressionS *exp = main_exp;
+
+ /* This is here for backward-compatibility only. GCC used to generated:
+
+ f@PLT + . - (.LPCS# + 2)
+
+ but we'd rather be able to handle this as a PIC-related reference
+ plus/minus a symbol. However, gas' parser gives us:
+
+ O_subtract (O_add (f@PLT, .), .LPCS#+2)
+
+ so we attempt to transform this into:
+
+ O_subtract (f@PLT, O_subtract (.LPCS#+2, .))
+
+ which we can handle simply below. */
+ if (exp->X_op == O_subtract)
+ {
+ if (sh_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+
+ if (exp && sh_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp && exp->X_op == O_add
+ && sh_PIC_related_p (exp->X_add_symbol))
+ {
+ symbolS *sym = exp->X_add_symbol;
+
+ exp->X_op = O_subtract;
+ exp->X_add_symbol = main_exp->X_op_symbol;
+
+ main_exp->X_op_symbol = main_exp->X_add_symbol;
+ main_exp->X_add_symbol = sym;
+
+ main_exp->X_add_number += exp->X_add_number;
+ exp->X_add_number = 0;
+ }
+
+ exp = main_exp;
+ }
+ else if (exp->X_op == O_add && sh_PIC_related_p (exp->X_op_symbol))
+ return 1;
+
+ if (exp->X_op == O_symbol || exp->X_op == O_add || exp->X_op == O_subtract)
+ {
+#ifdef HAVE_SH64
+ if (exp->X_add_symbol
+ && (exp->X_add_symbol == GOT_symbol
+ || (GOT_symbol
+ && *symbol_get_tc (exp->X_add_symbol) == GOT_symbol)))
+ {
+ switch (*r_type_p)
+ {
+ case BFD_RELOC_SH_IMM_LOW16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_LOW16;
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_MEDLOW16;
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_MEDHI16;
+ break;
+
+ case BFD_RELOC_SH_IMM_HI16:
+ *r_type_p = BFD_RELOC_SH_GOTPC_HI16;
+ break;
+
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_UNUSED:
+ *r_type_p = BFD_RELOC_SH_GOTPC;
+ break;
+
+ default:
+ abort ();
+ }
+ return 0;
+ }
+#else
+ if (exp->X_add_symbol && exp->X_add_symbol == GOT_symbol)
+ {
+ *r_type_p = BFD_RELOC_SH_GOTPC;
+ return 0;
+ }
+#endif
+ exp = symbol_get_value_expression (exp->X_add_symbol);
+ if (! exp)
+ return 0;
+ }
+
+ if (exp->X_op == O_PIC_reloc)
+ {
+ switch (*r_type_p)
+ {
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_UNUSED:
+ *r_type_p = exp->X_md;
+ break;
+
+ case BFD_RELOC_SH_DISP20:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT20;
+ break;
+
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF20;
+ break;
+
+ case BFD_RELOC_SH_GOTFUNCDESC:
+ *r_type_p = BFD_RELOC_SH_GOTFUNCDESC20;
+ break;
+
+ case BFD_RELOC_SH_GOTOFFFUNCDESC:
+ *r_type_p = BFD_RELOC_SH_GOTOFFFUNCDESC20;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+#ifdef HAVE_SH64
+ case BFD_RELOC_SH_IMM_LOW16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_LOW16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_LOW16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_LOW16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_LOW16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_MEDLOW16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_MEDLOW16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_MEDLOW16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_MEDLOW16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_MEDHI16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_MEDHI16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_MEDHI16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_MEDHI16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case BFD_RELOC_SH_IMM_HI16:
+ switch (exp->X_md)
+ {
+ case BFD_RELOC_32_GOTOFF:
+ *r_type_p = BFD_RELOC_SH_GOTOFF_HI16;
+ break;
+
+ case BFD_RELOC_SH_GOTPLT32:
+ *r_type_p = BFD_RELOC_SH_GOTPLT_HI16;
+ break;
+
+ case BFD_RELOC_32_GOT_PCREL:
+ *r_type_p = BFD_RELOC_SH_GOT_HI16;
+ break;
+
+ case BFD_RELOC_32_PLT_PCREL:
+ *r_type_p = BFD_RELOC_SH_PLT_HI16;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+#endif
+
+ default:
+ abort ();
+ }
+ if (exp == main_exp)
+ exp->X_op = O_symbol;
+ else
+ {
+ main_exp->X_add_symbol = exp->X_add_symbol;
+ main_exp->X_add_number += exp->X_add_number;
+ }
+ }
+ else
+ return (sh_PIC_related_p (exp->X_add_symbol)
+ || sh_PIC_related_p (exp->X_op_symbol));
+
+ return 0;
+}
+
+/* Add expression EXP of SIZE bytes to offset OFF of fragment FRAG. */
+
+void
+sh_cons_fix_new (fragS *frag, int off, int size, expressionS *exp,
+ bfd_reloc_code_real_type r_type)
+{
+ r_type = BFD_RELOC_UNUSED;
+
+ if (sh_check_fixup (exp, &r_type))
+ as_bad (_("Invalid PIC expression."));
+
+ if (r_type == BFD_RELOC_UNUSED)
+ switch (size)
+ {
+ case 1:
+ r_type = BFD_RELOC_8;
+ break;
+
+ case 2:
+ r_type = BFD_RELOC_16;
+ break;
+
+ case 4:
+ r_type = BFD_RELOC_32;
+ break;
+
+ case 8:
+ r_type = BFD_RELOC_64;
+ break;
+
+ default:
+ goto error;
+ }
+ else if (size != 4)
+ {
+ error:
+ as_bad (_("unsupported BFD relocation size %u"), size);
+ r_type = BFD_RELOC_UNUSED;
+ }
+
+ fix_new_exp (frag, off, size, exp, 0, r_type);
+}
+
+/* The regular cons() function, that reads constants, doesn't support
+ suffixes such as @GOT, @GOTOFF and @PLT, that generate
+ machine-specific relocation types. So we must define it here. */
+/* Clobbers input_line_pointer, checks end-of-line. */
+/* NBYTES 1=.byte, 2=.word, 4=.long */
+static void
+sh_elf_cons (register int nbytes)
+{
+ expressionS exp;
+
+#ifdef HAVE_SH64
+
+ /* Update existing range to include a previous insn, if there was one. */
+ sh64_update_contents_mark (TRUE);
+
+ /* We need to make sure the contents type is set to data. */
+ sh64_flag_output ();
+
+#endif /* HAVE_SH64 */
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
+
+ do
+ {
+ expression (&exp);
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ if (*input_line_pointer == '#' || *input_line_pointer == '!')
+ {
+ while (! is_end_of_line[(unsigned char) *input_line_pointer++]);
+ }
+ else
+ demand_empty_rest_of_line ();
+}
+
+/* The regular frag_offset_fixed_p doesn't work for rs_align_test
+ frags. */
+
+static bfd_boolean
+align_test_frag_offset_fixed_p (const fragS *frag1, const fragS *frag2,
+ bfd_vma *offset)
+{
+ const fragS *frag;
+ bfd_vma off;
+
+ /* Start with offset initialised to difference between the two frags.
+ Prior to assigning frag addresses this will be zero. */
+ off = frag1->fr_address - frag2->fr_address;
+ if (frag1 == frag2)
+ {
+ *offset = off;
+ return TRUE;
+ }
+
+ /* Maybe frag2 is after frag1. */
+ frag = frag1;
+ while (frag->fr_type == rs_fill
+ || frag->fr_type == rs_align_test)
+ {
+ if (frag->fr_type == rs_fill)
+ off += frag->fr_fix + frag->fr_offset * frag->fr_var;
+ else
+ off += frag->fr_fix;
+ frag = frag->fr_next;
+ if (frag == NULL)
+ break;
+ if (frag == frag2)
+ {
+ *offset = off;
+ return TRUE;
+ }
+ }
+
+ /* Maybe frag1 is after frag2. */
+ off = frag1->fr_address - frag2->fr_address;
+ frag = frag2;
+ while (frag->fr_type == rs_fill
+ || frag->fr_type == rs_align_test)
+ {
+ if (frag->fr_type == rs_fill)
+ off -= frag->fr_fix + frag->fr_offset * frag->fr_var;
+ else
+ off -= frag->fr_fix;
+ frag = frag->fr_next;
+ if (frag == NULL)
+ break;
+ if (frag == frag1)
+ {
+ *offset = off;
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+/* Optimize a difference of symbols which have rs_align_test frag if
+ possible. */
+
+int
+sh_optimize_expr (expressionS *l, operatorT op, expressionS *r)
+{
+ bfd_vma frag_off;
+
+ if (op == O_subtract
+ && l->X_op == O_symbol
+ && r->X_op == O_symbol
+ && S_GET_SEGMENT (l->X_add_symbol) == S_GET_SEGMENT (r->X_add_symbol)
+ && (SEG_NORMAL (S_GET_SEGMENT (l->X_add_symbol))
+ || r->X_add_symbol == l->X_add_symbol)
+ && align_test_frag_offset_fixed_p (symbol_get_frag (l->X_add_symbol),
+ symbol_get_frag (r->X_add_symbol),
+ &frag_off))
+ {
+ offsetT symval_diff = S_GET_VALUE (l->X_add_symbol)
+ - S_GET_VALUE (r->X_add_symbol);
+ subtract_from_result (l, r->X_add_number, r->X_extrabit);
+ subtract_from_result (l, frag_off / OCTETS_PER_BYTE, 0);
+ add_to_result (l, symval_diff, symval_diff < 0);
+ l->X_op = O_constant;
+ l->X_add_symbol = 0;
+ return 1;
+ }
+ return 0;
+}
+#endif /* OBJ_ELF */
+
+/* This function is called once, at assembler startup time. This should
+ set up all the tables, etc that the MD part of the assembler needs. */
+
+void
+md_begin (void)
+{
+ const sh_opcode_info *opcode;
+ char *prev_name = "";
+ unsigned int target_arch;
+
+ target_arch
+ = preset_target_arch ? preset_target_arch : arch_sh_up & ~arch_sh_has_dsp;
+ valid_arch = target_arch;
+
+#ifdef HAVE_SH64
+ shmedia_md_begin ();
+#endif
+
+ opcode_hash_control = hash_new ();
+
+ /* Insert unique names into hash table. */
+ for (opcode = sh_table; opcode->name; opcode++)
+ {
+ if (strcmp (prev_name, opcode->name) != 0)
+ {
+ if (!SH_MERGE_ARCH_SET_VALID (opcode->arch, target_arch))
+ continue;
+ prev_name = opcode->name;
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ }
+ }
+}
+
+static int reg_m;
+static int reg_n;
+static int reg_x, reg_y;
+static int reg_efg;
+static int reg_b;
+
+#define IDENT_CHAR(c) (ISALNUM (c) || (c) == '_')
+
+/* Try to parse a reg name. Return the number of chars consumed. */
+
+static unsigned int
+parse_reg_without_prefix (char *src, int *mode, int *reg)
+{
+ char l0 = TOLOWER (src[0]);
+ char l1 = l0 ? TOLOWER (src[1]) : 0;
+
+ /* We use ! IDENT_CHAR for the next character after the register name, to
+ make sure that we won't accidentally recognize a symbol name such as
+ 'sram' or sr_ram as being a reference to the register 'sr'. */
+
+ if (l0 == 'r')
+ {
+ if (l1 == '1')
+ {
+ if (src[2] >= '0' && src[2] <= '5'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_REG_N;
+ *reg = 10 + src[2] - '0';
+ return 3;
+ }
+ }
+ if (l1 >= '0' && l1 <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_REG_N;
+ *reg = (l1 - '0');
+ return 2;
+ }
+ if (l1 >= '0' && l1 <= '7' && strncasecmp (&src[2], "_bank", 5) == 0
+ && ! IDENT_CHAR ((unsigned char) src[7]))
+ {
+ *mode = A_REG_B;
+ *reg = (l1 - '0');
+ return 7;
+ }
+
+ if (l1 == 'e' && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_RE;
+ return 2;
+ }
+ if (l1 == 's' && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_RS;
+ return 2;
+ }
+ }
+
+ if (l0 == 'a')
+ {
+ if (l1 == '0')
+ {
+ if (! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = DSP_REG_N;
+ *reg = A_A0_NUM;
+ return 2;
+ }
+ if (TOLOWER (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = DSP_REG_N;
+ *reg = A_A0G_NUM;
+ return 3;
+ }
+ }
+ if (l1 == '1')
+ {
+ if (! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = DSP_REG_N;
+ *reg = A_A1_NUM;
+ return 2;
+ }
+ if (TOLOWER (src[2]) == 'g' && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = DSP_REG_N;
+ *reg = A_A1G_NUM;
+ return 3;
+ }
+ }
+
+ if (l1 == 'x' && src[2] >= '0' && src[2] <= '1'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_REG_N;
+ *reg = 4 + (l1 - '0');
+ return 3;
+ }
+ if (l1 == 'y' && src[2] >= '0' && src[2] <= '1'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_REG_N;
+ *reg = 6 + (l1 - '0');
+ return 3;
+ }
+ if (l1 == 's' && src[2] >= '0' && src[2] <= '3'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ int n = l1 - '0';
+
+ *mode = A_REG_N;
+ *reg = n | ((~n & 2) << 1);
+ return 3;
+ }
+ }
+
+ if (l0 == 'i' && l1 && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ if (l1 == 's')
+ {
+ *mode = A_REG_N;
+ *reg = 8;
+ return 2;
+ }
+ if (l1 == 'x')
+ {
+ *mode = A_REG_N;
+ *reg = 8;
+ return 2;
+ }
+ if (l1 == 'y')
+ {
+ *mode = A_REG_N;
+ *reg = 9;
+ return 2;
+ }
+ }
+
+ if (l0 == 'x' && l1 >= '0' && l1 <= '1'
+ && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = DSP_REG_N;
+ *reg = A_X0_NUM + l1 - '0';
+ return 2;
+ }
+
+ if (l0 == 'y' && l1 >= '0' && l1 <= '1'
+ && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = DSP_REG_N;
+ *reg = A_Y0_NUM + l1 - '0';
+ return 2;
+ }
+
+ if (l0 == 'm' && l1 >= '0' && l1 <= '1'
+ && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = DSP_REG_N;
+ *reg = l1 == '0' ? A_M0_NUM : A_M1_NUM;
+ return 2;
+ }
+
+ if (l0 == 's'
+ && l1 == 's'
+ && TOLOWER (src[2]) == 'r' && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_SSR;
+ return 3;
+ }
+
+ if (l0 == 's' && l1 == 'p' && TOLOWER (src[2]) == 'c'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_SPC;
+ return 3;
+ }
+
+ if (l0 == 's' && l1 == 'g' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_SGR;
+ return 3;
+ }
+
+ if (l0 == 'd' && l1 == 's' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_DSR;
+ return 3;
+ }
+
+ if (l0 == 'd' && l1 == 'b' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_DBR;
+ return 3;
+ }
+
+ if (l0 == 's' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_SR;
+ return 2;
+ }
+
+ if (l0 == 's' && l1 == 'p' && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_REG_N;
+ *reg = 15;
+ return 2;
+ }
+
+ if (l0 == 'p' && l1 == 'r' && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_PR;
+ return 2;
+ }
+ if (l0 == 'p' && l1 == 'c' && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ /* Don't use A_DISP_PC here - that would accept stuff like 'mova pc,r0'
+ and use an uninitialized immediate. */
+ *mode = A_PC;
+ return 2;
+ }
+ if (l0 == 'g' && l1 == 'b' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_GBR;
+ return 3;
+ }
+ if (l0 == 'v' && l1 == 'b' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_VBR;
+ return 3;
+ }
+
+ if (l0 == 't' && l1 == 'b' && TOLOWER (src[2]) == 'r'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_TBR;
+ return 3;
+ }
+ if (l0 == 'm' && l1 == 'a' && TOLOWER (src[2]) == 'c'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ if (TOLOWER (src[3]) == 'l')
+ {
+ *mode = A_MACL;
+ return 4;
+ }
+ if (TOLOWER (src[3]) == 'h')
+ {
+ *mode = A_MACH;
+ return 4;
+ }
+ }
+ if (l0 == 'm' && l1 == 'o' && TOLOWER (src[2]) == 'd'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_MOD;
+ return 3;
+ }
+ if (l0 == 'f' && l1 == 'r')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '5'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = F_REG_N;
+ *reg = 10 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = F_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (l0 == 'd' && l1 == 'r')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = D_REG_N;
+ *reg = 10 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = D_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (l0 == 'x' && l1 == 'd')
+ {
+ if (src[2] == '1')
+ {
+ if (src[3] >= '0' && src[3] <= '4' && ! ((src[3] - '0') & 1)
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = X_REG_N;
+ *reg = 11 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '8' && ! ((src[2] - '0') & 1)
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = X_REG_N;
+ *reg = (src[2] - '0') + 1;
+ return 3;
+ }
+ }
+ if (l0 == 'f' && l1 == 'v')
+ {
+ if (src[2] == '1'&& src[3] == '2' && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = V_REG_N;
+ *reg = 12;
+ return 4;
+ }
+ if ((src[2] == '0' || src[2] == '4' || src[2] == '8')
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = V_REG_N;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+ if (l0 == 'f' && l1 == 'p' && TOLOWER (src[2]) == 'u'
+ && TOLOWER (src[3]) == 'l'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = FPUL_N;
+ return 4;
+ }
+
+ if (l0 == 'f' && l1 == 'p' && TOLOWER (src[2]) == 's'
+ && TOLOWER (src[3]) == 'c'
+ && TOLOWER (src[4]) == 'r' && ! IDENT_CHAR ((unsigned char) src[5]))
+ {
+ *mode = FPSCR_N;
+ return 5;
+ }
+
+ if (l0 == 'x' && l1 == 'm' && TOLOWER (src[2]) == 't'
+ && TOLOWER (src[3]) == 'r'
+ && TOLOWER (src[4]) == 'x' && ! IDENT_CHAR ((unsigned char) src[5]))
+ {
+ *mode = XMTRX_M4;
+ return 5;
+ }
+
+ return 0;
+}
+
+/* Like parse_reg_without_prefix, but this version supports
+ $-prefixed register names if enabled by the user. */
+
+static unsigned int
+parse_reg (char *src, int *mode, int *reg)
+{
+ unsigned int prefix;
+ unsigned int consumed;
+
+ if (src[0] == '$')
+ {
+ if (allow_dollar_register_prefix)
+ {
+ src ++;
+ prefix = 1;
+ }
+ else
+ return 0;
+ }
+ else
+ prefix = 0;
+
+ consumed = parse_reg_without_prefix (src, mode, reg);
+
+ if (consumed == 0)
+ return 0;
+
+ return consumed + prefix;
+}
+
+static char *
+parse_exp (char *s, sh_operand_info *op)
+{
+ char *save;
+ char *new_pointer;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+ expression (&op->immediate);
+ if (op->immediate.X_op == O_absent)
+ as_bad (_("missing operand"));
+ new_pointer = input_line_pointer;
+ input_line_pointer = save;
+ return new_pointer;
+}
+
+/* The many forms of operand:
+
+ Rn Register direct
+ @Rn Register indirect
+ @Rn+ Autoincrement
+ @-Rn Autodecrement
+ @(disp:4,Rn)
+ @(disp:8,GBR)
+ @(disp:8,PC)
+
+ @(R0,Rn)
+ @(R0,GBR)
+
+ disp:8
+ disp:12
+ #imm8
+ pr, gbr, vbr, macl, mach
+ */
+
+static char *
+parse_at (char *src, sh_operand_info *op)
+{
+ int len;
+ int mode;
+ src++;
+ if (src[0] == '@')
+ {
+ src = parse_at (src, op);
+ if (op->type == A_DISP_TBR)
+ op->type = A_DISP2_TBR;
+ else
+ as_bad (_("illegal double indirection"));
+ }
+ else if (src[0] == '-')
+ {
+ /* Must be predecrement. */
+ src++;
+
+ len = parse_reg (src, &mode, &(op->reg));
+ if (mode != A_REG_N)
+ as_bad (_("illegal register after @-"));
+
+ op->type = A_DEC_N;
+ src += len;
+ }
+ else if (src[0] == '(')
+ {
+ /* Could be @(disp, rn), @(disp, gbr), @(disp, pc), @(r0, gbr) or
+ @(r0, rn). */
+ src++;
+ len = parse_reg (src, &mode, &(op->reg));
+ if (len && mode == A_REG_N)
+ {
+ src += len;
+ if (op->reg != 0)
+ {
+ as_bad (_("must be @(r0,...)"));
+ }
+ if (src[0] == ',')
+ {
+ src++;
+ /* Now can be rn or gbr. */
+ len = parse_reg (src, &mode, &(op->reg));
+ }
+ else
+ {
+ len = 0;
+ }
+ if (len)
+ {
+ if (mode == A_GBR)
+ {
+ op->type = A_R0_GBR;
+ }
+ else if (mode == A_REG_N)
+ {
+ op->type = A_IND_R0_REG_N;
+ }
+ else
+ {
+ as_bad (_("syntax error in @(r0,...)"));
+ }
+ }
+ else
+ {
+ as_bad (_("syntax error in @(r0...)"));
+ }
+ }
+ else
+ {
+ /* Must be an @(disp,.. thing). */
+ src = parse_exp (src, op);
+ if (src[0] == ',')
+ src++;
+ /* Now can be rn, gbr or pc. */
+ len = parse_reg (src, &mode, &op->reg);
+ if (len)
+ {
+ if (mode == A_REG_N)
+ {
+ op->type = A_DISP_REG_N;
+ }
+ else if (mode == A_GBR)
+ {
+ op->type = A_DISP_GBR;
+ }
+ else if (mode == A_TBR)
+ {
+ op->type = A_DISP_TBR;
+ }
+ else if (mode == A_PC)
+ {
+ /* We want @(expr, pc) to uniformly address . + expr,
+ no matter if expr is a constant, or a more complex
+ expression, e.g. sym-. or sym1-sym2.
+ However, we also used to accept @(sym,pc)
+ as addressing sym, i.e. meaning the same as plain sym.
+ Some existing code does use the @(sym,pc) syntax, so
+ we give it the old semantics for now, but warn about
+ its use, so that users have some time to fix their code.
+
+ Note that due to this backward compatibility hack,
+ we'll get unexpected results when @(offset, pc) is used,
+ and offset is a symbol that is set later to an an address
+ difference, or an external symbol that is set to an
+ address difference in another source file, so we want to
+ eventually remove it. */
+ if (op->immediate.X_op == O_symbol)
+ {
+ op->type = A_DISP_PC;
+ as_warn (_("Deprecated syntax."));
+ }
+ else
+ {
+ op->type = A_DISP_PC_ABS;
+ /* Such operands don't get corrected for PC==.+4, so
+ make the correction here. */
+ op->immediate.X_add_number -= 4;
+ }
+ }
+ else
+ {
+ as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
+ }
+ }
+ else
+ {
+ as_bad (_("syntax error in @(disp,[Rn, gbr, pc])"));
+ }
+ }
+ src += len;
+ if (src[0] != ')')
+ as_bad (_("expecting )"));
+ else
+ src++;
+ }
+ else
+ {
+ src += parse_reg (src, &mode, &(op->reg));
+ if (mode != A_REG_N)
+ as_bad (_("illegal register after @"));
+
+ if (src[0] == '+')
+ {
+ char l0, l1;
+
+ src++;
+ l0 = TOLOWER (src[0]);
+ l1 = TOLOWER (src[1]);
+
+ if ((l0 == 'r' && l1 == '8')
+ || (l0 == 'i' && (l1 == 'x' || l1 == 's')))
+ {
+ src += 2;
+ op->type = AX_PMOD_N;
+ }
+ else if ( (l0 == 'r' && l1 == '9')
+ || (l0 == 'i' && l1 == 'y'))
+ {
+ src += 2;
+ op->type = AY_PMOD_N;
+ }
+ else
+ op->type = A_INC_N;
+ }
+ else
+ op->type = A_IND_N;
+ }
+ return src;
+}
+
+static void
+get_operand (char **ptr, sh_operand_info *op)
+{
+ char *src = *ptr;
+ int mode = -1;
+ unsigned int len;
+
+ if (src[0] == '#')
+ {
+ src++;
+ *ptr = parse_exp (src, op);
+ op->type = A_IMM;
+ return;
+ }
+
+ else if (src[0] == '@')
+ {
+ *ptr = parse_at (src, op);
+ return;
+ }
+ len = parse_reg (src, &mode, &(op->reg));
+ if (len)
+ {
+ *ptr = src + len;
+ op->type = mode;
+ return;
+ }
+ else
+ {
+ /* Not a reg, the only thing left is a displacement. */
+ *ptr = parse_exp (src, op);
+ op->type = A_DISP_PC;
+ return;
+ }
+}
+
+static char *
+get_operands (sh_opcode_info *info, char *args, sh_operand_info *operand)
+{
+ char *ptr = args;
+ if (info->arg[0])
+ {
+ /* The pre-processor will eliminate whitespace in front of '@'
+ after the first argument; we may be called multiple times
+ from assemble_ppi, so don't insist on finding whitespace here. */
+ if (*ptr == ' ')
+ ptr++;
+
+ get_operand (&ptr, operand + 0);
+ if (info->arg[1])
+ {
+ if (*ptr == ',')
+ {
+ ptr++;
+ }
+ get_operand (&ptr, operand + 1);
+ /* ??? Hack: psha/pshl have a varying operand number depending on
+ the type of the first operand. We handle this by having the
+ three-operand version first and reducing the number of operands
+ parsed to two if we see that the first operand is an immediate.
+ This works because no insn with three operands has an immediate
+ as first operand. */
+ if (info->arg[2] && operand[0].type != A_IMM)
+ {
+ if (*ptr == ',')
+ {
+ ptr++;
+ }
+ get_operand (&ptr, operand + 2);
+ }
+ else
+ {
+ operand[2].type = 0;
+ }
+ }
+ else
+ {
+ operand[1].type = 0;
+ operand[2].type = 0;
+ }
+ }
+ else
+ {
+ operand[0].type = 0;
+ operand[1].type = 0;
+ operand[2].type = 0;
+ }
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes, return the opcode which matches the opcodes
+ provided. */
+
+static sh_opcode_info *
+get_specific (sh_opcode_info *opcode, sh_operand_info *operands)
+{
+ sh_opcode_info *this_try = opcode;
+ char *name = opcode->name;
+ int n = 0;
+
+ while (opcode->name)
+ {
+ this_try = opcode++;
+ if ((this_try->name != name) && (strcmp (this_try->name, name) != 0))
+ {
+ /* We've looked so far down the table that we've run out of
+ opcodes with the same name. */
+ return 0;
+ }
+
+ /* Look at both operands needed by the opcodes and provided by
+ the user - since an arg test will often fail on the same arg
+ again and again, we'll try and test the last failing arg the
+ first on each opcode try. */
+ for (n = 0; this_try->arg[n]; n++)
+ {
+ sh_operand_info *user = operands + n;
+ sh_arg_type arg = this_try->arg[n];
+
+ switch (arg)
+ {
+ case A_DISP_PC:
+ if (user->type == A_DISP_PC_ABS)
+ break;
+ /* Fall through. */
+ case A_IMM:
+ case A_BDISP12:
+ case A_BDISP8:
+ case A_DISP_GBR:
+ case A_DISP2_TBR:
+ case A_MACH:
+ case A_PR:
+ case A_MACL:
+ if (user->type != arg)
+ goto fail;
+ break;
+ case A_R0:
+ /* opcode needs r0 */
+ if (user->type != A_REG_N || user->reg != 0)
+ goto fail;
+ break;
+ case A_R0_GBR:
+ if (user->type != A_R0_GBR || user->reg != 0)
+ goto fail;
+ break;
+ case F_FR0:
+ if (user->type != F_REG_N || user->reg != 0)
+ goto fail;
+ break;
+
+ case A_REG_N:
+ case A_INC_N:
+ case A_DEC_N:
+ case A_IND_N:
+ case A_IND_R0_REG_N:
+ case A_DISP_REG_N:
+ case F_REG_N:
+ case D_REG_N:
+ case X_REG_N:
+ case V_REG_N:
+ case FPUL_N:
+ case FPSCR_N:
+ case DSP_REG_N:
+ /* Opcode needs rn */
+ if (user->type != arg)
+ goto fail;
+ reg_n = user->reg;
+ break;
+ case DX_REG_N:
+ if (user->type != D_REG_N && user->type != X_REG_N)
+ goto fail;
+ reg_n = user->reg;
+ break;
+ case A_GBR:
+ case A_TBR:
+ case A_SR:
+ case A_VBR:
+ case A_DSR:
+ case A_MOD:
+ case A_RE:
+ case A_RS:
+ case A_SSR:
+ case A_SPC:
+ case A_SGR:
+ case A_DBR:
+ if (user->type != arg)
+ goto fail;
+ break;
+
+ case A_REG_B:
+ if (user->type != arg)
+ goto fail;
+ reg_b = user->reg;
+ break;
+
+ case A_INC_R15:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg != 15)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case A_DEC_R15:
+ if (user->type != A_DEC_N)
+ goto fail;
+ if (user->reg != 15)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case A_REG_M:
+ case A_INC_M:
+ case A_DEC_M:
+ case A_IND_M:
+ case A_IND_R0_REG_M:
+ case A_DISP_REG_M:
+ case DSP_REG_M:
+ /* Opcode needs rn */
+ if (user->type != arg - A_REG_M + A_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+
+ case AS_DEC_N:
+ if (user->type != A_DEC_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AS_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if (user->reg < 2 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AX_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if (user->reg < 4 || user->reg > 5)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AXY_PMOD_N:
+ if (user->type != AX_PMOD_N)
+ goto fail;
+ if ((user->reg < 4 || user->reg > 5)
+ && (user->reg < 0 || user->reg > 1))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AY_PMOD_N:
+ if (user->type != AY_PMOD_N)
+ goto fail;
+ if (user->reg < 6 || user->reg > 7)
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_INC_N:
+ if (user->type != A_INC_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_IND_N:
+ if (user->type != A_IND_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case AYX_PMOD_N:
+ if (user->type != AY_PMOD_N)
+ goto fail;
+ if ((user->reg < 6 || user->reg > 7)
+ && (user->reg < 2 || user->reg > 3))
+ goto fail;
+ reg_n = user->reg;
+ break;
+
+ case DSP_REG_A_M:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ if (user->reg != A_A0_NUM
+ && user->reg != A_A1_NUM)
+ goto fail;
+ reg_m = user->reg;
+ break;
+
+ case DSP_REG_AX:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_A0_NUM:
+ reg_x = 0;
+ break;
+ case A_A1_NUM:
+ reg_x = 2;
+ break;
+ case A_X0_NUM:
+ reg_x = 1;
+ break;
+ case A_X1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_XY:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_X0_NUM:
+ reg_x = 0;
+ break;
+ case A_X1_NUM:
+ reg_x = 2;
+ break;
+ case A_Y0_NUM:
+ reg_x = 1;
+ break;
+ case A_Y1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_AY:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_A0_NUM:
+ reg_y = 0;
+ break;
+ case A_A1_NUM:
+ reg_y = 1;
+ break;
+ case A_Y0_NUM:
+ reg_y = 2;
+ break;
+ case A_Y1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_YX:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_Y0_NUM:
+ reg_y = 0;
+ break;
+ case A_Y1_NUM:
+ reg_y = 1;
+ break;
+ case A_X0_NUM:
+ reg_y = 2;
+ break;
+ case A_X1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_X:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_X0_NUM:
+ reg_x = 0;
+ break;
+ case A_X1_NUM:
+ reg_x = 1;
+ break;
+ case A_A0_NUM:
+ reg_x = 2;
+ break;
+ case A_A1_NUM:
+ reg_x = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_Y:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_Y0_NUM:
+ reg_y = 0;
+ break;
+ case A_Y1_NUM:
+ reg_y = 1;
+ break;
+ case A_M0_NUM:
+ reg_y = 2;
+ break;
+ case A_M1_NUM:
+ reg_y = 3;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_E:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_X0_NUM:
+ reg_efg = 0 << 10;
+ break;
+ case A_X1_NUM:
+ reg_efg = 1 << 10;
+ break;
+ case A_Y0_NUM:
+ reg_efg = 2 << 10;
+ break;
+ case A_A1_NUM:
+ reg_efg = 3 << 10;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_F:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_Y0_NUM:
+ reg_efg |= 0 << 8;
+ break;
+ case A_Y1_NUM:
+ reg_efg |= 1 << 8;
+ break;
+ case A_X0_NUM:
+ reg_efg |= 2 << 8;
+ break;
+ case A_A1_NUM:
+ reg_efg |= 3 << 8;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case DSP_REG_G:
+ if (user->type != DSP_REG_N)
+ goto fail;
+ switch (user->reg)
+ {
+ case A_M0_NUM:
+ reg_efg |= 0 << 2;
+ break;
+ case A_M1_NUM:
+ reg_efg |= 1 << 2;
+ break;
+ case A_A0_NUM:
+ reg_efg |= 2 << 2;
+ break;
+ case A_A1_NUM:
+ reg_efg |= 3 << 2;
+ break;
+ default:
+ goto fail;
+ }
+ break;
+
+ case A_A0:
+ if (user->type != DSP_REG_N || user->reg != A_A0_NUM)
+ goto fail;
+ break;
+ case A_X0:
+ if (user->type != DSP_REG_N || user->reg != A_X0_NUM)
+ goto fail;
+ break;
+ case A_X1:
+ if (user->type != DSP_REG_N || user->reg != A_X1_NUM)
+ goto fail;
+ break;
+ case A_Y0:
+ if (user->type != DSP_REG_N || user->reg != A_Y0_NUM)
+ goto fail;
+ break;
+ case A_Y1:
+ if (user->type != DSP_REG_N || user->reg != A_Y1_NUM)
+ goto fail;
+ break;
+
+ case F_REG_M:
+ case D_REG_M:
+ case X_REG_M:
+ case V_REG_M:
+ case FPUL_M:
+ case FPSCR_M:
+ /* Opcode needs rn */
+ if (user->type != arg - F_REG_M + F_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+ case DX_REG_M:
+ if (user->type != D_REG_N && user->type != X_REG_N)
+ goto fail;
+ reg_m = user->reg;
+ break;
+ case XMTRX_M4:
+ if (user->type != XMTRX_M4)
+ goto fail;
+ reg_m = 4;
+ break;
+
+ default:
+ printf (_("unhandled %d\n"), arg);
+ goto fail;
+ }
+ if (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh2a_nofpu_up)
+ && ( arg == A_DISP_REG_M
+ || arg == A_DISP_REG_N))
+ {
+ /* Check a few key IMM* fields for overflow. */
+ int opf;
+ long val = user->immediate.X_add_number;
+
+ for (opf = 0; opf < 4; opf ++)
+ switch (this_try->nibbles[opf])
+ {
+ case IMM0_4:
+ case IMM1_4:
+ if (val < 0 || val > 15)
+ goto fail;
+ break;
+ case IMM0_4BY2:
+ case IMM1_4BY2:
+ if (val < 0 || val > 15 * 2)
+ goto fail;
+ break;
+ case IMM0_4BY4:
+ case IMM1_4BY4:
+ if (val < 0 || val > 15 * 4)
+ goto fail;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if ( !SH_MERGE_ARCH_SET_VALID (valid_arch, this_try->arch))
+ goto fail;
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, this_try->arch);
+ return this_try;
+ fail:
+ ;
+ }
+
+ return 0;
+}
+
+static void
+insert (char *where, int how, int pcrel, sh_operand_info *op)
+{
+ fix_new_exp (frag_now,
+ where - frag_now->fr_literal,
+ 2,
+ &op->immediate,
+ pcrel,
+ how);
+}
+
+static void
+insert4 (char * where, int how, int pcrel, sh_operand_info * op)
+{
+ fix_new_exp (frag_now,
+ where - frag_now->fr_literal,
+ 4,
+ & op->immediate,
+ pcrel,
+ how);
+}
+static void
+build_relax (sh_opcode_info *opcode, sh_operand_info *op)
+{
+ int high_byte = target_big_endian ? 0 : 1;
+ char *p;
+
+ if (opcode->arg[0] == A_BDISP8)
+ {
+ int what = (opcode->nibbles[1] & 4) ? COND_JUMP_DELAY : COND_JUMP;
+ p = frag_var (rs_machine_dependent,
+ md_relax_table[C (what, COND32)].rlx_length,
+ md_relax_table[C (what, COND8)].rlx_length,
+ C (what, 0),
+ op->immediate.X_add_symbol,
+ op->immediate.X_add_number,
+ 0);
+ p[high_byte] = (opcode->nibbles[0] << 4) | (opcode->nibbles[1]);
+ }
+ else if (opcode->arg[0] == A_BDISP12)
+ {
+ p = frag_var (rs_machine_dependent,
+ md_relax_table[C (UNCOND_JUMP, UNCOND32)].rlx_length,
+ md_relax_table[C (UNCOND_JUMP, UNCOND12)].rlx_length,
+ C (UNCOND_JUMP, 0),
+ op->immediate.X_add_symbol,
+ op->immediate.X_add_number,
+ 0);
+ p[high_byte] = (opcode->nibbles[0] << 4);
+ }
+
+}
+
+/* Insert ldrs & ldre with fancy relocations that relaxation can recognize. */
+
+static char *
+insert_loop_bounds (char *output, sh_operand_info *operand)
+{
+ char *name;
+ symbolS *end_sym;
+
+ /* Since the low byte of the opcode will be overwritten by the reloc, we
+ can just stash the high byte into both bytes and ignore endianness. */
+ output[0] = 0x8c;
+ output[1] = 0x8c;
+ insert (output, BFD_RELOC_SH_LOOP_START, 1, operand);
+ insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1);
+
+ if (sh_relax)
+ {
+ static int count = 0;
+
+ /* If the last loop insn is a two-byte-insn, it is in danger of being
+ swapped with the insn after it. To prevent this, create a new
+ symbol - complete with SH_LABEL reloc - after the last loop insn.
+ If the last loop insn is four bytes long, the symbol will be
+ right in the middle, but four byte insns are not swapped anyways. */
+ /* A REPEAT takes 6 bytes. The SH has a 32 bit address space.
+ Hence a 9 digit number should be enough to count all REPEATs. */
+ name = alloca (11);
+ sprintf (name, "_R%x", count++ & 0x3fffffff);
+ end_sym = symbol_new (name, undefined_section, 0, &zero_address_frag);
+ /* Make this a local symbol. */
+#ifdef OBJ_COFF
+ SF_SET_LOCAL (end_sym);
+#endif /* OBJ_COFF */
+ symbol_table_insert (end_sym);
+ end_sym->sy_value = operand[1].immediate;
+ end_sym->sy_value.X_add_number += 2;
+ fix_new (frag_now, frag_now_fix (), 2, end_sym, 0, 1, BFD_RELOC_SH_LABEL);
+ }
+
+ output = frag_more (2);
+ output[0] = 0x8e;
+ output[1] = 0x8e;
+ insert (output, BFD_RELOC_SH_LOOP_START, 1, operand);
+ insert (output, BFD_RELOC_SH_LOOP_END, 1, operand + 1);
+
+ return frag_more (2);
+}
+
+/* Now we know what sort of opcodes it is, let's build the bytes. */
+
+static unsigned int
+build_Mytes (sh_opcode_info *opcode, sh_operand_info *operand)
+{
+ int indx;
+ char nbuf[8];
+ char *output;
+ unsigned int size = 2;
+ int low_byte = target_big_endian ? 1 : 0;
+ int max_index = 4;
+ bfd_reloc_code_real_type r_type;
+#ifdef OBJ_ELF
+ int unhandled_pic = 0;
+#endif
+
+ nbuf[0] = 0;
+ nbuf[1] = 0;
+ nbuf[2] = 0;
+ nbuf[3] = 0;
+ nbuf[4] = 0;
+ nbuf[5] = 0;
+ nbuf[6] = 0;
+ nbuf[7] = 0;
+
+#ifdef OBJ_ELF
+ for (indx = 0; indx < 3; indx++)
+ if (opcode->arg[indx] == A_IMM
+ && operand[indx].type == A_IMM
+ && (operand[indx].immediate.X_op == O_PIC_reloc
+ || sh_PIC_related_p (operand[indx].immediate.X_add_symbol)
+ || sh_PIC_related_p (operand[indx].immediate.X_op_symbol)))
+ unhandled_pic = 1;
+#endif
+
+ if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
+ {
+ output = frag_more (4);
+ size = 4;
+ max_index = 8;
+ }
+ else
+ output = frag_more (2);
+
+ for (indx = 0; indx < max_index; indx++)
+ {
+ sh_nibble_type i = opcode->nibbles[indx];
+ if (i < 16)
+ {
+ nbuf[indx] = i;
+ }
+ else
+ {
+ switch (i)
+ {
+ case REG_N:
+ case REG_N_D:
+ nbuf[indx] = reg_n;
+ break;
+ case REG_M:
+ nbuf[indx] = reg_m;
+ break;
+ case SDT_REG_N:
+ if (reg_n < 2 || reg_n > 5)
+ as_bad (_("Invalid register: 'r%d'"), reg_n);
+ nbuf[indx] = (reg_n & 3) | 4;
+ break;
+ case REG_NM:
+ nbuf[indx] = reg_n | (reg_m >> 2);
+ break;
+ case REG_B:
+ nbuf[indx] = reg_b | 0x08;
+ break;
+ case REG_N_B01:
+ nbuf[indx] = reg_n | 0x01;
+ break;
+ case IMM0_3s:
+ nbuf[indx] |= 0x08;
+ case IMM0_3c:
+ insert (output + low_byte, BFD_RELOC_SH_IMM3, 0, operand);
+ break;
+ case IMM0_3Us:
+ nbuf[indx] |= 0x80;
+ case IMM0_3Uc:
+ insert (output + low_byte, BFD_RELOC_SH_IMM3U, 0, operand);
+ break;
+ case DISP0_12:
+ insert (output + 2, BFD_RELOC_SH_DISP12, 0, operand);
+ break;
+ case DISP0_12BY2:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY2, 0, operand);
+ break;
+ case DISP0_12BY4:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY4, 0, operand);
+ break;
+ case DISP0_12BY8:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY8, 0, operand);
+ break;
+ case DISP1_12:
+ insert (output + 2, BFD_RELOC_SH_DISP12, 0, operand+1);
+ break;
+ case DISP1_12BY2:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY2, 0, operand+1);
+ break;
+ case DISP1_12BY4:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY4, 0, operand+1);
+ break;
+ case DISP1_12BY8:
+ insert (output + 2, BFD_RELOC_SH_DISP12BY8, 0, operand+1);
+ break;
+ case IMM0_20_4:
+ break;
+ case IMM0_20:
+ r_type = BFD_RELOC_SH_DISP20;
+#ifdef OBJ_ELF
+ if (sh_check_fixup (&operand->immediate, &r_type))
+ as_bad (_("Invalid PIC expression."));
+ unhandled_pic = 0;
+#endif
+ insert4 (output, r_type, 0, operand);
+ break;
+ case IMM0_20BY8:
+ insert4 (output, BFD_RELOC_SH_DISP20BY8, 0, operand);
+ break;
+ case IMM0_4BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand);
+ break;
+ case IMM0_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand);
+ break;
+ case IMM0_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand);
+ break;
+ case IMM1_4BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY4, 0, operand + 1);
+ break;
+ case IMM1_4BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4BY2, 0, operand + 1);
+ break;
+ case IMM1_4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM4, 0, operand + 1);
+ break;
+ case IMM0_8BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand);
+ break;
+ case IMM0_8BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand);
+ break;
+ case IMM0_8:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand);
+ break;
+ case IMM1_8BY4:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY4, 0, operand + 1);
+ break;
+ case IMM1_8BY2:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8BY2, 0, operand + 1);
+ break;
+ case IMM1_8:
+ insert (output + low_byte, BFD_RELOC_SH_IMM8, 0, operand + 1);
+ break;
+ case PCRELIMM_8BY4:
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY4,
+ operand->type != A_DISP_PC_ABS, operand);
+ break;
+ case PCRELIMM_8BY2:
+ insert (output, BFD_RELOC_SH_PCRELIMM8BY2,
+ operand->type != A_DISP_PC_ABS, operand);
+ break;
+ case REPEAT:
+ output = insert_loop_bounds (output, operand);
+ nbuf[indx] = opcode->nibbles[3];
+ operand += 2;
+ break;
+ default:
+ printf (_("failed for %d\n"), i);
+ }
+ }
+ }
+#ifdef OBJ_ELF
+ if (unhandled_pic)
+ as_bad (_("misplaced PIC operand"));
+#endif
+ if (!target_big_endian)
+ {
+ output[1] = (nbuf[0] << 4) | (nbuf[1]);
+ output[0] = (nbuf[2] << 4) | (nbuf[3]);
+ }
+ else
+ {
+ output[0] = (nbuf[0] << 4) | (nbuf[1]);
+ output[1] = (nbuf[2] << 4) | (nbuf[3]);
+ }
+ if (SH_MERGE_ARCH_SET (opcode->arch, arch_op32))
+ {
+ if (!target_big_endian)
+ {
+ output[3] = (nbuf[4] << 4) | (nbuf[5]);
+ output[2] = (nbuf[6] << 4) | (nbuf[7]);
+ }
+ else
+ {
+ output[2] = (nbuf[4] << 4) | (nbuf[5]);
+ output[3] = (nbuf[6] << 4) | (nbuf[7]);
+ }
+ }
+ return size;
+}
+
+/* Find an opcode at the start of *STR_P in the hash table, and set
+ *STR_P to the first character after the last one read. */
+
+static sh_opcode_info *
+find_cooked_opcode (char **str_p)
+{
+ char *str = *str_p;
+ unsigned char *op_start;
+ unsigned char *op_end;
+ char name[20];
+ unsigned int nlen = 0;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the op code end.
+ The pre-processor will eliminate whitespace in front of
+ any '@' after the first argument; we may be called from
+ assemble_ppi, so the opcode might be terminated by an '@'. */
+ for (op_start = op_end = (unsigned char *) str;
+ *op_end
+ && nlen < sizeof (name) - 1
+ && !is_end_of_line[*op_end] && *op_end != ' ' && *op_end != '@';
+ op_end++)
+ {
+ unsigned char c = op_start[nlen];
+
+ /* The machine independent code will convert CMP/EQ into cmp/EQ
+ because it thinks the '/' is the end of the symbol. Moreover,
+ all but the first sub-insn is a parallel processing insn won't
+ be capitalized. Instead of hacking up the machine independent
+ code, we just deal with it here. */
+ c = TOLOWER (c);
+ name[nlen] = c;
+ nlen++;
+ }
+
+ name[nlen] = 0;
+ *str_p = (char *) op_end;
+
+ if (nlen == 0)
+ as_bad (_("can't find opcode "));
+
+ return (sh_opcode_info *) hash_find (opcode_hash_control, name);
+}
+
+/* Assemble a parallel processing insn. */
+#define DDT_BASE 0xf000 /* Base value for double data transfer insns */
+
+static unsigned int
+assemble_ppi (char *op_end, sh_opcode_info *opcode)
+{
+ int movx = 0;
+ int movy = 0;
+ int cond = 0;
+ int field_b = 0;
+ char *output;
+ int move_code;
+ unsigned int size;
+
+ for (;;)
+ {
+ sh_operand_info operand[3];
+
+ /* Some insn ignore one or more register fields, e.g. psts machl,a0.
+ Make sure we encode a defined insn pattern. */
+ reg_x = 0;
+ reg_y = 0;
+ reg_n = 0;
+
+ if (opcode->arg[0] != A_END)
+ op_end = get_operands (opcode, op_end, operand);
+ try_another_opcode:
+ opcode = get_specific (opcode, operand);
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands. */
+ char *where = frag_more (2);
+ size = 2;
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ as_bad (_("invalid operands for opcode"));
+ return size;
+ }
+
+ if (opcode->nibbles[0] != PPI)
+ as_bad (_("insn can't be combined with parallel processing insn"));
+
+ switch (opcode->nibbles[1])
+ {
+
+ case NOPX:
+ if (movx)
+ as_bad (_("multiple movx specifications"));
+ movx = DDT_BASE;
+ break;
+ case NOPY:
+ if (movy)
+ as_bad (_("multiple movy specifications"));
+ movy = DDT_BASE;
+ break;
+
+ case MOVX_NOPY:
+ if (movx)
+ as_bad (_("multiple movx specifications"));
+ if ((reg_n < 4 || reg_n > 5)
+ && (reg_n < 0 || reg_n > 1))
+ as_bad (_("invalid movx address register"));
+ if (movy && movy != DDT_BASE)
+ as_bad (_("insn cannot be combined with non-nopy"));
+ movx = ((((reg_n & 1) != 0) << 9)
+ + (((reg_n & 4) == 0) << 8)
+ + (reg_x << 6)
+ + (opcode->nibbles[2] << 4)
+ + opcode->nibbles[3]
+ + DDT_BASE);
+ break;
+
+ case MOVY_NOPX:
+ if (movy)
+ as_bad (_("multiple movy specifications"));
+ if ((reg_n < 6 || reg_n > 7)
+ && (reg_n < 2 || reg_n > 3))
+ as_bad (_("invalid movy address register"));
+ if (movx && movx != DDT_BASE)
+ as_bad (_("insn cannot be combined with non-nopx"));
+ movy = ((((reg_n & 1) != 0) << 8)
+ + (((reg_n & 4) == 0) << 9)
+ + (reg_y << 6)
+ + (opcode->nibbles[2] << 4)
+ + opcode->nibbles[3]
+ + DDT_BASE);
+ break;
+
+ case MOVX:
+ if (movx)
+ as_bad (_("multiple movx specifications"));
+ if (movy & 0x2ac)
+ as_bad (_("previous movy requires nopx"));
+ if (reg_n < 4 || reg_n > 5)
+ as_bad (_("invalid movx address register"));
+ if (opcode->nibbles[2] & 8)
+ {
+ if (reg_m == A_A1_NUM)
+ movx = 1 << 7;
+ else if (reg_m != A_A0_NUM)
+ as_bad (_("invalid movx dsp register"));
+ }
+ else
+ {
+ if (reg_x > 1)
+ as_bad (_("invalid movx dsp register"));
+ movx = reg_x << 7;
+ }
+ movx += ((reg_n - 4) << 9) + (opcode->nibbles[2] << 2) + DDT_BASE;
+ break;
+
+ case MOVY:
+ if (movy)
+ as_bad (_("multiple movy specifications"));
+ if (movx & 0x153)
+ as_bad (_("previous movx requires nopy"));
+ if (opcode->nibbles[2] & 8)
+ {
+ /* Bit 3 in nibbles[2] is intended for bit 4 of the opcode,
+ so add 8 more. */
+ movy = 8;
+ if (reg_m == A_A1_NUM)
+ movy += 1 << 6;
+ else if (reg_m != A_A0_NUM)
+ as_bad (_("invalid movy dsp register"));
+ }
+ else
+ {
+ if (reg_y > 1)
+ as_bad (_("invalid movy dsp register"));
+ movy = reg_y << 6;
+ }
+ if (reg_n < 6 || reg_n > 7)
+ as_bad (_("invalid movy address register"));
+ movy += ((reg_n - 6) << 8) + opcode->nibbles[2] + DDT_BASE;
+ break;
+
+ case PSH:
+ if (operand[0].immediate.X_op != O_constant)
+ as_bad (_("dsp immediate shift value not constant"));
+ field_b = ((opcode->nibbles[2] << 12)
+ | (operand[0].immediate.X_add_number & 127) << 4
+ | reg_n);
+ break;
+ case PPI3NC:
+ if (cond)
+ {
+ opcode++;
+ goto try_another_opcode;
+ }
+ /* Fall through. */
+ case PPI3:
+ if (field_b)
+ as_bad (_("multiple parallel processing specifications"));
+ field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
+ + (reg_x << 6) + (reg_y << 4) + reg_n);
+ switch (opcode->nibbles[4])
+ {
+ case HEX_0:
+ case HEX_XX00:
+ case HEX_00YY:
+ break;
+ case HEX_1:
+ case HEX_4:
+ field_b += opcode->nibbles[4] << 4;
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case PDC:
+ if (cond)
+ as_bad (_("multiple condition specifications"));
+ cond = opcode->nibbles[2] << 8;
+ if (*op_end)
+ goto skip_cond_check;
+ break;
+ case PPIC:
+ if (field_b)
+ as_bad (_("multiple parallel processing specifications"));
+ field_b = ((opcode->nibbles[2] << 12) + (opcode->nibbles[3] << 8)
+ + cond + (reg_x << 6) + (reg_y << 4) + reg_n);
+ cond = 0;
+ switch (opcode->nibbles[4])
+ {
+ case HEX_0:
+ case HEX_XX00:
+ case HEX_00YY:
+ break;
+ case HEX_1:
+ case HEX_4:
+ field_b += opcode->nibbles[4] << 4;
+ break;
+ default:
+ abort ();
+ }
+ break;
+ case PMUL:
+ if (field_b)
+ {
+ if ((field_b & 0xef00) == 0xa100)
+ field_b -= 0x8100;
+ /* pclr Dz pmuls Se,Sf,Dg */
+ else if ((field_b & 0xff00) == 0x8d00
+ && (SH_MERGE_ARCH_SET_VALID (valid_arch, arch_sh4al_dsp_up)))
+ {
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, arch_sh4al_dsp_up);
+ field_b -= 0x8cf0;
+ }
+ else
+ as_bad (_("insn cannot be combined with pmuls"));
+ switch (field_b & 0xf)
+ {
+ case A_X0_NUM:
+ field_b += 0 - A_X0_NUM;
+ break;
+ case A_Y0_NUM:
+ field_b += 1 - A_Y0_NUM;
+ break;
+ case A_A0_NUM:
+ field_b += 2 - A_A0_NUM;
+ break;
+ case A_A1_NUM:
+ field_b += 3 - A_A1_NUM;
+ break;
+ default:
+ as_bad (_("bad combined pmuls output operand"));
+ }
+ /* Generate warning if the destination register for padd / psub
+ and pmuls is the same ( only for A0 or A1 ).
+ If the last nibble is 1010 then A0 is used in both
+ padd / psub and pmuls. If it is 1111 then A1 is used
+ as destination register in both padd / psub and pmuls. */
+
+ if ((((field_b | reg_efg) & 0x000F) == 0x000A)
+ || (((field_b | reg_efg) & 0x000F) == 0x000F))
+ as_warn (_("destination register is same for parallel insns"));
+ }
+ field_b += 0x4000 + reg_efg;
+ break;
+ default:
+ abort ();
+ }
+ if (cond)
+ {
+ as_bad (_("condition not followed by conditionalizable insn"));
+ cond = 0;
+ }
+ if (! *op_end)
+ break;
+ skip_cond_check:
+ opcode = find_cooked_opcode (&op_end);
+ if (opcode == NULL)
+ {
+ (as_bad
+ (_("unrecognized characters at end of parallel processing insn")));
+ break;
+ }
+ }
+
+ move_code = movx | movy;
+ if (field_b)
+ {
+ /* Parallel processing insn. */
+ unsigned long ppi_code = (movx | movy | 0xf800) << 16 | field_b;
+
+ output = frag_more (4);
+ size = 4;
+ if (! target_big_endian)
+ {
+ output[3] = ppi_code >> 8;
+ output[2] = ppi_code;
+ }
+ else
+ {
+ output[2] = ppi_code >> 8;
+ output[3] = ppi_code;
+ }
+ move_code |= 0xf800;
+ }
+ else
+ {
+ /* Just a double data transfer. */
+ output = frag_more (2);
+ size = 2;
+ }
+ if (! target_big_endian)
+ {
+ output[1] = move_code >> 8;
+ output[0] = move_code;
+ }
+ else
+ {
+ output[0] = move_code >> 8;
+ output[1] = move_code;
+ }
+ return size;
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *str)
+{
+ char *op_end;
+ sh_operand_info operand[3];
+ sh_opcode_info *opcode;
+ unsigned int size = 0;
+ char *initial_str = str;
+
+#ifdef HAVE_SH64
+ if (sh64_isa_mode == sh64_isa_shmedia)
+ {
+ shmedia_md_assemble (str);
+ return;
+ }
+ else
+ {
+ /* If we've seen pseudo-directives, make sure any emitted data or
+ frags are marked as data. */
+ if (!seen_insn)
+ {
+ sh64_update_contents_mark (TRUE);
+ sh64_set_contents_type (CRT_SH5_ISA16);
+ }
+
+ seen_insn = TRUE;
+ }
+#endif /* HAVE_SH64 */
+
+ opcode = find_cooked_opcode (&str);
+ op_end = str;
+
+ if (opcode == NULL)
+ {
+ /* The opcode is not in the hash table.
+ This means we definitely have an assembly failure,
+ but the instruction may be valid in another CPU variant.
+ In this case emit something better than 'unknown opcode'.
+ Search the full table in sh-opc.h to check. */
+
+ char *name = initial_str;
+ int name_length = 0;
+ const sh_opcode_info *op;
+ int found = 0;
+
+ /* identify opcode in string */
+ while (ISSPACE (*name))
+ {
+ name++;
+ }
+ while (!ISSPACE (name[name_length]))
+ {
+ name_length++;
+ }
+
+ /* search for opcode in full list */
+ for (op = sh_table; op->name; op++)
+ {
+ if (strncasecmp (op->name, name, name_length) == 0
+ && op->name[name_length] == '\0')
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if ( found )
+ {
+ as_bad (_("opcode not valid for this cpu variant"));
+ }
+ else
+ {
+ as_bad (_("unknown opcode"));
+ }
+ return;
+ }
+
+ if (sh_relax
+ && ! seg_info (now_seg)->tc_segment_info_data.in_code)
+ {
+ /* Output a CODE reloc to tell the linker that the following
+ bytes are instructions, not data. */
+ fix_new (frag_now, frag_now_fix (), 2, &abs_symbol, 0, 0,
+ BFD_RELOC_SH_CODE);
+ seg_info (now_seg)->tc_segment_info_data.in_code = 1;
+ }
+
+ if (opcode->nibbles[0] == PPI)
+ {
+ size = assemble_ppi (op_end, opcode);
+ }
+ else
+ {
+ if (opcode->arg[0] == A_BDISP12
+ || opcode->arg[0] == A_BDISP8)
+ {
+ /* Since we skip get_specific here, we have to check & update
+ valid_arch now. */
+ if (SH_MERGE_ARCH_SET_VALID (valid_arch, opcode->arch))
+ valid_arch = SH_MERGE_ARCH_SET (valid_arch, opcode->arch);
+ else
+ as_bad (_("Delayed branches not available on SH1"));
+ parse_exp (op_end + 1, &operand[0]);
+ build_relax (opcode, &operand[0]);
+
+ /* All branches are currently 16 bit. */
+ size = 2;
+ }
+ else
+ {
+ if (opcode->arg[0] == A_END)
+ {
+ /* Ignore trailing whitespace. If there is any, it has already
+ been compressed to a single space. */
+ if (*op_end == ' ')
+ op_end++;
+ }
+ else
+ {
+ op_end = get_operands (opcode, op_end, operand);
+ }
+ opcode = get_specific (opcode, operand);
+
+ if (opcode == 0)
+ {
+ /* Couldn't find an opcode which matched the operands. */
+ char *where = frag_more (2);
+ size = 2;
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+ as_bad (_("invalid operands for opcode"));
+ }
+ else
+ {
+ if (*op_end)
+ as_bad (_("excess operands: '%s'"), op_end);
+
+ size = build_Mytes (opcode, operand);
+ }
+ }
+ }
+
+ dwarf2_emit_insn (size);
+}
+
+/* This routine is called each time a label definition is seen. It
+ emits a BFD_RELOC_SH_LABEL reloc if necessary. */
+
+void
+sh_frob_label (symbolS *sym)
+{
+ static fragS *last_label_frag;
+ static int last_label_offset;
+
+ if (sh_relax
+ && seg_info (now_seg)->tc_segment_info_data.in_code)
+ {
+ int offset;
+
+ offset = frag_now_fix ();
+ if (frag_now != last_label_frag
+ || offset != last_label_offset)
+ {
+ fix_new (frag_now, offset, 2, &abs_symbol, 0, 0, BFD_RELOC_SH_LABEL);
+ last_label_frag = frag_now;
+ last_label_offset = offset;
+ }
+ }
+
+ dwarf2_emit_label (sym);
+}
+
+/* This routine is called when the assembler is about to output some
+ data. It emits a BFD_RELOC_SH_DATA reloc if necessary. */
+
+void
+sh_flush_pending_output (void)
+{
+ if (sh_relax
+ && seg_info (now_seg)->tc_segment_info_data.in_code)
+ {
+ fix_new (frag_now, frag_now_fix (), 2, &abs_symbol, 0, 0,
+ BFD_RELOC_SH_DATA);
+ seg_info (now_seg)->tc_segment_info_data.in_code = 0;
+ }
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Various routines to kill one day. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* Handle the .uses pseudo-op. This pseudo-op is used just before a
+ call instruction. It refers to a label of the instruction which
+ loads the register which the call uses. We use it to generate a
+ special reloc for the linker. */
+
+static void
+s_uses (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS ex;
+
+ if (! sh_relax)
+ as_warn (_(".uses pseudo-op seen when not relaxing"));
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("bad .uses format"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ fix_new_exp (frag_now, frag_now_fix (), 2, &ex, 1, BFD_RELOC_SH_USES);
+
+ demand_empty_rest_of_line ();
+}
+
+enum options
+{
+ OPTION_RELAX = OPTION_MD_BASE,
+ OPTION_BIG,
+ OPTION_LITTLE,
+ OPTION_SMALL,
+ OPTION_DSP,
+ OPTION_ISA,
+ OPTION_RENESAS,
+ OPTION_ALLOW_REG_PREFIX,
+#ifdef HAVE_SH64
+ OPTION_ABI,
+ OPTION_NO_MIX,
+ OPTION_SHCOMPACT_CONST_CRANGE,
+ OPTION_NO_EXPAND,
+ OPTION_PT32,
+#endif
+ OPTION_H_TICK_HEX,
+#ifdef OBJ_ELF
+ OPTION_FDPIC,
+#endif
+ OPTION_DUMMY /* Not used. This is just here to make it easy to add and subtract options from this enum. */
+};
+
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+ {"relax", no_argument, NULL, OPTION_RELAX},
+ {"big", no_argument, NULL, OPTION_BIG},
+ {"little", no_argument, NULL, OPTION_LITTLE},
+ /* The next two switches are here because the
+ generic parts of the linker testsuite uses them. */
+ {"EB", no_argument, NULL, OPTION_BIG},
+ {"EL", no_argument, NULL, OPTION_LITTLE},
+ {"small", no_argument, NULL, OPTION_SMALL},
+ {"dsp", no_argument, NULL, OPTION_DSP},
+ {"isa", required_argument, NULL, OPTION_ISA},
+ {"renesas", no_argument, NULL, OPTION_RENESAS},
+ {"allow-reg-prefix", no_argument, NULL, OPTION_ALLOW_REG_PREFIX},
+
+#ifdef HAVE_SH64
+ {"abi", required_argument, NULL, OPTION_ABI},
+ {"no-mix", no_argument, NULL, OPTION_NO_MIX},
+ {"shcompact-const-crange", no_argument, NULL, OPTION_SHCOMPACT_CONST_CRANGE},
+ {"no-expand", no_argument, NULL, OPTION_NO_EXPAND},
+ {"expand-pt32", no_argument, NULL, OPTION_PT32},
+#endif /* HAVE_SH64 */
+ { "h-tick-hex", no_argument, NULL, OPTION_H_TICK_HEX },
+
+#ifdef OBJ_ELF
+ {"fdpic", no_argument, NULL, OPTION_FDPIC},
+#endif
+
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_RELAX:
+ sh_relax = 1;
+ break;
+
+ case OPTION_BIG:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_LITTLE:
+ target_big_endian = 0;
+ break;
+
+ case OPTION_SMALL:
+ sh_small = 1;
+ break;
+
+ case OPTION_DSP:
+ preset_target_arch = arch_sh_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+ break;
+
+ case OPTION_RENESAS:
+ dont_adjust_reloc_32 = 1;
+ break;
+
+ case OPTION_ALLOW_REG_PREFIX:
+ allow_dollar_register_prefix = 1;
+ break;
+
+ case OPTION_ISA:
+ if (strcasecmp (arg, "dsp") == 0)
+ preset_target_arch = arch_sh_up & ~(arch_sh_sp_fpu|arch_sh_dp_fpu);
+ else if (strcasecmp (arg, "fp") == 0)
+ preset_target_arch = arch_sh_up & ~arch_sh_has_dsp;
+ else if (strcasecmp (arg, "any") == 0)
+ preset_target_arch = arch_sh_up;
+#ifdef HAVE_SH64
+ else if (strcasecmp (arg, "shmedia") == 0)
+ {
+ if (sh64_isa_mode == sh64_isa_shcompact)
+ as_bad (_("Invalid combination: --isa=SHcompact with --isa=SHmedia"));
+ sh64_isa_mode = sh64_isa_shmedia;
+ }
+ else if (strcasecmp (arg, "shcompact") == 0)
+ {
+ if (sh64_isa_mode == sh64_isa_shmedia)
+ as_bad (_("Invalid combination: --isa=SHmedia with --isa=SHcompact"));
+ if (sh64_abi == sh64_abi_64)
+ as_bad (_("Invalid combination: --abi=64 with --isa=SHcompact"));
+ sh64_isa_mode = sh64_isa_shcompact;
+ }
+#endif /* HAVE_SH64 */
+ else
+ {
+ extern const bfd_arch_info_type bfd_sh_arch;
+ bfd_arch_info_type const *bfd_arch = &bfd_sh_arch;
+
+ preset_target_arch = 0;
+ for (; bfd_arch; bfd_arch=bfd_arch->next)
+ {
+ int len = strlen(bfd_arch->printable_name);
+
+ if (bfd_arch->mach == bfd_mach_sh5)
+ continue;
+
+ if (strncasecmp (bfd_arch->printable_name, arg, len) != 0)
+ continue;
+
+ if (arg[len] == '\0')
+ preset_target_arch =
+ sh_get_arch_from_bfd_mach (bfd_arch->mach);
+ else if (strcasecmp(&arg[len], "-up") == 0)
+ preset_target_arch =
+ sh_get_arch_up_from_bfd_mach (bfd_arch->mach);
+ else
+ continue;
+ break;
+ }
+
+ if (!preset_target_arch)
+ as_bad (_("Invalid argument to --isa option: %s"), arg);
+ }
+ break;
+
+#ifdef HAVE_SH64
+ case OPTION_ABI:
+ if (strcmp (arg, "32") == 0)
+ {
+ if (sh64_abi == sh64_abi_64)
+ as_bad (_("Invalid combination: --abi=32 with --abi=64"));
+ sh64_abi = sh64_abi_32;
+ }
+ else if (strcmp (arg, "64") == 0)
+ {
+ if (sh64_abi == sh64_abi_32)
+ as_bad (_("Invalid combination: --abi=64 with --abi=32"));
+ if (sh64_isa_mode == sh64_isa_shcompact)
+ as_bad (_("Invalid combination: --isa=SHcompact with --abi=64"));
+ sh64_abi = sh64_abi_64;
+ }
+ else
+ as_bad (_("Invalid argument to --abi option: %s"), arg);
+ break;
+
+ case OPTION_NO_MIX:
+ sh64_mix = FALSE;
+ break;
+
+ case OPTION_SHCOMPACT_CONST_CRANGE:
+ sh64_shcompact_const_crange = TRUE;
+ break;
+
+ case OPTION_NO_EXPAND:
+ sh64_expand = FALSE;
+ break;
+
+ case OPTION_PT32:
+ sh64_pt32 = TRUE;
+ break;
+#endif /* HAVE_SH64 */
+
+ case OPTION_H_TICK_HEX:
+ enable_h_tick_hex = 1;
+ break;
+
+#ifdef OBJ_ELF
+ case OPTION_FDPIC:
+ sh_fdpic = TRUE;
+ break;
+#endif /* OBJ_ELF */
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+SH options:\n\
+--little generate little endian code\n\
+--big generate big endian code\n\
+--relax alter jump instructions for long displacements\n\
+--renesas disable optimization with section symbol for\n\
+ compatibility with Renesas assembler.\n\
+--small align sections to 4 byte boundaries, not 16\n\
+--dsp enable sh-dsp insns, and disable floating-point ISAs.\n\
+--allow-reg-prefix allow '$' as a register name prefix.\n\
+--isa=[any use most appropriate isa\n\
+ | dsp same as '-dsp'\n\
+ | fp"));
+ {
+ extern const bfd_arch_info_type bfd_sh_arch;
+ bfd_arch_info_type const *bfd_arch = &bfd_sh_arch;
+
+ for (; bfd_arch; bfd_arch=bfd_arch->next)
+ if (bfd_arch->mach != bfd_mach_sh5)
+ {
+ fprintf (stream, "\n | %s", bfd_arch->printable_name);
+ fprintf (stream, "\n | %s-up", bfd_arch->printable_name);
+ }
+ }
+ fprintf (stream, "]\n");
+#ifdef HAVE_SH64
+ fprintf (stream, _("\
+--isa=[shmedia set as the default instruction set for SH64\n\
+ | SHmedia\n\
+ | shcompact\n\
+ | SHcompact]\n"));
+ fprintf (stream, _("\
+--abi=[32|64] set size of expanded SHmedia operands and object\n\
+ file type\n\
+--shcompact-const-crange emit code-range descriptors for constants in\n\
+ SHcompact code sections\n\
+--no-mix disallow SHmedia code in the same section as\n\
+ constants and SHcompact code\n\
+--no-expand do not expand MOVI, PT, PTA or PTB instructions\n\
+--expand-pt32 with -abi=64, expand PT, PTA and PTB instructions\n\
+ to 32 bits only\n"));
+#endif /* HAVE_SH64 */
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+--fdpic generate an FDPIC object file\n"));
+#endif /* OBJ_ELF */
+}
+
+/* This struct is used to pass arguments to sh_count_relocs through
+ bfd_map_over_sections. */
+
+struct sh_count_relocs
+{
+ /* Symbol we are looking for. */
+ symbolS *sym;
+ /* Count of relocs found. */
+ int count;
+};
+
+/* Count the number of fixups in a section which refer to a particular
+ symbol. This is called via bfd_map_over_sections. */
+
+static void
+sh_count_relocs (bfd *abfd ATTRIBUTE_UNUSED, segT sec, void *data)
+{
+ struct sh_count_relocs *info = (struct sh_count_relocs *) data;
+ segment_info_type *seginfo;
+ symbolS *sym;
+ fixS *fix;
+
+ seginfo = seg_info (sec);
+ if (seginfo == NULL)
+ return;
+
+ sym = info->sym;
+ for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+ {
+ if (fix->fx_addsy == sym)
+ {
+ ++info->count;
+ fix->fx_tcbit = 1;
+ }
+ }
+}
+
+/* Handle the count relocs for a particular section.
+ This is called via bfd_map_over_sections. */
+
+static void
+sh_frob_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec,
+ void *ignore ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo;
+ fixS *fix;
+
+ seginfo = seg_info (sec);
+ if (seginfo == NULL)
+ return;
+
+ for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+ {
+ symbolS *sym;
+
+ sym = fix->fx_addsy;
+ /* Check for a local_symbol. */
+ if (sym && sym->bsym == NULL)
+ {
+ struct local_symbol *ls = (struct local_symbol *)sym;
+ /* See if it's been converted. If so, canonicalize. */
+ if (local_symbol_converted_p (ls))
+ fix->fx_addsy = local_symbol_get_real_symbol (ls);
+ }
+ }
+
+ for (fix = seginfo->fix_root; fix != NULL; fix = fix->fx_next)
+ {
+ symbolS *sym;
+ bfd_vma val;
+ fixS *fscan;
+ struct sh_count_relocs info;
+
+ if (fix->fx_r_type != BFD_RELOC_SH_USES)
+ continue;
+
+ /* The BFD_RELOC_SH_USES reloc should refer to a defined local
+ symbol in the same section. */
+ sym = fix->fx_addsy;
+ if (sym == NULL
+ || fix->fx_subsy != NULL
+ || fix->fx_addnumber != 0
+ || S_GET_SEGMENT (sym) != sec
+ || S_IS_EXTERNAL (sym))
+ {
+ as_warn_where (fix->fx_file, fix->fx_line,
+ _(".uses does not refer to a local symbol in the same section"));
+ continue;
+ }
+
+ /* Look through the fixups again, this time looking for one
+ at the same location as sym. */
+ val = S_GET_VALUE (sym);
+ for (fscan = seginfo->fix_root;
+ fscan != NULL;
+ fscan = fscan->fx_next)
+ if (val == fscan->fx_frag->fr_address + fscan->fx_where
+ && fscan->fx_r_type != BFD_RELOC_SH_ALIGN
+ && fscan->fx_r_type != BFD_RELOC_SH_CODE
+ && fscan->fx_r_type != BFD_RELOC_SH_DATA
+ && fscan->fx_r_type != BFD_RELOC_SH_LABEL)
+ break;
+ if (fscan == NULL)
+ {
+ as_warn_where (fix->fx_file, fix->fx_line,
+ _("can't find fixup pointed to by .uses"));
+ continue;
+ }
+
+ if (fscan->fx_tcbit)
+ {
+ /* We've already done this one. */
+ continue;
+ }
+
+ /* The variable fscan should also be a fixup to a local symbol
+ in the same section. */
+ sym = fscan->fx_addsy;
+ if (sym == NULL
+ || fscan->fx_subsy != NULL
+ || fscan->fx_addnumber != 0
+ || S_GET_SEGMENT (sym) != sec
+ || S_IS_EXTERNAL (sym))
+ {
+ as_warn_where (fix->fx_file, fix->fx_line,
+ _(".uses target does not refer to a local symbol in the same section"));
+ continue;
+ }
+
+ /* Now we look through all the fixups of all the sections,
+ counting the number of times we find a reference to sym. */
+ info.sym = sym;
+ info.count = 0;
+ bfd_map_over_sections (stdoutput, sh_count_relocs, &info);
+
+ if (info.count < 1)
+ abort ();
+
+ /* Generate a BFD_RELOC_SH_COUNT fixup at the location of sym.
+ We have already adjusted the value of sym to include the
+ fragment address, so we undo that adjustment here. */
+ subseg_change (sec, 0);
+ fix_new (fscan->fx_frag,
+ S_GET_VALUE (sym) - fscan->fx_frag->fr_address,
+ 4, &abs_symbol, info.count, 0, BFD_RELOC_SH_COUNT);
+ }
+}
+
+/* This function is called after the symbol table has been completed,
+ but before the relocs or section contents have been written out.
+ If we have seen any .uses pseudo-ops, they point to an instruction
+ which loads a register with the address of a function. We look
+ through the fixups to find where the function address is being
+ loaded from. We then generate a COUNT reloc giving the number of
+ times that function address is referred to. The linker uses this
+ information when doing relaxing, to decide when it can eliminate
+ the stored function address entirely. */
+
+void
+sh_frob_file (void)
+{
+#ifdef HAVE_SH64
+ shmedia_frob_file_before_adjust ();
+#endif
+
+ if (! sh_relax)
+ return;
+
+ bfd_map_over_sections (stdoutput, sh_frob_section, NULL);
+}
+
+/* Called after relaxing. Set the correct sizes of the fragments, and
+ create relocs so that md_apply_fix will fill in the correct values. */
+
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED, segT seg, fragS *fragP)
+{
+ int donerelax = 0;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (COND_JUMP, COND8):
+ case C (COND_JUMP_DELAY, COND8):
+ subseg_change (seg, 0);
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+ 1, BFD_RELOC_SH_PCDISP8BY2);
+ fragP->fr_fix += 2;
+ fragP->fr_var = 0;
+ break;
+
+ case C (UNCOND_JUMP, UNCOND12):
+ subseg_change (seg, 0);
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+ 1, BFD_RELOC_SH_PCDISP12BY2);
+ fragP->fr_fix += 2;
+ fragP->fr_var = 0;
+ break;
+
+ case C (UNCOND_JUMP, UNCOND32):
+ case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+ if (fragP->fr_symbol == NULL)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("displacement overflows 12-bit field"));
+ else if (S_IS_DEFINED (fragP->fr_symbol))
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("displacement to defined symbol %s overflows 12-bit field"),
+ S_GET_NAME (fragP->fr_symbol));
+ else
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("displacement to undefined symbol %s overflows 12-bit field"),
+ S_GET_NAME (fragP->fr_symbol));
+ /* Stabilize this frag, so we don't trip an assert. */
+ fragP->fr_fix += fragP->fr_var;
+ fragP->fr_var = 0;
+ break;
+
+ case C (COND_JUMP, COND12):
+ case C (COND_JUMP_DELAY, COND12):
+ /* A bcond won't fit, so turn it into a b!cond; bra disp; nop. */
+ /* I found that a relax failure for gcc.c-torture/execute/930628-1.c
+ was due to gas incorrectly relaxing an out-of-range conditional
+ branch with delay slot. It turned:
+ bf.s L6 (slot mov.l r12,@(44,r0))
+ into:
+
+2c: 8f 01 a0 8b bf.s 32 <_main+32> (slot bra L6)
+30: 00 09 nop
+32: 10 cb mov.l r12,@(44,r0)
+ Therefore, branches with delay slots have to be handled
+ differently from ones without delay slots. */
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+ int highbyte = target_big_endian ? 0 : 1;
+ int lowbyte = target_big_endian ? 1 : 0;
+ int delay = fragP->fr_subtype == C (COND_JUMP_DELAY, COND12);
+
+ /* Toggle the true/false bit of the bcond. */
+ buffer[highbyte] ^= 0x2;
+
+ /* If this is a delayed branch, we may not put the bra in the
+ slot. So we change it to a non-delayed branch, like that:
+ b! cond slot_label; bra disp; slot_label: slot_insn
+ ??? We should try if swapping the conditional branch and
+ its delay-slot insn already makes the branch reach. */
+
+ /* Build a relocation to six / four bytes farther on. */
+ subseg_change (seg, 0);
+ fix_new (fragP, fragP->fr_fix, 2, section_symbol (seg),
+ fragP->fr_address + fragP->fr_fix + (delay ? 4 : 6),
+ 1, BFD_RELOC_SH_PCDISP8BY2);
+
+ /* Set up a jump instruction. */
+ buffer[highbyte + 2] = 0xa0;
+ buffer[lowbyte + 2] = 0;
+ fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_SH_PCDISP12BY2);
+
+ if (delay)
+ {
+ buffer[highbyte] &= ~0x4; /* Removes delay slot from branch. */
+ fragP->fr_fix += 4;
+ }
+ else
+ {
+ /* Fill in a NOP instruction. */
+ buffer[highbyte + 4] = 0x0;
+ buffer[lowbyte + 4] = 0x9;
+
+ fragP->fr_fix += 6;
+ }
+ fragP->fr_var = 0;
+ donerelax = 1;
+ }
+ break;
+
+ case C (COND_JUMP, COND32):
+ case C (COND_JUMP_DELAY, COND32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+ if (fragP->fr_symbol == NULL)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("displacement overflows 8-bit field"));
+ else if (S_IS_DEFINED (fragP->fr_symbol))
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("displacement to defined symbol %s overflows 8-bit field"),
+ S_GET_NAME (fragP->fr_symbol));
+ else
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("displacement to undefined symbol %s overflows 8-bit field "),
+ S_GET_NAME (fragP->fr_symbol));
+ /* Stabilize this frag, so we don't trip an assert. */
+ fragP->fr_fix += fragP->fr_var;
+ fragP->fr_var = 0;
+ break;
+
+ default:
+#ifdef HAVE_SH64
+ shmedia_md_convert_frag (headers, seg, fragP, TRUE);
+#else
+ abort ();
+#endif
+ }
+
+ if (donerelax && !sh_relax)
+ as_warn_where (fragP->fr_file, fragP->fr_line,
+ _("overflow in branch to %s; converted into longer instruction sequence"),
+ (fragP->fr_symbol != NULL
+ ? S_GET_NAME (fragP->fr_symbol)
+ : ""));
+}
+
+valueT
+md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
+{
+#ifdef OBJ_ELF
+ return size;
+#else /* ! OBJ_ELF */
+ return ((size + (1 << bfd_get_section_alignment (stdoutput, seg)) - 1)
+ & (-1 << bfd_get_section_alignment (stdoutput, seg)));
+#endif /* ! OBJ_ELF */
+}
+
+/* This static variable is set by s_uacons to tell sh_cons_align that
+ the expression does not need to be aligned. */
+
+static int sh_no_align_cons = 0;
+
+/* This handles the unaligned space allocation pseudo-ops, such as
+ .uaword. .uaword is just like .word, but the value does not need
+ to be aligned. */
+
+static void
+s_uacons (int bytes)
+{
+ /* Tell sh_cons_align not to align this value. */
+ sh_no_align_cons = 1;
+ cons (bytes);
+}
+
+/* If a .word, et. al., pseud-op is seen, warn if the value is not
+ aligned correctly. Note that this can cause warnings to be issued
+ when assembling initialized structured which were declared with the
+ packed attribute. FIXME: Perhaps we should require an option to
+ enable this warning? */
+
+void
+sh_cons_align (int nbytes)
+{
+ int nalign;
+
+ if (sh_no_align_cons)
+ {
+ /* This is an unaligned pseudo-op. */
+ sh_no_align_cons = 0;
+ return;
+ }
+
+ nalign = 0;
+ while ((nbytes & 1) == 0)
+ {
+ ++nalign;
+ nbytes >>= 1;
+ }
+
+ if (nalign == 0)
+ return;
+
+ if (now_seg == absolute_section)
+ {
+ if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+ as_warn (_("misaligned data"));
+ return;
+ }
+
+ frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+ record_alignment (now_seg, nalign);
+}
+
+/* When relaxing, we need to output a reloc for any .align directive
+ that requests alignment to a four byte boundary or larger. This is
+ also where we check for misaligned data. */
+
+void
+sh_handle_align (fragS *frag)
+{
+ int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
+
+ if (frag->fr_type == rs_align_code)
+ {
+ static const unsigned char big_nop_pattern[] = { 0x00, 0x09 };
+ static const unsigned char little_nop_pattern[] = { 0x09, 0x00 };
+
+ char *p = frag->fr_literal + frag->fr_fix;
+
+ if (bytes & 1)
+ {
+ *p++ = 0;
+ bytes--;
+ frag->fr_fix += 1;
+ }
+
+ if (target_big_endian)
+ {
+ memcpy (p, big_nop_pattern, sizeof big_nop_pattern);
+ frag->fr_var = sizeof big_nop_pattern;
+ }
+ else
+ {
+ memcpy (p, little_nop_pattern, sizeof little_nop_pattern);
+ frag->fr_var = sizeof little_nop_pattern;
+ }
+ }
+ else if (frag->fr_type == rs_align_test)
+ {
+ if (bytes != 0)
+ as_bad_where (frag->fr_file, frag->fr_line, _("misaligned data"));
+ }
+
+ if (sh_relax
+ && (frag->fr_type == rs_align
+ || frag->fr_type == rs_align_code)
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 1
+ && now_seg != bss_section)
+ fix_new (frag, frag->fr_fix, 2, &abs_symbol, frag->fr_offset, 0,
+ BFD_RELOC_SH_ALIGN);
+}
+
+/* See whether the relocation should be resolved locally. */
+
+static bfd_boolean
+sh_local_pcrel (fixS *fix)
+{
+ return (! sh_relax
+ && (fix->fx_r_type == BFD_RELOC_SH_PCDISP8BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCDISP12BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY2
+ || fix->fx_r_type == BFD_RELOC_SH_PCRELIMM8BY4
+ || fix->fx_r_type == BFD_RELOC_8_PCREL
+ || fix->fx_r_type == BFD_RELOC_SH_SWITCH16
+ || fix->fx_r_type == BFD_RELOC_SH_SWITCH32));
+}
+
+/* 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
+sh_force_relocation (fixS *fix)
+{
+ /* These relocations can't make it into a DSO, so no use forcing
+ them for global symbols. */
+ if (sh_local_pcrel (fix))
+ return 0;
+
+ /* Make sure some relocations get emitted. */
+ if (fix->fx_r_type == BFD_RELOC_SH_LOOP_START
+ || fix->fx_r_type == BFD_RELOC_SH_LOOP_END
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_GD_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LD_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_IE_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LDO_32
+ || fix->fx_r_type == BFD_RELOC_SH_TLS_LE_32
+ || generic_force_reloc (fix))
+ return 1;
+
+ if (! sh_relax)
+ return 0;
+
+ return (fix->fx_pcrel
+ || SWITCH_TABLE (fix)
+ || fix->fx_r_type == BFD_RELOC_SH_COUNT
+ || fix->fx_r_type == BFD_RELOC_SH_ALIGN
+ || fix->fx_r_type == BFD_RELOC_SH_CODE
+ || fix->fx_r_type == BFD_RELOC_SH_DATA
+#ifdef HAVE_SH64
+ || fix->fx_r_type == BFD_RELOC_SH_SHMEDIA_CODE
+#endif
+ || fix->fx_r_type == BFD_RELOC_SH_LABEL);
+}
+
+#ifdef OBJ_ELF
+bfd_boolean
+sh_fix_adjustable (fixS *fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_32_PLT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_32_GOT_PCREL
+ || fixP->fx_r_type == BFD_RELOC_SH_GOT20
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTPC
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTFUNCDESC20
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC
+ || fixP->fx_r_type == BFD_RELOC_SH_GOTOFFFUNCDESC20
+ || fixP->fx_r_type == BFD_RELOC_SH_FUNCDESC
+ || ((fixP->fx_r_type == BFD_RELOC_32) && dont_adjust_reloc_32)
+ || fixP->fx_r_type == BFD_RELOC_RVA)
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+void
+sh_elf_final_processing (void)
+{
+ int val;
+
+ /* Set file-specific flags to indicate if this code needs
+ a processor with the sh-dsp / sh2e ISA to execute. */
+#ifdef HAVE_SH64
+ /* SH5 and above don't know about the valid_arch arch_sh* bits defined
+ in sh-opc.h, so check SH64 mode before checking valid_arch. */
+ if (sh64_isa_mode != sh64_isa_unspecified)
+ val = EF_SH5;
+ else
+#elif defined TARGET_SYMBIAN
+ if (1)
+ {
+ extern int sh_symbian_find_elf_flags (unsigned int);
+
+ val = sh_symbian_find_elf_flags (valid_arch);
+ }
+ else
+#endif /* HAVE_SH64 */
+ val = sh_find_elf_flags (valid_arch);
+
+ elf_elfheader (stdoutput)->e_flags &= ~EF_SH_MACH_MASK;
+ elf_elfheader (stdoutput)->e_flags |= val;
+
+ if (sh_fdpic)
+ elf_elfheader (stdoutput)->e_flags |= EF_SH_FDPIC;
+}
+#endif
+
+#ifdef TE_UCLINUX
+/* Return the target format for uClinux. */
+
+const char *
+sh_uclinux_target_format (void)
+{
+ if (sh_fdpic)
+ return (!target_big_endian ? "elf32-sh-fdpic" : "elf32-shbig-fdpic");
+ else
+ return (!target_big_endian ? "elf32-shl" : "elf32-sh");
+}
+#endif
+
+/* Apply fixup FIXP to SIZE-byte field BUF given that VAL is its
+ assembly-time value. If we're generating a reloc for FIXP,
+ see whether the addend should be stored in-place or whether
+ it should be in an ELF r_addend field. */
+
+static void
+apply_full_field_fix (fixS *fixP, char *buf, bfd_vma val, int size)
+{
+ reloc_howto_type *howto;
+
+ if (fixP->fx_addsy != NULL || fixP->fx_pcrel)
+ {
+ howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (howto && !howto->partial_inplace)
+ {
+ fixP->fx_addnumber = val;
+ return;
+ }
+ }
+ md_number_to_chars (buf, val, size);
+}
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ int lowbyte = target_big_endian ? 1 : 0;
+ int highbyte = target_big_endian ? 0 : 1;
+ long val = (long) *valP;
+ long max, min;
+ int shift;
+
+ /* A difference between two symbols, the second of which is in the
+ current section, is transformed in a PC-relative relocation to
+ the other symbol. We have to adjust the relocation type here. */
+ if (fixP->fx_pcrel)
+ {
+#ifndef HAVE_SH64
+ /* Safeguard; this must not occur for non-sh64 configurations. */
+ gas_assert (fixP->fx_r_type != BFD_RELOC_64);
+#endif
+
+ switch (fixP->fx_r_type)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+
+ /* Currently, we only support 32-bit PCREL relocations.
+ We'd need a new reloc type to handle 16_PCREL, and
+ 8_PCREL is already taken for R_SH_SWITCH8, which
+ apparently does something completely different than what
+ we need. FIXME. */
+ case BFD_RELOC_16:
+ bfd_set_error (bfd_error_bad_value);
+ return;
+
+ case BFD_RELOC_8:
+ bfd_set_error (bfd_error_bad_value);
+ return;
+ }
+ }
+
+ /* The function adjust_reloc_syms won't convert a reloc against a weak
+ symbol into a reloc against a section, but bfd_install_relocation
+ will screw up if the symbol is defined, so we have to adjust val here
+ to avoid the screw up later.
+
+ For ordinary relocs, this does not happen for ELF, since for ELF,
+ bfd_install_relocation uses the "special function" field of the
+ howto, and does not execute the code that needs to be undone, as long
+ as the special function does not return bfd_reloc_continue.
+ It can happen for GOT- and PLT-type relocs the way they are
+ described in elf32-sh.c as they use bfd_elf_generic_reloc, but it
+ doesn't matter here since those relocs don't use VAL; see below. */
+ if (OUTPUT_FLAVOR != bfd_target_elf_flavour
+ && fixP->fx_addsy != NULL
+ && S_IS_WEAK (fixP->fx_addsy))
+ val -= S_GET_VALUE (fixP->fx_addsy);
+
+ if (SWITCH_TABLE (fixP))
+ val -= S_GET_VALUE (fixP->fx_subsy);
+
+ max = min = 0;
+ shift = 0;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_SH_IMM3:
+ max = 0x7;
+ * buf = (* buf & 0xf8) | (val & 0x7);
+ break;
+ case BFD_RELOC_SH_IMM3U:
+ max = 0x7;
+ * buf = (* buf & 0x8f) | ((val & 0x7) << 4);
+ break;
+ case BFD_RELOC_SH_DISP12:
+ max = 0xfff;
+ buf[lowbyte] = val & 0xff;
+ buf[highbyte] |= (val >> 8) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP12BY2:
+ max = 0xfff;
+ shift = 1;
+ buf[lowbyte] = (val >> 1) & 0xff;
+ buf[highbyte] |= (val >> 9) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP12BY4:
+ max = 0xfff;
+ shift = 2;
+ buf[lowbyte] = (val >> 2) & 0xff;
+ buf[highbyte] |= (val >> 10) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP12BY8:
+ max = 0xfff;
+ shift = 3;
+ buf[lowbyte] = (val >> 3) & 0xff;
+ buf[highbyte] |= (val >> 11) & 0x0f;
+ break;
+ case BFD_RELOC_SH_DISP20:
+ if (! target_big_endian)
+ abort();
+ max = 0x7ffff;
+ min = -0x80000;
+ buf[1] = (buf[1] & 0x0f) | ((val >> 12) & 0xf0);
+ buf[2] = (val >> 8) & 0xff;
+ buf[3] = val & 0xff;
+ break;
+ case BFD_RELOC_SH_DISP20BY8:
+ if (!target_big_endian)
+ abort();
+ max = 0x7ffff;
+ min = -0x80000;
+ shift = 8;
+ buf[1] = (buf[1] & 0x0f) | ((val >> 20) & 0xf0);
+ buf[2] = (val >> 16) & 0xff;
+ buf[3] = (val >> 8) & 0xff;
+ break;
+
+ case BFD_RELOC_SH_IMM4:
+ max = 0xf;
+ *buf = (*buf & 0xf0) | (val & 0xf);
+ break;
+
+ case BFD_RELOC_SH_IMM4BY2:
+ max = 0xf;
+ shift = 1;
+ *buf = (*buf & 0xf0) | ((val >> 1) & 0xf);
+ break;
+
+ case BFD_RELOC_SH_IMM4BY4:
+ max = 0xf;
+ shift = 2;
+ *buf = (*buf & 0xf0) | ((val >> 2) & 0xf);
+ break;
+
+ case BFD_RELOC_SH_IMM8BY2:
+ max = 0xff;
+ shift = 1;
+ *buf = val >> 1;
+ break;
+
+ case BFD_RELOC_SH_IMM8BY4:
+ max = 0xff;
+ shift = 2;
+ *buf = val >> 2;
+ break;
+
+ case BFD_RELOC_8:
+ case BFD_RELOC_SH_IMM8:
+ /* Sometimes the 8 bit value is sign extended (e.g., add) and
+ sometimes it is not (e.g., and). We permit any 8 bit value.
+ Note that adding further restrictions may invalidate
+ reasonable looking assembly code, such as ``and -0x1,r0''. */
+ max = 0xff;
+ min = -0xff;
+ *buf++ = val;
+ break;
+
+ case BFD_RELOC_SH_PCRELIMM8BY4:
+ /* If we are dealing with a known destination ... */
+ if ((fixP->fx_addsy == NULL || S_IS_DEFINED (fixP->fx_addsy))
+ && (fixP->fx_subsy == NULL || S_IS_DEFINED (fixP->fx_addsy)))
+ {
+ /* Don't silently move the destination due to misalignment.
+ The absolute address is the fragment base plus the offset into
+ the fragment plus the pc relative offset to the label. */
+ if ((fixP->fx_frag->fr_address + fixP->fx_where + val) & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset to unaligned destination"));
+
+ /* The displacement cannot be zero or backward even if aligned.
+ Allow -2 because val has already been adjusted somewhere. */
+ if (val < -2)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("negative offset"));
+ }
+
+ /* The lower two bits of the PC are cleared before the
+ displacement is added in. We can assume that the destination
+ is on a 4 byte boundary. If this instruction is also on a 4
+ byte boundary, then we want
+ (target - here) / 4
+ and target - here is a multiple of 4.
+ Otherwise, we are on a 2 byte boundary, and we want
+ (target - (here - 2)) / 4
+ and target - here is not a multiple of 4. Computing
+ (target - (here - 2)) / 4 == (target - here + 2) / 4
+ works for both cases, since in the first case the addition of
+ 2 will be removed by the division. target - here is in the
+ variable val. */
+ val = (val + 2) / 4;
+ if (val & ~0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val;
+ break;
+
+ case BFD_RELOC_SH_PCRELIMM8BY2:
+ val /= 2;
+ if (val & ~0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val;
+ break;
+
+ case BFD_RELOC_SH_PCDISP8BY2:
+ val /= 2;
+ if (val < -0x80 || val > 0x7f)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val;
+ break;
+
+ case BFD_RELOC_SH_PCDISP12BY2:
+ val /= 2;
+ if (val < -0x800 || val > 0x7ff)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("pcrel too far"));
+ buf[lowbyte] = val & 0xff;
+ buf[highbyte] |= (val >> 8) & 0xf;
+ break;
+
+#ifndef HAVE_SH64
+ case BFD_RELOC_64:
+ apply_full_field_fix (fixP, buf, *valP, 8);
+ break;
+#endif
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ apply_full_field_fix (fixP, buf, val, 4);
+ break;
+
+ case BFD_RELOC_16:
+ apply_full_field_fix (fixP, buf, val, 2);
+ break;
+
+ case BFD_RELOC_SH_USES:
+ /* Pass the value into sh_reloc(). */
+ fixP->fx_addnumber = val;
+ break;
+
+ case BFD_RELOC_SH_COUNT:
+ case BFD_RELOC_SH_ALIGN:
+ case BFD_RELOC_SH_CODE:
+ case BFD_RELOC_SH_DATA:
+ case BFD_RELOC_SH_LABEL:
+ /* Nothing to do here. */
+ break;
+
+ case BFD_RELOC_SH_LOOP_START:
+ case BFD_RELOC_SH_LOOP_END:
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ return;
+
+#ifdef OBJ_ELF
+ case BFD_RELOC_32_PLT_PCREL:
+ /* Make the jump instruction point to the address of the operand. At
+ runtime we merely add the offset to the actual PLT entry. */
+ * valP = 0xfffffffc;
+ val = fixP->fx_offset;
+ if (fixP->fx_subsy)
+ val -= S_GET_VALUE (fixP->fx_subsy);
+ apply_full_field_fix (fixP, buf, val, 4);
+ break;
+
+ case BFD_RELOC_SH_GOTPC:
+ /* This is tough to explain. We end up with this one if we have
+ operands that look like "_GLOBAL_OFFSET_TABLE_+[.-.L284]".
+ The goal here is to obtain the absolute address of the GOT,
+ and it is strongly preferable from a performance point of
+ view to avoid using a runtime relocation for this. There are
+ cases where you have something like:
+
+ .long _GLOBAL_OFFSET_TABLE_+[.-.L66]
+
+ and here no correction would be required. Internally in the
+ assembler we treat operands of this form as not being pcrel
+ since the '.' is explicitly mentioned, and I wonder whether
+ it would simplify matters to do it this way. Who knows. In
+ earlier versions of the PIC patches, the pcrel_adjust field
+ was used to store the correction, but since the expression is
+ not pcrel, I felt it would be confusing to do it this way. */
+ * valP -= 1;
+ apply_full_field_fix (fixP, buf, val, 4);
+ break;
+
+ case BFD_RELOC_SH_TLS_GD_32:
+ case BFD_RELOC_SH_TLS_LD_32:
+ case BFD_RELOC_SH_TLS_IE_32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fallthrough */
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_SH_GOT20:
+ case BFD_RELOC_SH_GOTPLT32:
+ case BFD_RELOC_SH_GOTFUNCDESC:
+ case BFD_RELOC_SH_GOTFUNCDESC20:
+ case BFD_RELOC_SH_GOTOFFFUNCDESC:
+ case BFD_RELOC_SH_GOTOFFFUNCDESC20:
+ case BFD_RELOC_SH_FUNCDESC:
+ * valP = 0; /* Fully resolved at runtime. No addend. */
+ apply_full_field_fix (fixP, buf, 0, 4);
+ break;
+
+ case BFD_RELOC_SH_TLS_LDO_32:
+ case BFD_RELOC_SH_TLS_LE_32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* Fallthrough */
+ case BFD_RELOC_32_GOTOFF:
+ case BFD_RELOC_SH_GOTOFF20:
+ apply_full_field_fix (fixP, buf, val, 4);
+ break;
+#endif
+
+ default:
+#ifdef HAVE_SH64
+ shmedia_md_apply_fix (fixP, valP);
+ return;
+#else
+ abort ();
+#endif
+ }
+
+ if (shift != 0)
+ {
+ if ((val & ((1 << shift) - 1)) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("misaligned offset"));
+ if (val >= 0)
+ val >>= shift;
+ else
+ val = ((val >> shift)
+ | ((long) -1 & ~ ((long) -1 >> shift)));
+ }
+
+ /* Extend sign for 64-bit host. */
+ val = ((val & 0xffffffff) ^ 0x80000000) - 0x80000000;
+ if (max != 0 && (val < min || val > max))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("offset out of range"));
+ else if (max != 0)
+ /* Stop the generic code from trying to overlow check the value as well.
+ It may not have the correct value anyway, as we do not store val back
+ into *valP. */
+ fixP->fx_no_overflow = 1;
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+/* Called just before address relaxation. Return the length
+ by which a fragment must grow to reach it's destination. */
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment_type)
+{
+ int what;
+
+ switch (fragP->fr_subtype)
+ {
+ default:
+#ifdef HAVE_SH64
+ return shmedia_md_estimate_size_before_relax (fragP, segment_type);
+#else
+ abort ();
+#endif
+
+
+ case C (UNCOND_JUMP, UNDEF_DISP):
+ /* Used to be a branch to somewhere which was unknown. */
+ if (!fragP->fr_symbol)
+ {
+ fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
+ }
+ else if (S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ fragP->fr_subtype = C (UNCOND_JUMP, UNCOND12);
+ }
+ else
+ {
+ fragP->fr_subtype = C (UNCOND_JUMP, UNDEF_WORD_DISP);
+ }
+ break;
+
+ case C (COND_JUMP, UNDEF_DISP):
+ case C (COND_JUMP_DELAY, UNDEF_DISP):
+ what = GET_WHAT (fragP->fr_subtype);
+ /* Used to be a branch to somewhere which was unknown. */
+ if (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment_type)
+ {
+ /* Got a symbol and it's defined in this segment, become byte
+ sized - maybe it will fix up. */
+ fragP->fr_subtype = C (what, COND8);
+ }
+ else if (fragP->fr_symbol)
+ {
+ /* Its got a segment, but its not ours, so it will always be long. */
+ fragP->fr_subtype = C (what, UNDEF_WORD_DISP);
+ }
+ else
+ {
+ /* We know the abs value. */
+ fragP->fr_subtype = C (what, COND8);
+ }
+ break;
+
+ case C (UNCOND_JUMP, UNCOND12):
+ case C (UNCOND_JUMP, UNCOND32):
+ case C (UNCOND_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP, COND8):
+ case C (COND_JUMP, COND12):
+ case C (COND_JUMP, COND32):
+ case C (COND_JUMP, UNDEF_WORD_DISP):
+ case C (COND_JUMP_DELAY, COND8):
+ case C (COND_JUMP_DELAY, COND12):
+ case C (COND_JUMP_DELAY, COND32):
+ case C (COND_JUMP_DELAY, UNDEF_WORD_DISP):
+ /* When relaxing a section for the second time, we don't need to
+ do anything besides return the current size. */
+ break;
+ }
+
+ fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+ return fragP->fr_var;
+}
+
+/* Put number into target byte order. */
+
+void
+md_number_to_chars (char *ptr, valueT use, int nbytes)
+{
+#ifdef HAVE_SH64
+ /* We might need to set the contents type to data. */
+ sh64_flag_output ();
+#endif
+
+ if (! target_big_endian)
+ number_to_chars_littleendian (ptr, use, nbytes);
+ else
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+
+/* This version is used in obj-coff.c eg. for the sh-hms target. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address + 2;
+}
+
+long
+md_pcrel_from_section (fixS *fixP, segT sec)
+{
+ if (! sh_local_pcrel (fixP)
+ && fixP->fx_addsy != (symbolS *) NULL
+ && (generic_force_reloc (fixP)
+ || S_GET_SEGMENT (fixP->fx_addsy) != sec))
+ {
+ /* The symbol is undefined (or is defined but not in this section,
+ or we're not sure about it being the final definition). Let the
+ linker figure it out. We need to adjust the subtraction of a
+ symbol to the position of the relocated data, though. */
+ return fixP->fx_subsy ? fixP->fx_where + fixP->fx_frag->fr_address : 0;
+ }
+
+ return md_pcrel_from (fixP);
+}
+
+/* Create a reloc. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type r_type;
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ r_type = fixp->fx_r_type;
+
+ if (SWITCH_TABLE (fixp))
+ {
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ rel->addend = rel->address - S_GET_VALUE(fixp->fx_subsy);
+ if (r_type == BFD_RELOC_16)
+ r_type = BFD_RELOC_SH_SWITCH16;
+ else if (r_type == BFD_RELOC_8)
+ r_type = BFD_RELOC_8_PCREL;
+ else if (r_type == BFD_RELOC_32)
+ r_type = BFD_RELOC_SH_SWITCH32;
+ else
+ abort ();
+ }
+ else if (r_type == BFD_RELOC_SH_USES)
+ rel->addend = fixp->fx_addnumber;
+ else if (r_type == BFD_RELOC_SH_COUNT)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_SH_ALIGN)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_VTABLE_INHERIT
+ || r_type == BFD_RELOC_VTABLE_ENTRY)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_SH_LOOP_START
+ || r_type == BFD_RELOC_SH_LOOP_END)
+ rel->addend = fixp->fx_offset;
+ else if (r_type == BFD_RELOC_SH_LABEL && fixp->fx_pcrel)
+ {
+ rel->addend = 0;
+ rel->address = rel->addend = fixp->fx_offset;
+ }
+#ifdef HAVE_SH64
+ else if (shmedia_init_reloc (rel, fixp))
+ ;
+#endif
+ else
+ rel->addend = fixp->fx_addnumber;
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (r_type));
+ /* Set howto to a garbage value so that we can keep going. */
+ rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32);
+ gas_assert (rel->howto != NULL);
+ }
+#ifdef OBJ_ELF
+ else if (rel->howto->type == R_SH_IND12W)
+ rel->addend += fixp->fx_offset - 4;
+#endif
+
+ return rel;
+}
+
+#ifdef OBJ_ELF
+inline static char *
+sh_end_of_match (char *cont, char *what)
+{
+ int len = strlen (what);
+
+ if (strncasecmp (cont, what, strlen (what)) == 0
+ && ! is_part_of_name (cont[len]))
+ return cont + len;
+
+ return NULL;
+}
+
+int
+sh_parse_name (char const *name,
+ expressionS *exprP,
+ enum expr_mode mode,
+ char *nextcharP)
+{
+ char *next = input_line_pointer;
+ char *next_end;
+ int reloc_type;
+ segT segment;
+
+ exprP->X_op_symbol = NULL;
+
+ if (strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0)
+ {
+ if (! GOT_symbol)
+ GOT_symbol = symbol_find_or_make (name);
+
+ exprP->X_add_symbol = GOT_symbol;
+ no_suffix:
+ /* If we have an absolute symbol or a reg, then we know its
+ value now. */
+ segment = S_GET_SEGMENT (exprP->X_add_symbol);
+ if (mode != expr_defer && segment == absolute_section)
+ {
+ exprP->X_op = O_constant;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else if (mode != expr_defer && segment == reg_section)
+ {
+ exprP->X_op = O_register;
+ exprP->X_add_number = S_GET_VALUE (exprP->X_add_symbol);
+ exprP->X_add_symbol = NULL;
+ }
+ else
+ {
+ exprP->X_op = O_symbol;
+ exprP->X_add_number = 0;
+ }
+
+ return 1;
+ }
+
+ exprP->X_add_symbol = symbol_find_or_make (name);
+
+ if (*nextcharP != '@')
+ goto no_suffix;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTOFF")))
+ reloc_type = BFD_RELOC_32_GOTOFF;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTPLT")))
+ reloc_type = BFD_RELOC_SH_GOTPLT32;
+ else if ((next_end = sh_end_of_match (next + 1, "GOT")))
+ reloc_type = BFD_RELOC_32_GOT_PCREL;
+ else if ((next_end = sh_end_of_match (next + 1, "PLT")))
+ reloc_type = BFD_RELOC_32_PLT_PCREL;
+ else if ((next_end = sh_end_of_match (next + 1, "TLSGD")))
+ reloc_type = BFD_RELOC_SH_TLS_GD_32;
+ else if ((next_end = sh_end_of_match (next + 1, "TLSLDM")))
+ reloc_type = BFD_RELOC_SH_TLS_LD_32;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTTPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_IE_32;
+ else if ((next_end = sh_end_of_match (next + 1, "TPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_LE_32;
+ else if ((next_end = sh_end_of_match (next + 1, "DTPOFF")))
+ reloc_type = BFD_RELOC_SH_TLS_LDO_32;
+ else if ((next_end = sh_end_of_match (next + 1, "PCREL")))
+ reloc_type = BFD_RELOC_32_PCREL;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTFUNCDESC")))
+ reloc_type = BFD_RELOC_SH_GOTFUNCDESC;
+ else if ((next_end = sh_end_of_match (next + 1, "GOTOFFFUNCDESC")))
+ reloc_type = BFD_RELOC_SH_GOTOFFFUNCDESC;
+ else if ((next_end = sh_end_of_match (next + 1, "FUNCDESC")))
+ reloc_type = BFD_RELOC_SH_FUNCDESC;
+ else
+ goto no_suffix;
+
+ *input_line_pointer = *nextcharP;
+ input_line_pointer = next_end;
+ *nextcharP = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ exprP->X_op = O_PIC_reloc;
+ exprP->X_add_number = 0;
+ exprP->X_md = reloc_type;
+
+ return 1;
+}
+
+void
+sh_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (15, 0);
+}
+
+int
+sh_regname_to_dw2regnum (char *regname)
+{
+ unsigned int regnum = -1;
+ unsigned int i;
+ const char *p;
+ char *q;
+ static struct { char *name; int dw2regnum; } regnames[] =
+ {
+ { "pr", 17 }, { "t", 18 }, { "gbr", 19 }, { "mach", 20 },
+ { "macl", 21 }, { "fpul", 23 }
+ };
+
+ for (i = 0; i < ARRAY_SIZE (regnames); ++i)
+ if (strcmp (regnames[i].name, regname) == 0)
+ return regnames[i].dw2regnum;
+
+ if (regname[0] == 'r')
+ {
+ p = regname + 1;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 16)
+ return -1;
+ }
+ else if (regname[0] == 'f' && regname[1] == 'r')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 16)
+ return -1;
+ regnum += 25;
+ }
+ else if (regname[0] == 'x' && regname[1] == 'd')
+ {
+ p = regname + 2;
+ regnum = strtoul (p, &q, 10);
+ if (p == q || *q || regnum >= 8)
+ return -1;
+ regnum += 87;
+ }
+ return regnum;
+}
+#endif /* OBJ_ELF */
diff --git a/gas/config/tc-sh.h b/gas/config/tc-sh.h
new file mode 100644
index 0000000..97b6b6d
--- /dev/null
+++ b/gas/config/tc-sh.h
@@ -0,0 +1,259 @@
+/* This file is tc-sh.h
+ Copyright (C) 1993-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_SH
+
+#define TARGET_ARCH bfd_arch_sh
+
+/* The type fixS is defined (to struct fix) in write.h, but write.h uses
+ definitions from this file. To avoid problems with including write.h
+ after the "right" definitions, don't; just forward-declare struct fix
+ here. */
+struct fix;
+struct segment_info_struct;
+struct internal_reloc;
+
+/* Whether -relax was used. */
+extern int sh_relax;
+
+/* Whether -small was used. */
+extern int sh_small;
+
+/* Don't try to break words. */
+#define WORKING_DOT_WORD
+
+/* We require .long, et. al., to be aligned correctly. */
+#define md_cons_align(nbytes) sh_cons_align (nbytes)
+extern void sh_cons_align (int);
+
+/* We need to optimize expr with taking account of rs_align_test
+ frags. */
+
+#ifdef OBJ_ELF
+#define md_optimize_expr(l,o,r) sh_optimize_expr (l, o, r)
+extern int sh_optimize_expr (expressionS *, operatorT, expressionS *);
+#endif
+
+/* When relaxing, we need to generate relocations for alignment
+ directives. */
+#define HANDLE_ALIGN(frag) sh_handle_align (frag)
+extern void sh_handle_align (fragS *);
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (1 + 2)
+
+/* We need to force out some relocations when relaxing. */
+#define TC_FORCE_RELOCATION(fix) sh_force_relocation (fix)
+extern int sh_force_relocation (struct fix *);
+
+/* This macro decides whether a particular reloc is an entry in a
+ switch table. It is used when relaxing, because the linker needs
+ to know about all such entries so that it can adjust them if
+ necessary. */
+
+#define SWITCH_TABLE(FIX) \
+ ((FIX)->fx_addsy != NULL \
+ && (FIX)->fx_subsy != NULL \
+ && S_GET_SEGMENT ((FIX)->fx_addsy) == text_section \
+ && S_GET_SEGMENT ((FIX)->fx_subsy) == text_section \
+ && ((FIX)->fx_r_type == BFD_RELOC_32 \
+ || (FIX)->fx_r_type == BFD_RELOC_16 \
+ || (FIX)->fx_r_type == BFD_RELOC_8))
+
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \
+ (! SEG_NORMAL (SEC) \
+ || TC_FORCE_RELOCATION (FIX) \
+ || (sh_relax && SWITCH_TABLE (FIX)))
+
+/* Don't complain when we leave fx_subsy around. */
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) \
+ ((md_register_arithmetic || (SEG) != reg_section) \
+ && sh_relax && SWITCH_TABLE (FIX))
+
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+/* SH_COUNT relocs are allowed outside of frag.
+ The target is also buggy and sets fix size too large for other relocs. */
+#define TC_FX_SIZE_SLACK(FIX) \
+ ((FIX)->fx_r_type == BFD_RELOC_SH_COUNT ? -1 : 2)
+
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define LISTING_HEADER \
+ (!target_big_endian \
+ ? "Renesas / SuperH SH GAS Little Endian" \
+ : "Renesas / SuperH SH GAS Big Endian")
+
+#define md_operand(x)
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* We record, for each section, whether we have most recently output a
+ CODE reloc or a DATA reloc. */
+struct sh_segment_info_type
+{
+ int in_code : 1;
+};
+#define TC_SEGMENT_INFO_TYPE struct sh_segment_info_type
+
+/* We call a routine to emit a reloc for a label, so that the linker
+ can align loads and stores without crossing a label. */
+extern void sh_frob_label (symbolS *);
+#define tc_frob_label(sym) sh_frob_label (sym)
+
+/* We call a routine to flush pending output in order to output a DATA
+ reloc when required. */
+extern void sh_flush_pending_output (void);
+#define md_flush_pending_output() sh_flush_pending_output ()
+
+#define tc_frob_file_before_adjust sh_frob_file
+extern void sh_frob_file (void);
+
+
+#ifdef OBJ_COFF
+/* COFF specific definitions. */
+
+#define COFF_MAGIC (!target_big_endian ? SH_ARCH_MAGIC_LITTLE : SH_ARCH_MAGIC_BIG)
+
+#define tc_coff_symbol_emit_hook(a) ; /* Not used. */
+
+#define TC_KEEP_FX_OFFSET 1
+
+#define SEG_NAME(SEG) segment_name (SEG)
+
+/* We align most sections to a 16 byte boundary. */
+#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) \
+ (strncmp (SEG_NAME (SEG), ".stabstr", 8) == 0 \
+ ? 0 \
+ : ((strncmp (SEG_NAME (SEG), ".stab", 5) == 0 \
+ || strcmp (SEG_NAME (SEG), ".ctors") == 0 \
+ || strcmp (SEG_NAME (SEG), ".dtors") == 0) \
+ ? 2 \
+ : (sh_small ? 2 : 4)))
+
+#endif /* OBJ_COFF */
+
+#ifdef OBJ_ELF
+/* ELF specific definitions. */
+
+/* Whether or not the target is big endian. */
+extern int target_big_endian;
+#ifdef TE_LINUX
+#define TARGET_FORMAT (!target_big_endian ? "elf32-sh-linux" : "elf32-shbig-linux")
+#elif defined(TE_NetBSD)
+#define TARGET_FORMAT (!target_big_endian ? "elf32-shl-nbsd" : "elf32-sh-nbsd")
+#elif defined TARGET_SYMBIAN
+#define TARGET_FORMAT (!target_big_endian ? "elf32-shl-symbian" : "elf32-sh-symbian")
+#elif defined (TE_VXWORKS)
+#define TARGET_FORMAT (!target_big_endian ? "elf32-shl-vxworks" : "elf32-sh-vxworks")
+#elif defined (TE_UCLINUX)
+#define TARGET_FORMAT sh_uclinux_target_format ()
+extern const char * sh_uclinux_target_format (void);
+#else
+#define TARGET_FORMAT (!target_big_endian ? "elf32-shl" : "elf32-sh")
+#endif
+
+#define elf_tc_final_processing sh_elf_final_processing
+extern void sh_elf_final_processing (void);
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs. */
+
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+
+/* This is the relocation type for direct references to
+ GLOBAL_OFFSET_TABLE. It comes up in complicated expressions such
+ as _GLOBAL_OFFSET_TABLE_+[.-.L284], which cannot be expressed
+ normally with the regular expressions. The fixup specified here
+ when used at runtime implies that we should add the address of the
+ GOT to the specified location, and as a result we have simplified
+ the expression into something we can use. */
+#define TC_RELOC_GLOBAL_OFFSET_TABLE BFD_RELOC_SH_GOTPC
+
+#define tc_fix_adjustable(FIX) sh_fix_adjustable(FIX)
+extern bfd_boolean sh_fix_adjustable (struct fix *);
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* This expression evaluates to true if the relocation is for a local object
+ for which we still want to do the relocation at runtime. False if we
+ are willing to perform this relocation while building the .o file.
+
+ We can't resolve references to the GOT or the PLT when creating the
+ object file, since these tables are only created by the linker.
+ Also, if the symbol is global, weak, common or not defined, the
+ assembler can't compute the appropriate reloc, since its location
+ can only be determined at link time. */
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
+ || (FIX)->fx_r_type == BFD_RELOC_32_GOT_PCREL \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC \
+ || TC_FORCE_RELOCATION (FIX))
+
+#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \
+ ((!md_register_arithmetic && (SEG) == reg_section) \
+ || (sh_relax && SWITCH_TABLE (FIX)))
+
+/* This keeps the subtracted symbol around, for use by PLT_PCREL
+ relocs. */
+#define TC_FORCE_RELOCATION_SUB_ABS(FIX, SEG) \
+ ((FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
+ || (!md_register_arithmetic && (SEG) == reg_section))
+
+/* Don't complain when we leave fx_subsy around. */
+#undef TC_VALIDATE_FIX_SUB
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) \
+ ((md_register_arithmetic || (SEG) != reg_section) \
+ && ((FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
+ || (sh_relax && SWITCH_TABLE (FIX))))
+
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ sh_parse_name ((name), (exprP), (mode), (nextcharP))
+int sh_parse_name (char const *, expressionS *,
+ enum expr_mode, char *);
+
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP, RELOC) \
+ sh_cons_fix_new ((FRAG), (OFF), (LEN), (EXP), (RELOC))
+void sh_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+/* This is used to construct expressions out of @GOTOFF, @PLT and @GOT
+ symbols. The relocation type is stored in X_md. */
+#define O_PIC_reloc O_md1
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions sh_cfi_frame_initial_instructions
+extern void sh_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum sh_regname_to_dw2regnum
+extern int sh_regname_to_dw2regnum (char *);
+
+/* All SH instructions are multiples of 16 bits. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+#define DWARF2_DEFAULT_RETURN_COLUMN 17
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+#endif /* OBJ_ELF */
+
+#define H_TICK_HEX 1
diff --git a/gas/config/tc-sh64.c b/gas/config/tc-sh64.c
new file mode 100644
index 0000000..eba0a5b
--- /dev/null
+++ b/gas/config/tc-sh64.c
@@ -0,0 +1,3527 @@
+/* tc-sh64.c -- Assemble code for the SuperH SH SHcompact and SHmedia.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* This file defines SHmedia ISA-specific functions and includes tc-sh.c.
+ The SHcompact ISA is in all useful aspects the "old" sh4 as implemented
+ in tc-sh.c. Not making this file part of tc-sh.c makes it easier to
+ keep a leaner sh[1-4]-only implementation. */
+
+#define HAVE_SH64
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcodes/sh64-opc.h"
+
+#ifndef OBJ_ELF
+#error This file assumes object output is in the ELF format
+#endif
+
+/* Suffix used when we make "datalabel" symbol copies. It must not
+ collide with anything that can normally appear in a symbol, "faked
+ symbol" or local symbol. */
+#define DATALABEL_SUFFIX " DL"
+
+/* See shmedia_md_apply_fix and shmedia_md_pcrel_from_section for usage. */
+#define SHMEDIA_MD_PCREL_FROM_FIX(FIXP) \
+ ((FIXP)->fx_size + (FIXP)->fx_where + (FIXP)->fx_frag->fr_address - 4)
+
+/* We use this internally to see which one is PT and which is a PTA/PTB
+ that should be error-checked. We give it a better name here (but not
+ one that looks official). Adding it to reloc.c would make it look too
+ much of a real reloc; it is just used temporarily as a fixup-type. */
+#define SHMEDIA_BFD_RELOC_PT BFD_RELOC_12_PCREL
+
+typedef struct
+ {
+ shmedia_arg_type type;
+
+ /* These could go into a union, but that would uglify the code. */
+ int reg;
+ expressionS immediate;
+
+ /* If IMMEDIATE was a shift-expression, like "(S >> N) & 65535", where
+ N = 0, 16, 32, 48, used to extract a certain 16-bit-field to make up
+ a MOVI or SHORI relocation for a symbol, then we put the
+ corresponding reloc-type here and modify the "immediate" expression
+ to S. Otherwise, this is just BFD_RELOC_NONE. */
+ bfd_reloc_code_real_type reloctype;
+ } shmedia_operand_info;
+
+/* Frag containing last base instruction. This is put in the TC field in
+ a frag, so we can emit fixups for fr_opcode without needing to make
+ sure that the opcode is in the same frag as any variant operand. */
+fragS *sh64_last_insn_frag = NULL;
+
+typedef struct
+ {
+ shmedia_operand_info operands[3];
+ unsigned long ops_val;
+ } shmedia_operands_info;
+
+enum sh64_abi_values
+ { sh64_abi_unspecified, sh64_abi_32, sh64_abi_64 };
+
+/* What ISA are we assembling code for? */
+enum sh64_isa_values sh64_isa_mode = sh64_isa_unspecified;
+
+/* What ABI was specified, if any (implicitly or explicitly)? */
+static enum sh64_abi_values sh64_abi = sh64_abi_unspecified;
+
+/* A note that says if we're in a sequence of insns without label
+ settings, segment or ISA mode changes or emitted data. */
+static bfd_boolean seen_insn = FALSE;
+
+/* This is set to TRUE in shmedia_md_end, so that we don't emit any
+ .cranges entries when the assembler calls output functions while
+ grinding along after all input is seen. */
+static bfd_boolean sh64_end_of_assembly = FALSE;
+
+/* Controlled by the option -no-mix, this invalidates mixing SHcompact and
+ SHmedia code in the same section, and also invalidates mixing data and
+ SHmedia code in the same section. No .cranges will therefore be
+ emitted, unless -shcompact-const-crange is specified and there is a
+ constant pool in SHcompact code. */
+static bfd_boolean sh64_mix = TRUE;
+
+static bfd_boolean sh64_shcompact_const_crange = FALSE;
+
+/* Controlled by the option -no-expand, this says whether or not we expand
+ MOVI and PT/PTA/PTB. When we do not expand these insns to fit an
+ operand, we will emit errors for operands out of range and generate the
+ basic instruction and reloc for an external symbol. */
+static bfd_boolean sh64_expand = TRUE;
+
+/* Controlled by the option -expand-pt32, this says whether we expand
+ PT/PTA/PTB of an external symbol to (only) 32 or (the full) 64 bits
+ when -abi=64 is in effect. */
+static bfd_boolean sh64_pt32 = FALSE;
+
+/* When emitting a .cranges descriptor, we want to avoid getting recursive
+ calls through emit_expr. */
+static bfd_boolean emitting_crange = FALSE;
+
+/* SHmedia mnemonics. */
+static struct hash_control *shmedia_opcode_hash_control = NULL;
+
+static const unsigned char shmedia_big_nop_pattern[4] =
+ {
+ (SHMEDIA_NOP_OPC >> 24) & 255, (SHMEDIA_NOP_OPC >> 16) & 255,
+ (SHMEDIA_NOP_OPC >> 8) & 255, SHMEDIA_NOP_OPC & 255
+ };
+
+static const unsigned char shmedia_little_nop_pattern[4] =
+ {
+ SHMEDIA_NOP_OPC & 255, (SHMEDIA_NOP_OPC >> 8) & 255,
+ (SHMEDIA_NOP_OPC >> 16) & 255, (SHMEDIA_NOP_OPC >> 24) & 255
+ };
+
+static void shmedia_md_begin (void);
+static int shmedia_parse_reg (char *, int *, int *, shmedia_arg_type);
+static void shmedia_md_assemble (char *);
+static void shmedia_md_apply_fix (fixS *, valueT *);
+static int shmedia_md_estimate_size_before_relax (fragS *, segT);
+static int shmedia_init_reloc (arelent *, fixS *);
+static char *shmedia_get_operands (shmedia_opcode_info *, char *,
+ shmedia_operands_info *);
+static void s_sh64_mode (int);
+static void s_sh64_abi (int);
+static void shmedia_md_convert_frag (bfd *, segT, fragS *, bfd_boolean);
+static void shmedia_check_limits (offsetT *, bfd_reloc_code_real_type,
+ fixS *);
+static void sh64_set_contents_type (enum sh64_elf_cr_type);
+static void shmedia_get_operand (char **, shmedia_operand_info *,
+ shmedia_arg_type);
+static unsigned long shmedia_immediate_op (char *, shmedia_operand_info *,
+ int, bfd_reloc_code_real_type);
+static char *shmedia_parse_exp (char *, shmedia_operand_info *);
+static void shmedia_frob_file_before_adjust (void);
+static void sh64_emit_crange (symbolS *, symbolS *, enum sh64_elf_cr_type);
+static void sh64_flush_last_crange (bfd *, asection *, void *);
+static void sh64_flag_output (void);
+static void sh64_update_contents_mark (bfd_boolean);
+static void sh64_vtable_entry (int);
+static void sh64_vtable_inherit (int);
+static char *strip_datalabels (void);
+static int shmedia_build_Mytes (shmedia_opcode_info *,
+ shmedia_operands_info *);
+static shmedia_opcode_info *shmedia_find_cooked_opcode (char **);
+static unsigned long shmedia_mask_number (unsigned long,
+ bfd_reloc_code_real_type);
+
+#include "tc-sh.c"
+
+void
+shmedia_md_end (void)
+{
+ symbolS *symp;
+
+ /* First, update the last range to include whatever data was last
+ emitted. */
+ sh64_update_contents_mark (TRUE);
+
+ /* Make sure frags generated after this point are not marked with the
+ wrong ISA; make them easily spottable. We still want to distinguish
+ it from sh64_isa_unspecified when we compile for SHcompact or
+ SHmedia. */
+ if (sh64_isa_mode != sh64_isa_unspecified)
+ sh64_isa_mode = sh64_isa_sh5_guard;
+
+ sh64_end_of_assembly = TRUE;
+
+ bfd_map_over_sections (stdoutput, sh64_flush_last_crange, NULL);
+
+ /* Iterate over segments and emit the last .cranges descriptor. */
+ for (symp = symbol_rootP; symp != NULL; symp = symp->sy_next)
+ {
+ symbolS *mainsym = *symbol_get_tc (symp);
+
+ /* Is this a datalabel symbol; does it have a pointer to the main
+ symbol? */
+ if (mainsym != NULL)
+ {
+ /* If the datalabel symbol is undefined, check if the main
+ symbol has changed in that respect. */
+ if (S_GET_SEGMENT (symp) == undefined_section)
+ {
+ segT symseg;
+
+ symseg = S_GET_SEGMENT (mainsym);
+
+ /* If the symbol is now defined to something that is not
+ global and without STO_SH5_ISA32, we just equate the
+ datalabel symbol to the main symbol, and the lack of
+ STO_SH5_ISA32 will handle the datalabelness. */
+ if (symseg != undefined_section)
+ {
+ if (S_GET_OTHER (mainsym) != STO_SH5_ISA32)
+ {
+ symp->sy_value.X_op = O_symbol;
+ symp->sy_value.X_add_symbol = mainsym;
+ symp->sy_value.X_op_symbol = NULL;
+ symp->sy_value.X_add_number = 0;
+ S_SET_SEGMENT (symp, S_GET_SEGMENT (mainsym));
+ symbol_set_frag (symp, &zero_address_frag);
+ copy_symbol_attributes (symp, mainsym);
+ }
+ else
+ {
+ /* An undefined symbol has since we saw it at
+ "datalabel", been defined to a BranchTarget
+ symbol. What we need to do here is very similar
+ to when we find the "datalabel" for a defined
+ symbol. FIXME: Break out to common function. */
+ symbol_set_value_expression (symp,
+ symbol_get_value_expression
+ (mainsym));
+ S_SET_SEGMENT (symp, symseg);
+ symbol_set_frag (symp, symbol_get_frag (mainsym));
+ copy_symbol_attributes (symp, mainsym);
+
+ /* Unset the BranchTarget mark that can be set at
+ attribute-copying. */
+ S_SET_OTHER (symp,
+ S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+
+ /* The GLOBAL and WEAK attributes are not copied
+ over by copy_symbol_attributes. Do it here. */
+ if (S_IS_WEAK (mainsym))
+ S_SET_WEAK (symp);
+ else if (S_IS_EXTERNAL (mainsym))
+ S_SET_EXTERNAL (symp);
+ }
+ }
+ else
+ {
+ /* A symbol that was defined at the time we saw
+ "datalabel" can since have been attributed with being
+ weak or global. */
+ if (S_IS_WEAK (mainsym))
+ S_SET_WEAK (symp);
+ else if (S_IS_EXTERNAL (mainsym))
+ S_SET_EXTERNAL (symp);
+ }
+ }
+ }
+ }
+
+ for (symp = symbol_rootP; symp != NULL; symp = symp->sy_next)
+ if (S_GET_OTHER (symp) & STO_SH5_ISA32)
+ symp->sy_value.X_add_number++;
+}
+
+/* When resolving symbols, the main assembler has done us a misfavour. It
+ has removed the equation to the main symbol for a datalabel reference
+ that should be equal to the main symbol, e.g. when it's a global or
+ weak symbol and is a non-BranchTarget symbol anyway. We change that
+ back, so that relocs are against the main symbol, not the local "section
+ + offset" value. */
+
+static void
+shmedia_frob_file_before_adjust (void)
+{
+ symbolS *symp;
+ for (symp = symbol_rootP; symp != NULL; symp = symp->sy_next)
+ {
+ symbolS *mainsym = *symbol_get_tc (symp);
+
+ if (mainsym != NULL
+ && S_GET_OTHER (mainsym) != STO_SH5_ISA32
+ && (S_IS_EXTERNAL (mainsym) || S_IS_WEAK (mainsym)))
+ {
+ symp->sy_value.X_op = O_symbol;
+ symp->sy_value.X_add_symbol = mainsym;
+ symp->sy_value.X_op_symbol = NULL;
+ symp->sy_value.X_add_number = 0;
+
+ /* For the "equation trick" to work, we have to set the section
+ to undefined. */
+ S_SET_SEGMENT (symp, undefined_section);
+ symbol_set_frag (symp, &zero_address_frag);
+ copy_symbol_attributes (symp, mainsym);
+
+ /* Don't forget to remove the STO_SH5_ISA32 attribute after
+ copying the other attributes. */
+ S_SET_OTHER (symp, S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+ }
+ }
+}
+
+/* We need to mark the current location after the alignment. This is
+ copied code the caller, do_align. We mark the frag location before and
+ after as we need and arrange to skip the same code in do_align.
+
+ An alternative to code duplication is to call the do_align recursively,
+ arranging to fall through into do_align if we're already here. That
+ would require do_align as an incoming function parameter, since it's
+ static in read.c. That solution was discarded a too kludgy. */
+
+void
+sh64_do_align (int n, const char *fill, int len, int max)
+{
+ /* Update region, or put a data region in front. */
+ sh64_update_contents_mark (TRUE);
+
+ /* Only make a frag if we HAVE to... */
+ if (n != 0 && !need_pass_2)
+ {
+ if (fill == NULL)
+ {
+ if (subseg_text_p (now_seg))
+ frag_align_code (n, max);
+ else
+ frag_align (n, 0, max);
+ }
+ else if (len <= 1)
+ frag_align (n, *fill, max);
+ else
+ frag_align_pattern (n, fill, len, max);
+ }
+
+ /* Update mark for current region with current type. */
+ sh64_update_contents_mark (FALSE);
+}
+
+/* The MAX_MEM_FOR_RS_ALIGN_CODE worker. We have to find out the ISA of
+ the current segment at this position. We can't look just at
+ sh64_isa_shmedia, and we can't look at frag_now. This is brittle:
+ callers are currently frag_align_code from subsegs_finish in write.c
+ (end of assembly) and frag_align_code from do_align in read.c (during
+ assembly). */
+
+int
+sh64_max_mem_for_rs_align_code (void)
+{
+ segment_info_type *seginfo;
+ fragS *mode_start_frag;
+ seginfo = seg_info (now_seg);
+
+ /* We don't use the contents type we find at the tc_segment_info_data,
+ since that does not give us absolute information about the ISA; the
+ contents type can presumably be CRT_DATA and we'd be none the wiser.
+ Instead we use the information stored at the frag of the symbol at
+ the start of this range. If any information is missing or NULL,
+ assume SHcompact. */
+ return
+ /* If the current ISA mode is SHmedia, that's the mode that we're
+ going to assign to the new frag, so request enough memory for
+ it, even if we switch modes afterwards, otherwise we may
+ allocate too little memory and end up overflowing our buffer. */
+ (sh64_isa_mode == sh64_isa_shmedia
+ || (sh64_isa_mode != sh64_isa_unspecified
+ && seginfo != NULL
+ && seginfo->tc_segment_info_data.mode_start_symbol != NULL
+ && ((mode_start_frag
+ = (symbol_get_frag
+ (seginfo->tc_segment_info_data.mode_start_symbol)))
+ != NULL)
+ && mode_start_frag->tc_frag_data.isa == sh64_isa_shmedia))
+ ? (3 + 4) : (2 + 1);
+}
+
+/* Put in SHmedia NOP:s if the alignment was created when in SHmedia mode. */
+
+void
+sh64_handle_align (fragS * frag)
+{
+ int bytes = frag->fr_next->fr_address - frag->fr_address - frag->fr_fix;
+ char * p = frag->fr_literal + frag->fr_fix;
+
+ if (frag->tc_frag_data.isa == sh64_isa_shmedia
+ && frag->fr_type == rs_align_code)
+ {
+ while (bytes & 3)
+ {
+ *p++ = 0;
+ bytes--;
+ frag->fr_fix += 1;
+ }
+
+ if (target_big_endian)
+ {
+ memcpy (p, shmedia_big_nop_pattern,
+ sizeof shmedia_big_nop_pattern);
+ frag->fr_var = sizeof shmedia_big_nop_pattern;
+ }
+ else
+ {
+ memcpy (p, shmedia_little_nop_pattern,
+ sizeof shmedia_little_nop_pattern);
+ frag->fr_var = sizeof shmedia_little_nop_pattern;
+ }
+ }
+ else
+ /* Punt to SHcompact function. */
+ sh_handle_align (frag);
+}
+
+/* Set SEC_SH64_ISA32 for SHmedia sections. */
+
+void
+shmedia_frob_section_type (asection *sec)
+{
+ segment_info_type *seginfo;
+ seginfo = seg_info (sec);
+
+ /* This and elf32-sh64.c:sh64_elf_fake_sections are the only places
+ where we use anything else than ELF header flags to communicate the
+ section as containing SHmedia or other contents. BFD SEC_* section
+ flags are running out and should not be overloaded with
+ target-specific semantics. This target is ELF only (semantics not
+ defined for other formats), so we use the target-specific pointer
+ field of the ELF section data. */
+ if (seginfo && sh64_abi == sh64_abi_32)
+ {
+ struct sh64_section_data *sec_elf_data;
+ flagword sec_type = 0;
+
+ if (seginfo->tc_segment_info_data.emitted_ranges != 0)
+ sec_type = SHF_SH5_ISA32_MIXED;
+ else if (seginfo->tc_segment_info_data.contents_type == CRT_SH5_ISA32)
+ sec_type = SHF_SH5_ISA32;
+
+ sec_elf_data = sh64_elf_section_data (sec)->sh64_info;
+ if (sec_elf_data == NULL)
+ {
+ sec_elf_data = xcalloc (1, sizeof (*sec_elf_data));
+ sh64_elf_section_data (sec)->sh64_info = sec_elf_data;
+ }
+
+ sec_elf_data->contents_flags = sec_type;
+ }
+}
+
+/* This function is called by write_object_file right before the symbol
+ table is written. We subtract 1 from all symbols marked STO_SH5_ISA32,
+ as their values are temporarily incremented in shmedia_md_end, before
+ symbols values are used by relocs and fixups.
+
+ To increment all symbols and then decrement here is admittedly a
+ hackish solution. The alternative is to add infrastructure and hooks
+ to symbol evaluation that evaluates symbols differently internally to
+ the value output into the object file, but at the moment that just
+ seems too much for little benefit. */
+
+void
+sh64_adjust_symtab (void)
+{
+ symbolS *symp;
+
+ for (symp = symbol_rootP; symp; symp = symbol_next (symp))
+ {
+ symbolS *main_symbol = *symbol_get_tc (symp);
+
+ if (main_symbol)
+ {
+ char *sym_name = (char *) S_GET_NAME (symp);
+
+ /* All datalabels not used in relocs should be gone by now.
+
+ We change those remaining to have the name of the main
+ symbol, and we set the ELF type of the symbol of the reloc to
+ STT_DATALABEL. */
+ sym_name[strlen (sym_name) - strlen (DATALABEL_SUFFIX)] = 0;
+ elf_symbol (symbol_get_bfdsym (symp))->internal_elf_sym.st_info
+ = STT_DATALABEL;
+
+ /* Also set this symbol to "undefined", so we'll have only one
+ definition. */
+ S_SET_SEGMENT (symp, undefined_section);
+ }
+ else if (S_GET_OTHER (symp) & STO_SH5_ISA32)
+ {
+ /* It's important to change the BFD symbol value, since it is now
+ set to the GAS symbolS value. */
+ symp->bsym->value--;
+
+ /* Note that we do *not* adjust symp->sy_value.X_add_number. If
+ you do this, the test case in sh/sh64/immexpr2.s will fail.
+ This is because *after* symbols have been output but before
+ relocs are output, fixups are inspected one more time, and
+ some leftover expressions are resolved. To resolve to the
+ same values, those expressions must have the same GAS symbol
+ values before as after symbols have been output. We could
+ "symp->sy_value.X_add_number++" on the STO_SH5_ISA32 symbols
+ through tc_frob_file after symbols have been output, but that
+ would be too gross. */
+ }
+ }
+}
+
+/* Fill-in an allocated arelent. */
+
+static int
+shmedia_init_reloc (arelent *rel, fixS *fixP)
+{
+ /* Adjust parts of *relp according to *fixp, and tell that it has been
+ done, so default initializations will not happen. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_SH_IMM_LOW16:
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ case BFD_RELOC_SH_IMM_HI16:
+ case BFD_RELOC_SH_IMM_LOW16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+ case BFD_RELOC_SH_IMM_HI16_PCREL:
+ case BFD_RELOC_SH_IMMU5:
+ case BFD_RELOC_SH_IMMU6:
+ case BFD_RELOC_SH_IMMS6:
+ case BFD_RELOC_SH_IMMS10:
+ case BFD_RELOC_SH_IMMS10BY2:
+ case BFD_RELOC_SH_IMMS10BY4:
+ case BFD_RELOC_SH_IMMS10BY8:
+ case BFD_RELOC_SH_IMMS16:
+ case BFD_RELOC_SH_IMMU16:
+ case BFD_RELOC_SH_PT_16:
+ case BFD_RELOC_SH_GOT_LOW16:
+ case BFD_RELOC_SH_GOT_MEDLOW16:
+ case BFD_RELOC_SH_GOT_MEDHI16:
+ case BFD_RELOC_SH_GOT_HI16:
+ case BFD_RELOC_SH_GOT10BY4:
+ case BFD_RELOC_SH_GOT10BY8:
+ case BFD_RELOC_SH_GOTPLT_LOW16:
+ case BFD_RELOC_SH_GOTPLT_MEDLOW16:
+ case BFD_RELOC_SH_GOTPLT_MEDHI16:
+ case BFD_RELOC_SH_GOTPLT_HI16:
+ case BFD_RELOC_SH_GOTPLT10BY4:
+ case BFD_RELOC_SH_GOTPLT10BY8:
+ case BFD_RELOC_SH_GOTOFF_LOW16:
+ case BFD_RELOC_SH_GOTOFF_MEDLOW16:
+ case BFD_RELOC_SH_GOTOFF_MEDHI16:
+ case BFD_RELOC_SH_GOTOFF_HI16:
+ case BFD_RELOC_SH_GOTPC_LOW16:
+ case BFD_RELOC_SH_GOTPC_MEDLOW16:
+ case BFD_RELOC_SH_GOTPC_MEDHI16:
+ case BFD_RELOC_SH_GOTPC_HI16:
+ case BFD_RELOC_SH_PLT_LOW16:
+ case BFD_RELOC_SH_PLT_MEDLOW16:
+ case BFD_RELOC_SH_PLT_MEDHI16:
+ case BFD_RELOC_SH_PLT_HI16:
+ rel->addend = fixP->fx_addnumber + fixP->fx_offset;
+ return 1;
+
+ case BFD_RELOC_SH_IMMS6BY32:
+ /* This must be resolved in assembly; we do not support it as a
+ reloc in an object file. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("This operand must be constant at assembly time"));
+ break;
+
+ /* There are valid cases where we get here for other than SHmedia
+ relocs, so don't make a BAD_CASE out of this. */
+ default:
+ ;
+ }
+
+ return 0;
+}
+
+/* Hook called from md_apply_fix in tc-sh.c. */
+
+static void
+shmedia_md_apply_fix (fixS *fixP, valueT *valp)
+{
+ offsetT val = *valp;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ unsigned long insn
+ = target_big_endian ? bfd_getb32 (buf) : bfd_getl32 (buf);
+ bfd_reloc_code_real_type orig_fx_r_type = fixP->fx_r_type;
+
+ /* Change a 64-bit pc-relative reloc into the correct type, just like
+ tc-sh.c:md_apply_fix. */
+ if (fixP->fx_pcrel)
+ {
+ switch (orig_fx_r_type)
+ {
+ case BFD_RELOC_64:
+ case BFD_RELOC_SH_IMM_LOW16:
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ case BFD_RELOC_SH_IMM_HI16:
+ /* Because write.c calls MD_PCREL_FROM_SECTION twice, we need to
+ undo one of the adjustments, if the relocation is not
+ actually for a symbol within the same segment (which we
+ cannot check, because we're not called from md_apply_fix, so
+ we have to keep the reloc). FIXME: This is a bug in
+ write.c:fixup_segment affecting most targets that change
+ ordinary relocs to pcrel relocs in md_apply_fix. */
+ fixP->fx_offset
+ = *valp + SHMEDIA_MD_PCREL_FROM_FIX (fixP);
+ break;
+
+ case BFD_RELOC_SH_PLT_LOW16:
+ case BFD_RELOC_SH_PLT_MEDLOW16:
+ case BFD_RELOC_SH_PLT_MEDHI16:
+ case BFD_RELOC_SH_PLT_HI16:
+ case BFD_RELOC_SH_GOTPC_LOW16:
+ case BFD_RELOC_SH_GOTPC_MEDLOW16:
+ case BFD_RELOC_SH_GOTPC_MEDHI16:
+ case BFD_RELOC_SH_GOTPC_HI16:
+ *valp = 0;
+ return;
+
+ default:
+ ;
+ }
+
+ /* We might need to change some relocs into the corresponding
+ PC-relative one. */
+ switch (orig_fx_r_type)
+ {
+ case BFD_RELOC_64:
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ break;
+
+ case BFD_RELOC_SH_IMM_LOW16:
+ fixP->fx_r_type = BFD_RELOC_SH_IMM_LOW16_PCREL;
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ fixP->fx_r_type = BFD_RELOC_SH_IMM_MEDLOW16_PCREL;
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ fixP->fx_r_type = BFD_RELOC_SH_IMM_MEDHI16_PCREL;
+ break;
+
+ case BFD_RELOC_SH_IMM_HI16:
+ fixP->fx_r_type = BFD_RELOC_SH_IMM_HI16_PCREL;
+ break;
+
+ case SHMEDIA_BFD_RELOC_PT:
+ /* This is how we see a difference between PT and PTA when not
+ expanding (in which case we handle it in
+ shmedia_md_convert_frag). Note that we don't see a
+ difference after the reloc is emitted. */
+ fixP->fx_r_type = BFD_RELOC_SH_PT_16;
+ break;
+
+ case BFD_RELOC_SH_PT_16:
+ /* This tells us there was a PTA or PTB insn explicitly
+ expressed as such (not as PT). We "or" in a 1 into the
+ lowest bit in the (unused) destination field to tell the
+ linker that it should check the right ISA type of the
+ destination and not just change a PTA to PTB (if necessary). */
+ md_number_to_chars (buf, insn | (1 << 10), 4);
+ break;
+
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_SH_IMM_LOW16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+ case BFD_RELOC_SH_IMM_HI16_PCREL:
+ /* Already handled. */
+ break;
+
+ default:
+ /* Everything else that changes into a pc-relative relocation is
+ an error. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Invalid operand expression"));
+ break;
+ }
+
+ return;
+ }
+
+ /* If an expression looked like it was PC-relative, but was completely
+ resolvable, we end up here with the result only in *VALP, and no
+ relocation will be emitted. */
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ {
+ /* Emit error for an out-of-range value. */
+ shmedia_check_limits ((offsetT *) valp, fixP->fx_r_type, fixP);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_SH_IMM_LOW16:
+ md_number_to_chars (buf, insn | ((val & 65535) << 10), 4);
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ md_number_to_chars (buf,
+ insn
+ | ((valueT) (val & ((valueT) 65535 << 16))
+ >> (16 - 10)), 4);
+ break;
+
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ md_number_to_chars (buf,
+ insn
+ | ((valueT) (val & ((valueT) 65535 << 32))
+ >> (32 - 10)), 4);
+ break;
+
+ case BFD_RELOC_SH_IMM_HI16:
+ md_number_to_chars (buf,
+ insn
+ | ((valueT) (val & ((valueT) 65535 << 48))
+ >> (48 - 10)), 4);
+ break;
+
+ case BFD_RELOC_SH_IMMS16:
+ case BFD_RELOC_SH_IMMU16:
+ md_number_to_chars (buf, insn | ((val & 65535) << 10), 4);
+ break;
+
+ case BFD_RELOC_SH_IMMS10:
+ md_number_to_chars (buf, insn | ((val & 0x3ff) << 10), 4);
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY2:
+ md_number_to_chars (buf,
+ insn | ((val & (0x3ff << 1)) << (10 - 1)), 4);
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY4:
+ md_number_to_chars (buf,
+ insn | ((val & (0x3ff << 2)) << (10 - 2)), 4);
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY8:
+ md_number_to_chars (buf,
+ insn | ((val & (0x3ff << 3)) << (10 - 3)), 4);
+ break;
+
+ case BFD_RELOC_SH_SHMEDIA_CODE:
+ /* We just ignore and remove this one for the moment. FIXME:
+ Use it when implementing relaxing. */
+ break;
+
+ case BFD_RELOC_64:
+ md_number_to_chars (buf, val, 8);
+ break;
+
+ case SHMEDIA_BFD_RELOC_PT:
+ /* Change a PT to PTB if the operand turned out to be SHcompact.
+ The basic opcode specified with PT is equivalent to PTA. */
+ if ((val & 1) == 0)
+ insn |= SHMEDIA_PTB_BIT;
+ /* Fall through. */
+
+ case BFD_RELOC_SH_PT_16:
+ if (! sh64_expand || sh_relax)
+ {
+ /* Check if the operand of a PTA or PTB was for the "wrong"
+ ISA. A PT had an incoming fixup of SHMEDIA_BFD_RELOC_PT,
+ which we have changed to the right type above. */
+ if (orig_fx_r_type != SHMEDIA_BFD_RELOC_PT)
+ {
+ if ((insn & SHMEDIA_PTB_BIT) != 0 && (val & 1) != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PTB operand is a SHmedia symbol"));
+ else if ((insn & SHMEDIA_PTB_BIT) == 0 && (val & 1) == 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PTA operand is a SHcompact symbol"));
+ }
+
+ md_number_to_chars (buf,
+ insn | ((val & (0xffff << 2))
+ << (10 - 2)),
+ 4);
+ break;
+ }
+ /* Fall through. */
+
+ default:
+ /* This isn't a BAD_CASE, because presumably we can get here
+ from unexpected operands. Since we don't handle them, make
+ them syntax errors. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid expression in operand"));
+ }
+ fixP->fx_done = 1;
+ }
+}
+
+/* Hook called from md_convert_frag in tc-sh.c. */
+
+static void
+shmedia_md_convert_frag (bfd *output_bfd ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED, fragS *fragP,
+ bfd_boolean final)
+{
+ /* Pointer to first byte in variable-sized part of the frag. */
+ char *var_partp;
+
+ /* Pointer to first opcode byte in frag. */
+ char *opcodep;
+
+ /* Pointer to frag of opcode. */
+ fragS *opc_fragP = fragP->tc_frag_data.opc_frag;
+
+ /* Size in bytes of variable-sized part of frag. */
+ int var_part_size = 0;
+
+ /* This is part of *fragP. It contains all information about addresses
+ and offsets to varying parts. */
+ symbolS *symbolP = fragP->fr_symbol;
+
+ bfd_boolean reloc_needed
+ = (! final
+ || sh_relax
+ || symbolP == NULL
+ || ! S_IS_DEFINED (symbolP)
+ || S_IS_EXTERNAL (symbolP)
+ || S_IS_WEAK (symbolP)
+ || (S_GET_SEGMENT (fragP->fr_symbol) != absolute_section
+ && S_GET_SEGMENT (fragP->fr_symbol) != seg));
+
+ bfd_reloc_code_real_type reloctype = BFD_RELOC_NONE;
+
+ unsigned long var_part_offset;
+
+ /* Where, in file space, does addr point? */
+ bfd_vma target_address;
+ bfd_vma opcode_address;
+
+ /* What was the insn? */
+ unsigned long insn;
+ know (fragP->fr_type == rs_machine_dependent);
+
+ var_part_offset = fragP->fr_fix;
+ var_partp = fragP->fr_literal + var_part_offset;
+ opcodep = fragP->fr_opcode;
+
+ insn = target_big_endian ? bfd_getb32 (opcodep) : bfd_getl32 (opcodep);
+
+ target_address
+ = ((symbolP && final && ! sh_relax ? S_GET_VALUE (symbolP) : 0)
+ + fragP->fr_offset);
+
+ /* The opcode that would be extended is the last four "fixed" bytes. */
+ opcode_address = fragP->fr_address + fragP->fr_fix - 4;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (SH64PCREL16PT_64, SH64PCREL16):
+ case C (SH64PCREL16PT_32, SH64PCREL16):
+ /* We can get a PT to a relaxed SHcompact address if it is in the
+ same section; a mixed-ISA section. Change the opcode to PTB if
+ so. */
+ if ((target_address & 1) == 0)
+ insn |= SHMEDIA_PTB_BIT;
+ /* Fall through. */
+
+ case C (SH64PCREL16_32, SH64PCREL16):
+ case C (SH64PCREL16_64, SH64PCREL16):
+ /* Check that a PTA or PTB points to the right type of target. We
+ can get here for a SHcompact target if we are in a mixed-ISA
+ section. */
+ if (((target_address & 1) == 0) && ((insn & SHMEDIA_PTB_BIT) == 0))
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("PTA operand is a SHcompact symbol"));
+ if (((target_address & 1) != 0) && ((insn & SHMEDIA_PTB_BIT) != 0))
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("PTB operand is a SHmedia symbol"));
+
+ /* When relaxing, we do not output the address in the insn, but
+ instead a 1 into the low bit. This matches what the linker
+ expects to find for a BFD_RELOC_SH_PT_16 reloc, when it checks
+ correctness for PTA/PTB insn; used when the target address is
+ unknown (which is not the case here). */
+ md_number_to_chars (opcodep,
+ insn
+ | (((sh_relax
+ ? 1 : ((target_address - opcode_address) / 4))
+ & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Note that we do not emit info that this was originally a PT since
+ we have resolved to which one of PTA or PTB it will be. */
+ if (sh_relax)
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1, BFD_RELOC_SH_PT_16);
+ var_part_size = 0;
+ break;
+
+ case C (SH64PCREL16_32, SH64PCRELPLT):
+ case C (SH64PCREL16PT_32, SH64PCRELPLT):
+ reloctype = BFD_RELOC_32_PLT_PCREL;
+ reloc_needed = 1;
+ /* Fall through */
+
+ case C (SH64PCREL16_32, SH64PCREL32):
+ case C (SH64PCREL16_64, SH64PCREL32):
+ case C (SH64PCREL16PT_32, SH64PCREL32):
+ case C (SH64PCREL16PT_64, SH64PCREL32):
+ /* In the fixed bit, put in a MOVI. */
+ md_number_to_chars (opcodep,
+ SHMEDIA_MOVI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - (opcode_address + 8))
+ ) >> 16) & 65535) << 10),
+ 4);
+
+ /* Fill in a SHORI for the low part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | (((reloc_needed
+ ? 0 : (target_address - (opcode_address + 8)))
+ & 65535) << 10),
+ 4);
+
+ /* End with a "PTREL R25,TRd". */
+ md_number_to_chars (var_partp + 4,
+ SHMEDIA_PTREL_OPC | (insn & SHMEDIA_LIKELY_BIT)
+ | (SHMEDIA_TEMP_REG << 10)
+ | (insn & (7 << 4)),
+ 4);
+
+ /* We need relocs only if the target symbol was undefined or if
+ we're relaxing. */
+ if (reloc_needed)
+ {
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset - 8, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDLOW16
+ : BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset - 4, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_LOW16
+ : BFD_RELOC_SH_IMM_LOW16_PCREL);
+ }
+
+ var_part_size = 8;
+ break;
+
+ case C (SH64PCREL16_64, SH64PCREL48):
+ case C (SH64PCREL16PT_64, SH64PCREL48):
+ /* In the fixed bit, put in a MOVI. */
+ md_number_to_chars (opcodep,
+ SHMEDIA_MOVI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - (opcode_address + 12))
+ ) >> 32) & 65535) << 10),
+ 4);
+
+ /* The first SHORI, for the medium part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - (opcode_address + 12))
+ ) >> 16) & 65535) << 10),
+ 4);
+
+ /* Fill in a SHORI for the low part. */
+ md_number_to_chars (var_partp + 4,
+ SHMEDIA_SHORI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | (((reloc_needed
+ ? 0 : (target_address - (opcode_address + 12)))
+ & 65535) << 10),
+ 4);
+
+ /* End with a "PTREL R25,TRd". */
+ md_number_to_chars (var_partp + 8,
+ SHMEDIA_PTREL_OPC | (insn & SHMEDIA_LIKELY_BIT)
+ | (SHMEDIA_TEMP_REG << 10)
+ | (insn & (7 << 4)),
+ 4);
+
+ /* We need relocs only if the target symbol was undefined or if
+ we're relaxing. */
+ if (reloc_needed)
+ {
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset - 12, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDHI16
+ : BFD_RELOC_SH_IMM_MEDHI16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset - 8, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDLOW16
+ : BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset - 4, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_LOW16
+ : BFD_RELOC_SH_IMM_LOW16_PCREL);
+ }
+
+ var_part_size = 12;
+ break;
+
+ case C (SH64PCREL16_64, SH64PCRELPLT):
+ case C (SH64PCREL16PT_64, SH64PCRELPLT):
+ reloctype = BFD_RELOC_32_PLT_PCREL;
+ reloc_needed = 1;
+ /* Fall through */
+
+ case C (SH64PCREL16_64, SH64PCREL64):
+ case C (SH64PCREL16PT_64, SH64PCREL64):
+ /* In the fixed bit, put in a MOVI. */
+ md_number_to_chars (opcodep,
+ SHMEDIA_MOVI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - (opcode_address + 16))
+ ) >> 48) & 65535) << 10),
+ 4);
+
+ /* The first SHORI, for the medium-high part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - (opcode_address + 16))
+ ) >> 32) & 65535) << 10),
+ 4);
+
+ /* A SHORI, for the medium-low part. */
+ md_number_to_chars (var_partp + 4,
+ SHMEDIA_SHORI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - (opcode_address + 16))
+ ) >> 16) & 65535) << 10),
+ 4);
+
+ /* Fill in a SHORI for the low part. */
+ md_number_to_chars (var_partp + 8,
+ SHMEDIA_SHORI_OPC
+ | (SHMEDIA_TEMP_REG << 4)
+ | (((reloc_needed
+ ? 0 : (target_address - (opcode_address + 16)))
+ & 65535) << 10),
+ 4);
+
+ /* End with a "PTREL R25,TRd". */
+ md_number_to_chars (var_partp + 12,
+ SHMEDIA_PTREL_OPC | (insn & SHMEDIA_LIKELY_BIT)
+ | (SHMEDIA_TEMP_REG << 10)
+ | (insn & (7 << 4)),
+ 4);
+
+ /* We need relocs only if the target symbol was undefined or if
+ we're relaxing. */
+ if (reloc_needed)
+ {
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset - 16, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_HI16
+ : BFD_RELOC_SH_IMM_HI16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset - 12, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDHI16
+ : BFD_RELOC_SH_IMM_MEDHI16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset - 8, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDLOW16
+ : BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal + 8, 4, fragP->fr_symbol,
+ fragP->fr_offset - 4, 1,
+ reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_LOW16
+ : BFD_RELOC_SH_IMM_LOW16_PCREL);
+ }
+
+ var_part_size = 16;
+ break;
+
+ case C (MOVI_IMM_64, MOVI_GOTOFF):
+ reloctype = BFD_RELOC_32_GOTOFF;
+ reloc_needed = 1;
+ /* Fall through. */
+
+ case C (MOVI_IMM_64, UNDEF_MOVI):
+ case C (MOVI_IMM_64, MOVI_64):
+ {
+ /* We only get here for undefined symbols, so we can simplify
+ handling compared to those above; we have 0 in the parts that
+ will be filled with the symbol parts. */
+
+ int reg = (insn >> 4) & 0x3f;
+
+ /* In the fixed bit, put in a MOVI. */
+ md_number_to_chars (opcodep, SHMEDIA_MOVI_OPC | (reg << 4), 4);
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_HI16
+ : reloctype == BFD_RELOC_32_GOTOFF
+ ? BFD_RELOC_SH_GOTOFF_HI16
+ : (abort (), BFD_RELOC_SH_IMM_HI16));
+
+ /* The first SHORI, for the medium-high part. */
+ md_number_to_chars (var_partp, SHMEDIA_SHORI_OPC | (reg << 4), 4);
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_MEDHI16
+ : reloctype == BFD_RELOC_32_GOTOFF
+ ? BFD_RELOC_SH_GOTOFF_MEDHI16
+ : (abort (), BFD_RELOC_SH_IMM_MEDHI16));
+
+ /* A SHORI, for the medium-low part. */
+ md_number_to_chars (var_partp + 4,
+ SHMEDIA_SHORI_OPC | (reg << 4), 4);
+ fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_MEDLOW16
+ : reloctype == BFD_RELOC_32_GOTOFF
+ ? BFD_RELOC_SH_GOTOFF_MEDLOW16
+ : (abort (), BFD_RELOC_SH_IMM_MEDLOW16));
+
+ /* Fill in a SHORI for the low part. */
+ md_number_to_chars (var_partp + 8,
+ SHMEDIA_SHORI_OPC | (reg << 4), 4);
+ fix_new (fragP, var_partp - fragP->fr_literal + 8, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_LOW16
+ : reloctype == BFD_RELOC_32_GOTOFF
+ ? BFD_RELOC_SH_GOTOFF_LOW16
+ : (abort (), BFD_RELOC_SH_IMM_LOW16));
+
+ var_part_size = 12;
+ break;
+ }
+
+ case C (MOVI_IMM_32, MOVI_GOTOFF):
+ reloctype = BFD_RELOC_32_GOTOFF;
+ reloc_needed = 1;
+ /* Fall through. */
+
+ case C (MOVI_IMM_32, UNDEF_MOVI):
+ case C (MOVI_IMM_32, MOVI_32):
+ {
+ /* Note that we only get here for undefined symbols. */
+
+ int reg = (insn >> 4) & 0x3f;
+
+ /* A MOVI, for the high part. */
+ md_number_to_chars (opcodep, SHMEDIA_MOVI_OPC | (reg << 4), 4);
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 0,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_MEDLOW16
+ : reloctype == BFD_RELOC_32_GOTOFF
+ ? BFD_RELOC_SH_GOTOFF_MEDLOW16
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_MEDLOW16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDLOW16
+ : (abort (), BFD_RELOC_SH_IMM_MEDLOW16));
+
+ /* Fill in a SHORI for the low part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC | (reg << 4), 4);
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset, 0,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_LOW16
+ : reloctype == BFD_RELOC_32_GOTOFF
+ ? BFD_RELOC_SH_GOTOFF_LOW16
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_LOW16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_LOW16
+ : (abort (), BFD_RELOC_SH_IMM_LOW16));
+
+ var_part_size = 4;
+ break;
+ }
+
+ case C (MOVI_IMM_32_PCREL, MOVI_16):
+ case C (MOVI_IMM_64_PCREL, MOVI_16):
+ md_number_to_chars (opcodep,
+ insn
+ | (((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ & 65535) << 10),
+ 4);
+ if (reloc_needed)
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_SH_IMM_LOW16_PCREL);
+ var_part_size = 0;
+ break;
+
+ case C (MOVI_IMM_32, MOVI_16):
+ case C (MOVI_IMM_64, MOVI_16):
+ md_number_to_chars (opcodep,
+ insn
+ | (((reloc_needed ? 0 : target_address)
+ & 65535) << 10),
+ 4);
+ if (reloc_needed)
+ abort ();
+ var_part_size = 0;
+ break;
+
+ case C (MOVI_IMM_32_PCREL, MOVI_PLT):
+ reloctype = BFD_RELOC_32_PLT_PCREL;
+ goto movi_imm_32_pcrel_reloc_needed;
+
+ case C (MOVI_IMM_32_PCREL, MOVI_GOTPC):
+ reloctype = BFD_RELOC_SH_GOTPC;
+ /* Fall through. */
+
+ movi_imm_32_pcrel_reloc_needed:
+ reloc_needed = 1;
+ /* Fall through. */
+
+ case C (MOVI_IMM_32_PCREL, MOVI_32):
+ case C (MOVI_IMM_64_PCREL, MOVI_32):
+ {
+ int reg = (insn >> 4) & 0x3f;
+
+ md_number_to_chars (opcodep,
+ insn
+ | (((((reloc_needed
+ ? 0 : (target_address - opcode_address)))
+ >> 16) & 65535) << 10), 4);
+
+ /* A SHORI, for the low part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | (((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ & 65535) << 10), 4);
+ if (reloc_needed)
+ {
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_MEDLOW16_PCREL
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_MEDLOW16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDLOW16
+ : (abort (), BFD_RELOC_SH_IMM_MEDLOW16_PCREL));
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset + 4, 1,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_LOW16_PCREL
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_LOW16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_LOW16
+ : (abort (), BFD_RELOC_SH_IMM_LOW16_PCREL));
+ }
+ var_part_size = 4;
+ }
+ break;
+
+ case C (MOVI_IMM_32_PCREL, MOVI_48):
+ case C (MOVI_IMM_64_PCREL, MOVI_48):
+ {
+ int reg = (insn >> 4) & 0x3f;
+
+ md_number_to_chars (opcodep,
+ insn
+ | (((((reloc_needed
+ ? 0 : (target_address - opcode_address)))
+ >> 32) & 65535) << 10), 4);
+
+ /* A SHORI, for the medium part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ >> 16) & 65535) << 10), 4);
+
+ /* A SHORI, for the low part. */
+ md_number_to_chars (var_partp + 4,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | (((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ & 65535) << 10), 4);
+ if (reloc_needed)
+ {
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ BFD_RELOC_SH_IMM_MEDHI16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset + 4, 1, BFD_RELOC_SH_IMM_MEDLOW16_PCREL);
+ fix_new (fragP, var_partp - fragP->fr_literal + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 8, 1, BFD_RELOC_SH_IMM_LOW16_PCREL);
+ }
+ var_part_size = 8;
+ }
+ break;
+
+ case C (MOVI_IMM_64_PCREL, MOVI_PLT):
+ reloctype = BFD_RELOC_32_PLT_PCREL;
+ goto movi_imm_64_pcrel_reloc_needed;
+
+ case C (MOVI_IMM_64_PCREL, MOVI_GOTPC):
+ reloctype = BFD_RELOC_SH_GOTPC;
+ /* Fall through. */
+
+ movi_imm_64_pcrel_reloc_needed:
+ reloc_needed = 1;
+ /* Fall through. */
+
+ case C (MOVI_IMM_32_PCREL, MOVI_64):
+ case C (MOVI_IMM_64_PCREL, MOVI_64):
+ {
+ int reg = (insn >> 4) & 0x3f;
+
+ md_number_to_chars (opcodep,
+ insn
+ | (((((reloc_needed
+ ? 0 : (target_address - opcode_address)))
+ >> 48) & 65535) << 10), 4);
+
+ /* A SHORI, for the medium-high part. */
+ md_number_to_chars (var_partp,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ >> 32) & 65535) << 10), 4);
+
+ /* A SHORI, for the medium-low part. */
+ md_number_to_chars (var_partp + 4,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | ((((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ >> 16) & 65535) << 10), 4);
+
+ /* A SHORI, for the low part. */
+ md_number_to_chars (var_partp + 8,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | (((reloc_needed
+ ? 0 : (target_address - opcode_address))
+ & 65535) << 10), 4);
+ if (reloc_needed)
+ {
+ fix_new (opc_fragP, opcodep - opc_fragP->fr_literal, 4,
+ fragP->fr_symbol, fragP->fr_offset, 1,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_HI16_PCREL
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_HI16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_HI16
+ : (abort (), BFD_RELOC_SH_IMM_HI16_PCREL));
+ fix_new (fragP, var_partp - fragP->fr_literal, 4, fragP->fr_symbol,
+ fragP->fr_offset + 4, 1,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_MEDHI16_PCREL
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_MEDHI16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDHI16
+ : (abort (), BFD_RELOC_SH_IMM_MEDHI16_PCREL));
+ fix_new (fragP, var_partp - fragP->fr_literal + 4, 4,
+ fragP->fr_symbol,
+ fragP->fr_offset + 8, 1,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_MEDLOW16_PCREL
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_MEDLOW16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_MEDLOW16
+ : (abort (), BFD_RELOC_SH_IMM_MEDLOW16_PCREL));
+ fix_new (fragP, var_partp - fragP->fr_literal + 8, 4,
+ fragP->fr_symbol,
+ fragP->fr_offset + 12, 1,
+ reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_IMM_LOW16_PCREL
+ : reloctype == BFD_RELOC_SH_GOTPC
+ ? BFD_RELOC_SH_GOTPC_LOW16
+ : reloctype == BFD_RELOC_32_PLT_PCREL
+ ? BFD_RELOC_SH_PLT_LOW16
+ : (abort (), BFD_RELOC_SH_IMM_LOW16_PCREL));
+ }
+ var_part_size = 12;
+ }
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ }
+
+ fragP->fr_fix += var_part_size;
+ fragP->fr_var = 0;
+}
+
+/* Mask NUMBER (originating from a signed number) corresponding to the HOW
+ reloc. */
+
+static unsigned long
+shmedia_mask_number (unsigned long number, bfd_reloc_code_real_type how)
+{
+ switch (how)
+ {
+ case BFD_RELOC_SH_IMMU5:
+ number &= (1 << 5) - 1;
+ break;
+
+ case BFD_RELOC_SH_IMMS6:
+ case BFD_RELOC_SH_IMMU6:
+ number &= (1 << 6) - 1;
+ break;
+
+ case BFD_RELOC_SH_IMMS6BY32:
+ number = (number & ((1 << (6 + 5)) - 1)) >> 5;
+ break;
+
+ case BFD_RELOC_SH_IMMS10:
+ number &= (1 << 10) - 1;
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY2:
+ number = (number & ((1 << (10 + 1)) - 1)) >> 1;
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY4:
+ number = (number & ((1 << (10 + 2)) - 1)) >> 2;
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY8:
+ number = (number & ((1 << (10 + 3)) - 1)) >> 3;
+ break;
+
+ case BFD_RELOC_SH_IMMS16:
+ case BFD_RELOC_SH_IMMU16:
+ number &= (1 << 16) - 1;
+ break;
+
+ default:
+ BAD_CASE (how);
+ }
+
+ return number;
+}
+
+/* Emit errors for values out-of-range, using as_bad_where if FRAGP is
+ non-NULL, as_bad otherwise. */
+
+static void
+shmedia_check_limits (offsetT *valp, bfd_reloc_code_real_type reloc,
+ fixS *fixp)
+{
+ offsetT val = *valp;
+
+ char *msg = NULL;
+
+ switch (reloc)
+ {
+ case BFD_RELOC_SH_IMMU5:
+ if (val < 0 || val > (1 << 5) - 1)
+ msg = _("invalid operand, not a 5-bit unsigned value: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS6:
+ if (val < -(1 << 5) || val > (1 << 5) - 1)
+ msg = _("invalid operand, not a 6-bit signed value: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMU6:
+ if (val < 0 || val > (1 << 6) - 1)
+ msg = _("invalid operand, not a 6-bit unsigned value: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS6BY32:
+ if (val < -(1 << 10) || val > (1 << 10) - 1)
+ msg = _("invalid operand, not a 11-bit signed value: %d");
+ else if (val & 31)
+ msg = _("invalid operand, not a multiple of 32: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS10:
+ if (val < -(1 << 9) || val > (1 << 9) - 1)
+ msg = _("invalid operand, not a 10-bit signed value: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY2:
+ if (val < -(1 << 10) || val > (1 << 10) - 1)
+ msg = _("invalid operand, not a 11-bit signed value: %d");
+ else if (val & 1)
+ msg = _("invalid operand, not an even value: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY4:
+ if (val < -(1 << 11) || val > (1 << 11) - 1)
+ msg = _("invalid operand, not a 12-bit signed value: %d");
+ else if (val & 3)
+ msg = _("invalid operand, not a multiple of 4: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS10BY8:
+ if (val < -(1 << 12) || val > (1 << 12) - 1)
+ msg = _("invalid operand, not a 13-bit signed value: %d");
+ else if (val & 7)
+ msg = _("invalid operand, not a multiple of 8: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMS16:
+ if (val < -(1 << 15) || val > (1 << 15) - 1)
+ msg = _("invalid operand, not a 16-bit signed value: %d");
+ break;
+
+ case BFD_RELOC_SH_IMMU16:
+ if (val < 0 || val > (1 << 16) - 1)
+ msg = _("invalid operand, not a 16-bit unsigned value: %d");
+ break;
+
+ case BFD_RELOC_SH_PT_16:
+ case SHMEDIA_BFD_RELOC_PT:
+ if (val < -(1 << 15) * 4 || val > ((1 << 15) - 1) * 4 + 1)
+ msg = _("operand out of range for PT, PTA and PTB");
+ else if ((val % 4) != 0 && ((val - 1) % 4) != 0)
+ msg = _("operand not a multiple of 4 for PT, PTA or PTB: %d");
+ break;
+
+ /* These have no limits; they take a 16-bit slice of a 32- or 64-bit
+ number. */
+ case BFD_RELOC_SH_IMM_HI16:
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ case BFD_RELOC_SH_IMM_LOW16:
+ case BFD_RELOC_SH_IMM_HI16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+ case BFD_RELOC_SH_IMM_LOW16_PCREL:
+
+ case BFD_RELOC_SH_SHMEDIA_CODE:
+ break;
+
+ /* This one has limits out of our reach. */
+ case BFD_RELOC_64:
+ break;
+
+ default:
+ BAD_CASE (reloc);
+ }
+
+ if (msg)
+ {
+ if (fixp)
+ as_bad_where (fixp->fx_file, fixp->fx_line, msg, val);
+ else
+ as_bad (msg, val);
+ }
+}
+
+/* Handle an immediate operand by checking limits and noting it for later
+ evaluation if not computable yet, and return a bitfield suitable to
+ "or" into the opcode (non-zero if the value was a constant number). */
+
+static unsigned long
+shmedia_immediate_op (char *where, shmedia_operand_info *op, int pcrel,
+ bfd_reloc_code_real_type how)
+{
+ unsigned long retval = 0;
+
+ /* If this is not an absolute number, make it a fixup. A constant in
+ place of a pc-relative operand also needs a fixup. */
+ if (op->immediate.X_op != O_constant || pcrel)
+ fix_new_exp (frag_now,
+ where - frag_now->fr_literal,
+ 4,
+ &op->immediate,
+ pcrel,
+ how);
+ else
+ {
+ /* Check that the number is within limits as represented by the
+ reloc, and return the number. */
+ shmedia_check_limits (&op->immediate.X_add_number, how, NULL);
+
+ retval
+ = shmedia_mask_number ((unsigned long) op->immediate.X_add_number,
+ how);
+ }
+
+ return retval << 10;
+}
+
+/* Try and parse a register name case-insensitively, return the number of
+ chars consumed. */
+
+static int
+shmedia_parse_reg (char *src, int *mode, int *reg, shmedia_arg_type argtype)
+{
+ int l0 = TOLOWER (src[0]);
+ int l1 = l0 ? TOLOWER (src[1]) : 0;
+
+ if (l0 == 'r')
+ {
+ if (src[1] >= '1' && src[1] <= '5')
+ {
+ if (src[2] >= '0' && src[2] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_GREG_M;
+ *reg = 10 * (src[1] - '0') + src[2] - '0';
+ return 3;
+ }
+ }
+
+ if (src[1] == '6')
+ {
+ if (src[2] >= '0' && src[2] <= '3'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_GREG_M;
+ *reg = 60 + src[2] - '0';
+ return 3;
+ }
+ }
+
+ if (src[1] >= '0' && src[1] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[2]))
+ {
+ *mode = A_GREG_M;
+ *reg = (src[1] - '0');
+ return 2;
+ }
+ }
+
+ if (l0 == 't' && l1 == 'r')
+ {
+ if (src[2] >= '0' && src[2] <= '7'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_TREG_B;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+
+ if (l0 == 'f' && l1 == 'r')
+ {
+ if (src[2] >= '1' && src[2] <= '5')
+ {
+ if (src[3] >= '0' && src[3] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_FREG_G;
+ *reg = 10 * (src[2] - '0') + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] == '6')
+ {
+ if (src[3] >= '0' && src[3] <= '3'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_FREG_G;
+ *reg = 60 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_FREG_G;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+
+ if (l0 == 'f' && l1 == 'v')
+ {
+ if (src[2] >= '1' && src[2] <= '5')
+ {
+ if (src[3] >= '0' && src[3] <= '9'
+ && ((10 * (src[2] - '0') + src[3] - '0') % 4) == 0
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_FVREG_G;
+ *reg = 10 * (src[2] - '0') + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] == '6')
+ {
+ if (src[3] == '0'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_FVREG_G;
+ *reg = 60 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '9'
+ && ((src[2] - '0') % 4) == 0
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_FVREG_G;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+
+ if (l0 == 'd' && l1 == 'r')
+ {
+ if (src[2] >= '1' && src[2] <= '5')
+ {
+ if (src[3] >= '0' && src[3] <= '9'
+ && ((src[3] - '0') % 2) == 0
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_DREG_G;
+ *reg = 10 * (src[2] - '0') + src[3] - '0';
+ return 4;
+ }
+ }
+
+ if (src[2] == '6')
+ {
+ if ((src[3] == '0' || src[3] == '2')
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_DREG_G;
+ *reg = 60 + src[3] - '0';
+ return 4;
+ }
+ }
+
+ if (src[2] >= '0' && src[2] <= '9'
+ && ((src[2] - '0') % 2) == 0
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_DREG_G;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+
+ if (l0 == 'f' && l1 == 'p')
+ {
+ if (src[2] >= '1' && src[2] <= '5')
+ {
+ if (src[3] >= '0' && src[3] <= '9'
+ && ((src[3] - '0') % 2) == 0
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_FPREG_G;
+ *reg = 10 * (src[2] - '0') + src[3] - '0';
+ return 4;
+ }
+ }
+
+ if (src[2] == '6')
+ {
+ if ((src[3] == '0' || src[3] == '2')
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_FPREG_G;
+ *reg = 60 + src[3] - '0';
+ return 4;
+ }
+ }
+
+ if (src[2] >= '0' && src[2] <= '9'
+ && ((src[2] - '0') % 2) == 0
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_FPREG_G;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+
+ if (l0 == 'm' && strncasecmp (src, "mtrx", 4) == 0)
+ {
+ if (src[4] == '0' && ! IDENT_CHAR ((unsigned char) src[5]))
+ {
+ *mode = A_FMREG_G;
+ *reg = 0;
+ return 5;
+ }
+
+ if (src[4] == '1' && src[5] == '6'
+ && ! IDENT_CHAR ((unsigned char) src[6]))
+ {
+ *mode = A_FMREG_G;
+ *reg = 16;
+ return 6;
+ }
+
+ if (src[4] == '3' && src[5] == '2'
+ && ! IDENT_CHAR ((unsigned char) src[6]))
+ {
+ *mode = A_FMREG_G;
+ *reg = 32;
+ return 6;
+ }
+
+ if (src[4] == '4' && src[5] == '8'
+ && ! IDENT_CHAR ((unsigned char) src[6]))
+ {
+ *mode = A_FMREG_G;
+ *reg = 48;
+ return 6;
+ }
+ }
+
+ if (l0 == 'c' && l1 == 'r')
+ {
+ if (src[2] >= '1' && src[2] <= '5')
+ {
+ if (src[3] >= '0' && src[3] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_CREG_K;
+ *reg = 10 * (src[2] - '0') + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] == '6')
+ {
+ if (src[3] >= '0' && src[3] <= '3'
+ && ! IDENT_CHAR ((unsigned char) src[4]))
+ {
+ *mode = A_CREG_K;
+ *reg = 60 + src[3] - '0';
+ return 4;
+ }
+ }
+ if (src[2] >= '0' && src[2] <= '9'
+ && ! IDENT_CHAR ((unsigned char) src[3]))
+ {
+ *mode = A_CREG_K;
+ *reg = (src[2] - '0');
+ return 3;
+ }
+ }
+
+ /* We either have an error, a symbol or a control register by predefined
+ name. To keep things simple but still fast for normal cases, we do
+ linear search in the (not to big) table of predefined control
+ registers. We only do this when we *expect* a control register.
+ Those instructions should be rare enough that linear searching is ok.
+ Or just read them into a hash-table in shmedia_md_begin. Since they
+ cannot be specified in the same place of symbol operands, don't add
+ them there to the *main* symbol table as being in "reg_section". */
+ if (argtype == A_CREG_J || argtype == A_CREG_K)
+ {
+ const shmedia_creg_info *cregp;
+ int len = 0;
+
+ for (cregp = shmedia_creg_table; cregp->name != NULL; cregp++)
+ {
+ len = strlen (cregp->name);
+ if (strncasecmp (cregp->name, src, len) == 0
+ && ! IDENT_CHAR (src[len]))
+ break;
+ }
+
+ if (cregp->name != NULL)
+ {
+ *mode = A_CREG_K;
+ *reg = cregp->cregno;
+ return len;
+ }
+ }
+
+ return 0;
+}
+
+/* Called from md_estimate_size_before_relax in tc-sh.c */
+
+static int
+shmedia_md_estimate_size_before_relax (fragS *fragP,
+ segT segment_type ATTRIBUTE_UNUSED)
+{
+ int old_fr_fix;
+ expressionS *exp;
+
+ /* For ELF, we can't relax externally visible symbols; see tc-i386.c. */
+ bfd_boolean sym_relaxable
+ = (fragP->fr_symbol
+ && S_GET_SEGMENT (fragP->fr_symbol) == segment_type
+ && ! S_IS_EXTERNAL (fragP->fr_symbol)
+ && ! S_IS_WEAK (fragP->fr_symbol));
+
+ old_fr_fix = fragP->fr_fix;
+
+ switch (fragP->fr_subtype)
+ {
+ case C (SH64PCREL16_32, UNDEF_SH64PCREL):
+ case C (SH64PCREL16PT_32, UNDEF_SH64PCREL):
+ /* Used to be to somewhere which was unknown. */
+ if (sym_relaxable)
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+
+ /* In this segment, so head for shortest. */
+ fragP->fr_subtype = C (what, SH64PCREL16);
+ }
+ else
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+ /* We know the abs value, but we don't know where we will be
+ linked, so we must make it the longest. Presumably we could
+ switch to a non-pcrel representation, but having absolute
+ values in PT operands should be rare enough not to be worth
+ adding that code. */
+ fragP->fr_subtype = C (what, SH64PCREL32);
+ }
+ fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+ break;
+
+ case C (SH64PCREL16_64, UNDEF_SH64PCREL):
+ case C (SH64PCREL16PT_64, UNDEF_SH64PCREL):
+ /* Used to be to somewhere which was unknown. */
+ if (sym_relaxable)
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+
+ /* In this segment, so head for shortest. */
+ fragP->fr_subtype = C (what, SH64PCREL16);
+ }
+ else
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+ /* We know the abs value, but we don't know where we will be
+ linked, so we must make it the longest. Presumably we could
+ switch to a non-pcrel representation, but having absolute
+ values in PT operands should be rare enough not to be worth
+ adding that code. */
+ fragP->fr_subtype = C (what, SH64PCREL64);
+ }
+ fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+ break;
+
+ case C (MOVI_IMM_64, UNDEF_MOVI):
+ case C (MOVI_IMM_32, UNDEF_MOVI):
+ exp = NULL;
+
+ /* Look inside the "symbol". If we find a PC-relative expression,
+ change this to a PC-relative, relaxable expression. */
+ if (fragP->fr_symbol != NULL
+ && (exp = symbol_get_value_expression (fragP->fr_symbol)) != NULL
+ && exp->X_op == O_subtract
+ && exp->X_op_symbol != NULL
+ && S_GET_SEGMENT (exp->X_op_symbol) == segment_type)
+ {
+ int what = GET_WHAT (fragP->fr_subtype);
+ int what_high = what == MOVI_IMM_32 ? MOVI_32 : MOVI_64;
+ expressionS *opexp
+ = symbol_get_value_expression (exp->X_op_symbol);
+ expressionS *addexp
+ = symbol_get_value_expression (exp->X_add_symbol);
+
+ /* Change the MOVI expression to the "X" in "X - Y" and subtract
+ Y:s offset to this location from X. Note that we can only
+ allow an Y which is offset from this frag. */
+ if (opexp != NULL
+ && addexp != NULL
+ && opexp->X_op == O_constant
+ && fragP == symbol_get_frag (exp->X_op_symbol))
+ {
+ /* At this point, before relaxing, the add-number of opexp
+ is the offset from the fr_fix part. */
+ fragP->fr_offset
+ = (exp->X_add_number
+ - (opexp->X_add_number - (fragP->fr_fix - 4)));
+ fragP->fr_symbol = exp->X_add_symbol;
+
+ what = what == MOVI_IMM_32
+ ? MOVI_IMM_32_PCREL : MOVI_IMM_64_PCREL;
+
+ /* Check the "X" symbol to estimate the size of this
+ PC-relative expression. */
+ if (S_GET_SEGMENT (exp->X_add_symbol) == segment_type
+ && ! S_IS_EXTERNAL (exp->X_add_symbol)
+ && ! S_IS_WEAK (exp->X_add_symbol))
+ fragP->fr_subtype = C (what, MOVI_16);
+ else
+ fragP->fr_subtype = C (what, what_high);
+
+ /* This is now a PC-relative expression, fit to be relaxed. */
+ }
+ else
+ fragP->fr_subtype = C (what, what_high);
+ }
+ else if (fragP->fr_symbol == NULL
+ || (S_GET_SEGMENT (fragP->fr_symbol) == absolute_section
+ && exp->X_op == O_constant))
+ {
+ unsigned long insn
+ = (target_big_endian
+ ? bfd_getb32 (fragP->fr_opcode)
+ : bfd_getl32 (fragP->fr_opcode));
+ offsetT one = (offsetT) 1;
+ offsetT value = fragP->fr_offset
+ + (fragP->fr_symbol == NULL ? 0 : S_GET_VALUE (fragP->fr_symbol));
+
+ if (value >= ((offsetT) -1 << 15) && value < ((offsetT) 1 << 15))
+ {
+ /* Fits in 16-bit signed number. */
+ int what = GET_WHAT (fragP->fr_subtype);
+ fragP->fr_subtype = C (what, MOVI_16);
+
+ /* Just "or" in the value. */
+ md_number_to_chars (fragP->fr_opcode,
+ insn | ((value & ((1 << 16) - 1)) << 10),
+ 4);
+ }
+ else if (value >= -(one << 31)
+ && (value < (one << 31)
+ || (sh64_abi == sh64_abi_32 && value < (one << 32))))
+ {
+ /* The value fits in a 32-bit signed number. */
+ int reg = (insn >> 4) & 0x3f;
+
+ /* Just "or" in the high bits of the value, making the first
+ MOVI. */
+ md_number_to_chars (fragP->fr_opcode,
+ insn
+ | (((value >> 16) & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Add a SHORI with the low bits. Note that this insn lives
+ in the variable fragment part. */
+ md_number_to_chars (fragP->fr_literal + old_fr_fix,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | ((value & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* We took a piece of the variable part. */
+ fragP->fr_fix += 4;
+ }
+ else if (GET_WHAT (fragP->fr_subtype) == MOVI_IMM_32)
+ {
+ /* Value out of range. */
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("MOVI operand is not a 32-bit signed value: 0x%8x%08x"),
+ ((unsigned int) (value >> 32)
+ & (unsigned int) 0xffffffff),
+ (unsigned int) value & (unsigned int) 0xffffffff);
+
+ /* Must advance size, or we will get internal inconsistency
+ and fall into an assert. */
+ fragP->fr_fix += 4;
+ }
+ /* Now we know we are allowed to expand to 48- and 64-bit values. */
+ else if (value >= -(one << 47) && value < (one << 47))
+ {
+ /* The value fits in a 48-bit signed number. */
+ int reg = (insn >> 4) & 0x3f;
+
+ /* Just "or" in the high bits of the value, making the first
+ MOVI. */
+ md_number_to_chars (fragP->fr_opcode,
+ insn
+ | (((value >> 32) & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Add a SHORI with the middle bits. Note that this insn lives
+ in the variable fragment part. */
+ md_number_to_chars (fragP->fr_literal + old_fr_fix,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | (((value >> 16) & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Add a SHORI with the low bits. */
+ md_number_to_chars (fragP->fr_literal + old_fr_fix + 4,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | ((value & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* We took a piece of the variable part. */
+ fragP->fr_fix += 8;
+ }
+ else
+ {
+ /* A 64-bit number. */
+ int reg = (insn >> 4) & 0x3f;
+
+ /* Just "or" in the high bits of the value, making the first
+ MOVI. */
+ md_number_to_chars (fragP->fr_opcode,
+ insn
+ | (((value >> 48) & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Add a SHORI with the midhigh bits. Note that this insn lives
+ in the variable fragment part. */
+ md_number_to_chars (fragP->fr_literal + old_fr_fix,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | (((value >> 32) & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Add a SHORI with the midlow bits. */
+ md_number_to_chars (fragP->fr_literal + old_fr_fix + 4,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | (((value >> 16) & ((1 << 16) - 1)) << 10),
+ 4);
+
+ /* Add a SHORI with the low bits. */
+ md_number_to_chars (fragP->fr_literal + old_fr_fix + 8,
+ SHMEDIA_SHORI_OPC
+ | (reg << 4)
+ | ((value & ((1 << 16) - 1)) << 10), 4);
+ /* We took all of the variable part. */
+ fragP->fr_fix += 12;
+ }
+
+ /* MOVI expansions that get here have not been converted to
+ PC-relative frags, but instead expanded by
+ md_number_to_chars or by calling shmedia_md_convert_frag
+ with final == FALSE. We must not have them around as
+ frags anymore; symbols would be prematurely evaluated
+ when relaxing. We will not need to have md_convert_frag
+ called again with them; any further handling is through
+ the already emitted fixups. */
+ frag_wane (fragP);
+ break;
+ }
+ fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+ break;
+
+ /* For relaxation states that remain unchanged, report the
+ estimated length. */
+ case C (SH64PCREL16_32, SH64PCREL16):
+ case C (SH64PCREL16PT_32, SH64PCREL16):
+ case C (SH64PCREL16_32, SH64PCREL32):
+ case C (SH64PCREL16PT_32, SH64PCREL32):
+ case C (SH64PCREL16_32, SH64PCRELPLT):
+ case C (SH64PCREL16PT_32, SH64PCRELPLT):
+ case C (SH64PCREL16_64, SH64PCREL16):
+ case C (SH64PCREL16PT_64, SH64PCREL16):
+ case C (SH64PCREL16_64, SH64PCREL32):
+ case C (SH64PCREL16PT_64, SH64PCREL32):
+ case C (SH64PCREL16_64, SH64PCREL48):
+ case C (SH64PCREL16PT_64, SH64PCREL48):
+ case C (SH64PCREL16_64, SH64PCREL64):
+ case C (SH64PCREL16PT_64, SH64PCREL64):
+ case C (SH64PCREL16_64, SH64PCRELPLT):
+ case C (SH64PCREL16PT_64, SH64PCRELPLT):
+ case C (MOVI_IMM_32, MOVI_16):
+ case C (MOVI_IMM_32, MOVI_32):
+ case C (MOVI_IMM_32, MOVI_GOTOFF):
+ case C (MOVI_IMM_32_PCREL, MOVI_16):
+ case C (MOVI_IMM_32_PCREL, MOVI_32):
+ case C (MOVI_IMM_32_PCREL, MOVI_PLT):
+ case C (MOVI_IMM_32_PCREL, MOVI_GOTPC):
+ case C (MOVI_IMM_64, MOVI_16):
+ case C (MOVI_IMM_64, MOVI_32):
+ case C (MOVI_IMM_64, MOVI_48):
+ case C (MOVI_IMM_64, MOVI_64):
+ case C (MOVI_IMM_64, MOVI_GOTOFF):
+ case C (MOVI_IMM_64_PCREL, MOVI_16):
+ case C (MOVI_IMM_64_PCREL, MOVI_32):
+ case C (MOVI_IMM_64_PCREL, MOVI_48):
+ case C (MOVI_IMM_64_PCREL, MOVI_64):
+ case C (MOVI_IMM_64_PCREL, MOVI_PLT):
+ case C (MOVI_IMM_64_PCREL, MOVI_GOTPC):
+ fragP->fr_var = md_relax_table[fragP->fr_subtype].rlx_length;
+ break;
+
+ default:
+ abort ();
+ }
+
+ return fragP->fr_var + (fragP->fr_fix - old_fr_fix);
+}
+
+/* Parse an expression, SH64-style. Copied from tc-sh.c, but with
+ datatypes adjusted. */
+
+static char *
+shmedia_parse_exp (char *s, shmedia_operand_info *op)
+{
+ char *save;
+ char *new_pointer;
+
+ save = input_line_pointer;
+ input_line_pointer = s;
+ expression (&op->immediate);
+ if (op->immediate.X_op == O_absent)
+ as_bad (_("missing operand"));
+ new_pointer = input_line_pointer;
+ input_line_pointer = save;
+ return new_pointer;
+}
+
+/* Parse an operand. Store pointer to next character in *PTR. */
+
+static void
+shmedia_get_operand (char **ptr, shmedia_operand_info *op,
+ shmedia_arg_type argtype)
+{
+ char *src = *ptr;
+ int mode = -1;
+ unsigned int len;
+
+ len = shmedia_parse_reg (src, &mode, &(op->reg), argtype);
+ if (len)
+ {
+ *ptr = src + len;
+ op->type = mode;
+ }
+ else
+ {
+ /* Not a reg, so it must be a displacement. */
+ *ptr = shmedia_parse_exp (src, op);
+ op->type = A_IMMM;
+
+ /* This is just an initialization; shmedia_get_operands will change
+ as needed. */
+ op->reloctype = BFD_RELOC_NONE;
+ }
+}
+
+/* Parse the operands for this insn; return NULL if invalid, else return
+ how much text was consumed. */
+
+static char *
+shmedia_get_operands (shmedia_opcode_info *info, char *args,
+ shmedia_operands_info *operands)
+{
+ char *ptr = args;
+ int i;
+
+ if (*ptr == ' ')
+ ptr++;
+
+ for (i = 0; info->arg[i] != 0; i++)
+ {
+ memset (operands->operands + i, 0, sizeof (operands->operands[0]));
+
+ /* No operand to get for these fields. */
+ if (info->arg[i] == A_REUSE_PREV)
+ continue;
+
+ shmedia_get_operand (&ptr, &operands->operands[i], info->arg[i]);
+
+ /* Check operands type match. */
+ switch (info->arg[i])
+ {
+ case A_GREG_M:
+ case A_GREG_N:
+ case A_GREG_D:
+ if (operands->operands[i].type != A_GREG_M)
+ return NULL;
+ break;
+
+ case A_FREG_G:
+ case A_FREG_H:
+ case A_FREG_F:
+ if (operands->operands[i].type != A_FREG_G)
+ return NULL;
+ break;
+
+ case A_FVREG_G:
+ case A_FVREG_H:
+ case A_FVREG_F:
+ if (operands->operands[i].type != A_FVREG_G)
+ return NULL;
+ break;
+
+ case A_FMREG_G:
+ case A_FMREG_H:
+ case A_FMREG_F:
+ if (operands->operands[i].type != A_FMREG_G)
+ return NULL;
+ break;
+
+ case A_FPREG_G:
+ case A_FPREG_H:
+ case A_FPREG_F:
+ if (operands->operands[i].type != A_FPREG_G)
+ return NULL;
+ break;
+
+ case A_DREG_G:
+ case A_DREG_H:
+ case A_DREG_F:
+ if (operands->operands[i].type != A_DREG_G)
+ return NULL;
+ break;
+
+ case A_TREG_A:
+ case A_TREG_B:
+ if (operands->operands[i].type != A_TREG_B)
+ return NULL;
+ break;
+
+ case A_CREG_J:
+ case A_CREG_K:
+ if (operands->operands[i].type != A_CREG_K)
+ return NULL;
+ break;
+
+ case A_IMMS16:
+ case A_IMMU16:
+ /* Check for an expression that looks like S & 65535 or
+ (S >> N) & 65535, where N = 0, 16, 32, 48.
+
+ Get the S and put at operands->operands[i].immediate, and
+ adjust operands->operands[i].reloctype. */
+ {
+ expressionS *imm_expr = &operands->operands[i].immediate;
+ expressionS *right_expr;
+
+ if (operands->operands[i].type == A_IMMM
+ && imm_expr->X_op == O_bit_and
+ && imm_expr->X_op_symbol != NULL
+ && ((right_expr
+ = symbol_get_value_expression (imm_expr->X_op_symbol))
+ ->X_op == O_constant)
+ && right_expr->X_add_number == 0xffff)
+ {
+ symbolS *inner = imm_expr->X_add_symbol;
+ bfd_reloc_code_real_type reloctype = BFD_RELOC_SH_IMM_LOW16;
+ expressionS *inner_expr
+ = symbol_get_value_expression (inner);
+
+ if (inner_expr->X_op == O_right_shift)
+ {
+ expressionS *inner_right;
+
+ if (inner_expr->X_op_symbol != NULL
+ && ((inner_right
+ = symbol_get_value_expression (inner_expr
+ ->X_op_symbol))
+ ->X_op == O_constant))
+ {
+ offsetT addnum
+ = inner_right->X_add_number;
+
+ if (addnum == 0 || addnum == 16 || addnum == 32
+ || addnum == 48)
+ {
+ reloctype
+ = (addnum == 0
+ ? BFD_RELOC_SH_IMM_LOW16
+ : (addnum == 16
+ ? BFD_RELOC_SH_IMM_MEDLOW16
+ : (addnum == 32
+ ? BFD_RELOC_SH_IMM_MEDHI16
+ : BFD_RELOC_SH_IMM_HI16)));
+
+ inner = inner_expr->X_add_symbol;
+ inner_expr = symbol_get_value_expression (inner);
+ }
+ }
+ }
+
+ /* I'm not sure I understand the logic, but evidently the
+ inner expression of a lone symbol is O_constant, with
+ the actual symbol in expr_section. For a constant, the
+ section would be absolute_section. For sym+offset,
+ it's O_symbol as always. See expr.c:make_expr_symbol,
+ first statements. */
+
+ if (inner_expr->X_op == O_constant
+ && S_GET_SEGMENT (inner) != absolute_section)
+ {
+ operands->operands[i].immediate.X_op = O_symbol;
+ operands->operands[i].immediate.X_add_symbol = inner;
+ operands->operands[i].immediate.X_add_number = 0;
+ }
+ else
+ operands->operands[i].immediate
+ = *symbol_get_value_expression (inner);
+
+ operands->operands[i].reloctype = reloctype;
+ }
+ }
+ /* Fall through. */
+ case A_IMMS6:
+ case A_IMMS6BY32:
+ case A_IMMS10:
+ case A_IMMS10BY1:
+ case A_IMMS10BY2:
+ case A_IMMS10BY4:
+ case A_IMMS10BY8:
+ case A_PCIMMS16BY4:
+ case A_PCIMMS16BY4_PT:
+ case A_IMMU5:
+ case A_IMMU6:
+ if (operands->operands[i].type != A_IMMM)
+ return NULL;
+
+ if (sh_check_fixup (&operands->operands[i].immediate,
+ &operands->operands[i].reloctype))
+ {
+ as_bad (_("invalid PIC reference"));
+ return NULL;
+ }
+
+ break;
+
+ default:
+ BAD_CASE (info->arg[i]);
+ }
+
+ if (*ptr == ',' && info->arg[i + 1])
+ ptr++;
+ }
+ return ptr;
+}
+
+
+/* Find an opcode at the start of *STR_P in the hash table, and set
+ *STR_P to the first character after the last one read. */
+
+static shmedia_opcode_info *
+shmedia_find_cooked_opcode (char **str_p)
+{
+ char *str = *str_p;
+ char *op_start;
+ char *op_end;
+ char name[20];
+ unsigned int nlen = 0;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the op code end. */
+ for (op_start = op_end = str;
+ *op_end
+ && nlen < sizeof (name) - 1
+ && ! is_end_of_line[(unsigned char) *op_end]
+ && ! ISSPACE ((unsigned char) *op_end);
+ op_end++)
+ {
+ unsigned char c = op_start[nlen];
+
+ /* The machine independent code will convert CMP/EQ into cmp/EQ
+ because it thinks the '/' is the end of the symbol. Moreover,
+ all but the first sub-insn is a parallel processing insn won't
+ be capitalized. Instead of hacking up the machine independent
+ code, we just deal with it here. */
+ c = TOLOWER (c);
+ name[nlen] = c;
+ nlen++;
+ }
+
+ name[nlen] = 0;
+ *str_p = op_end;
+
+ if (nlen == 0)
+ as_bad (_("can't find opcode"));
+
+ return
+ (shmedia_opcode_info *) hash_find (shmedia_opcode_hash_control, name);
+}
+
+/* Build up an instruction, including allocating the frag. */
+
+static int
+shmedia_build_Mytes (shmedia_opcode_info *opcode,
+ shmedia_operands_info *operands)
+{
+ unsigned long insn = opcode->opcode_base;
+ int i, j;
+ char *insn_loc = frag_more (4);
+
+ /* 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 we want .debug_line addresses to record (address | 1) for
+ SHmedia insns, we get the wanted effect by taking one off the size,
+ knowing it's a multiple of 4. We count from the first fix piece of
+ the insn. There must be no frags changes (frag_more or frag_var)
+ calls in-between the frag_more call we account for, and this
+ dwarf2_emit_insn call. */
+ dwarf2_emit_insn (3);
+
+ /* This is stored into any frag_var operand. */
+ sh64_last_insn_frag = frag_now;
+
+ /* Loop over opcode info, emit an instruction. */
+ for (i = 0, j = 0; opcode->arg[i]; i++)
+ {
+ shmedia_arg_type argtype = opcode->arg[i];
+ shmedia_operand_info *opjp = &operands->operands[j];
+ switch (argtype)
+ {
+ case A_TREG_A:
+ case A_TREG_B:
+ case A_GREG_M:
+ case A_GREG_N:
+ case A_GREG_D:
+ case A_FREG_G:
+ case A_FREG_H:
+ case A_FREG_F:
+ case A_FVREG_G:
+ case A_FVREG_H:
+ case A_FVREG_F:
+ case A_FMREG_G:
+ case A_FMREG_H:
+ case A_FMREG_F:
+ case A_FPREG_G:
+ case A_FPREG_H:
+ case A_FPREG_F:
+ case A_DREG_G:
+ case A_DREG_H:
+ case A_DREG_F:
+ case A_CREG_J:
+ case A_CREG_K:
+ /* Six-bit register fields. They just get filled with the
+ parsed register number. */
+ insn |= (opjp->reg << opcode->nibbles[i]);
+ j++;
+ break;
+
+ case A_REUSE_PREV:
+ /* Copy the register for the previous operand to this position. */
+ insn |= (operands->operands[j - 1].reg << opcode->nibbles[i]);
+ j++;
+ break;
+
+ case A_IMMS6:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMS6);
+ j++;
+ break;
+
+ case A_IMMS6BY32:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMS6BY32);
+ j++;
+ break;
+
+ case A_IMMS10BY1:
+ case A_IMMS10:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMS10);
+ j++;
+ break;
+
+ case A_IMMS10BY2:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMS10BY2);
+ j++;
+ break;
+
+ case A_IMMS10BY4:
+ if (opjp->reloctype == BFD_RELOC_NONE)
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMS10BY4);
+ else if (opjp->reloctype == BFD_RELOC_SH_GOTPLT32)
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_GOTPLT10BY4);
+ else if (opjp->reloctype == BFD_RELOC_32_GOT_PCREL)
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_GOT10BY4);
+ else
+ as_bad (_("invalid PIC reference"));
+ j++;
+ break;
+
+ case A_IMMS10BY8:
+ if (opjp->reloctype == BFD_RELOC_NONE)
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMS10BY8);
+ else if (opjp->reloctype == BFD_RELOC_SH_GOTPLT32)
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_GOTPLT10BY8);
+ else if (opjp->reloctype == BFD_RELOC_32_GOT_PCREL)
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_GOT10BY8);
+ else
+ as_bad (_("invalid PIC reference"));
+ j++;
+ break;
+
+ case A_IMMS16:
+ /* Sneak a peek if this is the MOVI insn. If so, check if we
+ should expand it. */
+ if (opjp->reloctype == BFD_RELOC_32_GOT_PCREL)
+ opjp->reloctype = BFD_RELOC_SH_GOT_LOW16;
+ else if (opjp->reloctype == BFD_RELOC_SH_GOTPLT32)
+ opjp->reloctype = BFD_RELOC_SH_GOTPLT_LOW16;
+
+ if ((opjp->reloctype == BFD_RELOC_NONE
+ || opjp->reloctype == BFD_RELOC_32_GOTOFF
+ || opjp->reloctype == BFD_RELOC_32_PLT_PCREL
+ || opjp->reloctype == BFD_RELOC_SH_GOTPC)
+ && opcode->opcode_base == SHMEDIA_MOVI_OPC
+ && (opjp->immediate.X_op != O_constant
+ || opjp->immediate.X_add_number < -32768
+ || opjp->immediate.X_add_number > 32767)
+ && (sh64_expand
+ || opjp->reloctype == BFD_RELOC_32_GOTOFF
+ || opjp->reloctype == BFD_RELOC_32_PLT_PCREL
+ || opjp->reloctype == BFD_RELOC_SH_GOTPC))
+ {
+ int what = sh64_abi == sh64_abi_64 ? MOVI_IMM_64 : MOVI_IMM_32;
+ offsetT max = sh64_abi == sh64_abi_64 ? MOVI_64 : MOVI_32;
+ offsetT min = MOVI_16;
+ offsetT init = UNDEF_MOVI;
+ valueT addvalue
+ = opjp->immediate.X_op_symbol != NULL
+ ? 0 : opjp->immediate.X_add_number;
+ symbolS *sym
+ = opjp->immediate.X_op_symbol != NULL
+ ? make_expr_symbol (&opjp->immediate)
+ : opjp->immediate.X_add_symbol;
+
+ if (opjp->reloctype == BFD_RELOC_32_GOTOFF)
+ init = max = min = MOVI_GOTOFF;
+ else if (opjp->reloctype == BFD_RELOC_32_PLT_PCREL)
+ {
+ init = max = min = MOVI_PLT;
+ what = (sh64_abi == sh64_abi_64
+ ? MOVI_IMM_64_PCREL
+ : MOVI_IMM_32_PCREL);
+ }
+ else if (opjp->reloctype == BFD_RELOC_SH_GOTPC)
+ {
+ init = max = min = MOVI_GOTPC;
+ what = (sh64_abi == sh64_abi_64
+ ? MOVI_IMM_64_PCREL
+ : MOVI_IMM_32_PCREL);
+ }
+
+ frag_var (rs_machine_dependent,
+ md_relax_table[C (what, max)].rlx_length,
+ md_relax_table[C (what, min)].rlx_length,
+ C (what, init), sym, addvalue, insn_loc);
+ }
+ else
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ (opjp->reloctype
+ == BFD_RELOC_NONE)
+ ? BFD_RELOC_SH_IMMS16
+ : opjp->reloctype);
+ j++;
+ break;
+
+ case A_PCIMMS16BY4:
+ {
+ int what
+ = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+ ? SH64PCREL16_64 : SH64PCREL16_32);
+ offsetT max
+ = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+ ? SH64PCREL64 : SH64PCREL32);
+ offsetT min = SH64PCREL16;
+ offsetT init = UNDEF_SH64PCREL;
+
+ /* Don't allow complex expressions here. */
+ if (opjp->immediate.X_op_symbol != NULL)
+ {
+ as_bad (_("invalid operand: expression in PT target"));
+ return 0;
+ }
+
+ if (opjp->reloctype == BFD_RELOC_32_PLT_PCREL)
+ init = max = min = SH64PCRELPLT;
+
+ /* If we're not expanding, then just emit a fixup. */
+ if (sh64_expand || opjp->reloctype != BFD_RELOC_NONE)
+ frag_var (rs_machine_dependent,
+ md_relax_table[C (what, max)].rlx_length,
+ md_relax_table[C (what, min)].rlx_length,
+ C (what, init),
+ opjp->immediate.X_add_symbol,
+ opjp->immediate.X_add_number,
+ insn_loc);
+ else
+ insn |= shmedia_immediate_op (insn_loc, opjp, 1,
+ opjp->reloctype == BFD_RELOC_NONE
+ ? BFD_RELOC_SH_PT_16
+ : opjp->reloctype);
+
+ j++;
+ break;
+ }
+
+ case A_PCIMMS16BY4_PT:
+ {
+ int what
+ = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+ ? SH64PCREL16PT_64 : SH64PCREL16PT_32);
+ offsetT max
+ = ((sh64_abi == sh64_abi_64 && ! sh64_pt32)
+ ? SH64PCREL64 : SH64PCREL32);
+ offsetT min = SH64PCREL16;
+ offsetT init = UNDEF_SH64PCREL;
+
+ /* Don't allow complex expressions here. */
+ if (opjp->immediate.X_op_symbol != NULL)
+ {
+ as_bad (_("invalid operand: expression in PT target"));
+ return 0;
+ }
+
+ if (opjp->reloctype == BFD_RELOC_32_PLT_PCREL)
+ init = max = min = SH64PCRELPLT;
+
+ /* If we're not expanding, then just emit a fixup. */
+ if (sh64_expand || opjp->reloctype != BFD_RELOC_NONE)
+ frag_var (rs_machine_dependent,
+ md_relax_table[C (what, max)].rlx_length,
+ md_relax_table[C (what, min)].rlx_length,
+ C (what, init),
+ opjp->immediate.X_add_symbol,
+ opjp->immediate.X_add_number,
+ insn_loc);
+ else
+ /* This reloc-type is just temporary, so we can distinguish
+ PTA from PT. It is changed in shmedia_md_apply_fix to
+ BFD_RELOC_SH_PT_16. */
+ insn |= shmedia_immediate_op (insn_loc, opjp, 1,
+ opjp->reloctype == BFD_RELOC_NONE
+ ? SHMEDIA_BFD_RELOC_PT
+ : opjp->reloctype);
+
+ j++;
+ break;
+ }
+
+ case A_IMMU5:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMU5);
+ j++;
+ break;
+
+ case A_IMMU6:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ BFD_RELOC_SH_IMMU6);
+ j++;
+ break;
+
+ case A_IMMU16:
+ insn |= shmedia_immediate_op (insn_loc, opjp, 0,
+ (opjp->reloctype
+ == BFD_RELOC_NONE)
+ ? BFD_RELOC_SH_IMMU16
+ : opjp->reloctype);
+ j++;
+ break;
+
+ default:
+ BAD_CASE (argtype);
+ }
+ }
+
+ md_number_to_chars (insn_loc, insn, 4);
+ return 4;
+}
+
+/* Assemble a SHmedia instruction. */
+
+static void
+shmedia_md_assemble (char *str)
+{
+ char *op_end;
+ shmedia_opcode_info *opcode;
+ shmedia_operands_info operands;
+ int size;
+
+ opcode = shmedia_find_cooked_opcode (&str);
+ op_end = str;
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ /* Start a SHmedia code region, if there has been pseudoinsns or similar
+ seen since the last one. */
+ if (!seen_insn)
+ {
+ sh64_update_contents_mark (TRUE);
+ sh64_set_contents_type (CRT_SH5_ISA32);
+ seen_insn = TRUE;
+ }
+
+ op_end = shmedia_get_operands (opcode, op_end, &operands);
+
+ if (op_end == NULL)
+ {
+ as_bad (_("invalid operands to %s"), opcode->name);
+ return;
+ }
+
+ if (*op_end)
+ {
+ as_bad (_("excess operands to %s"), opcode->name);
+ return;
+ }
+
+ size = shmedia_build_Mytes (opcode, &operands);
+ if (size == 0)
+ return;
+}
+
+/* Hook called from md_begin in tc-sh.c. */
+
+void
+shmedia_md_begin (void)
+{
+ const shmedia_opcode_info *shmedia_opcode;
+ shmedia_opcode_hash_control = hash_new ();
+
+ /* Create opcode table for SHmedia mnemonics. */
+ for (shmedia_opcode = shmedia_table;
+ shmedia_opcode->name;
+ shmedia_opcode++)
+ hash_insert (shmedia_opcode_hash_control, shmedia_opcode->name,
+ (char *) shmedia_opcode);
+}
+
+/* Switch instruction set. Only valid if one of the --isa or --abi
+ options was specified. */
+
+static void
+s_sh64_mode (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ /* Make sure data up to this location is handled according to the
+ previous ISA. */
+ sh64_update_contents_mark (TRUE);
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ /* If the mode was not set before, explicitly or implicitly, then we're
+ not emitting SH64 code, so this pseudo is invalid. */
+ if (sh64_isa_mode == sh64_isa_unspecified)
+ as_bad (_("The `.mode %s' directive is not valid with this architecture"),
+ name);
+
+ if (strcasecmp (name, "shcompact") == 0)
+ sh64_isa_mode = sh64_isa_shcompact;
+ else if (strcasecmp (name, "shmedia") == 0)
+ sh64_isa_mode = sh64_isa_shmedia;
+ else
+ as_bad (_("Invalid argument to .mode: %s"), name);
+
+ /* Make a new frag, marking it with the supposedly-changed ISA. */
+ frag_wane (frag_now);
+ frag_new (0);
+
+ /* Contents type up to this new point is the same as before; don't add a
+ data region just because the new frag we created. */
+ sh64_update_contents_mark (FALSE);
+
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* Check that the right ABI is used. Only valid if one of the --isa or
+ --abi options was specified. */
+
+static void
+s_sh64_abi (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer, ch;
+
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ input_line_pointer++;
+ ch = *input_line_pointer;
+ *input_line_pointer = '\0';
+
+ /* If the mode was not set before, explicitly or implicitly, then we're
+ not emitting SH64 code, so this pseudo is invalid. */
+ if (sh64_abi == sh64_abi_unspecified)
+ as_bad (_("The `.abi %s' directive is not valid with this architecture"),
+ name);
+
+ if (strcmp (name, "64") == 0)
+ {
+ if (sh64_abi != sh64_abi_64)
+ as_bad (_("`.abi 64' but command-line options do not specify 64-bit ABI"));
+ }
+ else if (strcmp (name, "32") == 0)
+ {
+ if (sh64_abi != sh64_abi_32)
+ as_bad (_("`.abi 32' but command-line options do not specify 32-bit ABI"));
+ }
+ else
+ as_bad (_("Invalid argument to .abi: %s"), name);
+
+ *input_line_pointer = ch;
+ demand_empty_rest_of_line ();
+}
+
+/* This function is the first target-specific function called after
+ parsing command-line options. Therefore we set default values from
+ command-line options here and do some sanity checking we couldn't do
+ when options were being parsed. */
+
+const char *
+sh64_target_format (void)
+{
+#ifdef TE_NetBSD
+ /* For NetBSD, if the ISA is unspecified, always use SHmedia. */
+ if (preset_target_arch == 0 && sh64_isa_mode == sh64_isa_unspecified)
+ sh64_isa_mode = sh64_isa_shmedia;
+
+ /* If the ABI is unspecified, select a default: based on how
+ we were configured: sh64 == sh64_abi_64, else sh64_abi_32. */
+ if (sh64_abi == sh64_abi_unspecified)
+ {
+ if (preset_target_arch != 0 || sh64_isa_mode == sh64_isa_shcompact)
+ sh64_abi = sh64_abi_32;
+ else if (strncmp (TARGET_CPU, "sh64", 4) == 0)
+ sh64_abi = sh64_abi_64;
+ else
+ sh64_abi = sh64_abi_32;
+ }
+#endif
+
+#ifdef TE_LINUX
+ if (preset_target_arch == 0 && sh64_isa_mode == sh64_isa_unspecified)
+ sh64_isa_mode = sh64_isa_shmedia;
+
+ if (sh64_abi == sh64_abi_unspecified)
+ sh64_abi = sh64_abi_32;
+#endif
+
+ if (sh64_abi == sh64_abi_64 && sh64_isa_mode == sh64_isa_unspecified)
+ sh64_isa_mode = sh64_isa_shmedia;
+
+ if (sh64_abi == sh64_abi_32 && sh64_isa_mode == sh64_isa_unspecified)
+ sh64_isa_mode = sh64_isa_shcompact;
+
+ if (sh64_isa_mode == sh64_isa_shcompact
+ && sh64_abi == sh64_abi_unspecified)
+ sh64_abi = sh64_abi_32;
+
+ if (sh64_isa_mode == sh64_isa_shmedia
+ && sh64_abi == sh64_abi_unspecified)
+ sh64_abi = sh64_abi_64;
+
+ if (sh64_isa_mode == sh64_isa_unspecified && ! sh64_mix)
+ as_bad (_("-no-mix is invalid without specifying SHcompact or SHmedia"));
+
+ if ((sh64_isa_mode == sh64_isa_unspecified
+ || sh64_isa_mode == sh64_isa_shmedia)
+ && sh64_shcompact_const_crange)
+ as_bad (_("-shcompact-const-crange is invalid without SHcompact"));
+
+ if (sh64_pt32 && sh64_abi != sh64_abi_64)
+ as_bad (_("-expand-pt32 only valid with -abi=64"));
+
+ if (! sh64_expand && sh64_isa_mode == sh64_isa_unspecified)
+ as_bad (_("-no-expand only valid with SHcompact or SHmedia"));
+
+ if (sh64_pt32 && ! sh64_expand)
+ as_bad (_("-expand-pt32 invalid together with -no-expand"));
+
+#ifdef TE_NetBSD
+ if (sh64_abi == sh64_abi_64)
+ return (target_big_endian ? "elf64-sh64-nbsd" : "elf64-sh64l-nbsd");
+ else
+ return (target_big_endian ? "elf32-sh64-nbsd" : "elf32-sh64l-nbsd");
+#elif defined (TE_LINUX)
+ if (sh64_abi == sh64_abi_64)
+ return (target_big_endian ? "elf64-sh64big-linux" : "elf64-sh64-linux");
+ else
+ return (target_big_endian ? "elf32-sh64big-linux" : "elf32-sh64-linux");
+#else
+ /* When the ISA is not one of SHmedia or SHcompact, use the old SH
+ object format. */
+ if (sh64_isa_mode == sh64_isa_unspecified)
+ return (target_big_endian ? "elf32-sh" : "elf32-shl");
+ else if (sh64_abi == sh64_abi_64)
+ return (target_big_endian ? "elf64-sh64" : "elf64-sh64l");
+ else
+ return (target_big_endian ? "elf32-sh64" : "elf32-sh64l");
+#endif
+}
+
+/* The worker function of TARGET_MACH. */
+
+int
+sh64_target_mach (void)
+{
+ /* We need to explicitly set bfd_mach_sh5 instead of the default 0. But
+ we only do this for the 64-bit ABI: if we do it for the 32-bit ABI,
+ the SH5 info in the bfd_arch_info structure will be selected.
+ However correct, as the machine has 64-bit addresses, functions
+ expected to emit 32-bit data for addresses will start failing. For
+ example, the dwarf2dbg.c functions will emit 64-bit debugging format,
+ and we don't want that in the 32-bit ABI.
+
+ We could have two bfd_arch_info structures for SH64; one for the
+ 32-bit ABI and one for the rest (64-bit ABI). But that would be a
+ bigger kludge: it's a flaw in the BFD design, and we need to just
+ work around it by having the default machine set here in the
+ assembler. For everything else but the assembler, the various bfd
+ functions will set the machine type right to bfd_mach_sh5 from object
+ file header flags regardless of the 0 here. */
+
+ return (sh64_abi == sh64_abi_64) ? bfd_mach_sh5 : 0;
+}
+
+/* This is MD_PCREL_FROM_SECTION, we we define so it is called instead of
+ md_pcrel_from (in tc-sh.c). */
+
+valueT
+shmedia_md_pcrel_from_section (struct fix *fixP, segT sec ATTRIBUTE_UNUSED)
+{
+ /* Use the ISA for the instruction to decide which offset to use. We
+ can glean it from the fisup type. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_SH_IMM_LOW16:
+ case BFD_RELOC_SH_IMM_MEDLOW16:
+ case BFD_RELOC_SH_IMM_MEDHI16:
+ case BFD_RELOC_SH_IMM_HI16:
+ case BFD_RELOC_SH_IMM_LOW16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDLOW16_PCREL:
+ case BFD_RELOC_SH_IMM_MEDHI16_PCREL:
+ case BFD_RELOC_SH_IMM_HI16_PCREL:
+ case BFD_RELOC_SH_IMMU5:
+ case BFD_RELOC_SH_IMMU6:
+ case BFD_RELOC_SH_IMMS6:
+ case BFD_RELOC_SH_IMMS10:
+ case BFD_RELOC_SH_IMMS10BY2:
+ case BFD_RELOC_SH_IMMS10BY4:
+ case BFD_RELOC_SH_IMMS10BY8:
+ case BFD_RELOC_SH_IMMS16:
+ case BFD_RELOC_SH_IMMU16:
+ case BFD_RELOC_SH_PT_16:
+ case SHMEDIA_BFD_RELOC_PT:
+ /* PC-relative relocs are relative to the address of the last generated
+ instruction, i.e. fx_size - 4. */
+ return SHMEDIA_MD_PCREL_FROM_FIX (fixP);
+
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ /* Fall through. */
+
+ default:
+ /* If section was SHcompact, use its function. */
+ return (valueT) md_pcrel_from_section (fixP, sec);
+ }
+
+ know (0 /* Shouldn't get here. */);
+ return 0;
+}
+
+/* Create one .cranges descriptor from two symbols, STARTSYM marking begin
+ and ENDSYM marking end, and CR_TYPE specifying the type. */
+
+static void
+sh64_emit_crange (symbolS *startsym, symbolS *endsym,
+ enum sh64_elf_cr_type cr_type)
+{
+ expressionS exp;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ asection *cranges
+ = bfd_make_section_old_way (stdoutput,
+ SH64_CRANGES_SECTION_NAME);
+
+ /* Temporarily change to the .cranges section. */
+ subseg_set (cranges, 0);
+
+ /* Emit the cr_addr part. */
+ exp.X_op = O_symbol;
+ exp.X_add_number = 0;
+ exp.X_op_symbol = NULL;
+ exp.X_add_symbol = startsym;
+ emit_expr (&exp, 4);
+
+ /* Emit the cr_size part. */
+ exp.X_op = O_subtract;
+ exp.X_add_number = 0;
+ exp.X_add_symbol = endsym;
+ exp.X_op_symbol = startsym;
+ emit_expr (&exp, 4);
+
+ /* Emit the cr_size part. */
+ exp.X_op = O_constant;
+ exp.X_add_number = cr_type;
+ exp.X_add_symbol = NULL;
+ exp.X_op_symbol = NULL;
+ emit_expr (&exp, 2);
+
+ /* Now back to our regular program. */
+ subseg_set (current_seg, current_subseg);
+}
+
+/* Called when the assembler is about to emit contents of some type into
+ SEG, so it is *known* that the type of that new contents is in
+ NEW_CONTENTS_TYPE. If just switching back and forth between different
+ contents types (for example, with consecutive .mode pseudos), then this
+ function isn't called. */
+
+static void
+sh64_set_contents_type (enum sh64_elf_cr_type new_contents_type)
+{
+ segment_info_type *seginfo;
+
+ /* We will not be called when emitting .cranges output, since callers
+ stop that. Validize that assumption. */
+ know (!emitting_crange);
+
+ seginfo = seg_info (now_seg);
+
+ if (seginfo)
+ {
+ symbolS *symp = seginfo->tc_segment_info_data.last_contents_mark;
+
+ enum sh64_elf_cr_type contents_type
+ = seginfo->tc_segment_info_data.contents_type;
+
+ /* If it was just SHcompact switching between code and constant
+ pool, don't change contents type. Just make sure we don't set
+ the contents type to data, as that would join with a data-region
+ in SHmedia mode. */
+ if (sh64_isa_mode == sh64_isa_shcompact
+ && ! sh64_shcompact_const_crange)
+ new_contents_type = CRT_SH5_ISA16;
+
+ /* If nothing changed, stop here. */
+ if (contents_type == new_contents_type)
+ return;
+
+ /* If we're in 64-bit ABI mode, we do not emit .cranges, as it is
+ only specified for 32-bit addresses. It could presumably be
+ extended, but in 64-bit ABI mode we don't have SHcompact code, so
+ we would only use it to mark code and data. */
+ if (sh64_abi == sh64_abi_64)
+ {
+ /* Make the code type "sticky". We don't want to set the
+ sections contents type to data if there's any code in it as
+ we don't have .cranges in 64-bit mode to notice the
+ difference. */
+ seginfo->tc_segment_info_data.contents_type
+ = (new_contents_type == CRT_SH5_ISA32
+ || contents_type == CRT_SH5_ISA32)
+ ? CRT_SH5_ISA32 : new_contents_type;
+ return;
+ }
+
+ /* If none was marked, create a start symbol for this range and
+ perhaps as a closing symbol for the old one. */
+ if (symp == NULL)
+ symp = symbol_new (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (),
+ frag_now);
+
+ /* We will use this symbol, so don't leave a pointer behind. */
+ seginfo->tc_segment_info_data.last_contents_mark = NULL;
+
+ /* We'll be making only datalabel references to it, if we emit a
+ .cranges descriptor, so remove any code flag. */
+ S_SET_OTHER (symp, S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+
+ /* If we have already marked the start of a range, we need to close
+ and emit it before marking a new one, so emit a new .cranges
+ descriptor into the .cranges section. */
+ if (seginfo->tc_segment_info_data.mode_start_symbol)
+ {
+ /* If we're not supposed to emit mixed-mode sections, make it an
+ error, but continue processing. */
+ if (! sh64_mix
+ && (new_contents_type == CRT_SH5_ISA32
+ || contents_type == CRT_SH5_ISA32))
+ as_bad (
+_("SHmedia code not allowed in same section as constants and SHcompact code"));
+
+ emitting_crange = TRUE;
+ sh64_emit_crange (seginfo->tc_segment_info_data.mode_start_symbol,
+ symp, contents_type);
+ emitting_crange = FALSE;
+ seginfo->tc_segment_info_data.emitted_ranges++;
+ }
+
+ seginfo->tc_segment_info_data.mode_start_symbol = symp;
+ seginfo->tc_segment_info_data.mode_start_subseg = now_subseg;
+ seginfo->tc_segment_info_data.contents_type = new_contents_type;
+
+ /* Always reset this, so the SHcompact code will emit a reloc when
+ it prepares to relax. */
+ seginfo->tc_segment_info_data.in_code = 0;
+ }
+ else
+ as_bad (_("No segment info for current section"));
+}
+
+/* Hook when defining symbols and labels. We set the ST_OTHER field if
+ the symbol is "shmedia" (with "bitor 1" automatically applied). Simple
+ semantics for a label being "shmedia" : It was defined when .mode
+ SHmedia was in effect, and it was defined in a code section. It
+ doesn't matter whether or not an assembled opcode is nearby. */
+
+void
+sh64_frob_label (symbolS *symp)
+{
+ segT seg = S_GET_SEGMENT (symp);
+ static const symbolS *null = NULL;
+
+ /* Reset the tc marker for all newly created symbols. */
+ symbol_set_tc (symp, (symbolS **) &null);
+
+ if (seg != NULL && sh64_isa_mode == sh64_isa_shmedia && subseg_text_p (seg))
+ S_SET_OTHER (symp, S_GET_OTHER (symp) | STO_SH5_ISA32);
+}
+
+/* Handle the "datalabel" qualifier. We need to call "operand", but it's
+ static, so a function pointer is passed here instead. FIXME: A target
+ hook for qualifiers is needed; we currently use the md_parse_name
+ symbol hook. */
+
+int
+sh64_consume_datalabel (const char *name, expressionS *exp,
+ enum expr_mode mode, char *cp,
+ segT (*operandf) (expressionS *, enum expr_mode))
+{
+ static int parsing_datalabel = 0;
+
+ if (strcasecmp (name, "datalabel") == 0)
+ {
+ int save_parsing_datalabel = parsing_datalabel;
+
+ if (parsing_datalabel)
+ as_bad (_("duplicate datalabel operator ignored"));
+
+ *input_line_pointer = *cp;
+ parsing_datalabel = 1;
+ (*operandf) (exp, expr_normal);
+ parsing_datalabel = save_parsing_datalabel;
+
+ if (exp->X_op == O_symbol || exp->X_op == O_PIC_reloc)
+ {
+ symbolS *symp = exp->X_add_symbol;
+ segT symseg = S_GET_SEGMENT (symp);
+
+ /* If the symbol is defined to something that is already a
+ datalabel, we don't need to bother with any special handling. */
+ if (symseg != undefined_section
+ && S_GET_OTHER (symp) != STO_SH5_ISA32)
+ /* Do nothing. */
+ ;
+ else
+ {
+ symbolS *dl_symp;
+ const char * sname = S_GET_NAME (symp);
+ char *dl_name
+ = xmalloc (strlen (sname) + sizeof (DATALABEL_SUFFIX));
+
+ /* Now we copy the datalabel-qualified symbol into a symbol
+ with the same name, but with " DL" appended. We mark the
+ symbol using the TC_SYMFIELD_TYPE field with a pointer to
+ the main symbol, so we don't have to inspect all symbol
+ names. Note that use of "datalabel" is not expected to
+ be a common case. */
+ strcpy (dl_name, sname);
+ strcat (dl_name, DATALABEL_SUFFIX);
+
+ /* A FAKE_LABEL_NAME marks "$" or ".". There can be any
+ number of them and all have the same (faked) name; we
+ must make a new one each time. */
+ if (strcmp (sname, FAKE_LABEL_NAME) == 0)
+ dl_symp = symbol_make (dl_name);
+ else
+ dl_symp = symbol_find_or_make (dl_name);
+
+ free (dl_name);
+ symbol_set_value_expression (dl_symp,
+ symbol_get_value_expression (symp));
+ S_SET_SEGMENT (dl_symp, symseg);
+ symbol_set_frag (dl_symp, symbol_get_frag (symp));
+ symbol_set_tc (dl_symp, &symp);
+ copy_symbol_attributes (dl_symp, symp);
+ exp->X_add_symbol = dl_symp;
+
+ /* Unset the BranchTarget mark that can be set at symbol
+ creation or attributes copying. */
+ S_SET_OTHER (dl_symp, S_GET_OTHER (dl_symp) & ~STO_SH5_ISA32);
+
+ /* The GLOBAL and WEAK attributes are not copied over by
+ copy_symbol_attributes. Do it here. */
+ if (S_IS_WEAK (symp))
+ S_SET_WEAK (dl_symp);
+ else if (S_IS_EXTERNAL (symp))
+ S_SET_EXTERNAL (dl_symp);
+ }
+ }
+ /* Complain about other types of operands than symbol, unless they
+ have already been complained about. A constant is always a
+ datalabel. Removing the low bit would therefore be wrong.
+ Complaining about it would also be wrong. */
+ else if (exp->X_op != O_illegal
+ && exp->X_op != O_absent
+ && exp->X_op != O_constant)
+ as_bad (_("Invalid DataLabel expression"));
+
+ *cp = *input_line_pointer;
+
+ return 1;
+ }
+
+ return sh_parse_name (name, exp, mode, cp);
+}
+
+/* This function is called just before symbols are being output. It
+ returns zero when a symbol must be output, non-zero otherwise.
+ Datalabel references that were fully resolved to local symbols are not
+ necessary to output. We also do not want to output undefined symbols
+ that are not used in relocs. For symbols that are used in a reloc, it
+ does not matter what we set here. If it is *not* used in a reloc, then
+ it was probably the datalabel counterpart that was used in a reloc;
+ then we need not output the main symbol. */
+
+int
+sh64_exclude_symbol (symbolS *symp)
+{
+ symbolS *main_symbol = *symbol_get_tc (symp);
+
+ return main_symbol != NULL || ! S_IS_DEFINED (symp);
+}
+
+/* If we haven't seen an insn since the last update, and location
+ indicators have moved (a new frag, new location within frag) we have
+ emitted data, so change contents type to data. Forget that we have
+ seen a sequence of insns and store the current location so we can mark
+ a new region if needed. */
+
+static void
+sh64_update_contents_mark (bfd_boolean update_type)
+{
+ segment_info_type *seginfo;
+ seginfo = seg_info (now_seg);
+
+ if (seginfo != NULL)
+ {
+ symbolS *symp = seginfo->tc_segment_info_data.last_contents_mark;
+
+ if (symp == NULL)
+ {
+ symp = symbol_new (FAKE_LABEL_NAME, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ seginfo->tc_segment_info_data.last_contents_mark = symp;
+ }
+ else
+ {
+ /* If we have moved location since last flush, we need to emit a
+ data range. The previous contents type ended at the location
+ of the last update. */
+ if ((S_GET_VALUE (symp) != frag_now_fix ()
+ || symbol_get_frag (symp) != frag_now))
+ {
+ enum sh64_elf_cr_type contents_type
+ = seginfo->tc_segment_info_data.contents_type;
+
+ if (update_type
+ && contents_type != CRT_DATA
+ && contents_type != CRT_NONE
+ && ! seen_insn)
+ {
+ sh64_set_contents_type (CRT_DATA);
+ symp = seginfo->tc_segment_info_data.last_contents_mark;
+ }
+
+ /* If the symbol wasn't used up to make up a new range
+ descriptor, update it to this new location. */
+ if (symp)
+ {
+ S_SET_VALUE (symp, (valueT) frag_now_fix ());
+ symbol_set_frag (symp, frag_now);
+ }
+ }
+ }
+ }
+
+ seen_insn = FALSE;
+}
+
+/* Called when the assembler is about to output some data, or maybe it's
+ just switching segments. */
+
+void
+sh64_flush_pending_output (void)
+{
+ sh64_update_contents_mark (TRUE);
+ sh_flush_pending_output ();
+}
+
+/* Flush out the last crange descriptor after all insns have been emitted. */
+
+static void
+sh64_flush_last_crange (bfd *abfd ATTRIBUTE_UNUSED, asection *seg,
+ void *countparg ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo;
+
+ seginfo = seg_info (seg);
+
+ if (seginfo
+ /* Only emit .cranges descriptors if we would make it more than one. */
+ && seginfo->tc_segment_info_data.emitted_ranges != 0)
+ {
+ symbolS *symp;
+
+ /* We need a closing symbol, so switch to the indicated section and
+ emit it. */
+
+ /* Change to the section we're about to handle. */
+ subseg_set (seg, seginfo->tc_segment_info_data.mode_start_subseg);
+
+ symp = symbol_new (FAKE_LABEL_NAME, now_seg, (valueT) frag_now_fix (),
+ frag_now);
+
+ /* We'll be making a datalabel reference to it, so remove any code
+ flag. */
+ S_SET_OTHER (symp, S_GET_OTHER (symp) & ~STO_SH5_ISA32);
+
+ sh64_emit_crange (seginfo->tc_segment_info_data.mode_start_symbol,
+ symp,
+ seginfo->tc_segment_info_data.contents_type);
+ }
+}
+
+/* If and only if we see a call to md_number_to_chars without flagging the
+ start of an insn, we set the contents type to CRT_DATA, and only when
+ in SHmedia mode. Note that by default we don't bother changing when
+ going from SHcompact to data, as the constant pools in GCC-generated
+ SHcompact code would create an inordinate amount of .cranges
+ descriptors. */
+
+static void
+sh64_flag_output (void)
+{
+ if (sh64_isa_mode != sh64_isa_unspecified
+ && !seen_insn
+ && !sh64_end_of_assembly
+ && !emitting_crange)
+ {
+ md_flush_pending_output ();
+ sh64_set_contents_type (CRT_DATA);
+ }
+}
+
+/* Vtables don't need "datalabel" but we allow it by simply deleting
+ any we find. */
+
+static char *
+strip_datalabels (void)
+{
+ char *src, *dest, *start=input_line_pointer;
+
+ for (src=input_line_pointer, dest=input_line_pointer; *src != '\n'; )
+ {
+ if (strncasecmp (src, "datalabel", 9) == 0
+ && ISSPACE (src[9])
+ && (src == start || !(ISALNUM (src[-1])) || src[-1] == '_'))
+ src += 10;
+ else
+ *dest++ = *src++;
+ }
+
+ if (dest < src)
+ *dest = '\n';
+ return src + 1;
+}
+
+static void
+sh64_vtable_entry (int ignore ATTRIBUTE_UNUSED)
+{
+ char *eol = strip_datalabels ();
+
+ obj_elf_vtable_entry (0);
+ input_line_pointer = eol;
+}
+
+static void
+sh64_vtable_inherit (int ignore ATTRIBUTE_UNUSED)
+{
+ char *eol = strip_datalabels ();
+
+ obj_elf_vtable_inherit (0);
+ input_line_pointer = eol;
+}
+
+int
+sh64_fake_label (const char *name)
+{
+ size_t len;
+
+ if (strcmp (name, FAKE_LABEL_NAME) == 0)
+ return 1;
+
+ len = strlen (name);
+ if (len >= (sizeof (DATALABEL_SUFFIX) - 1))
+ return strcmp (&name [len - sizeof (DATALABEL_SUFFIX) + 1],
+ DATALABEL_SUFFIX) == 0;
+
+ return 0;
+}
diff --git a/gas/config/tc-sh64.h b/gas/config/tc-sh64.h
new file mode 100644
index 0000000..35702a0
--- /dev/null
+++ b/gas/config/tc-sh64.h
@@ -0,0 +1,227 @@
+/* This file is tc-sh64.h
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_SH64
+#include "config/tc-sh.h"
+#include "elf/sh.h"
+#include "elf32-sh64.h"
+
+/* We need to override the tc-sh.h settings of HANDLE_ALIGN and
+ MAX_MEM_FOR_RS_ALIGN_CODE; we might need to put in SHmedia NOP:s, not
+ SHcompact NOP:s. */
+#undef HANDLE_ALIGN
+#define HANDLE_ALIGN(frag) sh64_handle_align (frag)
+extern void sh64_handle_align (fragS *);
+
+#undef MAX_MEM_FOR_RS_ALIGN_CODE
+#define MAX_MEM_FOR_RS_ALIGN_CODE sh64_max_mem_for_rs_align_code ()
+extern int sh64_max_mem_for_rs_align_code (void);
+
+#undef LISTING_HEADER
+#define LISTING_HEADER \
+ (target_big_endian ? \
+ "SuperH SHcompact/SHmedia Big Endian GAS" \
+ : "SuperH SHcompact/SHmedia Little Endian GAS")
+
+/* We need to record the new frag position after an .align. */
+extern void sh64_do_align (int, const char *, int, int);
+#define md_do_align(n, fill, len, max, l) \
+ do { sh64_do_align (n, fill, len, max); goto l; } while (0)
+
+struct sh64_segment_info_type
+{
+ /* The type of the section is initialized when the range_start_symbol
+ member is non-NULL. */
+ symbolS *mode_start_symbol;
+ subsegT mode_start_subseg;
+
+ /* A stored symbol indicating location of last call of
+ "md_flush_pending_output". It is NULLed when we actually use it;
+ otherwise the contents is just filled in with segment, frag and
+ offset within frag. */
+ symbolS *last_contents_mark;
+
+ unsigned int emitted_ranges;
+ enum sh64_elf_cr_type contents_type;
+
+ /* This is used by the SH1-4 parts; we set it to 0 for SHmedia code and
+ data. */
+ unsigned int in_code : 1;
+};
+
+#undef TC_SEGMENT_INFO_TYPE
+#define TC_SEGMENT_INFO_TYPE struct sh64_segment_info_type
+
+#undef TARGET_FORMAT
+#define TARGET_FORMAT sh64_target_format ()
+extern const char *sh64_target_format (void);
+
+#define TARGET_MACH sh64_target_mach ()
+extern int sh64_target_mach (void);
+
+#undef TC_FORCE_RELOCATION_LOCAL
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_LOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_MEDLOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_MEDHI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_PLT_HI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_32_GOT_PCREL \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_LOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_MEDLOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_MEDHI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOT_HI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOT10BY4 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOT10BY8 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT32 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_LOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_MEDLOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_MEDHI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT_HI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT10BY4 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPLT10BY8 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_LOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_MEDLOW16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_MEDHI16 \
+ || (FIX)->fx_r_type == BFD_RELOC_SH_GOTPC_HI16 \
+ || TC_FORCE_RELOCATION (FIX))
+
+#undef TC_FORCE_RELOCATION_SUB_SAME
+#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEC) \
+ (! SEG_NORMAL (SEC) \
+ || TC_FORCE_RELOCATION (FIX) \
+ || (sh_relax && SWITCH_TABLE (FIX)) \
+ || *symbol_get_tc ((FIX)->fx_addsy) != NULL)
+
+/* Don't complain when we leave fx_subsy around. */
+#undef TC_VALIDATE_FIX_SUB
+#define TC_VALIDATE_FIX_SUB(FIX, SEG) \
+ ((md_register_arithmetic || (SEG) != reg_section) \
+ && ((FIX)->fx_r_type == BFD_RELOC_32_PLT_PCREL \
+ || (sh_relax && SWITCH_TABLE (FIX)) \
+ || *symbol_get_tc ((FIX)->fx_addsy) != NULL))
+
+/* Note the kludge: we want to put back C, and we also want to consume the
+ expression, since we have handled it ourselves. FIXME: What we really
+ need is a new GAS infrastructure feature: md_qualifier. */
+#undef md_parse_name
+#define md_parse_name(NAME, EXP, MODE, CP) \
+ sh64_consume_datalabel (NAME, EXP, MODE, CP, operand)
+extern int sh64_consume_datalabel (const char *, expressionS *,
+ enum expr_mode, char *,
+ segT (*) (expressionS *, enum expr_mode));
+
+/* Saying "$" is the same as saying ".". */
+#define DOLLAR_DOT
+
+#undef MD_PCREL_FROM_SECTION
+#define MD_PCREL_FROM_SECTION(FIX, SEC) \
+ shmedia_md_pcrel_from_section (FIX, SEC)
+
+extern valueT shmedia_md_pcrel_from_section (struct fix *, segT);
+
+/* We need to mark this symbol as a BranchTarget; setting st_other for it
+ and adding 1 to its value (temporarily). */
+extern void sh64_frob_label (symbolS *);
+
+#undef tc_frob_label
+#define tc_frob_label(sym) \
+ do { sh_frob_label (sym); sh64_frob_label (sym); } while (0)
+
+#define tc_symbol_new_hook(s) sh64_frob_label (s)
+
+/* We use this to mark our "datalabel" symbol copies. The "mark" is NULL
+ for an ordinary symbol, and the pointer to the "ordinary" symbol for a
+ datalabel symbol. */
+#define TC_SYMFIELD_TYPE symbolS *
+
+#define tc_frob_symbol(symp, punt) \
+ do \
+ { \
+ punt = sh64_exclude_symbol (symp); \
+ } \
+ while (0)
+
+extern int sh64_exclude_symbol (symbolS *);
+
+extern void sh64_adjust_symtab (void);
+#define tc_adjust_symtab sh64_adjust_symtab
+
+#undef md_flush_pending_output
+#define md_flush_pending_output() sh64_flush_pending_output ()
+extern void sh64_flush_pending_output (void);
+
+/* Note that tc-sh.c has a sh_frob_section, but it's called from
+ tc_frob_file_before_adjust. */
+#define tc_frob_section(sec) shmedia_frob_section_type (sec)
+extern void shmedia_frob_section_type (asection *);
+
+/* We need to emit fixups relative to the frag in which the instruction
+ resides. Safest way without calculating max fragment growth or making
+ it a fixed number is to provide a pointer to the opcode frag.
+
+ We also need to emit the right NOP pattern in .align frags. This is
+ done after the text-to-bits assembly pass, so we need to mark it with
+ the ISA setting at the time the .align was assembled. */
+#define TC_FRAG_TYPE struct sh64_tc_frag_data
+
+enum sh64_isa_values
+ {
+ sh64_isa_unspecified,
+ sh64_isa_shcompact,
+ sh64_isa_shmedia,
+
+ /* Special guard value used in contexts when we don't know which ISA it
+ is, just that it's specified (not sh64_isa_unspecified). */
+ sh64_isa_sh5_guard
+ };
+
+struct sh64_tc_frag_data
+{
+ fragS *opc_frag;
+ enum sh64_isa_values isa;
+};
+
+extern enum sh64_isa_values sh64_isa_mode;
+
+#define TC_FRAG_INIT(FRAGP) \
+ do \
+ { \
+ (FRAGP)->tc_frag_data.opc_frag = sh64_last_insn_frag; \
+ (FRAGP)->tc_frag_data.isa = sh64_isa_mode; \
+ } \
+ while (0)
+
+/* This variable is set whenever we generate (or grow) a new opcode frag
+ in shmedia_build_Mytes. */
+extern fragS *sh64_last_insn_frag;
+
+#define md_end() shmedia_md_end ()
+void shmedia_md_end (void);
+
+/* Because we make .debug_line hold the SHmedia instruction address | 1,
+ we have to say we only have minimum byte-size insns. */
+#undef DWARF2_LINE_MIN_INSN_LENGTH
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+#define TC_FAKE_LABEL(NAME) sh64_fake_label(NAME)
+extern int sh64_fake_label (const char *);
diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c
new file mode 100644
index 0000000..758fcc8
--- /dev/null
+++ b/gas/config/tc-sparc.c
@@ -0,0 +1,4871 @@
+/* tc-sparc.c -- Assemble for the SPARC
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+
+#include "opcode/sparc.h"
+#include "dw2gencfi.h"
+
+#ifdef OBJ_ELF
+#include "elf/sparc.h"
+#include "dwarf2dbg.h"
+#endif
+
+/* Some ancient Sun C compilers would not take such hex constants as
+ unsigned, and would end up sign-extending them to form an offsetT,
+ so use these constants instead. */
+#define U0xffffffff ((((unsigned long) 1 << 16) << 16) - 1)
+#define U0x80000000 ((((unsigned long) 1 << 16) << 15))
+
+static int sparc_ip (char *, const struct sparc_opcode **);
+static int parse_keyword_arg (int (*) (const char *), char **, int *);
+static int parse_const_expr_arg (char **, int *);
+static int get_expression (char *);
+
+/* Default architecture. */
+/* ??? The default value should be V8, but sparclite support was added
+ by making it the default. GCC now passes -Asparclite, so maybe sometime in
+ the future we can set this to V8. */
+#ifndef DEFAULT_ARCH
+#define DEFAULT_ARCH "sparclite"
+#endif
+static char *default_arch = DEFAULT_ARCH;
+
+/* Non-zero if the initial values of `max_architecture' and `sparc_arch_size'
+ have been set. */
+static int default_init_p;
+
+/* Current architecture. We don't bump up unless necessary. */
+static enum sparc_opcode_arch_val current_architecture = SPARC_OPCODE_ARCH_V6;
+
+/* The maximum architecture level we can bump up to.
+ In a 32 bit environment, don't allow bumping up to v9 by default.
+ The native assembler works this way. The user is required to pass
+ an explicit argument before we'll create v9 object files. However, if
+ we don't see any v9 insns, a v8plus object file is not created. */
+static enum sparc_opcode_arch_val max_architecture;
+
+/* Either 32 or 64, selects file format. */
+static int sparc_arch_size;
+/* Initial (default) value, recorded separately in case a user option
+ changes the value before md_show_usage is called. */
+static int default_arch_size;
+
+#ifdef OBJ_ELF
+/* The currently selected v9 memory model. Currently only used for
+ ELF. */
+static enum { MM_TSO, MM_PSO, MM_RMO } sparc_memory_model = MM_RMO;
+
+#ifndef TE_SOLARIS
+/* Bitmask of instruction types seen so far, used to populate the
+ GNU attributes section with hwcap information. */
+static bfd_uint64_t hwcap_seen;
+#endif
+#endif
+
+static bfd_uint64_t hwcap_allowed;
+
+static int architecture_requested;
+static int warn_on_bump;
+
+/* If warn_on_bump and the needed architecture is higher than this
+ architecture, issue a warning. */
+static enum sparc_opcode_arch_val warn_after_architecture;
+
+/* Non-zero if as should generate error if an undeclared g[23] register
+ has been used in -64. */
+static int no_undeclared_regs;
+
+/* Non-zero if we should try to relax jumps and calls. */
+static int sparc_relax;
+
+/* Non-zero if we are generating PIC code. */
+int sparc_pic_code;
+
+/* Non-zero if we should give an error when misaligned data is seen. */
+static int enforce_aligned_data;
+
+extern int target_big_endian;
+
+static int target_little_endian_data;
+
+/* Symbols for global registers on v9. */
+static symbolS *globals[8];
+
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */
+int sparc_cie_data_alignment;
+
+/* V9 and 86x have big and little endian data, but instructions are always big
+ endian. The sparclet has bi-endian support but both data and insns have
+ the same endianness. Global `target_big_endian' is used for data.
+ The following macro is used for instructions. */
+#ifndef INSN_BIG_ENDIAN
+#define INSN_BIG_ENDIAN (target_big_endian \
+ || default_arch_type == sparc86x \
+ || SPARC_OPCODE_ARCH_V9_P (max_architecture))
+#endif
+
+/* Handle of the OPCODE hash table. */
+static struct hash_control *op_hash;
+
+static void s_data1 (void);
+static void s_seg (int);
+static void s_proc (int);
+static void s_reserve (int);
+static void s_common (int);
+static void s_empty (int);
+static void s_uacons (int);
+static void s_ncons (int);
+#ifdef OBJ_ELF
+static void s_register (int);
+#endif
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 0}, /* Defaulting is invalid (0). */
+ {"common", s_common, 0},
+ {"empty", s_empty, 0},
+ {"global", s_globl, 0},
+ {"half", cons, 2},
+ {"nword", s_ncons, 0},
+ {"optim", s_ignore, 0},
+ {"proc", s_proc, 0},
+ {"reserve", s_reserve, 0},
+ {"seg", s_seg, 0},
+ {"skip", s_space, 0},
+ {"word", cons, 4},
+ {"xword", cons, 8},
+ {"uahalf", s_uacons, 2},
+ {"uaword", s_uacons, 4},
+ {"uaxword", s_uacons, 8},
+#ifdef OBJ_ELF
+ /* These are specific to sparc/svr4. */
+ {"2byte", s_uacons, 2},
+ {"4byte", s_uacons, 4},
+ {"8byte", s_uacons, 8},
+ {"register", s_register, 0},
+#endif
+ {NULL, 0, 0},
+};
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char comment_chars[] = "!"; /* JF removed '|' from
+ comment_chars. */
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output. */
+/* Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output. */
+/* Also note that comments started like this one will always
+ work if '/' isn't otherwise defined. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point
+ nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456
+ or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+#define isoctal(c) ((unsigned) ((c) - '0') < 8)
+
+struct sparc_it
+ {
+ char *error;
+ unsigned long opcode;
+ struct nlist *nlistp;
+ expressionS exp;
+ expressionS exp2;
+ int pcrel;
+ bfd_reloc_code_real_type reloc;
+ };
+
+struct sparc_it the_insn, set_insn;
+
+static void output_insn (const struct sparc_opcode *, struct sparc_it *);
+
+/* Table of arguments to -A.
+ The sparc_opcode_arch table in sparc-opc.c is insufficient and incorrect
+ for this use. That table is for opcodes only. This table is for opcodes
+ and file formats. */
+
+enum sparc_arch_types {v6, v7, v8, leon, sparclet, sparclite, sparc86x, v8plus,
+ v8plusa, v9, v9a, v9b, v9_64};
+
+/* Hardware capability sets, used to keep sparc_arch_table easy to
+ read. */
+#define HWS_V8 HWCAP_MUL32 | HWCAP_DIV32 | HWCAP_FSMULD
+#define HWS_V9 HWS_V8 | HWCAP_POPC
+#define HWS_VA HWS_V9 | HWCAP_VIS
+#define HWS_VB HWS_VA | HWCAP_VIS2
+#define HWS_VC HWS_VB | HWCAP_ASI_BLK_INIT
+#define HWS_VD HWS_VC | HWCAP_FMAF | HWCAP_VIS3 | HWCAP_HPC
+#define HWS_VE HWS_VD \
+ | HWCAP_AES | HWCAP_DES | HWCAP_KASUMI | HWCAP_CAMELLIA \
+ | HWCAP_MD5 | HWCAP_SHA1 | HWCAP_SHA256 |HWCAP_SHA512 | HWCAP_MPMUL \
+ | HWCAP_MONT | HWCAP_CRC32C | HWCAP_CBCOND | HWCAP_PAUSE
+#define HWS_VV HWS_VE | HWCAP_FJFMAU | HWCAP_IMA
+#define HWS_VM HWS_VV
+
+#define HWS2_VM \
+ HWCAP2_VIS3B | HWCAP2_ADP | HWCAP2_SPARC5 | HWCAP2_MWAIT \
+ | HWCAP2_XMPMUL | HWCAP2_XMONT
+
+static struct sparc_arch {
+ char *name;
+ char *opcode_arch;
+ enum sparc_arch_types arch_type;
+ /* Default word size, as specified during configuration.
+ A value of zero means can't be used to specify default architecture. */
+ int default_arch_size;
+ /* Allowable arg to -A? */
+ int user_option_p;
+ int hwcap_allowed;
+ int hwcap2_allowed;
+} sparc_arch_table[] = {
+ { "v6", "v6", v6, 0, 1, 0, 0 },
+ { "v7", "v7", v7, 0, 1, 0, 0 },
+ { "v8", "v8", v8, 32, 1, HWS_V8, 0 },
+ { "v8a", "v8", v8, 32, 1, HWS_V8, 0 },
+ { "sparc", "v9", v9, 0, 1, HWCAP_V8PLUS|HWS_V9, 0 },
+ { "sparcvis", "v9a", v9, 0, 1, HWS_VA, 0 },
+ { "sparcvis2", "v9b", v9, 0, 1, HWS_VB, 0 },
+ { "sparcfmaf", "v9b", v9, 0, 1, HWS_VB|HWCAP_FMAF, 0 },
+ { "sparcima", "v9b", v9, 0, 1, HWS_VB|HWCAP_FMAF|HWCAP_IMA, 0 },
+ { "sparcvis3", "v9b", v9, 0, 1, HWS_VB|HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC, 0 },
+ { "sparcvis3r", "v9b", v9, 0, 1, HWS_VB|HWCAP_FMAF|HWCAP_VIS3|HWCAP_HPC|HWCAP_FJFMAU, 0 },
+
+ { "sparc4", "v9b", v9, 0, 1, HWS_VV, 0 },
+ { "sparc5", "v9b", v9, 0, 1, HWS_VM, HWS2_VM },
+
+ { "leon", "leon", leon, 32, 1, HWS_V8, 0 },
+ { "sparclet", "sparclet", sparclet, 32, 1, HWS_V8, 0 },
+ { "sparclite", "sparclite", sparclite, 32, 1, HWS_V8, 0 },
+ { "sparc86x", "sparclite", sparc86x, 32, 1, HWS_V8, 0 },
+
+ { "v8plus", "v9", v9, 0, 1, HWCAP_V8PLUS|HWS_V9, 0 },
+ { "v8plusa", "v9a", v9, 0, 1, HWCAP_V8PLUS|HWS_VA, 0 },
+ { "v8plusb", "v9b", v9, 0, 1, HWCAP_V8PLUS|HWS_VB, 0 },
+ { "v8plusc", "v9b", v9, 0, 1, HWCAP_V8PLUS|HWS_VC, 0 },
+ { "v8plusd", "v9b", v9, 0, 1, HWCAP_V8PLUS|HWS_VD, 0 },
+ { "v8pluse", "v9b", v9, 0, 1, HWCAP_V8PLUS|HWS_VE, 0 },
+ { "v8plusv", "v9b", v9, 0, 1, HWCAP_V8PLUS|HWS_VV, 0 },
+ { "v8plusm", "v9b", v9, 0, 1, HWCAP_V8PLUS|HWS_VM, 0 },
+
+ { "v9", "v9", v9, 0, 1, HWS_V9, 0 },
+ { "v9a", "v9a", v9, 0, 1, HWS_VA, 0 },
+ { "v9b", "v9b", v9, 0, 1, HWS_VB, 0 },
+ { "v9c", "v9b", v9, 0, 1, HWS_VC, 0 },
+ { "v9d", "v9b", v9, 0, 1, HWS_VD, 0 },
+ { "v9e", "v9b", v9, 0, 1, HWS_VE, 0 },
+ { "v9v", "v9b", v9, 0, 1, HWS_VV, 0 },
+ { "v9m", "v9b", v9, 0, 1, HWS_VM, HWS2_VM },
+
+ /* This exists to allow configure.tgt to pass one
+ value to specify both the default machine and default word size. */
+ { "v9-64", "v9", v9, 64, 0, HWS_V9, 0 },
+ { NULL, NULL, v8, 0, 0, 0, 0 }
+};
+
+/* Variant of default_arch */
+static enum sparc_arch_types default_arch_type;
+
+static struct sparc_arch *
+lookup_arch (char *name)
+{
+ struct sparc_arch *sa;
+
+ for (sa = &sparc_arch_table[0]; sa->name != NULL; sa++)
+ if (strcmp (sa->name, name) == 0)
+ break;
+ if (sa->name == NULL)
+ return NULL;
+ return sa;
+}
+
+/* Initialize the default opcode arch and word size from the default
+ architecture name. */
+
+static void
+init_default_arch (void)
+{
+ struct sparc_arch *sa = lookup_arch (default_arch);
+
+ if (sa == NULL
+ || sa->default_arch_size == 0)
+ as_fatal (_("Invalid default architecture, broken assembler."));
+
+ max_architecture = sparc_opcode_lookup_arch (sa->opcode_arch);
+ if (max_architecture == SPARC_OPCODE_ARCH_BAD)
+ as_fatal (_("Bad opcode table, broken assembler."));
+ default_arch_size = sparc_arch_size = sa->default_arch_size;
+ default_init_p = 1;
+ default_arch_type = sa->arch_type;
+}
+
+/* Called by TARGET_FORMAT. */
+
+const char *
+sparc_target_format (void)
+{
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ if (! default_init_p)
+ init_default_arch ();
+
+#ifdef OBJ_AOUT
+#ifdef TE_NetBSD
+ return "a.out-sparc-netbsd";
+#else
+#ifdef TE_SPARCAOUT
+ if (target_big_endian)
+ return "a.out-sunos-big";
+ else if (default_arch_type == sparc86x && target_little_endian_data)
+ return "a.out-sunos-big";
+ else
+ return "a.out-sparc-little";
+#else
+ return "a.out-sunos-big";
+#endif
+#endif
+#endif
+
+#ifdef OBJ_BOUT
+ return "b.out.big";
+#endif
+
+#ifdef OBJ_COFF
+#ifdef TE_LYNX
+ return "coff-sparc-lynx";
+#else
+ return "coff-sparc";
+#endif
+#endif
+
+#ifdef TE_VXWORKS
+ return "elf32-sparc-vxworks";
+#endif
+
+#ifdef OBJ_ELF
+ return sparc_arch_size == 64 ? ELF64_TARGET_FORMAT : ELF_TARGET_FORMAT;
+#endif
+
+ abort ();
+}
+
+/* md_parse_option
+ * Invocation line includes a switch not recognized by the base assembler.
+ * See if it's a processor-specific option. These are:
+ *
+ * -bump
+ * Warn on architecture bumps. See also -A.
+ *
+ * -Av6, -Av7, -Av8, -Aleon, -Asparclite, -Asparclet
+ * Standard 32 bit architectures.
+ * -Av9, -Av9a, -Av9b
+ * Sparc64 in either a 32 or 64 bit world (-32/-64 says which).
+ * This used to only mean 64 bits, but properly specifying it
+ * complicated gcc's ASM_SPECs, so now opcode selection is
+ * specified orthogonally to word size (except when specifying
+ * the default, but that is an internal implementation detail).
+ * -Av8plus, -Av8plusa, -Av8plusb
+ * Same as -Av9{,a,b}.
+ * -xarch=v8plus, -xarch=v8plusa, -xarch=v8plusb
+ * Same as -Av8plus{,a,b} -32, for compatibility with Sun's
+ * assembler.
+ * -xarch=v9, -xarch=v9a, -xarch=v9b
+ * Same as -Av9{,a,b} -64, for compatibility with Sun's
+ * assembler.
+ *
+ * Select the architecture and possibly the file format.
+ * Instructions or features not supported by the selected
+ * architecture cause fatal errors.
+ *
+ * The default is to start at v6, and bump the architecture up
+ * whenever an instruction is seen at a higher level. In 32 bit
+ * environments, v9 is not bumped up to, the user must pass
+ * -Av8plus{,a,b}.
+ *
+ * If -bump is specified, a warning is printing when bumping to
+ * higher levels.
+ *
+ * If an architecture is specified, all instructions must match
+ * that architecture. Any higher level instructions are flagged
+ * as errors. Note that in the 32 bit environment specifying
+ * -Av8plus does not automatically create a v8plus object file, a
+ * v9 insn must be seen.
+ *
+ * If both an architecture and -bump are specified, the
+ * architecture starts at the specified level, but bumps are
+ * warnings. Note that we can't set `current_architecture' to
+ * the requested level in this case: in the 32 bit environment,
+ * we still must avoid creating v8plus object files unless v9
+ * insns are seen.
+ *
+ * Note:
+ * Bumping between incompatible architectures is always an
+ * error. For example, from sparclite to v9.
+ */
+
+#ifdef OBJ_ELF
+const char *md_shortopts = "A:K:VQ:sq";
+#else
+#ifdef OBJ_AOUT
+const char *md_shortopts = "A:k";
+#else
+const char *md_shortopts = "A:";
+#endif
+#endif
+struct option md_longopts[] = {
+#define OPTION_BUMP (OPTION_MD_BASE)
+ {"bump", no_argument, NULL, OPTION_BUMP},
+#define OPTION_SPARC (OPTION_MD_BASE + 1)
+ {"sparc", no_argument, NULL, OPTION_SPARC},
+#define OPTION_XARCH (OPTION_MD_BASE + 2)
+ {"xarch", required_argument, NULL, OPTION_XARCH},
+#ifdef OBJ_ELF
+#define OPTION_32 (OPTION_MD_BASE + 3)
+ {"32", no_argument, NULL, OPTION_32},
+#define OPTION_64 (OPTION_MD_BASE + 4)
+ {"64", no_argument, NULL, OPTION_64},
+#define OPTION_TSO (OPTION_MD_BASE + 5)
+ {"TSO", no_argument, NULL, OPTION_TSO},
+#define OPTION_PSO (OPTION_MD_BASE + 6)
+ {"PSO", no_argument, NULL, OPTION_PSO},
+#define OPTION_RMO (OPTION_MD_BASE + 7)
+ {"RMO", no_argument, NULL, OPTION_RMO},
+#endif
+#ifdef SPARC_BIENDIAN
+#define OPTION_LITTLE_ENDIAN (OPTION_MD_BASE + 8)
+ {"EL", no_argument, NULL, OPTION_LITTLE_ENDIAN},
+#define OPTION_BIG_ENDIAN (OPTION_MD_BASE + 9)
+ {"EB", no_argument, NULL, OPTION_BIG_ENDIAN},
+#endif
+#define OPTION_ENFORCE_ALIGNED_DATA (OPTION_MD_BASE + 10)
+ {"enforce-aligned-data", no_argument, NULL, OPTION_ENFORCE_ALIGNED_DATA},
+#define OPTION_LITTLE_ENDIAN_DATA (OPTION_MD_BASE + 11)
+ {"little-endian-data", no_argument, NULL, OPTION_LITTLE_ENDIAN_DATA},
+#ifdef OBJ_ELF
+#define OPTION_NO_UNDECLARED_REGS (OPTION_MD_BASE + 12)
+ {"no-undeclared-regs", no_argument, NULL, OPTION_NO_UNDECLARED_REGS},
+#define OPTION_UNDECLARED_REGS (OPTION_MD_BASE + 13)
+ {"undeclared-regs", no_argument, NULL, OPTION_UNDECLARED_REGS},
+#endif
+#define OPTION_RELAX (OPTION_MD_BASE + 14)
+ {"relax", no_argument, NULL, OPTION_RELAX},
+#define OPTION_NO_RELAX (OPTION_MD_BASE + 15)
+ {"no-relax", no_argument, NULL, OPTION_NO_RELAX},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ if (! default_init_p)
+ init_default_arch ();
+
+ switch (c)
+ {
+ case OPTION_BUMP:
+ warn_on_bump = 1;
+ warn_after_architecture = SPARC_OPCODE_ARCH_V6;
+ break;
+
+ case OPTION_XARCH:
+#ifdef OBJ_ELF
+ if (!strncmp (arg, "v9", 2))
+ md_parse_option (OPTION_64, NULL);
+ else
+ {
+ if (!strncmp (arg, "v8", 2)
+ || !strncmp (arg, "v7", 2)
+ || !strncmp (arg, "v6", 2)
+ || !strcmp (arg, "sparclet")
+ || !strcmp (arg, "sparclite")
+ || !strcmp (arg, "sparc86x"))
+ md_parse_option (OPTION_32, NULL);
+ }
+#endif
+ /* Fall through. */
+
+ case 'A':
+ {
+ struct sparc_arch *sa;
+ enum sparc_opcode_arch_val opcode_arch;
+
+ sa = lookup_arch (arg);
+ if (sa == NULL
+ || ! sa->user_option_p)
+ {
+ if (c == OPTION_XARCH)
+ as_bad (_("invalid architecture -xarch=%s"), arg);
+ else
+ as_bad (_("invalid architecture -A%s"), arg);
+ return 0;
+ }
+
+ opcode_arch = sparc_opcode_lookup_arch (sa->opcode_arch);
+ if (opcode_arch == SPARC_OPCODE_ARCH_BAD)
+ as_fatal (_("Bad opcode table, broken assembler."));
+
+ if (!architecture_requested
+ || opcode_arch > max_architecture)
+ max_architecture = opcode_arch;
+ hwcap_allowed
+ |= (((bfd_uint64_t) sa->hwcap2_allowed) << 32) | sa->hwcap_allowed;
+ architecture_requested = 1;
+ }
+ break;
+
+ case OPTION_SPARC:
+ /* Ignore -sparc, used by SunOS make default .s.o rule. */
+ break;
+
+ case OPTION_ENFORCE_ALIGNED_DATA:
+ enforce_aligned_data = 1;
+ break;
+
+#ifdef SPARC_BIENDIAN
+ case OPTION_LITTLE_ENDIAN:
+ target_big_endian = 0;
+ if (default_arch_type != sparclet)
+ as_fatal ("This target does not support -EL");
+ break;
+ case OPTION_LITTLE_ENDIAN_DATA:
+ target_little_endian_data = 1;
+ target_big_endian = 0;
+ if (default_arch_type != sparc86x
+ && default_arch_type != v9)
+ as_fatal ("This target does not support --little-endian-data");
+ break;
+ case OPTION_BIG_ENDIAN:
+ target_big_endian = 1;
+ break;
+#endif
+
+#ifdef OBJ_AOUT
+ case 'k':
+ sparc_pic_code = 1;
+ break;
+#endif
+
+#ifdef OBJ_ELF
+ case OPTION_32:
+ case OPTION_64:
+ {
+ const char **list, **l;
+
+ sparc_arch_size = c == OPTION_32 ? 32 : 64;
+ list = bfd_target_list ();
+ for (l = list; *l != NULL; l++)
+ {
+ if (sparc_arch_size == 32)
+ {
+ if (CONST_STRNEQ (*l, "elf32-sparc"))
+ break;
+ }
+ else
+ {
+ if (CONST_STRNEQ (*l, "elf64-sparc"))
+ break;
+ }
+ }
+ if (*l == NULL)
+ as_fatal (_("No compiled in support for %d bit object file format"),
+ sparc_arch_size);
+ free (list);
+
+ if (sparc_arch_size == 64
+ && max_architecture < SPARC_OPCODE_ARCH_V9)
+ max_architecture = SPARC_OPCODE_ARCH_V9;
+ }
+ break;
+
+ case OPTION_TSO:
+ sparc_memory_model = MM_TSO;
+ break;
+
+ case OPTION_PSO:
+ sparc_memory_model = MM_PSO;
+ break;
+
+ case OPTION_RMO:
+ sparc_memory_model = MM_RMO;
+ break;
+
+ case 'V':
+ print_version_id ();
+ break;
+
+ case 'Q':
+ /* Qy - do emit .comment
+ Qn - do not emit .comment. */
+ break;
+
+ case 's':
+ /* Use .stab instead of .stab.excl. */
+ break;
+
+ case 'q':
+ /* quick -- Native assembler does fewer checks. */
+ break;
+
+ case 'K':
+ if (strcmp (arg, "PIC") != 0)
+ as_warn (_("Unrecognized option following -K"));
+ else
+ sparc_pic_code = 1;
+ break;
+
+ case OPTION_NO_UNDECLARED_REGS:
+ no_undeclared_regs = 1;
+ break;
+
+ case OPTION_UNDECLARED_REGS:
+ no_undeclared_regs = 0;
+ break;
+#endif
+
+ case OPTION_RELAX:
+ sparc_relax = 1;
+ break;
+
+ case OPTION_NO_RELAX:
+ sparc_relax = 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ const struct sparc_arch *arch;
+ int column;
+
+ /* We don't get a chance to initialize anything before we're called,
+ so handle that now. */
+ if (! default_init_p)
+ init_default_arch ();
+
+ fprintf (stream, _("SPARC options:\n"));
+ column = 0;
+ for (arch = &sparc_arch_table[0]; arch->name; arch++)
+ {
+ if (!arch->user_option_p)
+ continue;
+ if (arch != &sparc_arch_table[0])
+ fprintf (stream, " | ");
+ if (column + strlen (arch->name) > 70)
+ {
+ column = 0;
+ fputc ('\n', stream);
+ }
+ column += 5 + 2 + strlen (arch->name);
+ fprintf (stream, "-A%s", arch->name);
+ }
+ for (arch = &sparc_arch_table[0]; arch->name; arch++)
+ {
+ if (!arch->user_option_p)
+ continue;
+ fprintf (stream, " | ");
+ if (column + strlen (arch->name) > 65)
+ {
+ column = 0;
+ fputc ('\n', stream);
+ }
+ column += 5 + 7 + strlen (arch->name);
+ fprintf (stream, "-xarch=%s", arch->name);
+ }
+ fprintf (stream, _("\n\
+ specify variant of SPARC architecture\n\
+-bump warn when assembler switches architectures\n\
+-sparc ignored\n\
+--enforce-aligned-data force .long, etc., to be aligned correctly\n\
+-relax relax jumps and branches (default)\n\
+-no-relax avoid changing any jumps and branches\n"));
+#ifdef OBJ_AOUT
+ fprintf (stream, _("\
+-k generate PIC\n"));
+#endif
+#ifdef OBJ_ELF
+ fprintf (stream, _("\
+-32 create 32 bit object file\n\
+-64 create 64 bit object file\n"));
+ fprintf (stream, _("\
+ [default is %d]\n"), default_arch_size);
+ fprintf (stream, _("\
+-TSO use Total Store Ordering\n\
+-PSO use Partial Store Ordering\n\
+-RMO use Relaxed Memory Ordering\n"));
+ fprintf (stream, _("\
+ [default is %s]\n"), (default_arch_size == 64) ? "RMO" : "TSO");
+ fprintf (stream, _("\
+-KPIC generate PIC\n\
+-V print assembler version number\n\
+-undeclared-regs ignore application global register usage without\n\
+ appropriate .register directive (default)\n\
+-no-undeclared-regs force error on application global register usage\n\
+ without appropriate .register directive\n\
+-q ignored\n\
+-Qy, -Qn ignored\n\
+-s ignored\n"));
+#endif
+#ifdef SPARC_BIENDIAN
+ fprintf (stream, _("\
+-EL generate code for a little endian machine\n\
+-EB generate code for a big endian machine\n\
+--little-endian-data generate code for a machine having big endian\n\
+ instructions and little endian data.\n"));
+#endif
+}
+
+/* Native operand size opcode translation. */
+struct
+ {
+ char *name;
+ char *name32;
+ char *name64;
+ } native_op_table[] =
+{
+ {"ldn", "ld", "ldx"},
+ {"ldna", "lda", "ldxa"},
+ {"stn", "st", "stx"},
+ {"stna", "sta", "stxa"},
+ {"slln", "sll", "sllx"},
+ {"srln", "srl", "srlx"},
+ {"sran", "sra", "srax"},
+ {"casn", "cas", "casx"},
+ {"casna", "casa", "casxa"},
+ {"clrn", "clr", "clrx"},
+ {NULL, NULL, NULL},
+};
+
+/* sparc64 privileged and hyperprivileged registers. */
+
+struct priv_reg_entry
+{
+ char *name;
+ int regnum;
+};
+
+struct priv_reg_entry priv_reg_table[] =
+{
+ {"tpc", 0},
+ {"tnpc", 1},
+ {"tstate", 2},
+ {"tt", 3},
+ {"tick", 4},
+ {"tba", 5},
+ {"pstate", 6},
+ {"tl", 7},
+ {"pil", 8},
+ {"cwp", 9},
+ {"cansave", 10},
+ {"canrestore", 11},
+ {"cleanwin", 12},
+ {"otherwin", 13},
+ {"wstate", 14},
+ {"fq", 15},
+ {"gl", 16},
+ {"ver", 31},
+ {"", -1}, /* End marker. */
+};
+
+struct priv_reg_entry hpriv_reg_table[] =
+{
+ {"hpstate", 0},
+ {"htstate", 1},
+ {"hintp", 3},
+ {"htba", 5},
+ {"hver", 6},
+ {"hstick_offset", 28},
+ {"hstick_enable", 29},
+ {"hstick_cmpr", 31},
+ {"", -1}, /* End marker. */
+};
+
+/* v9a specific asrs. This table is ordered by initial
+ letter, in reverse. */
+
+struct priv_reg_entry v9a_asr_table[] =
+{
+ {"tick_cmpr", 23},
+ {"sys_tick_cmpr", 25},
+ {"sys_tick", 24},
+ {"stick_cmpr", 25},
+ {"stick", 24},
+ {"softint_clear", 21},
+ {"softint_set", 20},
+ {"softint", 22},
+ {"set_softint", 20},
+ {"pause", 27},
+ {"pic", 17},
+ {"pcr", 16},
+ {"mwait", 28},
+ {"gsr", 19},
+ {"dcr", 18},
+ {"cfr", 26},
+ {"clear_softint", 21},
+ {"", -1}, /* End marker. */
+};
+
+static int
+cmp_reg_entry (const void *parg, const void *qarg)
+{
+ const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg;
+ const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg;
+
+ return strcmp (q->name, p->name);
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will
+ need. */
+
+void
+md_begin (void)
+{
+ register const char *retval = NULL;
+ int lose = 0;
+ register unsigned int i = 0;
+
+ /* We don't get a chance to initialize anything before md_parse_option
+ is called, and it may not be called, so handle default initialization
+ now if not already done. */
+ if (! default_init_p)
+ init_default_arch ();
+
+ sparc_cie_data_alignment = sparc_arch_size == 64 ? -8 : -4;
+ op_hash = hash_new ();
+
+ while (i < (unsigned int) sparc_num_opcodes)
+ {
+ const char *name = sparc_opcodes[i].name;
+ retval = hash_insert (op_hash, name, (void *) &sparc_opcodes[i]);
+ if (retval != NULL)
+ {
+ as_bad (_("Internal error: can't hash `%s': %s\n"),
+ sparc_opcodes[i].name, retval);
+ lose = 1;
+ }
+ do
+ {
+ if (sparc_opcodes[i].match & sparc_opcodes[i].lose)
+ {
+ as_bad (_("Internal error: losing opcode: `%s' \"%s\"\n"),
+ sparc_opcodes[i].name, sparc_opcodes[i].args);
+ lose = 1;
+ }
+ ++i;
+ }
+ while (i < (unsigned int) sparc_num_opcodes
+ && !strcmp (sparc_opcodes[i].name, name));
+ }
+
+ for (i = 0; native_op_table[i].name; i++)
+ {
+ const struct sparc_opcode *insn;
+ char *name = ((sparc_arch_size == 32)
+ ? native_op_table[i].name32
+ : native_op_table[i].name64);
+ insn = (struct sparc_opcode *) hash_find (op_hash, name);
+ if (insn == NULL)
+ {
+ as_bad (_("Internal error: can't find opcode `%s' for `%s'\n"),
+ name, native_op_table[i].name);
+ lose = 1;
+ }
+ else
+ {
+ retval = hash_insert (op_hash, native_op_table[i].name,
+ (void *) insn);
+ if (retval != NULL)
+ {
+ as_bad (_("Internal error: can't hash `%s': %s\n"),
+ sparc_opcodes[i].name, retval);
+ lose = 1;
+ }
+ }
+ }
+
+ if (lose)
+ as_fatal (_("Broken assembler. No assembly attempted."));
+
+ qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]),
+ sizeof (priv_reg_table[0]), cmp_reg_entry);
+
+ /* If -bump, record the architecture level at which we start issuing
+ warnings. The behaviour is different depending upon whether an
+ architecture was explicitly specified. If it wasn't, we issue warnings
+ for all upwards bumps. If it was, we don't start issuing warnings until
+ we need to bump beyond the requested architecture or when we bump between
+ conflicting architectures. */
+
+ if (warn_on_bump
+ && architecture_requested)
+ {
+ /* `max_architecture' records the requested architecture.
+ Issue warnings if we go above it. */
+ warn_after_architecture = max_architecture;
+ }
+
+ /* Find the highest architecture level that doesn't conflict with
+ the requested one. */
+
+ if (warn_on_bump
+ || !architecture_requested)
+ {
+ enum sparc_opcode_arch_val current_max_architecture
+ = max_architecture;
+
+ for (max_architecture = SPARC_OPCODE_ARCH_MAX;
+ max_architecture > warn_after_architecture;
+ --max_architecture)
+ if (! SPARC_OPCODE_CONFLICT_P (max_architecture,
+ current_max_architecture))
+ break;
+ }
+}
+
+/* Called after all assembly has been done. */
+
+void
+sparc_md_end (void)
+{
+ unsigned long mach = bfd_mach_sparc;
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+ int hwcaps, hwcaps2;
+#endif
+
+ if (sparc_arch_size == 64)
+ switch (current_architecture)
+ {
+ case SPARC_OPCODE_ARCH_V9A: mach = bfd_mach_sparc_v9a; break;
+ case SPARC_OPCODE_ARCH_V9B: mach = bfd_mach_sparc_v9b; break;
+ default: mach = bfd_mach_sparc_v9; break;
+ }
+ else
+ switch (current_architecture)
+ {
+ case SPARC_OPCODE_ARCH_SPARCLET: mach = bfd_mach_sparc_sparclet; break;
+ case SPARC_OPCODE_ARCH_V9: mach = bfd_mach_sparc_v8plus; break;
+ case SPARC_OPCODE_ARCH_V9A: mach = bfd_mach_sparc_v8plusa; break;
+ case SPARC_OPCODE_ARCH_V9B: mach = bfd_mach_sparc_v8plusb; break;
+ /* The sparclite is treated like a normal sparc. Perhaps it shouldn't
+ be but for now it is (since that's the way it's always been
+ treated). */
+ default: break;
+ }
+ bfd_set_arch_mach (stdoutput, bfd_arch_sparc, mach);
+
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+ hwcaps = hwcap_seen & U0xffffffff;
+ hwcaps2 = hwcap_seen >> 32;
+
+ if (hwcaps)
+ bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, Tag_GNU_Sparc_HWCAPS, hwcaps);
+ if (hwcaps2)
+ bfd_elf_add_obj_attr_int (stdoutput, OBJ_ATTR_GNU, Tag_GNU_Sparc_HWCAPS2, hwcaps2);
+#endif
+}
+
+/* Return non-zero if VAL is in the range -(MAX+1) to MAX. */
+
+static inline int
+in_signed_range (bfd_signed_vma val, bfd_signed_vma max)
+{
+ if (max <= 0)
+ abort ();
+ /* Sign-extend the value from the architecture word size, so that
+ 0xffffffff is always considered -1 on sparc32. */
+ if (sparc_arch_size == 32)
+ {
+ bfd_signed_vma sign = (bfd_signed_vma) 1 << 31;
+ val = ((val & U0xffffffff) ^ sign) - sign;
+ }
+ if (val > max)
+ return 0;
+ if (val < ~max)
+ return 0;
+ return 1;
+}
+
+/* Return non-zero if VAL is in the range 0 to MAX. */
+
+static inline int
+in_unsigned_range (bfd_vma val, bfd_vma max)
+{
+ if (val > max)
+ return 0;
+ return 1;
+}
+
+/* Return non-zero if VAL is in the range -(MAX/2+1) to MAX.
+ (e.g. -15 to +31). */
+
+static inline int
+in_bitfield_range (bfd_signed_vma val, bfd_signed_vma max)
+{
+ if (max <= 0)
+ abort ();
+ if (val > max)
+ return 0;
+ if (val < ~(max >> 1))
+ return 0;
+ return 1;
+}
+
+static int
+sparc_ffs (unsigned int mask)
+{
+ int i;
+
+ if (mask == 0)
+ return -1;
+
+ for (i = 0; (mask & 1) == 0; ++i)
+ mask >>= 1;
+ return i;
+}
+
+/* Implement big shift right. */
+static bfd_vma
+BSR (bfd_vma val, int amount)
+{
+ if (sizeof (bfd_vma) <= 4 && amount >= 32)
+ as_fatal (_("Support for 64-bit arithmetic not compiled in."));
+ return val >> amount;
+}
+
+/* For communication between sparc_ip and get_expression. */
+static char *expr_end;
+
+/* Values for `special_case'.
+ Instructions that require wierd handling because they're longer than
+ 4 bytes. */
+#define SPECIAL_CASE_NONE 0
+#define SPECIAL_CASE_SET 1
+#define SPECIAL_CASE_SETSW 2
+#define SPECIAL_CASE_SETX 3
+/* FIXME: sparc-opc.c doesn't have necessary "S" trigger to enable this. */
+#define SPECIAL_CASE_FDIV 4
+
+/* Bit masks of various insns. */
+#define NOP_INSN 0x01000000
+#define OR_INSN 0x80100000
+#define XOR_INSN 0x80180000
+#define FMOVS_INSN 0x81A00020
+#define SETHI_INSN 0x01000000
+#define SLLX_INSN 0x81281000
+#define SRA_INSN 0x81380000
+
+/* The last instruction to be assembled. */
+static const struct sparc_opcode *last_insn;
+/* The assembled opcode of `last_insn'. */
+static unsigned long last_opcode;
+
+/* Handle the set and setuw synthetic instructions. */
+
+static void
+synthetize_setuw (const struct sparc_opcode *insn)
+{
+ int need_hi22_p = 0;
+ int rd = (the_insn.opcode & RD (~0)) >> 25;
+
+ if (the_insn.exp.X_op == O_constant)
+ {
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ {
+ if (sizeof (offsetT) > 4
+ && (the_insn.exp.X_add_number < 0
+ || the_insn.exp.X_add_number > (offsetT) U0xffffffff))
+ as_warn (_("set: number not in 0..4294967295 range"));
+ }
+ else
+ {
+ if (sizeof (offsetT) > 4
+ && (the_insn.exp.X_add_number < -(offsetT) U0x80000000
+ || the_insn.exp.X_add_number > (offsetT) U0xffffffff))
+ as_warn (_("set: number not in -2147483648..4294967295 range"));
+ the_insn.exp.X_add_number = (int) the_insn.exp.X_add_number;
+ }
+ }
+
+ /* See if operand is absolute and small; skip sethi if so. */
+ if (the_insn.exp.X_op != O_constant
+ || the_insn.exp.X_add_number >= (1 << 12)
+ || the_insn.exp.X_add_number < -(1 << 12))
+ {
+ the_insn.opcode = (SETHI_INSN | RD (rd)
+ | ((the_insn.exp.X_add_number >> 10)
+ & (the_insn.exp.X_op == O_constant
+ ? 0x3fffff : 0)));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_HI22 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ need_hi22_p = 1;
+ }
+
+ /* See if operand has no low-order bits; skip OR if so. */
+ if (the_insn.exp.X_op != O_constant
+ || (need_hi22_p && (the_insn.exp.X_add_number & 0x3FF) != 0)
+ || ! need_hi22_p)
+ {
+ the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (rd) : 0)
+ | RD (rd) | IMMED
+ | (the_insn.exp.X_add_number
+ & (the_insn.exp.X_op != O_constant
+ ? 0 : need_hi22_p ? 0x3ff : 0x1fff)));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_LO10 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+}
+
+/* Handle the setsw synthetic instruction. */
+
+static void
+synthetize_setsw (const struct sparc_opcode *insn)
+{
+ int low32, rd, opc;
+
+ rd = (the_insn.opcode & RD (~0)) >> 25;
+
+ if (the_insn.exp.X_op != O_constant)
+ {
+ synthetize_setuw (insn);
+
+ /* Need to sign extend it. */
+ the_insn.opcode = (SRA_INSN | RS1 (rd) | RD (rd));
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ return;
+ }
+
+ if (sizeof (offsetT) > 4
+ && (the_insn.exp.X_add_number < -(offsetT) U0x80000000
+ || the_insn.exp.X_add_number > (offsetT) U0xffffffff))
+ as_warn (_("setsw: number not in -2147483648..4294967295 range"));
+
+ low32 = the_insn.exp.X_add_number;
+
+ if (low32 >= 0)
+ {
+ synthetize_setuw (insn);
+ return;
+ }
+
+ opc = OR_INSN;
+
+ the_insn.reloc = BFD_RELOC_NONE;
+ /* See if operand is absolute and small; skip sethi if so. */
+ if (low32 < -(1 << 12))
+ {
+ the_insn.opcode = (SETHI_INSN | RD (rd)
+ | (((~the_insn.exp.X_add_number) >> 10) & 0x3fffff));
+ output_insn (insn, &the_insn);
+ low32 = 0x1c00 | (low32 & 0x3ff);
+ opc = RS1 (rd) | XOR_INSN;
+ }
+
+ the_insn.opcode = (opc | RD (rd) | IMMED
+ | (low32 & 0x1fff));
+ output_insn (insn, &the_insn);
+}
+
+/* Handle the setsw synthetic instruction. */
+
+static void
+synthetize_setx (const struct sparc_opcode *insn)
+{
+ int upper32, lower32;
+ int tmpreg = (the_insn.opcode & RS1 (~0)) >> 14;
+ int dstreg = (the_insn.opcode & RD (~0)) >> 25;
+ int upper_dstreg;
+ int need_hh22_p = 0, need_hm10_p = 0, need_hi22_p = 0, need_lo10_p = 0;
+ int need_xor10_p = 0;
+
+#define SIGNEXT32(x) ((((x) & U0xffffffff) ^ U0x80000000) - U0x80000000)
+ lower32 = SIGNEXT32 (the_insn.exp.X_add_number);
+ upper32 = SIGNEXT32 (BSR (the_insn.exp.X_add_number, 32));
+#undef SIGNEXT32
+
+ upper_dstreg = tmpreg;
+ /* The tmp reg should not be the dst reg. */
+ if (tmpreg == dstreg)
+ as_warn (_("setx: temporary register same as destination register"));
+
+ /* ??? Obviously there are other optimizations we can do
+ (e.g. sethi+shift for 0x1f0000000) and perhaps we shouldn't be
+ doing some of these. Later. If you do change things, try to
+ change all of this to be table driven as well. */
+ /* What to output depends on the number if it's constant.
+ Compute that first, then output what we've decided upon. */
+ if (the_insn.exp.X_op != O_constant)
+ {
+ if (sparc_arch_size == 32)
+ {
+ /* When arch size is 32, we want setx to be equivalent
+ to setuw for anything but constants. */
+ the_insn.exp.X_add_number &= 0xffffffff;
+ synthetize_setuw (insn);
+ return;
+ }
+ need_hh22_p = need_hm10_p = need_hi22_p = need_lo10_p = 1;
+ lower32 = 0;
+ upper32 = 0;
+ }
+ else
+ {
+ /* Reset X_add_number, we've extracted it as upper32/lower32.
+ Otherwise fixup_segment will complain about not being able to
+ write an 8 byte number in a 4 byte field. */
+ the_insn.exp.X_add_number = 0;
+
+ /* Only need hh22 if `or' insn can't handle constant. */
+ if (upper32 < -(1 << 12) || upper32 >= (1 << 12))
+ need_hh22_p = 1;
+
+ /* Does bottom part (after sethi) have bits? */
+ if ((need_hh22_p && (upper32 & 0x3ff) != 0)
+ /* No hh22, but does upper32 still have bits we can't set
+ from lower32? */
+ || (! need_hh22_p && upper32 != 0 && upper32 != -1))
+ need_hm10_p = 1;
+
+ /* If the lower half is all zero, we build the upper half directly
+ into the dst reg. */
+ if (lower32 != 0
+ /* Need lower half if number is zero or 0xffffffff00000000. */
+ || (! need_hh22_p && ! need_hm10_p))
+ {
+ /* No need for sethi if `or' insn can handle constant. */
+ if (lower32 < -(1 << 12) || lower32 >= (1 << 12)
+ /* Note that we can't use a negative constant in the `or'
+ insn unless the upper 32 bits are all ones. */
+ || (lower32 < 0 && upper32 != -1)
+ || (lower32 >= 0 && upper32 == -1))
+ need_hi22_p = 1;
+
+ if (need_hi22_p && upper32 == -1)
+ need_xor10_p = 1;
+
+ /* Does bottom part (after sethi) have bits? */
+ else if ((need_hi22_p && (lower32 & 0x3ff) != 0)
+ /* No sethi. */
+ || (! need_hi22_p && (lower32 & 0x1fff) != 0)
+ /* Need `or' if we didn't set anything else. */
+ || (! need_hi22_p && ! need_hh22_p && ! need_hm10_p))
+ need_lo10_p = 1;
+ }
+ else
+ /* Output directly to dst reg if lower 32 bits are all zero. */
+ upper_dstreg = dstreg;
+ }
+
+ if (!upper_dstreg && dstreg)
+ as_warn (_("setx: illegal temporary register g0"));
+
+ if (need_hh22_p)
+ {
+ the_insn.opcode = (SETHI_INSN | RD (upper_dstreg)
+ | ((upper32 >> 10) & 0x3fffff));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_SPARC_HH22 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+
+ if (need_hi22_p)
+ {
+ the_insn.opcode = (SETHI_INSN | RD (dstreg)
+ | (((need_xor10_p ? ~lower32 : lower32)
+ >> 10) & 0x3fffff));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_SPARC_LM22 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+
+ if (need_hm10_p)
+ {
+ the_insn.opcode = (OR_INSN
+ | (need_hh22_p ? RS1 (upper_dstreg) : 0)
+ | RD (upper_dstreg)
+ | IMMED
+ | (upper32 & (need_hh22_p ? 0x3ff : 0x1fff)));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_SPARC_HM10 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+
+ if (need_lo10_p)
+ {
+ /* FIXME: One nice optimization to do here is to OR the low part
+ with the highpart if hi22 isn't needed and the low part is
+ positive. */
+ the_insn.opcode = (OR_INSN | (need_hi22_p ? RS1 (dstreg) : 0)
+ | RD (dstreg)
+ | IMMED
+ | (lower32 & (need_hi22_p ? 0x3ff : 0x1fff)));
+ the_insn.reloc = (the_insn.exp.X_op != O_constant
+ ? BFD_RELOC_LO10 : BFD_RELOC_NONE);
+ output_insn (insn, &the_insn);
+ }
+
+ /* If we needed to build the upper part, shift it into place. */
+ if (need_hh22_p || need_hm10_p)
+ {
+ the_insn.opcode = (SLLX_INSN | RS1 (upper_dstreg) | RD (upper_dstreg)
+ | IMMED | 32);
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ }
+
+ /* To get -1 in upper32, we do sethi %hi(~x), r; xor r, -0x400 | x, r. */
+ if (need_xor10_p)
+ {
+ the_insn.opcode = (XOR_INSN | RS1 (dstreg) | RD (dstreg) | IMMED
+ | 0x1c00 | (lower32 & 0x3ff));
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ }
+
+ /* If we needed to build both upper and lower parts, OR them together. */
+ else if ((need_hh22_p || need_hm10_p) && (need_hi22_p || need_lo10_p))
+ {
+ the_insn.opcode = (OR_INSN | RS1 (dstreg) | RS2 (upper_dstreg)
+ | RD (dstreg));
+ the_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &the_insn);
+ }
+}
+
+/* Main entry point to assemble one instruction. */
+
+void
+md_assemble (char *str)
+{
+ const struct sparc_opcode *insn;
+ int special_case;
+
+ know (str);
+ special_case = sparc_ip (str, &insn);
+ if (insn == NULL)
+ return;
+
+ /* We warn about attempts to put a floating point branch in a delay slot,
+ unless the delay slot has been annulled. */
+ if (last_insn != NULL
+ && (insn->flags & F_FBR) != 0
+ && (last_insn->flags & F_DELAYED) != 0
+ /* ??? This test isn't completely accurate. We assume anything with
+ F_{UNBR,CONDBR,FBR} set is annullable. */
+ && ((last_insn->flags & (F_UNBR | F_CONDBR | F_FBR)) == 0
+ || (last_opcode & ANNUL) == 0))
+ as_warn (_("FP branch in delay slot"));
+
+ /* SPARC before v9 requires a nop instruction between a floating
+ point instruction and a floating point branch. We insert one
+ automatically, with a warning. */
+ if (max_architecture < SPARC_OPCODE_ARCH_V9
+ && last_insn != NULL
+ && (insn->flags & F_FBR) != 0
+ && (last_insn->flags & F_FLOAT) != 0)
+ {
+ struct sparc_it nop_insn;
+
+ nop_insn.opcode = NOP_INSN;
+ nop_insn.reloc = BFD_RELOC_NONE;
+ output_insn (insn, &nop_insn);
+ as_warn (_("FP branch preceded by FP instruction; NOP inserted"));
+ }
+
+ switch (special_case)
+ {
+ case SPECIAL_CASE_NONE:
+ /* Normal insn. */
+ output_insn (insn, &the_insn);
+ break;
+
+ case SPECIAL_CASE_SETSW:
+ synthetize_setsw (insn);
+ break;
+
+ case SPECIAL_CASE_SET:
+ synthetize_setuw (insn);
+ break;
+
+ case SPECIAL_CASE_SETX:
+ synthetize_setx (insn);
+ break;
+
+ case SPECIAL_CASE_FDIV:
+ {
+ int rd = (the_insn.opcode >> 25) & 0x1f;
+
+ output_insn (insn, &the_insn);
+
+ /* According to information leaked from Sun, the "fdiv" instructions
+ on early SPARC machines would produce incorrect results sometimes.
+ The workaround is to add an fmovs of the destination register to
+ itself just after the instruction. This was true on machines
+ with Weitek 1165 float chips, such as the Sun-4/260 and /280. */
+ gas_assert (the_insn.reloc == BFD_RELOC_NONE);
+ the_insn.opcode = FMOVS_INSN | rd | RD (rd);
+ output_insn (insn, &the_insn);
+ return;
+ }
+
+ default:
+ as_fatal (_("failed special case insn sanity check"));
+ }
+}
+
+static const char *
+get_hwcap_name (bfd_uint64_t mask)
+{
+ if (mask & HWCAP_MUL32)
+ return "mul32";
+ if (mask & HWCAP_DIV32)
+ return "div32";
+ if (mask & HWCAP_FSMULD)
+ return "fsmuld";
+ if (mask & HWCAP_V8PLUS)
+ return "v8plus";
+ if (mask & HWCAP_POPC)
+ return "popc";
+ if (mask & HWCAP_VIS)
+ return "vis";
+ if (mask & HWCAP_VIS2)
+ return "vis2";
+ if (mask & HWCAP_ASI_BLK_INIT)
+ return "ASIBlkInit";
+ if (mask & HWCAP_FMAF)
+ return "fmaf";
+ if (mask & HWCAP_VIS3)
+ return "vis3";
+ if (mask & HWCAP_HPC)
+ return "hpc";
+ if (mask & HWCAP_RANDOM)
+ return "random";
+ if (mask & HWCAP_TRANS)
+ return "trans";
+ if (mask & HWCAP_FJFMAU)
+ return "fjfmau";
+ if (mask & HWCAP_IMA)
+ return "ima";
+ if (mask & HWCAP_ASI_CACHE_SPARING)
+ return "cspare";
+ if (mask & HWCAP_AES)
+ return "aes";
+ if (mask & HWCAP_DES)
+ return "des";
+ if (mask & HWCAP_KASUMI)
+ return "kasumi";
+ if (mask & HWCAP_CAMELLIA)
+ return "camellia";
+ if (mask & HWCAP_MD5)
+ return "md5";
+ if (mask & HWCAP_SHA1)
+ return "sha1";
+ if (mask & HWCAP_SHA256)
+ return "sha256";
+ if (mask & HWCAP_SHA512)
+ return "sha512";
+ if (mask & HWCAP_MPMUL)
+ return "mpmul";
+ if (mask & HWCAP_MONT)
+ return "mont";
+ if (mask & HWCAP_PAUSE)
+ return "pause";
+ if (mask & HWCAP_CBCOND)
+ return "cbcond";
+ if (mask & HWCAP_CRC32C)
+ return "crc32c";
+
+ mask = mask >> 32;
+ if (mask & HWCAP2_FJATHPLUS)
+ return "fjathplus";
+ if (mask & HWCAP2_VIS3B)
+ return "vis3b";
+ if (mask & HWCAP2_ADP)
+ return "adp";
+ if (mask & HWCAP2_SPARC5)
+ return "sparc5";
+ if (mask & HWCAP2_MWAIT)
+ return "mwait";
+ if (mask & HWCAP2_XMPMUL)
+ return "xmpmul";
+ if (mask & HWCAP2_XMONT)
+ return "xmont";
+ if (mask & HWCAP2_NSEC)
+ return "nsec";
+
+ return "UNKNOWN";
+}
+
+/* Subroutine of md_assemble to do the actual parsing. */
+
+static int
+sparc_ip (char *str, const struct sparc_opcode **pinsn)
+{
+ char *error_message = "";
+ char *s;
+ const char *args;
+ char c;
+ const struct sparc_opcode *insn;
+ char *argsStart;
+ unsigned long opcode;
+ unsigned int mask = 0;
+ int match = 0;
+ int comma = 0;
+ int v9_arg_p;
+ int special_case = SPECIAL_CASE_NONE;
+
+ s = str;
+ if (ISLOWER (*s))
+ {
+ do
+ ++s;
+ while (ISLOWER (*s) || ISDIGIT (*s) || *s == '_');
+ }
+
+ switch (*s)
+ {
+ case '\0':
+ break;
+
+ case ',':
+ comma = 1;
+ /* Fall through. */
+
+ case ' ':
+ *s++ = '\0';
+ break;
+
+ default:
+ as_bad (_("Unknown opcode: `%s'"), str);
+ *pinsn = NULL;
+ return special_case;
+ }
+ insn = (struct sparc_opcode *) hash_find (op_hash, str);
+ *pinsn = insn;
+ if (insn == NULL)
+ {
+ as_bad (_("Unknown opcode: `%s'"), str);
+ return special_case;
+ }
+ if (comma)
+ {
+ *--s = ',';
+ }
+
+ argsStart = s;
+ for (;;)
+ {
+ opcode = insn->match;
+ memset (&the_insn, '\0', sizeof (the_insn));
+ the_insn.reloc = BFD_RELOC_NONE;
+ v9_arg_p = 0;
+
+ /* Build the opcode, checking as we go to make sure that the
+ operands match. */
+ for (args = insn->args;; ++args)
+ {
+ switch (*args)
+ {
+ case 'K':
+ {
+ int kmask = 0;
+
+ /* Parse a series of masks. */
+ if (*s == '#')
+ {
+ while (*s == '#')
+ {
+ int jmask;
+
+ if (! parse_keyword_arg (sparc_encode_membar, &s,
+ &jmask))
+ {
+ error_message = _(": invalid membar mask name");
+ goto error;
+ }
+ kmask |= jmask;
+ while (*s == ' ')
+ ++s;
+ if (*s == '|' || *s == '+')
+ ++s;
+ while (*s == ' ')
+ ++s;
+ }
+ }
+ else
+ {
+ if (! parse_const_expr_arg (&s, &kmask))
+ {
+ error_message = _(": invalid membar mask expression");
+ goto error;
+ }
+ if (kmask < 0 || kmask > 127)
+ {
+ error_message = _(": invalid membar mask number");
+ goto error;
+ }
+ }
+
+ opcode |= MEMBAR (kmask);
+ continue;
+ }
+
+ case '3':
+ {
+ int smask = 0;
+
+ if (! parse_const_expr_arg (&s, &smask))
+ {
+ error_message = _(": invalid siam mode expression");
+ goto error;
+ }
+ if (smask < 0 || smask > 7)
+ {
+ error_message = _(": invalid siam mode number");
+ goto error;
+ }
+ opcode |= smask;
+ continue;
+ }
+
+ case '*':
+ {
+ int fcn = 0;
+
+ /* Parse a prefetch function. */
+ if (*s == '#')
+ {
+ if (! parse_keyword_arg (sparc_encode_prefetch, &s, &fcn))
+ {
+ error_message = _(": invalid prefetch function name");
+ goto error;
+ }
+ }
+ else
+ {
+ if (! parse_const_expr_arg (&s, &fcn))
+ {
+ error_message = _(": invalid prefetch function expression");
+ goto error;
+ }
+ if (fcn < 0 || fcn > 31)
+ {
+ error_message = _(": invalid prefetch function number");
+ goto error;
+ }
+ }
+ opcode |= RD (fcn);
+ continue;
+ }
+
+ case '!':
+ case '?':
+ /* Parse a sparc64 privileged register. */
+ if (*s == '%')
+ {
+ struct priv_reg_entry *p = priv_reg_table;
+ unsigned int len = 9999999; /* Init to make gcc happy. */
+
+ s += 1;
+ while (p->name[0] > s[0])
+ p++;
+ while (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ p++;
+ }
+ if (p->name[0] != s[0])
+ {
+ error_message = _(": unrecognizable privileged register");
+ goto error;
+ }
+ if (*args == '?')
+ opcode |= (p->regnum << 14);
+ else
+ opcode |= (p->regnum << 25);
+ s += len;
+ continue;
+ }
+ else
+ {
+ error_message = _(": unrecognizable privileged register");
+ goto error;
+ }
+
+ case '$':
+ case '%':
+ /* Parse a sparc64 hyperprivileged register. */
+ if (*s == '%')
+ {
+ struct priv_reg_entry *p = hpriv_reg_table;
+ unsigned int len = 9999999; /* Init to make gcc happy. */
+
+ s += 1;
+ while (p->name[0] > s[0])
+ p++;
+ while (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ p++;
+ }
+ if (p->name[0] != s[0])
+ {
+ error_message = _(": unrecognizable hyperprivileged register");
+ goto error;
+ }
+ if (*args == '$')
+ opcode |= (p->regnum << 14);
+ else
+ opcode |= (p->regnum << 25);
+ s += len;
+ continue;
+ }
+ else
+ {
+ error_message = _(": unrecognizable hyperprivileged register");
+ goto error;
+ }
+
+ case '_':
+ case '/':
+ /* Parse a v9a/v9b ancillary state register. */
+ if (*s == '%')
+ {
+ struct priv_reg_entry *p = v9a_asr_table;
+ unsigned int len = 9999999; /* Init to make gcc happy. */
+
+ s += 1;
+ while (p->name[0] > s[0])
+ p++;
+ while (p->name[0] == s[0])
+ {
+ len = strlen (p->name);
+ if (strncmp (p->name, s, len) == 0)
+ break;
+ p++;
+ }
+ if (p->name[0] != s[0])
+ {
+ error_message = _(": unrecognizable v9a or v9b ancillary state register");
+ goto error;
+ }
+ if (*args == '/' && (p->regnum == 20 || p->regnum == 21))
+ {
+ error_message = _(": rd on write only ancillary state register");
+ goto error;
+ }
+ if (p->regnum >= 24
+ && (insn->architecture
+ & SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9A)))
+ {
+ /* %sys_tick and %sys_tick_cmpr are v9bnotv9a */
+ error_message = _(": unrecognizable v9a ancillary state register");
+ goto error;
+ }
+ if (*args == '/')
+ opcode |= (p->regnum << 14);
+ else
+ opcode |= (p->regnum << 25);
+ s += len;
+ continue;
+ }
+ else
+ {
+ error_message = _(": unrecognizable v9a or v9b ancillary state register");
+ goto error;
+ }
+
+ case 'M':
+ case 'm':
+ if (strncmp (s, "%asr", 4) == 0)
+ {
+ s += 4;
+
+ if (ISDIGIT (*s))
+ {
+ long num = 0;
+
+ while (ISDIGIT (*s))
+ {
+ num = num * 10 + *s - '0';
+ ++s;
+ }
+
+ if (current_architecture >= SPARC_OPCODE_ARCH_V9)
+ {
+ if (num < 16 || 31 < num)
+ {
+ error_message = _(": asr number must be between 16 and 31");
+ goto error;
+ }
+ }
+ else
+ {
+ if (num < 0 || 31 < num)
+ {
+ error_message = _(": asr number must be between 0 and 31");
+ goto error;
+ }
+ }
+
+ opcode |= (*args == 'M' ? RS1 (num) : RD (num));
+ continue;
+ }
+ else
+ {
+ error_message = _(": expecting %asrN");
+ goto error;
+ }
+ } /* if %asr */
+ break;
+
+ case 'I':
+ the_insn.reloc = BFD_RELOC_SPARC_11;
+ goto immediate;
+
+ case 'j':
+ the_insn.reloc = BFD_RELOC_SPARC_10;
+ goto immediate;
+
+ case ')':
+ if (*s == ' ')
+ s++;
+ if ((s[0] == '0' && s[1] == 'x' && ISXDIGIT (s[2]))
+ || ISDIGIT (*s))
+ {
+ long num = 0;
+
+ if (s[0] == '0' && s[1] == 'x')
+ {
+ s += 2;
+ while (ISXDIGIT (*s))
+ {
+ num <<= 4;
+ num |= hex_value (*s);
+ ++s;
+ }
+ }
+ else
+ {
+ while (ISDIGIT (*s))
+ {
+ num = num * 10 + *s - '0';
+ ++s;
+ }
+ }
+ if (num < 0 || num > 31)
+ {
+ error_message = _(": crypto immediate must be between 0 and 31");
+ goto error;
+ }
+
+ opcode |= RS3 (num);
+ continue;
+ }
+ else
+ {
+ error_message = _(": expecting crypto immediate");
+ goto error;
+ }
+
+ case 'X':
+ /* V8 systems don't understand BFD_RELOC_SPARC_5. */
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ the_insn.reloc = BFD_RELOC_SPARC_5;
+ else
+ the_insn.reloc = BFD_RELOC_SPARC13;
+ /* These fields are unsigned, but for upward compatibility,
+ allow negative values as well. */
+ goto immediate;
+
+ case 'Y':
+ /* V8 systems don't understand BFD_RELOC_SPARC_6. */
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ the_insn.reloc = BFD_RELOC_SPARC_6;
+ else
+ the_insn.reloc = BFD_RELOC_SPARC13;
+ /* These fields are unsigned, but for upward compatibility,
+ allow negative values as well. */
+ goto immediate;
+
+ case 'k':
+ the_insn.reloc = /* RELOC_WDISP2_14 */ BFD_RELOC_SPARC_WDISP16;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case '=':
+ the_insn.reloc = /* RELOC_WDISP2_8 */ BFD_RELOC_SPARC_WDISP10;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'G':
+ the_insn.reloc = BFD_RELOC_SPARC_WDISP19;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'N':
+ if (*s == 'p' && s[1] == 'n')
+ {
+ s += 2;
+ continue;
+ }
+ break;
+
+ case 'T':
+ if (*s == 'p' && s[1] == 't')
+ {
+ s += 2;
+ continue;
+ }
+ break;
+
+ case 'z':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%icc", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'Z':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%xcc", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case '6':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc0", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '7':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc1", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '8':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc2", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '9':
+ if (*s == ' ')
+ {
+ ++s;
+ }
+ if (strncmp (s, "%fcc3", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case 'P':
+ if (strncmp (s, "%pc", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'W':
+ if (strncmp (s, "%tick", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '\0': /* End of args. */
+ if (s[0] == ',' && s[1] == '%')
+ {
+ static const struct ops
+ {
+ /* The name as it appears in assembler. */
+ char *name;
+ /* strlen (name), precomputed for speed */
+ int len;
+ /* The reloc this pseudo-op translates to. */
+ int reloc;
+ /* 1 if tls call. */
+ int tls_call;
+ }
+ ops[] =
+ {
+ { "tgd_add", 7, BFD_RELOC_SPARC_TLS_GD_ADD, 0 },
+ { "tgd_call", 8, BFD_RELOC_SPARC_TLS_GD_CALL, 1 },
+ { "tldm_add", 8, BFD_RELOC_SPARC_TLS_LDM_ADD, 0 },
+ { "tldm_call", 9, BFD_RELOC_SPARC_TLS_LDM_CALL, 1 },
+ { "tldo_add", 8, BFD_RELOC_SPARC_TLS_LDO_ADD, 0 },
+ { "tie_ldx", 7, BFD_RELOC_SPARC_TLS_IE_LDX, 0 },
+ { "tie_ld", 6, BFD_RELOC_SPARC_TLS_IE_LD, 0 },
+ { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 },
+ { "gdop", 4, BFD_RELOC_SPARC_GOTDATA_OP, 0 },
+ { NULL, 0, 0, 0 }
+ };
+ const struct ops *o;
+ char *s1;
+ int npar = 0;
+
+ for (o = ops; o->name; o++)
+ if (strncmp (s + 2, o->name, o->len) == 0)
+ break;
+ if (o->name == NULL)
+ break;
+
+ if (s[o->len + 2] != '(')
+ {
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ return special_case;
+ }
+
+ if (! o->tls_call && the_insn.reloc != BFD_RELOC_NONE)
+ {
+ as_bad (_("Illegal operands: %%%s cannot be used together with other relocs in the insn ()"),
+ o->name);
+ return special_case;
+ }
+
+ if (o->tls_call
+ && (the_insn.reloc != BFD_RELOC_32_PCREL_S2
+ || the_insn.exp.X_add_number != 0
+ || the_insn.exp.X_add_symbol
+ != symbol_find_or_make ("__tls_get_addr")))
+ {
+ as_bad (_("Illegal operands: %%%s can be only used with call __tls_get_addr"),
+ o->name);
+ return special_case;
+ }
+
+ the_insn.reloc = o->reloc;
+ memset (&the_insn.exp, 0, sizeof (the_insn.exp));
+ s += o->len + 3;
+
+ for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+ if (*s1 == '(')
+ npar++;
+ else if (*s1 == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+
+ if (*s1 != ')')
+ {
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ return special_case;
+ }
+
+ *s1 = '\0';
+ (void) get_expression (s);
+ *s1 = ')';
+ s = s1 + 1;
+ }
+ if (*s == '\0')
+ match = 1;
+ break;
+
+ case '+':
+ if (*s == '+')
+ {
+ ++s;
+ continue;
+ }
+ if (*s == '-')
+ {
+ continue;
+ }
+ break;
+
+ case '[': /* These must match exactly. */
+ case ']':
+ case ',':
+ case ' ':
+ if (*s++ == *args)
+ continue;
+ break;
+
+ case '#': /* Must be at least one digit. */
+ if (ISDIGIT (*s++))
+ {
+ while (ISDIGIT (*s))
+ {
+ ++s;
+ }
+ continue;
+ }
+ break;
+
+ case 'C': /* Coprocessor state register. */
+ if (strncmp (s, "%csr", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'b': /* Next operand is a coprocessor register. */
+ case 'c':
+ case 'D':
+ if (*s++ == '%' && *s++ == 'c' && ISDIGIT (*s))
+ {
+ mask = *s++;
+ if (ISDIGIT (*s))
+ {
+ mask = 10 * (mask - '0') + (*s++ - '0');
+ if (mask >= 32)
+ {
+ break;
+ }
+ }
+ else
+ {
+ mask -= '0';
+ }
+ switch (*args)
+ {
+
+ case 'b':
+ opcode |= mask << 14;
+ continue;
+
+ case 'c':
+ opcode |= mask;
+ continue;
+
+ case 'D':
+ opcode |= mask << 25;
+ continue;
+ }
+ }
+ break;
+
+ case 'r': /* next operand must be a register */
+ case 'O':
+ case '1':
+ case '2':
+ case 'd':
+ if (*s++ == '%')
+ {
+ switch (c = *s++)
+ {
+
+ case 'f': /* frame pointer */
+ if (*s++ == 'p')
+ {
+ mask = 0x1e;
+ break;
+ }
+ goto error;
+
+ case 'g': /* global register */
+ c = *s++;
+ if (isoctal (c))
+ {
+ mask = c - '0';
+ break;
+ }
+ goto error;
+
+ case 'i': /* in register */
+ c = *s++;
+ if (isoctal (c))
+ {
+ mask = c - '0' + 24;
+ break;
+ }
+ goto error;
+
+ case 'l': /* local register */
+ c = *s++;
+ if (isoctal (c))
+ {
+ mask = (c - '0' + 16);
+ break;
+ }
+ goto error;
+
+ case 'o': /* out register */
+ c = *s++;
+ if (isoctal (c))
+ {
+ mask = (c - '0' + 8);
+ break;
+ }
+ goto error;
+
+ case 's': /* stack pointer */
+ if (*s++ == 'p')
+ {
+ mask = 0xe;
+ break;
+ }
+ goto error;
+
+ case 'r': /* any register */
+ if (!ISDIGIT ((c = *s++)))
+ {
+ goto error;
+ }
+ /* FALLTHROUGH */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (ISDIGIT (*s))
+ {
+ if ((c = 10 * (c - '0') + (*s++ - '0')) >= 32)
+ {
+ goto error;
+ }
+ }
+ else
+ {
+ c -= '0';
+ }
+ mask = c;
+ break;
+
+ default:
+ goto error;
+ }
+
+ if ((mask & ~1) == 2 && sparc_arch_size == 64
+ && no_undeclared_regs && ! globals[mask])
+ as_bad (_("detected global register use not covered by .register pseudo-op"));
+
+ /* Got the register, now figure out where
+ it goes in the opcode. */
+ switch (*args)
+ {
+ case '1':
+ opcode |= mask << 14;
+ continue;
+
+ case '2':
+ opcode |= mask;
+ continue;
+
+ case 'd':
+ opcode |= mask << 25;
+ continue;
+
+ case 'r':
+ opcode |= (mask << 25) | (mask << 14);
+ continue;
+
+ case 'O':
+ opcode |= (mask << 25) | (mask << 0);
+ continue;
+ }
+ }
+ break;
+
+ case 'e': /* next operand is a floating point register */
+ case 'v':
+ case 'V':
+
+ case 'f':
+ case 'B':
+ case 'R':
+
+ case '4':
+ case '5':
+
+ case 'g':
+ case 'H':
+ case 'J':
+ case '}':
+ {
+ char format;
+
+ if (*s++ == '%'
+ && ((format = *s) == 'f')
+ && ISDIGIT (*++s))
+ {
+ for (mask = 0; ISDIGIT (*s); ++s)
+ {
+ mask = 10 * mask + (*s - '0');
+ } /* read the number */
+
+ if ((*args == 'v'
+ || *args == 'B'
+ || *args == '5'
+ || *args == 'H')
+ && (mask & 1))
+ {
+ break;
+ } /* register must be even numbered */
+
+ if ((*args == 'V'
+ || *args == 'R'
+ || *args == 'J')
+ && (mask & 3))
+ {
+ break;
+ } /* register must be multiple of 4 */
+
+ if (mask >= 64)
+ {
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ error_message = _(": There are only 64 f registers; [0-63]");
+ else
+ error_message = _(": There are only 32 f registers; [0-31]");
+ goto error;
+ } /* on error */
+ else if (mask >= 32)
+ {
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ {
+ if (*args == 'e' || *args == 'f' || *args == 'g')
+ {
+ error_message
+ = _(": There are only 32 single precision f registers; [0-31]");
+ goto error;
+ }
+ v9_arg_p = 1;
+ mask -= 31; /* wrap high bit */
+ }
+ else
+ {
+ error_message = _(": There are only 32 f registers; [0-31]");
+ goto error;
+ }
+ }
+ }
+ else
+ {
+ break;
+ } /* if not an 'f' register. */
+
+ if (*args == '}' && mask != RS2 (opcode))
+ {
+ error_message
+ = _(": Instruction requires frs2 and frsd must be the same register");
+ goto error;
+ }
+
+ switch (*args)
+ {
+ case 'v':
+ case 'V':
+ case 'e':
+ opcode |= RS1 (mask);
+ continue;
+
+ case 'f':
+ case 'B':
+ case 'R':
+ opcode |= RS2 (mask);
+ continue;
+
+ case '4':
+ case '5':
+ opcode |= RS3 (mask);
+ continue;
+
+ case 'g':
+ case 'H':
+ case 'J':
+ case '}':
+ opcode |= RD (mask);
+ continue;
+ } /* Pack it in. */
+
+ know (0);
+ break;
+ } /* float arg */
+
+ case 'F':
+ if (strncmp (s, "%fsr", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case '(':
+ if (strncmp (s, "%efsr", 5) == 0)
+ {
+ s += 5;
+ continue;
+ }
+ break;
+
+ case '0': /* 64 bit immediate (set, setsw, setx insn) */
+ the_insn.reloc = BFD_RELOC_NONE; /* reloc handled elsewhere */
+ goto immediate;
+
+ case 'l': /* 22 bit PC relative immediate */
+ the_insn.reloc = BFD_RELOC_SPARC_WDISP22;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'L': /* 30 bit immediate */
+ the_insn.reloc = BFD_RELOC_32_PCREL_S2;
+ the_insn.pcrel = 1;
+ goto immediate;
+
+ case 'h':
+ case 'n': /* 22 bit immediate */
+ the_insn.reloc = BFD_RELOC_SPARC22;
+ goto immediate;
+
+ case 'i': /* 13 bit immediate */
+ the_insn.reloc = BFD_RELOC_SPARC13;
+
+ /* fallthrough */
+
+ immediate:
+ if (*s == ' ')
+ s++;
+
+ {
+ char *s1;
+ char *op_arg = NULL;
+ static expressionS op_exp;
+ bfd_reloc_code_real_type old_reloc = the_insn.reloc;
+
+ /* Check for %hi, etc. */
+ if (*s == '%')
+ {
+ static const struct ops {
+ /* The name as it appears in assembler. */
+ char *name;
+ /* strlen (name), precomputed for speed */
+ int len;
+ /* The reloc this pseudo-op translates to. */
+ int reloc;
+ /* Non-zero if for v9 only. */
+ int v9_p;
+ /* Non-zero if can be used in pc-relative contexts. */
+ int pcrel_p;/*FIXME:wip*/
+ } ops[] = {
+ /* hix/lox must appear before hi/lo so %hix won't be
+ mistaken for %hi. */
+ { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 },
+ { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 },
+ { "hi", 2, BFD_RELOC_HI22, 0, 1 },
+ { "lo", 2, BFD_RELOC_LO10, 0, 1 },
+ { "pc22", 4, BFD_RELOC_SPARC_PC22, 0, 1 },
+ { "pc10", 4, BFD_RELOC_SPARC_PC10, 0, 1 },
+ { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 },
+ { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 },
+ { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 },
+ { "h34", 3, BFD_RELOC_SPARC_H34, 1, 0 },
+ { "l34", 3, BFD_RELOC_SPARC_L44, 1, 0 },
+ { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 },
+ { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 },
+ { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 },
+ { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 },
+ { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 },
+ { "tgd_hi22", 8, BFD_RELOC_SPARC_TLS_GD_HI22, 0, 0 },
+ { "tgd_lo10", 8, BFD_RELOC_SPARC_TLS_GD_LO10, 0, 0 },
+ { "tldm_hi22", 9, BFD_RELOC_SPARC_TLS_LDM_HI22, 0, 0 },
+ { "tldm_lo10", 9, BFD_RELOC_SPARC_TLS_LDM_LO10, 0, 0 },
+ { "tldo_hix22", 10, BFD_RELOC_SPARC_TLS_LDO_HIX22, 0,
+ 0 },
+ { "tldo_lox10", 10, BFD_RELOC_SPARC_TLS_LDO_LOX10, 0,
+ 0 },
+ { "tie_hi22", 8, BFD_RELOC_SPARC_TLS_IE_HI22, 0, 0 },
+ { "tie_lo10", 8, BFD_RELOC_SPARC_TLS_IE_LO10, 0, 0 },
+ { "tle_hix22", 9, BFD_RELOC_SPARC_TLS_LE_HIX22, 0, 0 },
+ { "tle_lox10", 9, BFD_RELOC_SPARC_TLS_LE_LOX10, 0, 0 },
+ { "gdop_hix22", 10, BFD_RELOC_SPARC_GOTDATA_OP_HIX22,
+ 0, 0 },
+ { "gdop_lox10", 10, BFD_RELOC_SPARC_GOTDATA_OP_LOX10,
+ 0, 0 },
+ { NULL, 0, 0, 0, 0 }
+ };
+ const struct ops *o;
+
+ for (o = ops; o->name; o++)
+ if (strncmp (s + 1, o->name, o->len) == 0)
+ break;
+ if (o->name == NULL)
+ break;
+
+ if (s[o->len + 1] != '(')
+ {
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name);
+ return special_case;
+ }
+
+ op_arg = o->name;
+ the_insn.reloc = o->reloc;
+ s += o->len + 2;
+ v9_arg_p = o->v9_p;
+ }
+
+ /* Note that if the get_expression() fails, we will still
+ have created U entries in the symbol table for the
+ 'symbols' in the input string. Try not to create U
+ symbols for registers, etc. */
+
+ /* This stuff checks to see if the expression ends in
+ +%reg. If it does, it removes the register from
+ the expression, and re-sets 's' to point to the
+ right place. */
+
+ if (op_arg)
+ {
+ int npar = 0;
+
+ for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+ if (*s1 == '(')
+ npar++;
+ else if (*s1 == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+
+ if (*s1 != ')')
+ {
+ as_bad (_("Illegal operands: %%%s requires arguments in ()"), op_arg);
+ return special_case;
+ }
+
+ *s1 = '\0';
+ (void) get_expression (s);
+ *s1 = ')';
+ s = s1 + 1;
+ if (*s == ',' || *s == ']' || !*s)
+ continue;
+ if (*s != '+' && *s != '-')
+ {
+ as_bad (_("Illegal operands: Can't do arithmetics other than + and - involving %%%s()"), op_arg);
+ return special_case;
+ }
+ *s1 = '0';
+ s = s1;
+ op_exp = the_insn.exp;
+ memset (&the_insn.exp, 0, sizeof (the_insn.exp));
+ }
+
+ for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++)
+ ;
+
+ if (s1 != s && ISDIGIT (s1[-1]))
+ {
+ if (s1[-2] == '%' && s1[-3] == '+')
+ s1 -= 3;
+ else if (strchr ("golir0123456789", s1[-2]) && s1[-3] == '%' && s1[-4] == '+')
+ s1 -= 4;
+ else if (s1[-3] == 'r' && s1[-4] == '%' && s1[-5] == '+')
+ s1 -= 5;
+ else
+ s1 = NULL;
+ if (s1)
+ {
+ *s1 = '\0';
+ if (op_arg && s1 == s + 1)
+ the_insn.exp.X_op = O_absent;
+ else
+ (void) get_expression (s);
+ *s1 = '+';
+ if (op_arg)
+ *s = ')';
+ s = s1;
+ }
+ }
+ else
+ s1 = NULL;
+
+ if (!s1)
+ {
+ (void) get_expression (s);
+ if (op_arg)
+ *s = ')';
+ s = expr_end;
+ }
+
+ if (op_arg)
+ {
+ the_insn.exp2 = the_insn.exp;
+ the_insn.exp = op_exp;
+ if (the_insn.exp2.X_op == O_absent)
+ the_insn.exp2.X_op = O_illegal;
+ else if (the_insn.exp.X_op == O_absent)
+ {
+ the_insn.exp = the_insn.exp2;
+ the_insn.exp2.X_op = O_illegal;
+ }
+ else if (the_insn.exp.X_op == O_constant)
+ {
+ valueT val = the_insn.exp.X_add_number;
+ switch (the_insn.reloc)
+ {
+ default:
+ break;
+
+ case BFD_RELOC_SPARC_HH22:
+ val = BSR (val, 32);
+ /* Fall through. */
+
+ case BFD_RELOC_SPARC_LM22:
+ case BFD_RELOC_HI22:
+ val = (val >> 10) & 0x3fffff;
+ break;
+
+ case BFD_RELOC_SPARC_HM10:
+ val = BSR (val, 32);
+ /* Fall through. */
+
+ case BFD_RELOC_LO10:
+ val &= 0x3ff;
+ break;
+
+ case BFD_RELOC_SPARC_H34:
+ val >>= 12;
+ val &= 0x3fffff;
+ break;
+
+ case BFD_RELOC_SPARC_H44:
+ val >>= 22;
+ val &= 0x3fffff;
+ break;
+
+ case BFD_RELOC_SPARC_M44:
+ val >>= 12;
+ val &= 0x3ff;
+ break;
+
+ case BFD_RELOC_SPARC_L44:
+ val &= 0xfff;
+ break;
+
+ case BFD_RELOC_SPARC_HIX22:
+ val = ~val;
+ val = (val >> 10) & 0x3fffff;
+ break;
+
+ case BFD_RELOC_SPARC_LOX10:
+ val = (val & 0x3ff) | 0x1c00;
+ break;
+ }
+ the_insn.exp = the_insn.exp2;
+ the_insn.exp.X_add_number += val;
+ the_insn.exp2.X_op = O_illegal;
+ the_insn.reloc = old_reloc;
+ }
+ else if (the_insn.exp2.X_op != O_constant)
+ {
+ as_bad (_("Illegal operands: Can't add non-constant expression to %%%s()"), op_arg);
+ return special_case;
+ }
+ else
+ {
+ if (old_reloc != BFD_RELOC_SPARC13
+ || the_insn.reloc != BFD_RELOC_LO10
+ || sparc_arch_size != 64
+ || sparc_pic_code)
+ {
+ as_bad (_("Illegal operands: Can't do arithmetics involving %%%s() of a relocatable symbol"), op_arg);
+ return special_case;
+ }
+ the_insn.reloc = BFD_RELOC_SPARC_OLO10;
+ }
+ }
+ }
+ /* Check for constants that don't require emitting a reloc. */
+ if (the_insn.exp.X_op == O_constant
+ && the_insn.exp.X_add_symbol == 0
+ && the_insn.exp.X_op_symbol == 0)
+ {
+ /* For pc-relative call instructions, we reject
+ constants to get better code. */
+ if (the_insn.pcrel
+ && the_insn.reloc == BFD_RELOC_32_PCREL_S2
+ && in_signed_range (the_insn.exp.X_add_number, 0x3fff))
+ {
+ error_message = _(": PC-relative operand can't be a constant");
+ goto error;
+ }
+
+ if (the_insn.reloc >= BFD_RELOC_SPARC_TLS_GD_HI22
+ && the_insn.reloc <= BFD_RELOC_SPARC_TLS_TPOFF64)
+ {
+ error_message = _(": TLS operand can't be a constant");
+ goto error;
+ }
+
+ /* Constants that won't fit are checked in md_apply_fix
+ and bfd_install_relocation.
+ ??? It would be preferable to install the constants
+ into the insn here and save having to create a fixS
+ for each one. There already exists code to handle
+ all the various cases (e.g. in md_apply_fix and
+ bfd_install_relocation) so duplicating all that code
+ here isn't right. */
+
+ /* This is a special case to handle cbcond instructions
+ properly, which can need two relocations. The first
+ one is for the 5-bit immediate field and the latter
+ is going to be for the WDISP10 branch part. We
+ handle the R_SPARC_5 immediate directly here so that
+ we don't need to add support for multiple relocations
+ in one instruction just yet. */
+ if (the_insn.reloc == BFD_RELOC_SPARC_5)
+ {
+ valueT val = the_insn.exp.X_add_number;
+
+ if (! in_bitfield_range (val, 0x1f))
+ {
+ error_message = _(": Immediate value in cbcond is out of range.");
+ goto error;
+ }
+ opcode |= val & 0x1f;
+ the_insn.reloc = BFD_RELOC_NONE;
+ }
+ }
+
+ continue;
+
+ case 'a':
+ if (*s++ == 'a')
+ {
+ opcode |= ANNUL;
+ continue;
+ }
+ break;
+
+ case 'A':
+ {
+ int asi = 0;
+
+ /* Parse an asi. */
+ if (*s == '#')
+ {
+ if (! parse_keyword_arg (sparc_encode_asi, &s, &asi))
+ {
+ error_message = _(": invalid ASI name");
+ goto error;
+ }
+ }
+ else
+ {
+ if (! parse_const_expr_arg (&s, &asi))
+ {
+ error_message = _(": invalid ASI expression");
+ goto error;
+ }
+ if (asi < 0 || asi > 255)
+ {
+ error_message = _(": invalid ASI number");
+ goto error;
+ }
+ }
+ opcode |= ASI (asi);
+ continue;
+ } /* Alternate space. */
+
+ case 'p':
+ if (strncmp (s, "%psr", 4) == 0)
+ {
+ s += 4;
+ continue;
+ }
+ break;
+
+ case 'q': /* Floating point queue. */
+ if (strncmp (s, "%fq", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'Q': /* Coprocessor queue. */
+ if (strncmp (s, "%cq", 3) == 0)
+ {
+ s += 3;
+ continue;
+ }
+ break;
+
+ case 'S':
+ if (strcmp (str, "set") == 0
+ || strcmp (str, "setuw") == 0)
+ {
+ special_case = SPECIAL_CASE_SET;
+ continue;
+ }
+ else if (strcmp (str, "setsw") == 0)
+ {
+ special_case = SPECIAL_CASE_SETSW;
+ continue;
+ }
+ else if (strcmp (str, "setx") == 0)
+ {
+ special_case = SPECIAL_CASE_SETX;
+ continue;
+ }
+ else if (strncmp (str, "fdiv", 4) == 0)
+ {
+ special_case = SPECIAL_CASE_FDIV;
+ continue;
+ }
+ break;
+
+ case 'o':
+ if (strncmp (s, "%asi", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 's':
+ if (strncmp (s, "%fprs", 5) != 0)
+ break;
+ s += 5;
+ continue;
+
+ case '{':
+ if (strncmp (s, "%mcdper",7) != 0)
+ break;
+ s += 7;
+ continue;
+
+ case 'E':
+ if (strncmp (s, "%ccr", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 't':
+ if (strncmp (s, "%tbr", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 'w':
+ if (strncmp (s, "%wim", 4) != 0)
+ break;
+ s += 4;
+ continue;
+
+ case 'x':
+ {
+ char *push = input_line_pointer;
+ expressionS e;
+
+ input_line_pointer = s;
+ expression (&e);
+ if (e.X_op == O_constant)
+ {
+ int n = e.X_add_number;
+ if (n != e.X_add_number || (n & ~0x1ff) != 0)
+ as_bad (_("OPF immediate operand out of range (0-0x1ff)"));
+ else
+ opcode |= e.X_add_number << 5;
+ }
+ else
+ as_bad (_("non-immediate OPF operand, ignored"));
+ s = input_line_pointer;
+ input_line_pointer = push;
+ continue;
+ }
+
+ case 'y':
+ if (strncmp (s, "%y", 2) != 0)
+ break;
+ s += 2;
+ continue;
+
+ case 'u':
+ case 'U':
+ {
+ /* Parse a sparclet cpreg. */
+ int cpreg;
+ if (! parse_keyword_arg (sparc_encode_sparclet_cpreg, &s, &cpreg))
+ {
+ error_message = _(": invalid cpreg name");
+ goto error;
+ }
+ opcode |= (*args == 'U' ? RS1 (cpreg) : RD (cpreg));
+ continue;
+ }
+
+ default:
+ as_fatal (_("failed sanity check."));
+ } /* switch on arg code. */
+
+ /* Break out of for() loop. */
+ break;
+ } /* For each arg that we expect. */
+
+ error:
+ if (match == 0)
+ {
+ /* Args don't match. */
+ if (&insn[1] - sparc_opcodes < sparc_num_opcodes
+ && (insn->name == insn[1].name
+ || !strcmp (insn->name, insn[1].name)))
+ {
+ ++insn;
+ s = argsStart;
+ continue;
+ }
+ else
+ {
+ as_bad (_("Illegal operands%s"), error_message);
+ return special_case;
+ }
+ }
+ else
+ {
+ /* We have a match. Now see if the architecture is OK. */
+ int needed_arch_mask = insn->architecture;
+ bfd_uint64_t hwcaps
+ = (((bfd_uint64_t) insn->hwcaps2) << 32) | insn->hwcaps;
+
+#if defined(OBJ_ELF) && !defined(TE_SOLARIS)
+ if (hwcaps)
+ hwcap_seen |= hwcaps;
+#endif
+ if (v9_arg_p)
+ {
+ needed_arch_mask &=
+ ~(SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9) - 1);
+ if (! needed_arch_mask)
+ needed_arch_mask =
+ SPARC_OPCODE_ARCH_MASK (SPARC_OPCODE_ARCH_V9);
+ }
+
+ if (needed_arch_mask
+ & SPARC_OPCODE_SUPPORTED (current_architecture))
+ /* OK. */
+ ;
+ /* Can we bump up the architecture? */
+ else if (needed_arch_mask
+ & SPARC_OPCODE_SUPPORTED (max_architecture))
+ {
+ enum sparc_opcode_arch_val needed_architecture =
+ sparc_ffs (SPARC_OPCODE_SUPPORTED (max_architecture)
+ & needed_arch_mask);
+
+ gas_assert (needed_architecture <= SPARC_OPCODE_ARCH_MAX);
+ if (warn_on_bump
+ && needed_architecture > warn_after_architecture)
+ {
+ as_warn (_("architecture bumped from \"%s\" to \"%s\" on \"%s\""),
+ sparc_opcode_archs[current_architecture].name,
+ sparc_opcode_archs[needed_architecture].name,
+ str);
+ warn_after_architecture = needed_architecture;
+ }
+ current_architecture = needed_architecture;
+ hwcap_allowed |= hwcaps;
+ }
+ /* Conflict. */
+ /* ??? This seems to be a bit fragile. What if the next entry in
+ the opcode table is the one we want and it is supported?
+ It is possible to arrange the table today so that this can't
+ happen but what about tomorrow? */
+ else
+ {
+ int arch, printed_one_p = 0;
+ char *p;
+ char required_archs[SPARC_OPCODE_ARCH_MAX * 16];
+
+ /* Create a list of the architectures that support the insn. */
+ needed_arch_mask &= ~SPARC_OPCODE_SUPPORTED (max_architecture);
+ p = required_archs;
+ arch = sparc_ffs (needed_arch_mask);
+ while ((1 << arch) <= needed_arch_mask)
+ {
+ if ((1 << arch) & needed_arch_mask)
+ {
+ if (printed_one_p)
+ *p++ = '|';
+ strcpy (p, sparc_opcode_archs[arch].name);
+ p += strlen (p);
+ printed_one_p = 1;
+ }
+ ++arch;
+ }
+
+ as_bad (_("Architecture mismatch on \"%s\"."), str);
+ as_tsktsk (_(" (Requires %s; requested architecture is %s.)"),
+ required_archs,
+ sparc_opcode_archs[max_architecture].name);
+ return special_case;
+ }
+
+ /* Make sure the hwcaps used by the instruction are
+ currently enabled. */
+ if (hwcaps & ~hwcap_allowed)
+ {
+ const char *hwcap_name = get_hwcap_name(hwcaps & ~hwcap_allowed);
+
+ as_bad (_("Hardware capability \"%s\" not enabled for \"%s\"."),
+ hwcap_name, str);
+ return special_case;
+ }
+ } /* If no match. */
+
+ break;
+ } /* Forever looking for a match. */
+
+ the_insn.opcode = opcode;
+ return special_case;
+}
+
+/* Parse an argument that can be expressed as a keyword.
+ (eg: #StoreStore or %ccfr).
+ The result is a boolean indicating success.
+ If successful, INPUT_POINTER is updated. */
+
+static int
+parse_keyword_arg (int (*lookup_fn) (const char *),
+ char **input_pointerP,
+ int *valueP)
+{
+ int value;
+ char c, *p, *q;
+
+ p = *input_pointerP;
+ for (q = p + (*p == '#' || *p == '%');
+ ISALNUM (*q) || *q == '_';
+ ++q)
+ continue;
+ c = *q;
+ *q = 0;
+ value = (*lookup_fn) (p);
+ *q = c;
+ if (value == -1)
+ return 0;
+ *valueP = value;
+ *input_pointerP = q;
+ return 1;
+}
+
+/* Parse an argument that is a constant expression.
+ The result is a boolean indicating success. */
+
+static int
+parse_const_expr_arg (char **input_pointerP, int *valueP)
+{
+ char *save = input_line_pointer;
+ expressionS exp;
+
+ input_line_pointer = *input_pointerP;
+ /* The next expression may be something other than a constant
+ (say if we're not processing the right variant of the insn).
+ Don't call expression unless we're sure it will succeed as it will
+ signal an error (which we want to defer until later). */
+ /* FIXME: It might be better to define md_operand and have it recognize
+ things like %asi, etc. but continuing that route through to the end
+ is a lot of work. */
+ if (*input_line_pointer == '%')
+ {
+ input_line_pointer = save;
+ return 0;
+ }
+ expression (&exp);
+ *input_pointerP = input_line_pointer;
+ input_line_pointer = save;
+ if (exp.X_op != O_constant)
+ return 0;
+ *valueP = exp.X_add_number;
+ return 1;
+}
+
+/* Subroutine of sparc_ip to parse an expression. */
+
+static int
+get_expression (char *str)
+{
+ char *save_in;
+ segT seg;
+
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+ seg = expression (&the_insn.exp);
+ if (seg != absolute_section
+ && seg != text_section
+ && seg != data_section
+ && seg != bss_section
+ && seg != undefined_section)
+ {
+ the_insn.error = _("bad segment");
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
+ }
+ expr_end = input_line_pointer;
+ input_line_pointer = save_in;
+ return 0;
+}
+
+/* Subroutine of md_assemble to output one insn. */
+
+static void
+output_insn (const struct sparc_opcode *insn, struct sparc_it *theinsn)
+{
+ char *toP = frag_more (4);
+
+ /* Put out the opcode. */
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (toP, (valueT) theinsn->opcode, 4);
+ else
+ number_to_chars_littleendian (toP, (valueT) theinsn->opcode, 4);
+
+ /* Put out the symbol-dependent stuff. */
+ if (theinsn->reloc != BFD_RELOC_NONE)
+ {
+ fixS *fixP = fix_new_exp (frag_now, /* Which frag. */
+ (toP - frag_now->fr_literal), /* Where. */
+ 4, /* Size. */
+ &theinsn->exp,
+ theinsn->pcrel,
+ theinsn->reloc);
+ /* Turn off overflow checking in fixup_segment. We'll do our
+ own overflow checking in md_apply_fix. This is necessary because
+ the insn size is 4 and fixup_segment will signal an overflow for
+ large 8 byte quantities. */
+ fixP->fx_no_overflow = 1;
+ if (theinsn->reloc == BFD_RELOC_SPARC_OLO10)
+ fixP->tc_fix_data = theinsn->exp2.X_add_number;
+ }
+
+ last_insn = insn;
+ last_opcode = theinsn->opcode;
+
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (4);
+#endif
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* Write a value out to the object file, using the appropriate
+ endianness. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else if (target_little_endian_data
+ && ((n == 4 || n == 2) && ~now_seg->flags & SEC_ALLOC))
+ /* Output debug words, which are not in allocated sections, as big
+ endian. */
+ number_to_chars_bigendian (buf, val, n);
+ else if (target_little_endian_data || ! target_big_endian)
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Apply a fixS to the frags, now that we know the value it ought to
+ hold. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT segment ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ offsetT val = * (offsetT *) valP;
+ long insn;
+
+ gas_assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
+
+ fixP->fx_addnumber = val; /* Remember value for emit_reloc. */
+
+#ifdef OBJ_ELF
+ /* SPARC ELF relocations don't use an addend in the data field. */
+ if (fixP->fx_addsy != NULL)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_SPARC_TLS_GD_HI22:
+ case BFD_RELOC_SPARC_TLS_GD_LO10:
+ case BFD_RELOC_SPARC_TLS_GD_ADD:
+ case BFD_RELOC_SPARC_TLS_GD_CALL:
+ case BFD_RELOC_SPARC_TLS_LDM_HI22:
+ case BFD_RELOC_SPARC_TLS_LDM_LO10:
+ case BFD_RELOC_SPARC_TLS_LDM_ADD:
+ case BFD_RELOC_SPARC_TLS_LDM_CALL:
+ case BFD_RELOC_SPARC_TLS_LDO_HIX22:
+ case BFD_RELOC_SPARC_TLS_LDO_LOX10:
+ case BFD_RELOC_SPARC_TLS_LDO_ADD:
+ case BFD_RELOC_SPARC_TLS_IE_HI22:
+ case BFD_RELOC_SPARC_TLS_IE_LO10:
+ case BFD_RELOC_SPARC_TLS_IE_LD:
+ case BFD_RELOC_SPARC_TLS_IE_LDX:
+ case BFD_RELOC_SPARC_TLS_IE_ADD:
+ case BFD_RELOC_SPARC_TLS_LE_HIX22:
+ case BFD_RELOC_SPARC_TLS_LE_LOX10:
+ case BFD_RELOC_SPARC_TLS_DTPMOD32:
+ case BFD_RELOC_SPARC_TLS_DTPMOD64:
+ case BFD_RELOC_SPARC_TLS_DTPOFF32:
+ case BFD_RELOC_SPARC_TLS_DTPOFF64:
+ case BFD_RELOC_SPARC_TLS_TPOFF32:
+ case BFD_RELOC_SPARC_TLS_TPOFF64:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+
+ default:
+ break;
+ }
+
+ return;
+ }
+#endif
+
+ /* This is a hack. There should be a better way to
+ handle this. Probably in terms of howto fields, once
+ we can look at these fixups in terms of howtos. */
+ if (fixP->fx_r_type == BFD_RELOC_32_PCREL_S2 && fixP->fx_addsy)
+ val += fixP->fx_where + fixP->fx_frag->fr_address;
+
+#ifdef OBJ_AOUT
+ /* FIXME: More ridiculous gas reloc hacking. If we are going to
+ generate a reloc, then we just want to let the reloc addend set
+ the value. We do not want to also stuff the addend into the
+ object file. Including the addend in the object file works when
+ doing a static link, because the linker will ignore the object
+ file contents. However, the dynamic linker does not ignore the
+ object file contents. */
+ if (fixP->fx_addsy != NULL
+ && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2)
+ val = 0;
+
+ /* When generating PIC code, we do not want an addend for a reloc
+ against a local symbol. We adjust fx_addnumber to cancel out the
+ value already included in val, and to also cancel out the
+ adjustment which bfd_install_relocation will create. */
+ if (sparc_pic_code
+ && fixP->fx_r_type != BFD_RELOC_32_PCREL_S2
+ && fixP->fx_addsy != NULL
+ && ! S_IS_COMMON (fixP->fx_addsy)
+ && symbol_section_p (fixP->fx_addsy))
+ fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+
+ /* When generating PIC code, we need to fiddle to get
+ bfd_install_relocation to do the right thing for a PC relative
+ reloc against a local symbol which we are going to keep. */
+ if (sparc_pic_code
+ && fixP->fx_r_type == BFD_RELOC_32_PCREL_S2
+ && fixP->fx_addsy != NULL
+ && (S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy))
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! S_IS_COMMON (fixP->fx_addsy))
+ {
+ val = 0;
+ fixP->fx_addnumber -= 2 * S_GET_VALUE (fixP->fx_addsy);
+ }
+#endif
+
+ /* If this is a data relocation, just output VAL. */
+
+ if (fixP->fx_r_type == BFD_RELOC_8)
+ {
+ md_number_to_chars (buf, val, 1);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_SPARC_UA16)
+ {
+ md_number_to_chars (buf, val, 2);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_32
+ || fixP->fx_r_type == BFD_RELOC_SPARC_UA32
+ || fixP->fx_r_type == BFD_RELOC_SPARC_REV32)
+ {
+ md_number_to_chars (buf, val, 4);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_64
+ || fixP->fx_r_type == BFD_RELOC_SPARC_UA64)
+ {
+ md_number_to_chars (buf, val, 8);
+ }
+ else if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ fixP->fx_done = 0;
+ return;
+ }
+ else
+ {
+ /* It's a relocation against an instruction. */
+
+ if (INSN_BIG_ENDIAN)
+ insn = bfd_getb32 ((unsigned char *) buf);
+ else
+ insn = bfd_getl32 ((unsigned char *) buf);
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32_PCREL_S2:
+ val = val >> 2;
+ /* FIXME: This increment-by-one deserves a comment of why it's
+ being done! */
+ if (! sparc_pic_code
+ || fixP->fx_addsy == NULL
+ || symbol_section_p (fixP->fx_addsy))
+ ++val;
+
+ insn |= val & 0x3fffffff;
+
+ /* See if we have a delay slot. */
+ if (sparc_relax && fixP->fx_where + 8 <= fixP->fx_frag->fr_fix)
+ {
+#define G0 0
+#define O7 15
+#define XCC (2 << 20)
+#define COND(x) (((x)&0xf)<<25)
+#define CONDA COND(0x8)
+#define INSN_BPA (F2(0,1) | CONDA | BPRED | XCC)
+#define INSN_BA (F2(0,2) | CONDA)
+#define INSN_OR F3(2, 0x2, 0)
+#define INSN_NOP F2(0,4)
+
+ long delay;
+
+ /* If the instruction is a call with either:
+ restore
+ arithmetic instruction with rd == %o7
+ where rs1 != %o7 and rs2 if it is register != %o7
+ then we can optimize if the call destination is near
+ by changing the call into a branch always. */
+ if (INSN_BIG_ENDIAN)
+ delay = bfd_getb32 ((unsigned char *) buf + 4);
+ else
+ delay = bfd_getl32 ((unsigned char *) buf + 4);
+ if ((insn & OP (~0)) != OP (1) || (delay & OP (~0)) != OP (2))
+ break;
+ if ((delay & OP3 (~0)) != OP3 (0x3d) /* Restore. */
+ && ((delay & OP3 (0x28)) != 0 /* Arithmetic. */
+ || ((delay & RD (~0)) != RD (O7))))
+ break;
+ if ((delay & RS1 (~0)) == RS1 (O7)
+ || ((delay & F3I (~0)) == 0
+ && (delay & RS2 (~0)) == RS2 (O7)))
+ break;
+ /* Ensure the branch will fit into simm22. */
+ if ((val & 0x3fe00000)
+ && (val & 0x3fe00000) != 0x3fe00000)
+ break;
+ /* Check if the arch is v9 and branch will fit
+ into simm19. */
+ if (((val & 0x3c0000) == 0
+ || (val & 0x3c0000) == 0x3c0000)
+ && (sparc_arch_size == 64
+ || current_architecture >= SPARC_OPCODE_ARCH_V9))
+ /* ba,pt %xcc */
+ insn = INSN_BPA | (val & 0x7ffff);
+ else
+ /* ba */
+ insn = INSN_BA | (val & 0x3fffff);
+ if (fixP->fx_where >= 4
+ && ((delay & (0xffffffff ^ RS1 (~0)))
+ == (INSN_OR | RD (O7) | RS2 (G0))))
+ {
+ long setter;
+ int reg;
+
+ if (INSN_BIG_ENDIAN)
+ setter = bfd_getb32 ((unsigned char *) buf - 4);
+ else
+ setter = bfd_getl32 ((unsigned char *) buf - 4);
+ if ((setter & (0xffffffff ^ RD (~0)))
+ != (INSN_OR | RS1 (O7) | RS2 (G0)))
+ break;
+ /* The sequence was
+ or %o7, %g0, %rN
+ call foo
+ or %rN, %g0, %o7
+
+ If call foo was replaced with ba, replace
+ or %rN, %g0, %o7 with nop. */
+ reg = (delay & RS1 (~0)) >> 14;
+ if (reg != ((setter & RD (~0)) >> 25)
+ || reg == G0 || reg == O7)
+ break;
+
+ if (INSN_BIG_ENDIAN)
+ bfd_putb32 (INSN_NOP, (unsigned char *) buf + 4);
+ else
+ bfd_putl32 (INSN_NOP, (unsigned char *) buf + 4);
+ }
+ }
+ break;
+
+ case BFD_RELOC_SPARC_11:
+ if (! in_signed_range (val, 0x7ff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x7ff;
+ break;
+
+ case BFD_RELOC_SPARC_10:
+ if (! in_signed_range (val, 0x3ff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x3ff;
+ break;
+
+ case BFD_RELOC_SPARC_7:
+ if (! in_bitfield_range (val, 0x7f))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x7f;
+ break;
+
+ case BFD_RELOC_SPARC_6:
+ if (! in_bitfield_range (val, 0x3f))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x3f;
+ break;
+
+ case BFD_RELOC_SPARC_5:
+ if (! in_bitfield_range (val, 0x1f))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x1f;
+ break;
+
+ case BFD_RELOC_SPARC_WDISP10:
+ if ((val & 3)
+ || val >= 0x007fc
+ || val <= -(offsetT) 0x808)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ /* FIXME: The +1 deserves a comment. */
+ val = (val >> 2) + 1;
+ insn |= ((val & 0x300) << 11)
+ | ((val & 0xff) << 5);
+ break;
+
+ case BFD_RELOC_SPARC_WDISP16:
+ if ((val & 3)
+ || val >= 0x1fffc
+ || val <= -(offsetT) 0x20008)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ /* FIXME: The +1 deserves a comment. */
+ val = (val >> 2) + 1;
+ insn |= ((val & 0xc000) << 6) | (val & 0x3fff);
+ break;
+
+ case BFD_RELOC_SPARC_WDISP19:
+ if ((val & 3)
+ || val >= 0xffffc
+ || val <= -(offsetT) 0x100008)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ /* FIXME: The +1 deserves a comment. */
+ val = (val >> 2) + 1;
+ insn |= val & 0x7ffff;
+ break;
+
+ case BFD_RELOC_SPARC_HH22:
+ val = BSR (val, 32);
+ /* Fall through. */
+
+ case BFD_RELOC_SPARC_LM22:
+ case BFD_RELOC_HI22:
+ if (!fixP->fx_addsy)
+ insn |= (val >> 10) & 0x3fffff;
+ else
+ /* FIXME: Need comment explaining why we do this. */
+ insn &= ~0xffff;
+ break;
+
+ case BFD_RELOC_SPARC22:
+ if (val & ~0x003fffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= (val & 0x3fffff);
+ break;
+
+ case BFD_RELOC_SPARC_HM10:
+ val = BSR (val, 32);
+ /* Fall through. */
+
+ case BFD_RELOC_LO10:
+ if (!fixP->fx_addsy)
+ insn |= val & 0x3ff;
+ else
+ /* FIXME: Need comment explaining why we do this. */
+ insn &= ~0xff;
+ break;
+
+ case BFD_RELOC_SPARC_OLO10:
+ val &= 0x3ff;
+ val += fixP->tc_fix_data;
+ /* Fall through. */
+
+ case BFD_RELOC_SPARC13:
+ if (! in_signed_range (val, 0x1fff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relocation overflow"));
+ insn |= val & 0x1fff;
+ break;
+
+ case BFD_RELOC_SPARC_WDISP22:
+ val = (val >> 2) + 1;
+ /* Fall through. */
+ case BFD_RELOC_SPARC_BASE22:
+ insn |= val & 0x3fffff;
+ break;
+
+ case BFD_RELOC_SPARC_H34:
+ if (!fixP->fx_addsy)
+ {
+ bfd_vma tval = val;
+ tval >>= 12;
+ insn |= tval & 0x3fffff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC_H44:
+ if (!fixP->fx_addsy)
+ {
+ bfd_vma tval = val;
+ tval >>= 22;
+ insn |= tval & 0x3fffff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC_M44:
+ if (!fixP->fx_addsy)
+ insn |= (val >> 12) & 0x3ff;
+ break;
+
+ case BFD_RELOC_SPARC_L44:
+ if (!fixP->fx_addsy)
+ insn |= val & 0xfff;
+ break;
+
+ case BFD_RELOC_SPARC_HIX22:
+ if (!fixP->fx_addsy)
+ {
+ val ^= ~(offsetT) 0;
+ insn |= (val >> 10) & 0x3fffff;
+ }
+ break;
+
+ case BFD_RELOC_SPARC_LOX10:
+ if (!fixP->fx_addsy)
+ insn |= 0x1c00 | (val & 0x3ff);
+ break;
+
+ case BFD_RELOC_NONE:
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad or unhandled relocation type: 0x%02x"),
+ fixP->fx_r_type);
+ break;
+ }
+
+ if (INSN_BIG_ENDIAN)
+ bfd_putb32 (insn, (unsigned char *) buf);
+ else
+ bfd_putl32 (insn, (unsigned char *) buf);
+ }
+
+ /* Are we finished with this relocation now? */
+ if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
+ fixP->fx_done = 1;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent **
+tc_gen_reloc (asection *section, fixS *fixp)
+{
+ static arelent *relocs[3];
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ relocs[0] = reloc = (arelent *) xmalloc (sizeof (arelent));
+ relocs[1] = NULL;
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_16:
+ case BFD_RELOC_32:
+ case BFD_RELOC_HI22:
+ case BFD_RELOC_LO10:
+ case BFD_RELOC_32_PCREL_S2:
+ case BFD_RELOC_SPARC13:
+ case BFD_RELOC_SPARC22:
+ case BFD_RELOC_SPARC_PC22:
+ case BFD_RELOC_SPARC_PC10:
+ case BFD_RELOC_SPARC_BASE13:
+ case BFD_RELOC_SPARC_WDISP10:
+ case BFD_RELOC_SPARC_WDISP16:
+ case BFD_RELOC_SPARC_WDISP19:
+ case BFD_RELOC_SPARC_WDISP22:
+ case BFD_RELOC_64:
+ case BFD_RELOC_SPARC_5:
+ case BFD_RELOC_SPARC_6:
+ case BFD_RELOC_SPARC_7:
+ case BFD_RELOC_SPARC_10:
+ case BFD_RELOC_SPARC_11:
+ case BFD_RELOC_SPARC_HH22:
+ case BFD_RELOC_SPARC_HM10:
+ case BFD_RELOC_SPARC_LM22:
+ case BFD_RELOC_SPARC_PC_HH22:
+ case BFD_RELOC_SPARC_PC_HM10:
+ case BFD_RELOC_SPARC_PC_LM22:
+ case BFD_RELOC_SPARC_H34:
+ case BFD_RELOC_SPARC_H44:
+ case BFD_RELOC_SPARC_M44:
+ case BFD_RELOC_SPARC_L44:
+ case BFD_RELOC_SPARC_HIX22:
+ case BFD_RELOC_SPARC_LOX10:
+ case BFD_RELOC_SPARC_REV32:
+ case BFD_RELOC_SPARC_OLO10:
+ case BFD_RELOC_SPARC_UA16:
+ case BFD_RELOC_SPARC_UA32:
+ case BFD_RELOC_SPARC_UA64:
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_64_PCREL:
+ case BFD_RELOC_SPARC_PLT32:
+ case BFD_RELOC_SPARC_PLT64:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_SPARC_TLS_GD_HI22:
+ case BFD_RELOC_SPARC_TLS_GD_LO10:
+ case BFD_RELOC_SPARC_TLS_GD_ADD:
+ case BFD_RELOC_SPARC_TLS_GD_CALL:
+ case BFD_RELOC_SPARC_TLS_LDM_HI22:
+ case BFD_RELOC_SPARC_TLS_LDM_LO10:
+ case BFD_RELOC_SPARC_TLS_LDM_ADD:
+ case BFD_RELOC_SPARC_TLS_LDM_CALL:
+ case BFD_RELOC_SPARC_TLS_LDO_HIX22:
+ case BFD_RELOC_SPARC_TLS_LDO_LOX10:
+ case BFD_RELOC_SPARC_TLS_LDO_ADD:
+ case BFD_RELOC_SPARC_TLS_IE_HI22:
+ case BFD_RELOC_SPARC_TLS_IE_LO10:
+ case BFD_RELOC_SPARC_TLS_IE_LD:
+ case BFD_RELOC_SPARC_TLS_IE_LDX:
+ case BFD_RELOC_SPARC_TLS_IE_ADD:
+ case BFD_RELOC_SPARC_TLS_LE_HIX22:
+ case BFD_RELOC_SPARC_TLS_LE_LOX10:
+ case BFD_RELOC_SPARC_TLS_DTPOFF32:
+ case BFD_RELOC_SPARC_TLS_DTPOFF64:
+ case BFD_RELOC_SPARC_GOTDATA_OP_HIX22:
+ case BFD_RELOC_SPARC_GOTDATA_OP_LOX10:
+ case BFD_RELOC_SPARC_GOTDATA_OP:
+ code = fixp->fx_r_type;
+ break;
+ default:
+ abort ();
+ return NULL;
+ }
+
+#if defined (OBJ_ELF) || defined (OBJ_AOUT)
+ /* If we are generating PIC code, we need to generate a different
+ set of relocs. */
+
+#ifdef OBJ_ELF
+#define GOT_NAME "_GLOBAL_OFFSET_TABLE_"
+#else
+#define GOT_NAME "__GLOBAL_OFFSET_TABLE_"
+#endif
+#ifdef TE_VXWORKS
+#define GOTT_BASE "__GOTT_BASE__"
+#define GOTT_INDEX "__GOTT_INDEX__"
+#endif
+
+ /* This code must be parallel to the OBJ_ELF tc_fix_adjustable. */
+
+ if (sparc_pic_code)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_32_PCREL_S2:
+ if (generic_force_reloc (fixp))
+ code = BFD_RELOC_SPARC_WPLT30;
+ break;
+ case BFD_RELOC_HI22:
+ code = BFD_RELOC_SPARC_GOT22;
+ if (fixp->fx_addsy != NULL)
+ {
+ if (strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_SPARC_PC22;
+#ifdef TE_VXWORKS
+ if (strcmp (S_GET_NAME (fixp->fx_addsy), GOTT_BASE) == 0
+ || strcmp (S_GET_NAME (fixp->fx_addsy), GOTT_INDEX) == 0)
+ code = BFD_RELOC_HI22; /* Unchanged. */
+#endif
+ }
+ break;
+ case BFD_RELOC_LO10:
+ code = BFD_RELOC_SPARC_GOT10;
+ if (fixp->fx_addsy != NULL)
+ {
+ if (strcmp (S_GET_NAME (fixp->fx_addsy), GOT_NAME) == 0)
+ code = BFD_RELOC_SPARC_PC10;
+#ifdef TE_VXWORKS
+ if (strcmp (S_GET_NAME (fixp->fx_addsy), GOTT_BASE) == 0
+ || strcmp (S_GET_NAME (fixp->fx_addsy), GOTT_INDEX) == 0)
+ code = BFD_RELOC_LO10; /* Unchanged. */
+#endif
+ }
+ break;
+ case BFD_RELOC_SPARC13:
+ code = BFD_RELOC_SPARC_GOT13;
+ break;
+ default:
+ break;
+ }
+ }
+#endif /* defined (OBJ_ELF) || defined (OBJ_AOUT) */
+
+ /* Nothing is aligned in DWARF debugging sections. */
+ if (bfd_get_section_flags (stdoutput, section) & SEC_DEBUGGING)
+ switch (code)
+ {
+ case BFD_RELOC_16: code = BFD_RELOC_SPARC_UA16; break;
+ case BFD_RELOC_32: code = BFD_RELOC_SPARC_UA32; break;
+ case BFD_RELOC_64: code = BFD_RELOC_SPARC_UA64; break;
+ default: break;
+ }
+
+ if (code == BFD_RELOC_SPARC_OLO10)
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_LO10);
+ else
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (reloc->howto == 0)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("internal error: can't export reloc type %d (`%s')"),
+ fixp->fx_r_type, bfd_get_reloc_code_name (code));
+ xfree (reloc);
+ relocs[0] = NULL;
+ return relocs;
+ }
+
+ /* @@ Why fx_addnumber sometimes and fx_offset other times? */
+#ifdef OBJ_AOUT
+
+ if (reloc->howto->pc_relative == 0
+ || code == BFD_RELOC_SPARC_PC10
+ || code == BFD_RELOC_SPARC_PC22)
+ reloc->addend = fixp->fx_addnumber;
+ else if (sparc_pic_code
+ && fixp->fx_r_type == BFD_RELOC_32_PCREL_S2
+ && fixp->fx_addsy != NULL
+ && (S_IS_EXTERNAL (fixp->fx_addsy)
+ || S_IS_WEAK (fixp->fx_addsy))
+ && S_IS_DEFINED (fixp->fx_addsy)
+ && ! S_IS_COMMON (fixp->fx_addsy))
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = fixp->fx_offset - reloc->address;
+
+#else /* elf or coff */
+
+ if (code != BFD_RELOC_32_PCREL_S2
+ && code != BFD_RELOC_SPARC_WDISP22
+ && code != BFD_RELOC_SPARC_WDISP16
+ && code != BFD_RELOC_SPARC_WDISP19
+ && code != BFD_RELOC_SPARC_WDISP10
+ && code != BFD_RELOC_SPARC_WPLT30
+ && code != BFD_RELOC_SPARC_TLS_GD_CALL
+ && code != BFD_RELOC_SPARC_TLS_LDM_CALL)
+ reloc->addend = fixp->fx_addnumber;
+ else if (symbol_section_p (fixp->fx_addsy))
+ reloc->addend = (section->vma
+ + fixp->fx_addnumber
+ + md_pcrel_from (fixp));
+ else
+ reloc->addend = fixp->fx_offset;
+#endif
+
+ /* We expand R_SPARC_OLO10 to R_SPARC_LO10 and R_SPARC_13
+ on the same location. */
+ if (code == BFD_RELOC_SPARC_OLO10)
+ {
+ relocs[1] = reloc = (arelent *) xmalloc (sizeof (arelent));
+ relocs[2] = NULL;
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr
+ = symbol_get_bfdsym (section_symbol (absolute_section));
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_SPARC13);
+ reloc->addend = fixp->tc_fix_data;
+ }
+
+ return relocs;
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+#ifndef OBJ_ELF
+ /* This is not right for ELF; a.out wants it, and COFF will force
+ the alignment anyways. */
+ valueT align = ((valueT) 1
+ << (valueT) bfd_get_section_alignment (stdoutput, segment));
+ valueT newsize;
+
+ /* Turn alignment value into a mask. */
+ align--;
+ newsize = (size + align) & ~align;
+ return newsize;
+#else
+ return size;
+#endif
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the sparc, they're relative to the address of the offset, plus
+ its size. This gets us to the following instruction.
+ (??? Is this right? FIXME-SOON) */
+long
+md_pcrel_from (fixS *fixP)
+{
+ long ret;
+
+ ret = fixP->fx_where + fixP->fx_frag->fr_address;
+ if (! sparc_pic_code
+ || fixP->fx_addsy == NULL
+ || symbol_section_p (fixP->fx_addsy))
+ ret += fixP->fx_size;
+ return ret;
+}
+
+/* Return log2 (VALUE), or -1 if VALUE is not an exact positive power
+ of two. */
+
+static int
+mylog2 (int value)
+{
+ int shift;
+
+ if (value <= 0)
+ return -1;
+
+ for (shift = 0; (value & 1) == 0; value >>= 1)
+ ++shift;
+
+ return (value == 1) ? shift : -1;
+}
+
+/* Sort of like s_lcomm. */
+
+#ifndef OBJ_ELF
+static int max_alignment = 15;
+#endif
+
+static void
+s_reserve (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char *p;
+ char c;
+ int align;
+ int size;
+ int temp;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ as_bad (_("BSS length (%d.) <0! Ignored."), size);
+ ignore_rest_of_line ();
+ return;
+ } /* Bad length. */
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (strncmp (input_line_pointer, ",\"bss\"", 6) != 0
+ && strncmp (input_line_pointer, ",\".bss\"", 7) != 0)
+ {
+ as_bad (_("bad .reserve segment -- expected BSS segment"));
+ return;
+ }
+
+ if (input_line_pointer[2] == '.')
+ input_line_pointer += 7;
+ else
+ input_line_pointer += 6;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ {
+ as_bad (_("missing alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ align = (int) get_absolute_expression ();
+
+#ifndef OBJ_ELF
+ if (align > max_alignment)
+ {
+ align = max_alignment;
+ as_warn (_("alignment too large; assuming %d"), align);
+ }
+#endif
+
+ if (align < 0)
+ {
+ as_bad (_("negative alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (align != 0)
+ {
+ temp = mylog2 (align);
+ if (temp < 0)
+ {
+ as_bad (_("alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ align = temp;
+ }
+
+ record_alignment (bss_section, align);
+ }
+ else
+ align = 0;
+
+ if (!S_IS_DEFINED (symbolP)
+#ifdef OBJ_AOUT
+ && S_GET_OTHER (symbolP) == 0
+ && S_GET_DESC (symbolP) == 0
+#endif
+ )
+ {
+ if (! need_pass_2)
+ {
+ char *pfrag;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ /* Switch to bss. */
+ subseg_set (bss_section, 1);
+
+ if (align)
+ /* Do alignment. */
+ frag_align (align, 0, 0);
+
+ /* Detach from old frag. */
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = NULL;
+
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *pfrag = 0;
+
+ S_SET_SEGMENT (symbolP, bss_section);
+
+ subseg_set (current_seg, current_subseg);
+
+#ifdef OBJ_ELF
+ S_SET_SIZE (symbolP, size);
+#endif
+ }
+ }
+ else
+ {
+ as_warn (_("Ignoring attempt to re-define symbol %s"),
+ S_GET_NAME (symbolP));
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_common (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT temp, size;
+ symbolS *symbolP;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Skip ','. */
+ input_line_pointer++;
+
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ as_bad (_(".COMMon length (%lu) out of range ignored"),
+ (unsigned long) temp);
+ ignore_rest_of_line ();
+ return;
+ }
+ size = temp;
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (S_GET_VALUE (symbolP) != 0)
+ {
+ if (S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), (long) size);
+ }
+ }
+ else
+ {
+#ifndef OBJ_ELF
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_EXTERNAL (symbolP);
+#endif
+ }
+ know (symbol_get_frag (symbolP) == &zero_address_frag);
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after common length"));
+ ignore_rest_of_line ();
+ return;
+ }
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '"')
+ {
+ temp = get_absolute_expression ();
+
+#ifndef OBJ_ELF
+ if (temp > max_alignment)
+ {
+ temp = max_alignment;
+ as_warn (_("alignment too large; assuming %ld"), (long) temp);
+ }
+#endif
+
+ if (temp < 0)
+ {
+ as_bad (_("negative alignment"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+#ifdef OBJ_ELF
+ if (symbol_get_obj (symbolP)->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ int align;
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ if (temp == 0)
+ align = 0;
+ else
+ align = mylog2 (temp);
+
+ if (align < 0)
+ {
+ as_bad (_("alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ record_alignment (bss_section, align);
+ subseg_set (bss_section, 0);
+ if (align)
+ frag_align (align, 0, 0);
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ symbol_set_frag (symbolP, frag_now);
+ p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *p = 0;
+ S_SET_SEGMENT (symbolP, bss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ S_SET_SIZE (symbolP, size);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+#endif /* OBJ_ELF */
+ {
+ allocate_common:
+ S_SET_VALUE (symbolP, (valueT) size);
+#ifdef OBJ_ELF
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_SIZE (symbolP, size);
+#endif
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+ }
+ }
+ else
+ {
+ input_line_pointer++;
+ /* @@ Some use the dot, some don't. Can we get some consistency?? */
+ if (*input_line_pointer == '.')
+ input_line_pointer++;
+ /* @@ Some say data, some say bss. */
+ if (strncmp (input_line_pointer, "bss\"", 4)
+ && strncmp (input_line_pointer, "data\"", 5))
+ {
+ while (*--input_line_pointer != '"')
+ ;
+ input_line_pointer--;
+ goto bad_common_segment;
+ }
+ while (*input_line_pointer++ != '"')
+ ;
+ goto allocate_common;
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+
+ demand_empty_rest_of_line ();
+ return;
+
+ {
+ bad_common_segment:
+ p = input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c = *p;
+ *p = '\0';
+ as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+ *p = c;
+ input_line_pointer = p;
+ ignore_rest_of_line ();
+ return;
+ }
+}
+
+/* Handle the .empty pseudo-op. This suppresses the warnings about
+ invalid delay slot usage. */
+
+static void
+s_empty (int ignore ATTRIBUTE_UNUSED)
+{
+ /* The easy way to implement is to just forget about the last
+ instruction. */
+ last_insn = NULL;
+}
+
+static void
+s_seg (int ignore ATTRIBUTE_UNUSED)
+{
+
+ if (strncmp (input_line_pointer, "\"text\"", 6) == 0)
+ {
+ input_line_pointer += 6;
+ s_text (0);
+ return;
+ }
+ if (strncmp (input_line_pointer, "\"data\"", 6) == 0)
+ {
+ input_line_pointer += 6;
+ s_data (0);
+ return;
+ }
+ if (strncmp (input_line_pointer, "\"data1\"", 7) == 0)
+ {
+ input_line_pointer += 7;
+ s_data1 ();
+ return;
+ }
+ if (strncmp (input_line_pointer, "\"bss\"", 5) == 0)
+ {
+ input_line_pointer += 5;
+ /* We only support 2 segments -- text and data -- for now, so
+ things in the "bss segment" will have to go into data for now.
+ You can still allocate SEG_BSS stuff with .lcomm or .reserve. */
+ subseg_set (data_section, 255); /* FIXME-SOMEDAY. */
+ return;
+ }
+ as_bad (_("Unknown segment type"));
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_data1 (void)
+{
+ subseg_set (data_section, 1);
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_proc (int ignore ATTRIBUTE_UNUSED)
+{
+ while (!is_end_of_line[(unsigned char) *input_line_pointer])
+ {
+ ++input_line_pointer;
+ }
+ ++input_line_pointer;
+}
+
+/* This static variable is set by s_uacons to tell sparc_cons_align
+ that the expression does not need to be aligned. */
+
+static int sparc_no_align_cons = 0;
+
+/* This handles the unaligned space allocation pseudo-ops, such as
+ .uaword. .uaword is just like .word, but the value does not need
+ to be aligned. */
+
+static void
+s_uacons (int bytes)
+{
+ /* Tell sparc_cons_align not to align this value. */
+ sparc_no_align_cons = 1;
+ cons (bytes);
+ sparc_no_align_cons = 0;
+}
+
+/* This handles the native word allocation pseudo-op .nword.
+ For sparc_arch_size 32 it is equivalent to .word, for
+ sparc_arch_size 64 it is equivalent to .xword. */
+
+static void
+s_ncons (int bytes ATTRIBUTE_UNUSED)
+{
+ cons (sparc_arch_size == 32 ? 4 : 8);
+}
+
+#ifdef OBJ_ELF
+/* Handle the SPARC ELF .register pseudo-op. This sets the binding of a
+ global register.
+ The syntax is:
+
+ .register %g[2367],{#scratch|symbolname|#ignore}
+*/
+
+static void
+s_register (int ignore ATTRIBUTE_UNUSED)
+{
+ char c;
+ int reg;
+ int flags;
+ const char *regname;
+
+ if (input_line_pointer[0] != '%'
+ || input_line_pointer[1] != 'g'
+ || ((input_line_pointer[2] & ~1) != '2'
+ && (input_line_pointer[2] & ~1) != '6')
+ || input_line_pointer[3] != ',')
+ as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
+ reg = input_line_pointer[2] - '0';
+ input_line_pointer += 4;
+
+ if (*input_line_pointer == '#')
+ {
+ ++input_line_pointer;
+ regname = input_line_pointer;
+ c = get_symbol_end ();
+ if (strcmp (regname, "scratch") && strcmp (regname, "ignore"))
+ as_bad (_("register syntax is .register %%g[2367],{#scratch|symbolname|#ignore}"));
+ if (regname[0] == 'i')
+ regname = NULL;
+ else
+ regname = "";
+ }
+ else
+ {
+ regname = input_line_pointer;
+ c = get_symbol_end ();
+ }
+ if (sparc_arch_size == 64)
+ {
+ if (globals[reg])
+ {
+ if ((regname && globals[reg] != (symbolS *) 1
+ && strcmp (S_GET_NAME (globals[reg]), regname))
+ || ((regname != NULL) ^ (globals[reg] != (symbolS *) 1)))
+ as_bad (_("redefinition of global register"));
+ }
+ else
+ {
+ if (regname == NULL)
+ globals[reg] = (symbolS *) 1;
+ else
+ {
+ if (*regname)
+ {
+ if (symbol_find (regname))
+ as_bad (_("Register symbol %s already defined."),
+ regname);
+ }
+ globals[reg] = symbol_make (regname);
+ flags = symbol_get_bfdsym (globals[reg])->flags;
+ if (! *regname)
+ flags = flags & ~(BSF_GLOBAL|BSF_LOCAL|BSF_WEAK);
+ if (! (flags & (BSF_GLOBAL|BSF_LOCAL|BSF_WEAK)))
+ flags |= BSF_GLOBAL;
+ symbol_get_bfdsym (globals[reg])->flags = flags;
+ S_SET_VALUE (globals[reg], (valueT) reg);
+ S_SET_ALIGN (globals[reg], reg);
+ S_SET_SIZE (globals[reg], 0);
+ /* Although we actually want undefined_section here,
+ we have to use absolute_section, because otherwise
+ generic as code will make it a COM section.
+ We fix this up in sparc_adjust_symtab. */
+ S_SET_SEGMENT (globals[reg], absolute_section);
+ S_SET_OTHER (globals[reg], 0);
+ elf_symbol (symbol_get_bfdsym (globals[reg]))
+ ->internal_elf_sym.st_info =
+ ELF_ST_INFO(STB_GLOBAL, STT_REGISTER);
+ elf_symbol (symbol_get_bfdsym (globals[reg]))
+ ->internal_elf_sym.st_shndx = SHN_UNDEF;
+ }
+ }
+ }
+
+ *input_line_pointer = c;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Adjust the symbol table. We set undefined sections for STT_REGISTER
+ symbols which need it. */
+
+void
+sparc_adjust_symtab (void)
+{
+ symbolS *sym;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ELF_ST_TYPE (elf_symbol (symbol_get_bfdsym (sym))
+ ->internal_elf_sym.st_info) != STT_REGISTER)
+ continue;
+
+ if (ELF_ST_TYPE (elf_symbol (symbol_get_bfdsym (sym))
+ ->internal_elf_sym.st_shndx != SHN_UNDEF))
+ continue;
+
+ S_SET_SEGMENT (sym, undefined_section);
+ }
+}
+#endif
+
+/* If the --enforce-aligned-data option is used, we require .word,
+ et. al., to be aligned correctly. We do it by setting up an
+ rs_align_code frag, and checking in HANDLE_ALIGN to make sure that
+ no unexpected alignment was introduced.
+
+ The SunOS and Solaris native assemblers enforce aligned data by
+ default. We don't want to do that, because gcc can deliberately
+ generate misaligned data if the packed attribute is used. Instead,
+ we permit misaligned data by default, and permit the user to set an
+ option to check for it. */
+
+void
+sparc_cons_align (int nbytes)
+{
+ int nalign;
+
+ /* Only do this if we are enforcing aligned data. */
+ if (! enforce_aligned_data)
+ return;
+
+ /* Don't align if this is an unaligned pseudo-op. */
+ if (sparc_no_align_cons)
+ return;
+
+ nalign = mylog2 (nbytes);
+ if (nalign == 0)
+ return;
+
+ gas_assert (nalign > 0);
+
+ if (now_seg == absolute_section)
+ {
+ if ((abs_section_offset & ((1 << nalign) - 1)) != 0)
+ as_bad (_("misaligned data"));
+ return;
+ }
+
+ frag_var (rs_align_test, 1, 1, (relax_substateT) 0,
+ (symbolS *) NULL, (offsetT) nalign, (char *) NULL);
+
+ record_alignment (now_seg, nalign);
+}
+
+/* This is called from HANDLE_ALIGN in tc-sparc.h. */
+
+void
+sparc_handle_align (fragS *fragp)
+{
+ int count, fix;
+ char *p;
+
+ count = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+
+ switch (fragp->fr_type)
+ {
+ case rs_align_test:
+ if (count != 0)
+ as_bad_where (fragp->fr_file, fragp->fr_line, _("misaligned data"));
+ break;
+
+ case rs_align_code:
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ if (count & 3)
+ {
+ fix = count & 3;
+ memset (p, 0, fix);
+ p += fix;
+ count -= fix;
+ }
+
+ if (SPARC_OPCODE_ARCH_V9_P (max_architecture) && count > 8)
+ {
+ unsigned wval = (0x30680000 | count >> 2); /* ba,a,pt %xcc, 1f */
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (p, wval, 4);
+ else
+ number_to_chars_littleendian (p, wval, 4);
+ p += 4;
+ count -= 4;
+ fix += 4;
+ }
+
+ if (INSN_BIG_ENDIAN)
+ number_to_chars_bigendian (p, 0x01000000, 4);
+ else
+ number_to_chars_littleendian (p, 0x01000000, 4);
+
+ fragp->fr_fix += fix;
+ fragp->fr_var = 4;
+ break;
+
+ default:
+ break;
+ }
+}
+
+#ifdef OBJ_ELF
+/* Some special processing for a Sparc ELF file. */
+
+void
+sparc_elf_final_processing (void)
+{
+ /* Set the Sparc ELF flag bits. FIXME: There should probably be some
+ sort of BFD interface for this. */
+ if (sparc_arch_size == 64)
+ {
+ switch (sparc_memory_model)
+ {
+ case MM_RMO:
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_RMO;
+ break;
+ case MM_PSO:
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARCV9_PSO;
+ break;
+ default:
+ break;
+ }
+ }
+ else if (current_architecture >= SPARC_OPCODE_ARCH_V9)
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARC_32PLUS;
+ if (current_architecture == SPARC_OPCODE_ARCH_V9A)
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1;
+ else if (current_architecture == SPARC_OPCODE_ARCH_V9B)
+ elf_elfheader (stdoutput)->e_flags |= EF_SPARC_SUN_US1|EF_SPARC_SUN_US3;
+}
+
+const char *
+sparc_cons (expressionS *exp, int size)
+{
+ char *save;
+ const char *sparc_cons_special_reloc = NULL;
+
+ SKIP_WHITESPACE ();
+ save = input_line_pointer;
+ if (input_line_pointer[0] == '%'
+ && input_line_pointer[1] == 'r'
+ && input_line_pointer[2] == '_')
+ {
+ if (strncmp (input_line_pointer + 3, "disp", 4) == 0)
+ {
+ input_line_pointer += 7;
+ sparc_cons_special_reloc = "disp";
+ }
+ else if (strncmp (input_line_pointer + 3, "plt", 3) == 0)
+ {
+ if (size != 4 && size != 8)
+ as_bad (_("Illegal operands: %%r_plt in %d-byte data field"), size);
+ else
+ {
+ input_line_pointer += 6;
+ sparc_cons_special_reloc = "plt";
+ }
+ }
+ else if (strncmp (input_line_pointer + 3, "tls_dtpoff", 10) == 0)
+ {
+ if (size != 4 && size != 8)
+ as_bad (_("Illegal operands: %%r_tls_dtpoff in %d-byte data field"), size);
+ else
+ {
+ input_line_pointer += 13;
+ sparc_cons_special_reloc = "tls_dtpoff";
+ }
+ }
+ if (sparc_cons_special_reloc)
+ {
+ int bad = 0;
+
+ switch (size)
+ {
+ case 1:
+ if (*input_line_pointer != '8')
+ bad = 1;
+ input_line_pointer--;
+ break;
+ case 2:
+ if (input_line_pointer[0] != '1' || input_line_pointer[1] != '6')
+ bad = 1;
+ break;
+ case 4:
+ if (input_line_pointer[0] != '3' || input_line_pointer[1] != '2')
+ bad = 1;
+ break;
+ case 8:
+ if (input_line_pointer[0] != '6' || input_line_pointer[1] != '4')
+ bad = 1;
+ break;
+ default:
+ bad = 1;
+ break;
+ }
+
+ if (bad)
+ {
+ as_bad (_("Illegal operands: Only %%r_%s%d allowed in %d-byte data fields"),
+ sparc_cons_special_reloc, size * 8, size);
+ }
+ else
+ {
+ input_line_pointer += 2;
+ if (*input_line_pointer != '(')
+ {
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ sparc_cons_special_reloc, size * 8);
+ bad = 1;
+ }
+ }
+
+ if (bad)
+ {
+ input_line_pointer = save;
+ sparc_cons_special_reloc = NULL;
+ }
+ else
+ {
+ int c;
+ char *end = ++input_line_pointer;
+ int npar = 0;
+
+ while (! is_end_of_line[(c = *end)])
+ {
+ if (c == '(')
+ npar++;
+ else if (c == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+ end++;
+ }
+
+ if (c != ')')
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ sparc_cons_special_reloc, size * 8);
+ else
+ {
+ *end = '\0';
+ expression (exp);
+ *end = c;
+ if (input_line_pointer != end)
+ {
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ sparc_cons_special_reloc, size * 8);
+ }
+ else
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (! is_end_of_line[c] && c != ',')
+ as_bad (_("Illegal operands: garbage after %%r_%s%d()"),
+ sparc_cons_special_reloc, size * 8);
+ }
+ }
+ }
+ }
+ }
+ if (sparc_cons_special_reloc == NULL)
+ expression (exp);
+ return sparc_cons_special_reloc;
+}
+
+#endif
+
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+ reloc for a cons. We could use the definition there, except that
+ we want to handle little endian relocs specially. */
+
+void
+cons_fix_new_sparc (fragS *frag,
+ int where,
+ unsigned int nbytes,
+ expressionS *exp,
+ const char *sparc_cons_special_reloc)
+{
+ bfd_reloc_code_real_type r;
+
+ r = (nbytes == 1 ? BFD_RELOC_8 :
+ (nbytes == 2 ? BFD_RELOC_16 :
+ (nbytes == 4 ? BFD_RELOC_32 : BFD_RELOC_64)));
+
+ if (target_little_endian_data
+ && nbytes == 4
+ && now_seg->flags & SEC_ALLOC)
+ r = BFD_RELOC_SPARC_REV32;
+
+ if (sparc_cons_special_reloc)
+ {
+ if (*sparc_cons_special_reloc == 'd')
+ switch (nbytes)
+ {
+ case 1: r = BFD_RELOC_8_PCREL; break;
+ case 2: r = BFD_RELOC_16_PCREL; break;
+ case 4: r = BFD_RELOC_32_PCREL; break;
+ case 8: r = BFD_RELOC_64_PCREL; break;
+ default: abort ();
+ }
+ else if (*sparc_cons_special_reloc == 'p')
+ switch (nbytes)
+ {
+ case 4: r = BFD_RELOC_SPARC_PLT32; break;
+ case 8: r = BFD_RELOC_SPARC_PLT64; break;
+ }
+ else
+ switch (nbytes)
+ {
+ case 4: r = BFD_RELOC_SPARC_TLS_DTPOFF32; break;
+ case 8: r = BFD_RELOC_SPARC_TLS_DTPOFF64; break;
+ }
+ }
+ else if (sparc_no_align_cons)
+ {
+ switch (nbytes)
+ {
+ case 2: r = BFD_RELOC_SPARC_UA16; break;
+ case 4: r = BFD_RELOC_SPARC_UA32; break;
+ case 8: r = BFD_RELOC_SPARC_UA64; break;
+ default: abort ();
+ }
+ }
+
+ fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+}
+
+void
+sparc_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (14, sparc_arch_size == 64 ? 0x7ff : 0);
+}
+
+int
+sparc_regname_to_dw2regnum (char *regname)
+{
+ char *p, *q;
+
+ if (!regname[0])
+ return -1;
+
+ q = "goli";
+ p = strchr (q, regname[0]);
+ if (p)
+ {
+ if (regname[1] < '0' || regname[1] > '8' || regname[2])
+ return -1;
+ return (p - q) * 8 + regname[1] - '0';
+ }
+ if (regname[0] == 's' && regname[1] == 'p' && !regname[2])
+ return 14;
+ if (regname[0] == 'f' && regname[1] == 'p' && !regname[2])
+ return 30;
+ if (regname[0] == 'f' || regname[0] == 'r')
+ {
+ unsigned int regnum;
+
+ regnum = strtoul (regname + 1, &q, 10);
+ if (p == q || *q)
+ return -1;
+ if (regnum >= ((regname[0] == 'f'
+ && SPARC_OPCODE_ARCH_V9_P (max_architecture))
+ ? 64 : 32))
+ return -1;
+ if (regname[0] == 'f')
+ {
+ regnum += 32;
+ if (regnum >= 64 && (regnum & 1))
+ return -1;
+ }
+ return regnum;
+ }
+ return -1;
+}
+
+void
+sparc_cfi_emit_pcrel_expr (expressionS *exp, unsigned int nbytes)
+{
+ sparc_no_align_cons = 1;
+ emit_expr_with_reloc (exp, nbytes, "disp");
+ sparc_no_align_cons = 0;
+}
diff --git a/gas/config/tc-sparc.h b/gas/config/tc-sparc.h
new file mode 100644
index 0000000..ef76c0b
--- /dev/null
+++ b/gas/config/tc-sparc.h
@@ -0,0 +1,204 @@
+/* tc-sparc.h - Macros and type defines for the sparc.
+ Copyright (C) 1989-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public
+ License along with GAS; see the file COPYING. If not, write
+ to the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#ifndef TC_SPARC
+#define TC_SPARC 1
+
+struct frag;
+
+/* This is used to set the default value for `target_big_endian'. */
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+#define LOCAL_LABELS_FB 1
+
+#define TARGET_ARCH bfd_arch_sparc
+
+#ifdef TE_FreeBSD
+#define ELF_TARGET_FORMAT "elf32-sparc"
+#define ELF64_TARGET_FORMAT "elf64-sparc-freebsd"
+#endif
+
+#ifdef TE_SOLARIS
+#define ELF_TARGET_FORMAT "elf32-sparc-sol2"
+#define ELF64_TARGET_FORMAT "elf64-sparc-sol2"
+#endif
+
+#ifndef ELF_TARGET_FORMAT
+#define ELF_TARGET_FORMAT "elf32-sparc"
+#endif
+
+#ifndef ELF64_TARGET_FORMAT
+#define ELF64_TARGET_FORMAT "elf64-sparc"
+#endif
+
+extern const char *sparc_target_format (void);
+#define TARGET_FORMAT sparc_target_format ()
+
+#define RELOC_EXPANSION_POSSIBLE
+#define MAX_RELOC_EXPANSION 2
+
+/* Make it unconditional and check if -EL is valid after option parsing */
+#define SPARC_BIENDIAN
+
+#define WORKING_DOT_WORD
+
+#define md_convert_frag(b,s,f) \
+ as_fatal (_("sparc convert_frag\n"))
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal (_("estimate_size_before_relax called")), 1)
+
+#define LISTING_HEADER "SPARC GAS "
+
+extern int sparc_pic_code;
+
+/* We require .word, et. al., to be aligned correctly. */
+#define md_cons_align(nbytes) sparc_cons_align (nbytes)
+extern void sparc_cons_align (int);
+
+#define HANDLE_ALIGN(fragp) sparc_handle_align (fragp)
+extern void sparc_handle_align (struct frag *);
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4 + 4)
+
+/* I know that "call 0" fails in sparc-coff if this doesn't return 1. I
+ don't know about other relocation types, or other formats, yet. */
+#ifdef OBJ_COFF
+#define TC_FORCE_RELOCATION_ABS(FIX) \
+ ((FIX)->fx_r_type == BFD_RELOC_32_PCREL_S2 \
+ || TC_FORCE_RELOCATION (FIX))
+
+#define RELOC_REQUIRES_SYMBOL
+#endif
+
+#ifdef OBJ_AOUT
+/* This expression evaluates to true if the relocation is for a local
+ object for which we still want to do the relocation at runtime.
+ False if we are willing to perform this relocation while building
+ the .o file. */
+
+#define TC_FORCE_RELOCATION_LOCAL(FIX) \
+ (!(FIX)->fx_pcrel \
+ || (sparc_pic_code \
+ && S_IS_EXTERNAL ((FIX)->fx_addsy)) \
+ || TC_FORCE_RELOCATION (FIX))
+#endif
+
+#ifdef OBJ_ELF
+/* Don't turn certain relocs into relocations against sections. This
+ is required for the dynamic linker to operate properly. When
+ generating PIC, we need to keep any non PC relative reloc. The PIC
+ part of this test must be parallel to the code in tc_gen_reloc which
+ converts relocations to GOT relocations. */
+#define tc_fix_adjustable(FIX) \
+ ((FIX)->fx_r_type != BFD_RELOC_VTABLE_INHERIT \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_ENTRY \
+ && ((FIX)->fx_r_type < BFD_RELOC_SPARC_TLS_GD_HI22 \
+ || (FIX)->fx_r_type > BFD_RELOC_SPARC_TLS_TPOFF64) \
+ && (! sparc_pic_code \
+ || ((FIX)->fx_r_type != BFD_RELOC_HI22 \
+ && (FIX)->fx_r_type != BFD_RELOC_LO10 \
+ && (FIX)->fx_r_type != BFD_RELOC_SPARC13 \
+ && ((FIX)->fx_r_type != BFD_RELOC_32_PCREL_S2 \
+ || !generic_force_reloc (FIX)) \
+ && ((FIX)->fx_pcrel \
+ || ((FIX)->fx_subsy != NULL \
+ && (S_GET_SEGMENT ((FIX)->fx_subsy) \
+ == S_GET_SEGMENT ((FIX)->fx_addsy))) \
+ || S_IS_LOCAL ((FIX)->fx_addsy)))))
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* Finish up the entire symtab. */
+#define tc_adjust_symtab() sparc_adjust_symtab ()
+extern void sparc_adjust_symtab (void);
+#endif
+
+#ifdef OBJ_AOUT
+/* When generating PIC code, we must not adjust any reloc which will
+ turn into a reloc against the global offset table, nor any reloc
+ which we will need if a symbol is overridden. */
+#define tc_fix_adjustable(FIX) \
+ (! sparc_pic_code \
+ || ((FIX)->fx_pcrel \
+ && ((FIX)->fx_addsy == NULL \
+ || (! S_IS_EXTERNAL ((FIX)->fx_addsy) \
+ && ! S_IS_WEAK ((FIX)->fx_addsy)))) \
+ || (FIX)->fx_r_type == BFD_RELOC_16 \
+ || (FIX)->fx_r_type == BFD_RELOC_32)
+#endif
+
+#define elf_tc_final_processing sparc_elf_final_processing
+extern void sparc_elf_final_processing (void);
+
+#define md_operand(x)
+
+extern void sparc_md_end (void);
+#define md_end() sparc_md_end ()
+
+#endif
+
+#define TC_PARSE_CONS_RETURN_TYPE const char *
+#define TC_PARSE_CONS_RETURN_NONE NULL
+
+#ifdef OBJ_ELF
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) sparc_cons (EXP, NBYTES)
+extern const char *sparc_cons (expressionS *, int);
+#endif
+
+#define TC_CONS_FIX_NEW cons_fix_new_sparc
+extern void cons_fix_new_sparc
+(struct frag *, int, unsigned int, struct expressionS *, const char *);
+
+#define TC_FIX_TYPE valueT
+
+#define TC_INIT_FIX_DATA(X) \
+ do \
+ { \
+ (X)->tc_fix_data = 0; \
+ } \
+ while (0)
+
+#define TC_FIX_DATA_PRINT(FILE, FIX) \
+ do \
+ { \
+ fprintf ((FILE), "addend2=%ld\n", \
+ (unsigned long) (FIX)->tc_fix_data); \
+ } \
+ while (0)
+
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions sparc_cfi_frame_initial_instructions
+extern void sparc_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum sparc_regname_to_dw2regnum
+extern int sparc_regname_to_dw2regnum (char *regname);
+
+#define tc_cfi_emit_pcrel_expr sparc_cfi_emit_pcrel_expr
+extern void sparc_cfi_emit_pcrel_expr (expressionS *, unsigned int);
+
+extern int sparc_cie_data_alignment;
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 4
+#define DWARF2_DEFAULT_RETURN_COLUMN 15
+#define DWARF2_CIE_DATA_ALIGNMENT sparc_cie_data_alignment
+
+/* end of tc-sparc.h */
diff --git a/gas/config/tc-spu.c b/gas/config/tc-spu.c
new file mode 100644
index 0000000..717cc33
--- /dev/null
+++ b/gas/config/tc-spu.c
@@ -0,0 +1,1100 @@
+/* spu.c -- Assembler for the IBM Synergistic Processing Unit (SPU)
+
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "dwarf2dbg.h"
+
+const struct spu_opcode spu_opcodes[] = {
+#define APUOP(TAG,MACFORMAT,OPCODE,MNEMONIC,ASMFORMAT,DEP,PIPE) \
+ { MACFORMAT, (OPCODE) << (32-11), MNEMONIC, ASMFORMAT },
+#define APUOPFB(TAG,MACFORMAT,OPCODE,FB,MNEMONIC,ASMFORMAT,DEP,PIPE) \
+ { MACFORMAT, ((OPCODE) << (32-11)) | ((FB) << (32-18)), MNEMONIC, ASMFORMAT },
+#include "opcode/spu-insns.h"
+#undef APUOP
+#undef APUOPFB
+};
+
+static const int spu_num_opcodes =
+ sizeof (spu_opcodes) / sizeof (spu_opcodes[0]);
+
+#define MAX_RELOCS 2
+
+struct spu_insn
+{
+ unsigned int opcode;
+ expressionS exp[MAX_RELOCS];
+ int reloc_arg[MAX_RELOCS];
+ bfd_reloc_code_real_type reloc[MAX_RELOCS];
+ enum spu_insns tag;
+};
+
+static const char *get_imm (const char *param, struct spu_insn *insn, int arg);
+static const char *get_reg (const char *param, struct spu_insn *insn, int arg,
+ int accept_expr);
+static int calcop (struct spu_opcode *format, const char *param,
+ struct spu_insn *insn);
+static void spu_brinfo (int);
+static void spu_cons (int);
+
+extern char *myname;
+static struct hash_control *op_hash = NULL;
+
+/* These bits should be turned off in the first address of every segment */
+int md_seg_align = 7;
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = "#";
+
+/* These chars only start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* gods own line continuation char */
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant */
+/* as in 0f123.456 */
+/* or 0H1.234E-12 (see exp chars above) */
+const char FLT_CHARS[] = "dDfF";
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_ptwo, 4},
+ {"brinfo", spu_brinfo, 0},
+ {"bss", s_lcomm_bytes, 1},
+ {"def", s_set, 0},
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {"global", s_globl, 0},
+ {"half", cons, 2},
+ {"int", spu_cons, 4},
+ {"long", spu_cons, 4},
+ {"quad", spu_cons, 8},
+ {"string", stringer, 8 + 1},
+ {"word", spu_cons, 4},
+ /* Force set to be treated as an instruction. */
+ {"set", NULL, 0},
+ {".set", s_set, 0},
+ /* Likewise for eqv. */
+ {"eqv", NULL, 0},
+ {".eqv", s_set, -1},
+ {0,0,0}
+};
+
+/* Bits plugged into branch instruction offset field. */
+unsigned int brinfo;
+
+void
+md_begin (void)
+{
+ const char *retval = NULL;
+ int i;
+
+ /* initialize hash table */
+
+ op_hash = hash_new ();
+
+ /* loop until you see the end of the list */
+
+ for (i = 0; i < spu_num_opcodes; i++)
+ {
+ /* hash each mnemonic and record its position */
+
+ retval = hash_insert (op_hash, spu_opcodes[i].mnemonic,
+ (void *) &spu_opcodes[i]);
+
+ if (retval != NULL && strcmp (retval, "exists") != 0)
+ as_fatal (_("Can't hash instruction '%s':%s"),
+ spu_opcodes[i].mnemonic, retval);
+ }
+}
+
+const char *md_shortopts = "";
+struct option md_longopts[] = {
+#define OPTION_APUASM (OPTION_MD_BASE)
+ {"apuasm", no_argument, NULL, OPTION_APUASM},
+#define OPTION_DD2 (OPTION_MD_BASE+1)
+ {"mdd2.0", no_argument, NULL, OPTION_DD2},
+#define OPTION_DD1 (OPTION_MD_BASE+2)
+ {"mdd1.0", no_argument, NULL, OPTION_DD1},
+#define OPTION_DD3 (OPTION_MD_BASE+3)
+ {"mdd3.0", no_argument, NULL, OPTION_DD3},
+ { NULL, no_argument, NULL, 0 }
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* When set (by -apuasm) our assembler emulates the behaviour of apuasm.
+ * e.g. don't add bias to float conversion and don't right shift
+ * immediate values. */
+static int emulate_apuasm;
+
+/* Use the dd2.0 instructions set. The only differences are some new
+ * register names and the orx insn */
+static int use_dd2 = 1;
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ case OPTION_APUASM:
+ emulate_apuasm = 1;
+ break;
+ case OPTION_DD3:
+ use_dd2 = 1;
+ break;
+ case OPTION_DD2:
+ use_dd2 = 1;
+ break;
+ case OPTION_DD1:
+ use_dd2 = 0;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fputs (_("\
+SPU options:\n\
+ --apuasm emulate behaviour of apuasm\n"),
+ stream);
+}
+
+
+struct arg_encode {
+ int size;
+ int pos;
+ int rshift;
+ int lo, hi;
+ int wlo, whi;
+ bfd_reloc_code_real_type reloc;
+};
+
+static struct arg_encode arg_encode[A_MAX] = {
+ { 7, 0, 0, 0, 127, 0, -1, 0 }, /* A_T */
+ { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_A */
+ { 7, 14, 0, 0, 127, 0, -1, 0 }, /* A_B */
+ { 7, 21, 0, 0, 127, 0, -1, 0 }, /* A_C */
+ { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_S */
+ { 7, 7, 0, 0, 127, 0, -1, 0 }, /* A_H */
+ { 0, 0, 0, 0, -1, 0, -1, 0 }, /* A_P */
+ { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_S3 */
+ { 7, 14, 0, -32, 31, -31, 0, BFD_RELOC_SPU_IMM7 }, /* A_S6 */
+ { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_S7N */
+ { 7, 14, 0, -64, 63, -63, 0, BFD_RELOC_SPU_IMM7 }, /* A_S7 */
+ { 8, 14, 0, 0, 127, 0, -1, BFD_RELOC_SPU_IMM8 }, /* A_U7A */
+ { 8, 14, 0, 0, 127, 0, -1, BFD_RELOC_SPU_IMM8 }, /* A_U7B */
+ { 10, 14, 0, -512, 511, -128, 255, BFD_RELOC_SPU_IMM10 }, /* A_S10B */
+ { 10, 14, 0, -512, 511, 0, -1, BFD_RELOC_SPU_IMM10 }, /* A_S10 */
+ { 2, 23, 9, -1024, 1023, 0, -1, BFD_RELOC_SPU_PCREL9a }, /* A_S11 */
+ { 2, 14, 9, -1024, 1023, 0, -1, BFD_RELOC_SPU_PCREL9b }, /* A_S11I */
+ { 10, 14, 4, -8192, 8191, 0, -1, BFD_RELOC_SPU_IMM10W }, /* A_S14 */
+ { 16, 7, 0, -32768, 32767, 0, -1, BFD_RELOC_SPU_IMM16 }, /* A_S16 */
+ { 16, 7, 2, -131072, 262143, 0, -1, BFD_RELOC_SPU_IMM16W }, /* A_S18 */
+ { 16, 7, 2, -262144, 262143, 0, -1, BFD_RELOC_SPU_PCREL16 }, /* A_R18 */
+ { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_U3 */
+ { 7, 14, 0, 0, 127, 0, 31, BFD_RELOC_SPU_IMM7 }, /* A_U5 */
+ { 7, 14, 0, 0, 127, 0, 63, BFD_RELOC_SPU_IMM7 }, /* A_U6 */
+ { 7, 14, 0, 0, -1, 0, -1, BFD_RELOC_SPU_IMM7 }, /* A_U7 */
+ { 14, 0, 0, 0, 16383, 0, -1, 0 }, /* A_U14 */
+ { 16, 7, 0, -32768, 65535, 0, -1, BFD_RELOC_SPU_IMM16 }, /* A_X16 */
+ { 18, 7, 0, 0, 262143, 0, -1, BFD_RELOC_SPU_IMM18 }, /* A_U18 */
+};
+
+/* Some flags for handling errors. This is very hackish and added after
+ * the fact. */
+static int syntax_error_arg;
+static const char *syntax_error_param;
+static int syntax_reg;
+
+static char *
+insn_fmt_string (struct spu_opcode *format)
+{
+ static char buf[64];
+ int len = 0;
+ int i;
+
+ len += sprintf (&buf[len], "%s\t", format->mnemonic);
+ for (i = 1; i <= format->arg[0]; i++)
+ {
+ int arg = format->arg[i];
+ char *exp;
+ if (i > 1 && arg != A_P && format->arg[i-1] != A_P)
+ buf[len++] = ',';
+ if (arg == A_P)
+ exp = "(";
+ else if (arg < A_P)
+ exp = i == syntax_error_arg ? "REG" : "reg";
+ else
+ exp = i == syntax_error_arg ? "IMM" : "imm";
+ len += sprintf (&buf[len], "%s", exp);
+ if (i > 1 && format->arg[i-1] == A_P)
+ buf[len++] = ')';
+ }
+ buf[len] = 0;
+ return buf;
+}
+
+void
+md_assemble (char *op)
+{
+ char *param, *thisfrag;
+ char c;
+ struct spu_opcode *format;
+ struct spu_insn insn;
+ int i;
+
+ gas_assert (op);
+
+ /* skip over instruction to find parameters */
+
+ for (param = op; *param != 0 && !ISSPACE (*param); param++)
+ ;
+ c = *param;
+ *param = 0;
+
+ if (c != 0 && c != '\n')
+ param++;
+
+ /* try to find the instruction in the hash table */
+
+ if ((format = (struct spu_opcode *) hash_find (op_hash, op)) == NULL)
+ {
+ as_bad (_("Invalid mnemonic '%s'"), op);
+ return;
+ }
+
+ if (!use_dd2 && strcmp (format->mnemonic, "orx") == 0)
+ {
+ as_bad (_("'%s' is only available in DD2.0 or higher."), op);
+ return;
+ }
+
+ while (1)
+ {
+ /* try parsing this instruction into insn */
+ for (i = 0; i < MAX_RELOCS; i++)
+ {
+ insn.exp[i].X_add_symbol = 0;
+ insn.exp[i].X_op_symbol = 0;
+ insn.exp[i].X_add_number = 0;
+ insn.exp[i].X_op = O_illegal;
+ insn.reloc_arg[i] = -1;
+ insn.reloc[i] = BFD_RELOC_NONE;
+ }
+ insn.opcode = format->opcode;
+ insn.tag = (enum spu_insns) (format - spu_opcodes);
+
+ syntax_error_arg = 0;
+ syntax_error_param = 0;
+ syntax_reg = 0;
+ if (calcop (format, param, &insn))
+ break;
+
+ /* if it doesn't parse try the next instruction */
+ if (!strcmp (format[0].mnemonic, format[1].mnemonic))
+ format++;
+ else
+ {
+ int parg = format[0].arg[syntax_error_arg-1];
+
+ as_fatal (_("Error in argument %d. Expecting: \"%s\""),
+ syntax_error_arg - (parg == A_P),
+ insn_fmt_string (format));
+ return;
+ }
+ }
+
+ if ((syntax_reg & 4)
+ && ! (insn.tag == M_RDCH
+ || insn.tag == M_RCHCNT
+ || insn.tag == M_WRCH))
+ as_warn (_("Mixing register syntax, with and without '$'."));
+ if (syntax_error_param)
+ {
+ const char *d = syntax_error_param;
+ while (*d != '$')
+ d--;
+ as_warn (_("Treating '%-*s' as a symbol."), (int)(syntax_error_param - d), d);
+ }
+
+ if (brinfo != 0
+ && (insn.tag <= M_BRASL
+ || (insn.tag >= M_BRZ && insn.tag <= M_BRHNZ))
+ && (insn.opcode & 0x7ff80) == 0
+ && (insn.reloc_arg[0] == A_R18
+ || insn.reloc_arg[0] == A_S18
+ || insn.reloc_arg[1] == A_R18
+ || insn.reloc_arg[1] == A_S18))
+ insn.opcode |= brinfo << 7;
+
+ /* grow the current frag and plop in the opcode */
+
+ thisfrag = frag_more (4);
+ md_number_to_chars (thisfrag, insn.opcode, 4);
+
+ /* if this instruction requires labels mark it for later */
+
+ for (i = 0; i < MAX_RELOCS; i++)
+ if (insn.reloc_arg[i] >= 0)
+ {
+ fixS *fixP;
+ bfd_reloc_code_real_type reloc = insn.reloc[i];
+ int pcrel = 0;
+
+ if (reloc == BFD_RELOC_SPU_PCREL9a
+ || reloc == BFD_RELOC_SPU_PCREL9b
+ || reloc == BFD_RELOC_SPU_PCREL16)
+ pcrel = 1;
+ fixP = fix_new_exp (frag_now,
+ thisfrag - frag_now->fr_literal,
+ 4,
+ &insn.exp[i],
+ pcrel,
+ reloc);
+ fixP->tc_fix_data.arg_format = insn.reloc_arg[i];
+ fixP->tc_fix_data.insn_tag = insn.tag;
+ }
+ dwarf2_emit_insn (4);
+
+ /* .brinfo lasts exactly one instruction. */
+ brinfo = 0;
+}
+
+static int
+calcop (struct spu_opcode *format, const char *param, struct spu_insn *insn)
+{
+ int i;
+ int paren = 0;
+ int arg;
+
+ for (i = 1; i <= format->arg[0]; i++)
+ {
+ arg = format->arg[i];
+ syntax_error_arg = i;
+
+ while (ISSPACE (*param))
+ param++;
+ if (*param == 0 || *param == ',')
+ return 0;
+ if (arg < A_P)
+ param = get_reg (param, insn, arg, 1);
+ else if (arg > A_P)
+ param = get_imm (param, insn, arg);
+ else if (arg == A_P)
+ {
+ paren++;
+ if ('(' != *param++)
+ return 0;
+ }
+
+ if (!param)
+ return 0;
+
+ while (ISSPACE (*param))
+ param++;
+
+ if (arg != A_P && paren)
+ {
+ paren--;
+ if (')' != *param++)
+ return 0;
+ }
+ else if (i < format->arg[0]
+ && format->arg[i] != A_P
+ && format->arg[i+1] != A_P)
+ {
+ if (',' != *param++)
+ {
+ syntax_error_arg++;
+ return 0;
+ }
+ }
+ }
+ while (ISSPACE (*param))
+ param++;
+ return !paren && (*param == 0 || *param == '\n');
+}
+
+struct reg_name {
+ unsigned int regno;
+ unsigned int length;
+ char name[32];
+};
+
+#define REG_NAME(NO,NM) { NO, sizeof (NM) - 1, NM }
+
+static struct reg_name reg_name[] = {
+ REG_NAME (0, "lr"), /* link register */
+ REG_NAME (1, "sp"), /* stack pointer */
+ REG_NAME (0, "rp"), /* link register */
+ REG_NAME (127, "fp"), /* frame pointer */
+};
+
+static struct reg_name sp_reg_name[] = {
+};
+
+static struct reg_name ch_reg_name[] = {
+ REG_NAME ( 0, "SPU_RdEventStat"),
+ REG_NAME ( 1, "SPU_WrEventMask"),
+ REG_NAME ( 2, "SPU_WrEventAck"),
+ REG_NAME ( 3, "SPU_RdSigNotify1"),
+ REG_NAME ( 4, "SPU_RdSigNotify2"),
+ REG_NAME ( 7, "SPU_WrDec"),
+ REG_NAME ( 8, "SPU_RdDec"),
+ REG_NAME ( 11, "SPU_RdEventMask"), /* DD2.0 only */
+ REG_NAME ( 13, "SPU_RdMachStat"),
+ REG_NAME ( 14, "SPU_WrSRR0"),
+ REG_NAME ( 15, "SPU_RdSRR0"),
+ REG_NAME ( 28, "SPU_WrOutMbox"),
+ REG_NAME ( 29, "SPU_RdInMbox"),
+ REG_NAME ( 30, "SPU_WrOutIntrMbox"),
+ REG_NAME ( 9, "MFC_WrMSSyncReq"),
+ REG_NAME ( 12, "MFC_RdTagMask"), /* DD2.0 only */
+ REG_NAME ( 16, "MFC_LSA"),
+ REG_NAME ( 17, "MFC_EAH"),
+ REG_NAME ( 18, "MFC_EAL"),
+ REG_NAME ( 19, "MFC_Size"),
+ REG_NAME ( 20, "MFC_TagID"),
+ REG_NAME ( 21, "MFC_Cmd"),
+ REG_NAME ( 22, "MFC_WrTagMask"),
+ REG_NAME ( 23, "MFC_WrTagUpdate"),
+ REG_NAME ( 24, "MFC_RdTagStat"),
+ REG_NAME ( 25, "MFC_RdListStallStat"),
+ REG_NAME ( 26, "MFC_WrListStallAck"),
+ REG_NAME ( 27, "MFC_RdAtomicStat"),
+};
+#undef REG_NAME
+
+static const char *
+get_reg (const char *param, struct spu_insn *insn, int arg, int accept_expr)
+{
+ unsigned regno;
+ int saw_prefix = 0;
+
+ if (*param == '$')
+ {
+ saw_prefix = 1;
+ param++;
+ }
+
+ if (arg == A_H) /* Channel */
+ {
+ if ((param[0] == 'c' || param[0] == 'C')
+ && (param[1] == 'h' || param[1] == 'H')
+ && ISDIGIT (param[2]))
+ param += 2;
+ }
+ else if (arg == A_S) /* Special purpose register */
+ {
+ if ((param[0] == 's' || param[0] == 'S')
+ && (param[1] == 'p' || param[1] == 'P')
+ && ISDIGIT (param[2]))
+ param += 2;
+ }
+
+ if (ISDIGIT (*param))
+ {
+ regno = 0;
+ while (ISDIGIT (*param))
+ regno = regno * 10 + *param++ - '0';
+ }
+ else
+ {
+ struct reg_name *rn;
+ unsigned int i, n, l = 0;
+
+ if (arg == A_H) /* Channel */
+ {
+ rn = ch_reg_name;
+ n = sizeof (ch_reg_name) / sizeof (*ch_reg_name);
+ }
+ else if (arg == A_S) /* Special purpose register */
+ {
+ rn = sp_reg_name;
+ n = sizeof (sp_reg_name) / sizeof (*sp_reg_name);
+ }
+ else
+ {
+ rn = reg_name;
+ n = sizeof (reg_name) / sizeof (*reg_name);
+ }
+ regno = 128;
+ for (i = 0; i < n; i++)
+ if (rn[i].length > l
+ && 0 == strncasecmp (param, rn[i].name, rn[i].length))
+ {
+ l = rn[i].length;
+ regno = rn[i].regno;
+ }
+ param += l;
+ }
+
+ if (!use_dd2
+ && arg == A_H)
+ {
+ if (regno == 11)
+ as_bad (_("'SPU_RdEventMask' (channel 11) is only available in DD2.0 or higher."));
+ else if (regno == 12)
+ as_bad (_("'MFC_RdTagMask' (channel 12) is only available in DD2.0 or higher."));
+ }
+
+ if (regno < 128)
+ {
+ insn->opcode |= regno << arg_encode[arg].pos;
+ if ((!saw_prefix && syntax_reg == 1)
+ || (saw_prefix && syntax_reg == 2))
+ syntax_reg |= 4;
+ syntax_reg |= saw_prefix ? 1 : 2;
+ return param;
+ }
+
+ if (accept_expr)
+ {
+ char *save_ptr;
+ expressionS ex;
+ save_ptr = input_line_pointer;
+ input_line_pointer = (char *)param;
+ expression (&ex);
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+ if (ex.X_op == O_register || ex.X_op == O_constant)
+ {
+ insn->opcode |= ex.X_add_number << arg_encode[arg].pos;
+ return param;
+ }
+ }
+ return 0;
+}
+
+static const char *
+get_imm (const char *param, struct spu_insn *insn, int arg)
+{
+ int val;
+ char *save_ptr;
+ int low = 0, high = 0;
+ int reloc_i = insn->reloc_arg[0] >= 0 ? 1 : 0;
+
+ if (strncasecmp (param, "%lo(", 4) == 0)
+ {
+ param += 3;
+ low = 1;
+ as_warn (_("Using old style, %%lo(expr), please change to PPC style, expr@l."));
+ }
+ else if (strncasecmp (param, "%hi(", 4) == 0)
+ {
+ param += 3;
+ high = 1;
+ as_warn (_("Using old style, %%hi(expr), please change to PPC style, expr@h."));
+ }
+ else if (strncasecmp (param, "%pic(", 5) == 0)
+ {
+ /* Currently we expect %pic(expr) == expr, so do nothing here.
+ i.e. for code loaded at address 0 $toc will be 0. */
+ param += 4;
+ }
+
+ if (*param == '$')
+ {
+ /* Symbols can start with $, but if this symbol matches a register
+ name, it's probably a mistake. The only way to avoid this
+ warning is to rename the symbol. */
+ struct spu_insn tmp_insn;
+ const char *np = get_reg (param, &tmp_insn, arg, 0);
+
+ if (np)
+ syntax_error_param = np;
+ }
+
+ save_ptr = input_line_pointer;
+ input_line_pointer = (char *) param;
+ expression (&insn->exp[reloc_i]);
+ param = input_line_pointer;
+ input_line_pointer = save_ptr;
+
+ /* Similar to ppc_elf_suffix in tc-ppc.c. We have so few cases to
+ handle we do it inlined here. */
+ if (param[0] == '@' && !ISALNUM (param[2]) && param[2] != '@')
+ {
+ if (param[1] == 'h' || param[1] == 'H')
+ {
+ high = 1;
+ param += 2;
+ }
+ else if (param[1] == 'l' || param[1] == 'L')
+ {
+ low = 1;
+ param += 2;
+ }
+ }
+
+ if (insn->exp[reloc_i].X_op == O_constant)
+ {
+ val = insn->exp[reloc_i].X_add_number;
+
+ if (emulate_apuasm)
+ {
+ /* Convert the value to a format we expect. */
+ val <<= arg_encode[arg].rshift;
+ if (arg == A_U7A)
+ val = 173 - val;
+ else if (arg == A_U7B)
+ val = 155 - val;
+ }
+
+ if (high)
+ val = val >> 16;
+ else if (low)
+ val = val & 0xffff;
+
+ /* Warn about out of range expressions. */
+ {
+ int hi = arg_encode[arg].hi;
+ int lo = arg_encode[arg].lo;
+ int whi = arg_encode[arg].whi;
+ int wlo = arg_encode[arg].wlo;
+
+ if (hi > lo && (val < lo || val > hi))
+ as_fatal (_("Constant expression %d out of range, [%d, %d]."),
+ val, lo, hi);
+ else if (whi > wlo && (val < wlo || val > whi))
+ as_warn (_("Constant expression %d out of range, [%d, %d]."),
+ val, wlo, whi);
+ }
+
+ if (arg == A_U7A)
+ val = 173 - val;
+ else if (arg == A_U7B)
+ val = 155 - val;
+
+ /* Branch hints have a split encoding. Do the bottom part. */
+ if (arg == A_S11 || arg == A_S11I)
+ insn->opcode |= ((val >> 2) & 0x7f);
+
+ insn->opcode |= (((val >> arg_encode[arg].rshift)
+ & ((1 << arg_encode[arg].size) - 1))
+ << arg_encode[arg].pos);
+ }
+ else
+ {
+ insn->reloc_arg[reloc_i] = arg;
+ if (high)
+ insn->reloc[reloc_i] = BFD_RELOC_SPU_HI16;
+ else if (low)
+ insn->reloc[reloc_i] = BFD_RELOC_SPU_LO16;
+ else
+ insn->reloc[reloc_i] = arg_encode[arg].reloc;
+ }
+
+ return param;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+#ifndef WORKING_DOT_WORD
+int md_short_jump_size = 4;
+
+void
+md_create_short_jump (char *ptr,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag,
+ symbolS *to_symbol)
+{
+ ptr[0] = (char) 0xc0;
+ ptr[1] = 0x00;
+ ptr[2] = 0x00;
+ ptr[3] = 0x00;
+ fix_new (frag,
+ ptr - frag->fr_literal,
+ 4,
+ to_symbol,
+ (offsetT) 0,
+ 0,
+ BFD_RELOC_SPU_PCREL16);
+}
+
+int md_long_jump_size = 4;
+
+void
+md_create_long_jump (char *ptr,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag,
+ symbolS *to_symbol)
+{
+ ptr[0] = (char) 0xc0;
+ ptr[1] = 0x00;
+ ptr[2] = 0x00;
+ ptr[3] = 0x00;
+ fix_new (frag,
+ ptr - frag->fr_literal,
+ 4,
+ to_symbol,
+ (offsetT) 0,
+ 0,
+ BFD_RELOC_SPU_PCREL16);
+}
+#endif
+
+/* Handle .brinfo <priority>,<lrlive>. */
+static void
+spu_brinfo (int ignore ATTRIBUTE_UNUSED)
+{
+ addressT priority;
+ addressT lrlive;
+
+ priority = get_absolute_expression ();
+ SKIP_WHITESPACE ();
+
+ lrlive = 0;
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ lrlive = get_absolute_expression ();
+ }
+
+ if (priority > 0x1fff)
+ {
+ as_bad (_("invalid priority '%lu'"), (unsigned long) priority);
+ priority = 0;
+ }
+
+ if (lrlive > 7)
+ {
+ as_bad (_("invalid lrlive '%lu'"), (unsigned long) lrlive);
+ lrlive = 0;
+ }
+
+ brinfo = (lrlive << 13) | priority;
+ demand_empty_rest_of_line ();
+}
+
+/* Support @ppu on symbols referenced in .int/.long/.word/.quad. */
+static void
+spu_cons (int nbytes)
+{
+ expressionS exp;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ deferred_expression (&exp);
+ if ((exp.X_op == O_symbol
+ || exp.X_op == O_constant)
+ && strncasecmp (input_line_pointer, "@ppu", 4) == 0)
+ {
+ char *p = frag_more (nbytes);
+ enum bfd_reloc_code_real reloc;
+
+ /* Check for identifier@suffix+constant. */
+ input_line_pointer += 4;
+ if (*input_line_pointer == '-' || *input_line_pointer == '+')
+ {
+ expressionS new_exp;
+
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ exp.X_add_number += new_exp.X_add_number;
+ }
+
+ reloc = nbytes == 4 ? BFD_RELOC_SPU_PPU32 : BFD_RELOC_SPU_PPU64;
+ fix_new_exp (frag_now, p - frag_now->fr_literal, nbytes,
+ &exp, 0, reloc);
+ }
+ else
+ emit_expr (&exp, nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Put terminator back into stream. */
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segment_type ATTRIBUTE_UNUSED)
+{
+ as_fatal (_("Relaxation should never occur"));
+ return -1;
+}
+
+/* If while processing a fixup, a reloc really needs to be created,
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ if (fixp->fx_addsy)
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ else if (fixp->fx_subsy)
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+ else
+ abort ();
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+ reloc->addend = fixp->fx_addnumber;
+ return reloc;
+}
+
+/* Round up a section's size to the appropriate boundary. */
+
+valueT
+md_section_align (segT seg, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ valueT mask = ((valueT) 1 << align) - 1;
+
+ return (size + mask) & ~mask;
+}
+
+/* Where a PC relative offset is calculated from. On the spu they
+ are calculated from the beginning of the branch instruction. */
+
+long
+md_pcrel_from (fixS *fixp)
+{
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+/* Fill in rs_align_code fragments. */
+
+void
+spu_handle_align (fragS *fragp)
+{
+ static const unsigned char nop_pattern[8] = {
+ 0x40, 0x20, 0x00, 0x00, /* even nop */
+ 0x00, 0x20, 0x00, 0x00, /* odd nop */
+ };
+
+ int bytes;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+
+ if (bytes & 3)
+ {
+ int fix = bytes & 3;
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ fragp->fr_fix += fix;
+ }
+ if (bytes & 4)
+ {
+ memcpy (p, &nop_pattern[4], 4);
+ p += 4;
+ bytes -= 4;
+ fragp->fr_fix += 4;
+ }
+
+ memcpy (p, nop_pattern, 8);
+ fragp->fr_var = 8;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ unsigned int res;
+ unsigned int mask;
+ valueT val = *valP;
+ char *place = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ }
+
+ if (fixP->fx_addsy != NULL)
+ {
+ if (fixP->fx_pcrel)
+ {
+ /* Hack around bfd_install_relocation brain damage. */
+ val += fixP->fx_frag->fr_address + fixP->fx_where;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+
+ case BFD_RELOC_SPU_PCREL16:
+ case BFD_RELOC_SPU_PCREL9a:
+ case BFD_RELOC_SPU_PCREL9b:
+ case BFD_RELOC_32_PCREL:
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("expression too complex"));
+ break;
+ }
+ }
+ }
+
+ fixP->fx_addnumber = val;
+
+ if (fixP->fx_r_type == BFD_RELOC_SPU_PPU32
+ || fixP->fx_r_type == BFD_RELOC_SPU_PPU64
+ || fixP->fx_r_type == BFD_RELOC_SPU_ADD_PIC)
+ return;
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ {
+ fixP->fx_done = 1;
+ res = 0;
+ mask = 0;
+ if (fixP->tc_fix_data.arg_format > A_P)
+ {
+ int hi = arg_encode[fixP->tc_fix_data.arg_format].hi;
+ int lo = arg_encode[fixP->tc_fix_data.arg_format].lo;
+ if (hi > lo && ((offsetT) val < lo || (offsetT) val > hi))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Relocation doesn't fit. (relocation value = 0x%lx)"),
+ (long) val);
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (place, val, 1);
+ return;
+
+ case BFD_RELOC_16:
+ md_number_to_chars (place, val, 2);
+ return;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ md_number_to_chars (place, val, 4);
+ return;
+
+ case BFD_RELOC_64:
+ md_number_to_chars (place, val, 8);
+ return;
+
+ case BFD_RELOC_SPU_IMM7:
+ res = val << 14;
+ mask = 0x7f << 14;
+ break;
+
+ case BFD_RELOC_SPU_IMM8:
+ res = val << 14;
+ mask = 0xff << 14;
+ break;
+
+ case BFD_RELOC_SPU_IMM10:
+ res = val << 14;
+ mask = 0x3ff << 14;
+ break;
+
+ case BFD_RELOC_SPU_IMM10W:
+ res = val << 10;
+ mask = 0x3ff0 << 10;
+ break;
+
+ case BFD_RELOC_SPU_IMM16:
+ res = val << 7;
+ mask = 0xffff << 7;
+ break;
+
+ case BFD_RELOC_SPU_IMM16W:
+ res = val << 5;
+ mask = 0x3fffc << 5;
+ break;
+
+ case BFD_RELOC_SPU_IMM18:
+ res = val << 7;
+ mask = 0x3ffff << 7;
+ break;
+
+ case BFD_RELOC_SPU_PCREL9a:
+ res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 14);
+ mask = (0x1fc >> 2) | (0x600 << 14);
+ break;
+
+ case BFD_RELOC_SPU_PCREL9b:
+ res = ((val & 0x1fc) >> 2) | ((val & 0x600) << 5);
+ mask = (0x1fc >> 2) | (0x600 << 5);
+ break;
+
+ case BFD_RELOC_SPU_PCREL16:
+ res = val << 5;
+ mask = 0x3fffc << 5;
+ break;
+
+ case BFD_RELOC_SPU_HI16:
+ res = val >> 9;
+ mask = 0xffff << 7;
+ break;
+
+ case BFD_RELOC_SPU_LO16:
+ res = val << 7;
+ mask = 0xffff << 7;
+ break;
+
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixP->fx_r_type);
+ }
+
+ res &= mask;
+ place[0] = (place[0] & (~mask >> 24)) | ((res >> 24) & 0xff);
+ place[1] = (place[1] & (~mask >> 16)) | ((res >> 16) & 0xff);
+ place[2] = (place[2] & (~mask >> 8)) | ((res >> 8) & 0xff);
+ place[3] = (place[3] & ~mask) | (res & 0xff);
+ }
+}
diff --git a/gas/config/tc-spu.h b/gas/config/tc-spu.h
new file mode 100644
index 0000000..0839344
--- /dev/null
+++ b/gas/config/tc-spu.h
@@ -0,0 +1,109 @@
+/* spu.h -- Assembler for spu
+
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_SPU
+#define TC_SPU 1
+
+#include "opcode/spu.h"
+
+#define TARGET_FORMAT "elf32-spu"
+#define TARGET_ARCH bfd_arch_spu
+#define TARGET_NAME "elf32-spu"
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+struct tc_fix_info {
+ unsigned short arg_format;
+ unsigned short insn_tag;
+};
+
+/* fixS will have a member named tc_fix_data of this type. */
+#define TC_FIX_TYPE struct tc_fix_info
+#define TC_INIT_FIX_DATA(FIXP) \
+ do \
+ { \
+ (FIXP)->tc_fix_data.arg_format = 0; \
+ (FIXP)->tc_fix_data.insn_tag = 0; \
+ } \
+ while (0)
+
+/* Don't reduce function symbols to section symbols, and don't adjust
+ references to PPU symbols. */
+#define tc_fix_adjustable(FIXP) \
+ (!(S_IS_FUNCTION ((FIXP)->fx_addsy) \
+ || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU32 \
+ || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU64 \
+ || (FIXP)->fx_r_type == BFD_RELOC_SPU_ADD_PIC))
+
+/* Keep relocs on calls. Branches to function symbols are tail or
+ sibling calls. */
+#define TC_FORCE_RELOCATION(FIXP) \
+ ((FIXP)->tc_fix_data.insn_tag == M_BRSL \
+ || (FIXP)->tc_fix_data.insn_tag == M_BRASL \
+ || (((FIXP)->tc_fix_data.insn_tag == M_BR \
+ || (FIXP)->tc_fix_data.insn_tag == M_BRA) \
+ && (FIXP)->fx_addsy != NULL \
+ && S_IS_FUNCTION ((FIXP)->fx_addsy)) \
+ || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU32 \
+ || (FIXP)->fx_r_type == BFD_RELOC_SPU_PPU64 \
+ || (FIXP)->fx_r_type == BFD_RELOC_SPU_ADD_PIC \
+ || generic_force_reloc (FIXP))
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* The spu uses pseudo-ops with no leading period. */
+#define NO_PSEUDO_DOT 1
+
+/* Don't warn on word overflow; it happens on %hi relocs. */
+#undef WARN_SIGNED_OVERFLOW_WORD
+
+#define DIFF_EXPR_OK
+
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+#define md_convert_frag(b,s,f) {as_fatal (_("spu convert_frag\n"));}
+
+/* We don't need to do anything special for undefined symbols. */
+#define md_undefined_symbol(s) 0
+
+extern symbolS *section_symbol (asection *);
+#define md_operand(e) \
+ do { \
+ if (strncasecmp (input_line_pointer, "@ppu", 4) == 0) \
+ { \
+ e->X_op = O_symbol; \
+ if (abs_section_sym == NULL) \
+ abs_section_sym = section_symbol (absolute_section); \
+ e->X_add_symbol = abs_section_sym; \
+ e->X_add_number = 0; \
+ } \
+ } while (0)
+
+/* Fill in rs_align_code fragments. */
+extern void spu_handle_align (fragS *);
+#define HANDLE_ALIGN(frag) spu_handle_align (frag)
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (7 + 8)
+
+#endif /* TC_SPU */
diff --git a/gas/config/tc-tic30.c b/gas/config/tc-tic30.c
new file mode 100644
index 0000000..dbcbf3c
--- /dev/null
+++ b/gas/config/tc-tic30.c
@@ -0,0 +1,2002 @@
+/* tc-c30.c -- Assembly code for the Texas Instruments TMS320C30
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+ Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Texas Instruments TMS320C30 machine specific gas.
+ Written by Steven Haworth (steve@pm.cse.rmit.edu.au).
+ Bugs & suggestions are completely welcome. This is free software.
+ Please help us make it better. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcode/tic30.h"
+
+/* Put here all non-digit non-letter characters that may occur in an
+ operand. */
+static char operand_special_chars[] = "%$-+(,)*._~/<>&^!:[@]";
+static char *ordinal_names[] =
+{
+ N_("first"), N_("second"), N_("third"), N_("fourth"), N_("fifth")
+};
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "*";
+const char line_separator_chars[] = "";
+
+const char *md_shortopts = "";
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456
+ or 0d1.2345e12. */
+const char FLT_CHARS[] = "fFdDxX";
+
+/* Chars that can be used to separate mant from exp in floating point
+ nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Tables for lexical analysis. */
+static char opcode_chars[256];
+static char register_chars[256];
+static char operand_chars[256];
+static char space_chars[256];
+static char identifier_chars[256];
+static char digit_chars[256];
+
+/* Lexical macros. */
+#define is_opcode_char(x) (opcode_chars [(unsigned char) x])
+#define is_operand_char(x) (operand_chars [(unsigned char) x])
+#define is_register_char(x) (register_chars [(unsigned char) x])
+#define is_space_char(x) (space_chars [(unsigned char) x])
+#define is_identifier_char(x) (identifier_chars [(unsigned char) x])
+#define is_digit_char(x) (digit_chars [(unsigned char) x])
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {0, 0, 0}
+};
+
+static int ATTRIBUTE_PRINTF_1
+debug (const char *string, ...)
+{
+ if (flag_debug)
+ {
+ char str[100];
+ va_list argptr;
+
+ va_start (argptr, string);
+ vsprintf (str, string, argptr);
+ va_end (argptr);
+ if (str[0] == '\0')
+ return (0);
+ fputs (str, USE_STDOUT ? stdout : stderr);
+ return strlen (str);
+ }
+ else
+ return 0;
+}
+
+/* Hash table for opcode lookup. */
+static struct hash_control *op_hash;
+/* Hash table for parallel opcode lookup. */
+static struct hash_control *parop_hash;
+/* Hash table for register lookup. */
+static struct hash_control *reg_hash;
+/* Hash table for indirect addressing lookup. */
+static struct hash_control *ind_hash;
+
+void
+md_begin (void)
+{
+ const char *hash_err;
+
+ debug ("In md_begin()\n");
+ op_hash = hash_new ();
+
+ {
+ const insn_template *current_optab = tic30_optab;
+
+ for (; current_optab < tic30_optab_end; current_optab++)
+ {
+ hash_err = hash_insert (op_hash, current_optab->name,
+ (char *) current_optab);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s",
+ current_optab->name, hash_err);
+ }
+ }
+
+ parop_hash = hash_new ();
+
+ {
+ const partemplate *current_parop = tic30_paroptab;
+
+ for (; current_parop < tic30_paroptab_end; current_parop++)
+ {
+ hash_err = hash_insert (parop_hash, current_parop->name,
+ (char *) current_parop);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s",
+ current_parop->name, hash_err);
+ }
+ }
+
+ reg_hash = hash_new ();
+
+ {
+ const reg *current_reg = tic30_regtab;
+
+ for (; current_reg < tic30_regtab_end; current_reg++)
+ {
+ hash_err = hash_insert (reg_hash, current_reg->name,
+ (char *) current_reg);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s",
+ current_reg->name, hash_err);
+ }
+ }
+
+ ind_hash = hash_new ();
+
+ {
+ const ind_addr_type *current_ind = tic30_indaddr_tab;
+
+ for (; current_ind < tic30_indaddrtab_end; current_ind++)
+ {
+ hash_err = hash_insert (ind_hash, current_ind->syntax,
+ (char *) current_ind);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't Hash %s: %s",
+ current_ind->syntax, hash_err);
+ }
+ }
+
+ /* Fill in lexical tables: opcode_chars, operand_chars, space_chars. */
+ {
+ int c;
+ char *p;
+
+ for (c = 0; c < 256; c++)
+ {
+ if (ISLOWER (c) || ISDIGIT (c))
+ {
+ opcode_chars[c] = c;
+ register_chars[c] = c;
+ }
+ else if (ISUPPER (c))
+ {
+ opcode_chars[c] = TOLOWER (c);
+ register_chars[c] = opcode_chars[c];
+ }
+ else if (c == ')' || c == '(')
+ register_chars[c] = c;
+
+ if (ISUPPER (c) || ISLOWER (c) || ISDIGIT (c))
+ operand_chars[c] = c;
+
+ if (ISDIGIT (c) || c == '-')
+ digit_chars[c] = c;
+
+ if (ISALPHA (c) || c == '_' || c == '.' || ISDIGIT (c))
+ identifier_chars[c] = c;
+
+ if (c == ' ' || c == '\t')
+ space_chars[c] = c;
+
+ if (c == '_')
+ opcode_chars[c] = c;
+ }
+ for (p = operand_special_chars; *p != '\0'; p++)
+ operand_chars[(unsigned char) *p] = *p;
+ }
+}
+
+/* Address Mode OR values. */
+#define AM_Register 0x00000000
+#define AM_Direct 0x00200000
+#define AM_Indirect 0x00400000
+#define AM_Immediate 0x00600000
+#define AM_NotReq 0xFFFFFFFF
+
+/* PC Relative OR values. */
+#define PC_Register 0x00000000
+#define PC_Relative 0x02000000
+
+typedef struct
+{
+ unsigned op_type;
+ struct
+ {
+ int resolved;
+ unsigned address;
+ char *label;
+ expressionS direct_expr;
+ } direct;
+ struct
+ {
+ unsigned mod;
+ int ARnum;
+ unsigned char disp;
+ } indirect;
+ struct
+ {
+ unsigned opcode;
+ } reg;
+ struct
+ {
+ int resolved;
+ int decimal_found;
+ float f_number;
+ int s_number;
+ unsigned int u_number;
+ char *label;
+ expressionS imm_expr;
+ } immediate;
+} operand;
+
+insn_template *opcode;
+
+struct tic30_insn
+{
+ insn_template *tm; /* Template of current instruction. */
+ unsigned opcode; /* Final opcode. */
+ unsigned int operands; /* Number of given operands. */
+ /* Type of operand given in instruction. */
+ operand *operand_type[MAX_OPERANDS];
+ unsigned addressing_mode; /* Final addressing mode of instruction. */
+};
+
+struct tic30_insn insn;
+static int found_parallel_insn;
+
+static char output_invalid_buf[sizeof (unsigned char) * 2 + 6];
+
+static char *
+output_invalid (char c)
+{
+ if (ISPRINT (c))
+ snprintf (output_invalid_buf, sizeof (output_invalid_buf),
+ "'%c'", c);
+ else
+ snprintf (output_invalid_buf, sizeof (output_invalid_buf),
+ "(0x%x)", (unsigned char) c);
+ return output_invalid_buf;
+}
+
+/* next_line points to the next line after the current instruction
+ (current_line). Search for the parallel bars, and if found, merge two
+ lines into internal syntax for a parallel instruction:
+ q_[INSN1]_[INSN2] [OPERANDS1] | [OPERANDS2]
+ By this stage, all comments are scrubbed, and only the bare lines are
+ given. */
+
+#define NONE 0
+#define START_OPCODE 1
+#define END_OPCODE 2
+#define START_OPERANDS 3
+#define END_OPERANDS 4
+
+static char *
+tic30_find_parallel_insn (char *current_line, char *next_line)
+{
+ int found_parallel = 0;
+ char first_opcode[256];
+ char second_opcode[256];
+ char first_operands[256];
+ char second_operands[256];
+ char *parallel_insn;
+
+ debug ("In tic30_find_parallel_insn()\n");
+ while (!is_end_of_line[(unsigned char) *next_line])
+ {
+ if (*next_line == PARALLEL_SEPARATOR
+ && *(next_line + 1) == PARALLEL_SEPARATOR)
+ {
+ found_parallel = 1;
+ next_line++;
+ break;
+ }
+ next_line++;
+ }
+ if (!found_parallel)
+ return NULL;
+ debug ("Found a parallel instruction\n");
+
+ {
+ int i;
+ char *op, *operands, *line;
+
+ for (i = 0; i < 2; i++)
+ {
+ if (i == 0)
+ {
+ op = &first_opcode[0];
+ operands = &first_operands[0];
+ line = current_line;
+ }
+ else
+ {
+ op = &second_opcode[0];
+ operands = &second_operands[0];
+ line = next_line;
+ }
+
+ {
+ int search_status = NONE;
+ int char_ptr = 0;
+ char c;
+
+ while (!is_end_of_line[(unsigned char) (c = *line)])
+ {
+ if (is_opcode_char (c) && search_status == NONE)
+ {
+ op[char_ptr++] = TOLOWER (c);
+ search_status = START_OPCODE;
+ }
+ else if (is_opcode_char (c) && search_status == START_OPCODE)
+ op[char_ptr++] = TOLOWER (c);
+ else if (!is_opcode_char (c) && search_status == START_OPCODE)
+ {
+ op[char_ptr] = '\0';
+ char_ptr = 0;
+ search_status = END_OPCODE;
+ }
+ else if (is_operand_char (c) && search_status == START_OPERANDS)
+ operands[char_ptr++] = c;
+
+ if (is_operand_char (c) && search_status == END_OPCODE)
+ {
+ operands[char_ptr++] = c;
+ search_status = START_OPERANDS;
+ }
+
+ line++;
+ }
+ if (search_status != START_OPERANDS)
+ return NULL;
+ operands[char_ptr] = '\0';
+ }
+ }
+ }
+ parallel_insn = malloc (strlen (first_opcode) + strlen (first_operands)
+ + strlen (second_opcode) + strlen (second_operands) + 8);
+ sprintf (parallel_insn, "q_%s_%s %s | %s",
+ first_opcode, second_opcode,
+ first_operands, second_operands);
+ debug ("parallel insn = %s\n", parallel_insn);
+ return parallel_insn;
+}
+
+#undef NONE
+#undef START_OPCODE
+#undef END_OPCODE
+#undef START_OPERANDS
+#undef END_OPERANDS
+
+static operand *
+tic30_operand (char *token)
+{
+ unsigned int count;
+ char ind_buffer[strlen (token)];
+ operand *current_op;
+
+ debug ("In tic30_operand with %s\n", token);
+ current_op = malloc (sizeof (* current_op));
+ memset (current_op, '\0', sizeof (operand));
+
+ if (*token == DIRECT_REFERENCE)
+ {
+ char *token_posn = token + 1;
+ int direct_label = 0;
+
+ debug ("Found direct reference\n");
+ while (*token_posn)
+ {
+ if (!is_digit_char (*token_posn))
+ direct_label = 1;
+ token_posn++;
+ }
+
+ if (direct_label)
+ {
+ char *save_input_line_pointer;
+ segT retval;
+
+ debug ("Direct reference is a label\n");
+ current_op->direct.label = token + 1;
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = token + 1;
+ debug ("Current input_line_pointer: %s\n", input_line_pointer);
+ retval = expression (&current_op->direct.direct_expr);
+
+ debug ("Expression type: %d\n",
+ current_op->direct.direct_expr.X_op);
+ debug ("Expression addnum: %ld\n",
+ (long) current_op->direct.direct_expr.X_add_number);
+ debug ("Segment: %p\n", retval);
+
+ input_line_pointer = save_input_line_pointer;
+
+ if (current_op->direct.direct_expr.X_op == O_constant)
+ {
+ current_op->direct.address =
+ current_op->direct.direct_expr.X_add_number;
+ current_op->direct.resolved = 1;
+ }
+ }
+ else
+ {
+ debug ("Direct reference is a number\n");
+ current_op->direct.address = atoi (token + 1);
+ current_op->direct.resolved = 1;
+ }
+ current_op->op_type = Direct;
+ }
+ else if (*token == INDIRECT_REFERENCE)
+ {
+ /* Indirect reference operand. */
+ int found_ar = 0;
+ int found_disp = 0;
+ int ar_number = -1;
+ int disp_number = 0;
+ int buffer_posn = 1;
+ ind_addr_type *ind_addr_op;
+
+ debug ("Found indirect reference\n");
+ ind_buffer[0] = *token;
+
+ for (count = 1; count < strlen (token); count++)
+ {
+ /* Strip operand. */
+ ind_buffer[buffer_posn] = TOLOWER (*(token + count));
+
+ if ((*(token + count - 1) == 'a' || *(token + count - 1) == 'A')
+ && (*(token + count) == 'r' || *(token + count) == 'R'))
+ {
+ /* AR reference is found, so get its number and remove
+ it from the buffer so it can pass through hash_find(). */
+ if (found_ar)
+ {
+ as_bad (_("More than one AR register found in indirect reference"));
+ return NULL;
+ }
+ if (*(token + count + 1) < '0' || *(token + count + 1) > '7')
+ {
+ as_bad (_("Illegal AR register in indirect reference"));
+ return NULL;
+ }
+ ar_number = *(token + count + 1) - '0';
+ found_ar = 1;
+ count++;
+ }
+
+ if (*(token + count) == '(')
+ {
+ /* Parenthesis found, so check if a displacement value is
+ inside. If so, get the value and remove it from the
+ buffer. */
+ if (is_digit_char (*(token + count + 1)))
+ {
+ char disp[10];
+ int disp_posn = 0;
+
+ if (found_disp)
+ {
+ as_bad (_("More than one displacement found in indirect reference"));
+ return NULL;
+ }
+ count++;
+ while (*(token + count) != ')')
+ {
+ if (!is_digit_char (*(token + count)))
+ {
+ as_bad (_("Invalid displacement in indirect reference"));
+ return NULL;
+ }
+ disp[disp_posn++] = *(token + (count++));
+ }
+ disp[disp_posn] = '\0';
+ disp_number = atoi (disp);
+ count--;
+ found_disp = 1;
+ }
+ }
+ buffer_posn++;
+ }
+
+ ind_buffer[buffer_posn] = '\0';
+ if (!found_ar)
+ {
+ as_bad (_("AR register not found in indirect reference"));
+ return NULL;
+ }
+
+ ind_addr_op = (ind_addr_type *) hash_find (ind_hash, ind_buffer);
+ if (ind_addr_op)
+ {
+ debug ("Found indirect reference: %s\n", ind_addr_op->syntax);
+ if (ind_addr_op->displacement == IMPLIED_DISP)
+ {
+ found_disp = 1;
+ disp_number = 1;
+ }
+ else if ((ind_addr_op->displacement == DISP_REQUIRED) && !found_disp)
+ {
+ /* Maybe an implied displacement of 1 again. */
+ as_bad (_("required displacement wasn't given in indirect reference"));
+ return 0;
+ }
+ }
+ else
+ {
+ as_bad (_("illegal indirect reference"));
+ return NULL;
+ }
+
+ if (found_disp && (disp_number < 0 || disp_number > 255))
+ {
+ as_bad (_("displacement must be an unsigned 8-bit number"));
+ return NULL;
+ }
+
+ current_op->indirect.mod = ind_addr_op->modfield;
+ current_op->indirect.disp = disp_number;
+ current_op->indirect.ARnum = ar_number;
+ current_op->op_type = Indirect;
+ }
+ else
+ {
+ reg *regop = (reg *) hash_find (reg_hash, token);
+
+ if (regop)
+ {
+ debug ("Found register operand: %s\n", regop->name);
+ if (regop->regtype == REG_ARn)
+ current_op->op_type = ARn;
+ else if (regop->regtype == REG_Rn)
+ current_op->op_type = Rn;
+ else if (regop->regtype == REG_DP)
+ current_op->op_type = DPReg;
+ else
+ current_op->op_type = OtherReg;
+ current_op->reg.opcode = regop->opcode;
+ }
+ else
+ {
+ if (!is_digit_char (*token)
+ || *(token + 1) == 'x'
+ || strchr (token, 'h'))
+ {
+ char *save_input_line_pointer;
+ segT retval;
+
+ debug ("Probably a label: %s\n", token);
+ current_op->immediate.label = malloc (strlen (token) + 1);
+ strcpy (current_op->immediate.label, token);
+ current_op->immediate.label[strlen (token)] = '\0';
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = token;
+
+ debug ("Current input_line_pointer: %s\n", input_line_pointer);
+ retval = expression (&current_op->immediate.imm_expr);
+ debug ("Expression type: %d\n",
+ current_op->immediate.imm_expr.X_op);
+ debug ("Expression addnum: %ld\n",
+ (long) current_op->immediate.imm_expr.X_add_number);
+ debug ("Segment: %p\n", retval);
+ input_line_pointer = save_input_line_pointer;
+
+ if (current_op->immediate.imm_expr.X_op == O_constant)
+ {
+ current_op->immediate.s_number
+ = current_op->immediate.imm_expr.X_add_number;
+ current_op->immediate.u_number
+ = (unsigned int) current_op->immediate.imm_expr.X_add_number;
+ current_op->immediate.resolved = 1;
+ }
+ }
+ else
+ {
+ debug ("Found a number or displacement\n");
+ for (count = 0; count < strlen (token); count++)
+ if (*(token + count) == '.')
+ current_op->immediate.decimal_found = 1;
+ current_op->immediate.label = malloc (strlen (token) + 1);
+ strcpy (current_op->immediate.label, token);
+ current_op->immediate.label[strlen (token)] = '\0';
+ current_op->immediate.f_number = (float) atof (token);
+ current_op->immediate.s_number = (int) atoi (token);
+ current_op->immediate.u_number = (unsigned int) atoi (token);
+ current_op->immediate.resolved = 1;
+ }
+ current_op->op_type = Disp | Abs24 | Imm16 | Imm24;
+ if (current_op->immediate.u_number <= 31)
+ current_op->op_type |= IVector;
+ }
+ }
+ return current_op;
+}
+
+struct tic30_par_insn
+{
+ partemplate *tm; /* Template of current parallel instruction. */
+ unsigned operands[2]; /* Number of given operands for each insn. */
+ /* Type of operand given in instruction. */
+ operand *operand_type[2][MAX_OPERANDS];
+ int swap_operands; /* Whether to swap operands around. */
+ unsigned p_field; /* Value of p field in multiply add/sub instructions. */
+ unsigned opcode; /* Final opcode. */
+};
+
+struct tic30_par_insn p_insn;
+
+static int
+tic30_parallel_insn (char *token)
+{
+ static partemplate *p_opcode;
+ char *current_posn = token;
+ char *token_start;
+ char save_char;
+
+ debug ("In tic30_parallel_insn with %s\n", token);
+ memset (&p_insn, '\0', sizeof (p_insn));
+
+ while (is_opcode_char (*current_posn))
+ current_posn++;
+ {
+ /* Find instruction. */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ p_opcode = (partemplate *) hash_find (parop_hash, token);
+ if (p_opcode)
+ {
+ debug ("Found instruction %s\n", p_opcode->name);
+ p_insn.tm = p_opcode;
+ }
+ else
+ {
+ char first_opcode[6] = {0};
+ char second_opcode[6] = {0};
+ unsigned int i;
+ int current_opcode = -1;
+ int char_ptr = 0;
+
+ for (i = 0; i < strlen (token); i++)
+ {
+ char ch = *(token + i);
+
+ if (ch == '_' && current_opcode == -1)
+ {
+ current_opcode = 0;
+ continue;
+ }
+
+ if (ch == '_' && current_opcode == 0)
+ {
+ current_opcode = 1;
+ char_ptr = 0;
+ continue;
+ }
+
+ switch (current_opcode)
+ {
+ case 0:
+ first_opcode[char_ptr++] = ch;
+ break;
+ case 1:
+ second_opcode[char_ptr++] = ch;
+ break;
+ }
+ }
+
+ debug ("first_opcode = %s\n", first_opcode);
+ debug ("second_opcode = %s\n", second_opcode);
+ sprintf (token, "q_%s_%s", second_opcode, first_opcode);
+ p_opcode = (partemplate *) hash_find (parop_hash, token);
+
+ if (p_opcode)
+ {
+ debug ("Found instruction %s\n", p_opcode->name);
+ p_insn.tm = p_opcode;
+ p_insn.swap_operands = 1;
+ }
+ else
+ return 0;
+ }
+ *current_posn = save_char;
+ }
+
+ {
+ /* Find operands. */
+ int paren_not_balanced;
+ int expecting_operand = 0;
+ int found_separator = 0;
+
+ do
+ {
+ /* Skip optional white space before operand. */
+ while (!is_operand_char (*current_posn)
+ && *current_posn != END_OF_INSN)
+ {
+ if (!is_space_char (*current_posn)
+ && *current_posn != PARALLEL_SEPARATOR)
+ {
+ as_bad (_("Invalid character %s before %s operand"),
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return 1;
+ }
+ if (*current_posn == PARALLEL_SEPARATOR)
+ found_separator = 1;
+ current_posn++;
+ }
+
+ token_start = current_posn;
+ paren_not_balanced = 0;
+
+ while (paren_not_balanced || *current_posn != ',')
+ {
+ if (*current_posn == END_OF_INSN)
+ {
+ if (paren_not_balanced)
+ {
+ as_bad (_("Unbalanced parenthesis in %s operand."),
+ ordinal_names[insn.operands]);
+ return 1;
+ }
+ else
+ break;
+ }
+ else if (*current_posn == PARALLEL_SEPARATOR)
+ {
+ while (is_space_char (*(current_posn - 1)))
+ current_posn--;
+ break;
+ }
+ else if (!is_operand_char (*current_posn)
+ && !is_space_char (*current_posn))
+ {
+ as_bad (_("Invalid character %s in %s operand"),
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return 1;
+ }
+
+ if (*current_posn == '(')
+ ++paren_not_balanced;
+ if (*current_posn == ')')
+ --paren_not_balanced;
+ current_posn++;
+ }
+
+ if (current_posn != token_start)
+ {
+ /* Yes, we've read in another operand. */
+ p_insn.operands[found_separator]++;
+ if (p_insn.operands[found_separator] > MAX_OPERANDS)
+ {
+ as_bad (_("Spurious operands; (%d operands/instruction max)"),
+ MAX_OPERANDS);
+ return 1;
+ }
+
+ /* Now parse operand adding info to 'insn' as we go along. */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1] =
+ tic30_operand (token_start);
+ *current_posn = save_char;
+ if (!p_insn.operand_type[found_separator][p_insn.operands[found_separator] - 1])
+ return 1;
+ }
+ else
+ {
+ if (expecting_operand)
+ {
+ as_bad (_("Expecting operand after ','; got nothing"));
+ return 1;
+ }
+ if (*current_posn == ',')
+ {
+ as_bad (_("Expecting operand before ','; got nothing"));
+ return 1;
+ }
+ }
+
+ /* Now *current_posn must be either ',' or END_OF_INSN. */
+ if (*current_posn == ',')
+ {
+ if (*++current_posn == END_OF_INSN)
+ {
+ /* Just skip it, if it's \n complain. */
+ as_bad (_("Expecting operand after ','; got nothing"));
+ return 1;
+ }
+ expecting_operand = 1;
+ }
+ }
+ while (*current_posn != END_OF_INSN);
+ }
+
+ if (p_insn.swap_operands)
+ {
+ int temp_num, i;
+ operand *temp_op;
+
+ temp_num = p_insn.operands[0];
+ p_insn.operands[0] = p_insn.operands[1];
+ p_insn.operands[1] = temp_num;
+ for (i = 0; i < MAX_OPERANDS; i++)
+ {
+ temp_op = p_insn.operand_type[0][i];
+ p_insn.operand_type[0][i] = p_insn.operand_type[1][i];
+ p_insn.operand_type[1][i] = temp_op;
+ }
+ }
+
+ if (p_insn.operands[0] != p_insn.tm->operands_1)
+ {
+ as_bad (_("incorrect number of operands given in the first instruction"));
+ return 1;
+ }
+
+ if (p_insn.operands[1] != p_insn.tm->operands_2)
+ {
+ as_bad (_("incorrect number of operands given in the second instruction"));
+ return 1;
+ }
+
+ debug ("Number of operands in first insn: %d\n", p_insn.operands[0]);
+ debug ("Number of operands in second insn: %d\n", p_insn.operands[1]);
+
+ {
+ /* Now check if operands are correct. */
+ int count;
+ int num_rn = 0;
+ int num_ind = 0;
+
+ for (count = 0; count < 2; count++)
+ {
+ unsigned int i;
+ for (i = 0; i < p_insn.operands[count]; i++)
+ {
+ if ((p_insn.operand_type[count][i]->op_type &
+ p_insn.tm->operand_types[count][i]) == 0)
+ {
+ as_bad (_("%s instruction, operand %d doesn't match"),
+ ordinal_names[count], i + 1);
+ return 1;
+ }
+
+ /* Get number of R register and indirect reference contained
+ within the first two operands of each instruction. This is
+ required for the multiply parallel instructions which require
+ two R registers and two indirect references, but not in any
+ particular place. */
+ if ((p_insn.operand_type[count][i]->op_type & Rn) && i < 2)
+ num_rn++;
+ else if ((p_insn.operand_type[count][i]->op_type & Indirect)
+ && i < 2)
+ num_ind++;
+ }
+ }
+
+ if ((p_insn.tm->operand_types[0][0] & (Indirect | Rn))
+ == (Indirect | Rn))
+ {
+ /* Check for the multiply instructions. */
+ if (num_rn != 2)
+ {
+ as_bad (_("incorrect format for multiply parallel instruction"));
+ return 1;
+ }
+
+ if (num_ind != 2)
+ {
+ /* Shouldn't get here. */
+ as_bad (_("incorrect format for multiply parallel instruction"));
+ return 1;
+ }
+
+ if ((p_insn.operand_type[0][2]->reg.opcode != 0x00)
+ && (p_insn.operand_type[0][2]->reg.opcode != 0x01))
+ {
+ as_bad (_("destination for multiply can only be R0 or R1"));
+ return 1;
+ }
+
+ if ((p_insn.operand_type[1][2]->reg.opcode != 0x02)
+ && (p_insn.operand_type[1][2]->reg.opcode != 0x03))
+ {
+ as_bad (_("destination for add/subtract can only be R2 or R3"));
+ return 1;
+ }
+
+ /* Now determine the P field for the instruction. */
+ if (p_insn.operand_type[0][0]->op_type & Indirect)
+ {
+ if (p_insn.operand_type[0][1]->op_type & Indirect)
+ p_insn.p_field = 0x00000000; /* Ind * Ind, Rn +/- Rn. */
+ else if (p_insn.operand_type[1][0]->op_type & Indirect)
+ p_insn.p_field = 0x01000000; /* Ind * Rn, Ind +/- Rn. */
+ else
+ p_insn.p_field = 0x03000000; /* Ind * Rn, Rn +/- Ind. */
+ }
+ else
+ {
+ if (p_insn.operand_type[0][1]->op_type & Rn)
+ p_insn.p_field = 0x02000000; /* Rn * Rn, Ind +/- Ind. */
+ else if (p_insn.operand_type[1][0]->op_type & Indirect)
+ {
+ operand *temp;
+ p_insn.p_field = 0x01000000; /* Rn * Ind, Ind +/- Rn. */
+ /* Need to swap the two multiply operands around so that
+ everything is in its place for the opcode makeup.
+ ie so Ind * Rn, Ind +/- Rn. */
+ temp = p_insn.operand_type[0][0];
+ p_insn.operand_type[0][0] = p_insn.operand_type[0][1];
+ p_insn.operand_type[0][1] = temp;
+ }
+ else
+ {
+ operand *temp;
+ p_insn.p_field = 0x03000000; /* Rn * Ind, Rn +/- Ind. */
+ temp = p_insn.operand_type[0][0];
+ p_insn.operand_type[0][0] = p_insn.operand_type[0][1];
+ p_insn.operand_type[0][1] = temp;
+ }
+ }
+ }
+ }
+
+ debug ("P field: %08X\n", p_insn.p_field);
+
+ /* Finalise opcode. This is easier for parallel instructions as they have
+ to be fully resolved, there are no memory addresses allowed, except
+ through indirect addressing, so there are no labels to resolve. */
+ p_insn.opcode = p_insn.tm->base_opcode;
+
+ switch (p_insn.tm->oporder)
+ {
+ case OO_4op1:
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22);
+ break;
+
+ case OO_4op2:
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 19);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 22);
+ if (p_insn.operand_type[1][1]->reg.opcode == p_insn.operand_type[0][1]->reg.opcode)
+ as_warn (_("loading the same register in parallel operation"));
+ break;
+
+ case OO_4op3:
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 22);
+ break;
+
+ case OO_5op1:
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
+ p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22);
+ break;
+
+ case OO_5op2:
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19);
+ p_insn.opcode |= (p_insn.operand_type[0][2]->reg.opcode << 22);
+ break;
+
+ case OO_PField:
+ p_insn.opcode |= p_insn.p_field;
+ if (p_insn.operand_type[0][2]->reg.opcode == 0x01)
+ p_insn.opcode |= 0x00800000;
+ if (p_insn.operand_type[1][2]->reg.opcode == 0x03)
+ p_insn.opcode |= 0x00400000;
+
+ switch (p_insn.p_field)
+ {
+ case 0x00000000:
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 19);
+ break;
+ case 0x01000000:
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
+ break;
+ case 0x02000000:
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->reg.opcode << 19);
+ break;
+ case 0x03000000:
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.ARnum);
+ p_insn.opcode |= (p_insn.operand_type[1][1]->indirect.mod << 3);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.ARnum << 8);
+ p_insn.opcode |= (p_insn.operand_type[0][0]->indirect.mod << 11);
+ p_insn.opcode |= (p_insn.operand_type[1][0]->reg.opcode << 16);
+ p_insn.opcode |= (p_insn.operand_type[0][1]->reg.opcode << 19);
+ break;
+ }
+ break;
+ }
+
+ {
+ char *p;
+
+ p = frag_more (INSN_SIZE);
+ md_number_to_chars (p, (valueT) p_insn.opcode, INSN_SIZE);
+ }
+
+ {
+ unsigned int i, j;
+
+ for (i = 0; i < 2; i++)
+ for (j = 0; j < p_insn.operands[i]; j++)
+ free (p_insn.operand_type[i][j]);
+ }
+
+ debug ("Final opcode: %08X\n", p_insn.opcode);
+ debug ("\n");
+
+ return 1;
+}
+
+/* In order to get gas to ignore any | chars at the start of a line,
+ this function returns true if a | is found in a line. */
+
+int
+tic30_unrecognized_line (int c)
+{
+ debug ("In tc_unrecognized_line\n");
+ return (c == PARALLEL_SEPARATOR);
+}
+
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ debug ("In md_estimate_size_before_relax()\n");
+ return 0;
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ register fragS *fragP ATTRIBUTE_UNUSED)
+{
+ debug ("In md_convert_frag()\n");
+}
+
+void
+md_apply_fix (fixS *fixP,
+ valueT *valP,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ valueT value = *valP;
+
+ debug ("In md_apply_fix() with value = %ld\n", (long) value);
+ debug ("Values in fixP\n");
+ debug ("fx_size = %d\n", fixP->fx_size);
+ debug ("fx_pcrel = %d\n", fixP->fx_pcrel);
+ debug ("fx_where = %ld\n", fixP->fx_where);
+ debug ("fx_offset = %d\n", (int) fixP->fx_offset);
+ {
+ char *buf = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ value /= INSN_SIZE;
+ if (fixP->fx_size == 1)
+ /* Special fix for LDP instruction. */
+ value = (value & 0x00FF0000) >> 16;
+
+ debug ("new value = %ld\n", (long) value);
+ md_number_to_chars (buf, value, fixP->fx_size);
+ }
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char *arg ATTRIBUTE_UNUSED)
+{
+ debug ("In md_parse_option()\n");
+ return 0;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+ debug ("In md_show_usage()\n");
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ debug ("In md_undefined_symbol()\n");
+ return (symbolS *) 0;
+}
+
+valueT
+md_section_align (segT segment, valueT size)
+{
+ debug ("In md_section_align() segment = %p and size = %lu\n",
+ segment, (unsigned long) size);
+ size = (size + 3) / 4;
+ size *= 4;
+ debug ("New size value = %lu\n", (unsigned long) size);
+ return size;
+}
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ int offset;
+
+ debug ("In md_pcrel_from()\n");
+ debug ("fx_where = %ld\n", fixP->fx_where);
+ debug ("fx_size = %d\n", fixP->fx_size);
+ /* Find the opcode that represents the current instruction in the
+ fr_literal storage area, and check bit 21. Bit 21 contains whether the
+ current instruction is a delayed one or not, and then set the offset
+ value appropriately. */
+ if (fixP->fx_frag->fr_literal[fixP->fx_where - fixP->fx_size + 1] & 0x20)
+ offset = 3;
+ else
+ offset = 1;
+ debug ("offset = %d\n", offset);
+ /* PC Relative instructions have a format:
+ displacement = Label - (PC + offset)
+ This function returns PC + offset where:
+ fx_where - fx_size = PC
+ INSN_SIZE * offset = offset number of instructions. */
+ return fixP->fx_where - fixP->fx_size + (INSN_SIZE * offset);
+}
+
+char *
+md_atof (int what_statement_type,
+ char *literalP,
+ int *sizeP)
+{
+ int prec;
+ char *token;
+ char keepval;
+ unsigned long value;
+ float float_value;
+
+ debug ("In md_atof()\n");
+ debug ("precision = %c\n", what_statement_type);
+ debug ("literal = %s\n", literalP);
+ debug ("line = ");
+ token = input_line_pointer;
+ while (!is_end_of_line[(unsigned char) *input_line_pointer]
+ && (*input_line_pointer != ','))
+ {
+ debug ("%c", *input_line_pointer);
+ input_line_pointer++;
+ }
+
+ keepval = *input_line_pointer;
+ *input_line_pointer = '\0';
+ debug ("\n");
+ float_value = (float) atof (token);
+ *input_line_pointer = keepval;
+ debug ("float_value = %f\n", float_value);
+
+ switch (what_statement_type)
+ {
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Unrecognized or unsupported floating point constant");
+ }
+
+ if (float_value == 0.0)
+ value = (prec == 2) ? 0x00008000L : 0x80000000L;
+ else
+ {
+ unsigned long exp, sign, mant, tmsfloat;
+ union
+ {
+ float f;
+ long l;
+ }
+ converter;
+
+ converter.f = float_value;
+ tmsfloat = converter.l;
+ sign = tmsfloat & 0x80000000;
+ mant = tmsfloat & 0x007FFFFF;
+ exp = tmsfloat & 0x7F800000;
+ exp <<= 1;
+ if (exp == 0xFF000000)
+ {
+ if (mant == 0)
+ value = 0x7F7FFFFF;
+ else if (sign == 0)
+ value = 0x7F7FFFFF;
+ else
+ value = 0x7F800000;
+ }
+ else
+ {
+ exp -= 0x7F000000;
+ if (sign)
+ {
+ mant = mant & 0x007FFFFF;
+ mant = -mant;
+ mant = mant & 0x00FFFFFF;
+ if (mant == 0)
+ {
+ mant |= 0x00800000;
+ exp = (long) exp - 0x01000000;
+ }
+ }
+ tmsfloat = exp | mant;
+ value = tmsfloat;
+ }
+ if (prec == 2)
+ {
+ long expon, mantis;
+
+ if (tmsfloat == 0x80000000)
+ value = 0x8000;
+ else
+ {
+ value = 0;
+ expon = (tmsfloat & 0xFF000000);
+ expon >>= 24;
+ mantis = tmsfloat & 0x007FFFFF;
+ if (tmsfloat & 0x00800000)
+ {
+ mantis |= 0xFF000000;
+ mantis += 0x00000800;
+ mantis >>= 12;
+ mantis |= 0x00000800;
+ mantis &= 0x0FFF;
+ if (expon > 7)
+ value = 0x7800;
+ }
+ else
+ {
+ mantis |= 0x00800000;
+ mantis += 0x00000800;
+ expon += (mantis >> 24);
+ mantis >>= 12;
+ mantis &= 0x07FF;
+ if (expon > 7)
+ value = 0x77FF;
+ }
+ if (expon < -8)
+ value = 0x8000;
+ if (value == 0)
+ {
+ mantis = (expon << 12) | mantis;
+ value = mantis & 0xFFFF;
+ }
+ }
+ }
+ }
+ md_number_to_chars (literalP, value, prec);
+ *sizeP = prec;
+ return NULL;
+}
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ debug ("In md_number_to_chars()\n");
+ number_to_chars_bigendian (buf, val, n);
+}
+
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixP)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code = 0;
+
+ debug ("In tc_gen_reloc()\n");
+ debug ("fixP.size = %d\n", fixP->fx_size);
+ debug ("fixP.pcrel = %d\n", fixP->fx_pcrel);
+ debug ("addsy.name = %s\n", S_GET_NAME (fixP->fx_addsy));
+
+ switch (F (fixP->fx_size, fixP->fx_pcrel))
+ {
+ MAP (1, 0, BFD_RELOC_TIC30_LDP);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (3, 0, BFD_RELOC_24);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 0, BFD_RELOC_32);
+ default:
+ as_bad (_("Can not do %d byte %srelocation"), fixP->fx_size,
+ fixP->fx_pcrel ? _("pc-relative ") : "");
+ }
+#undef MAP
+#undef F
+
+ rel = xmalloc (sizeof (* rel));
+ gas_assert (rel != 0);
+ rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ rel->addend = 0;
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (!rel->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 rel;
+}
+
+void
+md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+{
+ debug ("In md_operand()\n");
+}
+
+void
+md_assemble (char *line)
+{
+ insn_template *op;
+ char *current_posn;
+ char *token_start;
+ char save_char;
+ unsigned int count;
+
+ debug ("In md_assemble() with argument %s\n", line);
+ memset (&insn, '\0', sizeof (insn));
+ if (found_parallel_insn)
+ {
+ debug ("Line is second part of parallel instruction\n\n");
+ found_parallel_insn = 0;
+ return;
+ }
+ if ((current_posn =
+ tic30_find_parallel_insn (line, input_line_pointer + 1)) == NULL)
+ current_posn = line;
+ else
+ found_parallel_insn = 1;
+
+ while (is_space_char (*current_posn))
+ current_posn++;
+
+ token_start = current_posn;
+
+ if (!is_opcode_char (*current_posn))
+ {
+ as_bad (_("Invalid character %s in opcode"),
+ output_invalid (*current_posn));
+ return;
+ }
+ /* Check if instruction is a parallel instruction
+ by seeing if the first character is a q. */
+ if (*token_start == 'q')
+ {
+ if (tic30_parallel_insn (token_start))
+ {
+ if (found_parallel_insn)
+ free (token_start);
+ return;
+ }
+ }
+ while (is_opcode_char (*current_posn))
+ current_posn++;
+ {
+ /* Find instruction. */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ op = (insn_template *) hash_find (op_hash, token_start);
+ if (op)
+ {
+ debug ("Found instruction %s\n", op->name);
+ insn.tm = op;
+ }
+ else
+ {
+ debug ("Didn't find insn\n");
+ as_bad (_("Unknown TMS320C30 instruction: %s"), token_start);
+ return;
+ }
+ *current_posn = save_char;
+ }
+
+ if (*current_posn != END_OF_INSN)
+ {
+ /* Find operands. */
+ int paren_not_balanced;
+ int expecting_operand = 0;
+ int this_operand;
+ do
+ {
+ /* Skip optional white space before operand. */
+ while (!is_operand_char (*current_posn)
+ && *current_posn != END_OF_INSN)
+ {
+ if (!is_space_char (*current_posn))
+ {
+ as_bad (_("Invalid character %s before %s operand"),
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return;
+ }
+ current_posn++;
+ }
+ token_start = current_posn;
+ paren_not_balanced = 0;
+ while (paren_not_balanced || *current_posn != ',')
+ {
+ if (*current_posn == END_OF_INSN)
+ {
+ if (paren_not_balanced)
+ {
+ as_bad (_("Unbalanced parenthesis in %s operand."),
+ ordinal_names[insn.operands]);
+ return;
+ }
+ else
+ break;
+ }
+ else if (!is_operand_char (*current_posn)
+ && !is_space_char (*current_posn))
+ {
+ as_bad (_("Invalid character %s in %s operand"),
+ output_invalid (*current_posn),
+ ordinal_names[insn.operands]);
+ return;
+ }
+ if (*current_posn == '(')
+ ++paren_not_balanced;
+ if (*current_posn == ')')
+ --paren_not_balanced;
+ current_posn++;
+ }
+ if (current_posn != token_start)
+ {
+ /* Yes, we've read in another operand. */
+ this_operand = insn.operands++;
+ if (insn.operands > MAX_OPERANDS)
+ {
+ as_bad (_("Spurious operands; (%d operands/instruction max)"),
+ MAX_OPERANDS);
+ return;
+ }
+
+ /* Now parse operand adding info to 'insn' as we go along. */
+ save_char = *current_posn;
+ *current_posn = '\0';
+ insn.operand_type[this_operand] = tic30_operand (token_start);
+ *current_posn = save_char;
+ if (insn.operand_type[this_operand] == NULL)
+ return;
+ }
+ else
+ {
+ if (expecting_operand)
+ {
+ as_bad (_("Expecting operand after ','; got nothing"));
+ return;
+ }
+ if (*current_posn == ',')
+ {
+ as_bad (_("Expecting operand before ','; got nothing"));
+ return;
+ }
+ }
+
+ /* Now *current_posn must be either ',' or END_OF_INSN. */
+ if (*current_posn == ',')
+ {
+ if (*++current_posn == END_OF_INSN)
+ {
+ /* Just skip it, if it's \n complain. */
+ as_bad (_("Expecting operand after ','; got nothing"));
+ return;
+ }
+ expecting_operand = 1;
+ }
+ }
+ while (*current_posn != END_OF_INSN);
+ }
+
+ debug ("Number of operands found: %d\n", insn.operands);
+
+ /* Check that number of operands is correct. */
+ if (insn.operands != insn.tm->operands)
+ {
+ unsigned int i;
+ unsigned int numops = insn.tm->operands;
+
+ /* If operands are not the same, then see if any of the operands are
+ not required. Then recheck with number of given operands. If they
+ are still not the same, then give an error, otherwise carry on. */
+ for (i = 0; i < insn.tm->operands; i++)
+ if (insn.tm->operand_types[i] & NotReq)
+ numops--;
+ if (insn.operands != numops)
+ {
+ as_bad (_("Incorrect number of operands given"));
+ return;
+ }
+ }
+ insn.addressing_mode = AM_NotReq;
+ for (count = 0; count < insn.operands; count++)
+ {
+ if (insn.operand_type[count]->op_type & insn.tm->operand_types[count])
+ {
+ debug ("Operand %d matches\n", count + 1);
+ /* If instruction has two operands and has an AddressMode
+ modifier then set addressing mode type for instruction. */
+ if (insn.tm->opcode_modifier == AddressMode)
+ {
+ int addr_insn = 0;
+ /* Store instruction uses the second
+ operand for the address mode. */
+ if ((insn.tm->operand_types[1] & (Indirect | Direct))
+ == (Indirect | Direct))
+ addr_insn = 1;
+
+ if (insn.operand_type[addr_insn]->op_type & (AllReg))
+ insn.addressing_mode = AM_Register;
+ else if (insn.operand_type[addr_insn]->op_type & Direct)
+ insn.addressing_mode = AM_Direct;
+ else if (insn.operand_type[addr_insn]->op_type & Indirect)
+ insn.addressing_mode = AM_Indirect;
+ else
+ insn.addressing_mode = AM_Immediate;
+ }
+ }
+ else
+ {
+ as_bad (_("The %s operand doesn't match"), ordinal_names[count]);
+ return;
+ }
+ }
+
+ /* Now set the addressing mode for 3 operand instructions. */
+ if ((insn.tm->operand_types[0] & op3T1)
+ && (insn.tm->operand_types[1] & op3T2))
+ {
+ /* Set the addressing mode to the values used for 2 operand
+ instructions in the G addressing field of the opcode. */
+ char *p;
+ switch (insn.operand_type[0]->op_type)
+ {
+ case Rn:
+ case ARn:
+ case DPReg:
+ case OtherReg:
+ if (insn.operand_type[1]->op_type & (AllReg))
+ insn.addressing_mode = AM_Register;
+ else if (insn.operand_type[1]->op_type & Indirect)
+ insn.addressing_mode = AM_Direct;
+ else
+ {
+ /* Shouldn't make it to this stage. */
+ as_bad (_("Incompatible first and second operands in instruction"));
+ return;
+ }
+ break;
+ case Indirect:
+ if (insn.operand_type[1]->op_type & (AllReg))
+ insn.addressing_mode = AM_Indirect;
+ else if (insn.operand_type[1]->op_type & Indirect)
+ insn.addressing_mode = AM_Immediate;
+ else
+ {
+ /* Shouldn't make it to this stage. */
+ as_bad (_("Incompatible first and second operands in instruction"));
+ return;
+ }
+ break;
+ }
+ /* Now make up the opcode for the 3 operand instructions. As in
+ parallel instructions, there will be no unresolved values, so they
+ can be fully formed and added to the frag table. */
+ insn.opcode = insn.tm->base_opcode;
+ if (insn.operand_type[0]->op_type & Indirect)
+ {
+ insn.opcode |= (insn.operand_type[0]->indirect.ARnum);
+ insn.opcode |= (insn.operand_type[0]->indirect.mod << 3);
+ }
+ else
+ insn.opcode |= (insn.operand_type[0]->reg.opcode);
+
+ if (insn.operand_type[1]->op_type & Indirect)
+ {
+ insn.opcode |= (insn.operand_type[1]->indirect.ARnum << 8);
+ insn.opcode |= (insn.operand_type[1]->indirect.mod << 11);
+ }
+ else
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 8);
+
+ if (insn.operands == 3)
+ insn.opcode |= (insn.operand_type[2]->reg.opcode << 16);
+
+ insn.opcode |= insn.addressing_mode;
+ p = frag_more (INSN_SIZE);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ /* Not a three operand instruction. */
+ char *p;
+ int am_insn = -1;
+ insn.opcode = insn.tm->base_opcode;
+ /* Create frag for instruction - all instructions are 4 bytes long. */
+ p = frag_more (INSN_SIZE);
+ if ((insn.operands > 0) && (insn.tm->opcode_modifier == AddressMode))
+ {
+ insn.opcode |= insn.addressing_mode;
+ if (insn.addressing_mode == AM_Indirect)
+ {
+ /* Determine which operand gives the addressing mode. */
+ if (insn.operand_type[0]->op_type & Indirect)
+ am_insn = 0;
+ if ((insn.operands > 1)
+ && (insn.operand_type[1]->op_type & Indirect))
+ am_insn = 1;
+ insn.opcode |= (insn.operand_type[am_insn]->indirect.disp);
+ insn.opcode |= (insn.operand_type[am_insn]->indirect.ARnum << 8);
+ insn.opcode |= (insn.operand_type[am_insn]->indirect.mod << 11);
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[!am_insn]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.addressing_mode == AM_Register)
+ {
+ insn.opcode |= (insn.operand_type[0]->reg.opcode);
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.addressing_mode == AM_Direct)
+ {
+ if (insn.operand_type[0]->op_type & Direct)
+ am_insn = 0;
+ if ((insn.operands > 1)
+ && (insn.operand_type[1]->op_type & Direct))
+ am_insn = 1;
+ if (insn.operands > 1)
+ insn.opcode |=
+ (insn.operand_type[! am_insn]->reg.opcode << 16);
+ if (insn.operand_type[am_insn]->direct.resolved == 1)
+ {
+ /* Resolved values can be placed straight
+ into instruction word, and output. */
+ insn.opcode |=
+ (insn.operand_type[am_insn]->direct.address & 0x0000FFFF);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ /* Unresolved direct addressing mode instruction. */
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2,
+ & insn.operand_type[am_insn]->direct.direct_expr,
+ 0, 0);
+ }
+ }
+ else if (insn.addressing_mode == AM_Immediate)
+ {
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ char *keeploc;
+ int size;
+
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
+
+ switch (insn.tm->imm_arg_type)
+ {
+ case Imm_Float:
+ debug ("Floating point first operand\n");
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+
+ keeploc = input_line_pointer;
+ input_line_pointer =
+ insn.operand_type[0]->immediate.label;
+
+ if (md_atof ('f', p + 2, & size) != 0)
+ {
+ as_bad (_("invalid short form floating point immediate operand"));
+ return;
+ }
+
+ input_line_pointer = keeploc;
+ break;
+
+ case Imm_UInt:
+ debug ("Unsigned int first operand\n");
+ if (insn.operand_type[0]->immediate.decimal_found)
+ as_warn (_("rounding down first operand float to unsigned int"));
+ if (insn.operand_type[0]->immediate.u_number > 0xFFFF)
+ as_warn (_("only lower 16-bits of first operand are used"));
+ insn.opcode |=
+ (insn.operand_type[0]->immediate.u_number & 0x0000FFFFL);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ break;
+
+ case Imm_SInt:
+ debug ("Int first operand\n");
+
+ if (insn.operand_type[0]->immediate.decimal_found)
+ as_warn (_("rounding down first operand float to signed int"));
+
+ if (insn.operand_type[0]->immediate.s_number < -32768 ||
+ insn.operand_type[0]->immediate.s_number > 32767)
+ {
+ as_bad (_("first operand is too large for 16-bit signed int"));
+ return;
+ }
+ insn.opcode |=
+ (insn.operand_type[0]->immediate.s_number & 0x0000FFFFL);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ break;
+ }
+ }
+ else
+ {
+ /* Unresolved immediate label. */
+ if (insn.operands > 1)
+ insn.opcode |= (insn.operand_type[1]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal), 2,
+ & insn.operand_type[0]->immediate.imm_expr,
+ 0, 0);
+ }
+ }
+ }
+ else if (insn.tm->opcode_modifier == PCRel)
+ {
+ /* Conditional Branch and Call instructions. */
+ if ((insn.tm->operand_types[0] & (AllReg | Disp))
+ == (AllReg | Disp))
+ {
+ if (insn.operand_type[0]->op_type & (AllReg))
+ {
+ insn.opcode |= (insn.operand_type[0]->reg.opcode);
+ insn.opcode |= PC_Register;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ insn.opcode |= PC_Relative;
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ insn.opcode |=
+ (insn.operand_type[0]->immediate.s_number & 0x0000FFFF);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - (frag_now->fr_literal),
+ 2, & insn.operand_type[0]->immediate.imm_expr,
+ 1, 0);
+ }
+ }
+ }
+ else if ((insn.tm->operand_types[0] & ARn) == ARn)
+ {
+ /* Decrement and Branch instructions. */
+ insn.opcode |= ((insn.operand_type[0]->reg.opcode - 0x08) << 22);
+ if (insn.operand_type[1]->op_type & (AllReg))
+ {
+ insn.opcode |= (insn.operand_type[1]->reg.opcode);
+ insn.opcode |= PC_Register;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.operand_type[1]->immediate.resolved == 1)
+ {
+ if (insn.operand_type[0]->immediate.decimal_found)
+ {
+ as_bad (_("first operand is floating point"));
+ return;
+ }
+ if (insn.operand_type[0]->immediate.s_number < -32768 ||
+ insn.operand_type[0]->immediate.s_number > 32767)
+ {
+ as_bad (_("first operand is too large for 16-bit signed int"));
+ return;
+ }
+ insn.opcode |= (insn.operand_type[1]->immediate.s_number);
+ insn.opcode |= PC_Relative;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ insn.opcode |= PC_Relative;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 2 - frag_now->fr_literal, 2,
+ & insn.operand_type[1]->immediate.imm_expr,
+ 1, 0);
+ }
+ }
+ }
+ else if (insn.tm->operand_types[0] == IVector)
+ {
+ /* Trap instructions. */
+ if (insn.operand_type[0]->op_type & IVector)
+ insn.opcode |= (insn.operand_type[0]->immediate.u_number);
+ else
+ {
+ /* Shouldn't get here. */
+ as_bad (_("interrupt vector for trap instruction out of range"));
+ return;
+ }
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if (insn.tm->opcode_modifier == StackOp
+ || insn.tm->opcode_modifier == Rotate)
+ {
+ /* Push, Pop and Rotate instructions. */
+ insn.opcode |= (insn.operand_type[0]->reg.opcode << 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else if ((insn.tm->operand_types[0] & (Abs24 | Direct))
+ == (Abs24 | Direct))
+ {
+ /* LDP Instruction needs to be tested
+ for before the next section. */
+ if (insn.operand_type[0]->op_type & Direct)
+ {
+ if (insn.operand_type[0]->direct.resolved == 1)
+ {
+ /* Direct addressing uses lower 8 bits of direct address. */
+ insn.opcode |=
+ (insn.operand_type[0]->direct.address & 0x00FF0000) >> 16;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ fixS *fix;
+
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal),
+ 1, &insn.operand_type[0]->direct.direct_expr, 0, 0);
+ /* Ensure that the assembler doesn't complain
+ about fitting a 24-bit address into 8 bits. */
+ fix->fx_no_overflow = 1;
+ }
+ }
+ else
+ {
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ /* Immediate addressing uses upper 8 bits of address. */
+ if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF)
+ {
+ as_bad (_("LDP instruction needs a 24-bit operand"));
+ return;
+ }
+ insn.opcode |=
+ ((insn.operand_type[0]->immediate.u_number & 0x00FF0000) >> 16);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ fixS *fix;
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix = fix_new_exp (frag_now, p + 3 - (frag_now->fr_literal),
+ 1, &insn.operand_type[0]->immediate.imm_expr,
+ 0, 0);
+ fix->fx_no_overflow = 1;
+ }
+ }
+ }
+ else if (insn.tm->operand_types[0] & (Imm24))
+ {
+ /* Unconditional Branch and Call instructions. */
+ if (insn.operand_type[0]->immediate.resolved == 1)
+ {
+ if (insn.operand_type[0]->immediate.u_number > 0x00FFFFFF)
+ as_warn (_("first operand is too large for a 24-bit displacement"));
+ insn.opcode |=
+ (insn.operand_type[0]->immediate.u_number & 0x00FFFFFF);
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ else
+ {
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ fix_new_exp (frag_now, p + 1 - (frag_now->fr_literal), 3,
+ & insn.operand_type[0]->immediate.imm_expr, 0, 0);
+ }
+ }
+ else if (insn.tm->operand_types[0] & NotReq)
+ /* Check for NOP instruction without arguments. */
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+
+ else if (insn.tm->operands == 0)
+ /* Check for instructions without operands. */
+ md_number_to_chars (p, (valueT) insn.opcode, INSN_SIZE);
+ }
+ debug ("Addressing mode: %08X\n", insn.addressing_mode);
+ {
+ unsigned int i;
+
+ for (i = 0; i < insn.operands; i++)
+ {
+ if (insn.operand_type[i]->immediate.label)
+ free (insn.operand_type[i]->immediate.label);
+ free (insn.operand_type[i]);
+ }
+ }
+ debug ("Final opcode: %08X\n", insn.opcode);
+ debug ("\n");
+}
diff --git a/gas/config/tc-tic30.h b/gas/config/tc-tic30.h
new file mode 100644
index 0000000..e39d7b0
--- /dev/null
+++ b/gas/config/tc-tic30.h
@@ -0,0 +1,51 @@
+/* tc-tic30.h -- Header file for tc-tic30.c
+ Copyright (C) 1998-2014 Free Software Foundation, Inc.
+ Contributed by Steven Haworth (steve@pm.cse.rmit.edu.au)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef _TC_TIC30_H_
+#define _TC_TIC30_H_
+
+#define TC_TIC30 1
+
+#ifdef OBJ_AOUT
+#define TARGET_FORMAT "a.out-tic30"
+#endif
+
+#define TARGET_ARCH bfd_arch_tic30
+#define TARGET_BYTES_BIG_ENDIAN 1
+#define WORKING_DOT_WORD
+#define END_OF_INSN '\0'
+#define MAX_OPERANDS 6
+#define DIRECT_REFERENCE '@'
+#define INDIRECT_REFERENCE '*'
+#define PARALLEL_SEPARATOR '|'
+#define INSN_SIZE 4
+
+/* Define this to 1 if you want the debug output to be on stdout,
+ otherwise stderr will be used. If stderr is used, there will be a
+ better synchronisation with the as_bad outputs, but you can't
+ capture the output. */
+#define USE_STDOUT 0
+
+#define tc_unrecognized_line tic30_unrecognized_line
+
+extern int tic30_unrecognized_line (int);
+
+#endif
diff --git a/gas/config/tc-tic4x.c b/gas/config/tc-tic4x.c
new file mode 100644
index 0000000..dc82168
--- /dev/null
+++ b/gas/config/tc-tic4x.c
@@ -0,0 +1,3037 @@
+/* tc-tic4x.c -- Assemble for the Texas Instruments TMS320C[34]x.
+ Copyright (C) 1997-2014 Free Software Foundation, Inc.
+
+ Contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+/*
+ TODOs:
+ ------
+
+ o .align cannot handle fill-data-width larger than 0xFF/8-bits. It
+ should be possible to define a 32-bits pattern.
+
+ o .align: Implement a 'bu' insn if the number of nop's exceeds 4
+ within the align frag. if(fragsize>4words) insert bu fragend+1
+ first.
+
+ o .usect if has symbol on previous line not implemented
+
+ o .sym, .eos, .stag, .etag, .member not implemented
+
+ o Evaluation of constant floating point expressions (expr.c needs
+ work!)
+
+ o Support 'abc' constants (that is 0x616263). */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "opcode/tic4x.h"
+#include "subsegs.h"
+
+/* OK, we accept a syntax similar to the other well known C30
+ assembly tools. With TIC4X_ALT_SYNTAX defined we are more
+ flexible, allowing a more Unix-like syntax: `%' in front of
+ register names, `#' in front of immediate constants, and
+ not requiring `@' in front of direct addresses. */
+
+#define TIC4X_ALT_SYNTAX
+
+/* Equal to MAX_PRECISION in atof-ieee.c. */
+#define MAX_LITTLENUMS 6 /* (12 bytes) */
+
+/* Handle of the inst mnemonic hash table. */
+static struct hash_control *tic4x_op_hash = NULL;
+
+/* Handle asg pseudo. */
+static struct hash_control *tic4x_asg_hash = NULL;
+
+static unsigned int tic4x_cpu = 0; /* Default to TMS320C40. */
+static unsigned int tic4x_revision = 0; /* CPU revision */
+static unsigned int tic4x_idle2 = 0; /* Idle2 support */
+static unsigned int tic4x_lowpower = 0; /* Lowpower support */
+static unsigned int tic4x_enhanced = 0; /* Enhanced opcode support */
+static unsigned int tic4x_big_model = 0; /* Default to small memory model. */
+static unsigned int tic4x_reg_args = 0; /* Default to args passed on stack. */
+static unsigned long tic4x_oplevel = 0; /* Opcode level */
+
+#define OPTION_CPU 'm'
+#define OPTION_BIG (OPTION_MD_BASE + 1)
+#define OPTION_SMALL (OPTION_MD_BASE + 2)
+#define OPTION_MEMPARM (OPTION_MD_BASE + 3)
+#define OPTION_REGPARM (OPTION_MD_BASE + 4)
+#define OPTION_IDLE2 (OPTION_MD_BASE + 5)
+#define OPTION_LOWPOWER (OPTION_MD_BASE + 6)
+#define OPTION_ENHANCED (OPTION_MD_BASE + 7)
+#define OPTION_REV (OPTION_MD_BASE + 8)
+
+const char *md_shortopts = "bm:prs";
+struct option md_longopts[] =
+{
+ { "mcpu", required_argument, NULL, OPTION_CPU },
+ { "mdsp", required_argument, NULL, OPTION_CPU },
+ { "mbig", no_argument, NULL, OPTION_BIG },
+ { "msmall", no_argument, NULL, OPTION_SMALL },
+ { "mmemparm", no_argument, NULL, OPTION_MEMPARM },
+ { "mregparm", no_argument, NULL, OPTION_REGPARM },
+ { "midle2", no_argument, NULL, OPTION_IDLE2 },
+ { "mlowpower", no_argument, NULL, OPTION_LOWPOWER },
+ { "menhanced", no_argument, NULL, OPTION_ENHANCED },
+ { "mrev", required_argument, NULL, OPTION_REV },
+ { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+
+typedef enum
+ {
+ M_UNKNOWN, M_IMMED, M_DIRECT, M_REGISTER, M_INDIRECT,
+ M_IMMED_F, M_PARALLEL, M_HI
+ }
+tic4x_addr_mode_t;
+
+typedef struct tic4x_operand
+ {
+ tic4x_addr_mode_t mode; /* Addressing mode. */
+ expressionS expr; /* Expression. */
+ int disp; /* Displacement for indirect addressing. */
+ int aregno; /* Aux. register number. */
+ LITTLENUM_TYPE fwords[MAX_LITTLENUMS]; /* Float immed. number. */
+ }
+tic4x_operand_t;
+
+typedef struct tic4x_insn
+ {
+ char name[TIC4X_NAME_MAX]; /* Mnemonic of instruction. */
+ unsigned int in_use; /* True if in_use. */
+ unsigned int parallel; /* True if parallel instruction. */
+ unsigned int nchars; /* This is always 4 for the C30. */
+ unsigned long opcode; /* Opcode number. */
+ expressionS exp; /* Expression required for relocation. */
+ int reloc; /* Relocation type required. */
+ int pcrel; /* True if relocation PC relative. */
+ char *pname; /* Name of instruction in parallel. */
+ unsigned int num_operands; /* Number of operands in total. */
+ tic4x_inst_t *inst; /* Pointer to first template. */
+ tic4x_operand_t operands[TIC4X_OPERANDS_MAX];
+ }
+tic4x_insn_t;
+
+static tic4x_insn_t the_insn; /* Info about our instruction. */
+static tic4x_insn_t *insn = &the_insn;
+
+static void tic4x_asg (int);
+static void tic4x_bss (int);
+static void tic4x_globl (int);
+static void tic4x_cons (int);
+static void tic4x_stringer (int);
+static void tic4x_eval (int);
+static void tic4x_newblock (int);
+static void tic4x_sect (int);
+static void tic4x_set (int);
+static void tic4x_usect (int);
+static void tic4x_version (int);
+
+
+const pseudo_typeS
+ md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 32},
+ {"ascii", tic4x_stringer, 1},
+ {"asciz", tic4x_stringer, 0},
+ {"asg", tic4x_asg, 0},
+ {"block", s_space, 4},
+ {"byte", tic4x_cons, 1},
+ {"bss", tic4x_bss, 0},
+ {"copy", s_include, 0},
+ {"def", tic4x_globl, 0},
+ {"equ", tic4x_set, 0},
+ {"eval", tic4x_eval, 0},
+ {"global", tic4x_globl, 0},
+ {"globl", tic4x_globl, 0},
+ {"hword", tic4x_cons, 2},
+ {"ieee", float_cons, 'i'},
+ {"int", tic4x_cons, 4}, /* .int allocates 4 bytes. */
+ {"ldouble", float_cons, 'e'},
+ {"newblock", tic4x_newblock, 0},
+ {"ref", s_ignore, 0}, /* All undefined treated as external. */
+ {"set", tic4x_set, 0},
+ {"sect", tic4x_sect, 1}, /* Define named section. */
+ {"space", s_space, 4},
+ {"string", tic4x_stringer, 0},
+ {"usect", tic4x_usect, 0}, /* Reserve space in uninit. named sect. */
+ {"version", tic4x_version, 0},
+ {"word", tic4x_cons, 4}, /* .word allocates 4 bytes. */
+ {"xdef", tic4x_globl, 0},
+ {NULL, 0, 0},
+};
+
+int md_short_jump_size = 4;
+int md_long_jump_size = 4;
+
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+#ifdef TIC4X_ALT_SYNTAX
+const char comment_chars[] = ";!";
+#else
+const char comment_chars[] = ";";
+#endif
+
+/* This array holds the chars that only start a comment at the beginning of
+ a line. If the line seems to have the form '# 123 filename'
+ .line and .file directives will appear in the pre-processed output.
+ Note that input_file.c hand checks for '#' at the beginning of the
+ first line of the input file. This is because the compiler outputs
+ #NO_APP at the beginning of its output.
+ Also note that comments like this one will always work. */
+const char line_comment_chars[] = "#*";
+
+/* We needed an unused char for line separation to work around the
+ lack of macros, using sed and such. */
+const char line_separator_chars[] = "&";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant. */
+/* As in 0f12.456 */
+/* or 0d1.2345e12 */
+const char FLT_CHARS[] = "fFilsS";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c. Ideally it shouldn't have to know about it at
+ all, but nothing is ideal around here. */
+
+/* Flonums returned here. */
+extern FLONUM_TYPE generic_floating_point_number;
+
+/* Precision in LittleNums. */
+#define MAX_PRECISION (4) /* Its a bit overkill for us, but the code
+ requires it... */
+#define S_PRECISION (1) /* Short float constants 16-bit. */
+#define F_PRECISION (2) /* Float and double types 32-bit. */
+#define E_PRECISION (4) /* Extended precision, 64-bit (real 40-bit). */
+#define GUARD (2)
+
+/* Turn generic_floating_point_number into a real short/float/double. */
+static int
+tic4x_gen_to_words (FLONUM_TYPE flonum, LITTLENUM_TYPE *words, int precision)
+{
+ int return_value = 0;
+ LITTLENUM_TYPE *p; /* Littlenum pointer. */
+ int mantissa_bits; /* Bits in mantissa field. */
+ int exponent_bits; /* Bits in exponent field. */
+ int exponent;
+ unsigned int sone; /* Scaled one. */
+ unsigned int sfract; /* Scaled fraction. */
+ unsigned int smant; /* Scaled mantissa. */
+ unsigned int tmp;
+ unsigned int mover; /* Mantissa overflow bits */
+ unsigned int rbit; /* Round bit. */
+ int shift; /* Shift count. */
+
+ /* NOTE: Svein Seldal <Svein@dev.seldal.com>
+ The code in this function is altered slightly to support floats
+ with 31-bits mantissas, thus the documentation below may be a
+ little bit inaccurate.
+
+ By Michael P. Hayes <m.hayes@elec.canterbury.ac.nz>
+ Here is how a generic floating point number is stored using
+ flonums (an extension of bignums) where p is a pointer to an
+ array of LITTLENUMs.
+
+ For example 2e-3 is stored with exp = -4 and
+ bits[0] = 0x0000
+ bits[1] = 0x0000
+ bits[2] = 0x4fde
+ bits[3] = 0x978d
+ bits[4] = 0x126e
+ bits[5] = 0x0083
+ with low = &bits[2], high = &bits[5], and leader = &bits[5].
+
+ This number can be written as
+ 0x0083126e978d4fde.00000000 * 65536**-4 or
+ 0x0.0083126e978d4fde * 65536**0 or
+ 0x0.83126e978d4fde * 2**-8 = 2e-3
+
+ Note that low points to the 65536**0 littlenum (bits[2]) and
+ leader points to the most significant non-zero littlenum
+ (bits[5]).
+
+ TMS320C3X floating point numbers are a bit of a strange beast.
+ The 32-bit flavour has the 8 MSBs representing the exponent in
+ twos complement format (-128 to +127). There is then a sign bit
+ followed by 23 bits of mantissa. The mantissa is expressed in
+ twos complement format with the binary point after the most
+ significant non sign bit. The bit after the binary point is
+ suppressed since it is the complement of the sign bit. The
+ effective mantissa is thus 24 bits. Zero is represented by an
+ exponent of -128.
+
+ The 16-bit flavour has the 4 MSBs representing the exponent in
+ twos complement format (-8 to +7). There is then a sign bit
+ followed by 11 bits of mantissa. The mantissa is expressed in
+ twos complement format with the binary point after the most
+ significant non sign bit. The bit after the binary point is
+ suppressed since it is the complement of the sign bit. The
+ effective mantissa is thus 12 bits. Zero is represented by an
+ exponent of -8. For example,
+
+ number norm mant m x e s i fraction f
+ +0.500 => 1.00000000000 -1 -1 0 1 .00000000000 (1 + 0) * 2^(-1)
+ +0.999 => 1.11111111111 -1 -1 0 1 .11111111111 (1 + 0.99) * 2^(-1)
+ +1.000 => 1.00000000000 0 0 0 1 .00000000000 (1 + 0) * 2^(0)
+ +1.500 => 1.10000000000 0 0 0 1 .10000000000 (1 + 0.5) * 2^(0)
+ +1.999 => 1.11111111111 0 0 0 1 .11111111111 (1 + 0.9) * 2^(0)
+ +2.000 => 1.00000000000 1 1 0 1 .00000000000 (1 + 0) * 2^(1)
+ +4.000 => 1.00000000000 2 2 0 1 .00000000000 (1 + 0) * 2^(2)
+ -0.500 => 1.00000000000 -1 -1 1 0 .10000000000 (-2 + 0) * 2^(-2)
+ -1.000 => 1.00000000000 0 -1 1 0 .00000000000 (-2 + 0) * 2^(-1)
+ -1.500 => 1.10000000000 0 0 1 0 .10000000000 (-2 + 0.5) * 2^(0)
+ -1.999 => 1.11111111111 0 0 1 0 .00000000001 (-2 + 0.11) * 2^(0)
+ -2.000 => 1.00000000000 1 1 1 0 .00000000000 (-2 + 0) * 2^(0)
+ -4.000 => 1.00000000000 2 1 1 0 .00000000000 (-2 + 0) * 2^(1)
+
+ where e is the exponent, s is the sign bit, i is the implied bit,
+ and f is the fraction stored in the mantissa field.
+
+ num = (1 + f) * 2^x = m * 2^e if s = 0
+ num = (-2 + f) * 2^x = -m * 2^e if s = 1
+ where 0 <= f < 1.0 and 1.0 <= m < 2.0
+
+ The fraction (f) and exponent (e) fields for the TMS320C3X format
+ can be derived from the normalised mantissa (m) and exponent (x) using:
+
+ f = m - 1, e = x if s = 0
+ f = 2 - m, e = x if s = 1 and m != 1.0
+ f = 0, e = x - 1 if s = 1 and m = 1.0
+ f = 0, e = -8 if m = 0
+
+
+ OK, the other issue we have to consider is rounding since the
+ mantissa has a much higher potential precision than what we can
+ represent. To do this we add half the smallest storable fraction.
+ We then have to renormalise the number to allow for overflow.
+
+ To convert a generic flonum into a TMS320C3X floating point
+ number, here's what we try to do....
+
+ The first thing is to generate a normalised mantissa (m) where
+ 1.0 <= m < 2 and to convert the exponent from base 16 to base 2.
+ We desire the binary point to be placed after the most significant
+ non zero bit. This process is done in two steps: firstly, the
+ littlenum with the most significant non zero bit is located (this
+ is done for us since leader points to this littlenum) and the
+ binary point (which is currently after the LSB of the littlenum
+ pointed to by low) is moved to before the MSB of the littlenum
+ pointed to by leader. This requires the exponent to be adjusted
+ by leader - low + 1. In the earlier example, the new exponent is
+ thus -4 + (5 - 2 + 1) = 0 (base 65536). We now need to convert
+ the exponent to base 2 by multiplying the exponent by 16 (log2
+ 65536). The exponent base 2 is thus also zero.
+
+ The second step is to hunt for the most significant non zero bit
+ in the leader littlenum. We do this by left shifting a copy of
+ the leader littlenum until bit 16 is set (0x10000) and counting
+ the number of shifts, S, required. The number of shifts then has to
+ be added to correct the exponent (base 2). For our example, this
+ will require 9 shifts and thus our normalised exponent (base 2) is
+ 0 + 9 = 9. Note that the worst case scenario is when the leader
+ littlenum is 1, thus requiring 16 shifts.
+
+ We now have to left shift the other littlenums by the same amount,
+ propagating the shifted bits into the more significant littlenums.
+ To save a lot of unnecessary shifting we only have to consider
+ two or three littlenums, since the greatest number of mantissa
+ bits required is 24 + 1 rounding bit. While two littlenums
+ provide 32 bits of precision, the most significant littlenum
+ may only contain a single significant bit and thus an extra
+ littlenum is required.
+
+ Denoting the number of bits in the fraction field as F, we require
+ G = F + 2 bits (one extra bit is for rounding, the other gets
+ suppressed). Say we required S shifts to find the most
+ significant bit in the leader littlenum, the number of left shifts
+ required to move this bit into bit position G - 1 is L = G + S - 17.
+ Note that this shift count may be negative for the short floating
+ point flavour (where F = 11 and thus G = 13 and potentially S < 3).
+ If L > 0 we have to shunt the next littlenum into position. Bit
+ 15 (the MSB) of the next littlenum needs to get moved into position
+ L - 1 (If L > 15 we need all the bits of this littlenum and
+ some more from the next one.). We subtract 16 from L and use this
+ as the left shift count; the resultant value we or with the
+ previous result. If L > 0, we repeat this operation. */
+
+ if (precision != S_PRECISION)
+ words[1] = 0x0000;
+ if (precision == E_PRECISION)
+ words[2] = words[3] = 0x0000;
+
+ /* 0.0e0 or NaN seen. */
+ if (flonum.low > flonum.leader /* = 0.0e0 */
+ || flonum.sign == 0) /* = NaN */
+ {
+ if(flonum.sign == 0)
+ as_bad (_("Nan, using zero."));
+ words[0] = 0x8000;
+ return return_value;
+ }
+
+ if (flonum.sign == 'P')
+ {
+ /* +INF: Replace with maximum float. */
+ if (precision == S_PRECISION)
+ words[0] = 0x77ff;
+ else
+ {
+ words[0] = 0x7f7f;
+ words[1] = 0xffff;
+ }
+ if (precision == E_PRECISION)
+ {
+ words[2] = 0x7fff;
+ words[3] = 0xffff;
+ }
+ return return_value;
+ }
+ else if (flonum.sign == 'N')
+ {
+ /* -INF: Replace with maximum float. */
+ if (precision == S_PRECISION)
+ words[0] = 0x7800;
+ else
+ words[0] = 0x7f80;
+ if (precision == E_PRECISION)
+ words[2] = 0x8000;
+ return return_value;
+ }
+
+ exponent = (flonum.exponent + flonum.leader - flonum.low + 1) * 16;
+
+ if (!(tmp = *flonum.leader))
+ abort (); /* Hmmm. */
+ shift = 0; /* Find position of first sig. bit. */
+ while (tmp >>= 1)
+ shift++;
+ exponent -= (16 - shift); /* Adjust exponent. */
+
+ if (precision == S_PRECISION) /* Allow 1 rounding bit. */
+ {
+ exponent_bits = 4;
+ mantissa_bits = 11;
+ }
+ else if(precision == F_PRECISION)
+ {
+ exponent_bits = 8;
+ mantissa_bits = 23;
+ }
+ else /* E_PRECISION */
+ {
+ exponent_bits = 8;
+ mantissa_bits = 31;
+ }
+
+ shift = mantissa_bits - shift;
+
+ smant = 0;
+ mover = 0;
+ rbit = 0;
+ /* Store the mantissa data into smant and the roundbit into rbit */
+ for (p = flonum.leader; p >= flonum.low && shift > -16; p--)
+ {
+ tmp = shift >= 0 ? *p << shift : *p >> -shift;
+ rbit = shift < 0 ? ((*p >> (-shift-1)) & 0x1) : 0;
+ smant |= tmp;
+ shift -= 16;
+ }
+
+ /* OK, we've got our scaled mantissa so let's round it up */
+ if(rbit)
+ {
+ /* If the mantissa is going to overflow when added, lets store
+ the extra bit in mover. -- A special case exists when
+ mantissa_bits is 31 (E_PRECISION). Then the first test cannot
+ be trusted, as result is host-dependent, thus the second
+ test. */
+ if( smant == ((unsigned)(1<<(mantissa_bits+1))-1)
+ || smant == (unsigned)-1 ) /* This is to catch E_PRECISION cases */
+ mover=1;
+ smant++;
+ }
+
+ /* Get the scaled one value */
+ sone = (1 << (mantissa_bits));
+
+ /* The number may be unnormalised so renormalise it... */
+ if(mover)
+ {
+ smant >>= 1;
+ smant |= sone; /* Insert the bit from mover into smant */
+ exponent++;
+ }
+
+ /* The binary point is now between bit positions 11 and 10 or 23 and 22,
+ i.e., between mantissa_bits - 1 and mantissa_bits - 2 and the
+ bit at mantissa_bits - 1 should be set. */
+ if (!(sone&smant))
+ abort (); /* Ooops. */
+
+ if (flonum.sign == '+')
+ sfract = smant - sone; /* smant - 1.0. */
+ else
+ {
+ /* This seems to work. */
+ if (smant == sone)
+ {
+ exponent--;
+ sfract = 0;
+ }
+ else
+ {
+ sfract = -smant & (sone-1); /* 2.0 - smant. */
+ }
+ sfract |= sone; /* Insert sign bit. */
+ }
+
+ if (abs (exponent) >= (1 << (exponent_bits - 1)))
+ as_bad (_("Cannot represent exponent in %d bits"), exponent_bits);
+
+ /* Force exponent to fit in desired field width. */
+ exponent &= (1 << (exponent_bits)) - 1;
+
+ if (precision == E_PRECISION)
+ {
+ /* Map the float part first (100% equal format as F_PRECISION) */
+ words[0] = exponent << (mantissa_bits+1-24);
+ words[0] |= sfract >> 24;
+ words[1] = sfract >> 8;
+
+ /* Map the mantissa in the next */
+ words[2] = sfract >> 16;
+ words[3] = sfract & 0xffff;
+ }
+ else
+ {
+ /* Insert the exponent data into the word */
+ sfract |= exponent << (mantissa_bits+1);
+
+ if (precision == S_PRECISION)
+ words[0] = sfract;
+ else
+ {
+ words[0] = sfract >> 16;
+ words[1] = sfract & 0xffff;
+ }
+ }
+
+ return return_value;
+}
+
+/* Returns pointer past text consumed. */
+static char *
+tic4x_atof (char *str, char what_kind, LITTLENUM_TYPE *words)
+{
+ /* Extra bits for zeroed low-order bits. The 1st MAX_PRECISION are
+ zeroed, the last contain flonum bits. */
+ static LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD];
+ char *return_value;
+ /* Number of 16-bit words in the format. */
+ int precision;
+ FLONUM_TYPE save_gen_flonum;
+
+ /* We have to save the generic_floating_point_number because it
+ contains storage allocation about the array of LITTLENUMs where
+ the value is actually stored. We will allocate our own array of
+ littlenums below, but have to restore the global one on exit. */
+ save_gen_flonum = generic_floating_point_number;
+
+ return_value = str;
+ generic_floating_point_number.low = bits + MAX_PRECISION;
+ generic_floating_point_number.high = NULL;
+ generic_floating_point_number.leader = NULL;
+ generic_floating_point_number.exponent = 0;
+ generic_floating_point_number.sign = '\0';
+
+ /* Use more LittleNums than seems necessary: the highest flonum may
+ have 15 leading 0 bits, so could be useless. */
+
+ memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION);
+
+ switch (what_kind)
+ {
+ case 's':
+ case 'S':
+ precision = S_PRECISION;
+ break;
+
+ case 'd':
+ case 'D':
+ case 'f':
+ case 'F':
+ precision = F_PRECISION;
+ break;
+
+ case 'E':
+ case 'e':
+ precision = E_PRECISION;
+ break;
+
+ default:
+ as_bad (_("Invalid floating point number"));
+ return (NULL);
+ }
+
+ generic_floating_point_number.high
+ = generic_floating_point_number.low + precision - 1 + GUARD;
+
+ if (atof_generic (&return_value, ".", EXP_CHARS,
+ &generic_floating_point_number))
+ {
+ as_bad (_("Invalid floating point number"));
+ return (NULL);
+ }
+
+ tic4x_gen_to_words (generic_floating_point_number,
+ words, precision);
+
+ /* Restore the generic_floating_point_number's storage alloc (and
+ everything else). */
+ generic_floating_point_number = save_gen_flonum;
+
+ return return_value;
+}
+
+static void
+tic4x_insert_reg (char *regname, int regnum)
+{
+ char buf[32];
+ int i;
+
+ symbol_table_insert (symbol_new (regname, reg_section, (valueT) regnum,
+ &zero_address_frag));
+ for (i = 0; regname[i]; i++)
+ buf[i] = ISLOWER (regname[i]) ? TOUPPER (regname[i]) : regname[i];
+ buf[i] = '\0';
+
+ symbol_table_insert (symbol_new (buf, reg_section, (valueT) regnum,
+ &zero_address_frag));
+}
+
+static void
+tic4x_insert_sym (char *symname, int value)
+{
+ symbolS *symbolP;
+
+ symbolP = symbol_new (symname, absolute_section,
+ (valueT) value, &zero_address_frag);
+ SF_SET_LOCAL (symbolP);
+ symbol_table_insert (symbolP);
+}
+
+static char *
+tic4x_expression (char *str, expressionS *exp)
+{
+ char *s;
+ char *t;
+
+ t = input_line_pointer; /* Save line pointer. */
+ input_line_pointer = str;
+ expression (exp);
+ s = input_line_pointer;
+ input_line_pointer = t; /* Restore line pointer. */
+ return s; /* Return pointer to where parsing stopped. */
+}
+
+static char *
+tic4x_expression_abs (char *str, offsetT *value)
+{
+ char *s;
+ char *t;
+
+ t = input_line_pointer; /* Save line pointer. */
+ input_line_pointer = str;
+ *value = get_absolute_expression ();
+ s = input_line_pointer;
+ input_line_pointer = t; /* Restore line pointer. */
+ return s;
+}
+
+static void
+tic4x_emit_char (char c, int b)
+{
+ expressionS exp;
+
+ exp.X_op = O_constant;
+ exp.X_add_number = c;
+ emit_expr (&exp, b);
+}
+
+static void
+tic4x_seg_alloc (char *name ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ int size,
+ symbolS *symbolP)
+{
+ /* Note that the size is in words
+ so we multiply it by 4 to get the number of bytes to allocate. */
+
+ /* If we have symbol: .usect ".fred", size etc.,
+ the symbol needs to point to the first location reserved
+ by the pseudo op. */
+
+ if (size)
+ {
+ char *p;
+
+ p = frag_var (rs_fill, 1, 1, (relax_substateT) 0,
+ (symbolS *) symbolP,
+ size * OCTETS_PER_BYTE, (char *) 0);
+ *p = 0;
+ }
+}
+
+/* .asg ["]character-string["], symbol */
+static void
+tic4x_asg (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *name;
+ char *str;
+ char *tmp;
+
+ SKIP_WHITESPACE ();
+ str = input_line_pointer;
+
+ /* Skip string expression. */
+ while (*input_line_pointer != ',' && *input_line_pointer)
+ input_line_pointer++;
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Comma expected\n"));
+ return;
+ }
+ *input_line_pointer++ = '\0';
+ name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ tmp = xmalloc (strlen (str) + 1);
+ strcpy (tmp, str);
+ str = tmp;
+ tmp = xmalloc (strlen (name) + 1);
+ strcpy (tmp, name);
+ name = tmp;
+ if (hash_find (tic4x_asg_hash, name))
+ hash_replace (tic4x_asg_hash, name, (void *) str);
+ else
+ hash_insert (tic4x_asg_hash, name, (void *) str);
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* .bss symbol, size */
+static void
+tic4x_bss (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *name;
+ char *p;
+ offsetT size;
+ segT current_seg;
+ subsegT current_subseg;
+ symbolS *symbolP;
+
+ current_seg = now_seg; /* Save current seg. */
+ current_subseg = now_subseg; /* Save current subseg. */
+
+ SKIP_WHITESPACE ();
+ name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ if (c != ',')
+ {
+ as_bad (_(".bss size argument missing\n"));
+ return;
+ }
+
+ input_line_pointer =
+ tic4x_expression_abs (++input_line_pointer, &size);
+ if (size < 0)
+ {
+ as_bad (_(".bss size %ld < 0!"), (long) size);
+ return;
+ }
+ subseg_set (bss_section, 0);
+ symbolP = symbol_find_or_make (name);
+
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+
+ symbol_set_frag (symbolP, frag_now);
+
+ p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ size * OCTETS_PER_BYTE, (char *) 0);
+ *p = 0; /* Fill char. */
+
+ S_SET_SEGMENT (symbolP, bss_section);
+
+ /* The symbol may already have been created with a preceding
+ ".globl" directive -- be careful not to step on storage class
+ in that case. Otherwise, set it to static. */
+ if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+
+ subseg_set (current_seg, current_subseg); /* Restore current seg. */
+ demand_empty_rest_of_line ();
+}
+
+static void
+tic4x_globl (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+ *input_line_pointer = c;
+ SKIP_WHITESPACE ();
+ S_SET_STORAGE_CLASS (symbolP, C_EXT);
+ S_SET_EXTERNAL (symbolP);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\n')
+ c = '\n';
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* Handle .byte, .word. .int, .long */
+static void
+tic4x_cons (int bytes)
+{
+ register unsigned int c;
+ do
+ {
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ {
+ input_line_pointer++;
+ while (is_a_char (c = next_char_of_string ()))
+ tic4x_emit_char (c, 4);
+ know (input_line_pointer[-1] == '\"');
+ }
+ else
+ {
+ expressionS exp;
+
+ input_line_pointer = tic4x_expression (input_line_pointer, &exp);
+ if (exp.X_op == O_constant)
+ {
+ switch (bytes)
+ {
+ case 1:
+ exp.X_add_number &= 255;
+ break;
+ case 2:
+ exp.X_add_number &= 65535;
+ break;
+ }
+ }
+ /* Perhaps we should disallow .byte and .hword with
+ a non constant expression that will require relocation. */
+ emit_expr (&exp, 4);
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+/* Handle .ascii, .asciz, .string */
+static void
+tic4x_stringer (int append_zero)
+{
+ int bytes;
+ register unsigned int c;
+
+ bytes = 0;
+ do
+ {
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ {
+ input_line_pointer++;
+ while (is_a_char (c = next_char_of_string ()))
+ {
+ tic4x_emit_char (c, 1);
+ bytes++;
+ }
+
+ if (append_zero)
+ {
+ tic4x_emit_char (c, 1);
+ bytes++;
+ }
+
+ know (input_line_pointer[-1] == '\"');
+ }
+ else
+ {
+ expressionS exp;
+
+ input_line_pointer = tic4x_expression (input_line_pointer, &exp);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("Non-constant symbols not allowed\n"));
+ return;
+ }
+ exp.X_add_number &= 255; /* Limit numeber to 8-bit */
+ emit_expr (&exp, 1);
+ bytes++;
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ /* Fill out the rest of the expression with 0's to fill up a full word */
+ if ( bytes&0x3 )
+ tic4x_emit_char (0, 4-(bytes&0x3));
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+/* .eval expression, symbol */
+static void
+tic4x_eval (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ offsetT value;
+ char *name;
+
+ SKIP_WHITESPACE ();
+ input_line_pointer =
+ tic4x_expression_abs (input_line_pointer, &value);
+ if (*input_line_pointer++ != ',')
+ {
+ as_bad (_("Symbol missing\n"));
+ return;
+ }
+ name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ tic4x_insert_sym (name, value);
+ *input_line_pointer++ = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Reset local labels. */
+static void
+tic4x_newblock (int x ATTRIBUTE_UNUSED)
+{
+ dollar_label_clear ();
+}
+
+/* .sect "section-name" [, value] */
+/* .sect ["]section-name[:subsection-name]["] [, value] */
+static void
+tic4x_sect (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *section_name;
+ char *name;
+ segT seg;
+ offsetT num;
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ input_line_pointer++;
+ section_name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ input_line_pointer++; /* Skip null symbol terminator. */
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ /* TI C from version 5.0 allows a section name to contain a
+ subsection name as well. The subsection name is separated by a
+ ':' from the section name. Currently we scan the subsection
+ name and discard it.
+ Volker Kuhlmann <v.kuhlmann@elec.canterbury.ac.nz>. */
+ if (c == ':')
+ {
+ c = get_symbol_end (); /* Get terminator. */
+ input_line_pointer++; /* Skip null symbol terminator. */
+ as_warn (_(".sect: subsection name ignored"));
+ }
+
+ /* We might still have a '"' to discard, but the character after a
+ symbol name will be overwritten with a \0 by get_symbol_end()
+ [VK]. */
+
+ if (c == ',')
+ input_line_pointer =
+ tic4x_expression_abs (input_line_pointer, &num);
+ else if (*input_line_pointer == ',')
+ {
+ input_line_pointer =
+ tic4x_expression_abs (++input_line_pointer, &num);
+ }
+ else
+ num = 0;
+
+ seg = subseg_new (name, num);
+ if (line_label != NULL)
+ {
+ S_SET_SEGMENT (line_label, seg);
+ symbol_set_frag (line_label, frag_now);
+ }
+
+ if (bfd_get_section_flags (stdoutput, seg) == SEC_NO_FLAGS)
+ {
+ if (!bfd_set_section_flags (stdoutput, seg, SEC_DATA))
+ as_warn (_("Error setting flags for \"%s\": %s"), name,
+ bfd_errmsg (bfd_get_error ()));
+ }
+
+ /* If the last character overwritten by get_symbol_end() was an
+ end-of-line, we must restore it or the end of the line will not be
+ recognised and scanning extends into the next line, stopping with
+ an error (blame Volker Kuhlmann <v.kuhlmann@elec.canterbury.ac.nz>
+ if this is not true). */
+ if (is_end_of_line[(unsigned char) c])
+ *(--input_line_pointer) = c;
+
+ demand_empty_rest_of_line ();
+}
+
+/* symbol[:] .set value or .set symbol, value */
+static void
+tic4x_set (int x ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP;
+
+ SKIP_WHITESPACE ();
+ if ((symbolP = line_label) == NULL)
+ {
+ char c;
+ char *name;
+
+ name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ if (c != ',')
+ {
+ as_bad (_(".set syntax invalid\n"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+ symbolP = symbol_find_or_make (name);
+ }
+ else
+ symbol_table_insert (symbolP);
+
+ pseudo_set (symbolP);
+ demand_empty_rest_of_line ();
+}
+
+/* [symbol] .usect ["]section-name["], size-in-words [, alignment-flag] */
+static void
+tic4x_usect (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *name;
+ char *section_name;
+ segT seg;
+ offsetT size, alignment_flag;
+ segT current_seg;
+ subsegT current_subseg;
+
+ current_seg = now_seg; /* save current seg. */
+ current_subseg = now_subseg; /* save current subseg. */
+
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '"')
+ input_line_pointer++;
+ section_name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ input_line_pointer++; /* Skip null symbol terminator. */
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ if (c == ',')
+ input_line_pointer =
+ tic4x_expression_abs (input_line_pointer, &size);
+ else if (*input_line_pointer == ',')
+ {
+ input_line_pointer =
+ tic4x_expression_abs (++input_line_pointer, &size);
+ }
+ else
+ size = 0;
+
+ /* Read a possibly present third argument (alignment flag) [VK]. */
+ if (*input_line_pointer == ',')
+ {
+ input_line_pointer =
+ tic4x_expression_abs (++input_line_pointer, &alignment_flag);
+ }
+ else
+ alignment_flag = 0;
+ if (alignment_flag)
+ as_warn (_(".usect: non-zero alignment flag ignored"));
+
+ seg = subseg_new (name, 0);
+ if (line_label != NULL)
+ {
+ S_SET_SEGMENT (line_label, seg);
+ symbol_set_frag (line_label, frag_now);
+ S_SET_VALUE (line_label, frag_now_fix ());
+ }
+ seg_info (seg)->bss = 1; /* Uninitialised data. */
+ if (!bfd_set_section_flags (stdoutput, seg, SEC_ALLOC))
+ as_warn (_("Error setting flags for \"%s\": %s"), name,
+ bfd_errmsg (bfd_get_error ()));
+ tic4x_seg_alloc (name, seg, size, line_label);
+
+ if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
+ S_SET_STORAGE_CLASS (line_label, C_STAT);
+
+ subseg_set (current_seg, current_subseg); /* Restore current seg. */
+ demand_empty_rest_of_line ();
+}
+
+/* .version cpu-version. */
+static void
+tic4x_version (int x ATTRIBUTE_UNUSED)
+{
+ offsetT temp;
+
+ input_line_pointer =
+ tic4x_expression_abs (input_line_pointer, &temp);
+ if (!IS_CPU_TIC3X (temp) && !IS_CPU_TIC4X (temp))
+ as_bad (_("This assembler does not support processor generation %ld"),
+ (long) temp);
+
+ if (tic4x_cpu && temp != (offsetT) tic4x_cpu)
+ as_warn (_("Changing processor generation on fly not supported..."));
+ tic4x_cpu = temp;
+ demand_empty_rest_of_line ();
+}
+
+static void
+tic4x_init_regtable (void)
+{
+ unsigned int i;
+
+ for (i = 0; i < tic3x_num_registers; i++)
+ tic4x_insert_reg (tic3x_registers[i].name,
+ tic3x_registers[i].regno);
+
+ if (IS_CPU_TIC4X (tic4x_cpu))
+ {
+ /* Add additional Tic4x registers, overriding some C3x ones. */
+ for (i = 0; i < tic4x_num_registers; i++)
+ tic4x_insert_reg (tic4x_registers[i].name,
+ tic4x_registers[i].regno);
+ }
+}
+
+static void
+tic4x_init_symbols (void)
+{
+ /* The TI tools accept case insensitive versions of these symbols,
+ we don't !
+
+ For TI C/Asm 5.0
+
+ .TMS320xx 30,31,32,40,or 44 set according to -v flag
+ .C3X or .C3x 1 or 0 1 if -v30,-v31,or -v32
+ .C30 1 or 0 1 if -v30
+ .C31 1 or 0 1 if -v31
+ .C32 1 or 0 1 if -v32
+ .C4X or .C4x 1 or 0 1 if -v40, or -v44
+ .C40 1 or 0 1 if -v40
+ .C44 1 or 0 1 if -v44
+
+ .REGPARM 1 or 0 1 if -mr option used
+ .BIGMODEL 1 or 0 1 if -mb option used
+
+ These symbols are currently supported but will be removed in a
+ later version:
+ .TMS320C30 1 or 0 1 if -v30,-v31,or -v32
+ .TMS320C31 1 or 0 1 if -v31
+ .TMS320C32 1 or 0 1 if -v32
+ .TMS320C40 1 or 0 1 if -v40, or -v44
+ .TMS320C44 1 or 0 1 if -v44
+
+ Source: TI: TMS320C3x/C4x Assembly Language Tools User's Guide,
+ 1997, SPRU035C, p. 3-17/3-18. */
+ tic4x_insert_sym (".REGPARM", tic4x_reg_args);
+ tic4x_insert_sym (".MEMPARM", !tic4x_reg_args);
+ tic4x_insert_sym (".BIGMODEL", tic4x_big_model);
+ tic4x_insert_sym (".C30INTERRUPT", 0);
+ tic4x_insert_sym (".TMS320xx", tic4x_cpu == 0 ? 40 : tic4x_cpu);
+ tic4x_insert_sym (".C3X", tic4x_cpu == 30 || tic4x_cpu == 31 || tic4x_cpu == 32 || tic4x_cpu == 33);
+ tic4x_insert_sym (".C3x", tic4x_cpu == 30 || tic4x_cpu == 31 || tic4x_cpu == 32 || tic4x_cpu == 33);
+ tic4x_insert_sym (".C4X", tic4x_cpu == 0 || tic4x_cpu == 40 || tic4x_cpu == 44);
+ tic4x_insert_sym (".C4x", tic4x_cpu == 0 || tic4x_cpu == 40 || tic4x_cpu == 44);
+ /* Do we need to have the following symbols also in lower case? */
+ tic4x_insert_sym (".TMS320C30", tic4x_cpu == 30 || tic4x_cpu == 31 || tic4x_cpu == 32 || tic4x_cpu == 33);
+ tic4x_insert_sym (".tms320C30", tic4x_cpu == 30 || tic4x_cpu == 31 || tic4x_cpu == 32 || tic4x_cpu == 33);
+ tic4x_insert_sym (".TMS320C31", tic4x_cpu == 31);
+ tic4x_insert_sym (".tms320C31", tic4x_cpu == 31);
+ tic4x_insert_sym (".TMS320C32", tic4x_cpu == 32);
+ tic4x_insert_sym (".tms320C32", tic4x_cpu == 32);
+ tic4x_insert_sym (".TMS320C33", tic4x_cpu == 33);
+ tic4x_insert_sym (".tms320C33", tic4x_cpu == 33);
+ tic4x_insert_sym (".TMS320C40", tic4x_cpu == 40 || tic4x_cpu == 44 || tic4x_cpu == 0);
+ tic4x_insert_sym (".tms320C40", tic4x_cpu == 40 || tic4x_cpu == 44 || tic4x_cpu == 0);
+ tic4x_insert_sym (".TMS320C44", tic4x_cpu == 44);
+ tic4x_insert_sym (".tms320C44", tic4x_cpu == 44);
+ tic4x_insert_sym (".TMX320C40", 0); /* C40 first pass silicon ? */
+ tic4x_insert_sym (".tmx320C40", 0);
+}
+
+/* Insert a new instruction template into hash table. */
+static int
+tic4x_inst_insert (const tic4x_inst_t *inst)
+{
+ static char prev_name[16];
+ const char *retval = NULL;
+
+ /* Only insert the first name if have several similar entries. */
+ if (!strcmp (inst->name, prev_name) || inst->name[0] == '\0')
+ return 1;
+
+ retval = hash_insert (tic4x_op_hash, inst->name, (void *) inst);
+ if (retval != NULL)
+ fprintf (stderr, "internal error: can't hash `%s': %s\n",
+ inst->name, retval);
+ else
+ strcpy (prev_name, inst->name);
+ return retval == NULL;
+}
+
+/* Make a new instruction template. */
+static tic4x_inst_t *
+tic4x_inst_make (char *name, unsigned long opcode, char *args)
+{
+ static tic4x_inst_t *insts = NULL;
+ static char *names = NULL;
+ static int iindex = 0;
+
+ if (insts == NULL)
+ {
+ /* Allocate memory to store name strings. */
+ names = (char *) xmalloc (sizeof (char) * 8192);
+ /* Allocate memory for additional insts. */
+ insts = (tic4x_inst_t *)
+ xmalloc (sizeof (tic4x_inst_t) * 1024);
+ }
+ insts[iindex].name = names;
+ insts[iindex].opcode = opcode;
+ insts[iindex].opmask = 0xffffffff;
+ insts[iindex].args = args;
+ iindex++;
+
+ do
+ *names++ = *name++;
+ while (*name);
+ *names++ = '\0';
+
+ return &insts[iindex - 1];
+}
+
+/* Add instruction template, creating dynamic templates as required. */
+static int
+tic4x_inst_add (const tic4x_inst_t *insts)
+{
+ char *s = insts->name;
+ char *d;
+ unsigned int i;
+ int ok = 1;
+ char name[16];
+
+ d = name;
+
+ /* We do not care about INSNs that is not a part of our
+ oplevel setting. */
+ if ((insts->oplevel & tic4x_oplevel) == 0)
+ return ok;
+
+ while (1)
+ {
+ switch (*s)
+ {
+ case 'B':
+ case 'C':
+ /* Dynamically create all the conditional insts. */
+ for (i = 0; i < tic4x_num_conds; i++)
+ {
+ tic4x_inst_t *inst;
+ int k = 0;
+ char *c = tic4x_conds[i].name;
+ char *e = d;
+
+ while (*c)
+ *e++ = *c++;
+ c = s + 1;
+ while (*c)
+ *e++ = *c++;
+ *e = '\0';
+
+ /* If instruction found then have already processed it. */
+ if (hash_find (tic4x_op_hash, name))
+ return 1;
+
+ do
+ {
+ inst = tic4x_inst_make (name, insts[k].opcode +
+ (tic4x_conds[i].cond <<
+ (*s == 'B' ? 16 : 23)),
+ insts[k].args);
+ if (k == 0) /* Save strcmp() with following func. */
+ ok &= tic4x_inst_insert (inst);
+ k++;
+ }
+ while (!strcmp (insts->name,
+ insts[k].name));
+ }
+ return ok;
+ break;
+
+ case '\0':
+ return tic4x_inst_insert (insts);
+ break;
+
+ default:
+ *d++ = *s++;
+ break;
+ }
+ }
+}
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc., that the MD part of the assembler will
+ need. */
+void
+md_begin (void)
+{
+ int ok = 1;
+ unsigned int i;
+
+ /* Setup the proper opcode level according to the
+ commandline parameters */
+ tic4x_oplevel = OP_C3X;
+
+ if ( IS_CPU_TIC4X(tic4x_cpu) )
+ tic4x_oplevel |= OP_C4X;
+
+ if ( ( tic4x_cpu == 31 && tic4x_revision >= 6)
+ || (tic4x_cpu == 32 && tic4x_revision >= 2)
+ || (tic4x_cpu == 33)
+ || tic4x_enhanced )
+ tic4x_oplevel |= OP_ENH;
+
+ if ( ( tic4x_cpu == 30 && tic4x_revision >= 7)
+ || (tic4x_cpu == 31 && tic4x_revision >= 5)
+ || (tic4x_cpu == 32)
+ || tic4x_lowpower )
+ tic4x_oplevel |= OP_LPWR;
+
+ if ( ( tic4x_cpu == 30 && tic4x_revision >= 7)
+ || (tic4x_cpu == 31 && tic4x_revision >= 5)
+ || (tic4x_cpu == 32)
+ || (tic4x_cpu == 33)
+ || (tic4x_cpu == 40 && tic4x_revision >= 5)
+ || (tic4x_cpu == 44)
+ || tic4x_idle2 )
+ tic4x_oplevel |= OP_IDLE2;
+
+ /* Create hash table for mnemonics. */
+ tic4x_op_hash = hash_new ();
+
+ /* Create hash table for asg pseudo. */
+ tic4x_asg_hash = hash_new ();
+
+ /* Add mnemonics to hash table, expanding conditional mnemonics on fly. */
+ for (i = 0; i < tic4x_num_insts; i++)
+ ok &= tic4x_inst_add (tic4x_insts + i);
+
+ /* Create dummy inst to avoid errors accessing end of table. */
+ tic4x_inst_make ("", 0, "");
+
+ if (!ok)
+ as_fatal ("Broken assembler. No assembly attempted.");
+
+ /* Add registers to symbol table. */
+ tic4x_init_regtable ();
+
+ /* Add predefined symbols to symbol table. */
+ tic4x_init_symbols ();
+}
+
+void
+tic4x_end (void)
+{
+ bfd_set_arch_mach (stdoutput, bfd_arch_tic4x,
+ IS_CPU_TIC4X (tic4x_cpu) ? bfd_mach_tic4x : bfd_mach_tic3x);
+}
+
+static int
+tic4x_indirect_parse (tic4x_operand_t *operand,
+ const tic4x_indirect_t *indirect)
+{
+ char *n = indirect->name;
+ char *s = input_line_pointer;
+ char *b;
+ symbolS *symbolP;
+ char name[32];
+
+ operand->disp = 0;
+ for (; *n; n++)
+ {
+ switch (*n)
+ {
+ case 'a': /* Need to match aux register. */
+ b = name;
+#ifdef TIC4X_ALT_SYNTAX
+ if (*s == '%')
+ s++;
+#endif
+ while (ISALNUM (*s))
+ *b++ = *s++;
+ *b++ = '\0';
+ if (!(symbolP = symbol_find (name)))
+ return 0;
+
+ if (S_GET_SEGMENT (symbolP) != reg_section)
+ return 0;
+
+ operand->aregno = S_GET_VALUE (symbolP);
+ if (operand->aregno >= REG_AR0 && operand->aregno <= REG_AR7)
+ break;
+
+ as_bad (_("Auxiliary register AR0--AR7 required for indirect"));
+ return -1;
+
+ case 'd': /* Need to match constant for disp. */
+#ifdef TIC4X_ALT_SYNTAX
+ if (*s == '%') /* expr() will die if we don't skip this. */
+ s++;
+#endif
+ s = tic4x_expression (s, &operand->expr);
+ if (operand->expr.X_op != O_constant)
+ return 0;
+ operand->disp = operand->expr.X_add_number;
+ if (operand->disp < 0 || operand->disp > 255)
+ {
+ as_bad (_("Bad displacement %d (require 0--255)\n"),
+ operand->disp);
+ return -1;
+ }
+ break;
+
+ case 'y': /* Need to match IR0. */
+ case 'z': /* Need to match IR1. */
+#ifdef TIC4X_ALT_SYNTAX
+ if (*s == '%')
+ s++;
+#endif
+ s = tic4x_expression (s, &operand->expr);
+ if (operand->expr.X_op != O_register)
+ return 0;
+ if (operand->expr.X_add_number != REG_IR0
+ && operand->expr.X_add_number != REG_IR1)
+ {
+ as_bad (_("Index register IR0,IR1 required for displacement"));
+ return -1;
+ }
+
+ if (*n == 'y' && operand->expr.X_add_number == REG_IR0)
+ break;
+ if (*n == 'z' && operand->expr.X_add_number == REG_IR1)
+ break;
+ return 0;
+
+ case '(':
+ if (*s != '(') /* No displacement, assume to be 1. */
+ {
+ operand->disp = 1;
+ while (*n != ')')
+ n++;
+ }
+ else
+ s++;
+ break;
+
+ default:
+ if (TOLOWER (*s) != *n)
+ return 0;
+ s++;
+ }
+ }
+ if (*s != ' ' && *s != ',' && *s != '\0')
+ return 0;
+ input_line_pointer = s;
+ return 1;
+}
+
+static char *
+tic4x_operand_parse (char *s, tic4x_operand_t *operand)
+{
+ unsigned int i;
+ char c;
+ int ret;
+ expressionS *exp = &operand->expr;
+ char *save = input_line_pointer;
+ char *str;
+ char *new_pointer;
+ struct hash_entry *entry = NULL;
+
+ input_line_pointer = s;
+ SKIP_WHITESPACE ();
+
+ str = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ new_pointer = input_line_pointer;
+ if (strlen (str) && (entry = hash_find (tic4x_asg_hash, str)) != NULL)
+ {
+ *input_line_pointer = c;
+ input_line_pointer = (char *) entry;
+ }
+ else
+ {
+ *input_line_pointer = c;
+ input_line_pointer = str;
+ }
+
+ operand->mode = M_UNKNOWN;
+ switch (*input_line_pointer)
+ {
+#ifdef TIC4X_ALT_SYNTAX
+ case '%':
+ input_line_pointer = tic4x_expression (++input_line_pointer, exp);
+ if (exp->X_op != O_register)
+ as_bad (_("Expecting a register name"));
+ operand->mode = M_REGISTER;
+ break;
+
+ case '^':
+ /* Denotes high 16 bits. */
+ input_line_pointer = tic4x_expression (++input_line_pointer, exp);
+ if (exp->X_op == O_constant)
+ operand->mode = M_IMMED;
+ else if (exp->X_op == O_big)
+ {
+ if (exp->X_add_number)
+ as_bad (_("Number too large")); /* bignum required */
+ else
+ {
+ tic4x_gen_to_words (generic_floating_point_number,
+ operand->fwords, S_PRECISION);
+ operand->mode = M_IMMED_F;
+ }
+ }
+ /* Allow ori ^foo, ar0 to be equivalent to ldi .hi.foo, ar0 */
+ /* WARNING : The TI C40 assembler cannot do this. */
+ else if (exp->X_op == O_symbol)
+ {
+ operand->mode = M_HI;
+ break;
+ }
+
+ case '#':
+ input_line_pointer = tic4x_expression (++input_line_pointer, exp);
+ if (exp->X_op == O_constant)
+ operand->mode = M_IMMED;
+ else if (exp->X_op == O_big)
+ {
+ if (exp->X_add_number > 0)
+ as_bad (_("Number too large")); /* bignum required. */
+ else
+ {
+ tic4x_gen_to_words (generic_floating_point_number,
+ operand->fwords, S_PRECISION);
+ operand->mode = M_IMMED_F;
+ }
+ }
+ /* Allow ori foo, ar0 to be equivalent to ldi .lo.foo, ar0 */
+ /* WARNING : The TI C40 assembler cannot do this. */
+ else if (exp->X_op == O_symbol)
+ {
+ operand->mode = M_IMMED;
+ break;
+ }
+
+ else
+ as_bad (_("Expecting a constant value"));
+ break;
+ case '\\':
+#endif
+ case '@':
+ input_line_pointer = tic4x_expression (++input_line_pointer, exp);
+ if (exp->X_op != O_constant && exp->X_op != O_symbol)
+ as_bad (_("Bad direct addressing construct %s"), s);
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number < 0)
+ as_bad (_("Direct value of %ld is not suitable"),
+ (long) exp->X_add_number);
+ }
+ operand->mode = M_DIRECT;
+ break;
+
+ case '*':
+ ret = -1;
+ for (i = 0; i < tic4x_num_indirects; i++)
+ if ((ret = tic4x_indirect_parse (operand, &tic4x_indirects[i])))
+ break;
+ if (ret < 0)
+ break;
+ if (i < tic4x_num_indirects)
+ {
+ operand->mode = M_INDIRECT;
+ /* Indirect addressing mode number. */
+ operand->expr.X_add_number = tic4x_indirects[i].modn;
+ /* Convert *+ARn(0) to *ARn etc. Maybe we should
+ squeal about silly ones? */
+ if (operand->expr.X_add_number < 0x08 && !operand->disp)
+ operand->expr.X_add_number = 0x18;
+ }
+ else
+ as_bad (_("Unknown indirect addressing mode"));
+ break;
+
+ default:
+ operand->mode = M_IMMED; /* Assume immediate. */
+ str = input_line_pointer;
+ input_line_pointer = tic4x_expression (input_line_pointer, exp);
+ if (exp->X_op == O_register)
+ {
+ know (exp->X_add_symbol == 0);
+ know (exp->X_op_symbol == 0);
+ operand->mode = M_REGISTER;
+ break;
+ }
+ else if (exp->X_op == O_big)
+ {
+ if (exp->X_add_number > 0)
+ as_bad (_("Number too large")); /* bignum required. */
+ else
+ {
+ tic4x_gen_to_words (generic_floating_point_number,
+ operand->fwords, S_PRECISION);
+ operand->mode = M_IMMED_F;
+ }
+ break;
+ }
+#ifdef TIC4X_ALT_SYNTAX
+ /* Allow ldi foo, ar0 to be equivalent to ldi @foo, ar0. */
+ else if (exp->X_op == O_symbol)
+ {
+ operand->mode = M_DIRECT;
+ break;
+ }
+#endif
+ }
+ if (entry == NULL)
+ new_pointer = input_line_pointer;
+ input_line_pointer = save;
+ return new_pointer;
+}
+
+static int
+tic4x_operands_match (tic4x_inst_t *inst, tic4x_insn_t *tinsn, int check)
+{
+ const char *args = inst->args;
+ unsigned long opcode = inst->opcode;
+ int num_operands = tinsn->num_operands;
+ tic4x_operand_t *operand = tinsn->operands;
+ expressionS *exp = &operand->expr;
+ int ret = 1;
+ int reg;
+
+ /* Build the opcode, checking as we go to make sure that the
+ operands match.
+
+ If an operand matches, we modify insn or opcode appropriately,
+ and do a "continue". If an operand fails to match, we "break". */
+
+ tinsn->nchars = 4; /* Instructions always 4 bytes. */
+ tinsn->reloc = NO_RELOC;
+ tinsn->pcrel = 0;
+
+ if (*args == '\0')
+ {
+ tinsn->opcode = opcode;
+ return num_operands == 0;
+ }
+
+ for (;; ++args)
+ {
+ switch (*args)
+ {
+
+ case '\0': /* End of args. */
+ if (num_operands == 1)
+ {
+ tinsn->opcode = opcode;
+ return ret;
+ }
+ break; /* Too many operands. */
+
+ case '#': /* This is only used for ldp. */
+ if (operand->mode != M_DIRECT && operand->mode != M_IMMED)
+ break;
+ /* While this looks like a direct addressing mode, we actually
+ use an immediate mode form of ldiu or ldpk instruction. */
+ if (exp->X_op == O_constant)
+ {
+ if( ( IS_CPU_TIC4X (tic4x_cpu) && exp->X_add_number <= 65535 )
+ || ( IS_CPU_TIC3X (tic4x_cpu) && exp->X_add_number <= 255 ) )
+ {
+ INSERTS (opcode, exp->X_add_number, 15, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Immediate value of %ld is too large for ldf"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ else if (exp->X_op == O_symbol)
+ {
+ tinsn->reloc = BFD_RELOC_HI16;
+ tinsn->exp = *exp;
+ continue;
+ }
+ break; /* Not direct (dp) addressing. */
+
+ case '@': /* direct. */
+ if (operand->mode != M_DIRECT)
+ break;
+ if (exp->X_op == O_constant)
+ {
+ /* Store only the 16 LSBs of the number. */
+ INSERTS (opcode, exp->X_add_number, 15, 0);
+ continue;
+ }
+ else if (exp->X_op == O_symbol)
+ {
+ tinsn->reloc = BFD_RELOC_LO16;
+ tinsn->exp = *exp;
+ continue;
+ }
+ break; /* Not direct addressing. */
+
+ case 'A':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_AR0 && reg <= REG_AR7)
+ INSERTU (opcode, reg - REG_AR0, 24, 22);
+ else
+ {
+ if (!check)
+ as_bad (_("Destination register must be ARn"));
+ ret = -1;
+ }
+ continue;
+
+ case 'B': /* Unsigned integer immediate. */
+ /* Allow br label or br @label. */
+ if (operand->mode != M_IMMED && operand->mode != M_DIRECT)
+ break;
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number < (1 << 24))
+ {
+ INSERTU (opcode, exp->X_add_number, 23, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Immediate value of %ld is too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ if (IS_CPU_TIC4X (tic4x_cpu))
+ {
+ tinsn->reloc = BFD_RELOC_24_PCREL;
+ tinsn->pcrel = 1;
+ }
+ else
+ {
+ tinsn->reloc = BFD_RELOC_24;
+ tinsn->pcrel = 0;
+ }
+ tinsn->exp = *exp;
+ continue;
+
+ case 'C':
+ if (!IS_CPU_TIC4X (tic4x_cpu))
+ break;
+ if (operand->mode != M_INDIRECT)
+ break;
+ /* Require either *+ARn(disp) or *ARn. */
+ if (operand->expr.X_add_number != 0
+ && operand->expr.X_add_number != 0x18)
+ {
+ if (!check)
+ as_bad (_("Invalid indirect addressing mode"));
+ ret = -1;
+ continue;
+ }
+ INSERTU (opcode, operand->aregno - REG_AR0, 2, 0);
+ INSERTU (opcode, operand->disp, 7, 3);
+ continue;
+
+ case 'E':
+ if (!(operand->mode == M_REGISTER))
+ break;
+ INSERTU (opcode, exp->X_add_number, 7, 0);
+ continue;
+
+ case 'e':
+ if (!(operand->mode == M_REGISTER))
+ break;
+ reg = exp->X_add_number;
+ if ( (reg >= REG_R0 && reg <= REG_R7)
+ || (IS_CPU_TIC4X (tic4x_cpu) && reg >= REG_R8 && reg <= REG_R11) )
+ INSERTU (opcode, reg, 7, 0);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be Rn"));
+ ret = -1;
+ }
+ continue;
+
+ case 'F':
+ if (operand->mode != M_IMMED_F
+ && !(operand->mode == M_IMMED && exp->X_op == O_constant))
+ break;
+
+ if (operand->mode != M_IMMED_F)
+ {
+ /* OK, we 've got something like cmpf 0, r0
+ Why can't they stick in a bloody decimal point ?! */
+ char string[16];
+
+ /* Create floating point number string. */
+ sprintf (string, "%d.0", (int) exp->X_add_number);
+ tic4x_atof (string, 's', operand->fwords);
+ }
+
+ INSERTU (opcode, operand->fwords[0], 15, 0);
+ continue;
+
+ case 'G':
+ if (operand->mode != M_REGISTER)
+ break;
+ INSERTU (opcode, exp->X_add_number, 15, 8);
+ continue;
+
+ case 'g':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if ( (reg >= REG_R0 && reg <= REG_R7)
+ || (IS_CPU_TIC4X (tic4x_cpu) && reg >= REG_R8 && reg <= REG_R11) )
+ INSERTU (opcode, reg, 15, 8);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be Rn"));
+ ret = -1;
+ }
+ continue;
+
+ case 'H':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_R0 && reg <= REG_R7)
+ INSERTU (opcode, reg - REG_R0, 18, 16);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be R0--R7"));
+ ret = -1;
+ }
+ continue;
+
+ case 'i':
+ if ( operand->mode == M_REGISTER
+ && tic4x_oplevel & OP_ENH )
+ {
+ reg = exp->X_add_number;
+ INSERTU (opcode, reg, 4, 0);
+ INSERTU (opcode, 7, 7, 5);
+ continue;
+ }
+ /* Fallthrough */
+
+ case 'I':
+ if (operand->mode != M_INDIRECT)
+ break;
+ if (operand->disp != 0 && operand->disp != 1)
+ {
+ if (IS_CPU_TIC4X (tic4x_cpu))
+ break;
+ if (!check)
+ as_bad (_("Invalid indirect addressing mode displacement %d"),
+ operand->disp);
+ ret = -1;
+ continue;
+ }
+ INSERTU (opcode, operand->aregno - REG_AR0, 2, 0);
+ INSERTU (opcode, operand->expr.X_add_number, 7, 3);
+ continue;
+
+ case 'j':
+ if ( operand->mode == M_REGISTER
+ && tic4x_oplevel & OP_ENH )
+ {
+ reg = exp->X_add_number;
+ INSERTU (opcode, reg, 12, 8);
+ INSERTU (opcode, 7, 15, 13);
+ continue;
+ }
+ /* Fallthrough */
+
+ case 'J':
+ if (operand->mode != M_INDIRECT)
+ break;
+ if (operand->disp != 0 && operand->disp != 1)
+ {
+ if (IS_CPU_TIC4X (tic4x_cpu))
+ break;
+ if (!check)
+ as_bad (_("Invalid indirect addressing mode displacement %d"),
+ operand->disp);
+ ret = -1;
+ continue;
+ }
+ INSERTU (opcode, operand->aregno - REG_AR0, 10, 8);
+ INSERTU (opcode, operand->expr.X_add_number, 15, 11);
+ continue;
+
+ case 'K':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_R0 && reg <= REG_R7)
+ INSERTU (opcode, reg - REG_R0, 21, 19);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be R0--R7"));
+ ret = -1;
+ }
+ continue;
+
+ case 'L':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_R0 && reg <= REG_R7)
+ INSERTU (opcode, reg - REG_R0, 24, 22);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be R0--R7"));
+ ret = -1;
+ }
+ continue;
+
+ case 'M':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg == REG_R2 || reg == REG_R3)
+ INSERTU (opcode, reg - REG_R2, 22, 22);
+ else
+ {
+ if (!check)
+ as_bad (_("Destination register must be R2 or R3"));
+ ret = -1;
+ }
+ continue;
+
+ case 'N':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg == REG_R0 || reg == REG_R1)
+ INSERTU (opcode, reg - REG_R0, 23, 23);
+ else
+ {
+ if (!check)
+ as_bad (_("Destination register must be R0 or R1"));
+ ret = -1;
+ }
+ continue;
+
+ case 'O':
+ if (!IS_CPU_TIC4X (tic4x_cpu))
+ break;
+ if (operand->mode != M_INDIRECT)
+ break;
+ /* Require either *+ARn(disp) or *ARn. */
+ if (operand->expr.X_add_number != 0
+ && operand->expr.X_add_number != 0x18)
+ {
+ if (!check)
+ as_bad (_("Invalid indirect addressing mode"));
+ ret = -1;
+ continue;
+ }
+ INSERTU (opcode, operand->aregno - REG_AR0, 10, 8);
+ INSERTU (opcode, operand->disp, 15, 11);
+ continue;
+
+ case 'P': /* PC relative displacement. */
+ /* Allow br label or br @label. */
+ if (operand->mode != M_IMMED && operand->mode != M_DIRECT)
+ break;
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number >= -32768 && exp->X_add_number <= 32767)
+ {
+ INSERTS (opcode, exp->X_add_number, 15, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Displacement value of %ld is too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ tinsn->reloc = BFD_RELOC_16_PCREL;
+ tinsn->pcrel = 1;
+ tinsn->exp = *exp;
+ continue;
+
+ case 'Q':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ INSERTU (opcode, reg, 15, 0);
+ continue;
+
+ case 'q':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if ( (reg >= REG_R0 && reg <= REG_R7)
+ || (IS_CPU_TIC4X (tic4x_cpu) && reg >= REG_R8 && reg <= REG_R11) )
+ INSERTU (opcode, reg, 15, 0);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be Rn"));
+ ret = -1;
+ }
+ continue;
+
+ case 'R':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ INSERTU (opcode, reg, 20, 16);
+ continue;
+
+ case 'r':
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if ( (reg >= REG_R0 && reg <= REG_R7)
+ || (IS_CPU_TIC4X (tic4x_cpu) && reg >= REG_R8 && reg <= REG_R11) )
+ INSERTU (opcode, reg, 20, 16);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be Rn"));
+ ret = -1;
+ }
+ continue;
+
+ case 'S': /* Short immediate int. */
+ if (operand->mode != M_IMMED && operand->mode != M_HI)
+ break;
+ if (exp->X_op == O_big)
+ {
+ if (!check)
+ as_bad (_("Floating point number not valid in expression"));
+ ret = -1;
+ continue;
+ }
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number >= -32768 && exp->X_add_number <= 65535)
+ {
+ INSERTS (opcode, exp->X_add_number, 15, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Signed immediate value %ld too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ else if (exp->X_op == O_symbol)
+ {
+ if (operand->mode == M_HI)
+ {
+ tinsn->reloc = BFD_RELOC_HI16;
+ }
+ else
+ {
+ tinsn->reloc = BFD_RELOC_LO16;
+ }
+ tinsn->exp = *exp;
+ continue;
+ }
+ /* Handle cases like ldi foo - $, ar0 where foo
+ is a forward reference. Perhaps we should check
+ for X_op == O_symbol and disallow things like
+ ldi foo, ar0. */
+ tinsn->reloc = BFD_RELOC_16;
+ tinsn->exp = *exp;
+ continue;
+
+ case 'T': /* 5-bit immediate value for tic4x stik. */
+ if (!IS_CPU_TIC4X (tic4x_cpu))
+ break;
+ if (operand->mode != M_IMMED)
+ break;
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number < 16 && exp->X_add_number >= -16)
+ {
+ INSERTS (opcode, exp->X_add_number, 20, 16);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Immediate value of %ld is too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ break; /* No relocations allowed. */
+
+ case 'U': /* Unsigned integer immediate. */
+ if (operand->mode != M_IMMED && operand->mode != M_HI)
+ break;
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number < (1 << 16) && exp->X_add_number >= 0)
+ {
+ INSERTU (opcode, exp->X_add_number, 15, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Unsigned immediate value %ld too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ else if (exp->X_op == O_symbol)
+ {
+ if (operand->mode == M_HI)
+ tinsn->reloc = BFD_RELOC_HI16;
+ else
+ tinsn->reloc = BFD_RELOC_LO16;
+
+ tinsn->exp = *exp;
+ continue;
+ }
+ tinsn->reloc = BFD_RELOC_16;
+ tinsn->exp = *exp;
+ continue;
+
+ case 'V': /* Trap numbers (immediate field). */
+ if (operand->mode != M_IMMED)
+ break;
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number < 512 && IS_CPU_TIC4X (tic4x_cpu))
+ {
+ INSERTU (opcode, exp->X_add_number, 8, 0);
+ continue;
+ }
+ else if (exp->X_add_number < 32 && IS_CPU_TIC3X (tic4x_cpu))
+ {
+ INSERTU (opcode, exp->X_add_number | 0x20, 4, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Immediate value of %ld is too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ break; /* No relocations allowed. */
+
+ case 'W': /* Short immediate int (0--7). */
+ if (!IS_CPU_TIC4X (tic4x_cpu))
+ break;
+ if (operand->mode != M_IMMED)
+ break;
+ if (exp->X_op == O_big)
+ {
+ if (!check)
+ as_bad (_("Floating point number not valid in expression"));
+ ret = -1;
+ continue;
+ }
+ if (exp->X_op == O_constant)
+ {
+ if (exp->X_add_number >= -256 && exp->X_add_number <= 127)
+ {
+ INSERTS (opcode, exp->X_add_number, 7, 0);
+ continue;
+ }
+ else
+ {
+ if (!check)
+ as_bad (_("Immediate value %ld too large"),
+ (long) exp->X_add_number);
+ ret = -1;
+ continue;
+ }
+ }
+ tinsn->reloc = BFD_RELOC_16;
+ tinsn->exp = *exp;
+ continue;
+
+ case 'X': /* Expansion register for tic4x. */
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_IVTP && reg <= REG_TVTP)
+ INSERTU (opcode, reg - REG_IVTP, 4, 0);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be ivtp or tvtp"));
+ ret = -1;
+ }
+ continue;
+
+ case 'Y': /* Address register for tic4x lda. */
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_AR0 && reg <= REG_SP)
+ INSERTU (opcode, reg, 20, 16);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be address register"));
+ ret = -1;
+ }
+ continue;
+
+ case 'Z': /* Expansion register for tic4x. */
+ if (operand->mode != M_REGISTER)
+ break;
+ reg = exp->X_add_number;
+ if (reg >= REG_IVTP && reg <= REG_TVTP)
+ INSERTU (opcode, reg - REG_IVTP, 20, 16);
+ else
+ {
+ if (!check)
+ as_bad (_("Register must be ivtp or tvtp"));
+ ret = -1;
+ }
+ continue;
+
+ case '*':
+ if (operand->mode != M_INDIRECT)
+ break;
+ INSERTS (opcode, operand->disp, 7, 0);
+ INSERTU (opcode, operand->aregno - REG_AR0, 10, 8);
+ INSERTU (opcode, operand->expr.X_add_number, 15, 11);
+ continue;
+
+ case '|': /* treat as `,' if have ldi_ldi form. */
+ if (tinsn->parallel)
+ {
+ if (--num_operands < 0)
+ break; /* Too few operands. */
+ operand++;
+ if (operand->mode != M_PARALLEL)
+ break;
+ }
+ /* Fall through. */
+
+ case ',': /* Another operand. */
+ if (--num_operands < 0)
+ break; /* Too few operands. */
+ operand++;
+ exp = &operand->expr;
+ continue;
+
+ case ';': /* Another optional operand. */
+ if (num_operands == 1 || operand[1].mode == M_PARALLEL)
+ continue;
+ if (--num_operands < 0)
+ break; /* Too few operands. */
+ operand++;
+ exp = &operand->expr;
+ continue;
+
+ default:
+ BAD_CASE (*args);
+ }
+ return 0;
+ }
+}
+
+static void
+tic4x_insn_check (tic4x_insn_t *tinsn)
+{
+
+ if (!strcmp (tinsn->name, "lda"))
+ {
+ if (tinsn->num_operands < 2 || tinsn->num_operands > 2)
+ as_fatal ("Illegal internal LDA insn definition");
+
+ if (tinsn->operands[0].mode == M_REGISTER
+ && tinsn->operands[1].mode == M_REGISTER
+ && tinsn->operands[0].expr.X_add_number == tinsn->operands[1].expr.X_add_number )
+ as_bad (_("Source and destination register should not be equal"));
+ }
+ else if (!strcmp (tinsn->name, "ldi_ldi")
+ || !strcmp (tinsn->name, "ldi1_ldi2")
+ || !strcmp (tinsn->name, "ldi2_ldi1")
+ || !strcmp (tinsn->name, "ldf_ldf")
+ || !strcmp (tinsn->name, "ldf1_ldf2")
+ || !strcmp (tinsn->name, "ldf2_ldf1") )
+ {
+ if (tinsn->num_operands < 4 && tinsn->num_operands > 5 )
+ as_fatal ("Illegal internal %s insn definition", tinsn->name);
+
+ if (tinsn->operands[1].mode == M_REGISTER
+ && tinsn->operands[tinsn->num_operands-1].mode == M_REGISTER
+ && tinsn->operands[1].expr.X_add_number == tinsn->operands[tinsn->num_operands-1].expr.X_add_number )
+ as_warn (_("Equal parallell destination registers, one result will be discarded"));
+ }
+}
+
+static void
+tic4x_insn_output (tic4x_insn_t *tinsn)
+{
+ char *dst;
+
+ /* Grab another fragment for opcode. */
+ dst = frag_more (tinsn->nchars);
+
+ /* Put out opcode word as a series of bytes in little endian order. */
+ md_number_to_chars (dst, tinsn->opcode, tinsn->nchars);
+
+ /* Put out the symbol-dependent stuff. */
+ if (tinsn->reloc != NO_RELOC)
+ {
+ /* Where is the offset into the fragment for this instruction. */
+ fix_new_exp (frag_now,
+ dst - frag_now->fr_literal, /* where */
+ tinsn->nchars, /* size */
+ &tinsn->exp,
+ tinsn->pcrel,
+ tinsn->reloc);
+ }
+}
+
+/* Parse the operands. */
+static int
+tic4x_operands_parse (char *s, tic4x_operand_t *operands, int num_operands)
+{
+ if (!*s)
+ return num_operands;
+
+ do
+ s = tic4x_operand_parse (s, &operands[num_operands++]);
+ while (num_operands < TIC4X_OPERANDS_MAX && *s++ == ',');
+
+ if (num_operands > TIC4X_OPERANDS_MAX)
+ {
+ as_bad (_("Too many operands scanned"));
+ return -1;
+ }
+ return num_operands;
+}
+
+/* Assemble a single instruction. Its label has already been handled
+ by the generic front end. We just parse mnemonic and operands, and
+ produce the bytes of data and relocation. */
+void
+md_assemble (char *str)
+{
+ int ok = 0;
+ char *s;
+ int i;
+ int parsed = 0;
+ tic4x_inst_t *inst; /* Instruction template. */
+ tic4x_inst_t *first_inst;
+
+ /* Scan for parallel operators */
+ if (str)
+ {
+ s = str;
+ while (*s && *s != '|')
+ s++;
+
+ if (*s && s[1]=='|')
+ {
+ if(insn->parallel)
+ {
+ as_bad (_("Parallel opcode cannot contain more than two instructions"));
+ insn->parallel = 0;
+ insn->in_use = 0;
+ return;
+ }
+
+ /* Lets take care of the first part of the parallel insn */
+ *s++ = 0;
+ md_assemble(str);
+ insn->parallel = 1;
+ str = ++s;
+ /* .. and let the second run though here */
+ }
+ }
+
+ if (str && insn->parallel)
+ {
+ /* Find mnemonic (second part of parallel instruction). */
+ s = str;
+ /* Skip past instruction mnemonic. */
+ while (*s && *s != ' ')
+ s++;
+ if (*s) /* Null terminate for hash_find. */
+ *s++ = '\0'; /* and skip past null. */
+ strcat (insn->name, "_");
+ strncat (insn->name, str, TIC4X_NAME_MAX - 1 - strlen (insn->name));
+
+ insn->operands[insn->num_operands++].mode = M_PARALLEL;
+
+ if ((i = tic4x_operands_parse
+ (s, insn->operands, insn->num_operands)) < 0)
+ {
+ insn->parallel = 0;
+ insn->in_use = 0;
+ return;
+ }
+ insn->num_operands = i;
+ parsed = 1;
+ }
+
+ if (insn->in_use)
+ {
+ if ((insn->inst = (struct tic4x_inst *)
+ hash_find (tic4x_op_hash, insn->name)) == NULL)
+ {
+ as_bad (_("Unknown opcode `%s'."), insn->name);
+ insn->parallel = 0;
+ insn->in_use = 0;
+ return;
+ }
+
+ inst = insn->inst;
+ first_inst = NULL;
+ do
+ {
+ ok = tic4x_operands_match (inst, insn, 1);
+ if (ok < 0)
+ {
+ if (!first_inst)
+ first_inst = inst;
+ ok = 0;
+ }
+ } while (!ok && !strcmp (inst->name, inst[1].name) && inst++);
+
+ if (ok > 0)
+ {
+ tic4x_insn_check (insn);
+ tic4x_insn_output (insn);
+ }
+ else if (!ok)
+ {
+ if (first_inst)
+ tic4x_operands_match (first_inst, insn, 0);
+ as_bad (_("Invalid operands for %s"), insn->name);
+ }
+ else
+ as_bad (_("Invalid instruction %s"), insn->name);
+ }
+
+ if (str && !parsed)
+ {
+ /* Find mnemonic. */
+ s = str;
+ while (*s && *s != ' ') /* Skip past instruction mnemonic. */
+ s++;
+ if (*s) /* Null terminate for hash_find. */
+ *s++ = '\0'; /* and skip past null. */
+ strncpy (insn->name, str, TIC4X_NAME_MAX - 3);
+
+ if ((i = tic4x_operands_parse (s, insn->operands, 0)) < 0)
+ {
+ insn->inst = NULL; /* Flag that error occurred. */
+ insn->parallel = 0;
+ insn->in_use = 0;
+ return;
+ }
+ insn->num_operands = i;
+ insn->in_use = 1;
+ }
+ else
+ insn->in_use = 0;
+ insn->parallel = 0;
+}
+
+void
+tic4x_cleanup (void)
+{
+ if (insn->in_use)
+ md_assemble (NULL);
+}
+
+/* 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 chars emitted is stored in *sizeP. An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ int ieee;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 's': /* .single */
+ case 'S':
+ ieee = 0;
+ prec = 1;
+ break;
+
+ case 'd': /* .double */
+ case 'D':
+ case 'f': /* .float */
+ case 'F':
+ ieee = 0;
+ prec = 2; /* 1 32-bit word */
+ break;
+
+ case 'i': /* .ieee */
+ case 'I':
+ prec = 2;
+ ieee = 1;
+ type = 'f'; /* Rewrite type to be usable by atof_ieee(). */
+ break;
+
+ case 'e': /* .ldouble */
+ case 'E':
+ prec = 4; /* 2 32-bit words */
+ ieee = 0;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("Unrecognized or unsupported floating point constant");
+ }
+
+ if (ieee)
+ t = atof_ieee (input_line_pointer, type, words);
+ else
+ t = tic4x_atof (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * sizeof (LITTLENUM_TYPE);
+
+ /* This loops outputs the LITTLENUMs in REVERSE order; in accord with
+ little endian byte order. */
+ /* SES: However it is required to put the words (32-bits) out in the
+ correct order, hence we write 2 and 2 littlenums in little endian
+ order, while we keep the original order on successive words. */
+ for (wordP = words; wordP<(words+prec) ; wordP+=2)
+ {
+ if (wordP < (words + prec - 1)) /* Dump wordP[1] (if we have one). */
+ {
+ md_number_to_chars (litP, (valueT) (wordP[1]),
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+
+ /* Dump wordP[0] */
+ md_number_to_chars (litP, (valueT) (wordP[0]),
+ sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return NULL;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *value, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ valueT val = *value;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_HI16:
+ val >>= 16;
+ break;
+
+ case BFD_RELOC_LO16:
+ val &= 0xffff;
+ break;
+ default:
+ break;
+ }
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ buf[3] = val >> 24;
+ case BFD_RELOC_24:
+ case BFD_RELOC_24_PCREL:
+ buf[2] = val >> 16;
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_HI16:
+ buf[1] = val >> 8;
+ buf[0] = val;
+ break;
+
+ case NO_RELOC:
+ default:
+ as_bad (_("Bad relocation type: 0x%02x"), fixP->fx_r_type);
+ break;
+ }
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0) fixP->fx_done = 1;
+}
+
+/* Should never be called for tic4x. */
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ as_fatal ("md_convert_frag");
+}
+
+/* Should never be called for tic4x. */
+void
+md_create_short_jump (char *ptr ATTRIBUTE_UNUSED,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ as_fatal ("md_create_short_jmp\n");
+}
+
+/* Should never be called for tic4x. */
+void
+md_create_long_jump (char *ptr ATTRIBUTE_UNUSED,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ as_fatal ("md_create_long_jump\n");
+}
+
+/* Should never be called for tic4x. */
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segtype ATTRIBUTE_UNUSED)
+{
+ as_fatal ("md_estimate_size_before_relax\n");
+ return 0;
+}
+
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_CPU: /* cpu brand */
+ if (TOLOWER (*arg) == 'c')
+ arg++;
+ tic4x_cpu = atoi (arg);
+ if (!IS_CPU_TIC3X (tic4x_cpu) && !IS_CPU_TIC4X (tic4x_cpu))
+ as_warn (_("Unsupported processor generation %d"), tic4x_cpu);
+ break;
+
+ case OPTION_REV: /* cpu revision */
+ tic4x_revision = atoi (arg);
+ break;
+
+ case 'b':
+ as_warn (_("Option -b is depreciated, please use -mbig"));
+ case OPTION_BIG: /* big model */
+ tic4x_big_model = 1;
+ break;
+
+ case 'p':
+ as_warn (_("Option -p is depreciated, please use -mmemparm"));
+ case OPTION_MEMPARM: /* push args */
+ tic4x_reg_args = 0;
+ break;
+
+ case 'r':
+ as_warn (_("Option -r is depreciated, please use -mregparm"));
+ case OPTION_REGPARM: /* register args */
+ tic4x_reg_args = 1;
+ break;
+
+ case 's':
+ as_warn (_("Option -s is depreciated, please use -msmall"));
+ case OPTION_SMALL: /* small model */
+ tic4x_big_model = 0;
+ break;
+
+ case OPTION_IDLE2:
+ tic4x_idle2 = 1;
+ break;
+
+ case OPTION_LOWPOWER:
+ tic4x_lowpower = 1;
+ break;
+
+ case OPTION_ENHANCED:
+ tic4x_enhanced = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream,
+ _("\nTIC4X options:\n"
+ " -mcpu=CPU -mCPU select architecture variant. CPU can be:\n"
+ " 30 - TMS320C30\n"
+ " 31 - TMS320C31, TMS320LC31\n"
+ " 32 - TMS320C32\n"
+ " 33 - TMS320VC33\n"
+ " 40 - TMS320C40\n"
+ " 44 - TMS320C44\n"
+ " -mrev=REV set cpu hardware revision (integer numbers).\n"
+ " Combinations of -mcpu and -mrev will enable/disable\n"
+ " the appropriate options (-midle2, -mlowpower and\n"
+ " -menhanced) according to the selected type\n"
+ " -mbig select big memory model\n"
+ " -msmall select small memory model (default)\n"
+ " -mregparm select register parameters (default)\n"
+ " -mmemparm select memory parameters\n"
+ " -midle2 enable IDLE2 support\n"
+ " -mlowpower enable LOPOWER and MAXSPEED support\n"
+ " -menhanced enable enhanced opcode support\n"));
+}
+
+/* This is called when a line is unrecognized. This is used to handle
+ definitions of TI C3x tools style local labels $n where n is a single
+ decimal digit. */
+int
+tic4x_unrecognized_line (int c)
+{
+ int lab;
+ char *s;
+
+ if (c != '$' || ! ISDIGIT (input_line_pointer[0]))
+ return 0;
+
+ s = input_line_pointer;
+
+ /* Let's allow multiple digit local labels. */
+ lab = 0;
+ while (ISDIGIT (*s))
+ {
+ lab = lab * 10 + *s - '0';
+ s++;
+ }
+
+ if (dollar_label_defined (lab))
+ {
+ as_bad (_("Label \"$%d\" redefined"), lab);
+ return 0;
+ }
+
+ define_dollar_label (lab);
+ colon (dollar_label_name (lab, 0));
+ input_line_pointer = s + 1;
+
+ return 1;
+}
+
+/* Handle local labels peculiar to us referred to in an expression. */
+symbolS *
+md_undefined_symbol (char *name)
+{
+ /* Look for local labels of the form $n. */
+ if (name[0] == '$' && ISDIGIT (name[1]))
+ {
+ symbolS *symbolP;
+ char *s = name + 1;
+ int lab = 0;
+
+ while (ISDIGIT ((unsigned char) *s))
+ {
+ lab = lab * 10 + *s - '0';
+ s++;
+ }
+ if (dollar_label_defined (lab))
+ {
+ name = dollar_label_name (lab, 0);
+ symbolP = symbol_find (name);
+ }
+ else
+ {
+ name = dollar_label_name (lab, 1);
+ symbolP = symbol_find_or_make (name);
+ }
+
+ return symbolP;
+ }
+ return NULL;
+}
+
+/* Parse an operand that is machine-specific. */
+void
+md_operand (expressionS *expressionP ATTRIBUTE_UNUSED)
+{
+}
+
+/* Round up a section size to the appropriate boundary---do we need this? */
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ return size; /* Byte (i.e., 32-bit) alignment is fine? */
+}
+
+static int
+tic4x_pc_offset (unsigned int op)
+{
+ /* Determine the PC offset for a C[34]x instruction.
+ This could be simplified using some boolean algebra
+ but at the expense of readability. */
+ switch (op >> 24)
+ {
+ case 0x60: /* br */
+ case 0x62: /* call (C4x) */
+ case 0x64: /* rptb (C4x) */
+ return 1;
+ case 0x61: /* brd */
+ case 0x63: /* laj */
+ case 0x65: /* rptbd (C4x) */
+ return 3;
+ case 0x66: /* swi */
+ case 0x67:
+ return 0;
+ default:
+ break;
+ }
+
+ switch ((op & 0xffe00000) >> 20)
+ {
+ case 0x6a0: /* bB */
+ case 0x720: /* callB */
+ case 0x740: /* trapB */
+ return 1;
+
+ case 0x6a2: /* bBd */
+ case 0x6a6: /* bBat */
+ case 0x6aa: /* bBaf */
+ case 0x722: /* lajB */
+ case 0x748: /* latB */
+ case 0x798: /* rptbd */
+ return 3;
+
+ default:
+ break;
+ }
+
+ switch ((op & 0xfe200000) >> 20)
+ {
+ case 0x6e0: /* dbB */
+ return 1;
+
+ case 0x6e2: /* dbBd */
+ return 3;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ With the C3x we have the following:
+ DBcond, Bcond disp + PC + 1 => PC
+ DBcondD, BcondD disp + PC + 3 => PC
+ */
+long
+md_pcrel_from (fixS *fixP)
+{
+ unsigned char *buf;
+ unsigned int op;
+
+ buf = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+ op = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+
+ return ((fixP->fx_where + fixP->fx_frag->fr_address) >> 2) +
+ tic4x_pc_offset (op);
+}
+
+/* Fill the alignment area with NOP's on .text, unless fill-data
+ was specified. */
+int
+tic4x_do_align (int alignment,
+ const char *fill,
+ int len,
+ int max)
+{
+ /* Because we are talking lwords, not bytes, adjust alignment to do words */
+ alignment += 2;
+
+ if (alignment != 0 && !need_pass_2)
+ {
+ if (fill == NULL)
+ {
+ if (subseg_text_p (now_seg))
+ {
+ char nop[4];
+
+ md_number_to_chars (nop, TIC_NOP_OPCODE, 4);
+ frag_align_pattern (alignment, nop, sizeof (nop), max);
+ }
+ else
+ frag_align (alignment, 0, max);
+ }
+ else if (len <= 1)
+ frag_align (alignment, *fill, max);
+ else
+ frag_align_pattern (alignment, fill, len, max);
+ }
+
+ /* Return 1 to skip the default alignment function */
+ return 1;
+}
+
+/* Look for and remove parallel instruction operator ||. */
+void
+tic4x_start_line (void)
+{
+ char *s = input_line_pointer;
+
+ SKIP_WHITESPACE ();
+
+ /* If parallel instruction prefix found at start of line, skip it. */
+ if (*input_line_pointer == '|' && input_line_pointer[1] == '|')
+ {
+ if (insn->in_use)
+ {
+ insn->parallel = 1;
+ input_line_pointer ++;
+ *input_line_pointer = ' ';
+ /* So line counters get bumped. */
+ input_line_pointer[-1] = '\n';
+ }
+ }
+ else
+ {
+ /* Write out the previous insn here */
+ if (insn->in_use)
+ md_assemble (NULL);
+ input_line_pointer = s;
+ }
+}
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixP)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
+ reloc->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ reloc->address /= OCTETS_PER_BYTE;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Reloc %d not supported by object file format"),
+ (int) fixP->fx_r_type);
+ return NULL;
+ }
+
+ if (fixP->fx_r_type == BFD_RELOC_HI16)
+ reloc->addend = fixP->fx_offset;
+ else
+ reloc->addend = fixP->fx_addnumber;
+
+ return reloc;
+}
diff --git a/gas/config/tc-tic4x.h b/gas/config/tc-tic4x.h
new file mode 100644
index 0000000..14b3511
--- /dev/null
+++ b/gas/config/tc-tic4x.h
@@ -0,0 +1,93 @@
+/* tc-tic4x.h -- Assemble for the Texas TMS320C[34]X.
+ Copyright (C) 1997-2014 Free Software Foundation, Inc.
+
+ Contributed by Michael P. Hayes (m.hayes@elec.canterbury.ac.nz)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_TIC4X
+#define TIC4X
+
+#define TARGET_ARCH bfd_arch_tic4x
+
+#define WORKING_DOT_WORD
+
+/* There are a number of different formats used for local labels. gas
+ expects local labels either of the form `10$:' or `n:', where n is
+ a single digit. When LOCAL_LABEL_DOLLARS is defined labels of the
+ form `10$:' are expected. When LOCAL_LABEL_FB is defined labels of
+ the form `n:' are expected. The latter are expected to be referred
+ to using `nf' for a forward reference of `nb' for a backward
+ reference.
+
+ The local labels expected by the TI tools are of the form `$n:',
+ where the colon is optional. Now the $ character is considered to
+ be valid symbol name character, so gas doesn't recognise our local
+ symbols by default. Defining LEX_DOLLAR to be 1 means that gas
+ won't allow labels starting with $ and thus the hook
+ tc_unrecognized_line() will be called from read.c. We can thus
+ parse lines starting with $n as having local labels.
+
+ The other problem is the forward reference of local labels. If a
+ symbol is undefined, symbol_make() calls the md_undefined_symbol()
+ hook where we create a local label if recognised. */
+
+/* Don't stick labels starting with 'L' into symbol table of COFF file. */
+#define LOCAL_LABEL(name) ((name)[0] == '$' || (name)[0] == 'L')
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+#define OCTETS_PER_BYTE_POWER 2
+
+#define TARGET_ARCH bfd_arch_tic4x
+
+#define TIC_NOP_OPCODE 0x0c800000
+
+#define reloc_type int
+
+#define NO_RELOC 0
+
+/* '||' denotes parallel instruction */
+#define DOUBLEBAR_PARALLEL 1
+
+/* Labels are not required to have a colon for a suffix. */
+#define LABELS_WITHOUT_COLONS 1
+
+/* Use $ as the section program counter (SPC). */
+#define DOLLAR_DOT
+
+/* Accept numbers with a suffix, e.g. 0ffffh, 1010b. */
+#define NUMBERS_WITH_SUFFIX 1
+
+extern int tic4x_unrecognized_line (int);
+#define tc_unrecognized_line(c) tic4x_unrecognized_line (c)
+
+#define md_number_to_chars number_to_chars_littleendian
+
+extern int tic4x_do_align (int, const char *, int, int);
+#define md_do_align(n,fill,len,max,label) if( tic4x_do_align (n,fill,len,max) ) goto label;
+
+/* Start of line hook to remove parallel instruction operator || */
+extern void tic4x_start_line (void);
+#define md_start_line_hook() tic4x_start_line()
+
+extern void tic4x_cleanup (void);
+#define md_cleanup() tic4x_cleanup()
+
+extern void tic4x_end (void);
+#define md_end() tic4x_end()
+
diff --git a/gas/config/tc-tic54x.c b/gas/config/tc-tic54x.c
new file mode 100644
index 0000000..c997297
--- /dev/null
+++ b/gas/config/tc-tic54x.c
@@ -0,0 +1,5408 @@
+/* tc-tic54x.c -- Assembly code for the Texas Instruments TMS320C54X
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Contributed by Timothy Wall (twall@cygnus.com)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Texas Instruments TMS320C54X machine specific gas.
+ Written by Timothy Wall (twall@alum.mit.edu).
+
+ Valuable things to do:
+ Pipeline conflict warnings
+ We encode/decode "ld #_label, dp" differently in relocatable files
+ This means we're not compatible with TI output containing those
+ expressions. We store the upper nine bits; TI stores the lower nine
+ bits. How they recover the original upper nine bits is beyond me.
+
+ Tests to add to expect testsuite:
+ '=' and '==' with .if, .elseif, and .break
+
+ Incompatibilities (mostly trivial):
+ We don't allow '''
+ We fill text section with zeroes instead of "nop"s
+ We don't convert '' or "" to a single instance
+ We don't convert '' to '\0'
+ We don't allow strings with .byte/.half/.short/.long
+ Probably details of the subsym stuff are different
+ TI sets labels to be data type 4 (T_INT); GAS uses T_NULL.
+
+ COFF1 limits section names to 8 characters.
+ Some of the default behavior changed from COFF1 to COFF2. */
+
+#include "as.h"
+#include <limits.h>
+#include "safe-ctype.h"
+#include "sb.h"
+#include "macro.h"
+#include "subsegs.h"
+#include "struc-symbol.h"
+#include "opcode/tic54x.h"
+#include "obj-coff.h"
+#include <math.h>
+
+
+static struct stag
+{
+ symbolS *sym; /* Symbol for this stag; value is offset. */
+ const char *name; /* Shortcut to symbol name. */
+ bfd_vma size; /* Size of struct/union. */
+ int current_bitfield_offset; /* Temporary for tracking fields. */
+ int is_union;
+ struct stag_field /* List of fields. */
+ {
+ const char *name;
+ bfd_vma offset; /* Of start of this field. */
+ int bitfield_offset; /* Of start of this field. */
+ struct stag *stag; /* If field is struct/union. */
+ struct stag_field *next;
+ } *field;
+ /* For nesting; used only in stag construction. */
+ struct stag *inner; /* Enclosed .struct. */
+ struct stag *outer; /* Enclosing .struct. */
+} *current_stag = NULL;
+
+#define MAX_LINE 256 /* Lines longer than this are truncated by TI's asm. */
+
+typedef struct _tic54x_insn
+{
+ const insn_template *tm; /* Opcode template. */
+
+ char mnemonic[MAX_LINE]; /* Opcode name/mnemonic. */
+ char parmnemonic[MAX_LINE]; /* 2nd mnemonic of parallel insn. */
+
+ int opcount;
+ struct opstruct
+ {
+ char buf[MAX_LINE];
+ enum optype type;
+ expressionS exp;
+ } operands[MAX_OPERANDS];
+
+ int paropcount;
+ struct opstruct paroperands[MAX_OPERANDS];
+
+ int is_lkaddr;
+ int lkoperand;
+ int words; /* Size of insn in 16-bit words. */
+ int using_default_dst; /* Do we need to explicitly set an
+ omitted OP_DST operand? */
+ struct
+ {
+ unsigned short word; /* Final encoded opcode data. */
+ int unresolved;
+ int r_nchars; /* Relocation size. */
+ bfd_reloc_code_real_type r_type; /* Relocation type. */
+ expressionS addr_expr; /* Storage for unresolved expressions. */
+ } opcode[3];
+} tic54x_insn;
+
+enum cpu_version
+{
+ VNONE = 0, V541 = 1, V542 = 2, V543 = 3, V545 = 5, V548 = 8, V549 = 9,
+ V545LP = 15, V546LP = 16
+};
+
+enum address_mode
+{
+ c_mode, /* 16-bit addresses. */
+ far_mode /* >16-bit addresses. */
+};
+
+static segT stag_saved_seg;
+static subsegT stag_saved_subseg;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = ";*#"; /* At column zero only. */
+const char line_separator_chars[] = ""; /* Not permitted. */
+
+int emitting_long = 0;
+
+/* Characters which indicate that this is a floating point constant. */
+const char FLT_CHARS[] = "fF";
+
+/* Characters that can be used to separate mantissa from exp in FP
+ nums. */
+const char EXP_CHARS[] = "eE";
+
+const char *md_shortopts = "";
+
+#define OPTION_ADDRESS_MODE (OPTION_MD_BASE)
+#define OPTION_CPU_VERSION (OPTION_ADDRESS_MODE + 1)
+#define OPTION_COFF_VERSION (OPTION_CPU_VERSION + 1)
+#define OPTION_STDERR_TO_FILE (OPTION_COFF_VERSION + 1)
+
+struct option md_longopts[] =
+{
+ { "mfar-mode", no_argument, NULL, OPTION_ADDRESS_MODE },
+ { "mf", no_argument, NULL, OPTION_ADDRESS_MODE },
+ { "mcpu", required_argument, NULL, OPTION_CPU_VERSION },
+ { "merrors-to-file", required_argument, NULL, OPTION_STDERR_TO_FILE },
+ { "me", required_argument, NULL, OPTION_STDERR_TO_FILE },
+ { NULL, no_argument, NULL, 0},
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+static int assembly_begun = 0;
+/* Addressing mode is not entirely implemented; the latest rev of the Other
+ assembler doesn't seem to make any distinction whatsoever; all relocations
+ are stored as extended relocatiosn. Older versions used REL16 vs RELEXT16,
+ but now it seems all relocations are RELEXT16. We use all RELEXT16.
+
+ The cpu version is kind of a waste of time as well. There is one
+ instruction (RND) for LP devices only, and several for devices with
+ extended addressing only. We include it for compatibility. */
+static enum address_mode amode = c_mode;
+static enum cpu_version cpu = VNONE;
+
+/* Include string substitutions in listing? */
+static int listing_sslist = 0;
+
+/* Did we do subsym substitutions on the line? */
+static int substitution_line = 0;
+
+/* Last label seen. */
+static symbolS *last_label_seen = NULL;
+
+/* This ensures that all new labels are unique. */
+static int local_label_id;
+
+static struct hash_control *subsym_recurse_hash; /* Prevent infinite recurse. */
+static struct hash_control *math_hash; /* Built-in math functions. */
+/* Allow maximum levels of macro nesting; level 0 is the main substitution
+ symbol table. The other assembler only does 32 levels, so there! */
+static struct hash_control *subsym_hash[100];
+
+/* Keep track of local labels so we can substitute them before GAS sees them
+ since macros use their own 'namespace' for local labels, use a separate hash
+
+ We do our own local label handling 'cuz it's subtly different from the
+ stock GAS handling.
+
+ We use our own macro nesting counter, since GAS overloads it when expanding
+ other things (like conditionals and repeat loops). */
+static int macro_level = 0;
+static struct hash_control *local_label_hash[100];
+/* Keep track of struct/union tags. */
+static struct hash_control *stag_hash;
+static struct hash_control *op_hash;
+static struct hash_control *parop_hash;
+static struct hash_control *reg_hash;
+static struct hash_control *mmreg_hash;
+static struct hash_control *cc_hash;
+static struct hash_control *cc2_hash;
+static struct hash_control *cc3_hash;
+static struct hash_control *sbit_hash;
+static struct hash_control *misc_symbol_hash;
+
+/* Only word (et al.), align, or conditionals are allowed within
+ .struct/.union. */
+#define ILLEGAL_WITHIN_STRUCT() \
+ do \
+ if (current_stag != NULL) \
+ { \
+ as_bad (_("pseudo-op illegal within .struct/.union")); \
+ return; \
+ } \
+ while (0)
+
+
+static void subsym_create_or_replace (char *, char *);
+static char *subsym_lookup (char *, int);
+static char *subsym_substitute (char *, int);
+
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("C54x-specific command line options:\n"));
+ fprintf (stream, _("-mfar-mode | -mf Use extended addressing\n"));
+ fprintf (stream, _("-mcpu=<CPU version> Specify the CPU version\n"));
+ fprintf (stream, _("-merrors-to-file <filename>\n"));
+ fprintf (stream, _("-me <filename> Redirect errors to a file\n"));
+}
+
+/* Output a single character (upper octect is zero). */
+
+static void
+tic54x_emit_char (char c)
+{
+ expressionS expn;
+
+ expn.X_op = O_constant;
+ expn.X_add_number = c;
+ emit_expr (&expn, 2);
+}
+
+/* Walk backwards in the frag chain. */
+
+static fragS *
+frag_prev (fragS *frag, segT seg)
+{
+ segment_info_type *seginfo = seg_info (seg);
+ fragS *fragp;
+
+ for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next)
+ if (fragp->fr_next == frag)
+ return fragp;
+
+ return NULL;
+}
+
+static fragS *
+bit_offset_frag (fragS *frag, segT seg)
+{
+ while (frag != NULL)
+ {
+ if (frag->fr_fix == 0
+ && frag->fr_opcode == NULL
+ && frag->tc_frag_data == 0)
+ frag = frag_prev (frag, seg);
+ else
+ return frag;
+ }
+ return NULL;
+}
+
+/* Return the number of bits allocated in the most recent word, or zero if
+ none. .field/.space/.bes may leave words partially allocated. */
+
+static int
+frag_bit_offset (fragS *frag, segT seg)
+{
+ frag = bit_offset_frag (frag, seg);
+
+ if (frag)
+ return frag->fr_opcode != NULL ? -1 : frag->tc_frag_data;
+
+ return 0;
+}
+
+/* Read an expression from a C string; returns a pointer past the end of the
+ expression. */
+
+static char *
+parse_expression (char *str, expressionS *expn)
+{
+ char *s;
+ char *tmp;
+
+ tmp = input_line_pointer; /* Save line pointer. */
+ input_line_pointer = str;
+ expression (expn);
+ s = input_line_pointer;
+ input_line_pointer = tmp; /* Restore line pointer. */
+ return s; /* Return pointer to where parsing stopped. */
+}
+
+/* .asg "character-string"|character-string, symbol
+
+ .eval is the only pseudo-op allowed to perform arithmetic on substitution
+ symbols. all other use of symbols defined with .asg are currently
+ unsupported. */
+
+static void
+tic54x_asg (int x ATTRIBUTE_UNUSED)
+{
+ int c;
+ char *name;
+ char *str;
+ char *tmp;
+ int quoted = *input_line_pointer == '"';
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ if (quoted)
+ {
+ int len;
+ str = demand_copy_C_string (&len);
+ c = *input_line_pointer;
+ }
+ else
+ {
+ str = input_line_pointer;
+ while ((c = *input_line_pointer) != ',')
+ {
+ if (is_end_of_line[(int) *input_line_pointer])
+ break;
+ ++input_line_pointer;
+ }
+ *input_line_pointer = 0;
+ }
+ if (c != ',')
+ {
+ as_bad (_("Comma and symbol expected for '.asg STRING, SYMBOL'"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ name = ++input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ if (!ISALPHA (*name))
+ {
+ as_bad (_("symbols assigned with .asg must begin with a letter"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ tmp = xmalloc (strlen (str) + 1);
+ strcpy (tmp, str);
+ str = tmp;
+ tmp = xmalloc (strlen (name) + 1);
+ strcpy (tmp, name);
+ name = tmp;
+ subsym_create_or_replace (name, str);
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* .eval expression, symbol
+ There's something screwy about this. The other assembler sometimes does and
+ sometimes doesn't substitute symbols defined with .eval.
+ We'll put the symbols into the subsym table as well as the normal symbol
+ table, since that's what works best. */
+
+static void
+tic54x_eval (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ int value;
+ char *name;
+ symbolS *symbolP;
+ char valuestr[32], *tmp;
+ int quoted;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ SKIP_WHITESPACE ();
+
+ quoted = *input_line_pointer == '"';
+ if (quoted)
+ ++input_line_pointer;
+ value = get_absolute_expression ();
+ if (quoted)
+ {
+ if (*input_line_pointer != '"')
+ {
+ as_bad (_("Unterminated string after absolute expression"));
+ ignore_rest_of_line ();
+ return;
+ }
+ ++input_line_pointer;
+ }
+ if (*input_line_pointer++ != ',')
+ {
+ as_bad (_("Comma and symbol expected for '.eval EXPR, SYMBOL'"));
+ ignore_rest_of_line ();
+ return;
+ }
+ name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ tmp = xmalloc (strlen (name) + 1);
+ name = strcpy (tmp, name);
+ *input_line_pointer = c;
+
+ if (!ISALPHA (*name))
+ {
+ as_bad (_("symbols assigned with .eval must begin with a letter"));
+ ignore_rest_of_line ();
+ return;
+ }
+ symbolP = symbol_new (name, absolute_section,
+ (valueT) value, &zero_address_frag);
+ SF_SET_LOCAL (symbolP);
+ symbol_table_insert (symbolP);
+
+ /* The "other" assembler sometimes doesn't put .eval's in the subsym table
+ But since there's not written rule as to when, don't even bother trying
+ to match their behavior. */
+ sprintf (valuestr, "%d", value);
+ tmp = xmalloc (strlen (valuestr) + 1);
+ strcpy (tmp, valuestr);
+ subsym_create_or_replace (name, tmp);
+
+ demand_empty_rest_of_line ();
+}
+
+/* .bss symbol, size [, [blocking flag] [, alignment flag]
+
+ alignment is to a longword boundary; blocking is to 128-word boundary.
+
+ 1) if there is a hole in memory, this directive should attempt to fill it
+ (not yet implemented).
+
+ 2) if the blocking flag is not set, allocate at the current SPC
+ otherwise, check to see if the current SPC plus the space to be
+ allocated crosses the page boundary (128 words).
+ if there's not enough space, create a hole and align with the next page
+ boundary.
+ (not yet implemented). */
+
+static void
+tic54x_bss (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *name;
+ char *p;
+ int words;
+ segT current_seg;
+ subsegT current_subseg;
+ symbolS *symbolP;
+ int block = 0;
+ int align = 0;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ current_seg = now_seg; /* Save current seg. */
+ current_subseg = now_subseg; /* Save current subseg. */
+
+ name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ if (c != ',')
+ {
+ as_bad (_(".bss size argument missing\n"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ ++input_line_pointer;
+ words = get_absolute_expression ();
+ if (words < 0)
+ {
+ as_bad (_(".bss size %d < 0!"), words);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (*input_line_pointer == ',')
+ {
+ /* The blocking flag may be missing. */
+ ++input_line_pointer;
+ if (*input_line_pointer != ',')
+ block = get_absolute_expression ();
+ else
+ block = 0;
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ }
+ else
+ align = 0;
+ }
+ else
+ block = align = 0;
+
+ subseg_set (bss_section, 0);
+ symbolP = symbol_find_or_make (name);
+
+ if (S_GET_SEGMENT (symbolP) == bss_section)
+ symbolP->sy_frag->fr_symbol = (symbolS *) NULL;
+
+ symbol_set_frag (symbolP, frag_now);
+ p = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) (words * OCTETS_PER_BYTE), (char *) 0);
+ *p = 0; /* Fill char. */
+
+ S_SET_SEGMENT (symbolP, bss_section);
+
+ /* The symbol may already have been created with a preceding
+ ".globl" directive -- be careful not to step on storage class
+ in that case. Otherwise, set it to static. */
+ if (S_GET_STORAGE_CLASS (symbolP) != C_EXT)
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+
+ if (align)
+ {
+ /* s_align eats end of line; restore it */
+ s_align_bytes (4);
+ --input_line_pointer;
+ }
+
+ if (block)
+ bss_section->flags |= SEC_TIC54X_BLOCK;
+
+ subseg_set (current_seg, current_subseg); /* Restore current seg. */
+ demand_empty_rest_of_line ();
+}
+
+static void
+stag_add_field_symbols (struct stag *stag,
+ const char *path,
+ bfd_vma base_offset,
+ symbolS *rootsym,
+ const char *root_stag_name)
+{
+ char prefix[strlen (path) + 2];
+ struct stag_field *field = stag->field;
+
+ /* Construct a symbol for every field contained within this structure
+ including fields within structure fields. */
+ strcpy (prefix, path);
+ if (*path)
+ strcat (prefix, ".");
+
+ while (field != NULL)
+ {
+ int len = strlen (prefix) + strlen (field->name) + 2;
+ char *name = xmalloc (len);
+ strcpy (name, prefix);
+ strcat (name, field->name);
+
+ if (rootsym == NULL)
+ {
+ symbolS *sym;
+ sym = symbol_new (name, absolute_section,
+ (field->stag ? field->offset :
+ (valueT) (base_offset + field->offset)),
+ &zero_address_frag);
+ SF_SET_LOCAL (sym);
+ symbol_table_insert (sym);
+ }
+ else
+ {
+ char *replacement = xmalloc (strlen (name)
+ + strlen (stag->name) + 2);
+ strcpy (replacement, S_GET_NAME (rootsym));
+ strcat (replacement, "+");
+ strcat (replacement, root_stag_name);
+ strcat (replacement, name + strlen (S_GET_NAME (rootsym)));
+ hash_insert (subsym_hash[0], name, replacement);
+ }
+
+ /* Recurse if the field is a structure.
+ Note the field offset is relative to the outermost struct. */
+ if (field->stag != NULL)
+ stag_add_field_symbols (field->stag, name,
+ field->offset,
+ rootsym, root_stag_name);
+ field = field->next;
+ }
+}
+
+/* Keep track of stag fields so that when structures are nested we can add the
+ complete dereferencing symbols to the symbol table. */
+
+static void
+stag_add_field (struct stag *parent,
+ const char *name,
+ bfd_vma offset,
+ struct stag *stag)
+{
+ struct stag_field *sfield = xmalloc (sizeof (struct stag_field));
+
+ memset (sfield, 0, sizeof (*sfield));
+ sfield->name = strcpy (xmalloc (strlen (name) + 1), name);
+ sfield->offset = offset;
+ sfield->bitfield_offset = parent->current_bitfield_offset;
+ sfield->stag = stag;
+ if (parent->field == NULL)
+ parent->field = sfield;
+ else
+ {
+ struct stag_field *sf = parent->field;
+ while (sf->next != NULL)
+ sf = sf->next;
+ sf->next = sfield;
+ }
+ /* Only create a symbol for this field if the parent has no name. */
+ if (!strncmp (".fake", parent->name, 5))
+ {
+ symbolS *sym = symbol_new (name, absolute_section,
+ (valueT) offset, &zero_address_frag);
+ SF_SET_LOCAL (sym);
+ symbol_table_insert (sym);
+ }
+}
+
+/* [STAG] .struct [OFFSET]
+ Start defining structure offsets (symbols in absolute section). */
+
+static void
+tic54x_struct (int arg)
+{
+ int start_offset = 0;
+ int is_union = arg;
+
+ if (!current_stag)
+ {
+ /* Starting a new struct, switch to absolute section. */
+ stag_saved_seg = now_seg;
+ stag_saved_subseg = now_subseg;
+ subseg_set (absolute_section, 0);
+ }
+ /* Align the current pointer. */
+ else if (current_stag->current_bitfield_offset != 0)
+ {
+ ++abs_section_offset;
+ current_stag->current_bitfield_offset = 0;
+ }
+
+ /* Offset expression is only meaningful for global .structs. */
+ if (!is_union)
+ {
+ /* Offset is ignored in inner structs. */
+ SKIP_WHITESPACE ();
+ if (!is_end_of_line[(int) *input_line_pointer])
+ start_offset = get_absolute_expression ();
+ else
+ start_offset = 0;
+ }
+
+ if (current_stag)
+ {
+ /* Nesting, link to outer one. */
+ current_stag->inner = (struct stag *) xmalloc (sizeof (struct stag));
+ memset (current_stag->inner, 0, sizeof (struct stag));
+ current_stag->inner->outer = current_stag;
+ current_stag = current_stag->inner;
+ if (start_offset)
+ as_warn (_("Offset on nested structures is ignored"));
+ start_offset = abs_section_offset;
+ }
+ else
+ {
+ current_stag = (struct stag *) xmalloc (sizeof (struct stag));
+ memset (current_stag, 0, sizeof (struct stag));
+ abs_section_offset = start_offset;
+ }
+ current_stag->is_union = is_union;
+
+ if (line_label == NULL)
+ {
+ static int struct_count = 0;
+ char fake[] = ".fake_stagNNNNNNN";
+ sprintf (fake, ".fake_stag%d", struct_count++);
+ current_stag->sym = symbol_new (fake, absolute_section,
+ (valueT) abs_section_offset,
+ &zero_address_frag);
+ }
+ else
+ {
+ char label[strlen (S_GET_NAME (line_label)) + 1];
+ strcpy (label, S_GET_NAME (line_label));
+ current_stag->sym = symbol_new (label, absolute_section,
+ (valueT) abs_section_offset,
+ &zero_address_frag);
+ }
+ current_stag->name = S_GET_NAME (current_stag->sym);
+ SF_SET_LOCAL (current_stag->sym);
+ /* Nested .structs don't go into the symbol table. */
+ if (current_stag->outer == NULL)
+ symbol_table_insert (current_stag->sym);
+
+ line_label = NULL;
+}
+
+/* [LABEL] .endstruct
+ finish defining structure offsets; optional LABEL's value will be the size
+ of the structure. */
+
+static void
+tic54x_endstruct (int is_union)
+{
+ int size;
+ const char *path =
+ !strncmp (current_stag->name, ".fake", 5) ? "" : current_stag->name;
+
+ if (!current_stag || current_stag->is_union != is_union)
+ {
+ as_bad (_(".end%s without preceding .%s"),
+ is_union ? "union" : "struct",
+ is_union ? "union" : "struct");
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Align end of structures. */
+ if (current_stag->current_bitfield_offset)
+ {
+ ++abs_section_offset;
+ current_stag->current_bitfield_offset = 0;
+ }
+
+ if (current_stag->is_union)
+ size = current_stag->size;
+ else
+ size = abs_section_offset - S_GET_VALUE (current_stag->sym);
+ if (line_label != NULL)
+ {
+ S_SET_VALUE (line_label, size);
+ symbol_table_insert (line_label);
+ line_label = NULL;
+ }
+
+ /* Union size has already been calculated. */
+ if (!current_stag->is_union)
+ current_stag->size = size;
+ /* Nested .structs don't get put in the stag table. */
+ if (current_stag->outer == NULL)
+ {
+ hash_insert (stag_hash, current_stag->name, current_stag);
+ stag_add_field_symbols (current_stag, path,
+ S_GET_VALUE (current_stag->sym),
+ NULL, NULL);
+ }
+ current_stag = current_stag->outer;
+
+ /* If this is a nested .struct/.union, add it as a field to the enclosing
+ one. otherwise, restore the section we were in. */
+ if (current_stag != NULL)
+ {
+ stag_add_field (current_stag, current_stag->inner->name,
+ S_GET_VALUE (current_stag->inner->sym),
+ current_stag->inner);
+ }
+ else
+ subseg_set (stag_saved_seg, stag_saved_subseg);
+}
+
+/* [LABEL] .tag STAG
+ Reference a structure within a structure, as a sized field with an optional
+ label.
+ If used outside of a .struct/.endstruct, overlays the given structure
+ format on the existing allocated space. */
+
+static void
+tic54x_tag (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer;
+ int c = get_symbol_end ();
+ struct stag *stag = (struct stag *) hash_find (stag_hash, name);
+
+ if (!stag)
+ {
+ if (*name)
+ as_bad (_("Unrecognized struct/union tag '%s'"), name);
+ else
+ as_bad (_(".tag requires a structure tag"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (line_label == NULL)
+ {
+ as_bad (_("Label required for .tag"));
+ ignore_rest_of_line ();
+ return;
+ }
+ else
+ {
+ char label[strlen (S_GET_NAME (line_label)) + 1];
+
+ strcpy (label, S_GET_NAME (line_label));
+ if (current_stag != NULL)
+ stag_add_field (current_stag, label,
+ abs_section_offset - S_GET_VALUE (current_stag->sym),
+ stag);
+ else
+ {
+ symbolS *sym = symbol_find (label);
+
+ if (!sym)
+ {
+ as_bad (_(".tag target '%s' undefined"), label);
+ ignore_rest_of_line ();
+ return;
+ }
+ stag_add_field_symbols (stag, S_GET_NAME (sym),
+ S_GET_VALUE (stag->sym), sym, stag->name);
+ }
+ }
+
+ /* Bump by the struct size, but only if we're within a .struct section. */
+ if (current_stag != NULL && !current_stag->is_union)
+ abs_section_offset += stag->size;
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+ line_label = NULL;
+}
+
+/* Handle all .byte, .char, .double, .field, .float, .half, .int, .long,
+ .short, .string, .ubyte, .uchar, .uhalf, .uint, .ulong, .ushort, .uword,
+ and .word. */
+
+static void
+tic54x_struct_field (int type)
+{
+ int size;
+ int count = 1;
+ int new_bitfield_offset = 0;
+ int field_align = current_stag->current_bitfield_offset != 0;
+ int longword_align = 0;
+
+ SKIP_WHITESPACE ();
+ if (!is_end_of_line[(int) *input_line_pointer])
+ count = get_absolute_expression ();
+
+ switch (type)
+ {
+ case 'b':
+ case 'B':
+ case 'c':
+ case 'C':
+ case 'h':
+ case 'H':
+ case 'i':
+ case 'I':
+ case 's':
+ case 'S':
+ case 'w':
+ case 'W':
+ case '*': /* String. */
+ size = 1;
+ break;
+ case 'f':
+ case 'l':
+ case 'L':
+ longword_align = 1;
+ size = 2;
+ break;
+ case '.': /* Bitfield. */
+ size = 0;
+ if (count < 1 || count > 32)
+ {
+ as_bad (_(".field count '%d' out of range (1 <= X <= 32)"), count);
+ ignore_rest_of_line ();
+ return;
+ }
+ if (current_stag->current_bitfield_offset + count > 16)
+ {
+ /* Set the appropriate size and new field offset. */
+ if (count == 32)
+ {
+ size = 2;
+ count = 1;
+ }
+ else if (count > 16)
+ {
+ size = 1;
+ count = 1;
+ new_bitfield_offset = count - 16;
+ }
+ else
+ new_bitfield_offset = count;
+ }
+ else
+ {
+ field_align = 0;
+ new_bitfield_offset = current_stag->current_bitfield_offset + count;
+ }
+ break;
+ default:
+ as_bad (_("Unrecognized field type '%c'"), type);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (field_align)
+ {
+ /* Align to the actual starting position of the field. */
+ current_stag->current_bitfield_offset = 0;
+ ++abs_section_offset;
+ }
+ /* Align to longword boundary. */
+ if (longword_align && (abs_section_offset & 0x1))
+ ++abs_section_offset;
+
+ if (line_label == NULL)
+ {
+ static int fieldno = 0;
+ char fake[] = ".fake_fieldNNNNN";
+
+ sprintf (fake, ".fake_field%d", fieldno++);
+ stag_add_field (current_stag, fake,
+ abs_section_offset - S_GET_VALUE (current_stag->sym),
+ NULL);
+ }
+ else
+ {
+ char label[strlen (S_GET_NAME (line_label) + 1)];
+
+ strcpy (label, S_GET_NAME (line_label));
+ stag_add_field (current_stag, label,
+ abs_section_offset - S_GET_VALUE (current_stag->sym),
+ NULL);
+ }
+
+ if (current_stag->is_union)
+ {
+ /* Note we treat the element as if it were an array of COUNT. */
+ if (current_stag->size < (unsigned) size * count)
+ current_stag->size = size * count;
+ }
+ else
+ {
+ abs_section_offset += (unsigned) size * count;
+ current_stag->current_bitfield_offset = new_bitfield_offset;
+ }
+ line_label = NULL;
+}
+
+/* Handle .byte, .word. .int, .long and all variants. */
+
+static void
+tic54x_cons (int type)
+{
+ unsigned int c;
+ int octets;
+
+ /* If we're within a .struct construct, don't actually allocate space. */
+ if (current_stag != NULL)
+ {
+ tic54x_struct_field (type);
+ return;
+ }
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ generate_lineno_debug ();
+
+ /* Align long words to long word boundaries (4 octets). */
+ if (type == 'l' || type == 'L')
+ {
+ frag_align (2, 0, 2);
+ /* If there's a label, assign it to the first allocated word. */
+ if (line_label != NULL)
+ {
+ symbol_set_frag (line_label, frag_now);
+ S_SET_VALUE (line_label, frag_now_fix ());
+ }
+ }
+
+ switch (type)
+ {
+ case 'l':
+ case 'L':
+ case 'x':
+ octets = 4;
+ break;
+ case 'b':
+ case 'B':
+ case 'c':
+ case 'C':
+ octets = 1;
+ break;
+ default:
+ octets = 2;
+ break;
+ }
+
+ do
+ {
+ if (*input_line_pointer == '"')
+ {
+ input_line_pointer++;
+ while (is_a_char (c = next_char_of_string ()))
+ tic54x_emit_char (c);
+ know (input_line_pointer[-1] == '\"');
+ }
+ else
+ {
+ expressionS expn;
+
+ input_line_pointer = parse_expression (input_line_pointer, &expn);
+ if (expn.X_op == O_constant)
+ {
+ offsetT value = expn.X_add_number;
+ /* Truncate overflows. */
+ switch (octets)
+ {
+ case 1:
+ if ((value > 0 && value > 0xFF)
+ || (value < 0 && value < - 0x100))
+ as_warn (_("Overflow in expression, truncated to 8 bits"));
+ break;
+ case 2:
+ if ((value > 0 && value > 0xFFFF)
+ || (value < 0 && value < - 0x10000))
+ as_warn (_("Overflow in expression, truncated to 16 bits"));
+ break;
+ }
+ }
+ if (expn.X_op != O_constant && octets < 2)
+ {
+ /* Disallow .byte with a non constant expression that will
+ require relocation. */
+ as_bad (_("Relocatable values require at least WORD storage"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (expn.X_op != O_constant
+ && amode == c_mode
+ && octets == 4)
+ {
+ /* FIXME -- at one point TI tools used to output REL16
+ relocations, but I don't think the latest tools do at all
+ The current tools output extended relocations regardless of
+ the addressing mode (I actually think that ".c_mode" is
+ totally ignored in the latest tools). */
+ amode = far_mode;
+ emitting_long = 1;
+ emit_expr (&expn, 4);
+ emitting_long = 0;
+ amode = c_mode;
+ }
+ else
+ {
+ emitting_long = octets == 4;
+ emit_expr (&expn, (octets == 1) ? 2 : octets);
+ emitting_long = 0;
+ }
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+/* .global <symbol>[,...,<symbolN>]
+ .def <symbol>[,...,<symbolN>]
+ .ref <symbol>[,...,<symbolN>]
+
+ These all identify global symbols.
+
+ .def means the symbol is defined in the current module and can be accessed
+ by other files. The symbol should be placed in the symbol table.
+
+ .ref means the symbol is used in the current module but defined in another
+ module. The linker is to resolve this symbol's definition at link time.
+
+ .global should act as a .ref or .def, as needed.
+
+ global, def and ref all have symbol storage classes of C_EXT.
+
+ I can't identify any difference in how the "other" c54x assembler treats
+ these, so we ignore the type here. */
+
+void
+tic54x_global (int type)
+{
+ char *name;
+ int c;
+ symbolS *symbolP;
+
+ if (type == 'r')
+ as_warn (_("Use of .def/.ref is deprecated. Use .global instead"));
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ do
+ {
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ symbolP = symbol_find_or_make (name);
+
+ *input_line_pointer = c;
+ S_SET_STORAGE_CLASS (symbolP, C_EXT);
+ if (c == ',')
+ {
+ input_line_pointer++;
+ if (is_end_of_line[(int) *input_line_pointer])
+ c = *input_line_pointer;
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* Remove the symbol from the local label hash lookup. */
+
+static void
+tic54x_remove_local_label (const char *key, void *value ATTRIBUTE_UNUSED)
+{
+ void *elem = hash_delete (local_label_hash[macro_level], key, FALSE);
+ free (elem);
+}
+
+/* Reset all local labels. */
+
+static void
+tic54x_clear_local_labels (int ignored ATTRIBUTE_UNUSED)
+{
+ hash_traverse (local_label_hash[macro_level], tic54x_remove_local_label);
+}
+
+/* .text
+ .data
+ .sect "section name"
+
+ Initialized section
+ make sure local labels get cleared when changing sections
+
+ ARG is 't' for text, 'd' for data, or '*' for a named section
+
+ For compatibility, '*' sections are SEC_CODE if instructions are
+ encountered, or SEC_DATA if not.
+*/
+
+static void
+tic54x_sect (int arg)
+{
+ ILLEGAL_WITHIN_STRUCT ();
+
+ /* Local labels are cleared when changing sections. */
+ tic54x_clear_local_labels (0);
+
+ if (arg == 't')
+ s_text (0);
+ else if (arg == 'd')
+ s_data (0);
+ else
+ {
+ char *name = NULL;
+ int len;
+
+ /* If there are quotes, remove them. */
+ if (*input_line_pointer == '"')
+ {
+ name = demand_copy_C_string (&len);
+ demand_empty_rest_of_line ();
+ name = strcpy (xmalloc (len + 10), name);
+ }
+ else
+ {
+ int c;
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ len = strlen(name);
+ name = strcpy (xmalloc (len + 10), name);
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+ }
+ /* Make sure all named initialized sections flagged properly. If we
+ encounter instructions, we'll flag it with SEC_CODE as well. */
+ strcat (name, ",\"w\"\n");
+ input_scrub_insert_line (name);
+ obj_coff_section (0);
+
+ /* If there was a line label, make sure that it gets assigned the proper
+ section. This is for compatibility, even though the actual behavior
+ is not explicitly defined. For consistency, we make .sect behave
+ like .usect, since that is probably what people expect. */
+ if (line_label != NULL)
+ {
+ S_SET_SEGMENT (line_label, now_seg);
+ symbol_set_frag (line_label, frag_now);
+ S_SET_VALUE (line_label, frag_now_fix ());
+ if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
+ S_SET_STORAGE_CLASS (line_label, C_LABEL);
+ }
+ }
+}
+
+/* [symbol] .space space_in_bits
+ [symbol] .bes space_in_bits
+ BES puts the symbol at the *last* word allocated
+
+ cribbed from s_space. */
+
+static void
+tic54x_space (int arg)
+{
+ expressionS expn;
+ char *p = 0;
+ int octets = 0;
+ long words;
+ int bits_per_byte = (OCTETS_PER_BYTE * 8);
+ int bit_offset = 0;
+ symbolS *label = line_label;
+ int bes = arg;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ /* Read the bit count. */
+ expression (&expn);
+
+ /* Some expressions are unresolvable until later in the assembly pass;
+ postpone until relaxation/fixup. we also have to postpone if a previous
+ partial allocation has not been completed yet. */
+ if (expn.X_op != O_constant || frag_bit_offset (frag_now, now_seg) == -1)
+ {
+ struct bit_info *bi = xmalloc (sizeof (struct bit_info));
+
+ bi->seg = now_seg;
+ bi->type = bes;
+ bi->sym = label;
+ p = frag_var (rs_machine_dependent,
+ 65536 * 2, 1, (relax_substateT) 0,
+ make_expr_symbol (&expn), (offsetT) 0,
+ (char *) bi);
+ if (p)
+ *p = 0;
+
+ return;
+ }
+
+ /* Reduce the required size by any bit offsets currently left over
+ from a previous .space/.bes/.field directive. */
+ bit_offset = frag_now->tc_frag_data;
+ if (bit_offset != 0 && bit_offset < 16)
+ {
+ int spare_bits = bits_per_byte - bit_offset;
+
+ if (spare_bits >= expn.X_add_number)
+ {
+ /* Don't have to do anything; sufficient bits have already been
+ allocated; just point the label to the right place. */
+ if (label != NULL)
+ {
+ symbol_set_frag (label, frag_now);
+ S_SET_VALUE (label, frag_now_fix () - 1);
+ label = NULL;
+ }
+ frag_now->tc_frag_data += expn.X_add_number;
+ goto getout;
+ }
+ expn.X_add_number -= spare_bits;
+ /* Set the label to point to the first word allocated, which in this
+ case is the previous word, which was only partially filled. */
+ if (!bes && label != NULL)
+ {
+ symbol_set_frag (label, frag_now);
+ S_SET_VALUE (label, frag_now_fix () - 1);
+ label = NULL;
+ }
+ }
+ /* Convert bits to bytes/words and octets, rounding up. */
+ words = ((expn.X_add_number + bits_per_byte - 1) / bits_per_byte);
+ /* How many do we have left over? */
+ bit_offset = expn.X_add_number % bits_per_byte;
+ octets = words * OCTETS_PER_BYTE;
+ if (octets < 0)
+ {
+ as_warn (_(".space/.bes repeat count is negative, ignored"));
+ goto getout;
+ }
+ else if (octets == 0)
+ {
+ as_warn (_(".space/.bes repeat count is zero, ignored"));
+ goto getout;
+ }
+
+ /* If we are in the absolute section, just bump the offset. */
+ if (now_seg == absolute_section)
+ {
+ abs_section_offset += words;
+ if (bes && label != NULL)
+ S_SET_VALUE (label, abs_section_offset - 1);
+ frag_now->tc_frag_data = bit_offset;
+ goto getout;
+ }
+
+ if (!need_pass_2)
+ p = frag_var (rs_fill, 1, 1,
+ (relax_substateT) 0, (symbolS *) 0,
+ (offsetT) octets, (char *) 0);
+
+ /* Make note of how many bits of this word we've allocated so far. */
+ frag_now->tc_frag_data = bit_offset;
+
+ /* .bes puts label at *last* word allocated. */
+ if (bes && label != NULL)
+ {
+ symbol_set_frag (label, frag_now);
+ S_SET_VALUE (label, frag_now_fix () - 1);
+ }
+
+ if (p)
+ *p = 0;
+
+ getout:
+
+ demand_empty_rest_of_line ();
+}
+
+/* [symbol] .usect "section-name", size-in-words
+ [, [blocking-flag] [, alignment-flag]]
+
+ Uninitialized section.
+ Non-zero blocking means that if the section would cross a page (128-word)
+ boundary, it will be page-aligned.
+ Non-zero alignment aligns on a longword boundary.
+
+ Has no effect on the current section. */
+
+static void
+tic54x_usect (int x ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *name;
+ char *section_name;
+ char *p;
+ segT seg;
+ int size, blocking_flag, alignment_flag;
+ segT current_seg;
+ subsegT current_subseg;
+ flagword flags;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ current_seg = now_seg; /* Save current seg. */
+ current_subseg = now_subseg; /* Save current subseg. */
+
+ if (*input_line_pointer == '"')
+ input_line_pointer++;
+ section_name = input_line_pointer;
+ c = get_symbol_end (); /* Get terminator. */
+ input_line_pointer++; /* Skip null symbol terminator. */
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ else if (c != ',')
+ {
+ as_bad (_("Missing size argument"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ size = get_absolute_expression ();
+
+ /* Read a possibly present third argument (blocking flag). */
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ if (*input_line_pointer != ',')
+ blocking_flag = get_absolute_expression ();
+ else
+ blocking_flag = 0;
+
+ /* Read a possibly present fourth argument (alignment flag). */
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ alignment_flag = get_absolute_expression ();
+ }
+ else
+ alignment_flag = 0;
+ }
+ else
+ blocking_flag = alignment_flag = 0;
+
+ seg = subseg_new (name, 0);
+ flags = bfd_get_section_flags (stdoutput, seg) | SEC_ALLOC;
+
+ if (alignment_flag)
+ {
+ /* s_align eats end of line; restore it. */
+ s_align_bytes (4);
+ --input_line_pointer;
+ }
+
+ if (line_label != NULL)
+ {
+ S_SET_SEGMENT (line_label, seg);
+ symbol_set_frag (line_label, frag_now);
+ S_SET_VALUE (line_label, frag_now_fix ());
+ /* Set scl to label, since that's what TI does. */
+ if (S_GET_STORAGE_CLASS (line_label) != C_EXT)
+ S_SET_STORAGE_CLASS (line_label, C_LABEL);
+ }
+
+ seg_info (seg)->bss = 1; /* Uninitialized data. */
+
+ p = frag_var (rs_fill, 1, 1,
+ (relax_substateT) 0, (symbolS *) line_label,
+ size * OCTETS_PER_BYTE, (char *) 0);
+ *p = 0;
+
+ if (blocking_flag)
+ flags |= SEC_TIC54X_BLOCK;
+
+ if (!bfd_set_section_flags (stdoutput, seg, flags))
+ as_warn (_("Error setting flags for \"%s\": %s"), name,
+ bfd_errmsg (bfd_get_error ()));
+
+ subseg_set (current_seg, current_subseg); /* Restore current seg. */
+ demand_empty_rest_of_line ();
+}
+
+static enum cpu_version
+lookup_version (const char *ver)
+{
+ enum cpu_version version = VNONE;
+
+ if (ver[0] == '5' && ver[1] == '4')
+ {
+ if (strlen (ver) == 3
+ && (ver[2] == '1' || ver[2] == '2' || ver[2] == '3'
+ || ver[2] == '5' || ver[2] == '8' || ver[2] == '9'))
+ version = ver[2] - '0';
+ else if (strlen (ver) == 5
+ && TOUPPER (ver[3]) == 'L'
+ && TOUPPER (ver[4]) == 'P'
+ && (ver[2] == '5' || ver[2] == '6'))
+ version = ver[2] - '0' + 10;
+ }
+
+ return version;
+}
+
+static void
+set_cpu (enum cpu_version version)
+{
+ cpu = version;
+ if (version == V545LP || version == V546LP)
+ {
+ symbolS *symbolP = symbol_new ("__allow_lp", absolute_section,
+ (valueT) 1, &zero_address_frag);
+ SF_SET_LOCAL (symbolP);
+ symbol_table_insert (symbolP);
+ }
+}
+
+/* .version cpu-version
+ cpu-version may be one of the following:
+ 541
+ 542
+ 543
+ 545
+ 545LP
+ 546LP
+ 548
+ 549
+
+ This is for compatibility only. It currently has no affect on assembly. */
+static int cpu_needs_set = 1;
+
+static void
+tic54x_version (int x ATTRIBUTE_UNUSED)
+{
+ enum cpu_version version = VNONE;
+ enum cpu_version old_version = cpu;
+ int c;
+ char *ver;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ SKIP_WHITESPACE ();
+ ver = input_line_pointer;
+ while (!is_end_of_line[(int) *input_line_pointer])
+ ++input_line_pointer;
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ version = lookup_version (ver);
+
+ if (cpu != VNONE && cpu != version)
+ as_warn (_("CPU version has already been set"));
+
+ if (version == VNONE)
+ {
+ as_bad (_("Unrecognized version '%s'"), ver);
+ ignore_rest_of_line ();
+ return;
+ }
+ else if (assembly_begun && version != old_version)
+ {
+ as_bad (_("Changing of CPU version on the fly not supported"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ set_cpu (version);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* 'f' = float, 'x' = xfloat, 'd' = double, 'l' = ldouble. */
+
+static void
+tic54x_float_cons (int type)
+{
+ if (current_stag != 0)
+ tic54x_struct_field ('f');
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ /* Align to long word boundary (4 octets) unless it's ".xfloat". */
+ if (type != 'x')
+ {
+ frag_align (2, 0, 2);
+ /* If there's a label, assign it to the first allocated word. */
+ if (line_label != NULL)
+ {
+ symbol_set_frag (line_label, frag_now);
+ S_SET_VALUE (line_label, frag_now_fix ());
+ }
+ }
+
+ float_cons ('f');
+}
+
+/* The argument is capitalized if it should be zero-terminated
+ 's' is normal string with upper 8-bits zero-filled, 'p' is packed.
+ Code copied from stringer, and slightly modified so that strings are packed
+ and encoded into the correct octets. */
+
+static void
+tic54x_stringer (int type)
+{
+ unsigned int c;
+ int append_zero = type == 'S' || type == 'P';
+ int packed = type == 'p' || type == 'P';
+ int last_char = -1; /* Packed strings need two bytes at a time to encode. */
+
+ if (current_stag != NULL)
+ {
+ tic54x_struct_field ('*');
+ return;
+ }
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ c = ','; /* Do loop. */
+ while (c == ',')
+ {
+ SKIP_WHITESPACE ();
+ switch (*input_line_pointer)
+ {
+ default:
+ {
+ unsigned short value = get_absolute_expression ();
+ FRAG_APPEND_1_CHAR ( value & 0xFF);
+ FRAG_APPEND_1_CHAR ((value >> 8) & 0xFF);
+ break;
+ }
+ case '\"':
+ ++input_line_pointer; /* -> 1st char of string. */
+ while (is_a_char (c = next_char_of_string ()))
+ {
+ if (!packed)
+ {
+ FRAG_APPEND_1_CHAR (c);
+ FRAG_APPEND_1_CHAR (0);
+ }
+ else
+ {
+ /* Packed strings are filled MS octet first. */
+ if (last_char == -1)
+ last_char = c;
+ else
+ {
+ FRAG_APPEND_1_CHAR (c);
+ FRAG_APPEND_1_CHAR (last_char);
+ last_char = -1;
+ }
+ }
+ }
+ if (append_zero)
+ {
+ if (packed && last_char != -1)
+ {
+ FRAG_APPEND_1_CHAR (0);
+ FRAG_APPEND_1_CHAR (last_char);
+ last_char = -1;
+ }
+ else
+ {
+ FRAG_APPEND_1_CHAR (0);
+ FRAG_APPEND_1_CHAR (0);
+ }
+ }
+ know (input_line_pointer[-1] == '\"');
+ break;
+ }
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (!is_end_of_line[c])
+ ++input_line_pointer;
+ }
+
+ /* Finish up any leftover packed string. */
+ if (packed && last_char != -1)
+ {
+ FRAG_APPEND_1_CHAR (0);
+ FRAG_APPEND_1_CHAR (last_char);
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+tic54x_p2align (int arg ATTRIBUTE_UNUSED)
+{
+ as_bad (_("p2align not supported on this target"));
+}
+
+static void
+tic54x_align_words (int arg)
+{
+ /* Only ".align" with no argument is allowed within .struct/.union. */
+ int count = arg;
+
+ if (!is_end_of_line[(int) *input_line_pointer])
+ {
+ if (arg == 2)
+ as_warn (_("Argument to .even ignored"));
+ else
+ count = get_absolute_expression ();
+ }
+
+ if (current_stag != NULL && arg == 128)
+ {
+ if (current_stag->current_bitfield_offset != 0)
+ {
+ current_stag->current_bitfield_offset = 0;
+ ++abs_section_offset;
+ }
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ s_align_bytes (count << 1);
+}
+
+/* Initialize multiple-bit fields withing a single word of memory. */
+
+static void
+tic54x_field (int ignore ATTRIBUTE_UNUSED)
+{
+ expressionS expn;
+ int size = 16;
+ char *p;
+ valueT value;
+ symbolS *label = line_label;
+
+ if (current_stag != NULL)
+ {
+ tic54x_struct_field ('.');
+ return;
+ }
+
+ input_line_pointer = parse_expression (input_line_pointer, &expn);
+
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ size = get_absolute_expression ();
+ if (size < 1 || size > 32)
+ {
+ as_bad (_("Invalid field size, must be from 1 to 32"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+
+ /* Truncate values to the field width. */
+ if (expn.X_op != O_constant)
+ {
+ /* If the expression value is relocatable, the field size *must*
+ be 16. */
+ if (size != 16)
+ {
+ as_bad (_("field size must be 16 when value is relocatable"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ frag_now->tc_frag_data = 0;
+ emit_expr (&expn, 2);
+ }
+ else
+ {
+ unsigned long fmask = (size == 32) ? 0xFFFFFFFF : (1ul << size) - 1;
+
+ value = expn.X_add_number;
+ expn.X_add_number &= fmask;
+ if (value != (valueT) expn.X_add_number)
+ as_warn (_("field value truncated"));
+ value = expn.X_add_number;
+ /* Bits are stored MS first. */
+ while (size >= 16)
+ {
+ frag_now->tc_frag_data = 0;
+ p = frag_more (2);
+ md_number_to_chars (p, (value >> (size - 16)) & 0xFFFF, 2);
+ size -= 16;
+ }
+ if (size > 0)
+ {
+ int bit_offset = frag_bit_offset (frag_now, now_seg);
+
+ fragS *alloc_frag = bit_offset_frag (frag_now, now_seg);
+ if (bit_offset == -1)
+ {
+ struct bit_info *bi = xmalloc (sizeof (struct bit_info));
+ /* We don't know the previous offset at this time, so store the
+ info we need and figure it out later. */
+ expressionS size_exp;
+
+ size_exp.X_op = O_constant;
+ size_exp.X_add_number = size;
+ bi->seg = now_seg;
+ bi->type = TYPE_FIELD;
+ bi->value = value;
+ p = frag_var (rs_machine_dependent,
+ 4, 1, (relax_substateT) 0,
+ make_expr_symbol (&size_exp), (offsetT) 0,
+ (char *) bi);
+ goto getout;
+ }
+ else if (bit_offset == 0 || bit_offset + size > 16)
+ {
+ /* Align a new field. */
+ p = frag_more (2);
+ frag_now->tc_frag_data = 0;
+ alloc_frag = frag_now;
+ }
+ else
+ {
+ /* Put the new value entirely within the existing one. */
+ p = alloc_frag == frag_now ?
+ frag_now->fr_literal + frag_now_fix_octets () - 2 :
+ alloc_frag->fr_literal;
+ if (label != NULL)
+ {
+ symbol_set_frag (label, alloc_frag);
+ if (alloc_frag == frag_now)
+ S_SET_VALUE (label, frag_now_fix () - 1);
+ label = NULL;
+ }
+ }
+ value <<= 16 - alloc_frag->tc_frag_data - size;
+
+ /* OR in existing value. */
+ if (alloc_frag->tc_frag_data)
+ value |= ((unsigned short) p[1] << 8) | p[0];
+ md_number_to_chars (p, value, 2);
+ alloc_frag->tc_frag_data += size;
+ if (alloc_frag->tc_frag_data == 16)
+ alloc_frag->tc_frag_data = 0;
+ }
+ }
+ getout:
+ demand_empty_rest_of_line ();
+}
+
+/* Ideally, we want to check SEC_LOAD and SEC_HAS_CONTENTS, but those aren't
+ available yet. seg_info ()->bss is the next best thing. */
+
+static int
+tic54x_initialized_section (segT seg)
+{
+ return !seg_info (seg)->bss;
+}
+
+/* .clink ["section name"]
+
+ Marks the section as conditionally linked (link only if contents are
+ referenced elsewhere.
+ Without a name, refers to the current initialized section.
+ Name is required for uninitialized sections. */
+
+static void
+tic54x_clink (int ignored ATTRIBUTE_UNUSED)
+{
+ segT seg = now_seg;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ if (*input_line_pointer == '\"')
+ {
+ char *section_name = ++input_line_pointer;
+ char *name;
+
+ while (is_a_char (next_char_of_string ()))
+ ;
+ know (input_line_pointer[-1] == '\"');
+ input_line_pointer[-1] = 0;
+ name = xmalloc (input_line_pointer - section_name + 1);
+ strcpy (name, section_name);
+
+ seg = bfd_get_section_by_name (stdoutput, name);
+ if (seg == NULL)
+ {
+ as_bad (_("Unrecognized section '%s'"), section_name);
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ {
+ if (!tic54x_initialized_section (seg))
+ {
+ as_bad (_("Current section is unitialized, "
+ "section name required for .clink"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+
+ seg->flags |= SEC_TIC54X_CLINK;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Change the default include directory to be the current source file's
+ directory, instead of the current working directory. If DOT is non-zero,
+ set to "." instead. */
+
+static void
+tic54x_set_default_include (int dot)
+{
+ char *dir = ".";
+ char *tmp = NULL;
+
+ if (!dot)
+ {
+ char *curfile;
+ unsigned lineno;
+
+ as_where (&curfile, &lineno);
+ dir = strcpy (xmalloc (strlen (curfile) + 1), curfile);
+ tmp = strrchr (dir, '/');
+ }
+ if (tmp != NULL)
+ {
+ int len;
+
+ *tmp = '\0';
+ len = strlen (dir);
+ if (include_dir_count == 0)
+ {
+ include_dirs = (char **) xmalloc (sizeof (*include_dirs));
+ include_dir_count = 1;
+ }
+ include_dirs[0] = dir;
+ if (len > include_dir_maxlen)
+ include_dir_maxlen = len;
+ }
+ else if (include_dirs != NULL)
+ include_dirs[0] = ".";
+}
+
+/* .include "filename" | filename
+ .copy "filename" | filename
+
+ FIXME 'include' file should be omitted from any output listing,
+ 'copy' should be included in any output listing
+ FIXME -- prevent any included files from changing listing (compat only)
+ FIXME -- need to include source file directory in search path; what's a
+ good way to do this?
+
+ Entering/exiting included/copied file clears all local labels. */
+
+static void
+tic54x_include (int ignored ATTRIBUTE_UNUSED)
+{
+ char newblock[] = " .newblock\n";
+ char *filename;
+ char *input;
+ int len, c = -1;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer == '"')
+ {
+ filename = demand_copy_C_string (&len);
+ demand_empty_rest_of_line ();
+ }
+ else
+ {
+ filename = input_line_pointer;
+ while (!is_end_of_line[(int) *input_line_pointer])
+ ++input_line_pointer;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+ filename = strcpy (xmalloc (strlen (filename) + 1), filename);
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+ }
+ /* Insert a partial line with the filename (for the sake of s_include)
+ and a .newblock.
+ The included file will be inserted before the newblock, so that the
+ newblock is executed after the included file is processed. */
+ input = xmalloc (sizeof (newblock) + strlen (filename) + 4);
+ sprintf (input, "\"%s\"\n%s", filename, newblock);
+ input_scrub_insert_line (input);
+
+ tic54x_clear_local_labels (0);
+
+ tic54x_set_default_include (0);
+
+ s_include (0);
+}
+
+static void
+tic54x_message (int type)
+{
+ char *msg;
+ char c;
+ int len;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ if (*input_line_pointer == '"')
+ msg = demand_copy_C_string (&len);
+ else
+ {
+ msg = input_line_pointer;
+ while (!is_end_of_line[(int) *input_line_pointer])
+ ++input_line_pointer;
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+ msg = strcpy (xmalloc (strlen (msg) + 1), msg);
+ *input_line_pointer = c;
+ }
+
+ switch (type)
+ {
+ case 'm':
+ as_tsktsk ("%s", msg);
+ break;
+ case 'w':
+ as_warn ("%s", msg);
+ break;
+ case 'e':
+ as_bad ("%s", msg);
+ break;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* .label <symbol>
+ Define a special symbol that refers to the loadtime address rather than the
+ runtime address within the current section.
+
+ This symbol gets a special storage class so that when it is resolved, it is
+ resolved relative to the load address (lma) of the section rather than the
+ run address (vma). */
+
+static void
+tic54x_label (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name = input_line_pointer;
+ symbolS *symbolP;
+ int c;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ c = get_symbol_end ();
+ symbolP = colon (name);
+ S_SET_STORAGE_CLASS (symbolP, C_STATLAB);
+
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* .mmregs
+ Install all memory-mapped register names into the symbol table as
+ absolute local symbols. */
+
+static void
+tic54x_mmregs (int ignored ATTRIBUTE_UNUSED)
+{
+ symbol *sym;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ for (sym = (symbol *) mmregs; sym->name; sym++)
+ {
+ symbolS *symbolP = symbol_new (sym->name, absolute_section,
+ (valueT) sym->value, &zero_address_frag);
+ SF_SET_LOCAL (symbolP);
+ symbol_table_insert (symbolP);
+ }
+}
+
+/* .loop [count]
+ Count defaults to 1024. */
+
+static void
+tic54x_loop (int count)
+{
+ ILLEGAL_WITHIN_STRUCT ();
+
+ SKIP_WHITESPACE ();
+ if (!is_end_of_line[(int) *input_line_pointer])
+ count = get_absolute_expression ();
+
+ do_repeat (count, "LOOP", "ENDLOOP");
+}
+
+/* Normally, endloop gets eaten by the preceding loop. */
+
+static void
+tic54x_endloop (int ignore ATTRIBUTE_UNUSED)
+{
+ as_bad (_("ENDLOOP without corresponding LOOP"));
+ ignore_rest_of_line ();
+}
+
+/* .break [condition]. */
+
+static void
+tic54x_break (int ignore ATTRIBUTE_UNUSED)
+{
+ int cond = 1;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ SKIP_WHITESPACE ();
+ if (!is_end_of_line[(int) *input_line_pointer])
+ cond = get_absolute_expression ();
+
+ if (cond)
+ end_repeat (substitution_line ? 1 : 0);
+}
+
+static void
+set_address_mode (int mode)
+{
+ amode = mode;
+ if (mode == far_mode)
+ {
+ symbolS *symbolP = symbol_new ("__allow_far", absolute_section,
+ (valueT) 1, &zero_address_frag);
+ SF_SET_LOCAL (symbolP);
+ symbol_table_insert (symbolP);
+ }
+}
+
+static int address_mode_needs_set = 1;
+
+static void
+tic54x_address_mode (int mode)
+{
+ if (assembly_begun && amode != (unsigned) mode)
+ {
+ as_bad (_("Mixing of normal and extended addressing not supported"));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (mode == far_mode && cpu != VNONE && cpu != V548 && cpu != V549)
+ {
+ as_bad (_("Extended addressing not supported on the specified CPU"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ set_address_mode (mode);
+ demand_empty_rest_of_line ();
+}
+
+/* .sblock "section"|section [,...,"section"|section]
+ Designate initialized sections for blocking. */
+
+static void
+tic54x_sblock (int ignore ATTRIBUTE_UNUSED)
+{
+ int c = ',';
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ while (c == ',')
+ {
+ segT seg;
+ char *name;
+
+ if (*input_line_pointer == '"')
+ {
+ int len;
+
+ name = demand_copy_C_string (&len);
+ }
+ else
+ {
+ char *section_name = input_line_pointer;
+
+ c = get_symbol_end ();
+ name = xmalloc (strlen (section_name) + 1);
+ strcpy (name, section_name);
+ *input_line_pointer = c;
+ }
+
+ seg = bfd_get_section_by_name (stdoutput, name);
+ if (seg == NULL)
+ {
+ as_bad (_("Unrecognized section '%s'"), name);
+ ignore_rest_of_line ();
+ return;
+ }
+ else if (!tic54x_initialized_section (seg))
+ {
+ as_bad (_(".sblock may be used for initialized sections only"));
+ ignore_rest_of_line ();
+ return;
+ }
+ seg->flags |= SEC_TIC54X_BLOCK;
+
+ c = *input_line_pointer;
+ if (!is_end_of_line[(int) c])
+ ++input_line_pointer;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* symbol .set value
+ symbol .equ value
+
+ value must be defined externals; no forward-referencing allowed
+ symbols assigned with .set/.equ may not be redefined. */
+
+static void
+tic54x_set (int ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP;
+ char *name;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ if (!line_label)
+ {
+ as_bad (_("Symbol missing for .set/.equ"));
+ ignore_rest_of_line ();
+ return;
+ }
+ name = xstrdup (S_GET_NAME (line_label));
+ line_label = NULL;
+ if ((symbolP = symbol_find (name)) == NULL
+ && (symbolP = md_undefined_symbol (name)) == NULL)
+ {
+ symbolP = symbol_new (name, absolute_section, 0, &zero_address_frag);
+ S_SET_STORAGE_CLASS (symbolP, C_STAT);
+ }
+ free (name);
+ S_SET_DATA_TYPE (symbolP, T_INT);
+ S_SET_SEGMENT (symbolP, absolute_section);
+ symbol_table_insert (symbolP);
+ pseudo_set (symbolP);
+ demand_empty_rest_of_line ();
+}
+
+/* .fclist
+ .fcnolist
+ List false conditional blocks. */
+
+static void
+tic54x_fclist (int show)
+{
+ if (show)
+ listing &= ~LISTING_NOCOND;
+ else
+ listing |= LISTING_NOCOND;
+ demand_empty_rest_of_line ();
+}
+
+static void
+tic54x_sslist (int show)
+{
+ ILLEGAL_WITHIN_STRUCT ();
+
+ listing_sslist = show;
+}
+
+/* .var SYM[,...,SYMN]
+ Define a substitution string to be local to a macro. */
+
+static void
+tic54x_var (int ignore ATTRIBUTE_UNUSED)
+{
+ static char empty[] = "";
+ char *name;
+ int c;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ if (macro_level == 0)
+ {
+ as_bad (_(".var may only be used within a macro definition"));
+ ignore_rest_of_line ();
+ return;
+ }
+ do
+ {
+ if (!ISALPHA (*input_line_pointer))
+ {
+ as_bad (_("Substitution symbols must begin with a letter"));
+ ignore_rest_of_line ();
+ return;
+ }
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ /* .var symbols start out with a null string. */
+ name = strcpy (xmalloc (strlen (name) + 1), name);
+ hash_insert (subsym_hash[macro_level], name, empty);
+ *input_line_pointer = c;
+ if (c == ',')
+ {
+ ++input_line_pointer;
+ if (is_end_of_line[(int) *input_line_pointer])
+ c = *input_line_pointer;
+ }
+ }
+ while (c == ',');
+
+ demand_empty_rest_of_line ();
+}
+
+/* .mlib <macro library filename>
+
+ Macro libraries are archived (standard AR-format) text macro definitions
+ Expand the file and include it.
+
+ FIXME need to try the source file directory as well. */
+
+static void
+tic54x_mlib (int ignore ATTRIBUTE_UNUSED)
+{
+ char *filename;
+ char *path;
+ int len, i;
+ bfd *abfd, *mbfd;
+
+ ILLEGAL_WITHIN_STRUCT ();
+
+ /* Parse the filename. */
+ if (*input_line_pointer == '"')
+ {
+ if ((filename = demand_copy_C_string (&len)) == NULL)
+ return;
+ }
+ else
+ {
+ SKIP_WHITESPACE ();
+ len = 0;
+ while (!is_end_of_line[(int) *input_line_pointer]
+ && !ISSPACE (*input_line_pointer))
+ {
+ obstack_1grow (&notes, *input_line_pointer);
+ ++input_line_pointer;
+ ++len;
+ }
+ obstack_1grow (&notes, '\0');
+ filename = obstack_finish (&notes);
+ }
+ demand_empty_rest_of_line ();
+
+ tic54x_set_default_include (0);
+ path = xmalloc ((unsigned long) len + include_dir_maxlen + 5);
+
+ for (i = 0; i < include_dir_count; i++)
+ {
+ FILE *try;
+
+ strcpy (path, include_dirs[i]);
+ strcat (path, "/");
+ strcat (path, filename);
+ if ((try = fopen (path, "r")) != NULL)
+ {
+ fclose (try);
+ break;
+ }
+ }
+
+ if (i >= include_dir_count)
+ {
+ free (path);
+ path = filename;
+ }
+
+ /* FIXME: if path is found, malloc'd storage is not freed. Of course, this
+ happens all over the place, and since the assembler doesn't usually keep
+ running for a very long time, it really doesn't matter. */
+ register_dependency (path);
+
+ /* Expand all archive entries to temporary files and include them. */
+ abfd = bfd_openr (path, NULL);
+ if (!abfd)
+ {
+ as_bad (_("can't open macro library file '%s' for reading: %s"),
+ path, bfd_errmsg (bfd_get_error ()));
+ ignore_rest_of_line ();
+ return;
+ }
+ if (!bfd_check_format (abfd, bfd_archive))
+ {
+ as_bad (_("File '%s' not in macro archive format"), path);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Open each BFD as binary (it should be straight ASCII text). */
+ for (mbfd = bfd_openr_next_archived_file (abfd, NULL);
+ mbfd != NULL; mbfd = bfd_openr_next_archived_file (abfd, mbfd))
+ {
+ /* Get a size at least as big as the archive member. */
+ bfd_size_type size = bfd_get_size (mbfd);
+ char *buf = xmalloc (size);
+ char *fname = tmpnam (NULL);
+ FILE *ftmp;
+
+ /* We're not sure how big it is, but it will be smaller than "size". */
+ size = bfd_bread (buf, size, mbfd);
+
+ /* Write to a temporary file, then use s_include to include it
+ a bit of a hack. */
+ ftmp = fopen (fname, "w+b");
+ fwrite ((void *) buf, size, 1, ftmp);
+ if (size == 0 || buf[size - 1] != '\n')
+ fwrite ("\n", 1, 1, ftmp);
+ fclose (ftmp);
+ free (buf);
+ input_scrub_insert_file (fname);
+ unlink (fname);
+ }
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "algebraic", s_ignore , 0 },
+ { "align" , tic54x_align_words , 128 },
+ { "ascii" , tic54x_stringer , 'p' },
+ { "asciz" , tic54x_stringer , 'P' },
+ { "even" , tic54x_align_words , 2 },
+ { "asg" , tic54x_asg , 0 },
+ { "eval" , tic54x_eval , 0 },
+ { "bss" , tic54x_bss , 0 },
+ { "byte" , tic54x_cons , 'b' },
+ { "ubyte" , tic54x_cons , 'B' },
+ { "char" , tic54x_cons , 'c' },
+ { "uchar" , tic54x_cons , 'C' },
+ { "clink" , tic54x_clink , 0 },
+ { "c_mode" , tic54x_address_mode , c_mode },
+ { "copy" , tic54x_include , 'c' },
+ { "include" , tic54x_include , 'i' },
+ { "data" , tic54x_sect , 'd' },
+ { "double" , tic54x_float_cons , 'd' },
+ { "ldouble" , tic54x_float_cons , 'l' },
+ { "drlist" , s_ignore , 0 },
+ { "drnolist" , s_ignore , 0 },
+ { "emsg" , tic54x_message , 'e' },
+ { "mmsg" , tic54x_message , 'm' },
+ { "wmsg" , tic54x_message , 'w' },
+ { "far_mode" , tic54x_address_mode , far_mode },
+ { "fclist" , tic54x_fclist , 1 },
+ { "fcnolist" , tic54x_fclist , 0 },
+ { "field" , tic54x_field , -1 },
+ { "float" , tic54x_float_cons , 'f' },
+ { "xfloat" , tic54x_float_cons , 'x' },
+ { "global" , tic54x_global , 'g' },
+ { "def" , tic54x_global , 'd' },
+ { "ref" , tic54x_global , 'r' },
+ { "half" , tic54x_cons , 'h' },
+ { "uhalf" , tic54x_cons , 'H' },
+ { "short" , tic54x_cons , 's' },
+ { "ushort" , tic54x_cons , 'S' },
+ { "if" , s_if , (int) O_ne },
+ { "elseif" , s_elseif , (int) O_ne },
+ { "else" , s_else , 0 },
+ { "endif" , s_endif , 0 },
+ { "int" , tic54x_cons , 'i' },
+ { "uint" , tic54x_cons , 'I' },
+ { "word" , tic54x_cons , 'w' },
+ { "uword" , tic54x_cons , 'W' },
+ { "label" , tic54x_label , 0 }, /* Loadtime
+ address. */
+ { "length" , s_ignore , 0 },
+ { "width" , s_ignore , 0 },
+ { "long" , tic54x_cons , 'l' },
+ { "ulong" , tic54x_cons , 'L' },
+ { "xlong" , tic54x_cons , 'x' },
+ { "loop" , tic54x_loop , 1024 },
+ { "break" , tic54x_break , 0 },
+ { "endloop" , tic54x_endloop , 0 },
+ { "mlib" , tic54x_mlib , 0 },
+ { "mlist" , s_ignore , 0 },
+ { "mnolist" , s_ignore , 0 },
+ { "mmregs" , tic54x_mmregs , 0 },
+ { "newblock" , tic54x_clear_local_labels, 0 },
+ { "option" , s_ignore , 0 },
+ { "p2align" , tic54x_p2align , 0 },
+ { "sblock" , tic54x_sblock , 0 },
+ { "sect" , tic54x_sect , '*' },
+ { "set" , tic54x_set , 0 },
+ { "equ" , tic54x_set , 0 },
+ { "space" , tic54x_space , 0 },
+ { "bes" , tic54x_space , 1 },
+ { "sslist" , tic54x_sslist , 1 },
+ { "ssnolist" , tic54x_sslist , 0 },
+ { "string" , tic54x_stringer , 's' },
+ { "pstring" , tic54x_stringer , 'p' },
+ { "struct" , tic54x_struct , 0 },
+ { "tag" , tic54x_tag , 0 },
+ { "endstruct", tic54x_endstruct , 0 },
+ { "tab" , s_ignore , 0 },
+ { "text" , tic54x_sect , 't' },
+ { "union" , tic54x_struct , 1 },
+ { "endunion" , tic54x_endstruct , 1 },
+ { "usect" , tic54x_usect , 0 },
+ { "var" , tic54x_var , 0 },
+ { "version" , tic54x_version , 0 },
+ {0 , 0 , 0 }
+};
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+ case OPTION_COFF_VERSION:
+ {
+ int version = atoi (arg);
+
+ if (version != 0 && version != 1 && version != 2)
+ as_fatal (_("Bad COFF version '%s'"), arg);
+ /* FIXME -- not yet implemented. */
+ break;
+ }
+ case OPTION_CPU_VERSION:
+ {
+ cpu = lookup_version (arg);
+ cpu_needs_set = 1;
+ if (cpu == VNONE)
+ as_fatal (_("Bad CPU version '%s'"), arg);
+ break;
+ }
+ case OPTION_ADDRESS_MODE:
+ amode = far_mode;
+ address_mode_needs_set = 1;
+ break;
+ case OPTION_STDERR_TO_FILE:
+ {
+ char *filename = arg;
+ FILE *fp = fopen (filename, "w+");
+
+ if (fp == NULL)
+ as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
+ fclose (fp);
+ if ((fp = freopen (filename, "w+", stderr)) == NULL)
+ as_fatal (_("Can't redirect stderr to the file '%s'"), filename);
+ break;
+ }
+ }
+
+ return 1;
+}
+
+/* Create a "local" substitution string hash table for a new macro level
+ Some docs imply that macros have to use .newblock in order to be able
+ to re-use a local label. We effectively do an automatic .newblock by
+ deleting the local label hash between macro invocations. */
+
+void
+tic54x_macro_start (void)
+{
+ ++macro_level;
+ subsym_hash[macro_level] = hash_new ();
+ local_label_hash[macro_level] = hash_new ();
+}
+
+void
+tic54x_macro_info (const macro_entry *macro)
+{
+ const formal_entry *entry;
+
+ /* Put the formal args into the substitution symbol table. */
+ for (entry = macro->formals; entry; entry = entry->next)
+ {
+ char *name = strncpy (xmalloc (entry->name.len + 1),
+ entry->name.ptr, entry->name.len);
+ char *value = strncpy (xmalloc (entry->actual.len + 1),
+ entry->actual.ptr, entry->actual.len);
+
+ name[entry->name.len] = '\0';
+ value[entry->actual.len] = '\0';
+ hash_insert (subsym_hash[macro_level], name, value);
+ }
+}
+
+/* Get rid of this macro's .var's, arguments, and local labels. */
+
+void
+tic54x_macro_end (void)
+{
+ hash_die (subsym_hash[macro_level]);
+ subsym_hash[macro_level] = NULL;
+ hash_die (local_label_hash[macro_level]);
+ local_label_hash[macro_level] = NULL;
+ --macro_level;
+}
+
+static int
+subsym_symlen (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ return strlen (a);
+}
+
+/* Compare symbol A to string B. */
+
+static int
+subsym_symcmp (char *a, char *b)
+{
+ return strcmp (a, b);
+}
+
+/* Return the index of the first occurrence of B in A, or zero if none
+ assumes b is an integer char value as a string. Index is one-based. */
+
+static int
+subsym_firstch (char *a, char *b)
+{
+ int val = atoi (b);
+ char *tmp = strchr (a, val);
+
+ return tmp ? tmp - a + 1 : 0;
+}
+
+/* Similar to firstch, but returns index of last occurrence of B in A. */
+
+static int
+subsym_lastch (char *a, char *b)
+{
+ int val = atoi (b);
+ char *tmp = strrchr (a, val);
+
+ return tmp ? tmp - a + 1 : 0;
+}
+
+/* Returns 1 if string A is defined in the symbol table (NOT the substitution
+ symbol table). */
+
+static int
+subsym_isdefed (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ symbolS *symbolP = symbol_find (a);
+
+ return symbolP != NULL;
+}
+
+/* Assign first member of comma-separated list B (e.g. "1,2,3") to the symbol
+ A, or zero if B is a null string. Both arguments *must* be substitution
+ symbols, unsubstituted. */
+
+static int
+subsym_ismember (char *sym, char *list)
+{
+ char *elem, *ptr, *listv;
+
+ if (!list)
+ return 0;
+
+ listv = subsym_lookup (list, macro_level);
+ if (!listv)
+ {
+ as_bad (_("Undefined substitution symbol '%s'"), list);
+ ignore_rest_of_line ();
+ return 0;
+ }
+
+ ptr = elem = xmalloc (strlen (listv) + 1);
+ strcpy (elem, listv);
+ while (*ptr && *ptr != ',')
+ ++ptr;
+ *ptr++ = 0;
+
+ subsym_create_or_replace (sym, elem);
+
+ /* Reassign the list. */
+ subsym_create_or_replace (list, ptr);
+
+ /* Assume this value, docs aren't clear. */
+ return *list != 0;
+}
+
+/* Return zero if not a constant; otherwise:
+ 1 if binary
+ 2 if octal
+ 3 if hexadecimal
+ 4 if character
+ 5 if decimal. */
+
+static int
+subsym_iscons (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ expressionS expn;
+
+ parse_expression (a, &expn);
+
+ if (expn.X_op == O_constant)
+ {
+ int len = strlen (a);
+
+ switch (TOUPPER (a[len - 1]))
+ {
+ case 'B':
+ return 1;
+ case 'Q':
+ return 2;
+ case 'H':
+ return 3;
+ case '\'':
+ return 4;
+ default:
+ break;
+ }
+ /* No suffix; either octal, hex, or decimal. */
+ if (*a == '0' && len > 1)
+ {
+ if (TOUPPER (a[1]) == 'X')
+ return 3;
+ return 2;
+ }
+ return 5;
+ }
+
+ return 0;
+}
+
+/* Return 1 if A is a valid symbol name. Expects string input. */
+
+static int
+subsym_isname (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (!is_name_beginner (*a))
+ return 0;
+ while (*a)
+ {
+ if (!is_part_of_name (*a))
+ return 0;
+ ++a;
+ }
+ return 1;
+}
+
+/* Return whether the string is a register; accepts ar0-7, unless .mmregs has
+ been seen; if so, recognize any memory-mapped register.
+ Note this does not recognize "A" or "B" accumulators. */
+
+static int
+subsym_isreg (char *a, char *ignore ATTRIBUTE_UNUSED)
+{
+ if (hash_find (reg_hash, a))
+ return 1;
+ if (hash_find (mmreg_hash, a))
+ return 1;
+ return 0;
+}
+
+/* Return the structure size, given the stag. */
+
+static int
+subsym_structsz (char *name, char *ignore ATTRIBUTE_UNUSED)
+{
+ struct stag *stag = (struct stag *) hash_find (stag_hash, name);
+
+ if (stag)
+ return stag->size;
+
+ return 0;
+}
+
+/* If anybody actually uses this, they can fix it :)
+ FIXME I'm not sure what the "reference point" of a structure is. It might
+ be either the initial offset given .struct, or it may be the offset of the
+ structure within another structure, or it might be something else
+ altogether. since the TI assembler doesn't seem to ever do anything but
+ return zero, we punt and return zero. */
+
+static int
+subsym_structacc (char *stag_name ATTRIBUTE_UNUSED,
+ char *ignore ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+static float
+math_ceil (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) ceil (arg1);
+}
+
+static float
+math_cvi (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (int) arg1;
+}
+
+static float
+math_floor (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) floor (arg1);
+}
+
+static float
+math_fmod (float arg1, float arg2)
+{
+ return (int) arg1 % (int) arg2;
+}
+
+static float
+math_int (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return ((float) ((int) arg1)) == arg1;
+}
+
+static float
+math_round (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return arg1 > 0 ? (int) (arg1 + 0.5) : (int) (arg1 - 0.5);
+}
+
+static float
+math_sgn (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (arg1 < 0) ? -1 : (arg1 ? 1 : 0);
+}
+
+static float
+math_trunc (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (int) arg1;
+}
+
+static float
+math_acos (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) acos (arg1);
+}
+
+static float
+math_asin (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) asin (arg1);
+}
+
+static float
+math_atan (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) atan (arg1);
+}
+
+static float
+math_atan2 (float arg1, float arg2)
+{
+ return (float) atan2 (arg1, arg2);
+}
+
+static float
+math_cosh (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) cosh (arg1);
+}
+
+static float
+math_cos (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) cos (arg1);
+}
+
+static float
+math_cvf (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) arg1;
+}
+
+static float
+math_exp (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) exp (arg1);
+}
+
+static float
+math_fabs (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) fabs (arg1);
+}
+
+/* expr1 * 2^expr2. */
+
+static float
+math_ldexp (float arg1, float arg2)
+{
+ return arg1 * (float) pow (2.0, arg2);
+}
+
+static float
+math_log10 (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) log10 (arg1);
+}
+
+static float
+math_log (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) log (arg1);
+}
+
+static float
+math_max (float arg1, float arg2)
+{
+ return (arg1 > arg2) ? arg1 : arg2;
+}
+
+static float
+math_min (float arg1, float arg2)
+{
+ return (arg1 < arg2) ? arg1 : arg2;
+}
+
+static float
+math_pow (float arg1, float arg2)
+{
+ return (float) pow (arg1, arg2);
+}
+
+static float
+math_sin (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) sin (arg1);
+}
+
+static float
+math_sinh (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) sinh (arg1);
+}
+
+static float
+math_sqrt (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) sqrt (arg1);
+}
+
+static float
+math_tan (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) tan (arg1);
+}
+
+static float
+math_tanh (float arg1, float ignore ATTRIBUTE_UNUSED)
+{
+ return (float) tanh (arg1);
+}
+
+/* Built-in substitution symbol functions and math functions. */
+typedef struct
+{
+ char *name;
+ int (*proc) (char *, char *);
+ int nargs;
+} subsym_proc_entry;
+
+static const subsym_proc_entry subsym_procs[] =
+{
+ /* Assembler built-in string substitution functions. */
+ { "$symlen", subsym_symlen, 1, },
+ { "$symcmp", subsym_symcmp, 2, },
+ { "$firstch", subsym_firstch, 2, },
+ { "$lastch", subsym_lastch, 2, },
+ { "$isdefed", subsym_isdefed, 1, },
+ { "$ismember", subsym_ismember, 2, },
+ { "$iscons", subsym_iscons, 1, },
+ { "$isname", subsym_isname, 1, },
+ { "$isreg", subsym_isreg, 1, },
+ { "$structsz", subsym_structsz, 1, },
+ { "$structacc", subsym_structacc, 1, },
+ { NULL, NULL, 0 },
+};
+
+typedef struct
+{
+ char *name;
+ float (*proc) (float, float);
+ int nargs;
+ int int_return;
+} math_proc_entry;
+
+static const math_proc_entry math_procs[] =
+{
+ /* Integer-returning built-in math functions. */
+ { "$cvi", math_cvi, 1, 1 },
+ { "$int", math_int, 1, 1 },
+ { "$sgn", math_sgn, 1, 1 },
+
+ /* Float-returning built-in math functions. */
+ { "$acos", math_acos, 1, 0 },
+ { "$asin", math_asin, 1, 0 },
+ { "$atan", math_atan, 1, 0 },
+ { "$atan2", math_atan2, 2, 0 },
+ { "$ceil", math_ceil, 1, 0 },
+ { "$cosh", math_cosh, 1, 0 },
+ { "$cos", math_cos, 1, 0 },
+ { "$cvf", math_cvf, 1, 0 },
+ { "$exp", math_exp, 1, 0 },
+ { "$fabs", math_fabs, 1, 0 },
+ { "$floor", math_floor, 1, 0 },
+ { "$fmod", math_fmod, 2, 0 },
+ { "$ldexp", math_ldexp, 2, 0 },
+ { "$log10", math_log10, 1, 0 },
+ { "$log", math_log, 1, 0 },
+ { "$max", math_max, 2, 0 },
+ { "$min", math_min, 2, 0 },
+ { "$pow", math_pow, 2, 0 },
+ { "$round", math_round, 1, 0 },
+ { "$sin", math_sin, 1, 0 },
+ { "$sinh", math_sinh, 1, 0 },
+ { "$sqrt", math_sqrt, 1, 0 },
+ { "$tan", math_tan, 1, 0 },
+ { "$tanh", math_tanh, 1, 0 },
+ { "$trunc", math_trunc, 1, 0 },
+ { NULL, NULL, 0, 0 },
+};
+
+void
+md_begin (void)
+{
+ insn_template *tm;
+ symbol *sym;
+ const subsym_proc_entry *subsym_proc;
+ const math_proc_entry *math_proc;
+ const char *hash_err;
+ char **symname;
+ char *TIC54X_DIR = getenv ("TIC54X_DIR");
+ char *A_DIR = TIC54X_DIR ? TIC54X_DIR : getenv ("A_DIR");
+
+ local_label_id = 0;
+
+ /* Look for A_DIR and add it to the include list. */
+ if (A_DIR != NULL)
+ {
+ char *tmp = xstrdup (A_DIR);
+
+ do
+ {
+ char *next = strchr (tmp, ';');
+
+ if (next)
+ *next++ = '\0';
+ add_include_dir (tmp);
+ tmp = next;
+ }
+ while (tmp != NULL);
+ }
+
+ op_hash = hash_new ();
+ for (tm = (insn_template *) tic54x_optab; tm->name; tm++)
+ {
+ if (hash_find (op_hash, tm->name))
+ continue;
+ hash_err = hash_insert (op_hash, tm->name, (char *) tm);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't hash %s: %s",
+ tm->name, hash_err);
+ }
+ parop_hash = hash_new ();
+ for (tm = (insn_template *) tic54x_paroptab; tm->name; tm++)
+ {
+ if (hash_find (parop_hash, tm->name))
+ continue;
+ hash_err = hash_insert (parop_hash, tm->name, (char *) tm);
+ if (hash_err)
+ as_fatal ("Internal Error: Can't hash %s: %s",
+ tm->name, hash_err);
+ }
+ reg_hash = hash_new ();
+ for (sym = (symbol *) regs; sym->name; sym++)
+ {
+ /* Add basic registers to the symbol table. */
+ symbolS *symbolP = symbol_new (sym->name, absolute_section,
+ (valueT) sym->value, &zero_address_frag);
+ SF_SET_LOCAL (symbolP);
+ symbol_table_insert (symbolP);
+ hash_err = hash_insert (reg_hash, sym->name, (char *) sym);
+ }
+ for (sym = (symbol *) mmregs; sym->name; sym++)
+ hash_err = hash_insert (reg_hash, sym->name, (char *) sym);
+ mmreg_hash = hash_new ();
+ for (sym = (symbol *) mmregs; sym->name; sym++)
+ hash_err = hash_insert (mmreg_hash, sym->name, (char *) sym);
+
+ cc_hash = hash_new ();
+ for (sym = (symbol *) condition_codes; sym->name; sym++)
+ hash_err = hash_insert (cc_hash, sym->name, (char *) sym);
+
+ cc2_hash = hash_new ();
+ for (sym = (symbol *) cc2_codes; sym->name; sym++)
+ hash_err = hash_insert (cc2_hash, sym->name, (char *) sym);
+
+ cc3_hash = hash_new ();
+ for (sym = (symbol *) cc3_codes; sym->name; sym++)
+ hash_err = hash_insert (cc3_hash, sym->name, (char *) sym);
+
+ sbit_hash = hash_new ();
+ for (sym = (symbol *) status_bits; sym->name; sym++)
+ hash_err = hash_insert (sbit_hash, sym->name, (char *) sym);
+
+ misc_symbol_hash = hash_new ();
+ for (symname = (char **) misc_symbols; *symname; symname++)
+ hash_err = hash_insert (misc_symbol_hash, *symname, *symname);
+
+ /* Only the base substitution table and local label table are initialized;
+ the others (for local macro substitution) get instantiated as needed. */
+ local_label_hash[0] = hash_new ();
+ subsym_hash[0] = hash_new ();
+ for (subsym_proc = subsym_procs; subsym_proc->name; subsym_proc++)
+ hash_err = hash_insert (subsym_hash[0], subsym_proc->name,
+ (char *) subsym_proc);
+
+ math_hash = hash_new ();
+ for (math_proc = math_procs; math_proc->name; math_proc++)
+ {
+ /* Insert into the main subsym hash for recognition; insert into
+ the math hash to actually store information. */
+ hash_err = hash_insert (subsym_hash[0], math_proc->name,
+ (char *) math_proc);
+ hash_err = hash_insert (math_hash, math_proc->name,
+ (char *) math_proc);
+ }
+ subsym_recurse_hash = hash_new ();
+ stag_hash = hash_new ();
+}
+
+static int
+is_accumulator (struct opstruct *operand)
+{
+ return strcasecmp (operand->buf, "a") == 0
+ || strcasecmp (operand->buf, "b") == 0;
+}
+
+/* Return the number of operands found, or -1 on error, copying the
+ operands into the given array and the accompanying expressions into
+ the next array. */
+
+static int
+get_operands (struct opstruct operands[], char *line)
+{
+ char *lptr = line;
+ int numexp = 0;
+ int expecting_operand = 0;
+ int i;
+
+ while (numexp < MAX_OPERANDS && !is_end_of_line[(int) *lptr])
+ {
+ int paren_not_balanced = 0;
+ char *op_start, *op_end;
+
+ while (*lptr && ISSPACE (*lptr))
+ ++lptr;
+ op_start = lptr;
+ while (paren_not_balanced || *lptr != ',')
+ {
+ if (*lptr == '\0')
+ {
+ if (paren_not_balanced)
+ {
+ as_bad (_("Unbalanced parenthesis in operand %d"), numexp);
+ return -1;
+ }
+ else
+ break;
+ }
+ if (*lptr == '(')
+ ++paren_not_balanced;
+ else if (*lptr == ')')
+ --paren_not_balanced;
+ ++lptr;
+ }
+ op_end = lptr;
+ if (op_end != op_start)
+ {
+ int len = op_end - op_start;
+
+ strncpy (operands[numexp].buf, op_start, len);
+ operands[numexp].buf[len] = 0;
+ /* Trim trailing spaces; while the preprocessor gets rid of most,
+ there are weird usage patterns that can introduce them
+ (i.e. using strings for macro args). */
+ while (len > 0 && ISSPACE (operands[numexp].buf[len - 1]))
+ operands[numexp].buf[--len] = 0;
+ lptr = op_end;
+ ++numexp;
+ }
+ else
+ {
+ if (expecting_operand || *lptr == ',')
+ {
+ as_bad (_("Expecting operand after ','"));
+ return -1;
+ }
+ }
+ if (*lptr == ',')
+ {
+ if (*++lptr == '\0')
+ {
+ as_bad (_("Expecting operand after ','"));
+ return -1;
+ }
+ expecting_operand = 1;
+ }
+ }
+
+ while (*lptr && ISSPACE (*lptr++))
+ ;
+ if (!is_end_of_line[(int) *lptr])
+ {
+ as_bad (_("Extra junk on line"));
+ return -1;
+ }
+
+ /* OK, now parse them into expressions. */
+ for (i = 0; i < numexp; i++)
+ {
+ memset (&operands[i].exp, 0, sizeof (operands[i].exp));
+ if (operands[i].buf[0] == '#')
+ {
+ /* Immediate. */
+ parse_expression (operands[i].buf + 1, &operands[i].exp);
+ }
+ else if (operands[i].buf[0] == '@')
+ {
+ /* Direct notation. */
+ parse_expression (operands[i].buf + 1, &operands[i].exp);
+ }
+ else if (operands[i].buf[0] == '*')
+ {
+ /* Indirect. */
+ char *paren = strchr (operands[i].buf, '(');
+
+ /* Allow immediate syntax in the inner expression. */
+ if (paren && paren[1] == '#')
+ *++paren = '(';
+
+ /* Pull out the lk expression or SP offset, if present. */
+ if (paren != NULL)
+ {
+ int len = strlen (paren);
+ char *end = paren + len;
+ int c;
+
+ while (end[-1] != ')')
+ if (--end <= paren)
+ {
+ as_bad (_("Badly formed address expression"));
+ return -1;
+ }
+ c = *end;
+ *end = '\0';
+ parse_expression (paren, &operands[i].exp);
+ *end = c;
+ }
+ else
+ operands[i].exp.X_op = O_absent;
+ }
+ else
+ parse_expression (operands[i].buf, &operands[i].exp);
+ }
+
+ return numexp;
+}
+
+/* Predicates for different operand types. */
+
+static int
+is_immediate (struct opstruct *operand)
+{
+ return *operand->buf == '#';
+}
+
+/* This is distinguished from immediate because some numbers must be constants
+ and must *not* have the '#' prefix. */
+
+static int
+is_absolute (struct opstruct *operand)
+{
+ return operand->exp.X_op == O_constant && !is_immediate (operand);
+}
+
+/* Is this an indirect operand? */
+
+static int
+is_indirect (struct opstruct *operand)
+{
+ return operand->buf[0] == '*';
+}
+
+/* Is this a valid dual-memory operand? */
+
+static int
+is_dual (struct opstruct *operand)
+{
+ if (is_indirect (operand) && strncasecmp (operand->buf, "*ar", 3) == 0)
+ {
+ char *tmp = operand->buf + 3;
+ int arf;
+ int valid_mod;
+
+ arf = *tmp++ - '0';
+ /* Only allow *ARx, *ARx-, *ARx+, or *ARx+0%. */
+ valid_mod = *tmp == '\0' ||
+ strcasecmp (tmp, "-") == 0 ||
+ strcasecmp (tmp, "+") == 0 ||
+ strcasecmp (tmp, "+0%") == 0;
+ return arf >= 2 && arf <= 5 && valid_mod;
+ }
+ return 0;
+}
+
+static int
+is_mmreg (struct opstruct *operand)
+{
+ return (is_absolute (operand)
+ || is_immediate (operand)
+ || hash_find (mmreg_hash, operand->buf) != 0);
+}
+
+static int
+is_type (struct opstruct *operand, enum optype type)
+{
+ switch (type)
+ {
+ case OP_None:
+ return operand->buf[0] == 0;
+ case OP_Xmem:
+ case OP_Ymem:
+ return is_dual (operand);
+ case OP_Sind:
+ return is_indirect (operand);
+ case OP_xpmad_ms7:
+ /* This one *must* be immediate. */
+ return is_immediate (operand);
+ case OP_xpmad:
+ case OP_pmad:
+ case OP_PA:
+ case OP_dmad:
+ case OP_Lmem:
+ case OP_MMR:
+ return 1;
+ case OP_Smem:
+ /* Address may be a numeric, indirect, or an expression. */
+ return !is_immediate (operand);
+ case OP_MMRY:
+ case OP_MMRX:
+ return is_mmreg (operand);
+ case OP_SRC:
+ case OP_SRC1:
+ case OP_RND:
+ case OP_DST:
+ return is_accumulator (operand);
+ case OP_B:
+ return is_accumulator (operand) && TOUPPER (operand->buf[0]) == 'B';
+ case OP_A:
+ return is_accumulator (operand) && TOUPPER (operand->buf[0]) == 'A';
+ case OP_ARX:
+ return strncasecmp ("ar", operand->buf, 2) == 0
+ && ISDIGIT (operand->buf[2]);
+ case OP_SBIT:
+ return hash_find (sbit_hash, operand->buf) != 0 || is_absolute (operand);
+ case OP_CC:
+ return hash_find (cc_hash, operand->buf) != 0;
+ case OP_CC2:
+ return hash_find (cc2_hash, operand->buf) != 0;
+ case OP_CC3:
+ return hash_find (cc3_hash, operand->buf) != 0
+ || is_immediate (operand) || is_absolute (operand);
+ case OP_16:
+ return (is_immediate (operand) || is_absolute (operand))
+ && operand->exp.X_add_number == 16;
+ case OP_N:
+ /* Allow st0 or st1 instead of a numeric. */
+ return is_absolute (operand) || is_immediate (operand) ||
+ strcasecmp ("st0", operand->buf) == 0 ||
+ strcasecmp ("st1", operand->buf) == 0;
+ case OP_12:
+ case OP_123:
+ return is_absolute (operand) || is_immediate (operand);
+ case OP_SHFT:
+ return (is_immediate (operand) || is_absolute (operand))
+ && operand->exp.X_add_number >= 0 && operand->exp.X_add_number < 16;
+ case OP_SHIFT:
+ /* Let this one catch out-of-range values. */
+ return (is_immediate (operand) || is_absolute (operand))
+ && operand->exp.X_add_number != 16;
+ case OP_BITC:
+ case OP_031:
+ case OP_k8:
+ return is_absolute (operand) || is_immediate (operand);
+ case OP_k8u:
+ return is_immediate (operand)
+ && operand->exp.X_op == O_constant
+ && operand->exp.X_add_number >= 0
+ && operand->exp.X_add_number < 256;
+ case OP_lk:
+ case OP_lku:
+ /* Allow anything; assumes opcodes are ordered with Smem operands
+ versions first. */
+ return 1;
+ case OP_k5:
+ case OP_k3:
+ case OP_k9:
+ /* Just make sure it's an integer; check range later. */
+ return is_immediate (operand);
+ case OP_T:
+ return strcasecmp ("t", operand->buf) == 0 ||
+ strcasecmp ("treg", operand->buf) == 0;
+ case OP_TS:
+ return strcasecmp ("ts", operand->buf) == 0;
+ case OP_ASM:
+ return strcasecmp ("asm", operand->buf) == 0;
+ case OP_TRN:
+ return strcasecmp ("trn", operand->buf) == 0;
+ case OP_DP:
+ return strcasecmp ("dp", operand->buf) == 0;
+ case OP_ARP:
+ return strcasecmp ("arp", operand->buf) == 0;
+ default:
+ return 0;
+ }
+}
+
+static int
+operands_match (tic54x_insn *insn,
+ struct opstruct *operands,
+ int opcount,
+ const enum optype *refoptype,
+ int minops,
+ int maxops)
+{
+ int op = 0, refop = 0;
+
+ if (opcount == 0 && minops == 0)
+ return 1;
+
+ while (op <= maxops && refop <= maxops)
+ {
+ while (!is_type (&operands[op], OPTYPE (refoptype[refop])))
+ {
+ /* Skip an optional template operand if it doesn't agree
+ with the current operand. */
+ if (refoptype[refop] & OPT)
+ {
+ ++refop;
+ --maxops;
+ if (refop > maxops)
+ return 0;
+ }
+ else
+ return 0;
+ }
+
+ /* Save the actual operand type for later use. */
+ operands[op].type = OPTYPE (refoptype[refop]);
+ ++refop;
+ ++op;
+ /* Have we matched them all yet? */
+ if (op == opcount)
+ {
+ while (op < maxops)
+ {
+ /* If a later operand is *not* optional, no match. */
+ if ((refoptype[refop] & OPT) == 0)
+ return 0;
+ /* Flag any implicit default OP_DST operands so we know to add
+ them explicitly when encoding the operand later. */
+ if (OPTYPE (refoptype[refop]) == OP_DST)
+ insn->using_default_dst = 1;
+ ++refop;
+ ++op;
+ }
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* 16-bit direct memory address
+ Explicit dmad operands are always in last word of insn (usually second
+ word, but bumped to third if lk addressing is used)
+
+ We allow *(dmad) notation because the TI assembler allows it.
+
+ XPC_CODE:
+ 0 for 16-bit addresses
+ 1 for full 23-bit addresses
+ 2 for the upper 7 bits of a 23-bit address (LDX). */
+
+static int
+encode_dmad (tic54x_insn *insn, struct opstruct *operand, int xpc_code)
+{
+ int op = 1 + insn->is_lkaddr;
+
+ /* Only allow *(dmad) expressions; all others are invalid. */
+ if (is_indirect (operand) && operand->buf[strlen (operand->buf) - 1] != ')')
+ {
+ as_bad (_("Invalid dmad syntax '%s'"), operand->buf);
+ return 0;
+ }
+
+ insn->opcode[op].addr_expr = operand->exp;
+
+ if (insn->opcode[op].addr_expr.X_op == O_constant)
+ {
+ valueT value = insn->opcode[op].addr_expr.X_add_number;
+
+ if (xpc_code == 1)
+ {
+ insn->opcode[0].word &= 0xFF80;
+ insn->opcode[0].word |= (value >> 16) & 0x7F;
+ insn->opcode[1].word = value & 0xFFFF;
+ }
+ else if (xpc_code == 2)
+ insn->opcode[op].word = (value >> 16) & 0xFFFF;
+ else
+ insn->opcode[op].word = value;
+ }
+ else
+ {
+ /* Do the fixup later; just store the expression. */
+ insn->opcode[op].word = 0;
+ insn->opcode[op].r_nchars = 2;
+
+ if (amode == c_mode)
+ insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
+ else if (xpc_code == 1)
+ {
+ /* This relocation spans two words, so adjust accordingly. */
+ insn->opcode[0].addr_expr = operand->exp;
+ insn->opcode[0].r_type = BFD_RELOC_TIC54X_23;
+ insn->opcode[0].r_nchars = 4;
+ insn->opcode[0].unresolved = 1;
+ /* It's really 2 words, but we want to stop encoding after the
+ first, since we must encode both words at once. */
+ insn->words = 1;
+ }
+ else if (xpc_code == 2)
+ insn->opcode[op].r_type = BFD_RELOC_TIC54X_MS7_OF_23;
+ else
+ insn->opcode[op].r_type = BFD_RELOC_TIC54X_16_OF_23;
+
+ insn->opcode[op].unresolved = 1;
+ }
+
+ return 1;
+}
+
+/* 7-bit direct address encoding. */
+
+static int
+encode_address (tic54x_insn *insn, struct opstruct *operand)
+{
+ /* Assumes that dma addresses are *always* in word 0 of the opcode. */
+ insn->opcode[0].addr_expr = operand->exp;
+
+ if (operand->exp.X_op == O_constant)
+ insn->opcode[0].word |= (operand->exp.X_add_number & 0x7F);
+ else
+ {
+ if (operand->exp.X_op == O_register)
+ as_bad (_("Use the .mmregs directive to use memory-mapped register names such as '%s'"), operand->buf);
+ /* Do the fixup later; just store the expression. */
+ insn->opcode[0].r_nchars = 1;
+ insn->opcode[0].r_type = BFD_RELOC_TIC54X_PARTLS7;
+ insn->opcode[0].unresolved = 1;
+ }
+
+ return 1;
+}
+
+static int
+encode_indirect (tic54x_insn *insn, struct opstruct *operand)
+{
+ int arf;
+ int mod;
+
+ if (insn->is_lkaddr)
+ {
+ /* lk addresses always go in the second insn word. */
+ mod = ((TOUPPER (operand->buf[1]) == 'A') ? 12 :
+ (operand->buf[1] == '(') ? 15 :
+ (strchr (operand->buf, '%') != NULL) ? 14 : 13);
+ arf = ((mod == 12) ? operand->buf[3] - '0' :
+ (mod == 15) ? 0 : operand->buf[4] - '0');
+
+ insn->opcode[1].addr_expr = operand->exp;
+
+ if (operand->exp.X_op == O_constant)
+ insn->opcode[1].word = operand->exp.X_add_number;
+ else
+ {
+ insn->opcode[1].word = 0;
+ insn->opcode[1].r_nchars = 2;
+ insn->opcode[1].r_type = BFD_RELOC_TIC54X_16_OF_23;
+ insn->opcode[1].unresolved = 1;
+ }
+ }
+ else if (strncasecmp (operand->buf, "*sp (", 4) == 0)
+ {
+ /* Stack offsets look the same as 7-bit direct addressing. */
+ return encode_address (insn, operand);
+ }
+ else
+ {
+ arf = (TOUPPER (operand->buf[1]) == 'A' ?
+ operand->buf[3] : operand->buf[4]) - '0';
+
+ if (operand->buf[1] == '+')
+ {
+ mod = 3; /* *+ARx */
+ if (insn->tm->flags & FL_SMR)
+ as_warn (_("Address mode *+ARx is write-only. "
+ "Results of reading are undefined."));
+ }
+ else if (operand->buf[4] == '\0')
+ mod = 0; /* *ARx */
+ else if (operand->buf[5] == '\0')
+ mod = (operand->buf[4] == '-' ? 1 : 2); /* *ARx+ / *ARx- */
+ else if (operand->buf[6] == '\0')
+ {
+ if (operand->buf[5] == '0')
+ mod = (operand->buf[4] == '-' ? 5 : 6); /* *ARx+0 / *ARx-0 */
+ else
+ mod = (operand->buf[4] == '-' ? 8 : 10);/* *ARx+% / *ARx-% */
+ }
+ else if (TOUPPER (operand->buf[6]) == 'B')
+ mod = (operand->buf[4] == '-' ? 4 : 7); /* ARx+0B / *ARx-0B */
+ else if (TOUPPER (operand->buf[6]) == '%')
+ mod = (operand->buf[4] == '-' ? 9 : 11); /* ARx+0% / *ARx - 0% */
+ else
+ {
+ as_bad (_("Unrecognized indirect address format \"%s\""),
+ operand->buf);
+ return 0;
+ }
+ }
+
+ insn->opcode[0].word |= 0x80 | (mod << 3) | arf;
+
+ return 1;
+}
+
+static int
+encode_integer (tic54x_insn *insn,
+ struct opstruct *operand,
+ int which,
+ int min,
+ int max,
+ unsigned short mask)
+{
+ long parse, integer;
+
+ insn->opcode[which].addr_expr = operand->exp;
+
+ if (operand->exp.X_op == O_constant)
+ {
+ parse = operand->exp.X_add_number;
+ /* Hack -- fixup for 16-bit hex quantities that get converted positive
+ instead of negative. */
+ if ((parse & 0x8000) && min == -32768 && max == 32767)
+ integer = (short) parse;
+ else
+ integer = parse;
+
+ if (integer >= min && integer <= max)
+ {
+ insn->opcode[which].word |= (integer & mask);
+ return 1;
+ }
+ as_bad (_("Operand '%s' out of range (%d <= x <= %d)"),
+ operand->buf, min, max);
+ }
+ else
+ {
+ if (insn->opcode[which].addr_expr.X_op == O_constant)
+ {
+ insn->opcode[which].word |=
+ insn->opcode[which].addr_expr.X_add_number & mask;
+ }
+ else
+ {
+ /* Do the fixup later; just store the expression. */
+ bfd_reloc_code_real_type rtype =
+ (mask == 0x1FF ? BFD_RELOC_TIC54X_PARTMS9 :
+ mask == 0xFFFF ? BFD_RELOC_TIC54X_16_OF_23 :
+ mask == 0x7F ? BFD_RELOC_TIC54X_PARTLS7 : BFD_RELOC_8);
+ int size = (mask == 0x1FF || mask == 0xFFFF) ? 2 : 1;
+
+ if (rtype == BFD_RELOC_8)
+ as_bad (_("Error in relocation handling"));
+
+ insn->opcode[which].r_nchars = size;
+ insn->opcode[which].r_type = rtype;
+ insn->opcode[which].unresolved = 1;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+encode_condition (tic54x_insn *insn, struct opstruct *operand)
+{
+ symbol *cc = (symbol *) hash_find (cc_hash, operand->buf);
+ if (!cc)
+ {
+ as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
+ return 0;
+ }
+#define CC_GROUP 0x40
+#define CC_ACC 0x08
+#define CATG_A1 0x07
+#define CATG_B1 0x30
+#define CATG_A2 0x30
+#define CATG_B2 0x0C
+#define CATG_C2 0x03
+ /* Disallow group 1 conditions mixed with group 2 conditions
+ if group 1, allow only one category A and one category B
+ if group 2, allow only one each of category A, B, and C. */
+ if (((insn->opcode[0].word & 0xFF) != 0))
+ {
+ if ((insn->opcode[0].word & CC_GROUP) != (cc->value & CC_GROUP))
+ {
+ as_bad (_("Condition \"%s\" does not match preceding group"),
+ operand->buf);
+ return 0;
+ }
+ if (insn->opcode[0].word & CC_GROUP)
+ {
+ if ((insn->opcode[0].word & CC_ACC) != (cc->value & CC_ACC))
+ {
+ as_bad (_("Condition \"%s\" uses a different accumulator from "
+ "a preceding condition"),
+ operand->buf);
+ return 0;
+ }
+ if ((insn->opcode[0].word & CATG_A1) && (cc->value & CATG_A1))
+ {
+ as_bad (_("Only one comparison conditional allowed"));
+ return 0;
+ }
+ if ((insn->opcode[0].word & CATG_B1) && (cc->value & CATG_B1))
+ {
+ as_bad (_("Only one overflow conditional allowed"));
+ return 0;
+ }
+ }
+ else if ( ((insn->opcode[0].word & CATG_A2) && (cc->value & CATG_A2))
+ || ((insn->opcode[0].word & CATG_B2) && (cc->value & CATG_B2))
+ || ((insn->opcode[0].word & CATG_C2) && (cc->value & CATG_C2)))
+ {
+ as_bad (_("Duplicate %s conditional"), operand->buf);
+ return 0;
+ }
+ }
+
+ insn->opcode[0].word |= cc->value;
+ return 1;
+}
+
+static int
+encode_cc3 (tic54x_insn *insn, struct opstruct *operand)
+{
+ symbol *cc3 = (symbol *) hash_find (cc3_hash, operand->buf);
+ int value = cc3 ? cc3->value : operand->exp.X_add_number << 8;
+
+ if ((value & 0x0300) != value)
+ {
+ as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
+ return 0;
+ }
+ insn->opcode[0].word |= value;
+ return 1;
+}
+
+static int
+encode_arx (tic54x_insn *insn, struct opstruct *operand)
+{
+ int arf = strlen (operand->buf) >= 3 ? operand->buf[2] - '0' : -1;
+
+ if (strncasecmp ("ar", operand->buf, 2) || arf < 0 || arf > 7)
+ {
+ as_bad (_("Invalid auxiliary register (use AR0-AR7)"));
+ return 0;
+ }
+ insn->opcode[0].word |= arf;
+ return 1;
+}
+
+static int
+encode_cc2 (tic54x_insn *insn, struct opstruct *operand)
+{
+ symbol *cc2 = (symbol *) hash_find (cc2_hash, operand->buf);
+
+ if (!cc2)
+ {
+ as_bad (_("Unrecognized condition code \"%s\""), operand->buf);
+ return 0;
+ }
+ insn->opcode[0].word |= cc2->value;
+ return 1;
+}
+
+static int
+encode_operand (tic54x_insn *insn, enum optype type, struct opstruct *operand)
+{
+ int ext = (insn->tm->flags & FL_EXT) != 0;
+
+ if (type == OP_MMR && operand->exp.X_op != O_constant)
+ {
+ /* Disallow long-constant addressing for memory-mapped addressing. */
+ if (insn->is_lkaddr)
+ {
+ as_bad (_("lk addressing modes are invalid for memory-mapped "
+ "register addressing"));
+ return 0;
+ }
+ type = OP_Smem;
+ /* Warn about *+ARx when used with MMR operands. */
+ if (strncasecmp (operand->buf, "*+ar", 4) == 0)
+ {
+ as_warn (_("Address mode *+ARx is not allowed in memory-mapped "
+ "register addressing. Resulting behavior is "
+ "undefined."));
+ }
+ }
+
+ switch (type)
+ {
+ case OP_None:
+ return 1;
+ case OP_dmad:
+ /* 16-bit immediate value. */
+ return encode_dmad (insn, operand, 0);
+ case OP_SRC:
+ if (TOUPPER (*operand->buf) == 'B')
+ {
+ insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 9);
+ if (insn->using_default_dst)
+ insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 8);
+ }
+ return 1;
+ case OP_RND:
+ /* Make sure this agrees with the OP_DST operand. */
+ if (!((TOUPPER (operand->buf[0]) == 'B') ^
+ ((insn->opcode[0].word & (1 << 8)) != 0)))
+ {
+ as_bad (_("Destination accumulator for each part of this parallel "
+ "instruction must be different"));
+ return 0;
+ }
+ return 1;
+ case OP_SRC1:
+ case OP_DST:
+ if (TOUPPER (operand->buf[0]) == 'B')
+ insn->opcode[ext ? (1 + insn->is_lkaddr) : 0].word |= (1 << 8);
+ return 1;
+ case OP_Xmem:
+ case OP_Ymem:
+ {
+ int mod = (operand->buf[4] == '\0' ? 0 : /* *arx */
+ operand->buf[4] == '-' ? 1 : /* *arx- */
+ operand->buf[5] == '\0' ? 2 : 3); /* *arx+, *arx+0% */
+ int arf = operand->buf[3] - '0' - 2;
+ int code = (mod << 2) | arf;
+ insn->opcode[0].word |= (code << (type == OP_Xmem ? 4 : 0));
+ return 1;
+ }
+ case OP_Lmem:
+ case OP_Smem:
+ if (!is_indirect (operand))
+ return encode_address (insn, operand);
+ /* Fall through. */
+ case OP_Sind:
+ return encode_indirect (insn, operand);
+ case OP_xpmad_ms7:
+ return encode_dmad (insn, operand, 2);
+ case OP_xpmad:
+ return encode_dmad (insn, operand, 1);
+ case OP_PA:
+ case OP_pmad:
+ return encode_dmad (insn, operand, 0);
+ case OP_ARX:
+ return encode_arx (insn, operand);
+ case OP_MMRX:
+ case OP_MMRY:
+ case OP_MMR:
+ {
+ int value = operand->exp.X_add_number;
+
+ if (type == OP_MMR)
+ insn->opcode[0].word |= value;
+ else
+ {
+ if (value < 16 || value > 24)
+ {
+ as_bad (_("Memory mapped register \"%s\" out of range"),
+ operand->buf);
+ return 0;
+ }
+ if (type == OP_MMRX)
+ insn->opcode[0].word |= (value - 16) << 4;
+ else
+ insn->opcode[0].word |= (value - 16);
+ }
+ return 1;
+ }
+ case OP_B:
+ case OP_A:
+ return 1;
+ case OP_SHFT:
+ return encode_integer (insn, operand, ext + insn->is_lkaddr,
+ 0, 15, 0xF);
+ case OP_SHIFT:
+ return encode_integer (insn, operand, ext + insn->is_lkaddr,
+ -16, 15, 0x1F);
+ case OP_lk:
+ return encode_integer (insn, operand, 1 + insn->is_lkaddr,
+ -32768, 32767, 0xFFFF);
+ case OP_CC:
+ return encode_condition (insn, operand);
+ case OP_CC2:
+ return encode_cc2 (insn, operand);
+ case OP_CC3:
+ return encode_cc3 (insn, operand);
+ case OP_BITC:
+ return encode_integer (insn, operand, 0, 0, 15, 0xF);
+ case OP_k8:
+ return encode_integer (insn, operand, 0, -128, 127, 0xFF);
+ case OP_123:
+ {
+ int value = operand->exp.X_add_number;
+ int code;
+ if (value < 1 || value > 3)
+ {
+ as_bad (_("Invalid operand (use 1, 2, or 3)"));
+ return 0;
+ }
+ code = value == 1 ? 0 : value == 2 ? 0x2 : 0x1;
+ insn->opcode[0].word |= (code << 8);
+ return 1;
+ }
+ case OP_031:
+ return encode_integer (insn, operand, 0, 0, 31, 0x1F);
+ case OP_k8u:
+ return encode_integer (insn, operand, 0, 0, 255, 0xFF);
+ case OP_lku:
+ return encode_integer (insn, operand, 1 + insn->is_lkaddr,
+ 0, 65535, 0xFFFF);
+ case OP_SBIT:
+ {
+ symbol *sbit = (symbol *) hash_find (sbit_hash, operand->buf);
+ int value = is_absolute (operand) ?
+ operand->exp.X_add_number : (sbit ? sbit->value : -1);
+ int reg = 0;
+
+ if (insn->opcount == 1)
+ {
+ if (!sbit)
+ {
+ as_bad (_("A status register or status bit name is required"));
+ return 0;
+ }
+ /* Guess the register based on the status bit; "ovb" is the last
+ status bit defined for st0. */
+ if (sbit > (symbol *) hash_find (sbit_hash, "ovb"))
+ reg = 1;
+ }
+ if (value == -1)
+ {
+ as_bad (_("Unrecognized status bit \"%s\""), operand->buf);
+ return 0;
+ }
+ insn->opcode[0].word |= value;
+ insn->opcode[0].word |= (reg << 9);
+ return 1;
+ }
+ case OP_N:
+ if (strcasecmp (operand->buf, "st0") == 0
+ || strcasecmp (operand->buf, "st1") == 0)
+ {
+ insn->opcode[0].word |=
+ ((unsigned short) (operand->buf[2] - '0')) << 9;
+ return 1;
+ }
+ else if (operand->exp.X_op == O_constant
+ && (operand->exp.X_add_number == 0
+ || operand->exp.X_add_number == 1))
+ {
+ insn->opcode[0].word |=
+ ((unsigned short) (operand->exp.X_add_number)) << 9;
+ return 1;
+ }
+ as_bad (_("Invalid status register \"%s\""), operand->buf);
+ return 0;
+ case OP_k5:
+ return encode_integer (insn, operand, 0, -16, 15, 0x1F);
+ case OP_k3:
+ return encode_integer (insn, operand, 0, 0, 7, 0x7);
+ case OP_k9:
+ return encode_integer (insn, operand, 0, 0, 0x1FF, 0x1FF);
+ case OP_12:
+ if (operand->exp.X_add_number != 1
+ && operand->exp.X_add_number != 2)
+ {
+ as_bad (_("Operand \"%s\" out of range (use 1 or 2)"), operand->buf);
+ return 0;
+ }
+ insn->opcode[0].word |= (operand->exp.X_add_number - 1) << 9;
+ return 1;
+ case OP_16:
+ case OP_T:
+ case OP_TS:
+ case OP_ASM:
+ case OP_TRN:
+ case OP_DP:
+ case OP_ARP:
+ /* No encoding necessary. */
+ return 1;
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+emit_insn (tic54x_insn *insn)
+{
+ int i;
+ flagword oldflags = bfd_get_section_flags (stdoutput, now_seg);
+ flagword flags = oldflags | SEC_CODE;
+
+ if (! bfd_set_section_flags (stdoutput, now_seg, flags))
+ as_warn (_("error setting flags for \"%s\": %s"),
+ bfd_section_name (stdoutput, now_seg),
+ bfd_errmsg (bfd_get_error ()));
+
+ for (i = 0; i < insn->words; i++)
+ {
+ int size = (insn->opcode[i].unresolved
+ && insn->opcode[i].r_type == BFD_RELOC_TIC54X_23) ? 4 : 2;
+ char *p = frag_more (size);
+
+ if (size == 2)
+ md_number_to_chars (p, (valueT) insn->opcode[i].word, 2);
+ else
+ md_number_to_chars (p, (valueT) insn->opcode[i].word << 16, 4);
+
+ if (insn->opcode[i].unresolved)
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ insn->opcode[i].r_nchars, &insn->opcode[i].addr_expr,
+ FALSE, insn->opcode[i].r_type);
+ }
+}
+
+/* Convert the operand strings into appropriate opcode values
+ return the total number of words used by the instruction. */
+
+static int
+build_insn (tic54x_insn *insn)
+{
+ int i;
+
+ /* Only non-parallel instructions support lk addressing. */
+ if (!(insn->tm->flags & FL_PAR))
+ {
+ for (i = 0; i < insn->opcount; i++)
+ {
+ if ((OPTYPE (insn->operands[i].type) == OP_Smem
+ || OPTYPE (insn->operands[i].type) == OP_Lmem
+ || OPTYPE (insn->operands[i].type) == OP_Sind)
+ && strchr (insn->operands[i].buf, '(')
+ /* Don't mistake stack-relative addressing for lk addressing. */
+ && strncasecmp (insn->operands[i].buf, "*sp (", 4) != 0)
+ {
+ insn->is_lkaddr = 1;
+ insn->lkoperand = i;
+ break;
+ }
+ }
+ }
+ insn->words = insn->tm->words + insn->is_lkaddr;
+
+ insn->opcode[0].word = insn->tm->opcode;
+ if (insn->tm->flags & FL_EXT)
+ insn->opcode[1 + insn->is_lkaddr].word = insn->tm->opcode2;
+
+ for (i = 0; i < insn->opcount; i++)
+ {
+ enum optype type = insn->operands[i].type;
+
+ if (!encode_operand (insn, type, &insn->operands[i]))
+ return 0;
+ }
+ if (insn->tm->flags & FL_PAR)
+ for (i = 0; i < insn->paropcount; i++)
+ {
+ enum optype partype = insn->paroperands[i].type;
+
+ if (!encode_operand (insn, partype, &insn->paroperands[i]))
+ return 0;
+ }
+
+ emit_insn (insn);
+
+ return insn->words;
+}
+
+static int
+optimize_insn (tic54x_insn *insn)
+{
+ /* Optimize some instructions, helping out the brain-dead programmer. */
+#define is_zero(op) ((op).exp.X_op == O_constant && (op).exp.X_add_number == 0)
+ if (strcasecmp (insn->tm->name, "add") == 0)
+ {
+ if (insn->opcount > 1
+ && is_accumulator (&insn->operands[insn->opcount - 2])
+ && is_accumulator (&insn->operands[insn->opcount - 1])
+ && strcasecmp (insn->operands[insn->opcount - 2].buf,
+ insn->operands[insn->opcount - 1].buf) == 0)
+ {
+ --insn->opcount;
+ insn->using_default_dst = 1;
+ return 1;
+ }
+
+ /* Try to collapse if Xmem and shift count is zero. */
+ if ((OPTYPE (insn->tm->operand_types[0]) == OP_Xmem
+ && OPTYPE (insn->tm->operand_types[1]) == OP_SHFT
+ && is_zero (insn->operands[1]))
+ /* Or if Smem, shift is zero or absent, and SRC == DST. */
+ || (OPTYPE (insn->tm->operand_types[0]) == OP_Smem
+ && OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
+ && is_type (&insn->operands[1], OP_SHIFT)
+ && is_zero (insn->operands[1]) && insn->opcount == 3))
+ {
+ insn->operands[1] = insn->operands[2];
+ insn->opcount = 2;
+ return 1;
+ }
+ }
+ else if (strcasecmp (insn->tm->name, "ld") == 0)
+ {
+ if (insn->opcount == 3 && insn->operands[0].type != OP_SRC)
+ {
+ if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
+ || OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)
+ && is_zero (insn->operands[1])
+ && (OPTYPE (insn->tm->operand_types[0]) != OP_lk
+ || (insn->operands[0].exp.X_op == O_constant
+ && insn->operands[0].exp.X_add_number <= 255
+ && insn->operands[0].exp.X_add_number >= 0)))
+ {
+ insn->operands[1] = insn->operands[2];
+ insn->opcount = 2;
+ return 1;
+ }
+ }
+ }
+ else if (strcasecmp (insn->tm->name, "sth") == 0
+ || strcasecmp (insn->tm->name, "stl") == 0)
+ {
+ if ((OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT
+ || OPTYPE (insn->tm->operand_types[1]) == OP_SHFT)
+ && is_zero (insn->operands[1]))
+ {
+ insn->operands[1] = insn->operands[2];
+ insn->opcount = 2;
+ return 1;
+ }
+ }
+ else if (strcasecmp (insn->tm->name, "sub") == 0)
+ {
+ if (insn->opcount > 1
+ && is_accumulator (&insn->operands[insn->opcount - 2])
+ && is_accumulator (&insn->operands[insn->opcount - 1])
+ && strcasecmp (insn->operands[insn->opcount - 2].buf,
+ insn->operands[insn->opcount - 1].buf) == 0)
+ {
+ --insn->opcount;
+ insn->using_default_dst = 1;
+ return 1;
+ }
+
+ if ( ((OPTYPE (insn->tm->operand_types[0]) == OP_Smem
+ && OPTYPE (insn->tm->operand_types[1]) == OP_SHIFT)
+ || (OPTYPE (insn->tm->operand_types[0]) == OP_Xmem
+ && OPTYPE (insn->tm->operand_types[1]) == OP_SHFT))
+ && is_zero (insn->operands[1])
+ && insn->opcount == 3)
+ {
+ insn->operands[1] = insn->operands[2];
+ insn->opcount = 2;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Find a matching template if possible, and get the operand strings. */
+
+static int
+tic54x_parse_insn (tic54x_insn *insn, char *line)
+{
+ insn->tm = (insn_template *) hash_find (op_hash, insn->mnemonic);
+ if (!insn->tm)
+ {
+ as_bad (_("Unrecognized instruction \"%s\""), insn->mnemonic);
+ return 0;
+ }
+
+ insn->opcount = get_operands (insn->operands, line);
+ if (insn->opcount < 0)
+ return 0;
+
+ /* Check each variation of operands for this mnemonic. */
+ while (insn->tm->name && strcasecmp (insn->tm->name, insn->mnemonic) == 0)
+ {
+ if (insn->opcount >= insn->tm->minops
+ && insn->opcount <= insn->tm->maxops
+ && operands_match (insn, &insn->operands[0], insn->opcount,
+ insn->tm->operand_types,
+ insn->tm->minops, insn->tm->maxops))
+ {
+ /* SUCCESS! now try some optimizations. */
+ if (optimize_insn (insn))
+ {
+ insn->tm = (insn_template *) hash_find (op_hash,
+ insn->mnemonic);
+ continue;
+ }
+
+ return 1;
+ }
+ ++(insn->tm);
+ }
+ as_bad (_("Unrecognized operand list '%s' for instruction '%s'"),
+ line, insn->mnemonic);
+ return 0;
+}
+
+/* We set this in start_line_hook, 'cause if we do a line replacement, we
+ won't be able to see the next line. */
+static int parallel_on_next_line_hint = 0;
+
+/* See if this is part of a parallel instruction
+ Look for a subsequent line starting with "||". */
+
+static int
+next_line_shows_parallel (char *next_line)
+{
+ /* Look for the second half. */
+ while (ISSPACE (*next_line))
+ ++next_line;
+
+ return (next_line[0] == PARALLEL_SEPARATOR
+ && next_line[1] == PARALLEL_SEPARATOR);
+}
+
+static int
+tic54x_parse_parallel_insn_firstline (tic54x_insn *insn, char *line)
+{
+ insn->tm = (insn_template *) hash_find (parop_hash, insn->mnemonic);
+ if (!insn->tm)
+ {
+ as_bad (_("Unrecognized parallel instruction \"%s\""),
+ insn->mnemonic);
+ return 0;
+ }
+
+ while (insn->tm->name && strcasecmp (insn->tm->name,
+ insn->mnemonic) == 0)
+ {
+ insn->opcount = get_operands (insn->operands, line);
+ if (insn->opcount < 0)
+ return 0;
+ if (insn->opcount == 2
+ && operands_match (insn, &insn->operands[0], insn->opcount,
+ insn->tm->operand_types, 2, 2))
+ {
+ return 1;
+ }
+ ++(insn->tm);
+ }
+ /* Didn't find a matching parallel; try for a normal insn. */
+ return 0;
+}
+
+/* Parse the second line of a two-line parallel instruction. */
+
+static int
+tic54x_parse_parallel_insn_lastline (tic54x_insn *insn, char *line)
+{
+ int valid_mnemonic = 0;
+
+ insn->paropcount = get_operands (insn->paroperands, line);
+ while (insn->tm->name && strcasecmp (insn->tm->name,
+ insn->mnemonic) == 0)
+ {
+ if (strcasecmp (insn->tm->parname, insn->parmnemonic) == 0)
+ {
+ valid_mnemonic = 1;
+
+ if (insn->paropcount >= insn->tm->minops
+ && insn->paropcount <= insn->tm->maxops
+ && operands_match (insn, insn->paroperands,
+ insn->paropcount,
+ insn->tm->paroperand_types,
+ insn->tm->minops, insn->tm->maxops))
+ return 1;
+ }
+ ++(insn->tm);
+ }
+ if (valid_mnemonic)
+ as_bad (_("Invalid operand (s) for parallel instruction \"%s\""),
+ insn->parmnemonic);
+ else
+ as_bad (_("Unrecognized parallel instruction combination \"%s || %s\""),
+ insn->mnemonic, insn->parmnemonic);
+
+ return 0;
+}
+
+/* If quotes found, return copy of line up to closing quote;
+ otherwise up until terminator.
+ If it's a string, pass as-is; otherwise attempt substitution symbol
+ replacement on the value. */
+
+static char *
+subsym_get_arg (char *line, char *terminators, char **str, int nosub)
+{
+ char *ptr = line;
+ char *endp;
+ int is_string = *line == '"';
+ int is_char = ISDIGIT (*line);
+
+ if (is_char)
+ {
+ while (ISDIGIT (*ptr))
+ ++ptr;
+ endp = ptr;
+ *str = xmalloc (ptr - line + 1);
+ strncpy (*str, line, ptr - line);
+ (*str)[ptr - line] = 0;
+ }
+ else if (is_string)
+ {
+ char *savedp = input_line_pointer;
+ int len;
+
+ input_line_pointer = ptr;
+ *str = demand_copy_C_string (&len);
+ endp = input_line_pointer;
+ input_line_pointer = savedp;
+
+ /* Do forced substitutions if requested. */
+ if (!nosub && **str == ':')
+ *str = subsym_substitute (*str, 1);
+ }
+ else
+ {
+ char *term = terminators;
+ char *value = NULL;
+
+ while (*ptr && *ptr != *term)
+ {
+ if (!*term)
+ {
+ term = terminators;
+ ++ptr;
+ }
+ else
+ ++term;
+ }
+ endp = ptr;
+ *str = xmalloc (ptr - line + 1);
+ strncpy (*str, line, ptr - line);
+ (*str)[ptr - line] = 0;
+ /* Do simple substitution, if available. */
+ if (!nosub && (value = subsym_lookup (*str, macro_level)) != NULL)
+ *str = value;
+ }
+
+ return endp;
+}
+
+/* Replace the given substitution string.
+ We start at the innermost macro level, so that existing locals remain local
+ Note: we're treating macro args identically to .var's; I don't know if
+ that's compatible w/TI's assembler. */
+
+static void
+subsym_create_or_replace (char *name, char *value)
+{
+ int i;
+
+ for (i = macro_level; i > 0; i--)
+ {
+ if (hash_find (subsym_hash[i], name))
+ {
+ hash_replace (subsym_hash[i], name, value);
+ return;
+ }
+ }
+ if (hash_find (subsym_hash[0], name))
+ hash_replace (subsym_hash[0], name, value);
+ else
+ hash_insert (subsym_hash[0], name, value);
+}
+
+/* Look up the substitution string replacement for the given symbol.
+ Start with the innermost macro substitution table given and work
+ outwards. */
+
+static char *
+subsym_lookup (char *name, int nest_level)
+{
+ char *value = hash_find (subsym_hash[nest_level], name);
+
+ if (value || nest_level == 0)
+ return value;
+
+ return subsym_lookup (name, nest_level - 1);
+}
+
+/* Do substitution-symbol replacement on the given line (recursively).
+ return the argument if no substitution was done
+
+ Also look for built-in functions ($func (arg)) and local labels.
+
+ If FORCED is set, look for forced substitutions of the form ':SYMBOL:'. */
+
+static char *
+subsym_substitute (char *line, int forced)
+{
+ /* For each apparent symbol, see if it's a substitution symbol, and if so,
+ replace it in the input. */
+ char *replacement; /* current replacement for LINE. */
+ char *head; /* Start of line. */
+ char *ptr; /* Current examination point. */
+ int changed = 0; /* Did we make a substitution? */
+ int eval_line = 0; /* Is this line a .eval/.asg statement? */
+ int eval_symbol = 0; /* Are we in the middle of the symbol for
+ .eval/.asg? */
+ char *eval_end = NULL;
+ int recurse = 1;
+ int line_conditional = 0;
+ char *tmp;
+
+ /* Work with a copy of the input line. */
+ replacement = xmalloc (strlen (line) + 1);
+ strcpy (replacement, line);
+
+ ptr = head = replacement;
+
+ /* Flag lines where we might need to replace a single '=' with two;
+ GAS uses single '=' to assign macro args values, and possibly other
+ places, so limit what we replace. */
+ if (strstr (line, ".if")
+ || strstr (line, ".elseif")
+ || strstr (line, ".break"))
+ line_conditional = 1;
+
+ /* Watch out for .eval, so that we avoid doing substitution on the
+ symbol being assigned a value. */
+ if (strstr (line, ".eval") || strstr (line, ".asg"))
+ eval_line = 1;
+
+ /* If it's a macro definition, don't do substitution on the argument
+ names. */
+ if (strstr (line, ".macro"))
+ return line;
+
+ while (!is_end_of_line[(int) *ptr])
+ {
+ int current_char = *ptr;
+
+ /* Need to update this since LINE may have been modified. */
+ if (eval_line)
+ eval_end = strrchr (ptr, ',');
+
+ /* Replace triple double quotes with bounding quote/escapes. */
+ if (current_char == '"' && ptr[1] == '"' && ptr[2] == '"')
+ {
+ ptr[1] = '\\';
+ tmp = strstr (ptr + 2, "\"\"\"");
+ if (tmp)
+ tmp[0] = '\\';
+ changed = 1;
+ }
+
+ /* Replace a single '=' with a '==';
+ for compatibility with older code only. */
+ if (line_conditional && current_char == '=')
+ {
+ if (ptr[1] == '=')
+ {
+ ptr += 2;
+ continue;
+ }
+ *ptr++ = '\0';
+ tmp = xmalloc (strlen (head) + 2 + strlen (ptr) + 1);
+ sprintf (tmp, "%s==%s", head, ptr);
+ /* Continue examining after the '=='. */
+ ptr = tmp + strlen (head) + 2;
+ free (replacement);
+ head = replacement = tmp;
+ changed = 1;
+ }
+
+ /* Flag when we've reached the symbol part of .eval/.asg. */
+ if (eval_line && ptr >= eval_end)
+ eval_symbol = 1;
+
+ /* For each apparent symbol, see if it's a substitution symbol, and if
+ so, replace it in the input. */
+ if ((forced && current_char == ':')
+ || (!forced && is_name_beginner (current_char)))
+ {
+ char *name; /* Symbol to be replaced. */
+ char *savedp = input_line_pointer;
+ int c;
+ char *value = NULL;
+ char *tail; /* Rest of line after symbol. */
+
+ /* Skip the colon. */
+ if (forced)
+ ++ptr;
+
+ name = input_line_pointer = ptr;
+ c = get_symbol_end ();
+ /* '?' is not normally part of a symbol, but it IS part of a local
+ label. */
+ if (c == '?')
+ {
+ *input_line_pointer++ = c;
+ c = *input_line_pointer;
+ *input_line_pointer = '\0';
+ }
+ /* Avoid infinite recursion; if a symbol shows up a second time for
+ substitution, leave it as is. */
+ if (hash_find (subsym_recurse_hash, name) == NULL)
+ value = subsym_lookup (name, macro_level);
+ else
+ as_warn (_("%s symbol recursion stopped at "
+ "second appearance of '%s'"),
+ forced ? "Forced substitution" : "Substitution", name);
+ ptr = tail = input_line_pointer;
+ input_line_pointer = savedp;
+
+ /* Check for local labels; replace them with the appropriate
+ substitution. */
+ if ((*name == '$' && ISDIGIT (name[1]) && name[2] == '\0')
+ || name[strlen (name) - 1] == '?')
+ {
+ /* Use an existing identifier for that label if, available, or
+ create a new, unique identifier. */
+ value = hash_find (local_label_hash[macro_level], name);
+ if (value == NULL)
+ {
+ char digit[11];
+ char *namecopy = strcpy (xmalloc (strlen (name) + 1), name);
+
+ value = strcpy (xmalloc (strlen (name) + sizeof (digit) + 1),
+ name);
+ if (*value != '$')
+ value[strlen (value) - 1] = '\0';
+ sprintf (digit, ".%d", local_label_id++);
+ strcat (value, digit);
+ hash_insert (local_label_hash[macro_level], namecopy, value);
+ }
+ /* Indicate where to continue looking for substitutions. */
+ ptr = tail;
+ }
+ /* Check for built-in subsym and math functions. */
+ else if (value != NULL && *name == '$')
+ {
+ subsym_proc_entry *entry = (subsym_proc_entry *) value;
+ math_proc_entry *math_entry = hash_find (math_hash, name);
+ char *arg1, *arg2 = NULL;
+
+ *ptr = c;
+ if (entry == NULL)
+ {
+ as_bad (_("Unrecognized substitution symbol function"));
+ break;
+ }
+ else if (*ptr != '(')
+ {
+ as_bad (_("Missing '(' after substitution symbol function"));
+ break;
+ }
+ ++ptr;
+ if (math_entry != NULL)
+ {
+ float farg1, farg2 = 0;
+ volatile float fresult;
+
+ farg1 = (float) strtod (ptr, &ptr);
+ if (math_entry->nargs == 2)
+ {
+ if (*ptr++ != ',')
+ {
+ as_bad (_("Expecting second argument"));
+ break;
+ }
+ farg2 = (float) strtod (ptr, &ptr);
+ }
+ fresult = (*math_entry->proc) (farg1, farg2);
+ value = xmalloc (128);
+ if (math_entry->int_return)
+ sprintf (value, "%d", (int) fresult);
+ else
+ sprintf (value, "%f", fresult);
+ if (*ptr++ != ')')
+ {
+ as_bad (_("Extra junk in function call, expecting ')'"));
+ break;
+ }
+ /* Don't bother recursing; the replacement isn't a
+ symbol. */
+ recurse = 0;
+ }
+ else
+ {
+ int val;
+ int arg_type[2] = { *ptr == '"' , 0 };
+ int ismember = !strcmp (entry->name, "$ismember");
+
+ /* Parse one or two args, which must be a substitution
+ symbol, string or a character-string constant. */
+ /* For all functions, a string or substitution symbol may be
+ used, with the following exceptions:
+ firstch/lastch: 2nd arg must be character constant
+ ismember: both args must be substitution symbols. */
+ ptr = subsym_get_arg (ptr, ",)", &arg1, ismember);
+ if (!arg1)
+ break;
+ if (entry->nargs == 2)
+ {
+ if (*ptr++ != ',')
+ {
+ as_bad (_("Function expects two arguments"));
+ break;
+ }
+ /* Character constants are converted to numerics
+ by the preprocessor. */
+ arg_type[1] = (ISDIGIT (*ptr)) ? 2 : (*ptr == '"');
+ ptr = subsym_get_arg (ptr, ")", &arg2, ismember);
+ }
+ /* Args checking. */
+ if ((!strcmp (entry->name, "$firstch")
+ || !strcmp (entry->name, "$lastch"))
+ && arg_type[1] != 2)
+ {
+ as_bad (_("Expecting character constant argument"));
+ break;
+ }
+ if (ismember
+ && (arg_type[0] != 0 || arg_type[1] != 0))
+ {
+ as_bad (_("Both arguments must be substitution symbols"));
+ break;
+ }
+ if (*ptr++ != ')')
+ {
+ as_bad (_("Extra junk in function call, expecting ')'"));
+ break;
+ }
+ val = (*entry->proc) (arg1, arg2);
+ value = xmalloc (64);
+ sprintf (value, "%d", val);
+ }
+ /* Fix things up to replace the entire expression, not just the
+ function name. */
+ tail = ptr;
+ c = *tail;
+ }
+
+ if (value != NULL && !eval_symbol)
+ {
+ /* Replace the symbol with its string replacement and
+ continue. Recursively replace VALUE until either no
+ substitutions are performed, or a substitution that has been
+ previously made is encountered again.
+
+ put the symbol into the recursion hash table so we only
+ try to replace a symbol once. */
+ if (recurse)
+ {
+ hash_insert (subsym_recurse_hash, name, name);
+ value = subsym_substitute (value, macro_level > 0);
+ hash_delete (subsym_recurse_hash, name, FALSE);
+ }
+
+ /* Temporarily zero-terminate where the symbol started. */
+ *name = 0;
+ if (forced)
+ {
+ if (c == '(')
+ {
+ /* Subscripted substitution symbol -- use just the
+ indicated portion of the string; the description
+ kinda indicates that forced substitution is not
+ supposed to be recursive, but I'm not sure. */
+ unsigned beg, len = 1; /* default to a single char */
+ char *newval = strcpy (xmalloc (strlen (value) + 1),
+ value);
+
+ savedp = input_line_pointer;
+ input_line_pointer = tail + 1;
+ beg = get_absolute_expression ();
+ if (beg < 1)
+ {
+ as_bad (_("Invalid subscript (use 1 to %d)"),
+ (int) strlen (value));
+ break;
+ }
+ if (*input_line_pointer == ',')
+ {
+ ++input_line_pointer;
+ len = get_absolute_expression ();
+ if (beg + len > strlen (value))
+ {
+ as_bad (_("Invalid length (use 0 to %d"),
+ (int) strlen (value) - beg);
+ break;
+ }
+ }
+ newval += beg - 1;
+ newval[len] = 0;
+ tail = input_line_pointer;
+ if (*tail++ != ')')
+ {
+ as_bad (_("Missing ')' in subscripted substitution "
+ "symbol expression"));
+ break;
+ }
+ c = *tail;
+ input_line_pointer = savedp;
+
+ value = newval;
+ }
+ name[-1] = 0;
+ }
+ tmp = xmalloc (strlen (head) + strlen (value) +
+ strlen (tail + 1) + 2);
+ strcpy (tmp, head);
+ strcat (tmp, value);
+ /* Make sure forced substitutions are properly terminated. */
+ if (forced)
+ {
+ if (c != ':')
+ {
+ as_bad (_("Missing forced substitution terminator ':'"));
+ break;
+ }
+ ++tail;
+ }
+ else
+ /* Restore the character after the symbol end. */
+ *tail = c;
+ strcat (tmp, tail);
+ /* Continue examining after the replacement value. */
+ ptr = tmp + strlen (head) + strlen (value);
+ free (replacement);
+ head = replacement = tmp;
+ changed = 1;
+ }
+ else
+ *ptr = c;
+ }
+ else
+ {
+ ++ptr;
+ }
+ }
+
+ if (changed)
+ return replacement;
+ else
+ return line;
+}
+
+/* We use this to handle substitution symbols
+ hijack input_line_pointer, replacing it with our substituted string.
+
+ .sslist should enable listing the line after replacements are made...
+
+ returns the new buffer limit. */
+
+void
+tic54x_start_line_hook (void)
+{
+ char *line, *endp;
+ char *replacement = NULL;
+
+ /* Work with a copy of the input line, including EOL char. */
+ endp = input_line_pointer;
+ while (!is_end_of_line[(int) *endp++])
+ ;
+ line = xmalloc (endp - input_line_pointer + 1);
+ strncpy (line, input_line_pointer, endp - input_line_pointer + 1);
+ line[endp - input_line_pointer] = 0;
+
+ /* Scan ahead for parallel insns. */
+ parallel_on_next_line_hint = next_line_shows_parallel (endp);
+
+ /* If within a macro, first process forced replacements. */
+ if (macro_level > 0)
+ replacement = subsym_substitute (line, 1);
+ else
+ replacement = line;
+ replacement = subsym_substitute (replacement, 0);
+
+ if (replacement != line)
+ {
+ char *tmp = replacement;
+ char *comment = strchr (replacement, ';');
+ char endc = replacement[strlen (replacement) - 1];
+
+ /* Clean up the replacement; we'd prefer to have this done by the
+ standard preprocessing equipment (maybe do_scrub_chars?)
+ but for now, do a quick-and-dirty. */
+ if (comment != NULL)
+ {
+ comment[0] = endc;
+ comment[1] = 0;
+ --comment;
+ }
+ else
+ comment = replacement + strlen (replacement) - 1;
+
+ /* Trim trailing whitespace. */
+ while (ISSPACE (*comment))
+ {
+ comment[0] = endc;
+ comment[1] = 0;
+ --comment;
+ }
+
+ /* Compact leading whitespace. */
+ while (ISSPACE (tmp[0]) && ISSPACE (tmp[1]))
+ ++tmp;
+
+ input_line_pointer = endp;
+ input_scrub_insert_line (tmp);
+ free (replacement);
+ free (line);
+ /* Keep track of whether we've done a substitution. */
+ substitution_line = 1;
+ }
+ else
+ {
+ /* No change. */
+ free (line);
+ substitution_line = 0;
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+void
+md_assemble (char *line)
+{
+ static int repeat_slot = 0;
+ static int delay_slots = 0; /* How many delay slots left to fill? */
+ static int is_parallel = 0;
+ static tic54x_insn insn;
+ char *lptr;
+ char *savedp = input_line_pointer;
+ int c;
+
+ input_line_pointer = line;
+ c = get_symbol_end ();
+
+ if (cpu == VNONE)
+ cpu = V542;
+ if (address_mode_needs_set)
+ {
+ set_address_mode (amode);
+ address_mode_needs_set = 0;
+ }
+ if (cpu_needs_set)
+ {
+ set_cpu (cpu);
+ cpu_needs_set = 0;
+ }
+ assembly_begun = 1;
+
+ if (is_parallel)
+ {
+ is_parallel = 0;
+
+ strcpy (insn.parmnemonic, line);
+ lptr = input_line_pointer;
+ *lptr = c;
+ input_line_pointer = savedp;
+
+ if (tic54x_parse_parallel_insn_lastline (&insn, lptr))
+ {
+ int words = build_insn (&insn);
+
+ if (delay_slots != 0)
+ {
+ if (words > delay_slots)
+ {
+ as_bad (_("Instruction does not fit in available delay "
+ "slots (%d-word insn, %d slots left)"),
+ words, delay_slots);
+ delay_slots = 0;
+ return;
+ }
+ delay_slots -= words;
+ }
+ }
+ return;
+ }
+
+ memset (&insn, 0, sizeof (insn));
+ strcpy (insn.mnemonic, line);
+ lptr = input_line_pointer;
+ *lptr = c;
+ input_line_pointer = savedp;
+
+ /* See if this line is part of a parallel instruction; if so, either this
+ line or the next line will have the "||" specifier preceding the
+ mnemonic, and we look for it in the parallel insn hash table. */
+ if (strstr (line, "||") != NULL || parallel_on_next_line_hint)
+ {
+ char *tmp = strstr (line, "||");
+ if (tmp != NULL)
+ *tmp = '\0';
+
+ if (tic54x_parse_parallel_insn_firstline (&insn, lptr))
+ {
+ is_parallel = 1;
+ /* If the parallel part is on the same line, process it now,
+ otherwise let the assembler pick up the next line for us. */
+ if (tmp != NULL)
+ {
+ while (ISSPACE (tmp[2]))
+ ++tmp;
+ md_assemble (tmp + 2);
+ }
+ }
+ else
+ {
+ as_bad (_("Unrecognized parallel instruction '%s'"), line);
+ }
+ return;
+ }
+
+ if (tic54x_parse_insn (&insn, lptr))
+ {
+ int words;
+
+ if ((insn.tm->flags & FL_LP)
+ && cpu != V545LP && cpu != V546LP)
+ {
+ as_bad (_("Instruction '%s' requires an LP cpu version"),
+ insn.tm->name);
+ return;
+ }
+ if ((insn.tm->flags & FL_FAR)
+ && amode != far_mode)
+ {
+ as_bad (_("Instruction '%s' requires far mode addressing"),
+ insn.tm->name);
+ return;
+ }
+
+ words = build_insn (&insn);
+
+ /* Is this instruction in a delay slot? */
+ if (delay_slots)
+ {
+ if (words > delay_slots)
+ {
+ as_warn (_("Instruction does not fit in available delay "
+ "slots (%d-word insn, %d slots left). "
+ "Resulting behavior is undefined."),
+ words, delay_slots);
+ delay_slots = 0;
+ return;
+ }
+ /* Branches in delay slots are not allowed. */
+ if (insn.tm->flags & FL_BMASK)
+ {
+ as_warn (_("Instructions which cause PC discontinuity are not "
+ "allowed in a delay slot. "
+ "Resulting behavior is undefined."));
+ }
+ delay_slots -= words;
+ }
+
+ /* Is this instruction the target of a repeat? */
+ if (repeat_slot)
+ {
+ if (insn.tm->flags & FL_NR)
+ as_warn (_("'%s' is not repeatable. "
+ "Resulting behavior is undefined."),
+ insn.tm->name);
+ else if (insn.is_lkaddr)
+ as_warn (_("Instructions using long offset modifiers or absolute "
+ "addresses are not repeatable. "
+ "Resulting behavior is undefined."));
+ repeat_slot = 0;
+ }
+
+ /* Make sure we check the target of a repeat instruction. */
+ if (insn.tm->flags & B_REPEAT)
+ {
+ repeat_slot = 1;
+ /* FIXME -- warn if repeat_slot == 1 at EOF. */
+ }
+ /* Make sure we check our delay slots for validity. */
+ if (insn.tm->flags & FL_DELAY)
+ {
+ delay_slots = 2;
+ /* FIXME -- warn if delay_slots != 0 at EOF. */
+ }
+ }
+}
+
+/* Do a final adjustment on the symbol table; in this case, make sure we have
+ a ".file" symbol. */
+
+void
+tic54x_adjust_symtab (void)
+{
+ if (symbol_rootP == NULL
+ || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE)
+ {
+ char *filename;
+ unsigned lineno;
+ as_where (&filename, &lineno);
+ c_dot_file_symbol (filename, 0);
+ }
+}
+
+/* In order to get gas to ignore any | chars at the start of a line,
+ this function returns true if a | is found in a line.
+ This lets us process parallel instructions, which span two lines. */
+
+int
+tic54x_unrecognized_line (int c)
+{
+ return c == PARALLEL_SEPARATOR;
+}
+
+/* Watch for local labels of the form $[0-9] and [_a-zA-Z][_a-zA-Z0-9]*?
+ Encode their names so that only we see them and can map them to the
+ appropriate places.
+ FIXME -- obviously this isn't done yet. These locals still show up in the
+ symbol table. */
+void
+tic54x_define_label (symbolS *sym)
+{
+ /* Just in case we need this later; note that this is not necessarily the
+ same thing as line_label...
+ When aligning or assigning labels to fields, sometimes the label is
+ assigned other than the address at which the label appears.
+ FIXME -- is this really needed? I think all the proper label assignment
+ is done in tic54x_cons. */
+ last_label_seen = sym;
+}
+
+/* Try to parse something that normal parsing failed at. */
+
+symbolS *
+tic54x_undefined_symbol (char *name)
+{
+ symbol *sym;
+
+ /* Not sure how to handle predefined symbols. */
+ if ((sym = (symbol *) hash_find (cc_hash, name)) != NULL ||
+ (sym = (symbol *) hash_find (cc2_hash, name)) != NULL ||
+ (sym = (symbol *) hash_find (cc3_hash, name)) != NULL ||
+ (sym = (symbol *) hash_find (misc_symbol_hash, name)) != NULL ||
+ (sym = (symbol *) hash_find (sbit_hash, name)) != NULL)
+ {
+ return symbol_new (name, reg_section,
+ (valueT) sym->value,
+ &zero_address_frag);
+ }
+
+ if ((sym = (symbol *) hash_find (reg_hash, name)) != NULL ||
+ (sym = (symbol *) hash_find (mmreg_hash, name)) != NULL ||
+ !strcasecmp (name, "a") || !strcasecmp (name, "b"))
+ {
+ return symbol_new (name, reg_section,
+ (valueT) sym ? sym->value : 0,
+ &zero_address_frag);
+ }
+
+ return NULL;
+}
+
+/* Parse a name in an expression before the expression parser takes a stab at
+ it. */
+
+int
+tic54x_parse_name (char *name ATTRIBUTE_UNUSED,
+ expressionS *expn ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *literalP, int *sizeP)
+{
+ /* Target data is little-endian, but floats are stored
+ big-"word"ian. ugh. */
+ return ieee_md_atof (type, literalP, sizeP, TRUE);
+}
+
+arelent *
+tc_gen_reloc (asection *section, fixS *fixP)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type code = fixP->fx_r_type;
+ asymbol *sym = symbol_get_bfdsym (fixP->fx_addsy);
+
+ rel = (arelent *) xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = sym;
+ /* We assume that all rel->address are host byte offsets. */
+ rel->address = fixP->fx_frag->fr_address + fixP->fx_where;
+ rel->address /= OCTETS_PER_BYTE;
+ rel->howto = bfd_reloc_type_lookup (stdoutput, code);
+ if (!strcmp (sym->name, section->name))
+ rel->howto += HOWTO_BANK;
+
+ if (!rel->howto)
+ {
+ const char *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 NULL;
+ }
+ return rel;
+}
+
+/* Handle cons expressions. */
+
+void
+tic54x_cons_fix_new (fragS *frag, int where, int octets, expressionS *expn,
+ bfd_reloc_code_real_type r)
+{
+ switch (octets)
+ {
+ default:
+ as_bad (_("Unsupported relocation size %d"), octets);
+ r = BFD_RELOC_TIC54X_16_OF_23;
+ break;
+ case 2:
+ r = BFD_RELOC_TIC54X_16_OF_23;
+ break;
+ case 4:
+ /* TI assembler always uses this, regardless of addressing mode. */
+ if (emitting_long)
+ r = BFD_RELOC_TIC54X_23;
+ else
+ /* We never want to directly generate this; this is provided for
+ stabs support only. */
+ r = BFD_RELOC_32;
+ break;
+ }
+ fix_new_exp (frag, where, octets, expn, 0, r);
+}
+
+/* Attempt to simplify or even eliminate a fixup.
+ To indicate that a fixup has been eliminated, set fixP->fx_done.
+
+ If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ valueT val = * valP;
+
+ switch (fixP->fx_r_type)
+ {
+ default:
+ as_fatal ("Bad relocation type: 0x%02x", fixP->fx_r_type);
+ return;
+ case BFD_RELOC_TIC54X_MS7_OF_23:
+ val = (val >> 16) & 0x7F;
+ /* Fall through. */
+ case BFD_RELOC_TIC54X_16_OF_23:
+ case BFD_RELOC_16:
+ bfd_put_16 (stdoutput, val, buf);
+ /* Indicate what we're actually writing, so that we don't get warnings
+ about exceeding available space. */
+ *valP = val & 0xFFFF;
+ break;
+ case BFD_RELOC_TIC54X_PARTLS7:
+ bfd_put_16 (stdoutput,
+ (bfd_get_16 (stdoutput, buf) & 0xFF80) | (val & 0x7F),
+ buf);
+ /* Indicate what we're actually writing, so that we don't get warnings
+ about exceeding available space. */
+ *valP = val & 0x7F;
+ break;
+ case BFD_RELOC_TIC54X_PARTMS9:
+ /* TI assembler doesn't shift its encoding for relocatable files, and is
+ thus incompatible with this implementation's relocatable files. */
+ bfd_put_16 (stdoutput,
+ (bfd_get_16 (stdoutput, buf) & 0xFE00) | (val >> 7),
+ buf);
+ break;
+ case BFD_RELOC_32:
+ case BFD_RELOC_TIC54X_23:
+ bfd_put_32 (stdoutput,
+ (bfd_get_32 (stdoutput, buf) & 0xFF800000) | val,
+ buf);
+ break;
+ }
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+/* This is our chance to record section alignment
+ don't need to do anything here, since BFD does the proper encoding. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT section_size)
+{
+ return section_size;
+}
+
+long
+md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Mostly little-endian, but longwords (4 octets) get MS word stored
+ first. */
+
+void
+tic54x_number_to_chars (char *buf, valueT val, int n)
+{
+ if (n != 4)
+ number_to_chars_littleendian (buf, val, n);
+ else
+ {
+ number_to_chars_littleendian (buf , val >> 16 , 2);
+ number_to_chars_littleendian (buf + 2, val & 0xFFFF, 2);
+ }
+}
+
+int
+tic54x_estimate_size_before_relax (fragS *frag ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* We use this to handle bit allocations which we couldn't handle before due
+ to symbols being in different frags. return number of octets added. */
+
+int
+tic54x_relax_frag (fragS *frag, long stretch ATTRIBUTE_UNUSED)
+{
+ symbolS *sym = frag->fr_symbol;
+ int growth = 0;
+ int i;
+
+ if (sym != NULL)
+ {
+ struct bit_info *bi = (struct bit_info *) frag->fr_opcode;
+ int bit_offset = frag_bit_offset (frag_prev (frag, bi->seg), bi->seg);
+ int size = S_GET_VALUE (sym);
+ fragS *prev_frag = bit_offset_frag (frag_prev (frag, bi->seg), bi->seg);
+ int available = 16 - bit_offset;
+
+ if (symbol_get_frag (sym) != &zero_address_frag
+ || S_IS_COMMON (sym)
+ || !S_IS_DEFINED (sym))
+ as_bad_where (frag->fr_file, frag->fr_line,
+ _("non-absolute value used with .space/.bes"));
+
+ if (size < 0)
+ {
+ as_warn (_("negative value ignored in %s"),
+ bi->type == TYPE_SPACE ? ".space" :
+ bi->type == TYPE_BES ? ".bes" : ".field");
+ growth = 0;
+ frag->tc_frag_data = frag->fr_fix = 0;
+ return 0;
+ }
+
+ if (bi->type == TYPE_FIELD)
+ {
+ /* Bit fields of 16 or larger will have already been handled. */
+ if (bit_offset != 0 && available >= size)
+ {
+ char *p = prev_frag->fr_literal;
+
+ valueT value = bi->value;
+ value <<= available - size;
+ value |= ((unsigned short) p[1] << 8) | p[0];
+ md_number_to_chars (p, value, 2);
+ if ((prev_frag->tc_frag_data += size) == 16)
+ prev_frag->tc_frag_data = 0;
+ if (bi->sym)
+ symbol_set_frag (bi->sym, prev_frag);
+ /* This frag is no longer used. */
+ growth = -frag->fr_fix;
+ frag->fr_fix = 0;
+ frag->tc_frag_data = 0;
+ }
+ else
+ {
+ char *p = frag->fr_literal;
+
+ valueT value = bi->value << (16 - size);
+ md_number_to_chars (p, value, 2);
+ if ((frag->tc_frag_data = size) == 16)
+ frag->tc_frag_data = 0;
+ growth = 0;
+ }
+ }
+ else
+ {
+ if (bit_offset != 0 && bit_offset < 16)
+ {
+ if (available >= size)
+ {
+ if ((prev_frag->tc_frag_data += size) == 16)
+ prev_frag->tc_frag_data = 0;
+ if (bi->sym)
+ symbol_set_frag (bi->sym, prev_frag);
+ /* This frag is no longer used. */
+ growth = -frag->fr_fix;
+ frag->fr_fix = 0;
+ frag->tc_frag_data = 0;
+ goto getout;
+ }
+ if (bi->type == TYPE_SPACE && bi->sym)
+ symbol_set_frag (bi->sym, prev_frag);
+ size -= available;
+ }
+ growth = (size + 15) / 16 * OCTETS_PER_BYTE - frag->fr_fix;
+ for (i = 0; i < growth; i++)
+ frag->fr_literal[i] = 0;
+ frag->fr_fix = growth;
+ frag->tc_frag_data = size % 16;
+ /* Make sure any BES label points to the LAST word allocated. */
+ if (bi->type == TYPE_BES && bi->sym)
+ S_SET_VALUE (bi->sym, frag->fr_fix / OCTETS_PER_BYTE - 1);
+ }
+ getout:
+ frag->fr_symbol = 0;
+ frag->fr_opcode = 0;
+ free ((void *) bi);
+ }
+ return growth;
+}
+
+void
+tic54x_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ fragS *frag)
+{
+ /* Offset is in bytes. */
+ frag->fr_offset = (frag->fr_next->fr_address
+ - frag->fr_address
+ - frag->fr_fix) / frag->fr_var;
+ if (frag->fr_offset < 0)
+ {
+ as_bad_where (frag->fr_file, frag->fr_line,
+ _("attempt to .space/.bes backwards? (%ld)"),
+ (long) frag->fr_offset);
+ }
+ frag->fr_type = rs_space;
+}
+
+/* We need to avoid having labels defined for certain directives/pseudo-ops
+ since once the label is defined, it's in the symbol table for good. TI
+ syntax puts the symbol *before* the pseudo (which is kinda like MRI syntax,
+ I guess, except I've never seen a definition of MRI syntax).
+
+ C is the character that used to be at *REST, which points to the end of the
+ label.
+
+ Don't allow labels to start with '.' */
+
+int
+tic54x_start_label (int c, char *rest)
+{
+ /* If within .struct/.union, no auto line labels, please. */
+ if (current_stag != NULL)
+ return 0;
+
+ /* Disallow labels starting with "." */
+ if (c != ':')
+ {
+ char *label = rest;
+
+ while (!is_end_of_line[(int) label[-1]])
+ --label;
+ if (*label == '.')
+ {
+ as_bad (_("Invalid label '%s'"), label);
+ return 0;
+ }
+ }
+
+ if (is_end_of_line[(int) c])
+ return 1;
+
+ if (ISSPACE (c))
+ while (ISSPACE (c = *++rest))
+ ;
+ if (c == '.')
+ {
+ /* Don't let colon () define a label for any of these... */
+ return (strncasecmp (rest, ".tag", 4) != 0 || !ISSPACE (rest[4]))
+ && (strncasecmp (rest, ".struct", 7) != 0 || !ISSPACE (rest[7]))
+ && (strncasecmp (rest, ".union", 6) != 0 || !ISSPACE (rest[6]))
+ && (strncasecmp (rest, ".macro", 6) != 0 || !ISSPACE (rest[6]))
+ && (strncasecmp (rest, ".set", 4) != 0 || !ISSPACE (rest[4]))
+ && (strncasecmp (rest, ".equ", 4) != 0 || !ISSPACE (rest[4]));
+ }
+
+ return 1;
+}
diff --git a/gas/config/tc-tic54x.h b/gas/config/tc-tic54x.h
new file mode 100644
index 0000000..3fe8be8
--- /dev/null
+++ b/gas/config/tc-tic54x.h
@@ -0,0 +1,128 @@
+/* tc-tic54x.h -- Header file for tc-tic54x.c
+ Copyright (C) 1999-2014 Free Software Foundation, Inc.
+ Contributed by Timothy Wall (twall@alum.mit.edu)
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef _TC_TIC54X_H_
+#define _TC_TIC54X_H_
+
+/* select the proper coff format (see obj-coff.h) */
+#define TC_TIC54X
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+#define OCTETS_PER_BYTE_POWER 1
+
+#define TARGET_ARCH bfd_arch_tic54x
+
+#define WORKING_DOT_WORD 1
+
+#define MAX_OPERANDS 4
+#define PARALLEL_SEPARATOR '|'
+#define LABELS_WITHOUT_COLONS 1
+/* accept 0FFFFh, 1010b, etc. */
+#define NUMBERS_WITH_SUFFIX 1
+/* $ is section program counter */
+#define DOLLAR_DOT 1
+/* accept parallel lines like
+ add #1,a || ld #1, b
+ (may also be split across lines)
+*/
+#define DOUBLEBAR_PARALLEL 1
+/* affects preprocessor */
+#define KEEP_WHITE_AROUND_COLON 1
+
+struct bit_info
+{
+ segT seg;
+#define TYPE_SPACE 0
+#define TYPE_BES 1
+#define TYPE_FIELD 2
+ int type;
+ symbolS *sym;
+ valueT value;
+ char *where;
+ int offset;
+};
+
+/* We sometimes need to keep track of bit offsets within words */
+#define TC_FRAG_TYPE int
+#define TC_FRAG_INIT(FRAGP) do {(FRAGP)->tc_frag_data = 0;}while (0)
+
+/* tell GAS whether the given token is indeed a code label */
+#define TC_START_LABEL_WITHOUT_COLON(c,ptr) tic54x_start_label(c,ptr)
+extern int tic54x_start_label (int, char *);
+
+/* custom handling for relocations in cons expressions */
+#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP, RELOC) \
+ tic54x_cons_fix_new (FRAG, OFF, LEN, EXP, RELOC)
+extern void tic54x_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+/* Define md_number_to_chars as the appropriate standard big endian or
+ little endian function. Mostly littleendian, but longwords and floats are
+ stored MS word first.
+*/
+
+#define md_number_to_chars tic54x_number_to_chars
+extern void tic54x_number_to_chars (char *, valueT, int);
+#define tc_adjust_symtab() tic54x_adjust_symtab()
+extern void tic54x_adjust_symtab (void);
+#define tc_unrecognized_line(ch) tic54x_unrecognized_line(ch)
+extern int tic54x_unrecognized_line (int ch);
+#define md_parse_name(s,e,m,c) tic54x_parse_name(s,e)
+extern int tic54x_parse_name (char *name, expressionS *e);
+#define md_undefined_symbol(s) tic54x_undefined_symbol(s)
+extern symbolS *tic54x_undefined_symbol (char *name);
+#define md_macro_start() tic54x_macro_start()
+extern void tic54x_macro_start (void);
+#define md_macro_end() tic54x_macro_end()
+extern void tic54x_macro_end (void);
+#define md_macro_info(args) tic54x_macro_info(args)
+struct macro_struct;
+extern void tic54x_macro_info (const struct macro_struct *);
+#define tc_frob_label(sym) tic54x_define_label (sym)
+extern void tic54x_define_label (symbolS *);
+
+#define md_start_line_hook() tic54x_start_line_hook()
+extern void tic54x_start_line_hook (void);
+
+#define md_estimate_size_before_relax(f,s) \
+tic54x_estimate_size_before_relax(f,s)
+extern int tic54x_estimate_size_before_relax(fragS *, segT);
+
+#define md_relax_frag(seg, f,s) tic54x_relax_frag(f,s)
+extern int tic54x_relax_frag(fragS *, long);
+
+#define md_convert_frag(b,s,f) tic54x_convert_frag(b,s,f)
+extern void tic54x_convert_frag(bfd *, segT, fragS *);
+
+/* Other things we don't support... */
+
+/* Define away the call to md_operand in the expression parsing code.
+ This is called whenever the expression parser can't parse the input
+ and gives the assembler backend a chance to deal with it instead. */
+
+#define md_operand(X)
+
+/* spruce up the listing output */
+#define LISTING_WORD_SIZE 2
+
+extern void tic54x_global (int);
+
+#endif
diff --git a/gas/config/tc-tic6x.c b/gas/config/tc-tic6x.c
new file mode 100644
index 0000000..3f2912a
--- /dev/null
+++ b/gas/config/tc-tic6x.c
@@ -0,0 +1,5377 @@
+/* TI C6X assembler.
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Contributed by Joseph Myers <joseph@codesourcery.com>
+ Bernd Schmidt <bernds@codesourcery.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/tic6x.h"
+#include "elf/tic6x.h"
+#include "elf32-tic6x.h"
+
+/* Truncate and sign-extend at 32 bits, so that building on a 64-bit
+ host gives identical results to a 32-bit host. */
+#define TRUNC(X) ((valueT) (X) & 0xffffffffU)
+#define SEXT(X) ((TRUNC (X) ^ 0x80000000U) - 0x80000000U)
+
+#define streq(a, b) (strcmp (a, b) == 0)
+
+/* Stuff for .scomm symbols. */
+static segT sbss_section;
+static asection scom_section;
+static asymbol scom_symbol;
+
+const char comment_chars[] = ";";
+const char line_comment_chars[] = "#*;";
+const char line_separator_chars[] = "@";
+
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dDfF";
+
+const char *md_shortopts = "";
+
+enum
+ {
+ OPTION_MARCH = OPTION_MD_BASE,
+ OPTION_MBIG_ENDIAN,
+ OPTION_MLITTLE_ENDIAN,
+ OPTION_MDSBT,
+ OPTION_MNO_DSBT,
+ OPTION_MPID,
+ OPTION_MPIC,
+ OPTION_MNO_PIC,
+ OPTION_MGENERATE_REL
+ };
+
+struct option md_longopts[] =
+ {
+ { "march", required_argument, NULL, OPTION_MARCH },
+ { "mbig-endian", no_argument, NULL, OPTION_MBIG_ENDIAN },
+ { "mlittle-endian", no_argument, NULL, OPTION_MLITTLE_ENDIAN },
+ { "mdsbt", no_argument, NULL, OPTION_MDSBT },
+ { "mno-dsbt", no_argument, NULL, OPTION_MNO_DSBT },
+ { "mpid", required_argument, NULL, OPTION_MPID },
+ { "mpic", no_argument, NULL, OPTION_MPIC },
+ { "mno-pic", no_argument, NULL, OPTION_MNO_PIC },
+ { "mgenerate-rel", no_argument, NULL, OPTION_MGENERATE_REL },
+ { NULL, no_argument, NULL, 0 }
+ };
+size_t md_longopts_size = sizeof (md_longopts);
+
+/* The instructions enabled based only on the selected architecture
+ (all instructions, if no architecture specified). */
+static unsigned short tic6x_arch_enable = (TIC6X_INSN_C62X
+ | TIC6X_INSN_C64X
+ | TIC6X_INSN_C64XP
+ | TIC6X_INSN_C67X
+ | TIC6X_INSN_C67XP
+ | TIC6X_INSN_C674X);
+
+/* The instructions enabled based on the current set of features
+ (architecture, as modified by other options). */
+static unsigned short tic6x_features;
+
+/* The architecture attribute value, or C6XABI_Tag_ISA_none if
+ not yet set. */
+static int tic6x_arch_attribute = C6XABI_Tag_ISA_none;
+
+/* Whether any instructions at all have been seen. Once any
+ instructions have been seen, architecture attributes merge into the
+ previous attribute value rather than replacing it. */
+static bfd_boolean tic6x_seen_insns = FALSE;
+
+/* The number of registers in each register file supported by the
+ current architecture. */
+static unsigned int tic6x_num_registers;
+
+/* Whether predication on A0 is possible. */
+static bfd_boolean tic6x_predicate_a0;
+
+/* Whether execute packets can cross fetch packet boundaries. */
+static bfd_boolean tic6x_can_cross_fp_boundary;
+
+/* Whether there are constraints on simultaneous reads and writes of
+ 40-bit data. */
+static bfd_boolean tic6x_long_data_constraints;
+
+/* Whether compact instructions are available. */
+static bfd_boolean tic6x_compact_insns;
+
+/* Whether to generate RELA relocations. */
+static bfd_boolean tic6x_generate_rela = TRUE;
+
+/* Whether the code uses DSBT addressing. */
+static bfd_boolean tic6x_dsbt;
+
+/* Types of position-independent data (attribute values for
+ Tag_ABI_PID). */
+typedef enum
+ {
+ tic6x_pid_no = 0,
+ tic6x_pid_near = 1,
+ tic6x_pid_far = 2
+ } tic6x_pid_type;
+
+/* The type of data addressing used in this code. */
+static tic6x_pid_type tic6x_pid;
+
+/* Whether the code uses position-independent code. */
+static bfd_boolean tic6x_pic;
+
+/* Table of supported architecture variants. */
+typedef struct
+{
+ const char *arch;
+ int attr;
+ unsigned short features;
+} tic6x_arch_table;
+static const tic6x_arch_table tic6x_arches[] =
+ {
+ { "c62x", C6XABI_Tag_ISA_C62X, TIC6X_INSN_C62X },
+ { "c64x", C6XABI_Tag_ISA_C64X, TIC6X_INSN_C62X | TIC6X_INSN_C64X },
+ { "c64x+", C6XABI_Tag_ISA_C64XP, (TIC6X_INSN_C62X
+ | TIC6X_INSN_C64X
+ | TIC6X_INSN_C64XP) },
+ { "c67x", C6XABI_Tag_ISA_C67X, TIC6X_INSN_C62X | TIC6X_INSN_C67X },
+ { "c67x+", C6XABI_Tag_ISA_C67XP, (TIC6X_INSN_C62X
+ | TIC6X_INSN_C67X
+ | TIC6X_INSN_C67XP) },
+ { "c674x", C6XABI_Tag_ISA_C674X, (TIC6X_INSN_C62X
+ | TIC6X_INSN_C64X
+ | TIC6X_INSN_C64XP
+ | TIC6X_INSN_C67X
+ | TIC6X_INSN_C67XP
+ | TIC6X_INSN_C674X) }
+ };
+
+/* Caller saved register encodings. The standard frame layout uses this
+ order, starting from the highest address. There must be
+ TIC6X_NUM_UNWIND_REGS values. */
+enum
+{
+ UNWIND_A15,
+ UNWIND_B15,
+ UNWIND_B14,
+ UNWIND_B13,
+ UNWIND_B12,
+ UNWIND_B11,
+ UNWIND_B10,
+ UNWIND_B3,
+ UNWIND_A14,
+ UNWIND_A13,
+ UNWIND_A12,
+ UNWIND_A11,
+ UNWIND_A10
+};
+
+static void tic6x_output_unwinding (bfd_boolean need_extab);
+
+/* Return the frame unwind state for the current function, allocating
+ as necessary. */
+
+static tic6x_unwind_info *tic6x_get_unwind (void)
+{
+ tic6x_unwind_info *unwind;
+
+ unwind = seg_info (now_seg)->tc_segment_info_data.unwind;
+ if (unwind)
+ return unwind;
+
+ unwind = seg_info (now_seg)->tc_segment_info_data.text_unwind;
+ if (unwind)
+ return unwind;
+
+ unwind = (tic6x_unwind_info *)xmalloc (sizeof (tic6x_unwind_info));
+ seg_info (now_seg)->tc_segment_info_data.unwind = unwind;
+ memset (unwind, 0, sizeof (*unwind));
+ return unwind;
+}
+
+/* Update the selected architecture based on ARCH, giving an error if
+ ARCH is an invalid value. Does not call tic6x_update_features; the
+ caller must do that if necessary. */
+
+static void
+tic6x_use_arch (const char *arch)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (tic6x_arches); i++)
+ if (strcmp (arch, tic6x_arches[i].arch) == 0)
+ {
+ tic6x_arch_enable = tic6x_arches[i].features;
+ if (tic6x_seen_insns)
+ tic6x_arch_attribute
+ = elf32_tic6x_merge_arch_attributes (tic6x_arch_attribute,
+ tic6x_arches[i].attr);
+ else
+ tic6x_arch_attribute = tic6x_arches[i].attr;
+ return;
+ }
+
+ as_bad (_("unknown architecture '%s'"), arch);
+}
+
+/* Table of supported -mpid arguments. */
+typedef struct
+{
+ const char *arg;
+ tic6x_pid_type attr;
+} tic6x_pid_type_table;
+static const tic6x_pid_type_table tic6x_pid_types[] =
+ {
+ { "no", tic6x_pid_no },
+ { "near", tic6x_pid_near },
+ { "far", tic6x_pid_far }
+ };
+
+/* Handle -mpid=ARG. */
+
+static void
+tic6x_use_pid (const char *arg)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (tic6x_pid_types); i++)
+ if (strcmp (arg, tic6x_pid_types[i].arg) == 0)
+ {
+ tic6x_pid = tic6x_pid_types[i].attr;
+ return;
+ }
+
+ as_bad (_("unknown -mpid= argument '%s'"), arg);
+}
+
+/* Parse a target-specific option. */
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_MARCH:
+ tic6x_use_arch (arg);
+ break;
+
+ case OPTION_MBIG_ENDIAN:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_MLITTLE_ENDIAN:
+ target_big_endian = 0;
+ break;
+
+ case OPTION_MDSBT:
+ tic6x_dsbt = 1;
+ break;
+
+ case OPTION_MNO_DSBT:
+ tic6x_dsbt = 0;
+ break;
+
+ case OPTION_MPID:
+ tic6x_use_pid (arg);
+ break;
+
+ case OPTION_MPIC:
+ tic6x_pic = 1;
+ break;
+
+ case OPTION_MNO_PIC:
+ tic6x_pic = 0;
+ break;
+
+ case OPTION_MGENERATE_REL:
+ tic6x_generate_rela = FALSE;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream ATTRIBUTE_UNUSED)
+{
+ unsigned int i;
+
+ fputc ('\n', stream);
+ fprintf (stream, _("TMS320C6000 options:\n"));
+ fprintf (stream, _(" -march=ARCH enable instructions from architecture ARCH\n"));
+ fprintf (stream, _(" -mbig-endian generate big-endian code\n"));
+ fprintf (stream, _(" -mlittle-endian generate little-endian code\n"));
+ fprintf (stream, _(" -mdsbt code uses DSBT addressing\n"));
+ fprintf (stream, _(" -mno-dsbt code does not use DSBT addressing\n"));
+ fprintf (stream, _(" -mpid=no code uses position-dependent data addressing\n"));
+ fprintf (stream, _(" -mpid=near code uses position-independent data addressing,\n"
+ " GOT accesses use near DP addressing\n"));
+ fprintf (stream, _(" -mpid=far code uses position-independent data addressing,\n"
+ " GOT accesses use far DP addressing\n"));
+ fprintf (stream, _(" -mpic code addressing is position-independent\n"));
+ fprintf (stream, _(" -mno-pic code addressing is position-dependent\n"));
+ /* -mgenerate-rel is only for testsuite use and is deliberately
+ undocumented. */
+
+ fputc ('\n', stream);
+ fprintf (stream, _("Supported ARCH values are:"));
+ for (i = 0; i < ARRAY_SIZE (tic6x_arches); i++)
+ fprintf (stream, " %s", tic6x_arches[i].arch);
+ fputc ('\n', stream);
+}
+
+/* Update enabled features based on the current architecture and
+ related settings. */
+static void
+tic6x_update_features (void)
+{
+ tic6x_features = tic6x_arch_enable;
+
+ tic6x_num_registers
+ = (tic6x_arch_enable & (TIC6X_INSN_C64X | TIC6X_INSN_C67XP)) ? 32 : 16;
+
+ tic6x_predicate_a0 = (tic6x_arch_enable & TIC6X_INSN_C64X) ? TRUE : FALSE;
+
+ tic6x_can_cross_fp_boundary
+ = (tic6x_arch_enable
+ & (TIC6X_INSN_C64X | TIC6X_INSN_C67XP)) ? TRUE : FALSE;
+
+ tic6x_long_data_constraints
+ = (tic6x_arch_enable & TIC6X_INSN_C64X) ? FALSE : TRUE;
+
+ tic6x_compact_insns = (tic6x_arch_enable & TIC6X_INSN_C64XP) ? TRUE : FALSE;
+}
+
+/* Do configuration after all options have been parsed. */
+
+void
+tic6x_after_parse_args (void)
+{
+ tic6x_update_features ();
+}
+
+/* Parse a .cantunwind directive. */
+static void
+s_tic6x_cantunwind (int ignored ATTRIBUTE_UNUSED)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ /* GCC sometimes spits out superfluous .cantunwind directives, so ignore
+ them. */
+ if (unwind->data_bytes == 0)
+ return;
+
+ if (unwind->data_bytes != -1)
+ {
+ as_bad (_("unexpected .cantunwind directive"));
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ if (unwind->personality_routine || unwind->personality_index != -1)
+ as_bad (_("personality routine specified for cantunwind frame"));
+
+ unwind->personality_index = -2;
+}
+
+/* Parse a .handlerdata directive. */
+static void
+s_tic6x_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ if (!unwind->saved_seg)
+ {
+ as_bad (_("unexpected .handlerdata directive"));
+ return;
+ }
+
+ if (unwind->table_entry || unwind->personality_index == -2)
+ {
+ as_bad (_("duplicate .handlerdata directive"));
+ return;
+ }
+
+ if (unwind->personality_index == -1 && unwind->personality_routine == NULL)
+ {
+ as_bad (_("personality routine required before .handlerdata directive"));
+ return;
+ }
+
+ tic6x_output_unwinding (TRUE);
+}
+
+/* Parse a .endp directive. */
+static void
+s_tic6x_endp (int ignored ATTRIBUTE_UNUSED)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ if (unwind->data_bytes != 0)
+ {
+ /* Output a .exidx entry if we have not already done so.
+ Then switch back to the text section. */
+ if (!unwind->table_entry)
+ tic6x_output_unwinding (FALSE);
+
+ subseg_set (unwind->saved_seg, unwind->saved_subseg);
+ }
+
+ unwind->saved_seg = NULL;
+ unwind->table_entry = NULL;
+ unwind->data_bytes = 0;
+}
+
+/* Parse a .personalityindex directive. */
+static void
+s_tic6x_personalityindex (int ignored ATTRIBUTE_UNUSED)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+ expressionS exp;
+
+ if (unwind->personality_routine || unwind->personality_index != -1)
+ as_bad (_("duplicate .personalityindex directive"));
+
+ expression (&exp);
+
+ if (exp.X_op != O_constant
+ || exp.X_add_number < 0 || exp.X_add_number > 15)
+ {
+ as_bad (_("bad personality routine number"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ unwind->personality_index = exp.X_add_number;
+
+ demand_empty_rest_of_line ();
+}
+
+static void
+s_tic6x_personality (int ignored ATTRIBUTE_UNUSED)
+{
+ char *name, *p, c;
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ if (unwind->personality_routine || unwind->personality_index != -1)
+ as_bad (_("duplicate .personality directive"));
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ unwind->personality_routine = symbol_find_or_make (name);
+ *p = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a .arch directive. */
+static void
+s_tic6x_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ char c;
+ char *arch;
+
+ arch = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE (*input_line_pointer))
+ input_line_pointer++;
+ c = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ tic6x_use_arch (arch);
+ tic6x_update_features ();
+ *input_line_pointer = c;
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a .ehtype directive. */
+
+static void
+s_tic6x_ehtype (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ char *p;
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+#ifdef md_cons_align
+ md_cons_align (4);
+#endif
+
+
+ expression (&exp);
+
+ if (exp.X_op != O_symbol)
+ {
+ as_bad (_("expected symbol"));
+ return;
+ }
+
+ p = frag_more (4);
+ memset (p, 0, 4);
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 4,
+ &exp, 0, BFD_RELOC_C6000_EHTYPE);
+
+ demand_empty_rest_of_line ();
+}
+
+/* Parse a .nocmp directive. */
+
+static void
+s_tic6x_nocmp (int ignored ATTRIBUTE_UNUSED)
+{
+ seg_info (now_seg)->tc_segment_info_data.nocmp = TRUE;
+ demand_empty_rest_of_line ();
+}
+
+/* .scomm pseudo-op handler.
+
+ This is a new pseudo-op to handle putting objects in .scommon.
+ By doing this the linker won't need to do any work,
+ and more importantly it removes the implicit -G arg necessary to
+ correctly link the object file. */
+
+static void
+s_tic6x_scomm (int ignore ATTRIBUTE_UNUSED)
+{
+ char *name;
+ char c;
+ char *p;
+ offsetT size;
+ symbolS *symbolP;
+ offsetT align;
+ int align2;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("expected comma after symbol name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Skip ','. */
+ input_line_pointer++;
+ if ((size = get_absolute_expression ()) < 0)
+ {
+ /* xgettext:c-format */
+ as_warn (_("invalid length for .scomm directive"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* The third argument to .scomm is the alignment. */
+ if (*input_line_pointer != ',')
+ align = 8;
+ else
+ {
+ ++input_line_pointer;
+ align = get_absolute_expression ();
+ if (align <= 0)
+ {
+ as_warn (_("alignment is not a positive number"));
+ align = 8;
+ }
+ }
+
+ /* Convert to a power of 2 alignment. */
+ if (align)
+ {
+ for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2)
+ continue;
+ if (align != 1)
+ {
+ as_bad (_("alignment is not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align2 = 0;
+
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP))
+ {
+ /* xgettext:c-format */
+ as_bad (_("attempt to re-define symbol `%s'"),
+ S_GET_NAME (symbolP));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
+ {
+ /* xgettext:c-format */
+ as_bad (_("attempt to redefine `%s' with a different length"),
+ S_GET_NAME (symbolP));
+
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (symbol_get_obj (symbolP)->local)
+ {
+ segT old_sec = now_seg;
+ int old_subsec = now_subseg;
+ char *pfrag;
+
+ record_alignment (sbss_section, align2);
+ subseg_set (sbss_section, 0);
+
+ if (align2)
+ frag_align (align2, 0, 0);
+
+ if (S_GET_SEGMENT (symbolP) == sbss_section)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+
+ symbol_set_frag (symbolP, frag_now);
+
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size,
+ (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+ S_SET_SEGMENT (symbolP, sbss_section);
+ S_CLEAR_EXTERNAL (symbolP);
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, 1 << align2);
+ S_SET_EXTERNAL (symbolP);
+ S_SET_SEGMENT (symbolP, &scom_section);
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Track for each attribute whether it has been set explicitly (and so
+ should not have a default value set by the assembler). */
+static bfd_boolean tic6x_attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES];
+
+/* Parse a .c6xabi_attribute directive. */
+
+static void
+s_tic6x_c6xabi_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+ int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
+
+ if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
+ tic6x_attributes_set_explicitly[tag] = TRUE;
+}
+
+typedef struct
+{
+ const char *name;
+ int tag;
+} tic6x_attribute_table;
+
+static const tic6x_attribute_table tic6x_attributes[] =
+ {
+#define TAG(tag, value) { #tag, tag },
+#include "elf/tic6x-attrs.h"
+#undef TAG
+ };
+
+/* Convert an attribute name to a number. */
+
+int
+tic6x_convert_symbolic_attribute (const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (tic6x_attributes); i++)
+ if (strcmp (name, tic6x_attributes[i].name) == 0)
+ return tic6x_attributes[i].tag;
+
+ return -1;
+}
+
+const pseudo_typeS md_pseudo_table[] =
+ {
+ { "arch", s_tic6x_arch, 0 },
+ { "c6xabi_attribute", s_tic6x_c6xabi_attribute, 0 },
+ { "nocmp", s_tic6x_nocmp, 0 },
+ { "scomm", s_tic6x_scomm, 0 },
+ { "word", cons, 4 },
+ { "ehtype", s_tic6x_ehtype, 0 },
+ { "endp", s_tic6x_endp, 0 },
+ { "handlerdata", s_tic6x_handlerdata, 0 },
+ { "personalityindex", s_tic6x_personalityindex, 0 },
+ { "personality", s_tic6x_personality, 0 },
+ { "cantunwind", s_tic6x_cantunwind, 0 },
+ { 0, 0, 0 }
+ };
+
+/* Hash table of opcodes. For each opcode name, this stores a pointer
+ to a tic6x_opcode_list listing (in an arbitrary order) all opcode
+ table entries with that name. */
+static struct hash_control *opcode_hash;
+
+/* Initialize the assembler (called once at assembler startup). */
+
+void
+md_begin (void)
+{
+ tic6x_opcode_id id;
+ flagword applicable;
+ segT seg;
+ subsegT subseg;
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, 0);
+
+ /* Insert opcodes into the hash table. */
+ opcode_hash = hash_new ();
+ for (id = 0; id < tic6x_opcode_max; id++)
+ {
+ const char *errmsg;
+ tic6x_opcode_list *opc = xmalloc (sizeof (tic6x_opcode_list));
+
+ opc->id = id;
+ opc->next = hash_find (opcode_hash, tic6x_opcode_table[id].name);
+ if ((errmsg = hash_jam (opcode_hash, tic6x_opcode_table[id].name, opc))
+ != NULL)
+ as_fatal ("%s", _(errmsg));
+ }
+
+ /* Save the current subseg so we can restore it [it's the default one and
+ we don't want the initial section to be .sbss]. */
+ seg = now_seg;
+ subseg = now_subseg;
+
+ /* The sbss section is for local .scomm symbols. */
+ sbss_section = subseg_new (".bss", 0);
+ seg_info (sbss_section)->bss = 1;
+
+ /* This is copied from perform_an_assembly_pass. */
+ applicable = bfd_applicable_section_flags (stdoutput);
+ bfd_set_section_flags (stdoutput, sbss_section, applicable & SEC_ALLOC);
+
+ subseg_set (seg, subseg);
+
+ /* We must construct a fake section similar to bfd_com_section
+ but with the name .scommon. */
+ scom_section = *bfd_com_section_ptr;
+ scom_section.name = ".scommon";
+ scom_section.output_section = & scom_section;
+ scom_section.symbol = & scom_symbol;
+ scom_section.symbol_ptr_ptr = & scom_section.symbol;
+ scom_symbol = * bfd_com_section_ptr->symbol;
+ scom_symbol.name = ".scommon";
+ scom_symbol.section = & scom_section;
+}
+
+/* Whether the current line being parsed had the "||" parallel bars. */
+static bfd_boolean tic6x_line_parallel;
+
+/* Whether the current line being parsed started "||^" to indicate an
+ SPMASKed parallel instruction. */
+static bfd_boolean tic6x_line_spmask;
+
+/* If the current line being parsed had an instruction predicate, the
+ creg value for that predicate (which must be nonzero); otherwise
+ 0. */
+static unsigned int tic6x_line_creg;
+
+/* If the current line being parsed had an instruction predicate, the
+ z value for that predicate; otherwise 0. */
+static unsigned int tic6x_line_z;
+
+/* Return 1 (updating input_line_pointer as appropriate) if the line
+ starting with C (immediately before input_line_pointer) starts with
+ pre-opcode text appropriate for this target, 0 otherwise. */
+
+int
+tic6x_unrecognized_line (int c)
+{
+ char *p, *endp;
+ unsigned int z;
+ bfd_boolean areg;
+ bfd_boolean bad_predicate;
+
+ switch (c)
+ {
+ case '|':
+ if (input_line_pointer[0] == '|')
+ {
+ if (input_line_pointer[1] == '^')
+ {
+ tic6x_line_spmask = TRUE;
+ input_line_pointer += 2;
+ }
+ else
+ input_line_pointer += 1;
+ if (tic6x_line_parallel)
+ as_bad (_("multiple '||' on same line"));
+ tic6x_line_parallel = TRUE;
+ if (tic6x_line_creg)
+ as_bad (_("'||' after predicate"));
+ return 1;
+ }
+ return 0;
+
+ case '[':
+ /* If it doesn't look like a predicate at all, just return 0.
+ If it looks like one but not a valid one, give a better
+ error. */
+ p = input_line_pointer;
+ while (*p != ']' && !is_end_of_line[(unsigned char) *p])
+ p++;
+ if (*p != ']')
+ return 0;
+ endp = p + 1;
+ p = input_line_pointer;
+ z = 0;
+ bad_predicate = FALSE;
+ if (*p == '!')
+ {
+ z = 1;
+ p++;
+ }
+ if (*p == 'A' || *p == 'a')
+ areg = TRUE;
+ else if (*p == 'B' || *p == 'b')
+ areg = FALSE;
+ else
+ {
+ areg = TRUE; /* Avoid uninitialized warning. */
+ bad_predicate = TRUE;
+ }
+ if (!bad_predicate)
+ {
+ p++;
+ if (*p != '0' && *p != '1' && *p != '2')
+ bad_predicate = TRUE;
+ else if (p[1] != ']')
+ bad_predicate = TRUE;
+ else
+ input_line_pointer = p + 2;
+ }
+
+ if (tic6x_line_creg)
+ as_bad (_("multiple predicates on same line"));
+
+ if (bad_predicate)
+ {
+ char ctmp = *endp;
+ *endp = 0;
+ as_bad (_("bad predicate '%s'"), input_line_pointer - 1);
+ *endp = ctmp;
+ input_line_pointer = endp;
+ return 1;
+ }
+
+ switch (*p)
+ {
+ case '0':
+ tic6x_line_creg = (areg ? 6 : 1);
+ if (areg && !tic6x_predicate_a0)
+ as_bad (_("predication on A0 not supported on this architecture"));
+ break;
+
+ case '1':
+ tic6x_line_creg = (areg ? 4 : 2);
+ break;
+
+ case '2':
+ tic6x_line_creg = (areg ? 5 : 3);
+ break;
+
+ default:
+ abort ();
+ }
+
+ tic6x_line_z = z;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+/* Do any target-specific handling of a label required. */
+
+void
+tic6x_frob_label (symbolS *sym)
+{
+ segment_info_type *si;
+ tic6x_label_list *list;
+
+ if (tic6x_line_parallel)
+ {
+ as_bad (_("label after '||'"));
+ tic6x_line_parallel = FALSE;
+ tic6x_line_spmask = FALSE;
+ }
+ if (tic6x_line_creg)
+ {
+ as_bad (_("label after predicate"));
+ tic6x_line_creg = 0;
+ tic6x_line_z = 0;
+ }
+
+ si = seg_info (now_seg);
+ list = si->tc_segment_info_data.label_list;
+ si->tc_segment_info_data.label_list = xmalloc (sizeof (tic6x_label_list));
+ si->tc_segment_info_data.label_list->next = list;
+ si->tc_segment_info_data.label_list->label = sym;
+
+ /* Defining tc_frob_label overrides the ELF definition of
+ obj_frob_label, so we need to apply its effects here. */
+ dwarf2_emit_label (sym);
+}
+
+/* At end-of-line, give errors for start-of-line decorations that
+ needed an instruction but were not followed by one. */
+
+static void
+tic6x_end_of_line (void)
+{
+ if (tic6x_line_parallel)
+ {
+ as_bad (_("'||' not followed by instruction"));
+ tic6x_line_parallel = FALSE;
+ tic6x_line_spmask = FALSE;
+ }
+ if (tic6x_line_creg)
+ {
+ as_bad (_("predicate not followed by instruction"));
+ tic6x_line_creg = 0;
+ tic6x_line_z = 0;
+ }
+}
+
+/* Do any target-specific handling of the start of a logical line. */
+
+void
+tic6x_start_line_hook (void)
+{
+ tic6x_end_of_line ();
+}
+
+/* Do target-specific handling immediately after an input file from
+ the command line, and any other inputs it includes, have been
+ read. */
+
+void
+tic6x_cleanup (void)
+{
+ tic6x_end_of_line ();
+}
+
+/* Do target-specific initialization after arguments have been
+ processed and the output file created. */
+
+void
+tic6x_init_after_args (void)
+{
+ elf32_tic6x_set_use_rela_p (stdoutput, tic6x_generate_rela);
+}
+
+/* Free LIST of labels (possibly NULL). */
+
+static void
+tic6x_free_label_list (tic6x_label_list *list)
+{
+ while (list)
+ {
+ tic6x_label_list *old = list;
+
+ list = list->next;
+ free (old);
+ }
+}
+
+/* Handle a data alignment of N bytes. */
+
+void
+tic6x_cons_align (int n ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (now_seg);
+
+ /* Data means there is no current execute packet, and that any label
+ applies to that data rather than a subsequent instruction. */
+ tic6x_free_label_list (seginfo->tc_segment_info_data.label_list);
+ seginfo->tc_segment_info_data.label_list = NULL;
+ seginfo->tc_segment_info_data.execute_packet_frag = NULL;
+ seginfo->tc_segment_info_data.last_insn_lsb = NULL;
+ seginfo->tc_segment_info_data.spmask_addr = NULL;
+ seginfo->tc_segment_info_data.func_units_used = 0;
+}
+
+/* Handle an alignment directive. Return TRUE if the
+ machine-independent frag generation should be skipped. */
+
+bfd_boolean
+tic6x_do_align (int n, char *fill, int len ATTRIBUTE_UNUSED, int max)
+{
+ /* Given code alignments of 4, 8, 16 or 32 bytes, we try to handle
+ them in the md_end pass by inserting NOPs in parallel with
+ previous instructions. We only do this in sections containing
+ nothing but instructions. Code alignments of 1 or 2 bytes have
+ no effect in such sections (but we record them with
+ machine-dependent frags anyway so they can be skipped or
+ converted to machine-independent), while those of more than 64
+ bytes cannot reliably be handled in this way. */
+ if (n > 0
+ && max >= 0
+ && max < (1 << n)
+ && !need_pass_2
+ && fill == NULL
+ && subseg_text_p (now_seg))
+ {
+ fragS *align_frag;
+ char *p;
+
+ if (n > 5)
+ return FALSE;
+
+ /* Machine-independent code would generate a frag here, but we
+ wish to handle it in a machine-dependent way. */
+ if (frag_now_fix () != 0)
+ {
+ if (frag_now->fr_type != rs_machine_dependent)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (32);
+ align_frag = frag_now;
+ p = frag_var (rs_machine_dependent, 32, 32, max, NULL, n, NULL);
+ /* This must be the same as the frag to which a pointer was just
+ saved. */
+ if (p != align_frag->fr_literal)
+ abort ();
+ align_frag->tc_frag_data.is_insns = FALSE;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/* Types of operand for parsing purposes. These are used as bit-masks
+ to tell tic6x_parse_operand what forms of operand are
+ permitted. */
+#define TIC6X_OP_EXP 0x0001u
+#define TIC6X_OP_REG 0x0002u
+#define TIC6X_OP_REGPAIR 0x0004u
+#define TIC6X_OP_IRP 0x0008u
+#define TIC6X_OP_NRP 0x0010u
+/* With TIC6X_OP_MEM_NOUNREG, the contents of a () offset are always
+ interpreted as an expression, which may be a symbol with the same
+ name as a register that ends up being implicitly DP-relative. With
+ TIC6X_OP_MEM_UNREG, the contents of a () offset are interpreted as
+ a register if they match one, and failing that as an expression,
+ which must be constant. */
+#define TIC6X_OP_MEM_NOUNREG 0x0020u
+#define TIC6X_OP_MEM_UNREG 0x0040u
+#define TIC6X_OP_CTRL 0x0080u
+#define TIC6X_OP_FUNC_UNIT 0x0100u
+
+/* A register or register pair read by the assembler. */
+typedef struct
+{
+ /* The side the register is on (1 or 2). */
+ unsigned int side;
+ /* The register number (0 to 31). */
+ unsigned int num;
+} tic6x_register;
+
+/* Types of modification of a base address. */
+typedef enum
+ {
+ tic6x_mem_mod_none,
+ tic6x_mem_mod_plus,
+ tic6x_mem_mod_minus,
+ tic6x_mem_mod_preinc,
+ tic6x_mem_mod_predec,
+ tic6x_mem_mod_postinc,
+ tic6x_mem_mod_postdec
+ } tic6x_mem_mod;
+
+/* Scaled [] or unscaled () nature of an offset. */
+typedef enum
+ {
+ tic6x_offset_none,
+ tic6x_offset_scaled,
+ tic6x_offset_unscaled
+ } tic6x_mem_scaling;
+
+/* A memory operand read by the assembler. */
+typedef struct
+{
+ /* The base register. */
+ tic6x_register base_reg;
+ /* How the base register is modified. */
+ tic6x_mem_mod mod;
+ /* Whether there is an offset (required with plain "+" and "-"), and
+ whether it is scaled or unscaled if so. */
+ tic6x_mem_scaling scaled;
+ /* Whether the offset is a register (TRUE) or an expression
+ (FALSE). */
+ bfd_boolean offset_is_reg;
+ /* The offset. */
+ union
+ {
+ expressionS exp;
+ tic6x_register reg;
+ } offset;
+} tic6x_mem_ref;
+
+/* A functional unit in SPMASK operands read by the assembler. */
+typedef struct
+{
+ /* The basic unit. */
+ tic6x_func_unit_base base;
+ /* The side (1 or 2). */
+ unsigned int side;
+} tic6x_func_unit_operand;
+
+/* An operand read by the assembler. */
+typedef struct
+{
+ /* The syntactic form of the operand, as one of the bit-masks
+ above. */
+ unsigned int form;
+ /* The operand value. */
+ union
+ {
+ /* An expression: TIC6X_OP_EXP. */
+ expressionS exp;
+ /* A register: TIC6X_OP_REG, TIC6X_OP_REGPAIR. */
+ tic6x_register reg;
+ /* A memory reference: TIC6X_OP_MEM_NOUNREG,
+ TIC6X_OP_MEM_UNREG. */
+ tic6x_mem_ref mem;
+ /* A control register: TIC6X_OP_CTRL. */
+ tic6x_ctrl_id ctrl;
+ /* A functional unit: TIC6X_OP_FUNC_UNIT. */
+ tic6x_func_unit_operand func_unit;
+ } value;
+} tic6x_operand;
+
+#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
+
+/* Parse a register operand, or part of an operand, starting at *P.
+ If syntactically OK (including that the number is in the range 0 to
+ 31, but not necessarily in range for this architecture), return
+ TRUE, putting the register side and number in *REG and update *P to
+ point immediately after the register number; otherwise return FALSE
+ without changing *P (but possibly changing *REG). Do not print any
+ diagnostics. */
+
+static bfd_boolean
+tic6x_parse_register (char **p, tic6x_register *reg)
+{
+ char *r = *p;
+
+ switch (*r)
+ {
+ case 'a':
+ case 'A':
+ reg->side = 1;
+ break;
+
+ case 'b':
+ case 'B':
+ reg->side = 2;
+ break;
+
+ default:
+ return FALSE;
+ }
+ r++;
+
+ if (*r >= '0' && *r <= '9')
+ {
+ reg->num = *r - '0';
+ r++;
+ }
+ else
+ return FALSE;
+
+ if (reg->num > 0 && *r >= '0' && *r <= '9')
+ {
+ reg->num = reg->num * 10 + (*r - '0');
+ r++;
+ }
+
+ if (*r >= '0' && *r <= '9')
+ return FALSE;
+
+ if (reg->num >= 32)
+ return FALSE;
+ *p = r;
+ return TRUE;
+}
+
+/* Parse the initial two characters of a functional unit name starting
+ at *P. If OK, set *BASE and *SIDE and return TRUE; otherwise,
+ return FALSE. */
+
+static bfd_boolean
+tic6x_parse_func_unit_base (char *p, tic6x_func_unit_base *base,
+ unsigned int *side)
+{
+ bfd_boolean good_func_unit = TRUE;
+ tic6x_func_unit_base maybe_base = tic6x_func_unit_nfu;
+ unsigned int maybe_side = 0;
+
+ switch (p[0])
+ {
+ case 'd':
+ case 'D':
+ maybe_base = tic6x_func_unit_d;
+ break;
+
+ case 'l':
+ case 'L':
+ maybe_base = tic6x_func_unit_l;
+ break;
+
+ case 'm':
+ case 'M':
+ maybe_base = tic6x_func_unit_m;
+ break;
+
+ case 's':
+ case 'S':
+ maybe_base = tic6x_func_unit_s;
+ break;
+
+ default:
+ good_func_unit = FALSE;
+ break;
+ }
+
+ if (good_func_unit)
+ switch (p[1])
+ {
+ case '1':
+ maybe_side = 1;
+ break;
+
+ case '2':
+ maybe_side = 2;
+ break;
+
+ default:
+ good_func_unit = FALSE;
+ break;
+ }
+
+ if (good_func_unit)
+ {
+ *base = maybe_base;
+ *side = maybe_side;
+ }
+
+ return good_func_unit;
+}
+
+/* Parse an operand starting at *P. If the operand parses OK, return
+ TRUE and store the value in *OP; otherwise return FALSE (possibly
+ changing *OP). In any case, update *P to point to the following
+ comma or end of line. The possible operand forms are given by
+ OP_FORMS. For diagnostics, this is operand OPNO of an opcode
+ starting at STR, length OPC_LEN. */
+
+static bfd_boolean
+tic6x_parse_operand (char **p, tic6x_operand *op, unsigned int op_forms,
+ char *str, int opc_len, unsigned int opno)
+{
+ bfd_boolean operand_parsed = FALSE;
+ char *q = *p;
+
+ if ((op_forms & (TIC6X_OP_MEM_NOUNREG | TIC6X_OP_MEM_UNREG))
+ == (TIC6X_OP_MEM_NOUNREG | TIC6X_OP_MEM_UNREG))
+ abort ();
+
+ /* Check for functional unit names for SPMASK and SPMASKR. */
+ if (!operand_parsed && (op_forms & TIC6X_OP_FUNC_UNIT))
+ {
+ tic6x_func_unit_base base = tic6x_func_unit_nfu;
+ unsigned int side = 0;
+
+ if (tic6x_parse_func_unit_base (q, &base, &side))
+ {
+ char *rq = q + 2;
+
+ skip_whitespace (rq);
+ if (is_end_of_line[(unsigned char) *rq] || *rq == ',')
+ {
+ op->form = TIC6X_OP_FUNC_UNIT;
+ op->value.func_unit.base = base;
+ op->value.func_unit.side = side;
+ operand_parsed = TRUE;
+ q = rq;
+ }
+ }
+ }
+
+ /* Check for literal "irp". */
+ if (!operand_parsed && (op_forms & TIC6X_OP_IRP))
+ {
+ if ((q[0] == 'i' || q[0] == 'I')
+ && (q[1] == 'r' || q[1] == 'R')
+ && (q[2] == 'p' || q[2] == 'P'))
+ {
+ char *rq = q + 3;
+
+ skip_whitespace (rq);
+ if (is_end_of_line[(unsigned char) *rq] || *rq == ',')
+ {
+ op->form = TIC6X_OP_IRP;
+ operand_parsed = TRUE;
+ q = rq;
+ }
+ }
+ }
+
+ /* Check for literal "nrp". */
+ if (!operand_parsed && (op_forms & TIC6X_OP_NRP))
+ {
+ if ((q[0] == 'n' || q[0] == 'N')
+ && (q[1] == 'r' || q[1] == 'R')
+ && (q[2] == 'p' || q[2] == 'P'))
+ {
+ char *rq = q + 3;
+
+ skip_whitespace (rq);
+ if (is_end_of_line[(unsigned char) *rq] || *rq == ',')
+ {
+ op->form = TIC6X_OP_NRP;
+ operand_parsed = TRUE;
+ q = rq;
+ }
+ }
+ }
+
+ /* Check for control register names. */
+ if (!operand_parsed && (op_forms & TIC6X_OP_CTRL))
+ {
+ tic6x_ctrl_id crid;
+
+ for (crid = 0; crid < tic6x_ctrl_max; crid++)
+ {
+ size_t len = strlen (tic6x_ctrl_table[crid].name);
+
+ if (strncasecmp (tic6x_ctrl_table[crid].name, q, len) == 0)
+ {
+ char *rq = q + len;
+
+ skip_whitespace (rq);
+ if (is_end_of_line[(unsigned char) *rq] || *rq == ',')
+ {
+ op->form = TIC6X_OP_CTRL;
+ op->value.ctrl = crid;
+ operand_parsed = TRUE;
+ q = rq;
+ if (!(tic6x_ctrl_table[crid].isa_variants & tic6x_features))
+ as_bad (_("control register '%s' not supported "
+ "on this architecture"),
+ tic6x_ctrl_table[crid].name);
+ }
+ }
+ }
+ }
+
+ /* See if this looks like a memory reference. */
+ if (!operand_parsed
+ && (op_forms & (TIC6X_OP_MEM_NOUNREG | TIC6X_OP_MEM_UNREG)))
+ {
+ bfd_boolean mem_ok = TRUE;
+ char *mq = q;
+ tic6x_mem_mod mem_mod = tic6x_mem_mod_none;
+ tic6x_register base_reg;
+ bfd_boolean require_offset, permit_offset;
+ tic6x_mem_scaling scaled;
+ bfd_boolean offset_is_reg;
+ expressionS offset_exp;
+ tic6x_register offset_reg;
+
+ if (*mq == '*')
+ mq++;
+ else
+ mem_ok = FALSE;
+
+ if (mem_ok)
+ {
+ skip_whitespace (mq);
+ switch (*mq)
+ {
+ case '+':
+ if (mq[1] == '+')
+ {
+ mem_mod = tic6x_mem_mod_preinc;
+ mq += 2;
+ }
+ else
+ {
+ mem_mod = tic6x_mem_mod_plus;
+ mq++;
+ }
+ break;
+
+ case '-':
+ if (mq[1] == '-')
+ {
+ mem_mod = tic6x_mem_mod_predec;
+ mq += 2;
+ }
+ else
+ {
+ mem_mod = tic6x_mem_mod_minus;
+ mq++;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (mem_ok)
+ {
+ skip_whitespace (mq);
+ mem_ok = tic6x_parse_register (&mq, &base_reg);
+ }
+
+ if (mem_ok && mem_mod == tic6x_mem_mod_none)
+ {
+ skip_whitespace (mq);
+ if (mq[0] == '+' && mq[1] == '+')
+ {
+ mem_mod = tic6x_mem_mod_postinc;
+ mq += 2;
+ }
+ else if (mq[0] == '-' && mq[1] == '-')
+ {
+ mem_mod = tic6x_mem_mod_postdec;
+ mq += 2;
+ }
+ }
+
+ if (mem_mod == tic6x_mem_mod_none)
+ permit_offset = FALSE;
+ else
+ permit_offset = TRUE;
+ if (mem_mod == tic6x_mem_mod_plus || mem_mod == tic6x_mem_mod_minus)
+ require_offset = TRUE;
+ else
+ require_offset = FALSE;
+ scaled = tic6x_offset_none;
+ offset_is_reg = FALSE;
+
+ if (mem_ok && permit_offset)
+ {
+ char endc = 0;
+
+ skip_whitespace (mq);
+ switch (*mq)
+ {
+ case '[':
+ scaled = tic6x_offset_scaled;
+ mq++;
+ endc = ']';
+ break;
+
+ case '(':
+ scaled = tic6x_offset_unscaled;
+ mq++;
+ endc = ')';
+ break;
+
+ default:
+ break;
+ }
+ if (scaled != tic6x_offset_none)
+ {
+ skip_whitespace (mq);
+ if (scaled == tic6x_offset_scaled
+ || (op_forms & TIC6X_OP_MEM_UNREG))
+ {
+ bfd_boolean reg_ok;
+ char *rq = mq;
+
+ reg_ok = tic6x_parse_register (&rq, &offset_reg);
+ if (reg_ok)
+ {
+ skip_whitespace (rq);
+ if (*rq == endc)
+ {
+ mq = rq;
+ offset_is_reg = TRUE;
+ }
+ }
+ }
+ if (!offset_is_reg)
+ {
+ char *save_input_line_pointer;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = mq;
+ expression (&offset_exp);
+ mq = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+ }
+ skip_whitespace (mq);
+ if (*mq == endc)
+ mq++;
+ else
+ mem_ok = FALSE;
+ }
+ }
+
+ if (mem_ok && require_offset && scaled == tic6x_offset_none)
+ mem_ok = FALSE;
+
+ if (mem_ok)
+ {
+ skip_whitespace (mq);
+ if (!is_end_of_line[(unsigned char) *mq] && *mq != ',')
+ mem_ok = FALSE;
+ }
+
+ if (mem_ok)
+ {
+ op->form = op_forms & (TIC6X_OP_MEM_NOUNREG | TIC6X_OP_MEM_UNREG);
+ op->value.mem.base_reg = base_reg;
+ op->value.mem.mod = mem_mod;
+ op->value.mem.scaled = scaled;
+ op->value.mem.offset_is_reg = offset_is_reg;
+ if (offset_is_reg)
+ op->value.mem.offset.reg = offset_reg;
+ else
+ op->value.mem.offset.exp = offset_exp;
+ operand_parsed = TRUE;
+ q = mq;
+ if (base_reg.num >= tic6x_num_registers)
+ as_bad (_("register number %u not supported on this architecture"),
+ base_reg.num);
+ if (offset_is_reg && offset_reg.num >= tic6x_num_registers)
+ as_bad (_("register number %u not supported on this architecture"),
+ offset_reg.num);
+ }
+ }
+
+ /* See if this looks like a register or register pair. */
+ if (!operand_parsed && (op_forms & (TIC6X_OP_REG | TIC6X_OP_REGPAIR)))
+ {
+ tic6x_register first_reg, second_reg;
+ bfd_boolean reg_ok;
+ char *rq = q;
+
+ reg_ok = tic6x_parse_register (&rq, &first_reg);
+
+ if (reg_ok)
+ {
+ if (*rq == ':' && (op_forms & TIC6X_OP_REGPAIR))
+ {
+ rq++;
+ reg_ok = tic6x_parse_register (&rq, &second_reg);
+ if (reg_ok)
+ {
+ skip_whitespace (rq);
+ if (is_end_of_line[(unsigned char) *rq] || *rq == ',')
+ {
+ if ((second_reg.num & 1)
+ || (first_reg.num != second_reg.num + 1)
+ || (first_reg.side != second_reg.side))
+ as_bad (_("register pair for operand %u of '%.*s'"
+ " not a valid even/odd pair"), opno,
+ opc_len, str);
+ op->form = TIC6X_OP_REGPAIR;
+ op->value.reg = second_reg;
+ operand_parsed = TRUE;
+ q = rq;
+ }
+ }
+ }
+ else if (op_forms & TIC6X_OP_REG)
+ {
+ skip_whitespace (rq);
+ if (is_end_of_line[(unsigned char) *rq] || *rq == ',')
+ {
+ op->form = TIC6X_OP_REG;
+ op->value.reg = first_reg;
+ operand_parsed = TRUE;
+ q = rq;
+ }
+ }
+ }
+ if (operand_parsed)
+ {
+ if (first_reg.num >= tic6x_num_registers)
+ as_bad (_("register number %u not supported on this architecture"),
+ first_reg.num);
+ if (op->form == TIC6X_OP_REGPAIR
+ && second_reg.num >= tic6x_num_registers)
+ as_bad (_("register number %u not supported on this architecture"),
+ second_reg.num);
+ }
+ }
+
+ /* Otherwise, parse it as an expression. */
+ if (!operand_parsed && (op_forms & TIC6X_OP_EXP))
+ {
+ char *save_input_line_pointer;
+
+ save_input_line_pointer = input_line_pointer;
+ input_line_pointer = q;
+ op->form = TIC6X_OP_EXP;
+ expression (&op->value.exp);
+ q = input_line_pointer;
+ input_line_pointer = save_input_line_pointer;
+ operand_parsed = TRUE;
+ }
+
+ if (operand_parsed)
+ {
+ /* Now the operand has been parsed, there must be nothing more
+ before the comma or end of line. */
+ skip_whitespace (q);
+ if (!is_end_of_line[(unsigned char) *q] && *q != ',')
+ {
+ operand_parsed = FALSE;
+ as_bad (_("junk after operand %u of '%.*s'"), opno,
+ opc_len, str);
+ while (!is_end_of_line[(unsigned char) *q] && *q != ',')
+ q++;
+ }
+ }
+ else
+ {
+ /* This could not be parsed as any acceptable form of
+ operand. */
+ switch (op_forms)
+ {
+ case TIC6X_OP_REG | TIC6X_OP_REGPAIR:
+ as_bad (_("bad register or register pair for operand %u of '%.*s'"),
+ opno, opc_len, str);
+ break;
+
+ case TIC6X_OP_REG | TIC6X_OP_CTRL:
+ case TIC6X_OP_REG:
+ as_bad (_("bad register for operand %u of '%.*s'"),
+ opno, opc_len, str);
+ break;
+
+ case TIC6X_OP_REGPAIR:
+ as_bad (_("bad register pair for operand %u of '%.*s'"),
+ opno, opc_len, str);
+ break;
+
+ case TIC6X_OP_FUNC_UNIT:
+ as_bad (_("bad functional unit for operand %u of '%.*s'"),
+ opno, opc_len, str);
+ break;
+
+ default:
+ as_bad (_("bad operand %u of '%.*s'"),
+ opno, opc_len, str);
+ break;
+
+ }
+ while (!is_end_of_line[(unsigned char) *q] && *q != ',')
+ q++;
+ }
+ *p = q;
+ return operand_parsed;
+}
+
+/* Table of assembler operators and associated O_* values. */
+typedef struct
+{
+ const char *name;
+ operatorT op;
+} tic6x_operator_table;
+static const tic6x_operator_table tic6x_operators[] = {
+#define O_dsbt_index O_md1
+ { "dsbt_index", O_dsbt_index },
+#define O_got O_md2
+ { "got", O_got },
+#define O_dpr_got O_md3
+ { "dpr_got", O_dpr_got },
+#define O_dpr_byte O_md4
+ { "dpr_byte", O_dpr_byte },
+#define O_dpr_hword O_md5
+ { "dpr_hword", O_dpr_hword },
+#define O_dpr_word O_md6
+ { "dpr_word", O_dpr_word },
+#define O_pcr_offset O_md7
+ { "pcr_offset", O_pcr_offset }
+};
+
+/* Parse a name in some machine-specific way. Used on C6X to handle
+ assembler operators. */
+
+int
+tic6x_parse_name (const char *name, expressionS *exprP,
+ enum expr_mode mode ATTRIBUTE_UNUSED, char *nextchar)
+{
+ char *p = input_line_pointer;
+ char c, *name_start, *name_end;
+ const char *inner_name;
+ unsigned int i;
+ operatorT op = O_illegal;
+ symbolS *sym, *op_sym = NULL;
+
+ if (*name != '$')
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE (tic6x_operators); i++)
+ if (strcasecmp (name + 1, tic6x_operators[i].name) == 0)
+ {
+ op = tic6x_operators[i].op;
+ break;
+ }
+
+ if (op == O_illegal)
+ return 0;
+
+ *input_line_pointer = *nextchar;
+ skip_whitespace (p);
+
+ if (*p != '(')
+ {
+ *input_line_pointer = 0;
+ return 0;
+ }
+ p++;
+ skip_whitespace (p);
+
+ if (!is_name_beginner (*p))
+ {
+ *input_line_pointer = 0;
+ return 0;
+ }
+
+ name_start = p;
+ p++;
+ while (is_part_of_name (*p))
+ p++;
+ name_end = p;
+ skip_whitespace (p);
+
+ if (op == O_pcr_offset)
+ {
+ char *op_name_start, *op_name_end;
+
+ if (*p != ',')
+ {
+ *input_line_pointer = 0;
+ return 0;
+ }
+ p++;
+ skip_whitespace (p);
+
+ if (!is_name_beginner (*p))
+ {
+ *input_line_pointer = 0;
+ return 0;
+ }
+
+ op_name_start = p;
+ p++;
+ while (is_part_of_name (*p))
+ p++;
+ op_name_end = p;
+ skip_whitespace (p);
+
+ c = *op_name_end;
+ *op_name_end = 0;
+ op_sym = symbol_find_or_make (op_name_start);
+ *op_name_end = c;
+ }
+
+ if (*p != ')')
+ {
+ *input_line_pointer = 0;
+ return 0;
+ }
+
+ input_line_pointer = p + 1;
+ *nextchar = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ c = *name_end;
+ *name_end = 0;
+ inner_name = name_start;
+ if (op == O_dsbt_index && strcmp (inner_name, "__c6xabi_DSBT_BASE") != 0)
+ {
+ as_bad (_("$DSBT_INDEX must be used with __c6xabi_DSBT_BASE"));
+ inner_name = "__c6xabi_DSBT_BASE";
+ }
+ sym = symbol_find_or_make (inner_name);
+ *name_end = c;
+
+ exprP->X_op = op;
+ exprP->X_add_symbol = sym;
+ exprP->X_add_number = 0;
+ exprP->X_op_symbol = op_sym;
+ exprP->X_md = 0;
+
+ return 1;
+}
+
+/* Create a fixup for an expression. Same arguments as fix_new_exp,
+ plus FIX_ADDA which is TRUE for ADDA instructions (to indicate that
+ fixes resolving to constants should have those constants implicitly
+ shifted) and FALSE otherwise, but look for C6X-specific expression
+ types and adjust the relocations or give errors accordingly. */
+
+static void
+tic6x_fix_new_exp (fragS *frag, int where, int size, expressionS *exp,
+ int pcrel, bfd_reloc_code_real_type r_type,
+ bfd_boolean fix_adda)
+{
+ bfd_reloc_code_real_type new_reloc = BFD_RELOC_UNUSED;
+ symbolS *subsy = NULL;
+ fixS *fix;
+
+ switch (exp->X_op)
+ {
+ case O_dsbt_index:
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_SBR_U15_W:
+ new_reloc = BFD_RELOC_C6000_DSBT_INDEX;
+ break;
+
+ default:
+ as_bad (_("$DSBT_INDEX not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_got:
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_SBR_U15_W:
+ new_reloc = BFD_RELOC_C6000_SBR_GOT_U15_W;
+ break;
+
+ default:
+ as_bad (_("$GOT not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_dpr_got:
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_ABS_L16:
+ new_reloc = BFD_RELOC_C6000_SBR_GOT_L16_W;
+ break;
+
+ case BFD_RELOC_C6000_ABS_H16:
+ new_reloc = BFD_RELOC_C6000_SBR_GOT_H16_W;
+ break;
+
+ default:
+ as_bad (_("$DPR_GOT not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_dpr_byte:
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_ABS_S16:
+ new_reloc = BFD_RELOC_C6000_SBR_S16;
+ break;
+
+ case BFD_RELOC_C6000_ABS_L16:
+ new_reloc = BFD_RELOC_C6000_SBR_L16_B;
+ break;
+
+ case BFD_RELOC_C6000_ABS_H16:
+ new_reloc = BFD_RELOC_C6000_SBR_H16_B;
+ break;
+
+ default:
+ as_bad (_("$DPR_BYTE not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_dpr_hword:
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_ABS_L16:
+ new_reloc = BFD_RELOC_C6000_SBR_L16_H;
+ break;
+
+ case BFD_RELOC_C6000_ABS_H16:
+ new_reloc = BFD_RELOC_C6000_SBR_H16_H;
+ break;
+
+ default:
+ as_bad (_("$DPR_HWORD not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_dpr_word:
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_ABS_L16:
+ new_reloc = BFD_RELOC_C6000_SBR_L16_W;
+ break;
+
+ case BFD_RELOC_C6000_ABS_H16:
+ new_reloc = BFD_RELOC_C6000_SBR_H16_W;
+ break;
+
+ default:
+ as_bad (_("$DPR_WORD not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_pcr_offset:
+ subsy = exp->X_op_symbol;
+ switch (r_type)
+ {
+ case BFD_RELOC_C6000_ABS_S16:
+ case BFD_RELOC_C6000_ABS_L16:
+ new_reloc = BFD_RELOC_C6000_PCR_L16;
+ break;
+
+ case BFD_RELOC_C6000_ABS_H16:
+ new_reloc = BFD_RELOC_C6000_PCR_H16;
+ break;
+
+ default:
+ as_bad (_("$PCR_OFFSET not supported in this context"));
+ return;
+ }
+ break;
+
+ case O_symbol:
+ break;
+
+ default:
+ if (pcrel)
+ {
+ as_bad (_("invalid PC-relative operand"));
+ return;
+ }
+ break;
+ }
+
+ if (new_reloc == BFD_RELOC_UNUSED)
+ fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
+ else
+ fix = fix_new (frag, where, size, exp->X_add_symbol, exp->X_add_number,
+ pcrel, new_reloc);
+ fix->tc_fix_data.fix_subsy = subsy;
+ fix->tc_fix_data.fix_adda = fix_adda;
+}
+
+/* Generate a fix for a constant (.word etc.). Needed to ensure these
+ go through the error checking in tic6x_fix_new_exp. */
+
+void
+tic6x_cons_fix_new (fragS *frag, int where, int size, expressionS *exp,
+ bfd_reloc_code_real_type r_type)
+{
+ switch (size)
+ {
+ case 1:
+ r_type = BFD_RELOC_8;
+ break;
+
+ case 2:
+ r_type = BFD_RELOC_16;
+ break;
+
+ case 4:
+ r_type = BFD_RELOC_32;
+ break;
+
+ default:
+ as_bad (_("no %d-byte relocations available"), size);
+ return;
+ }
+
+ tic6x_fix_new_exp (frag, where, size, exp, 0, r_type, FALSE);
+}
+
+/* Initialize target-specific fix data. */
+
+void
+tic6x_init_fix_data (fixS *fixP)
+{
+ fixP->tc_fix_data.fix_adda = FALSE;
+ fixP->tc_fix_data.fix_subsy = NULL;
+}
+
+/* Return true if the fix can be handled by GAS, false if it must
+ be passed through to the linker. */
+
+bfd_boolean
+tic6x_fix_adjustable (fixS *fixP)
+{
+ switch (fixP->fx_r_type)
+ {
+ /* Adjust_reloc_syms doesn't know about the GOT. */
+ case BFD_RELOC_C6000_SBR_GOT_U15_W:
+ case BFD_RELOC_C6000_SBR_GOT_H16_W:
+ case BFD_RELOC_C6000_SBR_GOT_L16_W:
+ case BFD_RELOC_C6000_EHTYPE:
+ return 0;
+
+ case BFD_RELOC_C6000_PREL31:
+ return 0;
+
+ case BFD_RELOC_C6000_PCR_H16:
+ case BFD_RELOC_C6000_PCR_L16:
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+/* Given the fine-grained form of an operand, return the coarse
+ (bit-mask) form. */
+
+static unsigned int
+tic6x_coarse_operand_form (tic6x_operand_form form)
+{
+ switch (form)
+ {
+ case tic6x_operand_asm_const:
+ case tic6x_operand_link_const:
+ return TIC6X_OP_EXP;
+
+ case tic6x_operand_reg:
+ case tic6x_operand_xreg:
+ case tic6x_operand_dreg:
+ case tic6x_operand_areg:
+ case tic6x_operand_retreg:
+ return TIC6X_OP_REG;
+
+ case tic6x_operand_regpair:
+ case tic6x_operand_xregpair:
+ case tic6x_operand_dregpair:
+ return TIC6X_OP_REGPAIR;
+
+ case tic6x_operand_irp:
+ return TIC6X_OP_IRP;
+
+ case tic6x_operand_nrp:
+ return TIC6X_OP_NRP;
+
+ case tic6x_operand_ctrl:
+ return TIC6X_OP_CTRL;
+
+ case tic6x_operand_mem_short:
+ case tic6x_operand_mem_long:
+ case tic6x_operand_mem_deref:
+ return TIC6X_OP_MEM_NOUNREG;
+
+ case tic6x_operand_mem_ndw:
+ return TIC6X_OP_MEM_UNREG;
+
+ case tic6x_operand_func_unit:
+ return TIC6X_OP_FUNC_UNIT;
+
+ default:
+ abort ();
+ }
+}
+
+/* How an operand may match or not match a desired form. If different
+ instruction alternatives fail in different ways, the first failure
+ in this list determines the diagnostic. */
+typedef enum
+ {
+ /* Matches. */
+ tic6x_match_matches,
+ /* Bad coarse form. */
+ tic6x_match_coarse,
+ /* Not constant. */
+ tic6x_match_non_const,
+ /* Register on wrong side. */
+ tic6x_match_wrong_side,
+ /* Not a valid address register. */
+ tic6x_match_bad_address,
+ /* Not a valid return address register. */
+ tic6x_match_bad_return,
+ /* Control register not readable. */
+ tic6x_match_ctrl_write_only,
+ /* Control register not writable. */
+ tic6x_match_ctrl_read_only,
+ /* Not a valid memory reference for this instruction. */
+ tic6x_match_bad_mem
+ } tic6x_operand_match;
+
+/* Return whether an operand matches the given fine-grained form and
+ read/write usage, and, if it does not match, how it fails to match.
+ The main functional unit side is SIDE; the cross-path side is CROSS
+ (the same as SIDE if a cross path not used); the data side is
+ DATA_SIDE. */
+static tic6x_operand_match
+tic6x_operand_matches_form (const tic6x_operand *op, tic6x_operand_form form,
+ tic6x_rw rw, unsigned int side, unsigned int cross,
+ unsigned int data_side)
+{
+ unsigned int coarse = tic6x_coarse_operand_form (form);
+
+ if (coarse != op->form)
+ return tic6x_match_coarse;
+
+ switch (form)
+ {
+ case tic6x_operand_asm_const:
+ if (op->value.exp.X_op == O_constant)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_non_const;
+
+ case tic6x_operand_link_const:
+ case tic6x_operand_irp:
+ case tic6x_operand_nrp:
+ case tic6x_operand_func_unit:
+ /* All expressions are link-time constants, although there may
+ not be relocations to express them in the output file. "irp"
+ and "nrp" are unique operand values. All parsed functional
+ unit names are valid. */
+ return tic6x_match_matches;
+
+ case tic6x_operand_reg:
+ case tic6x_operand_regpair:
+ if (op->value.reg.side == side)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_wrong_side;
+
+ case tic6x_operand_xreg:
+ case tic6x_operand_xregpair:
+ if (op->value.reg.side == cross)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_wrong_side;
+
+ case tic6x_operand_dreg:
+ case tic6x_operand_dregpair:
+ if (op->value.reg.side == data_side)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_wrong_side;
+
+ case tic6x_operand_areg:
+ if (op->value.reg.side != cross)
+ return tic6x_match_wrong_side;
+ else if (op->value.reg.side == 2
+ && (op->value.reg.num == 14 || op->value.reg.num == 15))
+ return tic6x_match_matches;
+ else
+ return tic6x_match_bad_address;
+
+ case tic6x_operand_retreg:
+ if (op->value.reg.side != side)
+ return tic6x_match_wrong_side;
+ else if (op->value.reg.num != 3)
+ return tic6x_match_bad_return;
+ else
+ return tic6x_match_matches;
+
+ case tic6x_operand_ctrl:
+ switch (rw)
+ {
+ case tic6x_rw_read:
+ if (tic6x_ctrl_table[op->value.ctrl].rw == tic6x_rw_read
+ || tic6x_ctrl_table[op->value.ctrl].rw == tic6x_rw_read_write)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_ctrl_write_only;
+
+ case tic6x_rw_write:
+ if (tic6x_ctrl_table[op->value.ctrl].rw == tic6x_rw_write
+ || tic6x_ctrl_table[op->value.ctrl].rw == tic6x_rw_read_write)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_ctrl_read_only;
+
+ default:
+ abort ();
+ }
+
+ case tic6x_operand_mem_deref:
+ if (op->value.mem.mod != tic6x_mem_mod_none)
+ return tic6x_match_bad_mem;
+ else if (op->value.mem.scaled != tic6x_offset_none)
+ abort ();
+ else if (op->value.mem.base_reg.side != side)
+ return tic6x_match_bad_mem;
+ else
+ return tic6x_match_matches;
+
+ case tic6x_operand_mem_short:
+ case tic6x_operand_mem_ndw:
+ if (op->value.mem.base_reg.side != side)
+ return tic6x_match_bad_mem;
+ if (op->value.mem.mod == tic6x_mem_mod_none)
+ {
+ if (op->value.mem.scaled != tic6x_offset_none)
+ abort ();
+ return tic6x_match_matches;
+ }
+ if (op->value.mem.scaled == tic6x_offset_none)
+ {
+ if (op->value.mem.mod == tic6x_mem_mod_plus
+ || op->value.mem.mod == tic6x_mem_mod_minus)
+ abort ();
+ return tic6x_match_matches;
+ }
+ if (op->value.mem.offset_is_reg)
+ {
+ if (op->value.mem.scaled == tic6x_offset_unscaled
+ && form != tic6x_operand_mem_ndw)
+ abort ();
+ if (op->value.mem.offset.reg.side == side)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_bad_mem;
+ }
+ else
+ {
+ if (op->value.mem.offset.exp.X_op == O_constant)
+ return tic6x_match_matches;
+ else
+ return tic6x_match_bad_mem;
+ }
+
+ case tic6x_operand_mem_long:
+ if (op->value.mem.base_reg.side == 2
+ && (op->value.mem.base_reg.num == 14
+ || op->value.mem.base_reg.num == 15))
+ {
+ switch (op->value.mem.mod)
+ {
+ case tic6x_mem_mod_none:
+ if (op->value.mem.scaled != tic6x_offset_none)
+ abort ();
+ return tic6x_match_matches;
+
+ case tic6x_mem_mod_plus:
+ if (op->value.mem.scaled == tic6x_offset_none)
+ abort ();
+ if (op->value.mem.offset_is_reg)
+ return tic6x_match_bad_mem;
+ else if (op->value.mem.scaled == tic6x_offset_scaled
+ && op->value.mem.offset.exp.X_op != O_constant)
+ return tic6x_match_bad_mem;
+ else
+ return tic6x_match_matches;
+
+ case tic6x_mem_mod_minus:
+ case tic6x_mem_mod_preinc:
+ case tic6x_mem_mod_predec:
+ case tic6x_mem_mod_postinc:
+ case tic6x_mem_mod_postdec:
+ return tic6x_match_bad_mem;
+
+ default:
+ abort ();
+ }
+
+ }
+ else
+ return tic6x_match_bad_mem;
+
+ default:
+ abort ();
+ }
+}
+
+/* Return the number of bits shift used with DP-relative coding method
+ CODING. */
+
+static unsigned int
+tic6x_dpr_shift (tic6x_coding_method coding)
+{
+ switch (coding)
+ {
+ case tic6x_coding_ulcst_dpr_byte:
+ return 0;
+
+ case tic6x_coding_ulcst_dpr_half:
+ return 1;
+
+ case tic6x_coding_ulcst_dpr_word:
+ return 2;
+
+ default:
+ abort ();
+ }
+}
+
+/* Return the relocation used with DP-relative coding method
+ CODING. */
+
+static bfd_reloc_code_real_type
+tic6x_dpr_reloc (tic6x_coding_method coding)
+{
+ switch (coding)
+ {
+ case tic6x_coding_ulcst_dpr_byte:
+ return BFD_RELOC_C6000_SBR_U15_B;
+
+ case tic6x_coding_ulcst_dpr_half:
+ return BFD_RELOC_C6000_SBR_U15_H;
+
+ case tic6x_coding_ulcst_dpr_word:
+ return BFD_RELOC_C6000_SBR_U15_W;
+
+ default:
+ abort ();
+ }
+}
+
+/* Given a memory reference *MEM_REF as originally parsed, fill in
+ defaults for missing offsets. */
+
+static void
+tic6x_default_mem_ref (tic6x_mem_ref *mem_ref)
+{
+ switch (mem_ref->mod)
+ {
+ case tic6x_mem_mod_none:
+ if (mem_ref->scaled != tic6x_offset_none)
+ abort ();
+ mem_ref->mod = tic6x_mem_mod_plus;
+ mem_ref->scaled = tic6x_offset_unscaled;
+ mem_ref->offset_is_reg = FALSE;
+ memset (&mem_ref->offset.exp, 0, sizeof mem_ref->offset.exp);
+ mem_ref->offset.exp.X_op = O_constant;
+ mem_ref->offset.exp.X_add_number = 0;
+ mem_ref->offset.exp.X_unsigned = 0;
+ break;
+
+ case tic6x_mem_mod_plus:
+ case tic6x_mem_mod_minus:
+ if (mem_ref->scaled == tic6x_offset_none)
+ abort ();
+ break;
+
+ case tic6x_mem_mod_preinc:
+ case tic6x_mem_mod_predec:
+ case tic6x_mem_mod_postinc:
+ case tic6x_mem_mod_postdec:
+ if (mem_ref->scaled != tic6x_offset_none)
+ break;
+ mem_ref->scaled = tic6x_offset_scaled;
+ mem_ref->offset_is_reg = FALSE;
+ memset (&mem_ref->offset.exp, 0, sizeof mem_ref->offset.exp);
+ mem_ref->offset.exp.X_op = O_constant;
+ mem_ref->offset.exp.X_add_number = 1;
+ mem_ref->offset.exp.X_unsigned = 0;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Return the encoding in the 8-bit field of an SPMASK or SPMASKR
+ instruction of the specified UNIT, side SIDE. */
+
+static unsigned int
+tic6x_encode_spmask (tic6x_func_unit_base unit, unsigned int side)
+{
+ switch (unit)
+ {
+ case tic6x_func_unit_l:
+ return 1 << (side - 1);
+
+ case tic6x_func_unit_s:
+ return 1 << (side + 1);
+
+ case tic6x_func_unit_d:
+ return 1 << (side + 3);
+
+ case tic6x_func_unit_m:
+ return 1 << (side + 5);
+
+ default:
+ abort ();
+ }
+}
+
+/* Try to encode the instruction with opcode number ID and operands
+ OPERANDS (number NUM_OPERANDS), creg value THIS_LINE_CREG and z
+ value THIS_LINE_Z; FUNC_UNIT_SIDE, FUNC_UNIT_CROSS and
+ FUNC_UNIT_DATA_SIDE describe the functional unit specification;
+ SPLOOP_II is the ii value from the previous SPLOOP-family
+ instruction, or 0 if not in such a loop; the only possible problems
+ are operands being out of range (they already match the
+ fine-grained form), and inappropriate predication. If this
+ succeeds, return the encoding and set *OK to TRUE; otherwise return
+ 0 and set *OK to FALSE. If a fix is needed, set *FIX_NEEDED to
+ true and fill in *FIX_EXP, *FIX_PCREL, *FX_R_TYPE and *FIX_ADDA.
+ Print error messages for failure if PRINT_ERRORS is TRUE; the
+ opcode starts at STR and has length OPC_LEN. */
+
+static unsigned int
+tic6x_try_encode (tic6x_opcode_id id, tic6x_operand *operands,
+ unsigned int num_operands, unsigned int this_line_creg,
+ unsigned int this_line_z, unsigned int func_unit_side,
+ unsigned int func_unit_cross,
+ unsigned int func_unit_data_side, int sploop_ii,
+ expressionS **fix_exp, int *fix_pcrel,
+ bfd_reloc_code_real_type *fx_r_type, bfd_boolean *fix_adda,
+ bfd_boolean *fix_needed, bfd_boolean *ok,
+ bfd_boolean print_errors, char *str, int opc_len)
+{
+ const tic6x_opcode *opct;
+ const tic6x_insn_format *fmt;
+ unsigned int opcode_value;
+ unsigned int fld;
+
+ opct = &tic6x_opcode_table[id];
+ fmt = &tic6x_insn_format_table[opct->format];
+ opcode_value = fmt->cst_bits;
+
+ for (fld = 0; fld < opct->num_fixed_fields; fld++)
+ {
+ if (opct->fixed_fields[fld].min_val == opct->fixed_fields[fld].max_val)
+ {
+ const tic6x_insn_field *fldd;
+ fldd = tic6x_field_from_fmt (fmt, opct->fixed_fields[fld].field_id);
+ if (fldd == NULL)
+ abort ();
+ opcode_value |= opct->fixed_fields[fld].min_val << fldd->bitfields[0].low_pos;
+ }
+ }
+
+ for (fld = 0; fld < opct->num_variable_fields; fld++)
+ {
+ const tic6x_insn_field *fldd;
+ unsigned int value;
+ unsigned int opno;
+ unsigned int ffld;
+ offsetT sign_value;
+ unsigned int bits;
+ unsigned int fcyc_bits;
+ expressionS *expp;
+ expressionS ucexp;
+ tic6x_mem_ref mem;
+
+ fldd = tic6x_field_from_fmt (fmt, opct->variable_fields[fld].field_id);
+ if (fldd == NULL)
+ abort ();
+ opno = opct->variable_fields[fld].operand_num;
+ switch (opct->variable_fields[fld].coding_method)
+ {
+ case tic6x_coding_ucst:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op != O_constant)
+ abort ();
+ ucexp = operands[opno].value.exp;
+ unsigned_constant:
+ if (ucexp.X_add_number < 0
+ || ucexp.X_add_number >= (1 << fldd->bitfields[0].width))
+ {
+ if (print_errors)
+ as_bad (_("operand %u of '%.*s' out of range"), opno + 1,
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value = ucexp.X_add_number;
+ break;
+
+ case tic6x_coding_scst:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op != O_constant)
+ {
+ value = 0;
+ /* Opcode table should not permit non-constants without
+ a known relocation for them. */
+ if (fldd->bitfields[0].low_pos != 7 || fldd->bitfields[0].width != 16)
+ abort ();
+ *fix_needed = TRUE;
+ *fix_exp = &operands[opno].value.exp;
+ *fix_pcrel = 0;
+ *fx_r_type = BFD_RELOC_C6000_ABS_S16;
+ *fix_adda = FALSE;
+ break;
+ }
+ sign_value = SEXT (operands[opno].value.exp.X_add_number);
+ signed_constant:
+ if (sign_value < -(1 << (fldd->bitfields[0].width - 1))
+ || (sign_value >= (1 << (fldd->bitfields[0].width - 1))))
+ {
+ if (print_errors)
+ as_bad (_("operand %u of '%.*s' out of range"), opno + 1,
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value = sign_value + (1 << (fldd->bitfields[0].width - 1));
+ value ^= (1 << (fldd->bitfields[0].width - 1));
+ break;
+
+ case tic6x_coding_ucst_minus_one:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op != O_constant)
+ abort ();
+ if (operands[opno].value.exp.X_add_number <= 0
+ || operands[opno].value.exp.X_add_number > (1 << fldd->bitfields[0].width))
+ {
+ if (print_errors)
+ as_bad (_("operand %u of '%.*s' out of range"), opno + 1,
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value = operands[opno].value.exp.X_add_number - 1;
+ break;
+
+ case tic6x_coding_scst_negate:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op != O_constant)
+ abort ();
+ sign_value = SEXT (-operands[opno].value.exp.X_add_number);
+ goto signed_constant;
+
+ case tic6x_coding_ulcst_dpr_byte:
+ case tic6x_coding_ulcst_dpr_half:
+ case tic6x_coding_ulcst_dpr_word:
+ bits = tic6x_dpr_shift (opct->variable_fields[fld].coding_method);
+ switch (operands[opno].form)
+ {
+ case TIC6X_OP_EXP:
+ if (operands[opno].value.exp.X_op == O_constant)
+ {
+ ucexp = operands[opno].value.exp;
+ goto unsigned_constant;
+ }
+ expp = &operands[opno].value.exp;
+ break;
+
+ case TIC6X_OP_MEM_NOUNREG:
+ mem = operands[opno].value.mem;
+ tic6x_default_mem_ref (&mem);
+ if (mem.offset_is_reg)
+ abort ();
+ if (mem.offset.exp.X_op == O_constant)
+ {
+ ucexp = mem.offset.exp;
+ if (mem.scaled == tic6x_offset_unscaled)
+ {
+ if (ucexp.X_add_number & ((1 << bits) - 1))
+ {
+ if (print_errors)
+ as_bad (_("offset in operand %u of '%.*s' not "
+ "divisible by %u"), opno + 1, opc_len,
+ str, 1u << bits);
+ *ok = FALSE;
+ return 0;
+ }
+ ucexp.X_add_number >>= bits;
+ }
+ goto unsigned_constant;
+ }
+ if (mem.scaled != tic6x_offset_unscaled)
+ abort ();
+ if (operands[opno].value.mem.mod == tic6x_mem_mod_none
+ || operands[opno].value.mem.scaled != tic6x_offset_unscaled
+ || operands[opno].value.mem.offset_is_reg)
+ abort ();
+ expp = &operands[opno].value.mem.offset.exp;
+ break;
+
+ default:
+ abort ();
+ }
+ value = 0;
+ /* Opcode table should not use this encoding without a known
+ relocation. */
+ if (fldd->bitfields[0].low_pos != 8 || fldd->bitfields[0].width != 15)
+ abort ();
+ /* We do not check for offset divisibility here; such a
+ check is not needed at this point to encode the value,
+ and if there is eventually a problem it will be detected
+ either in md_apply_fix or at link time. */
+ *fix_needed = TRUE;
+ *fix_exp = expp;
+ *fix_pcrel = 0;
+ *fx_r_type
+ = tic6x_dpr_reloc (opct->variable_fields[fld].coding_method);
+ if (operands[opno].form == TIC6X_OP_EXP)
+ *fix_adda = TRUE;
+ else
+ *fix_adda = FALSE;
+ break;
+
+ case tic6x_coding_lcst_low16:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op == O_constant)
+ value = operands[opno].value.exp.X_add_number & 0xffff;
+ else
+ {
+ value = 0;
+ /* Opcode table should not use this encoding without a
+ known relocation. */
+ if (fldd->bitfields[0].low_pos != 7 || fldd->bitfields[0].width != 16)
+ abort ();
+ *fix_needed = TRUE;
+ *fix_exp = &operands[opno].value.exp;
+ *fix_pcrel = 0;
+ *fx_r_type = BFD_RELOC_C6000_ABS_L16;
+ *fix_adda = FALSE;
+ }
+ break;
+
+ case tic6x_coding_lcst_high16:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op == O_constant)
+ value = (operands[opno].value.exp.X_add_number >> 16) & 0xffff;
+ else
+ {
+ value = 0;
+ /* Opcode table should not use this encoding without a
+ known relocation. */
+ if (fldd->bitfields[0].low_pos != 7 || fldd->bitfields[0].width != 16)
+ abort ();
+ *fix_needed = TRUE;
+ *fix_exp = &operands[opno].value.exp;
+ *fix_pcrel = 0;
+ *fx_r_type = BFD_RELOC_C6000_ABS_H16;
+ *fix_adda = FALSE;
+ }
+ break;
+
+ case tic6x_coding_pcrel:
+ case tic6x_coding_pcrel_half:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ value = 0;
+ *fix_needed = TRUE;
+ *fix_exp = &operands[opno].value.exp;
+ *fix_pcrel = 1;
+ if (fldd->bitfields[0].low_pos == 7 && fldd->bitfields[0].width == 21)
+ *fx_r_type = BFD_RELOC_C6000_PCR_S21;
+ else if (fldd->bitfields[0].low_pos == 16 && fldd->bitfields[0].width == 12)
+ *fx_r_type = BFD_RELOC_C6000_PCR_S12;
+ else if (fldd->bitfields[0].low_pos == 13 && fldd->bitfields[0].width == 10)
+ *fx_r_type = BFD_RELOC_C6000_PCR_S10;
+ else if (fldd->bitfields[0].low_pos == 16 && fldd->bitfields[0].width == 7)
+ *fx_r_type = BFD_RELOC_C6000_PCR_S7;
+ else
+ /* Opcode table should not use this encoding without a
+ known relocation. */
+ abort ();
+ *fix_adda = FALSE;
+ break;
+
+ case tic6x_coding_regpair_lsb:
+ switch (operands[opno].form)
+ {
+ case TIC6X_OP_REGPAIR:
+ value = operands[opno].value.reg.num;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case tic6x_coding_regpair_msb:
+ switch (operands[opno].form)
+ {
+ case TIC6X_OP_REGPAIR:
+ value = operands[opno].value.reg.num + 1;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case tic6x_coding_reg:
+ switch (operands[opno].form)
+ {
+ case TIC6X_OP_REG:
+ case TIC6X_OP_REGPAIR:
+ value = operands[opno].value.reg.num;
+ break;
+
+ case TIC6X_OP_MEM_NOUNREG:
+ case TIC6X_OP_MEM_UNREG:
+ value = operands[opno].value.mem.base_reg.num;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case tic6x_coding_areg:
+ switch (operands[opno].form)
+ {
+ case TIC6X_OP_REG:
+ value = (operands[opno].value.reg.num == 15 ? 1 : 0);
+ break;
+
+ case TIC6X_OP_MEM_NOUNREG:
+ value = (operands[opno].value.mem.base_reg.num == 15 ? 1 : 0);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case tic6x_coding_crlo:
+ if (operands[opno].form != TIC6X_OP_CTRL)
+ abort ();
+ value = tic6x_ctrl_table[operands[opno].value.ctrl].crlo;
+ break;
+
+ case tic6x_coding_crhi:
+ if (operands[opno].form != TIC6X_OP_CTRL)
+ abort ();
+ value = 0;
+ break;
+
+ case tic6x_coding_reg_shift:
+ if (operands[opno].form != TIC6X_OP_REGPAIR)
+ abort ();
+ value = operands[opno].value.reg.num >> 1;
+ break;
+
+ case tic6x_coding_mem_offset:
+ if (operands[opno].form != TIC6X_OP_MEM_NOUNREG)
+ abort ();
+ mem = operands[opno].value.mem;
+ tic6x_default_mem_ref (&mem);
+ if (mem.offset_is_reg)
+ {
+ if (mem.scaled != tic6x_offset_scaled)
+ abort ();
+ value = mem.offset.reg.num;
+ }
+ else
+ {
+ int scale;
+
+ if (mem.offset.exp.X_op != O_constant)
+ abort ();
+ switch (mem.scaled)
+ {
+ case tic6x_offset_scaled:
+ scale = 1;
+ break;
+
+ case tic6x_offset_unscaled:
+ scale = opct->operand_info[opno].size;
+ if (scale != 1 && scale != 2 && scale != 4 && scale != 8)
+ abort ();
+ break;
+
+ default:
+ abort ();
+ }
+ if (mem.offset.exp.X_add_number < 0
+ || mem.offset.exp.X_add_number >= (1 << fldd->bitfields[0].width) * scale)
+ {
+ if (print_errors)
+ as_bad (_("offset in operand %u of '%.*s' out of range"),
+ opno + 1, opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ if (mem.offset.exp.X_add_number % scale)
+ {
+ if (print_errors)
+ as_bad (_("offset in operand %u of '%.*s' not "
+ "divisible by %u"),
+ opno + 1, opc_len, str, scale);
+ *ok = FALSE;
+ return 0;
+ }
+ value = mem.offset.exp.X_add_number / scale;
+ }
+ break;
+
+ case tic6x_coding_mem_offset_noscale:
+ if (operands[opno].form != TIC6X_OP_MEM_UNREG)
+ abort ();
+ mem = operands[opno].value.mem;
+ tic6x_default_mem_ref (&mem);
+ if (mem.offset_is_reg)
+ value = mem.offset.reg.num;
+ else
+ {
+ if (mem.offset.exp.X_op != O_constant)
+ abort ();
+ if (mem.offset.exp.X_add_number < 0
+ || mem.offset.exp.X_add_number >= (1 << fldd->bitfields[0].width))
+ {
+ if (print_errors)
+ as_bad (_("offset in operand %u of '%.*s' out of range"),
+ opno + 1, opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value = mem.offset.exp.X_add_number;
+ }
+ break;
+
+ case tic6x_coding_mem_mode:
+ if (operands[opno].form != TIC6X_OP_MEM_NOUNREG
+ && operands[opno].form != TIC6X_OP_MEM_UNREG)
+ abort ();
+ mem = operands[opno].value.mem;
+ tic6x_default_mem_ref (&mem);
+ switch (mem.mod)
+ {
+ case tic6x_mem_mod_plus:
+ value = 1;
+ break;
+
+ case tic6x_mem_mod_minus:
+ value = 0;
+ break;
+
+ case tic6x_mem_mod_preinc:
+ value = 9;
+ break;
+
+ case tic6x_mem_mod_predec:
+ value = 8;
+ break;
+
+ case tic6x_mem_mod_postinc:
+ value = 11;
+ break;
+
+ case tic6x_mem_mod_postdec:
+ value = 10;
+ break;
+
+ default:
+ abort ();
+ }
+ value += (mem.offset_is_reg ? 4 : 0);
+ break;
+
+ case tic6x_coding_scaled:
+ if (operands[opno].form != TIC6X_OP_MEM_UNREG)
+ abort ();
+ mem = operands[opno].value.mem;
+ tic6x_default_mem_ref (&mem);
+ switch (mem.scaled)
+ {
+ case tic6x_offset_unscaled:
+ value = 0;
+ break;
+
+ case tic6x_offset_scaled:
+ value = 1;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case tic6x_coding_spmask:
+ /* The position of such a field is hardcoded in the handling
+ of "||^". */
+ if (fldd->bitfields[0].low_pos != 18)
+ abort ();
+ value = 0;
+ for (opno = 0; opno < num_operands; opno++)
+ {
+ unsigned int v;
+
+ v = tic6x_encode_spmask (operands[opno].value.func_unit.base,
+ operands[opno].value.func_unit.side);
+ if (value & v)
+ {
+ if (print_errors)
+ as_bad (_("functional unit already masked for operand "
+ "%u of '%.*s'"), opno + 1, opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value |= v;
+ }
+ break;
+
+ case tic6x_coding_reg_unused:
+ /* This is a placeholder; correct handling goes along with
+ resource constraint checks. */
+ value = 0;
+ break;
+
+ case tic6x_coding_fstg:
+ case tic6x_coding_fcyc:
+ if (operands[opno].form != TIC6X_OP_EXP)
+ abort ();
+ if (operands[opno].value.exp.X_op != O_constant)
+ abort ();
+ if (!sploop_ii)
+ {
+ if (print_errors)
+ as_bad (_("'%.*s' instruction not in a software "
+ "pipelined loop"),
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+
+ if (sploop_ii <= 1)
+ fcyc_bits = 0;
+ else if (sploop_ii <= 2)
+ fcyc_bits = 1;
+ else if (sploop_ii <= 4)
+ fcyc_bits = 2;
+ else if (sploop_ii <= 8)
+ fcyc_bits = 3;
+ else if (sploop_ii <= 14)
+ fcyc_bits = 4;
+ else
+ abort ();
+ if (fcyc_bits > fldd->bitfields[0].width)
+ abort ();
+
+ if (opct->variable_fields[fld].coding_method == tic6x_coding_fstg)
+ {
+ int i, t;
+ if (operands[opno].value.exp.X_add_number < 0
+ || (operands[opno].value.exp.X_add_number
+ >= (1 << (fldd->bitfields[0].width - fcyc_bits))))
+ {
+ if (print_errors)
+ as_bad (_("operand %u of '%.*s' out of range"), opno + 1,
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value = operands[opno].value.exp.X_add_number;
+ for (t = 0, i = fcyc_bits; i < fldd->bitfields[0].width; i++)
+ {
+ t = (t << 1) | (value & 1);
+ value >>= 1;
+ }
+ value = t << fcyc_bits;
+ }
+ else
+ {
+ if (operands[opno].value.exp.X_add_number < 0
+ || (operands[opno].value.exp.X_add_number >= sploop_ii))
+ {
+ if (print_errors)
+ as_bad (_("operand %u of '%.*s' out of range"), opno + 1,
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ value = operands[opno].value.exp.X_add_number;
+ }
+ break;
+
+ case tic6x_coding_fu:
+ value = func_unit_side == 2 ? 1 : 0;
+ break;
+
+ case tic6x_coding_data_fu:
+ value = func_unit_data_side == 2 ? 1 : 0;
+ break;
+
+ case tic6x_coding_xpath:
+ value = func_unit_cross;
+ break;
+
+ default:
+ abort ();
+ }
+
+ for (ffld = 0; ffld < opct->num_fixed_fields; ffld++)
+ if ((opct->fixed_fields[ffld].field_id
+ == opct->variable_fields[fld].field_id)
+ && (value < opct->fixed_fields[ffld].min_val
+ || value > opct->fixed_fields[ffld].max_val))
+ {
+ if (print_errors)
+ as_bad (_("operand %u of '%.*s' out of range"), opno + 1,
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+
+ opcode_value |= value << fldd->bitfields[0].low_pos;
+ }
+
+ if (this_line_creg)
+ {
+ const tic6x_insn_field *creg;
+ const tic6x_insn_field *z;
+
+ creg = tic6x_field_from_fmt (fmt, tic6x_field_creg);
+ if (creg == NULL)
+ {
+ if (print_errors)
+ as_bad (_("instruction '%.*s' cannot be predicated"),
+ opc_len, str);
+ *ok = FALSE;
+ return 0;
+ }
+ z = tic6x_field_from_fmt (fmt, tic6x_field_z);
+ /* If there is a creg field, there must be a z field; otherwise
+ there is an error in the format table. */
+ if (z == NULL)
+ abort ();
+
+ opcode_value |= this_line_creg << creg->bitfields[0].low_pos;
+ opcode_value |= this_line_z << z->bitfields[0].low_pos;
+ }
+
+ *ok = TRUE;
+ return opcode_value;
+}
+
+/* Convert the target integer stored in N bytes in BUF to a host
+ integer, returning that value. */
+
+static valueT
+md_chars_to_number (char *buf, int n)
+{
+ valueT result = 0;
+ unsigned char *p = (unsigned char *) buf;
+
+ if (target_big_endian)
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*p++ & 0xff);
+ }
+ }
+ else
+ {
+ while (n--)
+ {
+ result <<= 8;
+ result |= (p[n] & 0xff);
+ }
+ }
+
+ return result;
+}
+
+/* Assemble the instruction starting at STR (an opcode, with the
+ opcode name all-lowercase). */
+
+void
+md_assemble (char *str)
+{
+ char *p;
+ int opc_len;
+ bfd_boolean this_line_parallel;
+ bfd_boolean this_line_spmask;
+ unsigned int this_line_creg;
+ unsigned int this_line_z;
+ tic6x_label_list *this_insn_label_list;
+ segment_info_type *seginfo;
+ tic6x_opcode_list *opc_list, *opc;
+ tic6x_func_unit_base func_unit_base = tic6x_func_unit_nfu;
+ unsigned int func_unit_side = 0;
+ unsigned int func_unit_cross = 0;
+ unsigned int cross_side = 0;
+ unsigned int func_unit_data_side = 0;
+ unsigned int max_matching_opcodes, num_matching_opcodes;
+ tic6x_opcode_id *opcm = NULL;
+ unsigned int opc_rank[TIC6X_NUM_PREFER];
+ const tic6x_opcode *opct = NULL;
+ int min_rank, try_rank, max_rank;
+ bfd_boolean num_operands_permitted[TIC6X_MAX_SOURCE_OPERANDS + 1]
+ = { FALSE };
+ unsigned int operand_forms[TIC6X_MAX_SOURCE_OPERANDS] = { 0 };
+ tic6x_operand operands[TIC6X_MAX_SOURCE_OPERANDS];
+ unsigned int max_num_operands;
+ unsigned int num_operands_read;
+ bfd_boolean ok_this_arch, ok_this_fu, ok_this_arch_fu;
+ bfd_boolean bad_operands = FALSE;
+ unsigned int opcode_value;
+ bfd_boolean encoded_ok;
+ bfd_boolean fix_needed = FALSE;
+ expressionS *fix_exp = NULL;
+ int fix_pcrel = 0;
+ bfd_reloc_code_real_type fx_r_type = BFD_RELOC_UNUSED;
+ bfd_boolean fix_adda = FALSE;
+ fragS *insn_frag;
+ char *output;
+
+ p = str;
+ while (*p && !is_end_of_line[(unsigned char) *p] && *p != ' ')
+ p++;
+
+ /* This function should only have been called when there is actually
+ an instruction to assemble. */
+ if (p == str)
+ abort ();
+
+ /* Now an instruction has been seen, architecture attributes from
+ .arch directives merge with rather than overriding the previous
+ value. */
+ tic6x_seen_insns = TRUE;
+ /* If no .arch directives or -march options have been seen, we are
+ assessing instruction validity based on the C674X default, so set
+ the attribute accordingly. */
+ if (tic6x_arch_attribute == C6XABI_Tag_ISA_none)
+ tic6x_arch_attribute = C6XABI_Tag_ISA_C674X;
+
+ /* Reset global settings for parallel bars and predicates now to
+ avoid extra errors if there are problems with this opcode. */
+ this_line_parallel = tic6x_line_parallel;
+ this_line_spmask = tic6x_line_spmask;
+ this_line_creg = tic6x_line_creg;
+ this_line_z = tic6x_line_z;
+ tic6x_line_parallel = FALSE;
+ tic6x_line_spmask = FALSE;
+ tic6x_line_creg = 0;
+ tic6x_line_z = 0;
+ seginfo = seg_info (now_seg);
+ this_insn_label_list = seginfo->tc_segment_info_data.label_list;
+ seginfo->tc_segment_info_data.label_list = NULL;
+
+ opc_list = hash_find_n (opcode_hash, str, p - str);
+ if (opc_list == NULL)
+ {
+ char c = *p;
+ *p = 0;
+ as_bad (_("unknown opcode '%s'"), str);
+ *p = c;
+ return;
+ }
+
+ opc_len = p - str;
+ skip_whitespace (p);
+
+ /* See if there is something that looks like a functional unit
+ specifier. */
+ if (*p == '.')
+ {
+ bfd_boolean good_func_unit;
+ tic6x_func_unit_base maybe_base = tic6x_func_unit_nfu;
+ unsigned int maybe_side = 0;
+ unsigned int maybe_cross = 0;
+ unsigned int maybe_data_side = 0;
+
+ good_func_unit = tic6x_parse_func_unit_base (p + 1, &maybe_base,
+ &maybe_side);
+
+ if (good_func_unit)
+ {
+ if (p[3] == ' ' || is_end_of_line[(unsigned char) p[3]])
+ p += 3;
+ else if ((p[3] == 'x' || p[3] == 'X')
+ && (p[4] == ' ' || is_end_of_line[(unsigned char) p[4]]))
+ {
+ maybe_cross = 1;
+ p += 4;
+ }
+ else if (maybe_base == tic6x_func_unit_d
+ && (p[3] == 't' || p[3] == 'T')
+ && (p[4] == '1' || p[4] == '2')
+ && (p[5] == ' ' || is_end_of_line[(unsigned char) p[5]]))
+ {
+ maybe_data_side = p[4] - '0';
+ p += 5;
+ }
+ else
+ good_func_unit = FALSE;
+ }
+
+ if (good_func_unit)
+ {
+ func_unit_base = maybe_base;
+ func_unit_side = maybe_side;
+ func_unit_cross = maybe_cross;
+ cross_side = (func_unit_cross ? 3 - func_unit_side : func_unit_side);
+ func_unit_data_side = maybe_data_side;
+ }
+
+ skip_whitespace (p);
+ }
+
+ /* Determine which entries in the opcode table match, and the
+ associated permitted forms of operands. */
+ max_matching_opcodes = 0;
+ for (opc = opc_list; opc; opc = opc->next)
+ max_matching_opcodes++;
+ num_matching_opcodes = 0;
+ opcm = xmalloc (max_matching_opcodes * sizeof (*opcm));
+ max_num_operands = 0;
+ ok_this_arch = FALSE;
+ ok_this_fu = FALSE;
+ ok_this_arch_fu = FALSE;
+ for (opc = opc_list; opc; opc = opc->next)
+ {
+ unsigned int num_operands;
+ unsigned int i;
+ bfd_boolean this_opc_arch_ok = TRUE;
+ bfd_boolean this_opc_fu_ok = TRUE;
+
+ if (tic6x_insn_format_table[tic6x_opcode_table[opc->id].format].num_bits
+ != 32)
+ continue;
+ if (!(tic6x_opcode_table[opc->id].isa_variants & tic6x_features))
+ this_opc_arch_ok = FALSE;
+ if (tic6x_opcode_table[opc->id].func_unit != func_unit_base)
+ this_opc_fu_ok = FALSE;
+ if (func_unit_side == 1
+ && (tic6x_opcode_table[opc->id].flags & TIC6X_FLAG_SIDE_B_ONLY))
+ this_opc_fu_ok = FALSE;
+ if (func_unit_cross
+ && (tic6x_opcode_table[opc->id].flags & TIC6X_FLAG_NO_CROSS))
+ this_opc_fu_ok = FALSE;
+ if (!func_unit_data_side
+ && (tic6x_opcode_table[opc->id].flags
+ & (TIC6X_FLAG_LOAD | TIC6X_FLAG_STORE)))
+ this_opc_fu_ok = FALSE;
+ if (func_unit_data_side
+ && !(tic6x_opcode_table[opc->id].flags
+ & (TIC6X_FLAG_LOAD | TIC6X_FLAG_STORE)))
+ this_opc_fu_ok = FALSE;
+ if (func_unit_data_side == 1
+ && (tic6x_opcode_table[opc->id].flags & TIC6X_FLAG_SIDE_T2_ONLY))
+ this_opc_fu_ok = FALSE;
+ if (this_opc_arch_ok)
+ ok_this_arch = TRUE;
+ if (this_opc_fu_ok)
+ ok_this_fu = TRUE;
+ if (!this_opc_arch_ok || !this_opc_fu_ok)
+ continue;
+ ok_this_arch_fu = TRUE;
+ opcm[num_matching_opcodes] = opc->id;
+ num_matching_opcodes++;
+ num_operands = tic6x_opcode_table[opc->id].num_operands;
+
+ if (tic6x_opcode_table[opc->id].flags & TIC6X_FLAG_SPMASK)
+ {
+ if (num_operands != 1
+ || (tic6x_opcode_table[opc->id].operand_info[0].form
+ != tic6x_operand_func_unit))
+ abort ();
+ num_operands = 8;
+ for (i = 0; i < num_operands; i++)
+ {
+ operand_forms[i]
+ |= tic6x_coarse_operand_form (tic6x_operand_func_unit);
+ num_operands_permitted[i] = TRUE;
+ }
+ }
+ else
+ {
+ for (i = 0; i < num_operands; i++)
+ {
+ tic6x_operand_form f
+ = tic6x_opcode_table[opc->id].operand_info[i].form;
+
+ operand_forms[i] |= tic6x_coarse_operand_form (f);
+ }
+ }
+ num_operands_permitted[num_operands] = TRUE;
+ if (num_operands > max_num_operands)
+ max_num_operands = num_operands;
+ }
+
+ if (!ok_this_arch)
+ {
+ as_bad (_("'%.*s' instruction not supported on this architecture"),
+ opc_len, str);
+ free (opcm);
+ return;
+ }
+
+ if (!ok_this_fu)
+ {
+ as_bad (_("'%.*s' instruction not supported on this functional unit"),
+ opc_len, str);
+ free (opcm);
+ return;
+ }
+
+ if (!ok_this_arch_fu)
+ {
+ as_bad (_("'%.*s' instruction not supported on this functional unit"
+ " for this architecture"),
+ opc_len, str);
+ free (opcm);
+ return;
+ }
+
+ /* If there were no instructions matching the above availability
+ checks, we should now have given an error and returned. */
+ if (num_matching_opcodes == 0)
+ abort ();
+
+ num_operands_read = 0;
+ while (TRUE)
+ {
+ skip_whitespace (p);
+ if (is_end_of_line[(unsigned char) *p])
+ {
+ if (num_operands_read > 0)
+ {
+ as_bad (_("missing operand after comma"));
+ bad_operands = TRUE;
+ }
+ break;
+ }
+
+ if (max_num_operands == 0)
+ {
+ as_bad (_("too many operands to '%.*s'"), opc_len, str);
+ bad_operands = TRUE;
+ break;
+ }
+
+ if (!tic6x_parse_operand (&p, &operands[num_operands_read],
+ operand_forms[num_operands_read], str, opc_len,
+ num_operands_read + 1))
+ bad_operands = TRUE;
+ num_operands_read++;
+
+ if (is_end_of_line[(unsigned char) *p])
+ break;
+ else if (*p == ',')
+ {
+ p++;
+ if (num_operands_read == max_num_operands)
+ {
+ as_bad (_("too many operands to '%.*s'"), opc_len, str);
+ bad_operands = TRUE;
+ break;
+ }
+ continue;
+ }
+ else
+ /* Operand parsing should consume whole operands. */
+ abort ();
+ }
+
+ if (!bad_operands && !num_operands_permitted[num_operands_read])
+ {
+ as_bad (_("bad number of operands to '%.*s'"), opc_len, str);
+ bad_operands = TRUE;
+ }
+
+ if (!bad_operands)
+ {
+ /* Each operand is of the right syntactic form for some opcode
+ choice, and the number of operands is valid. Check that each
+ operand is OK in detail for some opcode choice with the right
+ number of operands. */
+ unsigned int i;
+
+ for (i = 0; i < num_operands_read; i++)
+ {
+ bfd_boolean coarse_ok = FALSE;
+ bfd_boolean fine_ok = FALSE;
+ tic6x_operand_match fine_failure = tic6x_match_matches;
+ unsigned int j;
+
+ for (j = 0; j < num_matching_opcodes; j++)
+ {
+ tic6x_operand_form f;
+ tic6x_rw rw;
+ unsigned int cf;
+ tic6x_operand_match this_fine_failure;
+
+ if (tic6x_opcode_table[opcm[j]].flags & TIC6X_FLAG_SPMASK)
+ {
+ f = tic6x_operand_func_unit;
+ rw = tic6x_rw_none;
+ }
+ else
+ {
+ if (tic6x_opcode_table[opcm[j]].num_operands
+ != num_operands_read)
+ continue;
+
+ f = tic6x_opcode_table[opcm[j]].operand_info[i].form;
+ rw = tic6x_opcode_table[opcm[j]].operand_info[i].rw;
+ }
+ cf = tic6x_coarse_operand_form (f);
+
+ if (operands[i].form != cf)
+ continue;
+
+ coarse_ok = TRUE;
+ this_fine_failure
+ = tic6x_operand_matches_form (&operands[i], f, rw,
+ func_unit_side,
+ cross_side,
+ func_unit_data_side);
+ if (this_fine_failure == tic6x_match_matches)
+ {
+ fine_ok = TRUE;
+ break;
+ }
+ if (fine_failure == tic6x_match_matches
+ || fine_failure > this_fine_failure)
+ fine_failure = this_fine_failure;
+ }
+
+ /* No instructions should have operand syntactic forms only
+ acceptable with certain numbers of operands, so no
+ diagnostic for this case. */
+ if (!coarse_ok)
+ abort ();
+
+ if (!fine_ok)
+ {
+ switch (fine_failure)
+ {
+ case tic6x_match_non_const:
+ as_bad (_("operand %u of '%.*s' not constant"),
+ i + 1, opc_len, str);
+ break;
+
+ case tic6x_match_wrong_side:
+ as_bad (_("operand %u of '%.*s' on wrong side"),
+ i + 1, opc_len, str);
+ break;
+
+ case tic6x_match_bad_return:
+ as_bad (_("operand %u of '%.*s' not a valid return "
+ "address register"),
+ i + 1, opc_len, str);
+ break;
+
+ case tic6x_match_ctrl_write_only:
+ as_bad (_("operand %u of '%.*s' is write-only"),
+ i + 1, opc_len, str);
+ break;
+
+ case tic6x_match_ctrl_read_only:
+ as_bad (_("operand %u of '%.*s' is read-only"),
+ i + 1, opc_len, str);
+ break;
+
+ case tic6x_match_bad_mem:
+ as_bad (_("operand %u of '%.*s' not a valid memory "
+ "reference"),
+ i + 1, opc_len, str);
+ break;
+
+ case tic6x_match_bad_address:
+ as_bad (_("operand %u of '%.*s' not a valid base "
+ "address register"),
+ i + 1, opc_len, str);
+ break;
+
+ default:
+ abort ();
+ }
+ bad_operands = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (!bad_operands)
+ {
+ /* Each operand is OK for some opcode choice, and the number of
+ operands is valid. Check whether there is an opcode choice
+ for which all operands are simultaneously valid. */
+ unsigned int i;
+ bfd_boolean found_match = FALSE;
+
+ for (i = 0; i < TIC6X_NUM_PREFER; i++)
+ opc_rank[i] = (unsigned int) -1;
+
+ min_rank = TIC6X_NUM_PREFER - 1;
+ max_rank = 0;
+
+ for (i = 0; i < num_matching_opcodes; i++)
+ {
+ unsigned int j;
+ bfd_boolean this_matches = TRUE;
+
+ if (!(tic6x_opcode_table[opcm[i]].flags & TIC6X_FLAG_SPMASK)
+ && tic6x_opcode_table[opcm[i]].num_operands != num_operands_read)
+ continue;
+
+ for (j = 0; j < num_operands_read; j++)
+ {
+ tic6x_operand_form f;
+ tic6x_rw rw;
+
+ if (tic6x_opcode_table[opcm[i]].flags & TIC6X_FLAG_SPMASK)
+ {
+ f = tic6x_operand_func_unit;
+ rw = tic6x_rw_none;
+ }
+ else
+ {
+ f = tic6x_opcode_table[opcm[i]].operand_info[j].form;
+ rw = tic6x_opcode_table[opcm[i]].operand_info[j].rw;
+ }
+ if (tic6x_operand_matches_form (&operands[j], f, rw,
+ func_unit_side,
+ cross_side,
+ func_unit_data_side)
+ != tic6x_match_matches)
+ {
+ this_matches = FALSE;
+ break;
+ }
+ }
+
+ if (this_matches)
+ {
+ int rank = TIC6X_PREFER_VAL (tic6x_opcode_table[opcm[i]].flags);
+
+ if (rank < min_rank)
+ min_rank = rank;
+ if (rank > max_rank)
+ max_rank = rank;
+
+ if (opc_rank[rank] == (unsigned int) -1)
+ opc_rank[rank] = i;
+ else
+ /* The opcode table should provide a total ordering
+ for all cases where multiple matches may get
+ here. */
+ abort ();
+
+ found_match = TRUE;
+ }
+ }
+
+ if (!found_match)
+ {
+ as_bad (_("bad operand combination for '%.*s'"), opc_len, str);
+ bad_operands = TRUE;
+ }
+ }
+
+ if (bad_operands)
+ {
+ free (opcm);
+ return;
+ }
+
+ opcode_value = 0;
+ encoded_ok = FALSE;
+ for (try_rank = max_rank; try_rank >= min_rank; try_rank--)
+ {
+ fix_needed = FALSE;
+
+ if (opc_rank[try_rank] == (unsigned int) -1)
+ continue;
+
+ opcode_value = tic6x_try_encode (opcm[opc_rank[try_rank]], operands,
+ num_operands_read, this_line_creg,
+ this_line_z, func_unit_side,
+ func_unit_cross, func_unit_data_side,
+ seginfo->tc_segment_info_data.sploop_ii,
+ &fix_exp, &fix_pcrel, &fx_r_type,
+ &fix_adda, &fix_needed, &encoded_ok,
+ (try_rank == min_rank ? TRUE : FALSE),
+ str, opc_len);
+ if (encoded_ok)
+ {
+ opct = &tic6x_opcode_table[opcm[opc_rank[try_rank]]];
+ break;
+ }
+ }
+
+ free (opcm);
+
+ if (!encoded_ok)
+ return;
+
+ if (this_line_parallel)
+ {
+ insn_frag = seginfo->tc_segment_info_data.execute_packet_frag;
+ if (insn_frag == NULL)
+ {
+ as_bad (_("parallel instruction not following another instruction"));
+ return;
+ }
+
+ if (insn_frag->fr_fix >= 32)
+ {
+ as_bad (_("too many instructions in execute packet"));
+ return;
+ }
+
+ if (this_insn_label_list != NULL)
+ as_bad (_("label not at start of execute packet"));
+
+ if (opct->flags & TIC6X_FLAG_FIRST)
+ as_bad (_("'%.*s' instruction not at start of execute packet"),
+ opc_len, str);
+
+ *seginfo->tc_segment_info_data.last_insn_lsb |= 0x1;
+ output = insn_frag->fr_literal + insn_frag->fr_fix;
+ }
+ else
+ {
+ tic6x_label_list *l;
+
+ seginfo->tc_segment_info_data.spmask_addr = NULL;
+ seginfo->tc_segment_info_data.func_units_used = 0;
+
+ /* Start a new frag for this execute packet. */
+ if (frag_now_fix () != 0)
+ {
+ if (frag_now->fr_type != rs_machine_dependent)
+ frag_wane (frag_now);
+
+ frag_new (0);
+ }
+ frag_grow (32);
+ insn_frag = seginfo->tc_segment_info_data.execute_packet_frag = frag_now;
+ for (l = this_insn_label_list; l; l = l->next)
+ {
+ symbol_set_frag (l->label, frag_now);
+ S_SET_VALUE (l->label, 0);
+ S_SET_SEGMENT (l->label, now_seg);
+ }
+ tic6x_free_label_list (this_insn_label_list);
+ dwarf2_emit_insn (0);
+ output = frag_var (rs_machine_dependent, 32, 32, 0, NULL, 0, NULL);
+ /* This must be the same as the frag to which a pointer was just
+ saved. */
+ if (output != insn_frag->fr_literal)
+ abort ();
+ insn_frag->tc_frag_data.is_insns = TRUE;
+ insn_frag->tc_frag_data.can_cross_fp_boundary
+ = tic6x_can_cross_fp_boundary;
+ }
+
+ if (func_unit_base != tic6x_func_unit_nfu)
+ {
+ unsigned int func_unit_enc;
+
+ func_unit_enc = tic6x_encode_spmask (func_unit_base, func_unit_side);
+
+ if (seginfo->tc_segment_info_data.func_units_used & func_unit_enc)
+ as_bad (_("functional unit already used in this execute packet"));
+
+ seginfo->tc_segment_info_data.func_units_used |= func_unit_enc;
+ }
+
+ if (opct->flags & TIC6X_FLAG_SPLOOP)
+ {
+ if (seginfo->tc_segment_info_data.sploop_ii)
+ as_bad (_("nested software pipelined loop"));
+ if (num_operands_read != 1
+ || operands[0].form != TIC6X_OP_EXP
+ || operands[0].value.exp.X_op != O_constant)
+ abort ();
+ seginfo->tc_segment_info_data.sploop_ii
+ = operands[0].value.exp.X_add_number;
+ }
+ else if (opct->flags & TIC6X_FLAG_SPKERNEL)
+ {
+ if (!seginfo->tc_segment_info_data.sploop_ii)
+ as_bad (_("'%.*s' instruction not in a software pipelined loop"),
+ opc_len, str);
+ seginfo->tc_segment_info_data.sploop_ii = 0;
+ }
+
+ if (this_line_spmask)
+ {
+ if (seginfo->tc_segment_info_data.spmask_addr == NULL)
+ as_bad (_("'||^' without previous SPMASK"));
+ else if (func_unit_base == tic6x_func_unit_nfu)
+ as_bad (_("cannot mask instruction using no functional unit"));
+ else
+ {
+ unsigned int spmask_opcode;
+ unsigned int mask_bit;
+
+ spmask_opcode
+ = md_chars_to_number (seginfo->tc_segment_info_data.spmask_addr,
+ 4);
+ mask_bit = tic6x_encode_spmask (func_unit_base, func_unit_side);
+ mask_bit <<= 18;
+ if (spmask_opcode & mask_bit)
+ as_bad (_("functional unit already masked"));
+ spmask_opcode |= mask_bit;
+ md_number_to_chars (seginfo->tc_segment_info_data.spmask_addr,
+ spmask_opcode, 4);
+ }
+ }
+
+ record_alignment (now_seg, 5);
+ md_number_to_chars (output, opcode_value, 4);
+ if (fix_needed)
+ tic6x_fix_new_exp (insn_frag, output - insn_frag->fr_literal, 4, fix_exp,
+ fix_pcrel, fx_r_type, fix_adda);
+ insn_frag->fr_fix += 4;
+ insn_frag->fr_var -= 4;
+ seginfo->tc_segment_info_data.last_insn_lsb
+ = (target_big_endian ? output + 3 : output);
+ if (opct->flags & TIC6X_FLAG_SPMASK)
+ seginfo->tc_segment_info_data.spmask_addr = output;
+}
+
+/* Modify NEWVAL (32-bit) by inserting VALUE, shifted right by SHIFT
+ and the least significant BITS bits taken, at position POS. */
+#define MODIFY_VALUE(NEWVAL, VALUE, SHIFT, POS, BITS) \
+ do { \
+ (NEWVAL) &= 0xffffffffU & ~(((1U << (BITS)) - 1) << (POS)); \
+ (NEWVAL) |= (((VALUE) >> (SHIFT)) & ((1U << (BITS)) - 1)) << (POS); \
+ } while (0)
+
+/* Apply a fixup to the object file. */
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ offsetT value = *valP;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ value = SEXT (value);
+ *valP = value;
+
+ fixP->fx_offset = SEXT (fixP->fx_offset);
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+
+ /* We do our own overflow checks. */
+ fixP->fx_no_overflow = 1;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_NONE:
+ case BFD_RELOC_C6000_EHTYPE:
+ /* Force output to the object file. */
+ fixP->fx_done = 0;
+ break;
+
+ case BFD_RELOC_32:
+ if (fixP->fx_done || !seg->use_rela_p)
+ md_number_to_chars (buf, value, 4);
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (value < -0x8000 || value > 0xffff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("value too large for 2-byte field"));
+ md_number_to_chars (buf, value, 2);
+ }
+ break;
+
+ case BFD_RELOC_8:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ if (value < -0x80 || value > 0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("value too large for 1-byte field"));
+ *buf = value;
+ }
+ break;
+
+ case BFD_RELOC_C6000_ABS_S16:
+ case BFD_RELOC_C6000_ABS_L16:
+ case BFD_RELOC_C6000_SBR_S16:
+ case BFD_RELOC_C6000_SBR_L16_B:
+ case BFD_RELOC_C6000_SBR_L16_H:
+ case BFD_RELOC_C6000_SBR_L16_W:
+ case BFD_RELOC_C6000_SBR_GOT_L16_W:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+ int shift;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_C6000_SBR_L16_H:
+ shift = 1;
+ break;
+
+ case BFD_RELOC_C6000_SBR_L16_W:
+ case BFD_RELOC_C6000_SBR_GOT_L16_W:
+ shift = 2;
+ break;
+
+ default:
+ shift = 0;
+ break;
+ }
+
+ MODIFY_VALUE (newval, value, shift, 7, 16);
+ if ((value < -0x8000 || value > 0x7fff)
+ && (fixP->fx_r_type == BFD_RELOC_C6000_ABS_S16
+ || fixP->fx_r_type == BFD_RELOC_C6000_SBR_S16))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ if (fixP->fx_done
+ && fixP->fx_r_type != BFD_RELOC_C6000_ABS_S16
+ && fixP->fx_r_type != BFD_RELOC_C6000_ABS_L16)
+ abort ();
+ break;
+
+ case BFD_RELOC_C6000_ABS_H16:
+ case BFD_RELOC_C6000_SBR_H16_B:
+ case BFD_RELOC_C6000_SBR_H16_H:
+ case BFD_RELOC_C6000_SBR_H16_W:
+ case BFD_RELOC_C6000_SBR_GOT_H16_W:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+ int shift;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_C6000_SBR_H16_H:
+ shift = 17;
+ break;
+
+ case BFD_RELOC_C6000_SBR_H16_W:
+ case BFD_RELOC_C6000_SBR_GOT_H16_W:
+ shift = 18;
+ break;
+
+ default:
+ shift = 16;
+ break;
+ }
+
+ MODIFY_VALUE (newval, value, shift, 7, 16);
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ if (fixP->fx_done && fixP->fx_r_type != BFD_RELOC_C6000_ABS_H16)
+ abort ();
+ break;
+
+ case BFD_RELOC_C6000_PCR_H16:
+ case BFD_RELOC_C6000_PCR_L16:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+ int shift = fixP->fx_r_type == BFD_RELOC_C6000_PCR_H16 ? 16 : 0;
+
+ MODIFY_VALUE (newval, value, shift, 7, 16);
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_SBR_U15_B:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ MODIFY_VALUE (newval, value, 0, 8, 15);
+ if (value < 0 || value > 0x7fff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_SBR_U15_H:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ /* Constant ADDA operands, processed as constant when the
+ instruction is parsed, are encoded as-is rather than
+ shifted. If the operand of an ADDA instruction is now
+ constant (for example, the difference between two labels
+ found after the instruction), ensure it is encoded the
+ same way it would have been if the constant value had
+ been known when the instruction was parsed. */
+ if (fixP->tc_fix_data.fix_adda && fixP->fx_done)
+ value <<= 1;
+
+ MODIFY_VALUE (newval, value, 1, 8, 15);
+ if (value & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset not 2-byte-aligned"));
+ if (value < 0 || value > 0xfffe)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_SBR_U15_W:
+ case BFD_RELOC_C6000_SBR_GOT_U15_W:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ /* Constant ADDA operands, processed as constant when the
+ instruction is parsed, are encoded as-is rather than
+ shifted. If the operand of an ADDA instruction is now
+ constant (for example, the difference between two labels
+ found after the instruction), ensure it is encoded the
+ same way it would have been if the constant value had
+ been known when the instruction was parsed. */
+ if (fixP->tc_fix_data.fix_adda && fixP->fx_done)
+ value <<= 2;
+
+ MODIFY_VALUE (newval, value, 2, 8, 15);
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset not 4-byte-aligned"));
+ if (value < 0 || value > 0x1fffc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ if (fixP->fx_done && fixP->fx_r_type != BFD_RELOC_C6000_SBR_U15_W)
+ abort ();
+ break;
+
+ case BFD_RELOC_C6000_DSBT_INDEX:
+ if (value != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("addend used with $DSBT_INDEX"));
+ if (fixP->fx_done)
+ abort ();
+ break;
+
+ case BFD_RELOC_C6000_PCR_S21:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ MODIFY_VALUE (newval, value, 2, 7, 21);
+
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset not 4-byte-aligned"));
+ if (value < -0x400000 || value > 0x3ffffc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_PCR_S12:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ MODIFY_VALUE (newval, value, 2, 16, 12);
+
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset not 4-byte-aligned"));
+ if (value < -0x2000 || value > 0x1ffc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_PCR_S10:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ MODIFY_VALUE (newval, value, 2, 13, 10);
+
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset not 4-byte-aligned"));
+ if (value < -0x800 || value > 0x7fc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_PCR_S7:
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ offsetT newval = md_chars_to_number (buf, 4);
+
+ MODIFY_VALUE (newval, value, 2, 16, 7);
+
+ if (value & 3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset not 4-byte-aligned"));
+ if (value < -0x100 || value > 0xfc)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("PC-relative offset out of range"));
+
+ md_number_to_chars (buf, newval, 4);
+ }
+ break;
+
+ case BFD_RELOC_C6000_PREL31:
+ /* Force output to the object file. */
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+/* Convert a floating-point number to target (IEEE) format. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+/* Adjust the frags in SECTION (see tic6x_end). */
+
+static void
+tic6x_adjust_section (bfd *abfd ATTRIBUTE_UNUSED, segT section,
+ void *dummy ATTRIBUTE_UNUSED)
+{
+ segment_info_type *info;
+ frchainS *frchp;
+ fragS *fragp;
+ bfd_boolean have_code = FALSE;
+ bfd_boolean have_non_code = FALSE;
+
+ info = seg_info (section);
+ if (info == NULL)
+ return;
+
+ for (frchp = info->frchainP; frchp; frchp = frchp->frch_next)
+ for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next)
+ switch (fragp->fr_type)
+ {
+ case rs_machine_dependent:
+ if (fragp->tc_frag_data.is_insns)
+ have_code = TRUE;
+ break;
+
+ case rs_dummy:
+ case rs_fill:
+ if (fragp->fr_fix > 0)
+ have_non_code = TRUE;
+ break;
+
+ default:
+ have_non_code = TRUE;
+ break;
+ }
+
+ /* Process alignment requirements in a code-only section. */
+ if (have_code && !have_non_code)
+ {
+ /* If we need to insert an odd number of instructions to meet an
+ alignment requirement, there must have been an odd number of
+ instructions since the last 8-byte-aligned execute packet
+ boundary. So there must have been an execute packet with an
+ odd number (and so a number fewer than 8) of instructions
+ into which we can insert a NOP without breaking any previous
+ alignments.
+
+ If then we need to insert a number 2 mod 4 of instructions,
+ the number of instructions since the last 16-byte-aligned
+ execute packet boundary must be 2 mod 4. So between that
+ boundary and the following 8-byte-aligned boundary there must
+ either be at least one execute packet with 2-mod-4
+ instructions, or at least two with an odd number of
+ instructions; again, greedily inserting NOPs as soon as
+ possible suffices to meet the alignment requirement.
+
+ If then we need to insert 4 instructions, we look between the
+ last 32-byte-aligned boundary and the following
+ 16-byte-aligned boundary. The sizes of the execute packets
+ in this range total 4 instructions mod 8, so again there is
+ room for greedy insertion of NOPs to meet the alignment
+ requirement, and before any intermediate point with 8-byte
+ (2-instruction) alignment requirement the sizes of execute
+ packets (and so the room for NOPs) will total 2 instructions
+ mod 4 so greedy insertion will not break such alignments.
+
+ So we can always meet these alignment requirements by
+ inserting NOPs in parallel with existing execute packets, and
+ by induction the approach described above inserts the minimum
+ number of such NOPs. */
+
+ /* The number of NOPs we are currently looking to insert, if we
+ have gone back to insert NOPs. */
+ unsigned int want_insert = 0;
+
+ /* Out of that number, the number inserted so far in the current
+ stage of the above algorithm. */
+ unsigned int want_insert_done_so_far = 0;
+
+ /* The position mod 32 at the start of the current frag. */
+ unsigned int pos = 0;
+
+ /* The locations in the frag chain of the most recent frags at
+ the start of which there is the given alignment. */
+ frchainS *frchp_last32, *frchp_last16, *frchp_last8;
+ fragS *fragp_last32, *fragp_last16, *fragp_last8;
+ unsigned int pos_last32, pos_last16, pos_last8;
+
+ frchp_last32 = frchp_last16 = frchp_last8 = info->frchainP;
+ fragp_last32 = fragp_last16 = fragp_last8 = info->frchainP->frch_root;
+ pos_last32 = pos_last16 = pos_last8 = 0;
+
+ for (frchp = info->frchainP; frchp; frchp = frchp->frch_next)
+ for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next)
+ look_at_frag:
+ {
+ bfd_boolean go_back = FALSE;
+ frchainS *frchp_next;
+ fragS *fragp_next;
+
+ if (fragp->fr_type != rs_machine_dependent)
+ continue;
+
+ if (fragp->tc_frag_data.is_insns
+ && pos + fragp->fr_fix > 32
+ && !fragp->tc_frag_data.can_cross_fp_boundary)
+ {
+ /* As described above, we should always have met an
+ alignment requirement by the time we come back to
+ it. */
+ if (want_insert)
+ abort ();
+
+ if (pos & 3)
+ abort ();
+ want_insert = (32 - pos) >> 2;
+ if (want_insert > 7)
+ abort ();
+ want_insert_done_so_far = 0;
+ go_back = TRUE;
+ }
+
+ if (!fragp->tc_frag_data.is_insns)
+ {
+ unsigned int would_insert_bytes;
+
+ if (!(pos & ((1 << fragp->fr_offset) - 1)))
+ /* This alignment requirement is already met. */
+ continue;
+
+ /* As described above, we should always have met an
+ alignment requirement by the time we come back to
+ it. */
+ if (want_insert)
+ abort ();
+
+ /* We may not be able to meet this requirement within
+ the given number of characters. */
+ would_insert_bytes
+ = ((1 << fragp->fr_offset)
+ - (pos & ((1 << fragp->fr_offset) - 1)));
+
+ if (fragp->fr_subtype != 0
+ && would_insert_bytes > fragp->fr_subtype)
+ continue;
+
+ /* An unmet alignment must be 8, 16 or 32 bytes;
+ smaller ones must always be met within code-only
+ sections and larger ones cause the section not to
+ be code-only. */
+ if (fragp->fr_offset != 3
+ && fragp->fr_offset != 4
+ && fragp->fr_offset != 5)
+ abort ();
+
+ if (would_insert_bytes & 3)
+ abort ();
+ want_insert = would_insert_bytes >> 2;
+ if (want_insert > 7)
+ abort ();
+ want_insert_done_so_far = 0;
+ go_back = TRUE;
+ }
+ else if (want_insert && !go_back)
+ {
+ unsigned int num_insns = fragp->fr_fix >> 2;
+ unsigned int max_poss_nops = 8 - num_insns;
+
+ if (max_poss_nops)
+ {
+ unsigned int cur_want_nops, max_want_nops, do_nops, i;
+
+ if (want_insert & 1)
+ cur_want_nops = 1;
+ else if (want_insert & 2)
+ cur_want_nops = 2;
+ else if (want_insert & 4)
+ cur_want_nops = 4;
+ else
+ abort ();
+
+ max_want_nops = cur_want_nops - want_insert_done_so_far;
+
+ do_nops = (max_poss_nops < max_want_nops
+ ? max_poss_nops
+ : max_want_nops);
+ for (i = 0; i < do_nops; i++)
+ {
+ md_number_to_chars (fragp->fr_literal + fragp->fr_fix,
+ 0, 4);
+ if (target_big_endian)
+ fragp->fr_literal[fragp->fr_fix - 1] |= 0x1;
+ else
+ fragp->fr_literal[fragp->fr_fix - 4] |= 0x1;
+ fragp->fr_fix += 4;
+ fragp->fr_var -= 4;
+ }
+ want_insert_done_so_far += do_nops;
+ if (want_insert_done_so_far == cur_want_nops)
+ {
+ want_insert -= want_insert_done_so_far;
+ want_insert_done_so_far = 0;
+ if (want_insert)
+ go_back = TRUE;
+ }
+ }
+ }
+ if (go_back)
+ {
+ if (want_insert & 1)
+ {
+ frchp = frchp_last8;
+ fragp = fragp_last8;
+ pos = pos_last8;
+ }
+ else if (want_insert & 2)
+ {
+ frchp = frchp_last8 = frchp_last16;
+ fragp = fragp_last8 = fragp_last16;
+ pos = pos_last8 = pos_last16;
+ }
+ else if (want_insert & 4)
+ {
+ frchp = frchp_last8 = frchp_last16 = frchp_last32;
+ fragp = fragp_last8 = fragp_last16 = fragp_last32;
+ pos = pos_last8 = pos_last16 = pos_last32;
+ }
+ else
+ abort ();
+
+ goto look_at_frag;
+ }
+
+ /* Update current position for moving past a code
+ frag. */
+ pos += fragp->fr_fix;
+ pos &= 31;
+ frchp_next = frchp;
+ fragp_next = fragp->fr_next;
+ if (fragp_next == NULL)
+ {
+ frchp_next = frchp->frch_next;
+ if (frchp_next != NULL)
+ fragp_next = frchp_next->frch_root;
+ }
+ if (!(pos & 7))
+ {
+ frchp_last8 = frchp_next;
+ fragp_last8 = fragp_next;
+ pos_last8 = pos;
+ }
+ if (!(pos & 15))
+ {
+ frchp_last16 = frchp_next;
+ fragp_last16 = fragp_next;
+ pos_last16 = pos;
+ }
+ if (!(pos & 31))
+ {
+ frchp_last32 = frchp_next;
+ fragp_last32 = fragp_next;
+ pos_last32 = pos;
+ }
+ }
+ }
+
+ /* Now convert the machine-dependent frags to machine-independent
+ ones. */
+ for (frchp = info->frchainP; frchp; frchp = frchp->frch_next)
+ for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next)
+ {
+ if (fragp->fr_type == rs_machine_dependent)
+ {
+ if (fragp->tc_frag_data.is_insns)
+ frag_wane (fragp);
+ else
+ {
+ fragp->fr_type = rs_align_code;
+ fragp->fr_var = 1;
+ *fragp->fr_literal = 0;
+ }
+ }
+ }
+}
+
+/* Initialize the machine-dependent parts of a frag. */
+
+void
+tic6x_frag_init (fragS *fragp)
+{
+ fragp->tc_frag_data.is_insns = FALSE;
+ fragp->tc_frag_data.can_cross_fp_boundary = FALSE;
+}
+
+/* Set an attribute if it has not already been set by the user. */
+
+static void
+tic6x_set_attribute_int (int tag, int value)
+{
+ if (tag < 1
+ || tag >= NUM_KNOWN_OBJ_ATTRIBUTES)
+ abort ();
+ if (!tic6x_attributes_set_explicitly[tag])
+ bfd_elf_add_proc_attr_int (stdoutput, tag, value);
+}
+
+/* Set object attributes deduced from the input file and command line
+ rather than given explicitly. */
+static void
+tic6x_set_attributes (void)
+{
+ if (tic6x_arch_attribute == C6XABI_Tag_ISA_none)
+ tic6x_arch_attribute = C6XABI_Tag_ISA_C674X;
+
+ tic6x_set_attribute_int (Tag_ISA, tic6x_arch_attribute);
+ tic6x_set_attribute_int (Tag_ABI_DSBT, tic6x_dsbt);
+ tic6x_set_attribute_int (Tag_ABI_PID, tic6x_pid);
+ tic6x_set_attribute_int (Tag_ABI_PIC, tic6x_pic);
+}
+
+/* Do machine-dependent manipulations of the frag chains after all
+ input has been read and before the machine-independent sizing and
+ relaxing. */
+
+void
+tic6x_end (void)
+{
+ /* Set object attributes at this point if not explicitly set. */
+ tic6x_set_attributes ();
+
+ /* Meeting alignment requirements may require inserting NOPs in
+ parallel in execute packets earlier in the segment. Future
+ 16-bit instruction generation involves whole-segment optimization
+ to determine the best choice and ordering of 32-bit or 16-bit
+ instructions. This doesn't fit will in the general relaxation
+ framework, so handle alignment and 16-bit instruction generation
+ here. */
+ bfd_map_over_sections (stdoutput, tic6x_adjust_section, NULL);
+}
+
+/* No machine-dependent frags at this stage; all converted in
+ tic6x_end. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+/* No machine-dependent frags at this stage; all converted in
+ tic6x_end. */
+
+int
+md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ abort ();
+}
+
+/* Put a number into target byte order. */
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Machine-dependent operand parsing not currently needed. */
+
+void
+md_operand (expressionS *op ATTRIBUTE_UNUSED)
+{
+}
+
+/* PC-relative operands are relative to the start of the fetch
+ packet. */
+
+long
+tic6x_pcrel_from_section (fixS *fixp, segT sec)
+{
+ if (fixp->fx_addsy != NULL
+ && (!S_IS_DEFINED (fixp->fx_addsy)
+ || S_GET_SEGMENT (fixp->fx_addsy) != sec))
+ return 0;
+ return (fixp->fx_where + fixp->fx_frag->fr_address) & ~(long) 0x1f;
+}
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
+{
+ /* Round up section sizes to ensure that text sections consist of
+ whole fetch packets. */
+ int align = bfd_get_section_alignment (stdoutput, segment);
+ return ((size + (1 << align) - 1) & ((valueT) -1 << align));
+}
+
+/* No special undefined symbol handling needed for now. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ asymbol *symbol;
+ bfd_reloc_code_real_type r_type;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ symbol = symbol_get_bfdsym (fixp->fx_addsy);
+ *reloc->sym_ptr_ptr = symbol;
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = (tic6x_generate_rela ? fixp->fx_offset : 0);
+ r_type = fixp->fx_r_type;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (r_type));
+ return NULL;
+ }
+
+ /* Correct for adjustments bfd_install_relocation will make. */
+ if (reloc->howto->pcrel_offset && reloc->howto->partial_inplace)
+ {
+ reloc->addend += reloc->address;
+ if (!bfd_is_com_section (symbol))
+ reloc->addend -= symbol->value;
+ }
+ if (r_type == BFD_RELOC_C6000_PCR_H16
+ || r_type == BFD_RELOC_C6000_PCR_L16)
+ {
+ symbolS *t = fixp->tc_fix_data.fix_subsy;
+ segT sub_symbol_segment;
+
+ resolve_symbol_value (t);
+ sub_symbol_segment = S_GET_SEGMENT (t);
+ if (sub_symbol_segment == undefined_section)
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("undefined symbol %s in PCR relocation"),
+ S_GET_NAME (t));
+ else
+ {
+ reloc->addend = reloc->address & ~0x1F;
+ reloc->addend -= S_GET_VALUE (t);
+ }
+ }
+ return reloc;
+}
+
+/* Convert REGNAME to a DWARF-2 register number. */
+
+int
+tic6x_regname_to_dw2regnum (char *regname)
+{
+ bfd_boolean reg_ok;
+ tic6x_register reg;
+ char *rq = regname;
+
+ reg_ok = tic6x_parse_register (&rq, &reg);
+
+ if (!reg_ok)
+ return -1;
+
+ switch (reg.side)
+ {
+ case 1: /* A regs. */
+ if (reg.num < 16)
+ return reg.num;
+ else if (reg.num < 32)
+ return (reg.num - 16) + 37;
+ else
+ return -1;
+
+ case 2: /* B regs. */
+ if (reg.num < 16)
+ return reg.num + 16;
+ else if (reg.num < 32)
+ return (reg.num - 16) + 53;
+ else
+ return -1;
+
+ default:
+ return -1;
+ }
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure. */
+
+void
+tic6x_frame_initial_instructions (void)
+{
+ /* CFA is initial stack pointer (B15). */
+ cfi_add_CFA_def_cfa (31, 0);
+}
+
+/* Start an exception table entry. If idx is nonzero this is an index table
+ entry. */
+
+static void
+tic6x_start_unwind_section (const segT text_seg, int idx)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+ const char * text_name;
+ const char * prefix;
+ const char * prefix_once;
+ const char * group_name;
+ size_t prefix_len;
+ size_t text_len;
+ char * sec_name;
+ size_t sec_name_len;
+ int type;
+ int flags;
+ int linkonce;
+
+ if (idx)
+ {
+ prefix = ELF_STRING_C6000_unwind;
+ prefix_once = ELF_STRING_C6000_unwind_once;
+ type = SHT_C6000_UNWIND;
+ }
+ else
+ {
+ prefix = ELF_STRING_C6000_unwind_info;
+ prefix_once = ELF_STRING_C6000_unwind_info_once;
+ type = SHT_PROGBITS;
+ }
+
+ text_name = segment_name (text_seg);
+ if (streq (text_name, ".text"))
+ text_name = "";
+
+ if (strncmp (text_name, ".gnu.linkonce.t.",
+ strlen (".gnu.linkonce.t.")) == 0)
+ {
+ prefix = prefix_once;
+ text_name += strlen (".gnu.linkonce.t.");
+ }
+
+ prefix_len = strlen (prefix);
+ text_len = strlen (text_name);
+ sec_name_len = prefix_len + text_len;
+ sec_name = (char *) xmalloc (sec_name_len + 1);
+ memcpy (sec_name, prefix, prefix_len);
+ memcpy (sec_name + prefix_len, text_name, text_len);
+ sec_name[prefix_len + text_len] = '\0';
+
+ flags = SHF_ALLOC;
+ linkonce = 0;
+ group_name = 0;
+
+ /* Handle COMDAT group. */
+ if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
+ {
+ group_name = elf_group_name (text_seg);
+ if (group_name == NULL)
+ {
+ as_bad (_("group section `%s' has no group signature"),
+ segment_name (text_seg));
+ ignore_rest_of_line ();
+ return;
+ }
+ flags |= SHF_GROUP;
+ linkonce = 1;
+ }
+
+ obj_elf_change_section (sec_name, type, flags, 0, group_name, linkonce, 0);
+
+ /* Set the section link for index tables. */
+ if (idx)
+ elf_linked_to_section (now_seg) = text_seg;
+
+ seg_info (now_seg)->tc_segment_info_data.text_unwind = unwind;
+}
+
+
+static const int
+tic6x_unwind_frame_regs[TIC6X_NUM_UNWIND_REGS] =
+/* A15 B15 B14 B13 B12 B11 B10 B3 A14 A13 A12 A11 A10. */
+ { 15, 31, 30, 29, 28, 27, 26, 19, 14, 13, 12, 11, 10 };
+
+/* Register save offsets for __c6xabi_push_rts. */
+static const int
+tic6x_pop_rts_offset_little[TIC6X_NUM_UNWIND_REGS] =
+/* A15 B15 B14 B13 B12 B11 B10 B3 A14 A13 A12 A11 A10. */
+ { -1, 1, 0, -3, -4, -7, -8,-11, -2, -5, -6, -9,-10};
+
+static const int
+tic6x_pop_rts_offset_big[TIC6X_NUM_UNWIND_REGS] =
+/* A15 B15 B14 B13 B12 B11 B10 B3 A14 A13 A12 A11 A10. */
+ { -2, 1, 0, -4, -3, -8, -7,-12, -1, -6, -5,-10, -9};
+
+/* Map from dwarf register number to unwind frame register number. */
+static int
+tic6x_unwind_reg_from_dwarf (int dwarf)
+{
+ int reg;
+
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ if (tic6x_unwind_frame_regs[reg] == dwarf)
+ return reg;
+ }
+
+ return -1;
+}
+
+/* Unwinding bytecode definitions. */
+#define UNWIND_OP_ADD_SP 0x00
+#define UNWIND_OP_ADD_SP2 0xd2
+#define UNWIND_OP2_POP 0x8000
+#define UNWIND_OP2_POP_COMPACT 0xa000
+#define UNWIND_OP_POP_REG 0xc0
+#define UNWIND_OP_MV_FP 0xd0
+#define UNWIND_OP_POP_RTS 0xd1
+#define UNWIND_OP_RET 0xe0
+
+/* Maximum stack adjustment for __c6xabi_unwind_cpp_pr3/4 */
+#define MAX_COMPACT_SP_OFFSET (0x7f << 3)
+
+static void
+tic6x_flush_unwind_word (valueT data)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+ char *ptr;
+
+ /* Create EXTAB entry if it does not exist. */
+ if (unwind->table_entry == NULL)
+ {
+ tic6x_start_unwind_section (unwind->saved_seg, 0);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+ unwind->table_entry = expr_build_dot ();
+ ptr = frag_more (4);
+ unwind->frag_start = ptr;
+ }
+ else
+ {
+ /* Append additional word of data. */
+ ptr = frag_more (4);
+ }
+
+ md_number_to_chars (ptr, data, 4);
+}
+
+/* Add a single byte of unwinding data. */
+
+static void
+tic6x_unwind_byte (int byte)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ unwind->data_bytes++;
+ /* Only flush the first word after we know multiple words are required. */
+ if (unwind->data_bytes == 5)
+ {
+ if (unwind->personality_index == -1)
+ {
+ /* At this point we know we are too big for pr0. */
+ unwind->personality_index = 1;
+ tic6x_flush_unwind_word (0x81000000 | ((unwind->data >> 8) & 0xffff));
+ unwind->data = ((unwind->data & 0xff) << 8) | byte;
+ unwind->data_bytes++;
+ }
+ else
+ {
+ tic6x_flush_unwind_word (unwind->data);
+ unwind->data = byte;
+ }
+ }
+ else
+ {
+ unwind->data = (unwind->data << 8) | byte;
+ if ((unwind->data_bytes & 3) == 0 && unwind->data_bytes > 4)
+ {
+ tic6x_flush_unwind_word (unwind->data);
+ unwind->data = 0;
+ }
+ }
+}
+
+/* Add a two-byte unwinding opcode. */
+static void
+tic6x_unwind_2byte (int bytes)
+{
+ tic6x_unwind_byte (bytes >> 8);
+ tic6x_unwind_byte (bytes & 0xff);
+}
+
+static void
+tic6x_unwind_uleb (offsetT offset)
+{
+ while (offset > 0x7f)
+ {
+ tic6x_unwind_byte ((offset & 0x7f) | 0x80);
+ offset >>= 7;
+ }
+ tic6x_unwind_byte (offset);
+}
+
+void
+tic6x_cfi_startproc (void)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ unwind->personality_index = -1;
+ unwind->personality_routine = NULL;
+ if (unwind->table_entry)
+ as_bad (_("missing .endp before .cfi_startproc"));
+
+ unwind->table_entry = NULL;
+ unwind->data_bytes = -1;
+}
+
+static void
+tic6x_output_exidx_entry (void)
+{
+ char *ptr;
+ long where;
+ unsigned int marked_pr_dependency;
+ segT old_seg;
+ subsegT old_subseg;
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+
+ old_seg = now_seg;
+ old_subseg = now_subseg;
+
+ /* Add index table entry. This is two words. */
+ tic6x_start_unwind_section (unwind->saved_seg, 1);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+ ptr = frag_more (8);
+ memset (ptr, 0, 8);
+ where = frag_now_fix () - 8;
+
+ /* Self relative offset of the function start. */
+ fix_new (frag_now, where, 4, unwind->function_start, 0, 1,
+ BFD_RELOC_C6000_PREL31);
+
+ /* Indicate dependency on ABI-defined personality routines to the
+ linker, if it hasn't been done already. */
+ marked_pr_dependency
+ = seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency;
+ if (unwind->personality_index >= 0 && unwind->personality_index < 5
+ && !(marked_pr_dependency & (1 << unwind->personality_index)))
+ {
+ static const char *const name[] =
+ {
+ "__c6xabi_unwind_cpp_pr0",
+ "__c6xabi_unwind_cpp_pr1",
+ "__c6xabi_unwind_cpp_pr2",
+ "__c6xabi_unwind_cpp_pr3",
+ "__c6xabi_unwind_cpp_pr4"
+ };
+ symbolS *pr = symbol_find_or_make (name[unwind->personality_index]);
+ fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+ seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+ |= 1 << unwind->personality_index;
+ }
+
+ if (unwind->table_entry)
+ {
+ /* Self relative offset of the table entry. */
+ fix_new (frag_now, where + 4, 4, unwind->table_entry, 0, 1,
+ BFD_RELOC_C6000_PREL31);
+ }
+ else
+ {
+ /* Inline exception table entry. */
+ md_number_to_chars (ptr + 4, unwind->data, 4);
+ }
+
+ /* Restore the original section. */
+ subseg_set (old_seg, old_subseg);
+}
+
+static void
+tic6x_output_unwinding (bfd_boolean need_extab)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+ unsigned safe_mask = unwind->safe_mask;
+ unsigned compact_mask = unwind->compact_mask;
+ unsigned reg_saved_mask = unwind->reg_saved_mask;
+ offsetT cfa_offset = unwind->cfa_offset;
+ long where;
+ int reg;
+
+ if (unwind->personality_index == -2)
+ {
+ /* Function can not be unwound. */
+ unwind->data = 1;
+ tic6x_output_exidx_entry ();
+ return;
+ }
+
+ if (unwind->personality_index == -1 && unwind->personality_routine == NULL)
+ {
+ /* Auto-select a personality routine if none specified. */
+ if (reg_saved_mask || cfa_offset >= MAX_COMPACT_SP_OFFSET)
+ unwind->personality_index = -1;
+ else if (safe_mask)
+ unwind->personality_index = 3;
+ else
+ unwind->personality_index = 4;
+ }
+
+ /* Calculate unwinding opcodes, and emit to EXTAB if necessary. */
+ unwind->table_entry = NULL;
+ if (unwind->personality_index == 3 || unwind->personality_index == 4)
+ {
+ if (cfa_offset >= MAX_COMPACT_SP_OFFSET)
+ {
+ as_bad (_("stack pointer offset too large for personality routine"));
+ return;
+ }
+ if (reg_saved_mask
+ || (unwind->personality_index == 3 && compact_mask != 0)
+ || (unwind->personality_index == 4 && safe_mask != 0))
+ {
+ as_bad (_("stack frame layout does not match personality routine"));
+ return;
+ }
+
+ unwind->data = (1u << 31) | (unwind->personality_index << 24);
+ if (unwind->cfa_reg == 15)
+ unwind->data |= 0x7f << 17;
+ else
+ unwind->data |= cfa_offset << (17 - 3);
+
+ if (unwind->personality_index == 3)
+ unwind->data |= safe_mask << 4;
+ else
+ unwind->data |= compact_mask << 4;
+ unwind->data |= unwind->return_reg;
+ unwind->data_bytes = 4;
+ }
+ else
+ {
+ if (unwind->personality_routine)
+ {
+ unwind->data = 0;
+ unwind->data_bytes = 5;
+ tic6x_flush_unwind_word (0);
+ /* First word is personality routine. */
+ where = frag_now_fix () - 4;
+ fix_new (frag_now, where, 4, unwind->personality_routine, 0, 1,
+ BFD_RELOC_C6000_PREL31);
+ }
+ else if (unwind->personality_index > 0)
+ {
+ unwind->data = 0x8000 | (unwind->personality_index << 8);
+ unwind->data_bytes = 2;
+ }
+ else /* pr0 or undecided */
+ {
+ unwind->data = 0x80;
+ unwind->data_bytes = 1;
+ }
+
+ if (unwind->return_reg != UNWIND_B3)
+ {
+ tic6x_unwind_byte (UNWIND_OP_RET | unwind->return_reg);
+ }
+
+ if (unwind->cfa_reg == 15)
+ {
+ tic6x_unwind_byte (UNWIND_OP_MV_FP);
+ }
+ else if (cfa_offset != 0)
+ {
+ cfa_offset >>= 3;
+ if (cfa_offset > 0x80)
+ {
+ tic6x_unwind_byte (UNWIND_OP_ADD_SP2);
+ tic6x_unwind_uleb (cfa_offset - 0x81);
+ }
+ else if (cfa_offset > 0x40)
+ {
+ tic6x_unwind_byte (UNWIND_OP_ADD_SP | 0x3f);
+ tic6x_unwind_byte (UNWIND_OP_ADD_SP | (cfa_offset - 0x40));
+ }
+ else
+ {
+ tic6x_unwind_byte (UNWIND_OP_ADD_SP | (cfa_offset - 1));
+ }
+ }
+
+ if (safe_mask)
+ tic6x_unwind_2byte (UNWIND_OP2_POP | unwind->safe_mask);
+ else if (unwind->pop_rts)
+ tic6x_unwind_byte (UNWIND_OP_POP_RTS);
+ else if (compact_mask)
+ tic6x_unwind_2byte (UNWIND_OP2_POP_COMPACT | unwind->compact_mask);
+ else if (reg_saved_mask)
+ {
+ offsetT cur_offset;
+ int val;
+ int last_val;
+
+ tic6x_unwind_byte (UNWIND_OP_POP_REG | unwind->saved_reg_count);
+ last_val = 0;
+ for (cur_offset = 0; unwind->saved_reg_count > 0; cur_offset -= 4)
+ {
+ val = 0xf;
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ if (!unwind->reg_saved[reg])
+ continue;
+
+ if (unwind->reg_offset[reg] == cur_offset)
+ {
+ unwind->saved_reg_count--;
+ val = reg;
+ break;
+ }
+ }
+ if ((cur_offset & 4) == 4)
+ tic6x_unwind_byte ((last_val << 4) | val);
+ else
+ last_val = val;
+ }
+ if ((cur_offset & 4) == 4)
+ tic6x_unwind_byte ((last_val << 4) | 0xf);
+ }
+
+ /* Pad with RETURN opcodes. */
+ while ((unwind->data_bytes & 3) != 0)
+ tic6x_unwind_byte (UNWIND_OP_RET | UNWIND_B3);
+
+ if (unwind->personality_index == -1 && unwind->personality_routine == NULL)
+ unwind->personality_index = 0;
+ }
+
+ /* Force creation of an EXTAB entry if an LSDA is required. */
+ if (need_extab && !unwind->table_entry)
+ {
+ if (unwind->data_bytes != 4)
+ abort ();
+
+ tic6x_flush_unwind_word (unwind->data);
+ }
+ else if (unwind->table_entry && !need_extab)
+ {
+ /* Add an empty descriptor if there is no user-specified data. */
+ char *ptr = frag_more (4);
+ md_number_to_chars (ptr, 0, 4);
+ }
+
+ /* Fill in length of unwinding bytecode. */
+ if (unwind->table_entry)
+ {
+ valueT tmp;
+ if (unwind->data_bytes > 0x400)
+ as_bad (_("too many unwinding instructions"));
+
+ if (unwind->personality_index == -1)
+ {
+ tmp = md_chars_to_number (unwind->frag_start + 4, 4);
+ tmp |= ((unwind->data_bytes - 8) >> 2) << 24;
+ md_number_to_chars (unwind->frag_start + 4, tmp, 4);
+ }
+ else if (unwind->personality_index == 1 || unwind->personality_index == 2)
+ {
+ tmp = md_chars_to_number (unwind->frag_start, 4);
+ tmp |= ((unwind->data_bytes - 4) >> 2) << 16;
+ md_number_to_chars (unwind->frag_start, tmp, 4);
+ }
+ }
+ tic6x_output_exidx_entry ();
+}
+
+/* FIXME: This will get horribly confused if cfi directives are emitted for
+ function epilogue. */
+void
+tic6x_cfi_endproc (struct fde_entry *fde)
+{
+ tic6x_unwind_info *unwind = tic6x_get_unwind ();
+ struct cfi_insn_data *insn;
+ int reg;
+ unsigned safe_mask = 0;
+ unsigned compact_mask = 0;
+ unsigned reg_saved_mask = 0;
+ offsetT cfa_offset = 0;
+ offsetT save_offset = 0;
+
+ unwind->cfa_reg = 31;
+ unwind->return_reg = UNWIND_B3;
+ unwind->saved_reg_count = 0;
+ unwind->pop_rts = FALSE;
+
+ unwind->saved_seg = now_seg;
+ unwind->saved_subseg = now_subseg;
+
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ unwind->reg_saved[reg] = FALSE;
+
+ /* Scan FDE instructions to build up stack frame layout. */
+ for (insn = fde->data; insn; insn = insn->next)
+ {
+ switch (insn->insn)
+ {
+ case DW_CFA_advance_loc:
+ break;
+
+ case DW_CFA_def_cfa:
+ unwind->cfa_reg = insn->u.ri.reg;
+ cfa_offset = insn->u.ri.offset;
+ break;
+
+ case DW_CFA_def_cfa_register:
+ unwind->cfa_reg = insn->u.r;
+ break;
+
+ case DW_CFA_def_cfa_offset:
+ cfa_offset = insn->u.i;
+ break;
+
+ case DW_CFA_undefined:
+ case DW_CFA_same_value:
+ reg = tic6x_unwind_reg_from_dwarf (insn->u.r);
+ if (reg >= 0)
+ unwind->reg_saved[reg] = FALSE;
+ break;
+
+ case DW_CFA_offset:
+ reg = tic6x_unwind_reg_from_dwarf (insn->u.ri.reg);
+ if (reg < 0)
+ {
+ as_bad (_("unable to generate unwinding opcode for reg %d"),
+ insn->u.ri.reg);
+ return;
+ }
+ unwind->reg_saved[reg] = TRUE;
+ unwind->reg_offset[reg] = insn->u.ri.offset;
+ if (insn->u.ri.reg == UNWIND_B3)
+ unwind->return_reg = UNWIND_B3;
+ break;
+
+ case DW_CFA_register:
+ if (insn->u.rr.reg1 != 19)
+ {
+ as_bad (_("unable to generate unwinding opcode for reg %d"),
+ insn->u.rr.reg1);
+ return;
+ }
+
+ reg = tic6x_unwind_reg_from_dwarf (insn->u.rr.reg2);
+ if (reg < 0)
+ {
+ as_bad (_("unable to generate unwinding opcode for reg %d"),
+ insn->u.rr.reg2);
+ return;
+ }
+
+ unwind->return_reg = reg;
+ unwind->reg_saved[UNWIND_B3] = FALSE;
+ if (unwind->reg_saved[reg])
+ {
+ as_bad (_("unable to restore return address from "
+ "previously restored reg"));
+ return;
+ }
+ break;
+
+ case DW_CFA_restore:
+ case DW_CFA_remember_state:
+ case DW_CFA_restore_state:
+ case DW_CFA_GNU_window_save:
+ case CFI_escape:
+ case CFI_val_encoded_addr:
+ as_bad (_("unhandled CFA insn for unwinding (%d)"), insn->insn);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ if (unwind->cfa_reg != 15 && unwind->cfa_reg != 31)
+ {
+ as_bad (_("unable to generate unwinding opcode for frame pointer reg %d"),
+ unwind->cfa_reg);
+ return;
+ }
+
+ if (unwind->cfa_reg == 15)
+ {
+ if (cfa_offset != 0)
+ {
+ as_bad (_("unable to generate unwinding opcode for "
+ "frame pointer offset"));
+ return;
+ }
+ }
+ else
+ {
+ if ((cfa_offset & 7) != 0)
+ {
+ as_bad (_("unwound stack pointer not doubleword aligned"));
+ return;
+ }
+ }
+
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ if (unwind->reg_saved[reg])
+ reg_saved_mask |= 1 << (TIC6X_NUM_UNWIND_REGS - (reg + 1));
+ }
+
+ /* Check for standard "safe debug" frame layout */
+ if (reg_saved_mask)
+ {
+ save_offset = 0;
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ if (!unwind->reg_saved[reg])
+ continue;
+
+ if (target_big_endian
+ && reg < TIC6X_NUM_UNWIND_REGS - 1
+ && unwind->reg_saved[reg + 1]
+ && tic6x_unwind_frame_regs[reg]
+ == tic6x_unwind_frame_regs[reg + 1] + 1
+ && (tic6x_unwind_frame_regs[reg] & 1) == 1
+ && (save_offset & 4) == 4)
+ {
+ /* Swapped pair */
+ if (save_offset != unwind->reg_offset[reg + 1]
+ || save_offset - 4 != unwind->reg_offset[reg])
+ break;
+ save_offset -= 8;
+ reg++;
+ }
+ else
+ {
+ if (save_offset != unwind->reg_offset[reg])
+ break;
+ save_offset -= 4;
+ }
+ }
+ if (reg == TIC6X_NUM_UNWIND_REGS)
+ {
+ safe_mask = reg_saved_mask;
+ reg_saved_mask = 0;
+ }
+ }
+
+ /* Check for compact frame layout. */
+ if (reg_saved_mask)
+ {
+ save_offset = 0;
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ int reg2;
+
+ if (!unwind->reg_saved[reg])
+ continue;
+
+ if (reg < TIC6X_NUM_UNWIND_REGS - 1)
+ {
+ reg2 = reg + 1;
+
+ if (!unwind->reg_saved[reg2]
+ || tic6x_unwind_frame_regs[reg]
+ != tic6x_unwind_frame_regs[reg2] + 1
+ || (tic6x_unwind_frame_regs[reg2] & 1) != 0
+ || save_offset == 0)
+ reg2 = -1;
+ }
+ else
+ reg2 = -1;
+
+ if (reg2 >= 0)
+ {
+ int high_offset;
+ if (target_big_endian)
+ high_offset = 4; /* lower address = positive stack offset. */
+ else
+ high_offset = 0;
+
+ if (save_offset + 4 - high_offset != unwind->reg_offset[reg]
+ || save_offset + high_offset != unwind->reg_offset[reg2])
+ {
+ break;
+ }
+ reg++;
+ }
+ else
+ {
+ if (save_offset != unwind->reg_offset[reg])
+ break;
+ }
+ save_offset -= 8;
+ }
+
+ if (reg == TIC6X_NUM_UNWIND_REGS)
+ {
+ compact_mask = reg_saved_mask;
+ reg_saved_mask = 0;
+ }
+ }
+
+ /* Check for __c6xabi_pop_rts format */
+ if (reg_saved_mask == 0x17ff)
+ {
+ const int *pop_rts_offset = target_big_endian
+ ? tic6x_pop_rts_offset_big
+ : tic6x_pop_rts_offset_little;
+
+ save_offset = 0;
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ if (reg == UNWIND_B15)
+ continue;
+
+ if (unwind->reg_offset[reg] != pop_rts_offset[reg] * 4)
+ break;
+ }
+
+ if (reg == TIC6X_NUM_UNWIND_REGS)
+ {
+ unwind->pop_rts = TRUE;
+ reg_saved_mask = 0;
+ }
+ }
+ /* If all else fails then describe the frame manually. */
+ if (reg_saved_mask)
+ {
+ save_offset = 0;
+
+ for (reg = 0; reg < TIC6X_NUM_UNWIND_REGS; reg++)
+ {
+ if (!unwind->reg_saved[reg])
+ continue;
+
+ unwind->saved_reg_count++;
+ /* Encoding uses 4 bits per word, so size of unwinding opcode data
+ limits the save area size. The exact cap will be figured out
+ later due to overflow, the 0x800 here is just a quick sanity
+ check to weed out obviously excessive offsets. */
+ if (unwind->reg_offset[reg] > 0 || unwind->reg_offset[reg] < -0x800
+ || (unwind->reg_offset[reg] & 3) != 0)
+ {
+ as_bad (_("stack frame layout too complex for unwinder"));
+ return;
+ }
+
+ if (unwind->reg_offset[reg] < save_offset)
+ save_offset = unwind->reg_offset[reg] - 4;
+ }
+ }
+
+ /* Align to 8-byte boundary (stack grows towards negative offsets). */
+ save_offset &= ~7;
+
+ if (unwind->cfa_reg == 31 && !reg_saved_mask)
+ {
+ cfa_offset += save_offset;
+ if (cfa_offset < 0)
+ {
+ as_bad (_("unwound frame has negative size"));
+ return;
+ }
+ }
+
+ unwind->safe_mask = safe_mask;
+ unwind->compact_mask = compact_mask;
+ unwind->reg_saved_mask = reg_saved_mask;
+ unwind->cfa_offset = cfa_offset;
+ unwind->function_start = fde->start_address;
+}
diff --git a/gas/config/tc-tic6x.h b/gas/config/tc-tic6x.h
new file mode 100644
index 0000000..dc110e8
--- /dev/null
+++ b/gas/config/tc-tic6x.h
@@ -0,0 +1,227 @@
+/* Definitions for TI C6X assembler.
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_TIC6X 1
+#define TARGET_BYTES_BIG_ENDIAN 0
+#define WORKING_DOT_WORD
+#define DOUBLEBAR_PARALLEL
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#define TC_PREDICATE_START_CHAR '['
+#define TC_PREDICATE_END_CHAR ']'
+/* For TI C6X, we keep spaces in what the preprocessor considers
+ operands as they may separate functional unit specifiers from
+ operands. */
+#define TC_KEEP_OPERAND_SPACES 1
+
+#define TARGET_ARCH bfd_arch_tic6x
+#define TARGET_FORMAT (target_big_endian \
+ ? "elf32-tic6x-be" \
+ : "elf32-tic6x-le")
+
+typedef struct tic6x_label_list
+{
+ struct tic6x_label_list *next;
+ symbolS *label;
+} tic6x_label_list;
+
+/* Must be consistent with the enum in tc-tic6x.c. */
+#define TIC6X_NUM_UNWIND_REGS 13
+
+/* Unwinding information state. */
+typedef struct tic6x_unwind_info {
+ int personality_index;
+ symbolS *personality_routine;
+ symbolS *function_start;
+ segT saved_seg;
+ subsegT saved_subseg;
+ /* NULL if table entry is inline. */
+ symbolS *table_entry;
+ char *frag_start;
+ valueT data;
+ /* 0 before .cfi_startproc
+ -1 between .cfi_startproc and .handlerdata
+ >0 between .handlerdata and .endp */
+ int data_bytes;
+
+ offsetT reg_offset[TIC6X_NUM_UNWIND_REGS];
+ bfd_boolean reg_saved[TIC6X_NUM_UNWIND_REGS];
+ int cfa_reg;
+ int return_reg;
+ unsigned safe_mask;
+ unsigned compact_mask;
+ unsigned reg_saved_mask;
+ offsetT cfa_offset;
+ bfd_boolean pop_rts;
+ /* Only valid for UNWIND_OP_POP_REG */
+ int saved_reg_count;
+} tic6x_unwind_info;
+
+typedef struct
+{
+ /* Any labels seen since the last instruction or data. If not NULL,
+ a following instruction may not have parallel bars, but must
+ start a new execute packet. */
+ tic6x_label_list *label_list;
+
+ /* Whether compact instructions are forbidden here. */
+ bfd_boolean nocmp;
+
+ /* If there is a current execute packet, the frag being used for
+ that execute packet. */
+ fragS *execute_packet_frag;
+
+ /* If there is a current execute packet, a pointer to the
+ least-significant byte of the last instruction in it (for setting
+ the p-bit). */
+ char *last_insn_lsb;
+
+ /* If there has been an SPMASK instruction in the current execute
+ packet, a pointer to the first byte in it (for processing
+ ||^); otherwise NULL. */
+ char *spmask_addr;
+
+ /* The functional units used in the current execute packet, recorded
+ by setting the same bits as would be set in the 32-bit SPMASK
+ instruction. */
+ unsigned int func_units_used;
+
+ /* If an SPLOOP-family instruction has been seen, and a following
+ SPKERNEL-family instruction has not yet been seen, the ii value
+ from the SPLOOP instruction (in the range 1 to 14); otherwise
+ 0. */
+ int sploop_ii;
+
+ /* Bit N indicates that an R_C6000_NONE relocation has been output for
+ __c6xabi_unwind_cpp_prN already if set. This enables dependencies to be
+ emitted only once per section, to save unnecessary bloat. */
+ unsigned int marked_pr_dependency;
+
+ tic6x_unwind_info *unwind;
+ tic6x_unwind_info *text_unwind;
+} tic6x_segment_info_type;
+#define TC_SEGMENT_INFO_TYPE tic6x_segment_info_type
+
+typedef struct
+{
+ /* Whether this machine-dependent frag is used for instructions (as
+ opposed to code alignment). */
+ bfd_boolean is_insns;
+
+ /* For a frag used for instructions, whether it is may cross a fetch
+ packet boundary (subject to alignment requirements). */
+ bfd_boolean can_cross_fp_boundary;
+} tic6x_frag_info;
+#define TC_FRAG_TYPE tic6x_frag_info
+#define TC_FRAG_INIT(fragP) tic6x_frag_init (fragP)
+extern void tic6x_frag_init (fragS *fragp);
+
+typedef struct
+{
+ /* Whether this fix was for an ADDA instruction. If so, a constant
+ resulting from resolving the fix should be implicitly shifted
+ left (it represents a value to be encoded literally in the
+ instruction, whereas a non-constant represents a DP-relative
+ value counting in the appropriate units). */
+ bfd_boolean fix_adda;
+ /* The symbol to be subtracted in case of a PCR_H16 or PCR_L16
+ reloc. */
+ symbolS *fix_subsy;
+} tic6x_fix_info;
+#define TC_FIX_TYPE tic6x_fix_info
+#define TC_INIT_FIX_DATA(fixP) tic6x_init_fix_data (fixP)
+struct fix;
+extern void tic6x_init_fix_data (struct fix *fixP);
+
+#define md_after_parse_args() tic6x_after_parse_args ()
+extern void tic6x_after_parse_args (void);
+
+#define md_cleanup() tic6x_cleanup ()
+extern void tic6x_cleanup (void);
+
+#define md_cons_align(n) tic6x_cons_align (n)
+extern void tic6x_cons_align (int n);
+
+#define md_do_align(n, fill, len, max, label) \
+ do { \
+ if (tic6x_do_align (n, fill, len, max)) \
+ goto label; \
+ } while (0)
+extern bfd_boolean tic6x_do_align (int n, char *fill, int len, int max);
+
+#define CONVERT_SYMBOLIC_ATTRIBUTE(name) \
+ tic6x_convert_symbolic_attribute (name)
+extern int tic6x_convert_symbolic_attribute (const char *);
+
+#define md_end() tic6x_end ();
+extern void tic6x_end (void);
+
+#define md_parse_name(name, exprP, mode, nextcharP) \
+ tic6x_parse_name (name, exprP, mode, nextcharP)
+extern int tic6x_parse_name (const char *name, expressionS *exprP,
+ enum expr_mode mode, char *nextchar);
+
+#define MD_PCREL_FROM_SECTION(FIX, SEC) tic6x_pcrel_from_section (FIX, SEC)
+extern long tic6x_pcrel_from_section (struct fix *fixp, segT sec);
+
+#define md_start_line_hook() tic6x_start_line_hook ()
+extern void tic6x_start_line_hook (void);
+
+#define TC_CONS_FIX_NEW(frag, where, size, exp, reloc) \
+ tic6x_cons_fix_new (frag, where, size, exp, reloc)
+extern void tic6x_cons_fix_new (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+#define tc_fix_adjustable(FIX) tic6x_fix_adjustable (FIX)
+extern bfd_boolean tic6x_fix_adjustable (struct fix *);
+
+#define tc_frob_label(sym) tic6x_frob_label (sym)
+extern void tic6x_frob_label (symbolS *sym);
+
+#define tc_init_after_args() tic6x_init_after_args ()
+extern void tic6x_init_after_args (void);
+
+#define tc_unrecognized_line(c) tic6x_unrecognized_line (c)
+extern int tic6x_unrecognized_line (int c);
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+/* CFI hooks. */
+#define tc_regname_to_dw2regnum tic6x_regname_to_dw2regnum
+int tic6x_regname_to_dw2regnum (char *regname);
+
+#define tc_cfi_frame_initial_instructions tic6x_frame_initial_instructions
+void tic6x_frame_initial_instructions (void);
+
+/* The return register is B3. */
+#define DWARF2_DEFAULT_RETURN_COLUMN (16 + 3)
+
+/* Registers are generally saved at negative offsets to the CFA. */
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+#define tc_cfi_startproc tic6x_cfi_startproc
+void tic6x_cfi_startproc (void);
+
+#define tc_cfi_endproc tic6x_cfi_endproc
+struct fde_entry;
+void tic6x_cfi_endproc (struct fde_entry *fde);
+
+#define tc_cfi_section_name ".c6xabi.exidx"
diff --git a/gas/config/tc-tilegx.c b/gas/config/tc-tilegx.c
new file mode 100644
index 0000000..3176d2f
--- /dev/null
+++ b/gas/config/tc-tilegx.c
@@ -0,0 +1,1895 @@
+/* tc-tilegx.c -- Assemble for a Tile-Gx chip.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include "struc-symbol.h"
+#include "subsegs.h"
+
+#include "elf/tilegx.h"
+#include "opcode/tilegx.h"
+
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+#include "safe-ctype.h"
+
+
+/* Special registers. */
+#define TREG_IDN0 57
+#define TREG_IDN1 58
+#define TREG_UDN0 59
+#define TREG_UDN1 60
+#define TREG_UDN2 61
+#define TREG_UDN3 62
+#define TREG_ZERO 63
+
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */
+int tilegx_cie_data_alignment;
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Either 32 or 64. */
+static int tilegx_arch_size = 64;
+
+
+const char *
+tilegx_target_format (void)
+{
+ if (target_big_endian) {
+ return tilegx_arch_size == 64 ? "elf64-tilegx-be" : "elf32-tilegx-be";
+ } else {
+ return tilegx_arch_size == 64 ? "elf64-tilegx-le" : "elf32-tilegx-le";
+ }
+}
+
+
+#define OPTION_32 (OPTION_MD_BASE + 0)
+#define OPTION_64 (OPTION_MD_BASE + 1)
+#define OPTION_EB (OPTION_MD_BASE + 2)
+#define OPTION_EL (OPTION_MD_BASE + 3)
+
+const char *md_shortopts = "VQ:";
+
+struct option md_longopts[] =
+{
+ {"32", no_argument, NULL, OPTION_32},
+ {"64", no_argument, NULL, OPTION_64},
+ {"EB", no_argument, NULL, OPTION_EB },
+ {"EL", no_argument, NULL, OPTION_EL },
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ case OPTION_32:
+ tilegx_arch_size = 32;
+ break;
+
+ case OPTION_64:
+ tilegx_arch_size = 64;
+ break;
+
+ case OPTION_EB:
+ target_big_endian = 1;
+ break;
+
+ case OPTION_EL:
+ target_big_endian = 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+ -Q ignored\n\
+ -V print assembler version number\n\
+ -EB/-EL generate big-endian/little-endian code\n\
+ --32/--64 generate 32bit/64bit code\n"));
+}
+
+
+/* Extra expression types. */
+
+#define O_hw0 O_md1
+#define O_hw1 O_md2
+#define O_hw2 O_md3
+#define O_hw3 O_md4
+#define O_hw0_last O_md5
+#define O_hw1_last O_md6
+#define O_hw2_last O_md7
+#define O_hw0_got O_md8
+#define O_hw0_last_got O_md9
+#define O_hw1_last_got O_md10
+#define O_plt O_md11
+#define O_hw0_tls_gd O_md12
+#define O_hw0_last_tls_gd O_md13
+#define O_hw1_last_tls_gd O_md14
+#define O_hw0_tls_ie O_md15
+#define O_hw0_last_tls_ie O_md16
+#define O_hw1_last_tls_ie O_md17
+#define O_hw0_tls_le O_md18
+#define O_hw0_last_tls_le O_md19
+#define O_hw1_last_tls_le O_md20
+#define O_tls_gd_call O_md21
+#define O_tls_gd_add O_md22
+#define O_tls_ie_load O_md23
+#define O_tls_add O_md24
+#define O_hw0_plt O_md25
+#define O_hw1_plt O_md26
+#define O_hw1_last_plt O_md27
+#define O_hw2_last_plt O_md28
+
+static struct hash_control *special_operator_hash;
+
+/* Hash tables for instruction mnemonic lookup. */
+static struct hash_control *op_hash;
+
+/* Hash table for spr lookup. */
+static struct hash_control *spr_hash;
+
+/* True temporarily while parsing an SPR expression. This changes the
+ * namespace to include SPR names. */
+static int parsing_spr;
+
+/* Are we currently inside `{ ... }'? */
+static int inside_bundle;
+
+struct tilegx_instruction
+{
+ const struct tilegx_opcode *opcode;
+ tilegx_pipeline pipe;
+ expressionS operand_values[TILEGX_MAX_OPERANDS];
+};
+
+/* This keeps track of the current bundle being built up. */
+static struct tilegx_instruction current_bundle[TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE];
+
+/* Index in current_bundle for the next instruction to parse. */
+static int current_bundle_index;
+
+/* Allow 'r63' in addition to 'zero', etc. Normally we disallow this as
+ 'zero' is not a real register, so using it accidentally would be a
+ nasty bug. For other registers, such as 'sp', code using multiple names
+ for the same physical register is excessively confusing.
+
+ The '.require_canonical_reg_names' pseudo-op turns this error on,
+ and the '.no_require_canonical_reg_names' pseudo-op turns this off.
+ By default the error is on. */
+static int require_canonical_reg_names;
+
+/* Allow bundles that do undefined or suspicious things like write
+ two different values to the same register at the same time.
+
+ The '.no_allow_suspicious_bundles' pseudo-op turns this error on,
+ and the '.allow_suspicious_bundles' pseudo-op turns this off. */
+static int allow_suspicious_bundles;
+
+
+/* A hash table of main processor registers, mapping each register name
+ to its index.
+
+ Furthermore, if the register number is greater than the number
+ of registers for that processor, the user used an illegal alias
+ for that register (e.g. r63 instead of zero), so we should generate
+ a warning. The attempted register number can be found by clearing
+ NONCANONICAL_REG_NAME_FLAG. */
+static struct hash_control *main_reg_hash;
+
+
+/* We cannot unambiguously store a 0 in a hash table and look it up,
+ so we OR in this flag to every canonical register. */
+#define CANONICAL_REG_NAME_FLAG 0x1000
+
+/* By default we disallow register aliases like r63, but we record
+ them in the hash table in case the .no_require_canonical_reg_names
+ directive is used. Noncanonical names have this value added to them. */
+#define NONCANONICAL_REG_NAME_FLAG 0x2000
+
+/* Discards flags for register hash table entries and returns the
+ reg number. */
+#define EXTRACT_REGNO(p) ((p) & 63)
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc., that the MD part of the assembler will
+ need. */
+
+void
+md_begin (void)
+{
+ const struct tilegx_opcode *op;
+ int i;
+ int mach = (tilegx_arch_size == 64) ? bfd_mach_tilegx : bfd_mach_tilegx32;
+
+ if (! bfd_set_arch_mach (stdoutput, bfd_arch_tilegx, mach))
+ as_warn (_("Could not set architecture and machine"));
+
+ /* Guarantee text section is aligned. */
+ bfd_set_section_alignment (stdoutput, text_section,
+ TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES);
+
+ require_canonical_reg_names = 1;
+ allow_suspicious_bundles = 0;
+ current_bundle_index = 0;
+ inside_bundle = 0;
+
+ tilegx_cie_data_alignment = (tilegx_arch_size == 64 ? -8 : -4);
+
+ /* Initialize special operator hash table. */
+ special_operator_hash = hash_new ();
+#define INSERT_SPECIAL_OP(name) \
+ hash_insert (special_operator_hash, #name, (void *)O_##name)
+
+ INSERT_SPECIAL_OP (hw0);
+ INSERT_SPECIAL_OP (hw1);
+ INSERT_SPECIAL_OP (hw2);
+ INSERT_SPECIAL_OP (hw3);
+ INSERT_SPECIAL_OP (hw0_last);
+ INSERT_SPECIAL_OP (hw1_last);
+ INSERT_SPECIAL_OP (hw2_last);
+ /* hw3_last is a convenience alias for the equivalent hw3. */
+ hash_insert (special_operator_hash, "hw3_last", (void*)O_hw3);
+ INSERT_SPECIAL_OP (hw0_got);
+ INSERT_SPECIAL_OP (hw0_last_got);
+ INSERT_SPECIAL_OP (hw1_last_got);
+ INSERT_SPECIAL_OP(plt);
+ INSERT_SPECIAL_OP (hw0_tls_gd);
+ INSERT_SPECIAL_OP (hw0_last_tls_gd);
+ INSERT_SPECIAL_OP (hw1_last_tls_gd);
+ INSERT_SPECIAL_OP (hw0_tls_ie);
+ INSERT_SPECIAL_OP (hw0_last_tls_ie);
+ INSERT_SPECIAL_OP (hw1_last_tls_ie);
+ INSERT_SPECIAL_OP (hw0_tls_le);
+ INSERT_SPECIAL_OP (hw0_last_tls_le);
+ INSERT_SPECIAL_OP (hw1_last_tls_le);
+ INSERT_SPECIAL_OP (tls_gd_call);
+ INSERT_SPECIAL_OP (tls_gd_add);
+ INSERT_SPECIAL_OP (tls_ie_load);
+ INSERT_SPECIAL_OP (tls_add);
+ INSERT_SPECIAL_OP (hw0_plt);
+ INSERT_SPECIAL_OP (hw1_plt);
+ INSERT_SPECIAL_OP (hw1_last_plt);
+ INSERT_SPECIAL_OP (hw2_last_plt);
+#undef INSERT_SPECIAL_OP
+
+ /* Initialize op_hash hash table. */
+ op_hash = hash_new ();
+ for (op = &tilegx_opcodes[0]; op->name != NULL; op++)
+ {
+ const char *hash_err = hash_insert (op_hash, op->name, (void *)op);
+ if (hash_err != NULL)
+ as_fatal (_("Internal Error: Can't hash %s: %s"), op->name, hash_err);
+ }
+
+ /* Initialize the spr hash table. */
+ parsing_spr = 0;
+ spr_hash = hash_new ();
+ for (i = 0; i < tilegx_num_sprs; i++)
+ hash_insert (spr_hash, tilegx_sprs[i].name,
+ (void *) &tilegx_sprs[i]);
+
+ /* Set up the main_reg_hash table. We use this instead of
+ creating a symbol in the register section to avoid ambiguities
+ with labels that have the same names as registers. */
+ main_reg_hash = hash_new ();
+ for (i = 0; i < TILEGX_NUM_REGISTERS; i++)
+ {
+ char buf[64];
+
+ hash_insert (main_reg_hash, tilegx_register_names[i],
+ (void *) (long) (i | CANONICAL_REG_NAME_FLAG));
+
+ /* See if we should insert a noncanonical alias, like r63. */
+ sprintf (buf, "r%d", i);
+ if (strcmp (buf, tilegx_register_names[i]) != 0)
+ hash_insert (main_reg_hash, xstrdup (buf),
+ (void *) (long) (i | NONCANONICAL_REG_NAME_FLAG));
+ }
+}
+
+#define BUNDLE_TEMPLATE_MASK(p0, p1, p2) \
+ ((p0) | ((p1) << 8) | ((p2) << 16))
+#define BUNDLE_TEMPLATE(p0, p1, p2) \
+ { { (p0), (p1), (p2) }, \
+ BUNDLE_TEMPLATE_MASK(1 << (p0), 1 << (p1), (1 << (p2))) \
+ }
+
+#define NO_PIPELINE TILEGX_NUM_PIPELINE_ENCODINGS
+
+struct bundle_template
+{
+ tilegx_pipeline pipe[TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE];
+ unsigned int pipe_mask;
+};
+
+static const struct bundle_template bundle_templates[] =
+{
+ /* In Y format we must always have something in Y2, since it has
+ no fnop, so this conveys that Y2 must always be used. */
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y0, TILEGX_PIPELINE_Y2, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y1, TILEGX_PIPELINE_Y2, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y2, TILEGX_PIPELINE_Y0, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y2, TILEGX_PIPELINE_Y1, NO_PIPELINE),
+
+ /* Y format has three instructions. */
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y0,TILEGX_PIPELINE_Y1,TILEGX_PIPELINE_Y2),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y0,TILEGX_PIPELINE_Y2,TILEGX_PIPELINE_Y1),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y1,TILEGX_PIPELINE_Y0,TILEGX_PIPELINE_Y2),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y1,TILEGX_PIPELINE_Y2,TILEGX_PIPELINE_Y0),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y2,TILEGX_PIPELINE_Y0,TILEGX_PIPELINE_Y1),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_Y2,TILEGX_PIPELINE_Y1,TILEGX_PIPELINE_Y0),
+
+ /* X format has only two instructions. */
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_X0, TILEGX_PIPELINE_X1, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEGX_PIPELINE_X1, TILEGX_PIPELINE_X0, NO_PIPELINE)
+};
+
+
+static void
+prepend_nop_to_bundle (tilegx_mnemonic mnemonic)
+{
+ memmove (&current_bundle[1], &current_bundle[0],
+ current_bundle_index * sizeof current_bundle[0]);
+ current_bundle[0].opcode = &tilegx_opcodes[mnemonic];
+ ++current_bundle_index;
+}
+
+static tilegx_bundle_bits
+insert_operand (tilegx_bundle_bits bits,
+ const struct tilegx_operand *operand,
+ int operand_value,
+ char *file,
+ unsigned lineno)
+{
+ /* Range-check the immediate. */
+ int num_bits = operand->num_bits;
+
+ operand_value >>= operand->rightshift;
+
+ if (bfd_check_overflow (operand->is_signed
+ ? complain_overflow_signed
+ : complain_overflow_unsigned,
+ num_bits,
+ 0,
+ bfd_arch_bits_per_address (stdoutput),
+ operand_value)
+ != bfd_reloc_ok)
+ {
+ offsetT min, max;
+ if (operand->is_signed)
+ {
+ min = -(1 << (num_bits - 1));
+ max = (1 << (num_bits - 1)) - 1;
+ }
+ else
+ {
+ min = 0;
+ max = (1 << num_bits) - 1;
+ }
+ as_bad_value_out_of_range (_("operand"), operand_value, min, max,
+ file, lineno);
+ }
+
+ /* Write out the bits for the immediate. */
+ return bits | operand->insert (operand_value);
+}
+
+
+static int
+apply_special_operator (operatorT op, offsetT num, char *file, unsigned lineno)
+{
+ int ret;
+ int check_shift = -1;
+
+ switch (op)
+ {
+ case O_hw0_last:
+ check_shift = 0;
+ /* Fall through. */
+ case O_hw0:
+ ret = (signed short)num;
+ break;
+
+ case O_hw1_last:
+ check_shift = 16;
+ /* Fall through. */
+ case O_hw1:
+ ret = (signed short)(num >> 16);
+ break;
+
+ case O_hw2_last:
+ check_shift = 32;
+ /* Fall through. */
+ case O_hw2:
+ ret = (signed short)(num >> 32);
+ break;
+
+ case O_hw3:
+ ret = (signed short)(num >> 48);
+ break;
+
+ default:
+ abort ();
+ break;
+ }
+
+ if (check_shift >= 0 && ret != (num >> check_shift))
+ {
+ as_bad_value_out_of_range (_("operand"), num,
+ ~0ULL << (check_shift + 16 - 1),
+ ~0ULL >> (64 - (check_shift + 16 - 1)),
+ file, lineno);
+ }
+
+ return ret;
+}
+
+static tilegx_bundle_bits
+emit_tilegx_instruction (tilegx_bundle_bits bits,
+ int num_operands,
+ const unsigned char *operands,
+ expressionS *operand_values,
+ char *bundle_start)
+{
+ int i;
+
+ for (i = 0; i < num_operands; i++)
+ {
+ const struct tilegx_operand *operand =
+ &tilegx_operands[operands[i]];
+ expressionS *operand_exp = &operand_values[i];
+ int is_pc_relative = operand->is_pc_relative;
+
+ if (operand_exp->X_op == O_register
+ || (operand_exp->X_op == O_constant && !is_pc_relative))
+ {
+ /* We know what the bits are right now, so insert them. */
+ bits = insert_operand (bits, operand, operand_exp->X_add_number,
+ NULL, 0);
+ }
+ else
+ {
+ bfd_reloc_code_real_type reloc = operand->default_reloc;
+ expressionS subexp;
+ int die = 0, use_subexp = 0, require_symbol = 0;
+ fixS *fixP;
+
+ /* Take an expression like hw0(x) and turn it into x with
+ a different reloc type. */
+ switch (operand_exp->X_op)
+ {
+#define HANDLE_OP16(suffix) \
+ switch (reloc) \
+ { \
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST: \
+ reloc = BFD_RELOC_TILEGX_IMM16_X0_##suffix; \
+ break; \
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST: \
+ reloc = BFD_RELOC_TILEGX_IMM16_X1_##suffix; \
+ break; \
+ default: \
+ die = 1; \
+ break; \
+ } \
+ use_subexp = 1
+
+ case O_hw0:
+ HANDLE_OP16 (HW0);
+ break;
+
+ case O_hw1:
+ HANDLE_OP16 (HW1);
+ break;
+
+ case O_hw2:
+ HANDLE_OP16 (HW2);
+ break;
+
+ case O_hw3:
+ HANDLE_OP16 (HW3);
+ break;
+
+ case O_hw0_last:
+ HANDLE_OP16 (HW0_LAST);
+ break;
+
+ case O_hw1_last:
+ HANDLE_OP16 (HW1_LAST);
+ break;
+
+ case O_hw2_last:
+ HANDLE_OP16 (HW2_LAST);
+ break;
+
+ case O_hw0_got:
+ HANDLE_OP16 (HW0_GOT);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_last_got:
+ HANDLE_OP16 (HW0_LAST_GOT);
+ require_symbol = 1;
+ break;
+
+ case O_hw1_last_got:
+ HANDLE_OP16 (HW1_LAST_GOT);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_tls_gd:
+ HANDLE_OP16 (HW0_TLS_GD);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_last_tls_gd:
+ HANDLE_OP16 (HW0_LAST_TLS_GD);
+ require_symbol = 1;
+ break;
+
+ case O_hw1_last_tls_gd:
+ HANDLE_OP16 (HW1_LAST_TLS_GD);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_tls_ie:
+ HANDLE_OP16 (HW0_TLS_IE);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_last_tls_ie:
+ HANDLE_OP16 (HW0_LAST_TLS_IE);
+ require_symbol = 1;
+ break;
+
+ case O_hw1_last_tls_ie:
+ HANDLE_OP16 (HW1_LAST_TLS_IE);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_tls_le:
+ HANDLE_OP16 (HW0_TLS_LE);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_last_tls_le:
+ HANDLE_OP16 (HW0_LAST_TLS_LE);
+ require_symbol = 1;
+ break;
+
+ case O_hw1_last_tls_le:
+ HANDLE_OP16 (HW1_LAST_TLS_LE);
+ require_symbol = 1;
+ break;
+
+ case O_hw0_plt:
+ HANDLE_OP16 (HW0_PLT_PCREL);
+ break;
+
+ case O_hw1_plt:
+ HANDLE_OP16 (HW1_PLT_PCREL);
+ break;
+
+ case O_hw1_last_plt:
+ HANDLE_OP16 (HW1_LAST_PLT_PCREL);
+ break;
+
+ case O_hw2_last_plt:
+ HANDLE_OP16 (HW2_LAST_PLT_PCREL);
+ break;
+
+#undef HANDLE_OP16
+
+ case O_plt:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEGX_JUMPOFF_X1:
+ reloc = BFD_RELOC_TILEGX_JUMPOFF_X1_PLT;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_call:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEGX_JUMPOFF_X1:
+ reloc = BFD_RELOC_TILEGX_TLS_GD_CALL;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_add:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEGX_IMM8_X0:
+ reloc = BFD_RELOC_TILEGX_IMM8_X0_TLS_GD_ADD;
+ break;
+ case BFD_RELOC_TILEGX_IMM8_X1:
+ reloc = BFD_RELOC_TILEGX_IMM8_X1_TLS_GD_ADD;
+ break;
+ case BFD_RELOC_TILEGX_IMM8_Y0:
+ reloc = BFD_RELOC_TILEGX_IMM8_Y0_TLS_GD_ADD;
+ break;
+ case BFD_RELOC_TILEGX_IMM8_Y1:
+ reloc = BFD_RELOC_TILEGX_IMM8_Y1_TLS_GD_ADD;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_ie_load:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEGX_IMM8_X1:
+ reloc = BFD_RELOC_TILEGX_TLS_IE_LOAD;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_add:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEGX_IMM8_X0:
+ reloc = BFD_RELOC_TILEGX_IMM8_X0_TLS_ADD;
+ break;
+ case BFD_RELOC_TILEGX_IMM8_X1:
+ reloc = BFD_RELOC_TILEGX_IMM8_X1_TLS_ADD;
+ break;
+ case BFD_RELOC_TILEGX_IMM8_Y0:
+ reloc = BFD_RELOC_TILEGX_IMM8_Y0_TLS_ADD;
+ break;
+ case BFD_RELOC_TILEGX_IMM8_Y1:
+ reloc = BFD_RELOC_TILEGX_IMM8_Y1_TLS_ADD;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ default:
+ /* Do nothing. */
+ break;
+ }
+
+ if (die)
+ {
+ as_bad (_("Invalid operator for operand."));
+ }
+ else if (use_subexp)
+ {
+ /* Now that we've changed the reloc, change ha16(x) into x,
+ etc. */
+
+ if (!operand_exp->X_add_symbol->sy_flags.sy_local_symbol
+ && operand_exp->X_add_symbol->sy_value.X_md)
+ {
+ /* HACK: We used X_md to mark this symbol as a fake wrapper
+ around a real expression. To unwrap it, we just grab its
+ value here. */
+ operand_exp = &operand_exp->X_add_symbol->sy_value;
+
+ if (require_symbol)
+ {
+ /* Look at the expression, and reject it if it's not a
+ plain symbol. */
+ if (operand_exp->X_op != O_symbol
+ || operand_exp->X_add_number != 0)
+ as_bad (_("Operator may only be applied to symbols."));
+ }
+ }
+ else
+ {
+ /* The value of this expression is an actual symbol, so
+ turn that into an expression. */
+ memset (&subexp, 0, sizeof subexp);
+ subexp.X_op = O_symbol;
+ subexp.X_add_symbol = operand_exp->X_add_symbol;
+ operand_exp = &subexp;
+ }
+ }
+
+ /* Create a fixup to handle this later. */
+ fixP = fix_new_exp (frag_now,
+ bundle_start - frag_now->fr_literal,
+ (operand->num_bits + 7) >> 3,
+ operand_exp,
+ is_pc_relative,
+ reloc);
+ fixP->tc_fix_data = operand;
+
+ /* Don't do overflow checking if we are applying a function like
+ ha16. */
+ fixP->fx_no_overflow |= use_subexp;
+ }
+ }
+ return bits;
+}
+
+
+/* Detects and complains if two instructions in current_bundle write
+ to the same register, either implicitly or explicitly, or if a
+ read-only register is written. */
+static void
+check_illegal_reg_writes (void)
+{
+ BFD_HOST_U_64_BIT all_regs_written = 0;
+ int j;
+
+ for (j = 0; j < current_bundle_index; j++)
+ {
+ const struct tilegx_instruction *instr = &current_bundle[j];
+ int k;
+ BFD_HOST_U_64_BIT regs =
+ ((BFD_HOST_U_64_BIT)1) << instr->opcode->implicitly_written_register;
+ BFD_HOST_U_64_BIT conflict;
+
+ for (k = 0; k < instr->opcode->num_operands; k++)
+ {
+ const struct tilegx_operand *operand =
+ &tilegx_operands[instr->opcode->operands[instr->pipe][k]];
+
+ if (operand->is_dest_reg)
+ {
+ int regno = instr->operand_values[k].X_add_number;
+ BFD_HOST_U_64_BIT mask = ((BFD_HOST_U_64_BIT)1) << regno;
+
+ if ((mask & ( (((BFD_HOST_U_64_BIT)1) << TREG_IDN1)
+ | (((BFD_HOST_U_64_BIT)1) << TREG_UDN1)
+ | (((BFD_HOST_U_64_BIT)1) << TREG_UDN2)
+ | (((BFD_HOST_U_64_BIT)1) << TREG_UDN3))) != 0
+ && !allow_suspicious_bundles)
+ {
+ as_bad (_("Writes to register '%s' are not allowed."),
+ tilegx_register_names[regno]);
+ }
+
+ regs |= mask;
+ }
+ }
+
+ /* Writing to the zero register doesn't count. */
+ regs &= ~(((BFD_HOST_U_64_BIT)1) << TREG_ZERO);
+
+ conflict = all_regs_written & regs;
+ if (conflict != 0 && !allow_suspicious_bundles)
+ {
+ /* Find which register caused the conflict. */
+ const char *conflicting_reg_name = "???";
+ int i;
+
+ for (i = 0; i < TILEGX_NUM_REGISTERS; i++)
+ {
+ if (((conflict >> i) & 1) != 0)
+ {
+ conflicting_reg_name = tilegx_register_names[i];
+ break;
+ }
+ }
+
+ as_bad (_("Two instructions in the same bundle both write "
+ "to register %s, which is not allowed."),
+ conflicting_reg_name);
+ }
+
+ all_regs_written |= regs;
+ }
+}
+
+
+static void
+tilegx_flush_bundle (void)
+{
+ unsigned i;
+ int j;
+ addressT addr_mod;
+ unsigned compatible_pipes;
+ const struct bundle_template *match;
+ char *f;
+
+ inside_bundle = 0;
+
+ switch (current_bundle_index)
+ {
+ case 0:
+ /* No instructions. */
+ return;
+ case 1:
+ if (current_bundle[0].opcode->can_bundle)
+ {
+ /* Simplify later logic by adding an explicit fnop. */
+ prepend_nop_to_bundle (TILEGX_OPC_FNOP);
+ }
+ else
+ {
+ /* This instruction cannot be bundled with anything else.
+ Prepend an explicit 'nop', rather than an 'fnop', because
+ fnops can be replaced by later binary-processing tools while
+ nops cannot. */
+ prepend_nop_to_bundle (TILEGX_OPC_NOP);
+ }
+ break;
+ default:
+ if (!allow_suspicious_bundles)
+ {
+ /* Make sure all instructions can be bundled with other
+ instructions. */
+ const struct tilegx_opcode *cannot_bundle = NULL;
+ bfd_boolean seen_non_nop = FALSE;
+
+ for (j = 0; j < current_bundle_index; j++)
+ {
+ const struct tilegx_opcode *op = current_bundle[j].opcode;
+
+ if (!op->can_bundle && cannot_bundle == NULL)
+ cannot_bundle = op;
+ else if (op->mnemonic != TILEGX_OPC_NOP
+ && op->mnemonic != TILEGX_OPC_INFO
+ && op->mnemonic != TILEGX_OPC_INFOL)
+ seen_non_nop = TRUE;
+ }
+
+ if (cannot_bundle != NULL && seen_non_nop)
+ {
+ current_bundle_index = 0;
+ as_bad (_("'%s' may not be bundled with other instructions."),
+ cannot_bundle->name);
+ return;
+ }
+ }
+ break;
+ }
+
+ compatible_pipes =
+ BUNDLE_TEMPLATE_MASK(current_bundle[0].opcode->pipes,
+ current_bundle[1].opcode->pipes,
+ (current_bundle_index == 3
+ ? current_bundle[2].opcode->pipes
+ : (1 << NO_PIPELINE)));
+
+ /* Find a template that works, if any. */
+ match = NULL;
+ for (i = 0; i < sizeof bundle_templates / sizeof bundle_templates[0]; i++)
+ {
+ const struct bundle_template *b = &bundle_templates[i];
+ if ((b->pipe_mask & compatible_pipes) == b->pipe_mask)
+ {
+ match = b;
+ break;
+ }
+ }
+
+ if (match == NULL)
+ {
+ current_bundle_index = 0;
+ as_bad (_("Invalid combination of instructions for bundle."));
+ return;
+ }
+
+ /* If the section seems to have no alignment set yet, go ahead and
+ make it large enough to hold code. */
+ if (bfd_get_section_alignment (stdoutput, now_seg) == 0)
+ bfd_set_section_alignment (stdoutput, now_seg,
+ TILEGX_LOG2_BUNDLE_ALIGNMENT_IN_BYTES);
+
+ for (j = 0; j < current_bundle_index; j++)
+ current_bundle[j].pipe = match->pipe[j];
+
+ if (current_bundle_index == 2 && !tilegx_is_x_pipeline (match->pipe[0]))
+ {
+ /* We are in Y mode with only two instructions, so add an FNOP. */
+ prepend_nop_to_bundle (TILEGX_OPC_FNOP);
+
+ /* Figure out what pipe the fnop must be in via arithmetic.
+ * p0 + p1 + p2 must sum to the sum of TILEGX_PIPELINE_Y[012]. */
+ current_bundle[0].pipe =
+ (tilegx_pipeline)((TILEGX_PIPELINE_Y0
+ + TILEGX_PIPELINE_Y1
+ + TILEGX_PIPELINE_Y2) -
+ (current_bundle[1].pipe + current_bundle[2].pipe));
+ }
+
+ check_illegal_reg_writes ();
+
+ f = frag_more (TILEGX_BUNDLE_SIZE_IN_BYTES);
+
+ /* Check to see if this bundle is at an offset that is a multiple of 8-bytes
+ from the start of the frag. */
+ addr_mod = frag_now_fix () & (TILEGX_BUNDLE_ALIGNMENT_IN_BYTES - 1);
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ as_bad (_("instruction address is not a multiple of 8"));
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+
+ tilegx_bundle_bits bits = 0;
+ for (j = 0; j < current_bundle_index; j++)
+ {
+ struct tilegx_instruction *instr = &current_bundle[j];
+ tilegx_pipeline pipeline = instr->pipe;
+ const struct tilegx_opcode *opcode = instr->opcode;
+
+ bits |= emit_tilegx_instruction (opcode->fixed_bit_values[pipeline],
+ opcode->num_operands,
+ &opcode->operands[pipeline][0],
+ instr->operand_values,
+ f);
+ }
+
+ number_to_chars_littleendian (f, bits, 8);
+ current_bundle_index = 0;
+
+ /* Emit DWARF2 debugging information. */
+ dwarf2_emit_insn (TILEGX_BUNDLE_SIZE_IN_BYTES);
+}
+
+
+/* Extend the expression parser to handle hw0(label), etc.
+ as well as SPR names when in the context of parsing an SPR. */
+
+int
+tilegx_parse_name (char *name, expressionS *e, char *nextcharP)
+{
+ operatorT op = O_illegal;
+
+ if (parsing_spr)
+ {
+ void* val = hash_find (spr_hash, name);
+ if (val == NULL)
+ return 0;
+
+ memset (e, 0, sizeof *e);
+ e->X_op = O_constant;
+ e->X_add_number = ((const struct tilegx_spr *)val)->number;
+ return 1;
+ }
+
+ if (*nextcharP != '(')
+ {
+ /* hw0, etc. not followed by a paren is just a label with that name. */
+ return 0;
+ }
+ else
+ {
+ /* Look up the operator in our table. */
+ void* val = hash_find (special_operator_hash, name);
+ if (val == 0)
+ return 0;
+ op = (operatorT)(long)val;
+ }
+
+ /* Restore old '(' and skip it. */
+ *input_line_pointer = '(';
+ ++input_line_pointer;
+
+ expression (e);
+
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("Missing ')'"));
+ *nextcharP = *input_line_pointer;
+ return 0;
+ }
+ /* Skip ')'. */
+ ++input_line_pointer;
+
+ if (e->X_op == O_register || e->X_op == O_absent)
+ {
+ as_bad (_("Invalid expression."));
+ e->X_op = O_constant;
+ e->X_add_number = 0;
+ }
+ else
+ {
+ /* Wrap subexpression with a unary operator. */
+ symbolS *sym = make_expr_symbol (e);
+
+ if (sym != e->X_add_symbol)
+ {
+ /* HACK: mark this symbol as a temporary wrapper around a proper
+ expression, so we can unwrap it later once we have communicated
+ the relocation type. */
+ sym->sy_value.X_md = 1;
+ }
+
+ memset (e, 0, sizeof *e);
+ e->X_op = op;
+ e->X_add_symbol = sym;
+ e->X_add_number = 0;
+ }
+
+ *nextcharP = *input_line_pointer;
+ return 1;
+}
+
+
+/* Parses an expression which must be a register name. */
+
+static void
+parse_reg_expression (expressionS* expression)
+{
+ /* Zero everything to make sure we don't miss any flags. */
+ memset (expression, 0, sizeof *expression);
+
+ char* regname = input_line_pointer;
+ char terminating_char = get_symbol_end ();
+
+ void* pval = hash_find (main_reg_hash, regname);
+
+ if (pval == NULL)
+ {
+ as_bad (_("Expected register, got '%s'."), regname);
+ }
+
+ int regno_and_flags = (int)(size_t)pval;
+ int regno = EXTRACT_REGNO(regno_and_flags);
+
+ if ((regno_and_flags & NONCANONICAL_REG_NAME_FLAG)
+ && require_canonical_reg_names)
+ {
+ as_warn (_("Found use of non-canonical register name %s; "
+ "use %s instead."),
+ regname,
+ tilegx_register_names[regno]);
+ }
+
+ /* Restore the old character following the register name. */
+ *input_line_pointer = terminating_char;
+
+ /* Fill in the expression fields to indicate it's a register. */
+ expression->X_op = O_register;
+ expression->X_add_number = regno;
+}
+
+
+/* Parses and type-checks comma-separated operands in input_line_pointer. */
+
+static void
+parse_operands (const char *opcode_name,
+ const unsigned char *operands,
+ int num_operands,
+ expressionS *operand_values)
+{
+ int i;
+
+ memset (operand_values, 0, num_operands * sizeof operand_values[0]);
+
+ SKIP_WHITESPACE ();
+ for (i = 0; i < num_operands; i++)
+ {
+ tilegx_operand_type type = tilegx_operands[operands[i]].type;
+
+ SKIP_WHITESPACE ();
+
+ if (type == TILEGX_OP_TYPE_REGISTER)
+ {
+ parse_reg_expression (&operand_values[i]);
+ }
+ else if (*input_line_pointer == '}')
+ {
+ operand_values[i].X_op = O_absent;
+ }
+ else if (type == TILEGX_OP_TYPE_SPR)
+ {
+ /* Modify the expression parser to add SPRs to the namespace. */
+ parsing_spr = 1;
+ expression (&operand_values[i]);
+ parsing_spr = 0;
+ }
+ else
+ {
+ expression (&operand_values[i]);
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (i + 1 < num_operands)
+ {
+ int separator = (unsigned char)*input_line_pointer++;
+
+ if (is_end_of_line[separator] || (separator == '}'))
+ {
+ as_bad (_("Too few operands to '%s'."), opcode_name);
+ return;
+ }
+ else if (separator != ',')
+ {
+ as_bad (_("Unexpected character '%c' after operand %d to %s."),
+ (char)separator, i + 1, opcode_name);
+ return;
+ }
+ }
+
+ /* Arbitrarily use the first valid pipe to get the operand type,
+ since they are all the same. */
+ switch (tilegx_operands[operands[i]].type)
+ {
+ case TILEGX_OP_TYPE_REGISTER:
+ /* Handled in parse_reg_expression already. */
+ break;
+ case TILEGX_OP_TYPE_SPR:
+ /* Fall through */
+ case TILEGX_OP_TYPE_IMMEDIATE:
+ /* Fall through */
+ case TILEGX_OP_TYPE_ADDRESS:
+ if ( operand_values[i].X_op == O_register
+ || operand_values[i].X_op == O_illegal
+ || operand_values[i].X_op == O_absent)
+ as_bad (_("Expected immediate expression"));
+ break;
+ default:
+ abort();
+ }
+ }
+
+ if (!is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ case '}':
+ if (!inside_bundle)
+ as_bad (_("Found '}' when not bundling."));
+ ++input_line_pointer;
+ inside_bundle = 0;
+ demand_empty_rest_of_line ();
+ break;
+
+ case ',':
+ as_bad (_("Too many operands"));
+ break;
+
+ default:
+ /* Use default error for unrecognized garbage. */
+ demand_empty_rest_of_line ();
+ break;
+ }
+ }
+}
+
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit the
+ frags/bytes it assembles to. */
+
+void
+md_assemble (char *str)
+{
+ char old_char;
+ size_t opname_len;
+ char *old_input_line_pointer;
+ const struct tilegx_opcode *op;
+ int first_pipe;
+
+ /* Split off the opcode and look it up. */
+ opname_len = strcspn (str, " {}");
+ old_char = str[opname_len];
+ str[opname_len] = '\0';
+
+ op = hash_find(op_hash, str);
+ str[opname_len] = old_char;
+ if (op == NULL)
+ {
+ as_bad (_("Unknown opcode `%.*s'."), (int)opname_len, str);
+ return;
+ }
+
+ /* Prepare to parse the operands. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = str + opname_len;
+ SKIP_WHITESPACE ();
+
+ if (current_bundle_index == TILEGX_MAX_INSTRUCTIONS_PER_BUNDLE)
+ {
+ as_bad (_("Too many instructions for bundle."));
+ tilegx_flush_bundle ();
+ }
+
+ /* Make sure we have room for the upcoming bundle before we
+ create any fixups. Otherwise if we have to switch to a new
+ frag the fixup dot_value fields will be wrong. */
+ frag_grow (TILEGX_BUNDLE_SIZE_IN_BYTES);
+
+ /* Find a valid pipe for this opcode. */
+ for (first_pipe = 0; (op->pipes & (1 << first_pipe)) == 0; first_pipe++)
+ ;
+
+ /* Call the function that assembles this instruction. */
+ current_bundle[current_bundle_index].opcode = op;
+ parse_operands (op->name,
+ &op->operands[first_pipe][0],
+ op->num_operands,
+ current_bundle[current_bundle_index].operand_values);
+ ++current_bundle_index;
+
+ /* Restore the saved value of input_line_pointer. */
+ input_line_pointer = old_input_line_pointer;
+
+ /* If we weren't inside curly braces, go ahead and emit
+ this lone instruction as a bundle right now. */
+ if (!inside_bundle)
+ tilegx_flush_bundle ();
+}
+
+
+static void
+s_require_canonical_reg_names (int require)
+{
+ demand_empty_rest_of_line ();
+ require_canonical_reg_names = require;
+}
+
+static void
+s_allow_suspicious_bundles (int allow)
+{
+ demand_empty_rest_of_line ();
+ allow_suspicious_bundles = allow;
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 0}, /* Defaulting is invalid (0). */
+ {"word", cons, 4},
+ {"require_canonical_reg_names", s_require_canonical_reg_names, 1 },
+ {"no_require_canonical_reg_names", s_require_canonical_reg_names, 0 },
+ {"allow_suspicious_bundles", s_allow_suspicious_bundles, 1 },
+ {"no_allow_suspicious_bundles", s_allow_suspicious_bundles, 0 },
+ { NULL, 0, 0 }
+};
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+/* Turn the string pointed to by litP into a floating point constant
+ of type TYPE, and emit the appropriate bytes. The number of
+ LITTLENUMS emitted is stored in *SIZEP. An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ 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);
+ /* This loops outputs the LITTLENUMs in REVERSE order; in accord with
+ the bigendian 386. */
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+
+void
+tilegx_cons_fix_new (fragS *frag,
+ int where,
+ int nbytes,
+ expressionS *exp)
+{
+ expressionS subexp;
+ bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
+ int no_overflow = 0;
+ fixS *fixP;
+
+ /* See if it's one of our special functions. */
+ switch (exp->X_op)
+ {
+ case O_hw0:
+ reloc = BFD_RELOC_TILEGX_HW0;
+ no_overflow = 1;
+ break;
+ case O_hw1:
+ reloc = BFD_RELOC_TILEGX_HW1;
+ no_overflow = 1;
+ break;
+ case O_hw2:
+ reloc = BFD_RELOC_TILEGX_HW2;
+ no_overflow = 1;
+ break;
+ case O_hw3:
+ reloc = BFD_RELOC_TILEGX_HW3;
+ no_overflow = 1;
+ break;
+ case O_hw0_last:
+ reloc = BFD_RELOC_TILEGX_HW0_LAST;
+ break;
+ case O_hw1_last:
+ reloc = BFD_RELOC_TILEGX_HW1_LAST;
+ break;
+ case O_hw2_last:
+ reloc = BFD_RELOC_TILEGX_HW2_LAST;
+ break;
+
+ default:
+ /* Do nothing. */
+ break;
+ }
+
+ if (reloc != BFD_RELOC_NONE)
+ {
+ if (nbytes != 2)
+ {
+ as_bad (_("This operator only produces two byte values."));
+ nbytes = 2;
+ }
+
+ memset (&subexp, 0, sizeof subexp);
+ subexp.X_op = O_symbol;
+ subexp.X_add_symbol = exp->X_add_symbol;
+ exp = &subexp;
+ }
+ else
+ {
+ switch (nbytes)
+ {
+ case 1:
+ reloc = BFD_RELOC_8;
+ break;
+ case 2:
+ reloc = BFD_RELOC_16;
+ break;
+ case 4:
+ reloc = BFD_RELOC_32;
+ break;
+ case 8:
+ reloc = BFD_RELOC_64;
+ break;
+ default:
+ as_bad (_("unsupported BFD relocation size %d"), nbytes);
+ reloc = BFD_RELOC_64;
+ break;
+ }
+ }
+
+ fixP = fix_new_exp (frag, where, nbytes, exp, 0, reloc);
+ fixP->tc_fix_data = NULL;
+ fixP->fx_no_overflow |= no_overflow;
+}
+
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
+{
+ const struct tilegx_operand *operand;
+ valueT value = *valP;
+ operatorT special;
+ char *p;
+
+ /* Leave these for the linker. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return;
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ }
+
+ /* Correct relocation types for pc-relativeness. */
+ switch (fixP->fx_r_type)
+ {
+#define FIX_PCREL(rtype) \
+ case rtype: \
+ if (fixP->fx_pcrel) \
+ fixP->fx_r_type = rtype##_PCREL; \
+ break; \
+ \
+ case rtype##_PCREL: \
+ if (!fixP->fx_pcrel) \
+ fixP->fx_r_type = rtype; \
+ break
+
+#define FIX_PLT_PCREL(rtype) \
+ case rtype##_PLT_PCREL: \
+ if (!fixP->fx_pcrel) \
+ fixP->fx_r_type = rtype; \
+ \
+ break;
+
+ FIX_PCREL (BFD_RELOC_8);
+ FIX_PCREL (BFD_RELOC_16);
+ FIX_PCREL (BFD_RELOC_32);
+ FIX_PCREL (BFD_RELOC_64);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW0);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW0);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW1);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW1);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW2);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW2);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW3);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW3);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW2_LAST);
+ FIX_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW2_LAST);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW0);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW0);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW1);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW1);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X0_HW2_LAST);
+ FIX_PLT_PCREL (BFD_RELOC_TILEGX_IMM16_X1_HW2_LAST);
+
+#undef FIX_PCREL
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ if (fixP->fx_addsy != NULL)
+ {
+#ifdef OBJ_ELF
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_TILEGX_IMM8_X0_TLS_ADD:
+ case BFD_RELOC_TILEGX_IMM8_X1_TLS_ADD:
+ case BFD_RELOC_TILEGX_IMM8_Y0_TLS_ADD:
+ case BFD_RELOC_TILEGX_IMM8_Y1_TLS_ADD:
+ case BFD_RELOC_TILEGX_IMM8_X0_TLS_GD_ADD:
+ case BFD_RELOC_TILEGX_IMM8_X1_TLS_GD_ADD:
+ case BFD_RELOC_TILEGX_IMM8_Y0_TLS_GD_ADD:
+ case BFD_RELOC_TILEGX_IMM8_Y1_TLS_GD_ADD:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_TLS_GD:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_TLS_GD:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST_TLS_GD:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST_TLS_GD:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST_TLS_GD:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST_TLS_GD:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_TLS_IE:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_TLS_IE:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST_TLS_IE:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST_TLS_IE:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST_TLS_IE:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST_TLS_IE:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_TLS_LE:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_TLS_LE:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST_TLS_LE:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST_TLS_LE:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST_TLS_LE:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST_TLS_LE:
+ case BFD_RELOC_TILEGX_TLS_GD_CALL:
+ case BFD_RELOC_TILEGX_TLS_IE_LOAD:
+ case BFD_RELOC_TILEGX_TLS_DTPMOD64:
+ case BFD_RELOC_TILEGX_TLS_DTPOFF64:
+ case BFD_RELOC_TILEGX_TLS_TPOFF64:
+ case BFD_RELOC_TILEGX_TLS_DTPMOD32:
+ case BFD_RELOC_TILEGX_TLS_DTPOFF32:
+ case BFD_RELOC_TILEGX_TLS_TPOFF32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+#endif
+ return;
+ }
+
+ /* Apply hw0, etc. */
+ special = O_illegal;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_TILEGX_HW0:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_PLT_PCREL:
+ special = O_hw0;
+ break;
+
+ case BFD_RELOC_TILEGX_HW0_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL:
+ special = O_hw0_last;
+ break;
+
+ case BFD_RELOC_TILEGX_HW1:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_PLT_PCREL:
+ special = O_hw1;
+ break;
+
+ case BFD_RELOC_TILEGX_HW1_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL:
+ special = O_hw1_last;
+ break;
+
+ case BFD_RELOC_TILEGX_HW2:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW2:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW2:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW2_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW2_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW2_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW2_PLT_PCREL:
+ special = O_hw2;
+ break;
+
+ case BFD_RELOC_TILEGX_HW2_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW2_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW2_LAST:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW2_LAST_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW2_LAST_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL:
+ special = O_hw2_last;
+ break;
+
+ case BFD_RELOC_TILEGX_HW3:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW3:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW3:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW3_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW3_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X0_HW3_PLT_PCREL:
+ case BFD_RELOC_TILEGX_IMM16_X1_HW3_PLT_PCREL:
+ special = O_hw3;
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ if (special != O_illegal)
+ {
+ *valP = value = apply_special_operator (special, value,
+ fixP->fx_file, fixP->fx_line);
+ }
+
+ p = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ operand = fixP->tc_fix_data;
+ if (operand != NULL)
+ {
+ /* It's an instruction operand. */
+ tilegx_bundle_bits bits =
+ insert_operand (0, operand, value, fixP->fx_file, fixP->fx_line);
+
+ /* Note that we might either be writing out bits for a bundle
+ or a static network instruction, which are different sizes, so it's
+ important to stop touching memory once we run out of bits.
+ ORing in values is OK since we know the existing bits for
+ this operand are zero. */
+ for (; bits != 0; bits >>= 8)
+ *p++ |= (char)bits;
+ }
+ else
+ {
+ /* Some other kind of relocation. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ case BFD_RELOC_8_PCREL:
+ md_number_to_chars (p, value, 1);
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ md_number_to_chars (p, value, 2);
+ break;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ md_number_to_chars (p, value, 4);
+ break;
+
+ case BFD_RELOC_64:
+ case BFD_RELOC_64_PCREL:
+ md_number_to_chars (p, value, 8);
+ break;
+
+ default:
+ /* Leave it for the linker. */
+ return;
+ }
+ }
+
+ fixP->fx_done = 1;
+}
+
+
+/* Generate the BFD reloc to be stuck in the object file from the
+ fixup used internally in the assembler. */
+
+arelent *
+tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ gas_assert ((int) fixp->fx_r_type > 0);
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ {
+ as_fatal (_("internal error? cannot generate `%s' relocation (%d, %d)"),
+ bfd_get_reloc_code_name (fixp->fx_r_type),
+ fixp->fx_pcrel, reloc->howto->pc_relative);
+ }
+ gas_assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->addend = fixp->fx_offset;
+
+ return reloc;
+}
+
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_frag->fr_address + fixP->fx_where;
+}
+
+
+/* Return 1 if it's OK to adjust a reloc by replacing the symbol with
+ a section symbol plus some offset. */
+int
+tilegx_fix_adjustable (fixS *fix)
+{
+ /* Prevent all adjustments to global symbols */
+ if (S_IS_EXTERNAL (fix->fx_addsy) || S_IS_WEAK (fix->fx_addsy))
+ return 0;
+
+ return 1;
+}
+
+
+int
+tilegx_unrecognized_line (int ch)
+{
+ switch (ch)
+ {
+ case '{':
+ if (inside_bundle)
+ {
+ as_bad (_("Found '{' when already bundling."));
+ }
+ else
+ {
+ inside_bundle = 1;
+ current_bundle_index = 0;
+ }
+ return 1;
+
+ case '}':
+ if (!inside_bundle)
+ {
+ as_bad (_("Found '}' when not bundling."));
+ }
+ else
+ {
+ tilegx_flush_bundle ();
+ }
+
+ /* Allow '{' to follow on the same line. We also allow ";;", but that
+ happens automatically because ';' is an end of line marker. */
+ SKIP_WHITESPACE ();
+ if (input_line_pointer[0] == '{')
+ {
+ input_line_pointer++;
+ return tilegx_unrecognized_line ('{');
+ }
+
+ demand_empty_rest_of_line ();
+ return 1;
+
+ default:
+ break;
+ }
+
+ /* Not a valid line. */
+ return 0;
+}
+
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+tilegx_handle_align (fragS *fragp)
+{
+ addressT bytes, fix;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ /* Determine the bits for NOP. */
+ const struct tilegx_opcode *nop_opcode =
+ &tilegx_opcodes[TILEGX_OPC_NOP];
+ tilegx_bundle_bits nop =
+ ( nop_opcode->fixed_bit_values[TILEGX_PIPELINE_X0]
+ | nop_opcode->fixed_bit_values[TILEGX_PIPELINE_X1]);
+
+ if ((bytes & (TILEGX_BUNDLE_SIZE_IN_BYTES - 1)) != 0)
+ {
+ fix = bytes & (TILEGX_BUNDLE_SIZE_IN_BYTES - 1);
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ number_to_chars_littleendian (p, nop, 8);
+ fragp->fr_fix += fix;
+ fragp->fr_var = TILEGX_BUNDLE_SIZE_IN_BYTES;
+}
+
+/* Standard calling conventions leave the CFA at SP on entry. */
+void
+tilegx_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (54);
+}
+
+int
+tc_tilegx_regname_to_dw2regnum (char *regname)
+{
+ int i;
+ for (i = 0; i < TILEGX_NUM_REGISTERS; i++)
+ {
+ if (!strcmp (regname, tilegx_register_names[i]))
+ return i;
+ }
+
+ return -1;
+}
diff --git a/gas/config/tc-tilegx.h b/gas/config/tc-tilegx.h
new file mode 100644
index 0000000..6de89da
--- /dev/null
+++ b/gas/config/tc-tilegx.h
@@ -0,0 +1,93 @@
+/* tc-tilegx.h - Macros and type defines for a TILE-Gx chip.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef TC_TILEGX
+
+#include "opcode/tilegx.h"
+
+#define TC_TILEGX
+
+#ifndef TARGET_BYTES_BIG_ENDIAN
+#define TARGET_BYTES_BIG_ENDIAN 0
+#endif
+
+#define WORKING_DOT_WORD
+
+#define TARGET_ARCH bfd_arch_tilegx
+
+extern const char * tilegx_target_format (void);
+#define TARGET_FORMAT tilegx_target_format ()
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 8
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+#define HANDLE_ALIGN(fragp) tilegx_handle_align (fragp)
+extern void tilegx_handle_align (struct frag *);
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (7 + 8)
+
+struct tilegx_operand;
+#define TC_FIX_TYPE const struct tilegx_operand *
+
+/* Initialize the TC_FIX_TYPE field. */
+#define TC_INIT_FIX_DATA(FIX) \
+ FIX->tc_fix_data = 0
+
+extern void tilegx_cons_fix_new (struct frag *, int,
+ int, struct expressionS *);
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \
+ tilegx_cons_fix_new (FRAG, WHERE, NBYTES, EXP)
+
+extern int tilegx_parse_name (char *, expressionS *, char *);
+#define md_parse_name(name, e, m, nextP) tilegx_parse_name (name, e, nextP)
+
+extern int tilegx_fix_adjustable (struct fix *);
+#define tc_fix_adjustable(FIX) tilegx_fix_adjustable (FIX)
+
+extern int tilegx_unrecognized_line (int);
+#define tc_unrecognized_line(ch) tilegx_unrecognized_line (ch)
+
+/* Values passed to md_apply_fix3 don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_convert_frag(b,s,f) \
+ as_fatal ("tilegx convert_frag called")
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal ("tilegx estimate_size_before_relax called"),1)
+#define md_operand(x)
+
+#define md_section_align(seg,size) (size)
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions tilegx_cfi_frame_initial_instructions
+extern void tilegx_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_tilegx_regname_to_dw2regnum
+extern int tc_tilegx_regname_to_dw2regnum (char *);
+
+extern int tilegx_cie_data_alignment;
+
+#define DWARF2_DEFAULT_RETURN_COLUMN 55
+#define DWARF2_CIE_DATA_ALIGNMENT tilegx_cie_data_alignment
+
+#endif /* TC_TILEGX */
diff --git a/gas/config/tc-tilepro.c b/gas/config/tc-tilepro.c
new file mode 100644
index 0000000..8a378c0
--- /dev/null
+++ b/gas/config/tc-tilepro.c
@@ -0,0 +1,1677 @@
+/* tc-tilepro.c -- Assemble for a TILEPro chip.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include "struc-symbol.h"
+#include "subsegs.h"
+
+#include "elf/tilepro.h"
+#include "opcode/tilepro.h"
+
+#include "dwarf2dbg.h"
+#include "dw2gencfi.h"
+
+#include "safe-ctype.h"
+
+
+/* Special registers. */
+#define TREG_IDN0 57
+#define TREG_IDN1 58
+#define TREG_UDN0 59
+#define TREG_UDN1 60
+#define TREG_UDN2 61
+#define TREG_UDN3 62
+#define TREG_ZERO 63
+
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = "#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+const char *md_shortopts = "VQ:";
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+
+ /* -V: SVR4 argument to print version ID. */
+ case 'V':
+ print_version_id ();
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+ -Q ignored\n\
+ -V print assembler version number\n"));
+}
+
+/* Extra expression types. */
+
+#define O_lo16 O_md1
+#define O_hi16 O_md2
+#define O_ha16 O_md3
+#define O_got O_md4
+#define O_got_lo16 O_md5
+#define O_got_hi16 O_md6
+#define O_got_ha16 O_md7
+#define O_plt O_md8
+#define O_tls_gd O_md9
+#define O_tls_gd_lo16 O_md10
+#define O_tls_gd_hi16 O_md11
+#define O_tls_gd_ha16 O_md12
+#define O_tls_ie O_md13
+#define O_tls_ie_lo16 O_md14
+#define O_tls_ie_hi16 O_md15
+#define O_tls_ie_ha16 O_md16
+#define O_tls_le O_md17
+#define O_tls_le_lo16 O_md18
+#define O_tls_le_hi16 O_md19
+#define O_tls_le_ha16 O_md20
+#define O_tls_gd_call O_md21
+#define O_tls_gd_add O_md22
+#define O_tls_ie_load O_md23
+
+static struct hash_control *special_operator_hash;
+
+/* Hash tables for instruction mnemonic lookup. */
+static struct hash_control *op_hash;
+
+/* Hash table for spr lookup. */
+static struct hash_control *spr_hash;
+
+/* True temporarily while parsing an SPR expression. This changes the
+ * namespace to include SPR names. */
+static int parsing_spr;
+
+/* Are we currently inside `{ ... }'? */
+static int inside_bundle;
+
+struct tilepro_instruction
+{
+ const struct tilepro_opcode *opcode;
+ tilepro_pipeline pipe;
+ expressionS operand_values[TILEPRO_MAX_OPERANDS];
+};
+
+/* This keeps track of the current bundle being built up. */
+static struct tilepro_instruction
+current_bundle[TILEPRO_MAX_INSTRUCTIONS_PER_BUNDLE];
+
+/* Index in current_bundle for the next instruction to parse. */
+static int current_bundle_index;
+
+/* Allow 'r63' in addition to 'zero', etc. Normally we disallow this as
+ 'zero' is not a real register, so using it accidentally would be a
+ nasty bug. For other registers, such as 'sp', code using multiple names
+ for the same physical register is excessively confusing.
+
+ The '.require_canonical_reg_names' pseudo-op turns this error on,
+ and the '.no_require_canonical_reg_names' pseudo-op turns this off.
+ By default the error is on. */
+static int require_canonical_reg_names;
+
+/* Allow bundles that do undefined or suspicious things like write
+ two different values to the same register at the same time.
+
+ The '.no_allow_suspicious_bundles' pseudo-op turns this error on,
+ and the '.allow_suspicious_bundles' pseudo-op turns this off. */
+static int allow_suspicious_bundles;
+
+
+/* A hash table of main processor registers, mapping each register name
+ to its index.
+
+ Furthermore, if the register number is greater than the number
+ of registers for that processor, the user used an illegal alias
+ for that register (e.g. r63 instead of zero), so we should generate
+ a warning. The attempted register number can be found by clearing
+ NONCANONICAL_REG_NAME_FLAG. */
+static struct hash_control *main_reg_hash;
+
+
+/* We cannot unambiguously store a 0 in a hash table and look it up,
+ so we OR in this flag to every canonical register. */
+#define CANONICAL_REG_NAME_FLAG 0x1000
+
+/* By default we disallow register aliases like r63, but we record
+ them in the hash table in case the .no_require_canonical_reg_names
+ directive is used. Noncanonical names have this value added to them. */
+#define NONCANONICAL_REG_NAME_FLAG 0x2000
+
+/* Discards flags for register hash table entries and returns the
+ reg number. */
+#define EXTRACT_REGNO(p) ((p) & 63)
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc., that the MD part of the assembler will
+ need. */
+void
+md_begin (void)
+{
+ const struct tilepro_opcode *op;
+ int i;
+
+ /* Guarantee text section is aligned. */
+ bfd_set_section_alignment (stdoutput, text_section,
+ TILEPRO_LOG2_BUNDLE_ALIGNMENT_IN_BYTES);
+
+ require_canonical_reg_names = 1;
+ allow_suspicious_bundles = 0;
+ current_bundle_index = 0;
+ inside_bundle = 0;
+
+ /* Initialize special operator hash table. */
+ special_operator_hash = hash_new ();
+#define INSERT_SPECIAL_OP(name) \
+ hash_insert (special_operator_hash, #name, (void *)O_##name)
+
+ INSERT_SPECIAL_OP(lo16);
+ INSERT_SPECIAL_OP(hi16);
+ INSERT_SPECIAL_OP(ha16);
+ INSERT_SPECIAL_OP(got);
+ INSERT_SPECIAL_OP(got_lo16);
+ INSERT_SPECIAL_OP(got_hi16);
+ INSERT_SPECIAL_OP(got_ha16);
+ INSERT_SPECIAL_OP(plt);
+ INSERT_SPECIAL_OP(tls_gd);
+ INSERT_SPECIAL_OP(tls_gd_lo16);
+ INSERT_SPECIAL_OP(tls_gd_hi16);
+ INSERT_SPECIAL_OP(tls_gd_ha16);
+ INSERT_SPECIAL_OP(tls_ie);
+ INSERT_SPECIAL_OP(tls_ie_lo16);
+ INSERT_SPECIAL_OP(tls_ie_hi16);
+ INSERT_SPECIAL_OP(tls_ie_ha16);
+ INSERT_SPECIAL_OP(tls_le);
+ INSERT_SPECIAL_OP(tls_le_lo16);
+ INSERT_SPECIAL_OP(tls_le_hi16);
+ INSERT_SPECIAL_OP(tls_le_ha16);
+ INSERT_SPECIAL_OP(tls_gd_call);
+ INSERT_SPECIAL_OP(tls_gd_add);
+ INSERT_SPECIAL_OP(tls_ie_load);
+#undef INSERT_SPECIAL_OP
+
+ /* Initialize op_hash hash table. */
+ op_hash = hash_new ();
+ for (op = &tilepro_opcodes[0]; op->name != NULL; op++)
+ {
+ const char *hash_err = hash_insert (op_hash, op->name, (void *)op);
+ if (hash_err != NULL)
+ {
+ as_fatal (_("Internal Error: Can't hash %s: %s"),
+ op->name, hash_err);
+ }
+ }
+
+ /* Initialize the spr hash table. */
+ parsing_spr = 0;
+ spr_hash = hash_new ();
+ for (i = 0; i < tilepro_num_sprs; i++)
+ hash_insert (spr_hash, tilepro_sprs[i].name,
+ (void *) &tilepro_sprs[i]);
+
+ /* Set up the main_reg_hash table. We use this instead of
+ * creating a symbol in the register section to avoid ambiguities
+ * with labels that have the same names as registers. */
+ main_reg_hash = hash_new ();
+ for (i = 0; i < TILEPRO_NUM_REGISTERS; i++)
+ {
+ char buf[64];
+
+ hash_insert (main_reg_hash, tilepro_register_names[i],
+ (void *) (long)(i | CANONICAL_REG_NAME_FLAG));
+
+ /* See if we should insert a noncanonical alias, like r63. */
+ sprintf (buf, "r%d", i);
+ if (strcmp (buf, tilepro_register_names[i]) != 0)
+ hash_insert (main_reg_hash, xstrdup (buf),
+ (void *) (long)(i | NONCANONICAL_REG_NAME_FLAG));
+ }
+
+ /* Insert obsolete backwards-compatibility register names. */
+ hash_insert (main_reg_hash, "io0",
+ (void *) (long) (TREG_IDN0 | CANONICAL_REG_NAME_FLAG));
+ hash_insert (main_reg_hash, "io1",
+ (void *) (long) (TREG_IDN1 | CANONICAL_REG_NAME_FLAG));
+ hash_insert (main_reg_hash, "us0",
+ (void *) (long) (TREG_UDN0 | CANONICAL_REG_NAME_FLAG));
+ hash_insert (main_reg_hash, "us1",
+ (void *) (long) (TREG_UDN1 | CANONICAL_REG_NAME_FLAG));
+ hash_insert (main_reg_hash, "us2",
+ (void *) (long) (TREG_UDN2 | CANONICAL_REG_NAME_FLAG));
+ hash_insert (main_reg_hash, "us3",
+ (void *) (long) (TREG_UDN3 | CANONICAL_REG_NAME_FLAG));
+
+}
+
+
+#define BUNDLE_TEMPLATE_MASK(p0, p1, p2) \
+ ((p0) | ((p1) << 8) | ((p2) << 16))
+#define BUNDLE_TEMPLATE(p0, p1, p2) \
+ { { (p0), (p1), (p2) }, \
+ BUNDLE_TEMPLATE_MASK(1 << (p0), 1 << (p1), (1 << (p2))) \
+ }
+
+#define NO_PIPELINE TILEPRO_NUM_PIPELINE_ENCODINGS
+
+struct bundle_template
+{
+ tilepro_pipeline pipe[TILEPRO_MAX_INSTRUCTIONS_PER_BUNDLE];
+ unsigned int pipe_mask;
+};
+
+static const struct bundle_template bundle_templates[] =
+{
+ /* In Y format we must always have something in Y2, since it has
+ * no fnop, so this conveys that Y2 must always be used. */
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y0, TILEPRO_PIPELINE_Y2, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y1, TILEPRO_PIPELINE_Y2, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y2, TILEPRO_PIPELINE_Y0, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y2, TILEPRO_PIPELINE_Y1, NO_PIPELINE),
+
+ /* Y format has three instructions. */
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y0, TILEPRO_PIPELINE_Y1, TILEPRO_PIPELINE_Y2),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y0, TILEPRO_PIPELINE_Y2, TILEPRO_PIPELINE_Y1),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y1, TILEPRO_PIPELINE_Y0, TILEPRO_PIPELINE_Y2),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y1, TILEPRO_PIPELINE_Y2, TILEPRO_PIPELINE_Y0),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y2, TILEPRO_PIPELINE_Y0, TILEPRO_PIPELINE_Y1),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_Y2, TILEPRO_PIPELINE_Y1, TILEPRO_PIPELINE_Y0),
+
+ /* X format has only two instructions. */
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_X0, TILEPRO_PIPELINE_X1, NO_PIPELINE),
+ BUNDLE_TEMPLATE(TILEPRO_PIPELINE_X1, TILEPRO_PIPELINE_X0, NO_PIPELINE)
+};
+
+
+static void
+prepend_nop_to_bundle (tilepro_mnemonic mnemonic)
+{
+ memmove (&current_bundle[1], &current_bundle[0],
+ current_bundle_index * sizeof current_bundle[0]);
+ current_bundle[0].opcode = &tilepro_opcodes[mnemonic];
+ ++current_bundle_index;
+}
+
+
+static tilepro_bundle_bits
+insert_operand (tilepro_bundle_bits bits,
+ const struct tilepro_operand *operand,
+ int operand_value,
+ char *file,
+ unsigned lineno)
+{
+ /* Range-check the immediate. */
+ int num_bits = operand->num_bits;
+
+ operand_value >>= operand->rightshift;
+
+ if (bfd_check_overflow (operand->is_signed
+ ? complain_overflow_signed
+ : complain_overflow_unsigned,
+ num_bits,
+ 0,
+ bfd_arch_bits_per_address (stdoutput),
+ operand_value)
+ != bfd_reloc_ok)
+ {
+ offsetT min, max;
+ if (operand->is_signed)
+ {
+ min = -(1 << (num_bits - 1));
+ max = (1 << (num_bits - 1)) - 1;
+ }
+ else
+ {
+ min = 0;
+ max = (1 << num_bits) - 1;
+ }
+ as_bad_value_out_of_range (_("operand"), operand_value, min, max,
+ file, lineno);
+ }
+
+ /* Write out the bits for the immediate. */
+ return bits | operand->insert (operand_value);
+}
+
+
+static int
+apply_special_operator (operatorT op, int num)
+{
+ switch (op)
+ {
+ case O_lo16:
+ return (signed short)num;
+
+ case O_hi16:
+ return (signed short)(num >> 16);
+
+ case O_ha16:
+ return (signed short)((num + 0x8000) >> 16);
+
+ default:
+ abort ();
+ }
+}
+
+
+static tilepro_bundle_bits
+emit_tilepro_instruction (tilepro_bundle_bits bits,
+ int num_operands,
+ const unsigned char *operands,
+ expressionS *operand_values,
+ char *bundle_start)
+{
+ int i;
+
+ for (i = 0; i < num_operands; i++)
+ {
+ const struct tilepro_operand *operand =
+ &tilepro_operands[operands[i]];
+ expressionS *operand_exp = &operand_values[i];
+ int is_pc_relative = operand->is_pc_relative;
+
+ if (operand_exp->X_op == O_register
+ || (operand_exp->X_op == O_constant && !is_pc_relative))
+ {
+ /* We know what the bits are right now, so insert them. */
+ bits = insert_operand (bits, operand, operand_exp->X_add_number,
+ NULL, 0);
+ }
+ else
+ {
+ bfd_reloc_code_real_type reloc = operand->default_reloc;
+ expressionS subexp;
+ int die = 0, use_subexp = 0, require_symbol = 0;
+ fixS *fixP;
+
+ /* Take an expression like hi16(x) and turn it into x with
+ a different reloc type. */
+ switch (operand_exp->X_op)
+ {
+#define HANDLE_OP16(suffix) \
+ switch (reloc) \
+ { \
+ case BFD_RELOC_TILEPRO_IMM16_X0: \
+ reloc = BFD_RELOC_TILEPRO_IMM16_X0_##suffix; \
+ break; \
+ case BFD_RELOC_TILEPRO_IMM16_X1: \
+ reloc = BFD_RELOC_TILEPRO_IMM16_X1_##suffix; \
+ break; \
+ default: \
+ die = 1; \
+ break; \
+ } \
+ use_subexp = 1
+
+ case O_lo16:
+ HANDLE_OP16 (LO);
+ break;
+
+ case O_hi16:
+ HANDLE_OP16 (HI);
+ break;
+
+ case O_ha16:
+ HANDLE_OP16 (HA);
+ break;
+
+ case O_got:
+ HANDLE_OP16 (GOT);
+ require_symbol = 1;
+ break;
+
+ case O_got_lo16:
+ HANDLE_OP16 (GOT_LO);
+ require_symbol = 1;
+ break;
+
+ case O_got_hi16:
+ HANDLE_OP16 (GOT_HI);
+ require_symbol = 1;
+ break;
+
+ case O_got_ha16:
+ HANDLE_OP16 (GOT_HA);
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd:
+ HANDLE_OP16 (TLS_GD);
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_lo16:
+ HANDLE_OP16 (TLS_GD_LO);
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_hi16:
+ HANDLE_OP16 (TLS_GD_HI);
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_ha16:
+ HANDLE_OP16 (TLS_GD_HA);
+ require_symbol = 1;
+ break;
+
+ case O_tls_ie:
+ HANDLE_OP16 (TLS_IE);
+ require_symbol = 1;
+ break;
+
+ case O_tls_ie_lo16:
+ HANDLE_OP16 (TLS_IE_LO);
+ require_symbol = 1;
+ break;
+
+ case O_tls_ie_hi16:
+ HANDLE_OP16 (TLS_IE_HI);
+ require_symbol = 1;
+ break;
+
+ case O_tls_ie_ha16:
+ HANDLE_OP16 (TLS_IE_HA);
+ require_symbol = 1;
+ break;
+
+ case O_tls_le:
+ HANDLE_OP16 (TLS_LE);
+ require_symbol = 1;
+ break;
+
+ case O_tls_le_lo16:
+ HANDLE_OP16 (TLS_LE_LO);
+ require_symbol = 1;
+ break;
+
+ case O_tls_le_hi16:
+ HANDLE_OP16 (TLS_LE_HI);
+ require_symbol = 1;
+ break;
+
+ case O_tls_le_ha16:
+ HANDLE_OP16 (TLS_LE_HA);
+ require_symbol = 1;
+ break;
+
+#undef HANDLE_OP16
+
+ case O_plt:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEPRO_JOFFLONG_X1:
+ reloc = BFD_RELOC_TILEPRO_JOFFLONG_X1_PLT;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_call:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEPRO_JOFFLONG_X1:
+ reloc = BFD_RELOC_TILEPRO_TLS_GD_CALL;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_gd_add:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEPRO_IMM8_X0:
+ reloc = BFD_RELOC_TILEPRO_IMM8_X0_TLS_GD_ADD;
+ break;
+ case BFD_RELOC_TILEPRO_IMM8_X1:
+ reloc = BFD_RELOC_TILEPRO_IMM8_X1_TLS_GD_ADD;
+ break;
+ case BFD_RELOC_TILEPRO_IMM8_Y0:
+ reloc = BFD_RELOC_TILEPRO_IMM8_Y0_TLS_GD_ADD;
+ break;
+ case BFD_RELOC_TILEPRO_IMM8_Y1:
+ reloc = BFD_RELOC_TILEPRO_IMM8_Y1_TLS_GD_ADD;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ case O_tls_ie_load:
+ switch (reloc)
+ {
+ case BFD_RELOC_TILEPRO_IMM8_X1:
+ reloc = BFD_RELOC_TILEPRO_TLS_IE_LOAD;
+ break;
+ default:
+ die = 1;
+ break;
+ }
+ use_subexp = 1;
+ require_symbol = 1;
+ break;
+
+ default:
+ /* Do nothing. */
+ break;
+ }
+
+ if (die)
+ {
+ as_bad (_("Invalid operator for operand."));
+ }
+ else if (use_subexp)
+ {
+ /* Now that we've changed the reloc, change ha16(x) into x,
+ etc. */
+
+ if (!operand_exp->X_add_symbol->sy_flags.sy_local_symbol
+ && operand_exp->X_add_symbol->sy_value.X_md)
+ {
+ /* HACK: We used X_md to mark this symbol as a fake wrapper
+ around a real expression. To unwrap it, we just grab its
+ value here. */
+ operand_exp = &operand_exp->X_add_symbol->sy_value;
+
+ if (require_symbol)
+ {
+ /* Look at the expression, and reject it if it's not a
+ plain symbol. */
+ if (operand_exp->X_op != O_symbol
+ || operand_exp->X_add_number != 0)
+ as_bad (_("Operator may only be applied to symbols."));
+ }
+ }
+ else
+ {
+ /* The value of this expression is an actual symbol, so
+ turn that into an expression. */
+ memset (&subexp, 0, sizeof subexp);
+ subexp.X_op = O_symbol;
+ subexp.X_add_symbol = operand_exp->X_add_symbol;
+ operand_exp = &subexp;
+ }
+ }
+
+ /* Create a fixup to handle this later. */
+ fixP = fix_new_exp (frag_now,
+ bundle_start - frag_now->fr_literal,
+ (operand->num_bits + 7) >> 3,
+ operand_exp,
+ is_pc_relative,
+ reloc);
+ fixP->tc_fix_data = operand;
+
+ /* Don't do overflow checking if we are applying a function like
+ ha16. */
+ fixP->fx_no_overflow |= use_subexp;
+ }
+ }
+ return bits;
+}
+
+
+/* Detects and complains if two instructions in current_bundle write
+ to the same register, either implicitly or explicitly, or if a
+ read-only register is written. */
+static void
+check_illegal_reg_writes (void)
+{
+ BFD_HOST_U_64_BIT all_regs_written = 0;
+ int j;
+
+ for (j = 0; j < current_bundle_index; j++)
+ {
+ const struct tilepro_instruction *instr = &current_bundle[j];
+ int k;
+ BFD_HOST_U_64_BIT regs =
+ ((BFD_HOST_U_64_BIT)1) << instr->opcode->implicitly_written_register;
+ BFD_HOST_U_64_BIT conflict;
+
+ for (k = 0; k < instr->opcode->num_operands; k++)
+ {
+ const struct tilepro_operand *operand =
+ &tilepro_operands[instr->opcode->operands[instr->pipe][k]];
+
+ if (operand->is_dest_reg)
+ {
+ int regno = instr->operand_values[k].X_add_number;
+ BFD_HOST_U_64_BIT mask = ((BFD_HOST_U_64_BIT)1) << regno;
+
+ if ((mask & ( (((BFD_HOST_U_64_BIT)1) << TREG_IDN1)
+ | (((BFD_HOST_U_64_BIT)1) << TREG_UDN1)
+ | (((BFD_HOST_U_64_BIT)1) << TREG_UDN2)
+ | (((BFD_HOST_U_64_BIT)1) << TREG_UDN3))) != 0
+ && !allow_suspicious_bundles)
+ {
+ as_bad (_("Writes to register '%s' are not allowed."),
+ tilepro_register_names[regno]);
+ }
+
+ regs |= mask;
+ }
+ }
+
+ /* Writing to the zero register doesn't count. */
+ regs &= ~(((BFD_HOST_U_64_BIT)1) << TREG_ZERO);
+
+ conflict = all_regs_written & regs;
+ if (conflict != 0 && !allow_suspicious_bundles)
+ {
+ /* Find which register caused the conflict. */
+ const char *conflicting_reg_name = "???";
+ int i;
+
+ for (i = 0; i < TILEPRO_NUM_REGISTERS; i++)
+ {
+ if (((conflict >> i) & 1) != 0)
+ {
+ conflicting_reg_name = tilepro_register_names[i];
+ break;
+ }
+ }
+
+ as_bad (_("Two instructions in the same bundle both write "
+ "to register %s, which is not allowed."),
+ conflicting_reg_name);
+ }
+
+ all_regs_written |= regs;
+ }
+}
+
+
+static void
+tilepro_flush_bundle (void)
+{
+ unsigned i;
+ int j, addr_mod;
+ unsigned compatible_pipes;
+ const struct bundle_template *match;
+ char *f;
+
+ inside_bundle = 0;
+
+ switch (current_bundle_index)
+ {
+ case 0:
+ /* No instructions. */
+ return;
+ case 1:
+ if (current_bundle[0].opcode->can_bundle)
+ {
+ /* Simplify later logic by adding an explicit fnop. */
+ prepend_nop_to_bundle (TILEPRO_OPC_FNOP);
+ }
+ else
+ {
+ /* This instruction cannot be bundled with anything else.
+ Prepend an explicit 'nop', rather than an 'fnop', because
+ fnops can be replaced by later binary-processing tools
+ while nops cannot. */
+ prepend_nop_to_bundle (TILEPRO_OPC_NOP);
+ }
+ break;
+ default:
+ if (!allow_suspicious_bundles)
+ {
+ /* Make sure all instructions can be bundled with other
+ instructions. */
+ const struct tilepro_opcode *cannot_bundle = NULL;
+ bfd_boolean seen_non_nop = FALSE;
+
+ for (j = 0; j < current_bundle_index; j++)
+ {
+ const struct tilepro_opcode *op = current_bundle[j].opcode;
+
+ if (!op->can_bundle && cannot_bundle == NULL)
+ cannot_bundle = op;
+ else if (op->mnemonic != TILEPRO_OPC_NOP
+ && op->mnemonic != TILEPRO_OPC_INFO
+ && op->mnemonic != TILEPRO_OPC_INFOL)
+ seen_non_nop = TRUE;
+ }
+
+ if (cannot_bundle != NULL && seen_non_nop)
+ {
+ current_bundle_index = 0;
+ as_bad (_("'%s' may not be bundled with other instructions."),
+ cannot_bundle->name);
+ return;
+ }
+ }
+ break;
+ }
+
+ compatible_pipes =
+ BUNDLE_TEMPLATE_MASK(current_bundle[0].opcode->pipes,
+ current_bundle[1].opcode->pipes,
+ (current_bundle_index == 3
+ ? current_bundle[2].opcode->pipes
+ : (1 << NO_PIPELINE)));
+
+ /* Find a template that works, if any. */
+ match = NULL;
+ for (i = 0; i < sizeof bundle_templates / sizeof bundle_templates[0]; i++)
+ {
+ const struct bundle_template *b = &bundle_templates[i];
+ if ((b->pipe_mask & compatible_pipes) == b->pipe_mask)
+ {
+ match = b;
+ break;
+ }
+ }
+
+ if (match == NULL)
+ {
+ current_bundle_index = 0;
+ as_bad (_("Invalid combination of instructions for bundle."));
+ return;
+ }
+
+ /* If the section seems to have no alignment set yet, go ahead and
+ make it large enough to hold code. */
+ if (bfd_get_section_alignment (stdoutput, now_seg) == 0)
+ bfd_set_section_alignment (stdoutput, now_seg,
+ TILEPRO_LOG2_BUNDLE_ALIGNMENT_IN_BYTES);
+
+ for (j = 0; j < current_bundle_index; j++)
+ current_bundle[j].pipe = match->pipe[j];
+
+ if (current_bundle_index == 2 && !tilepro_is_x_pipeline(match->pipe[0]))
+ {
+ /* We are in Y mode with only two instructions, so add an FNOP. */
+ prepend_nop_to_bundle (TILEPRO_OPC_FNOP);
+
+ /* Figure out what pipe the fnop must be in via arithmetic.
+ * p0 + p1 + p2 must sum to the sum of TILEPRO_PIPELINE_Y[012]. */
+ current_bundle[0].pipe =
+ (tilepro_pipeline)((TILEPRO_PIPELINE_Y0
+ + TILEPRO_PIPELINE_Y1
+ + TILEPRO_PIPELINE_Y2) -
+ (current_bundle[1].pipe + current_bundle[2].pipe));
+ }
+
+ check_illegal_reg_writes ();
+
+ f = frag_more (TILEPRO_BUNDLE_SIZE_IN_BYTES);
+
+ /* Check to see if this bundle is at an offset that is a multiple of 8-bytes
+ from the start of the frag. */
+ addr_mod = frag_now_fix () & (TILEPRO_BUNDLE_ALIGNMENT_IN_BYTES - 1);
+ if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+ as_bad (_("instruction address is not a multiple of 8"));
+ frag_now->insn_addr = addr_mod;
+ frag_now->has_code = 1;
+
+ tilepro_bundle_bits bits = 0;
+ for (j = 0; j < current_bundle_index; j++)
+ {
+ struct tilepro_instruction *instr = &current_bundle[j];
+ tilepro_pipeline pipeline = instr->pipe;
+ const struct tilepro_opcode *opcode = instr->opcode;
+
+ bits |= emit_tilepro_instruction (opcode->fixed_bit_values[pipeline],
+ opcode->num_operands,
+ &opcode->operands[pipeline][0],
+ instr->operand_values,
+ f);
+ }
+
+ number_to_chars_littleendian (f, (unsigned int)bits, 4);
+ number_to_chars_littleendian (f + 4, (unsigned int)(bits >> 32), 4);
+ current_bundle_index = 0;
+
+ /* Emit DWARF2 debugging information. */
+ dwarf2_emit_insn (TILEPRO_BUNDLE_SIZE_IN_BYTES);
+}
+
+
+/* Extend the expression parser to handle hi16(label), etc.
+ as well as SPR names when in the context of parsing an SPR. */
+int
+tilepro_parse_name (char *name, expressionS *e, char *nextcharP)
+{
+ operatorT op = O_illegal;
+
+ if (parsing_spr)
+ {
+ void *val = hash_find (spr_hash, name);
+ if (val == NULL)
+ return 0;
+
+ memset (e, 0, sizeof *e);
+ e->X_op = O_constant;
+ e->X_add_number = ((const struct tilepro_spr *)val)->number;
+ return 1;
+ }
+
+ if (*nextcharP != '(')
+ {
+ /* hi16, etc. not followed by a paren is just a label with that
+ name. */
+ return 0;
+ }
+ else
+ {
+ /* Look up the operator in our table. */
+ void *val = hash_find (special_operator_hash, name);
+ if (val == 0)
+ return 0;
+ op = (operatorT)(long)val;
+ }
+
+ /* Restore old '(' and skip it. */
+ *input_line_pointer = '(';
+ ++input_line_pointer;
+
+ expression (e);
+
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("Missing ')'"));
+ *nextcharP = *input_line_pointer;
+ return 0;
+ }
+ /* Skip ')'. */
+ ++input_line_pointer;
+
+ if (e->X_op == O_register || e->X_op == O_absent)
+ {
+ as_bad (_("Invalid expression."));
+ e->X_op = O_constant;
+ e->X_add_number = 0;
+ }
+ else
+ {
+ /* Wrap subexpression with a unary operator. */
+ symbolS *sym = make_expr_symbol (e);
+
+ if (sym != e->X_add_symbol)
+ {
+ /* HACK: mark this symbol as a temporary wrapper around a proper
+ expression, so we can unwrap it later once we have communicated
+ the relocation type. */
+ sym->sy_value.X_md = 1;
+ }
+
+ memset (e, 0, sizeof *e);
+ e->X_op = op;
+ e->X_add_symbol = sym;
+ e->X_add_number = 0;
+ }
+
+ *nextcharP = *input_line_pointer;
+ return 1;
+}
+
+
+/* Parses an expression which must be a register name. */
+
+static void
+parse_reg_expression (expressionS* expression)
+{
+ /* Zero everything to make sure we don't miss any flags. */
+ memset (expression, 0, sizeof *expression);
+
+ char* regname = input_line_pointer;
+ char terminating_char = get_symbol_end ();
+
+ void* pval = hash_find (main_reg_hash, regname);
+
+ if (pval == NULL)
+ as_bad (_("Expected register, got '%s'."), regname);
+
+ int regno_and_flags = (int)(size_t)pval;
+ int regno = EXTRACT_REGNO(regno_and_flags);
+
+ if ((regno_and_flags & NONCANONICAL_REG_NAME_FLAG)
+ && require_canonical_reg_names)
+ as_warn (_("Found use of non-canonical register name %s; "
+ "use %s instead."),
+ regname, tilepro_register_names[regno]);
+
+ /* Restore the old character following the register name. */
+ *input_line_pointer = terminating_char;
+
+ /* Fill in the expression fields to indicate it's a register. */
+ expression->X_op = O_register;
+ expression->X_add_number = regno;
+}
+
+
+/* Parses and type-checks comma-separated operands in input_line_pointer. */
+static void
+parse_operands (const char *opcode_name,
+ const unsigned char *operands,
+ int num_operands,
+ expressionS *operand_values)
+{
+ int i;
+
+ memset (operand_values, 0, num_operands * sizeof operand_values[0]);
+
+ SKIP_WHITESPACE ();
+ for (i = 0; i < num_operands; i++)
+ {
+ tilepro_operand_type type = tilepro_operands[operands[i]].type;
+
+ SKIP_WHITESPACE ();
+
+ if (type == TILEPRO_OP_TYPE_REGISTER)
+ {
+ parse_reg_expression (&operand_values[i]);
+ }
+ else if (*input_line_pointer == '}')
+ {
+ operand_values[i].X_op = O_absent;
+ }
+ else if (type == TILEPRO_OP_TYPE_SPR)
+ {
+ /* Modify the expression parser to add SPRs to the namespace. */
+ parsing_spr = 1;
+ expression (&operand_values[i]);
+ parsing_spr = 0;
+ }
+ else
+ {
+ expression (&operand_values[i]);
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (i + 1 < num_operands)
+ {
+ int separator = (unsigned char)*input_line_pointer++;
+
+ if (is_end_of_line[separator] || (separator == '}'))
+ {
+ as_bad (_("Too few operands to '%s'."), opcode_name);
+ return;
+ }
+ else if (separator != ',')
+ {
+ as_bad (_("Unexpected character '%c' after operand %d to %s."),
+ (char)separator, i + 1, opcode_name);
+ return;
+ }
+ }
+
+ /* Arbitrarily use the first valid pipe to get the operand type,
+ since they are all the same. */
+ switch (tilepro_operands[operands[i]].type)
+ {
+ case TILEPRO_OP_TYPE_REGISTER:
+ /* Handled in parse_reg_expression already. */
+ break;
+ case TILEPRO_OP_TYPE_SPR:
+ /* Fall through */
+ case TILEPRO_OP_TYPE_IMMEDIATE:
+ /* Fall through */
+ case TILEPRO_OP_TYPE_ADDRESS:
+ if ( operand_values[i].X_op == O_register
+ || operand_values[i].X_op == O_illegal
+ || operand_values[i].X_op == O_absent)
+ as_bad (_("Expected immediate expression"));
+ break;
+ default:
+ abort ();
+ }
+ }
+
+ if (!is_end_of_line[(unsigned char)*input_line_pointer])
+ {
+ switch (*input_line_pointer)
+ {
+ case '}':
+ if (!inside_bundle)
+ as_bad (_("Found '}' when not bundling."));
+ ++input_line_pointer;
+ inside_bundle = 0;
+ demand_empty_rest_of_line ();
+ break;
+
+ case ',':
+ as_bad (_("Too many operands"));
+ break;
+
+ default:
+ /* Use default error for unrecognized garbage. */
+ demand_empty_rest_of_line ();
+ break;
+ }
+ }
+}
+
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+void
+md_assemble (char *str)
+{
+ char old_char;
+ size_t opname_len;
+ char *old_input_line_pointer;
+ const struct tilepro_opcode *op;
+ int first_pipe;
+
+ /* Split off the opcode and look it up. */
+ opname_len = strcspn (str, " {}");
+ old_char = str[opname_len];
+ str[opname_len] = '\0';
+
+ op = hash_find(op_hash, str);
+ str[opname_len] = old_char;
+ if (op == NULL)
+ {
+ as_bad (_("Unknown opcode `%.*s'."), (int)opname_len, str);
+ return;
+ }
+
+ /* Prepare to parse the operands. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = str + opname_len;
+ SKIP_WHITESPACE ();
+
+ if (current_bundle_index == TILEPRO_MAX_INSTRUCTIONS_PER_BUNDLE)
+ {
+ as_bad (_("Too many instructions for bundle."));
+ tilepro_flush_bundle ();
+ }
+
+ /* Make sure we have room for the upcoming bundle before we
+ create any fixups. Otherwise if we have to switch to a new
+ frag the fixup dot_value fields will be wrong. */
+ frag_grow (TILEPRO_BUNDLE_SIZE_IN_BYTES);
+
+ /* Find a valid pipe for this opcode. */
+ for (first_pipe = 0; (op->pipes & (1 << first_pipe)) == 0; first_pipe++)
+ ;
+
+ /* Call the function that assembles this instruction. */
+ current_bundle[current_bundle_index].opcode = op;
+ parse_operands (op->name,
+ &op->operands[first_pipe][0],
+ op->num_operands,
+ current_bundle[current_bundle_index].operand_values);
+ ++current_bundle_index;
+
+ /* Restore the saved value of input_line_pointer. */
+ input_line_pointer = old_input_line_pointer;
+
+ /* If we weren't inside curly braces, go ahead and emit
+ this lone instruction as a bundle right now. */
+ if (!inside_bundle)
+ tilepro_flush_bundle ();
+}
+
+static void
+s_require_canonical_reg_names (int require)
+{
+ demand_empty_rest_of_line ();
+ require_canonical_reg_names = require;
+}
+
+static void
+s_allow_suspicious_bundles (int allow)
+{
+ demand_empty_rest_of_line ();
+ allow_suspicious_bundles = allow;
+}
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"align", s_align_bytes, 0}, /* Defaulting is invalid (0). */
+ {"word", cons, 4},
+ {"require_canonical_reg_names", s_require_canonical_reg_names, 1 },
+ {"no_require_canonical_reg_names", s_require_canonical_reg_names, 0 },
+ {"allow_suspicious_bundles", s_allow_suspicious_bundles, 1 },
+ {"no_allow_suspicious_bundles", s_allow_suspicious_bundles, 0 },
+ { NULL, 0, 0 }
+};
+
+/* Equal to MAX_PRECISION in atof-ieee.c */
+#define MAX_LITTLENUMS 6
+
+/* Turn the string pointed to by litP into a floating point constant
+ of type TYPE, and emit the appropriate bytes. The number of
+ LITTLENUMS emitted is stored in *SIZEP. An error message is
+ returned, or NULL on OK. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ LITTLENUM_TYPE *wordP;
+ char *t;
+
+ switch (type)
+ {
+ case 'f':
+ case 'F':
+ prec = 2;
+ break;
+
+ case 'd':
+ case 'D':
+ prec = 4;
+ break;
+
+ 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);
+ /* This loops outputs the LITTLENUMs in REVERSE order; in accord with
+ the bigendian 386. */
+ for (wordP = words + prec - 1; prec--;)
+ {
+ md_number_to_chars (litP, (valueT) (*wordP--), sizeof (LITTLENUM_TYPE));
+ litP += sizeof (LITTLENUM_TYPE);
+ }
+ return 0;
+}
+
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+
+void
+tilepro_cons_fix_new (fragS *frag,
+ int where,
+ int nbytes,
+ expressionS *exp)
+{
+ expressionS subexp;
+ bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
+ int no_overflow = 0;
+ fixS *fixP;
+
+ /* See if it's one of our special functions. */
+ switch (exp->X_op)
+ {
+ case O_lo16:
+ reloc = BFD_RELOC_LO16;
+ no_overflow = 1;
+ break;
+ case O_hi16:
+ reloc = BFD_RELOC_HI16;
+ no_overflow = 1;
+ break;
+ case O_ha16:
+ reloc = BFD_RELOC_HI16_S;
+ no_overflow = 1;
+ break;
+
+ default:
+ /* Do nothing. */
+ break;
+ }
+
+ if (reloc != BFD_RELOC_NONE)
+ {
+ if (nbytes != 2)
+ {
+ as_bad (_("This operator only produces two byte values."));
+ nbytes = 2;
+ }
+
+ memset (&subexp, 0, sizeof subexp);
+ subexp.X_op = O_symbol;
+ subexp.X_add_symbol = exp->X_add_symbol;
+ exp = &subexp;
+ }
+ else
+ {
+ switch (nbytes)
+ {
+ case 1:
+ reloc = BFD_RELOC_8;
+ break;
+ case 2:
+ reloc = BFD_RELOC_16;
+ break;
+ case 4:
+ reloc = BFD_RELOC_32;
+ break;
+ case 8:
+ reloc = BFD_RELOC_64;
+ break;
+ default:
+ as_bad (_("unsupported BFD relocation size %d"), nbytes);
+ reloc = BFD_RELOC_32;
+ break;
+ }
+ }
+
+ fixP = fix_new_exp (frag, where, nbytes, exp, 0, reloc);
+ fixP->tc_fix_data = NULL;
+ fixP->fx_no_overflow |= no_overflow;
+}
+
+
+void
+md_apply_fix (fixS *fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
+{
+ const struct tilepro_operand *operand;
+ valueT value = *valP;
+ char *p;
+
+ /* Leave these for the linker. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return;
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ /* We can't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+ }
+
+ /* Correct relocation types for pc-relativeness. */
+ switch (fixP->fx_r_type)
+ {
+#define FIX_PCREL(rtype) \
+ case rtype: \
+ if (fixP->fx_pcrel) \
+ fixP->fx_r_type = rtype##_PCREL; \
+ break; \
+ \
+ case rtype##_PCREL: \
+ if (!fixP->fx_pcrel) \
+ fixP->fx_r_type = rtype; \
+ break
+
+ FIX_PCREL (BFD_RELOC_8);
+ FIX_PCREL (BFD_RELOC_16);
+ FIX_PCREL (BFD_RELOC_32);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X0);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X1);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X0_LO);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X1_LO);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X0_HI);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X1_HI);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X0_HA);
+ FIX_PCREL (BFD_RELOC_TILEPRO_IMM16_X1_HA);
+
+#undef FIX_PCREL
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ if (fixP->fx_addsy != NULL)
+ {
+#ifdef OBJ_ELF
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_TILEPRO_IMM8_X0_TLS_GD_ADD:
+ case BFD_RELOC_TILEPRO_IMM8_X1_TLS_GD_ADD:
+ case BFD_RELOC_TILEPRO_IMM8_Y0_TLS_GD_ADD:
+ case BFD_RELOC_TILEPRO_IMM8_Y1_TLS_GD_ADD:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_GD:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_GD:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_GD_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_GD_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_GD_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_GD_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_GD_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_GD_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_IE:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_IE:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_IE_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_IE_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_IE_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_IE_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_IE_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_IE_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_LE:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_LE:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_LE_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_LE_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_LE_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_LE_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X0_TLS_LE_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X1_TLS_LE_HA:
+ case BFD_RELOC_TILEPRO_TLS_GD_CALL:
+ case BFD_RELOC_TILEPRO_TLS_IE_LOAD:
+ case BFD_RELOC_TILEPRO_TLS_DTPMOD32:
+ case BFD_RELOC_TILEPRO_TLS_DTPOFF32:
+ case BFD_RELOC_TILEPRO_TLS_TPOFF32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+#endif
+ return;
+ }
+
+ /* Apply lo16, hi16, ha16, etc. munging. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_TILEPRO_IMM16_X0_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X1_LO:
+ case BFD_RELOC_TILEPRO_IMM16_X0_LO_PCREL:
+ case BFD_RELOC_TILEPRO_IMM16_X1_LO_PCREL:
+ *valP = value = apply_special_operator (O_lo16, value);
+ break;
+
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_TILEPRO_IMM16_X0_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X1_HI:
+ case BFD_RELOC_TILEPRO_IMM16_X0_HI_PCREL:
+ case BFD_RELOC_TILEPRO_IMM16_X1_HI_PCREL:
+ *valP = value = apply_special_operator (O_hi16, value);
+ break;
+
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_TILEPRO_IMM16_X0_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X1_HA:
+ case BFD_RELOC_TILEPRO_IMM16_X0_HA_PCREL:
+ case BFD_RELOC_TILEPRO_IMM16_X1_HA_PCREL:
+ *valP = value = apply_special_operator (O_ha16, value);
+ break;
+
+ default:
+ /* Do nothing */
+ break;
+ }
+
+ p = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ operand = fixP->tc_fix_data;
+ if (operand != NULL)
+ {
+ /* It's an instruction operand. */
+ tilepro_bundle_bits bits =
+ insert_operand (0, operand, value, fixP->fx_file, fixP->fx_line);
+
+ /* Note that we might either be writing out bits for a bundle or a
+ static network instruction, which are different sizes, so it's
+ important to stop touching memory once we run out of bits. ORing in
+ values is OK since we know the existing bits for this operand are
+ zero. */
+ for (; bits != 0; bits >>= 8)
+ *p++ |= (char)bits;
+ }
+ else
+ {
+ /* Some other kind of relocation. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ case BFD_RELOC_8_PCREL:
+ md_number_to_chars (p, value, 1);
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_16_PCREL:
+ md_number_to_chars (p, value, 2);
+ break;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_32_PCREL:
+ md_number_to_chars (p, value, 4);
+ break;
+
+ default:
+ /* Leave it for the linker. */
+ return;
+ }
+ }
+
+ fixP->fx_done = 1;
+}
+
+
+/* Generate the BFD reloc to be stuck in the object file from the
+ fixup used internally in the assembler. */
+
+arelent *
+tc_gen_reloc (asection *sec ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ gas_assert ((int) fixp->fx_r_type > 0);
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ {
+ as_fatal (_("internal error? cannot generate `%s' relocation (%d, %d)"),
+ bfd_get_reloc_code_name (fixp->fx_r_type),
+ fixp->fx_pcrel, reloc->howto->pc_relative);
+ }
+ gas_assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
+
+ reloc->addend = fixp->fx_offset;
+
+ return reloc;
+}
+
+
+/* The location from which a PC relative jump should be calculated,
+ given a PC relative reloc. */
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_frag->fr_address + fixP->fx_where;
+}
+
+
+/* Return 1 if it's OK to adjust a reloc by replacing the symbol with
+ a section symbol plus some offset. */
+int
+tilepro_fix_adjustable (fixS *fix)
+{
+ /* Prevent all adjustments to global symbols */
+ if (S_IS_EXTERNAL (fix->fx_addsy) || S_IS_WEAK (fix->fx_addsy))
+ return 0;
+
+ return 1;
+}
+
+
+int
+tilepro_unrecognized_line (int ch)
+{
+ switch (ch)
+ {
+ case '{':
+ if (inside_bundle)
+ {
+ as_bad (_("Found '{' when already bundling."));
+ }
+ else
+ {
+ inside_bundle = 1;
+ current_bundle_index = 0;
+ }
+ return 1;
+
+ case '}':
+ if (!inside_bundle)
+ {
+ as_bad (_("Found '}' when not bundling."));
+ }
+ else
+ {
+ tilepro_flush_bundle ();
+ }
+
+ /* Allow '{' to follow on the same line. We also allow ";;", but that
+ happens automatically because ';' is an end of line marker. */
+ SKIP_WHITESPACE ();
+ if (input_line_pointer[0] == '{')
+ {
+ input_line_pointer++;
+ return tilepro_unrecognized_line ('{');
+ }
+
+ demand_empty_rest_of_line ();
+ return 1;
+
+ default:
+ break;
+ }
+
+ /* Not a valid line. */
+ return 0;
+}
+
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+tilepro_handle_align (fragS *fragp)
+{
+ int bytes, fix;
+ char *p;
+
+ if (fragp->fr_type != rs_align_code)
+ return;
+
+ bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix;
+ p = fragp->fr_literal + fragp->fr_fix;
+ fix = 0;
+
+ /* Determine the bits for NOP. */
+ const struct tilepro_opcode *nop_opcode =
+ &tilepro_opcodes[TILEPRO_OPC_NOP];
+ tilepro_bundle_bits nop =
+ ( nop_opcode->fixed_bit_values[TILEPRO_PIPELINE_X0]
+ | nop_opcode->fixed_bit_values[TILEPRO_PIPELINE_X1]);
+
+ if ((bytes & (TILEPRO_BUNDLE_SIZE_IN_BYTES - 1)) != 0)
+ {
+ fix = bytes & (TILEPRO_BUNDLE_SIZE_IN_BYTES - 1);
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ number_to_chars_littleendian (p, (unsigned int)nop, 4);
+ number_to_chars_littleendian (p + 4, (unsigned int)(nop >> 32), 4);
+ fragp->fr_fix += fix;
+ fragp->fr_var = TILEPRO_BUNDLE_SIZE_IN_BYTES;
+}
+
+/* Standard calling conventions leave the CFA at SP on entry. */
+void
+tilepro_cfi_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa_register (54);
+}
+
+int
+tc_tilepro_regname_to_dw2regnum (char *regname)
+{
+ int i;
+
+ for (i = 0; i < TILEPRO_NUM_REGISTERS; i++)
+ {
+ if (!strcmp (regname, tilepro_register_names[i]))
+ return i;
+ }
+
+ return -1;
+}
diff --git a/gas/config/tc-tilepro.h b/gas/config/tc-tilepro.h
new file mode 100644
index 0000000..92d31b1
--- /dev/null
+++ b/gas/config/tc-tilepro.h
@@ -0,0 +1,93 @@
+/* tc-tile.h - Macros and type defines for a TILEPro chip.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef TC_TILEPRO
+
+#include "opcode/tilepro.h"
+
+#define TC_TILEPRO
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define WORKING_DOT_WORD
+
+#define TARGET_ARCH bfd_arch_tilepro
+
+#define TARGET_FORMAT "elf32-tilepro"
+
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 8
+
+#define md_number_to_chars number_to_chars_littleendian
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */
+
+#define HANDLE_ALIGN(fragp) tilepro_handle_align (fragp)
+extern void tilepro_handle_align (struct frag *);
+
+#define MAX_MEM_FOR_RS_ALIGN_CODE (7 + 8)
+
+struct tilepro_operand;
+#define TC_FIX_TYPE const struct tilepro_operand *
+
+/* Initialize the TC_FIX_TYPE field. */
+#define TC_INIT_FIX_DATA(FIX) \
+ FIX->tc_fix_data = 0
+
+extern void tilepro_cons_fix_new (struct frag *, int,
+ int, struct expressionS *);
+
+#define TC_CONS_FIX_NEW(FRAG, WHERE, NBYTES, EXP, RELOC) \
+ tilepro_cons_fix_new (FRAG, WHERE, NBYTES, EXP)
+
+extern int tilepro_parse_name (char *, expressionS *, char *);
+#define md_parse_name(name, e, m, nextP) tilepro_parse_name (name, e, nextP)
+
+extern int tilepro_fix_adjustable (struct fix *);
+#define tc_fix_adjustable(FIX) tilepro_fix_adjustable (FIX)
+
+extern int tilepro_unrecognized_line (int);
+#define tc_unrecognized_line(ch) tilepro_unrecognized_line (ch)
+
+/* Values passed to md_apply_fix3 don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_convert_frag(b,s,f) \
+ as_fatal ("tilepro convert_frag called")
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal ("tilepro estimate_size_before_relax called"),1)
+#define md_operand(x)
+
+#define md_section_align(seg,size) (size)
+
+/* We want .cfi_* pseudo-ops for generating unwind info. */
+#define TARGET_USE_CFIPOP 1
+
+#define tc_cfi_frame_initial_instructions \
+ tilepro_cfi_frame_initial_instructions
+extern void tilepro_cfi_frame_initial_instructions (void);
+
+#define tc_regname_to_dw2regnum tc_tilepro_regname_to_dw2regnum
+extern int tc_tilepro_regname_to_dw2regnum (char *);
+
+#define DWARF2_DEFAULT_RETURN_COLUMN 55
+#define DWARF2_CIE_DATA_ALIGNMENT (-4)
+
+#endif /* TC_TILEPRO */
diff --git a/gas/config/tc-v850.c b/gas/config/tc-v850.c
new file mode 100644
index 0000000..91acec4
--- /dev/null
+++ b/gas/config/tc-v850.c
@@ -0,0 +1,3718 @@
+/* tc-v850.c -- Assembler code for the NEC V850
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/v850.h"
+#include "dwarf2dbg.h"
+
+/* Sign-extend a 16-bit number. */
+#define SEXT16(x) ((((x) & 0xffff) ^ (~0x7fff)) + 0x8000)
+
+/* Set to TRUE if we want to be pedantic about signed overflows. */
+static bfd_boolean warn_signed_overflows = FALSE;
+static bfd_boolean warn_unsigned_overflows = FALSE;
+
+/* Indicates the target BFD machine number. */
+static int machine = -1;
+
+
+/* Indiciates the target BFD architecture. */
+int v850_target_arch = bfd_arch_v850_rh850;
+const char * v850_target_format = "elf32-v850-rh850";
+static flagword v850_e_flags = 0;
+
+/* Indicates the target processor(s) for the assemble. */
+static int processor_mask = 0;
+
+/* Structure to hold information about predefined registers. */
+struct reg_name
+{
+ const char *name;
+ int value;
+ unsigned int processors;
+};
+
+/* Generic assembler global variables which must be defined by all
+ targets. */
+
+/* Characters which always start a comment. */
+const char comment_chars[] = "#";
+
+/* Characters which start a comment at the beginning of a line. */
+const char line_comment_chars[] = ";#";
+
+/* Characters which may be used to separate multiple commands on a
+ single line. */
+const char line_separator_chars[] = ";";
+
+/* Characters which are used to indicate an exponent in a floating
+ point number. */
+const char EXP_CHARS[] = "eE";
+
+/* Characters which mean that a number is a floating point constant,
+ as in 0d1.0. */
+const char FLT_CHARS[] = "dD";
+
+const relax_typeS md_relax_table[] =
+{
+ /* Conditional branches.(V850/V850E, max 22bit) */
+#define SUBYPTE_COND_9_22 0
+ {0xfe, -0x100, 2, SUBYPTE_COND_9_22 + 1},
+ {0x1ffffe + 2, -0x200000 + 2, 6, 0},
+ /* Conditional branches.(V850/V850E, max 22bit) */
+#define SUBYPTE_SA_9_22 2
+ {0xfe, -0x100, 2, SUBYPTE_SA_9_22 + 1},
+ {0x1ffffe + 4, -0x200000 + 4, 8, 0},
+ /* Unconditional branches.(V850/V850E, max 22bit) */
+#define SUBYPTE_UNCOND_9_22 4
+ {0xfe, -0x100, 2, SUBYPTE_UNCOND_9_22 + 1},
+ {0x1ffffe, -0x200000, 4, 0},
+ /* Conditional branches.(V850E2, max 32bit) */
+#define SUBYPTE_COND_9_22_32 6
+ {0xfe, -0x100, 2, SUBYPTE_COND_9_22_32 + 1},
+ {0x1fffff + 2, -0x200000 + 2, 6, SUBYPTE_COND_9_22_32 + 2},
+ {0x7ffffffe, -0x80000000, 8, 0},
+ /* Conditional branches.(V850E2, max 32bit) */
+#define SUBYPTE_SA_9_22_32 9
+ {0xfe, -0x100, 2, SUBYPTE_SA_9_22_32 + 1},
+ {0x1ffffe + 4, -0x200000 + 4, 8, SUBYPTE_SA_9_22_32 + 2},
+ {0x7ffffffe, -0x80000000, 10, 0},
+ /* Unconditional branches.(V850E2, max 32bit) */
+#define SUBYPTE_UNCOND_9_22_32 12
+ {0xfe, -0x100, 2, SUBYPTE_UNCOND_9_22_32 + 1},
+ {0x1ffffe, -0x200000, 4, SUBYPTE_UNCOND_9_22_32 + 2},
+ {0x7ffffffe, -0x80000000, 6, 0},
+ /* Conditional branches.(V850E2R max 22bit) */
+#define SUBYPTE_COND_9_17_22 15
+ {0xfe, -0x100, 2, SUBYPTE_COND_9_17_22 + 1},
+ {0xfffe, -0x10000, 4, SUBYPTE_COND_9_17_22 + 2},
+ {0x1ffffe + 2, -0x200000 + 2, 6, 0},
+ /* Conditional branches.(V850E2R max 22bit) */
+#define SUBYPTE_SA_9_17_22 18
+ {0xfe, -0x100, 2, SUBYPTE_SA_9_17_22 + 1},
+ {0xfffe, -0x10000, 4, SUBYPTE_SA_9_17_22 + 2},
+ {0x1ffffe + 4, -0x200000 + 4, 8, 0},
+ /* Conditional branches.(V850E2R max 32bit) */
+#define SUBYPTE_COND_9_17_22_32 21
+ {0xfe, -0x100, 2, SUBYPTE_COND_9_17_22_32 + 1},
+ {0xfffe, -0x10000, 4, SUBYPTE_COND_9_17_22_32 + 2},
+ {0x1ffffe + 2, -0x200000 + 2, 6, SUBYPTE_COND_9_17_22_32 + 3},
+ {0x7ffffffe, -0x80000000, 8, 0},
+ /* Conditional branches.(V850E2R max 32bit) */
+#define SUBYPTE_SA_9_17_22_32 25
+ {0xfe, -0x100, 2, SUBYPTE_SA_9_17_22_32 + 1},
+ {0xfffe, -0x10000, 4, SUBYPTE_SA_9_17_22_32 + 2},
+ {0x1ffffe + 4, -0x200000 + 4, 8, SUBYPTE_SA_9_17_22_32 + 3},
+ {0x7ffffffe, -0x80000000, 10, 0},
+ /* Loop. (V850E2V4_UP, max 22-bit). */
+#define SUBYPTE_LOOP_16_22 29
+ {0x0, -0x0fffe, 4, SUBYPTE_LOOP_16_22 + 1},
+ {0x1ffffe + 2, -0x200000 + 2, 6, 0},
+};
+
+static int v850_relax = 0;
+
+/* Default branch disp size 22 or 32. */
+static int default_disp_size = 22;
+
+/* Default no using bcond17. */
+static int no_bcond17 = 0;
+
+/* Default no using ld/st 23bit offset. */
+static int no_stld23 = 0;
+
+/* Fixups. */
+#define MAX_INSN_FIXUPS 5
+
+struct v850_fixup
+{
+ expressionS exp;
+ int opindex;
+ bfd_reloc_code_real_type reloc;
+};
+
+struct v850_fixup fixups[MAX_INSN_FIXUPS];
+static int fc;
+
+struct v850_seg_entry
+{
+ segT s;
+ const char *name;
+ flagword flags;
+};
+
+struct v850_seg_entry v850_seg_table[] =
+{
+ { NULL, ".sdata",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS
+ | SEC_SMALL_DATA },
+ { NULL, ".tdata",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS },
+ { NULL, ".zdata",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS },
+ { NULL, ".sbss",
+ SEC_ALLOC | SEC_SMALL_DATA },
+ { NULL, ".tbss",
+ SEC_ALLOC },
+ { NULL, ".zbss",
+ SEC_ALLOC},
+ { NULL, ".rosdata",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_DATA
+ | SEC_HAS_CONTENTS | SEC_SMALL_DATA },
+ { NULL, ".rozdata",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_DATA
+ | SEC_HAS_CONTENTS },
+ { NULL, ".scommon",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS
+ | SEC_SMALL_DATA | SEC_IS_COMMON },
+ { NULL, ".tcommon",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS
+ | SEC_IS_COMMON },
+ { NULL, ".zcommon",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS
+ | SEC_IS_COMMON },
+ { NULL, ".call_table_data",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_DATA | SEC_HAS_CONTENTS },
+ { NULL, ".call_table_text",
+ SEC_ALLOC | SEC_LOAD | SEC_RELOC | SEC_READONLY | SEC_CODE
+ | SEC_HAS_CONTENTS},
+ { NULL, ".bss",
+ SEC_ALLOC }
+};
+
+#define SDATA_SECTION 0
+#define TDATA_SECTION 1
+#define ZDATA_SECTION 2
+#define SBSS_SECTION 3
+#define TBSS_SECTION 4
+#define ZBSS_SECTION 5
+#define ROSDATA_SECTION 6
+#define ROZDATA_SECTION 7
+#define SCOMMON_SECTION 8
+#define TCOMMON_SECTION 9
+#define ZCOMMON_SECTION 10
+#define CALL_TABLE_DATA_SECTION 11
+#define CALL_TABLE_TEXT_SECTION 12
+#define BSS_SECTION 13
+
+static void
+do_v850_seg (int i, subsegT sub)
+{
+ struct v850_seg_entry *seg = v850_seg_table + i;
+
+ obj_elf_section_change_hook ();
+
+ if (seg->s != NULL)
+ subseg_set (seg->s, sub);
+ else
+ {
+ seg->s = subseg_new (seg->name, sub);
+ bfd_set_section_flags (stdoutput, seg->s, seg->flags);
+ if ((seg->flags & SEC_LOAD) == 0)
+ seg_info (seg->s)->bss = 1;
+ }
+}
+
+static void
+v850_seg (int i)
+{
+ subsegT sub = get_absolute_expression ();
+
+ do_v850_seg (i, sub);
+ demand_empty_rest_of_line ();
+}
+
+static void
+v850_offset (int ignore ATTRIBUTE_UNUSED)
+{
+ char *pfrag;
+ int temp = get_absolute_expression ();
+
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT)0, (symbolS *)0,
+ (offsetT) temp, (char *) 0);
+ *pfrag = 0;
+
+ demand_empty_rest_of_line ();
+}
+
+/* Copied from obj_elf_common() in gas/config/obj-elf.c. */
+
+static void
+v850_comm (int area)
+{
+ char *name;
+ char c;
+ char *p;
+ int temp;
+ unsigned int size;
+ symbolS *symbolP;
+ int have_align;
+
+ name = input_line_pointer;
+ c = get_symbol_end ();
+
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ as_bad (_("Expected comma after symbol-name"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Skip ','. */
+ input_line_pointer++;
+
+ if ((temp = get_absolute_expression ()) < 0)
+ {
+ /* xgettext:c-format */
+ as_bad (_(".COMMon length (%d.) < 0! Ignored."), temp);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ size = temp;
+ *p = 0;
+ symbolP = symbol_find_or_make (name);
+ *p = c;
+
+ if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
+ {
+ as_bad (_("Ignoring attempt to re-define symbol"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ if (S_GET_VALUE (symbolP) != 0)
+ {
+ if (S_GET_VALUE (symbolP) != size)
+ /* xgettext:c-format */
+ as_warn (_("Length of .comm \"%s\" is already %ld. Not changed to %d."),
+ S_GET_NAME (symbolP), (long) S_GET_VALUE (symbolP), size);
+ }
+
+ know (symbol_get_frag (symbolP) == &zero_address_frag);
+
+ if (*input_line_pointer != ',')
+ have_align = 0;
+ else
+ {
+ have_align = 1;
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ }
+
+ if (! have_align || *input_line_pointer != '"')
+ {
+ if (! have_align)
+ temp = 0;
+ else
+ {
+ temp = get_absolute_expression ();
+
+ if (temp < 0)
+ {
+ temp = 0;
+ as_warn (_("Common alignment negative; 0 assumed"));
+ }
+ }
+
+ if (symbol_get_obj (symbolP)->local)
+ {
+ segT old_sec;
+ int old_subsec;
+ char *pfrag;
+ int align;
+ flagword applicable;
+
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ applicable = bfd_applicable_section_flags (stdoutput);
+
+ applicable &= SEC_ALLOC;
+
+ switch (area)
+ {
+ case SCOMMON_SECTION:
+ do_v850_seg (SBSS_SECTION, 0);
+ break;
+
+ case ZCOMMON_SECTION:
+ do_v850_seg (ZBSS_SECTION, 0);
+ break;
+
+ case TCOMMON_SECTION:
+ do_v850_seg (TBSS_SECTION, 0);
+ break;
+ }
+
+ if (temp)
+ {
+ /* Convert to a power of 2 alignment. */
+ for (align = 0; (temp & 1) == 0; temp >>= 1, ++align)
+ ;
+
+ if (temp != 1)
+ {
+ as_bad (_("Common alignment not a power of 2"));
+ ignore_rest_of_line ();
+ return;
+ }
+ }
+ else
+ align = 0;
+
+ record_alignment (now_seg, align);
+
+ if (align)
+ frag_align (align, 0, 0);
+
+ switch (area)
+ {
+ case SCOMMON_SECTION:
+ if (S_GET_SEGMENT (symbolP) == v850_seg_table[SBSS_SECTION].s)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ break;
+
+ case ZCOMMON_SECTION:
+ if (S_GET_SEGMENT (symbolP) == v850_seg_table[ZBSS_SECTION].s)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ break;
+
+ case TCOMMON_SECTION:
+ if (S_GET_SEGMENT (symbolP) == v850_seg_table[TBSS_SECTION].s)
+ symbol_get_frag (symbolP)->fr_symbol = 0;
+ break;
+
+ default:
+ abort ();
+ }
+
+ symbol_set_frag (symbolP, frag_now);
+ pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP,
+ (offsetT) size, (char *) 0);
+ *pfrag = 0;
+ S_SET_SIZE (symbolP, size);
+
+ switch (area)
+ {
+ case SCOMMON_SECTION:
+ S_SET_SEGMENT (symbolP, v850_seg_table[SBSS_SECTION].s);
+ break;
+
+ case ZCOMMON_SECTION:
+ S_SET_SEGMENT (symbolP, v850_seg_table[ZBSS_SECTION].s);
+ break;
+
+ case TCOMMON_SECTION:
+ S_SET_SEGMENT (symbolP, v850_seg_table[TBSS_SECTION].s);
+ break;
+
+ default:
+ abort ();
+ }
+
+ S_CLEAR_EXTERNAL (symbolP);
+ obj_elf_section_change_hook ();
+ subseg_set (old_sec, old_subsec);
+ }
+ else
+ {
+ segT old_sec;
+ int old_subsec;
+
+ allocate_common:
+ old_sec = now_seg;
+ old_subsec = now_subseg;
+
+ S_SET_VALUE (symbolP, (valueT) size);
+ S_SET_ALIGN (symbolP, temp);
+ S_SET_EXTERNAL (symbolP);
+
+ switch (area)
+ {
+ case SCOMMON_SECTION:
+ case ZCOMMON_SECTION:
+ case TCOMMON_SECTION:
+ do_v850_seg (area, 0);
+ S_SET_SEGMENT (symbolP, v850_seg_table[area].s);
+ break;
+
+ default:
+ abort ();
+ }
+
+ obj_elf_section_change_hook ();
+ subseg_set (old_sec, old_subsec);
+ }
+ }
+ else
+ {
+ input_line_pointer++;
+
+ /* @@ Some use the dot, some don't. Can we get some consistency?? */
+ if (*input_line_pointer == '.')
+ input_line_pointer++;
+
+ /* @@ Some say data, some say bss. */
+ if (strncmp (input_line_pointer, "bss\"", 4)
+ && strncmp (input_line_pointer, "data\"", 5))
+ {
+ while (*--input_line_pointer != '"')
+ ;
+ input_line_pointer--;
+ goto bad_common_segment;
+ }
+
+ while (*input_line_pointer++ != '"')
+ ;
+
+ goto allocate_common;
+ }
+
+ symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+
+ demand_empty_rest_of_line ();
+ return;
+
+ {
+ bad_common_segment:
+ p = input_line_pointer;
+ while (*p && *p != '\n')
+ p++;
+ c = *p;
+ *p = '\0';
+ as_bad (_("bad .common segment %s"), input_line_pointer + 1);
+ *p = c;
+ input_line_pointer = p;
+ ignore_rest_of_line ();
+ return;
+ }
+}
+
+static void
+set_machine (int number)
+{
+ machine = number;
+ bfd_set_arch_mach (stdoutput, v850_target_arch, machine);
+
+ switch (machine)
+ {
+ case 0: SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850); break;
+ case bfd_mach_v850: SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850); break;
+ case bfd_mach_v850e: SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E); break;
+ case bfd_mach_v850e1: SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E); break;
+ case bfd_mach_v850e2: SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E2); break;
+ case bfd_mach_v850e2v3:SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E2V3); break;
+ case bfd_mach_v850e3v5: SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E3V5); break;
+ }
+}
+
+static void
+v850_longcode (int type)
+{
+ expressionS ex;
+
+ if (! v850_relax)
+ {
+ if (type == 1)
+ as_warn (_(".longcall pseudo-op seen when not relaxing"));
+ else
+ as_warn (_(".longjump pseudo-op seen when not relaxing"));
+ }
+
+ expression (&ex);
+
+ if (ex.X_op != O_symbol || ex.X_add_number != 0)
+ {
+ as_bad (_("bad .longcall format"));
+ ignore_rest_of_line ();
+
+ return;
+ }
+
+ if (type == 1)
+ fix_new_exp (frag_now, frag_now_fix (), 4, & ex, 1,
+ BFD_RELOC_V850_LONGCALL);
+ else
+ fix_new_exp (frag_now, frag_now_fix (), 4, & ex, 1,
+ BFD_RELOC_V850_LONGJUMP);
+
+ demand_empty_rest_of_line ();
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "sdata", v850_seg, SDATA_SECTION },
+ { "tdata", v850_seg, TDATA_SECTION },
+ { "zdata", v850_seg, ZDATA_SECTION },
+ { "sbss", v850_seg, SBSS_SECTION },
+ { "tbss", v850_seg, TBSS_SECTION },
+ { "zbss", v850_seg, ZBSS_SECTION },
+ { "rosdata", v850_seg, ROSDATA_SECTION },
+ { "rozdata", v850_seg, ROZDATA_SECTION },
+ { "bss", v850_seg, BSS_SECTION },
+ { "offset", v850_offset, 0 },
+ { "word", cons, 4 },
+ { "zcomm", v850_comm, ZCOMMON_SECTION },
+ { "scomm", v850_comm, SCOMMON_SECTION },
+ { "tcomm", v850_comm, TCOMMON_SECTION },
+ { "v850", set_machine, 0 },
+ { "call_table_data", v850_seg, CALL_TABLE_DATA_SECTION },
+ { "call_table_text", v850_seg, CALL_TABLE_TEXT_SECTION },
+ { "v850e", set_machine, bfd_mach_v850e },
+ { "v850e1", set_machine, bfd_mach_v850e1 },
+ { "v850e2", set_machine, bfd_mach_v850e2 },
+ { "v850e2v3", set_machine, bfd_mach_v850e2v3 },
+ { "v850e2v4", set_machine, bfd_mach_v850e3v5 },
+ { "v850e3v5", set_machine, bfd_mach_v850e3v5 },
+ { "longcall", v850_longcode, 1 },
+ { "longjump", v850_longcode, 2 },
+ { NULL, NULL, 0 }
+};
+
+/* Opcode hash table. */
+static struct hash_control *v850_hash;
+
+/* This table is sorted. Suitable for searching by a binary search. */
+static const struct reg_name pre_defined_registers[] =
+{
+ { "ep", 30, PROCESSOR_ALL }, /* ep - element ptr. */
+ { "gp", 4, PROCESSOR_ALL }, /* gp - global ptr. */
+ { "hp", 2, PROCESSOR_ALL }, /* hp - handler stack ptr. */
+ { "lp", 31, PROCESSOR_ALL }, /* lp - link ptr. */
+ { "r0", 0, PROCESSOR_ALL },
+ { "r1", 1, PROCESSOR_ALL },
+ { "r10", 10, PROCESSOR_ALL },
+ { "r11", 11, PROCESSOR_ALL },
+ { "r12", 12, PROCESSOR_ALL },
+ { "r13", 13, PROCESSOR_ALL },
+ { "r14", 14, PROCESSOR_ALL },
+ { "r15", 15, PROCESSOR_ALL },
+ { "r16", 16, PROCESSOR_ALL },
+ { "r17", 17, PROCESSOR_ALL },
+ { "r18", 18, PROCESSOR_ALL },
+ { "r19", 19, PROCESSOR_ALL },
+ { "r2", 2, PROCESSOR_ALL },
+ { "r20", 20, PROCESSOR_ALL },
+ { "r21", 21, PROCESSOR_ALL },
+ { "r22", 22, PROCESSOR_ALL },
+ { "r23", 23, PROCESSOR_ALL },
+ { "r24", 24, PROCESSOR_ALL },
+ { "r25", 25, PROCESSOR_ALL },
+ { "r26", 26, PROCESSOR_ALL },
+ { "r27", 27, PROCESSOR_ALL },
+ { "r28", 28, PROCESSOR_ALL },
+ { "r29", 29, PROCESSOR_ALL },
+ { "r3", 3, PROCESSOR_ALL },
+ { "r30", 30, PROCESSOR_ALL },
+ { "r31", 31, PROCESSOR_ALL },
+ { "r4", 4, PROCESSOR_ALL },
+ { "r5", 5, PROCESSOR_ALL },
+ { "r6", 6, PROCESSOR_ALL },
+ { "r7", 7, PROCESSOR_ALL },
+ { "r8", 8, PROCESSOR_ALL },
+ { "r9", 9, PROCESSOR_ALL },
+ { "sp", 3, PROCESSOR_ALL }, /* sp - stack ptr. */
+ { "tp", 5, PROCESSOR_ALL }, /* tp - text ptr. */
+ { "zero", 0, PROCESSOR_ALL },
+};
+
+#define REG_NAME_CNT \
+ (sizeof (pre_defined_registers) / sizeof (struct reg_name))
+
+static const struct reg_name system_registers[] =
+{
+ { "asid", 23, PROCESSOR_NOT_V850 },
+ { "bpam", 25, PROCESSOR_NOT_V850 },
+ { "bpav", 24, PROCESSOR_NOT_V850 },
+ { "bpc", 22, PROCESSOR_NOT_V850 },
+ { "bpdm", 27, PROCESSOR_NOT_V850 },
+ { "bpdv", 26, PROCESSOR_NOT_V850 },
+ { "bsel", 31, PROCESSOR_V850E2_UP },
+ { "cfg", 7, PROCESSOR_V850E2V3_UP },
+ { "ctbp", 20, PROCESSOR_NOT_V850 },
+ { "ctpc", 16, PROCESSOR_NOT_V850 },
+ { "ctpsw", 17, PROCESSOR_NOT_V850 },
+ { "dbic", 15, PROCESSOR_V850E2_UP },
+ { "dbpc", 18, PROCESSOR_NOT_V850 },
+ { "dbpsw", 19, PROCESSOR_NOT_V850 },
+ { "dbwr", 30, PROCESSOR_V850E2_UP },
+ { "dir", 21, PROCESSOR_NOT_V850 },
+ { "dpa0l", 16, PROCESSOR_V850E2V3_UP },
+ { "dpa0u", 17, PROCESSOR_V850E2V3_UP },
+ { "dpa1l", 18, PROCESSOR_V850E2V3_UP },
+ { "dpa1u", 19, PROCESSOR_V850E2V3_UP },
+ { "dpa2l", 20, PROCESSOR_V850E2V3_UP },
+ { "dpa2u", 21, PROCESSOR_V850E2V3_UP },
+ { "dpa3l", 22, PROCESSOR_V850E2V3_UP },
+ { "dpa3u", 23, PROCESSOR_V850E2V3_UP },
+ { "dpa4l", 24, PROCESSOR_V850E2V3_UP },
+ { "dpa4u", 25, PROCESSOR_V850E2V3_UP },
+ { "dpa5l", 26, PROCESSOR_V850E2V3_UP },
+ { "dpa5u", 27, PROCESSOR_V850E2V3_UP },
+ { "ecr", 4, PROCESSOR_ALL },
+ { "eh_base", 3, PROCESSOR_V850E2V3_UP },
+ { "eh_cfg", 1, PROCESSOR_V850E2V3_UP },
+ { "eh_reset", 2, PROCESSOR_V850E2V3_UP },
+ { "eiic", 13, PROCESSOR_V850E2_UP },
+ { "eipc", 0, PROCESSOR_ALL },
+ { "eipsw", 1, PROCESSOR_ALL },
+ { "eiwr", 28, PROCESSOR_V850E2_UP },
+ { "feic", 14, PROCESSOR_V850E2_UP },
+ { "fepc", 2, PROCESSOR_ALL },
+ { "fepsw", 3, PROCESSOR_ALL },
+ { "fewr", 29, PROCESSOR_V850E2_UP },
+ { "fpcc", 9, PROCESSOR_V850E2V3_UP },
+ { "fpcfg", 10, PROCESSOR_V850E2V3_UP },
+ { "fpec", 11, PROCESSOR_V850E2V3_UP },
+ { "fpepc", 7, PROCESSOR_V850E2V3_UP },
+ { "fpspc", 27, PROCESSOR_V850E2V3_UP },
+ { "fpsr", 6, PROCESSOR_V850E2V3_UP },
+ { "fpst", 8, PROCESSOR_V850E2V3_UP },
+ { "ipa0l", 6, PROCESSOR_V850E2V3_UP },
+ { "ipa0u", 7, PROCESSOR_V850E2V3_UP },
+ { "ipa1l", 8, PROCESSOR_V850E2V3_UP },
+ { "ipa1u", 9, PROCESSOR_V850E2V3_UP },
+ { "ipa2l", 10, PROCESSOR_V850E2V3_UP },
+ { "ipa2u", 11, PROCESSOR_V850E2V3_UP },
+ { "ipa3l", 12, PROCESSOR_V850E2V3_UP },
+ { "ipa3u", 13, PROCESSOR_V850E2V3_UP },
+ { "ipa4l", 14, PROCESSOR_V850E2V3_UP },
+ { "ipa4u", 15, PROCESSOR_V850E2V3_UP },
+ { "mca", 24, PROCESSOR_V850E2V3_UP },
+ { "mcc", 26, PROCESSOR_V850E2V3_UP },
+ { "mcr", 27, PROCESSOR_V850E2V3_UP },
+ { "mcs", 25, PROCESSOR_V850E2V3_UP },
+ { "mpc", 1, PROCESSOR_V850E2V3_UP },
+ { "mpm", 0, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa0l", 16, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa0u", 17, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa1l", 18, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa1u", 19, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa2l", 20, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa2u", 21, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa3l", 22, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa3u", 23, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa4l", 24, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa4u", 25, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa5l", 26, PROCESSOR_V850E2V3_UP },
+ { "mpu10_dpa5u", 27, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa0l", 6, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa0u", 7, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa1l", 8, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa1u", 9, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa2l", 10, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa2u", 11, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa3l", 12, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa3u", 13, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa4l", 14, PROCESSOR_V850E2V3_UP },
+ { "mpu10_ipa4u", 15, PROCESSOR_V850E2V3_UP },
+ { "mpu10_mpc", 1, PROCESSOR_V850E2V3_UP },
+ { "mpu10_mpm", 0, PROCESSOR_V850E2V3_UP },
+ { "mpu10_tid", 2, PROCESSOR_V850E2V3_UP },
+ { "mpu10_vmadr", 5, PROCESSOR_V850E2V3_UP },
+ { "mpu10_vmecr", 3, PROCESSOR_V850E2V3_UP },
+ { "mpu10_vmtid", 4, PROCESSOR_V850E2V3_UP },
+ { "pid", 6, PROCESSOR_V850E2V3_UP },
+ { "pmcr0", 4, PROCESSOR_V850E2V3_UP },
+ { "pmis2", 14, PROCESSOR_V850E2V3_UP },
+ { "psw", 5, PROCESSOR_ALL },
+ { "scbp", 12, PROCESSOR_V850E2V3_UP },
+ { "sccfg", 11, PROCESSOR_V850E2V3_UP },
+ { "sr0", 0, PROCESSOR_ALL },
+ { "sr1", 1, PROCESSOR_ALL },
+ { "sr10", 10, PROCESSOR_ALL },
+ { "sr11", 11, PROCESSOR_ALL },
+ { "sr12", 12, PROCESSOR_ALL },
+ { "sr13", 13, PROCESSOR_ALL },
+ { "sr14", 14, PROCESSOR_ALL },
+ { "sr15", 15, PROCESSOR_ALL },
+ { "sr16", 16, PROCESSOR_ALL },
+ { "sr17", 17, PROCESSOR_ALL },
+ { "sr18", 18, PROCESSOR_ALL },
+ { "sr19", 19, PROCESSOR_ALL },
+ { "sr2", 2, PROCESSOR_ALL },
+ { "sr20", 20, PROCESSOR_ALL },
+ { "sr21", 21, PROCESSOR_ALL },
+ { "sr22", 22, PROCESSOR_ALL },
+ { "sr23", 23, PROCESSOR_ALL },
+ { "sr24", 24, PROCESSOR_ALL },
+ { "sr25", 25, PROCESSOR_ALL },
+ { "sr26", 26, PROCESSOR_ALL },
+ { "sr27", 27, PROCESSOR_ALL },
+ { "sr28", 28, PROCESSOR_ALL },
+ { "sr29", 29, PROCESSOR_ALL },
+ { "sr3", 3, PROCESSOR_ALL },
+ { "sr30", 30, PROCESSOR_ALL },
+ { "sr31", 31, PROCESSOR_ALL },
+ { "sr4", 4, PROCESSOR_ALL },
+ { "sr5", 5, PROCESSOR_ALL },
+ { "sr6", 6, PROCESSOR_ALL },
+ { "sr7", 7, PROCESSOR_ALL },
+ { "sr8", 8, PROCESSOR_ALL },
+ { "sr9", 9, PROCESSOR_ALL },
+ { "sw_base", 3, PROCESSOR_V850E2V3_UP },
+ { "sw_cfg", 1, PROCESSOR_V850E2V3_UP },
+ { "sw_ctl", 0, PROCESSOR_V850E2V3_UP },
+ { "tid", 2, PROCESSOR_V850E2V3_UP },
+ { "vmadr", 6, PROCESSOR_V850E2V3_UP },
+ { "vmecr", 4, PROCESSOR_V850E2V3_UP },
+ { "vmtid", 5, PROCESSOR_V850E2V3_UP },
+ { "vsadr", 2, PROCESSOR_V850E2V3_UP },
+ { "vsecr", 0, PROCESSOR_V850E2V3_UP },
+ { "vstid", 1, PROCESSOR_V850E2V3_UP },
+};
+
+#define SYSREG_NAME_CNT \
+ (sizeof (system_registers) / sizeof (struct reg_name))
+
+
+static const struct reg_name cc_names[] =
+{
+ { "c", 0x1, PROCESSOR_ALL },
+ { "e", 0x2, PROCESSOR_ALL },
+ { "ge", 0xe, PROCESSOR_ALL },
+ { "gt", 0xf, PROCESSOR_ALL },
+ { "h", 0xb, PROCESSOR_ALL },
+ { "l", 0x1, PROCESSOR_ALL },
+ { "le", 0x7, PROCESSOR_ALL },
+ { "lt", 0x6, PROCESSOR_ALL },
+ { "n", 0x4, PROCESSOR_ALL },
+ { "nc", 0x9, PROCESSOR_ALL },
+ { "ne", 0xa, PROCESSOR_ALL },
+ { "nh", 0x3, PROCESSOR_ALL },
+ { "nl", 0x9, PROCESSOR_ALL },
+ { "ns", 0xc, PROCESSOR_ALL },
+ { "nv", 0x8, PROCESSOR_ALL },
+ { "nz", 0xa, PROCESSOR_ALL },
+ { "p", 0xc, PROCESSOR_ALL },
+ { "s", 0x4, PROCESSOR_ALL },
+#define COND_SA_NUM 0xd
+ { "sa", COND_SA_NUM, PROCESSOR_ALL },
+ { "t", 0x5, PROCESSOR_ALL },
+ { "v", 0x0, PROCESSOR_ALL },
+ { "z", 0x2, PROCESSOR_ALL },
+};
+
+#define CC_NAME_CNT \
+ (sizeof (cc_names) / sizeof (struct reg_name))
+
+static const struct reg_name float_cc_names[] =
+{
+ { "eq", 0x2, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "f", 0x0, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "ge", 0xd, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "gl", 0xb, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "gle", 0x9, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "gt", 0xf, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "le", 0xe, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "lt", 0xc, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "neq", 0x2, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "nge", 0xd, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "ngl", 0xb, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "ngle",0x9, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "ngt", 0xf, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "nle", 0xe, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "nlt", 0xc, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "oge", 0x5, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "ogl", 0x3, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "ogt", 0x7, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "ole", 0x6, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "olt", 0x4, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "or", 0x1, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "seq", 0xa, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "sf", 0x8, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "sne", 0xa, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "st", 0x8, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "t", 0x0, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "ueq", 0x3, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "uge", 0x4, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "ugt", 0x6, PROCESSOR_V850E2V3_UP }, /* false. */
+ { "ule", 0x7, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "ult", 0x5, PROCESSOR_V850E2V3_UP }, /* true. */
+ { "un", 0x1, PROCESSOR_V850E2V3_UP }, /* true. */
+};
+
+#define FLOAT_CC_NAME_CNT \
+ (sizeof (float_cc_names) / sizeof (struct reg_name))
+
+
+static const struct reg_name cacheop_names[] =
+{
+ { "cfald", 0x44, PROCESSOR_V850E3V5_UP },
+ { "cfali", 0x40, PROCESSOR_V850E3V5_UP },
+ { "chbid", 0x04, PROCESSOR_V850E3V5_UP },
+ { "chbii", 0x00, PROCESSOR_V850E3V5_UP },
+ { "chbiwbd", 0x06, PROCESSOR_V850E3V5_UP },
+ { "chbwbd", 0x07, PROCESSOR_V850E3V5_UP },
+ { "cibid", 0x24, PROCESSOR_V850E3V5_UP },
+ { "cibii", 0x20, PROCESSOR_V850E3V5_UP },
+ { "cibiwbd", 0x26, PROCESSOR_V850E3V5_UP },
+ { "cibwbd", 0x27, PROCESSOR_V850E3V5_UP },
+ { "cildd", 0x65, PROCESSOR_V850E3V5_UP },
+ { "cildi", 0x61, PROCESSOR_V850E3V5_UP },
+ { "cistd", 0x64, PROCESSOR_V850E3V5_UP },
+ { "cisti", 0x60, PROCESSOR_V850E3V5_UP },
+};
+
+#define CACHEOP_NAME_CNT \
+ (sizeof (cacheop_names) / sizeof (struct reg_name))
+
+static const struct reg_name prefop_names[] =
+{
+ { "prefd", 0x04, PROCESSOR_V850E3V5_UP },
+ { "prefi", 0x00, PROCESSOR_V850E3V5_UP },
+};
+
+#define PREFOP_NAME_CNT \
+ (sizeof (prefop_names) / sizeof (struct reg_name))
+
+static const struct reg_name vector_registers[] =
+{
+ { "vr0", 0, PROCESSOR_V850E3V5_UP },
+ { "vr1", 1, PROCESSOR_V850E3V5_UP },
+ { "vr10", 10, PROCESSOR_V850E3V5_UP },
+ { "vr11", 11, PROCESSOR_V850E3V5_UP },
+ { "vr12", 12, PROCESSOR_V850E3V5_UP },
+ { "vr13", 13, PROCESSOR_V850E3V5_UP },
+ { "vr14", 14, PROCESSOR_V850E3V5_UP },
+ { "vr15", 15, PROCESSOR_V850E3V5_UP },
+ { "vr16", 16, PROCESSOR_V850E3V5_UP },
+ { "vr17", 17, PROCESSOR_V850E3V5_UP },
+ { "vr18", 18, PROCESSOR_V850E3V5_UP },
+ { "vr19", 19, PROCESSOR_V850E3V5_UP },
+ { "vr2", 2, PROCESSOR_V850E3V5_UP },
+ { "vr20", 20, PROCESSOR_V850E3V5_UP },
+ { "vr21", 21, PROCESSOR_V850E3V5_UP },
+ { "vr22", 22, PROCESSOR_V850E3V5_UP },
+ { "vr23", 23, PROCESSOR_V850E3V5_UP },
+ { "vr24", 24, PROCESSOR_V850E3V5_UP },
+ { "vr25", 25, PROCESSOR_V850E3V5_UP },
+ { "vr26", 26, PROCESSOR_V850E3V5_UP },
+ { "vr27", 27, PROCESSOR_V850E3V5_UP },
+ { "vr28", 28, PROCESSOR_V850E3V5_UP },
+ { "vr29", 29, PROCESSOR_V850E3V5_UP },
+ { "vr3", 3, PROCESSOR_V850E3V5_UP },
+ { "vr30", 30, PROCESSOR_V850E3V5_UP },
+ { "vr31", 31, PROCESSOR_V850E3V5_UP },
+ { "vr4", 4, PROCESSOR_V850E3V5_UP },
+ { "vr5", 5, PROCESSOR_V850E3V5_UP },
+ { "vr6", 6, PROCESSOR_V850E3V5_UP },
+ { "vr7", 7, PROCESSOR_V850E3V5_UP },
+ { "vr8", 8, PROCESSOR_V850E3V5_UP },
+ { "vr9", 9, PROCESSOR_V850E3V5_UP },
+};
+
+#define VREG_NAME_CNT \
+ (sizeof (vector_registers) / sizeof (struct reg_name))
+
+/* Do a binary search of the given register table to see if NAME is a
+ valid regiter name. Return the register number from the array on
+ success, or -1 on failure. */
+
+static int
+reg_name_search (const struct reg_name *regs,
+ int regcount,
+ const char *name,
+ bfd_boolean accept_numbers)
+{
+ int middle, low, high;
+ int cmp;
+ symbolS *symbolP;
+
+ /* If the register name is a symbol, then evaluate it. */
+ if ((symbolP = symbol_find (name)) != NULL)
+ {
+ /* If the symbol is an alias for another name then use that.
+ If the symbol is an alias for a number, then return the number. */
+ if (symbol_equated_p (symbolP))
+ name
+ = S_GET_NAME (symbol_get_value_expression (symbolP)->X_add_symbol);
+ else if (accept_numbers)
+ {
+ int reg = S_GET_VALUE (symbolP);
+ return reg;
+ }
+
+ /* Otherwise drop through and try parsing name normally. */
+ }
+
+ low = 0;
+ high = regcount - 1;
+
+ do
+ {
+ middle = (low + high) / 2;
+ cmp = strcasecmp (name, regs[middle].name);
+ if (cmp < 0)
+ high = middle - 1;
+ else if (cmp > 0)
+ low = middle + 1;
+ else
+ return ((regs[middle].processors & processor_mask)
+ ? regs[middle].value
+ : -1);
+ }
+ while (low <= high);
+ return -1;
+}
+
+/* Summary of register_name().
+
+ in: Input_line_pointer points to 1st char of operand.
+
+ out: An expressionS.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in
+ its original state. */
+
+static bfd_boolean
+register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+
+ reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT,
+ name, FALSE);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+
+ return FALSE;
+}
+
+/* Summary of system_register_name().
+
+ in: INPUT_LINE_POINTER points to 1st char of operand.
+ EXPRESSIONP points to an expression structure to be filled in.
+ ACCEPT_NUMBERS is true iff numerical register names may be used.
+
+ out: An expressionS structure in expressionP.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in
+ its original state. */
+
+static bfd_boolean
+system_register_name (expressionS *expressionP,
+ bfd_boolean accept_numbers)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (system_registers, SYSREG_NAME_CNT, name,
+ accept_numbers);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ if (reg_number < 0
+ && accept_numbers)
+ {
+ /* Reset input_line pointer. */
+ input_line_pointer = start;
+
+ if (ISDIGIT (*input_line_pointer))
+ {
+ reg_number = strtol (input_line_pointer, &input_line_pointer, 0);
+ }
+ }
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+
+ return FALSE;
+}
+
+/* Summary of cc_name().
+
+ in: INPUT_LINE_POINTER points to 1st char of operand.
+
+ out: An expressionS.
+ The operand may have been a register: in this case, X_op == O_register,
+ X_add_number is set to the register number, and truth is returned.
+ Input_line_pointer->(next non-blank) char after operand, or is in
+ its original state. */
+
+static bfd_boolean
+cc_name (expressionS *expressionP,
+ bfd_boolean accept_numbers)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (cc_names, CC_NAME_CNT, name, accept_numbers);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ if (reg_number < 0
+ && accept_numbers)
+ {
+ /* Reset input_line pointer. */
+ input_line_pointer = start;
+
+ if (ISDIGIT (*input_line_pointer))
+ {
+ reg_number = strtol (input_line_pointer, &input_line_pointer, 0);
+ }
+ }
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_constant;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+ expressionP->X_add_number = 0;
+
+ return FALSE;
+}
+
+static bfd_boolean
+float_cc_name (expressionS *expressionP,
+ bfd_boolean accept_numbers)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (float_cc_names, FLOAT_CC_NAME_CNT, name, accept_numbers);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ if (reg_number < 0
+ && accept_numbers)
+ {
+ /* Reset input_line pointer. */
+ input_line_pointer = start;
+
+ if (ISDIGIT (*input_line_pointer))
+ {
+ reg_number = strtol (input_line_pointer, &input_line_pointer, 0);
+ }
+ }
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_constant;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+ expressionP->X_add_number = 0;
+
+ return FALSE;
+}
+
+static bfd_boolean
+cacheop_name (expressionS * expressionP,
+ bfd_boolean accept_numbers)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (cacheop_names, CACHEOP_NAME_CNT, name, accept_numbers);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ if (reg_number < 0
+ && accept_numbers)
+ {
+ /* Reset input_line pointer. */
+ input_line_pointer = start;
+
+ if (ISDIGIT (*input_line_pointer))
+ reg_number = strtol (input_line_pointer, &input_line_pointer, 0);
+ }
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_constant;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+ expressionP->X_add_number = 0;
+
+ return FALSE;
+}
+
+static bfd_boolean
+prefop_name (expressionS * expressionP,
+ bfd_boolean accept_numbers)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+ reg_number = reg_name_search (prefop_names, PREFOP_NAME_CNT, name, accept_numbers);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ if (reg_number < 0
+ && accept_numbers)
+ {
+ /* Reset input_line pointer. */
+ input_line_pointer = start;
+
+ if (ISDIGIT (*input_line_pointer))
+ reg_number = strtol (input_line_pointer, &input_line_pointer, 0);
+ }
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_constant;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+ expressionP->X_add_number = 0;
+
+ return FALSE;
+}
+
+static bfd_boolean
+vector_register_name (expressionS *expressionP)
+{
+ int reg_number;
+ char *name;
+ char *start;
+ char c;
+
+ /* Find the spelling of the operand. */
+ start = name = input_line_pointer;
+
+ c = get_symbol_end ();
+
+ reg_number = reg_name_search (vector_registers, VREG_NAME_CNT,
+ name, FALSE);
+
+ /* Put back the delimiting char. */
+ *input_line_pointer = c;
+
+ expressionP->X_add_symbol = NULL;
+ expressionP->X_op_symbol = NULL;
+
+ /* Look to see if it's in the register table. */
+ if (reg_number >= 0)
+ {
+ expressionP->X_op = O_register;
+ expressionP->X_add_number = reg_number;
+
+ return TRUE;
+ }
+
+ /* Reset the line as if we had not done anything. */
+ input_line_pointer = start;
+
+ expressionP->X_op = O_illegal;
+
+ return FALSE;
+}
+
+static void
+skip_white_space (void)
+{
+ while (*input_line_pointer == ' '
+ || *input_line_pointer == '\t')
+ ++input_line_pointer;
+}
+
+/* Summary of parse_register_list ().
+
+ in: INPUT_LINE_POINTER points to 1st char of a list of registers.
+ INSN is the partially constructed instruction.
+ OPERAND is the operand being inserted.
+
+ out: NULL if the parse completed successfully, otherwise a
+ pointer to an error message is returned. If the parse
+ completes the correct bit fields in the instruction
+ will be filled in.
+
+ Parses register lists with the syntax:
+
+ { rX }
+ { rX, rY }
+ { rX - rY }
+ { rX - rY, rZ }
+ etc
+
+ and also parses constant expressions whoes bits indicate the
+ registers in the lists. The LSB in the expression refers to
+ the lowest numbered permissible register in the register list,
+ and so on upwards. System registers are considered to be very
+ high numbers. */
+
+static char *
+parse_register_list (unsigned long *insn,
+ const struct v850_operand *operand)
+{
+ static int type1_regs[32] =
+ {
+ 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 31, 29, 28, 23, 22, 21, 20, 27, 26, 25, 24
+ };
+
+ int *regs;
+ expressionS exp;
+
+ /* Select a register array to parse. */
+ switch (operand->shift)
+ {
+ case 0xffe00001: regs = type1_regs; break;
+ default:
+ as_bad (_("unknown operand shift: %x\n"), operand->shift);
+ return _("internal failure in parse_register_list");
+ }
+
+ skip_white_space ();
+
+ /* If the expression starts with a curly brace it is a register list.
+ Otherwise it is a constant expression, whoes bits indicate which
+ registers are to be included in the list. */
+ if (*input_line_pointer != '{')
+ {
+ int reg;
+ int i;
+
+ expression (&exp);
+
+ if (exp.X_op != O_constant)
+ return _("constant expression or register list expected");
+
+ if (regs == type1_regs)
+ {
+ if (exp.X_add_number & 0xFFFFF000)
+ return _("high bits set in register list expression");
+
+ for (reg = 20; reg < 32; reg++)
+ if (exp.X_add_number & (1 << (reg - 20)))
+ {
+ for (i = 0; i < 32; i++)
+ if (regs[i] == reg)
+ *insn |= (1 << i);
+ }
+ }
+
+ return NULL;
+ }
+
+ input_line_pointer++;
+
+ /* Parse the register list until a terminator (closing curly brace or
+ new-line) is found. */
+ for (;;)
+ {
+ skip_white_space ();
+
+ if (register_name (&exp))
+ {
+ int i;
+
+ /* Locate the given register in the list, and if it is there,
+ insert the corresponding bit into the instruction. */
+ for (i = 0; i < 32; i++)
+ {
+ if (regs[i] == exp.X_add_number)
+ {
+ *insn |= (1 << i);
+ break;
+ }
+ }
+
+ if (i == 32)
+ return _("illegal register included in list");
+ }
+ else if (system_register_name (&exp, TRUE))
+ {
+ if (regs == type1_regs)
+ {
+ return _("system registers cannot be included in list");
+ }
+ }
+
+ if (*input_line_pointer == '}')
+ {
+ input_line_pointer++;
+ break;
+ }
+ else if (*input_line_pointer == ',')
+ {
+ input_line_pointer++;
+ continue;
+ }
+ else if (*input_line_pointer == '-')
+ {
+ /* We have encountered a range of registers: rX - rY. */
+ int j;
+ expressionS exp2;
+
+ /* Skip the dash. */
+ ++input_line_pointer;
+
+ /* Get the second register in the range. */
+ if (! register_name (&exp2))
+ {
+ return _("second register should follow dash in register list");
+ }
+
+ if (exp.X_add_number > exp2.X_add_number)
+ {
+ return _("second register should be greater than first register");
+ }
+
+ /* Add the rest of the registers in the range. */
+ for (j = exp.X_add_number + 1; j <= exp2.X_add_number; j++)
+ {
+ int i;
+
+ /* Locate the given register in the list, and if it is there,
+ insert the corresponding bit into the instruction. */
+ for (i = 0; i < 32; i++)
+ {
+ if (regs[i] == j)
+ {
+ *insn |= (1 << i);
+ break;
+ }
+ }
+
+ if (i == 32)
+ return _("illegal register included in list");
+ }
+
+ exp = exp2;
+ }
+ else
+ break;
+ }
+
+ return NULL;
+}
+
+const char *md_shortopts = "m:";
+
+struct option md_longopts[] =
+{
+#define OPTION_DISP_SIZE_DEFAULT_22 (OPTION_MD_BASE)
+ {"disp-size-default-22", no_argument, NULL, OPTION_DISP_SIZE_DEFAULT_22},
+#define OPTION_DISP_SIZE_DEFAULT_32 (OPTION_MD_BASE + 1)
+ {"disp-size-default-32", no_argument, NULL, OPTION_DISP_SIZE_DEFAULT_32},
+ {NULL, no_argument, NULL, 0}
+};
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _(" V850 options:\n"));
+ fprintf (stream, _(" -mwarn-signed-overflow Warn if signed immediate values overflow\n"));
+ fprintf (stream, _(" -mwarn-unsigned-overflow Warn if unsigned immediate values overflow\n"));
+ fprintf (stream, _(" -mv850 The code is targeted at the v850\n"));
+ fprintf (stream, _(" -mv850e The code is targeted at the v850e\n"));
+ fprintf (stream, _(" -mv850e1 The code is targeted at the v850e1\n"));
+ fprintf (stream, _(" -mv850e2 The code is targeted at the v850e2\n"));
+ fprintf (stream, _(" -mv850e2v3 The code is targeted at the v850e2v3\n"));
+ fprintf (stream, _(" -mv850e2v4 Alias for -mv850e3v5\n"));
+ fprintf (stream, _(" -mv850e3v5 The code is targeted at the v850e3v5\n"));
+ fprintf (stream, _(" -mrelax Enable relaxation\n"));
+ fprintf (stream, _(" --disp-size-default-22 branch displacement with unknown size is 22 bits (default)\n"));
+ fprintf (stream, _(" --disp-size-default-32 branch displacement with unknown size is 32 bits\n"));
+ fprintf (stream, _(" -mextension enable extension opcode support\n"));
+ fprintf (stream, _(" -mno-bcond17 disable b<cond> disp17 instruction\n"));
+ fprintf (stream, _(" -mno-stld23 disable st/ld offset23 instruction\n"));
+ fprintf (stream, _(" -mgcc-abi Mark the binary as using the old GCC ABI\n"));
+ fprintf (stream, _(" -mrh850-abi Mark the binary as using the RH850 ABI (default)\n"));
+ fprintf (stream, _(" -m8byte-align Mark the binary as using 64-bit alignment\n"));
+ fprintf (stream, _(" -m4byte-align Mark the binary as using 32-bit alignment (default)\n"));
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ if (c != 'm')
+ {
+ switch (c)
+ {
+ case OPTION_DISP_SIZE_DEFAULT_22:
+ default_disp_size = 22;
+ return 1;
+
+ case OPTION_DISP_SIZE_DEFAULT_32:
+ default_disp_size = 32;
+ return 1;
+ }
+ return 0;
+ }
+
+ if (strcmp (arg, "warn-signed-overflow") == 0)
+ warn_signed_overflows = TRUE;
+
+ else if (strcmp (arg, "warn-unsigned-overflow") == 0)
+ warn_unsigned_overflows = TRUE;
+
+ else if (strcmp (arg, "v850") == 0)
+ {
+ machine = 0;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850);
+ }
+ else if (strcmp (arg, "v850e") == 0)
+ {
+ machine = bfd_mach_v850e;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E);
+ }
+ else if (strcmp (arg, "v850e1") == 0)
+ {
+ machine = bfd_mach_v850e1;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E1);
+ }
+ else if (strcmp (arg, "v850e2") == 0)
+ {
+ machine = bfd_mach_v850e2;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E2);
+ }
+ else if (strcmp (arg, "v850e2v3") == 0)
+ {
+ machine = bfd_mach_v850e2v3;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E2V3);
+ }
+ else if (strcmp (arg, "v850e2v4") == 0)
+ {
+ machine = bfd_mach_v850e3v5;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E3V5);
+ }
+ else if (strcmp (arg, "v850e3v5") == 0)
+ {
+ machine = bfd_mach_v850e3v5;
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E3V5);
+ }
+ else if (strcmp (arg, "extension") == 0)
+ {
+ processor_mask |= PROCESSOR_OPTION_EXTENSION | PROCESSOR_OPTION_ALIAS;
+ }
+ else if (strcmp (arg, "no-bcond17") == 0)
+ {
+ no_bcond17 = 1;
+ }
+ else if (strcmp (arg, "no-stld23") == 0)
+ {
+ no_stld23 = 1;
+ }
+ else if (strcmp (arg, "relax") == 0)
+ v850_relax = 1;
+ else if (strcmp (arg, "gcc-abi") == 0)
+ {
+ v850_target_arch = bfd_arch_v850;
+ v850_target_format = "elf32-v850";
+ }
+ else if (strcmp (arg, "rh850-abi") == 0)
+ {
+ v850_target_arch = bfd_arch_v850_rh850;
+ v850_target_format = "elf32-v850-rh850";
+ }
+ else if (strcmp (arg, "8byte-align") == 0)
+ v850_e_flags |= EF_RH850_DATA_ALIGN8;
+ else if (strcmp (arg, "4byte-align") == 0)
+ v850_e_flags &= ~ EF_RH850_DATA_ALIGN8;
+ else
+ return 0;
+
+ return 1;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litp, int *sizep)
+{
+ return ieee_md_atof (type, litp, sizep, FALSE);
+}
+
+/* Very gross. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ fragS *fragP)
+{
+ union u
+ {
+ bfd_reloc_code_real_type fx_r_type;
+ char * fr_opcode;
+ }
+ opcode_converter;
+ subseg_change (sec, 0);
+
+ opcode_converter.fr_opcode = fragP->fr_opcode;
+
+ subseg_change (sec, 0);
+
+ if (fragP->fr_subtype == SUBYPTE_LOOP_16_22)
+ {
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ BFD_RELOC_UNUSED + opcode_converter.fx_r_type);
+ fragP->fr_fix += 4;
+ }
+ else if (fragP->fr_subtype == SUBYPTE_LOOP_16_22 + 1)
+ {
+ unsigned char * buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+ int loop_reg = (buffer[0] & 0x1f);
+
+ /* Add -1.reg. */
+ md_number_to_chars ((char *) buffer, 0x025f | (loop_reg << 11), 2);
+ /* Now create the conditional branch + fixup to the final target. */
+ /* 0x000107ea = bne LBL(disp17). */
+ md_number_to_chars ((char *) buffer + 2, 0x000107ea, 4);
+ fix_new (fragP, fragP->fr_fix+2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ BFD_RELOC_V850_17_PCREL);
+ fragP->fr_fix += 6;
+ }
+ /* In range conditional or unconditional branch. */
+ else if (fragP->fr_subtype == SUBYPTE_COND_9_22
+ || fragP->fr_subtype == SUBYPTE_UNCOND_9_22
+ || fragP->fr_subtype == SUBYPTE_COND_9_22_32
+ || fragP->fr_subtype == SUBYPTE_UNCOND_9_22_32
+ || fragP->fr_subtype == SUBYPTE_COND_9_17_22
+ || fragP->fr_subtype == SUBYPTE_COND_9_17_22_32
+ || fragP->fr_subtype == SUBYPTE_SA_9_22
+ || fragP->fr_subtype == SUBYPTE_SA_9_22_32
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22_32)
+
+ {
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ BFD_RELOC_UNUSED + opcode_converter.fx_r_type);
+ fragP->fr_fix += 2;
+ }
+ /* V850e2r-v3 17bit conditional branch. */
+ else if (fragP->fr_subtype == SUBYPTE_COND_9_17_22 + 1
+ || fragP->fr_subtype == SUBYPTE_COND_9_17_22_32 + 1
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22 + 1
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22_32 + 1)
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ buffer[0] &= 0x0f; /* Use condition. */
+ buffer[0] |= 0xe0;
+ buffer[1] = 0x07;
+
+ /* Now create the unconditional branch + fixup to the final
+ target. */
+ md_number_to_chars ((char *) buffer + 2, 0x0001, 2);
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_V850_17_PCREL);
+ fragP->fr_fix += 4;
+ }
+ /* Out of range conditional branch. Emit a branch around a 22bit jump. */
+ else if (fragP->fr_subtype == SUBYPTE_COND_9_22 + 1
+ || fragP->fr_subtype == SUBYPTE_COND_9_22_32 + 1
+ || fragP->fr_subtype == SUBYPTE_COND_9_17_22 + 2
+ || fragP->fr_subtype == SUBYPTE_COND_9_17_22_32 + 2)
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ /* Reverse the condition of the first branch. */
+ buffer[0] ^= 0x08;
+ /* Mask off all the displacement bits. */
+ buffer[0] &= 0x8f;
+ buffer[1] &= 0x07;
+ /* Now set the displacement bits so that we branch
+ around the unconditional branch. */
+ buffer[0] |= 0x30;
+
+ /* Now create the unconditional branch + fixup to the final
+ target. */
+ md_number_to_chars ((char *) buffer + 2, 0x00000780, 4);
+ fix_new (fragP, fragP->fr_fix + 2, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_V850_22_PCREL);
+ fragP->fr_fix += 6;
+ }
+ /* Out of range conditional branch. Emit a branch around a 32bit jump. */
+ else if (fragP->fr_subtype == SUBYPTE_COND_9_22_32 + 2
+ || fragP->fr_subtype == SUBYPTE_COND_9_17_22_32 + 3)
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ /* Reverse the condition of the first branch. */
+ buffer[0] ^= 0x08;
+ /* Mask off all the displacement bits. */
+ buffer[0] &= 0x8f;
+ buffer[1] &= 0x07;
+ /* Now set the displacement bits so that we branch
+ around the unconditional branch. */
+ buffer[0] |= 0x40;
+
+ /* Now create the unconditional branch + fixup to the final
+ target. */
+ md_number_to_chars ((char *) buffer + 2, 0x02e0, 2);
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_V850_32_PCREL);
+ fragP->fr_fix += 8;
+ }
+ /* Out of range unconditional branch. Emit a 22bit jump. */
+ else if (fragP->fr_subtype == SUBYPTE_UNCOND_9_22 + 1
+ || fragP->fr_subtype == SUBYPTE_UNCOND_9_22_32 + 1)
+ {
+ md_number_to_chars (fragP->fr_fix + fragP->fr_literal, 0x00000780, 4);
+ fix_new (fragP, fragP->fr_fix, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, BFD_RELOC_V850_22_PCREL);
+ fragP->fr_fix += 4;
+ }
+ /* Out of range unconditional branch. Emit a 32bit jump. */
+ else if (fragP->fr_subtype == SUBYPTE_UNCOND_9_22_32 + 2)
+ {
+ md_number_to_chars (fragP->fr_fix + fragP->fr_literal, 0x02e0, 2);
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_V850_32_PCREL);
+ fragP->fr_fix += 6;
+ }
+ /* Out of range SA conditional branch. Emit a branch to a 22bit jump. */
+ else if (fragP->fr_subtype == SUBYPTE_SA_9_22 + 1
+ || fragP->fr_subtype == SUBYPTE_SA_9_22_32 + 1
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22 + 2
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22_32 + 2)
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ /* bsa .+4 */
+ buffer[0] &= 0x8f;
+ buffer[0] |= 0x20;
+ buffer[1] &= 0x07;
+
+ /* br .+6 */
+ md_number_to_chars ((char *) buffer + 2, 0x05b5, 2);
+
+ /* Now create the unconditional branch + fixup to the final
+ target. */
+ /* jr SYM */
+ md_number_to_chars ((char *) buffer + 4, 0x00000780, 4);
+ fix_new (fragP, fragP->fr_fix + 4, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1,
+ BFD_RELOC_V850_22_PCREL);
+ fragP->fr_fix += 8;
+ }
+ /* Out of range SA conditional branch. Emit a branch around a 32bit jump. */
+ else if (fragP->fr_subtype == SUBYPTE_SA_9_22_32 + 2
+ || fragP->fr_subtype == SUBYPTE_SA_9_17_22_32 + 3)
+ {
+ unsigned char *buffer =
+ (unsigned char *) (fragP->fr_fix + fragP->fr_literal);
+
+ /* bsa .+2 */
+ buffer[0] &= 0x8f;
+ buffer[0] |= 0x20;
+ buffer[1] &= 0x07;
+
+ /* br .+8 */
+ md_number_to_chars ((char *) buffer + 2, 0x05c5, 2);
+
+ /* Now create the unconditional branch + fixup to the final
+ target. */
+ /* jr SYM */
+ md_number_to_chars ((char *) buffer + 4, 0x02e0, 2);
+ fix_new (fragP, fragP->fr_fix + 6, 4, fragP->fr_symbol,
+ fragP->fr_offset + 2, 1, BFD_RELOC_V850_32_PCREL);
+
+ fragP->fr_fix += 10;
+ }
+ else
+ abort ();
+}
+
+valueT
+md_section_align (asection *seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_begin (void)
+{
+ char *prev_name = "";
+ const struct v850_opcode *op;
+
+ if (strncmp (TARGET_CPU, "v850e3v5", 8) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e3v5;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E3V5);
+ }
+ else if (strncmp (TARGET_CPU, "v850e2v4", 8) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e3v5;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E3V5);
+ }
+ else if (strncmp (TARGET_CPU, "v850e2v3", 8) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e2v3;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E2V3);
+ }
+ else if (strncmp (TARGET_CPU, "v850e2", 6) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e2;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E2);
+ }
+ else if (strncmp (TARGET_CPU, "v850e1", 6) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e1;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E1);
+ }
+ else if (strncmp (TARGET_CPU, "v850e", 5) == 0)
+ {
+ if (machine == -1)
+ machine = bfd_mach_v850e;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850E);
+ }
+ else if (strncmp (TARGET_CPU, "v850", 4) == 0)
+ {
+ if (machine == -1)
+ machine = 0;
+
+ if (!processor_mask)
+ SET_PROCESSOR_MASK (processor_mask, PROCESSOR_V850);
+ }
+ else
+ /* xgettext:c-format */
+ as_bad (_("Unable to determine default target processor from string: %s"),
+ TARGET_CPU);
+
+ v850_hash = hash_new ();
+
+ /* Insert unique names into hash table. The V850 instruction set
+ has many identical opcode names that have different opcodes based
+ on the operands. This hash table then provides a quick index to
+ the first opcode with a particular name in the opcode table. */
+ op = v850_opcodes;
+ while (op->name)
+ {
+ if (strcmp (prev_name, op->name))
+ {
+ prev_name = (char *) op->name;
+ hash_insert (v850_hash, op->name, (char *) op);
+ }
+ op++;
+ }
+
+ v850_seg_table[BSS_SECTION].s = bss_section;
+ bfd_set_arch_mach (stdoutput, v850_target_arch, machine);
+ bfd_set_private_flags (stdoutput, v850_e_flags);
+}
+
+
+static bfd_reloc_code_real_type
+handle_hi016 (const struct v850_operand *operand, const char **errmsg)
+{
+ if (operand == NULL)
+ return BFD_RELOC_HI16;
+
+ if (operand->default_reloc == BFD_RELOC_HI16)
+ return BFD_RELOC_HI16;
+
+ if (operand->default_reloc == BFD_RELOC_HI16_S)
+ return BFD_RELOC_HI16;
+
+ if (operand->default_reloc == BFD_RELOC_16)
+ return BFD_RELOC_HI16;
+
+ *errmsg = _("hi0() relocation used on an instruction which does "
+ "not support it");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+}
+
+static bfd_reloc_code_real_type
+handle_hi16 (const struct v850_operand *operand, const char **errmsg)
+{
+ if (operand == NULL)
+ return BFD_RELOC_HI16_S;
+
+ if (operand->default_reloc == BFD_RELOC_HI16_S)
+ return BFD_RELOC_HI16_S;
+
+ if (operand->default_reloc == BFD_RELOC_HI16)
+ return BFD_RELOC_HI16_S;
+
+ if (operand->default_reloc == BFD_RELOC_16)
+ return BFD_RELOC_HI16_S;
+
+ *errmsg = _("hi() relocation used on an instruction which does "
+ "not support it");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+}
+
+static bfd_reloc_code_real_type
+handle_lo16 (const struct v850_operand *operand, const char **errmsg)
+{
+ if (operand == NULL)
+ return BFD_RELOC_LO16;
+
+ if (operand->default_reloc == BFD_RELOC_LO16)
+ return BFD_RELOC_LO16;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_SPLIT_OFFSET)
+ return BFD_RELOC_V850_LO16_SPLIT_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_S1)
+ return BFD_RELOC_V850_LO16_S1;
+
+ if (operand->default_reloc == BFD_RELOC_16)
+ return BFD_RELOC_LO16;
+
+ *errmsg = _("lo() relocation used on an instruction which does "
+ "not support it");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+}
+
+static bfd_reloc_code_real_type
+handle_ctoff (const struct v850_operand *operand, const char **errmsg)
+{
+ if (v850_target_arch == bfd_arch_v850_rh850)
+ {
+ *errmsg = _("ctoff() is not supported by the rh850 ABI. Use -mgcc-abi instead");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+ }
+
+ if (operand == NULL)
+ return BFD_RELOC_V850_CALLT_16_16_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_V850_CALLT_6_7_OFFSET)
+ return operand->default_reloc;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_S1)
+ return BFD_RELOC_V850_CALLT_15_16_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_16)
+ return BFD_RELOC_V850_CALLT_16_16_OFFSET;
+
+ *errmsg = _("ctoff() relocation used on an instruction which does not support it");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+}
+
+static bfd_reloc_code_real_type
+handle_sdaoff (const struct v850_operand *operand, const char **errmsg)
+{
+ if (operand == NULL)
+ return BFD_RELOC_V850_SDA_16_16_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_SPLIT_OFFSET)
+ return BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_16)
+ return BFD_RELOC_V850_SDA_16_16_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_S1)
+ return BFD_RELOC_V850_SDA_15_16_OFFSET;
+
+ *errmsg = _("sdaoff() relocation used on an instruction which does not support it");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+}
+
+static bfd_reloc_code_real_type
+handle_zdaoff (const struct v850_operand *operand, const char **errmsg)
+{
+ if (operand == NULL)
+ return BFD_RELOC_V850_ZDA_16_16_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_SPLIT_OFFSET)
+ return BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_16)
+ return BFD_RELOC_V850_ZDA_16_16_OFFSET;
+
+ if (operand->default_reloc == BFD_RELOC_V850_16_S1)
+ return BFD_RELOC_V850_ZDA_15_16_OFFSET;
+
+ *errmsg = _("zdaoff() relocation used on an instruction which does not support it");
+ return BFD_RELOC_64; /* Used to indicate an error condition. */
+}
+
+static bfd_reloc_code_real_type
+handle_tdaoff (const struct v850_operand *operand, const char **errmsg)
+{
+ if (operand == NULL)
+ /* Data item, not an instruction. */
+ return BFD_RELOC_V850_TDA_16_16_OFFSET;
+
+ switch (operand->default_reloc)
+ {
+ /* sld.hu, operand: D5-4. */
+ case BFD_RELOC_V850_TDA_4_5_OFFSET:
+ /* sld.bu, operand: D4. */
+ case BFD_RELOC_V850_TDA_4_4_OFFSET:
+ /* sld.w/sst.w, operand: D8_6. */
+ case BFD_RELOC_V850_TDA_6_8_OFFSET:
+ /* sld.h/sst.h, operand: D8_7. */
+ case BFD_RELOC_V850_TDA_7_8_OFFSET:
+ /* sld.b/sst.b, operand: D7. */
+ case BFD_RELOC_V850_TDA_7_7_OFFSET:
+ return operand->default_reloc;
+ default:
+ break;
+ }
+
+ if (operand->default_reloc == BFD_RELOC_16 && operand->shift == 16)
+ /* set1 & chums, operands: D16. */
+ return BFD_RELOC_V850_TDA_16_16_OFFSET;
+
+ *errmsg = _("tdaoff() relocation used on an instruction which does not support it");
+ /* Used to indicate an error condition. */
+ return BFD_RELOC_64;
+}
+
+/* Warning: The code in this function relies upon the definitions
+ in the v850_operands[] array (defined in opcodes/v850-opc.c)
+ matching the hard coded values contained herein. */
+
+static bfd_reloc_code_real_type
+v850_reloc_prefix (const struct v850_operand *operand, const char **errmsg)
+{
+ bfd_boolean paren_skipped = FALSE;
+
+ /* Skip leading opening parenthesis. */
+ if (*input_line_pointer == '(')
+ {
+ ++input_line_pointer;
+ paren_skipped = TRUE;
+ }
+
+#define CHECK_(name, reloc) \
+ if (strncmp (input_line_pointer, name "(", strlen (name) + 1) == 0) \
+ { \
+ input_line_pointer += strlen (name); \
+ return reloc; \
+ }
+
+ CHECK_ ("hi0", handle_hi016(operand, errmsg) );
+ CHECK_ ("hi", handle_hi16(operand, errmsg) );
+ CHECK_ ("lo", handle_lo16 (operand, errmsg) );
+ CHECK_ ("sdaoff", handle_sdaoff (operand, errmsg));
+ CHECK_ ("zdaoff", handle_zdaoff (operand, errmsg));
+ CHECK_ ("tdaoff", handle_tdaoff (operand, errmsg));
+ CHECK_ ("hilo", BFD_RELOC_32);
+ CHECK_ ("lo23", BFD_RELOC_V850_23);
+ CHECK_ ("ctoff", handle_ctoff (operand, errmsg) );
+
+ /* Restore skipped parenthesis. */
+ if (paren_skipped)
+ --input_line_pointer;
+
+ return BFD_RELOC_NONE;
+}
+
+/* Insert an operand value into an instruction. */
+
+static unsigned long
+v850_insert_operand (unsigned long insn,
+ const struct v850_operand *operand,
+ offsetT val,
+ const char **errmsg)
+{
+ if (operand->insert)
+ {
+ const char *message = NULL;
+
+ insn = operand->insert (insn, val, &message);
+ if (message != NULL)
+ {
+ if ((operand->flags & V850_OPERAND_SIGNED)
+ && ! warn_signed_overflows
+ && v850_msg_is_out_of_range (message))
+ {
+ /* Skip warning... */
+ }
+ else if ((operand->flags & V850_OPERAND_SIGNED) == 0
+ && ! warn_unsigned_overflows
+ && v850_msg_is_out_of_range (message))
+ {
+ /* Skip warning... */
+ }
+ else
+ {
+ if (errmsg != NULL)
+ *errmsg = message;
+ }
+ }
+ }
+ else if (operand->bits == -1
+ || operand->flags & V850E_IMMEDIATE16
+ || operand->flags & V850E_IMMEDIATE23
+ || operand->flags & V850E_IMMEDIATE32)
+ {
+ abort ();
+ }
+ else
+ {
+ if (operand->bits < 32)
+ {
+ long min, max;
+
+ if ((operand->flags & V850_OPERAND_SIGNED) != 0)
+ {
+ if (! warn_signed_overflows)
+ max = (1 << operand->bits) - 1;
+ else
+ max = (1 << (operand->bits - 1)) - 1;
+
+ min = -(1 << (operand->bits - 1));
+ }
+ else
+ {
+ max = (1 << operand->bits) - 1;
+
+ if (! warn_unsigned_overflows)
+ min = -(1 << (operand->bits - 1));
+ else
+ min = 0;
+ }
+
+ /* Some people write constants with the sign extension done by
+ hand but only up to 32 bits. This shouldn't really be valid,
+ but, to permit this code to assemble on a 64-bit host, we
+ sign extend the 32-bit value to 64 bits if so doing makes the
+ value valid. */
+ if (val > max
+ && (offsetT) (val - 0x80000000 - 0x80000000) >= min
+ && (offsetT) (val - 0x80000000 - 0x80000000) <= max)
+ val = val - 0x80000000 - 0x80000000;
+
+ /* Similarly, people write expressions like ~(1<<15), and expect
+ this to be OK for a 32-bit unsigned value. */
+ else if (val < min
+ && (offsetT) (val + 0x80000000 + 0x80000000) >= min
+ && (offsetT) (val + 0x80000000 + 0x80000000) <= max)
+ val = val + 0x80000000 + 0x80000000;
+
+ else if (val < (offsetT) min || val > (offsetT) max)
+ {
+ static char buf [128];
+
+ /* Restore min and mix to expected values for decimal ranges. */
+ if ((operand->flags & V850_OPERAND_SIGNED)
+ && ! warn_signed_overflows)
+ max = (1 << (operand->bits - 1)) - 1;
+
+ if (! (operand->flags & V850_OPERAND_SIGNED)
+ && ! warn_unsigned_overflows)
+ min = 0;
+
+ sprintf (buf, _("operand out of range (%d is not between %d and %d)"),
+ (int) val, (int) min, (int) max);
+ *errmsg = buf;
+ }
+
+ insn |= (((long) val & ((1 << operand->bits) - 1)) << operand->shift);
+ }
+ else
+ {
+ insn |= (((long) val) << operand->shift);
+ }
+ }
+
+ return insn;
+}
+
+static char copy_of_instruction[128];
+
+void
+md_assemble (char *str)
+{
+ char *s;
+ char *start_of_operands;
+ struct v850_opcode *opcode;
+ struct v850_opcode *next_opcode;
+ const unsigned char *opindex_ptr;
+ int next_opindex;
+ int relaxable = 0;
+ unsigned long insn;
+ unsigned long insn_size;
+ char *f = NULL;
+ int i;
+ int match;
+ bfd_boolean extra_data_after_insn = FALSE;
+ unsigned extra_data_len = 0;
+ unsigned long extra_data = 0;
+ char *saved_input_line_pointer;
+ char most_match_errmsg[1024];
+ int most_match_count = -1;
+
+ strncpy (copy_of_instruction, str, sizeof (copy_of_instruction) - 1);
+ most_match_errmsg[0] = 0;
+
+ /* Get the opcode. */
+ for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
+ continue;
+
+ if (*s != '\0')
+ *s++ = '\0';
+
+ /* Find the first opcode with the proper name. */
+ opcode = (struct v850_opcode *) hash_find (v850_hash, str);
+ if (opcode == NULL)
+ {
+ /* xgettext:c-format */
+ as_bad (_("Unrecognized opcode: `%s'"), str);
+ ignore_rest_of_line ();
+ return;
+ }
+
+ str = s;
+ while (ISSPACE (*str))
+ ++str;
+
+ start_of_operands = str;
+
+ saved_input_line_pointer = input_line_pointer;
+
+ for (;;)
+ {
+ const char *errmsg = NULL;
+ const char *warningmsg = NULL;
+
+ match = 0;
+ opindex_ptr = opcode->operands;
+
+ if (no_stld23)
+ {
+ if ((strncmp (opcode->name, "st.", 3) == 0
+ && v850_operands[opcode->operands[1]].bits == 23)
+ || (strncmp (opcode->name, "ld.", 3) == 0
+ && v850_operands[opcode->operands[0]].bits == 23))
+ {
+ errmsg = _("st/ld offset 23 instruction was disabled .");
+ goto error;
+ }
+ }
+
+ if ((opcode->processors & processor_mask & PROCESSOR_MASK) == 0
+ || (((opcode->processors & ~PROCESSOR_MASK) != 0)
+ && ((opcode->processors & processor_mask & ~PROCESSOR_MASK) == 0)))
+ {
+ errmsg = _("Target processor does not support this instruction.");
+ goto error;
+ }
+
+ relaxable = 0;
+ fc = 0;
+ next_opindex = 0;
+ insn = opcode->opcode;
+ extra_data_len = 0;
+ extra_data_after_insn = FALSE;
+
+ input_line_pointer = str = start_of_operands;
+
+ for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
+ {
+ const struct v850_operand *operand;
+ char *hold;
+ expressionS ex;
+ bfd_reloc_code_real_type reloc;
+
+ if (next_opindex == 0)
+ operand = &v850_operands[*opindex_ptr];
+ else
+ {
+ operand = &v850_operands[next_opindex];
+ next_opindex = 0;
+ }
+
+ errmsg = NULL;
+
+ while (*str == ' ')
+ ++str;
+
+ if (operand->flags & V850_OPERAND_BANG
+ && *str == '!')
+ ++str;
+ else if (operand->flags & V850_OPERAND_PERCENT
+ && *str == '%')
+ ++str;
+
+ if (*str == ',' || *str == '[' || *str == ']')
+ ++str;
+
+ while (*str == ' ')
+ ++str;
+
+ if ( (strcmp (opcode->name, "pushsp") == 0
+ || strcmp (opcode->name, "popsp") == 0
+ || strcmp (opcode->name, "dbpush") == 0)
+ && (*str == '-'))
+ ++str;
+
+ if (operand->flags & V850_OPERAND_RELAX)
+ relaxable = 1;
+
+ /* Gather the operand. */
+ hold = input_line_pointer;
+ input_line_pointer = str;
+
+ /* lo(), hi(), hi0(), etc... */
+ if ((reloc = v850_reloc_prefix (operand, &errmsg)) != BFD_RELOC_NONE)
+ {
+ /* This is a fake reloc, used to indicate an error condition. */
+ if (reloc == BFD_RELOC_64)
+ {
+ /* match = 1; */
+ goto error;
+ }
+
+ expression (&ex);
+
+ if (ex.X_op == O_constant)
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_V850_ZDA_16_16_OFFSET:
+ case BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET:
+ case BFD_RELOC_V850_ZDA_15_16_OFFSET:
+ /* To cope with "not1 7, zdaoff(0xfffff006)[r0]"
+ and the like. */
+ /* Fall through. */
+
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_V850_LO16_S1:
+ case BFD_RELOC_V850_LO16_SPLIT_OFFSET:
+ {
+ /* Truncate, then sign extend the value. */
+ ex.X_add_number = SEXT16 (ex.X_add_number);
+ break;
+ }
+
+ case BFD_RELOC_HI16:
+ {
+ /* Truncate, then sign extend the value. */
+ ex.X_add_number = SEXT16 (ex.X_add_number >> 16);
+ break;
+ }
+
+ case BFD_RELOC_HI16_S:
+ {
+ /* Truncate, then sign extend the value. */
+ int temp = (ex.X_add_number >> 16) & 0xffff;
+
+ temp += (ex.X_add_number >> 15) & 1;
+
+ ex.X_add_number = SEXT16 (temp);
+ break;
+ }
+
+ case BFD_RELOC_V850_23:
+ if ((operand->flags & V850E_IMMEDIATE23) == 0)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ break;
+
+ case BFD_RELOC_32:
+ case BFD_RELOC_V850_32_ABS:
+ case BFD_RELOC_V850_32_PCREL:
+ if ((operand->flags & V850E_IMMEDIATE32) == 0)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+
+ break;
+
+ default:
+ as_bad (_("AAARG -> unhandled constant reloc: %d"), reloc);
+ break;
+ }
+
+ if (operand->flags & V850E_IMMEDIATE32)
+ {
+ extra_data_after_insn = TRUE;
+ extra_data_len = 4;
+ extra_data = 0;
+ }
+ else if (operand->flags & V850E_IMMEDIATE23)
+ {
+ if (reloc != BFD_RELOC_V850_23)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ extra_data_after_insn = TRUE;
+ extra_data_len = 2;
+ extra_data = 0;
+ }
+ else if ((operand->flags & V850E_IMMEDIATE16)
+ || (operand->flags & V850E_IMMEDIATE16HI))
+ {
+ if (operand->flags & V850E_IMMEDIATE16HI
+ && reloc != BFD_RELOC_HI16
+ && reloc != BFD_RELOC_HI16_S)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ else if (operand->flags & V850E_IMMEDIATE16
+ && reloc != BFD_RELOC_LO16)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+
+ extra_data_after_insn = TRUE;
+ extra_data_len = 2;
+ extra_data = 0;
+ }
+
+ if (fc > MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ fc++;
+ }
+ else /* ex.X_op != O_constant. */
+ {
+ if ((reloc == BFD_RELOC_32
+ || reloc == BFD_RELOC_V850_32_ABS
+ || reloc == BFD_RELOC_V850_32_PCREL)
+ && operand->bits < 32)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ else if (reloc == BFD_RELOC_V850_23
+ && (operand->flags & V850E_IMMEDIATE23) == 0)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ else if ((reloc == BFD_RELOC_HI16
+ || reloc == BFD_RELOC_HI16_S)
+ && operand->bits < 16)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+
+ if (operand->flags & V850E_IMMEDIATE32)
+ {
+ extra_data_after_insn = TRUE;
+ extra_data_len = 4;
+ extra_data = 0;
+ }
+ else if (operand->flags & V850E_IMMEDIATE23)
+ {
+ if (reloc != BFD_RELOC_V850_23)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ extra_data_after_insn = TRUE;
+ extra_data_len = 2;
+ extra_data = 0;
+ }
+ else if ((operand->flags & V850E_IMMEDIATE16)
+ || (operand->flags & V850E_IMMEDIATE16HI))
+ {
+ if (operand->flags & V850E_IMMEDIATE16HI
+ && reloc != BFD_RELOC_HI16
+ && reloc != BFD_RELOC_HI16_S)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+ else if (operand->flags & V850E_IMMEDIATE16
+ && reloc != BFD_RELOC_LO16)
+ {
+ errmsg = _("immediate operand is too large");
+ goto error;
+ }
+
+ extra_data_after_insn = TRUE;
+ extra_data_len = 2;
+ extra_data = 0;
+ }
+
+ if (fc > MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = reloc;
+ fc++;
+ }
+ }
+ else if (operand->flags & V850E_IMMEDIATE16
+ || operand->flags & V850E_IMMEDIATE16HI)
+ {
+ expression (&ex);
+
+ switch (ex.X_op)
+ {
+ case O_constant:
+ if (operand->flags & V850E_IMMEDIATE16HI)
+ {
+ if (ex.X_add_number & 0xffff)
+ {
+ errmsg = _("constant too big to fit into instruction");
+ goto error;
+ }
+
+ ex.X_add_number >>= 16;
+ }
+ if (operand->flags & V850E_IMMEDIATE16)
+ {
+ if ((ex.X_add_number & 0xffff8000)
+ && ((ex.X_add_number & 0xffff8000) != 0xffff8000))
+ {
+ errmsg = _("constant too big to fit into instruction");
+ goto error;
+ }
+ }
+ break;
+
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+
+ default:
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = operand->default_reloc;
+ ++fc;
+
+ ex.X_add_number = 0;
+ break;
+ }
+
+ extra_data_after_insn = TRUE;
+ extra_data_len = 2;
+ extra_data = ex.X_add_number;
+ }
+ else if (operand->flags & V850E_IMMEDIATE23)
+ {
+ expression (&ex);
+
+ switch (ex.X_op)
+ {
+ case O_constant:
+ break;
+
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+
+ default:
+ break;
+ }
+
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = operand->default_reloc;
+ ++fc;
+
+ extra_data_after_insn = TRUE;
+ extra_data_len = 2;
+ extra_data = 0;
+ }
+ else if (operand->flags & V850E_IMMEDIATE32)
+ {
+ expression (&ex);
+
+ switch (ex.X_op)
+ {
+ case O_constant:
+ if ((operand->default_reloc == BFD_RELOC_V850_32_ABS
+ || operand->default_reloc == BFD_RELOC_V850_32_PCREL)
+ && (ex.X_add_number & 1))
+ {
+ errmsg = _("odd number cannot be used here");
+ goto error;
+ }
+ break;
+
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+
+ default:
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = operand->default_reloc;
+ ++fc;
+
+ ex.X_add_number = 0;
+ break;
+ }
+
+ extra_data_after_insn = TRUE;
+ extra_data_len = 4;
+ extra_data = ex.X_add_number;
+ }
+ else if (operand->flags & V850E_OPERAND_REG_LIST)
+ {
+ errmsg = parse_register_list (&insn, operand);
+
+ if (errmsg)
+ goto error;
+ }
+ else
+ {
+ errmsg = NULL;
+
+ if ((operand->flags & V850_OPERAND_REG) != 0)
+ {
+ if (!register_name (&ex))
+ {
+ errmsg = _("invalid register name");
+ }
+
+ if ((operand->flags & V850_NOT_R0)
+ && ex.X_add_number == 0)
+ {
+ errmsg = _("register r0 cannot be used here");
+ }
+
+ if (operand->flags & V850_REG_EVEN)
+ {
+ if (ex.X_add_number % 2)
+ errmsg = _("odd register cannot be used here");
+ ex.X_add_number = ex.X_add_number / 2;
+ }
+
+ }
+ else if ((operand->flags & V850_OPERAND_SRG) != 0)
+ {
+ if (!system_register_name (&ex, TRUE))
+ {
+ errmsg = _("invalid system register name");
+ }
+ }
+ else if ((operand->flags & V850_OPERAND_EP) != 0)
+ {
+ char *start = input_line_pointer;
+ char c = get_symbol_end ();
+
+ if (strcmp (start, "ep") != 0 && strcmp (start, "r30") != 0)
+ {
+ /* Put things back the way we found them. */
+ *input_line_pointer = c;
+ input_line_pointer = start;
+ errmsg = _("expected EP register");
+ goto error;
+ }
+
+ *input_line_pointer = c;
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ','
+ || *str == '[' || *str == ']')
+ ++str;
+ continue;
+ }
+ else if ((operand->flags & V850_OPERAND_CC) != 0)
+ {
+ if (!cc_name (&ex, TRUE))
+ {
+ errmsg = _("invalid condition code name");
+ }
+
+ if ((operand->flags & V850_NOT_SA)
+ && ex.X_add_number == COND_SA_NUM)
+ {
+ errmsg = _("condition sa cannot be used here");
+ }
+ }
+ else if ((operand->flags & V850_OPERAND_FLOAT_CC) != 0)
+ {
+ if (!float_cc_name (&ex, TRUE))
+ {
+ errmsg = _("invalid condition code name");
+ }
+ }
+ else if ((operand->flags & V850_OPERAND_CACHEOP) != 0)
+ {
+ if (!cacheop_name (&ex, TRUE))
+ errmsg = _("invalid cache oparation name");
+ }
+ else if ((operand->flags & V850_OPERAND_PREFOP) != 0)
+ {
+ if (!prefop_name (&ex, TRUE))
+ errmsg = _("invalid pref oparation name");
+ }
+ else if ((operand->flags & V850_OPERAND_VREG) != 0)
+ {
+ if (!vector_register_name (&ex))
+ errmsg = _("invalid vector register name");
+ }
+ else if ((register_name (&ex)
+ && (operand->flags & V850_OPERAND_REG) == 0))
+ {
+ char c;
+ int exists = 0;
+
+ /* It is possible that an alias has been defined that
+ matches a register name. For example the code may
+ include a ".set ZERO, 0" directive, which matches
+ the register name "zero". Attempt to reparse the
+ field as an expression, and only complain if we
+ cannot generate a constant. */
+
+ input_line_pointer = str;
+
+ c = get_symbol_end ();
+
+ if (symbol_find (str) != NULL)
+ exists = 1;
+
+ *input_line_pointer = c;
+ input_line_pointer = str;
+
+ expression (&ex);
+
+ if (ex.X_op != O_constant)
+ {
+ /* If this register is actually occurring too early on
+ the parsing of the instruction, (because another
+ field is missing) then report this. */
+ if (opindex_ptr[1] != 0
+ && ((v850_operands[opindex_ptr[1]].flags
+ & V850_OPERAND_REG)
+ ||(v850_operands[opindex_ptr[1]].flags
+ & V850_OPERAND_VREG)))
+ errmsg = _("syntax error: value is missing before the register name");
+ else
+ errmsg = _("syntax error: register not expected");
+
+ /* If we created a symbol in the process of this
+ test then delete it now, so that it will not
+ be output with the real symbols... */
+ if (exists == 0
+ && ex.X_op == O_symbol)
+ symbol_remove (ex.X_add_symbol,
+ &symbol_rootP, &symbol_lastP);
+ }
+ }
+ else if (system_register_name (&ex, FALSE)
+ && (operand->flags & V850_OPERAND_SRG) == 0)
+ {
+ errmsg = _("syntax error: system register not expected");
+ }
+ else if (cc_name (&ex, FALSE)
+ && (operand->flags & V850_OPERAND_CC) == 0)
+ {
+ errmsg = _("syntax error: condition code not expected");
+ }
+ else if (float_cc_name (&ex, FALSE)
+ && (operand->flags & V850_OPERAND_FLOAT_CC) == 0)
+ {
+ errmsg = _("syntax error: condition code not expected");
+ }
+ else if (vector_register_name (&ex)
+ && (operand->flags & V850_OPERAND_VREG) == 0)
+ {
+ errmsg = _("syntax error: vector register not expected");
+ }
+ else
+ {
+ expression (&ex);
+
+ if ((operand->flags & V850_NOT_IMM0)
+ && ex.X_op == O_constant
+ && ex.X_add_number == 0)
+ {
+ errmsg = _("immediate 0 cannot be used here");
+ }
+
+ /* Special case:
+ If we are assembling a MOV/JARL/JR instruction and the immediate
+ value does not fit into the bits available then create a
+ fake error so that the next MOV/JARL/JR instruction will be
+ selected. This one has a 32 bit immediate field. */
+
+ if ((strcmp (opcode->name, "mov") == 0
+ || strcmp (opcode->name, "jarl") == 0
+ || strcmp (opcode->name, "jr") == 0)
+ && ex.X_op == O_constant
+ && (ex.X_add_number < (-(1 << (operand->bits - 1)))
+ || ex.X_add_number > ((1 << (operand->bits - 1)) - 1)))
+ {
+ errmsg = _("immediate operand is too large");
+ }
+
+ if ((strcmp (opcode->name, "jarl") == 0
+ || strcmp (opcode->name, "jr") == 0)
+ && ex.X_op != O_constant
+ && operand->bits != default_disp_size)
+ {
+ errmsg = _("immediate operand is not match");
+ }
+
+ /* Special case2 :
+ If we are assembling a ld/st instruction and the immediate
+ value does not fit into the bits available then create a
+ fake error so that the next ld/st instruction will be
+ selected. */
+ if ( ( (strncmp (opcode->name, "st.", 3) == 0)
+ || (strncmp (opcode->name, "ld.", 3) == 0))
+ && ex.X_op == O_constant
+ && (ex.X_add_number < (-(1 << (operand->bits - 1)))
+ || ex.X_add_number > ((1 << (operand->bits - 1)) - 1)))
+ errmsg = _("displacement is too large");
+ }
+
+ if (errmsg)
+ goto error;
+
+ switch (ex.X_op)
+ {
+ case O_illegal:
+ errmsg = _("illegal operand");
+ goto error;
+ case O_absent:
+ errmsg = _("missing operand");
+ goto error;
+ case O_register:
+ if ((operand->flags
+ & (V850_OPERAND_REG | V850_OPERAND_SRG | V850_OPERAND_VREG)) == 0)
+ {
+ errmsg = _("invalid operand");
+ goto error;
+ }
+
+ insn = v850_insert_operand (insn, operand,
+ ex.X_add_number,
+ &warningmsg);
+
+ break;
+
+ case O_constant:
+ insn = v850_insert_operand (insn, operand, ex.X_add_number,
+ &warningmsg);
+ break;
+
+ default:
+ /* We need to generate a fixup for this expression. */
+ if (fc >= MAX_INSN_FIXUPS)
+ as_fatal (_("too many fixups"));
+
+ fixups[fc].exp = ex;
+ fixups[fc].opindex = *opindex_ptr;
+ fixups[fc].reloc = BFD_RELOC_NONE;
+ ++fc;
+ break;
+ }
+ }
+
+ str = input_line_pointer;
+ input_line_pointer = hold;
+
+ while (*str == ' ' || *str == ',' || *str == '[' || *str == ']'
+ || *str == ')')
+ ++str;
+ }
+
+ while (ISSPACE (*str))
+ ++str;
+
+ if (*str == '\0')
+ match = 1;
+
+ error:
+ if (match == 0)
+ {
+ if ((opindex_ptr - opcode->operands) >= most_match_count)
+ {
+ most_match_count = opindex_ptr - opcode->operands;
+ if (errmsg != NULL)
+ strncpy (most_match_errmsg, errmsg, sizeof (most_match_errmsg)-1);
+ }
+
+ next_opcode = opcode + 1;
+ if (next_opcode->name != NULL
+ && strcmp (next_opcode->name, opcode->name) == 0)
+ {
+ opcode = next_opcode;
+
+ /* Skip versions that are not supported by the target
+ processor. */
+ if ((opcode->processors & processor_mask) == 0)
+ goto error;
+
+ continue;
+ }
+
+ if (most_match_errmsg[0] == 0)
+ /* xgettext:c-format. */
+ as_bad (_("junk at end of line: `%s'"), str);
+ else
+ as_bad ("%s: %s", copy_of_instruction, most_match_errmsg);
+
+ if (*input_line_pointer == ']')
+ ++input_line_pointer;
+
+ ignore_rest_of_line ();
+ input_line_pointer = saved_input_line_pointer;
+ return;
+ }
+
+ if (warningmsg != NULL)
+ as_warn ("%s", warningmsg);
+ break;
+ }
+
+ input_line_pointer = str;
+
+ /* Tie dwarf2 debug info to the address at the start of the insn.
+ We can't do this after the insn has been output as the current
+ frag may have been closed off. eg. by frag_var. */
+ dwarf2_emit_insn (0);
+
+ /* Write out the instruction. */
+
+ if (relaxable && fc > 0)
+ {
+ insn_size = 2;
+ fc = 0;
+
+ if (strcmp (opcode->name, "loop") == 0)
+ {
+ if (((processor_mask & PROCESSOR_V850E3V5_UP) == 0) || default_disp_size == 22)
+ {
+ insn_size = 4;
+ f = frag_var (rs_machine_dependent, 6, 2, SUBYPTE_LOOP_16_22,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f+4, 0, 4);
+ }
+ else
+ {
+ as_bad (_("loop: 32-bit displacement not supported"));
+ }
+ }
+ else if (strcmp (opcode->name, "br") == 0
+ || strcmp (opcode->name, "jbr") == 0)
+ {
+ if ((processor_mask & PROCESSOR_V850E2_UP) == 0 || default_disp_size == 22)
+ {
+ f = frag_var (rs_machine_dependent, 4, 2, SUBYPTE_UNCOND_9_22,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 2);
+ }
+ else
+ {
+ f = frag_var (rs_machine_dependent, 6, 4, SUBYPTE_UNCOND_9_22_32,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 4);
+ }
+ }
+ else /* b<cond>, j<cond>. */
+ {
+ if (default_disp_size == 22
+ || (processor_mask & PROCESSOR_V850E2_UP) == 0)
+ {
+ if (processor_mask & PROCESSOR_V850E2V3_UP && !no_bcond17)
+ {
+ if (strcmp (opcode->name, "bsa") == 0)
+ {
+ f = frag_var (rs_machine_dependent, 8, 6, SUBYPTE_SA_9_17_22,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 6);
+ }
+ else
+ {
+ f = frag_var (rs_machine_dependent, 6, 4, SUBYPTE_COND_9_17_22,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 4);
+ }
+ }
+ else
+ {
+ if (strcmp (opcode->name, "bsa") == 0)
+ {
+ f = frag_var (rs_machine_dependent, 8, 6, SUBYPTE_SA_9_22,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 6);
+ }
+ else
+ {
+ f = frag_var (rs_machine_dependent, 6, 4, SUBYPTE_COND_9_22,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 4);
+ }
+ }
+ }
+ else
+ {
+ if (processor_mask & PROCESSOR_V850E2V3_UP && !no_bcond17)
+ {
+ if (strcmp (opcode->name, "bsa") == 0)
+ {
+ f = frag_var (rs_machine_dependent, 10, 8, SUBYPTE_SA_9_17_22_32,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 8);
+ }
+ else
+ {
+ f = frag_var (rs_machine_dependent, 8, 6, SUBYPTE_COND_9_17_22_32,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 6);
+ }
+ }
+ else
+ {
+ if (strcmp (opcode->name, "bsa") == 0)
+ {
+ f = frag_var (rs_machine_dependent, 10, 8, SUBYPTE_SA_9_22_32,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 8);
+ }
+ else
+ {
+ f = frag_var (rs_machine_dependent, 8, 6, SUBYPTE_COND_9_22_32,
+ fixups[0].exp.X_add_symbol,
+ fixups[0].exp.X_add_number,
+ (char *)(size_t) fixups[0].opindex);
+ md_number_to_chars (f, insn, insn_size);
+ md_number_to_chars (f + 2, 0, 6);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* Four byte insns have an opcode with the two high bits on. */
+ if ((insn & 0x0600) == 0x0600)
+ insn_size = 4;
+ else
+ insn_size = 2;
+
+ /* Special case: 32 bit MOV. */
+ if ((insn & 0xffe0) == 0x0620)
+ insn_size = 2;
+
+ /* Special case: 32 bit JARL,JMP,JR. */
+ if ((insn & 0x1ffe0) == 0x2e0 /* JARL. */
+ || (insn & 0x1ffe0) == 0x6e0 /* JMP. */
+ || (insn & 0x1ffff) == 0x2e0) /* JR. */
+ insn_size = 2;
+
+ if (obstack_room (& frchain_now->frch_obstack) < (insn_size + extra_data_len))
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+
+ f = frag_more (insn_size);
+ md_number_to_chars (f, insn, insn_size);
+
+ if (extra_data_after_insn)
+ {
+ f = frag_more (extra_data_len);
+ md_number_to_chars (f, extra_data, extra_data_len);
+
+ extra_data_after_insn = FALSE;
+ }
+ }
+
+ /* Create any fixups. At this point we do not use a
+ bfd_reloc_code_real_type, but instead just use the
+ BFD_RELOC_UNUSED plus the operand index. This lets us easily
+ handle fixups for any operand type, although that is admittedly
+ not a very exciting feature. We pick a BFD reloc type in
+ md_apply_fix. */
+ for (i = 0; i < fc; i++)
+ {
+ const struct v850_operand *operand;
+ bfd_reloc_code_real_type reloc;
+
+ operand = &v850_operands[fixups[i].opindex];
+
+ reloc = fixups[i].reloc;
+
+ if (reloc != BFD_RELOC_NONE)
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput, reloc);
+ int size;
+ int address;
+ fixS *fixP;
+
+ if (!reloc_howto)
+ abort ();
+
+ size = bfd_get_reloc_size (reloc_howto);
+
+ /* XXX This will abort on an R_V850_8 reloc -
+ is this reloc actually used? */
+ if (size != 2 && size != 4)
+ abort ();
+
+ if (extra_data_len == 0)
+ {
+ address = (f - frag_now->fr_literal) + insn_size - size;
+ }
+ else
+ {
+ address = (f - frag_now->fr_literal) + extra_data_len - size;
+ }
+
+ if ((operand->flags & V850E_IMMEDIATE32) && (operand->flags & V850_PCREL))
+ {
+ fixups[i].exp.X_add_number += 2;
+ }
+ else if (operand->default_reloc == BFD_RELOC_V850_16_PCREL)
+ {
+ fixups[i].exp.X_add_number += 2;
+ address += 2;
+ }
+
+ /* fprintf (stderr, "0x%x %d %ld\n", address, size, fixups[i].exp.X_add_number); */
+ fixP = fix_new_exp (frag_now, address, size,
+ &fixups[i].exp,
+ reloc_howto->pc_relative,
+ reloc);
+
+ fixP->tc_fix_data = (void *) operand;
+
+ switch (reloc)
+ {
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_V850_LO16_S1:
+ case BFD_RELOC_V850_LO16_SPLIT_OFFSET:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ fixP->fx_no_overflow = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ gas_assert (f != NULL);
+ fix_new_exp (frag_now,
+ f - frag_now->fr_literal, 4,
+ & fixups[i].exp,
+ (operand->flags & V850_PCREL) != 0,
+ (bfd_reloc_code_real_type) (fixups[i].opindex
+ + (int) BFD_RELOC_UNUSED));
+ }
+ }
+
+ input_line_pointer = saved_input_line_pointer;
+}
+
+/* If while processing a fixup, a reloc really needs to be created
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if ( fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+ || fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixp->fx_r_type == BFD_RELOC_V850_LONGCALL
+ || fixp->fx_r_type == BFD_RELOC_V850_LONGJUMP
+ || fixp->fx_r_type == BFD_RELOC_V850_ALIGN)
+ reloc->addend = fixp->fx_offset;
+ else
+ {
+#if 0
+ if (fixp->fx_r_type == BFD_RELOC_32
+ && fixp->fx_pcrel)
+ fixp->fx_r_type = BFD_RELOC_32_PCREL;
+#endif
+
+ reloc->addend = fixp->fx_addnumber;
+ }
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ /* xgettext:c-format */
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+
+ xfree (reloc);
+
+ return NULL;
+ }
+
+ return reloc;
+}
+
+void
+v850_handle_align (fragS * frag)
+{
+ if (v850_relax
+ && frag->fr_type == rs_align
+ && frag->fr_address + frag->fr_fix > 0
+ && frag->fr_offset > 1
+ && now_seg != bss_section
+ && now_seg != v850_seg_table[SBSS_SECTION].s
+ && now_seg != v850_seg_table[TBSS_SECTION].s
+ && now_seg != v850_seg_table[ZBSS_SECTION].s)
+ fix_new (frag, frag->fr_fix, 2, & abs_symbol, frag->fr_offset, 0,
+ BFD_RELOC_V850_ALIGN);
+}
+
+/* Return current size of variable part of frag. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *seg ATTRIBUTE_UNUSED)
+{
+ if (fragp->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
+}
+
+long
+v850_pcrel_from_section (fixS *fixp, segT section)
+{
+ /* If the symbol is undefined, or in a section other than our own,
+ or it is weak (in which case it may well be in another section,
+ then let the linker figure it out. */
+ if (fixp->fx_addsy != (symbolS *) NULL
+ && (! S_IS_DEFINED (fixp->fx_addsy)
+ || S_IS_WEAK (fixp->fx_addsy)
+ || (S_GET_SEGMENT (fixp->fx_addsy) != section)))
+ return 0;
+
+ return fixp->fx_frag->fr_address + fixp->fx_where;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valueP, segT seg ATTRIBUTE_UNUSED)
+{
+ valueT value = * valueP;
+ char *where;
+
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_V850_LONGCALL
+ || fixP->fx_r_type == BFD_RELOC_V850_LONGJUMP
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ {
+ fixP->fx_done = 0;
+ return;
+ }
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_addnumber = value,
+ fixP->fx_done = 1;
+
+ else if (fixP->fx_pcrel)
+ fixP->fx_addnumber = fixP->fx_offset;
+
+ else
+ {
+ value = fixP->fx_offset;
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ {
+ if (S_GET_SEGMENT (fixP->fx_subsy) == absolute_section)
+ value -= S_GET_VALUE (fixP->fx_subsy);
+ else
+ /* We don't actually support subtracting a symbol. */
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("expression too complex"));
+ }
+ fixP->fx_addnumber = value;
+ }
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex;
+ const struct v850_operand *operand;
+ unsigned long insn;
+ const char *errmsg = NULL;
+
+ opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ operand = &v850_operands[opindex];
+
+ /* Fetch the instruction, insert the fully resolved operand
+ value, and stuff the instruction back again.
+
+ Note the instruction has been stored in little endian
+ format! */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (fixP->fx_size > 2)
+ insn = bfd_getl32 ((unsigned char *) where);
+ else
+ insn = bfd_getl16 ((unsigned char *) where);
+
+ /* When inserting loop offets a backwards displacement
+ is encoded as a positive value. */
+ if (operand->flags & V850_INVERSE_PCREL)
+ value = - value;
+
+ insn = v850_insert_operand (insn, operand, (offsetT) value,
+ &errmsg);
+ if (errmsg)
+ as_warn_where (fixP->fx_file, fixP->fx_line, "%s", errmsg);
+
+ if (fixP->fx_size > 2)
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ else
+ bfd_putl16 ((bfd_vma) insn, (unsigned char *) where);
+
+ if (fixP->fx_done)
+ /* Nothing else to do here. */
+ return;
+
+ /* Determine a BFD reloc value based on the operand information.
+ We are only prepared to turn a few of the operands into relocs. */
+
+ if (operand->default_reloc == BFD_RELOC_NONE)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ fixP->fx_done = 1;
+ return;
+ }
+
+ {
+ fixP->fx_r_type = operand->default_reloc;
+ if (operand->default_reloc == BFD_RELOC_V850_16_PCREL)
+ {
+ fixP->fx_where += 2;
+ fixP->fx_size = 2;
+ fixP->fx_addnumber += 2;
+ }
+ }
+ }
+ else if (fixP->fx_done)
+ {
+ /* We still have to insert the value into memory! */
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (fixP->tc_fix_data != NULL
+ && ((struct v850_operand *) fixP->tc_fix_data)->insert != NULL)
+ {
+ const char * message = NULL;
+ struct v850_operand * operand = (struct v850_operand *) fixP->tc_fix_data;
+ unsigned long insn;
+
+ /* The variable "where" currently points at the exact point inside
+ the insn where we need to insert the value. But we need to
+ extract the entire insn so we probably need to move "where"
+ back a few bytes. */
+
+ if (fixP->fx_size == 2)
+ where -= 2;
+ else if (fixP->fx_size == 1)
+ where -= 3;
+
+ insn = bfd_getl32 ((unsigned char *) where);
+
+ /* Use the operand's insertion procedure, if present, in order to
+ make sure that the value is correctly stored in the insn. */
+ insn = operand->insert (insn, (offsetT) value, & message);
+ /* Ignore message even if it is set. */
+
+ bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+ }
+ else
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_V850_32_ABS:
+ case BFD_RELOC_V850_32_PCREL:
+ bfd_putl32 (value & 0xfffffffe, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_32:
+ bfd_putl32 (value, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_V850_23:
+ bfd_putl32 (((value & 0x7f) << 4) | ((value & 0x7fff80) << (16-7))
+ | (bfd_getl32 (where) & ~((0x7f << 4) | (0xffff << 16))),
+ (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_16:
+ case BFD_RELOC_HI16:
+ case BFD_RELOC_HI16_S:
+ case BFD_RELOC_LO16:
+ case BFD_RELOC_V850_ZDA_16_16_OFFSET:
+ case BFD_RELOC_V850_SDA_16_16_OFFSET:
+ case BFD_RELOC_V850_TDA_16_16_OFFSET:
+ case BFD_RELOC_V850_CALLT_16_16_OFFSET:
+ bfd_putl16 (value & 0xffff, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_8:
+ *where = value & 0xff;
+ break;
+
+ case BFD_RELOC_V850_9_PCREL:
+ bfd_putl16 (((value & 0x1f0) << 7) | ((value & 0x0e) << 3)
+ | (bfd_getl16 (where) & ~((0x1f0 << 7) | (0x0e << 3))), where);
+ break;
+
+ case BFD_RELOC_V850_17_PCREL:
+ bfd_putl32 (((value & 0x10000) >> (16 - 4)) | ((value & 0xfffe) << 16)
+ | (bfd_getl32 (where) & ~((0x10000 >> (16 - 4)) | (0xfffe << 16))), where);
+ break;
+
+ case BFD_RELOC_V850_16_PCREL:
+ bfd_putl16 ((-value & 0xfffe) | (bfd_getl16 (where + 2) & 0x0001),
+ (unsigned char *) (where + 2));
+ break;
+
+ case BFD_RELOC_V850_22_PCREL:
+ bfd_putl32 (((value & 0xfffe) << 16) | ((value & 0x3f0000) >> 16)
+ | (bfd_getl32 (where) & ~((0xfffe << 16) | (0x3f0000 >> 16))), where);
+ break;
+
+ case BFD_RELOC_V850_16_S1:
+ case BFD_RELOC_V850_LO16_S1:
+ case BFD_RELOC_V850_ZDA_15_16_OFFSET:
+ case BFD_RELOC_V850_SDA_15_16_OFFSET:
+ bfd_putl16 (value & 0xfffe, (unsigned char *) where);
+ break;
+
+ case BFD_RELOC_V850_16_SPLIT_OFFSET:
+ case BFD_RELOC_V850_LO16_SPLIT_OFFSET:
+ case BFD_RELOC_V850_ZDA_16_16_SPLIT_OFFSET:
+ case BFD_RELOC_V850_SDA_16_16_SPLIT_OFFSET:
+ bfd_putl32 (((value << 16) & 0xfffe0000)
+ | ((value << 5) & 0x20)
+ | (bfd_getl32 (where) & ~0xfffe0020), where);
+ break;
+
+ case BFD_RELOC_V850_TDA_6_8_OFFSET:
+ *where = (*where & ~0x7e) | ((value >> 1) & 0x7e);
+ break;
+
+ case BFD_RELOC_V850_TDA_7_8_OFFSET:
+ *where = (*where & ~0x7f) | ((value >> 1) & 0x7f);
+ break;
+
+ case BFD_RELOC_V850_TDA_7_7_OFFSET:
+ *where = (*where & ~0x7f) | (value & 0x7f);
+ break;
+
+ case BFD_RELOC_V850_TDA_4_5_OFFSET:
+ *where = (*where & ~0xf) | ((value >> 1) & 0xf);
+ break;
+
+ case BFD_RELOC_V850_TDA_4_4_OFFSET:
+ *where = (*where & ~0xf) | (value & 0xf);
+ break;
+
+ case BFD_RELOC_V850_CALLT_6_7_OFFSET:
+ *where = (*where & ~0x3f) | (value & 0x3f);
+ break;
+
+ default:
+ abort ();
+ }
+ }
+ }
+}
+
+/* Parse a cons expression. We have to handle hi(), lo(), etc
+ on the v850. */
+
+bfd_reloc_code_real_type
+parse_cons_expression_v850 (expressionS *exp)
+{
+ const char *errmsg;
+ bfd_reloc_code_real_type r;
+
+ /* See if there's a reloc prefix like hi() we have to handle. */
+ r = v850_reloc_prefix (NULL, &errmsg);
+
+ /* Do normal expression parsing. */
+ expression (exp);
+ return r;
+}
+
+/* Create a fixup for a cons expression. If parse_cons_expression_v850
+ found a reloc prefix, then we use that reloc, else we choose an
+ appropriate one based on the size of the expression. */
+
+void
+cons_fix_new_v850 (fragS *frag,
+ int where,
+ int size,
+ expressionS *exp,
+ bfd_reloc_code_real_type r)
+{
+ if (r == BFD_RELOC_NONE)
+ {
+ if (size == 4)
+ r = BFD_RELOC_32;
+ if (size == 2)
+ r = BFD_RELOC_16;
+ if (size == 1)
+ r = BFD_RELOC_8;
+ }
+
+ if (exp != NULL)
+ fix_new_exp (frag, where, size, exp, 0, r);
+ else
+ fix_new (frag, where, size, NULL, 0, 0, r);
+}
+
+bfd_boolean
+v850_fix_adjustable (fixS *fixP)
+{
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ /* Don't adjust function names. */
+ if (S_IS_FUNCTION (fixP->fx_addsy))
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+int
+v850_force_relocation (struct fix *fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_V850_LONGCALL
+ || fixP->fx_r_type == BFD_RELOC_V850_LONGJUMP)
+ return 1;
+
+ if (v850_relax
+ && (fixP->fx_pcrel
+ || fixP->fx_r_type == BFD_RELOC_V850_ALIGN
+ || fixP->fx_r_type == BFD_RELOC_V850_9_PCREL
+ || fixP->fx_r_type == BFD_RELOC_V850_16_PCREL
+ || fixP->fx_r_type == BFD_RELOC_V850_17_PCREL
+ || fixP->fx_r_type == BFD_RELOC_V850_22_PCREL
+ || fixP->fx_r_type == BFD_RELOC_V850_32_PCREL
+ || fixP->fx_r_type >= BFD_RELOC_UNUSED))
+ return 1;
+
+ return generic_force_reloc (fixP);
+}
diff --git a/gas/config/tc-v850.h b/gas/config/tc-v850.h
new file mode 100644
index 0000000..a0aeeb4
--- /dev/null
+++ b/gas/config/tc-v850.h
@@ -0,0 +1,86 @@
+/* tc-v850.h -- Header file for tc-v850.c.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_V850
+
+#include "elf/v850.h"
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* The target BFD architecture. */
+#define TARGET_ARCH v850_target_arch
+extern int v850_target_arch;
+
+/* The target BFD format. */
+#define TARGET_FORMAT v850_target_format
+extern const char * v850_target_format;
+
+#define md_operand(x)
+
+#define tc_fix_adjustable(FIX) v850_fix_adjustable (FIX)
+extern bfd_boolean v850_fix_adjustable (struct fix *);
+
+#define TC_FORCE_RELOCATION(FIX) v850_force_relocation(FIX)
+extern int v850_force_relocation (struct fix *);
+
+#ifdef OBJ_ELF
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#endif
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs. */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_littleendian
+
+/* We need to handle lo(), hi(), etc etc in .hword, .word, etc
+ directives, so we have to parse "cons" expressions ourselves. */
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_cons_expression_v850 (EXP)
+extern bfd_reloc_code_real_type parse_cons_expression_v850 (expressionS *);
+
+#define TC_CONS_FIX_NEW cons_fix_new_v850
+extern void cons_fix_new_v850 (fragS *, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+extern const struct relax_type md_relax_table[];
+
+/* When relaxing, we need to generate
+ relocations for alignment directives. */
+#define HANDLE_ALIGN(frag) v850_handle_align (frag)
+extern void v850_handle_align (fragS *);
+
+/* We need space in a frag's fixed size to allow for alignment when relaxing. */
+#define TC_FX_SIZE_SLACK(FIX) 2
+
+#define MD_PCREL_FROM_SECTION(FIX, SEC) v850_pcrel_from_section (FIX, SEC)
+extern long v850_pcrel_from_section (struct fix *, asection *);
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* We need to record the operand involved when a pseudo-reloc is
+ processed so that the resulting value can be inserted correctly. */
+#define TC_FIX_TYPE void *
+#define TC_INIT_FIX_DATA(fixP) (fixP)->tc_fix_data = NULL
diff --git a/gas/config/tc-vax.c b/gas/config/tc-vax.c
new file mode 100644
index 0000000..bccf596
--- /dev/null
+++ b/gas/config/tc-vax.c
@@ -0,0 +1,3410 @@
+/* tc-vax.c - vax-specific -
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+
+#include "vax-inst.h"
+#include "obstack.h" /* For FRAG_APPEND_1_CHAR macro in "frags.h" */
+#include "subsegs.h"
+#include "safe-ctype.h"
+
+#ifdef OBJ_ELF
+#include "elf/vax.h"
+#endif
+
+/* These chars start a comment anywhere in a source file (except inside
+ another comment */
+const char comment_chars[] = "#";
+
+/* These chars only start a comment at the beginning of a line. */
+/* Note that for the VAX the are the same as comment_chars above. */
+const char line_comment_chars[] = "#";
+
+const char line_separator_chars[] = ";";
+
+/* Chars that can be used to separate mant from exp in floating point nums. */
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant
+ as in 0f123.456
+ or 0H1.234E-12 (see exp chars above). */
+const char FLT_CHARS[] = "dDfFgGhH";
+
+/* Also be aware that MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT may have to be
+ changed in read.c . Ideally it shouldn't have to know about it at all,
+ but nothing is ideal around here. */
+
+/* Hold details of an operand expression. */
+static expressionS exp_of_operand[VIT_MAX_OPERANDS];
+static segT seg_of_operand[VIT_MAX_OPERANDS];
+
+/* A vax instruction after decoding. */
+static struct vit v;
+
+/* Hold details of big operands. */
+LITTLENUM_TYPE big_operand_bits[VIT_MAX_OPERANDS][SIZE_OF_LARGE_NUMBER];
+FLONUM_TYPE float_operand[VIT_MAX_OPERANDS];
+/* Above is made to point into big_operand_bits by md_begin(). */
+
+#ifdef OBJ_ELF
+#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
+#define PROCEDURE_LINKAGE_TABLE_NAME "_PROCEDURE_LINKAGE_TABLE_"
+symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE_". */
+symbolS *PLT_symbol; /* Pre-defined "_PROCEDURE_LINKAGE_TABLE_". */
+#endif
+
+int flag_hash_long_names; /* -+ */
+int flag_one; /* -1 */
+int flag_show_after_trunc; /* -H */
+int flag_no_hash_mixed_case; /* -h NUM */
+#ifdef OBJ_ELF
+int flag_want_pic; /* -k */
+#endif
+
+/* For VAX, relative addresses of "just the right length" are easy.
+ The branch displacement is always the last operand, even in
+ synthetic instructions.
+ For VAX, we encode the relax_substateTs (in e.g. fr_substate) as:
+
+ 4 3 2 1 0 bit number
+ ---/ /--+-------+-------+-------+-------+-------+
+ | what state ? | how long ? |
+ ---/ /--+-------+-------+-------+-------+-------+
+
+ The "how long" bits are 00=byte, 01=word, 10=long.
+ This is a Un*x convention.
+ Not all lengths are legit for a given value of (what state).
+ The "how long" refers merely to the displacement length.
+ The address usually has some constant bytes in it as well.
+
+ groups for VAX address relaxing.
+
+ 1. "foo" pc-relative.
+ length of byte, word, long
+
+ 2a. J<cond> where <cond> is a simple flag test.
+ length of byte, word, long.
+ VAX opcodes are: (Hex)
+ bneq/bnequ 12
+ beql/beqlu 13
+ bgtr 14
+ bleq 15
+ bgeq 18
+ blss 19
+ bgtru 1a
+ blequ 1b
+ bvc 1c
+ bvs 1d
+ bgequ/bcc 1e
+ blssu/bcs 1f
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcode, then 1-byte displacement.
+
+ 2b. J<cond> where cond tests a memory bit.
+ length of byte, word, long.
+ Vax opcodes are: (Hex)
+ bbs e0
+ bbc e1
+ bbss e2
+ bbcs e3
+ bbsc e4
+ bbcc e5
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcde, longword-address, byte-address, 1-byte-displacement
+
+ 2c. J<cond> where cond tests low-order memory bit
+ length of byte,word,long.
+ Vax opcodes are: (Hex)
+ blbs e8
+ blbc e9
+ Always, you complement 0th bit to reverse condition.
+ Always, 1-byte opcode, longword-address, 1-byte displacement.
+
+ 3. Jbs/Jbr.
+ length of byte,word,long.
+ Vax opcodes are: (Hex)
+ bsbb 10
+ brb 11
+ These are like (2) but there is no condition to reverse.
+ Always, 1 byte opcode, then displacement/absolute.
+
+ 4a. JacbX
+ length of word, long.
+ Vax opcodes are: (Hex)
+ acbw 3d
+ acbf 4f
+ acbd 6f
+ abcb 9d
+ acbl f1
+ acbg 4ffd
+ acbh 6ffd
+ Always, we cannot reverse the sense of the branch; we have a word
+ displacement.
+ The double-byte op-codes don't hurt: we never want to modify the
+ opcode, so we don't care how many bytes are between the opcode and
+ the operand.
+
+ 4b. JXobXXX
+ length of long, long, byte.
+ Vax opcodes are: (Hex)
+ aoblss f2
+ aobleq f3
+ sobgeq f4
+ sobgtr f5
+ Always, we cannot reverse the sense of the branch; we have a byte
+ displacement.
+
+ The only time we need to modify the opcode is for class 2 instructions.
+ After relax() we may complement the lowest order bit of such instruction
+ to reverse sense of branch.
+
+ For class 2 instructions, we store context of "where is the opcode literal".
+ We can change an opcode's lowest order bit without breaking anything else.
+
+ We sometimes store context in the operand literal. This way we can figure out
+ after relax() what the original addressing mode was. */
+
+/* These displacements are relative to the start address of the
+ displacement. The first letter is Byte, Word. 2nd letter is
+ Forward, Backward. */
+#define BF (1+ 127)
+#define BB (1+-128)
+#define WF (2+ 32767)
+#define WB (2+-32768)
+/* Dont need LF, LB because they always reach. [They are coded as 0.] */
+
+#define C(a,b) ENCODE_RELAX(a,b)
+/* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+#define RELAX_STATE(s) ((s) >> 2)
+#define RELAX_LENGTH(s) ((s) & 3)
+
+const relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0}, /* error sentinel 0,0 */
+ {1, 1, 0, 0}, /* unused 0,1 */
+ {1, 1, 0, 0}, /* unused 0,2 */
+ {1, 1, 0, 0}, /* unused 0,3 */
+
+ {BF + 1, BB + 1, 2, C (1, 1)},/* B^"foo" 1,0 */
+ {WF + 1, WB + 1, 3, C (1, 2)},/* W^"foo" 1,1 */
+ {0, 0, 5, 0}, /* L^"foo" 1,2 */
+ {1, 1, 0, 0}, /* unused 1,3 */
+
+ {BF, BB, 1, C (2, 1)}, /* b<cond> B^"foo" 2,0 */
+ {WF + 2, WB + 2, 4, C (2, 2)},/* br.+? brw X 2,1 */
+ {0, 0, 7, 0}, /* br.+? jmp X 2,2 */
+ {1, 1, 0, 0}, /* unused 2,3 */
+
+ {BF, BB, 1, C (3, 1)}, /* brb B^foo 3,0 */
+ {WF, WB, 2, C (3, 2)}, /* brw W^foo 3,1 */
+ {0, 0, 5, 0}, /* Jmp L^foo 3,2 */
+ {1, 1, 0, 0}, /* unused 3,3 */
+
+ {1, 1, 0, 0}, /* unused 4,0 */
+ {WF, WB, 2, C (4, 2)}, /* acb_ ^Wfoo 4,1 */
+ {0, 0, 10, 0}, /* acb_,br,jmp L^foo4,2 */
+ {1, 1, 0, 0}, /* unused 4,3 */
+
+ {BF, BB, 1, C (5, 1)}, /* Xob___,,foo 5,0 */
+ {WF + 4, WB + 4, 6, C (5, 2)},/* Xob.+2,brb.+3,brw5,1 */
+ {0, 0, 9, 0}, /* Xob.+2,brb.+6,jmp5,2 */
+ {1, 1, 0, 0}, /* unused 5,3 */
+};
+
+#undef C
+#undef BF
+#undef BB
+#undef WF
+#undef WB
+
+void float_cons (int);
+int flonum_gen2vax (char, FLONUM_TYPE *, LITTLENUM_TYPE *);
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ {"dfloat", float_cons, 'd'},
+ {"ffloat", float_cons, 'f'},
+ {"gfloat", float_cons, 'g'},
+ {"hfloat", float_cons, 'h'},
+ {"d_floating", float_cons, 'd'},
+ {"f_floating", float_cons, 'f'},
+ {"g_floating", float_cons, 'g'},
+ {"h_floating", float_cons, 'h'},
+ {NULL, NULL, 0},
+};
+
+#define STATE_PC_RELATIVE (1)
+#define STATE_CONDITIONAL_BRANCH (2)
+#define STATE_ALWAYS_BRANCH (3) /* includes BSB... */
+#define STATE_COMPLEX_BRANCH (4)
+#define STATE_COMPLEX_HOP (5)
+
+#define STATE_BYTE (0)
+#define STATE_WORD (1)
+#define STATE_LONG (2)
+#define STATE_UNDF (3) /* Symbol undefined in pass1. */
+
+#define min(a, b) ((a) < (b) ? (a) : (b))
+
+void
+md_number_to_chars (char con[], valueT value, int nbytes)
+{
+ number_to_chars_littleendian (con, value, nbytes);
+}
+
+/* Fix up some data or instructions after we find out the value of a symbol
+ that they reference. */
+
+void /* Knows about order of bytes in address. */
+md_apply_fix (fixS *fixP, valueT *valueP, segT seg ATTRIBUTE_UNUSED)
+{
+ valueT value = * valueP;
+
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+
+ if (fixP->fx_done)
+ number_to_chars_littleendian (fixP->fx_where + fixP->fx_frag->fr_literal,
+ value, fixP->fx_size);
+ else
+ /* Initialise the part of an instruction frag covered by the
+ relocation. (Many occurrences of frag_more followed by fix_new
+ lack any init of the frag.) Since VAX uses RELA relocs the
+ value we write into this field doesn't really matter. */
+ memset (fixP->fx_where + fixP->fx_frag->fr_literal, 0, fixP->fx_size);
+}
+
+/* Convert a number from VAX byte order (little endian)
+ into host byte order.
+ con is the buffer to convert,
+ nbytes is the length of the given buffer. */
+static long
+md_chars_to_number (unsigned char con[], int nbytes)
+{
+ long retval;
+
+ for (retval = 0, con += nbytes - 1; nbytes--; con--)
+ {
+ retval <<= BITS_PER_CHAR;
+ retval |= *con;
+ }
+ return retval;
+}
+
+/* Copy a bignum from in to out.
+ If the output is shorter than the input, copy lower-order
+ littlenums. Return 0 or the number of significant littlenums
+ dropped. Assumes littlenum arrays are densely packed: no unused
+ chars between the littlenums. Uses memcpy() to move littlenums, and
+ wants to know length (in chars) of the input bignum. */
+
+static int
+bignum_copy (LITTLENUM_TYPE *in,
+ int in_length, /* in sizeof(littlenum)s */
+ LITTLENUM_TYPE *out,
+ int out_length /* in sizeof(littlenum)s */)
+{
+ int significant_littlenums_dropped;
+
+ if (out_length < in_length)
+ {
+ LITTLENUM_TYPE *p; /* -> most significant (non-zero) input
+ littlenum. */
+
+ memcpy ((void *) out, (void *) in,
+ (unsigned int) out_length << LITTLENUM_SHIFT);
+ for (p = in + in_length - 1; p >= in; --p)
+ {
+ if (*p)
+ break;
+ }
+ significant_littlenums_dropped = p - in - in_length + 1;
+
+ if (significant_littlenums_dropped < 0)
+ significant_littlenums_dropped = 0;
+ }
+ else
+ {
+ memcpy ((char *) out, (char *) in,
+ (unsigned int) in_length << LITTLENUM_SHIFT);
+
+ if (out_length > in_length)
+ memset ((char *) (out + in_length), '\0',
+ (unsigned int) (out_length - in_length) << LITTLENUM_SHIFT);
+
+ significant_littlenums_dropped = 0;
+ }
+
+ return significant_littlenums_dropped;
+}
+
+/* md_estimate_size_before_relax(), called just before relax().
+ Any symbol that is now undefined will not become defined.
+ Return the correct fr_subtype in the frag and the growth beyond
+ fr_fix. */
+int
+md_estimate_size_before_relax (fragS *fragP, segT segment)
+{
+ if (RELAX_LENGTH (fragP->fr_subtype) == STATE_UNDF)
+ {
+ if (S_GET_SEGMENT (fragP->fr_symbol) != segment
+#ifdef OBJ_ELF
+ || S_IS_WEAK (fragP->fr_symbol)
+ || S_IS_EXTERNAL (fragP->fr_symbol)
+#endif
+ )
+ {
+ /* Non-relaxable cases. */
+ int reloc_type = NO_RELOC;
+ char *p;
+ int old_fr_fix;
+
+ old_fr_fix = fragP->fr_fix;
+ p = fragP->fr_literal + old_fr_fix;
+#ifdef OBJ_ELF
+ /* If this is to an undefined symbol, then if it's an indirect
+ reference indicate that is can mutated into a GLOB_DAT or
+ JUMP_SLOT by the loader. We restrict ourselves to no offset
+ due to a limitation in the NetBSD linker. */
+
+ if (GOT_symbol == NULL)
+ GOT_symbol = symbol_find (GLOBAL_OFFSET_TABLE_NAME);
+ if (PLT_symbol == NULL)
+ PLT_symbol = symbol_find (PROCEDURE_LINKAGE_TABLE_NAME);
+ if ((GOT_symbol == NULL || fragP->fr_symbol != GOT_symbol)
+ && (PLT_symbol == NULL || fragP->fr_symbol != PLT_symbol)
+ && fragP->fr_symbol != NULL
+ && flag_want_pic
+ && (!S_IS_DEFINED (fragP->fr_symbol)
+ || S_IS_WEAK (fragP->fr_symbol)
+ || S_IS_EXTERNAL (fragP->fr_symbol)))
+ {
+ /* Indirect references cannot go through the GOT or PLT,
+ let's hope they'll become local in the final link. */
+ if ((ELF_ST_VISIBILITY (S_GET_OTHER (fragP->fr_symbol))
+ != STV_DEFAULT)
+ || (p[0] & 0x10))
+ reloc_type = BFD_RELOC_32_PCREL;
+ else if (((unsigned char *) fragP->fr_opcode)[0] == VAX_CALLS
+ || ((unsigned char *) fragP->fr_opcode)[0] == VAX_CALLG
+ || ((unsigned char *) fragP->fr_opcode)[0] == VAX_JSB
+ || ((unsigned char *) fragP->fr_opcode)[0] == VAX_JMP
+ || S_IS_FUNCTION (fragP->fr_symbol))
+ reloc_type = BFD_RELOC_32_PLT_PCREL;
+ else
+ reloc_type = BFD_RELOC_32_GOT_PCREL;
+ }
+#endif
+ switch (RELAX_STATE (fragP->fr_subtype))
+ {
+ case STATE_PC_RELATIVE:
+ p[0] |= VAX_PC_RELATIVE_MODE; /* Preserve @ bit. */
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, reloc_type);
+ break;
+
+ case STATE_CONDITIONAL_BRANCH:
+ *fragP->fr_opcode ^= 1; /* Reverse sense of branch. */
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_PC_RELATIVE_MODE; /* ...(PC) */
+ fragP->fr_fix += 1 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 3, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ break;
+
+ case STATE_COMPLEX_BRANCH:
+ p[0] = 2;
+ p[1] = 0;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_PC_RELATIVE_MODE; /* ...(pc) */
+ fragP->fr_fix += 2 + 2 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 6, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ break;
+
+ case STATE_COMPLEX_HOP:
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE; /* ...(pc) */
+ fragP->fr_fix += 1 + 2 + 1 + 1 + 4;
+ fix_new (fragP, old_fr_fix + 5, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ break;
+
+ case STATE_ALWAYS_BRANCH:
+ *fragP->fr_opcode += VAX_WIDEN_LONG;
+ p[0] = VAX_PC_RELATIVE_MODE; /* ...(PC) */
+ fragP->fr_fix += 1 + 4;
+ fix_new (fragP, old_fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ break;
+
+ default:
+ abort ();
+ }
+ frag_wane (fragP);
+
+ /* Return the growth in the fixed part of the frag. */
+ return fragP->fr_fix - old_fr_fix;
+ }
+
+ /* Relaxable cases. Set up the initial guess for the variable
+ part of the frag. */
+ switch (RELAX_STATE (fragP->fr_subtype))
+ {
+ case STATE_PC_RELATIVE:
+ fragP->fr_subtype = ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE);
+ break;
+ case STATE_CONDITIONAL_BRANCH:
+ fragP->fr_subtype = ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE);
+ break;
+ case STATE_COMPLEX_BRANCH:
+ fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD);
+ break;
+ case STATE_COMPLEX_HOP:
+ fragP->fr_subtype = ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE);
+ break;
+ case STATE_ALWAYS_BRANCH:
+ fragP->fr_subtype = ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE);
+ break;
+ }
+ }
+
+ if (fragP->fr_subtype >= sizeof (md_relax_table) / sizeof (md_relax_table[0]))
+ abort ();
+
+ /* Return the size of the variable part of the frag. */
+ return md_relax_table[fragP->fr_subtype].rlx_length;
+}
+
+/* Called after relax() is finished.
+ In: Address of frag.
+ fr_type == rs_machine_dependent.
+ fr_subtype is what the address relaxed to.
+
+ Out: Any fixSs and constants are set up.
+ Caller will turn frag into a ".space 0". */
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ fragS *fragP)
+{
+ char *addressP; /* -> _var to change. */
+ char *opcodeP; /* -> opcode char(s) to change. */
+ short int extension = 0; /* Size of relaxed address. */
+ /* Added to fr_fix: incl. ALL var chars. */
+ symbolS *symbolP;
+ long where;
+
+ know (fragP->fr_type == rs_machine_dependent);
+ where = fragP->fr_fix;
+ addressP = fragP->fr_literal + where;
+ opcodeP = fragP->fr_opcode;
+ symbolP = fragP->fr_symbol;
+ know (symbolP);
+
+ switch (fragP->fr_subtype)
+ {
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_BYTE):
+ know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xAF; /* Byte displacement. */
+ fix_new (fragP, fragP->fr_fix + 1, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD):
+ know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xCF; /* Word displacement. */
+ fix_new (fragP, fragP->fr_fix + 1, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 3;
+ break;
+
+ case ENCODE_RELAX (STATE_PC_RELATIVE, STATE_LONG):
+ know (*addressP == 0 || *addressP == 0x10); /* '@' bit. */
+ addressP[0] |= 0xEF; /* Long word displacement. */
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_BYTE):
+ fix_new (fragP, fragP->fr_fix, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_WORD):
+ opcodeP[0] ^= 1; /* Reverse sense of test. */
+ addressP[0] = 3;
+ addressP[1] = VAX_BRW;
+ fix_new (fragP, fragP->fr_fix + 2, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 4;
+ break;
+
+ case ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, STATE_LONG):
+ opcodeP[0] ^= 1; /* Reverse sense of test. */
+ addressP[0] = 6;
+ addressP[1] = VAX_JMP;
+ addressP[2] = VAX_PC_RELATIVE_MODE;
+ fix_new (fragP, fragP->fr_fix + 3, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 7;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_BYTE):
+ fix_new (fragP, fragP->fr_fix, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_WORD):
+ opcodeP[0] += VAX_WIDEN_WORD; /* brb -> brw, bsbb -> bsbw */
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol, fragP->fr_offset,
+ 1, NO_RELOC);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_ALWAYS_BRANCH, STATE_LONG):
+ opcodeP[0] += VAX_WIDEN_LONG; /* brb -> jmp, bsbb -> jsb */
+ addressP[0] = VAX_PC_RELATIVE_MODE;
+ fix_new (fragP, fragP->fr_fix + 1, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 5;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_WORD):
+ fix_new (fragP, fragP->fr_fix, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 2;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_BRANCH, STATE_LONG):
+ addressP[0] = 2;
+ addressP[1] = 0;
+ addressP[2] = VAX_BRB;
+ addressP[3] = 6;
+ addressP[4] = VAX_JMP;
+ addressP[5] = VAX_PC_RELATIVE_MODE;
+ fix_new (fragP, fragP->fr_fix + 6, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 10;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_BYTE):
+ fix_new (fragP, fragP->fr_fix, 1, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 1;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_WORD):
+ addressP[0] = 2;
+ addressP[1] = VAX_BRB;
+ addressP[2] = 3;
+ addressP[3] = VAX_BRW;
+ fix_new (fragP, fragP->fr_fix + 4, 2, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 6;
+ break;
+
+ case ENCODE_RELAX (STATE_COMPLEX_HOP, STATE_LONG):
+ addressP[0] = 2;
+ addressP[1] = VAX_BRB;
+ addressP[2] = 6;
+ addressP[3] = VAX_JMP;
+ addressP[4] = VAX_PC_RELATIVE_MODE;
+ fix_new (fragP, fragP->fr_fix + 5, 4, fragP->fr_symbol,
+ fragP->fr_offset, 1, NO_RELOC);
+ extension = 9;
+ break;
+
+ default:
+ BAD_CASE (fragP->fr_subtype);
+ break;
+ }
+ fragP->fr_fix += extension;
+}
+
+/* Translate internal format of relocation info into target format.
+
+ On vax: first 4 bytes are normal unsigned long, next three bytes
+ are symbolnum, least sig. byte first. Last byte is broken up with
+ the upper nibble as nuthin, bit 3 as extern, bits 2 & 1 as length, and
+ bit 0 as pcrel. */
+#ifdef comment
+void
+md_ri_to_chars (char *the_bytes, struct reloc_info_generic ri)
+{
+ /* This is easy. */
+ md_number_to_chars (the_bytes, ri.r_address, sizeof (ri.r_address));
+ /* Now the fun stuff. */
+ the_bytes[6] = (ri.r_symbolnum >> 16) & 0x0ff;
+ the_bytes[5] = (ri.r_symbolnum >> 8) & 0x0ff;
+ the_bytes[4] = ri.r_symbolnum & 0x0ff;
+ the_bytes[7] = (((ri.r_extern << 3) & 0x08) | ((ri.r_length << 1) & 0x06)
+ | ((ri.r_pcrel << 0) & 0x01)) & 0x0F;
+}
+
+#endif /* comment */
+
+/* BUGS, GRIPES, APOLOGIA, etc.
+
+ The opcode table 'votstrs' needs to be sorted on opcode frequency.
+ That is, AFTER we hash it with hash_...(), we want most-used opcodes
+ to come out of the hash table faster.
+
+ I am sorry to inflict yet another VAX assembler on the world, but
+ RMS says we must do everything from scratch, to prevent pin-heads
+ restricting this software.
+
+ This is a vaguely modular set of routines in C to parse VAX
+ assembly code using DEC mnemonics. It is NOT un*x specific.
+
+ The idea here is that the assembler has taken care of all:
+ labels
+ macros
+ listing
+ pseudo-ops
+ line continuation
+ comments
+ condensing any whitespace down to exactly one space
+ and all we have to do is parse 1 line into a vax instruction
+ partially formed. We will accept a line, and deliver:
+ an error message (hopefully empty)
+ a skeleton VAX instruction (tree structure)
+ textual pointers to all the operand expressions
+ a warning message that notes a silly operand (hopefully empty)
+
+ E D I T H I S T O R Y
+
+ 17may86 Dean Elsner. Bug if line ends immediately after opcode.
+ 30apr86 Dean Elsner. New vip_op() uses arg block so change call.
+ 6jan86 Dean Elsner. Crock vip_begin() to call vip_op_defaults().
+ 2jan86 Dean Elsner. Invent synthetic opcodes.
+ Widen vax_opcodeT to 32 bits. Use a bit for VIT_OPCODE_SYNTHETIC,
+ which means this is not a real opcode, it is like a macro; it will
+ be relax()ed into 1 or more instructions.
+ Use another bit for VIT_OPCODE_SPECIAL if the op-code is not optimised
+ like a regular branch instruction. Option added to vip_begin():
+ exclude synthetic opcodes. Invent synthetic_votstrs[].
+ 31dec85 Dean Elsner. Invent vit_opcode_nbytes.
+ Also make vit_opcode into a char[]. We now have n-byte vax opcodes,
+ so caller's don't have to know the difference between a 1-byte & a
+ 2-byte op-code. Still need vax_opcodeT concept, so we know how
+ big an object must be to hold an op.code.
+ 30dec85 Dean Elsner. Widen typedef vax_opcodeT in "vax-inst.h"
+ because vax opcodes may be 16 bits. Our crufty C compiler was
+ happily initialising 8-bit vot_codes with 16-bit numbers!
+ (Wouldn't the 'phone company like to compress data so easily!)
+ 29dec85 Dean Elsner. New static table vax_operand_width_size[].
+ Invented so we know hw many bytes a "I^#42" needs in its immediate
+ operand. Revised struct vop in "vax-inst.h": explicitly include
+ byte length of each operand, and it's letter-code datum type.
+ 17nov85 Dean Elsner. Name Change.
+ Due to ar(1) truncating names, we learned the hard way that
+ "vax-inst-parse.c" -> "vax-inst-parse." dropping the "o" off
+ the archived object name. SO... we shortened the name of this
+ source file, and changed the makefile. */
+
+/* Handle of the OPCODE hash table. */
+static struct hash_control *op_hash;
+
+/* In: 1 character, from "bdfghloqpw" being the data-type of an operand
+ of a vax instruction.
+
+ Out: the length of an operand of that type, in bytes.
+ Special branch operands types "-?!" have length 0. */
+
+static const short int vax_operand_width_size[256] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 0, 8, 0, 4, 8, 16, 0, 0, 0, 4, 0, 0,16, /* ..b.d.fgh...l..o */
+ 0, 8, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, /* .q.....w........ */
+ 0, 0, 1, 0, 8, 0, 4, 8, 16, 0, 0, 0, 4, 0, 0,16, /* ..b.d.fgh...l..o */
+ 0, 8, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, /* .q.....w........ */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+/* This perversion encodes all the vax opcodes as a bunch of strings.
+ RMS says we should build our hash-table at run-time. Hmm.
+ Please would someone arrange these in decreasing frequency of opcode?
+ Because of the way hash_...() works, the most frequently used opcode
+ should be textually first and so on.
+
+ Input for this table was 'vax.opcodes', awk(1)ed by 'vax.opcodes.c.awk' .
+ So change 'vax.opcodes', then re-generate this table. */
+
+#include "opcode/vax.h"
+
+/* This is a table of optional op-codes. All of them represent
+ 'synthetic' instructions that seem popular.
+
+ Here we make some pseudo op-codes. Every code has a bit set to say
+ it is synthetic. This lets you catch them if you want to
+ ban these opcodes. They are mnemonics for "elastic" instructions
+ that are supposed to assemble into the fewest bytes needed to do a
+ branch, or to do a conditional branch, or whatever.
+
+ The opcode is in the usual place [low-order n*8 bits]. This means
+ that if you mask off the bucky bits, the usual rules apply about
+ how long the opcode is.
+
+ All VAX branch displacements come at the end of the instruction.
+ For simple branches (1-byte opcode + 1-byte displacement) the last
+ operand is coded 'b?' where the "data type" '?' is a clue that we
+ may reverse the sense of the branch (complement lowest order bit)
+ and branch around a jump. This is by far the most common case.
+ That is why the VIT_OPCODE_SYNTHETIC bit is set: it says this is
+ a 0-byte op-code followed by 2 or more bytes of operand address.
+
+ If the op-code has VIT_OPCODE_SPECIAL set, then we have a more unusual
+ case.
+
+ For JBSB & JBR the treatment is the similar, except (1) we have a 'bw'
+ option before (2) we can directly JSB/JMP because there is no condition.
+ These operands have 'b-' as their access/data type.
+
+ That leaves a bunch of random opcodes: JACBx, JxOBxxx. In these
+ cases, we do the same idea. JACBxxx are all marked with a 'b!'
+ JAOBxxx & JSOBxxx are marked with a 'b:'. */
+#if (VIT_OPCODE_SYNTHETIC != 0x80000000)
+#error "You have just broken the encoding below, which assumes the sign bit means 'I am an imaginary instruction'."
+#endif
+
+#if (VIT_OPCODE_SPECIAL != 0x40000000)
+#error "You have just broken the encoding below, which assumes the 0x40 M bit means 'I am not to be "optimised" the way normal branches are'."
+#endif
+
+static const struct vot
+ synthetic_votstrs[] =
+{
+ {"jbsb", {"b-", 0xC0000010}}, /* BSD 4.2 */
+/* jsb used already */
+ {"jbr", {"b-", 0xC0000011}}, /* BSD 4.2 */
+ {"jr", {"b-", 0xC0000011}}, /* consistent */
+ {"jneq", {"b?", 0x80000012}},
+ {"jnequ", {"b?", 0x80000012}},
+ {"jeql", {"b?", 0x80000013}},
+ {"jeqlu", {"b?", 0x80000013}},
+ {"jgtr", {"b?", 0x80000014}},
+ {"jleq", {"b?", 0x80000015}},
+/* un-used opcodes here */
+ {"jgeq", {"b?", 0x80000018}},
+ {"jlss", {"b?", 0x80000019}},
+ {"jgtru", {"b?", 0x8000001a}},
+ {"jlequ", {"b?", 0x8000001b}},
+ {"jvc", {"b?", 0x8000001c}},
+ {"jvs", {"b?", 0x8000001d}},
+ {"jgequ", {"b?", 0x8000001e}},
+ {"jcc", {"b?", 0x8000001e}},
+ {"jlssu", {"b?", 0x8000001f}},
+ {"jcs", {"b?", 0x8000001f}},
+
+ {"jacbw", {"rwrwmwb!", 0xC000003d}},
+ {"jacbf", {"rfrfmfb!", 0xC000004f}},
+ {"jacbd", {"rdrdmdb!", 0xC000006f}},
+ {"jacbb", {"rbrbmbb!", 0xC000009d}},
+ {"jacbl", {"rlrlmlb!", 0xC00000f1}},
+ {"jacbg", {"rgrgmgb!", 0xC0004ffd}},
+ {"jacbh", {"rhrhmhb!", 0xC0006ffd}},
+
+ {"jbs", {"rlvbb?", 0x800000e0}},
+ {"jbc", {"rlvbb?", 0x800000e1}},
+ {"jbss", {"rlvbb?", 0x800000e2}},
+ {"jbcs", {"rlvbb?", 0x800000e3}},
+ {"jbsc", {"rlvbb?", 0x800000e4}},
+ {"jbcc", {"rlvbb?", 0x800000e5}},
+ {"jbssi", {"rlvbb?", 0x800000e6}},
+ {"jbcci", {"rlvbb?", 0x800000e7}},
+ {"jlbs", {"rlb?", 0x800000e8}},
+ {"jlbc", {"rlb?", 0x800000e9}},
+
+ {"jaoblss", {"rlmlb:", 0xC00000f2}},
+ {"jaobleq", {"rlmlb:", 0xC00000f3}},
+ {"jsobgeq", {"mlb:", 0xC00000f4}},
+ {"jsobgtr", {"mlb:", 0xC00000f5}},
+
+/* CASEx has no branch addresses in our conception of it. */
+/* You should use ".word ..." statements after the "case ...". */
+
+ {"", {"", 0}} /* Empty is end sentinel. */
+};
+
+/* Because this module is useful for both VMS and UN*X style assemblers
+ and because of the variety of UN*X assemblers we must recognise
+ the different conventions for assembler operand notation. For example
+ VMS says "#42" for immediate mode, while most UN*X say "$42".
+ We permit arbitrary sets of (single) characters to represent the
+ 3 concepts that DEC writes '#', '@', '^'. */
+
+/* Character tests. */
+#define VIP_IMMEDIATE 01 /* Character is like DEC # */
+#define VIP_INDIRECT 02 /* Char is like DEC @ */
+#define VIP_DISPLEN 04 /* Char is like DEC ^ */
+
+#define IMMEDIATEP(c) (vip_metacharacters [(c) & 0xff] & VIP_IMMEDIATE)
+#define INDIRECTP(c) (vip_metacharacters [(c) & 0xff] & VIP_INDIRECT)
+#define DISPLENP(c) (vip_metacharacters [(c) & 0xff] & VIP_DISPLEN)
+
+/* We assume 8 bits per byte. Use vip_op_defaults() to set these up BEFORE we
+ are ever called. */
+
+#if defined(CONST_TABLE)
+#define _ 0,
+#define I VIP_IMMEDIATE,
+#define S VIP_INDIRECT,
+#define D VIP_DISPLEN,
+static const char
+vip_metacharacters[256] =
+{
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /* ^@ ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M ^N ^O*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /* ^P ^Q ^R ^S ^T ^U ^V ^W ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */
+ _ _ _ _ I _ _ _ _ _ S _ _ _ _ _ /* sp ! " # $ % & ' ( ) * + , - . / */
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*0 1 2 3 4 5 6 7 8 9 : ; < = > ?*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*@ A B C D E F G H I J K L M N O*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*P Q R S T U V W X Y Z [ \ ] ^ _*/
+ D _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*` a b c d e f g h i j k l m n o*/
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /*p q r s t u v w x y z { | } ~ ^?*/
+
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
+};
+#undef _
+#undef I
+#undef S
+#undef D
+
+#else
+
+static char vip_metacharacters[256];
+
+static void
+vip_op_1 (int bit, const char *syms)
+{
+ unsigned char t;
+
+ while ((t = *syms++) != 0)
+ vip_metacharacters[t] |= bit;
+}
+
+/* Can be called any time. More arguments may appear in future. */
+static void
+vip_op_defaults (const char *immediate, const char *indirect, const char *displen)
+{
+ vip_op_1 (VIP_IMMEDIATE, immediate);
+ vip_op_1 (VIP_INDIRECT, indirect);
+ vip_op_1 (VIP_DISPLEN, displen);
+}
+
+#endif
+
+/* Call me once before you decode any lines.
+ I decode votstrs into a hash table at op_hash (which I create).
+ I return an error text or null.
+ If you want, I will include the 'synthetic' jXXX instructions in the
+ instruction table.
+ You must nominate metacharacters for eg DEC's "#", "@", "^". */
+
+static const char *
+vip_begin (int synthetic_too, /* 1 means include jXXX op-codes. */
+ const char *immediate,
+ const char *indirect,
+ const char *displen)
+{
+ const struct vot *vP; /* scan votstrs */
+ const char *retval = 0; /* error text */
+
+ op_hash = hash_new ();
+
+ for (vP = votstrs; *vP->vot_name && !retval; vP++)
+ retval = hash_insert (op_hash, vP->vot_name, (void *) &vP->vot_detail);
+
+ if (synthetic_too)
+ for (vP = synthetic_votstrs; *vP->vot_name && !retval; vP++)
+ retval = hash_insert (op_hash, vP->vot_name, (void *) &vP->vot_detail);
+
+#ifndef CONST_TABLE
+ vip_op_defaults (immediate, indirect, displen);
+#endif
+
+ return retval;
+}
+
+/* Take 3 char.s, the last of which may be `\0` (non-existent)
+ and return the VAX register number that they represent.
+
+ Return -1 if they don't form a register name. Good names return
+ a number from 0:15 inclusive.
+
+ Case is not important in a name.
+
+ Register names understood are:
+
+ R0
+ R1
+ R2
+ R3
+ R4
+ R5
+ R6
+ R7
+ R8
+ R9
+ R10
+ R11
+ R12 AP
+ R13 FP
+ R14 SP
+ R15 PC */
+
+#define AP 12
+#define FP 13
+#define SP 14
+#define PC 15
+
+/* Returns the register number of something like '%r15' or 'ap', supplied
+ in four single chars. Returns -1 if the register isn't recognized,
+ 0..15 otherwise. */
+static int
+vax_reg_parse (char c1, char c2, char c3, char c4)
+{
+ int retval = -1;
+
+#ifdef OBJ_ELF
+ if (c1 != '%') /* Register prefixes are mandatory for ELF. */
+ return retval;
+ c1 = c2;
+ c2 = c3;
+ c3 = c4;
+#endif
+#ifdef OBJ_VMS
+ if (c4 != 0) /* Register prefixes are not allowed under VMS. */
+ return retval;
+#endif
+#ifdef OBJ_AOUT
+ if (c1 == '%') /* Register prefixes are optional under a.out. */
+ {
+ c1 = c2;
+ c2 = c3;
+ c3 = c4;
+ }
+ else if (c3 && c4) /* Can't be 4 characters long. */
+ return retval;
+#endif
+
+ c1 = TOLOWER (c1);
+ c2 = TOLOWER (c2);
+ if (ISDIGIT (c2) && c1 == 'r')
+ {
+ retval = c2 - '0';
+ if (ISDIGIT (c3))
+ {
+ retval = retval * 10 + c3 - '0';
+ retval = (retval > 15) ? -1 : retval;
+ /* clamp the register value to 1 hex digit */
+ }
+ else if (c3)
+ retval = -1; /* c3 must be '\0' or a digit. */
+ }
+ else if (c3) /* There are no three letter regs. */
+ retval = -1;
+ else if (c2 == 'p')
+ {
+ switch (c1)
+ {
+ case 's':
+ retval = SP;
+ break;
+ case 'f':
+ retval = FP;
+ break;
+ case 'a':
+ retval = AP;
+ break;
+ default:
+ retval = -1;
+ }
+ }
+ else if (c1 == 'p' && c2 == 'c')
+ retval = PC;
+ else
+ retval = -1;
+ return retval;
+}
+
+/* Parse a vax operand in DEC assembler notation.
+ For speed, expect a string of whitespace to be reduced to a single ' '.
+ This is the case for GNU AS, and is easy for other DEC-compatible
+ assemblers.
+
+ Knowledge about DEC VAX assembler operand notation lives here.
+ This doesn't even know what a register name is, except it believes
+ all register names are 2 or 3 characters, and lets vax_reg_parse() say
+ what number each name represents.
+ It does, however, know that PC, SP etc are special registers so it can
+ detect addressing modes that are silly for those registers.
+
+ Where possible, it delivers 1 fatal or 1 warning message if the operand
+ is suspect. Exactly what we test for is still evolving.
+
+ ---
+ Arg block.
+
+ There were a number of 'mismatched argument type' bugs to vip_op.
+ The most general solution is to typedef each (of many) arguments.
+ We used instead a typedef'd argument block. This is less modular
+ than using separate return pointers for each result, but runs faster
+ on most engines, and seems to keep programmers happy. It will have
+ to be done properly if we ever want to use vip_op as a general-purpose
+ module (it was designed to be).
+
+ G^
+
+ Doesn't support DEC "G^" format operands. These always take 5 bytes
+ to express, and code as modes 8F or 9F. Reason: "G^" deprives you of
+ optimising to (say) a "B^" if you are lucky in the way you link.
+ When someone builds a linker smart enough to convert "G^" to "B^", "W^"
+ whenever possible, then we should implement it.
+ If there is some other use for "G^", feel free to code it in!
+
+ speed
+
+ If I nested if()s more, I could avoid testing (*err) which would save
+ time, space and page faults. I didn't nest all those if()s for clarity
+ and because I think the mode testing can be re-arranged 1st to test the
+ commoner constructs 1st. Does anybody have statistics on this?
+
+ error messages
+
+ In future, we should be able to 'compose' error messages in a scratch area
+ and give the user MUCH more informative error messages. Although this takes
+ a little more code at run-time, it will make this module much more self-
+ documenting. As an example of what sucks now: most error messages have
+ hardwired into them the DEC VAX metacharacters "#^@" which are nothing like
+ the Un*x characters "$`*", that most users will expect from this AS.
+
+ ----
+
+ The input is a string, ending with '\0'.
+
+ We also require a 'hint' of what kind of operand is expected: so
+ we can remind caller not to write into literals for instance.
+
+ The output is a skeletal instruction.
+
+ The algorithm has two parts.
+ 1. extract the syntactic features (parse off all the @^#-()+[] mode crud);
+ 2. express the @^#-()+[] as some parameters suited to further analysis.
+
+ 2nd step is where we detect the googles of possible invalid combinations
+ a human (or compiler) might write. Note that if we do a half-way
+ decent assembler, we don't know how long to make (eg) displacement
+ fields when we first meet them (because they may not have defined values).
+ So we must wait until we know how many bits are needed for each address,
+ then we can know both length and opcodes of instructions.
+ For reason(s) above, we will pass to our caller a 'broken' instruction
+ of these major components, from which our caller can generate instructions:
+ - displacement length I^ S^ L^ B^ W^ unspecified
+ - mode (many)
+ - register R0-R15 or absent
+ - index register R0-R15 or absent
+ - expression text what we don't parse
+ - error text(s) why we couldn't understand the operand
+
+ ----
+
+ To decode output of this, test errtxt. If errtxt[0] == '\0', then
+ we had no errors that prevented parsing. Also, if we ever report
+ an internal bug, errtxt[0] is set non-zero. So one test tells you
+ if the other outputs are to be taken seriously.
+
+ ----
+
+ Dec defines the semantics of address modes (and values)
+ by a two-letter code, explained here.
+
+ letter 1: access type
+
+ a address calculation - no data access, registers forbidden
+ b branch displacement
+ m read - let go of bus - write back "modify"
+ r read
+ v bit field address: like 'a' but registers are OK
+ w write
+ space no operator (eg ".long foo") [our convention]
+
+ letter 2: data type (i.e. width, alignment)
+
+ b byte
+ d double precision floating point (D format)
+ f single precision floating point (F format)
+ g G format floating
+ h H format floating
+ l longword
+ o octaword
+ q quadword
+ w word
+ ? simple synthetic branch operand
+ - unconditional synthetic JSB/JSR operand
+ ! complex synthetic branch operand
+
+ The '-?!' letter 2's are not for external consumption. They are used
+ for various assemblers. Generally, all unknown widths are assumed 0.
+ We don't limit your choice of width character.
+
+ DEC operands are hard work to parse. For example, '@' as the first
+ character means indirect (deferred) mode but elsewhere it is a shift
+ operator.
+ The long-winded explanation of how this is supposed to work is
+ cancelled. Read a DEC vax manual.
+ We try hard not to parse anything that MIGHT be part of the expression
+ buried in that syntax. For example if we see @...(Rn) we don't check
+ for '-' before the '(' because mode @-(Rn) does not exist.
+
+ After parsing we have:
+
+ at 1 if leading '@' (or Un*x '*')
+ len takes one value from " bilsw". eg B^ -> 'b'.
+ hash 1 if leading '#' (or Un*x '$')
+ expr_begin, expr_end the expression we did not parse
+ even though we don't interpret it, we make use
+ of its presence or absence.
+ sign -1: -(Rn) 0: absent +1: (Rn)+
+ paren 1 if () are around register
+ reg major register number 0:15 -1 means absent
+ ndx index register number 0:15 -1 means absent
+
+ Again, I dare not explain it: just trace ALL the code!
+
+ Summary of vip_op outputs.
+
+ mode reg len ndx
+ (Rn) => @Rn
+ {@}Rn 5+@ n ' ' optional
+ branch operand 0 -1 ' ' -1
+ S^#foo 0 -1 's' -1
+ -(Rn) 7 n ' ' optional
+ {@}(Rn)+ 8+@ n ' ' optional
+ {@}#foo, no S^ 8+@ PC " i" optional
+ {@}{q^}{(Rn)} 10+@+q option " bwl" optional */
+
+/* Dissect user-input 'optext' (which is something like "@B^foo@bar(AP)[FP]:")
+ using the vop in vopP. vopP's vop_access and vop_width. We fill _ndx, _reg,
+ _mode, _short, _warn, _error, _expr_begin, _expr_end and _nbytes. */
+
+static void
+vip_op (char *optext, struct vop *vopP)
+{
+ /* Track operand text forward. */
+ char *p;
+ /* Track operand text backward. */
+ char *q;
+ /* 1 if leading '@' ('*') seen. */
+ int at;
+ /* one of " bilsw" */
+ char len;
+ /* 1 if leading '#' ('$') seen. */
+ int hash;
+ /* -1, 0 or +1. */
+ int sign = 0;
+ /* 1 if () surround register. */
+ int paren = 0;
+ /* Register number, -1:absent. */
+ int reg = 0;
+ /* Index register number -1:absent. */
+ int ndx = 0;
+ /* Report illegal operand, ""==OK. */
+ /* " " is a FAKE error: means we won. */
+ /* ANY err that begins with ' ' is a fake. */
+ /* " " is converted to "" before return. */
+ const char *err;
+ /* Warn about weird modes pf address. */
+ const char *wrn;
+ /* Preserve q in case we backup. */
+ char *oldq = NULL;
+ /* Build up 4-bit operand mode here. */
+ /* Note: index mode is in ndx, this is. */
+ /* The major mode of operand address. */
+ int mode = 0;
+ /* Notice how we move wrong-arg-type bugs INSIDE this module: if we
+ get the types wrong below, we lose at compile time rather than at
+ lint or run time. */
+ char access_mode; /* vop_access. */
+
+ access_mode = vopP->vop_access;
+ /* None of our code bugs (yet), no user text errors, no warnings
+ even. */
+ err = wrn = 0;
+
+ p = optext;
+
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+
+ if ((at = INDIRECTP (*p)) != 0)
+ { /* 1 if *p=='@'(or '*' for Un*x) */
+ p++; /* at is determined */
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++; /* skip over whitespace */
+ }
+
+ /* This code is subtle. It tries to detect all legal (letter)'^'
+ but it doesn't waste time explicitly testing for premature '\0' because
+ this case is rejected as a mismatch against either (letter) or '^'. */
+ {
+ char c;
+
+ c = *p;
+ c = TOLOWER (c);
+ if (DISPLENP (p[1]) && strchr ("bilws", len = c))
+ p += 2; /* Skip (letter) '^'. */
+ else /* No (letter) '^' seen. */
+ len = ' '; /* Len is determined. */
+ }
+
+ if (*p == ' ') /* Expect all whitespace reduced to ' '. */
+ p++;
+
+ if ((hash = IMMEDIATEP (*p)) != 0) /* 1 if *p=='#' ('$' for Un*x) */
+ p++; /* Hash is determined. */
+
+ /* p points to what may be the beginning of an expression.
+ We have peeled off the front all that is peelable.
+ We know at, len, hash.
+
+ Lets point q at the end of the text and parse that (backwards). */
+
+ for (q = p; *q; q++)
+ ;
+ q--; /* Now q points at last char of text. */
+
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+
+ /* Reverse over whitespace, but don't. */
+ /* Run back over *p. */
+
+ /* As a matter of policy here, we look for [Rn], although both Rn and S^#
+ forbid [Rn]. This is because it is easy, and because only a sick
+ cyborg would have [...] trailing an expression in a VAX-like assembler.
+ A meticulous parser would first check for Rn followed by '(' or '['
+ and not parse a trailing ']' if it found another. We just ban expressions
+ ending in ']'. */
+ if (*q == ']')
+ {
+ while (q >= p && *q != '[')
+ q--;
+ /* Either q<p or we got matching '['. */
+ if (q < p)
+ err = _("no '[' to match ']'");
+ else
+ {
+ /* Confusers like "[]" will eventually lose with a bad register
+ * name error. So again we don't need to check for early '\0'. */
+ if (q[3] == ']')
+ ndx = vax_reg_parse (q[1], q[2], 0, 0);
+ else if (q[4] == ']')
+ ndx = vax_reg_parse (q[1], q[2], q[3], 0);
+ else if (q[5] == ']')
+ ndx = vax_reg_parse (q[1], q[2], q[3], q[4]);
+ else
+ ndx = -1;
+ /* Since we saw a ']' we will demand a register name in the [].
+ * If luser hasn't given us one: be rude. */
+ if (ndx < 0)
+ err = _("bad register in []");
+ else if (ndx == PC)
+ err = _("[PC] index banned");
+ else
+ /* Point q just before "[...]". */
+ q--;
+ }
+ }
+ else
+ /* No ']', so no iNDeX register. */
+ ndx = -1;
+
+ /* If err = "..." then we lost: run away.
+ Otherwise ndx == -1 if there was no "[...]".
+ Otherwise, ndx is index register number, and q points before "[...]". */
+
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* Reverse over whitespace, but don't. */
+ /* Run back over *p. */
+ if (!err || !*err)
+ {
+ /* no ()+ or -() seen yet */
+ sign = 0;
+
+ if (q > p + 3 && *q == '+' && q[-1] == ')')
+ {
+ sign = 1; /* we saw a ")+" */
+ q--; /* q points to ')' */
+ }
+
+ if (*q == ')' && q > p + 2)
+ {
+ paren = 1; /* assume we have "(...)" */
+ while (q >= p && *q != '(')
+ q--;
+ /* either q<p or we got matching '(' */
+ if (q < p)
+ err = _("no '(' to match ')'");
+ else
+ {
+ /* Confusers like "()" will eventually lose with a bad register
+ name error. So again we don't need to check for early '\0'. */
+ if (q[3] == ')')
+ reg = vax_reg_parse (q[1], q[2], 0, 0);
+ else if (q[4] == ')')
+ reg = vax_reg_parse (q[1], q[2], q[3], 0);
+ else if (q[5] == ')')
+ reg = vax_reg_parse (q[1], q[2], q[3], q[4]);
+ else
+ reg = -1;
+ /* Since we saw a ')' we will demand a register name in the ')'.
+ This is nasty: why can't our hypothetical assembler permit
+ parenthesised expressions? BECAUSE I AM LAZY! That is why.
+ Abuse luser if we didn't spy a register name. */
+ if (reg < 0)
+ {
+ /* JF allow parenthesized expressions. I hope this works. */
+ paren = 0;
+ while (*q != ')')
+ q++;
+ /* err = "unknown register in ()"; */
+ }
+ else
+ q--; /* point just before '(' of "(...)" */
+ /* If err == "..." then we lost. Run away.
+ Otherwise if reg >= 0 then we saw (Rn). */
+ }
+ /* If err == "..." then we lost.
+ Otherwise paren==1 and reg = register in "()". */
+ }
+ else
+ paren = 0;
+ /* If err == "..." then we lost.
+ Otherwise, q points just before "(Rn)", if any.
+ If there was a "(...)" then paren==1, and reg is the register. */
+
+ /* We should only seek '-' of "-(...)" if:
+ we saw "(...)" paren == 1
+ we have no errors so far ! *err
+ we did not see '+' of "(...)+" sign < 1
+ We don't check len. We want a specific error message later if
+ user tries "x^...-(Rn)". This is a feature not a bug. */
+ if (!err || !*err)
+ {
+ if (paren && sign < 1)/* !sign is adequate test */
+ {
+ if (*q == '-')
+ {
+ sign = -1;
+ q--;
+ }
+ }
+ /* We have back-tracked over most
+ of the crud at the end of an operand.
+ Unless err, we know: sign, paren. If paren, we know reg.
+ The last case is of an expression "Rn".
+ This is worth hunting for if !err, !paren.
+ We wouldn't be here if err.
+ We remember to save q, in case we didn't want "Rn" anyway. */
+ if (!paren)
+ {
+ if (*q == ' ' && q >= p) /* Expect all whitespace reduced to ' '. */
+ q--;
+ /* Reverse over whitespace, but don't. */
+ /* Run back over *p. */
+ /* Room for Rn or Rnn (include prefix) exactly? */
+ if (q > p && q < p + 4)
+ reg = vax_reg_parse (p[0], p[1],
+ q < p + 2 ? 0 : p[2],
+ q < p + 3 ? 0 : p[3]);
+ else
+ reg = -1; /* Always comes here if no register at all. */
+ /* Here with a definitive reg value. */
+ if (reg >= 0)
+ {
+ oldq = q;
+ q = p - 1;
+ }
+ }
+ }
+ }
+ /* have reg. -1:absent; else 0:15. */
+
+ /* We have: err, at, len, hash, ndx, sign, paren, reg.
+ Also, any remaining expression is from *p through *q inclusive.
+ Should there be no expression, q==p-1. So expression length = q-p+1.
+ This completes the first part: parsing the operand text. */
+
+ /* We now want to boil the data down, checking consistency on the way.
+ We want: len, mode, reg, ndx, err, p, q, wrn, bug.
+ We will deliver a 4-bit reg, and a 4-bit mode. */
+
+ /* Case of branch operand. Different. No L^B^W^I^S^ allowed for instance.
+
+ in: at ?
+ len ?
+ hash ?
+ p:q ?
+ sign ?
+ paren ?
+ reg ?
+ ndx ?
+
+ out: mode 0
+ reg -1
+ len ' '
+ p:q whatever was input
+ ndx -1
+ err " " or error message, and other outputs trashed. */
+ /* Branch operands have restricted forms. */
+ if ((!err || !*err) && access_mode == 'b')
+ {
+ if (at || hash || sign || paren || ndx >= 0 || reg >= 0 || len != ' ')
+ err = _("invalid branch operand");
+ else
+ err = " ";
+ }
+
+ /* Since nobody seems to use it: comment this 'feature'(?) out for now. */
+#ifdef NEVER
+ /* Case of stand-alone operand. e.g. ".long foo"
+
+ in: at ?
+ len ?
+ hash ?
+ p:q ?
+ sign ?
+ paren ?
+ reg ?
+ ndx ?
+
+ out: mode 0
+ reg -1
+ len ' '
+ p:q whatever was input
+ ndx -1
+ err " " or error message, and other outputs trashed. */
+ if ((!err || !*err) && access_mode == ' ')
+ {
+ if (at)
+ err = _("address prohibits @");
+ else if (hash)
+ err = _("address prohibits #");
+ else if (sign)
+ {
+ if (sign < 0)
+ err = _("address prohibits -()");
+ else
+ err = _("address prohibits ()+");
+ }
+ else if (paren)
+ err = _("address prohibits ()");
+ else if (ndx >= 0)
+ err = _("address prohibits []");
+ else if (reg >= 0)
+ err = _("address prohibits register");
+ else if (len != ' ')
+ err = _("address prohibits displacement length specifier");
+ else
+ {
+ err = " "; /* succeed */
+ mode = 0;
+ }
+ }
+#endif
+
+ /* Case of S^#.
+
+ in: at 0
+ len 's' definition
+ hash 1 demand
+ p:q demand not empty
+ sign 0 by paren==0
+ paren 0 by "()" scan logic because "S^" seen
+ reg -1 or nn by mistake
+ ndx -1
+
+ out: mode 0
+ reg -1
+ len 's'
+ exp
+ ndx -1 */
+ if ((!err || !*err) && len == 's')
+ {
+ if (!hash || paren || at || ndx >= 0)
+ err = _("invalid operand of S^#");
+ else
+ {
+ if (reg >= 0)
+ {
+ /* Darn! we saw S^#Rnn ! put the Rnn back in
+ expression. KLUDGE! Use oldq so we don't
+ need to know exact length of reg name. */
+ q = oldq;
+ reg = 0;
+ }
+ /* We have all the expression we will ever get. */
+ if (p > q)
+ err = _("S^# needs expression");
+ else if (access_mode == 'r')
+ {
+ err = " "; /* WIN! */
+ mode = 0;
+ }
+ else
+ err = _("S^# may only read-access");
+ }
+ }
+
+ /* Case of -(Rn), which is weird case.
+
+ in: at 0
+ len '
+ hash 0
+ p:q q<p
+ sign -1 by definition
+ paren 1 by definition
+ reg present by definition
+ ndx optional
+
+ out: mode 7
+ reg present
+ len ' '
+ exp "" enforce empty expression
+ ndx optional warn if same as reg. */
+ if ((!err || !*err) && sign < 0)
+ {
+ if (len != ' ' || hash || at || p <= q)
+ err = _("invalid operand of -()");
+ else
+ {
+ err = " "; /* win */
+ mode = 7;
+ if (reg == PC)
+ wrn = _("-(PC) unpredictable");
+ else if (reg == ndx)
+ wrn = _("[]index same as -()register: unpredictable");
+ }
+ }
+
+ /* We convert "(Rn)" to "@Rn" for our convenience.
+ (I hope this is convenient: has someone got a better way to parse this?)
+ A side-effect of this is that "@Rn" is a valid operand. */
+ if (paren && !sign && !hash && !at && len == ' ' && p > q)
+ {
+ at = 1;
+ paren = 0;
+ }
+
+ /* Case of (Rn)+, which is slightly different.
+
+ in: at
+ len ' '
+ hash 0
+ p:q q<p
+ sign +1 by definition
+ paren 1 by definition
+ reg present by definition
+ ndx optional
+
+ out: mode 8+@
+ reg present
+ len ' '
+ exp "" enforce empty expression
+ ndx optional warn if same as reg. */
+ if ((!err || !*err) && sign > 0)
+ {
+ if (len != ' ' || hash || p <= q)
+ err = _("invalid operand of ()+");
+ else
+ {
+ err = " "; /* win */
+ mode = 8 + (at ? 1 : 0);
+ if (reg == PC)
+ wrn = _("(PC)+ unpredictable");
+ else if (reg == ndx)
+ wrn = _("[]index same as ()+register: unpredictable");
+ }
+ }
+
+ /* Case of #, without S^.
+
+ in: at
+ len ' ' or 'i'
+ hash 1 by definition
+ p:q
+ sign 0
+ paren 0
+ reg absent
+ ndx optional
+
+ out: mode 8+@
+ reg PC
+ len ' ' or 'i'
+ exp
+ ndx optional. */
+ if ((!err || !*err) && hash)
+ {
+ if (len != 'i' && len != ' ')
+ err = _("# conflicts length");
+ else if (paren)
+ err = _("# bars register");
+ else
+ {
+ if (reg >= 0)
+ {
+ /* Darn! we saw #Rnn! Put the Rnn back into the expression.
+ By using oldq, we don't need to know how long Rnn was.
+ KLUDGE! */
+ q = oldq;
+ reg = -1; /* No register any more. */
+ }
+ err = " "; /* Win. */
+
+ /* JF a bugfix, I think! */
+ if (at && access_mode == 'a')
+ vopP->vop_nbytes = 4;
+
+ mode = (at ? 9 : 8);
+ reg = PC;
+ if ((access_mode == 'm' || access_mode == 'w') && !at)
+ wrn = _("writing or modifying # is unpredictable");
+ }
+ }
+ /* If !*err, then sign == 0
+ hash == 0 */
+
+ /* Case of Rn. We separate this one because it has a few special
+ errors the remaining modes lack.
+
+ in: at optional
+ len ' '
+ hash 0 by program logic
+ p:q empty
+ sign 0 by program logic
+ paren 0 by definition
+ reg present by definition
+ ndx optional
+
+ out: mode 5+@
+ reg present
+ len ' ' enforce no length
+ exp "" enforce empty expression
+ ndx optional warn if same as reg. */
+ if ((!err || !*err) && !paren && reg >= 0)
+ {
+ if (len != ' ')
+ err = _("length not needed");
+ else if (at)
+ {
+ err = " "; /* win */
+ mode = 6; /* @Rn */
+ }
+ else if (ndx >= 0)
+ err = _("can't []index a register, because it has no address");
+ else if (access_mode == 'a')
+ err = _("a register has no address");
+ else
+ {
+ /* Idea here is to detect from length of datum
+ and from register number if we will touch PC.
+ Warn if we do.
+ vop_nbytes is number of bytes in operand.
+ Compute highest byte affected, compare to PC0. */
+ if ((vopP->vop_nbytes + reg * 4) > 60)
+ wrn = _("PC part of operand unpredictable");
+ err = " "; /* win */
+ mode = 5; /* Rn */
+ }
+ }
+ /* If !*err, sign == 0
+ hash == 0
+ paren == 1 OR reg==-1 */
+
+ /* Rest of cases fit into one bunch.
+
+ in: at optional
+ len ' ' or 'b' or 'w' or 'l'
+ hash 0 by program logic
+ p:q expected (empty is not an error)
+ sign 0 by program logic
+ paren optional
+ reg optional
+ ndx optional
+
+ out: mode 10 + @ + len
+ reg optional
+ len ' ' or 'b' or 'w' or 'l'
+ exp maybe empty
+ ndx optional warn if same as reg. */
+ if (!err || !*err)
+ {
+ err = " "; /* win (always) */
+ mode = 10 + (at ? 1 : 0);
+ switch (len)
+ {
+ case 'l':
+ mode += 2;
+ case 'w':
+ mode += 2;
+ case ' ': /* Assumed B^ until our caller changes it. */
+ case 'b':
+ break;
+ }
+ }
+
+ /* here with completely specified mode
+ len
+ reg
+ expression p,q
+ ndx. */
+
+ if (*err == ' ')
+ err = 0; /* " " is no longer an error. */
+
+ vopP->vop_mode = mode;
+ vopP->vop_reg = reg;
+ vopP->vop_short = len;
+ vopP->vop_expr_begin = p;
+ vopP->vop_expr_end = q;
+ vopP->vop_ndx = ndx;
+ vopP->vop_error = err;
+ vopP->vop_warn = wrn;
+}
+
+/* This converts a string into a vax instruction.
+ The string must be a bare single instruction in dec-vax (with BSD4 frobs)
+ format.
+ It provides some error messages: at most one fatal error message (which
+ stops the scan) and at most one warning message for each operand.
+ The vax instruction is returned in exploded form, since we have no
+ knowledge of how you parse (or evaluate) your expressions.
+ We do however strip off and decode addressing modes and operation
+ mnemonic.
+
+ The exploded instruction is returned to a struct vit of your choice.
+ #include "vax-inst.h" to know what a struct vit is.
+
+ This function's value is a string. If it is not "" then an internal
+ logic error was found: read this code to assign meaning to the string.
+ No argument string should generate such an error string:
+ it means a bug in our code, not in the user's text.
+
+ You MUST have called vip_begin() once before using this function. */
+
+static void
+vip (struct vit *vitP, /* We build an exploded instruction here. */
+ char *instring) /* Text of a vax instruction: we modify. */
+{
+ /* How to bit-encode this opcode. */
+ struct vot_wot *vwP;
+ /* 1/skip whitespace.2/scan vot_how */
+ char *p;
+ char *q;
+ /* counts number of operands seen */
+ unsigned char count;
+ /* scan operands in struct vit */
+ struct vop *operandp;
+ /* error over all operands */
+ const char *alloperr;
+ /* Remember char, (we clobber it with '\0' temporarily). */
+ char c;
+ /* Op-code of this instruction. */
+ vax_opcodeT oc;
+
+ if (*instring == ' ')
+ ++instring;
+
+ /* MUST end in end-of-string or exactly 1 space. */
+ for (p = instring; *p && *p != ' '; p++)
+ ;
+
+ /* Scanned up to end of operation-code. */
+ /* Operation-code is ended with whitespace. */
+ if (p - instring == 0)
+ {
+ vitP->vit_error = _("No operator");
+ count = 0;
+ memset (vitP->vit_opcode, '\0', sizeof (vitP->vit_opcode));
+ }
+ else
+ {
+ c = *p;
+ *p = '\0';
+ /* Here with instring pointing to what better be an op-name, and p
+ pointing to character just past that.
+ We trust instring points to an op-name, with no whitespace. */
+ vwP = (struct vot_wot *) hash_find (op_hash, instring);
+ /* Restore char after op-code. */
+ *p = c;
+ if (vwP == 0)
+ {
+ vitP->vit_error = _("Unknown operator");
+ count = 0;
+ memset (vitP->vit_opcode, '\0', sizeof (vitP->vit_opcode));
+ }
+ else
+ {
+ /* We found a match! So let's pick up as many operands as the
+ instruction wants, and even gripe if there are too many.
+ We expect comma to separate each operand.
+ We let instring track the text, while p tracks a part of the
+ struct vot. */
+ const char *howp;
+ /* The lines below know about 2-byte opcodes starting FD,FE or FF.
+ They also understand synthetic opcodes. Note:
+ we return 32 bits of opcode, including bucky bits, BUT
+ an opcode length is either 8 or 16 bits for vit_opcode_nbytes. */
+ oc = vwP->vot_code; /* The op-code. */
+ vitP->vit_opcode_nbytes = (oc & 0xFF) >= 0xFD ? 2 : 1;
+ md_number_to_chars (vitP->vit_opcode, oc, 4);
+ count = 0; /* No operands seen yet. */
+ instring = p; /* Point just past operation code. */
+ alloperr = "";
+ for (howp = vwP->vot_how, operandp = vitP->vit_operand;
+ !(alloperr && *alloperr) && *howp;
+ operandp++, howp += 2)
+ {
+ /* Here to parse one operand. Leave instring pointing just
+ past any one ',' that marks the end of this operand. */
+ if (!howp[1])
+ as_fatal (_("odd number of bytes in operand description"));
+ else if (*instring)
+ {
+ for (q = instring; (c = *q) && c != ','; q++)
+ ;
+ /* Q points to ',' or '\0' that ends argument. C is that
+ character. */
+ *q = 0;
+ operandp->vop_width = howp[1];
+ operandp->vop_nbytes = vax_operand_width_size[(unsigned) howp[1]];
+ operandp->vop_access = howp[0];
+ vip_op (instring, operandp);
+ *q = c; /* Restore input text. */
+ if (operandp->vop_error)
+ alloperr = _("Bad operand");
+ instring = q + (c ? 1 : 0); /* Next operand (if any). */
+ count++; /* Won another argument, may have an operr. */
+ }
+ else
+ alloperr = _("Not enough operands");
+ }
+ if (!*alloperr)
+ {
+ if (*instring == ' ')
+ instring++;
+ if (*instring)
+ alloperr = _("Too many operands");
+ }
+ vitP->vit_error = alloperr;
+ }
+ }
+ vitP->vit_operands = count;
+}
+
+#ifdef test
+
+/* Test program for above. */
+
+struct vit myvit; /* Build an exploded vax instruction here. */
+char answer[100]; /* Human types a line of vax assembler here. */
+char *mybug; /* "" or an internal logic diagnostic. */
+int mycount; /* Number of operands. */
+struct vop *myvop; /* Scan operands from myvit. */
+int mysynth; /* 1 means want synthetic opcodes. */
+char my_immediate[200];
+char my_indirect[200];
+char my_displen[200];
+
+int
+main (void)
+{
+ char *p;
+
+ printf ("0 means no synthetic instructions. ");
+ printf ("Value for vip_begin? ");
+ gets (answer);
+ sscanf (answer, "%d", &mysynth);
+ printf ("Synthetic opcodes %s be included.\n", mysynth ? "will" : "will not");
+ printf ("enter immediate symbols eg enter # ");
+ gets (my_immediate);
+ printf ("enter indirect symbols eg enter @ ");
+ gets (my_indirect);
+ printf ("enter displen symbols eg enter ^ ");
+ gets (my_displen);
+
+ if (p = vip_begin (mysynth, my_immediate, my_indirect, my_displen))
+ error ("vip_begin=%s", p);
+
+ printf ("An empty input line will quit you from the vax instruction parser\n");
+ for (;;)
+ {
+ printf ("vax instruction: ");
+ fflush (stdout);
+ gets (answer);
+ if (!*answer)
+ break; /* Out of for each input text loop. */
+
+ vip (& myvit, answer);
+ if (*myvit.vit_error)
+ printf ("ERR:\"%s\"\n", myvit.vit_error);
+
+ printf ("opcode=");
+ for (mycount = myvit.vit_opcode_nbytes, p = myvit.vit_opcode;
+ mycount;
+ mycount--, p++)
+ printf ("%02x ", *p & 0xFF);
+
+ printf (" operand count=%d.\n", mycount = myvit.vit_operands);
+ for (myvop = myvit.vit_operand; mycount; mycount--, myvop++)
+ {
+ printf ("mode=%xx reg=%xx ndx=%xx len='%c'=%c%c%d. expr=\"",
+ myvop->vop_mode, myvop->vop_reg, myvop->vop_ndx,
+ myvop->vop_short, myvop->vop_access, myvop->vop_width,
+ myvop->vop_nbytes);
+ for (p = myvop->vop_expr_begin; p <= myvop->vop_expr_end; p++)
+ putchar (*p);
+
+ printf ("\"\n");
+ if (myvop->vop_error)
+ printf (" err:\"%s\"\n", myvop->vop_error);
+
+ if (myvop->vop_warn)
+ printf (" wrn:\"%s\"\n", myvop->vop_warn);
+ }
+ }
+ vip_end ();
+ exit (EXIT_SUCCESS);
+}
+
+#endif
+
+#ifdef TEST /* #Define to use this testbed. */
+
+/* Follows a test program for this function.
+ We declare arrays non-local in case some of our tiny-minded machines
+ default to small stacks. Also, helps with some debuggers. */
+
+char answer[100]; /* Human types into here. */
+char *p; /* */
+char *myerr;
+char *mywrn;
+char *mybug;
+char myaccess;
+char mywidth;
+char mymode;
+char myreg;
+char mylen;
+char *myleft;
+char *myright;
+char myndx;
+int my_operand_length;
+char my_immediate[200];
+char my_indirect[200];
+char my_displen[200];
+
+int
+main (void)
+{
+ printf ("enter immediate symbols eg enter # ");
+ gets (my_immediate);
+ printf ("enter indirect symbols eg enter @ ");
+ gets (my_indirect);
+ printf ("enter displen symbols eg enter ^ ");
+ gets (my_displen);
+ vip_op_defaults (my_immediate, my_indirect, my_displen);
+
+ for (;;)
+ {
+ printf ("access,width (eg 'ab' or 'wh') [empty line to quit] : ");
+ fflush (stdout);
+ gets (answer);
+ if (!answer[0])
+ exit (EXIT_SUCCESS);
+ myaccess = answer[0];
+ mywidth = answer[1];
+ switch (mywidth)
+ {
+ case 'b':
+ my_operand_length = 1;
+ break;
+ case 'd':
+ my_operand_length = 8;
+ break;
+ case 'f':
+ my_operand_length = 4;
+ break;
+ case 'g':
+ my_operand_length = 16;
+ break;
+ case 'h':
+ my_operand_length = 32;
+ break;
+ case 'l':
+ my_operand_length = 4;
+ break;
+ case 'o':
+ my_operand_length = 16;
+ break;
+ case 'q':
+ my_operand_length = 8;
+ break;
+ case 'w':
+ my_operand_length = 2;
+ break;
+ case '!':
+ case '?':
+ case '-':
+ my_operand_length = 0;
+ break;
+
+ default:
+ my_operand_length = 2;
+ printf ("I dn't understand access width %c\n", mywidth);
+ break;
+ }
+ printf ("VAX assembler instruction operand: ");
+ fflush (stdout);
+ gets (answer);
+ mybug = vip_op (answer, myaccess, mywidth, my_operand_length,
+ &mymode, &myreg, &mylen, &myleft, &myright, &myndx,
+ &myerr, &mywrn);
+ if (*myerr)
+ {
+ printf ("error: \"%s\"\n", myerr);
+ if (*mybug)
+ printf (" bug: \"%s\"\n", mybug);
+ }
+ else
+ {
+ if (*mywrn)
+ printf ("warning: \"%s\"\n", mywrn);
+ mumble ("mode", mymode);
+ mumble ("register", myreg);
+ mumble ("index", myndx);
+ printf ("width:'%c' ", mylen);
+ printf ("expression: \"");
+ while (myleft <= myright)
+ putchar (*myleft++);
+ printf ("\"\n");
+ }
+ }
+}
+
+void
+mumble (char *text, int value)
+{
+ printf ("%s:", text);
+ if (value >= 0)
+ printf ("%xx", value);
+ else
+ printf ("ABSENT");
+ printf (" ");
+}
+
+#endif
+
+int md_short_jump_size = 3;
+int md_long_jump_size = 6;
+
+void
+md_create_short_jump (char *ptr,
+ addressT from_addr,
+ addressT to_addr ATTRIBUTE_UNUSED,
+ fragS *frag ATTRIBUTE_UNUSED,
+ symbolS *to_symbol ATTRIBUTE_UNUSED)
+{
+ valueT offset;
+
+ /* This former calculation was off by two:
+ offset = to_addr - (from_addr + 1);
+ We need to account for the one byte instruction and also its
+ two byte operand. */
+ offset = to_addr - (from_addr + 1 + 2);
+ *ptr++ = VAX_BRW; /* Branch with word (16 bit) offset. */
+ md_number_to_chars (ptr, offset, 2);
+}
+
+void
+md_create_long_jump (char *ptr,
+ addressT from_addr ATTRIBUTE_UNUSED,
+ addressT to_addr,
+ fragS *frag,
+ symbolS *to_symbol)
+{
+ valueT offset;
+
+ offset = to_addr - S_GET_VALUE (to_symbol);
+ *ptr++ = VAX_JMP; /* Arbitrary jump. */
+ *ptr++ = VAX_ABSOLUTE_MODE;
+ md_number_to_chars (ptr, offset, 4);
+ fix_new (frag, ptr - frag->fr_literal, 4, to_symbol, (long) 0, 0, NO_RELOC);
+}
+
+#ifdef OBJ_VMS
+const char *md_shortopts = "d:STt:V+1h:Hv::";
+#elif defined(OBJ_ELF)
+const char *md_shortopts = "d:STt:VkKQ:";
+#else
+const char *md_shortopts = "d:STt:V";
+#endif
+struct option md_longopts[] =
+{
+#ifdef OBJ_ELF
+#define OPTION_PIC (OPTION_MD_BASE)
+ { "pic", no_argument, NULL, OPTION_PIC },
+#endif
+ { NULL, no_argument, NULL, 0 }
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'S':
+ as_warn (_("SYMBOL TABLE not implemented"));
+ break;
+
+ case 'T':
+ as_warn (_("TOKEN TRACE not implemented"));
+ break;
+
+ case 'd':
+ as_warn (_("Displacement length %s ignored!"), arg);
+ break;
+
+ case 't':
+ as_warn (_("I don't need or use temp. file \"%s\"."), arg);
+ break;
+
+ case 'V':
+ as_warn (_("I don't use an interpass file! -V ignored"));
+ break;
+
+#ifdef OBJ_VMS
+ case '+': /* For g++. Hash any name > 31 chars long. */
+ flag_hash_long_names = 1;
+ break;
+
+ case '1': /* For backward compatibility. */
+ flag_one = 1;
+ break;
+
+ case 'H': /* Show new symbol after hash truncation. */
+ flag_show_after_trunc = 1;
+ break;
+
+ case 'h': /* No hashing of mixed-case names. */
+ {
+ extern char vms_name_mapping;
+ vms_name_mapping = atoi (arg);
+ flag_no_hash_mixed_case = 1;
+ }
+ break;
+
+ case 'v':
+ {
+ extern char *compiler_version_string;
+
+ if (!arg || !*arg || access (arg, 0) == 0)
+ return 0; /* Have caller show the assembler version. */
+ compiler_version_string = arg;
+ }
+ break;
+#endif
+
+#ifdef OBJ_ELF
+ case OPTION_PIC:
+ case 'k':
+ flag_want_pic = 1;
+ break; /* -pic, Position Independent Code. */
+
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment
+ section should be emitted or not. FIXME: Not implemented. */
+ case 'Q':
+ break;
+#endif
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+VAX options:\n\
+-d LENGTH ignored\n\
+-J ignored\n\
+-S ignored\n\
+-t FILE ignored\n\
+-T ignored\n\
+-V ignored\n"));
+#ifdef OBJ_VMS
+ fprintf (stream, _("\
+VMS options:\n\
+-+ hash encode names longer than 31 characters\n\
+-1 `const' handling compatible with gcc 1.x\n\
+-H show new symbol after hash truncation\n\
+-h NUM don't hash mixed-case names, and adjust case:\n\
+ 0 = upper, 2 = lower, 3 = preserve case\n\
+-v\"VERSION\" code being assembled was produced by compiler \"VERSION\"\n"));
+#endif
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+/* Round up a section size to the appropriate boundary. */
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ /* Byte alignment is fine */
+ return size;
+}
+
+/* Exactly what point is a PC-relative offset relative TO?
+ On the vax, they're relative to the address of the offset, plus
+ its size. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+ bfd_reloc_code_real_type code;
+
+ if (fixp->fx_tcbit)
+ abort ();
+
+ if (fixp->fx_r_type != NO_RELOC)
+ {
+ code = fixp->fx_r_type;
+
+ if (fixp->fx_pcrel)
+ {
+ switch (code)
+ {
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_16_PCREL:
+ case BFD_RELOC_32_PCREL:
+#ifdef OBJ_ELF
+ case BFD_RELOC_8_GOT_PCREL:
+ case BFD_RELOC_16_GOT_PCREL:
+ case BFD_RELOC_32_GOT_PCREL:
+ case BFD_RELOC_8_PLT_PCREL:
+ case BFD_RELOC_16_PLT_PCREL:
+ case BFD_RELOC_32_PLT_PCREL:
+#endif
+ break;
+ default:
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot make %s relocation PC relative"),
+ bfd_get_reloc_code_name (code));
+ }
+ }
+ }
+ else
+ {
+#define F(SZ,PCREL) (((SZ) << 1) + (PCREL))
+ switch (F (fixp->fx_size, fixp->fx_pcrel))
+ {
+#define MAP(SZ,PCREL,TYPE) case F(SZ,PCREL): code = (TYPE); break
+ MAP (1, 0, BFD_RELOC_8);
+ MAP (2, 0, BFD_RELOC_16);
+ MAP (4, 0, BFD_RELOC_32);
+ MAP (1, 1, BFD_RELOC_8_PCREL);
+ MAP (2, 1, BFD_RELOC_16_PCREL);
+ MAP (4, 1, BFD_RELOC_32_PCREL);
+ default:
+ abort ();
+ }
+ }
+#undef F
+#undef MAP
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+#ifndef OBJ_ELF
+ if (fixp->fx_pcrel)
+ reloc->addend = fixp->fx_addnumber;
+ else
+ reloc->addend = 0;
+#else
+ reloc->addend = fixp->fx_offset;
+#endif
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
+ gas_assert (reloc->howto != 0);
+
+ return reloc;
+}
+
+/* vax:md_assemble() emit frags for 1 instruction given in textual form. */
+void
+md_assemble (char *instruction_string)
+{
+ /* Non-zero if operand expression's segment is not known yet. */
+ int is_undefined;
+ /* Non-zero if operand expression's segment is absolute. */
+ int is_absolute;
+ int length_code;
+ char *p;
+ /* An operand. Scans all operands. */
+ struct vop *operandP;
+ char *save_input_line_pointer;
+ /* What used to live after an expression. */
+ char c_save;
+ /* 1: instruction_string bad for all passes. */
+ int goofed;
+ /* Points to slot just after last operand. */
+ struct vop *end_operandP;
+ /* Points to expression values for this operand. */
+ expressionS *expP;
+ segT *segP;
+
+ /* These refer to an instruction operand expression. */
+ /* Target segment of the address. */
+ segT to_seg;
+ valueT this_add_number;
+ /* Positive (minuend) symbol. */
+ symbolS *this_add_symbol;
+ /* As a number. */
+ long opcode_as_number;
+ /* Least significant byte 1st. */
+ char *opcode_as_chars;
+ /* As an array of characters. */
+ /* Least significant byte 1st */
+ char *opcode_low_byteP;
+ /* length (bytes) meant by vop_short. */
+ int length;
+ /* 0, or 1 if '@' is in addressing mode. */
+ int at;
+ /* From vop_nbytes: vax_operand_width (in bytes) */
+ int nbytes;
+ FLONUM_TYPE *floatP;
+ LITTLENUM_TYPE literal_float[8];
+ /* Big enough for any floating point literal. */
+
+ vip (&v, instruction_string);
+
+ /* Now we try to find as many as_warn()s as we can. If we do any as_warn()s
+ then goofed=1. Notice that we don't make any frags yet.
+ Should goofed be 1, then this instruction will wedge in any pass,
+ and we can safely flush it, without causing interpass symbol phase
+ errors. That is, without changing label values in different passes. */
+ if ((goofed = (*v.vit_error)) != 0)
+ {
+ as_fatal (_("Ignoring statement due to \"%s\""), v.vit_error);
+ }
+ /* We need to use expression() and friends, which require us to diddle
+ input_line_pointer. So we save it and restore it later. */
+ save_input_line_pointer = input_line_pointer;
+ for (operandP = v.vit_operand,
+ expP = exp_of_operand,
+ segP = seg_of_operand,
+ floatP = float_operand,
+ end_operandP = v.vit_operand + v.vit_operands;
+
+ operandP < end_operandP;
+
+ operandP++, expP++, segP++, floatP++)
+ {
+ if (operandP->vop_error)
+ {
+ as_fatal (_("Aborting because statement has \"%s\""), operandP->vop_error);
+ goofed = 1;
+ }
+ else
+ {
+ /* Statement has no syntax goofs: let's sniff the expression. */
+ int can_be_short = 0; /* 1 if a bignum can be reduced to a short literal. */
+
+ input_line_pointer = operandP->vop_expr_begin;
+ c_save = operandP->vop_expr_end[1];
+ operandP->vop_expr_end[1] = '\0';
+ /* If to_seg == SEG_PASS1, expression() will have set need_pass_2 = 1. */
+ *segP = expression (expP);
+ switch (expP->X_op)
+ {
+ case O_absent:
+ /* for BSD4.2 compatibility, missing expression is absolute 0 */
+ expP->X_op = O_constant;
+ expP->X_add_number = 0;
+ /* For SEG_ABSOLUTE, we shouldn't need to set X_op_symbol,
+ X_add_symbol to any particular value. But, we will program
+ defensively. Since this situation occurs rarely so it costs
+ us little to do, and stops Dean worrying about the origin of
+ random bits in expressionS's. */
+ expP->X_add_symbol = NULL;
+ expP->X_op_symbol = NULL;
+ break;
+
+ case O_symbol:
+ case O_constant:
+ break;
+
+ default:
+ /* Major bug. We can't handle the case of a
+ SEG_OP expression in a VIT_OPCODE_SYNTHETIC
+ variable-length instruction.
+ We don't have a frag type that is smart enough to
+ relax a SEG_OP, and so we just force all
+ SEG_OPs to behave like SEG_PASS1s.
+ Clearly, if there is a demand we can invent a new or
+ modified frag type and then coding up a frag for this
+ case will be easy. SEG_OP was invented for the
+ .words after a CASE opcode, and was never intended for
+ instruction operands. */
+ need_pass_2 = 1;
+ as_fatal (_("Can't relocate expression"));
+ break;
+
+ case O_big:
+ /* Preserve the bits. */
+ if (expP->X_add_number > 0)
+ {
+ bignum_copy (generic_bignum, expP->X_add_number,
+ floatP->low, SIZE_OF_LARGE_NUMBER);
+ }
+ else
+ {
+ know (expP->X_add_number < 0);
+ flonum_copy (&generic_floating_point_number,
+ floatP);
+ if (strchr ("s i", operandP->vop_short))
+ {
+ /* Could possibly become S^# */
+ flonum_gen2vax (-expP->X_add_number, floatP, literal_float);
+ switch (-expP->X_add_number)
+ {
+ case 'f':
+ can_be_short =
+ (literal_float[0] & 0xFC0F) == 0x4000
+ && literal_float[1] == 0;
+ break;
+
+ case 'd':
+ can_be_short =
+ (literal_float[0] & 0xFC0F) == 0x4000
+ && literal_float[1] == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0;
+ break;
+
+ case 'g':
+ can_be_short =
+ (literal_float[0] & 0xFF81) == 0x4000
+ && literal_float[1] == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0;
+ break;
+
+ case 'h':
+ can_be_short = ((literal_float[0] & 0xFFF8) == 0x4000
+ && (literal_float[1] & 0xE000) == 0
+ && literal_float[2] == 0
+ && literal_float[3] == 0
+ && literal_float[4] == 0
+ && literal_float[5] == 0
+ && literal_float[6] == 0
+ && literal_float[7] == 0);
+ break;
+
+ default:
+ BAD_CASE (-expP->X_add_number);
+ break;
+ }
+ }
+ }
+
+ if (operandP->vop_short == 's'
+ || operandP->vop_short == 'i'
+ || (operandP->vop_short == ' '
+ && operandP->vop_reg == 0xF
+ && (operandP->vop_mode & 0xE) == 0x8))
+ {
+ /* Saw a '#'. */
+ if (operandP->vop_short == ' ')
+ {
+ /* We must chose S^ or I^. */
+ if (expP->X_add_number > 0)
+ {
+ /* Bignum: Short literal impossible. */
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ }
+ else
+ {
+ /* Flonum: Try to do it. */
+ if (can_be_short)
+ {
+ operandP->vop_short = 's';
+ operandP->vop_mode = 0;
+ operandP->vop_ndx = -1;
+ operandP->vop_reg = -1;
+ expP->X_op = O_constant;
+ }
+ else
+ {
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC */
+ }
+ } /* bignum or flonum ? */
+ } /* if #, but no S^ or I^ seen. */
+ /* No more ' ' case: either 's' or 'i'. */
+ if (operandP->vop_short == 's')
+ {
+ /* Wants to be a short literal. */
+ if (expP->X_add_number > 0)
+ {
+ as_warn (_("Bignum not permitted in short literal. Immediate mode assumed."));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ }
+ else
+ {
+ if (!can_be_short)
+ {
+ as_warn (_("Can't do flonum short literal: immediate mode used."));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF; /* VAX PC. */
+ }
+ else
+ {
+ /* Encode short literal now. */
+ int temp = 0;
+
+ switch (-expP->X_add_number)
+ {
+ case 'f':
+ case 'd':
+ temp = literal_float[0] >> 4;
+ break;
+
+ case 'g':
+ temp = literal_float[0] >> 1;
+ break;
+
+ case 'h':
+ temp = ((literal_float[0] << 3) & 070)
+ | ((literal_float[1] >> 13) & 07);
+ break;
+
+ default:
+ BAD_CASE (-expP->X_add_number);
+ break;
+ }
+
+ floatP->low[0] = temp & 077;
+ floatP->low[1] = 0;
+ }
+ }
+ }
+ else
+ {
+ /* I^# seen: set it up if float. */
+ if (expP->X_add_number < 0)
+ {
+ memcpy (floatP->low, literal_float, sizeof (literal_float));
+ }
+ } /* if S^# seen. */
+ }
+ else
+ {
+ as_warn (_("A bignum/flonum may not be a displacement: 0x%lx used"),
+ (expP->X_add_number = 0x80000000L));
+ /* Chosen so luser gets the most offset bits to patch later. */
+ }
+ expP->X_add_number = floatP->low[0]
+ | ((LITTLENUM_MASK & (floatP->low[1])) << LITTLENUM_NUMBER_OF_BITS);
+
+ /* For the O_big case we have:
+ If vop_short == 's' then a short floating literal is in the
+ lowest 6 bits of floatP -> low [0], which is
+ big_operand_bits [---] [0].
+ If vop_short == 'i' then the appropriate number of elements
+ of big_operand_bits [---] [...] are set up with the correct
+ bits.
+ Also, just in case width is byte word or long, we copy the lowest
+ 32 bits of the number to X_add_number. */
+ break;
+ }
+ if (input_line_pointer != operandP->vop_expr_end + 1)
+ {
+ as_fatal ("Junk at end of expression \"%s\"", input_line_pointer);
+ goofed = 1;
+ }
+ operandP->vop_expr_end[1] = c_save;
+ }
+ }
+
+ input_line_pointer = save_input_line_pointer;
+
+ if (need_pass_2 || goofed)
+ return;
+
+ dwarf2_emit_insn (0);
+ /* Emit op-code. */
+ /* Remember where it is, in case we want to modify the op-code later. */
+ opcode_low_byteP = frag_more (v.vit_opcode_nbytes);
+ memcpy (opcode_low_byteP, v.vit_opcode, v.vit_opcode_nbytes);
+ opcode_as_chars = v.vit_opcode;
+ opcode_as_number = md_chars_to_number ((unsigned char *) opcode_as_chars, 4);
+ for (operandP = v.vit_operand,
+ expP = exp_of_operand,
+ segP = seg_of_operand,
+ floatP = float_operand,
+ end_operandP = v.vit_operand + v.vit_operands;
+
+ operandP < end_operandP;
+
+ operandP++,
+ floatP++,
+ segP++,
+ expP++)
+ {
+ if (operandP->vop_ndx >= 0)
+ {
+ /* Indexed addressing byte. */
+ /* Legality of indexed mode already checked: it is OK. */
+ FRAG_APPEND_1_CHAR (0x40 + operandP->vop_ndx);
+ } /* if(vop_ndx>=0) */
+
+ /* Here to make main operand frag(s). */
+ this_add_number = expP->X_add_number;
+ this_add_symbol = expP->X_add_symbol;
+ to_seg = *segP;
+ is_undefined = (to_seg == undefined_section);
+ is_absolute = (to_seg == absolute_section);
+ at = operandP->vop_mode & 1;
+ length = (operandP->vop_short == 'b'
+ ? 1 : (operandP->vop_short == 'w'
+ ? 2 : (operandP->vop_short == 'l'
+ ? 4 : 0)));
+ nbytes = operandP->vop_nbytes;
+ if (operandP->vop_access == 'b')
+ {
+ if (to_seg == now_seg || is_undefined)
+ {
+ /* If is_undefined, then it might BECOME now_seg. */
+ if (nbytes)
+ {
+ p = frag_more (nbytes);
+ fix_new (frag_now, p - frag_now->fr_literal, nbytes,
+ this_add_symbol, this_add_number, 1, NO_RELOC);
+ }
+ else
+ {
+ /* to_seg==now_seg || to_seg == SEG_UNKNOWN */
+ /* nbytes==0 */
+ length_code = is_undefined ? STATE_UNDF : STATE_BYTE;
+ if (opcode_as_number & VIT_OPCODE_SPECIAL)
+ {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP)
+ {
+ /* br or jsb */
+ frag_var (rs_machine_dependent, 5, 1,
+ ENCODE_RELAX (STATE_ALWAYS_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ else
+ {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP)
+ {
+ length_code = STATE_WORD;
+ /* JF: There is no state_byte for this one! */
+ frag_var (rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_COMPLEX_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ frag_var (rs_machine_dependent, 9, 1,
+ ENCODE_RELAX (STATE_COMPLEX_HOP, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ }
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP);
+ frag_var (rs_machine_dependent, 7, 1,
+ ENCODE_RELAX (STATE_CONDITIONAL_BRANCH, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ }
+ }
+ }
+ else
+ {
+ /* to_seg != now_seg && to_seg != SEG_UNKNOWN */
+ /* --- SEG FLOAT MAY APPEAR HERE --- */
+ if (is_absolute)
+ {
+ if (nbytes)
+ {
+ know (!(opcode_as_number & VIT_OPCODE_SYNTHETIC));
+ p = frag_more (nbytes);
+ /* Conventional relocation. */
+ fix_new (frag_now, p - frag_now->fr_literal, nbytes,
+ section_symbol (absolute_section),
+ this_add_number, 1, NO_RELOC);
+ }
+ else
+ {
+ know (opcode_as_number & VIT_OPCODE_SYNTHETIC);
+ if (opcode_as_number & VIT_OPCODE_SPECIAL)
+ {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP)
+ {
+ /* br or jsb */
+ *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG;
+ know (opcode_as_chars[1] == 0);
+ p = frag_more (5);
+ p[0] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 1, this_add_number, 4);
+ /* Now (eg) JMP @#foo or JSB @#foo. */
+ }
+ else
+ {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP)
+ {
+ p = frag_more (10);
+ p[0] = 2;
+ p[1] = 0;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 6, this_add_number, 4);
+ /* Now (eg) ACBx 1f
+ BRB 2f
+ 1: JMP @#foo
+ 2: */
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ p = frag_more (9);
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 5, this_add_number, 4);
+ /* Now (eg) xOBxxx 1f
+ BRB 2f
+ 1: JMP @#foo
+ 2: */
+ }
+ }
+ }
+ else
+ {
+ /* b<cond> */
+ *opcode_low_byteP ^= 1;
+ /* To reverse the condition in a VAX branch,
+ complement the lowest order bit. */
+ p = frag_more (7);
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 3, this_add_number, 4);
+ /* Now (eg) BLEQ 1f
+ JMP @#foo
+ 1: */
+ }
+ }
+ }
+ else
+ {
+ /* to_seg != now_seg && !is_undefinfed && !is_absolute */
+ if (nbytes > 0)
+ {
+ /* Pc-relative. Conventional relocation. */
+ know (!(opcode_as_number & VIT_OPCODE_SYNTHETIC));
+ p = frag_more (nbytes);
+ fix_new (frag_now, p - frag_now->fr_literal, nbytes,
+ section_symbol (absolute_section),
+ this_add_number, 1, NO_RELOC);
+ }
+ else
+ {
+ know (opcode_as_number & VIT_OPCODE_SYNTHETIC);
+ if (opcode_as_number & VIT_OPCODE_SPECIAL)
+ {
+ if (operandP->vop_width == VAX_WIDTH_UNCONDITIONAL_JUMP)
+ {
+ /* br or jsb */
+ know (opcode_as_chars[1] == 0);
+ *opcode_low_byteP = opcode_as_chars[0] + VAX_WIDEN_LONG;
+ p = frag_more (5);
+ p[0] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now,
+ p + 1 - frag_now->fr_literal, 4,
+ this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ /* Now eg JMP foo or JSB foo. */
+ }
+ else
+ {
+ if (operandP->vop_width == VAX_WIDTH_WORD_JUMP)
+ {
+ p = frag_more (10);
+ p[0] = 0;
+ p[1] = 2;
+ p[2] = VAX_BRB;
+ p[3] = 6;
+ p[4] = VAX_JMP;
+ p[5] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now,
+ p + 6 - frag_now->fr_literal, 4,
+ this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ /* Now (eg) ACBx 1f
+ BRB 2f
+ 1: JMP foo
+ 2: */
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_BYTE_JUMP);
+ p = frag_more (10);
+ p[0] = 2;
+ p[1] = VAX_BRB;
+ p[2] = 6;
+ p[3] = VAX_JMP;
+ p[4] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now,
+ p + 5 - frag_now->fr_literal,
+ 4, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ /* Now (eg) xOBxxx 1f
+ BRB 2f
+ 1: JMP foo
+ 2: */
+ }
+ }
+ }
+ else
+ {
+ know (operandP->vop_width == VAX_WIDTH_CONDITIONAL_JUMP);
+ *opcode_low_byteP ^= 1; /* Reverse branch condition. */
+ p = frag_more (7);
+ p[0] = 6;
+ p[1] = VAX_JMP;
+ p[2] = VAX_PC_RELATIVE_MODE;
+ fix_new (frag_now, p + 3 - frag_now->fr_literal,
+ 4, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* So it is ordinary operand. */
+ know (operandP->vop_access != 'b');
+ /* ' ' target-independent: elsewhere. */
+ know (operandP->vop_access != ' ');
+ know (operandP->vop_access == 'a'
+ || operandP->vop_access == 'm'
+ || operandP->vop_access == 'r'
+ || operandP->vop_access == 'v'
+ || operandP->vop_access == 'w');
+ if (operandP->vop_short == 's')
+ {
+ if (is_absolute)
+ {
+ if (this_add_number >= 64)
+ {
+ as_warn (_("Short literal overflow(%ld.), immediate mode assumed."),
+ (long) this_add_number);
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF;
+ }
+ }
+ else
+ {
+ as_warn (_("Forced short literal to immediate mode. now_seg=%s to_seg=%s"),
+ segment_name (now_seg), segment_name (to_seg));
+ operandP->vop_short = 'i';
+ operandP->vop_mode = 8;
+ operandP->vop_reg = 0xF;
+ }
+ }
+ if (operandP->vop_reg >= 0 && (operandP->vop_mode < 8
+ || (operandP->vop_reg != 0xF && operandP->vop_mode < 10)))
+ {
+ /* One byte operand. */
+ know (operandP->vop_mode > 3);
+ FRAG_APPEND_1_CHAR (operandP->vop_mode << 4 | operandP->vop_reg);
+ /* All 1-bytes except S^# happen here. */
+ }
+ else
+ {
+ /* {@}{q^}foo{(Rn)} or S^#foo */
+ if (operandP->vop_reg == -1 && operandP->vop_short != 's')
+ {
+ /* "{@}{q^}foo" */
+ if (to_seg == now_seg)
+ {
+ if (length == 0)
+ {
+ know (operandP->vop_short == ' ');
+ length_code = STATE_BYTE;
+#ifdef OBJ_ELF
+ if (S_IS_EXTERNAL (this_add_symbol)
+ || S_IS_WEAK (this_add_symbol))
+ length_code = STATE_UNDF;
+#endif
+ p = frag_var (rs_machine_dependent, 10, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ know (operandP->vop_mode == 10 + at);
+ *p = at << 4;
+ /* At is the only context we need to carry
+ to other side of relax() process. Must
+ be in the correct bit position of VAX
+ operand spec. byte. */
+ }
+ else
+ {
+ know (length);
+ know (operandP->vop_short != ' ');
+ p = frag_more (length + 1);
+ p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4);
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ length, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ else
+ {
+ /* to_seg != now_seg */
+ if (this_add_symbol == NULL)
+ {
+ know (is_absolute);
+ /* Do @#foo: simpler relocation than foo-.(pc) anyway. */
+ p = frag_more (5);
+ p[0] = VAX_ABSOLUTE_MODE; /* @#... */
+ md_number_to_chars (p + 1, this_add_number, 4);
+ if (length && length != 4)
+ as_warn (_("Length specification ignored. Address mode 9F used"));
+ }
+ else
+ {
+ /* {@}{q^}other_seg */
+ know ((length == 0 && operandP->vop_short == ' ')
+ || (length > 0 && operandP->vop_short != ' '));
+ if (is_undefined
+#ifdef OBJ_ELF
+ || S_IS_WEAK(this_add_symbol)
+ || S_IS_EXTERNAL(this_add_symbol)
+#endif
+ )
+ {
+ switch (length)
+ {
+ default: length_code = STATE_UNDF; break;
+ case 1: length_code = STATE_BYTE; break;
+ case 2: length_code = STATE_WORD; break;
+ case 4: length_code = STATE_LONG; break;
+ }
+ /* We have a SEG_UNKNOWN symbol. It might
+ turn out to be in the same segment as
+ the instruction, permitting relaxation. */
+ p = frag_var (rs_machine_dependent, 5, 2,
+ ENCODE_RELAX (STATE_PC_RELATIVE, length_code),
+ this_add_symbol, this_add_number,
+ opcode_low_byteP);
+ p[0] = at << 4;
+ }
+ else
+ {
+ if (length == 0)
+ {
+ know (operandP->vop_short == ' ');
+ length = 4; /* Longest possible. */
+ }
+ p = frag_more (length + 1);
+ p[0] = 0xF | ((at + "?\12\14?\16"[length]) << 4);
+ md_number_to_chars (p + 1, this_add_number, length);
+ fix_new (frag_now,
+ p + 1 - frag_now->fr_literal,
+ length, this_add_symbol,
+ this_add_number, 1, NO_RELOC);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* {@}{q^}foo(Rn) or S^# or I^# or # */
+ if (operandP->vop_mode < 0xA)
+ {
+ /* # or S^# or I^# */
+ if (operandP->vop_access == 'v'
+ || operandP->vop_access == 'a')
+ {
+ if (operandP->vop_access == 'v')
+ as_warn (_("Invalid operand: immediate value used as base address."));
+ else
+ as_warn (_("Invalid operand: immediate value used as address."));
+ /* gcc 2.6.3 is known to generate these in at least
+ one case. */
+ }
+ if (length == 0
+ && is_absolute && (expP->X_op != O_big)
+ && operandP->vop_mode == 8 /* No '@'. */
+ && this_add_number < 64)
+ {
+ operandP->vop_short = 's';
+ }
+ if (operandP->vop_short == 's')
+ {
+ FRAG_APPEND_1_CHAR (this_add_number);
+ }
+ else
+ {
+ /* I^#... */
+ know (nbytes);
+ p = frag_more (nbytes + 1);
+ know (operandP->vop_reg == 0xF);
+#ifdef OBJ_ELF
+ if (flag_want_pic && operandP->vop_mode == 8
+ && this_add_symbol != NULL)
+ {
+ as_warn (_("Symbol %s used as immediate operand in PIC mode."),
+ S_GET_NAME (this_add_symbol));
+ }
+#endif
+ p[0] = (operandP->vop_mode << 4) | 0xF;
+ if ((is_absolute) && (expP->X_op != O_big))
+ {
+ /* If nbytes > 4, then we are scrod. We
+ don't know if the high order bytes
+ are to be 0xFF or 0x00. BSD4.2 & RMS
+ say use 0x00. OK --- but this
+ assembler needs ANOTHER rewrite to
+ cope properly with this bug. */
+ md_number_to_chars (p + 1, this_add_number,
+ min (sizeof (valueT),
+ (size_t) nbytes));
+ if ((size_t) nbytes > sizeof (valueT))
+ memset (p + 1 + sizeof (valueT),
+ '\0', nbytes - sizeof (valueT));
+ }
+ else
+ {
+ if (expP->X_op == O_big)
+ {
+ /* Problem here is to get the bytes
+ in the right order. We stored
+ our constant as LITTLENUMs, not
+ bytes. */
+ LITTLENUM_TYPE *lP;
+
+ lP = floatP->low;
+ if (nbytes & 1)
+ {
+ know (nbytes == 1);
+ p[1] = *lP;
+ }
+ else
+ {
+ for (p++; nbytes; nbytes -= 2, p += 2, lP++)
+ md_number_to_chars (p, *lP, 2);
+ }
+ }
+ else
+ {
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ nbytes, this_add_symbol,
+ this_add_number, 0, NO_RELOC);
+ }
+ }
+ }
+ }
+ else
+ {
+ /* {@}{q^}foo(Rn) */
+ know ((length == 0 && operandP->vop_short == ' ')
+ || (length > 0 && operandP->vop_short != ' '));
+ if (length == 0)
+ {
+ if (is_absolute)
+ {
+ long test;
+
+ test = this_add_number;
+
+ if (test < 0)
+ test = ~test;
+
+ length = test & 0xffff8000 ? 4
+ : test & 0xffffff80 ? 2
+ : 1;
+ }
+ else
+ {
+ length = 4;
+ }
+ }
+ p = frag_more (1 + length);
+ know (operandP->vop_reg >= 0);
+ p[0] = operandP->vop_reg
+ | ((at | "?\12\14?\16"[length]) << 4);
+ if (is_absolute)
+ {
+ md_number_to_chars (p + 1, this_add_number, length);
+ }
+ else
+ {
+ fix_new (frag_now, p + 1 - frag_now->fr_literal,
+ length, this_add_symbol,
+ this_add_number, 0, NO_RELOC);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void
+md_begin (void)
+{
+ const char *errtxt;
+ FLONUM_TYPE *fP;
+ int i;
+
+ if ((errtxt = vip_begin (1, "$", "*", "`")) != 0)
+ as_fatal (_("VIP_BEGIN error:%s"), errtxt);
+
+ for (i = 0, fP = float_operand;
+ fP < float_operand + VIT_MAX_OPERANDS;
+ i++, fP++)
+ {
+ fP->low = &big_operand_bits[i][0];
+ fP->high = &big_operand_bits[i][SIZE_OF_LARGE_NUMBER - 1];
+ }
+}
+
+bfd_reloc_code_real_type
+vax_cons (expressionS *exp, int size)
+{
+ char *save;
+ char *vax_cons_special_reloc;
+
+ SKIP_WHITESPACE ();
+ vax_cons_special_reloc = NULL;
+ save = input_line_pointer;
+ if (input_line_pointer[0] == '%')
+ {
+ if (strncmp (input_line_pointer + 1, "pcrel", 5) == 0)
+ {
+ input_line_pointer += 6;
+ vax_cons_special_reloc = "pcrel";
+ }
+ if (vax_cons_special_reloc)
+ {
+ int bad = 0;
+
+ switch (size)
+ {
+ case 1:
+ if (*input_line_pointer != '8')
+ bad = 1;
+ input_line_pointer--;
+ break;
+ case 2:
+ if (input_line_pointer[0] != '1' || input_line_pointer[1] != '6')
+ bad = 1;
+ break;
+ case 4:
+ if (input_line_pointer[0] != '3' || input_line_pointer[1] != '2')
+ bad = 1;
+ break;
+ default:
+ bad = 1;
+ break;
+ }
+
+ if (bad)
+ {
+ as_bad (_("Illegal operands: Only %%r_%s%d allowed in %d-byte data fields"),
+ vax_cons_special_reloc, size * 8, size);
+ }
+ else
+ {
+ input_line_pointer += 2;
+ if (*input_line_pointer != '(')
+ {
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ vax_cons_special_reloc, size * 8);
+ bad = 1;
+ }
+ }
+
+ if (bad)
+ {
+ input_line_pointer = save;
+ vax_cons_special_reloc = NULL;
+ }
+ else
+ {
+ int c;
+ char *end = ++input_line_pointer;
+ int npar = 0;
+
+ while (! is_end_of_line[(c = *end)])
+ {
+ if (c == '(')
+ npar++;
+ else if (c == ')')
+ {
+ if (!npar)
+ break;
+ npar--;
+ }
+ end++;
+ }
+
+ if (c != ')')
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ vax_cons_special_reloc, size * 8);
+ else
+ {
+ *end = '\0';
+ expression (exp);
+ *end = c;
+ if (input_line_pointer != end)
+ {
+ as_bad (_("Illegal operands: %%r_%s%d requires arguments in ()"),
+ vax_cons_special_reloc, size * 8);
+ }
+ else
+ {
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+ c = *input_line_pointer;
+ if (! is_end_of_line[c] && c != ',')
+ as_bad (_("Illegal operands: garbage after %%r_%s%d()"),
+ vax_cons_special_reloc, size * 8);
+ }
+ }
+ }
+ }
+ }
+ if (vax_cons_special_reloc == NULL)
+ expression (exp);
+ else
+ switch (size)
+ {
+ case 1: return BFD_RELOC_8_PCREL;
+ case 2: return BFD_RELOC_16_PCREL;
+ case 4: return BFD_RELOC_32_PCREL;
+ }
+ return NO_RELOC;
+}
+
+/* This is called by emit_expr via TC_CONS_FIX_NEW when creating a
+ reloc for a cons. */
+
+void
+vax_cons_fix_new (fragS *frag, int where, unsigned int nbytes, expressionS *exp,
+ bfd_reloc_code_real_type r)
+{
+ if (r == NO_RELOC)
+ r = (nbytes == 1 ? BFD_RELOC_8
+ : nbytes == 2 ? BFD_RELOC_16
+ : BFD_RELOC_32);
+
+ fix_new_exp (frag, where, (int) nbytes, exp, 0, r);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return vax_md_atof (type, litP, sizeP);
+}
diff --git a/gas/config/tc-vax.h b/gas/config/tc-vax.h
new file mode 100644
index 0000000..cc94bb2
--- /dev/null
+++ b/gas/config/tc-vax.h
@@ -0,0 +1,79 @@
+/* tc-vax.h -- Header file for tc-vax.c.
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_VAX 1
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#ifdef OBJ_AOUT
+#ifdef TE_NetBSD
+#define TARGET_FORMAT "a.out-vax-netbsd"
+#endif
+#ifndef TARGET_FORMAT
+#define TARGET_FORMAT "a.out-vax-bsd"
+#endif
+#endif
+
+#ifdef OBJ_VMS
+#define TARGET_FORMAT "vms-vax"
+#endif
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-vax"
+#endif
+
+#define TARGET_ARCH bfd_arch_vax
+
+#define NO_RELOC BFD_RELOC_NONE
+#define NOP_OPCODE 0x01
+
+#define md_operand(x)
+
+#ifdef OBJ_ELF
+#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) vax_cons (EXP, NBYTES)
+#define TC_CONS_FIX_NEW vax_cons_fix_new
+bfd_reloc_code_real_type vax_cons (expressionS *, int);
+void vax_cons_fix_new (struct frag *, int, unsigned int, struct expressionS *,
+ bfd_reloc_code_real_type);
+#endif
+
+extern const struct relax_type md_relax_table[];
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+
+/* Values passed to md_apply_fix don't include symbol values. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define tc_fix_adjustable(FIX) \
+ ((FIX)->fx_r_type != BFD_RELOC_VTABLE_INHERIT \
+ && (FIX)->fx_r_type != BFD_RELOC_32_PLT_PCREL \
+ && (FIX)->fx_r_type != BFD_RELOC_32_GOT_PCREL \
+ && (FIX)->fx_r_type != BFD_RELOC_VTABLE_ENTRY \
+ && ((FIX)->fx_pcrel \
+ || ((FIX)->fx_subsy != NULL \
+ && (S_GET_SEGMENT ((FIX)->fx_subsy) \
+ == S_GET_SEGMENT ((FIX)->fx_addsy))) \
+ || S_IS_LOCAL ((FIX)->fx_addsy)))
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
diff --git a/gas/config/tc-xc16x.c b/gas/config/tc-xc16x.c
new file mode 100644
index 0000000..6073387
--- /dev/null
+++ b/gas/config/tc-xc16x.c
@@ -0,0 +1,349 @@
+/* tc-xc16x.c -- Assembler for the Infineon XC16X.
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Contributed by KPIT Cummins Infosystems
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/xc16x-desc.h"
+#include "opcodes/xc16x-opc.h"
+#include "cgen.h"
+#include "dwarf2dbg.h"
+
+
+#ifdef OBJ_ELF
+#include "elf/xc16x.h"
+#endif
+
+/* 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];
+}
+xc16x_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 XC16X_SHORTOPTS ""
+const char * md_shortopts = XC16X_SHORTOPTS;
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+static void
+xc16xlmode (int arg ATTRIBUTE_UNUSED)
+{
+ if (stdoutput != NULL)
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_xc16x, bfd_mach_xc16xl))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+xc16xsmode (int arg ATTRIBUTE_UNUSED)
+{
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_xc16x, bfd_mach_xc16xs))
+ as_warn (_("could not set architecture and machine"));
+}
+
+static void
+xc16xmode (int arg ATTRIBUTE_UNUSED)
+{
+ if (!bfd_set_arch_mach (stdoutput, bfd_arch_xc16x, bfd_mach_xc16x))
+ as_warn (_("could not set architecture and machine"));
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 2 },
+ {"xc16xl", xc16xlmode, 0},
+ {"xc16xs", xc16xsmode, 0},
+ {"xc16x", xc16xmode, 0},
+ { NULL, NULL, 0 }
+};
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = xc16x_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_LITTLE,
+ CGEN_CPU_OPEN_END);
+ xc16x_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);
+}
+
+void
+md_assemble (char *str)
+{
+ xc16x_insn insn;
+ char *errmsg;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = xc16x_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", 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);
+}
+
+/* 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)
+{
+ switch (operand->type)
+ {
+ case XC16X_OPERAND_REL:
+ /* ??? Adjust size? */
+ fixP->fx_where += 1;
+ fixP->fx_pcrel = 1;
+ return BFD_RELOC_8_PCREL;
+
+ case XC16X_OPERAND_CADDR:
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ return BFD_RELOC_16;
+
+ case XC16X_OPERAND_UIMM7:
+ /* ??? Adjust size? */
+ fixP->fx_where += 1;
+ fixP->fx_pcrel = 1;
+ return BFD_RELOC_8_PCREL;
+
+ case XC16X_OPERAND_UIMM16:
+ case XC16X_OPERAND_MEMORY:
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ return BFD_RELOC_16;
+
+ case XC16X_OPERAND_UPOF16:
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ return BFD_RELOC_XC16X_POF;
+
+ case XC16X_OPERAND_UPAG16:
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ return BFD_RELOC_XC16X_PAG;
+
+ case XC16X_OPERAND_USEG8:
+ /* ??? This is an 8 bit field, why the 16 bit reloc? */
+ fixP->fx_where += 1;
+ return BFD_RELOC_XC16X_SEG;
+
+ case XC16X_OPERAND_USEG16:
+ case XC16X_OPERAND_USOF16:
+ fixP->fx_size = 2;
+ fixP->fx_where += 2;
+ return BFD_RELOC_XC16X_SOF;
+
+ default : /* Avoid -Wall warning. */
+ break;
+ }
+
+ return BFD_RELOC_NONE;
+}
+
+/* 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);
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _(" XC16X specific command line options:\n"));
+}
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char *arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
+
+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 NULL;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segment_type ATTRIBUTE_UNUSED)
+{
+ printf (_("call to md_estimate_size_before_relax \n"));
+ abort ();
+}
+
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ long temp_val;
+ temp_val=fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+
+ return temp_val;
+}
+
+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
+ || S_IS_EXTERNAL (fixP->fx_addsy)
+ || S_IS_WEAK (fixP->fx_addsy)))
+ {
+ return 0;
+ }
+
+ return md_pcrel_from (fixP);
+}
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *rel;
+ bfd_reloc_code_real_type r_type;
+
+ if (fixp->fx_addsy && fixp->fx_subsy)
+ {
+ if ((S_GET_SEGMENT (fixp->fx_addsy) != S_GET_SEGMENT (fixp->fx_subsy))
+ || S_GET_SEGMENT (fixp->fx_addsy) == undefined_section)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Difference of symbols in different sections is not supported"));
+ return NULL;
+ }
+ }
+
+ rel = xmalloc (sizeof (arelent));
+ rel->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ rel->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ rel->addend = fixp->fx_offset;
+
+ r_type = fixp->fx_r_type;
+
+#define DEBUG 0
+#if DEBUG
+ fprintf (stderr, "%s\n", bfd_get_reloc_code_name (r_type));
+ fflush (stderr);
+#endif
+
+ rel->howto = bfd_reloc_type_lookup (stdoutput, r_type);
+ if (rel->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent relocation type %s"),
+ bfd_get_reloc_code_name (r_type));
+ return NULL;
+ }
+
+ return rel;
+}
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
+{
+ if (!strstr (seg->name,".debug"))
+ {
+ if (*valP < 128)
+ *valP /= 2;
+ if (*valP>268435455)
+ {
+ *valP = *valP * (-1);
+ *valP /= 2;
+ *valP = 256 - (*valP);
+ }
+ }
+
+ gas_cgen_md_apply_fix (fixP, valP, seg);
+ return;
+}
+
+void
+md_convert_frag (bfd *headers ATTRIBUTE_UNUSED,
+ segT seg ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ printf (_("call to md_convert_frag \n"));
+ abort ();
+}
diff --git a/gas/config/tc-xc16x.h b/gas/config/tc-xc16x.h
new file mode 100644
index 0000000..03bfec9
--- /dev/null
+++ b/gas/config/tc-xc16x.h
@@ -0,0 +1,60 @@
+/* This file is tc-xc16x.h
+ Copyright (C) 2006-2014 Free Software Foundation, Inc.
+ Contributed by KPIT Cummins Infosystems
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_XC16X
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+#define TARGET_ARCH bfd_arch_xc16x
+
+#ifdef OBJ_ELF
+#define TARGET_FORMAT "elf32-xc16x"
+#define LOCAL_LABEL_PREFIX '.'
+#define LOCAL_LABEL(NAME) (NAME[0] == '.' && NAME[1] == 'L')
+#define FAKE_LABEL_NAME ".L0\001"
+#endif
+
+struct fix;
+struct internal_reloc;
+
+#define WORKING_DOT_WORD
+
+#define BFD_ARCH bfd_arch_xc16x
+#define TC_COUNT_RELOC(x) 1
+#define IGNORE_NONSTANDARD_ESCAPES
+
+#define TC_RELOC_MANGLE(s,a,b,c) tc_reloc_mangle(a,b,c)
+extern void tc_reloc_mangle (struct fix *, struct internal_reloc *, bfd_vma);
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* Minimum instruction is of 16 bits. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+#define DO_NOT_STRIP 0
+#define LISTING_HEADER "Infineon XC16X GAS "
+#define NEED_FX_R_TYPE 1
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define md_operand(x)
diff --git a/gas/config/tc-xgate.c b/gas/config/tc-xgate.c
new file mode 100644
index 0000000..f244c57
--- /dev/null
+++ b/gas/config/tc-xgate.c
@@ -0,0 +1,1352 @@
+/* tc-xgate.c -- Assembler code for Freescale XGATE
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
+ Contributed by Sean Keys <skeys@ipdatasys.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+#include "opcode/xgate.h"
+#include "dwarf2dbg.h"
+#include "elf/xgate.h"
+
+const char comment_chars[] = ";!";
+const char line_comment_chars[] = "#*";
+const char line_separator_chars[] = "";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "dD";
+
+/* Max opcodes per opcode handle. */
+#define MAX_OPCODES 0x05
+
+#define SIXTEENTH_BIT 0x8000
+#define N_BITS_IN_WORD 16
+#define MAX_NUM_OPERANDS 3
+
+/* #define STATE_CONDITIONAL_BRANCH (1) */
+#define STATE_PC_RELATIVE (2)
+#define REGISTER_P(ptr) (ptr == 'r')
+#define INCREMENT 01
+#define DECREMENT 02
+#define MAXREGISTER 07
+#define MINREGISTER 00
+
+#define OPTION_MMCU 'm'
+
+/* This macro has no side-effects. */
+#define ENCODE_RELAX(what,length) (((what) << 2) + (length))
+
+/* Each unique opcode name has a handle. That handle may
+ contain pointers to opcodes with the same name but
+ different address modes. */
+struct xgate_opcode_handle
+{
+ int number_of_modes;
+ char *name;
+ struct xgate_opcode *opc0[MAX_OPCODES];
+};
+
+/* XGATE's registers all are 16-bit general purpose.
+ They are numbered according to the specifications. */
+typedef enum register_id
+{
+ REG_NONE = -1,
+ REG_R0 = 0,
+ REG_R1 = 1,
+ REG_R2 = 2,
+ REG_R3 = 3,
+ REG_R4 = 4,
+ REG_R5 = 5,
+ REG_R6 = 6,
+ REG_R7 = 7,
+ REG_PC = 8,
+ REG_CCR = 9
+} register_id;
+
+/* Operand Modifiers */
+typedef enum op_modifiers
+{
+ MOD_NONE = -1,
+ MOD_POSTINC = 1,
+ MOD_PREDEC = 2,
+ MOD_CONSTANT = 3,
+ MOD_LOAD_HIGH = 4,
+ MOD_LOAD_LOW = 5
+}op_modifiers;
+
+typedef struct s_operand
+{
+ expressionS exp;
+ register_id reg;
+ op_modifiers mod;
+} s_operand;
+
+
+/* Forward declarations. */
+static inline char *skip_whitespace (char *);
+static void get_default_target (void);
+static char *extract_word (char *, char *, int);
+static struct xgate_opcode *xgate_find_match (struct xgate_opcode_handle *,
+ int, s_operand [], unsigned int);
+static int cmp_opcode (struct xgate_opcode *, struct xgate_opcode *);
+static void xgate_print_table (void);
+static unsigned int xgate_get_operands (char *, s_operand []);
+static register_id reg_name_search (char *);
+static op_modifiers xgate_determine_modifiers(char **);
+static void xgate_scan_operands (struct xgate_opcode *opcode, s_operand []);
+static unsigned int xgate_parse_operand (struct xgate_opcode *, int *, int,
+ char **, s_operand);
+
+static struct hash_control *xgate_hash;
+
+/* Previous opcode. */
+static unsigned int prev = 0;
+
+static unsigned char fixup_required = 0;
+
+/* Used to enable clipping of 16 bit operands into 8 bit constraints. */
+static unsigned char autoHiLo = 0;
+
+static char oper_check;
+static char flag_print_insn_syntax = 0;
+static char flag_print_opcodes = 0;
+
+static int current_architecture;
+static const char *default_cpu;
+
+/* ELF flags to set in the output file header. */
+static int elf_flags = E_XGATE_F64;
+
+/* This table describes how you change sizes for the various types of variable
+ size expressions. This version only supports two kinds. */
+
+/* The fields are:
+ How far Forward this mode will reach.
+ How far Backward this mode will reach.
+ How many bytes this mode will add to the size of the frag.
+ Which mode to go to if the offset won't fit in this one. */
+
+relax_typeS md_relax_table[] =
+{
+ {1, 1, 0, 0}, /* First entries aren't used. */
+ {1, 1, 0, 0}, /* For no good reason except. */
+ {1, 1, 0, 0}, /* that the VAX doesn't either. */
+ {1, 1, 0, 0},
+ /* XGATE 9 and 10 bit pc rel todo complete and test */
+/*{(511), (-512), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)},
+ {(1023), (-1024), 0, ENCODE_RELAX (STATE_PC_RELATIVE, STATE_WORD)}, */
+ {0, 0, 0, 0}
+};
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are: pseudo-op name without dot function to
+ call to execute this pseudo-op Integer arg to pass to the function. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ /* The following pseudo-ops are supported for MRI compatibility. */
+ {0, 0, 0}
+};
+
+const char *md_shortopts = "m:";
+
+struct option md_longopts[] =
+{
+#define OPTION_PRINT_INSN_SYNTAX (OPTION_MD_BASE + 0)
+ { "print-insn-syntax", no_argument, NULL, OPTION_PRINT_INSN_SYNTAX },
+
+#define OPTION_PRINT_OPCODES (OPTION_MD_BASE + 1)
+ { "print-opcodes", no_argument, NULL, OPTION_PRINT_OPCODES },
+
+#define OPTION_GENERATE_EXAMPLE (OPTION_MD_BASE + 2)
+ { "generate-example", no_argument, NULL, OPTION_GENERATE_EXAMPLE },
+
+#define OPTION_MSHORT (OPTION_MD_BASE + 3)
+ { "mshort", no_argument, NULL, OPTION_MSHORT },
+
+#define OPTION_MLONG (OPTION_MD_BASE + 4)
+ { "mlong", no_argument, NULL, OPTION_MLONG },
+
+#define OPTION_MSHORT_DOUBLE (OPTION_MD_BASE + 5)
+ { "mshort-double", no_argument, NULL, OPTION_MSHORT_DOUBLE },
+
+#define OPTION_MLONG_DOUBLE (OPTION_MD_BASE + 6)
+ { "mlong-double", no_argument, NULL, OPTION_MLONG_DOUBLE },
+
+ { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof(md_longopts);
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case OPTION_MMCU:
+ if (strcasecmp (arg, "v1") == 0)
+ current_architecture = XGATE_V1;
+ else if (strcasecmp (arg, "v2") == 0)
+ current_architecture = XGATE_V2;
+ else if (strcasecmp (arg, "v3") == 0)
+ current_architecture = XGATE_V3;
+ else
+ as_bad (_(" architecture variant invalid"));
+ break;
+
+ case OPTION_PRINT_INSN_SYNTAX:
+ flag_print_insn_syntax = 1;
+ break;
+
+ case OPTION_PRINT_OPCODES:
+ flag_print_opcodes = 1;
+ break;
+
+ case OPTION_GENERATE_EXAMPLE:
+ flag_print_opcodes = 2;
+ break;
+
+ case OPTION_MSHORT:
+ elf_flags &= ~E_XGATE_I32;
+ break;
+
+ case OPTION_MLONG:
+ elf_flags |= E_XGATE_I32;
+ break;
+
+ case OPTION_MSHORT_DOUBLE:
+ elf_flags &= ~E_XGATE_F64;
+ break;
+
+ case OPTION_MLONG_DOUBLE:
+ elf_flags |= E_XGATE_F64;
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+const char *
+xgate_arch_format (void)
+{
+ get_default_target ();
+
+ if (current_architecture & cpuxgate)
+ return "elf32-xgate";
+
+ return "error";
+}
+
+static void
+get_default_target (void)
+{
+ const bfd_target *target;
+ bfd abfd;
+
+ if (current_architecture != 0)
+ return;
+
+ default_cpu = "unknown";
+ target = bfd_find_target (0, &abfd);
+
+ if (target && target->name)
+ {
+ if (strcmp (target->name, "elf32-xgate") == 0)
+ {
+ current_architecture = cpuxgate;
+ default_cpu = "XGATE V1";
+ return;
+ }
+
+ as_bad (_("Default target `%s' is not supported."), target->name);
+ }
+}
+
+void
+md_begin (void)
+{
+ struct xgate_opcode *xgate_opcode_ptr = NULL;
+ struct xgate_opcode *xgate_op_table = NULL;
+ struct xgate_opcode_handle *op_handles = 0;
+ char *prev_op_name = 0;
+ int handle_enum = 0;
+ int number_of_op_handles = 0;
+ int i, j = 0;
+
+ /* Create a local copy of our opcode table
+ including an extra line for NULL termination. */
+ xgate_op_table = (struct xgate_opcode *)
+ xmalloc ((xgate_num_opcodes) * sizeof (struct xgate_opcode));
+
+ memset (xgate_op_table, 0,
+ sizeof(struct xgate_opcode) * (xgate_num_opcodes));
+
+ for (xgate_opcode_ptr = (struct xgate_opcode*) xgate_opcodes, i = 0;
+ i < xgate_num_opcodes; i++)
+ xgate_op_table[i] = xgate_opcode_ptr[i];
+
+ qsort (xgate_op_table, xgate_num_opcodes, sizeof(struct xgate_opcode),
+ (int (*)(const void *, const void *)) cmp_opcode);
+
+ /* Calculate number of handles since this will be
+ smaller than the raw number of opcodes in the table. */
+ prev_op_name = "";
+ for (xgate_opcode_ptr = xgate_op_table, i = 0; i < xgate_num_opcodes;
+ xgate_opcode_ptr++, i++)
+ {
+ if (strcmp (prev_op_name, xgate_opcode_ptr->name))
+ number_of_op_handles++;
+ prev_op_name = xgate_opcode_ptr->name;
+ }
+
+ op_handles = (struct xgate_opcode_handle *)
+ xmalloc (sizeof(struct xgate_opcode_handle) * (number_of_op_handles));
+
+ /* Insert unique opcode names into hash table, aliasing duplicates. */
+ xgate_hash = hash_new ();
+
+ prev_op_name = "";
+ for (xgate_opcode_ptr = xgate_op_table, i = 0, j = 0; i < xgate_num_opcodes;
+ i++, xgate_opcode_ptr++)
+ {
+ if (!strcmp (prev_op_name, xgate_opcode_ptr->name))
+ {
+ handle_enum++;
+ op_handles[j].opc0[handle_enum] = xgate_opcode_ptr;
+ }
+ else
+ {
+ handle_enum = 0;
+ if (i)
+ j++;
+ op_handles[j].name = xgate_opcode_ptr->name;
+ op_handles[j].opc0[0] = xgate_opcode_ptr;
+ hash_insert (xgate_hash, (char *) op_handles[j].name,
+ (char *) &(op_handles[j]));
+ }
+ op_handles[j].number_of_modes = handle_enum;
+ prev_op_name = op_handles[j].name;
+ }
+
+ if (flag_print_opcodes)
+ {
+ xgate_print_table ();
+ exit (EXIT_SUCCESS);
+ }
+}
+
+void
+xgate_init_after_args (void)
+{
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ get_default_target ();
+
+ fprintf (stream,
+ _("\
+Freescale XGATE co-processor options:\n\
+ -mshort use 16-bit int ABI (default)\n\
+ -mlong use 32-bit int ABI\n\
+ -mshort-double use 32-bit double ABI\n\
+ -mlong-double use 64-bit double ABI (default)\n\
+ --mxgate specify the processor variant[default %s]\n\
+ --print-insn-syntax print the syntax of instruction in case of error\n\
+ --print-opcodes print the list of instructions with syntax\n\
+ --generate-example generate an example of each instruction"),
+ default_cpu);
+}
+
+enum bfd_architecture
+xgate_arch (void)
+{
+ get_default_target ();
+ return bfd_arch_xgate;
+}
+
+int
+xgate_mach (void)
+{
+ return 0;
+}
+
+static void
+xgate_print_syntax (char *name)
+{
+ int i;
+
+ for (i = 0; i < xgate_num_opcodes; i++)
+ {
+ if (!strcmp (xgate_opcodes[i].name, name))
+ {
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IDR))
+ printf ("\tFormat is %s\tRx, Rx, Rx+|-Rx|Rx\n",
+ xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_INH))
+ printf ("\tFormat is %s\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_TRI))
+ printf ("\tFormat is %s\tRx, Rx, Rx\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_DYA))
+ printf ("\tFormat is %s\tRx, Rx\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM3))
+ printf ("\tFormat is %s\t<3-bit value>\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM4))
+ printf ("\tFormat is %s\t<4 -bit value>\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM8))
+ printf ("\tFormat is %s\tRx, <8-bit value>\n",
+ xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM16))
+ printf ("\tFormat is %s\tRx, <16-bit value>\n",
+ xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_R_C))
+ printf ("\tFormat is %s\tRx, CCR\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_C_R))
+ printf ("\tFormat is %s\tCCR, Rx\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_MON_R_P))
+ printf ("\tFormat is %s\tRx, PC\n", xgate_opcodes[i].name);
+ if (!strcmp (xgate_opcodes[i].constraints, XGATE_OP_IMM16mLDW))
+ printf ("\tFormat is %s\tRx, <16-bit value>\n",
+ xgate_opcodes[i].name);
+ }
+ }
+}
+
+static void
+xgate_print_table (void)
+{
+ int i;
+
+ for (i = 0; i < xgate_num_opcodes; i++)
+ xgate_print_syntax (xgate_opcodes[i].name);
+
+ return;
+}
+
+const char *
+xgate_listing_header (void)
+{
+ if (current_architecture & cpuxgate)
+ return "XGATE GAS ";
+
+ return "ERROR MC9S12X GAS ";
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* GAS will call this function for each section at the end of the assembly,
+ to permit the CPU backend to adjust the alignment of a section. */
+
+valueT
+md_section_align (asection * seg, valueT addr)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ return ((addr + (1 << align) - 1) & (-1 << align));
+}
+
+void
+md_assemble (char *input_line)
+{
+ struct xgate_opcode *opcode = 0;
+ struct xgate_opcode *macro_opcode = 0;
+ struct xgate_opcode_handle *opcode_handle = 0;
+ /* Caller expects it to be returned as it was passed. */
+ char *saved_input_line = input_line;
+ char op_name[9] = { 0 };
+ unsigned int operandCount = 0;
+ char *p = 0;
+
+ s_operand new_operands[MAX_NUM_OPERANDS];
+
+ fixup_required = 0;
+ oper_check = 0; /* set error flags */
+ input_line = extract_word (input_line, op_name, sizeof(op_name));
+
+ /* Check to make sure we are not reading a bogus line. */
+ if (!op_name[0])
+ as_bad (_("opcode missing or not found on input line"));
+
+ if (!(opcode_handle = (struct xgate_opcode_handle *) hash_find (xgate_hash,
+ op_name)))
+ {
+ as_bad (_("opcode %s not found in opcode hash table"), op_name);
+ }
+ else
+ {
+ /* Parse operands so we can find the proper opcode bin. */
+
+ operandCount = xgate_get_operands (input_line, new_operands);
+
+ opcode = xgate_find_match (opcode_handle, opcode_handle->number_of_modes,
+ new_operands, operandCount);
+
+ if (!opcode)
+ {
+ as_bad (_("matching operands to opcode "));
+ xgate_print_syntax (opcode_handle->opc0[0]->name);
+ }
+ else if (opcode->size == 2)
+ {
+ /* Size is one word - assemble that native insn. */
+ xgate_scan_operands (opcode, new_operands);
+ }
+ else
+ {
+ /* Insn is a simplified instruction - expand it out. */
+ autoHiLo = 1;
+ unsigned int i;
+
+ /* skip past our ';' separator. */
+ for (i = strlen (opcode->constraints), p = opcode->constraints; i > 0;
+ i--, p++)
+ {
+ if (*p == ';')
+ {
+ p++;
+ break;
+ }
+ }
+ input_line = skip_whitespace (input_line);
+ char *macro_inline = input_line;
+
+ /* Loop though the macro's opcode list and apply operands to
+ each real opcode. */
+ for (i = 0; *p && i < (opcode->size / 2); i++)
+ {
+ /* Loop though macro operand list. */
+ input_line = macro_inline; /* Rewind. */
+ p = extract_word (p, op_name, 10);
+
+ if (!(opcode_handle = (struct xgate_opcode_handle *)
+ hash_find (xgate_hash, op_name)))
+ {
+ as_bad (_(": processing macro, real opcode handle"
+ " not found in hash"));
+ break;
+ }
+ else
+ {
+ operandCount = xgate_get_operands(input_line, new_operands);
+ macro_opcode = xgate_find_match (opcode_handle,
+ opcode_handle->number_of_modes, new_operands,
+ operandCount);
+ xgate_scan_operands (macro_opcode, new_operands);
+ }
+ }
+ }
+ }
+ autoHiLo = 0;
+ input_line = saved_input_line;
+}
+
+/* Force truly undefined symbols to their maximum size, and generally set up
+ the frag list to be relaxed. */
+
+int
+md_estimate_size_before_relax (fragS *fragp, asection *seg)
+{
+ /* If symbol is undefined or located in a different section,
+ select the largest supported relocation. */
+ relax_substateT subtype;
+ relax_substateT rlx_state[] = { 0, 2 };
+
+ for (subtype = 0; subtype < ARRAY_SIZE (rlx_state); subtype += 2)
+ {
+ if (fragp->fr_subtype == rlx_state[subtype]
+ && (!S_IS_DEFINED (fragp->fr_symbol)
+ || seg != S_GET_SEGMENT (fragp->fr_symbol)))
+ {
+ fragp->fr_subtype = rlx_state[subtype + 1];
+ break;
+ }
+ }
+
+ if (fragp->fr_subtype >= ARRAY_SIZE (md_relax_table))
+ abort ();
+
+ return md_relax_table[fragp->fr_subtype].rlx_length;
+}
+
+
+/* Relocation, relaxation and frag conversions. */
+
+/* PC-relative offsets are relative to the start of the
+ next instruction. That is, the address of the offset, plus its
+ size, since the offset is always the last part of the insn. */
+
+long
+md_pcrel_from (fixS * fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+/* If while processing a fixup, a reloc really needs to be created
+ then it is done here. */
+
+arelent *
+tc_gen_reloc (asection * section ATTRIBUTE_UNUSED, fixS * fixp)
+{
+ arelent * reloc;
+
+ reloc = (arelent *) xmalloc (sizeof(arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof(asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ if (fixp->fx_r_type == 0)
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_16);
+ else
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (reloc->howto == (reloc_howto_type *) NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line, _
+ ("Relocation %d is not supported by object file format."),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ /* Since we use Rel instead of Rela, encode the vtable entry to be
+ used in the relocation's section offset. */
+ if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ reloc->address = fixp->fx_offset;
+ reloc->addend = 0;
+ return reloc;
+}
+
+/* Patch the instruction with the resolved operand. Elf relocation
+ info will also be generated to take care of linker/loader fixups.
+ The XGATE addresses only 16-bit addresses.The BFD_RELOC_32 is necessary
+ for the support of --gstabs. */
+
+void
+md_apply_fix (fixS * fixP, valueT * valP, segT seg ATTRIBUTE_UNUSED)
+{
+ char *where;
+ long value = *valP;
+ int opcode = 0;
+ ldiv_t result;
+
+ /* If the fixup is done mark it done so no further symbol resolution
+ will take place. */
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ /* We don't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("Expression too complex."));
+
+ where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ opcode = bfd_getl16 (where);
+ int mask = 0;
+
+ switch (fixP->fx_r_type)
+ {
+ case R_XGATE_PCREL_9:
+ if (value < -512 || value > 511)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value %ld too large for 9-bit PC-relative branch."),
+ value);
+ result = ldiv (value, 2); /* from bytes to words */
+ value = result.quot;
+ if (result.rem)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _
+ ("Value %ld not aligned by 2 for 9-bit"
+ " PC-relative branch."), value);
+ /* Clip into 8-bit field.
+ FIXME I'm sure there is a more proper place for this. */
+ mask = 0x1FF;
+ value &= mask;
+ number_to_chars_bigendian (where, (opcode | value), 2);
+ break;
+ case R_XGATE_PCREL_10:
+ if (value < -1024 || value > 1023)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value %ld too large for 10-bit PC-relative branch."),
+ value);
+ result = ldiv (value, 2); /* from bytes to words */
+ value = result.quot;
+ if (result.rem)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _
+ ("Value %ld not aligned by 2 for 10-bit"
+ " PC-relative branch."), value);
+ /* Clip into 9-bit field.
+ FIXME I'm sure there is a more proper place for this. */
+ mask = 0x3FF;
+ value &= mask;
+ number_to_chars_bigendian (where, (opcode | value), 2);
+ break;
+ case BFD_RELOC_XGATE_IMM8_HI:
+ if (value < -65537 || value > 65535)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value out of 16-bit range."));
+ value >>= 8;
+ value &= 0x00ff;
+ bfd_putb16 ((bfd_vma) value | opcode, (void *) where);
+ break;
+ case BFD_RELOC_XGATE_24:
+ case BFD_RELOC_XGATE_IMM8_LO:
+ if (value < -65537 || value > 65535)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value out of 16-bit range."));
+ value &= 0x00ff;
+ bfd_putb16 ((bfd_vma) value | opcode, (void *) where);
+ break;
+ case BFD_RELOC_XGATE_IMM3:
+ if (value < 0 || value > 7)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value out of 3-bit range."));
+ value <<= 8; /* make big endian */
+ number_to_chars_bigendian (where, (opcode | value), 2);
+ break;
+ case BFD_RELOC_XGATE_IMM4:
+ if (value < 0 || value > 15)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value out of 4-bit range."));
+ value <<= 4; /* align the operand bits */
+ number_to_chars_bigendian (where, (opcode | value), 2);
+ break;
+ case BFD_RELOC_XGATE_IMM5:
+ if (value < 0 || value > 31)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Value out of 5-bit range."));
+ value <<= 5; /* align the operand bits */
+ number_to_chars_bigendian (where, (opcode | value), 2);
+ break;
+ case BFD_RELOC_8:
+ ((bfd_byte *) where)[0] = (bfd_byte) value;
+ break;
+ case BFD_RELOC_32:
+ bfd_putb32 ((bfd_vma) value, (unsigned char *) where);
+ break;
+ case BFD_RELOC_16:
+ bfd_putb16 ((bfd_vma) value, (unsigned char *) where);
+ break;
+ default:
+ as_fatal (_("Line %d: unknown relocation type: 0x%x."), fixP->fx_line,
+ fixP->fx_r_type);
+ break;
+ }
+}
+
+/* See whether we need to force a relocation into the output file. */
+
+int
+tc_xgate_force_relocation (fixS * fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_XGATE_RL_GROUP)
+ return 1;
+ return generic_force_reloc (fixP);
+}
+
+/* Here we decide which fixups can be adjusted to make them relative
+ to the beginning of the section instead of the symbol. Basically
+ we need to make sure that the linker relaxation is done
+ correctly, so in some cases we force the original symbol to be
+ used. */
+
+int
+tc_xgate_fix_adjustable (fixS * fixP)
+{
+ switch (fixP->fx_r_type)
+ {
+ /* For the linker relaxation to work correctly, these relocs
+ need to be on the symbol itself. */
+ case BFD_RELOC_16:
+ case BFD_RELOC_XGATE_RL_JUMP:
+ case BFD_RELOC_XGATE_RL_GROUP:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ case BFD_RELOC_32:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ asection * sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+ as_bad (("md_convert_frag not implemented yet"));
+ abort ();
+}
+
+/* Set the ELF specific flags. */
+
+void
+xgate_elf_final_processing (void)
+{
+ elf_flags |= EF_XGATE_MACH;
+ elf_elfheader (stdoutput)->e_flags &= ~EF_XGATE_ABI;
+ elf_elfheader (stdoutput)->e_flags |= elf_flags;
+}
+
+static inline char *
+skip_whitespace (char *s)
+{
+ while (*s == ' ' || *s == '\t' || *s == '(' || *s == ')')
+ s++;
+
+ return s;
+}
+
+/* Extract a word (continuous alpha-numeric chars) from the input line. */
+
+static char *
+extract_word (char *from, char *to, int limit)
+{
+ char *op_end;
+ int size = 0;
+
+ /* Drop leading whitespace. */
+ from = skip_whitespace (from);
+ *to = 0;
+ /* Find the op code end. */
+ for (op_end = from; *op_end != 0 && is_part_of_name (*op_end);)
+ {
+ to[size++] = *op_end++;
+ if (size + 1 >= limit)
+ break;
+ }
+ to[size] = 0;
+ return op_end;
+}
+
+static char *
+xgate_new_instruction (int size)
+{
+ char *f = frag_more (size);
+ dwarf2_emit_insn (size);
+ return f;
+}
+
+static unsigned short
+xgate_apply_operand (unsigned short new_mask,
+ unsigned short *availiable_mask_bits,
+ unsigned short mask,
+ unsigned char n_bits)
+{
+ unsigned short n_shifts;
+ unsigned int n_drop_bits;
+
+ /* Shift until you find an available operand bit "1" and record
+ the number of shifts. */
+ for (n_shifts = 0;
+ !(*availiable_mask_bits & SIXTEENTH_BIT) && n_shifts < 16;
+ n_shifts++)
+ *availiable_mask_bits <<= 1;
+
+ /* Shift for the number of bits your operand requires while bits
+ are available. */
+ for (n_drop_bits = n_bits;
+ n_drop_bits && (*availiable_mask_bits & SIXTEENTH_BIT);
+ --n_drop_bits)
+ *availiable_mask_bits <<= 1;
+
+ if (n_drop_bits)
+ as_bad (_(":operand has too many bits"));
+ *availiable_mask_bits >>= n_shifts + n_bits;
+ if ((n_drop_bits == 0) && (*availiable_mask_bits == 0))
+ {
+ oper_check = 1; /* flag operand check as good */
+ }
+ new_mask <<= N_BITS_IN_WORD - (n_shifts + n_bits);
+ mask |= new_mask;
+ return mask;
+}
+
+/* Parse ordinary expression. */
+
+static char *
+xgate_parse_exp (char *s, expressionS * op)
+{
+ input_line_pointer = s;
+ expression(op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ return input_line_pointer;
+}
+
+static int
+cmp_opcode (struct xgate_opcode *op1, struct xgate_opcode *op2)
+{
+ return strcmp (op1->name, op2->name);
+}
+
+static struct xgate_opcode *
+xgate_find_match (struct xgate_opcode_handle *opcode_handle,
+ int numberOfModes, s_operand oprs[], unsigned int operandCount)
+{
+ int i;
+
+ if (numberOfModes == 0)
+ return opcode_handle->opc0[0];
+
+ for (i = 0; i <= numberOfModes; i++)
+ {
+ switch (operandCount)
+ {
+ case 0:
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_INH))
+ return opcode_handle->opc0[i];
+ break;
+ case 1:
+ if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7)
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_MON))
+ return opcode_handle->opc0[i];
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_DYA_MON))
+ return opcode_handle->opc0[i];
+ if (oprs[0].reg == REG_NONE)
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM3))
+ return opcode_handle->opc0[i];
+ break;
+ case 2:
+ if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7)
+ {
+ if (oprs[1].reg >= REG_R0 && oprs[1].reg <= REG_R7)
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_DYA))
+ return opcode_handle->opc0[i];
+ if (oprs[1].reg == REG_CCR)
+ if (!strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_MON_R_C))
+ return opcode_handle->opc0[i];
+ if (oprs[1].reg == REG_PC)
+ if (!strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_MON_R_P))
+ return opcode_handle->opc0[i];
+ if (oprs[1].reg == REG_NONE)
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM16)
+ || !strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM8)
+ || !strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_IMM4)
+ || !strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IMM16mADD)
+ || !strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IMM16mAND)
+ || !strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IMM16mCPC)
+ || !strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IMM16mSUB)
+ || !strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IMM16mLDW))
+ return opcode_handle->opc0[i];
+ }
+ if (oprs[0].reg == REG_CCR)
+ if (!strcmp(opcode_handle->opc0[i]->constraints, XGATE_OP_MON_C_R))
+ return opcode_handle->opc0[i];
+ break;
+ case 3:
+ if (oprs[0].reg >= REG_R0 && oprs[0].reg <= REG_R7)
+ {
+ if (oprs[1].reg >= REG_R0 && oprs[1].reg <= REG_R7)
+ {
+ if (oprs[2].reg >= REG_R0 && oprs[2].reg <= REG_R7)
+ {
+ if (!strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IDR)
+ || !strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_TRI))
+ return opcode_handle->opc0[i];
+ }
+
+ if (oprs[2].reg == REG_NONE)
+ if (!strcmp(opcode_handle->opc0[i]->constraints,
+ XGATE_OP_IDO5))
+ return opcode_handle->opc0[i];
+ }
+ }
+ break;
+ default:
+ as_bad(_("unknown operand count"));
+ break;
+ }
+ }
+ return NULL ;
+}
+
+/* Because we are dealing with two different core that view the system
+ memory with different offsets, we must differentiate what core a
+ symbol belongs to, in order for the linker to cross-link. */
+
+int
+xgate_frob_symbol (symbolS *sym)
+{
+ asymbol *bfdsym;
+ elf_symbol_type *elfsym;
+
+ bfdsym = symbol_get_bfdsym (sym);
+ elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+
+ gas_assert(elfsym);
+
+ /* Mark the symbol as being *from XGATE */
+ elfsym->internal_elf_sym.st_target_internal = 1;
+
+ return 0;
+}
+
+static unsigned int
+xgate_get_operands (char *line, s_operand oprs[])
+{
+ int num_operands;
+
+ /* If there are no operands, then it must be inherent. */
+ if (*line == 0 || *line == '\n' || *line == '\r')
+ return 0;
+
+ for (num_operands = 0; strlen (line) && (num_operands < MAX_NUM_OPERANDS);
+ num_operands++)
+ {
+ line = skip_whitespace (line);
+ if (*line == '#')
+ line++;
+
+ oprs[num_operands].mod = xgate_determine_modifiers (&line);
+
+ if ((oprs[num_operands].reg = reg_name_search (line)) == REG_NONE)
+ line = xgate_parse_exp (line, &oprs[num_operands].exp);
+
+ /* skip to next operand */
+ while (*line != 0)
+ {
+ if (*line == ',')
+ {
+ line++;
+ break;
+ }
+ line++;
+ }
+ }
+ if (num_operands > MAX_NUM_OPERANDS)
+ return 0;
+ return num_operands;
+}
+
+/* reg_name_search() finds the register number given its name.
+ Returns the register number or REG_NONE on failure. */
+static register_id
+reg_name_search (char *name)
+{
+ if (strncasecmp (name, "r0", 2) == 0)
+ return REG_R0;
+ if (strncasecmp (name, "r1", 2) == 0)
+ return REG_R1;
+ if (strncasecmp (name, "r2", 2) == 0)
+ return REG_R2;
+ if (strncasecmp (name, "r3", 2) == 0)
+ return REG_R3;
+ if (strncasecmp (name, "r4", 2) == 0)
+ return REG_R4;
+ if (strncasecmp (name, "r5", 2) == 0)
+ return REG_R5;
+ if (strncasecmp (name, "r6", 2) == 0)
+ return REG_R6;
+ if (strncasecmp (name, "r7", 2) == 0)
+ return REG_R7;
+ if (strncasecmp (name, "pc", 2) == 0)
+ return REG_PC;
+ if (strncasecmp (name, "ccr", 3) == 0)
+ return REG_CCR;
+ return REG_NONE;
+}
+
+/* Parse operand modifiers such as inc/dec/hi/low. */
+
+static op_modifiers
+xgate_determine_modifiers(char **line)
+{
+ char *local_line = line[0];
+
+ if (strncasecmp (local_line, "%hi", 3) == 0)
+ {
+ *line += 3;
+ return MOD_LOAD_HIGH;
+ }
+ if (strncasecmp (local_line, "%lo", 3) == 0)
+ {
+ *line += 3;
+ return MOD_LOAD_LOW;
+ }
+ if (*(local_line + 2) == '+')
+ return MOD_POSTINC;
+ if (strncasecmp (local_line, "-r", 2) == 0)
+ {
+ *line += 1;
+ return MOD_PREDEC;
+ }
+ return MOD_NONE;
+}
+
+/* Parse instruction operands. */
+
+static void
+xgate_scan_operands (struct xgate_opcode *opcode, s_operand oprs[])
+{
+ char *frag = xgate_new_instruction (opcode->size);
+ int where = frag - frag_now->fr_literal;
+ char *op = opcode->constraints;
+ unsigned int bin = (int) opcode->bin_opcode;
+ unsigned short oper_mask = 0;
+ int operand_bit_length = 0;
+ unsigned int operand = 0;
+ char n_operand_bits = 0;
+ char first_operand_equals_second = 0;
+ int i = 0;
+ char c = 0;
+
+ /* Generate available operand bits mask. */
+ for (i = 0; (c = opcode->format[i]); i++)
+ {
+ if (ISDIGIT (c) || (c == 's'))
+ {
+ oper_mask <<= 1;
+ }
+ else
+ {
+ oper_mask <<= 1;
+ oper_mask += 1;
+ n_operand_bits++;
+ }
+ }
+
+ /* Parse first operand. */
+ if (*op)
+ {
+ if (*op == '=')
+ {
+ first_operand_equals_second = 1;
+ ++op;
+ }
+ operand = xgate_parse_operand (opcode, &operand_bit_length, where,
+ &op, oprs[0]);
+ ++op;
+ bin = xgate_apply_operand (operand, &oper_mask, bin, operand_bit_length);
+
+ if(first_operand_equals_second)
+ bin = xgate_apply_operand (operand, &oper_mask, bin,
+ operand_bit_length);
+ /* Parse second operand. */
+ if (*op)
+ {
+ if (*op == ',')
+ ++op;
+ if (first_operand_equals_second)
+ {
+ bin = xgate_apply_operand (operand, &oper_mask, bin,
+ operand_bit_length);
+ ++op;
+ }
+ else
+ {
+ operand = xgate_parse_operand (opcode, &operand_bit_length, where,
+ &op, oprs[1]);
+ bin = xgate_apply_operand (operand, &oper_mask, bin,
+ operand_bit_length);
+ ++op;
+ }
+ }
+ /* Parse the third register. */
+ if (*op)
+ {
+ if (*op == ',')
+ ++op;
+ operand = xgate_parse_operand (opcode, &operand_bit_length, where,
+ &op, oprs[2]);
+ bin = xgate_apply_operand (operand, &oper_mask, bin,
+ operand_bit_length);
+ }
+ }
+ if (opcode->size == 2 && fixup_required)
+ {
+ bfd_putl16 (bin, frag);
+ }
+ else if ( !strcmp (opcode->constraints, XGATE_OP_REL9)
+ || !strcmp (opcode->constraints, XGATE_OP_REL10))
+ {
+ /* Write our data to a frag for further processing. */
+ bfd_putl16 (opcode->bin_opcode, frag);
+ }
+ else
+ {
+ /* Apply operand mask(s)to bin opcode and write the output. */
+ /* Since we are done write this frag in xgate BE format. */
+ number_to_chars_bigendian (frag, bin, opcode->size);
+ }
+ prev = bin;
+ return;
+}
+
+static unsigned int
+xgate_parse_operand (struct xgate_opcode *opcode,
+ int *bit_width,
+ int where,
+ char **op_con,
+ s_operand operand)
+{
+ char *op_constraint = *op_con;
+ unsigned int op_mask = 0;
+ unsigned int pp_fix = 0;
+ unsigned short max_size = 0;
+ int i;
+
+ *bit_width = 0;
+ /* Reset. */
+
+ switch (*op_constraint)
+ {
+ case '+': /* Indexed register operand +/- or plain r. */
+ /* Default to neither inc or dec. */
+ pp_fix = 0;
+ *bit_width = 5;
+
+ if (operand.reg == REG_NONE)
+ as_bad (_(": expected register name r0-r7 ") );
+ op_mask = operand.reg;
+ if(operand.mod == MOD_POSTINC)
+ pp_fix = INCREMENT;
+ if(operand.mod == MOD_PREDEC)
+ pp_fix = DECREMENT;
+ op_mask <<= 2;
+ op_mask |= pp_fix;
+ break;
+
+ case 'r': /* Register operand. */
+ if (operand.reg == REG_NONE)
+ as_bad (_(": expected register name r0-r7 "));
+
+ *bit_width = 3;
+
+ op_mask = operand.reg;
+ break;
+
+ case 'i': /* Immediate value or expression expected. */
+ /* Advance the original format pointer. */
+ (*op_con)++;
+ op_constraint++;
+ if (ISDIGIT (*op_constraint))
+ *bit_width = (int) *op_constraint - '0';
+ else if (*op_constraint == 'a')
+ *bit_width = 0x0A;
+ else if (*op_constraint == 'f')
+ *bit_width = 0x0F;
+
+ /* http://tigcc.ticalc.org/doc/gnuasm.html#SEC31 */
+ if (operand.exp.X_op == O_constant)
+ {
+ op_mask = operand.exp.X_add_number;
+ if (((opcode->name[strlen (opcode->name) - 1] == 'l') && autoHiLo)
+ || operand.mod == MOD_LOAD_LOW)
+ op_mask &= 0x00FF;
+ else if (((opcode->name[strlen (opcode->name) - 1]) == 'h'
+ && autoHiLo) || operand.mod == MOD_LOAD_HIGH)
+ op_mask >>= 8;
+
+ /* Make sure it fits. */
+ for (i = *bit_width; i; i--)
+ {
+ max_size <<= 1;
+ max_size += 1;
+ }
+ if (op_mask > max_size)
+ as_bad (_(":operand value(%d) too big for constraint"), op_mask);
+ }
+ else
+ {
+ /* Should be BFD_RELOC_XGATE_IMM8_LO instead of BFD_RELOC_XGATE_24
+ TODO fix. */
+ fixup_required = 1;
+ if (*op_constraint == '8')
+ {
+ if (((opcode->name[strlen (opcode->name) - 1] == 'l')
+ && autoHiLo) || operand.mod == MOD_LOAD_LOW)
+ fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
+ BFD_RELOC_XGATE_24);
+ else if (((opcode->name[strlen (opcode->name) - 1]) == 'h'
+ && autoHiLo) || operand.mod == MOD_LOAD_HIGH )
+ fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
+ BFD_RELOC_XGATE_IMM8_HI);
+ else
+ as_bad (_("you must use a hi/lo directive or 16-bit macro "
+ "to load a 16-bit value."));
+ }
+ else if (*op_constraint == '5')
+ fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
+ BFD_RELOC_XGATE_IMM5);
+ else if (*op_constraint == '4')
+ fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
+ BFD_RELOC_XGATE_IMM4);
+ else if (*op_constraint == '3')
+ fix_new_exp (frag_now, where, 2, &operand.exp, FALSE,
+ BFD_RELOC_XGATE_IMM3);
+ else
+ as_bad (_(":unknown relocation constraint size"));
+ }
+ break;
+
+ case 'c': /* CCR register expected. */
+ *bit_width = 0;
+ if (operand.reg != REG_CCR)
+ as_bad (_(": expected register name ccr "));
+ break;
+
+ case 'p': /* PC register expected. */
+ *bit_width = 0;
+ if (operand.reg != REG_PC)
+ as_bad (_(": expected register name pc "));
+ break;
+
+ case 'b': /* Branch expected. */
+ (*op_con)++;
+ op_constraint++;
+
+ if (operand.exp.X_op != O_register)
+ {
+ if (*op_constraint == '9')
+ fix_new_exp (frag_now, where, 2, &operand.exp, TRUE,
+ R_XGATE_PCREL_9);
+ else if (*op_constraint == 'a')
+ fix_new_exp (frag_now, where, 2, &operand.exp, TRUE,
+ R_XGATE_PCREL_10);
+ }
+ else
+ as_fatal (_("Operand `%x' not recognized in fixup8."),
+ operand.exp.X_op);
+ break;
+ case '?':
+ break;
+
+ default:
+ as_bad (_("unknown constraint `%c'"), *op_constraint);
+ break;
+ }
+ return op_mask;
+}
diff --git a/gas/config/tc-xgate.h b/gas/config/tc-xgate.h
new file mode 100644
index 0000000..30cff98
--- /dev/null
+++ b/gas/config/tc-xgate.h
@@ -0,0 +1,115 @@
+/* tc-xgate.h -- Header file for tc-xgate.c.
+ Copyright (C) 2010-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_XGATE
+#define cpuxgate 1
+
+struct fix;
+
+/* Define TC_M68K so that we can use the MRI mode. */
+#define TC_M68K
+
+#define TARGET_BYTES_BIG_ENDIAN 1
+
+/* Motorola assembler specs does not require '.' before pseudo-ops. */
+#define NO_PSEUDO_DOT 1
+
+/* The target BFD architecture. */
+#define TARGET_ARCH (xgate_arch ())
+extern enum bfd_architecture xgate_arch (void);
+
+#define TARGET_MACH (xgate_mach ())
+extern int xgate_mach (void);
+
+#define TARGET_FORMAT (xgate_arch_format ())
+extern const char *xgate_arch_format (void);
+
+#define LISTING_WORD_SIZE 1 /* A word is 1 bytes. */
+#define LISTING_LHS_WIDTH 4 /* One word on the first line. */
+#define LISTING_LHS_WIDTH_SECOND 4 /* One word on the second line. */
+#define LISTING_LHS_CONT_LINES 4 /* And 4 lines max. */
+#define LISTING_HEADER xgate_listing_header ()
+extern const char *xgate_listing_header (void);
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define tc_init_after_args xgate_init_after_args
+extern void xgate_init_after_args (void);
+
+#define md_parse_long_option xgate_parse_long_option
+extern int xgate_parse_long_option (char *);
+
+#define DWARF2_LINE_MIN_INSN_LENGTH 1
+
+/* Use 32-bit address to represent a symbol address so that we can
+ represent them with their page number. */
+#define DWARF2_ADDR_SIZE(bfd) 4
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+#define md_number_to_chars number_to_chars_bigendian
+
+/* Relax table to translate short relative branches (-128..127) into
+ absolute branches. */
+#define TC_GENERIC_RELAX_TABLE md_relax_table
+extern struct relax_type md_relax_table[];
+
+/* GAS only handles relaxations for pc-relative data targeting addresses
+ in the same segment, we have to encode all other cases */
+/* FIXME: impliment this. */
+/* #define md_relax_frag(SEG, FRAGP, STRETCH) \
+ ((FRAGP)->fr_symbol != NULL \
+ && S_GET_SEGMENT ((FRAGP)->fr_symbol) == (SEG) \
+ ? relax_frag (SEG, FRAGP, STRETCH) \
+ : xgate_relax_frag (SEG, FRAGP, STRETCH))
+extern long xgate_relax_frag (segT, fragS*, long); */
+
+#define TC_HANDLES_FX_DONE
+
+#define DIFF_EXPR_OK /* .-foo gets turned into PC relative relocs */
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+/* No shared lib support, so we don't need to ensure externally
+ visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+#define TC_FORCE_RELOCATION(fix) tc_xgate_force_relocation (fix)
+extern int tc_xgate_force_relocation (struct fix *);
+
+#define tc_fix_adjustable(X) tc_xgate_fix_adjustable(X)
+extern int tc_xgate_fix_adjustable (struct fix *);
+
+#define md_operand(x)
+
+#define elf_tc_final_processing xgate_elf_final_processing
+extern void xgate_elf_final_processing (void);
+
+/* Mark the symbol as being from XGATE. */
+#define tc_frob_symbol(sym, punt) punt = xgate_frob_symbol (sym)
+extern int xgate_frob_symbol (symbolS *);
+
+#if 0
+#define tc_print_statistics(FILE) xgate_print_statistics (FILE)
+extern void xgate_print_statistics (FILE *);
+#endif
diff --git a/gas/config/tc-xstormy16.c b/gas/config/tc-xstormy16.c
new file mode 100644
index 0000000..e8eba89
--- /dev/null
+++ b/gas/config/tc-xstormy16.c
@@ -0,0 +1,600 @@
+/* tc-xstormy16.c -- Assembler for the Sanyo XSTORMY16.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "subsegs.h"
+#include "symcat.h"
+#include "opcodes/xstormy16-desc.h"
+#include "opcodes/xstormy16-opc.h"
+#include "cgen.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];
+}
+xstormy16_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 O_fptr_symbol (O_max + 1)
+
+#define XSTORMY16_SHORTOPTS ""
+const char * md_shortopts = XSTORMY16_SHORTOPTS;
+
+struct option md_longopts[] =
+{
+ {NULL, no_argument, NULL, 0}
+};
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c ATTRIBUTE_UNUSED,
+ char * arg ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+void
+md_show_usage (FILE * stream)
+{
+ fprintf (stream, _(" XSTORMY16 specific command line options:\n"));
+}
+
+/* The target specific pseudo-ops which we support. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "word", cons, 4 },
+ { NULL, NULL, 0 }
+};
+
+
+void
+md_begin (void)
+{
+ /* Initialize the `cgen' interface. */
+
+ /* Set the machine number and endian. */
+ gas_cgen_cpu_desc = xstormy16_cgen_cpu_open (CGEN_CPU_OPEN_MACHS, 0,
+ CGEN_CPU_OPEN_ENDIAN,
+ CGEN_ENDIAN_LITTLE,
+ CGEN_CPU_OPEN_END);
+ xstormy16_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);
+}
+
+static bfd_boolean skipping_fptr = FALSE;
+
+void
+md_assemble (char * str)
+{
+ xstormy16_insn insn;
+ char * errmsg;
+
+ /* Make sure that if we had an erroneous input line which triggered
+ the skipping_fptr boolean that it does not affect following lines. */
+ skipping_fptr = FALSE;
+
+ /* Initialize GAS's cgen interface for a new instruction. */
+ gas_cgen_init_parse ();
+
+ insn.insn = xstormy16_cgen_assemble_insn
+ (gas_cgen_cpu_desc, str, & insn.fields, insn.buffer, & errmsg);
+
+ if (!insn.insn)
+ {
+ as_bad ("%s", 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), 0, NULL);
+}
+
+void
+md_operand (expressionS * e)
+{
+ if (*input_line_pointer != '@')
+ return;
+
+ if (strncmp (input_line_pointer + 1, "fptr", 4) == 0)
+ {
+ input_line_pointer += 5;
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer != '(')
+ {
+ as_bad (_("Expected '('"));
+ goto err;
+ }
+ input_line_pointer++;
+
+ expression (e);
+
+ if (*input_line_pointer != ')')
+ {
+ as_bad (_("Missing ')'"));
+ goto err;
+ }
+ input_line_pointer++;
+ SKIP_WHITESPACE ();
+
+ if (e->X_op != O_symbol)
+ as_bad (_("Not a symbolic expression"));
+ else if (* input_line_pointer == '-')
+ /* We are computing the difference of two function pointers
+ like this:
+
+ .hword @fptr (foo) - @fptr (bar)
+
+ In this situation we do not want to generate O_fptr_symbol
+ operands because the result is an absolute value, not a
+ function pointer.
+
+ We need to make the check here, rather than when the fixup
+ is generated as the function names (foo & bar in the above
+ example) might be local symbols and we want the expression
+ to be evaluated now. This kind of thing can happen when
+ gcc is generating computed gotos. */
+ skipping_fptr = TRUE;
+ else if (skipping_fptr)
+ skipping_fptr = FALSE;
+ else
+ e->X_op = O_fptr_symbol;
+ }
+
+ return;
+ err:
+ ignore_rest_of_line ();
+}
+
+/* Called while parsing data to create a fixup.
+ Create BFD_RELOC_XSTORMY16_FPTR16 relocations. */
+
+void
+xstormy16_cons_fix_new (fragS *f,
+ int where,
+ int nbytes,
+ expressionS *exp,
+ bfd_reloc_code_real_type code)
+{
+ if (exp->X_op == O_fptr_symbol)
+ {
+ switch (nbytes)
+ {
+ case 4:
+ /* This can happen when gcc is generating debug output.
+ For example it can create a stab with the address of
+ a function:
+
+ .stabs "foo:F(0,21)",36,0,0,@fptr(foo)
+
+ Since this does not involve switching code pages, we
+ just allow the reloc to be generated without any
+ @fptr behaviour. */
+ exp->X_op = O_symbol;
+ code = BFD_RELOC_32;
+ break;
+
+ case 2:
+ exp->X_op = O_symbol;
+ code = BFD_RELOC_XSTORMY16_FPTR16;
+ break;
+
+ default:
+ as_bad (_("unsupported fptr fixup size %d"), nbytes);
+ return;
+ }
+ }
+ else if (nbytes == 1)
+ code = BFD_RELOC_8;
+ else if (nbytes == 2)
+ code = BFD_RELOC_16;
+ else if (nbytes == 4)
+ code = BFD_RELOC_32;
+ else
+ {
+ as_bad (_("unsupported fixup size %d"), nbytes);
+ return;
+ }
+
+ fix_new_exp (f, where, nbytes, exp, 0, code);
+}
+
+/* Called while parsing an instruction to create a fixup.
+ Create BFD_RELOC_XSTORMY16_FPTR16 relocations. */
+
+fixS *
+xstormy16_cgen_record_fixup_exp (fragS * frag,
+ int where,
+ const CGEN_INSN * insn,
+ int length,
+ const CGEN_OPERAND * operand,
+ int opinfo,
+ expressionS * exp)
+{
+ fixS *fixP;
+ operatorT op = exp->X_op;
+
+ if (op == O_fptr_symbol)
+ exp->X_op = O_symbol;
+
+ fixP = gas_cgen_record_fixup_exp (frag, where, insn, length,
+ operand, opinfo, exp);
+
+ if (op == O_fptr_symbol)
+ {
+ if (operand->type != XSTORMY16_OPERAND_IMM16)
+ as_bad (_("unsupported fptr fixup"));
+ else
+ {
+ fixP->fx_r_type = BFD_RELOC_XSTORMY16_FPTR16;
+ fixP->fx_where += 2;
+ }
+ }
+
+ return fixP;
+}
+
+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;
+}
+
+/* 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 ATTRIBUTE_UNUSED,
+ segT segment ATTRIBUTE_UNUSED)
+{
+ /* No assembler relaxation is defined (or necessary) for this port. */
+ abort ();
+}
+
+/* *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. */
+
+void
+md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS * fragP ATTRIBUTE_UNUSED)
+{
+ /* No assembler relaxation is defined (or necessary) for this port. */
+ abort ();
+}
+
+/* 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))
+ || xstormy16_force_relocation (fixP))
+ /* The symbol is undefined,
+ or it is defined but not in this section,
+ or the relocation will be relative to this symbol not the section symbol.
+ 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)
+{
+ switch (operand->type)
+ {
+ case XSTORMY16_OPERAND_IMM2:
+ case XSTORMY16_OPERAND_IMM3:
+ case XSTORMY16_OPERAND_IMM3B:
+ case XSTORMY16_OPERAND_IMM4:
+ case XSTORMY16_OPERAND_HMEM8:
+ return BFD_RELOC_NONE;
+
+ case XSTORMY16_OPERAND_IMM12:
+ fixP->fx_where += 2;
+ return BFD_RELOC_XSTORMY16_12;
+
+ case XSTORMY16_OPERAND_IMM8:
+ case XSTORMY16_OPERAND_LMEM8:
+ return fixP->fx_pcrel ? BFD_RELOC_8_PCREL : BFD_RELOC_8;
+
+ case XSTORMY16_OPERAND_IMM16:
+ /* This might have been processed at parse time. */
+ fixP->fx_where += 2;
+ if (fixP->fx_cgen.opinfo && fixP->fx_cgen.opinfo != BFD_RELOC_NONE)
+ return fixP->fx_cgen.opinfo;
+ return fixP->fx_pcrel ? BFD_RELOC_16_PCREL : BFD_RELOC_16;
+
+ case XSTORMY16_OPERAND_ABS24:
+ return BFD_RELOC_XSTORMY16_24;
+
+ case XSTORMY16_OPERAND_REL8_4:
+ fixP->fx_addnumber -= 2;
+ case XSTORMY16_OPERAND_REL8_2:
+ fixP->fx_addnumber -= 2;
+ fixP->fx_pcrel = 1;
+ return BFD_RELOC_8_PCREL;
+
+ case XSTORMY16_OPERAND_REL12:
+ fixP->fx_where += 2;
+ /* Fall through... */
+ case XSTORMY16_OPERAND_REL12A:
+ fixP->fx_addnumber -= 2;
+ fixP->fx_pcrel = 1;
+ return BFD_RELOC_XSTORMY16_REL_12;
+
+ default : /* avoid -Wall warning */
+ abort ();
+ }
+}
+
+/* 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
+xstormy16_force_relocation (fixS * fix)
+{
+ if (fix->fx_r_type == BFD_RELOC_XSTORMY16_FPTR16)
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+/* Return true if a relocation against a symbol may be replaced with
+ a relocation against section+offset. */
+
+bfd_boolean
+xstormy16_fix_adjustable (fixS * fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return FALSE;
+
+ if (fixP->fx_r_type == BFD_RELOC_XSTORMY16_FPTR16)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* This is a copy of gas_cgen_md_apply_fix, with some enhancements to
+ do various things that would not be valid for all ports. */
+
+void
+xstormy16_md_apply_fix (fixS * fixP,
+ valueT * valueP,
+ segT seg ATTRIBUTE_UNUSED)
+{
+ char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT value = *valueP;
+ /* Canonical name, since used a lot. */
+ CGEN_CPU_DESC cd = gas_cgen_cpu_desc;
+
+ /* md_cgen_lookup_reloc() will adjust this to compensate for where
+ in the opcode the relocation happens, for pcrel relocations. We
+ have no other way of keeping track of what this offset needs to
+ be. */
+ fixP->fx_addnumber = 0;
+
+ /* This port has pc-relative relocs and DIFF_EXPR_OK defined, so
+ it must deal with turning a BFD_RELOC_{8,16,32,64} into a
+ BFD_RELOC_*_PCREL for the case of
+
+ .word something-. */
+ if (fixP->fx_pcrel)
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_8_PCREL;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_16_PCREL;
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_32_PCREL;
+ break;
+ case BFD_RELOC_64:
+ fixP->fx_r_type = BFD_RELOC_64_PCREL;
+ break;
+ default:
+ break;
+ }
+
+ if (fixP->fx_addsy == (symbolS *) NULL)
+ fixP->fx_done = 1;
+
+ /* We don't actually support subtracting a symbol. */
+ if (fixP->fx_subsy != (symbolS *) NULL)
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+ {
+ int opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+ const CGEN_OPERAND *operand = cgen_operand_lookup_by_num (cd, opindex);
+ const char *errmsg;
+ bfd_reloc_code_real_type reloc_type;
+ CGEN_FIELDS *fields = alloca (CGEN_CPU_SIZEOF_FIELDS (cd));
+ const CGEN_INSN *insn = fixP->fx_cgen.insn;
+
+ /* If the reloc has been fully resolved finish the operand here. */
+ /* FIXME: This duplicates the capabilities of code in BFD. */
+ if (fixP->fx_done)
+ {
+ CGEN_CPU_SET_FIELDS_BITSIZE (cd) (fields, CGEN_INSN_BITSIZE (insn));
+ CGEN_CPU_SET_VMA_OPERAND (cd) (cd, opindex, fields, (bfd_vma) value);
+
+#if CGEN_INT_INSN_P
+ {
+ CGEN_INSN_INT insn_value =
+ cgen_get_insn_value (cd, (unsigned char *) where,
+ CGEN_INSN_BITSIZE (insn));
+
+ /* ??? 0 is passed for `pc'. */
+ errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields,
+ &insn_value, (bfd_vma) 0);
+ cgen_put_insn_value (cd, (unsigned char *) where,
+ CGEN_INSN_BITSIZE (insn), insn_value);
+ }
+#else
+ /* ??? 0 is passed for `pc'. */
+ errmsg = CGEN_CPU_INSERT_OPERAND (cd) (cd, opindex, fields,
+ (unsigned char *) where,
+ (bfd_vma) 0);
+#endif
+ if (errmsg)
+ as_bad_where (fixP->fx_file, fixP->fx_line, "%s", errmsg);
+ }
+
+ if (fixP->fx_done)
+ return;
+
+ /* The operand isn't fully resolved. Determine a BFD reloc value
+ based on the operand information and leave it to
+ bfd_install_relocation. Note that this doesn't work when
+ !partial_inplace. */
+
+ reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ if (reloc_type != BFD_RELOC_NONE)
+ fixP->fx_r_type = reloc_type;
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("unresolved expression that must be resolved"));
+ fixP->fx_done = 1;
+ return;
+ }
+ }
+ else if (fixP->fx_done)
+ {
+ /* We're finished with this fixup. Install it because
+ bfd_install_relocation won't be called to do it. */
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ md_number_to_chars (where, value, 1);
+ break;
+ case BFD_RELOC_16:
+ md_number_to_chars (where, value, 2);
+ break;
+ case BFD_RELOC_32:
+ md_number_to_chars (where, value, 4);
+ break;
+ case BFD_RELOC_64:
+ md_number_to_chars (where, value, 8);
+ break;
+ default:
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("internal error: can't install fix for reloc type %d (`%s')"),
+ fixP->fx_r_type, bfd_get_reloc_code_name (fixP->fx_r_type));
+ break;
+ }
+ }
+ else
+ {
+ /* bfd_install_relocation will be called to finish things up. */
+ }
+
+ /* This is a RELA port. Thus, it does not need to store a
+ value if it is going to make a reloc. What's more, when
+ assembling a line like
+
+ .byte global-0x7f00
+
+ we'll get a spurious error message if we try to stuff 0x7f00 into
+ the byte. */
+ if (! fixP->fx_done)
+ *valueP = 0;
+
+ /* Tuck `value' away for use by tc_gen_reloc.
+ See the comment describing fx_addnumber in write.h.
+ This field is misnamed (or misused :-). */
+ fixP->fx_addnumber += value;
+}
+
+
+/* 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);
+}
+
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, FALSE);
+}
diff --git a/gas/config/tc-xstormy16.h b/gas/config/tc-xstormy16.h
new file mode 100644
index 0000000..064a85c
--- /dev/null
+++ b/gas/config/tc-xstormy16.h
@@ -0,0 +1,69 @@
+/* tc-xstormy16.h -- Header file for tc-xstormy16.c.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TC_XSTORMY16
+
+#define LISTING_HEADER "XSTORMY16 GAS "
+
+/* The target BFD architecture. */
+#define TARGET_ARCH bfd_arch_xstormy16
+
+#define TARGET_FORMAT "elf32-xstormy16"
+
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* Permit temporary numeric labels. */
+#define LOCAL_LABELS_FB 1
+
+#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs. */
+
+/* We don't need to handle .word strangely. */
+#define WORKING_DOT_WORD
+
+/* Values passed to md_apply_fix don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define md_apply_fix xstormy16_md_apply_fix
+
+#define tc_fix_adjustable(FIX) xstormy16_fix_adjustable (FIX)
+extern bfd_boolean xstormy16_fix_adjustable (struct fix *);
+
+#define TC_FORCE_RELOCATION(fix) xstormy16_force_relocation (fix)
+extern int xstormy16_force_relocation (struct fix *);
+
+#define TC_HANDLES_FX_DONE
+
+#define tc_gen_reloc gas_cgen_tc_gen_reloc
+
+/* Call md_pcrel_from_section(), not md_pcrel_from(). */
+#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from_section (FIX, SEC)
+extern long md_pcrel_from_section (struct fix *, segT);
+
+#define TC_CONS_FIX_NEW xstormy16_cons_fix_new
+extern void xstormy16_cons_fix_new (fragS *f, int, int, expressionS *,
+ bfd_reloc_code_real_type);
+
+#define md_cgen_record_fixup_exp xstormy16_cgen_record_fixup_exp
+
+/* Minimum instruction is two bytes. */
+#define DWARF2_LINE_MIN_INSN_LENGTH 2
+
+/* This target is buggy, and sets fix size too large. */
+#define TC_FX_SIZE_SLACK(FIX) 2
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
new file mode 100644
index 0000000..d11b0c7
--- /dev/null
+++ b/gas/config/tc-xtensa.c
@@ -0,0 +1,12603 @@
+/* tc-xtensa.c -- Assemble Xtensa instructions.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#include "as.h"
+#include <limits.h>
+#include "sb.h"
+#include "safe-ctype.h"
+#include "tc-xtensa.h"
+#include "subsegs.h"
+#include "xtensa-relax.h"
+#include "dwarf2dbg.h"
+#include "xtensa-istack.h"
+#include "struc-symbol.h"
+#include "xtensa-config.h"
+
+/* Provide default values for new configuration settings. */
+#ifndef XSHAL_ABI
+#define XSHAL_ABI 0
+#endif
+
+#ifndef uint32
+#define uint32 unsigned int
+#endif
+#ifndef int32
+#define int32 signed int
+#endif
+
+/* Notes:
+
+ Naming conventions (used somewhat inconsistently):
+ The xtensa_ functions are exported
+ The xg_ functions are internal
+
+ We also have a couple of different extensibility mechanisms.
+ 1) The idiom replacement:
+ This is used when a line is first parsed to
+ replace an instruction pattern with another instruction
+ It is currently limited to replacements of instructions
+ with constant operands.
+ 2) The xtensa-relax.c mechanism that has stronger instruction
+ replacement patterns. When an instruction's immediate field
+ does not fit the next instruction sequence is attempted.
+ In addition, "narrow" opcodes are supported this way. */
+
+
+/* Define characters with special meanings to GAS. */
+const char comment_chars[] = "#";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+const char EXP_CHARS[] = "eE";
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+
+/* Flags to indicate whether the hardware supports the density and
+ absolute literals options. */
+
+bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
+bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
+
+static vliw_insn cur_vinsn;
+
+unsigned xtensa_num_pipe_stages;
+unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
+
+static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
+
+/* Some functions are only valid in the front end. This variable
+ allows us to assert that we haven't crossed over into the
+ back end. */
+static bfd_boolean past_xtensa_end = FALSE;
+
+/* Flags for properties of the last instruction in a segment. */
+#define FLAG_IS_A0_WRITER 0x1
+#define FLAG_IS_BAD_LOOPEND 0x2
+
+
+/* We define a special segment names ".literal" to place literals
+ into. The .fini and .init sections are special because they
+ contain code that is moved together by the linker. We give them
+ their own special .fini.literal and .init.literal sections. */
+
+#define LITERAL_SECTION_NAME xtensa_section_rename (".literal")
+#define LIT4_SECTION_NAME xtensa_section_rename (".lit4")
+#define INIT_SECTION_NAME xtensa_section_rename (".init")
+#define FINI_SECTION_NAME xtensa_section_rename (".fini")
+
+
+/* This type is used for the directive_stack to keep track of the
+ state of the literal collection pools. If lit_prefix is set, it is
+ used to determine the literal section names; otherwise, the literal
+ sections are determined based on the current text section. The
+ lit_seg and lit4_seg fields cache these literal sections, with the
+ current_text_seg field used a tag to indicate whether the cached
+ values are valid. */
+
+typedef struct lit_state_struct
+{
+ char *lit_prefix;
+ segT current_text_seg;
+ segT lit_seg;
+ segT lit4_seg;
+} lit_state;
+
+static lit_state default_lit_sections;
+
+
+/* We keep a list of literal segments. The seg_list type is the node
+ for this list. The literal_head pointer is the head of the list,
+ with the literal_head_h dummy node at the start. */
+
+typedef struct seg_list_struct
+{
+ struct seg_list_struct *next;
+ segT seg;
+} seg_list;
+
+static seg_list literal_head_h;
+static seg_list *literal_head = &literal_head_h;
+
+
+/* Lists of symbols. We keep a list of symbols that label the current
+ instruction, so that we can adjust the symbols when inserting alignment
+ for various instructions. We also keep a list of all the symbols on
+ literals, so that we can fix up those symbols when the literals are
+ later moved into the text sections. */
+
+typedef struct sym_list_struct
+{
+ struct sym_list_struct *next;
+ symbolS *sym;
+} sym_list;
+
+static sym_list *insn_labels = NULL;
+static sym_list *free_insn_labels = NULL;
+static sym_list *saved_insn_labels = NULL;
+
+static sym_list *literal_syms;
+
+
+/* Flags to determine whether to prefer const16 or l32r
+ if both options are available. */
+int prefer_const16 = 0;
+int prefer_l32r = 0;
+
+/* Global flag to indicate when we are emitting literals. */
+int generating_literals = 0;
+
+/* The following PROPERTY table definitions are copied from
+ <elf/xtensa.h> and must be kept in sync with the code there. */
+
+/* Flags in the property tables to specify whether blocks of memory
+ are literals, instructions, data, or unreachable. For
+ instructions, blocks that begin loop targets and branch targets are
+ designated. Blocks that do not allow density, instruction
+ reordering or transformation are also specified. Finally, for
+ branch targets, branch target alignment priority is included.
+ Alignment of the next block is specified in the current block
+ and the size of the current block does not include any fill required
+ to align to the next block. */
+
+#define XTENSA_PROP_LITERAL 0x00000001
+#define XTENSA_PROP_INSN 0x00000002
+#define XTENSA_PROP_DATA 0x00000004
+#define XTENSA_PROP_UNREACHABLE 0x00000008
+/* Instruction only properties at beginning of code. */
+#define XTENSA_PROP_INSN_LOOP_TARGET 0x00000010
+#define XTENSA_PROP_INSN_BRANCH_TARGET 0x00000020
+/* Instruction only properties about code. */
+#define XTENSA_PROP_INSN_NO_DENSITY 0x00000040
+#define XTENSA_PROP_INSN_NO_REORDER 0x00000080
+/* Historically, NO_TRANSFORM was a property of instructions,
+ but it should apply to literals under certain circumstances. */
+#define XTENSA_PROP_NO_TRANSFORM 0x00000100
+
+/* Branch target alignment information. This transmits information
+ to the linker optimization about the priority of aligning a
+ particular block for branch target alignment: None, low priority,
+ high priority, or required. These only need to be checked in
+ instruction blocks marked as XTENSA_PROP_INSN_BRANCH_TARGET.
+ Common usage is
+
+ switch (GET_XTENSA_PROP_BT_ALIGN (flags))
+ case XTENSA_PROP_BT_ALIGN_NONE:
+ case XTENSA_PROP_BT_ALIGN_LOW:
+ case XTENSA_PROP_BT_ALIGN_HIGH:
+ case XTENSA_PROP_BT_ALIGN_REQUIRE:
+*/
+#define XTENSA_PROP_BT_ALIGN_MASK 0x00000600
+
+/* No branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_NONE 0x0
+/* Low priority branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_LOW 0x1
+/* High priority branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_HIGH 0x2
+/* Required branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_REQUIRE 0x3
+
+#define GET_XTENSA_PROP_BT_ALIGN(flag) \
+ (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
+#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
+ (((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
+ (((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
+
+
+/* Alignment is specified in the block BEFORE the one that needs
+ alignment. Up to 5 bits. Use GET_XTENSA_PROP_ALIGNMENT(flags) to
+ get the required alignment specified as a power of 2. Use
+ SET_XTENSA_PROP_ALIGNMENT(flags, pow2) to set the required
+ alignment. Be careful of side effects since the SET will evaluate
+ flags twice. Also, note that the SIZE of a block in the property
+ table does not include the alignment size, so the alignment fill
+ must be calculated to determine if two blocks are contiguous.
+ TEXT_ALIGN is not currently implemented but is a placeholder for a
+ possible future implementation. */
+
+#define XTENSA_PROP_ALIGN 0x00000800
+
+#define XTENSA_PROP_ALIGNMENT_MASK 0x0001f000
+
+#define GET_XTENSA_PROP_ALIGNMENT(flag) \
+ (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
+#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
+ (((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
+ (((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
+
+#define XTENSA_PROP_INSN_ABSLIT 0x00020000
+
+
+/* Structure for saving instruction and alignment per-fragment data
+ that will be written to the object file. This structure is
+ equivalent to the actual data that will be written out to the file
+ but is easier to use. We provide a conversion to file flags
+ in frag_flags_to_number. */
+
+typedef struct frag_flags_struct frag_flags;
+
+struct frag_flags_struct
+{
+ /* is_literal should only be used after xtensa_move_literals.
+ If you need to check if you are generating a literal fragment,
+ then use the generating_literals global. */
+
+ unsigned is_literal : 1;
+ unsigned is_insn : 1;
+ unsigned is_data : 1;
+ unsigned is_unreachable : 1;
+
+ /* is_specific_opcode implies no_transform. */
+ unsigned is_no_transform : 1;
+
+ struct
+ {
+ unsigned is_loop_target : 1;
+ unsigned is_branch_target : 1; /* Branch targets have a priority. */
+ unsigned bt_align_priority : 2;
+
+ unsigned is_no_density : 1;
+ /* no_longcalls flag does not need to be placed in the object file. */
+
+ unsigned is_no_reorder : 1;
+
+ /* Uses absolute literal addressing for l32r. */
+ unsigned is_abslit : 1;
+ } insn;
+ unsigned is_align : 1;
+ unsigned alignment : 5;
+};
+
+
+/* Structure for saving information about a block of property data
+ for frags that have the same flags. */
+struct xtensa_block_info_struct
+{
+ segT sec;
+ bfd_vma offset;
+ size_t size;
+ frag_flags flags;
+ struct xtensa_block_info_struct *next;
+};
+
+
+/* Structure for saving the current state before emitting literals. */
+typedef struct emit_state_struct
+{
+ const char *name;
+ segT now_seg;
+ subsegT now_subseg;
+ int generating_literals;
+} emit_state;
+
+
+/* Opcode placement information */
+
+typedef unsigned long long bitfield;
+#define bit_is_set(bit, bf) ((bf) & (0x01ll << (bit)))
+#define set_bit(bit, bf) ((bf) |= (0x01ll << (bit)))
+#define clear_bit(bit, bf) ((bf) &= ~(0x01ll << (bit)))
+
+#define MAX_FORMATS 32
+
+typedef struct op_placement_info_struct
+{
+ int num_formats;
+ /* A number describing how restrictive the issue is for this
+ opcode. For example, an opcode that fits lots of different
+ formats has a high freedom, as does an opcode that fits
+ only one format but many slots in that format. The most
+ restrictive is the opcode that fits only one slot in one
+ format. */
+ int issuef;
+ xtensa_format narrowest;
+ char narrowest_size;
+ char narrowest_slot;
+
+ /* formats is a bitfield with the Nth bit set
+ if the opcode fits in the Nth xtensa_format. */
+ bitfield formats;
+
+ /* slots[N]'s Mth bit is set if the op fits in the
+ Mth slot of the Nth xtensa_format. */
+ bitfield slots[MAX_FORMATS];
+
+ /* A count of the number of slots in a given format
+ an op can fit (i.e., the bitcount of the slot field above). */
+ char slots_in_format[MAX_FORMATS];
+
+} op_placement_info, *op_placement_info_table;
+
+op_placement_info_table op_placement_table;
+
+
+/* Extra expression types. */
+
+#define O_pltrel O_md1 /* like O_symbol but use a PLT reloc */
+#define O_hi16 O_md2 /* use high 16 bits of symbolic value */
+#define O_lo16 O_md3 /* use low 16 bits of symbolic value */
+#define O_pcrel O_md4 /* value is a PC-relative offset */
+#define O_tlsfunc O_md5 /* TLS_FUNC/TLSDESC_FN relocation */
+#define O_tlsarg O_md6 /* TLS_ARG/TLSDESC_ARG relocation */
+#define O_tlscall O_md7 /* TLS_CALL relocation */
+#define O_tpoff O_md8 /* TPOFF relocation */
+#define O_dtpoff O_md9 /* DTPOFF relocation */
+
+struct suffix_reloc_map
+{
+ char *suffix;
+ int length;
+ bfd_reloc_code_real_type reloc;
+ unsigned char operator;
+};
+
+#define SUFFIX_MAP(str, reloc, op) { str, sizeof (str) - 1, reloc, op }
+
+static struct suffix_reloc_map suffix_relocs[] =
+{
+ SUFFIX_MAP ("l", BFD_RELOC_LO16, O_lo16),
+ SUFFIX_MAP ("h", BFD_RELOC_HI16, O_hi16),
+ SUFFIX_MAP ("plt", BFD_RELOC_XTENSA_PLT, O_pltrel),
+ SUFFIX_MAP ("pcrel", BFD_RELOC_32_PCREL, O_pcrel),
+ SUFFIX_MAP ("tlsfunc", BFD_RELOC_XTENSA_TLS_FUNC, O_tlsfunc),
+ SUFFIX_MAP ("tlsarg", BFD_RELOC_XTENSA_TLS_ARG, O_tlsarg),
+ SUFFIX_MAP ("tlscall", BFD_RELOC_XTENSA_TLS_CALL, O_tlscall),
+ SUFFIX_MAP ("tpoff", BFD_RELOC_XTENSA_TLS_TPOFF, O_tpoff),
+ SUFFIX_MAP ("dtpoff", BFD_RELOC_XTENSA_TLS_DTPOFF, O_dtpoff),
+ { (char *) 0, 0, BFD_RELOC_UNUSED, 0 }
+};
+
+
+/* Directives. */
+
+typedef enum
+{
+ directive_none = 0,
+ directive_literal,
+ directive_density,
+ directive_transform,
+ directive_freeregs,
+ directive_longcalls,
+ directive_literal_prefix,
+ directive_schedule,
+ directive_absolute_literals,
+ directive_last_directive
+} directiveE;
+
+typedef struct
+{
+ const char *name;
+ bfd_boolean can_be_negated;
+} directive_infoS;
+
+const directive_infoS directive_info[] =
+{
+ { "none", FALSE },
+ { "literal", FALSE },
+ { "density", TRUE },
+ { "transform", TRUE },
+ { "freeregs", FALSE },
+ { "longcalls", TRUE },
+ { "literal_prefix", FALSE },
+ { "schedule", TRUE },
+ { "absolute-literals", TRUE }
+};
+
+bfd_boolean directive_state[] =
+{
+ FALSE, /* none */
+ FALSE, /* literal */
+#if !XCHAL_HAVE_DENSITY
+ FALSE, /* density */
+#else
+ TRUE, /* density */
+#endif
+ TRUE, /* transform */
+ FALSE, /* freeregs */
+ FALSE, /* longcalls */
+ FALSE, /* literal_prefix */
+ FALSE, /* schedule */
+#if XSHAL_USE_ABSOLUTE_LITERALS
+ TRUE /* absolute_literals */
+#else
+ FALSE /* absolute_literals */
+#endif
+};
+
+
+/* Directive functions. */
+
+static void xtensa_begin_directive (int);
+static void xtensa_end_directive (int);
+static void xtensa_literal_prefix (void);
+static void xtensa_literal_position (int);
+static void xtensa_literal_pseudo (int);
+static void xtensa_frequency_pseudo (int);
+static void xtensa_elf_cons (int);
+static void xtensa_leb128 (int);
+
+/* Parsing and Idiom Translation. */
+
+static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
+
+/* Various Other Internal Functions. */
+
+extern bfd_boolean xg_is_single_relaxable_insn (TInsn *, TInsn *, bfd_boolean);
+static bfd_boolean xg_build_to_insn (TInsn *, TInsn *, BuildInstr *);
+static void xtensa_mark_literal_pool_location (void);
+static addressT get_expanded_loop_offset (xtensa_opcode);
+static fragS *get_literal_pool_location (segT);
+static void set_literal_pool_location (segT, fragS *);
+static void xtensa_set_frag_assembly_state (fragS *);
+static void finish_vinsn (vliw_insn *);
+static bfd_boolean emit_single_op (TInsn *);
+static int total_frag_text_expansion (fragS *);
+static bfd_boolean use_trampolines = TRUE;
+static void xtensa_check_frag_count (void);
+static void xtensa_create_trampoline_frag (bfd_boolean);
+static void xtensa_maybe_create_trampoline_frag (void);
+struct trampoline_frag;
+static int init_trampoline_frag (struct trampoline_frag *);
+
+/* Alignment Functions. */
+
+static int get_text_align_power (unsigned);
+static int get_text_align_max_fill_size (int, bfd_boolean, bfd_boolean);
+static int branch_align_power (segT);
+
+/* Helpers for xtensa_relax_frag(). */
+
+static long relax_frag_add_nop (fragS *);
+
+/* Accessors for additional per-subsegment information. */
+
+static unsigned get_last_insn_flags (segT, subsegT);
+static void set_last_insn_flags (segT, subsegT, unsigned, bfd_boolean);
+static float get_subseg_total_freq (segT, subsegT);
+static float get_subseg_target_freq (segT, subsegT);
+static void set_subseg_freq (segT, subsegT, float, float);
+
+/* Segment list functions. */
+
+static void xtensa_move_literals (void);
+static void xtensa_reorder_segments (void);
+static void xtensa_switch_to_literal_fragment (emit_state *);
+static void xtensa_switch_to_non_abs_literal_fragment (emit_state *);
+static void xtensa_switch_section_emit_state (emit_state *, segT, subsegT);
+static void xtensa_restore_emit_state (emit_state *);
+static segT cache_literal_section (bfd_boolean);
+
+/* Import from elf32-xtensa.c in BFD library. */
+
+extern asection *xtensa_make_property_section (asection *, const char *);
+
+/* op_placement_info functions. */
+
+static void init_op_placement_info_table (void);
+extern bfd_boolean opcode_fits_format_slot (xtensa_opcode, xtensa_format, int);
+static int xg_get_single_size (xtensa_opcode);
+static xtensa_format xg_get_single_format (xtensa_opcode);
+static int xg_get_single_slot (xtensa_opcode);
+
+/* TInsn and IStack functions. */
+
+static bfd_boolean tinsn_has_symbolic_operands (const TInsn *);
+static bfd_boolean tinsn_has_invalid_symbolic_operands (const TInsn *);
+static bfd_boolean tinsn_has_complex_operands (const TInsn *);
+static bfd_boolean tinsn_to_insnbuf (TInsn *, xtensa_insnbuf);
+static bfd_boolean tinsn_check_arguments (const TInsn *);
+static void tinsn_from_chars (TInsn *, char *, int);
+static void tinsn_immed_from_frag (TInsn *, fragS *, int);
+static int get_num_stack_text_bytes (IStack *);
+static int get_num_stack_literal_bytes (IStack *);
+static bfd_boolean tinsn_to_slotbuf (xtensa_format, int, TInsn *, xtensa_insnbuf);
+
+/* vliw_insn functions. */
+
+static void xg_init_vinsn (vliw_insn *);
+static void xg_copy_vinsn (vliw_insn *, vliw_insn *);
+static void xg_clear_vinsn (vliw_insn *);
+static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
+static void xg_free_vinsn (vliw_insn *);
+static bfd_boolean vinsn_to_insnbuf
+ (vliw_insn *, char *, fragS *, bfd_boolean);
+static void vinsn_from_chars (vliw_insn *, char *);
+
+/* Expression Utilities. */
+
+bfd_boolean expr_is_const (const expressionS *);
+offsetT get_expr_const (const expressionS *);
+void set_expr_const (expressionS *, offsetT);
+bfd_boolean expr_is_register (const expressionS *);
+offsetT get_expr_register (const expressionS *);
+void set_expr_symbol_offset (expressionS *, symbolS *, offsetT);
+bfd_boolean expr_is_equal (expressionS *, expressionS *);
+static void copy_expr (expressionS *, const expressionS *);
+
+/* Section renaming. */
+
+static void build_section_rename (const char *);
+
+
+/* ISA imported from bfd. */
+extern xtensa_isa xtensa_default_isa;
+
+extern int target_big_endian;
+
+static xtensa_opcode xtensa_addi_opcode;
+static xtensa_opcode xtensa_addmi_opcode;
+static xtensa_opcode xtensa_call0_opcode;
+static xtensa_opcode xtensa_call4_opcode;
+static xtensa_opcode xtensa_call8_opcode;
+static xtensa_opcode xtensa_call12_opcode;
+static xtensa_opcode xtensa_callx0_opcode;
+static xtensa_opcode xtensa_callx4_opcode;
+static xtensa_opcode xtensa_callx8_opcode;
+static xtensa_opcode xtensa_callx12_opcode;
+static xtensa_opcode xtensa_const16_opcode;
+static xtensa_opcode xtensa_entry_opcode;
+static xtensa_opcode xtensa_extui_opcode;
+static xtensa_opcode xtensa_movi_opcode;
+static xtensa_opcode xtensa_movi_n_opcode;
+static xtensa_opcode xtensa_isync_opcode;
+static xtensa_opcode xtensa_j_opcode;
+static xtensa_opcode xtensa_jx_opcode;
+static xtensa_opcode xtensa_l32r_opcode;
+static xtensa_opcode xtensa_loop_opcode;
+static xtensa_opcode xtensa_loopnez_opcode;
+static xtensa_opcode xtensa_loopgtz_opcode;
+static xtensa_opcode xtensa_nop_opcode;
+static xtensa_opcode xtensa_nop_n_opcode;
+static xtensa_opcode xtensa_or_opcode;
+static xtensa_opcode xtensa_ret_opcode;
+static xtensa_opcode xtensa_ret_n_opcode;
+static xtensa_opcode xtensa_retw_opcode;
+static xtensa_opcode xtensa_retw_n_opcode;
+static xtensa_opcode xtensa_rsr_lcount_opcode;
+static xtensa_opcode xtensa_waiti_opcode;
+static int config_max_slots = 0;
+
+
+/* Command-line Options. */
+
+bfd_boolean use_literal_section = TRUE;
+enum flix_level produce_flix = FLIX_ALL;
+static bfd_boolean align_targets = TRUE;
+static bfd_boolean warn_unaligned_branch_targets = FALSE;
+static bfd_boolean has_a0_b_retw = FALSE;
+static bfd_boolean workaround_a0_b_retw = FALSE;
+static bfd_boolean workaround_b_j_loop_end = FALSE;
+static bfd_boolean workaround_short_loop = FALSE;
+static bfd_boolean maybe_has_short_loop = FALSE;
+static bfd_boolean workaround_close_loop_end = FALSE;
+static bfd_boolean maybe_has_close_loop_end = FALSE;
+static bfd_boolean enforce_three_byte_loop_align = FALSE;
+
+/* When workaround_short_loops is TRUE, all loops with early exits must
+ have at least 3 instructions. workaround_all_short_loops is a modifier
+ to the workaround_short_loop flag. In addition to the
+ workaround_short_loop actions, all straightline loopgtz and loopnez
+ must have at least 3 instructions. */
+
+static bfd_boolean workaround_all_short_loops = FALSE;
+
+
+static void
+xtensa_setup_hw_workarounds (int earliest, int latest)
+{
+ if (earliest > latest)
+ as_fatal (_("illegal range of target hardware versions"));
+
+ /* Enable all workarounds for pre-T1050.0 hardware. */
+ if (earliest < 105000 || latest < 105000)
+ {
+ workaround_a0_b_retw |= TRUE;
+ workaround_b_j_loop_end |= TRUE;
+ workaround_short_loop |= TRUE;
+ workaround_close_loop_end |= TRUE;
+ workaround_all_short_loops |= TRUE;
+ enforce_three_byte_loop_align = TRUE;
+ }
+}
+
+
+enum
+{
+ option_density = OPTION_MD_BASE,
+ option_no_density,
+
+ option_flix,
+ option_no_generate_flix,
+ option_no_flix,
+
+ option_relax,
+ option_no_relax,
+
+ option_link_relax,
+ option_no_link_relax,
+
+ option_generics,
+ option_no_generics,
+
+ option_transform,
+ option_no_transform,
+
+ option_text_section_literals,
+ option_no_text_section_literals,
+
+ option_absolute_literals,
+ option_no_absolute_literals,
+
+ option_align_targets,
+ option_no_align_targets,
+
+ option_warn_unaligned_targets,
+
+ option_longcalls,
+ option_no_longcalls,
+
+ option_workaround_a0_b_retw,
+ option_no_workaround_a0_b_retw,
+
+ option_workaround_b_j_loop_end,
+ option_no_workaround_b_j_loop_end,
+
+ option_workaround_short_loop,
+ option_no_workaround_short_loop,
+
+ option_workaround_all_short_loops,
+ option_no_workaround_all_short_loops,
+
+ option_workaround_close_loop_end,
+ option_no_workaround_close_loop_end,
+
+ option_no_workarounds,
+
+ option_rename_section_name,
+
+ option_prefer_l32r,
+ option_prefer_const16,
+
+ option_target_hardware,
+
+ option_trampolines,
+ option_no_trampolines,
+};
+
+const char *md_shortopts = "";
+
+struct option md_longopts[] =
+{
+ { "density", no_argument, NULL, option_density },
+ { "no-density", no_argument, NULL, option_no_density },
+
+ { "flix", no_argument, NULL, option_flix },
+ { "no-generate-flix", no_argument, NULL, option_no_generate_flix },
+ { "no-allow-flix", no_argument, NULL, option_no_flix },
+
+ /* Both "relax" and "generics" are deprecated and treated as equivalent
+ to the "transform" option. */
+ { "relax", no_argument, NULL, option_relax },
+ { "no-relax", no_argument, NULL, option_no_relax },
+ { "generics", no_argument, NULL, option_generics },
+ { "no-generics", no_argument, NULL, option_no_generics },
+
+ { "transform", no_argument, NULL, option_transform },
+ { "no-transform", no_argument, NULL, option_no_transform },
+ { "text-section-literals", no_argument, NULL, option_text_section_literals },
+ { "no-text-section-literals", no_argument, NULL,
+ option_no_text_section_literals },
+ { "absolute-literals", no_argument, NULL, option_absolute_literals },
+ { "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
+ /* This option was changed from -align-target to -target-align
+ because it conflicted with the "-al" option. */
+ { "target-align", no_argument, NULL, option_align_targets },
+ { "no-target-align", no_argument, NULL, option_no_align_targets },
+ { "warn-unaligned-targets", no_argument, NULL,
+ option_warn_unaligned_targets },
+ { "longcalls", no_argument, NULL, option_longcalls },
+ { "no-longcalls", no_argument, NULL, option_no_longcalls },
+
+ { "no-workaround-a0-b-retw", no_argument, NULL,
+ option_no_workaround_a0_b_retw },
+ { "workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw },
+
+ { "no-workaround-b-j-loop-end", no_argument, NULL,
+ option_no_workaround_b_j_loop_end },
+ { "workaround-b-j-loop-end", no_argument, NULL,
+ option_workaround_b_j_loop_end },
+
+ { "no-workaround-short-loops", no_argument, NULL,
+ option_no_workaround_short_loop },
+ { "workaround-short-loops", no_argument, NULL,
+ option_workaround_short_loop },
+
+ { "no-workaround-all-short-loops", no_argument, NULL,
+ option_no_workaround_all_short_loops },
+ { "workaround-all-short-loop", no_argument, NULL,
+ option_workaround_all_short_loops },
+
+ { "prefer-l32r", no_argument, NULL, option_prefer_l32r },
+ { "prefer-const16", no_argument, NULL, option_prefer_const16 },
+
+ { "no-workarounds", no_argument, NULL, option_no_workarounds },
+
+ { "no-workaround-close-loop-end", no_argument, NULL,
+ option_no_workaround_close_loop_end },
+ { "workaround-close-loop-end", no_argument, NULL,
+ option_workaround_close_loop_end },
+
+ { "rename-section", required_argument, NULL, option_rename_section_name },
+
+ { "link-relax", no_argument, NULL, option_link_relax },
+ { "no-link-relax", no_argument, NULL, option_no_link_relax },
+
+ { "target-hardware", required_argument, NULL, option_target_hardware },
+
+ { "trampolines", no_argument, NULL, option_trampolines },
+ { "no-trampolines", no_argument, NULL, option_no_trampolines },
+
+ { NULL, no_argument, NULL, 0 }
+};
+
+size_t md_longopts_size = sizeof md_longopts;
+
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case option_density:
+ as_warn (_("--density option is ignored"));
+ return 1;
+ case option_no_density:
+ as_warn (_("--no-density option is ignored"));
+ return 1;
+ case option_link_relax:
+ linkrelax = 1;
+ return 1;
+ case option_no_link_relax:
+ linkrelax = 0;
+ return 1;
+ case option_flix:
+ produce_flix = FLIX_ALL;
+ return 1;
+ case option_no_generate_flix:
+ produce_flix = FLIX_NO_GENERATE;
+ return 1;
+ case option_no_flix:
+ produce_flix = FLIX_NONE;
+ return 1;
+ case option_generics:
+ as_warn (_("--generics is deprecated; use --transform instead"));
+ return md_parse_option (option_transform, arg);
+ case option_no_generics:
+ as_warn (_("--no-generics is deprecated; use --no-transform instead"));
+ return md_parse_option (option_no_transform, arg);
+ case option_relax:
+ as_warn (_("--relax is deprecated; use --transform instead"));
+ return md_parse_option (option_transform, arg);
+ case option_no_relax:
+ as_warn (_("--no-relax is deprecated; use --no-transform instead"));
+ return md_parse_option (option_no_transform, arg);
+ case option_longcalls:
+ directive_state[directive_longcalls] = TRUE;
+ return 1;
+ case option_no_longcalls:
+ directive_state[directive_longcalls] = FALSE;
+ return 1;
+ case option_text_section_literals:
+ use_literal_section = FALSE;
+ return 1;
+ case option_no_text_section_literals:
+ use_literal_section = TRUE;
+ return 1;
+ case option_absolute_literals:
+ if (!absolute_literals_supported)
+ {
+ as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
+ return 0;
+ }
+ directive_state[directive_absolute_literals] = TRUE;
+ return 1;
+ case option_no_absolute_literals:
+ directive_state[directive_absolute_literals] = FALSE;
+ return 1;
+
+ case option_workaround_a0_b_retw:
+ workaround_a0_b_retw = TRUE;
+ return 1;
+ case option_no_workaround_a0_b_retw:
+ workaround_a0_b_retw = FALSE;
+ return 1;
+ case option_workaround_b_j_loop_end:
+ workaround_b_j_loop_end = TRUE;
+ return 1;
+ case option_no_workaround_b_j_loop_end:
+ workaround_b_j_loop_end = FALSE;
+ return 1;
+
+ case option_workaround_short_loop:
+ workaround_short_loop = TRUE;
+ return 1;
+ case option_no_workaround_short_loop:
+ workaround_short_loop = FALSE;
+ return 1;
+
+ case option_workaround_all_short_loops:
+ workaround_all_short_loops = TRUE;
+ return 1;
+ case option_no_workaround_all_short_loops:
+ workaround_all_short_loops = FALSE;
+ return 1;
+
+ case option_workaround_close_loop_end:
+ workaround_close_loop_end = TRUE;
+ return 1;
+ case option_no_workaround_close_loop_end:
+ workaround_close_loop_end = FALSE;
+ return 1;
+
+ case option_no_workarounds:
+ workaround_a0_b_retw = FALSE;
+ workaround_b_j_loop_end = FALSE;
+ workaround_short_loop = FALSE;
+ workaround_all_short_loops = FALSE;
+ workaround_close_loop_end = FALSE;
+ return 1;
+
+ case option_align_targets:
+ align_targets = TRUE;
+ return 1;
+ case option_no_align_targets:
+ align_targets = FALSE;
+ return 1;
+
+ case option_warn_unaligned_targets:
+ warn_unaligned_branch_targets = TRUE;
+ return 1;
+
+ case option_rename_section_name:
+ build_section_rename (arg);
+ return 1;
+
+ case 'Q':
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
+ return 1;
+
+ case option_prefer_l32r:
+ if (prefer_const16)
+ as_fatal (_("prefer-l32r conflicts with prefer-const16"));
+ prefer_l32r = 1;
+ return 1;
+
+ case option_prefer_const16:
+ if (prefer_l32r)
+ as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
+ prefer_const16 = 1;
+ return 1;
+
+ case option_target_hardware:
+ {
+ int earliest, latest = 0;
+ if (*arg == 0 || *arg == '-')
+ as_fatal (_("invalid target hardware version"));
+
+ earliest = strtol (arg, &arg, 0);
+
+ if (*arg == 0)
+ latest = earliest;
+ else if (*arg == '-')
+ {
+ if (*++arg == 0)
+ as_fatal (_("invalid target hardware version"));
+ latest = strtol (arg, &arg, 0);
+ }
+ if (*arg != 0)
+ as_fatal (_("invalid target hardware version"));
+
+ xtensa_setup_hw_workarounds (earliest, latest);
+ return 1;
+ }
+
+ case option_transform:
+ /* This option has no affect other than to use the defaults,
+ which are already set. */
+ return 1;
+
+ case option_no_transform:
+ /* This option turns off all transformations of any kind.
+ However, because we want to preserve the state of other
+ directives, we only change its own field. Thus, before
+ you perform any transformation, always check if transform
+ is available. If you use the functions we provide for this
+ purpose, you will be ok. */
+ directive_state[directive_transform] = FALSE;
+ return 1;
+
+ case option_trampolines:
+ use_trampolines = TRUE;
+ return 1;
+
+ case option_no_trampolines:
+ use_trampolines = FALSE;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+void
+md_show_usage (FILE *stream)
+{
+ fputs ("\n\
+Xtensa options:\n\
+ --[no-]text-section-literals\n\
+ [Do not] put literals in the text section\n\
+ --[no-]absolute-literals\n\
+ [Do not] default to use non-PC-relative literals\n\
+ --[no-]target-align [Do not] try to align branch targets\n\
+ --[no-]longcalls [Do not] emit 32-bit call sequences\n\
+ --[no-]transform [Do not] transform instructions\n\
+ --flix both allow hand-written and generate flix bundles\n\
+ --no-generate-flix allow hand-written but do not generate\n\
+ flix bundles\n\
+ --no-allow-flix neither allow hand-written nor generate\n\
+ flix bundles\n\
+ --rename-section old=new Rename section 'old' to 'new'\n\
+ --[no-]trampolines [Do not] generate trampolines (jumps to jumps)\n\
+ when jumps do not reach their targets\n", stream);
+}
+
+
+/* Functions related to the list of current label symbols. */
+
+static void
+xtensa_add_insn_label (symbolS *sym)
+{
+ sym_list *l;
+
+ if (!free_insn_labels)
+ l = (sym_list *) xmalloc (sizeof (sym_list));
+ else
+ {
+ l = free_insn_labels;
+ free_insn_labels = l->next;
+ }
+
+ l->sym = sym;
+ l->next = insn_labels;
+ insn_labels = l;
+}
+
+
+static void
+xtensa_clear_insn_labels (void)
+{
+ sym_list **pl;
+
+ for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+ ;
+ *pl = insn_labels;
+ insn_labels = NULL;
+}
+
+
+static void
+xtensa_move_labels (fragS *new_frag, valueT new_offset)
+{
+ sym_list *lit;
+
+ for (lit = insn_labels; lit; lit = lit->next)
+ {
+ symbolS *lit_sym = lit->sym;
+ S_SET_VALUE (lit_sym, new_offset);
+ symbol_set_frag (lit_sym, new_frag);
+ }
+}
+
+
+/* Directive data and functions. */
+
+typedef struct state_stackS_struct
+{
+ directiveE directive;
+ bfd_boolean negated;
+ bfd_boolean old_state;
+ const char *file;
+ unsigned int line;
+ const void *datum;
+ struct state_stackS_struct *prev;
+} state_stackS;
+
+state_stackS *directive_state_stack;
+
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0). */
+ { "literal_position", xtensa_literal_position, 0 },
+ { "frame", s_ignore, 0 }, /* Formerly used for STABS debugging. */
+ { "long", xtensa_elf_cons, 4 },
+ { "word", xtensa_elf_cons, 4 },
+ { "4byte", xtensa_elf_cons, 4 },
+ { "short", xtensa_elf_cons, 2 },
+ { "2byte", xtensa_elf_cons, 2 },
+ { "sleb128", xtensa_leb128, 1},
+ { "uleb128", xtensa_leb128, 0},
+ { "begin", xtensa_begin_directive, 0 },
+ { "end", xtensa_end_directive, 0 },
+ { "literal", xtensa_literal_pseudo, 0 },
+ { "frequency", xtensa_frequency_pseudo, 0 },
+ { NULL, 0, 0 },
+};
+
+
+static bfd_boolean
+use_transform (void)
+{
+ /* After md_end, you should be checking frag by frag, rather
+ than state directives. */
+ gas_assert (!past_xtensa_end);
+ return directive_state[directive_transform];
+}
+
+
+static bfd_boolean
+do_align_targets (void)
+{
+ /* Do not use this function after md_end; just look at align_targets
+ instead. There is no target-align directive, so alignment is either
+ enabled for all frags or not done at all. */
+ gas_assert (!past_xtensa_end);
+ return align_targets && use_transform ();
+}
+
+
+static void
+directive_push (directiveE directive, bfd_boolean negated, const void *datum)
+{
+ char *file;
+ unsigned int line;
+ state_stackS *stack = (state_stackS *) xmalloc (sizeof (state_stackS));
+
+ as_where (&file, &line);
+
+ stack->directive = directive;
+ stack->negated = negated;
+ stack->old_state = directive_state[directive];
+ stack->file = file;
+ stack->line = line;
+ stack->datum = datum;
+ stack->prev = directive_state_stack;
+ directive_state_stack = stack;
+
+ directive_state[directive] = !negated;
+}
+
+
+static void
+directive_pop (directiveE *directive,
+ bfd_boolean *negated,
+ const char **file,
+ unsigned int *line,
+ const void **datum)
+{
+ state_stackS *top = directive_state_stack;
+
+ if (!directive_state_stack)
+ {
+ as_bad (_("unmatched end directive"));
+ *directive = directive_none;
+ return;
+ }
+
+ directive_state[directive_state_stack->directive] = top->old_state;
+ *directive = top->directive;
+ *negated = top->negated;
+ *file = top->file;
+ *line = top->line;
+ *datum = top->datum;
+ directive_state_stack = top->prev;
+ free (top);
+}
+
+
+static void
+directive_balance (void)
+{
+ while (directive_state_stack)
+ {
+ directiveE directive;
+ bfd_boolean negated;
+ const char *file;
+ unsigned int line;
+ const void *datum;
+
+ directive_pop (&directive, &negated, &file, &line, &datum);
+ as_warn_where ((char *) file, line,
+ _(".begin directive with no matching .end directive"));
+ }
+}
+
+
+static bfd_boolean
+inside_directive (directiveE dir)
+{
+ state_stackS *top = directive_state_stack;
+
+ while (top && top->directive != dir)
+ top = top->prev;
+
+ return (top != NULL);
+}
+
+
+static void
+get_directive (directiveE *directive, bfd_boolean *negated)
+{
+ int len;
+ unsigned i;
+ char *directive_string;
+
+ if (strncmp (input_line_pointer, "no-", 3) != 0)
+ *negated = FALSE;
+ else
+ {
+ *negated = TRUE;
+ input_line_pointer += 3;
+ }
+
+ len = strspn (input_line_pointer,
+ "abcdefghijklmnopqrstuvwxyz_-/0123456789.");
+
+ /* This code is a hack to make .begin [no-][generics|relax] exactly
+ equivalent to .begin [no-]transform. We should remove it when
+ we stop accepting those options. */
+
+ if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
+ {
+ as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
+ directive_string = "transform";
+ }
+ else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
+ {
+ as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
+ directive_string = "transform";
+ }
+ else
+ directive_string = input_line_pointer;
+
+ for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
+ {
+ if (strncmp (directive_string, directive_info[i].name, len) == 0)
+ {
+ input_line_pointer += len;
+ *directive = (directiveE) i;
+ if (*negated && !directive_info[i].can_be_negated)
+ as_bad (_("directive %s cannot be negated"),
+ directive_info[i].name);
+ return;
+ }
+ }
+
+ as_bad (_("unknown directive"));
+ *directive = (directiveE) XTENSA_UNDEFINED;
+}
+
+
+static void
+xtensa_begin_directive (int ignore ATTRIBUTE_UNUSED)
+{
+ directiveE directive;
+ bfd_boolean negated;
+ emit_state *state;
+ lit_state *ls;
+
+ get_directive (&directive, &negated);
+ if (directive == (directiveE) XTENSA_UNDEFINED)
+ {
+ discard_rest_of_line ();
+ return;
+ }
+
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
+ switch (directive)
+ {
+ case directive_literal:
+ if (!inside_directive (directive_literal))
+ {
+ /* Previous labels go with whatever follows this directive, not with
+ the literal, so save them now. */
+ saved_insn_labels = insn_labels;
+ insn_labels = NULL;
+ }
+ as_warn (_(".begin literal is deprecated; use .literal instead"));
+ state = (emit_state *) xmalloc (sizeof (emit_state));
+ xtensa_switch_to_literal_fragment (state);
+ directive_push (directive_literal, negated, state);
+ break;
+
+ case directive_literal_prefix:
+ /* Have to flush pending output because a movi relaxed to an l32r
+ might produce a literal. */
+ md_flush_pending_output ();
+ /* Check to see if the current fragment is a literal
+ fragment. If it is, then this operation is not allowed. */
+ if (generating_literals)
+ {
+ as_bad (_("cannot set literal_prefix inside literal fragment"));
+ return;
+ }
+
+ /* Allocate the literal state for this section and push
+ onto the directive stack. */
+ ls = xmalloc (sizeof (lit_state));
+ gas_assert (ls);
+
+ *ls = default_lit_sections;
+ directive_push (directive_literal_prefix, negated, ls);
+
+ /* Process the new prefix. */
+ xtensa_literal_prefix ();
+ break;
+
+ case directive_freeregs:
+ /* This information is currently unused, but we'll accept the statement
+ and just discard the rest of the line. This won't check the syntax,
+ but it will accept every correct freeregs directive. */
+ input_line_pointer += strcspn (input_line_pointer, "\n");
+ directive_push (directive_freeregs, negated, 0);
+ break;
+
+ case directive_schedule:
+ md_flush_pending_output ();
+ frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ directive_push (directive_schedule, negated, 0);
+ xtensa_set_frag_assembly_state (frag_now);
+ break;
+
+ case directive_density:
+ as_warn (_(".begin [no-]density is ignored"));
+ break;
+
+ case directive_absolute_literals:
+ md_flush_pending_output ();
+ if (!absolute_literals_supported && !negated)
+ {
+ as_warn (_("Xtensa absolute literals option not supported; ignored"));
+ break;
+ }
+ xtensa_set_frag_assembly_state (frag_now);
+ directive_push (directive, negated, 0);
+ break;
+
+ default:
+ md_flush_pending_output ();
+ xtensa_set_frag_assembly_state (frag_now);
+ directive_push (directive, negated, 0);
+ break;
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+
+static void
+xtensa_end_directive (int ignore ATTRIBUTE_UNUSED)
+{
+ directiveE begin_directive, end_directive;
+ bfd_boolean begin_negated, end_negated;
+ const char *file;
+ unsigned int line;
+ emit_state *state;
+ emit_state **state_ptr;
+ lit_state *s;
+
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
+ get_directive (&end_directive, &end_negated);
+
+ md_flush_pending_output ();
+
+ switch ((int) end_directive)
+ {
+ case XTENSA_UNDEFINED:
+ discard_rest_of_line ();
+ return;
+
+ case (int) directive_density:
+ as_warn (_(".end [no-]density is ignored"));
+ demand_empty_rest_of_line ();
+ break;
+
+ case (int) directive_absolute_literals:
+ if (!absolute_literals_supported && !end_negated)
+ {
+ as_warn (_("Xtensa absolute literals option not supported; ignored"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ state_ptr = &state; /* use state_ptr to avoid type-punning warning */
+ directive_pop (&begin_directive, &begin_negated, &file, &line,
+ (const void **) state_ptr);
+
+ if (begin_directive != directive_none)
+ {
+ if (begin_directive != end_directive || begin_negated != end_negated)
+ {
+ as_bad (_("does not match begin %s%s at %s:%d"),
+ begin_negated ? "no-" : "",
+ directive_info[begin_directive].name, file, line);
+ }
+ else
+ {
+ switch (end_directive)
+ {
+ case directive_literal:
+ frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_restore_emit_state (state);
+ xtensa_set_frag_assembly_state (frag_now);
+ free (state);
+ if (!inside_directive (directive_literal))
+ {
+ /* Restore the list of current labels. */
+ xtensa_clear_insn_labels ();
+ insn_labels = saved_insn_labels;
+ }
+ break;
+
+ case directive_literal_prefix:
+ /* Restore the default collection sections from saved state. */
+ s = (lit_state *) state;
+ gas_assert (s);
+ default_lit_sections = *s;
+
+ /* Free the state storage. */
+ free (s->lit_prefix);
+ free (s);
+ break;
+
+ case directive_schedule:
+ case directive_freeregs:
+ break;
+
+ default:
+ xtensa_set_frag_assembly_state (frag_now);
+ break;
+ }
+ }
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Place an aligned literal fragment at the current location. */
+
+static void
+xtensa_literal_position (int ignore ATTRIBUTE_UNUSED)
+{
+ md_flush_pending_output ();
+
+ if (inside_directive (directive_literal))
+ as_warn (_(".literal_position inside literal directive; ignoring"));
+ xtensa_mark_literal_pool_location ();
+
+ demand_empty_rest_of_line ();
+ xtensa_clear_insn_labels ();
+}
+
+
+/* Support .literal label, expr, ... */
+
+static void
+xtensa_literal_pseudo (int ignored ATTRIBUTE_UNUSED)
+{
+ emit_state state;
+ char *p, *base_name;
+ char c;
+ segT dest_seg;
+
+ if (inside_directive (directive_literal))
+ {
+ as_bad (_(".literal not allowed inside .begin literal region"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ md_flush_pending_output ();
+
+ /* Previous labels go with whatever follows this directive, not with
+ the literal, so save them now. */
+ saved_insn_labels = insn_labels;
+ insn_labels = NULL;
+
+ /* If we are using text-section literals, then this is the right value... */
+ dest_seg = now_seg;
+
+ base_name = input_line_pointer;
+
+ xtensa_switch_to_literal_fragment (&state);
+
+ /* ...but if we aren't using text-section-literals, then we
+ need to put them in the section we just switched to. */
+ if (use_literal_section || directive_state[directive_absolute_literals])
+ dest_seg = now_seg;
+
+ /* FIXME, despite the previous comments, dest_seg is unused... */
+ (void) dest_seg;
+
+ /* All literals are aligned to four-byte boundaries. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+ c = get_symbol_end ();
+ /* Just after name is now '\0'. */
+ p = input_line_pointer;
+ *p = c;
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',' && *input_line_pointer != ':')
+ {
+ as_bad (_("expected comma or colon after symbol name; "
+ "rest of line ignored"));
+ ignore_rest_of_line ();
+ xtensa_restore_emit_state (&state);
+ return;
+ }
+ *p = 0;
+
+ colon (base_name);
+
+ *p = c;
+ input_line_pointer++; /* skip ',' or ':' */
+
+ xtensa_elf_cons (4);
+
+ xtensa_restore_emit_state (&state);
+
+ /* Restore the list of current labels. */
+ xtensa_clear_insn_labels ();
+ insn_labels = saved_insn_labels;
+}
+
+
+static void
+xtensa_literal_prefix (void)
+{
+ char *name;
+ int len;
+
+ /* Parse the new prefix from the input_line_pointer. */
+ SKIP_WHITESPACE ();
+ len = strspn (input_line_pointer,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz_/0123456789.$");
+
+ /* Get a null-terminated copy of the name. */
+ name = xmalloc (len + 1);
+ gas_assert (name);
+ strncpy (name, input_line_pointer, len);
+ name[len] = 0;
+
+ /* Skip the name in the input line. */
+ input_line_pointer += len;
+
+ default_lit_sections.lit_prefix = name;
+
+ /* Clear cached literal sections, since the prefix has changed. */
+ default_lit_sections.lit_seg = NULL;
+ default_lit_sections.lit4_seg = NULL;
+}
+
+
+/* Support ".frequency branch_target_frequency fall_through_frequency". */
+
+static void
+xtensa_frequency_pseudo (int ignored ATTRIBUTE_UNUSED)
+{
+ float fall_through_f, target_f;
+
+ fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
+ if (fall_through_f < 0)
+ {
+ as_bad (_("fall through frequency must be greater than 0"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ target_f = (float) strtod (input_line_pointer, &input_line_pointer);
+ if (target_f < 0)
+ {
+ as_bad (_("branch target frequency must be greater than 0"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ set_subseg_freq (now_seg, now_subseg, target_f + fall_through_f, target_f);
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Like normal .long/.short/.word, except support @plt, etc.
+ Clobbers input_line_pointer, checks end-of-line. */
+
+static void
+xtensa_elf_cons (int nbytes)
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ md_flush_pending_output ();
+
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
+ != BFD_RELOC_NONE))
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput, reloc);
+
+ if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
+ as_bad (_("unsupported relocation"));
+ else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
+ || (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
+ as_bad (_("opcode-specific %s relocation used outside "
+ "an instruction"), reloc_howto->name);
+ else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
+ as_bad (_("%s relocations do not fit in %d bytes"),
+ reloc_howto->name, nbytes);
+ else if (reloc == BFD_RELOC_XTENSA_TLS_FUNC
+ || reloc == BFD_RELOC_XTENSA_TLS_ARG
+ || reloc == BFD_RELOC_XTENSA_TLS_CALL)
+ as_bad (_("invalid use of %s relocation"), reloc_howto->name);
+ else
+ {
+ char *p = frag_more ((int) nbytes);
+ xtensa_set_frag_assembly_state (frag_now);
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ nbytes, &exp, reloc_howto->pc_relative, reloc);
+ }
+ }
+ else
+ {
+ xtensa_set_frag_assembly_state (frag_now);
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+static bfd_boolean is_leb128_expr;
+
+static void
+xtensa_leb128 (int sign)
+{
+ is_leb128_expr = TRUE;
+ s_leb128 (sign);
+ is_leb128_expr = FALSE;
+}
+
+
+/* Parsing and Idiom Translation. */
+
+/* Parse @plt, etc. and return the desired relocation. */
+static bfd_reloc_code_real_type
+xtensa_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ struct suffix_reloc_map *ptr;
+
+ if (*str++ != '@')
+ return BFD_RELOC_NONE;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &suffix_relocs[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->suffix[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->suffix, ptr->length) == 0)
+ {
+ /* Now check for "identifier@suffix+constant". */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+
+ *str_p = str;
+ return ptr->reloc;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+
+/* Find the matching operator type. */
+static unsigned char
+map_suffix_reloc_to_operator (bfd_reloc_code_real_type reloc)
+{
+ struct suffix_reloc_map *sfx;
+ unsigned char operator = (unsigned char) -1;
+
+ for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
+ {
+ if (sfx->reloc == reloc)
+ {
+ operator = sfx->operator;
+ break;
+ }
+ }
+ gas_assert (operator != (unsigned char) -1);
+ return operator;
+}
+
+
+/* Find the matching reloc type. */
+static bfd_reloc_code_real_type
+map_operator_to_reloc (unsigned char operator, bfd_boolean is_literal)
+{
+ struct suffix_reloc_map *sfx;
+ bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
+
+ for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
+ {
+ if (sfx->operator == operator)
+ {
+ reloc = sfx->reloc;
+ break;
+ }
+ }
+
+ if (is_literal)
+ {
+ if (reloc == BFD_RELOC_XTENSA_TLS_FUNC)
+ return BFD_RELOC_XTENSA_TLSDESC_FN;
+ else if (reloc == BFD_RELOC_XTENSA_TLS_ARG)
+ return BFD_RELOC_XTENSA_TLSDESC_ARG;
+ }
+
+ if (reloc == BFD_RELOC_UNUSED)
+ return BFD_RELOC_32;
+
+ return reloc;
+}
+
+
+static const char *
+expression_end (const char *name)
+{
+ while (1)
+ {
+ switch (*name)
+ {
+ case '}':
+ case ';':
+ case '\0':
+ case ',':
+ case ':':
+ return name;
+ case ' ':
+ case '\t':
+ ++name;
+ continue;
+ default:
+ return 0;
+ }
+ }
+}
+
+
+#define ERROR_REG_NUM ((unsigned) -1)
+
+static unsigned
+tc_get_register (const char *prefix)
+{
+ unsigned reg;
+ const char *next_expr;
+ const char *old_line_pointer;
+
+ SKIP_WHITESPACE ();
+ old_line_pointer = input_line_pointer;
+
+ if (*input_line_pointer == '$')
+ ++input_line_pointer;
+
+ /* Accept "sp" as a synonym for "a1". */
+ if (input_line_pointer[0] == 's' && input_line_pointer[1] == 'p'
+ && expression_end (input_line_pointer + 2))
+ {
+ input_line_pointer += 2;
+ return 1; /* AR[1] */
+ }
+
+ while (*input_line_pointer++ == *prefix++)
+ ;
+ --input_line_pointer;
+ --prefix;
+
+ if (*prefix)
+ {
+ as_bad (_("bad register name: %s"), old_line_pointer);
+ return ERROR_REG_NUM;
+ }
+
+ if (!ISDIGIT ((unsigned char) *input_line_pointer))
+ {
+ as_bad (_("bad register number: %s"), input_line_pointer);
+ return ERROR_REG_NUM;
+ }
+
+ reg = 0;
+
+ while (ISDIGIT ((int) *input_line_pointer))
+ reg = reg * 10 + *input_line_pointer++ - '0';
+
+ if (!(next_expr = expression_end (input_line_pointer)))
+ {
+ as_bad (_("bad register name: %s"), old_line_pointer);
+ return ERROR_REG_NUM;
+ }
+
+ input_line_pointer = (char *) next_expr;
+
+ return reg;
+}
+
+
+static void
+expression_maybe_register (xtensa_opcode opc, int opnd, expressionS *tok)
+{
+ xtensa_isa isa = xtensa_default_isa;
+
+ /* Check if this is an immediate operand. */
+ if (xtensa_operand_is_register (isa, opc, opnd) == 0)
+ {
+ bfd_reloc_code_real_type reloc;
+ segT t = expression (tok);
+
+ if (t == absolute_section
+ && xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
+ {
+ gas_assert (tok->X_op == O_constant);
+ tok->X_op = O_symbol;
+ tok->X_add_symbol = &abs_symbol;
+ }
+
+ if ((tok->X_op == O_constant || tok->X_op == O_symbol)
+ && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
+ != BFD_RELOC_NONE))
+ {
+ switch (reloc)
+ {
+ case BFD_RELOC_LO16:
+ if (tok->X_op == O_constant)
+ {
+ tok->X_add_number &= 0xffff;
+ return;
+ }
+ break;
+ case BFD_RELOC_HI16:
+ if (tok->X_op == O_constant)
+ {
+ tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
+ return;
+ }
+ break;
+ case BFD_RELOC_UNUSED:
+ as_bad (_("unsupported relocation"));
+ return;
+ case BFD_RELOC_32_PCREL:
+ as_bad (_("pcrel relocation not allowed in an instruction"));
+ return;
+ default:
+ break;
+ }
+ tok->X_op = map_suffix_reloc_to_operator (reloc);
+ }
+ }
+ else
+ {
+ xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
+ unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
+
+ if (reg != ERROR_REG_NUM) /* Already errored */
+ {
+ uint32 buf = reg;
+ if (xtensa_operand_encode (isa, opc, opnd, &buf))
+ as_bad (_("register number out of range"));
+ }
+
+ tok->X_op = O_register;
+ tok->X_add_symbol = 0;
+ tok->X_add_number = reg;
+ }
+}
+
+
+/* Split up the arguments for an opcode or pseudo-op. */
+
+static int
+tokenize_arguments (char **args, char *str)
+{
+ char *old_input_line_pointer;
+ bfd_boolean saw_comma = FALSE;
+ bfd_boolean saw_arg = FALSE;
+ bfd_boolean saw_colon = FALSE;
+ int num_args = 0;
+ char *arg_end, *arg;
+ int arg_len;
+
+ /* Save and restore input_line_pointer around this function. */
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = str;
+
+ while (*input_line_pointer)
+ {
+ SKIP_WHITESPACE ();
+ switch (*input_line_pointer)
+ {
+ case '\0':
+ case '}':
+ goto fini;
+
+ case ':':
+ input_line_pointer++;
+ if (saw_comma || saw_colon || !saw_arg)
+ goto err;
+ saw_colon = TRUE;
+ break;
+
+ case ',':
+ input_line_pointer++;
+ if (saw_comma || saw_colon || !saw_arg)
+ goto err;
+ saw_comma = TRUE;
+ break;
+
+ default:
+ if (!saw_comma && !saw_colon && saw_arg)
+ goto err;
+
+ arg_end = input_line_pointer + 1;
+ while (!expression_end (arg_end))
+ arg_end += 1;
+
+ arg_len = arg_end - input_line_pointer;
+ arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
+ args[num_args] = arg;
+
+ if (saw_colon)
+ *arg++ = ':';
+ strncpy (arg, input_line_pointer, arg_len);
+ arg[arg_len] = '\0';
+
+ input_line_pointer = arg_end;
+ num_args += 1;
+ saw_comma = FALSE;
+ saw_colon = FALSE;
+ saw_arg = TRUE;
+ break;
+ }
+ }
+
+fini:
+ if (saw_comma || saw_colon)
+ goto err;
+ input_line_pointer = old_input_line_pointer;
+ return num_args;
+
+err:
+ if (saw_comma)
+ as_bad (_("extra comma"));
+ else if (saw_colon)
+ as_bad (_("extra colon"));
+ else if (!saw_arg)
+ as_bad (_("missing argument"));
+ else
+ as_bad (_("missing comma or colon"));
+ input_line_pointer = old_input_line_pointer;
+ return -1;
+}
+
+
+/* Parse the arguments to an opcode. Return TRUE on error. */
+
+static bfd_boolean
+parse_arguments (TInsn *insn, int num_args, char **arg_strings)
+{
+ expressionS *tok, *last_tok;
+ xtensa_opcode opcode = insn->opcode;
+ bfd_boolean had_error = TRUE;
+ xtensa_isa isa = xtensa_default_isa;
+ int n, num_regs = 0;
+ int opcode_operand_count;
+ int opnd_cnt, last_opnd_cnt;
+ unsigned int next_reg = 0;
+ char *old_input_line_pointer;
+
+ if (insn->insn_type == ITYPE_LITERAL)
+ opcode_operand_count = 1;
+ else
+ opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
+
+ tok = insn->tok;
+ memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
+
+ /* Save and restore input_line_pointer around this function. */
+ old_input_line_pointer = input_line_pointer;
+
+ last_tok = 0;
+ last_opnd_cnt = -1;
+ opnd_cnt = 0;
+
+ /* Skip invisible operands. */
+ while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
+ {
+ opnd_cnt += 1;
+ tok++;
+ }
+
+ for (n = 0; n < num_args; n++)
+ {
+ input_line_pointer = arg_strings[n];
+ if (*input_line_pointer == ':')
+ {
+ xtensa_regfile opnd_rf;
+ input_line_pointer++;
+ if (num_regs == 0)
+ goto err;
+ gas_assert (opnd_cnt > 0);
+ num_regs--;
+ opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
+ if (next_reg
+ != tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
+ as_warn (_("incorrect register number, ignoring"));
+ next_reg++;
+ }
+ else
+ {
+ if (opnd_cnt >= opcode_operand_count)
+ {
+ as_warn (_("too many arguments"));
+ goto err;
+ }
+ gas_assert (opnd_cnt < MAX_INSN_ARGS);
+
+ expression_maybe_register (opcode, opnd_cnt, tok);
+ next_reg = tok->X_add_number + 1;
+
+ if (tok->X_op == O_illegal || tok->X_op == O_absent)
+ goto err;
+ if (xtensa_operand_is_register (isa, opcode, opnd_cnt) == 1)
+ {
+ num_regs = xtensa_operand_num_regs (isa, opcode, opnd_cnt) - 1;
+ /* minus 1 because we are seeing one right now */
+ }
+ else
+ num_regs = 0;
+
+ last_tok = tok;
+ last_opnd_cnt = opnd_cnt;
+ demand_empty_rest_of_line ();
+
+ do
+ {
+ opnd_cnt += 1;
+ tok++;
+ }
+ while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
+ }
+ }
+
+ if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
+ goto err;
+
+ insn->ntok = tok - insn->tok;
+ had_error = FALSE;
+
+ err:
+ input_line_pointer = old_input_line_pointer;
+ return had_error;
+}
+
+
+static int
+get_invisible_operands (TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_format fmt;
+ xtensa_opcode opc = insn->opcode;
+ int slot, opnd, fmt_found;
+ unsigned val;
+
+ if (!slotbuf)
+ slotbuf = xtensa_insnbuf_alloc (isa);
+
+ /* Find format/slot where this can be encoded. */
+ fmt_found = 0;
+ slot = 0;
+ for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+ {
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
+ {
+ fmt_found = 1;
+ break;
+ }
+ }
+ if (fmt_found) break;
+ }
+
+ if (!fmt_found)
+ {
+ as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
+ return -1;
+ }
+
+ /* First encode all the visible operands
+ (to deal with shared field operands). */
+ for (opnd = 0; opnd < insn->ntok; opnd++)
+ {
+ if (xtensa_operand_is_visible (isa, opc, opnd) == 1
+ && (insn->tok[opnd].X_op == O_register
+ || insn->tok[opnd].X_op == O_constant))
+ {
+ val = insn->tok[opnd].X_add_number;
+ xtensa_operand_encode (isa, opc, opnd, &val);
+ xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
+ }
+ }
+
+ /* Then pull out the values for the invisible ones. */
+ for (opnd = 0; opnd < insn->ntok; opnd++)
+ {
+ if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
+ {
+ xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
+ xtensa_operand_decode (isa, opc, opnd, &val);
+ insn->tok[opnd].X_add_number = val;
+ if (xtensa_operand_is_register (isa, opc, opnd) == 1)
+ insn->tok[opnd].X_op = O_register;
+ else
+ insn->tok[opnd].X_op = O_constant;
+ }
+ }
+
+ return 0;
+}
+
+
+static void
+xg_reverse_shift_count (char **cnt_argp)
+{
+ char *cnt_arg, *new_arg;
+ cnt_arg = *cnt_argp;
+
+ /* replace the argument with "31-(argument)" */
+ new_arg = (char *) xmalloc (strlen (cnt_arg) + 6);
+ sprintf (new_arg, "31-(%s)", cnt_arg);
+
+ free (cnt_arg);
+ *cnt_argp = new_arg;
+}
+
+
+/* If "arg" is a constant expression, return non-zero with the value
+ in *valp. */
+
+static int
+xg_arg_is_constant (char *arg, offsetT *valp)
+{
+ expressionS exp;
+ char *save_ptr = input_line_pointer;
+
+ input_line_pointer = arg;
+ expression (&exp);
+ input_line_pointer = save_ptr;
+
+ if (exp.X_op == O_constant)
+ {
+ *valp = exp.X_add_number;
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void
+xg_replace_opname (char **popname, char *newop)
+{
+ free (*popname);
+ *popname = (char *) xmalloc (strlen (newop) + 1);
+ strcpy (*popname, newop);
+}
+
+
+static int
+xg_check_num_args (int *pnum_args,
+ int expected_num,
+ char *opname,
+ char **arg_strings)
+{
+ int num_args = *pnum_args;
+
+ if (num_args < expected_num)
+ {
+ as_bad (_("not enough operands (%d) for '%s'; expected %d"),
+ num_args, opname, expected_num);
+ return -1;
+ }
+
+ if (num_args > expected_num)
+ {
+ as_warn (_("too many operands (%d) for '%s'; expected %d"),
+ num_args, opname, expected_num);
+ while (num_args-- > expected_num)
+ {
+ free (arg_strings[num_args]);
+ arg_strings[num_args] = 0;
+ }
+ *pnum_args = expected_num;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* If the register is not specified as part of the opcode,
+ then get it from the operand and move it to the opcode. */
+
+static int
+xg_translate_sysreg_op (char **popname, int *pnum_args, char **arg_strings)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_sysreg sr;
+ char *opname, *new_opname;
+ const char *sr_name;
+ int is_user, is_write;
+
+ opname = *popname;
+ if (*opname == '_')
+ opname += 1;
+ is_user = (opname[1] == 'u');
+ is_write = (opname[0] == 'w');
+
+ /* Opname == [rw]ur or [rwx]sr... */
+
+ if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
+ return -1;
+
+ /* Check if the argument is a symbolic register name. */
+ sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
+ /* Handle WSR to "INTSET" as a special case. */
+ if (sr == XTENSA_UNDEFINED && is_write && !is_user
+ && !strcasecmp (arg_strings[1], "intset"))
+ sr = xtensa_sysreg_lookup_name (isa, "interrupt");
+ if (sr == XTENSA_UNDEFINED
+ || (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
+ {
+ /* Maybe it's a register number.... */
+ offsetT val;
+ if (!xg_arg_is_constant (arg_strings[1], &val))
+ {
+ as_bad (_("invalid register '%s' for '%s' instruction"),
+ arg_strings[1], opname);
+ return -1;
+ }
+ sr = xtensa_sysreg_lookup (isa, val, is_user);
+ if (sr == XTENSA_UNDEFINED)
+ {
+ as_bad (_("invalid register number (%ld) for '%s' instruction"),
+ (long) val, opname);
+ return -1;
+ }
+ }
+
+ /* Remove the last argument, which is now part of the opcode. */
+ free (arg_strings[1]);
+ arg_strings[1] = 0;
+ *pnum_args = 1;
+
+ /* Translate the opcode. */
+ sr_name = xtensa_sysreg_name (isa, sr);
+ /* Another special case for "WSR.INTSET".... */
+ if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
+ sr_name = "intset";
+ new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ sprintf (new_opname, "%s.%s", *popname, sr_name);
+ free (*popname);
+ *popname = new_opname;
+
+ return 0;
+}
+
+
+static int
+xtensa_translate_old_userreg_ops (char **popname)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_sysreg sr;
+ char *opname, *new_opname;
+ const char *sr_name;
+ bfd_boolean has_underbar = FALSE;
+
+ opname = *popname;
+ if (opname[0] == '_')
+ {
+ has_underbar = TRUE;
+ opname += 1;
+ }
+
+ sr = xtensa_sysreg_lookup_name (isa, opname + 1);
+ if (sr != XTENSA_UNDEFINED)
+ {
+ /* The new default name ("nnn") is different from the old default
+ name ("URnnn"). The old default is handled below, and we don't
+ want to recognize [RW]nnn, so do nothing if the name is the (new)
+ default. */
+ static char namebuf[10];
+ sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
+ if (strcmp (namebuf, opname + 1) == 0)
+ return 0;
+ }
+ else
+ {
+ offsetT val;
+ char *end;
+
+ /* Only continue if the reg name is "URnnn". */
+ if (opname[1] != 'u' || opname[2] != 'r')
+ return 0;
+ val = strtoul (opname + 3, &end, 10);
+ if (*end != '\0')
+ return 0;
+
+ sr = xtensa_sysreg_lookup (isa, val, 1);
+ if (sr == XTENSA_UNDEFINED)
+ {
+ as_bad (_("invalid register number (%ld) for '%s'"),
+ (long) val, opname);
+ return -1;
+ }
+ }
+
+ /* Translate the opcode. */
+ sr_name = xtensa_sysreg_name (isa, sr);
+ new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
+ opname[0], sr_name);
+ free (*popname);
+ *popname = new_opname;
+
+ return 0;
+}
+
+
+static int
+xtensa_translate_zero_immed (char *old_op,
+ char *new_op,
+ char **popname,
+ int *pnum_args,
+ char **arg_strings)
+{
+ char *opname;
+ offsetT val;
+
+ opname = *popname;
+ gas_assert (opname[0] != '_');
+
+ if (strcmp (opname, old_op) != 0)
+ return 0;
+
+ if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
+ return -1;
+ if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
+ {
+ xg_replace_opname (popname, new_op);
+ free (arg_strings[1]);
+ arg_strings[1] = arg_strings[2];
+ arg_strings[2] = 0;
+ *pnum_args = 2;
+ }
+
+ return 0;
+}
+
+
+/* If the instruction is an idiom (i.e., a built-in macro), translate it.
+ Returns non-zero if an error was found. */
+
+static int
+xg_translate_idioms (char **popname, int *pnum_args, char **arg_strings)
+{
+ char *opname = *popname;
+ bfd_boolean has_underbar = FALSE;
+
+ if (*opname == '_')
+ {
+ has_underbar = TRUE;
+ opname += 1;
+ }
+
+ if (strcmp (opname, "mov") == 0)
+ {
+ if (use_transform () && !has_underbar && density_supported)
+ xg_replace_opname (popname, "mov.n");
+ else
+ {
+ if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
+ return -1;
+ xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
+ arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
+ strcpy (arg_strings[2], arg_strings[1]);
+ *pnum_args = 3;
+ }
+ return 0;
+ }
+
+ if (strcmp (opname, "bbsi.l") == 0)
+ {
+ if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
+ return -1;
+ xg_replace_opname (popname, (has_underbar ? "_bbsi" : "bbsi"));
+ if (target_big_endian)
+ xg_reverse_shift_count (&arg_strings[1]);
+ return 0;
+ }
+
+ if (strcmp (opname, "bbci.l") == 0)
+ {
+ if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
+ return -1;
+ xg_replace_opname (popname, (has_underbar ? "_bbci" : "bbci"));
+ if (target_big_endian)
+ xg_reverse_shift_count (&arg_strings[1]);
+ return 0;
+ }
+
+ /* Don't do anything special with NOPs inside FLIX instructions. They
+ are handled elsewhere. Real NOP instructions are always available
+ in configurations with FLIX, so this should never be an issue but
+ check for it anyway. */
+ if (!cur_vinsn.inside_bundle && xtensa_nop_opcode == XTENSA_UNDEFINED
+ && strcmp (opname, "nop") == 0)
+ {
+ if (use_transform () && !has_underbar && density_supported)
+ xg_replace_opname (popname, "nop.n");
+ else
+ {
+ if (xg_check_num_args (pnum_args, 0, opname, arg_strings))
+ return -1;
+ xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
+ arg_strings[0] = (char *) xmalloc (3);
+ arg_strings[1] = (char *) xmalloc (3);
+ arg_strings[2] = (char *) xmalloc (3);
+ strcpy (arg_strings[0], "a1");
+ strcpy (arg_strings[1], "a1");
+ strcpy (arg_strings[2], "a1");
+ *pnum_args = 3;
+ }
+ return 0;
+ }
+
+ /* Recognize [RW]UR and [RWX]SR. */
+ if ((((opname[0] == 'r' || opname[0] == 'w')
+ && (opname[1] == 'u' || opname[1] == 's'))
+ || (opname[0] == 'x' && opname[1] == 's'))
+ && opname[2] == 'r'
+ && opname[3] == '\0')
+ return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
+
+ /* Backward compatibility for RUR and WUR: Recognize [RW]UR<nnn> and
+ [RW]<name> if <name> is the non-default name of a user register. */
+ if ((opname[0] == 'r' || opname[0] == 'w')
+ && xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
+ return xtensa_translate_old_userreg_ops (popname);
+
+ /* Relax branches that don't allow comparisons against an immediate value
+ of zero to the corresponding branches with implicit zero immediates. */
+ if (!has_underbar && use_transform ())
+ {
+ if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
+ pnum_args, arg_strings))
+ return -1;
+
+ if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
+ pnum_args, arg_strings))
+ return -1;
+
+ if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
+ pnum_args, arg_strings))
+ return -1;
+
+ if (xtensa_translate_zero_immed ("blti", "bltz", popname,
+ pnum_args, arg_strings))
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* Functions for dealing with the Xtensa ISA. */
+
+/* Currently the assembler only allows us to use a single target per
+ fragment. Because of this, only one operand for a given
+ instruction may be symbolic. If there is a PC-relative operand,
+ the last one is chosen. Otherwise, the result is the number of the
+ last immediate operand, and if there are none of those, we fail and
+ return -1. */
+
+static int
+get_relaxable_immed (xtensa_opcode opcode)
+{
+ int last_immed = -1;
+ int noperands, opi;
+
+ if (opcode == XTENSA_UNDEFINED)
+ return -1;
+
+ noperands = xtensa_opcode_num_operands (xtensa_default_isa, opcode);
+ for (opi = noperands - 1; opi >= 0; opi--)
+ {
+ if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
+ continue;
+ if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
+ return opi;
+ if (last_immed == -1
+ && xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
+ last_immed = opi;
+ }
+ return last_immed;
+}
+
+
+static xtensa_opcode
+get_opcode_from_buf (const char *buf, int slot)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt;
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, (const unsigned char *) buf, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return XTENSA_UNDEFINED;
+
+ if (slot >= xtensa_format_num_slots (isa, fmt))
+ return XTENSA_UNDEFINED;
+
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+}
+
+
+#ifdef TENSILICA_DEBUG
+
+/* For debugging, print out the mapping of opcode numbers to opcodes. */
+
+static void
+xtensa_print_insn_table (void)
+{
+ int num_opcodes, num_operands;
+ xtensa_opcode opcode;
+ xtensa_isa isa = xtensa_default_isa;
+
+ num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+ for (opcode = 0; opcode < num_opcodes; opcode++)
+ {
+ int opn;
+ fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
+ num_operands = xtensa_opcode_num_operands (isa, opcode);
+ for (opn = 0; opn < num_operands; opn++)
+ {
+ if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
+ continue;
+ if (xtensa_operand_is_register (isa, opcode, opn) == 1)
+ {
+ xtensa_regfile opnd_rf =
+ xtensa_operand_regfile (isa, opcode, opn);
+ fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
+ }
+ else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
+ fputs ("[lLr] ", stderr);
+ else
+ fputs ("i ", stderr);
+ }
+ fprintf (stderr, "\n");
+ }
+}
+
+
+static void
+print_vliw_insn (xtensa_insnbuf vbuf)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format f = xtensa_format_decode (isa, vbuf);
+ xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
+ int op;
+
+ fprintf (stderr, "format = %d\n", f);
+
+ for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
+ {
+ xtensa_opcode opcode;
+ const char *opname;
+ int operands;
+
+ xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
+ opcode = xtensa_opcode_decode (isa, f, op, sbuf);
+ opname = xtensa_opcode_name (isa, opcode);
+
+ fprintf (stderr, "op in slot %i is %s;\n", op, opname);
+ fprintf (stderr, " operands = ");
+ for (operands = 0;
+ operands < xtensa_opcode_num_operands (isa, opcode);
+ operands++)
+ {
+ unsigned int val;
+ if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
+ continue;
+ xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
+ xtensa_operand_decode (isa, opcode, operands, &val);
+ fprintf (stderr, "%d ", val);
+ }
+ fprintf (stderr, "\n");
+ }
+ xtensa_insnbuf_free (isa, sbuf);
+}
+
+#endif /* TENSILICA_DEBUG */
+
+
+static bfd_boolean
+is_direct_call_opcode (xtensa_opcode opcode)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int n, num_operands;
+
+ if (xtensa_opcode_is_call (isa, opcode) != 1)
+ return FALSE;
+
+ num_operands = xtensa_opcode_num_operands (isa, opcode);
+ for (n = 0; n < num_operands; n++)
+ {
+ if (xtensa_operand_is_register (isa, opcode, n) == 0
+ && xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* Convert from BFD relocation type code to slot and operand number.
+ Returns non-zero on failure. */
+
+static int
+decode_reloc (bfd_reloc_code_real_type reloc, int *slot, bfd_boolean *is_alt)
+{
+ if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
+ {
+ *slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
+ *is_alt = FALSE;
+ }
+ else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
+ {
+ *slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
+ *is_alt = TRUE;
+ }
+ else
+ return -1;
+
+ return 0;
+}
+
+
+/* Convert from slot number to BFD relocation type code for the
+ standard PC-relative relocations. Return BFD_RELOC_NONE on
+ failure. */
+
+static bfd_reloc_code_real_type
+encode_reloc (int slot)
+{
+ if (slot < 0 || slot > 14)
+ return BFD_RELOC_NONE;
+
+ return BFD_RELOC_XTENSA_SLOT0_OP + slot;
+}
+
+
+/* Convert from slot numbers to BFD relocation type code for the
+ "alternate" relocations. Return BFD_RELOC_NONE on failure. */
+
+static bfd_reloc_code_real_type
+encode_alt_reloc (int slot)
+{
+ if (slot < 0 || slot > 14)
+ return BFD_RELOC_NONE;
+
+ return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
+}
+
+
+static void
+xtensa_insnbuf_set_operand (xtensa_insnbuf slotbuf,
+ xtensa_format fmt,
+ int slot,
+ xtensa_opcode opcode,
+ int operand,
+ uint32 value,
+ const char *file,
+ unsigned int line)
+{
+ uint32 valbuf = value;
+
+ if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
+ {
+ if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
+ == 1)
+ as_bad_where ((char *) file, line,
+ _("operand %d of '%s' has out of range value '%u'"),
+ operand + 1,
+ xtensa_opcode_name (xtensa_default_isa, opcode),
+ value);
+ else
+ as_bad_where ((char *) file, line,
+ _("operand %d of '%s' has invalid value '%u'"),
+ operand + 1,
+ xtensa_opcode_name (xtensa_default_isa, opcode),
+ value);
+ return;
+ }
+
+ xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
+ slotbuf, valbuf);
+}
+
+
+static uint32
+xtensa_insnbuf_get_operand (xtensa_insnbuf slotbuf,
+ xtensa_format fmt,
+ int slot,
+ xtensa_opcode opcode,
+ int opnum)
+{
+ uint32 val = 0;
+ (void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
+ fmt, slot, slotbuf, &val);
+ (void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
+ return val;
+}
+
+
+/* Checks for rules from xtensa-relax tables. */
+
+/* The routine xg_instruction_matches_option_term must return TRUE
+ when a given option term is true. The meaning of all of the option
+ terms is given interpretation by this function. */
+
+static bfd_boolean
+xg_instruction_matches_option_term (TInsn *insn, const ReqOrOption *option)
+{
+ if (strcmp (option->option_name, "realnop") == 0
+ || strncmp (option->option_name, "IsaUse", 6) == 0)
+ {
+ /* These conditions were evaluated statically when building the
+ relaxation table. There's no need to reevaluate them now. */
+ return TRUE;
+ }
+ else if (strcmp (option->option_name, "FREEREG") == 0)
+ return insn->extra_arg.X_op == O_register;
+ else
+ {
+ as_fatal (_("internal error: unknown option name '%s'"),
+ option->option_name);
+ }
+}
+
+
+static bfd_boolean
+xg_instruction_matches_or_options (TInsn *insn,
+ const ReqOrOptionList *or_option)
+{
+ const ReqOrOption *option;
+ /* Must match each of the AND terms. */
+ for (option = or_option; option != NULL; option = option->next)
+ {
+ if (xg_instruction_matches_option_term (insn, option))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
+{
+ const ReqOption *req_options;
+ /* Must match each of the AND terms. */
+ for (req_options = options;
+ req_options != NULL;
+ req_options = req_options->next)
+ {
+ /* Must match one of the OR clauses. */
+ if (!xg_instruction_matches_or_options (insn,
+ req_options->or_option_terms))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/* Return the transition rule that matches or NULL if none matches. */
+
+static bfd_boolean
+xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
+{
+ PreconditionList *condition_l;
+
+ if (rule->opcode != insn->opcode)
+ return FALSE;
+
+ for (condition_l = rule->conditions;
+ condition_l != NULL;
+ condition_l = condition_l->next)
+ {
+ expressionS *exp1;
+ expressionS *exp2;
+ Precondition *cond = condition_l->precond;
+
+ switch (cond->typ)
+ {
+ case OP_CONSTANT:
+ /* The expression must be the constant. */
+ gas_assert (cond->op_num < insn->ntok);
+ exp1 = &insn->tok[cond->op_num];
+ if (expr_is_const (exp1))
+ {
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (get_expr_const (exp1) != cond->op_data)
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (get_expr_const (exp1) == cond->op_data)
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else if (expr_is_register (exp1))
+ {
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (get_expr_register (exp1) != cond->op_data)
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (get_expr_register (exp1) == cond->op_data)
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else
+ return FALSE;
+ break;
+
+ case OP_OPERAND:
+ gas_assert (cond->op_num < insn->ntok);
+ gas_assert (cond->op_data < insn->ntok);
+ exp1 = &insn->tok[cond->op_num];
+ exp2 = &insn->tok[cond->op_data];
+
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (!expr_is_equal (exp1, exp2))
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (expr_is_equal (exp1, exp2))
+ return FALSE;
+ break;
+ }
+ break;
+
+ case OP_LITERAL:
+ case OP_LABEL:
+ default:
+ return FALSE;
+ }
+ }
+ if (!xg_instruction_matches_options (insn, rule->options))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static int
+transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
+{
+ bfd_boolean a_greater = FALSE;
+ bfd_boolean b_greater = FALSE;
+
+ ReqOptionList *l_a = a->options;
+ ReqOptionList *l_b = b->options;
+
+ /* We only care if they both are the same except for
+ a const16 vs. an l32r. */
+
+ while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
+ {
+ ReqOrOptionList *l_or_a = l_a->or_option_terms;
+ ReqOrOptionList *l_or_b = l_b->or_option_terms;
+ while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
+ {
+ if (l_or_a->is_true != l_or_b->is_true)
+ return 0;
+ if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
+ {
+ /* This is the case we care about. */
+ if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
+ && strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
+ {
+ if (prefer_const16)
+ a_greater = TRUE;
+ else
+ b_greater = TRUE;
+ }
+ else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
+ && strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
+ {
+ if (prefer_const16)
+ b_greater = TRUE;
+ else
+ a_greater = TRUE;
+ }
+ else
+ return 0;
+ }
+ l_or_a = l_or_a->next;
+ l_or_b = l_or_b->next;
+ }
+ if (l_or_a || l_or_b)
+ return 0;
+
+ l_a = l_a->next;
+ l_b = l_b->next;
+ }
+ if (l_a || l_b)
+ return 0;
+
+ /* Incomparable if the substitution was used differently in two cases. */
+ if (a_greater && b_greater)
+ return 0;
+
+ if (b_greater)
+ return 1;
+ if (a_greater)
+ return -1;
+
+ return 0;
+}
+
+
+static TransitionRule *
+xg_instruction_match (TInsn *insn)
+{
+ TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
+ TransitionList *l;
+ gas_assert (insn->opcode < table->num_opcodes);
+
+ /* Walk through all of the possible transitions. */
+ for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+ {
+ TransitionRule *rule = l->rule;
+ if (xg_instruction_matches_rule (insn, rule))
+ return rule;
+ }
+ return NULL;
+}
+
+
+/* Various Other Internal Functions. */
+
+static bfd_boolean
+is_unique_insn_expansion (TransitionRule *r)
+{
+ if (!r->to_instr || r->to_instr->next != NULL)
+ return FALSE;
+ if (r->to_instr->typ != INSTR_INSTR)
+ return FALSE;
+ return TRUE;
+}
+
+
+/* Check if there is exactly one relaxation for INSN that converts it to
+ another instruction of equal or larger size. If so, and if TARG is
+ non-null, go ahead and generate the relaxed instruction into TARG. If
+ NARROW_ONLY is true, then only consider relaxations that widen a narrow
+ instruction, i.e., ignore relaxations that convert to an instruction of
+ equal size. In some contexts where this function is used, only
+ a single widening is allowed and the NARROW_ONLY argument is used to
+ exclude cases like ADDI being "widened" to an ADDMI, which may
+ later be relaxed to an ADDMI/ADDI pair. */
+
+bfd_boolean
+xg_is_single_relaxable_insn (TInsn *insn, TInsn *targ, bfd_boolean narrow_only)
+{
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
+ TransitionList *l;
+ TransitionRule *match = 0;
+
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->opcode < table->num_opcodes);
+
+ for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+ {
+ TransitionRule *rule = l->rule;
+
+ if (xg_instruction_matches_rule (insn, rule)
+ && is_unique_insn_expansion (rule)
+ && (xg_get_single_size (insn->opcode) + (narrow_only ? 1 : 0)
+ <= xg_get_single_size (rule->to_instr->opcode)))
+ {
+ if (match)
+ return FALSE;
+ match = rule;
+ }
+ }
+ if (!match)
+ return FALSE;
+
+ if (targ)
+ xg_build_to_insn (targ, insn, match->to_instr);
+ return TRUE;
+}
+
+
+/* Return the maximum number of bytes this opcode can expand to. */
+
+static int
+xg_get_max_insn_widen_size (xtensa_opcode opcode)
+{
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
+ TransitionList *l;
+ int max_size = xg_get_single_size (opcode);
+
+ gas_assert (opcode < table->num_opcodes);
+
+ for (l = table->table[opcode]; l != NULL; l = l->next)
+ {
+ TransitionRule *rule = l->rule;
+ BuildInstr *build_list;
+ int this_size = 0;
+
+ if (!rule)
+ continue;
+ build_list = rule->to_instr;
+ if (is_unique_insn_expansion (rule))
+ {
+ gas_assert (build_list->typ == INSTR_INSTR);
+ this_size = xg_get_max_insn_widen_size (build_list->opcode);
+ }
+ else
+ for (; build_list != NULL; build_list = build_list->next)
+ {
+ switch (build_list->typ)
+ {
+ case INSTR_INSTR:
+ this_size += xg_get_single_size (build_list->opcode);
+ break;
+ case INSTR_LITERAL_DEF:
+ case INSTR_LABEL_DEF:
+ default:
+ break;
+ }
+ }
+ if (this_size > max_size)
+ max_size = this_size;
+ }
+ return max_size;
+}
+
+
+/* Return the maximum number of literal bytes this opcode can generate. */
+
+static int
+xg_get_max_insn_widen_literal_size (xtensa_opcode opcode)
+{
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
+ TransitionList *l;
+ int max_size = 0;
+
+ gas_assert (opcode < table->num_opcodes);
+
+ for (l = table->table[opcode]; l != NULL; l = l->next)
+ {
+ TransitionRule *rule = l->rule;
+ BuildInstr *build_list;
+ int this_size = 0;
+
+ if (!rule)
+ continue;
+ build_list = rule->to_instr;
+ if (is_unique_insn_expansion (rule))
+ {
+ gas_assert (build_list->typ == INSTR_INSTR);
+ this_size = xg_get_max_insn_widen_literal_size (build_list->opcode);
+ }
+ else
+ for (; build_list != NULL; build_list = build_list->next)
+ {
+ switch (build_list->typ)
+ {
+ case INSTR_LITERAL_DEF:
+ /* Hard-coded 4-byte literal. */
+ this_size += 4;
+ break;
+ case INSTR_INSTR:
+ case INSTR_LABEL_DEF:
+ default:
+ break;
+ }
+ }
+ if (this_size > max_size)
+ max_size = this_size;
+ }
+ return max_size;
+}
+
+
+static bfd_boolean
+xg_is_relaxable_insn (TInsn *insn, int lateral_steps)
+{
+ int steps_taken = 0;
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
+ TransitionList *l;
+
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->opcode < table->num_opcodes);
+
+ for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+ {
+ TransitionRule *rule = l->rule;
+
+ if (xg_instruction_matches_rule (insn, rule))
+ {
+ if (steps_taken == lateral_steps)
+ return TRUE;
+ steps_taken++;
+ }
+ }
+ return FALSE;
+}
+
+
+static symbolS *
+get_special_literal_symbol (void)
+{
+ static symbolS *sym = NULL;
+
+ if (sym == NULL)
+ sym = symbol_find_or_make ("SPECIAL_LITERAL0\001");
+ return sym;
+}
+
+
+static symbolS *
+get_special_label_symbol (void)
+{
+ static symbolS *sym = NULL;
+
+ if (sym == NULL)
+ sym = symbol_find_or_make ("SPECIAL_LABEL0\001");
+ return sym;
+}
+
+
+static bfd_boolean
+xg_valid_literal_expression (const expressionS *exp)
+{
+ switch (exp->X_op)
+ {
+ case O_constant:
+ case O_symbol:
+ case O_big:
+ case O_uminus:
+ case O_subtract:
+ case O_pltrel:
+ case O_pcrel:
+ case O_tlsfunc:
+ case O_tlsarg:
+ case O_tpoff:
+ case O_dtpoff:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+/* This will check to see if the value can be converted into the
+ operand type. It will return TRUE if it does not fit. */
+
+static bfd_boolean
+xg_check_operand (int32 value, xtensa_opcode opcode, int operand)
+{
+ uint32 valbuf = value;
+ if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
+ return TRUE;
+ return FALSE;
+}
+
+
+/* Assumes: All immeds are constants. Check that all constants fit
+ into their immeds; return FALSE if not. */
+
+static bfd_boolean
+xg_immeds_fit (const TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
+
+ int n = insn->ntok;
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ for (i = 0; i < n; ++i)
+ {
+ const expressionS *exp = &insn->tok[i];
+
+ if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+ continue;
+
+ switch (exp->X_op)
+ {
+ case O_register:
+ case O_constant:
+ if (xg_check_operand (exp->X_add_number, insn->opcode, i))
+ return FALSE;
+ break;
+
+ default:
+ /* The symbol should have a fixup associated with it. */
+ gas_assert (FALSE);
+ break;
+ }
+ }
+ return TRUE;
+}
+
+
+/* This should only be called after we have an initial
+ estimate of the addresses. */
+
+static bfd_boolean
+xg_symbolic_immeds_fit (const TInsn *insn,
+ segT pc_seg,
+ fragS *pc_frag,
+ offsetT pc_offset,
+ long stretch)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ symbolS *symbolP;
+ fragS *sym_frag;
+ offsetT target, pc;
+ uint32 new_offset;
+ int i;
+ int n = insn->ntok;
+
+ gas_assert (insn->insn_type == ITYPE_INSN);
+
+ for (i = 0; i < n; ++i)
+ {
+ const expressionS *exp = &insn->tok[i];
+
+ if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+ continue;
+
+ switch (exp->X_op)
+ {
+ case O_register:
+ case O_constant:
+ if (xg_check_operand (exp->X_add_number, insn->opcode, i))
+ return FALSE;
+ break;
+
+ case O_lo16:
+ case O_hi16:
+ /* Check for the worst case. */
+ if (xg_check_operand (0xffff, insn->opcode, i))
+ return FALSE;
+ break;
+
+ case O_symbol:
+ /* We only allow symbols for PC-relative references.
+ If pc_frag == 0, then we don't have frag locations yet. */
+ if (pc_frag == 0
+ || xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0)
+ return FALSE;
+
+ /* If it is a weak symbol or a symbol in a different section,
+ it cannot be known to fit at assembly time. */
+ if (S_IS_WEAK (exp->X_add_symbol)
+ || S_GET_SEGMENT (exp->X_add_symbol) != pc_seg)
+ {
+ /* For a direct call with --no-longcalls, be optimistic and
+ assume it will be in range. If the symbol is weak and
+ undefined, it may remain undefined at link-time, in which
+ case it will have a zero value and almost certainly be out
+ of range for a direct call; thus, relax for undefined weak
+ symbols even if longcalls is not enabled. */
+ if (is_direct_call_opcode (insn->opcode)
+ && ! pc_frag->tc_frag_data.use_longcalls
+ && (! S_IS_WEAK (exp->X_add_symbol)
+ || S_IS_DEFINED (exp->X_add_symbol)))
+ return TRUE;
+
+ return FALSE;
+ }
+
+ symbolP = exp->X_add_symbol;
+ sym_frag = symbol_get_frag (symbolP);
+ target = S_GET_VALUE (symbolP) + exp->X_add_number;
+ pc = pc_frag->fr_address + pc_offset;
+
+ /* If frag has yet to be reached on this pass, assume it
+ will move by STRETCH just as we did. If this is not so,
+ it will be because some frag between grows, and that will
+ force another pass. Beware zero-length frags. There
+ should be a faster way to do this. */
+
+ if (stretch != 0
+ && sym_frag->relax_marker != pc_frag->relax_marker
+ && S_GET_SEGMENT (symbolP) == pc_seg)
+ {
+ target += stretch;
+ }
+
+ new_offset = target;
+ xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
+ if (xg_check_operand (new_offset, insn->opcode, i))
+ return FALSE;
+ break;
+
+ default:
+ /* The symbol should have a fixup associated with it. */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* Return TRUE on success. */
+
+static bfd_boolean
+xg_build_to_insn (TInsn *targ, TInsn *insn, BuildInstr *bi)
+{
+ BuildOp *op;
+ symbolS *sym;
+
+ tinsn_init (targ);
+ targ->debug_line = insn->debug_line;
+ targ->loc_directive_seen = insn->loc_directive_seen;
+ switch (bi->typ)
+ {
+ case INSTR_INSTR:
+ op = bi->ops;
+ targ->opcode = bi->opcode;
+ targ->insn_type = ITYPE_INSN;
+ targ->is_specific_opcode = FALSE;
+
+ for (; op != NULL; op = op->next)
+ {
+ int op_num = op->op_num;
+ int op_data = op->op_data;
+
+ gas_assert (op->op_num < MAX_INSN_ARGS);
+
+ if (targ->ntok <= op_num)
+ targ->ntok = op_num + 1;
+
+ switch (op->typ)
+ {
+ case OP_CONSTANT:
+ set_expr_const (&targ->tok[op_num], op_data);
+ break;
+ case OP_OPERAND:
+ gas_assert (op_data < insn->ntok);
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ break;
+ case OP_FREEREG:
+ if (insn->extra_arg.X_op != O_register)
+ return FALSE;
+ copy_expr (&targ->tok[op_num], &insn->extra_arg);
+ break;
+ case OP_LITERAL:
+ sym = get_special_literal_symbol ();
+ set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
+ if (insn->tok[op_data].X_op == O_tlsfunc
+ || insn->tok[op_data].X_op == O_tlsarg)
+ copy_expr (&targ->extra_arg, &insn->tok[op_data]);
+ break;
+ case OP_LABEL:
+ sym = get_special_label_symbol ();
+ set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
+ break;
+ case OP_OPERAND_HI16U:
+ case OP_OPERAND_LOW16U:
+ gas_assert (op_data < insn->ntok);
+ if (expr_is_const (&insn->tok[op_data]))
+ {
+ long val;
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ val = xg_apply_userdef_op_fn (op->typ,
+ targ->tok[op_num].
+ X_add_number);
+ targ->tok[op_num].X_add_number = val;
+ }
+ else
+ {
+ /* For const16 we can create relocations for these. */
+ if (targ->opcode == XTENSA_UNDEFINED
+ || (targ->opcode != xtensa_const16_opcode))
+ return FALSE;
+ gas_assert (op_data < insn->ntok);
+ /* Need to build a O_lo16 or O_hi16. */
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ if (targ->tok[op_num].X_op == O_symbol)
+ {
+ if (op->typ == OP_OPERAND_HI16U)
+ targ->tok[op_num].X_op = O_hi16;
+ else if (op->typ == OP_OPERAND_LOW16U)
+ targ->tok[op_num].X_op = O_lo16;
+ else
+ return FALSE;
+ }
+ }
+ break;
+ default:
+ /* currently handles:
+ OP_OPERAND_LOW8
+ OP_OPERAND_HI24S
+ OP_OPERAND_F32MINUS */
+ if (xg_has_userdef_op_fn (op->typ))
+ {
+ gas_assert (op_data < insn->ntok);
+ if (expr_is_const (&insn->tok[op_data]))
+ {
+ long val;
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ val = xg_apply_userdef_op_fn (op->typ,
+ targ->tok[op_num].
+ X_add_number);
+ targ->tok[op_num].X_add_number = val;
+ }
+ else
+ return FALSE; /* We cannot use a relocation for this. */
+ break;
+ }
+ gas_assert (0);
+ break;
+ }
+ }
+ break;
+
+ case INSTR_LITERAL_DEF:
+ op = bi->ops;
+ targ->opcode = XTENSA_UNDEFINED;
+ targ->insn_type = ITYPE_LITERAL;
+ targ->is_specific_opcode = FALSE;
+ for (; op != NULL; op = op->next)
+ {
+ int op_num = op->op_num;
+ int op_data = op->op_data;
+ gas_assert (op->op_num < MAX_INSN_ARGS);
+
+ if (targ->ntok <= op_num)
+ targ->ntok = op_num + 1;
+
+ switch (op->typ)
+ {
+ case OP_OPERAND:
+ gas_assert (op_data < insn->ntok);
+ /* We can only pass resolvable literals through. */
+ if (!xg_valid_literal_expression (&insn->tok[op_data]))
+ return FALSE;
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ break;
+ case OP_LITERAL:
+ case OP_CONSTANT:
+ case OP_LABEL:
+ default:
+ gas_assert (0);
+ break;
+ }
+ }
+ break;
+
+ case INSTR_LABEL_DEF:
+ op = bi->ops;
+ targ->opcode = XTENSA_UNDEFINED;
+ targ->insn_type = ITYPE_LABEL;
+ targ->is_specific_opcode = FALSE;
+ /* Literal with no ops is a label? */
+ gas_assert (op == NULL);
+ break;
+
+ default:
+ gas_assert (0);
+ }
+
+ return TRUE;
+}
+
+
+/* Return TRUE on success. */
+
+static bfd_boolean
+xg_build_to_stack (IStack *istack, TInsn *insn, BuildInstr *bi)
+{
+ for (; bi != NULL; bi = bi->next)
+ {
+ TInsn *next_insn = istack_push_space (istack);
+
+ if (!xg_build_to_insn (next_insn, insn, bi))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+/* Return TRUE on valid expansion. */
+
+static bfd_boolean
+xg_expand_to_stack (IStack *istack, TInsn *insn, int lateral_steps)
+{
+ int stack_size = istack->ninsn;
+ int steps_taken = 0;
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
+ TransitionList *l;
+
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->opcode < table->num_opcodes);
+
+ for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+ {
+ TransitionRule *rule = l->rule;
+
+ if (xg_instruction_matches_rule (insn, rule))
+ {
+ if (lateral_steps == steps_taken)
+ {
+ int i;
+
+ /* This is it. Expand the rule to the stack. */
+ if (!xg_build_to_stack (istack, insn, rule->to_instr))
+ return FALSE;
+
+ /* Check to see if it fits. */
+ for (i = stack_size; i < istack->ninsn; i++)
+ {
+ TInsn *tinsn = &istack->insn[i];
+
+ if (tinsn->insn_type == ITYPE_INSN
+ && !tinsn_has_symbolic_operands (tinsn)
+ && !xg_immeds_fit (tinsn))
+ {
+ istack->ninsn = stack_size;
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ steps_taken++;
+ }
+ }
+ return FALSE;
+}
+
+
+/* Relax the assembly instruction at least "min_steps".
+ Return the number of steps taken.
+
+ For relaxation to correctly terminate, every relaxation chain must
+ terminate in one of two ways:
+
+ 1. If the chain from one instruction to the next consists entirely of
+ single instructions, then the chain *must* handle all possible
+ immediates without failing. It must not ever fail because an
+ immediate is out of range. The MOVI.N -> MOVI -> L32R relaxation
+ chain is one example. L32R loads 32 bits, and there cannot be an
+ immediate larger than 32 bits, so it satisfies this condition.
+ Single instruction relaxation chains are as defined by
+ xg_is_single_relaxable_instruction.
+
+ 2. Otherwise, the chain must end in a multi-instruction expansion: e.g.,
+ BNEZ.N -> BNEZ -> BNEZ.W15 -> BENZ.N/J
+
+ Strictly speaking, in most cases you can violate condition 1 and be OK
+ -- in particular when the last two instructions have the same single
+ size. But nevertheless, you should guarantee the above two conditions.
+
+ We could fix this so that single-instruction expansions correctly
+ terminate when they can't handle the range, but the error messages are
+ worse, and it actually turns out that in every case but one (18-bit wide
+ branches), you need a multi-instruction expansion to get the full range
+ anyway. And because 18-bit branches are handled identically to 15-bit
+ branches, there isn't any point in changing it. */
+
+static int
+xg_assembly_relax (IStack *istack,
+ TInsn *insn,
+ segT pc_seg,
+ fragS *pc_frag, /* if pc_frag == 0, not pc-relative */
+ offsetT pc_offset, /* offset in fragment */
+ int min_steps, /* minimum conversion steps */
+ long stretch) /* number of bytes stretched so far */
+{
+ int steps_taken = 0;
+
+ /* Some of its immeds don't fit. Try to build a relaxed version.
+ This may go through a couple of stages of single instruction
+ transformations before we get there. */
+
+ TInsn single_target;
+ TInsn current_insn;
+ int lateral_steps = 0;
+ int istack_size = istack->ninsn;
+
+ if (xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
+ && steps_taken >= min_steps)
+ {
+ istack_push (istack, insn);
+ return steps_taken;
+ }
+ current_insn = *insn;
+
+ /* Walk through all of the single instruction expansions. */
+ while (xg_is_single_relaxable_insn (&current_insn, &single_target, FALSE))
+ {
+ steps_taken++;
+ if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
+ stretch))
+ {
+ if (steps_taken >= min_steps)
+ {
+ istack_push (istack, &single_target);
+ return steps_taken;
+ }
+ }
+ current_insn = single_target;
+ }
+
+ /* Now check for a multi-instruction expansion. */
+ while (xg_is_relaxable_insn (&current_insn, lateral_steps))
+ {
+ if (xg_symbolic_immeds_fit (&current_insn, pc_seg, pc_frag, pc_offset,
+ stretch))
+ {
+ if (steps_taken >= min_steps)
+ {
+ istack_push (istack, &current_insn);
+ return steps_taken;
+ }
+ }
+ steps_taken++;
+ if (xg_expand_to_stack (istack, &current_insn, lateral_steps))
+ {
+ if (steps_taken >= min_steps)
+ return steps_taken;
+ }
+ lateral_steps++;
+ istack->ninsn = istack_size;
+ }
+
+ /* It's not going to work -- use the original. */
+ istack_push (istack, insn);
+ return steps_taken;
+}
+
+
+static void
+xg_finish_frag (char *last_insn,
+ enum xtensa_relax_statesE frag_state,
+ enum xtensa_relax_statesE slot0_state,
+ int max_growth,
+ bfd_boolean is_insn)
+{
+ /* Finish off this fragment so that it has at LEAST the desired
+ max_growth. If it doesn't fit in this fragment, close this one
+ and start a new one. In either case, return a pointer to the
+ beginning of the growth area. */
+
+ fragS *old_frag;
+
+ frag_grow (max_growth);
+ old_frag = frag_now;
+
+ frag_now->fr_opcode = last_insn;
+ if (is_insn)
+ frag_now->tc_frag_data.is_insn = TRUE;
+
+ frag_var (rs_machine_dependent, max_growth, max_growth,
+ frag_state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
+
+ old_frag->tc_frag_data.slot_subtypes[0] = slot0_state;
+ xtensa_set_frag_assembly_state (frag_now);
+
+ /* Just to make sure that we did not split it up. */
+ gas_assert (old_frag->fr_next == frag_now);
+}
+
+
+/* Return TRUE if the target frag is one of the next non-empty frags. */
+
+static bfd_boolean
+is_next_frag_target (const fragS *fragP, const fragS *target)
+{
+ if (fragP == NULL)
+ return FALSE;
+
+ for (; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP == target)
+ return TRUE;
+ if (fragP->fr_fix != 0)
+ return FALSE;
+ if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
+ return FALSE;
+ if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
+ && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
+ return FALSE;
+ if (fragP->fr_type == rs_space)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+is_branch_jmp_to_next (TInsn *insn, fragS *fragP)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
+ int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
+ int target_op = -1;
+ symbolS *sym;
+ fragS *target_frag;
+
+ if (xtensa_opcode_is_branch (isa, insn->opcode) != 1
+ && xtensa_opcode_is_jump (isa, insn->opcode) != 1)
+ return FALSE;
+
+ for (i = 0; i < num_ops; i++)
+ {
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
+ {
+ target_op = i;
+ break;
+ }
+ }
+ if (target_op == -1)
+ return FALSE;
+
+ if (insn->ntok <= target_op)
+ return FALSE;
+
+ if (insn->tok[target_op].X_op != O_symbol)
+ return FALSE;
+
+ sym = insn->tok[target_op].X_add_symbol;
+ if (sym == NULL)
+ return FALSE;
+
+ if (insn->tok[target_op].X_add_number != 0)
+ return FALSE;
+
+ target_frag = symbol_get_frag (sym);
+ if (target_frag == NULL)
+ return FALSE;
+
+ if (is_next_frag_target (fragP->fr_next, target_frag)
+ && S_GET_VALUE (sym) == target_frag->fr_address)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static void
+xg_add_branch_and_loop_targets (TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
+
+ if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
+ {
+ int i = 1;
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
+ && insn->tok[i].X_op == O_symbol)
+ symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
+ return;
+ }
+
+ if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
+ || xtensa_opcode_is_loop (isa, insn->opcode) == 1)
+ {
+ int i;
+
+ for (i = 0; i < insn->ntok && i < num_ops; i++)
+ {
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
+ && insn->tok[i].X_op == O_symbol)
+ {
+ symbolS *sym = insn->tok[i].X_add_symbol;
+ symbol_get_tc (sym)->is_branch_target = TRUE;
+ if (S_IS_DEFINED (sym))
+ symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+ }
+ }
+ }
+}
+
+
+/* Return FALSE if no error. */
+
+static bfd_boolean
+xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
+{
+ int num_ops = 0;
+ BuildOp *b_op;
+
+ switch (instr_spec->typ)
+ {
+ case INSTR_INSTR:
+ new_insn->insn_type = ITYPE_INSN;
+ new_insn->opcode = instr_spec->opcode;
+ break;
+ case INSTR_LITERAL_DEF:
+ new_insn->insn_type = ITYPE_LITERAL;
+ new_insn->opcode = XTENSA_UNDEFINED;
+ break;
+ case INSTR_LABEL_DEF:
+ abort ();
+ }
+ new_insn->is_specific_opcode = FALSE;
+ new_insn->debug_line = old_insn->debug_line;
+ new_insn->loc_directive_seen = old_insn->loc_directive_seen;
+
+ for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
+ {
+ expressionS *exp;
+ const expressionS *src_exp;
+
+ num_ops++;
+ switch (b_op->typ)
+ {
+ case OP_CONSTANT:
+ /* The expression must be the constant. */
+ gas_assert (b_op->op_num < MAX_INSN_ARGS);
+ exp = &new_insn->tok[b_op->op_num];
+ set_expr_const (exp, b_op->op_data);
+ break;
+
+ case OP_OPERAND:
+ gas_assert (b_op->op_num < MAX_INSN_ARGS);
+ gas_assert (b_op->op_data < (unsigned) old_insn->ntok);
+ src_exp = &old_insn->tok[b_op->op_data];
+ exp = &new_insn->tok[b_op->op_num];
+ copy_expr (exp, src_exp);
+ break;
+
+ case OP_LITERAL:
+ case OP_LABEL:
+ as_bad (_("can't handle generation of literal/labels yet"));
+ gas_assert (0);
+
+ default:
+ as_bad (_("can't handle undefined OP TYPE"));
+ gas_assert (0);
+ }
+ }
+
+ new_insn->ntok = num_ops;
+ return FALSE;
+}
+
+
+/* Return TRUE if it was simplified. */
+
+static bfd_boolean
+xg_simplify_insn (TInsn *old_insn, TInsn *new_insn)
+{
+ TransitionRule *rule;
+ BuildInstr *insn_spec;
+
+ if (old_insn->is_specific_opcode || !density_supported)
+ return FALSE;
+
+ rule = xg_instruction_match (old_insn);
+ if (rule == NULL)
+ return FALSE;
+
+ insn_spec = rule->to_instr;
+ /* There should only be one. */
+ gas_assert (insn_spec != NULL);
+ gas_assert (insn_spec->next == NULL);
+ if (insn_spec->next != NULL)
+ return FALSE;
+
+ xg_build_token_insn (insn_spec, old_insn, new_insn);
+
+ return TRUE;
+}
+
+
+/* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i ->
+ l32i.n. (2) Check the number of operands. (3) Place the instruction
+ tokens into the stack or relax it and place multiple
+ instructions/literals onto the stack. Return FALSE if no error. */
+
+static bfd_boolean
+xg_expand_assembly_insn (IStack *istack, TInsn *orig_insn)
+{
+ int noperands;
+ TInsn new_insn;
+ bfd_boolean do_expand;
+
+ tinsn_init (&new_insn);
+
+ /* Narrow it if we can. xg_simplify_insn now does all the
+ appropriate checking (e.g., for the density option). */
+ if (xg_simplify_insn (orig_insn, &new_insn))
+ orig_insn = &new_insn;
+
+ noperands = xtensa_opcode_num_operands (xtensa_default_isa,
+ orig_insn->opcode);
+ if (orig_insn->ntok < noperands)
+ {
+ as_bad (_("found %d operands for '%s': Expected %d"),
+ orig_insn->ntok,
+ xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
+ noperands);
+ return TRUE;
+ }
+ if (orig_insn->ntok > noperands)
+ as_warn (_("found too many (%d) operands for '%s': Expected %d"),
+ orig_insn->ntok,
+ xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
+ noperands);
+
+ /* If there are not enough operands, we will assert above. If there
+ are too many, just cut out the extras here. */
+ orig_insn->ntok = noperands;
+
+ if (tinsn_has_invalid_symbolic_operands (orig_insn))
+ return TRUE;
+
+ /* Special case for extui opcode which has constraints not handled
+ by the ordinary operand encoding checks. The number of operands
+ and related syntax issues have already been checked. */
+ if (orig_insn->opcode == xtensa_extui_opcode)
+ {
+ int shiftimm = orig_insn->tok[2].X_add_number;
+ int maskimm = orig_insn->tok[3].X_add_number;
+ if (shiftimm + maskimm > 32)
+ {
+ as_bad (_("immediate operands sum to greater than 32"));
+ return TRUE;
+ }
+ }
+
+ /* If the instruction will definitely need to be relaxed, it is better
+ to expand it now for better scheduling. Decide whether to expand
+ now.... */
+ do_expand = (!orig_insn->is_specific_opcode && use_transform ());
+
+ /* Calls should be expanded to longcalls only in the backend relaxation
+ so that the assembly scheduler will keep the L32R/CALLX instructions
+ adjacent. */
+ if (is_direct_call_opcode (orig_insn->opcode))
+ do_expand = FALSE;
+
+ if (tinsn_has_symbolic_operands (orig_insn))
+ {
+ /* The values of symbolic operands are not known yet, so only expand
+ now if an operand is "complex" (e.g., difference of symbols) and
+ will have to be stored as a literal regardless of the value. */
+ if (!tinsn_has_complex_operands (orig_insn))
+ do_expand = FALSE;
+ }
+ else if (xg_immeds_fit (orig_insn))
+ do_expand = FALSE;
+
+ if (do_expand)
+ xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
+ else
+ istack_push (istack, orig_insn);
+
+ return FALSE;
+}
+
+
+/* Return TRUE if the section flags are marked linkonce
+ or the name is .gnu.linkonce.*. */
+
+static int linkonce_len = sizeof (".gnu.linkonce.") - 1;
+
+static bfd_boolean
+get_is_linkonce_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec)
+{
+ flagword flags, link_once_flags;
+
+ flags = bfd_get_section_flags (abfd, sec);
+ link_once_flags = (flags & SEC_LINK_ONCE);
+
+ /* Flags might not be set yet. */
+ if (!link_once_flags
+ && strncmp (segment_name (sec), ".gnu.linkonce.", linkonce_len) == 0)
+ link_once_flags = SEC_LINK_ONCE;
+
+ return (link_once_flags != 0);
+}
+
+
+static void
+xtensa_add_literal_sym (symbolS *sym)
+{
+ sym_list *l;
+
+ l = (sym_list *) xmalloc (sizeof (sym_list));
+ l->sym = sym;
+ l->next = literal_syms;
+ literal_syms = l;
+}
+
+
+static symbolS *
+xtensa_create_literal_symbol (segT sec, fragS *frag)
+{
+ static int lit_num = 0;
+ static char name[256];
+ symbolS *symbolP;
+
+ sprintf (name, ".L_lit_sym%d", lit_num);
+
+ /* Create a local symbol. If it is in a linkonce section, we have to
+ be careful to make sure that if it is used in a relocation that the
+ symbol will be in the output file. */
+ if (get_is_linkonce_section (stdoutput, sec))
+ {
+ symbolP = symbol_new (name, sec, 0, frag);
+ S_CLEAR_EXTERNAL (symbolP);
+ /* symbolP->local = 1; */
+ }
+ else
+ symbolP = symbol_new (name, sec, 0, frag);
+
+ xtensa_add_literal_sym (symbolP);
+
+ lit_num++;
+ return symbolP;
+}
+
+
+/* Currently all literals that are generated here are 32-bit L32R targets. */
+
+static symbolS *
+xg_assemble_literal (/* const */ TInsn *insn)
+{
+ emit_state state;
+ symbolS *lit_sym = NULL;
+ bfd_reloc_code_real_type reloc;
+ bfd_boolean pcrel = FALSE;
+ char *p;
+
+ /* size = 4 for L32R. It could easily be larger when we move to
+ larger constants. Add a parameter later. */
+ offsetT litsize = 4;
+ offsetT litalign = 2; /* 2^2 = 4 */
+ expressionS saved_loc;
+ expressionS * emit_val;
+
+ set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
+
+ gas_assert (insn->insn_type == ITYPE_LITERAL);
+ gas_assert (insn->ntok == 1); /* must be only one token here */
+
+ xtensa_switch_to_literal_fragment (&state);
+
+ emit_val = &insn->tok[0];
+ if (emit_val->X_op == O_big)
+ {
+ int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
+ if (size > litsize)
+ {
+ /* This happens when someone writes a "movi a2, big_number". */
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("invalid immediate"));
+ xtensa_restore_emit_state (&state);
+ return NULL;
+ }
+ }
+
+ /* Force a 4-byte align here. Note that this opens a new frag, so all
+ literals done with this function have a frag to themselves. That's
+ important for the way text section literals work. */
+ frag_align (litalign, 0, 0);
+ record_alignment (now_seg, litalign);
+
+ switch (emit_val->X_op)
+ {
+ case O_pcrel:
+ pcrel = TRUE;
+ /* fall through */
+ case O_pltrel:
+ case O_tlsfunc:
+ case O_tlsarg:
+ case O_tpoff:
+ case O_dtpoff:
+ p = frag_more (litsize);
+ xtensa_set_frag_assembly_state (frag_now);
+ reloc = map_operator_to_reloc (emit_val->X_op, TRUE);
+ if (emit_val->X_add_symbol)
+ emit_val->X_op = O_symbol;
+ else
+ emit_val->X_op = O_constant;
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ litsize, emit_val, pcrel, reloc);
+ break;
+
+ default:
+ emit_expr (emit_val, litsize);
+ break;
+ }
+
+ gas_assert (frag_now->tc_frag_data.literal_frag == NULL);
+ frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
+ frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
+ lit_sym = frag_now->fr_symbol;
+
+ /* Go back. */
+ xtensa_restore_emit_state (&state);
+ return lit_sym;
+}
+
+
+static void
+xg_assemble_literal_space (/* const */ int size, int slot)
+{
+ emit_state state;
+ /* We might have to do something about this alignment. It only
+ takes effect if something is placed here. */
+ offsetT litalign = 2; /* 2^2 = 4 */
+ fragS *lit_saved_frag;
+
+ gas_assert (size % 4 == 0);
+
+ xtensa_switch_to_literal_fragment (&state);
+
+ /* Force a 4-byte align here. */
+ frag_align (litalign, 0, 0);
+ record_alignment (now_seg, litalign);
+
+ frag_grow (size);
+
+ lit_saved_frag = frag_now;
+ frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
+ frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
+ xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
+
+ /* Go back. */
+ xtensa_restore_emit_state (&state);
+ frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
+}
+
+
+/* Put in a fixup record based on the opcode.
+ Return TRUE on success. */
+
+static bfd_boolean
+xg_add_opcode_fix (TInsn *tinsn,
+ int opnum,
+ xtensa_format fmt,
+ int slot,
+ expressionS *exp,
+ fragS *fragP,
+ offsetT offset)
+{
+ xtensa_opcode opcode = tinsn->opcode;
+ bfd_reloc_code_real_type reloc;
+ reloc_howto_type *howto;
+ int fmt_length;
+ fixS *the_fix;
+
+ reloc = BFD_RELOC_NONE;
+
+ /* First try the special cases for "alternate" relocs. */
+ if (opcode == xtensa_l32r_opcode)
+ {
+ if (fragP->tc_frag_data.use_absolute_literals)
+ reloc = encode_alt_reloc (slot);
+ }
+ else if (opcode == xtensa_const16_opcode)
+ {
+ if (exp->X_op == O_lo16)
+ {
+ reloc = encode_reloc (slot);
+ exp->X_op = O_symbol;
+ }
+ else if (exp->X_op == O_hi16)
+ {
+ reloc = encode_alt_reloc (slot);
+ exp->X_op = O_symbol;
+ }
+ }
+
+ if (opnum != get_relaxable_immed (opcode))
+ {
+ as_bad (_("invalid relocation for operand %i of '%s'"),
+ opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
+
+ /* Handle erroneous "@h" and "@l" expressions here before they propagate
+ into the symbol table where the generic portions of the assembler
+ won't know what to do with them. */
+ if (exp->X_op == O_lo16 || exp->X_op == O_hi16)
+ {
+ as_bad (_("invalid expression for operand %i of '%s'"),
+ opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
+
+ /* Next try the generic relocs. */
+ if (reloc == BFD_RELOC_NONE)
+ reloc = encode_reloc (slot);
+ if (reloc == BFD_RELOC_NONE)
+ {
+ as_bad (_("invalid relocation in instruction slot %i"), slot);
+ return FALSE;
+ }
+
+ howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ if (!howto)
+ {
+ as_bad (_("undefined symbol for opcode \"%s\""),
+ xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
+
+ fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
+ the_fix = fix_new_exp (fragP, offset, fmt_length, exp,
+ howto->pc_relative, reloc);
+ the_fix->fx_no_overflow = 1;
+ the_fix->tc_fix_data.X_add_symbol = exp->X_add_symbol;
+ the_fix->tc_fix_data.X_add_number = exp->X_add_number;
+ the_fix->tc_fix_data.slot = slot;
+
+ return TRUE;
+}
+
+
+static bfd_boolean
+xg_emit_insn_to_buf (TInsn *tinsn,
+ char *buf,
+ fragS *fragP,
+ offsetT offset,
+ bfd_boolean build_fix)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ bfd_boolean has_symbolic_immed = FALSE;
+ bfd_boolean ok = TRUE;
+
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+
+ has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
+ if (has_symbolic_immed && build_fix)
+ {
+ /* Add a fixup. */
+ xtensa_format fmt = xg_get_single_format (tinsn->opcode);
+ int slot = xg_get_single_slot (tinsn->opcode);
+ int opnum = get_relaxable_immed (tinsn->opcode);
+ expressionS *exp = &tinsn->tok[opnum];
+
+ if (!xg_add_opcode_fix (tinsn, opnum, fmt, slot, exp, fragP, offset))
+ ok = FALSE;
+ }
+ fragP->tc_frag_data.is_insn = TRUE;
+ xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+ (unsigned char *) buf, 0);
+ return ok;
+}
+
+
+static void
+xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
+{
+ symbolS *sym = get_special_literal_symbol ();
+ int i;
+ if (lit_sym == 0)
+ return;
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ for (i = 0; i < insn->ntok; i++)
+ if (insn->tok[i].X_add_symbol == sym)
+ insn->tok[i].X_add_symbol = lit_sym;
+
+}
+
+
+static void
+xg_resolve_labels (TInsn *insn, symbolS *label_sym)
+{
+ symbolS *sym = get_special_label_symbol ();
+ int i;
+ for (i = 0; i < insn->ntok; i++)
+ if (insn->tok[i].X_add_symbol == sym)
+ insn->tok[i].X_add_symbol = label_sym;
+
+}
+
+
+/* Return TRUE if the instruction can write to the specified
+ integer register. */
+
+static bfd_boolean
+is_register_writer (const TInsn *insn, const char *regset, int regnum)
+{
+ int i;
+ int num_ops;
+ xtensa_isa isa = xtensa_default_isa;
+
+ num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
+
+ for (i = 0; i < num_ops; i++)
+ {
+ char inout;
+ inout = xtensa_operand_inout (isa, insn->opcode, i);
+ if ((inout == 'o' || inout == 'm')
+ && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+ {
+ xtensa_regfile opnd_rf =
+ xtensa_operand_regfile (isa, insn->opcode, i);
+ if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
+ {
+ if ((insn->tok[i].X_op == O_register)
+ && (insn->tok[i].X_add_number == regnum))
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+is_bad_loopend_opcode (const TInsn *tinsn)
+{
+ xtensa_opcode opcode = tinsn->opcode;
+
+ if (opcode == XTENSA_UNDEFINED)
+ return FALSE;
+
+ if (opcode == xtensa_call0_opcode
+ || opcode == xtensa_callx0_opcode
+ || opcode == xtensa_call4_opcode
+ || opcode == xtensa_callx4_opcode
+ || opcode == xtensa_call8_opcode
+ || opcode == xtensa_callx8_opcode
+ || opcode == xtensa_call12_opcode
+ || opcode == xtensa_callx12_opcode
+ || opcode == xtensa_isync_opcode
+ || opcode == xtensa_ret_opcode
+ || opcode == xtensa_ret_n_opcode
+ || opcode == xtensa_retw_opcode
+ || opcode == xtensa_retw_n_opcode
+ || opcode == xtensa_waiti_opcode
+ || opcode == xtensa_rsr_lcount_opcode)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/* Labels that begin with ".Ln" or ".LM" are unaligned.
+ This allows the debugger to add unaligned labels.
+ Also, the assembler generates stabs labels that need
+ not be aligned: FAKE_LABEL_NAME . {"F", "L", "endfunc"}. */
+
+static bfd_boolean
+is_unaligned_label (symbolS *sym)
+{
+ const char *name = S_GET_NAME (sym);
+ static size_t fake_size = 0;
+
+ if (name
+ && name[0] == '.'
+ && name[1] == 'L' && (name[2] == 'n' || name[2] == 'M'))
+ return TRUE;
+
+ /* FAKE_LABEL_NAME followed by "F", "L" or "endfunc" */
+ if (fake_size == 0)
+ fake_size = strlen (FAKE_LABEL_NAME);
+
+ if (name
+ && strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
+ && (name[fake_size] == 'F'
+ || name[fake_size] == 'L'
+ || (name[fake_size] == 'e'
+ && strncmp ("endfunc", name+fake_size, 7) == 0)))
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static fragS *
+next_non_empty_frag (const fragS *fragP)
+{
+ fragS *next_fragP = fragP->fr_next;
+
+ /* Sometimes an empty will end up here due storage allocation issues.
+ So we have to skip until we find something legit. */
+ while (next_fragP && next_fragP->fr_fix == 0)
+ next_fragP = next_fragP->fr_next;
+
+ if (next_fragP == NULL || next_fragP->fr_fix == 0)
+ return NULL;
+
+ return next_fragP;
+}
+
+
+static bfd_boolean
+next_frag_opcode_is_loop (const fragS *fragP, xtensa_opcode *opcode)
+{
+ xtensa_opcode out_opcode;
+ const fragS *next_fragP = next_non_empty_frag (fragP);
+
+ if (next_fragP == NULL)
+ return FALSE;
+
+ out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
+ {
+ *opcode = out_opcode;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static int
+frag_format_size (const fragS *fragP)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt;
+ int fmt_size;
+
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
+ if (fragP == NULL)
+ return XTENSA_UNDEFINED;
+
+ xtensa_insnbuf_from_chars (isa, insnbuf,
+ (unsigned char *) fragP->fr_literal, 0);
+
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return XTENSA_UNDEFINED;
+ fmt_size = xtensa_format_length (isa, fmt);
+
+ /* If the next format won't be changing due to relaxation, just
+ return the length of the first format. */
+ if (fragP->fr_opcode != fragP->fr_literal)
+ return fmt_size;
+
+ /* If during relaxation we have to pull an instruction out of a
+ multi-slot instruction, we will return the more conservative
+ number. This works because alignment on bigger instructions
+ is more restrictive than alignment on smaller instructions.
+ This is more conservative than we would like, but it happens
+ infrequently. */
+
+ if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
+ return fmt_size;
+
+ /* If we aren't doing one of our own relaxations or it isn't
+ slot-based, then the insn size won't change. */
+ if (fragP->fr_type != rs_machine_dependent)
+ return fmt_size;
+ if (fragP->fr_subtype != RELAX_SLOTS)
+ return fmt_size;
+
+ /* If an instruction is about to grow, return the longer size. */
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
+ || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2
+ || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP3)
+ {
+ /* For most frags at RELAX_IMMED_STEPX, with X > 0, the first
+ instruction in the relaxed version is of length 3. (The case
+ where we have to pull the instruction out of a FLIX bundle
+ is handled conservatively above.) However, frags with opcodes
+ that are expanding to wide branches end up having formats that
+ are not determinable by the RELAX_IMMED_STEPX enumeration, and
+ we can't tell directly what format the relaxer picked. This
+ is a wart in the design of the relaxer that should someday be
+ fixed, but would require major changes, or at least should
+ be accompanied by major changes to make use of that data.
+
+ In any event, we can tell that we are expanding from a single-slot
+ format to a wider one with the logic below. */
+
+ int i;
+ int relaxed_size = fmt_size + fragP->tc_frag_data.text_expansion[0];
+
+ for (i = 0; i < xtensa_isa_num_formats (isa); i++)
+ {
+ if (relaxed_size == xtensa_format_length (isa, i))
+ return relaxed_size;
+ }
+
+ return 3;
+ }
+
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ return 2 + fragP->tc_frag_data.text_expansion[0];
+
+ return fmt_size;
+}
+
+
+static int
+next_frag_format_size (const fragS *fragP)
+{
+ const fragS *next_fragP = next_non_empty_frag (fragP);
+ return frag_format_size (next_fragP);
+}
+
+
+/* In early Xtensa Processors, for reasons that are unclear, the ISA
+ required two-byte instructions to be treated as three-byte instructions
+ for loop instruction alignment. This restriction was removed beginning
+ with Xtensa LX. Now the only requirement on loop instruction alignment
+ is that the first instruction of the loop must appear at an address that
+ does not cross a fetch boundary. */
+
+static int
+get_loop_align_size (int insn_size)
+{
+ if (insn_size == XTENSA_UNDEFINED)
+ return xtensa_fetch_width;
+
+ if (enforce_three_byte_loop_align && insn_size == 2)
+ return 3;
+
+ return insn_size;
+}
+
+
+/* If the next legit fragment is an end-of-loop marker,
+ switch its state so it will instantiate a NOP. */
+
+static void
+update_next_frag_state (fragS *fragP)
+{
+ fragS *next_fragP = fragP->fr_next;
+ fragS *new_target = NULL;
+
+ if (align_targets)
+ {
+ /* We are guaranteed there will be one of these... */
+ while (!(next_fragP->fr_type == rs_machine_dependent
+ && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || next_fragP->fr_subtype == RELAX_UNREACHABLE)))
+ next_fragP = next_fragP->fr_next;
+
+ gas_assert (next_fragP->fr_type == rs_machine_dependent
+ && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || next_fragP->fr_subtype == RELAX_UNREACHABLE));
+
+ /* ...and one of these. */
+ new_target = next_fragP->fr_next;
+ while (!(new_target->fr_type == rs_machine_dependent
+ && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+ || new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
+ new_target = new_target->fr_next;
+
+ gas_assert (new_target->fr_type == rs_machine_dependent
+ && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+ || new_target->fr_subtype == RELAX_DESIRE_ALIGN));
+ }
+
+ while (next_fragP && next_fragP->fr_fix == 0)
+ {
+ if (next_fragP->fr_type == rs_machine_dependent
+ && next_fragP->fr_subtype == RELAX_LOOP_END)
+ {
+ next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
+ return;
+ }
+
+ next_fragP = next_fragP->fr_next;
+ }
+}
+
+
+static bfd_boolean
+next_frag_is_branch_target (const fragS *fragP)
+{
+ /* Sometimes an empty will end up here due to storage allocation issues,
+ so we have to skip until we find something legit. */
+ for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->tc_frag_data.is_branch_target)
+ return TRUE;
+ if (fragP->fr_fix != 0)
+ break;
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+next_frag_is_loop_target (const fragS *fragP)
+{
+ /* Sometimes an empty will end up here due storage allocation issues.
+ So we have to skip until we find something legit. */
+ for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->tc_frag_data.is_loop_target)
+ return TRUE;
+ if (fragP->fr_fix != 0)
+ break;
+ }
+ return FALSE;
+}
+
+
+/* As specified in the relaxation table, when a loop instruction is
+ relaxed, there are 24 bytes between the loop instruction itself and
+ the first instruction in the loop. */
+
+#define RELAXED_LOOP_INSN_BYTES 24
+
+static addressT
+next_frag_pre_opcode_bytes (const fragS *fragp)
+{
+ const fragS *next_fragp = fragp->fr_next;
+ xtensa_opcode next_opcode;
+
+ if (!next_frag_opcode_is_loop (fragp, &next_opcode))
+ return 0;
+
+ /* Sometimes an empty will end up here due to storage allocation issues,
+ so we have to skip until we find something legit. */
+ while (next_fragp->fr_fix == 0)
+ next_fragp = next_fragp->fr_next;
+
+ if (next_fragp->fr_type != rs_machine_dependent)
+ return 0;
+
+ /* There is some implicit knowledge encoded in here.
+ The LOOP instructions that are NOT RELAX_IMMED have
+ been relaxed. Note that we can assume that the LOOP
+ instruction is in slot 0 because loops aren't bundleable. */
+ if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
+ return get_expanded_loop_offset (next_opcode) + RELAXED_LOOP_INSN_BYTES;
+
+ return 0;
+}
+
+
+/* Mark a location where we can later insert literal frags. Update
+ the section's literal_pool_loc, so subsequent literals can be
+ placed nearest to their use. */
+
+static void
+xtensa_mark_literal_pool_location (void)
+{
+ /* Any labels pointing to the current location need
+ to be adjusted to after the literal pool. */
+ emit_state s;
+ fragS *pool_location;
+
+ if (use_literal_section)
+ return;
+
+ /* We stash info in these frags so we can later move the literal's
+ fixes into this frchain's fix list. */
+ pool_location = frag_now;
+ frag_now->tc_frag_data.lit_frchain = frchain_now;
+ frag_now->tc_frag_data.literal_frag = frag_now;
+ frag_variant (rs_machine_dependent, 0, 0,
+ RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.lit_seg = now_seg;
+ frag_variant (rs_machine_dependent, 0, 0,
+ RELAX_LITERAL_POOL_END, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+
+ /* Now put a frag into the literal pool that points to this location. */
+ set_literal_pool_location (now_seg, pool_location);
+ xtensa_switch_to_non_abs_literal_fragment (&s);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+
+ /* Close whatever frag is there. */
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.literal_frag = pool_location;
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_restore_emit_state (&s);
+ xtensa_set_frag_assembly_state (frag_now);
+}
+
+
+/* Build a nop of the correct size into tinsn. */
+
+static void
+build_nop (TInsn *tinsn, int size)
+{
+ tinsn_init (tinsn);
+ switch (size)
+ {
+ case 2:
+ tinsn->opcode = xtensa_nop_n_opcode;
+ tinsn->ntok = 0;
+ if (tinsn->opcode == XTENSA_UNDEFINED)
+ as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
+ break;
+
+ case 3:
+ if (xtensa_nop_opcode == XTENSA_UNDEFINED)
+ {
+ tinsn->opcode = xtensa_or_opcode;
+ set_expr_const (&tinsn->tok[0], 1);
+ set_expr_const (&tinsn->tok[1], 1);
+ set_expr_const (&tinsn->tok[2], 1);
+ tinsn->ntok = 3;
+ }
+ else
+ tinsn->opcode = xtensa_nop_opcode;
+
+ gas_assert (tinsn->opcode != XTENSA_UNDEFINED);
+ }
+}
+
+
+/* Assemble a NOP of the requested size in the buffer. User must have
+ allocated "buf" with at least "size" bytes. */
+
+static void
+assemble_nop (int size, char *buf)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ TInsn tinsn;
+
+ build_nop (&tinsn, size);
+
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+
+ tinsn_to_insnbuf (&tinsn, insnbuf);
+ xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+ (unsigned char *) buf, 0);
+}
+
+
+/* Return the number of bytes for the offset of the expanded loop
+ instruction. This should be incorporated into the relaxation
+ specification but is hard-coded here. This is used to auto-align
+ the loop instruction. It is invalid to call this function if the
+ configuration does not have loops or if the opcode is not a loop
+ opcode. */
+
+static addressT
+get_expanded_loop_offset (xtensa_opcode opcode)
+{
+ /* This is the OFFSET of the loop instruction in the expanded loop.
+ This MUST correspond directly to the specification of the loop
+ expansion. It will be validated on fragment conversion. */
+ gas_assert (opcode != XTENSA_UNDEFINED);
+ if (opcode == xtensa_loop_opcode)
+ return 0;
+ if (opcode == xtensa_loopnez_opcode)
+ return 3;
+ if (opcode == xtensa_loopgtz_opcode)
+ return 6;
+ as_fatal (_("get_expanded_loop_offset: invalid opcode"));
+ return 0;
+}
+
+
+static fragS *
+get_literal_pool_location (segT seg)
+{
+ return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
+}
+
+
+static void
+set_literal_pool_location (segT seg, fragS *literal_pool_loc)
+{
+ seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
+}
+
+
+/* Set frag assembly state should be called when a new frag is
+ opened and after a frag has been closed. */
+
+static void
+xtensa_set_frag_assembly_state (fragS *fragP)
+{
+ if (!density_supported)
+ fragP->tc_frag_data.is_no_density = TRUE;
+
+ /* This function is called from subsegs_finish, which is called
+ after xtensa_end, so we can't use "use_transform" or
+ "use_schedule" here. */
+ if (!directive_state[directive_transform])
+ fragP->tc_frag_data.is_no_transform = TRUE;
+ if (directive_state[directive_longcalls])
+ fragP->tc_frag_data.use_longcalls = TRUE;
+ fragP->tc_frag_data.use_absolute_literals =
+ directive_state[directive_absolute_literals];
+ fragP->tc_frag_data.is_assembly_state_set = TRUE;
+}
+
+
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+ return ((sec->flags & SEC_DEBUGGING) == 0
+ && strcmp (sec->name, ".eh_frame") != 0);
+}
+
+
+static void
+xtensa_mark_frags_for_org (void)
+{
+ segT *seclist;
+
+ /* Walk over each fragment of all of the current segments. If we find
+ a .org frag in any of the segments, mark all frags prior to it as
+ "no transform", which will prevent linker optimizations from messing
+ up the .org distance. This should be done after
+ xtensa_find_unmarked_state_frags, because we don't want to worry here
+ about that function trashing the data we save here. */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+ segment_info_type *seginfo;
+ fragS *fragP;
+ flagword flags;
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ seginfo = seg_info (sec);
+ if (seginfo && seginfo->frchainP)
+ {
+ fragS *last_fragP = seginfo->frchainP->frch_root;
+ for (fragP = seginfo->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ /* cvt_frag_to_fill has changed the fr_type of org frags to
+ rs_fill, so use the value as cached in rs_subtype here. */
+ if (fragP->fr_subtype == RELAX_ORG)
+ {
+ while (last_fragP != fragP->fr_next)
+ {
+ last_fragP->tc_frag_data.is_no_transform = TRUE;
+ last_fragP = last_fragP->fr_next;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+static void
+xtensa_find_unmarked_state_frags (void)
+{
+ segT *seclist;
+
+ /* Walk over each fragment of all of the current segments. For each
+ unmarked fragment, mark it with the same info as the previous
+ fragment. */
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+ segment_info_type *seginfo;
+ fragS *fragP;
+ flagword flags;
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ seginfo = seg_info (sec);
+ if (seginfo && seginfo->frchainP)
+ {
+ fragS *last_fragP = 0;
+ for (fragP = seginfo->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ if (fragP->fr_fix != 0
+ && !fragP->tc_frag_data.is_assembly_state_set)
+ {
+ if (last_fragP == 0)
+ {
+ as_warn_where (fragP->fr_file, fragP->fr_line,
+ _("assembly state not set for first frag in section %s"),
+ sec->name);
+ }
+ else
+ {
+ fragP->tc_frag_data.is_assembly_state_set = TRUE;
+ fragP->tc_frag_data.is_no_density =
+ last_fragP->tc_frag_data.is_no_density;
+ fragP->tc_frag_data.is_no_transform =
+ last_fragP->tc_frag_data.is_no_transform;
+ fragP->tc_frag_data.use_longcalls =
+ last_fragP->tc_frag_data.use_longcalls;
+ fragP->tc_frag_data.use_absolute_literals =
+ last_fragP->tc_frag_data.use_absolute_literals;
+ }
+ }
+ if (fragP->tc_frag_data.is_assembly_state_set)
+ last_fragP = fragP;
+ }
+ }
+ }
+}
+
+
+static void
+xtensa_find_unaligned_branch_targets (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ flagword flags = bfd_get_section_flags (abfd, sec);
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *frag = seginfo->frchainP->frch_root;
+
+ if (flags & SEC_CODE)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+ while (frag != NULL)
+ {
+ if (frag->tc_frag_data.is_branch_target)
+ {
+ int op_size;
+ addressT branch_align, frag_addr;
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ branch_align = 1 << branch_align_power (sec);
+ frag_addr = frag->fr_address % branch_align;
+ if (frag_addr + op_size > branch_align)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned branch target: %d bytes at 0x%lx"),
+ op_size, (long) frag->fr_address);
+ }
+ frag = frag->fr_next;
+ }
+ xtensa_insnbuf_free (isa, insnbuf);
+ }
+}
+
+
+static void
+xtensa_find_unaligned_loops (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ flagword flags = bfd_get_section_flags (abfd, sec);
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *frag = seginfo->frchainP->frch_root;
+ xtensa_isa isa = xtensa_default_isa;
+
+ if (flags & SEC_CODE)
+ {
+ xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+ while (frag != NULL)
+ {
+ if (frag->tc_frag_data.is_first_loop_insn)
+ {
+ int op_size;
+ addressT frag_addr;
+ xtensa_format fmt;
+
+ if (frag->fr_fix == 0)
+ frag = next_non_empty_frag (frag);
+
+ if (frag)
+ {
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ frag_addr = frag->fr_address % xtensa_fetch_width;
+
+ if (frag_addr + op_size > xtensa_fetch_width)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned loop: %d bytes at 0x%lx"),
+ op_size, (long) frag->fr_address);
+ }
+ }
+ frag = frag->fr_next;
+ }
+ xtensa_insnbuf_free (isa, insnbuf);
+ }
+}
+
+
+static int
+xg_apply_fix_value (fixS *fixP, valueT val)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_format fmt;
+ int slot;
+ bfd_boolean alt_reloc;
+ xtensa_opcode opcode;
+ char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc)
+ || alt_reloc)
+ as_fatal (_("unexpected fix"));
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("undecodable fix"));
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+ if (opcode == XTENSA_UNDEFINED)
+ as_fatal (_("undecodable fix"));
+
+ /* CONST16 immediates are not PC-relative, despite the fact that we
+ reuse the normal PC-relative operand relocations for the low part
+ of a CONST16 operand. */
+ if (opcode == xtensa_const16_opcode)
+ return 0;
+
+ xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
+ get_relaxable_immed (opcode), val,
+ fixP->fx_file, fixP->fx_line);
+
+ xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
+ xtensa_insnbuf_to_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
+
+ return 1;
+}
+
+
+/* External Functions and Other GAS Hooks. */
+
+const char *
+xtensa_target_format (void)
+{
+ return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
+}
+
+
+void
+xtensa_file_arch_init (bfd *abfd)
+{
+ bfd_set_private_flags (abfd, 0x100 | 0x200);
+}
+
+
+void
+md_number_to_chars (char *buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
+ else
+ number_to_chars_littleendian (buf, val, n);
+}
+
+
+/* This function is called once, at assembler startup time. It should
+ set up all the tables, etc. that the MD part of the assembler will
+ need. */
+
+void
+md_begin (void)
+{
+ segT current_section = now_seg;
+ int current_subsec = now_subseg;
+ xtensa_isa isa;
+ int i;
+
+ xtensa_default_isa = xtensa_isa_init (0, 0);
+ isa = xtensa_default_isa;
+
+ linkrelax = 1;
+
+ /* Set up the literal sections. */
+ memset (&default_lit_sections, 0, sizeof (default_lit_sections));
+
+ subseg_set (current_section, current_subsec);
+
+ xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
+ xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
+ xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
+ xtensa_call4_opcode = xtensa_opcode_lookup (isa, "call4");
+ xtensa_call8_opcode = xtensa_opcode_lookup (isa, "call8");
+ xtensa_call12_opcode = xtensa_opcode_lookup (isa, "call12");
+ xtensa_callx0_opcode = xtensa_opcode_lookup (isa, "callx0");
+ xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
+ xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
+ xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
+ xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
+ xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
+ xtensa_extui_opcode = xtensa_opcode_lookup (isa, "extui");
+ xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
+ xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
+ xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
+ xtensa_j_opcode = xtensa_opcode_lookup (isa, "j");
+ xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
+ xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
+ xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
+ xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
+ xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
+ xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
+ xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
+ xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
+ xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
+ xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
+ xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
+ xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
+ xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
+ xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
+
+ for (i = 0; i < xtensa_isa_num_formats (isa); i++)
+ {
+ int format_slots = xtensa_format_num_slots (isa, i);
+ if (format_slots > config_max_slots)
+ config_max_slots = format_slots;
+ }
+
+ xg_init_vinsn (&cur_vinsn);
+
+ xtensa_num_pipe_stages = xtensa_isa_num_pipe_stages (isa);
+
+ init_op_placement_info_table ();
+
+ /* Set up the assembly state. */
+ if (!frag_now->tc_frag_data.is_assembly_state_set)
+ xtensa_set_frag_assembly_state (frag_now);
+}
+
+
+/* TC_INIT_FIX_DATA hook */
+
+void
+xtensa_init_fix_data (fixS *x)
+{
+ x->tc_fix_data.slot = 0;
+ x->tc_fix_data.X_add_symbol = NULL;
+ x->tc_fix_data.X_add_number = 0;
+}
+
+
+/* tc_frob_label hook */
+
+void
+xtensa_frob_label (symbolS *sym)
+{
+ float freq;
+
+ if (cur_vinsn.inside_bundle)
+ {
+ as_bad (_("labels are not valid inside bundles"));
+ return;
+ }
+
+ freq = get_subseg_target_freq (now_seg, now_subseg);
+
+ /* Since the label was already attached to a frag associated with the
+ previous basic block, it now needs to be reset to the current frag. */
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
+ if (generating_literals)
+ xtensa_add_literal_sym (sym);
+ else
+ xtensa_add_insn_label (sym);
+
+ if (symbol_get_tc (sym)->is_loop_target)
+ {
+ if ((get_last_insn_flags (now_seg, now_subseg)
+ & FLAG_IS_BAD_LOOPEND) != 0)
+ as_bad (_("invalid last instruction for a zero-overhead loop"));
+
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_var (rs_machine_dependent, 4, 4, RELAX_LOOP_END,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+
+ xtensa_set_frag_assembly_state (frag_now);
+ xtensa_move_labels (frag_now, 0);
+ }
+
+ /* No target aligning in the absolute section. */
+ if (now_seg != absolute_section
+ && !is_unaligned_label (sym)
+ && !generating_literals)
+ {
+ xtensa_set_frag_assembly_state (frag_now);
+
+ if (do_align_targets ())
+ frag_var (rs_machine_dependent, 0, (int) freq,
+ RELAX_DESIRE_ALIGN_IF_TARGET, frag_now->fr_symbol,
+ frag_now->fr_offset, NULL);
+ else
+ frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ xtensa_move_labels (frag_now, 0);
+ }
+
+ /* We need to mark the following properties even if we aren't aligning. */
+
+ /* If the label is already known to be a branch target, i.e., a
+ forward branch, mark the frag accordingly. Backward branches
+ are handled by xg_add_branch_and_loop_targets. */
+ if (symbol_get_tc (sym)->is_branch_target)
+ symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+
+ /* Loops only go forward, so they can be identified here. */
+ if (symbol_get_tc (sym)->is_loop_target)
+ symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
+
+ dwarf2_emit_label (sym);
+}
+
+
+/* tc_unrecognized_line hook */
+
+int
+xtensa_unrecognized_line (int ch)
+{
+ switch (ch)
+ {
+ case '{' :
+ if (cur_vinsn.inside_bundle == 0)
+ {
+ /* PR8110: Cannot emit line number info inside a FLIX bundle
+ when using --gstabs. Temporarily disable debug info. */
+ generate_lineno_debug ();
+ if (debug_type == DEBUG_STABS)
+ {
+ xt_saved_debug_type = debug_type;
+ debug_type = DEBUG_NONE;
+ }
+
+ cur_vinsn.inside_bundle = 1;
+ }
+ else
+ {
+ as_bad (_("extra opening brace"));
+ return 0;
+ }
+ break;
+
+ case '}' :
+ if (cur_vinsn.inside_bundle)
+ finish_vinsn (&cur_vinsn);
+ else
+ {
+ as_bad (_("extra closing brace"));
+ return 0;
+ }
+ break;
+ default:
+ as_bad (_("syntax error"));
+ return 0;
+ }
+ return 1;
+}
+
+
+/* md_flush_pending_output hook */
+
+void
+xtensa_flush_pending_output (void)
+{
+ /* This line fixes a bug where automatically generated gstabs info
+ separates a function label from its entry instruction, ending up
+ with the literal position between the function label and the entry
+ instruction and crashing code. It only happens with --gstabs and
+ --text-section-literals, and when several other obscure relaxation
+ conditions are met. */
+ if (outputting_stabs_line_debug)
+ return;
+
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("missing closing brace"));
+
+ /* If there is a non-zero instruction fragment, close it. */
+ if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+ frag_now->tc_frag_data.is_insn = FALSE;
+
+ xtensa_clear_insn_labels ();
+}
+
+
+/* We had an error while parsing an instruction. The string might look
+ like this: "insn arg1, arg2 }". If so, we need to see the closing
+ brace and reset some fields. Otherwise, the vinsn never gets closed
+ and the num_slots field will grow past the end of the array of slots,
+ and bad things happen. */
+
+static void
+error_reset_cur_vinsn (void)
+{
+ if (cur_vinsn.inside_bundle)
+ {
+ if (*input_line_pointer == '}'
+ || *(input_line_pointer - 1) == '}'
+ || *(input_line_pointer - 2) == '}')
+ xg_clear_vinsn (&cur_vinsn);
+ }
+}
+
+
+void
+md_assemble (char *str)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ char *opname;
+ unsigned opnamelen;
+ bfd_boolean has_underbar = FALSE;
+ char *arg_strings[MAX_INSN_ARGS];
+ int num_args;
+ TInsn orig_insn; /* Original instruction from the input. */
+
+ tinsn_init (&orig_insn);
+
+ /* Split off the opcode. */
+ opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/0123456789.");
+ opname = xmalloc (opnamelen + 1);
+ memcpy (opname, str, opnamelen);
+ opname[opnamelen] = '\0';
+
+ num_args = tokenize_arguments (arg_strings, str + opnamelen);
+ if (num_args == -1)
+ {
+ as_bad (_("syntax error"));
+ return;
+ }
+
+ if (xg_translate_idioms (&opname, &num_args, arg_strings))
+ return;
+
+ /* Check for an underbar prefix. */
+ if (*opname == '_')
+ {
+ has_underbar = TRUE;
+ opname += 1;
+ }
+
+ orig_insn.insn_type = ITYPE_INSN;
+ orig_insn.ntok = 0;
+ orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
+ orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
+
+ /* Special case: Check for "CALLXn.TLS" psuedo op. If found, grab its
+ extra argument and set the opcode to "CALLXn". */
+ if (orig_insn.opcode == XTENSA_UNDEFINED
+ && strncasecmp (opname, "callx", 5) == 0)
+ {
+ unsigned long window_size;
+ char *suffix;
+
+ window_size = strtoul (opname + 5, &suffix, 10);
+ if (suffix != opname + 5
+ && (window_size == 0
+ || window_size == 4
+ || window_size == 8
+ || window_size == 12)
+ && strcasecmp (suffix, ".tls") == 0)
+ {
+ switch (window_size)
+ {
+ case 0: orig_insn.opcode = xtensa_callx0_opcode; break;
+ case 4: orig_insn.opcode = xtensa_callx4_opcode; break;
+ case 8: orig_insn.opcode = xtensa_callx8_opcode; break;
+ case 12: orig_insn.opcode = xtensa_callx12_opcode; break;
+ }
+
+ if (num_args != 2)
+ as_bad (_("wrong number of operands for '%s'"), opname);
+ else
+ {
+ bfd_reloc_code_real_type reloc;
+ char *old_input_line_pointer;
+ expressionS *tok = &orig_insn.extra_arg;
+
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = arg_strings[num_args - 1];
+
+ expression (tok);
+ if (tok->X_op == O_symbol
+ && ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
+ == BFD_RELOC_XTENSA_TLS_CALL))
+ tok->X_op = map_suffix_reloc_to_operator (reloc);
+ else
+ as_bad (_("bad relocation expression for '%s'"), opname);
+
+ input_line_pointer = old_input_line_pointer;
+ num_args -= 1;
+ }
+ }
+ }
+
+ /* Special case: Check for "j.l" psuedo op. */
+ if (orig_insn.opcode == XTENSA_UNDEFINED
+ && strncasecmp (opname, "j.l", 3) == 0)
+ {
+ if (num_args != 2)
+ as_bad (_("wrong number of operands for '%s'"), opname);
+ else
+ {
+ char *old_input_line_pointer;
+ expressionS *tok = &orig_insn.extra_arg;
+
+ old_input_line_pointer = input_line_pointer;
+ input_line_pointer = arg_strings[num_args - 1];
+
+ expression_maybe_register (xtensa_jx_opcode, 0, tok);
+ input_line_pointer = old_input_line_pointer;
+
+ num_args -= 1;
+ orig_insn.opcode = xtensa_j_opcode;
+ }
+ }
+
+ if (orig_insn.opcode == XTENSA_UNDEFINED)
+ {
+ xtensa_format fmt = xtensa_format_lookup (isa, opname);
+ if (fmt == XTENSA_UNDEFINED)
+ {
+ as_bad (_("unknown opcode or format name '%s'"), opname);
+ error_reset_cur_vinsn ();
+ return;
+ }
+ if (!cur_vinsn.inside_bundle)
+ {
+ as_bad (_("format names only valid inside bundles"));
+ error_reset_cur_vinsn ();
+ return;
+ }
+ if (cur_vinsn.format != XTENSA_UNDEFINED)
+ as_warn (_("multiple formats specified for one bundle; using '%s'"),
+ opname);
+ cur_vinsn.format = fmt;
+ free (has_underbar ? opname - 1 : opname);
+ error_reset_cur_vinsn ();
+ return;
+ }
+
+ /* Parse the arguments. */
+ if (parse_arguments (&orig_insn, num_args, arg_strings))
+ {
+ as_bad (_("syntax error"));
+ error_reset_cur_vinsn ();
+ return;
+ }
+
+ /* Free the opcode and argument strings, now that they've been parsed. */
+ free (has_underbar ? opname - 1 : opname);
+ opname = 0;
+ while (num_args-- > 0)
+ free (arg_strings[num_args]);
+
+ /* Get expressions for invisible operands. */
+ if (get_invisible_operands (&orig_insn))
+ {
+ error_reset_cur_vinsn ();
+ return;
+ }
+
+ /* Check for the right number and type of arguments. */
+ if (tinsn_check_arguments (&orig_insn))
+ {
+ error_reset_cur_vinsn ();
+ return;
+ }
+
+ /* Record the line number for each TInsn, because a FLIX bundle may be
+ spread across multiple input lines and individual instructions may be
+ moved around in some cases. */
+ orig_insn.loc_directive_seen = dwarf2_loc_directive_seen;
+ dwarf2_where (&orig_insn.debug_line);
+ dwarf2_consume_line_info ();
+
+ xg_add_branch_and_loop_targets (&orig_insn);
+
+ /* Check that immediate value for ENTRY is >= 16. */
+ if (orig_insn.opcode == xtensa_entry_opcode && orig_insn.ntok >= 3)
+ {
+ expressionS *exp = &orig_insn.tok[2];
+ if (exp->X_op == O_constant && exp->X_add_number < 16)
+ as_warn (_("entry instruction with stack decrement < 16"));
+ }
+
+ /* Finish it off:
+ assemble_tokens (opcode, tok, ntok);
+ expand the tokens from the orig_insn into the
+ stack of instructions that will not expand
+ unless required at relaxation time. */
+
+ if (!cur_vinsn.inside_bundle)
+ emit_single_op (&orig_insn);
+ else /* We are inside a bundle. */
+ {
+ cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
+ cur_vinsn.num_slots++;
+ if (*input_line_pointer == '}'
+ || *(input_line_pointer - 1) == '}'
+ || *(input_line_pointer - 2) == '}')
+ finish_vinsn (&cur_vinsn);
+ }
+
+ /* We've just emitted a new instruction so clear the list of labels. */
+ xtensa_clear_insn_labels ();
+
+ xtensa_check_frag_count ();
+}
+
+
+/* HANDLE_ALIGN hook */
+
+/* For a .align directive, we mark the previous block with the alignment
+ information. This will be placed in the object file in the
+ property section corresponding to this section. */
+
+void
+xtensa_handle_align (fragS *fragP)
+{
+ if (linkrelax
+ && ! fragP->tc_frag_data.is_literal
+ && (fragP->fr_type == rs_align
+ || fragP->fr_type == rs_align_code)
+ && fragP->fr_offset > 0
+ && now_seg != bss_section)
+ {
+ fragP->tc_frag_data.is_align = TRUE;
+ fragP->tc_frag_data.alignment = fragP->fr_offset;
+ }
+
+ if (fragP->fr_type == rs_align_test)
+ {
+ int count;
+ count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ if (count != 0)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("unaligned entry instruction"));
+ }
+
+ if (linkrelax && fragP->fr_type == rs_org)
+ fragP->fr_subtype = RELAX_ORG;
+}
+
+
+/* TC_FRAG_INIT hook */
+
+void
+xtensa_frag_init (fragS *frag)
+{
+ xtensa_set_frag_assembly_state (frag);
+}
+
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+
+/* Round up a section size to the appropriate boundary. */
+
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
+{
+ return size; /* Byte alignment is fine. */
+}
+
+
+long
+md_pcrel_from (fixS *fixP)
+{
+ char *insn_p;
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ int opnum;
+ uint32 opnd_value;
+ xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
+ xtensa_isa isa = xtensa_default_isa;
+ valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+ bfd_boolean alt_reloc;
+
+ if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
+ return 0;
+
+ if (fixP->fx_r_type == BFD_RELOC_32_PCREL)
+ return addr;
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
+ xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) insn_p, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("bad instruction format"));
+
+ if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
+ as_fatal (_("invalid relocation"));
+
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+ /* Check for "alternate" relocations (operand not specified). None
+ of the current uses for these are really PC-relative. */
+ if (alt_reloc || opcode == xtensa_const16_opcode)
+ {
+ if (opcode != xtensa_l32r_opcode
+ && opcode != xtensa_const16_opcode)
+ as_fatal (_("invalid relocation for '%s' instruction"),
+ xtensa_opcode_name (isa, opcode));
+ return 0;
+ }
+
+ opnum = get_relaxable_immed (opcode);
+ opnd_value = 0;
+ if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
+ || xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
+ {
+ as_bad_where (fixP->fx_file,
+ fixP->fx_line,
+ _("invalid relocation for operand %d of '%s'"),
+ opnum, xtensa_opcode_name (isa, opcode));
+ return 0;
+ }
+ return 0 - opnd_value;
+}
+
+
+/* TC_FORCE_RELOCATION hook */
+
+int
+xtensa_force_relocation (fixS *fix)
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_XTENSA_ASM_EXPAND:
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ return 1;
+ default:
+ break;
+ }
+
+ if (linkrelax && fix->fx_addsy
+ && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+
+/* TC_VALIDATE_FIX_SUB hook */
+
+int
+xtensa_validate_fix_sub (fixS *fix)
+{
+ segT add_symbol_segment, sub_symbol_segment;
+
+ /* The difference of two symbols should be resolved by the assembler when
+ linkrelax is not set. If the linker may relax the section containing
+ the symbols, then an Xtensa DIFF relocation must be generated so that
+ the linker knows to adjust the difference value. */
+ if (!linkrelax || fix->fx_addsy == NULL)
+ return 0;
+
+ /* Make sure both symbols are in the same segment, and that segment is
+ "normal" and relaxable. If the segment is not "normal", then the
+ fix is not valid. If the segment is not "relaxable", then the fix
+ should have been handled earlier. */
+ add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+ if (! SEG_NORMAL (add_symbol_segment) ||
+ ! relaxable_section (add_symbol_segment))
+ return 0;
+ sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+ return (sub_symbol_segment == add_symbol_segment);
+}
+
+
+/* NO_PSEUDO_DOT hook */
+
+/* This function has nothing to do with pseudo dots, but this is the
+ nearest macro to where the check needs to take place. FIXME: This
+ seems wrong. */
+
+bfd_boolean
+xtensa_check_inside_bundle (void)
+{
+ if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
+ as_bad (_("directives are not valid inside bundles"));
+
+ /* This function must always return FALSE because it is called via a
+ macro that has nothing to do with bundling. */
+ return FALSE;
+}
+
+
+/* md_elf_section_change_hook */
+
+void
+xtensa_elf_section_change_hook (void)
+{
+ /* Set up the assembly state. */
+ if (!frag_now->tc_frag_data.is_assembly_state_set)
+ xtensa_set_frag_assembly_state (frag_now);
+}
+
+
+/* tc_fix_adjustable hook */
+
+bfd_boolean
+xtensa_fix_adjustable (fixS *fixP)
+{
+ /* We need the symbol name for the VTABLE entries. */
+ if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ return 1;
+}
+
+
+/* tc_symbol_new_hook */
+
+symbolS *expr_symbols = NULL;
+
+void
+xtensa_symbol_new_hook (symbolS *sym)
+{
+ if (is_leb128_expr && S_GET_SEGMENT (sym) == expr_section)
+ {
+ symbol_get_tc (sym)->next_expr_symbol = expr_symbols;
+ expr_symbols = sym;
+ }
+}
+
+
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
+{
+ char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT val = 0;
+
+ /* Subtracted symbols are only allowed for a few relocation types, and
+ unless linkrelax is enabled, they should not make it to this point. */
+ if (fixP->fx_subsy && !(linkrelax && (fixP->fx_r_type == BFD_RELOC_32
+ || fixP->fx_r_type == BFD_RELOC_16
+ || fixP->fx_r_type == BFD_RELOC_8)))
+ as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32_PCREL:
+ case BFD_RELOC_32:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ if (fixP->fx_subsy)
+ {
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+ fixP->fx_signed = 1;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+ fixP->fx_signed = 1;
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+ fixP->fx_signed = 1;
+ break;
+ default:
+ break;
+ }
+
+ val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ - S_GET_VALUE (fixP->fx_subsy));
+
+ /* The difference value gets written out, and the DIFF reloc
+ identifies the address of the subtracted symbol (i.e., the one
+ with the lowest address). */
+ *valP = val;
+ fixP->fx_offset -= val;
+ fixP->fx_subsy = NULL;
+ }
+ else if (! fixP->fx_addsy)
+ {
+ val = *valP;
+ fixP->fx_done = 1;
+ }
+ /* fall through */
+
+ case BFD_RELOC_XTENSA_PLT:
+ md_number_to_chars (fixpos, val, fixP->fx_size);
+ fixP->fx_no_overflow = 0; /* Use the standard overflow check. */
+ break;
+
+ case BFD_RELOC_XTENSA_TLSDESC_FN:
+ case BFD_RELOC_XTENSA_TLSDESC_ARG:
+ case BFD_RELOC_XTENSA_TLS_TPOFF:
+ case BFD_RELOC_XTENSA_TLS_DTPOFF:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ md_number_to_chars (fixpos, 0, fixP->fx_size);
+ fixP->fx_no_overflow = 0; /* Use the standard overflow check. */
+ break;
+
+ case BFD_RELOC_XTENSA_SLOT0_OP:
+ case BFD_RELOC_XTENSA_SLOT1_OP:
+ case BFD_RELOC_XTENSA_SLOT2_OP:
+ case BFD_RELOC_XTENSA_SLOT3_OP:
+ case BFD_RELOC_XTENSA_SLOT4_OP:
+ case BFD_RELOC_XTENSA_SLOT5_OP:
+ case BFD_RELOC_XTENSA_SLOT6_OP:
+ case BFD_RELOC_XTENSA_SLOT7_OP:
+ case BFD_RELOC_XTENSA_SLOT8_OP:
+ case BFD_RELOC_XTENSA_SLOT9_OP:
+ case BFD_RELOC_XTENSA_SLOT10_OP:
+ case BFD_RELOC_XTENSA_SLOT11_OP:
+ case BFD_RELOC_XTENSA_SLOT12_OP:
+ case BFD_RELOC_XTENSA_SLOT13_OP:
+ case BFD_RELOC_XTENSA_SLOT14_OP:
+ if (linkrelax)
+ {
+ /* Write the tentative value of a PC-relative relocation to a
+ local symbol into the instruction. The value will be ignored
+ by the linker, and it makes the object file disassembly
+ readable when all branch targets are encoded in relocations. */
+
+ gas_assert (fixP->fx_addsy);
+ if (S_GET_SEGMENT (fixP->fx_addsy) == seg
+ && !S_FORCE_RELOC (fixP->fx_addsy, 1))
+ {
+ val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ - md_pcrel_from (fixP));
+ (void) xg_apply_fix_value (fixP, val);
+ }
+ }
+ else if (! fixP->fx_addsy)
+ {
+ val = *valP;
+ if (xg_apply_fix_value (fixP, val))
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_XTENSA_ASM_EXPAND:
+ case BFD_RELOC_XTENSA_TLS_FUNC:
+ case BFD_RELOC_XTENSA_TLS_ARG:
+ case BFD_RELOC_XTENSA_TLS_CALL:
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ /* These all need to be resolved at link-time. Do nothing now. */
+ break;
+
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ fixP->fx_done = 0;
+ break;
+
+ default:
+ as_bad (_("unhandled local relocation fix %s"),
+ bfd_get_reloc_code_name (fixP->fx_r_type));
+ }
+}
+
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
+}
+
+
+int
+md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
+{
+ return total_frag_text_expansion (fragP);
+}
+
+
+/* Translate internal representation of relocation info to BFD target
+ format. */
+
+arelent *
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
+{
+ arelent *reloc;
+
+ reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+ /* Make sure none of our internal relocations make it this far.
+ They'd better have been fully resolved by this point. */
+ gas_assert ((int) fixp->fx_r_type > 0);
+
+ reloc->addend = fixp->fx_offset;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error; cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+
+ return reloc;
+}
+
+
+/* Checks for resource conflicts between instructions. */
+
+/* The func unit stuff could be implemented as bit-vectors rather
+ than the iterative approach here. If it ends up being too
+ slow, we will switch it. */
+
+resource_table *
+new_resource_table (void *data,
+ int cycles,
+ int nu,
+ unit_num_copies_func uncf,
+ opcode_num_units_func onuf,
+ opcode_funcUnit_use_unit_func ouuf,
+ opcode_funcUnit_use_stage_func ousf)
+{
+ int i;
+ resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
+ rt->data = data;
+ rt->cycles = cycles;
+ rt->allocated_cycles = cycles;
+ rt->num_units = nu;
+ rt->unit_num_copies = uncf;
+ rt->opcode_num_units = onuf;
+ rt->opcode_unit_use = ouuf;
+ rt->opcode_unit_stage = ousf;
+
+ rt->units = (unsigned char **) xcalloc (cycles, sizeof (unsigned char *));
+ for (i = 0; i < cycles; i++)
+ rt->units[i] = (unsigned char *) xcalloc (nu, sizeof (unsigned char));
+
+ return rt;
+}
+
+
+void
+clear_resource_table (resource_table *rt)
+{
+ int i, j;
+ for (i = 0; i < rt->allocated_cycles; i++)
+ for (j = 0; j < rt->num_units; j++)
+ rt->units[i][j] = 0;
+}
+
+
+/* We never shrink it, just fake it into thinking so. */
+
+void
+resize_resource_table (resource_table *rt, int cycles)
+{
+ int i, old_cycles;
+
+ rt->cycles = cycles;
+ if (cycles <= rt->allocated_cycles)
+ return;
+
+ old_cycles = rt->allocated_cycles;
+ rt->allocated_cycles = cycles;
+
+ rt->units = xrealloc (rt->units,
+ rt->allocated_cycles * sizeof (unsigned char *));
+ for (i = 0; i < old_cycles; i++)
+ rt->units[i] = xrealloc (rt->units[i],
+ rt->num_units * sizeof (unsigned char));
+ for (i = old_cycles; i < cycles; i++)
+ rt->units[i] = xcalloc (rt->num_units, sizeof (unsigned char));
+}
+
+
+bfd_boolean
+resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ int copies_in_use = rt->units[stage + cycle][unit];
+ int copies = (rt->unit_num_copies) (rt->data, unit);
+ if (copies_in_use >= copies)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ /* Note that this allows resources to be oversubscribed. That's
+ essential to the way the optional scheduler works.
+ resources_available reports when a resource is over-subscribed,
+ so it's easy to tell. */
+ rt->units[stage + cycle][unit]++;
+ }
+}
+
+
+void
+release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ gas_assert (rt->units[stage + cycle][unit] > 0);
+ rt->units[stage + cycle][unit]--;
+ }
+}
+
+
+/* Wrapper functions make parameterized resource reservation
+ more convenient. */
+
+int
+opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->unit;
+}
+
+
+int
+opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->stage;
+}
+
+
+/* Note that this function does not check issue constraints, but
+ solely whether the hardware is available to execute the given
+ instructions together. It also doesn't check if the tinsns
+ write the same state, or access the same tieports. That is
+ checked by check_t1_t2_reads_and_writes. */
+
+static bfd_boolean
+resources_conflict (vliw_insn *vinsn)
+{
+ int i;
+ static resource_table *rt = NULL;
+
+ /* This is the most common case by far. Optimize it. */
+ if (vinsn->num_slots == 1)
+ return FALSE;
+
+ if (rt == NULL)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ rt = new_resource_table
+ (isa, xtensa_num_pipe_stages,
+ xtensa_isa_num_funcUnits (isa),
+ (unit_num_copies_func) xtensa_funcUnit_num_copies,
+ (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
+ opcode_funcUnit_use_unit,
+ opcode_funcUnit_use_stage);
+ }
+
+ clear_resource_table (rt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (!resources_available (rt, vinsn->slots[i].opcode, 0))
+ return TRUE;
+ reserve_resources (rt, vinsn->slots[i].opcode, 0);
+ }
+
+ return FALSE;
+}
+
+
+/* finish_vinsn, emit_single_op and helper functions. */
+
+static bfd_boolean find_vinsn_conflicts (vliw_insn *);
+static xtensa_format xg_find_narrowest_format (vliw_insn *);
+static void xg_assemble_vliw_tokens (vliw_insn *);
+
+
+/* We have reached the end of a bundle; emit into the frag. */
+
+static void
+finish_vinsn (vliw_insn *vinsn)
+{
+ IStack slotstack;
+ int i;
+ char *file_name;
+ unsigned line;
+
+ if (find_vinsn_conflicts (vinsn))
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ if (xtensa_format_num_slots (xtensa_default_isa, vinsn->format) > 1
+ && produce_flix == FLIX_NONE)
+ {
+ as_bad (_("The option \"--no-allow-flix\" prohibits multi-slot flix."));
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (vinsn->format == XTENSA_UNDEFINED)
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line,
+ _("couldn't find a valid instruction format"));
+ fprintf (stderr, _(" ops were: "));
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, _(" %s;"),
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, _("\n"));
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (vinsn->num_slots
+ != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
+ {
+ as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
+ xtensa_format_name (xtensa_default_isa, vinsn->format),
+ xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
+ vinsn->num_slots);
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
+ {
+ symbolS *lit_sym = NULL;
+ int j;
+ bfd_boolean e = FALSE;
+ bfd_boolean saved_density = density_supported;
+
+ /* We don't want to narrow ops inside multi-slot bundles. */
+ if (vinsn->num_slots > 1)
+ density_supported = FALSE;
+
+ istack_init (&slotstack);
+ if (vinsn->slots[i].opcode == xtensa_nop_opcode)
+ {
+ vinsn->slots[i].opcode =
+ xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+ vinsn->slots[i].ntok = 0;
+ }
+
+ if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
+ {
+ e = TRUE;
+ continue;
+ }
+
+ density_supported = saved_density;
+
+ if (e)
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (j = 0; j < slotstack.ninsn; j++)
+ {
+ TInsn *insn = &slotstack.insn[j];
+ if (insn->insn_type == ITYPE_LITERAL)
+ {
+ gas_assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ }
+ else
+ {
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (j != slotstack.ninsn - 1)
+ emit_single_op (insn);
+ }
+ }
+
+ if (vinsn->num_slots > 1)
+ {
+ if (opcode_fits_format_slot
+ (slotstack.insn[slotstack.ninsn - 1].opcode,
+ vinsn->format, i))
+ {
+ vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
+ }
+ else
+ {
+ emit_single_op (&slotstack.insn[slotstack.ninsn - 1]);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->slots[i].opcode = xtensa_nop_opcode;
+ else
+ vinsn->slots[i].opcode
+ = xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+
+ vinsn->slots[i].ntok = 0;
+ }
+ }
+ else
+ {
+ vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
+ vinsn->format = XTENSA_UNDEFINED;
+ }
+ }
+ }
+
+ /* Now check resource conflicts on the modified bundle. */
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ xg_assemble_vliw_tokens (vinsn);
+
+ xg_clear_vinsn (vinsn);
+
+ xtensa_check_frag_count ();
+}
+
+
+/* Given an vliw instruction, what conflicts are there in register
+ usage and in writes to states and queues?
+
+ This function does two things:
+ 1. Reports an error when a vinsn contains illegal combinations
+ of writes to registers states or queues.
+ 2. Marks individual tinsns as not relaxable if the combination
+ contains antidependencies.
+
+ Job 2 handles things like swap semantics in instructions that need
+ to be relaxed. For example,
+
+ addi a0, a1, 100000
+
+ normally would be relaxed to
+
+ l32r a0, some_label
+ add a0, a1, a0
+
+ _but_, if the above instruction is bundled with an a0 reader, e.g.,
+
+ { addi a0, a1, 10000 ; add a2, a0, a4 ; }
+
+ then we can't relax it into
+
+ l32r a0, some_label
+ { add a0, a1, a0 ; add a2, a0, a4 ; }
+
+ because the value of a0 is trashed before the second add can read it. */
+
+static char check_t1_t2_reads_and_writes (TInsn *, TInsn *);
+
+static bfd_boolean
+find_vinsn_conflicts (vliw_insn *vinsn)
+{
+ int i, j;
+ int branches = 0;
+ xtensa_isa isa = xtensa_default_isa;
+
+ gas_assert (!past_xtensa_end);
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+ if (op1->is_specific_opcode)
+ op1->keep_wide = TRUE;
+ else
+ op1->keep_wide = FALSE;
+ }
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+
+ if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
+ branches++;
+
+ for (j = 0; j < vinsn->num_slots; j++)
+ {
+ if (i != j)
+ {
+ TInsn *op2 = &vinsn->slots[j];
+ char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
+ switch (conflict_type)
+ {
+ case 'c':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'd':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'e':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same port"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'f':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile port accesses"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ default:
+ /* Everything is OK. */
+ break;
+ }
+ op2->is_specific_opcode = (op2->is_specific_opcode
+ || conflict_type == 'a');
+ }
+ }
+ }
+
+ if (branches > 1)
+ {
+ as_bad (_("multiple branches or jumps in the same bundle"));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Check how the state used by t1 and t2 relate.
+ Cases found are:
+
+ case A: t1 reads a register t2 writes (an antidependency within a bundle)
+ case B: no relationship between what is read and written (both could
+ read the same reg though)
+ case C: t1 writes a register t2 writes (a register conflict within a
+ bundle)
+ case D: t1 writes a state that t2 also writes
+ case E: t1 writes a tie queue that t2 also writes
+ case F: two volatile queue accesses
+*/
+
+static char
+check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_regfile t1_regfile, t2_regfile;
+ int t1_reg, t2_reg;
+ int t1_base_reg, t1_last_reg;
+ int t2_base_reg, t2_last_reg;
+ char t1_inout, t2_inout;
+ int i, j;
+ char conflict = 'b';
+ int t1_states;
+ int t2_states;
+ int t1_interfaces;
+ int t2_interfaces;
+ bfd_boolean t1_volatile = FALSE;
+ bfd_boolean t2_volatile = FALSE;
+
+ /* Check registers. */
+ for (j = 0; j < t2->ntok; j++)
+ {
+ if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
+ continue;
+
+ t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
+ t2_base_reg = t2->tok[j].X_add_number;
+ t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
+
+ for (i = 0; i < t1->ntok; i++)
+ {
+ if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
+ continue;
+
+ t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
+
+ if (t1_regfile != t2_regfile)
+ continue;
+
+ t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
+ t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
+
+ if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
+ || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
+ {
+ if (t1_inout == 'm' || t1_inout == 'o'
+ || t2_inout == 'm' || t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+ }
+
+ t1_base_reg = t1->tok[i].X_add_number;
+ t1_last_reg = (t1_base_reg
+ + xtensa_operand_num_regs (isa, t1->opcode, i));
+
+ for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
+ {
+ for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
+ {
+ if (t1_reg != t2_reg)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'c';
+ }
+ }
+ }
+ }
+
+ /* Check states. */
+ t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
+ t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
+ for (j = 0; j < t2_states; j++)
+ {
+ xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
+ t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
+ for (i = 0; i < t1_states; i++)
+ {
+ xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
+ t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
+ if (t1_so != t2_so || xtensa_state_is_shared_or (isa, t1_so) == 1)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'd';
+ }
+ }
+
+ /* Check tieports. */
+ t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
+ t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
+ for (j = 0; j < t2_interfaces; j++)
+ {
+ xtensa_interface t2_int
+ = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
+ int t2_class = xtensa_interface_class_id (isa, t2_int);
+
+ t2_inout = xtensa_interface_inout (isa, t2_int);
+ if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
+ t2_volatile = TRUE;
+
+ for (i = 0; i < t1_interfaces; i++)
+ {
+ xtensa_interface t1_int
+ = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
+ int t1_class = xtensa_interface_class_id (isa, t1_int);
+
+ t1_inout = xtensa_interface_inout (isa, t1_int);
+ if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
+ t1_volatile = TRUE;
+
+ if (t1_volatile && t2_volatile && (t1_class == t2_class))
+ return 'f';
+
+ if (t1_int != t2_int)
+ continue;
+
+ if (t2_inout == 'i' && t1_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'e';
+ }
+ }
+
+ return conflict;
+}
+
+
+static xtensa_format
+xg_find_narrowest_format (vliw_insn *vinsn)
+{
+ /* Right now we assume that the ops within the vinsn are properly
+ ordered for the slots that the programmer wanted them in. In
+ other words, we don't rearrange the ops in hopes of finding a
+ better format. The scheduler handles that. */
+
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format format;
+ xtensa_opcode nop_opcode = xtensa_nop_opcode;
+
+ if (vinsn->num_slots == 1)
+ return xg_get_single_format (vinsn->slots[0].opcode);
+
+ for (format = 0; format < xtensa_isa_num_formats (isa); format++)
+ {
+ vliw_insn v_copy;
+ xg_copy_vinsn (&v_copy, vinsn);
+ if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
+ {
+ int slot;
+ int fit = 0;
+ for (slot = 0; slot < v_copy.num_slots; slot++)
+ {
+ if (v_copy.slots[slot].opcode == nop_opcode)
+ {
+ v_copy.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, format, slot);
+ v_copy.slots[slot].ntok = 0;
+ }
+
+ if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
+ format, slot))
+ fit++;
+ else if (v_copy.num_slots > 1)
+ {
+ TInsn widened;
+ /* Try the widened version. */
+ if (!v_copy.slots[slot].keep_wide
+ && !v_copy.slots[slot].is_specific_opcode
+ && xg_is_single_relaxable_insn (&v_copy.slots[slot],
+ &widened, TRUE)
+ && opcode_fits_format_slot (widened.opcode,
+ format, slot))
+ {
+ v_copy.slots[slot] = widened;
+ fit++;
+ }
+ }
+ }
+ if (fit == v_copy.num_slots)
+ {
+ xg_copy_vinsn (vinsn, &v_copy);
+ xtensa_format_encode (isa, format, vinsn->insnbuf);
+ vinsn->format = format;
+ break;
+ }
+ }
+ }
+
+ if (format == xtensa_isa_num_formats (isa))
+ return XTENSA_UNDEFINED;
+
+ return format;
+}
+
+
+/* Return the additional space needed in a frag
+ for possible relaxations of any ops in a VLIW insn.
+ Also fill out the relaxations that might be required of
+ each tinsn in the vinsn. */
+
+static int
+relaxation_requirements (vliw_insn *vinsn, bfd_boolean *pfinish_frag)
+{
+ bfd_boolean finish_frag = FALSE;
+ int extra_space = 0;
+ int slot;
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ if (!tinsn_has_symbolic_operands (tinsn))
+ {
+ /* A narrow instruction could be widened later to help
+ alignment issues. */
+ if (xg_is_single_relaxable_insn (tinsn, 0, TRUE)
+ && !tinsn->is_specific_opcode
+ && vinsn->num_slots == 1)
+ {
+ /* Difference in bytes between narrow and wide insns... */
+ extra_space += 1;
+ tinsn->subtype = RELAX_NARROW;
+ }
+ }
+ else
+ {
+ if (workaround_b_j_loop_end
+ && tinsn->opcode == xtensa_jx_opcode
+ && use_transform ())
+ {
+ /* Add 2 of these. */
+ extra_space += 3; /* for the nop size */
+ tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
+ }
+
+ /* Need to assemble it with space for the relocation. */
+ if (xg_is_relaxable_insn (tinsn, 0)
+ && !tinsn->is_specific_opcode)
+ {
+ int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
+ int max_literal_size =
+ xg_get_max_insn_widen_literal_size (tinsn->opcode);
+
+ tinsn->literal_space = max_literal_size;
+
+ tinsn->subtype = RELAX_IMMED;
+ extra_space += max_size;
+ }
+ else
+ {
+ /* A fix record will be added for this instruction prior
+ to relaxation, so make it end the frag. */
+ finish_frag = TRUE;
+ }
+ }
+ }
+ *pfinish_frag = finish_frag;
+ return extra_space;
+}
+
+
+static void
+bundle_tinsn (TInsn *tinsn, vliw_insn *vinsn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int slot, chosen_slot;
+
+ vinsn->format = xg_get_single_format (tinsn->opcode);
+ gas_assert (vinsn->format != XTENSA_UNDEFINED);
+ vinsn->num_slots = xtensa_format_num_slots (isa, vinsn->format);
+
+ chosen_slot = xg_get_single_slot (tinsn->opcode);
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ if (slot == chosen_slot)
+ vinsn->slots[slot] = *tinsn;
+ else
+ {
+ vinsn->slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, vinsn->format, slot);
+ vinsn->slots[slot].ntok = 0;
+ vinsn->slots[slot].insn_type = ITYPE_INSN;
+ }
+ }
+}
+
+
+static bfd_boolean
+emit_single_op (TInsn *orig_insn)
+{
+ int i;
+ IStack istack; /* put instructions into here */
+ symbolS *lit_sym = NULL;
+ symbolS *label_sym = NULL;
+
+ istack_init (&istack);
+
+ /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
+ Because the scheduling and bundling characteristics of movi and
+ l32r or const16 are so different, we can do much better if we relax
+ it prior to scheduling and bundling, rather than after. */
+ if ((orig_insn->opcode == xtensa_movi_opcode
+ || orig_insn->opcode == xtensa_movi_n_opcode)
+ && !cur_vinsn.inside_bundle
+ && (orig_insn->tok[1].X_op == O_symbol
+ || orig_insn->tok[1].X_op == O_pltrel
+ || orig_insn->tok[1].X_op == O_tlsfunc
+ || orig_insn->tok[1].X_op == O_tlsarg
+ || orig_insn->tok[1].X_op == O_tpoff
+ || orig_insn->tok[1].X_op == O_dtpoff)
+ && !orig_insn->is_specific_opcode && use_transform ())
+ xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
+ else
+ if (xg_expand_assembly_insn (&istack, orig_insn))
+ return TRUE;
+
+ for (i = 0; i < istack.ninsn; i++)
+ {
+ TInsn *insn = &istack.insn[i];
+ switch (insn->insn_type)
+ {
+ case ITYPE_LITERAL:
+ gas_assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ break;
+ case ITYPE_LABEL:
+ {
+ static int relaxed_sym_idx = 0;
+ char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
+ sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
+ colon (label);
+ gas_assert (label_sym == NULL);
+ label_sym = symbol_find_or_make (label);
+ gas_assert (label_sym);
+ free (label);
+ }
+ break;
+ case ITYPE_INSN:
+ {
+ vliw_insn v;
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (label_sym)
+ xg_resolve_labels (insn, label_sym);
+ xg_init_vinsn (&v);
+ bundle_tinsn (insn, &v);
+ finish_vinsn (&v);
+ xg_free_vinsn (&v);
+ }
+ break;
+ default:
+ gas_assert (0);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+static int
+total_frag_text_expansion (fragS *fragP)
+{
+ int slot;
+ int total_expansion = 0;
+
+ for (slot = 0; slot < config_max_slots; slot++)
+ total_expansion += fragP->tc_frag_data.text_expansion[slot];
+
+ return total_expansion;
+}
+
+
+/* Emit a vliw instruction to the current fragment. */
+
+static void
+xg_assemble_vliw_tokens (vliw_insn *vinsn)
+{
+ bfd_boolean finish_frag;
+ bfd_boolean is_jump = FALSE;
+ bfd_boolean is_branch = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
+ int insn_size;
+ int extra_space;
+ char *f = NULL;
+ int slot;
+ struct dwarf2_line_info debug_line;
+ bfd_boolean loc_directive_seen = FALSE;
+ TInsn *tinsn;
+
+ memset (&debug_line, 0, sizeof (struct dwarf2_line_info));
+
+ if (generating_literals)
+ {
+ static int reported = 0;
+ if (reported < 4)
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("cannot assemble into a literal fragment"));
+ if (reported == 3)
+ as_bad (_("..."));
+ reported++;
+ return;
+ }
+
+ if (frag_now_fix () != 0
+ && (! frag_now->tc_frag_data.is_insn
+ || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ || (!use_transform ()) != frag_now->tc_frag_data.is_no_transform
+ || (directive_state[directive_longcalls]
+ != frag_now->tc_frag_data.use_longcalls)
+ || (directive_state[directive_absolute_literals]
+ != frag_now->tc_frag_data.use_absolute_literals)))
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (workaround_a0_b_retw
+ && vinsn->num_slots == 1
+ && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
+ && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
+ && use_transform ())
+ {
+ has_a0_b_retw = TRUE;
+
+ /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
+ After the first assembly pass we will check all of them and
+ add a nop if needed. */
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ tinsn = &vinsn->slots[slot];
+
+ /* See if the instruction implies an aligned section. */
+ if (xtensa_opcode_is_loop (isa, tinsn->opcode) == 1)
+ record_alignment (now_seg, 2);
+
+ /* Determine the best line number for debug info. */
+ if ((tinsn->loc_directive_seen || !loc_directive_seen)
+ && (tinsn->debug_line.filenum != debug_line.filenum
+ || tinsn->debug_line.line < debug_line.line
+ || tinsn->debug_line.column < debug_line.column))
+ debug_line = tinsn->debug_line;
+ if (tinsn->loc_directive_seen)
+ loc_directive_seen = TRUE;
+ }
+
+ /* Special cases for instructions that force an alignment... */
+ /* None of these opcodes are bundle-able. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
+ {
+ int max_fill;
+
+ /* Remember the symbol that marks the end of the loop in the frag
+ that marks the start of the loop. This way we can easily find
+ the end of the loop at the beginning, without adding special code
+ to mark the loop instructions themselves. */
+ symbolS *target_sym = NULL;
+ if (vinsn->slots[0].tok[1].X_op == O_symbol)
+ target_sym = vinsn->slots[0].tok[1].X_add_symbol;
+
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+
+ max_fill = get_text_align_max_fill_size
+ (get_text_align_power (xtensa_fetch_width),
+ TRUE, frag_now->tc_frag_data.is_no_density);
+
+ if (use_transform ())
+ frag_var (rs_machine_dependent, max_fill, max_fill,
+ RELAX_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
+ else
+ frag_var (rs_machine_dependent, 0, 0,
+ RELAX_CHECK_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (vinsn->slots[0].opcode == xtensa_entry_opcode
+ && !vinsn->slots[0].is_specific_opcode)
+ {
+ xtensa_mark_literal_pool_location ();
+ xtensa_move_labels (frag_now, 0);
+ frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
+ }
+
+ if (vinsn->num_slots == 1)
+ {
+ if (workaround_a0_b_retw && use_transform ())
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
+ is_register_writer (&vinsn->slots[0], "a", 0));
+
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
+ is_bad_loopend_opcode (&vinsn->slots[0]));
+ }
+ else
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
+
+ insn_size = xtensa_format_length (isa, vinsn->format);
+
+ extra_space = relaxation_requirements (vinsn, &finish_frag);
+
+ /* vinsn_to_insnbuf will produce the error. */
+ if (vinsn->format != XTENSA_UNDEFINED)
+ {
+ f = frag_more (insn_size + extra_space);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ }
+
+ vinsn_to_insnbuf (vinsn, f, frag_now, FALSE);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ return;
+
+ xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
+
+ if (debug_type == DEBUG_DWARF2 || loc_directive_seen)
+ dwarf2_gen_line_info (frag_now_fix () - (insn_size + extra_space),
+ &debug_line);
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ tinsn = &vinsn->slots[slot];
+ frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
+ frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
+ frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
+ frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
+ if (tinsn->literal_space != 0)
+ xg_assemble_literal_space (tinsn->literal_space, slot);
+ frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
+
+ if (tinsn->subtype == RELAX_NARROW)
+ gas_assert (vinsn->num_slots == 1);
+ if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
+ is_jump = TRUE;
+ if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
+ is_branch = TRUE;
+
+ if (tinsn->subtype || tinsn->symbol || tinsn->offset
+ || tinsn->literal_frag || is_jump || is_branch)
+ finish_frag = TRUE;
+ }
+
+ if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ frag_now->tc_frag_data.is_specific_opcode = TRUE;
+
+ if (finish_frag)
+ {
+ frag_variant (rs_machine_dependent,
+ extra_space, extra_space, RELAX_SLOTS,
+ frag_now->fr_symbol, frag_now->fr_offset, f);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ /* Special cases for loops:
+ close_loop_end should be inserted AFTER short_loop.
+ Make sure that CLOSE loops are processed BEFORE short_loops
+ when converting them. */
+
+ /* "short_loop": Add a NOP if the loop is < 4 bytes. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1
+ && !vinsn->slots[0].is_specific_opcode)
+ {
+ if (workaround_short_loop && use_transform ())
+ {
+ maybe_has_short_loop = TRUE;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ }
+
+ /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
+ loop at least 12 bytes away from another loop's end. */
+ if (workaround_close_loop_end && use_transform ())
+ {
+ maybe_has_close_loop_end = TRUE;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 12, 12,
+ RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ }
+ }
+
+ if (use_transform ())
+ {
+ if (is_jump)
+ {
+ gas_assert (finish_frag);
+ frag_var (rs_machine_dependent,
+ xtensa_fetch_width, xtensa_fetch_width,
+ RELAX_UNREACHABLE,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ xtensa_maybe_create_trampoline_frag ();
+ }
+ else if (is_branch && do_align_targets ())
+ {
+ gas_assert (finish_frag);
+ frag_var (rs_machine_dependent,
+ xtensa_fetch_width, xtensa_fetch_width,
+ RELAX_MAYBE_UNREACHABLE,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_var (rs_machine_dependent,
+ 0, 0,
+ RELAX_MAYBE_DESIRE_ALIGN,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+ }
+
+ /* Now, if the original opcode was a call... */
+ if (do_align_targets ()
+ && xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
+ {
+ float freq = get_subseg_total_freq (now_seg, now_subseg);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+}
+
+
+/* xtensa_end and helper functions. */
+
+static void xtensa_cleanup_align_frags (void);
+static void xtensa_fix_target_frags (void);
+static void xtensa_mark_narrow_branches (void);
+static void xtensa_mark_zcl_first_insns (void);
+static void xtensa_mark_difference_of_two_symbols (void);
+static void xtensa_fix_a0_b_retw_frags (void);
+static void xtensa_fix_b_j_loop_end_frags (void);
+static void xtensa_fix_close_loop_end_frags (void);
+static void xtensa_fix_short_loop_frags (void);
+static void xtensa_sanity_check (void);
+static void xtensa_add_config_info (void);
+
+void
+xtensa_end (void)
+{
+ directive_balance ();
+ xtensa_flush_pending_output ();
+
+ past_xtensa_end = TRUE;
+
+ xtensa_move_literals ();
+
+ xtensa_reorder_segments ();
+ xtensa_cleanup_align_frags ();
+ xtensa_fix_target_frags ();
+ if (workaround_a0_b_retw && has_a0_b_retw)
+ xtensa_fix_a0_b_retw_frags ();
+ if (workaround_b_j_loop_end)
+ xtensa_fix_b_j_loop_end_frags ();
+
+ /* "close_loop_end" should be processed BEFORE "short_loop". */
+ if (workaround_close_loop_end && maybe_has_close_loop_end)
+ xtensa_fix_close_loop_end_frags ();
+
+ if (workaround_short_loop && maybe_has_short_loop)
+ xtensa_fix_short_loop_frags ();
+ if (align_targets)
+ xtensa_mark_narrow_branches ();
+ xtensa_mark_zcl_first_insns ();
+
+ xtensa_sanity_check ();
+
+ xtensa_add_config_info ();
+
+ xtensa_check_frag_count ();
+}
+
+
+struct trampoline_frag
+{
+ struct trampoline_frag *next;
+ bfd_boolean needs_jump_around;
+ fragS *fragP;
+ fixS *fixP;
+};
+
+struct trampoline_seg
+{
+ struct trampoline_seg *next;
+ asection *seg;
+ struct trampoline_frag trampoline_list;
+};
+
+static struct trampoline_seg trampoline_seg_list;
+#define J_RANGE (128 * 1024)
+
+static int unreachable_count = 0;
+
+
+static void
+xtensa_maybe_create_trampoline_frag (void)
+{
+ if (!use_trampolines)
+ return;
+
+ /* We create an area for possible trampolines every 10 unreachable frags.
+ These are preferred over the ones not preceded by an unreachable frag,
+ because we don't have to jump around them. This function is called after
+ each RELAX_UNREACHABLE frag is created. */
+
+ if (++unreachable_count > 10)
+ {
+ xtensa_create_trampoline_frag (FALSE);
+ clear_frag_count ();
+ unreachable_count = 0;
+ }
+}
+
+static void
+xtensa_check_frag_count (void)
+{
+ if (!use_trampolines || frag_now->tc_frag_data.is_no_transform)
+ return;
+
+ /* We create an area for possible trampolines every 8000 frags or so. This
+ is an estimate based on the max range of a "j" insn (+/-128K) divided
+ by a typical frag byte count (16), minus a few for safety. This function
+ is called after each source line is processed. */
+
+ if (get_frag_count () > 8000)
+ {
+ xtensa_create_trampoline_frag (TRUE);
+ clear_frag_count ();
+ unreachable_count = 0;
+ }
+}
+
+static xtensa_insnbuf trampoline_buf = NULL;
+static xtensa_insnbuf trampoline_slotbuf = NULL;
+
+#define TRAMPOLINE_FRAG_SIZE 3000
+
+static void
+xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
+{
+ /* Emit a frag where we can place intermediate jump instructions,
+ in case we need to jump farther than 128K bytes.
+ Each jump instruction takes three bytes.
+ We allocate enough for 1000 trampolines in each frag.
+ If that's not enough, oh well. */
+
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+ struct trampoline_frag *tf;
+ char *varP;
+ fragS *fragP;
+ int size = TRAMPOLINE_FRAG_SIZE;
+
+ for ( ; ts; ts = ts->next)
+ {
+ if (ts->seg == now_seg)
+ break;
+ }
+
+ if (ts == NULL)
+ {
+ ts = (struct trampoline_seg *)xcalloc(sizeof (struct trampoline_seg), 1);
+ ts->next = trampoline_seg_list.next;
+ trampoline_seg_list.next = ts;
+ ts->seg = now_seg;
+ }
+
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ varP = frag_var (rs_machine_dependent, size, size, RELAX_TRAMPOLINE, NULL, 0, NULL);
+ fragP = (fragS *)(varP - SIZEOF_STRUCT_FRAG);
+ if (trampoline_buf == NULL)
+ {
+ trampoline_buf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ trampoline_slotbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ }
+ tf = (struct trampoline_frag *)xmalloc(sizeof (struct trampoline_frag));
+ tf->next = ts->trampoline_list.next;
+ ts->trampoline_list.next = tf;
+ tf->needs_jump_around = needs_jump_around;
+ tf->fragP = fragP;
+ tf->fixP = NULL;
+}
+
+
+static struct trampoline_seg *
+find_trampoline_seg (asection *seg)
+{
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+
+ for ( ; ts; ts = ts->next)
+ {
+ if (ts->seg == seg)
+ return ts;
+ }
+
+ return NULL;
+}
+
+
+void dump_trampolines (void);
+
+void
+dump_trampolines (void)
+{
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+
+ for ( ; ts; ts = ts->next)
+ {
+ asection *seg = ts->seg;
+
+ if (seg == NULL)
+ continue;
+ fprintf(stderr, "SECTION %s\n", seg->name);
+ struct trampoline_frag *tf = ts->trampoline_list.next;
+ for ( ; tf; tf = tf->next)
+ {
+ if (tf->fragP == NULL)
+ continue;
+ fprintf(stderr, " 0x%08x: fix=%d, jump_around=%s\n",
+ (int)tf->fragP->fr_address, (int)tf->fragP->fr_fix,
+ tf->needs_jump_around ? "T" : "F");
+ }
+ }
+}
+
+static void
+xtensa_cleanup_align_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if ((fragP->fr_type == rs_align
+ || fragP->fr_type == rs_align_code
+ || (fragP->fr_type == rs_machine_dependent
+ && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
+ || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
+ && fragP->fr_fix == 0)
+ {
+ fragS *next = fragP->fr_next;
+
+ while (next
+ && next->fr_fix == 0
+ && next->fr_type == rs_machine_dependent
+ && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+ {
+ frag_wane (next);
+ next = next->fr_next;
+ }
+ }
+ /* If we don't widen branch targets, then they
+ will be easier to align. */
+ if (fragP->tc_frag_data.is_branch_target
+ && fragP->fr_opcode == fragP->fr_literal
+ && fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ frag_wane (fragP);
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_UNREACHABLE)
+ fragP->tc_frag_data.is_unreachable = TRUE;
+ }
+ }
+}
+
+
+/* Re-process all of the fragments looking to convert all of the
+ RELAX_DESIRE_ALIGN_IF_TARGET fragments. If there is a branch
+ target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
+ Otherwise, convert to a .fill 0. */
+
+static void
+xtensa_fix_target_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ /* When this routine is called, all of the subsections are still intact
+ so we walk over subsections instead of sections. */
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+ {
+ if (next_frag_is_branch_target (fragP))
+ fragP->fr_subtype = RELAX_DESIRE_ALIGN;
+ else
+ frag_wane (fragP);
+ }
+ }
+ }
+}
+
+
+static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
+
+static void
+xtensa_mark_narrow_branches (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+ {
+ vliw_insn vinsn;
+
+ vinsn_from_chars (&vinsn, fragP->fr_opcode);
+ tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
+
+ if (vinsn.num_slots == 1
+ && xtensa_opcode_is_branch (xtensa_default_isa,
+ vinsn.slots[0].opcode) == 1
+ && xg_get_single_size (vinsn.slots[0].opcode) == 2
+ && is_narrow_branch_guaranteed_in_range (fragP,
+ &vinsn.slots[0]))
+ {
+ fragP->fr_subtype = RELAX_SLOTS;
+ fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
+ fragP->tc_frag_data.is_aligning_branch = 1;
+ }
+ }
+ }
+ }
+}
+
+
+/* A branch is typically widened only when its target is out of
+ range. However, we would like to widen them to align a subsequent
+ branch target when possible.
+
+ Because the branch relaxation code is so convoluted, the optimal solution
+ (combining the two cases) is difficult to get right in all circumstances.
+ We therefore go with an "almost as good" solution, where we only
+ use for alignment narrow branches that definitely will not expand to a
+ jump and a branch. These functions find and mark these cases. */
+
+/* The range in bytes of BNEZ.N and BEQZ.N. The target operand is encoded
+ as PC + 4 + imm6, where imm6 is a 6-bit immediate ranging from 0 to 63.
+ We start counting beginning with the frag after the 2-byte branch, so the
+ maximum offset is (4 - 2) + 63 = 65. */
+#define MAX_IMMED6 65
+
+static offsetT unrelaxed_frag_max_size (fragS *);
+
+static bfd_boolean
+is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
+{
+ const expressionS *exp = &tinsn->tok[1];
+ symbolS *symbolP = exp->X_add_symbol;
+ offsetT max_distance = exp->X_add_number;
+ fragS *target_frag;
+
+ if (exp->X_op != O_symbol)
+ return FALSE;
+
+ target_frag = symbol_get_frag (symbolP);
+
+ max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
+ if (is_branch_jmp_to_next (tinsn, fragP))
+ return FALSE;
+
+ /* The branch doesn't branch over it's own frag,
+ but over the subsequent ones. */
+ fragP = fragP->fr_next;
+ while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
+ {
+ max_distance += unrelaxed_frag_max_size (fragP);
+ fragP = fragP->fr_next;
+ }
+ if (max_distance <= MAX_IMMED6 && fragP == target_frag)
+ return TRUE;
+ return FALSE;
+}
+
+
+static void
+xtensa_mark_zcl_first_insns (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
+ || fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
+ {
+ /* Find the loop frag. */
+ fragS *loop_frag = next_non_empty_frag (fragP);
+ /* Find the first insn frag. */
+ fragS *targ_frag = next_non_empty_frag (loop_frag);
+
+ /* Handle a corner case that comes up in hardware
+ diagnostics. The original assembly looks like this:
+
+ loop aX, LabelA
+ <empty_frag>--not found by next_non_empty_frag
+ loop aY, LabelB
+
+ Depending on the start address, the assembler may or
+ may not change it to look something like this:
+
+ loop aX, LabelA
+ nop--frag isn't empty anymore
+ loop aY, LabelB
+
+ So set up to check the alignment of the nop if it
+ exists */
+ while (loop_frag != targ_frag)
+ {
+ if (loop_frag->fr_type == rs_machine_dependent
+ && (loop_frag->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
+ || loop_frag->fr_subtype
+ == RELAX_CHECK_ALIGN_NEXT_OPCODE))
+ targ_frag = loop_frag;
+ else
+ loop_frag = loop_frag->fr_next;
+ }
+
+ /* Of course, sometimes (mostly for toy test cases) a
+ zero-cost loop instruction is the last in a section. */
+ if (targ_frag)
+ {
+ targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
+ /* Do not widen a frag that is the first instruction of a
+ zero-cost loop. It makes that loop harder to align. */
+ if (targ_frag->fr_type == rs_machine_dependent
+ && targ_frag->fr_subtype == RELAX_SLOTS
+ && (targ_frag->tc_frag_data.slot_subtypes[0]
+ == RELAX_NARROW))
+ {
+ if (targ_frag->tc_frag_data.is_aligning_branch)
+ targ_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
+ else
+ {
+ frag_wane (targ_frag);
+ targ_frag->tc_frag_data.slot_subtypes[0] = 0;
+ }
+ }
+ }
+ if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
+ frag_wane (fragP);
+ }
+ }
+ }
+}
+
+
+/* When a difference-of-symbols expression is encoded as a uleb128 or
+ sleb128 value, the linker is unable to adjust that value to account for
+ link-time relaxation. Mark all the code between such symbols so that
+ its size cannot be changed by linker relaxation. */
+
+static void
+xtensa_mark_difference_of_two_symbols (void)
+{
+ symbolS *expr_sym;
+
+ for (expr_sym = expr_symbols; expr_sym;
+ expr_sym = symbol_get_tc (expr_sym)->next_expr_symbol)
+ {
+ expressionS *exp = symbol_get_value_expression (expr_sym);
+
+ if (exp->X_op == O_subtract)
+ {
+ symbolS *left = exp->X_add_symbol;
+ symbolS *right = exp->X_op_symbol;
+
+ /* Difference of two symbols not in the same section
+ are handled with relocations in the linker. */
+ if (S_GET_SEGMENT (left) == S_GET_SEGMENT (right))
+ {
+ fragS *start;
+ fragS *end;
+ fragS *walk;
+
+ if (symbol_get_frag (left)->fr_address
+ <= symbol_get_frag (right)->fr_address)
+ {
+ start = symbol_get_frag (left);
+ end = symbol_get_frag (right);
+ }
+ else
+ {
+ start = symbol_get_frag (right);
+ end = symbol_get_frag (left);
+ }
+
+ if (start->tc_frag_data.no_transform_end != NULL)
+ walk = start->tc_frag_data.no_transform_end;
+ else
+ walk = start;
+ do
+ {
+ walk->tc_frag_data.is_no_transform = 1;
+ walk = walk->fr_next;
+ }
+ while (walk && walk->fr_address < end->fr_address);
+
+ start->tc_frag_data.no_transform_end = walk;
+ }
+ }
+ }
+}
+
+
+/* Re-process all of the fragments looking to convert all of the
+ RELAX_ADD_NOP_IF_A0_B_RETW. If the next instruction is a
+ conditional branch or a retw/retw.n, convert this frag to one that
+ will generate a NOP. In any case close it off with a .fill 0. */
+
+static bfd_boolean next_instrs_are_b_retw (fragS *);
+
+static void
+xtensa_fix_a0_b_retw_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ /* When this routine is called, all of the subsections are still intact
+ so we walk over subsections instead of sections. */
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
+ {
+ if (next_instrs_are_b_retw (fragP))
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
+ }
+ }
+ }
+}
+
+
+static bfd_boolean
+next_instrs_are_b_retw (fragS *fragP)
+{
+ xtensa_opcode opcode;
+ xtensa_format fmt;
+ const fragS *next_fragP = next_non_empty_frag (fragP);
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ int offset = 0;
+ int slot;
+ bfd_boolean branch_seen = FALSE;
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ if (next_fragP == NULL)
+ return FALSE;
+
+ /* Check for the conditional branch. */
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return FALSE;
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+ branch_seen = (branch_seen
+ || xtensa_opcode_is_branch (isa, opcode) == 1);
+ }
+
+ if (!branch_seen)
+ return FALSE;
+
+ offset += xtensa_format_length (isa, fmt);
+ if (offset == next_fragP->fr_fix)
+ {
+ next_fragP = next_non_empty_frag (next_fragP);
+ offset = 0;
+ }
+
+ if (next_fragP == NULL)
+ return FALSE;
+
+ /* Check for the retw/retw.n. */
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ /* Because RETW[.N] is not bundleable, a VLIW bundle here means that we
+ have no problems. */
+ if (fmt == XTENSA_UNDEFINED
+ || xtensa_format_num_slots (isa, fmt) != 1)
+ return FALSE;
+
+ xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
+
+ if (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+/* Re-process all of the fragments looking to convert all of the
+ RELAX_ADD_NOP_IF_PRE_LOOP_END. If there is one instruction and a
+ loop end label, convert this frag to one that will generate a NOP.
+ In any case close it off with a .fill 0. */
+
+static bfd_boolean next_instr_is_loop_end (fragS *);
+
+static void
+xtensa_fix_b_j_loop_end_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ /* When this routine is called, all of the subsections are still intact
+ so we walk over subsections instead of sections. */
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
+ {
+ if (next_instr_is_loop_end (fragP))
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
+ }
+ }
+ }
+}
+
+
+static bfd_boolean
+next_instr_is_loop_end (fragS *fragP)
+{
+ const fragS *next_fragP;
+
+ if (next_frag_is_loop_target (fragP))
+ return FALSE;
+
+ next_fragP = next_non_empty_frag (fragP);
+ if (next_fragP == NULL)
+ return FALSE;
+
+ if (!next_frag_is_loop_target (next_fragP))
+ return FALSE;
+
+ /* If the size is >= 3 then there is more than one instruction here.
+ The hardware bug will not fire. */
+ if (next_fragP->fr_fix > 3)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/* Re-process all of the fragments looking to convert all of the
+ RELAX_ADD_NOP_IF_CLOSE_LOOP_END. If there is an loop end that is
+ not MY loop's loop end within 12 bytes, add enough nops here to
+ make it at least 12 bytes away. In any case close it off with a
+ .fill 0. */
+
+static offsetT min_bytes_to_other_loop_end
+ (fragS *, fragS *, offsetT);
+
+static void
+xtensa_fix_close_loop_end_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ /* When this routine is called, all of the subsections are still intact
+ so we walk over subsections instead of sections. */
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+
+ fragS *current_target = NULL;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
+ || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
+ current_target = symbol_get_frag (fragP->fr_symbol);
+
+ if (current_target
+ && fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
+ {
+ offsetT min_bytes;
+ int bytes_added = 0;
+
+#define REQUIRED_LOOP_DIVIDING_BYTES 12
+ /* Max out at 12. */
+ min_bytes = min_bytes_to_other_loop_end
+ (fragP->fr_next, current_target, REQUIRED_LOOP_DIVIDING_BYTES);
+
+ if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("loop end too close to another loop end may trigger hardware errata"));
+ else
+ {
+ while (min_bytes + bytes_added
+ < REQUIRED_LOOP_DIVIDING_BYTES)
+ {
+ int length = 3;
+
+ if (fragP->fr_var < length)
+ as_fatal (_("fr_var %lu < length %d"),
+ (long) fragP->fr_var, length);
+ else
+ {
+ assemble_nop (length,
+ fragP->fr_literal + fragP->fr_fix);
+ fragP->fr_fix += length;
+ fragP->fr_var -= length;
+ }
+ bytes_added += length;
+ }
+ }
+ }
+ frag_wane (fragP);
+ }
+ gas_assert (fragP->fr_type != rs_machine_dependent
+ || fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
+ }
+ }
+}
+
+
+static offsetT unrelaxed_frag_min_size (fragS *);
+
+static offsetT
+min_bytes_to_other_loop_end (fragS *fragP,
+ fragS *current_target,
+ offsetT max_size)
+{
+ offsetT offset = 0;
+ fragS *current_fragP;
+
+ for (current_fragP = fragP;
+ current_fragP;
+ current_fragP = current_fragP->fr_next)
+ {
+ if (current_fragP->tc_frag_data.is_loop_target
+ && current_fragP != current_target)
+ return offset;
+
+ offset += unrelaxed_frag_min_size (current_fragP);
+
+ if (offset >= max_size)
+ return max_size;
+ }
+ return max_size;
+}
+
+
+static offsetT
+unrelaxed_frag_min_size (fragS *fragP)
+{
+ offsetT size = fragP->fr_fix;
+
+ /* Add fill size. */
+ if (fragP->fr_type == rs_fill)
+ size += fragP->fr_offset;
+
+ return size;
+}
+
+
+static offsetT
+unrelaxed_frag_max_size (fragS *fragP)
+{
+ offsetT size = fragP->fr_fix;
+ switch (fragP->fr_type)
+ {
+ case 0:
+ /* Empty frags created by the obstack allocation scheme
+ end up with type 0. */
+ break;
+ case rs_fill:
+ case rs_org:
+ case rs_space:
+ size += fragP->fr_offset;
+ break;
+ case rs_align:
+ case rs_align_code:
+ case rs_align_test:
+ case rs_leb128:
+ case rs_cfa:
+ case rs_dwarf2dbg:
+ /* No further adjustments needed. */
+ break;
+ case rs_machine_dependent:
+ if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
+ size += fragP->fr_var;
+ break;
+ default:
+ /* We had darn well better know how big it is. */
+ gas_assert (0);
+ break;
+ }
+
+ return size;
+}
+
+
+/* Re-process all of the fragments looking to convert all
+ of the RELAX_ADD_NOP_IF_SHORT_LOOP. If:
+
+ A)
+ 1) the instruction size count to the loop end label
+ is too short (<= 2 instructions),
+ 2) loop has a jump or branch in it
+
+ or B)
+ 1) workaround_all_short_loops is TRUE
+ 2) The generating loop was a 'loopgtz' or 'loopnez'
+ 3) the instruction size count to the loop end label is too short
+ (<= 2 instructions)
+ then convert this frag (and maybe the next one) to generate a NOP.
+ In any case close it off with a .fill 0. */
+
+static int count_insns_to_loop_end (fragS *, bfd_boolean, int);
+static bfd_boolean branch_before_loop_end (fragS *);
+
+static void
+xtensa_fix_short_loop_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ /* When this routine is called, all of the subsections are still intact
+ so we walk over subsections instead of sections. */
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ xtensa_opcode current_opcode = XTENSA_UNDEFINED;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && ((fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
+ || (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)))
+ {
+ TInsn t_insn;
+ fragS *loop_frag = next_non_empty_frag (fragP);
+ tinsn_from_chars (&t_insn, loop_frag->fr_opcode, 0);
+ current_opcode = t_insn.opcode;
+ gas_assert (xtensa_opcode_is_loop (xtensa_default_isa,
+ current_opcode) == 1);
+ }
+
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
+ {
+ if (count_insns_to_loop_end (fragP->fr_next, TRUE, 3) < 3
+ && (branch_before_loop_end (fragP->fr_next)
+ || (workaround_all_short_loops
+ && current_opcode != XTENSA_UNDEFINED
+ && current_opcode != xtensa_loop_opcode)))
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("loop containing less than three instructions may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
+ }
+ }
+ }
+}
+
+
+static int unrelaxed_frag_min_insn_count (fragS *);
+
+static int
+count_insns_to_loop_end (fragS *base_fragP,
+ bfd_boolean count_relax_add,
+ int max_count)
+{
+ fragS *fragP = NULL;
+ int insn_count = 0;
+
+ fragP = base_fragP;
+
+ for (; fragP && !fragP->tc_frag_data.is_loop_target; fragP = fragP->fr_next)
+ {
+ insn_count += unrelaxed_frag_min_insn_count (fragP);
+ if (insn_count >= max_count)
+ return max_count;
+
+ if (count_relax_add)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
+ {
+ /* In order to add the appropriate number of
+ NOPs, we count an instruction for downstream
+ occurrences. */
+ insn_count++;
+ if (insn_count >= max_count)
+ return max_count;
+ }
+ }
+ }
+ return insn_count;
+}
+
+
+static int
+unrelaxed_frag_min_insn_count (fragS *fragP)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf insnbuf = NULL;
+ int insn_count = 0;
+ int offset = 0;
+
+ if (!fragP->tc_frag_data.is_insn)
+ return insn_count;
+
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
+ /* Decode the fixed instructions. */
+ while (offset < fragP->fr_fix)
+ {
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ if (fmt == XTENSA_UNDEFINED)
+ {
+ as_fatal (_("undecodable instruction in instruction frag"));
+ return insn_count;
+ }
+ offset += xtensa_format_length (isa, fmt);
+ insn_count++;
+ }
+
+ return insn_count;
+}
+
+
+static bfd_boolean unrelaxed_frag_has_b_j (fragS *);
+
+static bfd_boolean
+branch_before_loop_end (fragS *base_fragP)
+{
+ fragS *fragP;
+
+ for (fragP = base_fragP;
+ fragP && !fragP->tc_frag_data.is_loop_target;
+ fragP = fragP->fr_next)
+ {
+ if (unrelaxed_frag_has_b_j (fragP))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+unrelaxed_frag_has_b_j (fragS *fragP)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ int offset = 0;
+
+ if (!fragP->tc_frag_data.is_insn)
+ return FALSE;
+
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
+ /* Decode the fixed instructions. */
+ while (offset < fragP->fr_fix)
+ {
+ xtensa_format fmt;
+ int slot;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return FALSE;
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ xtensa_opcode opcode =
+ get_opcode_from_buf (fragP->fr_literal + offset, slot);
+ if (xtensa_opcode_is_branch (isa, opcode) == 1
+ || xtensa_opcode_is_jump (isa, opcode) == 1)
+ return TRUE;
+ }
+ offset += xtensa_format_length (isa, fmt);
+ }
+ return FALSE;
+}
+
+
+/* Checks to be made after initial assembly but before relaxation. */
+
+static bfd_boolean is_empty_loop (const TInsn *, fragS *);
+static bfd_boolean is_local_forward_loop (const TInsn *, fragS *);
+
+static void
+xtensa_sanity_check (void)
+{
+ char *file_name;
+ unsigned line;
+ frchainS *frchP;
+ asection *s;
+
+ as_where (&file_name, &line);
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+ {
+ static xtensa_insnbuf insnbuf = NULL;
+ TInsn t_insn;
+
+ if (fragP->fr_opcode != NULL)
+ {
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
+ tinsn_immed_from_frag (&t_insn, fragP, 0);
+
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
+ {
+ if (is_empty_loop (&t_insn, fragP))
+ {
+ new_logical_line (fragP->fr_file, fragP->fr_line);
+ as_bad (_("invalid empty loop"));
+ }
+ if (!is_local_forward_loop (&t_insn, fragP))
+ {
+ new_logical_line (fragP->fr_file, fragP->fr_line);
+ as_bad (_("loop target does not follow "
+ "loop instruction in section"));
+ }
+ }
+ }
+ }
+ }
+ }
+ new_logical_line (file_name, line);
+}
+
+
+#define LOOP_IMMED_OPN 1
+
+/* Return TRUE if the loop target is the next non-zero fragment. */
+
+static bfd_boolean
+is_empty_loop (const TInsn *insn, fragS *fragP)
+{
+ const expressionS *exp;
+ symbolS *symbolP;
+ fragS *next_fragP;
+
+ if (insn->insn_type != ITYPE_INSN)
+ return FALSE;
+
+ if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
+ return FALSE;
+
+ if (insn->ntok <= LOOP_IMMED_OPN)
+ return FALSE;
+
+ exp = &insn->tok[LOOP_IMMED_OPN];
+
+ if (exp->X_op != O_symbol)
+ return FALSE;
+
+ symbolP = exp->X_add_symbol;
+ if (!symbolP)
+ return FALSE;
+
+ if (symbol_get_frag (symbolP) == NULL)
+ return FALSE;
+
+ if (S_GET_VALUE (symbolP) != 0)
+ return FALSE;
+
+ /* Walk through the zero-size fragments from this one. If we find
+ the target fragment, then this is a zero-size loop. */
+
+ for (next_fragP = fragP->fr_next;
+ next_fragP != NULL;
+ next_fragP = next_fragP->fr_next)
+ {
+ if (next_fragP == symbol_get_frag (symbolP))
+ return TRUE;
+ if (next_fragP->fr_fix != 0)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+is_local_forward_loop (const TInsn *insn, fragS *fragP)
+{
+ const expressionS *exp;
+ symbolS *symbolP;
+ fragS *next_fragP;
+
+ if (insn->insn_type != ITYPE_INSN)
+ return FALSE;
+
+ if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
+ return FALSE;
+
+ if (insn->ntok <= LOOP_IMMED_OPN)
+ return FALSE;
+
+ exp = &insn->tok[LOOP_IMMED_OPN];
+
+ if (exp->X_op != O_symbol)
+ return FALSE;
+
+ symbolP = exp->X_add_symbol;
+ if (!symbolP)
+ return FALSE;
+
+ if (symbol_get_frag (symbolP) == NULL)
+ return FALSE;
+
+ /* Walk through fragments until we find the target.
+ If we do not find the target, then this is an invalid loop. */
+
+ for (next_fragP = fragP->fr_next;
+ next_fragP != NULL;
+ next_fragP = next_fragP->fr_next)
+ {
+ if (next_fragP == symbol_get_frag (symbolP))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+#define XTINFO_NAME "Xtensa_Info"
+#define XTINFO_NAMESZ 12
+#define XTINFO_TYPE 1
+
+static void
+xtensa_add_config_info (void)
+{
+ asection *info_sec;
+ char *data, *p;
+ int sz;
+
+ info_sec = subseg_new (".xtensa.info", 0);
+ bfd_set_section_flags (stdoutput, info_sec, SEC_HAS_CONTENTS | SEC_READONLY);
+
+ data = xmalloc (100);
+ sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n",
+ XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI);
+ sz = strlen (data) + 1;
+
+ /* Add enough null terminators to pad to a word boundary. */
+ do
+ data[sz++] = 0;
+ while ((sz & 3) != 0);
+
+ /* Follow the standard note section layout:
+ First write the length of the name string. */
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) XTINFO_NAMESZ, 4);
+
+ /* Next comes the length of the "descriptor", i.e., the actual data. */
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) sz, 4);
+
+ /* Write the note type. */
+ p = frag_more (4);
+ md_number_to_chars (p, (valueT) XTINFO_TYPE, 4);
+
+ /* Write the name field. */
+ p = frag_more (XTINFO_NAMESZ);
+ memcpy (p, XTINFO_NAME, XTINFO_NAMESZ);
+
+ /* Finally, write the descriptor. */
+ p = frag_more (sz);
+ memcpy (p, data, sz);
+
+ free (data);
+}
+
+
+/* Alignment Functions. */
+
+static int
+get_text_align_power (unsigned target_size)
+{
+ if (target_size <= 4)
+ return 2;
+
+ if (target_size <= 8)
+ return 3;
+
+ if (target_size <= 16)
+ return 4;
+
+ if (target_size <= 32)
+ return 5;
+
+ if (target_size <= 64)
+ return 6;
+
+ if (target_size <= 128)
+ return 7;
+
+ if (target_size <= 256)
+ return 8;
+
+ if (target_size <= 512)
+ return 9;
+
+ if (target_size <= 1024)
+ return 10;
+
+ gas_assert (0);
+ return 0;
+}
+
+
+static int
+get_text_align_max_fill_size (int align_pow,
+ bfd_boolean use_nops,
+ bfd_boolean use_no_density)
+{
+ if (!use_nops)
+ return (1 << align_pow);
+ if (use_no_density)
+ return 3 * (1 << align_pow);
+
+ return 1 + (1 << align_pow);
+}
+
+
+/* Calculate the minimum bytes of fill needed at "address" to align a
+ target instruction of size "target_size" so that it does not cross a
+ power-of-two boundary specified by "align_pow". If "use_nops" is FALSE,
+ the fill can be an arbitrary number of bytes. Otherwise, the space must
+ be filled by NOP instructions. */
+
+static int
+get_text_align_fill_size (addressT address,
+ int align_pow,
+ int target_size,
+ bfd_boolean use_nops,
+ bfd_boolean use_no_density)
+{
+ addressT alignment, fill, fill_limit, fill_step;
+ bfd_boolean skip_one = FALSE;
+
+ alignment = (1 << align_pow);
+ gas_assert (target_size > 0 && alignment >= (addressT) target_size);
+
+ if (!use_nops)
+ {
+ fill_limit = alignment;
+ fill_step = 1;
+ }
+ else if (!use_no_density)
+ {
+ /* Combine 2- and 3-byte NOPs to fill anything larger than one. */
+ fill_limit = alignment * 2;
+ fill_step = 1;
+ skip_one = TRUE;
+ }
+ else
+ {
+ /* Fill with 3-byte NOPs -- can only fill multiples of 3. */
+ fill_limit = alignment * 3;
+ fill_step = 3;
+ }
+
+ /* Try all fill sizes until finding one that works. */
+ for (fill = 0; fill < fill_limit; fill += fill_step)
+ {
+ if (skip_one && fill == 1)
+ continue;
+ if ((address + fill) >> align_pow
+ == (address + fill + target_size - 1) >> align_pow)
+ return fill;
+ }
+ gas_assert (0);
+ return 0;
+}
+
+
+static int
+branch_align_power (segT sec)
+{
+ /* If the Xtensa processor has a fetch width of X, and
+ the section is aligned to at least that boundary, then a branch
+ target need only fit within that aligned block of memory to avoid
+ a stall. Otherwise, try to fit branch targets within 4-byte
+ aligned blocks (which may be insufficient, e.g., if the section
+ has no alignment, but it's good enough). */
+ int fetch_align = get_text_align_power(xtensa_fetch_width);
+ int sec_align = get_recorded_alignment (sec);
+
+ if (sec_align >= fetch_align)
+ return fetch_align;
+
+ return 2;
+}
+
+
+/* This will assert if it is not possible. */
+
+static int
+get_text_align_nop_count (offsetT fill_size, bfd_boolean use_no_density)
+{
+ int count = 0;
+
+ if (use_no_density)
+ {
+ gas_assert (fill_size % 3 == 0);
+ return (fill_size / 3);
+ }
+
+ gas_assert (fill_size != 1); /* Bad argument. */
+
+ while (fill_size > 1)
+ {
+ int insn_size = 3;
+ if (fill_size == 2 || fill_size == 4)
+ insn_size = 2;
+ fill_size -= insn_size;
+ count++;
+ }
+ gas_assert (fill_size != 1); /* Bad algorithm. */
+ return count;
+}
+
+
+static int
+get_text_align_nth_nop_size (offsetT fill_size,
+ int n,
+ bfd_boolean use_no_density)
+{
+ int count = 0;
+
+ if (use_no_density)
+ return 3;
+
+ gas_assert (fill_size != 1); /* Bad argument. */
+
+ while (fill_size > 1)
+ {
+ int insn_size = 3;
+ if (fill_size == 2 || fill_size == 4)
+ insn_size = 2;
+ fill_size -= insn_size;
+ count++;
+ if (n + 1 == count)
+ return insn_size;
+ }
+ gas_assert (0);
+ return 0;
+}
+
+
+/* For the given fragment, find the appropriate address
+ for it to begin at if we are using NOPs to align it. */
+
+static addressT
+get_noop_aligned_address (fragS *fragP, addressT address)
+{
+ /* The rule is: get next fragment's FIRST instruction. Find
+ the smallest number of bytes that need to be added to
+ ensure that the next fragment's FIRST instruction will fit
+ in a single word.
+
+ E.G., 2 bytes : 0, 1, 2 mod 4
+ 3 bytes: 0, 1 mod 4
+
+ If the FIRST instruction MIGHT be relaxed,
+ assume that it will become a 3-byte instruction.
+
+ Note again here that LOOP instructions are not bundleable,
+ and this relaxation only applies to LOOP opcodes. */
+
+ int fill_size = 0;
+ int first_insn_size;
+ int loop_insn_size;
+ addressT pre_opcode_bytes;
+ int align_power;
+ fragS *first_insn;
+ xtensa_opcode opcode;
+ bfd_boolean is_loop;
+
+ gas_assert (fragP->fr_type == rs_machine_dependent);
+ gas_assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
+
+ /* Find the loop frag. */
+ first_insn = next_non_empty_frag (fragP);
+ /* Now find the first insn frag. */
+ first_insn = next_non_empty_frag (first_insn);
+
+ is_loop = next_frag_opcode_is_loop (fragP, &opcode);
+ gas_assert (is_loop);
+ loop_insn_size = xg_get_single_size (opcode);
+
+ pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
+ pre_opcode_bytes += loop_insn_size;
+
+ /* For loops, the alignment depends on the size of the
+ instruction following the loop, not the LOOP instruction. */
+
+ if (first_insn == NULL)
+ first_insn_size = xtensa_fetch_width;
+ else
+ first_insn_size = get_loop_align_size (frag_format_size (first_insn));
+
+ /* If it was 8, then we'll need a larger alignment for the section. */
+ align_power = get_text_align_power (first_insn_size);
+ record_alignment (now_seg, align_power);
+
+ fill_size = get_text_align_fill_size
+ (address + pre_opcode_bytes, align_power, first_insn_size, TRUE,
+ fragP->tc_frag_data.is_no_density);
+
+ return address + fill_size;
+}
+
+
+/* 3 mechanisms for relaxing an alignment:
+
+ Align to a power of 2.
+ Align so the next fragment's instruction does not cross a word boundary.
+ Align the current instruction so that if the next instruction
+ were 3 bytes, it would not cross a word boundary.
+
+ We can align with:
+
+ zeros - This is easy; always insert zeros.
+ nops - 3-byte and 2-byte instructions
+ 2 - 2-byte nop
+ 3 - 3-byte nop
+ 4 - 2 2-byte nops
+ >=5 : 3-byte instruction + fn (n-3)
+ widening - widen previous instructions. */
+
+static offsetT
+get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
+{
+ addressT target_address, loop_insn_offset;
+ int target_size;
+ xtensa_opcode loop_opcode;
+ bfd_boolean is_loop;
+ int align_power;
+ offsetT opt_diff;
+ offsetT branch_align;
+ fragS *loop_frag;
+
+ gas_assert (fragP->fr_type == rs_machine_dependent);
+ switch (fragP->fr_subtype)
+ {
+ case RELAX_DESIRE_ALIGN:
+ target_size = next_frag_format_size (fragP);
+ if (target_size == XTENSA_UNDEFINED)
+ target_size = 3;
+ align_power = branch_align_power (now_seg);
+ branch_align = 1 << align_power;
+ /* Don't count on the section alignment being as large as the target. */
+ if (target_size > branch_align)
+ target_size = branch_align;
+ opt_diff = get_text_align_fill_size (address, align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = (opt_diff + branch_align
+ - (target_size + ((address + opt_diff) % branch_align)));
+ gas_assert (*max_diff >= opt_diff);
+ return opt_diff;
+
+ case RELAX_ALIGN_NEXT_OPCODE:
+ /* The next non-empty frag after this one holds the LOOP instruction
+ that needs to be aligned. The required alignment depends on the
+ size of the next non-empty frag after the loop frag, i.e., the
+ first instruction in the loop. */
+ loop_frag = next_non_empty_frag (fragP);
+ target_size = get_loop_align_size (next_frag_format_size (loop_frag));
+ loop_insn_offset = 0;
+ is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
+ gas_assert (is_loop);
+
+ /* If the loop has been expanded then the LOOP instruction
+ could be at an offset from this fragment. */
+ if (loop_frag->tc_frag_data.slot_subtypes[0] != RELAX_IMMED)
+ loop_insn_offset = get_expanded_loop_offset (loop_opcode);
+
+ /* In an ideal world, which is what we are shooting for here,
+ we wouldn't need to use any NOPs immediately prior to the
+ LOOP instruction. If this approach fails, relax_frag_loop_align
+ will call get_noop_aligned_address. */
+ target_address =
+ address + loop_insn_offset + xg_get_single_size (loop_opcode);
+ align_power = get_text_align_power (target_size);
+ opt_diff = get_text_align_fill_size (target_address, align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = xtensa_fetch_width
+ - ((target_address + opt_diff) % xtensa_fetch_width)
+ - target_size + opt_diff;
+ gas_assert (*max_diff >= opt_diff);
+ return opt_diff;
+
+ default:
+ break;
+ }
+ gas_assert (0);
+ return 0;
+}
+
+
+/* md_relax_frag Hook and Helper Functions. */
+
+static long relax_frag_loop_align (fragS *, long);
+static long relax_frag_for_align (fragS *, long);
+static long relax_frag_immed
+ (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
+
+
+/* Return the number of bytes added to this fragment, given that the
+ input has been stretched already by "stretch". */
+
+long
+xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int unreported = fragP->tc_frag_data.unreported_expansion;
+ long new_stretch = 0;
+ char *file_name;
+ unsigned line;
+ int lit_size;
+ static xtensa_insnbuf vbuf = NULL;
+ int slot, num_slots;
+ xtensa_format fmt;
+
+ as_where (&file_name, &line);
+ new_logical_line (fragP->fr_file, fragP->fr_line);
+
+ fragP->tc_frag_data.unreported_expansion = 0;
+
+ switch (fragP->fr_subtype)
+ {
+ case RELAX_ALIGN_NEXT_OPCODE:
+ /* Always convert. */
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch = relax_frag_loop_align (fragP, stretch);
+ break;
+
+ case RELAX_LOOP_END:
+ /* Do nothing. */
+ break;
+
+ case RELAX_LOOP_END_ADD_NOP:
+ /* Add a NOP and switch to .fill 0. */
+ new_stretch = relax_frag_add_nop (fragP);
+ frag_wane (fragP);
+ break;
+
+ case RELAX_DESIRE_ALIGN:
+ /* Do nothing. The narrowing before this frag will either align
+ it or not. */
+ break;
+
+ case RELAX_LITERAL:
+ case RELAX_LITERAL_FINAL:
+ return 0;
+
+ case RELAX_LITERAL_NR:
+ lit_size = 4;
+ fragP->fr_subtype = RELAX_LITERAL_FINAL;
+ gas_assert (unreported == lit_size);
+ memset (&fragP->fr_literal[fragP->fr_fix], 0, 4);
+ fragP->fr_var -= lit_size;
+ fragP->fr_fix += lit_size;
+ new_stretch = 4;
+ break;
+
+ case RELAX_SLOTS:
+ if (vbuf == NULL)
+ vbuf = xtensa_insnbuf_alloc (isa);
+
+ xtensa_insnbuf_from_chars
+ (isa, vbuf, (unsigned char *) fragP->fr_opcode, 0);
+ fmt = xtensa_format_decode (isa, vbuf);
+ num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (slot = 0; slot < num_slots; slot++)
+ {
+ switch (fragP->tc_frag_data.slot_subtypes[slot])
+ {
+ case RELAX_NARROW:
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch += relax_frag_for_align (fragP, stretch);
+ break;
+
+ case RELAX_IMMED:
+ case RELAX_IMMED_STEP1:
+ case RELAX_IMMED_STEP2:
+ case RELAX_IMMED_STEP3:
+ /* Place the immediate. */
+ new_stretch += relax_frag_immed
+ (now_seg, fragP, stretch,
+ fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+ fmt, slot, stretched_p, FALSE);
+ break;
+
+ default:
+ /* This is OK; see the note in xg_assemble_vliw_tokens. */
+ break;
+ }
+ }
+ break;
+
+ case RELAX_LITERAL_POOL_BEGIN:
+ case RELAX_LITERAL_POOL_END:
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ /* No relaxation required. */
+ break;
+
+ case RELAX_FILL_NOP:
+ case RELAX_UNREACHABLE:
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch += relax_frag_for_align (fragP, stretch);
+ break;
+
+ case RELAX_TRAMPOLINE:
+ if (fragP->tc_frag_data.relax_seen)
+ {
+ segment_info_type *seginfo = seg_info (now_seg);
+ fragS *fP; /* The out-of-range jump. */
+ fixS *fixP;
+
+ /* Scan for jumps that will not reach. */
+ for (fixP = seginfo->fix_root; fixP ; fixP = fixP->fx_next)
+ {
+ symbolS *s = fixP->fx_addsy;
+ xtensa_opcode opcode;
+ int target;
+ int addr;
+ int delta;
+
+ if (fixP->fx_r_type < BFD_RELOC_XTENSA_SLOT0_OP ||
+ fixP->fx_r_type > BFD_RELOC_XTENSA_SLOT14_OP)
+ continue;
+ xtensa_insnbuf_from_chars (isa, trampoline_buf,
+ (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where,
+ 0);
+ fmt = xtensa_format_decode (isa, trampoline_buf);
+ gas_assert (fmt != XTENSA_UNDEFINED);
+ slot = fixP->tc_fix_data.slot;
+ xtensa_format_get_slot (isa, fmt, slot, trampoline_buf, trampoline_slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, trampoline_slotbuf);
+ if (opcode != xtensa_j_opcode)
+ continue;
+ target = S_GET_VALUE (s);
+ addr = fixP->fx_frag->fr_address;
+ delta = target - addr + stretch;
+ if (delta > J_RANGE || delta < -1 * J_RANGE)
+ { /* Found an out-of-range jump; scan the list of trampolines for the best match. */
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ struct trampoline_frag *tf = ts->trampoline_list.next;
+ struct trampoline_frag *prev = &ts->trampoline_list;
+ int lower = (target < addr) ? target : addr;
+ int upper = (target > addr) ? target : addr;
+ int midpoint = lower + (upper - lower) / 2;
+
+ if ((upper - lower) > 2 * J_RANGE)
+ {
+ /* One trampoline won't suffice; we need multiple jumps.
+ Jump to the trampoline that's farthest, but still in
+ range relative to the original "j" instruction. */
+ for ( ; tf; prev = tf, tf = tf->next )
+ {
+ int this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ int next_addr = (tf->next) ? tf->next->fragP->fr_address + tf->next->fragP->fr_fix : 0 ;
+
+ if (addr == lower)
+ {
+ /* Forward jump. */
+ if (this_addr - addr < J_RANGE)
+ break;
+ }
+ else
+ {
+ /* Backward jump. */
+ if (next_addr == 0 || addr - next_addr > J_RANGE)
+ break;
+ }
+ }
+ }
+ else
+ {
+ struct trampoline_frag *best_tf = NULL;
+ int best_delta = 0;
+
+ for ( ; tf; prev = tf, tf = tf->next )
+ {
+ int this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ int this_delta = abs (this_addr - midpoint);
+
+ if (!best_tf || this_delta < best_delta)
+ {
+ best_tf = tf;
+ best_delta = this_delta;
+ }
+ }
+ tf = best_tf;
+ }
+ if (tf->fragP == fragP)
+ {
+ int trampaddr = fragP->fr_address + fragP->fr_fix;
+
+ if (abs (addr - trampaddr) < J_RANGE)
+ { /* The trampoline is in range of original; fix it! */
+ fixS *newfixP;
+ int offset;
+ TInsn insn;
+ symbolS *lsym;
+
+ new_stretch += init_trampoline_frag (tf);
+ offset = fragP->fr_fix; /* Where to assemble the j insn. */
+ lsym = fragP->fr_symbol;
+ fP = fixP->fx_frag;
+ /* Assemble a jump to the target label here. */
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], lsym, offset);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
+ xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)fragP->fr_literal + offset, 3);
+ fragP->fr_fix += 3;
+ fragP->fr_var -= 3;
+ /* Add a fix-up for the original j insn. */
+ newfixP = fix_new (fP, fixP->fx_where, fixP->fx_size, lsym, fragP->fr_fix - 3, TRUE, fixP->fx_r_type);
+ newfixP->fx_no_overflow = 1;
+ newfixP->tc_fix_data.X_add_symbol = lsym;
+ newfixP->tc_fix_data.X_add_number = offset;
+ newfixP->tc_fix_data.slot = slot;
+ /* Move the fix-up from the original j insn to this one. */
+ fixP->fx_frag = fragP;
+ fixP->fx_where = fragP->fr_fix - 3;
+ fixP->tc_fix_data.slot = 0;
+ /* Adjust the jump around this trampoline (if present). */
+ if (tf->fixP != NULL)
+ {
+ tf->fixP->fx_offset += 3;
+ }
+ new_stretch += 3;
+ fragP->tc_frag_data.relax_seen = FALSE; /* Need another pass. */
+ /* Do we have room for more? */
+ if (fragP->fr_var < 3)
+ { /* No, convert to fill. */
+ frag_wane (fragP);
+ fragP->fr_subtype = 0;
+ /* Remove from the trampoline_list. */
+ prev->next = tf->next;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ as_bad (_("bad relaxation state"));
+ }
+
+ /* Tell gas we need another relaxation pass. */
+ if (! fragP->tc_frag_data.relax_seen)
+ {
+ fragP->tc_frag_data.relax_seen = TRUE;
+ *stretched_p = 1;
+ }
+
+ new_logical_line (file_name, line);
+ return new_stretch;
+}
+
+
+static long
+relax_frag_loop_align (fragS *fragP, long stretch)
+{
+ addressT old_address, old_next_address, old_size;
+ addressT new_address, new_next_address, new_size;
+ addressT growth;
+
+ /* All the frags with relax_frag_for_alignment prior to this one in the
+ section have been done, hopefully eliminating the need for a NOP here.
+ But, this will put it in if necessary. */
+
+ /* Calculate the old address of this fragment and the next fragment. */
+ old_address = fragP->fr_address - stretch;
+ old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
+ fragP->tc_frag_data.text_expansion[0]);
+ old_size = old_next_address - old_address;
+
+ /* Calculate the new address of this fragment and the next fragment. */
+ new_address = fragP->fr_address;
+ new_next_address =
+ get_noop_aligned_address (fragP, fragP->fr_address + fragP->fr_fix);
+ new_size = new_next_address - new_address;
+
+ growth = new_size - old_size;
+
+ /* Fix up the text_expansion field and return the new growth. */
+ fragP->tc_frag_data.text_expansion[0] += growth;
+ return growth;
+}
+
+
+/* Add a NOP instruction. */
+
+static long
+relax_frag_add_nop (fragS *fragP)
+{
+ char *nop_buf = fragP->fr_literal + fragP->fr_fix;
+ int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
+ assemble_nop (length, nop_buf);
+ fragP->tc_frag_data.is_insn = TRUE;
+
+ if (fragP->fr_var < length)
+ {
+ as_fatal (_("fr_var (%ld) < length (%d)"), (long) fragP->fr_var, length);
+ return 0;
+ }
+
+ fragP->fr_fix += length;
+ fragP->fr_var -= length;
+ return length;
+}
+
+
+static long future_alignment_required (fragS *, long);
+
+static long
+relax_frag_for_align (fragS *fragP, long stretch)
+{
+ /* Overview of the relaxation procedure for alignment:
+ We can widen with NOPs or by widening instructions or by filling
+ bytes after jump instructions. Find the opportune places and widen
+ them if necessary. */
+
+ long stretch_me;
+ long diff;
+
+ gas_assert (fragP->fr_subtype == RELAX_FILL_NOP
+ || fragP->fr_subtype == RELAX_UNREACHABLE
+ || (fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
+
+ stretch_me = future_alignment_required (fragP, stretch);
+ diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
+ if (diff == 0)
+ return 0;
+
+ if (diff < 0)
+ {
+ /* We expanded on a previous pass. Can we shrink now? */
+ long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
+ if (shrink <= stretch && stretch > 0)
+ {
+ fragP->tc_frag_data.text_expansion[0] = stretch_me;
+ return -shrink;
+ }
+ return 0;
+ }
+
+ /* Below here, diff > 0. */
+ fragP->tc_frag_data.text_expansion[0] = stretch_me;
+
+ return diff;
+}
+
+
+/* Return the address of the next frag that should be aligned.
+
+ By "address" we mean the address it _would_ be at if there
+ is no action taken to align it between here and the target frag.
+ In other words, if no narrows and no fill nops are used between
+ here and the frag to align, _even_if_ some of the frags we use
+ to align targets have already expanded on a previous relaxation
+ pass.
+
+ Also, count each frag that may be used to help align the target.
+
+ Return 0 if there are no frags left in the chain that need to be
+ aligned. */
+
+static addressT
+find_address_of_next_align_frag (fragS **fragPP,
+ int *wide_nops,
+ int *narrow_nops,
+ int *widens,
+ bfd_boolean *paddable)
+{
+ fragS *fragP = *fragPP;
+ addressT address = fragP->fr_address;
+
+ /* Do not reset the counts to 0. */
+
+ while (fragP)
+ {
+ /* Limit this to a small search. */
+ if (*widens >= (int) xtensa_fetch_width)
+ {
+ *fragPP = fragP;
+ return 0;
+ }
+ address += fragP->fr_fix;
+
+ if (fragP->fr_type == rs_fill)
+ address += fragP->fr_offset * fragP->fr_var;
+ else if (fragP->fr_type == rs_machine_dependent)
+ {
+ switch (fragP->fr_subtype)
+ {
+ case RELAX_UNREACHABLE:
+ *paddable = TRUE;
+ break;
+
+ case RELAX_FILL_NOP:
+ (*wide_nops)++;
+ if (!fragP->tc_frag_data.is_no_density)
+ (*narrow_nops)++;
+ break;
+
+ case RELAX_SLOTS:
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ {
+ (*widens)++;
+ break;
+ }
+ address += total_frag_text_expansion (fragP);
+ break;
+
+ case RELAX_IMMED:
+ address += fragP->tc_frag_data.text_expansion[0];
+ break;
+
+ case RELAX_ALIGN_NEXT_OPCODE:
+ case RELAX_DESIRE_ALIGN:
+ *fragPP = fragP;
+ return address;
+
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ /* Do nothing. */
+ break;
+
+ default:
+ /* Just punt if we don't know the type. */
+ *fragPP = fragP;
+ return 0;
+ }
+ }
+ else
+ {
+ /* Just punt if we don't know the type. */
+ *fragPP = fragP;
+ return 0;
+ }
+ fragP = fragP->fr_next;
+ }
+
+ *fragPP = fragP;
+ return 0;
+}
+
+
+static long bytes_to_stretch (fragS *, int, int, int, int);
+
+static long
+future_alignment_required (fragS *fragP, long stretch ATTRIBUTE_UNUSED)
+{
+ fragS *this_frag = fragP;
+ long address;
+ int num_widens = 0;
+ int wide_nops = 0;
+ int narrow_nops = 0;
+ bfd_boolean paddable = FALSE;
+ offsetT local_opt_diff;
+ offsetT opt_diff;
+ offsetT max_diff;
+ int stretch_amount = 0;
+ int local_stretch_amount;
+ int global_stretch_amount;
+
+ address = find_address_of_next_align_frag
+ (&fragP, &wide_nops, &narrow_nops, &num_widens, &paddable);
+
+ if (!address)
+ {
+ if (this_frag->tc_frag_data.is_aligning_branch)
+ this_frag->tc_frag_data.slot_subtypes[0] = RELAX_IMMED;
+ else
+ frag_wane (this_frag);
+ }
+ else
+ {
+ local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
+ opt_diff = local_opt_diff;
+ gas_assert (opt_diff >= 0);
+ gas_assert (max_diff >= opt_diff);
+ if (max_diff == 0)
+ return 0;
+
+ if (fragP)
+ fragP = fragP->fr_next;
+
+ while (fragP && opt_diff < max_diff && address)
+ {
+ /* We only use these to determine if we can exit early
+ because there will be plenty of ways to align future
+ align frags. */
+ int glob_widens = 0;
+ int dnn = 0;
+ int dw = 0;
+ bfd_boolean glob_pad = 0;
+ address = find_address_of_next_align_frag
+ (&fragP, &glob_widens, &dnn, &dw, &glob_pad);
+ /* If there is a padable portion, then skip. */
+ if (glob_pad || glob_widens >= (1 << branch_align_power (now_seg)))
+ address = 0;
+
+ if (address)
+ {
+ offsetT next_m_diff;
+ offsetT next_o_diff;
+
+ /* Downrange frags haven't had stretch added to them yet. */
+ address += stretch;
+
+ /* The address also includes any text expansion from this
+ frag in a previous pass, but we don't want that. */
+ address -= this_frag->tc_frag_data.text_expansion[0];
+
+ /* Assume we are going to move at least opt_diff. In
+ reality, we might not be able to, but assuming that
+ we will helps catch cases where moving opt_diff pushes
+ the next target from aligned to unaligned. */
+ address += opt_diff;
+
+ next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
+
+ /* Now cleanup for the adjustments to address. */
+ next_o_diff += opt_diff;
+ next_m_diff += opt_diff;
+ if (next_o_diff <= max_diff && next_o_diff > opt_diff)
+ opt_diff = next_o_diff;
+ if (next_m_diff < max_diff)
+ max_diff = next_m_diff;
+ fragP = fragP->fr_next;
+ }
+ }
+
+ /* If there are enough wideners in between, do it. */
+ if (paddable)
+ {
+ if (this_frag->fr_subtype == RELAX_UNREACHABLE)
+ {
+ gas_assert (opt_diff <= (signed) xtensa_fetch_width);
+ return opt_diff;
+ }
+ return 0;
+ }
+ local_stretch_amount
+ = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+ num_widens, local_opt_diff);
+ global_stretch_amount
+ = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+ num_widens, opt_diff);
+ /* If the condition below is true, then the frag couldn't
+ stretch the correct amount for the global case, so we just
+ optimize locally. We'll rely on the subsequent frags to get
+ the correct alignment in the global case. */
+ if (global_stretch_amount < local_stretch_amount)
+ stretch_amount = local_stretch_amount;
+ else
+ stretch_amount = global_stretch_amount;
+
+ if (this_frag->fr_subtype == RELAX_SLOTS
+ && this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ gas_assert (stretch_amount <= 1);
+ else if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ {
+ if (this_frag->tc_frag_data.is_no_density)
+ gas_assert (stretch_amount == 3 || stretch_amount == 0);
+ else
+ gas_assert (stretch_amount <= 3);
+ }
+ }
+ return stretch_amount;
+}
+
+
+/* The idea: widen everything you can to get a target or loop aligned,
+ then start using NOPs.
+
+ wide_nops = the number of wide NOPs available for aligning
+ narrow_nops = the number of narrow NOPs available for aligning
+ (a subset of wide_nops)
+ widens = the number of narrow instructions that should be widened
+
+*/
+
+static long
+bytes_to_stretch (fragS *this_frag,
+ int wide_nops,
+ int narrow_nops,
+ int num_widens,
+ int desired_diff)
+{
+ int nops_needed;
+ int nop_bytes;
+ int extra_bytes;
+ int bytes_short = desired_diff - num_widens;
+
+ gas_assert (desired_diff >= 0
+ && desired_diff < (signed) xtensa_fetch_width);
+ if (desired_diff == 0)
+ return 0;
+
+ gas_assert (wide_nops > 0 || num_widens > 0);
+
+ /* Always prefer widening to NOP-filling. */
+ if (bytes_short < 0)
+ {
+ /* There are enough RELAX_NARROW frags after this one
+ to align the target without widening this frag in any way. */
+ return 0;
+ }
+
+ if (bytes_short == 0)
+ {
+ /* Widen every narrow between here and the align target
+ and the align target will be properly aligned. */
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return 0;
+ else
+ return 1;
+ }
+
+ /* From here we will need at least one NOP to get an alignment.
+ However, we may not be able to align at all, in which case,
+ don't widen. */
+ nops_needed = desired_diff / 3;
+
+ /* If there aren't enough nops, don't widen. */
+ if (nops_needed > wide_nops)
+ return 0;
+
+ /* First try it with all wide nops. */
+ nop_bytes = nops_needed * 3;
+ extra_bytes = desired_diff - nop_bytes;
+
+ if (nop_bytes + num_widens >= desired_diff)
+ {
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return 3;
+ else if (num_widens == extra_bytes)
+ return 1;
+ return 0;
+ }
+
+ /* Add a narrow nop. */
+ nops_needed++;
+ nop_bytes += 2;
+ extra_bytes -= 2;
+ if (narrow_nops == 0 || nops_needed > wide_nops)
+ return 0;
+
+ if (nop_bytes + num_widens >= desired_diff && extra_bytes >= 0)
+ {
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return !this_frag->tc_frag_data.is_no_density ? 2 : 3;
+ else if (num_widens == extra_bytes)
+ return 1;
+ return 0;
+ }
+
+ /* Replace a wide nop with a narrow nop--we can get here if
+ extra_bytes was negative in the previous conditional. */
+ if (narrow_nops == 1)
+ return 0;
+ nop_bytes--;
+ extra_bytes++;
+ if (nop_bytes + num_widens >= desired_diff)
+ {
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return !this_frag->tc_frag_data.is_no_density ? 2 : 3;
+ else if (num_widens == extra_bytes)
+ return 1;
+ return 0;
+ }
+
+ /* If we can't satisfy any of the above cases, then we can't align
+ using padding or fill nops. */
+ return 0;
+}
+
+
+static struct trampoline_frag *
+search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
+{
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ struct trampoline_frag *tf = (ts) ? ts->trampoline_list.next : NULL;
+ struct trampoline_frag *best_tf = NULL;
+ int best_delta = 0;
+ int best_addr = 0;
+ symbolS *sym = tinsn->tok[0].X_add_symbol;
+ offsetT target = S_GET_VALUE (sym) + tinsn->tok[0].X_add_number;
+ offsetT addr = fragP->fr_address;
+ offsetT lower = (addr < target) ? addr : target;
+ offsetT upper = (addr > target) ? addr : target;
+ int delta = upper - lower;
+ offsetT midpoint = lower + delta / 2;
+ int this_delta = -1;
+ int this_addr = -1;
+
+ if (delta > 2 * J_RANGE)
+ {
+ /* One trampoline won't do; we need multiple.
+ Choose the farthest trampoline that's still in range of the original
+ and let a later pass finish the job. */
+ for ( ; tf; tf = tf->next)
+ {
+ int next_addr = (tf->next) ? tf->next->fragP->fr_address + tf->next->fragP->fr_fix : 0;
+
+ this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ if (lower == addr)
+ {
+ /* Forward jump. */
+ if (this_addr - addr < J_RANGE)
+ break;
+ }
+ else
+ {
+ /* Backward jump. */
+ if (next_addr == 0 || addr - next_addr > J_RANGE)
+ break;
+ }
+ if (abs (addr - this_addr) < J_RANGE)
+ return tf;
+
+ return NULL;
+ }
+ }
+ for ( ; tf; tf = tf->next)
+ {
+ this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ this_delta = abs (this_addr - midpoint);
+ if (unreachable_only && tf->needs_jump_around)
+ continue;
+ if (!best_tf || this_delta < best_delta)
+ {
+ best_tf = tf;
+ best_delta = this_delta;
+ best_addr = this_addr;
+ }
+ }
+
+ if (best_tf &&
+ best_delta < J_RANGE &&
+ abs(best_addr - lower) < J_RANGE &&
+ abs(best_addr - upper) < J_RANGE)
+ return best_tf;
+
+ return NULL; /* No suitable trampoline found. */
+}
+
+
+static struct trampoline_frag *
+get_best_trampoline (TInsn *tinsn, fragS *fragP)
+{
+ struct trampoline_frag *tf = NULL;
+
+ tf = search_trampolines (tinsn, fragP, TRUE); /* Try unreachable first. */
+
+ if (tf == NULL)
+ tf = search_trampolines (tinsn, fragP, FALSE); /* Try ones needing a jump-around, too. */
+
+ return tf;
+}
+
+
+static void
+check_and_update_trampolines (void)
+{
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ struct trampoline_frag *tf = ts->trampoline_list.next;
+ struct trampoline_frag *prev = &ts->trampoline_list;
+
+ for ( ; tf; prev = tf, tf = tf->next)
+ {
+ if (tf->fragP->fr_var < 3)
+ {
+ frag_wane (tf->fragP);
+ prev->next = tf->next;
+ tf->fragP = NULL;
+ }
+ }
+}
+
+
+static int
+init_trampoline_frag (struct trampoline_frag *trampP)
+{
+ fragS *fp = trampP->fragP;
+ int growth = 0;
+
+ if (fp->fr_fix == 0)
+ {
+ symbolS *lsym;
+ char label[10 + 2 * sizeof(fp)];
+ sprintf (label, ".L0_TR_%p", fp);
+
+ lsym = (symbolS *)local_symbol_make (label, now_seg, 0, fp);
+ fp->fr_symbol = lsym;
+ if (trampP->needs_jump_around)
+ {
+ /* Add a jump around this block of jumps, in case
+ control flows into this block. */
+ fixS *fixP;
+ TInsn insn;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+
+ fp->tc_frag_data.is_insn = 1;
+ /* Assemble a jump insn. */
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], lsym, 3);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
+ xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)fp->fr_literal, 3);
+ fp->fr_fix += 3;
+ fp->fr_var -= 3;
+ growth = 3;
+ fixP = fix_new (fp, 0, 3, lsym, 3, TRUE, BFD_RELOC_XTENSA_SLOT0_OP);
+ trampP->fixP = fixP;
+ }
+ }
+ return growth;
+}
+
+
+static int
+add_jump_to_trampoline (struct trampoline_frag *trampP, fragS *origfrag)
+{
+ fragS *tramp = trampP->fragP;
+ fixS *fixP;
+ int offset = tramp->fr_fix; /* Where to assemble the j insn. */
+ TInsn insn;
+ symbolS *lsym;
+ symbolS *tsym;
+ int toffset;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+ int growth = 0;
+
+ lsym = tramp->fr_symbol;
+ /* Assemble a jump to the target label in the trampoline frag. */
+ tsym = origfrag->tc_frag_data.slot_symbols[0];
+ toffset = origfrag-> tc_frag_data.slot_offsets[0];
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], tsym, toffset);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
+ xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)tramp->fr_literal + offset, 3);
+ tramp->fr_fix += 3;
+ tramp->fr_var -= 3;
+ growth = 3;
+ /* add a fix-up for the trampoline jump. */
+ fixP = fix_new (tramp, tramp->fr_fix - 3, 3, tsym, toffset, TRUE, BFD_RELOC_XTENSA_SLOT0_OP);
+ /* Modify the jump at the start of this trampoline to point past the newly-added jump. */
+ fixP = trampP->fixP;
+ if (fixP)
+ fixP->fx_offset += 3;
+ /* Modify the original j to point here. */
+ origfrag->tc_frag_data.slot_symbols[0] = lsym;
+ origfrag->tc_frag_data.slot_offsets[0] = tramp->fr_fix - 3;
+ /* If trampoline is full, remove it from the list. */
+ check_and_update_trampolines ();
+
+ return growth;
+}
+
+
+static long
+relax_frag_immed (segT segP,
+ fragS *fragP,
+ long stretch,
+ int min_steps,
+ xtensa_format fmt,
+ int slot,
+ int *stretched_p,
+ bfd_boolean estimate_only)
+{
+ TInsn tinsn;
+ int old_size;
+ bfd_boolean negatable_branch = FALSE;
+ bfd_boolean branch_jmp_to_next = FALSE;
+ bfd_boolean from_wide_insn = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
+ IStack istack;
+ offsetT frag_offset;
+ int num_steps;
+ int num_text_bytes, num_literal_bytes;
+ int literal_diff, total_text_diff, this_text_diff;
+
+ gas_assert (fragP->fr_opcode != NULL);
+
+ xg_clear_vinsn (&cur_vinsn);
+ vinsn_from_chars (&cur_vinsn, fragP->fr_opcode);
+ if (cur_vinsn.num_slots > 1)
+ from_wide_insn = TRUE;
+
+ tinsn = cur_vinsn.slots[slot];
+ tinsn_immed_from_frag (&tinsn, fragP, slot);
+
+ if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode) == 1)
+ return 0;
+
+ if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
+ branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
+
+ negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
+
+ old_size = xtensa_format_length (isa, fmt);
+
+ /* Special case: replace a branch to the next instruction with a NOP.
+ This is required to work around a hardware bug in T1040.0 and also
+ serves as an optimization. */
+
+ if (branch_jmp_to_next
+ && ((old_size == 2) || (old_size == 3))
+ && !next_frag_is_loop_target (fragP))
+ return 0;
+
+ /* Here is the fun stuff: Get the immediate field from this
+ instruction. If it fits, we are done. If not, find the next
+ instruction sequence that fits. */
+
+ frag_offset = fragP->fr_opcode - fragP->fr_literal;
+ istack_init (&istack);
+ num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
+ min_steps, stretch);
+ gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
+
+ fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
+
+ /* Figure out the number of bytes needed. */
+ num_literal_bytes = get_num_stack_literal_bytes (&istack);
+ literal_diff
+ = num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
+ num_text_bytes = get_num_stack_text_bytes (&istack);
+
+ if (from_wide_insn)
+ {
+ int first = 0;
+ while (istack.insn[first].opcode == XTENSA_UNDEFINED)
+ first++;
+
+ num_text_bytes += old_size;
+ if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
+ num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
+ else
+ {
+ /* The first instruction in the relaxed sequence will go after
+ the current wide instruction, and thus its symbolic immediates
+ might not fit. */
+
+ istack_init (&istack);
+ num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP,
+ frag_offset + old_size,
+ min_steps, stretch + old_size);
+ gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
+
+ fragP->tc_frag_data.slot_subtypes[slot]
+ = (int) RELAX_IMMED + num_steps;
+
+ num_literal_bytes = get_num_stack_literal_bytes (&istack);
+ literal_diff
+ = num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
+
+ num_text_bytes = get_num_stack_text_bytes (&istack) + old_size;
+ }
+ }
+
+ total_text_diff = num_text_bytes - old_size;
+ this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
+
+ /* It MUST get larger. If not, we could get an infinite loop. */
+ gas_assert (num_text_bytes >= 0);
+ gas_assert (literal_diff >= 0);
+ gas_assert (total_text_diff >= 0);
+
+ fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
+ fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
+ gas_assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
+ gas_assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
+
+ /* Find the associated expandable literal for this. */
+ if (literal_diff != 0)
+ {
+ fragS *lit_fragP = fragP->tc_frag_data.literal_frags[slot];
+ if (lit_fragP)
+ {
+ gas_assert (literal_diff == 4);
+ lit_fragP->tc_frag_data.unreported_expansion += literal_diff;
+
+ /* We expect that the literal section state has NOT been
+ modified yet. */
+ gas_assert (lit_fragP->fr_type == rs_machine_dependent
+ && lit_fragP->fr_subtype == RELAX_LITERAL);
+ lit_fragP->fr_subtype = RELAX_LITERAL_NR;
+
+ /* We need to mark this section for another iteration
+ of relaxation. */
+ (*stretched_p)++;
+ }
+ }
+
+ if (negatable_branch && istack.ninsn > 1)
+ update_next_frag_state (fragP);
+
+ /* If last insn is a jump, and it cannot reach its target, try to find a trampoline. */
+ if (istack.ninsn > 2 &&
+ istack.insn[istack.ninsn - 1].insn_type == ITYPE_LABEL &&
+ istack.insn[istack.ninsn - 2].insn_type == ITYPE_INSN &&
+ istack.insn[istack.ninsn - 2].opcode == xtensa_j_opcode)
+ {
+ TInsn *jinsn = &istack.insn[istack.ninsn - 2];
+
+ if (!xg_symbolic_immeds_fit (jinsn, segP, fragP, fragP->fr_offset, total_text_diff))
+ {
+ struct trampoline_frag *tf = get_best_trampoline (jinsn, fragP);
+
+ if (tf)
+ {
+ this_text_diff += init_trampoline_frag (tf);
+ this_text_diff += add_jump_to_trampoline (tf, fragP);
+ }
+ else
+ {
+ /* If target symbol is undefined, assume it will reach once linked. */
+ expressionS *exp = &istack.insn[istack.ninsn - 2].tok[0];
+
+ if (exp->X_op == O_symbol && S_IS_DEFINED (exp->X_add_symbol))
+ {
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("jump target out of range; no usable trampoline found"));
+ }
+ }
+ }
+ }
+
+ return this_text_diff;
+}
+
+
+/* md_convert_frag Hook and Helper Functions. */
+
+static void convert_frag_align_next_opcode (fragS *);
+static void convert_frag_narrow (segT, fragS *, xtensa_format, int);
+static void convert_frag_fill_nop (fragS *);
+static void convert_frag_immed (segT, fragS *, int, xtensa_format, int);
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
+{
+ static xtensa_insnbuf vbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ int slot;
+ int num_slots;
+ xtensa_format fmt;
+ char *file_name;
+ unsigned line;
+
+ as_where (&file_name, &line);
+ new_logical_line (fragp->fr_file, fragp->fr_line);
+
+ switch (fragp->fr_subtype)
+ {
+ case RELAX_ALIGN_NEXT_OPCODE:
+ /* Always convert. */
+ convert_frag_align_next_opcode (fragp);
+ break;
+
+ case RELAX_DESIRE_ALIGN:
+ /* Do nothing. If not aligned already, too bad. */
+ break;
+
+ case RELAX_LITERAL:
+ case RELAX_LITERAL_FINAL:
+ break;
+
+ case RELAX_SLOTS:
+ if (vbuf == NULL)
+ vbuf = xtensa_insnbuf_alloc (isa);
+
+ xtensa_insnbuf_from_chars
+ (isa, vbuf, (unsigned char *) fragp->fr_opcode, 0);
+ fmt = xtensa_format_decode (isa, vbuf);
+ num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (slot = 0; slot < num_slots; slot++)
+ {
+ switch (fragp->tc_frag_data.slot_subtypes[slot])
+ {
+ case RELAX_NARROW:
+ convert_frag_narrow (sec, fragp, fmt, slot);
+ break;
+
+ case RELAX_IMMED:
+ case RELAX_IMMED_STEP1:
+ case RELAX_IMMED_STEP2:
+ case RELAX_IMMED_STEP3:
+ /* Place the immediate. */
+ convert_frag_immed
+ (sec, fragp,
+ fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+ fmt, slot);
+ break;
+
+ default:
+ /* This is OK because some slots could have
+ relaxations and others have none. */
+ break;
+ }
+ }
+ break;
+
+ case RELAX_UNREACHABLE:
+ memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
+ fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
+ fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
+ frag_wane (fragp);
+ break;
+
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ frag_wane (fragp);
+ break;
+
+ case RELAX_FILL_NOP:
+ convert_frag_fill_nop (fragp);
+ break;
+
+ case RELAX_LITERAL_NR:
+ if (use_literal_section)
+ {
+ /* This should have been handled during relaxation. When
+ relaxing a code segment, literals sometimes need to be
+ added to the corresponding literal segment. If that
+ literal segment has already been relaxed, then we end up
+ in this situation. Marking the literal segments as data
+ would make this happen less often (since GAS always relaxes
+ code before data), but we could still get into trouble if
+ there are instructions in a segment that is not marked as
+ containing code. Until we can implement a better solution,
+ cheat and adjust the addresses of all the following frags.
+ This could break subsequent alignments, but the linker's
+ literal coalescing will do that anyway. */
+
+ fragS *f;
+ fragp->fr_subtype = RELAX_LITERAL_FINAL;
+ gas_assert (fragp->tc_frag_data.unreported_expansion == 4);
+ memset (&fragp->fr_literal[fragp->fr_fix], 0, 4);
+ fragp->fr_var -= 4;
+ fragp->fr_fix += 4;
+ for (f = fragp->fr_next; f; f = f->fr_next)
+ f->fr_address += 4;
+ }
+ else
+ as_bad (_("invalid relaxation fragment result"));
+ break;
+
+ case RELAX_TRAMPOLINE:
+ break;
+ }
+
+ fragp->fr_var = 0;
+ new_logical_line (file_name, line);
+}
+
+
+static void
+convert_frag_align_next_opcode (fragS *fragp)
+{
+ char *nop_buf; /* Location for Writing. */
+ bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
+ addressT aligned_address;
+ offsetT fill_size;
+ int nop, nop_count;
+
+ aligned_address = get_noop_aligned_address (fragp, fragp->fr_address +
+ fragp->fr_fix);
+ fill_size = aligned_address - (fragp->fr_address + fragp->fr_fix);
+ nop_count = get_text_align_nop_count (fill_size, use_no_density);
+ nop_buf = fragp->fr_literal + fragp->fr_fix;
+
+ for (nop = 0; nop < nop_count; nop++)
+ {
+ int nop_size;
+ nop_size = get_text_align_nth_nop_size (fill_size, nop, use_no_density);
+
+ assemble_nop (nop_size, nop_buf);
+ nop_buf += nop_size;
+ }
+
+ fragp->fr_fix += fill_size;
+ fragp->fr_var -= fill_size;
+}
+
+
+static void
+convert_frag_narrow (segT segP, fragS *fragP, xtensa_format fmt, int slot)
+{
+ TInsn tinsn, single_target;
+ int size, old_size, diff;
+ offsetT frag_offset;
+
+ gas_assert (slot == 0);
+ tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
+
+ if (fragP->tc_frag_data.is_aligning_branch == 1)
+ {
+ gas_assert (fragP->tc_frag_data.text_expansion[0] == 1
+ || fragP->tc_frag_data.text_expansion[0] == 0);
+ convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
+ fmt, slot);
+ return;
+ }
+
+ if (fragP->tc_frag_data.text_expansion[0] == 0)
+ {
+ /* No conversion. */
+ fragP->fr_var = 0;
+ return;
+ }
+
+ gas_assert (fragP->fr_opcode != NULL);
+
+ /* Frags in this relaxation state should only contain
+ single instruction bundles. */
+ tinsn_immed_from_frag (&tinsn, fragP, 0);
+
+ /* Just convert it to a wide form.... */
+ size = 0;
+ old_size = xg_get_single_size (tinsn.opcode);
+
+ tinsn_init (&single_target);
+ frag_offset = fragP->fr_opcode - fragP->fr_literal;
+
+ if (! xg_is_single_relaxable_insn (&tinsn, &single_target, FALSE))
+ {
+ as_bad (_("unable to widen instruction"));
+ return;
+ }
+
+ size = xg_get_single_size (single_target.opcode);
+ xg_emit_insn_to_buf (&single_target, fragP->fr_opcode, fragP,
+ frag_offset, TRUE);
+
+ diff = size - old_size;
+ gas_assert (diff >= 0);
+ gas_assert (diff <= fragP->fr_var);
+ fragP->fr_var -= diff;
+ fragP->fr_fix += diff;
+
+ /* clean it up */
+ fragP->fr_var = 0;
+}
+
+
+static void
+convert_frag_fill_nop (fragS *fragP)
+{
+ char *loc = &fragP->fr_literal[fragP->fr_fix];
+ int size = fragP->tc_frag_data.text_expansion[0];
+ gas_assert ((unsigned) size == (fragP->fr_next->fr_address
+ - fragP->fr_address - fragP->fr_fix));
+ if (size == 0)
+ {
+ /* No conversion. */
+ fragP->fr_var = 0;
+ return;
+ }
+ assemble_nop (size, loc);
+ fragP->tc_frag_data.is_insn = TRUE;
+ fragP->fr_var -= size;
+ fragP->fr_fix += size;
+ frag_wane (fragP);
+}
+
+
+static fixS *fix_new_exp_in_seg
+ (segT, subsegT, fragS *, int, int, expressionS *, int,
+ bfd_reloc_code_real_type);
+static void convert_frag_immed_finish_loop (segT, fragS *, TInsn *);
+
+static void
+convert_frag_immed (segT segP,
+ fragS *fragP,
+ int min_steps,
+ xtensa_format fmt,
+ int slot)
+{
+ char *immed_instr = fragP->fr_opcode;
+ TInsn orig_tinsn;
+ bfd_boolean expanded = FALSE;
+ bfd_boolean branch_jmp_to_next = FALSE;
+ char *fr_opcode = fragP->fr_opcode;
+ xtensa_isa isa = xtensa_default_isa;
+ bfd_boolean from_wide_insn = FALSE;
+ int bytes;
+ bfd_boolean is_loop;
+
+ gas_assert (fr_opcode != NULL);
+
+ xg_clear_vinsn (&cur_vinsn);
+
+ vinsn_from_chars (&cur_vinsn, fr_opcode);
+ if (cur_vinsn.num_slots > 1)
+ from_wide_insn = TRUE;
+
+ orig_tinsn = cur_vinsn.slots[slot];
+ tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
+
+ is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
+
+ if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
+ branch_jmp_to_next = is_branch_jmp_to_next (&orig_tinsn, fragP);
+
+ if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
+ {
+ /* Conversion just inserts a NOP and marks the fix as completed. */
+ bytes = xtensa_format_length (isa, fmt);
+ if (bytes >= 4)
+ {
+ cur_vinsn.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, cur_vinsn.format, slot);
+ cur_vinsn.slots[slot].ntok = 0;
+ }
+ else
+ {
+ bytes += fragP->tc_frag_data.text_expansion[0];
+ gas_assert (bytes == 2 || bytes == 3);
+ build_nop (&cur_vinsn.slots[0], bytes);
+ fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
+ }
+ vinsn_to_insnbuf (&cur_vinsn, fr_opcode, frag_now, TRUE);
+ xtensa_insnbuf_to_chars
+ (isa, cur_vinsn.insnbuf, (unsigned char *) fr_opcode, 0);
+ fragP->fr_var = 0;
+ }
+ else
+ {
+ /* Here is the fun stuff: Get the immediate field from this
+ instruction. If it fits, we're done. If not, find the next
+ instruction sequence that fits. */
+
+ IStack istack;
+ int i;
+ symbolS *lit_sym = NULL;
+ int total_size = 0;
+ int target_offset = 0;
+ int old_size;
+ int diff;
+ symbolS *gen_label = NULL;
+ offsetT frag_offset;
+ bfd_boolean first = TRUE;
+
+ /* It does not fit. Find something that does and
+ convert immediately. */
+ frag_offset = fr_opcode - fragP->fr_literal;
+ istack_init (&istack);
+ xg_assembly_relax (&istack, &orig_tinsn,
+ segP, fragP, frag_offset, min_steps, 0);
+
+ old_size = xtensa_format_length (isa, fmt);
+
+ /* Assemble this right inline. */
+
+ /* First, create the mapping from a label name to the REAL label. */
+ target_offset = 0;
+ for (i = 0; i < istack.ninsn; i++)
+ {
+ TInsn *tinsn = &istack.insn[i];
+ fragS *lit_frag;
+
+ switch (tinsn->insn_type)
+ {
+ case ITYPE_LITERAL:
+ if (lit_sym != NULL)
+ as_bad (_("multiple literals in expansion"));
+ /* First find the appropriate space in the literal pool. */
+ lit_frag = fragP->tc_frag_data.literal_frags[slot];
+ if (lit_frag == NULL)
+ as_bad (_("no registered fragment for literal"));
+ if (tinsn->ntok != 1)
+ as_bad (_("number of literal tokens != 1"));
+
+ /* Set the literal symbol and add a fixup. */
+ lit_sym = lit_frag->fr_symbol;
+ break;
+
+ case ITYPE_LABEL:
+ if (align_targets && !is_loop)
+ {
+ fragS *unreach = fragP->fr_next;
+ while (!(unreach->fr_type == rs_machine_dependent
+ && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || unreach->fr_subtype == RELAX_UNREACHABLE)))
+ {
+ unreach = unreach->fr_next;
+ }
+
+ gas_assert (unreach->fr_type == rs_machine_dependent
+ && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || unreach->fr_subtype == RELAX_UNREACHABLE));
+
+ target_offset += unreach->tc_frag_data.text_expansion[0];
+ }
+ gas_assert (gen_label == NULL);
+ gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
+ fr_opcode - fragP->fr_literal
+ + target_offset, fragP);
+ break;
+
+ case ITYPE_INSN:
+ if (first && from_wide_insn)
+ {
+ target_offset += xtensa_format_length (isa, fmt);
+ first = FALSE;
+ if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ target_offset += xg_get_single_size (tinsn->opcode);
+ }
+ else
+ target_offset += xg_get_single_size (tinsn->opcode);
+ break;
+ }
+ }
+
+ total_size = 0;
+ first = TRUE;
+ for (i = 0; i < istack.ninsn; i++)
+ {
+ TInsn *tinsn = &istack.insn[i];
+ fragS *lit_frag;
+ int size;
+ segT target_seg;
+ bfd_reloc_code_real_type reloc_type;
+
+ switch (tinsn->insn_type)
+ {
+ case ITYPE_LITERAL:
+ lit_frag = fragP->tc_frag_data.literal_frags[slot];
+ /* Already checked. */
+ gas_assert (lit_frag != NULL);
+ gas_assert (lit_sym != NULL);
+ gas_assert (tinsn->ntok == 1);
+ /* Add a fixup. */
+ target_seg = S_GET_SEGMENT (lit_sym);
+ gas_assert (target_seg);
+ reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op, TRUE);
+ fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
+ &tinsn->tok[0], FALSE, reloc_type);
+ break;
+
+ case ITYPE_LABEL:
+ break;
+
+ case ITYPE_INSN:
+ xg_resolve_labels (tinsn, gen_label);
+ xg_resolve_literals (tinsn, lit_sym);
+ if (from_wide_insn && first)
+ {
+ first = FALSE;
+ if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ {
+ cur_vinsn.slots[slot] = *tinsn;
+ }
+ else
+ {
+ cur_vinsn.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, fmt, slot);
+ cur_vinsn.slots[slot].ntok = 0;
+ }
+ vinsn_to_insnbuf (&cur_vinsn, immed_instr, fragP, TRUE);
+ xtensa_insnbuf_to_chars (isa, cur_vinsn.insnbuf,
+ (unsigned char *) immed_instr, 0);
+ fragP->tc_frag_data.is_insn = TRUE;
+ size = xtensa_format_length (isa, fmt);
+ if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ {
+ xg_emit_insn_to_buf
+ (tinsn, immed_instr + size, fragP,
+ immed_instr - fragP->fr_literal + size, TRUE);
+ size += xg_get_single_size (tinsn->opcode);
+ }
+ }
+ else
+ {
+ size = xg_get_single_size (tinsn->opcode);
+ xg_emit_insn_to_buf (tinsn, immed_instr, fragP,
+ immed_instr - fragP->fr_literal, TRUE);
+ }
+ immed_instr += size;
+ total_size += size;
+ break;
+ }
+ }
+
+ diff = total_size - old_size;
+ gas_assert (diff >= 0);
+ if (diff != 0)
+ expanded = TRUE;
+ gas_assert (diff <= fragP->fr_var);
+ fragP->fr_var -= diff;
+ fragP->fr_fix += diff;
+ }
+
+ /* Check for undefined immediates in LOOP instructions. */
+ if (is_loop)
+ {
+ symbolS *sym;
+ sym = orig_tinsn.tok[1].X_add_symbol;
+ if (sym != NULL && !S_IS_DEFINED (sym))
+ {
+ as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
+ return;
+ }
+ sym = orig_tinsn.tok[1].X_op_symbol;
+ if (sym != NULL && !S_IS_DEFINED (sym))
+ {
+ as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
+ return;
+ }
+ }
+
+ if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
+ convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
+
+ if (expanded && is_direct_call_opcode (orig_tinsn.opcode))
+ {
+ /* Add an expansion note on the expanded instruction. */
+ fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
+ &orig_tinsn.tok[0], TRUE,
+ BFD_RELOC_XTENSA_ASM_EXPAND);
+ }
+}
+
+
+/* Add a new fix expression into the desired segment. We have to
+ switch to that segment to do this. */
+
+static fixS *
+fix_new_exp_in_seg (segT new_seg,
+ subsegT new_subseg,
+ fragS *frag,
+ int where,
+ int size,
+ expressionS *exp,
+ int pcrel,
+ bfd_reloc_code_real_type r_type)
+{
+ fixS *new_fix;
+ segT seg = now_seg;
+ subsegT subseg = now_subseg;
+
+ gas_assert (new_seg != 0);
+ subseg_set (new_seg, new_subseg);
+
+ new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
+ subseg_set (seg, subseg);
+ return new_fix;
+}
+
+
+/* Relax a loop instruction so that it can span loop >256 bytes.
+
+ loop as, .L1
+ .L0:
+ rsr as, LEND
+ wsr as, LBEG
+ addi as, as, lo8 (label-.L1)
+ addmi as, as, mid8 (label-.L1)
+ wsr as, LEND
+ isync
+ rsr as, LCOUNT
+ addi as, as, 1
+ .L1:
+ <<body>>
+ label:
+*/
+
+static void
+convert_frag_immed_finish_loop (segT segP, fragS *fragP, TInsn *tinsn)
+{
+ TInsn loop_insn;
+ TInsn addi_insn;
+ TInsn addmi_insn;
+ unsigned long target;
+ static xtensa_insnbuf insnbuf = NULL;
+ unsigned int loop_length, loop_length_hi, loop_length_lo;
+ xtensa_isa isa = xtensa_default_isa;
+ addressT loop_offset;
+ addressT addi_offset = 9;
+ addressT addmi_offset = 12;
+ fragS *next_fragP;
+ int target_count;
+
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
+ /* Get the loop offset. */
+ loop_offset = get_expanded_loop_offset (tinsn->opcode);
+
+ /* Validate that there really is a LOOP at the loop_offset. Because
+ loops are not bundleable, we can assume that the instruction will be
+ in slot 0. */
+ tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
+ tinsn_immed_from_frag (&loop_insn, fragP, 0);
+
+ gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
+ addi_offset += loop_offset;
+ addmi_offset += loop_offset;
+
+ gas_assert (tinsn->ntok == 2);
+ if (tinsn->tok[1].X_op == O_constant)
+ target = tinsn->tok[1].X_add_number;
+ else if (tinsn->tok[1].X_op == O_symbol)
+ {
+ /* Find the fragment. */
+ symbolS *sym = tinsn->tok[1].X_add_symbol;
+ gas_assert (S_GET_SEGMENT (sym) == segP
+ || S_GET_SEGMENT (sym) == absolute_section);
+ target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
+ }
+ else
+ {
+ as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
+ target = 0;
+ }
+
+ loop_length = target - (fragP->fr_address + fragP->fr_fix);
+ loop_length_hi = loop_length & ~0x0ff;
+ loop_length_lo = loop_length & 0x0ff;
+ if (loop_length_lo >= 128)
+ {
+ loop_length_lo -= 256;
+ loop_length_hi += 256;
+ }
+
+ /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
+ 32512. If the loop is larger than that, then we just fail. */
+ if (loop_length_hi > 32512)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("loop too long for LOOP instruction"));
+
+ tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
+ gas_assert (addi_insn.opcode == xtensa_addi_opcode);
+
+ tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
+ gas_assert (addmi_insn.opcode == xtensa_addmi_opcode);
+
+ set_expr_const (&addi_insn.tok[2], loop_length_lo);
+ tinsn_to_insnbuf (&addi_insn, insnbuf);
+
+ fragP->tc_frag_data.is_insn = TRUE;
+ xtensa_insnbuf_to_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addi_offset, 0);
+
+ set_expr_const (&addmi_insn.tok[2], loop_length_hi);
+ tinsn_to_insnbuf (&addmi_insn, insnbuf);
+ xtensa_insnbuf_to_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addmi_offset, 0);
+
+ /* Walk through all of the frags from here to the loop end
+ and mark them as no_transform to keep them from being modified
+ by the linker. If we ever have a relocation for the
+ addi/addmi of the difference of two symbols we can remove this. */
+
+ target_count = 0;
+ for (next_fragP = fragP; next_fragP != NULL;
+ next_fragP = next_fragP->fr_next)
+ {
+ next_fragP->tc_frag_data.is_no_transform = TRUE;
+ if (next_fragP->tc_frag_data.is_loop_target)
+ target_count++;
+ if (target_count == 2)
+ break;
+ }
+}
+
+
+/* A map that keeps information on a per-subsegment basis. This is
+ maintained during initial assembly, but is invalid once the
+ subsegments are smashed together. I.E., it cannot be used during
+ the relaxation. */
+
+typedef struct subseg_map_struct
+{
+ /* the key */
+ segT seg;
+ subsegT subseg;
+
+ /* the data */
+ unsigned flags;
+ float total_freq; /* fall-through + branch target frequency */
+ float target_freq; /* branch target frequency alone */
+
+ struct subseg_map_struct *next;
+} subseg_map;
+
+
+static subseg_map *sseg_map = NULL;
+
+static subseg_map *
+get_subseg_info (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e;
+
+ for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
+ {
+ if (seg == subseg_e->seg && subseg == subseg_e->subseg)
+ break;
+ }
+ return subseg_e;
+}
+
+
+static subseg_map *
+add_subseg_info (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
+ memset (subseg_e, 0, sizeof (subseg_map));
+ subseg_e->seg = seg;
+ subseg_e->subseg = subseg;
+ subseg_e->flags = 0;
+ /* Start off considering every branch target very important. */
+ subseg_e->target_freq = 1.0;
+ subseg_e->total_freq = 1.0;
+ subseg_e->next = sseg_map;
+ sseg_map = subseg_e;
+ return subseg_e;
+}
+
+
+static unsigned
+get_last_insn_flags (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (subseg_e)
+ return subseg_e->flags;
+ return 0;
+}
+
+
+static void
+set_last_insn_flags (segT seg,
+ subsegT subseg,
+ unsigned fl,
+ bfd_boolean val)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (! subseg_e)
+ subseg_e = add_subseg_info (seg, subseg);
+ if (val)
+ subseg_e->flags |= fl;
+ else
+ subseg_e->flags &= ~fl;
+}
+
+
+static float
+get_subseg_total_freq (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (subseg_e)
+ return subseg_e->total_freq;
+ return 1.0;
+}
+
+
+static float
+get_subseg_target_freq (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (subseg_e)
+ return subseg_e->target_freq;
+ return 1.0;
+}
+
+
+static void
+set_subseg_freq (segT seg, subsegT subseg, float total_f, float target_f)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (! subseg_e)
+ subseg_e = add_subseg_info (seg, subseg);
+ subseg_e->total_freq = total_f;
+ subseg_e->target_freq = target_f;
+}
+
+
+/* Segment Lists and emit_state Stuff. */
+
+static void
+xtensa_move_seg_list_to_beginning (seg_list *head)
+{
+ head = head->next;
+ while (head)
+ {
+ segT literal_section = head->seg;
+
+ /* Move the literal section to the front of the section list. */
+ gas_assert (literal_section);
+ if (literal_section != stdoutput->sections)
+ {
+ bfd_section_list_remove (stdoutput, literal_section);
+ bfd_section_list_prepend (stdoutput, literal_section);
+ }
+ head = head->next;
+ }
+}
+
+
+static void mark_literal_frags (seg_list *);
+
+static void
+xtensa_move_literals (void)
+{
+ seg_list *segment;
+ frchainS *frchain_from, *frchain_to;
+ fragS *search_frag, *next_frag, *literal_pool, *insert_after;
+ fragS **frag_splice;
+ emit_state state;
+ segT dest_seg;
+ fixS *fix, *next_fix, **fix_splice;
+ sym_list *lit;
+
+ mark_literal_frags (literal_head->next);
+
+ if (use_literal_section)
+ return;
+
+ for (segment = literal_head->next; segment; segment = segment->next)
+ {
+ /* Keep the literals for .init and .fini in separate sections. */
+ if (!strcmp (segment_name (segment->seg), INIT_SECTION_NAME)
+ || !strcmp (segment_name (segment->seg), FINI_SECTION_NAME))
+ continue;
+
+ frchain_from = seg_info (segment->seg)->frchainP;
+ search_frag = frchain_from->frch_root;
+ literal_pool = NULL;
+ frchain_to = NULL;
+ frag_splice = &(frchain_from->frch_root);
+
+ while (!search_frag->tc_frag_data.literal_frag)
+ {
+ gas_assert (search_frag->fr_fix == 0
+ || search_frag->fr_type == rs_align);
+ search_frag = search_frag->fr_next;
+ }
+
+ gas_assert (search_frag->tc_frag_data.literal_frag->fr_subtype
+ == RELAX_LITERAL_POOL_BEGIN);
+ xtensa_switch_section_emit_state (&state, segment->seg, 0);
+
+ /* Make sure that all the frags in this series are closed, and
+ that there is at least one left over of zero-size. This
+ prevents us from making a segment with an frchain without any
+ frags in it. */
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+
+ while (search_frag != frag_now)
+ {
+ next_frag = search_frag->fr_next;
+
+ /* First, move the frag out of the literal section and
+ to the appropriate place. */
+ if (search_frag->tc_frag_data.literal_frag)
+ {
+ literal_pool = search_frag->tc_frag_data.literal_frag;
+ gas_assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
+ frchain_to = literal_pool->tc_frag_data.lit_frchain;
+ gas_assert (frchain_to);
+ }
+ insert_after = literal_pool->tc_frag_data.literal_frag;
+ dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
+
+ *frag_splice = next_frag;
+ search_frag->fr_next = insert_after->fr_next;
+ insert_after->fr_next = search_frag;
+ search_frag->tc_frag_data.lit_seg = dest_seg;
+ literal_pool->tc_frag_data.literal_frag = search_frag;
+
+ /* Now move any fixups associated with this frag to the
+ right section. */
+ fix = frchain_from->fix_root;
+ fix_splice = &(frchain_from->fix_root);
+ while (fix)
+ {
+ next_fix = fix->fx_next;
+ if (fix->fx_frag == search_frag)
+ {
+ *fix_splice = next_fix;
+ fix->fx_next = frchain_to->fix_root;
+ frchain_to->fix_root = fix;
+ if (frchain_to->fix_tail == NULL)
+ frchain_to->fix_tail = fix;
+ }
+ else
+ fix_splice = &(fix->fx_next);
+ fix = next_fix;
+ }
+ search_frag = next_frag;
+ }
+
+ if (frchain_from->fix_root != NULL)
+ {
+ frchain_from = seg_info (segment->seg)->frchainP;
+ as_warn (_("fixes not all moved from %s"), segment->seg->name);
+
+ gas_assert (frchain_from->fix_root == NULL);
+ }
+ frchain_from->fix_tail = NULL;
+ xtensa_restore_emit_state (&state);
+ }
+
+ /* Now fix up the SEGMENT value for all the literal symbols. */
+ for (lit = literal_syms; lit; lit = lit->next)
+ {
+ symbolS *lit_sym = lit->sym;
+ segT dseg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
+ if (dseg)
+ S_SET_SEGMENT (lit_sym, dseg);
+ }
+}
+
+
+/* Walk over all the frags for segments in a list and mark them as
+ containing literals. As clunky as this is, we can't rely on frag_var
+ and frag_variant to get called in all situations. */
+
+static void
+mark_literal_frags (seg_list *segment)
+{
+ frchainS *frchain_from;
+ fragS *search_frag;
+
+ while (segment)
+ {
+ frchain_from = seg_info (segment->seg)->frchainP;
+ search_frag = frchain_from->frch_root;
+ while (search_frag)
+ {
+ search_frag->tc_frag_data.is_literal = TRUE;
+ search_frag = search_frag->fr_next;
+ }
+ segment = segment->next;
+ }
+}
+
+
+static void
+xtensa_reorder_seg_list (seg_list *head, segT after)
+{
+ /* Move all of the sections in the section list to come
+ after "after" in the gnu segment list. */
+
+ head = head->next;
+ while (head)
+ {
+ segT literal_section = head->seg;
+
+ /* Move the literal section after "after". */
+ gas_assert (literal_section);
+ if (literal_section != after)
+ {
+ bfd_section_list_remove (stdoutput, literal_section);
+ bfd_section_list_insert_after (stdoutput, after, literal_section);
+ }
+
+ head = head->next;
+ }
+}
+
+
+/* Push all the literal segments to the end of the gnu list. */
+
+static void
+xtensa_reorder_segments (void)
+{
+ segT sec;
+ segT last_sec = 0;
+ int old_count = 0;
+ int new_count = 0;
+
+ for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
+ {
+ last_sec = sec;
+ old_count++;
+ }
+
+ /* Now that we have the last section, push all the literal
+ sections to the end. */
+ xtensa_reorder_seg_list (literal_head, last_sec);
+
+ /* Now perform the final error check. */
+ for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
+ new_count++;
+ gas_assert (new_count == old_count);
+}
+
+
+/* Change the emit state (seg, subseg, and frag related stuff) to the
+ correct location. Return a emit_state which can be passed to
+ xtensa_restore_emit_state to return to current fragment. */
+
+static void
+xtensa_switch_to_literal_fragment (emit_state *result)
+{
+ if (directive_state[directive_absolute_literals])
+ {
+ segT lit4_seg = cache_literal_section (TRUE);
+ xtensa_switch_section_emit_state (result, lit4_seg, 0);
+ }
+ else
+ xtensa_switch_to_non_abs_literal_fragment (result);
+
+ /* Do a 4-byte align here. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+}
+
+
+static void
+xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
+{
+ static bfd_boolean recursive = FALSE;
+ fragS *pool_location = get_literal_pool_location (now_seg);
+ segT lit_seg;
+ bfd_boolean is_init =
+ (now_seg && !strcmp (segment_name (now_seg), INIT_SECTION_NAME));
+ bfd_boolean is_fini =
+ (now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME));
+
+ if (pool_location == NULL
+ && !use_literal_section
+ && !recursive
+ && !is_init && ! is_fini)
+ {
+ as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
+
+ /* When we mark a literal pool location, we want to put a frag in
+ the literal pool that points to it. But to do that, we want to
+ switch_to_literal_fragment. But literal sections don't have
+ literal pools, so their location is always null, so we would
+ recurse forever. This is kind of hacky, but it works. */
+
+ recursive = TRUE;
+ xtensa_mark_literal_pool_location ();
+ recursive = FALSE;
+ }
+
+ lit_seg = cache_literal_section (FALSE);
+ xtensa_switch_section_emit_state (result, lit_seg, 0);
+
+ if (!use_literal_section
+ && !is_init && !is_fini
+ && get_literal_pool_location (now_seg) != pool_location)
+ {
+ /* Close whatever frag is there. */
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.literal_frag = pool_location;
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+}
+
+
+/* Call this function before emitting data into the literal section.
+ This is a helper function for xtensa_switch_to_literal_fragment.
+ This is similar to a .section new_now_seg subseg. */
+
+static void
+xtensa_switch_section_emit_state (emit_state *state,
+ segT new_now_seg,
+ subsegT new_now_subseg)
+{
+ state->name = now_seg->name;
+ state->now_seg = now_seg;
+ state->now_subseg = now_subseg;
+ state->generating_literals = generating_literals;
+ generating_literals++;
+ subseg_set (new_now_seg, new_now_subseg);
+}
+
+
+/* Use to restore the emitting into the normal place. */
+
+static void
+xtensa_restore_emit_state (emit_state *state)
+{
+ generating_literals = state->generating_literals;
+ subseg_set (state->now_seg, state->now_subseg);
+}
+
+
+/* Predicate function used to look up a section in a particular group. */
+
+static bfd_boolean
+match_section_group (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *inf)
+{
+ const char *gname = inf;
+ const char *group_name = elf_group_name (sec);
+
+ return (group_name == gname
+ || (group_name != NULL
+ && gname != NULL
+ && strcmp (group_name, gname) == 0));
+}
+
+
+/* Get the literal section to be used for the current text section.
+ The result may be cached in the default_lit_sections structure. */
+
+static segT
+cache_literal_section (bfd_boolean use_abs_literals)
+{
+ const char *text_name, *group_name = 0;
+ char *base_name, *name, *suffix;
+ segT *pcached;
+ segT seg, current_section;
+ int current_subsec;
+ bfd_boolean linkonce = FALSE;
+
+ /* Save the current section/subsection. */
+ current_section = now_seg;
+ current_subsec = now_subseg;
+
+ /* Clear the cached values if they are no longer valid. */
+ if (now_seg != default_lit_sections.current_text_seg)
+ {
+ default_lit_sections.current_text_seg = now_seg;
+ default_lit_sections.lit_seg = NULL;
+ default_lit_sections.lit4_seg = NULL;
+ }
+
+ /* Check if the literal section is already cached. */
+ if (use_abs_literals)
+ pcached = &default_lit_sections.lit4_seg;
+ else
+ pcached = &default_lit_sections.lit_seg;
+
+ if (*pcached)
+ return *pcached;
+
+ text_name = default_lit_sections.lit_prefix;
+ if (! text_name || ! *text_name)
+ {
+ text_name = segment_name (current_section);
+ group_name = elf_group_name (current_section);
+ linkonce = (current_section->flags & SEC_LINK_ONCE) != 0;
+ }
+
+ base_name = use_abs_literals ? ".lit4" : ".literal";
+ if (group_name)
+ {
+ name = xmalloc (strlen (base_name) + strlen (group_name) + 2);
+ sprintf (name, "%s.%s", base_name, group_name);
+ }
+ else if (strncmp (text_name, ".gnu.linkonce.", linkonce_len) == 0)
+ {
+ suffix = strchr (text_name + linkonce_len, '.');
+
+ name = xmalloc (linkonce_len + strlen (base_name) + 1
+ + (suffix ? strlen (suffix) : 0));
+ strcpy (name, ".gnu.linkonce");
+ strcat (name, base_name);
+ if (suffix)
+ strcat (name, suffix);
+ linkonce = TRUE;
+ }
+ else
+ {
+ /* If the section name begins or ends with ".text", then replace
+ that portion instead of appending an additional suffix. */
+ size_t len = strlen (text_name);
+ if (len >= 5
+ && (strcmp (text_name + len - 5, ".text") == 0
+ || strncmp (text_name, ".text", 5) == 0))
+ len -= 5;
+
+ name = xmalloc (len + strlen (base_name) + 1);
+ if (strncmp (text_name, ".text", 5) == 0)
+ {
+ strcpy (name, base_name);
+ strcat (name, text_name + 5);
+ }
+ else
+ {
+ strcpy (name, text_name);
+ strcpy (name + len, base_name);
+ }
+ }
+
+ /* Canonicalize section names to allow renaming literal sections.
+ The group name, if any, came from the current text section and
+ has already been canonicalized. */
+ name = tc_canonicalize_symbol_name (name);
+
+ seg = bfd_get_section_by_name_if (stdoutput, name, match_section_group,
+ (void *) group_name);
+ if (! seg)
+ {
+ flagword flags;
+
+ seg = subseg_force_new (name, 0);
+
+ if (! use_abs_literals)
+ {
+ /* Add the newly created literal segment to the list. */
+ seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
+ n->seg = seg;
+ n->next = literal_head->next;
+ literal_head->next = n;
+ }
+
+ flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_ALLOC | SEC_LOAD
+ | (linkonce ? (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD) : 0)
+ | (use_abs_literals ? SEC_DATA : SEC_CODE));
+
+ elf_group_name (seg) = group_name;
+
+ bfd_set_section_flags (stdoutput, seg, flags);
+ bfd_set_section_alignment (stdoutput, seg, 2);
+ }
+
+ *pcached = seg;
+ subseg_set (current_section, current_subsec);
+ return seg;
+}
+
+
+/* Property Tables Stuff. */
+
+#define XTENSA_INSN_SEC_NAME ".xt.insn"
+#define XTENSA_LIT_SEC_NAME ".xt.lit"
+#define XTENSA_PROP_SEC_NAME ".xt.prop"
+
+typedef bfd_boolean (*frag_predicate) (const fragS *);
+typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
+
+static bfd_boolean get_frag_is_literal (const fragS *);
+static void xtensa_create_property_segments
+ (frag_predicate, frag_predicate, const char *, xt_section_type);
+static void xtensa_create_xproperty_segments
+ (frag_flags_fn, const char *, xt_section_type);
+static bfd_boolean exclude_section_from_property_tables (segT);
+static bfd_boolean section_has_property (segT, frag_predicate);
+static bfd_boolean section_has_xproperty (segT, frag_flags_fn);
+static void add_xt_block_frags
+ (segT, xtensa_block_info **, frag_predicate, frag_predicate);
+static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
+static void xtensa_frag_flags_init (frag_flags *);
+static void get_frag_property_flags (const fragS *, frag_flags *);
+static flagword frag_flags_to_number (const frag_flags *);
+static void add_xt_prop_frags (segT, xtensa_block_info **, frag_flags_fn);
+
+/* Set up property tables after relaxation. */
+
+void
+xtensa_post_relax_hook (void)
+{
+ xtensa_move_seg_list_to_beginning (literal_head);
+
+ xtensa_find_unmarked_state_frags ();
+ xtensa_mark_frags_for_org ();
+ xtensa_mark_difference_of_two_symbols ();
+
+ xtensa_create_property_segments (get_frag_is_literal,
+ NULL,
+ XTENSA_LIT_SEC_NAME,
+ xt_literal_sec);
+ xtensa_create_xproperty_segments (get_frag_property_flags,
+ XTENSA_PROP_SEC_NAME,
+ xt_prop_sec);
+
+ if (warn_unaligned_branch_targets)
+ bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
+ bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
+}
+
+
+/* This function is only meaningful after xtensa_move_literals. */
+
+static bfd_boolean
+get_frag_is_literal (const fragS *fragP)
+{
+ gas_assert (fragP != NULL);
+ return fragP->tc_frag_data.is_literal;
+}
+
+
+static void
+xtensa_create_property_segments (frag_predicate property_function,
+ frag_predicate end_property_function,
+ const char *section_name_base,
+ xt_section_type sec_type)
+{
+ segT *seclist;
+
+ /* Walk over all of the current segments.
+ Walk over each fragment
+ For each non-empty fragment,
+ Build a property record (append where possible). */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+
+ if (exclude_section_from_property_tables (sec))
+ continue;
+
+ if (section_has_property (sec, property_function))
+ {
+ segment_info_type *xt_seg_info;
+ xtensa_block_info **xt_blocks;
+ segT prop_sec = xtensa_make_property_section (sec, section_name_base);
+
+ prop_sec->output_section = prop_sec;
+ subseg_set (prop_sec, 0);
+ xt_seg_info = seg_info (prop_sec);
+ xt_blocks = &xt_seg_info->tc_segment_info_data.blocks[sec_type];
+
+ /* Walk over all of the frchains here and add new sections. */
+ add_xt_block_frags (sec, xt_blocks, property_function,
+ end_property_function);
+ }
+ }
+
+ /* Now we fill them out.... */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segment_info_type *seginfo;
+ xtensa_block_info *block;
+ segT sec = *seclist;
+
+ seginfo = seg_info (sec);
+ block = seginfo->tc_segment_info_data.blocks[sec_type];
+
+ if (block)
+ {
+ xtensa_block_info *cur_block;
+ int num_recs = 0;
+ bfd_size_type rec_size;
+
+ for (cur_block = block; cur_block; cur_block = cur_block->next)
+ num_recs++;
+
+ rec_size = num_recs * 8;
+ bfd_set_section_size (stdoutput, sec, rec_size);
+
+ if (num_recs)
+ {
+ char *frag_data;
+ int i;
+
+ subseg_set (sec, 0);
+ frag_data = frag_more (rec_size);
+ cur_block = block;
+ for (i = 0; i < num_recs; i++)
+ {
+ fixS *fix;
+
+ /* Write the fixup. */
+ gas_assert (cur_block);
+ fix = fix_new (frag_now, i * 8, 4,
+ section_symbol (cur_block->sec),
+ cur_block->offset,
+ FALSE, BFD_RELOC_32);
+ fix->fx_file = "<internal>";
+ fix->fx_line = 0;
+
+ /* Write the length. */
+ md_number_to_chars (&frag_data[4 + i * 8],
+ cur_block->size, 4);
+ cur_block = cur_block->next;
+ }
+ frag_wane (frag_now);
+ frag_new (0);
+ frag_wane (frag_now);
+ }
+ }
+ }
+}
+
+
+static void
+xtensa_create_xproperty_segments (frag_flags_fn flag_fn,
+ const char *section_name_base,
+ xt_section_type sec_type)
+{
+ segT *seclist;
+
+ /* Walk over all of the current segments.
+ Walk over each fragment.
+ For each fragment that has instructions,
+ build an instruction record (append where possible). */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+
+ if (exclude_section_from_property_tables (sec))
+ continue;
+
+ if (section_has_xproperty (sec, flag_fn))
+ {
+ segment_info_type *xt_seg_info;
+ xtensa_block_info **xt_blocks;
+ segT prop_sec = xtensa_make_property_section (sec, section_name_base);
+
+ prop_sec->output_section = prop_sec;
+ subseg_set (prop_sec, 0);
+ xt_seg_info = seg_info (prop_sec);
+ xt_blocks = &xt_seg_info->tc_segment_info_data.blocks[sec_type];
+
+ /* Walk over all of the frchains here and add new sections. */
+ add_xt_prop_frags (sec, xt_blocks, flag_fn);
+ }
+ }
+
+ /* Now we fill them out.... */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segment_info_type *seginfo;
+ xtensa_block_info *block;
+ segT sec = *seclist;
+
+ seginfo = seg_info (sec);
+ block = seginfo->tc_segment_info_data.blocks[sec_type];
+
+ if (block)
+ {
+ xtensa_block_info *cur_block;
+ int num_recs = 0;
+ bfd_size_type rec_size;
+
+ for (cur_block = block; cur_block; cur_block = cur_block->next)
+ num_recs++;
+
+ rec_size = num_recs * (8 + 4);
+ bfd_set_section_size (stdoutput, sec, rec_size);
+ /* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
+
+ if (num_recs)
+ {
+ char *frag_data;
+ int i;
+
+ subseg_set (sec, 0);
+ frag_data = frag_more (rec_size);
+ cur_block = block;
+ for (i = 0; i < num_recs; i++)
+ {
+ fixS *fix;
+
+ /* Write the fixup. */
+ gas_assert (cur_block);
+ fix = fix_new (frag_now, i * 12, 4,
+ section_symbol (cur_block->sec),
+ cur_block->offset,
+ FALSE, BFD_RELOC_32);
+ fix->fx_file = "<internal>";
+ fix->fx_line = 0;
+
+ /* Write the length. */
+ md_number_to_chars (&frag_data[4 + i * 12],
+ cur_block->size, 4);
+ md_number_to_chars (&frag_data[8 + i * 12],
+ frag_flags_to_number (&cur_block->flags),
+ sizeof (flagword));
+ cur_block = cur_block->next;
+ }
+ frag_wane (frag_now);
+ frag_new (0);
+ frag_wane (frag_now);
+ }
+ }
+ }
+}
+
+
+static bfd_boolean
+exclude_section_from_property_tables (segT sec)
+{
+ flagword flags = bfd_get_section_flags (stdoutput, sec);
+
+ /* Sections that don't contribute to the memory footprint are excluded. */
+ if ((flags & SEC_DEBUGGING)
+ || !(flags & SEC_ALLOC)
+ || (flags & SEC_MERGE))
+ return TRUE;
+
+ /* Linker cie and fde optimizations mess up property entries for
+ eh_frame sections, but there is nothing inside them relevant to
+ property tables anyway. */
+ if (strcmp (sec->name, ".eh_frame") == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+
+static bfd_boolean
+section_has_property (segT sec, frag_predicate property_function)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragP;
+
+ if (seginfo && seginfo->frchainP)
+ {
+ for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (property_function (fragP)
+ && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+static bfd_boolean
+section_has_xproperty (segT sec, frag_flags_fn property_function)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragP;
+
+ if (seginfo && seginfo->frchainP)
+ {
+ for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ frag_flags prop_flags;
+ property_function (fragP, &prop_flags);
+ if (!xtensa_frag_flags_is_empty (&prop_flags))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/* Two types of block sections exist right now: literal and insns. */
+
+static void
+add_xt_block_frags (segT sec,
+ xtensa_block_info **xt_block,
+ frag_predicate property_function,
+ frag_predicate end_property_function)
+{
+ fragS *fragP;
+
+ /* Build it if needed. */
+ while (*xt_block != NULL)
+ xt_block = &(*xt_block)->next;
+ /* We are either at NULL at the beginning or at the end. */
+
+ /* Walk through the frags. */
+ if (seg_info (sec)->frchainP)
+ {
+ for (fragP = seg_info (sec)->frchainP->frch_root;
+ fragP;
+ fragP = fragP->fr_next)
+ {
+ if (property_function (fragP)
+ && (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
+ {
+ if (*xt_block != NULL)
+ {
+ if ((*xt_block)->offset + (*xt_block)->size
+ == fragP->fr_address)
+ (*xt_block)->size += fragP->fr_fix;
+ else
+ xt_block = &((*xt_block)->next);
+ }
+ if (*xt_block == NULL)
+ {
+ xtensa_block_info *new_block = (xtensa_block_info *)
+ xmalloc (sizeof (xtensa_block_info));
+ new_block->sec = sec;
+ new_block->offset = fragP->fr_address;
+ new_block->size = fragP->fr_fix;
+ new_block->next = NULL;
+ xtensa_frag_flags_init (&new_block->flags);
+ *xt_block = new_block;
+ }
+ if (end_property_function
+ && end_property_function (fragP))
+ {
+ xt_block = &((*xt_block)->next);
+ }
+ }
+ }
+ }
+}
+
+
+/* Break the encapsulation of add_xt_prop_frags here. */
+
+static bfd_boolean
+xtensa_frag_flags_is_empty (const frag_flags *prop_flags)
+{
+ if (prop_flags->is_literal
+ || prop_flags->is_insn
+ || prop_flags->is_data
+ || prop_flags->is_unreachable)
+ return FALSE;
+ return TRUE;
+}
+
+
+static void
+xtensa_frag_flags_init (frag_flags *prop_flags)
+{
+ memset (prop_flags, 0, sizeof (frag_flags));
+}
+
+
+static void
+get_frag_property_flags (const fragS *fragP, frag_flags *prop_flags)
+{
+ xtensa_frag_flags_init (prop_flags);
+ if (fragP->tc_frag_data.is_literal)
+ prop_flags->is_literal = TRUE;
+ if (fragP->tc_frag_data.is_specific_opcode
+ || fragP->tc_frag_data.is_no_transform)
+ {
+ prop_flags->is_no_transform = TRUE;
+ if (xtensa_frag_flags_is_empty (prop_flags))
+ prop_flags->is_data = TRUE;
+ }
+ if (fragP->tc_frag_data.is_unreachable)
+ prop_flags->is_unreachable = TRUE;
+ else if (fragP->tc_frag_data.is_insn)
+ {
+ prop_flags->is_insn = TRUE;
+ if (fragP->tc_frag_data.is_loop_target)
+ prop_flags->insn.is_loop_target = TRUE;
+ if (fragP->tc_frag_data.is_branch_target)
+ prop_flags->insn.is_branch_target = TRUE;
+ if (fragP->tc_frag_data.is_no_density)
+ prop_flags->insn.is_no_density = TRUE;
+ if (fragP->tc_frag_data.use_absolute_literals)
+ prop_flags->insn.is_abslit = TRUE;
+ }
+ if (fragP->tc_frag_data.is_align)
+ {
+ prop_flags->is_align = TRUE;
+ prop_flags->alignment = fragP->tc_frag_data.alignment;
+ if (xtensa_frag_flags_is_empty (prop_flags))
+ prop_flags->is_data = TRUE;
+ }
+}
+
+
+static flagword
+frag_flags_to_number (const frag_flags *prop_flags)
+{
+ flagword num = 0;
+ if (prop_flags->is_literal)
+ num |= XTENSA_PROP_LITERAL;
+ if (prop_flags->is_insn)
+ num |= XTENSA_PROP_INSN;
+ if (prop_flags->is_data)
+ num |= XTENSA_PROP_DATA;
+ if (prop_flags->is_unreachable)
+ num |= XTENSA_PROP_UNREACHABLE;
+ if (prop_flags->insn.is_loop_target)
+ num |= XTENSA_PROP_INSN_LOOP_TARGET;
+ if (prop_flags->insn.is_branch_target)
+ {
+ num |= XTENSA_PROP_INSN_BRANCH_TARGET;
+ num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
+ }
+
+ if (prop_flags->insn.is_no_density)
+ num |= XTENSA_PROP_INSN_NO_DENSITY;
+ if (prop_flags->is_no_transform)
+ num |= XTENSA_PROP_NO_TRANSFORM;
+ if (prop_flags->insn.is_no_reorder)
+ num |= XTENSA_PROP_INSN_NO_REORDER;
+ if (prop_flags->insn.is_abslit)
+ num |= XTENSA_PROP_INSN_ABSLIT;
+
+ if (prop_flags->is_align)
+ {
+ num |= XTENSA_PROP_ALIGN;
+ num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
+ }
+
+ return num;
+}
+
+
+static bfd_boolean
+xtensa_frag_flags_combinable (const frag_flags *prop_flags_1,
+ const frag_flags *prop_flags_2)
+{
+ /* Cannot combine with an end marker. */
+
+ if (prop_flags_1->is_literal != prop_flags_2->is_literal)
+ return FALSE;
+ if (prop_flags_1->is_insn != prop_flags_2->is_insn)
+ return FALSE;
+ if (prop_flags_1->is_data != prop_flags_2->is_data)
+ return FALSE;
+
+ if (prop_flags_1->is_insn)
+ {
+ /* Properties of the beginning of the frag. */
+ if (prop_flags_2->insn.is_loop_target)
+ return FALSE;
+ if (prop_flags_2->insn.is_branch_target)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_density !=
+ prop_flags_2->insn.is_no_density)
+ return FALSE;
+ if (prop_flags_1->is_no_transform !=
+ prop_flags_2->is_no_transform)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_reorder !=
+ prop_flags_2->insn.is_no_reorder)
+ return FALSE;
+ if (prop_flags_1->insn.is_abslit !=
+ prop_flags_2->insn.is_abslit)
+ return FALSE;
+ }
+
+ if (prop_flags_1->is_align)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static bfd_vma
+xt_block_aligned_size (const xtensa_block_info *xt_block)
+{
+ bfd_vma end_addr;
+ unsigned align_bits;
+
+ if (!xt_block->flags.is_align)
+ return xt_block->size;
+
+ end_addr = xt_block->offset + xt_block->size;
+ align_bits = xt_block->flags.alignment;
+ end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
+ return end_addr - xt_block->offset;
+}
+
+
+static bfd_boolean
+xtensa_xt_block_combine (xtensa_block_info *xt_block,
+ const xtensa_block_info *xt_block_2)
+{
+ if (xt_block->sec != xt_block_2->sec)
+ return FALSE;
+ if (xt_block->offset + xt_block_aligned_size (xt_block)
+ != xt_block_2->offset)
+ return FALSE;
+
+ if (xt_block_2->size == 0
+ && (!xt_block_2->flags.is_unreachable
+ || xt_block->flags.is_unreachable))
+ {
+ if (xt_block_2->flags.is_align
+ && xt_block->flags.is_align)
+ {
+ /* Nothing needed. */
+ if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
+ return TRUE;
+ }
+ else
+ {
+ if (xt_block_2->flags.is_align)
+ {
+ /* Push alignment to previous entry. */
+ xt_block->flags.is_align = xt_block_2->flags.is_align;
+ xt_block->flags.alignment = xt_block_2->flags.alignment;
+ }
+ return TRUE;
+ }
+ }
+ if (!xtensa_frag_flags_combinable (&xt_block->flags,
+ &xt_block_2->flags))
+ return FALSE;
+
+ xt_block->size += xt_block_2->size;
+
+ if (xt_block_2->flags.is_align)
+ {
+ xt_block->flags.is_align = TRUE;
+ xt_block->flags.alignment = xt_block_2->flags.alignment;
+ }
+
+ return TRUE;
+}
+
+
+static void
+add_xt_prop_frags (segT sec,
+ xtensa_block_info **xt_block,
+ frag_flags_fn property_function)
+{
+ fragS *fragP;
+
+ /* Build it if needed. */
+ while (*xt_block != NULL)
+ {
+ xt_block = &(*xt_block)->next;
+ }
+ /* We are either at NULL at the beginning or at the end. */
+
+ /* Walk through the frags. */
+ if (seg_info (sec)->frchainP)
+ {
+ for (fragP = seg_info (sec)->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ xtensa_block_info tmp_block;
+ tmp_block.sec = sec;
+ tmp_block.offset = fragP->fr_address;
+ tmp_block.size = fragP->fr_fix;
+ tmp_block.next = NULL;
+ property_function (fragP, &tmp_block.flags);
+
+ if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
+ /* && fragP->fr_fix != 0) */
+ {
+ if ((*xt_block) == NULL
+ || !xtensa_xt_block_combine (*xt_block, &tmp_block))
+ {
+ xtensa_block_info *new_block;
+ if ((*xt_block) != NULL)
+ xt_block = &(*xt_block)->next;
+ new_block = (xtensa_block_info *)
+ xmalloc (sizeof (xtensa_block_info));
+ *new_block = tmp_block;
+ *xt_block = new_block;
+ }
+ }
+ }
+ }
+}
+
+
+/* op_placement_info_table */
+
+/* op_placement_info makes it easier to determine which
+ ops can go in which slots. */
+
+static void
+init_op_placement_info_table (void)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
+ xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
+ int num_opcodes = xtensa_isa_num_opcodes (isa);
+
+ op_placement_table = (op_placement_info_table)
+ xmalloc (sizeof (op_placement_info) * num_opcodes);
+ gas_assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
+
+ for (opcode = 0; opcode < num_opcodes; opcode++)
+ {
+ op_placement_info *opi = &op_placement_table[opcode];
+ /* FIXME: Make tinsn allocation dynamic. */
+ if (xtensa_opcode_num_operands (isa, opcode) > MAX_INSN_ARGS)
+ as_fatal (_("too many operands in instruction"));
+ opi->narrowest = XTENSA_UNDEFINED;
+ opi->narrowest_size = 0x7F;
+ opi->narrowest_slot = 0;
+ opi->formats = 0;
+ opi->num_formats = 0;
+ opi->issuef = 0;
+ for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+ {
+ opi->slots[fmt] = 0;
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
+ {
+ int fmt_length = xtensa_format_length (isa, fmt);
+ opi->issuef++;
+ set_bit (fmt, opi->formats);
+ set_bit (slot, opi->slots[fmt]);
+ if (fmt_length < opi->narrowest_size
+ || (fmt_length == opi->narrowest_size
+ && (xtensa_format_num_slots (isa, fmt)
+ < xtensa_format_num_slots (isa,
+ opi->narrowest))))
+ {
+ opi->narrowest = fmt;
+ opi->narrowest_size = fmt_length;
+ opi->narrowest_slot = slot;
+ }
+ }
+ }
+ if (opi->formats)
+ opi->num_formats++;
+ }
+ }
+ xtensa_insnbuf_free (isa, ibuf);
+}
+
+
+bfd_boolean
+opcode_fits_format_slot (xtensa_opcode opcode, xtensa_format fmt, int slot)
+{
+ return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
+}
+
+
+/* If the opcode is available in a single slot format, return its size. */
+
+static int
+xg_get_single_size (xtensa_opcode opcode)
+{
+ return op_placement_table[opcode].narrowest_size;
+}
+
+
+static xtensa_format
+xg_get_single_format (xtensa_opcode opcode)
+{
+ return op_placement_table[opcode].narrowest;
+}
+
+
+static int
+xg_get_single_slot (xtensa_opcode opcode)
+{
+ return op_placement_table[opcode].narrowest_slot;
+}
+
+
+/* Instruction Stack Functions (from "xtensa-istack.h"). */
+
+void
+istack_init (IStack *stack)
+{
+ stack->ninsn = 0;
+}
+
+
+bfd_boolean
+istack_empty (IStack *stack)
+{
+ return (stack->ninsn == 0);
+}
+
+
+bfd_boolean
+istack_full (IStack *stack)
+{
+ return (stack->ninsn == MAX_ISTACK);
+}
+
+
+/* Return a pointer to the top IStack entry.
+ It is an error to call this if istack_empty () is TRUE. */
+
+TInsn *
+istack_top (IStack *stack)
+{
+ int rec = stack->ninsn - 1;
+ gas_assert (!istack_empty (stack));
+ return &stack->insn[rec];
+}
+
+
+/* Add a new TInsn to an IStack.
+ It is an error to call this if istack_full () is TRUE. */
+
+void
+istack_push (IStack *stack, TInsn *insn)
+{
+ int rec = stack->ninsn;
+ gas_assert (!istack_full (stack));
+ stack->insn[rec] = *insn;
+ stack->ninsn++;
+}
+
+
+/* Clear space for the next TInsn on the IStack and return a pointer
+ to it. It is an error to call this if istack_full () is TRUE. */
+
+TInsn *
+istack_push_space (IStack *stack)
+{
+ int rec = stack->ninsn;
+ TInsn *insn;
+ gas_assert (!istack_full (stack));
+ insn = &stack->insn[rec];
+ tinsn_init (insn);
+ stack->ninsn++;
+ return insn;
+}
+
+
+/* Remove the last pushed instruction. It is an error to call this if
+ istack_empty () returns TRUE. */
+
+void
+istack_pop (IStack *stack)
+{
+ int rec = stack->ninsn - 1;
+ gas_assert (!istack_empty (stack));
+ stack->ninsn--;
+ tinsn_init (&stack->insn[rec]);
+}
+
+
+/* TInsn functions. */
+
+void
+tinsn_init (TInsn *dst)
+{
+ memset (dst, 0, sizeof (TInsn));
+}
+
+
+/* Return TRUE if ANY of the operands in the insn are symbolic. */
+
+static bfd_boolean
+tinsn_has_symbolic_operands (const TInsn *insn)
+{
+ int i;
+ int n = insn->ntok;
+
+ gas_assert (insn->insn_type == ITYPE_INSN);
+
+ for (i = 0; i < n; ++i)
+ {
+ switch (insn->tok[i].X_op)
+ {
+ case O_register:
+ case O_constant:
+ break;
+ default:
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+bfd_boolean
+tinsn_has_invalid_symbolic_operands (const TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
+ int n = insn->ntok;
+
+ gas_assert (insn->insn_type == ITYPE_INSN);
+
+ for (i = 0; i < n; ++i)
+ {
+ switch (insn->tok[i].X_op)
+ {
+ case O_register:
+ case O_constant:
+ break;
+ case O_big:
+ case O_illegal:
+ case O_absent:
+ /* Errors for these types are caught later. */
+ break;
+ case O_hi16:
+ case O_lo16:
+ default:
+ /* Symbolic immediates are only allowed on the last immediate
+ operand. At this time, CONST16 is the only opcode where we
+ support non-PC-relative relocations. */
+ if (i != get_relaxable_immed (insn->opcode)
+ || (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
+ && insn->opcode != xtensa_const16_opcode))
+ {
+ as_bad (_("invalid symbolic operand"));
+ return TRUE;
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/* For assembly code with complex expressions (e.g. subtraction),
+ we have to build them in the literal pool so that
+ their results are calculated correctly after relaxation.
+ The relaxation only handles expressions that
+ boil down to SYMBOL + OFFSET. */
+
+static bfd_boolean
+tinsn_has_complex_operands (const TInsn *insn)
+{
+ int i;
+ int n = insn->ntok;
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ for (i = 0; i < n; ++i)
+ {
+ switch (insn->tok[i].X_op)
+ {
+ case O_register:
+ case O_constant:
+ case O_symbol:
+ case O_lo16:
+ case O_hi16:
+ break;
+ default:
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+/* Encode a TInsn opcode and its constant operands into slotbuf.
+ Return TRUE if there is a symbol in the immediate field. This
+ function assumes that:
+ 1) The number of operands are correct.
+ 2) The insn_type is ITYPE_INSN.
+ 3) The opcode can be encoded in the specified format and slot.
+ 4) Operands are either O_constant or O_symbol, and all constants fit. */
+
+static bfd_boolean
+tinsn_to_slotbuf (xtensa_format fmt,
+ int slot,
+ TInsn *tinsn,
+ xtensa_insnbuf slotbuf)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_opcode opcode = tinsn->opcode;
+ bfd_boolean has_fixup = FALSE;
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
+ int i;
+
+ gas_assert (tinsn->insn_type == ITYPE_INSN);
+ if (noperands != tinsn->ntok)
+ as_fatal (_("operand number mismatch"));
+
+ if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
+ {
+ as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
+ xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
+ return FALSE;
+ }
+
+ for (i = 0; i < noperands; i++)
+ {
+ expressionS *exp = &tinsn->tok[i];
+ int rc;
+ unsigned line;
+ char *file_name;
+ uint32 opnd_value;
+
+ switch (exp->X_op)
+ {
+ case O_register:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ /* The register number has already been checked in
+ expression_maybe_register, so we don't need to check here. */
+ opnd_value = exp->X_add_number;
+ (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
+ rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
+ opnd_value);
+ if (rc != 0)
+ as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
+ break;
+
+ case O_constant:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ as_where (&file_name, &line);
+ /* It is a constant and we called this function
+ then we have to try to fit it. */
+ xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
+ exp->X_add_number, file_name, line);
+ break;
+
+ default:
+ has_fixup = TRUE;
+ break;
+ }
+ }
+
+ return has_fixup;
+}
+
+
+/* Encode a single TInsn into an insnbuf. If the opcode can only be encoded
+ into a multi-slot instruction, fill the other slots with NOPs.
+ Return TRUE if there is a symbol in the immediate field. See also the
+ assumptions listed for tinsn_to_slotbuf. */
+
+static bfd_boolean
+tinsn_to_insnbuf (TInsn *tinsn, xtensa_insnbuf insnbuf)
+{
+ static xtensa_insnbuf slotbuf = 0;
+ static vliw_insn vinsn;
+ xtensa_isa isa = xtensa_default_isa;
+ bfd_boolean has_fixup = FALSE;
+ int i;
+
+ if (!slotbuf)
+ {
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ xg_init_vinsn (&vinsn);
+ }
+
+ xg_clear_vinsn (&vinsn);
+
+ bundle_tinsn (tinsn, &vinsn);
+
+ xtensa_format_encode (isa, vinsn.format, insnbuf);
+
+ for (i = 0; i < vinsn.num_slots; i++)
+ {
+ /* Only one slot may have a fix-up because the rest contains NOPs. */
+ has_fixup |=
+ tinsn_to_slotbuf (vinsn.format, i, &vinsn.slots[i], vinsn.slotbuf[i]);
+ xtensa_format_set_slot (isa, vinsn.format, i, insnbuf, vinsn.slotbuf[i]);
+ }
+
+ return has_fixup;
+}
+
+
+/* Check the instruction arguments. Return TRUE on failure. */
+
+static bfd_boolean
+tinsn_check_arguments (const TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_opcode opcode = insn->opcode;
+ xtensa_regfile t1_regfile, t2_regfile;
+ int t1_reg, t2_reg;
+ int t1_base_reg, t1_last_reg;
+ int t2_base_reg, t2_last_reg;
+ char t1_inout, t2_inout;
+ int i, j;
+
+ if (opcode == XTENSA_UNDEFINED)
+ {
+ as_bad (_("invalid opcode"));
+ return TRUE;
+ }
+
+ if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
+ {
+ as_bad (_("too few operands"));
+ return TRUE;
+ }
+
+ if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
+ {
+ as_bad (_("too many operands"));
+ return TRUE;
+ }
+
+ /* Check registers. */
+ for (j = 0; j < insn->ntok; j++)
+ {
+ if (xtensa_operand_is_register (isa, insn->opcode, j) != 1)
+ continue;
+
+ t2_regfile = xtensa_operand_regfile (isa, insn->opcode, j);
+ t2_base_reg = insn->tok[j].X_add_number;
+ t2_last_reg
+ = t2_base_reg + xtensa_operand_num_regs (isa, insn->opcode, j);
+
+ for (i = 0; i < insn->ntok; i++)
+ {
+ if (i == j)
+ continue;
+
+ if (xtensa_operand_is_register (isa, insn->opcode, i) != 1)
+ continue;
+
+ t1_regfile = xtensa_operand_regfile (isa, insn->opcode, i);
+
+ if (t1_regfile != t2_regfile)
+ continue;
+
+ t1_inout = xtensa_operand_inout (isa, insn->opcode, i);
+ t2_inout = xtensa_operand_inout (isa, insn->opcode, j);
+
+ t1_base_reg = insn->tok[i].X_add_number;
+ t1_last_reg = (t1_base_reg
+ + xtensa_operand_num_regs (isa, insn->opcode, i));
+
+ for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
+ {
+ for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
+ {
+ if (t1_reg != t2_reg)
+ continue;
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ {
+ as_bad (_("multiple writes to the same register"));
+ return TRUE;
+ }
+ }
+ }
+ }
+ }
+ return FALSE;
+}
+
+
+/* Load an instruction from its encoded form. */
+
+static void
+tinsn_from_chars (TInsn *tinsn, char *f, int slot)
+{
+ vliw_insn vinsn;
+
+ xg_init_vinsn (&vinsn);
+ vinsn_from_chars (&vinsn, f);
+
+ *tinsn = vinsn.slots[slot];
+ xg_free_vinsn (&vinsn);
+}
+
+
+static void
+tinsn_from_insnbuf (TInsn *tinsn,
+ xtensa_insnbuf slotbuf,
+ xtensa_format fmt,
+ int slot)
+{
+ int i;
+ xtensa_isa isa = xtensa_default_isa;
+
+ /* Find the immed. */
+ tinsn_init (tinsn);
+ tinsn->insn_type = ITYPE_INSN;
+ tinsn->is_specific_opcode = FALSE; /* must not be specific */
+ tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+ tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
+ for (i = 0; i < tinsn->ntok; i++)
+ {
+ set_expr_const (&tinsn->tok[i],
+ xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
+ tinsn->opcode, i));
+ }
+}
+
+
+/* Read the value of the relaxable immed from the fr_symbol and fr_offset. */
+
+static void
+tinsn_immed_from_frag (TInsn *tinsn, fragS *fragP, int slot)
+{
+ xtensa_opcode opcode = tinsn->opcode;
+ int opnum;
+
+ if (fragP->tc_frag_data.slot_symbols[slot])
+ {
+ opnum = get_relaxable_immed (opcode);
+ gas_assert (opnum >= 0);
+ set_expr_symbol_offset (&tinsn->tok[opnum],
+ fragP->tc_frag_data.slot_symbols[slot],
+ fragP->tc_frag_data.slot_offsets[slot]);
+ }
+ tinsn->extra_arg = fragP->tc_frag_data.free_reg[slot];
+}
+
+
+static int
+get_num_stack_text_bytes (IStack *istack)
+{
+ int i;
+ int text_bytes = 0;
+
+ for (i = 0; i < istack->ninsn; i++)
+ {
+ TInsn *tinsn = &istack->insn[i];
+ if (tinsn->insn_type == ITYPE_INSN)
+ text_bytes += xg_get_single_size (tinsn->opcode);
+ }
+ return text_bytes;
+}
+
+
+static int
+get_num_stack_literal_bytes (IStack *istack)
+{
+ int i;
+ int lit_bytes = 0;
+
+ for (i = 0; i < istack->ninsn; i++)
+ {
+ TInsn *tinsn = &istack->insn[i];
+ if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
+ lit_bytes += 4;
+ }
+ return lit_bytes;
+}
+
+
+/* vliw_insn functions. */
+
+static void
+xg_init_vinsn (vliw_insn *v)
+{
+ int i;
+ xtensa_isa isa = xtensa_default_isa;
+
+ xg_clear_vinsn (v);
+
+ v->insnbuf = xtensa_insnbuf_alloc (isa);
+ if (v->insnbuf == NULL)
+ as_fatal (_("out of memory"));
+
+ for (i = 0; i < config_max_slots; i++)
+ {
+ v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
+ if (v->slotbuf[i] == NULL)
+ as_fatal (_("out of memory"));
+ }
+}
+
+
+static void
+xg_clear_vinsn (vliw_insn *v)
+{
+ int i;
+
+ memset (v, 0, offsetof (vliw_insn, slots)
+ + sizeof(TInsn) * config_max_slots);
+
+ v->format = XTENSA_UNDEFINED;
+ v->num_slots = 0;
+ v->inside_bundle = FALSE;
+
+ if (xt_saved_debug_type != DEBUG_NONE)
+ debug_type = xt_saved_debug_type;
+
+ for (i = 0; i < config_max_slots; i++)
+ v->slots[i].opcode = XTENSA_UNDEFINED;
+}
+
+
+static void
+xg_copy_vinsn (vliw_insn *dst, vliw_insn *src)
+{
+ memcpy (dst, src,
+ offsetof(vliw_insn, slots) + src->num_slots * sizeof(TInsn));
+ dst->insnbuf = src->insnbuf;
+ memcpy (dst->slotbuf, src->slotbuf, src->num_slots * sizeof(xtensa_insnbuf));
+}
+
+
+static bfd_boolean
+vinsn_has_specific_opcodes (vliw_insn *v)
+{
+ int i;
+
+ for (i = 0; i < v->num_slots; i++)
+ {
+ if (v->slots[i].is_specific_opcode)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static void
+xg_free_vinsn (vliw_insn *v)
+{
+ int i;
+ xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
+ for (i = 0; i < config_max_slots; i++)
+ xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
+}
+
+
+/* Encode a vliw_insn into an insnbuf. Return TRUE if there are any symbolic
+ operands. See also the assumptions listed for tinsn_to_slotbuf. */
+
+static bfd_boolean
+vinsn_to_insnbuf (vliw_insn *vinsn,
+ char *frag_offset,
+ fragS *fragP,
+ bfd_boolean record_fixup)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt = vinsn->format;
+ xtensa_insnbuf insnbuf = vinsn->insnbuf;
+ int slot;
+ bfd_boolean has_fixup = FALSE;
+
+ xtensa_format_encode (isa, fmt, insnbuf);
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ expressionS *extra_arg = &tinsn->extra_arg;
+ bfd_boolean tinsn_has_fixup =
+ tinsn_to_slotbuf (vinsn->format, slot, tinsn,
+ vinsn->slotbuf[slot]);
+
+ xtensa_format_set_slot (isa, fmt, slot,
+ insnbuf, vinsn->slotbuf[slot]);
+ if (extra_arg->X_op != O_illegal && extra_arg->X_op != O_register)
+ {
+ if (vinsn->num_slots != 1)
+ as_bad (_("TLS relocation not allowed in FLIX bundle"));
+ else if (record_fixup)
+ /* Instructions that generate TLS relocations should always be
+ relaxed in the front-end. If "record_fixup" is set, then this
+ function is being called during back-end relaxation, so flag
+ the unexpected behavior as an error. */
+ as_bad (_("unexpected TLS relocation"));
+ else
+ fix_new (fragP, frag_offset - fragP->fr_literal,
+ xtensa_format_length (isa, fmt),
+ extra_arg->X_add_symbol, extra_arg->X_add_number,
+ FALSE, map_operator_to_reloc (extra_arg->X_op, FALSE));
+ }
+ if (tinsn_has_fixup)
+ {
+ int i;
+ xtensa_opcode opcode = tinsn->opcode;
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
+ has_fixup = TRUE;
+
+ for (i = 0; i < noperands; i++)
+ {
+ expressionS* exp = &tinsn->tok[i];
+ switch (exp->X_op)
+ {
+ case O_symbol:
+ case O_lo16:
+ case O_hi16:
+ if (get_relaxable_immed (opcode) == i)
+ {
+ /* Add a fix record for the instruction, except if this
+ function is being called prior to relaxation, i.e.,
+ if record_fixup is false, and the instruction might
+ be relaxed later. */
+ if (record_fixup
+ || tinsn->is_specific_opcode
+ || !xg_is_relaxable_insn (tinsn, 0))
+ {
+ xg_add_opcode_fix (tinsn, i, fmt, slot, exp, fragP,
+ frag_offset - fragP->fr_literal);
+ }
+ else
+ {
+ if (exp->X_op != O_symbol)
+ as_bad (_("invalid operand"));
+ tinsn->symbol = exp->X_add_symbol;
+ tinsn->offset = exp->X_add_number;
+ }
+ }
+ else
+ as_bad (_("symbolic operand not allowed"));
+ break;
+
+ case O_constant:
+ case O_register:
+ break;
+
+ default:
+ as_bad (_("expression too complex"));
+ break;
+ }
+ }
+ }
+ }
+
+ return has_fixup;
+}
+
+
+static void
+vinsn_from_chars (vliw_insn *vinsn, char *f)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ int i;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) f, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("cannot decode instruction format"));
+ vinsn->format = fmt;
+ vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ TInsn *tinsn = &vinsn->slots[i];
+ xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
+ tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
+ }
+}
+
+
+/* Expression utilities. */
+
+/* Return TRUE if the expression is an integer constant. */
+
+bfd_boolean
+expr_is_const (const expressionS *s)
+{
+ return (s->X_op == O_constant);
+}
+
+
+/* Get the expression constant.
+ Calling this is illegal if expr_is_const () returns TRUE. */
+
+offsetT
+get_expr_const (const expressionS *s)
+{
+ gas_assert (expr_is_const (s));
+ return s->X_add_number;
+}
+
+
+/* Set the expression to a constant value. */
+
+void
+set_expr_const (expressionS *s, offsetT val)
+{
+ s->X_op = O_constant;
+ s->X_add_number = val;
+ s->X_add_symbol = NULL;
+ s->X_op_symbol = NULL;
+}
+
+
+bfd_boolean
+expr_is_register (const expressionS *s)
+{
+ return (s->X_op == O_register);
+}
+
+
+/* Get the expression constant.
+ Calling this is illegal if expr_is_const () returns TRUE. */
+
+offsetT
+get_expr_register (const expressionS *s)
+{
+ gas_assert (expr_is_register (s));
+ return s->X_add_number;
+}
+
+
+/* Set the expression to a symbol + constant offset. */
+
+void
+set_expr_symbol_offset (expressionS *s, symbolS *sym, offsetT offset)
+{
+ s->X_op = O_symbol;
+ s->X_add_symbol = sym;
+ s->X_op_symbol = NULL; /* unused */
+ s->X_add_number = offset;
+}
+
+
+/* Return TRUE if the two expressions are equal. */
+
+bfd_boolean
+expr_is_equal (expressionS *s1, expressionS *s2)
+{
+ if (s1->X_op != s2->X_op)
+ return FALSE;
+ if (s1->X_add_symbol != s2->X_add_symbol)
+ return FALSE;
+ if (s1->X_op_symbol != s2->X_op_symbol)
+ return FALSE;
+ if (s1->X_add_number != s2->X_add_number)
+ return FALSE;
+ return TRUE;
+}
+
+
+static void
+copy_expr (expressionS *dst, const expressionS *src)
+{
+ memcpy (dst, src, sizeof (expressionS));
+}
+
+
+/* Support for the "--rename-section" option. */
+
+struct rename_section_struct
+{
+ char *old_name;
+ char *new_name;
+ struct rename_section_struct *next;
+};
+
+static struct rename_section_struct *section_rename;
+
+
+/* Parse the string "oldname=new_name(:oldname2=new_name2)*" and add
+ entries to the section_rename list. Note: Specifying multiple
+ renamings separated by colons is not documented and is retained only
+ for backward compatibility. */
+
+static void
+build_section_rename (const char *arg)
+{
+ struct rename_section_struct *r;
+ char *this_arg = NULL;
+ char *next_arg = NULL;
+
+ for (this_arg = xstrdup (arg); this_arg != NULL; this_arg = next_arg)
+ {
+ char *old_name, *new_name;
+
+ if (this_arg)
+ {
+ next_arg = strchr (this_arg, ':');
+ if (next_arg)
+ {
+ *next_arg = '\0';
+ next_arg++;
+ }
+ }
+
+ old_name = this_arg;
+ new_name = strchr (this_arg, '=');
+
+ if (*old_name == '\0')
+ {
+ as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
+ continue;
+ }
+ if (!new_name || new_name[1] == '\0')
+ {
+ as_warn (_("ignoring invalid '-rename-section' specification: '%s'"),
+ old_name);
+ continue;
+ }
+ *new_name = '\0';
+ new_name++;
+
+ /* Check for invalid section renaming. */
+ for (r = section_rename; r != NULL; r = r->next)
+ {
+ if (strcmp (r->old_name, old_name) == 0)
+ as_bad (_("section %s renamed multiple times"), old_name);
+ if (strcmp (r->new_name, new_name) == 0)
+ as_bad (_("multiple sections remapped to output section %s"),
+ new_name);
+ }
+
+ /* Now add it. */
+ r = (struct rename_section_struct *)
+ xmalloc (sizeof (struct rename_section_struct));
+ r->old_name = xstrdup (old_name);
+ r->new_name = xstrdup (new_name);
+ r->next = section_rename;
+ section_rename = r;
+ }
+}
+
+
+char *
+xtensa_section_rename (char *name)
+{
+ struct rename_section_struct *r = section_rename;
+
+ for (r = section_rename; r != NULL; r = r->next)
+ {
+ if (strcmp (r->old_name, name) == 0)
+ return r->new_name;
+ }
+
+ return name;
+}
diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
new file mode 100644
index 0000000..4672bc6
--- /dev/null
+++ b/gas/config/tc-xtensa.h
@@ -0,0 +1,465 @@
+/* tc-xtensa.h -- Header file for tc-xtensa.c.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_XTENSA
+#define TC_XTENSA 1
+
+struct fix;
+
+#ifndef OBJ_ELF
+#error Xtensa support requires ELF object format
+#endif
+
+#include "xtensa-isa.h"
+#include "xtensa-config.h"
+
+#define TARGET_BYTES_BIG_ENDIAN XCHAL_HAVE_BE
+
+
+/* Maximum number of opcode slots in a VLIW instruction. */
+#define MAX_SLOTS 15
+
+
+/* For all xtensa relax states except RELAX_DESIRE_ALIGN and
+ RELAX_DESIRE_ALIGN_IF_TARGET, the amount a frag might grow is stored
+ in the fr_var field. For the two exceptions, fr_var is a float value
+ that records the frequency with which the following instruction is
+ executed as a branch target. The aligner uses this information to
+ tell which targets are most important to be aligned. */
+
+enum xtensa_relax_statesE
+{
+ RELAX_XTENSA_NONE,
+
+ RELAX_ALIGN_NEXT_OPCODE,
+ /* Use the first opcode of the next fragment to determine the
+ alignment requirements. This is ONLY used for LOOPs currently. */
+
+ RELAX_CHECK_ALIGN_NEXT_OPCODE,
+ /* The next non-empty frag contains a loop instruction. Check to see
+ if it is correctly aligned, but do not align it. */
+
+ RELAX_DESIRE_ALIGN_IF_TARGET,
+ /* These are placed in front of labels and converted to either
+ RELAX_DESIRE_ALIGN / RELAX_LOOP_END or rs_fill of 0 before
+ relaxation begins. */
+
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ /* These are placed in front of conditional branches. Before
+ relaxation begins, they are turned into either NOPs for branches
+ immediately followed by RETW or RETW.N or rs_fills of 0. This is
+ used to avoid a hardware bug in some early versions of the
+ processor. */
+
+ RELAX_ADD_NOP_IF_PRE_LOOP_END,
+ /* These are placed after JX instructions. Before relaxation begins,
+ they are turned into either NOPs, if the JX is one instruction
+ before a loop end label, or rs_fills of 0. This is used to avoid a
+ hardware interlock issue prior to Xtensa version T1040. */
+
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ /* These are placed after LOOP instructions and turned into NOPs when:
+ (1) there are less than 3 instructions in the loop; we place 2 of
+ these in a row to add up to 2 NOPS in short loops; or (2) the
+ instructions in the loop do not include a branch or jump.
+ Otherwise they are turned into rs_fills of 0 before relaxation
+ begins. This is used to avoid hardware bug PR3830. */
+
+ RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
+ /* These are placed after LOOP instructions and turned into NOPs if
+ there are less than 12 bytes to the end of some other loop's end.
+ Otherwise they are turned into rs_fills of 0 before relaxation
+ begins. This is used to avoid hardware bug PR3830. */
+
+ RELAX_DESIRE_ALIGN,
+ /* The next fragment would like its first instruction to NOT cross an
+ instruction fetch boundary. */
+
+ RELAX_MAYBE_DESIRE_ALIGN,
+ /* The next fragment might like its first instruction to NOT cross an
+ instruction fetch boundary. These are placed after a branch that
+ might be relaxed. If the branch is relaxed, then this frag will be
+ a branch target and this frag will be changed to RELAX_DESIRE_ALIGN
+ frag. */
+
+ RELAX_LOOP_END,
+ /* This will be turned into a NOP or NOP.N if the previous instruction
+ is expanded to negate a loop. */
+
+ RELAX_LOOP_END_ADD_NOP,
+ /* When the code density option is available, this will generate a
+ NOP.N marked RELAX_NARROW. Otherwise, it will create an rs_fill
+ fragment with a NOP in it. Once a frag has been converted to
+ RELAX_LOOP_END_ADD_NOP, it should never be changed back to
+ RELAX_LOOP_END. */
+
+ RELAX_LITERAL,
+ /* Another fragment could generate an expansion here but has not yet. */
+
+ RELAX_LITERAL_NR,
+ /* Expansion has been generated by an instruction that generates a
+ literal. However, the stretch has NOT been reported yet in this
+ fragment. */
+
+ RELAX_LITERAL_FINAL,
+ /* Expansion has been generated by an instruction that generates a
+ literal. */
+
+ RELAX_LITERAL_POOL_BEGIN,
+ RELAX_LITERAL_POOL_END,
+ /* Technically these are not relaxations at all but mark a location
+ to store literals later. Note that fr_var stores the frchain for
+ BEGIN frags and fr_var stores now_seg for END frags. */
+
+ RELAX_NARROW,
+ /* The last instruction in this fragment (at->fr_opcode) can be
+ freely replaced with a single wider instruction if a future
+ alignment desires or needs it. */
+
+ RELAX_IMMED,
+ /* The last instruction in this fragment (at->fr_opcode) contains
+ an immediate or symbol. If the value does not fit, relax the
+ opcode using expansions from the relax table. */
+
+ RELAX_IMMED_STEP1,
+ /* The last instruction in this fragment (at->fr_opcode) contains a
+ literal. It has already been expanded 1 step. */
+
+ RELAX_IMMED_STEP2,
+ /* The last instruction in this fragment (at->fr_opcode) contains a
+ literal. It has already been expanded 2 steps. */
+
+ RELAX_IMMED_STEP3,
+ /* The last instruction in this fragment (at->fr_opcode) contains a
+ literal. It has already been expanded 3 steps. */
+
+ RELAX_SLOTS,
+ /* There are instructions within the last VLIW instruction that need
+ relaxation. Find the relaxation based on the slot info in
+ xtensa_frag_type. Relaxations that deal with particular opcodes
+ are slot-based (e.g., converting a MOVI to an L32R). Relaxations
+ that deal with entire instructions, such as alignment, are not
+ slot-based. */
+
+ RELAX_FILL_NOP,
+ /* This marks the location of a pipeline stall. We can fill these guys
+ in for alignment of any size. */
+
+ RELAX_UNREACHABLE,
+ /* This marks the location as unreachable. The assembler may widen or
+ narrow this area to meet alignment requirements of nearby
+ instructions. */
+
+ RELAX_MAYBE_UNREACHABLE,
+ /* This marks the location as possibly unreachable. These are placed
+ after a branch that may be relaxed into a branch and jump. If the
+ branch is relaxed, then this frag will be converted to a
+ RELAX_UNREACHABLE frag. */
+
+ RELAX_ORG,
+ /* This marks the location as having previously been an rs_org frag.
+ rs_org frags are converted to fill-zero frags immediately after
+ relaxation. However, we need to remember where they were so we can
+ prevent the linker from changing the size of any frag between the
+ section start and the org frag. */
+
+ RELAX_TRAMPOLINE,
+ /* Every few thousand frags, we insert one of these, just in case we may
+ need some space for a trampoline (jump to a jump) because the function
+ has gotten too big. If not needed, it disappears. */
+
+ RELAX_NONE
+};
+
+/* This is used as a stopper to bound the number of steps that
+ can be taken. */
+#define RELAX_IMMED_MAXSTEPS (RELAX_IMMED_STEP3 - RELAX_IMMED)
+
+struct xtensa_frag_type
+{
+ /* Info about the current state of assembly, e.g., transform,
+ absolute_literals, etc. These need to be passed to the backend and
+ then to the object file.
+
+ When is_assembly_state_set is false, the frag inherits some of the
+ state settings from the previous frag in this segment. Because it
+ is not possible to intercept all fragment closures (frag_more and
+ frag_append_1_char can close a frag), we use a pass after initial
+ assembly to fill in the assembly states. */
+
+ unsigned int is_assembly_state_set : 1;
+ unsigned int is_no_density : 1;
+ unsigned int is_no_transform : 1;
+ unsigned int use_longcalls : 1;
+ unsigned int use_absolute_literals : 1;
+
+ /* Inhibits relaxation of machine-dependent alignment frags the
+ first time through a relaxation.... */
+ unsigned int relax_seen : 1;
+
+ /* Information that is needed in the object file and set when known. */
+ unsigned int is_literal : 1;
+ unsigned int is_loop_target : 1;
+ unsigned int is_branch_target : 1;
+ unsigned int is_insn : 1;
+ unsigned int is_unreachable : 1;
+
+ unsigned int is_specific_opcode : 1; /* also implies no_transform */
+
+ unsigned int is_align : 1;
+ unsigned int is_text_align : 1;
+ unsigned int alignment : 5;
+
+ /* A frag with this bit set is the first in a loop that actually
+ contains an instruction. */
+ unsigned int is_first_loop_insn : 1;
+
+ /* A frag with this bit set is a branch that we are using to
+ align branch targets as if it were a normal narrow instruction. */
+ unsigned int is_aligning_branch : 1;
+
+ /* For text fragments that can generate literals at relax time, this
+ variable points to the frag where the literal will be stored. For
+ literal frags, this variable points to the nearest literal pool
+ location frag. This literal frag will be moved to after this
+ location. For RELAX_LITERAL_POOL_BEGIN frags, this field points
+ to the frag immediately before the corresponding RELAX_LITERAL_POOL_END
+ frag, to make moving frags for this literal pool efficient. */
+ fragS *literal_frag;
+
+ /* The destination segment for literal frags. (Note that this is only
+ valid after xtensa_move_literals.) This field is also used for
+ LITERAL_POOL_END frags. */
+ segT lit_seg;
+
+ /* Frag chain for LITERAL_POOL_BEGIN frags. */
+ struct frchain *lit_frchain;
+
+ /* For the relaxation scheme, some literal fragments can have their
+ expansions modified by an instruction that relaxes. */
+ int text_expansion[MAX_SLOTS];
+ int literal_expansion[MAX_SLOTS];
+ int unreported_expansion;
+
+ /* For slots that have a free register for relaxation, record that
+ register. */
+ expressionS free_reg[MAX_SLOTS];
+
+ /* For text fragments that can generate literals at relax time: */
+ fragS *literal_frags[MAX_SLOTS];
+ enum xtensa_relax_statesE slot_subtypes[MAX_SLOTS];
+ symbolS *slot_symbols[MAX_SLOTS];
+ offsetT slot_offsets[MAX_SLOTS];
+
+ /* When marking frags after this one in the chain as no transform,
+ cache the last one in the chain, so that we can skip to the
+ end of the chain. */
+ fragS *no_transform_end;
+};
+
+
+/* For VLIW support, we need to know what slot a fixup applies to. */
+typedef struct xtensa_fix_data_struct
+{
+ int slot;
+ symbolS *X_add_symbol;
+ offsetT X_add_number;
+} xtensa_fix_data;
+
+
+/* Structure to record xtensa-specific symbol information. */
+typedef struct xtensa_symfield_type
+{
+ unsigned int is_loop_target : 1;
+ unsigned int is_branch_target : 1;
+ symbolS *next_expr_symbol;
+} xtensa_symfield_type;
+
+
+/* Structure for saving information about a block of property data
+ for frags that have the same flags. The forward reference is
+ in this header file. The actual definition is in tc-xtensa.c. */
+struct xtensa_block_info_struct;
+typedef struct xtensa_block_info_struct xtensa_block_info;
+
+
+/* Property section types. */
+typedef enum
+{
+ xt_literal_sec,
+ xt_prop_sec,
+ max_xt_sec
+} xt_section_type;
+
+typedef struct xtensa_segment_info_struct
+{
+ fragS *literal_pool_loc;
+ xtensa_block_info *blocks[max_xt_sec];
+} xtensa_segment_info;
+
+
+extern const char *xtensa_target_format (void);
+extern void xtensa_init_fix_data (struct fix *);
+extern void xtensa_frag_init (fragS *);
+extern int xtensa_force_relocation (struct fix *);
+extern int xtensa_validate_fix_sub (struct fix *);
+extern void xtensa_frob_label (struct symbol *);
+extern void xtensa_end (void);
+extern void xtensa_post_relax_hook (void);
+extern void xtensa_file_arch_init (bfd *);
+extern void xtensa_flush_pending_output (void);
+extern bfd_boolean xtensa_fix_adjustable (struct fix *);
+extern void xtensa_symbol_new_hook (symbolS *);
+extern long xtensa_relax_frag (fragS *, long, int *);
+extern void xtensa_elf_section_change_hook (void);
+extern int xtensa_unrecognized_line (int);
+extern bfd_boolean xtensa_check_inside_bundle (void);
+extern void xtensa_handle_align (fragS *);
+extern char *xtensa_section_rename (char *);
+
+#define TARGET_FORMAT xtensa_target_format ()
+#define TARGET_ARCH bfd_arch_xtensa
+#define TC_SEGMENT_INFO_TYPE xtensa_segment_info
+#define TC_SYMFIELD_TYPE struct xtensa_symfield_type
+#define TC_FIX_TYPE xtensa_fix_data
+#define TC_INIT_FIX_DATA(x) xtensa_init_fix_data (x)
+#define TC_FRAG_TYPE struct xtensa_frag_type
+#define TC_FRAG_INIT(frag) xtensa_frag_init (frag)
+#define TC_FORCE_RELOCATION(fix) xtensa_force_relocation (fix)
+#define TC_FORCE_RELOCATION_SUB_SAME(fix, seg) \
+ (! SEG_NORMAL (seg) || xtensa_force_relocation (fix))
+#define TC_VALIDATE_FIX_SUB(fix, seg) xtensa_validate_fix_sub (fix)
+#define NO_PSEUDO_DOT xtensa_check_inside_bundle ()
+#define tc_canonicalize_symbol_name(s) xtensa_section_rename (s)
+#define tc_canonicalize_section_name(s) xtensa_section_rename (s)
+#define tc_init_after_args() xtensa_file_arch_init (stdoutput)
+#define tc_fix_adjustable(fix) xtensa_fix_adjustable (fix)
+#define tc_frob_label(sym) xtensa_frob_label (sym)
+#define tc_unrecognized_line(ch) xtensa_unrecognized_line (ch)
+#define tc_symbol_new_hook(sym) xtensa_symbol_new_hook (sym)
+#define md_do_align(a,b,c,d,e) xtensa_flush_pending_output ()
+#define md_elf_section_change_hook xtensa_elf_section_change_hook
+#define md_end xtensa_end
+#define md_flush_pending_output() xtensa_flush_pending_output ()
+#define md_operand(x)
+#define TEXT_SECTION_NAME xtensa_section_rename (".text")
+#define DATA_SECTION_NAME xtensa_section_rename (".data")
+#define BSS_SECTION_NAME xtensa_section_rename (".bss")
+#define HANDLE_ALIGN(fragP) xtensa_handle_align (fragP)
+#define MAX_MEM_FOR_RS_ALIGN_CODE 1
+
+
+/* The renumber_section function must be mapped over all the sections
+ after calling xtensa_post_relax_hook. That function is static in
+ write.c so it cannot be called from xtensa_post_relax_hook itself. */
+
+#define md_post_relax_hook \
+ do \
+ { \
+ int i = 0; \
+ xtensa_post_relax_hook (); \
+ bfd_map_over_sections (stdoutput, renumber_sections, &i); \
+ } \
+ while (0)
+
+
+/* Because xtensa relaxation can insert a new literal into the middle of
+ fragment and thus require re-running the relaxation pass on the
+ section, we need an explicit flag here. We explicitly use the name
+ "stretched" here to avoid changing the source code in write.c. */
+
+#define md_relax_frag(segment, fragP, stretch) \
+ xtensa_relax_frag (fragP, stretch, &stretched)
+
+/* Only allow call frame debug info optimization when linker relaxation is
+ not enabled as otherwise we could generate the DWARF directives without
+ the relocs necessary to patch them up. */
+#define md_allow_eh_opt (linkrelax == 0)
+
+#define LOCAL_LABELS_FB 1
+#define WORKING_DOT_WORD 1
+#define DOUBLESLASH_LINE_COMMENTS
+#define TC_HANDLES_FX_DONE
+#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 0
+#define TC_LINKRELAX_FIXUP(SEG) 0
+#define MD_APPLY_SYM_VALUE(FIX) 0
+#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
+
+/* Use line number format that is amenable to linker relaxation. */
+#define DWARF2_USE_FIXED_ADVANCE_PC (linkrelax != 0)
+
+
+/* Resource reservation info functions. */
+
+/* Returns the number of copies of a particular unit. */
+typedef int (*unit_num_copies_func) (void *, xtensa_funcUnit);
+
+/* Returns the number of units the opcode uses. */
+typedef int (*opcode_num_units_func) (void *, xtensa_opcode);
+
+/* Given an opcode and an index into the opcode's funcUnit list,
+ returns the unit used for the index. */
+typedef int (*opcode_funcUnit_use_unit_func) (void *, xtensa_opcode, int);
+
+/* Given an opcode and an index into the opcode's funcUnit list,
+ returns the cycle during which the unit is used. */
+typedef int (*opcode_funcUnit_use_stage_func) (void *, xtensa_opcode, int);
+
+/* The above typedefs parameterize the resource_table so that the
+ optional scheduler doesn't need its own resource reservation system.
+
+ For simple resource checking, which is all that happens normally,
+ the functions will be as follows (with some wrapping to make the
+ interface more convenient):
+
+ unit_num_copies_func = xtensa_funcUnit_num_copies
+ opcode_num_units_func = xtensa_opcode_num_funcUnit_uses
+ opcode_funcUnit_use_unit_func = xtensa_opcode_funcUnit_use->unit
+ opcode_funcUnit_use_stage_func = xtensa_opcode_funcUnit_use->stage
+
+ Of course the optional scheduler has its own reservation table
+ and functions. */
+
+int opcode_funcUnit_use_unit (void *, xtensa_opcode, int);
+int opcode_funcUnit_use_stage (void *, xtensa_opcode, int);
+
+typedef struct
+{
+ void *data;
+ int cycles;
+ int allocated_cycles;
+ int num_units;
+ unit_num_copies_func unit_num_copies;
+ opcode_num_units_func opcode_num_units;
+ opcode_funcUnit_use_unit_func opcode_unit_use;
+ opcode_funcUnit_use_stage_func opcode_unit_stage;
+ unsigned char **units;
+} resource_table;
+
+resource_table *new_resource_table
+ (void *, int, int, unit_num_copies_func, opcode_num_units_func,
+ opcode_funcUnit_use_unit_func, opcode_funcUnit_use_stage_func);
+void resize_resource_table (resource_table *, int);
+void clear_resource_table (resource_table *);
+bfd_boolean resources_available (resource_table *, xtensa_opcode, int);
+void reserve_resources (resource_table *, xtensa_opcode, int);
+void release_resources (resource_table *, xtensa_opcode, int);
+
+#endif /* TC_XTENSA */
diff --git a/gas/config/tc-z80.c b/gas/config/tc-z80.c
new file mode 100644
index 0000000..54fa322
--- /dev/null
+++ b/gas/config/tc-z80.c
@@ -0,0 +1,2064 @@
+/* tc-z80.c -- Assemble code for the Zilog Z80 and ASCII R800
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+ Contributed by Arnold Metselaar <arnold_m@operamail.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#include "subsegs.h"
+
+/* Exported constants. */
+const char comment_chars[] = ";\0";
+const char line_comment_chars[] = "#;\0";
+const char line_separator_chars[] = "\0";
+const char EXP_CHARS[] = "eE\0";
+const char FLT_CHARS[] = "RrFf\0";
+
+/* For machine specific options. */
+const char * md_shortopts = ""; /* None yet. */
+
+enum options
+{
+ OPTION_MACH_Z80 = OPTION_MD_BASE,
+ OPTION_MACH_R800,
+ OPTION_MACH_IUD,
+ OPTION_MACH_WUD,
+ OPTION_MACH_FUD,
+ OPTION_MACH_IUP,
+ OPTION_MACH_WUP,
+ OPTION_MACH_FUP
+};
+
+#define INS_Z80 1
+#define INS_UNDOC 2
+#define INS_UNPORT 4
+#define INS_R800 8
+
+struct option md_longopts[] =
+{
+ { "z80", no_argument, NULL, OPTION_MACH_Z80},
+ { "r800", no_argument, NULL, OPTION_MACH_R800},
+ { "ignore-undocumented-instructions", no_argument, NULL, OPTION_MACH_IUD },
+ { "Wnud", no_argument, NULL, OPTION_MACH_IUD },
+ { "warn-undocumented-instructions", no_argument, NULL, OPTION_MACH_WUD },
+ { "Wud", no_argument, NULL, OPTION_MACH_WUD },
+ { "forbid-undocumented-instructions", no_argument, NULL, OPTION_MACH_FUD },
+ { "Fud", no_argument, NULL, OPTION_MACH_FUD },
+ { "ignore-unportable-instructions", no_argument, NULL, OPTION_MACH_IUP },
+ { "Wnup", no_argument, NULL, OPTION_MACH_IUP },
+ { "warn-unportable-instructions", no_argument, NULL, OPTION_MACH_WUP },
+ { "Wup", no_argument, NULL, OPTION_MACH_WUP },
+ { "forbid-unportable-instructions", no_argument, NULL, OPTION_MACH_FUP },
+ { "Fup", no_argument, NULL, OPTION_MACH_FUP },
+
+ { NULL, no_argument, NULL, 0 }
+} ;
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+extern int coff_flags;
+/* Instruction classes that silently assembled. */
+static int ins_ok = INS_Z80 | INS_UNDOC;
+/* Instruction classes that generate errors. */
+static int ins_err = INS_R800;
+/* Instruction classes actually used, determines machine type. */
+static int ins_used = INS_Z80;
+
+int
+md_parse_option (int c, char* arg ATTRIBUTE_UNUSED)
+{
+ switch (c)
+ {
+ default:
+ return 0;
+ case OPTION_MACH_Z80:
+ ins_ok &= ~INS_R800;
+ ins_err |= INS_R800;
+ break;
+ case OPTION_MACH_R800:
+ ins_ok = INS_Z80 | INS_UNDOC | INS_R800;
+ ins_err = INS_UNPORT;
+ break;
+ case OPTION_MACH_IUD:
+ ins_ok |= INS_UNDOC;
+ ins_err &= ~INS_UNDOC;
+ break;
+ case OPTION_MACH_IUP:
+ ins_ok |= INS_UNDOC | INS_UNPORT;
+ ins_err &= ~(INS_UNDOC | INS_UNPORT);
+ break;
+ case OPTION_MACH_WUD:
+ if ((ins_ok & INS_R800) == 0)
+ {
+ ins_ok &= ~(INS_UNDOC|INS_UNPORT);
+ ins_err &= ~INS_UNDOC;
+ }
+ break;
+ case OPTION_MACH_WUP:
+ ins_ok &= ~INS_UNPORT;
+ ins_err &= ~(INS_UNDOC|INS_UNPORT);
+ break;
+ case OPTION_MACH_FUD:
+ if ((ins_ok & INS_R800) == 0)
+ {
+ ins_ok &= (INS_UNDOC | INS_UNPORT);
+ ins_err |= INS_UNDOC | INS_UNPORT;
+ }
+ break;
+ case OPTION_MACH_FUP:
+ ins_ok &= ~INS_UNPORT;
+ ins_err |= INS_UNPORT;
+ break;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE * f)
+{
+ fprintf (f, "\n\
+CPU model/instruction set options:\n\
+\n\
+ -z80\t\t assemble for Z80\n\
+ -ignore-undocumented-instructions\n\
+ -Wnud\n\
+\tsilently assemble undocumented Z80-instructions that work on R800\n\
+ -ignore-unportable-instructions\n\
+ -Wnup\n\
+\tsilently assemble all undocumented Z80-instructions\n\
+ -warn-undocumented-instructions\n\
+ -Wud\n\
+\tissue warnings for undocumented Z80-instructions that work on R800\n\
+ -warn-unportable-instructions\n\
+ -Wup\n\
+\tissue warnings for other undocumented Z80-instructions\n\
+ -forbid-undocumented-instructions\n\
+ -Fud\n\
+\ttreat all undocumented z80-instructions as errors\n\
+ -forbid-unportable-instructions\n\
+ -Fup\n\
+\ttreat undocumented z80-instructions that do not work on R800 as errors\n\
+ -r800\t assemble for R800\n\n\
+Default: -z80 -ignore-undocument-instructions -warn-unportable-instructions.\n");
+}
+
+static symbolS * zero;
+
+struct reg_entry
+{
+ char* name;
+ int number;
+};
+#define R_STACKABLE (0x80)
+#define R_ARITH (0x40)
+#define R_IX (0x20)
+#define R_IY (0x10)
+#define R_INDEX (R_IX | R_IY)
+
+#define REG_A (7)
+#define REG_B (0)
+#define REG_C (1)
+#define REG_D (2)
+#define REG_E (3)
+#define REG_H (4)
+#define REG_L (5)
+#define REG_F (6 | 8)
+#define REG_I (9)
+#define REG_R (10)
+
+#define REG_AF (3 | R_STACKABLE)
+#define REG_BC (0 | R_STACKABLE | R_ARITH)
+#define REG_DE (1 | R_STACKABLE | R_ARITH)
+#define REG_HL (2 | R_STACKABLE | R_ARITH)
+#define REG_IX (REG_HL | R_IX)
+#define REG_IY (REG_HL | R_IY)
+#define REG_SP (3 | R_ARITH)
+
+static const struct reg_entry regtable[] =
+{
+ {"a", REG_A },
+ {"af", REG_AF },
+ {"b", REG_B },
+ {"bc", REG_BC },
+ {"c", REG_C },
+ {"d", REG_D },
+ {"de", REG_DE },
+ {"e", REG_E },
+ {"f", REG_F },
+ {"h", REG_H },
+ {"hl", REG_HL },
+ {"i", REG_I },
+ {"ix", REG_IX },
+ {"ixh",REG_H | R_IX },
+ {"ixl",REG_L | R_IX },
+ {"iy", REG_IY },
+ {"iyh",REG_H | R_IY },
+ {"iyl",REG_L | R_IY },
+ {"l", REG_L },
+ {"r", REG_R },
+ {"sp", REG_SP },
+} ;
+
+#define BUFLEN 8 /* Large enough for any keyword. */
+
+void
+md_begin (void)
+{
+ expressionS nul, reg;
+ char * p;
+ unsigned int i, j, k;
+ char buf[BUFLEN];
+
+ reg.X_op = O_register;
+ reg.X_md = 0;
+ reg.X_add_symbol = reg.X_op_symbol = 0;
+ for ( i = 0 ; i < ARRAY_SIZE ( regtable ) ; ++i )
+ {
+ reg.X_add_number = regtable[i].number;
+ k = strlen ( regtable[i].name );
+ buf[k] = 0;
+ if ( k+1 < BUFLEN )
+ {
+ for ( j = ( 1<<k ) ; j ; --j )
+ {
+ for ( k = 0 ; regtable[i].name[k] ; ++k )
+ {
+ buf[k] = ( j & ( 1<<k ) ) ? TOUPPER ( regtable[i].name[k] ) : regtable[i].name[k];
+ }
+ symbolS * psym = symbol_find_or_make(buf);
+ S_SET_SEGMENT(psym, reg_section);
+ symbol_set_value_expression(psym, &reg);
+ }
+ }
+ }
+ p = input_line_pointer;
+ input_line_pointer = "0";
+ nul.X_md=0;
+ expression (& nul);
+ input_line_pointer = p;
+ zero = make_expr_symbol (& nul);
+ /* We do not use relaxation (yet). */
+ linkrelax = 0;
+}
+
+void
+z80_md_end (void)
+{
+ int mach_type;
+
+ if (ins_used & (INS_UNPORT | INS_R800))
+ ins_used |= INS_UNDOC;
+
+ switch (ins_used)
+ {
+ case INS_Z80:
+ mach_type = bfd_mach_z80strict;
+ break;
+ case INS_Z80|INS_UNDOC:
+ mach_type = bfd_mach_z80;
+ break;
+ case INS_Z80|INS_UNDOC|INS_UNPORT:
+ mach_type = bfd_mach_z80full;
+ break;
+ case INS_Z80|INS_UNDOC|INS_R800:
+ mach_type = bfd_mach_r800;
+ break;
+ default:
+ mach_type = 0;
+ }
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach_type);
+}
+
+static const char *
+skip_space (const char *s)
+{
+ while (*s == ' ' || *s == '\t')
+ ++s;
+ return s;
+}
+
+/* A non-zero return-value causes a continue in the
+ function read_a_source_file () in ../read.c. */
+int
+z80_start_line_hook (void)
+{
+ char *p, quote;
+ char buf[4];
+
+ /* Convert one character constants. */
+ for (p = input_line_pointer; *p && *p != '\n'; ++p)
+ {
+ switch (*p)
+ {
+ case '\'':
+ if (p[1] != 0 && p[1] != '\'' && p[2] == '\'')
+ {
+ snprintf (buf, 4, "%3d", (unsigned char)p[1]);
+ *p++ = buf[0];
+ *p++ = buf[1];
+ *p++ = buf[2];
+ break;
+ }
+ case '"':
+ for (quote = *p++; quote != *p && '\n' != *p; ++p)
+ /* No escapes. */ ;
+ if (quote != *p)
+ {
+ as_bad (_("-- unterminated string"));
+ ignore_rest_of_line ();
+ return 1;
+ }
+ break;
+ }
+ }
+ /* Check for <label>[:] [.](EQU|DEFL) <value>. */
+ if (is_name_beginner (*input_line_pointer))
+ {
+ char c, *rest, *line_start;
+ int len;
+
+ line_start = input_line_pointer;
+ if (ignore_input ())
+ return 0;
+
+ c = get_symbol_end ();
+ rest = input_line_pointer + 1;
+
+ if (*rest == ':')
+ ++rest;
+ if (*rest == ' ' || *rest == '\t')
+ ++rest;
+ if (*rest == '.')
+ ++rest;
+ if (strncasecmp (rest, "EQU", 3) == 0)
+ len = 3;
+ else if (strncasecmp (rest, "DEFL", 4) == 0)
+ len = 4;
+ else
+ len = 0;
+ if (len && (!ISALPHA(rest[len]) ) )
+ {
+ /* Handle assignment here. */
+ if (line_start[-1] == '\n')
+ {
+ bump_line_counters ();
+ LISTING_NEWLINE ();
+ }
+ input_line_pointer = rest + len - 1;
+ /* Allow redefining with "DEFL" (len == 4), but not with "EQU". */
+ equals (line_start, len == 4);
+ return 1;
+ }
+ else
+ {
+ /* Restore line and pointer. */
+ *input_line_pointer = c;
+ input_line_pointer = line_start;
+ }
+ }
+ return 0;
+}
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return NULL;
+}
+
+char *
+md_atof (int type ATTRIBUTE_UNUSED, char *litP ATTRIBUTE_UNUSED,
+ int *sizeP ATTRIBUTE_UNUSED)
+{
+ return _("floating point numbers are not implemented");
+}
+
+valueT
+md_section_align (segT seg ATTRIBUTE_UNUSED, valueT size)
+{
+ return size;
+}
+
+long
+md_pcrel_from (fixS * fixp)
+{
+ return fixp->fx_where +
+ fixp->fx_frag->fr_address + 1;
+}
+
+typedef const char * (asfunc)(char, char, const char*);
+
+typedef struct _table_t
+{
+ char* name;
+ char prefix;
+ char opcode;
+ asfunc * fp;
+} table_t;
+
+/* Compares the key for structs that start with a char * to the key. */
+static int
+key_cmp (const void * a, const void * b)
+{
+ const char *str_a, *str_b;
+
+ str_a = *((const char**)a);
+ str_b = *((const char**)b);
+ return strcmp (str_a, str_b);
+}
+
+char buf[BUFLEN];
+const char *key = buf;
+
+/* Prevent an error on a line from also generating
+ a "junk at end of line" error message. */
+static char err_flag;
+
+static void
+error (const char * message)
+{
+ as_bad ("%s", message);
+ err_flag = 1;
+}
+
+static void
+ill_op (void)
+{
+ error (_("illegal operand"));
+}
+
+static void
+wrong_mach (int ins_type)
+{
+ const char *p;
+
+ switch (ins_type)
+ {
+ case INS_UNDOC:
+ p = "undocumented instruction";
+ break;
+ case INS_UNPORT:
+ p = "instruction does not work on R800";
+ break;
+ case INS_R800:
+ p = "instruction only works R800";
+ break;
+ default:
+ p = 0; /* Not reachable. */
+ }
+
+ if (ins_type & ins_err)
+ error (_(p));
+ else
+ as_warn ("%s", _(p));
+}
+
+static void
+check_mach (int ins_type)
+{
+ if ((ins_type & ins_ok) == 0)
+ wrong_mach (ins_type);
+ ins_used |= ins_type;
+}
+
+/* Check whether an expression is indirect. */
+static int
+is_indir (const char *s)
+{
+ char quote;
+ const char *p;
+ int indir, depth;
+
+ /* Indirection is indicated with parentheses. */
+ indir = (*s == '(');
+
+ for (p = s, depth = 0; *p && *p != ','; ++p)
+ {
+ switch (*p)
+ {
+ case '"':
+ case '\'':
+ for (quote = *p++; quote != *p && *p != '\n'; ++p)
+ if (*p == '\\' && p[1])
+ ++p;
+ break;
+ case '(':
+ ++ depth;
+ break;
+ case ')':
+ -- depth;
+ if (depth == 0)
+ {
+ p = skip_space (p + 1);
+ if (*p && *p != ',')
+ indir = 0;
+ --p;
+ }
+ if (depth < 0)
+ error (_("mismatched parentheses"));
+ break;
+ }
+ }
+
+ if (depth != 0)
+ error (_("mismatched parentheses"));
+
+ return indir;
+}
+
+/* Check whether a symbol involves a register. */
+static int
+contains_register(symbolS *sym)
+{
+ if (sym)
+ {
+ expressionS * ex = symbol_get_value_expression(sym);
+ return (O_register == ex->X_op)
+ || (ex->X_add_symbol && contains_register(ex->X_add_symbol))
+ || (ex->X_op_symbol && contains_register(ex->X_op_symbol));
+ }
+ else
+ return 0;
+}
+
+/* Parse general expression, not loooking for indexed adressing. */
+static const char *
+parse_exp_not_indexed (const char *s, expressionS *op)
+{
+ const char *p;
+ int indir;
+
+ p = skip_space (s);
+ op->X_md = indir = is_indir (p);
+ input_line_pointer = (char*) s ;
+ expression (op);
+ switch (op->X_op)
+ {
+ case O_absent:
+ error (_("missing operand"));
+ break;
+ case O_illegal:
+ error (_("bad expression syntax"));
+ break;
+ }
+ return input_line_pointer;
+}
+
+/* Parse expression, change operator to O_md1 for indexed addressing*/
+static const char *
+parse_exp (const char *s, expressionS *op)
+{
+ const char* res = parse_exp_not_indexed (s, op);
+ switch (op->X_op)
+ {
+ case O_add:
+ case O_subtract:
+ if (op->X_md && (O_register == symbol_get_value_expression(op->X_add_symbol)->X_op))
+ {
+ int rnum = symbol_get_value_expression(op->X_add_symbol)->X_add_number;
+ if ( ((REG_IX != rnum) && (REG_IY != rnum)) || contains_register(op->X_op_symbol) )
+ {
+ ill_op();
+ }
+ else
+ {
+ if (O_subtract == op->X_op)
+ {
+ expressionS minus;
+ minus.X_op = O_uminus;
+ minus.X_add_number = 0;
+ minus.X_add_symbol = op->X_op_symbol;
+ minus.X_op_symbol = 0;
+ op->X_op_symbol = make_expr_symbol(&minus);
+ op->X_op = O_add;
+ }
+ symbol_get_value_expression(op->X_op_symbol)->X_add_number += op->X_add_number;
+ op->X_add_number = rnum;
+ op->X_add_symbol = op->X_op_symbol;
+ op->X_op_symbol = 0;
+ op->X_op = O_md1;
+ }
+ }
+ break;
+ case O_register:
+ if ( op->X_md && ((REG_IX == op->X_add_number)||(REG_IY == op->X_add_number)) )
+ {
+ op->X_add_symbol = zero;
+ op->X_op = O_md1;
+ }
+ break;
+ }
+ return res;
+}
+
+/* Condition codes, including some synonyms provided by HiTech zas. */
+static const struct reg_entry cc_tab[] =
+{
+ { "age", 6 << 3 },
+ { "alt", 7 << 3 },
+ { "c", 3 << 3 },
+ { "di", 4 << 3 },
+ { "ei", 5 << 3 },
+ { "lge", 2 << 3 },
+ { "llt", 3 << 3 },
+ { "m", 7 << 3 },
+ { "nc", 2 << 3 },
+ { "nz", 0 << 3 },
+ { "p", 6 << 3 },
+ { "pe", 5 << 3 },
+ { "po", 4 << 3 },
+ { "z", 1 << 3 },
+} ;
+
+/* Parse condition code. */
+static const char *
+parse_cc (const char *s, char * op)
+{
+ const char *p;
+ int i;
+ struct reg_entry * cc_p;
+
+ for (i = 0; i < BUFLEN; ++i)
+ {
+ if (!ISALPHA (s[i])) /* Condition codes consist of letters only. */
+ break;
+ buf[i] = TOLOWER (s[i]);
+ }
+
+ if ((i < BUFLEN)
+ && ((s[i] == 0) || (s[i] == ',')))
+ {
+ buf[i] = 0;
+ cc_p = bsearch (&key, cc_tab, ARRAY_SIZE (cc_tab),
+ sizeof (cc_tab[0]), key_cmp);
+ }
+ else
+ cc_p = NULL;
+
+ if (cc_p)
+ {
+ *op = cc_p->number;
+ p = s + i;
+ }
+ else
+ p = NULL;
+
+ return p;
+}
+
+static const char *
+emit_insn (char prefix, char opcode, const char * args)
+{
+ char *p;
+
+ if (prefix)
+ {
+ p = frag_more (2);
+ *p++ = prefix;
+ }
+ else
+ p = frag_more (1);
+ *p = opcode;
+ return args;
+}
+
+void z80_cons_fix_new (fragS *frag_p, int offset, int nbytes, expressionS *exp)
+{
+ bfd_reloc_code_real_type r[4] =
+ {
+ BFD_RELOC_8,
+ BFD_RELOC_16,
+ BFD_RELOC_24,
+ BFD_RELOC_32
+ };
+
+ if (nbytes < 1 || nbytes > 4)
+ {
+ as_bad (_("unsupported BFD relocation size %u"), nbytes);
+ }
+ else
+ {
+ fix_new_exp (frag_p, offset, nbytes, exp, 0, r[nbytes-1]);
+ }
+}
+
+static void
+emit_byte (expressionS * val, bfd_reloc_code_real_type r_type)
+{
+ char *p;
+ int lo, hi;
+
+ p = frag_more (1);
+ *p = val->X_add_number;
+ if ( contains_register(val->X_add_symbol) || contains_register(val->X_op_symbol) )
+ {
+ ill_op();
+ }
+ else if ((r_type == BFD_RELOC_8_PCREL) && (val->X_op == O_constant))
+ {
+ as_bad (_("cannot make a relative jump to an absolute location"));
+ }
+ else if (val->X_op == O_constant)
+ {
+ lo = -128;
+ hi = (BFD_RELOC_8 == r_type) ? 255 : 127;
+
+ if ((val->X_add_number < lo) || (val->X_add_number > hi))
+ {
+ if (r_type == BFD_RELOC_Z80_DISP8)
+ as_bad (_("offset too large"));
+ else
+ as_warn (_("overflow"));
+ }
+ }
+ else
+ {
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 1, val,
+ (r_type == BFD_RELOC_8_PCREL) ? TRUE : FALSE, r_type);
+ /* FIXME : Process constant offsets immediately. */
+ }
+}
+
+static void
+emit_word (expressionS * val)
+{
+ char *p;
+
+ p = frag_more (2);
+ if ( (val->X_op == O_register)
+ || (val->X_op == O_md1)
+ || contains_register(val->X_add_symbol)
+ || contains_register(val->X_op_symbol) )
+ ill_op ();
+ else
+ {
+ *p = val->X_add_number;
+ p[1] = (val->X_add_number>>8);
+ if (val->X_op != O_constant)
+ fix_new_exp (frag_now, p - frag_now->fr_literal, 2,
+ val, FALSE, BFD_RELOC_16);
+ }
+}
+
+static void
+emit_mx (char prefix, char opcode, int shift, expressionS * arg)
+ /* The operand m may be r, (hl), (ix+d), (iy+d),
+ if 0 == prefix m may also be ixl, ixh, iyl, iyh. */
+{
+ char *q;
+ int rnum;
+
+ rnum = arg->X_add_number;
+ switch (arg->X_op)
+ {
+ case O_register:
+ if (arg->X_md)
+ {
+ if (rnum != REG_HL)
+ {
+ ill_op ();
+ break;
+ }
+ else
+ rnum = 6;
+ }
+ else
+ {
+ if ((prefix == 0) && (rnum & R_INDEX))
+ {
+ prefix = (rnum & R_IX) ? 0xDD : 0xFD;
+ check_mach (INS_UNDOC);
+ rnum &= ~R_INDEX;
+ }
+ if (rnum > 7)
+ {
+ ill_op ();
+ break;
+ }
+ }
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ * q ++ = prefix;
+ * q ++ = opcode + (rnum << shift);
+ break;
+ case O_md1:
+ q = frag_more (2);
+ *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
+ *q = (prefix) ? prefix : (opcode + (6 << shift));
+ {
+ expressionS offset = *arg;
+ offset.X_op = O_symbol;
+ offset.X_add_number = 0;
+ emit_byte (&offset, BFD_RELOC_Z80_DISP8);
+ }
+ if (prefix)
+ {
+ q = frag_more (1);
+ *q = opcode+(6<<shift);
+ }
+ break;
+ default:
+ abort ();
+ }
+}
+
+/* The operand m may be r, (hl), (ix+d), (iy+d),
+ if 0 = prefix m may also be ixl, ixh, iyl, iyh. */
+static const char *
+emit_m (char prefix, char opcode, const char *args)
+{
+ expressionS arg_m;
+ const char *p;
+
+ p = parse_exp (args, &arg_m);
+ switch (arg_m.X_op)
+ {
+ case O_md1:
+ case O_register:
+ emit_mx (prefix, opcode, 0, &arg_m);
+ break;
+ default:
+ ill_op ();
+ }
+ return p;
+}
+
+/* The operand m may be as above or one of the undocumented
+ combinations (ix+d),r and (iy+d),r (if unportable instructions
+ are allowed). */
+static const char *
+emit_mr (char prefix, char opcode, const char *args)
+{
+ expressionS arg_m, arg_r;
+ const char *p;
+
+ p = parse_exp (args, & arg_m);
+
+ switch (arg_m.X_op)
+ {
+ case O_md1:
+ if (*p == ',')
+ {
+ p = parse_exp (p + 1, & arg_r);
+
+ if ((arg_r.X_md == 0)
+ && (arg_r.X_op == O_register)
+ && (arg_r.X_add_number < 8))
+ opcode += arg_r.X_add_number-6; /* Emit_mx () will add 6. */
+ else
+ {
+ ill_op ();
+ break;
+ }
+ check_mach (INS_UNPORT);
+ }
+ case O_register:
+ emit_mx (prefix, opcode, 0, & arg_m);
+ break;
+ default:
+ ill_op ();
+ }
+ return p;
+}
+
+static void
+emit_sx (char prefix, char opcode, expressionS * arg_p)
+{
+ char *q;
+
+ switch (arg_p->X_op)
+ {
+ case O_register:
+ case O_md1:
+ emit_mx (prefix, opcode, 0, arg_p);
+ break;
+ default:
+ if (arg_p->X_md)
+ ill_op ();
+ else
+ {
+ q = frag_more (prefix ? 2 : 1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode ^ 0x46;
+ emit_byte (arg_p, BFD_RELOC_8);
+ }
+ }
+}
+
+/* The operand s may be r, (hl), (ix+d), (iy+d), n. */
+static const char *
+emit_s (char prefix, char opcode, const char *args)
+{
+ expressionS arg_s;
+ const char *p;
+
+ p = parse_exp (args, & arg_s);
+ emit_sx (prefix, opcode, & arg_s);
+ return p;
+}
+
+static const char *
+emit_call (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ expressionS addr;
+ const char *p; char *q;
+
+ p = parse_exp_not_indexed (args, &addr);
+ if (addr.X_md)
+ ill_op ();
+ else
+ {
+ q = frag_more (1);
+ *q = opcode;
+ emit_word (& addr);
+ }
+ return p;
+}
+
+/* Operand may be rr, r, (hl), (ix+d), (iy+d). */
+static const char *
+emit_incdec (char prefix, char opcode, const char * args)
+{
+ expressionS operand;
+ int rnum;
+ const char *p; char *q;
+
+ p = parse_exp (args, &operand);
+ rnum = operand.X_add_number;
+ if ((! operand.X_md)
+ && (operand.X_op == O_register)
+ && (R_ARITH&rnum))
+ {
+ q = frag_more ((rnum & R_INDEX) ? 2 : 1);
+ if (rnum & R_INDEX)
+ *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
+ *q = prefix + ((rnum & 3) << 4);
+ }
+ else
+ {
+ if ((operand.X_op == O_md1) || (operand.X_op == O_register))
+ emit_mx (0, opcode, 3, & operand);
+ else
+ ill_op ();
+ }
+ return p;
+}
+
+static const char *
+emit_jr (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ expressionS addr;
+ const char *p;
+ char *q;
+
+ p = parse_exp_not_indexed (args, &addr);
+ if (addr.X_md)
+ ill_op ();
+ else
+ {
+ q = frag_more (1);
+ *q = opcode;
+ emit_byte (&addr, BFD_RELOC_8_PCREL);
+ }
+ return p;
+}
+
+static const char *
+emit_jp (char prefix, char opcode, const char * args)
+{
+ expressionS addr;
+ const char *p;
+ char *q;
+ int rnum;
+
+ p = parse_exp_not_indexed (args, & addr);
+ if (addr.X_md)
+ {
+ rnum = addr.X_add_number;
+ if ((O_register == addr.X_op) && (REG_HL == (rnum & ~R_INDEX)))
+ {
+ q = frag_more ((rnum & R_INDEX) ? 2 : 1);
+ if (rnum & R_INDEX)
+ *q++ = (rnum & R_IX) ? 0xDD : 0xFD;
+ *q = prefix;
+ }
+ else
+ ill_op ();
+ }
+ else
+ {
+ q = frag_more (1);
+ *q = opcode;
+ emit_word (& addr);
+ }
+ return p;
+}
+
+static const char *
+emit_im (char prefix, char opcode, const char * args)
+{
+ expressionS mode;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & mode);
+ if (mode.X_md || (mode.X_op != O_constant))
+ ill_op ();
+ else
+ switch (mode.X_add_number)
+ {
+ case 1:
+ case 2:
+ ++mode.X_add_number;
+ /* Fall through. */
+ case 0:
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode + 8*mode.X_add_number;
+ break;
+ default:
+ ill_op ();
+ }
+ return p;
+}
+
+static const char *
+emit_pop (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ expressionS regp;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & regp);
+ if ((!regp.X_md)
+ && (regp.X_op == O_register)
+ && (regp.X_add_number & R_STACKABLE))
+ {
+ int rnum;
+
+ rnum = regp.X_add_number;
+ if (rnum&R_INDEX)
+ {
+ q = frag_more (2);
+ *q++ = (rnum&R_IX)?0xDD:0xFD;
+ }
+ else
+ q = frag_more (1);
+ *q = opcode + ((rnum & 3) << 4);
+ }
+ else
+ ill_op ();
+
+ return p;
+}
+
+static const char *
+emit_retcc (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ char cc, *q;
+ const char *p;
+
+ p = parse_cc (args, &cc);
+ q = frag_more (1);
+ if (p)
+ *q = opcode + cc;
+ else
+ *q = prefix;
+ return p ? p : args;
+}
+
+static const char *
+emit_adc (char prefix, char opcode, const char * args)
+{
+ expressionS term;
+ int rnum;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, &term);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+
+ if ((term.X_md) || (term.X_op != O_register))
+ ill_op ();
+ else
+ switch (term.X_add_number)
+ {
+ case REG_A:
+ p = emit_s (0, prefix, p);
+ break;
+ case REG_HL:
+ p = parse_exp (p, &term);
+ if ((!term.X_md) && (term.X_op == O_register))
+ {
+ rnum = term.X_add_number;
+ if (R_ARITH == (rnum & (R_ARITH | R_INDEX)))
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = opcode + ((rnum & 3) << 4);
+ break;
+ }
+ }
+ /* Fall through. */
+ default:
+ ill_op ();
+ }
+ return p;
+}
+
+static const char *
+emit_add (char prefix, char opcode, const char * args)
+{
+ expressionS term;
+ int lhs, rhs;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, &term);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+
+ if ((term.X_md) || (term.X_op != O_register))
+ ill_op ();
+ else
+ switch (term.X_add_number & ~R_INDEX)
+ {
+ case REG_A:
+ p = emit_s (0, prefix, p);
+ break;
+ case REG_HL:
+ lhs = term.X_add_number;
+ p = parse_exp (p, &term);
+ if ((!term.X_md) && (term.X_op == O_register))
+ {
+ rhs = term.X_add_number;
+ if ((rhs & R_ARITH)
+ && ((rhs == lhs) || ((rhs & ~R_INDEX) != REG_HL)))
+ {
+ q = frag_more ((lhs & R_INDEX) ? 2 : 1);
+ if (lhs & R_INDEX)
+ *q++ = (lhs & R_IX) ? 0xDD : 0xFD;
+ *q = opcode + ((rhs & 3) << 4);
+ break;
+ }
+ }
+ /* Fall through. */
+ default:
+ ill_op ();
+ }
+ return p;
+}
+
+static const char *
+emit_bit (char prefix, char opcode, const char * args)
+{
+ expressionS b;
+ int bn;
+ const char *p;
+
+ p = parse_exp (args, &b);
+ if (*p++ != ',')
+ error (_("bad instruction syntax"));
+
+ bn = b.X_add_number;
+ if ((!b.X_md)
+ && (b.X_op == O_constant)
+ && (0 <= bn)
+ && (bn < 8))
+ {
+ if (opcode == 0x40)
+ /* Bit : no optional third operand. */
+ p = emit_m (prefix, opcode + (bn << 3), p);
+ else
+ /* Set, res : resulting byte can be copied to register. */
+ p = emit_mr (prefix, opcode + (bn << 3), p);
+ }
+ else
+ ill_op ();
+ return p;
+}
+
+static const char *
+emit_jpcc (char prefix, char opcode, const char * args)
+{
+ char cc;
+ const char *p;
+
+ p = parse_cc (args, & cc);
+ if (p && *p++ == ',')
+ p = emit_call (0, opcode + cc, p);
+ else
+ p = (prefix == (char)0xC3)
+ ? emit_jp (0xE9, prefix, args)
+ : emit_call (0, prefix, args);
+ return p;
+}
+
+static const char *
+emit_jrcc (char prefix, char opcode, const char * args)
+{
+ char cc;
+ const char *p;
+
+ p = parse_cc (args, &cc);
+ if (p && *p++ == ',')
+ {
+ if (cc > (3 << 3))
+ error (_("condition code invalid for jr"));
+ else
+ p = emit_jr (0, opcode + cc, p);
+ }
+ else
+ p = emit_jr (0, prefix, args);
+
+ return p;
+}
+
+static const char *
+emit_ex (char prefix_in ATTRIBUTE_UNUSED,
+ char opcode_in ATTRIBUTE_UNUSED, const char * args)
+{
+ expressionS op;
+ const char * p;
+ char prefix, opcode;
+
+ p = parse_exp_not_indexed (args, &op);
+ p = skip_space (p);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+
+ prefix = opcode = 0;
+ if (op.X_op == O_register)
+ switch (op.X_add_number | (op.X_md ? 0x8000 : 0))
+ {
+ case REG_AF:
+ if (TOLOWER (*p++) == 'a' && TOLOWER (*p++) == 'f')
+ {
+ /* The scrubber changes '\'' to '`' in this context. */
+ if (*p == '`')
+ ++p;
+ opcode = 0x08;
+ }
+ break;
+ case REG_DE:
+ if (TOLOWER (*p++) == 'h' && TOLOWER (*p++) == 'l')
+ opcode = 0xEB;
+ break;
+ case REG_SP|0x8000:
+ p = parse_exp (p, & op);
+ if (op.X_op == O_register
+ && op.X_md == 0
+ && (op.X_add_number & ~R_INDEX) == REG_HL)
+ {
+ opcode = 0xE3;
+ if (R_INDEX & op.X_add_number)
+ prefix = (R_IX & op.X_add_number) ? 0xDD : 0xFD;
+ }
+ break;
+ }
+ if (opcode)
+ emit_insn (prefix, opcode, p);
+ else
+ ill_op ();
+
+ return p;
+}
+
+static const char *
+emit_in (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
+ const char * args)
+{
+ expressionS reg, port;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, &reg);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+
+ p = parse_exp (p, &port);
+ if (reg.X_md == 0
+ && reg.X_op == O_register
+ && (reg.X_add_number <= 7 || reg.X_add_number == REG_F)
+ && (port.X_md))
+ {
+ if (port.X_op != O_md1 && port.X_op != O_register)
+ {
+ if (REG_A == reg.X_add_number)
+ {
+ q = frag_more (1);
+ *q = 0xDB;
+ emit_byte (&port, BFD_RELOC_8);
+ }
+ else
+ ill_op ();
+ }
+ else
+ {
+ if (port.X_add_number == REG_C)
+ {
+ if (reg.X_add_number == REG_F)
+ check_mach (INS_UNDOC);
+ else
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = 0x40|((reg.X_add_number&7)<<3);
+ }
+ }
+ else
+ ill_op ();
+ }
+ }
+ else
+ ill_op ();
+ return p;
+}
+
+static const char *
+emit_out (char prefix ATTRIBUTE_UNUSED, char opcode ATTRIBUTE_UNUSED,
+ const char * args)
+{
+ expressionS reg, port;
+ const char *p;
+ char *q;
+
+ p = parse_exp (args, & port);
+ if (*p++ != ',')
+ {
+ error (_("bad instruction syntax"));
+ return p;
+ }
+ p = parse_exp (p, &reg);
+ if (!port.X_md)
+ { ill_op (); return p; }
+ /* Allow "out (c), 0" as unportable instruction. */
+ if (reg.X_op == O_constant && reg.X_add_number == 0)
+ {
+ check_mach (INS_UNPORT);
+ reg.X_op = O_register;
+ reg.X_add_number = 6;
+ }
+ if (reg.X_md
+ || reg.X_op != O_register
+ || reg.X_add_number > 7)
+ ill_op ();
+ else
+ if (port.X_op != O_register && port.X_op != O_md1)
+ {
+ if (REG_A == reg.X_add_number)
+ {
+ q = frag_more (1);
+ *q = 0xD3;
+ emit_byte (&port, BFD_RELOC_8);
+ }
+ else
+ ill_op ();
+ }
+ else
+ {
+ if (REG_C == port.X_add_number)
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = 0x41 | (reg.X_add_number << 3);
+ }
+ else
+ ill_op ();
+ }
+ return p;
+}
+
+static const char *
+emit_rst (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ expressionS addr;
+ const char *p;
+ char *q;
+
+ p = parse_exp_not_indexed (args, &addr);
+ if (addr.X_op != O_constant)
+ {
+ error ("rst needs constant address");
+ return p;
+ }
+
+ if (addr.X_add_number & ~(7 << 3))
+ ill_op ();
+ else
+ {
+ q = frag_more (1);
+ *q = opcode + (addr.X_add_number & (7 << 3));
+ }
+ return p;
+}
+
+static void
+emit_ldxhl (char prefix, char opcode, expressionS *src, expressionS *d)
+{
+ char *q;
+
+ if (src->X_md)
+ ill_op ();
+ else
+ {
+ if (src->X_op == O_register)
+ {
+ if (src->X_add_number>7)
+ ill_op ();
+ if (prefix)
+ {
+ q = frag_more (2);
+ *q++ = prefix;
+ }
+ else
+ q = frag_more (1);
+ *q = opcode + src->X_add_number;
+ if (d)
+ emit_byte (d, BFD_RELOC_Z80_DISP8);
+ }
+ else
+ {
+ if (prefix)
+ {
+ q = frag_more (2);
+ *q++ = prefix;
+ }
+ else
+ q = frag_more (1);
+ *q = opcode^0x46;
+ if (d)
+ emit_byte (d, BFD_RELOC_Z80_DISP8);
+ emit_byte (src, BFD_RELOC_8);
+ }
+ }
+}
+
+static void
+emit_ldreg (int dest, expressionS * src)
+{
+ char *q;
+ int rnum;
+
+ switch (dest)
+ {
+ /* 8 Bit ld group: */
+ case REG_I:
+ case REG_R:
+ if (src->X_md == 0 && src->X_op == O_register && src->X_add_number == REG_A)
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = (dest == REG_I) ? 0x47 : 0x4F;
+ }
+ else
+ ill_op ();
+ break;
+
+ case REG_A:
+ if ((src->X_md) && src->X_op != O_register && src->X_op != O_md1)
+ {
+ q = frag_more (1);
+ *q = 0x3A;
+ emit_word (src);
+ break;
+ }
+
+ if ((src->X_md)
+ && src->X_op == O_register
+ && (src->X_add_number == REG_BC || src->X_add_number == REG_DE))
+ {
+ q = frag_more (1);
+ *q = 0x0A + ((src->X_add_number & 1) << 4);
+ break;
+ }
+
+ if ((!src->X_md)
+ && src->X_op == O_register
+ && (src->X_add_number == REG_R || src->X_add_number == REG_I))
+ {
+ q = frag_more (2);
+ *q++ = 0xED;
+ *q = (src->X_add_number == REG_I) ? 0x57 : 0x5F;
+ break;
+ }
+ /* Fall through. */
+ case REG_B:
+ case REG_C:
+ case REG_D:
+ case REG_E:
+ emit_sx (0, 0x40 + (dest << 3), src);
+ break;
+
+ case REG_H:
+ case REG_L:
+ if ((src->X_md == 0)
+ && (src->X_op == O_register)
+ && (src->X_add_number & R_INDEX))
+ ill_op ();
+ else
+ emit_sx (0, 0x40 + (dest << 3), src);
+ break;
+
+ case R_IX | REG_H:
+ case R_IX | REG_L:
+ case R_IY | REG_H:
+ case R_IY | REG_L:
+ if (src->X_md)
+ {
+ ill_op ();
+ break;
+ }
+ check_mach (INS_UNDOC);
+ if (src-> X_op == O_register)
+ {
+ rnum = src->X_add_number;
+ if ((rnum & ~R_INDEX) < 8
+ && ((rnum & R_INDEX) == (dest & R_INDEX)
+ || ( (rnum & ~R_INDEX) != REG_H
+ && (rnum & ~R_INDEX) != REG_L)))
+ {
+ q = frag_more (2);
+ *q++ = (dest & R_IX) ? 0xDD : 0xFD;
+ *q = 0x40 + ((dest & 0x07) << 3) + (rnum & 7);
+ }
+ else
+ ill_op ();
+ }
+ else
+ {
+ q = frag_more (2);
+ *q++ = (dest & R_IX) ? 0xDD : 0xFD;
+ *q = 0x06 + ((dest & 0x07) << 3);
+ emit_byte (src, BFD_RELOC_8);
+ }
+ break;
+
+ /* 16 Bit ld group: */
+ case REG_SP:
+ if (src->X_md == 0
+ && src->X_op == O_register
+ && REG_HL == (src->X_add_number &~ R_INDEX))
+ {
+ q = frag_more ((src->X_add_number & R_INDEX) ? 2 : 1);
+ if (src->X_add_number & R_INDEX)
+ *q++ = (src->X_add_number & R_IX) ? 0xDD : 0xFD;
+ *q = 0xF9;
+ break;
+ }
+ /* Fall through. */
+ case REG_BC:
+ case REG_DE:
+ if (src->X_op == O_register || src->X_op == O_md1)
+ ill_op ();
+ q = frag_more (src->X_md ? 2 : 1);
+ if (src->X_md)
+ {
+ *q++ = 0xED;
+ *q = 0x4B + ((dest & 3) << 4);
+ }
+ else
+ *q = 0x01 + ((dest & 3) << 4);
+ emit_word (src);
+ break;
+
+ case REG_HL:
+ case REG_HL | R_IX:
+ case REG_HL | R_IY:
+ if (src->X_op == O_register || src->X_op == O_md1)
+ ill_op ();
+ q = frag_more ((dest & R_INDEX) ? 2 : 1);
+ if (dest & R_INDEX)
+ * q ++ = (dest & R_IX) ? 0xDD : 0xFD;
+ *q = (src->X_md) ? 0x2A : 0x21;
+ emit_word (src);
+ break;
+
+ case REG_AF:
+ case REG_F:
+ ill_op ();
+ break;
+
+ default:
+ abort ();
+ }
+}
+
+static const char *
+emit_ld (char prefix_in ATTRIBUTE_UNUSED, char opcode_in ATTRIBUTE_UNUSED,
+ const char * args)
+{
+ expressionS dst, src;
+ const char *p;
+ char *q;
+ char prefix, opcode;
+
+ p = parse_exp (args, &dst);
+ if (*p++ != ',')
+ error (_("bad instruction syntax"));
+ p = parse_exp (p, &src);
+
+ switch (dst.X_op)
+ {
+ case O_md1:
+ {
+ expressionS dst_offset = dst;
+ dst_offset.X_op = O_symbol;
+ dst_offset.X_add_number = 0;
+ emit_ldxhl ((dst.X_add_number & R_IX) ? 0xDD : 0xFD, 0x70,
+ &src, &dst_offset);
+ }
+ break;
+
+ case O_register:
+ if (dst.X_md)
+ {
+ switch (dst.X_add_number)
+ {
+ case REG_BC:
+ case REG_DE:
+ if (src.X_md == 0 && src.X_op == O_register && src.X_add_number == REG_A)
+ {
+ q = frag_more (1);
+ *q = 0x02 + ( (dst.X_add_number & 1) << 4);
+ }
+ else
+ ill_op ();
+ break;
+ case REG_HL:
+ emit_ldxhl (0, 0x70, &src, NULL);
+ break;
+ default:
+ ill_op ();
+ }
+ }
+ else
+ emit_ldreg (dst.X_add_number, &src);
+ break;
+
+ default:
+ if (src.X_md != 0 || src.X_op != O_register)
+ ill_op ();
+ prefix = opcode = 0;
+ switch (src.X_add_number)
+ {
+ case REG_A:
+ opcode = 0x32; break;
+ case REG_BC: case REG_DE: case REG_SP:
+ prefix = 0xED; opcode = 0x43 + ((src.X_add_number&3)<<4); break;
+ case REG_HL:
+ opcode = 0x22; break;
+ case REG_HL|R_IX:
+ prefix = 0xDD; opcode = 0x22; break;
+ case REG_HL|R_IY:
+ prefix = 0xFD; opcode = 0x22; break;
+ }
+ if (opcode)
+ {
+ q = frag_more (prefix?2:1);
+ if (prefix)
+ *q++ = prefix;
+ *q = opcode;
+ emit_word (&dst);
+ }
+ else
+ ill_op ();
+ }
+ return p;
+}
+
+static void
+emit_data (int size ATTRIBUTE_UNUSED)
+{
+ const char *p, *q;
+ char *u, quote;
+ int cnt;
+ expressionS exp;
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+ p = skip_space (input_line_pointer);
+
+ do
+ {
+ if (*p == '\"' || *p == '\'')
+ {
+ for (quote = *p, q = ++p, cnt = 0; *p && quote != *p; ++p, ++cnt)
+ ;
+ u = frag_more (cnt);
+ memcpy (u, q, cnt);
+ if (!*p)
+ as_warn (_("unterminated string"));
+ else
+ p = skip_space (p+1);
+ }
+ else
+ {
+ p = parse_exp (p, &exp);
+ if (exp.X_op == O_md1 || exp.X_op == O_register)
+ {
+ ill_op ();
+ break;
+ }
+ if (exp.X_md)
+ as_warn (_("parentheses ignored"));
+ emit_byte (&exp, BFD_RELOC_8);
+ p = skip_space (p);
+ }
+ }
+ while (*p++ == ',') ;
+ input_line_pointer = (char *)(p-1);
+}
+
+static const char *
+emit_mulub (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ const char *p;
+
+ p = skip_space (args);
+ if (TOLOWER (*p++) != 'a' || *p++ != ',')
+ ill_op ();
+ else
+ {
+ char *q, reg;
+
+ reg = TOLOWER (*p++);
+ switch (reg)
+ {
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ check_mach (INS_R800);
+ if (!*skip_space (p))
+ {
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode + ((reg - 'b') << 3);
+ break;
+ }
+ default:
+ ill_op ();
+ }
+ }
+ return p;
+}
+
+static const char *
+emit_muluw (char prefix ATTRIBUTE_UNUSED, char opcode, const char * args)
+{
+ const char *p;
+
+ p = skip_space (args);
+ if (TOLOWER (*p++) != 'h' || TOLOWER (*p++) != 'l' || *p++ != ',')
+ ill_op ();
+ else
+ {
+ expressionS reg;
+ char *q;
+
+ p = parse_exp (p, & reg);
+
+ if ((!reg.X_md) && reg.X_op == O_register)
+ switch (reg.X_add_number)
+ {
+ case REG_BC:
+ case REG_SP:
+ check_mach (INS_R800);
+ q = frag_more (2);
+ *q++ = prefix;
+ *q = opcode + ((reg.X_add_number & 3) << 4);
+ break;
+ default:
+ ill_op ();
+ }
+ }
+ return p;
+}
+
+/* Port specific pseudo ops. */
+const pseudo_typeS md_pseudo_table[] =
+{
+ { "db" , emit_data, 1},
+ { "d24", cons, 3},
+ { "d32", cons, 4},
+ { "def24", cons, 3},
+ { "def32", cons, 4},
+ { "defb", emit_data, 1},
+ { "defs", s_space, 1}, /* Synonym for ds on some assemblers. */
+ { "defw", cons, 2},
+ { "ds", s_space, 1}, /* Fill with bytes rather than words. */
+ { "dw", cons, 2},
+ { "psect", obj_coff_section, 0}, /* TODO: Translate attributes. */
+ { "set", 0, 0}, /* Real instruction on z80. */
+ { NULL, 0, 0 }
+} ;
+
+static table_t instab[] =
+{
+ { "adc", 0x88, 0x4A, emit_adc },
+ { "add", 0x80, 0x09, emit_add },
+ { "and", 0x00, 0xA0, emit_s },
+ { "bit", 0xCB, 0x40, emit_bit },
+ { "call", 0xCD, 0xC4, emit_jpcc },
+ { "ccf", 0x00, 0x3F, emit_insn },
+ { "cp", 0x00, 0xB8, emit_s },
+ { "cpd", 0xED, 0xA9, emit_insn },
+ { "cpdr", 0xED, 0xB9, emit_insn },
+ { "cpi", 0xED, 0xA1, emit_insn },
+ { "cpir", 0xED, 0xB1, emit_insn },
+ { "cpl", 0x00, 0x2F, emit_insn },
+ { "daa", 0x00, 0x27, emit_insn },
+ { "dec", 0x0B, 0x05, emit_incdec },
+ { "di", 0x00, 0xF3, emit_insn },
+ { "djnz", 0x00, 0x10, emit_jr },
+ { "ei", 0x00, 0xFB, emit_insn },
+ { "ex", 0x00, 0x00, emit_ex},
+ { "exx", 0x00, 0xD9, emit_insn },
+ { "halt", 0x00, 0x76, emit_insn },
+ { "im", 0xED, 0x46, emit_im },
+ { "in", 0x00, 0x00, emit_in },
+ { "inc", 0x03, 0x04, emit_incdec },
+ { "ind", 0xED, 0xAA, emit_insn },
+ { "indr", 0xED, 0xBA, emit_insn },
+ { "ini", 0xED, 0xA2, emit_insn },
+ { "inir", 0xED, 0xB2, emit_insn },
+ { "jp", 0xC3, 0xC2, emit_jpcc },
+ { "jr", 0x18, 0x20, emit_jrcc },
+ { "ld", 0x00, 0x00, emit_ld },
+ { "ldd", 0xED, 0xA8, emit_insn },
+ { "lddr", 0xED, 0xB8, emit_insn },
+ { "ldi", 0xED, 0xA0, emit_insn },
+ { "ldir", 0xED, 0xB0, emit_insn },
+ { "mulub", 0xED, 0xC5, emit_mulub }, /* R800 only. */
+ { "muluw", 0xED, 0xC3, emit_muluw }, /* R800 only. */
+ { "neg", 0xed, 0x44, emit_insn },
+ { "nop", 0x00, 0x00, emit_insn },
+ { "or", 0x00, 0xB0, emit_s },
+ { "otdr", 0xED, 0xBB, emit_insn },
+ { "otir", 0xED, 0xB3, emit_insn },
+ { "out", 0x00, 0x00, emit_out },
+ { "outd", 0xED, 0xAB, emit_insn },
+ { "outi", 0xED, 0xA3, emit_insn },
+ { "pop", 0x00, 0xC1, emit_pop },
+ { "push", 0x00, 0xC5, emit_pop },
+ { "res", 0xCB, 0x80, emit_bit },
+ { "ret", 0xC9, 0xC0, emit_retcc },
+ { "reti", 0xED, 0x4D, emit_insn },
+ { "retn", 0xED, 0x45, emit_insn },
+ { "rl", 0xCB, 0x10, emit_mr },
+ { "rla", 0x00, 0x17, emit_insn },
+ { "rlc", 0xCB, 0x00, emit_mr },
+ { "rlca", 0x00, 0x07, emit_insn },
+ { "rld", 0xED, 0x6F, emit_insn },
+ { "rr", 0xCB, 0x18, emit_mr },
+ { "rra", 0x00, 0x1F, emit_insn },
+ { "rrc", 0xCB, 0x08, emit_mr },
+ { "rrca", 0x00, 0x0F, emit_insn },
+ { "rrd", 0xED, 0x67, emit_insn },
+ { "rst", 0x00, 0xC7, emit_rst},
+ { "sbc", 0x98, 0x42, emit_adc },
+ { "scf", 0x00, 0x37, emit_insn },
+ { "set", 0xCB, 0xC0, emit_bit },
+ { "sla", 0xCB, 0x20, emit_mr },
+ { "sli", 0xCB, 0x30, emit_mr },
+ { "sll", 0xCB, 0x30, emit_mr },
+ { "sra", 0xCB, 0x28, emit_mr },
+ { "srl", 0xCB, 0x38, emit_mr },
+ { "sub", 0x00, 0x90, emit_s },
+ { "xor", 0x00, 0xA8, emit_s },
+} ;
+
+void
+md_assemble (char* str)
+{
+ const char *p;
+ char * old_ptr;
+ int i;
+ table_t *insp;
+
+ err_flag = 0;
+ old_ptr = input_line_pointer;
+ p = skip_space (str);
+ for (i = 0; (i < BUFLEN) && (ISALPHA (*p));)
+ buf[i++] = TOLOWER (*p++);
+
+ if (i == BUFLEN)
+ {
+ buf[BUFLEN-3] = buf[BUFLEN-2] = '.'; /* Mark opcode as abbreviated. */
+ buf[BUFLEN-1] = 0;
+ as_bad (_("Unknown instruction '%s'"), buf);
+ }
+ else if ((*p) && (!ISSPACE (*p)))
+ as_bad (_("syntax error"));
+ else
+ {
+ buf[i] = 0;
+ p = skip_space (p);
+ key = buf;
+
+ insp = bsearch (&key, instab, ARRAY_SIZE (instab),
+ sizeof (instab[0]), key_cmp);
+ if (!insp)
+ as_bad (_("Unknown instruction '%s'"), buf);
+ else
+ {
+ p = insp->fp (insp->prefix, insp->opcode, p);
+ p = skip_space (p);
+ if ((!err_flag) && *p)
+ as_bad (_("junk at end of line, first unrecognized character is `%c'"),
+ *p);
+ }
+ }
+ input_line_pointer = old_ptr;
+}
+
+void
+md_apply_fix (fixS * fixP, valueT* valP, segT seg ATTRIBUTE_UNUSED)
+{
+ long val = * (long *) valP;
+ char *p_lit = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8_PCREL:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ fixP->fx_no_overflow = (-128 <= val && val < 128);
+ if (!fixP->fx_no_overflow)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relative jump out of range"));
+ *p_lit++ = val;
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_Z80_DISP8:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ fixP->fx_no_overflow = (-128 <= val && val < 128);
+ if (!fixP->fx_no_overflow)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("index offset out of range"));
+ *p_lit++ = val;
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_8:
+ if (val > 255 || val < -128)
+ as_warn_where (fixP->fx_file, fixP->fx_line, _("overflow"));
+ *p_lit++ = val;
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_16:
+ *p_lit++ = val;
+ *p_lit++ = (val >> 8);
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_24: /* Def24 may produce this. */
+ *p_lit++ = val;
+ *p_lit++ = (val >> 8);
+ *p_lit++ = (val >> 16);
+ fixP->fx_no_overflow = 1;
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ case BFD_RELOC_32: /* Def32 and .long may produce this. */
+ *p_lit++ = val;
+ *p_lit++ = (val >> 8);
+ *p_lit++ = (val >> 16);
+ *p_lit++ = (val >> 24);
+ if (fixP->fx_addsy == NULL)
+ fixP->fx_done = 1;
+ break;
+
+ default:
+ printf (_("md_apply_fix: unknown r_type 0x%x\n"), fixP->fx_r_type);
+ abort ();
+ }
+}
+
+/* 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'. */
+
+/* If while processing a fixup, a reloc really
+ needs to be created then it is done here. */
+
+arelent *
+tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED , fixS *fixp)
+{
+ arelent *reloc;
+
+ if (! bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type))
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("reloc %d not supported by object file format"),
+ (int) fixp->fx_r_type);
+ return NULL;
+ }
+
+ reloc = xmalloc (sizeof (arelent));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ reloc->addend = fixp->fx_offset;
+
+ return reloc;
+}
diff --git a/gas/config/tc-z80.h b/gas/config/tc-z80.h
new file mode 100644
index 0000000..7409871
--- /dev/null
+++ b/gas/config/tc-z80.h
@@ -0,0 +1,109 @@
+/* this is tc-z80.h
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ Contributed by Arnold Metselaar <arnold_m@operamail.com>
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of .the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef TC_Z80
+#define TC_Z80
+
+#define TARGET_ARCH bfd_arch_z80
+#define BFD_ARCH TARGET_ARCH
+#define COFF_MAGIC 0x5A80
+#define TARGET_MACH 0
+#define TARGET_BYTES_BIG_ENDIAN 0
+
+/* If you define this macro, GAS will warn about the
+ use of nonstandard escape sequences in a string. */
+#define ONLY_STANDARD_ESCAPES
+
+/* GAS will call this function for any expression that can not be
+ recognized. When the function is called, `input_line_pointer'
+ will point to the start of the expression. */
+#define md_operand(x)
+
+/* This should just call either `number_to_chars_bigendian' or
+ `number_to_chars_littleendian', whichever is appropriate. On
+ targets like the MIPS which support options to change the
+ endianness, which function to call is a runtime decision. On
+ other targets, `md_number_to_chars' can be a simple macro. */
+#define md_number_to_chars number_to_chars_littleendian
+
+#define TC_COUNT_RELOC(x) 1
+
+#define TC_COFF_FIX2RTYPE(fixP) tc_coff_fix2rtype (fixP)
+#define md_convert_frag(b,s,f) as_fatal ("convert_frag called\n")
+#define md_estimate_size_before_relax(f,s) \
+ (as_fatal (_("estimate_size_before_relax called")), 1)
+
+/* Define some functions to be called by generic code. */
+#define md_end z80_md_end
+#define md_start_line_hook() { if (z80_start_line_hook ()) continue; }
+#define TC_CONS_FIX_NEW(f,w,s,e,r) z80_cons_fix_new ((f), (w), (s), (e))
+
+extern void z80_md_end (void);
+extern int z80_start_line_hook (void);
+extern void z80_cons_fix_new (fragS *, int, int, expressionS *);
+
+#define WORKING_DOT_WORD
+
+/* If you define this macro, it means that `tc_gen_reloc' may return
+ multiple relocation entries for a single fixup. In this case, the
+ return value of `tc_gen_reloc' is a pointer to a null terminated
+ array. */
+#undef RELOC_EXPANSION_POSSIBLE
+
+/* No shared lib support, so we don't need to ensure
+ externally visible symbols can be overridden. */
+#define EXTERN_FORCE_RELOC 0
+
+/* Values passed to md_apply_fix3 don't include the symbol value. */
+#define MD_APPLY_SYM_VALUE(FIX) 0
+
+#define LISTING_WORD_SIZE 2
+
+/* A single '=' is accepted as a comparison operator. */
+#define O_SINGLE_EQ O_eq
+
+/* A '$' is used to refer to the current location or as a hex. prefix. */
+#define DOLLAR_DOT
+#define DOLLAR_AMBIGU 1
+#define LOCAL_LABELS_FB 1
+#define LITERAL_PREFIXPERCENT_BIN
+#define NUMBERS_WITH_SUFFIX 1
+#define NO_PSEUDO_DOT 1
+/* We allow single quotes to delimit character constants as
+ well, but it is cleaner to handle that in tc-z80.c. */
+#define SINGLE_QUOTE_STRINGS
+#define NO_STRING_ESCAPES
+
+/* An `.lcomm' directive with no explicit alignment parameter will
+ use this macro to set P2VAR to the alignment that a request for
+ SIZE bytes will have. The alignment is expressed as a power of
+ two. If no alignment should take place, the macro definition
+ should do nothing. Some targets define a `.bss' directive that is
+ also affected by this macro. The default definition will set
+ P2VAR to the truncated power of two of sizes up to eight bytes. */
+#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) (P2VAR) = 0
+
+/* It does not make any sense to perform arithmetic on the numbers
+ we use to identify registers. */
+#define md_register_arithmetic 0
+
+#endif
diff --git a/gas/config/tc-z8k.c b/gas/config/tc-z8k.c
new file mode 100644
index 0000000..3b5f0b8
--- /dev/null
+++ b/gas/config/tc-z8k.c
@@ -0,0 +1,1565 @@
+/* tc-z8k.c -- Assemble code for the Zilog Z800n
+ Copyright (C) 1992-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Written By Steve Chamberlain <sac@cygnus.com>. */
+
+#include "as.h"
+#include "safe-ctype.h"
+#define DEFINE_TABLE
+#include "opcodes/z8k-opc.h"
+
+const char comment_chars[] = "!";
+const char line_comment_chars[] = "#";
+const char line_separator_chars[] = ";";
+
+extern int machine;
+extern int coff_flags;
+int segmented_mode;
+
+/* This is non-zero if target was set from the command line.
+ If non-zero, 1 means Z8002 (non-segmented), 2 means Z8001 (segmented). */
+static int z8k_target_from_cmdline;
+
+static void
+s_segm (int segm)
+{
+ if (segm)
+ {
+ segmented_mode = 1;
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, bfd_mach_z8001);
+ }
+ else
+ {
+ segmented_mode = 0;
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, bfd_mach_z8002);
+ }
+}
+
+static void
+even (int ignore ATTRIBUTE_UNUSED)
+{
+ frag_align (1, 0, 0);
+ record_alignment (now_seg, 1);
+}
+
+static int
+tohex (int c)
+{
+ if (ISDIGIT (c))
+ return c - '0';
+ if (ISLOWER (c))
+ return c - 'a' + 10;
+ return c - 'A' + 10;
+}
+
+static void
+sval (int ignore ATTRIBUTE_UNUSED)
+{
+ SKIP_WHITESPACE ();
+ if (*input_line_pointer == '\'')
+ {
+ int c;
+ input_line_pointer++;
+ c = *input_line_pointer++;
+ while (c != '\'')
+ {
+ if (c == '%')
+ {
+ c = (tohex (input_line_pointer[0]) << 4)
+ | tohex (input_line_pointer[1]);
+ input_line_pointer += 2;
+ }
+ FRAG_APPEND_1_CHAR (c);
+ c = *input_line_pointer++;
+ }
+ demand_empty_rest_of_line ();
+ }
+}
+
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function
+ */
+
+const pseudo_typeS md_pseudo_table[] = {
+ {"int" , cons , 2},
+ {"data.b" , cons , 1},
+ {"data.w" , cons , 2},
+ {"data.l" , cons , 4},
+ {"form" , listing_psize , 0},
+ {"heading", listing_title , 0},
+ {"import" , s_ignore , 0},
+ {"page" , listing_eject , 0},
+ {"program", s_ignore , 0},
+ {"z8001" , s_segm , 1},
+ {"z8002" , s_segm , 0},
+
+ {"segm" , s_segm , 1},
+ {"unsegm" , s_segm , 0},
+ {"unseg" , s_segm , 0},
+ {"name" , s_app_file , 0},
+ {"global" , s_globl , 0},
+ {"wval" , cons , 2},
+ {"lval" , cons , 4},
+ {"bval" , cons , 1},
+ {"sval" , sval , 0},
+ {"rsect" , obj_coff_section, 0},
+ {"sect" , obj_coff_section, 0},
+ {"block" , s_space , 0},
+ {"even" , even , 0},
+ {0 , 0 , 0}
+};
+
+const char EXP_CHARS[] = "eE";
+
+/* Chars that mean this number is a floating point constant.
+ As in 0f12.456
+ or 0d1.2345e12 */
+const char FLT_CHARS[] = "rRsSfFdDxXpP";
+
+/* Opcode mnemonics. */
+static struct hash_control *opcode_hash_control;
+
+void
+md_begin (void)
+{
+ const opcode_entry_type *opcode;
+ int idx = -1;
+
+ opcode_hash_control = hash_new ();
+
+ for (opcode = z8k_table; opcode->name; opcode++)
+ {
+ /* Only enter unique codes into the table. */
+ if (idx != opcode->idx)
+ hash_insert (opcode_hash_control, opcode->name, (char *) opcode);
+ idx = opcode->idx;
+ }
+
+ /* Default to z8002. */
+ s_segm (z8k_target_from_cmdline ? z8k_target_from_cmdline - 1 : 0);
+
+ /* Insert the pseudo ops, too. */
+ for (idx = 0; md_pseudo_table[idx].poc_name; idx++)
+ {
+ opcode_entry_type *fake_opcode;
+ fake_opcode = (opcode_entry_type *) malloc (sizeof (opcode_entry_type));
+ fake_opcode->name = md_pseudo_table[idx].poc_name;
+ fake_opcode->func = (void *) (md_pseudo_table + idx);
+ fake_opcode->opcode = 250;
+ hash_insert (opcode_hash_control, fake_opcode->name, fake_opcode);
+ }
+}
+
+typedef struct z8k_op {
+ /* CLASS_REG_xxx. */
+ int regsize;
+
+ /* 0 .. 15. */
+ unsigned int reg;
+
+ int mode;
+
+ /* Any other register associated with the mode. */
+ unsigned int x_reg;
+
+ /* Any expression. */
+ expressionS exp;
+} op_type;
+
+static expressionS *da_operand;
+static expressionS *imm_operand;
+
+static int reg[16];
+static int the_cc;
+static int the_ctrl;
+static int the_flags;
+static int the_interrupt;
+
+/* Determine register number. src points to the ascii number
+ (after "rl", "rh", "r", "rr", or "rq"). If a character
+ outside the set of {0,',',')','('} follows the number,
+ return NULL to indicate that it's not a valid register
+ number. */
+
+static char *
+whatreg (unsigned int *preg, char *src)
+{
+ unsigned int new_reg;
+
+ /* src[0] is already known to be a digit. */
+ if (ISDIGIT (src[1]))
+ {
+ new_reg = (src[0] - '0') * 10 + src[1] - '0';
+ src += 2;
+ }
+ else
+ {
+ new_reg = (src[0] - '0');
+ src += 1;
+ }
+
+ if (src[0] != 0 && src[0] != ',' && src[0] != '(' && src[0] != ')')
+ return NULL;
+
+ *preg = new_reg;
+ return src;
+}
+
+/* Parse operands
+
+ rh0-rh7, rl0-rl7
+ r0-r15
+ rr0-rr14
+ rq0--rq12
+ WREG r0,r1,r2,r3,r4,r5,r6,r7,fp,sp
+ r0l,r0h,..r7l,r7h
+ @WREG
+ @WREG+
+ @-WREG
+ #const
+*/
+
+/* Try to parse a reg name. Return a pointer to the first character
+ in SRC after the reg name. */
+
+static char *
+parse_reg (char *src, int *mode, unsigned int *preg)
+{
+ char *res = NULL;
+ char regno;
+
+ /* Check for stack pointer "sp" alias. */
+ if ((src[0] == 's' || src[0] == 'S')
+ && (src[1] == 'p' || src[1] == 'P')
+ && (src[2] == 0 || src[2] == ','))
+ {
+ if (segmented_mode)
+ {
+ *mode = CLASS_REG_LONG;
+ *preg = 14;
+ }
+ else
+ {
+ *mode = CLASS_REG_WORD;
+ *preg = 15;
+ }
+ return src + 2;
+ }
+
+ if (src[0] == 'r' || src[0] == 'R')
+ {
+ if (src[1] == 'r' || src[1] == 'R')
+ {
+ if (src[2] < '0' || src[2] > '9')
+ return NULL; /* Assume no register name but a label starting with 'rr'. */
+ *mode = CLASS_REG_LONG;
+ res = whatreg (preg, src + 2);
+ if (res == NULL)
+ return NULL; /* Not a valid register name. */
+ regno = *preg;
+ if (regno > 14)
+ as_bad (_("register rr%d out of range"), regno);
+ if (regno & 1)
+ as_bad (_("register rr%d does not exist"), regno);
+ }
+ else if (src[1] == 'h' || src[1] == 'H')
+ {
+ if (src[2] < '0' || src[2] > '9')
+ return NULL; /* Assume no register name but a label starting with 'rh'. */
+ *mode = CLASS_REG_BYTE;
+ res = whatreg (preg, src + 2);
+ if (res == NULL)
+ return NULL; /* Not a valid register name. */
+ regno = *preg;
+ if (regno > 7)
+ as_bad (_("register rh%d out of range"), regno);
+ }
+ else if (src[1] == 'l' || src[1] == 'L')
+ {
+ if (src[2] < '0' || src[2] > '9')
+ return NULL; /* Assume no register name but a label starting with 'rl'. */
+ *mode = CLASS_REG_BYTE;
+ res = whatreg (preg, src + 2);
+ if (res == NULL)
+ return NULL; /* Not a valid register name. */
+ regno = *preg;
+ if (regno > 7)
+ as_bad (_("register rl%d out of range"), regno);
+ *preg += 8;
+ }
+ else if (src[1] == 'q' || src[1] == 'Q')
+ {
+ if (src[2] < '0' || src[2] > '9')
+ return NULL; /* Assume no register name but a label starting with 'rq'. */
+ *mode = CLASS_REG_QUAD;
+ res = whatreg (preg, src + 2);
+ if (res == NULL)
+ return NULL; /* Not a valid register name. */
+ regno = *preg;
+ if (regno > 12)
+ as_bad (_("register rq%d out of range"), regno);
+ if (regno & 3)
+ as_bad (_("register rq%d does not exist"), regno);
+ }
+ else
+ {
+ if (src[1] < '0' || src[1] > '9')
+ return NULL; /* Assume no register name but a label starting with 'r'. */
+ *mode = CLASS_REG_WORD;
+ res = whatreg (preg, src + 1);
+ if (res == NULL)
+ return NULL; /* Not a valid register name. */
+ regno = *preg;
+ if (regno > 15)
+ as_bad (_("register r%d out of range"), regno);
+ }
+ }
+ return res;
+}
+
+static char *
+parse_exp (char *s, expressionS *op)
+{
+ char *save = input_line_pointer;
+ char *new_pointer;
+
+ input_line_pointer = s;
+ expression (op);
+ if (op->X_op == O_absent)
+ as_bad (_("missing operand"));
+ new_pointer = input_line_pointer;
+ input_line_pointer = save;
+ return new_pointer;
+}
+
+/* The many forms of operand:
+
+ <rb>
+ <r>
+ <rr>
+ <rq>
+ @r
+ #exp
+ exp
+ exp(r)
+ r(#exp)
+ r(r)
+ */
+
+static char *
+checkfor (char *ptr, char what)
+{
+ if (*ptr == what)
+ ptr++;
+ else
+ as_bad (_("expected %c"), what);
+
+ return ptr;
+}
+
+/* Make sure the mode supplied is the size of a word. */
+
+static void
+regword (int mode, char *string)
+{
+ int ok;
+
+ ok = CLASS_REG_WORD;
+ if (ok != mode)
+ {
+ as_bad (_("register is wrong size for a word %s"), string);
+ }
+}
+
+/* Make sure the mode supplied is the size of an address. */
+
+static void
+regaddr (int mode, char *string)
+{
+ int ok;
+
+ ok = segmented_mode ? CLASS_REG_LONG : CLASS_REG_WORD;
+ if (ok != mode)
+ {
+ as_bad (_("register is wrong size for address %s"), string);
+ }
+}
+
+struct ctrl_names {
+ int value;
+ char *name;
+};
+
+static struct ctrl_names ctrl_table[] = {
+ { 0x1, "flags" }, /* ldctlb only. */
+ { 0x2, "fcw" }, /* ldctl only. Applies to all remaining control registers. */
+ { 0x3, "refresh" },
+ { 0x4, "psapseg" },
+ { 0x5, "psapoff" },
+ { 0x5, "psap" },
+ { 0x6, "nspseg" },
+ { 0x7, "nspoff" },
+ { 0x7, "nsp" },
+ { 0 , 0 }
+};
+
+static void
+get_ctrl_operand (char **ptr, struct z8k_op *mode, unsigned int dst ATTRIBUTE_UNUSED)
+{
+ char *src = *ptr;
+ int i, l;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_CTRL;
+ for (i = 0; ctrl_table[i].name; i++)
+ {
+ l = strlen (ctrl_table[i].name);
+ if (! strncasecmp (ctrl_table[i].name, src, l))
+ {
+ the_ctrl = ctrl_table[i].value;
+ if (*(src + l) && *(src + l) != ',')
+ break;
+ *ptr = src + l; /* Valid control name found: "consume" it. */
+ return;
+ }
+ }
+ the_ctrl = 0;
+}
+
+struct flag_names {
+ int value;
+ char *name;
+};
+
+static struct flag_names flag_table[] = {
+ { 0x1, "P" },
+ { 0x1, "V" },
+ { 0x2, "S" },
+ { 0x4, "Z" },
+ { 0x8, "C" },
+ { 0x0, "+" },
+ { 0x0, "," },
+ { 0, 0 }
+};
+
+static void
+get_flags_operand (char **ptr, struct z8k_op *mode, unsigned int dst ATTRIBUTE_UNUSED)
+{
+ char *src = *ptr;
+ char c;
+ int i;
+ int j;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_FLAGS;
+ the_flags = 0;
+ for (j = 0; j <= 9; j++)
+ {
+ if (!src[j])
+ goto done;
+ c = TOUPPER(src[j]);
+ for (i = 0; flag_table[i].name; i++)
+ {
+ if (flag_table[i].name[0] == c)
+ {
+ the_flags = the_flags | flag_table[i].value;
+ goto match;
+ }
+ }
+ goto done;
+ match:
+ ;
+ }
+ done:
+ *ptr = src + j;
+}
+
+struct interrupt_names {
+ int value;
+ char *name;
+};
+
+static struct interrupt_names intr_table[] = {
+ { 0x1, "nvi" },
+ { 0x2, "vi" },
+ { 0x3, "both" },
+ { 0x3, "all" },
+ { 0, 0 }
+};
+
+static void
+get_interrupt_operand (char **ptr, struct z8k_op *mode, unsigned int dst ATTRIBUTE_UNUSED)
+{
+ char *src = *ptr;
+ int i, l;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_IMM;
+ the_interrupt = 0;
+
+ while (*src)
+ {
+ for (i = 0; intr_table[i].name; i++)
+ {
+ l = strlen (intr_table[i].name);
+ if (! strncasecmp (intr_table[i].name, src, l))
+ {
+ the_interrupt |= intr_table[i].value;
+ if (*(src + l) && *(src + l) != ',')
+ {
+ *ptr = src + l;
+ invalid:
+ as_bad (_("unknown interrupt %s"), src);
+ while (**ptr && ! is_end_of_line[(unsigned char) **ptr])
+ (*ptr)++; /* Consume rest of line. */
+ return;
+ }
+ src += l;
+ if (! *src)
+ {
+ *ptr = src;
+ return;
+ }
+ }
+ }
+ if (*src == ',')
+ src++;
+ else
+ {
+ *ptr = src;
+ goto invalid;
+ }
+ }
+
+ /* No interrupt type specified, opcode won't do anything. */
+ as_warn (_("opcode has no effect"));
+ the_interrupt = 0x0;
+}
+
+struct cc_names {
+ int value;
+ char *name;
+};
+
+static struct cc_names table[] = {
+ { 0x0, "f" },
+ { 0x1, "lt" },
+ { 0x2, "le" },
+ { 0x3, "ule" },
+ { 0x4, "ov/pe" },
+ { 0x4, "ov" },
+ { 0x4, "pe/ov" },
+ { 0x4, "pe" },
+ { 0x5, "mi" },
+ { 0x6, "eq" },
+ { 0x6, "z" },
+ { 0x7, "c/ult" },
+ { 0x7, "c" },
+ { 0x7, "ult/c" },
+ { 0x7, "ult" },
+ { 0x8, "t" },
+ { 0x9, "ge" },
+ { 0xa, "gt" },
+ { 0xb, "ugt" },
+ { 0xc, "nov/po" },
+ { 0xc, "nov" },
+ { 0xc, "po/nov" },
+ { 0xc, "po" },
+ { 0xd, "pl" },
+ { 0xe, "ne" },
+ { 0xe, "nz" },
+ { 0xf, "nc/uge" },
+ { 0xf, "nc" },
+ { 0xf, "uge/nc" },
+ { 0xf, "uge" },
+ { 0 , 0 }
+};
+
+static void
+get_cc_operand (char **ptr, struct z8k_op *mode, unsigned int dst ATTRIBUTE_UNUSED)
+{
+ char *src = *ptr;
+ int i, l;
+
+ while (*src == ' ')
+ src++;
+
+ mode->mode = CLASS_CC;
+ for (i = 0; table[i].name; i++)
+ {
+ l = strlen (table[i].name);
+ if (! strncasecmp (table[i].name, src, l))
+ {
+ the_cc = table[i].value;
+ if (*(src + l) && *(src + l) != ',')
+ break;
+ *ptr = src + l; /* Valid cc found: "consume" it. */
+ return;
+ }
+ }
+ the_cc = 0x8; /* Not recognizing the cc defaults to t. (Assuming no cc present.) */
+}
+
+static void
+get_operand (char **ptr, struct z8k_op *mode, unsigned int dst ATTRIBUTE_UNUSED)
+{
+ char *src = *ptr;
+ char *end;
+
+ mode->mode = 0;
+
+ while (*src == ' ')
+ src++;
+ if (*src == '#')
+ {
+ mode->mode = CLASS_IMM;
+ imm_operand = &(mode->exp);
+ src = parse_exp (src + 1, &(mode->exp));
+ }
+ else if (*src == '@')
+ {
+ mode->mode = CLASS_IR;
+ src = parse_reg (src + 1, &mode->regsize, &mode->reg);
+ }
+ else
+ {
+ unsigned int regn;
+
+ end = parse_reg (src, &mode->mode, &regn);
+
+ if (end)
+ {
+ int nw;
+ unsigned int nr;
+
+ src = end;
+ if (*src == '(')
+ {
+ src++;
+ end = parse_reg (src, &nw, &nr);
+ if (end)
+ {
+ /* Got Ra(Rb). */
+ src = end;
+
+ if (*src != ')')
+ as_bad (_("Missing ) in ra(rb)"));
+ else
+ src++;
+
+ regaddr (mode->mode, "ra(rb) ra");
+ mode->mode = CLASS_BX;
+ mode->reg = regn;
+ mode->x_reg = nr;
+ reg[ARG_RX] = nr;
+ }
+ else
+ {
+ /* Got Ra(disp). */
+ if (*src == '#')
+ src++;
+ src = parse_exp (src, &(mode->exp));
+ src = checkfor (src, ')');
+ mode->mode = CLASS_BA;
+ mode->reg = regn;
+ mode->x_reg = 0;
+ imm_operand = &(mode->exp);
+ }
+ }
+ else
+ {
+ mode->reg = regn;
+ mode->x_reg = 0;
+ }
+ }
+ else
+ {
+ /* No initial reg. */
+ src = parse_exp (src, &(mode->exp));
+ if (*src == '(')
+ {
+ src++;
+ end = parse_reg (src, &(mode->mode), &regn);
+ regword (mode->mode, "addr(Ra) ra");
+ mode->mode = CLASS_X;
+ mode->reg = regn;
+ mode->x_reg = 0;
+ da_operand = &(mode->exp);
+ src = checkfor (end, ')');
+ }
+ else
+ {
+ /* Just an address. */
+ mode->mode = CLASS_DA;
+ mode->reg = 0;
+ mode->x_reg = 0;
+ da_operand = &(mode->exp);
+ }
+ }
+ }
+ *ptr = src;
+}
+
+static char *
+get_operands (const opcode_entry_type *opcode, char *op_end, op_type *operand)
+{
+ char *ptr = op_end;
+ char *savptr;
+
+ switch (opcode->noperands)
+ {
+ case 0:
+ operand[0].mode = 0;
+ operand[1].mode = 0;
+ while (*ptr == ' ')
+ ptr++;
+ break;
+
+ case 1:
+ if (opcode->arg_info[0] == CLASS_CC)
+ {
+ get_cc_operand (&ptr, operand + 0, 0);
+ while (*ptr == ' ')
+ ptr++;
+ if (*ptr && ! is_end_of_line[(unsigned char) *ptr])
+ {
+ as_bad (_("invalid condition code '%s'"), ptr);
+ while (*ptr && ! is_end_of_line[(unsigned char) *ptr])
+ ptr++; /* Consume rest of line. */
+ }
+ }
+ else if (opcode->arg_info[0] == CLASS_FLAGS)
+ {
+ get_flags_operand (&ptr, operand + 0, 0);
+ while (*ptr == ' ')
+ ptr++;
+ if (*ptr && ! is_end_of_line[(unsigned char) *ptr])
+ {
+ as_bad (_("invalid flag '%s'"), ptr);
+ while (*ptr && ! is_end_of_line[(unsigned char) *ptr])
+ ptr++; /* Consume rest of line. */
+ }
+ }
+ else if (opcode->arg_info[0] == (CLASS_IMM + (ARG_IMM2)))
+ get_interrupt_operand (&ptr, operand + 0, 0);
+ else
+ get_operand (&ptr, operand + 0, 0);
+
+ operand[1].mode = 0;
+ break;
+
+ case 2:
+ savptr = ptr;
+ if (opcode->arg_info[0] == CLASS_CC)
+ {
+ get_cc_operand (&ptr, operand + 0, 0);
+ while (*ptr == ' ')
+ ptr++;
+ if (*ptr != ',' && strchr (ptr + 1, ','))
+ {
+ savptr = ptr;
+ while (*ptr != ',')
+ ptr++;
+ *ptr = 0;
+ ptr++;
+ as_bad (_("invalid condition code '%s'"), savptr);
+ }
+ }
+ else if (opcode->arg_info[0] == CLASS_CTRL)
+ {
+ get_ctrl_operand (&ptr, operand + 0, 0);
+
+ if (the_ctrl == 0)
+ {
+ ptr = savptr;
+ get_operand (&ptr, operand + 0, 0);
+
+ if (ptr == 0)
+ return NULL;
+ if (*ptr == ',')
+ ptr++;
+ get_ctrl_operand (&ptr, operand + 1, 1);
+ if (the_ctrl == 0)
+ return NULL;
+ return ptr;
+ }
+ }
+ else
+ get_operand (&ptr, operand + 0, 0);
+
+ if (ptr == 0)
+ return NULL;
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1);
+ break;
+
+ case 3:
+ get_operand (&ptr, operand + 0, 0);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 2, 2);
+ break;
+
+ case 4:
+ get_operand (&ptr, operand + 0, 0);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 1, 1);
+ if (*ptr == ',')
+ ptr++;
+ get_operand (&ptr, operand + 2, 2);
+ if (*ptr == ',')
+ ptr++;
+ get_cc_operand (&ptr, operand + 3, 3);
+ break;
+
+ default:
+ abort ();
+ }
+
+ return ptr;
+}
+
+/* Passed a pointer to a list of opcodes which use different
+ addressing modes. Return the opcode which matches the opcodes
+ provided. */
+
+static opcode_entry_type *
+get_specific (opcode_entry_type *opcode, op_type *operands)
+{
+ opcode_entry_type *this_try = opcode;
+ int found = 0;
+ unsigned int noperands = opcode->noperands;
+
+ int this_index = opcode->idx;
+
+ while (this_index == opcode->idx && !found)
+ {
+ unsigned int i;
+
+ this_try = opcode++;
+ for (i = 0; i < noperands; i++)
+ {
+ unsigned int mode = operands[i].mode;
+
+ if (((mode & CLASS_MASK) == CLASS_IR) && ((this_try->arg_info[i] & CLASS_MASK) == CLASS_IRO))
+ {
+ mode = operands[i].mode = (operands[i].mode & ~CLASS_MASK) | CLASS_IRO;
+ }
+
+ if ((mode & CLASS_MASK) != (this_try->arg_info[i] & CLASS_MASK))
+ {
+ /* It could be a pc rel operand, if this is a da mode
+ and we like disps, then insert it. */
+
+ if (mode == CLASS_DA && this_try->arg_info[i] == CLASS_DISP)
+ {
+ /* This is the case. */
+ operands[i].mode = CLASS_DISP;
+ }
+ else if (mode == CLASS_BA && this_try->arg_info[i])
+ {
+ /* Can't think of a way to turn what we've been
+ given into something that's OK. */
+ goto fail;
+ }
+ else if (this_try->arg_info[i] & CLASS_PR)
+ {
+ if (mode == CLASS_REG_LONG && segmented_mode)
+ {
+ /* OK. */
+ }
+ else if (mode == CLASS_REG_WORD && !segmented_mode)
+ {
+ /* OK. */
+ }
+ else
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ switch (mode & CLASS_MASK)
+ {
+ default:
+ break;
+ case CLASS_IRO:
+ if (operands[i].regsize != CLASS_REG_WORD)
+ as_bad (_("invalid indirect register size"));
+ reg[this_try->arg_info[i] & ARG_MASK] = operands[i].reg;
+ break;
+ case CLASS_IR:
+ if ((segmented_mode && operands[i].regsize != CLASS_REG_LONG)
+ || (!segmented_mode && operands[i].regsize != CLASS_REG_WORD))
+ as_bad (_("invalid indirect register size"));
+ reg[this_try->arg_info[i] & ARG_MASK] = operands[i].reg;
+ break;
+ case CLASS_X:
+ case CLASS_BA:
+ case CLASS_BX:
+ case CLASS_DISP:
+ case CLASS_REG:
+ case CLASS_REG_WORD:
+ case CLASS_REG_BYTE:
+ case CLASS_REG_QUAD:
+ case CLASS_REG_LONG:
+ case CLASS_REGN0:
+ reg[this_try->arg_info[i] & ARG_MASK] = operands[i].reg;
+ break;
+ case CLASS_CTRL:
+ if (this_try->opcode == OPC_ldctlb && the_ctrl != 1)
+ as_bad (_("invalid control register name"));
+ break;
+ }
+ }
+
+ found = 1;
+ fail:
+ ;
+ }
+ if (found)
+ return this_try;
+ else
+ return 0;
+}
+
+static char buffer[20];
+
+static void
+newfix (int ptr, int type, int size, expressionS *operand)
+{
+ int is_pcrel = 0;
+ fixS *fixP;
+
+ /* Size is in nibbles. */
+ if (operand->X_add_symbol
+ || operand->X_op_symbol
+ || operand->X_add_number)
+ {
+ switch(type)
+ {
+ case BFD_RELOC_8_PCREL:
+ case BFD_RELOC_Z8K_CALLR:
+ case BFD_RELOC_Z8K_DISP7:
+ is_pcrel = 1;
+ }
+ fixP = fix_new_exp (frag_now, ptr, size / 2,
+ operand, is_pcrel, type);
+ if (is_pcrel)
+ fixP->fx_no_overflow = 1;
+ }
+}
+
+static char *
+apply_fix (char *ptr, int type, expressionS *operand, int size)
+{
+ long n = operand->X_add_number;
+
+ /* size is in nibbles. */
+
+ newfix ((ptr - buffer) / 2, type, size + 1, operand);
+ switch (size)
+ {
+ case 8: /* 8 nibbles == 32 bits. */
+ *ptr++ = n >> 28;
+ *ptr++ = n >> 24;
+ *ptr++ = n >> 20;
+ *ptr++ = n >> 16;
+ case 4: /* 4 nibbles == 16 bits. */
+ *ptr++ = n >> 12;
+ *ptr++ = n >> 8;
+ case 2:
+ *ptr++ = n >> 4;
+ case 1:
+ *ptr++ = n >> 0;
+ break;
+ }
+ return ptr;
+}
+
+/* Now we know what sort of opcodes it is. Let's build the bytes. */
+
+static void
+build_bytes (opcode_entry_type *this_try, struct z8k_op *operand ATTRIBUTE_UNUSED)
+{
+ char *output_ptr = buffer;
+ int c;
+ int nibble;
+ unsigned int *class_ptr;
+
+ frag_wane (frag_now);
+ frag_new (0);
+
+ if (frag_room () < 8)
+ frag_grow (8); /* Make room for maximum instruction size. */
+
+ memset (buffer, 0, sizeof (buffer));
+ class_ptr = this_try->byte_info;
+
+ for (nibble = 0; (c = *class_ptr++); nibble++)
+ {
+
+ switch (c & CLASS_MASK)
+ {
+ default:
+ abort ();
+
+ case CLASS_ADDRESS:
+ /* Direct address, we don't cope with the SS mode right now. */
+ if (segmented_mode)
+ {
+ /* da_operand->X_add_number |= 0x80000000; -- Now set at relocation time. */
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_32, da_operand, 8);
+ }
+ else
+ {
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_16, da_operand, 4);
+ }
+ da_operand = 0;
+ break;
+ case CLASS_DISP8:
+ /* pc rel 8 bit */
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_8_PCREL, da_operand, 2);
+ da_operand = 0;
+ break;
+
+ case CLASS_0DISP7:
+ /* pc rel 7 bit */
+ *output_ptr = 0;
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_Z8K_DISP7, da_operand, 2);
+ da_operand = 0;
+ break;
+
+ case CLASS_1DISP7:
+ /* pc rel 7 bit */
+ *output_ptr = 0x80;
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_Z8K_DISP7, da_operand, 2);
+ output_ptr[-2] = 0x8;
+ da_operand = 0;
+ break;
+
+ case CLASS_BIT_1OR2:
+ *output_ptr = c & 0xf;
+ if (imm_operand)
+ {
+ if (imm_operand->X_add_number == 2)
+ *output_ptr |= 2;
+ else if (imm_operand->X_add_number != 1)
+ as_bad (_("immediate must be 1 or 2"));
+ }
+ else
+ as_bad (_("immediate 1 or 2 expected"));
+ output_ptr++;
+ break;
+ case CLASS_CC:
+ *output_ptr++ = the_cc;
+ break;
+ case CLASS_0CCC:
+ if (the_ctrl < 2 || the_ctrl > 7)
+ as_bad (_("invalid control register name"));
+ *output_ptr++ = the_ctrl;
+ break;
+ case CLASS_1CCC:
+ if (the_ctrl < 2 || the_ctrl > 7)
+ as_bad (_("invalid control register name"));
+ *output_ptr++ = the_ctrl | 0x8;
+ break;
+ case CLASS_00II:
+ *output_ptr++ = (~the_interrupt & 0x3);
+ break;
+ case CLASS_01II:
+ *output_ptr++ = (~the_interrupt & 0x3) | 0x4;
+ break;
+ case CLASS_FLAGS:
+ *output_ptr++ = the_flags;
+ break;
+ case CLASS_IGNORE:
+ case CLASS_BIT:
+ *output_ptr++ = c & 0xf;
+ break;
+ case CLASS_REGN0:
+ if (reg[c & 0xf] == 0)
+ as_bad (_("can't use R0 here"));
+ /* Fall through. */
+ case CLASS_REG:
+ case CLASS_REG_BYTE:
+ case CLASS_REG_WORD:
+ case CLASS_REG_LONG:
+ case CLASS_REG_QUAD:
+ /* Insert bit mattern of right reg. */
+ *output_ptr++ = reg[c & 0xf];
+ break;
+ case CLASS_DISP:
+ switch (c & ARG_MASK)
+ {
+ case ARG_DISP12:
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_Z8K_CALLR, da_operand, 4);
+ break;
+ case ARG_DISP16:
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_16_PCREL, da_operand, 4);
+ break;
+ default:
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_16, da_operand, 4);
+ }
+ da_operand = 0;
+ break;
+
+ case CLASS_IMM:
+ {
+ switch (c & ARG_MASK)
+ {
+ case ARG_NIM4:
+ if (imm_operand->X_add_number > 15)
+ as_bad (_("immediate value out of range"));
+ imm_operand->X_add_number = -imm_operand->X_add_number;
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_Z8K_IMM4L, imm_operand, 1);
+ break;
+ /*case ARG_IMMNMINUS1: not used. */
+ case ARG_IMM4M1:
+ imm_operand->X_add_number--;
+ /* Drop through. */
+ case ARG_IMM4:
+ if (imm_operand->X_add_number > 15)
+ as_bad (_("immediate value out of range"));
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_Z8K_IMM4L, imm_operand, 1);
+ break;
+ case ARG_NIM8:
+ imm_operand->X_add_number = -imm_operand->X_add_number;
+ /* Drop through. */
+ case ARG_IMM8:
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_8, imm_operand, 2);
+ break;
+ case ARG_IMM16:
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_16, imm_operand, 4);
+ break;
+ case ARG_IMM32:
+ output_ptr = apply_fix (output_ptr, BFD_RELOC_32, imm_operand, 8);
+ break;
+ default:
+ abort ();
+ }
+ }
+ }
+ }
+
+ /* Copy from the nibble buffer into the frag. */
+ {
+ int length = (output_ptr - buffer) / 2;
+ char *src = buffer;
+ char *fragp = frag_more (length);
+
+ while (src < output_ptr)
+ {
+ *fragp = (src[0] << 4) | src[1];
+ src += 2;
+ fragp++;
+ }
+ }
+}
+
+/* This is the guts of the machine-dependent assembler. STR points to a
+ machine dependent instruction. This function is supposed to emit
+ the frags/bytes it assembles to. */
+
+void
+md_assemble (char *str)
+{
+ char c;
+ char *op_start;
+ char *op_end;
+ struct z8k_op operand[4];
+ opcode_entry_type *opcode;
+
+ /* Drop leading whitespace. */
+ while (*str == ' ')
+ str++;
+
+ /* Find the op code end. */
+ for (op_start = op_end = str;
+ *op_end != 0 && *op_end != ' ' && ! is_end_of_line[(unsigned char) *op_end];
+ op_end++)
+ ;
+
+ if (op_end == op_start)
+ {
+ as_bad (_("can't find opcode "));
+ }
+ c = *op_end;
+
+ *op_end = 0; /* Zero-terminate op code string for hash_find() call. */
+
+ opcode = (opcode_entry_type *) hash_find (opcode_hash_control, op_start);
+
+ if (opcode == NULL)
+ {
+ as_bad (_("unknown opcode"));
+ return;
+ }
+
+ *op_end = c; /* Restore original string. */
+
+ if (opcode->opcode == 250)
+ {
+ pseudo_typeS *p;
+ char oc;
+ char *old = input_line_pointer;
+
+ /* Was really a pseudo op. */
+
+ input_line_pointer = op_end;
+
+ oc = *old;
+ *old = '\n';
+ while (*input_line_pointer == ' ')
+ input_line_pointer++;
+ p = (pseudo_typeS *) (opcode->func);
+
+ (p->poc_handler) (p->poc_val);
+ input_line_pointer = old;
+ *old = oc;
+ }
+ else
+ {
+ char *new_input_line_pointer;
+
+ new_input_line_pointer = get_operands (opcode, op_end, operand);
+ if (new_input_line_pointer)
+ {
+ input_line_pointer = new_input_line_pointer;
+ opcode = get_specific (opcode, operand);
+ }
+
+ if (new_input_line_pointer == NULL || opcode == NULL)
+ {
+ /* Couldn't find an opcode which matched the operands. */
+ char *where = frag_more (2);
+
+ where[0] = 0x0;
+ where[1] = 0x0;
+
+ as_bad (_("Can't find opcode to match operands"));
+ return;
+ }
+
+ build_bytes (opcode, operand);
+ }
+}
+
+/* We have no need to default values of symbols. */
+
+symbolS *
+md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
+{
+ return 0;
+}
+
+/* Various routines to kill one day. */
+
+char *
+md_atof (int type, char *litP, int *sizeP)
+{
+ return ieee_md_atof (type, litP, sizeP, TRUE);
+}
+
+const char *md_shortopts = "z:";
+
+struct option md_longopts[] =
+ {
+#define OPTION_RELAX (OPTION_MD_BASE)
+ {"linkrelax", no_argument, NULL, OPTION_RELAX},
+ {NULL, no_argument, NULL, 0}
+ };
+
+size_t md_longopts_size = sizeof (md_longopts);
+
+int
+md_parse_option (int c, char *arg)
+{
+ switch (c)
+ {
+ case 'z':
+ if (!strcmp (arg, "8001"))
+ z8k_target_from_cmdline = 2;
+ else if (!strcmp (arg, "8002"))
+ z8k_target_from_cmdline = 1;
+ else
+ {
+ as_bad (_("invalid architecture -z%s"), arg);
+ return 0;
+ }
+ break;
+
+ case OPTION_RELAX:
+ linkrelax = 1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+md_show_usage (FILE *stream)
+{
+ fprintf (stream, _("\
+ Z8K options:\n\
+ -z8001 generate segmented code\n\
+ -z8002 generate unsegmented code\n\
+ -linkrelax create linker relaxable code\n"));
+}
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED,
+ segT sec ATTRIBUTE_UNUSED,
+ fragS *fragP ATTRIBUTE_UNUSED)
+{
+ printf (_("call to md_convert_frag\n"));
+ abort ();
+}
+
+/* Generate a machine dependent reloc from a fixup. */
+
+arelent*
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED,
+ fixS *fixp ATTRIBUTE_UNUSED)
+{
+ arelent *reloc;
+
+ reloc = xmalloc (sizeof (*reloc));
+ reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *));
+ *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
+ reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+ reloc->addend = fixp->fx_offset;
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+
+ if (! reloc->howto)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("Cannot represent %s relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ abort ();
+ }
+ return reloc;
+}
+
+valueT
+md_section_align (segT seg, valueT size)
+{
+ int align = bfd_get_section_alignment (stdoutput, seg);
+ valueT mask = ((valueT) 1 << align) - 1;
+
+ return (size + mask) & ~mask;
+}
+
+/* Attempt to simplify or eliminate a fixup. To indicate that a fixup
+ has been eliminated, set fix->fx_done. If fix->fx_addsy is non-NULL,
+ we will have to generate a reloc entry. */
+void
+md_apply_fix (fixS *fixP, valueT *valP, segT segment ATTRIBUTE_UNUSED)
+{
+ long val = * (long *) valP;
+ char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_Z8K_IMM4L:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ buf[0] = (buf[0] & 0xf0) | (val & 0xf);
+ break;
+
+ case BFD_RELOC_8:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ *buf++ = val;
+ break;
+
+ case BFD_RELOC_16:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ }
+ break;
+
+ case BFD_RELOC_32:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ *buf++ = (val >> 24);
+ *buf++ = (val >> 16);
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ }
+ break;
+
+ case BFD_RELOC_8_PCREL:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ if (val & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot branch to odd address"));
+ val /= 2;
+ if (val > 127 || val < -128)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relative jump out of range"));
+ *buf++ = val;
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_16_PCREL:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ val = val - fixP->fx_frag->fr_address + fixP->fx_where - fixP->fx_size;
+ if (val > 32767 || val < -32768)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relative address out of range"));
+ *buf++ = (val >> 8);
+ *buf++ = val;
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_Z8K_CALLR:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ if (val & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot branch to odd address"));
+ if (val > 4096 || val < -4095)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relative call out of range"));
+ val = -val / 2;
+ *buf = (*buf & 0xf0) | ((val >> 8) & 0xf);
+ buf++;
+ *buf++ = val & 0xff;
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 1;
+ }
+ break;
+
+ case BFD_RELOC_Z8K_DISP7:
+ if (fixP->fx_addsy)
+ {
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 0;
+ }
+ else
+ {
+ if (val & 1)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot branch to odd address"));
+ val /= 2;
+ if (val > 0 || val < -127)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("relative jump out of range"));
+ *buf = (*buf & 0x80) | (-val & 0x7f);
+ fixP->fx_no_overflow = 1;
+ fixP->fx_done = 1;
+ }
+ break;
+
+ default:
+ printf(_("md_apply_fix: unknown r_type 0x%x\n"), fixP->fx_r_type);
+ abort ();
+ }
+
+ if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
+ fixP->fx_done = 1;
+}
+
+int
+md_estimate_size_before_relax (fragS *fragP ATTRIBUTE_UNUSED,
+ segT segment_type ATTRIBUTE_UNUSED)
+{
+ printf (_("call to md_estimate_size_before_relax\n"));
+ abort ();
+}
+
+/* Put number into target byte order. */
+
+void
+md_number_to_chars (char *ptr, valueT use, int nbytes)
+{
+ number_to_chars_bigendian (ptr, use, nbytes);
+}
+
+/* On the Z8000, a PC-relative offset is relative to the address of the
+ instruction plus its size. */
+long
+md_pcrel_from (fixS *fixP)
+{
+ return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address;
+}
+
+void
+tc_coff_symbol_emit_hook (symbolS *s ATTRIBUTE_UNUSED)
+{
+}
diff --git a/gas/config/tc-z8k.h b/gas/config/tc-z8k.h
new file mode 100644
index 0000000..be5f651
--- /dev/null
+++ b/gas/config/tc-z8k.h
@@ -0,0 +1,39 @@
+/* This file is tc-z8k.h
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TC_Z8K
+#define TARGET_BYTES_BIG_ENDIAN 1
+#define TARGET_ARCH bfd_arch_z8k
+#define TARGET_FORMAT "coff-z8k"
+
+struct internal_reloc;
+
+#define WORKING_DOT_WORD
+
+#define COFF_MAGIC 0x8000
+#define IGNORE_NONSTANDARD_ESCAPES
+#undef WARN_SIGNED_OVERFLOW_WORD
+
+#define tc_fix_adjustable(X) 0
+
+#define LISTING_HEADER "Zilog Z8000 GAS "
+#define RELOC_32 1234
+
+#define md_operand(x)
diff --git a/gas/config/te-386bsd.h b/gas/config/te-386bsd.h
new file mode 100644
index 0000000..7228d9a
--- /dev/null
+++ b/gas/config/te-386bsd.h
@@ -0,0 +1,32 @@
+/* te-386bsd.h -- 386BSD target environment declarations.
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_386BSD 1
+
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-386bsd.h */
diff --git a/gas/config/te-aix5.h b/gas/config/te-aix5.h
new file mode 100644
index 0000000..91ba577
--- /dev/null
+++ b/gas/config/te-aix5.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_AIX5
+
+#include "obj-format.h"
diff --git a/gas/config/te-armeabi.h b/gas/config/te-armeabi.h
new file mode 100644
index 0000000..fdd78ab
--- /dev/null
+++ b/gas/config/te-armeabi.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* The EABI requires the use of VFP. */
+#define FPU_DEFAULT FPU_ARCH_VFP
+#define EABI_DEFAULT EF_ARM_EABI_VER5
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-armfbsdeabi.h b/gas/config/te-armfbsdeabi.h
new file mode 100644
index 0000000..8a72e21
--- /dev/null
+++ b/gas/config/te-armfbsdeabi.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "te-freebsd.h"
+
+#define EABI_DEFAULT EF_ARM_EABI_VER5
diff --git a/gas/config/te-armfbsdvfp.h b/gas/config/te-armfbsdvfp.h
new file mode 100644
index 0000000..945ac7c
--- /dev/null
+++ b/gas/config/te-armfbsdvfp.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "te-armfbsdeabi.h"
+
+#define FPU_DEFAULT FPU_ARCH_VFP
diff --git a/gas/config/te-armlinuxeabi.h b/gas/config/te-armlinuxeabi.h
new file mode 100644
index 0000000..9147f77
--- /dev/null
+++ b/gas/config/te-armlinuxeabi.h
@@ -0,0 +1,24 @@
+/* Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#include "te-linux.h"
+
+/* The EABI requires the use of VFP. */
+#define FPU_DEFAULT FPU_ARCH_VFP
+#define EABI_DEFAULT EF_ARM_EABI_VER5
diff --git a/gas/config/te-dragonfly.h b/gas/config/te-dragonfly.h
new file mode 100644
index 0000000..9db1b39
--- /dev/null
+++ b/gas/config/te-dragonfly.h
@@ -0,0 +1,30 @@
+/* te-dragonfly.h -- DragonFlyBSD target environment declarations.
+ Copyright (C) 2011-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Target environment for DragonFlyBSD. It is the same as the generic
+ target, except that it arranges via the TE_DragonFly define to
+ suppress the use of "/" as a comment character. Some code in the
+ DragonFlyBSD kernel uses "/" to mean division. (What a concept!) */
+#define TE_DragonFly 1
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-dynix.h b/gas/config/te-dynix.h
new file mode 100644
index 0000000..05a497e
--- /dev/null
+++ b/gas/config/te-dynix.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This is for i386-sequent-bsd. The assembler probably does not
+ actually work, as the support in BFD is not complete as of this
+ writing. See bfd/i386-dynix.c. */
+
+#define TE_DYNIX 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-epoc-pe.h b/gas/config/te-epoc-pe.h
new file mode 100644
index 0000000..50dbd68
--- /dev/null
+++ b/gas/config/te-epoc-pe.h
@@ -0,0 +1,27 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_PE
+#define TE_EPOC
+#define LEX_AT 1 /* can have @'s inside labels */
+
+/* The PE format supports long section names. */
+#define COFF_LONG_SECTION_NAMES
+
+#include "obj-format.h"
diff --git a/gas/config/te-freebsd.h b/gas/config/te-freebsd.h
new file mode 100644
index 0000000..3744294
--- /dev/null
+++ b/gas/config/te-freebsd.h
@@ -0,0 +1,30 @@
+/* te-freebsd.h -- FreeBSD target environment declarations.
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* Target environment for FreeBSD. It is the same as the generic
+ target, except that it arranges via the TE_FreeBSD define to
+ suppress the use of "/" as a comment character. Some code in the
+ FreeBSD kernel uses "/" to mean division. (What a concept!) */
+#define TE_FreeBSD 1
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-generic.h b/gas/config/te-generic.h
new file mode 100644
index 0000000..9427d58
--- /dev/null
+++ b/gas/config/te-generic.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-generic.h and is intended to be a template for
+ target environment specific header files.
+
+ It is my intent that this file will evolve into a file suitable for config,
+ compile, and copying as an aid for testing and porting. xoxorich. */
+
+/* Added these, because if we don't know what we're targeting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* These define interfaces. */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
+
diff --git a/gas/config/te-gnu.h b/gas/config/te-gnu.h
new file mode 100644
index 0000000..0ea31fa
--- /dev/null
+++ b/gas/config/te-gnu.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_GNU
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-go32.h b/gas/config/te-go32.h
new file mode 100644
index 0000000..70a59fc
--- /dev/null
+++ b/gas/config/te-go32.h
@@ -0,0 +1,31 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_GO32
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* GAS should treat '.align value' as an alignment of 2**value. */
+#define USE_ALIGN_PTWO
+
+#define COFF_LONG_SECTION_NAMES
+
+/* These define interfaces. */
+#include "obj-format.h"
diff --git a/gas/config/te-hppa.h b/gas/config/te-hppa.h
new file mode 100644
index 0000000..5c78978
--- /dev/null
+++ b/gas/config/te-hppa.h
@@ -0,0 +1,28 @@
+/* Machine specific defines for the PA machine
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* HP PA-RISC and OSF/1 support was contributed by the Center for
+ Software Science at the University of Utah. */
+
+/* Labels are not required to have a colon for a suffix. */
+#define LABELS_WITHOUT_COLONS 1
+
+/* These define interfaces. */
+#include "obj-format.h"
diff --git a/gas/config/te-hppa64.h b/gas/config/te-hppa64.h
new file mode 100644
index 0000000..519b01a
--- /dev/null
+++ b/gas/config/te-hppa64.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TARGET_ARCH_SIZE 64
+
+/* Labels are not required to have a colon for a suffix. */
+#define LABELS_WITHOUT_COLONS 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-hppalinux64.h b/gas/config/te-hppalinux64.h
new file mode 100644
index 0000000..a98b041
--- /dev/null
+++ b/gas/config/te-hppalinux64.h
@@ -0,0 +1,24 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_LINUX
+#define TARGET_ARCH_SIZE 64
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-hpux.h b/gas/config/te-hpux.h
new file mode 100644
index 0000000..67cd30b
--- /dev/null
+++ b/gas/config/te-hpux.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_HPUX
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-i386aix.h b/gas/config/te-i386aix.h
new file mode 100644
index 0000000..79e3cc8
--- /dev/null
+++ b/gas/config/te-i386aix.h
@@ -0,0 +1,38 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-i386aix.h and is built from pieces of code from
+ Minh Tran-Le <TRANLE@INTELLICORP.COM> by rich@cygnus.com. */
+
+#define TE_I386AIX 1
+
+#include "obj-format.h"
+
+/* Define KEEP_RELOC_INFO so that the strip reloc info flag F_RELFLG is
+ not used in the filehdr for COFF output. */
+#define KEEP_RELOC_INFO
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 79
+ * End:
+ */
+
+/* end of te-i386aix.h */
diff --git a/gas/config/te-ia64aix.h b/gas/config/te-ia64aix.h
new file mode 100644
index 0000000..63768a3
--- /dev/null
+++ b/gas/config/te-ia64aix.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_AIX50
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-interix.h b/gas/config/te-interix.h
new file mode 100644
index 0000000..a348921
--- /dev/null
+++ b/gas/config/te-interix.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_PE_DYN /* PE with dynamic linking (UNIX shared lib) support */
+#define TE_PE
+#define LEX_AT 1 /* can have @'s inside labels */
+#define LEX_QM 3 /* can have ?'s in or begin labels */
+
+/* The PE format supports long section names. */
+#define COFF_LONG_SECTION_NAMES
+
+#define GLOBAL_OFFSET_TABLE_NAME "__GLOBAL_OFFSET_TABLE_"
+
+/* Both architectures use these. */
+#ifndef LOCAL_LABELS_FB
+#define LOCAL_LABELS_FB 1
+#endif
+
+#include "obj-format.h"
diff --git a/gas/config/te-irix.h b/gas/config/te-irix.h
new file mode 100644
index 0000000..ed81905
--- /dev/null
+++ b/gas/config/te-irix.h
@@ -0,0 +1,31 @@
+/* IRIX targets
+ Copyright (C) 2002-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-irix.h and is intended to provide support for
+ IRIX targets. Created by Alexandre Oliva <aoliva@redhat.com>. */
+
+#define TE_IRIX 1
+
+/* these define interfaces */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
diff --git a/gas/config/te-linux.h b/gas/config/te-linux.h
new file mode 100644
index 0000000..466f664
--- /dev/null
+++ b/gas/config/te-linux.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_LINUX
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-lynx.h b/gas/config/te-lynx.h
new file mode 100644
index 0000000..336d1b0
--- /dev/null
+++ b/gas/config/te-lynx.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_LYNX
+
+#include "obj-format.h"
+
+#ifndef LOCAL_LABELS_FB
+#define LOCAL_LABELS_FB 1
+#endif
diff --git a/gas/config/te-mach.h b/gas/config/te-mach.h
new file mode 100644
index 0000000..77f873d
--- /dev/null
+++ b/gas/config/te-mach.h
@@ -0,0 +1,21 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_Mach
+#include "obj-format.h"
diff --git a/gas/config/te-macos.h b/gas/config/te-macos.h
new file mode 100644
index 0000000..336444c
--- /dev/null
+++ b/gas/config/te-macos.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_POWERMAC 1
+
+/* Added these, because if we don't know what we're targeting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-nacl.h b/gas/config/te-nacl.h
new file mode 100644
index 0000000..2547f9f
--- /dev/null
+++ b/gas/config/te-nacl.h
@@ -0,0 +1,30 @@
+/* Copyright (C) 2012-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_NACL
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* These are for ARM but don't hurt other CPU targets.
+ They match the settings from te-armeabi.h; NaCl/ARM is based on EABI. */
+#define FPU_DEFAULT FPU_ARCH_VFP
+#define EABI_DEFAULT EF_ARM_EABI_VER5
+
+#include "obj-format.h"
diff --git a/gas/config/te-nbsd.h b/gas/config/te-nbsd.h
new file mode 100644
index 0000000..4249291
--- /dev/null
+++ b/gas/config/te-nbsd.h
@@ -0,0 +1,23 @@
+/* te-nbsd.h -- NetBSD target environment declarations.
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_NetBSD 1
+#define LOCAL_LABELS_FB 1
+#include "obj-format.h"
diff --git a/gas/config/te-nbsd532.h b/gas/config/te-nbsd532.h
new file mode 100644
index 0000000..7952483
--- /dev/null
+++ b/gas/config/te-nbsd532.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-netbsd532.h
+ Written by Ian Dall <idall@eleceng.adelaide.edu.au>
+ 19-Jun-94. */
+
+#define TARGET_FORMAT "a.out-ns32k-netbsd"
+
+#include "obj-format.h"
+
+/* Maybe these should be more like TC_NS32532 and TC_NS32381 in case
+ of conflicts. NS32381 is used in opcode/ns32k.h and that is also
+ used by GDB. Need to check. */
+#define NS32532
+#define NS32381
diff --git a/gas/config/te-netware.h b/gas/config/te-netware.h
new file mode 100644
index 0000000..9fc5928
--- /dev/null
+++ b/gas/config/te-netware.h
@@ -0,0 +1,28 @@
+/* te-netware.h -- NetWare target environment declarations.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_NETWARE
+#define LOCAL_LABELS_FB 1
+
+#define LEX_AT (LEX_NAME | LEX_BEGIN_NAME) /* Can have @'s inside labels. */
+#define LEX_PCT (LEX_NAME | LEX_BEGIN_NAME) /* Can have %'s inside labels. */
+#define LEX_QM (LEX_NAME | LEX_BEGIN_NAME) /* Can have ?'s inside labels. */
+
+#include "obj-format.h"
diff --git a/gas/config/te-pc532mach.h b/gas/config/te-pc532mach.h
new file mode 100644
index 0000000..4241c5a
--- /dev/null
+++ b/gas/config/te-pc532mach.h
@@ -0,0 +1,32 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-pc532.h
+ Written by Ian Dall <idall@eleceng.adelaide.edu.au>
+ 24-May-94. */
+
+#define TARGET_FORMAT "a.out-pc532-mach"
+
+#include "obj-format.h"
+
+/* Maybe these should be more like TC_NS32532 and TC_NS32381 in case
+ of conflicts. NS32381 is used in opcode/ns32k.h and that is also
+ used by GDB. Need to check. */
+#define NS32532
+#define NS32381
diff --git a/gas/config/te-pe.h b/gas/config/te-pe.h
new file mode 100644
index 0000000..a6b857d
--- /dev/null
+++ b/gas/config/te-pe.h
@@ -0,0 +1,26 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_PE
+#define LEX_AT (LEX_BEGIN_NAME | LEX_NAME) /* Can have @'s inside labels. */
+
+/* The PE format supports long section names. */
+#define COFF_LONG_SECTION_NAMES
+
+#include "obj-format.h"
diff --git a/gas/config/te-pep.h b/gas/config/te-pep.h
new file mode 100644
index 0000000..2995761
--- /dev/null
+++ b/gas/config/te-pep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2006-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_PEP
+#define COFF_WITH_pex64
+
+#define TE_PE
+#define LEX_AT (LEX_BEGIN_NAME | LEX_NAME) /* Can have @'s inside labels. */
+
+/* The PE format supports long section names. */
+#define COFF_LONG_SECTION_NAMES
+
+#include "obj-format.h"
diff --git a/gas/config/te-psos.h b/gas/config/te-psos.h
new file mode 100644
index 0000000..e6e2d97
--- /dev/null
+++ b/gas/config/te-psos.h
@@ -0,0 +1,35 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-psos.h for embedded systems running pSOS.
+ Contributed by Martin Anantharaman (martin@mail.imech.uni-duisburg.de). */
+
+#define TE_PSOS
+
+/* Added these, because if we don't know what we're targeting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* This makes GAS more versatile and blocks some ELF'isms in
+ tc-m68k.h. */
+#define REGISTER_PREFIX_OPTIONAL 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-riscix.h b/gas/config/te-riscix.h
new file mode 100644
index 0000000..9fa9f85
--- /dev/null
+++ b/gas/config/te-riscix.h
@@ -0,0 +1,25 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_RISCIX
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-solaris.h b/gas/config/te-solaris.h
new file mode 100644
index 0000000..3865999
--- /dev/null
+++ b/gas/config/te-solaris.h
@@ -0,0 +1,40 @@
+/* Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_SOLARIS
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
+
+/* The Sun linker doesn't merge read-only and read-write sections into
+ a single section so we must force all EH frame sections to use the
+ same flags. For SPARC and 32-bit i386 this is read-write, whilst
+ for x86_64 this is read-only, matching GCC behavior.
+
+ See the definition of EH_TABLES_CAN_BE_READ_ONLY in
+ gcc/config/i386/sol2.h in the GCC sources and the thread starting at
+ http://sourceware.org/ml/binutils/2010-01/msg00401.html. */
+#ifdef TC_SPARC
+#define DWARF2_EH_FRAME_READ_ONLY SEC_NO_FLAGS
+#else
+#define DWARF2_EH_FRAME_READ_ONLY \
+ (bfd_get_arch_size (stdoutput) == 64 ? SEC_READONLY : SEC_NO_FLAGS)
+#endif
diff --git a/gas/config/te-sparcaout.h b/gas/config/te-sparcaout.h
new file mode 100644
index 0000000..5502650
--- /dev/null
+++ b/gas/config/te-sparcaout.h
@@ -0,0 +1,22 @@
+/* te-sparcaout.h -- embedded sparc-aout target environment declarations.
+ Copyright (C) 1996-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+#define TE_SPARCAOUT 1
+#include "obj-format.h"
diff --git a/gas/config/te-sun3.h b/gas/config/te-sun3.h
new file mode 100644
index 0000000..7138e22
--- /dev/null
+++ b/gas/config/te-sun3.h
@@ -0,0 +1,48 @@
+/* te-sun3.h -- Sun-3 target environment declarations.
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This header file contains the #defines specific
+ to SUN computer SUN 3 series computers. (The only kind
+ we have around here, unfortunately.)
+
+ Rumor has it that this file will work on the Sun-2 if the assembler
+ is called with -m68010 This is not tested. */
+
+#define TE_SUN3 1
+
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* Could also be :
+ #define S_LOCAL_NAME(s) (S_GET_NAME(s)[0] == '.' &&
+ S_GET_NAME(s)[1] == 'L' ||
+ S_GET_NAME(s)[1] == '.')
+ */
+
+#include "obj-format.h"
+
+/*
+ * Local Variables:
+ * comment-column: 0
+ * fill-column: 131
+ * End:
+ */
+
+/* end of te-sun3.h */
diff --git a/gas/config/te-svr4.h b/gas/config/te-svr4.h
new file mode 100644
index 0000000..f989a9d
--- /dev/null
+++ b/gas/config/te-svr4.h
@@ -0,0 +1,23 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_SVR4
+#define LOCAL_LABELS_FB 1
+
+#include "obj-format.h"
diff --git a/gas/config/te-symbian.h b/gas/config/te-symbian.h
new file mode 100644
index 0000000..4b52b22
--- /dev/null
+++ b/gas/config/te-symbian.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2004-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_SYMBIAN 1
+
+#include "te-armeabi.h"
diff --git a/gas/config/te-tmips.h b/gas/config/te-tmips.h
new file mode 100644
index 0000000..ce5dbda
--- /dev/null
+++ b/gas/config/te-tmips.h
@@ -0,0 +1,40 @@
+/* Traditional MIPS targets
+ Copyright (C) 2000-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+/* This file is te-tmips.h and is intended to provide support for
+ traditional mips targets like mips-dde-sysv4.2MP (Supermax ) ,
+ mips-sni-sysv4* (Sinix) etc. The base for this file is te-generic.h.
+ Created by Koundinya.K < kk@ddeorg.soft.net > with the help of
+ Ian Lance Taylor, Cygnus Support, <ian@cygnus.com>. */
+
+/* Added these, because if we don't know what we're targeting we may
+ need an assembler version of libgcc, and that will use local
+ labels. */
+
+#define TE_TMIPS 1
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* these define interfaces */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
diff --git a/gas/config/te-uclinux.h b/gas/config/te-uclinux.h
new file mode 100644
index 0000000..bb0ead0
--- /dev/null
+++ b/gas/config/te-uclinux.h
@@ -0,0 +1,22 @@
+/* Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_UCLINUX
+
+#include "te-generic.h"
diff --git a/gas/config/te-vms.c b/gas/config/te-vms.c
new file mode 100644
index 0000000..8bb15c5
--- /dev/null
+++ b/gas/config/te-vms.c
@@ -0,0 +1,347 @@
+/* te-vms.c -- Utilities for VMS.
+ Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ Written by Douglas B Rupp <rupp@gnat.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program 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 this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#include "as.h"
+#include "te-vms.h"
+
+/* The purspose of the two alternate versions below is to have one that
+ works for native VMS and one that works on an NFS mounted filesystem
+ (Unix Server/VMS client). The main issue being to generate the special
+ VMS file timestamps for the debug info. */
+
+#ifdef VMS
+#define __NEW_STARLET 1
+#include <vms/starlet.h>
+#include <vms/rms.h>
+#include <vms/atrdef.h>
+#include <vms/fibdef.h>
+#include <vms/stsdef.h>
+#include <vms/iodef.h>
+#include <vms/fatdef.h>
+#include <errno.h>
+#include <vms/descrip.h>
+#include <string.h>
+#include <unixlib.h>
+
+#define MAXPATH 256
+
+/* Descrip.h doesn't have everything... */
+typedef struct fibdef * __fibdef_ptr32 __attribute__ (( mode (SI) ));
+
+struct dsc$descriptor_fib
+{
+ unsigned int fib$l_len;
+ __fibdef_ptr32 fib$l_addr;
+};
+
+/* I/O Status Block. */
+struct IOSB
+{
+ unsigned short status, count;
+ unsigned int devdep;
+};
+
+static char *tryfile;
+
+/* Variable length string. */
+struct vstring
+{
+ short length;
+ char string[NAM$C_MAXRSS+1];
+};
+
+static char filename_buff [MAXPATH];
+static char vms_filespec [MAXPATH];
+
+/* Callback function for filespec style conversion. */
+
+static int
+translate_unix (char *name, int type ATTRIBUTE_UNUSED)
+{
+ strncpy (filename_buff, name, MAXPATH);
+ filename_buff [MAXPATH - 1] = (char) 0;
+ return 0;
+}
+
+/* Wrapper for DECC function that converts a Unix filespec
+ to VMS style filespec. */
+
+static char *
+to_vms_file_spec (char *filespec)
+{
+ strncpy (vms_filespec, "", MAXPATH);
+ decc$to_vms (filespec, translate_unix, 1, 1);
+ strncpy (vms_filespec, filename_buff, MAXPATH);
+
+ vms_filespec [MAXPATH - 1] = (char) 0;
+
+ return vms_filespec;
+}
+
+#else /* not VMS */
+
+#define _BSD_SOURCE 1
+#include <sys/stat.h>
+#include <time.h>
+
+#define VMS_EPOCH_OFFSET 35067168000000000LL
+#define VMS_GRANULARITY_FACTOR 10000000
+
+#endif /* VMS */
+
+/* Return VMS file date, size, format, version given a name. */
+
+static int
+vms_file_stats_name (const char *dirname,
+ const char *filename,
+ long long *cdt,
+ long *siz,
+ char *rfo,
+ int *ver)
+{
+ char fullname[strlen (dirname) + strlen (filename) + 1];
+#ifdef VMS
+ struct FAB fab;
+ struct NAM nam;
+
+ unsigned long long create;
+ FAT recattr;
+ char ascnamebuff [256];
+
+ ATRDEF atrlst[]
+ = {
+ { ATR$S_CREDATE, ATR$C_CREDATE, &create },
+ { ATR$S_RECATTR, ATR$C_RECATTR, &recattr },
+ { ATR$S_ASCNAME, ATR$C_ASCNAME, &ascnamebuff },
+ { 0, 0, 0}
+ };
+
+ FIBDEF fib;
+ struct dsc$descriptor_fib fibdsc = {sizeof (fib), (void *) &fib};
+
+ struct IOSB iosb;
+
+ long status;
+ unsigned short chan;
+
+ struct vstring file;
+ struct dsc$descriptor_s filedsc
+ = {NAM$C_MAXRSS, DSC$K_DTYPE_T, DSC$K_CLASS_S, (void *) file.string};
+ struct vstring device;
+ struct dsc$descriptor_s devicedsc
+ = {NAM$C_MAXRSS, DSC$K_DTYPE_T, DSC$K_CLASS_S, (void *) device.string};
+ struct vstring result;
+ struct dsc$descriptor_s resultdsc
+ = {NAM$C_MAXRSS, DSC$K_DTYPE_VT, DSC$K_CLASS_VS, (void *) result.string};
+
+ if (strcmp (filename, "<internal>") == 0
+ || strcmp (filename, "<built-in>") == 0)
+ {
+ if (cdt)
+ *cdt = 0;
+
+ if (siz)
+ *siz = 0;
+
+ if (rfo)
+ *rfo = 0;
+
+ if (ver)
+ *ver = 0;
+
+ return 0;
+ }
+
+ strcpy (fullname, dirname);
+ strcat (fullname, filename);
+
+ tryfile = to_vms_file_spec (fullname);
+
+ /* Allocate and initialize a FAB and NAM structures. */
+ fab = cc$rms_fab;
+ nam = cc$rms_nam;
+
+ nam.nam$l_esa = file.string;
+ nam.nam$b_ess = NAM$C_MAXRSS;
+ nam.nam$l_rsa = result.string;
+ nam.nam$b_rss = NAM$C_MAXRSS;
+ fab.fab$l_fna = tryfile;
+ fab.fab$b_fns = strlen (tryfile);
+ fab.fab$l_nam = &nam;
+
+ /* Validate filespec syntax and device existence. */
+ status = SYS$PARSE (&fab, 0, 0);
+ if ((status & 1) != 1)
+ return 1;
+
+ file.string[nam.nam$b_esl] = 0;
+
+ /* Find matching filespec. */
+ status = SYS$SEARCH (&fab, 0, 0);
+ if ((status & 1) != 1)
+ return 1;
+
+ file.string[nam.nam$b_esl] = 0;
+ result.string[result.length=nam.nam$b_rsl] = 0;
+
+ /* Get the device name and assign an IO channel. */
+ strncpy (device.string, nam.nam$l_dev, nam.nam$b_dev);
+ devicedsc.dsc$w_length = nam.nam$b_dev;
+ chan = 0;
+ status = SYS$ASSIGN (&devicedsc, &chan, 0, 0, 0);
+ if ((status & 1) != 1)
+ return 1;
+
+ /* Initialize the FIB and fill in the directory id field. */
+ memset (&fib, 0, sizeof (fib));
+ fib.fib$w_did[0] = nam.nam$w_did[0];
+ fib.fib$w_did[1] = nam.nam$w_did[1];
+ fib.fib$w_did[2] = nam.nam$w_did[2];
+ fib.fib$l_acctl = 0;
+ fib.fib$l_wcc = 0;
+ strcpy (file.string, (strrchr (result.string, ']') + 1));
+ filedsc.dsc$w_length = strlen (file.string);
+ result.string[result.length = 0] = 0;
+
+ /* Open and close the file to fill in the attributes. */
+ status
+ = SYS$QIOW (0, chan, IO$_ACCESS|IO$M_ACCESS, &iosb, 0, 0,
+ &fibdsc, &filedsc, &result.length, &resultdsc, &atrlst, 0);
+ if ((status & 1) != 1)
+ return 1;
+ if ((iosb.status & 1) != 1)
+ return 1;
+
+ result.string[result.length] = 0;
+ status = SYS$QIOW (0, chan, IO$_DEACCESS, &iosb, 0, 0, &fibdsc, 0, 0, 0,
+ &atrlst, 0);
+ if ((status & 1) != 1)
+ return 1;
+ if ((iosb.status & 1) != 1)
+ return 1;
+
+ /* Deassign the channel and exit. */
+ status = SYS$DASSGN (chan);
+ if ((status & 1) != 1)
+ return 1;
+
+ if (cdt) *cdt = create;
+ if (siz) *siz = (512 * 65536 * recattr.fat$w_efblkh) +
+ (512 * (recattr.fat$w_efblkl - 1)) +
+ recattr.fat$w_ffbyte;
+ if (rfo) *rfo = recattr.fat$v_rtype;
+ if (ver) *ver = strtol (strrchr (ascnamebuff, ';') + 1, 0, 10);
+#else /* not VMS */
+
+ struct stat buff;
+ struct tm *ts;
+ long long gmtoff, secs, nsecs;
+
+ strcpy (fullname, dirname);
+ strcat (fullname, filename);
+
+ if ((stat (fullname, &buff)) != 0)
+ return 1;
+
+ if (cdt)
+ {
+ ts = localtime (& buff.st_mtime);
+
+#ifdef HAVE_TM_GMTOFF
+ gmtoff = ts->tm_gmtoff;
+#else
+ {
+ extern long timezone;
+
+ if (ts->tm_isdst == 1)
+ gmtoff = - (timezone - 3600);
+ else
+ gmtoff = - timezone;
+ }
+#endif
+
+#ifdef HAVE_ST_MTIM_TV_SEC
+ secs = buff.st_mtim.tv_sec;
+#else
+ secs = buff.st_mtime;
+#endif
+
+#ifdef HAVE_ST_MTIM_TV_NSEC
+ nsecs = buff.st_mtim.tv_nsec;
+#else
+ nsecs = 0;
+#endif
+
+ /* VMS timestamps are stored in local time to 100 nsec accuracy, but by
+ experiment I found timestamps truncated to (at least) microseconds
+ on an NFS mounted filesystem, hence the adjustment below. DBR. */
+ *cdt = ((secs + gmtoff) * VMS_GRANULARITY_FACTOR)
+ + (nsecs / 1000 * 10) + VMS_EPOCH_OFFSET;
+ }
+
+ if (siz)
+ *siz = buff.st_size;
+
+ if (rfo)
+ *rfo = 2; /* Stream LF format. */
+
+ /* Returning a file version of 0 is never correct for debug info, version 1
+ will be correct if file editing is done only on the Unix side. If editing
+ is done on the VMS side, then its TBD. */
+ if (ver)
+ *ver = 1;
+#endif /* VMS */
+
+ return 0;
+}
+
+bfd_uint64_t
+vms_dwarf2_file_time_name (const char *filename, const char *dirname)
+{
+ long long cdt;
+
+ if (vms_file_stats_name (dirname, filename, &cdt, 0, 0, 0) == 0)
+ return cdt;
+ else
+ return 0;
+}
+
+long
+vms_dwarf2_file_size_name (const char *filename, const char *dirname)
+{
+ long siz;
+
+ if (vms_file_stats_name (dirname, filename, 0, &siz, 0, 0) == 0)
+ return siz;
+ else
+ return 0;
+}
+
+/* VMS debugger needs the filename with version appended. */
+/* Longest filename on VMS is 255 characters. Largest version is 32768. */
+char *
+vms_dwarf2_file_name (const char *filename, const char *dirname)
+{
+ int ver;
+ static char buff [255 + 7];
+
+ vms_file_stats_name (dirname, filename, 0, 0, 0, &ver);
+ snprintf (buff, 255 + 7, "%s;%d", filename, ver);
+ return buff;
+}
diff --git a/gas/config/te-vms.h b/gas/config/te-vms.h
new file mode 100644
index 0000000..34012e1
--- /dev/null
+++ b/gas/config/te-vms.h
@@ -0,0 +1,41 @@
+/* Copyright (C) 2009-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_VMS
+#include "obj-format.h"
+
+extern bfd_uint64_t vms_dwarf2_file_time_name (const char *, const char *);
+extern long vms_dwarf2_file_size_name (const char *, const char *);
+extern char *vms_dwarf2_file_name (const char *, const char *);
+
+/* VMS debugger expects a separator. */
+#define DWARF2_DIR_SHOULD_END_WITH_SEPARATOR 1
+
+/* VMS debugger needs the file timestamp. */
+#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) \
+ vms_dwarf2_file_time_name(FILENAME, DIRNAME)
+
+/* VMS debugger needs the file size. */
+#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) \
+ vms_dwarf2_file_size_name(FILENAME, DIRNAME)
+
+/* VMS debugger needs the filename with version appended. */
+/* Longest filename on VMS is 255 characters. Largest version is 32768. */
+#define DWARF2_FILE_NAME(FILENAME,DIRNAME) \
+ vms_dwarf2_file_name(FILENAME, DIRNAME)
diff --git a/gas/config/te-vxworks.h b/gas/config/te-vxworks.h
new file mode 100644
index 0000000..e22b1a3
--- /dev/null
+++ b/gas/config/te-vxworks.h
@@ -0,0 +1,30 @@
+/* te-vxworks.h -- VxWorks target environment declarations.
+ Copyright (C) 2005-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_VXWORKS 1
+#define LOCAL_LABELS_DOLLAR 1
+#define LOCAL_LABELS_FB 1
+
+/* these define interfaces */
+#ifdef OBJ_HEADER
+#include OBJ_HEADER
+#else
+#include "obj-format.h"
+#endif
diff --git a/gas/config/te-wince-pe.h b/gas/config/te-wince-pe.h
new file mode 100644
index 0000000..a2049ba
--- /dev/null
+++ b/gas/config/te-wince-pe.h
@@ -0,0 +1,21 @@
+/* Copyright (C) 2007-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 3,
+ or (at your option) any later version.
+
+ GAS is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#define TE_WINCE
+#include "te-pe.h"
diff --git a/gas/config/vax-inst.h b/gas/config/vax-inst.h
new file mode 100644
index 0000000..c6ce102
--- /dev/null
+++ b/gas/config/vax-inst.h
@@ -0,0 +1,79 @@
+/* vax-inst.h - GNU - Part of vax.c
+ Copyright (C) 1987-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */
+
+/*
+ * This is part of vax-ins-parse.c & friends.
+ * We want to parse a vax instruction text into a tree defined here.
+ */
+
+#define VIT_MAX_OPERANDS (6) /* maximum number of operands in one */
+/* single vax instruction */
+
+struct vop /* vax instruction operand */
+{
+ short int vop_ndx; /* -1, or index register. eg 7=[R7] */
+ short int vop_reg; /* -1, or register number. eg @I^#=0xF */
+ /* Helps distinguish "abs" from "abs(PC)". */
+ short int vop_mode; /* addressing mode 4 bits. eg I^#=0x9 */
+ char vop_short; /* operand displacement length as written */
+ /* ' '=none, "bilsw"=B^I^L^S^W^. */
+ char vop_access; /* 'b'branch ' 'no-instruction 'amrvw'norm */
+ char vop_width; /* Operand width, one of "bdfghloqw" */
+ const char *vop_warn; /* warning message of this operand, if any */
+ const char *vop_error; /* say if operand is inappropriate */
+ char *vop_expr_begin; /* Unparsed expression, 1st char ... */
+ char *vop_expr_end; /* ... last char. */
+ unsigned char vop_nbytes; /* number of bytes in datum */
+};
+
+typedef long vax_opcodeT; /* For initialising array of opcodes */
+/* Some synthetic opcodes > 16 bits! */
+
+#define VIT_OPCODE_SYNTHETIC 0x80000000 /* Not real hardware instruction. */
+#define VIT_OPCODE_SPECIAL 0x40000000 /* Not normal branch optimising. */
+/* Never set without ..._SYNTHETIC */
+
+#define VAX_WIDTH_UNCONDITIONAL_JUMP '-' /* These are encoded into */
+#define VAX_WIDTH_CONDITIONAL_JUMP '?' /* vop_width when vop_access=='b' */
+#define VAX_WIDTH_WORD_JUMP '!' /* and VIT_OPCODE_SYNTHETIC set. */
+#define VAX_WIDTH_BYTE_JUMP ':' /* */
+
+#define VAX_JSB (0x16) /* Jump to subroutine */
+#define VAX_JMP (0x17) /* Useful for branch optimising. Jump instr*/
+#define VAX_PC_RELATIVE_MODE (0xef) /* Use it after VAX_JMP */
+#define VAX_ABSOLUTE_MODE (0x9F)/* Use as @#... */
+#define VAX_BRB (0x11) /* Canonical branch. */
+#define VAX_BRW (0x31) /* Another canonical branch */
+#define VAX_CALLS (0xFB) /* Call with arg list on stack */
+#define VAX_CALLG (0xFA) /* Call with arg list in memory */
+#define VAX_WIDEN_WORD (0x20) /* Add this to byte branch to get word br. */
+#define VAX_WIDEN_LONG (0x6) /* Add this to byte branch to get long jmp.*/
+/* Needs VAX_PC_RELATIVE_MODE byte after it*/
+
+struct vit /* vax instruction tree */
+{
+ /* vit_opcode is char[] for portability. */
+ char vit_opcode[sizeof (vax_opcodeT)];
+ unsigned char vit_opcode_nbytes; /* How long is _opcode? (chars) */
+ unsigned char vit_operands; /* */
+ struct vop vit_operand[VIT_MAX_OPERANDS]; /* operands */
+ const char *vit_error; /* "" or error text */
+};
+
+/* end of vax-inst.h */
diff --git a/gas/config/xtensa-istack.h b/gas/config/xtensa-istack.h
new file mode 100644
index 0000000..d9aee8f
--- /dev/null
+++ b/gas/config/xtensa-istack.h
@@ -0,0 +1,105 @@
+/* Declarations for stacks of tokenized Xtensa instructions.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to the Free
+ Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
+ 02110-1301, USA. */
+
+#ifndef XTENSA_ISTACK_H
+#define XTENSA_ISTACK_H
+
+#include "xtensa-isa.h"
+
+#define MAX_ISTACK 12
+#define MAX_INSN_ARGS 64
+
+enum itype_enum
+{
+ ITYPE_INSN,
+ ITYPE_LITERAL,
+ ITYPE_LABEL
+};
+
+
+/* Literals have 1 token and no opcode.
+ Labels have 1 token and no opcode. */
+
+typedef struct tinsn_struct
+{
+ enum itype_enum insn_type;
+
+ xtensa_opcode opcode; /* Literals have an invalid opcode. */
+ bfd_boolean is_specific_opcode;
+ bfd_boolean keep_wide;
+ int ntok;
+ expressionS tok[MAX_INSN_ARGS];
+ bfd_boolean loc_directive_seen;
+ struct dwarf2_line_info debug_line;
+
+ /* This field is used for two types of special pseudo ops:
+ 1. TLS-related operations. Eg: callx8.tls
+ 2. j.l label, a2
+
+ For the tls-related operations, it will hold a tls-related opcode
+ and info to be used in a fixup. For j.l it will hold a
+ register to be used during relaxation. */
+ expressionS extra_arg;
+
+ /* Filled out by relaxation_requirements: */
+ enum xtensa_relax_statesE subtype;
+ int literal_space;
+
+ /* Filled out by vinsn_to_insnbuf: */
+ symbolS *symbol;
+ offsetT offset;
+ fragS *literal_frag;
+} TInsn;
+
+
+/* tinsn_stack: This is a stack of instructions to be placed. */
+
+typedef struct tinsn_stack
+{
+ int ninsn;
+ TInsn insn[MAX_ISTACK];
+} IStack;
+
+
+void istack_init (IStack *);
+bfd_boolean istack_empty (IStack *);
+bfd_boolean istack_full (IStack *);
+TInsn *istack_top (IStack *);
+void istack_push (IStack *, TInsn *);
+TInsn *istack_push_space (IStack *);
+void istack_pop (IStack *);
+
+/* TInsn utilities. */
+void tinsn_init (TInsn *);
+
+
+/* vliw_insn: bundles of TInsns. */
+
+typedef struct vliw_insn
+{
+ xtensa_format format;
+ int num_slots;
+ unsigned int inside_bundle;
+ TInsn slots[MAX_SLOTS];
+ xtensa_insnbuf insnbuf;
+ xtensa_insnbuf slotbuf[MAX_SLOTS];
+} vliw_insn;
+
+#endif /* !XTENSA_ISTACK_H */
diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c
new file mode 100644
index 0000000..df8a55a
--- /dev/null
+++ b/gas/config/xtensa-relax.c
@@ -0,0 +1,1924 @@
+/* Table of relaxations for Xtensa assembly.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+/* This file contains the code for generating runtime data structures
+ for relaxation pattern matching from statically specified strings.
+ Each action contains an instruction pattern to match and
+ preconditions for the match as well as an expansion if the pattern
+ matches. The preconditions can specify that two operands are the
+ same or an operand is a specific constant or register. The expansion
+ uses the bound variables from the pattern to specify that specific
+ operands from the pattern should be used in the result.
+
+ The code determines whether the condition applies to a constant or
+ a register depending on the type of the operand. You may get
+ unexpected results if you don't match the rule against the operand
+ type correctly.
+
+ The patterns match a language like:
+
+ INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )* ( '?' OPTIONPRED )*
+ INSN_TEMPL ::= OPCODE ' ' [ OPERAND (',' OPERAND)* ]
+ OPCODE ::= id
+ OPERAND ::= CONSTANT | VARIABLE | SPECIALFN '(' VARIABLE ')'
+ SPECIALFN ::= 'HI24S' | 'F32MINUS' | 'LOW8'
+ | 'HI16' | 'LOW16'
+ VARIABLE ::= '%' id
+ PRECOND ::= OPERAND CMPOP OPERAND
+ CMPOP ::= '==' | '!='
+ OPTIONPRED ::= OPTIONNAME ('+' OPTIONNAME)
+ OPTIONNAME ::= '"' id '"'
+
+ The replacement language
+ INSN_REPL ::= INSN_LABEL_LIT ( ';' INSN_LABEL_LIT )*
+ INSN_LABEL_LIT ::= INSN_TEMPL
+ | 'LABEL'
+ | 'LITERAL' VARIABLE
+
+ The operands in a PRECOND must be constants or variables bound by
+ the INSN_PATTERN.
+
+ The configuration options define a predicate on the availability of
+ options which must be TRUE for this rule to be valid. Examples are
+ requiring "density" for replacements with density instructions,
+ requiring "const16" for replacements that require const16
+ instructions, etc. The names are interpreted by the assembler to a
+ truth value for a particular frag.
+
+ The operands in the INSN_REPL must be constants, variables bound in
+ the associated INSN_PATTERN, special variables that are bound in
+ the INSN_REPL by LABEL or LITERAL definitions, or special value
+ manipulation functions.
+
+ A simple example of a replacement pattern:
+ {"movi.n %as,%imm", "movi %as,%imm"} would convert the narrow
+ movi.n instruction to the wide movi instruction.
+
+ A more complex example of a branch around:
+ {"beqz %as,%label", "bnez %as,%LABEL;j %label;LABEL"}
+ would convert a branch to a negated branch to the following instruction
+ with a jump to the original label.
+
+ An Xtensa-specific example that generates a literal:
+ {"movi %at,%imm", "LITERAL %imm; l32r %at,%LITERAL"}
+ will convert a movi instruction to an l32r of a literal
+ literal defined in the literal pool.
+
+ Even more complex is a conversion of a load with immediate offset
+ to a load of a freshly generated literal, an explicit add and
+ a load with 0 offset. This transformation is only valid, though
+ when the first and second operands are not the same as specified
+ by the "| %at!=%as" precondition clause.
+ {"l32i %at,%as,%imm | %at!=%as",
+ "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"}
+
+ There is special case for loop instructions here, but because we do
+ not currently have the ability to represent the difference of two
+ symbols, the conversion requires special code in the assembler to
+ write the operands of the addi/addmi pair representing the
+ difference of the old and new loop end label. */
+
+#include "as.h"
+#include "xtensa-isa.h"
+#include "xtensa-relax.h"
+#include <stddef.h>
+#include "xtensa-config.h"
+
+#ifndef XCHAL_HAVE_WIDE_BRANCHES
+#define XCHAL_HAVE_WIDE_BRANCHES 0
+#endif
+
+/* Imported from bfd. */
+extern xtensa_isa xtensa_default_isa;
+
+/* The opname_list is a small list of names that we use for opcode and
+ operand variable names to simplify ownership of these commonly used
+ strings. Strings entered in the table can be compared by pointer
+ equality. */
+
+typedef struct opname_list_struct opname_list;
+typedef opname_list opname_e;
+
+struct opname_list_struct
+{
+ char *opname;
+ opname_list *next;
+};
+
+static opname_list *local_opnames = NULL;
+
+
+/* The "opname_map" and its element structure "opname_map_e" are used
+ for binding an operand number to a name or a constant. */
+
+typedef struct opname_map_e_struct opname_map_e;
+typedef struct opname_map_struct opname_map;
+
+struct opname_map_e_struct
+{
+ const char *operand_name; /* If null, then use constant_value. */
+ int operand_num;
+ unsigned constant_value;
+ opname_map_e *next;
+};
+
+struct opname_map_struct
+{
+ opname_map_e *head;
+ opname_map_e **tail;
+};
+
+/* The "precond_list" and its element structure "precond_e" represents
+ explicit preconditions comparing operand variables and constants.
+ In the "precond_e" structure, a variable is identified by the name
+ in the "opname" field. If that field is NULL, then the operand
+ is the constant in field "opval". */
+
+typedef struct precond_e_struct precond_e;
+typedef struct precond_list_struct precond_list;
+
+struct precond_e_struct
+{
+ const char *opname1;
+ unsigned opval1;
+ CmpOp cmpop;
+ const char *opname2;
+ unsigned opval2;
+ precond_e *next;
+};
+
+struct precond_list_struct
+{
+ precond_e *head;
+ precond_e **tail;
+};
+
+
+/* The insn_templ represents the INSN_TEMPL instruction template. It
+ is an opcode name with a list of operands. These are used for
+ instruction patterns and replacement patterns. */
+
+typedef struct insn_templ_struct insn_templ;
+struct insn_templ_struct
+{
+ const char *opcode_name;
+ opname_map operand_map;
+};
+
+
+/* The insn_pattern represents an INSN_PATTERN instruction pattern.
+ It is an instruction template with preconditions that specify when
+ it actually matches a given instruction. */
+
+typedef struct insn_pattern_struct insn_pattern;
+struct insn_pattern_struct
+{
+ insn_templ t;
+ precond_list preconds;
+ ReqOptionList *options;
+};
+
+
+/* The "insn_repl" and associated element structure "insn_repl_e"
+ instruction replacement list is a list of
+ instructions/LITERALS/LABELS with constant operands or operands
+ with names bound to the operand names in the associated pattern. */
+
+typedef struct insn_repl_e_struct insn_repl_e;
+struct insn_repl_e_struct
+{
+ insn_templ t;
+ insn_repl_e *next;
+};
+
+typedef struct insn_repl_struct insn_repl;
+struct insn_repl_struct
+{
+ insn_repl_e *head;
+ insn_repl_e **tail;
+};
+
+
+/* The split_rec is a vector of allocated char * pointers. */
+
+typedef struct split_rec_struct split_rec;
+struct split_rec_struct
+{
+ char **vec;
+ int count;
+};
+
+/* The "string_pattern_pair" is a set of pairs containing instruction
+ patterns and replacement strings. */
+
+typedef struct string_pattern_pair_struct string_pattern_pair;
+struct string_pattern_pair_struct
+{
+ const char *pattern;
+ const char *replacement;
+};
+
+
+/* The widen_spec_list is a list of valid substitutions that generate
+ wider representations. These are generally used to specify
+ replacements for instructions whose immediates do not fit their
+ encodings. A valid transition may require multiple steps of
+ one-to-one instruction replacements with a final multiple
+ instruction replacement. As an example, here are the transitions
+ required to replace an 'addi.n' with an 'addi', 'addmi'.
+
+ addi.n a4, 0x1010
+ => addi a4, 0x1010
+ => addmi a4, 0x1010
+ => addmi a4, 0x1000, addi a4, 0x10.
+
+ See the comments in xg_assembly_relax for some important details
+ regarding how these chains must be built. */
+
+static string_pattern_pair widen_spec_list[] =
+{
+ {"add.n %ar,%as,%at ? IsaUseDensityInstruction", "add %ar,%as,%at"},
+ {"addi.n %ar,%as,%imm ? IsaUseDensityInstruction", "addi %ar,%as,%imm"},
+ {"beqz.n %as,%label ? IsaUseDensityInstruction", "beqz %as,%label"},
+ {"bnez.n %as,%label ? IsaUseDensityInstruction", "bnez %as,%label"},
+ {"l32i.n %at,%as,%imm ? IsaUseDensityInstruction", "l32i %at,%as,%imm"},
+ {"mov.n %at,%as ? IsaUseDensityInstruction", "or %at,%as,%as"},
+ {"movi.n %as,%imm ? IsaUseDensityInstruction", "movi %as,%imm"},
+ {"nop.n ? IsaUseDensityInstruction ? realnop", "nop"},
+ {"nop.n ? IsaUseDensityInstruction ? no-realnop", "or 1,1,1"},
+ {"ret.n %as ? IsaUseDensityInstruction", "ret %as"},
+ {"retw.n %as ? IsaUseDensityInstruction", "retw %as"},
+ {"s32i.n %at,%as,%imm ? IsaUseDensityInstruction", "s32i %at,%as,%imm"},
+ {"srli %at,%as,%imm", "extui %at,%as,%imm,F32MINUS(%imm)"},
+ {"slli %ar,%as,0", "or %ar,%as,%as"},
+
+ /* Widening with literals or const16. */
+ {"movi %at,%imm ? IsaUseL32R ",
+ "LITERAL %imm; l32r %at,%LITERAL"},
+ {"movi %at,%imm ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm)"},
+
+ {"addi %ar,%as,%imm", "addmi %ar,%as,%imm"},
+ /* LOW8 is the low 8 bits of the Immed
+ MID8S is the middle 8 bits of the Immed */
+ {"addmi %ar,%as,%imm", "addmi %ar,%as,HI24S(%imm); addi %ar,%ar,LOW8(%imm)"},
+
+ /* In the end convert to either an l32r or const16. */
+ {"addmi %ar,%as,%imm | %ar!=%as ? IsaUseL32R",
+ "LITERAL %imm; l32r %ar,%LITERAL; add %ar,%as,%ar"},
+ {"addmi %ar,%as,%imm | %ar!=%as ? IsaUseConst16",
+ "const16 %ar,HI16U(%imm); const16 %ar,LOW16U(%imm); add %ar,%as,%ar"},
+
+ /* Widening the load instructions with too-large immediates */
+ {"l8ui %at,%as,%imm | %at!=%as ? IsaUseL32R",
+ "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l8ui %at,%at,0"},
+ {"l16si %at,%as,%imm | %at!=%as ? IsaUseL32R",
+ "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l16si %at,%at,0"},
+ {"l16ui %at,%as,%imm | %at!=%as ? IsaUseL32R",
+ "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l16ui %at,%at,0"},
+ {"l32i %at,%as,%imm | %at!=%as ? IsaUseL32R",
+ "LITERAL %imm; l32r %at,%LITERAL; add %at,%at,%as; l32i %at,%at,0"},
+
+ /* Widening load instructions with const16s. */
+ {"l8ui %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l8ui %at,%at,0"},
+ {"l16si %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l16si %at,%at,0"},
+ {"l16ui %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l16ui %at,%at,0"},
+ {"l32i %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l32i %at,%at,0"},
+
+ /* This is only PART of the loop instruction. In addition,
+ hardcoded into its use is a modification of the final operand in
+ the instruction in bytes 9 and 12. */
+ {"loop %as,%label | %as!=1 ? IsaUseLoops",
+ "loop %as,%LABEL;"
+ "rsr.lend %as;" /* LEND */
+ "wsr.lbeg %as;" /* LBEG */
+ "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */
+ "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */
+ "wsr.lend %as;"
+ "isync;"
+ "rsr.lcount %as;" /* LCOUNT */
+ "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */
+ "LABEL"},
+ {"loopgtz %as,%label | %as!=1 ? IsaUseLoops",
+ "beqz %as,%label;"
+ "bltz %as,%label;"
+ "loopgtz %as,%LABEL;"
+ "rsr.lend %as;" /* LEND */
+ "wsr.lbeg %as;" /* LBEG */
+ "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */
+ "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */
+ "wsr.lend %as;"
+ "isync;"
+ "rsr.lcount %as;" /* LCOUNT */
+ "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */
+ "LABEL"},
+ {"loopnez %as,%label | %as!=1 ? IsaUseLoops",
+ "beqz %as,%label;"
+ "loopnez %as,%LABEL;"
+ "rsr.lend %as;" /* LEND */
+ "wsr.lbeg %as;" /* LBEG */
+ "addi %as, %as, 0;" /* lo8(%label-%LABEL1) */
+ "addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */
+ "wsr.lend %as;"
+ "isync;"
+ "rsr.lcount %as;" /* LCOUNT */
+ "addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */
+ "LABEL"},
+
+ /* Relaxing to wide branches. Order is important here. With wide
+ branches, there is more than one correct relaxation for an
+ out-of-range branch. Put the wide branch relaxations first in the
+ table since they are more efficient than the branch-around
+ relaxations. */
+
+ {"beqz %as,%label ? IsaUseWideBranches", "WIDE.beqz %as,%label"},
+ {"bnez %as,%label ? IsaUseWideBranches", "WIDE.bnez %as,%label"},
+ {"bgez %as,%label ? IsaUseWideBranches", "WIDE.bgez %as,%label"},
+ {"bltz %as,%label ? IsaUseWideBranches", "WIDE.bltz %as,%label"},
+ {"beqi %as,%imm,%label ? IsaUseWideBranches", "WIDE.beqi %as,%imm,%label"},
+ {"bnei %as,%imm,%label ? IsaUseWideBranches", "WIDE.bnei %as,%imm,%label"},
+ {"bgei %as,%imm,%label ? IsaUseWideBranches", "WIDE.bgei %as,%imm,%label"},
+ {"blti %as,%imm,%label ? IsaUseWideBranches", "WIDE.blti %as,%imm,%label"},
+ {"bgeui %as,%imm,%label ? IsaUseWideBranches", "WIDE.bgeui %as,%imm,%label"},
+ {"bltui %as,%imm,%label ? IsaUseWideBranches", "WIDE.bltui %as,%imm,%label"},
+ {"bbci %as,%imm,%label ? IsaUseWideBranches", "WIDE.bbci %as,%imm,%label"},
+ {"bbsi %as,%imm,%label ? IsaUseWideBranches", "WIDE.bbsi %as,%imm,%label"},
+ {"beq %as,%at,%label ? IsaUseWideBranches", "WIDE.beq %as,%at,%label"},
+ {"bne %as,%at,%label ? IsaUseWideBranches", "WIDE.bne %as,%at,%label"},
+ {"bge %as,%at,%label ? IsaUseWideBranches", "WIDE.bge %as,%at,%label"},
+ {"blt %as,%at,%label ? IsaUseWideBranches", "WIDE.blt %as,%at,%label"},
+ {"bgeu %as,%at,%label ? IsaUseWideBranches", "WIDE.bgeu %as,%at,%label"},
+ {"bltu %as,%at,%label ? IsaUseWideBranches", "WIDE.bltu %as,%at,%label"},
+ {"bany %as,%at,%label ? IsaUseWideBranches", "WIDE.bany %as,%at,%label"},
+ {"bnone %as,%at,%label ? IsaUseWideBranches", "WIDE.bnone %as,%at,%label"},
+ {"ball %as,%at,%label ? IsaUseWideBranches", "WIDE.ball %as,%at,%label"},
+ {"bnall %as,%at,%label ? IsaUseWideBranches", "WIDE.bnall %as,%at,%label"},
+ {"bbc %as,%at,%label ? IsaUseWideBranches", "WIDE.bbc %as,%at,%label"},
+ {"bbs %as,%at,%label ? IsaUseWideBranches", "WIDE.bbs %as,%at,%label"},
+
+ /* Widening branch comparisons eq/ne to zero. Prefer relaxing to narrow
+ branches if the density option is available. */
+ {"beqz %as,%label ? IsaUseDensityInstruction", "bnez.n %as,%LABEL;j %label;LABEL"},
+ {"bnez %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%LABEL;j %label;LABEL"},
+ {"beqz %as,%label", "bnez %as,%LABEL;j %label;LABEL"},
+ {"bnez %as,%label", "beqz %as,%LABEL;j %label;LABEL"},
+ {"WIDE.beqz %as,%label ? IsaUseDensityInstruction", "bnez.n %as,%LABEL;j %label;LABEL"},
+ {"WIDE.bnez %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%LABEL;j %label;LABEL"},
+ {"WIDE.beqz %as,%label", "bnez %as,%LABEL;j %label;LABEL"},
+ {"WIDE.bnez %as,%label", "beqz %as,%LABEL;j %label;LABEL"},
+
+ /* Widening expect-taken branches. */
+ {"beqzt %as,%label ? IsaUsePredictedBranches", "bnez %as,%LABEL;j %label;LABEL"},
+ {"bnezt %as,%label ? IsaUsePredictedBranches", "beqz %as,%LABEL;j %label;LABEL"},
+ {"beqt %as,%at,%label ? IsaUsePredictedBranches", "bne %as,%at,%LABEL;j %label;LABEL"},
+ {"bnet %as,%at,%label ? IsaUsePredictedBranches", "beq %as,%at,%LABEL;j %label;LABEL"},
+
+ /* Widening branches from the Xtensa boolean option. */
+ {"bt %bs,%label ? IsaUseBooleans", "bf %bs,%LABEL;j %label;LABEL"},
+ {"bf %bs,%label ? IsaUseBooleans", "bt %bs,%LABEL;j %label;LABEL"},
+
+ /* Other branch-around-jump widenings. */
+ {"bgez %as,%label", "bltz %as,%LABEL;j %label;LABEL"},
+ {"bltz %as,%label", "bgez %as,%LABEL;j %label;LABEL"},
+ {"beqi %as,%imm,%label", "bnei %as,%imm,%LABEL;j %label;LABEL"},
+ {"bnei %as,%imm,%label", "beqi %as,%imm,%LABEL;j %label;LABEL"},
+ {"bgei %as,%imm,%label", "blti %as,%imm,%LABEL;j %label;LABEL"},
+ {"blti %as,%imm,%label", "bgei %as,%imm,%LABEL;j %label;LABEL"},
+ {"bgeui %as,%imm,%label", "bltui %as,%imm,%LABEL;j %label;LABEL"},
+ {"bltui %as,%imm,%label", "bgeui %as,%imm,%LABEL;j %label;LABEL"},
+ {"bbci %as,%imm,%label", "bbsi %as,%imm,%LABEL;j %label;LABEL"},
+ {"bbsi %as,%imm,%label", "bbci %as,%imm,%LABEL;j %label;LABEL"},
+ {"beq %as,%at,%label", "bne %as,%at,%LABEL;j %label;LABEL"},
+ {"bne %as,%at,%label", "beq %as,%at,%LABEL;j %label;LABEL"},
+ {"bge %as,%at,%label", "blt %as,%at,%LABEL;j %label;LABEL"},
+ {"blt %as,%at,%label", "bge %as,%at,%LABEL;j %label;LABEL"},
+ {"bgeu %as,%at,%label", "bltu %as,%at,%LABEL;j %label;LABEL"},
+ {"bltu %as,%at,%label", "bgeu %as,%at,%LABEL;j %label;LABEL"},
+ {"bany %as,%at,%label", "bnone %as,%at,%LABEL;j %label;LABEL"},
+ {"bnone %as,%at,%label", "bany %as,%at,%LABEL;j %label;LABEL"},
+ {"ball %as,%at,%label", "bnall %as,%at,%LABEL;j %label;LABEL"},
+ {"bnall %as,%at,%label", "ball %as,%at,%LABEL;j %label;LABEL"},
+ {"bbc %as,%at,%label", "bbs %as,%at,%LABEL;j %label;LABEL"},
+ {"bbs %as,%at,%label", "bbc %as,%at,%LABEL;j %label;LABEL"},
+
+ {"WIDE.bgez %as,%label", "bltz %as,%LABEL;j %label;LABEL"},
+ {"WIDE.bltz %as,%label", "bgez %as,%LABEL;j %label;LABEL"},
+ {"WIDE.beqi %as,%imm,%label", "bnei %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.bnei %as,%imm,%label", "beqi %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.bgei %as,%imm,%label", "blti %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.blti %as,%imm,%label", "bgei %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.bgeui %as,%imm,%label", "bltui %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.bltui %as,%imm,%label", "bgeui %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.bbci %as,%imm,%label", "bbsi %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.bbsi %as,%imm,%label", "bbci %as,%imm,%LABEL;j %label;LABEL"},
+ {"WIDE.beq %as,%at,%label", "bne %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bne %as,%at,%label", "beq %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bge %as,%at,%label", "blt %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.blt %as,%at,%label", "bge %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bgeu %as,%at,%label", "bltu %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bltu %as,%at,%label", "bgeu %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bany %as,%at,%label", "bnone %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bnone %as,%at,%label", "bany %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.ball %as,%at,%label", "bnall %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bnall %as,%at,%label", "ball %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bbc %as,%at,%label", "bbs %as,%at,%LABEL;j %label;LABEL"},
+ {"WIDE.bbs %as,%at,%label", "bbc %as,%at,%LABEL;j %label;LABEL"},
+
+ /* Expanding calls with literals. */
+ {"call0 %label,%ar0 ? IsaUseL32R",
+ "LITERAL %label; l32r a0,%LITERAL; callx0 a0,%ar0"},
+ {"call4 %label,%ar4 ? IsaUseL32R",
+ "LITERAL %label; l32r a4,%LITERAL; callx4 a4,%ar4"},
+ {"call8 %label,%ar8 ? IsaUseL32R",
+ "LITERAL %label; l32r a8,%LITERAL; callx8 a8,%ar8"},
+ {"call12 %label,%ar12 ? IsaUseL32R",
+ "LITERAL %label; l32r a12,%LITERAL; callx12 a12,%ar12"},
+
+ /* Expanding calls with const16. */
+ {"call0 %label,%ar0 ? IsaUseConst16",
+ "const16 a0,HI16U(%label); const16 a0,LOW16U(%label); callx0 a0,%ar0"},
+ {"call4 %label,%ar4 ? IsaUseConst16",
+ "const16 a4,HI16U(%label); const16 a4,LOW16U(%label); callx4 a4,%ar4"},
+ {"call8 %label,%ar8 ? IsaUseConst16",
+ "const16 a8,HI16U(%label); const16 a8,LOW16U(%label); callx8 a8,%ar8"},
+ {"call12 %label,%ar12 ? IsaUseConst16",
+ "const16 a12,HI16U(%label); const16 a12,LOW16U(%label); callx12 a12,%ar12"},
+
+ /* Expanding j.l with literals. */
+ {"j %label ? FREEREG ? IsaUseL32R",
+ "LITERAL %label; l32r FREEREG,%LITERAL; jx FREEREG"},
+ /* Expanding j.l with const16. */
+ {"j %label ? FREEREG ? IsaUseConst16",
+ "const16 FREEREG,HI16U(%label); const16 FREEREG,LOW16U(%label); jx FREEREG"},
+};
+
+#define WIDEN_COUNT (sizeof (widen_spec_list) / sizeof (string_pattern_pair))
+
+
+/* The simplify_spec_list specifies simplifying transformations that
+ will reduce the instruction width or otherwise simplify an
+ instruction. These are usually applied before relaxation in the
+ assembler. It is always legal to simplify. Even for "addi as, 0",
+ the "addi.n as, 0" will eventually be widened back to an "addi 0"
+ after the widening table is applied. Note: The usage of this table
+ has changed somewhat so that it is entirely specific to "narrowing"
+ instructions to use the density option. This table is not used at
+ all when the density option is not available. */
+
+string_pattern_pair simplify_spec_list[] =
+{
+ {"add %ar,%as,%at ? IsaUseDensityInstruction", "add.n %ar,%as,%at"},
+ {"addi.n %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+ {"addi %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+ {"addi %ar,%as,%imm ? IsaUseDensityInstruction", "addi.n %ar,%as,%imm"},
+ {"addmi %ar,%as,%imm ? IsaUseDensityInstruction", "addi.n %ar,%as,%imm"},
+ {"beqz %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%label"},
+ {"bnez %as,%label ? IsaUseDensityInstruction", "bnez.n %as,%label"},
+ {"l32i %at,%as,%imm ? IsaUseDensityInstruction", "l32i.n %at,%as,%imm"},
+ {"movi %as,%imm ? IsaUseDensityInstruction", "movi.n %as,%imm"},
+ {"nop ? realnop ? IsaUseDensityInstruction", "nop.n"},
+ {"or %ar,%as,%at | %ar==%as | %as==%at ? IsaUseDensityInstruction", "nop.n"},
+ {"or %ar,%as,%at | %ar!=%as | %as==%at ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+ {"ret %as ? IsaUseDensityInstruction", "ret.n %as"},
+ {"retw %as ? IsaUseDensityInstruction", "retw.n %as"},
+ {"s32i %at,%as,%imm ? IsaUseDensityInstruction", "s32i.n %at,%as,%imm"},
+ {"slli %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"}
+};
+
+#define SIMPLIFY_COUNT \
+ (sizeof (simplify_spec_list) / sizeof (string_pattern_pair))
+
+
+/* Externally visible functions. */
+
+extern bfd_boolean xg_has_userdef_op_fn (OpType);
+extern long xg_apply_userdef_op_fn (OpType, long);
+
+
+static void
+append_transition (TransitionTable *tt,
+ xtensa_opcode opcode,
+ TransitionRule *t,
+ transition_cmp_fn cmp)
+{
+ TransitionList *tl = (TransitionList *) xmalloc (sizeof (TransitionList));
+ TransitionList *prev;
+ TransitionList **t_p;
+ gas_assert (tt != NULL);
+ gas_assert (opcode < tt->num_opcodes);
+
+ prev = tt->table[opcode];
+ tl->rule = t;
+ tl->next = NULL;
+ if (prev == NULL)
+ {
+ tt->table[opcode] = tl;
+ return;
+ }
+
+ for (t_p = &tt->table[opcode]; (*t_p) != NULL; t_p = &(*t_p)->next)
+ {
+ if (cmp && cmp (t, (*t_p)->rule) < 0)
+ {
+ /* Insert it here. */
+ tl->next = *t_p;
+ *t_p = tl;
+ return;
+ }
+ }
+ (*t_p) = tl;
+}
+
+
+static void
+append_condition (TransitionRule *tr, Precondition *cond)
+{
+ PreconditionList *pl =
+ (PreconditionList *) xmalloc (sizeof (PreconditionList));
+ PreconditionList *prev = tr->conditions;
+ PreconditionList *nxt;
+
+ pl->precond = cond;
+ pl->next = NULL;
+ if (prev == NULL)
+ {
+ tr->conditions = pl;
+ return;
+ }
+ nxt = prev->next;
+ while (nxt != NULL)
+ {
+ prev = nxt;
+ nxt = nxt->next;
+ }
+ prev->next = pl;
+}
+
+
+static void
+append_value_condition (TransitionRule *tr,
+ CmpOp cmp,
+ unsigned op1,
+ unsigned op2)
+{
+ Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition));
+
+ cond->cmp = cmp;
+ cond->op_num = op1;
+ cond->typ = OP_OPERAND;
+ cond->op_data = op2;
+ append_condition (tr, cond);
+}
+
+
+static void
+append_constant_value_condition (TransitionRule *tr,
+ CmpOp cmp,
+ unsigned op1,
+ unsigned cnst)
+{
+ Precondition *cond = (Precondition *) xmalloc (sizeof (Precondition));
+
+ cond->cmp = cmp;
+ cond->op_num = op1;
+ cond->typ = OP_CONSTANT;
+ cond->op_data = cnst;
+ append_condition (tr, cond);
+}
+
+
+static void
+append_build_insn (TransitionRule *tr, BuildInstr *bi)
+{
+ BuildInstr *prev = tr->to_instr;
+ BuildInstr *nxt;
+
+ bi->next = NULL;
+ if (prev == NULL)
+ {
+ tr->to_instr = bi;
+ return;
+ }
+ nxt = prev->next;
+ while (nxt != 0)
+ {
+ prev = nxt;
+ nxt = prev->next;
+ }
+ prev->next = bi;
+}
+
+
+static void
+append_op (BuildInstr *bi, BuildOp *b_op)
+{
+ BuildOp *prev = bi->ops;
+ BuildOp *nxt;
+
+ if (prev == NULL)
+ {
+ bi->ops = b_op;
+ return;
+ }
+ nxt = prev->next;
+ while (nxt != NULL)
+ {
+ prev = nxt;
+ nxt = nxt->next;
+ }
+ prev->next = b_op;
+}
+
+
+static void
+append_literal_op (BuildInstr *bi, unsigned op1, unsigned src_op)
+{
+ BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));
+
+ b_op->op_num = op1;
+ b_op->typ = OP_LITERAL;
+ b_op->op_data = src_op;
+ b_op->next = NULL;
+ append_op (bi, b_op);
+}
+
+
+static void
+append_label_op (BuildInstr *bi, unsigned op1)
+{
+ BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));
+
+ b_op->op_num = op1;
+ b_op->typ = OP_LABEL;
+ b_op->op_data = 0;
+ b_op->next = NULL;
+ append_op (bi, b_op);
+}
+
+
+static void
+append_constant_op (BuildInstr *bi, unsigned op1, unsigned cnst)
+{
+ BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));
+
+ b_op->op_num = op1;
+ b_op->typ = OP_CONSTANT;
+ b_op->op_data = cnst;
+ b_op->next = NULL;
+ append_op (bi, b_op);
+}
+
+
+static void
+append_field_op (BuildInstr *bi, unsigned op1, unsigned src_op)
+{
+ BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));
+
+ b_op->op_num = op1;
+ b_op->typ = OP_OPERAND;
+ b_op->op_data = src_op;
+ b_op->next = NULL;
+ append_op (bi, b_op);
+}
+
+
+/* These could be generated but are not currently. */
+
+static void
+append_user_fn_field_op (BuildInstr *bi,
+ unsigned op1,
+ OpType typ,
+ unsigned src_op)
+{
+ BuildOp *b_op = (BuildOp *) xmalloc (sizeof (BuildOp));
+
+ b_op->op_num = op1;
+ b_op->typ = typ;
+ b_op->op_data = src_op;
+ b_op->next = NULL;
+ append_op (bi, b_op);
+}
+
+
+/* These operand functions are the semantics of user-defined
+ operand functions. */
+
+static long
+operand_function_HI24S (long a)
+{
+ if (a & 0x80)
+ return (a & (~0xff)) + 0x100;
+ else
+ return (a & (~0xff));
+}
+
+
+static long
+operand_function_F32MINUS (long a)
+{
+ return (32 - a);
+}
+
+
+static long
+operand_function_LOW8 (long a)
+{
+ if (a & 0x80)
+ return (a & 0xff) | ~0xff;
+ else
+ return (a & 0xff);
+}
+
+
+static long
+operand_function_LOW16U (long a)
+{
+ return (a & 0xffff);
+}
+
+
+static long
+operand_function_HI16U (long a)
+{
+ unsigned long b = a & 0xffff0000;
+ return (long) (b >> 16);
+}
+
+
+bfd_boolean
+xg_has_userdef_op_fn (OpType op)
+{
+ switch (op)
+ {
+ case OP_OPERAND_F32MINUS:
+ case OP_OPERAND_LOW8:
+ case OP_OPERAND_HI24S:
+ case OP_OPERAND_LOW16U:
+ case OP_OPERAND_HI16U:
+ return TRUE;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+
+long
+xg_apply_userdef_op_fn (OpType op, long a)
+{
+ switch (op)
+ {
+ case OP_OPERAND_F32MINUS:
+ return operand_function_F32MINUS (a);
+ case OP_OPERAND_LOW8:
+ return operand_function_LOW8 (a);
+ case OP_OPERAND_HI24S:
+ return operand_function_HI24S (a);
+ case OP_OPERAND_LOW16U:
+ return operand_function_LOW16U (a);
+ case OP_OPERAND_HI16U:
+ return operand_function_HI16U (a);
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+
+/* Generate a transition table. */
+
+static const char *
+enter_opname_n (const char *name, int len)
+{
+ opname_e *op;
+
+ for (op = local_opnames; op != NULL; op = op->next)
+ {
+ if (strlen (op->opname) == (unsigned) len
+ && strncmp (op->opname, name, len) == 0)
+ return op->opname;
+ }
+ op = (opname_e *) xmalloc (sizeof (opname_e));
+ op->opname = (char *) xmalloc (len + 1);
+ strncpy (op->opname, name, len);
+ op->opname[len] = '\0';
+ return op->opname;
+}
+
+
+static const char *
+enter_opname (const char *name)
+{
+ opname_e *op;
+
+ for (op = local_opnames; op != NULL; op = op->next)
+ {
+ if (strcmp (op->opname, name) == 0)
+ return op->opname;
+ }
+ op = (opname_e *) xmalloc (sizeof (opname_e));
+ op->opname = xstrdup (name);
+ return op->opname;
+}
+
+
+static void
+init_opname_map (opname_map *m)
+{
+ m->head = NULL;
+ m->tail = &m->head;
+}
+
+
+static void
+clear_opname_map (opname_map *m)
+{
+ opname_map_e *e;
+
+ while (m->head != NULL)
+ {
+ e = m->head;
+ m->head = e->next;
+ free (e);
+ }
+ m->tail = &m->head;
+}
+
+
+static bfd_boolean
+same_operand_name (const opname_map_e *m1, const opname_map_e *m2)
+{
+ if (m1->operand_name == NULL || m1->operand_name == NULL)
+ return FALSE;
+ return (m1->operand_name == m2->operand_name);
+}
+
+
+static opname_map_e *
+get_opmatch (opname_map *map, const char *operand_name)
+{
+ opname_map_e *m;
+
+ for (m = map->head; m != NULL; m = m->next)
+ {
+ if (strcmp (m->operand_name, operand_name) == 0)
+ return m;
+ }
+ return NULL;
+}
+
+
+static bfd_boolean
+op_is_constant (const opname_map_e *m1)
+{
+ return (m1->operand_name == NULL);
+}
+
+
+static unsigned
+op_get_constant (const opname_map_e *m1)
+{
+ gas_assert (m1->operand_name == NULL);
+ return m1->constant_value;
+}
+
+
+static void
+init_precond_list (precond_list *l)
+{
+ l->head = NULL;
+ l->tail = &l->head;
+}
+
+
+static void
+clear_precond_list (precond_list *l)
+{
+ precond_e *e;
+
+ while (l->head != NULL)
+ {
+ e = l->head;
+ l->head = e->next;
+ free (e);
+ }
+ l->tail = &l->head;
+}
+
+
+static void
+init_insn_templ (insn_templ *t)
+{
+ t->opcode_name = NULL;
+ init_opname_map (&t->operand_map);
+}
+
+
+static void
+clear_insn_templ (insn_templ *t)
+{
+ clear_opname_map (&t->operand_map);
+}
+
+
+static void
+init_insn_pattern (insn_pattern *p)
+{
+ init_insn_templ (&p->t);
+ init_precond_list (&p->preconds);
+ p->options = NULL;
+}
+
+
+static void
+clear_insn_pattern (insn_pattern *p)
+{
+ clear_insn_templ (&p->t);
+ clear_precond_list (&p->preconds);
+}
+
+
+static void
+init_insn_repl (insn_repl *r)
+{
+ r->head = NULL;
+ r->tail = &r->head;
+}
+
+
+static void
+clear_insn_repl (insn_repl *r)
+{
+ insn_repl_e *e;
+
+ while (r->head != NULL)
+ {
+ e = r->head;
+ r->head = e->next;
+ clear_insn_templ (&e->t);
+ }
+ r->tail = &r->head;
+}
+
+
+static int
+insn_templ_operand_count (const insn_templ *t)
+{
+ int i = 0;
+ const opname_map_e *op;
+
+ for (op = t->operand_map.head; op != NULL; op = op->next, i++)
+ ;
+ return i;
+}
+
+
+/* Convert a string to a number. E.G.: parse_constant("10", &num) */
+
+static bfd_boolean
+parse_constant (const char *in, unsigned *val_p)
+{
+ unsigned val = 0;
+ const char *p;
+
+ if (in == NULL)
+ return FALSE;
+ p = in;
+
+ while (*p != '\0')
+ {
+ if (*p >= '0' && *p <= '9')
+ val = val * 10 + (*p - '0');
+ else
+ return FALSE;
+ ++p;
+ }
+ *val_p = val;
+ return TRUE;
+}
+
+
+static bfd_boolean
+parse_special_fn (const char *name,
+ const char **fn_name_p,
+ const char **arg_name_p)
+{
+ char *p_start;
+ const char *p_end;
+
+ p_start = strchr (name, '(');
+ if (p_start == NULL)
+ return FALSE;
+
+ p_end = strchr (p_start, ')');
+
+ if (p_end == NULL)
+ return FALSE;
+
+ if (p_end[1] != '\0')
+ return FALSE;
+
+ *fn_name_p = enter_opname_n (name, p_start - name);
+ *arg_name_p = enter_opname_n (p_start + 1, p_end - p_start - 1);
+ return TRUE;
+}
+
+
+static const char *
+skip_white (const char *p)
+{
+ if (p == NULL)
+ return p;
+ while (*p == ' ')
+ ++p;
+ return p;
+}
+
+
+static void
+trim_whitespace (char *in)
+{
+ char *last_white = NULL;
+ char *p = in;
+
+ while (p && *p != '\0')
+ {
+ while (*p == ' ')
+ {
+ if (last_white == NULL)
+ last_white = p;
+ p++;
+ }
+ if (*p != '\0')
+ {
+ last_white = NULL;
+ p++;
+ }
+ }
+ if (last_white)
+ *last_white = '\0';
+}
+
+
+/* Split a string into component strings where "c" is the
+ delimiter. Place the result in the split_rec. */
+
+static void
+split_string (split_rec *rec,
+ const char *in,
+ char c,
+ bfd_boolean elide_whitespace)
+{
+ int cnt = 0;
+ int i;
+ const char *p = in;
+
+ while (p != NULL && *p != '\0')
+ {
+ cnt++;
+ p = strchr (p, c);
+ if (p)
+ p++;
+ }
+ rec->count = cnt;
+ rec->vec = NULL;
+
+ if (rec->count == 0)
+ return;
+
+ rec->vec = (char **) xmalloc (sizeof (char *) * cnt);
+ for (i = 0; i < cnt; i++)
+ rec->vec[i] = 0;
+
+ p = in;
+ for (i = 0; i < cnt; i++)
+ {
+ const char *q;
+ int len;
+
+ q = p;
+ if (elide_whitespace)
+ q = skip_white (q);
+
+ p = strchr (q, c);
+ if (p == NULL)
+ rec->vec[i] = xstrdup (q);
+ else
+ {
+ len = p - q;
+ rec->vec[i] = (char *) xmalloc (sizeof (char) * (len + 1));
+ strncpy (rec->vec[i], q, len);
+ rec->vec[i][len] = '\0';
+ p++;
+ }
+
+ if (elide_whitespace)
+ trim_whitespace (rec->vec[i]);
+ }
+}
+
+
+static void
+clear_split_rec (split_rec *rec)
+{
+ int i;
+
+ for (i = 0; i < rec->count; i++)
+ free (rec->vec[i]);
+
+ if (rec->count > 0)
+ free (rec->vec);
+}
+
+
+/* Initialize a split record. The split record must be initialized
+ before split_string is called. */
+
+static void
+init_split_rec (split_rec *rec)
+{
+ rec->vec = NULL;
+ rec->count = 0;
+}
+
+
+/* Parse an instruction template like "insn op1, op2, op3". */
+
+static bfd_boolean
+parse_insn_templ (const char *s, insn_templ *t)
+{
+ const char *p = s;
+ int insn_name_len;
+ split_rec oprec;
+ int i;
+
+ /* First find the first whitespace. */
+
+ init_split_rec (&oprec);
+
+ p = skip_white (p);
+ insn_name_len = strcspn (s, " ");
+ if (insn_name_len == 0)
+ return FALSE;
+
+ init_insn_templ (t);
+ t->opcode_name = enter_opname_n (p, insn_name_len);
+
+ p = p + insn_name_len;
+
+ /* Split by ',' and skip beginning and trailing whitespace. */
+ split_string (&oprec, p, ',', TRUE);
+
+ for (i = 0; i < oprec.count; i++)
+ {
+ const char *opname = oprec.vec[i];
+ opname_map_e *e = (opname_map_e *) xmalloc (sizeof (opname_map_e));
+ e->next = NULL;
+ e->operand_name = NULL;
+ e->constant_value = 0;
+ e->operand_num = i;
+
+ /* If it begins with a number, assume that it is a number. */
+ if (opname && opname[0] >= '0' && opname[0] <= '9')
+ {
+ unsigned val;
+
+ if (parse_constant (opname, &val))
+ e->constant_value = val;
+ else
+ {
+ free (e);
+ clear_split_rec (&oprec);
+ clear_insn_templ (t);
+ return FALSE;
+ }
+ }
+ else
+ e->operand_name = enter_opname (oprec.vec[i]);
+
+ *t->operand_map.tail = e;
+ t->operand_map.tail = &e->next;
+ }
+ clear_split_rec (&oprec);
+ return TRUE;
+}
+
+
+static bfd_boolean
+parse_precond (const char *s, precond_e *precond)
+{
+ /* All preconditions are currently of the form:
+ a == b or a != b or a == k (where k is a constant).
+ Later we may use some special functions like DENSITY == 1
+ to identify when density is available. */
+
+ const char *p = s;
+ int len;
+ precond->opname1 = NULL;
+ precond->opval1 = 0;
+ precond->cmpop = OP_EQUAL;
+ precond->opname2 = NULL;
+ precond->opval2 = 0;
+ precond->next = NULL;
+
+ p = skip_white (p);
+
+ len = strcspn (p, " !=");
+
+ if (len == 0)
+ return FALSE;
+
+ precond->opname1 = enter_opname_n (p, len);
+ p = p + len;
+ p = skip_white (p);
+
+ /* Check for "==" and "!=". */
+ if (strncmp (p, "==", 2) == 0)
+ precond->cmpop = OP_EQUAL;
+ else if (strncmp (p, "!=", 2) == 0)
+ precond->cmpop = OP_NOTEQUAL;
+ else
+ return FALSE;
+
+ p = p + 2;
+ p = skip_white (p);
+
+ /* No trailing whitespace from earlier parsing. */
+ if (p[0] >= '0' && p[0] <= '9')
+ {
+ unsigned val;
+ if (parse_constant (p, &val))
+ precond->opval2 = val;
+ else
+ return FALSE;
+ }
+ else
+ precond->opname2 = enter_opname (p);
+ return TRUE;
+}
+
+
+static void
+clear_req_or_option_list (ReqOrOption **r_p)
+{
+ if (*r_p == NULL)
+ return;
+
+ free ((*r_p)->option_name);
+ clear_req_or_option_list (&(*r_p)->next);
+ *r_p = NULL;
+}
+
+
+static void
+clear_req_option_list (ReqOption **r_p)
+{
+ if (*r_p == NULL)
+ return;
+
+ clear_req_or_option_list (&(*r_p)->or_option_terms);
+ clear_req_option_list (&(*r_p)->next);
+ *r_p = NULL;
+}
+
+
+static ReqOrOption *
+clone_req_or_option_list (ReqOrOption *req_or_option)
+{
+ ReqOrOption *new_req_or_option;
+
+ if (req_or_option == NULL)
+ return NULL;
+
+ new_req_or_option = (ReqOrOption *) xmalloc (sizeof (ReqOrOption));
+ new_req_or_option->option_name = xstrdup (req_or_option->option_name);
+ new_req_or_option->is_true = req_or_option->is_true;
+ new_req_or_option->next = NULL;
+ new_req_or_option->next = clone_req_or_option_list (req_or_option->next);
+ return new_req_or_option;
+}
+
+
+static ReqOption *
+clone_req_option_list (ReqOption *req_option)
+{
+ ReqOption *new_req_option;
+
+ if (req_option == NULL)
+ return NULL;
+
+ new_req_option = (ReqOption *) xmalloc (sizeof (ReqOption));
+ new_req_option->or_option_terms = NULL;
+ new_req_option->next = NULL;
+ new_req_option->or_option_terms =
+ clone_req_or_option_list (req_option->or_option_terms);
+ new_req_option->next = clone_req_option_list (req_option->next);
+ return new_req_option;
+}
+
+
+static bfd_boolean
+parse_option_cond (const char *s, ReqOption *option)
+{
+ int i;
+ split_rec option_term_rec;
+
+ /* All option or conditions are of the form:
+ optionA + no-optionB + ...
+ "Ands" are divided by "?". */
+
+ init_split_rec (&option_term_rec);
+ split_string (&option_term_rec, s, '+', TRUE);
+
+ if (option_term_rec.count == 0)
+ {
+ clear_split_rec (&option_term_rec);
+ return FALSE;
+ }
+
+ for (i = 0; i < option_term_rec.count; i++)
+ {
+ char *option_name = option_term_rec.vec[i];
+ bfd_boolean is_true = TRUE;
+ ReqOrOption *req;
+ ReqOrOption **r_p;
+
+ if (strncmp (option_name, "no-", 3) == 0)
+ {
+ option_name = xstrdup (&option_name[3]);
+ is_true = FALSE;
+ }
+ else
+ option_name = xstrdup (option_name);
+
+ req = (ReqOrOption *) xmalloc (sizeof (ReqOrOption));
+ req->option_name = option_name;
+ req->is_true = is_true;
+ req->next = NULL;
+
+ /* Append to list. */
+ for (r_p = &option->or_option_terms; (*r_p) != NULL;
+ r_p = &(*r_p)->next)
+ ;
+ (*r_p) = req;
+ }
+ return TRUE;
+}
+
+
+/* Parse a string like:
+ "insn op1, op2, op3, op4 | op1 != op2 | op2 == op3 | op4 == 1".
+ I.E., instruction "insn" with 4 operands where operand 1 and 2 are not
+ the same and operand 2 and 3 are the same and operand 4 is 1.
+
+ or:
+
+ "insn op1 | op1 == 1 / density + boolean / no-useroption".
+ i.e. instruction "insn" with 1 operands where operand 1 is 1
+ when "density" or "boolean" options are available and
+ "useroption" is not available.
+
+ Because the current implementation of this parsing scheme uses
+ split_string, it requires that '|' and '?' are only used as
+ delimiters for predicates and required options. */
+
+static bfd_boolean
+parse_insn_pattern (const char *in, insn_pattern *insn)
+{
+ split_rec rec;
+ split_rec optionrec;
+ int i;
+
+ init_insn_pattern (insn);
+
+ init_split_rec (&optionrec);
+ split_string (&optionrec, in, '?', TRUE);
+ if (optionrec.count == 0)
+ {
+ clear_split_rec (&optionrec);
+ return FALSE;
+ }
+
+ init_split_rec (&rec);
+
+ split_string (&rec, optionrec.vec[0], '|', TRUE);
+
+ if (rec.count == 0)
+ {
+ clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
+ return FALSE;
+ }
+
+ if (!parse_insn_templ (rec.vec[0], &insn->t))
+ {
+ clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
+ return FALSE;
+ }
+
+ for (i = 1; i < rec.count; i++)
+ {
+ precond_e *cond = (precond_e *) xmalloc (sizeof (precond_e));
+
+ if (!parse_precond (rec.vec[i], cond))
+ {
+ clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
+ clear_insn_pattern (insn);
+ return FALSE;
+ }
+
+ /* Append the condition. */
+ *insn->preconds.tail = cond;
+ insn->preconds.tail = &cond->next;
+ }
+
+ for (i = 1; i < optionrec.count; i++)
+ {
+ /* Handle the option conditions. */
+ ReqOption **r_p;
+ ReqOption *req_option = (ReqOption *) xmalloc (sizeof (ReqOption));
+ req_option->or_option_terms = NULL;
+ req_option->next = NULL;
+
+ if (!parse_option_cond (optionrec.vec[i], req_option))
+ {
+ clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
+ clear_insn_pattern (insn);
+ clear_req_option_list (&req_option);
+ return FALSE;
+ }
+
+ /* Append the condition. */
+ for (r_p = &insn->options; (*r_p) != NULL; r_p = &(*r_p)->next)
+ ;
+
+ (*r_p) = req_option;
+ }
+
+ clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
+ return TRUE;
+}
+
+
+static bfd_boolean
+parse_insn_repl (const char *in, insn_repl *r_p)
+{
+ /* This is a list of instruction templates separated by ';'. */
+ split_rec rec;
+ int i;
+
+ split_string (&rec, in, ';', TRUE);
+
+ for (i = 0; i < rec.count; i++)
+ {
+ insn_repl_e *e = (insn_repl_e *) xmalloc (sizeof (insn_repl_e));
+
+ e->next = NULL;
+
+ if (!parse_insn_templ (rec.vec[i], &e->t))
+ {
+ free (e);
+ clear_insn_repl (r_p);
+ return FALSE;
+ }
+ *r_p->tail = e;
+ r_p->tail = &e->next;
+ }
+ return TRUE;
+}
+
+
+static bfd_boolean
+transition_applies (insn_pattern *initial_insn,
+ const char *from_string ATTRIBUTE_UNUSED,
+ const char *to_string ATTRIBUTE_UNUSED)
+{
+ ReqOption *req_option;
+
+ for (req_option = initial_insn->options;
+ req_option != NULL;
+ req_option = req_option->next)
+ {
+ ReqOrOption *req_or_option = req_option->or_option_terms;
+
+ if (req_or_option == NULL
+ || req_or_option->next != NULL)
+ continue;
+
+ if (strncmp (req_or_option->option_name, "IsaUse", 6) == 0)
+ {
+ bfd_boolean option_available = FALSE;
+ char *option_name = req_or_option->option_name + 6;
+ if (!strcmp (option_name, "DensityInstruction"))
+ option_available = (XCHAL_HAVE_DENSITY == 1);
+ else if (!strcmp (option_name, "L32R"))
+ option_available = (XCHAL_HAVE_L32R == 1);
+ else if (!strcmp (option_name, "Const16"))
+ option_available = (XCHAL_HAVE_CONST16 == 1);
+ else if (!strcmp (option_name, "Loops"))
+ option_available = (XCHAL_HAVE_LOOPS == 1);
+ else if (!strcmp (option_name, "WideBranches"))
+ option_available
+ = (XCHAL_HAVE_WIDE_BRANCHES == 1 && produce_flix == FLIX_ALL);
+ else if (!strcmp (option_name, "PredictedBranches"))
+ option_available
+ = (XCHAL_HAVE_PREDICTED_BRANCHES == 1
+ && produce_flix == FLIX_ALL);
+ else if (!strcmp (option_name, "Booleans"))
+ option_available = (XCHAL_HAVE_BOOLEANS == 1);
+ else
+ as_warn (_("invalid configuration option '%s' in transition rule '%s'"),
+ req_or_option->option_name, from_string);
+ if ((option_available ^ req_or_option->is_true) != 0)
+ return FALSE;
+ }
+ else if (strcmp (req_or_option->option_name, "realnop") == 0)
+ {
+ bfd_boolean nop_available =
+ (xtensa_opcode_lookup (xtensa_default_isa, "nop")
+ != XTENSA_UNDEFINED);
+ if ((nop_available ^ req_or_option->is_true) != 0)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
+static bfd_boolean
+wide_branch_opcode (const char *opcode_name,
+ char *suffix,
+ xtensa_opcode *popcode)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_opcode opcode;
+ static char wbr_name_buf[20];
+
+ if (strncmp (opcode_name, "WIDE.", 5) != 0)
+ return FALSE;
+
+ strcpy (wbr_name_buf, opcode_name + 5);
+ strcat (wbr_name_buf, suffix);
+ opcode = xtensa_opcode_lookup (isa, wbr_name_buf);
+ if (opcode != XTENSA_UNDEFINED)
+ {
+ *popcode = opcode;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static TransitionRule *
+build_transition (insn_pattern *initial_insn,
+ insn_repl *replace_insns,
+ const char *from_string,
+ const char *to_string)
+{
+ TransitionRule *tr = NULL;
+ xtensa_opcode opcode;
+ xtensa_isa isa = xtensa_default_isa;
+ BuildInstr *literal_bi;
+
+ opname_map_e *op1;
+ opname_map_e *op2;
+
+ precond_e *precond;
+ insn_repl_e *r;
+
+ if (!wide_branch_opcode (initial_insn->t.opcode_name, ".w18", &opcode)
+ && !wide_branch_opcode (initial_insn->t.opcode_name, ".w15", &opcode))
+ opcode = xtensa_opcode_lookup (isa, initial_insn->t.opcode_name);
+
+ if (opcode == XTENSA_UNDEFINED)
+ {
+ /* It is OK to not be able to translate some of these opcodes. */
+ return NULL;
+ }
+
+
+ if (xtensa_opcode_num_operands (isa, opcode)
+ != insn_templ_operand_count (&initial_insn->t))
+ {
+ /* This is also OK because there are opcodes that
+ have different numbers of operands on different
+ architecture variations. */
+ return NULL;
+ }
+
+ tr = (TransitionRule *) xmalloc (sizeof (TransitionRule));
+ tr->opcode = opcode;
+ tr->conditions = NULL;
+ tr->to_instr = NULL;
+
+ /* Build the conditions. First, equivalent operand condition.... */
+ for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next)
+ {
+ for (op2 = op1->next; op2 != NULL; op2 = op2->next)
+ {
+ if (same_operand_name (op1, op2))
+ {
+ append_value_condition (tr, OP_EQUAL,
+ op1->operand_num, op2->operand_num);
+ }
+ }
+ }
+
+ /* Now the condition that an operand value must be a constant.... */
+ for (op1 = initial_insn->t.operand_map.head; op1 != NULL; op1 = op1->next)
+ {
+ if (op_is_constant (op1))
+ {
+ append_constant_value_condition (tr,
+ OP_EQUAL,
+ op1->operand_num,
+ op_get_constant (op1));
+ }
+ }
+
+
+ /* Now add the explicit preconditions listed after the "|" in the spec.
+ These are currently very limited, so we do a special case
+ parse for them. We expect spaces, opname != opname. */
+ for (precond = initial_insn->preconds.head;
+ precond != NULL;
+ precond = precond->next)
+ {
+ op1 = NULL;
+ op2 = NULL;
+
+ if (precond->opname1)
+ {
+ op1 = get_opmatch (&initial_insn->t.operand_map, precond->opname1);
+ if (op1 == NULL)
+ as_fatal (_("opcode '%s': no bound opname '%s' "
+ "for precondition in '%s'"),
+ xtensa_opcode_name (isa, opcode),
+ precond->opname1, from_string);
+ }
+
+ if (precond->opname2)
+ {
+ op2 = get_opmatch (&initial_insn->t.operand_map, precond->opname2);
+ if (op2 == NULL)
+ as_fatal (_("opcode '%s': no bound opname '%s' "
+ "for precondition in %s"),
+ xtensa_opcode_name (isa, opcode),
+ precond->opname2, from_string);
+ }
+
+ if (op1 == NULL && op2 == NULL)
+ as_fatal (_("opcode '%s': precondition only contains "
+ "constants in '%s'"),
+ xtensa_opcode_name (isa, opcode), from_string);
+ else if (op1 != NULL && op2 != NULL)
+ append_value_condition (tr, precond->cmpop,
+ op1->operand_num, op2->operand_num);
+ else if (op2 == NULL)
+ append_constant_value_condition (tr, precond->cmpop,
+ op1->operand_num, precond->opval2);
+ else
+ append_constant_value_condition (tr, precond->cmpop,
+ op2->operand_num, precond->opval1);
+ }
+
+ tr->options = clone_req_option_list (initial_insn->options);
+
+ /* Generate the replacement instructions. Some of these
+ "instructions" are actually labels and literals. There can be at
+ most one literal and at most one label. A literal must be defined
+ (e.g., "LITERAL %imm") before use (e.g., "%LITERAL"). The labels
+ can be used before they are defined. Also there are a number of
+ special operands (e.g., HI24S). */
+
+ literal_bi = NULL;
+ for (r = replace_insns->head; r != NULL; r = r->next)
+ {
+ BuildInstr *bi;
+ const char *opcode_name;
+ int operand_count;
+ opname_map_e *op;
+ const char *fn_name;
+ const char *operand_arg_name;
+
+ bi = (BuildInstr *) xmalloc (sizeof (BuildInstr));
+ append_build_insn (tr, bi);
+
+ bi->opcode = XTENSA_UNDEFINED;
+ bi->ops = NULL;
+ bi->next = NULL;
+
+ opcode_name = r->t.opcode_name;
+ operand_count = insn_templ_operand_count (&r->t);
+
+ if (strcmp (opcode_name, "LITERAL") == 0)
+ {
+ bi->typ = INSTR_LITERAL_DEF;
+ if (operand_count != 1)
+ as_fatal (_("expected one operand for generated literal"));
+ literal_bi = bi;
+ }
+ else if (strcmp (opcode_name, "LABEL") == 0)
+ {
+ bi->typ = INSTR_LABEL_DEF;
+ if (operand_count != 0)
+ as_fatal (_("expected 0 operands for generated label"));
+ }
+ else
+ {
+ bi->typ = INSTR_INSTR;
+ if (wide_branch_opcode (opcode_name, ".w18", &bi->opcode)
+ || wide_branch_opcode (opcode_name, ".w15", &bi->opcode))
+ opcode_name = xtensa_opcode_name (isa, bi->opcode);
+ else
+ bi->opcode = xtensa_opcode_lookup (isa, opcode_name);
+
+ if (bi->opcode == XTENSA_UNDEFINED)
+ {
+ as_warn (_("invalid opcode '%s' in transition rule '%s'"),
+ opcode_name, to_string);
+ return NULL;
+ }
+
+ /* Check for the right number of ops. */
+ if (xtensa_opcode_num_operands (isa, bi->opcode)
+ != (int) operand_count)
+ as_fatal (_("opcode '%s': replacement does not have %d ops"),
+ opcode_name,
+ xtensa_opcode_num_operands (isa, bi->opcode));
+ }
+
+ for (op = r->t.operand_map.head; op != NULL; op = op->next)
+ {
+ unsigned idnum;
+
+ if (op_is_constant (op))
+ append_constant_op (bi, op->operand_num, op_get_constant (op));
+ else if (strcmp (op->operand_name, "%LITERAL") == 0)
+ {
+ if (! literal_bi || ! literal_bi->ops || literal_bi->ops->next)
+ as_fatal (_("opcode '%s': cannot find literal definition"),
+ opcode_name);
+ append_literal_op (bi, op->operand_num,
+ literal_bi->ops->op_data);
+ }
+ else if (strcmp (op->operand_name, "%LABEL") == 0)
+ append_label_op (bi, op->operand_num);
+ else if (op->operand_name[0] == 'a'
+ && parse_constant (op->operand_name + 1, &idnum))
+ append_constant_op (bi, op->operand_num, idnum);
+ else if (op->operand_name[0] == '%')
+ {
+ opname_map_e *orig_op;
+ orig_op = get_opmatch (&initial_insn->t.operand_map,
+ op->operand_name);
+ if (orig_op == NULL)
+ as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"),
+ opcode_name, op->operand_name, to_string);
+ append_field_op (bi, op->operand_num, orig_op->operand_num);
+ }
+ else if (strcmp (op->operand_name, "FREEREG") == 0)
+ {
+ append_user_fn_field_op (bi, op->operand_num, OP_FREEREG, 0);
+ }
+ else if (parse_special_fn (op->operand_name,
+ &fn_name, &operand_arg_name))
+ {
+ opname_map_e *orig_op;
+ OpType typ = OP_CONSTANT;
+
+ if (strcmp (fn_name, "LOW8") == 0)
+ typ = OP_OPERAND_LOW8;
+ else if (strcmp (fn_name, "HI24S") == 0)
+ typ = OP_OPERAND_HI24S;
+ else if (strcmp (fn_name, "F32MINUS") == 0)
+ typ = OP_OPERAND_F32MINUS;
+ else if (strcmp (fn_name, "LOW16U") == 0)
+ typ = OP_OPERAND_LOW16U;
+ else if (strcmp (fn_name, "HI16U") == 0)
+ typ = OP_OPERAND_HI16U;
+ else
+ as_fatal (_("unknown user-defined function %s"), fn_name);
+
+ orig_op = get_opmatch (&initial_insn->t.operand_map,
+ operand_arg_name);
+ if (orig_op == NULL)
+ as_fatal (_("opcode %s: unidentified operand '%s' in '%s'"),
+ opcode_name, op->operand_name, to_string);
+ append_user_fn_field_op (bi, op->operand_num,
+ typ, orig_op->operand_num);
+ }
+ else
+ as_fatal (_("opcode %s: could not parse operand '%s' in '%s'"),
+ opcode_name, op->operand_name, to_string);
+ }
+ }
+
+ return tr;
+}
+
+
+static TransitionTable *
+build_transition_table (const string_pattern_pair *transitions,
+ int transition_count,
+ transition_cmp_fn cmp)
+{
+ TransitionTable *table = NULL;
+ int num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+ int i, tnum;
+
+ if (table != NULL)
+ return table;
+
+ /* Otherwise, build it now. */
+ table = (TransitionTable *) xmalloc (sizeof (TransitionTable));
+ table->num_opcodes = num_opcodes;
+ table->table =
+ (TransitionList **) xmalloc (sizeof (TransitionTable *) * num_opcodes);
+
+ for (i = 0; i < num_opcodes; i++)
+ table->table[i] = NULL;
+
+ for (tnum = 0; tnum < transition_count; tnum++)
+ {
+ const char *from_string = transitions[tnum].pattern;
+ const char *to_string = transitions[tnum].replacement;
+
+ insn_pattern initial_insn;
+ insn_repl replace_insns;
+ TransitionRule *tr;
+
+ init_insn_pattern (&initial_insn);
+ if (!parse_insn_pattern (from_string, &initial_insn))
+ as_fatal (_("could not parse INSN_PATTERN '%s'"), from_string);
+
+ init_insn_repl (&replace_insns);
+ if (!parse_insn_repl (to_string, &replace_insns))
+ as_fatal (_("could not parse INSN_REPL '%s'"), to_string);
+
+ if (transition_applies (&initial_insn, from_string, to_string))
+ {
+ tr = build_transition (&initial_insn, &replace_insns,
+ from_string, to_string);
+ if (tr)
+ append_transition (table, tr->opcode, tr, cmp);
+ else
+ {
+#if TENSILICA_DEBUG
+ as_warn (_("could not build transition for %s => %s"),
+ from_string, to_string);
+#endif
+ }
+ }
+
+ clear_insn_repl (&replace_insns);
+ clear_insn_pattern (&initial_insn);
+ }
+ return table;
+}
+
+
+extern TransitionTable *
+xg_build_widen_table (transition_cmp_fn cmp)
+{
+ static TransitionTable *table = NULL;
+ if (table == NULL)
+ table = build_transition_table (widen_spec_list, WIDEN_COUNT, cmp);
+ return table;
+}
+
+
+extern TransitionTable *
+xg_build_simplify_table (transition_cmp_fn cmp)
+{
+ static TransitionTable *table = NULL;
+ if (table == NULL)
+ table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT, cmp);
+ return table;
+}
diff --git a/gas/config/xtensa-relax.h b/gas/config/xtensa-relax.h
new file mode 100644
index 0000000..ba07939
--- /dev/null
+++ b/gas/config/xtensa-relax.h
@@ -0,0 +1,189 @@
+/* Table of relaxations for Xtensa assembly.
+ Copyright (C) 2003-2014 Free Software Foundation, Inc.
+
+ This file is part of GAS, the GNU Assembler.
+
+ GAS is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ GAS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GAS; see the file COPYING. If not, write to
+ the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+#ifndef XTENSA_RELAX_H
+#define XTENSA_RELAX_H
+
+#include "xtensa-isa.h"
+
+
+/* Data structures for the table-driven relaxations for Xtensa processors.
+ See xtensa-relax.c for details. */
+
+typedef struct transition_list TransitionList;
+typedef struct transition_table TransitionTable;
+typedef struct transition_rule TransitionRule;
+typedef struct precondition_list PreconditionList;
+typedef struct precondition Precondition;
+
+typedef struct req_or_option_list ReqOrOptionList;
+typedef struct req_or_option_list ReqOrOption;
+typedef struct req_option_list ReqOptionList;
+typedef struct req_option_list ReqOption;
+
+struct transition_table
+{
+ int num_opcodes;
+ TransitionList **table; /* Possible transitions for each opcode. */
+};
+
+struct transition_list
+{
+ TransitionRule *rule;
+ TransitionList *next;
+};
+
+struct precondition_list
+{
+ Precondition *precond;
+ PreconditionList *next;
+};
+
+
+/* The required options for a rule are represented with a two-level
+ structure, with leaf expressions combined by logical ORs at the
+ lower level, and the results then combined by logical ANDs at the
+ top level. The AND terms are linked in a list, and each one can
+ contain a reference to a list of OR terms. The leaf expressions,
+ i.e., the OR options, can be negated by setting the is_true field
+ to FALSE. There are two classes of leaf expressions: (1) those
+ that are properties of the Xtensa configuration and can be
+ evaluated once when building the tables, and (2) those that depend
+ of the state of directives or other settings that may vary during
+ the assembly. The following expressions may be used in group (1):
+
+ IsaUse*: Xtensa configuration settings.
+ realnop: TRUE if the instruction set includes a NOP instruction.
+
+ There are currently no expressions in group (2), but they are still
+ supported since there is a good chance they'll be needed again for
+ something. */
+
+struct req_option_list
+{
+ ReqOrOptionList *or_option_terms;
+ ReqOptionList *next;
+};
+
+struct req_or_option_list
+{
+ char *option_name;
+ bfd_boolean is_true;
+ ReqOrOptionList *next;
+};
+
+/* Operand types and constraints on operands: */
+
+typedef enum op_type OpType;
+typedef enum cmp_op CmpOp;
+
+enum op_type
+{
+ OP_CONSTANT,
+ OP_OPERAND,
+ OP_OPERAND_LOW8, /* Sign-extended low 8 bits of immed. */
+ OP_OPERAND_HI24S, /* High 24 bits of immed,
+ plus 0x100 if low 8 bits are signed. */
+ OP_OPERAND_F32MINUS, /* 32 - immed. */
+ OP_OPERAND_LOW16U, /* Low 16 bits of immed. */
+ OP_OPERAND_HI16U, /* High 16 bits of immed. */
+ OP_LITERAL,
+ OP_FREEREG,
+ OP_LABEL
+};
+
+enum cmp_op
+{
+ OP_EQUAL,
+ OP_NOTEQUAL,
+};
+
+struct precondition
+{
+ CmpOp cmp;
+ int op_num;
+ OpType typ; /* CONSTANT: op_data is a constant.
+ OPERAND: operand op_num must equal op_data.
+ Cannot be LITERAL or LABEL. */
+ int op_data;
+};
+
+
+typedef struct build_op BuildOp;
+
+struct build_op
+{
+ int op_num;
+ OpType typ;
+ unsigned op_data; /* CONSTANT: op_data is the value to encode.
+ OPERAND: op_data is the field in the
+ source instruction to take the value from
+ and encode in the op_num field here.
+ LITERAL: op_data is field in the source
+ instruction that is stored in the literal.
+ LABEL: unused. */
+ BuildOp *next;
+};
+
+typedef struct build_instr BuildInstr;
+typedef enum instr_type InstrType;
+
+enum instr_type
+{
+ INSTR_INSTR,
+ INSTR_LITERAL_DEF,
+ INSTR_LABEL_DEF
+};
+
+struct build_instr
+{
+ InstrType typ;
+ xtensa_opcode opcode; /* Unused for LITERAL_DEF or LABEL_DEF. */
+ BuildOp *ops;
+ BuildInstr *next;
+};
+
+struct transition_rule
+{
+ xtensa_opcode opcode;
+ PreconditionList *conditions;
+ ReqOptionList *options;
+ BuildInstr *to_instr;
+};
+
+typedef int (*transition_cmp_fn) (const TransitionRule *,
+ const TransitionRule *);
+
+extern TransitionTable *xg_build_simplify_table (transition_cmp_fn);
+extern TransitionTable *xg_build_widen_table (transition_cmp_fn);
+
+extern bfd_boolean xg_has_userdef_op_fn (OpType);
+extern long xg_apply_userdef_op_fn (OpType, long);
+
+enum flix_level
+{
+ FLIX_ALL,
+ FLIX_NO_GENERATE,
+ FLIX_NONE
+};
+
+extern enum flix_level produce_flix;
+
+#endif /* !XTENSA_RELAX_H */