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
925namespace 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
7187public:
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
0 commit comments