From 1b45938aadd6e03e9210d88436be9c393623fb42 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Fri, 4 Mar 2016 17:09:47 -0800 Subject: Make initial and max memory sizes be in pages instead of bytes The AST and everything that uses it treats the values as pages. Javascript continues to use bytes. This matches v8 and sexpr-wasm, and the consensus from live discussion and PR209 in the spec. --- src/asm2wasm-main.cpp | 7 ++++++- src/binaryen-shell.cpp | 4 ++-- src/s2wasm.h | 17 ++++++++++++----- src/wasm-binary.h | 23 ++++++----------------- src/wasm-interpreter.h | 2 +- src/wasm-js.cpp | 16 +++++++++++++--- src/wasm.h | 4 +++- 7 files changed, 43 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/src/asm2wasm-main.cpp b/src/asm2wasm-main.cpp index 5ab8bb0cf..73f6853d8 100644 --- a/src/asm2wasm-main.cpp +++ b/src/asm2wasm-main.cpp @@ -58,6 +58,11 @@ int main(int argc, const char *argv[]) { const auto &tm_it = options.extra.find("total memory"); size_t totalMemory = tm_it == options.extra.end() ? 16 * 1024 * 1024 : atoi(tm_it->second.c_str()); + if (totalMemory & ~Memory::kPageMask) { + std::cerr << "Error: total memory size " << totalMemory << + " is not a multiple of the 64k wasm page size\n"; + exit(EXIT_FAILURE); + } Asm2WasmPreProcessor pre; auto input( @@ -75,7 +80,7 @@ int main(int argc, const char *argv[]) { if (options.debug) std::cerr << "wasming..." << std::endl; AllocatingModule wasm; - wasm.memory.initial = wasm.memory.max = totalMemory; + wasm.memory.initial = wasm.memory.max = totalMemory / Memory::kPageSize; Asm2WasmBuilder asm2wasm(wasm, pre.memoryGrowth, options.debug); asm2wasm.processAsm(asmjs); diff --git a/src/binaryen-shell.cpp b/src/binaryen-shell.cpp index 61cab723c..bfed8277e 100644 --- a/src/binaryen-shell.cpp +++ b/src/binaryen-shell.cpp @@ -111,10 +111,10 @@ struct ShellExternalInterface : ModuleInstance::ExternalInterface { ShellExternalInterface() : memory() {} void init(Module& wasm) override { - memory.resize(wasm.memory.initial); + memory.resize(wasm.memory.initial * wasm::Memory::kPageSize); // apply memory segments for (auto segment : wasm.memory.segments) { - assert(segment.offset + segment.size <= wasm.memory.initial); + assert(segment.offset + segment.size <= wasm.memory.initial * wasm::Memory::kPageSize); for (size_t i = 0; i != segment.size; ++i) { memory.set(segment.offset + i, segment.data[i]); } diff --git a/src/s2wasm.h b/src/s2wasm.h index b1c5f7c54..f87742bd0 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -55,7 +55,8 @@ class S2WasmBuilder { ignoreUnknownSymbols(ignoreUnknownSymbols), startFunction(startFunction), globalBase(globalBase), - nextStatic(globalBase) { + nextStatic(globalBase), + initialMemory(0) { s = input; scan(); s = input; @@ -75,6 +76,7 @@ class S2WasmBuilder { size_t globalBase, // where globals can start to be statically allocated, i.e., the data segment nextStatic; // location of next static allocation std::map staticAddresses; // name => address + size_t initialMemory; // Initial size (in bytes) of memory (after linking, this is rounded and set on the wasm object in pages) struct Relocation { uint32_t* data; @@ -396,7 +398,7 @@ class S2WasmBuilder { addressSegments[nextStatic] = wasm.memory.segments.size(); wasm.memory.segments.emplace_back( nextStatic, reinterpret_cast(raw), pointerSize); - wasm.memory.initial = nextStatic + pointerSize; + initialMemory = nextStatic + pointerSize; } nextStatic += pointerSize; } @@ -407,7 +409,7 @@ class S2WasmBuilder { nextStatic = (nextStatic + 15) & static_cast(-16); staticAddresses[".stack"] = nextStatic; nextStatic += stackAllocation; - wasm.memory.initial = nextStatic; + initialMemory = nextStatic; } void process() { @@ -1154,7 +1156,7 @@ class S2WasmBuilder { wasm.memory.segments.emplace_back(nextStatic, (const char*)&(*raw)[0], size); } nextStatic += size; - wasm.memory.initial = nextStatic; + initialMemory = nextStatic; } void parseLcomm(Name name, size_t align=1) { @@ -1168,7 +1170,7 @@ class S2WasmBuilder { while (nextStatic % align) nextStatic++; staticAddresses[name] = nextStatic; nextStatic += size; - wasm.memory.initial = nextStatic; + initialMemory = nextStatic; } void skipImports() { @@ -1183,6 +1185,11 @@ class S2WasmBuilder { } void fix() { + // Round the memory size up to a page, and update the page-increment versions + // of initial and max + wasm.memory.initial = ((initialMemory + Memory::kPageSize - 1) & Memory::kPageMask) / + Memory::kPageSize; + auto ensureFunctionIndex = [&](Name name) { if (functionIndexes.count(name) == 0) { functionIndexes[name] = wasm.table.names.size(); diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 8a03ca843..47da374c1 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -402,18 +402,10 @@ public: void writeMemory() { if (wasm->memory.max == 0) return; if (debug) std::cerr << "== writeMemory" << std::endl; - o << int8_t(BinaryConsts::Memory); - if (wasm->memory.initial == 0) { // XXX diverge from v8, 0 means 0, 1 and above are powers of 2 starting at 0 - o << int8_t(0); - } else { - o << int8_t(std::min(ceil(log2(wasm->memory.initial)), 31.0) + 1); // up to 31 bits, don't let ceil get us to UINT_MAX which can overflow - } - if (wasm->memory.max == 0) { - o << int8_t(0); - } else { - o << int8_t(std::min(ceil(log2(wasm->memory.max)), 31.0) + 1); - } - o << int8_t(1); // export memory + o << int8_t(BinaryConsts::Memory) + << LEB128(wasm->memory.initial) + << LEB128(wasm->memory.max) + << int8_t(1); // export memory } void writeSignatures() { @@ -1088,10 +1080,8 @@ public: void readMemory() { if (debug) std::cerr << "== readMemory" << std::endl; - size_t initial = getInt8(); - wasm.memory.initial = initial == 0 ? 0 : std::pow(2, initial - 1); - size_t max = getInt8(); - wasm.memory.max = max == 0 ? 0 : std::pow(2, max - 1); + wasm.memory.initial = getLEB128(); + wasm.memory.max = getLEB128(); verifyInt8(1); // export memory } @@ -1671,4 +1661,3 @@ public: } // namespace wasm #endif // wasm_wasm_binary_h - diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 223a9e1a5..2edaad846 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -102,7 +102,7 @@ public: Module& wasm; ModuleInstance(Module& wasm, ExternalInterface* externalInterface) : wasm(wasm), externalInterface(externalInterface) { - memorySize = wasm.memory.initial; + memorySize = wasm.memory.initial * Memory::kPageSize; externalInterface->init(wasm); if (wasm.start.is()) { LiteralList arguments; diff --git a/src/wasm-js.cpp b/src/wasm-js.cpp index 7b9afab30..000092da8 100644 --- a/src/wasm-js.cpp +++ b/src/wasm-js.cpp @@ -67,9 +67,14 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_asm2wasm(char *input) { Ref asmjs = builder.parseToplevel(input); module = new AllocatingModule(); - module->memory.initial = EM_ASM_INT_V({ + uint32_t providedMemory = EM_ASM_INT_V({ return Module['providedTotalMemory']; // we receive the size of memory from emscripten }); + if (providedMemory & ~Memory::kPageMask) { + std::cerr << "Error: provided memory is not a multiple of the 64k wasm page size\n"; + exit(EXIT_FAILURE); + } + module->memory.initial = providedMemory / Memory::kPageSize; module->memory.max = pre.memoryGrowth ? -1 : module->memory.initial; if (wasmJSDebug) std::cerr << "wasming...\n"; @@ -114,9 +119,14 @@ extern "C" void EMSCRIPTEN_KEEPALIVE load_s_expr2wasm(char *input, char *mappedG abort(); }); - module->memory.initial = EM_ASM_INT_V({ + uint32_t providedMemory = EM_ASM_INT_V({ return Module['providedTotalMemory']; // we receive the size of memory from emscripten }); + if (providedMemory & ~Memory::kPageMask) { + std::cerr << "Error: provided memory is not a multiple of the 64k wasm page size\n"; + exit(EXIT_FAILURE); + } + module->memory.initial = providedMemory / Memory::kPageSize; module->memory.max = (module->exportsMap.find(GROW_WASM_MEMORY) != module->exportsMap.end()) ? -1 : module->memory.initial; // global mapping is done in js in post.js @@ -151,7 +161,7 @@ extern "C" void EMSCRIPTEN_KEEPALIVE instantiate() { // create a new buffer here, just like native wasm support would. EM_ASM_({ Module['outside']['newBuffer'] = new ArrayBuffer($0); - }, wasm.memory.initial); + }, wasm.memory.initial * Memory::kPageSize); for (auto segment : wasm.memory.segments) { EM_ASM_({ var source = Module['HEAP8'].subarray($1, $1 + $2); diff --git a/src/wasm.h b/src/wasm.h index 6cbe93e3b..ce6b9d294 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -1074,6 +1074,8 @@ public: class Memory { public: + static const size_t kPageSize = 64 * 1024; + static const size_t kPageMask = ~(kPageSize - 1); struct Segment { size_t offset; const char* data; @@ -1082,7 +1084,7 @@ public: Segment(size_t offset, const char *data, size_t size) : offset(offset), data(data), size(size) {} }; - size_t initial, max; + size_t initial, max; // sizes are in pages std::vector segments; Memory() : initial(0), max((uint32_t)-1) {} -- cgit v1.2.3 From 0c0850ed5e2a2e82ad42f803894defcc53692ccd Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 9 Mar 2016 13:05:41 -0800 Subject: Implement rotates --- check.py | 2 +- src/passes/Print.cpp | 2 ++ src/wasm-binary.h | 8 ++++++++ src/wasm-interpreter.h | 4 ++++ src/wasm-s-parser.h | 6 +++++- src/wasm.h | 29 ++++++++++++++++++++++++++++- test/spec | 2 +- 7 files changed, 49 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/check.py b/check.py index 926282eab..272e1394c 100755 --- a/check.py +++ b/check.py @@ -354,7 +354,7 @@ for t in tests: print '\n[ checking binaryen-shell spec testcases... ]\n' if len(requested) == 0: - BLACKLIST = ['i32.wast', 'i64.wast'] + BLACKLIST = [] spec_tests = [os.path.join('spec', t) for t in sorted(os.listdir(os.path.join('test', 'spec'))) if t not in BLACKLIST] else: spec_tests = requested[:] diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp index 91c4040f6..b69f7a977 100644 --- a/src/passes/Print.cpp +++ b/src/passes/Print.cpp @@ -284,6 +284,8 @@ struct PrintSExpression : public WasmVisitor { case Shl: o << "shl"; break; case ShrU: o << "shr_u"; break; case ShrS: o << "shr_s"; break; + case RotL: o << "rotl"; break; + case RotR: o << "rotr"; break; case Div: o << "div"; break; case CopySign: o << "copysign"; break; case Min: o << "min"; break; diff --git a/src/wasm-binary.h b/src/wasm-binary.h index 47da374c1..715c06287 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -287,6 +287,10 @@ enum ASTNodes { F64ConvertF32 = 0xb2, F64ReinterpretI64 = 0xb3, I64ReinterpretF64 = 0xb5, + I32RotR = 0xb6, + I32RotL = 0xb7, + I64RotR = 0xb8, + I64RotL = 0xb9, I32ReinterpretF32 = 0xfe, // XXX not in v8 spec doc I32LoadMem8S = 0x20, @@ -883,6 +887,8 @@ public: case Shl: INT_TYPED_CODE(Shl);; case ShrU: INT_TYPED_CODE(ShrU); case ShrS: INT_TYPED_CODE(ShrS); + case RotL: INT_TYPED_CODE(RotL); + case RotR: INT_TYPED_CODE(RotR); case Div: FLOAT_TYPED_CODE(Div); case CopySign: FLOAT_TYPED_CODE(CopySign); case Min: FLOAT_TYPED_CODE(Min); @@ -1593,6 +1599,8 @@ public: INT_TYPED_CODE(Shl); INT_TYPED_CODE(ShrU); INT_TYPED_CODE(ShrS); + INT_TYPED_CODE(RotL); + INT_TYPED_CODE(RotR); FLOAT_TYPED_CODE(Div); FLOAT_TYPED_CODE(CopySign); FLOAT_TYPED_CODE(Min); diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 2edaad846..f6a3e1027 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -464,6 +464,8 @@ private: case Shl: return left.shl(right.and_(Literal(int32_t(31)))); case ShrU: return left.shrU(right.and_(Literal(int32_t(31)))); case ShrS: return left.shrS(right.and_(Literal(int32_t(31)))); + case RotL: return left.rotL(right); + case RotR: return left.rotR(right); case Eq: return left.eq(right); case Ne: return left.ne(right); case LtS: return left.ltS(right); @@ -505,6 +507,8 @@ private: case Shl: return left.shl(right.and_(Literal(int64_t(63)))); case ShrU: return left.shrU(right.and_(Literal(int64_t(63)))); case ShrS: return left.shrS(right.and_(Literal(int64_t(63)))); + case RotL: return left.rotL(right); + case RotR: return left.rotR(right); case Eq: return left.eq(right); case Ne: return left.ne(right); case LtS: return left.ltS(right); diff --git a/src/wasm-s-parser.h b/src/wasm-s-parser.h index 1cd614c1d..cfabbf52d 100644 --- a/src/wasm-s-parser.h +++ b/src/wasm-s-parser.h @@ -430,7 +430,8 @@ public: WasmType type = stringToWasmType(str, false, true); // Local copy to index into op without bounds checking. constexpr size_t maxNameSize = 15; - char op[maxNameSize + 1] = { '\0' }; + char op[maxNameSize + 1]; + memset(op, 0, maxNameSize + 1); // ensure the whole string is cleared. strncpy(op, dot + 1, maxNameSize); switch (op[0]) { case 'a': { @@ -523,6 +524,9 @@ public: if (op[2] == 'm') return makeBinary(s, op[4] == 'u' ? BinaryOp::RemU : BinaryOp::RemS, type); if (op[2] == 'i') return makeUnary(s, isWasmTypeFloat(type) ? UnaryOp::ReinterpretInt : UnaryOp::ReinterpretFloat, type); } + if (op[1] == 'o' && op[2] == 't') { + return makeBinary(s, op[3] == 'l' ? BinaryOp::RotL : BinaryOp::RotR, type); + } abort_on(op); } case 's': { diff --git a/src/wasm.h b/src/wasm.h index ce6b9d294..26b5e18e1 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -45,6 +45,7 @@ #include #include +#include #include #include #include @@ -503,6 +504,32 @@ public: default: WASM_UNREACHABLE(); } } + template + static T rol(T val, T count) { + T mask = sizeof(T) * CHAR_BIT - 1; + count &= mask; + return (val << count) | (val >> (-count & mask)); + } + Literal rotL(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(rol(uint32_t(i32), uint32_t(other.i32))); + case WasmType::i64: return Literal(rol(uint64_t(i64), uint64_t(other.i64))); + default: WASM_UNREACHABLE(); + } + } + template + static T ror (T val, T count) { + T mask = sizeof(T) * CHAR_BIT - 1; + count &= mask; + return (val >> count) | (val << (-count & mask)); + } + Literal rotR(const Literal& other) const { + switch (type) { + case WasmType::i32: return Literal(ror(uint32_t(i32), uint32_t(other.i32))); + case WasmType::i64: return Literal(ror(uint64_t(i64), uint64_t(other.i64))); + default: WASM_UNREACHABLE(); + } + } Literal eq(const Literal& other) const { switch (type) { @@ -676,7 +703,7 @@ enum UnaryOp { enum BinaryOp { Add, Sub, Mul, // int or float - DivS, DivU, RemS, RemU, And, Or, Xor, Shl, ShrU, ShrS, // int + DivS, DivU, RemS, RemU, And, Or, Xor, Shl, ShrU, ShrS, RotL, RotR, // int Div, CopySign, Min, Max, // float // relational ops Eq, Ne, // int or float diff --git a/test/spec b/test/spec index 3fdec99e9..6e6134562 160000 --- a/test/spec +++ b/test/spec @@ -1 +1 @@ -Subproject commit 3fdec99e94434b58d7477834c2be6b81c1aea918 +Subproject commit 6e6134562e70299bea9e85fc845e5ce210e96db6 -- cgit v1.2.3 From ac9d61d45fec988640b57dc6b9de97e7d46c41f5 Mon Sep 17 00:00:00 2001 From: Derek Schuff Date: Wed, 9 Mar 2016 17:02:00 -0800 Subject: Move rol/ror to src/support/bits.h --- src/support/bits.h | 15 +++++++++++++++ src/wasm.h | 21 ++++----------------- 2 files changed, 19 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/support/bits.h b/src/support/bits.h index bbafb29d4..6c9fbad94 100644 --- a/src/support/bits.h +++ b/src/support/bits.h @@ -17,6 +17,7 @@ #ifndef wasm_support_bits_h #define wasm_support_bits_h +#include #include #include @@ -65,6 +66,20 @@ int CountLeadingZeroes(T v) { return CountLeadingZeroes(typename std::make_unsigned::type(v)); } +template +inline static T RotateLeft(T val, T count) { + T mask = sizeof(T) * CHAR_BIT - 1; + count &= mask; + return (val << count) | (val >> (-count & mask)); +} +template +inline static T RotateRight(T val, T count) { + T mask = sizeof(T) * CHAR_BIT - 1; + count &= mask; + return (val >> count) | (val << (-count & mask)); +} + + } // namespace wasm #endif // wasm_support_bits_h diff --git a/src/wasm.h b/src/wasm.h index 26b5e18e1..4bcb4bc87 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -45,7 +45,6 @@ #include #include -#include #include #include #include @@ -504,29 +503,17 @@ public: default: WASM_UNREACHABLE(); } } - template - static T rol(T val, T count) { - T mask = sizeof(T) * CHAR_BIT - 1; - count &= mask; - return (val << count) | (val >> (-count & mask)); - } Literal rotL(const Literal& other) const { switch (type) { - case WasmType::i32: return Literal(rol(uint32_t(i32), uint32_t(other.i32))); - case WasmType::i64: return Literal(rol(uint64_t(i64), uint64_t(other.i64))); + case WasmType::i32: return Literal(RotateLeft(uint32_t(i32), uint32_t(other.i32))); + case WasmType::i64: return Literal(RotateLeft(uint64_t(i64), uint64_t(other.i64))); default: WASM_UNREACHABLE(); } } - template - static T ror (T val, T count) { - T mask = sizeof(T) * CHAR_BIT - 1; - count &= mask; - return (val >> count) | (val << (-count & mask)); - } Literal rotR(const Literal& other) const { switch (type) { - case WasmType::i32: return Literal(ror(uint32_t(i32), uint32_t(other.i32))); - case WasmType::i64: return Literal(ror(uint64_t(i64), uint64_t(other.i64))); + case WasmType::i32: return Literal(RotateRight(uint32_t(i32), uint32_t(other.i32))); + case WasmType::i64: return Literal(RotateRight(uint64_t(i64), uint64_t(other.i64))); default: WASM_UNREACHABLE(); } } -- cgit v1.2.3