diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/passes/RemoveUnusedBrs.cpp | 15 | ||||
-rw-r--r-- | src/s2wasm.h | 66 | ||||
-rw-r--r-- | src/wasm-binary.h | 54 | ||||
-rw-r--r-- | src/wasm.h | 11 |
4 files changed, 113 insertions, 33 deletions
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp index 245642055..c7fa43df9 100644 --- a/src/passes/RemoveUnusedBrs.cpp +++ b/src/passes/RemoveUnusedBrs.cpp @@ -28,7 +28,15 @@ struct RemoveUnusedBrs : public WalkerPass<WasmWalker<RemoveUnusedBrs>> { // specifically for if-else, turn an if-else with branches to the same target at the end of each // child, and with a value, to a branch to that target containing the if-else void visitIf(If* curr) { - if (!curr->ifFalse) return; + if (!curr->ifFalse) { + // try to reduce an if (condition) br => br_if (condition) , which might open up other optimization opportunities + Break* br = curr->ifTrue->dyn_cast<Break>(); + if (br && !br->condition) { // TODO: if there is a condition, join them + br->condition = curr->condition; + replaceCurrent(br); + } + return; + } if (curr->type != none) return; // already has a returned value // an if_else that indirectly returns a value by breaking to the same target can potentially remove both breaks, and break outside once auto getLast = [](Expression *side) -> Expression* { @@ -66,9 +74,10 @@ struct RemoveUnusedBrs : public WalkerPass<WasmWalker<RemoveUnusedBrs>> { void visitBlock(Block *curr) { if (curr->name.isNull()) return; if (curr->list.size() == 0) return; - // preparation - remove all code after a break, since it can't execute, and it might confuse us (we look at the last) + // preparation - remove all code after an unconditional break, since it can't execute, and it might confuse us (we look at the last) for (size_t i = 0; i < curr->list.size()-1; i++) { - if (curr->list[i]->is<Break>()) { + Break* br = curr->list[i]->dyn_cast<Break>(); + if (br && !br->condition) { curr->list.resize(i+1); break; } diff --git a/src/s2wasm.h b/src/s2wasm.h index 46cf972c8..82675df1b 100644 --- a/src/s2wasm.h +++ b/src/s2wasm.h @@ -159,6 +159,7 @@ private: return cashew::IString(str.c_str(), false); } + // get an int int32_t getInt() { const char* loc = s; uint32_t value = 0; @@ -192,6 +193,15 @@ private: return value; } + // get an int from an arbitrary string, with our full error handling + int32_t getInt(const char *from) { + const char *before = s; + s = from; + auto ret = getInt(); + s = before; + return ret; + } + // gets a constant, which may be a relocation for later. // returns whether this is a relocation bool getConst(uint32_t* target) { @@ -343,7 +353,9 @@ private: } void prepare() { - staticAddresses["__stack_pointer"] = 0; // XXX HACK for now + assert(nextStatic == globalBase); // we are the first allocation + staticAddresses["__stack_pointer"] = nextStatic; + nextStatic += 4; } void process() { @@ -359,7 +371,7 @@ private: else if (match("imports")) skipImports(); else if (match("data")) {} else if (match("ident")) {} - else if (match("section") || match("align")) s = strchr(s, '\n'); + else if (match("section") || match("align") || match("p2align")) s = strchr(s, '\n'); else if (match("Lfunc_end")) { // skip the next line, which has a .size we can ignore s = strstr(s, ".size"); @@ -375,7 +387,8 @@ private: if (!*s) break; if (*s != '.') break; s++; - if (match("file")) parseFile(); + if (parseVersionMin()); + else if (match("file")) parseFile(); else if (match("globl")) parseGlobl(); else if (match("type")) parseType(); else { @@ -402,6 +415,15 @@ private: skipWhitespace(); } + bool parseVersionMin() { + if (match("watchos_version_min") || match("tvos_version_min") || match("ios_version_min") || match("macosx_version_min")) { + s = strchr(s, '\n'); + skipWhitespace(); + return true; + } else + return false; + } + void parseFunction() { if (debug) dump("func"); Name name = getStrToSep(); @@ -490,6 +512,10 @@ private: inputs[i] = curr; } if (*s == ')') s++; // tolerate 0(argument) syntax, where we started at the 'a' + if (*s == ':') { // tolerate :attribute=value syntax (see getAttributes) + s++; + skipToSep(); + } if (i < num - 1) skipComma(); } for (int i = num-1; i >= 0; i--) { @@ -513,6 +539,24 @@ private: addToBlock(set); } }; + auto getAttributes = [&](int num) { + const char *before = s; + std::vector<const char*> attributes; // TODO: optimize (if .s format doesn't change) + attributes.resize(num); + for (int i = 0; i < num; i++) { + skipToSep(); + if (*s == ')') s++; // tolerate 0(argument) syntax, where we started at the 'a' + if (*s == ':') { + attributes[i] = s + 1; + } else { + attributes[i] = nullptr; + } + if (i < num - 1) skipComma(); + } + s = before; + return attributes; + }; + // auto makeBinary = [&](BinaryOp op, WasmType type) { Name assign = getAssign(); skipComma(); @@ -557,9 +601,14 @@ private: match("_u"); Name assign = getAssign(); getConst(&curr->offset); - curr->align = curr->bytes; // XXX mustMatch("("); + auto attributes = getAttributes(1); curr->ptr = getInput(); + curr->align = curr->bytes; + if (attributes[0]) { + assert(strncmp(attributes[0], "p2align=", 8) == 0); + curr->align = pow(2, getInt(attributes[0] + 8)); + } setOutput(curr, assign); }; auto makeStore = [&](WasmType type) { @@ -568,12 +617,17 @@ private: curr->type = type; int32_t bytes = getInt(); curr->bytes = bytes > 0 ? bytes : getWasmTypeSize(type); - curr->align = curr->bytes; // XXX Name assign = getAssign(); getConst(&curr->offset); mustMatch("("); + auto attributes = getAttributes(2); auto inputs = getInputs(2); curr->ptr = inputs[0]; + curr->align = curr->bytes; + if (attributes[0]) { + assert(strncmp(attributes[0], "p2align=", 8) == 0); + curr->align = pow(2, getInt(attributes[0] + 8)); + } curr->value = inputs[1]; setOutput(curr, assign); }; @@ -930,7 +984,7 @@ private: mustMatch(name.str); skipWhitespace(); } - if (match(".align")) { + if (match(".align") || match(".p2align")) { align = getInt(); skipWhitespace(); } diff --git a/src/wasm-binary.h b/src/wasm-binary.h index a6c388c52..89cb85535 100644 --- a/src/wasm-binary.h +++ b/src/wasm-binary.h @@ -252,7 +252,7 @@ enum ASTNodes { F64CopySign = 0x91, F64Ceil = 0x92, F64Floor = 0x93, - F64Trunc = 0x94, // XXX what is this? trunc f64 to f64? + F64Trunc = 0x94, F64NearestInt = 0x95, F64Sqrt = 0x96, F64Eq = 0x97, @@ -388,6 +388,7 @@ public: } void writeMemory() { + if (wasm->memory.max == 0) return; if (debug) std::cerr << "== writeMemory" << std::endl; o << int8_t(BinaryConsts::Memory) << int8_t(log2(wasm->memory.initial)) << int8_t(log2(wasm->memory.max)) @@ -395,6 +396,7 @@ public: } void writeSignatures() { + if (wasm->functionTypes.size() == 0) return; if (debug) std::cerr << "== writeSignatures" << std::endl; o << int8_t(BinaryConsts::Signatures) << LEB128(wasm->functionTypes.size()); for (auto* type : wasm->functionTypes) { @@ -456,6 +458,7 @@ public: } void writeFunctions() { + if (wasm->functions.size() + wasm->imports.size() == 0) return; if (debug) std::cerr << "== writeFunctions" << std::endl; size_t total = wasm->imports.size() + wasm->functions.size(); o << int8_t(BinaryConsts::Functions) << LEB128(total); @@ -491,7 +494,7 @@ public: } if (function) { size_t sizePos = o.size(); - o << (uint16_t)0; // placeholder, we fill in the size later when we have it + o << (uint32_t)0; // placeholder, we fill in the size later when we have it // XXX int32, diverge from v8 format, to get more code to compile size_t start = o.size(); depth = 0; recurse(function->body); @@ -499,12 +502,13 @@ public: size_t size = o.size() - start; assert(size <= std::numeric_limits<uint16_t>::max()); if (debug) std::cerr << "body size: " << size << ", writing at " << sizePos << ", next starts at " << o.size() << std::endl; - o.writeAt(sizePos, uint16_t(size)); + o.writeAt(sizePos, uint32_t(size)); // XXX int32, diverge from v8 format, to get more code to compile } } } void writeDataSegments() { + if (wasm->memory.segments.size() == 0) return; o << int8_t(BinaryConsts::DataSegments) << LEB128(wasm->memory.segments.size()); for (auto& segment : wasm->memory.segments) { o << int32_t(segment.offset); @@ -526,6 +530,7 @@ public: } void writeFunctionTable() { + if (wasm->table.names.size() == 0) return; if (debug) std::cerr << "== writeFunctionTable" << std::endl; o << int8_t(BinaryConsts::FunctionTable) << LEB128(wasm->table.names.size()); for (auto name : wasm->table.names) { @@ -585,7 +590,7 @@ public: void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; - o << int8_t(BinaryConsts::Block) << int8_t(curr->list.size()); + o << int8_t(BinaryConsts::Block) << LEB128(curr->list.size()); // XXX larger block size, divergence from v8 to get more code to build breakStack.push_back(curr->name); size_t i = 0; for (auto* child : curr->list) { @@ -924,12 +929,24 @@ public: WasmBinaryBuilder(AllocatingModule& wasm, std::vector<char>& input, bool debug) : wasm(wasm), allocator(wasm.allocator), input(input), debug(debug), pos(0) {} void read() { - readMemory(); - readSignatures(); - readFunctions(); - readDataSegments(); - readFunctionTable(); - readEnd(); + // read sections until the end + while (1) { + int8_t section = getInt8(); + + if (section == BinaryConsts::End) { + if (debug) std::cerr << "== readEnd" << std::endl; + break; + } + + switch (section) { + case BinaryConsts::Memory: readMemory(); break; + case BinaryConsts::Signatures: readSignatures(); break; + case BinaryConsts::Functions: readFunctions(); break; + case BinaryConsts::DataSegments: readDataSegments(); break; + case BinaryConsts::FunctionTable: readFunctionTable(); break; + default: abort(); + } + } processFunctions(); } @@ -1026,7 +1043,6 @@ public: void readMemory() { if (debug) std::cerr << "== readMemory" << std::endl; - verifyInt8(BinaryConsts::Memory); wasm.memory.initial = std::pow<size_t>(2, getInt8()); wasm.memory.max = std::pow<size_t>(2, getInt8()); verifyInt8(1); // export memory @@ -1034,7 +1050,6 @@ public: void readSignatures() { if (debug) std::cerr << "== readSignatures" << std::endl; - verifyInt8(BinaryConsts::Signatures); size_t numTypes = getLEB128(); if (debug) std::cerr << "num: " << numTypes << std::endl; for (size_t i = 0; i < numTypes; i++) { @@ -1060,7 +1075,6 @@ public: void readFunctions() { if (debug) std::cerr << "== readFunctions" << std::endl; - verifyInt8(BinaryConsts::Functions); size_t total = getLEB128(); // imports and functions for (size_t i = 0; i < total; i++) { if (debug) std::cerr << "read one at " << pos << std::endl; @@ -1106,7 +1120,7 @@ public: addLocals(f32); addLocals(f64); } - size_t size = getInt16(); + size_t size = getInt32(); // XXX int32, diverge from v8 format, to get more code to compile // we can't read the function yet - it might call other functions that are defined later, // and we do depend on the function type, as well as the mappedFunctions table. functions.emplace_back(func, pos, size); @@ -1165,7 +1179,6 @@ public: void readDataSegments() { if (debug) std::cerr << "== readDataSegments" << std::endl; - verifyInt8(BinaryConsts::DataSegments); auto num = getLEB128(); for (size_t i = 0; i < num; i++) { auto curr = allocator.alloc<Memory::Segment>(); @@ -1182,18 +1195,12 @@ public: void readFunctionTable() { if (debug) std::cerr << "== readFunctionTable" << std::endl; - verifyInt8(BinaryConsts::FunctionTable); auto num = getLEB128(); for (size_t i = 0; i < num; i++) { wasm.table.names.push_back(mappedFunctions[getInt16()]); } } - void readEnd() { - if (debug) std::cerr << "== readEnd" << std::endl; - verifyInt8(BinaryConsts::End); - } - // AST reading int depth; // only for debugging @@ -1258,10 +1265,10 @@ public: void visitBlock(Block *curr) { if (debug) std::cerr << "zz node: Block" << std::endl; - auto num = getInt8(); + auto num = getLEB128(); // XXX larger block size, divergence from v8 to get more code to build curr->name = getNextLabel(); breakStack.push_back(curr->name); - for (auto i = 0; i < num; i++) { + for (uint32_t i = 0; i < num; i++) { if (debug) std::cerr << " " << size_t(curr) << "\n zz Block element " << i << std::endl; Expression* child; readExpression(child); @@ -1569,6 +1576,7 @@ public: case BinaryConsts::MemorySize: curr->op = MemorySize; break; case BinaryConsts::GrowMemory: { curr->op = GrowMemory; + curr->operands.resize(1); readExpression(curr->operands[0]); break; } diff --git a/src/wasm.h b/src/wasm.h index 5a8d57fd1..7c6ff85e7 100644 --- a/src/wasm.h +++ b/src/wasm.h @@ -412,7 +412,16 @@ public: o << ' ' << in; } incIndent(o, indent); - printFullLine(o, indent, body); + auto block = body->dyn_cast<Block>(); + if (block && block->name.isNull()) { + // wasm spec has loops containing children directly, while our ast + // has a single child for simplicity. print out the optimal form. + for (auto expression : block->list) { + printFullLine(o, indent, expression); + } + } else { + printFullLine(o, indent, body); + } return decIndent(o, indent); } }; |