blob: aafea8a973a04fddf13f276cdfd8821f067483f3 [file] [log] [blame] [edit]
/*
* Copyright 2016 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.
*/
//
// Transforms code into SSA form. That ensures each variable has a
// single assignment.
//
// Note that "SSA form" usually means SSA + phis. This pass does not
// create phis, we still emit something in our AST, which does not
// have a phi instruction. What we emit when control flow joins
// require more than one input to a value is multiple assignments
// to the same local, with the SSA guarantee that one and only one
// of those assignments will arrive at the uses of that "merge local".
// TODO: consider adding a "proper" phi node to the AST, that passes
// can utilize
//
#include <iterator>
#include "wasm.h"
#include "pass.h"
#include "wasm-builder.h"
#include "support/permutations.h"
#include "ast/literal-utils.h"
#include "ast/local-graph.h"
namespace wasm {
// A set we know is impossible / not in the ast
SetLocal IMPOSSIBLE_SET;
// Tracks assignments to locals, assuming single-assignment form, i.e.,
// each assignment creates a new variable.
struct SSAify : public Pass {
bool isFunctionParallel() override { return true; }
Pass* create() override { return new SSAify; }
Module* module;
Function* func;
std::vector<Expression*> functionPrepends; // things we add to the function prologue
void runFunction(PassRunner* runner, Module* module_, Function* func_) override {
module = module_;
func = func_;
LocalGraph graph(func, module);
// create new local indexes, one for each set
createNewIndexes(graph);
// we now know the sets for each get, and can compute get indexes and handle phis
computeGetsAndPhis(graph);
// add prepends to function
addPrepends();
}
void createNewIndexes(LocalGraph& graph) {
for (auto& pair : graph.locations) {
auto* curr = pair.first;
if (auto* set = curr->dynCast<SetLocal>()) {
set->index = addLocal(func->getLocalType(set->index));
}
}
}
void computeGetsAndPhis(LocalGraph& graph) {
for (auto& iter : graph.getSetses) {
auto* get = iter.first;
auto& sets = iter.second;
if (sets.size() == 0) {
continue; // unreachable, ignore
}
if (sets.size() == 1) {
// easy, just one set, use its index
auto* set = *sets.begin();
if (set) {
get->index = set->index;
} else {
// no set, assign param or zero
if (func->isParam(get->index)) {
// leave it, it's fine
} else {
// zero it out
(*graph.locations[get]) = LiteralUtils::makeZero(get->type, *module);
}
}
continue;
}
// more than 1 set, need a phi: a new local written to at each of the sets
// if there is already a local with that property, reuse it
auto gatherIndexes = [](SetLocal* set) {
std::set<Index> ret;
while (set) {
ret.insert(set->index);
set = set->value->dynCast<SetLocal>();
}
return ret;
};
auto indexes = gatherIndexes(*sets.begin());
for (auto* set : sets) {
if (set == *sets.begin()) continue;
auto currIndexes = gatherIndexes(set);
std::vector<Index> intersection;
std::set_intersection(indexes.begin(), indexes.end(),
currIndexes.begin(), currIndexes.end(),
std::back_inserter(intersection));
indexes.clear();
if (intersection.empty()) break;
// TODO: or keep sorted vectors?
for (Index i : intersection) {
indexes.insert(i);
}
}
if (!indexes.empty()) {
// we found an index, use it
get->index = *indexes.begin();
} else {
// we need to create a local for this phi'ing
auto new_ = addLocal(get->type);
auto old = get->index;
get->index = new_;
Builder builder(*module);
// write to the local in each of our sets
for (auto* set : sets) {
if (set) {
// a set exists, just add a tee of its value
auto* value = set->value;
auto* tee = builder.makeTeeLocal(
new_,
value
);
set->value = tee;
// the value may have been something we tracked the location
// of. if so, update that, since we moved it into the tee
if (graph.locations.count(value) > 0) {
assert(graph.locations[value] == &set->value);
graph.locations[value] = &tee->value;
}
} else {
// this is a param or the zero init value.
if (func->isParam(old)) {
// we add a set with the proper
// param value at the beginning of the function
auto* set = builder.makeSetLocal(
new_,
builder.makeGetLocal(old, func->getLocalType(old))
);
functionPrepends.push_back(set);
} else {
// this is a zero init, so we don't need to do anything actually
}
}
}
}
}
}
Index addLocal(WasmType type) {
return Builder::addVar(func, type);
}
void addPrepends() {
if (functionPrepends.size() > 0) {
Builder builder(*module);
auto* block = builder.makeBlock();
for (auto* pre : functionPrepends) {
block->list.push_back(pre);
}
block->list.push_back(func->body);
block->finalize(func->body->type);
func->body = block;
}
}
};
Pass *createSSAifyPass() {
return new SSAify();
}
} // namespace wasm