Skip to content

Commit 1df7dad

Browse files
nandedamanaanakryiko
authored andcommitted
bpf: Improve the general precision of tnum_mul
Drop the value-mask decomposition technique and adopt straightforward long-multiplication with a twist: when LSB(a) is uncertain, find the two partial products (for LSB(a) = known 0 and LSB(a) = known 1) and take a union. Experiment shows that applying this technique in long multiplication improves the precision in a significant number of cases (at the cost of losing precision in a relatively lower number of cases). Signed-off-by: Nandakumar Edamana <[email protected]> Signed-off-by: Andrii Nakryiko <[email protected]> Tested-by: Harishankar Vishwanathan <[email protected]> Reviewed-by: Harishankar Vishwanathan <[email protected]> Acked-by: Eduard Zingerman <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent 2465bb8 commit 1df7dad

File tree

2 files changed

+45
-13
lines changed

2 files changed

+45
-13
lines changed

include/linux/tnum.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ bool tnum_overlap(struct tnum a, struct tnum b);
5757
/* Return a tnum representing numbers satisfying both @a and @b */
5858
struct tnum tnum_intersect(struct tnum a, struct tnum b);
5959

60+
/* Returns a tnum representing numbers satisfying either @a or @b */
61+
struct tnum tnum_union(struct tnum t1, struct tnum t2);
62+
6063
/* Return @a with all but the lowest @size bytes cleared */
6164
struct tnum tnum_cast(struct tnum a, u8 size);
6265

kernel/bpf/tnum.c

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -116,31 +116,47 @@ struct tnum tnum_xor(struct tnum a, struct tnum b)
116116
return TNUM(v & ~mu, mu);
117117
}
118118

119-
/* Generate partial products by multiplying each bit in the multiplier (tnum a)
120-
* with the multiplicand (tnum b), and add the partial products after
121-
* appropriately bit-shifting them. Instead of directly performing tnum addition
122-
* on the generated partial products, equivalenty, decompose each partial
123-
* product into two tnums, consisting of the value-sum (acc_v) and the
124-
* mask-sum (acc_m) and then perform tnum addition on them. The following paper
125-
* explains the algorithm in more detail: https://arxiv.org/abs/2105.05398.
119+
/* Perform long multiplication, iterating through the bits in a using rshift:
120+
* - if LSB(a) is a known 0, keep current accumulator
121+
* - if LSB(a) is a known 1, add b to current accumulator
122+
* - if LSB(a) is unknown, take a union of the above cases.
123+
*
124+
* For example:
125+
*
126+
* acc_0: acc_1:
127+
*
128+
* 11 * -> 11 * -> 11 * -> union(0011, 1001) == x0x1
129+
* x1 01 11
130+
* ------ ------ ------
131+
* 11 11 11
132+
* xx 00 11
133+
* ------ ------ ------
134+
* ???? 0011 1001
126135
*/
127136
struct tnum tnum_mul(struct tnum a, struct tnum b)
128137
{
129-
u64 acc_v = a.value * b.value;
130-
struct tnum acc_m = TNUM(0, 0);
138+
struct tnum acc = TNUM(0, 0);
131139

132140
while (a.value || a.mask) {
133141
/* LSB of tnum a is a certain 1 */
134142
if (a.value & 1)
135-
acc_m = tnum_add(acc_m, TNUM(0, b.mask));
143+
acc = tnum_add(acc, b);
136144
/* LSB of tnum a is uncertain */
137-
else if (a.mask & 1)
138-
acc_m = tnum_add(acc_m, TNUM(0, b.value | b.mask));
145+
else if (a.mask & 1) {
146+
/* acc = tnum_union(acc_0, acc_1), where acc_0 and
147+
* acc_1 are partial accumulators for cases
148+
* LSB(a) = certain 0 and LSB(a) = certain 1.
149+
* acc_0 = acc + 0 * b = acc.
150+
* acc_1 = acc + 1 * b = tnum_add(acc, b).
151+
*/
152+
153+
acc = tnum_union(acc, tnum_add(acc, b));
154+
}
139155
/* Note: no case for LSB is certain 0 */
140156
a = tnum_rshift(a, 1);
141157
b = tnum_lshift(b, 1);
142158
}
143-
return tnum_add(TNUM(acc_v, 0), acc_m);
159+
return acc;
144160
}
145161

146162
bool tnum_overlap(struct tnum a, struct tnum b)
@@ -163,6 +179,19 @@ struct tnum tnum_intersect(struct tnum a, struct tnum b)
163179
return TNUM(v & ~mu, mu);
164180
}
165181

182+
/* Returns a tnum with the uncertainty from both a and b, and in addition, new
183+
* uncertainty at any position that a and b disagree. This represents a
184+
* superset of the union of the concrete sets of both a and b. Despite the
185+
* overapproximation, it is optimal.
186+
*/
187+
struct tnum tnum_union(struct tnum a, struct tnum b)
188+
{
189+
u64 v = a.value & b.value;
190+
u64 mu = (a.value ^ b.value) | a.mask | b.mask;
191+
192+
return TNUM(v & ~mu, mu);
193+
}
194+
166195
struct tnum tnum_cast(struct tnum a, u8 size)
167196
{
168197
a.value &= (1ULL << (size * 8)) - 1;

0 commit comments

Comments
 (0)