diff options
Diffstat (limited to 'src/wasm-binary.h')
-rw-r--r-- | src/wasm-binary.h | 297 |
1 files changed, 173 insertions, 124 deletions
diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 1f6222cba..aa13685e1 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -25,14 +25,14 @@ #include <ostream> #include <type_traits> -#include "wasm.h" -#include "wasm-traversal.h" -#include "asmjs/shared-constants.h" #include "asm_v_wasm.h" +#include "asmjs/shared-constants.h" +#include "ir/import-utils.h" #include "parsing.h" #include "wasm-builder.h" +#include "wasm-traversal.h" #include "wasm-validator.h" -#include "ir/import-utils.h" +#include "wasm.h" namespace wasm { @@ -49,8 +49,7 @@ enum WebLimitations { MaxFunctionLocals = 50 * 1000 }; -template<typename T, typename MiniT> -struct LEB { +template<typename T, typename MiniT> struct LEB { static_assert(sizeof(MiniT) == 1, "MiniT must be a byte"); T value; @@ -59,8 +58,12 @@ struct LEB { LEB(T value) : value(value) {} bool hasMore(T temp, MiniT byte) { - // for signed, we must ensure the last bit has the right sign, as it will zero extend - return std::is_signed<T>::value ? (temp != 0 && temp != T(-1)) || (value >= 0 && (byte & 64)) || (value < 0 && !(byte & 64)) : (temp != 0); + // for signed, we must ensure the last bit has the right sign, as it will + // zero extend + return std::is_signed<T>::value + ? (temp != 0 && temp != T(-1)) || (value >= 0 && (byte & 64)) || + (value < 0 && !(byte & 64)) + : (temp != 0); } void write(std::vector<uint8_t>* out) { @@ -106,8 +109,8 @@ struct LEB { T payload = byte & 127; typedef typename std::make_unsigned<T>::type mask_type; auto shift_mask = 0 == shift - ? ~mask_type(0) - : ((mask_type(1) << (sizeof(T) * 8 - shift)) - 1u); + ? ~mask_type(0) + : ((mask_type(1) << (sizeof(T) * 8 - shift)) - 1u); T significant_payload = payload & shift_mask; if (significant_payload != payload) { if (!(std::is_signed<T>::value && last)) { @@ -115,7 +118,8 @@ struct LEB { } } value |= significant_payload << shift; - if (last) break; + if (last) + break; shift += 7; if (size_t(shift) >= sizeof(T) * 8) { throw ParseException("LEB overflow"); @@ -130,7 +134,8 @@ struct LEB { value <<= sext_bits; value >>= sext_bits; if (value >= 0) { - throw ParseException(" LEBsign-extend should produce a negative value"); + throw ParseException( + " LEBsign-extend should produce a negative value"); } } } @@ -155,33 +160,48 @@ public: BufferWithRandomAccess(bool debug = false) : debug(debug) {} BufferWithRandomAccess& operator<<(int8_t x) { - if (debug) std::cerr << "writeInt8: " << (int)(uint8_t)x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeInt8: " << (int)(uint8_t)x << " (at " << size() << ")" + << std::endl; push_back(x); return *this; } BufferWithRandomAccess& operator<<(int16_t x) { - if (debug) std::cerr << "writeInt16: " << x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeInt16: " << x << " (at " << size() << ")" << std::endl; push_back(x & 0xff); push_back(x >> 8); return *this; } BufferWithRandomAccess& operator<<(int32_t x) { - if (debug) std::cerr << "writeInt32: " << x << " (at " << size() << ")" << std::endl; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; + if (debug) + std::cerr << "writeInt32: " << x << " (at " << size() << ")" << std::endl; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; push_back(x & 0xff); return *this; } BufferWithRandomAccess& operator<<(int64_t x) { - if (debug) std::cerr << "writeInt64: " << x << " (at " << size() << ")" << std::endl; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; - push_back(x & 0xff); x >>= 8; + if (debug) + std::cerr << "writeInt64: " << x << " (at " << size() << ")" << std::endl; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; + push_back(x & 0xff); + x >>= 8; push_back(x & 0xff); return *this; } @@ -189,7 +209,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeU32LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeU32LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -203,7 +224,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeU64LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeU64LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -217,7 +239,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeS32LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeS32LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -231,7 +254,8 @@ public: size_t before = -1; if (debug) { before = size(); - std::cerr << "writeS64LEB: " << x.value << " (at " << before << ")" << std::endl; + std::cerr << "writeS64LEB: " << x.value << " (at " << before << ")" + << std::endl; } x.write(this); if (debug) { @@ -242,57 +266,63 @@ public: return *this; } - BufferWithRandomAccess& operator<<(uint8_t x) { - return *this << (int8_t)x; - } - BufferWithRandomAccess& operator<<(uint16_t x) { - return *this << (int16_t)x; - } - BufferWithRandomAccess& operator<<(uint32_t x) { - return *this << (int32_t)x; - } - BufferWithRandomAccess& operator<<(uint64_t x) { - return *this << (int64_t)x; - } + BufferWithRandomAccess& operator<<(uint8_t x) { return *this << (int8_t)x; } + BufferWithRandomAccess& operator<<(uint16_t x) { return *this << (int16_t)x; } + BufferWithRandomAccess& operator<<(uint32_t x) { return *this << (int32_t)x; } + BufferWithRandomAccess& operator<<(uint64_t x) { return *this << (int64_t)x; } BufferWithRandomAccess& operator<<(float x) { - if (debug) std::cerr << "writeFloat32: " << x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeFloat32: " << x << " (at " << size() << ")" + << std::endl; return *this << Literal(x).reinterpreti32(); } BufferWithRandomAccess& operator<<(double x) { - if (debug) std::cerr << "writeFloat64: " << x << " (at " << size() << ")" << std::endl; + if (debug) + std::cerr << "writeFloat64: " << x << " (at " << size() << ")" + << std::endl; return *this << Literal(x).reinterpreti64(); } void writeAt(size_t i, uint16_t x) { - if (debug) std::cerr << "backpatchInt16: " << x << " (at " << i << ")" << std::endl; + if (debug) + std::cerr << "backpatchInt16: " << x << " (at " << i << ")" << std::endl; (*this)[i] = x & 0xff; - (*this)[i+1] = x >> 8; + (*this)[i + 1] = x >> 8; } void writeAt(size_t i, uint32_t x) { - if (debug) std::cerr << "backpatchInt32: " << x << " (at " << i << ")" << std::endl; - (*this)[i] = x & 0xff; x >>= 8; - (*this)[i+1] = x & 0xff; x >>= 8; - (*this)[i+2] = x & 0xff; x >>= 8; - (*this)[i+3] = x & 0xff; + if (debug) + std::cerr << "backpatchInt32: " << x << " (at " << i << ")" << std::endl; + (*this)[i] = x & 0xff; + x >>= 8; + (*this)[i + 1] = x & 0xff; + x >>= 8; + (*this)[i + 2] = x & 0xff; + x >>= 8; + (*this)[i + 3] = x & 0xff; } // writes out an LEB to an arbitrary location. this writes the LEB as a full // 5 bytes, the fixed amount that can easily be set aside ahead of time void writeAtFullFixedSize(size_t i, U32LEB x) { - if (debug) std::cerr << "backpatchU32LEB: " << x.value << " (at " << i << ")" << std::endl; - x.writeAt(this, i, MaxLEB32Bytes); // fill all 5 bytes, we have to do this when backpatching + if (debug) + std::cerr << "backpatchU32LEB: " << x.value << " (at " << i << ")" + << std::endl; + // fill all 5 bytes, we have to do this when backpatching + x.writeAt(this, i, MaxLEB32Bytes); } // writes out an LEB of normal size // returns how many bytes were written size_t writeAt(size_t i, U32LEB x) { - if (debug) std::cerr << "writeAtU32LEB: " << x.value << " (at " << i << ")" << std::endl; + if (debug) + std::cerr << "writeAtU32LEB: " << x.value << " (at " << i << ")" + << std::endl; return x.writeAt(this, i); } - template<typename T> - void writeTo(T& o) { - for (auto c : *this) o << c; + template<typename T> void writeTo(T& o) { + for (auto c : *this) + o << c; } std::vector<char> getAsChars() { @@ -305,10 +335,7 @@ public: namespace BinaryConsts { -enum Meta { - Magic = 0x6d736100, - Version = 0x01 -}; +enum Meta { Magic = 0x6d736100, Version = 0x01 }; enum Section { User = 0, @@ -333,10 +360,10 @@ enum SegmentFlag { enum EncodedType { // value_type - i32 = -0x1, // 0x7f - i64 = -0x2, // 0x7e - f32 = -0x3, // 0x7d - f64 = -0x4, // 0x7c + i32 = -0x1, // 0x7f + i64 = -0x2, // 0x7e + f32 = -0x3, // 0x7d + f64 = -0x4, // 0x7c v128 = -0x5, // 0x7b // elem_type AnyFunc = -0x10, // 0x70 @@ -394,7 +421,6 @@ enum ASTNodes { GetGlobal = 0x23, SetGlobal = 0x24, - I32LoadMem = 0x28, I64LoadMem = 0x29, F32LoadMem = 0x2a, @@ -809,15 +835,12 @@ enum BulkMemoryOpcodes { }; enum MemoryAccess { - Offset = 0x10, // bit 4 - Alignment = 0x80, // bit 7 + Offset = 0x10, // bit 4 + Alignment = 0x80, // bit 7 NaturalAlignment = 0 }; -enum MemoryFlags { - HasMaximum = 1 << 0, - IsShared = 1 << 1 -}; +enum MemoryFlags { HasMaximum = 1 << 0, IsShared = 1 << 1 }; enum FeaturePrefix { FeatureUsed = '+', @@ -827,18 +850,30 @@ enum FeaturePrefix { } // namespace BinaryConsts - inline S32LEB binaryType(Type type) { int ret = 0; switch (type) { // None only used for block signatures. TODO: Separate out? - case none: ret = BinaryConsts::EncodedType::Empty; break; - case i32: ret = BinaryConsts::EncodedType::i32; break; - case i64: ret = BinaryConsts::EncodedType::i64; break; - case f32: ret = BinaryConsts::EncodedType::f32; break; - case f64: ret = BinaryConsts::EncodedType::f64; break; - case v128: ret = BinaryConsts::EncodedType::v128; break; - case unreachable: WASM_UNREACHABLE(); + case none: + ret = BinaryConsts::EncodedType::Empty; + break; + case i32: + ret = BinaryConsts::EncodedType::i32; + break; + case i64: + ret = BinaryConsts::EncodedType::i64; + break; + case f32: + ret = BinaryConsts::EncodedType::f32; + break; + case f64: + ret = BinaryConsts::EncodedType::f64; + break; + case v128: + ret = BinaryConsts::EncodedType::v128; + break; + case unreachable: + WASM_UNREACHABLE(); } return S32LEB(ret); } @@ -847,10 +882,8 @@ inline S32LEB binaryType(Type type) { class WasmBinaryWriter { public: - WasmBinaryWriter(Module* input, - BufferWithRandomAccess& o, - bool debug = false) : - wasm(input), o(o), debug(debug) { + WasmBinaryWriter(Module* input, BufferWithRandomAccess& o, bool debug = false) + : wasm(input), o(o), debug(debug) { prepare(); } @@ -859,8 +892,9 @@ public: struct Entry { Name name; size_t offset; // where the entry starts - size_t size; // the size of the entry - Entry(Name name, size_t offset, size_t size) : name(name), offset(offset), size(size) {} + size_t size; // the size of the entry + Entry(Name name, size_t offset, size_t size) + : name(name), offset(offset), size(size) {} }; std::vector<Entry> functionBodies; } tableOfContents; @@ -875,9 +909,11 @@ public: void write(); void writeHeader(); int32_t writeU32LEBPlaceholder(); - void writeResizableLimits(Address initial, Address maximum, bool hasMaximum, bool shared); - template<typename T> - int32_t startSection(T code); + void writeResizableLimits(Address initial, + Address maximum, + bool hasMaximum, + bool shared); + template<typename T> int32_t startSection(T code); void finishSection(int32_t start); int32_t startSubsection(BinaryConsts::UserSections::Subsection code); void finishSubsection(int32_t start); @@ -895,8 +931,10 @@ public: void writeDataCount(); void writeDataSegments(); - std::unordered_map<Name, Index> mappedFunctions; // name of the Function => index. first imports, then internals - std::unordered_map<Name, uint32_t> mappedGlobals; // name of the Global => index. first imported globals, then internal globals + // name of the Function => index. first imports, then internals + std::unordered_map<Name, Index> mappedFunctions; + // name of the Global => index. first imported globals, then internal globals + std::unordered_map<Name, uint32_t> mappedGlobals; uint32_t getFunctionIndex(Name name); uint32_t getGlobalIndex(Name name); @@ -925,13 +963,14 @@ public: const char* data; size_t size; size_t pointerLocation; - Buffer(const char* data, size_t size, size_t pointerLocation) : data(data), size(size), pointerLocation(pointerLocation) {} + Buffer(const char* data, size_t size, size_t pointerLocation) + : data(data), size(size), pointerLocation(pointerLocation) {} }; std::vector<Buffer> buffersToWrite; void emitBuffer(const char* data, size_t size); - void emitString(const char *str); + void emitString(const char* str); void finishUp(); Module* getModule() { return wasm; } @@ -948,9 +987,10 @@ private: MixedArena allocator; - // storage of source map locations until the section is placed at its final location - // (shrinking LEBs may cause changes there) - std::vector<std::pair<size_t, const Function::DebugLocation*>> sourceMapLocations; + // storage of source map locations until the section is placed at its final + // location (shrinking LEBs may cause changes there) + std::vector<std::pair<size_t, const Function::DebugLocation*>> + sourceMapLocations; size_t sourceMapLocationsSizeAtSectionStart; Function::DebugLocation lastDebugLocation; @@ -975,13 +1015,8 @@ class WasmBinaryBuilder { public: WasmBinaryBuilder(Module& wasm, const std::vector<char>& input, bool debug) - : wasm(wasm), - allocator(wasm.allocator), - input(input), - debug(debug), - sourceMap(nullptr), - nextDebugLocation(0, { 0, 0, 0 }), - debugLocation() {} + : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), + sourceMap(nullptr), nextDebugLocation(0, {0, 0, 0}), debugLocation() {} void read(); void readUserSection(size_t payloadLen); @@ -993,7 +1028,8 @@ public: uint32_t getInt32(); uint64_t getInt64(); uint8_t getLaneIndex(size_t lanes); - // it is unsafe to return a float directly, due to ABI issues with the signalling bit + // it is unsafe to return a float directly, due to ABI issues with the + // signalling bit Literal getFloat32Literal(); Literal getFloat64Literal(); Literal getVec128Literal(); @@ -1016,7 +1052,10 @@ public: // gets a name in the combined function import+defined function space Name getFunctionIndexName(Index i); - void getResizableLimits(Address& initial, Address& max, bool& shared, Address defaultIfNoMax); + void getResizableLimits(Address& initial, + Address& max, + bool& shared, + Address defaultIfNoMax); void readImports(); std::vector<FunctionType*> functionTypes; // types of defined functions @@ -1026,12 +1065,20 @@ public: Name getNextLabel(); - // We read functions before we know their names, so we need to backpatch the names later - std::vector<Function*> functions; // we store functions here before wasm.addFunction after we know their names - std::vector<Function*> functionImports; // we store function imports here before wasm.addFunctionImport after we know their names - std::map<Index, std::vector<Call*>> functionCalls; // at index i we have all calls to the function i + // We read functions before we know their names, so we need to backpatch the + // names later + + // we store functions here before wasm.addFunction after we know their names + std::vector<Function*> functions; + // we store function imports here before wasm.addFunctionImport after we know + // their names + std::vector<Function*> functionImports; + // at index i we have all calls to the function i + std::map<Index, std::vector<Call*>> functionCalls; Function* currFunction = nullptr; - Index endOfFunction = -1; // before we see a function (like global init expressions), there is no end of function to check + // before we see a function (like global init expressions), there is no end of + // function to check + Index endOfFunction = -1; // Throws a parsing error if we are not in a function context void requireFunctionContext(const char* error); @@ -1051,22 +1098,24 @@ public: BreakTarget(Name name, int arity) : name(name), arity(arity) {} }; std::vector<BreakTarget> breakStack; - // the names that breaks target. this lets us know if a block has breaks to it or not. + // the names that breaks target. this lets us know if a block has breaks to it + // or not. std::unordered_set<Name> breakTargetNames; std::vector<Expression*> expressionStack; - // set when we know code is unreachable in the sense of the wasm spec: we are in a block - // and after an unreachable element. - // this helps parse stacky wasm code, which can be unsuitable for our IR when unreachable. + // set when we know code is unreachable in the sense of the wasm spec: we are + // in a block and after an unreachable element. this helps parse stacky wasm + // code, which can be unsuitable for our IR when unreachable. bool unreachableInTheWasmSense; - // set when the current code being processed will not be emitted in the output, which is the - // case when it is literally unreachable, for example, + // set when the current code being processed will not be emitted in the + // output, which is the case when it is literally unreachable, for example, // (block $a // (unreachable) // (block $b - // ;; code here is reachable in the wasm sense, even though $b as a whole is not + // ;; code here is reachable in the wasm sense, even though $b as a whole + // ;; is not // (unreachable) // ;; code here is unreachable in the wasm sense // ) @@ -1075,14 +1124,16 @@ public: BinaryConsts::ASTNodes lastSeparator = BinaryConsts::End; - // process a block-type scope, until an end or else marker, or the end of the function + // process a block-type scope, until an end or else marker, or the end of the + // function void processExpressions(); void skipUnreachableCode(); Expression* popExpression(); Expression* popNonVoidExpression(); - std::map<Index, Name> mappedGlobals; // index of the Global => name. first imported globals, then internal globals + // index of the Global => name. first imported globals, then internal globals + std::map<Index, Name> mappedGlobals; Name getGlobalName(Index index); void validateBinary(); // validations that cannot be performed on the Module @@ -1102,9 +1153,7 @@ public: void readFeatures(size_t); // Debug information reading helpers - void setDebugLocations(std::istream* sourceMap_) { - sourceMap = sourceMap_; - } + void setDebugLocations(std::istream* sourceMap_) { sourceMap = sourceMap_; } std::unordered_map<std::string, Index> debugInfoFileIndices; void readNextDebugLocation(); void readSourceMapHeader(); @@ -1122,13 +1171,13 @@ public: void visitIf(If* curr); void visitLoop(Loop* curr); BreakTarget getBreakTarget(int32_t offset); - void visitBreak(Break *curr, uint8_t code); + void visitBreak(Break* curr, uint8_t code); void visitSwitch(Switch* curr); void visitCall(Call* curr); void visitCallIndirect(CallIndirect* curr); void visitGetLocal(GetLocal* curr); - void visitSetLocal(SetLocal *curr, uint8_t code); + void visitSetLocal(SetLocal* curr, uint8_t code); void visitGetGlobal(GetGlobal* curr); void visitSetGlobal(SetGlobal* curr); void readMemoryAccess(Address& alignment, Address& offset); |