#include <string>
#include <ir/element-utils.h>
#include <ir/literal-utils.h>
#include <pass.h>
#include <wasm-builder.h>
#include <wasm.h>
namespace wasm {
static Expression* toABI(Expression* value, Module* module) {
Builder builder(*module);
switch (value->type.getBasic()) {
case Type::i32: {
value = builder.makeUnary(ExtendUInt32, value);
break;
}
case Type::i64: {
break;
}
case Type::f32: {
value = builder.makeUnary(ExtendUInt32,
builder.makeUnary(ReinterpretFloat32, value));
break;
}
case Type::f64: {
value = builder.makeUnary(ReinterpretFloat64, value);
break;
}
case Type::v128: {
WASM_UNREACHABLE("v128 not implemented yet");
}
case Type::none: {
value =
builder.makeSequence(value, LiteralUtils::makeZero(Type::i64, *module));
break;
}
case Type::unreachable: {
break;
}
}
return value;
}
static Expression* fromABI(Expression* value, Type type, Module* module) {
Builder builder(*module);
switch (type.getBasic()) {
case Type::i32: {
value = builder.makeUnary(WrapInt64, value);
break;
}
case Type::i64: {
break;
}
case Type::f32: {
value = builder.makeUnary(ReinterpretInt32,
builder.makeUnary(WrapInt64, value));
break;
}
case Type::f64: {
value = builder.makeUnary(ReinterpretInt64, value);
break;
}
case Type::v128: {
WASM_UNREACHABLE("v128 not implemented yet");
}
case Type::none: {
value = builder.makeDrop(value);
break;
}
case Type::unreachable: {
break;
}
}
return value;
}
struct ParallelFuncCastEmulation
: public WalkerPass<PostWalker<ParallelFuncCastEmulation>> {
bool isFunctionParallel() override { return true; }
std::unique_ptr<Pass> create() override {
return std::make_unique<ParallelFuncCastEmulation>(ABIType, numParams);
}
ParallelFuncCastEmulation(HeapType ABIType, Index numParams)
: ABIType(ABIType), numParams(numParams) {}
void visitCallIndirect(CallIndirect* curr) {
if (curr->operands.size() > numParams) {
Fatal() << "max-func-params needs to be at least "
<< curr->operands.size();
}
for (Expression*& operand : curr->operands) {
operand = toABI(operand, getModule());
}
while (curr->operands.size() < numParams) {
curr->operands.push_back(LiteralUtils::makeZero(Type::i64, *getModule()));
}
curr->heapType = ABIType;
auto oldType = curr->type;
curr->type = Type::i64;
curr->finalize(); replaceCurrent(fromABI(curr, oldType, getModule()));
}
private:
HeapType ABIType;
Index numParams;
};
struct FuncCastEmulation : public Pass {
bool addsEffects() override { return true; }
void run(Module* module) override {
Index numParams = std::stoul(
getPassOptions().getArgumentOrDefault("max-func-params", "16"));
HeapType ABIType(
Signature(Type(std::vector<Type>(numParams, Type::i64)), Type::i64));
std::unordered_map<Name, Name> funcThunks;
ElementUtils::iterAllElementFunctionNames(module, [&](Name& name) {
auto iter = funcThunks.find(name);
if (iter == funcThunks.end()) {
auto thunk = makeThunk(name, module, numParams);
funcThunks[name] = thunk;
name = thunk;
} else {
name = iter->second;
}
});
ParallelFuncCastEmulation(ABIType, numParams).run(getPassRunner(), module);
}
private:
Name makeThunk(Name name, Module* module, Index numParams) {
Name thunk = std::string("byn$fpcast-emu$") + name.toString();
if (module->getFunctionOrNull(thunk)) {
Fatal() << "FuncCastEmulation::makeThunk seems a thunk name already in "
"use. Was the pass already run on this code?";
}
auto* func = module->getFunction(name);
Type type = func->getResults();
Builder builder(*module);
std::vector<Expression*> callOperands;
Index i = 0;
for (const auto& param : func->getParams()) {
callOperands.push_back(
fromABI(builder.makeLocalGet(i++, Type::i64), param, module));
}
auto* call = builder.makeCall(name, callOperands, type);
std::vector<Type> thunkParams;
for (Index i = 0; i < numParams; i++) {
thunkParams.push_back(Type::i64);
}
auto thunkFunc =
builder.makeFunction(thunk,
Signature(Type(thunkParams), Type::i64),
{}, toABI(call, module));
module->addFunction(std::move(thunkFunc));
return thunk;
}
};
Pass* createFuncCastEmulationPass() { return new FuncCastEmulation(); }
}