Commit 5ca44dc4 authored by smasher164's avatar smasher164 Committed by Filippo Valsorda

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/+/170758Reviewed-by: default avatarFilippo Valsorda <filippo@golang.org>
Reviewed-by: default avatarKeith Randall <khr@golang.org>
Run-TryBot: Filippo Valsorda <filippo@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 1ab063ce
......@@ -332,23 +332,21 @@ func Len64(x uint64) (n int) {
// The carry input must be 0 or 1; otherwise the behavior is undefined.
// The carryOut output is guaranteed to be 0 or 1.
func Add(x, y, carry uint) (sum, carryOut uint) {
yc := y + carry
sum = x + yc
if sum < x || yc < y {
carryOut = 1
if UintSize == 32 {
s32, c32 := Add32(uint32(x), uint32(y), uint32(carry))
return uint(s32), uint(c32)
}
return
s64, c64 := Add64(uint64(x), uint64(y), uint64(carry))
return uint(s64), uint(c64)
}
// Add32 returns the sum with carry of x, y and carry: sum = x + y + carry.
// The carry input must be 0 or 1; otherwise the behavior is undefined.
// The carryOut output is guaranteed to be 0 or 1.
func Add32(x, y, carry uint32) (sum, carryOut uint32) {
yc := y + carry
sum = x + yc
if sum < x || yc < y {
carryOut = 1
}
sum64 := uint64(x) + uint64(y) + uint64(carry)
sum = uint32(sum64)
carryOut = uint32(sum64 >> 32)
return
}
......@@ -356,11 +354,8 @@ func Add32(x, y, carry uint32) (sum, carryOut uint32) {
// The carry input must be 0 or 1; otherwise the behavior is undefined.
// The carryOut output is guaranteed to be 0 or 1.
func Add64(x, y, carry uint64) (sum, carryOut uint64) {
yc := y + carry
sum = x + yc
if sum < x || yc < y {
carryOut = 1
}
sum = x + y + carry
carryOut = ((x & y) | ((x | y) &^ sum)) >> 63
return
}
......@@ -370,23 +365,20 @@ func Add64(x, y, carry uint64) (sum, carryOut uint64) {
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
// The borrowOut output is guaranteed to be 0 or 1.
func Sub(x, y, borrow uint) (diff, borrowOut uint) {
yb := y + borrow
diff = x - yb
if diff > x || yb < y {
borrowOut = 1
if UintSize == 32 {
d32, b32 := Sub32(uint32(x), uint32(y), uint32(borrow))
return uint(d32), uint(b32)
}
return
d64, b64 := Sub64(uint64(x), uint64(y), uint64(borrow))
return uint(d64), uint(b64)
}
// Sub32 returns the difference of x, y and borrow, diff = x - y - borrow.
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
// The borrowOut output is guaranteed to be 0 or 1.
func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) {
yb := y + borrow
diff = x - yb
if diff > x || yb < y {
borrowOut = 1
}
diff = x - y - borrow
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 31
return
}
......@@ -394,11 +386,8 @@ func Sub32(x, y, borrow uint32) (diff, borrowOut uint32) {
// The borrow input must be 0 or 1; otherwise the behavior is undefined.
// The borrowOut output is guaranteed to be 0 or 1.
func Sub64(x, y, borrow uint64) (diff, borrowOut uint64) {
yb := y + borrow
diff = x - yb
if diff > x || yb < y {
borrowOut = 1
}
diff = x - y - borrow
borrowOut = ((^x & y) | (^(x ^ y) & diff)) >> 63
return
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment