LoongArch/GAS: Add support for branch relaxation
authormengqinggang <mengqinggang@loongson.cn>
Sun, 24 Sep 2023 06:53:28 +0000 (14:53 +0800)
committerliuzhensong <liuzhensong@loongson.cn>
Tue, 10 Oct 2023 08:34:33 +0000 (16:34 +0800)
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

gas/config/tc-loongarch.c
gas/testsuite/gas/loongarch/la_branch_relax_1.d [new file with mode: 0644]
gas/testsuite/gas/loongarch/la_branch_relax_1.s [new file with mode: 0644]
gas/testsuite/gas/loongarch/la_branch_relax_2.d [new file with mode: 0644]
gas/testsuite/gas/loongarch/la_branch_relax_2.s [new file with mode: 0644]
include/opcode/loongarch.h

index d1d21fadb777021ebbcb710915dca5b82ed74868..b563982b933167223da2bf82c194b9acac58eddf 100644 (file)
@@ -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)
@@ -1487,14 +1534,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 (file)
index 0000000..7984b6d
--- /dev/null
@@ -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 (file)
index 0000000..53288f2
--- /dev/null
@@ -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 (file)
index 0000000..4a3c638
--- /dev/null
@@ -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 (file)
index 0000000..3e6c553
--- /dev/null
@@ -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
index 2ed4082cf43ab68661e7a39344cbdbd403b1c02b..f358ff429e4f33cfcf8a3c095e722ce58f44807c 100644 (file)
@@ -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;