blob: 9ab261b545cdfb87f2eccf86fb4d8cab0f39ace1 [file] [log] [blame]
/*
* Copyright 2015 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Parses WebAssembly code in S-Expression format, as in .wast files
// such as are in the spec test suite.
//
#ifndef wasm_wasm_s_parser_h
#define wasm_wasm_s_parser_h
#include "mixed_arena.h"
#include "parsing.h" // for UniqueNameMapper. TODO: move dependency to cpp file?
#include "wasm-builder.h"
#include "wasm.h"
namespace wasm {
class SourceLocation {
public:
cashew::IString filename;
uint32_t line;
uint32_t column;
SourceLocation(cashew::IString filename_,
uint32_t line_,
uint32_t column_ = 0)
: filename(filename_), line(line_), column(column_) {}
};
//
// An element in an S-Expression: a list or a string
//
class Element {
typedef ArenaVector<Element*> List;
bool isList_ = true;
List list_;
cashew::IString str_;
bool dollared_;
bool quoted_;
public:
Element(MixedArena& allocator) : list_(allocator) {}
bool isList() const { return isList_; }
bool isStr() const { return !isList_; }
bool dollared() const { return isStr() && dollared_; }
bool quoted() const { return isStr() && quoted_; }
size_t line = -1;
size_t col = -1;
// original locations at the start/end of the S-Expression list
SourceLocation* startLoc = nullptr;
SourceLocation* endLoc = nullptr;
// list methods
List& list();
Element* operator[](unsigned i);
size_t size() { return list().size(); }
List::Iterator begin() { return list().begin(); }
List::Iterator end() { return list().end(); }
// string methods
cashew::IString str() const;
const char* c_str() const;
Element* setString(cashew::IString str__, bool dollared__, bool quoted__);
Element* setMetadata(size_t line_, size_t col_, SourceLocation* startLoc_);
// comparisons
bool operator==(Name name) { return isStr() && str() == name; }
template<typename T> bool operator!=(T t) { return !(*this == t); }
// printing
friend std::ostream& operator<<(std::ostream& o, Element& e);
void dump();
};
//
// Generic S-Expression parsing into lists
//
class SExpressionParser {
char* input;
size_t line;
char* lineStart;
SourceLocation* loc = nullptr;
MixedArena allocator;
public:
// Assumes control of and modifies the input.
SExpressionParser(char* input);
Element* root;
private:
Element* parse();
void skipWhitespace();
void parseDebugLocation();
Element* parseString();
};
//
// SExpressions => WebAssembly module
//
class SExpressionWasmBuilder {
Module& wasm;
MixedArena& allocator;
IRProfile profile;
// The main list of types declared in the module
std::vector<HeapType> types;
std::unordered_map<std::string, size_t> typeIndices;
std::vector<Name> functionNames;
std::vector<Name> tableNames;
std::vector<Name> globalNames;
std::vector<Name> tagNames;
int functionCounter = 0;
int globalCounter = 0;
int tagCounter = 0;
int tableCounter = 0;
int elemCounter = 0;
int memoryCounter = 0;
// we need to know function return types before we parse their contents
std::map<Name, HeapType> functionTypes;
std::unordered_map<cashew::IString, Index> debugInfoFileIndices;
// Maps type indexes to a mapping of field index => name. This is not the same
// as the field names stored on the wasm object, as that maps types after
// their canonicalization. Canonicalization loses information, which means
// that structurally identical types cannot have different names. However,
// while parsing the text format we keep this mapping of type indexes to names
// which does allow reading such content.
std::unordered_map<size_t, std::unordered_map<size_t, Name>> fieldNames;
public:
// Assumes control of and modifies the input.
SExpressionWasmBuilder(Module& wasm, Element& module, IRProfile profile);
private:
void preParseHeapTypes(Element& module);
// pre-parse types and function definitions, so we know function return types
// before parsing their contents
void preParseFunctionType(Element& s);
bool isImport(Element& curr);
void preParseImports(Element& curr);
void parseModuleElement(Element& curr);
// function parsing state
std::unique_ptr<Function> currFunction;
bool brokeToAutoBlock;
UniqueNameMapper nameMapper;
Name getFunctionName(Element& s);
Name getTableName(Element& s);
Name getGlobalName(Element& s);
Name getTagName(Element& s);
void parseStart(Element& s) { wasm.addStart(getFunctionName(*s[1])); }
// returns the next index in s
size_t parseFunctionNames(Element& s, Name& name, Name& exportName);
void parseFunction(Element& s, bool preParseImport = false);
Type stringToType(cashew::IString str,
bool allowError = false,
bool prefix = false) {
return stringToType(str.str, allowError, prefix);
}
Type
stringToType(const char* str, bool allowError = false, bool prefix = false);
HeapType stringToHeapType(cashew::IString str, bool prefix = false) {
return stringToHeapType(str.str, prefix);
}
HeapType stringToHeapType(const char* str, bool prefix = false);
Type elementToType(Element& s);
Type stringToLaneType(const char* str);
bool isType(cashew::IString str) {
return stringToType(str, true) != Type::none;
}
HeapType getFunctionType(Name name, Element& s);
public:
Expression* parseExpression(Element* s) { return parseExpression(*s); }
Expression* parseExpression(Element& s);
Module& getModule() { return wasm; }
private:
Expression* makeExpression(Element& s);
Expression* makeUnreachable();
Expression* makeNop();
Expression* makeBinary(Element& s, BinaryOp op);
Expression* makeUnary(Element& s, UnaryOp op);
Expression* makeSelect(Element& s);
Expression* makeDrop(Element& s);
Expression* makeMemorySize(Element& s);
Expression* makeMemoryGrow(Element& s);
Index getLocalIndex(Element& s);
Expression* makeLocalGet(Element& s);
Expression* makeLocalTee(Element& s);
Expression* makeLocalSet(Element& s);
Expression* makeGlobalGet(Element& s);
Expression* makeGlobalSet(Element& s);
Expression* makeBlock(Element& s);
Expression* makeThenOrElse(Element& s);
Expression* makeConst(Element& s, Type type);
Expression* makeLoad(Element& s, Type type, bool isAtomic);
Expression* makeStore(Element& s, Type type, bool isAtomic);
Expression* makeAtomicRMWOrCmpxchg(Element& s, Type type);
Expression*
makeAtomicRMW(Element& s, Type type, uint8_t bytes, const char* extra);
Expression*
makeAtomicCmpxchg(Element& s, Type type, uint8_t bytes, const char* extra);
Expression* makeAtomicWait(Element& s, Type type);
Expression* makeAtomicNotify(Element& s);
Expression* makeAtomicFence(Element& s);
Expression* makeSIMDExtract(Element& s, SIMDExtractOp op, size_t lanes);
Expression* makeSIMDReplace(Element& s, SIMDReplaceOp op, size_t lanes);
Expression* makeSIMDShuffle(Element& s);
Expression* makeSIMDTernary(Element& s, SIMDTernaryOp op);
Expression* makeSIMDShift(Element& s, SIMDShiftOp op);
Expression* makeSIMDLoad(Element& s, SIMDLoadOp op);
Expression* makeSIMDLoadStoreLane(Element& s, SIMDLoadStoreLaneOp op);
Expression* makeMemoryInit(Element& s);
Expression* makeDataDrop(Element& s);
Expression* makeMemoryCopy(Element& s);
Expression* makeMemoryFill(Element& s);
Expression* makePush(Element& s);
Expression* makePop(Element& s);
Expression* makeIf(Element& s);
Expression* makeMaybeBlock(Element& s, size_t i, Type type);
Expression* makeLoop(Element& s);
Expression* makeCall(Element& s, bool isReturn);
Expression* makeCallIndirect(Element& s, bool isReturn);
template<class T> void parseOperands(Element& s, Index i, Index j, T& list) {
while (i < j) {
list.push_back(parseExpression(s[i]));
i++;
}
}
template<class T>
void parseCallOperands(Element& s, Index i, Index j, T* call) {
parseOperands(s, i, j, call->operands);
}
enum class LabelType { Break, Exception };
Name getLabel(Element& s, LabelType labelType = LabelType::Break);
Expression* makeBreak(Element& s);
Expression* makeBreakTable(Element& s);
Expression* makeReturn(Element& s);
Expression* makeRefNull(Element& s);
Expression* makeRefIs(Element& s, RefIsOp op);
Expression* makeRefFunc(Element& s);
Expression* makeRefEq(Element& s);
Expression* makeTableGet(Element& s);
Expression* makeTableSet(Element& s);
Expression* makeTableSize(Element& s);
Expression* makeTableGrow(Element& s);
Expression* makeTry(Element& s);
Expression* makeTryOrCatchBody(Element& s, Type type, bool isTry);
Expression* makeThrow(Element& s);
Expression* makeRethrow(Element& s);
Expression* makeTupleMake(Element& s);
Expression* makeTupleExtract(Element& s);
Expression* makeCallRef(Element& s, bool isReturn);
Expression* makeI31New(Element& s);
Expression* makeI31Get(Element& s, bool signed_);
Expression* makeRefTest(Element& s);
Expression* makeRefTestStatic(Element& s);
Expression* makeRefCast(Element& s);
Expression* makeRefCastStatic(Element& s);
Expression* makeBrOn(Element& s, BrOnOp op);
Expression* makeBrOnStatic(Element& s, BrOnOp op);
Expression* makeRttCanon(Element& s);
Expression* makeRttSub(Element& s);
Expression* makeRttFreshSub(Element& s);
Expression* makeStructNew(Element& s, bool default_);
Expression* makeStructNewStatic(Element& s, bool default_);
Index getStructIndex(Element& type, Element& field);
Expression* makeStructGet(Element& s, bool signed_ = false);
Expression* makeStructSet(Element& s);
Expression* makeArrayNew(Element& s, bool default_);
Expression* makeArrayNewStatic(Element& s, bool default_);
Expression* makeArrayInit(Element& s);
Expression* makeArrayInitStatic(Element& s);
Expression* makeArrayGet(Element& s, bool signed_ = false);
Expression* makeArraySet(Element& s);
Expression* makeArrayLen(Element& s);
Expression* makeArrayCopy(Element& s);
Expression* makeRefAs(Element& s, RefAsOp op);
// Helper functions
Type parseOptionalResultType(Element& s, Index& i);
Index parseMemoryLimits(Element& s, Index i);
Index parseMemoryIndex(Element& s, Index i);
std::vector<Type> parseParamOrLocal(Element& s);
std::vector<NameType> parseParamOrLocal(Element& s, size_t& localIndex);
std::vector<Type> parseResults(Element& s);
HeapType parseTypeRef(Element& s);
size_t parseTypeUse(Element& s,
size_t startPos,
HeapType& functionType,
std::vector<NameType>& namedParams);
size_t parseTypeUse(Element& s, size_t startPos, HeapType& functionType);
void stringToBinary(const char* input, size_t size, std::vector<char>& data);
void parseMemory(Element& s, bool preParseImport = false);
void parseData(Element& s);
void parseInnerData(
Element& s, Index i, Name name, Expression* offset, bool isPassive);
void parseExport(Element& s);
void parseImport(Element& s);
void parseGlobal(Element& s, bool preParseImport = false);
void parseTable(Element& s, bool preParseImport = false);
void parseElem(Element& s, Table* table = nullptr);
ElementSegment* parseElemFinish(Element& s,
std::unique_ptr<ElementSegment>& segment,
Index i = 1,
bool usesExpressions = false);
// Parses something like (func ..), (array ..), (struct)
HeapType parseHeapType(Element& s);
void parseTag(Element& s, bool preParseImport = false);
Function::DebugLocation getDebugLocation(const SourceLocation& loc);
// Struct/Array instructions have an unnecessary heap type that is just for
// validation (except for the case of unreachability, but that's not a problem
// anyhow, we can ignore it there). That is, we also have a reference / rtt
// child from which we can infer the type anyhow, and we just need to check
// that type is the same.
void
validateHeapTypeUsingChild(Expression* child, HeapType heapType, Element& s);
};
} // namespace wasm
#endif // wasm_wasm_s_parser_h