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
22using namespace llvm;
23
24static 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.
101static 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
114namespace {
115class SparcAsmBackend : public MCAsmBackend {
116protected:
117 bool Is64Bit;
118 bool IsV8Plus;
119
120public:
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
150class ELFSparcAsmBackend : public SparcAsmBackend {
151 Triple::OSType OSType;
152
153public:
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
165std::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
182MCFixupKindInfo 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
256void 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
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