From b42920ed10ceb00ca975d7b51afb680993adb42a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 1 Apr 2022 10:51:17 -0700 Subject: [PATCH 01/10] Revert "Remove old/non-working SpillPointers pass (#3261)" This reverts commit c61cb6f07a244067a7964d0d99c0fef0a068a3bf. --- src/abi/abi.h | 33 + src/abi/stack.h | 144 ++++ src/passes/CMakeLists.txt | 1 + src/passes/SpillPointers.cpp | 206 +++++ src/passes/pass.cpp | 3 + src/passes/passes.h | 1 + test/passes/spill-pointers.txt | 1291 +++++++++++++++++++++++++++++++ test/passes/spill-pointers.wast | 338 ++++++++ 8 files changed, 2017 insertions(+) create mode 100644 src/abi/abi.h create mode 100644 src/abi/stack.h create mode 100644 src/passes/SpillPointers.cpp create mode 100644 test/passes/spill-pointers.txt create mode 100644 test/passes/spill-pointers.wast diff --git a/src/abi/abi.h b/src/abi/abi.h new file mode 100644 index 00000000000..239efd3f3bd --- /dev/null +++ b/src/abi/abi.h @@ -0,0 +1,33 @@ +/* + * Copyright 2017 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_abi_abi_h +#define wasm_abi_abi_h + +#include "wasm.h" + +namespace wasm { + +namespace ABI { + +// The pointer type. Will need to update this for wasm64 +const static Type PointerType = Type::i32; + +} // namespace ABI + +} // namespace wasm + +#endif // wasm_abi_abi_h diff --git a/src/abi/stack.h b/src/abi/stack.h new file mode 100644 index 00000000000..e217d7caaba --- /dev/null +++ b/src/abi/stack.h @@ -0,0 +1,144 @@ +/* + * Copyright 2017 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_abi_stack_h +#define wasm_abi_stack_h + +#include "abi.h" +#include "asmjs/shared-constants.h" +#include "ir/find_all.h" +#include "ir/global-utils.h" +#include "shared-constants.h" +#include "wasm-builder.h" +#include "wasm.h" + +namespace wasm { + +namespace ABI { + +enum { StackAlign = 16 }; + +inline Index stackAlign(Index size) { + return (size + StackAlign - 1) & -StackAlign; +} + +// Allocate some space on the stack, and assign it to a local. +// The local will have the same constant value in all the function, so you can +// just local.get it anywhere there. +// +// FIXME: This function assumes that the stack grows upward, per the convention +// used by fastcomp. The stack grows downward when using the WASM backend. + +inline void +getStackSpace(Index local, Function* func, Index size, Module& wasm) { + // Attempt to locate the stack pointer by recognizing code idioms + // used by Emscripten. First, look for a global initialized to an + // imported variable named "STACKTOP" in environment "env". + auto* stackPointer = + GlobalUtils::getGlobalInitializedToImport(wasm, ENV, "STACKTOP"); + // Starting with Emscripten 1.38.24, the stack pointer variable is + // initialized with a literal constant, eliminating the import that + // we used to locate the stack pointer by name. We must match a more + // complicated idiom, expecting to see the module structured as follows: + // + //(module + // ... + // (export "stackSave" (func $stackSave)) + // ... + // (func $stackSave (; 410 ;) (; has Stack IR ;) (result i32) + // (global.get $STACKTOP) + // ) + // ... + //) + if (!stackPointer) { + auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave"); + if (stackSaveFunctionExport && + stackSaveFunctionExport->kind == ExternalKind::Function) { + auto* stackSaveFunction = + wasm.getFunction(stackSaveFunctionExport->value); + assert(!stackSaveFunction->imported()); + auto* globalGet = stackSaveFunction->body->dynCast(); + if (globalGet) { + stackPointer = wasm.getGlobal(globalGet->name); + } + } + } + if (!stackPointer) { + Fatal() << "getStackSpace: failed to find the stack pointer"; + } + // align the size + size = stackAlign(size); + // TODO: find existing stack usage, and add on top of that - carefully + Builder builder(wasm); + auto* block = builder.makeBlock(); + block->list.push_back(builder.makeLocalSet( + local, builder.makeGlobalGet(stackPointer->name, PointerType))); + // TODO: add stack max check + Expression* added; + if (PointerType == Type::i32) { + added = builder.makeBinary(AddInt32, + builder.makeLocalGet(local, PointerType), + builder.makeConst(int32_t(size))); + } else { + WASM_UNREACHABLE("unhandled PointerType"); + } + block->list.push_back(builder.makeGlobalSet(stackPointer->name, added)); + auto makeStackRestore = [&]() { + return builder.makeGlobalSet(stackPointer->name, + builder.makeLocalGet(local, PointerType)); + }; + // add stack restores to the returns + FindAllPointers finder(func->body); + for (auto** ptr : finder.list) { + auto* ret = (*ptr)->cast(); + if (ret->value && ret->value->type != Type::unreachable) { + // handle the returned value + auto* block = builder.makeBlock(); + auto temp = builder.addVar(func, ret->value->type); + block->list.push_back(builder.makeLocalSet(temp, ret->value)); + block->list.push_back(makeStackRestore()); + block->list.push_back( + builder.makeReturn(builder.makeLocalGet(temp, ret->value->type))); + block->finalize(); + *ptr = block; + } else { + // restore, then return + *ptr = builder.makeSequence(makeStackRestore(), ret); + } + } + // add stack restores to the body + if (func->body->type == Type::none) { + block->list.push_back(func->body); + block->list.push_back(makeStackRestore()); + } else if (func->body->type == Type::unreachable) { + block->list.push_back(func->body); + // no need to restore the old stack value, we're gone anyhow + } else { + // save the return value + auto temp = builder.addVar(func, func->sig.results); + block->list.push_back(builder.makeLocalSet(temp, func->body)); + block->list.push_back(makeStackRestore()); + block->list.push_back(builder.makeLocalGet(temp, func->sig.results)); + } + block->finalize(); + func->body = block; +} + +} // namespace ABI + +} // namespace wasm + +#endif // wasm_abi_stack_h diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt index c83e95d9e57..4a5357a6d5b 100644 --- a/src/passes/CMakeLists.txt +++ b/src/passes/CMakeLists.txt @@ -95,6 +95,7 @@ set(passes_SOURCES SimplifyGlobals.cpp SimplifyLocals.cpp Souperify.cpp + SpillPointers.cpp StackCheck.cpp SSAify.cpp Untee.cpp diff --git a/src/passes/SpillPointers.cpp b/src/passes/SpillPointers.cpp new file mode 100644 index 00000000000..4535105924c --- /dev/null +++ b/src/passes/SpillPointers.cpp @@ -0,0 +1,206 @@ +/* + * Copyright 2017 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. + */ + +// +// Spills values that might be pointers to the C stack. This allows +// Boehm-style GC to see them properly. +// +// To reduce the overhead of the extra operations added here, you +// should probably run optimizations after doing it. +// TODO: add a dead store elimination pass, which would help here +// +// * There is currently no check that there is enough stack space. +// + +#include "abi/abi.h" +#include "abi/stack.h" +#include "cfg/liveness-traversal.h" +#include "pass.h" +#include "wasm-builder.h" +#include "wasm.h" + +namespace wasm { + +struct SpillPointers + : public WalkerPass>> { + bool isFunctionParallel() override { return true; } + + Pass* create() override { return new SpillPointers; } + + // a mapping of the pointers to all the spillable things. We need to know + // how to replace them, and as we spill we may modify them. This map + // gives us, for an Expression** seen during the walk (and placed in the + // basic block, which is what we iterate on for efficiency) => the + // current actual pointer, which may have moded + std::unordered_map actualPointers; + + // note calls in basic blocks + template void visitSpillable(T* curr) { + // if in unreachable code, ignore + if (!currBasicBlock) { + return; + } + auto* pointer = getCurrentPointer(); + currBasicBlock->contents.actions.emplace_back(pointer); + // starts out as correct, may change later + actualPointers[pointer] = pointer; + } + + void visitCall(Call* curr) { visitSpillable(curr); } + void visitCallIndirect(CallIndirect* curr) { visitSpillable(curr); } + + // main entry point + + void doWalkFunction(Function* func) { + if (!canRun(func)) { + return; + } + super::doWalkFunction(func); + spillPointers(); + } + + // map pointers to their offset in the spill area + typedef std::unordered_map PointerMap; + + void spillPointers() { + // we only care about possible pointers + auto* func = getFunction(); + PointerMap pointerMap; + for (Index i = 0; i < func->getNumLocals(); i++) { + if (func->getLocalType(i) == ABI::PointerType) { + auto offset = pointerMap.size() * ABI::PointerType.getByteSize(); + pointerMap[i] = offset; + } + } + // find calls and spill around them + bool spilled = false; + Index spillLocal = -1; + for (auto& curr : basicBlocks) { + if (liveBlocks.count(curr.get()) == 0) { + continue; // ignore dead blocks + } + auto& liveness = curr->contents; + auto& actions = liveness.actions; + Index lastCall = -1; + for (Index i = 0; i < actions.size(); i++) { + auto& action = liveness.actions[i]; + if (action.isOther()) { + lastCall = i; + } + } + if (lastCall == Index(-1)) { + continue; // nothing to see here + } + // scan through the block, spilling around the calls + // TODO: we can filter on pointerMap everywhere + SetOfLocals live = liveness.end; + for (int i = int(actions.size()) - 1; i >= 0; i--) { + auto& action = actions[i]; + if (action.isGet()) { + live.insert(action.index); + } else if (action.isSet()) { + live.erase(action.index); + } else if (action.isOther()) { + std::vector toSpill; + for (auto index : live) { + if (pointerMap.count(index) > 0) { + toSpill.push_back(index); + } + } + if (!toSpill.empty()) { + // we now have a call + the information about which locals + // should be spilled + if (!spilled) { + // prepare stack support: get a pointer to stack space big enough + // for all our data + spillLocal = Builder::addVar(func, ABI::PointerType); + spilled = true; + } + // the origin was seen at walk, but the thing may have moved + auto* pointer = actualPointers[action.origin]; + spillPointersAroundCall( + pointer, toSpill, spillLocal, pointerMap, func, getModule()); + } + } else { + WASM_UNREACHABLE("unexpected action"); + } + } + } + if (spilled) { + // get the stack space, and set the local to it + ABI::getStackSpace(spillLocal, + func, + ABI::PointerType.getByteSize() * pointerMap.size(), + *getModule()); + } + } + + void spillPointersAroundCall(Expression** origin, + std::vector& toSpill, + Index spillLocal, + PointerMap& pointerMap, + Function* func, + Module* module) { + auto* call = *origin; + if (call->type == Type::unreachable) { + return; // the call is never reached anyhow, ignore + } + Builder builder(*module); + auto* block = builder.makeBlock(); + // move the operands into locals, as we must spill after they are executed + auto handleOperand = [&](Expression*& operand) { + auto temp = builder.addVar(func, operand->type); + auto* set = builder.makeLocalSet(temp, operand); + block->list.push_back(set); + block->finalize(); + if (actualPointers.count(&operand) > 0) { + // this is something we track, and it's moving - update + actualPointers[&operand] = &set->value; + } + operand = builder.makeLocalGet(temp, operand->type); + }; + if (call->is()) { + for (auto*& operand : call->cast()->operands) { + handleOperand(operand); + } + } else if (call->is()) { + for (auto*& operand : call->cast()->operands) { + handleOperand(operand); + } + handleOperand(call->cast()->target); + } else { + WASM_UNREACHABLE("unexpected expr"); + } + // add the spills + for (auto index : toSpill) { + block->list.push_back( + builder.makeStore(ABI::PointerType.getByteSize(), + pointerMap[index], + ABI::PointerType.getByteSize(), + builder.makeLocalGet(spillLocal, ABI::PointerType), + builder.makeLocalGet(index, ABI::PointerType), + ABI::PointerType)); + } + // add the (modified) call + block->list.push_back(call); + block->finalize(); + *origin = block; + } +}; + +Pass* createSpillPointersPass() { return new SpillPointers(); } + +} // namespace wasm diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp index 04947ae07e7..47a67050412 100644 --- a/src/passes/pass.cpp +++ b/src/passes/pass.cpp @@ -387,6 +387,9 @@ void PassRegistry::registerPasses() { registerPass("souperify-single-use", "emit Souper IR in text form (single-use nodes only)", createSouperifySingleUsePass); + registerPass("spill-pointers", + "spill pointers to the C stack (useful for Boehm-style GC)", + createSpillPointersPass); registerPass("stub-unsupported-js", "stub out unsupported JS operations", createStubUnsupportedJSOpsPass); diff --git a/src/passes/passes.h b/src/passes/passes.h index d7a6f9989bb..ae682232ac6 100644 --- a/src/passes/passes.h +++ b/src/passes/passes.h @@ -128,6 +128,7 @@ Pass* createStripProducersPass(); Pass* createStripTargetFeaturesPass(); Pass* createSouperifyPass(); Pass* createSouperifySingleUsePass(); +Pass* createSpillPointersPass(); Pass* createStubUnsupportedJSOpsPass(); Pass* createSSAifyPass(); Pass* createSSAifyNoMergePass(); diff --git a/test/passes/spill-pointers.txt b/test/passes/spill-pointers.txt new file mode 100644 index 00000000000..fd7683fda18 --- /dev/null +++ b/test/passes/spill-pointers.txt @@ -0,0 +1,1291 @@ +(module + (type $none_=>_none (func)) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $none_=>_i32 (func (result i32))) + (type $i32_i32_=>_none (func (param i32 i32))) + (type $i32_=>_none (func (param i32))) + (type $f64_=>_none (func (param f64))) + (import "env" "STACKTOP" (global $STACKTOP$asm2wasm$import i32)) + (import "env" "segfault" (func $segfault (param i32))) + (memory $0 10) + (table $0 1 1 funcref) + (global $stack_ptr (mut i32) (global.get $STACKTOP$asm2wasm$import)) + (func $nothing + (nop) + ) + (func $not-alive + (local $x i32) + (local.set $x + (i32.const 1) + ) + (call $nothing) + ) + (func $spill + (local $x i32) + (local $1 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (block + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + ) + (func $ignore-non-pointers + (local $x i32) + (local $y i64) + (local $z f32) + (local $w f64) + (local $4 i32) + (local.set $4 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $4) + (i32.const 16) + ) + ) + (block + (local.set $x + (i32.const 1) + ) + (local.set $y + (i64.const 1) + ) + (local.set $z + (f32.const 1) + ) + (local.set $w + (f64.const 1) + ) + (block + (i32.store + (local.get $4) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (local.get $y) + ) + (drop + (local.get $z) + ) + (drop + (local.get $w) + ) + ) + (global.set $stack_ptr + (local.get $4) + ) + ) + (func $spill4 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local $4 i32) + (local.set $4 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $4) + (i32.const 16) + ) + ) + (block + (local.set $x + (i32.const 1) + ) + (local.set $y + (i32.const 1) + ) + (local.set $z + (i32.const 1) + ) + (local.set $w + (i32.const 1) + ) + (block + (i32.store + (local.get $4) + (local.get $x) + ) + (i32.store offset=4 + (local.get $4) + (local.get $y) + ) + (i32.store offset=8 + (local.get $4) + (local.get $z) + ) + (i32.store offset=12 + (local.get $4) + (local.get $w) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (local.get $y) + ) + (drop + (local.get $z) + ) + (drop + (local.get $w) + ) + ) + (global.set $stack_ptr + (local.get $4) + ) + ) + (func $spill5 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local $a i32) + (local $5 i32) + (local.set $5 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $5) + (i32.const 32) + ) + ) + (block + (local.set $x + (i32.const 1) + ) + (local.set $y + (i32.const 1) + ) + (local.set $z + (i32.const 1) + ) + (local.set $w + (i32.const 1) + ) + (local.set $a + (i32.const 1) + ) + (block + (i32.store + (local.get $5) + (local.get $x) + ) + (i32.store offset=4 + (local.get $5) + (local.get $y) + ) + (i32.store offset=8 + (local.get $5) + (local.get $z) + ) + (i32.store offset=12 + (local.get $5) + (local.get $w) + ) + (i32.store offset=16 + (local.get $5) + (local.get $a) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (local.get $y) + ) + (drop + (local.get $z) + ) + (drop + (local.get $w) + ) + (drop + (local.get $a) + ) + ) + (global.set $stack_ptr + (local.get $5) + ) + ) + (func $some-alive + (local $x i32) + (local $y i32) + (local $2 i32) + (local.set $2 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $2) + (i32.const 16) + ) + ) + (block + (block + (i32.store + (local.get $2) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $2) + ) + ) + (func $spill-args (param $p i32) (param $q i32) + (local $x i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local.set $3 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $3) + (i32.const 16) + ) + ) + (block + (block + (local.set $4 + (i32.const 1) + ) + (local.set $5 + (i32.const 2) + ) + (i32.store offset=8 + (local.get $3) + (local.get $x) + ) + (call $spill-args + (local.get $4) + (local.get $5) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $3) + ) + ) + (func $spill-ret (result i32) + (local $x i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (local.set $4 + (block (result i32) + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (if + (i32.const 1) + (block + (local.set $2 + (i32.const 2) + ) + (global.set $stack_ptr + (local.get $1) + ) + (return + (local.get $2) + ) + ) + (block + (local.set $3 + (i32.const 3) + ) + (global.set $stack_ptr + (local.get $1) + ) + (return + (local.get $3) + ) + ) + ) + (i32.const 4) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + (local.get $4) + ) + (func $spill-unreachable (result i32) + (local $x i32) + (local $1 i32) + (local $2 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (local.set $2 + (block (result i32) + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (unreachable) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + (local.get $2) + ) + (func $spill-call-call0 (param $p i32) (result i32) + (unreachable) + ) + (func $spill-call-call1 (param $p i32) (result i32) + (local $x i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local.set $2 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $2) + (i32.const 16) + ) + ) + (local.set $5 + (block (result i32) + (drop + (block (result i32) + (local.set $3 + (block (result i32) + (local.set $4 + (i32.const 1) + ) + (i32.store offset=4 + (local.get $2) + (local.get $x) + ) + (call $spill-call-call1 + (local.get $4) + ) + ) + ) + (i32.store offset=4 + (local.get $2) + (local.get $x) + ) + (call $spill-call-call0 + (local.get $3) + ) + ) + ) + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $2) + ) + (local.get $5) + ) + (func $spill-call-ret (param $p i32) (result i32) + (local $x i32) + (drop + (call $spill-call-call0 + (return + (i32.const 1) + ) + ) + ) + (i32.const 0) + ) + (func $spill-ret-call (param $p i32) (result i32) + (local $x i32) + (drop + (return + (call $spill-call-call0 + (i32.const 1) + ) + ) + ) + (i32.const 0) + ) + (func $spill-ret-ret (result i32) + (local $x i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (local.set $3 + (block (result i32) + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (block + (global.set $stack_ptr + (local.get $1) + ) + (return + (block + (local.set $2 + (i32.const 1) + ) + (global.set $stack_ptr + (local.get $1) + ) + (return + (local.get $2) + ) + ) + ) + ) + ) + (i32.const 0) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + (local.get $3) + ) + (func $spill-call-othertype (param $y f64) + (local $x i32) + (local $2 i32) + (local $3 f64) + (local.set $2 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $2) + (i32.const 16) + ) + ) + (block + (block + (local.set $3 + (f64.const 1) + ) + (i32.store + (local.get $2) + (local.get $x) + ) + (call $spill-call-othertype + (local.get $3) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $2) + ) + ) + (func $spill-call_indirect + (local $x i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (block + (block + (local.set $2 + (i32.const 123) + ) + (local.set $3 + (i32.const 456) + ) + (local.set $4 + (i32.const 789) + ) + (i32.store + (local.get $1) + (local.get $x) + ) + (call_indirect (type $i32_i32_=>_none) + (local.get $2) + (local.get $3) + (local.get $4) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + ) + (func $spill-call_import + (local $x i32) + (local $1 i32) + (local $2 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (block + (block + (local.set $2 + (i32.const 200) + ) + (i32.store + (local.get $1) + (local.get $x) + ) + (call $segfault + (local.get $2) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + ) +) +(module + (type $none_=>_none (func)) + (type $none_=>_i32 (func (result i32))) + (type $i32_=>_i32 (func (param i32) (result i32))) + (type $i32_i32_=>_none (func (param i32 i32))) + (type $i32_=>_none (func (param i32))) + (type $f64_=>_none (func (param f64))) + (import "env" "segfault" (func $segfault (param i32))) + (memory $0 10) + (table $0 1 1 funcref) + (global $stack_ptr (mut i32) (i32.const 1716592)) + (export "stackSave" (func $stack_save)) + (func $stack_save (result i32) + (global.get $stack_ptr) + ) + (func $nothing + (nop) + ) + (func $not-alive + (local $x i32) + (local.set $x + (i32.const 1) + ) + (call $nothing) + ) + (func $spill + (local $x i32) + (local $1 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (block + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + ) + (func $ignore-non-pointers + (local $x i32) + (local $y i64) + (local $z f32) + (local $w f64) + (local $4 i32) + (local.set $4 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $4) + (i32.const 16) + ) + ) + (block + (local.set $x + (i32.const 1) + ) + (local.set $y + (i64.const 1) + ) + (local.set $z + (f32.const 1) + ) + (local.set $w + (f64.const 1) + ) + (block + (i32.store + (local.get $4) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (local.get $y) + ) + (drop + (local.get $z) + ) + (drop + (local.get $w) + ) + ) + (global.set $stack_ptr + (local.get $4) + ) + ) + (func $spill4 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local $4 i32) + (local.set $4 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $4) + (i32.const 16) + ) + ) + (block + (local.set $x + (i32.const 1) + ) + (local.set $y + (i32.const 1) + ) + (local.set $z + (i32.const 1) + ) + (local.set $w + (i32.const 1) + ) + (block + (i32.store + (local.get $4) + (local.get $x) + ) + (i32.store offset=4 + (local.get $4) + (local.get $y) + ) + (i32.store offset=8 + (local.get $4) + (local.get $z) + ) + (i32.store offset=12 + (local.get $4) + (local.get $w) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (local.get $y) + ) + (drop + (local.get $z) + ) + (drop + (local.get $w) + ) + ) + (global.set $stack_ptr + (local.get $4) + ) + ) + (func $spill5 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local $a i32) + (local $5 i32) + (local.set $5 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $5) + (i32.const 32) + ) + ) + (block + (local.set $x + (i32.const 1) + ) + (local.set $y + (i32.const 1) + ) + (local.set $z + (i32.const 1) + ) + (local.set $w + (i32.const 1) + ) + (local.set $a + (i32.const 1) + ) + (block + (i32.store + (local.get $5) + (local.get $x) + ) + (i32.store offset=4 + (local.get $5) + (local.get $y) + ) + (i32.store offset=8 + (local.get $5) + (local.get $z) + ) + (i32.store offset=12 + (local.get $5) + (local.get $w) + ) + (i32.store offset=16 + (local.get $5) + (local.get $a) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (local.get $y) + ) + (drop + (local.get $z) + ) + (drop + (local.get $w) + ) + (drop + (local.get $a) + ) + ) + (global.set $stack_ptr + (local.get $5) + ) + ) + (func $some-alive + (local $x i32) + (local $y i32) + (local $2 i32) + (local.set $2 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $2) + (i32.const 16) + ) + ) + (block + (block + (i32.store + (local.get $2) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $2) + ) + ) + (func $spill-args (param $p i32) (param $q i32) + (local $x i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local.set $3 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $3) + (i32.const 16) + ) + ) + (block + (block + (local.set $4 + (i32.const 1) + ) + (local.set $5 + (i32.const 2) + ) + (i32.store offset=8 + (local.get $3) + (local.get $x) + ) + (call $spill-args + (local.get $4) + (local.get $5) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $3) + ) + ) + (func $spill-ret (result i32) + (local $x i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (local.set $4 + (block (result i32) + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (if + (i32.const 1) + (block + (local.set $2 + (i32.const 2) + ) + (global.set $stack_ptr + (local.get $1) + ) + (return + (local.get $2) + ) + ) + (block + (local.set $3 + (i32.const 3) + ) + (global.set $stack_ptr + (local.get $1) + ) + (return + (local.get $3) + ) + ) + ) + (i32.const 4) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + (local.get $4) + ) + (func $spill-unreachable (result i32) + (local $x i32) + (local $1 i32) + (local $2 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (local.set $2 + (block (result i32) + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (unreachable) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + (local.get $2) + ) + (func $spill-call-call0 (param $p i32) (result i32) + (unreachable) + ) + (func $spill-call-call1 (param $p i32) (result i32) + (local $x i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local $5 i32) + (local.set $2 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $2) + (i32.const 16) + ) + ) + (local.set $5 + (block (result i32) + (drop + (block (result i32) + (local.set $3 + (block (result i32) + (local.set $4 + (i32.const 1) + ) + (i32.store offset=4 + (local.get $2) + (local.get $x) + ) + (call $spill-call-call1 + (local.get $4) + ) + ) + ) + (i32.store offset=4 + (local.get $2) + (local.get $x) + ) + (call $spill-call-call0 + (local.get $3) + ) + ) + ) + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $2) + ) + (local.get $5) + ) + (func $spill-call-ret (param $p i32) (result i32) + (local $x i32) + (drop + (call $spill-call-call0 + (return + (i32.const 1) + ) + ) + ) + (i32.const 0) + ) + (func $spill-ret-call (param $p i32) (result i32) + (local $x i32) + (drop + (return + (call $spill-call-call0 + (i32.const 1) + ) + ) + ) + (i32.const 0) + ) + (func $spill-ret-ret (result i32) + (local $x i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (local.set $3 + (block (result i32) + (block + (i32.store + (local.get $1) + (local.get $x) + ) + (call $nothing) + ) + (drop + (local.get $x) + ) + (drop + (block + (global.set $stack_ptr + (local.get $1) + ) + (return + (block + (local.set $2 + (i32.const 1) + ) + (global.set $stack_ptr + (local.get $1) + ) + (return + (local.get $2) + ) + ) + ) + ) + ) + (i32.const 0) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + (local.get $3) + ) + (func $spill-call-othertype (param $y f64) + (local $x i32) + (local $2 i32) + (local $3 f64) + (local.set $2 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $2) + (i32.const 16) + ) + ) + (block + (block + (local.set $3 + (f64.const 1) + ) + (i32.store + (local.get $2) + (local.get $x) + ) + (call $spill-call-othertype + (local.get $3) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $2) + ) + ) + (func $spill-call_indirect + (local $x i32) + (local $1 i32) + (local $2 i32) + (local $3 i32) + (local $4 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (block + (block + (local.set $2 + (i32.const 123) + ) + (local.set $3 + (i32.const 456) + ) + (local.set $4 + (i32.const 789) + ) + (i32.store + (local.get $1) + (local.get $x) + ) + (call_indirect (type $i32_i32_=>_none) + (local.get $2) + (local.get $3) + (local.get $4) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + ) + (func $spill-call_import + (local $x i32) + (local $1 i32) + (local $2 i32) + (local.set $1 + (global.get $stack_ptr) + ) + (global.set $stack_ptr + (i32.add + (local.get $1) + (i32.const 16) + ) + ) + (block + (block + (local.set $2 + (i32.const 200) + ) + (i32.store + (local.get $1) + (local.get $x) + ) + (call $segfault + (local.get $2) + ) + ) + (drop + (local.get $x) + ) + ) + (global.set $stack_ptr + (local.get $1) + ) + ) +) diff --git a/test/passes/spill-pointers.wast b/test/passes/spill-pointers.wast new file mode 100644 index 00000000000..4eb05a72116 --- /dev/null +++ b/test/passes/spill-pointers.wast @@ -0,0 +1,338 @@ +(module + (memory 10) + (type $ii (func (param i32 i32))) + (table 1 1 funcref) + (elem (i32.const 0)) + (import "env" "STACKTOP" (global $STACKTOP$asm2wasm$import i32)) + (import "env" "segfault" (func $segfault (param i32))) + (global $stack_ptr (mut i32) (global.get $STACKTOP$asm2wasm$import)) + + (func $nothing + ) + (func $not-alive + (local $x i32) + (local.set $x (i32.const 1)) + (call $nothing) + ) + (func $spill + (local $x i32) + (call $nothing) + (drop (local.get $x)) + ) + (func $ignore-non-pointers + (local $x i32) + (local $y i64) + (local $z f32) + (local $w f64) + (local.set $x (i32.const 1)) + (local.set $y (i64.const 1)) + (local.set $z (f32.const 1)) + (local.set $w (f64.const 1)) + (call $nothing) + (drop (local.get $x)) + (drop (local.get $y)) + (drop (local.get $z)) + (drop (local.get $w)) + ) + (func $spill4 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local.set $x (i32.const 1)) + (local.set $y (i32.const 1)) + (local.set $z (i32.const 1)) + (local.set $w (i32.const 1)) + (call $nothing) + (drop (local.get $x)) + (drop (local.get $y)) + (drop (local.get $z)) + (drop (local.get $w)) + ) + (func $spill5 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local $a i32) + (local.set $x (i32.const 1)) + (local.set $y (i32.const 1)) + (local.set $z (i32.const 1)) + (local.set $w (i32.const 1)) + (local.set $a (i32.const 1)) + (call $nothing) + (drop (local.get $x)) + (drop (local.get $y)) + (drop (local.get $z)) + (drop (local.get $w)) + (drop (local.get $a)) + ) + (func $some-alive + (local $x i32) + (local $y i32) + (call $nothing) + (drop (local.get $x)) + ) + (func $spill-args (param $p i32) (param $q i32) + (local $x i32) + (call $spill-args (i32.const 1) (i32.const 2)) + (drop (local.get $x)) + ) + (func $spill-ret (result i32) + (local $x i32) + (call $nothing) + (drop (local.get $x)) + (if (i32.const 1) + (return (i32.const 2)) + (return (i32.const 3)) + ) + (i32.const 4) + ) + (func $spill-unreachable (result i32) + (local $x i32) + (call $nothing) + (drop (local.get $x)) + (unreachable) + ) + (func $spill-call-call0 (param $p i32) (result i32) + (unreachable) + ) + (func $spill-call-call1 (param $p i32) (result i32) + (local $x i32) + (drop + (call $spill-call-call0 + (call $spill-call-call1 + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-call-ret (param $p i32) (result i32) + (local $x i32) + (drop + (call $spill-call-call0 + (return + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-ret-call (param $p i32) (result i32) + (local $x i32) + (drop + (return + (call $spill-call-call0 + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-ret-ret (result i32) + (local $x i32) + (call $nothing) + (drop (local.get $x)) + (drop + (return + (return + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-call-othertype (param $y f64) + (local $x i32) + (call $spill-call-othertype (f64.const 1)) + (drop (local.get $x)) + ) + (func $spill-call_indirect + (local $x i32) + (call_indirect (type $ii) + (i32.const 123) + (i32.const 456) + (i32.const 789) + ) + (drop (local.get $x)) + ) + (func $spill-call_import + (local $x i32) + (call $segfault + (i32.const 200) + ) + (drop (local.get $x)) + ) +) + +(module + (memory 10) + (type $ii (func (param i32 i32))) + (table 1 1 funcref) + (elem (i32.const 0)) + (global $stack_ptr (mut i32) (i32.const 1716592)) + (export "stackSave" (func $stack_save)) + (import "env" "segfault" (func $segfault (param i32))) + (func $stack_save (result i32) + (global.get $stack_ptr) + ) + + (func $nothing + ) + (func $not-alive + (local $x i32) + (local.set $x (i32.const 1)) + (call $nothing) + ) + (func $spill + (local $x i32) + (call $nothing) + (drop (local.get $x)) + ) + (func $ignore-non-pointers + (local $x i32) + (local $y i64) + (local $z f32) + (local $w f64) + (local.set $x (i32.const 1)) + (local.set $y (i64.const 1)) + (local.set $z (f32.const 1)) + (local.set $w (f64.const 1)) + (call $nothing) + (drop (local.get $x)) + (drop (local.get $y)) + (drop (local.get $z)) + (drop (local.get $w)) + ) + (func $spill4 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local.set $x (i32.const 1)) + (local.set $y (i32.const 1)) + (local.set $z (i32.const 1)) + (local.set $w (i32.const 1)) + (call $nothing) + (drop (local.get $x)) + (drop (local.get $y)) + (drop (local.get $z)) + (drop (local.get $w)) + ) + (func $spill5 + (local $x i32) + (local $y i32) + (local $z i32) + (local $w i32) + (local $a i32) + (local.set $x (i32.const 1)) + (local.set $y (i32.const 1)) + (local.set $z (i32.const 1)) + (local.set $w (i32.const 1)) + (local.set $a (i32.const 1)) + (call $nothing) + (drop (local.get $x)) + (drop (local.get $y)) + (drop (local.get $z)) + (drop (local.get $w)) + (drop (local.get $a)) + ) + (func $some-alive + (local $x i32) + (local $y i32) + (call $nothing) + (drop (local.get $x)) + ) + (func $spill-args (param $p i32) (param $q i32) + (local $x i32) + (call $spill-args (i32.const 1) (i32.const 2)) + (drop (local.get $x)) + ) + (func $spill-ret (result i32) + (local $x i32) + (call $nothing) + (drop (local.get $x)) + (if (i32.const 1) + (return (i32.const 2)) + (return (i32.const 3)) + ) + (i32.const 4) + ) + (func $spill-unreachable (result i32) + (local $x i32) + (call $nothing) + (drop (local.get $x)) + (unreachable) + ) + (func $spill-call-call0 (param $p i32) (result i32) + (unreachable) + ) + (func $spill-call-call1 (param $p i32) (result i32) + (local $x i32) + (drop + (call $spill-call-call0 + (call $spill-call-call1 + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-call-ret (param $p i32) (result i32) + (local $x i32) + (drop + (call $spill-call-call0 + (return + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-ret-call (param $p i32) (result i32) + (local $x i32) + (drop + (return + (call $spill-call-call0 + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-ret-ret (result i32) + (local $x i32) + (call $nothing) + (drop (local.get $x)) + (drop + (return + (return + (i32.const 1) + ) + ) + ) + (local.get $x) + ) + (func $spill-call-othertype (param $y f64) + (local $x i32) + (call $spill-call-othertype (f64.const 1)) + (drop (local.get $x)) + ) + (func $spill-call_indirect + (local $x i32) + (call_indirect (type $ii) + (i32.const 123) + (i32.const 456) + (i32.const 789) + ) + (drop (local.get $x)) + ) + (func $spill-call_import + (local $x i32) + (call $segfault + (i32.const 200) + ) + (drop (local.get $x)) + ) +) From 12c0cb31893d76510923b56e27312afc35f22a97 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 1 Apr 2022 10:56:09 -0700 Subject: [PATCH 02/10] fix --- src/abi/stack.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/abi/stack.h b/src/abi/stack.h index e217d7caaba..aa4382fd129 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -89,7 +89,8 @@ getStackSpace(Index local, Function* func, Index size, Module& wasm) { // TODO: add stack max check Expression* added; if (PointerType == Type::i32) { - added = builder.makeBinary(AddInt32, + // The stack goes downward in the LLVM wasm backend. + added = builder.makeBinary(SubInt32, builder.makeLocalGet(local, PointerType), builder.makeConst(int32_t(size))); } else { @@ -128,10 +129,10 @@ getStackSpace(Index local, Function* func, Index size, Module& wasm) { // no need to restore the old stack value, we're gone anyhow } else { // save the return value - auto temp = builder.addVar(func, func->sig.results); + auto temp = builder.addVar(func, func->getResults()); block->list.push_back(builder.makeLocalSet(temp, func->body)); block->list.push_back(makeStackRestore()); - block->list.push_back(builder.makeLocalGet(temp, func->sig.results)); + block->list.push_back(builder.makeLocalGet(temp, func->getResults())); } block->finalize(); func->body = block; From af976715958ccff1c76942dbed9041703ddb7425 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 1 Apr 2022 10:56:38 -0700 Subject: [PATCH 03/10] help text --- test/lit/help/wasm-opt.test | 3 +++ test/lit/help/wasm2js.test | 3 +++ 2 files changed, 6 insertions(+) diff --git a/test/lit/help/wasm-opt.test b/test/lit/help/wasm-opt.test index d786bfdfc6b..992396d5228 100644 --- a/test/lit/help/wasm-opt.test +++ b/test/lit/help/wasm-opt.test @@ -382,6 +382,9 @@ ;; CHECK-NEXT: --souperify-single-use emit Souper IR in text form ;; CHECK-NEXT: (single-use nodes only) ;; CHECK-NEXT: +;; CHECK-NEXT: --spill-pointers spill pointers to the C stack +;; CHECK-NEXT: (useful for Boehm-style GC) +;; CHECK-NEXT: ;; CHECK-NEXT: --ssa ssa-ify variables so that they ;; CHECK-NEXT: have a single assignment ;; CHECK-NEXT: diff --git a/test/lit/help/wasm2js.test b/test/lit/help/wasm2js.test index 504e087261c..6e5a23fb408 100644 --- a/test/lit/help/wasm2js.test +++ b/test/lit/help/wasm2js.test @@ -344,6 +344,9 @@ ;; CHECK-NEXT: --souperify-single-use emit Souper IR in text form ;; CHECK-NEXT: (single-use nodes only) ;; CHECK-NEXT: +;; CHECK-NEXT: --spill-pointers spill pointers to the C stack +;; CHECK-NEXT: (useful for Boehm-style GC) +;; CHECK-NEXT: ;; CHECK-NEXT: --ssa ssa-ify variables so that they ;; CHECK-NEXT: have a single assignment ;; CHECK-NEXT: From 6b9e4bf627bf4fe41f3578ac97782c8a5eb6c0b2 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 1 Apr 2022 10:57:03 -0700 Subject: [PATCH 04/10] update test --- test/passes/spill-pointers.txt | 66 +++++++++++++++++----------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/test/passes/spill-pointers.txt b/test/passes/spill-pointers.txt index fd7683fda18..dcd8b2cbfdc 100644 --- a/test/passes/spill-pointers.txt +++ b/test/passes/spill-pointers.txt @@ -2,14 +2,15 @@ (type $none_=>_none (func)) (type $i32_=>_i32 (func (param i32) (result i32))) (type $none_=>_i32 (func (result i32))) - (type $i32_i32_=>_none (func (param i32 i32))) + (type $ii (func (param i32 i32))) (type $i32_=>_none (func (param i32))) (type $f64_=>_none (func (param f64))) (import "env" "STACKTOP" (global $STACKTOP$asm2wasm$import i32)) (import "env" "segfault" (func $segfault (param i32))) + (global $stack_ptr (mut i32) (global.get $STACKTOP$asm2wasm$import)) (memory $0 10) (table $0 1 1 funcref) - (global $stack_ptr (mut i32) (global.get $STACKTOP$asm2wasm$import)) + (elem (i32.const 0)) (func $nothing (nop) ) @@ -27,7 +28,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -58,7 +59,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $4) (i32.const 16) ) @@ -110,7 +111,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $4) (i32.const 16) ) @@ -175,7 +176,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $5) (i32.const 32) ) @@ -247,7 +248,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $2) (i32.const 16) ) @@ -277,7 +278,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $3) (i32.const 16) ) @@ -317,7 +318,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -375,7 +376,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -413,7 +414,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $2) (i32.const 16) ) @@ -484,7 +485,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -537,7 +538,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $2) (i32.const 16) ) @@ -573,7 +574,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -593,7 +594,7 @@ (local.get $1) (local.get $x) ) - (call_indirect (type $i32_i32_=>_none) + (call_indirect (type $ii) (local.get $2) (local.get $3) (local.get $4) @@ -615,7 +616,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -646,13 +647,14 @@ (type $none_=>_none (func)) (type $none_=>_i32 (func (result i32))) (type $i32_=>_i32 (func (param i32) (result i32))) - (type $i32_i32_=>_none (func (param i32 i32))) + (type $ii (func (param i32 i32))) (type $i32_=>_none (func (param i32))) (type $f64_=>_none (func (param f64))) (import "env" "segfault" (func $segfault (param i32))) + (global $stack_ptr (mut i32) (i32.const 1716592)) (memory $0 10) (table $0 1 1 funcref) - (global $stack_ptr (mut i32) (i32.const 1716592)) + (elem (i32.const 0)) (export "stackSave" (func $stack_save)) (func $stack_save (result i32) (global.get $stack_ptr) @@ -674,7 +676,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -705,7 +707,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $4) (i32.const 16) ) @@ -757,7 +759,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $4) (i32.const 16) ) @@ -822,7 +824,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $5) (i32.const 32) ) @@ -894,7 +896,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $2) (i32.const 16) ) @@ -924,7 +926,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $3) (i32.const 16) ) @@ -964,7 +966,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -1022,7 +1024,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -1060,7 +1062,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $2) (i32.const 16) ) @@ -1131,7 +1133,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -1184,7 +1186,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $2) (i32.const 16) ) @@ -1220,7 +1222,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) @@ -1240,7 +1242,7 @@ (local.get $1) (local.get $x) ) - (call_indirect (type $i32_i32_=>_none) + (call_indirect (type $ii) (local.get $2) (local.get $3) (local.get $4) @@ -1262,7 +1264,7 @@ (global.get $stack_ptr) ) (global.set $stack_ptr - (i32.add + (i32.sub (local.get $1) (i32.const 16) ) From 8e3a6f0e0d5115d916dc15533cd37e962ebf5603 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Jun 2022 12:42:43 -0700 Subject: [PATCH 05/10] fix --- src/abi/stack.h | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/abi/stack.h b/src/abi/stack.h index aa4382fd129..f3ad025c018 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -45,35 +45,35 @@ inline Index stackAlign(Index size) { inline void getStackSpace(Index local, Function* func, Index size, Module& wasm) { // Attempt to locate the stack pointer by recognizing code idioms - // used by Emscripten. First, look for a global initialized to an - // imported variable named "STACKTOP" in environment "env". - auto* stackPointer = - GlobalUtils::getGlobalInitializedToImport(wasm, ENV, "STACKTOP"); + // used by Emscripten. + // // Starting with Emscripten 1.38.24, the stack pointer variable is // initialized with a literal constant, eliminating the import that - // we used to locate the stack pointer by name. We must match a more - // complicated idiom, expecting to see the module structured as follows: + // we used to locate the stack pointer by name. We must match the following + // idiom, expecting to see the module structured as follows: // //(module // ... // (export "stackSave" (func $stackSave)) // ... - // (func $stackSave (; 410 ;) (; has Stack IR ;) (result i32) + // (func $stackSave (result i32) // (global.get $STACKTOP) // ) // ... //) - if (!stackPointer) { - auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave"); - if (stackSaveFunctionExport && - stackSaveFunctionExport->kind == ExternalKind::Function) { - auto* stackSaveFunction = - wasm.getFunction(stackSaveFunctionExport->value); - assert(!stackSaveFunction->imported()); - auto* globalGet = stackSaveFunction->body->dynCast(); - if (globalGet) { - stackPointer = wasm.getGlobal(globalGet->name); - } + // + // That is, we don't know the name of the global, but we look for an export + // called "stackSave" and find the global from its internals. + Global* stackPointer = nullptr; + auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave"); + if (stackSaveFunctionExport && + stackSaveFunctionExport->kind == ExternalKind::Function) { + auto* stackSaveFunction = + wasm.getFunction(stackSaveFunctionExport->value); + assert(!stackSaveFunction->imported()); + auto* globalGet = stackSaveFunction->body->dynCast(); + if (globalGet) { + stackPointer = wasm.getGlobal(globalGet->name); } } if (!stackPointer) { From 8d8ce27d7b35e39bce46b3e08a26b73912451564 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Jun 2022 12:42:51 -0700 Subject: [PATCH 06/10] format --- src/abi/stack.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/abi/stack.h b/src/abi/stack.h index f3ad025c018..ab4fef52e75 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -68,8 +68,7 @@ getStackSpace(Index local, Function* func, Index size, Module& wasm) { auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave"); if (stackSaveFunctionExport && stackSaveFunctionExport->kind == ExternalKind::Function) { - auto* stackSaveFunction = - wasm.getFunction(stackSaveFunctionExport->value); + auto* stackSaveFunction = wasm.getFunction(stackSaveFunctionExport->value); assert(!stackSaveFunction->imported()); auto* globalGet = stackSaveFunction->body->dynCast(); if (globalGet) { From d6cbaeb02a9a00593c719be926a9a5657effcad0 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Jun 2022 12:44:33 -0700 Subject: [PATCH 07/10] simpler --- src/abi/stack.h | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/abi/stack.h b/src/abi/stack.h index ab4fef52e75..45b8c17a545 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -44,37 +44,7 @@ inline Index stackAlign(Index size) { inline void getStackSpace(Index local, Function* func, Index size, Module& wasm) { - // Attempt to locate the stack pointer by recognizing code idioms - // used by Emscripten. - // - // Starting with Emscripten 1.38.24, the stack pointer variable is - // initialized with a literal constant, eliminating the import that - // we used to locate the stack pointer by name. We must match the following - // idiom, expecting to see the module structured as follows: - // - //(module - // ... - // (export "stackSave" (func $stackSave)) - // ... - // (func $stackSave (result i32) - // (global.get $STACKTOP) - // ) - // ... - //) - // - // That is, we don't know the name of the global, but we look for an export - // called "stackSave" and find the global from its internals. - Global* stackPointer = nullptr; - auto* stackSaveFunctionExport = wasm.getExportOrNull("stackSave"); - if (stackSaveFunctionExport && - stackSaveFunctionExport->kind == ExternalKind::Function) { - auto* stackSaveFunction = wasm.getFunction(stackSaveFunctionExport->value); - assert(!stackSaveFunction->imported()); - auto* globalGet = stackSaveFunction->body->dynCast(); - if (globalGet) { - stackPointer = wasm.getGlobal(globalGet->name); - } - } + auto* stackPointer = getStackPointerGlobal(wasm); if (!stackPointer) { Fatal() << "getStackSpace: failed to find the stack pointer"; } From 1362117c52c1acdf847055681e3968094a535c5c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Jun 2022 12:45:59 -0700 Subject: [PATCH 08/10] fix --- src/abi/stack.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/abi/stack.h b/src/abi/stack.h index 45b8c17a545..2cf09bacfda 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -23,6 +23,7 @@ #include "ir/global-utils.h" #include "shared-constants.h" #include "wasm-builder.h" +#include "wasm-emscripten.h" #include "wasm.h" namespace wasm { From 3a2ee91c3a2a93e9c7d4eded783c4de01828ec67 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Jun 2022 12:47:24 -0700 Subject: [PATCH 09/10] fix --- src/passes/SpillPointers.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/passes/SpillPointers.cpp b/src/passes/SpillPointers.cpp index 4535105924c..eb942b9dcd6 100644 --- a/src/passes/SpillPointers.cpp +++ b/src/passes/SpillPointers.cpp @@ -65,9 +65,6 @@ struct SpillPointers // main entry point void doWalkFunction(Function* func) { - if (!canRun(func)) { - return; - } super::doWalkFunction(func); spillPointers(); } From 3a4b1066526343708097c20abfc765e4698b1598 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Fri, 3 Jun 2022 12:51:42 -0700 Subject: [PATCH 10/10] remove header --- src/abi/abi.h | 33 --------------------------------- src/abi/stack.h | 12 ++++++------ src/passes/SpillPointers.cpp | 23 +++++++++++++---------- 3 files changed, 19 insertions(+), 49 deletions(-) delete mode 100644 src/abi/abi.h diff --git a/src/abi/abi.h b/src/abi/abi.h deleted file mode 100644 index 239efd3f3bd..00000000000 --- a/src/abi/abi.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 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_abi_abi_h -#define wasm_abi_abi_h - -#include "wasm.h" - -namespace wasm { - -namespace ABI { - -// The pointer type. Will need to update this for wasm64 -const static Type PointerType = Type::i32; - -} // namespace ABI - -} // namespace wasm - -#endif // wasm_abi_abi_h diff --git a/src/abi/stack.h b/src/abi/stack.h index 2cf09bacfda..cc678b6e86f 100644 --- a/src/abi/stack.h +++ b/src/abi/stack.h @@ -17,7 +17,6 @@ #ifndef wasm_abi_stack_h #define wasm_abi_stack_h -#include "abi.h" #include "asmjs/shared-constants.h" #include "ir/find_all.h" #include "ir/global-utils.h" @@ -51,25 +50,26 @@ getStackSpace(Index local, Function* func, Index size, Module& wasm) { } // align the size size = stackAlign(size); + auto pointerType = wasm.memory.indexType; // TODO: find existing stack usage, and add on top of that - carefully Builder builder(wasm); auto* block = builder.makeBlock(); block->list.push_back(builder.makeLocalSet( - local, builder.makeGlobalGet(stackPointer->name, PointerType))); + local, builder.makeGlobalGet(stackPointer->name, pointerType))); // TODO: add stack max check Expression* added; - if (PointerType == Type::i32) { + if (pointerType == Type::i32) { // The stack goes downward in the LLVM wasm backend. added = builder.makeBinary(SubInt32, - builder.makeLocalGet(local, PointerType), + builder.makeLocalGet(local, pointerType), builder.makeConst(int32_t(size))); } else { - WASM_UNREACHABLE("unhandled PointerType"); + WASM_UNREACHABLE("unhandled pointerType"); } block->list.push_back(builder.makeGlobalSet(stackPointer->name, added)); auto makeStackRestore = [&]() { return builder.makeGlobalSet(stackPointer->name, - builder.makeLocalGet(local, PointerType)); + builder.makeLocalGet(local, pointerType)); }; // add stack restores to the returns FindAllPointers finder(func->body); diff --git a/src/passes/SpillPointers.cpp b/src/passes/SpillPointers.cpp index eb942b9dcd6..46b1fb47cf4 100644 --- a/src/passes/SpillPointers.cpp +++ b/src/passes/SpillPointers.cpp @@ -25,7 +25,6 @@ // * There is currently no check that there is enough stack space. // -#include "abi/abi.h" #include "abi/stack.h" #include "cfg/liveness-traversal.h" #include "pass.h" @@ -72,13 +71,17 @@ struct SpillPointers // map pointers to their offset in the spill area typedef std::unordered_map PointerMap; + Type pointerType; + void spillPointers() { + pointerType = getModule()->memory.indexType; + // we only care about possible pointers auto* func = getFunction(); PointerMap pointerMap; for (Index i = 0; i < func->getNumLocals(); i++) { - if (func->getLocalType(i) == ABI::PointerType) { - auto offset = pointerMap.size() * ABI::PointerType.getByteSize(); + if (func->getLocalType(i) == pointerType) { + auto offset = pointerMap.size() * pointerType.getByteSize(); pointerMap[i] = offset; } } @@ -123,7 +126,7 @@ struct SpillPointers if (!spilled) { // prepare stack support: get a pointer to stack space big enough // for all our data - spillLocal = Builder::addVar(func, ABI::PointerType); + spillLocal = Builder::addVar(func, pointerType); spilled = true; } // the origin was seen at walk, but the thing may have moved @@ -140,7 +143,7 @@ struct SpillPointers // get the stack space, and set the local to it ABI::getStackSpace(spillLocal, func, - ABI::PointerType.getByteSize() * pointerMap.size(), + pointerType.getByteSize() * pointerMap.size(), *getModule()); } } @@ -184,12 +187,12 @@ struct SpillPointers // add the spills for (auto index : toSpill) { block->list.push_back( - builder.makeStore(ABI::PointerType.getByteSize(), + builder.makeStore(pointerType.getByteSize(), pointerMap[index], - ABI::PointerType.getByteSize(), - builder.makeLocalGet(spillLocal, ABI::PointerType), - builder.makeLocalGet(index, ABI::PointerType), - ABI::PointerType)); + pointerType.getByteSize(), + builder.makeLocalGet(spillLocal, pointerType), + builder.makeLocalGet(index, pointerType), + pointerType)); } // add the (modified) call block->list.push_back(call);