Try precise effects for heap
diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..e450a75 --- /dev/null +++ b/build.sh
@@ -0,0 +1,118 @@ +#!/bin/bash +# +# Runs Binaryen optimization stages as defined in j2wasm_application.bzl +# Usage: ./run_wasm_opts.sh <input.wat> +# +# Make sure 'wasm-opt' is in your PATH. You can get it via: +# blaze build //third_party/java_src/j2cl/third_party:binaryen +# and then run like this: +# WASM_OPT=./blaze-bin/third_party/java_src/j2cl/third_party/wasm-opt ./run_wasm_opts.sh <input.wat> +# +# Alternatively, binaryen may be available via apt: +# sudo apt-get install binaryen + +set -euo pipefail + +if [[ -z "${1-}" ]]; then + echo "Usage: $0 <input.wat>" + exit 1 +fi + +INPUT_WAT=$1 +S1_OUT=intermediate1.wasm +S2_OUT=intermediate2.wasm +FINAL_OUT=output.wasm +WASM_OPT=${WASM_OPT:-wasm-opt} + +echo "Using wasm-opt: $(which "$WASM_OPT" || echo "$WASM_OPT")" + +COMMON_ARGS=( + --enable-exception-handling + --enable-gc + --enable-reference-types + --enable-sign-ext + --enable-strings + --enable-nontrapping-float-to-int + --enable-bulk-memory + --closed-world + --traps-never-happen +) + +STAGE1_ARGS=( + --merge-j2cl-itables + "--no-inline=*_<once>_*" + --generate-global-effects + -O3 + --cfp-reftest + --optimize-j2cl + --gufa + --unsubtyping + -O3 + --cfp-reftest + --optimize-j2cl + -O3 + --cfp-reftest + --optimize-j2cl +) + +STAGE2_ARGS=( + "--no-inline=*_<once>_*" + --generate-global-effects + --partial-inlining-ifs=4 + -fimfs=25 + --gufa + --unsubtyping + -O3 + --cfp-reftest + --optimize-j2cl + -O3 + --cfp-reftest + --optimize-j2cl + -O3 + --cfp-reftest + --optimize-j2cl + --gufa + --unsubtyping + -O3 + --cfp-reftest + --optimize-j2cl + -O3 + --cfp-reftest + --optimize-j2cl +) + +STAGE3_ARGS=( + "--no-full-inline=*_<once>_*" + --generate-global-effects + --partial-inlining-ifs=4 + --intrinsic-lowering + --gufa + --unsubtyping + -O3 + --cfp-reftest + --optimize-j2cl + -O3 + --optimize-j2cl + --cfp-reftest + --type-merging + -O3 + --cfp-reftest + --optimize-j2cl + --string-lowering-magic-imports-assert + --remove-unused-module-elements + --reorder-globals + --type-finalizing + --reorder-types +) + +echo "Running stage 1: $INPUT_WAT -> $S1_OUT" +"$WASM_OPT" "$INPUT_WAT" "${COMMON_ARGS[@]}" "${STAGE1_ARGS[@]}" --debuginfo -o "$S1_OUT" + +echo "Running stage 2: $S1_OUT -> $S2_OUT" +"$WASM_OPT" "$S1_OUT" "${COMMON_ARGS[@]}" "${STAGE2_ARGS[@]}" --debuginfo -o "$S2_OUT" + +echo "Running stage 3: $S2_OUT -> $FINAL_OUT" +"$WASM_OPT" "$S2_OUT" "${COMMON_ARGS[@]}" "${STAGE3_ARGS[@]}" -o "$FINAL_OUT" + +echo "Done. Intermediate files are $S1_OUT and $S2_OUT." +echo "Final output written to $FINAL_OUT"
diff --git a/src/ir/effects.h b/src/ir/effects.h index 56cea0a..c8246b3 100644 --- a/src/ir/effects.h +++ b/src/ir/effects.h
@@ -89,6 +89,11 @@ bool readsSharedMutableArray : 1; bool writesSharedArray : 1; + std::vector<std::pair<HeapType, Index>> structsWrittenList; + std::vector<std::pair<HeapType, Index>> structsReadList; + std::vector<HeapType> arraysWrittenList; + std::vector<HeapType> arraysReadList; + // A trap, either from an unreachable instruction, or from an implicit trap // that we do not ignore (see below). // @@ -347,19 +352,70 @@ ((writesSharedMemory || calls) && other.accessesSharedMemory()) || ((other.writesSharedMemory || other.calls) && accessesSharedMemory()) || ((writesTable || calls) && other.accessesTable()) || - ((other.writesTable || other.calls) && accessesTable()) || - ((writesStruct || calls) && other.accessesMutableStruct()) || - ((other.writesStruct || other.calls) && accessesMutableStruct()) || - ((writesSharedStruct || calls) && - other.accessesSharedMutableStruct()) || - ((other.writesSharedStruct || other.calls) && - accessesSharedMutableStruct()) || - ((writesArray || calls) && other.accessesArray()) || - ((other.writesArray || other.calls) && accessesArray()) || - ((writesSharedArray || calls) && other.accessesSharedArray()) || - ((other.writesSharedArray || other.calls) && accessesSharedArray())) { + ((other.writesTable || other.calls) && accessesTable())) { return true; } + + auto structsConflict = [&]() { + if ((calls && other.accessesMutableStruct()) || + (other.calls && accessesMutableStruct()) || + (calls && other.accessesSharedMutableStruct()) || + (other.calls && accessesSharedMutableStruct())) { + return true; + } + auto overlap = [](const std::vector<std::pair<HeapType, Index>>& a, + const std::vector<std::pair<HeapType, Index>>& b) { + for (auto& x : a) { + for (auto& y : b) { + if (x.second == y.second && + (HeapType::isSubType(x.first, y.first) || + HeapType::isSubType(y.first, x.first))) { + return true; + } + } + } + return false; + }; + if (overlap(structsWrittenList, other.structsWrittenList) || + overlap(structsWrittenList, other.structsReadList) || + overlap(structsReadList, other.structsWrittenList)) { + return true; + } + return false; + }; + if (structsConflict()) { + return true; + } + + auto arraysConflict = [&]() { + if ((calls && other.accessesArray()) || + (other.calls && accessesArray()) || + (calls && other.accessesSharedArray()) || + (other.calls && accessesSharedArray())) { + return true; + } + auto overlap = [](const std::vector<HeapType>& a, + const std::vector<HeapType>& b) { + for (auto& x : a) { + for (auto& y : b) { + if (HeapType::isSubType(x, y) || HeapType::isSubType(y, x)) { + return true; + } + } + } + return false; + }; + if (overlap(arraysWrittenList, other.arraysWrittenList) || + overlap(arraysWrittenList, other.arraysReadList) || + overlap(arraysReadList, other.arraysWrittenList)) { + return true; + } + return false; + }; + if (arraysConflict()) { + return true; + } + // Cannot reorder anything before dangling pops. if (danglingPop) { return true; @@ -477,6 +533,27 @@ readOrder = std::max(readOrder, other.readOrder); writeOrder = std::max(writeOrder, other.writeOrder); + for (auto& x : other.structsWrittenList) { + if (std::find(structsWrittenList.begin(), structsWrittenList.end(), x) == + structsWrittenList.end()) + structsWrittenList.push_back(x); + } + for (auto& x : other.structsReadList) { + if (std::find(structsReadList.begin(), structsReadList.end(), x) == + structsReadList.end()) + structsReadList.push_back(x); + } + for (auto& x : other.arraysWrittenList) { + if (std::find(arraysWrittenList.begin(), arraysWrittenList.end(), x) == + arraysWrittenList.end()) + arraysWrittenList.push_back(x); + } + for (auto& x : other.arraysReadList) { + if (std::find(arraysReadList.begin(), arraysReadList.end(), x) == + arraysReadList.end()) + arraysReadList.push_back(x); + } + for (auto i : other.localsRead) { localsRead.insert(i); } @@ -631,6 +708,11 @@ void readsStruct(HeapType type, Index index, MemoryOrder order) { assert(type.isStruct()); if (type.getStruct().fields[index].mutable_ == Mutable) { + if (std::find(parent.structsReadList.begin(), + parent.structsReadList.end(), + std::pair<HeapType, Index>{type, index}) == + parent.structsReadList.end()) + parent.structsReadList.push_back({type, index}); if (type.isShared()) { parent.readsSharedMutableStruct = true; parent.readOrder = std::max(parent.readOrder, order); @@ -642,6 +724,11 @@ void writesStruct(HeapType type, Index index, MemoryOrder order) { assert(type.isStruct()); + if (std::find(parent.structsWrittenList.begin(), + parent.structsWrittenList.end(), + std::pair<HeapType, Index>{type, index}) == + parent.structsWrittenList.end()) + parent.structsWrittenList.push_back({type, index}); if (type.isShared()) { parent.writesSharedStruct = true; parent.writeOrder = std::max(parent.writeOrder, order); @@ -653,6 +740,10 @@ void readsArray(HeapType type, MemoryOrder order) { assert(type.isArray()); if (type.getArray().element.mutable_ == Mutable) { + if (std::find(parent.arraysReadList.begin(), + parent.arraysReadList.end(), + type) == parent.arraysReadList.end()) + parent.arraysReadList.push_back(type); if (type.isShared()) { parent.readsSharedMutableArray = true; parent.readOrder = std::max(parent.readOrder, order); @@ -664,6 +755,10 @@ void writesArray(HeapType type, MemoryOrder order) { assert(type.isArray()); + if (std::find(parent.arraysWrittenList.begin(), + parent.arraysWrittenList.end(), + type) == parent.arraysWrittenList.end()) + parent.arraysWrittenList.push_back(type); if (type.isShared()) { parent.writesSharedArray = true; parent.writeOrder = std::max(parent.writeOrder, order);