| /* | |
| * 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" | |
| #include <optional> | |
| namespace wasm { | |
| // Useful data structures | |
| struct NameType { | |
| Name name; | |
| Type type; | |
| NameType() : 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, | |
| Type type, | |
| std::vector<Type>&& vars, | |
| Expression* body = nullptr) { | |
| assert(type.isSignature() && type.isNonNullable()); | |
| 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, | |
| HeapType type, | |
| std::vector<Type>&& vars, | |
| Expression* body = nullptr) { | |
| return makeFunction( | |
| name, Type(type, NonNullable, Exact), std::move(vars), body); | |
| } | |
| static std::unique_ptr<Function> makeFunction(Name name, | |
| std::vector<NameType>&& params, | |
| Type 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<Function> makeFunction(Name name, | |
| std::vector<NameType>&& params, | |
| HeapType type, | |
| std::vector<NameType>&& vars, | |
| Expression* body = nullptr) { | |
| return makeFunction(name, | |
| std::move(params), | |
| Type(type, NonNullable, Exact), | |
| std::move(vars), | |
| body); | |
| } | |
| static std::unique_ptr<Table> makeTable(Name name, | |
| Type type = Type(HeapType::func, | |
| Nullable), | |
| Address initial = 0, | |
| Address max = Table::kMaxSize, | |
| Type addressType = Type::i32, | |
| Expression* init = nullptr) { | |
| auto table = std::make_unique<Table>(); | |
| table->name = name; | |
| table->type = type; | |
| table->addressType = addressType; | |
| table->initial = initial; | |
| table->max = max; | |
| table->init = init; | |
| 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::kDefaultMaxSize32, | |
| bool shared = false, | |
| uint8_t pageSizeLog2 = Memory::kDefaultPageSizeLog2, | |
| Type addressType = Type::i32) { | |
| auto memory = std::make_unique<Memory>(); | |
| memory->name = name; | |
| memory->initial = initial; | |
| memory->max = max; | |
| memory->pageSizeLog2 = pageSizeLog2; | |
| memory->shared = shared; | |
| memory->addressType = addressType; | |
| return memory; | |
| } | |
| static std::unique_ptr<DataSegment> | |
| makeDataSegment(Name name = "", | |
| Name memory = Name(), | |
| Expression* offset = nullptr, | |
| const char* init = "", | |
| Address size = 0) { | |
| auto seg = std::make_unique<DataSegment>(); | |
| seg->name = name; | |
| seg->memory = memory; | |
| 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, std::variant<Name, HeapType> value, ExternalKind kind) { | |
| return std::make_unique<Export>(name, kind, value); | |
| } | |
| 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, HeapType type) { | |
| auto tag = std::make_unique<Tag>(); | |
| tag->name = name; | |
| tag->type = type; | |
| 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, std::optional<Type> type = std::nullopt) { | |
| 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, | |
| std::optional<Type> type = std::nullopt) { | |
| 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, | |
| std::optional<Type> type = std::nullopt) { | |
| return makeBlock(items, type); | |
| } | |
| Block* makeBlock(Name name, | |
| std::initializer_list<Expression*>&& items, | |
| std::optional<Type> type = std::nullopt) { | |
| return makeBlock(name, items, type); | |
| } | |
| If* makeIf(Expression* condition, | |
| Expression* ifTrue, | |
| Expression* ifFalse = nullptr, | |
| std::optional<Type> type = std::nullopt) { | |
| 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, | |
| std::optional<Type> type = std::nullopt) { | |
| 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; | |
| call->finalize(); | |
| 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->bytes = bytes; | |
| ret->signed_ = signed_; | |
| ret->offset = offset; | |
| ret->align = align; | |
| ret->ptr = ptr; | |
| ret->type = type; | |
| ret->memory = memory; | |
| ret->order = MemoryOrder::Unordered; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| Load* makeAtomicLoad(unsigned bytes, | |
| Address offset, | |
| Expression* ptr, | |
| Type type, | |
| Name memory, | |
| MemoryOrder order) { | |
| assert(order != MemoryOrder::Unordered && | |
| "Atomic loads can't be unordered"); | |
| Load* load = makeLoad(bytes, false, offset, bytes, ptr, type, memory); | |
| load->order = order; | |
| 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(MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<AtomicFence>(); | |
| ret->order = order; | |
| return ret; | |
| } | |
| Pause* makePause() { return wasm.allocator.alloc<Pause>(); } | |
| Store* makeStore(unsigned bytes, | |
| Address offset, | |
| unsigned align, | |
| Expression* ptr, | |
| Expression* value, | |
| Type type, | |
| Name memory) { | |
| auto* ret = wasm.allocator.alloc<Store>(); | |
| ret->bytes = bytes; | |
| ret->offset = offset; | |
| ret->align = align; | |
| ret->ptr = ptr; | |
| ret->value = value; | |
| ret->valueType = type; | |
| ret->memory = memory; | |
| ret->order = MemoryOrder::Unordered; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| Store* makeAtomicStore(unsigned bytes, | |
| Address offset, | |
| Expression* ptr, | |
| Expression* value, | |
| Type type, | |
| Name memory, | |
| MemoryOrder order) { | |
| assert(order != MemoryOrder::Unordered && | |
| "Atomic stores can't be unordered"); | |
| Store* store = makeStore(bytes, offset, bytes, ptr, value, type, memory); | |
| store->order = order; | |
| return store; | |
| } | |
| AtomicRMW* makeAtomicRMW(AtomicRMWOp op, | |
| unsigned bytes, | |
| Address offset, | |
| Expression* ptr, | |
| Expression* value, | |
| Type type, | |
| Name memory, | |
| MemoryOrder order) { | |
| 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->memory = memory; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| AtomicCmpxchg* makeAtomicCmpxchg(unsigned bytes, | |
| Address offset, | |
| Expression* ptr, | |
| Expression* expected, | |
| Expression* replacement, | |
| Type type, | |
| Name memory, | |
| MemoryOrder order) { | |
| 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->memory = memory; | |
| ret->order = order; | |
| ret->finalize(); | |
| 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(Name 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(Name 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 addressType) { | |
| return makeConst(Literal::makeFromInt64(val, addressType)); | |
| } | |
| 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; | |
| } | |
| WideIntAddSub* makeWideIntAddSub(WideIntAddSubOp op, | |
| Expression* leftLow, | |
| Expression* leftHigh, | |
| Expression* rightLow, | |
| Expression* rightHigh) { | |
| auto* ret = wasm.allocator.alloc<WideIntAddSub>(); | |
| ret->op = op; | |
| ret->leftLow = leftLow; | |
| ret->leftHigh = leftHigh; | |
| ret->rightLow = rightLow; | |
| ret->rightHigh = rightHigh; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| WideIntMul* | |
| makeWideIntMul(WideIntMulOp op, Expression* left, Expression* right) { | |
| auto* ret = wasm.allocator.alloc<WideIntMul>(); | |
| 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; | |
| } | |
| 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()); | |
| } | |
| bool isTable64(Name tableName) { return wasm.getTable(tableName)->is64(); } | |
| MemorySize* makeMemorySize(Name memoryName, | |
| MemoryInfo info = MemoryInfo::Unspecified) { | |
| auto* ret = wasm.allocator.alloc<MemorySize>(); | |
| if (isMemory64(memoryName, info)) { | |
| ret->type = Type::i64; | |
| } | |
| 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->type = Type::i64; | |
| } | |
| 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; | |
| } | |
| RefIsNull* makeRefIsNull(Expression* value) { | |
| auto* ret = wasm.allocator.alloc<RefIsNull>(); | |
| ret->value = value; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| RefFunc* makeRefFunc(Name func, Type type) { | |
| auto* ret = wasm.allocator.alloc<RefFunc>(); | |
| ret->func = func; | |
| // Just apply the type, trusting it completely. This is safe to do even in | |
| // the middle of an operation (where the Module is in the process of being | |
| // altered, and should not be read from, which finalize normally does). | |
| ret->type = type; | |
| return ret; | |
| } | |
| RefFunc* makeRefFunc(Name func) { | |
| auto* ret = wasm.allocator.alloc<RefFunc>(); | |
| ret->func = func; | |
| ret->finalize(wasm); | |
| 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; | |
| if (isTable64(table)) { | |
| ret->type = Type::i64; | |
| } | |
| 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; | |
| if (isTable64(table)) { | |
| ret->type = Type::i64; | |
| } | |
| ret->finalize(); | |
| return ret; | |
| } | |
| TableFill* makeTableFill(Name table, | |
| Expression* dest, | |
| Expression* value, | |
| Expression* size) { | |
| auto* ret = wasm.allocator.alloc<TableFill>(); | |
| ret->table = table; | |
| ret->dest = dest; | |
| ret->value = value; | |
| ret->size = size; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| TableCopy* makeTableCopy(Expression* dest, | |
| Expression* source, | |
| Expression* size, | |
| Name destTable, | |
| Name sourceTable) { | |
| auto* ret = wasm.allocator.alloc<TableCopy>(); | |
| ret->dest = dest; | |
| ret->source = source; | |
| ret->size = size; | |
| ret->destTable = destTable; | |
| ret->sourceTable = sourceTable; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| TableInit* makeTableInit(Name segment, | |
| Expression* dest, | |
| Expression* offset, | |
| Expression* size, | |
| Name table) { | |
| auto* ret = wasm.allocator.alloc<TableInit>(); | |
| ret->segment = segment; | |
| ret->dest = dest; | |
| ret->offset = offset; | |
| ret->size = size; | |
| ret->table = table; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ElemDrop* makeElemDrop(Name segment) { | |
| auto* ret = wasm.allocator.alloc<ElemDrop>(); | |
| ret->segment = segment; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| private: | |
| Try* makeTry(Name name, | |
| Expression* body, | |
| const std::vector<Name>& catchTags, | |
| const std::vector<Expression*>& catchBodies, | |
| Name delegateTarget, | |
| std::optional<Type> type = std::nullopt) { | |
| auto* ret = wasm.allocator.alloc<Try>(); | |
| ret->name = name; | |
| ret->body = body; | |
| ret->catchTags.set(catchTags); | |
| ret->catchBodies.set(catchBodies); | |
| ret->delegateTarget = delegateTarget; | |
| ret->finalize(type); | |
| return ret; | |
| } | |
| public: | |
| // TODO delete? | |
| Try* makeTry(Expression* body, | |
| const std::vector<Name>& catchTags, | |
| const std::vector<Expression*>& catchBodies, | |
| std::optional<Type> type = std::nullopt) { | |
| return makeTry(Name(), body, catchTags, catchBodies, Name(), type); | |
| } | |
| Try* makeTry(Name name, | |
| Expression* body, | |
| const std::vector<Name>& catchTags, | |
| const std::vector<Expression*>& catchBodies, | |
| std::optional<Type> type = std::nullopt) { | |
| return makeTry(name, body, catchTags, catchBodies, Name(), type); | |
| } | |
| Try* makeTry(Expression* body, | |
| Name delegateTarget, | |
| std::optional<Type> type = std::nullopt) { | |
| return makeTry(Name(), body, {}, {}, delegateTarget, type); | |
| } | |
| Try* makeTry(Name name, | |
| Expression* body, | |
| Name delegateTarget, | |
| std::optional<Type> type = std::nullopt) { | |
| return makeTry(name, body, {}, {}, delegateTarget, type); | |
| } | |
| TryTable* makeTryTable(Expression* body, | |
| const std::vector<Name>& catchTags, | |
| const std::vector<Name>& catchDests, | |
| const std::vector<bool>& catchRefs, | |
| std::optional<Type> type = std::nullopt) { | |
| auto* ret = wasm.allocator.alloc<TryTable>(); | |
| ret->body = body; | |
| ret->catchTags.set(catchTags); | |
| ret->catchDests.set(catchDests); | |
| ret->catchRefs.set(catchRefs); | |
| ret->finalize(type, &wasm); | |
| return ret; | |
| } | |
| Throw* makeThrow(Tag* tag, const std::vector<Expression*>& args) { | |
| return makeThrow(tag->name, args); | |
| } | |
| template<typename T> Throw* makeThrow(Name tag, const T& 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; | |
| } | |
| ThrowRef* makeThrowRef(Expression* exnref) { | |
| auto* ret = wasm.allocator.alloc<ThrowRef>(); | |
| ret->exnref = exnref; | |
| 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; | |
| } | |
| RefI31* makeRefI31(Expression* value, Shareability share = Unshared) { | |
| auto* ret = wasm.allocator.alloc<RefI31>(); | |
| ret->value = value; | |
| ret->type = Type(HeapTypes::i31.getBasic(share), NonNullable); | |
| 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) { | |
| return makeRefCast(ref, nullptr, type); | |
| } | |
| RefCast* makeRefCast(Expression* ref, Expression* desc, Type type) { | |
| auto* ret = wasm.allocator.alloc<RefCast>(); | |
| ret->ref = ref; | |
| ret->desc = desc; | |
| ret->type = type; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| RefGetDesc* makeRefGetDesc(Expression* ref) { | |
| auto* ret = wasm.allocator.alloc<RefGetDesc>(); | |
| ret->ref = ref; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| BrOn* makeBrOn(BrOnOp op, | |
| Name name, | |
| Expression* ref, | |
| Type castType = Type::none, | |
| Expression* desc = nullptr) { | |
| assert((desc && (op == BrOnCastDescEq || op == BrOnCastDescEqFail)) || | |
| (!desc && op != BrOnCastDescEq && op != BrOnCastDescEqFail)); | |
| auto* ret = wasm.allocator.alloc<BrOn>(); | |
| ret->op = op; | |
| ret->name = name; | |
| ret->ref = ref; | |
| ret->desc = desc; | |
| ret->castType = castType; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructNew* makeStructNew(HeapType type, | |
| std::initializer_list<Expression*> args, | |
| Expression* descriptor = nullptr) { | |
| auto* ret = wasm.allocator.alloc<StructNew>(); | |
| ret->operands.set(args); | |
| ret->desc = descriptor; | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructNew* makeStructNew(HeapType type, | |
| ExpressionList&& args, | |
| Expression* descriptor = nullptr) { | |
| auto* ret = wasm.allocator.alloc<StructNew>(); | |
| ret->operands = std::move(args); | |
| ret->desc = descriptor; | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| template<typename T> | |
| StructNew* makeStructNew(HeapType type, | |
| const T& args, | |
| Expression* descriptor = nullptr) { | |
| auto* ret = wasm.allocator.alloc<StructNew>(); | |
| ret->operands.set(args); | |
| ret->desc = descriptor; | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructGet* makeStructGet(Index index, | |
| Expression* ref, | |
| MemoryOrder order, | |
| Type type, | |
| bool signed_ = false) { | |
| auto* ret = wasm.allocator.alloc<StructGet>(); | |
| ret->index = index; | |
| ret->ref = ref; | |
| ret->type = type; | |
| ret->signed_ = signed_; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructSet* makeStructSet(Index index, | |
| Expression* ref, | |
| Expression* value, | |
| MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<StructSet>(); | |
| ret->index = index; | |
| ret->ref = ref; | |
| ret->value = value; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructRMW* makeStructRMW(AtomicRMWOp op, | |
| Index index, | |
| Expression* ref, | |
| Expression* value, | |
| MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<StructRMW>(); | |
| ret->op = op; | |
| ret->index = index; | |
| ret->ref = ref; | |
| ret->value = value; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructCmpxchg* makeStructCmpxchg(Index index, | |
| Expression* ref, | |
| Expression* expected, | |
| Expression* replacement, | |
| MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<StructCmpxchg>(); | |
| ret->index = index; | |
| ret->ref = ref; | |
| ret->expected = expected; | |
| ret->replacement = replacement; | |
| ret->order = order; | |
| 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, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayNewData* makeArrayNewData(HeapType type, | |
| Name seg, | |
| Expression* offset, | |
| Expression* size) { | |
| auto* ret = wasm.allocator.alloc<ArrayNewData>(); | |
| ret->segment = seg; | |
| ret->offset = offset; | |
| ret->size = size; | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayNewElem* makeArrayNewElem(HeapType type, | |
| Name seg, | |
| Expression* offset, | |
| Expression* size) { | |
| auto* ret = wasm.allocator.alloc<ArrayNewElem>(); | |
| ret->segment = seg; | |
| ret->offset = offset; | |
| ret->size = size; | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| template<typename T> | |
| ArrayNewFixed* makeArrayNewFixed(HeapType type, const T& values) { | |
| auto* ret = wasm.allocator.alloc<ArrayNewFixed>(); | |
| ret->values.set(values); | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayNewFixed* | |
| makeArrayNewFixed(HeapType type, | |
| std::initializer_list<Expression*>&& values) { | |
| return makeArrayNewFixed(type, values); | |
| } | |
| ArrayGet* makeArrayGet(Expression* ref, | |
| Expression* index, | |
| MemoryOrder order, | |
| Type type, | |
| bool signed_ = false) { | |
| auto* ret = wasm.allocator.alloc<ArrayGet>(); | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->type = type; | |
| ret->signed_ = signed_; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArraySet* makeArraySet(Expression* ref, | |
| Expression* index, | |
| Expression* value, | |
| MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<ArraySet>(); | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->value = value; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayLoad* makeArrayLoad(unsigned bytes, | |
| bool signed_, | |
| Address offset, | |
| Address align, | |
| Expression* ref, | |
| Expression* index, | |
| Type type) { | |
| auto* ret = wasm.allocator.alloc<ArrayLoad>(); | |
| ret->bytes = bytes; | |
| ret->signed_ = signed_; | |
| ret->offset = offset; | |
| ret->align = align ? align : Address(bytes); | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->type = type; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayStore* makeArrayStore(unsigned bytes, | |
| Address offset, | |
| Address align, | |
| Expression* ref, | |
| Expression* index, | |
| Expression* value) { | |
| auto* ret = wasm.allocator.alloc<ArrayStore>(); | |
| ret->bytes = bytes; | |
| ret->offset = offset; | |
| ret->align = align ? align : Address(bytes); | |
| 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; | |
| } | |
| ArrayFill* makeArrayFill(Expression* ref, | |
| Expression* index, | |
| Expression* value, | |
| Expression* size) { | |
| auto* ret = wasm.allocator.alloc<ArrayFill>(); | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->value = value; | |
| ret->size = size; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayInitData* makeArrayInitData(Name seg, | |
| Expression* ref, | |
| Expression* index, | |
| Expression* offset, | |
| Expression* size) { | |
| auto* ret = wasm.allocator.alloc<ArrayInitData>(); | |
| ret->segment = seg; | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->offset = offset; | |
| ret->size = size; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayInitElem* makeArrayInitElem(Name seg, | |
| Expression* ref, | |
| Expression* index, | |
| Expression* offset, | |
| Expression* size) { | |
| auto* ret = wasm.allocator.alloc<ArrayInitElem>(); | |
| ret->segment = seg; | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->offset = offset; | |
| ret->size = size; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayRMW* makeArrayRMW(AtomicRMWOp op, | |
| Expression* ref, | |
| Expression* index, | |
| Expression* value, | |
| MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<ArrayRMW>(); | |
| ret->op = op; | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->value = value; | |
| ret->order = order; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ArrayCmpxchg* makeArrayCmpxchg(Expression* ref, | |
| Expression* index, | |
| Expression* expected, | |
| Expression* replacement, | |
| MemoryOrder order) { | |
| auto* ret = wasm.allocator.alloc<ArrayCmpxchg>(); | |
| ret->ref = ref; | |
| ret->index = index; | |
| ret->expected = expected; | |
| ret->replacement = replacement; | |
| ret->order = order; | |
| 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* ref, | |
| Expression* start = nullptr, | |
| Expression* end = nullptr) { | |
| assert((start && end) != (op == StringNewFromCodePoint)); | |
| auto* ret = wasm.allocator.alloc<StringNew>(); | |
| ret->op = op; | |
| ret->ref = ref; | |
| ret->start = start; | |
| ret->end = end; | |
| 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* str, | |
| Expression* array, | |
| Expression* start = nullptr) { | |
| auto* ret = wasm.allocator.alloc<StringEncode>(); | |
| ret->op = op; | |
| ret->str = str; | |
| ret->array = array; | |
| 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; | |
| } | |
| StringTest* makeStringTest(Expression* ref) { | |
| auto* ret = wasm.allocator.alloc<StringTest>(); | |
| ret->ref = ref; | |
| 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; | |
| } | |
| StringSliceWTF* | |
| makeStringSliceWTF(Expression* ref, Expression* start, Expression* end) { | |
| auto* ret = wasm.allocator.alloc<StringSliceWTF>(); | |
| ret->ref = ref; | |
| ret->start = start; | |
| ret->end = end; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ContNew* makeContNew(HeapType type, Expression* func) { | |
| auto* ret = wasm.allocator.alloc<ContNew>(); | |
| ret->type = Type(type, NonNullable, Exact); | |
| ret->func = func; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ContBind* makeContBind(HeapType targetType, | |
| ExpressionList&& operands, | |
| Expression* cont) { | |
| auto* ret = wasm.allocator.alloc<ContBind>(); | |
| ret->type = Type(targetType, NonNullable, Exact); | |
| ret->operands = std::move(operands); | |
| ret->cont = cont; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| template<typename T> | |
| ContBind* | |
| makeContBind(HeapType targetType, const T& operands, Expression* cont) { | |
| auto* ret = wasm.allocator.alloc<ContBind>(); | |
| ret->type = Type(targetType, NonNullable, Exact); | |
| ret->operands.set(operands); | |
| ret->cont = cont; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| Suspend* makeSuspend(Name tag, const std::vector<Expression*>& args) { | |
| auto* ret = wasm.allocator.alloc<Suspend>(); | |
| ret->tag = tag; | |
| ret->operands.set(args); | |
| ret->finalize(&wasm); | |
| return ret; | |
| } | |
| Resume* makeResume(const std::vector<Name>& handlerTags, | |
| const std::vector<Name>& handlerBlocks, | |
| const std::vector<Type>& sentTypes, | |
| ExpressionList&& operands, | |
| Expression* cont, | |
| HeapType contType) { | |
| auto* ret = wasm.allocator.alloc<Resume>(); | |
| ret->handlerTags.set(handlerTags); | |
| ret->handlerBlocks.set(handlerBlocks); | |
| ret->sentTypes.set(sentTypes); | |
| ret->operands = std::move(operands); | |
| ret->cont = cont; | |
| ret->type = contType.getContinuation().type.getSignature().results; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| ResumeThrow* makeResumeThrow(Name tag, | |
| const std::vector<Name>& handlerTags, | |
| const std::vector<Name>& handlerBlocks, | |
| const std::vector<Type>& sentTypes, | |
| ExpressionList&& operands, | |
| Expression* cont, | |
| HeapType contType) { | |
| auto* ret = wasm.allocator.alloc<ResumeThrow>(); | |
| ret->tag = tag; | |
| ret->handlerTags.set(handlerTags); | |
| ret->handlerBlocks.set(handlerBlocks); | |
| ret->sentTypes.set(sentTypes); | |
| ret->operands = std::move(operands); | |
| ret->cont = cont; | |
| ret->type = contType.getContinuation().type.getSignature().results; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StackSwitch* | |
| makeStackSwitch(Name tag, ExpressionList&& operands, Expression* cont) { | |
| auto* ret = wasm.allocator.alloc<StackSwitch>(); | |
| ret->tag = tag; | |
| ret->operands = std::move(operands); | |
| ret->cont = cont; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| StructWait* makeStructWait(Index index, | |
| Expression* ref, | |
| Expression* waitqueue, | |
| Expression* expected, | |
| Expression* timeout) { | |
| auto* ret = wasm.allocator.alloc<StructWait>(); | |
| ret->index = index; | |
| ret->ref = ref; | |
| ret->waitqueue = waitqueue; | |
| ret->expected = expected; | |
| ret->timeout = timeout; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| WaitqueueNew* makeWaitqueueNew() { | |
| auto* ret = wasm.allocator.alloc<WaitqueueNew>(); | |
| ret->finalize(); | |
| return ret; | |
| } | |
| WaitqueueNotify* makeWaitqueueNotify(Expression* waitqueue, | |
| Expression* count) { | |
| auto* ret = wasm.allocator.alloc<WaitqueueNotify>(); | |
| ret->waitqueue = waitqueue; | |
| ret->count = count; | |
| ret->finalize(); | |
| return ret; | |
| } | |
| Publish* makePublish(Expression* ref) { | |
| auto* ret = wasm.allocator.alloc<Publish>(); | |
| ret->ref = ref; | |
| 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.getHeapType()); | |
| } | |
| if (type.isFunction()) { | |
| return makeRefFunc(value.getFunc()); | |
| } | |
| if (type.isRef() && type.getHeapType().isMaybeShared(HeapType::i31)) { | |
| return makeRefI31(makeConst(value.geti31()), | |
| type.getHeapType().getShared()); | |
| } | |
| if (type.isString()) { | |
| // The string is already WTF-16, but we need to convert from `Literals` to | |
| // actual string. | |
| std::stringstream wtf16; | |
| for (auto c : value.getGCData()->getLiterals()) { | |
| auto u = c.getInteger(); | |
| assert(u < 0x10000); | |
| wtf16 << uint8_t(u & 0xFF); | |
| wtf16 << uint8_t(u >> 8); | |
| } | |
| // TODO: Use wtf16.view() once we have C++20. | |
| return makeStringConst(wtf16.str()); | |
| } | |
| if (type.isRef() && type.getHeapType().isMaybeShared(HeapType::ext)) { | |
| return makeRefAs(ExternConvertAny, | |
| 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 = func->type.with(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, | |
| std::optional<Type> type = std::nullopt) { | |
| Block* block = nullptr; | |
| if (any) { | |
| block = any->dynCast<Block>(); | |
| } | |
| if (!block || block->name.is()) { | |
| block = makeBlock(name, any); | |
| } else { | |
| block->name = name; | |
| } | |
| if (append) { | |
| block->list.push_back(append); | |
| } | |
| if (append || type) { | |
| block->finalize(type); | |
| } | |
| 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, | |
| std::optional<Type> type = std::nullopt) { | |
| 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); | |
| } | |
| // 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) { | |
| auto type = curr->type; | |
| // Anything that would otherwise have a more refined type than the original | |
| // expression needs to be wrapped in a block with the original type. | |
| auto maybeWrap = [&](Expression* expr) -> Expression* { | |
| return expr->type == type ? expr : makeBlock({expr}, type); | |
| }; | |
| if (curr->type.isTuple() && curr->type.isDefaultable()) { | |
| return maybeWrap(makeConstantExpression(Literal::makeZeros(curr->type))); | |
| } | |
| if (curr->type.isNullable()) { | |
| return maybeWrap( | |
| ExpressionManipulator::refNull(curr, curr->type.getHeapType())); | |
| } | |
| if (curr->type.isRef() && | |
| curr->type.getHeapType().isMaybeShared(HeapType::i31)) { | |
| return maybeWrap( | |
| makeRefI31(makeConst(0), curr->type.getHeapType().getShared())); | |
| } | |
| 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 |