xref: /linux-6.15/kernel/bpf/tnum.c (revision b03c9f9f)
1f1174f77SEdward Cree /* tnum: tracked (or tristate) numbers
2f1174f77SEdward Cree  *
3f1174f77SEdward Cree  * A tnum tracks knowledge about the bits of a value.  Each bit can be either
4f1174f77SEdward Cree  * known (0 or 1), or unknown (x).  Arithmetic operations on tnums will
5f1174f77SEdward Cree  * propagate the unknown bits such that the tnum result represents all the
6f1174f77SEdward Cree  * possible results for possible values of the operands.
7f1174f77SEdward Cree  */
8f1174f77SEdward Cree #include <linux/kernel.h>
9f1174f77SEdward Cree #include <linux/tnum.h>
10f1174f77SEdward Cree 
11f1174f77SEdward Cree #define TNUM(_v, _m)	(struct tnum){.value = _v, .mask = _m}
12f1174f77SEdward Cree /* A completely unknown value */
13f1174f77SEdward Cree const struct tnum tnum_unknown = { .value = 0, .mask = -1 };
14f1174f77SEdward Cree 
15f1174f77SEdward Cree struct tnum tnum_const(u64 value)
16f1174f77SEdward Cree {
17f1174f77SEdward Cree 	return TNUM(value, 0);
18f1174f77SEdward Cree }
19f1174f77SEdward Cree 
20*b03c9f9fSEdward Cree struct tnum tnum_range(u64 min, u64 max)
21*b03c9f9fSEdward Cree {
22*b03c9f9fSEdward Cree 	u64 chi = min ^ max, delta;
23*b03c9f9fSEdward Cree 	u8 bits = fls64(chi);
24*b03c9f9fSEdward Cree 
25*b03c9f9fSEdward Cree 	/* special case, needed because 1ULL << 64 is undefined */
26*b03c9f9fSEdward Cree 	if (bits > 63)
27*b03c9f9fSEdward Cree 		return tnum_unknown;
28*b03c9f9fSEdward Cree 	/* e.g. if chi = 4, bits = 3, delta = (1<<3) - 1 = 7.
29*b03c9f9fSEdward Cree 	 * if chi = 0, bits = 0, delta = (1<<0) - 1 = 0, so we return
30*b03c9f9fSEdward Cree 	 *  constant min (since min == max).
31*b03c9f9fSEdward Cree 	 */
32*b03c9f9fSEdward Cree 	delta = (1ULL << bits) - 1;
33*b03c9f9fSEdward Cree 	return TNUM(min & ~delta, delta);
34*b03c9f9fSEdward Cree }
35*b03c9f9fSEdward Cree 
36f1174f77SEdward Cree struct tnum tnum_lshift(struct tnum a, u8 shift)
37f1174f77SEdward Cree {
38f1174f77SEdward Cree 	return TNUM(a.value << shift, a.mask << shift);
39f1174f77SEdward Cree }
40f1174f77SEdward Cree 
41f1174f77SEdward Cree struct tnum tnum_rshift(struct tnum a, u8 shift)
42f1174f77SEdward Cree {
43f1174f77SEdward Cree 	return TNUM(a.value >> shift, a.mask >> shift);
44f1174f77SEdward Cree }
45f1174f77SEdward Cree 
46f1174f77SEdward Cree struct tnum tnum_add(struct tnum a, struct tnum b)
47f1174f77SEdward Cree {
48f1174f77SEdward Cree 	u64 sm, sv, sigma, chi, mu;
49f1174f77SEdward Cree 
50f1174f77SEdward Cree 	sm = a.mask + b.mask;
51f1174f77SEdward Cree 	sv = a.value + b.value;
52f1174f77SEdward Cree 	sigma = sm + sv;
53f1174f77SEdward Cree 	chi = sigma ^ sv;
54f1174f77SEdward Cree 	mu = chi | a.mask | b.mask;
55f1174f77SEdward Cree 	return TNUM(sv & ~mu, mu);
56f1174f77SEdward Cree }
57f1174f77SEdward Cree 
58f1174f77SEdward Cree struct tnum tnum_sub(struct tnum a, struct tnum b)
59f1174f77SEdward Cree {
60f1174f77SEdward Cree 	u64 dv, alpha, beta, chi, mu;
61f1174f77SEdward Cree 
62f1174f77SEdward Cree 	dv = a.value - b.value;
63f1174f77SEdward Cree 	alpha = dv + a.mask;
64f1174f77SEdward Cree 	beta = dv - b.mask;
65f1174f77SEdward Cree 	chi = alpha ^ beta;
66f1174f77SEdward Cree 	mu = chi | a.mask | b.mask;
67f1174f77SEdward Cree 	return TNUM(dv & ~mu, mu);
68f1174f77SEdward Cree }
69f1174f77SEdward Cree 
70f1174f77SEdward Cree struct tnum tnum_and(struct tnum a, struct tnum b)
71f1174f77SEdward Cree {
72f1174f77SEdward Cree 	u64 alpha, beta, v;
73f1174f77SEdward Cree 
74f1174f77SEdward Cree 	alpha = a.value | a.mask;
75f1174f77SEdward Cree 	beta = b.value | b.mask;
76f1174f77SEdward Cree 	v = a.value & b.value;
77f1174f77SEdward Cree 	return TNUM(v, alpha & beta & ~v);
78f1174f77SEdward Cree }
79f1174f77SEdward Cree 
80f1174f77SEdward Cree struct tnum tnum_or(struct tnum a, struct tnum b)
81f1174f77SEdward Cree {
82f1174f77SEdward Cree 	u64 v, mu;
83f1174f77SEdward Cree 
84f1174f77SEdward Cree 	v = a.value | b.value;
85f1174f77SEdward Cree 	mu = a.mask | b.mask;
86f1174f77SEdward Cree 	return TNUM(v, mu & ~v);
87f1174f77SEdward Cree }
88f1174f77SEdward Cree 
89f1174f77SEdward Cree struct tnum tnum_xor(struct tnum a, struct tnum b)
90f1174f77SEdward Cree {
91f1174f77SEdward Cree 	u64 v, mu;
92f1174f77SEdward Cree 
93f1174f77SEdward Cree 	v = a.value ^ b.value;
94f1174f77SEdward Cree 	mu = a.mask | b.mask;
95f1174f77SEdward Cree 	return TNUM(v & ~mu, mu);
96f1174f77SEdward Cree }
97f1174f77SEdward Cree 
98f1174f77SEdward Cree /* half-multiply add: acc += (unknown * mask * value).
99f1174f77SEdward Cree  * An intermediate step in the multiply algorithm.
100f1174f77SEdward Cree  */
101f1174f77SEdward Cree static struct tnum hma(struct tnum acc, u64 value, u64 mask)
102f1174f77SEdward Cree {
103f1174f77SEdward Cree 	while (mask) {
104f1174f77SEdward Cree 		if (mask & 1)
105f1174f77SEdward Cree 			acc = tnum_add(acc, TNUM(0, value));
106f1174f77SEdward Cree 		mask >>= 1;
107f1174f77SEdward Cree 		value <<= 1;
108f1174f77SEdward Cree 	}
109f1174f77SEdward Cree 	return acc;
110f1174f77SEdward Cree }
111f1174f77SEdward Cree 
112f1174f77SEdward Cree struct tnum tnum_mul(struct tnum a, struct tnum b)
113f1174f77SEdward Cree {
114f1174f77SEdward Cree 	struct tnum acc;
115f1174f77SEdward Cree 	u64 pi;
116f1174f77SEdward Cree 
117f1174f77SEdward Cree 	pi = a.value * b.value;
118f1174f77SEdward Cree 	acc = hma(TNUM(pi, 0), a.mask, b.mask | b.value);
119f1174f77SEdward Cree 	return hma(acc, b.mask, a.value);
120f1174f77SEdward Cree }
121f1174f77SEdward Cree 
122f1174f77SEdward Cree /* Note that if a and b disagree - i.e. one has a 'known 1' where the other has
123f1174f77SEdward Cree  * a 'known 0' - this will return a 'known 1' for that bit.
124f1174f77SEdward Cree  */
125f1174f77SEdward Cree struct tnum tnum_intersect(struct tnum a, struct tnum b)
126f1174f77SEdward Cree {
127f1174f77SEdward Cree 	u64 v, mu;
128f1174f77SEdward Cree 
129f1174f77SEdward Cree 	v = a.value | b.value;
130f1174f77SEdward Cree 	mu = a.mask & b.mask;
131f1174f77SEdward Cree 	return TNUM(v & ~mu, mu);
132f1174f77SEdward Cree }
133f1174f77SEdward Cree 
134f1174f77SEdward Cree struct tnum tnum_cast(struct tnum a, u8 size)
135f1174f77SEdward Cree {
136f1174f77SEdward Cree 	a.value &= (1ULL << (size * 8)) - 1;
137f1174f77SEdward Cree 	a.mask &= (1ULL << (size * 8)) - 1;
138f1174f77SEdward Cree 	return a;
139f1174f77SEdward Cree }
140f1174f77SEdward Cree 
141f1174f77SEdward Cree bool tnum_is_aligned(struct tnum a, u64 size)
142f1174f77SEdward Cree {
143f1174f77SEdward Cree 	if (!size)
144f1174f77SEdward Cree 		return true;
145f1174f77SEdward Cree 	return !((a.value | a.mask) & (size - 1));
146f1174f77SEdward Cree }
147f1174f77SEdward Cree 
148f1174f77SEdward Cree bool tnum_in(struct tnum a, struct tnum b)
149f1174f77SEdward Cree {
150f1174f77SEdward Cree 	if (b.mask & ~a.mask)
151f1174f77SEdward Cree 		return false;
152f1174f77SEdward Cree 	b.value &= ~a.mask;
153f1174f77SEdward Cree 	return a.value == b.value;
154f1174f77SEdward Cree }
155f1174f77SEdward Cree 
156f1174f77SEdward Cree int tnum_strn(char *str, size_t size, struct tnum a)
157f1174f77SEdward Cree {
158f1174f77SEdward Cree 	return snprintf(str, size, "(%#llx; %#llx)", a.value, a.mask);
159f1174f77SEdward Cree }
160f1174f77SEdward Cree EXPORT_SYMBOL_GPL(tnum_strn);
161f1174f77SEdward Cree 
162f1174f77SEdward Cree int tnum_sbin(char *str, size_t size, struct tnum a)
163f1174f77SEdward Cree {
164f1174f77SEdward Cree 	size_t n;
165f1174f77SEdward Cree 
166f1174f77SEdward Cree 	for (n = 64; n; n--) {
167f1174f77SEdward Cree 		if (n < size) {
168f1174f77SEdward Cree 			if (a.mask & 1)
169f1174f77SEdward Cree 				str[n - 1] = 'x';
170f1174f77SEdward Cree 			else if (a.value & 1)
171f1174f77SEdward Cree 				str[n - 1] = '1';
172f1174f77SEdward Cree 			else
173f1174f77SEdward Cree 				str[n - 1] = '0';
174f1174f77SEdward Cree 		}
175f1174f77SEdward Cree 		a.mask >>= 1;
176f1174f77SEdward Cree 		a.value >>= 1;
177f1174f77SEdward Cree 	}
178f1174f77SEdward Cree 	str[min(size - 1, (size_t)64)] = 0;
179f1174f77SEdward Cree 	return 64;
180f1174f77SEdward Cree }
181