From 0fc9e55f2f2c42dd8efbdb637af431bb9f4ec3a1 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:07:09 -0700 Subject: [PATCH 01/10] go --- src/passes/ConstantFieldPropagation.cpp | 5 +- src/support/inplace_vector.h | 193 ++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 3 deletions(-) create mode 100644 src/support/inplace_vector.h diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index 66f45bb6047..93d1ac7c567 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -60,6 +60,7 @@ #include "ir/utils.h" #include "pass.h" #include "support/hash.h" +#include "support/inplace_vector.h" #include "support/small_vector.h" #include "support/unique_deferring_queue.h" #include "wasm-builder.h" @@ -311,9 +312,7 @@ struct FunctionOptimizer : public WalkerPass> { // values = [ { 42, [A, B] }, { 1337, [C] } ]; struct Value { PossibleConstantValues constant; - // Use a SmallVector as we'll only have 2 Values, and so the stack usage - // here is fixed. - SmallVector types; + std::inplace_vector types; // Whether this slot is used. If so, |constant| has a value, and |types| // is not empty. diff --git a/src/support/inplace_vector.h b/src/support/inplace_vector.h new file mode 100644 index 00000000000..0df08dd8c5e --- /dev/null +++ b/src/support/inplace_vector.h @@ -0,0 +1,193 @@ +/* + * Copyright 2026 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. + */ + +// +// A vector of elements with a maximum size, storing them all in-place. This is +// similar to C++26's inplace_vector, and is basically a small_vector, except +// there is never any dynamic storage. +// + +#ifndef wasm_support_inplace_vector_h +#define wasm_support_inplace_vector_h + +#include +#include +#include + +#include "support/parent_index_iterator.h" + +namespace std { + +template class inplace_vector { + // fixed-space storage + size_t usedFixed = 0; + std::array fixed{}; + +public: + using value_type = T; + + inplace_vector() {} + inplace_vector(const inplace_vector& other) + : usedFixed(other.usedFixed), fixed(other.fixed) {} + inplace_vector(inplace_vector&& other) + : usedFixed(other.usedFixed), fixed(std::move(other.fixed)) {} + inplace_vector(std::initializer_list init) { + for (T item : init) { + push_back(item); + } + } + inplace_vector(size_t initialSize) { resize(initialSize); } + + inplace_vector& operator=(const inplace_vector& other) { + usedFixed = other.usedFixed; + fixed = other.fixed; + return *this; + } + + inplace_vector& operator=(inplace_vector&& other) { + usedFixed = other.usedFixed; + fixed = std::move(other.fixed); + return *this; + } + + T& operator[](size_t i) { return fixed[i]; } + + const T& operator[](size_t i) const { + return const_cast&>(*this)[i]; + } + + void push_back(const T& x) { + assert(usedFixed < N); + fixed[usedFixed++] = x; + } + + template void emplace_back(ArgTypes&&... Args) { + assert(usedFixed < N); + new (&fixed[usedFixed++]) T(std::forward(Args)...); + } + + void pop_back() { + assert(usedFixed > 0); + usedFixed--; + } + + T& back() { + assert(usedFixed > 0); + return fixed[usedFixed - 1]; + } + + const T& back() const { + assert(usedFixed > 0); + return fixed[usedFixed - 1]; + } + + size_t size() const { return usedFixed; } + + bool empty() const { return size() == 0; } + + void clear() { usedFixed = 0; } + + void resize(size_t newSize) { + assert(newSize <= N); + usedFixed = newSize; + } + + size_t capacity() const { return N; } + + bool operator==(const inplace_vector& other) const { + if (usedFixed != other.usedFixed) { + return false; + } + for (size_t i = 0; i < usedFixed; i++) { + if (fixed[i] != other.fixed[i]) { + return false; + } + } + return true; + } + + bool operator!=(const inplace_vector& other) const { + return !(*this == other); + } + + // iteration + + struct Iterator : wasm::ParentIndexIterator*, Iterator> { + using value_type = T; + using pointer = T*; + using reference = T&; + + Iterator(inplace_vector* parent, size_t index) + : wasm::ParentIndexIterator*, Iterator>{parent, + index} {} + Iterator(const Iterator& other) = default; + + T& operator*() { return (*this->parent)[this->index]; } + }; + + struct ConstIterator + : wasm::ParentIndexIterator*, ConstIterator> { + using value_type = const T; + using pointer = const T*; + using reference = const T&; + + ConstIterator(const inplace_vector* parent, size_t index) + : wasm::ParentIndexIterator*, ConstIterator>{ + parent, index} {} + ConstIterator(const ConstIterator& other) = default; + + const T& operator*() const { return (*this->parent)[this->index]; } + }; + + Iterator begin() { return Iterator(this, 0); } + Iterator end() { return Iterator(this, size()); } + ConstIterator begin() const { return ConstIterator(this, 0); } + ConstIterator end() const { return ConstIterator(this, size()); } + + void erase(Iterator a, Iterator b) { + // Atm we only support erasing at the end, which is very efficient. + assert(b == end()); + resize(a.index); + } +}; + +// A inplace_vector for which some values may be read before they are written, +// and in that case they have the value zero. +template +struct ZeroInitinplace_vector : public inplace_vector { + T& operator[](size_t i) { + if (i >= this->size()) { + resize(i + 1); + } + return inplace_vector::operator[](i); + } + + const T& operator[](size_t i) const { + return const_cast&>(*this)[i]; + } + + void resize(size_t newSize) { + auto oldSize = this->size(); + inplace_vector::resize(newSize); + for (size_t i = oldSize; i < this->size(); i++) { + (*this)[i] = 0; + } + } +}; + +} // namespace std + +#endif // wasm_support_inplace_vector_h From 5ad1b7511ec258bc946f8b68024c9994dafff8ce Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:09:53 -0700 Subject: [PATCH 02/10] go --- test/gtest/CMakeLists.txt | 1 + test/gtest/inplace_vector.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 test/gtest/inplace_vector.cpp diff --git a/test/gtest/CMakeLists.txt b/test/gtest/CMakeLists.txt index 05ce44006a6..4bd358032d7 100644 --- a/test/gtest/CMakeLists.txt +++ b/test/gtest/CMakeLists.txt @@ -17,6 +17,7 @@ set(unittest_SOURCES int128.cpp leaves.cpp glbs.cpp + inplace_vector.cpp interpreter.cpp intervals.cpp istring.cpp diff --git a/test/gtest/inplace_vector.cpp b/test/gtest/inplace_vector.cpp new file mode 100644 index 00000000000..8fb2307dfe8 --- /dev/null +++ b/test/gtest/inplace_vector.cpp @@ -0,0 +1,9 @@ +#include "support/inplace_vector.h" +#include "gtest/gtest.h" + +using InplaceVectorTest = ::testing::Test; + +TEST_F(InplaceVectorTest, Basics) { + std::inplace_vector vec; + EXPECT_EQ(sizeof(vec), sizeof(size_t) + 10 * sizeof(int64_t)); +} From 81813634c5acdc2bf9e7916311923cdd4bfa0510 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:14:36 -0700 Subject: [PATCH 03/10] go --- src/support/inplace_vector.h | 24 ------------------------ test/gtest/inplace_vector.cpp | 31 ++++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/support/inplace_vector.h b/src/support/inplace_vector.h index 0df08dd8c5e..95b123a3eec 100644 --- a/src/support/inplace_vector.h +++ b/src/support/inplace_vector.h @@ -164,30 +164,6 @@ template class inplace_vector { } }; -// A inplace_vector for which some values may be read before they are written, -// and in that case they have the value zero. -template -struct ZeroInitinplace_vector : public inplace_vector { - T& operator[](size_t i) { - if (i >= this->size()) { - resize(i + 1); - } - return inplace_vector::operator[](i); - } - - const T& operator[](size_t i) const { - return const_cast&>(*this)[i]; - } - - void resize(size_t newSize) { - auto oldSize = this->size(); - inplace_vector::resize(newSize); - for (size_t i = oldSize; i < this->size(); i++) { - (*this)[i] = 0; - } - } -}; - } // namespace std #endif // wasm_support_inplace_vector_h diff --git a/test/gtest/inplace_vector.cpp b/test/gtest/inplace_vector.cpp index 8fb2307dfe8..ad0f2627c0b 100644 --- a/test/gtest/inplace_vector.cpp +++ b/test/gtest/inplace_vector.cpp @@ -3,7 +3,36 @@ using InplaceVectorTest = ::testing::Test; -TEST_F(InplaceVectorTest, Basics) { +TEST_F(InplaceVectorTest, Size) { std::inplace_vector vec; EXPECT_EQ(sizeof(vec), sizeof(size_t) + 10 * sizeof(int64_t)); } + +TEST_F(InplaceVectorTest, Basics) { + std::inplace_vector vec; + + vec.push_back(10); + EXPECT_EQ(vec[0], 10); + + vec.resize(3); + EXPECT_EQ(vec.size(), 3); + + vec[1] = 20; + vec[2] = 30; + EXPECT_EQ(vec[1], 20); + EXPECT_EQ(vec[2], 30); + + vec.pop_back(); + EXPECT_EQ(vec.size(), 2); +} + +TEST_F(InplaceVectorTest, I) { + std::inplace_vector vec{10, 20, 30}; + std::vector normal; + + for (auto x : vec) { + normal.push_back(x); + } + + EXPECT_EQ(normal, std::vector({10, 20, 30})); +} From d0ad2f48cc4cb06fa9507457816217eded05548c Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:14:43 -0700 Subject: [PATCH 04/10] go --- test/gtest/inplace_vector.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/gtest/inplace_vector.cpp b/test/gtest/inplace_vector.cpp index ad0f2627c0b..bf651b096e7 100644 --- a/test/gtest/inplace_vector.cpp +++ b/test/gtest/inplace_vector.cpp @@ -18,7 +18,7 @@ TEST_F(InplaceVectorTest, Basics) { EXPECT_EQ(vec.size(), 3); vec[1] = 20; - vec[2] = 30; + vec[2] = 30; EXPECT_EQ(vec[1], 20); EXPECT_EQ(vec[2], 30); From c5b7d1d2752dc3b731328560f0f6e08b730e9d38 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:15:38 -0700 Subject: [PATCH 05/10] go --- src/support/inplace_vector.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/support/inplace_vector.h b/src/support/inplace_vector.h index 95b123a3eec..a4971c8b2b4 100644 --- a/src/support/inplace_vector.h +++ b/src/support/inplace_vector.h @@ -16,8 +16,9 @@ // // A vector of elements with a maximum size, storing them all in-place. This is -// similar to C++26's inplace_vector, and is basically a small_vector, except +// similar to c++26's inplace_vector, and is basically a small_vector, except // there is never any dynamic storage. +// TODO: remove when we have c++26 // #ifndef wasm_support_inplace_vector_h From 0e35b2b857e1176ffc8141a74501f455e4052414 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:16:42 -0700 Subject: [PATCH 06/10] go --- test/gtest/inplace_vector.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/gtest/inplace_vector.cpp b/test/gtest/inplace_vector.cpp index bf651b096e7..10c46b1f633 100644 --- a/test/gtest/inplace_vector.cpp +++ b/test/gtest/inplace_vector.cpp @@ -5,6 +5,7 @@ using InplaceVectorTest = ::testing::Test; TEST_F(InplaceVectorTest, Size) { std::inplace_vector vec; + // An inplace_vector is just a size plus the in-place storage. EXPECT_EQ(sizeof(vec), sizeof(size_t) + 10 * sizeof(int64_t)); } From 626b5d77eee5f09e772404d8fb09553e7d04ee9a Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:41:01 -0700 Subject: [PATCH 07/10] feedback --- src/support/inplace_vector.h | 4 ++-- test/gtest/inplace_vector.cpp | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/support/inplace_vector.h b/src/support/inplace_vector.h index a4971c8b2b4..35dd7ed30b2 100644 --- a/src/support/inplace_vector.h +++ b/src/support/inplace_vector.h @@ -30,7 +30,7 @@ #include "support/parent_index_iterator.h" -namespace std { +namespace wasm { template class inplace_vector { // fixed-space storage @@ -165,6 +165,6 @@ template class inplace_vector { } }; -} // namespace std +} // namespace wasm #endif // wasm_support_inplace_vector_h diff --git a/test/gtest/inplace_vector.cpp b/test/gtest/inplace_vector.cpp index 10c46b1f633..34c1a3d3e1b 100644 --- a/test/gtest/inplace_vector.cpp +++ b/test/gtest/inplace_vector.cpp @@ -3,17 +3,22 @@ using InplaceVectorTest = ::testing::Test; +using namespace wasm; + TEST_F(InplaceVectorTest, Size) { - std::inplace_vector vec; + inplace_vector vec; // An inplace_vector is just a size plus the in-place storage. EXPECT_EQ(sizeof(vec), sizeof(size_t) + 10 * sizeof(int64_t)); } TEST_F(InplaceVectorTest, Basics) { - std::inplace_vector vec; + inplace_vector vec; + EXPECT_EQ(vec.size(), 0); + EXPECT_TRUE(vec.empty()); vec.push_back(10); EXPECT_EQ(vec[0], 10); + EXPECT_EQ(vec.size(), 1); vec.resize(3); EXPECT_EQ(vec.size(), 3); @@ -28,7 +33,7 @@ TEST_F(InplaceVectorTest, Basics) { } TEST_F(InplaceVectorTest, I) { - std::inplace_vector vec{10, 20, 30}; + inplace_vector vec{10, 20, 30}; std::vector normal; for (auto x : vec) { From 58964063b1db7975aee5e48e546af5bb428a0657 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 14:41:20 -0700 Subject: [PATCH 08/10] feedback --- src/passes/ConstantFieldPropagation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index 93d1ac7c567..172a266d703 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -312,7 +312,7 @@ struct FunctionOptimizer : public WalkerPass> { // values = [ { 42, [A, B] }, { 1337, [C] } ]; struct Value { PossibleConstantValues constant; - std::inplace_vector types; + inplace_vector types; // Whether this slot is used. If so, |constant| has a value, and |types| // is not empty. From 6e80fde633f8e876e3bad5a39d6bbbfdbbc9ad12 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 15:40:13 -0700 Subject: [PATCH 09/10] const --- src/support/inplace_vector.h | 2 +- src/support/small_vector.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/support/inplace_vector.h b/src/support/inplace_vector.h index 35dd7ed30b2..21826ea737d 100644 --- a/src/support/inplace_vector.h +++ b/src/support/inplace_vector.h @@ -46,7 +46,7 @@ template class inplace_vector { inplace_vector(inplace_vector&& other) : usedFixed(other.usedFixed), fixed(std::move(other.fixed)) {} inplace_vector(std::initializer_list init) { - for (T item : init) { + for (const T& item : init) { push_back(item); } } diff --git a/src/support/small_vector.h b/src/support/small_vector.h index 8cad6ade29e..f4a1560d21f 100644 --- a/src/support/small_vector.h +++ b/src/support/small_vector.h @@ -49,7 +49,7 @@ template class SmallVector { : usedFixed(other.usedFixed), fixed(std::move(other.fixed)), flexible(std::move(other.flexible)) {} SmallVector(std::initializer_list init) { - for (T item : init) { + for (const T& item : init) { push_back(item); } } From cf7fcc6522757ee362efccf63a5ae319ecb33a3d Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Mon, 8 Jun 2026 15:44:56 -0700 Subject: [PATCH 10/10] undo CFP change --- src/passes/ConstantFieldPropagation.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/passes/ConstantFieldPropagation.cpp b/src/passes/ConstantFieldPropagation.cpp index 172a266d703..66f45bb6047 100644 --- a/src/passes/ConstantFieldPropagation.cpp +++ b/src/passes/ConstantFieldPropagation.cpp @@ -60,7 +60,6 @@ #include "ir/utils.h" #include "pass.h" #include "support/hash.h" -#include "support/inplace_vector.h" #include "support/small_vector.h" #include "support/unique_deferring_queue.h" #include "wasm-builder.h" @@ -312,7 +311,9 @@ struct FunctionOptimizer : public WalkerPass> { // values = [ { 42, [A, B] }, { 1337, [C] } ]; struct Value { PossibleConstantValues constant; - inplace_vector types; + // Use a SmallVector as we'll only have 2 Values, and so the stack usage + // here is fixed. + SmallVector types; // Whether this slot is used. If so, |constant| has a value, and |types| // is not empty.