Skip to content

Commit

Permalink
[analysis] Implement Flat lattice (WebAssembly#6039)
Browse files Browse the repository at this point in the history
Given a type `T`, `Flat<T>` 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`.
  • Loading branch information
tlively authored and radekdoulik committed Jul 12, 2024
1 parent 30fe835 commit 172fce2
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 0 deletions.
93 changes: 93 additions & 0 deletions src/analysis/lattices/flat.h
Original file line number Diff line number Diff line change
@@ -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 <variant>

#if __cplusplus >= 202002L
#include <concepts>
#endif

#include "../lattice.h"
#include "support/utilities.h"

namespace wasm::analysis {

#if __cplusplus >= 202002L

template<typename T>
concept Flattenable = std::copyable<T> && std::equality_comparable<T>;

// Given a type T, Flat<T> 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<Flattenable T>
#else
template<typename T>
#endif
struct Flat {
private:
struct Bot {};
struct Top {};

public:
struct Element : std::variant<Bot, T, Top> {
bool isBottom() const noexcept { return std::get_if<Bot>(this); }
bool isTop() const noexcept { return std::get_if<Top>(this); }
const T* getVal() const noexcept { return std::get_if<T>(this); }
T* getVal() noexcept { return std::get_if<T>(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<Flat<int>>);
#endif

} // namespace wasm::analysis

#endif // wasm_analysis_lattices_flat_h
103 changes: 103 additions & 0 deletions test/gtest/lattices.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -194,3 +195,105 @@ TEST(InvertedLattice, DoubleInverted) {
EXPECT_FALSE(identity.getBottom());
EXPECT_TRUE(identity.getTop());
}

TEST(FlatLattice, GetBottom) {
analysis::Flat<int> flat;
EXPECT_TRUE(flat.getBottom().isBottom());
EXPECT_FALSE(flat.getBottom().getVal());
EXPECT_FALSE(flat.getBottom().isTop());
}

TEST(FlatLattice, GetVal) {
analysis::Flat<int> 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<int> flat;
EXPECT_FALSE(flat.getTop().isBottom());
EXPECT_FALSE(flat.getTop().getVal());
EXPECT_TRUE(flat.getTop().isTop());
}

TEST(FlatLattice, Compare) {
analysis::Flat<int> 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<int> 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());
}

0 comments on commit 172fce2

Please sign in to comment.