aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormengqinggang <mengqinggang@loongson.cn>2023-09-24 14:53:28 +0800
committerliuzhensong <liuzhensong@loongson.cn>2023-10-10 16:34:33 +0800
commit1fb3cdd87ec61715a5684925fb6d6a6cf53bb97c (patch)
tree29ff87c34597a975906f77e6bfaeedf8e433c37e
parent9847ba8f085a7da0d85e90718ca987ab76637cbe (diff)
downloadgdb-1fb3cdd87ec61715a5684925fb6d6a6cf53bb97c.zip
gdb-1fb3cdd87ec61715a5684925fb6d6a6cf53bb97c.tar.gz
gdb-1fb3cdd87ec61715a5684925fb6d6a6cf53bb97c.tar.bz2
LoongArch/GAS: Add support for branch relaxation
For the instructions of R_LARCH_B16/B21, if the immediate overflow, add a B instruction and R_LARCH_B26 relocation. For example: .L1 ... blt $t0, $t1, .L1 R_LARCH_B16 change to: .L1 ... bge $t0, $t1, .L2 b .L1 R_LARCH_B26 .L2
-rw-r--r--gas/config/tc-loongarch.c236
-rw-r--r--gas/testsuite/gas/loongarch/la_branch_relax_1.d64
-rw-r--r--gas/testsuite/gas/loongarch/la_branch_relax_1.s33
-rw-r--r--gas/testsuite/gas/loongarch/la_branch_relax_2.d40
-rw-r--r--gas/testsuite/gas/loongarch/la_branch_relax_2.s23
-rw-r--r--include/opcode/loongarch.h12
6 files changed, 367 insertions, 41 deletions
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
index d1d21fa..b563982 100644
--- a/gas/config/tc-loongarch.c
+++ b/gas/config/tc-loongarch.c
@@ -106,6 +106,16 @@ const char *md_shortopts = "O::g::G:";
static const char default_arch[] = DEFAULT_ARCH;
+/* The lowest 4-bit is the bytes of instructions. */
+#define RELAX_BRANCH_16 0xc0000014
+#define RELAX_BRANCH_21 0xc0000024
+#define RELAX_BRANCH_26 0xc0000048
+
+#define RELAX_BRANCH(x) \
+ (((x) & 0xf0000000) == 0xc0000000)
+#define RELAX_BRANCH_ENCODE(x) \
+ (BFD_RELOC_LARCH_B16 == (x) ? RELAX_BRANCH_16 : RELAX_BRANCH_21)
+
enum options
{
OPTION_IGNORE = OPTION_MD_BASE,
@@ -953,11 +963,22 @@ append_fixed_insn (struct loongarch_cl_insn *insn)
move_insn (insn, frag_now, f - frag_now->fr_literal);
}
+/* Add instructions based on the worst-case scenario firstly. */
+static void
+append_relaxed_branch_insn (struct loongarch_cl_insn *insn, int max_chars,
+ int var, relax_substateT subtype, symbolS *symbol, offsetT offset)
+{
+ frag_grow (max_chars);
+ move_insn (insn, frag_now, frag_more (0) - frag_now->fr_literal);
+ frag_var (rs_machine_dependent, max_chars, var,
+ subtype, symbol, offset, NULL);
+}
+
static void
append_fixp_and_insn (struct loongarch_cl_insn *ip)
{
reloc_howto_type *howto;
- bfd_reloc_code_real_type reloc_type;
+ bfd_reloc_code_real_type r_type;
struct reloc_info *reloc_info = ip->reloc_info;
size_t i;
@@ -965,14 +986,40 @@ append_fixp_and_insn (struct loongarch_cl_insn *ip)
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);
+ r_type = reloc_info[i].type;
+
+ if (r_type != BFD_RELOC_UNUSED)
+ {
+
+ gas_assert (&(reloc_info[i].value));
+ if (BFD_RELOC_LARCH_B16 == r_type || BFD_RELOC_LARCH_B21 == r_type)
+ {
+ int min_bytes = 4; /* One branch instruction. */
+ unsigned max_bytes = 8; /* Branch and jump instructions. */
+
+ if (now_seg == absolute_section)
+ {
+ as_bad (_("relaxable branches not supported in absolute section"));
+ return;
+ }
+
+ append_relaxed_branch_insn (ip, max_bytes, min_bytes,
+ RELAX_BRANCH_ENCODE (r_type),
+ reloc_info[i].value.X_add_symbol,
+ reloc_info[i].value.X_add_number);
+ return;
+ }
+ else
+ {
+ howto = bfd_reloc_type_lookup (stdoutput, r_type);
+ if (howto == NULL)
+ as_fatal (_("no HOWTO loong relocation number %d"), r_type);
+
+ ip->fixp[i] = fix_new_exp (ip->frag, ip->where,
+ bfd_get_reloc_size (howto),
+ &reloc_info[i].value, FALSE, r_type);
+ }
+ }
}
if (ip->insn_length < ip->relax_max_length)
@@ -1488,14 +1535,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
}
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)
{
@@ -1526,30 +1565,6 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
return reloc;
}
-/* Convert a machine dependent frag. */
-void
-md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
- fragS *fragp)
-{
- expressionS exp;
- exp.X_op = O_symbol;
- exp.X_add_symbol = fragp->fr_symbol;
- exp.X_add_number = fragp->fr_offset;
- bfd_byte *buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
-
- fixS *fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
- 4, &exp, false, fragp->fr_subtype);
- buf += 4;
-
- fixp->fx_file = fragp->fr_file;
- fixp->fx_line = fragp->fr_line;
- fragp->fr_fix += fragp->fr_var;
-
- gas_assert (fragp->fr_next == NULL
- || (fragp->fr_next->fr_address - fragp->fr_address
- == fragp->fr_fix));
-}
-
/* Standard calling conventions leave the CFA at SP on entry. */
void
loongarch_cfi_frame_initial_instructions (void)
@@ -1775,3 +1790,142 @@ loongarch_elf_final_processing (void)
{
elf_elfheader (stdoutput)->e_flags = LARCH_opts.ase_abi;
}
+
+/* Compute the length of a branch sequence, and adjust the stored length
+ accordingly. If FRAGP is NULL, the worst-case length is returned. */
+static unsigned
+loongarch_relaxed_branch_length (fragS *fragp, asection *sec, int update)
+{
+ int length = 4;
+
+ if (!fragp)
+ return 8;
+
+ if (fragp->fr_symbol != NULL
+ && S_IS_DEFINED (fragp->fr_symbol)
+ && !S_IS_WEAK (fragp->fr_symbol)
+ && sec == S_GET_SEGMENT (fragp->fr_symbol))
+ {
+ offsetT val = S_GET_VALUE (fragp->fr_symbol) + fragp->fr_offset;
+
+ val -= fragp->fr_address + fragp->fr_fix;
+
+ if (RELAX_BRANCH_16 == fragp->fr_subtype
+ && OUT_OF_RANGE (val, 16, 2))
+ {
+ length = 8;
+ if (update)
+ fragp->fr_subtype = RELAX_BRANCH_26;
+ }
+
+ if (RELAX_BRANCH_21 == fragp->fr_subtype
+ && OUT_OF_RANGE (val, 21, 2))
+ {
+ length = 8;
+ if (update)
+ fragp->fr_subtype = RELAX_BRANCH_26;
+ }
+
+ if (RELAX_BRANCH_26 == fragp->fr_subtype)
+ length = 8;
+ }
+
+ return length;
+}
+
+int
+loongarch_relax_frag (asection *sec ATTRIBUTE_UNUSED,
+ fragS *fragp ATTRIBUTE_UNUSED,
+ long stretch ATTRIBUTE_UNUSED)
+{
+ if (RELAX_BRANCH (fragp->fr_subtype))
+ {
+ offsetT old_var = fragp->fr_var;
+ fragp->fr_var = loongarch_relaxed_branch_length (fragp, sec, true);
+ return fragp->fr_var - old_var;
+ }
+ return 0;
+}
+
+/* Expand far branches to multi-instruction sequences.
+ Branch instructions:
+ beq, bne, blt, bgt, bltz, bgtz, ble, bge, blez, bgez
+ bltu, bgtu, bleu, bgeu
+ beqz, bnez, bceqz, bcnez. */
+
+static void
+loongarch_convert_frag_branch (fragS *fragp)
+{
+ bfd_byte *buf;
+ expressionS exp;
+ fixS *fixp;
+ insn_t insn;
+
+ buf = (bfd_byte *)fragp->fr_literal + fragp->fr_fix;
+
+ exp.X_op = O_symbol;
+ exp.X_add_symbol = fragp->fr_symbol;
+ exp.X_add_number = fragp->fr_offset;
+
+ gas_assert ((fragp->fr_subtype & 0xf) == fragp->fr_var);
+
+ /* blt $t0, $t1, .L1
+ nop
+ change to:
+ bge $t0, $t1, .L2
+ b .L1
+ .L2:
+ nop */
+ switch (fragp->fr_subtype)
+ {
+ case RELAX_BRANCH_26:
+ insn = bfd_getl32 (buf);
+ /* Invert the branch condition. */
+ if (LARCH_FLOAT_BRANCH == (insn & LARCH_BRANCH_OPCODE_MASK))
+ insn ^= LARCH_FLOAT_BRANCH_INVERT_BIT;
+ else
+ insn ^= LARCH_BRANCH_INVERT_BIT;
+ insn |= ENCODE_BRANCH16_IMM (8); /* Set target to PC + 8. */
+ bfd_putl32 (insn, buf);
+ buf += 4;
+
+ /* Add the B instruction and jump to the original target. */
+ bfd_putl32 (LARCH_B, buf);
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, false, BFD_RELOC_LARCH_B26);
+ buf += 4;
+ break;
+ case RELAX_BRANCH_21:
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, false, BFD_RELOC_LARCH_B21);
+ buf += 4;
+ break;
+ case RELAX_BRANCH_16:
+ fixp = fix_new_exp (fragp, buf - (bfd_byte *)fragp->fr_literal,
+ 4, &exp, false, BFD_RELOC_LARCH_B16);
+ buf += 4;
+ break;
+
+ default:
+ abort();
+ }
+
+ fixp->fx_file = fragp->fr_file;
+ fixp->fx_line = fragp->fr_line;
+
+ gas_assert (buf == (bfd_byte *)fragp->fr_literal
+ + fragp->fr_fix + fragp->fr_var);
+
+ fragp->fr_fix += fragp->fr_var;
+}
+
+/* Relax a machine dependent frag. This returns the amount by which
+ the current size of the frag should change. */
+
+void
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT asec ATTRIBUTE_UNUSED,
+ fragS *fragp)
+{
+ gas_assert (RELAX_BRANCH (fragp->fr_subtype));
+ loongarch_convert_frag_branch (fragp);
+}
diff --git a/gas/testsuite/gas/loongarch/la_branch_relax_1.d b/gas/testsuite/gas/loongarch/la_branch_relax_1.d
new file mode 100644
index 0000000..7984b6d
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/la_branch_relax_1.d
@@ -0,0 +1,64 @@
+#as:
+#objdump: -dr
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0* <.L1>:
+[ ]+...
+[ ]+48d158:[ ]+5c00098d[ ]+bne[ ]+\$t0, \$t1, 8[ ]+# 48d160 <.L1\+0x48d160>
+[ ]+48d15c:[ ]+532ea7ed[ ]+b[ ]+-4772188[ ]+# 0 <.L1>
+[ ]+48d15c: R_LARCH_B26[ ]+.L1
+[ ]+48d160:[ ]+5800098d[ ]+beq[ ]+\$t0, \$t1, 8[ ]+# 48d168 <.L1\+0x48d168>
+[ ]+48d164:[ ]+532e9fed[ ]+b[ ]+-4772196[ ]+# 0 <.L1>
+[ ]+48d164: R_LARCH_B26[ ]+.L1
+[ ]+48d168:[ ]+6400098d[ ]+bge[ ]+\$t0, \$t1, 8[ ]+# 48d170 <.L1\+0x48d170>
+[ ]+48d16c:[ ]+532e97ed[ ]+b[ ]+-4772204[ ]+# 0 <.L1>
+[ ]+48d16c: R_LARCH_B26[ ]+.L1
+[ ]+48d170:[ ]+640009ac[ ]+bge[ ]+\$t1, \$t0, 8[ ]+# 48d178 <.L1\+0x48d178>
+[ ]+48d174:[ ]+532e8fed[ ]+b[ ]+-4772212[ ]+# 0 <.L1>
+[ ]+48d174: R_LARCH_B26[ ]+.L1
+[ ]+48d178:[ ]+64000980[ ]+bgez[ ]+\$t0, 8[ ]+# 48d180 <.L1\+0x48d180>
+[ ]+48d17c:[ ]+532e87ed[ ]+b[ ]+-4772220[ ]+# 0 <.L1>
+[ ]+48d17c: R_LARCH_B26[ ]+.L1
+[ ]+48d180:[ ]+6400080c[ ]+blez[ ]+\$t0, 8[ ]+# 48d188 <.L1\+0x48d188>
+[ ]+48d184:[ ]+532e7fed[ ]+b[ ]+-4772228[ ]+# 0 <.L1>
+[ ]+48d184: R_LARCH_B26[ ]+.L1
+[ ]+48d188:[ ]+600009ac[ ]+blt[ ]+\$t1, \$t0, 8[ ]+# 48d190 <.L1\+0x48d190>
+[ ]+48d18c:[ ]+532e77ed[ ]+b[ ]+-4772236[ ]+# 0 <.L1>
+[ ]+48d18c: R_LARCH_B26[ ]+.L1
+[ ]+48d190:[ ]+6000098d[ ]+blt[ ]+\$t0, \$t1, 8[ ]+# 48d198 <.L1\+0x48d198>
+[ ]+48d194:[ ]+532e6fed[ ]+b[ ]+-4772244[ ]+# 0 <.L1>
+[ ]+48d194: R_LARCH_B26[ ]+.L1
+[ ]+48d198:[ ]+6000080c[ ]+bgtz[ ]+\$t0, 8[ ]+# 48d1a0 <.L1\+0x48d1a0>
+[ ]+48d19c:[ ]+532e67ed[ ]+b[ ]+-4772252[ ]+# 0 <.L1>
+[ ]+48d19c: R_LARCH_B26[ ]+.L1
+[ ]+48d1a0:[ ]+60000980[ ]+bltz[ ]+\$t0, 8[ ]+# 48d1a8 <.L1\+0x48d1a8>
+[ ]+48d1a4:[ ]+532e5fed[ ]+b[ ]+-4772260[ ]+# 0 <.L1>
+[ ]+48d1a4: R_LARCH_B26[ ]+.L1
+[ ]+48d1a8:[ ]+6c00098d[ ]+bgeu[ ]+\$t0, \$t1, 8[ ]+# 48d1b0 <.L1\+0x48d1b0>
+[ ]+48d1ac:[ ]+532e57ed[ ]+b[ ]+-4772268[ ]+# 0 <.L1>
+[ ]+48d1ac: R_LARCH_B26[ ]+.L1
+[ ]+48d1b0:[ ]+6c0009ac[ ]+bgeu[ ]+\$t1, \$t0, 8[ ]+# 48d1b8 <.L1\+0x48d1b8>
+[ ]+48d1b4:[ ]+532e4fed[ ]+b[ ]+-4772276[ ]+# 0 <.L1>
+[ ]+48d1b4: R_LARCH_B26[ ]+.L1
+[ ]+48d1b8:[ ]+680009ac[ ]+bltu[ ]+\$t1, \$t0, 8[ ]+# 48d1c0 <.L1\+0x48d1c0>
+[ ]+48d1bc:[ ]+532e47ed[ ]+b[ ]+-4772284[ ]+# 0 <.L1>
+[ ]+48d1bc: R_LARCH_B26[ ]+.L1
+[ ]+48d1c0:[ ]+6800098d[ ]+bltu[ ]+\$t0, \$t1, 8[ ]+# 48d1c8 <.L1\+0x48d1c8>
+[ ]+48d1c4:[ ]+532e3fed[ ]+b[ ]+-4772292[ ]+# 0 <.L1>
+[ ]+48d1c4: R_LARCH_B26[ ]+.L1
+[ ]+48d1c8:[ ]+44000980[ ]+bnez[ ]+\$t0, 8[ ]+# 48d1d0 <.L1\+0x48d1d0>
+[ ]+48d1cc:[ ]+532e37ed[ ]+b[ ]+-4772300[ ]+# 0 <.L1>
+[ ]+48d1cc: R_LARCH_B26[ ]+.L1
+[ ]+48d1d0:[ ]+40000980[ ]+beqz[ ]+\$t0, 8[ ]+# 48d1d8 <.L1\+0x48d1d8>
+[ ]+48d1d4:[ ]+532e2fed[ ]+b[ ]+-4772308[ ]+# 0 <.L1>
+[ ]+48d1d4: R_LARCH_B26[ ]+.L1
+[ ]+48d1d8:[ ]+48000900[ ]+bcnez[ ]+\$fcc0, 8[ ]+# 48d1e0 <.L1\+0x48d1e0>
+[ ]+48d1dc:[ ]+532e27ed[ ]+b[ ]+-4772316[ ]+# 0 <.L1>
+[ ]+48d1dc: R_LARCH_B26[ ]+.L1
+[ ]+48d1e0:[ ]+48000800[ ]+bceqz[ ]+\$fcc0, 8[ ]+# 48d1e8 <.L1\+0x48d1e8>
+[ ]+48d1e4:[ ]+532e1fed[ ]+b[ ]+-4772324[ ]+# 0 <.L1>
+[ ]+48d1e4: R_LARCH_B26[ ]+.L1
diff --git a/gas/testsuite/gas/loongarch/la_branch_relax_1.s b/gas/testsuite/gas/loongarch/la_branch_relax_1.s
new file mode 100644
index 0000000..53288f2
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/la_branch_relax_1.s
@@ -0,0 +1,33 @@
+# Instruction and Relocation generating tests
+
+.L1:
+ .fill 0x123456, 4, 0x0
+
+# R_LARCH_B16
+ beq $r12, $r13, .L1
+ bne $r12, $r13, .L1
+
+ blt $r12, $r13, .L1
+ bgt $r12, $r13, .L1
+
+ bltz $r12, .L1
+ bgtz $r12, .L1
+
+ ble $r12, $r13, .L1
+ bge $r12, $r13, .L1
+
+ blez $r12, .L1
+ bgez $r12, .L1
+
+ bltu $r12, $r13, .L1
+ bgtu $r12, $r13, .L1
+
+ bleu $r12, $r13, .L1
+ bgeu $r12, $r13, .L1
+
+# R_LARCH_B21
+ beqz $r12, .L1
+ bnez $r12, .L1
+
+ bceqz $fcc0, .L1
+ bcnez $fcc0, .L1
diff --git a/gas/testsuite/gas/loongarch/la_branch_relax_2.d b/gas/testsuite/gas/loongarch/la_branch_relax_2.d
new file mode 100644
index 0000000..4a3c638
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/la_branch_relax_2.d
@@ -0,0 +1,40 @@
+#as:
+#objdump: -dr
+
+.*:[ ]+file format .*
+
+
+Disassembly of section .text:
+
+0* <.L1>:
+[ ]+...
+[ ]+20000:[ ]+5a00018d[ ]+beq[ ]+\$t0, \$t1, -131072[ ]+# 0 <.L1>
+[ ]+20000: R_LARCH_B16[ ]+.L1
+[ ]+20004:[ ]+5c00098d[ ]+bne[ ]+\$t0, \$t1, 8[ ]+# 2000c <.L1\+0x2000c>
+[ ]+20008:[ ]+51fffbff[ ]+b[ ]+-131080[ ]+# 0 <.L1>
+[ ]+20008: R_LARCH_B26[ ]+.L1
+[ ]+2000c:[ ]+5c00098d[ ]+bne[ ]+\$t0, \$t1, 8[ ]+# 20014 <.L1\+0x20014>
+[ ]+20010:[ ]+52000000[ ]+b[ ]+131072[ ]+# 40010 <.L2>
+[ ]+20010: R_LARCH_B26[ ]+.L2
+[ ]+20014:[ ]+59fffd8d[ ]+beq[ ]+\$t0, \$t1, 131068[ ]+# 40010 <.L2>
+[ ]+20014: R_LARCH_B16[ ]+.L2
+[ ]+...
+0*40010 <.L2>:
+[ ]+...
+[ ]+440010:[ ]+40000190[ ]+beqz[ ]+\$t0, -4194304[ ]+# 40010 <.L2>
+[ ]+440010: R_LARCH_B21[ ]+.L2
+[ ]+440014:[ ]+44000980[ ]+bnez[ ]+\$t0, 8[ ]+# 44001c <.L2\+0x40000c>
+[ ]+440018:[ ]+53fffbef[ ]+b[ ]+-4194312[ ]+# 40010 <.L2>
+[ ]+440018: R_LARCH_B26[ ]+.L2
+[ ]+44001c:[ ]+44000980[ ]+bnez[ ]+\$t0, 8[ ]+# 440024 <.L2\+0x400014>
+[ ]+440020:[ ]+50000010[ ]+b[ ]+4194304[ ]+# 840020 <.L3>
+[ ]+440020: R_LARCH_B26[ ]+.L3
+[ ]+440024:[ ]+43fffd8f[ ]+beqz[ ]+\$t0, 4194300[ ]+# 840020 <.L3>
+[ ]+440024: R_LARCH_B21[ ]+.L3
+[ ]+...
+0*840020 <.L3>:
+[ ]+840020:[ ]+5800018d[ ]+beq[ ]+\$t0, \$t1, 0[ ]+# 840020 <.L3>
+[ ]+840020: R_LARCH_B16[ ]+.L4
+0*840024 <.L5>:
+[ ]+840024:[ ]+40000180[ ]+beqz[ ]+\$t0, 0[ ]+# 840024 <.L5>
+[ ]+840024: R_LARCH_B21[ ]+.L5
diff --git a/gas/testsuite/gas/loongarch/la_branch_relax_2.s b/gas/testsuite/gas/loongarch/la_branch_relax_2.s
new file mode 100644
index 0000000..3e6c553
--- /dev/null
+++ b/gas/testsuite/gas/loongarch/la_branch_relax_2.s
@@ -0,0 +1,23 @@
+# Immediate boundary value tests
+
+.L1:
+ .fill 0x8000, 4, 0
+ beq $r12, $r13, .L1 # min imm -0x20000
+ beq $r12, $r13, .L1 # out of range
+ beq $r12, $r13, .L2 # out of range
+ beq $r12, $r13, .L2 # max imm 0x1fffc
+ .fill 0x7ffe, 4, 0
+.L2:
+ .fill 0x100000, 4, 0
+ beqz $r12, .L2 # min imm -0x400000
+ beqz $r12, .L2 # out of range
+ beqz $r12, .L3 # out of range
+ beqz $r12, .L3 # max imm 0x3ffffc
+ .fill 0xffffe, 4, 0
+.L3:
+
+# 0 imm
+.L4:
+ beq $r12, $r13, .L4
+.L5:
+ beqz $r12, .L5
diff --git a/include/opcode/loongarch.h b/include/opcode/loongarch.h
index 2ed4082..f358ff4 100644
--- a/include/opcode/loongarch.h
+++ b/include/opcode/loongarch.h
@@ -29,6 +29,18 @@ extern "C"
#endif
#define LARCH_NOP 0x03400000
+ #define LARCH_B 0x50000000
+ /* BCEQZ/BCNEZ. */
+ #define LARCH_FLOAT_BRANCH 0x48000000
+ #define LARCH_BRANCH_OPCODE_MASK 0xfc000000
+ #define LARCH_BRANCH_INVERT_BIT 0x04000000
+ #define LARCH_FLOAT_BRANCH_INVERT_BIT 0x00000100
+
+ #define ENCODE_BRANCH16_IMM(x) (((x) >> 2) << 10)
+
+ #define OUT_OF_RANGE(value, bits, align) \
+ ((value) < (-(1 << ((bits) - 1) << align)) \
+ || (value) > ((((1 << ((bits) - 1)) - 1) << align)))
typedef uint32_t insn_t;