[llvm] [lld] [LoongArch] MC relaxation for out-of-range conditional branch (PR #72095)

Jinyang He via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 13 01:48:32 PST 2023


https://github.com/MQ-mengqing created https://github.com/llvm/llvm-project/pull/72095

For the conditional branch with R_LARCH_{B16, B21} relocs, if the imm overflow, convert it to an inverted conditional branch and an unconditional jump with R_LARCH_B26 reloc.

For example,

```
  beqz $t0, .L1
    R_LARCH_B16
```

change to

```
  bnez $t0, <8>
  b .L1
    R_LARCH_B26
```

If the symbol is unresolved at assembly time, not do this relaxation. To disable branch relaxation, use `-loongarch-asm-relax-branches=0`.

>From dac7740e0f0ff59d2f02a2e356fc1a9ca71ecfae Mon Sep 17 00:00:00 2001
From: Jinyang He <hejinyang at loongson.cn>
Date: Fri, 3 Nov 2023 10:18:47 +0800
Subject: [PATCH] [LoongArch] MC relaxation for out-of-range conditional branch

For the conditional branch with R_LARCH_{B16, B21} relocs, if the imm
overflow, convert it to an inverted conditional branch and an
unconditional jump with R_LARCH_B26 reloc.

For example,

```
  beqz $t0, .L1
    R_LARCH_B16
```

change to

```
  bnez $t0, <8>
  b .L1
    R_LARCH_B26
```

If the symbol is unresolved at assembly time, not do this relaxation.
To disable branch relaxation, use `-loongarch-asm-relax-branches=0`.
---
 lld/test/ELF/loongarch-branch.s               |   4 +-
 .../LoongArch/LoongArchFloat32InstrInfo.td    |   7 ++
 .../Target/LoongArch/LoongArchInstrInfo.td    |  19 ++++
 .../MCTargetDesc/LoongArchAsmBackend.cpp      |  89 +++++++++++++++
 .../MCTargetDesc/LoongArchAsmBackend.h        |  13 ++-
 .../MCTargetDesc/LoongArchMCCodeEmitter.cpp   |  83 ++++++++++++++
 .../MC/LoongArch/Misc/long-conditional-jump.s | 104 ++++++++++++++++++
 .../Relocations/fixups-diagnostics.s          |   2 +-
 8 files changed, 316 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/MC/LoongArch/Misc/long-conditional-jump.s

diff --git a/lld/test/ELF/loongarch-branch.s b/lld/test/ELF/loongarch-branch.s
index b223ff95bd89a8c..40f82cceaa96d21 100644
--- a/lld/test/ELF/loongarch-branch.s
+++ b/lld/test/ELF/loongarch-branch.s
@@ -1,7 +1,7 @@
 # REQUIRES: loongarch
 
-# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf %s -o %t.la32.o
-# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf %s -o %t.la64.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch32-unknown-elf -loongarch-asm-relax-branches=0 %s -o %t.la32.o
+# RUN: llvm-mc --filetype=obj --triple=loongarch64-unknown-elf -loongarch-asm-relax-branches=0 %s -o %t.la64.o
 
 # RUN: ld.lld %t.la32.o --defsym foo16=b16+4 --defsym bar16=b16 --defsym foo21=b21+4 --defsym bar21=b21 --defsym foo26=b26+4 --defsym bar26=b26 -o %t.la32
 # RUN: ld.lld %t.la64.o --defsym foo16=b16+4 --defsym bar16=b16 --defsym foo21=b21+4 --defsym bar21=b21 --defsym foo26=b26+4 --defsym bar26=b26 -o %t.la64
diff --git a/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td
index 6f35609df705f66..3fa46e95938e352 100644
--- a/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td
@@ -143,6 +143,13 @@ def PseudoCopyCFR : Pseudo<(outs CFR:$dst), (ins CFR:$src)> {
   let Size = 12;
 }
 
+// Pseduo float conditional long jump instructions.
+let isBarrier = 1, isBranch = 1, hasSideEffects = 0, mayLoad = 0,
+    mayStore = 0, isAsmParserOnly = 1, hasNoSchedulingInfo = 1 in {
+def PseudoLongBCEQZ : Pseudo<(outs), (ins CFR:$cc, simm26_b:$imm26), []>;
+def PseudoLongBCNEZ : Pseudo<(outs), (ins CFR:$cc, simm26_b:$imm26), []>;
+}
+
 } // Predicates = [HasBasicF]
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
index d030fc9aa51ca23..0ff804662340a01 100644
--- a/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
+++ b/llvm/lib/Target/LoongArch/LoongArchInstrInfo.td
@@ -1438,6 +1438,25 @@ def PseudoJIRL_TAIL : Pseudo<(outs), (ins GPR:$rj, simm16_lsl2:$imm16)>,
                       PseudoInstExpansion<(JIRL R0, GPR:$rj,
                                            simm16_lsl2:$imm16)>;
 
+// Pseduo conditional long jump instructions.
+let isBarrier = 1, isBranch = 1, hasSideEffects = 0, mayLoad = 0,
+    mayStore = 0, isAsmParserOnly = 1, hasNoSchedulingInfo = 1 in {
+def PseudoLongBEQ : Pseudo<(outs),
+                           (ins GPR:$rs1, GPR:$rs2, simm26_b:$imm26), []>;
+def PseudoLongBNE : Pseudo<(outs),
+                           (ins GPR:$rs1, GPR:$rs2, simm26_b:$imm26), []>;
+def PseudoLongBLT : Pseudo<(outs),
+                           (ins GPR:$rs1, GPR:$rs2, simm26_b:$imm26), []>;
+def PseudoLongBGE : Pseudo<(outs),
+                           (ins GPR:$rs1, GPR:$rs2, simm26_b:$imm26), []>;
+def PseudoLongBLTU : Pseudo<(outs),
+                           (ins GPR:$rs1, GPR:$rs2, simm26_b:$imm26), []>;
+def PseudoLongBGEU : Pseudo<(outs),
+                           (ins GPR:$rs1, GPR:$rs2, simm26_b:$imm26), []>;
+def PseudoLongBEQZ : Pseudo<(outs), (ins GPR:$rs1, simm26_b:$imm26), []>;
+def PseudoLongBNEZ : Pseudo<(outs), (ins GPR:$rs1, simm26_b:$imm26), []>;
+}
+
 /// Load address (la*) macro instructions.
 
 // Define isCodeGenOnly = 0 to expose them to tablegened assembly parser.
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
index 8744bcb45f75f30..b6b0597774377d9 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp
@@ -17,12 +17,16 @@
 #include "llvm/MC/MCContext.h"
 #include "llvm/MC/MCELFObjectWriter.h"
 #include "llvm/MC/MCValue.h"
+#include "llvm/Support/CommandLine.h"
 #include "llvm/Support/EndianStream.h"
 
 #define DEBUG_TYPE "loongarch-asmbackend"
 
 using namespace llvm;
 
+static cl::opt<bool> RelaxBranches("loongarch-asm-relax-branches",
+                                   cl::init(true), cl::Hidden);
+
 std::optional<MCFixupKind>
 LoongArchAsmBackend::getFixupKind(StringRef Name) const {
   if (STI.getTargetTriple().isOSBinFormatELF()) {
@@ -176,6 +180,91 @@ bool LoongArchAsmBackend::shouldForceRelocation(const MCAssembler &Asm,
   }
 }
 
+bool LoongArchAsmBackend::fixupNeedsRelaxationAdvanced(
+    const MCFixup &Fixup, bool Resolved, uint64_t Value,
+    const MCRelaxableFragment *DF, const MCAsmLayout &Layout,
+    const bool WasForced) const {
+  if (!RelaxBranches)
+    return false;
+
+  int64_t Offset = int64_t(Value);
+  unsigned Kind = Fixup.getTargetKind();
+
+  // Do not relax unresolved conditional branch to consist with GAS.
+  if (!Resolved)
+    return false;
+
+  switch (Kind) {
+  default:
+    return false;
+  case LoongArch::fixup_loongarch_b16:
+    return !isInt<18>(Offset);
+  case LoongArch::fixup_loongarch_b21:
+    return !isInt<23>(Offset);
+  }
+}
+
+static unsigned getRelaxedOpcode(unsigned Op) {
+  switch (Op) {
+  default:
+    return Op;
+  case LoongArch::BEQ:
+    return LoongArch::PseudoLongBEQ;
+  case LoongArch::BNE:
+    return LoongArch::PseudoLongBNE;
+  case LoongArch::BLT:
+    return LoongArch::PseudoLongBLT;
+  case LoongArch::BGE:
+    return LoongArch::PseudoLongBGE;
+  case LoongArch::BLTU:
+    return LoongArch::PseudoLongBLTU;
+  case LoongArch::BGEU:
+    return LoongArch::PseudoLongBGEU;
+  case LoongArch::BEQZ:
+    return LoongArch::PseudoLongBEQZ;
+  case LoongArch::BNEZ:
+    return LoongArch::PseudoLongBNEZ;
+  case LoongArch::BCEQZ:
+    return LoongArch::PseudoLongBCEQZ;
+  case LoongArch::BCNEZ:
+    return LoongArch::PseudoLongBCNEZ;
+  }
+}
+
+void LoongArchAsmBackend::relaxInstruction(MCInst &Inst,
+                                           const MCSubtargetInfo &STI) const {
+  MCInst Res;
+  switch (Inst.getOpcode()) {
+  default:
+    llvm_unreachable("Opcode not expected!");
+  case LoongArch::BEQ:
+  case LoongArch::BNE:
+  case LoongArch::BLT:
+  case LoongArch::BGE:
+  case LoongArch::BLTU:
+  case LoongArch::BGEU:
+    Res.setOpcode(getRelaxedOpcode(Inst.getOpcode()));
+    Res.addOperand(Inst.getOperand(0));
+    Res.addOperand(Inst.getOperand(1));
+    Res.addOperand(Inst.getOperand(2));
+    break;
+  case LoongArch::BEQZ:
+  case LoongArch::BNEZ:
+  case LoongArch::BCEQZ:
+  case LoongArch::BCNEZ:
+    Res.setOpcode(getRelaxedOpcode(Inst.getOpcode()));
+    Res.addOperand(Inst.getOperand(0));
+    Res.addOperand(Inst.getOperand(1));
+    break;
+  }
+  Inst = std::move(Res);
+}
+
+bool LoongArchAsmBackend::mayNeedRelaxation(const MCInst &Inst,
+                                            const MCSubtargetInfo &STI) const {
+  return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode();
+}
+
 bool LoongArchAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
                                        const MCSubtargetInfo *STI) const {
   // We mostly follow binutils' convention here: align to 4-byte boundary with a
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
index f840f9fa2b6a007..adf7456161002fc 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h
@@ -46,9 +46,15 @@ class LoongArchAsmBackend : public MCAsmBackend {
   bool fixupNeedsRelaxation(const MCFixup &Fixup, uint64_t Value,
                             const MCRelaxableFragment *DF,
                             const MCAsmLayout &Layout) const override {
-    return false;
+    llvm_unreachable("Handled by fixupNeedsRelaxationAdvanced");
   }
 
+  bool fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, bool Resolved,
+                                    uint64_t Value,
+                                    const MCRelaxableFragment *DF,
+                                    const MCAsmLayout &Layout,
+                                    const bool WasForced) const override;
+
   unsigned getNumFixupKinds() const override {
     return LoongArch::NumTargetFixupKinds;
   }
@@ -57,8 +63,11 @@ class LoongArchAsmBackend : public MCAsmBackend {
 
   const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override;
 
+  bool mayNeedRelaxation(const MCInst &Inst,
+                         const MCSubtargetInfo &STI) const override;
+
   void relaxInstruction(MCInst &Inst,
-                        const MCSubtargetInfo &STI) const override {}
+                        const MCSubtargetInfo &STI) const override;
 
   bool writeNopData(raw_ostream &OS, uint64_t Count,
                     const MCSubtargetInfo *STI) const override;
diff --git a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
index fbe817a2b5475a2..c45fb055c4e6343 100644
--- a/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
+++ b/llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp
@@ -48,6 +48,10 @@ class LoongArchMCCodeEmitter : public MCCodeEmitter {
                          SmallVectorImpl<MCFixup> &Fixups,
                          const MCSubtargetInfo &STI) const;
 
+  void expandLongCondBr(const MCInst &MI, SmallVectorImpl<char> &CB,
+                        SmallVectorImpl<MCFixup> &Fixups,
+                        const MCSubtargetInfo &STI) const;
+
   /// TableGen'erated function for getting the binary encoding for an
   /// instruction.
   uint64_t getBinaryCodeForInstr(const MCInst &MI,
@@ -299,6 +303,74 @@ void LoongArchMCCodeEmitter::expandToVectorLDI(
   support::endian::write(CB, Binary, llvm::endianness::little);
 }
 
+static unsigned getInvertedBranchOp(unsigned BrOp) {
+  switch (BrOp) {
+  default:
+    llvm_unreachable("Unexpected branch opcode!");
+  case LoongArch::PseudoLongBEQ:
+    return LoongArch::BNE;
+  case LoongArch::PseudoLongBNE:
+    return LoongArch::BEQ;
+  case LoongArch::PseudoLongBLT:
+    return LoongArch::BGE;
+  case LoongArch::PseudoLongBGE:
+    return LoongArch::BLT;
+  case LoongArch::PseudoLongBLTU:
+    return LoongArch::BGEU;
+  case LoongArch::PseudoLongBGEU:
+    return LoongArch::BLTU;
+  case LoongArch::PseudoLongBEQZ:
+    return LoongArch::BNEZ;
+  case LoongArch::PseudoLongBNEZ:
+    return LoongArch::BEQZ;
+  case LoongArch::PseudoLongBCEQZ:
+    return LoongArch::BCNEZ;
+  case LoongArch::PseudoLongBCNEZ:
+    return LoongArch::BCEQZ;
+  }
+}
+
+// Expand PseudoLongBxx to an inverted conditional branch and an unconditional
+// jump.
+void LoongArchMCCodeEmitter::expandLongCondBr(
+    const MCInst &MI, SmallVectorImpl<char> &CB,
+    SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const {
+  uint32_t Binary;
+  MCInst TmpInst;
+  MCRegister SrcReg1, SrcReg2;
+  MCOperand SrcSymbol;
+  unsigned InvOpc = getInvertedBranchOp(MI.getOpcode());
+  bool IsSingleReg = InvOpc == LoongArch::BEQZ || InvOpc == LoongArch::BNEZ ||
+                     InvOpc == LoongArch::BCEQZ || InvOpc == LoongArch::BCNEZ;
+
+  // Emit an inverted conditional jump out of this branch
+  if (IsSingleReg) {
+    SrcReg1 = MI.getOperand(0).getReg();
+    SrcSymbol = MI.getOperand(1);
+    TmpInst = MCInstBuilder(InvOpc).addReg(SrcReg1).addImm(8);
+  } else {
+    SrcReg1 = MI.getOperand(0).getReg();
+    SrcReg2 = MI.getOperand(1).getReg();
+    SrcSymbol = MI.getOperand(2);
+    TmpInst = MCInstBuilder(InvOpc).addReg(SrcReg1).addReg(SrcReg2).addImm(8);
+  }
+  Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
+  support::endian::write(CB, Binary, llvm::endianness::little);
+
+  // Emit an unconditional jump to the destination.
+  TmpInst = MCInstBuilder(LoongArch::B).addOperand(SrcSymbol);
+  Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI);
+  support::endian::write(CB, Binary, llvm::endianness::little);
+
+  Fixups.clear();
+  if (!SrcSymbol.isExpr())
+    return;
+
+  Fixups.push_back(MCFixup::create(4, SrcSymbol.getExpr(),
+                                   MCFixupKind(LoongArch::fixup_loongarch_b26),
+                                   MI.getLoc()));
+}
+
 void LoongArchMCCodeEmitter::encodeInstruction(
     const MCInst &MI, SmallVectorImpl<char> &CB,
     SmallVectorImpl<MCFixup> &Fixups, const MCSubtargetInfo &STI) const {
@@ -319,6 +391,17 @@ void LoongArchMCCodeEmitter::encodeInstruction(
   case LoongArch::PseudoXVREPLI_W:
   case LoongArch::PseudoXVREPLI_D:
     return expandToVectorLDI<LoongArch::XVLDI>(MI, CB, Fixups, STI);
+  case LoongArch::PseudoLongBEQ:
+  case LoongArch::PseudoLongBNE:
+  case LoongArch::PseudoLongBLT:
+  case LoongArch::PseudoLongBGE:
+  case LoongArch::PseudoLongBLTU:
+  case LoongArch::PseudoLongBGEU:
+  case LoongArch::PseudoLongBCEQZ:
+  case LoongArch::PseudoLongBCNEZ:
+  case LoongArch::PseudoLongBEQZ:
+  case LoongArch::PseudoLongBNEZ:
+    return expandLongCondBr(MI, CB, Fixups, STI);
   }
 
   switch (Size) {
diff --git a/llvm/test/MC/LoongArch/Misc/long-conditional-jump.s b/llvm/test/MC/LoongArch/Misc/long-conditional-jump.s
new file mode 100644
index 000000000000000..a1e293ca4c4f839
--- /dev/null
+++ b/llvm/test/MC/LoongArch/Misc/long-conditional-jump.s
@@ -0,0 +1,104 @@
+# RUN: llvm-mc --filetype=obj --triple=loongarch64 %s -o %t.o
+# RUN: llvm-objdump -dr --no-show-raw-insn %t.o | FileCheck %s
+
+  .text
+  .type   test, at function
+test:
+  nop
+.L1:
+  .fill 0x100000, 4, 0x0
+
+## R_LARCH_B16
+
+# CHECK:         bne     $t0, $t1, 8
+# CHECK-NEXT:    b       -4194308
+  beq $t0, $t1, .L1
+
+# CHECK:         beq     $t0, $t1, 8
+# CHECK-NEXT:    b       -4194316
+  bne $t0, $t1, .L1
+
+# CHECK:         bge     $t0, $t1, 8
+# CHECK-NEXT:    b       -4194324
+  blt $t0, $t1, .L1
+
+# CHECK:         bge     $t1, $t0, 8
+# CHECK-NEXT:    b       -4194332
+  bgt $t0, $t1, .L1
+
+# CHECK:         bge     $t0, $zero, 8
+# CHECK-NEXT:    b       -4194340
+  bltz $t0, .L1
+
+# CHECK:         bge     $zero, $t0, 8
+# CHECK-NEXT:    b       -4194348
+  bgtz $t0, .L1
+
+# CHECK:         blt     $t1, $t0, 8
+# CHECK-NEXT:    b       -4194356
+  ble $t0, $t1, .L1
+
+# CHECK:         blt     $t0, $t1, 8
+# CHECK-NEXT:    b       -4194364
+  bge $t0, $t1, .L1
+
+# CHECK:         blt     $zero, $t0, 8
+# CHECK-NEXT:    b       -4194372
+  blez $t0, .L1
+
+# CHECK:         blt     $t0, $zero, 8
+# CHECK-NEXT:    b       -4194380
+  bgez $t0, .L1
+
+# CHECK:         bgeu    $t0, $t1, 8
+# CHECK-NEXT:    b       -4194388
+  bltu $t0, $t1, .L1
+
+# CHECK:         bgeu    $t1, $t0, 8
+# CHECK-NEXT:    b       -4194396
+  bgtu $t0, $t1, .L1
+
+# CHECK:         bltu    $t1, $t0, 8
+# CHECK-NEXT:    b       -4194404
+  bleu $t0, $t1, .L1
+
+# CHECK:         bltu    $t0, $t1, 8
+# CHECK-NEXT:    b       -4194412
+  bgeu $t0, $t1, .L1
+
+## R_LARCH_B21
+
+# CHECK:         bnez    $t0, 8
+# CHECK-NEXT:    b       -4194420
+  beqz $t0, .L1
+
+# CHECK:         beqz    $t0, 8
+# CHECK-NEXT:    b       -4194428
+  bnez $t0, .L1
+
+# CHECK:         bcnez   $fcc0, 8
+# CHECK-NEXT:    b       -4194436
+  bceqz $fcc0, .L1
+
+# CHECK:         bceqz   $fcc0, 8
+# CHECK-NEXT:    b       -4194444
+  bcnez $fcc0, .L1
+
+## Not relax if symbol is unresolved
+# CHECK:         bnez    $t0, 0
+# CHECK-NEXT:    R_LARCH_B21  foo
+# CHECK-NEXT:    bnez    $t0, 0
+# CHECK-NEXT:    R_LARCH_B21  .text2
+# CHECK-NEXT:    ret
+  bnez $t0, foo
+  bnez $t0, test2
+  ret
+.Lfunc_end0:
+  .size test, .Lfunc_end0-test
+
+  .section .text2, "ax"
+  .type   test2, at function
+test2:
+  ret
+.Lfunc_end1:
+  .size test2, .Lfunc_end1-test2
diff --git a/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s b/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s
index c72eef7cd991681..61f753aa08aaab8 100644
--- a/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s
+++ b/llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s
@@ -1,4 +1,4 @@
-# RUN: not llvm-mc --triple=loongarch64 --filetype=obj %s -o /dev/null 2>&1 | FileCheck %s
+# RUN: not llvm-mc --triple=loongarch64 --filetype=obj %s -loongarch-asm-relax-branches=0 -o /dev/null 2>&1 | FileCheck %s
 
   beq $a0, $a1, unaligned # CHECK: :[[#@LINE]]:3: error: fixup value must be 4-byte aligned
   beqz $a0, unaligned # CHECK: :[[#@LINE]]:3: error: fixup value must be 4-byte aligned



More information about the llvm-commits mailing list