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 } 297