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