Skip to content

Commit 5ca44dc

Browse files
smasher164FiloSottile
authored andcommitted
math/bits: make Add and Sub fallbacks constant time
Make the extended precision add-with-carry and sub-with-carry operations take a constant amount of time to execute, regardless of input. name old time/op new time/op delta Add-4 1.16ns ±11% 1.51ns ± 5% +30.52% (p=0.008 n=5+5) Add32-4 1.08ns ± 0% 1.03ns ± 1% -4.86% (p=0.029 n=4+4) Add64-4 1.09ns ± 1% 1.95ns ± 3% +79.23% (p=0.008 n=5+5) Add64multiple-4 4.03ns ± 1% 4.55ns ±11% +13.07% (p=0.008 n=5+5) Sub-4 1.08ns ± 1% 1.50ns ± 0% +38.17% (p=0.016 n=5+4) Sub32-4 1.09ns ± 2% 1.53ns ±10% +40.26% (p=0.008 n=5+5) Sub64-4 1.10ns ± 1% 1.47ns ± 1% +33.39% (p=0.008 n=5+5) Sub64multiple-4 4.30ns ± 2% 4.08ns ± 4% -5.07% (p=0.032 n=5+5) Fixes #31267 Change-Id: I1824b1b3ab8f09902ce8b5fef84ce2fdb8847ed9 Reviewed-on: https://go-review.googlesource.com/c/go/+/170758 Reviewed-by: Filippo Valsorda <[email protected]> Reviewed-by: Keith Randall <[email protected]> Run-TryBot: Filippo Valsorda <[email protected]> TryBot-Result: Gobot Gobot <[email protected]>
1 parent 1ab063c commit 5ca44dc

File tree

1 file changed

+19
-30
lines changed

1 file changed

+19
-30
lines changed

src/math/bits/bits.go

Lines changed: 19 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -332,35 +332,30 @@ func Len64(x uint64) (n int) {
332332
// The carry input must be 0 or 1; otherwise the behavior is undefined.
333333
// The carryOut output is guaranteed to be 0 or 1.
334334
func Add(x, y, carry uint) (sum, carryOut uint) {
335-
yc := y + carry
336-
sum = x + yc
337-
if sum < x || yc < y {
338-
carryOut = 1
335+
if UintSize == 32 {
336+
s32, c32 := Add32(uint32(x), uint32(y), uint32(carry))
337+
return uint(s32), uint(c32)
339338
}
340-
return
339+
s64, c64 := Add64(uint64(x), uint64(y), uint64(carry))
340+
return uint(s64), uint(c64)
341341
}
342342

343343
// Add32 returns the sum with carry of x, y and carry: sum = x + y + carry.
344344
// The carry input must be 0 or 1; otherwise the behavior is undefined.
345345
// The carryOut output is guaranteed to be 0 or 1.
346346
func Add32(x, y, carry uint32) (sum, carryOut uint32) {
347-
yc := y + carry
348-
sum = x + yc
349-
if sum < x || yc < y {
350-
carryOut = 1
351-
}
347+
sum64 := uint64(x) + uint64(y) + uint64(carry)
348+
sum = uint32(sum64)
349+
carryOut = uint32(sum64 >> 32)
352350
return
353351
}
354352

355353
// Add64 returns the sum with carry of x, y and carry: sum = x + y + carry.
356354
// The carry input must be 0 or 1; otherwise the behavior is undefined.
357355
// The carryOut output is guaranteed to be 0 or 1.
358356
func Add64(x, y, carry uint64) (sum, carryOut uint64) {
359-
yc := y + carry
360-
sum = x + yc
361-
if sum < x || yc < y {
362-
carryOut = 1
363-
}
357+
sum = x + y + carry
358+
carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
364359
return
365360
}
366361

@@ -370,35 +365,29 @@ func Add64(x, y, carry uint64) (sum, carryOut uint64) {
370365
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
371366
// The borrowOut output is guaranteed to be 0 or 1.
372367
func Sub(x, y, borrow uint) (diff, borrowOut uint) {
373-
yb := y + borrow
374-
diff = x - yb
375-
if diff > x || yb < y {
376-
borrowOut = 1
368+
if UintSize == 32 {
369+
d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow))
370+
return uint(d32), uint(b32)
377371
}
378-
return
372+
d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow))
373+
return uint(d64), uint(b64)
379374
}
380375

381376
// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow.
382377
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
383378
// The borrowOut output is guaranteed to be 0 or 1.
384379
func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) {
385-
yb := y + borrow
386-
diff = x - yb
387-
if diff > x || yb < y {
388-
borrowOut = 1
389-
}
380+
diff = x - y - borrow
381+
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31
390382
return
391383
}
392384

393385
// Sub64 returns the difference of x, y and borrow: diff = x - y - borrow.
394386
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
395387
// The borrowOut output is guaranteed to be 0 or 1.
396388
func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) {
397-
yb := y + borrow
398-
diff = x - yb
399-
if diff > x || yb < y {
400-
borrowOut = 1
401-
}
389+
diff = x - y - borrow
390+
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
402391
return
403392
}
404393

0 commit comments

Comments
 (0)