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/WebAssemblyMCAsmInfo.h"
18 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
19 #include "MCTargetDesc/WebAssemblyMCTypeUtilities.h"
20 #include "MCTargetDesc/WebAssemblyTargetStreamer.h"
21 #include "TargetInfo/WebAssemblyTargetInfo.h"
22 #include "llvm/MC/MCContext.h"
23 #include "llvm/MC/MCExpr.h"
24 #include "llvm/MC/MCInst.h"
25 #include "llvm/MC/MCInstrInfo.h"
26 #include "llvm/MC/MCParser/MCParsedAsmOperand.h"
27 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
28 #include "llvm/MC/MCSectionWasm.h"
29 #include "llvm/MC/MCStreamer.h"
30 #include "llvm/MC/MCSubtargetInfo.h"
31 #include "llvm/MC/MCSymbol.h"
32 #include "llvm/MC/MCSymbolWasm.h"
33 #include "llvm/MC/TargetRegistry.h"
34 #include "llvm/Support/Compiler.h"
35 #include "llvm/Support/SourceMgr.h"
36 #include <sstream>
37
38 using namespace llvm;
39
40 #define DEBUG_TYPE "wasm-asm-parser"
41
42 extern StringRef getMnemonic(unsigned Opc);
43
44 namespace llvm {
45
WebAssemblyAsmTypeCheck(MCAsmParser & Parser,const MCInstrInfo & MII,bool Is64)46 WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck(MCAsmParser &Parser,
47 const MCInstrInfo &MII,
48 bool Is64)
49 : Parser(Parser), MII(MII), Is64(Is64) {}
50
funcDecl(const wasm::WasmSignature & Sig)51 void WebAssemblyAsmTypeCheck::funcDecl(const wasm::WasmSignature &Sig) {
52 LocalTypes.assign(Sig.Params.begin(), Sig.Params.end());
53 BlockInfoStack.push_back({Sig, 0, false});
54 }
55
localDecl(const SmallVectorImpl<wasm::ValType> & Locals)56 void WebAssemblyAsmTypeCheck::localDecl(
57 const SmallVectorImpl<wasm::ValType> &Locals) {
58 llvm::append_range(LocalTypes, Locals);
59 }
60
dumpTypeStack(Twine Msg)61 void WebAssemblyAsmTypeCheck::dumpTypeStack(Twine Msg) {
62 LLVM_DEBUG({ dbgs() << Msg << getTypesString(Stack) << "\n"; });
63 }
64
typeError(SMLoc ErrorLoc,const Twine & Msg)65 bool WebAssemblyAsmTypeCheck::typeError(SMLoc ErrorLoc, const Twine &Msg) {
66 dumpTypeStack("current stack: ");
67 return Parser.Error(ErrorLoc, Msg);
68 }
69
match(StackType TypeA,StackType TypeB)70 bool WebAssemblyAsmTypeCheck::match(StackType TypeA, StackType TypeB) {
71 // These should have been filtered out in checkTypes()
72 assert(!std::get_if<Polymorphic>(&TypeA) &&
73 !std::get_if<Polymorphic>(&TypeB));
74
75 if (TypeA == TypeB)
76 return false;
77 if (std::get_if<Any>(&TypeA) || std::get_if<Any>(&TypeB))
78 return false;
79
80 if (std::get_if<Ref>(&TypeB))
81 std::swap(TypeA, TypeB);
82 assert(std::get_if<wasm::ValType>(&TypeB));
83 if (std::get_if<Ref>(&TypeA) &&
84 WebAssembly::isRefType(std::get<wasm::ValType>(TypeB)))
85 return false;
86 return true;
87 }
88
getTypesString(ArrayRef<StackType> Types,size_t StartPos)89 std::string WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<StackType> Types,
90 size_t StartPos) {
91 SmallVector<std::string, 4> TypeStrs;
92 for (auto I = Types.size(); I > StartPos; I--) {
93 if (std::get_if<Polymorphic>(&Types[I - 1])) {
94 TypeStrs.push_back("...");
95 break;
96 }
97 if (std::get_if<Any>(&Types[I - 1]))
98 TypeStrs.push_back("any");
99 else if (std::get_if<Ref>(&Types[I - 1]))
100 TypeStrs.push_back("ref");
101 else
102 TypeStrs.push_back(
103 WebAssembly::typeToString(std::get<wasm::ValType>(Types[I - 1])));
104 }
105
106 std::stringstream SS;
107 SS << "[";
108 bool First = true;
109 for (auto It = TypeStrs.rbegin(); It != TypeStrs.rend(); ++It) {
110 if (!First)
111 SS << ", ";
112 SS << *It;
113 First = false;
114 }
115 SS << "]";
116 return SS.str();
117 }
118
119 std::string
getTypesString(ArrayRef<wasm::ValType> Types,size_t StartPos)120 WebAssemblyAsmTypeCheck::getTypesString(ArrayRef<wasm::ValType> Types,
121 size_t StartPos) {
122 return getTypesString(valTypesToStackTypes(Types), StartPos);
123 }
124
125 SmallVector<WebAssemblyAsmTypeCheck::StackType, 4>
valTypesToStackTypes(ArrayRef<wasm::ValType> ValTypes)126 WebAssemblyAsmTypeCheck::valTypesToStackTypes(
127 ArrayRef<wasm::ValType> ValTypes) {
128 SmallVector<StackType, 4> Types(ValTypes.size());
129 llvm::transform(ValTypes, Types.begin(),
130 [](wasm::ValType Val) -> StackType { return Val; });
131 return Types;
132 }
133
checkTypes(SMLoc ErrorLoc,ArrayRef<wasm::ValType> ValTypes,bool ExactMatch)134 bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
135 ArrayRef<wasm::ValType> ValTypes,
136 bool ExactMatch) {
137 return checkTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
138 }
139
checkTypes(SMLoc ErrorLoc,ArrayRef<StackType> Types,bool ExactMatch)140 bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
141 ArrayRef<StackType> Types,
142 bool ExactMatch) {
143 auto StackI = Stack.size();
144 auto TypeI = Types.size();
145 assert(!BlockInfoStack.empty());
146 auto BlockStackStartPos = BlockInfoStack.back().StackStartPos;
147 bool Error = false;
148 bool PolymorphicStack = false;
149 // Compare elements one by one from the stack top
150 for (; StackI > BlockStackStartPos && TypeI > 0; StackI--, TypeI--) {
151 // If the stack is polymorphic, we assume all types in 'Types' have been
152 // compared and matched
153 if (std::get_if<Polymorphic>(&Stack[StackI - 1])) {
154 TypeI = 0;
155 break;
156 }
157 if (match(Stack[StackI - 1], Types[TypeI - 1])) {
158 Error = true;
159 break;
160 }
161 }
162
163 // If the stack top is polymorphic, the stack is in the polymorphic state.
164 if (StackI > BlockStackStartPos &&
165 std::get_if<Polymorphic>(&Stack[StackI - 1]))
166 PolymorphicStack = true;
167
168 // Even if no match failure has happened in the loop above, if not all
169 // elements of Types has been matched, that means we don't have enough
170 // elements on the stack.
171 //
172 // Also, if not all elements of the Stack has been matched and when
173 // 'ExactMatch' is true and the current stack is not polymorphic, that means
174 // we have superfluous elements remaining on the stack (e.g. at the end of a
175 // function).
176 if (TypeI > 0 ||
177 (ExactMatch && !PolymorphicStack && StackI > BlockStackStartPos))
178 Error = true;
179
180 if (!Error)
181 return false;
182
183 auto StackStartPos = ExactMatch
184 ? BlockStackStartPos
185 : std::max((int)BlockStackStartPos,
186 (int)Stack.size() - (int)Types.size());
187 return typeError(ErrorLoc, "type mismatch, expected " +
188 getTypesString(Types) + " but got " +
189 getTypesString(Stack, StackStartPos));
190 }
191
popTypes(SMLoc ErrorLoc,ArrayRef<wasm::ValType> ValTypes,bool ExactMatch)192 bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
193 ArrayRef<wasm::ValType> ValTypes,
194 bool ExactMatch) {
195 return popTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
196 }
197
popTypes(SMLoc ErrorLoc,ArrayRef<StackType> Types,bool ExactMatch)198 bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
199 ArrayRef<StackType> Types,
200 bool ExactMatch) {
201 bool Error = checkTypes(ErrorLoc, Types, ExactMatch);
202 auto NumPops = std::min(Stack.size() - BlockInfoStack.back().StackStartPos,
203 Types.size());
204 for (size_t I = 0, E = NumPops; I != E; I++) {
205 if (std::get_if<Polymorphic>(&Stack.back()))
206 break;
207 Stack.pop_back();
208 }
209 return Error;
210 }
211
popType(SMLoc ErrorLoc,StackType Type)212 bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, StackType Type) {
213 return popTypes(ErrorLoc, {Type});
214 }
215
popRefType(SMLoc ErrorLoc)216 bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
217 return popType(ErrorLoc, Ref{});
218 }
219
popAnyType(SMLoc ErrorLoc)220 bool WebAssemblyAsmTypeCheck::popAnyType(SMLoc ErrorLoc) {
221 return popType(ErrorLoc, Any{});
222 }
223
pushTypes(ArrayRef<wasm::ValType> ValTypes)224 void WebAssemblyAsmTypeCheck::pushTypes(ArrayRef<wasm::ValType> ValTypes) {
225 Stack.append(valTypesToStackTypes(ValTypes));
226 }
227
getLocal(SMLoc ErrorLoc,const MCOperand & LocalOp,wasm::ValType & Type)228 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp,
229 wasm::ValType &Type) {
230 auto Local = static_cast<size_t>(LocalOp.getImm());
231 if (Local >= LocalTypes.size())
232 return typeError(ErrorLoc, StringRef("no local type specified for index ") +
233 std::to_string(Local));
234 Type = LocalTypes[Local];
235 return false;
236 }
237
checkSig(SMLoc ErrorLoc,const wasm::WasmSignature & Sig)238 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
239 const wasm::WasmSignature &Sig) {
240 bool Error = popTypes(ErrorLoc, Sig.Params);
241 pushTypes(Sig.Returns);
242 return Error;
243 }
244
getSymRef(SMLoc ErrorLoc,const MCOperand & SymOp,const MCSymbolRefExpr * & SymRef)245 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCOperand &SymOp,
246 const MCSymbolRefExpr *&SymRef) {
247 if (!SymOp.isExpr())
248 return typeError(ErrorLoc, StringRef("expected expression operand"));
249 SymRef = dyn_cast<MCSymbolRefExpr>(SymOp.getExpr());
250 if (!SymRef)
251 return typeError(ErrorLoc, StringRef("expected symbol operand"));
252 return false;
253 }
254
getGlobal(SMLoc ErrorLoc,const MCOperand & GlobalOp,wasm::ValType & Type)255 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc,
256 const MCOperand &GlobalOp,
257 wasm::ValType &Type) {
258 const MCSymbolRefExpr *SymRef;
259 if (getSymRef(ErrorLoc, GlobalOp, SymRef))
260 return true;
261 const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
262 switch (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA)) {
263 case wasm::WASM_SYMBOL_TYPE_GLOBAL:
264 Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
265 break;
266 case wasm::WASM_SYMBOL_TYPE_FUNCTION:
267 case wasm::WASM_SYMBOL_TYPE_DATA:
268 switch (SymRef->getSpecifier()) {
269 case WebAssembly::S_GOT:
270 case WebAssembly::S_GOT_TLS:
271 Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
272 return false;
273 default:
274 break;
275 }
276 [[fallthrough]];
277 default:
278 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
279 ": missing .globaltype");
280 }
281 return false;
282 }
283
getTable(SMLoc ErrorLoc,const MCOperand & TableOp,wasm::ValType & Type)284 bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCOperand &TableOp,
285 wasm::ValType &Type) {
286 const MCSymbolRefExpr *SymRef;
287 if (getSymRef(ErrorLoc, TableOp, SymRef))
288 return true;
289 const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
290 if (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA) !=
291 wasm::WASM_SYMBOL_TYPE_TABLE)
292 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
293 ": missing .tabletype");
294 Type = static_cast<wasm::ValType>(WasmSym->getTableType().ElemType);
295 return false;
296 }
297
getSignature(SMLoc ErrorLoc,const MCOperand & SigOp,wasm::WasmSymbolType Type,const wasm::WasmSignature * & Sig)298 bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
299 const MCOperand &SigOp,
300 wasm::WasmSymbolType Type,
301 const wasm::WasmSignature *&Sig) {
302 const MCSymbolRefExpr *SymRef = nullptr;
303 if (getSymRef(ErrorLoc, SigOp, SymRef))
304 return true;
305 const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
306 Sig = WasmSym->getSignature();
307
308 if (!Sig || WasmSym->getType() != Type) {
309 const char *TypeName = nullptr;
310 switch (Type) {
311 case wasm::WASM_SYMBOL_TYPE_FUNCTION:
312 TypeName = "func";
313 break;
314 case wasm::WASM_SYMBOL_TYPE_TAG:
315 TypeName = "tag";
316 break;
317 default:
318 llvm_unreachable("Signature symbol should either be a function or a tag");
319 }
320 return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
321 ": missing ." + TypeName + "type");
322 }
323 return false;
324 }
325
endOfFunction(SMLoc ErrorLoc,bool ExactMatch)326 bool WebAssemblyAsmTypeCheck::endOfFunction(SMLoc ErrorLoc, bool ExactMatch) {
327 assert(!BlockInfoStack.empty());
328 const auto &FuncInfo = BlockInfoStack[0];
329 return checkTypes(ErrorLoc, FuncInfo.Sig.Returns, ExactMatch);
330 }
331
332 // Unlike checkTypes() family, this just compare the equivalence of the two
333 // ValType vectors
compareTypes(ArrayRef<wasm::ValType> TypesA,ArrayRef<wasm::ValType> TypesB)334 static bool compareTypes(ArrayRef<wasm::ValType> TypesA,
335 ArrayRef<wasm::ValType> TypesB) {
336 if (TypesA.size() != TypesB.size())
337 return true;
338 for (size_t I = 0, E = TypesA.size(); I < E; I++)
339 if (TypesA[I] != TypesB[I])
340 return true;
341 return false;
342 }
343
checkTryTable(SMLoc ErrorLoc,const MCInst & Inst)344 bool WebAssemblyAsmTypeCheck::checkTryTable(SMLoc ErrorLoc,
345 const MCInst &Inst) {
346 bool Error = false;
347 unsigned OpIdx = 1; // OpIdx 0 is the block type
348 int64_t NumCatches = Inst.getOperand(OpIdx++).getImm();
349 for (int64_t I = 0; I < NumCatches; I++) {
350 int64_t Opcode = Inst.getOperand(OpIdx++).getImm();
351 std::string ErrorMsgBase =
352 "try_table: catch index " + std::to_string(I) + ": ";
353
354 const wasm::WasmSignature *Sig = nullptr;
355 SmallVector<wasm::ValType> SentTypes;
356 if (Opcode == wasm::WASM_OPCODE_CATCH ||
357 Opcode == wasm::WASM_OPCODE_CATCH_REF) {
358 if (!getSignature(ErrorLoc, Inst.getOperand(OpIdx++),
359 wasm::WASM_SYMBOL_TYPE_TAG, Sig))
360 llvm::append_range(SentTypes, Sig->Params);
361 else
362 Error = true;
363 }
364 if (Opcode == wasm::WASM_OPCODE_CATCH_REF ||
365 Opcode == wasm::WASM_OPCODE_CATCH_ALL_REF) {
366 SentTypes.push_back(wasm::ValType::EXNREF);
367 }
368
369 unsigned Level = Inst.getOperand(OpIdx++).getImm();
370 if (Level < BlockInfoStack.size()) {
371 const auto &DestBlockInfo =
372 BlockInfoStack[BlockInfoStack.size() - Level - 1];
373 ArrayRef<wasm::ValType> DestTypes;
374 if (DestBlockInfo.IsLoop)
375 DestTypes = DestBlockInfo.Sig.Params;
376 else
377 DestTypes = DestBlockInfo.Sig.Returns;
378 if (compareTypes(SentTypes, DestTypes)) {
379 std::string ErrorMsg =
380 ErrorMsgBase + "type mismatch, catch tag type is " +
381 getTypesString(SentTypes) + ", but destination's type is " +
382 getTypesString(DestTypes);
383 Error |= typeError(ErrorLoc, ErrorMsg);
384 }
385 } else {
386 Error = typeError(ErrorLoc, ErrorMsgBase + "invalid depth " +
387 std::to_string(Level));
388 }
389 }
390 return Error;
391 }
392
typeCheck(SMLoc ErrorLoc,const MCInst & Inst,OperandVector & Operands)393 bool WebAssemblyAsmTypeCheck::typeCheck(SMLoc ErrorLoc, const MCInst &Inst,
394 OperandVector &Operands) {
395 auto Opc = Inst.getOpcode();
396 auto Name = getMnemonic(Opc);
397 dumpTypeStack("typechecking " + Name + ": ");
398 wasm::ValType Type;
399
400 if (Name == "local.get") {
401 if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
402 pushType(Type);
403 return false;
404 }
405 pushType(Any{});
406 return true;
407 }
408
409 if (Name == "local.set") {
410 if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
411 return popType(ErrorLoc, Type);
412 popType(ErrorLoc, Any{});
413 return true;
414 }
415
416 if (Name == "local.tee") {
417 if (!getLocal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
418 bool Error = popType(ErrorLoc, Type);
419 pushType(Type);
420 return Error;
421 }
422 popType(ErrorLoc, Any{});
423 pushType(Any{});
424 return true;
425 }
426
427 if (Name == "global.get") {
428 if (!getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
429 pushType(Type);
430 return false;
431 }
432 pushType(Any{});
433 return true;
434 }
435
436 if (Name == "global.set") {
437 if (!getGlobal(Operands[1]->getStartLoc(), Inst.getOperand(0), Type))
438 return popType(ErrorLoc, Type);
439 popType(ErrorLoc, Any{});
440 return true;
441 }
442
443 if (Name == "table.get") {
444 bool Error = popType(ErrorLoc, wasm::ValType::I32);
445 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
446 pushType(Type);
447 return Error;
448 }
449 pushType(Any{});
450 return true;
451 }
452
453 if (Name == "table.set") {
454 bool Error = false;
455 SmallVector<StackType, 2> PopTypes;
456 PopTypes.push_back(wasm::ValType::I32);
457 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
458 PopTypes.push_back(Type);
459 } else {
460 Error = true;
461 PopTypes.push_back(Any{});
462 }
463 Error |= popTypes(ErrorLoc, PopTypes);
464 return Error;
465 }
466
467 if (Name == "table.size") {
468 bool Error = getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type);
469 pushType(wasm::ValType::I32);
470 return Error;
471 }
472
473 if (Name == "table.grow") {
474 bool Error = false;
475 SmallVector<StackType, 2> PopTypes;
476 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
477 PopTypes.push_back(Type);
478 } else {
479 Error = true;
480 PopTypes.push_back(Any{});
481 }
482 PopTypes.push_back(wasm::ValType::I32);
483 Error |= popTypes(ErrorLoc, PopTypes);
484 pushType(wasm::ValType::I32);
485 return Error;
486 }
487
488 if (Name == "table.fill") {
489 bool Error = false;
490 SmallVector<StackType, 2> PopTypes;
491 PopTypes.push_back(wasm::ValType::I32);
492 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
493 PopTypes.push_back(Type);
494 } else {
495 Error = true;
496 PopTypes.push_back(Any{});
497 }
498 PopTypes.push_back(wasm::ValType::I32);
499 Error |= popTypes(ErrorLoc, PopTypes);
500 return Error;
501 }
502
503 if (Name == "memory.fill") {
504 Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
505 bool Error = popType(ErrorLoc, Type);
506 Error |= popType(ErrorLoc, wasm::ValType::I32);
507 Error |= popType(ErrorLoc, Type);
508 return Error;
509 }
510
511 if (Name == "memory.copy") {
512 Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
513 bool Error = popType(ErrorLoc, Type);
514 Error |= popType(ErrorLoc, Type);
515 Error |= popType(ErrorLoc, Type);
516 return Error;
517 }
518
519 if (Name == "memory.init") {
520 Type = Is64 ? wasm::ValType::I64 : wasm::ValType::I32;
521 bool Error = popType(ErrorLoc, wasm::ValType::I32);
522 Error |= popType(ErrorLoc, wasm::ValType::I32);
523 Error |= popType(ErrorLoc, Type);
524 return Error;
525 }
526
527 if (Name == "drop") {
528 return popType(ErrorLoc, Any{});
529 }
530
531 if (Name == "block" || Name == "loop" || Name == "if" || Name == "try" ||
532 Name == "try_table") {
533 bool Error = Name == "if" && popType(ErrorLoc, wasm::ValType::I32);
534 // Pop block input parameters and check their types are correct
535 Error |= popTypes(ErrorLoc, LastSig.Params);
536 if (Name == "try_table")
537 Error |= checkTryTable(ErrorLoc, Inst);
538 // Push a new block info
539 BlockInfoStack.push_back({LastSig, Stack.size(), Name == "loop"});
540 // Push back block input parameters
541 pushTypes(LastSig.Params);
542 return Error;
543 }
544
545 if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
546 Name == "end_try" || Name == "delegate" || Name == "end_try_table" ||
547 Name == "else" || Name == "catch" || Name == "catch_all") {
548 assert(!BlockInfoStack.empty());
549 // Check if the types on the stack match with the block return type
550 const auto &LastBlockInfo = BlockInfoStack.back();
551 bool Error = checkTypes(ErrorLoc, LastBlockInfo.Sig.Returns, true);
552 // Pop all types added to the stack for the current block level
553 Stack.truncate(LastBlockInfo.StackStartPos);
554 if (Name == "else") {
555 // 'else' expects the block input parameters to be on the stack, in the
556 // same way we entered 'if'
557 pushTypes(LastBlockInfo.Sig.Params);
558 } else if (Name == "catch") {
559 // 'catch' instruction pushes values whose types are specified in the
560 // tag's 'params' part
561 const wasm::WasmSignature *Sig = nullptr;
562 if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
563 wasm::WASM_SYMBOL_TYPE_TAG, Sig))
564 pushTypes(Sig->Params);
565 else
566 Error = true;
567 } else if (Name == "catch_all") {
568 // 'catch_all' does not push anything onto the stack
569 } else {
570 // For normal end markers, push block return value types onto the stack
571 // and pop the block info
572 pushTypes(LastBlockInfo.Sig.Returns);
573 BlockInfoStack.pop_back();
574 }
575 return Error;
576 }
577
578 if (Name == "br" || Name == "br_if") {
579 bool Error = false;
580 if (Name == "br_if")
581 Error |= popType(ErrorLoc, wasm::ValType::I32); // cond
582 const MCOperand &Operand = Inst.getOperand(0);
583 if (Operand.isImm()) {
584 unsigned Level = Operand.getImm();
585 if (Level < BlockInfoStack.size()) {
586 const auto &DestBlockInfo =
587 BlockInfoStack[BlockInfoStack.size() - Level - 1];
588 if (DestBlockInfo.IsLoop)
589 Error |= checkTypes(ErrorLoc, DestBlockInfo.Sig.Params, false);
590 else
591 Error |= checkTypes(ErrorLoc, DestBlockInfo.Sig.Returns, false);
592 } else {
593 Error = typeError(ErrorLoc, StringRef("br: invalid depth ") +
594 std::to_string(Level));
595 }
596 } else {
597 Error =
598 typeError(Operands[1]->getStartLoc(), "depth should be an integer");
599 }
600 if (Name == "br")
601 pushType(Polymorphic{});
602 return Error;
603 }
604
605 if (Name == "return") {
606 bool Error = endOfFunction(ErrorLoc, false);
607 pushType(Polymorphic{});
608 return Error;
609 }
610
611 if (Name == "call_indirect" || Name == "return_call_indirect") {
612 // Function value.
613 bool Error = popType(ErrorLoc, wasm::ValType::I32);
614 Error |= checkSig(ErrorLoc, LastSig);
615 if (Name == "return_call_indirect") {
616 Error |= endOfFunction(ErrorLoc, false);
617 pushType(Polymorphic{});
618 }
619 return Error;
620 }
621
622 if (Name == "call" || Name == "return_call") {
623 bool Error = false;
624 const wasm::WasmSignature *Sig = nullptr;
625 if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
626 wasm::WASM_SYMBOL_TYPE_FUNCTION, Sig))
627 Error |= checkSig(ErrorLoc, *Sig);
628 else
629 Error = true;
630 if (Name == "return_call") {
631 Error |= endOfFunction(ErrorLoc, false);
632 pushType(Polymorphic{});
633 }
634 return Error;
635 }
636
637 if (Name == "unreachable") {
638 pushType(Polymorphic{});
639 return false;
640 }
641
642 if (Name == "ref.is_null") {
643 bool Error = popRefType(ErrorLoc);
644 pushType(wasm::ValType::I32);
645 return Error;
646 }
647
648 if (Name == "throw") {
649 bool Error = false;
650 const wasm::WasmSignature *Sig = nullptr;
651 if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
652 wasm::WASM_SYMBOL_TYPE_TAG, Sig))
653 Error |= checkSig(ErrorLoc, *Sig);
654 else
655 Error = true;
656 pushType(Polymorphic{});
657 return Error;
658 }
659
660 if (Name == "throw_ref") {
661 bool Error = popType(ErrorLoc, wasm::ValType::EXNREF);
662 pushType(Polymorphic{});
663 return Error;
664 }
665
666 // The current instruction is a stack instruction which doesn't have
667 // explicit operands that indicate push/pop types, so we get those from
668 // the register version of the same instruction.
669 auto RegOpc = WebAssembly::getRegisterOpcode(Opc);
670 assert(RegOpc != -1 && "Failed to get register version of MC instruction");
671 const auto &II = MII.get(RegOpc);
672 // First pop all the uses off the stack and check them.
673 SmallVector<wasm::ValType, 4> PopTypes;
674 for (unsigned I = II.getNumDefs(); I < II.getNumOperands(); I++) {
675 const auto &Op = II.operands()[I];
676 if (Op.OperandType == MCOI::OPERAND_REGISTER)
677 PopTypes.push_back(WebAssembly::regClassToValType(Op.RegClass));
678 }
679 bool Error = popTypes(ErrorLoc, PopTypes);
680 SmallVector<wasm::ValType, 4> PushTypes;
681 // Now push all the defs onto the stack.
682 for (unsigned I = 0; I < II.getNumDefs(); I++) {
683 const auto &Op = II.operands()[I];
684 assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected");
685 PushTypes.push_back(WebAssembly::regClassToValType(Op.RegClass));
686 }
687 pushTypes(PushTypes);
688 return Error;
689 }
690
691 } // end namespace llvm
692