diff --git a/src/analysis/lattices/flat.h b/src/analysis/lattices/flat.h new file mode 100644 index 00000000000..c34f8fc7cc2 --- /dev/null +++ b/src/analysis/lattices/flat.h @@ -0,0 +1,93 @@ +/* + * Copyright 2023 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_analysis_lattices_flat_h +#define wasm_analysis_lattices_flat_h + +#include + +#if __cplusplus >= 202002L +#include +#endif + +#include "../lattice.h" +#include "support/utilities.h" + +namespace wasm::analysis { + +#if __cplusplus >= 202002L + +template +concept Flattenable = std::copyable && std::equality_comparable; + +// Given a type T, Flat is the lattice where none of the values of T are +// comparable except with themselves, but they are all greater than a common +// bottom element not in T and less than a common top element also not in T. +template +#else +template +#endif +struct Flat { +private: + struct Bot {}; + struct Top {}; + +public: + struct Element : std::variant { + bool isBottom() const noexcept { return std::get_if(this); } + bool isTop() const noexcept { return std::get_if(this); } + const T* getVal() const noexcept { return std::get_if(this); } + T* getVal() noexcept { return std::get_if(this); } + }; + + Element getBottom() const noexcept { return Element{Bot{}}; } + Element getTop() const noexcept { return Element{Top{}}; } + Element get(T&& val) const noexcept { return Element{std::move(val)}; } + + LatticeComparison compare(const Element& a, const Element& b) const noexcept { + if (a.index() < b.index()) { + return LESS; + } else if (a.index() > b.index()) { + return GREATER; + } else if (auto pA = a.getVal(); pA && *pA != *b.getVal()) { + return NO_RELATION; + } else { + return EQUAL; + } + } + bool join(Element& self, const Element& other) const noexcept { + switch (compare(self, other)) { + case LESS: + self = other; + return true; + case NO_RELATION: + self = Element{Top{}}; + return true; + case GREATER: + case EQUAL: + return false; + } + WASM_UNREACHABLE("unexpected comparison result"); + } +}; + +#if __cplusplus >= 202002L +static_assert(Lattice>); +#endif + +} // namespace wasm::analysis + +#endif // wasm_analysis_lattices_flat_h diff --git a/test/gtest/lattices.cpp b/test/gtest/lattices.cpp index 75b232b8e16..44b8d0132e6 100644 --- a/test/gtest/lattices.cpp +++ b/test/gtest/lattices.cpp @@ -15,6 +15,7 @@ */ #include "analysis/lattices/bool.h" +#include "analysis/lattices/flat.h" #include "analysis/lattices/int.h" #include "analysis/lattices/inverted.h" #include "gtest/gtest.h" @@ -194,3 +195,105 @@ TEST(InvertedLattice, DoubleInverted) { EXPECT_FALSE(identity.getBottom()); EXPECT_TRUE(identity.getTop()); } + +TEST(FlatLattice, GetBottom) { + analysis::Flat flat; + EXPECT_TRUE(flat.getBottom().isBottom()); + EXPECT_FALSE(flat.getBottom().getVal()); + EXPECT_FALSE(flat.getBottom().isTop()); +} + +TEST(FlatLattice, GetVal) { + analysis::Flat flat; + EXPECT_FALSE(flat.get(10).isBottom()); + ASSERT_TRUE(flat.get(10).getVal()); + EXPECT_FALSE(flat.get(10).isTop()); + + auto val = flat.get(10); + EXPECT_EQ(*val.getVal(), 10); +} + +TEST(FlatLattice, GetTop) { + analysis::Flat flat; + EXPECT_FALSE(flat.getTop().isBottom()); + EXPECT_FALSE(flat.getTop().getVal()); + EXPECT_TRUE(flat.getTop().isTop()); +} + +TEST(FlatLattice, Compare) { + analysis::Flat flat; + auto bot = flat.getBottom(); + auto a = flat.get(0); + auto b = flat.get(1); + auto top = flat.getTop(); + + EXPECT_EQ(flat.compare(bot, bot), analysis::EQUAL); + EXPECT_EQ(flat.compare(bot, a), analysis::LESS); + EXPECT_EQ(flat.compare(bot, b), analysis::LESS); + EXPECT_EQ(flat.compare(bot, top), analysis::LESS); + + EXPECT_EQ(flat.compare(a, bot), analysis::GREATER); + EXPECT_EQ(flat.compare(a, a), analysis::EQUAL); + EXPECT_EQ(flat.compare(a, b), analysis::NO_RELATION); + EXPECT_EQ(flat.compare(a, top), analysis::LESS); + + EXPECT_EQ(flat.compare(b, bot), analysis::GREATER); + EXPECT_EQ(flat.compare(b, a), analysis::NO_RELATION); + EXPECT_EQ(flat.compare(b, b), analysis::EQUAL); + EXPECT_EQ(flat.compare(b, top), analysis::LESS); + + EXPECT_EQ(flat.compare(top, bot), analysis::GREATER); + EXPECT_EQ(flat.compare(top, a), analysis::GREATER); + EXPECT_EQ(flat.compare(top, b), analysis::GREATER); + EXPECT_EQ(flat.compare(top, top), analysis::EQUAL); +} + +TEST(FlatLattice, Join) { + analysis::Flat flat; + auto elem = flat.getBottom(); + + // bot u bot = bot + EXPECT_FALSE(flat.join(elem, flat.getBottom())); + EXPECT_TRUE(elem.isBottom()); + + // bot u top = top + EXPECT_TRUE(flat.join(elem, flat.getTop())); + EXPECT_TRUE(elem.isTop()); + + // bot u 10 = 10 + elem = flat.getBottom(); + EXPECT_TRUE(flat.join(elem, flat.get(10))); + ASSERT_TRUE(elem.getVal()); + EXPECT_EQ(*elem.getVal(), 10); + + // 10 u bot = 10 + EXPECT_FALSE(flat.join(elem, flat.getBottom())); + ASSERT_TRUE(elem.getVal()); + EXPECT_EQ(*elem.getVal(), 10); + + // 10 u 10 = 10 + EXPECT_FALSE(flat.join(elem, flat.get(10))); + ASSERT_TRUE(elem.getVal()); + EXPECT_EQ(*elem.getVal(), 10); + + // 10 u 999 = top + EXPECT_TRUE(flat.join(elem, flat.get(999))); + ASSERT_TRUE(elem.isTop()); + + // 10 u top = top + elem = flat.get(10); + EXPECT_TRUE(flat.join(elem, flat.getTop())); + ASSERT_TRUE(elem.isTop()); + + // top u bot = top + EXPECT_FALSE(flat.join(elem, flat.getBottom())); + EXPECT_TRUE(elem.isTop()); + + // top u 10 = top + EXPECT_FALSE(flat.join(elem, flat.get(10))); + EXPECT_TRUE(elem.isTop()); + + // top u top = top + EXPECT_FALSE(flat.join(elem, flat.getTop())); + EXPECT_TRUE(elem.isTop()); +}