/* 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 . */ %{ #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 INTEGER %token IDENTIFIER %type 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 ; %%