]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/llvm/lib/Target/WebAssembly/MCTargetDesc/WebAssemblyInstPrinter.cpp
MFC r355940:
[FreeBSD/FreeBSD.git] / contrib / llvm-project / llvm / lib / Target / WebAssembly / MCTargetDesc / WebAssemblyInstPrinter.cpp
1 //=- WebAssemblyInstPrinter.cpp - WebAssembly assembly instruction printing -=//
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 /// Print MCInst instructions to wasm format.
11 ///
12 //===----------------------------------------------------------------------===//
13
14 #include "MCTargetDesc/WebAssemblyInstPrinter.h"
15 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
16 #include "WebAssembly.h"
17 #include "WebAssemblyMachineFunctionInfo.h"
18 #include "llvm/ADT/SmallSet.h"
19 #include "llvm/ADT/StringExtras.h"
20 #include "llvm/CodeGen/TargetRegisterInfo.h"
21 #include "llvm/MC/MCExpr.h"
22 #include "llvm/MC/MCInst.h"
23 #include "llvm/MC/MCInstrInfo.h"
24 #include "llvm/MC/MCSubtargetInfo.h"
25 #include "llvm/MC/MCSymbol.h"
26 #include "llvm/Support/ErrorHandling.h"
27 #include "llvm/Support/FormattedStream.h"
28 using namespace llvm;
29
30 #define DEBUG_TYPE "asm-printer"
31
32 #include "WebAssemblyGenAsmWriter.inc"
33
34 WebAssemblyInstPrinter::WebAssemblyInstPrinter(const MCAsmInfo &MAI,
35                                                const MCInstrInfo &MII,
36                                                const MCRegisterInfo &MRI)
37     : MCInstPrinter(MAI, MII, MRI) {}
38
39 void WebAssemblyInstPrinter::printRegName(raw_ostream &OS,
40                                           unsigned RegNo) const {
41   assert(RegNo != WebAssemblyFunctionInfo::UnusedReg);
42   // Note that there's an implicit local.get/local.set here!
43   OS << "$" << RegNo;
44 }
45
46 void WebAssemblyInstPrinter::printInst(const MCInst *MI, raw_ostream &OS,
47                                        StringRef Annot,
48                                        const MCSubtargetInfo &STI) {
49   // Print the instruction (this uses the AsmStrings from the .td files).
50   printInstruction(MI, OS);
51
52   // Print any additional variadic operands.
53   const MCInstrDesc &Desc = MII.get(MI->getOpcode());
54   if (Desc.isVariadic())
55     for (auto I = Desc.getNumOperands(), E = MI->getNumOperands(); I < E; ++I) {
56       // FIXME: For CALL_INDIRECT_VOID, don't print a leading comma, because
57       // we have an extra flags operand which is not currently printed, for
58       // compatiblity reasons.
59       if (I != 0 && ((MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID &&
60                       MI->getOpcode() != WebAssembly::CALL_INDIRECT_VOID_S) ||
61                      I != Desc.getNumOperands()))
62         OS << ", ";
63       printOperand(MI, I, OS);
64     }
65
66   // Print any added annotation.
67   printAnnotation(OS, Annot);
68
69   if (CommentStream) {
70     // Observe any effects on the control flow stack, for use in annotating
71     // control flow label references.
72     unsigned Opc = MI->getOpcode();
73     switch (Opc) {
74     default:
75       break;
76
77     case WebAssembly::LOOP:
78     case WebAssembly::LOOP_S:
79       printAnnotation(OS, "label" + utostr(ControlFlowCounter) + ':');
80       ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, true));
81       break;
82
83     case WebAssembly::BLOCK:
84     case WebAssembly::BLOCK_S:
85       ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
86       break;
87
88     case WebAssembly::TRY:
89     case WebAssembly::TRY_S:
90       ControlFlowStack.push_back(std::make_pair(ControlFlowCounter++, false));
91       EHPadStack.push_back(EHPadStackCounter++);
92       LastSeenEHInst = TRY;
93       break;
94
95     case WebAssembly::END_LOOP:
96     case WebAssembly::END_LOOP_S:
97       if (ControlFlowStack.empty()) {
98         printAnnotation(OS, "End marker mismatch!");
99       } else {
100         ControlFlowStack.pop_back();
101       }
102       break;
103
104     case WebAssembly::END_BLOCK:
105     case WebAssembly::END_BLOCK_S:
106       if (ControlFlowStack.empty()) {
107         printAnnotation(OS, "End marker mismatch!");
108       } else {
109         printAnnotation(
110             OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
111       }
112       break;
113
114     case WebAssembly::END_TRY:
115     case WebAssembly::END_TRY_S:
116       if (ControlFlowStack.empty()) {
117         printAnnotation(OS, "End marker mismatch!");
118       } else {
119         printAnnotation(
120             OS, "label" + utostr(ControlFlowStack.pop_back_val().first) + ':');
121         LastSeenEHInst = END_TRY;
122       }
123       break;
124
125     case WebAssembly::CATCH:
126     case WebAssembly::CATCH_S:
127       if (EHPadStack.empty()) {
128         printAnnotation(OS, "try-catch mismatch!");
129       } else {
130         printAnnotation(OS, "catch" + utostr(EHPadStack.pop_back_val()) + ':');
131       }
132       break;
133     }
134
135     // Annotate any control flow label references.
136
137     // rethrow instruction does not take any depth argument and rethrows to the
138     // nearest enclosing catch scope, if any. If there's no enclosing catch
139     // scope, it throws up to the caller.
140     if (Opc == WebAssembly::RETHROW || Opc == WebAssembly::RETHROW_S) {
141       if (EHPadStack.empty()) {
142         printAnnotation(OS, "to caller");
143       } else {
144         printAnnotation(OS, "down to catch" + utostr(EHPadStack.back()));
145       }
146
147     } else {
148       unsigned NumFixedOperands = Desc.NumOperands;
149       SmallSet<uint64_t, 8> Printed;
150       for (unsigned I = 0, E = MI->getNumOperands(); I < E; ++I) {
151         // See if this operand denotes a basic block target.
152         if (I < NumFixedOperands) {
153           // A non-variable_ops operand, check its type.
154           if (Desc.OpInfo[I].OperandType != WebAssembly::OPERAND_BASIC_BLOCK)
155             continue;
156         } else {
157           // A variable_ops operand, which currently can be immediates (used in
158           // br_table) which are basic block targets, or for call instructions
159           // when using -wasm-keep-registers (in which case they are registers,
160           // and should not be processed).
161           if (!MI->getOperand(I).isImm())
162             continue;
163         }
164         uint64_t Depth = MI->getOperand(I).getImm();
165         if (!Printed.insert(Depth).second)
166           continue;
167         if (Depth >= ControlFlowStack.size()) {
168           printAnnotation(OS, "Invalid depth argument!");
169         } else {
170           const auto &Pair = ControlFlowStack.rbegin()[Depth];
171           printAnnotation(OS, utostr(Depth) + ": " +
172                                   (Pair.second ? "up" : "down") + " to label" +
173                                   utostr(Pair.first));
174         }
175       }
176     }
177   }
178 }
179
180 static std::string toString(const APFloat &FP) {
181   // Print NaNs with custom payloads specially.
182   if (FP.isNaN() && !FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) &&
183       !FP.bitwiseIsEqual(
184           APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) {
185     APInt AI = FP.bitcastToAPInt();
186     return std::string(AI.isNegative() ? "-" : "") + "nan:0x" +
187            utohexstr(AI.getZExtValue() &
188                          (AI.getBitWidth() == 32 ? INT64_C(0x007fffff)
189                                                  : INT64_C(0x000fffffffffffff)),
190                      /*LowerCase=*/true);
191   }
192
193   // Use C99's hexadecimal floating-point representation.
194   static const size_t BufBytes = 128;
195   char Buf[BufBytes];
196   auto Written = FP.convertToHexString(
197       Buf, /*HexDigits=*/0, /*UpperCase=*/false, APFloat::rmNearestTiesToEven);
198   (void)Written;
199   assert(Written != 0);
200   assert(Written < BufBytes);
201   return Buf;
202 }
203
204 void WebAssemblyInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
205                                           raw_ostream &O) {
206   const MCOperand &Op = MI->getOperand(OpNo);
207   if (Op.isReg()) {
208     unsigned WAReg = Op.getReg();
209     if (int(WAReg) >= 0)
210       printRegName(O, WAReg);
211     else if (OpNo >= MII.get(MI->getOpcode()).getNumDefs())
212       O << "$pop" << WebAssemblyFunctionInfo::getWARegStackId(WAReg);
213     else if (WAReg != WebAssemblyFunctionInfo::UnusedReg)
214       O << "$push" << WebAssemblyFunctionInfo::getWARegStackId(WAReg);
215     else
216       O << "$drop";
217     // Add a '=' suffix if this is a def.
218     if (OpNo < MII.get(MI->getOpcode()).getNumDefs())
219       O << '=';
220   } else if (Op.isImm()) {
221     O << Op.getImm();
222   } else if (Op.isFPImm()) {
223     const MCInstrDesc &Desc = MII.get(MI->getOpcode());
224     const MCOperandInfo &Info = Desc.OpInfo[OpNo];
225     if (Info.OperandType == WebAssembly::OPERAND_F32IMM) {
226       // TODO: MC converts all floating point immediate operands to double.
227       // This is fine for numeric values, but may cause NaNs to change bits.
228       O << ::toString(APFloat(float(Op.getFPImm())));
229     } else {
230       assert(Info.OperandType == WebAssembly::OPERAND_F64IMM);
231       O << ::toString(APFloat(Op.getFPImm()));
232     }
233   } else {
234     assert(Op.isExpr() && "unknown operand kind in printOperand");
235     Op.getExpr()->print(O, &MAI);
236   }
237 }
238
239 void WebAssemblyInstPrinter::printBrList(const MCInst *MI, unsigned OpNo,
240                                          raw_ostream &O) {
241   O << "{";
242   for (unsigned I = OpNo, E = MI->getNumOperands(); I != E; ++I) {
243     if (I != OpNo)
244       O << ", ";
245     O << MI->getOperand(I).getImm();
246   }
247   O << "}";
248 }
249
250 void WebAssemblyInstPrinter::printWebAssemblyP2AlignOperand(const MCInst *MI,
251                                                             unsigned OpNo,
252                                                             raw_ostream &O) {
253   int64_t Imm = MI->getOperand(OpNo).getImm();
254   if (Imm == WebAssembly::GetDefaultP2Align(MI->getOpcode()))
255     return;
256   O << ":p2align=" << Imm;
257 }
258
259 void WebAssemblyInstPrinter::printWebAssemblySignatureOperand(const MCInst *MI,
260                                                               unsigned OpNo,
261                                                               raw_ostream &O) {
262   auto Imm = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
263   if (Imm != wasm::WASM_TYPE_NORESULT)
264     O << WebAssembly::anyTypeToString(Imm);
265 }
266
267 // We have various enums representing a subset of these types, use this
268 // function to convert any of them to text.
269 const char *llvm::WebAssembly::anyTypeToString(unsigned Ty) {
270   switch (Ty) {
271   case wasm::WASM_TYPE_I32:
272     return "i32";
273   case wasm::WASM_TYPE_I64:
274     return "i64";
275   case wasm::WASM_TYPE_F32:
276     return "f32";
277   case wasm::WASM_TYPE_F64:
278     return "f64";
279   case wasm::WASM_TYPE_V128:
280     return "v128";
281   case wasm::WASM_TYPE_FUNCREF:
282     return "funcref";
283   case wasm::WASM_TYPE_FUNC:
284     return "func";
285   case wasm::WASM_TYPE_EXNREF:
286     return "exnref";
287   case wasm::WASM_TYPE_NORESULT:
288     return "void";
289   default:
290     return "invalid_type";
291   }
292 }
293
294 const char *llvm::WebAssembly::typeToString(wasm::ValType Ty) {
295   return anyTypeToString(static_cast<unsigned>(Ty));
296 }