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/Support/Compiler.h" 35 #include "llvm/Support/Endian.h" 36 #include "llvm/Support/SourceMgr.h" 37 #include "llvm/Support/TargetRegistry.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 TypeErrorThisFunction = true; 78 dumpTypeStack("current stack: "); 79 return Parser.Error(ErrorLoc, Msg); 80 } 81 82 bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, 83 Optional<wasm::ValType> EVT) { 84 if (Stack.empty()) { 85 return typeError(ErrorLoc, 86 EVT.hasValue() 87 ? StringRef("empty stack while popping ") + 88 WebAssembly::typeToString(EVT.getValue()) 89 : StringRef( 90 "empty stack while popping value")); 91 } 92 auto PVT = Stack.back(); 93 Stack.pop_back(); 94 if (EVT.hasValue() && EVT.getValue() != PVT) { 95 return typeError( 96 ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) + 97 ", expected " + 98 WebAssembly::typeToString(EVT.getValue())); 99 } 100 return false; 101 } 102 103 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst, 104 wasm::ValType &Type) { 105 auto Local = static_cast<size_t>(Inst.getOperand(0).getImm()); 106 if (Local >= LocalTypes.size()) 107 return typeError(ErrorLoc, StringRef("no local type specified for index ") + 108 std::to_string(Local)); 109 Type = LocalTypes[Local]; 110 return false; 111 } 112 113 bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc) { 114 if (LastSig.Returns.size() > Stack.size()) 115 return typeError(ErrorLoc, "end: insufficient values on the type stack"); 116 for (size_t i = 0; i < LastSig.Returns.size(); i++) { 117 auto EVT = LastSig.Returns[i]; 118 auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i]; 119 if (PVT != EVT) 120 return typeError( 121 ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) + 122 ", expected " + WebAssembly::typeToString(EVT)); 123 } 124 return false; 125 } 126 127 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc, 128 const wasm::WasmSignature& Sig) { 129 for (auto VT : llvm::reverse(Sig.Params)) 130 if (popType(ErrorLoc, VT)) return true; 131 Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end()); 132 return false; 133 } 134 135 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst, 136 const MCSymbolRefExpr *&SymRef) { 137 auto Op = Inst.getOperand(0); 138 if (!Op.isExpr()) 139 return typeError(ErrorLoc, StringRef("expected expression operand")); 140 SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr()); 141 if (!SymRef) 142 return typeError(ErrorLoc, StringRef("expected symbol operand")); 143 return false; 144 } 145 146 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst, 147 wasm::ValType &Type) { 148 const MCSymbolRefExpr *SymRef; 149 if (getSymRef(ErrorLoc, Inst, SymRef)) 150 return true; 151 auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); 152 switch (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA)) { 153 case wasm::WASM_SYMBOL_TYPE_GLOBAL: 154 Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type); 155 break; 156 case wasm::WASM_SYMBOL_TYPE_FUNCTION: 157 case wasm::WASM_SYMBOL_TYPE_DATA: 158 if (SymRef->getKind() == MCSymbolRefExpr::VK_GOT) { 159 Type = is64 ? wasm::ValType::I64 : wasm::ValType::I32; 160 break; 161 } 162 LLVM_FALLTHROUGH; 163 default: 164 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + 165 " missing .globaltype"); 166 } 167 return false; 168 } 169 170 void WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc) { 171 // Check the return types. 172 for (auto RVT : llvm::reverse(ReturnTypes)) { 173 popType(ErrorLoc, RVT); 174 } 175 if (!Stack.empty()) { 176 typeError(ErrorLoc, 177 std::to_string(Stack.size()) + " superfluous return values"); 178 } 179 // Reset the type checker state. 180 Clear(); 181 } 182 183 bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst) { 184 auto Opc = Inst.getOpcode(); 185 auto Name = GetMnemonic(Opc); 186 dumpTypeStack("typechecking " + Name + ": "); 187 wasm::ValType Type; 188 if (Name == "local.get") { 189 if (getLocal(ErrorLoc, Inst, Type)) 190 return true; 191 Stack.push_back(Type); 192 } else if (Name == "local.set") { 193 if (getLocal(ErrorLoc, Inst, Type)) 194 return true; 195 if (popType(ErrorLoc, Type)) 196 return true; 197 } else if (Name == "local.tee") { 198 if (getLocal(ErrorLoc, Inst, Type)) 199 return true; 200 if (popType(ErrorLoc, Type)) 201 return true; 202 Stack.push_back(Type); 203 } else if (Name == "global.get") { 204 if (getGlobal(ErrorLoc, Inst, Type)) 205 return true; 206 Stack.push_back(Type); 207 } else if (Name == "global.set") { 208 if (getGlobal(ErrorLoc, Inst, Type)) 209 return true; 210 if (popType(ErrorLoc, Type)) 211 return true; 212 } else if (Name == "drop") { 213 if (popType(ErrorLoc, {})) 214 return true; 215 } else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" || 216 Name == "else") { 217 if (checkEnd(ErrorLoc)) 218 return true; 219 } else if (Name == "call_indirect" || Name == "return_call_indirect") { 220 // Function value. 221 if (popType(ErrorLoc, wasm::ValType::I32)) return true; 222 if (checkSig(ErrorLoc, LastSig)) return true; 223 } else if (Name == "call" || Name == "return_call") { 224 const MCSymbolRefExpr *SymRef; 225 if (getSymRef(ErrorLoc, Inst, SymRef)) 226 return true; 227 auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol()); 228 auto Sig = WasmSym->getSignature(); 229 if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION) 230 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() + 231 " missing .functype"); 232 if (checkSig(ErrorLoc, *Sig)) return true; 233 } else if (Name == "ref.null") { 234 auto VT = static_cast<wasm::ValType>(Inst.getOperand(0).getImm()); 235 Stack.push_back(VT); 236 } else { 237 // The current instruction is a stack instruction which doesn't have 238 // explicit operands that indicate push/pop types, so we get those from 239 // the register version of the same instruction. 240 auto RegOpc = WebAssembly::getRegisterOpcode(Opc); 241 assert(RegOpc != -1 && "Failed to get register version of MC instruction"); 242 const auto &II = MII.get(RegOpc); 243 // First pop all the uses off the stack and check them. 244 for (unsigned I = II.getNumOperands(); I > II.getNumDefs(); I--) { 245 const auto &Op = II.OpInfo[I - 1]; 246 if (Op.OperandType == MCOI::OPERAND_REGISTER) { 247 auto VT = WebAssembly::regClassToValType(Op.RegClass); 248 if (popType(ErrorLoc, VT)) 249 return true; 250 } 251 } 252 // Now push all the defs onto the stack. 253 for (unsigned I = 0; I < II.getNumDefs(); I++) { 254 const auto &Op = II.OpInfo[I]; 255 assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected"); 256 auto VT = WebAssembly::regClassToValType(Op.RegClass); 257 Stack.push_back(VT); 258 } 259 } 260 return false; 261 } 262 263 } // end namespace llvm 264