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