blob: 58e56cba6992e7ac4a82f537b5067d3b196aaff6 [file] [log] [blame] [edit]
/*
* Copyright 2015 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Lowers 64-bit ints to pairs of 32-bit ints, plus some library routines
//
// This is useful for wasm2asm, as JS has no native 64-bit integer support.
//
#include <memory>
#include <wasm.h>
#include <pass.h>
namespace wasm {
cashew::IString GET_HIGH("getHigh");
struct LowerInt64 : public Pass {
MixedArena* allocator;
std::unique_ptr<NameManager> namer;
void prepare(PassRunner* runner, Module *module) override {
allocator = runner->allocator;
namer = std::unique_ptr<NameManager>(new NameManager());
namer->run(runner, module);
}
std::map<Expression*, Expression*> fixes; // fixed nodes, outputs of lowering, mapped to their high bits
std::map<Name, Name> locals; // maps locals which were i64->i32 to their high bits
void makeGetHigh() {
auto ret = allocator->alloc<CallImport>();
ret->target = GET_HIGH;
ret->type = i32;
return ret;
}
void fixCall(CallBase *call) {
auto& operands = call->operands;
for (size_t i = 0; i < operands.size(); i++) {
auto fix = fixes.find(operands[i]);
if (fix != fixes.end()) {
operands.insert(operands.begin() + i + 1, *fix);
}
}
if (curr->type == i64) {
curr->type = i32;
fixes[curr] = makeGetHigh(); // called function will setHigh
}
}
void visitCall(Call *curr) {
fixCall(curr);
}
void visitCallImport(CallImport *curr) {
fixCall(curr);
}
void visitCallIndirect(CallIndirect *curr) {
fixCall(curr);
}
void visitGetLocal(GetLocal *curr) {
if (curr->type == i64) {
if (locals.count(curr->name) == 0) {
Name highName = namer->getUnique("high");
locals[curr->name] = highName;
};
curr->type = i32;
auto high = allocator->alloc<GetLocal>();
high->name = locals[curr->name];
high->type = i32;
fixes[curr] = high;
}
}
void visitSetLocal(SetLocal *curr) {
if (curr->type == i64) {
Name highName;
if (locals.count(curr->name) == 0) {
highName = namer->getUnique("high");
locals[curr->name] = highName;
} else {
highName = locals[curr->name];
}
curr->type = i32;
auto high = allocator->alloc<GetLocal>();
high->name = highName;
high->type = i32;
fixes[curr] = high;
// Set the high bits
auto set = allocator.alloc<SetLocal>();
set->name = highName;
set->value = fixes[curr->value];
set->type = i32;
assert(set->value);
auto low = allocator->alloc<GetLocal>();
low->name = curr->name;
low->type = i32;
auto ret = allocator.alloc<Block>();
ret->list.push_back(curr);
ret->list.push_back(set);
ret->list.push_back(low); // so the block returns the low bits
ret->type = i32;
fixes[ret] = high;
replaceCurrent(ret);
}
}
// sets an expression to a local, and returns a block
Block* setToLocalForBlock(Expression *value, Name& local, Block *ret = nullptr) {
if (!ret) ret = allocator->alloc<Block>();
if (value->is<GetLocal>()) {
local = value->name;
} else if (value->is<SetLocal>()) {
local = value->name;
} else {
auto set = allocator.alloc<SetLocal>();
set->name = local = namer->getUnique("temp");
set->value = value;
set->type = value->type;
ret->list.push_back(set);
}
return ret;
}
GetLocal* getLocal(Name name) {
auto ret = allocator->alloc<GetLocal>();
ret->name = name;
ret->type = i32;
return ret;
}
void visitLoad(Load *curr) {
if (curr->type == i64) {
Name local;
auto ret = setToLocalForBlock(curr->ptr, local);
curr->ptr = getLocal(local);
curr->type = i32;
curr->bytes = 4;
auto high = allocator->alloc<Load>();
*high = *curr;
high->ptr = getLocal(local);
high->offset += 4;
ret->list.push_back(curr);
fixes[ret] = high;
replaceCurrent(ret);
}
}
void visitStore(Store *curr) {
if (curr->type == i64) {
Name localPtr, localValue;
auto ret = setToLocalForBlock(curr->ptr, localPtr);
setToLocalForBlock(curr->value, localValue);
curr->ptr = getLocal(localPtr);
curr->value = getLocal(localValue);
curr->type = i32;
curr->bytes = 4;
auto high = allocator->alloc<Load>();
*high = *curr;
high->ptr = getLocal(localPtr);
high->value = getLocal(localValue);
high->offset += 4;
ret->list.push_back(high);
ret->list.push_back(curr);
fixes[ret] = high;
replaceCurrent(ret);
}
}
void visitFunction(Function *curr) {
// TODO: new params
for (auto localPair : locals) { // TODO: ignore params
curr->locals.emplace_back(localPair.second, i32);
}
fixes.clear();
locals.clear();
}
};
static RegisterPass<LowerInt64> registerPass("lower-i64", "lowers i64 into pairs of i32s");
} // namespace wasm