diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 56c2a0de5bcd10..22cc65d7184b88 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -143,6 +143,15 @@ bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, const MCRelaxableFragment *DF, const MCAsmLayout &Layout, const bool WasForced) const { + int64_t Offset = int64_t(Value); + unsigned Kind = Fixup.getTargetKind(); + + // We only do conditional branch relaxation when the symbol is resolved. + // For conditional branch, the immediate must be in the range + // [-4096, 4094]. + if (Kind == RISCV::fixup_riscv_branch) + return Resolved && !isInt<13>(Offset); + // Return true if the symbol is actually unresolved. // Resolved could be always false when shouldForceRelocation return true. // We use !WasForced to indicate that the symbol is unresolved and not forced @@ -150,8 +159,7 @@ bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, if (!Resolved && !WasForced) return true; - int64_t Offset = int64_t(Value); - switch (Fixup.getTargetKind()) { + switch (Kind) { default: return false; case RISCV::fixup_riscv_rvc_branch: @@ -174,12 +182,24 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst, case RISCV::C_BEQZ: case RISCV::C_BNEZ: case RISCV::C_J: - case RISCV::C_JAL: + case RISCV::C_JAL: { bool Success = RISCVRVC::uncompress(Res, Inst, STI); assert(Success && "Can't uncompress instruction"); (void)Success; break; } + case RISCV::BEQ: + case RISCV::BNE: + case RISCV::BLT: + case RISCV::BGE: + case RISCV::BLTU: + case RISCV::BGEU: + Res.setOpcode(getRelaxedOpcode(Inst.getOpcode())); + Res.addOperand(Inst.getOperand(0)); + Res.addOperand(Inst.getOperand(1)); + Res.addOperand(Inst.getOperand(2)); + break; + } Inst = std::move(Res); } @@ -321,6 +341,18 @@ unsigned RISCVAsmBackend::getRelaxedOpcode(unsigned Op) const { case RISCV::C_J: case RISCV::C_JAL: // fall through. return RISCV::JAL; + case RISCV::BEQ: + return RISCV::PseudoLongBEQ; + case RISCV::BNE: + return RISCV::PseudoLongBNE; + case RISCV::BLT: + return RISCV::PseudoLongBLT; + case RISCV::BGE: + return RISCV::PseudoLongBGE; + case RISCV::BLTU: + return RISCV::PseudoLongBLTU; + case RISCV::BGEU: + return RISCV::PseudoLongBGEU; } } diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index 9f5561bfd2aeec..c01f3aae447d93 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -61,6 +61,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter { SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + void expandLongCondBr(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + /// TableGen'erated function for getting the binary encoding for an /// instruction. uint64_t getBinaryCodeForInstr(const MCInst &MI, @@ -179,6 +183,81 @@ void RISCVMCCodeEmitter::expandAddTPRel(const MCInst &MI, raw_ostream &OS, support::endian::write(OS, Binary, support::little); } +static unsigned getInvertedBranchOp(unsigned BrOp) { + switch (BrOp) { + default: + llvm_unreachable("Unexpected branch opcode!"); + case RISCV::PseudoLongBEQ: + return RISCV::BNE; + case RISCV::PseudoLongBNE: + return RISCV::BEQ; + case RISCV::PseudoLongBLT: + return RISCV::BGE; + case RISCV::PseudoLongBGE: + return RISCV::BLT; + case RISCV::PseudoLongBLTU: + return RISCV::BGEU; + case RISCV::PseudoLongBGEU: + return RISCV::BLTU; + } +} + +// Expand PseudoLongBxx to an inverted conditional branch and an unconditional +// jump. +void RISCVMCCodeEmitter::expandLongCondBr(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + MCRegister SrcReg1 = MI.getOperand(0).getReg(); + MCRegister SrcReg2 = MI.getOperand(1).getReg(); + MCOperand SrcSymbol = MI.getOperand(2); + unsigned Opcode = MI.getOpcode(); + bool IsEqTest = + Opcode == RISCV::PseudoLongBNE || Opcode == RISCV::PseudoLongBEQ; + + bool UseCompressedBr = false; + if (IsEqTest && (STI.getFeatureBits()[RISCV::FeatureStdExtC] || + STI.getFeatureBits()[RISCV::FeatureExtZca])) { + if (RISCV::X8 <= SrcReg1.id() && SrcReg1.id() <= RISCV::X15 && + SrcReg2.id() == RISCV::X0) { + UseCompressedBr = true; + } else if (RISCV::X8 <= SrcReg2.id() && SrcReg2.id() <= RISCV::X15 && + SrcReg1.id() == RISCV::X0) { + std::swap(SrcReg1, SrcReg2); + UseCompressedBr = true; + } + } + + uint32_t Offset; + if (UseCompressedBr) { + unsigned InvOpc = + Opcode == RISCV::PseudoLongBNE ? RISCV::C_BEQZ : RISCV::C_BNEZ; + MCInst TmpInst = MCInstBuilder(InvOpc).addReg(SrcReg1).addImm(6); + uint16_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + Offset = 2; + } else { + unsigned InvOpc = getInvertedBranchOp(Opcode); + MCInst TmpInst = + MCInstBuilder(InvOpc).addReg(SrcReg1).addReg(SrcReg2).addImm(8); + uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + Offset = 4; + } + + // Emit an unconditional jump to the destination. + MCInst TmpInst = + MCInstBuilder(RISCV::JAL).addReg(RISCV::X0).addOperand(SrcSymbol); + uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::write(OS, Binary, support::little); + + Fixups.clear(); + if (SrcSymbol.isExpr()) { + Fixups.push_back(MCFixup::create(Offset, SrcSymbol.getExpr(), + MCFixupKind(RISCV::fixup_riscv_jal), + MI.getLoc())); + } +} + void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { @@ -203,6 +282,15 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, expandAddTPRel(MI, OS, Fixups, STI); MCNumEmitted += 1; return; + case RISCV::PseudoLongBEQ: + case RISCV::PseudoLongBNE: + case RISCV::PseudoLongBLT: + case RISCV::PseudoLongBGE: + case RISCV::PseudoLongBLTU: + case RISCV::PseudoLongBGEU: + expandLongCondBr(MI, OS, Fixups, STI); + MCNumEmitted += 2; + return; } switch (Size) { diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index fb313b935ff9c7..81c25373352f96 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1469,6 +1469,26 @@ let Predicates = [HasStdExtC, OptForMinSize] in { def : BrccCompessOpt; } +class LongBccPseudo : Pseudo<(outs), + (ins GPR:$rs1, GPR:$rs2, simm21_lsb0_jal:$imm20), + []> { + let Size = 8; + let isBarrier = 1; + let isBranch = 1; + let hasSideEffects = 0; + let mayStore = 0; + let mayLoad = 0; + let isAsmParserOnly = 1; + let hasNoSchedulingInfo = 1; +} + +def PseudoLongBEQ : LongBccPseudo; +def PseudoLongBNE : LongBccPseudo; +def PseudoLongBLT : LongBccPseudo; +def PseudoLongBGE : LongBccPseudo; +def PseudoLongBLTU : LongBccPseudo; +def PseudoLongBGEU : LongBccPseudo; + let isBarrier = 1, isBranch = 1, isTerminator = 1 in def PseudoBR : Pseudo<(outs), (ins simm21_lsb0_jal:$imm20), [(br bb:$imm20)]>, PseudoInstExpansion<(JAL X0, simm21_lsb0_jal:$imm20)>; diff --git a/llvm/test/MC/RISCV/fixups-diagnostics.s b/llvm/test/MC/RISCV/fixups-diagnostics.s index d346605221c75c..3c99673b81fc26 100644 --- a/llvm/test/MC/RISCV/fixups-diagnostics.s +++ b/llvm/test/MC/RISCV/fixups-diagnostics.s @@ -3,7 +3,6 @@ jal a0, far_distant # CHECK: :[[@LINE]]:3: error: fixup value out of range jal a0, unaligned # CHECK: :[[@LINE]]:3: error: fixup value must be 2-byte aligned - beq a0, a1, distant # CHECK: :[[@LINE]]:3: error: fixup value out of range blt t0, t1, unaligned # CHECK: :[[@LINE]]:3: error: fixup value must be 2-byte aligned .byte 0 @@ -12,7 +11,5 @@ unaligned: .byte 0 .byte 0 - .space 1<<12 -distant: .space 1<<20 far_distant: diff --git a/llvm/test/MC/RISCV/long-conditional-jump.s b/llvm/test/MC/RISCV/long-conditional-jump.s new file mode 100644 index 00000000000000..27d3d105838d33 --- /dev/null +++ b/llvm/test/MC/RISCV/long-conditional-jump.s @@ -0,0 +1,93 @@ +# RUN: llvm-mc -filetype=obj -triple=riscv64 %s \ +# RUN: | llvm-objdump -d -M no-aliases - \ +# RUN: | FileCheck --check-prefix=CHECK-INST %s +# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c %s \ +# RUN: | llvm-objdump -d -M no-aliases - \ +# RUN: | FileCheck --check-prefix=CHECK-INST-C %s + + .text + .p2align 3 + .type test,@function +test: +# CHECK-INST: beq a0, a1, 0x8 +# CHECK-INST-NEXT: jal zero, 0x1458 +# CHECK-INST-C: beq a0, a1, 0x8 +# CHECK-INST-C-NEXT: jal zero, 0x1458 + bne a0, a1, .L1 +.fill 1300, 4, 0 +.L1: + ret +# CHECK-INST: bne a0, a1, 0x1464 +# CHECK-INST-NEXT: jal zero, 0x28b4 +# CHECK-INST-C: bne a0, a1, 0x1462 +# CHECK-INST-C-NEXT: jal zero, 0x28b2 + beq a0, a1, .L2 +.fill 1300, 4, 0 +.L2: + ret +# CHECK-INST: bge a0, a1, 0x28c0 +# CHECK-INST-NEXT: jal zero, 0x3d10 +# CHECK-INST-C: bge a0, a1, 0x28bc +# CHECK-INST-C-NEXT: jal zero, 0x3d0c + blt a0, a1, .L3 +.fill 1300, 4, 0 +.L3: + ret +# CHECK-INST: blt a0, a1, 0x3d1c +# CHECK-INST-NEXT: jal zero, 0x516c +# CHECK-INST-C: blt a0, a1, 0x3d16 +# CHECK-INST-C-NEXT: jal zero, 0x5166 + bge a0, a1, .L4 +.fill 1300, 4, 0 +.L4: + ret +# CHECK-INST: bgeu a0, a1, 0x5178 +# CHECK-INST-NEXT: jal zero, 0x65c8 +# CHECK-INST-C: bgeu a0, a1, 0x5170 +# CHECK-INST-C-NEXT: jal zero, 0x65c0 + bltu a0, a1, .L5 +.fill 1300, 4, 0 +.L5: + ret +# CHECK-INST: bltu a0, a1, 0x65d4 +# CHECK-INST-NEXT: jal zero, 0x7a24 +# CHECK-INST-C: bltu a0, a1, 0x65ca +# CHECK-INST-C-NEXT: jal zero, 0x7a1a + bgeu a0, a1, .L6 +.fill 1300, 4, 0 +.L6: + ret +# CHECK-INST: bne a0, zero, 0x7a30 +# CHECK-INST-NEXT: jal zero, 0x8e80 +# CHECK-INST-C: c.bnez a0, 0x7a22 +# CHECK-INST-C-NEXT: jal zero, 0x8e72 + beqz a0, .L7 +.fill 1300, 4, 0 +.L7: + ret +# CHECK-INST: bne zero, a0, 0x8e8c +# CHECK-INST-NEXT: jal zero, 0xa2dc +# CHECK-INST-C: c.bnez a0, 0x8e7a +# CHECK-INST-C-NEXT: jal zero, 0xa2ca + beq x0, a0, .L8 +.fill 1300, 4, 0 +.L8: + ret +# CHECK-INST: beq a0, zero, 0xa2e8 +# CHECK-INST-NEXT: jal zero, 0xb738 +# CHECK-INST-C: c.beqz a0, 0xa2d2 +# CHECK-INST-C-NEXT: jal zero, 0xb722 + bnez a0, .L9 +.fill 1300, 4, 0 +.L9: + ret +# CHECK-INST: beq a6, zero, 0xb744 +# CHECK-INST-NEXT: jal zero, 0xcb94 +# CHECK-INST-C: beq a6, zero, 0xb72c +# CHECK-INST-C-NEXT: jal zero, 0xcb7c + bnez x16, .L10 +.fill 1300, 4, 0 +.L10: + ret +.Lfunc_end0: + .size test, .Lfunc_end0-test diff --git a/llvm/test/MC/RISCV/rv64-relax-all.s b/llvm/test/MC/RISCV/rv64-relax-all.s index 84ef373b741b49..70a3f77540c997 100644 --- a/llvm/test/MC/RISCV/rv64-relax-all.s +++ b/llvm/test/MC/RISCV/rv64-relax-all.s @@ -7,7 +7,8 @@ NEAR: # INSTR: c.beqz a0, 0x0 -# RELAX-INSTR: beq a0, zero, 0x0 +# RELAX-INSTR: c.bnez a0, 0x6 +# RELAX-INSTR-NEXT:jal zero, 0x0 c.beqz a0, NEAR # INSTR: c.j 0x0