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