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