Skip to content

Commit

Permalink
Merge pull request #29 from markkurossi/topic/divider
Browse files Browse the repository at this point in the history
Topic/divider
  • Loading branch information
markkurossi authored May 10, 2024
2 parents ad38731 + c47a01d commit a19365a
Show file tree
Hide file tree
Showing 12 changed files with 352 additions and 63 deletions.
8 changes: 7 additions & 1 deletion apps/garbled/examples/div.mpcl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@

package main

func main(a, b uint8) uint {
import (
"math"
)

func main(a, b uint64) uint {
return a / b

//return int64(math.DivUint64(uint64(a), uint64(b)))
}
25 changes: 15 additions & 10 deletions benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -678,13 +678,18 @@ BenchmarkGarbleINV-8 14924721 77.97 ns/op

Optimized circuits from [pkg/math/](pkg/math/):

| Implementation | XOR gates | XOR % | !XOR gates | !XOR % |
|:---------------|----------:|------:|-----------:|-------:|
| add64.circ | 313 | | 63 | |
| sub64.circ | 313 | | 126 | |
| mul64.circ | 9642 | | 4033 | |
| div64.circ | 24817 | | 5109 | |
| MPCL a+b | 251 | 80.2 | 63 | 100.0 |
| MPCL a-b | 252 | 80.5 | 65 | 51.6 |
| MPCL a*b | 8261 | 85.7 | 4007 | 99.4 |
| MPCL a/b | 24515 | 98.8 | 8194 | 160.4 |
| Implementation | XOR gates | XOR % | !XOR gates | !XOR % |
|:----------------|----------:|------:|-----------:|-------:|
| add64.circ | 313 | | 63 | |
| sub64.circ | 313 | | 126 | |
| mul64.circ | 9642 | | 4033 | |
| div64.circ | 24817 | | 5109 | |
| MPCL a+b | 251 | 80.2 | 63 | 100.0 |
| MPCL a-b | 252 | 80.5 | 65 | 51.6 |
| MPCL a*b | 8261 | 85.7 | 4007 | 99.4 |
| MPCL R int a/b | 33273 | 134.1 | 10463 | 204.8 |
| MPCL R uint a/b | 32133 | 129.5 | 10084 | 197.4 |
| MPCL A int a/b | 25655 | 103.4 | 8574 | 167.8 |
| MPCL A uint a/b | 24515 | 98.8 | 8194 | 160.4 |
| MPCL L int a/b | 25209 | 101.6 | 8447 | 165.3 |
| MPCL L uint a/b | 24069 | 97.0 | 8068 | 160.0 |
229 changes: 226 additions & 3 deletions compiler/circuits/circ_divider.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,131 @@
//
// Copyright (c) 2019-2023 Markku Rossi
// Copyright (c) 2019-2024 Markku Rossi
//
// All rights reserved.
//

package circuits

// NewDivider creates a division circuit computing r=a/b, q=a%b.
func NewDivider(cc *Compiler, a, b, q, r []*Wire) error {
// NewUDividerLong creates an unsigned integer division circuit
// computing r=a/b, q=a%b. This function uses Long Division algorithm.
func NewUDividerLong(cc *Compiler, a, b, q, rret []*Wire) error {
a, b = cc.ZeroPad(a, b)

r := make([]*Wire, len(a))
for i := 0; i < len(r); i++ {
r[i] = cc.ZeroWire()
}

for i := len(a) - 1; i >= 0; i-- {
// r << 1
for j := len(r) - 1; j > 0; j-- {
r[j] = r[j-1]
}
r[0] = a[i]

// r-d, overlow: r < d
diff := make([]*Wire, len(r)+1)
for j := 0; j < len(diff); j++ {
diff[j] = cc.Calloc.Wire()
}
err := NewSubtractor(cc, r, b, diff)
if err != nil {
return err
}
if i < len(q) {
err = NewMUX(cc, diff[len(diff)-1:], []*Wire{cc.ZeroWire()},
[]*Wire{cc.OneWire()}, q[i:i+1])
if err != nil {
return err
}
}
nr := make([]*Wire, len(r))
for j := 0; j < len(nr); j++ {
if i == 0 && j < len(rret) {
nr[j] = rret[j]
} else {
nr[j] = cc.Calloc.Wire()
}
}

err = NewMUX(cc, diff[len(diff)-1:], r, diff[:len(diff)-1], nr)
if err != nil {
return err
}
r = nr
}

return nil
}

// NewUDividerRestoring creates an unsigned integer division circuit
// computing r=a/b, q=a%b. This function uses Restoring Division
// algorithm.
func NewUDividerRestoring(cc *Compiler, a, b, q, rret []*Wire) error {
a, b = cc.ZeroPad(a, b)

r := make([]*Wire, len(a)*2)
for i := 0; i < len(r); i++ {
if i < len(a) {
r[i] = a[i]
} else {
r[i] = cc.ZeroWire()
}
}
d := make([]*Wire, len(b)*2)
for i := 0; i < len(d); i++ {
if i < len(b) {
d[i] = cc.ZeroWire()
} else {
d[i] = b[i-len(b)]
}
}

for i := len(a) - 1; i >= 0; i-- {
// r << 1
for j := len(r) - 1; j > 0; j-- {
r[j] = r[j-1]
}
r[0] = cc.ZeroWire()

// r-d, overlow: r < d
diff := make([]*Wire, len(r)+1)
for j := 0; j < len(diff); j++ {
diff[j] = cc.Calloc.Wire()
}
err := NewSubtractor(cc, r, d, diff)
if err != nil {
return err
}
if i < len(q) {
err = NewMUX(cc, diff[len(diff)-1:], []*Wire{cc.ZeroWire()},
[]*Wire{cc.OneWire()}, q[i:i+1])
if err != nil {
return err
}
}
nr := make([]*Wire, len(r))
for j := 0; j < len(nr); j++ {
if i == 0 && j >= len(a) && j-len(a) < len(rret) {
nr[j] = rret[j-len(a)]
} else {
nr[j] = cc.Calloc.Wire()
}
}

err = NewMUX(cc, diff[len(diff)-1:], r, diff[:len(diff)-1], nr)
if err != nil {
return err
}
r = nr
}

return nil
}

// NewUDividerArray creates an unsigned integer division circuit
// computing r=a/b, q=a%b. This function uses Array Divider algorithm.
func NewUDividerArray(cc *Compiler, a, b, q, r []*Wire) error {
a, b = cc.ZeroPad(a, b)

rIn := make([]*Wire, len(b)+1)
Expand Down Expand Up @@ -84,3 +202,108 @@ func NewDivider(cc *Compiler, a, b, q, r []*Wire) error {

return nil
}

// NewUDivider creates an unsigned integer division circuit computing
// r=a/b, q=a%b.
func NewUDivider(cc *Compiler, a, b, q, r []*Wire) error {
return NewUDividerLong(cc, a, b, q, r)
}

// NewIDivider creates a signed integer division circuit computing
// r=a/b, q=a%b. The function converts negative a and b to positive
// values before doing the divmod with NewUDivider. If a or b was
// negative, the funtion converts the result quotient to negative
// value.
func NewIDivider(cc *Compiler, a, b, q, r []*Wire) error {
a, b = cc.ZeroPad(a, b)

zero := []*Wire{cc.ZeroWire()}
neg0 := cc.ZeroWire()

// If a is negative, set neg=!neg, a=-a.

neg1 := cc.Calloc.Wire()
cc.INV(neg0, neg1)

a1 := make([]*Wire, len(a))
for i := 0; i < len(a1); i++ {
a1[i] = cc.Calloc.Wire()
}
err := NewSubtractor(cc, zero, a, a1)
if err != nil {
return err
}

neg2 := cc.Calloc.Wire()
err = NewMUX(cc, a[len(a)-1:], []*Wire{neg1}, []*Wire{neg0}, []*Wire{neg2})
if err != nil {
return err
}

a2 := make([]*Wire, len(a))
for i := 0; i < len(a2); i++ {
a2[i] = cc.Calloc.Wire()
}

err = NewMUX(cc, a[len(a)-1:], a1, a, a2)
if err != nil {
return err
}

// If b is negative, set neg=!neg, b=-b.

neg3 := cc.Calloc.Wire()
cc.INV(neg2, neg3)

b1 := make([]*Wire, len(b))
for i := 0; i < len(b1); i++ {
b1[i] = cc.Calloc.Wire()
}
err = NewSubtractor(cc, zero, b, b1)
if err != nil {
return err
}

neg4 := cc.Calloc.Wire()
err = NewMUX(cc, b[len(b)-1:], []*Wire{neg3}, []*Wire{neg2}, []*Wire{neg4})
if err != nil {
return err
}

b2 := make([]*Wire, len(b))
for i := 0; i < len(a2); i++ {
b2[i] = cc.Calloc.Wire()
}

err = NewMUX(cc, b[len(b)-1:], b1, b, b2)
if err != nil {
return err
}

if len(q) == 0 {
// Modulo operation.
return NewUDivider(cc, a2, b2, q, r)
}

// If neg is set, set q=-q

q0 := make([]*Wire, len(q))
for i := 0; i < len(q0); i++ {
q0[i] = cc.Calloc.Wire()
}
err = NewUDivider(cc, a2, b2, q0, r)
if err != nil {
return err
}

q1 := make([]*Wire, len(q))
for i := 0; i < len(q1); i++ {
q1[i] = cc.Calloc.Wire()
}
err = NewSubtractor(cc, zero, q0, q1)
if err != nil {
return err
}

return NewMUX(cc, []*Wire{neg4}, q1, q0, q)
}
4 changes: 2 additions & 2 deletions compiler/circuits/wire.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2019-2023 Markku Rossi
// Copyright (c) 2019-2024 Markku Rossi
//
// All rights reserved.
//
Expand Down Expand Up @@ -159,7 +159,7 @@ func (w *Wire) SetInput(gate *Gate) {
w.gates = append(w.gates, gate)
} else {
if w.gates[0] != nil {
panic("Input gate already set")
panic("wire input gate already set")
}
w.gates[0] = gate
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/mpa/mpint.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2023 Markku Rossi
// Copyright (c) 2023-2024 Markku Rossi
//
// All rights reserved.
//
Expand Down Expand Up @@ -257,7 +257,7 @@ func (z *Int) Div(x, y *Int) *Int {
panic(err)
}

err = circuits.NewDivider(cc, i0w, i1w, o0w, o1w)
err = circuits.NewIDivider(cc, i0w, i1w, o0w, o1w)
if err != nil {
panic(err)
}
Expand Down Expand Up @@ -337,7 +337,7 @@ func (z *Int) Mod(x, y *Int) *Int {
panic(err)
}

err = circuits.NewDivider(cc, i0w, i1w, o0w, o1w)
err = circuits.NewIDivider(cc, i0w, i1w, o0w, o1w)
if err != nil {
panic(err)
}
Expand Down
32 changes: 27 additions & 5 deletions compiler/ssa/circuitgen.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// Copyright (c) 2020-2023 Markku Rossi
// Copyright (c) 2020-2024 Markku Rossi
//
// All rights reserved.
//
Expand Down Expand Up @@ -145,24 +145,46 @@ func (prog *Program) Circuit(cc *circuits.Compiler) error {
return err
}

case Idiv, Udiv:
case Idiv:
o, err := prog.walloc.Wires(*instr.Out, instr.Out.Type.Bits)
if err != nil {
return err
}

err = circuits.NewDivider(cc, wires[0], wires[1], o, nil)
err = circuits.NewIDivider(cc, wires[0], wires[1], o, nil)
if err != nil {
return err
}

case Imod, Umod:
case Udiv:
o, err := prog.walloc.Wires(*instr.Out, instr.Out.Type.Bits)
if err != nil {
return err
}

err = circuits.NewDivider(cc, wires[0], wires[1], nil, o)
err = circuits.NewUDivider(cc, wires[0], wires[1], o, nil)
if err != nil {
return err
}

case Imod:
o, err := prog.walloc.Wires(*instr.Out, instr.Out.Type.Bits)
if err != nil {
return err
}

err = circuits.NewIDivider(cc, wires[0], wires[1], nil, o)
if err != nil {
return err
}

case Umod:
o, err := prog.walloc.Wires(*instr.Out, instr.Out.Type.Bits)
if err != nil {
return err
}

err = circuits.NewUDivider(cc, wires[0], wires[1], nil, o)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit a19365a

Please sign in to comment.