1 | //===-- MipsInstPrinter.cpp - Convert Mips MCInst to assembly syntax ------===// |
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 | // This class prints an Mips MCInst to a .s file. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "MipsInstPrinter.h" |
14 | #include "Mips.h" |
15 | #include "MipsMCExpr.h" |
16 | #include "llvm/ADT/StringExtras.h" |
17 | #include "llvm/MC/MCExpr.h" |
18 | #include "llvm/MC/MCInst.h" |
19 | #include "llvm/MC/MCInstrInfo.h" |
20 | #include "llvm/MC/MCSubtargetInfo.h" |
21 | #include "llvm/MC/MCSymbol.h" |
22 | #include "llvm/Support/ErrorHandling.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | using namespace llvm; |
25 | |
26 | #define DEBUG_TYPE "asm-printer" |
27 | |
28 | #define PRINT_ALIAS_INSTR |
29 | #include "MipsGenAsmWriter.inc" |
30 | |
31 | template<unsigned R> |
32 | static bool isReg(const MCInst &MI, unsigned OpNo) { |
33 | assert(MI.getOperand(OpNo).isReg() && "Register operand expected." ); |
34 | return MI.getOperand(i: OpNo).getReg() == R; |
35 | } |
36 | |
37 | const char* Mips::MipsFCCToString(Mips::CondCode CC) { |
38 | switch (CC) { |
39 | case FCOND_F: |
40 | case FCOND_T: return "f" ; |
41 | case FCOND_UN: |
42 | case FCOND_OR: return "un" ; |
43 | case FCOND_OEQ: |
44 | case FCOND_UNE: return "eq" ; |
45 | case FCOND_UEQ: |
46 | case FCOND_ONE: return "ueq" ; |
47 | case FCOND_OLT: |
48 | case FCOND_UGE: return "olt" ; |
49 | case FCOND_ULT: |
50 | case FCOND_OGE: return "ult" ; |
51 | case FCOND_OLE: |
52 | case FCOND_UGT: return "ole" ; |
53 | case FCOND_ULE: |
54 | case FCOND_OGT: return "ule" ; |
55 | case FCOND_SF: |
56 | case FCOND_ST: return "sf" ; |
57 | case FCOND_NGLE: |
58 | case FCOND_GLE: return "ngle" ; |
59 | case FCOND_SEQ: |
60 | case FCOND_SNE: return "seq" ; |
61 | case FCOND_NGL: |
62 | case FCOND_GL: return "ngl" ; |
63 | case FCOND_LT: |
64 | case FCOND_NLT: return "lt" ; |
65 | case FCOND_NGE: |
66 | case FCOND_GE: return "nge" ; |
67 | case FCOND_LE: |
68 | case FCOND_NLE: return "le" ; |
69 | case FCOND_NGT: |
70 | case FCOND_GT: return "ngt" ; |
71 | } |
72 | llvm_unreachable("Impossible condition code!" ); |
73 | } |
74 | |
75 | void MipsInstPrinter::printRegName(raw_ostream &OS, MCRegister Reg) const { |
76 | markup(OS, M: Markup::Register) |
77 | << '$' << StringRef(getRegisterName(Reg)).lower(); |
78 | } |
79 | |
80 | void MipsInstPrinter::printInst(const MCInst *MI, uint64_t Address, |
81 | StringRef Annot, const MCSubtargetInfo &STI, |
82 | raw_ostream &O) { |
83 | switch (MI->getOpcode()) { |
84 | default: |
85 | break; |
86 | case Mips::RDHWR: |
87 | case Mips::RDHWR64: |
88 | O << "\t.set\tpush\n" ; |
89 | O << "\t.set\tmips32r2\n" ; |
90 | break; |
91 | case Mips::Save16: |
92 | O << "\tsave\t" ; |
93 | printSaveRestore(MI, STI, O); |
94 | O << " # 16 bit inst\n" ; |
95 | return; |
96 | case Mips::SaveX16: |
97 | O << "\tsave\t" ; |
98 | printSaveRestore(MI, STI, O); |
99 | O << "\n" ; |
100 | return; |
101 | case Mips::Restore16: |
102 | O << "\trestore\t" ; |
103 | printSaveRestore(MI, STI, O); |
104 | O << " # 16 bit inst\n" ; |
105 | return; |
106 | case Mips::RestoreX16: |
107 | O << "\trestore\t" ; |
108 | printSaveRestore(MI, STI, O); |
109 | O << "\n" ; |
110 | return; |
111 | } |
112 | |
113 | // Try to print any aliases first. |
114 | if (!printAliasInstr(MI, Address, STI, OS&: O) && |
115 | !printAlias(MI: *MI, Address, STI, OS&: O)) |
116 | printInstruction(MI, Address, STI, O); |
117 | printAnnotation(OS&: O, Annot); |
118 | |
119 | switch (MI->getOpcode()) { |
120 | default: |
121 | break; |
122 | case Mips::RDHWR: |
123 | case Mips::RDHWR64: |
124 | O << "\n\t.set\tpop" ; |
125 | } |
126 | } |
127 | |
128 | void MipsInstPrinter::printOperand(const MCInst *MI, unsigned OpNo, |
129 | const MCSubtargetInfo &STI, raw_ostream &O) { |
130 | const MCOperand &Op = MI->getOperand(i: OpNo); |
131 | if (Op.isReg()) { |
132 | printRegName(OS&: O, Reg: Op.getReg()); |
133 | return; |
134 | } |
135 | |
136 | if (Op.isImm()) { |
137 | markup(OS&: O, M: Markup::Immediate) << formatImm(Value: Op.getImm()); |
138 | return; |
139 | } |
140 | |
141 | assert(Op.isExpr() && "unknown operand kind in printOperand" ); |
142 | Op.getExpr()->print(OS&: O, MAI: &MAI, InParens: true); |
143 | } |
144 | |
145 | void MipsInstPrinter::printJumpOperand(const MCInst *MI, unsigned OpNo, |
146 | const MCSubtargetInfo &STI, |
147 | raw_ostream &O) { |
148 | const MCOperand &Op = MI->getOperand(i: OpNo); |
149 | if (!Op.isImm()) |
150 | return printOperand(MI, OpNo, STI, O); |
151 | |
152 | if (PrintBranchImmAsAddress) |
153 | markup(OS&: O, M: Markup::Immediate) << formatHex(Value: Op.getImm()); |
154 | else |
155 | markup(OS&: O, M: Markup::Immediate) << formatImm(Value: Op.getImm()); |
156 | } |
157 | |
158 | void MipsInstPrinter::printBranchOperand(const MCInst *MI, uint64_t Address, |
159 | unsigned OpNo, |
160 | const MCSubtargetInfo &STI, |
161 | raw_ostream &O) { |
162 | const MCOperand &Op = MI->getOperand(i: OpNo); |
163 | if (!Op.isImm()) |
164 | return printOperand(MI, OpNo, STI, O); |
165 | |
166 | if (PrintBranchImmAsAddress) { |
167 | uint64_t Target = Address + Op.getImm(); |
168 | if (STI.hasFeature(Feature: Mips::FeatureMips32)) |
169 | Target &= 0xffffffff; |
170 | else if (STI.hasFeature(Feature: Mips::FeatureMips16)) |
171 | Target &= 0xffff; |
172 | markup(OS&: O, M: Markup::Immediate) << formatHex(Value: Target); |
173 | } else { |
174 | markup(OS&: O, M: Markup::Immediate) << formatImm(Value: Op.getImm()); |
175 | } |
176 | } |
177 | |
178 | template <unsigned Bits, unsigned Offset> |
179 | void MipsInstPrinter::printUImm(const MCInst *MI, int opNum, |
180 | const MCSubtargetInfo &STI, raw_ostream &O) { |
181 | const MCOperand &MO = MI->getOperand(i: opNum); |
182 | if (MO.isImm()) { |
183 | uint64_t Imm = MO.getImm(); |
184 | Imm -= Offset; |
185 | Imm &= (1 << Bits) - 1; |
186 | Imm += Offset; |
187 | markup(OS&: O, M: Markup::Immediate) << formatImm(Value: Imm); |
188 | return; |
189 | } |
190 | |
191 | printOperand(MI, OpNo: opNum, STI, O); |
192 | } |
193 | |
194 | void MipsInstPrinter::printMemOperand(const MCInst *MI, int opNum, |
195 | const MCSubtargetInfo &STI, |
196 | raw_ostream &O) { |
197 | // Load/Store memory operands -- imm($reg) |
198 | // If PIC target the target is loaded as the |
199 | // pattern lw $25,%call16($28) |
200 | |
201 | // opNum can be invalid if instruction had reglist as operand. |
202 | // MemOperand is always last operand of instruction (base + offset). |
203 | switch (MI->getOpcode()) { |
204 | default: |
205 | break; |
206 | case Mips::SWM32_MM: |
207 | case Mips::LWM32_MM: |
208 | case Mips::SWM16_MM: |
209 | case Mips::SWM16_MMR6: |
210 | case Mips::LWM16_MM: |
211 | case Mips::LWM16_MMR6: |
212 | opNum = MI->getNumOperands() - 2; |
213 | break; |
214 | } |
215 | |
216 | WithMarkup M = markup(OS&: O, M: Markup::Memory); |
217 | printOperand(MI, OpNo: opNum + 1, STI, O); |
218 | O << "(" ; |
219 | printOperand(MI, OpNo: opNum, STI, O); |
220 | O << ")" ; |
221 | } |
222 | |
223 | void MipsInstPrinter::printMemOperandEA(const MCInst *MI, int opNum, |
224 | const MCSubtargetInfo &STI, |
225 | raw_ostream &O) { |
226 | // when using stack locations for not load/store instructions |
227 | // print the same way as all normal 3 operand instructions. |
228 | printOperand(MI, OpNo: opNum, STI, O); |
229 | O << ", " ; |
230 | printOperand(MI, OpNo: opNum + 1, STI, O); |
231 | } |
232 | |
233 | void MipsInstPrinter::printFCCOperand(const MCInst *MI, int opNum, |
234 | const MCSubtargetInfo & /* STI */, |
235 | raw_ostream &O) { |
236 | const MCOperand &MO = MI->getOperand(i: opNum); |
237 | O << MipsFCCToString(CC: (Mips::CondCode)MO.getImm()); |
238 | } |
239 | |
240 | void MipsInstPrinter:: |
241 | printSHFMask(const MCInst *MI, int opNum, raw_ostream &O) { |
242 | llvm_unreachable("TODO" ); |
243 | } |
244 | |
245 | bool MipsInstPrinter::printAlias(const char *Str, const MCInst &MI, |
246 | uint64_t Address, unsigned OpNo, |
247 | const MCSubtargetInfo &STI, raw_ostream &OS, |
248 | bool IsBranch) { |
249 | OS << "\t" << Str << "\t" ; |
250 | if (IsBranch) |
251 | printBranchOperand(MI: &MI, Address, OpNo, STI, O&: OS); |
252 | else |
253 | printOperand(MI: &MI, OpNo, STI, O&: OS); |
254 | return true; |
255 | } |
256 | |
257 | bool MipsInstPrinter::printAlias(const char *Str, const MCInst &MI, |
258 | uint64_t Address, unsigned OpNo0, |
259 | unsigned OpNo1, const MCSubtargetInfo &STI, |
260 | raw_ostream &OS, bool IsBranch) { |
261 | printAlias(Str, MI, Address, OpNo: OpNo0, STI, OS, IsBranch); |
262 | OS << ", " ; |
263 | if (IsBranch) |
264 | printBranchOperand(MI: &MI, Address, OpNo: OpNo1, STI, O&: OS); |
265 | else |
266 | printOperand(MI: &MI, OpNo: OpNo1, STI, O&: OS); |
267 | return true; |
268 | } |
269 | |
270 | bool MipsInstPrinter::printAlias(const MCInst &MI, uint64_t Address, |
271 | const MCSubtargetInfo &STI, raw_ostream &OS) { |
272 | switch (MI.getOpcode()) { |
273 | case Mips::BEQ: |
274 | case Mips::BEQ_MM: |
275 | // beq $zero, $zero, $L2 => b $L2 |
276 | // beq $r0, $zero, $L2 => beqz $r0, $L2 |
277 | return (isReg<Mips::ZERO>(MI, OpNo: 0) && isReg<Mips::ZERO>(MI, OpNo: 1) && |
278 | printAlias(Str: "b" , MI, Address, OpNo: 2, STI, OS, IsBranch: true)) || |
279 | (isReg<Mips::ZERO>(MI, OpNo: 1) && |
280 | printAlias(Str: "beqz" , MI, Address, OpNo0: 0, OpNo1: 2, STI, OS, IsBranch: true)); |
281 | case Mips::BEQ64: |
282 | // beq $r0, $zero, $L2 => beqz $r0, $L2 |
283 | return isReg<Mips::ZERO_64>(MI, OpNo: 1) && |
284 | printAlias(Str: "beqz" , MI, Address, OpNo0: 0, OpNo1: 2, STI, OS, IsBranch: true); |
285 | case Mips::BNE: |
286 | case Mips::BNE_MM: |
287 | // bne $r0, $zero, $L2 => bnez $r0, $L2 |
288 | return isReg<Mips::ZERO>(MI, OpNo: 1) && |
289 | printAlias(Str: "bnez" , MI, Address, OpNo0: 0, OpNo1: 2, STI, OS, IsBranch: true); |
290 | case Mips::BNE64: |
291 | // bne $r0, $zero, $L2 => bnez $r0, $L2 |
292 | return isReg<Mips::ZERO_64>(MI, OpNo: 1) && |
293 | printAlias(Str: "bnez" , MI, Address, OpNo0: 0, OpNo1: 2, STI, OS, IsBranch: true); |
294 | case Mips::BGEZAL: |
295 | // bgezal $zero, $L1 => bal $L1 |
296 | return isReg<Mips::ZERO>(MI, OpNo: 0) && |
297 | printAlias(Str: "bal" , MI, Address, OpNo: 1, STI, OS, IsBranch: true); |
298 | case Mips::BC1T: |
299 | // bc1t $fcc0, $L1 => bc1t $L1 |
300 | return isReg<Mips::FCC0>(MI, OpNo: 0) && |
301 | printAlias(Str: "bc1t" , MI, Address, OpNo: 1, STI, OS, IsBranch: true); |
302 | case Mips::BC1F: |
303 | // bc1f $fcc0, $L1 => bc1f $L1 |
304 | return isReg<Mips::FCC0>(MI, OpNo: 0) && |
305 | printAlias(Str: "bc1f" , MI, Address, OpNo: 1, STI, OS, IsBranch: true); |
306 | case Mips::JALR: |
307 | // jalr $zero, $r1 => jr $r1 |
308 | // jalr $ra, $r1 => jalr $r1 |
309 | return (isReg<Mips::ZERO>(MI, OpNo: 0) && |
310 | printAlias(Str: "jr" , MI, Address, OpNo: 1, STI, OS)) || |
311 | (isReg<Mips::RA>(MI, OpNo: 0) && |
312 | printAlias(Str: "jalr" , MI, Address, OpNo: 1, STI, OS)); |
313 | case Mips::JALR64: |
314 | // jalr $zero, $r1 => jr $r1 |
315 | // jalr $ra, $r1 => jalr $r1 |
316 | return (isReg<Mips::ZERO_64>(MI, OpNo: 0) && |
317 | printAlias(Str: "jr" , MI, Address, OpNo: 1, STI, OS)) || |
318 | (isReg<Mips::RA_64>(MI, OpNo: 0) && |
319 | printAlias(Str: "jalr" , MI, Address, OpNo: 1, STI, OS)); |
320 | case Mips::NOR: |
321 | case Mips::NOR_MM: |
322 | case Mips::NOR_MMR6: |
323 | // nor $r0, $r1, $zero => not $r0, $r1 |
324 | return isReg<Mips::ZERO>(MI, OpNo: 2) && |
325 | printAlias(Str: "not" , MI, Address, OpNo0: 0, OpNo1: 1, STI, OS); |
326 | case Mips::NOR64: |
327 | // nor $r0, $r1, $zero => not $r0, $r1 |
328 | return isReg<Mips::ZERO_64>(MI, OpNo: 2) && |
329 | printAlias(Str: "not" , MI, Address, OpNo0: 0, OpNo1: 1, STI, OS); |
330 | case Mips::OR: |
331 | case Mips::ADDu: |
332 | // or $r0, $r1, $zero => move $r0, $r1 |
333 | // addu $r0, $r1, $zero => move $r0, $r1 |
334 | return isReg<Mips::ZERO>(MI, OpNo: 2) && |
335 | printAlias(Str: "move" , MI, Address, OpNo0: 0, OpNo1: 1, STI, OS); |
336 | default: |
337 | return false; |
338 | } |
339 | } |
340 | |
341 | void MipsInstPrinter::printSaveRestore(const MCInst *MI, |
342 | const MCSubtargetInfo &STI, |
343 | raw_ostream &O) { |
344 | for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) { |
345 | if (i != 0) O << ", " ; |
346 | if (MI->getOperand(i).isReg()) |
347 | printRegName(OS&: O, Reg: MI->getOperand(i).getReg()); |
348 | else |
349 | printUImm<16>(MI, opNum: i, STI, O); |
350 | } |
351 | } |
352 | |
353 | void MipsInstPrinter::printRegisterList(const MCInst *MI, int opNum, |
354 | const MCSubtargetInfo & /* STI */, |
355 | raw_ostream &O) { |
356 | // - 2 because register List is always first operand of instruction and it is |
357 | // always followed by memory operand (base + offset). |
358 | for (int i = opNum, e = MI->getNumOperands() - 2; i != e; ++i) { |
359 | if (i != opNum) |
360 | O << ", " ; |
361 | printRegName(OS&: O, Reg: MI->getOperand(i).getReg()); |
362 | } |
363 | } |
364 | |