1 | //===-- SparcAsmBackend.cpp - Sparc Assembler Backend ---------------------===// |
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 "MCTargetDesc/SparcFixupKinds.h" |
10 | #include "MCTargetDesc/SparcMCTargetDesc.h" |
11 | #include "llvm/ADT/StringSwitch.h" |
12 | #include "llvm/MC/MCAsmBackend.h" |
13 | #include "llvm/MC/MCELFObjectWriter.h" |
14 | #include "llvm/MC/MCExpr.h" |
15 | #include "llvm/MC/MCFixupKindInfo.h" |
16 | #include "llvm/MC/MCObjectWriter.h" |
17 | #include "llvm/MC/MCSubtargetInfo.h" |
18 | #include "llvm/MC/MCValue.h" |
19 | #include "llvm/MC/TargetRegistry.h" |
20 | #include "llvm/Support/EndianStream.h" |
21 | |
22 | using namespace llvm; |
23 | |
24 | static unsigned adjustFixupValue(unsigned Kind, uint64_t Value) { |
25 | switch (Kind) { |
26 | default: |
27 | assert(uint16_t(Kind) < FirstTargetFixupKind && "Unknown fixup kind!" ); |
28 | return Value; |
29 | case FK_Data_1: |
30 | case FK_Data_2: |
31 | case FK_Data_4: |
32 | case FK_Data_8: |
33 | return Value; |
34 | |
35 | case Sparc::fixup_sparc_call30: |
36 | return (Value >> 2) & 0x3fffffff; |
37 | |
38 | case ELF::R_SPARC_WDISP22: |
39 | return (Value >> 2) & 0x3fffff; |
40 | |
41 | case ELF::R_SPARC_WDISP19: |
42 | return (Value >> 2) & 0x7ffff; |
43 | |
44 | case ELF::R_SPARC_WDISP16: { |
45 | // A.3 Branch on Integer Register with Prediction (BPr) |
46 | // Inst{21-20} = d16hi; |
47 | // Inst{13-0} = d16lo; |
48 | unsigned d16hi = (Value >> 16) & 0x3; |
49 | unsigned d16lo = (Value >> 2) & 0x3fff; |
50 | return (d16hi << 20) | d16lo; |
51 | } |
52 | |
53 | case ELF::R_SPARC_WDISP10: { |
54 | // FIXME this really should be an error reporting check. |
55 | assert((Value & 0x3) == 0); |
56 | |
57 | // 7.17 Compare and Branch |
58 | // Inst{20-19} = d10hi; |
59 | // Inst{12-5} = d10lo; |
60 | unsigned d10hi = (Value >> 10) & 0x3; |
61 | unsigned d10lo = (Value >> 2) & 0xff; |
62 | return (d10hi << 19) | (d10lo << 5); |
63 | } |
64 | |
65 | case ELF::R_SPARC_HIX22: |
66 | return (~Value >> 10) & 0x3fffff; |
67 | |
68 | case ELF::R_SPARC_PC22: |
69 | case ELF::R_SPARC_HI22: |
70 | case ELF::R_SPARC_LM22: |
71 | return (Value >> 10) & 0x3fffff; |
72 | |
73 | case Sparc::fixup_sparc_13: |
74 | return Value & 0x1fff; |
75 | |
76 | case ELF::R_SPARC_5: |
77 | return Value & 0x1f; |
78 | |
79 | case ELF::R_SPARC_LOX10: |
80 | return (Value & 0x3ff) | 0x1c00; |
81 | |
82 | case ELF::R_SPARC_PC10: |
83 | case ELF::R_SPARC_LO10: |
84 | return Value & 0x3ff; |
85 | |
86 | case ELF::R_SPARC_H44: |
87 | return (Value >> 22) & 0x3fffff; |
88 | case ELF::R_SPARC_M44: |
89 | return (Value >> 12) & 0x3ff; |
90 | case ELF::R_SPARC_L44: |
91 | return Value & 0xfff; |
92 | |
93 | case ELF::R_SPARC_HH22: |
94 | return (Value >> 42) & 0x3fffff; |
95 | case ELF::R_SPARC_HM10: |
96 | return (Value >> 32) & 0x3ff; |
97 | } |
98 | } |
99 | |
100 | /// getFixupKindNumBytes - The number of bytes the fixup may change. |
101 | static unsigned getFixupKindNumBytes(unsigned Kind) { |
102 | switch (Kind) { |
103 | default: |
104 | return 4; |
105 | case FK_Data_1: |
106 | return 1; |
107 | case FK_Data_2: |
108 | return 2; |
109 | case FK_Data_8: |
110 | return 8; |
111 | } |
112 | } |
113 | |
114 | namespace { |
115 | class SparcAsmBackend : public MCAsmBackend { |
116 | protected: |
117 | bool Is64Bit; |
118 | bool IsV8Plus; |
119 | |
120 | public: |
121 | SparcAsmBackend(const MCSubtargetInfo &STI) |
122 | : MCAsmBackend(STI.getTargetTriple().isLittleEndian() |
123 | ? llvm::endianness::little |
124 | : llvm::endianness::big), |
125 | Is64Bit(STI.getTargetTriple().isArch64Bit()), |
126 | IsV8Plus(STI.hasFeature(Feature: Sparc::FeatureV8Plus)) {} |
127 | |
128 | std::optional<MCFixupKind> getFixupKind(StringRef Name) const override; |
129 | MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override; |
130 | void applyFixup(const MCFragment &, const MCFixup &, const MCValue &Target, |
131 | MutableArrayRef<char> Data, uint64_t Value, |
132 | bool IsResolved) override; |
133 | |
134 | bool writeNopData(raw_ostream &OS, uint64_t Count, |
135 | const MCSubtargetInfo *STI) const override { |
136 | |
137 | // If the count is not 4-byte aligned, we must be writing data into the |
138 | // text section (otherwise we have unaligned instructions, and thus have |
139 | // far bigger problems), so just write zeros instead. |
140 | OS.write_zeros(NumZeros: Count % 4); |
141 | |
142 | uint64_t NumNops = Count / 4; |
143 | for (uint64_t i = 0; i != NumNops; ++i) |
144 | support::endian::write<uint32_t>(os&: OS, value: 0x01000000, endian: Endian); |
145 | |
146 | return true; |
147 | } |
148 | }; |
149 | |
150 | class ELFSparcAsmBackend : public SparcAsmBackend { |
151 | Triple::OSType OSType; |
152 | |
153 | public: |
154 | ELFSparcAsmBackend(const MCSubtargetInfo &STI, Triple::OSType OSType) |
155 | : SparcAsmBackend(STI), OSType(OSType) {} |
156 | |
157 | std::unique_ptr<MCObjectTargetWriter> |
158 | createObjectTargetWriter() const override { |
159 | uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(OSType); |
160 | return createSparcELFObjectWriter(Is64Bit, IsV8Plus, OSABI); |
161 | } |
162 | }; |
163 | } // end anonymous namespace |
164 | |
165 | std::optional<MCFixupKind> SparcAsmBackend::getFixupKind(StringRef Name) const { |
166 | unsigned Type; |
167 | Type = llvm::StringSwitch<unsigned>(Name) |
168 | #define ELF_RELOC(X, Y) .Case(#X, Y) |
169 | #include "llvm/BinaryFormat/ELFRelocs/Sparc.def" |
170 | #undef ELF_RELOC |
171 | .Case(S: "BFD_RELOC_NONE" , Value: ELF::R_SPARC_NONE) |
172 | .Case(S: "BFD_RELOC_8" , Value: ELF::R_SPARC_8) |
173 | .Case(S: "BFD_RELOC_16" , Value: ELF::R_SPARC_16) |
174 | .Case(S: "BFD_RELOC_32" , Value: ELF::R_SPARC_32) |
175 | .Case(S: "BFD_RELOC_64" , Value: ELF::R_SPARC_64) |
176 | .Default(Value: -1u); |
177 | if (Type == -1u) |
178 | return std::nullopt; |
179 | return static_cast<MCFixupKind>(FirstLiteralRelocationKind + Type); |
180 | } |
181 | |
182 | MCFixupKindInfo SparcAsmBackend::getFixupKindInfo(MCFixupKind Kind) const { |
183 | // clang-format off |
184 | const static MCFixupKindInfo InfosBE[Sparc::NumTargetFixupKinds] = { |
185 | // name offset bits flags |
186 | { .Name: "fixup_sparc_call30" , .TargetOffset: 2, .TargetSize: 30, .Flags: MCFixupKindInfo::FKF_IsPCRel }, |
187 | { .Name: "fixup_sparc_13" , .TargetOffset: 19, .TargetSize: 13, .Flags: 0 }, |
188 | }; |
189 | |
190 | const static MCFixupKindInfo InfosLE[Sparc::NumTargetFixupKinds] = { |
191 | // name offset bits flags |
192 | { .Name: "fixup_sparc_call30" , .TargetOffset: 0, .TargetSize: 30, .Flags: MCFixupKindInfo::FKF_IsPCRel }, |
193 | { .Name: "fixup_sparc_13" , .TargetOffset: 0, .TargetSize: 13, .Flags: 0 }, |
194 | }; |
195 | // clang-format on |
196 | |
197 | if (!mc::isRelocation(FixupKind: Kind)) { |
198 | if (Kind < FirstTargetFixupKind) |
199 | return MCAsmBackend::getFixupKindInfo(Kind); |
200 | assert(unsigned(Kind - FirstTargetFixupKind) < Sparc::NumTargetFixupKinds && |
201 | "Invalid kind!" ); |
202 | if (Endian == llvm::endianness::little) |
203 | return InfosLE[Kind - FirstTargetFixupKind]; |
204 | |
205 | return InfosBE[Kind - FirstTargetFixupKind]; |
206 | } |
207 | |
208 | MCFixupKindInfo Info{}; |
209 | switch (uint16_t(Kind)) { |
210 | case ELF::R_SPARC_PC10: |
211 | Info = {.Name: "" , .TargetOffset: 22, .TargetSize: 10, .Flags: MCFixupKindInfo::FKF_IsPCRel}; |
212 | break; |
213 | case ELF::R_SPARC_PC22: |
214 | Info = {.Name: "" , .TargetOffset: 10, .TargetSize: 22, .Flags: MCFixupKindInfo::FKF_IsPCRel}; |
215 | break; |
216 | case ELF::R_SPARC_WDISP10: |
217 | Info = {.Name: "" , .TargetOffset: 0, .TargetSize: 32, .Flags: MCFixupKindInfo::FKF_IsPCRel}; |
218 | break; |
219 | case ELF::R_SPARC_WDISP16: |
220 | Info = {.Name: "" , .TargetOffset: 0, .TargetSize: 32, .Flags: MCFixupKindInfo::FKF_IsPCRel}; |
221 | break; |
222 | case ELF::R_SPARC_WDISP19: |
223 | Info = {.Name: "" , .TargetOffset: 13, .TargetSize: 19, .Flags: MCFixupKindInfo::FKF_IsPCRel}; |
224 | break; |
225 | case ELF::R_SPARC_WDISP22: |
226 | Info = {.Name: "" , .TargetOffset: 10, .TargetSize: 22, .Flags: MCFixupKindInfo::FKF_IsPCRel}; |
227 | break; |
228 | |
229 | case ELF::R_SPARC_HI22: |
230 | Info = {.Name: "" , .TargetOffset: 10, .TargetSize: 22, .Flags: 0}; |
231 | break; |
232 | case ELF::R_SPARC_LO10: |
233 | Info = {.Name: "" , .TargetOffset: 22, .TargetSize: 10, .Flags: 0}; |
234 | break; |
235 | case ELF::R_SPARC_HH22: |
236 | Info = {.Name: "" , .TargetOffset: 10, .TargetSize: 22, .Flags: 0}; |
237 | break; |
238 | case ELF::R_SPARC_HM10: |
239 | Info = {.Name: "" , .TargetOffset: 22, .TargetSize: 10, .Flags: 0}; |
240 | break; |
241 | case ELF::R_SPARC_LM22: |
242 | Info = {.Name: "" , .TargetOffset: 10, .TargetSize: 22, .Flags: 0}; |
243 | break; |
244 | case ELF::R_SPARC_HIX22: |
245 | Info = {.Name: "" , .TargetOffset: 10, .TargetSize: 22, .Flags: 0}; |
246 | break; |
247 | case ELF::R_SPARC_LOX10: |
248 | Info = {.Name: "" , .TargetOffset: 19, .TargetSize: 13, .Flags: 0}; |
249 | break; |
250 | } |
251 | if (Endian == llvm::endianness::little) |
252 | Info.TargetOffset = 32 - Info.TargetOffset - Info.TargetSize; |
253 | return Info; |
254 | } |
255 | |
256 | void SparcAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup, |
257 | const MCValue &Target, |
258 | MutableArrayRef<char> Data, uint64_t Value, |
259 | bool IsResolved) { |
260 | maybeAddReloc(F, Fixup, Target, Value, IsResolved); |
261 | if (!IsResolved) |
262 | return; |
263 | Value = adjustFixupValue(Kind: Fixup.getKind(), Value); |
264 | |
265 | unsigned NumBytes = getFixupKindNumBytes(Kind: Fixup.getKind()); |
266 | unsigned Offset = Fixup.getOffset(); |
267 | // For each byte of the fragment that the fixup touches, mask in the |
268 | // bits from the fixup value. |
269 | for (unsigned i = 0; i != NumBytes; ++i) { |
270 | unsigned Idx = Endian == llvm::endianness::little ? i : (NumBytes - 1) - i; |
271 | Data[Offset + Idx] |= uint8_t((Value >> (i * 8)) & 0xff); |
272 | } |
273 | } |
274 | |
275 | MCAsmBackend *llvm::createSparcAsmBackend(const Target &T, |
276 | const MCSubtargetInfo &STI, |
277 | const MCRegisterInfo &MRI, |
278 | const MCTargetOptions &Options) { |
279 | return new ELFSparcAsmBackend(STI, STI.getTargetTriple().getOS()); |
280 | } |
281 | |