mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-03 04:12:10 +08:00
3bf43af5f7
gas/ChangeLog: * config/tc-kvx.c (md_apply_fix): Free memory at this end.
2670 lines
72 KiB
C
2670 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)
|
|
|
|
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 }
|
|
};
|
|
|
|
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;
|
|
char *end_of_bundle;
|
|
|
|
if (asprintf (&nop, "nop") < 0)
|
|
as_fatal ("%s", xstrerror (errno));
|
|
|
|
if (asprintf (&end_of_bundle, "be") < 0)
|
|
as_fatal ("%s", xstrerror (errno));
|
|
|
|
char *saved_ilp = input_line_pointer;
|
|
md_assemble (nop);
|
|
md_assemble (end_of_bundle);
|
|
input_line_pointer = saved_ilp;
|
|
free (nop);
|
|
free (end_of_bundle);
|
|
}
|
|
|
|
/* 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;
|
|
}
|