1 //===-- MSP430AsmBackend.cpp - MSP430 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/MSP430FixupKinds.h"
10 #include "MCTargetDesc/MSP430MCTargetDesc.h"
11 #include "llvm/ADT/APInt.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/MCExpr.h"
17 #include "llvm/MC/MCObjectWriter.h"
18 #include "llvm/MC/MCSubtargetInfo.h"
19 #include "llvm/MC/MCSymbol.h"
20 #include "llvm/MC/MCTargetOptions.h"
21 #include "llvm/Support/ErrorHandling.h"
22 #include "llvm/Support/raw_ostream.h"
23
24 using namespace llvm;
25
26 namespace {
27 class MSP430AsmBackend : public MCAsmBackend {
28 uint8_t OSABI;
29
30 uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
31 MCContext &Ctx) const;
32
33 public:
MSP430AsmBackend(const MCSubtargetInfo & STI,uint8_t OSABI)34 MSP430AsmBackend(const MCSubtargetInfo &STI, uint8_t OSABI)
35 : MCAsmBackend(llvm::endianness::little), OSABI(OSABI) {}
36 ~MSP430AsmBackend() override = default;
37
38 void applyFixup(const MCFragment &, const MCFixup &, const MCValue &Target,
39 MutableArrayRef<char> Data, uint64_t Value,
40 bool IsResolved) override;
41
42 std::unique_ptr<MCObjectTargetWriter>
createObjectTargetWriter() const43 createObjectTargetWriter() const override {
44 return createMSP430ELFObjectWriter(OSABI);
45 }
46
getFixupKindInfo(MCFixupKind Kind) const47 MCFixupKindInfo getFixupKindInfo(MCFixupKind Kind) const override {
48 // clang-format off
49 const static MCFixupKindInfo Infos[MSP430::NumTargetFixupKinds] = {
50 // This table must be in the same order of enum in MSP430FixupKinds.h.
51 //
52 // name offset bits flags
53 {"fixup_32", 0, 32, 0},
54 {"fixup_10_pcrel", 0, 10, 0},
55 {"fixup_16", 0, 16, 0},
56 {"fixup_16_pcrel", 0, 16, 0},
57 {"fixup_16_byte", 0, 16, 0},
58 {"fixup_16_pcrel_byte", 0, 16, 0},
59 {"fixup_2x_pcrel", 0, 10, 0},
60 {"fixup_rl_pcrel", 0, 16, 0},
61 {"fixup_8", 0, 8, 0},
62 {"fixup_sym_diff", 0, 32, 0},
63 };
64 // clang-format on
65 static_assert((std::size(Infos)) == MSP430::NumTargetFixupKinds,
66 "Not all fixup kinds added to Infos array");
67
68 if (Kind < FirstTargetFixupKind)
69 return MCAsmBackend::getFixupKindInfo(Kind);
70
71 return Infos[Kind - FirstTargetFixupKind];
72 }
73
74 bool writeNopData(raw_ostream &OS, uint64_t Count,
75 const MCSubtargetInfo *STI) const override;
76 };
77
adjustFixupValue(const MCFixup & Fixup,uint64_t Value,MCContext & Ctx) const78 uint64_t MSP430AsmBackend::adjustFixupValue(const MCFixup &Fixup,
79 uint64_t Value,
80 MCContext &Ctx) const {
81 unsigned Kind = Fixup.getKind();
82 switch (Kind) {
83 case MSP430::fixup_10_pcrel: {
84 if (Value & 0x1)
85 Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned");
86
87 // Offset is signed
88 int16_t Offset = Value;
89 // Jumps are in words
90 Offset >>= 1;
91 // PC points to the next instruction so decrement by one
92 --Offset;
93
94 if (Offset < -512 || Offset > 511)
95 Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
96
97 // Mask 10 bits
98 Offset &= 0x3ff;
99
100 return Offset;
101 }
102 default:
103 return Value;
104 }
105 }
106
applyFixup(const MCFragment & F,const MCFixup & Fixup,const MCValue & Target,MutableArrayRef<char> Data,uint64_t Value,bool IsResolved)107 void MSP430AsmBackend::applyFixup(const MCFragment &F, const MCFixup &Fixup,
108 const MCValue &Target,
109 MutableArrayRef<char> Data, uint64_t Value,
110 bool IsResolved) {
111 maybeAddReloc(F, Fixup, Target, Value, IsResolved);
112 Value = adjustFixupValue(Fixup, Value, getContext());
113 MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());
114 if (!Value)
115 return; // Doesn't change encoding.
116
117 // Shift the value into position.
118 Value <<= Info.TargetOffset;
119
120 unsigned Offset = Fixup.getOffset();
121 unsigned NumBytes = alignTo(Info.TargetSize + Info.TargetOffset, 8) / 8;
122
123 assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!");
124
125 // For each byte of the fragment that the fixup touches, mask in the
126 // bits from the fixup value.
127 for (unsigned i = 0; i != NumBytes; ++i) {
128 Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
129 }
130 }
131
writeNopData(raw_ostream & OS,uint64_t Count,const MCSubtargetInfo * STI) const132 bool MSP430AsmBackend::writeNopData(raw_ostream &OS, uint64_t Count,
133 const MCSubtargetInfo *STI) const {
134 if ((Count % 2) != 0)
135 return false;
136
137 // The canonical nop on MSP430 is mov #0, r3
138 uint64_t NopCount = Count / 2;
139 while (NopCount--)
140 OS.write("\x03\x43", 2);
141
142 return true;
143 }
144
145 } // end anonymous namespace
146
createMSP430MCAsmBackend(const Target & T,const MCSubtargetInfo & STI,const MCRegisterInfo & MRI,const MCTargetOptions & Options)147 MCAsmBackend *llvm::createMSP430MCAsmBackend(const Target &T,
148 const MCSubtargetInfo &STI,
149 const MCRegisterInfo &MRI,
150 const MCTargetOptions &Options) {
151 return new MSP430AsmBackend(STI, ELF::ELFOSABI_STANDALONE);
152 }
153