1//===-- SystemZMCAsmBackend.cpp - SystemZ 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/SystemZMCFixups.h"
10#include "MCTargetDesc/SystemZMCTargetDesc.h"
11#include "llvm/ADT/StringSwitch.h"
12#include "llvm/MC/MCAsmBackend.h"
13#include "llvm/MC/MCAssembler.h"
14#include "llvm/MC/MCContext.h"
15#include "llvm/MC/MCELFObjectWriter.h"
16#include "llvm/MC/MCFixupKindInfo.h"
17#include "llvm/MC/MCInst.h"
18#include "llvm/MC/MCObjectWriter.h"
19#include "llvm/MC/MCSubtargetInfo.h"
20#include "llvm/MC/MCValue.h"
21
22using namespace llvm;
23
24// Value is a fully-resolved relocation value: Symbol + Addend [- Pivot].
25// Return the bits that should be installed in a relocation field for
26// fixup kind Kind.
27static uint64_t extractBitsForFixup(MCFixupKind Kind, uint64_t Value,
28 const MCFixup &Fixup, MCContext &Ctx) {
29 if (Kind < FirstTargetFixupKind)
30 return Value;
31
32 auto checkFixupInRange = [&](int64_t Min, int64_t Max) -> bool {
33 int64_t SVal = int64_t(Value);
34 if (SVal < Min || SVal > Max) {
35 Ctx.reportError(L: Fixup.getLoc(), Msg: "operand out of range (" + Twine(SVal) +
36 " not between " + Twine(Min) +
37 " and " + Twine(Max) + ")");
38 return false;
39 }
40 return true;
41 };
42
43 auto handlePCRelFixupValue = [&](unsigned W) -> uint64_t {
44 if (Value % 2 != 0)
45 Ctx.reportError(L: Fixup.getLoc(), Msg: "Non-even PC relative offset.");
46 if (!checkFixupInRange(minIntN(N: W) * 2, maxIntN(N: W) * 2))
47 return 0;
48 return (int64_t)Value / 2;
49 };
50
51 auto handleImmValue = [&](bool IsSigned, unsigned W) -> uint64_t {
52 if (!(IsSigned ? checkFixupInRange(minIntN(N: W), maxIntN(N: W))
53 : checkFixupInRange(0, maxUIntN(N: W))))
54 return 0;
55 return Value;
56 };
57
58 switch (unsigned(Kind)) {
59 case SystemZ::FK_390_PC12DBL:
60 return handlePCRelFixupValue(12);
61 case SystemZ::FK_390_PC16DBL:
62 return handlePCRelFixupValue(16);
63 case SystemZ::FK_390_PC24DBL:
64 return handlePCRelFixupValue(24);
65 case SystemZ::FK_390_PC32DBL:
66 return handlePCRelFixupValue(32);
67
68 case SystemZ::FK_390_TLS_CALL:
69 return 0;
70
71 case SystemZ::FK_390_S8Imm:
72 return handleImmValue(true, 8);
73 case SystemZ::FK_390_S16Imm:
74 return handleImmValue(true, 16);
75 case SystemZ::FK_390_S20Imm: {
76 Value = handleImmValue(true, 20);
77 // S20Imm is used only for signed 20-bit displacements.
78 // The high byte of a 20 bit displacement value comes first.
79 uint64_t DLo = Value & 0xfff;
80 uint64_t DHi = (Value >> 12) & 0xff;
81 return (DLo << 8) | DHi;
82 }
83 case SystemZ::FK_390_S32Imm:
84 return handleImmValue(true, 32);
85 case SystemZ::FK_390_U1Imm:
86 return handleImmValue(false, 1);
87 case SystemZ::FK_390_U2Imm:
88 return handleImmValue(false, 2);
89 case SystemZ::FK_390_U3Imm:
90 return handleImmValue(false, 3);
91 case SystemZ::FK_390_U4Imm:
92 return handleImmValue(false, 4);
93 case SystemZ::FK_390_U8Imm:
94 return handleImmValue(false, 8);
95 case SystemZ::FK_390_U12Imm:
96 return handleImmValue(false, 12);
97 case SystemZ::FK_390_U16Imm:
98 return handleImmValue(false, 16);
99 case SystemZ::FK_390_U32Imm:
100 return handleImmValue(false, 32);
101 case SystemZ::FK_390_U48Imm:
102 return handleImmValue(false, 48);
103 }
104
105 llvm_unreachable("Unknown fixup kind!");
106}
107
108namespace {
109class SystemZMCAsmBackend : public MCAsmBackend {
110public:
111 SystemZMCAsmBackend() : MCAsmBackend(llvm::endianness::big) {}
112
113 // Override MCAsmBackend
114 std::optional<MCFixupKind> getFixupKind(StringRef Name) const override;
115 MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override;
116 void applyFixup(const MCFragment &, const MCFixup &, const MCValue &Target,
117 MutableArrayRef<char> Data, uint64_t Value,
118 bool IsResolved) override;
119 bool writeNopData(raw_ostream &OS, uint64_t Count,
120 const MCSubtargetInfo *STI) const override;
121};
122} // end anonymous namespace
123
124std::optional<MCFixupKind>
125SystemZMCAsmBackend::getFixupKind(StringRef Name) const {
126 unsigned Type = llvm::StringSwitch<unsigned>(Name)
127#define ELF_RELOC(X, Y) .Case(#X, Y)
128#include "llvm/BinaryFormat/ELFRelocs/SystemZ.def"
129#undef ELF_RELOC
130 .Case(S: "BFD_RELOC_NONE", Value: ELF::R_390_NONE)
131 .Case(S: "BFD_RELOC_8", Value: ELF::R_390_8)
132 .Case(S: "BFD_RELOC_16", Value: ELF::R_390_16)
133 .Case(S: "BFD_RELOC_32", Value: ELF::R_390_32)
134 .Case(S: "BFD_RELOC_64", Value: ELF::R_390_64)
135 .Default(Value: -1u);
136 if (Type != -1u)
137 return static_cast<MCFixupKind>(FirstLiteralRelocationKind + Type);
138 return std::nullopt;
139}
140
141MCFixupKindInfo SystemZMCAsmBackend::getFixupKindInfo(MCFixupKind Kind) const {
142 // Fixup kinds from .reloc directive are like R_390_NONE. They
143 // do not require any extra processing.
144 if (mc::isRelocation(FixupKind: Kind))
145 return MCAsmBackend::getFixupKindInfo(Kind: FK_NONE);
146
147 if (Kind < FirstTargetFixupKind)
148 return MCAsmBackend::getFixupKindInfo(Kind);
149
150 assert(unsigned(Kind - FirstTargetFixupKind) < SystemZ::NumTargetFixupKinds &&
151 "Invalid kind!");
152 return SystemZ::MCFixupKindInfos[Kind - FirstTargetFixupKind];
153}
154
155void SystemZMCAsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
156 const MCValue &Target,
157 MutableArrayRef<char> Data, uint64_t Value,
158 bool IsResolved) {
159 if (Target.getSpecifier())
160 IsResolved = false;
161 maybeAddReloc(F, Fixup, Target, Value, IsResolved);
162 MCFixupKind Kind = Fixup.getKind();
163 if (mc::isRelocation(FixupKind: Kind))
164 return;
165 unsigned Offset = Fixup.getOffset();
166 unsigned BitSize = getFixupKindInfo(Kind).TargetSize;
167 unsigned Size = (BitSize + 7) / 8;
168
169 assert(Offset + Size <= Data.size() && "Invalid fixup offset!");
170
171 // Big-endian insertion of Size bytes.
172 Value = extractBitsForFixup(Kind, Value, Fixup, Ctx&: getContext());
173 if (BitSize < 64)
174 Value &= ((uint64_t)1 << BitSize) - 1;
175 unsigned ShiftValue = (Size * 8) - 8;
176 for (unsigned I = 0; I != Size; ++I) {
177 Data[Offset + I] |= uint8_t(Value >> ShiftValue);
178 ShiftValue -= 8;
179 }
180}
181
182bool SystemZMCAsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
183 const MCSubtargetInfo *STI) const {
184 for (uint64_t I = 0; I != Count; ++I)
185 OS << '\x7';
186 return true;
187}
188
189namespace {
190class ELFSystemZAsmBackend : public SystemZMCAsmBackend {
191 uint8_t OSABI;
192
193public:
194 ELFSystemZAsmBackend(uint8_t OsABI) : SystemZMCAsmBackend(), OSABI(OsABI){};
195
196 std::unique_ptr<MCObjectTargetWriter>
197 createObjectTargetWriter() const override {
198 return createSystemZELFObjectWriter(OSABI);
199 }
200};
201
202class GOFFSystemZAsmBackend : public SystemZMCAsmBackend {
203public:
204 GOFFSystemZAsmBackend() : SystemZMCAsmBackend(){};
205
206 std::unique_ptr<MCObjectTargetWriter>
207 createObjectTargetWriter() const override {
208 return createSystemZGOFFObjectWriter();
209 }
210};
211} // namespace
212
213MCAsmBackend *llvm::createSystemZMCAsmBackend(const Target &T,
214 const MCSubtargetInfo &STI,
215 const MCRegisterInfo &MRI,
216 const MCTargetOptions &Options) {
217 if (STI.getTargetTriple().isOSzOS()) {
218 return new GOFFSystemZAsmBackend();
219 }
220
221 uint8_t OSABI =
222 MCELFObjectTargetWriter::getOSABI(OSType: STI.getTargetTriple().getOS());
223 return new ELFSystemZAsmBackend(OSABI);
224}
225