[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