blob: e7d473da202d088af643c15fc9b4641935f6c00d [file] [log] [blame]
/*
* 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.
*/
#ifndef wasm_builder_h
#define wasm_builder_h
#include <wasm.h>
namespace wasm {
// Useful data structures
struct NameType {
Name name;
WasmType type;
NameType() : name(nullptr), type(none) {}
NameType(Name name, WasmType type) : name(name), type(type) {}
};
// General AST node builder
class Builder {
public:
Builder(MixedArena& allocator) : allocator(allocator) {}
Builder(Module& wasm) : allocator(wasm.allocator) {}
MixedArena& allocator;
// make* functions, create nodes
Function* makeFunction(Name name,
std::vector<NameType>&& params,
WasmType resultType,
std::vector<NameType>&& vars,
Expression* body = nullptr) {
auto* func = new Function;
func->name = name;
func->result = resultType;
func->body = body;
for (auto& param : params) {
func->params.push_back(param.type);
func->localIndices[param.name] = func->localNames.size();
func->localNames.push_back(param.name);
}
for (auto& var : vars) {
func->vars.push_back(var.type);
func->localIndices[var.name] = func->localNames.size();
func->localNames.push_back(var.name);
}
return func;
}
Nop* makeNop() {
return allocator.alloc<Nop>();
}
Block* makeBlock(Expression* first = nullptr) {
auto* ret = allocator.alloc<Block>();
if (first) {
ret->list.push_back(first, allocator);
ret->finalize();
}
return ret;
}
If* makeIf(Expression* condition, Expression* ifTrue, Expression* ifFalse = nullptr) {
auto* ret = allocator.alloc<If>();
ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse;
ret->finalize();
return ret;
}
Loop* makeLoop(Name name, Expression* body) {
auto* ret = allocator.alloc<Loop>();
ret->name = name; ret->body = body;
ret->finalize();
return ret;
}
Break* makeBreak(Name name, Expression* value = nullptr, Expression* condition = nullptr) {
auto* ret = allocator.alloc<Break>();
ret->name = name; ret->value = value; ret->condition = condition;
ret->finalize();
return ret;
}
template<typename T>
Switch* makeSwitch(T& list, Name default_, Expression* condition, Expression* value = nullptr) {
auto* ret = allocator.alloc<Switch>();
ret->targets.set(list, allocator);
ret->default_ = default_; ret->value = value; ret->condition = condition;
return ret;
}
Call* makeCall(Name target, const std::vector<Expression*>& args, WasmType type) {
auto* call = allocator.alloc<Call>();
call->type = type; // not all functions may exist yet, so type must be provided
call->target = target;
call->operands.set(args, allocator);
return call;
}
CallImport* makeCallImport(Name target, const std::vector<Expression*>& args, WasmType type) {
auto* call = allocator.alloc<CallImport>();
call->type = type; // similar to makeCall, for consistency
call->target = target;
call->operands.set(args, allocator);
return call;
}
template<typename T>
Call* makeCall(Name target, const T& args, WasmType type) {
auto* call = allocator.alloc<Call>();
call->type = type; // not all functions may exist yet, so type must be provided
call->target = target;
call->operands.set(args, allocator);
return call;
}
template<typename T>
CallImport* makeCallImport(Name target, const T& args, WasmType type) {
auto* call = allocator.alloc<CallImport>();
call->type = type; // similar to makeCall, for consistency
call->target = target;
call->operands.set(args, allocator);
return call;
}
CallIndirect* makeCallIndirect(FunctionType* type, Expression* target, const std::vector<Expression*>& args) {
auto* call = allocator.alloc<CallIndirect>();
call->fullType = type->name;
call->type = type->result;
call->target = target;
call->operands.set(args, allocator);
return call;
}
CallIndirect* makeCallIndirect(Name fullType, Expression* target, const std::vector<Expression*>& args, WasmType type) {
auto* call = allocator.alloc<CallIndirect>();
call->fullType = fullType;
call->type = type;
call->target = target;
call->operands.set(args, allocator);
return call;
}
// FunctionType
GetLocal* makeGetLocal(Index index, WasmType type) {
auto* ret = allocator.alloc<GetLocal>();
ret->index = index;
ret->type = type;
return ret;
}
SetLocal* makeSetLocal(Index index, Expression* value) {
auto* ret = allocator.alloc<SetLocal>();
ret->index = index;
ret->value = value;
ret->type = none;
return ret;
}
SetLocal* makeTeeLocal(Index index, Expression* value) {
auto* ret = allocator.alloc<SetLocal>();
ret->index = index;
ret->value = value;
ret->type = value->type;
return ret;
}
GetGlobal* makeGetGlobal(Name name, WasmType type) {
auto* ret = allocator.alloc<GetGlobal>();
ret->name = name;
ret->type = type;
return ret;
}
SetGlobal* makeSetGlobal(Name name, Expression* value) {
auto* ret = allocator.alloc<SetGlobal>();
ret->name = name;
ret->value = value;
return ret;
}
Load* makeLoad(unsigned bytes, bool signed_, uint32_t offset, unsigned align, Expression *ptr, WasmType type) {
auto* ret = allocator.alloc<Load>();
ret->bytes = bytes; ret->signed_ = signed_; ret->offset = offset; ret->align = align; ret->ptr = ptr;
ret->type = type;
return ret;
}
Store* makeStore(unsigned bytes, uint32_t offset, unsigned align, Expression *ptr, Expression *value, WasmType type) {
auto* ret = allocator.alloc<Store>();
ret->bytes = bytes; ret->offset = offset; ret->align = align; ret->ptr = ptr; ret->value = value; ret->valueType = type;
ret->finalize();
assert(isConcreteWasmType(ret->value->type) ? ret->value->type == type : true);
return ret;
}
Const* makeConst(Literal value) {
assert(isConcreteWasmType(value.type));
auto* ret = allocator.alloc<Const>();
ret->value = value;
ret->type = value.type;
return ret;
}
Unary* makeUnary(UnaryOp op, Expression *value) {
auto* ret = allocator.alloc<Unary>();
ret->op = op; ret->value = value;
ret->finalize();
return ret;
}
Binary* makeBinary(BinaryOp op, Expression *left, Expression *right) {
auto* ret = allocator.alloc<Binary>();
ret->op = op; ret->left = left; ret->right = right;
ret->finalize();
return ret;
}
Select* makeSelect(Expression* condition, Expression *ifTrue, Expression *ifFalse) {
auto* ret = allocator.alloc<Select>();
ret->condition = condition; ret->ifTrue = ifTrue; ret->ifFalse = ifFalse;
ret->finalize();
return ret;
}
Return* makeReturn(Expression *value = nullptr) {
auto* ret = allocator.alloc<Return>();
ret->value = value;
return ret;
}
Host* makeHost(HostOp op, Name nameOperand, std::vector<Expression*>&& operands) {
auto* ret = allocator.alloc<Host>();
ret->op = op;
ret->nameOperand = nameOperand;
ret->operands.set(operands, allocator);
ret->finalize();
return ret;
}
Unreachable* makeUnreachable() {
return allocator.alloc<Unreachable>();
}
// Additional helpers
Drop* makeDrop(Expression *value) {
auto* ret = allocator.alloc<Drop>();
ret->value = value;
ret->finalize();
return ret;
}
// Additional utility functions for building on top of nodes
// Convenient to have these on Builder, as it has allocation built in
static Index addParam(Function* func, Name name, WasmType type) {
// only ok to add a param if no vars, otherwise indices are invalidated
assert(func->localIndices.size() == func->params.size());
func->params.push_back(type);
Index index = func->localNames.size();
func->localIndices[name] = index;
func->localNames.push_back(name);
return index;
}
static Index addVar(Function* func, Name name, WasmType type) {
// always ok to add a var, it does not affect other indices
Index index = func->getNumLocals();
if (name.is()) {
// if there is a name, apply it, but here we assume all the rest have names too FIXME
assert(func->localIndices.size() == func->params.size() + func->vars.size());
func->localIndices[name] = index;
func->localNames.push_back(name);
}
func->vars.emplace_back(type);
return index;
}
static Index addVar(Function* func, WasmType type) {
return addVar(func, Name(), type);
}
static void clearLocals(Function* func) {
func->params.clear();
func->vars.clear();
func->localNames.clear();
func->localIndices.clear();
}
// ensure a node is a block, if it isn't already, and optionally append to the block
Block* blockify(Expression* any, Expression* append = nullptr) {
Block* block = nullptr;
if (any) block = any->dynCast<Block>();
if (!block) block = makeBlock(any);
if (append) {
block->list.push_back(append, allocator);
block->finalize(); // TODO: move out of if
}
return block;
}
// ensure a node is a block, if it isn't already, and optionally append to the block
// this variant sets a name for the block, so it will not reuse a block already named
Block* blockifyWithName(Expression* any, Name name, Expression* append = nullptr) {
Block* block = nullptr;
if (any) block = any->dynCast<Block>();
if (!block || block->name.is()) block = makeBlock(any);
block->name = name;
if (append) {
block->list.push_back(append, allocator);
block->finalize(); // TODO: move out of if
}
return block;
}
// ensures the first node is a block, if it isn't already, and merges in the second,
// either as a single element or, if a block, by appending to the first block
Block* blockifyMerge(Expression* any, Expression* append) {
Block* block = nullptr;
if (any) block = any->dynCast<Block>();
if (!block) {
block = makeBlock(any);
} else {
assert(!isConcreteWasmType(block->type));
}
auto* other = append->dynCast<Block>();
if (!other) {
block->list.push_back(append, allocator);
} else {
for (auto* item : other->list) {
block->list.push_back(item, allocator);
}
}
block->finalize(); // TODO: move out of if
return block;
}
// a helper for the common pattern of a sequence of two expressions. Similar to
// blockify, but does *not* reuse a block if the first is one.
Block* makeSequence(Expression* left, Expression* right) {
auto* block = makeBlock(left);
block->list.push_back(right, allocator);
block->finalize();
return block;
}
// Grab a slice out of a block, replacing it with nops, and returning
// either another block with the contents (if more than 1) or a single expression
Expression* stealSlice(Block* input, Index from, Index to) {
Expression* ret;
if (to == from + 1) {
// just one
ret = input->list[from];
} else {
auto* block = allocator.alloc<Block>();
for (Index i = from; i < to; i++) {
block->list.push_back(input->list[i], allocator);
}
block->finalize();
ret = block;
}
if (to == input->list.size()) {
input->list.resize(from, allocator);
} else {
for (Index i = from; i < to; i++) {
input->list[i] = allocator.alloc<Nop>();
}
}
input->finalize();
return ret;
}
// Drop an expression if it has a concrete type
Expression* dropIfConcretelyTyped(Expression* curr) {
if (!isConcreteWasmType(curr->type)) return curr;
return makeDrop(curr);
}
void flip(If* iff) {
std::swap(iff->ifTrue, iff->ifFalse);
iff->condition = makeUnary(EqZInt32, iff->condition);
}
};
} // namespace wasm
#endif // wasm_builder_h