mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
641cf0e2c0
First of all make the declarations globally visible, such that producer and consumer actually share them. For the latter two simply add const (as PPC already had it,), while for the former achieve the effect by converting to an array: There's no need for the extra level of indirection.
2662 lines
72 KiB
C
2662 lines
72 KiB
C
/* tc-kvx.c -- Assemble for the KVX ISA
|
|
|
|
Copyright (C) 2009-2024 Free Software Foundation, Inc.
|
|
Contributed by Kalray SA.
|
|
|
|
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 "obstack.h"
|
|
#include "subsegs.h"
|
|
#include "tc-kvx.h"
|
|
#include "libiberty.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
|
|
#ifdef OBJ_ELF
|
|
#include "elf/kvx.h"
|
|
#include "dwarf2dbg.h"
|
|
#include "dw2gencfi.h"
|
|
#endif
|
|
|
|
#define D(args...) do { if(debug) fprintf(args); } while(0)
|
|
|
|
static void supported_cores (char buf[], size_t buflen);
|
|
|
|
#define NELEMS(a) ((int) (sizeof (a)/sizeof ((a)[0])))
|
|
|
|
#define STREQ(x,y) !strcmp(((x) ? (x) : ""), ((y) ? (y) : ""))
|
|
#define STRNEQ(x,y,n) !strncmp(((x) ? (x) : ""), ((y) ? (y) : ""),(n))
|
|
|
|
/* The PARALLEL_BIT is set to 0 when an instruction is the last of a bundle. */
|
|
#define PARALLEL_BIT (1u << 31)
|
|
|
|
/*TB begin*/
|
|
int size_type_function = 1;
|
|
/*TB end */
|
|
|
|
struct kvx_as_env env = {
|
|
.params = {
|
|
.abi = ELF_KVX_ABI_UNDEF,
|
|
.osabi = ELFOSABI_NONE,
|
|
.core = -1,
|
|
.core_set = 0,
|
|
.abi_set = 0,
|
|
.osabi_set = 0,
|
|
.pic_flags = 0,
|
|
.arch_size = 64
|
|
},
|
|
.opts = {
|
|
.march = NULL,
|
|
.check_resource_usage = 1,
|
|
.generate_illegal_code = 0,
|
|
.dump_table = 0,
|
|
.dump_insn = 0,
|
|
.diagnostics = 1,
|
|
.more = 1,
|
|
.allow_all_sfr = 0
|
|
}
|
|
};
|
|
|
|
/* This string should contain position in string where error occured. */
|
|
|
|
/* Default kvx_registers array. */
|
|
const struct kvx_Register *kvx_registers = NULL;
|
|
/* Default kvx_modifiers array. */
|
|
const char ***kvx_modifiers = NULL;
|
|
/* Default kvx_regfiles array. */
|
|
const int *kvx_regfiles = NULL;
|
|
/* Default values used if no assume directive is given */
|
|
const struct kvx_core_info *kvx_core_info = NULL;
|
|
|
|
/***********************************************/
|
|
/* Generic Globals for GAS */
|
|
/***********************************************/
|
|
|
|
const char comment_chars[] = "#";
|
|
const char line_comment_chars[] = "#";
|
|
const char line_separator_chars[] = ";";
|
|
const char EXP_CHARS[] = "eE";
|
|
const char FLT_CHARS[] = "dD";
|
|
const int md_short_jump_size = 0;
|
|
const int md_long_jump_size = 0;
|
|
|
|
/***********************************************/
|
|
/* Local Types */
|
|
/***********************************************/
|
|
|
|
/* a fix up record */
|
|
|
|
struct kvx_fixup
|
|
{
|
|
/* The expression used. */
|
|
expressionS exp;
|
|
/* The place in the frag where this goes. */
|
|
int where;
|
|
/* The relocation. */
|
|
bfd_reloc_code_real_type reloc;
|
|
};
|
|
|
|
/* a single assembled instruction record */
|
|
/* may include immediate extension words */
|
|
struct kvxinsn
|
|
{
|
|
/* written out? */
|
|
int written;
|
|
/* Opcode table entry for this insn */
|
|
const struct kvxopc *opdef;
|
|
/* length of instruction in words (1 or 2) */
|
|
int len;
|
|
/* insn is extended */
|
|
int immx0;
|
|
/* insn has two immx */
|
|
int immx1;
|
|
/* order to stabilize sort */
|
|
int order;
|
|
/* instruction words */
|
|
uint32_t words[KVXMAXBUNDLEWORDS];
|
|
/* the number of fixups [0,2] */
|
|
int nfixups;
|
|
/* the actual fixups */
|
|
struct kvx_fixup fixup[2];
|
|
};
|
|
|
|
typedef void (*print_insn_t) (struct kvxopc * op);
|
|
static print_insn_t print_insn = NULL;
|
|
|
|
/* Set to TRUE when we assemble instructions. */
|
|
static bool assembling_insn = false;
|
|
|
|
#define NOIMMX -1
|
|
|
|
/* Was KVXMAXBUNDLEISSUE, changed because of NOPs */
|
|
static struct kvxinsn insbuf[KVXMAXBUNDLEWORDS];
|
|
static int insncnt = 0;
|
|
static struct kvxinsn immxbuf[KVXMAXBUNDLEWORDS];
|
|
static int immxcnt = 0;
|
|
|
|
static void
|
|
incr_immxcnt (void)
|
|
{
|
|
immxcnt++;
|
|
if (immxcnt >= KVXMAXBUNDLEWORDS)
|
|
as_bad ("Max immx number exceeded: %d", immxcnt);
|
|
}
|
|
|
|
static void set_byte_counter (asection * sec, int value);
|
|
static void
|
|
set_byte_counter (asection * sec, int value)
|
|
{
|
|
sec->target_index = value;
|
|
}
|
|
|
|
static int get_byte_counter (asection * sec);
|
|
int
|
|
get_byte_counter (asection * sec)
|
|
{
|
|
return sec->target_index;
|
|
}
|
|
|
|
const char *
|
|
kvx_target_format (void)
|
|
{
|
|
return env.params.arch_size == 64 ? "elf64-kvx" : "elf32-kvx";
|
|
}
|
|
|
|
/****************************************************/
|
|
/* ASSEMBLER Pseudo-ops. Some of this just */
|
|
/* extends the default definitions */
|
|
/* others are KVX specific */
|
|
/****************************************************/
|
|
|
|
static void kvx_check_resources (int);
|
|
static void kvx_proc (int start);
|
|
static void kvx_endp (int start);
|
|
static void kvx_type (int start);
|
|
|
|
const pseudo_typeS md_pseudo_table[] = {
|
|
/* override default 2-bytes */
|
|
{ "word", cons, 4 },
|
|
|
|
/* KVX specific */
|
|
{ "dword", cons, 8 },
|
|
|
|
/* Override align directives to have a boundary as argument (and not the
|
|
power of two as in p2align) */
|
|
{ "align", s_align_bytes, 0 },
|
|
|
|
{ "checkresources", kvx_check_resources, 1 },
|
|
{ "nocheckresources", kvx_check_resources, 0 },
|
|
|
|
{ "proc", kvx_proc, 1 },
|
|
{ "endp", kvx_endp, 0 },
|
|
|
|
{ "type", kvx_type, 0 },
|
|
|
|
#ifdef OBJ_ELF
|
|
{ "file", dwarf2_directive_file, 0 },
|
|
{ "loc", dwarf2_directive_loc, 0 },
|
|
#endif
|
|
{ NULL, 0, 0 }
|
|
};
|
|
|
|
|
|
static int inside_bundle = 0;
|
|
|
|
/* Stores the labels inside bundles (typically debug labels) that need
|
|
to be postponed to the next bundle. */
|
|
struct label_fix
|
|
{
|
|
struct label_fix *next;
|
|
symbolS *sym;
|
|
} *label_fixes = 0;
|
|
|
|
/*****************************************************/
|
|
/* OPTIONS PROCESSING */
|
|
/*****************************************************/
|
|
|
|
const char md_shortopts[] = "hV"; /* catted to std short options */
|
|
|
|
/* added to std long options */
|
|
|
|
#define OPTION_HEXFILE (OPTION_MD_BASE + 0)
|
|
#define OPTION_MARCH (OPTION_MD_BASE + 4)
|
|
#define OPTION_CHECK_RESOURCES (OPTION_MD_BASE + 5)
|
|
#define OPTION_NO_CHECK_RESOURCES (OPTION_MD_BASE + 6)
|
|
#define OPTION_GENERATE_ILLEGAL_CODE (OPTION_MD_BASE + 7)
|
|
#define OPTION_DUMP_TABLE (OPTION_MD_BASE + 8)
|
|
#define OPTION_PIC (OPTION_MD_BASE + 9)
|
|
#define OPTION_BIGPIC (OPTION_MD_BASE + 10)
|
|
#define OPTION_NOPIC (OPTION_MD_BASE + 12)
|
|
#define OPTION_32 (OPTION_MD_BASE + 13)
|
|
#define OPTION_DUMPINSN (OPTION_MD_BASE + 15)
|
|
#define OPTION_ALL_SFR (OPTION_MD_BASE + 16)
|
|
#define OPTION_DIAGNOSTICS (OPTION_MD_BASE + 17)
|
|
#define OPTION_NO_DIAGNOSTICS (OPTION_MD_BASE + 18)
|
|
#define OPTION_MORE (OPTION_MD_BASE + 19)
|
|
#define OPTION_NO_MORE (OPTION_MD_BASE + 20)
|
|
|
|
const struct option md_longopts[] = {
|
|
{ "march", required_argument, NULL, OPTION_MARCH },
|
|
{ "check-resources", no_argument, NULL, OPTION_CHECK_RESOURCES },
|
|
{ "no-check-resources", no_argument, NULL, OPTION_NO_CHECK_RESOURCES },
|
|
{ "generate-illegal-code", no_argument, NULL, OPTION_GENERATE_ILLEGAL_CODE },
|
|
{ "dump-table", no_argument, NULL, OPTION_DUMP_TABLE },
|
|
{ "mpic", no_argument, NULL, OPTION_PIC },
|
|
{ "mPIC", no_argument, NULL, OPTION_BIGPIC },
|
|
{ "mnopic", no_argument, NULL, OPTION_NOPIC },
|
|
{ "m32", no_argument, NULL, OPTION_32 },
|
|
{ "dump-insn", no_argument, NULL, OPTION_DUMPINSN },
|
|
{ "all-sfr", no_argument, NULL, OPTION_ALL_SFR },
|
|
{ "diagnostics", no_argument, NULL, OPTION_DIAGNOSTICS },
|
|
{ "no-diagnostics", no_argument, NULL, OPTION_NO_DIAGNOSTICS },
|
|
{ "more", no_argument, NULL, OPTION_MORE },
|
|
{ "no-more", no_argument, NULL, OPTION_NO_MORE },
|
|
{ NULL, no_argument, NULL, 0 }
|
|
};
|
|
|
|
const size_t md_longopts_size = sizeof (md_longopts);
|
|
|
|
int
|
|
md_parse_option (int c, const char *arg ATTRIBUTE_UNUSED)
|
|
{
|
|
int find_core = 0;
|
|
|
|
switch (c)
|
|
{
|
|
case 'h':
|
|
md_show_usage (stdout);
|
|
exit (EXIT_SUCCESS);
|
|
break;
|
|
|
|
/* -V: SVR4 argument to print version ID. */
|
|
case 'V':
|
|
print_version_id ();
|
|
exit (EXIT_SUCCESS);
|
|
break;
|
|
case OPTION_MARCH:
|
|
env.opts.march = strdup (arg);
|
|
for (int i = 0; i < KVXNUMCORES && !find_core; ++i)
|
|
if (!strcasecmp (env.opts.march, kvx_core_info_table[i]->name))
|
|
{
|
|
kvx_core_info = kvx_core_info_table[i];
|
|
kvx_registers = kvx_registers_table[i];
|
|
kvx_modifiers = kvx_modifiers_table[i];
|
|
kvx_regfiles = kvx_regfiles_table[i];
|
|
|
|
find_core = 1;
|
|
break;
|
|
}
|
|
if (!find_core)
|
|
{
|
|
char buf[100];
|
|
supported_cores (buf, sizeof (buf));
|
|
as_fatal ("Specified arch not supported [%s]", buf);
|
|
}
|
|
break;
|
|
case OPTION_CHECK_RESOURCES:
|
|
env.opts.check_resource_usage = 1;
|
|
break;
|
|
case OPTION_NO_CHECK_RESOURCES:
|
|
env.opts.check_resource_usage = 0;
|
|
break;
|
|
case OPTION_GENERATE_ILLEGAL_CODE:
|
|
env.opts.generate_illegal_code = 1;
|
|
break;
|
|
case OPTION_DUMP_TABLE:
|
|
env.opts.dump_table = 1;
|
|
break;
|
|
case OPTION_DUMPINSN:
|
|
env.opts.dump_insn = 1;
|
|
break;
|
|
case OPTION_ALL_SFR:
|
|
env.opts.allow_all_sfr = 1;
|
|
break;
|
|
case OPTION_DIAGNOSTICS:
|
|
env.opts.diagnostics = 1;
|
|
break;
|
|
case OPTION_NO_DIAGNOSTICS:
|
|
env.opts.diagnostics = 0;
|
|
break;
|
|
case OPTION_MORE:
|
|
env.opts.more = 1;
|
|
break;
|
|
case OPTION_NO_MORE:
|
|
env.opts.more = 0;
|
|
break;
|
|
case OPTION_PIC:
|
|
/* fallthrough, for now the same on KVX */
|
|
case OPTION_BIGPIC:
|
|
env.params.pic_flags |= ELF_KVX_ABI_PIC_BIT;
|
|
break;
|
|
case OPTION_NOPIC:
|
|
env.params.pic_flags &= ~(ELF_KVX_ABI_PIC_BIT);
|
|
break;
|
|
case OPTION_32:
|
|
env.params.arch_size = 32;
|
|
break;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void
|
|
md_show_usage (FILE * stream)
|
|
{
|
|
char buf[100];
|
|
supported_cores (buf, sizeof (buf));
|
|
|
|
fprintf (stream, "\n"
|
|
"KVX specific options:\n\n"
|
|
" --check-resources\t Perform minimal resource checking\n"
|
|
" --march [%s]\t Select architecture\n"
|
|
" -V \t\t\t Print assembler version number\n\n"
|
|
" The options -M, --mri and -f are not supported in this assembler.\n", buf);
|
|
}
|
|
|
|
/**************************************************/
|
|
/* UTILITIES */
|
|
/**************************************************/
|
|
|
|
/*
|
|
* Read a value from to the object file
|
|
*/
|
|
|
|
static valueT md_chars_to_number (char *buf, int n);
|
|
valueT
|
|
md_chars_to_number (char *buf, int n)
|
|
{
|
|
valueT val = 0;
|
|
|
|
if (n > (int) sizeof (val) || n <= 0)
|
|
abort ();
|
|
|
|
while (n--)
|
|
{
|
|
val <<= 8;
|
|
val |= (buf[n] & 0xff);
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
/* Returns the corresponding pseudo function matching SYM and to be
|
|
used for data section */
|
|
static struct pseudo_func *
|
|
kvx_get_pseudo_func_data_scn (symbolS * sym)
|
|
{
|
|
for (int i = 0; i < kvx_core_info->nb_pseudo_funcs; i++)
|
|
if (sym == kvx_core_info->pseudo_funcs[i].sym
|
|
&& kvx_core_info->pseudo_funcs[i].pseudo_relocs.single != BFD_RELOC_UNUSED)
|
|
return &kvx_core_info->pseudo_funcs[i];
|
|
return NULL;
|
|
}
|
|
|
|
/* Returns the corresponding pseudo function matching SYM and operand
|
|
format OPND */
|
|
static struct pseudo_func *
|
|
kvx_get_pseudo_func2 (symbolS *sym, struct kvx_operand * opnd)
|
|
{
|
|
for (int i = 0; i < kvx_core_info->nb_pseudo_funcs; i++)
|
|
if (sym == kvx_core_info->pseudo_funcs[i].sym)
|
|
for (int relidx = 0; relidx < opnd->reloc_nb; relidx++)
|
|
if (opnd->relocs[relidx] == kvx_core_info->pseudo_funcs[i].pseudo_relocs.kreloc
|
|
&& (env.params.arch_size == (int) kvx_core_info->pseudo_funcs[i].pseudo_relocs.avail_modes
|
|
|| kvx_core_info->pseudo_funcs[i].pseudo_relocs.avail_modes == PSEUDO_ALL))
|
|
return &kvx_core_info->pseudo_funcs[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
supported_cores (char buf[], size_t buflen)
|
|
{
|
|
buf[0] = '\0';
|
|
for (int i = 0; i < KVXNUMCORES; i++)
|
|
{
|
|
if (buf[0] == '\0')
|
|
strcpy (buf, kvx_core_info_table[i]->name);
|
|
else
|
|
if ((strlen (buf) + 1 + strlen (kvx_core_info_table[i]->name) + 1) < buflen)
|
|
{
|
|
strcat (buf, "|");
|
|
strcat (buf, kvx_core_info_table[i]->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************/
|
|
/* ASSEMBLE AN INSTRUCTION */
|
|
/***************************************************/
|
|
|
|
/*
|
|
* Insert ARG into the operand described by OPDEF in instruction INSN
|
|
* Returns 1 if the immediate extension (IMMX) has been
|
|
* handled along with relocation, 0 if not.
|
|
*/
|
|
static int
|
|
insert_operand (struct kvxinsn *insn, struct kvx_operand *opdef,
|
|
struct token_list *tok)
|
|
{
|
|
uint64_t op = 0;
|
|
struct kvx_bitfield *bfields = opdef->bfield;
|
|
int bf_nb = opdef->bitfields;
|
|
int immx_ready = 0;
|
|
|
|
if (opdef->width == 0)
|
|
return 0;
|
|
|
|
#define add_fixup(insn_, reloc_, exp_) \
|
|
do { \
|
|
(insn_)->fixup[(insn_)->nfixups].reloc = (reloc_); \
|
|
(insn_)->fixup[(insn_)->nfixups].exp = (exp_); \
|
|
(insn_)->fixup[(insn_)->nfixups].where = 0; \
|
|
(insn_)->nfixups++; \
|
|
} while (0)
|
|
|
|
#define add_immx(insn_, words_, reloc_, exp_, nfixups_, len_) \
|
|
do { \
|
|
immxbuf[immxcnt].words[0] = (words_); \
|
|
immxbuf[immxcnt].fixup[0].reloc = (reloc_); \
|
|
immxbuf[immxcnt].fixup[0].exp = (exp_); \
|
|
immxbuf[immxcnt].fixup[0].where = 0; \
|
|
immxbuf[immxcnt].nfixups = (nfixups_); \
|
|
immxbuf[immxcnt].len = (len_); \
|
|
/* decrement insn->len: immx part handled separately \
|
|
from insn and must not be emited twice. */ \
|
|
(insn_)->len -= 1; \
|
|
incr_immxcnt (); \
|
|
} while (0)
|
|
|
|
#define chk_imm(core_, imm_) \
|
|
(env.params.core == ELF_KVX_CORE_## core_ && opdef->type == (imm_))
|
|
|
|
/* try to resolve the value */
|
|
|
|
switch (tok->category)
|
|
{
|
|
case CAT_REGISTER:
|
|
op = S_GET_VALUE (str_hash_find (env.reg_hash, tok->tok));
|
|
op -= opdef->bias;
|
|
op >>= opdef->shift;
|
|
break;
|
|
case CAT_MODIFIER:
|
|
op = tok->val;
|
|
op -= opdef->bias;
|
|
op >>= opdef->shift;
|
|
break;
|
|
case CAT_IMMEDIATE:
|
|
{
|
|
char *ilp_save = input_line_pointer;
|
|
input_line_pointer = tok->tok;
|
|
expressionS exp = { 0 };
|
|
expression (&exp);
|
|
input_line_pointer = ilp_save;
|
|
|
|
/* We are dealing with a pseudo-function. */
|
|
if (tok->tok[0] == '@')
|
|
{
|
|
if (insn->nfixups == 0)
|
|
{
|
|
expressionS reloc_arg;
|
|
reloc_arg = exp;
|
|
reloc_arg.X_op = O_symbol;
|
|
struct pseudo_func *pf =
|
|
kvx_get_pseudo_func2 (exp.X_op_symbol, opdef);
|
|
/* S64 uses LO10/UP27/EX27 format (3 words), with one reloc in each words (3) */
|
|
/* S43 uses LO10/EX6/UP27 format (2 words), with 2 relocs in main syllabes and 1 in extra word */
|
|
/* S37 uses LO10/UP27 format (2 words), with one reloc in each word (2) */
|
|
|
|
/* Beware that immxbuf must be filled in the same order as relocs should be emitted. */
|
|
|
|
if (pf->pseudo_relocs.reloc_type == S64_LO10_UP27_EX27
|
|
|| pf->pseudo_relocs.reloc_type == S43_LO10_UP27_EX6
|
|
|| pf->pseudo_relocs.reloc_type == S37_LO10_UP27)
|
|
{
|
|
add_fixup (insn, pf->pseudo_relocs.reloc_lo10, reloc_arg);
|
|
|
|
insn->immx0 = immxcnt;
|
|
add_immx (insn, 0, pf->pseudo_relocs.reloc_up27,
|
|
reloc_arg, 1, 1);
|
|
immx_ready = 1;
|
|
}
|
|
else if (pf->pseudo_relocs.reloc_type == S32_LO5_UP27)
|
|
{
|
|
add_fixup (insn, pf->pseudo_relocs.reloc_lo5, reloc_arg);
|
|
|
|
insn->immx0 = immxcnt;
|
|
add_immx (insn, 0, pf->pseudo_relocs.reloc_up27,
|
|
reloc_arg, 1, 1);
|
|
immx_ready = 1;
|
|
}
|
|
else if (pf->pseudo_relocs.reloc_type == S16)
|
|
add_fixup (insn, pf->pseudo_relocs.single, reloc_arg);
|
|
else
|
|
as_fatal ("Unexpected fixup");
|
|
|
|
if (pf->pseudo_relocs.reloc_type == S64_LO10_UP27_EX27)
|
|
{
|
|
insn->immx1 = immxcnt;
|
|
add_immx (insn, 0, pf->pseudo_relocs.reloc_ex, reloc_arg,
|
|
1, 1);
|
|
}
|
|
else if (pf->pseudo_relocs.reloc_type == S43_LO10_UP27_EX6)
|
|
add_fixup (insn, pf->pseudo_relocs.reloc_ex, reloc_arg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (exp.X_op == O_constant)
|
|
{
|
|
/* This is a immediate: either a regular immediate, or an
|
|
immediate that was saved in a variable through `.equ'. */
|
|
uint64_t sval = (int64_t) tok->val;
|
|
op = opdef->flags & kvxSIGNED ? sval : tok->val;
|
|
op >>= opdef->shift;
|
|
}
|
|
else if (exp.X_op == O_subtract)
|
|
as_fatal ("O_subtract not supported.");
|
|
else
|
|
{
|
|
|
|
/* This is a symbol which needs a relocation. */
|
|
if (insn->nfixups == 0)
|
|
{
|
|
if (chk_imm (KV3_1, Immediate_kv3_v1_pcrel17)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_pcrel17)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_pcrel17))
|
|
add_fixup (insn, BFD_RELOC_KVX_PCREL17, exp);
|
|
else if (chk_imm (KV3_1, Immediate_kv3_v1_pcrel27)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_pcrel27)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_pcrel27))
|
|
add_fixup (insn, BFD_RELOC_KVX_PCREL27, exp);
|
|
else if (chk_imm (KV3_1, Immediate_kv3_v1_wrapped32)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_wrapped32)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_wrapped32))
|
|
{
|
|
add_fixup (insn, BFD_RELOC_KVX_S32_LO5, exp);
|
|
|
|
insn->immx0 = immxcnt;
|
|
add_immx (insn, 0, BFD_RELOC_KVX_S32_UP27, exp, 1, 1);
|
|
|
|
immx_ready = 1;
|
|
}
|
|
else if (chk_imm (KV3_1, Immediate_kv3_v1_signed10)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_signed10)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_signed10))
|
|
add_fixup (insn, BFD_RELOC_KVX_S37_LO10, exp);
|
|
else if (chk_imm (KV3_1, Immediate_kv3_v1_signed37)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_signed37)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_signed37))
|
|
{
|
|
add_fixup (insn, BFD_RELOC_KVX_S37_LO10, exp);
|
|
|
|
insn->immx0 = immxcnt;
|
|
add_immx (insn, 0, BFD_RELOC_KVX_S37_UP27, exp, 1, 1);
|
|
|
|
immx_ready = 1;
|
|
}
|
|
else if (chk_imm (KV3_1, Immediate_kv3_v1_signed43)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_signed43)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_signed43))
|
|
{
|
|
add_fixup (insn, BFD_RELOC_KVX_S43_LO10, exp);
|
|
add_fixup (insn, BFD_RELOC_KVX_S43_EX6, exp);
|
|
|
|
insn->immx0 = immxcnt;
|
|
add_immx (insn, insn->words[1],
|
|
BFD_RELOC_KVX_S43_UP27, exp, 1, 1);
|
|
|
|
immx_ready = 1;
|
|
}
|
|
else if (chk_imm (KV3_1, Immediate_kv3_v1_wrapped64)
|
|
|| chk_imm (KV3_2, Immediate_kv3_v2_wrapped64)
|
|
|| chk_imm (KV4_1, Immediate_kv4_v1_wrapped64))
|
|
{
|
|
add_fixup (insn, BFD_RELOC_KVX_S64_LO10, exp);
|
|
|
|
insn->immx0 = immxcnt;
|
|
add_immx (insn, insn->words[1],
|
|
BFD_RELOC_KVX_S64_UP27, exp, 1, 1);
|
|
|
|
insn->immx1 = immxcnt;
|
|
add_immx (insn, insn->words[2],
|
|
BFD_RELOC_KVX_S64_EX27, exp, 1, 1);
|
|
|
|
immx_ready = 1;
|
|
}
|
|
else
|
|
as_fatal ("don't know how to generate a fixup record");
|
|
return immx_ready;
|
|
}
|
|
else
|
|
as_fatal ("No room for fixup ");
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
for (int bf_idx = 0; bf_idx < bf_nb; bf_idx++)
|
|
{
|
|
uint64_t value =
|
|
((uint64_t) op >> bfields[bf_idx].from_offset);
|
|
int j = 0;
|
|
int to_offset = bfields[bf_idx].to_offset;
|
|
value &= (1LL << bfields[bf_idx].size) - 1;
|
|
j = to_offset / 32;
|
|
to_offset = to_offset % 32;
|
|
insn->words[j] |= (value << to_offset) & 0xffffffff;
|
|
}
|
|
|
|
return immx_ready;
|
|
|
|
#undef chk_imm
|
|
#undef add_immx
|
|
#undef add_fixup
|
|
}
|
|
|
|
/*
|
|
* Given a set of operands and a matching instruction,
|
|
* assemble it
|
|
*
|
|
*/
|
|
static void
|
|
assemble_insn (const struct kvxopc * opcode, struct token_list *tok, struct kvxinsn *insn)
|
|
{
|
|
unsigned immx_ready = 0;
|
|
|
|
memset (insn, 0, sizeof (*insn));
|
|
insn->opdef = opcode;
|
|
for (int i = 0; i < opcode->wordcount; i++)
|
|
{
|
|
insn->words[i] = opcode->codewords[i].opcode;
|
|
insn->len += 1;
|
|
}
|
|
|
|
insn->immx0 = NOIMMX;
|
|
insn->immx1 = NOIMMX;
|
|
|
|
struct token_list *tok_ = tok;
|
|
struct kvx_operand **format = (struct kvx_operand **) opcode->format;
|
|
|
|
while (tok_)
|
|
{
|
|
int ret = insert_operand (insn, *format, tok_);
|
|
immx_ready |= ret;
|
|
while ((tok_ = tok_->next) && tok_->category == CAT_SEPARATOR);
|
|
format++;
|
|
}
|
|
|
|
// Handle immx if insert_operand did not already take care of that
|
|
if (!immx_ready)
|
|
{
|
|
for (int i = 0; i < opcode->wordcount; i++)
|
|
{
|
|
if (opcode->codewords[i].flags & kvxOPCODE_FLAG_IMMX0)
|
|
{
|
|
insn->immx0 = immxcnt;
|
|
immxbuf[immxcnt].words[0] = insn->words[i];
|
|
immxbuf[immxcnt].nfixups = 0;
|
|
immxbuf[immxcnt].len = 1;
|
|
insn->len -= 1;
|
|
incr_immxcnt ();
|
|
}
|
|
if (opcode->codewords[i].flags & kvxOPCODE_FLAG_IMMX1)
|
|
{
|
|
insn->immx1 = immxcnt;
|
|
immxbuf[immxcnt].words[0] = insn->words[i];
|
|
immxbuf[immxcnt].nfixups = 0;
|
|
immxbuf[immxcnt].len = 1;
|
|
insn->len -= 1;
|
|
incr_immxcnt ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Emit an instruction from the instruction array into the object
|
|
* file. INSN points to an element of the instruction array. STOPFLAG
|
|
* is true if this is the last instruction in the bundle.
|
|
*
|
|
* Only handles main syllables of bundle. Immediate extensions are
|
|
* handled by insert_operand.
|
|
*/
|
|
static void
|
|
emit_insn (struct kvxinsn * insn, int insn_pos, int stopflag)
|
|
{
|
|
char *f;
|
|
unsigned int image;
|
|
|
|
/* if we are listing, attach frag to previous line. */
|
|
if (listing)
|
|
listing_prev_line ();
|
|
|
|
/* Update text size for lane parity checking. */
|
|
set_byte_counter (now_seg, (get_byte_counter (now_seg) + (insn->len * 4)));
|
|
|
|
/* allocate space in the fragment. */
|
|
f = frag_more (insn->len * 4);
|
|
|
|
/* spit out bits. */
|
|
for (int i = 0; i < insn->len; i++)
|
|
{
|
|
image = insn->words[i];
|
|
|
|
/* Handle bundle parallel bit. */ ;
|
|
if ((i == insn->len - 1) && stopflag)
|
|
image &= ~PARALLEL_BIT;
|
|
else
|
|
image |= PARALLEL_BIT;
|
|
|
|
/* Emit the instruction image. */
|
|
md_number_to_chars (f + (i * 4), image, 4);
|
|
}
|
|
|
|
/* generate fixup records */
|
|
|
|
for (int i = 0; i < insn->nfixups; i++)
|
|
{
|
|
int size, pcrel;
|
|
reloc_howto_type *reloc_howto =
|
|
bfd_reloc_type_lookup (stdoutput, insn->fixup[i].reloc);
|
|
assert (reloc_howto);
|
|
size = bfd_get_reloc_size (reloc_howto);
|
|
pcrel = reloc_howto->pc_relative;
|
|
|
|
/* In case the PCREL relocation is not for the first insn in the
|
|
bundle, we have to offset it. The pc used by the hardware
|
|
references a bundle and not separate insn.
|
|
*/
|
|
assert (!(insn_pos == -1 && pcrel));
|
|
if (pcrel && insn_pos > 0)
|
|
insn->fixup[i].exp.X_add_number += insn_pos * 4;
|
|
|
|
fixS *fixup = fix_new_exp (frag_now,
|
|
f - frag_now->fr_literal +
|
|
insn->fixup[i].where,
|
|
size,
|
|
&(insn->fixup[i].exp),
|
|
pcrel,
|
|
insn->fixup[i].reloc);
|
|
/*
|
|
* Set this bit so that large value can still be
|
|
* handled. Without it, assembler will fail in fixup_segment
|
|
* when it checks there is enough bits to store the value. As we
|
|
* usually split our reloc across different words, it may think
|
|
* that 4 bytes are not enough for large value. This simply
|
|
* skips the tests
|
|
*/
|
|
fixup->fx_no_overflow = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/* 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. */
|
|
/* FIXME: Should be done by the parser */
|
|
void
|
|
md_operand (expressionS * e)
|
|
{
|
|
/* enum pseudo_type pseudo_type; */
|
|
/* char *name = NULL; */
|
|
size_t len;
|
|
int ch, i;
|
|
|
|
switch (*input_line_pointer)
|
|
{
|
|
case '@':
|
|
/* Find what relocation pseudo-function we're dealing with. */
|
|
/* pseudo_type = 0; */
|
|
ch = *++input_line_pointer;
|
|
for (i = 0; i < kvx_core_info->nb_pseudo_funcs; ++i)
|
|
if (kvx_core_info->pseudo_funcs[i].name && kvx_core_info->pseudo_funcs[i].name[0] == ch)
|
|
{
|
|
len = strlen (kvx_core_info->pseudo_funcs[i].name);
|
|
if (strncmp (kvx_core_info->pseudo_funcs[i].name + 1,
|
|
input_line_pointer + 1, len - 1) == 0
|
|
&& !is_part_of_name (input_line_pointer[len]))
|
|
{
|
|
input_line_pointer += len;
|
|
break;
|
|
}
|
|
}
|
|
SKIP_WHITESPACE ();
|
|
if (*input_line_pointer != '(')
|
|
{
|
|
as_bad ("Expected '('");
|
|
goto err;
|
|
}
|
|
/* Skip '('. */
|
|
++input_line_pointer;
|
|
if (!kvx_core_info->pseudo_funcs[i].pseudo_relocs.has_no_arg)
|
|
expression (e);
|
|
if (*input_line_pointer++ != ')')
|
|
{
|
|
as_bad ("Missing ')'");
|
|
goto err;
|
|
}
|
|
if (!kvx_core_info->pseudo_funcs[i].pseudo_relocs.has_no_arg)
|
|
{
|
|
if (e->X_op != O_symbol)
|
|
as_fatal ("Illegal combination of relocation functions");
|
|
}
|
|
/* 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 = kvx_core_info->pseudo_funcs[i].sym;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
|
|
err:
|
|
ignore_rest_of_line ();
|
|
}
|
|
|
|
/*
|
|
* Return the Bundling type for an insn.
|
|
*/
|
|
static int
|
|
find_bundling (const struct kvxinsn * insn)
|
|
{
|
|
return insn->opdef->bundling;
|
|
}
|
|
|
|
static int
|
|
find_reservation (const struct kvxinsn * insn)
|
|
{
|
|
return insn->opdef->reservation;
|
|
}
|
|
|
|
static struct kvxopc *
|
|
assemble_tokens (struct token_list *tok_list)
|
|
{
|
|
assert (tok_list != NULL);
|
|
struct token_list *toks = tok_list;
|
|
|
|
/* make sure there is room in instruction buffer */
|
|
/* Was KVXMAXBUNDLEISSUE, changed because of NOPs */
|
|
if (insncnt >= KVXMAXBUNDLEWORDS)
|
|
as_fatal ("[assemble_tokens]: too many instructions in bundle.");
|
|
|
|
/* TODO: Merge */
|
|
struct kvxinsn *insn;
|
|
insn = insbuf + insncnt;
|
|
|
|
/* The formats table registers the modifier into the opcode, therefore we need
|
|
to fuse both before looking up the opcodes hashtable. */
|
|
char *opcode = NULL;
|
|
|
|
opcode = toks->tok;
|
|
toks = toks->next;
|
|
|
|
while (toks && toks->category == CAT_SEPARATOR)
|
|
toks = toks->next;
|
|
|
|
/* Find the format requested by the instruction. */
|
|
struct kvxopc *format_tbl = str_hash_find (env.opcode_hash, opcode);
|
|
struct kvxopc *format = NULL;
|
|
|
|
struct token_list *toks_ = toks;
|
|
|
|
while (!format && format_tbl && STREQ (opcode, format_tbl->as_op))
|
|
{
|
|
for (int i = 0 ; toks_ && format_tbl->format[i]
|
|
&& toks_->class_id == format_tbl->format[i]->type ;)
|
|
{
|
|
toks_ = toks_->next;
|
|
while (toks_ && toks_->category == CAT_SEPARATOR)
|
|
toks_ = toks_->next;
|
|
i += 1;
|
|
}
|
|
|
|
if (!toks_)
|
|
format = format_tbl;
|
|
else
|
|
{
|
|
toks_ = toks;
|
|
format_tbl++;
|
|
}
|
|
}
|
|
|
|
assert (format != NULL);
|
|
|
|
assemble_insn (format, toks, insn);
|
|
insncnt++;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* Write in buf at most buf_size.
|
|
* Returns the number of writen characters.
|
|
*/
|
|
static int ATTRIBUTE_UNUSED
|
|
insn_syntax (struct kvxopc * op, char *buf, int buf_size)
|
|
{
|
|
int chars = snprintf (buf, buf_size, "%s ", op->as_op);
|
|
const char *fmtp = op->fmtstring;
|
|
char ch = 0;
|
|
|
|
for (int i = 0; op->format[i]; i++)
|
|
{
|
|
int type = op->format[i]->type;
|
|
const char *type_name = TOKEN_NAME (type);
|
|
int offset = 0;
|
|
|
|
for (int j = 0 ; type_name[j] ; ++j)
|
|
if (type_name[j] == '_')
|
|
offset = j + 1;
|
|
|
|
/* Print characters in the format string up to the following * % or nul. */
|
|
while ((chars < buf_size) && (ch = *fmtp) && ch != '%')
|
|
{
|
|
buf[chars++] = ch;
|
|
fmtp++;
|
|
}
|
|
|
|
/* Skip past %s */
|
|
if (ch == '%')
|
|
{
|
|
ch = *fmtp++;
|
|
fmtp++;
|
|
}
|
|
|
|
chars += snprintf (&buf[chars], buf_size - chars, "%s", type_name + offset);
|
|
}
|
|
|
|
/* Print trailing characters in the format string, if any */
|
|
while ((chars < buf_size) && (ch = *fmtp))
|
|
{
|
|
buf[chars++] = ch;
|
|
fmtp++;
|
|
}
|
|
|
|
if (chars < buf_size)
|
|
buf[chars++] = '\0';
|
|
else
|
|
buf[buf_size - 1] = '\0';
|
|
|
|
return chars;
|
|
}
|
|
|
|
#define ASM_CHARS_MAX (71)
|
|
|
|
static void
|
|
kvx_print_insn (struct kvxopc * op ATTRIBUTE_UNUSED)
|
|
{
|
|
char asm_str[ASM_CHARS_MAX];
|
|
int chars = insn_syntax (op, asm_str, ASM_CHARS_MAX);
|
|
const char *insn_type = "UNKNOWN";
|
|
const char *insn_mode = "";
|
|
|
|
for (int i = chars - 1; i < ASM_CHARS_MAX - 1; i++)
|
|
asm_str[i] = '-';
|
|
|
|
/* This is a hack which works because the Bundling is the same for all cores
|
|
for now. */
|
|
switch ((int) op->bundling)
|
|
{
|
|
case Bundling_kv3_v1_ALL:
|
|
insn_type = "ALL ";
|
|
break;
|
|
case Bundling_kv3_v1_BCU:
|
|
insn_type = "BCU ";
|
|
break;
|
|
case Bundling_kv3_v1_TCA:
|
|
insn_type = "TCA ";
|
|
break;
|
|
case Bundling_kv3_v1_FULL:
|
|
case Bundling_kv3_v1_FULL_X:
|
|
case Bundling_kv3_v1_FULL_Y:
|
|
insn_type = "FULL ";
|
|
break;
|
|
case Bundling_kv3_v1_LITE:
|
|
case Bundling_kv3_v1_LITE_X:
|
|
case Bundling_kv3_v1_LITE_Y:
|
|
insn_type = "LITE ";
|
|
break;
|
|
case Bundling_kv3_v1_TINY:
|
|
case Bundling_kv3_v1_TINY_X:
|
|
case Bundling_kv3_v1_TINY_Y:
|
|
insn_type = "TINY ";
|
|
break;
|
|
case Bundling_kv3_v1_MAU:
|
|
case Bundling_kv3_v1_MAU_X:
|
|
case Bundling_kv3_v1_MAU_Y:
|
|
insn_type = "MAU ";
|
|
break;
|
|
case Bundling_kv3_v1_LSU:
|
|
case Bundling_kv3_v1_LSU_X:
|
|
case Bundling_kv3_v1_LSU_Y:
|
|
insn_type = "LSU ";
|
|
break;
|
|
case Bundling_kv3_v1_NOP:
|
|
insn_type = "NOP ";
|
|
break;
|
|
default:
|
|
as_fatal ("Unhandled Bundling class %d", op->bundling);
|
|
}
|
|
|
|
if (op->codewords[0].flags & kvxOPCODE_FLAG_MODE64
|
|
&& op->codewords[0].flags & kvxOPCODE_FLAG_MODE32)
|
|
insn_mode = "32 and 64";
|
|
else if (op->codewords[0].flags & kvxOPCODE_FLAG_MODE64)
|
|
insn_mode = "64";
|
|
else if (op->codewords[0].flags & kvxOPCODE_FLAG_MODE32)
|
|
insn_mode = "32";
|
|
else
|
|
as_fatal ("Unknown instruction mode.");
|
|
|
|
printf ("%s | syllables: %d | type: %s | mode: %s bits\n", asm_str,
|
|
op->wordcount, insn_type, insn_mode);
|
|
}
|
|
|
|
/* Comparison function compatible with qsort. This is used to sort the issues
|
|
into the right order. */
|
|
static int
|
|
kvxinsn_compare (const void *a, const void *b)
|
|
{
|
|
struct kvxinsn *kvxinsn_a = *(struct kvxinsn **) a;
|
|
struct kvxinsn *kvxinsn_b = *(struct kvxinsn **) b;
|
|
int bundling_a = find_bundling (kvxinsn_a);
|
|
int bundling_b = find_bundling (kvxinsn_b);
|
|
int order_a = kvxinsn_a->order;
|
|
int order_b = kvxinsn_b->order;
|
|
if (bundling_a != bundling_b)
|
|
return (bundling_b < bundling_a) - (bundling_a < bundling_b);
|
|
return (order_b < order_a) - (order_a < order_b);
|
|
}
|
|
|
|
static void
|
|
kvx_reorder_bundle (struct kvxinsn *bundle_insn[], int bundle_insncnt)
|
|
{
|
|
enum
|
|
{ EXU_BCU, EXU_TCA, EXU_ALU0, EXU_ALU1, EXU_MAU, EXU_LSU, EXU__ };
|
|
struct kvxinsn *issued[EXU__];
|
|
int tag, exu;
|
|
|
|
memset (issued, 0, sizeof (issued));
|
|
for (int i = 0; i < bundle_insncnt; i++)
|
|
{
|
|
struct kvxinsn *kvxinsn = bundle_insn[i];
|
|
tag = -1, exu = -1;
|
|
/* This is a hack. It works because all the Bundling are the same for all
|
|
cores for now. */
|
|
switch ((int) find_bundling (kvxinsn))
|
|
{
|
|
case Bundling_kv3_v1_ALL:
|
|
if (bundle_insncnt > 1)
|
|
as_fatal ("Too many ops in a single op bundle");
|
|
issued[0] = kvxinsn;
|
|
break;
|
|
case Bundling_kv3_v1_BCU:
|
|
if (!issued[EXU_BCU])
|
|
issued[EXU_BCU] = kvxinsn;
|
|
else
|
|
as_fatal ("More than one BCU instruction in bundle");
|
|
break;
|
|
case Bundling_kv3_v1_TCA:
|
|
if (!issued[EXU_TCA])
|
|
issued[EXU_TCA] = kvxinsn;
|
|
else
|
|
as_fatal ("More than one TCA instruction in bundle");
|
|
break;
|
|
case Bundling_kv3_v1_FULL:
|
|
case Bundling_kv3_v1_FULL_X:
|
|
case Bundling_kv3_v1_FULL_Y:
|
|
if (!issued[EXU_ALU0])
|
|
{
|
|
issued[EXU_ALU0] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_ALU0;
|
|
exu = EXU_ALU0;
|
|
}
|
|
else
|
|
as_fatal ("More than one ALU FULL instruction in bundle");
|
|
break;
|
|
case Bundling_kv3_v1_LITE:
|
|
case Bundling_kv3_v1_LITE_X:
|
|
case Bundling_kv3_v1_LITE_Y:
|
|
if (!issued[EXU_ALU0])
|
|
{
|
|
issued[EXU_ALU0] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_ALU0;
|
|
exu = EXU_ALU0;
|
|
}
|
|
else if (!issued[EXU_ALU1])
|
|
{
|
|
issued[EXU_ALU1] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_ALU1;
|
|
exu = EXU_ALU1;
|
|
}
|
|
else
|
|
as_fatal ("Too many ALU FULL or LITE instructions in bundle");
|
|
break;
|
|
case Bundling_kv3_v1_MAU:
|
|
case Bundling_kv3_v1_MAU_X:
|
|
case Bundling_kv3_v1_MAU_Y:
|
|
if (!issued[EXU_MAU])
|
|
{
|
|
issued[EXU_MAU] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_MAU;
|
|
exu = EXU_MAU;
|
|
}
|
|
else
|
|
as_fatal ("More than one MAU instruction in bundle");
|
|
break;
|
|
case Bundling_kv3_v1_LSU:
|
|
case Bundling_kv3_v1_LSU_X:
|
|
case Bundling_kv3_v1_LSU_Y:
|
|
if (!issued[EXU_LSU])
|
|
{
|
|
issued[EXU_LSU] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_LSU;
|
|
exu = EXU_LSU;
|
|
}
|
|
else
|
|
as_fatal ("More than one LSU instruction in bundle");
|
|
break;
|
|
case Bundling_kv3_v1_TINY:
|
|
case Bundling_kv3_v1_TINY_X:
|
|
case Bundling_kv3_v1_TINY_Y:
|
|
case Bundling_kv3_v1_NOP:
|
|
if (!issued[EXU_ALU0])
|
|
{
|
|
issued[EXU_ALU0] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_ALU0;
|
|
exu = EXU_ALU0;
|
|
}
|
|
else if (!issued[EXU_ALU1])
|
|
{
|
|
issued[EXU_ALU1] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_ALU1;
|
|
exu = EXU_ALU1;
|
|
}
|
|
else if (!issued[EXU_MAU])
|
|
{
|
|
issued[EXU_MAU] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_MAU;
|
|
exu = EXU_MAU;
|
|
}
|
|
else if (!issued[EXU_LSU])
|
|
{
|
|
issued[EXU_LSU] = kvxinsn;
|
|
tag = Modifier_kv3_v1_exunum_LSU;
|
|
exu = EXU_LSU;
|
|
}
|
|
else
|
|
as_fatal ("Too many ALU instructions in bundle");
|
|
break;
|
|
default:
|
|
as_fatal ("Unhandled Bundling class %d", find_bundling (kvxinsn));
|
|
}
|
|
if (tag >= 0)
|
|
{
|
|
if (issued[exu]->immx0 != NOIMMX)
|
|
immxbuf[issued[exu]->immx0].words[0] |= (tag << 27);
|
|
if (issued[exu]->immx1 != NOIMMX)
|
|
immxbuf[issued[exu]->immx1].words[0] |= (tag << 27);
|
|
}
|
|
}
|
|
|
|
int i;
|
|
for (i = 0, exu = 0; exu < EXU__; exu++)
|
|
{
|
|
if (issued[exu])
|
|
bundle_insn[i++] = issued[exu];
|
|
}
|
|
if (i != bundle_insncnt)
|
|
as_fatal ("Mismatch between bundle and issued instructions");
|
|
}
|
|
|
|
static void
|
|
kvx_check_resource_usage (struct kvxinsn **bundle_insn, int bundle_insncnt)
|
|
{
|
|
const int reservation_table_len =
|
|
(kvx_core_info->reservation_table_lines * kvx_core_info->resource_max);
|
|
const int *resources = kvx_core_info->resources;
|
|
int *resources_used =
|
|
malloc (reservation_table_len * sizeof (int));
|
|
memset (resources_used, 0, reservation_table_len * sizeof (int));
|
|
|
|
for (int i = 0; i < bundle_insncnt; i++)
|
|
{
|
|
int insn_reservation = find_reservation (bundle_insn[i]);
|
|
int reservation = insn_reservation & 0xff;
|
|
const int *reservation_table = kvx_core_info->reservation_table_table[reservation];
|
|
for (int j = 0; j < reservation_table_len; j++)
|
|
resources_used[j] += reservation_table[j];
|
|
}
|
|
|
|
for (int i = 0; i < kvx_core_info->reservation_table_lines; i++)
|
|
{
|
|
for (int j = 0; j < kvx_core_info->resource_max; j++)
|
|
if (resources_used[(i * kvx_core_info->resource_max) + j] > resources[j])
|
|
{
|
|
int v = resources_used[(i * kvx_core_info->resource_max) + j];
|
|
free (resources_used);
|
|
as_fatal ("Resource %s over-used in bundle: %d used, %d available",
|
|
kvx_core_info->resource_names[j], v, resources[j]);
|
|
}
|
|
}
|
|
free (resources_used);
|
|
}
|
|
|
|
/*
|
|
* Called by core to assemble a single line
|
|
*/
|
|
void
|
|
md_assemble (char *line)
|
|
{
|
|
char *line_cursor = line;
|
|
|
|
if (get_byte_counter (now_seg) & 3)
|
|
as_fatal ("code segment not word aligned in md_assemble");
|
|
|
|
while (line_cursor && line_cursor[0] && (line_cursor[0] == ' '))
|
|
line_cursor++;
|
|
|
|
/* ;; was converted to "be" by line hook */
|
|
/* here we look for the bundle end */
|
|
/* and actually output any instructions in bundle */
|
|
/* also we need to implement the stop bit */
|
|
/* check for bundle end */
|
|
if (strncmp (line_cursor, "be", 2) == 0)
|
|
{
|
|
inside_bundle = 0;
|
|
//int sec_align = bfd_get_section_alignment(stdoutput, now_seg);
|
|
/* Was KVXMAXBUNDLEISSUE, changed because of NOPs */
|
|
struct kvxinsn *bundle_insn[KVXMAXBUNDLEWORDS];
|
|
int bundle_insncnt = 0;
|
|
int syllables = 0;
|
|
int entry;
|
|
|
|
#ifdef OBJ_ELF
|
|
/* Emit Dwarf debug line information */
|
|
dwarf2_emit_insn (0);
|
|
#endif
|
|
for (int j = 0; j < insncnt; j++)
|
|
{
|
|
insbuf[j].order = j;
|
|
bundle_insn[bundle_insncnt++] = &insbuf[j];
|
|
syllables += insbuf[j].len;
|
|
}
|
|
|
|
if (syllables + immxcnt > KVXMAXBUNDLEWORDS)
|
|
as_fatal ("Bundle has too many syllables : %d instead of %d",
|
|
syllables + immxcnt, KVXMAXBUNDLEWORDS);
|
|
|
|
if (env.opts.check_resource_usage)
|
|
kvx_check_resource_usage (bundle_insn, bundle_insncnt);
|
|
|
|
/* Reorder and check the bundle. */
|
|
if (!env.opts.generate_illegal_code)
|
|
{
|
|
/* Sort the bundle_insn in order of bundling. */
|
|
qsort (bundle_insn, bundle_insncnt, sizeof (struct kvxinsn *), kvxinsn_compare);
|
|
|
|
kvx_reorder_bundle (bundle_insn, bundle_insncnt);
|
|
}
|
|
|
|
/* The ordering of the insns has been set correctly in bundle_insn. */
|
|
for (int i = 0; i < bundle_insncnt; i++)
|
|
{
|
|
emit_insn (bundle_insn[i], i, (i == bundle_insncnt + immxcnt - 1));
|
|
bundle_insn[i]->written = 1;
|
|
}
|
|
|
|
// Emit immx, ordering them by EXU tags, 0 to 3
|
|
entry = 0;
|
|
for (int tag = 0; tag < 4; tag++)
|
|
{
|
|
for (int j = 0; j < immxcnt; j++)
|
|
{
|
|
#define kv3_exunum2_fld(x) (int)(((unsigned int)(x) >> 27) & 0x3)
|
|
if (kv3_exunum2_fld (immxbuf[j].words[0]) == tag)
|
|
{
|
|
assert (immxbuf[j].written == 0);
|
|
int insn_pos = bundle_insncnt + entry;
|
|
emit_insn (&(immxbuf[j]), insn_pos, entry == immxcnt - 1);
|
|
immxbuf[j].written = 1;
|
|
entry++;
|
|
}
|
|
#undef kv3_exunum2_fld
|
|
}
|
|
}
|
|
if (entry != immxcnt)
|
|
as_fatal ("%d IMMX produced, only %d emitted.", immxcnt, entry);
|
|
|
|
/* The debug label that appear in the middle of bundles
|
|
had better appear to be attached to the next
|
|
bundle. This is because usually these labels point to
|
|
the first instruction where some condition is met. If
|
|
the label isn't handled this way it will be attached to
|
|
the current bundle which is wrong as the corresponding
|
|
instruction wasn't executed yet. */
|
|
while (label_fixes)
|
|
{
|
|
struct label_fix *fix = label_fixes;
|
|
|
|
label_fixes = fix->next;
|
|
symbol_set_value_now (fix->sym);
|
|
free (fix);
|
|
}
|
|
|
|
insncnt = 0;
|
|
immxcnt = 0;
|
|
memset (immxbuf, 0, sizeof (immxbuf));
|
|
|
|
return;
|
|
}
|
|
|
|
char *buf = NULL;
|
|
sscanf (line_cursor, "%m[^\n]", &buf);
|
|
struct token_s my_tok = { .insn = buf, .begin = 0, .end = 0, .class_id = -1 , .val = 0 };
|
|
struct token_list *tok_lst = parse (my_tok);
|
|
free (buf);
|
|
|
|
if (!tok_lst)
|
|
return;
|
|
|
|
/* Skip opcode */
|
|
line_cursor += strlen (tok_lst->tok);
|
|
|
|
assembling_insn = true;
|
|
|
|
inside_bundle = 1;
|
|
assemble_tokens (tok_lst);
|
|
free_token_list (tok_lst);
|
|
assembling_insn = false;
|
|
}
|
|
|
|
static void
|
|
kvx_set_cpu (void)
|
|
{
|
|
if (!kvx_core_info)
|
|
kvx_core_info = &kvx_kv3_v1_core_info;
|
|
|
|
if (!kvx_registers)
|
|
kvx_registers = kvx_kv3_v1_registers;
|
|
|
|
if (!kvx_regfiles)
|
|
kvx_regfiles = kvx_kv3_v1_regfiles;
|
|
|
|
if (!kvx_modifiers)
|
|
kvx_modifiers = kvx_kv3_v1_modifiers;
|
|
|
|
if (env.params.core == -1)
|
|
env.params.core = kvx_core_info->elf_core;
|
|
|
|
int kvx_bfd_mach;
|
|
print_insn = kvx_print_insn;
|
|
|
|
switch (kvx_core_info->elf_core)
|
|
{
|
|
case ELF_KVX_CORE_KV3_1:
|
|
kvx_bfd_mach = env.params.arch_size == 32 ? bfd_mach_kv3_1 : bfd_mach_kv3_1_64;
|
|
setup (ELF_KVX_CORE_KV3_1);
|
|
break;
|
|
case ELF_KVX_CORE_KV3_2:
|
|
kvx_bfd_mach = env.params.arch_size == 32 ? bfd_mach_kv3_2 : bfd_mach_kv3_2_64;
|
|
setup (ELF_KVX_CORE_KV3_2);
|
|
break;
|
|
case ELF_KVX_CORE_KV4_1:
|
|
kvx_bfd_mach = env.params.arch_size == 32 ? bfd_mach_kv4_1 : bfd_mach_kv4_1_64;
|
|
setup (ELF_KVX_CORE_KV4_1);
|
|
break;
|
|
default:
|
|
as_fatal ("Unknown elf core: 0x%x", kvx_core_info->elf_core);
|
|
}
|
|
|
|
if (!bfd_set_arch_mach (stdoutput, TARGET_ARCH, kvx_bfd_mach))
|
|
as_warn (_("could not set architecture and machine"));
|
|
}
|
|
|
|
static int
|
|
kvxop_compar (const void *a, const void *b)
|
|
{
|
|
const struct kvxopc *opa = (const struct kvxopc *) a;
|
|
const struct kvxopc *opb = (const struct kvxopc *) b;
|
|
int res = strcmp (opa->as_op, opb->as_op);
|
|
|
|
if (res)
|
|
return res;
|
|
else
|
|
{
|
|
for (int i = 0; opa->format[i] && opb->format[i]; ++i)
|
|
if (opa->format[i]->width != opb->format[i]->width)
|
|
return opa->format[i]->width - opb->format[i]->width;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/***************************************************/
|
|
/* INITIALIZE ASSEMBLER */
|
|
/***************************************************/
|
|
|
|
static int
|
|
print_hash (void **slot, void *arg ATTRIBUTE_UNUSED)
|
|
{
|
|
string_tuple_t *tuple = *((string_tuple_t **) slot);
|
|
printf ("%s\n", tuple->key);
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
declare_register (const char *name, int number)
|
|
{
|
|
symbolS *regS = symbol_create (name, reg_section,
|
|
&zero_address_frag, number);
|
|
|
|
if (str_hash_insert (env.reg_hash, S_GET_NAME (regS), regS, 0) != NULL)
|
|
as_fatal (_("duplicate %s"), name);
|
|
}
|
|
|
|
void
|
|
md_begin ()
|
|
{
|
|
kvx_set_cpu ();
|
|
|
|
/*
|
|
* Declare register names with symbols
|
|
*/
|
|
|
|
env.reg_hash = str_htab_create ();
|
|
|
|
for (int i = 0; i < kvx_regfiles[KVX_REGFILE_REGISTERS]; i++)
|
|
declare_register (kvx_registers[i].name, kvx_registers[i].id);
|
|
|
|
/* Sort optab, so that identical mnemonics appear consecutively */
|
|
{
|
|
int nel;
|
|
for (nel = 0; !STREQ ("", kvx_core_info->optab[nel].as_op); nel++)
|
|
;
|
|
qsort (kvx_core_info->optab, nel, sizeof (kvx_core_info->optab[0]),
|
|
kvxop_compar);
|
|
}
|
|
|
|
/* The '?' is an operand separator */
|
|
lex_type['?'] = 0;
|
|
|
|
/* Create the opcode hash table */
|
|
/* Each name should appear only once */
|
|
|
|
env.opcode_hash = str_htab_create ();
|
|
env.reloc_hash = str_htab_create ();
|
|
|
|
{
|
|
struct kvxopc *op;
|
|
const char *name = 0;
|
|
for (op = kvx_core_info->optab; !(STREQ ("", op->as_op)); op++)
|
|
{
|
|
/* enter in hash table if this is a new name */
|
|
if (!(STREQ (name, op->as_op)))
|
|
{
|
|
name = op->as_op;
|
|
if (str_hash_insert (env.opcode_hash, name, op, 0))
|
|
as_fatal ("internal error: can't hash opcode `%s'", name);
|
|
}
|
|
|
|
|
|
for (int i = 0 ; op->format[i] ; ++i)
|
|
{
|
|
const char *reloc_name = TOKEN_NAME (op->format[i]->type);
|
|
void *relocs = op->format[i]->relocs;
|
|
if (op->format[i]->relocs[0] != 0
|
|
&& !str_hash_find (env.reloc_hash, reloc_name))
|
|
if (str_hash_insert (env.reloc_hash, reloc_name, relocs, 0))
|
|
as_fatal ("internal error: can't hash type `%s'", reloc_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (env.opts.dump_table)
|
|
{
|
|
htab_traverse (env.opcode_hash, print_hash, NULL);
|
|
exit (0);
|
|
}
|
|
|
|
if (env.opts.dump_insn)
|
|
{
|
|
for (struct kvxopc *op = kvx_core_info->optab; !(STREQ ("", op->as_op)); op++)
|
|
print_insn (op);
|
|
exit (0);
|
|
}
|
|
|
|
/* Here we enforce the minimum section alignment. Remember, in
|
|
* the linker we can make the boudaries between the linked sections
|
|
* on larger boundaries. The text segment is aligned to long words
|
|
* because of the odd/even constraint on immediate extensions
|
|
*/
|
|
|
|
bfd_set_section_alignment (text_section, 3); /* -- 8 bytes */
|
|
bfd_set_section_alignment (data_section, 2); /* -- 4 bytes */
|
|
bfd_set_section_alignment (bss_section, 2); /* -- 4 bytes */
|
|
subseg_set (text_section, 0);
|
|
|
|
symbolS *gotoff_sym = symbol_create (".<gotoff>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *got_sym = symbol_create (".<got>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *plt_sym = symbol_create (".<plt>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *tlsgd_sym = symbol_create (".<tlsgd>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *tlsie_sym = symbol_create (".<tlsie>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *tlsle_sym = symbol_create (".<tlsle>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *tlsld_sym = symbol_create (".<tlsld>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *dtpoff_sym = symbol_create (".<dtpoff>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *plt64_sym = symbol_create (".<plt64>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *gotaddr_sym = symbol_create (".<gotaddr>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *pcrel16_sym = symbol_create (".<pcrel16>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *pcrel_sym = symbol_create (".<pcrel>", undefined_section, &zero_address_frag, 0);
|
|
symbolS *signed32_sym = symbol_create (".<signed32>", undefined_section, &zero_address_frag, 0);
|
|
|
|
for (int i = 0; i < kvx_core_info->nb_pseudo_funcs; ++i)
|
|
{
|
|
symbolS *sym;
|
|
if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "gotoff"))
|
|
sym = gotoff_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "got"))
|
|
sym = got_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "plt"))
|
|
sym = plt_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "tlsgd"))
|
|
sym = tlsgd_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "tlsle"))
|
|
sym = tlsle_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "tlsld"))
|
|
sym = tlsld_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "dtpoff"))
|
|
sym = dtpoff_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "tlsie"))
|
|
sym = tlsie_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "plt64"))
|
|
sym = plt64_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "pcrel16"))
|
|
sym = pcrel16_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "pcrel"))
|
|
sym = pcrel_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "gotaddr"))
|
|
sym = gotaddr_sym;
|
|
else if (!strcmp (kvx_core_info->pseudo_funcs[i].name, "signed32"))
|
|
sym = signed32_sym;
|
|
else
|
|
as_fatal ("internal error: Unknown pseudo func `%s'",
|
|
kvx_core_info->pseudo_funcs[i].name);
|
|
|
|
kvx_core_info->pseudo_funcs[i].sym = sym;
|
|
}
|
|
}
|
|
|
|
/***************************************************/
|
|
/* ASSEMBLER CLEANUP STUFF */
|
|
/***************************************************/
|
|
|
|
/* Return non-zero if the indicated VALUE has overflowed the maximum
|
|
range expressible by a signed number with the indicated number of
|
|
BITS.
|
|
|
|
This is from tc-aarch64.c
|
|
*/
|
|
|
|
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);
|
|
}
|
|
|
|
/***************************************************/
|
|
/* ASSEMBLER FIXUP STUFF */
|
|
/***************************************************/
|
|
|
|
void
|
|
md_apply_fix (fixS * fixP, valueT * valueP, segT segmentP ATTRIBUTE_UNUSED)
|
|
{
|
|
char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
|
|
valueT value = *valueP;
|
|
valueT image;
|
|
arelent *rel;
|
|
|
|
rel = (arelent *) xmalloc (sizeof (arelent));
|
|
|
|
rel->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
|
|
if (rel->howto == NULL)
|
|
{
|
|
as_fatal
|
|
("[md_apply_fix] unsupported relocation type (can't find howto)");
|
|
}
|
|
|
|
/* Note whether this will delete the relocation. */
|
|
if (fixP->fx_addsy == NULL && fixP->fx_pcrel == 0)
|
|
fixP->fx_done = 1;
|
|
|
|
if (fixP->fx_size > 0)
|
|
image = md_chars_to_number (fixpos, fixP->fx_size);
|
|
else
|
|
image = 0;
|
|
if (fixP->fx_addsy != NULL)
|
|
{
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
case BFD_RELOC_KVX_S37_TLS_LE_UP27:
|
|
case BFD_RELOC_KVX_S37_TLS_LE_LO10:
|
|
|
|
case BFD_RELOC_KVX_S43_TLS_LE_EX6:
|
|
case BFD_RELOC_KVX_S43_TLS_LE_UP27:
|
|
case BFD_RELOC_KVX_S43_TLS_LE_LO10:
|
|
|
|
case BFD_RELOC_KVX_S37_TLS_GD_LO10:
|
|
case BFD_RELOC_KVX_S37_TLS_GD_UP27:
|
|
|
|
case BFD_RELOC_KVX_S43_TLS_GD_LO10:
|
|
case BFD_RELOC_KVX_S43_TLS_GD_UP27:
|
|
case BFD_RELOC_KVX_S43_TLS_GD_EX6:
|
|
|
|
case BFD_RELOC_KVX_S37_TLS_IE_LO10:
|
|
case BFD_RELOC_KVX_S37_TLS_IE_UP27:
|
|
|
|
case BFD_RELOC_KVX_S43_TLS_IE_LO10:
|
|
case BFD_RELOC_KVX_S43_TLS_IE_UP27:
|
|
case BFD_RELOC_KVX_S43_TLS_IE_EX6:
|
|
|
|
case BFD_RELOC_KVX_S37_TLS_LD_LO10:
|
|
case BFD_RELOC_KVX_S37_TLS_LD_UP27:
|
|
|
|
case BFD_RELOC_KVX_S43_TLS_LD_LO10:
|
|
case BFD_RELOC_KVX_S43_TLS_LD_UP27:
|
|
case BFD_RELOC_KVX_S43_TLS_LD_EX6:
|
|
|
|
S_SET_THREAD_LOCAL (fixP->fx_addsy);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If relocation has been marked for deletion, apply remaining changes */
|
|
if (fixP->fx_done)
|
|
{
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
case BFD_RELOC_8:
|
|
case BFD_RELOC_16:
|
|
case BFD_RELOC_32:
|
|
case BFD_RELOC_64:
|
|
|
|
case BFD_RELOC_KVX_GLOB_DAT:
|
|
case BFD_RELOC_KVX_32_GOT:
|
|
case BFD_RELOC_KVX_64_GOT:
|
|
case BFD_RELOC_KVX_64_GOTOFF:
|
|
case BFD_RELOC_KVX_32_GOTOFF:
|
|
image = value;
|
|
md_number_to_chars (fixpos, image, fixP->fx_size);
|
|
break;
|
|
|
|
case BFD_RELOC_KVX_PCREL17:
|
|
if (signed_overflow (value, 17 + 2))
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("branch out of range"));
|
|
goto pcrel_common;
|
|
|
|
case BFD_RELOC_KVX_PCREL27:
|
|
if (signed_overflow (value, 27 + 2))
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("branch out of range"));
|
|
goto pcrel_common;
|
|
|
|
case BFD_RELOC_KVX_S16_PCREL:
|
|
if (signed_overflow (value, 16))
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("signed16 PCREL value out of range"));
|
|
goto pcrel_common;
|
|
|
|
case BFD_RELOC_KVX_S43_PCREL_LO10:
|
|
case BFD_RELOC_KVX_S43_PCREL_UP27:
|
|
case BFD_RELOC_KVX_S43_PCREL_EX6:
|
|
if (signed_overflow (value, 10 + 27 + 6))
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("signed43 PCREL value out of range"));
|
|
goto pcrel_common;
|
|
|
|
case BFD_RELOC_KVX_S37_PCREL_LO10:
|
|
case BFD_RELOC_KVX_S37_PCREL_UP27:
|
|
if (signed_overflow (value, 10 + 27))
|
|
as_bad_where (fixP->fx_file, fixP->fx_line,
|
|
_("signed37 PCREL value out of range"));
|
|
goto pcrel_common;
|
|
|
|
case BFD_RELOC_KVX_S64_PCREL_LO10:
|
|
case BFD_RELOC_KVX_S64_PCREL_UP27:
|
|
case BFD_RELOC_KVX_S64_PCREL_EX27:
|
|
|
|
pcrel_common:
|
|
if (fixP->fx_pcrel || fixP->fx_addsy)
|
|
return;
|
|
value =
|
|
(((value >> rel->howto->rightshift) << rel->howto->bitpos) & rel->
|
|
howto->dst_mask);
|
|
image = (image & ~(rel->howto->dst_mask)) | value;
|
|
md_number_to_chars (fixpos, image, fixP->fx_size);
|
|
break;
|
|
|
|
case BFD_RELOC_KVX_S64_GOTADDR_LO10:
|
|
case BFD_RELOC_KVX_S64_GOTADDR_UP27:
|
|
case BFD_RELOC_KVX_S64_GOTADDR_EX27:
|
|
|
|
case BFD_RELOC_KVX_S43_GOTADDR_LO10:
|
|
case BFD_RELOC_KVX_S43_GOTADDR_UP27:
|
|
case BFD_RELOC_KVX_S43_GOTADDR_EX6:
|
|
|
|
case BFD_RELOC_KVX_S37_GOTADDR_LO10:
|
|
case BFD_RELOC_KVX_S37_GOTADDR_UP27:
|
|
value = 0;
|
|
/* Fallthrough */
|
|
|
|
case BFD_RELOC_KVX_S32_UP27:
|
|
|
|
case BFD_RELOC_KVX_S37_UP27:
|
|
|
|
case BFD_RELOC_KVX_S43_UP27:
|
|
|
|
case BFD_RELOC_KVX_S64_UP27:
|
|
case BFD_RELOC_KVX_S64_EX27:
|
|
case BFD_RELOC_KVX_S64_LO10:
|
|
|
|
case BFD_RELOC_KVX_S43_TLS_LE_UP27:
|
|
case BFD_RELOC_KVX_S43_TLS_LE_EX6:
|
|
|
|
case BFD_RELOC_KVX_S37_TLS_LE_UP27:
|
|
|
|
case BFD_RELOC_KVX_S37_GOTOFF_UP27:
|
|
|
|
case BFD_RELOC_KVX_S43_GOTOFF_UP27:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_EX6:
|
|
|
|
case BFD_RELOC_KVX_S43_GOT_UP27:
|
|
case BFD_RELOC_KVX_S43_GOT_EX6:
|
|
|
|
case BFD_RELOC_KVX_S37_GOT_UP27:
|
|
|
|
case BFD_RELOC_KVX_S32_LO5:
|
|
case BFD_RELOC_KVX_S37_LO10:
|
|
|
|
case BFD_RELOC_KVX_S43_LO10:
|
|
case BFD_RELOC_KVX_S43_EX6:
|
|
|
|
case BFD_RELOC_KVX_S43_TLS_LE_LO10:
|
|
case BFD_RELOC_KVX_S37_TLS_LE_LO10:
|
|
|
|
case BFD_RELOC_KVX_S37_GOTOFF_LO10:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_LO10:
|
|
|
|
case BFD_RELOC_KVX_S43_GOT_LO10:
|
|
case BFD_RELOC_KVX_S37_GOT_LO10:
|
|
|
|
default:
|
|
as_fatal ("[md_apply_fix]:"
|
|
"unsupported relocation type (type not handled : %d)",
|
|
fixP->fx_r_type);
|
|
}
|
|
}
|
|
xfree (rel);
|
|
}
|
|
|
|
/*
|
|
* Warning: Can be called only in fixup_segment() after fx_addsy field
|
|
* has been updated by calling symbol_get_value_expression(...->X_add_symbol)
|
|
*/
|
|
int
|
|
kvx_validate_sub_fix (fixS * fixP)
|
|
{
|
|
segT add_symbol_segment, sub_symbol_segment;
|
|
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
case BFD_RELOC_8:
|
|
case BFD_RELOC_16:
|
|
case BFD_RELOC_32:
|
|
if (fixP->fx_addsy != NULL)
|
|
add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy);
|
|
else
|
|
return 0;
|
|
if (fixP->fx_subsy != NULL)
|
|
sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy);
|
|
else
|
|
return 0;
|
|
|
|
if ((strcmp (S_GET_NAME (fixP->fx_addsy),
|
|
S_GET_NAME (fixP->fx_subsy)) == 0) &&
|
|
(add_symbol_segment == sub_symbol_segment))
|
|
return 1;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This is called whenever some data item (not an instruction) needs a
|
|
* fixup. */
|
|
void
|
|
kvx_cons_fix_new (fragS * f, int where, int nbytes, expressionS * exp,
|
|
bfd_reloc_code_real_type code)
|
|
{
|
|
if (exp->X_op == O_pseudo_fixup)
|
|
{
|
|
exp->X_op = O_symbol;
|
|
struct pseudo_func *pf =
|
|
kvx_get_pseudo_func_data_scn (exp->X_op_symbol);
|
|
assert (pf != NULL);
|
|
code = pf->pseudo_relocs.single;
|
|
|
|
if (code == BFD_RELOC_UNUSED)
|
|
as_fatal ("Unsupported relocation");
|
|
}
|
|
else
|
|
{
|
|
switch (nbytes)
|
|
{
|
|
case 1:
|
|
code = BFD_RELOC_8;
|
|
break;
|
|
case 2:
|
|
code = BFD_RELOC_16;
|
|
break;
|
|
case 4:
|
|
code = BFD_RELOC_32;
|
|
break;
|
|
case 8:
|
|
code = BFD_RELOC_64;
|
|
break;
|
|
default:
|
|
as_fatal ("unsupported BFD relocation size %u", nbytes);
|
|
break;
|
|
}
|
|
}
|
|
fix_new_exp (f, where, nbytes, exp, 0, code);
|
|
}
|
|
|
|
/*
|
|
* generate a relocation record
|
|
*/
|
|
|
|
arelent *
|
|
tc_gen_reloc (asection * sec 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;
|
|
if (code == BFD_RELOC_32 && fixp->fx_pcrel)
|
|
code = BFD_RELOC_32_PCREL;
|
|
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 object file",
|
|
bfd_get_reloc_code_name (code));
|
|
return NULL;
|
|
}
|
|
|
|
// if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
|
|
// {
|
|
// as_fatal ("internal error? cannot generate `%s' relocation",
|
|
// bfd_get_reloc_code_name (code));
|
|
// }
|
|
// assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
|
|
|
|
reloc->addend = fixp->fx_offset;
|
|
|
|
/*
|
|
* Ohhh, this is ugly. The problem is that if this is a local global
|
|
* symbol, the relocation will entirely be performed at link time, not
|
|
* at assembly time. bfd_perform_reloc doesn't know about this sort
|
|
* of thing, and as a result we need to fake it out here.
|
|
*/
|
|
|
|
/* GD I'm not sure what this is used for in the kvx case but it sure */
|
|
/* messes up the relocs when emit_all_relocs is used as they are not */
|
|
/* resolved with respect to a global sysmbol (e.g. .text), and hence */
|
|
/* they are ALWAYS resolved at link time */
|
|
/* FIXME FIXME */
|
|
|
|
/* clarkes: 030827: This code (and the other half of the fix in write.c)
|
|
* have caused problems with the PIC relocations.
|
|
* The root problem is that bfd_install_relocation adds in to the reloc
|
|
* addend the section offset of a symbol defined in the current object.
|
|
* This causes problems on numerous other targets too, and there are
|
|
* several different methods used to get around it:
|
|
* 1. In tc_gen_reloc, subtract off the value that bfd_install_relocation
|
|
* added. That is what we do here, and it is also done the
|
|
* same way for alpha.
|
|
* 2. In md_apply_fix, subtract off the value that bfd_install_relocation
|
|
* will add. This is done on SH (non-ELF) and sparc targets.
|
|
* 3. In the howto structure for the relocations, specify a
|
|
* special function that does not return bfd_reloc_continue.
|
|
* This causes bfd_install_relocaion to terminate before it
|
|
* adds in the symbol offset. This is done on SH ELF targets.
|
|
* Note that on ST200 we specify bfd_elf_generic_reloc as
|
|
* the special function. This will return bfd_reloc_continue
|
|
* only in some circumstances, but in particular if the reloc
|
|
* is marked as partial_inplace in the bfd howto structure, then
|
|
* bfd_elf_generic_reloc will return bfd_reloc_continue.
|
|
* Some ST200 relocations are marked as partial_inplace
|
|
* (this is an error in my opinion because ST200 always uses
|
|
* a separate addend), but some are not. The PIC relocations
|
|
* are not marked as partial_inplace, so for them,
|
|
* bfd_elf_generic_reloc returns bfd_reloc_ok, and the addend
|
|
* is not modified by bfd_install_relocation. The relocations
|
|
* R_KVX_16 and R_KVX_32 are marked partial_inplace, and so for
|
|
* these we need to correct the addend.
|
|
* In the code below, the condition in the emit_all_relocs branch
|
|
* (now moved to write.c) is the inverse of the condition that
|
|
* bfd_elf_generic_reloc uses to short-circuit the code in
|
|
* bfd_install_relocation that modifies the addend. The condition
|
|
* in the else branch match the condition used in the alpha version
|
|
* of tc_gen_reloc (see tc-alpha.c).
|
|
* I do not know why we need to use different conditions in these
|
|
* two branches, it seems to me that the condition should be the same
|
|
* whether or not emit_all_relocs is true.
|
|
* I also do not understand why it was necessary to move the emit_all_relocs
|
|
* condition to write.c.
|
|
*/
|
|
|
|
if (S_IS_EXTERNAL (fixp->fx_addsy) &&
|
|
!S_IS_COMMON (fixp->fx_addsy) && reloc->howto->partial_inplace)
|
|
reloc->addend -= symbol_get_bfdsym (fixp->fx_addsy)->value;
|
|
|
|
return reloc;
|
|
}
|
|
|
|
/* Round up segment to appropriate boundary */
|
|
|
|
valueT
|
|
md_section_align (asection * seg ATTRIBUTE_UNUSED, valueT size)
|
|
{
|
|
#ifndef OBJ_ELF
|
|
/* This is not right for ELF; a.out wants it, and COFF will force
|
|
* the alignment anyways. */
|
|
int align = bfd_get_section_alignment (stdoutput, seg);
|
|
valueT mask = ((valueT) 1 << align) - 1;
|
|
return (size + mask) & ~mask;
|
|
#else
|
|
return size;
|
|
#endif
|
|
}
|
|
|
|
int
|
|
md_estimate_size_before_relax (register fragS * fragP ATTRIBUTE_UNUSED,
|
|
segT segtype ATTRIBUTE_UNUSED)
|
|
{
|
|
as_fatal ("estimate_size_before_relax called");
|
|
}
|
|
|
|
void
|
|
md_convert_frag (bfd * abfd ATTRIBUTE_UNUSED,
|
|
asection * sec ATTRIBUTE_UNUSED,
|
|
fragS * fragp ATTRIBUTE_UNUSED)
|
|
{
|
|
as_fatal ("kvx convert_frag");
|
|
}
|
|
|
|
symbolS *
|
|
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
const char *
|
|
md_atof (int type ATTRIBUTE_UNUSED,
|
|
char *litp ATTRIBUTE_UNUSED, int *sizep ATTRIBUTE_UNUSED)
|
|
{
|
|
return ieee_md_atof (type, litp, sizep, TARGET_BYTES_BIG_ENDIAN);
|
|
}
|
|
|
|
/*
|
|
* calculate the base for a pcrel fixup
|
|
* -- for relocation, we might need to add addend ?
|
|
*/
|
|
|
|
long
|
|
md_pcrel_from (fixS * fixP)
|
|
{
|
|
return (fixP->fx_where + fixP->fx_frag->fr_address);
|
|
}
|
|
|
|
/************************************************************/
|
|
/* Hooks into standard processing -- we hook into label */
|
|
/* handling code to detect double ':' and we hook before */
|
|
/* a line of code is processed to do some simple sed style */
|
|
/* edits. */
|
|
/************************************************************/
|
|
|
|
static symbolS *last_proc_sym = NULL;
|
|
static int update_last_proc_sym = 0;
|
|
|
|
void
|
|
kvx_frob_label (symbolS *sym)
|
|
{
|
|
if (update_last_proc_sym)
|
|
{
|
|
last_proc_sym = sym;
|
|
update_last_proc_sym = 0;
|
|
}
|
|
|
|
if (inside_bundle)
|
|
{
|
|
struct label_fix *fix;
|
|
fix = malloc (sizeof (*fix));
|
|
fix->next = label_fixes;
|
|
fix->sym = sym;
|
|
label_fixes = fix;
|
|
}
|
|
|
|
dwarf2_emit_label (sym);
|
|
}
|
|
|
|
void
|
|
kvx_check_label (symbolS *sym)
|
|
{
|
|
/* Labels followed by a second semi-colon are considered external symbols. */
|
|
if (*input_line_pointer == ':')
|
|
{
|
|
S_SET_EXTERNAL (sym);
|
|
input_line_pointer++;
|
|
}
|
|
}
|
|
|
|
/* Emit single bundle nop. This is needed by .nop asm directive
|
|
* Have to manage end of bundle done usually by start_line_hook
|
|
* using BE pseudo op
|
|
*/
|
|
void
|
|
kvx_emit_single_noop (void)
|
|
{
|
|
char nop[] = "nop";
|
|
char end_of_bundle[] = "be";
|
|
|
|
char *saved_ilp = input_line_pointer;
|
|
md_assemble (nop);
|
|
md_assemble (end_of_bundle);
|
|
input_line_pointer = saved_ilp;
|
|
}
|
|
|
|
/* edit out some syntactic sugar that confuses GAS */
|
|
/* input_line_pointer is guaranteed to point to the */
|
|
/* the current line but may include text from following */
|
|
/* lines. Thus, '\n' must be scanned for as well as '\0' */
|
|
|
|
void
|
|
kvx_md_start_line_hook (void)
|
|
{
|
|
char *t;
|
|
|
|
for (t = input_line_pointer; t && t[0] == ' '; t++);
|
|
|
|
/* Detect illegal syntax patterns:
|
|
* - two bundle ends on the same line: ;; ;;
|
|
* - illegal token: ;;;
|
|
*/
|
|
if (t && (t[0] == ';') && (t[1] == ';'))
|
|
{
|
|
char *tmp_t;
|
|
bool newline_seen = false;
|
|
|
|
if (t[2] == ';')
|
|
as_fatal ("Syntax error: Illegal ;;; token");
|
|
|
|
tmp_t = t + 2;
|
|
|
|
while (tmp_t && tmp_t[0])
|
|
{
|
|
while (tmp_t && tmp_t[0] &&
|
|
((tmp_t[0] == ' ') || (tmp_t[0] == '\n')))
|
|
{
|
|
if (tmp_t[0] == '\n')
|
|
newline_seen = true;
|
|
tmp_t++;
|
|
}
|
|
if (tmp_t[0] == ';' && tmp_t[1] == ';')
|
|
{
|
|
/* if there's no newline between the two bundle stops
|
|
* then raise a syntax error now, otherwise a strange error
|
|
* message from read.c will be raised: "junk at end of line..."
|
|
*/
|
|
if (tmp_t[2] == ';')
|
|
as_fatal ("Syntax error: Illegal ;;; token");
|
|
|
|
if (!newline_seen)
|
|
as_fatal ("Syntax error: More than one bundle stop on a line");
|
|
newline_seen = false; /* reset */
|
|
|
|
/* this is an empty bundle, transform it into an
|
|
* empty statement */
|
|
tmp_t[0] = ';';
|
|
tmp_t[1] = ' ';
|
|
|
|
tmp_t += 2;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* check for bundle end */
|
|
/* we transform these into a special opcode BE */
|
|
/* because gas has ';' hardwired as a statement end */
|
|
if (t && (t[0] == ';') && (t[1] == ';'))
|
|
{
|
|
t[0] = 'B';
|
|
t[1] = 'E';
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
kvx_check_resources (int f)
|
|
{
|
|
env.opts.check_resource_usage = f;
|
|
}
|
|
|
|
/** called before write_object_file */
|
|
void
|
|
kvx_end (void)
|
|
{
|
|
int newflags;
|
|
|
|
if (!env.params.core_set)
|
|
env.params.core = kvx_core_info->elf_core;
|
|
|
|
/* (pp) the flags must be set at once */
|
|
newflags = env.params.core | env.params.abi | env.params.pic_flags;
|
|
|
|
if (env.params.arch_size == 64)
|
|
newflags |= ELF_KVX_ABI_64B_ADDR_BIT;
|
|
|
|
bfd_set_private_flags (stdoutput, newflags);
|
|
|
|
cleanup ();
|
|
|
|
if (inside_bundle && insncnt != 0)
|
|
as_bad ("unexpected end-of-file while processing a bundle."
|
|
" Please check that ;; is on its own line.");
|
|
}
|
|
|
|
static void
|
|
kvx_type (int start ATTRIBUTE_UNUSED)
|
|
{
|
|
char *name;
|
|
char c;
|
|
int type;
|
|
char *typename = NULL;
|
|
symbolS *sym;
|
|
elf_symbol_type *elfsym;
|
|
|
|
c = get_symbol_name (&name);
|
|
sym = symbol_find_or_make (name);
|
|
elfsym = (elf_symbol_type *) symbol_get_bfdsym (sym);
|
|
*input_line_pointer = c;
|
|
|
|
if (!*S_GET_NAME (sym))
|
|
as_bad (_("Missing symbol name in directive"));
|
|
|
|
SKIP_WHITESPACE ();
|
|
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;
|
|
|
|
/* typename = input_line_pointer; */
|
|
/* c = get_symbol_end(); */
|
|
c = get_symbol_name (&typename);
|
|
|
|
type = 0;
|
|
if (strcmp (typename, "function") == 0
|
|
|| strcmp (typename, "STT_FUNC") == 0)
|
|
type = BSF_FUNCTION;
|
|
else if (strcmp (typename, "object") == 0
|
|
|| strcmp (typename, "STT_OBJECT") == 0)
|
|
type = BSF_OBJECT;
|
|
else if (strcmp (typename, "tls_object") == 0
|
|
|| strcmp (typename, "STT_TLS") == 0)
|
|
type = BSF_OBJECT | BSF_THREAD_LOCAL;
|
|
else if (strcmp (typename, "common") == 0
|
|
|| strcmp (typename, "STT_COMMON") == 0)
|
|
type = BSF_ELF_COMMON;
|
|
else if (strcmp (typename, "gnu_unique_object") == 0
|
|
|| strcmp (typename, "STB_GNU_UNIQUE") == 0)
|
|
{
|
|
elf_tdata (stdoutput)->has_gnu_osabi |= elf_gnu_osabi_unique;
|
|
type = BSF_OBJECT | BSF_GNU_UNIQUE;
|
|
}
|
|
else if (strcmp (typename, "notype") == 0
|
|
|| strcmp (typename, "STT_NOTYPE") == 0)
|
|
;
|
|
#ifdef md_elf_symbol_type
|
|
else if ((type = md_elf_symbol_type (typename, sym, elfsym)) != -1)
|
|
;
|
|
#endif
|
|
else
|
|
as_bad (_("unrecognized symbol type \"%s\""), typename);
|
|
|
|
*input_line_pointer = c;
|
|
|
|
if (*input_line_pointer == '"')
|
|
++input_line_pointer;
|
|
|
|
elfsym->symbol.flags |= type;
|
|
symbol_get_bfdsym (sym)->flags |= type;
|
|
|
|
demand_empty_rest_of_line ();
|
|
}
|
|
|
|
#define ENDPROCEXTENSION "$endproc"
|
|
#define MINUSEXPR ".-"
|
|
|
|
static int proc_endp_status = 0;
|
|
|
|
static void
|
|
kvx_endp (int start ATTRIBUTE_UNUSED)
|
|
{
|
|
char c;
|
|
char *name;
|
|
|
|
if (inside_bundle)
|
|
as_warn (".endp directive inside a bundle.");
|
|
/* function name is optionnal and is ignored */
|
|
/* there may be several names separated by commas... */
|
|
while (1)
|
|
{
|
|
SKIP_WHITESPACE ();
|
|
c = get_symbol_name (&name);
|
|
(void) restore_line_pointer (c);
|
|
SKIP_WHITESPACE ();
|
|
if (*input_line_pointer != ',')
|
|
break;
|
|
++input_line_pointer;
|
|
}
|
|
demand_empty_rest_of_line ();
|
|
|
|
if (!proc_endp_status)
|
|
{
|
|
as_warn (".endp directive doesn't follow .proc -- ignoring ");
|
|
return;
|
|
}
|
|
|
|
proc_endp_status = 0;
|
|
|
|
/* TB begin : add BSF_FUNCTION attribute to last_proc_sym symbol */
|
|
if (size_type_function)
|
|
{
|
|
if (!last_proc_sym)
|
|
{
|
|
as_bad ("Cannot set function attributes (bad symbol)");
|
|
return;
|
|
}
|
|
|
|
/* last_proc_sym->symbol.flags |= BSF_FUNCTION; */
|
|
symbol_get_bfdsym (last_proc_sym)->flags |= BSF_FUNCTION;
|
|
/* Add .size funcname,.-funcname in order to add size
|
|
* attribute to the current function */
|
|
{
|
|
const int newdirective_sz =
|
|
strlen (S_GET_NAME (last_proc_sym)) + strlen (MINUSEXPR) + 1;
|
|
char *newdirective = malloc (newdirective_sz);
|
|
char *savep = input_line_pointer;
|
|
expressionS exp;
|
|
|
|
memset (newdirective, 0, newdirective_sz);
|
|
|
|
/* BUILD :".-funcname" expression */
|
|
strcat (newdirective, MINUSEXPR);
|
|
strcat (newdirective, S_GET_NAME (last_proc_sym));
|
|
input_line_pointer = newdirective;
|
|
expression (&exp);
|
|
|
|
if (exp.X_op == O_constant)
|
|
{
|
|
S_SET_SIZE (last_proc_sym, exp.X_add_number);
|
|
if (symbol_get_obj (last_proc_sym)->size)
|
|
{
|
|
xfree (symbol_get_obj (last_proc_sym)->size);
|
|
symbol_get_obj (last_proc_sym)->size = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
symbol_get_obj (last_proc_sym)->size =
|
|
(expressionS *) xmalloc (sizeof (expressionS));
|
|
*symbol_get_obj (last_proc_sym)->size = exp;
|
|
}
|
|
|
|
/* just restore the real input pointer */
|
|
input_line_pointer = savep;
|
|
free (newdirective);
|
|
}
|
|
}
|
|
/* TB end */
|
|
|
|
last_proc_sym = NULL;
|
|
}
|
|
|
|
static void
|
|
kvx_proc (int start ATTRIBUTE_UNUSED)
|
|
{
|
|
char c;
|
|
char *name;
|
|
/* there may be several names separated by commas... */
|
|
while (1)
|
|
{
|
|
SKIP_WHITESPACE ();
|
|
c = get_symbol_name (&name);
|
|
(void) restore_line_pointer (c);
|
|
|
|
SKIP_WHITESPACE ();
|
|
if (*input_line_pointer != ',')
|
|
break;
|
|
++input_line_pointer;
|
|
}
|
|
demand_empty_rest_of_line ();
|
|
|
|
if (proc_endp_status)
|
|
{
|
|
as_warn (".proc follows .proc -- ignoring");
|
|
return;
|
|
}
|
|
|
|
proc_endp_status = 1;
|
|
|
|
/* this code emit a global symbol to mark the end of each function */
|
|
/* the symbol emitted has a name formed by the original function name */
|
|
/* concatenated with $endproc so if _foo is a function name the symbol */
|
|
/* marking the end of it is _foo$endproc */
|
|
/* It is also required for generation of .size directive in kvx_endp() */
|
|
|
|
if (size_type_function)
|
|
update_last_proc_sym = 1;
|
|
}
|
|
|
|
int
|
|
kvx_force_reloc (fixS * fixP)
|
|
{
|
|
symbolS *sym;
|
|
asection *symsec;
|
|
|
|
if (generic_force_reloc (fixP))
|
|
return 1;
|
|
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
case BFD_RELOC_KVX_32_GOTOFF:
|
|
case BFD_RELOC_KVX_S37_GOTOFF_UP27:
|
|
case BFD_RELOC_KVX_S37_GOTOFF_LO10:
|
|
|
|
case BFD_RELOC_KVX_64_GOTOFF:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_UP27:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_LO10:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_EX6:
|
|
|
|
case BFD_RELOC_KVX_32_GOT:
|
|
case BFD_RELOC_KVX_64_GOT:
|
|
case BFD_RELOC_KVX_S37_GOT_UP27:
|
|
case BFD_RELOC_KVX_S37_GOT_LO10:
|
|
|
|
case BFD_RELOC_KVX_GLOB_DAT:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
sym = fixP->fx_addsy;
|
|
if (sym)
|
|
{
|
|
symsec = S_GET_SEGMENT (sym);
|
|
/* if (bfd_is_abs_section (symsec)) return 0; */
|
|
if (!SEG_NORMAL (symsec))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
kvx_force_reloc_sub_same (fixS * fixP, segT sec)
|
|
{
|
|
symbolS *sym;
|
|
asection *symsec;
|
|
const char *sec_name = NULL;
|
|
|
|
if (generic_force_reloc (fixP))
|
|
return 1;
|
|
|
|
switch (fixP->fx_r_type)
|
|
{
|
|
case BFD_RELOC_KVX_32_GOTOFF:
|
|
case BFD_RELOC_KVX_S37_GOTOFF_UP27:
|
|
case BFD_RELOC_KVX_S37_GOTOFF_LO10:
|
|
|
|
case BFD_RELOC_KVX_64_GOTOFF:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_UP27:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_LO10:
|
|
case BFD_RELOC_KVX_S43_GOTOFF_EX6:
|
|
|
|
case BFD_RELOC_KVX_32_GOT:
|
|
case BFD_RELOC_KVX_64_GOT:
|
|
case BFD_RELOC_KVX_S37_GOT_UP27:
|
|
case BFD_RELOC_KVX_S37_GOT_LO10:
|
|
|
|
case BFD_RELOC_KVX_S37_LO10:
|
|
case BFD_RELOC_KVX_S37_UP27:
|
|
|
|
case BFD_RELOC_KVX_GLOB_DAT:
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
sym = fixP->fx_addsy;
|
|
if (sym)
|
|
{
|
|
symsec = S_GET_SEGMENT (sym);
|
|
/* if (bfd_is_abs_section (symsec)) return 0; */
|
|
if (!SEG_NORMAL (symsec))
|
|
return 0;
|
|
|
|
/*
|
|
* for .debug_arrange, .debug_frame, .eh_frame sections, containing
|
|
* expressions of the form "sym2 - sym1 + addend", solve them even when
|
|
* --emit-all-relocs is set. Otherwise, a relocation on two symbols
|
|
* is necessary and fails at elf level. Binopt should not be impacted by
|
|
* the resolution of this relocatable expression on symbols inside a
|
|
* function.
|
|
*/
|
|
sec_name = segment_name (sec);
|
|
if ((strcmp (sec_name, ".eh_frame") == 0) ||
|
|
(strcmp (sec_name, ".except_table") == 0) ||
|
|
(strncmp (sec_name, ".debug_", sizeof (".debug_")) == 0))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* Implement HANDLE_ALIGN. */
|
|
|
|
static void
|
|
kvx_make_nops (char *buf, bfd_vma bytes)
|
|
{
|
|
bfd_vma i = 0;
|
|
unsigned int j;
|
|
|
|
static unsigned int nop_single = 0;
|
|
|
|
if (!nop_single)
|
|
{
|
|
const struct kvxopc *opcode =
|
|
(struct kvxopc *) str_hash_find (env.opcode_hash, "nop");
|
|
|
|
if (opcode == NULL)
|
|
as_fatal
|
|
("internal error: could not find opcode for 'nop' during padding");
|
|
|
|
nop_single = opcode->codewords[0].opcode;
|
|
}
|
|
|
|
/* KVX instructions are always 4-bytes aligned. If we are at a position */
|
|
/* that is not 4 bytes aligned, it means this is not part of an instruction, */
|
|
/* so it is safe to use a zero byte for padding. */
|
|
|
|
for (j = bytes % 4; j > 0; j--)
|
|
buf[i++] = 0;
|
|
|
|
for (j = 0; j < (bytes - i); j += 4)
|
|
{
|
|
unsigned nop = nop_single;
|
|
|
|
// nop has bundle end only if #4 nop or last padding nop.
|
|
// Sets the parallel bit when neither conditions are matched.
|
|
// 4*4 = biggest nop bundle we can get
|
|
// 12 = offset when writting the last nop possible in a 4 nops bundle
|
|
// bytes-i-4 = offset for the last 4-words in the padding
|
|
if (j % (4 * 4) != 12 && j != (bytes - i - 4))
|
|
nop |= PARALLEL_BIT;
|
|
|
|
memcpy (buf + i + j, &nop, sizeof (nop));
|
|
}
|
|
}
|
|
|
|
/* Pads code section with bundle of nops when possible, 0 if not. */
|
|
void
|
|
kvx_handle_align (fragS *fragP)
|
|
{
|
|
switch (fragP->fr_type)
|
|
{
|
|
case rs_align_code:
|
|
{
|
|
bfd_signed_vma bytes = (fragP->fr_next->fr_address
|
|
- fragP->fr_address - fragP->fr_fix);
|
|
char *p = fragP->fr_literal + fragP->fr_fix;
|
|
|
|
if (bytes <= 0)
|
|
break;
|
|
|
|
/* Insert zeros or nops to get 4 byte alignment. */
|
|
kvx_make_nops (p, bytes);
|
|
fragP->fr_fix += bytes;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* This is just used for debugging
|
|
*/
|
|
|
|
ATTRIBUTE_UNUSED
|
|
static void
|
|
print_operand (expressionS * e, FILE * out)
|
|
{
|
|
if (e)
|
|
{
|
|
switch (e->X_op)
|
|
{
|
|
case O_register:
|
|
fprintf (out, "%s", kvx_registers[e->X_add_number].name);
|
|
break;
|
|
|
|
case O_constant:
|
|
if (e->X_add_symbol)
|
|
{
|
|
if (e->X_add_number)
|
|
fprintf (out, "(%s + %d)", S_GET_NAME (e->X_add_symbol),
|
|
(int) e->X_add_number);
|
|
else
|
|
fprintf (out, "%s", S_GET_NAME (e->X_add_symbol));
|
|
}
|
|
else
|
|
fprintf (out, "%d", (int) e->X_add_number);
|
|
break;
|
|
|
|
case O_symbol:
|
|
if (e->X_add_symbol)
|
|
{
|
|
if (e->X_add_number)
|
|
fprintf (out, "(%s + %d)", S_GET_NAME (e->X_add_symbol),
|
|
(int) e->X_add_number);
|
|
else
|
|
fprintf (out, "%s", S_GET_NAME (e->X_add_symbol));
|
|
}
|
|
else
|
|
fprintf (out, "%d", (int) e->X_add_number);
|
|
break;
|
|
|
|
default:
|
|
fprintf (out, "o,ptype-%d", e->X_op);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
kvx_cfi_frame_initial_instructions (void)
|
|
{
|
|
cfi_add_CFA_def_cfa (KVX_SP_REGNO, 0);
|
|
}
|
|
|
|
int
|
|
kvx_regname_to_dw2regnum (const char *regname)
|
|
{
|
|
unsigned int regnum = -1;
|
|
const char *p;
|
|
char *q;
|
|
|
|
if (regname[0] == 'r')
|
|
{
|
|
p = regname + 1;
|
|
regnum = strtoul (p, &q, 10);
|
|
if (p == q || *q || regnum >= 64)
|
|
return -1;
|
|
}
|
|
return regnum;
|
|
}
|