go/src/simd/_gen/unify/closure.go

155 lines
4.1 KiB
Go
Raw Normal View History

[dev.simd] simd/_gen: migrate simdgen from x/arch This moves the simdgen tool and its supporting unify package from golang.org/x/arch/internal as of CL 695619 to simd/_gen in the main repo. The simdgen tool was started in x/arch to live next to xeddata and a few other assembler generators that already lived there. However, as we've been developing simdgen, we've discovered that there's a tremendous amount of process friction coordinating commits to x/arch with the corresponding generated files in the main repo. Many of the existing generators in x/arch were started before modules existed. In GOPATH world, it was impractical for them to live in the main repo because they have dependencies that are not allowed in the main repo. However, now that we have modules and can use small submodules in the main repo, we can isolate these dependencies to just the generators, making it practical for them to live in the main repo. This commit was generated by the following script: # Checks set -e if [[ ! -d src/simd ]]; then echo >&2 "$PWD is not the root of the main repo on dev.simd" exit 1 fi if [[ -z "$XEDDATA" ]]; then echo >&2 "Must set \$XEDDATA" exit 1 fi which go >/dev/null # Move simdgen from x/arch xarch=$(mktemp -d) git clone https://go.googlesource.com/arch $xarch xarchCL=$(git -C $xarch log -1 --format=%b | awk -F/ '/^Reviewed-on:/ {print $NF}') echo >&2 "x/arch CL: $xarchCL" mv $xarch/internal src/simd/_gen sed --in-place s,golang.org/x/arch/internal/,simd/_gen/, src/simd/_gen/*/*.go # Create self-contained module cat > src/simd/_gen/go.mod <<EOF module simd/_gen go 1.24 EOF cd src/simd/_gen go mod tidy git add . git gofmt # Regenerate file go run -C simdgen . -xedPath $XEDDATA -o godefs -goroot $(go env GOROOT) go.yaml types.yaml categories.yaml go run -C ../../cmd/compile/internal/ssa/_gen . Change-Id: I56dd8473e913a9eb1978d9b3b3518ed632972f6f Reviewed-on: https://go-review.googlesource.com/c/go/+/695975 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com>
2025-08-13 15:30:27 -04:00
// Copyright 2025 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 unify
import (
"fmt"
"iter"
"maps"
"slices"
)
type Closure struct {
val *Value
env envSet
}
func NewSum(vs ...*Value) Closure {
id := &ident{name: "sum"}
return Closure{NewValue(Var{id}), topEnv.bind(id, vs...)}
}
// IsBottom returns whether c consists of no values.
func (c Closure) IsBottom() bool {
return c.val.Domain == nil
}
// Summands returns the top-level Values of c. This assumes the top-level of c
// was constructed as a sum, and is mostly useful for debugging.
func (c Closure) Summands() iter.Seq[*Value] {
return func(yield func(*Value) bool) {
var rec func(v *Value, env envSet) bool
rec = func(v *Value, env envSet) bool {
switch d := v.Domain.(type) {
case Var:
parts := env.partitionBy(d.id)
for _, part := range parts {
// It may be a sum of sums. Walk into this value.
if !rec(part.value, part.env) {
return false
}
}
return true
default:
return yield(v)
}
}
rec(c.val, c.env)
}
}
// All enumerates all possible concrete values of c by substituting variables
// from the environment.
//
// E.g., enumerating this Value
//
// a: !sum [1, 2]
// b: !sum [3, 4]
//
// results in
//
// - {a: 1, b: 3}
// - {a: 1, b: 4}
// - {a: 2, b: 3}
// - {a: 2, b: 4}
func (c Closure) All() iter.Seq[*Value] {
// In order to enumerate all concrete values under all possible variable
// bindings, we use a "non-deterministic continuation passing style" to
// implement this. We use CPS to traverse the Value tree, threading the
// (possibly narrowing) environment through that CPS following an Euler
// tour. Where the environment permits multiple choices, we invoke the same
// continuation for each choice. Similar to a yield function, the
// continuation can return false to stop the non-deterministic walk.
return func(yield func(*Value) bool) {
c.val.all1(c.env, func(v *Value, e envSet) bool {
return yield(v)
})
}
}
func (v *Value) all1(e envSet, cont func(*Value, envSet) bool) bool {
switch d := v.Domain.(type) {
default:
panic(fmt.Sprintf("unknown domain type %T", d))
case nil:
return true
case Top, String:
return cont(v, e)
case Def:
fields := d.keys()
// We can reuse this parts slice because we're doing a DFS through the
// state space. (Otherwise, we'd have to do some messy threading of an
// immutable slice-like value through allElt.)
parts := make(map[string]*Value, len(fields))
// TODO: If there are no Vars or Sums under this Def, then nothing can
// change the Value or env, so we could just cont(v, e).
var allElt func(elt int, e envSet) bool
allElt = func(elt int, e envSet) bool {
if elt == len(fields) {
// Build a new Def from the concrete parts. Clone parts because
// we may reuse it on other non-deterministic branches.
nVal := newValueFrom(Def{maps.Clone(parts)}, v)
return cont(nVal, e)
}
return d.fields[fields[elt]].all1(e, func(v *Value, e envSet) bool {
parts[fields[elt]] = v
return allElt(elt+1, e)
})
}
return allElt(0, e)
case Tuple:
// Essentially the same as Def.
if d.repeat != nil {
// There's nothing we can do with this.
return cont(v, e)
}
parts := make([]*Value, len(d.vs))
var allElt func(elt int, e envSet) bool
allElt = func(elt int, e envSet) bool {
if elt == len(d.vs) {
// Build a new tuple from the concrete parts. Clone parts because
// we may reuse it on other non-deterministic branches.
nVal := newValueFrom(Tuple{vs: slices.Clone(parts)}, v)
return cont(nVal, e)
}
return d.vs[elt].all1(e, func(v *Value, e envSet) bool {
parts[elt] = v
return allElt(elt+1, e)
})
}
return allElt(0, e)
case Var:
// Go each way this variable can be bound.
for _, ePart := range e.partitionBy(d.id) {
// d.id is no longer bound in this environment partition. We'll may
// need it later in the Euler tour, so bind it back to this single
// value.
env := ePart.env.bind(d.id, ePart.value)
if !ePart.value.all1(env, cont) {
return false
}
}
return true
}
}