diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h --- a/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/aarch64.h @@ -107,6 +107,28 @@ /// out-of-range error will be returned. Branch26PCRel, + /// A 14-bit PC-relative test and branch. + /// + /// Represents a PC-relative test and branch to a target within +/-32Kb. The + /// target must be 32-bit aligned. + /// + /// Fixup expression: + /// Fixup <- (Target - Fixup + Addend) >> 2 : int14 + /// + /// Notes: + /// The '14' in the name refers to the number operand bits and follows the + /// naming convention used by the corresponding ELF relocation. + /// Since the low two bits must be zero (because of the 32-bit alignment of + /// the target) the operand is effectively a signed 16-bit number. + /// + /// + /// Errors: + /// - The result of the unshifted part of the fixup expression must be + /// 32-bit aligned otherwise an alignment error will be returned. + /// - The result of the fixup expression must fit into an int14 otherwise an + /// out-of-range error will be returned. + TestAndBranch14PCRel, + /// A 19-bit PC-relative conditional branch. /// /// Represents a PC-relative conditional branch to a target within +/-1Mb. The @@ -321,6 +343,11 @@ return (Instr & LoadStoreImm12Mask) == 0x39000000; } +inline bool isTestAndBranchImm14(uint32_t Instr) { + constexpr uint32_t TestAndBranchImm14Mask = 0x7e000000; + return (Instr & TestAndBranchImm14Mask) == 0x36000000; +} + inline bool isCondBranchImm19(uint32_t Instr) { constexpr uint32_t CondBranchImm19Mask = 0xfe000000; return (Instr & CondBranchImm19Mask) == 0x54000000; @@ -463,6 +490,23 @@ *(ulittle32_t *)FixupPtr = FixedInstr; break; } + case TestAndBranch14PCRel: { + assert((FixupAddress.getValue() & 0x3) == 0 && + "Test and branch is not 32-bit aligned"); + uint32_t RawInstr = *(ulittle32_t *)FixupPtr; + assert(isTestAndBranchImm14(RawInstr) && + "RawInstr is not a test and branch"); + int64_t Delta = E.getTarget().getAddress() + E.getAddend() - FixupAddress; + if (Delta & 0x3) + return make_error( + "Test and branch literal target is not 32-bit aligned"); + if (!isInt<16>(Delta)) + return makeTargetOutOfRangeError(G, B, E); + uint32_t EncodedImm = ((static_cast(Delta) >> 2) & 0x3fff) << 5; + uint32_t FixedInstr = RawInstr | EncodedImm; + *(ulittle32_t *)FixupPtr = FixedInstr; + break; + } case CondBranch19PCRel: { assert((FixupAddress.getValue() & 0x3) == 0 && "Conditional branch is not 32-bit aligned"); diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp --- a/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_aarch64.cpp @@ -58,6 +58,7 @@ ELFMovwAbsG1, ELFMovwAbsG2, ELFMovwAbsG3, + ELFTstBr14, ELFCondBr19, ELFAbs32, ELFAbs64, @@ -100,6 +101,8 @@ return ELFMovwAbsG2; case ELF::R_AARCH64_MOVW_UABS_G3: return ELFMovwAbsG3; + case ELF::R_AARCH64_TSTBR14: + return ELFTstBr14; case ELF::R_AARCH64_CONDBR19: return ELFCondBr19; case ELF::R_AARCH64_ABS32: @@ -290,6 +293,15 @@ Kind = aarch64::MoveWide16; break; } + case ELFTstBr14: { + uint32_t Instr = *(const ulittle32_t *)FixupContent; + if (!aarch64::isTestAndBranchImm14(Instr)) + return make_error("R_AARCH64_TSTBR14 target is not a " + "test and branch instruction"); + + Kind = aarch64::TestAndBranch14PCRel; + break; + } case ELFCondBr19: { uint32_t Instr = *(const ulittle32_t *)FixupContent; if (!aarch64::isCondBranchImm19(Instr) && diff --git a/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp b/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp --- a/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/aarch64.cpp @@ -47,6 +47,8 @@ return "MoveWide16"; case LDRLiteral19: return "LDRLiteral19"; + case TestAndBranch14PCRel: + return "TestAndBranch14PCRel"; case CondBranch19PCRel: return "CondBranch19PCRel"; case Page21: diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s --- a/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s +++ b/llvm/test/ExecutionEngine/JITLink/AArch64/ELF_relocations.s @@ -241,6 +241,30 @@ ldr x0, [x0, :got_lo12:external_data] .size test_ld64_gotlo12_external, .-test_ld64_gotlo12_external +# Check R_AARCH64_TSTBR14 for tbz +# +# jitlink-check: decode_operand(test_tstbr14_tbz, 2) = \ +# jitlink-check: (test_tstbr14_tbz_target - test_tstbr14_tbz)[16:2] + .globl test_tstbr14_tbz, test_tstbr14_tbz_target + .p2align 2 +test_tstbr14_tbz: + tbz x0, 0, test_tstbr14_tbz_target + .skip (1 << 14) +test_tstbr14_tbz_target: + .size test_tstbr14_tbz, .-test_tstbr14_tbz + +# Check R_AARCH64_TSTBR14 for tbnz +# +# jitlink-check: decode_operand(test_tstbr14_tbnz, 2) = \ +# jitlink-check: (test_tstbr14_tbnz_target - test_tstbr14_tbnz)[16:2] + .globl test_tstbr14_tbnz, test_tstbr14_tbnz_target + .p2align 2 +test_tstbr14_tbnz: + tbnz x0, 0, test_tstbr14_tbnz_target + .skip (1 << 14) +test_tstbr14_tbnz_target: + .size test_tstbr14_tbnz, .-test_tstbr14_tbnz + # Check R_AARCH64_CONDBR19 for compare and branch instructions # # jitlink-check: decode_operand(test_condbr19_cbz, 1) = \