summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/passes/RemoveUnusedBrs.cpp15
-rw-r--r--src/s2wasm.h66
-rw-r--r--src/wasm-binary.h54
-rw-r--r--src/wasm.h11
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);
}
};