From 8fee344b68a15c37ff788a4ee4117209d03c5ec2 Mon Sep 17 00:00:00 2001 From: Markku Rossi Date: Mon, 4 Sep 2023 12:02:19 +0200 Subject: [PATCH] Cleaned wire allocation API. --- compiler/ssa/program.go | 4 +- compiler/ssa/wire_allocator.go | 354 ++++++++++++++++++++++++-- compiler/ssa/wire_allocator_string.go | 236 ----------------- compiler/ssa/wire_allocator_value.go | 348 ------------------------- 4 files changed, 332 insertions(+), 610 deletions(-) delete mode 100644 compiler/ssa/wire_allocator_string.go delete mode 100644 compiler/ssa/wire_allocator_value.go diff --git a/compiler/ssa/program.go b/compiler/ssa/program.go index 0a0da0ff..ff42ca78 100644 --- a/compiler/ssa/program.go +++ b/compiler/ssa/program.go @@ -29,7 +29,7 @@ type Program struct { OutputWires []*circuits.Wire Constants map[string]ConstantInst Steps []Step - walloc WireAllocator + walloc *WireAllocator calloc *circuits.Allocator zeroWire *circuits.Wire oneWire *circuits.Wire @@ -52,7 +52,7 @@ func NewProgram(params *utils.Params, in, out circuit.IO, Outputs: out, Constants: consts, Steps: steps, - walloc: NewWAllocValue(calloc), + walloc: NewWireAllocator(calloc), calloc: calloc, } diff --git a/compiler/ssa/wire_allocator.go b/compiler/ssa/wire_allocator.go index b3eb0ffe..67ded381 100644 --- a/compiler/ssa/wire_allocator.go +++ b/compiler/ssa/wire_allocator.go @@ -7,42 +7,348 @@ package ssa import ( + "fmt" + "math" + "github.com/markkurossi/mpc/circuit" "github.com/markkurossi/mpc/compiler/circuits" "github.com/markkurossi/mpc/types" ) -// WireAllocator implements dynamic wire allocation. -type WireAllocator interface { - // Allocated tests if the wires have been allocated for the value. - Allocated(v Value) bool +// WireAllocator implements wire allocation using Value.HashCode to +// map values to wires. +type WireAllocator struct { + calloc *circuits.Allocator + freeHdrs []*allocByValue + freeWires map[types.Size][][]*circuits.Wire + freeIDs map[types.Size][][]circuit.Wire + hash [10240]*allocByValue + nextWireID circuit.Wire + flHit int + flMiss int + lookupCount int + lookupFound int +} + +type allocByValue struct { + next *allocByValue + key Value + base circuit.Wire + wires []*circuits.Wire + ids []circuit.Wire +} + +func (alloc *allocByValue) String() string { + return fmt.Sprintf("%v[%v]: base=%v, len(wires)=%v", + alloc.key.String(), alloc.key.Type, + alloc.base, len(alloc.wires)) +} + +// NewWireAllocator creates a new WireAllocator. +func NewWireAllocator(calloc *circuits.Allocator) *WireAllocator { + return &WireAllocator{ + calloc: calloc, + freeWires: make(map[types.Size][][]*circuits.Wire), + freeIDs: make(map[types.Size][][]circuit.Wire), + } +} + +func (walloc *WireAllocator) hashCode(v Value) int { + return v.HashCode() % len(walloc.hash) +} + +func (walloc *WireAllocator) newHeader(v Value) (ret *allocByValue) { + if len(walloc.freeHdrs) == 0 { + ret = new(allocByValue) + } else { + ret = walloc.freeHdrs[len(walloc.freeHdrs)-1] + walloc.freeHdrs = walloc.freeHdrs[:len(walloc.freeHdrs)-1] + } + ret.key = v + ret.base = circuits.UnassignedID + return ret +} + +func (walloc *WireAllocator) newWires(bits types.Size) ( + result []*circuits.Wire) { + + fl, ok := walloc.freeWires[bits] + if ok && len(fl) > 0 { + result = fl[len(fl)-1] + walloc.freeWires[bits] = fl[:len(fl)-1] + walloc.flHit++ + } else { + result = walloc.calloc.Wires(bits) + walloc.flMiss++ + } + return result +} - // Streamer API. +func (walloc *WireAllocator) newIDs(bits types.Size) (result []circuit.Wire) { + fl, ok := walloc.freeIDs[bits] + if ok && len(fl) > 0 { + result = fl[len(fl)-1] + walloc.freeIDs[bits] = fl[:len(fl)-1] + walloc.flHit++ + } else { + result = make([]circuit.Wire, bits) + for i := 0; i < int(bits); i++ { + result[i] = circuits.UnassignedID + } + walloc.flMiss++ + } + return result +} - // NextWireID allocated and returns the next unassigned wire ID. - // XXX is this sync with circuits.Compiler.NextWireID()? - NextWireID() circuit.Wire +func (walloc *WireAllocator) lookup(hash int, v Value) *allocByValue { + var count int + for ptr := &walloc.hash[hash]; *ptr != nil; ptr = &(*ptr).next { + count++ + if (*ptr).key.Equal(&v) { + alloc := *ptr - // AssignedWires allocates assigned wires for the argument value. - AssignedWires(v Value, bits types.Size) ([]circuit.Wire, error) + if count > 2 { + // MRU in the hash bucket. + *ptr = alloc.next + alloc.next = walloc.hash[hash] + walloc.hash[hash] = alloc + } - // AssignedWires allocates assigned wires for the argument value. - AssignedWiresAndIDs(v Value, bits types.Size) ([]*circuits.Wire, error) + walloc.lookupCount++ + walloc.lookupFound += count + return alloc + } + } + return nil +} + +func (walloc *WireAllocator) alloc(bits types.Size, v Value, + wires, ids bool) *allocByValue { + + result := walloc.newHeader(v) + + if wires && ids { + result.wires = walloc.newWires(bits) + result.ids = walloc.newIDs(bits) + result.base = result.wires[0].ID() + + for i := 0; i < int(bits); i++ { + result.ids[i] = result.wires[i].ID() + } + } else if wires { + result.wires = walloc.newWires(bits) + result.base = result.wires[0].ID() + } else { + result.ids = walloc.newIDs(bits) + result.base = result.ids[0] + } + return result +} + +func (walloc *WireAllocator) remove(hash int, v Value) *allocByValue { + for ptr := &walloc.hash[hash]; *ptr != nil; ptr = &(*ptr).next { + if (*ptr).key.Equal(&v) { + ret := *ptr + *ptr = (*ptr).next + return ret + } + } + return nil +} + +// Allocated tests if the wires have been allocated for the value. +func (walloc *WireAllocator) Allocated(v Value) bool { + hash := walloc.hashCode(v) + alloc := walloc.lookup(hash, v) + return alloc != nil +} + +// NextWireID allocated and returns the next unassigned wire ID. +// XXX is this sync with circuits.Compiler.NextWireID()? +func (walloc *WireAllocator) NextWireID() circuit.Wire { + ret := walloc.nextWireID + walloc.nextWireID++ + return ret +} + +// AssignedWires allocates assigned wires for the argument value. +func (walloc *WireAllocator) AssignedWires(v Value, bits types.Size) ( + []circuit.Wire, error) { + if bits <= 0 { + return nil, fmt.Errorf("size not set for value %v", v) + } + hash := walloc.hashCode(v) + alloc := walloc.lookup(hash, v) + if alloc == nil { + alloc = walloc.alloc(bits, v, false, true) + alloc.next = walloc.hash[hash] + walloc.hash[hash] = alloc + + // Assign wire IDs. + if alloc.base == circuits.UnassignedID { + alloc.base = walloc.nextWireID + for i := 0; i < int(bits); i++ { + alloc.ids[i] = walloc.nextWireID + circuit.Wire(i) + } + walloc.nextWireID += circuit.Wire(bits) + } + } + if alloc.ids == nil { + alloc.ids = walloc.newIDs(bits) + for i := 0; i < int(bits); i++ { + alloc.ids[i] = alloc.wires[i].ID() + } + } + return alloc.ids, nil +} + +// AssignedWiresAndIDs allocates assigned wires for the argument value. +func (walloc *WireAllocator) AssignedWiresAndIDs(v Value, bits types.Size) ( + []*circuits.Wire, error) { + if bits <= 0 { + return nil, fmt.Errorf("size not set for value %v", v) + } + hash := walloc.hashCode(v) + alloc := walloc.lookup(hash, v) + if alloc == nil { + alloc = walloc.alloc(bits, v, true, true) + alloc.next = walloc.hash[hash] + walloc.hash[hash] = alloc + + // Assign wire IDs. + if alloc.base == circuits.UnassignedID { + alloc.base = walloc.nextWireID + for i := 0; i < int(bits); i++ { + alloc.wires[i].SetID(walloc.nextWireID + circuit.Wire(i)) + } + walloc.nextWireID += circuit.Wire(bits) + } + } + if alloc.ids == nil { + alloc.ids = walloc.newIDs(bits) + for i := 0; i < int(bits); i++ { + alloc.ids[i] = alloc.wires[i].ID() + } + } + return alloc.wires, nil +} + +// GCWires recycles the wires of the argument value. The wires must +// have been previously allocated with Wires, AssignedWires, or +// SetWires; the function panics if the wires have not been allocated. +func (walloc *WireAllocator) GCWires(v Value) { + hash := walloc.hashCode(v) + alloc := walloc.remove(hash, v) + if alloc == nil { + panic(fmt.Sprintf("GC: %s not known", v)) + } + + if alloc.wires != nil { + if alloc.base == circuits.UnassignedID { + alloc.base = alloc.wires[0].ID() + } + // Clear wires and reassign their IDs. + for i := 0; i < len(alloc.wires); i++ { + alloc.wires[i].Reset(alloc.base + circuit.Wire(i)) + } + bits := types.Size(len(alloc.wires)) + walloc.freeWires[bits] = append(walloc.freeWires[bits], alloc.wires) + } + if alloc.ids != nil { + if alloc.base == circuits.UnassignedID { + alloc.base = alloc.ids[0] + } + // Clear IDs. + for i := 0; i < len(alloc.ids); i++ { + alloc.ids[i] = alloc.base + circuit.Wire(i) + } + bits := types.Size(len(alloc.ids)) + walloc.freeIDs[bits] = append(walloc.freeIDs[bits], alloc.ids) + } + + alloc.next = nil + alloc.base = circuits.UnassignedID + alloc.wires = nil + alloc.ids = nil + walloc.freeHdrs = append(walloc.freeHdrs, alloc) +} + +// Wires allocates unassigned wires for the argument value. +func (walloc *WireAllocator) Wires(v Value, bits types.Size) ( + []*circuits.Wire, error) { + if bits <= 0 { + return nil, fmt.Errorf("size not set for value %v", v) + } + hash := walloc.hashCode(v) + alloc := walloc.lookup(hash, v) + if alloc == nil { + alloc = walloc.alloc(bits, v, true, false) + alloc.next = walloc.hash[hash] + walloc.hash[hash] = alloc + } + return alloc.wires, nil +} + +// SetWires allocates wire IDs for the value's wires. +func (walloc *WireAllocator) SetWires(v Value, w []*circuits.Wire) { + hash := walloc.hashCode(v) + alloc := walloc.lookup(hash, v) + if alloc != nil { + panic(fmt.Sprintf("wires already set for %v", v)) + } + alloc = &allocByValue{ + key: v, + wires: w, + ids: make([]circuit.Wire, len(w)), + } + if len(w) == 0 { + alloc.base = circuits.UnassignedID + } else { + alloc.base = w[0].ID() + for i := 0; i < len(w); i++ { + alloc.ids[i] = w[i].ID() + } + } + + alloc.next = walloc.hash[hash] + walloc.hash[hash] = alloc +} - // GCWires recycles the wires of the argument value. The wires - // must have been previously allocated with Wires, AssignedWires, - // or SetWires; the function panics if the wires have not been - // allocated. - GCWires(v Value) +// Debug prints debugging information about the wire allocator. +func (walloc *WireAllocator) Debug() { + total := float64(walloc.flHit + walloc.flMiss) + fmt.Printf("Wire freelist: hit=%v (%.2f%%), miss=%v (%.2f%%)\n", + walloc.flHit, float64(walloc.flHit)/total*100, + walloc.flMiss, float64(walloc.flMiss)/total*100) - // Circuit compilation API. + var sum, max int + min := math.MaxInt - // Wires allocates unassigned wires for the argument value. - Wires(v Value, bits types.Size) ([]*circuits.Wire, error) + var maxIndex int - // SetWires allocates wire IDs for the value's wires. - SetWires(v Value, w []*circuits.Wire) + for i := 0; i < len(walloc.hash); i++ { + var count int + for alloc := walloc.hash[i]; alloc != nil; alloc = alloc.next { + count++ + } + sum += count + if count < min { + min = count + } + if count > max { + max = count + maxIndex = i + } + } + fmt.Printf("Hash: min=%v, max=%v, avg=%.4f, lookup=%v (avg=%.4f)\n", + min, max, float64(sum)/float64(len(walloc.hash)), + walloc.lookupCount, + float64(walloc.lookupFound)/float64(walloc.lookupCount)) - // Debug prints debugging information about the wire allocator. - Debug() + if false { + fmt.Printf("Max bucket:\n") + for alloc := walloc.hash[maxIndex]; alloc != nil; alloc = alloc.next { + fmt.Printf(" %v: %v\n", alloc.key.String(), len(alloc.wires)) + } + } } diff --git a/compiler/ssa/wire_allocator_string.go b/compiler/ssa/wire_allocator_string.go deleted file mode 100644 index de0b7635..00000000 --- a/compiler/ssa/wire_allocator_string.go +++ /dev/null @@ -1,236 +0,0 @@ -// -// Copyright (c) 2020-2023 Markku Rossi -// -// All rights reserved. -// - -package ssa - -import ( - "fmt" - "sort" - - "github.com/markkurossi/mpc/circuit" - "github.com/markkurossi/mpc/compiler/circuits" - "github.com/markkurossi/mpc/types" -) - -// WAllocString implements WireAllocator using Value.String to map -// values to wires. -type WAllocString struct { - calloc *circuits.Allocator - freeWires map[types.Size][][]*circuits.Wire - wires map[string]*wireAlloc - nextWireID circuit.Wire - flHit int - flMiss int -} - -// NewWAllocString creates a new WAllocString. -func NewWAllocString(calloc *circuits.Allocator) WireAllocator { - return &WAllocString{ - calloc: calloc, - wires: make(map[string]*wireAlloc), - freeWires: make(map[types.Size][][]*circuits.Wire), - } -} - -var ( - vConst int - vTypeRef int - vPtr int - vDefault int - hash [1024]int -) - -func addValueStats(v Value) { - if v.Const { - vConst++ - } else if v.TypeRef { - vTypeRef++ - } else if v.Type.Type == types.TPtr { - vPtr++ - } else { - vDefault++ - } - - hash[v.HashCode()%len(hash)]++ -} - -// Allocated implements WireAllocator.Allocated. -func (walloc *WAllocString) Allocated(v Value) bool { - key := v.String() - _, ok := walloc.wires[key] - return ok -} - -// NextWireID implements WireAllocator.NextWireID. -func (walloc *WAllocString) NextWireID() circuit.Wire { - ret := walloc.nextWireID - walloc.nextWireID++ - return ret -} - -// Wires implements WireAllocator.Wires. -func (walloc *WAllocString) Wires(v Value, bits types.Size) ( - []*circuits.Wire, error) { - if bits <= 0 { - return nil, fmt.Errorf("size not set for value %v", v) - } - addValueStats(v) - key := v.String() - alloc, ok := walloc.wires[key] - if !ok { - alloc = walloc.allocWires(bits) - walloc.wires[key] = alloc - } - return alloc.Wires, nil -} - -// AssignedWires implements WireAllocator.AssignedWires. -func (walloc *WAllocString) AssignedWires(v Value, bits types.Size) ( - []circuit.Wire, error) { - if bits <= 0 { - return nil, fmt.Errorf("size not set for value %v", v) - } - addValueStats(v) - key := v.String() - alloc, ok := walloc.wires[key] - if !ok { - alloc = walloc.allocWires(bits) - walloc.wires[key] = alloc - - // Assign wire IDs. - if alloc.Base == circuits.UnassignedID { - alloc.Base = walloc.nextWireID - for i := 0; i < int(bits); i++ { - alloc.Wires[i].SetID(walloc.nextWireID + circuit.Wire(i)) - } - walloc.nextWireID += circuit.Wire(bits) - } - } - - return alloc.IDs, nil -} - -func (walloc *WAllocString) AssignedWiresAndIDs(v Value, bits types.Size) ( - []*circuits.Wire, error) { - return nil, fmt.Errorf("not implemented") -} - -type wireAlloc struct { - Base circuit.Wire - Wires []*circuits.Wire - IDs []circuit.Wire -} - -func (walloc *WAllocString) allocWires(bits types.Size) *wireAlloc { - result := &wireAlloc{ - Base: circuits.UnassignedID, - } - - fl, ok := walloc.freeWires[bits] - if ok && len(fl) > 0 { - result.Wires = fl[len(fl)-1] - result.Base = result.Wires[0].ID() - walloc.freeWires[bits] = fl[:len(fl)-1] - walloc.flHit++ - } else { - result.Wires = walloc.calloc.Wires(bits) - walloc.flMiss++ - } - - return result -} - -// SetWires implements WireAllocator.SetWires. -func (walloc *WAllocString) SetWires(v Value, w []*circuits.Wire) { - addValueStats(v) - key := v.String() - _, ok := walloc.wires[key] - if ok { - panic(fmt.Sprintf("wires already set for %v", key)) - } - alloc := &wireAlloc{ - Wires: w, - } - if len(w) == 0 { - alloc.Base = circuits.UnassignedID - } else { - alloc.Base = w[0].ID() - } - - walloc.wires[key] = alloc -} - -// GCWires implements WireAllocator.GCWires. -func (walloc *WAllocString) GCWires(v Value) { - key := v.String() - alloc, ok := walloc.wires[key] - if !ok { - panic(fmt.Sprintf("GC: %s not known", key)) - } - delete(walloc.wires, key) - - if alloc.Base == circuits.UnassignedID { - alloc.Base = alloc.Wires[0].ID() - } - // Clear wires and reassign their IDs. - bits := types.Size(len(alloc.Wires)) - for i := 0; i < int(bits); i++ { - alloc.Wires[i].Reset(alloc.Base + circuit.Wire(i)) - } - - fl := walloc.freeWires[bits] - fl = append(fl, alloc.Wires) - walloc.freeWires[bits] = fl - if false { - fmt.Printf("FL: %d: ", bits) - for k, v := range walloc.freeWires { - fmt.Printf(" %d:%d", k, len(v)) - } - fmt.Println() - } -} - -// Debug implements WireAllocator.Debug. -func (walloc *WAllocString) Debug() { - total := float64(walloc.flHit + walloc.flMiss) - fmt.Printf("Wire freelist: hit=%v (%.2f%%), miss=%v (%.2f%%)\n", - walloc.flHit, float64(walloc.flHit)/total*100, - walloc.flMiss, float64(walloc.flMiss)/total*100) - - var keys []types.Size - for k := range walloc.freeWires { - keys = append(keys, k) - } - sort.Slice(keys, func(i, j int) bool { - return keys[i] < keys[j] - }) - - for _, k := range keys { - fmt.Printf(" %d:\t%d\n", k, len(walloc.freeWires[types.Size(k)])) - } - fmt.Println() - - fmt.Println("Value Stats:") - - sum := float64(vConst + vTypeRef + vPtr + vDefault) - - fmt.Printf(" - vConst:\t%v\t%f%%\n", vConst, float64(vConst)/sum*100) - fmt.Printf(" - vTypeRef:\t%v\t%f%%\n", vTypeRef, float64(vTypeRef)/sum*100) - fmt.Printf(" - vPtr:\t%v\t%f%%\n", vPtr, float64(vPtr)/sum*100) - fmt.Printf(" - vDefault:\t%v\t%f%%\n", vDefault, float64(vDefault)/sum*100) - - if false { - var zeroes int - for idx, count := range hash { - if count == 0 { - zeroes++ - } else { - fmt.Printf("%v:\t%v\n", idx, count) - } - } - fmt.Printf("%v zero buckets\n", zeroes) - } -} diff --git a/compiler/ssa/wire_allocator_value.go b/compiler/ssa/wire_allocator_value.go deleted file mode 100644 index 8279f3f6..00000000 --- a/compiler/ssa/wire_allocator_value.go +++ /dev/null @@ -1,348 +0,0 @@ -// -// Copyright (c) 2023 Markku Rossi -// -// All rights reserved. -// - -package ssa - -import ( - "fmt" - "math" - - "github.com/markkurossi/mpc/circuit" - "github.com/markkurossi/mpc/compiler/circuits" - "github.com/markkurossi/mpc/types" -) - -// WAllocValue implements WireAllocator using Value.HashCode to map -// values to wires. -type WAllocValue struct { - calloc *circuits.Allocator - freeHdrs []*allocByValue - freeWires map[types.Size][][]*circuits.Wire - freeIDs map[types.Size][][]circuit.Wire - hash [10240]*allocByValue - nextWireID circuit.Wire - flHit int - flMiss int - lookupCount int - lookupFound int -} - -type allocByValue struct { - next *allocByValue - key Value - base circuit.Wire - wires []*circuits.Wire - ids []circuit.Wire -} - -func (alloc *allocByValue) String() string { - return fmt.Sprintf("%v[%v]: base=%v, len(wires)=%v", - alloc.key.String(), alloc.key.Type, - alloc.base, len(alloc.wires)) -} - -// NewWAllocValue creates a new WAllocValue. -func NewWAllocValue(calloc *circuits.Allocator) WireAllocator { - return &WAllocValue{ - calloc: calloc, - freeWires: make(map[types.Size][][]*circuits.Wire), - freeIDs: make(map[types.Size][][]circuit.Wire), - } -} - -func (walloc *WAllocValue) hashCode(v Value) int { - return v.HashCode() % len(walloc.hash) -} - -func (walloc *WAllocValue) newHeader(v Value) (ret *allocByValue) { - if len(walloc.freeHdrs) == 0 { - ret = new(allocByValue) - } else { - ret = walloc.freeHdrs[len(walloc.freeHdrs)-1] - walloc.freeHdrs = walloc.freeHdrs[:len(walloc.freeHdrs)-1] - } - ret.key = v - ret.base = circuits.UnassignedID - return ret -} - -func (walloc *WAllocValue) newWires(bits types.Size) (result []*circuits.Wire) { - fl, ok := walloc.freeWires[bits] - if ok && len(fl) > 0 { - result = fl[len(fl)-1] - walloc.freeWires[bits] = fl[:len(fl)-1] - walloc.flHit++ - } else { - result = walloc.calloc.Wires(bits) - walloc.flMiss++ - } - return result -} - -func (walloc *WAllocValue) newIDs(bits types.Size) (result []circuit.Wire) { - fl, ok := walloc.freeIDs[bits] - if ok && len(fl) > 0 { - result = fl[len(fl)-1] - walloc.freeIDs[bits] = fl[:len(fl)-1] - walloc.flHit++ - } else { - result = make([]circuit.Wire, bits) - for i := 0; i < int(bits); i++ { - result[i] = circuits.UnassignedID - } - walloc.flMiss++ - } - return result -} - -// Allocated implements WireAllocator.Allocated. -func (walloc *WAllocValue) Allocated(v Value) bool { - hash := walloc.hashCode(v) - alloc := walloc.lookup(hash, v) - return alloc != nil -} - -// NextWireID implements WireAllocator.NextWireID. -func (walloc *WAllocValue) NextWireID() circuit.Wire { - ret := walloc.nextWireID - walloc.nextWireID++ - return ret -} - -func (walloc *WAllocValue) lookup(hash int, v Value) *allocByValue { - var count int - for ptr := &walloc.hash[hash]; *ptr != nil; ptr = &(*ptr).next { - count++ - if (*ptr).key.Equal(&v) { - alloc := *ptr - - if count > 2 { - // MRU in the hash bucket. - *ptr = alloc.next - alloc.next = walloc.hash[hash] - walloc.hash[hash] = alloc - } - - walloc.lookupCount++ - walloc.lookupFound += count - return alloc - } - } - return nil -} - -func (walloc *WAllocValue) remove(hash int, v Value) *allocByValue { - for ptr := &walloc.hash[hash]; *ptr != nil; ptr = &(*ptr).next { - if (*ptr).key.Equal(&v) { - ret := *ptr - *ptr = (*ptr).next - return ret - } - } - return nil -} - -func (walloc *WAllocValue) alloc(bits types.Size, v Value, - wires, ids bool) *allocByValue { - - result := walloc.newHeader(v) - - if wires && ids { - result.wires = walloc.newWires(bits) - result.ids = walloc.newIDs(bits) - result.base = result.wires[0].ID() - - for i := 0; i < int(bits); i++ { - result.ids[i] = result.wires[i].ID() - } - } else if wires { - result.wires = walloc.newWires(bits) - result.base = result.wires[0].ID() - } else { - result.ids = walloc.newIDs(bits) - result.base = result.ids[0] - } - return result -} - -// Wires implements WireAllocator.Wires. -func (walloc *WAllocValue) Wires(v Value, bits types.Size) ( - []*circuits.Wire, error) { - if bits <= 0 { - return nil, fmt.Errorf("size not set for value %v", v) - } - hash := walloc.hashCode(v) - alloc := walloc.lookup(hash, v) - if alloc == nil { - alloc = walloc.alloc(bits, v, true, false) - alloc.next = walloc.hash[hash] - walloc.hash[hash] = alloc - } - return alloc.wires, nil -} - -// AssignedWires implements WireAllocator.AssignedWires. -func (walloc *WAllocValue) AssignedWires(v Value, bits types.Size) ( - []circuit.Wire, error) { - if bits <= 0 { - return nil, fmt.Errorf("size not set for value %v", v) - } - hash := walloc.hashCode(v) - alloc := walloc.lookup(hash, v) - if alloc == nil { - alloc = walloc.alloc(bits, v, false, true) - alloc.next = walloc.hash[hash] - walloc.hash[hash] = alloc - - // Assign wire IDs. - if alloc.base == circuits.UnassignedID { - alloc.base = walloc.nextWireID - for i := 0; i < int(bits); i++ { - alloc.ids[i] = walloc.nextWireID + circuit.Wire(i) - } - walloc.nextWireID += circuit.Wire(bits) - } - } - if alloc.ids == nil { - alloc.ids = walloc.newIDs(bits) - for i := 0; i < int(bits); i++ { - alloc.ids[i] = alloc.wires[i].ID() - } - } - return alloc.ids, nil -} - -func (walloc *WAllocValue) AssignedWiresAndIDs(v Value, bits types.Size) ( - []*circuits.Wire, error) { - if bits <= 0 { - return nil, fmt.Errorf("size not set for value %v", v) - } - hash := walloc.hashCode(v) - alloc := walloc.lookup(hash, v) - if alloc == nil { - alloc = walloc.alloc(bits, v, true, true) - alloc.next = walloc.hash[hash] - walloc.hash[hash] = alloc - - // Assign wire IDs. - if alloc.base == circuits.UnassignedID { - alloc.base = walloc.nextWireID - for i := 0; i < int(bits); i++ { - alloc.wires[i].SetID(walloc.nextWireID + circuit.Wire(i)) - } - walloc.nextWireID += circuit.Wire(bits) - } - } - if alloc.ids == nil { - alloc.ids = walloc.newIDs(bits) - for i := 0; i < int(bits); i++ { - alloc.ids[i] = alloc.wires[i].ID() - } - } - return alloc.wires, nil -} - -// SetWires implements WireAllocator.SetWires. -func (walloc *WAllocValue) SetWires(v Value, w []*circuits.Wire) { - hash := walloc.hashCode(v) - alloc := walloc.lookup(hash, v) - if alloc != nil { - panic(fmt.Sprintf("wires already set for %v", v)) - } - alloc = &allocByValue{ - key: v, - wires: w, - ids: make([]circuit.Wire, len(w)), - } - if len(w) == 0 { - alloc.base = circuits.UnassignedID - } else { - alloc.base = w[0].ID() - for i := 0; i < len(w); i++ { - alloc.ids[i] = w[i].ID() - } - } - - alloc.next = walloc.hash[hash] - walloc.hash[hash] = alloc -} - -// GCWires implements WireAllocator.GCWires. -func (walloc *WAllocValue) GCWires(v Value) { - hash := walloc.hashCode(v) - alloc := walloc.remove(hash, v) - if alloc == nil { - panic(fmt.Sprintf("GC: %s not known", v)) - } - - if alloc.wires != nil { - if alloc.base == circuits.UnassignedID { - alloc.base = alloc.wires[0].ID() - } - // Clear wires and reassign their IDs. - for i := 0; i < len(alloc.wires); i++ { - alloc.wires[i].Reset(alloc.base + circuit.Wire(i)) - } - bits := types.Size(len(alloc.wires)) - walloc.freeWires[bits] = append(walloc.freeWires[bits], alloc.wires) - } - if alloc.ids != nil { - if alloc.base == circuits.UnassignedID { - alloc.base = alloc.ids[0] - } - // Clear IDs. - for i := 0; i < len(alloc.ids); i++ { - alloc.ids[i] = alloc.base + circuit.Wire(i) - } - bits := types.Size(len(alloc.ids)) - walloc.freeIDs[bits] = append(walloc.freeIDs[bits], alloc.ids) - } - - alloc.next = nil - alloc.base = circuits.UnassignedID - alloc.wires = nil - alloc.ids = nil - walloc.freeHdrs = append(walloc.freeHdrs, alloc) -} - -// Debug implements WireAllocator.Debug. -func (walloc *WAllocValue) Debug() { - total := float64(walloc.flHit + walloc.flMiss) - fmt.Printf("Wire freelist: hit=%v (%.2f%%), miss=%v (%.2f%%)\n", - walloc.flHit, float64(walloc.flHit)/total*100, - walloc.flMiss, float64(walloc.flMiss)/total*100) - - var sum, max int - min := math.MaxInt - - var maxIndex int - - for i := 0; i < len(walloc.hash); i++ { - var count int - for alloc := walloc.hash[i]; alloc != nil; alloc = alloc.next { - count++ - } - sum += count - if count < min { - min = count - } - if count > max { - max = count - maxIndex = i - } - } - fmt.Printf("Hash: min=%v, max=%v, avg=%.4f, lookup=%v (avg=%.4f)\n", - min, max, float64(sum)/float64(len(walloc.hash)), - walloc.lookupCount, - float64(walloc.lookupFound)/float64(walloc.lookupCount)) - - if false { - fmt.Printf("Max bucket:\n") - for alloc := walloc.hash[maxIndex]; alloc != nil; alloc = alloc.next { - fmt.Printf(" %v: %v\n", alloc.key.String(), len(alloc.wires)) - } - } -}