Created
January 8, 2012 22:18
-
-
Save pozorvlak/1579912 to your computer and use it in GitHub Desktop.
The Big Function Of Doom
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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