1+ /*
2+ * Copyright 2025 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+
17+ #include " analysis/lattice.h"
18+ #include " wasm-type.h"
19+
20+ #ifndef wasm_analysis_lattices_conetype_h
21+ #define wasm_analysis_lattices_conetype_h
22+
23+ namespace wasm ::analysis {
24+
25+ // The value type lattice augmented with subtyping depths on reference types. An
26+ // element {(ref $foo), 1}, for example, represents the set of values that are
27+ // exactly $foo or exactly one of $foo's immediate subtypes, but not any deeper
28+ // type. Non-reference types and bottom references types always have a depth of
29+ // 0.
30+ struct ConeType {
31+ struct Element {
32+ Type type;
33+ Index depth;
34+ bool operator ==(const Element& other) const {
35+ return type == other.type && depth == other.depth ;
36+ }
37+ bool operator !=(const Element& other) const { return !(*this == other); }
38+ bool isBottom () const { return type == Type::unreachable; }
39+ bool isTop () const { return type == Type::none; }
40+ };
41+
42+ // Used only for initializing depths for new elements.
43+ const std::unordered_map<HeapType, Index> typeDepths;
44+
45+ ConeType (std::unordered_map<HeapType, Index>&& typeDepths)
46+ : typeDepths(std::move(typeDepths)) {}
47+
48+ Element get (Type type) const noexcept {
49+ assert (!type.isTuple ());
50+ if (!type.isRef () || type.isExact () || type.isNull () ||
51+ type.getHeapType ().isMaybeShared (HeapType::i31)) {
52+ return Element{type, 0 };
53+ }
54+ auto it = typeDepths.find (type.getHeapType ());
55+ assert (it != typeDepths.end ());
56+ return Element{type, it->second };
57+ }
58+
59+ Element getBottom () const noexcept { return Element{Type::unreachable, 0 }; }
60+
61+ Element getTop () const noexcept { return Element{Type::none, 0 }; }
62+
63+ bool join (Element& joinee, const Element& joiner) const noexcept {
64+ auto lub = Type::getLeastUpperBound (joinee.type , joiner.type );
65+ bool changed = lub != joinee.type ;
66+ if (!lub.isRef ()) {
67+ joinee.type = lub;
68+ joinee.depth = 0 ;
69+ return changed;
70+ }
71+ Index joineeToLub = 0 , joinerToLub = 0 ;
72+ if (!joinee.isBottom () && !joinee.type .isNull ()) {
73+ joineeToLub = depthToSuper (joinee, lub);
74+ }
75+ if (!joiner.isBottom () && !joiner.type .isNull ()) {
76+ joinerToLub = depthToSuper (joiner, lub);
77+ }
78+ Index newDepth =
79+ std::max (joinee.depth + joineeToLub, joiner.depth + joinerToLub);
80+ changed = changed || newDepth != joinee.depth ;
81+ joinee.type = lub;
82+ joinee.depth = newDepth;
83+ return changed;
84+ }
85+
86+ bool meet (Element& meetee, const Element& meeter) const noexcept {
87+ // Type::none does not behave like the top type in
88+ // Type::getGreatestLowerBound, so handle it separately first. Also handle
89+ // unreachables so we don't have to worry about them later.
90+ if (meetee.isBottom () || meeter.isTop ()) {
91+ return false ;
92+ }
93+ if (meetee.isTop () || meeter.isBottom ()) {
94+ meetee = meeter;
95+ return true ;
96+ }
97+ if (meetee.type == meeter.type ) {
98+ auto newDepth = std::min (meetee.depth , meeter.depth );
99+ bool changed = newDepth != meetee.depth ;
100+ meetee.depth = newDepth;
101+ return changed;
102+ }
103+ Index newDepth;
104+ auto glb = Type::getGreatestLowerBound (meetee.type , meeter.type );
105+ if (glb == Type::unreachable || glb.isNull ()) {
106+ newDepth = 0 ;
107+ } else if (HeapType::isSubType (meetee.type .getHeapType (),
108+ meeter.type .getHeapType ())) {
109+ auto diff = depthToSuper (meetee, meeter.type );
110+ if (meeter.depth < diff) {
111+ glb = glb.with (glb.getHeapType ().getBottom ());
112+ newDepth = 0 ;
113+ } else {
114+ newDepth = std::min (meeter.depth - diff, meetee.depth );
115+ }
116+ } else if (HeapType::isSubType (meeter.type .getHeapType (),
117+ meetee.type .getHeapType ())) {
118+ auto diff = depthToSuper (meeter, meetee.type );
119+ if (meetee.depth < diff) {
120+ glb = glb.with (glb.getHeapType ().getBottom ());
121+ newDepth = 0 ;
122+ } else {
123+ newDepth = std::min (meetee.depth - diff, meeter.depth );
124+ }
125+ } else {
126+ WASM_UNREACHABLE (" unexpected case" );
127+ }
128+ bool changed = glb != meetee.type || newDepth != meetee.depth ;
129+ meetee.type = glb;
130+ meetee.depth = newDepth;
131+ return changed;
132+ }
133+
134+ analysis::LatticeComparison compare (const Element& a,
135+ const Element& b) const noexcept {
136+ if (a == b) {
137+ return analysis::EQUAL;
138+ }
139+ if (a.isBottom () || b.isTop ()) {
140+ return analysis::LESS;
141+ }
142+ if (a.isTop () || b.isBottom ()) {
143+ return analysis::GREATER;
144+ }
145+ if (a.type == b.type ) {
146+ return a.depth < b.depth ? analysis::LESS : analysis::GREATER;
147+ }
148+ if (Type::isSubType (a.type , b.type )) {
149+ if (a.type .isNull ()) {
150+ return analysis::LESS;
151+ }
152+ Index diff = depthToSuper (a, b.type );
153+ return a.depth + diff <= b.depth ? analysis::LESS : analysis::NO_RELATION;
154+ }
155+ if (Type::isSubType (b.type , a.type )) {
156+ if (b.type .isNull ()) {
157+ return analysis::GREATER;
158+ }
159+ Index diff = depthToSuper (b, a.type );
160+ return b.depth + diff <= a.depth ? analysis::GREATER
161+ : analysis::NO_RELATION;
162+ }
163+ return analysis::NO_RELATION;
164+ }
165+
166+ private:
167+ Index depthToSuper (const Element& e, Type super) const noexcept {
168+ Index depth = 0 ;
169+ for (HeapType type = e.type .getHeapType (); type != super.getHeapType ();
170+ type = *type.getSuperType ()) {
171+ ++depth;
172+ }
173+ return depth;
174+ }
175+ };
176+
177+ #if __cplusplus >= 202002L
178+ static_assert (Lattice<ConeType>);
179+ static_assert (FullLattice<ConeType>);
180+ #endif
181+
182+ } // namespace wasm::analysis
183+
184+ #endif // wasm_analysis_lattices_conetype_h
0 commit comments