1 //==- WebAssemblyAsmTypeCheck.cpp - Assembler for WebAssembly -*- 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 is part of the WebAssembly Assembler. 11 /// 12 /// It contains code to translate a parsed .s file into MCInsts. 13 /// 14 //===----------------------------------------------------------------------===// 15 16 #include "AsmParser/WebAssemblyAsmTypeCheck.h" 17 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 18 #include "MCTargetDesc/WebAssemblyTargetStreamer.h" 19 #include "TargetInfo/WebAssemblyTargetInfo.h" 20 #include "Utils/WebAssemblyTypeUtilities.h" 21 #include "Utils/WebAssemblyUtilities.h" 22 #include "WebAssembly.h" 23 #include "llvm/MC/MCContext.h" 24 #include "llvm/MC/MCExpr.h" 25 #include "llvm/MC/MCInst.h" 26 #include "llvm/MC/MCInstrInfo.h" 27 #include "llvm/MC/MCParser/MCParsedAsmOperand.h" 28 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 29 #include "llvm/MC/MCSectionWasm.h" 30 #include "llvm/MC/MCStreamer.h" 31 #include "llvm/MC/MCSubtargetInfo.h" 32 #include "llvm/MC/MCSymbol.h" 33 #include "llvm/MC/MCSymbolWasm.h" 34 #include "llvm/MC/TargetRegistry.h" 35 #include "llvm/Support/Compiler.h" 36 #include "llvm/Support/Endian.h" 37 #include "llvm/Support/SourceMgr.h" 38 39 using namespace llvm; 40 41 #define DEBUG_TYPE "wasm-asm-parser" 42 43 extern StringRef GetMnemonic(unsigned Opc); 44 45 namespace llvm { 46 47 WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser, 48 const MCInstrInfo &MII, bool is64) 49 : Parser(Parser), MII(MII), is64(is64) { 50 } 51 52 void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) { 53 LocalTypes.assign(Sig.Params.begin(), Sig.Params.end()); 54 ReturnTypes.assign(Sig.Returns.begin(), Sig.Returns.end()); 55 } 56 57 void WebAssemblyAsmTypeCheck::localDecl(const SmallVector<wasm::ValType, 4> &Locals) { 58 LocalTypes.insert(LocalTypes.end(), Locals.begin(), Locals.end()); 59 } 60 61 void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) { 62 LLVM_DEBUG({ 63 std::string s; 64 for (auto VT : Stack) { 65 s += WebAssembly::typeToString(VT); 66 s += " "; 67 } 68 dbgs() << Msg << s << '\n'; 69 }); 70 } 71 72 bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) { 73 // Once you get one type error in a function, it will likely trigger more 74 // which are mostly not helpful. 75 if (TypeErrorThisFunction) 76 return true; 77 // If we're currently in unreachable code, we surpress errors as well. 78 if (Unreachable) 79 return true; 80 TypeErrorThisFunction = true; 81 dumpTypeStack("current stack: "); 82 return Parser.Error(ErrorLoc, Msg); 83 } 84 85 bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, 86 Optional<wasm::ValType> EVT) { 87 if (Stack.empty()) { 88 return typeError(ErrorLoc, 89 EVT.hasValue() 90 ? StringRef("empty stack while popping ") + 91 WebAssembly::typeToString(EVT.getValue()) 92 : StringRef( 93 "empty stack while popping value")); 94 } 95 auto PVT = Stack.pop_back_val(); 96 if (EVT.hasValue() && EVT.getValue() != PVT) { 97 return typeError( 98 ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) + 99 ", expected " + 100 WebAssembly::typeToString(EVT.getValue())); 101 } 102 return false; 103 } 104 105 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst, 106 wasm::ValType &Type) { 107 auto Local = static_cast<size_t>(Inst.getOperand(0).getImm()); 108 if (Local >= LocalTypes.size()) 109 return typeError(ErrorLoc, StringRef("no local type specified for index ") + 110 std::to_string(Local)); 111 Type = LocalTypes[Local]; 112 return false; 113 } 114 115 bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc, bool PopVals) { 116 if (LastSig.Returns.size() > Stack.size()) 117 return typeError(ErrorLoc, "end: insufficient values on the type stack"); 118 119 if (PopVals) { 120 for (auto VT : llvm::reverse(LastSig.Returns)) { 121 if (popType(ErrorLoc, VT)) 122 return true; 123 } 124 return false; 125 } 126 127 for (size_t i = 0; i < LastSig.Returns.size(); i++) { 128 auto EVT = LastSig.Returns[i]; 129 auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i]; 130 if (PVT != EVT) 131 return typeError( 132 ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) + 133 ", expected " + WebAssembly::typeToString(EVT)); 134 } 135 return false; 136 } 137 138 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc, 139 const wasm::WasmSignature& Sig) { 140 for (auto VT : llvm::reverse(Sig.Params)) 141 if (popType(ErrorLoc, VT)) return true; 142 Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end()); 143 return false; 144 } 145 146 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst, 147 const MCSymbolRefExpr *&SymRef) { 148 auto Op = Inst.getOperand(0); 149 if (!Op.isExpr()) 150 return typeError(ErrorLoc, StringRef("expected expression operand")); 151 SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr()); 152 if (!SymRef) 153 return typeError(ErrorLoc, StringRef("expected symbol operand")); 154 return false; 155 } 156 157 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst, 158 wasm::ValType &Type) { 159 const MCSymbolRefExpr *SymRef; 160 if (getSymRef(ErrorLoc, Inst, SymRef)) 161 return true; 162 auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); 163 switch (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA)) { 164 case wasm::WASM_SYMBOL_TYPE_GLOBAL: 165 Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type); 166 break; 167 case wasm::WASM_SYMBOL_TYPE_FUNCTION: 168 case wasm::WASM_SYMBOL_TYPE_DATA: 169 switch (SymRef->getKind()) { 170 case MCSymbolRefExpr::VK_GOT: 171 case MCSymbolRefExpr::VK_WASM_GOT_TLS: 172 Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32; 173 return false; 174 default: 175 break; 176 } 177 LLVM_FALLTHROUGH; 178 default: 179 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + 180 " missing .globaltype"); 181 } 182 return false; 183 } 184 185 bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) { 186 // Check the return types. 187 for (auto RVT : llvm::reverse(ReturnTypes)) { 188 if (popType(ErrorLoc, RVT)) 189 return true; 190 } 191 if (!Stack.empty()) { 192 return typeError(ErrorLoc, std::to_string(Stack.size()) + 193 " superfluous return values"); 194 } 195 Unreachable = true; 196 return false; 197 } 198 199 bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) { 200 auto Opc = Inst.getOpcode(); 201 auto Name = GetMnemonic(Opc); 202 dumpTypeStack("typechecking " + Name + ": "); 203 wasm::ValType Type; 204 if (Name == "local.get") { 205 if (getLocal(ErrorLoc, Inst, Type)) 206 return true; 207 Stack.push_back(Type); 208 } else if (Name == "local.set") { 209 if (getLocal(ErrorLoc, Inst, Type)) 210 return true; 211 if (popType(ErrorLoc, Type)) 212 return true; 213 } else if (Name == "local.tee") { 214 if (getLocal(ErrorLoc, Inst, Type)) 215 return true; 216 if (popType(ErrorLoc, Type)) 217 return true; 218 Stack.push_back(Type); 219 } else if (Name == "global.get") { 220 if (getGlobal(ErrorLoc, Inst, Type)) 221 return true; 222 Stack.push_back(Type); 223 } else if (Name == "global.set") { 224 if (getGlobal(ErrorLoc, Inst, Type)) 225 return true; 226 if (popType(ErrorLoc, Type)) 227 return true; 228 } else if (Name == "drop") { 229 if (popType(ErrorLoc, {})) 230 return true; 231 } else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" || 232 Name == "else" || Name == "end_try") { 233 if (checkEnd(ErrorLoc, Name == "else")) 234 return true; 235 if (Name == "end_block") 236 Unreachable = false; 237 } else if (Name == "return") { 238 if (endOfFunction(ErrorLoc)) 239 return true; 240 } else if (Name == "call_indirect" || Name == "return_call_indirect") { 241 // Function value. 242 if (popType(ErrorLoc, wasm::ValType::I32)) return true; 243 if (checkSig(ErrorLoc, LastSig)) return true; 244 if (Name == "return_call_indirect" && endOfFunction(ErrorLoc)) 245 return true; 246 } else if (Name == "call" || Name == "return_call") { 247 const MCSymbolRefExpr *SymRef; 248 if (getSymRef(ErrorLoc, Inst, SymRef)) 249 return true; 250 auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); 251 auto Sig = WasmSym->getSignature(); 252 if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION) 253 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + 254 " missing .functype"); 255 if (checkSig(ErrorLoc, *Sig)) return true; 256 if (Name == "return_call" && endOfFunction(ErrorLoc)) 257 return true; 258 } else if (Name == "catch") { 259 const MCSymbolRefExpr *SymRef; 260 if (getSymRef(ErrorLoc, Inst, SymRef)) 261 return true; 262 const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); 263 const auto *Sig = WasmSym->getSignature(); 264 if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG) 265 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + 266 " missing .tagtype"); 267 // catch instruction pushes values whose types are specified in the tag's 268 // "params" part 269 Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end()); 270 } else if (Name == "ref.null") { 271 auto VT = static_cast<wasm::ValType>(Inst.getOperand(0).getImm()); 272 Stack.push_back(VT); 273 } else if (Name == "unreachable") { 274 Unreachable = true; 275 } else { 276 // The current instruction is a stack instruction which doesn't have 277 // explicit operands that indicate push/pop types, so we get those from 278 // the register version of the same instruction. 279 auto RegOpc = WebAssembly::getRegisterOpcode(Opc); 280 assert(RegOpc != -1 && "Failed to get register version of MC instruction"); 281 const auto &II = MII.get(RegOpc); 282 // First pop all the uses off the stack and check them. 283 for (unsigned I = II.getNumOperands(); I > II.getNumDefs(); I--) { 284 const auto &Op = II.OpInfo[I - 1]; 285 if (Op.OperandType == MCOI::OPERAND_REGISTER) { 286 auto VT = WebAssembly::regClassToValType(Op.RegClass); 287 if (popType(ErrorLoc, VT)) 288 return true; 289 } 290 } 291 // Now push all the defs onto the stack. 292 for (unsigned I = 0; I < II.getNumDefs(); I++) { 293 const auto &Op = II.OpInfo[I]; 294 assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected"); 295 auto VT = WebAssembly::regClassToValType(Op.RegClass); 296 Stack.push_back(VT); 297 } 298 } 299 return false; 300 } 301 302 } // end namespace llvm 303