summaryrefslogtreecommitdiff
path: root/src/s2wasm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/s2wasm.h')
-rw-r--r--src/s2wasm.h134
1 files changed, 100 insertions, 34 deletions
diff --git a/src/s2wasm.h b/src/s2wasm.h
index 8db41a339..1fa624dd0 100644
--- a/src/s2wasm.h
+++ b/src/s2wasm.h
@@ -44,10 +44,12 @@ class S2WasmBuilder {
bool debug;
bool ignoreUnknownSymbols;
Name startFunction;
+ std::vector<Name> globls;
public:
S2WasmBuilder(AllocatingModule& wasm, const char* input, bool debug,
size_t globalBase, size_t stackAllocation,
+ size_t userInitialMemory, size_t userMaxMemory,
bool ignoreUnknownSymbols, Name startFunction)
: wasm(wasm),
allocator(wasm.allocator),
@@ -55,7 +57,20 @@ class S2WasmBuilder {
ignoreUnknownSymbols(ignoreUnknownSymbols),
startFunction(startFunction),
globalBase(globalBase),
- nextStatic(globalBase) {
+ nextStatic(globalBase),
+ minInitialMemory(0),
+ userInitialMemory(userInitialMemory),
+ userMaxMemory(userMaxMemory) {
+ if (userMaxMemory && userMaxMemory < userInitialMemory) {
+ Fatal() << "Specified max memory " << userMaxMemory <<
+ " is < specified initial memory " << userInitialMemory;
+ }
+ if (roundUpToPageSize(userMaxMemory) != userMaxMemory) {
+ Fatal() << "Specified max memory " << userMaxMemory << " is not a multiple of 64k";
+ }
+ if (roundUpToPageSize(userInitialMemory) != userInitialMemory) {
+ Fatal() << "Specified initial memory " << userInitialMemory << " is not a multiple of 64k";
+ }
s = input;
scan();
s = input;
@@ -75,6 +90,10 @@ 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<Name, int32_t> staticAddresses; // name => address
+ size_t minInitialMemory; // Minimum initial size (in bytes) of memory.
+ size_t userInitialMemory; // Initial memory size (in bytes) specified by the user.
+ size_t userMaxMemory; // Max memory size (in bytes) specified by the user.
+ //(after linking, this is rounded and set on the wasm object in pages)
struct Relocation {
uint32_t* data;
@@ -93,6 +112,23 @@ class S2WasmBuilder {
// utilities
+ // For fatal errors which could arise from input (i.e. not assertion failures)
+ class Fatal {
+ public:
+ Fatal() {
+ std::cerr << "Fatal: ";
+ }
+ template<typename T>
+ Fatal &operator<<(T arg) {
+ std::cerr << arg;
+ return *this;
+ }
+ ~Fatal() {
+ std::cerr << "\n";
+ exit(1);
+ }
+ };
+
void skipWhitespace() {
while (1) {
while (*s && isspace(*s)) s++;
@@ -396,7 +432,7 @@ class S2WasmBuilder {
addressSegments[nextStatic] = wasm.memory.segments.size();
wasm.memory.segments.emplace_back(
nextStatic, reinterpret_cast<char*>(raw), pointerSize);
- wasm.memory.initial = nextStatic + pointerSize;
+ minInitialMemory = nextStatic + pointerSize;
}
nextStatic += pointerSize;
}
@@ -407,7 +443,7 @@ class S2WasmBuilder {
nextStatic = (nextStatic + 15) & static_cast<size_t>(-16);
staticAddresses[".stack"] = nextStatic;
nextStatic += stackAllocation;
- wasm.memory.initial = nextStatic;
+ minInitialMemory = nextStatic;
}
void process() {
@@ -463,7 +499,8 @@ class S2WasmBuilder {
}
void parseGlobl() {
- (void)getStr();
+ auto str = getStr();
+ globls.push_back(str);
skipWhitespace();
}
@@ -528,6 +565,7 @@ class S2WasmBuilder {
last = last->cast<Loop>()->body;
}
last->cast<Block>()->list.push_back(curr);
+ last->cast<Block>()->finalize();
};
bstack.push_back(func->body);
std::vector<Expression*> estack;
@@ -665,7 +703,7 @@ class S2WasmBuilder {
curr->align = curr->bytes;
if (attributes[0]) {
assert(strncmp(attributes[0], "p2align=", 8) == 0);
- curr->align = pow(2, getInt(attributes[0] + 8));
+ curr->align = 1U << getInt(attributes[0] + 8);
}
setOutput(curr, assign);
};
@@ -684,7 +722,7 @@ class S2WasmBuilder {
curr->align = curr->bytes;
if (attributes[0]) {
assert(strncmp(attributes[0], "p2align=", 8) == 0);
- curr->align = pow(2, getInt(attributes[0] + 8));
+ curr->align = 1U << getInt(attributes[0] + 8);
}
curr->value = inputs[1];
setOutput(curr, assign);
@@ -714,16 +752,7 @@ class S2WasmBuilder {
indirect->operands.push_back(inputs[i]);
}
setOutput(indirect, assign);
- auto typeName = cashew::IString((std::string("FUNCSIG_") + getSig(indirect)).c_str(), false);
- if (wasm.functionTypesMap.count(typeName) == 0) {
- auto type = allocator.alloc<FunctionType>();
- *type = sigToFunctionType(getSig(indirect));
- type->name = typeName;
- wasm.addFunctionType(type);
- indirect->fullType = type;
- } else {
- indirect->fullType = wasm.functionTypesMap[typeName];
- }
+ indirect->fullType = wasm.functionTypesMap[ensureFunctionType(getSig(indirect), &wasm, allocator)->name];
} else {
// non-indirect call
CallBase* curr;
@@ -807,7 +836,8 @@ class S2WasmBuilder {
break;
}
case 'e': {
- if (match("eq")) makeBinary(BinaryOp::Eq, i32);
+ if (match("eqz")) makeUnary(UnaryOp::EqZ, i32);
+ else if (match("eq")) makeBinary(BinaryOp::Eq, i32);
else if (match("extend_s/i32")) makeUnary(UnaryOp::ExtendSInt32, type);
else if (match("extend_u/i32")) makeUnary(UnaryOp::ExtendUInt32, type);
else abort_on("type.e");
@@ -869,6 +899,8 @@ class S2WasmBuilder {
else if (match("rem_u")) makeBinary(BinaryOp::RemU, type);
else if (match("reinterpret/i32") || match("reinterpret/i64")) makeUnary(UnaryOp::ReinterpretInt, type);
else if (match("reinterpret/f32") || match("reinterpret/f64")) makeUnary(UnaryOp::ReinterpretFloat, type);
+ else if (match("rotl")) makeBinary(BinaryOp::RotL, type);
+ else if (match("rotr")) makeBinary(BinaryOp::RotR, type);
else abort_on("type.r");
break;
}
@@ -956,6 +988,16 @@ class S2WasmBuilder {
} else if (match("end_loop")) {
bstack.pop_back();
bstack.pop_back();
+ } else if (match("br_table")) {
+ auto curr = allocator.alloc<Switch>();
+ curr->condition = getInput();
+ while (skipComma()) {
+ curr->targets.push_back(getBranchLabel(getInt()));
+ }
+ assert(curr->targets.size() > 0);
+ curr->default_ = curr->targets.back();
+ curr->targets.pop_back();
+ addToBlock(curr);
} else if (match("br")) {
auto curr = allocator.alloc<Break>();
bool hasCondition = false;
@@ -990,15 +1032,6 @@ class S2WasmBuilder {
curr->value = getInput();
}
addToBlock(curr);
- } else if (match("tableswitch")) {
- auto curr = allocator.alloc<Switch>();
- curr->value = getInput();
- skipComma();
- curr->default_ = getBranchLabel(getInt());
- while (skipComma()) {
- curr->targets.push_back(getBranchLabel(getInt()));
- }
- addToBlock(curr);
} else if (match("unreachable")) {
addToBlock(allocator.alloc<Unreachable>());
} else if (match("memory_size")) {
@@ -1023,11 +1056,8 @@ class S2WasmBuilder {
for (auto block : loopBlocks) {
block->name = Name();
}
+ func->body->dyn_cast<Block>()->finalize();
wasm.addFunction(func);
- // XXX for now, export all functions
- auto exp = allocator.alloc<Export>();
- exp->name = exp->value = func->name;
- wasm.addExport(exp);
}
void parseType() {
@@ -1062,7 +1092,7 @@ class S2WasmBuilder {
align = getInt();
skipWhitespace();
}
- align = pow(2, align); // convert from power to actual bytes
+ align = (size_t)1 << align; // convert from power to actual bytes
if (match(".lcomm")) {
parseLcomm(name, align);
return;
@@ -1151,7 +1181,7 @@ class S2WasmBuilder {
wasm.memory.segments.emplace_back(nextStatic, (const char*)&(*raw)[0], size);
}
nextStatic += size;
- wasm.memory.initial = nextStatic;
+ minInitialMemory = nextStatic;
}
void parseLcomm(Name name, size_t align=1) {
@@ -1165,7 +1195,7 @@ class S2WasmBuilder {
while (nextStatic % align) nextStatic++;
staticAddresses[name] = nextStatic;
nextStatic += size;
- wasm.memory.initial = nextStatic;
+ minInitialMemory = nextStatic;
}
void skipImports() {
@@ -1179,7 +1209,36 @@ class S2WasmBuilder {
}
}
+ static size_t roundUpToPageSize(size_t size) {
+ return (size + Memory::kPageSize - 1) & Memory::kPageMask;
+ }
+
void fix() {
+ // Round the memory size up to a page, and update the page-increment versions
+ // of initial and max
+ size_t initialMem = roundUpToPageSize(minInitialMemory);
+ if (userInitialMemory) {
+ if (initialMem > userInitialMemory) {
+ Fatal() << "Specified initial memory size " << userInitialMemory <<
+ " is smaller than required size " << initialMem;
+ }
+ wasm.memory.initial = userInitialMemory / Memory::kPageSize;
+ } else {
+ wasm.memory.initial = initialMem / Memory::kPageSize;
+ }
+
+ if (userMaxMemory) wasm.memory.max = userMaxMemory / Memory::kPageSize;
+ wasm.memory.exportName = MEMORY;
+
+ // XXX For now, export all functions marked .globl.
+ for (Name name : globls) {
+ if (wasm.functionsMap.count(name)) {
+ auto exp = allocator.alloc<Export>();
+ exp->name = exp->value = name;
+ wasm.addExport(exp);
+ }
+ }
+
auto ensureFunctionIndex = [&](Name name) {
if (functionIndexes.count(name) == 0) {
functionIndexes[name] = wasm.table.names.size();
@@ -1243,8 +1302,15 @@ class S2WasmBuilder {
call->operands.push_back(param);
}
block->list.push_back(call);
+ block->finalize();
}
}
+
+ // ensure an explicit function type for indirect call targets
+ for (auto& name : wasm.table.names) {
+ auto* func = wasm.functionsMap[name];
+ func->type = ensureFunctionType(getSig(func), &wasm, allocator)->name;
+ }
}
template<class C>
@@ -1294,7 +1360,7 @@ public:
}
std::string sig = getSig(curr);
sigsForCode[code].insert(sig);
- std::string fixedTarget = std::string("_") + EMSCRIPTEN_ASM_CONST.str + '_' + sig;
+ std::string fixedTarget = EMSCRIPTEN_ASM_CONST.str + std::string("_") + sig;
curr->target = cashew::IString(fixedTarget.c_str(), false);
arg->value = Literal(id);
// add import, if necessary