// Copyright 2026 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 specexpr import ( "cmp" "fmt" "strings" ) // Num represents a number in the solver. This is abstracted because we work // with both concrete numbers and *symbolic widths* and need to be able to mix // them. A Num is also an [Expr]. type Num interface { Expr // ValidWidth returns true if this is a valid width: either a fixed width // (128, 256, 512) or the scalable width VW. ValidWidth() bool Mul(x Num) (Num, error) Div(x Num) (Num, error) Compare(o Num) (int, bool) String() string } // Int is an integer that satisfies [Num]. type Int int func (w Int) ValidWidth() bool { switch w { case 128, 256, 512: return true } return false } func (w Int) Mul(x Num) (Num, error) { switch x := x.(type) { case Int: return w * x, nil case ScalableWidth: return mkWidth(int(w)*x.num, x.denom), nil } panic("unknown Num") } func (w Int) Div(x Num) (Num, error) { switch x := x.(type) { case Int: if x == 0 { return nil, fmt.Errorf("division by zero") } if w%x != 0 { return nil, fmt.Errorf("inexact division %d/%d", w, x) } return w / x, nil case ScalableWidth: // ScalableWidth is implicitly multiplied by VW. We have no way to // express the inverse of VW. return nil, fmt.Errorf("cannot divide Int by ScalableWidth") } panic("unknown Num") } func (w Int) Compare(o Num) (int, bool) { if o, ok := o.(Int); ok { return cmp.Compare(w, o), true } return 0, false } func (w Int) String() string { return fmt.Sprint(int(w)) } func (w Int) eval(b *Bindings) (any, error) { return w, nil } func (w Int) preorder(yield func(Expr) bool) bool { return true } // An ScalableWidth represents a symbolic width relative to a fixed but unknown // scalable vector width VW. This is represented as a rational factor // VW*num/denom type ScalableWidth struct { num, denom int } // VW returns the base ScalableWidth representing a full-width scalable vector. func VW() ScalableWidth { return ScalableWidth{1, 1} } func gcd(a, b int) int { for b != 0 { a, b = b, a%b } if a < 0 { return -a } return a } func mkWidth(num, denom int) ScalableWidth { if denom == 0 { panic("denominator cannot be zero") } g := gcd(num, denom) num /= g denom /= g if denom < 0 { num = -num denom = -denom } return ScalableWidth{num, denom} } func (w ScalableWidth) ValidWidth() bool { return w == ScalableWidth{1, 1} } func (w ScalableWidth) Mul(x Num) (Num, error) { switch x := x.(type) { case Int: return mkWidth(w.num*int(x), w.denom), nil case ScalableWidth: return nil, fmt.Errorf("cannot multiply two scalable widths") } panic("unknown Num") } func (w ScalableWidth) Div(x Num) (Num, error) { switch x := x.(type) { case Int: if x == 0 { return nil, fmt.Errorf("division by zero") } return mkWidth(w.num, w.denom*int(x)), nil case ScalableWidth: a, b := w.num*x.denom, w.denom*x.num if b == 0 { return nil, fmt.Errorf("division by zero") } if a%b != 0 { return nil, fmt.Errorf("inexact division %d/%d", w, x) } return Int(a / b), nil } panic("unknown Num") } func (w ScalableWidth) Compare(o Num) (int, bool) { if o, ok := o.(ScalableWidth); ok { return cmp.Compare(w.num*o.denom, o.num*w.denom), true } return 0, false } func (w ScalableWidth) String() string { var buf strings.Builder buf.WriteString("VW") if w.num > 1 { fmt.Fprintf(&buf, "*%d", w.num) } if w.denom > 1 { fmt.Fprintf(&buf, "/%d", w.denom) } return buf.String() } func (w ScalableWidth) eval(b *Bindings) (any, error) { return w, nil } func (w ScalableWidth) preorder(yield func(Expr) bool) bool { return true }