Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
303 changes: 303 additions & 0 deletions src/analysis/lattices/bound.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
/*
* 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.
*/

#ifndef wasm_analysis_lattices_bound_h
#define wasm_analysis_lattices_bound_h

#include <iostream>
#include <optional>
#include <utility>
#include <variant>

#include "analysis/lattice.h"

namespace wasm::analysis {

enum class BoundRelation { LT, LE, GE, GT };

inline std::ostream& operator<<(std::ostream& os, BoundRelation rel) {
switch (rel) {
case BoundRelation::LT:
os << "<";
break;
case BoundRelation::LE:
os << "<=";
break;
case BoundRelation::GE:
os << ">=";
break;
case BoundRelation::GT:
os << ">";
break;
}
return os;
}

template<FullLattice L> struct Bound {
struct Bot {
bool operator==(const Bot&) const { return true; }
bool operator!=(const Bot&) const { return false; }
};
struct Top {
bool operator==(const Top&) const { return true; }
bool operator!=(const Top&) const { return false; }
};

struct Constraint {
BoundRelation rel;
typename L::Element val;

bool operator==(const Constraint& other) const {
return rel == other.rel && val == other.val;
}
bool operator!=(const Constraint& other) const { return !(*this == other); }
};

using Element = std::variant<Constraint, Bot, Top>;

L lattice;

Bound(L&& lattice) : lattice(std::move(lattice)) {}

Element getBottom() const noexcept { return Bot{}; }
Element getTop() const noexcept { return Top{}; }

Element makeBound(BoundRelation rel, typename L::Element val) const {
auto bot_L = lattice.getBottom();
std::optional<typename L::Element> top_L = std::nullopt;
if constexpr (requires {
{ lattice.getTop() } -> std::same_as<typename L::Element>;
}) {
top_L = lattice.getTop();
}

if (rel == BoundRelation::LT && lattice.compare(val, bot_L) == EQUAL) {
return Bot{};
}
if (top_L && rel == BoundRelation::GT &&
lattice.compare(val, *top_L) == EQUAL) {
return Bot{};
}
if (top_L && rel == BoundRelation::LE &&
lattice.compare(val, *top_L) == EQUAL) {
return Top{};
}
if (rel == BoundRelation::GE && lattice.compare(val, bot_L) == EQUAL) {
return Top{};
}

return Constraint{rel, val};
}

bool implies(const Constraint& c1, const Constraint& c2) const noexcept {
auto comp = lattice.compare(c1.val, c2.val);
switch (c1.rel) {
case BoundRelation::LT:
switch (c2.rel) {
case BoundRelation::LT:
return comp == LESS || comp == EQUAL;
case BoundRelation::LE:
return comp == LESS || comp == EQUAL;
default:
return false;
}
break;
case BoundRelation::LE:
switch (c2.rel) {
case BoundRelation::LE:
return comp == LESS || comp == EQUAL;
case BoundRelation::LT:
return comp == LESS;
default:
return false;
}
break;
case BoundRelation::GE:
switch (c2.rel) {
case BoundRelation::GE:
return comp == GREATER || comp == EQUAL;
case BoundRelation::GT:
return comp == GREATER;
default:
return false;
}
break;
case BoundRelation::GT:
switch (c2.rel) {
case BoundRelation::GT:
return comp == GREATER || comp == EQUAL;
case BoundRelation::GE:
return comp == GREATER || comp == EQUAL;
default:
return false;
}
break;
}
return false;
}

LatticeComparison compare(const Element& a, const Element& b) const noexcept {
if (std::holds_alternative<Bot>(a) && std::holds_alternative<Bot>(b))
return EQUAL;
if (std::holds_alternative<Bot>(a))
return LESS;
if (std::holds_alternative<Bot>(b))
return GREATER;

if (std::holds_alternative<Top>(a) && std::holds_alternative<Top>(b))
return EQUAL;
if (std::holds_alternative<Top>(a))
return GREATER;
if (std::holds_alternative<Top>(b))
return LESS;

const auto& c1 = std::get<Constraint>(a);
const auto& c2 = std::get<Constraint>(b);

bool a_implies_b = implies(c1, c2);
bool b_implies_a = implies(c2, c1);

if (a_implies_b && b_implies_a)
return EQUAL;
if (a_implies_b)
return LESS;
if (b_implies_a)
return GREATER;
return NO_RELATION;
}

bool join(Element& joinee, const Element& joiner) const noexcept {
if (std::holds_alternative<Bot>(joiner))
return false;
if (std::holds_alternative<Bot>(joinee)) {
joinee = joiner;
return true;
}
if (std::holds_alternative<Top>(joinee))
return false;
if (std::holds_alternative<Top>(joiner)) {
joinee = Top{};
return true;
}

const auto& c1 = std::get<Constraint>(joinee);
const auto& c2 = std::get<Constraint>(joiner);

auto result = joinConstraints(c1, c2);
if (joinee == result)
return false;
joinee = std::move(result);
return true;
}

bool meet(Element& meetee, const Element& meeter) const noexcept {
if (std::holds_alternative<Top>(meeter))
return false;
if (std::holds_alternative<Top>(meetee)) {
meetee = meeter;
return true;
}
if (std::holds_alternative<Bot>(meetee))
return false;
if (std::holds_alternative<Bot>(meeter)) {
meetee = Bot{};
return true;
}

const auto& c1 = std::get<Constraint>(meetee);
const auto& c2 = std::get<Constraint>(meeter);

auto result = meetConstraints(c1, c2);
if (meetee == result)
return false;
meetee = std::move(result);
return true;
}

private:
typename L::Element meet_values(const typename L::Element& a,
const typename L::Element& b) const {
auto copy = a;
lattice.meet(copy, b);
return copy;
}

Element joinConstraints(const Constraint& c1, const Constraint& c2) const {
if (implies(c1, c2))
return c2;
if (implies(c2, c1))
return c1;

auto is_upper = [](BoundRelation r) {
return r == BoundRelation::LT || r == BoundRelation::LE;
};
auto is_lower = [](BoundRelation r) {
return r == BoundRelation::GT || r == BoundRelation::GE;
};

if (is_upper(c1.rel) && is_upper(c2.rel)) {
auto joined_val = c1.val;
lattice.join(joined_val, c2.val);
return makeBound(BoundRelation::LT, joined_val);
}

if (is_lower(c1.rel) && is_lower(c2.rel)) {
auto met = meet_values(c1.val, c2.val);
return makeBound(BoundRelation::GT, met);
}

return Top{};
}

Element meetConstraints(const Constraint& c1, const Constraint& c2) const {
if (implies(c1, c2))
return c1;
if (implies(c2, c1))
return c2;

if (c1.rel == c2.rel) {
if (c1.rel == BoundRelation::LE) {
auto met_val = meet_values(c1.val, c2.val);
return makeBound(c1.rel, met_val);
}
if (c1.rel == BoundRelation::GE) {
auto joined_val = c1.val;
lattice.join(joined_val, c2.val);
return makeBound(c1.rel, joined_val);
}
}

return Bot{};
}
};

template<FullLattice L>
std::ostream& operator<<(std::ostream& os,
const typename Bound<L>::Element& elem) {
if (std::holds_alternative<typename Bound<L>::Bot>(elem)) {
os << "bound bot";
} else if (std::holds_alternative<typename Bound<L>::Top>(elem)) {
os << "bound top";
} else {
const auto& c = std::get<typename Bound<L>::Constraint>(elem);
os << "Bound(" << c.rel << " " << c.val << ")";
}
return os;
}

} // namespace wasm::analysis

#endif // wasm_analysis_lattices_bound_h
Loading
Loading