Skip to content

Commit 1f27e2f

Browse files
authored
Use exactness to optimize GUFA cone depth (#7582)
Exact references are known to point to exactly their heap type and not one of its subtypes. GUFA already analyzed exactness via the more general "cone" types that include an allowed subtyping depth. Exact types are equivalent to cone types of depth zero. Let GUFA take advantage of exactness information by normalizing cone depths to 0 whenever the cone type is exact.
1 parent 677e0c6 commit 1f27e2f

File tree

5 files changed

+222
-160
lines changed

5 files changed

+222
-160
lines changed

src/ir/possible-contents.cpp

Lines changed: 35 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ PossibleContents PossibleContents::combine(const PossibleContents& a,
144144

145145
void PossibleContents::intersect(const PossibleContents& other) {
146146
// This does not yet handle all possible content.
147-
assert(other.isFullConeType() || other.isLiteral() || other.isNone());
147+
assert((other.isConeType() &&
148+
(other.getType().isExact() || other.hasFullCone())) ||
149+
other.isLiteral() || other.isNone());
148150

149151
if (*this == other) {
150152
// Nothing changes.
@@ -201,6 +203,7 @@ void PossibleContents::intersect(const PossibleContents& other) {
201203
// The heap types are compatible, so intersect the cones.
202204
auto depthFromRoot = heapType.getDepth();
203205
auto otherDepthFromRoot = otherHeapType.getDepth();
206+
auto newDepthFromRoot = newType.getHeapType().getDepth();
204207

205208
// Note the global's information, if we started as a global. In that case, the
206209
// code below will refine our type but we can remain a global, which we will
@@ -210,38 +213,21 @@ void PossibleContents::intersect(const PossibleContents& other) {
210213
globalName = getGlobal();
211214
}
212215

213-
// By assumption |other| has full depth. Consider the other cone in |this|.
214-
if (hasFullCone()) {
216+
if (hasFullCone() && other.hasFullCone()) {
215217
// Both are full cones, so the result is as well.
216-
value = FullConeType(newType);
218+
value = DefaultConeType(newType);
217219
} else {
218-
// The result is a partial cone. If the cone starts in |otherHeapType| then
219-
// we need to adjust the depth down, since it will be smaller than the
220-
// original cone:
221-
/*
222-
// ..
223-
// /
224-
// otherHeapType
225-
// / \
226-
// heapType ..
227-
// \
228-
*/
229-
// E.g. if |this| is a cone of depth 10, and |otherHeapType| is an immediate
230-
// subtype of |this|, then the new cone must be of depth 9.
231-
auto newDepth = getCone().depth;
232-
if (newType.getHeapType() == otherHeapType) {
233-
assert(depthFromRoot <= otherDepthFromRoot);
234-
auto reduction = otherDepthFromRoot - depthFromRoot;
235-
if (reduction > newDepth) {
236-
// The cone on heapType does not even reach the cone on otherHeapType,
237-
// so the result is not a cone.
238-
setNoneOrNull();
239-
return;
240-
}
241-
newDepth -= reduction;
220+
// The result is a partial cone. Check whether the cones overlap, and if
221+
// they do, find the new depth.
222+
if (newDepthFromRoot - depthFromRoot > getCone().depth ||
223+
newDepthFromRoot - otherDepthFromRoot > other.getCone().depth) {
224+
setNoneOrNull();
225+
return;
242226
}
243-
244-
value = ConeType{newType, newDepth};
227+
Index newDepth = getCone().depth - (newDepthFromRoot - depthFromRoot);
228+
Index otherNewDepth =
229+
other.getCone().depth - (newDepthFromRoot - otherDepthFromRoot);
230+
value = ConeType{newType, std::min(newDepth, otherNewDepth)};
245231
}
246232

247233
if (globalName) {
@@ -371,7 +357,19 @@ bool PossibleContents::isSubContents(const PossibleContents& a,
371357
return false;
372358
}
373359

374-
WASM_UNREACHABLE("unhandled case of isSubContents");
360+
if (b.isGlobal()) {
361+
// We've already ruled out anything but another global or non-full cone type
362+
// for a.
363+
return false;
364+
}
365+
366+
assert(b.isConeType() && (a.isConeType() || a.isGlobal()));
367+
if (!Type::isSubType(a.getType(), b.getType())) {
368+
return false;
369+
}
370+
// Check that a's cone type is enclosed in b's cone type.
371+
return a.getType().getHeapType().getDepth() + a.getCone().depth <=
372+
b.getType().getHeapType().getDepth() + b.getCone().depth;
375373
}
376374

377375
namespace {
@@ -1463,7 +1461,7 @@ class TNHOracle : public ModuleUtils::ParallelFunctionAnalysis<TNHInfo> {
14631461

14641462
// Get the type we inferred was possible at a location.
14651463
PossibleContents getContents(Expression* curr) {
1466-
auto naiveContents = PossibleContents::fullConeType(curr->type);
1464+
auto naiveContents = PossibleContents::coneType(curr->type);
14671465

14681466
// If we inferred nothing, use the naive type.
14691467
auto iter = inferences.find(curr);
@@ -1871,8 +1869,8 @@ void TNHOracle::optimizeCallCasts(Expression* call,
18711869
// There are two constraints on this location: any value there must
18721870
// be of the declared type (curr->type) and also the cast type, so
18731871
// we know only their intersection can appear here.
1874-
auto declared = PossibleContents::fullConeType(curr->type);
1875-
auto intersection = PossibleContents::fullConeType(castType);
1872+
auto declared = PossibleContents::coneType(curr->type);
1873+
auto intersection = PossibleContents::coneType(castType);
18761874
intersection.intersect(declared);
18771875
if (intersection.isConeType()) {
18781876
auto intersectionType = intersection.getType();
@@ -1956,7 +1954,7 @@ struct Flower {
19561954
PossibleContents getTNHContents(Expression* curr) {
19571955
if (!tnhOracle) {
19581956
// No oracle; just use the type in the IR.
1959-
return PossibleContents::fullConeType(curr->type);
1957+
return PossibleContents::coneType(curr->type);
19601958
}
19611959
return tnhOracle->getContents(curr);
19621960
}
@@ -2858,7 +2856,7 @@ void Flower::readFromData(Type declaredType,
28582856
#ifndef NDEBUG
28592857
// We must not have anything in the reference that is invalid for the wasm
28602858
// type there.
2861-
auto maximalContents = PossibleContents::fullConeType(declaredType);
2859+
auto maximalContents = PossibleContents::coneType(declaredType);
28622860
assert(PossibleContents::isSubContents(refContents, maximalContents));
28632861
#endif
28642862

@@ -2953,7 +2951,7 @@ void Flower::writeToData(Expression* ref, Expression* value, Index fieldIndex) {
29532951
#ifndef NDEBUG
29542952
// We must not have anything in the reference that is invalid for the wasm
29552953
// type there.
2956-
auto maximalContents = PossibleContents::fullConeType(ref->type);
2954+
auto maximalContents = PossibleContents::coneType(ref->type);
29572955
assert(PossibleContents::isSubContents(refContents, maximalContents));
29582956
#endif
29592957

src/ir/possible-contents.h

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,12 @@ class PossibleContents {
126126

127127
static constexpr Index FullDepth = -1;
128128

129-
// Internal convenience for creating a cone type of unbounded depth, i.e., the
130-
// full cone of all subtypes for that type.
131-
static ConeType FullConeType(Type type) { return ConeType{type, FullDepth}; }
129+
// Internal convenience for creating a cone type carrying no information
130+
// besides the type. For exact references, the depth is 0 and for all other
131+
// types the depth is unbounded and includes all possible subtypes.
132+
static ConeType DefaultConeType(Type type) {
133+
return type.isExact() ? ExactType(type) : ConeType{type, FullDepth};
134+
}
132135

133136
template<typename T> PossibleContents(T value) : value(value) {}
134137

@@ -148,10 +151,11 @@ class PossibleContents {
148151
static PossibleContents exactType(Type type) {
149152
return PossibleContents{ExactType(type)};
150153
}
151-
// Helper for a cone with unbounded depth, i.e., the full cone of all subtypes
152-
// for that type.
153-
static PossibleContents fullConeType(Type type) {
154-
return PossibleContents{FullConeType(type)};
154+
// Helper for a cone with default depth for the given type. For exact
155+
// references this is depth 0 and for all other this tyis is unbounded depth
156+
// and includes all possible subtypes.
157+
static PossibleContents coneType(Type type) {
158+
return PossibleContents{DefaultConeType(type)};
155159
}
156160
static PossibleContents coneType(Type type, Index depth) {
157161
return PossibleContents{ConeType{type, depth}};
@@ -165,7 +169,7 @@ class PossibleContents {
165169

166170
if (type.isRef()) {
167171
// For a reference, subtyping matters.
168-
return fullConeType(type);
172+
return coneType(type);
169173
}
170174

171175
if (type == Type::unreachable) {
@@ -251,7 +255,7 @@ class PossibleContents {
251255
if (auto* literal = std::get_if<Literal>(&value)) {
252256
return ExactType(literal->type);
253257
} else if (auto* global = std::get_if<GlobalInfo>(&value)) {
254-
return FullConeType(global->type);
258+
return DefaultConeType(global->type);
255259
} else if (auto* coneType = std::get_if<ConeType>(&value)) {
256260
return *coneType;
257261
} else if (std::get_if<None>(&value)) {
@@ -333,7 +337,7 @@ class PossibleContents {
333337
// the separate items in the tuple (tuples themselves have no subtyping,
334338
// so the tuple's depth must be 0, i.e., an exact type).
335339
assert(cone->depth == 0);
336-
return fullConeType(type[i]);
340+
return coneType(type[i]);
337341
} else {
338342
WASM_UNREACHABLE("not a tuple");
339343
}

src/passes/GUFA.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ struct GUFAOptimizer
246246
// We have some knowledge of the type here. Use that to optimize: RefTest
247247
// returns 1 if the input is of a subtype of the intended type, that is,
248248
// we are looking for a type in that cone of types.
249-
auto intendedContents = PossibleContents::fullConeType(curr->castType);
249+
auto intendedContents = PossibleContents::coneType(curr->castType);
250250

251251
auto optimize = [&](int32_t result) {
252252
auto* last = Builder(*getModule()).makeConst(Literal(int32_t(result)));

0 commit comments

Comments
 (0)