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