/* * Copyright 2018 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. */ #include "ir/branch-utils.h" #include "ir/find_all.h" #include "ir/utils.h" namespace wasm { static Type getValueType(Expression* value) { return value ? value->type : none; } namespace { // Handles a branch fixup for visitBlock: if the branch goes to the // target name, give it a value which is unreachable. template void handleBranchForVisitBlock(T* curr, Name name, Module* module) { if (BranchUtils::getUniqueTargets(curr).count(name)) { assert(!curr->value); Builder builder(*module); curr->value = builder.makeUnreachable(); } } } // anonymous namespace void ReFinalize::visitBlock(Block* curr) { if (curr->list.size() == 0) { curr->type = none; return; } // do this quickly, without any validation // last element determines type curr->type = curr->list.back()->type; // if concrete, it doesn't matter if we have an unreachable child, and we // don't need to look at breaks if (curr->type.isConcrete()) { return; } // otherwise, we have no final fallthrough element to determine the type, // could be determined by breaks if (curr->name.is()) { auto iter = breakValues.find(curr->name); if (iter != breakValues.end()) { // there is a break to here auto type = iter->second; assert(type != unreachable); // we would have removed such branches curr->type = type; return; } } if (curr->type == unreachable) { return; } // type is none, but we might be unreachable if (curr->type == none) { for (auto* child : curr->list) { if (child->type == unreachable) { curr->type = unreachable; break; } } } } void ReFinalize::visitIf(If* curr) { curr->finalize(); } void ReFinalize::visitLoop(Loop* curr) { curr->finalize(); } void ReFinalize::visitBreak(Break* curr) { curr->finalize(); auto valueType = getValueType(curr->value); if (valueType == unreachable) { replaceUntaken(curr->value, curr->condition); } else { updateBreakValueType(curr->name, valueType); } } void ReFinalize::visitSwitch(Switch* curr) { curr->finalize(); auto valueType = getValueType(curr->value); if (valueType == unreachable) { replaceUntaken(curr->value, curr->condition); } else { for (auto target : curr->targets) { updateBreakValueType(target, valueType); } updateBreakValueType(curr->default_, valueType); } } void ReFinalize::visitCall(Call* curr) { curr->finalize(); } void ReFinalize::visitCallIndirect(CallIndirect* curr) { curr->finalize(); } void ReFinalize::visitLocalGet(LocalGet* curr) { curr->finalize(); } void ReFinalize::visitLocalSet(LocalSet* curr) { curr->finalize(); } void ReFinalize::visitGlobalGet(GlobalGet* curr) { curr->finalize(); } void ReFinalize::visitGlobalSet(GlobalSet* curr) { curr->finalize(); } void ReFinalize::visitLoad(Load* curr) { curr->finalize(); } void ReFinalize::visitStore(Store* curr) { curr->finalize(); } void ReFinalize::visitAtomicRMW(AtomicRMW* curr) { curr->finalize(); } void ReFinalize::visitAtomicCmpxchg(AtomicCmpxchg* curr) { curr->finalize(); } void ReFinalize::visitAtomicWait(AtomicWait* curr) { curr->finalize(); } void ReFinalize::visitAtomicNotify(AtomicNotify* curr) { curr->finalize(); } void ReFinalize::visitAtomicFence(AtomicFence* curr) { curr->finalize(); } void ReFinalize::visitSIMDExtract(SIMDExtract* curr) { curr->finalize(); } void ReFinalize::visitSIMDReplace(SIMDReplace* curr) { curr->finalize(); } void ReFinalize::visitSIMDShuffle(SIMDShuffle* curr) { curr->finalize(); } void ReFinalize::visitSIMDTernary(SIMDTernary* curr) { curr->finalize(); } void ReFinalize::visitSIMDShift(SIMDShift* curr) { curr->finalize(); } void ReFinalize::visitSIMDLoad(SIMDLoad* curr) { curr->finalize(); } void ReFinalize::visitMemoryInit(MemoryInit* curr) { curr->finalize(); } void ReFinalize::visitDataDrop(DataDrop* curr) { curr->finalize(); } void ReFinalize::visitMemoryCopy(MemoryCopy* curr) { curr->finalize(); } void ReFinalize::visitMemoryFill(MemoryFill* curr) { curr->finalize(); } void ReFinalize::visitConst(Const* curr) { curr->finalize(); } void ReFinalize::visitUnary(Unary* curr) { curr->finalize(); } void ReFinalize::visitBinary(Binary* curr) { curr->finalize(); } void ReFinalize::visitSelect(Select* curr) { curr->finalize(); } void ReFinalize::visitDrop(Drop* curr) { curr->finalize(); } void ReFinalize::visitReturn(Return* curr) { curr->finalize(); } void ReFinalize::visitHost(Host* curr) { curr->finalize(); } void ReFinalize::visitTry(Try* curr) { curr->finalize(); } void ReFinalize::visitThrow(Throw* curr) { curr->finalize(); } void ReFinalize::visitRethrow(Rethrow* curr) { curr->finalize(); } void ReFinalize::visitBrOnExn(BrOnExn* curr) { curr->finalize(); updateBreakValueType(curr->name, curr->sent); } void ReFinalize::visitNop(Nop* curr) { curr->finalize(); } void ReFinalize::visitUnreachable(Unreachable* curr) { curr->finalize(); } void ReFinalize::visitPush(Push* curr) { curr->finalize(); } void ReFinalize::visitPop(Pop* curr) { curr->finalize(); } void ReFinalize::visitFunction(Function* curr) { // we may have changed the body from unreachable to none, which might be bad // if the function has a return value if (curr->result != none && curr->body->type == none) { Builder builder(*getModule()); curr->body = builder.blockify(curr->body, builder.makeUnreachable()); } } void ReFinalize::visitFunctionType(FunctionType* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitExport(Export* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitGlobal(Global* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitTable(Table* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitMemory(Memory* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitEvent(Event* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::visitModule(Module* curr) { WASM_UNREACHABLE("unimp"); } void ReFinalize::updateBreakValueType(Name name, Type type) { if (type != unreachable || breakValues.count(name) == 0) { breakValues[name] = type; } } // Replace an untaken branch/switch with an unreachable value. // A condition may also exist and may or may not be unreachable. void ReFinalize::replaceUntaken(Expression* value, Expression* condition) { assert(value->type == unreachable); auto* replacement = value; if (condition) { Builder builder(*getModule()); // Even if we have // (block // (unreachable) // (i32.const 1) // ) // we want the block type to be unreachable. That is valid as // the value is unreachable, and necessary since the type of // the condition did not have an impact before (the break/switch // type was unreachable), and might not fit in. if (condition->type.isConcrete()) { condition = builder.makeDrop(condition); } replacement = builder.makeSequence(value, condition); assert(replacement->type); } replaceCurrent(replacement); } } // namespace wasm