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