return x
}
-// addMulVVW multiplies the multi-word value x by the single-word value y,
-// adding the result to the multi-word value z and returning the final carry.
-// It can be thought of as one row of a pen-and-paper column multiplication.
-func addMulVVW(z, x []uint, y uint) (carry uint) {
- _ = x[len(z)-1] // bounds check elimination hint
- for i := range z {
- hi, lo := bits.Mul(x[i], y)
- lo, c := bits.Add(lo, z[i], 0)
- // We use bits.Add with zero to get an add-with-carry instruction that
- // absorbs the carry from the previous bits.Add.
- hi, _ = bits.Add(hi, 0, c)
- lo, c = bits.Add(lo, carry, 0)
- hi, _ = bits.Add(hi, 0, c)
- carry = hi
- z[i] = lo
- }
- return carry
-}
-
// Mul calculates x = x * y mod m.
//
// The length of both operands must be the same as the modulus. Both operands
--- /dev/null
+// Copyright 2024 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.
+
+//go:build !wasm
+
+package bigmod
+
+import "math/bits"
+
+// addMulVVW multiplies the multi-word value x by the single-word value y,
+// adding the result to the multi-word value z and returning the final carry.
+// It can be thought of as one row of a pen-and-paper column multiplication.
+func addMulVVW(z, x []uint, y uint) (carry uint) {
+ _ = x[len(z)-1] // bounds check elimination hint
+ for i := range z {
+ hi, lo := bits.Mul(x[i], y)
+ lo, c := bits.Add(lo, z[i], 0)
+ // We use bits.Add with zero to get an add-with-carry instruction that
+ // absorbs the carry from the previous bits.Add.
+ hi, _ = bits.Add(hi, 0, c)
+ lo, c = bits.Add(lo, carry, 0)
+ hi, _ = bits.Add(hi, 0, c)
+ carry = hi
+ z[i] = lo
+ }
+ return carry
+}
--- /dev/null
+// Copyright 2024 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.
+
+package bigmod
+
+// The generic implementation relies on 64x64->128 bit multiplication and
+// 64-bit add-with-carry, which are compiler intrinsics on many architectures.
+// Wasm doesn't support those. Here we implement it with 32x32->64 bit
+// operations, which is more efficient on Wasm.
+
+// addMulVVW multiplies the multi-word value x by the single-word value y,
+// adding the result to the multi-word value z and returning the final carry.
+// It can be thought of as one row of a pen-and-paper column multiplication.
+func addMulVVW(z, x []uint, y uint) (carry uint) {
+ const mask32 = 1<<32 - 1
+ y0 := y & mask32
+ y1 := y >> 32
+ _ = x[len(z)-1] // bounds check elimination hint
+ for i, zi := range z {
+ xi := x[i]
+ x0 := xi & mask32
+ x1 := xi >> 32
+ z0 := zi & mask32
+ z1 := zi >> 32
+ c0 := carry & mask32
+ c1 := carry >> 32
+
+ w00 := x0*y0 + z0 + c0
+ l00 := w00 & mask32
+ h00 := w00 >> 32
+
+ w01 := x0*y1 + z1 + h00
+ l01 := w01 & mask32
+ h01 := w01 >> 32
+
+ w10 := x1*y0 + c1 + l01
+ h10 := w10 >> 32
+
+ carry = x1*y1 + h10 + h01
+ z[i] = w10<<32 + l00
+ }
+ return carry
+}