530 lines
16 KiB
Diff
530 lines
16 KiB
Diff
From f9249a44e8e12e90cf5a49729107f69661ed07ec Mon Sep 17 00:00:00 2001
|
|
From: mengqinggang <mengqinggang@loongson.cn>
|
|
Date: Sun, 24 Sep 2023 14:53:28 +0800
|
|
Subject: [PATCH 013/123] 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
|
|
---
|
|
gas/config/tc-loongarch.c | 236 +++++++++++++++---
|
|
.../gas/loongarch/la_branch_relax_1.d | 64 +++++
|
|
.../gas/loongarch/la_branch_relax_1.s | 33 +++
|
|
.../gas/loongarch/la_branch_relax_2.d | 40 +++
|
|
.../gas/loongarch/la_branch_relax_2.s | 23 ++
|
|
include/opcode/loongarch.h | 12 +
|
|
6 files changed, 367 insertions(+), 41 deletions(-)
|
|
create mode 100644 gas/testsuite/gas/loongarch/la_branch_relax_1.d
|
|
create mode 100644 gas/testsuite/gas/loongarch/la_branch_relax_1.s
|
|
create mode 100644 gas/testsuite/gas/loongarch/la_branch_relax_2.d
|
|
create mode 100644 gas/testsuite/gas/loongarch/la_branch_relax_2.s
|
|
|
|
diff --git a/gas/config/tc-loongarch.c b/gas/config/tc-loongarch.c
|
|
index 4c48382c..059a1711 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,
|
|
@@ -955,11 +965,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;
|
|
|
|
@@ -967,14 +988,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)
|
|
@@ -1489,14 +1536,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)
|
|
@@ -1528,30 +1567,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)
|
|
@@ -1777,3 +1792,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 00000000..7984b6df
|
|
--- /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 00000000..53288f25
|
|
--- /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 00000000..4a3c6384
|
|
--- /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 00000000..3e6c5534
|
|
--- /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 2ed4082c..f358ff42 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;
|
|
|
|
--
|
|
2.33.0
|
|
|