diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp index 424c3fe5f829f..eb1b90f7dc3f0 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -136,22 +136,42 @@ bool RISCVAsmBackend::fixupNeedsRelaxationAdvanced(const MCFixup &Fixup, // For conditional branch instructions the immediate must be // in the range [-4096, 4094]. return Offset > 4094 || Offset < -4096; + case RISCV::fixup_riscv_jal: + // For jump instructions the immediate must be in the range + // [-1048576, 1048574] + return Offset > 1048574 || Offset < -1048576; } } // Given a compressed control flow instruction this function returns -// the expanded instruction. -static unsigned getRelaxedOpcode(unsigned Op) { - switch (Op) { - default: - return Op; +// the expanded instruction, or the original instruction code if no +// expansion is available. +static unsigned getRelaxedOpcode(const MCInst &Inst, + const MCSubtargetInfo &STI) { + switch (Inst.getOpcode()) { case RISCV::C_BEQZ: return RISCV::BEQ; case RISCV::C_BNEZ: return RISCV::BNE; case RISCV::C_J: case RISCV::C_JAL: // fall through. + // This only relaxes one "step" - i.e. from C.J to JAL, not from C.J to + // QC.E.J, because we can always relax again if needed. return RISCV::JAL; + case RISCV::JAL: { + // We can only relax JAL if we have Xqcilb + if (!STI.hasFeature(RISCV::FeatureVendorXqcilb)) + break; + + // And only if it is using X0 or X1 for rd. + MCRegister Reg = Inst.getOperand(0).getReg(); + if (Reg == RISCV::X0) + return RISCV::QC_E_J; + if (Reg == RISCV::X1) + return RISCV::QC_E_JAL; + + break; + } case RISCV::BEQ: return RISCV::PseudoLongBEQ; case RISCV::BNE: @@ -189,6 +209,9 @@ static unsigned getRelaxedOpcode(unsigned Op) { case RISCV::QC_E_BGEUI: return RISCV::PseudoLongQC_E_BGEUI; } + + // Returning the original opcode means we cannot relax the instruction. + return Inst.getOpcode(); } void RISCVAsmBackend::relaxInstruction(MCInst &Inst, @@ -206,6 +229,20 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst, case RISCV::C_JAL: { [[maybe_unused]] bool Success = RISCVRVC::uncompress(Res, Inst, STI); assert(Success && "Can't uncompress instruction"); + assert(Res.getOpcode() == getRelaxedOpcode(Inst, STI) && + "Branch Relaxation Error"); + break; + } + case RISCV::JAL: { + // This has to be written manually because the QC.E.J -> JAL is + // compression-only, so that it is not used when printing disassembly. + assert(STI.hasFeature(RISCV::FeatureVendorXqcilb) && + "JAL is only relaxable with Xqcilb"); + assert((Inst.getOperand(0).getReg() == RISCV::X0 || + Inst.getOperand(0).getReg() == RISCV::X1) && + "JAL only relaxable with rd=x0 or rd=x1"); + Res.setOpcode(getRelaxedOpcode(Inst, STI)); + Res.addOperand(Inst.getOperand(1)); break; } case RISCV::BEQ: @@ -226,7 +263,7 @@ void RISCVAsmBackend::relaxInstruction(MCInst &Inst, case RISCV::QC_E_BGEI: case RISCV::QC_E_BLTUI: case RISCV::QC_E_BGEUI: - Res.setOpcode(getRelaxedOpcode(Inst.getOpcode())); + Res.setOpcode(getRelaxedOpcode(Inst, STI)); Res.addOperand(Inst.getOperand(0)); Res.addOperand(Inst.getOperand(1)); Res.addOperand(Inst.getOperand(2)); @@ -381,7 +418,7 @@ bool RISCVAsmBackend::mayNeedRelaxation(const MCInst &Inst, if (STI.hasFeature(RISCV::FeatureExactAssembly)) return false; - return getRelaxedOpcode(Inst.getOpcode()) != Inst.getOpcode(); + return getRelaxedOpcode(Inst, STI) != Inst.getOpcode(); } bool RISCVAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count, diff --git a/llvm/test/MC/RISCV/rv32-relaxation-xqci.s b/llvm/test/MC/RISCV/rv32-relaxation-xqci.s new file mode 100644 index 0000000000000..03c9914bb37d2 --- /dev/null +++ b/llvm/test/MC/RISCV/rv32-relaxation-xqci.s @@ -0,0 +1,170 @@ +# RUN: split-file %s %t +# RUN: llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcilb %t/pass.s -o - \ +# RUN: | llvm-objdump -dr -M no-aliases - --mattr=+experimental-xqcilb | FileCheck %s +# RUN: not llvm-mc -filetype=obj -triple riscv32 -mattr=+experimental-xqcilb %t/fail.s \ +# RUN: 2>&1 | FileCheck %t/fail.s --check-prefix=ERROR + +## This testcase shows how `c.j`, `c.jal` and `jal` can be relaxed to `qc.e.j` and `qc.e.jal` +## with Xqcilb, when the branches are out of range, but also that these can be compressed +## when referencing close labels. + +## The only problem we have here is when `jal` references an out-of-range label, and `rd` is +## not `x0` or `x1` - for which we have no equivalent sequence, so we just fail to relax, and +## emit a fixup-value-out-of-range error. + +#--- pass.s + +EXT_JUMP_NEGATIVE: + c.nop +.space 0x100000 + +FAR_JUMP_NEGATIVE: + c.nop +.space 0x1000 + +NEAR_NEGATIVE: + c.nop + +start: + c.j NEAR +# CHECK: c.j {{0x[0-9a-f]+}} + c.j NEAR_NEGATIVE +# CHECK: c.j {{0x[0-9a-f]+}} + c.j FAR_JUMP +# CHECK: jal zero, {{0x[0-9a-f]+}} + c.j FAR_JUMP_NEGATIVE +# CHECK: jal zero, {{0x[0-9a-f]+}} + c.j EXT_JUMP +# CHECK: qc.e.j {{0x[0-9a-f]+}} + c.j EXT_JUMP_NEGATIVE +# CHECK: qc.e.j {{0x[0-9a-f]+}} + c.j undef +# CHECK: qc.e.j {{0x[0-9a-f]+}} +# CHECK: R_RISCV_CUSTOM195 undef + + c.jal NEAR +# CHECK: c.jal {{0x[0-9a-f]+}} + c.jal NEAR_NEGATIVE +# CHECK: c.jal {{0x[0-9a-f]+}} + c.jal FAR_JUMP +# CHECK: jal ra, {{0x[0-9a-f]+}} + c.jal FAR_JUMP_NEGATIVE +# CHECK: jal ra, {{0x[0-9a-f]+}} + c.jal EXT_JUMP +# CHECK: qc.e.jal {{0x[0-9a-f]+}} + c.jal EXT_JUMP_NEGATIVE +# CHECK: qc.e.jal {{0x[0-9a-f]+}} + c.jal undef +# CHECK: qc.e.jal {{0x[0-9a-f]+}} +# CHECK: R_RISCV_CUSTOM195 undef + + jal zero, NEAR +# CHECK: c.j {{0x[0-9a-f]+}} + jal zero, NEAR_NEGATIVE +# CHECK: c.j {{0x[0-9a-f]+}} + jal zero, FAR_JUMP +# CHECK: jal zero, {{0x[0-9a-f]+}} + jal zero, FAR_JUMP_NEGATIVE +# CHECK: jal zero, {{0x[0-9a-f]+}} + jal zero, EXT_JUMP +# CHECK: qc.e.j {{0x[0-9a-f]+}} + jal zero, EXT_JUMP_NEGATIVE +# CHECK: qc.e.j {{0x[0-9a-f]+}} + jal zero, undef +# CHECK: qc.e.j {{0x[0-9a-f]+}} +# CHECK: R_RISCV_CUSTOM195 undef + + jal ra, NEAR +# CHECK: c.jal {{0x[0-9a-f]+}} + jal ra, NEAR_NEGATIVE +# CHECK: c.jal {{0x[0-9a-f]+}} + jal ra, FAR_JUMP +# CHECK: jal ra, {{0x[0-9a-f]+}} + jal ra, FAR_JUMP_NEGATIVE +# CHECK: jal ra, {{0x[0-9a-f]+}} + jal ra, EXT_JUMP +# CHECK: qc.e.jal {{0x[0-9a-f]+}} + jal ra, EXT_JUMP_NEGATIVE +# CHECK: qc.e.jal {{0x[0-9a-f]+}} + jal ra, undef +# CHECK: qc.e.jal {{0x[0-9a-f]+}} +# CHECK: R_RISCV_CUSTOM195 undef + + qc.e.j NEAR +# CHECK: c.j {{0x[0-9a-f]+}} + qc.e.j NEAR_NEGATIVE +# CHECK: c.j {{0x[0-9a-f]+}} + qc.e.j FAR_JUMP +# CHECK: jal zero, {{0x[0-9a-f]+}} + qc.e.j FAR_JUMP_NEGATIVE +# CHECK: jal zero, {{0x[0-9a-f]+}} + qc.e.j EXT_JUMP +# CHECK: qc.e.j {{0x[0-9a-f]+}} + qc.e.j EXT_JUMP_NEGATIVE +# CHECK: qc.e.j {{0x[0-9a-f]+}} + qc.e.j undef +# CHECK: qc.e.j {{0x[0-9a-f]+}} +# CHECK: R_RISCV_CUSTOM195 undef + + qc.e.jal NEAR +# CHECK: c.jal {{0x[0-9a-f]+}} + qc.e.jal NEAR_NEGATIVE +# CHECK: c.jal {{0x[0-9a-f]+}} + qc.e.jal FAR_JUMP +# CHECK: jal ra, {{0x[0-9a-f]+}} + qc.e.jal FAR_JUMP_NEGATIVE +# CHECK: jal ra, {{0x[0-9a-f]+}} + qc.e.jal EXT_JUMP +# CHECK: qc.e.jal {{0x[0-9a-f]+}} + qc.e.jal EXT_JUMP_NEGATIVE +# CHECK: qc.e.jal {{0x[0-9a-f]+}} + qc.e.jal undef +# CHECK: qc.e.jal {{0x[0-9a-f]+}} +# CHECK: R_RISCV_CUSTOM195 undef + + + + jal t1, NEAR +# CHECK: jal t1, {{0x[0-9a-f]+}} + jal t1, NEAR_NEGATIVE +# CHECK: jal t1, {{0x[0-9a-f]+}} + jal t1, FAR_JUMP +# CHECK: jal t1, {{0x[0-9a-f]+}} + jal t1, FAR_JUMP_NEGATIVE +# CHECK: jal t1, {{0x[0-9a-f]+}} + +## The two cases with EXT_JUMP and EXT_JUMP_NEGATIVE are +## in fail.s, below. + + jal t1, undef +# CHECK: jal t1, {{0x[0-9a-f]+}} +# CHECK: R_RISCV_JAL undef + + +NEAR: + c.nop +.space 0x1000 +FAR_JUMP: + c.nop +.space 0x100000 +EXT_JUMP: + c.nop + + +#--- fail.s + + +EXT_JUMP_NEGATIVE: + c.nop +.space 0x100000 +.space 0x1000 + + jal t1, EXT_JUMP +# ERROR: [[@LINE-1]]:3: error: fixup value out of range + jal t1, EXT_JUMP_NEGATIVE +# ERROR: [[@LINE-1]]:3: error: fixup value out of range + +.space 0x1000 +.space 0x100000 +EXT_JUMP: + c.nop