Skip to content

Commit 94f830e

Browse files
tlivelyradekdoulik
authored andcommitted
[analysis][NFC] Move the stack lattice to analysis/lattices (WebAssembly#6030)
Also shorten various names in the implementation to improve readability.
1 parent 57a1ed1 commit 94f830e

File tree

3 files changed

+74
-62
lines changed

3 files changed

+74
-62
lines changed

src/analysis/stack-lattice.h renamed to src/analysis/lattices/stack.h

Lines changed: 72 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
1-
#ifndef wasm_analysis_stack_lattice_h
2-
#define wasm_analysis_stack_lattice_h
1+
/*
2+
* Copyright 2023 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
#ifndef wasm_analysis_lattices_stack_h
17+
#define wasm_analysis_lattices_stack_h
318

419
#include <deque>
20+
#include <iostream>
521
#include <optional>
622

7-
#include "lattice.h"
23+
#include "../lattice.h"
824

925
namespace wasm::analysis {
1026

1127
// Note that in comments, bottom is left and top is right.
1228

13-
// This lattice models the behavior of a stack of values. The lattice
14-
// is templated on StackElementLattice, which is a lattice which can
15-
// model some abstract property of a value on the stack. The StackLattice
16-
// itself can push or pop abstract values and access the top of stack.
29+
// This lattice models the behavior of a stack of values. The lattice is
30+
// templated on L, which is a lattice which can model some abstract property of
31+
// a value on the stack. The StackLattice itself can push or pop abstract values
32+
// and access the top of stack.
1733
//
1834
// The goal here is not to operate directly on the stacks. Rather, the
19-
// StackLattice organizes the StackElementLattice elements in an efficient
20-
// and natural way which reflects the behavior of the wasm value stack.
21-
// Transfer functions will operate on stack elements individually. The
22-
// stack itself is an intermediate structure.
35+
// StackLattice organizes the L elements in an efficient and natural way which
36+
// reflects the behavior of the wasm value stack. Transfer functions will
37+
// operate on stack elements individually. The stack itself is an intermediate
38+
// structure.
2339
//
24-
// Comparisons are done elementwise, starting from the top of the stack.
25-
// For instance, to compare the stacks [c,b,a], [b',a'], we first compare
26-
// a with a', then b with b'. Then we make note of the fact that the first
27-
// stack is higher, with an extra c element at the bottom.
40+
// Comparisons are done elementwise, starting from the top of the stack. For
41+
// instance, to compare the stacks [c,b,a], [b',a'], we first compare a with a',
42+
// then b with b'. Then we make note of the fact that the first stack is higher,
43+
// with an extra c element at the bottom.
2844
//
29-
// Similarly, Least Upper Bounds are done elementwise starting from the top.
30-
// For instance LUB([b, a], [b', a']) = [LUB(b, b'), LUB(a, a')], while
31-
// LUB([c, b, a], [b', a']) = [c, LUB(b, b'), LUB(a, a')].
45+
// Similarly, least upper bounds are done elementwise starting from the top. For
46+
// instance LUB([b, a], [b', a']) = [LUB(b, b'), LUB(a, a')], while LUB([c, b,
47+
// a], [b', a']) = [c, LUB(b, b'), LUB(a, a')].
3248
//
3349
// These are done from the top of the stack because this addresses the problem
3450
// of scopes. For instance, if we have the following program
@@ -43,41 +59,40 @@ namespace wasm::analysis {
4359
// i32.add
4460
//
4561
// Before the if-else control flow, we have [] -> [i32], and after the if-else
46-
// control flow we have [i32, i32] -> [i32]. However, inside each of the if
47-
// and else conditions, we have [] -> [i32], because they cannot see the
48-
// stack elements pushed by the enclosing scope. In effect in the if and else,
49-
// we have a stack [i32 | i32], where we can't "see" left of the |.
62+
// control flow we have [i32, i32] -> [i32]. However, inside each of the if and
63+
// else conditions, we have [] -> [i32], because they cannot see the stack
64+
// elements pushed by the enclosing scope. In effect in the if and else, we have
65+
// a stack [i32 | i32], where we can't "see" left of the |.
5066
//
5167
// Conceptually, we can also imagine each stack [b, a] as being implicitly an
5268
// infinite stack of the form (bottom) [... BOTTOM, BOTTOM, b, a] (top). This
5369
// makes stacks in different scopes comparable, with only their contents
5470
// different. Stacks in more "inner" scopes simply have more bottom elements in
5571
// the bottom portion.
5672
//
57-
// A common application for this lattice is modeling the Wasm value stack.
58-
// For instance, one could use this to analyze the maximum bit size of
59-
// values on the Wasm value stack. When new actual values are pushed
60-
// or popped off the Wasm value stack by instructions, the same is done
61-
// to abstract lattice elements in the StackLattice.
73+
// A common application for this lattice is modeling the Wasm value stack. For
74+
// instance, one could use this to analyze the maximum bit size of values on the
75+
// Wasm value stack. When new actual values are pushed or popped off the Wasm
76+
// value stack by instructions, the same is done to abstract lattice elements in
77+
// the StackLattice.
6278
//
63-
// When two control flows are joined together, one with stack [b, a] and
64-
// another with stack [b, a'], we can take the least upper bound to
65-
// produce a stack [b, LUB(a, a')], where LUB(a, a') takes the maximum
66-
// of the two maximum bit values.
79+
// When two control flows are joined together, one with stack [b, a] and another
80+
// with stack [b, a'], we can take the least upper bound to produce a stack [b,
81+
// LUB(a, a')], where LUB(a, a') takes the maximum of the two maximum bit
82+
// values.
6783

68-
template<Lattice StackElementLattice> class StackLattice {
69-
StackElementLattice& stackElementLattice;
84+
template<Lattice L> class StackLattice {
85+
L& lattice;
7086

7187
public:
72-
StackLattice(StackElementLattice& stackElementLattice)
73-
: stackElementLattice(stackElementLattice) {}
88+
StackLattice(L& lattice) : lattice(lattice) {}
7489

7590
class Element {
7691
// The top lattice can be imagined as an infinitely high stack of top
7792
// elements, which is unreachable in most cases. In practice, we make the
7893
// stack an optional, and we represent top with the absence of a stack.
79-
std::optional<std::deque<typename StackElementLattice::Element>>
80-
stackValue = std::deque<typename StackElementLattice::Element>();
94+
std::optional<std::deque<typename L::Element>> stackValue =
95+
std::deque<typename L::Element>();
8196

8297
public:
8398
bool isTop() const { return !stackValue.has_value(); }
@@ -86,11 +101,9 @@ template<Lattice StackElementLattice> class StackLattice {
86101
}
87102
void setToTop() { stackValue.reset(); }
88103

89-
typename StackElementLattice::Element& stackTop() {
90-
return stackValue->back();
91-
}
104+
typename L::Element& stackTop() { return stackValue->back(); }
92105

93-
void push(typename StackElementLattice::Element&& element) {
106+
void push(typename L::Element&& element) {
94107
// We can imagine each stack [b, a] as being implicitly an infinite stack
95108
// of the form (bottom) [... BOTTOM, BOTTOM, b, a] (top). In that case,
96109
// adding a bottom element to an empty stack changes nothing, so we don't
@@ -101,15 +114,15 @@ template<Lattice StackElementLattice> class StackLattice {
101114
}
102115
}
103116

104-
void push(const typename StackElementLattice::Element& element) {
117+
void push(const typename L::Element& element) {
105118
if (stackValue.has_value() &&
106119
(!stackValue->empty() || !element.isBottom())) {
107120
stackValue->push_back(std::move(element));
108121
}
109122
}
110123

111-
typename StackElementLattice::Element pop() {
112-
typename StackElementLattice::Element value = stackValue->back();
124+
typename L::Element pop() {
125+
typename L::Element value = stackValue->back();
113126
stackValue->pop_back();
114127
return value;
115128
}
@@ -141,18 +154,18 @@ template<Lattice StackElementLattice> class StackLattice {
141154
// Merge the shorter height stack with the top of the longer height
142155
// stack. We do this by taking the LUB of each pair of matching elements
143156
// from both stacks.
144-
auto otherIterator = other.stackValue->crbegin();
145-
auto thisIterator = stackValue->rbegin();
146-
for (; thisIterator != stackValue->rend() &&
147-
otherIterator != other.stackValue->crend();
148-
++thisIterator, ++otherIterator) {
149-
modified |= thisIterator->makeLeastUpperBound(*otherIterator);
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);
150163
}
151164

152165
// If the other stack is higher, append the bottom of it to our current
153166
// stack.
154-
for (; otherIterator != other.stackValue->crend(); ++otherIterator) {
155-
stackValue->push_front(*otherIterator);
167+
for (; otherIt != other.stackValue->crend(); ++otherIt) {
168+
stackValue->push_front(*otherIt);
156169
modified = true;
157170
}
158171

@@ -203,13 +216,12 @@ template<Lattice StackElementLattice> class StackLattice {
203216
// Check the tops of both stacks which match (i.e. are within the heights
204217
// of both stacks). If there is a pair which is not related, the stacks
205218
// cannot be related.
206-
for (auto leftIterator = left.stackValue->crbegin(),
207-
rightIterator = right.stackValue->crbegin();
208-
leftIterator != left.stackValue->crend() &&
209-
rightIterator != right.stackValue->crend();
210-
++leftIterator, ++rightIterator) {
211-
LatticeComparison currComparison =
212-
stackElementLattice.compare(*leftIterator, *rightIterator);
219+
for (auto leftIt = left.stackValue->crbegin(),
220+
rightIt = right.stackValue->crbegin();
221+
leftIt != left.stackValue->crend() &&
222+
rightIt != right.stackValue->crend();
223+
++leftIt, ++rightIt) {
224+
LatticeComparison currComparison = lattice.compare(*leftIt, *rightIt);
213225
switch (currComparison) {
214226
case LatticeComparison::NO_RELATION:
215227
return LatticeComparison::NO_RELATION;
@@ -251,4 +263,4 @@ template<Lattice StackElementLattice> class StackLattice {
251263

252264
} // namespace wasm::analysis
253265

254-
#endif // wasm_analysis_stack_lattice_h
266+
#endif // wasm_analysis_lattices_stack_h

src/tools/wasm-fuzz-lattices.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@
1919
#include <string>
2020

2121
#include "analysis/lattice.h"
22+
#include "analysis/lattices/stack.h"
2223
#include "analysis/liveness-transfer-function.h"
2324
#include "analysis/reaching-definitions-transfer-function.h"
24-
#include "analysis/stack-lattice.h"
2525

2626
#include "support/command-line.h"
2727
#include "tools/fuzzing.h"

test/gtest/cfg.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
#include "analysis/cfg.h"
44
#include "analysis/lattice.h"
5+
#include "analysis/lattices/stack.h"
56
#include "analysis/liveness-transfer-function.h"
67
#include "analysis/monotone-analyzer.h"
78
#include "analysis/reaching-definitions-transfer-function.h"
8-
#include "analysis/stack-lattice.h"
99
#include "ir/find_all.h"
1010
#include "print-test.h"
1111
#include "wasm.h"

0 commit comments

Comments
 (0)