diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -143,6 +143,15 @@ 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 && (Offset > 4094 || Offset < -4096); + // 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 @@ 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: @@ -198,6 +206,17 @@ Res.addOperand(MCOperand::createReg(RISCV::X1)); Res.addOperand(Inst.getOperand(0)); 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); } @@ -344,6 +363,18 @@ 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 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -60,6 +60,10 @@ 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, @@ -187,6 +191,79 @@ support::endian::write(OS, Binary, support::little); } +static unsigned getInvertedBranchOp(unsigned BrOp, bool UseCompressedBr) { + switch (BrOp) { + default: + llvm_unreachable("Unexpected branch opcode!"); + case RISCV::PseudoLongBEQ: + if (UseCompressedBr) + return RISCV::C_BNEZ; + return RISCV::BNE; + case RISCV::PseudoLongBNE: + if (UseCompressedBr) + return RISCV::C_BEQZ; + 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 { + MCOperand SrcReg1 = MI.getOperand(0); + MCOperand SrcReg2 = MI.getOperand(1); + MCOperand SrcSymbol = MI.getOperand(2); + unsigned Opcode = MI.getOpcode(); + bool IsEqTest = + (Opcode == RISCV::PseudoLongBNE) || (Opcode == RISCV::PseudoLongBEQ); + + if (IsEqTest && SrcReg1.getReg() == RISCV::X0) + std::swap(SrcReg1, SrcReg2); + + // Emit an inverted conditional branch to skip the following jump. + bool UseCompressedBr = + STI.getFeatureBits()[RISCV::FeatureStdExtC] && + (RISCV::X8 <= SrcReg1.getReg() && SrcReg1.getReg() <= RISCV::X15) && + (SrcReg2.getReg() == RISCV::X0) && IsEqTest; + auto TmpInst = + MCInstBuilder(getInvertedBranchOp(MI.getOpcode(), UseCompressedBr)) + .addOperand(SrcReg1); + if (UseCompressedBr) + TmpInst.addImm(6); + else + TmpInst.addOperand(SrcReg2).addImm(8); + uint32_t Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + uint32_t Offset; + if (UseCompressedBr) { + support::endian::write(OS, Binary, support::little); + Offset = 2; + } else { + support::endian::write(OS, Binary, support::little); + Offset = 4; + } + + // Emit an unconditional jump to the destination. + TmpInst = MCInstBuilder(RISCV::JAL).addReg(RISCV::X0).addOperand(SrcSymbol); + 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 { @@ -196,14 +273,13 @@ const MCInstrDesc &Desc = MCII.get(MI.getOpcode()); // Get byte count of instruction. unsigned Size = Desc.getSize(); + unsigned Opcode = MI.getOpcode(); // RISCVInstrInfo::getInstSizeInBytes hard-codes the number of expanded // instructions for each pseudo, and must be updated when adding new pseudos // or changing existing ones. - if (MI.getOpcode() == RISCV::PseudoCALLReg || - MI.getOpcode() == RISCV::PseudoCALL || - MI.getOpcode() == RISCV::PseudoTAIL || - MI.getOpcode() == RISCV::PseudoJump) { + if (Opcode == RISCV::PseudoCALLReg || Opcode == RISCV::PseudoCALL || + Opcode == RISCV::PseudoTAIL || Opcode == RISCV::PseudoJump) { expandFunctionCall(MI, OS, Fixups, STI); MCNumEmitted += 2; return; @@ -215,6 +291,14 @@ return; } + if (Opcode == RISCV::PseudoLongBEQ || Opcode == RISCV::PseudoLongBNE || + Opcode == RISCV::PseudoLongBLT || Opcode == RISCV::PseudoLongBGE || + Opcode == RISCV::PseudoLongBLTU || Opcode == RISCV::PseudoLongBGEU) { + expandLongCondBr(MI, OS, Fixups, STI); + MCNumEmitted += 2; + return; + } + switch (Size) { default: llvm_unreachable("Unhandled encodeInstruction length!"); diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -788,6 +788,12 @@ case RISCV::PseudoLA: case RISCV::PseudoLA_TLS_IE: case RISCV::PseudoLA_TLS_GD: + case RISCV::PseudoLongBEQ: + case RISCV::PseudoLongBNE: + case RISCV::PseudoLongBLT: + case RISCV::PseudoLongBGE: + case RISCV::PseudoLongBLTU: + case RISCV::PseudoLongBGEU: return 8; case RISCV::PseudoAtomicLoadNand32: case RISCV::PseudoAtomicLoadNand64: diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -1013,6 +1013,26 @@ def : BccPat; def : BccPat; +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 --- 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 @@ .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 --- /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 a0, zero, 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 --- 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