- Notifications
You must be signed in to change notification settings - Fork 15.2k
[LoongArch] MC relaxation for out-of-range conditional branch #72095
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
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`. | @llvm/pr-subscribers-lld-elf @llvm/pr-subscribers-mc Author: Jinyang He (MQ-mengqing) ChangesFor 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, change to If the symbol is unresolved at assembly time, not do this relaxation. To disable branch relaxation, use Full diff: https://github.com/llvm/llvm-project/pull/72095.diff 8 Files Affected:
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 |
| @SixWeining |
| Add @xen0n @xry111 @wangleiat @MaskRay. |
| Assembler relaxing out-of-range conditional branches is probably not a good idea. See https://reviews.llvm.org/D108961 |
Thanks. After reading those comments in that link, I think I should look at BranchRelaxation.cpp and pay attention to the follows, |
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,
change to
If the symbol is unresolved at assembly time, not do this relaxation. To disable branch relaxation, use
-loongarch-asm-relax-branches=0.