LLVM  16.0.0git
WebAssemblyAsmTypeCheck.cpp
Go to the documentation of this file.
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 
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"
29 #include "llvm/MC/MCSectionWasm.h"
30 #include "llvm/MC/MCStreamer.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 
48  const MCInstrInfo &MII, bool is64)
49  : Parser(Parser), MII(MII), is64(is64) {
50 }
51 
53  LocalTypes.assign(Sig.Params.begin(), Sig.Params.end());
54  ReturnTypes.assign(Sig.Returns.begin(), Sig.Returns.end());
55 }
56 
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) {
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 suppress errors completely.
78  if (Unreachable)
79  return false;
80  TypeErrorThisFunction = true;
81  dumpTypeStack("current stack: ");
82  return Parser.Error(ErrorLoc, Msg);
83 }
84 
85 bool WebAssemblyAsmTypeCheck::popType(SMLoc ErrorLoc,
87  if (Stack.empty()) {
88  return typeError(ErrorLoc,
89  EVT ? StringRef("empty stack while popping ") +
91  : StringRef("empty stack while popping value"));
92  }
93  auto PVT = Stack.pop_back_val();
94  if (EVT && EVT.value() != PVT) {
95  return typeError(
96  ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) +
97  ", expected " + WebAssembly::typeToString(EVT.value()));
98  }
99  return false;
100 }
101 
102 bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
103  if (Stack.empty()) {
104  return typeError(ErrorLoc, StringRef("empty stack while popping reftype"));
105  }
106  auto PVT = Stack.pop_back_val();
107  if (!WebAssembly::isRefType(PVT)) {
108  return typeError(ErrorLoc, StringRef("popped ") +
110  ", expected reftype");
111  }
112  return false;
113 }
114 
115 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst,
116  wasm::ValType &Type) {
117  auto Local = static_cast<size_t>(Inst.getOperand(0).getImm());
118  if (Local >= LocalTypes.size())
119  return typeError(ErrorLoc, StringRef("no local type specified for index ") +
120  std::to_string(Local));
121  Type = LocalTypes[Local];
122  return false;
123 }
124 
125 bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc, bool PopVals) {
126  if (LastSig.Returns.size() > Stack.size())
127  return typeError(ErrorLoc, "end: insufficient values on the type stack");
128 
129  if (PopVals) {
130  for (auto VT : llvm::reverse(LastSig.Returns)) {
131  if (popType(ErrorLoc, VT))
132  return true;
133  }
134  return false;
135  }
136 
137  for (size_t i = 0; i < LastSig.Returns.size(); i++) {
138  auto EVT = LastSig.Returns[i];
139  auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i];
140  if (PVT != EVT)
141  return typeError(
142  ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) +
143  ", expected " + WebAssembly::typeToString(EVT));
144  }
145  return false;
146 }
147 
148 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
149  const wasm::WasmSignature& Sig) {
150  for (auto VT : llvm::reverse(Sig.Params))
151  if (popType(ErrorLoc, VT)) return true;
152  Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end());
153  return false;
154 }
155 
156 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
157  const MCSymbolRefExpr *&SymRef) {
158  auto Op = Inst.getOperand(0);
159  if (!Op.isExpr())
160  return typeError(ErrorLoc, StringRef("expected expression operand"));
161  SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr());
162  if (!SymRef)
163  return typeError(ErrorLoc, StringRef("expected symbol operand"));
164  return false;
165 }
166 
167 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
168  wasm::ValType &Type) {
169  const MCSymbolRefExpr *SymRef;
170  if (getSymRef(ErrorLoc, Inst, SymRef))
171  return true;
172  auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
173  switch (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA)) {
175  Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
176  break;
179  switch (SymRef->getKind()) {
183  return false;
184  default:
185  break;
186  }
187  [[fallthrough]];
188  default:
189  return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
190  " missing .globaltype");
191  }
192  return false;
193 }
194 
195 bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCInst &Inst,
196  wasm::ValType &Type) {
197  const MCSymbolRefExpr *SymRef;
198  if (getSymRef(ErrorLoc, Inst, SymRef))
199  return true;
200  auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
201  if (WasmSym->getType().value_or(wasm::WASM_SYMBOL_TYPE_DATA) !=
203  return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
204  " missing .tabletype");
205  Type = static_cast<wasm::ValType>(WasmSym->getTableType().ElemType);
206  return false;
207 }
208 
210  // Check the return types.
211  for (auto RVT : llvm::reverse(ReturnTypes)) {
212  if (popType(ErrorLoc, RVT))
213  return true;
214  }
215  if (!Stack.empty()) {
216  return typeError(ErrorLoc, std::to_string(Stack.size()) +
217  " superfluous return values");
218  }
219  Unreachable = true;
220  return false;
221 }
222 
225  auto Opc = Inst.getOpcode();
226  auto Name = GetMnemonic(Opc);
227  dumpTypeStack("typechecking " + Name + ": ");
229  if (Name == "local.get") {
230  if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
231  return true;
232  Stack.push_back(Type);
233  } else if (Name == "local.set") {
234  if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
235  return true;
236  if (popType(ErrorLoc, Type))
237  return true;
238  } else if (Name == "local.tee") {
239  if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
240  return true;
241  if (popType(ErrorLoc, Type))
242  return true;
243  Stack.push_back(Type);
244  } else if (Name == "global.get") {
245  if (getGlobal(Operands[1]->getStartLoc(), Inst, Type))
246  return true;
247  Stack.push_back(Type);
248  } else if (Name == "global.set") {
249  if (getGlobal(Operands[1]->getStartLoc(), Inst, Type))
250  return true;
251  if (popType(ErrorLoc, Type))
252  return true;
253  } else if (Name == "table.get") {
254  if (getTable(Operands[1]->getStartLoc(), Inst, Type))
255  return true;
256  if (popType(ErrorLoc, wasm::ValType::I32))
257  return true;
258  Stack.push_back(Type);
259  } else if (Name == "table.set") {
260  if (getTable(Operands[1]->getStartLoc(), Inst, Type))
261  return true;
262  if (popType(ErrorLoc, Type))
263  return true;
264  if (popType(ErrorLoc, wasm::ValType::I32))
265  return true;
266  } else if (Name == "table.fill") {
267  if (getTable(Operands[1]->getStartLoc(), Inst, Type))
268  return true;
269  if (popType(ErrorLoc, wasm::ValType::I32))
270  return true;
271  if (popType(ErrorLoc, Type))
272  return true;
273  if (popType(ErrorLoc, wasm::ValType::I32))
274  return true;
275  } else if (Name == "drop") {
276  if (popType(ErrorLoc, {}))
277  return true;
278  } else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
279  Name == "else" || Name == "end_try") {
280  if (checkEnd(ErrorLoc, Name == "else"))
281  return true;
282  if (Name == "end_block")
283  Unreachable = false;
284  } else if (Name == "return") {
285  if (endOfFunction(ErrorLoc))
286  return true;
287  } else if (Name == "call_indirect" || Name == "return_call_indirect") {
288  // Function value.
289  if (popType(ErrorLoc, wasm::ValType::I32)) return true;
290  if (checkSig(ErrorLoc, LastSig)) return true;
291  if (Name == "return_call_indirect" && endOfFunction(ErrorLoc))
292  return true;
293  } else if (Name == "call" || Name == "return_call") {
294  const MCSymbolRefExpr *SymRef;
295  if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
296  return true;
297  auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
298  auto Sig = WasmSym->getSignature();
299  if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION)
300  return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
301  WasmSym->getName() +
302  " missing .functype");
303  if (checkSig(ErrorLoc, *Sig)) return true;
304  if (Name == "return_call" && endOfFunction(ErrorLoc))
305  return true;
306  } else if (Name == "catch") {
307  const MCSymbolRefExpr *SymRef;
308  if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
309  return true;
310  const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
311  const auto *Sig = WasmSym->getSignature();
312  if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG)
313  return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
314  WasmSym->getName() +
315  " missing .tagtype");
316  // catch instruction pushes values whose types are specified in the tag's
317  // "params" part
318  Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end());
319  } else if (Name == "unreachable") {
320  Unreachable = true;
321  } else if (Name == "ref.is_null") {
322  if (popRefType(ErrorLoc))
323  return true;
324  Stack.push_back(wasm::ValType::I32);
325  } else {
326  // The current instruction is a stack instruction which doesn't have
327  // explicit operands that indicate push/pop types, so we get those from
328  // the register version of the same instruction.
329  auto RegOpc = WebAssembly::getRegisterOpcode(Opc);
330  assert(RegOpc != -1 && "Failed to get register version of MC instruction");
331  const auto &II = MII.get(RegOpc);
332  // First pop all the uses off the stack and check them.
333  for (unsigned I = II.getNumOperands(); I > II.getNumDefs(); I--) {
334  const auto &Op = II.OpInfo[I - 1];
335  if (Op.OperandType == MCOI::OPERAND_REGISTER) {
336  auto VT = WebAssembly::regClassToValType(Op.RegClass);
337  if (popType(ErrorLoc, VT))
338  return true;
339  }
340  }
341  // Now push all the defs onto the stack.
342  for (unsigned I = 0; I < II.getNumDefs(); I++) {
343  const auto &Op = II.OpInfo[I];
344  assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected");
345  auto VT = WebAssembly::regClassToValType(Op.RegClass);
346  Stack.push_back(VT);
347  }
348  }
349  return false;
350 }
351 
352 } // end namespace llvm
llvm::MCSymbolRefExpr::getKind
VariantKind getKind() const
Definition: MCExpr.h:401
i
i
Definition: README.txt:29
llvm::MCAsmParser
Generic assembler parser interface, for use by target specific assembly parsers.
Definition: MCAsmParser.h:124
llvm::logicalview::LVAttributeKind::Local
@ Local
llvm::MCAsmParser::Error
bool Error(SMLoc L, const Twine &Msg, SMRange Range=None)
Return an error at the location L, with the message Msg.
Definition: MCAsmParser.cpp:101
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
llvm::wasm::ValType::I32
@ I32
WebAssembly.h
llvm::MCSymbolRefExpr::VK_GOT
@ VK_GOT
Definition: MCExpr.h:198
llvm::SmallVector< wasm::ValType, 4 >
MCParsedAsmOperand.h
llvm::wasm::WASM_SYMBOL_TYPE_TABLE
@ WASM_SYMBOL_TYPE_TABLE
Definition: Wasm.h:388
llvm::wasm::WASM_SYMBOL_TYPE_GLOBAL
@ WASM_SYMBOL_TYPE_GLOBAL
Definition: Wasm.h:385
llvm::Type
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
llvm::Optional
Definition: APInt.h:33
llvm::MCInst
Instances of this class represent a single low-level machine instruction.
Definition: MCInst.h:184
MCTargetAsmParser.h
llvm::WebAssembly::regClassToValType
wasm::ValType regClassToValType(unsigned RC)
Definition: WebAssemblyTypeUtilities.cpp:151
llvm::WebAssemblyAsmTypeCheck::WebAssemblyAsmTypeCheck
WebAssemblyAsmTypeCheck(MCAsmParser &Parser, const MCInstrInfo &MII, bool is64)
Definition: WebAssemblyAsmTypeCheck.cpp:47
LLVM_DEBUG
#define LLVM_DEBUG(X)
Definition: Debug.h:101
GetMnemonic
StringRef GetMnemonic(unsigned Opc)
Definition: WebAssemblyAsmParser.cpp:1126
llvm::dbgs
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
llvm::WebAssemblyAsmTypeCheck::localDecl
void localDecl(const SmallVector< wasm::ValType, 4 > &Locals)
Definition: WebAssemblyAsmTypeCheck.cpp:57
WebAssemblyAsmTypeCheck.h
llvm::SMLoc
Represents a location in source code.
Definition: SMLoc.h:23
llvm::EVT
Extended Value Type.
Definition: ValueTypes.h:34
MCSymbolWasm.h
MCContext.h
MCInstrInfo.h
llvm::MCOperand::getImm
int64_t getImm() const
Definition: MCInst.h:80
MCSymbol.h
MCInst.h
MCSubtargetInfo.h
WebAssemblyTypeUtilities.h
llvm::MCSymbolRefExpr::getSymbol
const MCSymbol & getSymbol() const
Definition: MCExpr.h:399
llvm::wasm::WASM_SYMBOL_TYPE_FUNCTION
@ WASM_SYMBOL_TYPE_FUNCTION
Definition: Wasm.h:383
llvm::WebAssemblyAsmTypeCheck::typeCheck
bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst, OperandVector &Operands)
Definition: WebAssemblyAsmTypeCheck.cpp:223
llvm::wasm::ValType
ValType
Definition: Wasm.h:424
SourceMgr.h
WebAssemblyUtilities.h
WebAssemblyMCTargetDesc.h
Operands
mir Rename Register Operands
Definition: MIRNamerPass.cpp:74
WebAssemblyTargetStreamer.h
llvm::MCSymbolRefExpr::VK_WASM_GOT_TLS
@ VK_WASM_GOT_TLS
Definition: MCExpr.h:332
s
multiplies can be turned into SHL s
Definition: README.txt:370
llvm::wasm::ValType::I64
@ I64
llvm::WebAssembly::getRegisterOpcode
int getRegisterOpcode(unsigned short Opcode)
I
#define I(x, y, z)
Definition: MD5.cpp:58
WebAssemblyTargetInfo.h
llvm::wasm::WasmSignature::Returns
SmallVector< ValType, 1 > Returns
Definition: Wasm.h:435
llvm::MCOI::OPERAND_REGISTER
@ OPERAND_REGISTER
Definition: MCInstrDesc.h:60
llvm::wasm::WASM_SYMBOL_TYPE_TAG
@ WASM_SYMBOL_TYPE_TAG
Definition: Wasm.h:387
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::MCSymbolRefExpr
Represent a reference to a symbol from inside an expression.
Definition: MCExpr.h:192
llvm::WebAssemblyAsmTypeCheck::endOfFunction
bool endOfFunction(SMLoc ErrorLoc)
Definition: WebAssemblyAsmTypeCheck.cpp:209
llvm::WebAssembly::isRefType
bool isRefType(const Type *Ty)
Definition: WebAssemblyTypeUtilities.h:82
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
Compiler.h
llvm::AMDGPU::SendMsg::Msg
const CustomOperand< const MCSubtargetInfo & > Msg[]
Definition: AMDGPUAsmUtils.cpp:39
llvm::Twine
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:81
llvm::MCInstrInfo
Interface to description of machine instruction set.
Definition: MCInstrInfo.h:26
llvm::SmallVectorImpl::assign
void assign(size_type NumElts, ValueParamT Elt)
Definition: SmallVector.h:708
llvm::AMDGPU::SendMsg::Op
Op
Definition: SIDefines.h:348
llvm::MCInst::getOpcode
unsigned getOpcode() const
Definition: MCInst.h:198
llvm::WebAssembly::typeToString
const char * typeToString(wasm::ValType Type)
Definition: WebAssemblyTypeUtilities.cpp:102
MCStreamer.h
llvm::MCInst::getOperand
const MCOperand & getOperand(unsigned i) const
Definition: MCInst.h:206
llvm::SmallVectorImpl::pop_back_val
T pop_back_val()
Definition: SmallVector.h:677
llvm::to_string
std::string to_string(const T &Value)
Definition: ScopedPrinter.h:85
llvm::SmallVectorImpl
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
Definition: APFloat.h:42
llvm::WebAssemblyAsmTypeCheck::funcDecl
void funcDecl(const wasm::WasmSignature &Sig)
Definition: WebAssemblyAsmTypeCheck.cpp:52
llvm::reverse
auto reverse(ContainerTy &&C)
Definition: STLExtras.h:485
llvm::MCInstrInfo::get
const MCInstrDesc & get(unsigned Opcode) const
Return the machine instruction descriptor that corresponds to the specified instruction opcode.
Definition: MCInstrInfo.h:63
Endian.h
TargetRegistry.h
MCExpr.h
llvm::wasm::WasmSignature::Params
SmallVector< ValType, 4 > Params
Definition: Wasm.h:436
llvm::wasm::WASM_SYMBOL_TYPE_DATA
@ WASM_SYMBOL_TYPE_DATA
Definition: Wasm.h:384
llvm::wasm::WasmSignature
Definition: Wasm.h:434
MCSectionWasm.h
llvm::SmallVectorImpl::insert
iterator insert(iterator I, T &&Elt)
Definition: SmallVector.h:809