1 //===-- M68kMCCodeEmitter.cpp - Convert M68k code emitter -------*- C++ -*-===//
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 /// \file
10 /// This file contains defintions for M68k code emitter.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "MCTargetDesc/M68kMCCodeEmitter.h"
15 #include "MCTargetDesc/M68kBaseInfo.h"
16 #include "MCTargetDesc/M68kFixupKinds.h"
17 #include "MCTargetDesc/M68kMCTargetDesc.h"
18
19 #include "llvm/MC/MCCodeEmitter.h"
20 #include "llvm/MC/MCContext.h"
21 #include "llvm/MC/MCExpr.h"
22 #include "llvm/MC/MCInst.h"
23 #include "llvm/MC/MCInstrInfo.h"
24 #include "llvm/MC/MCRegisterInfo.h"
25 #include "llvm/MC/MCSubtargetInfo.h"
26 #include "llvm/MC/MCSymbol.h"
27 #include "llvm/Support/Debug.h"
28 #include "llvm/Support/EndianStream.h"
29 #include "llvm/Support/raw_ostream.h"
30 #include <type_traits>
31
32 using namespace llvm;
33
34 #define DEBUG_TYPE "m68k-mccodeemitter"
35
36 namespace {
37 class M68kMCCodeEmitter : public MCCodeEmitter {
38 M68kMCCodeEmitter(const M68kMCCodeEmitter &) = delete;
39 void operator=(const M68kMCCodeEmitter &) = delete;
40 const MCInstrInfo &MCII;
41 MCContext &Ctx;
42
43 void getBinaryCodeForInstr(const MCInst &MI, SmallVectorImpl<MCFixup> &Fixups,
44 APInt &Inst, APInt &Scratch,
45 const MCSubtargetInfo &STI) const;
46
47 void getMachineOpValue(const MCInst &MI, const MCOperand &Op,
48 unsigned InsertPos, APInt &Value,
49 SmallVectorImpl<MCFixup> &Fixups,
50 const MCSubtargetInfo &STI) const;
51
52 template <unsigned Size>
53 void encodeRelocImm(const MCInst &MI, unsigned OpIdx, unsigned InsertPos,
54 APInt &Value, SmallVectorImpl<MCFixup> &Fixups,
55 const MCSubtargetInfo &STI) const;
56
57 template <unsigned Size>
58 void encodePCRelImm(const MCInst &MI, unsigned OpIdx, unsigned InsertPos,
59 APInt &Value, SmallVectorImpl<MCFixup> &Fixups,
60 const MCSubtargetInfo &STI) const;
61
62 void encodeFPSYSSelect(const MCInst &MI, unsigned OpIdx, unsigned InsertPos,
63 APInt &Value, SmallVectorImpl<MCFixup> &Fixups,
64 const MCSubtargetInfo &STI) const;
65
66 public:
M68kMCCodeEmitter(const MCInstrInfo & mcii,MCContext & ctx)67 M68kMCCodeEmitter(const MCInstrInfo &mcii, MCContext &ctx)
68 : MCII(mcii), Ctx(ctx) {}
69
~M68kMCCodeEmitter()70 ~M68kMCCodeEmitter() override {}
71
72 void encodeInstruction(const MCInst &MI, SmallVectorImpl<char> &CB,
73 SmallVectorImpl<MCFixup> &Fixups,
74 const MCSubtargetInfo &STI) const override;
75 };
76
77 } // end anonymous namespace
78
79 #include "M68kGenMCCodeEmitter.inc"
80
81 // Select the proper unsigned integer type from a bit size.
82 template <unsigned Size> struct select_uint_t {
83 using type = typename std::conditional<
84 Size == 8, uint8_t,
85 typename std::conditional<
86 Size == 16, uint16_t,
87 typename std::conditional<Size == 32, uint32_t,
88 uint64_t>::type>::type>::type;
89 };
90
91 // Figure out which byte we're at in big endian mode.
getBytePosition(unsigned BitPos)92 template <unsigned Size> static unsigned getBytePosition(unsigned BitPos) {
93 if (Size % 16) {
94 return static_cast<unsigned>(BitPos / 8 + ((BitPos & 0b1111) < 8 ? 1 : -1));
95 } else {
96 assert(!(BitPos & 0b1111) && "Not aligned to word boundary?");
97 return BitPos / 8;
98 }
99 }
100
101 // We need special handlings for relocatable & pc-relative operands that are
102 // larger than a word.
103 // A M68k instruction is aligned by word (16 bits). That means, 32-bit
104 // (& 64-bit) immediate values are separated into hi & lo words and placed
105 // at lower & higher addresses, respectively. For immediate values that can
106 // be easily expressed in TG, we explicitly rotate the word ordering like
107 // this:
108 // ```
109 // (ascend (slice "$imm", 31, 16), (slice "$imm", 15, 0))
110 // ```
111 // For operands that call into encoder functions, we need to use the `swapWord`
112 // function to assure the correct word ordering on LE host. Note that
113 // M68kMCCodeEmitter does massage _byte_ ordering of the final encoded
114 // instruction but it assumes everything aligns on word boundaries. So things
115 // will go wrong if we don't take care of the _word_ ordering here.
116 template <unsigned Size>
encodeRelocImm(const MCInst & MI,unsigned OpIdx,unsigned InsertPos,APInt & Value,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const117 void M68kMCCodeEmitter::encodeRelocImm(const MCInst &MI, unsigned OpIdx,
118 unsigned InsertPos, APInt &Value,
119 SmallVectorImpl<MCFixup> &Fixups,
120 const MCSubtargetInfo &STI) const {
121 using value_t = typename select_uint_t<Size>::type;
122 const MCOperand &MCO = MI.getOperand(OpIdx);
123 if (MCO.isImm()) {
124 Value |= M68k::swapWord<value_t>(static_cast<value_t>(MCO.getImm()));
125 } else if (MCO.isExpr()) {
126 const MCExpr *Expr = MCO.getExpr();
127
128 // Absolute address
129 int64_t Addr;
130 if (Expr->evaluateAsAbsolute(Addr)) {
131 Value |= M68k::swapWord<value_t>(static_cast<value_t>(Addr));
132 return;
133 }
134
135 // Relocatable address
136 unsigned InsertByte = getBytePosition<Size>(InsertPos);
137 Fixups.push_back(MCFixup::create(InsertByte, Expr,
138 getFixupForSize(Size, /*IsPCRel=*/false),
139 MI.getLoc()));
140 }
141 }
142
143 template <unsigned Size>
encodePCRelImm(const MCInst & MI,unsigned OpIdx,unsigned InsertPos,APInt & Value,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const144 void M68kMCCodeEmitter::encodePCRelImm(const MCInst &MI, unsigned OpIdx,
145 unsigned InsertPos, APInt &Value,
146 SmallVectorImpl<MCFixup> &Fixups,
147 const MCSubtargetInfo &STI) const {
148 const MCOperand &MCO = MI.getOperand(OpIdx);
149 if (MCO.isImm()) {
150 using value_t = typename select_uint_t<Size>::type;
151 Value |= M68k::swapWord<value_t>(static_cast<value_t>(MCO.getImm()));
152 } else if (MCO.isExpr()) {
153 const MCExpr *Expr = MCO.getExpr();
154 unsigned InsertByte = getBytePosition<Size>(InsertPos);
155
156 // Special handlings for sizes smaller than a word.
157 if (Size < 16) {
158 int LabelOffset = 0;
159 if (InsertPos < 16)
160 // If the patch point is at the first word, PC is pointing at the
161 // next word.
162 LabelOffset = InsertByte - 2;
163 else if (InsertByte % 2)
164 // Otherwise the PC is pointing at the first byte of this word.
165 // So we need to consider the offset between PC and the fixup byte.
166 LabelOffset = 1;
167
168 if (LabelOffset)
169 Expr = MCBinaryExpr::createAdd(
170 Expr, MCConstantExpr::create(LabelOffset, Ctx), Ctx);
171 }
172
173 Fixups.push_back(MCFixup::create(InsertByte, Expr,
174 getFixupForSize(Size, /*IsPCRel=*/true),
175 MI.getLoc()));
176 }
177 }
178
encodeFPSYSSelect(const MCInst & MI,unsigned OpIdx,unsigned InsertPos,APInt & Value,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const179 void M68kMCCodeEmitter::encodeFPSYSSelect(const MCInst &MI, unsigned OpIdx,
180 unsigned InsertPos, APInt &Value,
181 SmallVectorImpl<MCFixup> &Fixups,
182 const MCSubtargetInfo &STI) const {
183 MCRegister FPSysReg = MI.getOperand(OpIdx).getReg();
184 switch (FPSysReg) {
185 case M68k::FPC:
186 Value = 0b100;
187 break;
188 case M68k::FPS:
189 Value = 0b010;
190 break;
191 case M68k::FPIAR:
192 Value = 0b001;
193 break;
194 default:
195 llvm_unreachable("Unrecognized FPSYS register");
196 }
197 }
198
getMachineOpValue(const MCInst & MI,const MCOperand & Op,unsigned InsertPos,APInt & Value,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const199 void M68kMCCodeEmitter::getMachineOpValue(const MCInst &MI, const MCOperand &Op,
200 unsigned InsertPos, APInt &Value,
201 SmallVectorImpl<MCFixup> &Fixups,
202 const MCSubtargetInfo &STI) const {
203 // Register
204 if (Op.isReg()) {
205 unsigned RegNum = Op.getReg();
206 const auto *RI = Ctx.getRegisterInfo();
207 Value |= RI->getEncodingValue(RegNum);
208 // Setup the D/A bit
209 if (M68kII::isAddressRegister(RegNum))
210 Value |= 0b1000;
211 } else if (Op.isImm()) {
212 // Immediate
213 Value |= static_cast<uint64_t>(Op.getImm());
214 } else if (Op.isExpr()) {
215 // Absolute address
216 int64_t Addr;
217 if (!Op.getExpr()->evaluateAsAbsolute(Addr))
218 report_fatal_error("Unsupported asm expression. Only absolute address "
219 "can be placed here.");
220 Value |= static_cast<uint64_t>(Addr);
221 } else {
222 llvm_unreachable("Unsupported operand type");
223 }
224 }
225
encodeInstruction(const MCInst & MI,SmallVectorImpl<char> & CB,SmallVectorImpl<MCFixup> & Fixups,const MCSubtargetInfo & STI) const226 void M68kMCCodeEmitter::encodeInstruction(const MCInst &MI,
227 SmallVectorImpl<char> &CB,
228 SmallVectorImpl<MCFixup> &Fixups,
229 const MCSubtargetInfo &STI) const {
230 LLVM_DEBUG(dbgs() << "EncodeInstruction: " << MCII.getName(MI.getOpcode())
231 << "(" << MI.getOpcode() << ")\n");
232 (void)MCII;
233
234 // Try using the new method first.
235 APInt EncodedInst(16, 0U);
236 APInt Scratch(64, 0U); // One APInt word is enough.
237 getBinaryCodeForInstr(MI, Fixups, EncodedInst, Scratch, STI);
238
239 ArrayRef<uint64_t> Data(EncodedInst.getRawData(), EncodedInst.getNumWords());
240 int64_t InstSize = EncodedInst.getBitWidth();
241 for (uint64_t Word : Data) {
242 for (int i = 0; i < 4 && InstSize > 0; ++i, InstSize -= 16) {
243 support::endian::write<uint16_t>(CB, static_cast<uint16_t>(Word),
244 llvm::endianness::big);
245 Word >>= 16;
246 }
247 }
248 }
249
createM68kMCCodeEmitter(const MCInstrInfo & MCII,MCContext & Ctx)250 MCCodeEmitter *llvm::createM68kMCCodeEmitter(const MCInstrInfo &MCII,
251 MCContext &Ctx) {
252 return new M68kMCCodeEmitter(MCII, Ctx);
253 }
254