From 9832c9f5a2a71c1df4b6ce2f109bf989973dadce Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Wed, 25 Dec 2024 16:27:31 +0800 Subject: [PATCH 1/5] [JITLink][LoongArch] Ignore R_LARCH_RELAX and check R_LARCH_ALIGN Linker relaxation is not implemented for jitlink now (maybe implement in the future). But if relaxation is enabled by clang, R_LARCH_RELAX and R_LARCH_ALIGN relocations will be emitted. So we just ignore linker relaxation and check the alignment now. If not, interpreting C++ code using clang-repl when relaxation is enabled will occur error: `JIT session error: Unsupported loongarch relocation:102: R_LARCH_ALIGN`. Similar to: https://github.com/llvm/llvm-project/commit/f5b5398ebf7da1ab54f6793331bca8fbc3340f14. --- .../ExecutionEngine/JITLink/ELF_loongarch.cpp | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp index a12e9f33e80a6..36f3b76a5c631 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp @@ -99,6 +99,30 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { Block &BlockToFix) { using Base = ELFLinkGraphBuilder; + uint32_t Type = Rel.getType(false); + // We do not implement linker relaxation for jitlink now, except what is + // required for alignment (see below). + if (Type == ELF::R_LARCH_RELAX) + return Error::success(); + + int64_t Addend = Rel.r_addend; + if (Type == ELF::R_LARCH_ALIGN) { + uint64_t Alignment = PowerOf2Ceil(Addend); + // FIXME: Implement support for ensuring alignment together with linker + // relaxation. Addend is always 28 in the most common case when + // interpreting C++ code in clang-repl. + if (Alignment > 32) + return make_error( + formatv("Unsupported relocation R_LARCH_ALIGN with alignment {0} " + "larger than 32 (addend: {1})", + Alignment, Addend)); + return Error::success(); + } + + Expected Kind = getRelocationKind(Type); + if (!Kind) + return Kind.takeError(); + uint32_t SymbolIndex = Rel.getSymbol(false); auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec); if (!ObjSymbol) @@ -113,12 +137,6 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { Base::GraphSymbols.size()), inconvertibleErrorCode()); - uint32_t Type = Rel.getType(false); - Expected Kind = getRelocationKind(Type); - if (!Kind) - return Kind.takeError(); - - int64_t Addend = Rel.r_addend; auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset; Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress(); Edge GE(*Kind, Offset, *GraphSymbol, Addend); From 91aea9bdf601af969a5d57089223af919e0afdc1 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Mon, 6 Jan 2025 20:51:19 +0800 Subject: [PATCH 2/5] [JITLink][LoongArch] Support R_LARCH_ALIGN relaxation Linker relaxation is not implemented for jitlink now. But if relaxation is enabled by clang, R_LARCH_RELAX and R_LARCH_ALIGN relocations will be emitted. This commit adapts lld's algorithm to jitlink. Currently, only relaxing R_LARCH_ALIGN is implemented. Other relaxable relocs can be implemented in the future. Without this, interpreting C++ code using clang-repl or running ir using lli when relaxation is enabled will occur error: `JIT session error: Unsupported loongarch relocation:102: R_LARCH_ALIGN`. Similar to https://github.com/llvm/llvm-project/commit/310473c536dd4837934832d1b5454d212f15d5cc but only implement align. --- .../ExecutionEngine/JITLink/ELF_loongarch.h | 4 + .../llvm/ExecutionEngine/JITLink/loongarch.h | 10 + .../ExecutionEngine/JITLink/ELF_loongarch.cpp | 269 +++++++++++++++++- .../lib/ExecutionEngine/JITLink/loongarch.cpp | 1 + .../JITLink/LoongArch/ELF_relax_align.s | 95 +++++++ 5 files changed, 364 insertions(+), 15 deletions(-) create mode 100644 llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_relax_align.s diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h index 7e5d0f1f91852..a8655dc6f14e3 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h @@ -33,6 +33,10 @@ Expected> createLinkGraphFromELFObject_loongarch( void link_ELF_loongarch(std::unique_ptr G, std::unique_ptr Ctx); +/// Returns a pass that performs linker relaxation. Should be added to +/// PostAllocationPasses. +LinkGraphPassFunction createRelaxationPass_ELF_loongarch(); + } // end namespace jitlink } // end namespace llvm diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h index 1db4b82218109..d6025edf7d110 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h @@ -225,6 +225,13 @@ enum EdgeKind_loongarch : Edge::Kind { /// out-of-range error will be returned. /// Call36PCRel, + + /// Alignment requirement used by linker relaxation. + /// + /// Linker relaxation will use this to ensure all code sequences are properly + /// aligned and then remove these edges from the graph. + /// + AlignRelaxable, }; /// Returns a string name for the given loongarch edge. For debugging purposes @@ -362,6 +369,9 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { *(little32_t *)(FixupPtr + 4) = Jirl | Lo16; break; } + case AlignRelaxable: + // Ignore when the relaxation pass did not run + break; default: return make_error( "In graph " + G.getName() + ", section " + B.getSection().getName() + diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp index 36f3b76a5c631..3f0a7b645e83f 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp @@ -45,6 +45,238 @@ class ELFJITLinker_loongarch : public JITLinker { } }; +namespace { + +struct SymbolAnchor { + uint64_t Offset; + Symbol *Sym; + bool End; // true for the anchor of getOffset() + getSize() +}; + +struct BlockRelaxAux { + // This records symbol start and end offsets which will be adjusted according + // to the nearest RelocDeltas element. + SmallVector Anchors; + // All edges that either 1) are R_LARCH_ALIGN or 2) have a R_LARCH_RELAX edge + // at the same offset. + SmallVector RelaxEdges; + // For RelaxEdges[I], the actual offset is RelaxEdges[I]->getOffset() - (I ? + // RelocDeltas[I - 1] : 0). + SmallVector RelocDeltas; + // For RelaxEdges[I], the actual type is EdgeKinds[I]. + SmallVector EdgeKinds; + // List of rewritten instructions. Contains one raw encoded instruction per + // element in EdgeKinds that isn't Invalid or R_LARCH_ALIGN. + SmallVector Writes; +}; + +struct RelaxAux { + DenseMap Blocks; +}; + +} // namespace + +static bool shouldRelax(const Section &S) { + return (S.getMemProt() & orc::MemProt::Exec) != orc::MemProt::None; +} + +static bool isRelaxable(const Edge &E) { + switch (E.getKind()) { + default: + return false; + case AlignRelaxable: + return true; + } +} + +static RelaxAux initRelaxAux(LinkGraph &G) { + RelaxAux Aux; + for (auto &S : G.sections()) { + if (!shouldRelax(S)) + continue; + for (auto *B : S.blocks()) { + auto BlockEmplaceResult = Aux.Blocks.try_emplace(B); + assert(BlockEmplaceResult.second && "Block encountered twice"); + auto &BlockAux = BlockEmplaceResult.first->second; + + for (auto &E : B->edges()) + if (isRelaxable(E)) + BlockAux.RelaxEdges.push_back(&E); + + if (BlockAux.RelaxEdges.empty()) { + Aux.Blocks.erase(BlockEmplaceResult.first); + continue; + } + + const auto NumEdges = BlockAux.RelaxEdges.size(); + BlockAux.RelocDeltas.resize(NumEdges, 0); + BlockAux.EdgeKinds.resize_for_overwrite(NumEdges); + + // Store anchors (offset and offset+size) for symbols. + for (auto *Sym : S.symbols()) { + if (!Sym->isDefined() || &Sym->getBlock() != B) + continue; + + BlockAux.Anchors.push_back({Sym->getOffset(), Sym, false}); + BlockAux.Anchors.push_back( + {Sym->getOffset() + Sym->getSize(), Sym, true}); + } + } + } + + // Sort anchors by offset so that we can find the closest relocation + // efficiently. For a zero size symbol, ensure that its start anchor precedes + // its end anchor. For two symbols with anchors at the same offset, their + // order does not matter. + for (auto &BlockAuxIter : Aux.Blocks) { + llvm::sort(BlockAuxIter.second.Anchors, [](auto &A, auto &B) { + return std::make_pair(A.Offset, A.End) < std::make_pair(B.Offset, B.End); + }); + } + + return Aux; +} + +static void relaxAlign(orc::ExecutorAddr Loc, const Edge &E, uint32_t &Remove, + Edge::Kind &NewEdgeKind) { + const uint64_t Addend = + !E.getTarget().isDefined() ? Log2_64(E.getAddend()) + 1 : E.getAddend(); + const uint64_t AllBytes = (1ULL << (Addend & 0xff)) - 4; + const uint64_t Align = 1ULL << (Addend & 0xff); + const uint64_t MaxBytes = Addend >> 8; + const uint64_t Off = Loc.getValue() & (Align - 1); + const uint64_t CurBytes = Off == 0 ? 0 : Align - Off; + // All bytes beyond the alignment boundary should be removed. + // If emit bytes more than max bytes to emit, remove all. + if (MaxBytes != 0 && CurBytes > MaxBytes) + Remove = AllBytes; + else + Remove = AllBytes - CurBytes; + + assert(static_cast(Remove) >= 0 && + "R_LARCH_ALIGN needs expanding the content"); + NewEdgeKind = AlignRelaxable; +} + +static bool relaxBlock(LinkGraph &G, Block &Block, BlockRelaxAux &Aux) { + const auto BlockAddr = Block.getAddress(); + bool Changed = false; + ArrayRef SA = ArrayRef(Aux.Anchors); + uint32_t Delta = 0; + + Aux.EdgeKinds.assign(Aux.EdgeKinds.size(), Edge::Invalid); + Aux.Writes.clear(); + + for (auto [I, E] : llvm::enumerate(Aux.RelaxEdges)) { + const auto Loc = BlockAddr + E->getOffset() - Delta; + auto &Cur = Aux.RelocDeltas[I]; + uint32_t Remove = 0; + switch (E->getKind()) { + case AlignRelaxable: + relaxAlign(Loc, *E, Remove, Aux.EdgeKinds[I]); + break; + default: + llvm_unreachable("Unexpected relaxable edge kind"); + } + + // For all anchors whose offsets are <= E->getOffset(), they are preceded by + // the previous relocation whose RelocDeltas value equals Delta. + // Decrease their offset and update their size. + for (; SA.size() && SA[0].Offset <= E->getOffset(); SA = SA.slice(1)) { + if (SA[0].End) + SA[0].Sym->setSize(SA[0].Offset - Delta - SA[0].Sym->getOffset()); + else + SA[0].Sym->setOffset(SA[0].Offset - Delta); + } + + Delta += Remove; + if (Delta != Cur) { + Cur = Delta; + Changed = true; + } + } + + for (const SymbolAnchor &A : SA) { + if (A.End) + A.Sym->setSize(A.Offset - Delta - A.Sym->getOffset()); + else + A.Sym->setOffset(A.Offset - Delta); + } + + return Changed; +} + +static bool relaxOnce(LinkGraph &G, RelaxAux &Aux) { + bool Changed = false; + + for (auto &[B, BlockAux] : Aux.Blocks) + Changed |= relaxBlock(G, *B, BlockAux); + + return Changed; +} + +static void finalizeBlockRelax(LinkGraph &G, Block &Block, BlockRelaxAux &Aux) { + auto Contents = Block.getAlreadyMutableContent(); + auto *Dest = Contents.data(); + uint32_t Offset = 0; + uint32_t Delta = 0; + + // Update section content: remove NOPs for R_LARCH_ALIGN and rewrite + // instructions for relaxed relocations. + for (auto [I, E] : llvm::enumerate(Aux.RelaxEdges)) { + uint32_t Remove = Aux.RelocDeltas[I] - Delta; + Delta = Aux.RelocDeltas[I]; + if (Remove == 0 && Aux.EdgeKinds[I] == Edge::Invalid) + continue; + + // Copy from last location to the current relocated location. + const auto Size = E->getOffset() - Offset; + std::memmove(Dest, Contents.data() + Offset, Size); + Dest += Size; + Offset = E->getOffset() + Remove; + } + + std::memmove(Dest, Contents.data() + Offset, Contents.size() - Offset); + + // Fixup edge offsets and kinds. + Delta = 0; + size_t I = 0; + for (auto &E : Block.edges()) { + E.setOffset(E.getOffset() - Delta); + + if (I < Aux.RelaxEdges.size() && Aux.RelaxEdges[I] == &E) { + if (Aux.EdgeKinds[I] != Edge::Invalid) + E.setKind(Aux.EdgeKinds[I]); + + Delta = Aux.RelocDeltas[I]; + ++I; + } + } + + // Remove AlignRelaxable edges: all other relaxable edges got modified and + // will be used later while linking. Alignment is entirely handled here so we + // don't need these edges anymore. + for (auto IE = Block.edges().begin(); IE != Block.edges().end();) { + if (IE->getKind() == AlignRelaxable) + IE = Block.removeEdge(IE); + else + ++IE; + } +} + +static void finalizeRelax(LinkGraph &G, RelaxAux &Aux) { + for (auto &[B, BlockAux] : Aux.Blocks) + finalizeBlockRelax(G, *B, BlockAux); +} + +static Error relax(LinkGraph &G) { + auto Aux = initRelaxAux(G); + while (relaxOnce(G, Aux)) { + } + finalizeRelax(G, Aux); + return Error::success(); +} + template class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { private: @@ -74,6 +306,8 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { return RequestGOTAndTransformToPageOffset12; case ELF::R_LARCH_CALL36: return Call36PCRel; + case ELF::R_LARCH_ALIGN: + return AlignRelaxable; } return make_error( @@ -81,6 +315,11 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { object::getELFRelocationTypeName(ELF::EM_LOONGARCH, Type)); } + EdgeKind_loongarch getRelaxableRelocationKind(EdgeKind_loongarch Kind) { + // TODO: Implement more. Just ignore all relaxations now. + return Kind; + } + Error addRelocations() override { LLVM_DEBUG(dbgs() << "Processing relocations:\n"); @@ -100,22 +339,17 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { using Base = ELFLinkGraphBuilder; uint32_t Type = Rel.getType(false); - // We do not implement linker relaxation for jitlink now, except what is - // required for alignment (see below). - if (Type == ELF::R_LARCH_RELAX) - return Error::success(); - int64_t Addend = Rel.r_addend; - if (Type == ELF::R_LARCH_ALIGN) { - uint64_t Alignment = PowerOf2Ceil(Addend); - // FIXME: Implement support for ensuring alignment together with linker - // relaxation. Addend is always 28 in the most common case when - // interpreting C++ code in clang-repl. - if (Alignment > 32) - return make_error( - formatv("Unsupported relocation R_LARCH_ALIGN with alignment {0} " - "larger than 32 (addend: {1})", - Alignment, Addend)); + + if (Type == ELF::R_LARCH_RELAX) { + if (BlockToFix.edges_empty()) + return make_error( + "R_LARCH_RELAX without preceding relocation", + inconvertibleErrorCode()); + + auto &PrevEdge = *std::prev(BlockToFix.edges().end()); + auto Kind = static_cast(PrevEdge.getKind()); + PrevEdge.setKind(getRelaxableRelocationKind(Kind)); return Error::success(); } @@ -227,6 +461,9 @@ void link_ELF_loongarch(std::unique_ptr G, // Add an in-place GOT/PLTStubs build pass. Config.PostPrunePasses.push_back(buildTables_ELF_loongarch); + + // Add a linker relaxation pass. + Config.PostAllocationPasses.push_back(relax); } if (auto Err = Ctx->modifyPassConfig(*G, Config)) @@ -235,5 +472,7 @@ void link_ELF_loongarch(std::unique_ptr G, ELFJITLinker_loongarch::link(std::move(Ctx), std::move(G), std::move(Config)); } +LinkGraphPassFunction createRelaxationPass_ELF_loongarch() { return relax; } + } // namespace jitlink } // namespace llvm diff --git a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp index cdb3da04354ee..a5579b074de7c 100644 --- a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp @@ -52,6 +52,7 @@ const char *getEdgeKindName(Edge::Kind K) { KIND_NAME_CASE(RequestGOTAndTransformToPage20) KIND_NAME_CASE(RequestGOTAndTransformToPageOffset12) KIND_NAME_CASE(Call36PCRel) + KIND_NAME_CASE(AlignRelaxable) default: return getGenericEdgeKindName(K); } diff --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_relax_align.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_relax_align.s new file mode 100644 index 0000000000000..ec1aceb25b614 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_relax_align.s @@ -0,0 +1,95 @@ +# RUN: rm -rf %t && mkdir %t && cd %t + +# RUN: llvm-mc --filetype=obj --triple=loongarch32 -mattr=+relax %s -o %t.la32 +# RUN: llvm-jitlink --noexec \ +# RUN: -slab-allocate 100Kb -slab-address 0x0 -slab-page-size 16384 \ +# RUN: --check %s %t.la32 + +# RUN: llvm-mc --filetype=obj --triple=loongarch64 -mattr=+relax %s -o %t.la64 +# RUN: llvm-jitlink --noexec \ +# RUN: -slab-allocate 100Kb -slab-address 0x0 -slab-page-size 16384 \ +# RUN: --check %s %t.la64 + +## Test that we can handle R_LARCH_ALIGN. + + .text + + .globl main,align4,align8,align16,align32,alignmax12,alignmax8 + .type main,@function +main: + bl f + .align 2 +align4: + bl f + .size align4, .-align4 + .align 3 +align8: + bl f + .size align8, .-align8 + .align 4 +align16: + bl f + .size align16, .-align16 + .align 5 +align32: + bl f + .size align32, .-align32 + .align 4,,12 +alignmax12: + bl f + .size alignmax12, .-alignmax12 + .align 4,,8 +alignmax8: + bl f + .size alignmax8, .-alignmax8 + .size main, .-main + + .globl f +f: + ret + .size f, .-f + +# jitlink-check: main = 0x0 +# jitlink-check: align4 = 0x4 +# jitlink-check: align8 = 0x8 +# jitlink-check: align16 = 0x10 +# jitlink-check: align32 = 0x20 +# jitlink-check: alignmax12 = 0x30 +## 3 nops (12 bytes) should be inserted to satisfy alignment. +## But the max bytes we can insert is 8. So alignment is ignored. +# jitlink-check: alignmax8 = 0x34 + +## main: bl f +# jitlink-check: (*{4}(main))[31:26] = 0x15 +# jitlink-check: decode_operand(main, 0)[27:0] = (f - main)[27:0] + +## align 4: bl f +# jitlink-check: (*{4}(align4))[31:26] = 0x15 +# jitlink-check: decode_operand(align4, 0)[27:0] = (f - align4)[27:0] + +## align8: bl f; nop +# jitlink-check: (*{4}(align8))[31:26] = 0x15 +# jitlink-check: decode_operand(align8, 0)[27:0] = (f - align8)[27:0] +# jitlink-check: (*{4}(align8+4)) = 0x3400000 + +## align16: bl f; nop; nop; nop +# jitlink-check: (*{4}(align16))[31:26] = 0x15 +# jitlink-check: decode_operand(align16, 0)[27:0] = (f - align16)[27:0] +# jitlink-check: (*{4}(align16+4)) = 0x3400000 +# jitlink-check: (*{4}(align16+8)) = 0x3400000 +# jitlink-check: (*{4}(align16+12)) = 0x3400000 + +## align32: bl f; nop; nop; nop +# jitlink-check: (*{4}(align32))[31:26] = 0x15 +# jitlink-check: decode_operand(align32, 0)[27:0] = (f - align32)[27:0] +# jitlink-check: (*{4}(align32+4)) = 0x3400000 +# jitlink-check: (*{4}(align32+8)) = 0x3400000 +# jitlink-check: (*{4}(align32+12)) = 0x3400000 + +## alignmax12: bl f +# jitlink-check: (*{4}(alignmax12))[31:26] = 0x15 +# jitlink-check: decode_operand(alignmax12, 0)[27:0] = (f - alignmax12)[27:0] + +## alignmax8: bl f +# jitlink-check: (*{4}(alignmax8))[31:26] = 0x15 +# jitlink-check: decode_operand(alignmax8, 0)[27:0] = (f - alignmax8)[27:0] From 4398bd99d87c58307e55798a3f6dde372b4d0cb6 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Fri, 27 Dec 2024 15:39:57 +0800 Subject: [PATCH 3/5] [JITLink][LoongArch] Add label addition and subtraction relocations --- .../llvm/ExecutionEngine/JITLink/loongarch.h | 180 ++++++++++++++++++ .../ExecutionEngine/JITLink/ELF_loongarch.cpp | 24 +++ .../lib/ExecutionEngine/JITLink/loongarch.cpp | 12 ++ .../JITLink/LoongArch/ELF_reloc_addsub.s | 53 ++++++ 4 files changed, 269 insertions(+) create mode 100644 llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h index d6025edf7d110..1d763e1255fc2 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h @@ -14,8 +14,10 @@ #define LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H #include "TableManager.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ExecutionEngine/JITLink/JITLink.h" #include "llvm/ExecutionEngine/Orc/Shared/MemoryFlags.h" +#include "llvm/Support/LEB128.h" namespace llvm { namespace jitlink { @@ -226,6 +228,90 @@ enum EdgeKind_loongarch : Edge::Kind { /// Call36PCRel, + /// low 6 bits label addition + /// + /// Fixup expression: + /// Fixup <- (*{1}Fixup + (Target + Addend) & 0x3f) : int8 + /// + Add6, + + /// 8 bits label addition + /// + /// Fixup expression: + /// Fixup <- (Target + *{1}Fixup + Addend) : int8 + /// + Add8, + + /// 16 bits label addition + /// + /// Fixup expression: + /// Fixup <- (Target + *{2}Fixup + Addend) : int16 + /// + Add16, + + /// 32 bits label addition + /// + /// Fixup expression: + /// Fixup <- (Target + *{4}Fixup + Addend) : int32 + /// + Add32, + + /// 64 bits label addition + /// + /// Fixup expression: + /// Fixup <- (Target + *{8}Fixup + Addend) : int64 + /// + Add64, + + /// ULEB128 bits label addition + /// + /// Fixup expression: + /// Fixup <- (Target + *{16}Fixup + Addend) : uleb128 + /// + AddUleb128, + + /// low 6 bits label subtraction + /// + /// Fixup expression: + /// Fixup <- (*{1}Fixup - (Target + Addend) & 0x3f) : int8 + /// + Sub6, + + /// 8 bits label subtraction + /// + /// Fixup expression: + /// Fixup <- (*{1}Fixup - Target - Addend) : int8 + /// + Sub8, + + /// 16 bits label subtraction + /// + /// Fixup expression: + /// Fixup <- (*{2}Fixup - Target - Addend) : int16 + /// + Sub16, + + /// 32 bits label subtraction + /// + /// Fixup expression: + /// Fixup <- (*{4}Fixup - Target - Addend) : int32 + /// + Sub32, + + /// 64 bits label subtraction + /// + /// Fixup expression: + /// Fixup <- (*{8}Fixup - Target - Addend) : int64 + /// + Sub64, + + /// ULEB128 bits label subtraction + /// + /// Fixup expression: + /// Fixup <- (*{16}Fixup - Target - Addend) : uleb128 + /// + SubUleb128, + /// Alignment requirement used by linker relaxation. /// /// Linker relaxation will use this to ensure all code sequences are properly @@ -369,6 +455,100 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) { *(little32_t *)(FixupPtr + 4) = Jirl | Lo16; break; } + case Add6: { + int64_t Value = *(reinterpret_cast(FixupPtr)); + Value += ((TargetAddress + Addend) & 0x3f); + *FixupPtr = (*FixupPtr & 0xc0) | (static_cast(Value) & 0x3f); + break; + } + case Add8: { + int64_t Value = + TargetAddress + *(reinterpret_cast(FixupPtr)) + Addend; + *FixupPtr = static_cast(Value); + break; + } + case Add16: { + int64_t Value = + TargetAddress + support::endian::read16le(FixupPtr) + Addend; + *(little16_t *)FixupPtr = static_cast(Value); + break; + } + case Add32: { + int64_t Value = + TargetAddress + support::endian::read32le(FixupPtr) + Addend; + *(little32_t *)FixupPtr = static_cast(Value); + break; + } + case Add64: { + int64_t Value = + TargetAddress + support::endian::read64le(FixupPtr) + Addend; + *(little64_t *)FixupPtr = static_cast(Value); + break; + } + case AddUleb128: { + const uint32_t Maxcount = 1 + 64 / 7; + uint32_t Count; + const char *Error = nullptr; + uint64_t Orig = decodeULEB128((reinterpret_cast(FixupPtr)), + &Count, nullptr, &Error); + + if (Count > Maxcount || (Count == Maxcount && Error)) + return make_error( + "0x" + llvm::utohexstr(orc::ExecutorAddr(FixupAddress).getValue()) + + ": extra space for uleb128"); + + uint64_t Mask = Count < Maxcount ? (1ULL << 7 * Count) - 1 : -1ULL; + encodeULEB128((Orig + TargetAddress + Addend) & Mask, + (reinterpret_cast(FixupPtr)), Count); + break; + } + case Sub6: { + int64_t Value = *(reinterpret_cast(FixupPtr)); + Value -= ((TargetAddress + Addend) & 0x3f); + *FixupPtr = (*FixupPtr & 0xc0) | (static_cast(Value) & 0x3f); + break; + } + case Sub8: { + int64_t Value = + *(reinterpret_cast(FixupPtr)) - TargetAddress - Addend; + *FixupPtr = static_cast(Value); + break; + } + case Sub16: { + int64_t Value = + support::endian::read16le(FixupPtr) - TargetAddress - Addend; + *(little16_t *)FixupPtr = static_cast(Value); + break; + } + case Sub32: { + int64_t Value = + support::endian::read32le(FixupPtr) - TargetAddress - Addend; + *(little32_t *)FixupPtr = static_cast(Value); + break; + } + case Sub64: { + int64_t Value = + support::endian::read64le(FixupPtr) - TargetAddress - Addend; + *(little64_t *)FixupPtr = static_cast(Value); + break; + } + case SubUleb128: { + const uint32_t Maxcount = 1 + 64 / 7; + uint32_t Count; + const char *Error = nullptr; + uint64_t Orig = decodeULEB128((reinterpret_cast(FixupPtr)), + &Count, nullptr, &Error); + + if (Count > Maxcount || (Count == Maxcount && Error)) + return make_error( + "0x" + llvm::utohexstr(orc::ExecutorAddr(FixupAddress).getValue()) + + ": extra space for uleb128"); + + uint64_t Mask = Count < Maxcount ? (1ULL << 7 * Count) - 1 : -1ULL; + encodeULEB128((Orig - TargetAddress - Addend) & Mask, + (reinterpret_cast(FixupPtr)), Count); + break; + } case AlignRelaxable: // Ignore when the relaxation pass did not run break; diff --git a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp index 3f0a7b645e83f..f23fb346c55f9 100644 --- a/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp @@ -306,6 +306,30 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder { return RequestGOTAndTransformToPageOffset12; case ELF::R_LARCH_CALL36: return Call36PCRel; + case ELF::R_LARCH_ADD6: + return Add6; + case ELF::R_LARCH_ADD8: + return Add8; + case ELF::R_LARCH_ADD16: + return Add16; + case ELF::R_LARCH_ADD32: + return Add32; + case ELF::R_LARCH_ADD64: + return Add64; + case ELF::R_LARCH_ADD_ULEB128: + return AddUleb128; + case ELF::R_LARCH_SUB6: + return Sub6; + case ELF::R_LARCH_SUB8: + return Sub8; + case ELF::R_LARCH_SUB16: + return Sub16; + case ELF::R_LARCH_SUB32: + return Sub32; + case ELF::R_LARCH_SUB64: + return Sub64; + case ELF::R_LARCH_SUB_ULEB128: + return SubUleb128; case ELF::R_LARCH_ALIGN: return AlignRelaxable; } diff --git a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp index a5579b074de7c..55389adb31b60 100644 --- a/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp +++ b/llvm/lib/ExecutionEngine/JITLink/loongarch.cpp @@ -52,6 +52,18 @@ const char *getEdgeKindName(Edge::Kind K) { KIND_NAME_CASE(RequestGOTAndTransformToPage20) KIND_NAME_CASE(RequestGOTAndTransformToPageOffset12) KIND_NAME_CASE(Call36PCRel) + KIND_NAME_CASE(Add6) + KIND_NAME_CASE(Add8) + KIND_NAME_CASE(Add16) + KIND_NAME_CASE(Add32) + KIND_NAME_CASE(Add64) + KIND_NAME_CASE(AddUleb128) + KIND_NAME_CASE(Sub6) + KIND_NAME_CASE(Sub8) + KIND_NAME_CASE(Sub16) + KIND_NAME_CASE(Sub32) + KIND_NAME_CASE(Sub64) + KIND_NAME_CASE(SubUleb128) KIND_NAME_CASE(AlignRelaxable) default: return getGenericEdgeKindName(K); diff --git a/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s new file mode 100644 index 0000000000000..86e3008ef4094 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/LoongArch/ELF_reloc_addsub.s @@ -0,0 +1,53 @@ +# RUN: rm -rf %t && mkdir -p %t +# RUN: llvm-mc --triple=loongarch32 -mattr=+relax --filetype=obj \ +# RUN: -o %t/la32_reloc_addsub.o %s +# RUN: llvm-jitlink --noexec --check %s %t/la32_reloc_addsub.o \ +# RUN: --slab-allocate=1Mb --slab-address=0x1000 --slab-page-size=0x4000 +# RUN: llvm-mc --triple=loongarch64 -mattr=+relax --filetype=obj \ +# RUN: -o %t/la64_reloc_addsub.o %s +# RUN: llvm-jitlink --noexec --check %s %t/la64_reloc_addsub.o \ +# RUN: --slab-allocate=1Mb --slab-address=0x1000 --slab-page-size=0x4000 + +# jitlink-check: *{8}(named_data) = 0x8 +# jitlink-check: *{4}(named_data+8) = 0x8 +# jitlink-check: *{2}(named_data+12) = 0x8 +# jitlink-check: *{1}(named_data+14) = 0x8 +# jitlink-check: *{1}(named_data+15) = 0x10 + +# jitlink-check: *{1}(leb_data) = 0x8 +# jitlink-check: *{2}(leb_data+1) = 0x180 +# jitlink-check: *{8}(leb_data+3) = 0xfffffffffffffff8 +# jitlink-check: *{2}(leb_data+11) = 0x1ff +# jitlink-check: *{1}(leb_data+13) = 0x7f +# jitlink-check: *{2}(leb_data+14) = 0x181 + +.section .alloc_data,"ax",@progbits +.global main +main: +.L0: +# Referencing named_data symbol to avoid the following relocations be +# skipped. This macro instruction will be expand to two instructions +# (pcalau12i + ld.w/d). + la.global $t0, named_data +.L1: + +named_data: +.reloc named_data+15, R_LARCH_ADD6, .L1 +.reloc named_data+15, R_LARCH_SUB6, .L0 +.dword .L1 - .L0 +.word .L1 - .L0 +.half .L1 - .L0 +.byte .L1 - .L0 +.byte 0x8 + +.size named_data, 16 + +leb_data: +.uleb128 .L1 - .L0 +.uleb128 .L1 - .L0 + 120 +.uleb128 -(.L1 - .L0) +.uleb128 leb_end - leb_data + 111 +.uleb128 leb_end - leb_data + 113 +leb_end: + +.size leb_data, 16 From ea920733f0b53436cc83d9dc7fb1baa3fa144d72 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Fri, 10 Jan 2025 10:08:14 +0800 Subject: [PATCH 4/5] update notes --- llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h index 1d763e1255fc2..6dbb924213472 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h @@ -238,35 +238,35 @@ enum EdgeKind_loongarch : Edge::Kind { /// 8 bits label addition /// /// Fixup expression: - /// Fixup <- (Target + *{1}Fixup + Addend) : int8 + /// Fixup <- (*{1}Fixup + Target + Addend) : int8 /// Add8, /// 16 bits label addition /// /// Fixup expression: - /// Fixup <- (Target + *{2}Fixup + Addend) : int16 + /// Fixup <- (*{2}Fixup + Target + Addend) : int16 /// Add16, /// 32 bits label addition /// /// Fixup expression: - /// Fixup <- (Target + *{4}Fixup + Addend) : int32 + /// Fixup <- (*{4}Fixup + Target + Addend) : int32 /// Add32, /// 64 bits label addition /// /// Fixup expression: - /// Fixup <- (Target + *{8}Fixup + Addend) : int64 + /// Fixup <- (*{8}Fixup + Target + Addend) : int64 /// Add64, /// ULEB128 bits label addition /// /// Fixup expression: - /// Fixup <- (Target + *{16}Fixup + Addend) : uleb128 + /// Fixup <- (*{16}Fixup + Target + Addend) : uleb128 /// AddUleb128, From d98c5d1b4ca404ab54b893d15c3c39d12a8fba20 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Fri, 10 Jan 2025 10:21:19 +0800 Subject: [PATCH 5/5] update notes --- llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h index 6dbb924213472..3c664296e9f33 100644 --- a/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h +++ b/llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h @@ -266,7 +266,7 @@ enum EdgeKind_loongarch : Edge::Kind { /// ULEB128 bits label addition /// /// Fixup expression: - /// Fixup <- (*{16}Fixup + Target + Addend) : uleb128 + /// Fixup <- (Fixup + Target + Addend) : uleb128 /// AddUleb128, @@ -308,7 +308,7 @@ enum EdgeKind_loongarch : Edge::Kind { /// ULEB128 bits label subtraction /// /// Fixup expression: - /// Fixup <- (*{16}Fixup - Target - Addend) : uleb128 + /// Fixup <- (Fixup - Target - Addend) : uleb128 /// SubUleb128,