-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathIREquality.h
116 lines (96 loc) · 3.21 KB
/
IREquality.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#ifndef HALIDE_IR_EQUALITY_H
#define HALIDE_IR_EQUALITY_H
/** \file
* Methods to test Exprs and Stmts for equality of value
*/
#include "IR.h"
namespace Halide {
namespace Internal {
/** A compare struct suitable for use in std::map and std::set that
* computes a lexical ordering on IR nodes. */
struct IRDeepCompare {
EXPORT bool operator()(const Expr &a, const Expr &b) const;
EXPORT bool operator()(const Stmt &a, const Stmt &b) const;
};
/** Lossily track known equal exprs with a cache. On collision, the
* old pair is evicted. Used below by ExprWithCompareCache. */
class IRCompareCache {
private:
struct Entry {
Expr a, b;
};
int bits;
uint32_t hash(const Expr &a, const Expr &b) const {
// Note this hash is symmetric in a and b, so that a
// comparison in a and b hashes to the same bucket as
// a comparison on b and a.
uint64_t pa = (uint64_t)(a.ptr);
uint64_t pb = (uint64_t)(b.ptr);
pa ^= pb;
pa ^= pa >> bits;
pa ^= pa >> (bits*2);
return pa & ((1 << bits) - 1);
}
std::vector<Entry> entries;
public:
void insert(const Expr &a, const Expr &b) {
uint32_t h = hash(a, b);
entries[h].a = a;
entries[h].b = b;
}
bool contains(const Expr &a, const Expr &b) const {
uint32_t h = hash(a, b);
const Entry &e = entries[h];
return ((a.same_as(e.a) && b.same_as(e.b)) ||
(a.same_as(e.b) && b.same_as(e.a)));
}
void clear() {
for (size_t i = 0; i < entries.size(); i++) {
entries[i].a = Expr();
entries[i].b = Expr();
}
}
IRCompareCache() {}
IRCompareCache(int b) : bits(b), entries(1 << bits) {}
};
/** A wrapper about Exprs so that they can be deeply compared with a
* cache for known-equal subexpressions. Useful for unsanitized Exprs
* coming in from the front-end, which may be horrible graphs with
* sub-expressions that are equal by value but not by identity. This
* isn't a comparison object like IRDeepCompare above, because libc++
* requires that comparison objects be stateless (and constructs a new
* one for each comparison!), so they can't have a cache associated
* with them. However, by sneakily making the cache a mutable member
* of the objects being compared, we can dodge this issue.
*
* Clunky example usage:
*
\code
Expr a, b, c, query;
std::set<ExprWithCompareCache> s;
IRCompareCache cache(8);
s.insert(ExprWithCompareCache(a, &cache));
s.insert(ExprWithCompareCache(b, &cache));
s.insert(ExprWithCompareCache(c, &cache));
if (m.contains(ExprWithCompareCache(query, &cache))) {...}
\endcode
*
*/
struct ExprWithCompareCache {
Expr expr;
mutable IRCompareCache *cache;
ExprWithCompareCache() : cache(NULL) {}
ExprWithCompareCache(const Expr &e, IRCompareCache *c) : expr(e), cache(c) {}
/** The comparison uses (and updates) the cache */
EXPORT bool operator<(const ExprWithCompareCache &other) const;
};
/** Compare IR nodes for equality of value. Traverses entire IR
* tree. For equality of reference, use Expr::same_as */
// @{
EXPORT bool equal(Expr a, Expr b);
EXPORT bool equal(Stmt a, Stmt b);
// @}
EXPORT void ir_equality_test();
}
}
#endif