From 4462d7c440247bfcdc464412332bbec13b8aab12 Mon Sep 17 00:00:00 2001 From: liuzhensong Date: Fri, 22 Oct 2021 16:42:06 +0800 Subject: LoongArch gas support 2021-10-22 Chenghua Xu Zhensong Liu Weinan Liu Xiaolin Tang gas/ * Makefile.am: Add LoongArch. * NEWS: Mention LoongArch support. * config/loongarch-lex-wrapper.c: New. * config/loongarch-lex.h: New. * config/loongarch-lex.l: New. * config/loongarch-parse.y: New. * config/tc-loongarch.c: New. * config/tc-loongarch.h: New. * configure.ac: Add LoongArch. * configure.tgt: Likewise. * doc/as.texi: Likewise. * doc/c-loongarch.texi: Likewise. * Makefile.in: Regenerate. * configure: Regenerate. * po/POTFILES.in: Regenerate. gas/testsuite/ * gas/all/gas.exp: Add LoongArch. * gas/elf/elf.exp: Likewise. * gas/loongarch/4opt_op.d: New. * gas/loongarch/4opt_op.s: Likewise. * gas/loongarch/fix_op.d: Likewise. * gas/loongarch/fix_op.s: Likewise. * gas/loongarch/float_op.d: Likewise. * gas/loongarch/float_op.s: Likewise. * gas/loongarch/imm_op.d: Likewise. * gas/loongarch/imm_op.s: Likewise. * gas/loongarch/jmp_op.d: Likewise. * gas/loongarch/jmp_op.s: Likewise. * gas/loongarch/load_store_op.d: Likewise. * gas/loongarch/load_store_op.s: Likewise. * gas/loongarch/loongarch.exp: Likewise. * gas/loongarch/macro_op.d: Likewise. * gas/loongarch/macro_op.s: Likewise. * gas/loongarch/nop.d: Likewise. * gas/loongarch/nop.s: Likewise. * gas/loongarch/privilege_op.d: Likewise. * gas/loongarch/privilege_op.s: Likewise. * gas/loongarch/syscall.d: Likewise. * gas/loongarch/syscall.s: Likewise. * lib/gas-defs.exp: Add LoongArch. --- gas/config/loongarch-lex-wrapper.c | 20 + gas/config/loongarch-lex.h | 34 + gas/config/loongarch-lex.l | 61 ++ gas/config/loongarch-parse.y | 441 ++++++++++++ gas/config/tc-loongarch.c | 1382 ++++++++++++++++++++++++++++++++++++ gas/config/tc-loongarch.h | 91 +++ 6 files changed, 2029 insertions(+) create mode 100644 gas/config/loongarch-lex-wrapper.c create mode 100644 gas/config/loongarch-lex.h create mode 100644 gas/config/loongarch-lex.l create mode 100644 gas/config/loongarch-parse.y create mode 100644 gas/config/tc-loongarch.c create mode 100644 gas/config/tc-loongarch.h (limited to 'gas/config') diff --git a/gas/config/loongarch-lex-wrapper.c b/gas/config/loongarch-lex-wrapper.c new file mode 100644 index 0000000..b3d290d --- /dev/null +++ b/gas/config/loongarch-lex-wrapper.c @@ -0,0 +1,20 @@ +/* Copyright (C) 2021 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 "sysdep.h" +#include "config/loongarch-lex.c" diff --git a/gas/config/loongarch-lex.h b/gas/config/loongarch-lex.h new file mode 100644 index 0000000..e2b3a4e --- /dev/null +++ b/gas/config/loongarch-lex.h @@ -0,0 +1,34 @@ +/* + Copyright (C) 2021 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 . */ + + +struct yy_buffer_state; + + +struct yy_buffer_state *yy_scan_string (const char *); +void yy_delete_buffer (struct yy_buffer_state *b); +void get_internal_label (expressionS *label_expr, + unsigned long label, + int augend); +int +loongarch_parse_expr (const char *expr, + struct reloc_info *reloc_stack_top, + size_t max_reloc_num, + size_t *reloc_num, + offsetT *imm); diff --git a/gas/config/loongarch-lex.l b/gas/config/loongarch-lex.l new file mode 100644 index 0000000..8ced60a --- /dev/null +++ b/gas/config/loongarch-lex.l @@ -0,0 +1,61 @@ +%option noyywrap +/* + Copyright (C) 2021 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-parse.h" + +/* Flex generates static functions "input" & "unput" which are not used. */ +#define YY_NO_INPUT +#define YY_NO_UNPUT +%} + +D [0-9] +/* We consider anything greater than \x7f to be a "letter" for UTF-8 + support. See the lex_type array in ../read.c. */ +L [a-zA-Z_\.\$\x80-\xff] +H [0-9A-Fa-f] + +hex 0[xX]{H}+ +oct 0[0-7]+ +bin 0[bB][01]+ +dec ([1-9]{D}*)|0 +id ({D}+[fb])|({L}({D}|{L})*)|(:{dec}[bf]) +ws [ \t\v\f]+ + +%% + +{dec} { yylval.imm = strtoull (yytext, 0, 0); return INTEGER; } +{hex} { yylval.imm = strtoull (yytext + 2, 0, 16); return INTEGER; } +{bin} { yylval.imm = strtoull (yytext + 2, 0, 2); return INTEGER; } +{oct} { yylval.imm = strtoull (yytext + 1, 0, 8); return INTEGER; } +{id} { yylval.c_str = strdup (yytext);return IDENTIFIER; } +{ws} { } + +">>" { return RIGHT_OP; } +"<<" { return LEFT_OP; } +"&&" { return AND_OP; } +"||" { return OR_OP; } +"<=" { return LE_OP; } +">=" { return GE_OP; } +"==" { return EQ_OP; } +"!=" { return NE_OP; } +. { return yytext[0];} + +%% diff --git a/gas/config/loongarch-parse.y b/gas/config/loongarch-parse.y new file mode 100644 index 0000000..a7477fb --- /dev/null +++ b/gas/config/loongarch-parse.y @@ -0,0 +1,441 @@ +/* + Copyright (C) 2021 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 + ; +%% + diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c new file mode 100644 index 0000000..f7235a1 --- /dev/null +++ b/gas/config/tc-loongarch.c @@ -0,0 +1,1382 @@ +/* tc-loongarch.c -- Assemble for the LoongArch ISA + + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the license, or + (at your option) any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING3. If not, + see . */ + +#include "as.h" +#include "dw2gencfi.h" +#include "loongarch-lex.h" +#include "elf/loongarch.h" +#include "opcode/loongarch.h" +#include "obj-elf.h" +#include +#include +#include +#include + +/* All information about an instruction during assemble. */ +struct loongarch_cl_insn +{ + /* First split string. */ + const char *name; + const char *arg_strs[MAX_ARG_NUM_PLUS_2]; + size_t arg_num; + + /* Second analyze name_str and each actual args string to match the insn + in 'loongarch-opc.c'. And actual args may need be relocated. + We get length of insn. If 'insn_length == 0 && insn_mo->macro != NULL', + it's a macro insntruction and we call 'md_assemble' recursively + after expanding it. */ + int match_now; + int all_match; + + const struct loongarch_opcode *insn; + size_t insn_length; + + offsetT args[MAX_ARG_NUM_PLUS_2]; + struct reloc_info reloc_info[MAX_RELOC_NUMBER_A_INSN]; + size_t reloc_num; + + /* For relax reserved. We not support relax now. + 'insn_length < relax_max_length' means need to relax. + And 'insn_length == relax_max_length' means no need to relax. */ + size_t relax_max_length; + relax_substateT subtype; + + /* Then we get the binary representation of insn + and write it in to section. */ + insn_t insn_bin; + + /* The frag that contains the instruction. */ + struct frag *frag; + /* The offset into FRAG of the first instruction byte. */ + long where; + /* The relocs associated with the instruction, if any. */ + fixS *fixp[MAX_RELOC_NUMBER_A_INSN]; +}; + +#ifndef DEFAULT_ARCH +#define DEFAULT_ARCH "loongarch64" +#endif + +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful. */ +const char comment_chars[] = "#"; + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output. */ +/* Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. */ +/* Also note that C style comments are always supported. */ +const char line_comment_chars[] = "#"; + +/* This array holds machine specific line separator characters. */ +const char line_separator_chars[] = ";"; + +/* Chars that can be used to separate mant from exp in floating point nums. */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant. */ +/* As in 0f12.456. */ +/* or 0d1.2345e12. */ +const char FLT_CHARS[] = "rRsSfFdDxXpP"; + +const char *md_shortopts = "O::g::G:"; + +static const char default_arch[] = DEFAULT_ARCH; + +enum options +{ + OPTION_IGNORE = OPTION_MD_BASE, + + OPTION_ABI, + OPTION_FLOAT_ABI, + + OPTION_FLOAT_ISA, + + OPTION_LA_LOCAL_WITH_ABS, + OPTION_LA_GLOBAL_WITH_PCREL, + OPTION_LA_GLOBAL_WITH_ABS, + + OPTION_END_OF_ENUM, +}; + +struct option md_longopts[] = +{ + { "mabi", required_argument, NULL, OPTION_ABI }, + { "mfloat-abi", required_argument, NULL, OPTION_FLOAT_ABI }, + + { "mfpu", required_argument, NULL, OPTION_FLOAT_ISA }, + + { "mla-local-with-abs", no_argument, NULL, OPTION_LA_LOCAL_WITH_ABS }, + { "mla-global-with-pcrel", no_argument, NULL, OPTION_LA_GLOBAL_WITH_PCREL }, + { "mla-global-with-abs", no_argument, NULL, OPTION_LA_GLOBAL_WITH_ABS }, + + { NULL, no_argument, NULL, 0 } +}; + +size_t md_longopts_size = sizeof (md_longopts); + +int +md_parse_option (int c, const char *arg) +{ + int ret = 1; + switch (c) + { + case OPTION_ABI: + if (strcasecmp (arg, "lp64") == 0) + { + LARCH_opts.ase_abi |= EF_LOONGARCH_ABI_LP64; + LARCH_opts.ase_ilp32 = 1; + LARCH_opts.ase_lp64 = 1; + } + else if (strcasecmp (arg, "ilp32") == 0) + { + LARCH_opts.ase_abi |= EF_LOONGARCH_ABI_ILP32; + LARCH_opts.ase_ilp32 = 1; + } + else + ret = 0; + break; + + case OPTION_FLOAT_ABI: + if (strcasecmp (arg, "soft") == 0) + LARCH_opts.ase_abi |= EF_LOONGARCH_FLOAT_ABI_SOFT; + else if (strcasecmp (arg, "single") == 0) + LARCH_opts.ase_abi |= EF_LOONGARCH_FLOAT_ABI_SINGLE; + else if (strcasecmp (arg, "double") == 0) + LARCH_opts.ase_abi |= EF_LOONGARCH_FLOAT_ABI_DOUBLE; + else + ret = 0; + break; + + case OPTION_FLOAT_ISA: + if (strcasecmp (arg, "soft") == 0) + LARCH_opts.ase_nf = 1; + else if (strcasecmp (arg, "single") == 0) + LARCH_opts.ase_sf = 1; + else if (strcasecmp (arg, "double") == 0) + { + LARCH_opts.ase_sf = 1; + LARCH_opts.ase_df = 1; + } + else + ret = 0; + break; + + case OPTION_LA_LOCAL_WITH_ABS: + LARCH_opts.ase_labs = 1; + break; + + case OPTION_LA_GLOBAL_WITH_PCREL: + LARCH_opts.ase_gpcr = 1; + break; + + case OPTION_LA_GLOBAL_WITH_ABS: + LARCH_opts.ase_gabs = 1; + break; + + case OPTION_IGNORE: + break; + + default: + ret = 0; + break; + } + return ret; +} + +static struct htab *r_htab = NULL; +static struct htab *f_htab = NULL; +static struct htab *c_htab = NULL; +static struct htab *cr_htab = NULL; +static struct htab *v_htab = NULL; +static struct htab *x_htab = NULL; + +void +loongarch_after_parse_args () +{ + /* Set default ABI/ISA LP64. */ + if (!EF_LOONGARCH_IS_LP64(LARCH_opts.ase_abi) + && !EF_LOONGARCH_IS_ILP32(LARCH_opts.ase_abi)) + { + if (strcmp (default_arch, "loongarch64") == 0) + { + LARCH_opts.ase_abi |= EF_LOONGARCH_ABI_LP64; + LARCH_opts.ase_ilp32 = 1; + LARCH_opts.ase_lp64 = 1; + } + else if (strcmp (default_arch, "loongarch32") == 0) + { + LARCH_opts.ase_abi |= EF_LOONGARCH_ABI_ILP32; + LARCH_opts.ase_ilp32 = 1; + } + else + as_bad ("unknown default architecture `%s'", default_arch); + } + + /* Set default ABI double-float. */ + if (!EF_LOONGARCH_IS_SOFT_FLOAT(LARCH_opts.ase_abi) + && !EF_LOONGARCH_IS_SINGLE_FLOAT(LARCH_opts.ase_abi) + && !EF_LOONGARCH_IS_DOUBLE_FLOAT(LARCH_opts.ase_abi)) + LARCH_opts.ase_abi |= EF_LOONGARCH_FLOAT_ABI_DOUBLE; + + /* Set default ISA double-float. */ + if (!LARCH_opts.ase_nf + && !LARCH_opts.ase_sf + && !LARCH_opts.ase_df) + { + LARCH_opts.ase_sf = 1; + LARCH_opts.ase_df = 1; + } + + size_t i; + + assert(LARCH_opts.ase_ilp32); + + /* Init ilp32/lp64 registers names. */ + if (!r_htab) + r_htab = str_htab_create (), str_hash_insert (r_htab, "", 0, 0); + + for (i = 0; i < ARRAY_SIZE (loongarch_r_normal_name); i++) + str_hash_insert (r_htab, loongarch_r_normal_name[i], (void *) (i + 1), 0); + + if (!cr_htab) + cr_htab = str_htab_create (), str_hash_insert (cr_htab, "", 0, 0); + + for (i = 0; i < ARRAY_SIZE (loongarch_cr_normal_name); i++) + str_hash_insert (cr_htab, loongarch_cr_normal_name[i], (void *) (i + 1), 0); + + /* Init single/double float registers names. */ + if (LARCH_opts.ase_sf || LARCH_opts.ase_df) + { + if (!f_htab) + f_htab = str_htab_create (), str_hash_insert (f_htab, "", 0, 0); + + for (i = 0; i < ARRAY_SIZE (loongarch_f_normal_name); i++) + str_hash_insert (f_htab, loongarch_f_normal_name[i], (void *) (i + 1), + 0); + + if (!c_htab) + c_htab = str_htab_create (), str_hash_insert (c_htab, "", 0, 0); + + for (i = 0; i < ARRAY_SIZE (loongarch_c_normal_name); i++) + str_hash_insert (c_htab, loongarch_c_normal_name[i], (void *) (i + 1), + 0); + + } + + /* Init lsx registers names. */ + if (LARCH_opts.ase_lsx) + { + if (!v_htab) + v_htab = str_htab_create (), str_hash_insert (v_htab, "", 0, 0); + for (i = 0; i < ARRAY_SIZE (loongarch_v_normal_name); i++) + str_hash_insert (v_htab, loongarch_v_normal_name[i], (void *) (i + 1), + 0); + } + + /* Init lasx registers names. */ + if (LARCH_opts.ase_lasx) + { + if (!x_htab) + x_htab = str_htab_create (), str_hash_insert (x_htab, "", 0, 0); + for (i = 0; i < ARRAY_SIZE (loongarch_x_normal_name); i++) + str_hash_insert (x_htab, loongarch_x_normal_name[i], (void *) (i + 1), + 0); + } + + /* Init lp64 registers alias. */ + if (LARCH_opts.ase_lp64) + { + for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name); i++) + str_hash_insert (r_htab, loongarch_r_lp64_name[i], (void *) (i + 1), + 0); + for (i = 0; i < ARRAY_SIZE (loongarch_r_lp64_name1); i++) + str_hash_insert (r_htab, loongarch_r_lp64_name1[i], (void *) (i + 1), + 0); + } + + /* Init float-lp64 registers alias */ + if ((LARCH_opts.ase_sf || LARCH_opts.ase_df) && LARCH_opts.ase_lp64) + { + for (i = 0; i < ARRAY_SIZE (loongarch_f_lp64_name); i++) + str_hash_insert (f_htab, loongarch_f_lp64_name[i], + (void *) (i + 1), 0); + for (i = 0; i < ARRAY_SIZE (loongarch_f_lp64_name1); i++) + str_hash_insert (f_htab, loongarch_f_lp64_name1[i], + (void *) (i + 1), 0); + } +} + +const char * +loongarch_target_format () +{ + return LARCH_opts.ase_lp64 ? "elf64-loongarch" : "elf32-loongarch"; +} + +void +md_begin () +{ + const struct loongarch_opcode *it; + struct loongarch_ase *ase; + for (ase = loongarch_ASEs; ase->enabled; ase++) + for (it = ase->opcodes; it->name; it++) + { + if (loongarch_check_format (it->format) != 0) + as_fatal (_("insn name: %s\tformat: %s\tsyntax error"), + it->name, it->format); + if (it->mask == 0 && it->macro == 0) + as_fatal (_("insn name: %s\nformat: %s\nwe want macro but " + "macro is NULL"), + it->name, it->format); + if (it->macro + && loongarch_check_macro (it->format, it->macro) != 0) + as_fatal (_("insn name: %s\nformat: %s\nmacro: %s\tsyntax error"), + it->name, it->format, it->macro); + } + + /* FIXME: expressionS use 'offsetT' as constant, + * we want this is 64-bit type. */ + assert (8 <= sizeof (offsetT)); +} + +static const expressionS const_0 = { .X_op = O_constant, .X_add_number = 0 }; + +static const char * +my_getExpression (expressionS *ep, const char *str) +{ + char *save_in, *ret; + 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 +s_loongarch_align (int arg) +{ + const char *t = input_line_pointer; + while (!is_end_of_line[(unsigned char) *t] && *t != ',') + ++t; + if (*t == ',') + s_align_ptwo (arg); + else + s_align_ptwo (0); +} + +/* Handle the .dtprelword and .dtpreldword pseudo-ops. They generate + a 32-bit or 64-bit DTP-relative relocation (BYTES says which) for + use in DWARF debug information. */ + +static void +s_dtprel (int bytes) +{ + expressionS ex; + char *p; + + expression (&ex); + + if (ex.X_op != O_symbol) + { + as_bad (_("Unsupported use of %s"), + (bytes == 8 ? ".dtpreldword" : ".dtprelword")); + ignore_rest_of_line (); + } + + p = frag_more (bytes); + md_number_to_chars (p, 0, bytes); + fix_new_exp (frag_now, p - frag_now->fr_literal, bytes, &ex, FALSE, + (bytes == 8 + ? BFD_RELOC_LARCH_TLS_DTPREL64 + : BFD_RELOC_LARCH_TLS_DTPREL32)); + + demand_empty_rest_of_line (); +} + +static const pseudo_typeS loongarch_pseudo_table[] = +{ + { "align", s_loongarch_align, -4 }, + { "dword", cons, 8 }, + { "word", cons, 4 }, + { "half", cons, 2 }, + { "dtprelword", s_dtprel, 4 }, + { "dtpreldword", s_dtprel, 8 }, + { NULL, NULL, 0 }, +}; + +void +loongarch_pop_insert (void) +{ + pop_insert (loongarch_pseudo_table); +} + +#define INTERNAL_LABEL_SPECIAL 10 +static unsigned long internal_label_count[INTERNAL_LABEL_SPECIAL] = { 0 }; + +static const char * +loongarch_internal_label_name (unsigned long label, int augend) +{ + static char symbol_name_build[24]; + unsigned long want_label; + char *p; + + want_label = internal_label_count[label] + augend; + + p = symbol_name_build; +#ifdef LOCAL_LABEL_PREFIX + *p++ = LOCAL_LABEL_PREFIX; +#endif + *p++ = 'L'; + for (; label; label /= 10) + *p++ = label % 10 + '0'; + /* Make sure internal label never belong to normal label namespace. */ + *p++ = ':'; + for (; want_label; want_label /= 10) + *p++ = want_label % 10 + '0'; + *p++ = '\0'; + return symbol_name_build; +} + +static void +setup_internal_label_here (unsigned long label) +{ + assert (label < INTERNAL_LABEL_SPECIAL); + internal_label_count[label]++; + colon (loongarch_internal_label_name (label, 0)); +} + +void +get_internal_label (expressionS *label_expr, unsigned long label, + int augend /* 0 for previous, 1 for next. */) +{ + assert (label < INTERNAL_LABEL_SPECIAL); + if (augend == 0 && internal_label_count[label] == 0) + as_fatal (_("internal error: we have no internal label yet")); + label_expr->X_op = O_symbol; + label_expr->X_add_symbol = + symbol_find_or_make (loongarch_internal_label_name (label, augend)); + label_expr->X_add_number = 0; +} + +extern int loongarch_parse_expr (const char *expr, + struct reloc_info *reloc_stack_top, + size_t max_reloc_num, size_t *reloc_num, + offsetT *imm_if_no_reloc); + +static int +is_internal_label (const char *c_str) +{ + do + { + if (*c_str != ':') + break; + c_str++; + if (!('0' <= *c_str && *c_str <= '9')) + break; + while ('0' <= *c_str && *c_str <= '9') + c_str++; + if (*c_str != 'b' && *c_str != 'f') + break; + c_str++; + return *c_str == '\0'; + } + while (0); + return 0; +} + +static int +is_label (const char *c_str) +{ + if (is_internal_label (c_str)) + return 1; + else if ('0' <= *c_str && *c_str <= '9') + { + /* [0-9]+[bf] */ + while ('0' <= *c_str && *c_str <= '9') + c_str++; + return *c_str == 'b' || *c_str == 'f'; + } + else if (is_name_beginner (*c_str)) + { + /* [a-zA-Z\._\$][0-9a-zA-Z\._\$]* */ + c_str++; + while (is_part_of_name (*c_str)) + c_str++; + return *c_str == '\0'; + } + else + return 0; +} + +static int +is_label_with_addend (const char *c_str) +{ + if (is_internal_label (c_str)) + return 1; + else if ('0' <= *c_str && *c_str <= '9') + { + /* [0-9]+[bf] */ + while ('0' <= *c_str && *c_str <= '9') + c_str++; + if (*c_str == 'b' || *c_str == 'f') + c_str++; + else + return 0; + return *c_str == '\0' + || ((*c_str == '-' || *c_str == '+') + && is_unsigned (c_str + 1)); + } + else if (is_name_beginner (*c_str)) + { + /* [a-zA-Z\._\$][0-9a-zA-Z\._\$]* */ + c_str++; + while (is_part_of_name (*c_str)) + c_str++; + return *c_str == '\0' + || ((*c_str == '-' || *c_str == '+') + && is_unsigned (c_str + 1)); + } + else + return 0; +} + +static int32_t +loongarch_args_parser_can_match_arg_helper (char esc_ch1, char esc_ch2, + const char *bit_field, + const char *arg, void *context) +{ + struct loongarch_cl_insn *ip = context; + offsetT imm, ret = 0; + size_t reloc_num_we_have = MAX_RELOC_NUMBER_A_INSN - ip->reloc_num; + size_t reloc_num = 0; + + if (!ip->match_now) + return 0; + + switch (esc_ch1) + { + case 'l': + switch (esc_ch2) + { + default: + ip->match_now = is_label (arg); + if (!ip->match_now && is_label_with_addend (arg)) + as_fatal (_("This label shouldn't be with addend.")); + break; + case 'a': + ip->match_now = is_label_with_addend (arg); + break; + } + break; + case 's': + case 'u': + ip->match_now = + loongarch_parse_expr (arg, ip->reloc_info + ip->reloc_num, + reloc_num_we_have, &reloc_num, &imm) == 0; + + if (!ip->match_now) + break; + + if (esc_ch1 == 's') + switch (esc_ch2) + { + case 'c': + ip->match_now = reloc_num == 0; + break; + } + else + switch (esc_ch2) + { + case 'c': + ip->match_now = reloc_num == 0 && 0 <= imm; + break; + } + + if (!ip->match_now) + break; + + ret = imm; + if (reloc_num) + { + bfd_reloc_code_real_type reloc_type = BFD_RELOC_NONE; + reloc_num_we_have -= reloc_num; + if (reloc_num_we_have == 0) + as_fatal (_("expr too huge") /* Want one more reloc. */); + if (esc_ch1 == 'u') + { + if (strncmp (bit_field, "10:12", strlen ("10:12")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_U_10_12; + } + else if (esc_ch1 == 's') + { + if (strncmp (bit_field, "10:16<<2", strlen ("10:16<<2")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2; + else if (strncmp (bit_field, "0:5|10:16<<2", + strlen ("0:5|10:16<<2")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2; + else if (strncmp (bit_field, "0:10|10:16<<2", + strlen ("0:10|10:16<<2")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2; + else if (strncmp (bit_field, "10:12", strlen ("10:12")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_12; + else if (strncmp (bit_field, "5:20", strlen ("5:20")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_5_20; + else if (strncmp (bit_field, "10:16", strlen ("10:16")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_16; + else if (strncmp (bit_field, "10:5", strlen ("10:5")) == 0) + reloc_type = BFD_RELOC_LARCH_SOP_POP_32_S_10_5; + } + if (reloc_type == BFD_RELOC_NONE) + as_fatal ( + _("not support reloc bit-field\nfmt: %c%c %s\nargs: %s"), + esc_ch1, esc_ch2, bit_field, arg); + reloc_num++; + ip->reloc_num += reloc_num; + ip->reloc_info[ip->reloc_num - 1].type = reloc_type; + ip->reloc_info[ip->reloc_num - 1].value = const_0; + } + break; + case 'r': + imm = (offsetT) str_hash_find (r_htab, arg); + ip->match_now = 0 < imm; + ret = imm - 1; + break; + case 'f': + imm = (offsetT) str_hash_find (f_htab, arg); + ip->match_now = 0 < imm; + ret = imm - 1; + break; + case 'c': + switch (esc_ch2) + { + case 'r': + imm = (offsetT) str_hash_find (cr_htab, arg); + break; + default: + imm = (offsetT) str_hash_find (c_htab, arg); + } + ip->match_now = 0 < imm; + ret = imm - 1; + break; + case 'v': + imm = (offsetT) str_hash_find (v_htab, arg); + ip->match_now = 0 < imm; + ret = imm - 1; + break; + case 'x': + imm = (offsetT) str_hash_find (x_htab, arg); + ip->match_now = 0 < imm; + ret = imm - 1; + break; + case '\0': + ip->all_match = ip->match_now; + ip->insn_length = + ip->insn->mask ? loongarch_insn_length (ip->insn->match) : 0; + /* FIXME: now we have no relax insn. */ + ip->relax_max_length = ip->insn_length; + break; + default: + as_fatal (_("unknown escape")); + } + + do + { + /* Check imm overflow. */ + int bit_width, bits_needed_s, bits_needed_u; + char *t; + + if (!ip->match_now) + break; + + if (0 < reloc_num) + break; + + bit_width = loongarch_get_bit_field_width (bit_field, &t); + + if (bit_width == -1) + /* No specify bit width. */ + break; + + imm = ret; + if (t[0] == '<' && t[1] == '<') + { + int i = strtol (t += 2, &t, 10), j; + for (j = i; 0 < j; j--, imm >>= 1) + if (imm & 1) + as_fatal (_("require imm low %d bit is 0."), i); + } + + if (*t == '+') + imm -= strtol (t, &t, 10); + + bits_needed_s = loongarch_bits_imm_needed (imm, 1); + bits_needed_u = loongarch_bits_imm_needed (imm, 0); + + if ((esc_ch1 == 's' && bit_width < bits_needed_s) + || (esc_ch1 != 's' && bit_width < bits_needed_u)) + /* How to do after we detect overflow. */ + as_fatal (_("Immediate overflow.\n" + "format: %c%c%s\n" + "arg: %s"), + esc_ch1, esc_ch2, bit_field, arg); + } + while (0); + + if (esc_ch1 != '\0') + { + ip->args[ip->arg_num] = ret; + ip->arg_num++; + } + return ret; +} + +static void +get_loongarch_opcode (struct loongarch_cl_insn *insn) +{ + const struct loongarch_opcode *it; + struct loongarch_ase *ase; + for (ase = loongarch_ASEs; ase->enabled; ase++) + { + if (!*ase->enabled || (ase->include && !*ase->include) + || (ase->exclude && *ase->exclude)) + continue; + + if (!ase->name_hash_entry) + { + ase->name_hash_entry = str_htab_create (); + for (it = ase->opcodes; it->name; it++) + str_hash_insert (ase->name_hash_entry, it->name, (void *) it, 0); + } + + if ((it = str_hash_find (ase->name_hash_entry, insn->name)) == NULL) + continue; + + do + { + insn->insn = it; + insn->match_now = 1; + insn->all_match = 0; + insn->arg_num = 0; + insn->reloc_num = 0; + insn->insn_bin = (loongarch_foreach_args + (it->format, insn->arg_strs, + loongarch_args_parser_can_match_arg_helper, + insn)); + if (insn->all_match && !(it->include && !*it->include) + && !(it->exclude && *it->exclude)) + { + insn->insn_bin |= it->match; + return; + } + it++; + } + while (it->name && strcasecmp (it->name, insn->name) == 0); + } +} + +static int +check_this_insn_before_appending (struct loongarch_cl_insn *ip) +{ + int ret = 0; + if (strcmp (ip->name, "la.abs") == 0) + { + ip->reloc_info[ip->reloc_num].type = BFD_RELOC_LARCH_MARK_LA; + my_getExpression (&ip->reloc_info[ip->reloc_num].value, ip->arg_strs[1]); + ip->reloc_num++; + } + else if (ip->insn->mask == 0xffff8000 + /* amswap.w rd, rk, rj */ + && ((ip->insn_bin & 0xfff00000) == 0x38600000 + /* ammax_db.wu rd, rk, rj */ + || (ip->insn_bin & 0xffff0000) == 0x38700000 + /* ammin_db.wu rd, rk, rj */ + || (ip->insn_bin & 0xffff0000) == 0x38710000)) + { + /* For AMO insn amswap.[wd], amadd.[wd], etc. */ + if (ip->args[0] != 0 + && (ip->args[0] == ip->args[1] || ip->args[0] == ip->args[2])) + as_fatal (_("AMO insns require rd != base && rd != rt" + " when rd isn't $r0")); + } + else if ((ip->insn->mask == 0xffe08000 + /* bstrins.w rd, rj, msbw, lsbw */ + && (ip->insn_bin & 0xffe00000) == 0x00600000) + || (ip->insn->mask == 0xffc00000 + /* bstrins.d rd, rj, msbd, lsbd */ + && (ip->insn_bin & 0xff800000) == 0x00800000)) + { + /* For bstr(ins|pick).[wd]. */ + if (ip->args[2] < ip->args[3]) + as_fatal (_("bstr(ins|pick).[wd] require msbd >= lsbd")); + } + else if (ip->insn->mask != 0 && (ip->insn_bin & 0xfe0003c0) == 0x04000000 + /* csrxchg rd, rj, csr_num */ + && (strcmp ("csrxchg", ip->name) == 0)) + as_fatal (_("csrxchg require rj != $r0 && rj != $r1")); + + return ret; +} + +static void +install_insn (const struct loongarch_cl_insn *insn) +{ + char *f = insn->frag->fr_literal + insn->where; + if (0 < insn->insn_length) + md_number_to_chars (f, insn->insn_bin, insn->insn_length); +} + +static void +move_insn (struct loongarch_cl_insn *insn, fragS *frag, long where) +{ + size_t i; + insn->frag = frag; + insn->where = where; + for (i = 0; i < insn->reloc_num; i++) + { + insn->fixp[i]->fx_frag = frag; + insn->fixp[i]->fx_where = where; + } + install_insn (insn); +} + +/* Add INSN to the end of the output. */ +static void +append_fixed_insn (struct loongarch_cl_insn *insn) +{ + char *f = frag_more (insn->insn_length); + move_insn (insn, frag_now, f - frag_now->fr_literal); +} + +static void +append_fixp_and_insn (struct loongarch_cl_insn *ip) +{ + reloc_howto_type *howto; + bfd_reloc_code_real_type reloc_type; + struct reloc_info *reloc_info = ip->reloc_info; + size_t i; + for (i = 0; i < ip->reloc_num; i++) + { + reloc_type = reloc_info[i].type; + howto = bfd_reloc_type_lookup (stdoutput, reloc_type); + if (howto == NULL) + as_fatal (_("no HOWTO loong relocation number %d"), reloc_type); + + ip->fixp[i] = + fix_new_exp (ip->frag, ip->where, bfd_get_reloc_size (howto), + &reloc_info[i].value, FALSE, reloc_type); + } + + if (ip->insn_length < ip->relax_max_length) + as_fatal (_("Internal error: not support relax now")); + else + append_fixed_insn (ip); + dwarf2_emit_insn (0); +} + +/* Ask helper for returning a malloced c_str or NULL. */ +static char * +assember_macro_helper (const char *const args[], void *context_ptr) +{ + struct loongarch_cl_insn *insn = context_ptr; + char *ret = NULL; + if ( strcmp (insn->name, "li.w") == 0 || strcmp (insn->name, "li.d") == 0) + { + char args_buf[50], insns_buf[200]; + const char *arg_strs[6]; + uint32_t hi32, lo32; + + /* We pay attention to sign extend beacause it is chance of reduce insn. + The exception is 12-bit and hi-12-bit unsigned, + we need a 'ori' or a 'lu52i.d' accordingly. */ + char all0_bit_vec, sign_bit_vec, allf_bit_vec, paritial_is_sext_of_prev; + + lo32 = insn->args[1] & 0xffffffff; + hi32 = insn->args[1] >> 32; + + if (strcmp (insn->name, "li.w") == 0) + { + if (hi32 != 0 && hi32 != 0xffffffff) + as_fatal (_("li overflow: hi32:0x%x lo32:0x%x"), hi32, lo32); + hi32 = lo32 & 0x80000000 ? 0xffffffff : 0; + } + + if (strcmp (insn->name, "li.d") == 0 && !LARCH_opts.ase_lp64) + as_fatal (_("we can't li.d on 32bit-arch")); + + snprintf (args_buf, sizeof (args_buf), "0x%x,0x%x,0x%x,0x%x,%s", + (hi32 >> 20) & 0xfff, hi32 & 0xfffff, (lo32 >> 12) & 0xfffff, + lo32 & 0xfff, args[0]); + loongarch_split_args_by_comma (args_buf, arg_strs); + + all0_bit_vec = + ((((hi32 & 0xfff00000) == 0) << 3) | (((hi32 & 0x000fffff) == 0) << 2) + | (((lo32 & 0xfffff000) == 0) << 1) | ((lo32 & 0x00000fff) == 0)); + sign_bit_vec = + ((((hi32 & 0x80000000) != 0) << 3) | (((hi32 & 0x00080000) != 0) << 2) + | (((lo32 & 0x80000000) != 0) << 1) | ((lo32 & 0x00000800) != 0)); + allf_bit_vec = + ((((hi32 & 0xfff00000) == 0xfff00000) << 3) + | (((hi32 & 0x000fffff) == 0x000fffff) << 2) + | (((lo32 & 0xfffff000) == 0xfffff000) << 1) + | ((lo32 & 0x00000fff) == 0x00000fff)); + paritial_is_sext_of_prev = + (all0_bit_vec ^ allf_bit_vec) & (all0_bit_vec ^ (sign_bit_vec << 1)); + + static const char *const li_32bit[] = + { + "lu12i.w %5,%3&0x80000?%3-0x100000:%3;ori %5,%5,%4;", + "lu12i.w %5,%3&0x80000?%3-0x100000:%3;", + "addi.w %5,$r0,%4&0x800?%4-0x1000:%4;", + "or %5,$r0,$r0;", + }; + static const char *const li_hi_32bit[] = + { + "lu32i.d %5,%2&0x80000?%2-0x100000:%2;" + "lu52i.d %5,%5,%1&0x800?%1-0x1000:%1;", + "lu52i.d %5,%5,%1&0x800?%1-0x1000:%1;", + "lu32i.d %5,%2&0x80000?%2-0x100000:%2;", + "", + }; + do + { + insns_buf[0] = '\0'; + if (paritial_is_sext_of_prev == 0x7) + { + strcat (insns_buf, "lu52i.d %5,$r0,%1&0x800?%1-0x1000:%1;"); + break; + } + if ((all0_bit_vec & 0x3) == 0x2) + strcat (insns_buf, "ori %5,$r0,%4;"); + else + strcat (insns_buf, li_32bit[paritial_is_sext_of_prev & 0x3]); + strcat (insns_buf, li_hi_32bit[paritial_is_sext_of_prev >> 2]); + } + while (0); + + ret = loongarch_expand_macro (insns_buf, arg_strs, NULL, NULL); + } + return ret; +} + +/* Accept instructions separated by ';' + * assuming 'not starting with space and not ending with space' or pass in + * empty c_str. */ +static void +loongarch_assemble_INSNs (char *str) +{ + char *rest; + + for (rest = str; *rest != ';' && *rest != '\0'; rest++); + if (*rest == ';') + *rest++ = '\0'; + + if (*str == ':') + { + str++; + setup_internal_label_here (strtol (str, &str, 10)); + str++; + } + + do + { + if (*str == '\0') + break; + + struct loongarch_cl_insn the_one = { 0 }; + the_one.name = str; + + for (; *str && *str != ' '; str++) + ; + if (*str == ' ') + *str++ = '\0'; + + loongarch_split_args_by_comma (str, the_one.arg_strs); + get_loongarch_opcode (&the_one); + + if (!the_one.all_match) + { + char *ss = loongarch_cat_splited_strs (the_one.arg_strs); + as_bad (_("no match insn: %s\t%s"), the_one.name, ss ? ss : ""); + free(ss); + return; + } + + if (check_this_insn_before_appending (&the_one) != 0) + break; + + append_fixp_and_insn (&the_one); + if (the_one.insn_length == 0 && the_one.insn->macro) + { + char *c_str = loongarch_expand_macro (the_one.insn->macro, + the_one.arg_strs, + assember_macro_helper, + &the_one); + loongarch_assemble_INSNs (c_str); + free (c_str); + } + } + while (0); + + if (*rest != '\0') + loongarch_assemble_INSNs (rest); +} + +void +md_assemble (char *str) +{ + loongarch_assemble_INSNs (str); +} + +const char * +md_atof (int type, char *litP, int *sizeP) +{ + return ieee_md_atof (type, litP, sizeP, FALSE); +} + +void +md_number_to_chars (char *buf, valueT val, int n) +{ + number_to_chars_littleendian (buf, val, n); +} + +/* The location from which a PC relative jump should be calculated, + given a PC relative reloc. */ +long +md_pcrel_from (fixS *fixP ATTRIBUTE_UNUSED) +{ + return 0; +} + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + static int64_t stack_top; + static int last_reloc_is_sop_push_pcrel_1 = 0; + int last_reloc_is_sop_push_pcrel = last_reloc_is_sop_push_pcrel_1; + insn_t insn; + last_reloc_is_sop_push_pcrel_1 = 0; + + char *buf = fixP->fx_frag->fr_literal + fixP->fx_where; + switch (fixP->fx_r_type) + { + case BFD_RELOC_LARCH_SOP_PUSH_TLS_TPREL: + case BFD_RELOC_LARCH_SOP_PUSH_TLS_GD: + case BFD_RELOC_LARCH_SOP_PUSH_TLS_GOT: + if (fixP->fx_addsy) + S_SET_THREAD_LOCAL (fixP->fx_addsy); + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Relocation against a constant")); + break; + case BFD_RELOC_LARCH_SOP_PUSH_PCREL: + case BFD_RELOC_LARCH_SOP_PUSH_PLT_PCREL: + if (fixP->fx_addsy == NULL) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("Relocation against a constant")); + if (fixP->fx_r_type == BFD_RELOC_LARCH_SOP_PUSH_PCREL) + { + last_reloc_is_sop_push_pcrel_1 = 1; + if (S_GET_SEGMENT (fixP->fx_addsy) == seg) + stack_top = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset + - (fixP->fx_where + fixP->fx_frag->fr_address)); + else + stack_top = 0; + } + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_10_5: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & ~(uint64_t) 0xf) != 0x0 + && (stack_top & ~(uint64_t) 0xf) != ~(uint64_t) 0xf) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = (insn & (~(uint32_t) 0x7c00)) | ((stack_top & 0x1f) << 10); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_U_10_12: + if (!last_reloc_is_sop_push_pcrel) + break; + if (stack_top & ~(uint64_t) 0xfff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = (insn & (~(uint32_t) 0x3ffc00)) | ((stack_top & 0xfff) << 10); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_10_12: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & ~(uint64_t) 0x7ff) != 0x0 + && (stack_top & ~(uint64_t) 0x7ff) != ~(uint64_t) 0x7ff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = (insn & (~(uint32_t) 0x3ffc00)) | ((stack_top & 0xfff) << 10); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_10_16: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & ~(uint64_t) 0x7fff) != 0x0 + && (stack_top & ~(uint64_t) 0x7fff) != ~(uint64_t) 0x7fff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = (insn & 0xfc0003ff) | ((stack_top & 0xffff) << 10); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_10_16_S2: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & 0x3) != 0) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + stack_top >>= 2; + if ((stack_top & ~(uint64_t) 0x7fff) != 0x0 + && (stack_top & ~(uint64_t) 0x7fff) != ~(uint64_t) 0x7fff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = (insn & 0xfc0003ff) | ((stack_top & 0xffff) << 10); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_0_5_10_16_S2: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & 0x3) != 0) + break; + stack_top >>= 2; + if ((stack_top & ~(uint64_t) 0xfffff) != 0x0 + && (stack_top & ~(uint64_t) 0xfffff) != ~(uint64_t) 0xfffff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = ((insn & 0xfc0003e0) + | ((stack_top & 0xffff) << 10) + | ((stack_top & 0x1f0000) >> 16)); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_5_20: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & ~(uint64_t) 0x7ffff) != 0x0 + && (stack_top & ~(uint64_t) 0x7ffff) != ~(uint64_t) 0x7ffff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = (insn & (~(uint32_t) 0x1ffffe0)) | ((stack_top & 0xfffff) << 5); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_S_0_10_10_16_S2: + if (!last_reloc_is_sop_push_pcrel) + break; + if ((stack_top & 0x3) != 0) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + stack_top >>= 2; + if ((stack_top & ~(uint64_t) 0x1ffffff) != 0x0 + && (stack_top & ~(uint64_t) 0x1ffffff) != ~(uint64_t) 0x1ffffff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + insn = bfd_getl32 (buf); + insn = ((insn & 0xfc000000) + | ((stack_top & 0xffff) << 10) + | ((stack_top & 0x3ff0000) >> 16)); + bfd_putl32 (insn, buf); + break; + + case BFD_RELOC_LARCH_SOP_POP_32_U: + if (!last_reloc_is_sop_push_pcrel) + break; + if (stack_top & ~(uint64_t) 0xffffffff) + as_warn_where (fixP->fx_file, fixP->fx_line, "Reloc overflow"); + bfd_putl32 (stack_top, buf); + break; + + case BFD_RELOC_64: + case BFD_RELOC_32: + if (fixP->fx_subsy) + { + case BFD_RELOC_24: + case BFD_RELOC_16: + case BFD_RELOC_8: + fixP->fx_next = xmemdup (fixP, sizeof (*fixP), sizeof (*fixP)); + fixP->fx_next->fx_addsy = fixP->fx_subsy; + fixP->fx_next->fx_subsy = NULL; + fixP->fx_next->fx_offset = 0; + fixP->fx_subsy = NULL; + + switch (fixP->fx_r_type) + { + case BFD_RELOC_64: + fixP->fx_r_type = BFD_RELOC_LARCH_ADD64; + fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB64; + break; + case BFD_RELOC_32: + fixP->fx_r_type = BFD_RELOC_LARCH_ADD32; + fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB32; + break; + case BFD_RELOC_24: + fixP->fx_r_type = BFD_RELOC_LARCH_ADD24; + fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB24; + break; + case BFD_RELOC_16: + fixP->fx_r_type = BFD_RELOC_LARCH_ADD16; + fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB16; + break; + case BFD_RELOC_8: + fixP->fx_r_type = BFD_RELOC_LARCH_ADD8; + fixP->fx_next->fx_r_type = BFD_RELOC_LARCH_SUB8; + break; + default: + break; + } + md_number_to_chars (buf, 0, fixP->fx_size); + if (fixP->fx_next->fx_addsy == NULL) + fixP->fx_next->fx_done = 1; + } + if (fixP->fx_addsy == NULL) + { + fixP->fx_done = 1; + md_number_to_chars (buf, *valP, fixP->fx_size); + } + break; + + default: + break; + } +} + +int +loongarch_relax_frag (asection *sec ATTRIBUTE_UNUSED, + fragS *fragp ATTRIBUTE_UNUSED, + long stretch ATTRIBUTE_UNUSED) +{ + return 0; +} + +int +md_estimate_size_before_relax (fragS *fragp ATTRIBUTE_UNUSED, + asection *segtype ATTRIBUTE_UNUSED) +{ + return 0; +} + +/* Translate internal representation of relocation info to BFD target + format. */ +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) +{ + arelent *reloc = (arelent *) xmalloc (sizeof (arelent)); + + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; + reloc->addend = fixp->fx_offset; + + reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); + if (reloc->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent %s relocation in object file"), + bfd_get_reloc_code_name (fixp->fx_r_type)); + return NULL; + } + + return reloc; +} + +/* Convert a machine dependent frag. */ +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED, + fragS *fragp ATTRIBUTE_UNUSED) +{ + /* fragp->fr_fix += 8; */ +} + +/* Standard calling conventions leave the CFA at SP on entry. */ +void +loongarch_cfi_frame_initial_instructions (void) +{ + cfi_add_CFA_def_cfa_register (3 /* $sp */); +} + +int +loongarch_dwarf2_addr_size (void) +{ + return LARCH_opts.ase_lp64 ? 8 : 4; +} + +void +tc_loongarch_parse_to_dw2regnum (expressionS *exp) +{ + expression_and_evaluate (exp); +} + +void +md_show_usage (FILE *stream) +{ + fprintf (stream, _("LARCH options:\n")); + /* FIXME */ +} + +/* Fill in an rs_align_code fragment. We want to fill 'andi $r0,$r0,0'. */ +void +loongarch_handle_align (fragS *fragp) +{ + /* char nop_opcode; */ + char *p; + int bytes, size, excess; + valueT opcode; + + if (fragp->fr_type != rs_align_code) + return; + + struct loongarch_cl_insn nop = + { .name = "andi", .arg_strs = { "$r0", "$r0", "0", NULL } }; + + get_loongarch_opcode (&nop); + gas_assert (nop.all_match); + + p = fragp->fr_literal + fragp->fr_fix; + opcode = nop.insn_bin; + size = 4; + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + excess = bytes % size; + + gas_assert (excess < 4); + fragp->fr_fix += excess; + + while (excess-- != 0) + *p++ = 0; + + md_number_to_chars (p, opcode, size); + fragp->fr_var = size; +} + +void +loongarch_elf_final_processing (void) +{ + elf_elfheader (stdoutput)->e_flags |= LARCH_opts.ase_abi; +} diff --git a/gas/config/tc-loongarch.h b/gas/config/tc-loongarch.h new file mode 100644 index 0000000..6a07616 --- /dev/null +++ b/gas/config/tc-loongarch.h @@ -0,0 +1,91 @@ +/* tc-loongarch.h -- Header file for tc-loongarch.c. + Copyright (C) 2021 Free Software Foundation, Inc. + Contributed by Loongson Ltd. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the license, or + (at your option) any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING3. If not, + see . */ + +#ifndef TC_LOONGARCH +#define TC_LOONGARCH + +#define TARGET_BYTES_BIG_ENDIAN 0 +#define TARGET_ARCH bfd_arch_loongarch + +#define WORKING_DOT_WORD 1 +#define REPEAT_CONS_EXPRESSIONS + +/* Early than md_begin. */ +#define md_after_parse_args loongarch_after_parse_args +extern void loongarch_after_parse_args (void); + +extern void loongarch_pop_insert (void); +#define md_pop_insert() loongarch_pop_insert () + +#define TARGET_FORMAT loongarch_target_format () +extern const char *loongarch_target_format (void); + +#define md_relax_frag(segment, fragp, stretch) \ + loongarch_relax_frag (segment, fragp, stretch) +extern int loongarch_relax_frag (asection *, struct frag *, long); +#define md_section_align(seg, size) (size) +#define md_undefined_symbol(name) (0) +#define md_operand(x) + +/* This is called to see whether a reloc against a defined symbol + should be converted into a reloc against a section. */ +#define tc_fix_adjustable(fixp) 0 + +/* Values passed to md_apply_fix don't include symbol values. */ +#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1 +#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1 +#define DIFF_EXPR_OK 1 + +#define TARGET_USE_CFIPOP 1 +#define DWARF2_DEFAULT_RETURN_COLUMN 1 /* $ra. */ +#define DWARF2_CIE_DATA_ALIGNMENT -4 +extern int loongarch_dwarf2_addr_size (void); +#define DWARF2_FDE_RELOC_SIZE loongarch_dwarf2_addr_size () +#define DWARF2_ADDR_SIZE(bfd) loongarch_dwarf2_addr_size () +#define CFI_DIFF_EXPR_OK 0 + +#define tc_cfi_frame_initial_instructions \ + loongarch_cfi_frame_initial_instructions +extern void loongarch_cfi_frame_initial_instructions (void); + +#define tc_parse_to_dw2regnum tc_loongarch_parse_to_dw2regnum +extern void tc_loongarch_parse_to_dw2regnum (expressionS *); + +/* A enumerated values to specific how to deal with align in '.text'. + Now we want to fill 'andi $r0,$r0,0x0'. + Here is the type 0, will fill andi insn later. */ +#define NOP_OPCODE (0x00) + +#define HANDLE_ALIGN(fragp) loongarch_handle_align (fragp) +extern void loongarch_handle_align (struct frag *); +#define MAX_MEM_FOR_RS_ALIGN_CODE (3 + 4) + +#define elf_tc_final_processing loongarch_elf_final_processing +extern void loongarch_elf_final_processing (void); + +#define MAX_RELOC_NUMBER_A_INSN 20 + +struct reloc_info +{ + bfd_reloc_code_real_type type; + expressionS value; +}; + +#endif -- cgit v1.1