Skip to content

Instantly share code, notes, and snippets.

@pozorvlak
Created January 8, 2012 22:18
Show Gist options
  • Save pozorvlak/1579912 to your computer and use it in GitHub Desktop.
Save pozorvlak/1579912 to your computer and use it in GitHub Desktop.
The Big Function Of Doom
/* tc-arc.c -- Assembler for the ARC
Copyright 1994, 1995, 1997, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2006 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to the Free
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
02110-1301, USA. */
/* snip 4760 lines of merely ordinary horribleness */
md_assemble (char *str)
{
const struct arc_opcode *opcode;
const struct arc_opcode *std_opcode;
struct arc_opcode *ext_opcode;
char *start, *s;
char *firstsuf;
const char *last_errmsg = 0;
int lm_present;
arc_insn insn;
long insn2;
static int init_tables_p = 0;
current_special_sym_flag = NO_TYPE;
char insn_name[16];
int insn_name_idx = 0;
// printf( "md_assemble( \"%s\" )\n", str );
assembling_instruction = 1;
/* Opcode table initialization is deferred until here because we have to
wait for a possible .option command. */
if (!init_tables_p)
{
init_opcode_tables (arc_mach_type);
init_tables_p = 1;
}
/* Skip leading white space. */
while (ISSPACE (*str))
str++;
/* Check whether insn being encoded is 16-bit ARCompact insn */
for (s = str; *s && ISALNUM (*s); s++)
{
insn_name[insn_name_idx] = *s;
insn_name_idx++;
}
/* All ARCompact 16 bit instructions have a <operation_name>_s which
* is what we attempt to exploit here .
*/
if ((*s && *s == '_' && *(s+1) == 's') || strcmp(str,"unimp") == 0) /* FIXME: cleanup required */
compact_insn_16 = 1;
else
compact_insn_16 = 0;
/* The instructions are stored in lists hashed by the first letter
* (though we needn't care how they're hashed). Get the first in
* the list.
*/
ext_opcode = arc_ext_opcodes;
std_opcode = arc_opcode_lookup_asm (str);
// fprintf (stderr, "Matching ****** %s *************\n", str);
/* Keep looking until we find a match. */
start = str;
for (opcode = (ext_opcode ? ext_opcode : std_opcode);
opcode != NULL;
opcode = (ARC_OPCODE_NEXT_ASM (opcode)
? ARC_OPCODE_NEXT_ASM (opcode)
: (ext_opcode ? ext_opcode = NULL, std_opcode : NULL)))
{
int past_opcode_p, fc, num_suffixes;
int fix_up_at = 0;
char *syn;
struct arc_fixup fixups[MAX_FIXUPS];
int mods=0;
/* Used as a sanity check. If we need a limm reloc, make sure we ask
for an extra 4 bytes from frag_more. */
int limm_reloc_p;
int ext_suffix_p;
const struct arc_operand_value *insn_suffixes[MAX_SUFFIXES];
int regb_p;
const struct arc_operand_value *regb;
/*
printf( "matching with opcode 0x%x, %lu/%lu[0x%x]\n",
(int)opcode->syntax, opcode->mask, opcode->value,
opcode->flags );
*/
/* Is this opcode supported by the selected cpu? */
if (!arc_opcode_supported (opcode))
continue;
/* If opcode syntax is for 32-bit insn but input is 16-bit insn,
then go for the next opcode */
for (syn = opcode->syntax; *syn && ISALNUM (*syn); syn++);
if (compact_insn_16 && !(*syn && *syn == '_' && *(syn + 1) == 's'))
if (strcmp(opcode->syntax,"unimp") !=0)
/* FIXME: This is too bad a check!!! cleanup required */
continue;
/* Scan the syntax string. If it doesn't match, try the next one. */
arc_opcode_init_insert ();
insn = opcode->value;
insn2 = opcode->value2;
lm_present = 0;
fc = 0;
num_suf.value = 0;
firstsuf = 0;
past_opcode_p = 0;
num_suffixes = 0;
limm_reloc_p = 0;
ext_suffix_p = 0;
regb_p = 0;
regb = NULL;
// fprintf (stderr, "Trying syntax %s\n", opcode->syntax);
/* We don't check for (*str != '\0') here because we want to parse
any trailing fake arguments in the syntax string. */
for (str = start, syn = opcode->syntax; *syn != '\0';)
{
const struct arc_operand *operand;
//printf(" syn=%s str=||%s||insn=%x\n",syn,str,insn);//ejm
/* Non operand chars must match exactly. */
if (*syn != '%' || *++syn == '%')
{
if (*str == *syn || (*syn=='.'&&*str=='!'))
{
if (*syn == ' '){
//printf( "str/syn contained ' '\n" );
past_opcode_p = 1;
}
++syn;
++str;
} else {
//printf( "breaking str/syn loop\n" );
break;
}
//printf( "continuing str/syn loop\n" );
continue;
}
//printf( "opcode selected: (0x%x)\"%s\"\n", (int)opcode,
// opcode->syntax );
if(firstsuf==0)firstsuf = syn-1;
/* We have an operand. Pick out any modifiers. */
mods = 0;
while( ARC_MOD_P (
arc_operands[arc_operand_map[(int) *syn]].flags ) )
{
if (arc_operands[arc_operand_map[(int) *syn]].insert)
#if 1
/* FIXME: Need 'operand' parameter which is
* uninitialized. */
abort ();
#else
(arc_operands[arc_operand_map[(int) *syn]].insert)
(insn, operand, mods, NULL, 0, NULL);
#endif
mods |= (arc_operands[arc_operand_map[(int) *syn]].flags
& ARC_MOD_BITS);
++syn;
} /* end while(ARC_MOD_P(...)) */
operand = arc_operands + arc_operand_map[(int) *syn];
if (operand->fmt == 0){
as_fatal ("unknown syntax format character `%c'", *syn);
} else {
/*printf( "operand: '%c'(%u) / %u\n", operand->fmt,
operand->fmt,
operand->flags )*/;
}
if (operand->flags & ARC_OPERAND_FAKE)
{
const char *errmsg = NULL;
if (operand->insert)
{
insn = (*operand->insert)
(insn,&insn2, operand, mods, NULL, 0, &errmsg);
if (errmsg != (const char *) NULL)
{
last_errmsg = errmsg;
if (operand->flags & ARC_OPERAND_ERROR)
{
as_bad (errmsg);
assembling_instruction = 0;
return;
}
else if (operand->flags & ARC_OPERAND_WARN)
as_warn (errmsg);
break;
}
if (limm_reloc_p
&& (operand->flags && operand->flags & ARC_OPERAND_LIMM)
&& (operand->flags
& (ARC_OPERAND_ABSOLUTE_BRANCH
| ARC_OPERAND_ADDRESS)))
{
fixups[fix_up_at].opindex = arc_operand_map[operand->fmt];
}
}
++syn;
}
/* Are we finished with suffixes? */
else if (!past_opcode_p)
{
int found,negflg;
char c;
char *s, *t;
const struct arc_operand_value *suf, *suffix_end;
struct arc_operand_value *varsuf;
const struct arc_operand_value *suffix = NULL;
if (!(operand->flags & ARC_OPERAND_SUFFIX)){
abort ();
}
//printf( "finished with suffixes\n" );
/* If we're at a space in the input string, we want to
* skip the remaining suffixes. There may be some fake
* ones though, so just go on to try the next one. */
if (*str == ' ')
{
++syn;
continue;
}
s = str;
negflg = 0;
if (mods & ARC_MOD_DOT)
{
negflg = *s=='!';
if (*s != '.'&&*s != '!')
break;
++s;
}
else
{
/* This can happen in "b.nd foo" and we're currently looking
for "%q" (ie: a condition code suffix). */
if (*s == '.')
{
++syn;
continue;
}
}
/* Pick the suffix out and look it up via the hash table. */
for (t = s; *t && ISALNUM (*t); ++t)
continue;
c = *t;
*t = '\0';
found = 0;
suf = NULL;
if(!found && ((insn >> 27) == 0x0a)){
char *restore;
int sum=0;
if(num_suf.type == 0){
int i;
for(i=0;i<256;i++){
if(arc_operands[i].fmt == ']'){
num_suf.type = i;
break;
}
if(arc_operands[i].fmt == 0)break;
}
} /* end if(num_suf.type == 0) */
if(*syn == ']' || *(syn+3) == ']'){
restore = str;
if(*str == '.' || *str == '!')str++;
if((*str == 'i' || *str == 'I') && (*(str+1) >= '0' &&
*(str+1)<='9')){
str++;
sum = 0;
if(*str == '1'){
sum = 1;
str++;
}
if(*str >= '0' && *str <= '9'){
sum = sum*10 + *str-'0';
str++;
}
sum = sum & 0xf;
if(negflg)
sum |= 0x20; //negation flag
suf = &num_suf;
varsuf = &num_suf;
varsuf->value = sum;
insn2 |= sum << 15;
insn2 |= 1 << 29;
lm_present = 1;
if(firstsuf)
syn = firstsuf-1;
found = 1;
}
else
{
if(*str == '0' && *(str+1) == 'x'){
str = str+2;
while(1){
if(*str >= '0' && *str <= '9')
{
sum = (sum << 4) + *str-'0';
str++;
}
else {
if(*str >= 'a' && *str <= 'z'){
sum = (sum <<4) + *str-'a'+10;
str++;
}
else
break;
}
} /* end while(1) */
suf = &num_suf;
varsuf = &num_suf;
/* lane masks accumulate */
varsuf->value |= sum;
found = 1;
if(firstsuf)
syn = firstsuf-1;
insn2 |= sum << 15;
lm_present = 1;
}
else
{
if(*(str) >= '0' && *(str) <= '9'){
while(*str >= '0' && *str <= '9'){
sum = sum*10 + *str-'0';
str++;
}
suf = &num_suf;
varsuf = &num_suf;
/* lane masks accumulate */
varsuf->value |= sum;
found = 1;
if(firstsuf)
syn = firstsuf-1;
insn2 |= sum << 15;
lm_present = 1;
}
else
{
if(*str == 'u'){
str++;
if(*str == 's' || *str == 'S')str++;
found = 1;
sum = 0x20;
insn2 |= sum << 23;
lm_present = 1;
suf=&num_suf;
}
if((*str == 's' || *str == 'S') && found == 0){
found = 1;
str++;
sum = 0x14;
insn2 |= sum << 23;
lm_present = 1;
suf = &num_suf;
}
if((*str == 'l' || *str == 'L') && found == 0){
found = 1;
str++;
sum = 0xc;
if(*str == 'e' || *str == 'E'){
str++;
sum = 0xc;
}
if(*str == 's' || *str == 'S'){
str++;
sum = 0xf;
}
if(*str == 't' || *str == 'T'){
str++;
sum = 0xb;
}
if(*str == 'o' || *str == 'O'){
str++;
sum = 0x5;
}
insn2 |= sum << 23;
lm_present = 1;
suf = &num_suf;
}
if((*str == 'g' || *str == 'G') && found==0){
found = 1;
str++;
sum = 0xa;
if(*str == 'e' || *str == 'E'){
str++;
sum = 0xa;
}
if(*str == 't' || *str == 'T'){
str++;
sum = 0x9;
}
insn2 |= sum << 23;
suf = &num_suf;
lm_present = 1;
}
if((*str == 'h' || *str == 'H') && found==0){
found = 1;
str++;
sum = 0xd;
if(*str == 'i' || *str == 'I'){
str++;
sum = 0xd;
}
if(*str == 's' || *str == 'S'){
str++;
sum = 0x6;
}
insn2 |= sum << 23;
lm_present = 1;
suf = &num_suf;
}
if((*str == 'z' || *str == 'Z') && found == 0){
str++;
insn2 |= 1 << 23;
found = 1;
lm_present = 1;
suf = &num_suf;
}
}
if((*str == 'e' || *str == 'E') && found == 0){
str++;
if(*str == 'q') str++;
insn2 |= 1 << 23;
lm_present = 1;
found = 1;
suf = &num_suf;
}
if((*str == 'f' || *str == 'F') && found == 0){
str++;
insn |= 1 << 15;
found = 1;
suf = &num_suf;
lm_present = 1;
}
if((*str == 'n' || *str == 'N') && found == 0){
str++;
sum = 2;
if(*str == 'z' || *str == 'Z'){
str++;
sum = 2;
}
if(*str == 'e' || *str == 'E'){
str++;
sum = 2;
}
if(*str == 'c' || *str == 'C'){
str++;
sum = 6;
}
insn2 |= sum << 23;
found = 1;
lm_present = 1;
suf = &num_suf;
}
if((*str == 'c' || *str == 'C') && found == 0){
str++;
if(*str == 'c' || *str == 'C')str++;
sum = 6;
found = 1;
insn2 |= sum << 23;
suf = &num_suf;
lm_present = 1;
}
if(!found){
str = restore;
}
}
}
}
} /* end if(!found&&insn>>27==0x0a) */
if(!suf){
if ((suf = get_ext_suffix (s,*syn))){
ext_suffix_p = 1;
}
else
{
suf = hash_find (arc_suffix_hash, s);
}
}
if (!suf)
{
/* This can happen in "blle foo" and we're currently using
the template "b%q%.n %j". The "bl" insn occurs later in
the table so "lle" isn't an illegal suffix. */
*t = c;
break;
}
/* Is it the right type? Note that the same character is used
several times, so we have to examine all of them. This is
relatively efficient as equivalent entries are kept
together. If it's not the right type, don't increment `str'
so we try the next one in the series. */
if (ext_suffix_p && arc_operands[suf->type].fmt == *syn)
{
/* Insert the suffix's value into the insn. */
*t = c;
if (operand->insert)
insn = (*operand->insert) (insn,&insn2, operand,
mods, NULL, suf->value,
NULL);
else
insn |= suf->value << operand->shift;
suffix = suf;
str = t;
found = 1;
}
else
{
*t = c;
suffix_end = arc_suffixes + arc_suffixes_count;
for (suffix = suf;
(suffix < suffix_end
&& strcmp (suffix->name, suf->name) == 0);
++suffix)
{
if (arc_operands[suffix->type].fmt == *syn)
{
/* Insert the suffix's value into the insn. */
if (operand->insert)
insn = (*operand->insert) (insn,&insn2, operand, mods,
NULL, suffix->value,
NULL);
else
insn |= suffix->value << operand->shift;
str = t;
found = 1;
break;
} /* end if(arc_operands[suffix->type].fmt == *syn) */
} /* end for(suffix=suf; ....) */
}
++syn;
if (!found)
/* Wrong type. Just go on to try next insn entry. */
;
else
{
if (num_suffixes == MAX_SUFFIXES)
as_bad ("too many suffixes");
else
insn_suffixes[num_suffixes++] = suffix;
}
}
else
/* This is either a register or an expression of some kind. */
{
char *hold;
const struct arc_operand_value *reg = NULL;
int match_failed = 0;
long value = 0;
expressionS exp;
exp.X_op = O_illegal;
if (operand->flags & ARC_OPERAND_SUFFIX)
abort ();
//printf( "this is a register or expression\n" );
/* Is there anything left to parse?
We don't check for this at the top because we want to
parse any trailing fake arguments in the syntax
string. */
if (is_end_of_line[(unsigned char) *str])
break;
/* Verify the input for the special operands for
* ARCompact ISA */
if (!mach_a4)
{
switch (operand->fmt)
{
case '4':
if (*str == '%')
str++;
if (strncmp (str, "r0", 2))
match_failed = 1;
else if (ISALNUM (*(str + 2)))
match_failed = 1;
break;
case '5':
if (*str == '%')
str++;
if (strncmp (str, "gp", 2))
match_failed = 1;
else if (ISALNUM (*(str + 2)))
match_failed = 1;
break;
case '6':
if (*str == '%')
str++;
if (strncmp (str, "sp", 2))
match_failed = 1;
else if (ISALNUM (*(str + 2)))
match_failed = 1;
break;
case '7':
if (*str == '%')
str++;
if (strncmp (str, "ilink1", 6))
match_failed = 1;
else if (ISALNUM (*(str + 6)))
match_failed = 1;
break;
case '8':
if (*str == '%')
str++;
if (strncmp (str, "ilink2", 6))
match_failed = 1;
else if (ISALNUM (*(str + 6)))
match_failed = 1;
break;
case '9':
if (*str == '%')
str++;
if (strncmp (str, "blink", 5))
match_failed = 1;
else if (ISALNUM (*(str + 5)))
match_failed = 1;
break;
case '!':
if (*str == '%')
str++;
if (strncmp (str, "pcl", 3))
match_failed = 1;
else if (ISALNUM (*(str + 3)))
match_failed = 1;
break;
} /* end switch(operand->fmt) */
if (match_failed) {
//printf( "match failed!\n" );
break;
}
} /* end if(!mach_a4) */
//printf( "end if(!mach_a4)\n" );
{
//printf( "parsing operand\n" );
/* Parse the operand. */
/* Attempt to parse PIC related stuff */
/*
Any identifier prefixed with '@' is treated as a
symbol. However there are a few expressions (or rather
special cases to be handled) viz. ...@gotpc, ...@gotoff,
...@plt and ...@h30. If it is any of these then we have
to do some special "PIC related stuff".
*/
char *tmpbuf = NULL;
hold = input_line_pointer;
tmpbuf = strchr (str, '@');
if (tmpbuf
&& (!strncmp (tmpbuf + 1, "gotpc", 5)
|| !strncmp (tmpbuf + 1, "gotoff", 6)
|| !strncmp (tmpbuf + 1, "plt", 3)
|| !strncmp (tmpbuf + 1, "h30", 3)))
*tmpbuf = 0;
input_line_pointer = str;
expression (&exp);
//printf( "expression created.\n");
if (tmpbuf
&& (!strncmp (tmpbuf + 1, "gotpc", 5)
|| !strncmp (tmpbuf + 1, "gotoff", 6)
|| !strncmp (tmpbuf + 1, "plt", 3)
|| !strncmp (tmpbuf + 1, "h30", 3)))
*tmpbuf = '@';
str = input_line_pointer;
input_line_pointer = hold;
}
if (exp.X_op == O_illegal)
as_bad ("illegal operand");
else if (exp.X_op == O_absent)
as_bad ("missing operand");
else if (exp.X_op == O_constant)
{
value = exp.X_add_number;
/* Ensure that the constant value is within the
operand's limit, for ARCompact ISA */
if (!mach_a4)
{
/* Try next insn syntax, if the current operand being
matched is not a constant operand */
if (!ac_constant_operand (operand))
break;
switch (operand->fmt)
{
case 'u':
if (opcode->flags & ARC_INCR_U6)
value++; /* Incrementing value of u6 for pseudo
mnemonics of BRcc . */
if ((value < 0) || (value > 63)){
match_failed = 1;
}
break;
case 'K':
if ((value < -2048) || (value > 2047))
match_failed = 1;
break;
case 'o':
if ((value < -256) || (value > 255))
match_failed = 1;
break;
case 'e':
if ((value < 0) || (value > 7))
match_failed = 1;
break;
case 'E':
if ((value < 0) || (value > 31))
match_failed = 1;
break;
case 'j':
if ((value < 0) || (value > 127))
match_failed = 1;
break;
case 'J':
if ((value < 0) || (value > 255))
match_failed = 1;
break;
case 'k':
if ((value % 2) || (value < 0) || (value > 63))
match_failed = 1;
break;
case 'l':
if ((value % 4) || (value < 0) || (value > 127))
match_failed = 1;
break;
case 'm':
if ((value % 4) || (value < 0) || (value > 1023))
match_failed = 1;
break;
case 'M':
if ((value < -256) || (value > 255))
match_failed = 1;
break;
case 'O':
if ((value % 2) || (value < -512) || (value > 511))
match_failed = 1;
break;
case 'R':
if ((value % 4) || (value < -1024) || (value > 1023))
match_failed = 1;
break;
case '\24':
if((value > 0x3fff) || (value <-(0x3fff)))
match_failed = 1;
break;
case '\20':
case '\23': /* discarded constant field */
break;
case '\21':
case '\22':
if(value<0||value >0xff)
match_failed = 1;
break;
case '\14': /* signed 12 bit operand */
switch(opcode->flags&(ARC_SIMD_SCALE1
| ARC_SIMD_SCALE2
| ARC_SIMD_SCALE3
| ARC_SIMD_SCALE4)){
case ARC_SIMD_SCALE1:
if((value&0x1)!=0)
as_warn("Offset must be divisible by 2.");
value = value>>1;
if((value>2047)||(value<-2048))
match_failed = 1;
break;
case ARC_SIMD_SCALE2:
if((value&0x3)!=0)
as_warn("Offset must be divisible by 4.");
value = value>>2;
if((value>2047)||(value<-2048))
match_failed = 1;
break;
case ARC_SIMD_SCALE3:
if((value&0x7)!=0)
as_warn("Offset must be divisible by 8.");
value = value>>3;
if((value>2047)||(value<-2048))
match_failed = 1;
break;
case ARC_SIMD_SCALE4:
if((value&0xf)!=0)
as_warn("Offset must be divisible by 16.");
value = value>>4;
if((value>2047)||(value<-2048))
match_failed = 1;
break;
default:;
break;
} /* end switch(opcode->flags&&(...)) */
break;
case '?': /* SIMD Unsigned 8 bit operand */
switch (opcode->flags & (ARC_SIMD_SCALE1
| ARC_SIMD_SCALE2
| ARC_SIMD_SCALE3
| ARC_SIMD_SCALE4))
{
case ARC_SIMD_SCALE1:
if (value != ((value >> 1) << 1))
as_warn ("Offset must be divisible by 2. Truncating last bit ");
value = value >> 1;
break;
case ARC_SIMD_SCALE2:
if (value != ((value >> 2) << 2))
as_warn ("Offset must be divisible by 4. Truncating last 2 bits ");
value = value >> 2;
break;
case ARC_SIMD_SCALE3:
if (value != ((value >> 3) << 3))
as_warn ("Offset must be divisible by 8. Truncating last 3 bits ");
value = value >> 3;
break;
case ARC_SIMD_SCALE4:
if (value != ((value >> 4) << 4))
as_warn ("Offset must be divisible by 16. Truncating last 4 bits ");
value = value >> 4;
break;
default:
;
} /* end switch (opcode->flags&&(ARC_SIMD_SCALE1...))*/
/* for compatibility with corner cases of MetaWare assembler allow to -128 */
if ((value < -128) || (value > 255)){
match_failed = 1;
}
break;
} /* end switch(operand->fmt) */
if (match_failed)
break;
} /* end if(!mach_a4) */
} /* else if(exp.X_op==O_constant ) */
/* For ARCompact ISA, try next insn syntax if the input operand
is a symbol but the current operand being matched is not a
symbol operand */
else if (!mach_a4 && (exp.X_op == O_symbol)
&& !ac_symbol_operand (operand))
{
break;
}
/* For ARCompact ISA, try next insn syntax if "%st" operand is
not being matched with long-immediate operand */
else if (!mach_a4 && (exp.X_op == O_right_shift)
&& (operand->fmt != 'L'))
break;
else if (!mach_a4 && (exp.X_op == O_subtract)
&& (operand->fmt != 'L')
&& ( (insn_name[0] == 'a' || insn_name[0] == 'A') &&
(insn_name[1] == 'd' || insn_name[1] == 'D') &&
(insn_name[2] == 'd' || insn_name[2] == 'D') ) )
{
break;
}
else if (exp.X_op == O_register)
{
//printf( "exp.X_op == O_register\n" );
reg = (struct arc_operand_value *) exp.X_add_number;
if (!mach_a4) /* For ARCompact ISA */
{
/* Try next instruction syntax, if the current operand
being matched is not a register operand. */
if ( !ac_register_operand (operand)
&& !ARC700_register_simd_operand (operand->fmt)
&& !EnCore_register_operand(operand->fmt)
)
{
break;
}
/* For 16-bit insns, select proper register value */
if (compact_insn_16
&& ((operand->fmt == 'a')
|| (operand->fmt == 'b')
|| (operand->fmt == 'c')))
{
int i, l;
for (i = 0; i < arc_reg_names_count; i++)
{
if (!arc_opval_supported (&arc_reg_names[i]))
continue;
l = strlen (arc_reg_names[i].name);
if ((arc_reg_names[i].flags & ARC_REGISTER_16)
&& !strncmp (reg->name,
arc_reg_names[i].name, l)
&& !ISALNUM (*(reg->name + l)))
{
reg = &arc_reg_names[i];
break;
}
} /* end for(i=0;i<arc_reg_names_count; i++ ) */
if (i == arc_reg_names_count)
break;
} /* end if(compact_insn_16...) */
/* Ashwin: For SIMD instructions checking if its any
of the SIMD register.*/
if ( ARC700_register_simd_operand (operand->fmt)
&& !ac_register_operand (operand)
&& !EnCore_register_operand( operand->fmt )
)
{
struct arc_ext_operand_value *ext_oper
= arc_ext_operands;
while (ext_oper)
{
short flg = 0;
switch (ext_oper->operand.flags
& (ARC_REGISTER_SIMD_VR
| ARC_REGISTER_SIMD_I
| ARC_REGISTER_SIMD_K
| ARC_REGISTER_SIMD_DR))
{
case ARC_REGISTER_SIMD_VR:
if ((ARC700_register_simd_operand
(operand->fmt) == 1)
&& !strcmp (reg->name,
ext_oper->operand.name))
flg = 1;
break;
case ARC_REGISTER_SIMD_I:
if ((ARC700_register_simd_operand
(operand->fmt) == 3)
&& !strcmp (reg->name,
ext_oper->operand.name))
flg = 1;
break;
case ARC_REGISTER_SIMD_K:
if ((ARC700_register_simd_operand
(operand->fmt) == 4)
&& !strcmp (reg->name,
ext_oper->operand.name))
flg = 1;
break;
case ARC_REGISTER_SIMD_DR:
if ((ARC700_register_simd_operand
(operand->fmt) == 2)
&& !strcmp (reg->name,
ext_oper->operand.name))
flg = 1;
break;
default:
break;
}
if (flg){
break;
}
ext_oper = ext_oper->next;
} /* end while(ext_oper ) */
if (!ext_oper)
break; /* Move on to next syntax. */
}
if ( EnCore_register_operand (operand->fmt)
&& !ac_register_operand (operand)
&& !ARC700_register_simd_operand( operand->fmt )
)
{
//printf( "this is an EnCore register...\n" );
}
/*Ashwin: Checking if SIMD registers dont try to sub
any of the Core registers. */
if (!ARC700_register_simd_operand (operand->fmt))
{
if ((reg->flags & ARC_REGISTER_SIMD_VR )
|| (reg->flags & ARC_REGISTER_SIMD_I )
|| (reg->flags & ARC_REGISTER_SIMD_K )
|| (reg->flags & ARC_REGISTER_SIMD_DR)
)
break;
}
/* For conditional code instruction (ex: addeq) and
some 16-bit insns, the destination register should
be same as that of first source register. Ensure
that same register is matched for first and second
occurance of the operand's format 'B'(or 'b') in the
instruction's syntax being matched */
/* Added # as destination version of B */
if ((*syn == 'B') || (*syn == 'b') || (*syn == '#'))
{
if (regb_p && regb != reg)
{
break;
}
else
{
regb_p = 1;
regb = reg;
}
}
/* Try next insn syntax, if input operand is a auxiliary
regiser but the current operand being matched is
not a auxiliary register */
if ((arc_operands[reg->type].fmt == 'G')
&& !(mods & ARC_MOD_AUXREG))
break;
}
}
#define IS_REG_DEST_OPERAND(o) (((o) == 'a') || (!mach_a4 && (o) == 'A'))
else if (IS_REG_DEST_OPERAND (*syn))
as_bad ("symbol as destination register");
else
{
int sda_seen_p = 0;
if (!strncmp (str, "@h30", 4))
{
arc_code_symbol (&exp);
str += 4;
}
else
{
if (strchr (str, '@'))
{
if (!strncmp (str, "@gotpc", 6))
{
str += 6;
if (arc_mach_type != bfd_mach_arc_arc700)
as_warn ("PIC not supported for processors prior to ARC 700\n");
else
current_special_sym_flag = GOT_TYPE;
}
else if (!strncmp (str, "@plt", 4))
{
str += 4;
if (arc_mach_type != bfd_mach_arc_arc700)
as_warn ("PIC not supported for processors prior to ARC 700\n");
else
current_special_sym_flag = PLT_TYPE;
}
else if (!strncmp (str, "@gotoff", 7))
{
if (arc_mach_type != bfd_mach_arc_arc700)
as_warn ("PIC not supported for processors prior to ARC 700\n");
else
current_special_sym_flag = GOTOFF_TYPE;
/* Now check for identifier@gotoff+constant */
if (*(str + 7) == '-' || *(str + 7) == '+')
{
char *orig_line = input_line_pointer;
expressionS new_exp;
input_line_pointer = str + 7;
expression (&new_exp);
if (new_exp.X_op == O_constant)
{
exp.X_add_number += new_exp.X_add_number;
str = input_line_pointer;
}
if (input_line_pointer != str)
input_line_pointer = orig_line;
}
else
str += 7;
}
else
{
if (!strncmp (str, "@sda", 3))
{
// fprintf (stderr, "sda seen\n");
if (!(mods & ARC_MOD_SDASYM))
{
// fprintf (stderr, "Error: failed to match\n");
break;
}
sda_seen_p = 1;
current_special_sym_flag = SDA_REF_TYPE;
str += 4;
/* Now check for identifier@sda+constant */
if (*(str) == '-' || *(str) == '+')
{
char *orig_line = input_line_pointer;
expressionS new_exp;
input_line_pointer = str + 1;
expression (&new_exp);
if (new_exp.X_op == O_constant)
{
exp.X_add_number
+= new_exp.X_add_number;
str = input_line_pointer;
}
// if (input_line_pointer != str)
input_line_pointer = orig_line;
}
}
}
/*
In any of the above PIC related cases we would
have to make a GOT symbol if it is NULL
*/
if (GOT_symbol == NULL)
GOT_symbol
= symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
}
else if (mods & ARC_MOD_SDASYM)
{
// fprintf (stderr, "Not the sda syntax string. Trying next ********\n");
break;
}
}
/* We need to generate a fixup for this expression. */
if (fc >= MAX_FIXUPS)
as_fatal ("too many fixups");
fixups[fc].exp = exp;
fixups[fc].modifier_flags = mods;
/* We don't support shimm relocs. break here to force
the assembler to output a limm. */
/*
#define IS_REG_SHIMM_OFFSET(o) ((o) == 'd')
if (IS_REG_SHIMM_OFFSET (*syn))
break;
*/
/* If this is a register constant (IE: one whose
register value gets stored as 61-63) then this
must be a limm. */
/* ??? This bit could use some cleaning up.
Referencing the format chars like this goes
against style. */
if (IS_SYMBOL_OPERAND (*syn))
{
const char *junk;
limm_reloc_p = 1;
/* Save this, we don't yet know what reloc to use. */
fix_up_at = fc;
/* Tell insert_reg we need a limm. This is
needed because the value at this point is
zero, a shimm. */
/* ??? We need a cleaner interface than this. */
(*arc_operands[arc_operand_map['Q']].insert)
(insn, &insn2,operand, mods, reg, 0L, &junk);
fixups[fc].opindex = arc_operand_map[0];
}
else
fixups[fc].opindex = arc_operand_map[(int) *syn];
++fc;
value = 0;
}
/* The sda modifier is allowed only with symbols */
if ((mods & ARC_MOD_SDASYM) && exp.X_op != O_symbol)
break;
//printf( "inserting register/expression into instruction\n" );
/* Insert the register or expression into the instruction.
* Using pre-defined insertion function if set.
*/
if (operand->insert)
{
const char *errmsg = NULL;
insn = (*operand->insert) (insn,&insn2, operand, mods,
reg, (long) value, &errmsg);
if (errmsg != (const char *) NULL)
{
last_errmsg = errmsg;
if (operand->flags & ARC_OPERAND_ERROR)
{
as_bad (errmsg);
assembling_instruction = 0;
return;
}
else if (operand->flags & ARC_OPERAND_WARN)
as_warn (errmsg);
break;
}
}
else if (!mach_a4)
{
switch (operand->fmt)
{
case 'K':
insn |= ((value & 0x3f) << operand->shift);
insn |= ((value >>6 ) & 0x3f);
break;
case 'l':
insn |= (value >> 2) << operand->shift;
break;
case 'E':
insn |= value << operand->shift;
break;
default:
insn |= ((value & ((1 << operand->bits) - 1))
<< operand->shift);
}
}
else
insn |= (value & ((1 << operand->bits) - 1)) << operand->shift;
++syn;
}
} /* end for(str=start,...) */
/* If we're at the end of the syntax string, we're done. */
/* FIXME: try to move this to a separate function. */
if (*syn == '\0')
{
int i;
char *f;
long limm, limm_p;
const char *errmsg=0;
const struct arc_operand *operand;
if(!lm_present && !(opcode->flags & AC_SIMD_SETLM))
insn2 |= (0xff << 15);
if(opcode->flags & ARC_SIMD_ZERVA){
operand = &arc_operands[arc_operand_map[zer_rega.type]];
if(operand->insert){
insn = (*operand->insert) (insn,&insn2, operand, mods,
&zer_rega, (long)zer_rega.value, &errmsg);
}
else
{
insn |= ((zer_rega.value & ((1 << operand->bits) - 1)) <<
operand->shift);
}
}
if(opcode->flags & ARC_SIMD_ZERVB){
operand = &arc_operands[arc_operand_map[zer_regb.type]];
if(operand->insert){
insn = (*operand->insert) (insn,&insn2, operand, mods,
&zer_regb, (long)zer_regb.value, &errmsg);
}
else
{
insn |= ((zer_regb.value & ((1 << operand->bits) - 1)) <<
operand->shift);
}
}
if(opcode->flags & ARC_SIMD_ZERVC){
operand = &arc_operands[arc_operand_map[zer_regc.type]];
if(operand->insert){
insn = (*operand->insert) (insn,&insn2, operand, mods,
&zer_regc, (long)zer_regc.value, &errmsg);
}
else
{
insn |= ((zer_regc.value & ((1 << operand->bits) - 1)) <<
operand->shift);
}
}
if(opcode->flags&ARC_SIMD_SETLM){
insn2 |= (0x3f)<<23;
}
/* For the moment we assume a valid `str' can only contain blanks
now. IE: We needn't try again with a longer version of the
insn and it is assumed that longer versions of insns appear
before shorter ones (eg: lsr r2,r3,1 vs lsr r2,r3). */
while (ISSPACE (*str))
++str;
if (!is_end_of_line[(unsigned char) *str])
as_bad ("junk at end of line: `%s'", str);
/* Is there a limm value? */
limm_p = arc_opcode_limm_p (&limm);
if(insn>>27==0x0a){
limm_p = 1;
limm = insn2;
}
/* Perform various error and warning tests. */
{
static int in_delay_slot_p = 0;
static int prev_insn_needs_cc_nop_p = 0;
/* delay slot type seen */
int delay_slot_type = ARC_DELAY_NONE;
/* conditional execution flag seen */
int conditional = 0;
/* 1 if condition codes are being set */
int cc_set_p = 0;
/* 1 if conditional branch, including `b' "branch always" */
int cond_branch_p = opcode->flags & ARC_OPCODE_COND_BRANCH;
for (i = 0; i < num_suffixes; ++i)
{
switch (arc_operands[insn_suffixes[i]->type].fmt)
{
case 'n':
case 'N':
delay_slot_type = insn_suffixes[i]->value;
break;
case 'q':
conditional = insn_suffixes[i]->value;
if (arc_mach_type != bfd_mach_arc_arc700
&& conditional > 15
&& !ext_suffix_p)
{
/* It is invalid for the ARC 600 and
A5 to have condition codes ss and sc
*/
as_bad ("Invalid condition code \n");
}
break;
case 'f':
cc_set_p = 1;
break;
}
}
insert_last_insn (insn, delay_slot_type, limm_p,
fixups[0].exp.X_add_symbol);
/* Putting an insn with a limm value in a delay slot is supposed
* to be legal, but let's warn the user anyway. Ditto for 8
* byte jumps with delay slots.
*/
if (in_delay_slot_p && limm_p)
as_warn ("8 byte instruction in delay slot");
if (delay_slot_type != ARC_DELAY_NONE
&& limm_p && arc_insn_not_jl (insn)) /* except for jl addr */
as_bad ("8 byte jump instruction with delay slot");
if (in_delay_slot_p)
{
if (!mach_a4)
{
if (ac_branch_or_jump_insn (insn))
as_bad ("branch/jump instruction in delay slot");
else if (ac_lpcc_insn (insn))
as_bad ("lpcc instruction in delay slot");
else if (ARC700_rtie_insn (insn))
as_bad ("rtie instruction in delay slot");
}
if (arc_mach_type != bfd_mach_arc_arc700)
{
if (a4_brk_insn (insn))
as_bad ("brk instruction in delay slot");
else if (ac_brk_s_insn (insn))
as_bad ("brk_s instruction in delay slot");
}
}
if (ac_lpcc_insn (insn))
{
add_loop_target ((fixups[0].exp).X_add_symbol);
}
in_delay_slot_p = (delay_slot_type != ARC_DELAY_NONE) && !limm_p;
/* Warn when a conditional branch immediately follows a set of
the condition codes. Note that this needn't be done if the
insn that sets the condition codes uses a limm. */
if (cond_branch_p && conditional != 0 /* 0 = "always" */
&& prev_insn_needs_cc_nop_p && arc_mach_type == bfd_mach_arc_a5)
as_warn ("conditional branch follows set of flags");
prev_insn_needs_cc_nop_p =
/* FIXME: ??? not required:
(delay_slot_type != ARC_DELAY_NONE) && */
cc_set_p && !limm_p;
}
/* Write out the instruction.
It is important to fetch enough space in one call to `frag_more'.
We use (f - frag_now->fr_literal) to compute where we are and we
don't want frag_now to change between calls. */
//printf( "emitting instruction encoded: 0x%x\n", insn );
if (limm_p)
{
if (compact_insn_16)
{
f = frag_more (6);
md_number_to_chars (f, insn, 2);
md_number_to_chars (f + 2, limm, -4);
dwarf2_emit_insn (6);
}
else
{
f = frag_more (8);
if (mach_a4)
{
md_number_to_chars (f, insn, 4);
md_number_to_chars (f + 4, limm, 4);
}
else
{
md_number_to_chars (f, insn, -4);
md_number_to_chars (f + 4, limm, -4);
}
dwarf2_emit_insn (8);
}
}
else if (limm_reloc_p)
/* We need a limm reloc, but the tables think we don't. */
abort ();
else
{
if (compact_insn_16)
{
f = frag_more (2);
md_number_to_chars (f, insn, 2);
dwarf2_emit_insn (2);
}
else
{
f = frag_more (4);
if (mach_a4)
{
md_number_to_chars (f, insn, 4);
}
else
{
md_number_to_chars (f, insn, -4);
}
dwarf2_emit_insn (4);
}
}
/* Create any fixups. */
for (i = 0; i < fc; ++i)
{
int op_type;
bfd_reloc_code_real_type reloc_type;
int offset = 0; /* offset of the location within the frag where
the fixup occurs. */
int size = 4; /* size of the fixup; mostly used for error
checking */
expressionS exptmp;
const struct arc_operand *operand;
/* Create a fixup for this operand.
At this point we do not use a bfd_reloc_code_real_type for
operands residing in the insn, but instead just use the
operand index. This lets us easily handle fixups for any
operand type, although that is admittedly not a very exciting
feature. We pick a BFD reloc type in md_apply_fix.
Limm values (4 byte immediate "constants") must be treated
normally because they're not part of the actual insn word
and thus the insertion routines don't handle them. */
if (arc_operands[fixups[i].opindex].flags & ARC_OPERAND_LIMM)
{
/* Modify the fixup addend as required by the cpu. */
fixups[i].exp.X_add_number += arc_limm_fixup_adjust (insn);
op_type = fixups[i].opindex;
/* FIXME: can we add this data to the operand table? */
if (op_type == arc_operand_map['L']
|| (mach_a4 && op_type == arc_operand_map['s'])
|| (mach_a4 && op_type == arc_operand_map['o'])
|| (mach_a4 && op_type == arc_operand_map['O']))
{
reloc_type = (mach_a4) ? BFD_RELOC_32 : BFD_RELOC_ARC_32_ME;
GAS_DEBUG_PIC (reloc_type);
}
else if (mach_a4 && (op_type == arc_operand_map['J']))
{
reloc_type = BFD_RELOC_ARC_B26;
GAS_DEBUG_PIC (reloc_type);
}
else
abort ();
reloc_type = get_arc_exp_reloc_type (1, reloc_type,
&fixups[i].exp,
&exptmp);
GAS_DEBUG_PIC (reloc_type);
}
else
{
op_type = get_arc_exp_reloc_type (0, fixups[i].opindex,
&fixups[i].exp, &exptmp);
reloc_type = op_type + (int) BFD_RELOC_UNUSED;
}
switch (current_special_sym_flag)
{
case SDA_REF_TYPE:
reloc_type = arc_get_sda_reloc (insn, compact_insn_16);
break;
case GOT_TYPE:
reloc_type = BFD_RELOC_ARC_GOTPC32;
break;
case PLT_TYPE:
reloc_type = BFD_RELOC_ARC_PLT32;
break;
case GOTOFF_TYPE:
reloc_type = BFD_RELOC_ARC_GOTOFF;
break;
default:
break;
}
operand = &arc_operands[op_type];
/* Calculate appropriate offset and size for the fixup */
if (compact_insn_16)
{
/* If limm is needed */
if ((operand->flags & ARC_OPERAND_LIMM)
&& (!(fixups[i].modifier_flags & ARC_MOD_SDASYM) || ac_add_reg_sdasym_insn (insn)))
{
offset = 2;
}
else
{
size = 2;
}
}
else /* for 32-bit instructions */
{
/* If limm is needed */
if ((operand->flags & ARC_OPERAND_LIMM)
&& (!(fixups[i].modifier_flags & ARC_MOD_SDASYM) || ac_add_reg_sdasym_insn (insn)))
offset = 4;
}
fix_new_exp (frag_now,
((f - frag_now->fr_literal) + offset),
/* + (operand->flags & ARC_OPERAND_LIMM ? 4 : 0)),*/
size,
&exptmp,
(current_special_sym_flag == PLT_TYPE)?0:
(operand->flags & ARC_OPERAND_RELATIVE_BRANCH) != 0,
(bfd_reloc_code_real_type) reloc_type);
}
assembling_instruction = 0;
return;
}
/* Try the next entry. */
}
if (NULL == last_errmsg){
as_bad ("bad instruction `%s'", start);
}
else
as_bad (last_errmsg);
assembling_instruction = 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment