summaryrefslogtreecommitdiff
path: root/src/asm2wasm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/asm2wasm.h')
-rw-r--r--src/asm2wasm.h175
1 files changed, 86 insertions, 89 deletions
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 048d363ef..fc841e634 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -28,7 +28,6 @@
#include "emscripten-optimizer/optimizer.h"
#include "ir/bits.h"
#include "ir/branch-utils.h"
-#include "ir/function-type-utils.h"
#include "ir/literal-utils.h"
#include "ir/module-utils.h"
#include "ir/trapping.h"
@@ -509,47 +508,51 @@ private:
// function types. we fill in this information as we see
// uses, in the first pass
- std::map<IString, std::unique_ptr<FunctionType>> importedFunctionTypes;
+ std::map<IString, Signature> importedSignatures;
void noteImportedFunctionCall(Ref ast, Type resultType, Call* call) {
assert(ast[0] == CALL && ast[1]->isString());
IString importName = ast[1]->getIString();
- auto type = make_unique<FunctionType>();
- type->name = IString((std::string("type$") + importName.str).c_str(),
- false); // TODO: make a list of such types
- type->result = resultType;
+ std::vector<Type> params;
for (auto* operand : call->operands) {
- type->params.push_back(operand->type);
+ params.push_back(operand->type);
}
+ Signature sig = Signature(Type(params), resultType);
// if we already saw this signature, verify it's the same (or else handle
// that)
- if (importedFunctionTypes.find(importName) != importedFunctionTypes.end()) {
- FunctionType* previous = importedFunctionTypes[importName].get();
- if (*type != *previous) {
+ if (importedSignatures.find(importName) != importedSignatures.end()) {
+ Signature& previous = importedSignatures[importName];
+ if (sig != previous) {
+ std::vector<Type> mergedParams = previous.params.expand();
// merge it in. we'll add on extra 0 parameters for ones not actually
// used, and upgrade types to double where there is a conflict (which is
// ok since in JS, double can contain everything i32 and f32 can).
- for (size_t i = 0; i < type->params.size(); i++) {
- if (previous->params.size() > i) {
- if (previous->params[i] == none) {
- previous->params[i] = type->params[i]; // use a more concrete type
- } else if (previous->params[i] != type->params[i]) {
- previous->params[i] = f64; // overloaded type, make it a double
+ for (size_t i = 0; i < params.size(); i++) {
+ if (mergedParams.size() > i) {
+ // TODO: Is this dead?
+ // if (mergedParams[i] == Type::none) {
+ // mergedParams[i] = params[i]; // use a more concrete type
+ // } else
+ if (mergedParams[i] != params[i]) {
+ mergedParams[i] = f64; // overloaded type, make it a double
}
} else {
- previous->params.push_back(type->params[i]); // add a new param
+ mergedParams.push_back(params[i]); // add a new param
}
}
+ previous.params = Type(mergedParams);
// we accept none and a concrete type, but two concrete types mean we
// need to use an f64 to contain anything
- if (previous->result == none) {
- previous->result = type->result; // use a more concrete type
- } else if (previous->result != type->result && type->result != none) {
- previous->result = f64; // overloaded return type, make it a double
+ if (previous.results == Type::none) {
+ previous.results = sig.results; // use a more concrete type
+ } else if (previous.results != sig.results &&
+ sig.results != Type::none) {
+ // overloaded return type, make it a double
+ previous.results = Type::f64;
}
}
} else {
- importedFunctionTypes[importName].swap(type);
+ importedSignatures[importName] = sig;
}
}
@@ -566,10 +569,14 @@ private:
return result;
}
- FunctionType*
- getFunctionType(Ref parent, ExpressionList& operands, AsmData* data) {
- Type result = getResultTypeOfCallUsingParent(parent, data);
- return ensureFunctionType(getSig(result, operands), &wasm);
+ Signature getSignature(Ref parent, ExpressionList& operands, AsmData* data) {
+ Type results = getResultTypeOfCallUsingParent(parent, data);
+ std::vector<Type> paramTypes;
+ for (auto& op : operands) {
+ assert(op->type != Type::unreachable);
+ paramTypes.push_back(op->type);
+ }
+ return Signature(Type(paramTypes), results);
}
public:
@@ -790,25 +797,29 @@ private:
}
}
- FunctionType* getBuiltinFunctionType(Name module,
- Name base,
- ExpressionList* operands = nullptr) {
+ bool getBuiltinSignature(Signature& sig,
+ Name module,
+ Name base,
+ ExpressionList* operands = nullptr) {
if (module == GLOBAL_MATH) {
if (base == ABS) {
assert(operands && operands->size() == 1);
Type type = (*operands)[0]->type;
if (type == i32) {
- return ensureFunctionType("ii", &wasm);
+ sig = Signature(Type::i32, Type::i32);
+ return true;
}
if (type == f32) {
- return ensureFunctionType("ff", &wasm);
+ sig = Signature(Type::f32, Type::f32);
+ return true;
}
if (type == f64) {
- return ensureFunctionType("dd", &wasm);
+ sig = Signature(Type::f64, Type::f64);
+ return true;
}
}
}
- return nullptr;
+ return false;
}
// ensure a nameless block
@@ -1043,6 +1054,7 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
import->name = name;
import->module = moduleName;
import->base = base;
+ import->sig = Signature(Type::none, Type::none);
wasm.addFunction(import);
}
};
@@ -1377,16 +1389,13 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
IString name = import->name;
- if (importedFunctionTypes.find(name) != importedFunctionTypes.end()) {
+ if (importedSignatures.find(name) != importedSignatures.end()) {
// special math builtins
- FunctionType* builtin =
- getBuiltinFunctionType(import->module, import->base);
- if (builtin) {
- import->type = builtin->name;
+ Signature builtin;
+ if (getBuiltinSignature(builtin, import->module, import->base)) {
+ import->sig = builtin;
} else {
- import->type =
- ensureFunctionType(getSig(importedFunctionTypes[name].get()), &wasm)
- ->name;
+ import->sig = importedSignatures[name];
}
} else if (import->module != ASM2WASM) { // special-case the special module
// never actually used, which means we don't know the function type since
@@ -1399,12 +1408,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
wasm.removeFunction(curr);
}
- // Finalize function imports now that we've seen all the calls
-
- ModuleUtils::iterImportedFunctions(wasm, [&](Function* func) {
- FunctionTypeUtils::fillFunction(func, wasm.getFunctionType(func->type));
- });
-
// Finalize calls now that everything is known and generated
struct FinalizeCalls : public WalkerPass<PostWalker<FinalizeCalls>> {
@@ -1450,9 +1453,9 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
if (calledFunc && !calledFunc->imported()) {
// The result type of the function being called is now known, and can be
// applied.
- auto result = calledFunc->result;
- if (curr->type != result) {
- curr->type = result;
+ auto results = calledFunc->sig.results;
+ if (curr->type != results) {
+ curr->type = results;
}
// Handle mismatched numbers of arguments. In clang, if a function is
// declared one way but called in another, it inserts bitcasts to make
@@ -1460,26 +1463,26 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
// parameters in native platforms, even though it's undefined behavior.
// We warn about it here, but tolerate it, if there is a simple
// solution.
- if (curr->operands.size() < calledFunc->params.size()) {
+ const std::vector<Type>& params = calledFunc->sig.params.expand();
+ if (curr->operands.size() < params.size()) {
notifyAboutWrongOperands("warning: asm2wasm adding operands",
calledFunc);
- while (curr->operands.size() < calledFunc->params.size()) {
+ while (curr->operands.size() < params.size()) {
// Add params as necessary, with zeros.
curr->operands.push_back(LiteralUtils::makeZero(
- calledFunc->params[curr->operands.size()], *getModule()));
+ params[curr->operands.size()], *getModule()));
}
}
- if (curr->operands.size() > calledFunc->params.size()) {
+ if (curr->operands.size() > params.size()) {
notifyAboutWrongOperands("warning: asm2wasm dropping operands",
calledFunc);
- curr->operands.resize(calledFunc->params.size());
+ curr->operands.resize(params.size());
}
// If the types are wrong, validation will fail later anyhow, but add a
// warning here, it may help people.
for (Index i = 0; i < curr->operands.size(); i++) {
auto sent = curr->operands[i]->type;
- auto expected = calledFunc->params[i];
- if (sent != unreachable && sent != expected) {
+ if (sent != Type::unreachable && sent != params[i]) {
notifyAboutWrongOperands(
"error: asm2wasm seeing an invalid argument type at index " +
std::to_string(i) + " (this will not validate)",
@@ -1490,23 +1493,23 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
// A call to an import
// fill things out: add extra params as needed, etc. asm tolerates ffi
// overloading, wasm does not
- auto iter = parent->importedFunctionTypes.find(curr->target);
- if (iter == parent->importedFunctionTypes.end()) {
+ auto iter = parent->importedSignatures.find(curr->target);
+ if (iter == parent->importedSignatures.end()) {
return; // one of our fake imports for callIndirect fixups
}
- auto type = iter->second.get();
- for (size_t i = 0; i < type->params.size(); i++) {
+ const std::vector<Type>& params = iter->second.params.expand();
+ for (size_t i = 0; i < params.size(); i++) {
if (i >= curr->operands.size()) {
// add a new param
auto val = parent->allocator.alloc<Const>();
- val->type = val->value.type = type->params[i];
+ val->type = val->value.type = params[i];
curr->operands.push_back(val);
- } else if (curr->operands[i]->type != type->params[i]) {
+ } else if (curr->operands[i]->type != params[i]) {
// if the param is used, then we have overloading here and the
// combined type must be f64; if this is an unreachable param, then
// it doesn't matter.
- assert(type->params[i] == f64 ||
- curr->operands[i]->type == unreachable);
+ assert(params[i] == Type::f64 ||
+ curr->operands[i]->type == Type::unreachable);
// overloaded, upgrade to f64
switch (curr->operands[i]->type) {
case i32:
@@ -1522,12 +1525,11 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
}
}
Module* wasm = getModule();
- auto importResult =
- wasm->getFunctionType(wasm->getFunction(curr->target)->type)->result;
- if (curr->type != importResult) {
+ Type importResults = wasm->getFunction(curr->target)->sig.results;
+ if (curr->type != importResults) {
auto old = curr->type;
- curr->type = importResult;
- if (importResult == f64) {
+ curr->type = importResults;
+ if (importResults == Type::f64) {
// we use a JS f64 value which is the most general, and convert to
// it
switch (old) {
@@ -1743,7 +1745,6 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
// if r then *r = x % y
// returns x / y
auto* func = wasm.getFunction(udivmoddi4);
- assert(!func->type.is());
Builder::clearLocals(func);
Index xl = Builder::addParam(func, "xl", i32),
xh = Builder::addParam(func, "xh", i32),
@@ -1786,6 +1787,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
BYN_TRACE("asm2wasming func: " << ast[1]->getIString().str << '\n');
auto function = new Function;
+ function->sig = Signature(Type::none, Type::none);
function->name = name;
Ref params = ast[2];
Ref body = ast[3];
@@ -1851,8 +1853,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
std::function<Expression*(Ref, unsigned)> processIgnoringShift;
std::function<Expression*(Ref)> process = [&](Ref ast) -> Expression* {
- AstStackHelper astStackHelper(
- ast); // TODO: only create one when we need it?
+ // TODO: only create one when we need it?
+ AstStackHelper astStackHelper(ast);
if (ast->isString()) {
IString name = ast->getIString();
if (functionVariables.has(name)) {
@@ -1873,9 +1875,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
import->name = DEBUGGER;
import->module = ASM2WASM;
import->base = DEBUGGER;
- auto* functionType = ensureFunctionType("v", &wasm);
- import->type = functionType->name;
- FunctionTypeUtils::fillFunction(import, functionType);
+ import->sig = Signature(Type::none, Type::none);
wasm.addFunction(import);
}
return call;
@@ -1989,9 +1989,7 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
import->name = F64_REM;
import->module = ASM2WASM;
import->base = F64_REM;
- auto* functionType = ensureFunctionType("ddd", &wasm);
- import->type = functionType->name;
- FunctionTypeUtils::fillFunction(import, functionType);
+ import->sig = Signature({Type::f64, Type::f64}, Type::f64);
wasm.addFunction(import);
}
return call;
@@ -2647,7 +2645,6 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
}
break;
}
- default: {}
}
}
// ftCall_* and mftCall_* represent function table calls, either from
@@ -2685,10 +2682,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
auto specific = ret->dynCast<CallIndirect>();
// note that we could also get the type from the suffix of the name,
// e.g., mftCall_vi
- auto* fullType = getFunctionType(
+ auto sig = getSignature(
astStackHelper.getParent(), specific->operands, &asmData);
- specific->fullType = fullType->name;
- specific->type = fullType->result;
+ specific->sig = sig;
+ specific->type = sig.results;
}
if (callImport) {
// apply the detected type from the parent
@@ -2719,10 +2716,10 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
for (unsigned i = 0; i < args->size(); i++) {
ret->operands.push_back(process(args[i]));
}
- auto* fullType =
- getFunctionType(astStackHelper.getParent(), ret->operands, &asmData);
- ret->fullType = fullType->name;
- ret->type = fullType->result;
+ auto sig =
+ getSignature(astStackHelper.getParent(), ret->operands, &asmData);
+ ret->sig = sig;
+ ret->type = sig.results;
// we don't know the table offset yet. emit target = target +
// callImport(tableName), which we fix up later when we know how asm
// function tables are layed out inside the wasm table.
@@ -2734,9 +2731,9 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
} else if (what == RETURN) {
Type type = !!ast[1] ? detectWasmType(ast[1], &asmData) : none;
if (seenReturn) {
- assert(function->result == type);
+ assert(function->sig.results == type);
} else {
- function->result = type;
+ function->sig.results = type;
}
// wasm has no return, so we just break on the topmost block
auto ret = allocator.alloc<Return>();