diff options
Diffstat (limited to 'pkg/sentry/time/arith_arm64.go')
-rw-r--r-- | pkg/sentry/time/arith_arm64.go | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/pkg/sentry/time/arith_arm64.go b/pkg/sentry/time/arith_arm64.go new file mode 100644 index 000000000..b94740c2a --- /dev/null +++ b/pkg/sentry/time/arith_arm64.go @@ -0,0 +1,70 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file provides a generic Go implementation of uint128 divided by uint64. + +// The code is derived from Go's generic math/big.divWW_g +// (src/math/big/arith.go), but is only used on ARM64. + +package time + +import "math/bits" + +type word uint + +const ( + _W = bits.UintSize // word size in bits + _W2 = _W / 2 // half word size in bits + _B2 = 1 << _W2 // half digit base + _M2 = _B2 - 1 // half digit mask +) + +// nlz returns the number of leading zeros in x. +// Wraps bits.LeadingZeros call for convenience. +func nlz(x word) uint { + return uint(bits.LeadingZeros(uint(x))) +} + +// q = (u1<<_W + u0 - r)/y +// Adapted from Warren, Hacker's Delight, p. 152. +func divWW(u1, u0, v word) (q, r word) { + if u1 >= v { + return 1<<_W - 1, 1<<_W - 1 + } + + s := nlz(v) + v <<= s + + vn1 := v >> _W2 + vn0 := v & _M2 + un32 := u1<<s | u0>>(_W-s) + un10 := u0 << s + un1 := un10 >> _W2 + un0 := un10 & _M2 + q1 := un32 / vn1 + rhat := un32 - q1*vn1 + + for q1 >= _B2 || q1*vn0 > _B2*rhat+un1 { + q1-- + rhat += vn1 + + if rhat >= _B2 { + break + } + } + + un21 := un32*_B2 + un1 - q1*v + q0 := un21 / vn1 + rhat = un21 - q0*vn1 + + for q0 >= _B2 || q0*vn0 > _B2*rhat+un0 { + q0-- + rhat += vn1 + if rhat >= _B2 { + break + } + } + + return q1*_B2 + q0, (un21*_B2 + un0 - q0*v) >> s +} |