blob: 8b34d6933d2ee177748681555c5ae1a63810da5d [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_wasm_builder_h
#define wasm_wasm_builder_h
#include "ir/manipulation.h"
#include "parsing.h"
#include "wasm.h"
namespace wasm {
// Useful data structures
struct NameType {
Name name;
Type type;
NameType() : name(nullptr), type(Type::none) {}
NameType(Name name, Type type) : name(name), type(type) {}
};
// General AST node builder
class Builder {
Module& wasm;
public:
Builder(Module& wasm) : wasm(wasm) {}
// make* functions create an expression instance.
static std::unique_ptr<Function> makeFunction(Name name,
HeapType type,
std::vector<Type>&& vars,
Expression* body = nullptr) {
assert(type.isSignature());
auto func = std::make_unique<Function>();
func->name = name;
func->type = type;
func->body = body;
func->vars.swap(vars);
return func;
}
static std::unique_ptr<Function> makeFunction(Name name,
std::vector<NameType>&& params,
HeapType type,
std::vector<NameType>&& vars,
Expression* body = nullptr) {
assert(type.isSignature());
auto func = std::make_unique<Function>();
func->name = name;
func->type = type;
func->body = body;
for (size_t i = 0; i < params.size(); ++i) {
NameType& param = params[i];
assert(func->getParams()[i] == param.type);
Index index = func->localNames.size();
func->localIndices[param.name] = index;
func->localNames[index] = param.name;
}
for (auto& var : vars) {
func->vars.push_back(var.type);
Index index = func->localNames.size();
func->localIndices[var.name] = index;
func->localNames[index] = var.name;
}
return func;
}
static std::unique_ptr<Table> makeTable(Name name,
Type type = Type(HeapType::func,
Nullable),
Address initial = 0,
Address max = Table::kMaxSize) {
auto table = std::make_unique<Table>();
table->name = name;
table->type = type;
table->initial = initial;
table->max = max;
return table;
}
static std::unique_ptr<ElementSegment>
makeElementSegment(Name name,
Name table,
Expression* offset = nullptr,
Type type = Type(HeapType::func, Nullable)) {
auto seg = std::make_unique<ElementSegment>();
seg->name = name;
seg->table = table;
seg->offset = offset;
seg->type = type;
return seg;
}
static std::unique_ptr<Memory> makeMemory(Name name,
Address initial = 0,
Address max = Memory::kMaxSize32,
bool shared = false,
Type indexType = Type::i32) {
auto memory = std::make_unique<Memory>();
memory->name = name;
memory->initial = initial;
memory->max = max;
memory->shared = shared;
memory->indexType = indexType;
return memory;
}
static std::unique_ptr<DataSegment>
makeDataSegment(Name name = "",
Name memory = "",
bool isPassive = false,
Expression* offset = nullptr,
const char* init = "",
Address size = 0) {
auto seg = std::make_unique<DataSegment>();
seg->name = name;
seg->memory = memory;
seg->isPassive = isPassive;
seg->offset = offset;
seg->data.resize(size);
std::copy_n(init, size, seg->data.begin());
return seg;
}
static std::unique_ptr<Export>
makeExport(Name name, Name value, ExternalKind kind) {
auto export_ = std::make_unique<Export>();
export_->name = name;
export_->value = value;
export_->kind = kind;
return export_;
}
enum Mutability { Mutable, Immutable };
static std::unique_ptr<Global>
makeGlobal(Name name, Type type, Expression* init, Mutability mutable_) {
auto glob = std::make_unique<Global>();
glob->name = name;
glob->type = type;
glob->init = init;
glob->mutable_ = mutable_ == Mutable;
return glob;
}
static std::unique_ptr<Tag> makeTag(Name name, Signature sig) {
auto tag = std::make_unique<Tag>();
tag->name = name;
tag->sig = sig;
return tag;
}
// IR nodes
Nop* makeNop() { return wasm.allocator.alloc<Nop>(); }
Block* makeBlock(Expression* first = nullptr) {
auto* ret = wasm.allocator.alloc<Block>();
if (first) {
ret->list.push_back(first);
ret->finalize();
}
return ret;
}
Block* makeBlock(Name name, Expression* first = nullptr) {
auto* ret = makeBlock(first);
ret->name = name;
ret->finalize();
return ret;
}
template<typename T>
using bool_if_not_expr_t =
std::enable_if_t<std::negation_v<std::is_convertible<T, Expression*>>,
bool>;
template<typename T, bool_if_not_expr_t<T> = true>
Block* makeBlock(const T& items) {
auto* ret = wasm.allocator.alloc<Block>();
ret->list.set(items);
ret->finalize();
return ret;
}
template<typename T, bool_if_not_expr_t<T> = true>
Block* makeBlock(const T& items, Type type) {
auto* ret = wasm.allocator.alloc<Block>();
ret->list.set(items);
ret->finalize(type);
return ret;
}
template<typename T, bool_if_not_expr_t<T> = true>
Block* makeBlock(Name name, const T& items, Type type) {
auto* ret = wasm.allocator.alloc<Block>();
ret->name = name;
ret->list.set(items);
ret->finalize(type);
return ret;
}
Block* makeBlock(std::initializer_list<Expression*>&& items) {
return makeBlock(items);
}
Block* makeBlock(std::initializer_list<Expression*>&& items, Type type) {
return makeBlock(items, type);
}
Block*
makeBlock(Name name, std::initializer_list<Expression*>&& items, Type type) {
return makeBlock(name, items, type);
}
If* makeIf(Expression* condition,
Expression* ifTrue,
Expression* ifFalse = nullptr) {
auto* ret = wasm.allocator.alloc<If>();
ret->condition = condition;
ret->ifTrue = ifTrue;
ret->ifFalse = ifFalse;
ret->finalize();
return ret;
}
If* makeIf(Expression* condition,
Expression* ifTrue,
Expression* ifFalse,
Type type) {
auto* ret = wasm.allocator.alloc<If>();
ret->condition = condition;
ret->ifTrue = ifTrue;
ret->ifFalse = ifFalse;
ret->finalize(type);
return ret;
}
Loop* makeLoop(Name name, Expression* body) {
auto* ret = wasm.allocator.alloc<Loop>();
ret->name = name;
ret->body = body;
ret->finalize();
return ret;
}
Loop* makeLoop(Name name, Expression* body, Type type) {
auto* ret = wasm.allocator.alloc<Loop>();
ret->name = name;
ret->body = body;
ret->finalize(type);
return ret;
}
Break* makeBreak(Name name,
Expression* value = nullptr,
Expression* condition = nullptr) {
auto* ret = wasm.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 = wasm.allocator.alloc<Switch>();
ret->targets.set(list);
ret->default_ = default_;
ret->value = value;
ret->condition = condition;
return ret;
}
Call* makeCall(Name target,
const std::vector<Expression*>& args,
Type type,
bool isReturn = false) {
auto* call = wasm.allocator.alloc<Call>();
// not all functions may exist yet, so type must be provided
call->type = type;
call->target = target;
call->operands.set(args);
call->isReturn = isReturn;
return call;
}
template<typename T>
Call* makeCall(Name target, const T& args, Type type, bool isReturn = false) {
auto* call = wasm.allocator.alloc<Call>();
// not all functions may exist yet, so type must be provided
call->type = type;
call->target = target;
call->operands.set(args);
call->isReturn = isReturn;
call->finalize();
return call;
}
template<typename T>
CallIndirect* makeCallIndirect(const Name table,
Expression* target,
const T& args,
HeapType heapType,
bool isReturn = false) {
assert(heapType.isSignature());
auto* call = wasm.allocator.alloc<CallIndirect>();
call->table = table;
call->heapType = heapType;
call->type = heapType.getSignature().results;
call->target = target;
call->operands.set(args);
call->isReturn = isReturn;
call->finalize();
return call;
}
template<typename T>
CallRef* makeCallRef(Expression* target,
const T& args,
Type type,
bool isReturn = false) {
auto* call = wasm.allocator.alloc<CallRef>();
call->type = type;
call->target = target;
call->operands.set(args);
call->isReturn = isReturn;
call->finalize();
return call;
}
LocalGet* makeLocalGet(Index index, Type type) {
auto* ret = wasm.allocator.alloc<LocalGet>();
ret->index = index;
ret->type = type;
return ret;
}
LocalSet* makeLocalSet(Index index, Expression* value) {
auto* ret = wasm.allocator.alloc<LocalSet>();
ret->index = index;
ret->value = value;
ret->makeSet();
ret->finalize();
return ret;
}
LocalSet* makeLocalTee(Index index, Expression* value, Type type) {
auto* ret = wasm.allocator.alloc<LocalSet>();
ret->index = index;
ret->value = value;
ret->makeTee(type);
return ret;
}
GlobalGet* makeGlobalGet(Name name, Type type) {
auto* ret = wasm.allocator.alloc<GlobalGet>();
ret->name = name;
ret->type = type;
return ret;
}
GlobalSet* makeGlobalSet(Name name, Expression* value) {
auto* ret = wasm.allocator.alloc<GlobalSet>();
ret->name = name;
ret->value = value;
ret->finalize();
return ret;
}
Load* makeLoad(unsigned bytes,
bool signed_,
Address offset,
unsigned align,
Expression* ptr,
Type type,
Name memory) {
auto* ret = wasm.allocator.alloc<Load>();
ret->isAtomic = false;
ret->bytes = bytes;
ret->signed_ = signed_;
ret->offset = offset;
ret->align = align;
ret->ptr = ptr;
ret->type = type;
ret->memory = memory;
return ret;
}
Load* makeAtomicLoad(
unsigned bytes, Address offset, Expression* ptr, Type type, Name memory) {
Load* load = makeLoad(bytes, false, offset, bytes, ptr, type, memory);
load->isAtomic = true;
return load;
}
AtomicWait* makeAtomicWait(Expression* ptr,
Expression* expected,
Expression* timeout,
Type expectedType,
Address offset,
Name memory) {
auto* wait = wasm.allocator.alloc<AtomicWait>();
wait->offset = offset;
wait->ptr = ptr;
wait->expected = expected;
wait->timeout = timeout;
wait->expectedType = expectedType;
wait->finalize();
wait->memory = memory;
return wait;
}
AtomicNotify* makeAtomicNotify(Expression* ptr,
Expression* notifyCount,
Address offset,
Name memory) {
auto* notify = wasm.allocator.alloc<AtomicNotify>();
notify->offset = offset;
notify->ptr = ptr;
notify->notifyCount = notifyCount;
notify->finalize();
notify->memory = memory;
return notify;
}
AtomicFence* makeAtomicFence() { return wasm.allocator.alloc<AtomicFence>(); }
Store* makeStore(unsigned bytes,
Address offset,
unsigned align,
Expression* ptr,
Expression* value,
Type type,
Name memory) {
auto* ret = wasm.allocator.alloc<Store>();
ret->isAtomic = false;
ret->bytes = bytes;
ret->offset = offset;
ret->align = align;
ret->ptr = ptr;
ret->value = value;
ret->valueType = type;
ret->memory = memory;
ret->finalize();
assert(ret->value->type.isConcrete() ? ret->value->type == type : true);
return ret;
}
Store* makeAtomicStore(unsigned bytes,
Address offset,
Expression* ptr,
Expression* value,
Type type,
Name memory) {
Store* store = makeStore(bytes, offset, bytes, ptr, value, type, memory);
store->isAtomic = true;
return store;
}
AtomicRMW* makeAtomicRMW(AtomicRMWOp op,
unsigned bytes,
Address offset,
Expression* ptr,
Expression* value,
Type type,
Name memory) {
auto* ret = wasm.allocator.alloc<AtomicRMW>();
ret->op = op;
ret->bytes = bytes;
ret->offset = offset;
ret->ptr = ptr;
ret->value = value;
ret->type = type;
ret->finalize();
ret->memory = memory;
return ret;
}
AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes,
Address offset,
Expression* ptr,
Expression* expected,
Expression* replacement,
Type type,
Name memory) {
auto* ret = wasm.allocator.alloc<AtomicCmpxchg>();
ret->bytes = bytes;
ret->offset = offset;
ret->ptr = ptr;
ret->expected = expected;
ret->replacement = replacement;
ret->type = type;
ret->finalize();
ret->memory = memory;
return ret;
}
SIMDExtract*
makeSIMDExtract(SIMDExtractOp op, Expression* vec, uint8_t index) {
auto* ret = wasm.allocator.alloc<SIMDExtract>();
ret->op = op;
ret->vec = vec;
ret->index = index;
ret->finalize();
return ret;
}
SIMDReplace* makeSIMDReplace(SIMDReplaceOp op,
Expression* vec,
uint8_t index,
Expression* value) {
auto* ret = wasm.allocator.alloc<SIMDReplace>();
ret->op = op;
ret->vec = vec;
ret->index = index;
ret->value = value;
ret->finalize();
return ret;
}
SIMDShuffle* makeSIMDShuffle(Expression* left,
Expression* right,
const std::array<uint8_t, 16>& mask) {
auto* ret = wasm.allocator.alloc<SIMDShuffle>();
ret->left = left;
ret->right = right;
ret->mask = mask;
ret->finalize();
return ret;
}
SIMDTernary* makeSIMDTernary(SIMDTernaryOp op,
Expression* a,
Expression* b,
Expression* c) {
auto* ret = wasm.allocator.alloc<SIMDTernary>();
ret->op = op;
ret->a = a;
ret->b = b;
ret->c = c;
ret->finalize();
return ret;
}
SIMDShift* makeSIMDShift(SIMDShiftOp op, Expression* vec, Expression* shift) {
auto* ret = wasm.allocator.alloc<SIMDShift>();
ret->op = op;
ret->vec = vec;
ret->shift = shift;
ret->finalize();
return ret;
}
SIMDLoad* makeSIMDLoad(SIMDLoadOp op,
Address offset,
Address align,
Expression* ptr,
Name memory) {
auto* ret = wasm.allocator.alloc<SIMDLoad>();
ret->op = op;
ret->offset = offset;
ret->align = align;
ret->ptr = ptr;
ret->memory = memory;
ret->finalize();
return ret;
}
SIMDLoadStoreLane* makeSIMDLoadStoreLane(SIMDLoadStoreLaneOp op,
Address offset,
Address align,
uint8_t index,
Expression* ptr,
Expression* vec,
Name memory) {
auto* ret = wasm.allocator.alloc<SIMDLoadStoreLane>();
ret->op = op;
ret->offset = offset;
ret->align = align;
ret->index = index;
ret->ptr = ptr;
ret->vec = vec;
ret->finalize();
ret->memory = memory;
return ret;
}
MemoryInit* makeMemoryInit(uint32_t segment,
Expression* dest,
Expression* offset,
Expression* size,
Name memory) {
auto* ret = wasm.allocator.alloc<MemoryInit>();
ret->segment = segment;
ret->dest = dest;
ret->offset = offset;
ret->size = size;
ret->memory = memory;
ret->finalize();
return ret;
}
DataDrop* makeDataDrop(uint32_t segment) {
auto* ret = wasm.allocator.alloc<DataDrop>();
ret->segment = segment;
ret->finalize();
return ret;
}
MemoryCopy* makeMemoryCopy(Expression* dest,
Expression* source,
Expression* size,
Name destMemory,
Name sourceMemory) {
auto* ret = wasm.allocator.alloc<MemoryCopy>();
ret->dest = dest;
ret->source = source;
ret->size = size;
ret->destMemory = destMemory;
ret->sourceMemory = sourceMemory;
ret->finalize();
return ret;
}
MemoryFill* makeMemoryFill(Expression* dest,
Expression* value,
Expression* size,
Name memory) {
auto* ret = wasm.allocator.alloc<MemoryFill>();
ret->dest = dest;
ret->value = value;
ret->size = size;
ret->memory = memory;
ret->finalize();
return ret;
}
Const* makeConst(Literal value) {
assert(value.type.isNumber());
auto* ret = wasm.allocator.alloc<Const>();
ret->value = value;
ret->type = value.type;
return ret;
}
template<typename T> Const* makeConst(T x) { return makeConst(Literal(x)); }
Unary* makeUnary(UnaryOp op, Expression* value) {
auto* ret = wasm.allocator.alloc<Unary>();
ret->op = op;
ret->value = value;
ret->finalize();
return ret;
}
Const* makeConstPtr(uint64_t val, Type indexType) {
return makeConst(Literal::makeFromInt64(val, indexType));
}
Binary* makeBinary(BinaryOp op, Expression* left, Expression* right) {
auto* ret = wasm.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 = wasm.allocator.alloc<Select>();
ret->condition = condition;
ret->ifTrue = ifTrue;
ret->ifFalse = ifFalse;
ret->finalize();
return ret;
}
Select* makeSelect(Expression* condition,
Expression* ifTrue,
Expression* ifFalse,
Type type) {
auto* ret = wasm.allocator.alloc<Select>();
ret->condition = condition;
ret->ifTrue = ifTrue;
ret->ifFalse = ifFalse;
ret->finalize(type);
return ret;
}
Return* makeReturn(Expression* value = nullptr) {
auto* ret = wasm.allocator.alloc<Return>();
ret->value = value;
return ret;
}
// Some APIs can be told if the memory is 32 or 64-bit. If they are not
// informed of that, then the memory they refer to is looked up and that
// information fetched from there.
enum MemoryInfo { Memory32, Memory64, Unspecified };
bool isMemory64(Name memoryName, MemoryInfo info) {
return info == MemoryInfo::Memory64 || (info == MemoryInfo::Unspecified &&
wasm.getMemory(memoryName)->is64());
}
MemorySize* makeMemorySize(Name memoryName,
MemoryInfo info = MemoryInfo::Unspecified) {
auto* ret = wasm.allocator.alloc<MemorySize>();
if (isMemory64(memoryName, info)) {
ret->make64();
}
ret->memory = memoryName;
ret->finalize();
return ret;
}
MemoryGrow* makeMemoryGrow(Expression* delta,
Name memoryName,
MemoryInfo info = MemoryInfo::Unspecified) {
auto* ret = wasm.allocator.alloc<MemoryGrow>();
if (isMemory64(memoryName, info)) {
ret->make64();
}
ret->delta = delta;
ret->memory = memoryName;
ret->finalize();
return ret;
}
RefNull* makeRefNull(HeapType type) {
auto* ret = wasm.allocator.alloc<RefNull>();
ret->finalize(Type(type.getBottom(), Nullable));
return ret;
}
RefNull* makeRefNull(Type type) {
assert(type.isNullable() && type.isNull());
auto* ret = wasm.allocator.alloc<RefNull>();
ret->finalize(type);
return ret;
}
RefIsNull* makeRefIsNull(Expression* value) {
auto* ret = wasm.allocator.alloc<RefIsNull>();
ret->value = value;
ret->finalize();
return ret;
}
RefFunc* makeRefFunc(Name func, HeapType heapType) {
auto* ret = wasm.allocator.alloc<RefFunc>();
ret->func = func;
ret->finalize(Type(heapType, NonNullable));
return ret;
}
RefEq* makeRefEq(Expression* left, Expression* right) {
auto* ret = wasm.allocator.alloc<RefEq>();
ret->left = left;
ret->right = right;
ret->finalize();
return ret;
}
TableGet* makeTableGet(Name table, Expression* index, Type type) {
auto* ret = wasm.allocator.alloc<TableGet>();
ret->table = table;
ret->index = index;
ret->type = type;
ret->finalize();
return ret;
}
TableSet* makeTableSet(Name table, Expression* index, Expression* value) {
auto* ret = wasm.allocator.alloc<TableSet>();
ret->table = table;
ret->index = index;
ret->value = value;
ret->finalize();
return ret;
}
TableSize* makeTableSize(Name table) {
auto* ret = wasm.allocator.alloc<TableSize>();
ret->table = table;
ret->finalize();
return ret;
}
TableGrow* makeTableGrow(Name table, Expression* value, Expression* delta) {
auto* ret = wasm.allocator.alloc<TableGrow>();
ret->table = table;
ret->value = value;
ret->delta = delta;
ret->finalize();
return ret;
}
private:
Try* makeTry(Name name,
Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies,
Name delegateTarget,
Type type,
bool hasType) { // differentiate whether a type was passed in
auto* ret = wasm.allocator.alloc<Try>();
ret->name = name;
ret->body = body;
ret->catchTags.set(catchTags);
ret->catchBodies.set(catchBodies);
if (hasType) {
ret->finalize(type);
} else {
ret->finalize();
}
return ret;
}
public:
Try* makeTry(Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies) {
return makeTry(
Name(), body, catchTags, catchBodies, Name(), Type::none, false);
}
Try* makeTry(Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies,
Type type) {
return makeTry(Name(), body, catchTags, catchBodies, Name(), type, true);
}
Try* makeTry(Name name,
Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies) {
return makeTry(
name, body, catchTags, catchBodies, Name(), Type::none, false);
}
Try* makeTry(Name name,
Expression* body,
const std::vector<Name>& catchTags,
const std::vector<Expression*>& catchBodies,
Type type) {
return makeTry(name, body, catchTags, catchBodies, Name(), type, true);
}
Try* makeTry(Expression* body, Name delegateTarget) {
return makeTry(Name(), body, {}, {}, delegateTarget, Type::none, false);
}
Try* makeTry(Expression* body, Name delegateTarget, Type type) {
return makeTry(Name(), body, {}, {}, delegateTarget, type, true);
}
Try* makeTry(Name name, Expression* body, Name delegateTarget) {
return makeTry(name, body, {}, {}, delegateTarget, Type::none, false);
}
Try* makeTry(Name name, Expression* body, Name delegateTarget, Type type) {
return makeTry(name, body, {}, {}, delegateTarget, type, true);
}
Throw* makeThrow(Tag* tag, const std::vector<Expression*>& args) {
return makeThrow(tag->name, args);
}
Throw* makeThrow(Name tag, const std::vector<Expression*>& args) {
auto* ret = wasm.allocator.alloc<Throw>();
ret->tag = tag;
ret->operands.set(args);
ret->finalize();
return ret;
}
Rethrow* makeRethrow(Name target) {
auto* ret = wasm.allocator.alloc<Rethrow>();
ret->target = target;
ret->finalize();
return ret;
}
Unreachable* makeUnreachable() { return wasm.allocator.alloc<Unreachable>(); }
Pop* makePop(Type type) {
auto* ret = wasm.allocator.alloc<Pop>();
ret->type = type;
ret->finalize();
return ret;
}
template<typename ListType> TupleMake* makeTupleMake(ListType&& operands) {
auto* ret = wasm.allocator.alloc<TupleMake>();
ret->operands.set(operands);
ret->finalize();
return ret;
}
TupleExtract* makeTupleExtract(Expression* tuple, Index index) {
auto* ret = wasm.allocator.alloc<TupleExtract>();
ret->tuple = tuple;
ret->index = index;
ret->finalize();
return ret;
}
I31New* makeI31New(Expression* value) {
auto* ret = wasm.allocator.alloc<I31New>();
ret->value = value;
ret->finalize();
return ret;
}
I31Get* makeI31Get(Expression* i31, bool signed_) {
auto* ret = wasm.allocator.alloc<I31Get>();
ret->i31 = i31;
ret->signed_ = signed_;
ret->finalize();
return ret;
}
RefTest* makeRefTest(Expression* ref, Type castType) {
auto* ret = wasm.allocator.alloc<RefTest>();
ret->ref = ref;
ret->castType = castType;
ret->finalize();
return ret;
}
RefCast* makeRefCast(Expression* ref, Type type, RefCast::Safety safety) {
auto* ret = wasm.allocator.alloc<RefCast>();
ret->ref = ref;
ret->type = type;
ret->safety = safety;
ret->finalize();
return ret;
}
BrOn*
makeBrOn(BrOnOp op, Name name, Expression* ref, Type castType = Type::none) {
auto* ret = wasm.allocator.alloc<BrOn>();
ret->op = op;
ret->name = name;
ret->ref = ref;
ret->castType = castType;
ret->finalize();
return ret;
}
template<typename T> StructNew* makeStructNew(HeapType type, const T& args) {
auto* ret = wasm.allocator.alloc<StructNew>();
ret->operands.set(args);
ret->type = Type(type, NonNullable);
ret->finalize();
return ret;
}
StructGet*
makeStructGet(Index index, Expression* ref, Type type, bool signed_ = false) {
auto* ret = wasm.allocator.alloc<StructGet>();
ret->index = index;
ret->ref = ref;
ret->type = type;
ret->signed_ = signed_;
ret->finalize();
return ret;
}
StructSet* makeStructSet(Index index, Expression* ref, Expression* value) {
auto* ret = wasm.allocator.alloc<StructSet>();
ret->index = index;
ret->ref = ref;
ret->value = value;
ret->finalize();
return ret;
}
ArrayNew*
makeArrayNew(HeapType type, Expression* size, Expression* init = nullptr) {
auto* ret = wasm.allocator.alloc<ArrayNew>();
ret->size = size;
ret->init = init;
ret->type = Type(type, NonNullable);
ret->finalize();
return ret;
}
ArrayNewSeg* makeArrayNewSeg(ArrayNewSegOp op,
HeapType type,
Index seg,
Expression* offset,
Expression* size) {
auto* ret = wasm.allocator.alloc<ArrayNewSeg>();
ret->op = op;
ret->segment = seg;
ret->offset = offset;
ret->size = size;
ret->type = Type(type, NonNullable);
ret->finalize();
return ret;
}
ArrayNewFixed* makeArrayNewFixed(HeapType type,
const std::vector<Expression*>& values) {
auto* ret = wasm.allocator.alloc<ArrayNewFixed>();
ret->values.set(values);
ret->type = Type(type, NonNullable);
ret->finalize();
return ret;
}
ArrayGet* makeArrayGet(Expression* ref,
Expression* index,
Type type,
bool signed_ = false) {
auto* ret = wasm.allocator.alloc<ArrayGet>();
ret->ref = ref;
ret->index = index;
ret->type = type;
ret->signed_ = signed_;
ret->finalize();
return ret;
}
ArraySet*
makeArraySet(Expression* ref, Expression* index, Expression* value) {
auto* ret = wasm.allocator.alloc<ArraySet>();
ret->ref = ref;
ret->index = index;
ret->value = value;
ret->finalize();
return ret;
}
ArrayLen* makeArrayLen(Expression* ref) {
auto* ret = wasm.allocator.alloc<ArrayLen>();
ret->ref = ref;
ret->finalize();
return ret;
}
ArrayCopy* makeArrayCopy(Expression* destRef,
Expression* destIndex,
Expression* srcRef,
Expression* srcIndex,
Expression* length) {
auto* ret = wasm.allocator.alloc<ArrayCopy>();
ret->destRef = destRef;
ret->destIndex = destIndex;
ret->srcRef = srcRef;
ret->srcIndex = srcIndex;
ret->length = length;
ret->finalize();
return ret;
}
RefAs* makeRefAs(RefAsOp op, Expression* value) {
auto* ret = wasm.allocator.alloc<RefAs>();
ret->op = op;
ret->value = value;
ret->finalize();
return ret;
}
StringNew* makeStringNew(StringNewOp op,
Expression* ptr,
Expression* length,
bool try_) {
auto* ret = wasm.allocator.alloc<StringNew>();
ret->op = op;
ret->ptr = ptr;
ret->length = length;
ret->try_ = try_;
ret->finalize();
return ret;
}
StringNew* makeStringNew(StringNewOp op,
Expression* ptr,
Expression* start,
Expression* end,
bool try_) {
auto* ret = wasm.allocator.alloc<StringNew>();
ret->op = op;
ret->ptr = ptr;
ret->start = start;
ret->end = end;
ret->try_ = try_;
ret->finalize();
return ret;
}
StringConst* makeStringConst(Name string) {
auto* ret = wasm.allocator.alloc<StringConst>();
ret->string = string;
ret->finalize();
return ret;
}
StringMeasure* makeStringMeasure(StringMeasureOp op, Expression* ref) {
auto* ret = wasm.allocator.alloc<StringMeasure>();
ret->op = op;
ret->ref = ref;
ret->finalize();
return ret;
}
StringEncode* makeStringEncode(StringEncodeOp op,
Expression* ref,
Expression* ptr,
Expression* start = nullptr) {
auto* ret = wasm.allocator.alloc<StringEncode>();
ret->op = op;
ret->ref = ref;
ret->ptr = ptr;
ret->start = start;
ret->finalize();
return ret;
}
StringConcat* makeStringConcat(Expression* left, Expression* right) {
auto* ret = wasm.allocator.alloc<StringConcat>();
ret->left = left;
ret->right = right;
ret->finalize();
return ret;
}
StringEq* makeStringEq(StringEqOp op, Expression* left, Expression* right) {
auto* ret = wasm.allocator.alloc<StringEq>();
ret->op = op;
ret->left = left;
ret->right = right;
ret->finalize();
return ret;
}
StringAs* makeStringAs(StringAsOp op, Expression* ref) {
auto* ret = wasm.allocator.alloc<StringAs>();
ret->op = op;
ret->ref = ref;
ret->finalize();
return ret;
}
StringWTF8Advance*
makeStringWTF8Advance(Expression* ref, Expression* pos, Expression* bytes) {
auto* ret = wasm.allocator.alloc<StringWTF8Advance>();
ret->ref = ref;
ret->pos = pos;
ret->bytes = bytes;
ret->finalize();
return ret;
}
StringWTF16Get* makeStringWTF16Get(Expression* ref, Expression* pos) {
auto* ret = wasm.allocator.alloc<StringWTF16Get>();
ret->ref = ref;
ret->pos = pos;
ret->finalize();
return ret;
}
StringIterNext* makeStringIterNext(Expression* ref) {
auto* ret = wasm.allocator.alloc<StringIterNext>();
ret->ref = ref;
ret->finalize();
return ret;
}
StringIterMove*
makeStringIterMove(StringIterMoveOp op, Expression* ref, Expression* num) {
auto* ret = wasm.allocator.alloc<StringIterMove>();
ret->op = op;
ret->ref = ref;
ret->num = num;
ret->finalize();
return ret;
}
StringSliceWTF* makeStringSliceWTF(StringSliceWTFOp op,
Expression* ref,
Expression* start,
Expression* end) {
auto* ret = wasm.allocator.alloc<StringSliceWTF>();
ret->op = op;
ret->ref = ref;
ret->start = start;
ret->end = end;
ret->finalize();
return ret;
}
StringSliceIter* makeStringSliceIter(Expression* ref, Expression* num) {
auto* ret = wasm.allocator.alloc<StringSliceIter>();
ret->ref = ref;
ret->num = num;
ret->finalize();
return ret;
}
// Additional helpers
Drop* makeDrop(Expression* value) {
auto* ret = wasm.allocator.alloc<Drop>();
ret->value = value;
ret->finalize();
return ret;
}
// Make a constant expression. This might be a wasm Const, or something
// else of constant value like ref.null.
Expression* makeConstantExpression(Literal value) {
auto type = value.type;
if (type.isNumber()) {
return makeConst(value);
}
if (value.isNull()) {
return makeRefNull(type);
}
if (type.isFunction()) {
return makeRefFunc(value.getFunc(), type.getHeapType());
}
if (type.isRef() && type.getHeapType() == HeapType::i31) {
return makeI31New(makeConst(value.geti31()));
}
if (type.isString()) {
// TODO: more than ascii support
std::string string;
for (auto c : value.getGCData()->values) {
string.push_back(c.getInteger());
}
return makeStringConst(string);
}
if (type.isRef() && type.getHeapType() == HeapType::ext) {
return makeRefAs(ExternExternalize,
makeConstantExpression(value.internalize()));
}
TODO_SINGLE_COMPOUND(type);
WASM_UNREACHABLE("unsupported constant expression");
}
Expression* makeConstantExpression(Literals values) {
assert(values.size() > 0);
if (values.size() == 1) {
return makeConstantExpression(values[0]);
} else {
std::vector<Expression*> consts;
for (auto value : values) {
consts.push_back(makeConstantExpression(value));
}
return makeTupleMake(consts);
}
}
// 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, Type type) {
// only ok to add a param if no vars, otherwise indices are invalidated
assert(func->localIndices.size() == func->getParams().size());
assert(name.is());
Signature sig = func->getSig();
std::vector<Type> params(sig.params.begin(), sig.params.end());
params.push_back(type);
func->type = Signature(Type(params), sig.results);
Index index = func->localNames.size();
func->localIndices[name] = index;
func->localNames[index] = name;
return index;
}
static Index addVar(Function* func, Name name, Type type) {
// always ok to add a var, it does not affect other indices
assert(type.isConcrete());
Index index = func->getNumLocals();
if (name.is()) {
func->localIndices[name] = index;
func->localNames[index] = name;
}
func->vars.emplace_back(type);
return index;
}
static Index addVar(Function* func, Type type) {
return addVar(func, Name(), type);
}
static void clearLocalNames(Function* func) {
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);
block->finalize();
}
return block;
}
template<typename... Ts>
Block* blockify(Expression* any, Expression* append, Ts... args) {
return blockify(blockify(any, append), args...);
}
// 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);
block->finalize();
}
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);
block->finalize();
return block;
}
Block* makeSequence(Expression* left, Expression* right, Type type) {
auto* block = makeBlock(left);
block->list.push_back(right);
block->finalize(type);
return block;
}
// Drop an expression if it has a concrete type
Expression* dropIfConcretelyTyped(Expression* curr) {
if (!curr->type.isConcrete()) {
return curr;
}
return makeDrop(curr);
}
void flip(If* iff) {
std::swap(iff->ifTrue, iff->ifFalse);
iff->condition = makeUnary(EqZInt32, iff->condition);
}
// Returns a replacement with the precise same type, and with minimal contents
// as best we can. As a replacement, this may reuse the input node.
template<typename T> Expression* replaceWithIdenticalType(T* curr) {
if (curr->type.isTuple() && curr->type.isDefaultable()) {
return makeConstantExpression(Literal::makeZeros(curr->type));
}
if (curr->type.isNullable() && curr->type.isNull()) {
return ExpressionManipulator::refNull(curr, curr->type);
}
if (curr->type.isRef() && curr->type.getHeapType() == HeapType::i31) {
return makeI31New(makeConst(0));
}
if (!curr->type.isBasic()) {
// We can't do any better, keep the original.
return curr;
}
Literal value;
// TODO: reuse node conditionally when possible for literals
switch (curr->type.getBasic()) {
case Type::i32:
value = Literal(int32_t(0));
break;
case Type::i64:
value = Literal(int64_t(0));
break;
case Type::f32:
value = Literal(float(0));
break;
case Type::f64:
value = Literal(double(0));
break;
case Type::v128: {
std::array<uint8_t, 16> bytes;
bytes.fill(0);
value = Literal(bytes.data());
break;
}
case Type::none:
return ExpressionManipulator::nop(curr);
case Type::unreachable:
return ExpressionManipulator::unreachable(curr);
}
return makeConst(value);
}
};
} // namespace wasm
#endif // wasm_wasm_builder_h