[5/7,LoongArch] Gas support

Message ID CAKjxQHnG8Yr669R_K9F=0776bfUN7nM48Afs0U-QYUm5akkpwA@mail.gmail.com
State New
Headers show
Series
  • Untitled series #41024
Related show

Commit Message

Alan Modra via Binutils Aug. 14, 2021, 8:18 a.m.

Comments

Alan Modra via Binutils Aug. 24, 2021, 11:57 a.m. | #1
On Sat, Aug 14, 2021 at 04:18:08PM +0800, Paul Hua via Binutils wrote:
> +++ b/gas/config/loongarch-parse.y

> +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

> +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;


> +++ b/gas/config/tc-loongarch.c

> +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);

> +


Don't do this.  Put the declaration in a header and include in both
files.  Otherwise would be OK if there was a proper testsuite.  That
needs to be written before this can be accepted.

-- 
Alan Modra
Australia Development Lab, IBM

Patch

From 5a20714d5432dd45df0cfcc3b778ffca2b85f7f9 Mon Sep 17 00:00:00 2001
From: liuzhensong <liuzhensong@loongson.cn>
Date: Sat, 14 Aug 2021 11:35:33 +0800
Subject: [PATCH 5/7] gas: LoongArch GAS Port.

  gas/Makefile.am
  gas/Makefile.in
  gas/NEWS
  gas/config/loongarch-lex-wrapper.c
  gas/config/loongarch-lex.l
  gas/config/loongarch-parse.y
  gas/config/tc-loongarch.c
  gas/config/tc-loongarch.h
  gas/configure
  gas/configure.ac
  gas/configure.tgt
  gas/doc/as.texi
  gas/doc/c-loongarch.texi
  gas/testsuite/gas/all/gas.exp
  gas/testsuite/gas/loongarch/loongarch.exp
  gas/testsuite/gas/loongarch/nop.d
  gas/testsuite/gas/loongarch/nop.s
  gas/testsuite/lib/gas-defs.exp
---
 gas/Makefile.am                           |   26 +-
 gas/Makefile.in                           |   29 +-
 gas/NEWS                                  |    4 +
 gas/config/loongarch-lex-wrapper.c        |   20 +
 gas/config/loongarch-lex.l                |   55 +
 gas/config/loongarch-parse.y              |  448 ++++++++
 gas/config/tc-loongarch.c                 | 1232 +++++++++++++++++++++
 gas/config/tc-loongarch.h                 |   90 ++
 gas/configure                             |    9 +
 gas/configure.ac                          |    9 +
 gas/configure.tgt                         |    5 +-
 gas/doc/as.texi                           |   31 +
 gas/doc/c-loongarch.texi                  |   39 +
 gas/testsuite/gas/all/gas.exp             |    5 +-
 gas/testsuite/gas/loongarch/loongarch.exp |   23 +
 gas/testsuite/gas/loongarch/nop.d         |   10 +
 gas/testsuite/gas/loongarch/nop.s         |    2 +
 gas/testsuite/lib/gas-defs.exp            |    4 +
 18 files changed, 2036 insertions(+), 5 deletions(-)
 create mode 100644 gas/config/loongarch-lex-wrapper.c
 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
 create mode 100644 gas/doc/c-loongarch.texi
 create mode 100644 gas/testsuite/gas/loongarch/loongarch.exp
 create mode 100644 gas/testsuite/gas/loongarch/nop.d
 create mode 100644 gas/testsuite/gas/loongarch/nop.s

diff --git a/gas/Makefile.am b/gas/Makefile.am
index 34190e78b8c..55b93938dd9 100644
--- a/gas/Makefile.am
+++ b/gas/Makefile.am
@@ -154,6 +154,7 @@  TARGET_CPU_CFILES = \
 	config/tc-ip2k.c \
 	config/tc-iq2000.c \
 	config/tc-lm32.c \
+	config/tc-loongarch.c \
 	config/tc-m32c.c \
 	config/tc-m32r.c \
 	config/tc-m68hc11.c \
@@ -374,7 +375,8 @@  EXTRA_SCRIPTS = .gdbinit
 EXTRA_DIST = config/m68k-parse.c itbl-parse.c itbl-parse.h itbl-lex.c \
 	config/bfin-parse.c config/bfin-parse.h config/bfin-lex.c \
 	config/rl78-parse.c config/rl78-parse.h \
-	config/rx-parse.c config/rx-parse.h
+	config/rx-parse.c config/rx-parse.h \
+	config/loongarch-parse.c config/loongarch-parse.h config/loongarch-lex.c
 
 diststuff: $(EXTRA_DIST) info
 
@@ -471,6 +473,28 @@  config/rx-parse.c: $(srcdir)/config/rx-parse.y
 config/rx-parse.h: config/rx-parse.c
 	@true
 
+# The LoongArch lexical analyzer and parser.
+EXTRA_as_new_SOURCES += config/loongarch-parse.y
+loongarch-parse.c: $(srcdir)/config/loongarch-parse.y
+	$(SHELL) $(YLWRAP) $(srcdir)/config/loongarch-parse.y y.tab.c loongarch-parse.c y.tab.h loongarch-parse.h -- $(YACCCOMPILE) -d ;
+loongarch-parse.h: loongarch-parse.c
+loongarch-parse.h: ; @true
+$(srcdir)/config/loongarch-parse.h: ; @true
+
+loongarch-lex.c: $(srcdir)/config/loongarch-lex.l
+	$(SHELL) $(YLWRAP) $(srcdir)/config/loongarch-lex.l lex.yy.c loongarch-lex.c -- $(LEXCOMPILE)
+loongarch-lex-wrapper.@OBJEXT@: $(srcdir)/config/loongarch-lex-wrapper.c loongarch-lex.c loongarch-parse.h
+if am__fastdepCC
+	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(srcdir)/config/loongarch-lex-wrapper.c $(NO_WERROR)
+	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+else
+if AMDEP
+	source='loongarch-lex-wrapper.c' object='$@' libtool=no @AMDEPBACKSLASH@
+	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+endif
+	$(COMPILE) -c $(srcdir)/config/loongarch-lex-wrapper.c $(NO_WERROR)
+endif
+
 # The mips instruction table specification lexical analyzer and parser.
 
 itbl-lex-wrapper.@OBJEXT@: itbl-lex.c itbl-parse.h
diff --git a/gas/Makefile.in b/gas/Makefile.in
index f3d66b8e8b6..311d0938c5a 100644
--- a/gas/Makefile.in
+++ b/gas/Makefile.in
@@ -544,6 +544,7 @@  TARGET_CPU_CFILES = \
 	config/tc-ip2k.c \
 	config/tc-iq2000.c \
 	config/tc-lm32.c \
+	config/tc-loongarch.c \
 	config/tc-m32c.c \
 	config/tc-m32r.c \
 	config/tc-m68hc11.c \
@@ -757,7 +758,8 @@  EXTRA_SCRIPTS = .gdbinit
 EXTRA_DIST = config/m68k-parse.c itbl-parse.c itbl-parse.h itbl-lex.c \
 	config/bfin-parse.c config/bfin-parse.h config/bfin-lex.c \
 	config/rl78-parse.c config/rl78-parse.h \
-	config/rx-parse.c config/rx-parse.h
+	config/rx-parse.c config/rx-parse.h \
+	config/loongarch-parse.c config/loongarch-parse.h config/loongarch-lex.c
 
 DISTCLEANFILES = targ-cpu.h obj-format.h targ-env.h itbl-cpu.h cgen-desc.h
 
@@ -789,12 +791,14 @@  as_new_LDADD = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
 as_new_DEPENDENCIES = $(TARG_CPU_O) $(OBJ_FORMAT_O) $(ATOF_TARG_O) \
 	$(extra_objects) $(GASLIBS) $(LIBINTL_DEP)
 
+
+# The LoongArch lexical analyzer and parser.
 EXTRA_as_new_SOURCES = $(CFILES) $(HFILES) $(TARGET_CPU_CFILES) \
 	$(TARGET_CPU_HFILES) $(TARGET_EXTRA_FILES) $(TARG_ENV_CFILES) \
 	$(OBJ_FORMAT_CFILES) $(OBJ_FORMAT_HFILES) \
 	$(CONFIG_ATOF_CFILES) $(MULTI_CFILES) config/m68k-parse.y \
 	config/bfin-parse.y config/bfin-lex.l config/rl78-parse.y \
-	config/rx-parse.y
+	config/rx-parse.y config/loongarch-parse.y
 EXPECT = expect
 RUNTEST = runtest
 RUNTESTFLAGS = 
@@ -939,6 +943,8 @@  config/tc-iq2000.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
 config/tc-lm32.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
+config/tc-loongarch.$(OBJEXT): config/$(am__dirstamp) \
+	config/$(DEPDIR)/$(am__dirstamp)
 config/tc-m32c.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
 config/tc-m32r.$(OBJEXT): config/$(am__dirstamp) \
@@ -1085,6 +1091,8 @@  config/rl78-parse.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
 config/rx-parse.$(OBJEXT): config/$(am__dirstamp) \
 	config/$(DEPDIR)/$(am__dirstamp)
+config/loongarch-parse.$(OBJEXT): config/$(am__dirstamp) \
+	config/$(DEPDIR)/$(am__dirstamp)
 
 as-new$(EXEEXT): $(as_new_OBJECTS) $(as_new_DEPENDENCIES) $(EXTRA_as_new_DEPENDENCIES) 
 	@rm -f as-new$(EXEEXT)
@@ -1146,6 +1154,7 @@  distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/e-i386coff.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/e-i386elf.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/e-mipself.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/loongarch-parse.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/m68k-parse.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/obj-aout.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/obj-coff.Po@am__quote@
@@ -1183,6 +1192,7 @@  distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-ip2k.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-iq2000.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-lm32.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-loongarch.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-m32c.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-m32r.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@config/$(DEPDIR)/tc-m68hc11.Po@am__quote@
@@ -1452,6 +1462,7 @@  maintainer-clean-generic:
 	@echo "it deletes files that may require special tools to rebuild."
 	-rm -f config/bfin-lex.c
 	-rm -f config/bfin-parse.c
+	-rm -f config/loongarch-parse.c
 	-rm -f config/m68k-parse.c
 	-rm -f config/rl78-parse.c
 	-rm -f config/rx-parse.c
@@ -1601,6 +1612,20 @@  config/rx-parse.c: $(srcdir)/config/rx-parse.y
 	$(SHELL) $(YLWRAP) $(srcdir)/config/rx-parse.y y.tab.c $@ y.tab.h config/rx-parse.h -- $(YACCCOMPILE) -d ;
 config/rx-parse.h: config/rx-parse.c
 	@true
+loongarch-parse.c: $(srcdir)/config/loongarch-parse.y
+	$(SHELL) $(YLWRAP) $(srcdir)/config/loongarch-parse.y y.tab.c loongarch-parse.c y.tab.h loongarch-parse.h -- $(YACCCOMPILE) -d ;
+loongarch-parse.h: loongarch-parse.c
+loongarch-parse.h: ; @true
+$(srcdir)/config/loongarch-parse.h: ; @true
+
+loongarch-lex.c: $(srcdir)/config/loongarch-lex.l
+	$(SHELL) $(YLWRAP) $(srcdir)/config/loongarch-lex.l lex.yy.c loongarch-lex.c -- $(LEXCOMPILE)
+loongarch-lex-wrapper.@OBJEXT@: $(srcdir)/config/loongarch-lex-wrapper.c loongarch-lex.c loongarch-parse.h
+@am__fastdepCC_TRUE@	$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $(srcdir)/config/loongarch-lex-wrapper.c $(NO_WERROR)
+@am__fastdepCC_TRUE@	mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='loongarch-lex-wrapper.c' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@	$(COMPILE) -c $(srcdir)/config/loongarch-lex-wrapper.c $(NO_WERROR)
 
 # The mips instruction table specification lexical analyzer and parser.
 
diff --git a/gas/NEWS b/gas/NEWS
index 9e24e4ddc17..b9848e41899 100644
--- a/gas/NEWS
+++ b/gas/NEWS
@@ -4,6 +4,10 @@ 
 
 Changes in 2.37:
 
+* Add support for the LoongArch instruction set.
+
+Changes in 2.37:
+
 * arm-symbianelf support removed.
 
 * Add support for Realm Management Extension (RME) for AArch64.
diff --git a/gas/config/loongarch-lex-wrapper.c b/gas/config/loongarch-lex-wrapper.c
new file mode 100644
index 00000000000..5a13a6e861a
--- /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 <http://www.gnu.org/licenses/>.  */
+
+#include "sysdep.h"
+#include "loongarch-lex.c"
diff --git a/gas/config/loongarch-lex.l b/gas/config/loongarch-lex.l
new file mode 100644
index 00000000000..1344a288065
--- /dev/null
+++ b/gas/config/loongarch-lex.l
@@ -0,0 +1,55 @@ 
+%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 <http://www.gnu.org/licenses/>.  */
+%{
+#include "as.h"
+#include "loongarch-parse.h"
+%}
+
+D	[0-9]
+L	[a-zA-Z_\.\$]
+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 00000000000..843d0feb473
--- /dev/null
+++ b/gas/config/loongarch-parse.y
@@ -0,0 +1,448 @@ 
+/*
+   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 <http://www.gnu.org/licenses/>.  */
+%{
+#include "as.h"
+#include "loongarch-parse.h"
+static void yyerror (const char *s ATTRIBUTE_UNUSED)
+{
+};
+extern int yylex (void);
+extern void yy_scan_string (const char *);
+extern void
+get_internal_label (expressionS *label_expr,
+		    unsigned long label,
+		    int augend);
+
+
+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
+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;
+  top = reloc_stack_top;
+  end = top + max_reloc_num;
+  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;
+    }
+  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
+	;
+%%
+
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
new file mode 100644
index 00000000000..26091722949
--- /dev/null
+++ b/gas/config/tc-loongarch.c
@@ -0,0 +1,1232 @@ 
+/* 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 <http://www.gnu.org/licenses/>.  */
+
+#include "as.h"
+#include "dw2gencfi.h"
+#include "elf/loongarch.h"
+#include "opcode/loongarch.h"
+#include "obj-elf.h"
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+
+/* 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];
+};
+
+/* 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:";
+
+enum options
+{
+  OPTION_IGNORE = OPTION_MD_BASE,
+
+  OPTION_SOFT_FLOAT,
+  OPTION_HARD_FLOAT,
+  OPTION_ABI,
+
+  OPTION_LA_LOCAL_WITH_ABS,
+  OPTION_LA_GLOBAL_WITH_PCREL,
+  OPTION_LA_GLOBAL_WITH_ABS,
+
+  OPTION_END_OF_ENUM,
+};
+
+struct option md_longopts[] =
+{
+  { "msoft-float", no_argument, NULL, OPTION_SOFT_FLOAT },
+  { "mhard-float", no_argument, NULL, OPTION_HARD_FLOAT },
+  { "mabi", required_argument, NULL, OPTION_ABI },
+
+  { "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_SOFT_FLOAT:
+      LARCH_opts.ase_float = 0;
+      break;
+    case OPTION_HARD_FLOAT:
+      LARCH_opts.ase_float = 1;
+      break;
+    case OPTION_ABI:
+      if (strcasecmp (arg, "lp64") == 0)
+	LARCH_opts.abi_is_lp64 = 1;
+      else if (strcasecmp (arg, "lp32") == 0)
+	LARCH_opts.abi_is_lp32 = 1;
+      else
+	ret = 0;
+      break;
+    case OPTION_LA_LOCAL_WITH_ABS:
+      LARCH_opts.la_local_with_abs = 1;
+      break;
+    case OPTION_LA_GLOBAL_WITH_PCREL:
+      LARCH_opts.la_global_with_pcrel = 1;
+      break;
+    case OPTION_LA_GLOBAL_WITH_ABS:
+      LARCH_opts.la_global_with_abs = 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 ()
+{
+  size_t i;
+
+  LARCH_opts.ase_fix = 1;
+  LARCH_opts.ase_float = 1;
+  LARCH_opts.ase_128vec = 1;
+  LARCH_opts.ase_256vec = 1;
+
+  if (!r_htab)
+    r_htab = str_htab_create (), str_hash_insert (r_htab, "", 0, 0);
+  if (!f_htab)
+    f_htab = str_htab_create (), str_hash_insert (f_htab, "", 0, 0);
+  if (!c_htab)
+    c_htab = str_htab_create (), str_hash_insert (c_htab, "", 0, 0);
+  if (!cr_htab)
+    cr_htab = str_htab_create (), str_hash_insert (cr_htab, "", 0, 0);
+  if (!v_htab)
+    v_htab = str_htab_create (), str_hash_insert (v_htab, "", 0, 0);
+  if (!x_htab)
+    x_htab = str_htab_create (), str_hash_insert (x_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);
+  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);
+  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);
+  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);
+  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);
+  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);
+
+  if (LARCH_opts.abi_is_lp64 + LARCH_opts.abi_is_lp32 == 0)
+    {
+      /* as_warn (_("default LARCH ABI is lp64")); */
+      LARCH_opts.abi_is_lp64 = 1;
+    }
+
+  if (1 < LARCH_opts.abi_is_lp64 + LARCH_opts.abi_is_lp32)
+    as_fatal (_ ("we can specify only ONE abi"));
+
+  if (LARCH_opts.abi_is_lp64)
+    {
+      LARCH_opts.addrwidth_is_64 = 1;
+      LARCH_opts.rlen_is_64 = 1;
+      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);
+      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);
+    }
+
+  if (LARCH_opts.abi_is_lp32)
+    {
+      LARCH_opts.addrwidth_is_32 = 1;
+      LARCH_opts.rlen_is_32 = 1;
+    }
+}
+
+const char *
+loongarch_target_format ()
+{
+  return LARCH_opts.addrwidth_is_32 ? "elf32-loongarch" : "elf64-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));
+}
+
+void
+md_operand (expressionS *e)
+{
+  /* Because we use 'expression' to check if a actual arg is a expr at first.
+     If not, we want a returning.  */
+  if (e->X_op == O_absent)
+    e->X_op = O_illegal;
+}
+
+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));
+}
+
+/* No static. used by 'loongarch-parse.y'.   */
+extern void
+get_internal_label (expressionS *label_expr, unsigned long label,
+		    int augend /* 0 for previous, 1 for next.  */);
+
+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 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.rlen_is_32)
+	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)
+	as_fatal (_ ("no match insn: %s\t%s"), the_one.name,
+		  loongarch_cat_splited_strs (the_one.arg_strs));
+
+      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.addrwidth_is_32 ? 4 : 8;
+}
+
+void
+tc_loongarch_parse_to_dw2regnum (expressionS *exp)
+{
+  expression_and_evaluate (exp);
+}
+
+void
+md_show_usage (FILE *stream)
+{
+  fprintf (stream, _ ("\
+		     LARCH options:\n\
+		     "));
+}
+
+/* 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)
+{
+  if (LARCH_opts.abi_is_lp64)
+    elf_elfheader (stdoutput)->e_flags |= EF_LARCH_ABI_LP64;
+  else if (LARCH_opts.abi_is_lp32)
+    elf_elfheader (stdoutput)->e_flags |= EF_LARCH_ABI_LP32;
+}
diff --git a/gas/config/tc-loongarch.h b/gas/config/tc-loongarch.h
new file mode 100644
index 00000000000..b03afb0050d
--- /dev/null
+++ b/gas/config/tc-loongarch.h
@@ -0,0 +1,90 @@ 
+/* 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 <http://www.gnu.org/licenses/>.  */
+
+#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)
+
+/* 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_regname_to_dw2regnum tc_loongarch_regname_to_dw2regnum  */
+#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'.  */
+#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
diff --git a/gas/configure b/gas/configure
index 7a528fd485e..265fdf5d073 100755
--- a/gas/configure
+++ b/gas/configure
@@ -12206,6 +12206,15 @@  _ACEOF
 	using_cgen=yes
 	;;
 
+      loongarch)
+	for f in loongarch-parse.o loongarch-lex-wrapper.o; do
+	  case " $extra_objects " in
+	    *" $f "*) ;;
+	    *) extra_objects="$extra_objects $f" ;;
+	  esac
+	done
+	;;
+
       m32c)
 	using_cgen=yes
 	;;
diff --git a/gas/configure.ac b/gas/configure.ac
index e2374268c56..d99f52ab1df 100644
--- a/gas/configure.ac
+++ b/gas/configure.ac
@@ -446,6 +446,15 @@  changequote([,])dnl
 	using_cgen=yes
 	;;
 
+      loongarch)
+	for f in loongarch-parse.o loongarch-lex-wrapper.o; do
+	  case " $extra_objects " in
+	    *" $f "*) ;;
+	    *) extra_objects="$extra_objects $f" ;;
+	  esac
+	done
+	;;
+
       m32c)
 	using_cgen=yes
 	;;
diff --git a/gas/configure.tgt b/gas/configure.tgt
index 68ee0468e8d..fce651a5a89 100644
--- a/gas/configure.tgt
+++ b/gas/configure.tgt
@@ -67,6 +67,7 @@  case ${cpu} in
   ip2k)			cpu_type=ip2k endian=big ;;
   iq2000)		cpu_type=iq2000 endian=big ;;
   lm32)			cpu_type=lm32 ;;
+  loongarch*)		cpu_type=loongarch ;;
   m32c)			cpu_type=m32c endian=little ;;
   m32r)			cpu_type=m32r endian=big ;;
   m32rle)		cpu_type=m32r endian=little ;;
@@ -274,6 +275,8 @@  case ${generic_target} in
 
   lm32-*-*)				fmt=elf ;;
 
+  loongarch*)				fmt=elf ;;
+
   m32c-*-elf)				fmt=elf ;;
 
   m32r-*-elf*)				fmt=elf ;;
@@ -435,7 +438,7 @@  esac
 
 case ${cpu_type} in
   aarch64 | alpha | arm | csky | i386 | ia64 | microblaze | mips | ns32k | \
-  or1k | or1knd | pdp11 | ppc | riscv | sh | sparc | z80 | z8k)
+  or1k | or1knd | pdp11 | ppc | riscv | sh | sparc | z80 | z8k | loongarch)
     bfd_gas=yes
     ;;
 esac
diff --git a/gas/doc/as.texi b/gas/doc/as.texi
index b8d5b9be15e..0cc7455d3ae 100644
--- a/gas/doc/as.texi
+++ b/gas/doc/as.texi
@@ -379,6 +379,11 @@  gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
 @emph{Target IP2K options:}
    [@b{-mip2022}|@b{-mip2022ext}]
 @end ifset
+@ifset LOONGARCH
+
+@emph{Target LOONGARCH options:}
+   [@b{-fpic}|@b{-fPIC}|@b{-fno-pic}]
+@end ifset
 @ifset M32C
 
 @emph{Target M32C options:}
@@ -1756,6 +1761,25 @@  Assemble for a little endian target.
 @end ifset
 @c man end
 
+@ifset LOONGARCH
+
+@ifclear man
+@xref{LoongArch-Options}, for the options available when @value{AS} is configured
+for a LoongArch processor.
+@end ifclear
+
+@ifset man
+@c man begin OPTIONS
+The following options are available when @value{AS} is configured for a
+LoongArch processor.
+@c man end
+@c man begin INCLUDE
+@include c-loongarch.texi
+@c ended inside the included file
+@end ifset
+
+@end ifset
+
 @ifset METAG
 
 @ifclear man
@@ -7837,6 +7861,9 @@  subject, see the hardware manufacturer's manual.
 @ifset IP2K
 * IP2K-Dependent::              IP2K Dependent Features
 @end ifset
+@ifset LOONGARCH
+* LoongArch-Dependent::         LoongArch Dependent Features
+@end ifset
 @ifset LM32
 * LM32-Dependent::              LM32 Dependent Features
 @end ifset
@@ -8061,6 +8088,10 @@  family.
 @include c-lm32.texi
 @end ifset
 
+@ifset LOONGARCH
+@include c-loongarch.texi
+@end ifset
+
 @ifset M32C
 @include c-m32c.texi
 @end ifset
diff --git a/gas/doc/c-loongarch.texi b/gas/doc/c-loongarch.texi
new file mode 100644
index 00000000000..2a139484577
--- /dev/null
+++ b/gas/doc/c-loongarch.texi
@@ -0,0 +1,39 @@ 
+@c Copyright (C) 2021 Free Software Foundation, Inc.
+@c This is part of the GAS anual.
+@c For copying conditions, see the file as.texinfo
+@c man end
+
+@ifset GENERIC
+@page
+@node LoongArch-Dependent
+@chapter LoongArch Dependent Features
+@end ifset
+@ifclear GENERIC
+@node Machine Dependencies
+@chapter LoongArch Dependent Features
+@end ifclear
+
+@cindex LoongArch support
+@menu
+* LoongArch-Options::        LoongArch Options
+@end menu
+
+@node LoongArch-Options
+@section LoongArch Options
+
+The following table lists all available LoongArch specific options.
+
+@c man begin OPTIONS
+@table @gcctabopt
+
+@cindex @samp{-fpic} option, LoongArch
+@item -fpic
+@itemx -fPIC
+Generate position-independent code
+
+@cindex @samp{-fno-pic} option, LoongArch
+@item -fno-pic
+Don't generate position-independent code (default)
+
+@end table
+@c man end
diff --git a/gas/testsuite/gas/all/gas.exp b/gas/testsuite/gas/all/gas.exp
index 389634f6165..4825d19460e 100644
--- a/gas/testsuite/gas/all/gas.exp
+++ b/gas/testsuite/gas/all/gas.exp
@@ -68,7 +68,8 @@  if {    ![istarget alpha*-*-*vms*]
      && ![istarget riscv*-*-*]
      && ![istarget rl78-*-*]
      && ![istarget rs6000*-*-aix*]
-     && ![istarget rx-*-*] } then {
+     && ![istarget rx-*-*]
+     && ![istarget loongarch*-*-*] } then {
     gas_test_error "diff1.s" "" "difference of two undefined symbols"
 }
 
@@ -160,9 +161,11 @@  switch -glob $target_triplet {
 	# symbols on relocs.
 	setup_xfail "m68hc1*-*-*" "m6811-*-*" "m6812-*-*" "rl78-*-*"
 	setup_xfail "riscv*-*-*" "rx-*-*" "vax*-*-*" "xgate*-*-*" "z8k-*-*"
+	setup_xfail "loongarch*-*-*"
 	run_dump_test redef2
 	setup_xfail "m68hc1*-*-*" "m6811-*-*" "m6812-*-*" "rl78-*-*"
 	setup_xfail "riscv*-*-*" "rx-*-*" "vax*-*-*" "xgate*-*-*" "z8k-*-*"
+	setup_xfail "loongarch*-*-*"
 	# rs6000-aix disallows redefinition via .comm.
 	if [is_xcoff_format] {
 	    setup_xfail *-*-*
diff --git a/gas/testsuite/gas/loongarch/loongarch.exp b/gas/testsuite/gas/loongarch/loongarch.exp
new file mode 100644
index 00000000000..c186b67315f
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/loongarch.exp
@@ -0,0 +1,23 @@ 
+# Expect script for LoongArch assembler tests.
+#   Copyright (C) 2021 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program 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.
+#
+# This program 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; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+if [istarget loongarch*-*-*] {
+    run_dump_tests [lsort [glob -nocomplain $srcdir/$subdir/*.d]]
+}
diff --git a/gas/testsuite/gas/loongarch/nop.d b/gas/testsuite/gas/loongarch/nop.d
new file mode 100644
index 00000000000..55248661d11
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/nop.d
@@ -0,0 +1,10 @@ 
+#as:
+#objdump: -dr
+
+.*:[ 	]+file format .*
+
+
+Disassembly of section .text:
+
+0+000 <target>:
+[ 	]+0:[ 	]+03400000[ 	]+andi .*
diff --git a/gas/testsuite/gas/loongarch/nop.s b/gas/testsuite/gas/loongarch/nop.s
new file mode 100644
index 00000000000..99456883315
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/nop.s
@@ -0,0 +1,2 @@ 
+target:
+	nop
diff --git a/gas/testsuite/lib/gas-defs.exp b/gas/testsuite/lib/gas-defs.exp
index f9ee6f4ac72..6074c2ee282 100644
--- a/gas/testsuite/lib/gas-defs.exp
+++ b/gas/testsuite/lib/gas-defs.exp
@@ -360,6 +360,10 @@  proc verbose_eval { expr { level 1 } } {
 # This definition is taken from an unreleased version of DejaGnu.  Once
 # that version gets released, and has been out in the world for a few
 # months at least, it may be safe to delete this copy.
+
+if { [istarget loongarch*-*-*] } {
+    rename prune_warnings prune_warnings_other
+}
 if ![string length [info proc prune_warnings]] {
     #
     # prune_warnings -- delete various system verbosities from TEXT.
-- 
2.27.0