1//===-- mips.cpp - Generic JITLink MIPS edge kinds and utilities ----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "llvm/ExecutionEngine/JITLink/mips.h"
10
11#include "llvm/ADT/STLExtras.h"
12
13namespace llvm {
14namespace jitlink {
15namespace mips {
16
17namespace {
18
19constexpr unsigned HalfwordBits = 16;
20constexpr unsigned MaxPointerJumpStubInstructions = 9;
21
22enum class Register : uint32_t {
23 Zero = 0,
24 T9 = 25,
25};
26
27enum class Opcode : uint32_t {
28 Addiu = 0x09,
29 Lui = 0x0f,
30 Daddiu = 0x19,
31 Lw = 0x23,
32 Ld = 0x37,
33};
34
35enum class Function : uint32_t {
36 Jr = 0x08,
37 Jalr = 0x09,
38 Dsll = 0x38,
39};
40
41constexpr unsigned OpcodeShift = 26;
42constexpr unsigned RSShift = 21;
43constexpr unsigned RTShift = 16;
44constexpr unsigned RDShift = 11;
45constexpr unsigned ShiftAmountShift = 6;
46
47constexpr uint32_t encodeIType(Opcode Op, Register RS, Register RT) {
48 return (static_cast<uint32_t>(Op) << OpcodeShift) |
49 (static_cast<uint32_t>(RS) << RSShift) |
50 (static_cast<uint32_t>(RT) << RTShift);
51}
52
53constexpr uint32_t encodeRType(Function Fn, Register RS, Register RT,
54 Register RD, unsigned ShiftAmount = 0) {
55 return (static_cast<uint32_t>(RS) << RSShift) |
56 (static_cast<uint32_t>(RT) << RTShift) |
57 (static_cast<uint32_t>(RD) << RDShift) |
58 (ShiftAmount << ShiftAmountShift) | static_cast<uint32_t>(Fn);
59}
60
61constexpr uint32_t encodeLui(Register RT) {
62 return encodeIType(Op: Opcode::Lui, RS: Register::Zero, RT);
63}
64
65constexpr uint32_t encodeAddiu(Register RT, Register RS) {
66 return encodeIType(Op: Opcode::Addiu, RS, RT);
67}
68
69constexpr uint32_t encodeDaddiu(Register RT, Register RS) {
70 return encodeIType(Op: Opcode::Daddiu, RS, RT);
71}
72
73constexpr uint32_t encodeLoadPointer32(Register RT, Register Base) {
74 return encodeIType(Op: Opcode::Lw, RS: Base, RT);
75}
76
77constexpr uint32_t encodeLoadPointer64(Register RT, Register Base) {
78 return encodeIType(Op: Opcode::Ld, RS: Base, RT);
79}
80
81constexpr uint32_t encodeDsll(Register RD, Register RT, unsigned ShiftAmount) {
82 return encodeRType(Fn: Function::Dsll, RS: Register::Zero, RT, RD, ShiftAmount);
83}
84
85constexpr uint32_t encodeIndirectJump(Register Target, bool R6) {
86 // JALR with $zero is the release-6 no-link compact jump.
87 return encodeRType(Fn: R6 ? Function::Jalr : Function::Jr, RS: Target, RT: Register::Zero,
88 RD: Register::Zero);
89}
90
91constexpr uint32_t encodeNop() { return 0; }
92
93struct StubFixup {
94 Edge::Kind Kind;
95 unsigned InstructionIndex;
96};
97
98constexpr StubFixup Pointer32StubFixups[] = {{.Kind: Hi16, .InstructionIndex: 0}, {.Kind: Lo16, .InstructionIndex: 1}};
99constexpr StubFixup Pointer64StubFixups[] = {
100 {.Kind: Highest16, .InstructionIndex: 0}, {.Kind: Higher16, .InstructionIndex: 1}, {.Kind: Hi16, .InstructionIndex: 3}, {.Kind: Lo16, .InstructionIndex: 5}};
101
102} // namespace
103
104const char *getEdgeKindName(Edge::Kind K) {
105 switch (K) {
106#define KIND_NAME(K) \
107 case K: \
108 return #K
109 KIND_NAME(Pointer32);
110 KIND_NAME(Pointer64);
111 KIND_NAME(PagePointer32);
112 KIND_NAME(PagePointer64);
113 KIND_NAME(Delta32);
114 KIND_NAME(Delta64);
115 KIND_NAME(NegDelta32);
116 KIND_NAME(Abs16);
117 KIND_NAME(Hi16);
118 KIND_NAME(Lo16);
119 KIND_NAME(Higher16);
120 KIND_NAME(Highest16);
121 KIND_NAME(Jump26);
122 KIND_NAME(PC16);
123 KIND_NAME(PC32);
124 KIND_NAME(PC18S3);
125 KIND_NAME(PC19S2);
126 KIND_NAME(PC21S2);
127 KIND_NAME(PC26S2);
128 KIND_NAME(PCHi16);
129 KIND_NAME(PCLo16);
130 KIND_NAME(GPDispHi16);
131 KIND_NAME(GPDispLo16);
132 KIND_NAME(GPRel16);
133 KIND_NAME(GPRel32);
134 KIND_NAME(GPRel64);
135 KIND_NAME(GOTOffset16);
136 KIND_NAME(GOTOffsetHi16);
137 KIND_NAME(GOTOffsetLo16);
138 KIND_NAME(GOTPageOffset16);
139 KIND_NAME(DTPRelHi16);
140 KIND_NAME(DTPRelLo16);
141 KIND_NAME(DTPRel32);
142 KIND_NAME(DTPRel64);
143 KIND_NAME(NegGPRelHi16);
144 KIND_NAME(NegGPRelLo16);
145 KIND_NAME(RequestGOTAndTransformToOffset16);
146 KIND_NAME(RequestGOTPageAndTransformToOffset16);
147 KIND_NAME(RequestGOTAndTransformToOffsetHi16);
148 KIND_NAME(RequestGOTAndTransformToOffsetLo16);
149 KIND_NAME(RequestTLSGDAndTransformToOffset16);
150 KIND_NAME(RequestTLSLDMAndTransformToOffset16);
151#undef KIND_NAME
152 default:
153 return getGenericEdgeKindName(K);
154 }
155}
156
157static const char NullPointerContent[sizeof(uint64_t)] = {};
158
159bool isR6(const LinkGraph &G) {
160 const auto &Features = G.getFeatures().getFeatures();
161 return llvm::is_contained(Range: Features, Element: "+mips32r6") ||
162 llvm::is_contained(Range: Features, Element: "+mips64r6");
163}
164
165Edge::Kind getPointerEdgeKind(const LinkGraph &G) {
166 return G.getPointerSize() == 8 ? Pointer64 : Pointer32;
167}
168
169ArrayRef<char> getPointerBlockContent(const LinkGraph &G) {
170 return {NullPointerContent, G.getPointerSize()};
171}
172
173Symbol &createAnonymousPointer(LinkGraph &G, Section &PointerSection,
174 Symbol *InitialTarget,
175 Edge::AddendT InitialAddend) {
176 auto &B = G.createContentBlock(Parent&: PointerSection, Content: getPointerBlockContent(G),
177 Address: orc::ExecutorAddr(), Alignment: G.getPointerSize(), AlignmentOffset: 0);
178 if (InitialTarget)
179 B.addEdge(K: getPointerEdgeKind(G), Offset: 0, Target&: *InitialTarget, Addend: InitialAddend);
180 return G.addAnonymousSymbol(Content&: B, Offset: 0, Size: G.getPointerSize(), IsCallable: false, IsLive: false);
181}
182
183Symbol &createAnonymousPointerJumpStub(LinkGraph &G, Section &StubSection,
184 Symbol &PointerSymbol) {
185 // Non-PIC callers do not enter with this graph's $gp.
186 unsigned PointerSize = G.getPointerSize();
187
188 SmallVector<uint32_t, MaxPointerJumpStubInstructions> Instructions;
189 ArrayRef<StubFixup> Fixups;
190 if (PointerSize == 8) {
191 Instructions = {
192 encodeLui(RT: Register::T9),
193 encodeDaddiu(RT: Register::T9, RS: Register::T9),
194 encodeDsll(RD: Register::T9, RT: Register::T9, ShiftAmount: HalfwordBits),
195 encodeDaddiu(RT: Register::T9, RS: Register::T9),
196 encodeDsll(RD: Register::T9, RT: Register::T9, ShiftAmount: HalfwordBits),
197 encodeDaddiu(RT: Register::T9, RS: Register::T9),
198 encodeLoadPointer64(RT: Register::T9, Base: Register::T9),
199 };
200 Fixups = Pointer64StubFixups;
201 } else {
202 Instructions = {
203 encodeLui(RT: Register::T9),
204 encodeAddiu(RT: Register::T9, RS: Register::T9),
205 encodeLoadPointer32(RT: Register::T9, Base: Register::T9),
206 };
207 Fixups = Pointer32StubFixups;
208 }
209 Instructions.push_back(Elt: encodeIndirectJump(Target: Register::T9, R6: isR6(G)));
210 Instructions.push_back(Elt: encodeNop());
211
212 auto Content = G.allocateBuffer(Size: Instructions.size() * InstructionSize);
213 for (auto [Index, Instruction] : llvm::enumerate(First&: Instructions))
214 support::endian::write32(P: Content.data() + Index * InstructionSize,
215 V: Instruction, E: G.getEndianness());
216
217 auto &B = G.createContentBlock(Parent&: StubSection, Content, Address: orc::ExecutorAddr(),
218 Alignment: alignof(uint32_t), AlignmentOffset: 0);
219 for (const StubFixup &F : Fixups)
220 B.addEdge(K: F.Kind, Offset: F.InstructionIndex * InstructionSize, Target&: PointerSymbol, Addend: 0);
221 return G.addAnonymousSymbol(Content&: B, Offset: 0, Size: Content.size(), IsCallable: true, IsLive: false);
222}
223
224} // namespace mips
225} // namespace jitlink
226} // namespace llvm
227