From dce2f1fba1cb4b9a2da8e0cced2e60cdbc319718 Mon Sep 17 00:00:00 2001 From: Evan Hellman Date: Mon, 24 Aug 2026 09:11:56 -0500 Subject: [PATCH] Fix Thumb2 conditional branch encoding with absolute targets Thumb2 conditional branches to an absolute address were encoded from the raw target address instead of a PC-relative offset, producing branches to the wrong location. ARMMCCodeEmitter::getBranchTargetOpValue() dispatched Thumb2 to the generic helper, which returns immediate operands unchanged. The T3 encoder then extracted S/J1/J2/imm6/imm11 from what it assumed was a PC-relative offset but was actually the absolute address. The unconditional t2B path already subtracts the instruction address; the conditional path did not. Assembling "bne 0x15f0" at 0x1248 emitted 41 f0 f8 82, which decodes to 0x283c. It now emits 40 f0 d2 81, which decodes to 0x15f0. Every wide conditional branch was affected, including backward branches, which failed to set the sign bit. Symbolic targets are unaffected: they still take the fixup_t2_condbranch path, which was already correct. Fixes #451 Co-Authored-By: Claude Opus 5 --- .../lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp index aa0206af..7e0bfef3 100644 --- a/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp +++ b/llvm/lib/Target/ARM/MCTargetDesc/ARMMCCodeEmitter.cpp @@ -708,9 +708,15 @@ getBranchTargetOpValue(const MCInst &MI, unsigned OpIdx, const MCSubtargetInfo &STI) const { // FIXME: This really, really shouldn't use TargetMachine. We don't want // coupling between MC and TM anywhere we can help it. - if (isThumb2(STI)) - return - ::getBranchTargetOpValue(MI, OpIdx, ARM::fixup_t2_condbranch, Fixups, STI); + if (isThumb2(STI)) { + const MCOperand MO = MI.getOperand(OpIdx); + if (MO.isExpr()) + return ::getBranchTargetOpValue(MI, OpIdx, ARM::fixup_t2_condbranch, + Fixups, STI); + // The branch target is an absolute address; convert it to a PC-relative + // byte offset. The encoder below drops the (always zero) low bit. + return (uint32_t)(int32_t)(MO.getImm() - MI.getAddress() - 4); + } return getARMBranchTargetOpValue(MI, OpIdx, Fixups, STI); }