Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions checks/ps6141_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,19 +147,30 @@ func ps6141Candidates(pass *analysis.Pass, owner *ast.FuncDecl, contract *config
case *ast.ExprStmt:
consume, _ = next.X.(*ast.CallExpr)
}
if consume == nil || !slices.Contains(live, ast.Node(block.List[nextIndex])) || ps6087FunctionID(pass, consume) != contract.Consumer || len(consume.Args) != 2 || consume.Ellipsis.IsValid() {
methodWeights := contract.ConsumerForm == "sourceSummary" && contract.WeightArgument == -1
consumerParams := 2
if methodWeights {
consumerParams = 1
}
if consume == nil || !slices.Contains(live, ast.Node(block.List[nextIndex])) || ps6087FunctionID(pass, consume) != contract.Consumer || len(consume.Args) != consumerParams || consume.Ellipsis.IsValid() {
continue
}
_, signature, typed := typedCallee(pass, consume.Fun)
if !typed || signature.Recv() != nil || signature.Variadic() || signature.TypeParams().Len() != 0 || signature.Params().Len() != 2 ||
if !typed || methodWeights != (signature.Recv() != nil) || signature.Variadic() || signature.TypeParams().Len() != 0 || signature.Params().Len() != consumerParams ||
!types.Identical(signature.Params().At(contract.PackedArgument).Type(), packedType) {
continue
}
packed, ok := consume.Args[contract.PackedArgument].(*ast.Ident)
if !ok || pass.TypesInfo.Uses[packed] != object || !types.Identical(pass.TypesInfo.TypeOf(packed), packedType) {
continue
}
if contract.ConsumerForm == "twoInputDot" || contract.ConsumerForm == "sourceSummary" || contract.ConsumerForm == "packedByteDot" {
if methodWeights {
recv := ps6141ActualReceiver(pass, consume, signature)
id, direct := recv.(*ast.Ident)
if !direct || !ps6141OwnerFormal(ownerObject, pass.TypesInfo.Uses[id]) || ps6141ObjectUses(pass, owner.Body, pass.TypesInfo.Uses[id]) != 1 {
continue
}
} else if contract.ConsumerForm == "twoInputDot" || contract.ConsumerForm == "sourceSummary" || contract.ConsumerForm == "packedByteDot" {
weight, direct := consume.Args[contract.WeightArgument].(*ast.Ident)
if !direct || !types.Identical(pass.TypesInfo.TypeOf(weight), packedType) {
continue
Expand Down
74 changes: 74 additions & 0 deletions checks/ps6141_methods.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package checks

import (
"go/ast"
"go/types"

"golang.org/x/tools/go/analysis"
)

// Receiver storage is bounded to one packed field plus numeric shape values.
// References/caches/interfaces and promoted fields are not source certificates.
func ps6141ReceiverPacked(t types.Type) types.Type {
if pointer, ok := types.Unalias(t).(*types.Pointer); ok {
t = pointer.Elem()
}
structure, ok := types.Unalias(t).Underlying().(*types.Struct)
if !ok {
return nil
}
var packed types.Type
for i := 0; i < structure.NumFields(); i++ {
field := structure.Field(i)
if field.Embedded() {
return nil
}
if slice, ok := types.Unalias(field.Type()).Underlying().(*types.Slice); ok {
if packed != nil || !ps6141SummaryLayout(slice.Elem(), 0) {
return nil
}
packed = field.Type()
} else if !ps6141NumericScalar(field.Type()) {
return nil
}
}
return packed
}

func ps6141MethodReceiver(pass *analysis.Pass, decl *ast.FuncDecl, recv *types.Var) bool {
if ps6141ReceiverPacked(recv.Type()) == nil {
return false
}
immutable := true
ast.Inspect(decl.Body, func(n ast.Node) bool {
var targets []ast.Expr
switch node := n.(type) {
case *ast.AssignStmt:
targets = node.Lhs
case *ast.IncDecStmt:
targets = []ast.Expr{node.X}
}
for _, target := range targets {
if id, ok := target.(*ast.Ident); ok && identObject(pass, id) == recv {
immutable = false
}
}
return true
})
return immutable
}

// Bind the actual receiver, not a method expression's extra argument or a
// captured method value. Exact types forbid implicit addressing/promotion.
func ps6141ActualReceiver(pass *analysis.Pass, call *ast.CallExpr, sig *types.Signature) ast.Expr {
selector, ok := ps2110Unparen(call.Fun).(*ast.SelectorExpr)
if !ok || sig.Recv() == nil {
return nil
}
selection := pass.TypesInfo.Selections[selector]
_, direct := ps2110Unparen(selector.X).(*ast.Ident)
if selection == nil || selection.Kind() != types.MethodVal || len(selection.Index()) != 1 || !direct || !types.Identical(pass.TypesInfo.TypeOf(selector.X), sig.Recv().Type()) {
return nil
}
return selector.X
}
113 changes: 113 additions & 0 deletions checks/ps6141_methods_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package checks

import (
"go/types"
"strings"
"testing"

"github.com/jxsl13/perfscan/config"
"golang.org/x/tools/go/analysis"
)

// A source-visible weight/shape receiver modeled on QuantLinear's API shape.
// This controlled activation owner is synthetic, not a historical pilot.
const ps6141MethodSynthetic = `package fixture
type Block struct { Q int8; Scale float32; Sum int16 }
type Packed []Block
type Kernel struct { Weight Packed; In, Out int }
var other *Kernel
func storage(n int) Packed {p:=make(Packed,n);return p}
func fill(p Packed,x []float32) {for i:=range x {p[i].Scale=x[i]*0.125;p[i].Q=int8(x[i]*8);p[i].Sum=int16(x[i])+3}}
func pack(x []float32) Packed {p:=storage(len(x));fill(p,x);return p}
func leaf(p,w Packed) float32 {if len(p)!=len(w){panic("shape")};sum:=float32(0);for i:=range p {sum+=float32(p[i].Q)*float32(w[i].Q)*p[i].Scale*w[i].Scale};return sum}
func (q *Kernel) reduce(p Packed) float32 {return leaf(p,q.Weight)}
func (q *Kernel) dot(p Packed) float32 {return q.reduce(p)}
func owner(x []float32,q *Kernel) float32 {p:=pack(x);return q.dot(p)}
`

func TestPS6141SourceMethodReceiverComposition(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
name, old, replacement string
want int
}{
{"sourcePointerWeightReceiver", "", "", 1},
{"sourceValueWeightReceiver", "*Kernel", "Kernel", 1},
{"directReceiverFieldForward", "return q.reduce(p)", "return leaf(p,q.Weight)", 1},
{"ownerReceiverAlias", "p:=pack(x);return q.dot(p)", "alias:=q;p:=pack(x);return alias.dot(p)", 0},
{"ownerReceiverRebind", "p:=pack(x);return q.dot(p)", "q=other;p:=pack(x);return q.dot(p)", 0},
{"ownerReceiverExposure", "p:=pack(x);return q.dot(p)", "expose(q);p:=pack(x);return q.dot(p)", 0},
{"helperReceiverAlias", "return q.reduce(p)", "alias:=q;return alias.reduce(p)", 0},
{"differentReceiverInvocation", "return q.reduce(p)", "return other.reduce(p)", 0},
{"receiverReboundInMethod", "return q.reduce(p)", "q=other;return q.reduce(p)", 0},
{"receiverFieldMutation", "return q.reduce(p)", "q.Weight[0].Q=0;return q.reduce(p)", 0},
{"receiverFieldRebind", "return q.reduce(p)", "q.Weight=p;return q.reduce(p)", 0},
{"receiverShapeMutation", "return q.reduce(p)", "q.In++;return q.reduce(p)", 0},
{"retainedPackedAlias", "return q.reduce(p)", "q.Weight=p;return q.reduce(p)", 0},
{"differentPackedInvocation", "return q.reduce(p)", "return q.reduce(q.Weight)", 0},
{"multipleMethodUses", "return q.reduce(p)", "return q.reduce(p)+q.reduce(p)", 0},
{"multipleOwnerUses", "return q.dot(p)", "q.dot(p);return q.dot(p)", 0},
{"receiverFieldPassedTwice", "return leaf(p,q.Weight)", "return leaf(q.Weight,q.Weight)", 0},
{"receiverStorageEscape", "return q.reduce(p)", "expose(q);return q.reduce(p)", 0},
{"opaqueMethod", "func (q *Kernel) reduce(p Packed) float32 {return leaf(p,q.Weight)}", "func (q *Kernel) reduce(p Packed) float32", 0},
{"methodValueCapture", "return q.reduce(p)", "f:=q.reduce;return f(p)", 0},
{"methodExpression", "return q.reduce(p)", "return (*Kernel).reduce(q,p)", 0},
{"memoryBearingCacheField", "In, Out int", "In, Out int; Cache *Block", 0},
{"secondStorageField", "In, Out int", "In, Out int; Scratch Packed", 0},
{"missingSourceWeightOrigin", "return leaf(p,q.Weight)", "return shapeOnly(p,q.Out)", 0},
{"errorReturningMethod", "float32 {return q.reduce(p)}", "(float32,error) {return q.reduce(p),nil}", 0},
} {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
source := ps6141MethodSynthetic + `func expose(q *Kernel){}
func shapeOnly(p Packed,n int)float32{sum:=float32(0);for i:=range p{sum+=float32(p[i].Q)*float32(n)};return sum}
`
if tc.old != "" {
changed := strings.ReplaceAll(source, tc.old, tc.replacement)
if changed == source {
t.Fatal("adversarial source mutation did not execute")
}
source = changed
}
if tc.name == "errorReturningMethod" {
source = strings.Replace(source, "return q.dot(p)", "v,_:=q.dot(p);return v", 1)
}
pass, owner := ps6141TypedFixture(t, source)
c := ps6141TestContract()
c.Consumer = "fixture.Kernel.dot"
c.ConsumerForm, c.WeightArgument, c.RowsArgument = "sourceSummary", -1, -1
if !c.Valid() {
t.Fatal("method-role contract prerequisite rejected")
}
if got := len(ps6141Candidates(pass, owner, &c)); got != tc.want {
t.Fatalf("candidates=%d want=%d", got, tc.want)
}
var diagnostics []analysis.Diagnostic
pass.Report = func(d analysis.Diagnostic) { diagnostics = append(diagnostics, d) }
if _, err := runPS6141WithContracts(pass, []config.SingleUseQuantizationContract{c}); err != nil || len(diagnostics) != tc.want {
t.Fatalf("advisories=%d want=%d err=%v", len(diagnostics), tc.want, err)
}
for _, diagnostic := range diagnostics {
if len(diagnostic.Related) != 1 || len(diagnostic.SuggestedFixes) != 0 {
t.Fatalf("method advisory lost source boundary or added a rewrite: %+v", diagnostic)
}
}
if tc.want == 1 {
index := &ps6141SummaryIndex{pass: pass, declarations: ps6099LocalFunctionDeclarations(pass), memo: map[*types.Func]ps6141SourceSummary{}, active: map[*types.Func]bool{}, remaining: 20000}
found := false
for fn := range index.declarations {
if ps6090FunctionID(fn) == c.Consumer {
found = true
summary := index.function(fn)
if !summary.valid || !summary.resultDeps[1] || !summary.resultDeps[2] || summary.traversals[1] != 1 || len(summary.writes) != 0 {
t.Fatalf("receiver origin/effect proof missing: %+v", summary)
}
}
}
if !found {
t.Fatal("typed method prerequisite absent")
}
}
})
}
}
63 changes: 55 additions & 8 deletions checks/ps6141_summary.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"go/constant"
"go/token"
"go/types"
"slices"

"github.com/jxsl13/perfscan/config"
"golang.org/x/tools/go/analysis"
Expand Down Expand Up @@ -76,7 +77,12 @@ func ps6141ComposedBoundary(pass *analysis.Pass, producer, consumer *ast.FuncDec
}
ps, qs := p.Type().(*types.Signature), q.Type().(*types.Signature)
destination := c.ProducerForm == "destination"
if (!destination && (ps.Params().Len() != 1 || ps.Results().Len() != 1)) || (destination && (ps.Params().Len() != 2 || ps.Results().Len() != 0)) || qs.Params().Len() != 2 || qs.Results().Len() != 1 {
methodWeights := c.ConsumerForm == "sourceSummary" && c.WeightArgument == -1
consumerParams := 2
if methodWeights {
consumerParams = 1
}
if ps.Recv() != nil || (!destination && (ps.Params().Len() != 1 || ps.Results().Len() != 1)) || (destination && (ps.Params().Len() != 2 || ps.Results().Len() != 0)) || qs.Params().Len() != consumerParams || qs.Results().Len() != 1 || methodWeights != (qs.Recv() != nil) {
return nil, nil
}
input, ok := types.Unalias(ps.Params().At(c.FloatInputArgument).Type()).Underlying().(*types.Slice)
Expand All @@ -90,12 +96,21 @@ func ps6141ComposedBoundary(pass *analysis.Pass, producer, consumer *ast.FuncDec
packed = ps.Results().At(0).Type()
}
layout, ok := types.Unalias(packed).Underlying().(*types.Slice)
if !ok || !ps6141SummaryLayout(layout.Elem(), 0) || !types.Identical(qs.Params().At(c.PackedArgument).Type(), packed) || !types.Identical(qs.Params().At(c.WeightArgument).Type(), packed) {
var weightType types.Type
if methodWeights {
weightType = ps6141ReceiverPacked(qs.Recv().Type())
} else {
weightType = qs.Params().At(c.WeightArgument).Type()
}
if !ok || !ps6141SummaryLayout(layout.Elem(), 0) || !types.Identical(qs.Params().At(c.PackedArgument).Type(), packed) || !types.Identical(weightType, packed) {
return nil, nil
}
index := &ps6141SummaryIndex{pass: pass, declarations: ps6099LocalFunctionDeclarations(pass), memo: map[*types.Func]ps6141SourceSummary{}, active: map[*types.Func]bool{}, remaining: 20000}
a, b := index.producer(p), index.function(q)
pr, wr := ps6141Root(c.PackedArgument+1), ps6141Root(c.WeightArgument+1)
if methodWeights {
wr = ps6141Root(qs.Params().Len() + 1)
}
if c.ConsumerForm == "packedByteDot" && (a.byteProducer == nil || b.byteConsumerStride != a.byteProducer.layout.byteStride || !b.byteConsumerInputs[pr] || !b.byteConsumerInputs[wr]) {
return nil, nil
}
Expand Down Expand Up @@ -146,7 +161,10 @@ func (index *ps6141SummaryIndex) function(fn *types.Func) ps6141SourceSummary {
decl := index.declarations[fn]
sig := fn.Type().(*types.Signature)
// Formal roots must never overlap the reserved 1000+ protocol namespaces.
if decl == nil || decl.Body == nil || sig.Params().Len() >= 1000 || sig.Recv() != nil || sig.Variadic() || sig.TypeParams().Len() != 0 || sig.Results().Len() > 1 {
if decl == nil || decl.Body == nil || sig.Params().Len() >= 1000 || sig.Recv() != nil && sig.Params().Len() >= 999 || sig.Variadic() || sig.TypeParams().Len() != 0 || sig.RecvTypeParams().Len() != 0 || sig.Results().Len() > 1 {
return ps6141SourceSummary{}
}
if sig.Recv() != nil && !ps6141MethodReceiver(index.pass, decl, sig.Recv()) {
return ps6141SourceSummary{}
}
index.active[fn] = true
Expand All @@ -170,6 +188,10 @@ func (index *ps6141SummaryIndex) function(fn *types.Func) ps6141SourceSummary {
body.summary.valid = false
}
}
if recv := sig.Recv(); recv != nil {
root := ps6141Root(sig.Params().Len() + 1)
body.roots[recv] = root
}
body.block(decl.Body)
if body.byteConsumer != nil {
body.summary.byteConsumerStride = body.byteConsumer.stride
Expand Down Expand Up @@ -581,6 +603,12 @@ func (b *ps6141SummaryBody) expression(expr ast.Expr) ps6141Deps {
case *ast.SelectorExpr:
selection := b.index.pass.TypesInfo.Selections[x]
if selection != nil && selection.Kind() == types.FieldVal {
if id, direct := ps2110Unparen(x.X).(*ast.Ident); direct && ps6141ReceiverPacked(b.index.pass.TypesInfo.TypeOf(id)) != nil && ps6141NumericScalar(b.index.pass.TypesInfo.TypeOf(x)) {
if !b.path(x) {
b.summary.valid = false
}
return nil // shape metadata cannot impersonate indexed weight origin
}
return b.expression(x.X)
}
case *ast.CallExpr:
Expand Down Expand Up @@ -630,10 +658,23 @@ func (b *ps6141SummaryBody) expression(expr ast.Expr) ps6141Deps {
// effects fail closed. Scalar helper arithmetic may be arbitrary source syntax.
func (b *ps6141SummaryBody) call(call *ast.CallExpr) ps6141SourceSummary {
fn, sig, ok := typedCallee(b.index.pass, call.Fun)
if !ok || sig.Recv() != nil || call.Ellipsis.IsValid() || len(call.Args) != sig.Params().Len() {
if !ok || call.Ellipsis.IsValid() || len(call.Args) != sig.Params().Len() {
b.summary.valid = false
return ps6141SourceSummary{}
}
args := slices.Clone(call.Args)
formals := make([]*types.Var, 0, sig.Params().Len()+1)
for i := 0; i < sig.Params().Len(); i++ {
formals = append(formals, sig.Params().At(i))
}
if sig.Recv() != nil {
recv := ps6141ActualReceiver(b.index.pass, call, sig)
if recv == nil {
b.summary.valid = false
return ps6141SourceSummary{}
}
args, formals = append(args, recv), append(formals, sig.Recv())
}
s := b.index.function(fn)
b.summary.byteLaneTransformUnknown = b.summary.byteLaneTransformUnknown || s.byteLaneTransformUnknown
if !s.valid {
Expand All @@ -643,10 +684,16 @@ func (b *ps6141SummaryBody) call(call *ast.CallExpr) ps6141SourceSummary {
bindings := map[ps6141Root]ps6141Deps{}
seen := map[ps6141Root]bool{}
// Symbolic-map domain: seen keys are actual invocation roots, including negative fresh-allocation identities
for i, arg := range call.Args {
if _, slice := types.Unalias(sig.Params().At(i).Type()).Underlying().(*types.Slice); slice {
for i, arg := range args {
_, slice := types.Unalias(formals[i].Type()).Underlying().(*types.Slice)
if slice || formals[i] == sig.Recv() {
r := b.root(arg)
_, direct := ps2110Unparen(arg).(*ast.Ident)
if selector, field := ps2110Unparen(arg).(*ast.SelectorExpr); field {
id, base := ps2110Unparen(selector.X).(*ast.Ident)
selection := b.index.pass.TypesInfo.Selections[selector]
direct = base && selection != nil && selection.Kind() == types.FieldVal && len(selection.Index()) == 1 && ps6141ReceiverPacked(b.index.pass.TypesInfo.TypeOf(id)) != nil
}
if r == 0 || seen[r] || !b.path(arg) || !direct { //perfscan:ignore PS3003 symbolic invocation roots include negative fresh identities and sparse 1000/2000/3000 protocol namespaces, not traversal indexes
b.summary.valid = false
return ps6141SourceSummary{}
Expand Down Expand Up @@ -680,8 +727,8 @@ func (b *ps6141SummaryBody) call(call *ast.CallExpr) ps6141SourceSummary {
out.resultDeps = ps6141ByteDropLaneTokens(out.resultDeps)
}
// Symbolic-map domain: resultDeps combines sparse formal origins with 1000/2000/3000 protocol namespaces
for i, arg := range call.Args {
if s.resultDeps[ps6141Root(i+1)] && ps6141SummaryScalar(sig.Params().At(i).Type()) { //perfscan:ignore PS3003 symbolic invocation roots include negative fresh identities and sparse 1000/2000/3000 protocol namespaces, not traversal indexes
for i, arg := range args {
if s.resultDeps[ps6141Root(i+1)] && ps6141SummaryScalar(formals[i].Type()) { //perfscan:ignore PS3003 symbolic invocation roots include negative fresh identities and sparse 1000/2000/3000 protocol namespaces, not traversal indexes
if b.erases(arg) {
out.resultSuppressed = true
}
Expand Down
9 changes: 8 additions & 1 deletion config/single_use_quantization.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ type SingleUseQuantizationContract struct {
// packedByteDot additionally requires source-proved current-block byte lanes,
// returned signed lane/header origins and the producer's exact byte stride.
// It does not attest authentic activation provenance or numeric equivalence.
ConsumerForm string `json:"consumerForm" yaml:"consumerForm"`
ConsumerForm string `json:"consumerForm" yaml:"consumerForm"`
// -1 selects sourceSummary receiver-held weights with one packed argument.
// The receiver's complete source body and storage/effects must prove the role.
WeightArgument int `json:"weightArgument" yaml:"weightArgument"`
QuantizationAndDotMeaningReviewed bool `json:"quantizationAndDotMeaningReviewed" yaml:"quantizationAndDotMeaningReviewed"`
BenchmarkExemptOwners []string `json:"benchmarkExemptOwners" yaml:"benchmarkExemptOwners"`
Expand Down Expand Up @@ -65,6 +67,11 @@ func (c *SingleUseQuantizationContract) Valid() bool {
return false
}
case "twoInputDot", "sourceSummary", "packedByteDot":
// A sourceSummary method may consume receiver-held weights. This role
// still requires typed source proof; configuration does not certify it.
if c.ConsumerForm == "sourceSummary" && c.WeightArgument == -1 && c.PackedArgument == 0 && c.RowsArgument == -1 {
break
}
if c.RowsArgument != -1 || c.WeightArgument < 0 || c.WeightArgument > 1 || c.PackedArgument == c.WeightArgument {
return false
}
Expand Down
Loading