Skip to content

Conversation

@MQ-mengqing
Copy link
Contributor

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.

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`.
@llvmbot
Copy link
Member

llvmbot commented Nov 13, 2023

@llvm/pr-subscribers-lld-elf
@llvm/pr-subscribers-lld
@llvm/pr-subscribers-backend-loongarch

@llvm/pr-subscribers-mc

Author: Jinyang He (MQ-mengqing)

Changes

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, &lt;8&gt; 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.


Full diff: https://github.com/llvm/llvm-project/pull/72095.diff

8 Files Affected:

  • (modified) lld/test/ELF/loongarch-branch.s (+2-2)
  • (modified) llvm/lib/Target/LoongArch/LoongArchFloat32InstrInfo.td (+7)
  • (modified) llvm/lib/Target/LoongArch/LoongArchInstrInfo.td (+19)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.cpp (+89)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchAsmBackend.h (+11-2)
  • (modified) llvm/lib/Target/LoongArch/MCTargetDesc/LoongArchMCCodeEmitter.cpp (+83)
  • (added) llvm/test/MC/LoongArch/Misc/long-conditional-jump.s (+104)
  • (modified) llvm/test/MC/LoongArch/Relocations/fixups-diagnostics.s (+1-1)
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,@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,@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 
@MQ-mengqing MQ-mengqing marked this pull request as draft November 13, 2023 10:05
@MQ-mengqing
Copy link
Contributor Author

@SixWeining
I am not familiar with llvm github development, so please point out if anything wrong for improvement. And I do not find the entry to add reviewers now.

@MQ-mengqing
Copy link
Contributor Author

@MaskRay
Copy link
Member

MaskRay commented Nov 14, 2023

Assembler relaxing out-of-range conditional branches is probably not a good idea. See https://reviews.llvm.org/D108961
If you can drop the gas code, do it, and it will be better than RISC-V :)

@MQ-mengqing
Copy link
Contributor Author

Assembler relaxing out-of-range conditional branches is probably not a good idea. See https://reviews.llvm.org/D108961 If you can drop the gas code, do it, and it will be better than RISC-V :)

Thanks. After reading those comments in that link, I think I should look at BranchRelaxation.cpp and pay attention to the follows,
1, the NOPs emited by alignment directives when relax enabled.
2, the bytes emited by hand-written assembly, include .align, .rept and other directives.
3, the timing of BranchRelaxation pass. E.g. it should run after the expandPseudo pass.
If I understand those comments correctly, I should try BranchRelaxation rather than what gas does because llvm has integrated assembler while gcc not.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

3 participants