Skip to content

Commit 2337d40

Browse files
committed
[analysis][NFC] Rename makeLeastUpperBound to join and move it to lattice
In general, the operation of taking the least upper bound of two lattice elements may depend on some state stored in the lattice object rather than in the elements themselves. To avoid forcing the elements to be larger and more complicated in that case (by storing a parent pointer back to the lattice), move the least upper bound operation to make it a method of the lattice rather than the lattice element. This is also more consistent with where we put e.g. the `compare` method. While we are at it, rename `makeLeastUpperBound` to `join`, which is much shorter and nicer. Usually we avoid using esoteric mathematical jargon like this, but "join" as a normal verb actually describes the operation nicely, so I think it is ok in this case.
1 parent 5be5539 commit 2337d40

File tree

6 files changed

+66
-61
lines changed

6 files changed

+66
-61
lines changed

src/analysis/lattice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ concept Lattice = requires(const L& lattice,
5555
} noexcept -> std::same_as<LatticeComparison>;
5656
// We need to be able to get the least upper bound of two elements and know
5757
// whether any change was made.
58-
{ elem.makeLeastUpperBound(constElem) } noexcept -> std::same_as<bool>;
58+
{ lattice.join(elem, constElem) } noexcept -> std::same_as<bool>;
5959
};
6060

6161
#else // __cplusplus >= 202002L

src/analysis/lattices/powerset-impl.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,18 @@ inline size_t FiniteIntPowersetLattice::Element::count() const {
7878
// Least upper bound is implemented as a logical OR between the bitvectors on
7979
// both sides. We return true if a bit is flipped in-place on the left so the
8080
// worklist algorithm will know if when to enqueue more work.
81-
inline bool FiniteIntPowersetLattice::Element::makeLeastUpperBound(
82-
const FiniteIntPowersetLattice::Element& other) noexcept {
81+
inline bool
82+
FiniteIntPowersetLattice::join(Element& self,
83+
const Element& other) const noexcept {
8384
// Both must be from powerset lattice of the same set.
84-
assert(other.bitvector.size() == bitvector.size());
85+
assert(other.bitvector.size() == self.bitvector.size());
8586

8687
bool modified = false;
87-
for (size_t i = 0; i < bitvector.size(); ++i) {
88+
for (size_t i = 0; i < self.bitvector.size(); ++i) {
8889
// Bit is flipped on self only if self is false and other is true when self
8990
// and other are OR'ed together.
90-
modified |= (!bitvector[i] && other.bitvector[i]);
91-
bitvector[i] = bitvector[i] || other.bitvector[i];
91+
modified |= (!self.bitvector[i] && other.bitvector[i]);
92+
self.bitvector[i] = self.bitvector[i] || other.bitvector[i];
9293
}
9394

9495
return modified;

src/analysis/lattices/powerset.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,6 @@ class FiniteIntPowersetLattice {
7171
bool isTop() const { return count() == bitvector.size(); }
7272
bool isBottom() const { return count() == 0; }
7373

74-
// Calculates the LUB of this element with some other element and sets
75-
// this element to the LUB in place. Returns true if this element before
76-
// this method call was different than the LUB.
77-
bool makeLeastUpperBound(const Element& other) noexcept;
78-
7974
// Prints out the bits in the bitvector for a lattice element.
8075
void print(std::ostream& os);
8176

@@ -89,6 +84,11 @@ class FiniteIntPowersetLattice {
8984

9085
// Returns an instance of the bottom lattice element.
9186
Element getBottom() const noexcept;
87+
88+
// Calculates the LUB of this element with some other element and sets
89+
// this element to the LUB in place. Returns true if this element before
90+
// this method call was different than the LUB.
91+
bool join(Element& self, const Element& other) const noexcept;
9292
};
9393

9494
// A layer of abstraction over FiniteIntPowersetLattice which maps
@@ -149,6 +149,10 @@ template<typename T> class FinitePowersetLattice {
149149
}
150150

151151
Element getBottom() const noexcept { return intLattice.getBottom(); }
152+
153+
bool join(Element& self, const Element& other) const noexcept {
154+
return intLattice.join(self, other);
155+
}
152156
};
153157

154158
} // namespace wasm::analysis

src/analysis/lattices/stack.h

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -127,51 +127,6 @@ template<Lattice L> class StackLattice {
127127
return value;
128128
}
129129

130-
// When taking the LUB, we take the LUBs of the elements of each stack
131-
// starting from the top of the stack. So, LUB([b, a], [b', a']) is
132-
// [LUB(b, b'), LUB(a, a')]. If one stack is higher than the other,
133-
// the bottom of the higher stack will be kept, while the LUB of the
134-
// corresponding tops of each stack will be taken. For instance,
135-
// LUB([d, c, b, a], [b', a']) is [d, c, LUB(b, b'), LUB(a, a')].
136-
//
137-
// We start at the top because it makes taking the LUB of stacks with
138-
// different scope easier, as mentioned at the top of the file. It also
139-
// fits with the conception of the stack starting at the top and having
140-
// an infinite bottom, which allows stacks of different height and scope
141-
// to be easily joined.
142-
bool makeLeastUpperBound(const Element& other) noexcept {
143-
// Top element cases, since top elements don't actually have the stack
144-
// value.
145-
if (isTop()) {
146-
return false;
147-
} else if (other.isTop()) {
148-
stackValue.reset();
149-
return true;
150-
}
151-
152-
bool modified = false;
153-
154-
// Merge the shorter height stack with the top of the longer height
155-
// stack. We do this by taking the LUB of each pair of matching elements
156-
// from both stacks.
157-
auto otherIt = other.stackValue->crbegin();
158-
auto thisIt = stackValue->rbegin();
159-
for (;
160-
thisIt != stackValue->rend() && otherIt != other.stackValue->crend();
161-
++thisIt, ++otherIt) {
162-
modified |= thisIt->makeLeastUpperBound(*otherIt);
163-
}
164-
165-
// If the other stack is higher, append the bottom of it to our current
166-
// stack.
167-
for (; otherIt != other.stackValue->crend(); ++otherIt) {
168-
stackValue->push_front(*otherIt);
169-
modified = true;
170-
}
171-
172-
return modified;
173-
}
174-
175130
void print(std::ostream& os) {
176131
if (isTop()) {
177132
os << "TOP STACK" << std::endl;
@@ -188,6 +143,8 @@ template<Lattice L> class StackLattice {
188143
friend StackLattice;
189144
};
190145

146+
Element getBottom() const noexcept { return Element{}; }
147+
191148
// Like in computing the LUB, we compare the tops of the two stacks, as it
192149
// handles the case of stacks of different scopes. Comparisons are done by
193150
// element starting from the top.
@@ -258,7 +215,50 @@ template<Lattice L> class StackLattice {
258215
}
259216
}
260217

261-
Element getBottom() const noexcept { return Element{}; }
218+
// When taking the LUB, we take the LUBs of the elements of each stack
219+
// starting from the top of the stack. So, LUB([b, a], [b', a']) is
220+
// [LUB(b, b'), LUB(a, a')]. If one stack is higher than the other,
221+
// the bottom of the higher stack will be kept, while the LUB of the
222+
// corresponding tops of each stack will be taken. For instance,
223+
// LUB([d, c, b, a], [b', a']) is [d, c, LUB(b, b'), LUB(a, a')].
224+
//
225+
// We start at the top because it makes taking the LUB of stacks with
226+
// different scope easier, as mentioned at the top of the file. It also
227+
// fits with the conception of the stack starting at the top and having
228+
// an infinite bottom, which allows stacks of different height and scope
229+
// to be easily joined.
230+
bool join(Element& self, const Element& other) const noexcept {
231+
// Top element cases, since top elements don't actually have the stack
232+
// value.
233+
if (self.isTop()) {
234+
return false;
235+
} else if (other.isTop()) {
236+
self.stackValue.reset();
237+
return true;
238+
}
239+
240+
bool modified = false;
241+
242+
// Merge the shorter height stack with the top of the longer height
243+
// stack. We do this by taking the LUB of each pair of matching elements
244+
// from both stacks.
245+
auto selfIt = self.stackValue->rbegin();
246+
auto otherIt = other.stackValue->crbegin();
247+
for (; selfIt != self.stackValue->rend() &&
248+
otherIt != other.stackValue->crend();
249+
++selfIt, ++otherIt) {
250+
modified |= lattice.join(*selfIt, *otherIt);
251+
}
252+
253+
// If the other stack is higher, append the bottom of it to our current
254+
// stack.
255+
for (; otherIt != other.stackValue->crend(); ++otherIt) {
256+
self.stackValue->push_front(*otherIt);
257+
modified = true;
258+
}
259+
260+
return modified;
261+
}
262262
};
263263

264264
} // namespace wasm::analysis

src/analysis/monotone-analyzer-impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ inline void MonotoneCFGAnalyzer<L, TxFn>::evaluate() {
4545
for (auto& dep : txfn.getDependents(cfg[i])) {
4646
// If the input state for the dependent block changes, we need to
4747
// re-analyze it.
48-
if (states[dep.getIndex()].makeLeastUpperBound(state)) {
48+
if (lattice.join(states[dep.getIndex()], state)) {
4949
worklist.push(dep.getIndex());
5050
}
5151
}

test/gtest/cfg.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ TEST_F(CFGTest, FinitePowersetLatticeFunctioning) {
164164
element2.print(ss);
165165
EXPECT_EQ(ss.str(), "100101");
166166
ss.str(std::string());
167-
element2.makeLeastUpperBound(element1);
167+
lattice.join(element2, element1);
168168
element2.print(ss);
169169
EXPECT_EQ(ss.str(), "101101");
170170
}
@@ -496,7 +496,7 @@ TEST_F(CFGTest, StackLatticeFunctioning) {
496496
EXPECT_EQ(stackLattice.compare(thirdStack, expectedStack),
497497
LatticeComparison::EQUAL);
498498

499-
EXPECT_EQ(thirdStack.makeLeastUpperBound(secondStack), true);
499+
EXPECT_TRUE(stackLattice.join(thirdStack, secondStack));
500500

501501
{
502502
expectedStack.stackTop().set(0, true);

0 commit comments

Comments
 (0)