441 lines
9.4 KiB
Text
441 lines
9.4 KiB
Text
/*
|
|
Copyright (C) 2021-2022 Free Software Foundation, Inc.
|
|
|
|
This file is part of GAS, the GNU Assembler.
|
|
|
|
GAS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GAS is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING3. If not,
|
|
see <http://www.gnu.org/licenses/>. */
|
|
%{
|
|
#include "as.h"
|
|
#include "loongarch-lex.h"
|
|
#include "loongarch-parse.h"
|
|
static void yyerror (const char *s ATTRIBUTE_UNUSED)
|
|
{
|
|
};
|
|
int yylex (void);
|
|
|
|
|
|
static struct reloc_info *top, *end;
|
|
|
|
static expressionS const_0 =
|
|
{
|
|
.X_op = O_constant,
|
|
.X_add_number = 0
|
|
};
|
|
|
|
static int
|
|
is_const (struct reloc_info *info)
|
|
{
|
|
return (info->type == BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE
|
|
&& info->value.X_op == O_constant);
|
|
}
|
|
|
|
int
|
|
loongarch_parse_expr (const char *expr,
|
|
struct reloc_info *reloc_stack_top,
|
|
size_t max_reloc_num,
|
|
size_t *reloc_num,
|
|
offsetT *imm)
|
|
{
|
|
int ret;
|
|
struct yy_buffer_state *buffstate;
|
|
top = reloc_stack_top;
|
|
end = top + max_reloc_num;
|
|
buffstate = yy_scan_string (expr);
|
|
ret = yyparse ();
|
|
|
|
if (ret == 0)
|
|
{
|
|
if (is_const (top - 1))
|
|
*imm = (--top)->value.X_add_number;
|
|
else
|
|
*imm = 0;
|
|
*reloc_num = top - reloc_stack_top;
|
|
}
|
|
yy_delete_buffer (buffstate);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
emit_const (offsetT imm)
|
|
{
|
|
if (end <= top)
|
|
as_fatal (_("expr too huge"));
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE;
|
|
top->value.X_op = O_constant;
|
|
top->value.X_add_number = imm;
|
|
top++;
|
|
}
|
|
|
|
static const char *
|
|
my_getExpression (expressionS *ep, const char *str)
|
|
{
|
|
char *save_in, *ret;
|
|
if (*str == ':')
|
|
{
|
|
unsigned long j;
|
|
char *str_1 = (char *) str;
|
|
str_1++;
|
|
j = strtol (str_1, &str_1, 10);
|
|
get_internal_label (ep, j, *str_1 == 'f');
|
|
return NULL;
|
|
}
|
|
save_in = input_line_pointer;
|
|
input_line_pointer = (char *)str;
|
|
expression (ep);
|
|
ret = input_line_pointer;
|
|
input_line_pointer = save_in;
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
reloc (const char *op_c_str, const char *id_c_str, offsetT addend)
|
|
{
|
|
expressionS id_sym_expr;
|
|
|
|
if (end <= top)
|
|
as_fatal (_("expr too huge"));
|
|
|
|
if (id_c_str)
|
|
{
|
|
my_getExpression (&id_sym_expr, id_c_str);
|
|
id_sym_expr.X_add_number += addend;
|
|
}
|
|
else
|
|
{
|
|
id_sym_expr.X_op = O_constant;
|
|
id_sym_expr.X_add_number = addend;
|
|
}
|
|
|
|
if (strcmp (op_c_str, "abs") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_ABSOLUTE;
|
|
top++;
|
|
}
|
|
else if (strcmp (op_c_str, "pcrel") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_PCREL;
|
|
top++;
|
|
}
|
|
else if (strcmp (op_c_str, "gprel") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_GPREL;
|
|
top++;
|
|
}
|
|
else if (strcmp (op_c_str, "tprel") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL;
|
|
top++;
|
|
}
|
|
else if (strcmp (op_c_str, "tlsgot") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT;
|
|
top++;
|
|
}
|
|
else if (strcmp (op_c_str, "tlsgd") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_TLS_GD;
|
|
top++;
|
|
}
|
|
else if (strcmp (op_c_str, "plt") == 0)
|
|
{
|
|
top->value = id_sym_expr;
|
|
top->type = BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL;
|
|
top++;
|
|
}
|
|
else
|
|
as_fatal (_("unknown reloc hint: %s"), op_c_str);
|
|
}
|
|
|
|
static void
|
|
emit_unary (char op)
|
|
{
|
|
struct reloc_info *s_top = top - 1;
|
|
if (is_const (s_top))
|
|
{
|
|
offsetT opr = s_top->value.X_add_number;
|
|
switch (op)
|
|
{
|
|
case '+':
|
|
break;
|
|
case '-':
|
|
opr = -opr;
|
|
break;
|
|
case '~':
|
|
opr = ~opr;
|
|
break;
|
|
case '!':
|
|
opr = !opr;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
s_top->value.X_add_number = opr;
|
|
}
|
|
else
|
|
{
|
|
if (end <= top)
|
|
as_fatal (_("expr too huge"));
|
|
switch (op)
|
|
{
|
|
case '!':
|
|
top->type = BFD_RELOC_LARCH_SOP_NOT;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
top->value = const_0;
|
|
top++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emit_bin (int op)
|
|
{
|
|
struct reloc_info *last_1st = top - 1, *last_2nd = top - 2;
|
|
if (is_const (last_1st) && is_const (last_2nd))
|
|
{
|
|
offsetT opr1 = last_2nd->value.X_add_number;
|
|
offsetT opr2 = last_1st->value.X_add_number;
|
|
switch (op)
|
|
{
|
|
case '*':
|
|
opr1 = opr1 * opr2;
|
|
break;
|
|
case '/':
|
|
opr1 = opr1 / opr2;
|
|
break;
|
|
case '%':
|
|
opr1 = opr1 % opr2;
|
|
break;
|
|
case '+':
|
|
opr1 = opr1 + opr2;
|
|
break;
|
|
case '-':
|
|
opr1 = opr1 - opr2;
|
|
break;
|
|
case LEFT_OP:
|
|
opr1 = opr1 << opr2;
|
|
break;
|
|
case RIGHT_OP:
|
|
/* Algorithm right shift. */
|
|
opr1 = (offsetT)opr1 >> (offsetT)opr2;
|
|
break;
|
|
case '<':
|
|
opr1 = opr1 < opr2;
|
|
break;
|
|
case '>':
|
|
opr1 = opr1 > opr2;
|
|
break;
|
|
case LE_OP:
|
|
opr1 = opr1 <= opr2;
|
|
break;
|
|
case GE_OP:
|
|
opr1 = opr1 >= opr2;
|
|
break;
|
|
case EQ_OP:
|
|
opr1 = opr1 == opr2;
|
|
break;
|
|
case NE_OP:
|
|
opr1 = opr1 != opr2;
|
|
break;
|
|
case '&':
|
|
opr1 = opr1 & opr2;
|
|
break;
|
|
case '^':
|
|
opr1 = opr1 ^ opr2;
|
|
break;
|
|
case '|':
|
|
opr1 = opr1 | opr2;
|
|
break;
|
|
case AND_OP:
|
|
opr1 = opr1 && opr2;
|
|
break;
|
|
case OR_OP:
|
|
opr1 = opr1 || opr2;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
last_2nd->value.X_add_number = opr1;
|
|
last_1st->type = 0;
|
|
top--;
|
|
}
|
|
else
|
|
{
|
|
if (end <= top)
|
|
as_fatal (_("expr too huge"));
|
|
switch (op)
|
|
{
|
|
case '+':
|
|
top->type = BFD_RELOC_LARCH_SOP_ADD;
|
|
break;
|
|
case '-':
|
|
top->type = BFD_RELOC_LARCH_SOP_SUB;
|
|
break;
|
|
case LEFT_OP:
|
|
top->type = BFD_RELOC_LARCH_SOP_SL;
|
|
break;
|
|
case RIGHT_OP:
|
|
top->type = BFD_RELOC_LARCH_SOP_SR;
|
|
break;
|
|
case '&':
|
|
top->type = BFD_RELOC_LARCH_SOP_AND;
|
|
break;
|
|
default:
|
|
abort ();
|
|
}
|
|
top->value = const_0;
|
|
top++;
|
|
}
|
|
}
|
|
|
|
static void
|
|
emit_if_else (void)
|
|
{
|
|
struct reloc_info *last_1st = top - 1;
|
|
struct reloc_info *last_2nd = top - 2;
|
|
struct reloc_info *last_3rd = top - 3;
|
|
if (is_const (last_1st) && is_const (last_2nd) && is_const (last_3rd))
|
|
{
|
|
offsetT opr1 = last_3rd->value.X_add_number;
|
|
offsetT opr2 = last_2nd->value.X_add_number;
|
|
offsetT opr3 = last_1st->value.X_add_number;
|
|
opr1 = opr1 ? opr2 : opr3;
|
|
last_3rd->value.X_add_number = opr1;
|
|
last_2nd->type = 0;
|
|
last_1st->type = 0;
|
|
top -= 2;
|
|
}
|
|
else
|
|
{
|
|
if (end <= top)
|
|
as_fatal (_("expr too huge"));
|
|
top->type = BFD_RELOC_LARCH_SOP_IF_ELSE;
|
|
top->value = const_0;
|
|
top++;
|
|
}
|
|
}
|
|
|
|
%}
|
|
|
|
%union {
|
|
char *c_str;
|
|
offsetT imm;
|
|
}
|
|
|
|
%token <imm> INTEGER
|
|
%token <c_str> IDENTIFIER
|
|
%type <imm> addend
|
|
|
|
%token LEFT_OP RIGHT_OP LE_OP GE_OP EQ_OP NE_OP AND_OP OR_OP
|
|
%start expression
|
|
%%
|
|
|
|
primary_expression
|
|
: INTEGER {emit_const ($1);}
|
|
| '(' expression ')'
|
|
| '%' IDENTIFIER '(' IDENTIFIER addend ')' {reloc ($2, $4, $5); free ($2); free ($4);}
|
|
| '%' IDENTIFIER '(' INTEGER addend ')' {reloc ($2, NULL, $4 + $5); free ($2);}
|
|
;
|
|
|
|
addend
|
|
: addend '-' INTEGER {$$ -= $3;}
|
|
| addend '+' INTEGER {$$ += $3;}
|
|
| {$$ = 0;}
|
|
;
|
|
|
|
unary_expression
|
|
: primary_expression
|
|
| '+' unary_expression {emit_unary ('+');}
|
|
| '-' unary_expression {emit_unary ('-');}
|
|
| '~' unary_expression {emit_unary ('~');}
|
|
| '!' unary_expression {emit_unary ('!');}
|
|
;
|
|
|
|
multiplicative_expression
|
|
: unary_expression
|
|
| multiplicative_expression '*' unary_expression {emit_bin ('*');}
|
|
| multiplicative_expression '/' unary_expression {emit_bin ('/');}
|
|
| multiplicative_expression '%' unary_expression {emit_bin ('%');}
|
|
;
|
|
|
|
additive_expression
|
|
: multiplicative_expression
|
|
| additive_expression '+' multiplicative_expression {emit_bin ('+');}
|
|
| additive_expression '-' multiplicative_expression {emit_bin ('-');}
|
|
;
|
|
|
|
shift_expression
|
|
: additive_expression
|
|
| shift_expression LEFT_OP additive_expression {emit_bin (LEFT_OP);}
|
|
| shift_expression RIGHT_OP additive_expression {emit_bin (RIGHT_OP);}
|
|
;
|
|
|
|
relational_expression
|
|
: shift_expression
|
|
| relational_expression '<' shift_expression {emit_bin ('<');}
|
|
| relational_expression '>' shift_expression {emit_bin ('>');}
|
|
| relational_expression LE_OP shift_expression {emit_bin (LE_OP);}
|
|
| relational_expression GE_OP shift_expression {emit_bin (GE_OP);}
|
|
;
|
|
|
|
equality_expression
|
|
: relational_expression
|
|
| equality_expression EQ_OP relational_expression {emit_bin (EQ_OP);}
|
|
| equality_expression NE_OP relational_expression {emit_bin (NE_OP);}
|
|
;
|
|
|
|
and_expression
|
|
: equality_expression
|
|
| and_expression '&' equality_expression {emit_bin ('&');}
|
|
;
|
|
|
|
exclusive_or_expression
|
|
: and_expression
|
|
| exclusive_or_expression '^' and_expression {emit_bin ('^');}
|
|
;
|
|
|
|
inclusive_or_expression
|
|
: exclusive_or_expression
|
|
| inclusive_or_expression '|' exclusive_or_expression {emit_bin ('|');}
|
|
;
|
|
|
|
logical_and_expression
|
|
: inclusive_or_expression
|
|
| logical_and_expression AND_OP inclusive_or_expression {emit_bin (AND_OP);}
|
|
;
|
|
|
|
logical_or_expression
|
|
: logical_and_expression
|
|
| logical_or_expression OR_OP logical_and_expression {emit_bin (OR_OP);}
|
|
;
|
|
|
|
conditional_expression
|
|
: logical_or_expression
|
|
| logical_or_expression '?' expression ':' conditional_expression {emit_if_else ();}
|
|
;
|
|
|
|
expression
|
|
: conditional_expression
|
|
;
|
|
%%
|
|
|