LLVM: lib/Target/WebAssembly/AsmParser/WebAssemblyAsmTypeCheck.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
36#include
37
38using namespace llvm;
39
40#define DEBUG_TYPE "wasm-asm-parser"
41
43
44namespace llvm {
45
48 bool Is64)
49 : Parser(Parser), MII(MII), Is64(Is64) {}
50
53 BlockInfoStack.push_back({Sig, 0, false});
54}
55
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("current stack: ");
67 return Parser.Error(ErrorLoc, Msg);
68}
69
70bool WebAssemblyAsmTypeCheck::match(StackType TypeA, StackType TypeB) {
71
72 assert(!std::get_if(&TypeA) &&
73 !std::get_if(&TypeB));
74
75 if (TypeA == TypeB)
76 return false;
77 if (std::get_if(&TypeA) || std::get_if(&TypeB))
78 return false;
79
80 if (std::get_if(&TypeB))
82 assert(std::get_ifwasm::ValType(&TypeB));
83 if (std::get_if(&TypeA) &&
85 return false;
86 return true;
87}
88
89std::string WebAssemblyAsmTypeCheck::getTypesString(ArrayRef 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(&Types[I - 1])) {
95 break;
96 }
97 if (std::get_if(&Types[I - 1]))
99 else if (std::get_if(&Types[I - 1]))
101 else
104 }
105
106 std::stringstream SS;
107 SS << "[";
108 bool First = true;
109 for (auto It = TypeStrs.rbegin(); It != TypeStrs.rend(); ++It) {
111 SS << ", ";
112 SS << *It;
114 }
115 SS << "]";
116 return SS.str();
117}
118
119std::string
121 size_t StartPos) {
122 return getTypesString(valTypesToStackTypes(Types), StartPos);
123}
124
126WebAssemblyAsmTypeCheck::valTypesToStackTypes(
130 [](wasm::ValType Val) -> StackType { return Val; });
132}
133
134bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
136 bool ExactMatch) {
137 return checkTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
138}
139
140bool WebAssemblyAsmTypeCheck::checkTypes(SMLoc ErrorLoc,
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
150 for (; StackI > BlockStackStartPos && TypeI > 0; StackI--, TypeI--) {
151
152
153 if (std::get_if(&Stack[StackI - 1])) {
154 TypeI = 0;
155 break;
156 }
157 if (match(Stack[StackI - 1], Types[TypeI - 1])) {
159 break;
160 }
161 }
162
163
164 if (StackI > BlockStackStartPos &&
165 std::get_if(&Stack[StackI - 1]))
166 PolymorphicStack = true;
167
168
169
170
171
172
173
174
175
176 if (TypeI > 0 ||
177 (ExactMatch && !PolymorphicStack && StackI > BlockStackStartPos))
179
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
192bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
194 bool ExactMatch) {
195 return popTypes(ErrorLoc, valTypesToStackTypes(ValTypes), ExactMatch);
196}
197
198bool WebAssemblyAsmTypeCheck::popTypes(SMLoc ErrorLoc,
200 bool ExactMatch) {
201 bool Error = checkTypes(ErrorLoc, Types, ExactMatch);
202 auto NumPops = std::min(Stack.size() - BlockInfoStack.back().StackStartPos,
204 for (size_t I = 0, E = NumPops; I != E; I++) {
205 if (std::get_if(&Stack.back()))
206 break;
207 Stack.pop_back();
208 }
210}
211
212bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc, StackType Type) {
213 return popTypes(ErrorLoc, {Type});
214}
215
216bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
217 return popType(ErrorLoc, Ref{});
218}
219
220bool WebAssemblyAsmTypeCheck::popAnyType(SMLoc ErrorLoc) {
221 return popType(ErrorLoc, Any{});
222}
223
225 Stack.append(valTypesToStackTypes(ValTypes));
226}
227
228bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCOperand &LocalOp,
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));
235 return false;
236}
237
238bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
240 bool Error = popTypes(ErrorLoc, Sig.Params);
243}
244
245bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCOperand &SymOp,
248 return typeError(ErrorLoc, StringRef("expected expression operand"));
250 if (!SymRef)
251 return typeError(ErrorLoc, StringRef("expected symbol operand"));
252 return false;
253}
254
255bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc,
258 const MCSymbolRefExpr *SymRef;
259 if (getSymRef(ErrorLoc, GlobalOp, SymRef))
260 return true;
261 auto *WasmSym = static_cast<const MCSymbolWasm *>(&SymRef->getSymbol());
265 break;
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
284bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCOperand &TableOp,
286 const MCSymbolRefExpr *SymRef;
287 if (getSymRef(ErrorLoc, TableOp, SymRef))
288 return true;
289 auto *WasmSym = static_cast<const MCSymbolWasm *>(&SymRef->getSymbol());
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
298bool WebAssemblyAsmTypeCheck::getSignature(SMLoc ErrorLoc,
302 const MCSymbolRefExpr *SymRef = nullptr;
303 if (getSymRef(ErrorLoc, SigOp, SymRef))
304 return true;
305 auto *WasmSym = static_cast<const MCSymbolWasm *>(&SymRef->getSymbol());
306 Sig = WasmSym->getSignature();
307
308 if (!Sig || WasmSym->getType() != Type) {
309 const char *TypeName = nullptr;
310 switch (Type) {
313 break;
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
327 assert(!BlockInfoStack.empty());
328 const auto &FuncInfo = BlockInfoStack[0];
329 return checkTypes(ErrorLoc, FuncInfo.Sig.Returns, ExactMatch);
330}
331
332
333
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
344bool WebAssemblyAsmTypeCheck::checkTryTable(SMLoc ErrorLoc,
345 const MCInst &Inst) {
346 bool Error = false;
347 unsigned OpIdx = 1;
349 for (int64_t I = 0; I < NumCatches; I++) {
351 std::string ErrorMsgBase =
352 "try_table: catch index " + std::to_string(I) + ": ";
353
361 else
363 }
367 }
368
370 if (Level < BlockInfoStack.size()) {
371 const auto &DestBlockInfo =
372 BlockInfoStack[BlockInfoStack.size() - Level - 1];
374 if (DestBlockInfo.IsLoop)
375 DestTypes = DestBlockInfo.Sig.Params;
376 else
377 DestTypes = DestBlockInfo.Sig.Returns;
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 }
391}
392
397 dumpTypeStack("typechecking " + Name + ": ");
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);
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") {
445 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
446 pushType(Type);
448 }
449 pushType(Any{});
450 return true;
451 }
452
453 if (Name == "table.set") {
454 bool Error = false;
457 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
459 } else {
462 }
463 Error |= popTypes(ErrorLoc, PopTypes);
465 }
466
467 if (Name == "table.size") {
468 bool Error = getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type);
471 }
472
473 if (Name == "table.grow") {
474 bool Error = false;
476 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
478 } else {
481 }
483 Error |= popTypes(ErrorLoc, PopTypes);
486 }
487
488 if (Name == "table.fill") {
489 bool Error = false;
492 if (!getTable(Operands[1]->getStartLoc(), Inst.getOperand(0), Type)) {
494 } else {
497 }
499 Error |= popTypes(ErrorLoc, PopTypes);
501 }
502
503 if (Name == "memory.fill") {
505 bool Error = popType(ErrorLoc, Type);
507 Error |= popType(ErrorLoc, Type);
509 }
510
511 if (Name == "memory.copy") {
513 bool Error = popType(ErrorLoc, Type);
514 Error |= popType(ErrorLoc, Type);
515 Error |= popType(ErrorLoc, Type);
517 }
518
519 if (Name == "memory.init") {
523 Error |= popType(ErrorLoc, Type);
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") {
534
535 Error |= popTypes(ErrorLoc, LastSig.Params);
536 if (Name == "try_table")
537 Error |= checkTryTable(ErrorLoc, Inst);
538
539 BlockInfoStack.push_back({LastSig, Stack.size(), Name == "loop"});
540
541 pushTypes(LastSig.Params);
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
550 const auto &LastBlockInfo = BlockInfoStack.back();
551 bool Error = checkTypes(ErrorLoc, LastBlockInfo.Sig.Returns, true);
552
553 Stack.truncate(LastBlockInfo.StackStartPos);
554 if (Name == "else") {
555
556
557 pushTypes(LastBlockInfo.Sig.Params);
558 } else if (Name == "catch") {
559
560
562 if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
564 pushTypes(Sig->Params);
565 else
567 } else if (Name == "catch_all") {
568
569 } else {
570
571
572 pushTypes(LastBlockInfo.Sig.Returns);
573 BlockInfoStack.pop_back();
574 }
576 }
577
578 if (Name == "br" || Name == "br_if") {
579 bool Error = false;
580 if (Name == "br_if")
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 {
598 typeError(Operands[1]->getStartLoc(), "depth should be an integer");
599 }
600 if (Name == "br")
601 pushType(Polymorphic{});
603 }
604
605 if (Name == "return") {
607 pushType(Polymorphic{});
609 }
610
611 if (Name == "call_indirect" || Name == "return_call_indirect") {
612
614 Error |= checkSig(ErrorLoc, LastSig);
615 if (Name == "return_call_indirect") {
617 pushType(Polymorphic{});
618 }
620 }
621
622 if (Name == "call" || Name == "return_call") {
623 bool Error = false;
625 if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
627 Error |= checkSig(ErrorLoc, *Sig);
628 else
630 if (Name == "return_call") {
632 pushType(Polymorphic{});
633 }
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);
646 }
647
648 if (Name == "throw") {
649 bool Error = false;
651 if (!getSignature(Operands[1]->getStartLoc(), Inst.getOperand(0),
653 Error |= checkSig(ErrorLoc, *Sig);
654 else
656 pushType(Polymorphic{});
658 }
659
660 if (Name == "throw_ref") {
662 pushType(Polymorphic{});
664 }
665
666
667
668
670 assert(RegOpc != -1 && "Failed to get register version of MC instruction");
671 const auto &II = MII.get(RegOpc);
672
674 for (unsigned I = II.getNumDefs(); I < II.getNumOperands(); I++) {
675 const auto &Op = II.operands()[I];
678 }
679 bool Error = popTypes(ErrorLoc, PopTypes);
681
682 for (unsigned I = 0; I < II.getNumDefs(); I++) {
683 const auto &Op = II.operands()[I];
686 }
687 pushTypes(PushTypes);
689}
690
691}
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
MachineInstr unsigned OpIdx
uint64_t IntrinsicInst * II
StringRef getMnemonic(unsigned Opc)
StringRef getMnemonic(unsigned Opc)
This file is part of the WebAssembly Assembler.
static std::string getSignature(FunctionType *FTy)
This file contains the declaration of the WebAssemblyMCAsmInfo class.
This file provides WebAssembly-specific target descriptions.
This file contains the declaration of the WebAssembly-specific type parsing utility functions.
This file registers the WebAssembly target.
This file declares WebAssembly-specific target streamer classes.
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
size_t size() const
size - Get the array size.
Lightweight error class with error context and mandatory checking.
Generic assembler parser interface, for use by target specific assembly parsers.
Instances of this class represent a single low-level machine instruction.
unsigned getOpcode() const
const MCOperand & getOperand(unsigned i) const
Interface to description of machine instruction set.
Instances of this class represent operands of the MCInst class.
const MCExpr * getExpr() const
Represent a reference to a symbol from inside an expression.
const MCSymbol & getSymbol() const
uint16_t getSpecifier() const
Represents a location in source code.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
void push_back(const T &Elt)
reverse_iterator rbegin()
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
The instances of the Type class are immutable: once they are created, they are never changed.
bool endOfFunction(SMLoc ErrorLoc, bool ExactMatch)
Definition WebAssemblyAsmTypeCheck.cpp:326
WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, bool Is64)
Definition WebAssemblyAsmTypeCheck.cpp:46
void funcDecl(const wasm::WasmSignature &Sig)
Definition WebAssemblyAsmTypeCheck.cpp:51
void localDecl(const SmallVectorImpl< wasm::ValType > &Locals)
Definition WebAssemblyAsmTypeCheck.cpp:56
bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst, OperandVector &Operands)
Definition WebAssemblyAsmTypeCheck.cpp:393
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
constexpr char TypeName[]
Key for Kernel::Arg::Metadata::mTypeName.
const char * typeToString(wasm::ValType Type)
wasm::ValType regClassToValType(unsigned RC)
bool isRefType(wasm::ValType Type)
int getRegisterOpcode(unsigned short Opcode)
@ WASM_OPCODE_CATCH_ALL_REF
@ WASM_SYMBOL_TYPE_GLOBAL
@ WASM_SYMBOL_TYPE_FUNCTION
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast - Return the argument parameter cast to the specified type.
static bool compareTypes(ArrayRef< wasm::ValType > TypesA, ArrayRef< wasm::ValType > TypesB)
Definition WebAssemblyAsmTypeCheck.cpp:334
void append_range(Container &C, Range &&R)
Wrapper function to append range R to container C.
OutputIt transform(R &&Range, OutputIt d_first, UnaryFunction F)
Wrapper function around std::transform to apply a function to a range and store the result elsewhere.
SmallVectorImpl< std::unique_ptr< MCParsedAsmOperand > > OperandVector
LLVM_ABI raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
SmallVector< ValType, 1 > Returns
SmallVector< ValType, 4 > Params