LLVM  15.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 surpress errors as well.
78  if (Unreachable)
79  return true;
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.hasValue()
90  ? StringRef("empty stack while popping ") +
91  WebAssembly::typeToString(EVT.getValue())
92  : StringRef(
93  "empty stack while popping value"));
94  }
95  auto PVT = Stack.pop_back_val();
96  if (EVT.hasValue() && EVT.getValue() != PVT) {
97  return typeError(
98  ErrorLoc, StringRef("popped ") + WebAssembly::typeToString(PVT) +
99  ", expected " +
100  WebAssembly::typeToString(EVT.getValue()));
101  }
102  return false;
103 }
104 
105 bool WebAssemblyAsmTypeCheck::popRefType(SMLoc ErrorLoc) {
106  if (Stack.empty()) {
107  return typeError(ErrorLoc, StringRef("empty stack while popping reftype"));
108  }
109  auto PVT = Stack.pop_back_val();
110  if (!WebAssembly::isRefType(PVT)) {
111  return typeError(ErrorLoc, StringRef("popped ") +
113  ", expected reftype");
114  }
115  return false;
116 }
117 
118 bool WebAssemblyAsmTypeCheck::getLocal(SMLoc ErrorLoc, const MCInst &Inst,
119  wasm::ValType &Type) {
120  auto Local = static_cast<size_t>(Inst.getOperand(0).getImm());
121  if (Local >= LocalTypes.size())
122  return typeError(ErrorLoc, StringRef("no local type specified for index ") +
123  std::to_string(Local));
124  Type = LocalTypes[Local];
125  return false;
126 }
127 
128 bool WebAssemblyAsmTypeCheck::checkEnd(SMLoc ErrorLoc, bool PopVals) {
129  if (LastSig.Returns.size() > Stack.size())
130  return typeError(ErrorLoc, "end: insufficient values on the type stack");
131 
132  if (PopVals) {
133  for (auto VT : llvm::reverse(LastSig.Returns)) {
134  if (popType(ErrorLoc, VT))
135  return true;
136  }
137  return false;
138  }
139 
140  for (size_t i = 0; i < LastSig.Returns.size(); i++) {
141  auto EVT = LastSig.Returns[i];
142  auto PVT = Stack[Stack.size() - LastSig.Returns.size() + i];
143  if (PVT != EVT)
144  return typeError(
145  ErrorLoc, StringRef("end got ") + WebAssembly::typeToString(PVT) +
146  ", expected " + WebAssembly::typeToString(EVT));
147  }
148  return false;
149 }
150 
151 bool WebAssemblyAsmTypeCheck::checkSig(SMLoc ErrorLoc,
152  const wasm::WasmSignature& Sig) {
153  for (auto VT : llvm::reverse(Sig.Params))
154  if (popType(ErrorLoc, VT)) return true;
155  Stack.insert(Stack.end(), Sig.Returns.begin(), Sig.Returns.end());
156  return false;
157 }
158 
159 bool WebAssemblyAsmTypeCheck::getSymRef(SMLoc ErrorLoc, const MCInst &Inst,
160  const MCSymbolRefExpr *&SymRef) {
161  auto Op = Inst.getOperand(0);
162  if (!Op.isExpr())
163  return typeError(ErrorLoc, StringRef("expected expression operand"));
164  SymRef = dyn_cast<MCSymbolRefExpr>(Op.getExpr());
165  if (!SymRef)
166  return typeError(ErrorLoc, StringRef("expected symbol operand"));
167  return false;
168 }
169 
170 bool WebAssemblyAsmTypeCheck::getGlobal(SMLoc ErrorLoc, const MCInst &Inst,
171  wasm::ValType &Type) {
172  const MCSymbolRefExpr *SymRef;
173  if (getSymRef(ErrorLoc, Inst, SymRef))
174  return true;
175  auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
176  switch (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA)) {
178  Type = static_cast<wasm::ValType>(WasmSym->getGlobalType().Type);
179  break;
182  switch (SymRef->getKind()) {
186  return false;
187  default:
188  break;
189  }
191  default:
192  return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
193  " missing .globaltype");
194  }
195  return false;
196 }
197 
198 bool WebAssemblyAsmTypeCheck::getTable(SMLoc ErrorLoc, const MCInst &Inst,
199  wasm::ValType &Type) {
200  const MCSymbolRefExpr *SymRef;
201  if (getSymRef(ErrorLoc, Inst, SymRef))
202  return true;
203  auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
204  if (WasmSym->getType().getValueOr(wasm::WASM_SYMBOL_TYPE_DATA) !=
206  return typeError(ErrorLoc, StringRef("symbol ") + WasmSym->getName() +
207  " missing .tabletype");
208  Type = static_cast<wasm::ValType>(WasmSym->getTableType().ElemType);
209  return false;
210 }
211 
213  // Check the return types.
214  for (auto RVT : llvm::reverse(ReturnTypes)) {
215  if (popType(ErrorLoc, RVT))
216  return true;
217  }
218  if (!Stack.empty()) {
219  return typeError(ErrorLoc, std::to_string(Stack.size()) +
220  " superfluous return values");
221  }
222  Unreachable = true;
223  return false;
224 }
225 
228  auto Opc = Inst.getOpcode();
229  auto Name = GetMnemonic(Opc);
230  dumpTypeStack("typechecking " + Name + ": ");
232  if (Name == "local.get") {
233  if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
234  return true;
235  Stack.push_back(Type);
236  } else if (Name == "local.set") {
237  if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
238  return true;
239  if (popType(ErrorLoc, Type))
240  return true;
241  } else if (Name == "local.tee") {
242  if (getLocal(Operands[1]->getStartLoc(), Inst, Type))
243  return true;
244  if (popType(ErrorLoc, Type))
245  return true;
246  Stack.push_back(Type);
247  } else if (Name == "global.get") {
248  if (getGlobal(Operands[1]->getStartLoc(), Inst, Type))
249  return true;
250  Stack.push_back(Type);
251  } else if (Name == "global.set") {
252  if (getGlobal(Operands[1]->getStartLoc(), Inst, Type))
253  return true;
254  if (popType(ErrorLoc, Type))
255  return true;
256  } else if (Name == "table.get") {
257  if (getTable(Operands[1]->getStartLoc(), Inst, Type))
258  return true;
259  if (popType(ErrorLoc, wasm::ValType::I32))
260  return true;
261  Stack.push_back(Type);
262  } else if (Name == "table.set") {
263  if (getTable(Operands[1]->getStartLoc(), Inst, Type))
264  return true;
265  if (popType(ErrorLoc, Type))
266  return true;
267  if (popType(ErrorLoc, wasm::ValType::I32))
268  return true;
269  } else if (Name == "table.fill") {
270  if (getTable(Operands[1]->getStartLoc(), Inst, Type))
271  return true;
272  if (popType(ErrorLoc, wasm::ValType::I32))
273  return true;
274  if (popType(ErrorLoc, Type))
275  return true;
276  if (popType(ErrorLoc, wasm::ValType::I32))
277  return true;
278  } else if (Name == "drop") {
279  if (popType(ErrorLoc, {}))
280  return true;
281  } else if (Name == "end_block" || Name == "end_loop" || Name == "end_if" ||
282  Name == "else" || Name == "end_try") {
283  if (checkEnd(ErrorLoc, Name == "else"))
284  return true;
285  if (Name == "end_block")
286  Unreachable = false;
287  } else if (Name == "return") {
288  if (endOfFunction(ErrorLoc))
289  return true;
290  } else if (Name == "call_indirect" || Name == "return_call_indirect") {
291  // Function value.
292  if (popType(ErrorLoc, wasm::ValType::I32)) return true;
293  if (checkSig(ErrorLoc, LastSig)) return true;
294  if (Name == "return_call_indirect" && endOfFunction(ErrorLoc))
295  return true;
296  } else if (Name == "call" || Name == "return_call") {
297  const MCSymbolRefExpr *SymRef;
298  if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
299  return true;
300  auto WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
301  auto Sig = WasmSym->getSignature();
302  if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_FUNCTION)
303  return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
304  WasmSym->getName() +
305  " missing .functype");
306  if (checkSig(ErrorLoc, *Sig)) return true;
307  if (Name == "return_call" && endOfFunction(ErrorLoc))
308  return true;
309  } else if (Name == "catch") {
310  const MCSymbolRefExpr *SymRef;
311  if (getSymRef(Operands[1]->getStartLoc(), Inst, SymRef))
312  return true;
313  const auto *WasmSym = cast<MCSymbolWasm>(&SymRef->getSymbol());
314  const auto *Sig = WasmSym->getSignature();
315  if (!Sig || WasmSym->getType() != wasm::WASM_SYMBOL_TYPE_TAG)
316  return typeError(Operands[1]->getStartLoc(), StringRef("symbol ") +
317  WasmSym->getName() +
318  " missing .tagtype");
319  // catch instruction pushes values whose types are specified in the tag's
320  // "params" part
321  Stack.insert(Stack.end(), Sig->Params.begin(), Sig->Params.end());
322  } else if (Name == "unreachable") {
323  Unreachable = true;
324  } else if (Name == "ref.is_null") {
325  if (popRefType(ErrorLoc))
326  return true;
327  Stack.push_back(wasm::ValType::I32);
328  } else {
329  // The current instruction is a stack instruction which doesn't have
330  // explicit operands that indicate push/pop types, so we get those from
331  // the register version of the same instruction.
332  auto RegOpc = WebAssembly::getRegisterOpcode(Opc);
333  assert(RegOpc != -1 && "Failed to get register version of MC instruction");
334  const auto &II = MII.get(RegOpc);
335  // First pop all the uses off the stack and check them.
336  for (unsigned I = II.getNumOperands(); I > II.getNumDefs(); I--) {
337  const auto &Op = II.OpInfo[I - 1];
338  if (Op.OperandType == MCOI::OPERAND_REGISTER) {
339  auto VT = WebAssembly::regClassToValType(Op.RegClass);
340  if (popType(ErrorLoc, VT))
341  return true;
342  }
343  }
344  // Now push all the defs onto the stack.
345  for (unsigned I = 0; I < II.getNumDefs(); I++) {
346  const auto &Op = II.OpInfo[I];
347  assert(Op.OperandType == MCOI::OPERAND_REGISTER && "Register expected");
348  auto VT = WebAssembly::regClassToValType(Op.RegClass);
349  Stack.push_back(VT);
350  }
351  }
352  return false;
353 }
354 
355 } // 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::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:99
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:17
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:386
llvm::wasm::WASM_SYMBOL_TYPE_GLOBAL
@ WASM_SYMBOL_TYPE_GLOBAL
Definition: Wasm.h:383
llvm::Type
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
llvm::reverse
auto reverse(ContainerTy &&C, std::enable_if_t< has_rbegin< ContainerTy >::value > *=nullptr)
Definition: STLExtras.h:380
llvm::Optional
Definition: APInt.h:33
llvm::MCInst
Instances of this class represent a single low-level machine instruction.
Definition: MCInst.h:184
llvm::SmallVectorImpl::pop_back_val
LLVM_NODISCARD T pop_back_val()
Definition: SmallVector.h:654
MCTargetAsmParser.h
llvm::WebAssembly::regClassToValType
wasm::ValType regClassToValType(unsigned RC)
Definition: WebAssemblyTypeUtilities.cpp:150
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:1138
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:381
llvm::WebAssemblyAsmTypeCheck::typeCheck
bool typeCheck(SMLoc ErrorLoc, const MCInst &Inst, OperandVector &Operands)
Definition: WebAssemblyAsmTypeCheck.cpp:226
llvm::wasm::ValType
ValType
Definition: Wasm.h:422
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:433
llvm::MCOI::OPERAND_REGISTER
@ OPERAND_REGISTER
Definition: MCInstrDesc.h:60
llvm::wasm::WASM_SYMBOL_TYPE_TAG
@ WASM_SYMBOL_TYPE_TAG
Definition: Wasm.h:385
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:212
llvm::WebAssembly::isRefType
bool isRefType(const Type *Ty)
Definition: WebAssemblyTypeUtilities.h:79
llvm::StringRef
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:58
Compiler.h
LLVM_FALLTHROUGH
#define LLVM_FALLTHROUGH
LLVM_FALLTHROUGH - Mark fallthrough cases in switch statements.
Definition: Compiler.h:280
llvm::AMDGPU::SendMsg::Msg
const CustomOperand< const MCSubtargetInfo & > Msg[]
Definition: AMDGPUAsmUtils.cpp:39
llvm::pdb::PDB_DataKind::Local
@ Local
llvm::Twine
Twine - A lightweight data structure for efficiently representing the concatenation of temporary valu...
Definition: Twine.h:83
llvm::MCInstrInfo
Interface to description of machine instruction set.
Definition: MCInstrInfo.h:26
llvm::GraphProgram::Name
Name
Definition: GraphWriter.h:50
llvm::SmallVectorImpl::assign
void assign(size_type NumElts, ValueParamT Elt)
Definition: SmallVector.h:688
llvm::AMDGPU::SendMsg::Op
Op
Definition: SIDefines.h:341
llvm::MCInst::getOpcode
unsigned getOpcode() const
Definition: MCInst.h:198
llvm::WebAssembly::typeToString
const char * typeToString(wasm::ValType Type)
Definition: WebAssemblyTypeUtilities.cpp:101
MCStreamer.h
llvm::MCInst::getOperand
const MCOperand & getOperand(unsigned i) const
Definition: MCInst.h:206
llvm::to_string
std::string to_string(const T &Value)
Definition: ScopedPrinter.h:86
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::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:434
llvm::wasm::WASM_SYMBOL_TYPE_DATA
@ WASM_SYMBOL_TYPE_DATA
Definition: Wasm.h:382
llvm::wasm::WasmSignature
Definition: Wasm.h:432
MCSectionWasm.h
llvm::SmallVectorImpl::insert
iterator insert(iterator I, T &&Elt)
Definition: SmallVector.h:792