xref: /freebsd/contrib/llvm-project/llvm/include/llvm/ExecutionEngine/JITLink/aarch32.h (revision 700637cbb5e582861067a11aaca4d053546871d2)
1 //===------ aarch32.h - Generic JITLink arm/thumb utilities -----*- C++ -*-===//
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 // Generic utilities for graphs representing arm/thumb objects.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_EXECUTIONENGINE_JITLINK_AARCH32
14 #define LLVM_EXECUTIONENGINE_JITLINK_AARCH32
15 
16 #include "TableManager.h"
17 #include "llvm/ExecutionEngine/JITLink/JITLink.h"
18 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h"
19 #include "llvm/Support/ARMBuildAttributes.h"
20 #include "llvm/Support/Compiler.h"
21 #include "llvm/Support/Error.h"
22 
23 namespace llvm {
24 namespace jitlink {
25 namespace aarch32 {
26 
27 /// Check whether the given target flags are set for this Symbol.
28 LLVM_ABI bool hasTargetFlags(Symbol &Sym, TargetFlagsType Flags);
29 
30 /// JITLink-internal AArch32 fixup kinds
31 enum EdgeKind_aarch32 : Edge::Kind {
32 
33   ///
34   /// Relocations of class Data respect target endianness (unless otherwise
35   /// specified)
36   ///
37   FirstDataRelocation = Edge::FirstRelocation,
38 
39   /// Relative 32-bit value relocation
40   Data_Delta32 = FirstDataRelocation,
41 
42   /// Absolute 32-bit value relocation
43   Data_Pointer32,
44 
45   /// Relative 31-bit value relocation that preserves the most-significant bit
46   Data_PRel31,
47 
48   /// Create GOT entry and store offset
49   Data_RequestGOTAndTransformToDelta32,
50 
51   LastDataRelocation = Data_RequestGOTAndTransformToDelta32,
52 
53   ///
54   /// Relocations of class Arm (covers fixed-width 4-byte instruction subset)
55   ///
56   FirstArmRelocation,
57 
58   /// Write immediate value for unconditional PC-relative branch with link.
59   /// We patch the instruction opcode to account for an instruction-set state
60   /// switch: we use the bl instruction to stay in ARM and the blx instruction
61   /// to switch to Thumb.
62   Arm_Call = FirstArmRelocation,
63 
64   /// Write immediate value for conditional PC-relative branch without link.
65   /// If the branch target is not ARM, we are forced to generate an explicit
66   /// interworking stub.
67   Arm_Jump24,
68 
69   /// Write immediate value to the lower halfword of the destination register
70   Arm_MovwAbsNC,
71 
72   /// Write immediate value to the top halfword of the destination register
73   Arm_MovtAbs,
74 
75   LastArmRelocation = Arm_MovtAbs,
76 
77   ///
78   /// Relocations of class Thumb16 and Thumb32 (covers Thumb instruction subset)
79   ///
80   FirstThumbRelocation,
81 
82   /// Write immediate value for unconditional PC-relative branch with link.
83   /// We patch the instruction opcode to account for an instruction-set state
84   /// switch: we use the bl instruction to stay in Thumb and the blx instruction
85   /// to switch to ARM.
86   Thumb_Call = FirstThumbRelocation,
87 
88   /// Write immediate value for PC-relative branch without link. The instruction
89   /// can be made conditional by an IT block. If the branch target is not ARM,
90   /// we are forced to generate an explicit interworking stub.
91   Thumb_Jump24,
92 
93   /// Write immediate value to the lower halfword of the destination register
94   Thumb_MovwAbsNC,
95 
96   /// Write immediate value to the top halfword of the destination register
97   Thumb_MovtAbs,
98 
99   /// Write PC-relative immediate value to the lower halfword of the destination
100   /// register
101   Thumb_MovwPrelNC,
102 
103   /// Write PC-relative immediate value to the top halfword of the destination
104   /// register
105   Thumb_MovtPrel,
106 
107   LastThumbRelocation = Thumb_MovtPrel,
108 
109   /// No-op relocation
110   None,
111 
112   LastRelocation = None,
113 };
114 
115 /// Flags enum for AArch32-specific symbol properties
116 enum TargetFlags_aarch32 : TargetFlagsType {
117   ThumbSymbol = 1 << 0,
118 };
119 
120 /// Human-readable name for a given CPU architecture kind
121 LLVM_ABI const char *getCPUArchName(ARMBuildAttrs::CPUArch K);
122 
123 /// Get a human-readable name for the given AArch32 edge kind.
124 LLVM_ABI const char *getEdgeKindName(Edge::Kind K);
125 
126 /// AArch32 uses stubs for a number of purposes, like branch range extension
127 /// or interworking between Arm and Thumb instruction subsets.
128 ///
129 /// Stub implementations vary depending on CPU architecture (v4, v6, v7),
130 /// instruction subset and branch type (absolute/PC-relative).
131 ///
132 /// For each kind of stub, the StubsFlavor defines one concrete form that is
133 /// used throughout the LinkGraph.
134 ///
135 /// Stubs are often called "veneers" in the official docs and online.
136 ///
137 enum class StubsFlavor {
138   Undefined = 0,
139   pre_v7,
140   v7,
141 };
142 
143 /// JITLink sub-arch configuration for Arm CPU models
144 struct ArmConfig {
145   bool J1J2BranchEncoding = false;
146   StubsFlavor Stubs = StubsFlavor::Undefined;
147   // In the long term, we might want a linker switch like --target1-rel
148   bool Target1Rel = false;
149 };
150 
151 /// Obtain the sub-arch configuration for a given Arm CPU model.
getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch)152 inline ArmConfig getArmConfigForCPUArch(ARMBuildAttrs::CPUArch CPUArch) {
153   ArmConfig ArmCfg;
154   if (CPUArch == ARMBuildAttrs::v7 || CPUArch >= ARMBuildAttrs::v7E_M) {
155     ArmCfg.J1J2BranchEncoding = true;
156     ArmCfg.Stubs = StubsFlavor::v7;
157   } else {
158     ArmCfg.J1J2BranchEncoding = false;
159     ArmCfg.Stubs = StubsFlavor::pre_v7;
160   }
161   return ArmCfg;
162 }
163 
164 /// Immutable pair of halfwords, Hi and Lo, with overflow check
165 struct HalfWords {
HalfWordsHalfWords166   constexpr HalfWords() : Hi(0), Lo(0) {}
HalfWordsHalfWords167   constexpr HalfWords(uint32_t Hi, uint32_t Lo) : Hi(Hi), Lo(Lo) {
168     assert(isUInt<16>(Hi) && "Overflow in first half-word");
169     assert(isUInt<16>(Lo) && "Overflow in second half-word");
170   }
171   const uint16_t Hi; // First halfword
172   const uint16_t Lo; // Second halfword
173 };
174 
175 /// FixupInfo base class is required for dynamic lookups.
176 struct FixupInfoBase {
177   LLVM_ABI static const FixupInfoBase *getDynFixupInfo(Edge::Kind K);
~FixupInfoBaseFixupInfoBase178   virtual ~FixupInfoBase() {}
179 };
180 
181 /// FixupInfo checks for Arm edge kinds work on 32-bit words
182 struct FixupInfoArm : public FixupInfoBase {
183   bool (*checkOpcode)(uint32_t Wd) = nullptr;
184 };
185 
186 /// FixupInfo check for Thumb32 edge kinds work on a pair of 16-bit halfwords
187 struct FixupInfoThumb : public FixupInfoBase {
188   bool (*checkOpcode)(uint16_t Hi, uint16_t Lo) = nullptr;
189 };
190 
191 /// Collection of named constants per fixup kind
192 ///
193 /// Mandatory entries:
194 ///   Opcode      - Values of the op-code bits in the instruction, with
195 ///                 unaffected bits nulled
196 ///   OpcodeMask  - Mask with all bits set that encode the op-code
197 ///
198 /// Other common entries:
199 ///   ImmMask     - Mask with all bits set that encode the immediate value
200 ///   RegMask     - Mask with all bits set that encode the register
201 ///
202 /// Specializations can add further custom fields without restrictions.
203 ///
204 template <EdgeKind_aarch32 Kind> struct FixupInfo {};
205 
206 struct FixupInfoArmBranch : public FixupInfoArm {
207   static constexpr uint32_t Opcode = 0x0a000000;
208   static constexpr uint32_t ImmMask = 0x00ffffff;
209 };
210 
211 template <> struct FixupInfo<Arm_Jump24> : public FixupInfoArmBranch {
212   static constexpr uint32_t OpcodeMask = 0x0f000000;
213 };
214 
215 template <> struct FixupInfo<Arm_Call> : public FixupInfoArmBranch {
216   static constexpr uint32_t OpcodeMask = 0x0e000000;
217   static constexpr uint32_t CondMask = 0xe0000000; // excluding BLX bit
218   static constexpr uint32_t Unconditional = 0xe0000000;
219   static constexpr uint32_t BitH = 0x01000000;
220   static constexpr uint32_t BitBlx = 0x10000000;
221 };
222 
223 struct FixupInfoArmMov : public FixupInfoArm {
224   static constexpr uint32_t OpcodeMask = 0x0ff00000;
225   static constexpr uint32_t ImmMask = 0x000f0fff;
226   static constexpr uint32_t RegMask = 0x0000f000;
227 };
228 
229 template <> struct FixupInfo<Arm_MovtAbs> : public FixupInfoArmMov {
230   static constexpr uint32_t Opcode = 0x03400000;
231 };
232 
233 template <> struct FixupInfo<Arm_MovwAbsNC> : public FixupInfoArmMov {
234   static constexpr uint32_t Opcode = 0x03000000;
235 };
236 
237 template <> struct FixupInfo<Thumb_Jump24> : public FixupInfoThumb {
238   static constexpr HalfWords Opcode{0xf000, 0x9000};
239   static constexpr HalfWords OpcodeMask{0xf800, 0x9000};
240   static constexpr HalfWords ImmMask{0x07ff, 0x2fff};
241 };
242 
243 template <> struct FixupInfo<Thumb_Call> : public FixupInfoThumb {
244   static constexpr HalfWords Opcode{0xf000, 0xc000};
245   static constexpr HalfWords OpcodeMask{0xf800, 0xc000};
246   static constexpr HalfWords ImmMask{0x07ff, 0x2fff};
247   static constexpr uint16_t LoBitH = 0x0001;
248   static constexpr uint16_t LoBitNoBlx = 0x1000;
249 };
250 
251 struct FixupInfoThumbMov : public FixupInfoThumb {
252   static constexpr HalfWords OpcodeMask{0xfbf0, 0x8000};
253   static constexpr HalfWords ImmMask{0x040f, 0x70ff};
254   static constexpr HalfWords RegMask{0x0000, 0x0f00};
255 };
256 
257 template <> struct FixupInfo<Thumb_MovtAbs> : public FixupInfoThumbMov {
258   static constexpr HalfWords Opcode{0xf2c0, 0x0000};
259 };
260 
261 template <> struct FixupInfo<Thumb_MovtPrel> : public FixupInfoThumbMov {
262   static constexpr HalfWords Opcode{0xf2c0, 0x0000};
263 };
264 
265 template <> struct FixupInfo<Thumb_MovwAbsNC> : public FixupInfoThumbMov {
266   static constexpr HalfWords Opcode{0xf240, 0x0000};
267 };
268 
269 template <> struct FixupInfo<Thumb_MovwPrelNC> : public FixupInfoThumbMov {
270   static constexpr HalfWords Opcode{0xf240, 0x0000};
271 };
272 
273 /// Helper function to read the initial addend for Data-class relocations.
274 LLVM_ABI Expected<int64_t>
275 readAddendData(LinkGraph &G, Block &B, Edge::OffsetT Offset, Edge::Kind Kind);
276 
277 /// Helper function to read the initial addend for Arm-class relocations.
278 LLVM_ABI Expected<int64_t> readAddendArm(LinkGraph &G, Block &B,
279                                          Edge::OffsetT Offset, Edge::Kind Kind);
280 
281 /// Helper function to read the initial addend for Thumb-class relocations.
282 LLVM_ABI Expected<int64_t> readAddendThumb(LinkGraph &G, Block &B,
283                                            Edge::OffsetT Offset,
284                                            Edge::Kind Kind,
285                                            const ArmConfig &ArmCfg);
286 
287 /// Read the initial addend for a REL-type relocation. It's the value encoded
288 /// in the immediate field of the fixup location by the compiler.
289 inline Expected<int64_t> readAddend(LinkGraph &G, Block &B,
290                                     Edge::OffsetT Offset, Edge::Kind Kind,
291                                     const ArmConfig &ArmCfg) {
292   if (Kind <= LastDataRelocation)
293     return readAddendData(G, B, Offset, Kind);
294 
295   if (Kind <= LastArmRelocation)
296     return readAddendArm(G, B, Offset, Kind);
297 
298   if (Kind <= LastThumbRelocation)
299     return readAddendThumb(G, B, Offset, Kind, ArmCfg);
300 
301   assert(Kind == None && "Not associated with a relocation class");
302   return 0;
303 }
304 
305 /// Helper function to apply the fixup for Data-class relocations.
306 LLVM_ABI Error applyFixupData(LinkGraph &G, Block &B, const Edge &E);
307 
308 /// Helper function to apply the fixup for Arm-class relocations.
309 LLVM_ABI Error applyFixupArm(LinkGraph &G, Block &B, const Edge &E);
310 
311 /// Helper function to apply the fixup for Thumb-class relocations.
312 LLVM_ABI Error applyFixupThumb(LinkGraph &G, Block &B, const Edge &E,
313                                const ArmConfig &ArmCfg);
314 
315 /// Apply fixup expression for edge to block content.
316 inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E,
317                         const ArmConfig &ArmCfg) {
318   Edge::Kind Kind = E.getKind();
319 
320   if (Kind <= LastDataRelocation)
321     return applyFixupData(G, B, E);
322 
323   if (Kind <= LastArmRelocation)
324     return applyFixupArm(G, B, E);
325 
326   if (Kind <= LastThumbRelocation)
327     return applyFixupThumb(G, B, E, ArmCfg);
328 
329   assert(Kind == None && "Not associated with a relocation class");
330   return Error::success();
331 }
332 
333 /// Populate a Global Offset Table from edges that request it.
334 class GOTBuilder : public TableManager<GOTBuilder> {
335 public:
336   static StringRef getSectionName() { return "$__GOT"; }
337 
338   LLVM_ABI bool visitEdge(LinkGraph &G, Block *B, Edge &E);
339   LLVM_ABI Symbol &createEntry(LinkGraph &G, Symbol &Target);
340 
341 private:
342   Section *GOTSection = nullptr;
343 };
344 
345 /// Stubs builder emits non-position-independent Arm stubs for pre-v7 CPUs.
346 /// These architectures have no MovT/MovW instructions and don't support Thumb2.
347 /// BL is the only Thumb instruction that can generate stubs and they can always
348 /// be transformed into BLX.
349 class StubsManager_prev7 {
350 public:
351   StubsManager_prev7() = default;
352 
353   /// Name of the object file section that will contain all our stubs.
354   static StringRef getSectionName() {
355     return "__llvm_jitlink_aarch32_STUBS_prev7";
356   }
357 
358   /// Implements link-graph traversal via visitExistingEdges()
359   LLVM_ABI bool visitEdge(LinkGraph &G, Block *B, Edge &E);
360 
361 private:
362   // Each stub uses a single block that can have 2 entryponts, one for Arm and
363   // one for Thumb
364   struct StubMapEntry {
365     Block *B = nullptr;
366     Symbol *ArmEntry = nullptr;
367     Symbol *ThumbEntry = nullptr;
368   };
369 
370   std::pair<StubMapEntry *, bool> getStubMapSlot(StringRef Name) {
371     auto &&[Stubs, NewStub] = StubMap.try_emplace(Name);
372     return std::make_pair(&Stubs->second, NewStub);
373   }
374 
375   Symbol *getOrCreateSlotEntrypoint(LinkGraph &G, StubMapEntry &Slot,
376                                     bool Thumb);
377 
378   DenseMap<StringRef, StubMapEntry> StubMap;
379   Section *StubsSection = nullptr;
380 };
381 
382 /// Stubs builder for v7 emits non-position-independent Arm and Thumb stubs.
383 class StubsManager_v7 {
384 public:
385   StubsManager_v7() = default;
386 
387   /// Name of the object file section that will contain all our stubs.
388   static StringRef getSectionName() {
389     return "__llvm_jitlink_aarch32_STUBS_v7";
390   }
391 
392   /// Implements link-graph traversal via visitExistingEdges().
393   LLVM_ABI bool visitEdge(LinkGraph &G, Block *B, Edge &E);
394 
395 private:
396   // Two slots per external: Arm and Thumb
397   using StubMapEntry = std::tuple<Symbol *, Symbol *>;
398 
399   Symbol *&getStubSymbolSlot(StringRef Name, bool Thumb) {
400     StubMapEntry &Stubs = StubMap[Name];
401     if (Thumb)
402       return std::get<1>(Stubs);
403     return std::get<0>(Stubs);
404   }
405 
406   DenseMap<StringRef, StubMapEntry> StubMap;
407   Section *StubsSection = nullptr;
408 };
409 
410 } // namespace aarch32
411 } // namespace jitlink
412 } // namespace llvm
413 
414 #endif // LLVM_EXECUTIONENGINE_JITLINK_AARCH32
415